Merge changes from topic 'BitmapFactory'
* changes:
Allow SkAndroidCodec to compute the decode color type and alpha type
Modify BitmapFactory to use SkAndroidCodec
Make NinePatchPeeker inherit from SkPngChunkReader
diff --git a/api/current.txt b/api/current.txt
index d17f4b6..39a3b1e 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -925,6 +925,7 @@
field public static final deprecated int phoneNumber = 16843111; // 0x1010167
field public static final int pivotX = 16843189; // 0x10101b5
field public static final int pivotY = 16843190; // 0x10101b6
+ field public static final int pointerShape = 16844042; // 0x101050a
field public static final int popupAnimationStyle = 16843465; // 0x10102c9
field public static final int popupBackground = 16843126; // 0x1010176
field public static final int popupCharacters = 16843332; // 0x1010244
@@ -5657,8 +5658,10 @@
method public android.graphics.drawable.Drawable peekFastDrawable();
method public void sendWallpaperCommand(android.os.IBinder, java.lang.String, int, int, int, android.os.Bundle);
method public void setBitmap(android.graphics.Bitmap) throws java.io.IOException;
+ method public void setBitmap(android.graphics.Bitmap, android.graphics.Rect, boolean) throws java.io.IOException;
method public void setResource(int) throws java.io.IOException;
method public void setStream(java.io.InputStream) throws java.io.IOException;
+ method public void setStream(java.io.InputStream, android.graphics.Rect, boolean) throws java.io.IOException;
method public void setWallpaperOffsetSteps(float, float);
method public void setWallpaperOffsets(android.os.IBinder, float, float);
method public void suggestDesiredDimensions(int, int);
@@ -8487,6 +8490,7 @@
field public static final int FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS = 8388608; // 0x800000
field public static final int FLAG_ACTIVITY_FORWARD_RESULT = 33554432; // 0x2000000
field public static final int FLAG_ACTIVITY_LAUNCHED_FROM_HISTORY = 1048576; // 0x100000
+ field public static final int FLAG_ACTIVITY_LAUNCH_TO_SIDE = 4096; // 0x1000
field public static final int FLAG_ACTIVITY_MULTIPLE_TASK = 134217728; // 0x8000000
field public static final int FLAG_ACTIVITY_NEW_DOCUMENT = 524288; // 0x80000
field public static final int FLAG_ACTIVITY_NEW_TASK = 268435456; // 0x10000000
@@ -9530,6 +9534,7 @@
field public static final java.lang.String FEATURE_NFC = "android.hardware.nfc";
field public static final java.lang.String FEATURE_NFC_HOST_CARD_EMULATION = "android.hardware.nfc.hce";
field public static final java.lang.String FEATURE_OPENGLES_EXTENSION_PACK = "android.hardware.opengles.aep";
+ field public static final java.lang.String FEATURE_PICTURE_IN_PICTURE = "android.software.picture_in_picture";
field public static final java.lang.String FEATURE_PRINTING = "android.software.print";
field public static final java.lang.String FEATURE_SCREEN_LANDSCAPE = "android.hardware.screen.landscape";
field public static final java.lang.String FEATURE_SCREEN_PORTRAIT = "android.hardware.screen.portrait";
@@ -9965,6 +9970,7 @@
method public java.lang.String getQuantityString(int, int, java.lang.Object...) throws android.content.res.Resources.NotFoundException;
method public java.lang.String getQuantityString(int, int) throws android.content.res.Resources.NotFoundException;
method public java.lang.CharSequence getQuantityText(int, int) throws android.content.res.Resources.NotFoundException;
+ method public java.util.Locale getResolvedLocale();
method public java.lang.String getResourceEntryName(int) throws android.content.res.Resources.NotFoundException;
method public java.lang.String getResourceName(int) throws android.content.res.Resources.NotFoundException;
method public java.lang.String getResourcePackageName(int) throws android.content.res.Resources.NotFoundException;
@@ -40336,6 +40342,38 @@
field public static final int ORIENTATION_UNKNOWN = -1; // 0xffffffff
}
+ public final class PointerIcon implements android.os.Parcelable {
+ method public static android.view.PointerIcon createCustomIcon(android.graphics.Bitmap, float, float);
+ method public int describeContents();
+ method public static android.view.PointerIcon getSystemIcon(android.content.Context, int);
+ method public static android.view.PointerIcon loadCustomIcon(android.content.res.Resources, int);
+ method public void writeToParcel(android.os.Parcel, int);
+ field public static final android.os.Parcelable.Creator<android.view.PointerIcon> CREATOR;
+ field public static final int STYLE_ALIAS = 1010; // 0x3f2
+ field public static final int STYLE_ALL_SCROLL = 1013; // 0x3f5
+ field public static final int STYLE_ARROW = 1000; // 0x3e8
+ field public static final int STYLE_CELL = 1006; // 0x3ee
+ field public static final int STYLE_CONTEXT_MENU = 1001; // 0x3e9
+ field public static final int STYLE_COPY = 1011; // 0x3f3
+ field public static final int STYLE_CROSSHAIR = 1007; // 0x3ef
+ field public static final int STYLE_DEFAULT = 1000; // 0x3e8
+ field public static final int STYLE_GRAB = 1020; // 0x3fc
+ field public static final int STYLE_GRABBING = 1021; // 0x3fd
+ field public static final int STYLE_HAND = 1002; // 0x3ea
+ field public static final int STYLE_HELP = 1003; // 0x3eb
+ field public static final int STYLE_HORIZONTAL_DOUBLE_ARROW = 1014; // 0x3f6
+ field public static final int STYLE_NO_DROP = 1012; // 0x3f4
+ field public static final int STYLE_NULL = 0; // 0x0
+ field public static final int STYLE_TEXT = 1008; // 0x3f0
+ field public static final int STYLE_TOP_LEFT_DIAGONAL_DOUBLE_ARROW = 1017; // 0x3f9
+ field public static final int STYLE_TOP_RIGHT_DIAGONAL_DOUBLE_ARROW = 1016; // 0x3f8
+ field public static final int STYLE_VERTICAL_DOUBLE_ARROW = 1015; // 0x3f7
+ field public static final int STYLE_VERTICAL_TEXT = 1009; // 0x3f1
+ field public static final int STYLE_WAIT = 1004; // 0x3ec
+ field public static final int STYLE_ZOOM_IN = 1018; // 0x3fa
+ field public static final int STYLE_ZOOM_OUT = 1019; // 0x3fb
+ }
+
public class ScaleGestureDetector {
ctor public ScaleGestureDetector(android.content.Context, android.view.ScaleGestureDetector.OnScaleGestureListener);
ctor public ScaleGestureDetector(android.content.Context, android.view.ScaleGestureDetector.OnScaleGestureListener, android.os.Handler);
@@ -40695,6 +40733,7 @@
method public android.view.ViewParent getParentForAccessibility();
method public float getPivotX();
method public float getPivotY();
+ method public android.view.PointerIcon getPointerIcon(android.view.MotionEvent, float, float);
method public android.content.res.Resources getResources();
method public final int getRight();
method protected float getRightFadingEdgeStrength();
@@ -40977,6 +41016,7 @@
method public void setPaddingRelative(int, int, int, int);
method public void setPivotX(float);
method public void setPivotY(float);
+ method public void setPointerIcon(android.view.PointerIcon);
method public void setPressed(boolean);
method public final void setRight(int);
method public void setRotation(float);
diff --git a/api/system-current.txt b/api/system-current.txt
index 492d6a9..9240d4f 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -1019,6 +1019,7 @@
field public static final deprecated int phoneNumber = 16843111; // 0x1010167
field public static final int pivotX = 16843189; // 0x10101b5
field public static final int pivotY = 16843190; // 0x10101b6
+ field public static final int pointerShape = 16844042; // 0x101050a
field public static final int popupAnimationStyle = 16843465; // 0x10102c9
field public static final int popupBackground = 16843126; // 0x1010176
field public static final int popupCharacters = 16843332; // 0x1010244
@@ -5778,10 +5779,12 @@
method public android.graphics.drawable.Drawable peekFastDrawable();
method public void sendWallpaperCommand(android.os.IBinder, java.lang.String, int, int, int, android.os.Bundle);
method public void setBitmap(android.graphics.Bitmap) throws java.io.IOException;
+ method public void setBitmap(android.graphics.Bitmap, android.graphics.Rect, boolean) throws java.io.IOException;
method public void setDisplayOffset(android.os.IBinder, int, int);
method public void setDisplayPadding(android.graphics.Rect);
method public void setResource(int) throws java.io.IOException;
method public void setStream(java.io.InputStream) throws java.io.IOException;
+ method public void setStream(java.io.InputStream, android.graphics.Rect, boolean) throws java.io.IOException;
method public boolean setWallpaperComponent(android.content.ComponentName);
method public void setWallpaperOffsetSteps(float, float);
method public void setWallpaperOffsets(android.os.IBinder, float, float);
@@ -8700,6 +8703,8 @@
field public static final int EXTRA_DOCK_STATE_UNDOCKED = 0; // 0x0
field public static final java.lang.String EXTRA_DONT_KILL_APP = "android.intent.extra.DONT_KILL_APP";
field public static final java.lang.String EXTRA_EMAIL = "android.intent.extra.EMAIL";
+ field public static final java.lang.String EXTRA_EPHEMERAL_FAILURE = "android.intent.extra.EPHEMERAL_FAILURE";
+ field public static final java.lang.String EXTRA_EPHEMERAL_SUCCESS = "android.intent.extra.EPHEMERAL_SUCCESS";
field public static final java.lang.String EXTRA_HTML_TEXT = "android.intent.extra.HTML_TEXT";
field public static final java.lang.String EXTRA_INITIAL_INTENTS = "android.intent.extra.INITIAL_INTENTS";
field public static final java.lang.String EXTRA_INSTALLER_PACKAGE_NAME = "android.intent.extra.INSTALLER_PACKAGE_NAME";
@@ -8753,6 +8758,7 @@
field public static final int FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS = 8388608; // 0x800000
field public static final int FLAG_ACTIVITY_FORWARD_RESULT = 33554432; // 0x2000000
field public static final int FLAG_ACTIVITY_LAUNCHED_FROM_HISTORY = 1048576; // 0x100000
+ field public static final int FLAG_ACTIVITY_LAUNCH_TO_SIDE = 4096; // 0x1000
field public static final int FLAG_ACTIVITY_MULTIPLE_TASK = 134217728; // 0x8000000
field public static final int FLAG_ACTIVITY_NEW_DOCUMENT = 524288; // 0x80000
field public static final int FLAG_ACTIVITY_NEW_TASK = 268435456; // 0x10000000
@@ -9448,6 +9454,18 @@
field protected static final java.lang.String TAG = "ContainerEncryptionParams";
}
+ public final class EphemeralResolveInfo implements android.os.Parcelable {
+ ctor public EphemeralResolveInfo(android.net.Uri, java.lang.String, java.util.List<android.content.IntentFilter>);
+ method public int describeContents();
+ method public byte[] getDigestBytes();
+ method public int getDigestPrefix();
+ method public java.util.List<android.content.IntentFilter> getFilters();
+ method public java.lang.String getPackageName();
+ method public void writeToParcel(android.os.Parcel, int);
+ field public static final android.os.Parcelable.Creator<android.content.pm.EphemeralResolveInfo> CREATOR;
+ field public static final java.lang.String SHA_ALGORITHM = "SHA-256";
+ }
+
public final class FeatureGroupInfo implements android.os.Parcelable {
ctor public FeatureGroupInfo();
ctor public FeatureGroupInfo(android.content.pm.FeatureGroupInfo);
@@ -9833,6 +9851,7 @@
field public static final java.lang.String FEATURE_NFC = "android.hardware.nfc";
field public static final java.lang.String FEATURE_NFC_HOST_CARD_EMULATION = "android.hardware.nfc.hce";
field public static final java.lang.String FEATURE_OPENGLES_EXTENSION_PACK = "android.hardware.opengles.aep";
+ field public static final java.lang.String FEATURE_PICTURE_IN_PICTURE = "android.software.picture_in_picture";
field public static final java.lang.String FEATURE_PRINTING = "android.software.print";
field public static final java.lang.String FEATURE_SCREEN_LANDSCAPE = "android.hardware.screen.landscape";
field public static final java.lang.String FEATURE_SCREEN_PORTRAIT = "android.hardware.screen.portrait";
@@ -10312,6 +10331,7 @@
method public java.lang.String getQuantityString(int, int, java.lang.Object...) throws android.content.res.Resources.NotFoundException;
method public java.lang.String getQuantityString(int, int) throws android.content.res.Resources.NotFoundException;
method public java.lang.CharSequence getQuantityText(int, int) throws android.content.res.Resources.NotFoundException;
+ method public java.util.Locale getResolvedLocale();
method public java.lang.String getResourceEntryName(int) throws android.content.res.Resources.NotFoundException;
method public java.lang.String getResourceName(int) throws android.content.res.Resources.NotFoundException;
method public java.lang.String getResourcePackageName(int) throws android.content.res.Resources.NotFoundException;
@@ -42674,6 +42694,38 @@
field public static final int ORIENTATION_UNKNOWN = -1; // 0xffffffff
}
+ public final class PointerIcon implements android.os.Parcelable {
+ method public static android.view.PointerIcon createCustomIcon(android.graphics.Bitmap, float, float);
+ method public int describeContents();
+ method public static android.view.PointerIcon getSystemIcon(android.content.Context, int);
+ method public static android.view.PointerIcon loadCustomIcon(android.content.res.Resources, int);
+ method public void writeToParcel(android.os.Parcel, int);
+ field public static final android.os.Parcelable.Creator<android.view.PointerIcon> CREATOR;
+ field public static final int STYLE_ALIAS = 1010; // 0x3f2
+ field public static final int STYLE_ALL_SCROLL = 1013; // 0x3f5
+ field public static final int STYLE_ARROW = 1000; // 0x3e8
+ field public static final int STYLE_CELL = 1006; // 0x3ee
+ field public static final int STYLE_CONTEXT_MENU = 1001; // 0x3e9
+ field public static final int STYLE_COPY = 1011; // 0x3f3
+ field public static final int STYLE_CROSSHAIR = 1007; // 0x3ef
+ field public static final int STYLE_DEFAULT = 1000; // 0x3e8
+ field public static final int STYLE_GRAB = 1020; // 0x3fc
+ field public static final int STYLE_GRABBING = 1021; // 0x3fd
+ field public static final int STYLE_HAND = 1002; // 0x3ea
+ field public static final int STYLE_HELP = 1003; // 0x3eb
+ field public static final int STYLE_HORIZONTAL_DOUBLE_ARROW = 1014; // 0x3f6
+ field public static final int STYLE_NO_DROP = 1012; // 0x3f4
+ field public static final int STYLE_NULL = 0; // 0x0
+ field public static final int STYLE_TEXT = 1008; // 0x3f0
+ field public static final int STYLE_TOP_LEFT_DIAGONAL_DOUBLE_ARROW = 1017; // 0x3f9
+ field public static final int STYLE_TOP_RIGHT_DIAGONAL_DOUBLE_ARROW = 1016; // 0x3f8
+ field public static final int STYLE_VERTICAL_DOUBLE_ARROW = 1015; // 0x3f7
+ field public static final int STYLE_VERTICAL_TEXT = 1009; // 0x3f1
+ field public static final int STYLE_WAIT = 1004; // 0x3ec
+ field public static final int STYLE_ZOOM_IN = 1018; // 0x3fa
+ field public static final int STYLE_ZOOM_OUT = 1019; // 0x3fb
+ }
+
public class ScaleGestureDetector {
ctor public ScaleGestureDetector(android.content.Context, android.view.ScaleGestureDetector.OnScaleGestureListener);
ctor public ScaleGestureDetector(android.content.Context, android.view.ScaleGestureDetector.OnScaleGestureListener, android.os.Handler);
@@ -43033,6 +43085,7 @@
method public android.view.ViewParent getParentForAccessibility();
method public float getPivotX();
method public float getPivotY();
+ method public android.view.PointerIcon getPointerIcon(android.view.MotionEvent, float, float);
method public android.content.res.Resources getResources();
method public final int getRight();
method protected float getRightFadingEdgeStrength();
@@ -43315,6 +43368,7 @@
method public void setPaddingRelative(int, int, int, int);
method public void setPivotX(float);
method public void setPivotY(float);
+ method public void setPointerIcon(android.view.PointerIcon);
method public void setPressed(boolean);
method public final void setRight(int);
method public void setRotation(float);
diff --git a/api/test-current.txt b/api/test-current.txt
index 680f783..e8d8748 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -925,6 +925,7 @@
field public static final deprecated int phoneNumber = 16843111; // 0x1010167
field public static final int pivotX = 16843189; // 0x10101b5
field public static final int pivotY = 16843190; // 0x10101b6
+ field public static final int pointerShape = 16844042; // 0x101050a
field public static final int popupAnimationStyle = 16843465; // 0x10102c9
field public static final int popupBackground = 16843126; // 0x1010176
field public static final int popupCharacters = 16843332; // 0x1010244
@@ -5657,8 +5658,10 @@
method public android.graphics.drawable.Drawable peekFastDrawable();
method public void sendWallpaperCommand(android.os.IBinder, java.lang.String, int, int, int, android.os.Bundle);
method public void setBitmap(android.graphics.Bitmap) throws java.io.IOException;
+ method public void setBitmap(android.graphics.Bitmap, android.graphics.Rect, boolean) throws java.io.IOException;
method public void setResource(int) throws java.io.IOException;
method public void setStream(java.io.InputStream) throws java.io.IOException;
+ method public void setStream(java.io.InputStream, android.graphics.Rect, boolean) throws java.io.IOException;
method public void setWallpaperOffsetSteps(float, float);
method public void setWallpaperOffsets(android.os.IBinder, float, float);
method public void suggestDesiredDimensions(int, int);
@@ -8487,6 +8490,7 @@
field public static final int FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS = 8388608; // 0x800000
field public static final int FLAG_ACTIVITY_FORWARD_RESULT = 33554432; // 0x2000000
field public static final int FLAG_ACTIVITY_LAUNCHED_FROM_HISTORY = 1048576; // 0x100000
+ field public static final int FLAG_ACTIVITY_LAUNCH_TO_SIDE = 4096; // 0x1000
field public static final int FLAG_ACTIVITY_MULTIPLE_TASK = 134217728; // 0x8000000
field public static final int FLAG_ACTIVITY_NEW_DOCUMENT = 524288; // 0x80000
field public static final int FLAG_ACTIVITY_NEW_TASK = 268435456; // 0x10000000
@@ -9530,6 +9534,7 @@
field public static final java.lang.String FEATURE_NFC = "android.hardware.nfc";
field public static final java.lang.String FEATURE_NFC_HOST_CARD_EMULATION = "android.hardware.nfc.hce";
field public static final java.lang.String FEATURE_OPENGLES_EXTENSION_PACK = "android.hardware.opengles.aep";
+ field public static final java.lang.String FEATURE_PICTURE_IN_PICTURE = "android.software.picture_in_picture";
field public static final java.lang.String FEATURE_PRINTING = "android.software.print";
field public static final java.lang.String FEATURE_SCREEN_LANDSCAPE = "android.hardware.screen.landscape";
field public static final java.lang.String FEATURE_SCREEN_PORTRAIT = "android.hardware.screen.portrait";
@@ -9965,6 +9970,7 @@
method public java.lang.String getQuantityString(int, int, java.lang.Object...) throws android.content.res.Resources.NotFoundException;
method public java.lang.String getQuantityString(int, int) throws android.content.res.Resources.NotFoundException;
method public java.lang.CharSequence getQuantityText(int, int) throws android.content.res.Resources.NotFoundException;
+ method public java.util.Locale getResolvedLocale();
method public java.lang.String getResourceEntryName(int) throws android.content.res.Resources.NotFoundException;
method public java.lang.String getResourceName(int) throws android.content.res.Resources.NotFoundException;
method public java.lang.String getResourcePackageName(int) throws android.content.res.Resources.NotFoundException;
@@ -40338,6 +40344,38 @@
field public static final int ORIENTATION_UNKNOWN = -1; // 0xffffffff
}
+ public final class PointerIcon implements android.os.Parcelable {
+ method public static android.view.PointerIcon createCustomIcon(android.graphics.Bitmap, float, float);
+ method public int describeContents();
+ method public static android.view.PointerIcon getSystemIcon(android.content.Context, int);
+ method public static android.view.PointerIcon loadCustomIcon(android.content.res.Resources, int);
+ method public void writeToParcel(android.os.Parcel, int);
+ field public static final android.os.Parcelable.Creator<android.view.PointerIcon> CREATOR;
+ field public static final int STYLE_ALIAS = 1010; // 0x3f2
+ field public static final int STYLE_ALL_SCROLL = 1013; // 0x3f5
+ field public static final int STYLE_ARROW = 1000; // 0x3e8
+ field public static final int STYLE_CELL = 1006; // 0x3ee
+ field public static final int STYLE_CONTEXT_MENU = 1001; // 0x3e9
+ field public static final int STYLE_COPY = 1011; // 0x3f3
+ field public static final int STYLE_CROSSHAIR = 1007; // 0x3ef
+ field public static final int STYLE_DEFAULT = 1000; // 0x3e8
+ field public static final int STYLE_GRAB = 1020; // 0x3fc
+ field public static final int STYLE_GRABBING = 1021; // 0x3fd
+ field public static final int STYLE_HAND = 1002; // 0x3ea
+ field public static final int STYLE_HELP = 1003; // 0x3eb
+ field public static final int STYLE_HORIZONTAL_DOUBLE_ARROW = 1014; // 0x3f6
+ field public static final int STYLE_NO_DROP = 1012; // 0x3f4
+ field public static final int STYLE_NULL = 0; // 0x0
+ field public static final int STYLE_TEXT = 1008; // 0x3f0
+ field public static final int STYLE_TOP_LEFT_DIAGONAL_DOUBLE_ARROW = 1017; // 0x3f9
+ field public static final int STYLE_TOP_RIGHT_DIAGONAL_DOUBLE_ARROW = 1016; // 0x3f8
+ field public static final int STYLE_VERTICAL_DOUBLE_ARROW = 1015; // 0x3f7
+ field public static final int STYLE_VERTICAL_TEXT = 1009; // 0x3f1
+ field public static final int STYLE_WAIT = 1004; // 0x3ec
+ field public static final int STYLE_ZOOM_IN = 1018; // 0x3fa
+ field public static final int STYLE_ZOOM_OUT = 1019; // 0x3fb
+ }
+
public class ScaleGestureDetector {
ctor public ScaleGestureDetector(android.content.Context, android.view.ScaleGestureDetector.OnScaleGestureListener);
ctor public ScaleGestureDetector(android.content.Context, android.view.ScaleGestureDetector.OnScaleGestureListener, android.os.Handler);
@@ -40697,6 +40735,7 @@
method public android.view.ViewParent getParentForAccessibility();
method public float getPivotX();
method public float getPivotY();
+ method public android.view.PointerIcon getPointerIcon(android.view.MotionEvent, float, float);
method public android.content.res.Resources getResources();
method public final int getRight();
method protected float getRightFadingEdgeStrength();
@@ -40979,6 +41018,7 @@
method public void setPaddingRelative(int, int, int, int);
method public void setPivotX(float);
method public void setPivotY(float);
+ method public void setPointerIcon(android.view.PointerIcon);
method public void setPressed(boolean);
method public final void setRight(int);
method public void setRotation(float);
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java
index 93c6bef..ef84ab0 100644
--- a/core/java/android/app/Activity.java
+++ b/core/java/android/app/Activity.java
@@ -1733,9 +1733,13 @@
*
* @param multiWindowMode True if the activity is in multi-window mode.
*/
+ @CallSuper
public void onMultiWindowModeChanged(boolean multiWindowMode) {
if (DEBUG_LIFECYCLE) Slog.v(TAG,
"onMultiWindowModeChanged " + this + ": " + multiWindowMode);
+ if (mWindow != null) {
+ mWindow.onMultiWindowModeChanged();
+ }
}
/**
diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java
index 81fe23c..c39ee75 100644
--- a/core/java/android/app/ActivityManager.java
+++ b/core/java/android/app/ActivityManager.java
@@ -485,12 +485,9 @@
return stackId == FREEFORM_WORKSPACE_STACK_ID;
}
- /**
- * Returns true if the task bounds should persist across power cycles.
- */
+ /** 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;
+ return stackId == FREEFORM_WORKSPACE_STACK_ID;
}
/**
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index f3539ff..3962abd 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -4399,10 +4399,14 @@
// onConfigurationChanged
int diff = activity.mCurrentConfig.diff(config);
if (diff != 0) {
- // If this activity doesn't handle any of the config changes
- // then don't bother calling onConfigurationChanged as we're
- // going to destroy it.
- if ((~activity.mActivityInfo.getRealConfigChanged() & diff) == 0) {
+ // If this activity doesn't handle any of the config changes then don't bother
+ // calling onConfigurationChanged as we're going to destroy it.
+ // Except in the case where the configuration changed on the activity manager side,
+ // but wasn't big enough to cause a resource change so the activity wasn't destroyed.
+ // In this case we still want to change the configuration of the activity but not
+ // report it to the app.
+ if ((~activity.mActivityInfo.getRealConfigChanged() & diff) == 0
+ || !reportToActivity) {
shouldChangeConfig = true;
}
}
diff --git a/core/java/android/app/KeyguardManager.java b/core/java/android/app/KeyguardManager.java
index 23e4d97..3910657 100644
--- a/core/java/android/app/KeyguardManager.java
+++ b/core/java/android/app/KeyguardManager.java
@@ -21,11 +21,14 @@
import android.app.trust.ITrustManager;
import android.content.Context;
import android.content.Intent;
+import android.content.pm.UserInfo;
import android.os.Binder;
import android.os.RemoteException;
import android.os.IBinder;
+import android.os.IUserManager;
import android.os.ServiceManager;
import android.os.UserHandle;
+import android.os.UserManager;
import android.view.IWindowManager;
import android.view.IOnKeyguardExitResult;
import android.view.WindowManagerGlobal;
@@ -40,6 +43,7 @@
public class KeyguardManager {
private IWindowManager mWM;
private ITrustManager mTrustManager;
+ private IUserManager mUserManager;
/**
* Intent used to prompt user for device credentials.
@@ -49,6 +53,13 @@
"android.app.action.CONFIRM_DEVICE_CREDENTIAL";
/**
+ * Intent used to prompt user for device credentials.
+ * @hide
+ */
+ public static final String ACTION_CONFIRM_DEVICE_CREDENTIAL_WITH_USER =
+ "android.app.action.CONFIRM_DEVICE_CREDENTIAL_WITH_USER";
+
+ /**
* A CharSequence dialog title to show to the user when used with a
* {@link #ACTION_CONFIRM_DEVICE_CREDENTIAL}.
* @hide
@@ -71,7 +82,7 @@
* @return the intent for launching the activity or null if no password is required.
**/
public Intent createConfirmDeviceCredentialIntent(CharSequence title, CharSequence description) {
- if (!isKeyguardSecure()) return null;
+ if (!isDeviceSecure()) return null;
Intent intent = new Intent(ACTION_CONFIRM_DEVICE_CREDENTIAL);
intent.putExtra(EXTRA_TITLE, title);
intent.putExtra(EXTRA_DESCRIPTION, description);
@@ -81,6 +92,28 @@
}
/**
+ * Get an intent to prompt the user to confirm credentials (pin, pattern or password)
+ * for the given user. The caller is expected to launch this activity using
+ * {@link android.app.Activity#startActivityForResult(Intent, int)} and check for
+ * {@link android.app.Activity#RESULT_OK} if the user successfully completes the challenge.
+ *
+ * @return the intent for launching the activity or null if no password is required.
+ *
+ * @hide
+ */
+ public Intent createConfirmDeviceCredentialIntent(
+ CharSequence title, CharSequence description, int userId) {
+ if (!isDeviceSecure(userId)) return null;
+ Intent intent = new Intent(ACTION_CONFIRM_DEVICE_CREDENTIAL_WITH_USER);
+ intent.putExtra(EXTRA_TITLE, title);
+ intent.putExtra(EXTRA_DESCRIPTION, description);
+ intent.putExtra(Intent.EXTRA_USER_ID, userId);
+ // For security reasons, only allow this to come from system settings.
+ intent.setPackage("com.android.settings");
+ return intent;
+ }
+
+ /**
* @deprecated Use {@link android.view.WindowManager.LayoutParams#FLAG_DISMISS_KEYGUARD}
* and/or {@link android.view.WindowManager.LayoutParams#FLAG_SHOW_WHEN_LOCKED}
* instead; this allows you to seamlessly hide the keyguard as your application
@@ -162,6 +195,8 @@
mWM = WindowManagerGlobal.getWindowManagerService();
mTrustManager = ITrustManager.Stub.asInterface(
ServiceManager.getService(Context.TRUST_SERVICE));
+ mUserManager = IUserManager.Stub.asInterface(
+ ServiceManager.getService(Context.USER_SERVICE));
}
/**
diff --git a/core/java/android/app/WallpaperManager.java b/core/java/android/app/WallpaperManager.java
index 22e79b6..5b5bba4 100644
--- a/core/java/android/app/WallpaperManager.java
+++ b/core/java/android/app/WallpaperManager.java
@@ -52,6 +52,8 @@
import android.util.Log;
import android.view.WindowManagerGlobal;
+import libcore.io.IoUtils;
+
import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileInputStream;
@@ -309,11 +311,7 @@
} catch (OutOfMemoryError e) {
Log.w(TAG, "Can't decode file", e);
} finally {
- try {
- fd.close();
- } catch (IOException e) {
- // Ignore
- }
+ IoUtils.closeQuietly(fd);
}
}
} catch (RemoteException e) {
@@ -321,7 +319,7 @@
}
return null;
}
-
+
private Bitmap getDefaultWallpaperLocked(Context context) {
InputStream is = openDefaultWallpaper(context);
if (is != null) {
@@ -331,11 +329,7 @@
} catch (OutOfMemoryError e) {
Log.w(TAG, "Can't decode stream", e);
} finally {
- try {
- is.close();
- } catch (IOException e) {
- // Ignore
- }
+ IoUtils.closeQuietly(is);
}
}
return null;
@@ -738,30 +732,65 @@
fos = new ParcelFileDescriptor.AutoCloseOutputStream(fd);
setWallpaper(resources.openRawResource(resid), fos);
} finally {
- if (fos != null) {
- fos.close();
- }
+ IoUtils.closeQuietly(fos);
}
}
} catch (RemoteException e) {
// Ignore
}
}
-
+
/**
* Change the current system wallpaper to a bitmap. The given bitmap is
* converted to a PNG and stored as the wallpaper. On success, the intent
* {@link Intent#ACTION_WALLPAPER_CHANGED} is broadcast.
*
+ * <p>This method is equivalent to calling
+ * {@link #setBitmap(Bitmap, Rect, boolean)} and passing {@code null} for the
+ * {@code visibleCrop} rectangle and {@code true} for the {@code allowBackup}
+ * parameter.
+ *
* <p>This method requires the caller to hold the permission
* {@link android.Manifest.permission#SET_WALLPAPER}.
*
* @param bitmap The bitmap to save.
*
- * @throws IOException If an error occurs reverting to the built-in
- * wallpaper.
+ * @throws IOException If an error occurs when attempting to set the wallpaper
+ * to the provided image.
*/
public void setBitmap(Bitmap bitmap) throws IOException {
+ setBitmap(bitmap, null, true);
+ }
+
+ /**
+ * Change the current system wallpaper to a bitmap, specifying a hint about
+ * which subrectangle of the full image is to be visible. The OS will then
+ * try to best present the given portion of the full image as the static system
+ * wallpaper image. On success, the intent
+ * {@link Intent#ACTION_WALLPAPER_CHANGED} is broadcast.
+ *
+ * <p>Passing {@code null} as the {@code visibleHint} parameter is equivalent to
+ * passing (0, 0, {@code fullImage.getWidth()}, {@code fullImage.getHeight()}).
+ *
+ * <p>This method requires the caller to hold the permission
+ * {@link android.Manifest.permission#SET_WALLPAPER}.
+ *
+ * @param fullImage A bitmap that will supply the wallpaper imagery.
+ * @param visibleCropHint The rectangular subregion of {@code fullImage} that should be
+ * displayed as wallpaper. Passing {@code null} for this parameter means that
+ * the full image should be displayed if possible given the image's and device's
+ * aspect ratios, etc.
+ * @param allowBackup {@code true} if the OS is permitted to back up this wallpaper
+ * image for restore to a future device; {@code false} otherwise.
+ *
+ * @throws IOException If an error occurs when attempting to set the wallpaper
+ * to the provided image.
+ * @throws IllegalArgumentException If the {@code visibleCropHint} rectangle is
+ * empty or invalid.
+ */
+ public void setBitmap(Bitmap fullImage, Rect visibleCropHint, boolean allowBackup)
+ throws IOException {
+ validateRect(visibleCropHint);
if (sGlobals.mService == null) {
Log.w(TAG, "WallpaperService not running");
return;
@@ -775,17 +804,21 @@
FileOutputStream fos = null;
try {
fos = new ParcelFileDescriptor.AutoCloseOutputStream(fd);
- bitmap.compress(Bitmap.CompressFormat.PNG, 90, fos);
+ fullImage.compress(Bitmap.CompressFormat.PNG, 90, fos);
} finally {
- if (fos != null) {
- fos.close();
- }
+ IoUtils.closeQuietly(fos);
}
} catch (RemoteException e) {
// Ignore
}
}
+ private final void validateRect(Rect rect) {
+ if (rect != null && rect.isEmpty()) {
+ throw new IllegalArgumentException("visibleCrop rectangle must be valid and non-empty");
+ }
+ }
+
/**
* Change the current system wallpaper to a specific byte stream. The
* give InputStream is copied into persistent storage and will now be
@@ -793,15 +826,60 @@
* image. On success, the intent {@link Intent#ACTION_WALLPAPER_CHANGED}
* is broadcast.
*
+ * <p>This method is equivalent to calling
+ * {@link #setStream(InputStream, Rect, boolean)} and passing {@code null} for the
+ * {@code visibleCrop} rectangle and {@code true} for the {@code allowBackup}
+ * parameter.
+ *
* <p>This method requires the caller to hold the permission
* {@link android.Manifest.permission#SET_WALLPAPER}.
*
- * @param data A stream containing the raw data to install as a wallpaper.
+ * @param bitmapData A stream containing the raw data to install as a wallpaper.
*
- * @throws IOException If an error occurs reverting to the built-in
- * wallpaper.
+ * @throws IOException If an error occurs when attempting to set the wallpaper
+ * based on the provided image data.
*/
- public void setStream(InputStream data) throws IOException {
+ public void setStream(InputStream bitmapData) throws IOException {
+ setStream(bitmapData, null, true);
+ }
+
+ private void setWallpaper(InputStream data, FileOutputStream fos)
+ throws IOException {
+ byte[] buffer = new byte[32768];
+ int amt;
+ while ((amt=data.read(buffer)) > 0) {
+ fos.write(buffer, 0, amt);
+ }
+ }
+
+ /**
+ * Change the current system wallpaper to a specific byte stream, specifying a
+ * hint about which subrectangle of the full image is to be visible. The OS will
+ * then try to best present the given portion of the full image as the static system
+ * wallpaper image. The data from the given InputStream is copied into persistent
+ * storage and will then be used as the system wallpaper. Currently the data must
+ * be either a JPEG or PNG image. On success, the intent
+ * {@link Intent#ACTION_WALLPAPER_CHANGED} is broadcast.
+ *
+ * <p>This method requires the caller to hold the permission
+ * {@link android.Manifest.permission#SET_WALLPAPER}.
+ *
+ * @param bitmapData A stream containing the raw data to install as a wallpaper.
+ * @param visibleCropHint The rectangular subregion of the streamed image that should be
+ * displayed as wallpaper. Passing {@code null} for this parameter means that
+ * the full image should be displayed if possible given the image's and device's
+ * aspect ratios, etc.
+ * @param allowBackup {@code true} if the OS is permitted to back up this wallpaper
+ * image for restore to a future device; {@code false} otherwise.
+ *
+ * @throws IOException If an error occurs when attempting to set the wallpaper
+ * based on the provided image data.
+ * @throws IllegalArgumentException If the {@code visibleCropHint} rectangle is
+ * empty or invalid.
+ */
+ public void setStream(InputStream bitmapData, Rect visibleCropHint, boolean allowBackup)
+ throws IOException {
+ validateRect(visibleCropHint);
if (sGlobals.mService == null) {
Log.w(TAG, "WallpaperService not running");
return;
@@ -815,26 +893,15 @@
FileOutputStream fos = null;
try {
fos = new ParcelFileDescriptor.AutoCloseOutputStream(fd);
- setWallpaper(data, fos);
+ setWallpaper(bitmapData, fos);
} finally {
- if (fos != null) {
- fos.close();
- }
+ IoUtils.closeQuietly(fos);
}
} catch (RemoteException e) {
// Ignore
}
}
- private void setWallpaper(InputStream data, FileOutputStream fos)
- throws IOException {
- byte[] buffer = new byte[32768];
- int amt;
- while ((amt=data.read(buffer)) > 0) {
- fos.write(buffer, 0, amt);
- }
- }
-
/**
* Return whether any users are currently set to use the wallpaper
* with the given resource ID. That is, their wallpaper has been
@@ -915,8 +982,8 @@
* the size of their workspace.
*
* <p>Note developers, who don't seem to be reading this. This is
- * for <em>home screens</em> to tell what size wallpaper they would like.
- * Nobody else should be calling this! Certainly not other non-home-screen
+ * for <em>home apps</em> to tell what size wallpaper they would like.
+ * Nobody else should be calling this! Certainly not other non-home
* apps that change the wallpaper. Those apps are supposed to
* <b>retrieve</b> the suggested size so they can construct a wallpaper
* that matches it.
@@ -1053,11 +1120,11 @@
}
/**
- * Set the position of the current wallpaper within any larger space, when
+ * Set the display position of the current wallpaper within any larger space, when
* that wallpaper is visible behind the given window. The X and Y offsets
* are floating point numbers ranging from 0 to 1, representing where the
* wallpaper should be positioned within the screen space. These only
- * make sense when the wallpaper is larger than the screen.
+ * make sense when the wallpaper is larger than the display.
*
* @param windowToken The window who these offsets should be associated
* with, as returned by {@link android.view.View#getWindowToken()
diff --git a/core/java/android/app/usage/NetworkStatsManager.java b/core/java/android/app/usage/NetworkStatsManager.java
index 2886cda..0fce7a9 100644
--- a/core/java/android/app/usage/NetworkStatsManager.java
+++ b/core/java/android/app/usage/NetworkStatsManager.java
@@ -21,6 +21,7 @@
import android.net.ConnectivityManager;
import android.net.NetworkIdentity;
import android.net.NetworkTemplate;
+import android.os.Build;
import android.os.RemoteException;
import android.util.Log;
@@ -29,10 +30,9 @@
* discrete bins of time called 'Buckets'. See {@link NetworkStats.Bucket} for details.
* <p />
* Queries can define a time interval in the form of start and end timestamps (Long.MIN_VALUE and
- * Long.MAX_VALUE can be used to simulate open ended intervals). All queries (except
- * {@link #querySummaryForDevice}) collect only network usage of apps belonging to the same user
- * as the client. In addition tethering usage, usage by removed users and apps, and usage by system
- * is also included in the results.
+ * Long.MAX_VALUE can be used to simulate open ended intervals). By default, apps can only obtain
+ * data about themselves. See the below note for special cases in which apps can obtain data about
+ * other applications.
* <h3>
* Summary queries
* </h3>
@@ -51,13 +51,20 @@
* multiple buckets for a particular key but all Bucket's state is going to be
* {@link NetworkStats.Bucket#STATE_ALL}.
* <p />
- * <b>NOTE:</b> This API requires the permission
+ * <b>NOTE:</b> Accessing stats for apps other than the calling app requires the permission
* {@link android.Manifest.permission#PACKAGE_USAGE_STATS}, which is a system-level permission and
* will not be granted to third-party apps. However, declaring the permission implies intention to
* use the API and the user of the device can grant permission through the Settings application.
* Profile owner apps are automatically granted permission to query data on the profile they manage
- * (that is, for any query except {@link #querySummaryForDevice}). Device owner apps likewise get
- * access to usage data of the primary user.
+ * (that is, for any query except {@link #querySummaryForDevice}). Device owner apps and carrier-
+ * privileged apps likewise get access to usage data for all users on the device.
+ * <p />
+ * In addition to tethering usage, usage by removed users and apps, and usage by the system
+ * is also included in the results for callers with one of these higher levels of access.
+ * <p />
+ * <b>NOTE:</b> Prior to API level {@value Build.VERSION_CODES#N}, all calls to these APIs required
+ * the above permission, even to access an app's own data usage, and carrier-privileged apps were
+ * not included.
*/
public class NetworkStatsManager {
private final static String TAG = "NetworkStatsManager";
diff --git a/core/java/android/bluetooth/BluetoothAdapter.java b/core/java/android/bluetooth/BluetoothAdapter.java
index 3e8a51e..eb4cb91 100644
--- a/core/java/android/bluetooth/BluetoothAdapter.java
+++ b/core/java/android/bluetooth/BluetoothAdapter.java
@@ -2165,7 +2165,7 @@
@Deprecated
@RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN)
public boolean startLeScan(final UUID[] serviceUuids, final LeScanCallback callback) {
- if (DBG) Log.d(TAG, "startLeScan(): " + serviceUuids);
+ if (DBG) Log.d(TAG, "startLeScan(): " + Arrays.toString(serviceUuids));
if (callback == null) {
if (DBG) Log.e(TAG, "startLeScan: null callback");
return false;
diff --git a/core/java/android/bluetooth/BluetoothSocket.java b/core/java/android/bluetooth/BluetoothSocket.java
index fb81fd1..ae12c88 100644
--- a/core/java/android/bluetooth/BluetoothSocket.java
+++ b/core/java/android/bluetooth/BluetoothSocket.java
@@ -27,6 +27,7 @@
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
+import java.util.Arrays;
import java.util.Locale;
import java.util.UUID;
import android.net.LocalSocket;
@@ -234,9 +235,9 @@
BluetoothSocket as = new BluetoothSocket(this);
as.mSocketState = SocketState.CONNECTED;
FileDescriptor[] fds = mSocket.getAncillaryFileDescriptors();
- if (DBG) Log.d(TAG, "socket fd passed by stack fds: " + fds);
+ if (DBG) Log.d(TAG, "socket fd passed by stack fds: " + Arrays.toString(fds));
if(fds == null || fds.length != 1) {
- Log.e(TAG, "socket fd passed from stack failed, fds: " + fds);
+ Log.e(TAG, "socket fd passed from stack failed, fds: " + Arrays.toString(fds));
as.close();
throw new IOException("bt socket acept failed");
}
diff --git a/core/java/android/content/ContentProvider.java b/core/java/android/content/ContentProvider.java
index ba9cf7c..1c3f45c 100644
--- a/core/java/android/content/ContentProvider.java
+++ b/core/java/android/content/ContentProvider.java
@@ -52,6 +52,7 @@
import java.io.IOException;
import java.io.PrintWriter;
import java.util.ArrayList;
+import java.util.Arrays;
/**
* Content providers are one of the primary building blocks of Android applications, providing
@@ -1854,7 +1855,7 @@
if (mAuthority != null) {
message += mAuthority;
} else {
- message += mAuthorities;
+ message += Arrays.toString(mAuthorities);
}
throw new SecurityException(message);
}
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java
index a27d1cb..9973faf 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -3687,6 +3687,20 @@
public static final String EXTRA_INITIAL_INTENTS = "android.intent.extra.INITIAL_INTENTS";
/**
+ * A {@link IntentSender} to start after ephemeral installation success.
+ * @hide
+ */
+ @SystemApi
+ public static final String EXTRA_EPHEMERAL_SUCCESS = "android.intent.extra.EPHEMERAL_SUCCESS";
+
+ /**
+ * A {@link IntentSender} to start after ephemeral installation failure.
+ * @hide
+ */
+ @SystemApi
+ public static final String EXTRA_EPHEMERAL_FAILURE = "android.intent.extra.EPHEMERAL_FAILURE";
+
+ /**
* A Bundle forming a mapping of potential target package names to different extras Bundles
* to add to the default intent extras in {@link #EXTRA_INTENT} when used with
* {@link #ACTION_CHOOSER}. Each key should be a package name. The package need not
@@ -4419,6 +4433,13 @@
public static final int FLAG_ACTIVITY_RETAIN_IN_RECENTS = 0x00002000;
/**
+ * This flag is only used in the multi-window mode. The new activity will be displayed on
+ * the other side than the one that is launching it. This can only be used in conjunction with
+ * {@link #FLAG_ACTIVITY_NEW_TASK}.
+ */
+ public static final int FLAG_ACTIVITY_LAUNCH_TO_SIDE = 0x00001000;
+
+ /**
* If set, when sending a broadcast only registered receivers will be
* called -- no BroadcastReceiver components will be launched.
*/
diff --git a/core/java/android/content/pm/ApplicationInfo.java b/core/java/android/content/pm/ApplicationInfo.java
index 0633bff..1eeace1 100644
--- a/core/java/android/content/pm/ApplicationInfo.java
+++ b/core/java/android/content/pm/ApplicationInfo.java
@@ -486,13 +486,6 @@
public static final int PRIVATE_FLAG_AUTOPLAY = 1 << 7;
/**
- * Value for {@link #flags}: {@code true} if the application is blocked via restrictions
- * and for most purposes is considered as not installed.
- * {@hide}
- */
- public static final int PRIVATE_FLAG_EPHEMERAL = 1<<8;
-
- /**
* When set, at least one component inside this application is encryption aware.
*
* @hide
@@ -500,6 +493,13 @@
public static final int PRIVATE_FLAG_PARTIALLY_ENCRYPTION_AWARE = 1 << 8;
/**
+ * Value for {@link #flags}: {@code true} if the application is blocked via restrictions
+ * and for most purposes is considered as not installed.
+ * {@hide}
+ */
+ public static final int PRIVATE_FLAG_EPHEMERAL = 1 << 9;
+
+ /**
* Private/hidden flags. See {@code PRIVATE_FLAG_...} constants.
* {@hide}
*/
@@ -746,7 +746,7 @@
pw.println(prefix + "splitPublicSourceDirs=" + Arrays.toString(splitPublicSourceDirs));
}
if (resourceDirs != null) {
- pw.println(prefix + "resourceDirs=" + resourceDirs);
+ pw.println(prefix + "resourceDirs=" + Arrays.toString(resourceDirs));
}
if ((flags&DUMP_FLAG_DETAILS) != 0 && seinfo != null) {
pw.println(prefix + "seinfo=" + seinfo);
diff --git a/core/java/android/content/pm/EphemeralResolveInfo.java b/core/java/android/content/pm/EphemeralResolveInfo.java
new file mode 100644
index 0000000..afb4c30
--- /dev/null
+++ b/core/java/android/content/pm/EphemeralResolveInfo.java
@@ -0,0 +1,139 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.pm;
+
+import android.annotation.NonNull;
+import android.annotation.SystemApi;
+import android.content.IntentFilter;
+import android.net.Uri;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Information about an ephemeral application.
+ * @hide
+ */
+@SystemApi
+public final class EphemeralResolveInfo implements Parcelable {
+ /** Algorithm that will be used to generate the domain digest */
+ public static final String SHA_ALGORITHM = "SHA-256";
+
+ /** Full digest of the domain hash */
+ private final byte[] mDigestBytes;
+ /** The first 4 bytes of the domain hash */
+ private final int mDigestPrefix;
+ private final String mPackageName;
+ /** The filters used to match domain */
+ private final List<IntentFilter> mFilters = new ArrayList<IntentFilter>();
+
+ public EphemeralResolveInfo(@NonNull Uri uri, @NonNull String packageName,
+ @NonNull List<IntentFilter> filters) {
+ // validate arguments
+ if (uri == null
+ || packageName == null
+ || filters == null
+ || filters.size() == 0) {
+ throw new IllegalArgumentException();
+ }
+
+ mDigestBytes = generateDigest(uri);
+ mDigestPrefix =
+ mDigestBytes[0] << 24
+ | mDigestBytes[1] << 16
+ | mDigestBytes[2] << 8
+ | mDigestBytes[3] << 0;
+ mFilters.addAll(filters);
+ mPackageName = packageName;
+ }
+
+ EphemeralResolveInfo(Parcel in) {
+ mDigestBytes = in.createByteArray();
+ mDigestPrefix = in.readInt();
+ mPackageName = in.readString();
+ in.readList(mFilters, null /*loader*/);
+ }
+
+ public byte[] getDigestBytes() {
+ return mDigestBytes;
+ }
+
+ public int getDigestPrefix() {
+ return mDigestPrefix;
+ }
+
+ public String getPackageName() {
+ return mPackageName;
+ }
+
+ public List<IntentFilter> getFilters() {
+ return mFilters;
+ }
+
+ private static byte[] generateDigest(Uri uri) {
+ try {
+ final MessageDigest digest = MessageDigest.getInstance(SHA_ALGORITHM);
+ final byte[] hostBytes = uri.getHost().getBytes();
+ return digest.digest(hostBytes);
+ } catch (NoSuchAlgorithmException e) {
+ throw new IllegalStateException("could not find digest algorithm");
+ }
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel out, int flags) {
+ out.writeByteArray(mDigestBytes);
+ out.writeInt(mDigestPrefix);
+ out.writeString(mPackageName);
+ out.writeList(mFilters);
+ }
+
+ public static final Parcelable.Creator<EphemeralResolveInfo> CREATOR
+ = new Parcelable.Creator<EphemeralResolveInfo>() {
+ public EphemeralResolveInfo createFromParcel(Parcel in) {
+ return new EphemeralResolveInfo(in);
+ }
+
+ public EphemeralResolveInfo[] newArray(int size) {
+ return new EphemeralResolveInfo[size];
+ }
+ };
+
+ /** @hide */
+ public static final class EphemeralResolveIntentInfo extends IntentFilter {
+ private final EphemeralResolveInfo mResolveInfo;
+
+ public EphemeralResolveIntentInfo(@NonNull IntentFilter orig,
+ @NonNull EphemeralResolveInfo resolveInfo) {
+ super(orig);
+ this.mResolveInfo = resolveInfo;
+ }
+
+ public EphemeralResolveInfo getEphemeralResolveInfo() {
+ return mResolveInfo;
+ }
+ }
+}
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index a822150..c40c8a6 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -1763,6 +1763,13 @@
/**
* Feature for {@link #getSystemAvailableFeatures} and {@link #hasSystemFeature}:
+ * The device supports picture-in-picture multi-window mode.
+ */
+ @SdkConstant(SdkConstantType.FEATURE)
+ public static final String FEATURE_PICTURE_IN_PICTURE = "android.software.picture_in_picture";
+
+ /**
+ * 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/ResolveInfo.java b/core/java/android/content/pm/ResolveInfo.java
index a5fb451..d5d3007 100644
--- a/core/java/android/content/pm/ResolveInfo.java
+++ b/core/java/android/content/pm/ResolveInfo.java
@@ -61,6 +61,19 @@
public ProviderInfo providerInfo;
/**
+ * The ephemeral application that corresponds to this resolution match. This will
+ * only be set in specific circumstances.
+ * @hide
+ */
+ public EphemeralResolveInfo ephemeralResolveInfo;
+
+ /**
+ * A ResolveInfo that points at the ephemeral installer.
+ * @hide
+ */
+ public ResolveInfo ephemeralInstaller;
+
+ /**
* The IntentFilter that was matched for this ResolveInfo.
*/
public IntentFilter filter;
diff --git a/core/java/android/content/res/Resources.java b/core/java/android/content/res/Resources.java
index cdca8698..60c6e82 100644
--- a/core/java/android/content/res/Resources.java
+++ b/core/java/android/content/res/Resources.java
@@ -160,6 +160,7 @@
final DisplayMetrics mMetrics = new DisplayMetrics();
private final Configuration mConfiguration = new Configuration();
+ private Locale mResolvedLocale = null;
private PluralRules mPluralRule;
private CompatibilityInfo mCompatibilityInfo = CompatibilityInfo.DEFAULT_COMPATIBILITY_INFO;
@@ -315,6 +316,16 @@
}
/**
+ * Return the Locale resulting from locale negotiation between the Resources and the
+ * Configuration objects used to construct the Resources. The locale is used for retrieving
+ * resources as well as for determining plural rules.
+ */
+ @NonNull
+ public Locale getResolvedLocale() {
+ return mResolvedLocale;
+ }
+
+ /**
* Return the string value associated with a particular resource ID. The
* returned object will be a String if this is a plain string; it will be
* some other type of CharSequence if it is styled.
@@ -378,7 +389,7 @@
private PluralRules getPluralRule() {
synchronized (sSync) {
if (mPluralRule == null) {
- mPluralRule = PluralRules.forLocale(mConfiguration.getLocales().getPrimary());
+ mPluralRule = PluralRules.forLocale(mResolvedLocale);
}
return mPluralRule;
}
@@ -441,7 +452,7 @@
@NonNull
public String getString(@StringRes int id, Object... formatArgs) throws NotFoundException {
final String raw = getString(id);
- return String.format(mConfiguration.getLocales().getPrimary(), raw, formatArgs);
+ return String.format(mResolvedLocale, raw, formatArgs);
}
/**
@@ -472,7 +483,7 @@
public String getQuantityString(@PluralsRes int id, int quantity, Object... formatArgs)
throws NotFoundException {
String raw = getQuantityText(id, quantity).toString();
- return String.format(mConfiguration.getLocales().getPrimary(), raw, formatArgs);
+ return String.format(mResolvedLocale, raw, formatArgs);
}
/**
@@ -1931,8 +1942,10 @@
mCompatibilityInfo.applyToDisplayMetrics(mMetrics);
final int configChanges = calcConfigChanges(config);
+
LocaleList locales = mConfiguration.getLocales();
- if (locales.isEmpty()) {
+ final boolean setLocalesToDefault = locales.isEmpty();
+ if (setLocalesToDefault) {
locales = LocaleList.getDefault();
mConfiguration.setLocales(locales);
}
@@ -1961,9 +1974,12 @@
keyboardHidden = mConfiguration.keyboardHidden;
}
- // TODO: Pass the whole locale list to setConfiguration()
+ if (setLocalesToDefault || mResolvedLocale == null
+ || (configChanges & Configuration.NATIVE_CONFIG_LOCALE) != 0) {
+ mResolvedLocale = locales.getFirstMatch(mAssets.getLocales());
+ }
mAssets.setConfiguration(mConfiguration.mcc, mConfiguration.mnc,
- adjustLanguageTag(locales.getPrimary().toLanguageTag()),
+ adjustLanguageTag(mResolvedLocale.toLanguageTag()),
mConfiguration.orientation,
mConfiguration.touchscreen,
mConfiguration.densityDpi, mConfiguration.keyboard,
@@ -1988,7 +2004,7 @@
}
synchronized (sSync) {
if (mPluralRule != null) {
- mPluralRule = PluralRules.forLocale(mConfiguration.getLocales().getPrimary());
+ mPluralRule = PluralRules.forLocale(mResolvedLocale);
}
}
}
diff --git a/core/java/android/database/sqlite/SQLiteConnection.java b/core/java/android/database/sqlite/SQLiteConnection.java
index 3cda39a..a762f59 100644
--- a/core/java/android/database/sqlite/SQLiteConnection.java
+++ b/core/java/android/database/sqlite/SQLiteConnection.java
@@ -26,6 +26,7 @@
import android.os.CancellationSignal;
import android.os.OperationCanceledException;
import android.os.ParcelFileDescriptor;
+import android.os.Trace;
import android.util.Log;
import android.util.LruCache;
import android.util.Printer;
@@ -1330,6 +1331,10 @@
}
}
operation.mCookie = newOperationCookieLocked(index);
+ if (Trace.isTagEnabled(Trace.TRACE_TAG_DATABASE)) {
+ Trace.asyncTraceBegin(Trace.TRACE_TAG_DATABASE, operation.getTraceMethodName(),
+ operation.mCookie);
+ }
mIndex = index;
return operation.mCookie;
}
@@ -1367,6 +1372,10 @@
private boolean endOperationDeferLogLocked(int cookie) {
final Operation operation = getOperationLocked(cookie);
if (operation != null) {
+ if (Trace.isTagEnabled(Trace.TRACE_TAG_DATABASE)) {
+ Trace.asyncTraceEnd(Trace.TRACE_TAG_DATABASE, operation.getTraceMethodName(),
+ operation.mCookie);
+ }
operation.mEndTime = System.currentTimeMillis();
operation.mFinished = true;
return SQLiteDebug.DEBUG_LOG_SLOW_QUERIES && SQLiteDebug.shouldLogSlowQuery(
@@ -1439,6 +1448,12 @@
}
private static final class Operation {
+ // Trim all SQL statements to 256 characters inside the trace marker.
+ // This limit gives plenty of context while leaving space for other
+ // entries in the trace buffer (and ensures atrace doesn't truncate the
+ // marker for us, potentially losing metadata in the process).
+ private static final int MAX_TRACE_METHOD_NAME_LEN = 256;
+
public long mStartTime;
public long mEndTime;
public String mKind;
@@ -1492,6 +1507,13 @@
return mException != null ? "failed" : "succeeded";
}
+ private String getTraceMethodName() {
+ String methodName = mKind + " " + mSql;
+ if (methodName.length() > MAX_TRACE_METHOD_NAME_LEN)
+ return methodName.substring(0, MAX_TRACE_METHOD_NAME_LEN);
+ return methodName;
+ }
+
private String getFormattedStartTime() {
// Note: SimpleDateFormat is not thread-safe, cannot be compile-time created, and is
// relatively expensive to create during preloading. This method is only used
diff --git a/core/java/android/hardware/input/IInputManager.aidl b/core/java/android/hardware/input/IInputManager.aidl
index 174291e..ff33bd9 100644
--- a/core/java/android/hardware/input/IInputManager.aidl
+++ b/core/java/android/hardware/input/IInputManager.aidl
@@ -24,6 +24,7 @@
import android.os.IBinder;
import android.view.InputDevice;
import android.view.InputEvent;
+import android.view.PointerIcon;
/** @hide */
interface IInputManager {
@@ -71,4 +72,5 @@
void cancelVibrate(int deviceId, IBinder token);
void setPointerIconShape(int shapeId);
+ void setCustomPointerIcon(in PointerIcon icon);
}
diff --git a/core/java/android/hardware/input/InputManager.java b/core/java/android/hardware/input/InputManager.java
index 201afee..fab4718 100644
--- a/core/java/android/hardware/input/InputManager.java
+++ b/core/java/android/hardware/input/InputManager.java
@@ -16,6 +16,7 @@
package android.hardware.input;
+import android.view.PointerIcon;
import com.android.internal.os.SomeArgs;
import com.android.internal.util.ArrayUtils;
@@ -819,6 +820,15 @@
}
}
+ /** @hide */
+ public void setCustomPointerIcon(PointerIcon icon) {
+ try {
+ mIm.setCustomPointerIcon(icon);
+ } catch (RemoteException ex) {
+ // Do nothing.
+ }
+ }
+
private void populateInputDevicesLocked() {
if (mInputDevicesChangedListener == null) {
final InputDevicesChangedListener listener = new InputDevicesChangedListener();
diff --git a/core/java/android/os/Trace.java b/core/java/android/os/Trace.java
index 7529c52..9e8103a 100644
--- a/core/java/android/os/Trace.java
+++ b/core/java/android/os/Trace.java
@@ -79,6 +79,8 @@
public static final long TRACE_TAG_PACKAGE_MANAGER = 1L << 18;
/** @hide */
public static final long TRACE_TAG_SYSTEM_SERVER = 1L << 19;
+ /** @hide */
+ public static final long TRACE_TAG_DATABASE = 1L << 20;
private static final long TRACE_TAG_NOT_READY = 1L << 63;
private static final int MAX_SECTION_NAME_LEN = 127;
diff --git a/core/java/android/os/UserManager.java b/core/java/android/os/UserManager.java
index 1f16c4a..c5adafe 100644
--- a/core/java/android/os/UserManager.java
+++ b/core/java/android/os/UserManager.java
@@ -1631,12 +1631,23 @@
}
/**
- * Returns a Bundle containing any saved application restrictions for this user, for the
+ * Returns a {@code Bundle} containing any saved application restrictions for this user, for the
* given package name. Only an application with this package name can call this method.
+ *
+ * <p>The returned {@link Bundle} consists of key-value pairs, as defined by the application,
+ * where the types of values may be:
+ * <ul>
+ * <li>{@code boolean}
+ * <li>{@code int}
+ * <li>{@code String} or {@code String[]}
+ * <li>From {@link android.os.Build.VERSION_CODES#M}, {@code Bundle} or {@code Bundle[]}
+ * </ul>
+ *
* @param packageName the package name of the calling application
- * @return a Bundle with the restrictions as key/value pairs, or null if there are no
- * saved restrictions. The values can be of type Boolean, String or String[], depending
- * on the restriction type, as defined by the application.
+ * @return a {@code Bundle} with the restrictions for that package, or {@code null} if there
+ * are no saved restrictions.
+ *
+ * @see #KEY_RESTRICTIONS_PENDING
*/
public Bundle getApplicationRestrictions(String packageName) {
try {
diff --git a/core/java/android/provider/DocumentsContract.java b/core/java/android/provider/DocumentsContract.java
index 77a4485..084ff77 100644
--- a/core/java/android/provider/DocumentsContract.java
+++ b/core/java/android/provider/DocumentsContract.java
@@ -660,6 +660,16 @@
}
/**
+ * Builds URI for user home directory on external (local) storage.
+ * {@hide}
+ */
+ public static Uri buildHomeUri() {
+ // TODO: Avoid this type of interpackage copying. Added here to avoid
+ // direct coupling, but not ideal.
+ return DocumentsContract.buildRootUri("com.android.externalstorage.documents", "home");
+ }
+
+ /**
* Build URI representing the recently modified documents of a specific root
* in a document provider. When queried, a provider will return zero or more
* rows with columns defined by {@link Document}.
diff --git a/core/java/android/security/net/config/ManifestConfigSource.java b/core/java/android/security/net/config/ManifestConfigSource.java
index bf1fb8a..0e137cd 100644
--- a/core/java/android/security/net/config/ManifestConfigSource.java
+++ b/core/java/android/security/net/config/ManifestConfigSource.java
@@ -79,7 +79,9 @@
if (DBG) {
Log.d(LOG_TAG, "No Network Security Config specified, using platform default");
}
- source = new DefaultConfigSource();
+ boolean usesCleartextTraffic =
+ (info.flags & ApplicationInfo.FLAG_USES_CLEARTEXT_TRAFFIC) != 0;
+ source = new DefaultConfigSource(usesCleartextTraffic);
}
mConfigSource = source;
return mConfigSource;
@@ -87,9 +89,18 @@
}
private static final class DefaultConfigSource implements ConfigSource {
+
+ private final NetworkSecurityConfig mDefaultConfig;
+
+ public DefaultConfigSource(boolean usesCleartextTraffic) {
+ mDefaultConfig = NetworkSecurityConfig.getDefaultBuilder()
+ .setCleartextTrafficPermitted(usesCleartextTraffic)
+ .build();
+ }
+
@Override
public NetworkSecurityConfig getDefaultConfig() {
- return NetworkSecurityConfig.DEFAULT;
+ return mDefaultConfig;
}
@Override
diff --git a/core/java/android/security/net/config/RootTrustManager.java b/core/java/android/security/net/config/RootTrustManager.java
index e307ad0..b4e58e6 100644
--- a/core/java/android/security/net/config/RootTrustManager.java
+++ b/core/java/android/security/net/config/RootTrustManager.java
@@ -71,6 +71,10 @@
*/
public List<X509Certificate> checkServerTrusted(X509Certificate[] certs, String authType,
String hostname) throws CertificateException {
+ if (hostname == null && mConfig.hasPerDomainConfigs()) {
+ throw new CertificateException(
+ "Domain specific configurations require that the hostname be provided");
+ }
NetworkSecurityConfig config = mConfig.getConfigForHostname(hostname);
return config.getTrustManager().checkServerTrusted(certs, authType, hostname);
}
diff --git a/core/java/android/text/Hyphenator.java b/core/java/android/text/Hyphenator.java
index 5d9d929..568b267 100644
--- a/core/java/android/text/Hyphenator.java
+++ b/core/java/android/text/Hyphenator.java
@@ -72,10 +72,20 @@
return result;
}
- // TODO: Convert this a proper locale-fallback system
+ // If there's a variant, fall back to language+variant only, if available
+ final String variant = locale.getVariant();
+ if (!variant.isEmpty()) {
+ final Locale languageAndVariantOnlyLocale =
+ new Locale(locale.getLanguage(), "", variant);
+ result = sMap.get(languageAndVariantOnlyLocale);
+ if (result != null) {
+ sMap.put(locale, result);
+ return result;
+ }
+ }
// Fall back to language-only, if available
- Locale languageOnlyLocale = new Locale(locale.getLanguage());
+ final Locale languageOnlyLocale = new Locale(locale.getLanguage());
result = sMap.get(languageOnlyLocale);
if (result != null) {
sMap.put(locale, result);
@@ -83,9 +93,9 @@
}
// Fall back to script-only, if available
- String script = locale.getScript();
+ final String script = locale.getScript();
if (!script.equals("")) {
- Locale scriptOnlyLocale = new Locale.Builder()
+ final Locale scriptOnlyLocale = new Locale.Builder()
.setLanguage("und")
.setScript(script)
.build();
@@ -142,6 +152,11 @@
{"en-UM", "en-US"}, // English (United States Minor Outlying Islands)
{"en-VI", "en-US"}, // English (Virgin Islands)
+ // For German, we're assuming the 1996 (and later) orthography by default.
+ {"de", "de-1996"},
+ // Liechtenstein uses the Swiss hyphenation rules for the 1901 orthography.
+ {"de-LI-1901", "de-CH-1901"},
+
// Norwegian is very probably Norwegian Bokmål.
{"no", "nb"},
@@ -166,7 +181,18 @@
sMap.put(null, null);
// TODO: replace this with a discovery-based method that looks into /system/usr/hyphen-data
- String[] availableLanguages = {"en-US", "es", "eu", "hu", "hy", "nb", "nn", "und-Ethi"};
+ String[] availableLanguages = {
+ "de-1901", "de-1996", "de-CH-1901",
+ "en-US",
+ "es",
+ "eu",
+ "hu",
+ "hy",
+ "nb",
+ "nn",
+ "pt",
+ "und-Ethi"
+ };
for (int i = 0; i < availableLanguages.length; i++) {
String languageTag = availableLanguages[i];
Hyphenator h = loadHyphenator(languageTag);
diff --git a/core/java/android/view/InputDevice.java b/core/java/android/view/InputDevice.java
index 46b9a46..88cd7ca 100644
--- a/core/java/android/view/InputDevice.java
+++ b/core/java/android/view/InputDevice.java
@@ -784,6 +784,15 @@
}
/**
+ * Specifies the current custom pointer.
+ * @param icon the icon data.
+ * @hide
+ */
+ public void setCustomPointerIcon(PointerIcon icon) {
+ InputManager.getInstance().setCustomPointerIcon(icon);
+ }
+
+ /**
* 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 55cf56f..f037958 100644
--- a/core/java/android/view/KeyEvent.java
+++ b/core/java/android/view/KeyEvent.java
@@ -1779,6 +1779,7 @@
switch (keyCode) {
case KeyEvent.KEYCODE_DPAD_CENTER:
case KeyEvent.KEYCODE_ENTER:
+ case KeyEvent.KEYCODE_SPACE:
return true;
default:
return false;
diff --git a/core/java/android/view/PointerIcon.java b/core/java/android/view/PointerIcon.java
index d2a7d4a..7ba046b 100644
--- a/core/java/android/view/PointerIcon.java
+++ b/core/java/android/view/PointerIcon.java
@@ -16,8 +16,8 @@
package android.view;
-import android.os.UserHandle;
-import android.provider.Settings;
+import android.annotation.NonNull;
+import android.util.SparseArray;
import com.android.internal.util.XmlUtils;
import android.annotation.XmlRes;
@@ -39,13 +39,11 @@
* Pointer icons can be provided either by the system using system styles,
* or by applications using bitmaps or application resources.
* </p>
- *
- * @hide
*/
public final class PointerIcon implements Parcelable {
private static final String TAG = "PointerIcon";
- /** Style constant: Custom icon with a user-supplied bitmap. */
+ /** {@hide} Style constant: Custom icon with a user-supplied bitmap. */
public static final int STYLE_CUSTOM = -1;
/** Style constant: Null icon. It has no bitmap. */
@@ -54,6 +52,7 @@
/** 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.
+ * @hide
*/
public static final int STYLE_NOT_SPECIFIED = 1;
@@ -135,10 +134,14 @@
// conflicts with any system styles that may be defined in the future.
private static final int STYLE_OEM_FIRST = 10000;
- /** {@hide} The default pointer icon. */
+ /** The default pointer icon. */
public static final int STYLE_DEFAULT = STYLE_ARROW;
private static final PointerIcon gNullIcon = new PointerIcon(STYLE_NULL);
+ private static final SparseArray<PointerIcon> gSystemIcons = new SparseArray<PointerIcon>();
+
+ /** @hide */
+ public static boolean sUseLargeIcons = false;
private final int mStyle;
private int mSystemIconResourceId;
@@ -160,6 +163,7 @@
* @return The null pointer icon.
*
* @see #STYLE_NULL
+ * @hide
*/
public static PointerIcon getNullIcon() {
return gNullIcon;
@@ -172,8 +176,9 @@
* @return The default pointer icon.
*
* @throws IllegalArgumentException if context is null.
+ * @hide
*/
- public static PointerIcon getDefaultIcon(Context context) {
+ public static PointerIcon getDefaultIcon(@NonNull Context context) {
return getSystemIcon(context, STYLE_DEFAULT);
}
@@ -187,7 +192,7 @@
*
* @throws IllegalArgumentException if context is null.
*/
- public static PointerIcon getSystemIcon(Context context, int style) {
+ public static PointerIcon getSystemIcon(@NonNull Context context, int style) {
if (context == null) {
throw new IllegalArgumentException("context must not be null");
}
@@ -196,15 +201,17 @@
return gNullIcon;
}
+ PointerIcon icon = gSystemIcons.get(style);
+ if (icon != null) {
+ return icon;
+ }
+
int styleIndex = getSystemIconStyleIndex(style);
if (styleIndex == 0) {
styleIndex = getSystemIconStyleIndex(STYLE_DEFAULT);
}
- int accessibilityConfig = Settings.Secure.getIntForUser(
- context.getContentResolver(), Settings.Secure.ACCESSIBILITY_LARGE_POINTER_ICON,
- 0, UserHandle.USER_CURRENT);
- int defStyle = (accessibilityConfig == 1) ?
+ int defStyle = sUseLargeIcons ?
com.android.internal.R.style.LargePointer : com.android.internal.R.style.Pointer;
TypedArray a = context.obtainStyledAttributes(null,
com.android.internal.R.styleable.Pointer,
@@ -217,12 +224,13 @@
return style == STYLE_DEFAULT ? gNullIcon : getSystemIcon(context, STYLE_DEFAULT);
}
- PointerIcon icon = new PointerIcon(style);
+ icon = new PointerIcon(style);
if ((resourceId & 0xff000000) == 0x01000000) {
icon.mSystemIconResourceId = resourceId;
} else {
icon.loadResource(context, context.getResources(), resourceId);
}
+ gSystemIcons.append(style, icon);
return icon;
}
@@ -239,7 +247,8 @@
* @throws IllegalArgumentException if bitmap is null, or if the x/y hotspot
* parameters are invalid.
*/
- public static PointerIcon createCustomIcon(Bitmap bitmap, float hotSpotX, float hotSpotY) {
+ public static PointerIcon createCustomIcon(
+ @NonNull Bitmap bitmap, float hotSpotX, float hotSpotY) {
if (bitmap == null) {
throw new IllegalArgumentException("bitmap must not be null");
}
@@ -273,7 +282,7 @@
* @throws Resources.NotFoundException if the resource was not found or the drawable
* linked in the resource was not found.
*/
- public static PointerIcon loadCustomIcon(Resources resources, @XmlRes int resourceId) {
+ public static PointerIcon loadCustomIcon(@NonNull Resources resources, @XmlRes int resourceId) {
if (resources == null) {
throw new IllegalArgumentException("resources must not be null");
}
@@ -291,10 +300,9 @@
* @return The loaded pointer icon.
*
* @throws IllegalArgumentException if context is null.
- * @see #isLoaded()
* @hide
*/
- public PointerIcon load(Context context) {
+ public PointerIcon load(@NonNull Context context) {
if (context == null) {
throw new IllegalArgumentException("context must not be null");
}
@@ -309,83 +317,11 @@
return result;
}
- /**
- * Returns true if the pointer icon style is {@link #STYLE_NULL}.
- *
- * @return True if the pointer icon style is {@link #STYLE_NULL}.
- */
- public boolean isNullIcon() {
- return mStyle == STYLE_NULL;
- }
-
- /**
- * Returns true if the pointer icon has been loaded and its bitmap and hotspot
- * information are available.
- *
- * @return True if the pointer icon is loaded.
- * @see #load(Context)
- */
- public boolean isLoaded() {
- return mBitmap != null || mStyle == STYLE_NULL;
- }
-
- /**
- * Gets the style of the pointer icon.
- *
- * @return The pointer icon style.
- */
+ /** @hide */
public int getStyle() {
return mStyle;
}
- /**
- * Gets the bitmap of the pointer icon.
- *
- * @return The pointer icon bitmap, or null if the style is {@link #STYLE_NULL}.
- *
- * @throws IllegalStateException if the bitmap is not loaded.
- * @see #isLoaded()
- * @see #load(Context)
- */
- public Bitmap getBitmap() {
- throwIfIconIsNotLoaded();
- return mBitmap;
- }
-
- /**
- * Gets the X offset of the pointer icon hotspot.
- *
- * @return The hotspot X offset.
- *
- * @throws IllegalStateException if the bitmap is not loaded.
- * @see #isLoaded()
- * @see #load(Context)
- */
- public float getHotSpotX() {
- throwIfIconIsNotLoaded();
- return mHotSpotX;
- }
-
- /**
- * Gets the Y offset of the pointer icon hotspot.
- *
- * @return The hotspot Y offset.
- *
- * @throws IllegalStateException if the bitmap is not loaded.
- * @see #isLoaded()
- * @see #load(Context)
- */
- public float getHotSpotY() {
- throwIfIconIsNotLoaded();
- return mHotSpotY;
- }
-
- private void throwIfIconIsNotLoaded() {
- if (!isLoaded()) {
- throw new IllegalStateException("The icon is not loaded.");
- }
- }
-
public static final Parcelable.Creator<PointerIcon> CREATOR
= new Parcelable.Creator<PointerIcon>() {
public PointerIcon createFromParcel(Parcel in) {
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 183ccf3..afa6c78 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -2628,7 +2628,7 @@
static final int PFLAG3_POINTER_ICON_LSHIFT = 15;
/**
- * Value indicating {@link PointerIcon.STYLE_NOT_SPECIFIED}.
+ * Value indicating no specific pointer icons.
*/
private static final int PFLAG3_POINTER_ICON_NOT_SPECIFIED = 0 << PFLAG3_POINTER_ICON_LSHIFT;
@@ -2638,14 +2638,9 @@
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;
+ private static final int PFLAG3_POINTER_ICON_VALUE_START = 2 << PFLAG3_POINTER_ICON_LSHIFT;
/**
* Always allow a user to over-scroll this view, provided it is a
@@ -3927,6 +3922,11 @@
private HandlerActionQueue mRunQueue;
/**
+ * The pointer icon when the mouse hovers on this view. The default is null.
+ */
+ private PointerIcon mPointerIcon;
+
+ /**
* @hide
*/
String mStartActivityRequestWho;
@@ -4487,6 +4487,12 @@
initializeScrollIndicators = true;
}
break;
+ case R.styleable.View_pointerShape:
+ final int pointerShape = a.getInt(attr, PointerIcon.STYLE_NOT_SPECIFIED);
+ if (pointerShape != PointerIcon.STYLE_NOT_SPECIFIED) {
+ setPointerIcon(PointerIcon.getSystemIcon(context, pointerShape));
+ }
+ break;
}
}
@@ -10156,8 +10162,8 @@
/**
* Default implementation of {@link KeyEvent.Callback#onKeyUp(int, KeyEvent)
* KeyEvent.Callback.onKeyUp()}: perform clicking of the view
- * when {@link KeyEvent#KEYCODE_DPAD_CENTER} or
- * {@link KeyEvent#KEYCODE_ENTER} is released.
+ * when {@link KeyEvent#KEYCODE_DPAD_CENTER}, {@link KeyEvent#KEYCODE_ENTER}
+ * or {@link KeyEvent#KEYCODE_SPACE} is released.
* <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.
@@ -21185,42 +21191,25 @@
}
}
- /** @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;
- }
+ /**
+ * Returns the pointer icon for the motion event, or null if it doesn't specify the icon.
+ * The default implementation does not care the location or event types, but some subclasses
+ * may use it (such as WebViews).
+ * @param event The MotionEvent from a mouse
+ * @param x The x position of the event, local to the view
+ * @param y The y position of the event, local to the view
+ * @see PointerIcon
+ */
+ public PointerIcon getPointerIcon(MotionEvent event, float x, float y) {
+ return mPointerIcon;
}
- /** @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;
- }
+ /**
+ * Set the pointer icon for the current view.
+ * @param pointerIcon A PointerIcon instance which will be shown when the mouse hovers.
+ */
+ public void setPointerIcon(PointerIcon pointerIcon) {
+ mPointerIcon = pointerIcon;
}
//
diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java
index 0f7d296..cd93dab 100644
--- a/core/java/android/view/ViewGroup.java
+++ b/core/java/android/view/ViewGroup.java
@@ -1715,9 +1715,8 @@
return false;
}
- /** @hide */
@Override
- public int getPointerShape(MotionEvent event, float x, float y) {
+ public PointerIcon getPointerIcon(MotionEvent event, float x, float y) {
// Check what the child under the pointer says about the pointer.
final int childrenCount = mChildrenCount;
if (childrenCount != 0) {
@@ -1731,9 +1730,9 @@
? 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;
+ final PointerIcon pointerIcon = child.getPointerIcon(event, point.x, point.y);
+ if (pointerIcon != null) {
+ return pointerIcon;
}
break;
}
@@ -1742,7 +1741,7 @@
// The pointer is not a child or the child has no preferences, returning the default
// implementation.
- return super.getPointerShape(event, x, y);
+ return super.getPointerIcon(event, x, y);
}
/**
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index 12cf66e..faf2640 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -332,6 +332,7 @@
private int mFpsNumFrames;
private int mPointerIconShape = PointerIcon.STYLE_NOT_SPECIFIED;
+ private PointerIcon mCustomPointerIcon = null;
/**
* see {@link #playSoundEffect(int)}
@@ -4210,16 +4211,23 @@
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;
- }
+ PointerIcon pointerIcon = mView.getPointerIcon(event, x, y);
+ int pointerShape = (pointerIcon != null) ?
+ pointerIcon.getStyle() : PointerIcon.STYLE_DEFAULT;
- if (mPointerIconShape != pointerShape) {
- mPointerIconShape = pointerShape;
- final InputDevice inputDevice = event.getDevice();
- if (inputDevice != null) {
- inputDevice.setPointerShape(pointerShape);
+ final InputDevice inputDevice = event.getDevice();
+ if (inputDevice != null) {
+ if (mPointerIconShape != pointerShape) {
+ mPointerIconShape = pointerShape;
+ if (mPointerIconShape != PointerIcon.STYLE_CUSTOM) {
+ mCustomPointerIcon = null;
+ inputDevice.setPointerShape(pointerShape);
+ }
+ }
+ if (mPointerIconShape == PointerIcon.STYLE_CUSTOM &&
+ !pointerIcon.equals(mCustomPointerIcon)) {
+ mCustomPointerIcon = pointerIcon;
+ inputDevice.setCustomPointerIcon(mCustomPointerIcon);
}
}
} else if (event.getActionMasked() == MotionEvent.ACTION_HOVER_MOVE) {
diff --git a/core/java/android/view/Window.java b/core/java/android/view/Window.java
index 53490b4..d7a98ab 100644
--- a/core/java/android/view/Window.java
+++ b/core/java/android/view/Window.java
@@ -2106,4 +2106,10 @@
* to better match your application.
*/
public abstract void setResizingCaptionDrawable(Drawable drawable);
+
+ /**
+ * Called when the activity changes from fullscreen mode to multi-window mode and visa-versa.
+ * @hide
+ */
+ public abstract void onMultiWindowModeChanged();
}
diff --git a/core/java/android/view/WindowManager.java b/core/java/android/view/WindowManager.java
index d6bc27c..2e884cc 100644
--- a/core/java/android/view/WindowManager.java
+++ b/core/java/android/view/WindowManager.java
@@ -1171,6 +1171,16 @@
*/
public static final int PRIVATE_FLAG_FORCE_DECOR_VIEW_VISIBILITY = 0x00004000;
+ /**
+ * Flag to indicate that this window is not expected to be replaced across
+ * configuration change triggered activity relaunches. In general the WindowManager
+ * expects Windows to be replaced after relaunch, and thus it will preserve their surfaces
+ * until the replacement is ready to show in order to prevent visual glitch. However
+ * some windows, such as PopupWindows expect to be cleared across configuration change,
+ * and thus should hint to the WindowManager that it should not wait for a replacement.
+ * @hide
+ */
+ public static final int PRIVATE_FLAG_WILL_NOT_REPLACE_ON_RELAUNCH = 0x00008000;
/**
* Control flags that are private to the platform.
diff --git a/core/java/android/view/WindowManagerPolicy.java b/core/java/android/view/WindowManagerPolicy.java
index b1a8479..01bfbb5 100644
--- a/core/java/android/view/WindowManagerPolicy.java
+++ b/core/java/android/view/WindowManagerPolicy.java
@@ -446,6 +446,13 @@
*/
public void switchKeyboardLayout(int deviceId, int direction);
+ /**
+ * Switch the input method, to be precise, input method subtype.
+ *
+ * @param forwardDirection {@code true} to rotate in a forward direction.
+ */
+ public void switchInputMethod(boolean forwardDirection);
+
public void shutdown(boolean confirm);
public void rebootSafeMode(boolean confirm);
diff --git a/core/java/android/view/inputmethod/InputMethodManager.java b/core/java/android/view/inputmethod/InputMethodManager.java
index 329d1b0..5e07347 100644
--- a/core/java/android/view/inputmethod/InputMethodManager.java
+++ b/core/java/android/view/inputmethod/InputMethodManager.java
@@ -54,6 +54,7 @@
import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@@ -2209,7 +2210,7 @@
p.println(" mCurrentTextBoxAttribute: null");
}
p.println(" mServedInputConnection=" + mServedInputConnection);
- p.println(" mCompletions=" + mCompletions);
+ p.println(" mCompletions=" + Arrays.toString(mCompletions));
p.println(" mCursorRect=" + mCursorRect);
p.println(" mCursorSelStart=" + mCursorSelStart
+ " mCursorSelEnd=" + mCursorSelEnd
diff --git a/core/java/android/view/inputmethod/InputMethodManagerInternal.java b/core/java/android/view/inputmethod/InputMethodManagerInternal.java
index c22127b..ce9908c 100644
--- a/core/java/android/view/inputmethod/InputMethodManagerInternal.java
+++ b/core/java/android/view/inputmethod/InputMethodManagerInternal.java
@@ -26,5 +26,10 @@
* Called by the power manager to tell the input method manager whether it
* should start watching for wake events.
*/
- public void setInteractive(boolean interactive);
+ void setInteractive(boolean interactive);
+
+ /**
+ * Called by the window manager to let the input method manager rotate the input method.
+ */
+ void switchInputMethod(boolean forwardDirection);
}
diff --git a/core/java/android/widget/GridView.java b/core/java/android/widget/GridView.java
index 607e955..ee73092 100644
--- a/core/java/android/widget/GridView.java
+++ b/core/java/android/widget/GridView.java
@@ -1643,8 +1643,16 @@
boolean handled = false;
int action = event.getAction();
+ if (KeyEvent.isConfirmKey(keyCode)
+ && event.hasNoModifiers() && action == KeyEvent.ACTION_UP) {
+ handled = resurrectSelectionIfNeeded();
+ if (!handled && event.getRepeatCount() == 0 && getChildCount() > 0) {
+ keyPressed();
+ handled = true;
+ }
+ }
- if (action != KeyEvent.ACTION_UP) {
+ if (!handled && action != KeyEvent.ACTION_UP) {
switch (keyCode) {
case KeyEvent.KEYCODE_DPAD_LEFT:
if (event.hasNoModifiers()) {
@@ -1674,28 +1682,6 @@
}
break;
- case KeyEvent.KEYCODE_DPAD_CENTER:
- case KeyEvent.KEYCODE_ENTER:
- if (event.hasNoModifiers()) {
- handled = resurrectSelectionIfNeeded();
- if (!handled
- && event.getRepeatCount() == 0 && getChildCount() > 0) {
- keyPressed();
- handled = true;
- }
- }
- break;
-
- case KeyEvent.KEYCODE_SPACE:
- if (mPopup == null || !mPopup.isShowing()) {
- if (event.hasNoModifiers()) {
- handled = resurrectSelectionIfNeeded() || pageScroll(FOCUS_DOWN);
- } else if (event.hasModifiers(KeyEvent.META_SHIFT_ON)) {
- handled = resurrectSelectionIfNeeded() || pageScroll(FOCUS_UP);
- }
- }
- break;
-
case KeyEvent.KEYCODE_PAGE_UP:
if (event.hasNoModifiers()) {
handled = resurrectSelectionIfNeeded() || pageScroll(FOCUS_UP);
diff --git a/core/java/android/widget/ListView.java b/core/java/android/widget/ListView.java
index 53ca6d1..064808b 100644
--- a/core/java/android/widget/ListView.java
+++ b/core/java/android/widget/ListView.java
@@ -2180,8 +2180,17 @@
boolean handled = false;
int action = event.getAction();
+ if (KeyEvent.isConfirmKey(keyCode)
+ && event.hasNoModifiers() && action == KeyEvent.ACTION_UP) {
+ handled = resurrectSelectionIfNeeded();
+ if (!handled && event.getRepeatCount() == 0 && getChildCount() > 0) {
+ keyPressed();
+ handled = true;
+ }
+ }
- if (action != KeyEvent.ACTION_UP) {
+
+ if (!handled && action != KeyEvent.ACTION_UP) {
switch (keyCode) {
case KeyEvent.KEYCODE_DPAD_UP:
if (event.hasNoModifiers()) {
@@ -2229,29 +2238,6 @@
}
break;
- case KeyEvent.KEYCODE_DPAD_CENTER:
- case KeyEvent.KEYCODE_ENTER:
- if (event.hasNoModifiers()) {
- handled = resurrectSelectionIfNeeded();
- if (!handled
- && event.getRepeatCount() == 0 && getChildCount() > 0) {
- keyPressed();
- handled = true;
- }
- }
- break;
-
- case KeyEvent.KEYCODE_SPACE:
- if (mPopup == null || !mPopup.isShowing()) {
- if (event.hasNoModifiers()) {
- handled = resurrectSelectionIfNeeded() || pageScroll(FOCUS_DOWN);
- } else if (event.hasModifiers(KeyEvent.META_SHIFT_ON)) {
- handled = resurrectSelectionIfNeeded() || pageScroll(FOCUS_UP);
- }
- handled = true;
- }
- break;
-
case KeyEvent.KEYCODE_PAGE_UP:
if (event.hasNoModifiers()) {
handled = resurrectSelectionIfNeeded() || pageScroll(FOCUS_UP);
diff --git a/core/java/android/widget/PopupWindow.java b/core/java/android/widget/PopupWindow.java
index 7b9de79..f4c343a 100644
--- a/core/java/android/widget/PopupWindow.java
+++ b/core/java/android/widget/PopupWindow.java
@@ -16,6 +16,8 @@
package android.widget;
+import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_WILL_NOT_REPLACE_ON_RELAUNCH;
+
import com.android.internal.R;
import android.annotation.NonNull;
@@ -1311,6 +1313,8 @@
p.width = mLastWidth = mWidth;
}
+ p.privateFlags = PRIVATE_FLAG_WILL_NOT_REPLACE_ON_RELAUNCH;
+
// Used for debugging.
p.setTitle("PopupWindow:" + Integer.toHexString(hashCode()));
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index 5574f86..17c803f 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -5955,15 +5955,12 @@
return mLayout != null ? mLayout.getHeight() : 0;
}
- /**
- * @hide
- */
@Override
- public int getPointerShape(MotionEvent event, float x, float y) {
+ public PointerIcon getPointerIcon(MotionEvent event, float x, float y) {
if (isTextSelectable() || isTextEditable()) {
- return PointerIcon.STYLE_TEXT;
+ return PointerIcon.getSystemIcon(mContext, PointerIcon.STYLE_TEXT);
}
- return super.getPointerShape(event, x, y);
+ return super.getPointerIcon(event, x, y);
}
@Override
diff --git a/core/java/com/android/internal/app/AlertController.java b/core/java/com/android/internal/app/AlertController.java
index 61ee00c..66374a6 100644
--- a/core/java/com/android/internal/app/AlertController.java
+++ b/core/java/com/android/internal/app/AlertController.java
@@ -359,6 +359,7 @@
if (mIconView != null) {
if (resId != 0) {
+ mIconView.setVisibility(View.VISIBLE);
mIconView.setImageResource(mIconId);
} else {
mIconView.setVisibility(View.GONE);
diff --git a/core/java/com/android/internal/app/EphemeralResolveInfo.java b/core/java/com/android/internal/app/EphemeralResolveInfo.java
deleted file mode 100644
index 0e7ef05..0000000
--- a/core/java/com/android/internal/app/EphemeralResolveInfo.java
+++ /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.
- */
-
-package com.android.internal.app;
-
-import android.content.IntentFilter;
-import android.net.Uri;
-import android.os.Parcel;
-import android.os.Parcelable;
-
-import java.security.MessageDigest;
-import java.security.NoSuchAlgorithmException;
-import java.util.ArrayList;
-import java.util.List;
-
-/**
- * Information that is returned when resolving ephemeral
- * applications.
- */
-public final class EphemeralResolveInfo implements Parcelable {
- public static final String SHA_ALGORITHM = "SHA-256";
- private byte[] mDigestBytes;
- private int mDigestPrefix;
- private final List<IntentFilter> mFilters = new ArrayList<IntentFilter>();
-
- public EphemeralResolveInfo(Uri uri, List<IntentFilter> filters) {
- generateDigest(uri);
- mFilters.addAll(filters);
- }
-
- private EphemeralResolveInfo(Parcel in) {
- readFromParcel(in);
- }
-
- public byte[] getDigestBytes() {
- return mDigestBytes;
- }
-
- public int getDigestPrefix() {
- return mDigestPrefix;
- }
-
- public List<IntentFilter> getFilters() {
- return mFilters;
- }
-
- private void generateDigest(Uri uri) {
- try {
- final MessageDigest digest = MessageDigest.getInstance(SHA_ALGORITHM);
- final byte[] hostBytes = uri.getHost().getBytes();
- final byte[] digestBytes = digest.digest(hostBytes);
- mDigestBytes = digestBytes;
- mDigestPrefix =
- digestBytes[0] << 24
- | digestBytes[1] << 16
- | digestBytes[2] << 8
- | digestBytes[3] << 0;
- } catch (NoSuchAlgorithmException e) {
- throw new IllegalStateException("could not find digest algorithm");
- }
- }
-
- @Override
- public int describeContents() {
- return 0;
- }
-
- @Override
- public void writeToParcel(Parcel out, int flags) {
- if (mDigestBytes == null) {
- out.writeInt(0);
- } else {
- out.writeInt(mDigestBytes.length);
- out.writeByteArray(mDigestBytes);
- }
- out.writeInt(mDigestPrefix);
- out.writeList(mFilters);
- }
-
- private void readFromParcel(Parcel in) {
- int digestBytesSize = in.readInt();
- if (digestBytesSize > 0) {
- mDigestBytes = new byte[digestBytesSize];
- in.readByteArray(mDigestBytes);
- }
- mDigestPrefix = in.readInt();
- in.readList(mFilters, null /*loader*/);
- }
-
- public static final Parcelable.Creator<EphemeralResolveInfo> CREATOR
- = new Parcelable.Creator<EphemeralResolveInfo>() {
- public EphemeralResolveInfo createFromParcel(Parcel in) {
- return new EphemeralResolveInfo(in);
- }
-
- public EphemeralResolveInfo[] newArray(int size) {
- return new EphemeralResolveInfo[size];
- }
- };
-}
diff --git a/core/java/com/android/internal/app/EphemeralResolverService.java b/core/java/com/android/internal/app/EphemeralResolverService.java
index 65530f2..6ba04a9 100644
--- a/core/java/com/android/internal/app/EphemeralResolverService.java
+++ b/core/java/com/android/internal/app/EphemeralResolverService.java
@@ -16,9 +16,11 @@
package com.android.internal.app;
+import android.annotation.SystemApi;
import android.app.Service;
import android.content.Context;
import android.content.Intent;
+import android.content.pm.EphemeralResolveInfo;
import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
@@ -33,6 +35,7 @@
* Base class for implementing the resolver service.
* @hide
*/
+@SystemApi
public abstract class EphemeralResolverService extends Service {
public static final String EXTRA_RESOLVE_INFO = "com.android.internal.app.RESOLVE_INFO";
public static final String EXTRA_SEQUENCE = "com.android.internal.app.SEQUENCE";
diff --git a/core/java/com/android/internal/policy/DecorView.java b/core/java/com/android/internal/policy/DecorView.java
index e405564..6a1e07b 100644
--- a/core/java/com/android/internal/policy/DecorView.java
+++ b/core/java/com/android/internal/policy/DecorView.java
@@ -17,11 +17,13 @@
package com.android.internal.policy;
import com.android.internal.R;
+import com.android.internal.policy.PhoneWindow.PhoneWindowMenuCallback;
import com.android.internal.view.FloatingActionMode;
import com.android.internal.view.RootViewSurfaceTaker;
import com.android.internal.view.StandaloneActionMode;
import com.android.internal.view.menu.ContextMenuBuilder;
import com.android.internal.view.menu.MenuHelper;
+import com.android.internal.view.menu.MenuPresenter;
import com.android.internal.widget.ActionBarContextView;
import com.android.internal.widget.BackgroundFallback;
import com.android.internal.widget.DecorCaptionView;
@@ -67,6 +69,7 @@
import android.widget.FrameLayout;
import android.widget.PopupWindow;
+import static android.app.ActivityManager.StackId;
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;
@@ -684,9 +687,10 @@
}
// Reuse the context menu builder.
+ final PhoneWindowMenuCallback callback = mWindow.mContextMenuCallback;
if (mWindow.mContextMenu == null) {
mWindow.mContextMenu = new ContextMenuBuilder(getContext());
- mWindow.mContextMenu.setCallback(mWindow.mContextMenuCallback);
+ mWindow.mContextMenu.setCallback(callback);
} else {
mWindow.mContextMenu.clearAll();
}
@@ -698,9 +702,11 @@
helper = mWindow.mContextMenu.showDialog(originalView, originalView.getWindowToken());
}
- if (helper != null) {
- helper.setPresenterCallback(mWindow.mContextMenuCallback);
- }
+ // If it's a dialog, the callback needs to handle showing sub-menus.
+ // Either way, the callback is required for propagating selection to
+ // Context.onContextMenuItemSelected().
+ callback.setShowDialogForSubmenu(!isPopup);
+ helper.setPresenterCallback(callback);
mWindow.mContextMenuHelper = helper;
return helper != null;
@@ -1188,7 +1194,7 @@
invalidate();
int opacity = PixelFormat.OPAQUE;
- if (ActivityManager.StackId.hasWindowShadow(mStackId)) {
+ if (StackId.hasWindowShadow(mStackId)) {
// If the window has a shadow, it must be translucent.
opacity = PixelFormat.TRANSLUCENT;
} else{
@@ -1571,13 +1577,25 @@
void onConfigurationChanged() {
int workspaceId = getStackId();
- if (mDecorCaptionView != null) {
- if (mStackId != workspaceId) {
- mStackId = workspaceId;
+ if (mStackId != workspaceId) {
+ mStackId = workspaceId;
+ if (mDecorCaptionView == null && StackId.hasWindowDecor(mStackId)) {
+ // Configuration now requires a caption.
+ final LayoutInflater inflater = mWindow.getLayoutInflater();
+ mDecorCaptionView = createDecorCaptionView(inflater);
+ if (mDecorCaptionView != null) {
+ if (mDecorCaptionView.getParent() == null) {
+ addView(mDecorCaptionView, 0,
+ new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT));
+ }
+ removeView(mContentRoot);
+ mDecorCaptionView.addView(mContentRoot,
+ new ViewGroup.MarginLayoutParams(MATCH_PARENT, MATCH_PARENT));
+ }
+ } else if (mDecorCaptionView != null) {
// We might have to change the kind of surface before we do anything else.
- mDecorCaptionView.onConfigurationChanged(
- ActivityManager.StackId.hasWindowDecor(mStackId));
- enableCaption(ActivityManager.StackId.hasWindowDecor(workspaceId));
+ mDecorCaptionView.onConfigurationChanged(StackId.hasWindowDecor(mStackId));
+ enableCaption(StackId.hasWindowDecor(workspaceId));
}
}
initializeElevation();
@@ -1630,8 +1648,7 @@
final boolean isApplication = attrs.type == TYPE_BASE_APPLICATION ||
attrs.type == TYPE_APPLICATION;
// Only a non floating application window on one of the allowed workspaces can get a caption
- if (!mWindow.isFloating() && isApplication
- && ActivityManager.StackId.hasWindowDecor(mStackId)) {
+ if (!mWindow.isFloating() && isApplication && StackId.hasWindowDecor(mStackId)) {
// Dependent on the brightness of the used title we either use the
// dark or the light button frame.
if (decorCaptionView == null) {
@@ -1854,7 +1871,7 @@
final boolean wasAdjustedForStack = mElevationAdjustedForStack;
// Do not use a shadow when we are in resizing mode (mBackdropFrameRenderer not null)
// since the shadow is bound to the content size and not the target size.
- if (ActivityManager.StackId.hasWindowShadow(mStackId) && !isResizing()) {
+ if (StackId.hasWindowShadow(mStackId) && !isResizing()) {
elevation = hasWindowFocus() ?
DECOR_SHADOW_FOCUSED_HEIGHT_IN_DIP : DECOR_SHADOW_UNFOCUSED_HEIGHT_IN_DIP;
// TODO(skuhne): Remove this if clause once b/22668382 got fixed.
diff --git a/core/java/com/android/internal/policy/PhoneWindow.java b/core/java/com/android/internal/policy/PhoneWindow.java
index 86bd782..2d8bfd4 100644
--- a/core/java/com/android/internal/policy/PhoneWindow.java
+++ b/core/java/com/android/internal/policy/PhoneWindow.java
@@ -127,7 +127,7 @@
* Simple callback used by the context menu and its submenus. The options
* menu submenus do not use this (their behavior is more complex).
*/
- final DialogMenuCallback mContextMenuCallback = new DialogMenuCallback(FEATURE_CONTEXT_MENU);
+ final PhoneWindowMenuCallback mContextMenuCallback = new PhoneWindowMenuCallback(this);
final TypedValue mMinWidthMajor = new TypedValue();
final TypedValue mMinWidthMinor = new TypedValue();
@@ -662,6 +662,13 @@
}
}
+ @Override
+ public void onMultiWindowModeChanged() {
+ if (mDecor != null) {
+ mDecor.onConfigurationChanged();
+ }
+ }
+
private static void clearMenuViews(PanelFeatureState st) {
// This can be called on config changes, so we should make sure
// the views will be reconstructed based on the new orientation, etc.
@@ -3592,27 +3599,34 @@
* <li> Calls back to the callback's onMenuItemSelected when an item is
* selected.
*/
- private final class DialogMenuCallback implements MenuBuilder.Callback, MenuPresenter.Callback {
- private int mFeatureId;
+ public static final class PhoneWindowMenuCallback
+ implements MenuBuilder.Callback, MenuPresenter.Callback {
+ private static final int FEATURE_ID = FEATURE_CONTEXT_MENU;
+
+ private final PhoneWindow mWindow;
+
private MenuDialogHelper mSubMenuHelper;
- public DialogMenuCallback(int featureId) {
- mFeatureId = featureId;
+ private boolean mShowDialogForSubmenu;
+
+ public PhoneWindowMenuCallback(PhoneWindow window) {
+ mWindow = window;
}
+ @Override
public void onCloseMenu(MenuBuilder menu, boolean allMenusAreClosing) {
if (menu.getRootMenu() != menu) {
onCloseSubMenu(menu);
}
if (allMenusAreClosing) {
- Callback callback = getCallback();
- if (callback != null && !isDestroyed()) {
- callback.onPanelClosed(mFeatureId, menu);
+ final Callback callback = mWindow.getCallback();
+ if (callback != null && !mWindow.isDestroyed()) {
+ callback.onPanelClosed(FEATURE_ID, menu);
}
- if (menu == mContextMenu) {
- dismissContextMenu();
+ if (menu == mWindow.mContextMenu) {
+ mWindow.dismissContextMenu();
}
// Dismiss the submenu, if it is showing
@@ -3623,33 +3637,45 @@
}
}
- public void onCloseSubMenu(MenuBuilder menu) {
- Callback callback = getCallback();
- if (callback != null && !isDestroyed()) {
- callback.onPanelClosed(mFeatureId, menu.getRootMenu());
+ private void onCloseSubMenu(MenuBuilder menu) {
+ final Callback callback = mWindow.getCallback();
+ if (callback != null && !mWindow.isDestroyed()) {
+ callback.onPanelClosed(FEATURE_ID, menu.getRootMenu());
}
}
+ @Override
public boolean onMenuItemSelected(MenuBuilder menu, MenuItem item) {
- Callback callback = getCallback();
- return (callback != null && !isDestroyed())
- && callback.onMenuItemSelected(mFeatureId, item);
+ final Callback callback = mWindow.getCallback();
+ return callback != null && !mWindow.isDestroyed()
+ && callback.onMenuItemSelected(FEATURE_ID, item);
}
+ @Override
public void onMenuModeChange(MenuBuilder menu) {
}
+ @Override
public boolean onOpenSubMenu(MenuBuilder subMenu) {
- if (subMenu == null) return false;
+ if (subMenu == null) {
+ return false;
+ }
// Set a simple callback for the submenu
subMenu.setCallback(this);
- // The window manager will give us a valid window token
- mSubMenuHelper = new MenuDialogHelper(subMenu);
- mSubMenuHelper.show(null);
+ if (mShowDialogForSubmenu) {
+ // The window manager will give us a valid window token
+ mSubMenuHelper = new MenuDialogHelper(subMenu);
+ mSubMenuHelper.show(null);
+ return true;
+ }
- return true;
+ return false;
+ }
+
+ public void setShowDialogForSubmenu(boolean enabled) {
+ mShowDialogForSubmenu = enabled;
}
}
diff --git a/core/java/com/android/internal/view/menu/CascadingMenuPopup.java b/core/java/com/android/internal/view/menu/CascadingMenuPopup.java
index e9b8447..320de90 100644
--- a/core/java/com/android/internal/view/menu/CascadingMenuPopup.java
+++ b/core/java/com/android/internal/view/menu/CascadingMenuPopup.java
@@ -3,6 +3,7 @@
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
+import java.util.LinkedList;
import java.util.List;
import android.annotation.AttrRes;
@@ -36,6 +37,7 @@
import android.widget.PopupWindow.OnDismissListener;
import android.widget.TextView;
+import com.android.internal.R;
import com.android.internal.util.Preconditions;
/**
@@ -45,6 +47,7 @@
*/
final class CascadingMenuPopup extends MenuPopup implements MenuPresenter, OnKeyListener,
PopupWindow.OnDismissListener {
+
@Retention(RetentionPolicy.SOURCE)
@IntDef({HORIZ_POSITION_LEFT, HORIZ_POSITION_RIGHT})
public @interface HorizPosition {}
@@ -65,11 +68,14 @@
private final boolean mOverflowOnly;
private final Handler mSubMenuHoverHandler;
+ /** List of menus that were added before this popup was shown. */
+ private final List<MenuBuilder> mPendingMenus = new LinkedList<>();
+
/**
* 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 List<CascadingMenuInfo> mShowingMenus = new ArrayList<>();
private final OnGlobalLayoutListener mGlobalLayoutListener = new OnGlobalLayoutListener() {
@Override
@@ -80,7 +86,7 @@
dismiss();
} else if (isShowing()) {
// Recompute window sizes and positions.
- for (CascadingMenuInfo info : mAddedMenus) {
+ for (CascadingMenuInfo info : mShowingMenus) {
info.window.show();
}
}
@@ -123,8 +129,8 @@
// 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) {
+ for (int i = 0, count = mShowingMenus.size(); i < count; i++) {
+ if (menu == mShowingMenus.get(i).menu) {
menuIndex = i;
break;
}
@@ -136,8 +142,8 @@
final CascadingMenuInfo nextInfo;
final int nextIndex = menuIndex + 1;
- if (nextIndex < mAddedMenus.size()) {
- nextInfo = mAddedMenus.get(nextIndex);
+ if (nextIndex < mShowingMenus.size()) {
+ nextInfo = mShowingMenus.get(nextIndex);
} else {
nextInfo = null;
}
@@ -228,35 +234,14 @@
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, count = mAddedMenus.size(); i < count; i++) {
- final CascadingMenuInfo info = mAddedMenus.get(i);
- final MenuPopupWindow popupWindow = info.window;
- popupWindow.show();
-
- 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,
- info.getListView(),
- false);
- TextView titleView = (TextView) titleItemView.findViewById(
- com.android.internal.R.id.title);
- titleView.setText(menu.getHeaderTitle());
- titleItemView.setEnabled(false);
- info.getListView().addHeaderView(titleItemView, null, false);
-
- // Update to show the title.
- popupWindow.show();
- }
+ // Display all pending menus.
+ for (MenuBuilder menu : mPendingMenus) {
+ showMenu(menu);
}
+ mPendingMenus.clear();
mShownAnchorView = mAnchorView;
+
if (mShownAnchorView != null) {
final boolean addGlobalListener = mTreeObserver == null;
mTreeObserver = mShownAnchorView.getViewTreeObserver(); // Refresh to latest
@@ -273,10 +258,10 @@
// 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();
+ final int length = mShowingMenus.size();
if (length > 0) {
final CascadingMenuInfo[] addedMenus =
- mAddedMenus.toArray(new CascadingMenuInfo[length]);
+ mShowingMenus.toArray(new CascadingMenuInfo[length]);
for (int i = length - 1; i >= 0; i--) {
final CascadingMenuInfo info = addedMenus[i];
if (info.window.isShowing()) {
@@ -315,7 +300,7 @@
*/
@HorizPosition
private int getNextMenuPosition(int nextMenuWidth) {
- ListView lastListView = mAddedMenus.get(mAddedMenus.size() - 1).getListView();
+ ListView lastListView = mShowingMenus.get(mShowingMenus.size() - 1).getListView();
final int[] screenLocation = new int[2];
lastListView.getLocationOnScreen(screenLocation);
@@ -342,6 +327,19 @@
public void addMenu(MenuBuilder menu) {
menu.addMenuPresenter(this, mContext);
+ if (isShowing()) {
+ showMenu(menu);
+ } else {
+ mPendingMenus.add(menu);
+ }
+ }
+
+ /**
+ * Prepares and shows the specified menu immediately.
+ *
+ * @param menu the menu to show
+ */
+ private void showMenu(@NonNull MenuBuilder menu) {
final LayoutInflater inflater = LayoutInflater.from(mContext);
final MenuAdapter adapter = new MenuAdapter(menu, inflater, mOverflowOnly);
adapter.setForceShowIcon(mForceShowIcon);
@@ -354,8 +352,8 @@
final CascadingMenuInfo parentInfo;
final View parentView;
- if (mAddedMenus.size() > 0) {
- parentInfo = mAddedMenus.get(mAddedMenus.size() - 1);
+ if (mShowingMenus.size() > 0) {
+ parentInfo = mShowingMenus.get(mShowingMenus.size() - 1);
parentView = findParentViewForSubmenu(parentInfo, menu);
} else {
parentInfo = null;
@@ -407,12 +405,21 @@
popupWindow.setVerticalOffset(y);
final CascadingMenuInfo menuInfo = new CascadingMenuInfo(popupWindow, menu, mLastPosition);
- mAddedMenus.add(menuInfo);
+ mShowingMenus.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();
+
+ // If this is the root menu, show the title if requested.
+ if (parentInfo == null && mShowTitle && menu.getHeaderTitle() != null) {
+ final ListView listView = popupWindow.getListView();
+ final FrameLayout titleItemView = (FrameLayout) inflater.inflate(
+ R.layout.popup_menu_header_item_layout, listView, false);
+ final TextView titleView = (TextView) titleItemView.findViewById(R.id.title);
+ titleItemView.setEnabled(false);
+ titleView.setText(menu.getHeaderTitle());
+ listView.addHeaderView(titleItemView, null, false);
+
+ // Show again to update the title.
popupWindow.show();
}
}
@@ -500,7 +507,7 @@
*/
@Override
public boolean isShowing() {
- return mAddedMenus.size() > 0 && mAddedMenus.get(0).window.isShowing();
+ return mShowingMenus.size() > 0 && mShowingMenus.get(0).window.isShowing();
}
/**
@@ -511,8 +518,8 @@
// 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);
+ for (int i = 0, count = mShowingMenus.size(); i < count; i++) {
+ final CascadingMenuInfo info = mShowingMenus.get(i);
if (!info.window.isShowing()) {
dismissedInfo = info;
break;
@@ -528,7 +535,7 @@
@Override
public void updateMenuView(boolean cleared) {
- for (CascadingMenuInfo info : mAddedMenus) {
+ for (CascadingMenuInfo info : mShowingMenus) {
toMenuAdapter(info.getListView().getAdapter()).notifyDataSetChanged();
}
}
@@ -541,7 +548,7 @@
@Override
public boolean onSubMenuSelected(SubMenuBuilder subMenu) {
// Don't allow double-opening of the same submenu.
- for (CascadingMenuInfo info : mAddedMenus) {
+ for (CascadingMenuInfo info : mShowingMenus) {
if (subMenu == info.menu) {
// Just re-focus that one.
info.getListView().requestFocus();
@@ -567,8 +574,8 @@
* @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);
+ for (int i = 0, count = mShowingMenus.size(); i < count; i++) {
+ final CascadingMenuInfo info = mShowingMenus.get(i);
if (menu == info.menu) {
return i;
}
@@ -586,13 +593,13 @@
// Recursively close descendant menus.
final int nextMenuIndex = menuIndex + 1;
- if (nextMenuIndex < mAddedMenus.size()) {
- final CascadingMenuInfo childInfo = mAddedMenus.get(nextMenuIndex);
+ if (nextMenuIndex < mShowingMenus.size()) {
+ final CascadingMenuInfo childInfo = mShowingMenus.get(nextMenuIndex);
childInfo.menu.close(false /* closeAllMenus */);
}
// Close the target menu.
- final CascadingMenuInfo info = mAddedMenus.remove(menuIndex);
+ final CascadingMenuInfo info = mShowingMenus.remove(menuIndex);
info.menu.removeMenuPresenter(this);
if (mShouldCloseImmediately) {
// Disable all exit animations.
@@ -601,9 +608,9 @@
}
info.window.dismiss();
- final int count = mAddedMenus.size();
+ final int count = mShowingMenus.size();
if (count > 0) {
- mLastPosition = mAddedMenus.get(count - 1).position;
+ mLastPosition = mShowingMenus.get(count - 1).position;
} else {
mLastPosition = getInitialMenuPosition();
}
@@ -631,7 +638,7 @@
// 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);
+ final CascadingMenuInfo rootInfo = mShowingMenus.get(0);
rootInfo.menu.close(false /* closeAllMenus */);
}
}
@@ -677,7 +684,7 @@
@Override
public ListView getListView() {
- return mAddedMenus.isEmpty() ? null : mAddedMenus.get(mAddedMenus.size() - 1).getListView();
+ return mShowingMenus.isEmpty() ? null : mShowingMenus.get(mShowingMenus.size() - 1).getListView();
}
@Override
diff --git a/core/java/com/android/internal/view/menu/StandardMenuPopup.java b/core/java/com/android/internal/view/menu/StandardMenuPopup.java
index 6a5f6d8..c2adc42 100644
--- a/core/java/com/android/internal/view/menu/StandardMenuPopup.java
+++ b/core/java/com/android/internal/view/menu/StandardMenuPopup.java
@@ -242,13 +242,13 @@
@Override
public boolean onSubMenuSelected(SubMenuBuilder subMenu) {
if (subMenu.hasVisibleItems()) {
- MenuPopupHelper subPopup = new MenuPopupHelper(
- mContext, subMenu, mShownAnchorView, mOverflowOnly, mPopupStyleAttr,
- mPopupStyleRes);
+ final MenuPopupHelper subPopup = new MenuPopupHelper(mContext, subMenu,
+ mShownAnchorView, mOverflowOnly, mPopupStyleAttr, mPopupStyleRes);
subPopup.setPresenterCallback(mPresenterCallback);
subPopup.setForceShowIcon(mAdapter.getForceShowIcon());
- if (subPopup.tryShow()) {
+ // Show the new sub-menu popup at the same location as this popup.
+ if (subPopup.tryShow(mXOffset, mYOffset)) {
if (mPresenterCallback != null) {
mPresenterCallback.onOpenSubMenu(subMenu);
}
diff --git a/core/java/com/android/internal/widget/LockPatternUtils.java b/core/java/com/android/internal/widget/LockPatternUtils.java
index 6c223c3..e38d82f 100644
--- a/core/java/com/android/internal/widget/LockPatternUtils.java
+++ b/core/java/com/android/internal/widget/LockPatternUtils.java
@@ -64,6 +64,11 @@
private static final boolean DEBUG = false;
/**
+ * The key to identify when the lock pattern enabled flag is being acccessed for legacy reasons.
+ */
+ public static final String LEGACY_LOCK_PATTERN_ENABLED = "legacy_lock_pattern_enabled";
+
+ /**
* The number of incorrect attempts before which we fall back on an alternative
* method of verifying the user, and resetting their lock pattern.
*/
@@ -1014,6 +1019,19 @@
return isLockPatternEnabled(getKeyguardStoredPasswordQuality(userId), userId);
}
+ @Deprecated
+ public boolean isLegacyLockPatternEnabled(int userId) {
+ // Note: this value should default to {@code true} to avoid any reset that might result.
+ // We must use a special key to read this value, since it will by default return the value
+ // based on the new logic.
+ return getBoolean(LEGACY_LOCK_PATTERN_ENABLED, true, userId);
+ }
+
+ @Deprecated
+ public void setLegacyLockPatternEnabled(int userId) {
+ setBoolean(Settings.Secure.LOCK_PATTERN_ENABLED, true, userId);
+ }
+
private boolean isLockPatternEnabled(int mode, int userId) {
return mode == DevicePolicyManager.PASSWORD_QUALITY_SOMETHING
&& savedPatternExists(userId);
diff --git a/core/jni/android_view_PointerIcon.cpp b/core/jni/android_view_PointerIcon.cpp
index d7e2c02..71be52e 100644
--- a/core/jni/android_view_PointerIcon.cpp
+++ b/core/jni/android_view_PointerIcon.cpp
@@ -65,33 +65,34 @@
return OK;
}
- jobject loadedPointerIconObj = env->CallObjectMethod(pointerIconObj,
- gPointerIconClassInfo.load, contextObj);
- if (env->ExceptionCheck() || !loadedPointerIconObj) {
+ ScopedLocalRef<jobject> loadedPointerIconObj(env, env->CallObjectMethod(pointerIconObj,
+ gPointerIconClassInfo.load, contextObj));
+ if (env->ExceptionCheck() || !loadedPointerIconObj.get()) {
ALOGW("An exception occurred while loading a pointer icon.");
LOGW_EX(env);
env->ExceptionClear();
return UNKNOWN_ERROR;
}
+ return android_view_PointerIcon_getLoadedIcon(env, loadedPointerIconObj.get(), outPointerIcon);
+}
- outPointerIcon->style = env->GetIntField(loadedPointerIconObj,
- gPointerIconClassInfo.mStyle);
- outPointerIcon->hotSpotX = env->GetFloatField(loadedPointerIconObj,
- gPointerIconClassInfo.mHotSpotX);
- outPointerIcon->hotSpotY = env->GetFloatField(loadedPointerIconObj,
- gPointerIconClassInfo.mHotSpotY);
+status_t android_view_PointerIcon_getLoadedIcon(JNIEnv* env, jobject pointerIconObj,
+ PointerIcon* outPointerIcon) {
+ outPointerIcon->style = env->GetIntField(pointerIconObj, gPointerIconClassInfo.mStyle);
+ outPointerIcon->hotSpotX = env->GetFloatField(pointerIconObj, gPointerIconClassInfo.mHotSpotX);
+ outPointerIcon->hotSpotY = env->GetFloatField(pointerIconObj, gPointerIconClassInfo.mHotSpotY);
- jobject bitmapObj = env->GetObjectField(loadedPointerIconObj, gPointerIconClassInfo.mBitmap);
- if (bitmapObj) {
- GraphicsJNI::getSkBitmap(env, bitmapObj, &(outPointerIcon->bitmap));
- env->DeleteLocalRef(bitmapObj);
+ ScopedLocalRef<jobject> bitmapObj(
+ env, env->GetObjectField(pointerIconObj, gPointerIconClassInfo.mBitmap));
+ if (bitmapObj.get()) {
+ GraphicsJNI::getSkBitmap(env, bitmapObj.get(), &(outPointerIcon->bitmap));
}
ScopedLocalRef<jobjectArray> bitmapFramesObj(env, reinterpret_cast<jobjectArray>(
- env->GetObjectField(loadedPointerIconObj, gPointerIconClassInfo.mBitmapFrames)));
+ env->GetObjectField(pointerIconObj, gPointerIconClassInfo.mBitmapFrames)));
if (bitmapFramesObj.get()) {
outPointerIcon->durationPerFrame = env->GetIntField(
- loadedPointerIconObj, gPointerIconClassInfo.mDurationPerFrame);
+ pointerIconObj, gPointerIconClassInfo.mDurationPerFrame);
jsize size = env->GetArrayLength(bitmapFramesObj.get());
outPointerIcon->bitmapFrames.resize(size);
for (jsize i = 0; i < size; ++i) {
@@ -100,7 +101,6 @@
}
}
- env->DeleteLocalRef(loadedPointerIconObj);
return OK;
}
diff --git a/core/jni/android_view_PointerIcon.h b/core/jni/android_view_PointerIcon.h
index ca08085..00bdfb4 100644
--- a/core/jni/android_view_PointerIcon.h
+++ b/core/jni/android_view_PointerIcon.h
@@ -97,6 +97,11 @@
extern status_t android_view_PointerIcon_load(JNIEnv* env,
jobject pointerIconObj, jobject contextObj, PointerIcon* outPointerIcon);
+/* Obtain the data of pointerIconObj and put to outPointerIcon. */
+extern status_t android_view_PointerIcon_getLoadedIcon(JNIEnv* env, jobject pointerIconObj,
+ PointerIcon* outPointerIcon);
+
+
/* Loads the bitmap associated with a pointer icon by style.
* If pointerIconObj is NULL, returns OK and a pointer icon with POINTER_ICON_STYLE_NULL. */
extern status_t android_view_PointerIcon_loadSystemIcon(JNIEnv* env,
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 1127197..de7f6ed 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -369,6 +369,22 @@
<protected-broadcast android:name="ScheduleConditionProvider.EVALUATE" />
<protected-broadcast android:name="wifi_scan_available" />
+ <protected-broadcast android:name="action.cne.started" />
+ <protected-broadcast android:name="android.appwidget.action.APPWIDGET_UPDATE" />
+ <protected-broadcast android:name="android.content.jobscheduler.JOB_DEADLINE_EXPIRED" />
+ <protected-broadcast android:name="android.intent.action.ACTION_UNSOL_RESPONSE_OEM_HOOK_RAW" />
+ <protected-broadcast android:name="android.location.HIGH_POWER_REQUEST_CHANGE" />
+ <protected-broadcast android:name="android.location.HIGH_POWER_REQUEST_CHANGE" />
+ <protected-broadcast android:name="android.net.conn.CONNECTIVITY_CHANGE_SUPL" />
+ <protected-broadcast android:name="android.os.action.ACTION_EFFECTS_SUPPRESSOR_CHANGED" />
+ <protected-broadcast android:name="android.os.action.LIGHT_DEVICE_IDLE_MODE_CHANGED" />
+ <protected-broadcast android:name="android.os.storage.action.VOLUME_STATE_CHANGED" />
+ <protected-broadcast android:name="com.android.server.action.UPDATE_TWILIGHT_STATE" />
+ <protected-broadcast android:name="com.android.server.device_idle.STEP_IDLE_STATE" />
+ <protected-broadcast android:name="com.android.server.device_idle.STEP_LIGHT_IDLE_STATE" />
+ <protected-broadcast android:name="com.android.server.Wifi.action.TOGGLE_PNO" />
+ <protected-broadcast android:name="intent.action.ACTION_RF_BAND_INFO" />
+
<!-- ====================================================================== -->
<!-- RUNTIME PERMISSIONS -->
<!-- ====================================================================== -->
@@ -643,14 +659,14 @@
<!-- Allows an application to access the IMS call service: making and
modifying a call
- <p>Protection level: signature|system
+ <p>Protection level: signature|privileged
@hide
-->
<permission android:name="android.permission.ACCESS_IMS_CALL_SERVICE"
android:permissionGroup="android.permission-group.PHONE"
android:label="@string/permlab_accessImsCallService"
android:description="@string/permdesc_accessImsCallService"
- android:protectionLevel="signature|system" />
+ android:protectionLevel="signature|privileged" />
<!-- Allows an application to read the user's call log.
<p class="note"><strong>Note:</strong> If your app uses the
@@ -927,7 +943,7 @@
<!-- @SystemApi @hide Allows an application to modify cell broadcasts through the content provider.
<p>Not for use by third-party applications. -->
<permission android:name="android.permission.MODIFY_CELL_BROADCASTS"
- android:protectionLevel="signature|system" />
+ android:protectionLevel="signature|privileged" />
<!-- =============================================================== -->
<!-- Permissions for setting the device alarm -->
@@ -948,16 +964,16 @@
<eat-comment />
<!-- Allows an application to modify and remove existing voicemails in the system
- <p>Protection level: system|signature
+ <p>Protection level: signature|privileged
-->
<permission android:name="com.android.voicemail.permission.WRITE_VOICEMAIL"
- android:protectionLevel="system|signature" />
+ android:protectionLevel="signature|privileged" />
<!-- Allows an application to read voicemails in the system.
- <p>Protection level: system|signature
+ <p>Protection level: signature|privileged
-->
<permission android:name="com.android.voicemail.permission.READ_VOICEMAIL"
- android:protectionLevel="system|signature" />
+ android:protectionLevel="signature|privileged" />
<!-- ======================================= -->
<!-- Permissions for accessing location info -->
@@ -1093,7 +1109,7 @@
allow or disallow phonebook access or message access.
This is not available to third party applications. -->
<permission android:name="android.permission.BLUETOOTH_PRIVILEGED"
- android:protectionLevel="system|signature" />
+ android:protectionLevel="signature|privileged" />
<!-- Control access to email providers exclusively for Bluetooth
@hide
@@ -1331,26 +1347,26 @@
corresponds to a device SIM.
@hide -->
<permission android:name="android.permission.REGISTER_SIM_SUBSCRIPTION"
- android:protectionLevel="system|signature" />
+ android:protectionLevel="signature|privileged" />
<!-- @SystemApi Protects the ability to register any PhoneAccount with
PhoneAccount#CAPABILITY_CALL_PROVIDER.
@hide -->
<permission android:name="android.permission.REGISTER_CALL_PROVIDER"
- android:protectionLevel="system|signature" />
+ android:protectionLevel="signature|privileged" />
<!-- @SystemApi Protects the ability to register any PhoneAccount with
PhoneAccount#CAPABILITY_CONNECTION_MANAGER
@hide -->
<permission android:name="android.permission.REGISTER_CONNECTION_MANAGER"
- android:protectionLevel="system|signature" />
+ android:protectionLevel="signature|privileged" />
<!-- Must be required by a {@link android.telecom.InCallService},
to ensure that only the system can bind to it.
- <p>Protection level: system|signature
+ <p>Protection level: signature|privileged
-->
<permission android:name="android.permission.BIND_INCALL_SERVICE"
- android:protectionLevel="system|signature" />
+ android:protectionLevel="signature|privileged" />
<!-- Must be required by a {@link android.telecom.ConnectionService},
to ensure that only the system can bind to it.
@@ -1359,24 +1375,24 @@
@SystemApi
@hide -->
<permission android:name="android.permission.BIND_CONNECTION_SERVICE"
- android:protectionLevel="system|signature" />
+ android:protectionLevel="signature|privileged" />
<!-- Must be required by a {@link android.telecom.ConnectionService},
to ensure that only the system can bind to it.
- <p>Protection level: system|signature
+ <p>Protection level: signature|privileged
-->
<permission android:name="android.permission.BIND_TELECOM_CONNECTION_SERVICE"
- android:protectionLevel="system|signature" />
+ android:protectionLevel="signature|privileged" />
<!-- @SystemApi Allows an application to control the in-call experience.
@hide -->
<permission android:name="android.permission.CONTROL_INCALL_EXPERIENCE"
- android:protectionLevel="system|signature" />
+ android:protectionLevel="signature|privileged" />
<!-- Allows an application to receive STK related commands.
@hide -->
<permission android:name="android.permission.RECEIVE_STK_COMMANDS"
- android:protectionLevel="system|signature" />
+ android:protectionLevel="signature|privileged" />
<!-- ================================== -->
<!-- Permissions for sdcard interaction -->
@@ -1731,12 +1747,12 @@
<!-- @SystemApi Allows mounting and unmounting file systems for removable storage.
<p>Not for use by third-party applications.-->
<permission android:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS"
- android:protectionLevel="system|signature" />
+ android:protectionLevel="signature|privileged" />
<!-- @SystemApi Allows formatting file systems for removable storage.
<p>Not for use by third-party applications. -->
<permission android:name="android.permission.MOUNT_FORMAT_FILESYSTEMS"
- android:protectionLevel="system|signature" />
+ android:protectionLevel="signature|privileged" />
<!-- @hide -->
<permission android:name="android.permission.STORAGE_INTERNAL"
@@ -2399,7 +2415,7 @@
access the network and acquire wakelocks.
<p>Not for use by third-party applications. -->
<permission android:name="android.permission.CHANGE_DEVICE_IDLE_TEMP_WHITELIST"
- android:protectionLevel="system|signature" />
+ android:protectionLevel="signature|privileged" />
<!-- Permission an application must hold in order to use
{@link android.provider.Settings#ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONS}.
@@ -2613,7 +2629,7 @@
<!-- Allows managing (adding, removing) fingerprint templates. Reserved for the system. @hide -->
<permission android:name="android.permission.MANAGE_FINGERPRINT"
- android:protectionLevel="system|signature" />
+ android:protectionLevel="signature|privileged" />
<!-- Allows an app to reset fingerprint attempt counter. Reserved for the system. @hide -->
<permission android:name="android.permission.RESET_FINGERPRINT_LOCKOUT"
diff --git a/core/res/res/drawable/ic_corp_badge_off.xml b/core/res/res/drawable/ic_corp_badge_off.xml
new file mode 100644
index 0000000..6799bf7
--- /dev/null
+++ b/core/res/res/drawable/ic_corp_badge_off.xml
@@ -0,0 +1,56 @@
+<?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="20dp"
+ android:height="20dp"
+ android:viewportWidth="20"
+ android:viewportHeight="20">
+
+ <path
+ android:fillColor="#607D8B"
+ android:pathData="M10,0 C15.5228,0,20,4.47715,20,10 C20,15.5228,15.5228,20,10,20
+C4.47715,20,0,15.5228,0,10 C0,4.47715,4.47715,0,10,0 Z" />
+ <path
+ android:pathData="M1.91667,1.91667 L18.0833,1.91667 L18.0833,18.0833 L1.91667,18.0833
+L1.91667,1.91667 Z" />
+ <path
+ android:fillColor="#ffffff"
+ android:pathData="M11.9167,11.9167 L11.4167,11.9167 L11.4167,12.8333 L8.5,12.8333 L8.5,11.9167
+L4.16667,11.9167 L4.16667,14.3333 C4.16667,14.8333,4.58333,15.25,5.08333,15.25
+L14.75,15.25 C14.9167,15.25,15,15.25,15.1667,15.1667 L11.9167,11.9167 Z" />
+ <path
+ android:fillColor="#ffffff"
+ android:pathData="M15.8333,13.75 L15.8333,11.9167 L14,11.9167
+C14.6667,12.6667,15.3333,13.3333,15.8333,13.75 Z" />
+ <path
+ android:fillColor="#ffffff"
+ android:pathData="M6.16667,6.16667 L4.66667,6.16667 C4.16667,6.16667,3.75,6.58333,3.75,7.08333
+L3.75,10 C3.75,10.5,4.16667,10.9167,4.66667,10.9167 L8.5,10.9167 L8.5,10 L10,10
+L6.16667,6.16667 Z" />
+ <path
+ android:fillColor="#ffffff"
+ android:pathData="M8.08333,6 L8.08333,5.16667 L11.9167,5.16667 L11.9167,6.08333 L8.16667,6.08333
+C9.66667,7.58333,11.4167,9.33333,12.9167,10.8333 L15.25,10.8333
+C15.75,10.8333,16.1667,10.4167,16.1667,9.91667 L16.1667,7.08333
+C16.1667,6.58333,15.75,6.16667,15.25,6.16667 L12.8333,6.16667 L12.8333,5.25
+L11.9167,4.33333 L8.08333,4.33333 L7.16667,5.16667
+C7.41667,5.41667,7.75,5.75,8.08333,6 Z" />
+ <path
+ android:fillColor="#ffffff"
+ android:pathData="M15.6824,15.676 L14.6807,16.6777 L3.24921,5.24624 L4.25093,4.24452
+L15.6824,15.676 Z" />
+</vector>
\ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/vector_drawable_place_left.xml b/core/res/res/drawable/work_widget_mask_view_background.xml
similarity index 61%
rename from packages/SystemUI/res/drawable/vector_drawable_place_left.xml
rename to core/res/res/drawable/work_widget_mask_view_background.xml
index 078f83c..17f0dbc 100644
--- a/packages/SystemUI/res/drawable/vector_drawable_place_left.xml
+++ b/core/res/res/drawable/work_widget_mask_view_background.xml
@@ -1,3 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
<!--
Copyright (C) 2015 The Android Open Source Project
@@ -13,12 +14,7 @@
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="#FF000000"
- android:pathData="M24.0,0.0L0.0,0.0l0.0,24.0l24.0,0.0L24.0,0.0zM14.0,4.0l0.0,16.0L4.0,20.0L4.0,4.0L14.0,4.0z"/>
-</vector>
+ <shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="rectangle">
+ <padding android:left="5dp" android:right="5dp" android:top="5dp" android:bottom="5dp"/>
+ <stroke android:width="1dp" android:color="#CCCCCC" />
+ </shape>
diff --git a/core/res/res/layout/work_widget_mask_view.xml b/core/res/res/layout/work_widget_mask_view.xml
new file mode 100644
index 0000000..ce86ddc
--- /dev/null
+++ b/core/res/res/layout/work_widget_mask_view.xml
@@ -0,0 +1,34 @@
+<?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="match_parent"
+ android:background="#F3374248" >
+
+ <ImageView android:id="@+id/work_widget_app_icon"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center"/>
+
+ <ImageView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="bottom|right"
+ android:layout_marginBottom="4dp"
+ android:layout_marginRight="4dp"
+ android:src="@drawable/ic_corp_badge_off" />
+</FrameLayout>
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index 786554c..9f13565 100644
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -2791,6 +2791,56 @@
<flag name="end" value="0x20" />
</attr>
+ <attr name="pointerShape">
+ <!-- Null icon, pointer becomes invisible. -->
+ <enum name="none" value="0" />
+ <!-- The default icon of arrow pointer. -->
+ <enum name="arrow" value="1000" />
+ <!-- Pointer icon indicating context-menu will appear. -->
+ <enum name="context_menu" value="1001" />
+ <!-- Pointer icon of a hand with the index finger. -->
+ <enum name="hand" value="1002" />
+ <!-- Pointer icon indicating help. -->
+ <enum name="help" value="1003" />
+ <!-- Pointer icon indicating something is going on and waiting. -->
+ <enum name="wait" value="1004" />
+ <!-- Pointer icon for cell and grid. -->
+ <enum name="cell" value="1006" />
+ <!-- Pointer icon of crosshair, indicating to spot a location. -->
+ <enum name="crosshair" value="1007" />
+ <!-- Pointer icon of I-beam, usually for text. -->
+ <enum name="text" value="1008" />
+ <!-- Pointer icon of I-beam with 90-degree rotated, for vertical text. -->
+ <enum name="vertical_text" value="1009" />
+ <!-- Pointer icon of 'alias', indicating an alias of/shortcut to something is to be
+ created. -->
+ <enum name="alias" value="1010" />
+ <!-- Pointer icon of 'copy', used for drag/drop. -->
+ <enum name="copy" value="1011" />
+ <!-- Pointer icon of 'no-drop', indicating the drop will not be accepted at the
+ current location. -->
+ <enum name="no_drop" value="1012" />
+ <!-- Pointer icon of four-way arrows, indicating scrolling all direction. -->
+ <enum name="all_scroll" value="1013" />
+ <!-- Pointer icon of horizontal double arrow, indicating horizontal resize. -->
+ <enum name="horizontal_double_arrow" value="1014" />
+ <!-- Pointer icon of vertical double arrow, indicating vertical resize. -->
+ <enum name="vertical_double_arrow" value="1015" />
+ <!-- Pointer icon of diagonal double arrow, starting from top-right to bottom-left.
+ Indicating freeform resize. -->
+ <enum name="top_right_diagonal_double_arrow" value="1016" />
+ <!-- Pointer icon of diagonal double arrow, starting from top-left to bottom-right.
+ Indicating freeform resize. -->
+ <enum name="top_left_diagonal_double_arrow" value="1017" />
+ <!-- Pointer icon indicating zoom-in. -->
+ <enum name="zoom_in" value="1018" />
+ <!-- Pointer icon indicating zoom-out. -->
+ <enum name="zoom_out" value="1019" />
+ <!-- Pointer icon of a hand sign to grab something. -->
+ <enum name="grab" value="1020" />
+ <!-- Pointer icon of a hand sign while grabbing something. -->
+ <enum name="grabbing" value="1021" />
+ </attr>
</declare-styleable>
<!-- Attributes that can be assigned to a tag for a particular View. -->
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index e2b1dbb..bff8b1a 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -1018,6 +1018,10 @@
<!-- Light sensor event rate in milliseconds for automatic brightness control. -->
<integer name="config_autoBrightnessLightSensorRate">250</integer>
+ <!-- The maximum range of gamma adjustment possible using the screen
+ auto-brightness adjustment setting. -->
+ <fraction name="config_autoBrightnessAdjustmentMaxGamma">300%</fraction>
+
<!-- If we allow automatic adjustment of screen brightness while dozing, how many times we want
to reduce it to preserve the battery. Value of 100% means no scaling. -->
<fraction name="config_screenAutoBrightnessDozeScaleFactor">100%</fraction>
@@ -1035,6 +1039,9 @@
adapt to the environment. This mode may be better suited for watches. -->
<bool name="config_autoBrightnessResetAmbientLuxAfterWarmUp">true</bool>
+ <!-- Period of time in which to consider light samples in milliseconds. -->
+ <integer name="config_autoBrightnessAmbientLightHorizon">10000</integer>
+
<!-- Screen brightness used to dim the screen when the user activity
timeout expires. May be less than the minimum allowed brightness setting
that can be set by the user. -->
diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml
index addeb05..ad36f3c 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -2684,6 +2684,7 @@
<public type="attr" name="preferenceFragmentStyle" />
<public type="attr" name="canControlMagnification" />
<public type="attr" name="languageTag" />
+ <public type="attr" name="pointerShape" />
<public type="style" name="Theme.Material.DayNight" />
<public type="style" name="Theme.Material.DayNight.DarkActionBar" />
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index bf758c2..832984e 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -1243,6 +1243,7 @@
<java-symbol type="drawable" name="cling_arrow_up" />
<java-symbol type="drawable" name="cling_bg" />
<java-symbol type="drawable" name="ic_corp_badge" />
+ <java-symbol type="drawable" name="ic_corp_badge_off" />
<java-symbol type="drawable" name="ic_corp_icon_badge" />
<java-symbol type="drawable" name="ic_corp_icon" />
<java-symbol type="drawable" name="ic_corp_statusbar_icon" />
@@ -1693,6 +1694,8 @@
<java-symbol type="fraction" name="config_dimBehindFadeDuration" />
<java-symbol type="fraction" name="config_displayFractionForDefaultMinimalSizeOfResizeableTask" />
<java-symbol type="fraction" name="config_screenAutoBrightnessDozeScaleFactor" />
+ <java-symbol type="fraction" name="config_autoBrightnessAdjustmentMaxGamma" />
+ <java-symbol type="integer" name="config_autoBrightnessAmbientLightHorizon"/>
<java-symbol type="integer" name="config_autoBrightnessBrighteningLightDebounce"/>
<java-symbol type="integer" name="config_autoBrightnessDarkeningLightDebounce"/>
<java-symbol type="integer" name="config_autoBrightnessLightSensorRate"/>
@@ -2382,4 +2385,8 @@
<java-symbol type="dimen" name="notification_content_margin_top" />
<java-symbol type="string" name="importance_from_topic" />
<java-symbol type="string" name="importance_from_person" />
+
+ <java-symbol type="layout" name="work_widget_mask_view" />
+ <java-symbol type="id" name="work_widget_app_icon" />
+ <java-symbol type="drawable" name="work_widget_mask_view_background" />
</resources>
diff --git a/drm/java/android/drm/DrmInfo.java b/drm/java/android/drm/DrmInfo.java
index 22d06c7..8c43252 100644
--- a/drm/java/android/drm/DrmInfo.java
+++ b/drm/java/android/drm/DrmInfo.java
@@ -17,6 +17,7 @@
package android.drm;
import java.io.IOException;
+import java.util.Arrays;
import java.util.HashMap;
import java.util.Iterator;
@@ -52,7 +53,7 @@
if (!isValid()) {
final String msg = "infoType: " + infoType + "," +
"mimeType: " + mimeType + "," +
- "data: " + data;
+ "data: " + Arrays.toString(data);
throw new IllegalArgumentException(msg);
}
@@ -79,7 +80,7 @@
if (!isValid()) {
final String msg = "infoType: " + infoType + "," +
"mimeType: " + mimeType + "," +
- "data: " + mData;
+ "data: " + Arrays.toString(mData);
throw new IllegalArgumentException();
}
diff --git a/drm/java/android/drm/DrmRights.java b/drm/java/android/drm/DrmRights.java
index a9b4f05..8747f77 100644
--- a/drm/java/android/drm/DrmRights.java
+++ b/drm/java/android/drm/DrmRights.java
@@ -18,6 +18,7 @@
import java.io.File;
import java.io.IOException;
+import java.util.Arrays;
/**
* An entity class that wraps the license information retrieved from the online DRM server.
@@ -103,7 +104,7 @@
mMimeType = mimeType;
if (!isValid()) {
final String msg = "mimeType: " + mMimeType + "," +
- "data: " + mData;
+ "data: " + Arrays.toString(mData);
throw new IllegalArgumentException(msg);
}
}
@@ -127,7 +128,7 @@
if (!isValid()) {
final String msg = "mimeType: " + mMimeType + "," +
- "data: " + mData;
+ "data: " + Arrays.toString(mData);
throw new IllegalArgumentException(msg);
}
}
diff --git a/libs/hwui/BakedOpDispatcher.cpp b/libs/hwui/BakedOpDispatcher.cpp
index d4dbb00..9e39797 100644
--- a/libs/hwui/BakedOpDispatcher.cpp
+++ b/libs/hwui/BakedOpDispatcher.cpp
@@ -414,7 +414,7 @@
.setMeshTexturedUnitQuad(texture->uvMapper)
.setFillTexturePaint(*texture, textureFillFlags, op.paint, state.alpha)
.setTransform(state.computedState.transform, TransformFlags::None)
- .setModelViewMapUnitToRectSnap(Rect(0, 0, texture->width, texture->height))
+ .setModelViewMapUnitToRectSnap(Rect(texture->width, texture->height))
.build();
renderer.renderGlop(state, glop);
}
@@ -518,6 +518,10 @@
renderer.renderGlop(state, glop);
}
+void BakedOpDispatcher::onFunctorOp(BakedOpRenderer& renderer, const FunctorOp& op, const BakedOpState& state) {
+ renderer.renderFunctor(op, state);
+}
+
void BakedOpDispatcher::onLinesOp(BakedOpRenderer& renderer, const LinesOp& op, const BakedOpState& state) {
VertexBuffer buffer;
PathTessellator::tessellateLines(op.points, op.floatCount, op.paint,
diff --git a/libs/hwui/BakedOpRenderer.cpp b/libs/hwui/BakedOpRenderer.cpp
index 93a9406..f8282dc 100644
--- a/libs/hwui/BakedOpRenderer.cpp
+++ b/libs/hwui/BakedOpRenderer.cpp
@@ -128,7 +128,7 @@
return texture;
}
-void BakedOpRenderer::renderGlop(const Rect* dirtyBounds, const Rect* clip, const Glop& glop) {
+void BakedOpRenderer::prepareRender(const Rect* dirtyBounds, const Rect* clip) {
mRenderState.scissor().setEnabled(clip != nullptr);
if (clip) {
mRenderState.scissor().set(clip->left, mRenderTarget.viewportHeight - clip->bottom,
@@ -140,10 +140,31 @@
dirtyBounds->right, dirtyBounds->bottom);
mRenderTarget.offscreenBuffer->region.orSelf(dirty);
}
+}
+
+void BakedOpRenderer::renderGlop(const Rect* dirtyBounds, const Rect* clip, const Glop& glop) {
+ prepareRender(dirtyBounds, clip);
mRenderState.render(glop, mRenderTarget.orthoMatrix);
if (!mRenderTarget.frameBufferId) mHasDrawn = true;
}
+void BakedOpRenderer::renderFunctor(const FunctorOp& op, const BakedOpState& state) {
+ prepareRender(&state.computedState.clippedBounds, &state.computedState.clipRect);
+
+ DrawGlInfo info;
+ auto&& clip = state.computedState.clipRect;
+ info.clipLeft = clip.left;
+ info.clipTop = clip.top;
+ info.clipRight = clip.right;
+ info.clipBottom = clip.bottom;
+ info.isLayer = offscreenRenderTarget();
+ info.width = mRenderTarget.viewportWidth;
+ info.height = mRenderTarget.viewportHeight;
+ state.computedState.transform.copyTo(&info.transform[0]);
+
+ mRenderState.invokeFunctor(op.functor, DrawGlInfo::kModeDraw, &info);
+}
+
void BakedOpRenderer::dirtyRenderTarget(const Rect& uiDirty) {
if (mRenderTarget.offscreenBuffer) {
android::Rect dirty(uiDirty.left, uiDirty.top, uiDirty.right, uiDirty.bottom);
diff --git a/libs/hwui/BakedOpRenderer.h b/libs/hwui/BakedOpRenderer.h
index d7600db..f158e8b 100644
--- a/libs/hwui/BakedOpRenderer.h
+++ b/libs/hwui/BakedOpRenderer.h
@@ -65,7 +65,7 @@
void endLayer();
Texture* getTexture(const SkBitmap* bitmap);
- const LightInfo& getLightInfo() { return mLightInfo; }
+ const LightInfo& getLightInfo() const { return mLightInfo; }
void renderGlop(const BakedOpState& state, const Glop& glop) {
bool useScissor = state.computedState.clipSideFlags != OpClipSideFlags::None;
@@ -73,14 +73,16 @@
useScissor ? &state.computedState.clipRect : nullptr,
glop);
}
+ void renderFunctor(const FunctorOp& op, const BakedOpState& state);
void renderGlop(const Rect* dirtyBounds, const Rect* clip, const Glop& glop);
bool offscreenRenderTarget() { return mRenderTarget.offscreenBuffer != nullptr; }
void dirtyRenderTarget(const Rect& dirtyRect);
- bool didDraw() { return mHasDrawn; }
+ bool didDraw() const { return mHasDrawn; }
private:
void setViewport(uint32_t width, uint32_t height);
void clearColorBuffer(const Rect& clearRect);
+ void prepareRender(const Rect* dirtyBounds, const Rect* clip);
RenderState& mRenderState;
Caches& mCaches;
diff --git a/libs/hwui/FontRenderer.cpp b/libs/hwui/FontRenderer.cpp
index 8acdb62..ed31a2c 100644
--- a/libs/hwui/FontRenderer.cpp
+++ b/libs/hwui/FontRenderer.cpp
@@ -83,7 +83,7 @@
.setMeshTexturedIndexedQuads(texture.mesh(), texture.meshElementCount())
.setFillTexturePaint(texture.getTexture(), textureFillFlags, paint, renderer->currentSnapshot()->alpha)
.setTransform(*(renderer->currentSnapshot()), transformFlags)
- .setModelViewOffsetRect(0, 0, Rect(0, 0, 0, 0))
+ .setModelViewOffsetRect(0, 0, Rect())
.build();
renderer->renderGlop(glop);
#endif
diff --git a/libs/hwui/GlopBuilder.cpp b/libs/hwui/GlopBuilder.cpp
index f3ac93b..2507ff3 100644
--- a/libs/hwui/GlopBuilder.cpp
+++ b/libs/hwui/GlopBuilder.cpp
@@ -101,7 +101,7 @@
GlopBuilder& GlopBuilder::setMeshTexturedUnitQuad(const UvMapper* uvMapper) {
if (uvMapper) {
// can't use unit quad VBO, so build UV vertices manually
- return setMeshTexturedUvQuad(uvMapper, Rect(0, 0, 1, 1));
+ return setMeshTexturedUvQuad(uvMapper, Rect(1, 1));
}
TRIGGER_STAGE(kMeshStage);
diff --git a/libs/hwui/OpReorderer.cpp b/libs/hwui/OpReorderer.cpp
index ec03e83..6d90a42 100644
--- a/libs/hwui/OpReorderer.cpp
+++ b/libs/hwui/OpReorderer.cpp
@@ -784,6 +784,12 @@
deferOvalOp(*resolvedOp);
}
+void OpReorderer::deferFunctorOp(const FunctorOp& op) {
+ BakedOpState* bakedState = tryBakeOpState(op);
+ if (!bakedState) return; // quick rejected
+ currentLayer().deferUnmergeableOp(mAllocator, bakedState, OpBatchType::None);
+}
+
void OpReorderer::deferLinesOp(const LinesOp& op) {
batchid_t batch = op.paint->isAntiAlias() ? OpBatchType::AlphaVertices : OpBatchType::Vertices;
deferStrokeableOp(op, batch, BakedOpState::StrokeBehavior::Forced);
diff --git a/libs/hwui/OpenGLRenderer.cpp b/libs/hwui/OpenGLRenderer.cpp
index f49237c..92b758d 100644
--- a/libs/hwui/OpenGLRenderer.cpp
+++ b/libs/hwui/OpenGLRenderer.cpp
@@ -1474,7 +1474,7 @@
.setMeshTexturedMesh(vertices, bitmapCount * 6)
.setFillTexturePaint(*texture, textureFillFlags, paint, currentSnapshot()->alpha)
.setTransform(*currentSnapshot(), transformFlags)
- .setModelViewOffsetRectOptionalSnap(snap, x, y, Rect(0, 0, bounds.getWidth(), bounds.getHeight()))
+ .setModelViewOffsetRectOptionalSnap(snap, x, y, Rect(bounds.getWidth(), bounds.getHeight()))
.build();
renderGlop(glop, GlopRenderType::Multi);
}
@@ -1497,7 +1497,7 @@
.setMeshTexturedUnitQuad(texture->uvMapper)
.setFillTexturePaint(*texture, textureFillFlags, paint, currentSnapshot()->alpha)
.setTransform(*currentSnapshot(), TransformFlags::None)
- .setModelViewMapUnitToRectSnap(Rect(0, 0, texture->width, texture->height))
+ .setModelViewMapUnitToRectSnap(Rect(texture->width, texture->height))
.build();
renderGlop(glop);
}
@@ -1642,7 +1642,7 @@
.setMeshPatchQuads(*mesh)
.setFillTexturePaint(*texture, textureFillFlags, paint, currentSnapshot()->alpha)
.setTransform(*currentSnapshot(), TransformFlags::None)
- .setModelViewOffsetRectSnap(left, top, Rect(0, 0, right - left, bottom - top)) // TODO: get minimal bounds from patch
+ .setModelViewOffsetRectSnap(left, top, Rect(right - left, bottom - top)) // TODO: get minimal bounds from patch
.build();
renderGlop(glop);
}
@@ -1672,7 +1672,7 @@
.setMeshTexturedIndexedQuads(vertices, elementCount)
.setFillTexturePaint(*texture, textureFillFlags, paint, currentSnapshot()->alpha)
.setTransform(*currentSnapshot(), transformFlags)
- .setModelViewOffsetRect(0, 0, Rect(0, 0, 0, 0))
+ .setModelViewOffsetRect(0, 0, Rect())
.build();
renderGlop(glop, GlopRenderType::Multi);
}
@@ -2268,7 +2268,7 @@
.setMeshTexturedIndexedQuads(layer->mesh, layer->meshElementCount)
.setFillLayer(layer->getTexture(), layer->getColorFilter(), getLayerAlpha(layer), layer->getMode(), Blend::ModeOrderSwap::NoSwap)
.setTransform(*currentSnapshot(), TransformFlags::None)
- .setModelViewOffsetRectSnap(0, 0, Rect(0, 0, layer->layer.getWidth(), layer->layer.getHeight()))
+ .setModelViewOffsetRectSnap(0, 0, Rect(layer->layer.getWidth(), layer->layer.getHeight()))
.build();
DRAW_DOUBLE_STENCIL_IF(!layer->hasDrawnSinceUpdate, renderGlop(glop));
#if DEBUG_LAYERS_AS_REGIONS
diff --git a/libs/hwui/RecordedOp.h b/libs/hwui/RecordedOp.h
index d1a4866..b58b774 100644
--- a/libs/hwui/RecordedOp.h
+++ b/libs/hwui/RecordedOp.h
@@ -49,6 +49,7 @@
U_OP_FN(BitmapMeshOp) \
U_OP_FN(BitmapRectOp) \
U_OP_FN(CirclePropsOp) \
+ U_OP_FN(FunctorOp) \
U_OP_FN(LinesOp) \
U_OP_FN(OvalOp) \
M_OP_FN(PatchOp) \
@@ -195,6 +196,13 @@
const float* radius;
};
+struct FunctorOp : RecordedOp {
+ FunctorOp(BASE_PARAMS_PAINTLESS, Functor* functor)
+ : SUPER_PAINTLESS(FunctorOp)
+ , functor(functor) {}
+ Functor* functor;
+};
+
struct LinesOp : RecordedOp {
LinesOp(BASE_PARAMS, const float* points, const int floatCount)
: SUPER(LinesOp)
@@ -341,7 +349,7 @@
*/
struct EndLayerOp : RecordedOp {
EndLayerOp()
- : RecordedOp(RecordedOpId::EndLayerOp, Rect(0, 0), Matrix4::identity(), Rect(0, 0), nullptr) {}
+ : RecordedOp(RecordedOpId::EndLayerOp, Rect(), Matrix4::identity(), Rect(), nullptr) {}
};
/**
diff --git a/libs/hwui/RecordingCanvas.cpp b/libs/hwui/RecordingCanvas.cpp
index 1bf92be..7f035e5 100644
--- a/libs/hwui/RecordingCanvas.cpp
+++ b/libs/hwui/RecordingCanvas.cpp
@@ -197,8 +197,7 @@
// Clip
bool RecordingCanvas::getClipBounds(SkRect* outRect) const {
- Rect bounds = mState.getLocalClipBounds();
- *outRect = SkRect::MakeLTRB(bounds.left, bounds.top, bounds.right, bounds.bottom);
+ *outRect = mState.getLocalClipBounds().toSkRect();
return !(outRect->isEmpty());
}
bool RecordingCanvas::quickRejectRect(float left, float top, float right, float bottom) const {
@@ -511,11 +510,12 @@
void RecordingCanvas::drawBitmap(const SkBitmap* bitmap, const SkPaint* paint) {
addOp(new (alloc()) BitmapOp(
- Rect(0, 0, bitmap->width(), bitmap->height()),
+ Rect(bitmap->width(), bitmap->height()),
*(mState.currentSnapshot()->transform),
mState.getRenderTargetClipBounds(),
refPaint(paint), refBitmap(*bitmap)));
}
+
void RecordingCanvas::drawRenderNode(RenderNode* renderNode) {
auto&& stagingProps = renderNode->stagingProperties();
RenderNodeOp* op = new (alloc()) RenderNodeOp(
@@ -536,6 +536,15 @@
}
}
+void RecordingCanvas::callDrawGLFunction(Functor* functor) {
+ mDisplayList->functors.push_back(functor);
+ addOp(new (alloc()) FunctorOp(
+ mState.getRenderTargetClipBounds(), // TODO: explicitly define bounds
+ *(mState.currentSnapshot()->transform),
+ mState.getRenderTargetClipBounds(),
+ functor));
+}
+
size_t RecordingCanvas::addOp(RecordedOp* op) {
// TODO: validate if "addDrawOp" quickrejection logic is useful before adding
int insertIndex = mDisplayList->ops.size();
diff --git a/libs/hwui/RecordingCanvas.h b/libs/hwui/RecordingCanvas.h
index 6fbaa8a..49bdba8 100644
--- a/libs/hwui/RecordingCanvas.h
+++ b/libs/hwui/RecordingCanvas.h
@@ -61,6 +61,9 @@
}
void drawRenderNode(RenderNode* renderNode);
+ // TODO: rename for consistency
+ void callDrawGLFunction(Functor* functor);
+
// ----------------------------------------------------------------------------
// CanvasStateClient interface
// ----------------------------------------------------------------------------
diff --git a/libs/hwui/tests/unit/BakedOpStateTests.cpp b/libs/hwui/tests/unit/BakedOpStateTests.cpp
index 8321ff9..33eff5b 100644
--- a/libs/hwui/tests/unit/BakedOpStateTests.cpp
+++ b/libs/hwui/tests/unit/BakedOpStateTests.cpp
@@ -34,7 +34,7 @@
auto parentSnapshot = TestUtils::makeSnapshot(Matrix4::identity(), Rect(100, 200));
ResolvedRenderState state(*parentSnapshot, recordedOp, false);
EXPECT_MATRIX_APPROX_EQ(state.transform, translate10x20);
- EXPECT_EQ(Rect(0, 0, 100, 200), state.clipRect);
+ EXPECT_EQ(Rect(100, 200), state.clipRect);
EXPECT_EQ(Rect(40, 60, 100, 200), state.clippedBounds); // translated and also clipped
EXPECT_EQ(OpClipSideFlags::Right | OpClipSideFlags::Bottom, state.clipSideFlags);
}
@@ -196,7 +196,7 @@
SkPaint paint;
paint.setStyle(SkPaint::kStrokeAndFill_Style);
paint.setStrokeWidth(0.0f);
- RectOp rejectOp(Rect(0, 0, 100, 200), Matrix4::identity(), Rect(100, 200), &paint);
+ RectOp rejectOp(Rect(100, 200), Matrix4::identity(), Rect(100, 200), &paint);
auto snapshot = TestUtils::makeSnapshot(Matrix4::identity(), Rect()); // Note: empty clip
auto bakedState = BakedOpState::tryStrokeableOpConstruct(allocator, *snapshot, rejectOp,
BakedOpState::StrokeBehavior::StyleDefined);
diff --git a/libs/hwui/tests/unit/CanvasStateTests.cpp b/libs/hwui/tests/unit/CanvasStateTests.cpp
index dfbf6d3..4df2687 100644
--- a/libs/hwui/tests/unit/CanvasStateTests.cpp
+++ b/libs/hwui/tests/unit/CanvasStateTests.cpp
@@ -57,7 +57,7 @@
simpleTranslate.loadTranslate(10, 20, 0);
state.setMatrix(simpleTranslate);
- ASSERT_EQ(state.getRenderTargetClipBounds(), Rect(0, 0, 200, 200));
+ ASSERT_EQ(state.getRenderTargetClipBounds(), Rect(200, 200));
ASSERT_EQ(state.getLocalClipBounds(), Rect(-10, -20, 190, 180));
EXPECT_TRUE(approxEqual(*state.currentTransform(), simpleTranslate));
EXPECT_TRUE(state.clipIsSimple());
@@ -69,7 +69,7 @@
0, 0, 200, 200, Vector3());
state.clipRect(0, 0, 100, 100, SkRegion::kIntersect_Op);
- ASSERT_EQ(state.getRenderTargetClipBounds(), Rect(0, 0, 100, 100));
+ ASSERT_EQ(state.getRenderTargetClipBounds(), Rect(100, 100));
state.clipRect(10, 10, 200, 200, SkRegion::kIntersect_Op);
ASSERT_EQ(state.getRenderTargetClipBounds(), Rect(10, 10, 100, 100));
@@ -122,10 +122,10 @@
state.save(SkCanvas::kClip_SaveFlag);
{
state.clipRect(0, 0, 10, 10, SkRegion::kIntersect_Op);
- ASSERT_EQ(state.getRenderTargetClipBounds(), Rect(0, 0, 10, 10));
+ ASSERT_EQ(state.getRenderTargetClipBounds(), Rect(10, 10));
}
state.restore();
- ASSERT_EQ(state.getRenderTargetClipBounds(), Rect(0, 0, 200, 200)); // verify restore
+ ASSERT_EQ(state.getRenderTargetClipBounds(), Rect(200, 200)); // verify restore
Matrix4 simpleTranslate;
simpleTranslate.loadTranslate(10, 10, 0);
@@ -146,10 +146,10 @@
state.save(SkCanvas::kMatrix_SaveFlag); // NOTE: clip not saved
{
state.clipRect(0, 0, 10, 10, SkRegion::kIntersect_Op);
- ASSERT_EQ(state.getRenderTargetClipBounds(), Rect(0, 0, 10, 10));
+ ASSERT_EQ(state.getRenderTargetClipBounds(), Rect(10, 10));
}
state.restore();
- ASSERT_EQ(state.getRenderTargetClipBounds(), Rect(0, 0, 10, 10)); // verify not restored
+ ASSERT_EQ(state.getRenderTargetClipBounds(), Rect(10, 10)); // verify not restored
Matrix4 simpleTranslate;
simpleTranslate.loadTranslate(10, 10, 0);
diff --git a/libs/hwui/tests/unit/OpReordererTests.cpp b/libs/hwui/tests/unit/OpReordererTests.cpp
index 5eac498..ac356a47 100644
--- a/libs/hwui/tests/unit/OpReordererTests.cpp
+++ b/libs/hwui/tests/unit/OpReordererTests.cpp
@@ -259,7 +259,7 @@
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(Rect(200, 200), state.computedState.clippedBounds);
EXPECT_EQ(SK_ColorDKGRAY, op.paint->getColor());
break;
case 1:
@@ -337,8 +337,8 @@
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);
+ EXPECT_EQ(Rect(180, 180), state.computedState.clippedBounds);
+ EXPECT_EQ(Rect(180, 180), state.computedState.clipRect);
Matrix4 expectedTransform;
expectedTransform.loadTranslate(-10, -10, 0);
@@ -347,7 +347,7 @@
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_EQ(Rect(200, 200), state.computedState.clipRect);
EXPECT_TRUE(state.computedState.transform.isIdentity());
}
};
@@ -399,19 +399,19 @@
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
+ EXPECT_EQ(Rect(400, 400), op.unmappedBounds); // inner rect
} else if (index == 4) {
- EXPECT_EQ(Rect(0, 0, 800, 800), op.unmappedBounds); // outer rect
+ EXPECT_EQ(Rect(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
+ EXPECT_EQ(Rect(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
+ EXPECT_EQ(Rect(800, 800), op.unmappedBounds); // outer layer
} else { ADD_FAILURE(); }
}
};
@@ -1091,7 +1091,7 @@
});
EXPECT_EQ(190u, observedData.layerWidth);
EXPECT_EQ(200u, observedData.layerHeight);
- EXPECT_EQ(Rect(0, 0, 190, 200), observedData.rectClippedBounds)
+ EXPECT_EQ(Rect(190, 200), observedData.rectClippedBounds)
<< "expect content to be clipped to screen area";
Matrix4 expected;
expected.loadTranslate(0, -2000, 0);
@@ -1114,7 +1114,7 @@
// ceil(sqrt(2) / 2 * 200) = 142
EXPECT_EQ(142u, observedData.layerWidth);
EXPECT_EQ(142u, observedData.layerHeight);
- EXPECT_EQ(Rect(0, 0, 142, 142), observedData.rectClippedBounds);
+ EXPECT_EQ(Rect(142, 142), observedData.rectClippedBounds);
EXPECT_MATRIX_APPROX_EQ(Matrix4::identity(), observedData.rectMatrix);
}
@@ -1128,7 +1128,7 @@
});
EXPECT_EQ(100u, observedData.layerWidth);
EXPECT_EQ(400u, observedData.layerHeight);
- EXPECT_EQ(Rect(0, 0, 100, 400), observedData.rectClippedBounds);
+ EXPECT_EQ(Rect(100, 400), observedData.rectClippedBounds);
EXPECT_MATRIX_APPROX_EQ(Matrix4::identity(), observedData.rectMatrix);
}
diff --git a/libs/hwui/tests/unit/RecordingCanvasTests.cpp b/libs/hwui/tests/unit/RecordingCanvasTests.cpp
index ba9d185..08f927c 100644
--- a/libs/hwui/tests/unit/RecordingCanvasTests.cpp
+++ b/libs/hwui/tests/unit/RecordingCanvasTests.cpp
@@ -54,7 +54,7 @@
ASSERT_EQ(RecordedOpId::LinesOp, op->opId);
EXPECT_EQ(4, ((LinesOp*)op)->floatCount)
<< "float count must be rounded down to closest multiple of 4";
- EXPECT_EQ(Rect(0, 0, 20, 10), op->unmappedBounds)
+ EXPECT_EQ(Rect(20, 10), op->unmappedBounds)
<< "unmapped bounds must be size of line, and not outset for stroke width";
}
@@ -66,7 +66,7 @@
ASSERT_EQ(1u, dl->getOps().size()) << "Must be exactly one op";
auto op = *(dl->getOps()[0]);
ASSERT_EQ(RecordedOpId::RectOp, op.opId);
- EXPECT_EQ(Rect(0, 0, 100, 200), op.localClipRect);
+ EXPECT_EQ(Rect(100, 200), op.localClipRect);
EXPECT_EQ(Rect(10, 20, 90, 180), op.unmappedBounds);
}
@@ -83,7 +83,7 @@
playbackOps(*dl, [&count](const RecordedOp& op) {
count++;
ASSERT_EQ(RecordedOpId::TextOp, op.opId);
- EXPECT_EQ(Rect(0, 0, 200, 200), op.localClipRect);
+ EXPECT_EQ(Rect(200, 200), op.localClipRect);
EXPECT_TRUE(op.localMatrix.isIdentity());
EXPECT_TRUE(op.unmappedBounds.contains(25, 15, 50, 25))
<< "Op expected to be 25+ pixels wide, 10+ pixels tall";
@@ -184,8 +184,8 @@
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);
+ EXPECT_EQ(Rect(100, 200), op.unmappedBounds);
+ EXPECT_EQ(Rect(100, 200), op.localClipRect);
Matrix4 expectedMatrix;
expectedMatrix.loadIdentity();
@@ -193,8 +193,8 @@
} 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);
+ EXPECT_EQ(Rect(25, 25), op.unmappedBounds);
+ EXPECT_EQ(Rect(100, 200), op.localClipRect);
Matrix4 expectedMatrix;
expectedMatrix.loadTranslate(25, 25, 0);
@@ -219,12 +219,12 @@
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_EQ(Rect(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(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);
@@ -254,9 +254,9 @@
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"
+ EXPECT_EQ(Rect(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);
+ EXPECT_EQ(Rect(400, 400), op.unmappedBounds);
expectedMatrix.loadTranslate(-100, -100, 0);
EXPECT_MATRIX_APPROX_EQ(expectedMatrix, op.localMatrix);
}
@@ -281,8 +281,8 @@
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_EQ(Rect(100, 100), op.localClipRect);
+ EXPECT_EQ(Rect(100, 100), op.unmappedBounds);
EXPECT_MATRIX_APPROX_EQ(Matrix4::identity(), op.localMatrix)
<< "Recorded op shouldn't see any canvas transform before the saveLayer";
}
@@ -313,7 +313,7 @@
// ...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);
+ EXPECT_EQ(Rect(400, 400), op.unmappedBounds);
expectedMatrix.loadIdentity();
EXPECT_MATRIX_APPROX_EQ(expectedMatrix, op.localMatrix);
}
diff --git a/libs/input/PointerController.cpp b/libs/input/PointerController.cpp
index 6a1167a..529849e 100644
--- a/libs/input/PointerController.cpp
+++ b/libs/input/PointerController.cpp
@@ -439,6 +439,17 @@
}
}
+void PointerController::setCustomPointerIcon(const SpriteIcon& icon) {
+ AutoMutex _l(mLock);
+
+ const int32_t iconId = mPolicy->getCustomPointerIconId();
+ mLocked.additionalMouseResources[iconId] = icon;
+ mLocked.requestedPointerShape = iconId;
+ mLocked.presentationChanged = true;
+
+ updatePointerLocked();
+}
+
void PointerController::handleMessage(const Message& message) {
switch (message.what) {
case MSG_INACTIVITY_TIMEOUT:
diff --git a/libs/input/PointerController.h b/libs/input/PointerController.h
index 4fd2d85..9ba37b3 100644
--- a/libs/input/PointerController.h
+++ b/libs/input/PointerController.h
@@ -67,6 +67,7 @@
virtual void loadAdditionalMouseResources(std::map<int32_t, SpriteIcon>* outResources,
std::map<int32_t, PointerAnimation>* outAnimationResources) = 0;
virtual int32_t getDefaultPointerIconId() = 0;
+ virtual int32_t getCustomPointerIconId() = 0;
};
@@ -105,6 +106,7 @@
virtual void clearSpots();
void updatePointerShape(int32_t iconId);
+ void setCustomPointerIcon(const SpriteIcon& icon);
void setDisplayViewport(int32_t width, int32_t height, int32_t orientation);
void setInactivityTimeout(InactivityTimeout inactivityTimeout);
void reloadPointerResources();
diff --git a/packages/BackupRestoreConfirmation/res/values-b+sr+Latn/strings.xml b/packages/BackupRestoreConfirmation/res/values-b+sr+Latn/strings.xml
new file mode 100644
index 0000000..c46ff49
--- /dev/null
+++ b/packages/BackupRestoreConfirmation/res/values-b+sr+Latn/strings.xml
@@ -0,0 +1,39 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Copyright (C) 2011 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="backup_confirm_title" msgid="827563724209303345">"Rezervna kopije svih podataka"</string>
+ <string name="restore_confirm_title" msgid="5469365809567486602">"Potpuno vraćanje"</string>
+ <string name="backup_confirm_text" msgid="1878021282758896593">"Zahtevana je potpuna rezervna kopija svih podataka na povezani stoni računar. Da li želite da dozvolite to?\n\nAko niste lično zahtevali rezervnu kopiju, ne dozvoljavajte nastavak radnje."</string>
+ <string name="allow_backup_button_label" msgid="4217228747769644068">"Napravi rezervnu kopiju mojih podataka"</string>
+ <string name="deny_backup_button_label" msgid="6009119115581097708">"Ne pravi rezervne kopije"</string>
+ <string name="restore_confirm_text" msgid="7499866728030461776">"Zahtevano je potpuno vraćanje svih podataka sa povezanog stonog računara. Da li želite da dozvolite to?\n\nAko niste lično zahtevali vraćanje, ne dozvoljavajte nastavak radnje. Time ćete zameniti sve podatke koji su trenutno na uređaju!"</string>
+ <string name="allow_restore_button_label" msgid="3081286752277127827">"Vrati moje podatke"</string>
+ <string name="deny_restore_button_label" msgid="1724367334453104378">"Ne vraćaj"</string>
+ <string name="current_password_text" msgid="8268189555578298067">"Unesite trenutnu lozinku rezervne kopije u nastavku:"</string>
+ <string name="device_encryption_restore_text" msgid="1570864916855208992">"Unesite lozinku uređaja za šifrovanje u nastavku."</string>
+ <string name="device_encryption_backup_text" msgid="5866590762672844664">"Unesite lozinku uređaja za šifrovanje. Ovo će se koristiti i za šifrovanje rezervne arhive."</string>
+ <string name="backup_enc_password_text" msgid="4981585714795233099">"Unesite lozinku koju ćete koristiti za šifrovanje podataka potpune rezervne kopije. Ako to polje ostavite prazno, koristiće se trenutna lozinka rezervne kopije:"</string>
+ <string name="backup_enc_password_optional" msgid="1350137345907579306">"Ako želite da šifrujete podatke potpune rezervne kopije, unesite lozinku u nastavku."</string>
+ <string name="backup_enc_password_required" msgid="7889652203371654149">"Pošto vam je uređaj šifrovan, morate da šifrujete rezervnu kopiju. Unesite lozinku u nastavku:"</string>
+ <string name="restore_enc_password_text" msgid="6140898525580710823">"Ako su podaci za vraćanje šifrovani, unesite lozinku u nastavku:"</string>
+ <string name="toast_backup_started" msgid="550354281452756121">"Pokretanje pravljenja rezervne kopije..."</string>
+ <string name="toast_backup_ended" msgid="3818080769548726424">"Rezervna kopija je napravljena"</string>
+ <string name="toast_restore_started" msgid="7881679218971277385">"Pokretanje vraćanja..."</string>
+ <string name="toast_restore_ended" msgid="1764041639199696132">"Vraćanje je završeno"</string>
+ <string name="toast_timeout" msgid="5276598587087626877">"Vreme za radnju je isteklo"</string>
+</resources>
diff --git a/packages/CaptivePortalLogin/res/values-b+sr+Latn/strings.xml b/packages/CaptivePortalLogin/res/values-b+sr+Latn/strings.xml
new file mode 100644
index 0000000..fb05a9ed
--- /dev/null
+++ b/packages/CaptivePortalLogin/res/values-b+sr+Latn/strings.xml
@@ -0,0 +1,11 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="app_name" msgid="5934709770924185752">"CaptivePortalLogin"</string>
+ <string name="action_use_network" msgid="6076184727448466030">"Koristi ovu mrežu takvu kakva je"</string>
+ <string name="action_do_not_use_network" msgid="4577366536956516683">"Ne koristi ovu mrežu"</string>
+ <string name="action_bar_label" msgid="917235635415966620">"Prijavi me na mrežu"</string>
+ <string name="ssl_error_warning" msgid="6653188881418638872">"Mreža kojoj pokušavate da se pridružite ima bezbednosnih problema."</string>
+ <string name="ssl_error_example" msgid="647898534624078900">"Na primer, stranica za prijavljivanje možda ne pripada prikazanoj organizaciji."</string>
+ <string name="ssl_error_continue" msgid="6492718244923937110">"Ipak nastavi preko pregledača"</string>
+</resources>
diff --git a/packages/DefaultContainerService/res/values-b+sr+Latn/strings.xml b/packages/DefaultContainerService/res/values-b+sr+Latn/strings.xml
new file mode 100644
index 0000000..a0e1734
--- /dev/null
+++ b/packages/DefaultContainerService/res/values-b+sr+Latn/strings.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+**
+** Copyright 2008, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="service_name" msgid="4841491635055379553">"Pomoćnik za pristup paketu"</string>
+</resources>
diff --git a/packages/DocumentsUI/res/values-b+sr+Latn/config.xml b/packages/DocumentsUI/res/values-b+sr+Latn/config.xml
new file mode 100644
index 0000000..843a8aa
--- /dev/null
+++ b/packages/DocumentsUI/res/values-b+sr+Latn/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-b+sr+Latn/strings.xml b/packages/DocumentsUI/res/values-b+sr+Latn/strings.xml
new file mode 100644
index 0000000..feace71
--- /dev/null
+++ b/packages/DocumentsUI/res/values-b+sr+Latn/strings.xml
@@ -0,0 +1,110 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="app_label" msgid="2783841764617238354">"Dokumenti"</string>
+ <string name="files_label" msgid="6051402950202690279">"Datoteke"</string>
+ <string name="downloads_label" msgid="959113951084633612">"Preuzimanja"</string>
+ <string name="title_open" msgid="4353228937663917801">"Otvori sa"</string>
+ <string name="title_save" msgid="2433679664882857999">"Sačuvaj u"</string>
+ <string name="menu_create_dir" msgid="2547620241173881754">"Novi direktorijum"</string>
+ <string name="menu_grid" msgid="6878021334497835259">"Prikaz mreže"</string>
+ <string name="menu_list" msgid="7279285939892417279">"Prikaz liste"</string>
+ <string name="menu_sort" msgid="7677740407158414452">"Sortiraj prema"</string>
+ <string name="menu_search" msgid="3816712084502856974">"Pretraži"</string>
+ <string name="menu_settings" msgid="6008033148948428823">"Podešavanja"</string>
+ <string name="menu_open" msgid="432922957274920903">"Otvori"</string>
+ <string name="menu_save" msgid="2394743337684426338">"Sačuvaj"</string>
+ <string name="menu_share" msgid="3075149983979628146">"Deli"</string>
+ <string name="menu_delete" msgid="8138799623850614177">"Izbriši"</string>
+ <string name="menu_select_all" msgid="8323579667348729928">"Izaberi sve"</string>
+ <string name="menu_copy" msgid="3612326052677229148">"Kopiraj na..."</string>
+ <string name="menu_move" msgid="1828090633118079817">"Premesti u..."</string>
+ <string name="menu_new_window" msgid="1226032889278727538">"Novi prozor"</string>
+ <string name="menu_copy_to_clipboard" msgid="489311381979634291">"Kopiraj"</string>
+ <string name="menu_paste_from_clipboard" msgid="2071583031180257091">"Nalepi"</string>
+ <string name="menu_advanced_show" product="nosdcard" msgid="4693652895715631401">"Prikaži internu memoriju"</string>
+ <string name="menu_advanced_show" product="default" msgid="5792182900084144261">"Prikaži SD karticu"</string>
+ <string name="menu_advanced_hide" product="nosdcard" msgid="4218809952721972589">"Sakrij internu memoriju"</string>
+ <string name="menu_advanced_hide" product="default" msgid="4845869969015718848">"Sakrij SD karticu"</string>
+ <string name="menu_file_size_show" msgid="3240323619260823076">"Prikaži veličinu datoteke"</string>
+ <string name="menu_file_size_hide" msgid="8881975928502581042">"Sakrij veličinu datoteke"</string>
+ <string name="button_select" msgid="527196987259139214">"Izaberi"</string>
+ <string name="button_copy" msgid="8706475544635021302">"Kopiraj"</string>
+ <string name="button_move" msgid="2202666023104202232">"Premesti"</string>
+ <string name="button_dismiss" msgid="3714065566893946085">"Odbaci"</string>
+ <string name="button_retry" msgid="4392027584153752797">"Pokušaj ponovo"</string>
+ <string name="sort_name" msgid="9183560467917256779">"Prema imenu"</string>
+ <string name="sort_date" msgid="586080032956151448">"Prema datumu izmene"</string>
+ <string name="sort_size" msgid="3350681319735474741">"Prema veličini"</string>
+ <string name="drawer_open" msgid="4545466532430226949">"Prikaži osnovne elemente"</string>
+ <string name="drawer_close" msgid="7602734368552123318">"Sakrij osnovne elemente"</string>
+ <string name="save_error" msgid="6167009778003223664">"Čuvanje dokumenta nije uspelo"</string>
+ <string name="create_error" msgid="3735649141335444215">"Direktorijum nije napravljen"</string>
+ <string name="query_error" msgid="1222448261663503501">"Slanje upita za dokumente nije uspelo"</string>
+ <string name="root_recent" msgid="4470053704320518133">"Nedavno"</string>
+ <string name="root_available_bytes" msgid="8568452858617033281">"Slobodno je <xliff:g id="SIZE">%1$s</xliff:g>"</string>
+ <string name="root_type_service" msgid="2178854894416775409">"Usluge skladištenja"</string>
+ <string name="root_type_shortcut" msgid="3318760609471618093">"Prečice"</string>
+ <string name="root_type_device" msgid="7121342474653483538">"Uređaji"</string>
+ <string name="root_type_apps" msgid="8838065367985945189">"Još aplikacija"</string>
+ <string name="empty" msgid="7858882803708117596">"Nema stavki"</string>
+ <string name="toast_no_application" msgid="1339885974067891667">"Nije moguće otvoriti datoteku"</string>
+ <string name="toast_failed_delete" msgid="2180678019407244069">"Nije moguće izbrisati neke dokumente"</string>
+ <string name="share_via" msgid="8966594246261344259">"Delite preko"</string>
+ <string name="copy_notification_title" msgid="6374299806748219777">"Kopiranje datoteka"</string>
+ <string name="move_notification_title" msgid="6193835179777284805">"Datoteke se premeštaju"</string>
+ <string name="copy_remaining" msgid="6283790937387975095">"Još <xliff:g id="DURATION">%s</xliff:g>"</string>
+ <plurals name="copy_begin" formatted="false" msgid="9071199452634086365">
+ <item quantity="one">Kopiranje <xliff:g id="COUNT_1">%1$d</xliff:g> datoteke.</item>
+ <item quantity="few">Kopiranje <xliff:g id="COUNT_1">%1$d</xliff:g> datoteke.</item>
+ <item quantity="other">Kopiranje <xliff:g id="COUNT_1">%1$d</xliff:g> datoteka.</item>
+ </plurals>
+ <plurals name="move_begin" formatted="false" msgid="8430330882138871643">
+ <item quantity="one">Premešta se <xliff:g id="COUNT_1">%1$d</xliff:g> datoteka.</item>
+ <item quantity="few">Premeštaju se <xliff:g id="COUNT_1">%1$d</xliff:g> datoteke.</item>
+ <item quantity="other">Premešta se <xliff:g id="COUNT_1">%1$d</xliff:g> datoteka.</item>
+ </plurals>
+ <plurals name="deleting" formatted="false" msgid="5054338566802559411">
+ <item quantity="one">Briše se <xliff:g id="COUNT_1">%1$d</xliff:g> datoteka.</item>
+ <item quantity="few">Brišu se <xliff:g id="COUNT_1">%1$d</xliff:g> datoteke.</item>
+ <item quantity="other">Briše se <xliff:g id="COUNT_1">%1$d</xliff:g> datoteka.</item>
+ </plurals>
+ <string name="undo" msgid="7905788502491742328">"Opozovi"</string>
+ <string name="copy_preparing" msgid="3896202461003039386">"Priprema se kopiranje…"</string>
+ <string name="move_preparing" msgid="2772219441375531410">"Priprema se premeštanje..."</string>
+ <plurals name="copy_error_notification_title" formatted="false" msgid="5267616889076217261">
+ <item quantity="one">Nismo uspeli da kopiramo <xliff:g id="COUNT_1">%1$d</xliff:g> datoteku</item>
+ <item quantity="few">Nismo uspeli da kopiramo <xliff:g id="COUNT_1">%1$d</xliff:g> datoteke</item>
+ <item quantity="other">Nismo uspeli da kopiramo <xliff:g id="COUNT_1">%1$d</xliff:g> datoteka</item>
+ </plurals>
+ <plurals name="move_error_notification_title" formatted="false" msgid="2779299594174898891">
+ <item quantity="one">Nije uspelo premeštanje <xliff:g id="COUNT_1">%1$d</xliff:g> datoteke</item>
+ <item quantity="few">Nije uspelo premeštanje <xliff:g id="COUNT_1">%1$d</xliff:g> datoteke</item>
+ <item quantity="other">Nije uspelo premeštanje <xliff:g id="COUNT_1">%1$d</xliff:g> datoteka</item>
+ </plurals>
+ <string name="notification_touch_for_details" msgid="4483108577842961665">"Dodirnite da biste videli detalje"</string>
+ <string name="retry" msgid="7564024179122207376">"Pokušaj ponovo"</string>
+ <string name="copy_failure_alert_content" msgid="3715575000297709082">"Sledeće datoteke nisu kopirane: <xliff:g id="LIST">%1$s</xliff:g>"</string>
+ <string name="move_failure_alert_content" msgid="7151140279020481180">"Ove datoteke nisu premeštene: <xliff:g id="LIST">%1$s</xliff:g>"</string>
+ <plurals name="clipboard_files_clipped" formatted="false" msgid="855459017537058539">
+ <item quantity="one">Kopirali ste <xliff:g id="COUNT_1">%1$d</xliff:g> datoteku u privremenu memoriju.</item>
+ <item quantity="few">Kopirali ste <xliff:g id="COUNT_1">%1$d</xliff:g> datoteke u privremenu memoriju.</item>
+ <item quantity="other">Kopirali ste <xliff:g id="COUNT_1">%1$d</xliff:g> datoteka u privremenu memoriju.</item>
+ </plurals>
+ <string name="clipboard_files_cannot_paste" msgid="2878324825602325706">"Izabrane datoteke ne mogu da se nalepe na ovoj lokaciji."</string>
+</resources>
diff --git a/packages/DocumentsUI/src/com/android/documentsui/CopyService.java b/packages/DocumentsUI/src/com/android/documentsui/CopyService.java
index b99c806..6d947d1 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/CopyService.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/CopyService.java
@@ -28,6 +28,7 @@
import android.content.ContentProviderClient;
import android.content.Context;
import android.content.Intent;
+import android.content.res.AssetFileDescriptor;
import android.content.res.Resources;
import android.database.Cursor;
import android.net.Uri;
@@ -170,8 +171,27 @@
setupCopyJob(srcs, stack, transferMode);
+ final String opDesc = transferMode == TRANSFER_MODE_COPY ? "copy" : "move";
+ DocumentInfo srcInfo;
+ DocumentInfo dstInfo;
for (int i = 0; i < srcs.size() && !mIsCancelled; ++i) {
- copy(srcs.get(i), stack.peek(), transferMode);
+ srcInfo = srcs.get(i);
+ dstInfo = stack.peek();
+
+ // Guard unsupported recursive operation.
+ if (dstInfo.equals(srcInfo) || isDescendentOf(srcInfo, dstInfo)) {
+ if (DEBUG) Log.d(TAG,
+ "Skipping recursive " + opDesc + " of directory " + dstInfo.derivedUri);
+ mFailedFiles.add(srcInfo);
+ continue;
+ }
+
+ if (DEBUG) Log.d(TAG,
+ "Performing " + opDesc + " of " + srcInfo.displayName
+ + " (" + srcInfo.derivedUri + ")" + " to " + dstInfo.displayName
+ + " (" + dstInfo.derivedUri + ")");
+
+ copy(srcInfo, dstInfo, transferMode);
}
} catch (Exception e) {
// Catch-all to prevent any copy errors from wedging the app.
@@ -447,22 +467,6 @@
*/
private void copy(DocumentInfo srcInfo, DocumentInfo dstDirInfo, int mode)
throws RemoteException {
-
- String opDesc = mode == TRANSFER_MODE_COPY ? "copy" : "move";
-
- // Guard unsupported recursive operation.
- if (dstDirInfo.equals(srcInfo) || isDescendentOf(srcInfo, dstDirInfo)) {
- if (DEBUG) Log.d(TAG,
- "Skipping recursive " + opDesc + " of directory " + dstDirInfo.derivedUri);
- mFailedFiles.add(srcInfo);
- return;
- }
-
- if (DEBUG) Log.d(TAG,
- "Performing " + opDesc + " of " + 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)) {
@@ -490,6 +494,8 @@
}
}
+ // Create the target document (either a file or a directory), then copy recursively the
+ // contents (bytes or children).
final Uri dstUri = DocumentsContract.createDocument(mDstClient, dstDirInfo.derivedUri,
srcInfo.mimeType, srcInfo.displayName);
if (dstUri == null) {
@@ -498,10 +504,30 @@
return;
}
- if (srcInfo.isDirectory()) {
- copyDirectoryHelper(srcInfo.derivedUri, dstUri, mode);
+ DocumentInfo dstInfo = null;
+ try {
+ final DocumentInfo dstDocInfo = DocumentInfo.fromUri(getContentResolver(), dstUri);
+ } catch (FileNotFoundException e) {
+ mFailedFiles.add(srcInfo);
+ return;
+ }
+
+ if (Document.MIME_TYPE_DIR.equals(srcInfo.mimeType)) {
+ copyDirectoryHelper(srcInfo, dstInfo, mode);
} else {
- copyFileHelper(srcInfo.derivedUri, dstUri, mode);
+ copyFileHelper(srcInfo, dstInfo, mode);
+ }
+
+ if (mode == TRANSFER_MODE_MOVE) {
+ try {
+ DocumentsContract.deleteDocument(mSrcClient, srcInfo.derivedUri);
+ } catch (RemoteException e) {
+ // RemoteExceptions usually signal that the connection is dead, so there's no
+ // point attempting to continue. Propagate the exception up so the copy job is
+ // cancelled.
+ Log.w(TAG, "Failed to clean up after move: " + srcInfo.derivedUri, e);
+ throw e;
+ }
}
}
@@ -520,48 +546,29 @@
* Handles recursion into a directory and copying its contents. Note that in linux terms, this
* does the equivalent of "cp src/* dst", not "cp -r src dst".
*
- * @param srcDirUri URI of the directory to copy from. The routine will copy the directory's
+ * @param srcDirInfo Info of the directory to copy from. The routine will copy the directory's
* contents, not the directory itself.
- * @param dstDirUri URI of the directory to copy to. Must be created beforehand.
+ * @param dstDirInfo Info of the directory to copy to. Must be created beforehand.
* @throws RemoteException
*/
- private void copyDirectoryHelper(Uri srcDirUri, Uri dstDirUri, int mode)
+ private void copyDirectoryHelper(DocumentInfo srcDirInfo, DocumentInfo dstDirInfo, int mode)
throws RemoteException {
// Recurse into directories. Copy children into the new subdirectory.
final String queryColumns[] = new String[] {
Document.COLUMN_DISPLAY_NAME,
Document.COLUMN_DOCUMENT_ID,
Document.COLUMN_MIME_TYPE,
- Document.COLUMN_SIZE
+ Document.COLUMN_SIZE,
+ Document.COLUMN_FLAGS
};
- final Uri queryUri = DocumentsContract.buildChildDocumentsUri(srcDirUri.getAuthority(),
- DocumentsContract.getDocumentId(srcDirUri));
Cursor cursor = null;
try {
// Iterate over srcs in the directory; copy to the destination directory.
- cursor = mSrcClient.query(queryUri, queryColumns, null, null, null);
+ DocumentInfo srcInfo;
+ cursor = mSrcClient.query(srcDirInfo.derivedUri, queryColumns, null, null, null);
while (cursor.moveToNext()) {
- final String childMimeType = getCursorString(cursor, Document.COLUMN_MIME_TYPE);
- final Uri dstUri = DocumentsContract.createDocument(mDstClient, dstDirUri,
- childMimeType, getCursorString(cursor, Document.COLUMN_DISPLAY_NAME));
- final Uri childUri = DocumentsContract.buildDocumentUri(srcDirUri.getAuthority(),
- getCursorString(cursor, Document.COLUMN_DOCUMENT_ID));
- if (Document.MIME_TYPE_DIR.equals(childMimeType)) {
- copyDirectoryHelper(childUri, dstUri, mode);
- } else {
- copyFileHelper(childUri, dstUri, mode);
- }
- }
- if (mode == TRANSFER_MODE_MOVE) {
- try {
- DocumentsContract.deleteDocument(mSrcClient, srcDirUri);
- } catch (RemoteException e) {
- // RemoteExceptions usually signal that the connection is dead, so there's no
- // point attempting to continue. Propagate the exception up so the copy job is
- // cancelled.
- Log.w(TAG, "Failed to clean up after move: " + srcDirUri, e);
- throw e;
- }
+ srcInfo = DocumentInfo.fromCursor(cursor, srcDirInfo.authority);
+ copy(srcInfo, dstDirInfo, mode);
}
} finally {
IoUtils.closeQuietly(cursor);
@@ -571,11 +578,11 @@
/**
* Handles copying a single file.
*
- * @param srcUri URI of the file to copy from.
- * @param dstUri URI of the *file* to copy to. Must be created beforehand.
+ * @param srcUriInfo Info of the file to copy from.
+ * @param dstUriInfo Info of the *file* to copy to. Must be created beforehand.
* @throws RemoteException
*/
- private void copyFileHelper(Uri srcUri, Uri dstUri, int mode)
+ private void copyFileHelper(DocumentInfo srcInfo, DocumentInfo dstInfo, int mode)
throws RemoteException {
// Copy an individual file.
CancellationSignal canceller = new CancellationSignal();
@@ -586,9 +593,26 @@
IOException copyError = null;
try {
- srcFile = mSrcClient.openFile(srcUri, "r", canceller);
- dstFile = mDstClient.openFile(dstUri, "w", canceller);
- src = new ParcelFileDescriptor.AutoCloseInputStream(srcFile);
+ // If the file is virtual, but can be converted to another format, then try to copy it
+ // as such format.
+ if (srcInfo.isVirtualDocument() && srcInfo.isTypedDocument()) {
+ final String[] streamTypes = mSrcClient.getStreamTypes(srcInfo.derivedUri, "*/*");
+ if (streamTypes.length > 0) {
+ // Pick the first streamable format.
+ final AssetFileDescriptor srcFileAsAsset =
+ mSrcClient.openTypedAssetFileDescriptor(
+ srcInfo.derivedUri, streamTypes[0], null, canceller);
+ srcFile = srcFileAsAsset.getParcelFileDescriptor();
+ src = new AssetFileDescriptor.AutoCloseInputStream(srcFileAsAsset);
+ } else {
+ // TODO: Log failures. b/26192412
+ mFailedFiles.add(srcInfo);
+ }
+ } else {
+ srcFile = mSrcClient.openFile(srcInfo.derivedUri, "r", canceller);
+ src = new ParcelFileDescriptor.AutoCloseInputStream(srcFile);
+ }
+ dstFile = mDstClient.openFile(dstInfo.derivedUri, "w", canceller);
dst = new ParcelFileDescriptor.AutoCloseOutputStream(dstFile);
byte[] buffer = new byte[8192];
@@ -601,18 +625,7 @@
srcFile.checkError();
} catch (IOException e) {
copyError = e;
-
- try {
- DocumentInfo info = DocumentInfo.fromUri(getContentResolver(), srcUri);
- mFailedFiles.add(info);
- } catch (FileNotFoundException ignore) {
- // Generate a dummy DocumentInfo so an error still gets reflected in the UI for this
- // file.
- DocumentInfo info = new DocumentInfo();
- info.derivedUri = srcUri;
- info.displayName = "Unknown [" + srcUri + "]";
- mFailedFiles.add(info);
- }
+ mFailedFiles.add(srcInfo);
if (dstFile != null) {
try {
@@ -627,26 +640,17 @@
IoUtils.closeQuietly(dst);
}
-
if (copyError != null || mIsCancelled) {
// Clean up half-copied files.
canceller.cancel();
try {
- DocumentsContract.deleteDocument(mDstClient, dstUri);
+ DocumentsContract.deleteDocument(mDstClient, dstInfo.derivedUri);
} catch (RemoteException e) {
- Log.w(TAG, "Failed to clean up after copy error: " + dstUri, e);
+ Log.w(TAG, "Failed to clean up after copy error: " + dstInfo.derivedUri, e);
// RemoteExceptions usually signal that the connection is dead, so there's no point
// attempting to continue. Propagate the exception up so the copy job is cancelled.
throw e;
}
- } else if (mode == TRANSFER_MODE_MOVE) {
- // Clean up src files after a successful move.
- try {
- DocumentsContract.deleteDocument(mSrcClient, srcUri);
- } catch (RemoteException e) {
- Log.w(TAG, "Failed to clean up after move: " + srcUri, e);
- throw e;
- }
}
}
diff --git a/packages/DocumentsUI/src/com/android/documentsui/FilesActivity.java b/packages/DocumentsUI/src/com/android/documentsui/FilesActivity.java
index bf44013..0b6a09f 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/FilesActivity.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/FilesActivity.java
@@ -101,21 +101,24 @@
// from EXTRA_STACK intent extra. In this case, we'll skip other means of
// loading or restoring the stack.
if (!mState.stack.isEmpty()) {
+ if (DEBUG) Log.d(TAG, "Launching with non-empty stack.");
// When restoring from a stack, if a URI is present, it should only ever
// be a launch URI. Launch URIs support sensible activity management, but
- // don't specify an real content target.
- if (uri != null) {
- checkState(LauncherActivity.isLaunchUri(uri));
- }
+ // don't specify a real content target.
+ checkState(uri == null || LauncherActivity.isLaunchUri(uri));
onCurrentDirectoryChanged(ANIM_NONE);
} else if (DocumentsContract.isRootUri(this, uri)) {
+ if (DEBUG) Log.d(TAG, "Launching with root URI.");
// If we've got a specific root to display, restore that root using a dedicated
// authority. That way a misbehaving provider won't result in an ANR.
new RestoreRootTask(uri).executeOnExecutor(
ProviderExecutor.forAuthority(uri.getAuthority()));
} else {
- // Finally, we try to restore a stack from recents.
- new RestoreStackTask().execute();
+ if (DEBUG) Log.d(TAG, "Launching into Home directory.");
+ // If all else fails, try to load "Home" directory.
+ uri = DocumentsContract.buildHomeUri();
+ new RestoreRootTask(uri).executeOnExecutor(
+ ProviderExecutor.forAuthority(uri.getAuthority()));
}
// TODO: Ensure we're handling CopyService errors correctly across all activities.
@@ -159,7 +162,17 @@
@Override
protected void onPostCreate(Bundle savedInstanceState) {
super.onPostCreate(savedInstanceState);
- updateActionBar();
+ // This check avoids a flicker from "Recents" to "Home".
+ // Only update action bar at this point if there is an active
+ // serach. Why? Because this avoid an early (undesired) load of
+ // the recents root...which is the default root in other activities.
+ // In Files app "Home" is the default, but it is loaded async.
+ // updateActionBar will be called once Home root is loaded.
+ // Except while searching we need this call to ensure the
+ // search bits get layed out correctly.
+ if (mSearchManager.isSearching()) {
+ updateActionBar();
+ }
}
@Override
diff --git a/packages/DocumentsUI/src/com/android/documentsui/model/DocumentInfo.java b/packages/DocumentsUI/src/com/android/documentsui/model/DocumentInfo.java
index 69b574d..215c6e6 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/model/DocumentInfo.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/model/DocumentInfo.java
@@ -251,6 +251,14 @@
return isDirectory() || isArchive();
}
+ public boolean isVirtualDocument() {
+ return (flags & Document.FLAG_VIRTUAL_DOCUMENT) != 0;
+ }
+
+ public boolean isTypedDocument() {
+ return (flags & Document.FLAG_SUPPORTS_TYPED_DOCUMENT) != 0;
+ }
+
public int hashCode() {
return derivedUri.hashCode() + mimeType.hashCode();
}
diff --git a/packages/DocumentsUI/tests/src/com/android/documentsui/FilesActivityUiTest.java b/packages/DocumentsUI/tests/src/com/android/documentsui/FilesActivityUiTest.java
index 71d8b34..f0dbfe9 100644
--- a/packages/DocumentsUI/tests/src/com/android/documentsui/FilesActivityUiTest.java
+++ b/packages/DocumentsUI/tests/src/com/android/documentsui/FilesActivityUiTest.java
@@ -137,11 +137,18 @@
mBot.assertHasDocuments("file0.log", "file1.png", "file2.csv");
}
+ public void testLoadsHomeByDefault() throws Exception {
+ initTestFiles();
+
+ mDevice.waitForIdle();
+ mBot.assertWindowTitle("Home");
+ }
+
public void testRootClickSetsWindowTitle() throws Exception {
initTestFiles();
- mBot.openRoot("Home");
- mBot.assertWindowTitle("Home");
+ mBot.openRoot("Downloads");
+ mBot.assertWindowTitle("Downloads");
}
public void testFilesList_LiveUpdate() throws Exception {
diff --git a/packages/ExternalStorageProvider/res/values-b+sr+Latn/strings.xml b/packages/ExternalStorageProvider/res/values-b+sr+Latn/strings.xml
new file mode 100644
index 0000000..b280d4f
--- /dev/null
+++ b/packages/ExternalStorageProvider/res/values-b+sr+Latn/strings.xml
@@ -0,0 +1,22 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="app_label" msgid="7123375275748530234">"Spoljna memorija"</string>
+ <string name="root_internal_storage" msgid="827844243068584127">"Interna memorija"</string>
+ <string name="root_home" msgid="7931555396767513359">"Početni"</string>
+</resources>
diff --git a/packages/FusedLocation/res/values-b+sr+Latn/strings.xml b/packages/FusedLocation/res/values-b+sr+Latn/strings.xml
new file mode 100644
index 0000000..0d2cccc
--- /dev/null
+++ b/packages/FusedLocation/res/values-b+sr+Latn/strings.xml
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="app_label" msgid="5379477904423203699">"Fused Location"</string>
+</resources>
diff --git a/packages/InputDevices/res/values-b+sr+Latn/strings.xml b/packages/InputDevices/res/values-b+sr+Latn/strings.xml
new file mode 100644
index 0000000..88a977f
--- /dev/null
+++ b/packages/InputDevices/res/values-b+sr+Latn/strings.xml
@@ -0,0 +1,44 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="app_label" msgid="8016145283189546017">"Ulazni uređaji"</string>
+ <string name="keyboard_layouts_label" msgid="6688773268302087545">"Android tastatura"</string>
+ <string name="keyboard_layout_english_uk_label" msgid="6664258463319999632">"engleska (UK)"</string>
+ <string name="keyboard_layout_english_us_label" msgid="8994890249649106291">"engleska (SAD)"</string>
+ <string name="keyboard_layout_english_us_intl" msgid="3705168594034233583">"engleska (SAD), međunarodni stil"</string>
+ <string name="keyboard_layout_english_us_colemak_label" msgid="4194969610343455380">"engleska (SAD), Colemak stil"</string>
+ <string name="keyboard_layout_english_us_dvorak_label" msgid="793528923171145202">"engleska (SAD), Dvorak stil"</string>
+ <string name="keyboard_layout_english_us_workman_label" msgid="2944541595262173111">"engleska (SAD), Workman stil"</string>
+ <string name="keyboard_layout_german_label" msgid="8451565865467909999">"nemačka"</string>
+ <string name="keyboard_layout_french_label" msgid="813450119589383723">"francuska"</string>
+ <string name="keyboard_layout_french_ca_label" msgid="365352601060604832">"francuska (Kanada)"</string>
+ <string name="keyboard_layout_russian_label" msgid="8724879775815042968">"ruska"</string>
+ <string name="keyboard_layout_russian_mac_label" msgid="3795866869038264796">"ruska, Mac stil"</string>
+ <string name="keyboard_layout_spanish_label" msgid="7091555148131908240">"španska"</string>
+ <string name="keyboard_layout_swiss_french_label" msgid="4659191025396371684">"švajcarsko francuska"</string>
+ <string name="keyboard_layout_swiss_german_label" msgid="2305520941993314258">"švajcarsko nemačka"</string>
+ <string name="keyboard_layout_belgian" msgid="2011984572838651558">"belgijska"</string>
+ <string name="keyboard_layout_bulgarian" msgid="8951224309972028398">"bugarska"</string>
+ <string name="keyboard_layout_italian" msgid="6497079660449781213">"italijanska"</string>
+ <string name="keyboard_layout_danish" msgid="8036432066627127851">"danska"</string>
+ <string name="keyboard_layout_norwegian" msgid="9090097917011040937">"norveška"</string>
+ <string name="keyboard_layout_swedish" msgid="732959109088479351">"švedska"</string>
+ <string name="keyboard_layout_finnish" msgid="5585659438924315466">"finska"</string>
+ <string name="keyboard_layout_croatian" msgid="4172229471079281138">"hrvatska"</string>
+ <string name="keyboard_layout_czech" msgid="1349256901452975343">"češka"</string>
+ <string name="keyboard_layout_estonian" msgid="8775830985185665274">"estonska"</string>
+ <string name="keyboard_layout_hungarian" msgid="4154963661406035109">"mađarska"</string>
+ <string name="keyboard_layout_icelandic" msgid="5836645650912489642">"islandska"</string>
+ <string name="keyboard_layout_brazilian" msgid="5117896443147781939">"brazilska"</string>
+ <string name="keyboard_layout_portuguese" msgid="2888198587329660305">"portugalska"</string>
+ <string name="keyboard_layout_slovak" msgid="2469379934672837296">"slovačka"</string>
+ <string name="keyboard_layout_slovenian" msgid="1735933028924982368">"slovenačka"</string>
+ <string name="keyboard_layout_turkish" msgid="7736163250907964898">"turska"</string>
+ <string name="keyboard_layout_ukrainian" msgid="8176637744389480417">"ukrajinska"</string>
+ <string name="keyboard_layout_arabic" msgid="5671970465174968712">"arapski"</string>
+ <string name="keyboard_layout_greek" msgid="7289253560162386040">"grčki"</string>
+ <string name="keyboard_layout_hebrew" msgid="7241473985890173812">"hebrejski"</string>
+ <string name="keyboard_layout_lithuanian" msgid="6943110873053106534">"litvanski"</string>
+ <string name="keyboard_layout_spanish_latin" msgid="5690539836069535697">"španski (Latinska Amerika)"</string>
+ <string name="keyboard_layout_latvian" msgid="4405417142306250595">"letonski"</string>
+</resources>
diff --git a/packages/Keyguard/res/values-b+sr+Latn/strings.xml b/packages/Keyguard/res/values-b+sr+Latn/strings.xml
new file mode 100644
index 0000000..a9a6b90
--- /dev/null
+++ b/packages/Keyguard/res/values-b+sr+Latn/strings.xml
@@ -0,0 +1,140 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/* //device/apps/common/assets/res/any/strings.xml
+**
+** Copyright 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.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="app_name" msgid="719438068451601849">"Zaštita tastera"</string>
+ <string name="keyguard_password_enter_pin_code" msgid="3037685796058495017">"Unesite PIN kôd"</string>
+ <string name="keyguard_password_enter_puk_code" msgid="3035856550289724338">"Unesite SIM PUK kôd i novi PIN kôd"</string>
+ <string name="keyguard_password_enter_puk_prompt" msgid="1801941051094974609">"SIM PUK kôd"</string>
+ <string name="keyguard_password_enter_pin_prompt" msgid="3201151840570492538">"Novi SIM PIN kôd"</string>
+ <string name="keyguard_password_entry_touch_hint" msgid="7858547464982981384"><font size="17">"Dodirnite da biste uneli lozinku"</font></string>
+ <string name="keyguard_password_enter_password_code" msgid="1054721668279049780">"Otkucajte lozinku da biste otključali"</string>
+ <string name="keyguard_password_enter_pin_password_code" msgid="6391755146112503443">"Unesite PIN za otključavanje"</string>
+ <string name="keyguard_password_wrong_pin_code" msgid="2422225591006134936">"PIN kôd je netačan."</string>
+ <string name="keyguard_charged" msgid="3272223906073492454">"Napunjeno"</string>
+ <string name="keyguard_plugged_in" msgid="9087497435553252863">"Punjenje"</string>
+ <string name="keyguard_plugged_in_charging_fast" msgid="6671162730167305479">"Brzo se puni"</string>
+ <string name="keyguard_plugged_in_charging_slowly" msgid="1964714661071163229">"Sporo se puni"</string>
+ <string name="keyguard_low_battery" msgid="8143808018719173859">"Povežite punjač."</string>
+ <string name="keyguard_instructions_when_pattern_disabled" msgid="1332288268600329841">"Pritisnite Meni da biste otključali."</string>
+ <string name="keyguard_network_locked_message" msgid="9169717779058037168">"Mreža je zaključana"</string>
+ <string name="keyguard_missing_sim_message_short" msgid="494980561304211931">"Nema SIM kartice"</string>
+ <string name="keyguard_missing_sim_message" product="tablet" msgid="1445849005909260039">"U tabletu nema SIM kartice."</string>
+ <string name="keyguard_missing_sim_message" product="default" msgid="3481110395508637643">"U telefonu nema SIM kartice."</string>
+ <string name="keyguard_missing_sim_instructions" msgid="5210891509995942250">"Umetnite SIM karticu."</string>
+ <string name="keyguard_missing_sim_instructions_long" msgid="5968985489463870358">"SIM kartica nedostaje ili ne može da se pročita. Umetnite SIM karticu."</string>
+ <string name="keyguard_permanent_disabled_sim_message_short" msgid="8340813989586622356">"SIM kartica je neupotrebljiva."</string>
+ <string name="keyguard_permanent_disabled_sim_instructions" msgid="5892940909699723544">"SIM kartica je trajno onemogućena.\n Obratite se dobavljaču usluge bežične mreže da biste dobili drugu SIM karticu."</string>
+ <string name="keyguard_sim_locked_message" msgid="6875773413306380902">"SIM kartica je zaključana."</string>
+ <string name="keyguard_sim_puk_locked_message" msgid="3747232467471801633">"SIM kartica je zaključana PUK kodom."</string>
+ <string name="keyguard_sim_unlock_progress_dialog_message" msgid="7975221805033614426">"Otključavanje SIM kartice…"</string>
+ <string name="keyguard_accessibility_pattern_unlock" msgid="1490840706075246612">"Otključavanje šablonom."</string>
+ <string name="keyguard_accessibility_pin_unlock" msgid="2469687111784035046">"Otključavanje PIN-om."</string>
+ <string name="keyguard_accessibility_password_unlock" msgid="7675777623912155089">"Otključavanje lozinkom."</string>
+ <string name="keyguard_accessibility_pattern_area" msgid="7679891324509597904">"Oblast šablona."</string>
+ <string name="keyguard_accessibility_slide_area" msgid="6736064494019979544">"Oblast prevlačenja."</string>
+ <string name="keyguard_accessibility_pin_area" msgid="7903959476607833485">"Oblast za PIN"</string>
+ <string name="keyguard_accessibility_sim_pin_area" msgid="3887780775111719336">"Oblast za PIN za SIM"</string>
+ <string name="keyguard_accessibility_sim_puk_area" msgid="1880823406954996207">"Oblast za PUK za SIM"</string>
+ <string name="keyguard_accessibility_next_alarm" msgid="7269583073750518672">"Sledeći alarm je podešen za <xliff:g id="ALARM">%1$s</xliff:g>"</string>
+ <string name="keyboardview_keycode_delete" msgid="3337914833206635744">"Izbriši"</string>
+ <string name="keyboardview_keycode_enter" msgid="2985864015076059467">"Enter"</string>
+ <string name="kg_forgot_pattern_button_text" msgid="8852021467868220608">"Zaboravljeni šablon"</string>
+ <string name="kg_wrong_pattern" msgid="1850806070801358830">"Pogrešan šablon"</string>
+ <string name="kg_wrong_password" msgid="2333281762128113157">"Pogrešna lozinka"</string>
+ <string name="kg_wrong_pin" msgid="1131306510833563801">"Pogrešan PIN"</string>
+ <string name="kg_too_many_failed_attempts_countdown" msgid="6358110221603297548">"Pokušajte ponovo za <xliff:g id="NUMBER">%d</xliff:g> sekunde(i)."</string>
+ <string name="kg_pattern_instructions" msgid="398978611683075868">"Nacrtajte šablon"</string>
+ <string name="kg_sim_pin_instructions" msgid="2319508550934557331">"Unesite PIN SIM kartice"</string>
+ <string name="kg_sim_pin_instructions_multi" msgid="7818515973197201434">"Unesite PIN za SIM „<xliff:g id="CARRIER">%1$s</xliff:g>“"</string>
+ <string name="kg_pin_instructions" msgid="2377242233495111557">"Unesite PIN"</string>
+ <string name="kg_password_instructions" msgid="5753646556186936819">"Unesite lozinku"</string>
+ <string name="kg_puk_enter_puk_hint" msgid="453227143861735537">"SIM kartica je sada onemogućena. Unesite PUK kôd da biste nastavili. Za detalje kontaktirajte operatera."</string>
+ <string name="kg_puk_enter_puk_hint_multi" msgid="363822494559783025">"SIM „<xliff:g id="CARRIER">%1$s</xliff:g>“ je sada onemogućen. Unesite PUK kôd da biste nastavili. Kontaktirajte operatera za detalje."</string>
+ <string name="kg_puk_enter_pin_hint" msgid="7871604527429602024">"Unesite željeni PIN kôd"</string>
+ <string name="kg_enter_confirm_pin_hint" msgid="325676184762529976">"Potvrdite željeni PIN kôd"</string>
+ <string name="kg_sim_unlock_progress_dialog_message" msgid="8950398016976865762">"Otključavanje SIM kartice…"</string>
+ <string name="kg_invalid_sim_pin_hint" msgid="8795159358110620001">"Unesite PIN koji ima od 4 do 8 brojeva."</string>
+ <string name="kg_invalid_sim_puk_hint" msgid="7553388325654369575">"PUK kôd treba da ima 8 ili više brojeva."</string>
+ <string name="kg_invalid_puk" msgid="3638289409676051243">"Ponovo unesite ispravni PUK kôd. Ponovljeni pokušaji će trajno onemogućiti SIM."</string>
+ <string name="kg_invalid_confirm_pin_hint" product="default" msgid="7003469261464593516">"PIN kodovi se ne podudaraju"</string>
+ <string name="kg_login_too_many_attempts" msgid="6486842094005698475">"Previše pokušaja unosa šablona"</string>
+ <string name="kg_too_many_failed_pin_attempts_dialog_message" msgid="8276745642049502550">"Uneli ste netačni PIN <xliff:g id="NUMBER_0">%d</xliff:g> puta. \n\nPokušajte ponovo za <xliff:g id="NUMBER_1">%d</xliff:g> sekunde(i)."</string>
+ <string name="kg_too_many_failed_password_attempts_dialog_message" msgid="7813713389422226531">"Uneli ste netačnu lozinku <xliff:g id="NUMBER_0">%d</xliff:g> puta. \n\nPokušajte ponovo za <xliff:g id="NUMBER_1">%d</xliff:g> sekunde(i)."</string>
+ <string name="kg_too_many_failed_pattern_attempts_dialog_message" msgid="74089475965050805">"Nacrtali ste šablon za otključavanje netačno <xliff:g id="NUMBER_0">%d</xliff:g> puta. \n\nPokušajte ponovo za <xliff:g id="NUMBER_1">%d</xliff:g> sekunde(i)."</string>
+ <string name="kg_failed_attempts_almost_at_wipe" product="tablet" msgid="8774056606869646621">"Pogrešno ste pokušali da otključate tablet <xliff:g id="NUMBER_0">%d</xliff:g> put(a). Imate još <xliff:g id="NUMBER_1">%d</xliff:g> pokušaj(a), nakon čega se tablet resetuje i svi podaci sa njega brišu."</string>
+ <string name="kg_failed_attempts_almost_at_wipe" product="default" msgid="1843331751334128428">"Pogrešno ste pokušali da otključate telefon <xliff:g id="NUMBER_0">%d</xliff:g> put(a). Imate još <xliff:g id="NUMBER_1">%d</xliff:g> pokušaj(a), nakon čega se telefon resetuje i svi podaci sa njega brišu."</string>
+ <string name="kg_failed_attempts_now_wiping" product="tablet" msgid="258925501999698032">"Pogrešno ste pokušali da otključate tablet <xliff:g id="NUMBER">%d</xliff:g> put(a). Tablet će biti resetovan i svi podaci sa njega će biti izbrisani."</string>
+ <string name="kg_failed_attempts_now_wiping" product="default" msgid="7154028908459817066">"Pogrešno ste pokušali da otključate telefon <xliff:g id="NUMBER">%d</xliff:g> put(a). Telefon će biti resetovan i svi podaci sa njega će biti izbrisani."</string>
+ <string name="kg_failed_attempts_almost_at_erase_user" product="tablet" msgid="6159955099372112688">"Pogrešno ste pokušali da otključate tablet <xliff:g id="NUMBER_0">%d</xliff:g> put(a). Imate još <xliff:g id="NUMBER_1">%d</xliff:g> pokušaj(a), nakon čega se ovaj korisnik uklanja i svi podaci korisnika brišu."</string>
+ <string name="kg_failed_attempts_almost_at_erase_user" product="default" msgid="6945823186629369880">"Pogrešno ste pokušali da otključate telefon <xliff:g id="NUMBER_0">%d</xliff:g> put(a). Imate još <xliff:g id="NUMBER_1">%d</xliff:g> pokušaj(a), nakon čega se ovaj korisnik uklanja i svi podaci korisnika brišu."</string>
+ <string name="kg_failed_attempts_now_erasing_user" product="tablet" msgid="3963486905355778734">"Pogrešno ste pokušali da otključate tablet <xliff:g id="NUMBER">%d</xliff:g> put(a). Ovaj korisnik će biti uklonjen i svi podaci korisnika će biti izbrisani."</string>
+ <string name="kg_failed_attempts_now_erasing_user" product="default" msgid="7729009752252111673">"Pogrešno ste pokušali da otključate telefon <xliff:g id="NUMBER">%d</xliff:g> put(a). Ovaj korisnik će biti uklonjen i svi podaci korisnika će biti izbrisani."</string>
+ <string name="kg_failed_attempts_almost_at_erase_profile" product="tablet" msgid="4621778507387853694">"Pogrešno ste pokušali da otključate tablet <xliff:g id="NUMBER_0">%d</xliff:g> put(a). Imate još <xliff:g id="NUMBER_1">%d</xliff:g> pokušaj(a), nakon čega se poslovni profil uklanja i svi podaci sa profila brišu."</string>
+ <string name="kg_failed_attempts_almost_at_erase_profile" product="default" msgid="6853071165802933545">"Pogrešno ste pokušali da otključate telefon <xliff:g id="NUMBER_0">%d</xliff:g> put(a). Imate još <xliff:g id="NUMBER_1">%d</xliff:g> pokušaj(a), nakon čega se poslovni profil uklanja i svi podaci sa profila brišu."</string>
+ <string name="kg_failed_attempts_now_erasing_profile" product="tablet" msgid="4686386497449912146">"Pogrešno ste pokušali da otključate tablet <xliff:g id="NUMBER">%d</xliff:g> put(a). Poslovni profil će biti uklonjen i svi podaci sa njega će biti izbrisani."</string>
+ <string name="kg_failed_attempts_now_erasing_profile" product="default" msgid="4951507352869831265">"Pogrešno ste pokušali da otključate telefon <xliff:g id="NUMBER">%d</xliff:g> put(a). Poslovni profil će biti uklonjen i svi podaci sa njega će biti izbrisani."</string>
+ <string name="kg_failed_attempts_almost_at_login" product="tablet" msgid="3253575572118914370">"Nacrtali ste šablon za otključavanje netačno <xliff:g id="NUMBER_0">%d</xliff:g> puta. Posle još <xliff:g id="NUMBER_1">%d</xliff:g> neuspešna(ih) pokušaja, od vas će biti zatraženo da otključate tablet pomoću naloga e-pošte.\n\nPokušajte ponovo za <xliff:g id="NUMBER_2">%d</xliff:g> sekunde(i)."</string>
+ <string name="kg_failed_attempts_almost_at_login" product="default" msgid="1437638152015574839">"Nacrtali ste šablon za otključavanje netačno <xliff:g id="NUMBER_0">%d</xliff:g> puta. Posle još <xliff:g id="NUMBER_1">%d</xliff:g> neuspešna(ih) pokušaja, od vas će biti zatraženo da otključate telefon pomoću naloga e-pošte.\n\nPokušajte ponovo za <xliff:g id="NUMBER_2">%d</xliff:g> sekunde(i)."</string>
+ <string name="kg_password_wrong_pin_code_pukked" msgid="30531039455764924">"Netačan SIM PIN kôd. Sada morate da kontaktirate mobilnog operatera da biste otključali uređaj."</string>
+ <plurals name="kg_password_wrong_pin_code" formatted="false" msgid="6721575017538162249">
+ <item quantity="one">Netačan SIM PIN kôd. Imate još <xliff:g id="NUMBER_1">%d</xliff:g> pokušaj.</item>
+ <item quantity="few">Netačan SIM PIN kôd. Imate još <xliff:g id="NUMBER_1">%d</xliff:g> pokušaja.</item>
+ <item quantity="other">Netačan SIM PIN kôd. Imate još <xliff:g id="NUMBER_1">%d</xliff:g> pokušaja.</item>
+ </plurals>
+ <string name="kg_password_wrong_puk_code_dead" msgid="7077536808291316208">"SIM kartica je neupotrebljiva. Kontaktirajte mobilnog operatera."</string>
+ <plurals name="kg_password_wrong_puk_code" formatted="false" msgid="7576227366999858780">
+ <item quantity="one">Netačan SIM PUK kôd. Imate još <xliff:g id="NUMBER_1">%d</xliff:g> pokušaj pre nego što SIM kartica postane trajno neupotrebljiva.</item>
+ <item quantity="few">Netačan SIM PUK kôd. Imate još <xliff:g id="NUMBER_1">%d</xliff:g> pokušaja pre nego što SIM kartica postane trajno neupotrebljiva.</item>
+ <item quantity="other">Netačan SIM PUK kôd. Imate još <xliff:g id="NUMBER_1">%d</xliff:g> pokušaja pre nego što SIM kartica postane trajno neupotrebljiva.</item>
+ </plurals>
+ <string name="kg_password_pin_failed" msgid="6268288093558031564">"Radnja sa SIM PIN kodom nije uspela!"</string>
+ <string name="kg_password_puk_failed" msgid="2838824369502455984">"Radnja sa SIM PUK kodom nije uspela!"</string>
+ <string name="kg_pin_accepted" msgid="1448241673570020097">"Kôd je prihvaćen!"</string>
+ <string name="keyguard_carrier_default" msgid="8700650403054042153">"Oflajn ste."</string>
+ <string name="accessibility_ime_switch_button" msgid="5032926134740456424">"Dugme Promeni metod unosa."</string>
+ <string name="airplane_mode" msgid="3122107900897202805">"Režim rada u avionu"</string>
+ <string name="kg_prompt_reason_restart_pattern" msgid="489430505491862444">"Šablon je obavezan kada ponovo pokrećete uređaj."</string>
+ <string name="kg_prompt_reason_restart_pin" msgid="994878216570694974">"PIN je obavezan kada ponovo pokrećete uređaj."</string>
+ <string name="kg_prompt_reason_restart_password" msgid="2375742919528461664">"Lozinka je obavezna kada ponovo pokrećete uređaj."</string>
+ <string name="kg_prompt_reason_timeout_pattern" msgid="8930047492617900785">"Potreban je šablon radi dodatne bezbednosti."</string>
+ <string name="kg_prompt_reason_timeout_pin" msgid="7470468607947726377">"Potreban je PIN radi dodatne bezbednosti."</string>
+ <string name="kg_prompt_reason_timeout_password" msgid="1177412542773936957">"Potrebna je lozinka radi dodatne bezbednosti."</string>
+ <string name="kg_prompt_reason_switch_profiles_pattern" msgid="3802056699323773969">"Šablon je obavezan kada prelazite sa jednog profila na drugi."</string>
+ <string name="kg_prompt_reason_switch_profiles_pin" msgid="8108020184731052246">"PIN je obavezan kada prelazite sa jednog profila na drugi."</string>
+ <string name="kg_prompt_reason_switch_profiles_password" msgid="6755997057852042672">"Lozinka je obavezna kada prelazite sa jednog profila na drugi."</string>
+ <plurals name="kg_prompt_reason_time_pattern" formatted="false" msgid="2697444392228541853">
+ <item quantity="one">Niste otključali uređaj <xliff:g id="NUMBER_1">%d</xliff:g> sat. Potvrdite šablon.</item>
+ <item quantity="few">Niste otključali uređaj <xliff:g id="NUMBER_1">%d</xliff:g> sata. Potvrdite šablon.</item>
+ <item quantity="other">Niste otključali uređaj <xliff:g id="NUMBER_1">%d</xliff:g> sati. Potvrdite šablon.</item>
+ </plurals>
+ <plurals name="kg_prompt_reason_time_pin" formatted="false" msgid="2118758475374354849">
+ <item quantity="one">Niste otključali uređaj <xliff:g id="NUMBER_1">%d</xliff:g> sat. Potvrdite PIN.</item>
+ <item quantity="few">Niste otključali uređaj <xliff:g id="NUMBER_1">%d</xliff:g> sata. Potvrdite PIN.</item>
+ <item quantity="other">Niste otključali uređaj <xliff:g id="NUMBER_1">%d</xliff:g> sati. Potvrdite PIN.</item>
+ </plurals>
+ <plurals name="kg_prompt_reason_time_password" formatted="false" msgid="5132693663364913675">
+ <item quantity="one">Niste otključali uređaj <xliff:g id="NUMBER_1">%d</xliff:g> sat. Potvrdite lozinku.</item>
+ <item quantity="few">Niste otključali uređaj <xliff:g id="NUMBER_1">%d</xliff:g> sata. Potvrdite lozinku.</item>
+ <item quantity="other">Niste otključali uređaj <xliff:g id="NUMBER_1">%d</xliff:g> sati. Potvrdite lozinku.</item>
+ </plurals>
+ <string name="fingerprint_not_recognized" msgid="2690661881608146617">"Nije prepoznat"</string>
+</resources>
diff --git a/packages/MtpDocumentsProvider/src/com/android/mtp/DocumentLoader.java b/packages/MtpDocumentsProvider/src/com/android/mtp/DocumentLoader.java
index 6fa0df2..1d72647 100644
--- a/packages/MtpDocumentsProvider/src/com/android/mtp/DocumentLoader.java
+++ b/packages/MtpDocumentsProvider/src/com/android/mtp/DocumentLoader.java
@@ -255,7 +255,7 @@
return;
}
if (mNumLoaded == 0) {
- mDatabase.getMapper().startAddingChildDocuments(mIdentifier.mDocumentId);
+ mDatabase.getMapper().startAddingDocuments(mIdentifier.mDocumentId);
}
try {
mDatabase.getMapper().putChildDocuments(
@@ -266,7 +266,7 @@
mNumLoaded = 0;
}
if (getState() != STATE_LOADING) {
- mDatabase.getMapper().stopAddingChildDocuments(mIdentifier.mDocumentId);
+ mDatabase.getMapper().stopAddingDocuments(mIdentifier.mDocumentId);
}
}
@@ -275,7 +275,7 @@
mError = message;
mNumLoaded = 0;
if (lastState == STATE_LOADING) {
- mDatabase.getMapper().stopAddingChildDocuments(mIdentifier.mDocumentId);
+ mDatabase.getMapper().stopAddingDocuments(mIdentifier.mDocumentId);
}
}
diff --git a/packages/MtpDocumentsProvider/src/com/android/mtp/Mapper.java b/packages/MtpDocumentsProvider/src/com/android/mtp/Mapper.java
index 0d9d60c..cb49535e 100644
--- a/packages/MtpDocumentsProvider/src/com/android/mtp/Mapper.java
+++ b/packages/MtpDocumentsProvider/src/com/android/mtp/Mapper.java
@@ -18,6 +18,7 @@
import static com.android.mtp.MtpDatabaseConstants.*;
+import android.annotation.Nullable;
import android.content.ContentValues;
import android.content.res.Resources;
import android.database.Cursor;
@@ -36,17 +37,18 @@
import static com.android.mtp.MtpDatabase.strings;
-
/**
* Mapping operations for MtpDatabase.
* Also see the comments of {@link MtpDatabase}.
*/
class Mapper {
+ private static final String[] EMPTY_ARGS = new String[0];
private final MtpDatabase mDatabase;
/**
- * Mapping mode for roots/documents where we start adding child documents.
+ * Mapping mode for a parent. The key is document ID of parent, or null for root documents.
* Methods operate the state needs to be synchronized.
+ * TODO: Replace this with unboxing int map.
*/
private final Map<String, Integer> mMappingMode = new HashMap<>();
@@ -55,47 +57,46 @@
}
/**
- * Invokes {@link #startAddingDocuments} for root documents.
- * @param deviceId Device ID.
+ * Puts device information to database.
+ * @return If device is added to the database.
*/
- synchronized void startAddingRootDocuments(int deviceId) {
- final String mappingStateKey = getRootDocumentsMappingStateKey(deviceId);
- Preconditions.checkState(!mMappingMode.containsKey(mappingStateKey));
- mMappingMode.put(
- mappingStateKey,
- startAddingDocuments(
- SELECTION_ROOT_DOCUMENTS, Integer.toString(deviceId)));
- }
-
- /**
- * Invokes {@link #startAddingDocuments} for child of specific documents.
- * @param parentDocumentId Document ID for parent document.
- */
- @VisibleForTesting
- synchronized void startAddingChildDocuments(String parentDocumentId) {
- final String mappingStateKey = getChildDocumentsMappingStateKey(parentDocumentId);
- Preconditions.checkState(!mMappingMode.containsKey(mappingStateKey));
- mMappingMode.put(
- mappingStateKey,
- startAddingDocuments(SELECTION_CHILD_DOCUMENTS, parentDocumentId));
+ synchronized boolean putDeviceDocument(MtpDeviceRecord device) {
+ final SQLiteDatabase database = mDatabase.getSQLiteDatabase();
+ Preconditions.checkState(mMappingMode.containsKey(/* no parent for root */ null));
+ database.beginTransaction();
+ try {
+ final ContentValues[] valuesList = new ContentValues[1];
+ valuesList[0] = new ContentValues();
+ MtpDatabase.getDeviceDocumentValues(valuesList[0], device);
+ final boolean changed = putDocuments(
+ valuesList,
+ COLUMN_PARENT_DOCUMENT_ID + " IS NULL",
+ EMPTY_ARGS,
+ /* heuristic */ false,
+ COLUMN_DEVICE_ID);
+ database.setTransactionSuccessful();
+ return changed;
+ } finally {
+ database.endTransaction();
+ }
}
/**
* Puts root information to database.
- * @param deviceId Device ID
+ * @param parentDocumentId Document ID of device document.
* @param resources Resources required to localize root name.
* @param roots List of root information.
* @return If roots are added or removed from the database.
*/
- synchronized boolean putRootDocuments(int deviceId, Resources resources, MtpRoot[] roots) {
+ synchronized boolean putRootDocuments(
+ String parentDocumentId, Resources resources, MtpRoot[] roots) {
final SQLiteDatabase database = mDatabase.getSQLiteDatabase();
database.beginTransaction();
try {
final boolean heuristic;
final String mapColumn;
- final String key = getRootDocumentsMappingStateKey(deviceId);
- Preconditions.checkState(mMappingMode.containsKey(key));
- switch (mMappingMode.get(key)) {
+ Preconditions.checkState(mMappingMode.containsKey(parentDocumentId));
+ switch (mMappingMode.get(parentDocumentId)) {
case MAP_BY_MTP_IDENTIFIER:
heuristic = false;
mapColumn = COLUMN_STORAGE_ID;
@@ -109,16 +110,14 @@
}
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();
- MtpDatabase.getRootDocumentValues(valuesList[i], resources, roots[i]);
+ MtpDatabase.getStorageDocumentValues(
+ valuesList[i], resources, parentDocumentId, roots[i]);
}
final boolean changed = putDocuments(
valuesList,
- SELECTION_ROOT_DOCUMENTS,
- Integer.toString(deviceId),
+ COLUMN_PARENT_DOCUMENT_ID + "=?",
+ strings(parentDocumentId),
heuristic,
mapColumn);
final ContentValues values = new ContentValues();
@@ -156,9 +155,8 @@
synchronized void putChildDocuments(int deviceId, String parentId, MtpObjectInfo[] documents) {
final boolean heuristic;
final String mapColumn;
- final String key = getChildDocumentsMappingStateKey(parentId);
- Preconditions.checkState(mMappingMode.containsKey(key));
- switch (mMappingMode.get(key)) {
+ Preconditions.checkState(mMappingMode.containsKey(parentId));
+ switch (mMappingMode.get(parentId)) {
case MAP_BY_MTP_IDENTIFIER:
heuristic = false;
mapColumn = COLUMN_OBJECT_HANDLE;
@@ -173,63 +171,15 @@
final ContentValues[] valuesList = new ContentValues[documents.length];
for (int i = 0; i < documents.length; i++) {
valuesList[i] = new ContentValues();
- MtpDatabase.getChildDocumentValues(
+ MtpDatabase.getObjectDocumentValues(
valuesList[i], deviceId, parentId, documents[i]);
}
putDocuments(
- valuesList, SELECTION_CHILD_DOCUMENTS, parentId, heuristic, mapColumn);
- }
-
- /**
- * Stops adding root documents.
- * @param deviceId Device ID.
- * @return True if new rows are added/removed.
- */
- synchronized boolean stopAddingRootDocuments(int deviceId) {
- final String key = getRootDocumentsMappingStateKey(deviceId);
- Preconditions.checkState(mMappingMode.containsKey(key));
- switch (mMappingMode.get(key)) {
- case MAP_BY_MTP_IDENTIFIER:
- mMappingMode.remove(key);
- return stopAddingDocuments(
- SELECTION_ROOT_DOCUMENTS,
- Integer.toString(deviceId),
- COLUMN_STORAGE_ID);
- case MAP_BY_NAME:
- mMappingMode.remove(key);
- return stopAddingDocuments(
- SELECTION_ROOT_DOCUMENTS,
- Integer.toString(deviceId),
- Document.COLUMN_DISPLAY_NAME);
- default:
- throw new Error("Unexpected mapping state.");
- }
- }
-
- /**
- * Stops adding documents under the parent.
- * @param parentId Document ID of the parent.
- */
- synchronized void stopAddingChildDocuments(String parentId) {
- final String key = getChildDocumentsMappingStateKey(parentId);
- Preconditions.checkState(mMappingMode.containsKey(key));
- switch (mMappingMode.get(key)) {
- case MAP_BY_MTP_IDENTIFIER:
- stopAddingDocuments(
- SELECTION_CHILD_DOCUMENTS,
- parentId,
- COLUMN_OBJECT_HANDLE);
- break;
- case MAP_BY_NAME:
- stopAddingDocuments(
- SELECTION_CHILD_DOCUMENTS,
- parentId,
- Document.COLUMN_DISPLAY_NAME);
- break;
- default:
- throw new Error("Unexpected mapping state.");
- }
- mMappingMode.remove(key);
+ valuesList,
+ COLUMN_PARENT_DOCUMENT_ID + "=?",
+ strings(parentId),
+ heuristic,
+ mapColumn);
}
@VisibleForTesting
@@ -257,31 +207,42 @@
* identifier or not. If all the documents have MTP identifier, it uses the identifier to find
* a corresponding existing row. Otherwise it does heuristic.
*
- * @param selection Query matches valid documents.
- * @param arg Argument for selection.
- * @return Mapping mode.
+ * @param parentDocumentId Parent document ID or NULL for root documents.
*/
- private int startAddingDocuments(String selection, String arg) {
+ void startAddingDocuments(@Nullable String parentDocumentId) {
+ Preconditions.checkState(!mMappingMode.containsKey(parentDocumentId));
+ final String selection;
+ final String[] args;
+ if (parentDocumentId != null) {
+ selection = COLUMN_PARENT_DOCUMENT_ID + " = ?";
+ args = strings(parentDocumentId);
+ } else {
+ selection = COLUMN_PARENT_DOCUMENT_ID + " IS NULL";
+ args = EMPTY_ARGS;
+ }
+
final SQLiteDatabase database = mDatabase.getSQLiteDatabase();
database.beginTransaction();
try {
// Delete all pending rows.
mDatabase.deleteDocumentsAndRootsRecursively(
- selection + " AND " + COLUMN_ROW_STATE + "=?", strings(arg, ROW_STATE_PENDING));
+ selection + " AND " + COLUMN_ROW_STATE + "=?",
+ DatabaseUtils.appendSelectionArgs(args, strings(ROW_STATE_PENDING)));
// Set all documents as invalidated.
final ContentValues values = new ContentValues();
values.put(COLUMN_ROW_STATE, ROW_STATE_INVALIDATED);
- database.update(TABLE_DOCUMENTS, values, selection, new String[] { arg });
+ database.update(TABLE_DOCUMENTS, values, selection, args);
// If we have rows that does not have MTP identifier, do heuristic mapping by name.
final boolean useNameForResolving = DatabaseUtils.queryNumEntries(
database,
TABLE_DOCUMENTS,
selection + " AND " + COLUMN_STORAGE_ID + " IS NULL",
- new String[] { arg }) > 0;
+ args) > 0;
database.setTransactionSuccessful();
- return useNameForResolving ? MAP_BY_NAME : MAP_BY_MTP_IDENTIFIER;
+ mMappingMode.put(
+ parentDocumentId, useNameForResolving ? MAP_BY_NAME : MAP_BY_MTP_IDENTIFIER);
} finally {
database.endTransaction();
}
@@ -292,19 +253,19 @@
* If the mapping mode is not heuristic, it just adds the rows to the database or updates the
* existing rows with the new values. If the mapping mode is heuristic, it adds some new rows as
* 'pending' state when that rows may be corresponding to existing 'invalidated' rows. Then
- * {@link #stopAddingDocuments(String, String, String)} turns the pending rows into 'valid'
+ * {@link #stopAddingDocuments(String)} turns the pending rows into 'valid'
* rows. If the methods adds rows to database, it updates valueList with correct document ID.
*
* @param valuesList Values for documents to be stored in the database.
* @param selection SQL where closure to select rows that shares the same parent.
- * @param arg Argument for selection SQL.
+ * @param args Argument for selection SQL.
* @param heuristic Whether the mapping mode is heuristic.
* @return Whether the method adds new rows.
*/
private boolean putDocuments(
ContentValues[] valuesList,
String selection,
- String arg,
+ String[] args,
boolean heuristic,
String mappingKey) {
final SQLiteDatabase database = mDatabase.getSQLiteDatabase();
@@ -318,7 +279,9 @@
selection + " AND " +
COLUMN_ROW_STATE + "=? AND " +
mappingKey + "=?",
- strings(arg, ROW_STATE_INVALIDATED, values.getAsString(mappingKey)),
+ DatabaseUtils.appendSelectionArgs(
+ args,
+ strings(ROW_STATE_INVALIDATED, values.getAsString(mappingKey))),
null,
null,
null,
@@ -362,12 +325,32 @@
* Maps 'pending' document and 'invalidated' document that shares the same column of groupKey.
* If the database does not find corresponding 'invalidated' document, it just removes
* 'invalidated' document from the database.
- * @param selection Query to select rows for resolving.
- * @param arg Argument for selection SQL.
- * @param groupKey Column name used to find corresponding rows.
+ * @param parentId Parent document ID or null for root documents.
* @return Whether the methods adds or removed visible rows.
*/
- private boolean stopAddingDocuments(String selection, String arg, String groupKey) {
+ boolean stopAddingDocuments(@Nullable String parentId) {
+ Preconditions.checkState(mMappingMode.containsKey(parentId));
+ final String selection;
+ final String[] args;
+ if (parentId != null) {
+ selection = COLUMN_PARENT_DOCUMENT_ID + "=?";
+ args = strings(parentId);
+ } else {
+ selection = COLUMN_PARENT_DOCUMENT_ID + " IS NULL";
+ args = EMPTY_ARGS;
+ }
+ final String groupKey;
+ switch (mMappingMode.get(parentId)) {
+ case MAP_BY_MTP_IDENTIFIER:
+ groupKey = parentId != null ? COLUMN_OBJECT_HANDLE : COLUMN_STORAGE_ID;
+ break;
+ case MAP_BY_NAME:
+ groupKey = Document.COLUMN_DISPLAY_NAME;
+ break;
+ default:
+ throw new Error("Unexpected mapping state.");
+ }
+ mMappingMode.remove(parentId);
final SQLiteDatabase database = mDatabase.getSQLiteDatabase();
database.beginTransaction();
try {
@@ -390,7 +373,7 @@
"group_concat(" + pendingIdQuery + ")"
},
selection,
- strings(arg),
+ args,
groupKey,
"count(" + invalidatedIdQuery + ") = 1 AND count(" + pendingIdQuery + ") = 1",
null);
@@ -439,7 +422,7 @@
// Delete all invalidated rows that cannot be mapped.
if (mDatabase.deleteDocumentsAndRootsRecursively(
COLUMN_ROW_STATE + " = ? AND " + selection,
- strings(ROW_STATE_INVALIDATED, arg))) {
+ DatabaseUtils.appendSelectionArgs(strings(ROW_STATE_INVALIDATED), args))) {
changed = true;
}
@@ -452,7 +435,7 @@
TABLE_DOCUMENTS,
values,
COLUMN_ROW_STATE + " = ? AND " + selection,
- strings(ROW_STATE_PENDING, arg)) != 0) {
+ DatabaseUtils.appendSelectionArgs(strings(ROW_STATE_PENDING), args)) != 0) {
changed = true;
}
database.setTransactionSuccessful();
@@ -473,12 +456,15 @@
final SQLiteDatabase database = mDatabase.getSQLiteDatabase();
values.clear();
final Cursor cursor = database.query(table, null, selection, args, null, null, null, "1");
- if (cursor.getCount() == 0) {
- return;
+ try {
+ if (cursor.getCount() == 0) {
+ return;
+ }
+ cursor.moveToNext();
+ DatabaseUtils.cursorRowToContentValues(cursor, values);
+ } finally {
+ cursor.close();
}
- cursor.moveToNext();
- DatabaseUtils.cursorRowToContentValues(cursor, values);
- cursor.close();
}
/**
@@ -494,20 +480,4 @@
return "CASE WHEN " + COLUMN_ROW_STATE + " = " + Integer.toString(state) +
" THEN " + a + " ELSE NULL END";
}
-
- /**
- * @param deviceId Device ID.
- * @return Key for {@link #mMappingMode}.
- */
- private static String getRootDocumentsMappingStateKey(int deviceId) {
- return "RootDocuments/" + deviceId;
- }
-
- /**
- * @param parentDocumentId Document ID for the parent document.
- * @return Key for {@link #mMappingMode}.
- */
- private static String getChildDocumentsMappingStateKey(String parentDocumentId) {
- return "ChildDocuments/" + parentDocumentId;
- }
}
diff --git a/packages/MtpDocumentsProvider/src/com/android/mtp/MtpDatabase.java b/packages/MtpDocumentsProvider/src/com/android/mtp/MtpDatabase.java
index 0e91fdd..10941eb 100644
--- a/packages/MtpDocumentsProvider/src/com/android/mtp/MtpDatabase.java
+++ b/packages/MtpDocumentsProvider/src/com/android/mtp/MtpDatabase.java
@@ -18,6 +18,7 @@
import static com.android.mtp.MtpDatabaseConstants.*;
+import android.annotation.Nullable;
import android.content.ContentValues;
import android.content.Context;
import android.content.res.Resources;
@@ -35,9 +36,7 @@
import com.android.internal.annotations.VisibleForTesting;
import java.io.FileNotFoundException;
-import java.util.HashSet;
import java.util.Objects;
-import java.util.Set;
/**
* Database for MTP objects.
@@ -53,13 +52,13 @@
* by comparing the directory structure and object name.
*
* To start putting documents into the database, the client needs to call
- * {@link Mapper#startAddingChildDocuments(String)} with the parent document ID. Also it
- * needs to call {@link Mapper#stopAddingChildDocuments(String)} after putting all child
+ * {@link Mapper#startAddingDocuments(String)} with the parent document ID. Also it
+ * needs to call {@link Mapper#stopAddingDocuments(String)} after putting all child
* documents to the database. (All explanations are same for root documents)
*
- * database.getMapper().startAddingChildDocuments();
+ * database.getMapper().startAddingDocuments();
* database.getMapper().putChildDocuments();
- * database.getMapper().stopAddingChildDocuments();
+ * database.getMapper().stopAddingDocuments();
*
* To update the existing documents, the client code can repeat to call the three methods again.
* The newly added rows update corresponding existing rows that have same MTP identifier like
@@ -107,11 +106,14 @@
* @return Database cursor.
*/
Cursor queryRoots(String[] columnNames) {
- return mDatabase.query(
- VIEW_ROOTS,
+ final SQLiteQueryBuilder builder = new SQLiteQueryBuilder();
+ builder.setTables(JOIN_ROOTS);
+ builder.setProjectionMap(COLUMN_MAP_ROOTS);
+ return builder.query(
+ mDatabase,
columnNames,
- COLUMN_ROW_STATE + " IN (?, ?)",
- strings(ROW_STATE_VALID, ROW_STATE_INVALIDATED),
+ COLUMN_ROW_STATE + " IN (?, ?) AND " + COLUMN_DOCUMENT_TYPE + " = ?",
+ strings(ROW_STATE_VALID, ROW_STATE_INVALIDATED, DOCUMENT_TYPE_STORAGE),
null,
null,
null);
@@ -128,8 +130,8 @@
return mDatabase.query(
TABLE_DOCUMENTS,
columnNames,
- COLUMN_ROW_STATE + " IN (?, ?)",
- strings(ROW_STATE_VALID, ROW_STATE_INVALIDATED),
+ COLUMN_ROW_STATE + " IN (?, ?) AND " + COLUMN_DOCUMENT_TYPE + "=?",
+ strings(ROW_STATE_VALID, ROW_STATE_INVALIDATED, DOCUMENT_TYPE_STORAGE),
null,
null,
null);
@@ -179,6 +181,27 @@
deleteDocumentsAndRoots(COLUMN_DEVICE_ID + "=?", strings(deviceId));
}
+ @Nullable String getDocumentIdForDevice(int deviceId) {
+ final Cursor cursor = mDatabase.query(
+ TABLE_DOCUMENTS,
+ strings(Document.COLUMN_DOCUMENT_ID),
+ COLUMN_DOCUMENT_TYPE + " = ? AND " + COLUMN_DEVICE_ID + " = ?",
+ strings(DOCUMENT_TYPE_DEVICE, deviceId),
+ null,
+ null,
+ null,
+ "1");
+ try {
+ if (cursor.moveToNext()) {
+ return cursor.getString(0);
+ } else {
+ return null;
+ }
+ } finally {
+ cursor.close();
+ }
+ }
+
/**
* Obtains parent document ID.
* @param documentId
@@ -216,7 +239,7 @@
*/
String putNewDocument(int deviceId, String parentDocumentId, MtpObjectInfo info) {
final ContentValues values = new ContentValues();
- getChildDocumentValues(values, deviceId, parentDocumentId, info);
+ getObjectDocumentValues(values, deviceId, parentDocumentId, info);
mDatabase.beginTransaction();
try {
final long id = mDatabase.insert(TABLE_DOCUMENTS, null, values);
@@ -307,32 +330,6 @@
}
}
- /**
- * Returns the set of device ID stored in the database.
- */
- int[] getDeviceIds() {
- final Cursor cursor = mDatabase.query(
- true,
- TABLE_DOCUMENTS,
- strings(COLUMN_DEVICE_ID),
- null,
- null,
- null,
- null,
- null,
- null);
- try {
- final int[] ids = new int[cursor.getCount()];
- for (int i = 0; i < ids.length; i++) {
- cursor.moveToNext();
- ids[i] = cursor.getInt(0);
- }
- return ids;
- } finally {
- cursor.close();
- }
- }
-
private boolean deleteDocumentsAndRoots(String selection, String[] args) {
mDatabase.beginTransaction();
try {
@@ -370,7 +367,6 @@
public void onCreate(SQLiteDatabase db) {
db.execSQL(QUERY_CREATE_DOCUMENTS);
db.execSQL(QUERY_CREATE_ROOT_EXTRA);
- db.execSQL(QUERY_CREATE_VIEW_ROOTS);
}
@Override
@@ -384,18 +380,40 @@
context.deleteDatabase(DATABASE_NAME);
}
+ static void getDeviceDocumentValues(ContentValues values, MtpDeviceRecord device) {
+ values.clear();
+ values.put(COLUMN_DEVICE_ID, device.deviceId);
+ values.putNull(COLUMN_STORAGE_ID);
+ values.putNull(COLUMN_OBJECT_HANDLE);
+ values.putNull(COLUMN_PARENT_DOCUMENT_ID);
+ values.put(COLUMN_ROW_STATE, ROW_STATE_VALID);
+ values.put(COLUMN_DOCUMENT_TYPE, DOCUMENT_TYPE_DEVICE);
+ values.put(Document.COLUMN_MIME_TYPE, Document.MIME_TYPE_DIR);
+ values.put(Document.COLUMN_DISPLAY_NAME, device.name);
+ values.putNull(Document.COLUMN_SUMMARY);
+ values.putNull(Document.COLUMN_LAST_MODIFIED);
+ values.put(Document.COLUMN_ICON, R.drawable.ic_root_mtp);
+ values.put(Document.COLUMN_FLAGS, 0);
+ long size = 0;
+ for (final MtpRoot root : device.roots) {
+ size += root.mMaxCapacity - root.mFreeSpace;
+ }
+ values.put(Document.COLUMN_SIZE, size);
+ }
+
/**
* 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}.
*/
- static void getRootDocumentValues(ContentValues values, Resources resources, MtpRoot root) {
+ static void getStorageDocumentValues(
+ ContentValues values, Resources resources, String parentDocumentId, 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_PARENT_DOCUMENT_ID, parentDocumentId);
values.put(COLUMN_ROW_STATE, ROW_STATE_VALID);
values.put(COLUMN_DOCUMENT_TYPE, DOCUMENT_TYPE_STORAGE);
values.put(Document.COLUMN_MIME_TYPE, Document.MIME_TYPE_DIR);
@@ -415,7 +433,7 @@
* @param parentId Parent document ID of the object.
* @param info MTP object info.
*/
- static void getChildDocumentValues(
+ static void getObjectDocumentValues(
ContentValues values, int deviceId, String parentId, MtpObjectInfo info) {
values.clear();
final String mimeType = info.getFormat() == MtpConstants.FORMAT_ASSOCIATION ?
diff --git a/packages/MtpDocumentsProvider/src/com/android/mtp/MtpDatabaseConstants.java b/packages/MtpDocumentsProvider/src/com/android/mtp/MtpDatabaseConstants.java
index 72bd6ee..a233589 100644
--- a/packages/MtpDocumentsProvider/src/com/android/mtp/MtpDatabaseConstants.java
+++ b/packages/MtpDocumentsProvider/src/com/android/mtp/MtpDatabaseConstants.java
@@ -16,9 +16,13 @@
package com.android.mtp;
+import android.database.sqlite.SQLiteQueryBuilder;
import android.provider.DocumentsContract.Document;
import android.provider.DocumentsContract.Root;
+import java.util.HashMap;
+import java.util.Map;
+
/**
* Class containing MtpDatabase constants.
*/
@@ -41,9 +45,13 @@
static final String TABLE_ROOT_EXTRA = "RootExtra";
/**
- * View to join Documents and RootExtra tables to provide roots information.
+ * 'FROM' closure of joining TABLE_DOCUMENTS and TABLE_ROOT_EXTRA.
*/
- static final String VIEW_ROOTS = "Roots";
+ static final String JOIN_ROOTS = createJoinFromClosure(
+ TABLE_DOCUMENTS,
+ TABLE_ROOT_EXTRA,
+ Document.COLUMN_DOCUMENT_ID,
+ Root.COLUMN_ROOT_ID);
static final String COLUMN_DEVICE_ID = "device_id";
static final String COLUMN_STORAGE_ID = "storage_id";
@@ -86,8 +94,6 @@
/**
* Document that represents a MTP device.
- * Note we have "device" document only when the device has multiple storage volumes. Otherwise
- * we regard the single "storage" document as root.
*/
static final int DOCUMENT_TYPE_DEVICE = 0;
@@ -103,9 +109,6 @@
static final String SELECTION_DOCUMENT_ID = Document.COLUMN_DOCUMENT_ID + " = ?";
static final String SELECTION_ROOT_ID = Root.COLUMN_ROOT_ID + " = ?";
- static final String SELECTION_ROOT_DOCUMENTS =
- COLUMN_DEVICE_ID + " = ? AND " + COLUMN_PARENT_DOCUMENT_ID + " IS NULL";
- static final String SELECTION_CHILD_DOCUMENTS = COLUMN_PARENT_DOCUMENT_ID + " = ?";
static final String QUERY_CREATE_DOCUMENTS =
"CREATE TABLE " + TABLE_DOCUMENTS + " (" +
@@ -134,30 +137,31 @@
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.
+ * Map for columns names to provide DocumentContract.Root compatible columns.
+ * @see SQLiteQueryBuilder#setProjectionMap(Map)
*/
- 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;
+ static final Map<String, String> COLUMN_MAP_ROOTS;
+ static {
+ COLUMN_MAP_ROOTS = new HashMap<>();
+ COLUMN_MAP_ROOTS.put(Root.COLUMN_ROOT_ID, TABLE_ROOT_EXTRA + "." + Root.COLUMN_ROOT_ID);
+ COLUMN_MAP_ROOTS.put(Root.COLUMN_FLAGS, TABLE_ROOT_EXTRA + "." + Root.COLUMN_FLAGS);
+ COLUMN_MAP_ROOTS.put(Root.COLUMN_ICON, TABLE_DOCUMENTS + "." + Document.COLUMN_ICON);
+ COLUMN_MAP_ROOTS.put(
+ Root.COLUMN_TITLE, TABLE_DOCUMENTS + "." + Document.COLUMN_DISPLAY_NAME);
+ COLUMN_MAP_ROOTS.put(Root.COLUMN_SUMMARY, TABLE_DOCUMENTS + "." + Document.COLUMN_SUMMARY);
+ COLUMN_MAP_ROOTS.put(
+ Root.COLUMN_DOCUMENT_ID, TABLE_DOCUMENTS + "." + Document.COLUMN_DOCUMENT_ID);
+ COLUMN_MAP_ROOTS.put(
+ Root.COLUMN_AVAILABLE_BYTES, TABLE_ROOT_EXTRA + "." + Root.COLUMN_AVAILABLE_BYTES);
+ COLUMN_MAP_ROOTS.put(
+ Root.COLUMN_CAPACITY_BYTES, TABLE_ROOT_EXTRA + "." + Root.COLUMN_CAPACITY_BYTES);
+ COLUMN_MAP_ROOTS.put(
+ Root.COLUMN_MIME_TYPES, TABLE_ROOT_EXTRA + "." + Root.COLUMN_MIME_TYPES);
+ }
+
+ private static String createJoinFromClosure(
+ String table1, String table2, String column1, String column2) {
+ return table1 + " INNER JOIN " + table2 +
+ " ON " + table1 + "." + column1 + " = " + table2 + "." + column2;
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/events/ui/ResizeTaskEvent.java b/packages/MtpDocumentsProvider/src/com/android/mtp/MtpDeviceRecord.java
similarity index 62%
rename from packages/SystemUI/src/com/android/systemui/recents/events/ui/ResizeTaskEvent.java
rename to packages/MtpDocumentsProvider/src/com/android/mtp/MtpDeviceRecord.java
index e0d83fd..71df5c1 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/events/ui/ResizeTaskEvent.java
+++ b/packages/MtpDocumentsProvider/src/com/android/mtp/MtpDeviceRecord.java
@@ -14,19 +14,18 @@
* limitations under the License.
*/
-package com.android.systemui.recents.events.ui;
+package com.android.mtp;
-import com.android.systemui.recents.events.EventBus;
-import com.android.systemui.recents.model.Task;
+class MtpDeviceRecord {
+ public final int deviceId;
+ public final String name;
+ public final boolean opened;
+ public final MtpRoot[] roots;
-/**
- * This is sent when a {@link Task} is resized.
- */
-public class ResizeTaskEvent extends EventBus.Event {
-
- public final Task task;
-
- public ResizeTaskEvent(Task task) {
- this.task = task;
+ MtpDeviceRecord(int deviceId, String name, boolean opened, MtpRoot[] roots) {
+ this.deviceId = deviceId;
+ this.name = name;
+ this.opened = opened;
+ this.roots = roots;
}
}
diff --git a/packages/MtpDocumentsProvider/src/com/android/mtp/MtpDocumentsProvider.java b/packages/MtpDocumentsProvider/src/com/android/mtp/MtpDocumentsProvider.java
index d5f00e6..57a68ba 100644
--- a/packages/MtpDocumentsProvider/src/com/android/mtp/MtpDocumentsProvider.java
+++ b/packages/MtpDocumentsProvider/src/com/android/mtp/MtpDocumentsProvider.java
@@ -190,9 +190,6 @@
getDocumentLoader(parentIdentifier).clearTask(parentIdentifier);
notifyChildDocumentsChange(parentIdentifier.mDocumentId);
} catch (IOException error) {
- for (final StackTraceElement element : error.getStackTrace()) {
- Log.e("hirono", element.toString());
- }
throw new FileNotFoundException(error.getMessage());
}
}
@@ -250,7 +247,7 @@
closeDeviceInternal(deviceId);
mDatabase.removeDeviceRows(deviceId);
}
- mRootScanner.notifyChange();
+ mRootScanner.resume();
}
int[] getOpenedDeviceIds() {
@@ -261,7 +258,12 @@
String getDeviceName(int deviceId) throws IOException {
synchronized (mDeviceListLock) {
- return mMtpManager.getDeviceName(deviceId);
+ for (final MtpDeviceRecord device : mMtpManager.getDevices()) {
+ if (device.deviceId == deviceId) {
+ return device.name;
+ }
+ }
+ throw new IOException("Not found the device: " + Integer.toString(deviceId));
}
}
@@ -293,19 +295,11 @@
}
/**
- * Reopens MTP devices based on database state.
+ * Clears MTP identifier in the database.
*/
private void resume() {
synchronized (mDeviceListLock) {
mDatabase.getMapper().clearMapping();
- final int[] ids = mDatabase.getDeviceIds();
- for (final int id : ids) {
- try {
- openDevice(id);
- } catch (IOException exception) {
- mDatabase.removeDeviceRows(id);
- }
- }
}
}
diff --git a/packages/MtpDocumentsProvider/src/com/android/mtp/MtpManager.java b/packages/MtpDocumentsProvider/src/com/android/mtp/MtpManager.java
index e7f94b7..88cab8b 100644
--- a/packages/MtpDocumentsProvider/src/com/android/mtp/MtpManager.java
+++ b/packages/MtpDocumentsProvider/src/com/android/mtp/MtpManager.java
@@ -17,8 +17,10 @@
package com.android.mtp;
import android.content.Context;
+import android.hardware.usb.UsbConstants;
import android.hardware.usb.UsbDevice;
import android.hardware.usb.UsbDeviceConnection;
+import android.hardware.usb.UsbInterface;
import android.hardware.usb.UsbManager;
import android.mtp.MtpConstants;
import android.mtp.MtpDevice;
@@ -26,12 +28,14 @@
import android.mtp.MtpObjectInfo;
import android.os.CancellationSignal;
import android.os.ParcelFileDescriptor;
+import android.util.Log;
import android.util.SparseArray;
import com.android.internal.annotations.VisibleForTesting;
import java.io.FileNotFoundException;
import java.io.IOException;
+import java.util.ArrayList;
/**
* The model wrapping android.mtp API.
@@ -39,6 +43,27 @@
class MtpManager {
final static int OBJECT_HANDLE_ROOT_CHILDREN = -1;
+ /**
+ * Subclass for PTP.
+ */
+ private static final int SUBCLASS_STILL_IMAGE_CAPTURE = 1;
+
+ /**
+ * Subclass for Android style MTP.
+ */
+ private static final int SUBCLASS_MTP = 0xff;
+
+ /**
+ * Protocol for Picture Transfer Protocol (PIMA 15470).
+ */
+ private static final int PROTOCOL_PICTURE_TRANSFER = 1;
+
+ /**
+ * Protocol for Android style MTP.
+ */
+ private static final int PROTOCOL_MTP = 0;
+
+
private final UsbManager mManager;
// TODO: Save and restore the set of opened device.
private final SparseArray<MtpDevice> mDevices = new SparseArray<>();
@@ -92,6 +117,33 @@
mDevices.remove(deviceId);
}
+ synchronized MtpDeviceRecord[] getDevices() {
+ final ArrayList<MtpDeviceRecord> devices = new ArrayList<>();
+ for (UsbDevice device : mManager.getDeviceList().values()) {
+ if (!isMtpDevice(device)) {
+ continue;
+ }
+ final boolean opened = mDevices.get(device.getDeviceId()) != null;
+ final String name = device.getProductName();
+ MtpRoot[] roots;
+ if (opened) {
+ try {
+ roots = getRoots(device.getDeviceId());
+ } catch (IOException exp) {
+ Log.e(MtpDocumentsProvider.TAG, exp.getMessage());
+ // If we failed to fetch roots for the device, we still returns device model
+ // with an empty set of roots so that the device is shown DocumentsUI as long as
+ // the device is physically connected.
+ roots = new MtpRoot[0];
+ }
+ } else {
+ roots = new MtpRoot[0];
+ }
+ devices.add(new MtpDeviceRecord(device.getDeviceId(), name, opened, roots));
+ }
+ return devices.toArray(new MtpDeviceRecord[devices.size()]);
+ }
+
synchronized int[] getOpenedDeviceIds() {
final int[] result = new int[mDevices.size()];
for (int i = 0; i < result.length; i++) {
@@ -100,28 +152,6 @@
return result;
}
- String getDeviceName(int deviceId) throws IOException {
- return getDevice(deviceId).getDeviceInfo().getModel();
- }
-
- MtpRoot[] getRoots(int deviceId) throws IOException {
- final MtpDevice device = getDevice(deviceId);
- synchronized (device) {
- final int[] storageIds = device.getStorageIds();
- if (storageIds == null) {
- throw new IOException("Failed to obtain storage IDs.");
- }
- final MtpRoot[] results = new MtpRoot[storageIds.length];
- for (int i = 0; i < storageIds.length; i++) {
- results[i] = new MtpRoot(
- device.getDeviceId(),
- device.getDeviceInfo().getModel(),
- device.getStorageInfo(storageIds[i]));
- }
- return results;
- }
- }
-
MtpObjectInfo getObjectInfo(int deviceId, int objectHandle)
throws IOException {
final MtpDevice device = getDevice(deviceId);
@@ -212,4 +242,40 @@
}
return device;
}
+
+ private MtpRoot[] getRoots(int deviceId) throws IOException {
+ final MtpDevice device = getDevice(deviceId);
+ synchronized (device) {
+ final int[] storageIds = device.getStorageIds();
+ if (storageIds == null) {
+ throw new IOException("Failed to obtain storage IDs.");
+ }
+ final MtpRoot[] results = new MtpRoot[storageIds.length];
+ for (int i = 0; i < storageIds.length; i++) {
+ results[i] = new MtpRoot(
+ device.getDeviceId(),
+ device.getDeviceInfo().getModel(),
+ device.getStorageInfo(storageIds[i]));
+ }
+ return results;
+ }
+ }
+
+ static boolean isMtpDevice(UsbDevice device) {
+ for (int i = 0; i < device.getInterfaceCount(); i++) {
+ final UsbInterface usbInterface = device.getInterface(i);
+ if ((usbInterface.getInterfaceClass() == UsbConstants.USB_CLASS_STILL_IMAGE &&
+ usbInterface.getInterfaceSubclass() == SUBCLASS_STILL_IMAGE_CAPTURE &&
+ usbInterface.getInterfaceProtocol() == PROTOCOL_PICTURE_TRANSFER)) {
+ return true;
+ }
+ if (usbInterface.getInterfaceClass() == UsbConstants.USB_SUBCLASS_VENDOR_SPEC &&
+ usbInterface.getInterfaceSubclass() == SUBCLASS_MTP &&
+ usbInterface.getInterfaceProtocol() == PROTOCOL_MTP &&
+ usbInterface.getName().equals("MTP")) {
+ return true;
+ }
+ }
+ return false;
+ }
}
diff --git a/packages/MtpDocumentsProvider/src/com/android/mtp/RootScanner.java b/packages/MtpDocumentsProvider/src/com/android/mtp/RootScanner.java
index df2ab01..e6c2726 100644
--- a/packages/MtpDocumentsProvider/src/com/android/mtp/RootScanner.java
+++ b/packages/MtpDocumentsProvider/src/com/android/mtp/RootScanner.java
@@ -2,13 +2,14 @@
import android.content.ContentResolver;
import android.content.res.Resources;
-import android.database.sqlite.SQLiteException;
import android.net.Uri;
import android.os.Process;
import android.provider.DocumentsContract;
import android.util.Log;
import java.io.IOException;
+import java.util.HashMap;
+import java.util.Map;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.FutureTask;
@@ -105,31 +106,36 @@
Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
int pollingCount = 0;
while (!Thread.interrupted()) {
- final int[] deviceIds = mManager.getOpenedDeviceIds();
- if (deviceIds.length == 0) {
- return;
- }
boolean changed = false;
- for (int deviceId : deviceIds) {
- try {
- final MtpRoot[] roots = mManager.getRoots(deviceId);
- mDatabase.getMapper().startAddingRootDocuments(deviceId);
- try {
- if (mDatabase.getMapper().putRootDocuments(
- deviceId, mResources, roots)) {
- changed = true;
- }
- } finally {
- if (mDatabase.getMapper().stopAddingRootDocuments(deviceId)) {
- changed = true;
- }
- }
- } catch (IOException | SQLiteException exception) {
- // The error may happen on the device. We would like to continue getting
- // roots for other devices.
- Log.e(MtpDocumentsProvider.TAG, exception.getMessage());
+
+ // Update devices.
+ final MtpDeviceRecord[] devices = mManager.getDevices();
+ mDatabase.getMapper().startAddingDocuments(null /* parentDocumentId */);
+ for (final MtpDeviceRecord device : devices) {
+ if (mDatabase.getMapper().putDeviceDocument(device)) {
+ changed = true;
}
}
+ if (mDatabase.getMapper().stopAddingDocuments(null /* parentDocumentId */)) {
+ changed = true;
+ }
+
+ // Update roots.
+ for (final MtpDeviceRecord device : devices) {
+ final String documentId = mDatabase.getDocumentIdForDevice(device.deviceId);
+ if (documentId == null) {
+ continue;
+ }
+ mDatabase.getMapper().startAddingDocuments(documentId);
+ if (mDatabase.getMapper().putRootDocuments(
+ documentId, mResources, device.roots)) {
+ changed = true;
+ }
+ if (mDatabase.getMapper().stopAddingDocuments(documentId)) {
+ changed = true;
+ }
+ }
+
if (changed) {
notifyChange();
}
diff --git a/packages/MtpDocumentsProvider/tests/src/com/android/mtp/DocumentLoaderTest.java b/packages/MtpDocumentsProvider/tests/src/com/android/mtp/DocumentLoaderTest.java
index f0b4343..7bd9a17 100644
--- a/packages/MtpDocumentsProvider/tests/src/com/android/mtp/DocumentLoaderTest.java
+++ b/packages/MtpDocumentsProvider/tests/src/com/android/mtp/DocumentLoaderTest.java
@@ -40,11 +40,11 @@
@Override
public void setUp() {
mDatabase = new MtpDatabase(getContext(), MtpDatabaseConstants.FLAG_DATABASE_IN_MEMORY);
- mDatabase.getMapper().startAddingRootDocuments(0);
- mDatabase.getMapper().putRootDocuments(0, new TestResources(), new MtpRoot[] {
+ mDatabase.getMapper().startAddingDocuments("deviceDocId");
+ mDatabase.getMapper().putRootDocuments("deviceDocId", new TestResources(), new MtpRoot[] {
new MtpRoot(0, 0, "Device", "Storage", 1000, 1000, "")
});
- mDatabase.getMapper().stopAddingRootDocuments(0);
+ mDatabase.getMapper().stopAddingDocuments("deviceDocId");
mManager = new BlockableTestMtpManager(getContext());
mResolver = new TestContentResolver();
mLoader = new DocumentLoader(mManager, mResolver, mDatabase);
diff --git a/packages/MtpDocumentsProvider/tests/src/com/android/mtp/MtpDatabaseTest.java b/packages/MtpDocumentsProvider/tests/src/com/android/mtp/MtpDatabaseTest.java
index 8166de1..b745175 100644
--- a/packages/MtpDocumentsProvider/tests/src/com/android/mtp/MtpDatabaseTest.java
+++ b/packages/MtpDocumentsProvider/tests/src/com/android/mtp/MtpDatabaseTest.java
@@ -27,7 +27,9 @@
import java.io.FileNotFoundException;
+import static android.provider.DocumentsContract.Document.*;
import static com.android.mtp.MtpDatabase.strings;
+import static com.android.mtp.MtpDatabaseConstants.*;
@SmallTest
public class MtpDatabaseTest extends AndroidTestCase {
@@ -60,9 +62,21 @@
mDatabase = null;
}
+ private static int getInt(Cursor cursor, String columnName) {
+ return cursor.getInt(cursor.getColumnIndex(columnName));
+ }
+
+ private static boolean isNull(Cursor cursor, String columnName) {
+ return cursor.isNull(cursor.getColumnIndex(columnName));
+ }
+
+ private static String getString(Cursor cursor, String columnName) {
+ return cursor.getString(cursor.getColumnIndex(columnName));
+ }
+
public void testPutRootDocuments() throws Exception {
- mDatabase.getMapper().startAddingRootDocuments(0);
- mDatabase.getMapper().putRootDocuments(0, resources, new MtpRoot[] {
+ mDatabase.getMapper().startAddingDocuments("deviceDocId");
+ mDatabase.getMapper().putRootDocuments("deviceDocId", 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,"")
@@ -73,27 +87,27 @@
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));
- assertEquals("icon", R.drawable.ic_root_mtp, cursor.getInt(8));
- assertEquals("flag", 0, cursor.getInt(9));
- assertEquals("size", 1000, cursor.getInt(10));
+ assertEquals(1, getInt(cursor, COLUMN_DOCUMENT_ID));
+ assertEquals(0, getInt(cursor, COLUMN_DEVICE_ID));
+ assertEquals(1, getInt(cursor, COLUMN_STORAGE_ID));
+ assertTrue(isNull(cursor, COLUMN_OBJECT_HANDLE));
+ assertEquals(DocumentsContract.Document.MIME_TYPE_DIR, getString(cursor, COLUMN_MIME_TYPE));
+ assertEquals("Device Storage", getString(cursor, COLUMN_DISPLAY_NAME));
+ assertTrue(isNull(cursor, COLUMN_SUMMARY));
+ assertTrue(isNull(cursor, COLUMN_LAST_MODIFIED));
+ assertEquals(R.drawable.ic_root_mtp, getInt(cursor, COLUMN_ICON));
+ assertEquals(0, getInt(cursor, COLUMN_FLAGS));
+ assertEquals(1000, getInt(cursor, COLUMN_SIZE));
assertEquals(
- "documentType", MtpDatabaseConstants.DOCUMENT_TYPE_STORAGE, cursor.getInt(11));
+ MtpDatabaseConstants.DOCUMENT_TYPE_STORAGE, getInt(cursor, COLUMN_DOCUMENT_TYPE));
cursor.moveToNext();
- assertEquals("documentId", 2, cursor.getInt(0));
- assertEquals("displayName", "Device Storage", cursor.getString(5));
+ assertEquals(2, getInt(cursor, COLUMN_DOCUMENT_ID));
+ assertEquals("Device Storage", getString(cursor, COLUMN_DISPLAY_NAME));
cursor.moveToNext();
- assertEquals("documentId", 3, cursor.getInt(0));
- assertEquals("displayName", "Device /@#%&<>Storage", cursor.getString(5));
+ assertEquals(3, getInt(cursor, COLUMN_DOCUMENT_ID));
+ assertEquals("Device /@#%&<>Storage", getString(cursor, COLUMN_DISPLAY_NAME));
cursor.close();
}
@@ -155,7 +169,7 @@
}
public void testPutChildDocuments() throws Exception {
- mDatabase.getMapper().startAddingChildDocuments("parentId");
+ mDatabase.getMapper().startAddingDocuments("parentId");
mDatabase.getMapper().putChildDocuments(0, "parentId", new MtpObjectInfo[] {
createDocument(100, "note.txt", MtpConstants.FORMAT_TEXT, 1024),
createDocument(101, "image.jpg", MtpConstants.FORMAT_EXIF_JPEG, 2 * 1024 * 1024),
@@ -166,61 +180,61 @@
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(1, getInt(cursor, COLUMN_DOCUMENT_ID));
+ assertEquals(0, getInt(cursor, COLUMN_DEVICE_ID));
+ assertEquals(0, getInt(cursor, COLUMN_STORAGE_ID));
+ assertEquals(100, getInt(cursor, COLUMN_OBJECT_HANDLE));
+ assertEquals("text/plain", getString(cursor, COLUMN_MIME_TYPE));
+ assertEquals("note.txt", getString(cursor, COLUMN_DISPLAY_NAME));
+ assertTrue(isNull(cursor, COLUMN_SUMMARY));
+ assertTrue(isNull(cursor, COLUMN_LAST_MODIFIED));
+ assertTrue(isNull(cursor, COLUMN_ICON));
assertEquals(
- "flag",
+ COLUMN_FLAGS,
DocumentsContract.Document.FLAG_SUPPORTS_DELETE |
DocumentsContract.Document.FLAG_SUPPORTS_WRITE,
cursor.getInt(9));
- assertEquals("size", 1024, cursor.getInt(10));
+ assertEquals(1024, getInt(cursor, COLUMN_SIZE));
assertEquals(
- "documentType", MtpDatabaseConstants.DOCUMENT_TYPE_OBJECT, cursor.getInt(11));
+ MtpDatabaseConstants.DOCUMENT_TYPE_OBJECT, getInt(cursor, COLUMN_DOCUMENT_TYPE));
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(2, getInt(cursor, COLUMN_DOCUMENT_ID));
+ assertEquals(0, getInt(cursor, COLUMN_DEVICE_ID));
+ assertEquals(0, getInt(cursor, COLUMN_STORAGE_ID));
+ assertEquals(101, getInt(cursor, COLUMN_OBJECT_HANDLE));
+ assertEquals("image/jpeg", getString(cursor, COLUMN_MIME_TYPE));
+ assertEquals("image.jpg", getString(cursor, COLUMN_DISPLAY_NAME));
+ assertTrue(isNull(cursor, COLUMN_SUMMARY));
+ assertTrue(isNull(cursor, COLUMN_LAST_MODIFIED));
+ assertTrue(isNull(cursor, COLUMN_ICON));
assertEquals(
- "flag",
+ COLUMN_FLAGS,
DocumentsContract.Document.FLAG_SUPPORTS_DELETE |
DocumentsContract.Document.FLAG_SUPPORTS_WRITE,
cursor.getInt(9));
- assertEquals("size", 2 * 1024 * 1024, cursor.getInt(10));
+ assertEquals(2 * 1024 * 1024, getInt(cursor, COLUMN_SIZE));
assertEquals(
- "documentType", MtpDatabaseConstants.DOCUMENT_TYPE_OBJECT, cursor.getInt(11));
+ MtpDatabaseConstants.DOCUMENT_TYPE_OBJECT, getInt(cursor, COLUMN_DOCUMENT_TYPE));
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(3, getInt(cursor, COLUMN_DOCUMENT_ID));
+ assertEquals(0, getInt(cursor, COLUMN_DEVICE_ID));
+ assertEquals(0, getInt(cursor, COLUMN_STORAGE_ID));
+ assertEquals(102, getInt(cursor, COLUMN_OBJECT_HANDLE));
+ assertEquals("audio/mpeg", getString(cursor, COLUMN_MIME_TYPE));
+ assertEquals("music.mp3", getString(cursor, COLUMN_DISPLAY_NAME));
+ assertTrue(isNull(cursor, COLUMN_SUMMARY));
+ assertTrue(isNull(cursor, COLUMN_LAST_MODIFIED));
+ assertTrue(isNull(cursor, COLUMN_ICON));
assertEquals(
- "flag",
+ COLUMN_FLAGS,
DocumentsContract.Document.FLAG_SUPPORTS_DELETE |
DocumentsContract.Document.FLAG_SUPPORTS_WRITE,
cursor.getInt(9));
- assertEquals("size", 3 * 1024 * 1024, cursor.getInt(10));
+ assertEquals(3 * 1024 * 1024, getInt(cursor, COLUMN_SIZE));
assertEquals(
- "documentType", MtpDatabaseConstants.DOCUMENT_TYPE_OBJECT, cursor.getInt(11));
+ MtpDatabaseConstants.DOCUMENT_TYPE_OBJECT, getInt(cursor, COLUMN_DOCUMENT_TYPE));
cursor.close();
}
@@ -236,8 +250,8 @@
Root.COLUMN_AVAILABLE_BYTES
};
- mDatabase.getMapper().startAddingRootDocuments(0);
- mDatabase.getMapper().putRootDocuments(0, resources, new MtpRoot[] {
+ mDatabase.getMapper().startAddingDocuments("deviceDocId");
+ mDatabase.getMapper().putRootDocuments("deviceDocId", resources, new MtpRoot[] {
new MtpRoot(0, 100, "Device", "Storage A", 1000, 0, ""),
new MtpRoot(0, 101, "Device", "Storage B", 1001, 0, "")
});
@@ -246,13 +260,13 @@
final Cursor cursor = mDatabase.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));
+ assertEquals(1, getInt(cursor, COLUMN_DOCUMENT_ID));
+ assertEquals(100, getInt(cursor, COLUMN_STORAGE_ID));
+ assertEquals("Device Storage A", getString(cursor, COLUMN_DISPLAY_NAME));
cursor.moveToNext();
- assertEquals("documentId", 2, cursor.getInt(0));
- assertEquals("storageId", 101, cursor.getInt(1));
- assertEquals("name", "Device Storage B", cursor.getString(2));
+ assertEquals(2, getInt(cursor, COLUMN_DOCUMENT_ID));
+ assertEquals(101, getInt(cursor, COLUMN_STORAGE_ID));
+ assertEquals("Device Storage B", getString(cursor, COLUMN_DISPLAY_NAME));
cursor.close();
}
@@ -260,11 +274,11 @@
final Cursor cursor = mDatabase.queryRoots(rootColumns);
assertEquals(2, cursor.getCount());
cursor.moveToNext();
- assertEquals("rootId", 1, cursor.getInt(0));
- assertEquals("availableBytes", 1000, cursor.getInt(1));
+ assertEquals(1, getInt(cursor, Root.COLUMN_ROOT_ID));
+ assertEquals(1000, getInt(cursor, Root.COLUMN_AVAILABLE_BYTES));
cursor.moveToNext();
- assertEquals("rootId", 2, cursor.getInt(0));
- assertEquals("availableBytes", 1001, cursor.getInt(1));
+ assertEquals(2, getInt(cursor, Root.COLUMN_ROOT_ID));
+ assertEquals(1001, getInt(cursor, Root.COLUMN_AVAILABLE_BYTES));
cursor.close();
}
@@ -274,13 +288,13 @@
final Cursor cursor = mDatabase.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));
+ assertEquals(1, getInt(cursor, COLUMN_DOCUMENT_ID));
+ assertTrue(isNull(cursor, COLUMN_STORAGE_ID));
+ assertEquals("Device Storage A", getString(cursor, COLUMN_DISPLAY_NAME));
cursor.moveToNext();
- assertEquals("documentId", 2, cursor.getInt(0));
- assertTrue("storageId", cursor.isNull(1));
- assertEquals("name", "Device Storage B", cursor.getString(2));
+ assertEquals(2, getInt(cursor, COLUMN_DOCUMENT_ID));
+ assertTrue(isNull(cursor, COLUMN_STORAGE_ID));
+ assertEquals("Device Storage B", getString(cursor, COLUMN_DISPLAY_NAME));
cursor.close();
}
@@ -288,16 +302,16 @@
final Cursor cursor = mDatabase.queryRoots(rootColumns);
assertEquals(2, cursor.getCount());
cursor.moveToNext();
- assertEquals("rootId", 1, cursor.getInt(0));
- assertEquals("availableBytes", 1000, cursor.getInt(1));
+ assertEquals(1, getInt(cursor, Root.COLUMN_ROOT_ID));
+ assertEquals(1000, getInt(cursor, Root.COLUMN_AVAILABLE_BYTES));
cursor.moveToNext();
- assertEquals("rootId", 2, cursor.getInt(0));
- assertEquals("availableBytes", 1001, cursor.getInt(1));
+ assertEquals(2, getInt(cursor, Root.COLUMN_ROOT_ID));
+ assertEquals(1001, getInt(cursor, Root.COLUMN_AVAILABLE_BYTES));
cursor.close();
}
- mDatabase.getMapper().startAddingRootDocuments(0);
- mDatabase.getMapper().putRootDocuments(0, resources, new MtpRoot[] {
+ mDatabase.getMapper().startAddingDocuments("deviceDocId");
+ mDatabase.getMapper().putRootDocuments("deviceDocId", resources, new MtpRoot[] {
new MtpRoot(0, 200, "Device", "Storage A", 2000, 0, ""),
new MtpRoot(0, 202, "Device", "Storage C", 2002, 0, "")
});
@@ -306,17 +320,17 @@
final Cursor cursor = mDatabase.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));
+ assertEquals(1, getInt(cursor, COLUMN_DOCUMENT_ID));
+ assertTrue(isNull(cursor, COLUMN_STORAGE_ID));
+ assertEquals("Device Storage A", getString(cursor, COLUMN_DISPLAY_NAME));
cursor.moveToNext();
- assertEquals("documentId", 2, cursor.getInt(0));
- assertTrue("storageId", cursor.isNull(1));
- assertEquals("name", "Device Storage B", cursor.getString(2));
+ assertEquals(2, getInt(cursor, COLUMN_DOCUMENT_ID));
+ assertTrue(isNull(cursor, COLUMN_STORAGE_ID));
+ assertEquals("Device Storage B", getString(cursor, COLUMN_DISPLAY_NAME));
cursor.moveToNext();
- assertEquals("documentId", 4, cursor.getInt(0));
- assertEquals("storageId", 202, cursor.getInt(1));
- assertEquals("name", "Device Storage C", cursor.getString(2));
+ assertEquals(4, getInt(cursor, COLUMN_DOCUMENT_ID));
+ assertEquals(202, getInt(cursor, COLUMN_STORAGE_ID));
+ assertEquals("Device Storage C", getString(cursor, COLUMN_DISPLAY_NAME));
cursor.close();
}
@@ -324,30 +338,30 @@
final Cursor cursor = mDatabase.queryRoots(rootColumns);
assertEquals(3, cursor.getCount());
cursor.moveToNext();
- assertEquals("rootId", 1, cursor.getInt(0));
- assertEquals("availableBytes", 1000, cursor.getInt(1));
+ assertEquals(1, getInt(cursor, Root.COLUMN_ROOT_ID));
+ assertEquals(1000, getInt(cursor, Root.COLUMN_AVAILABLE_BYTES));
cursor.moveToNext();
- assertEquals("rootId", 2, cursor.getInt(0));
- assertEquals("availableBytes", 1001, cursor.getInt(1));
+ assertEquals(2, getInt(cursor, Root.COLUMN_ROOT_ID));
+ assertEquals(1001, getInt(cursor, Root.COLUMN_AVAILABLE_BYTES));
cursor.moveToNext();
- assertEquals("rootId", 4, cursor.getInt(0));
- assertEquals("availableBytes", 2002, cursor.getInt(1));
+ assertEquals(4, getInt(cursor, Root.COLUMN_ROOT_ID));
+ assertEquals(2002, getInt(cursor, Root.COLUMN_AVAILABLE_BYTES));
cursor.close();
}
- mDatabase.getMapper().stopAddingRootDocuments(0);
+ mDatabase.getMapper().stopAddingDocuments("deviceDocId");
{
final Cursor cursor = mDatabase.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));
+ assertEquals(1, getInt(cursor, COLUMN_DOCUMENT_ID));
+ assertEquals(200, getInt(cursor, COLUMN_STORAGE_ID));
+ assertEquals("Device Storage A", getString(cursor, COLUMN_DISPLAY_NAME));
cursor.moveToNext();
- assertEquals("documentId", 4, cursor.getInt(0));
- assertEquals("storageId", 202, cursor.getInt(1));
- assertEquals("name", "Device Storage C", cursor.getString(2));
+ assertEquals(4, getInt(cursor, COLUMN_DOCUMENT_ID));
+ assertEquals(202, getInt(cursor, COLUMN_STORAGE_ID));
+ assertEquals("Device Storage C", getString(cursor, COLUMN_DISPLAY_NAME));
cursor.close();
}
@@ -355,11 +369,11 @@
final Cursor cursor = mDatabase.queryRoots(rootColumns);
assertEquals(2, cursor.getCount());
cursor.moveToNext();
- assertEquals("rootId", 1, cursor.getInt(0));
- assertEquals("availableBytes", 2000, cursor.getInt(1));
+ assertEquals(1, getInt(cursor, Root.COLUMN_ROOT_ID));
+ assertEquals(2000, getInt(cursor, Root.COLUMN_AVAILABLE_BYTES));
cursor.moveToNext();
- assertEquals("rootId", 4, cursor.getInt(0));
- assertEquals("availableBytes", 2002, cursor.getInt(1));
+ assertEquals(4, getInt(cursor, Root.COLUMN_ROOT_ID));
+ assertEquals(2002, getInt(cursor, Root.COLUMN_AVAILABLE_BYTES));
cursor.close();
}
}
@@ -370,7 +384,7 @@
MtpDatabaseConstants.COLUMN_OBJECT_HANDLE,
DocumentsContract.Document.COLUMN_DISPLAY_NAME
};
- mDatabase.getMapper().startAddingChildDocuments("parentId");
+ mDatabase.getMapper().startAddingDocuments("parentId");
mDatabase.getMapper().putChildDocuments(0, "parentId", new MtpObjectInfo[] {
createDocument(100, "note.txt", MtpConstants.FORMAT_TEXT, 1024),
createDocument(101, "image.jpg", MtpConstants.FORMAT_EXIF_JPEG, 2 * 1024 * 1024),
@@ -383,24 +397,24 @@
assertEquals(3, cursor.getCount());
cursor.moveToNext();
- assertEquals("documentId", 1, cursor.getInt(0));
- assertTrue("objectHandle", cursor.isNull(1));
- assertEquals("name", "note.txt", cursor.getString(2));
+ assertEquals(1, getInt(cursor, COLUMN_DOCUMENT_ID));
+ assertTrue(isNull(cursor, COLUMN_OBJECT_HANDLE));
+ assertEquals("note.txt", getString(cursor, COLUMN_DISPLAY_NAME));
cursor.moveToNext();
- assertEquals("documentId", 2, cursor.getInt(0));
- assertTrue("objectHandle", cursor.isNull(1));
- assertEquals("name", "image.jpg", cursor.getString(2));
+ assertEquals(2, getInt(cursor, COLUMN_DOCUMENT_ID));
+ assertTrue(isNull(cursor, COLUMN_OBJECT_HANDLE));
+ assertEquals("image.jpg", getString(cursor, COLUMN_DISPLAY_NAME));
cursor.moveToNext();
- assertEquals("documentId", 3, cursor.getInt(0));
- assertTrue("objectHandle", cursor.isNull(1));
- assertEquals("name", "music.mp3", cursor.getString(2));
+ assertEquals(3, getInt(cursor, COLUMN_DOCUMENT_ID));
+ assertTrue(isNull(cursor, COLUMN_OBJECT_HANDLE));
+ assertEquals("music.mp3", getString(cursor, COLUMN_DISPLAY_NAME));
cursor.close();
}
- mDatabase.getMapper().startAddingChildDocuments("parentId");
+ mDatabase.getMapper().startAddingDocuments("parentId");
mDatabase.getMapper().putChildDocuments(0, "parentId", new MtpObjectInfo[] {
createDocument(200, "note.txt", MtpConstants.FORMAT_TEXT, 1024),
createDocument(203, "video.mp4", MtpConstants.FORMAT_MP4_CONTAINER, 1024),
@@ -411,28 +425,28 @@
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));
+ assertEquals(5, getInt(cursor, COLUMN_DOCUMENT_ID));
+ assertEquals(203, getInt(cursor, COLUMN_OBJECT_HANDLE));
+ assertEquals("video.mp4", getString(cursor, COLUMN_DISPLAY_NAME));
cursor.close();
}
- mDatabase.getMapper().stopAddingChildDocuments("parentId");
+ mDatabase.getMapper().stopAddingDocuments("parentId");
{
final Cursor cursor = mDatabase.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));
+ assertEquals(1, getInt(cursor, COLUMN_DOCUMENT_ID));
+ assertEquals(200, getInt(cursor, COLUMN_OBJECT_HANDLE));
+ assertEquals("note.txt", getString(cursor, COLUMN_DISPLAY_NAME));
cursor.moveToNext();
- assertEquals("documentId", 5, cursor.getInt(0));
- assertEquals("objectHandle", 203, cursor.getInt(1));
- assertEquals("name", "video.mp4", cursor.getString(2));
+ assertEquals(5, getInt(cursor, COLUMN_DOCUMENT_ID));
+ assertEquals(203, getInt(cursor, COLUMN_OBJECT_HANDLE));
+ assertEquals("video.mp4", getString(cursor, COLUMN_DISPLAY_NAME));
cursor.close();
}
}
@@ -447,12 +461,12 @@
Root.COLUMN_ROOT_ID,
Root.COLUMN_AVAILABLE_BYTES
};
- mDatabase.getMapper().startAddingRootDocuments(0);
- mDatabase.getMapper().startAddingRootDocuments(1);
- mDatabase.getMapper().putRootDocuments(0, resources, new MtpRoot[] {
+ mDatabase.getMapper().startAddingDocuments("deviceDocIdA");
+ mDatabase.getMapper().startAddingDocuments("deviceDocIdB");
+ mDatabase.getMapper().putRootDocuments("deviceDocIdA", resources, new MtpRoot[] {
new MtpRoot(0, 100, "Device", "Storage", 0, 0, "")
});
- mDatabase.getMapper().putRootDocuments(1, resources, new MtpRoot[] {
+ mDatabase.getMapper().putRootDocuments("deviceDocIdB", resources, new MtpRoot[] {
new MtpRoot(1, 100, "Device", "Storage", 0, 0, "")
});
@@ -460,13 +474,13 @@
final Cursor cursor = mDatabase.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));
+ assertEquals(1, getInt(cursor, COLUMN_DOCUMENT_ID));
+ assertEquals(100, getInt(cursor, COLUMN_STORAGE_ID));
+ assertEquals("Device Storage", getString(cursor, COLUMN_DISPLAY_NAME));
cursor.moveToNext();
- assertEquals("documentId", 2, cursor.getInt(0));
- assertEquals("storageId", 100, cursor.getInt(1));
- assertEquals("name", "Device Storage", cursor.getString(2));
+ assertEquals(2, getInt(cursor, COLUMN_DOCUMENT_ID));
+ assertEquals(100, getInt(cursor, COLUMN_STORAGE_ID));
+ assertEquals("Device Storage", getString(cursor, COLUMN_DISPLAY_NAME));
cursor.close();
}
@@ -474,38 +488,38 @@
final Cursor cursor = mDatabase.queryRoots(rootColumns);
assertEquals(2, cursor.getCount());
cursor.moveToNext();
- assertEquals("rootId", 1, cursor.getInt(0));
- assertEquals("availableBytes", 0, cursor.getInt(1));
+ assertEquals(1, getInt(cursor, Root.COLUMN_ROOT_ID));
+ assertEquals(0, getInt(cursor, Root.COLUMN_AVAILABLE_BYTES));
cursor.moveToNext();
- assertEquals("rootId", 2, cursor.getInt(0));
- assertEquals("availableBytes", 0, cursor.getInt(1));
+ assertEquals(2, getInt(cursor, Root.COLUMN_ROOT_ID));
+ assertEquals(0, getInt(cursor, Root.COLUMN_AVAILABLE_BYTES));
cursor.close();
}
mDatabase.getMapper().clearMapping();
- mDatabase.getMapper().startAddingRootDocuments(0);
- mDatabase.getMapper().startAddingRootDocuments(1);
- mDatabase.getMapper().putRootDocuments(0, resources, new MtpRoot[] {
+ mDatabase.getMapper().startAddingDocuments("deviceDocIdA");
+ mDatabase.getMapper().startAddingDocuments("deviceDocIdB");
+ mDatabase.getMapper().putRootDocuments("deviceDocIdA", resources, new MtpRoot[] {
new MtpRoot(0, 200, "Device", "Storage", 2000, 0, "")
});
- mDatabase.getMapper().putRootDocuments(1, resources, new MtpRoot[] {
+ mDatabase.getMapper().putRootDocuments("deviceDocIdB", resources, new MtpRoot[] {
new MtpRoot(1, 300, "Device", "Storage", 3000, 0, "")
});
- mDatabase.getMapper().stopAddingRootDocuments(0);
- mDatabase.getMapper().stopAddingRootDocuments(1);
+ mDatabase.getMapper().stopAddingDocuments("deviceDocIdA");
+ mDatabase.getMapper().stopAddingDocuments("deviceDocIdB");
{
final Cursor cursor = mDatabase.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));
+ assertEquals(1, getInt(cursor, COLUMN_DOCUMENT_ID));
+ assertEquals(200, getInt(cursor, COLUMN_STORAGE_ID));
+ assertEquals("Device Storage", getString(cursor, COLUMN_DISPLAY_NAME));
cursor.moveToNext();
- assertEquals("documentId", 2, cursor.getInt(0));
- assertEquals("storageId", 300, cursor.getInt(1));
- assertEquals("name", "Device Storage", cursor.getString(2));
+ assertEquals(2, getInt(cursor, COLUMN_DOCUMENT_ID));
+ assertEquals(300, getInt(cursor, COLUMN_STORAGE_ID));
+ assertEquals("Device Storage", getString(cursor, COLUMN_DISPLAY_NAME));
cursor.close();
}
@@ -513,11 +527,11 @@
final Cursor cursor = mDatabase.queryRoots(rootColumns);
assertEquals(2, cursor.getCount());
cursor.moveToNext();
- assertEquals("rootId", 1, cursor.getInt(0));
- assertEquals("availableBytes", 2000, cursor.getInt(1));
+ assertEquals(1, getInt(cursor, Root.COLUMN_ROOT_ID));
+ assertEquals(2000, getInt(cursor, Root.COLUMN_AVAILABLE_BYTES));
cursor.moveToNext();
- assertEquals("rootId", 2, cursor.getInt(0));
- assertEquals("availableBytes", 3000, cursor.getInt(1));
+ assertEquals(2, getInt(cursor, Root.COLUMN_ROOT_ID));
+ assertEquals(3000, getInt(cursor, Root.COLUMN_AVAILABLE_BYTES));
cursor.close();
}
}
@@ -528,8 +542,8 @@
MtpDatabaseConstants.COLUMN_OBJECT_HANDLE
};
- mDatabase.getMapper().startAddingChildDocuments("parentId1");
- mDatabase.getMapper().startAddingChildDocuments("parentId2");
+ mDatabase.getMapper().startAddingDocuments("parentId1");
+ mDatabase.getMapper().startAddingDocuments("parentId2");
mDatabase.getMapper().putChildDocuments(0, "parentId1", new MtpObjectInfo[] {
createDocument(100, "note.txt", MtpConstants.FORMAT_TEXT, 1024),
});
@@ -538,30 +552,30 @@
});
mDatabase.getMapper().clearMapping();
- mDatabase.getMapper().startAddingChildDocuments("parentId1");
- mDatabase.getMapper().startAddingChildDocuments("parentId2");
+ mDatabase.getMapper().startAddingDocuments("parentId1");
+ mDatabase.getMapper().startAddingDocuments("parentId2");
mDatabase.getMapper().putChildDocuments(0, "parentId1", new MtpObjectInfo[] {
createDocument(200, "note.txt", MtpConstants.FORMAT_TEXT, 1024),
});
mDatabase.getMapper().putChildDocuments(0, "parentId2", new MtpObjectInfo[] {
createDocument(201, "note.txt", MtpConstants.FORMAT_TEXT, 1024),
});
- mDatabase.getMapper().stopAddingChildDocuments("parentId1");
+ mDatabase.getMapper().stopAddingDocuments("parentId1");
{
final Cursor cursor = mDatabase.queryChildDocuments(columns, "parentId1");
assertEquals(1, cursor.getCount());
cursor.moveToNext();
- assertEquals("documentId", 1, cursor.getInt(0));
- assertEquals("objectHandle", 200, cursor.getInt(1));
+ assertEquals(1, getInt(cursor, COLUMN_DOCUMENT_ID));
+ assertEquals(200, getInt(cursor, COLUMN_OBJECT_HANDLE));
cursor.close();
}
{
final Cursor cursor = mDatabase.queryChildDocuments(columns, "parentId2");
assertEquals(1, cursor.getCount());
cursor.moveToNext();
- assertEquals("documentId", 2, cursor.getInt(0));
- assertTrue("objectHandle", cursor.isNull(1));
+ assertEquals(2, getInt(cursor, COLUMN_DOCUMENT_ID));
+ assertTrue(isNull(cursor, COLUMN_OBJECT_HANDLE));
cursor.close();
}
}
@@ -577,39 +591,39 @@
Root.COLUMN_AVAILABLE_BYTES
};
- mDatabase.getMapper().startAddingRootDocuments(0);
- mDatabase.getMapper().putRootDocuments(0, resources, new MtpRoot[] {
+ mDatabase.getMapper().startAddingDocuments("deviceDocId");
+ mDatabase.getMapper().putRootDocuments("deviceDocId", resources, new MtpRoot[] {
new MtpRoot(0, 100, "Device", "Storage", 0, 0, ""),
});
mDatabase.getMapper().clearMapping();
- mDatabase.getMapper().startAddingRootDocuments(0);
- mDatabase.getMapper().putRootDocuments(0, resources, new MtpRoot[] {
+ mDatabase.getMapper().startAddingDocuments("deviceDocId");
+ mDatabase.getMapper().putRootDocuments("deviceDocId", resources, new MtpRoot[] {
new MtpRoot(0, 200, "Device", "Storage", 2000, 0, ""),
});
mDatabase.getMapper().clearMapping();
- mDatabase.getMapper().startAddingRootDocuments(0);
- mDatabase.getMapper().putRootDocuments(0, resources, new MtpRoot[] {
+ mDatabase.getMapper().startAddingDocuments("deviceDocId");
+ mDatabase.getMapper().putRootDocuments("deviceDocId", resources, new MtpRoot[] {
new MtpRoot(0, 300, "Device", "Storage", 3000, 0, ""),
});
- mDatabase.getMapper().stopAddingRootDocuments(0);
+ mDatabase.getMapper().stopAddingDocuments("deviceDocId");
{
final Cursor cursor = mDatabase.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));
+ assertEquals(1, getInt(cursor, COLUMN_DOCUMENT_ID));
+ assertEquals(300, getInt(cursor, COLUMN_STORAGE_ID));
+ assertEquals("Device Storage", getString(cursor, COLUMN_DISPLAY_NAME));
cursor.close();
}
{
final Cursor cursor = mDatabase.queryRoots(rootColumns);
assertEquals(1, cursor.getCount());
cursor.moveToNext();
- assertEquals("rootId", 1, cursor.getInt(0));
- assertEquals("availableBytes", 3000, cursor.getInt(1));
+ assertEquals(1, getInt(cursor, Root.COLUMN_ROOT_ID));
+ assertEquals(3000, getInt(cursor, Root.COLUMN_AVAILABLE_BYTES));
cursor.close();
}
}
@@ -625,41 +639,41 @@
Root.COLUMN_AVAILABLE_BYTES
};
- mDatabase.getMapper().startAddingRootDocuments(0);
- mDatabase.getMapper().putRootDocuments(0, resources, new MtpRoot[] {
+ mDatabase.getMapper().startAddingDocuments("deviceDocId");
+ mDatabase.getMapper().putRootDocuments("deviceDocId", resources, new MtpRoot[] {
new MtpRoot(0, 100, "Device", "Storage", 0, 0, ""),
});
mDatabase.getMapper().clearMapping();
- mDatabase.getMapper().startAddingRootDocuments(0);
- mDatabase.getMapper().putRootDocuments(0, resources, new MtpRoot[] {
+ mDatabase.getMapper().startAddingDocuments("deviceDocId");
+ mDatabase.getMapper().putRootDocuments("deviceDocId", resources, new MtpRoot[] {
new MtpRoot(0, 200, "Device", "Storage", 2000, 0, ""),
new MtpRoot(0, 201, "Device", "Storage", 2001, 0, ""),
});
- mDatabase.getMapper().stopAddingRootDocuments(0);
+ mDatabase.getMapper().stopAddingDocuments("deviceDocId");
{
final Cursor cursor = mDatabase.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));
+ assertEquals(2, getInt(cursor, COLUMN_DOCUMENT_ID));
+ assertEquals(200, getInt(cursor, COLUMN_STORAGE_ID));
+ assertEquals("Device Storage", getString(cursor, COLUMN_DISPLAY_NAME));
cursor.moveToNext();
- assertEquals("documentId", 3, cursor.getInt(0));
- assertEquals("storageId", 201, cursor.getInt(1));
- assertEquals("name", "Device Storage", cursor.getString(2));
+ assertEquals(3, getInt(cursor, COLUMN_DOCUMENT_ID));
+ assertEquals(201, getInt(cursor, COLUMN_STORAGE_ID));
+ assertEquals("Device Storage", getString(cursor, COLUMN_DISPLAY_NAME));
cursor.close();
}
{
final Cursor cursor = mDatabase.queryRoots(rootColumns);
assertEquals(2, cursor.getCount());
cursor.moveToNext();
- assertEquals("rootId", 2, cursor.getInt(0));
- assertEquals("availableBytes", 2000, cursor.getInt(1));
+ assertEquals(2, getInt(cursor, Root.COLUMN_ROOT_ID));
+ assertEquals(2000, getInt(cursor, Root.COLUMN_AVAILABLE_BYTES));
cursor.moveToNext();
- assertEquals("rootId", 3, cursor.getInt(0));
- assertEquals("availableBytes", 2001, cursor.getInt(1));
+ assertEquals(3, getInt(cursor, Root.COLUMN_ROOT_ID));
+ assertEquals(2001, getInt(cursor, Root.COLUMN_AVAILABLE_BYTES));
cursor.close();
}
}
@@ -667,17 +681,17 @@
public void testReplaceExistingRoots() {
// The client code should be able to replace existing rows with new information.
// Add one.
- mDatabase.getMapper().startAddingRootDocuments(0);
- mDatabase.getMapper().putRootDocuments(0, resources, new MtpRoot[] {
+ mDatabase.getMapper().startAddingDocuments("deviceDocId");
+ mDatabase.getMapper().putRootDocuments("deviceDocId", resources, new MtpRoot[] {
new MtpRoot(0, 100, "Device", "Storage A", 0, 0, ""),
});
- mDatabase.getMapper().stopAddingRootDocuments(0);
+ mDatabase.getMapper().stopAddingDocuments("deviceDocId");
// Replace it.
- mDatabase.getMapper().startAddingRootDocuments(0);
- mDatabase.getMapper().putRootDocuments(0, resources, new MtpRoot[] {
+ mDatabase.getMapper().startAddingDocuments("deviceDocId");
+ mDatabase.getMapper().putRootDocuments("deviceDocId", resources, new MtpRoot[] {
new MtpRoot(0, 100, "Device", "Storage B", 1000, 1000, ""),
});
- mDatabase.getMapper().stopAddingRootDocuments(0);
+ mDatabase.getMapper().stopAddingDocuments("deviceDocId");
{
final String[] columns = new String[] {
DocumentsContract.Document.COLUMN_DOCUMENT_ID,
@@ -687,9 +701,9 @@
final Cursor cursor = mDatabase.queryRootDocuments(columns);
assertEquals(1, cursor.getCount());
cursor.moveToNext();
- assertEquals("documentId", 1, cursor.getInt(0));
- assertEquals("storageId", 100, cursor.getInt(1));
- assertEquals("name", "Device Storage B", cursor.getString(2));
+ assertEquals(1, getInt(cursor, COLUMN_DOCUMENT_ID));
+ assertEquals(100, getInt(cursor, COLUMN_STORAGE_ID));
+ assertEquals("Device Storage B", getString(cursor, COLUMN_DISPLAY_NAME));
cursor.close();
}
{
@@ -700,8 +714,8 @@
final Cursor cursor = mDatabase.queryRoots(columns);
assertEquals(1, cursor.getCount());
cursor.moveToNext();
- assertEquals("rootId", 1, cursor.getInt(0));
- assertEquals("availableBytes", 1000, cursor.getInt(1));
+ assertEquals(1, getInt(cursor, Root.COLUMN_ROOT_ID));
+ assertEquals(1000, getInt(cursor, Root.COLUMN_AVAILABLE_BYTES));
cursor.close();
}
}
@@ -709,8 +723,8 @@
public void testFailToReplaceExisitingUnmappedRoots() {
// The client code should not be able to replace rows before resolving 'unmapped' rows.
// Add one.
- mDatabase.getMapper().startAddingRootDocuments(0);
- mDatabase.getMapper().putRootDocuments(0, resources, new MtpRoot[] {
+ mDatabase.getMapper().startAddingDocuments("deviceDocId");
+ mDatabase.getMapper().putRootDocuments("deviceDocId", resources, new MtpRoot[] {
new MtpRoot(0, 100, "Device", "Storage A", 0, 0, ""),
});
mDatabase.getMapper().clearMapping();
@@ -718,15 +732,15 @@
assertEquals(1, oldCursor.getCount());
// Add one.
- mDatabase.getMapper().startAddingRootDocuments(0);
- mDatabase.getMapper().putRootDocuments(0, resources, new MtpRoot[] {
+ mDatabase.getMapper().startAddingDocuments("deviceDocId");
+ mDatabase.getMapper().putRootDocuments("deviceDocId", resources, new MtpRoot[] {
new MtpRoot(0, 101, "Device", "Storage B", 1000, 1000, ""),
});
// Add one more before resolving unmapped documents.
- mDatabase.getMapper().putRootDocuments(0, resources, new MtpRoot[] {
+ mDatabase.getMapper().putRootDocuments("deviceDocId", resources, new MtpRoot[] {
new MtpRoot(0, 102, "Device", "Storage B", 1000, 1000, ""),
});
- mDatabase.getMapper().stopAddingRootDocuments(0);
+ mDatabase.getMapper().stopAddingDocuments("deviceDocId");
// Because the roots shares the same name, the roots should have new IDs.
final Cursor newCursor = mDatabase.queryRoots(strings(Root.COLUMN_ROOT_ID));
@@ -742,11 +756,11 @@
}
public void testQueryDocument() {
- mDatabase.getMapper().startAddingRootDocuments(0);
- mDatabase.getMapper().putRootDocuments(0, resources, new MtpRoot[] {
+ mDatabase.getMapper().startAddingDocuments("deviceDocId");
+ mDatabase.getMapper().putRootDocuments("deviceDocId", resources, new MtpRoot[] {
new MtpRoot(0, 100, "Device", "Storage A", 0, 0, ""),
});
- mDatabase.getMapper().stopAddingRootDocuments(0);
+ mDatabase.getMapper().stopAddingDocuments("deviceDocId");
final Cursor cursor = mDatabase.queryDocument("1", strings(Document.COLUMN_DISPLAY_NAME));
assertEquals(1, cursor.getCount());
@@ -756,48 +770,48 @@
}
public void testGetParentId() throws FileNotFoundException {
- mDatabase.getMapper().startAddingRootDocuments(0);
- mDatabase.getMapper().putRootDocuments(0, resources, new MtpRoot[] {
+ mDatabase.getMapper().startAddingDocuments("deviceDocId");
+ mDatabase.getMapper().putRootDocuments("deviceDocId", resources, new MtpRoot[] {
new MtpRoot(0, 100, "Device", "Storage A", 0, 0, ""),
});
- mDatabase.getMapper().stopAddingRootDocuments(0);
+ mDatabase.getMapper().stopAddingDocuments("deviceDocId");
- mDatabase.getMapper().startAddingChildDocuments("1");
+ mDatabase.getMapper().startAddingDocuments("1");
mDatabase.getMapper().putChildDocuments(
0,
"1",
new MtpObjectInfo[] {
createDocument(200, "note.txt", MtpConstants.FORMAT_TEXT, 1024),
});
- mDatabase.getMapper().stopAddingChildDocuments("1");
+ mDatabase.getMapper().stopAddingDocuments("1");
assertEquals("1", mDatabase.getParentId("2"));
}
public void testDeleteDocument() {
- mDatabase.getMapper().startAddingRootDocuments(0);
- mDatabase.getMapper().putRootDocuments(0, resources, new MtpRoot[] {
+ mDatabase.getMapper().startAddingDocuments("deviceDocId");
+ mDatabase.getMapper().putRootDocuments("deviceDocId", resources, new MtpRoot[] {
new MtpRoot(0, 100, "Device", "Storage A", 0, 0, ""),
});
- mDatabase.getMapper().stopAddingRootDocuments(0);
+ mDatabase.getMapper().stopAddingDocuments("deviceDocId");
- mDatabase.getMapper().startAddingChildDocuments("1");
+ mDatabase.getMapper().startAddingDocuments("1");
mDatabase.getMapper().putChildDocuments(
0,
"1",
new MtpObjectInfo[] {
createDocument(200, "dir", MtpConstants.FORMAT_ASSOCIATION, 1024),
});
- mDatabase.getMapper().stopAddingChildDocuments("1");
+ mDatabase.getMapper().stopAddingDocuments("1");
- mDatabase.getMapper().startAddingChildDocuments("2");
+ mDatabase.getMapper().startAddingDocuments("2");
mDatabase.getMapper().putChildDocuments(
0,
"2",
new MtpObjectInfo[] {
createDocument(200, "note.txt", MtpConstants.FORMAT_TEXT, 1024),
});
- mDatabase.getMapper().stopAddingChildDocuments("2");
+ mDatabase.getMapper().stopAddingDocuments("2");
mDatabase.deleteDocument("2");
@@ -819,11 +833,11 @@
}
public void testPutNewDocument() {
- mDatabase.getMapper().startAddingRootDocuments(0);
- mDatabase.getMapper().putRootDocuments(0, resources, new MtpRoot[] {
+ mDatabase.getMapper().startAddingDocuments("deviceDocId");
+ mDatabase.getMapper().putRootDocuments("deviceDocId", resources, new MtpRoot[] {
new MtpRoot(0, 100, "Device", "Storage A", 0, 0, ""),
});
- mDatabase.getMapper().stopAddingRootDocuments(0);
+ mDatabase.getMapper().stopAddingDocuments("deviceDocId");
assertEquals(
"2",
@@ -841,12 +855,12 @@
// The new document should not be mapped with existing invalidated document.
mDatabase.getMapper().clearMapping();
- mDatabase.getMapper().startAddingChildDocuments("1");
+ mDatabase.getMapper().startAddingDocuments("1");
mDatabase.putNewDocument(
0,
"1",
createDocument(201, "note.txt", MtpConstants.FORMAT_TEXT, 1024));
- mDatabase.getMapper().stopAddingChildDocuments("1");
+ mDatabase.getMapper().stopAddingDocuments("1");
{
final Cursor cursor =
@@ -857,4 +871,12 @@
cursor.close();
}
}
+
+ public void testGetDocumentIdForDevice() {
+ mDatabase.getMapper().startAddingDocuments(null);
+ mDatabase.getMapper().putDeviceDocument(
+ new MtpDeviceRecord(100, "Device", true, new MtpRoot[0]));
+ mDatabase.getMapper().stopAddingDocuments(null);
+ assertEquals("1", mDatabase.getDocumentIdForDevice(100));
+ }
}
diff --git a/packages/MtpDocumentsProvider/tests/src/com/android/mtp/MtpDocumentsProviderTest.java b/packages/MtpDocumentsProvider/tests/src/com/android/mtp/MtpDocumentsProviderTest.java
index dc6f79e..884b1e2 100644
--- a/packages/MtpDocumentsProvider/tests/src/com/android/mtp/MtpDocumentsProviderTest.java
+++ b/packages/MtpDocumentsProvider/tests/src/com/android/mtp/MtpDocumentsProviderTest.java
@@ -23,7 +23,6 @@
import android.provider.DocumentsContract.Root;
import android.provider.DocumentsContract;
import android.test.AndroidTestCase;
-import android.test.suitebuilder.annotation.MediumTest;
import android.test.suitebuilder.annotation.SmallTest;
import java.io.FileNotFoundException;
@@ -56,17 +55,20 @@
public void testOpenAndCloseDevice() throws Exception {
setupProvider(MtpDatabaseConstants.FLAG_DATABASE_IN_MEMORY);
- mMtpManager.addValidDevice(0);
- mMtpManager.setRoots(0, new MtpRoot[] {
- new MtpRoot(
- 0 /* deviceId */,
- 1 /* storageId */,
- "Device A" /* device model name */,
- "Storage A" /* volume description */,
- 1024 /* free space */,
- 2048 /* total space */,
- "" /* no volume identifier */)
- });
+ mMtpManager.addValidDevice(new MtpDeviceRecord(
+ 0,
+ "Device",
+ false /* unopened */,
+ new MtpRoot[] {
+ new MtpRoot(
+ 0 /* deviceId */,
+ 1 /* storageId */,
+ "Device A" /* device model name */,
+ "Storage A" /* volume description */,
+ 1024 /* free space */,
+ 2048 /* total space */,
+ "" /* no volume identifier */)
+ }));
mProvider.openDevice(0);
mResolver.waitForNotification(ROOTS_URI, 1);
@@ -92,45 +94,54 @@
}
// Check if the following notification is the first one or not.
- mMtpManager.addValidDevice(0);
- mMtpManager.setRoots(0, new MtpRoot[] {
- new MtpRoot(
- 0 /* deviceId */,
- 1 /* storageId */,
- "Device A" /* device model name */,
- "Storage A" /* volume description */,
- 1024 /* free space */,
- 2048 /* total space */,
- "" /* no volume identifier */)
- });
+ mMtpManager.addValidDevice(new MtpDeviceRecord(
+ 0,
+ "Device",
+ false /* unopened */,
+ new MtpRoot[] {
+ new MtpRoot(
+ 0 /* deviceId */,
+ 1 /* storageId */,
+ "Device A" /* device model name */,
+ "Storage A" /* volume description */,
+ 1024 /* free space */,
+ 2048 /* total space */,
+ "" /* no volume identifier */)
+ }));
mProvider.openDevice(0);
mResolver.waitForNotification(ROOTS_URI, 1);
}
public void testQueryRoots() throws Exception {
setupProvider(MtpDatabaseConstants.FLAG_DATABASE_IN_MEMORY);
- mMtpManager.addValidDevice(0);
- mMtpManager.addValidDevice(1);
- mMtpManager.setRoots(0, new MtpRoot[] {
- new MtpRoot(
- 0 /* deviceId */,
- 1 /* storageId */,
- "Device A" /* device model name */,
- "Storage A" /* volume description */,
- 1024 /* free space */,
- 2048 /* total space */,
- "" /* no volume identifier */)
- });
- mMtpManager.setRoots(1, new MtpRoot[] {
- new MtpRoot(
- 1 /* deviceId */,
- 1 /* storageId */,
- "Device B" /* device model name */,
- "Storage B" /* volume description */,
- 2048 /* free space */,
- 4096 /* total space */,
- "Identifier B" /* no volume identifier */)
- });
+ mMtpManager.addValidDevice(new MtpDeviceRecord(
+ 0,
+ "Device",
+ false /* unopened */,
+ new MtpRoot[] {
+ new MtpRoot(
+ 0 /* deviceId */,
+ 1 /* storageId */,
+ "Device A" /* device model name */,
+ "Storage A" /* volume description */,
+ 1024 /* free space */,
+ 2048 /* total space */,
+ "" /* no volume identifier */)
+ }));
+ mMtpManager.addValidDevice(new MtpDeviceRecord(
+ 1,
+ "Device",
+ false /* unopened */,
+ new MtpRoot[] {
+ new MtpRoot(
+ 1 /* deviceId */,
+ 1 /* storageId */,
+ "Device B" /* device model name */,
+ "Storage B" /* volume description */,
+ 2048 /* free space */,
+ 4096 /* total space */,
+ "Identifier B" /* no volume identifier */)
+ }));
{
mProvider.openDevice(0);
@@ -138,11 +149,11 @@
final Cursor cursor = mProvider.queryRoots(null);
assertEquals(1, cursor.getCount());
cursor.moveToNext();
- assertEquals("1", cursor.getString(0));
+ assertEquals("3", cursor.getString(0));
assertEquals(Root.FLAG_SUPPORTS_IS_CHILD | Root.FLAG_SUPPORTS_CREATE, cursor.getInt(1));
assertEquals(R.drawable.ic_root_mtp, cursor.getInt(2));
assertEquals("Device A Storage A", cursor.getString(3));
- assertEquals("1", cursor.getString(4));
+ assertEquals("3", cursor.getString(4));
assertEquals(1024, cursor.getInt(5));
}
@@ -153,30 +164,33 @@
assertEquals(2, cursor.getCount());
cursor.moveToNext();
cursor.moveToNext();
- assertEquals("2", cursor.getString(0));
+ assertEquals("4", cursor.getString(0));
assertEquals(Root.FLAG_SUPPORTS_IS_CHILD | Root.FLAG_SUPPORTS_CREATE, cursor.getInt(1));
assertEquals(R.drawable.ic_root_mtp, cursor.getInt(2));
assertEquals("Device B Storage B", cursor.getString(3));
- assertEquals("2", cursor.getString(4));
+ assertEquals("4", cursor.getString(4));
assertEquals(2048, cursor.getInt(5));
}
}
public void testQueryRoots_error() throws Exception {
setupProvider(MtpDatabaseConstants.FLAG_DATABASE_IN_MEMORY);
- mMtpManager.addValidDevice(0);
- mMtpManager.addValidDevice(1);
- // Not set roots for device 0 so that MtpManagerMock#getRoots throws IOException.
- mMtpManager.setRoots(1, new MtpRoot[] {
- new MtpRoot(
- 1 /* deviceId */,
- 1 /* storageId */,
- "Device B" /* device model name */,
- "Storage B" /* volume description */,
- 2048 /* free space */,
- 4096 /* total space */,
- "Identifier B" /* no volume identifier */)
- });
+ mMtpManager.addValidDevice(
+ new MtpDeviceRecord(0, "Device", false /* unopened */, new MtpRoot[0]));
+ mMtpManager.addValidDevice(new MtpDeviceRecord(
+ 1,
+ "Device",
+ false /* unopened */,
+ new MtpRoot[] {
+ new MtpRoot(
+ 1 /* deviceId */,
+ 1 /* storageId */,
+ "Device B" /* device model name */,
+ "Storage B" /* volume description */,
+ 2048 /* free space */,
+ 4096 /* total space */,
+ "Identifier B" /* no volume identifier */)
+ }));
{
mProvider.openDevice(0);
mProvider.openDevice(1);
@@ -185,20 +199,17 @@
final Cursor cursor = mProvider.queryRoots(null);
assertEquals(1, cursor.getCount());
cursor.moveToNext();
- assertEquals("1", cursor.getString(0));
+ assertEquals("3", cursor.getString(0));
assertEquals(Root.FLAG_SUPPORTS_IS_CHILD | Root.FLAG_SUPPORTS_CREATE, cursor.getInt(1));
assertEquals(R.drawable.ic_root_mtp, cursor.getInt(2));
assertEquals("Device B Storage B", cursor.getString(3));
- assertEquals("1", cursor.getString(4));
+ assertEquals("3", cursor.getString(4));
assertEquals(2048, cursor.getInt(5));
}
}
public void testQueryDocument() throws IOException, InterruptedException, TimeoutException {
setupProvider(MtpDatabaseConstants.FLAG_DATABASE_IN_MEMORY);
- mMtpManager.addValidDevice(0);
- mProvider.openDevice(0);
-
setupRoots(0, new MtpRoot[] { new MtpRoot(0, 0, "Device", "Storage", 1000, 1000, "") });
setupDocuments(
0,
@@ -216,12 +227,12 @@
.build()
});
- final Cursor cursor = mProvider.queryDocument("2", null);
+ final Cursor cursor = mProvider.queryDocument("3", null);
assertEquals(1, cursor.getCount());
cursor.moveToNext();
- assertEquals("2", cursor.getString(0));
+ assertEquals("3", cursor.getString(0));
assertEquals("image/jpeg", cursor.getString(1));
assertEquals("image.jpg", cursor.getString(2));
assertEquals(1422716400000L, cursor.getLong(3));
@@ -236,9 +247,6 @@
public void testQueryDocument_directory()
throws IOException, InterruptedException, TimeoutException {
setupProvider(MtpDatabaseConstants.FLAG_DATABASE_IN_MEMORY);
- mMtpManager.addValidDevice(0);
- mProvider.openDevice(0);
-
setupRoots(0, new MtpRoot[] { new MtpRoot(0, 0, "Device", "Storage", 1000, 1000, "") });
setupDocuments(
0,
@@ -255,11 +263,11 @@
.build()
});
- final Cursor cursor = mProvider.queryDocument("2", null);
+ final Cursor cursor = mProvider.queryDocument("3", null);
assertEquals(1, cursor.getCount());
cursor.moveToNext();
- assertEquals("2", cursor.getString(0));
+ assertEquals("3", cursor.getString(0));
assertEquals(DocumentsContract.Document.MIME_TYPE_DIR, cursor.getString(1));
assertEquals("directory", cursor.getString(2));
assertEquals(1422716400000L, cursor.getLong(3));
@@ -274,9 +282,6 @@
public void testQueryDocument_forRoot()
throws IOException, InterruptedException, TimeoutException {
setupProvider(MtpDatabaseConstants.FLAG_DATABASE_IN_MEMORY);
- mMtpManager.addValidDevice(0);
- mProvider.openDevice(0);
-
setupRoots(0, new MtpRoot[] {
new MtpRoot(
0 /* deviceId */,
@@ -287,11 +292,11 @@
4096 /* total space */,
"" /* no volume identifier */)
});
- final Cursor cursor = mProvider.queryDocument("1", null);
+ final Cursor cursor = mProvider.queryDocument("2", null);
assertEquals(1, cursor.getCount());
cursor.moveToNext();
- assertEquals("1", cursor.getString(0));
+ assertEquals("2", cursor.getString(0));
assertEquals(DocumentsContract.Document.MIME_TYPE_DIR, cursor.getString(1));
assertEquals("Device A Storage A", cursor.getString(2));
assertTrue(cursor.isNull(3));
@@ -301,9 +306,6 @@
public void testQueryChildDocuments() throws Exception {
setupProvider(MtpDatabaseConstants.FLAG_DATABASE_IN_MEMORY);
- mMtpManager.addValidDevice(0);
- mProvider.openDevice(0);
-
setupRoots(0, new MtpRoot[] { new MtpRoot(0, 0, "Device", "Storage", 1000, 1000, "") });
setupDocuments(
0,
@@ -325,7 +327,7 @@
assertEquals(1, cursor.getCount());
assertTrue(cursor.moveToNext());
- assertEquals("2", cursor.getString(0));
+ assertEquals("3", cursor.getString(0));
assertEquals("image/jpeg", cursor.getString(1));
assertEquals("image.jpg", cursor.getString(2));
assertEquals(0, cursor.getLong(3));
@@ -337,8 +339,6 @@
public void testQueryChildDocuments_cursorError() throws Exception {
setupProvider(MtpDatabaseConstants.FLAG_DATABASE_IN_MEMORY);
- mMtpManager.addValidDevice(0);
- mProvider.openDevice(0);
try {
mProvider.queryChildDocuments("1", null, null);
fail();
@@ -349,8 +349,6 @@
public void testQueryChildDocuments_documentError() throws Exception {
setupProvider(MtpDatabaseConstants.FLAG_DATABASE_IN_MEMORY);
- mMtpManager.addValidDevice(0);
- mProvider.openDevice(0);
setupRoots(0, new MtpRoot[] { new MtpRoot(0, 0, "Device", "Storage", 1000, 1000, "") });
mMtpManager.setObjectHandles(0, 0, -1, new int[] { 1 });
try {
@@ -363,8 +361,6 @@
public void testDeleteDocument() throws IOException, InterruptedException, TimeoutException {
setupProvider(MtpDatabaseConstants.FLAG_DATABASE_IN_MEMORY);
- mMtpManager.addValidDevice(0);
- mProvider.openDevice(0);
setupRoots(0, new MtpRoot[] {
new MtpRoot(0, 0, "Device", "Storage", 0, 0, "")
});
@@ -376,7 +372,7 @@
.build()
});
- mProvider.deleteDocument("2");
+ mProvider.deleteDocument("3");
assertEquals(1, mResolver.getChangeCount(
DocumentsContract.buildChildDocumentsUri(
MtpDocumentsProvider.AUTHORITY, "1")));
@@ -385,8 +381,6 @@
public void testDeleteDocument_error()
throws IOException, InterruptedException, TimeoutException {
setupProvider(MtpDatabaseConstants.FLAG_DATABASE_IN_MEMORY);
- mMtpManager.addValidDevice(0);
- mProvider.openDevice(0);
setupRoots(0, new MtpRoot[] {
new MtpRoot(0, 0, "Device", "Storage", 0, 0, "")
});
@@ -398,7 +392,7 @@
.build()
});
try {
- mProvider.deleteDocument("3");
+ mProvider.deleteDocument("4");
fail();
} catch (Throwable e) {
assertTrue(e instanceof IOException);
@@ -408,34 +402,6 @@
MtpDocumentsProvider.AUTHORITY, "1")));
}
- @MediumTest
- public void testPauseAndResume() throws Exception {
- setupProvider(MtpDatabaseConstants.FLAG_DATABASE_IN_FILE);
- mMtpManager.addValidDevice(0);
- mProvider.openDevice(0);
- setupRoots(0, new MtpRoot[] { new MtpRoot(0, 0, "Device", "Storage", 0, 0, "")});
-
- {
- final Cursor cursor = mProvider.queryRoots(
- strings(DocumentsContract.Root.COLUMN_ROOT_ID));
- cursor.moveToNext();
- assertEquals(1, cursor.getInt(0));
- }
-
- mProvider.shutdown();
- setupProvider(MtpDatabaseConstants.FLAG_DATABASE_IN_FILE);
-
- {
- // We can still fetch roots after relaunching the provider.
- final Cursor cursor = mProvider.queryRoots(
- strings(DocumentsContract.Root.COLUMN_ROOT_ID));
- assertEquals(1, cursor.getCount());
- cursor.moveToNext();
- assertEquals(1, cursor.getInt(0));
- assertEquals(1, mMtpManager.getOpenedDeviceIds().length);
- }
- }
-
private void setupProvider(int flag) {
mDatabase = new MtpDatabase(getContext(), flag);
mProvider = new MtpDocumentsProvider();
@@ -455,9 +421,11 @@
}
private String[] setupRoots(int deviceId, MtpRoot[] roots)
- throws FileNotFoundException, InterruptedException, TimeoutException {
+ throws InterruptedException, TimeoutException, IOException {
final int changeCount = mResolver.getChangeCount(ROOTS_URI);
- mMtpManager.setRoots(deviceId, roots);
+ mMtpManager.addValidDevice(
+ new MtpDeviceRecord(deviceId, "Device", false /* unopened */, roots));
+ mProvider.openDevice(deviceId);
mResolver.waitForNotification(ROOTS_URI, changeCount + 1);
return getStrings(mProvider.queryRoots(strings(DocumentsContract.Root.COLUMN_ROOT_ID)));
}
diff --git a/packages/MtpDocumentsProvider/tests/src/com/android/mtp/TestMtpManager.java b/packages/MtpDocumentsProvider/tests/src/com/android/mtp/TestMtpManager.java
index 3833799..bbd0a30 100644
--- a/packages/MtpDocumentsProvider/tests/src/com/android/mtp/TestMtpManager.java
+++ b/packages/MtpDocumentsProvider/tests/src/com/android/mtp/TestMtpManager.java
@@ -19,14 +19,12 @@
import android.content.Context;
import android.mtp.MtpObjectInfo;
import android.os.ParcelFileDescriptor;
+import android.util.SparseArray;
import java.io.IOException;
import java.util.Arrays;
import java.util.HashMap;
-import java.util.HashSet;
import java.util.Map;
-import java.util.Set;
-import java.util.TreeSet;
public class TestMtpManager extends MtpManager {
public static final int CREATED_DOCUMENT_HANDLE = 1000;
@@ -35,9 +33,7 @@
return Arrays.toString(args);
}
- private final Set<Integer> mValidDevices = new HashSet<>();
- private final Set<Integer> mOpenedDevices = new TreeSet<>();
- private final Map<Integer, MtpRoot[]> mRoots = new HashMap<>();
+ private final SparseArray<MtpDeviceRecord> mDevices = new SparseArray<>();
private final Map<String, MtpObjectInfo> mObjectInfos = new HashMap<>();
private final Map<String, int[]> mObjectHandles = new HashMap<>();
private final Map<String, byte[]> mThumbnailBytes = new HashMap<>();
@@ -47,18 +43,14 @@
super(context);
}
- void addValidDevice(int deviceId) {
- mValidDevices.add(deviceId);
+ void addValidDevice(MtpDeviceRecord device) {
+ mDevices.put(device.deviceId, device);
}
void setObjectHandles(int deviceId, int storageId, int parentHandle, int[] objectHandles) {
mObjectHandles.put(pack(deviceId, storageId, parentHandle), objectHandles);
}
- void setRoots(int deviceId, MtpRoot[] roots) {
- mRoots.put(deviceId, roots);
- }
-
void setObjectInfo(int deviceId, MtpObjectInfo objectInfo) {
mObjectInfos.put(pack(deviceId, objectInfo.getObjectHandle()), objectInfo);
}
@@ -76,28 +68,40 @@
}
@Override
+ MtpDeviceRecord[] getDevices() {
+ final MtpDeviceRecord[] result = new MtpDeviceRecord[mDevices.size()];
+ for (int i = 0; i < mDevices.size(); i++) {
+ final MtpDeviceRecord device = mDevices.valueAt(i);
+ if (device.opened) {
+ result[i] = device;
+ } else {
+ result[i] = new MtpDeviceRecord(
+ device.deviceId, device.name, device.opened, new MtpRoot[0]);
+ }
+ }
+ return result;
+ }
+
+ @Override
void openDevice(int deviceId) throws IOException {
- if (!mValidDevices.contains(deviceId) || mOpenedDevices.contains(deviceId)) {
+ final MtpDeviceRecord device = mDevices.get(deviceId);
+ if (device == null || device.opened) {
throw new IOException();
}
- mOpenedDevices.add(deviceId);
+ mDevices.put(
+ deviceId,
+ new MtpDeviceRecord(device.deviceId, device.name, true, device.roots));
}
@Override
void closeDevice(int deviceId) throws IOException {
- if (!mValidDevices.contains(deviceId) || !mOpenedDevices.contains(deviceId)) {
+ final MtpDeviceRecord device = mDevices.get(deviceId);
+ if (device == null || !device.opened) {
throw new IOException();
}
- mOpenedDevices.remove(deviceId);
- }
-
- @Override
- MtpRoot[] getRoots(int deviceId) throws IOException {
- if (mRoots.containsKey(deviceId)) {
- return mRoots.get(deviceId);
- } else {
- throw new IOException("getRoots error: " + Integer.toString(deviceId));
- }
+ mDevices.put(
+ deviceId,
+ new MtpDeviceRecord(device.deviceId, device.name, false, device.roots));
}
@Override
@@ -189,11 +193,14 @@
@Override
int[] getOpenedDeviceIds() {
- int i = 0;
- final int[] result = new int[mOpenedDevices.size()];
- for (int deviceId : mOpenedDevices) {
- result[i++] = deviceId;
+ final int[] result = new int[mDevices.size()];
+ int count = 0;
+ for (int i = 0; i < mDevices.size(); i++) {
+ final MtpDeviceRecord device = mDevices.valueAt(i);
+ if (device.opened) {
+ result[count++] = device.deviceId;
+ }
}
- return result;
+ return Arrays.copyOf(result, count);
}
}
diff --git a/packages/MtpDocumentsProvider/tests/src/com/android/mtp/TestUtil.java b/packages/MtpDocumentsProvider/tests/src/com/android/mtp/TestUtil.java
index f910321..611e831 100644
--- a/packages/MtpDocumentsProvider/tests/src/com/android/mtp/TestUtil.java
+++ b/packages/MtpDocumentsProvider/tests/src/com/android/mtp/TestUtil.java
@@ -120,9 +120,19 @@
private static void waitForStorages(
TestResultInstrumentation instrumentation,
MtpManager manager,
- int deviceId) throws IOException, InterruptedException {
+ int deviceId) throws InterruptedException, IOException {
while (true) {
- if (manager.getRoots(deviceId).length == 0) {
+ MtpDeviceRecord device = null;
+ for (final MtpDeviceRecord deviceCandidate : manager.getDevices()) {
+ if (deviceCandidate.deviceId == deviceId) {
+ device = deviceCandidate;
+ break;
+ }
+ }
+ if (device == null) {
+ throw new IOException("Device was detached.");
+ }
+ if (device.roots.length == 0) {
instrumentation.show("Wait for storages.");
Thread.sleep(1000);
continue;
diff --git a/packages/PrintSpooler/res/values-b+sr+Latn/strings.xml b/packages/PrintSpooler/res/values-b+sr+Latn/strings.xml
new file mode 100644
index 0000000..7484718
--- /dev/null
+++ b/packages/PrintSpooler/res/values-b+sr+Latn/strings.xml
@@ -0,0 +1,101 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="app_label" msgid="4469836075319831821">"Štamp. iz memor."</string>
+ <string name="more_options_button" msgid="2243228396432556771">"Još opcija"</string>
+ <string name="label_destination" msgid="9132510997381599275">"Odredište"</string>
+ <string name="label_copies" msgid="3634531042822968308">"Kopije"</string>
+ <string name="label_copies_summary" msgid="3861966063536529540">"Kopija:"</string>
+ <string name="label_paper_size" msgid="908654383827777759">"Veličina papira"</string>
+ <string name="label_paper_size_summary" msgid="5668204981332138168">"Veličina papira:"</string>
+ <string name="label_color" msgid="1108690305218188969">"Boja"</string>
+ <string name="label_duplex" msgid="5370037254347072243">"Dvostrano"</string>
+ <string name="label_orientation" msgid="2853142581990496477">"Položaj"</string>
+ <string name="label_pages" msgid="7768589729282182230">"Stranice"</string>
+ <string name="destination_default_text" msgid="5422708056807065710">"Izaberite štampač"</string>
+ <string name="template_all_pages" msgid="3322235982020148762">"Sve stranice (<xliff:g id="PAGE_COUNT">%1$s</xliff:g>)"</string>
+ <string name="template_page_range" msgid="428638530038286328">"Opseg od <xliff:g id="PAGE_COUNT">%1$s</xliff:g>"</string>
+ <string name="pages_range_example" msgid="8558694453556945172">"npr. 1–5, 8, 11–13"</string>
+ <string name="print_preview" msgid="8010217796057763343">"Pregled pre štampanja"</string>
+ <string name="install_for_print_preview" msgid="6366303997385509332">"Instaliraj PDF prikazivač za pregled"</string>
+ <string name="printing_app_crashed" msgid="854477616686566398">"Aplikacija za štampanje je otkazala"</string>
+ <string name="generating_print_job" msgid="3119608742651698916">"Generisanje zadatka za štampanje"</string>
+ <string name="save_as_pdf" msgid="5718454119847596853">"Sačuvaj kao PDF"</string>
+ <string name="all_printers" msgid="5018829726861876202">"Svi štampači…"</string>
+ <string name="print_dialog" msgid="32628687461331979">"Dijalog za štampanje"</string>
+ <string name="current_page_template" msgid="1386638343571771292">"<xliff:g id="CURRENT_PAGE">%1$d</xliff:g>/<xliff:g id="PAGE_COUNT">%2$d</xliff:g>"</string>
+ <string name="page_description_template" msgid="6831239682256197161">"<xliff:g id="CURRENT_PAGE">%1$d</xliff:g>. stranica od <xliff:g id="PAGE_COUNT">%2$d</xliff:g>"</string>
+ <string name="summary_template" msgid="8899734908625669193">"Rezime, kopije (<xliff:g id="COPIES">%1$s</xliff:g>), veličina papira <xliff:g id="PAPER_SIZE">%2$s</xliff:g>"</string>
+ <string name="expand_handle" msgid="7282974448109280522">"Regulator za širenje"</string>
+ <string name="collapse_handle" msgid="6886637989442507451">"Regulator za skupljanje"</string>
+ <string name="print_button" msgid="645164566271246268">"Štampaj"</string>
+ <string name="savetopdf_button" msgid="2976186791686924743">"Sačuvaj u PDF-u"</string>
+ <string name="print_options_expanded" msgid="6944679157471691859">"Opcije štampanja su proširene"</string>
+ <string name="print_options_collapsed" msgid="7455930445670414332">"Opcije štampanja su skupljene"</string>
+ <string name="search" msgid="5421724265322228497">"Pretraži"</string>
+ <string name="all_printers_label" msgid="3178848870161526399">"Svi štampači"</string>
+ <string name="add_print_service_label" msgid="5356702546188981940">"Dodaj uslugu"</string>
+ <string name="print_search_box_shown_utterance" msgid="7967404953901376090">"Okvir za pretragu je prikazan"</string>
+ <string name="print_search_box_hidden_utterance" msgid="5727755169343113351">"Okvir za pretragu je sakriven"</string>
+ <string name="print_add_printer" msgid="1088656468360653455">"Dodaj štampač"</string>
+ <string name="print_select_printer" msgid="7388760939873368698">"Izaberi štampač"</string>
+ <string name="print_forget_printer" msgid="5035287497291910766">"Zaboravi štampač"</string>
+ <plurals name="print_search_result_count_utterance" formatted="false" msgid="6997663738361080868">
+ <item quantity="one">Pronađen je <xliff:g id="COUNT_1">%1$s</xliff:g> štampač</item>
+ <item quantity="few">Pronađena su <xliff:g id="COUNT_1">%1$s</xliff:g> štampača</item>
+ <item quantity="other">Pronađeno je <xliff:g id="COUNT_1">%1$s</xliff:g> štampača</item>
+ </plurals>
+ <string name="choose_print_service" msgid="3740309762324459694">"Izaberite uslugu štampanja"</string>
+ <string name="print_searching_for_printers" msgid="6550424555079932867">"Pretraga štampača"</string>
+ <string name="print_no_print_services" msgid="8561247706423327966">"Nijedna usluga štampanja nije omogućena"</string>
+ <string name="print_no_printers" msgid="4869403323900054866">"Nije pronađen nijedan štampač"</string>
+ <string name="printing_notification_title_template" msgid="295903957762447362">"Štampa se <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
+ <string name="cancelling_notification_title_template" msgid="1821759594704703197">"Otkazuje se <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
+ <string name="failed_notification_title_template" msgid="2256217208186530973">"Greška štampača <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
+ <string name="blocked_notification_title_template" msgid="1175435827331588646">"Štampač je blokirao <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
+ <plurals name="composite_notification_title_template" formatted="false" msgid="6940956968211733780">
+ <item quantity="one">Zadaci štampanja <xliff:g id="PRINT_JOB_NAME_1">%1$d</xliff:g></item>
+ <item quantity="few">Zadaci štampanja <xliff:g id="PRINT_JOB_NAME_1">%1$d</xliff:g></item>
+ <item quantity="other">Zadaci štampanja <xliff:g id="PRINT_JOB_NAME_1">%1$d</xliff:g></item>
+ </plurals>
+ <string name="cancel" msgid="4373674107267141885">"Otkaži"</string>
+ <string name="restart" msgid="2472034227037808749">"Ponovo pokreni"</string>
+ <string name="no_connection_to_printer" msgid="2159246915977282728">"Nema veze sa štampačem"</string>
+ <string name="reason_unknown" msgid="5507940196503246139">"nepoznato"</string>
+ <string name="printer_unavailable" msgid="2434170617003315690">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> – nedostupan"</string>
+ <string name="print_service_security_warning_title" msgid="2160752291246775320">"Želite li da koristite <xliff:g id="SERVICE">%1$s</xliff:g>?"</string>
+ <string name="print_service_security_warning_summary" msgid="1427434625361692006">"Dokument može da prođe kroz jedan ili više servera na putu do štampača."</string>
+ <string-array name="color_mode_labels">
+ <item msgid="7602948745415174937">"Crno-belo"</item>
+ <item msgid="2762241247228983754">"Boja"</item>
+ </string-array>
+ <string-array name="duplex_mode_labels">
+ <item msgid="3882302912790928315">"Ništa"</item>
+ <item msgid="7296563835355641719">"Duga ivica"</item>
+ <item msgid="79513688117503758">"Kratka ivica"</item>
+ </string-array>
+ <string-array name="orientation_labels">
+ <item msgid="4061931020926489228">"Uspravno"</item>
+ <item msgid="3199660090246166812">"Vodoravno"</item>
+ </string-array>
+ <string name="print_write_error_message" msgid="5787642615179572543">"Upisivanje u datoteku nije moguće"</string>
+ <string name="print_error_default_message" msgid="8602678405502922346">"Žao nam je, ovo nije uspelo. Pokušajte ponovo."</string>
+ <string name="print_error_retry" msgid="1426421728784259538">"Pokušajte ponovo"</string>
+ <string name="print_error_printer_unavailable" msgid="8985614415253203381">"Ovaj štampač trenutno nije dostupan."</string>
+ <string name="print_preparing_preview" msgid="3939930735671364712">"Priprema pregleda..."</string>
+</resources>
diff --git a/packages/SettingsLib/res/values-af/strings.xml b/packages/SettingsLib/res/values-af/strings.xml
index 302ba04..0d9d79c 100644
--- a/packages/SettingsLib/res/values-af/strings.xml
+++ b/packages/SettingsLib/res/values-af/strings.xml
@@ -273,6 +273,8 @@
<string name="night_mode_no" msgid="9171772244775838901">"Gedeaktiveer"</string>
<string name="night_mode_yes" msgid="2218157265997633432">"Altyd aan"</string>
<string name="night_mode_auto" msgid="7508348175804304327">"Outomaties"</string>
+ <string name="select_webview_provider_title" msgid="4628592979751918907">"WebView-implementering"</string>
+ <string name="select_webview_provider_dialog_title" msgid="4370551378720004872">"Stel WebView-implementering"</string>
<string name="convert_to_file_encryption" msgid="3060156730651061223">"Skakel om na lêerenkripsie"</string>
<string name="convert_to_file_encryption_enabled" msgid="2861258671151428346">"Skakel om …"</string>
<string name="convert_to_file_encryption_done" msgid="7859766358000523953">"Lêerenkripsie is reeds uitgevoer"</string>
diff --git a/packages/SettingsLib/res/values-am/strings.xml b/packages/SettingsLib/res/values-am/strings.xml
index cd604d2..1c9b0a9 100644
--- a/packages/SettingsLib/res/values-am/strings.xml
+++ b/packages/SettingsLib/res/values-am/strings.xml
@@ -273,6 +273,8 @@
<string name="night_mode_no" msgid="9171772244775838901">"ተሰናክሏል"</string>
<string name="night_mode_yes" msgid="2218157265997633432">"ሁልጊዜ ይበራል"</string>
<string name="night_mode_auto" msgid="7508348175804304327">"ራስ-ሰር"</string>
+ <string name="select_webview_provider_title" msgid="4628592979751918907">"የWebView ትግበራ"</string>
+ <string name="select_webview_provider_dialog_title" msgid="4370551378720004872">"የWebView ትግበራን ያዘጋጁ"</string>
<string name="convert_to_file_encryption" msgid="3060156730651061223">"ወደ ፋይል ምሥጠራ ቀይር"</string>
<string name="convert_to_file_encryption_enabled" msgid="2861258671151428346">"ለውጥ…"</string>
<string name="convert_to_file_encryption_done" msgid="7859766358000523953">"ፋይል አስቀድሞ ተመስጥሯል"</string>
diff --git a/packages/SettingsLib/res/values-ar/strings.xml b/packages/SettingsLib/res/values-ar/strings.xml
index 7fcfba8..fc11a6b 100644
--- a/packages/SettingsLib/res/values-ar/strings.xml
+++ b/packages/SettingsLib/res/values-ar/strings.xml
@@ -273,6 +273,8 @@
<string name="night_mode_no" msgid="9171772244775838901">"معطَّل"</string>
<string name="night_mode_yes" msgid="2218157265997633432">"التشغيل دائمًا"</string>
<string name="night_mode_auto" msgid="7508348175804304327">"تلقائيًا"</string>
+ <string name="select_webview_provider_title" msgid="4628592979751918907">"تطبيق WebView"</string>
+ <string name="select_webview_provider_dialog_title" msgid="4370551378720004872">"تعيين تطبيق WebView"</string>
<string name="convert_to_file_encryption" msgid="3060156730651061223">"التحويل إلى تشفير ملفات"</string>
<string name="convert_to_file_encryption_enabled" msgid="2861258671151428346">"تحويل…"</string>
<string name="convert_to_file_encryption_done" msgid="7859766358000523953">"تم استخدام تشفير ملفات من قبل"</string>
diff --git a/packages/SettingsLib/res/values-az-rAZ/strings.xml b/packages/SettingsLib/res/values-az-rAZ/strings.xml
index 4c05b27..4a5ea37 100644
--- a/packages/SettingsLib/res/values-az-rAZ/strings.xml
+++ b/packages/SettingsLib/res/values-az-rAZ/strings.xml
@@ -273,6 +273,8 @@
<string name="night_mode_no" msgid="9171772244775838901">"Qeyri-aktiv"</string>
<string name="night_mode_yes" msgid="2218157265997633432">"Həmişə aktiv"</string>
<string name="night_mode_auto" msgid="7508348175804304327">"Avtomatik"</string>
+ <string name="select_webview_provider_title" msgid="4628592979751918907">"WebView icrası"</string>
+ <string name="select_webview_provider_dialog_title" msgid="4370551378720004872">"WebView icrasını ayarlayın"</string>
<string name="convert_to_file_encryption" msgid="3060156730651061223">"Fayl şifrələnməsinə çevirin"</string>
<string name="convert_to_file_encryption_enabled" msgid="2861258671151428346">"Çevirin..."</string>
<string name="convert_to_file_encryption_done" msgid="7859766358000523953">"Fayl artıq şifrələnib"</string>
diff --git a/packages/SettingsLib/res/values-b+sr+Latn/arrays.xml b/packages/SettingsLib/res/values-b+sr+Latn/arrays.xml
new file mode 100644
index 0000000..8bdec32
--- /dev/null
+++ b/packages/SettingsLib/res/values-b+sr+Latn/arrays.xml
@@ -0,0 +1,158 @@
+<?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">"Potvrđuje se autentičnost..."</item>
+ <item msgid="1943354004029184381">"Preuzimanje IP adrese..."</item>
+ <item msgid="4221763391123233270">"Povezano"</item>
+ <item msgid="624838831631122137">"Obustavljeno"</item>
+ <item msgid="7979680559596111948">"Prekidanje veze..."</item>
+ <item msgid="1634960474403853625">"Veza je prekinuta"</item>
+ <item msgid="746097431216080650">"Neuspešno"</item>
+ <item msgid="6367044185730295334">"Blokirano"</item>
+ <item msgid="503942654197908005">"Privremeno izbegavanje 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 sa mrežom <xliff:g id="NETWORK_NAME">%1$s</xliff:g>…"</item>
+ <item msgid="554971459996405634">"Proveravanje identiteta mreže <xliff:g id="NETWORK_NAME">%1$s</xliff:g>..."</item>
+ <item msgid="7928343808033020343">"Dobijanje IP adrese od mreže <xliff:g id="NETWORK_NAME">%1$s</xliff:g>…"</item>
+ <item msgid="8937994881315223448">"Povezano sa mrežom <xliff:g id="NETWORK_NAME">%1$s</xliff:g>"</item>
+ <item msgid="1330262655415760617">"Obustavljeno"</item>
+ <item msgid="7698638434317271902">"Prekidanje veze sa mrežom <xliff:g id="NETWORK_NAME">%1$s</xliff:g>…"</item>
+ <item msgid="197508606402264311">"Veza je prekinuta"</item>
+ <item msgid="8578370891960825148">"Neuspešno"</item>
+ <item msgid="5660739516542454527">"Blokirano"</item>
+ <item msgid="1805837518286731242">"Privremeno izbegavanje loše veze"</item>
+ </string-array>
+ <string-array name="hdcp_checking_titles">
+ <item msgid="441827799230089869">"Nikad ne proveravaj"</item>
+ <item msgid="6042769699089883931">"Potraži samo DRM sadržaj"</item>
+ <item msgid="9174900380056846820">"Uvek proveravaj"</item>
+ </string-array>
+ <string-array name="hdcp_checking_summaries">
+ <item msgid="505558545611516707">"Nikada ne koristi HDCP proveru"</item>
+ <item msgid="3878793616631049349">"Koristi HDCP proveru samo za DRM sadržaj"</item>
+ <item msgid="45075631231212732">"Uvek koristi HDCP proveru"</item>
+ </string-array>
+ <string-array name="select_logd_size_titles">
+ <item msgid="3100534874529240291">"64 kB"</item>
+ <item msgid="505611754508988476">"256 kB"</item>
+ <item msgid="6361286924268716397">"1 MB"</item>
+ <item msgid="6405203239560695266">"4 MB"</item>
+ <item msgid="3025431211013424097">"16 MB"</item>
+ </string-array>
+ <string-array name="select_logd_size_lowram_titles">
+ <item msgid="524799395770610154">"64 kB"</item>
+ <item msgid="3534782711045262344">"256 kB"</item>
+ <item msgid="8085867209202153403">"1 MB"</item>
+ </string-array>
+ <string-array name="select_logd_size_summaries">
+ <item msgid="7346595473588765019">"64 kB po baferu evidencije"</item>
+ <item msgid="2822309747675758628">"256 kB po baferu evidencije"</item>
+ <item msgid="6699306198357496731">"1 MB po baferu evidencije"</item>
+ <item msgid="5748528643937500349">"4 MB po baferu evidencije"</item>
+ <item msgid="1978629051085111592">"16 MB po baferu evidencije"</item>
+ </string-array>
+ <string-array name="window_animation_scale_entries">
+ <item msgid="8134156599370824081">"Animacija je isključena"</item>
+ <item msgid="6624864048416710414">"Razmera animacije 0,5x"</item>
+ <item msgid="2219332261255416635">"Razmera animacije 1x"</item>
+ <item msgid="3544428804137048509">"Razmera animacije 1,5x"</item>
+ <item msgid="3110710404225974514">"Razmera animacije 2x"</item>
+ <item msgid="4402738611528318731">"Razmera animacije 5x"</item>
+ <item msgid="6189539267968330656">"Razmera animacije 10x"</item>
+ </string-array>
+ <string-array name="transition_animation_scale_entries">
+ <item msgid="8464255836173039442">"Animacija je isključena"</item>
+ <item msgid="3375781541913316411">"Razmera animacije 0,5x"</item>
+ <item msgid="1991041427801869945">"Razmera animacije 1x"</item>
+ <item msgid="4012689927622382874">"Razmera animacije 1,5x"</item>
+ <item msgid="3289156759925947169">"Razmera animacije 2x"</item>
+ <item msgid="7705857441213621835">"Razmera animacije 5x"</item>
+ <item msgid="6660750935954853365">"Razmera animacije 10x"</item>
+ </string-array>
+ <string-array name="animator_duration_scale_entries">
+ <item msgid="6039901060648228241">"Animacija je isključena"</item>
+ <item msgid="1138649021950863198">"Razmera animacije 0,5x"</item>
+ <item msgid="4394388961370833040">"Razmera animacije 1x"</item>
+ <item msgid="8125427921655194973">"Razmera animacije 1,5x"</item>
+ <item msgid="3334024790739189573">"Razmera animacije 2x"</item>
+ <item msgid="3170120558236848008">"Razmera animacije 5x"</item>
+ <item msgid="1069584980746680398">"Razmera animacije 10x"</item>
+ </string-array>
+ <string-array name="overlay_display_devices_entries">
+ <item msgid="1606809880904982133">"Nijedno"</item>
+ <item msgid="9033194758688161545">"480 piksela"</item>
+ <item msgid="1025306206556583600">"480 piksela (bezbedno)"</item>
+ <item msgid="1853913333042744661">"720 piksela"</item>
+ <item msgid="3414540279805870511">"720 piksela (bezbedno)"</item>
+ <item msgid="9039818062847141551">"1080 piksela"</item>
+ <item msgid="4939496949750174834">"1080 piksela (bezbedno)"</item>
+ <item msgid="1833612718524903568">"4K"</item>
+ <item msgid="238303513127879234">"4K (bezbedno)"</item>
+ <item msgid="3547211260846843098">"4K (uvećana rezolucija)"</item>
+ <item msgid="5411365648951414254">"4K (uvećana rezolucija, bezbedno)"</item>
+ <item msgid="1311305077526792901">"720 piksela, 1080 piks. (2 ekrana)"</item>
+ </string-array>
+ <string-array name="enable_opengl_traces_entries">
+ <item msgid="3191973083884253830">"Nijedan"</item>
+ <item msgid="9089630089455370183">"Logcat"</item>
+ <item msgid="5397807424362304288">"Systrace (grafika)"</item>
+ <item msgid="1340692776955662664">"Grupno pozivanje funkcije glGetError"</item>
+ </string-array>
+ <string-array name="show_non_rect_clip_entries">
+ <item msgid="993742912147090253">"Isključeno"</item>
+ <item msgid="675719912558941285">"Nacrtaj oblast za isecanje koja nije pravougaonog oblika plavom bojom"</item>
+ <item msgid="1064373276095698656">"Istakni zelenom testirane komande za crtanje"</item>
+ </string-array>
+ <string-array name="track_frame_time_entries">
+ <item msgid="2193584639058893150">"Isključeno"</item>
+ <item msgid="2751513398307949636">"Na ekranu u vidu traka"</item>
+ <item msgid="1851438178120770973">"U adb shell dumpsys gfxinfo"</item>
+ </string-array>
+ <string-array name="debug_hw_overdraw_entries">
+ <item msgid="8190572633763871652">"Isključi"</item>
+ <item msgid="7688197031296835369">"Prikaži oblasti preklapanja"</item>
+ <item msgid="2290859360633824369">"Prikaži oblasti za deuteranomaliju"</item>
+ </string-array>
+ <string-array name="app_process_limit_entries">
+ <item msgid="3401625457385943795">"Standardno ograničenje"</item>
+ <item msgid="4071574792028999443">"Bez pozadinskih procesa"</item>
+ <item msgid="4810006996171705398">"Najviše jedan proces"</item>
+ <item msgid="8586370216857360863">"Najviše dva procesa"</item>
+ <item msgid="836593137872605381">"Najviše tri procesa"</item>
+ <item msgid="7899496259191969307">"Najviše četiri procesa"</item>
+ </string-array>
+ <string-array name="usb_configuration_titles">
+ <item msgid="488237561639712799">"Punjenje"</item>
+ <item msgid="5220695614993094977">"MTP (protokol za transfer medija)"</item>
+ <item msgid="2086000968159047375">"PTP (protokol za prenos slika)"</item>
+ <item msgid="7398830860950841822">"RNDIS (USB eternet)"</item>
+ <item msgid="1718924214939774352">"Izvor zvuka"</item>
+ <item msgid="8126315616613006284">"MIDI"</item>
+ </string-array>
+</resources>
diff --git a/packages/SettingsLib/res/values-b+sr+Latn/strings.xml b/packages/SettingsLib/res/values-b+sr+Latn/strings.xml
new file mode 100644
index 0000000..676c618
--- /dev/null
+++ b/packages/SettingsLib/res/values-b+sr+Latn/strings.xml
@@ -0,0 +1,294 @@
+<?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">"Nije moguće skenirati mreže"</string>
+ <string name="wifi_security_none" msgid="7985461072596594400">"Nema"</string>
+ <string name="wifi_remembered" msgid="4955746899347821096">"Sačuvano"</string>
+ <string name="wifi_disabled_generic" msgid="4259794910584943386">"Onemogućeno"</string>
+ <string name="wifi_disabled_network_failure" msgid="2364951338436007124">"IP konfiguracija je otkazala"</string>
+ <string name="wifi_disabled_wifi_failure" msgid="3081668066612876581">"Wi-Fi veza je otkazala"</string>
+ <string name="wifi_disabled_password_failure" msgid="8659805351763133575">"Problem sa potvrdom autentičnosti"</string>
+ <string name="wifi_not_in_range" msgid="1136191511238508967">"Nije u opsegu"</string>
+ <string name="wifi_no_internet" msgid="9151470775868728896">"Pristup internetu nije otkriven, automatsko povezivanje nije moguće."</string>
+ <string name="saved_network" msgid="4352716707126620811">"Sačuvao/la je <xliff:g id="NAME">%1$s</xliff:g>"</string>
+ <string name="connected_via_wfa" msgid="3805736726317410714">"Povezano preko Wi‑Fi pomoćnika"</string>
+ <string name="connected_via_passpoint" msgid="2826205693803088747">"Veza je uspostavljena preko pristupne tačke %1$s"</string>
+ <string name="available_via_passpoint" msgid="1617440946846329613">"Dostupna je preko pristupne tačke %1$s"</string>
+ <string name="wifi_connected_no_internet" msgid="3149853966840874992">"Veza je uspostavljena, nema interneta"</string>
+ <string name="bluetooth_disconnected" msgid="6557104142667339895">"Veza je prekinuta"</string>
+ <string name="bluetooth_disconnecting" msgid="8913264760027764974">"Prekidanje veze..."</string>
+ <string name="bluetooth_connecting" msgid="8555009514614320497">"Povezivanje…"</string>
+ <string name="bluetooth_connected" msgid="6038755206916626419">"Povezano"</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 je (nema 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">"Zvuk medija"</string>
+ <string name="bluetooth_profile_headset" msgid="8658779596261212609">"Zvuk telefona"</string>
+ <string name="bluetooth_profile_opp" msgid="9168139293654233697">"Prenos 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">"Deljenje kontakata"</string>
+ <string name="bluetooth_profile_pbap_summary" msgid="6605229608108852198">"Koristite za deljenje kontakata"</string>
+ <string name="bluetooth_profile_pan_nap" msgid="8429049285027482959">"Deljenje internet veze"</string>
+ <string name="bluetooth_profile_map" msgid="5465271250454324383">"Pristup porukama"</string>
+ <string name="bluetooth_profile_sap" msgid="5764222021851283125">"Pristup SIM kartici"</string>
+ <string name="bluetooth_a2dp_profile_summary_connected" msgid="963376081347721598">"Povezano sa zvukom medija"</string>
+ <string name="bluetooth_headset_profile_summary_connected" msgid="7661070206715520671">"Povezano sa zvukom telefona"</string>
+ <string name="bluetooth_opp_profile_summary_connected" msgid="2611913495968309066">"Povezano sa serverom za prenos datoteka"</string>
+ <string name="bluetooth_map_profile_summary_connected" msgid="8191407438851351713">"Povezano je sa mapom"</string>
+ <string name="bluetooth_sap_profile_summary_connected" msgid="8561765057453083838">"Veza sa tačkom pristupa uslugama je uspostavljena"</string>
+ <string name="bluetooth_opp_profile_summary_not_connected" msgid="1267091356089086285">"Nije povezano sa serverom za prenos datoteka"</string>
+ <string name="bluetooth_hid_profile_summary_connected" msgid="3381760054215168689">"Povezan sa ulaznim uređajem"</string>
+ <string name="bluetooth_pan_user_profile_summary_connected" msgid="4602294638909590612">"Povez. sa uređ. radi pristupa Internetu"</string>
+ <string name="bluetooth_pan_nap_profile_summary_connected" msgid="1561383706411975199">"Lokalna internet veza se deli sa uređajem"</string>
+ <string name="bluetooth_pan_profile_summary_use_for" msgid="5664884523822068653">"Koristi za pristup Internetu"</string>
+ <string name="bluetooth_map_profile_summary_use_for" msgid="5154200119919927434">"Koristi se za mapu"</string>
+ <string name="bluetooth_sap_profile_summary_use_for" msgid="7085362712786907993">"Koristi za pristup SIM kartici"</string>
+ <string name="bluetooth_a2dp_profile_summary_use_for" msgid="4630849022250168427">"Korišćenje za zvuk medija"</string>
+ <string name="bluetooth_headset_profile_summary_use_for" msgid="8705753622443862627">"Korišćenje za audio telefona"</string>
+ <string name="bluetooth_opp_profile_summary_use_for" msgid="1255674547144769756">"Korišćenje za prenos datoteka"</string>
+ <string name="bluetooth_hid_profile_summary_use_for" msgid="232727040453645139">"Koristi 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">"Otkaži"</string>
+ <string name="bluetooth_pairing_will_share_phonebook" msgid="4982239145676394429">"Uparivanje omogućava pristup kontaktima i istoriji poziva nakon povezivanja."</string>
+ <string name="bluetooth_pairing_error_message" msgid="3748157733635947087">"Uparivanje sa uređajem <xliff:g id="DEVICE_NAME">%1$s</xliff:g> nije moguće."</string>
+ <string name="bluetooth_pairing_pin_error_message" msgid="8337234855188925274">"Uparivanje sa uređajem <xliff:g id="DEVICE_NAME">%1$s</xliff:g> nije moguće zbog netačnog PIN-a ili pristupnog koda."</string>
+ <string name="bluetooth_pairing_device_down_error_message" msgid="7870998403045801381">"Nije moguće komunicirati sa uređajem <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> je odbio/la uparivanje"</string>
+ <string name="accessibility_wifi_off" msgid="1166761729660614716">"Wi-Fi je isključen."</string>
+ <string name="accessibility_no_wifi" msgid="8834610636137374508">"Wi-Fi veza je prekinuta."</string>
+ <string name="accessibility_wifi_one_bar" msgid="4869376278894301820">"Wi-Fi signal ima jednu crtu."</string>
+ <string name="accessibility_wifi_two_bars" msgid="3569851234710034416">"Wi-Fi signal ima dve crte."</string>
+ <string name="accessibility_wifi_three_bars" msgid="8134185644861380311">"Wi-Fi signal ima tri crte."</string>
+ <string name="accessibility_wifi_signal_full" msgid="7061045677694702">"Wi-Fi signal je najjači."</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 Internet povezivanje"</string>
+ <string name="tether_settings_title_wifi" msgid="3277144155960302049">"Prenosni hotspot"</string>
+ <string name="tether_settings_title_bluetooth" msgid="355855408317564420">"Bluetooth privezivanje"</string>
+ <string name="tether_settings_title_usb_bluetooth" msgid="5355828977109785001">"Povezivanje sa internetom"</string>
+ <string name="tether_settings_title_all" msgid="8356136101061143841">"Povezivanje i prenosni hotspot"</string>
+ <string name="managed_user_title" msgid="8101244883654409696">"Profil za posao"</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">"Podešene su neke podrazumevane vrednosti"</string>
+ <string name="launch_defaults_none" msgid="4241129108140034876">"Nisu podešene podrazumevane vrednosti"</string>
+ <string name="tts_settings" msgid="8186971894801348327">"Podešavanja prelaska iz teksta u govor"</string>
+ <string name="tts_settings_title" msgid="1237820681016639683">"Izlaz za 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 izgovaranja teksta"</string>
+ <string name="tts_default_lang_title" msgid="8018087612299820556">"Jezik"</string>
+ <string name="tts_lang_use_system" msgid="2679252467416513208">"Koristi jezik sistema"</string>
+ <string name="tts_lang_not_selected" msgid="7395787019276734765">"Jezik nije izabran"</string>
+ <string name="tts_default_lang_summary" msgid="5219362163902707785">"Podešava glas specifičan za jezik namenjen govornom tekstu"</string>
+ <string name="tts_play_example_title" msgid="7094780383253097230">"Poslušaj primer"</string>
+ <string name="tts_play_example_summary" msgid="8029071615047894486">"Puštanje kratke demonstracije sinteze govora"</string>
+ <string name="tts_install_data_title" msgid="4264378440508149986">"Instaliraj glasovne podatke"</string>
+ <string name="tts_install_data_summary" msgid="5742135732511822589">"Instaliranje govornih podataka potrebnih za sintezu govora"</string>
+ <string name="tts_engine_security_warning" msgid="8786238102020223650">"Ova tehnologija za sintezu govora možda može da prikuplja sav tekst koji će biti izgovoren, uključujući lične podatke kao što su lozinke i brojevi kreditnih kartica. To potiče iz tehnologije <xliff:g id="TTS_PLUGIN_ENGINE_NAME">%s</xliff:g>. Želite li da omogućite korišćenje ove tehnologije za sintezu govora?"</string>
+ <string name="tts_engine_network_required" msgid="1190837151485314743">"Za ovaj jezik je potrebna ispravna mrežna veza za pretvaranje teksta u govor."</string>
+ <string name="tts_default_sample_string" msgid="4040835213373086322">"Ovo je primer sinteze govora"</string>
+ <string name="tts_status_title" msgid="7268566550242584413">"Status podrazumevanog jezika"</string>
+ <string name="tts_status_ok" msgid="1309762510278029765">"<xliff:g id="LOCALE">%1$s</xliff:g> je podržan u potpunosti"</string>
+ <string name="tts_status_requires_network" msgid="6042500821503226892">"<xliff:g id="LOCALE">%1$s</xliff:g> zahteva vezu sa mrežom"</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">"Proverava se..."</string>
+ <string name="tts_engine_settings_title" msgid="3499112142425680334">"Podešavanja za <xliff:g id="TTS_ENGINE_NAME">%s</xliff:g>"</string>
+ <string name="tts_engine_settings_button" msgid="1030512042040722285">"Pokreni podešavanja mašine"</string>
+ <string name="tts_engine_preference_section_title" msgid="448294500990971413">"Željena mašina"</string>
+ <string name="tts_general_section_title" msgid="4402572014604490502">"Opšte"</string>
+ <string-array name="tts_rate_entries">
+ <item msgid="6695494874362656215">"Veoma sporo"</item>
+ <item msgid="4795095314303559268">"Sporo"</item>
+ <item msgid="8903157781070679765">"Normalno"</item>
+ <item msgid="164347302621392996">"Brzo"</item>
+ <item msgid="5794028588101562009">"Brže"</item>
+ <item msgid="7163942783888652942">"Veoma brzo"</item>
+ <item msgid="7831712693748700507">"Ubrzano"</item>
+ <item msgid="5194774745031751806">"Veoma ubrzano"</item>
+ <item msgid="9085102246155045744">"Najbrže"</item>
+ </string-array>
+ <string name="choose_profile" msgid="8229363046053568878">"Izaberite profil"</string>
+ <string name="category_personal" msgid="1299663247844969448">"Lično"</string>
+ <string name="category_work" msgid="8699184680584175622">"Posao"</string>
+ <string name="development_settings_title" msgid="215179176067683667">"Opcije za programera"</string>
+ <string name="development_settings_enable" msgid="542530994778109538">"Omogući opcije za programera"</string>
+ <string name="development_settings_summary" msgid="1815795401632854041">"Podešavanje opcija za programiranje aplikacije"</string>
+ <string name="development_settings_not_available" msgid="4308569041701535607">"Opcije za programere nisu dostupne za ovog korisnika"</string>
+ <string name="vpn_settings_not_available" msgid="956841430176985598">"Podešavanja VPN-a nisu dostupna za ovog korisnika"</string>
+ <string name="tethering_settings_not_available" msgid="6765770438438291012">"Podešavanja privezivanja nisu dostupna za ovog korisnika"</string>
+ <string name="apn_settings_not_available" msgid="7873729032165324000">"Podešavanja naziva pristupne tačke nisu dostupna za ovog korisnika"</string>
+ <string name="enable_adb" msgid="7982306934419797485">"Otklanjanje USB grešaka"</string>
+ <string name="enable_adb_summary" msgid="4881186971746056635">"Režim otklanjanja grešaka kada je USB povezan"</string>
+ <string name="clear_adb_keys" msgid="4038889221503122743">"Opozivanje odobrenja za uklanjanje USB grešaka"</string>
+ <string name="bugreport_in_power" msgid="7923901846375587241">"Prečica za izveštaj o greškama"</string>
+ <string name="bugreport_in_power_summary" msgid="1778455732762984579">"Prikaži dugme u meniju napajanja za pravljenje izveštaja o greškama"</string>
+ <string name="keep_screen_on" msgid="1146389631208760344">"Ne zaključavaj"</string>
+ <string name="keep_screen_on_summary" msgid="2173114350754293009">"Ekran neće biti u režimu spavanja tokom punjenja"</string>
+ <string name="bt_hci_snoop_log" msgid="3340699311158865670">"Omogući snoop evidenciju za Bluetooth HCI"</string>
+ <string name="bt_hci_snoop_log_summary" msgid="730247028210113851">"Snimi sve Bluetooth HCI pakete u datoteci"</string>
+ <string name="oem_unlock_enable" msgid="6040763321967327691">"Otključavanje OEM-a"</string>
+ <string name="oem_unlock_enable_summary" msgid="4720281828891618376">"Dozvoli otključavanje funkcije za pokretanje"</string>
+ <string name="confirm_enable_oem_unlock_title" msgid="4802157344812385674">"Želite li da dozvolite otključavanje proizvođača originalne opreme (OEM)?"</string>
+ <string name="confirm_enable_oem_unlock_text" msgid="5517144575601647022">"UPOZORENJE: Funkcije za zaštitu uređaja neće funkcionisati na ovom uređaju dok je ovo podešavanje uključeno."</string>
+ <string name="mock_location_app" msgid="7966220972812881854">"Izaberi aplikaciju za lažnu lokaciju"</string>
+ <string name="mock_location_app_not_set" msgid="809543285495344223">"Aplikacija za lažnu lokaciju nije podešena"</string>
+ <string name="mock_location_app_set" msgid="8966420655295102685">"Aplikacija za lažnu lokaciju: <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
+ <string name="debug_networking_category" msgid="7044075693643009662">"Umrežavanje"</string>
+ <string name="wifi_display_certification" msgid="8611569543791307533">"Sertifikacija bežičnog ekrana"</string>
+ <string name="wifi_verbose_logging" msgid="4203729756047242344">"Omogući detaljniju evidenciju za Wi‑Fi"</string>
+ <string name="wifi_aggressive_handover" msgid="9194078645887480917">"Agresivan prelaz sa Wi‑Fi mreže na mobilnu"</string>
+ <string name="wifi_allow_scan_with_traffic" msgid="3601853081178265786">"Uvek dozvoli skeniranje Wi‑Fi-ja u romingu"</string>
+ <string name="legacy_dhcp_client" msgid="694426978909127287">"Koristi zastareli DHCP klijent"</string>
+ <string name="mobile_data_always_on" msgid="7745605759775320362">"Podaci za mobilne uređaje su uvek aktivni"</string>
+ <string name="wifi_display_certification_summary" msgid="1155182309166746973">"Prikaz opcija za sertifikaciju bežičnog ekrana"</string>
+ <string name="wifi_verbose_logging_summary" msgid="6615071616111731958">"Povećava nivo evidentiranja za Wi‑Fi. Prikaz po SSID RSSI-u u biraču Wi‑Fi mreže"</string>
+ <string name="wifi_aggressive_handover_summary" msgid="6328455667642570371">"Kada se omogući, Wi‑Fi će biti agresivniji pri prebacivanju mreže za prenos podataka na Mobilnu, kada je Wi‑Fi signal slab"</string>
+ <string name="wifi_allow_scan_with_traffic_summary" msgid="2575101424972686310">"Dozvoli/zabrani skeniranje Wi-Fi-ja u romingu na osnovu prisutnog protoka podataka na interfejsu"</string>
+ <string name="select_logd_size_title" msgid="7433137108348553508">"Veličine bafera podataka u programu za evidentiranje"</string>
+ <string name="select_logd_size_dialog_title" msgid="1206769310236476760">"Izaberite veličine po baferu evidencije"</string>
+ <string name="select_usb_configuration_title" msgid="2649938511506971843">"Izaberite konfiguraciju USB-a"</string>
+ <string name="select_usb_configuration_dialog_title" msgid="6385564442851599963">"Izaberite konfiguraciju USB-a"</string>
+ <string name="allow_mock_location" msgid="2787962564578664888">"Dozvoli lažne lokacije"</string>
+ <string name="allow_mock_location_summary" msgid="317615105156345626">"Dozvoli lažne lokacije"</string>
+ <string name="debug_view_attributes" msgid="6485448367803310384">"Omogući proveru atributa za pregled"</string>
+ <string name="legacy_dhcp_client_summary" msgid="163383566317652040">"Koristite DHCP klijent iz Lollipop-a umesto novog Android DHCP klijenta."</string>
+ <string name="mobile_data_always_on_summary" msgid="8149773901431697910">"Neka podaci za mobilne uređaje uvek budu aktivni, čak i kada je Wi‑Fi aktivan (radi brze promene mreže)."</string>
+ <string name="adb_warning_title" msgid="6234463310896563253">"Dozvoli otklanjanje USB grešaka?"</string>
+ <string name="adb_warning_message" msgid="7316799925425402244">"Otklanjanje USB grešaka namenjeno je samo za svrhe programiranja. Koristite ga za kopiranje podataka sa računara na uređaj i obrnuto, instaliranje aplikacija na uređaju bez obaveštenja i čitanje podataka iz evidencije."</string>
+ <string name="adb_keys_warning_message" msgid="5659849457135841625">"Želite li da opozovete pristup otklanjanju USB grešaka sa svih računara koje ste prethodno odobrili?"</string>
+ <string name="dev_settings_warning_title" msgid="7244607768088540165">"Želite li da omogućite programerska podešavanja?"</string>
+ <string name="dev_settings_warning_message" msgid="2298337781139097964">"Ova podešavanja su namenjena samo za programiranje. Mogu da izazovu prestanak funkcionisanja ili neočekivano ponašanje uređaja i aplikacija na njemu."</string>
+ <string name="verify_apps_over_usb_title" msgid="4177086489869041953">"Verifikuj aplikacije preko USB-a"</string>
+ <string name="verify_apps_over_usb_summary" msgid="9164096969924529200">"Proverava da li su aplikacije instalirane preko ADB-a/ADT-a štetne."</string>
+ <string name="enable_terminal_title" msgid="95572094356054120">"Lokalni terminal"</string>
+ <string name="enable_terminal_summary" msgid="67667852659359206">"Omogući aplik. terminala za pristup lokalnom komandnom okruženju"</string>
+ <string name="hdcp_checking_title" msgid="8605478913544273282">"HDCP provera"</string>
+ <string name="hdcp_checking_dialog_title" msgid="5141305530923283">"Podešavanje ponašanja HDCP provere"</string>
+ <string name="debug_debugging_category" msgid="6781250159513471316">"Otklanjanje grešaka"</string>
+ <string name="debug_app" msgid="8349591734751384446">"Izaberite aplikaciju za otklanjanje grešaka"</string>
+ <string name="debug_app_not_set" msgid="718752499586403499">"Nema podešenih aplikacija za otklanjanje grešaka"</string>
+ <string name="debug_app_set" msgid="2063077997870280017">"Aplikacija za otklanjanje grešaka: <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
+ <string name="select_application" msgid="5156029161289091703">"Biranje aplikacije"</string>
+ <string name="no_application" msgid="2813387563129153880">"Nijedna"</string>
+ <string name="wait_for_debugger" msgid="1202370874528893091">"Sačekaj program za otklanjanje grešaka"</string>
+ <string name="wait_for_debugger_summary" msgid="1766918303462746804">"Aplikacija čeka program za otklanjanje grešaka da priloži pre izvršavanja"</string>
+ <string name="debug_input_category" msgid="1811069939601180246">"Unos"</string>
+ <string name="debug_drawing_category" msgid="6755716469267367852">"Crtanje"</string>
+ <string name="debug_hw_drawing_category" msgid="6220174216912308658">"Hardverski ubrzano prikazivanje"</string>
+ <string name="media_category" msgid="4388305075496848353">"Mediji"</string>
+ <string name="debug_monitoring_category" msgid="7640508148375798343">"Nadgledanje"</string>
+ <string name="strict_mode" msgid="1938795874357830695">"Omogućen je strogi režim"</string>
+ <string name="strict_mode_summary" msgid="142834318897332338">"Neka ekran treperi kada aplikacije obavljaju duge operacije na glavnoj niti"</string>
+ <string name="pointer_location" msgid="6084434787496938001">"Lokacija pokazivača"</string>
+ <string name="pointer_location_summary" msgid="840819275172753713">"Postav. element sa trenutnim podacima o dodiru"</string>
+ <string name="show_touches" msgid="1356420386500834339">"Prikaži dodire"</string>
+ <string name="show_touches_summary" msgid="6684407913145150041">"Prikaži vizuelne povratne informacije za dodire"</string>
+ <string name="show_screen_updates" msgid="5470814345876056420">"Prikaži ažuriranja površine"</string>
+ <string name="show_screen_updates_summary" msgid="2569622766672785529">"Osvetli sve površine prozora kada se ažuriraju"</string>
+ <string name="show_hw_screen_updates" msgid="5036904558145941590">"Prikaži ažur. GPU prikaza"</string>
+ <string name="show_hw_screen_updates_summary" msgid="1115593565980196197">"Osvetli prikaze u prozor. kada se crta sa GPU-om"</string>
+ <string name="show_hw_layers_updates" msgid="5645728765605699821">"Prikaži ažuriranja hardverskih slojeva"</string>
+ <string name="show_hw_layers_updates_summary" msgid="5296917233236661465">"Hardverski slojevi trepere zeleno kada se ažuriraju"</string>
+ <string name="debug_hw_overdraw" msgid="2968692419951565417">"Otkloni greške GPU preklapanja"</string>
+ <string name="disable_overlays" msgid="2074488440505934665">"Onemog. HW post. elemente"</string>
+ <string name="disable_overlays_summary" msgid="3578941133710758592">"Uvek koristi GPU za komponovanje ekrana"</string>
+ <string name="simulate_color_space" msgid="6745847141353345872">"Simuliraj prostor boje"</string>
+ <string name="enable_opengl_traces_title" msgid="6790444011053219871">"Omogući OpenGL tragove"</string>
+ <string name="usb_audio_disable_routing" msgid="8114498436003102671">"Onemogući USB preusm. zvuka"</string>
+ <string name="usb_audio_disable_routing_summary" msgid="980282760277312264">"Onemogući automat. preusmer. na USB audio periferne uređaje"</string>
+ <string name="debug_layout" msgid="5981361776594526155">"Prikaži granice rasporeda"</string>
+ <string name="debug_layout_summary" msgid="2001775315258637682">"Prikaži granice klipa, margine itd."</string>
+ <string name="force_rtl_layout_all_locales" msgid="2259906643093138978">"Nametni smer rasporeda zdesna nalevo"</string>
+ <string name="force_rtl_layout_all_locales_summary" msgid="9192797796616132534">"Nametni smer rasporeda ekrana zdesna nalevo za sve lokalitete"</string>
+ <string name="show_cpu_usage" msgid="2389212910758076024">"Prik. upotrebu procesora"</string>
+ <string name="show_cpu_usage_summary" msgid="2113341923988958266">"Postav. element sa trenutnom upotrebom procesora"</string>
+ <string name="force_hw_ui" msgid="6426383462520888732">"Prinudni prikaz pom. GPU"</string>
+ <string name="force_hw_ui_summary" msgid="5535991166074861515">"Prinudno koristi GPU za 2D crtanje"</string>
+ <string name="force_msaa" msgid="7920323238677284387">"Nametni 4x MSAA"</string>
+ <string name="force_msaa_summary" msgid="9123553203895817537">"Omogući 4x MSAA u OpenGL ES 2.0 aplikacijama"</string>
+ <string name="show_non_rect_clip" msgid="505954950474595172">"Otkloni greške u vezi sa radnjama za isecanje oblasti koje nisu pravougaonog oblika"</string>
+ <string name="track_frame_time" msgid="6146354853663863443">"Prikaži profil pomoću GPU"</string>
+ <string name="window_animation_scale_title" msgid="6162587588166114700">"Razmera animacije prozora"</string>
+ <string name="transition_animation_scale_title" msgid="387527540523595875">"Razmera animacije prelaza"</string>
+ <string name="animator_duration_scale_title" msgid="3406722410819934083">"Animatorova razmera trajanja"</string>
+ <string name="overlay_display_devices_title" msgid="5364176287998398539">"Simuliraj sekundarne ekrane"</string>
+ <string name="debug_applications_category" msgid="4206913653849771549">"Aplikacije"</string>
+ <string name="immediately_destroy_activities" msgid="1579659389568133959">"Ne čuvaj aktivnosti"</string>
+ <string name="immediately_destroy_activities_summary" msgid="3592221124808773368">"Uništi svaku aktivnost čim je korisnik napusti"</string>
+ <string name="app_process_limit_title" msgid="4280600650253107163">"Ograničenje pozadinskih procesa"</string>
+ <string name="show_all_anrs" msgid="28462979638729082">"Prikaži sve ANR-ove"</string>
+ <string name="show_all_anrs_summary" msgid="641908614413544127">"Prikaži dijalog Aplikacija ne reaguje za aplikacije u pozadini"</string>
+ <string name="force_allow_on_external" msgid="3215759785081916381">"Prinudno dozvoli aplikacije u spoljnoj"</string>
+ <string name="force_allow_on_external_summary" msgid="3191952505860343233">"Omogućava upisivanje svih aplikacija u spoljnu memoriju, bez obzira na vrednosti manifesta"</string>
+ <string name="force_resizable_activities" msgid="8615764378147824985">"Prinudno omogući promenu veličine aktivnosti"</string>
+ <string name="force_resizable_activities_summary" msgid="4508217476997182216">"Omogućava promenu veličine svih aktivnosti za režim sa više prozora, bez obzira na vrednosti manifesta."</string>
+ <string name="local_backup_password_title" msgid="3860471654439418822">"Lozinka rezervne kopije za računar"</string>
+ <string name="local_backup_password_summary_none" msgid="6951095485537767956">"Rezervne kopije čitavog sistema trenutno nisu zaštićene"</string>
+ <string name="local_backup_password_summary_change" msgid="2731163425081172638">"Dodirnite da biste promenili ili uklonili lozinku za pravljenje rezervnih kopija čitavog sistema na računaru"</string>
+ <string name="local_backup_password_toast_success" msgid="582016086228434290">"Postavljena je nova lozinka rezervne kopije"</string>
+ <string name="local_backup_password_toast_confirmation_mismatch" msgid="7805892532752708288">"Nova lozinka i njena potvrda se ne podudaraju"</string>
+ <string name="local_backup_password_toast_validation_failure" msgid="5646377234895626531">"Postavljanje lozinke rezervne kopije nije uspelo"</string>
+ <string-array name="color_mode_names">
+ <item msgid="2425514299220523812">"Živopisan (podrazumevano)"</item>
+ <item msgid="8446070607501413455">"Prirodan"</item>
+ <item msgid="6553408765810699025">"Standardan"</item>
+ </string-array>
+ <string-array name="color_mode_descriptions">
+ <item msgid="4979629397075120893">"Poboljšane boje"</item>
+ <item msgid="8280754435979370728">"Prirodne boje nalik onima koje registruje oko"</item>
+ <item msgid="5363960654009010371">"Boje optimizovane za digitalni sadržaj"</item>
+ </string-array>
+ <string name="inactive_apps_title" msgid="1317817863508274533">"Neaktivne aplikacije"</string>
+ <string name="inactive_app_inactive_summary" msgid="6768756967594202411">"Neaktivna. Dodirnite da biste je aktivirali."</string>
+ <string name="inactive_app_active_summary" msgid="4512911571954375968">"Aktivna. Dodirnite da biste je deaktivirali."</string>
+ <string name="runningservices_settings_title" msgid="8097287939865165213">"Pokrenute usluge"</string>
+ <string name="runningservices_settings_summary" msgid="854608995821032748">"Prikaz i kontrola trenutno pokrenutih usluga"</string>
+ <string name="night_mode_title" msgid="2594133148531256513">"Noćni režim"</string>
+ <string name="night_mode_summary" msgid="9196605054622017193">"%s"</string>
+ <string name="night_mode_no" msgid="9171772244775838901">"Onemogućeno"</string>
+ <string name="night_mode_yes" msgid="2218157265997633432">"Uvek uključeno"</string>
+ <string name="night_mode_auto" msgid="7508348175804304327">"Automatski"</string>
+ <string name="select_webview_provider_title" msgid="4628592979751918907">"Primena WebView-a"</string>
+ <string name="select_webview_provider_dialog_title" msgid="4370551378720004872">"Podesite primenu WebView-a"</string>
+ <string name="convert_to_file_encryption" msgid="3060156730651061223">"Konvertuj u šifrovanje datoteka"</string>
+ <string name="convert_to_file_encryption_enabled" msgid="2861258671151428346">"Konvertuj..."</string>
+ <string name="convert_to_file_encryption_done" msgid="7859766358000523953">"Već se koristi šifrovanje datoteka"</string>
+ <string name="title_convert_fbe" msgid="1263622876196444453">"Konvertovanje u šifrovanje pojedinačnih datoteka"</string>
+ <string name="convert_to_fbe_warning" msgid="6139067817148865527">"Konvertujte šifrovanje particije podataka u šifrovanje pojedinačnih datoteka.\n !!Upozorenje!! Time brišete sve podatke.\n Ovo je alfa verzija funkcije i verovatno neće funkcionisati ispravno.\n Pritisnite „Izbriši i konvertuj...“ da biste nastavili."</string>
+ <string name="button_convert_fbe" msgid="5152671181309826405">"Izbriši i konvertuj..."</string>
+ <string name="picture_color_mode" msgid="4560755008730283695">"Režim boja slika"</string>
+ <string name="picture_color_mode_desc" msgid="1141891467675548590">"Koristi sRGB"</string>
+ <string name="daltonizer_mode_disabled" msgid="7482661936053801862">"Onemogućeno je"</string>
+ <string name="daltonizer_mode_monochromacy" msgid="8485709880666106721">"Jednobojnost"</string>
+ <string name="daltonizer_mode_deuteranomaly" msgid="5475532989673586329">"Deuteranomalija (crveno-zeleno)"</string>
+ <string name="daltonizer_mode_protanomaly" msgid="8424148009038666065">"Protanomalija (crveno-zeleno)"</string>
+ <string name="daltonizer_mode_tritanomaly" msgid="481725854987912389">"Tritanomalija (plavo-žuto)"</string>
+ <string name="accessibility_display_daltonizer_preference_title" msgid="5800761362678707872">"Korekcija boja"</string>
+ <string name="accessibility_display_daltonizer_preference_subtitle" msgid="3484969015295282911">"Ova funkcija je eksperimentalna i može da utiče na performanse."</string>
+ <string name="daltonizer_type_overridden" msgid="3116947244410245916">"Zamenjuje ga <xliff:g id="TITLE">%1$s</xliff:g>"</string>
+</resources>
diff --git a/packages/SettingsLib/res/values-bg/strings.xml b/packages/SettingsLib/res/values-bg/strings.xml
index 105e533..8180261 100644
--- a/packages/SettingsLib/res/values-bg/strings.xml
+++ b/packages/SettingsLib/res/values-bg/strings.xml
@@ -273,6 +273,8 @@
<string name="night_mode_no" msgid="9171772244775838901">"Деактивирано"</string>
<string name="night_mode_yes" msgid="2218157265997633432">"Винаги включено"</string>
<string name="night_mode_auto" msgid="7508348175804304327">"Автоматично"</string>
+ <string name="select_webview_provider_title" msgid="4628592979751918907">"Внедряване на WebView"</string>
+ <string name="select_webview_provider_dialog_title" msgid="4370551378720004872">"Задаване на внедряването на WebView"</string>
<string name="convert_to_file_encryption" msgid="3060156730651061223">"Преобразуване към шифроване на ниво файл"</string>
<string name="convert_to_file_encryption_enabled" msgid="2861258671151428346">"Преобразуване…"</string>
<string name="convert_to_file_encryption_done" msgid="7859766358000523953">"Данните вече са шифровани на ниво файл"</string>
diff --git a/packages/SettingsLib/res/values-bn-rBD/strings.xml b/packages/SettingsLib/res/values-bn-rBD/strings.xml
index 5c83183..0e77871 100644
--- a/packages/SettingsLib/res/values-bn-rBD/strings.xml
+++ b/packages/SettingsLib/res/values-bn-rBD/strings.xml
@@ -273,6 +273,8 @@
<string name="night_mode_no" msgid="9171772244775838901">"অক্ষম করা রয়েছে"</string>
<string name="night_mode_yes" msgid="2218157265997633432">"সবসময় চালু"</string>
<string name="night_mode_auto" msgid="7508348175804304327">"স্বয়ংক্রিয়"</string>
+ <string name="select_webview_provider_title" msgid="4628592979751918907">"ওয়েবদর্শনের বাস্তবায়ন"</string>
+ <string name="select_webview_provider_dialog_title" msgid="4370551378720004872">"ওয়েবদর্শনের বাস্তবায়ন সেট করুন"</string>
<string name="convert_to_file_encryption" msgid="3060156730651061223">"ফাইল এনক্রিপশান রূপান্তর করুন"</string>
<string name="convert_to_file_encryption_enabled" msgid="2861258671151428346">"রূপান্তর করুন..."</string>
<string name="convert_to_file_encryption_done" msgid="7859766358000523953">"ফাইল ইতিমধ্যেই এনক্রিপ্ট করা রয়েছে"</string>
diff --git a/packages/SettingsLib/res/values-ca/strings.xml b/packages/SettingsLib/res/values-ca/strings.xml
index 71a331d2..8553ed9 100644
--- a/packages/SettingsLib/res/values-ca/strings.xml
+++ b/packages/SettingsLib/res/values-ca/strings.xml
@@ -273,6 +273,8 @@
<string name="night_mode_no" msgid="9171772244775838901">"Desactivat"</string>
<string name="night_mode_yes" msgid="2218157265997633432">"Sempre activat"</string>
<string name="night_mode_auto" msgid="7508348175804304327">"Automàtic"</string>
+ <string name="select_webview_provider_title" msgid="4628592979751918907">"Implementació de WebView"</string>
+ <string name="select_webview_provider_dialog_title" msgid="4370551378720004872">"Configura la implementació de WebView"</string>
<string name="convert_to_file_encryption" msgid="3060156730651061223">"Converteix en l\'encriptació de fitxers"</string>
<string name="convert_to_file_encryption_enabled" msgid="2861258671151428346">"Converteix…"</string>
<string name="convert_to_file_encryption_done" msgid="7859766358000523953">"El fitxer ja està encriptat"</string>
diff --git a/packages/SettingsLib/res/values-cs/strings.xml b/packages/SettingsLib/res/values-cs/strings.xml
index 8ec5e25..495f10f 100644
--- a/packages/SettingsLib/res/values-cs/strings.xml
+++ b/packages/SettingsLib/res/values-cs/strings.xml
@@ -273,6 +273,8 @@
<string name="night_mode_no" msgid="9171772244775838901">"Vypnuto"</string>
<string name="night_mode_yes" msgid="2218157265997633432">"Vždy zapnuto"</string>
<string name="night_mode_auto" msgid="7508348175804304327">"Automatický"</string>
+ <string name="select_webview_provider_title" msgid="4628592979751918907">"Implementace WebView"</string>
+ <string name="select_webview_provider_dialog_title" msgid="4370551378720004872">"Nastavte implementaci WebView"</string>
<string name="convert_to_file_encryption" msgid="3060156730651061223">"Převést na šifrování souborů"</string>
<string name="convert_to_file_encryption_enabled" msgid="2861258671151428346">"Převést…"</string>
<string name="convert_to_file_encryption_done" msgid="7859766358000523953">"Obsah je již na úrovni souborů zašifrován"</string>
diff --git a/packages/SettingsLib/res/values-da/strings.xml b/packages/SettingsLib/res/values-da/strings.xml
index 747bd14..82654bc 100644
--- a/packages/SettingsLib/res/values-da/strings.xml
+++ b/packages/SettingsLib/res/values-da/strings.xml
@@ -273,6 +273,8 @@
<string name="night_mode_no" msgid="9171772244775838901">"Deaktiveret"</string>
<string name="night_mode_yes" msgid="2218157265997633432">"Altid slået til"</string>
<string name="night_mode_auto" msgid="7508348175804304327">"Automatisk"</string>
+ <string name="select_webview_provider_title" msgid="4628592979751918907">"WebView-implementering"</string>
+ <string name="select_webview_provider_dialog_title" msgid="4370551378720004872">"Konfigurer WebView-implementering"</string>
<string name="convert_to_file_encryption" msgid="3060156730651061223">"Konvertér til filkryptering"</string>
<string name="convert_to_file_encryption_enabled" msgid="2861258671151428346">"Konvertér…"</string>
<string name="convert_to_file_encryption_done" msgid="7859766358000523953">"Allerede filkrypteret"</string>
diff --git a/packages/SettingsLib/res/values-de/strings.xml b/packages/SettingsLib/res/values-de/strings.xml
index 9b60b20..a2f750b 100644
--- a/packages/SettingsLib/res/values-de/strings.xml
+++ b/packages/SettingsLib/res/values-de/strings.xml
@@ -273,6 +273,8 @@
<string name="night_mode_no" msgid="9171772244775838901">"Deaktiviert"</string>
<string name="night_mode_yes" msgid="2218157265997633432">"Immer an"</string>
<string name="night_mode_auto" msgid="7508348175804304327">"Automatisch"</string>
+ <string name="select_webview_provider_title" msgid="4628592979751918907">"WebView-Implementierung"</string>
+ <string name="select_webview_provider_dialog_title" msgid="4370551378720004872">"WebView-Implementierung festlegen"</string>
<string name="convert_to_file_encryption" msgid="3060156730651061223">"Zu Dateiverschlüsselung wechseln"</string>
<string name="convert_to_file_encryption_enabled" msgid="2861258671151428346">"Wechseln…"</string>
<string name="convert_to_file_encryption_done" msgid="7859766358000523953">"Dateiverschlüsselung wird bereits verwendet."</string>
diff --git a/packages/SettingsLib/res/values-el/strings.xml b/packages/SettingsLib/res/values-el/strings.xml
index 008491f..d426616 100644
--- a/packages/SettingsLib/res/values-el/strings.xml
+++ b/packages/SettingsLib/res/values-el/strings.xml
@@ -273,6 +273,8 @@
<string name="night_mode_no" msgid="9171772244775838901">"Ανενεργό"</string>
<string name="night_mode_yes" msgid="2218157265997633432">"Πάντα ενεργό"</string>
<string name="night_mode_auto" msgid="7508348175804304327">"Αυτόματο"</string>
+ <string name="select_webview_provider_title" msgid="4628592979751918907">"Υλοποίηση WebView"</string>
+ <string name="select_webview_provider_dialog_title" msgid="4370551378720004872">"Ορισμός υλοποίησης WebView"</string>
<string name="convert_to_file_encryption" msgid="3060156730651061223">"Μετατροπή σε κρυπτογράφηση αρχείου"</string>
<string name="convert_to_file_encryption_enabled" msgid="2861258671151428346">"Μετατροπή…"</string>
<string name="convert_to_file_encryption_done" msgid="7859766358000523953">"Με κρυπτογράφηση αρχείου"</string>
diff --git a/packages/SettingsLib/res/values-en-rAU/strings.xml b/packages/SettingsLib/res/values-en-rAU/strings.xml
index dabc72ef..b8c8521 100644
--- a/packages/SettingsLib/res/values-en-rAU/strings.xml
+++ b/packages/SettingsLib/res/values-en-rAU/strings.xml
@@ -273,6 +273,8 @@
<string name="night_mode_no" msgid="9171772244775838901">"Disabled"</string>
<string name="night_mode_yes" msgid="2218157265997633432">"Always on"</string>
<string name="night_mode_auto" msgid="7508348175804304327">"Automatic"</string>
+ <string name="select_webview_provider_title" msgid="4628592979751918907">"WebView implementation"</string>
+ <string name="select_webview_provider_dialog_title" msgid="4370551378720004872">"Set WebView implementation"</string>
<string name="convert_to_file_encryption" msgid="3060156730651061223">"Convert to file encryption"</string>
<string name="convert_to_file_encryption_enabled" msgid="2861258671151428346">"Convert…"</string>
<string name="convert_to_file_encryption_done" msgid="7859766358000523953">"Already file encrypted"</string>
diff --git a/packages/SettingsLib/res/values-en-rGB/strings.xml b/packages/SettingsLib/res/values-en-rGB/strings.xml
index dabc72ef..b8c8521 100644
--- a/packages/SettingsLib/res/values-en-rGB/strings.xml
+++ b/packages/SettingsLib/res/values-en-rGB/strings.xml
@@ -273,6 +273,8 @@
<string name="night_mode_no" msgid="9171772244775838901">"Disabled"</string>
<string name="night_mode_yes" msgid="2218157265997633432">"Always on"</string>
<string name="night_mode_auto" msgid="7508348175804304327">"Automatic"</string>
+ <string name="select_webview_provider_title" msgid="4628592979751918907">"WebView implementation"</string>
+ <string name="select_webview_provider_dialog_title" msgid="4370551378720004872">"Set WebView implementation"</string>
<string name="convert_to_file_encryption" msgid="3060156730651061223">"Convert to file encryption"</string>
<string name="convert_to_file_encryption_enabled" msgid="2861258671151428346">"Convert…"</string>
<string name="convert_to_file_encryption_done" msgid="7859766358000523953">"Already file encrypted"</string>
diff --git a/packages/SettingsLib/res/values-en-rIN/strings.xml b/packages/SettingsLib/res/values-en-rIN/strings.xml
index dabc72ef..b8c8521 100644
--- a/packages/SettingsLib/res/values-en-rIN/strings.xml
+++ b/packages/SettingsLib/res/values-en-rIN/strings.xml
@@ -273,6 +273,8 @@
<string name="night_mode_no" msgid="9171772244775838901">"Disabled"</string>
<string name="night_mode_yes" msgid="2218157265997633432">"Always on"</string>
<string name="night_mode_auto" msgid="7508348175804304327">"Automatic"</string>
+ <string name="select_webview_provider_title" msgid="4628592979751918907">"WebView implementation"</string>
+ <string name="select_webview_provider_dialog_title" msgid="4370551378720004872">"Set WebView implementation"</string>
<string name="convert_to_file_encryption" msgid="3060156730651061223">"Convert to file encryption"</string>
<string name="convert_to_file_encryption_enabled" msgid="2861258671151428346">"Convert…"</string>
<string name="convert_to_file_encryption_done" msgid="7859766358000523953">"Already file encrypted"</string>
diff --git a/packages/SettingsLib/res/values-es-rUS/strings.xml b/packages/SettingsLib/res/values-es-rUS/strings.xml
index 2612943..34ebef4 100644
--- a/packages/SettingsLib/res/values-es-rUS/strings.xml
+++ b/packages/SettingsLib/res/values-es-rUS/strings.xml
@@ -273,6 +273,8 @@
<string name="night_mode_no" msgid="9171772244775838901">"Inhabilitado"</string>
<string name="night_mode_yes" msgid="2218157265997633432">"Siempre activado"</string>
<string name="night_mode_auto" msgid="7508348175804304327">"Automático"</string>
+ <string name="select_webview_provider_title" msgid="4628592979751918907">"Implementación de WebView"</string>
+ <string name="select_webview_provider_dialog_title" msgid="4370551378720004872">"Configurar la implementación de WebView"</string>
<string name="convert_to_file_encryption" msgid="3060156730651061223">"Convertir a encriptación de archivo"</string>
<string name="convert_to_file_encryption_enabled" msgid="2861258671151428346">"Convertir…"</string>
<string name="convert_to_file_encryption_done" msgid="7859766358000523953">"Ya está encriptado"</string>
diff --git a/packages/SettingsLib/res/values-es/strings.xml b/packages/SettingsLib/res/values-es/strings.xml
index 02a8306..cedd582 100644
--- a/packages/SettingsLib/res/values-es/strings.xml
+++ b/packages/SettingsLib/res/values-es/strings.xml
@@ -273,6 +273,8 @@
<string name="night_mode_no" msgid="9171772244775838901">"Inhabilitado"</string>
<string name="night_mode_yes" msgid="2218157265997633432">"Siempre activado"</string>
<string name="night_mode_auto" msgid="7508348175804304327">"Automático"</string>
+ <string name="select_webview_provider_title" msgid="4628592979751918907">"Implementación de WebView"</string>
+ <string name="select_webview_provider_dialog_title" msgid="4370551378720004872">"Establecer implementación de WebView"</string>
<string name="convert_to_file_encryption" msgid="3060156730651061223">"Convertir a cifrado de archivo"</string>
<string name="convert_to_file_encryption_enabled" msgid="2861258671151428346">"Convertir…"</string>
<string name="convert_to_file_encryption_done" msgid="7859766358000523953">"Ya está cifrado"</string>
diff --git a/packages/SettingsLib/res/values-et-rEE/strings.xml b/packages/SettingsLib/res/values-et-rEE/strings.xml
index 5fb55eb..714004e 100644
--- a/packages/SettingsLib/res/values-et-rEE/strings.xml
+++ b/packages/SettingsLib/res/values-et-rEE/strings.xml
@@ -273,6 +273,8 @@
<string name="night_mode_no" msgid="9171772244775838901">"Keelatud"</string>
<string name="night_mode_yes" msgid="2218157265997633432">"Alati sees"</string>
<string name="night_mode_auto" msgid="7508348175804304327">"Automaatne"</string>
+ <string name="select_webview_provider_title" msgid="4628592979751918907">"WebView\' rakendamine"</string>
+ <string name="select_webview_provider_dialog_title" msgid="4370551378720004872">"WebView\' rakendamise seadistamine"</string>
<string name="convert_to_file_encryption" msgid="3060156730651061223">"Teisendamine failikrüpteeringuga andmeteks"</string>
<string name="convert_to_file_encryption_enabled" msgid="2861258671151428346">"Teisenda …"</string>
<string name="convert_to_file_encryption_done" msgid="7859766358000523953">"Juba failikrüpteeringuga"</string>
diff --git a/packages/SettingsLib/res/values-eu-rES/strings.xml b/packages/SettingsLib/res/values-eu-rES/strings.xml
index 9f62710..7f47bf3 100644
--- a/packages/SettingsLib/res/values-eu-rES/strings.xml
+++ b/packages/SettingsLib/res/values-eu-rES/strings.xml
@@ -273,6 +273,8 @@
<string name="night_mode_no" msgid="9171772244775838901">"Desgaituta"</string>
<string name="night_mode_yes" msgid="2218157265997633432">"Beti aktibatuta"</string>
<string name="night_mode_auto" msgid="7508348175804304327">"Automatikoa"</string>
+ <string name="select_webview_provider_title" msgid="4628592979751918907">"WebView implementation"</string>
+ <string name="select_webview_provider_dialog_title" msgid="4370551378720004872">"Set WebView implementation"</string>
<string name="convert_to_file_encryption" msgid="3060156730651061223">"Eman fitxategietan oinarritutako enkriptatzea"</string>
<string name="convert_to_file_encryption_enabled" msgid="2861258671151428346">"Enkriptatu…"</string>
<string name="convert_to_file_encryption_done" msgid="7859766358000523953">"Fitxategietan oinarritutako enkriptatzea dauka dagoeneko"</string>
diff --git a/packages/SettingsLib/res/values-fa/strings.xml b/packages/SettingsLib/res/values-fa/strings.xml
index 2551ba5..1f8b575 100644
--- a/packages/SettingsLib/res/values-fa/strings.xml
+++ b/packages/SettingsLib/res/values-fa/strings.xml
@@ -273,6 +273,8 @@
<string name="night_mode_no" msgid="9171772244775838901">"غیرفعال است"</string>
<string name="night_mode_yes" msgid="2218157265997633432">"همیشه روشن"</string>
<string name="night_mode_auto" msgid="7508348175804304327">"خودکار"</string>
+ <string name="select_webview_provider_title" msgid="4628592979751918907">"اجرای WebView"</string>
+ <string name="select_webview_provider_dialog_title" msgid="4370551378720004872">"تنظیم اجرای WebView"</string>
<string name="convert_to_file_encryption" msgid="3060156730651061223">"تبدیل به رمزگذاری برحسب فایل"</string>
<string name="convert_to_file_encryption_enabled" msgid="2861258671151428346">"تبدیل…"</string>
<string name="convert_to_file_encryption_done" msgid="7859766358000523953">"از قبل به رمزگذاری بر حسب فایل تبدیل شده است"</string>
diff --git a/packages/SettingsLib/res/values-fi/strings.xml b/packages/SettingsLib/res/values-fi/strings.xml
index 8a0e3b5..e4b4b93 100644
--- a/packages/SettingsLib/res/values-fi/strings.xml
+++ b/packages/SettingsLib/res/values-fi/strings.xml
@@ -273,6 +273,8 @@
<string name="night_mode_no" msgid="9171772244775838901">"Ei käytössä"</string>
<string name="night_mode_yes" msgid="2218157265997633432">"Aina käytössä"</string>
<string name="night_mode_auto" msgid="7508348175804304327">"Automaattinen"</string>
+ <string name="select_webview_provider_title" msgid="4628592979751918907">"WebView-käyttöönotto"</string>
+ <string name="select_webview_provider_dialog_title" msgid="4370551378720004872">"Määritä WebView-käyttöönotto"</string>
<string name="convert_to_file_encryption" msgid="3060156730651061223">"Muunna tiedostojen salaukseksi"</string>
<string name="convert_to_file_encryption_enabled" msgid="2861258671151428346">"Muunna…"</string>
<string name="convert_to_file_encryption_done" msgid="7859766358000523953">"Tiedostot on jo salattu."</string>
diff --git a/packages/SettingsLib/res/values-fr-rCA/strings.xml b/packages/SettingsLib/res/values-fr-rCA/strings.xml
index f2c79de..66c54a1 100644
--- a/packages/SettingsLib/res/values-fr-rCA/strings.xml
+++ b/packages/SettingsLib/res/values-fr-rCA/strings.xml
@@ -273,6 +273,8 @@
<string name="night_mode_no" msgid="9171772244775838901">"Désactivé"</string>
<string name="night_mode_yes" msgid="2218157265997633432">"Toujours actif"</string>
<string name="night_mode_auto" msgid="7508348175804304327">"Automatique"</string>
+ <string name="select_webview_provider_title" msgid="4628592979751918907">"Mise en œuvre WebView"</string>
+ <string name="select_webview_provider_dialog_title" msgid="4370551378720004872">"Définir la mise en œuvre WebView"</string>
<string name="convert_to_file_encryption" msgid="3060156730651061223">"Convertir en chiffrement basé sur un fichier"</string>
<string name="convert_to_file_encryption_enabled" msgid="2861258671151428346">"Convertir..."</string>
<string name="convert_to_file_encryption_done" msgid="7859766358000523953">"Déjà chiffré par un fichier"</string>
diff --git a/packages/SettingsLib/res/values-fr/strings.xml b/packages/SettingsLib/res/values-fr/strings.xml
index 95e8804..e56940a 100644
--- a/packages/SettingsLib/res/values-fr/strings.xml
+++ b/packages/SettingsLib/res/values-fr/strings.xml
@@ -273,6 +273,8 @@
<string name="night_mode_no" msgid="9171772244775838901">"Désactivé"</string>
<string name="night_mode_yes" msgid="2218157265997633432">"Toujours activé"</string>
<string name="night_mode_auto" msgid="7508348175804304327">"Automatique"</string>
+ <string name="select_webview_provider_title" msgid="4628592979751918907">"Mise en œuvre WebView"</string>
+ <string name="select_webview_provider_dialog_title" msgid="4370551378720004872">"Définir la mise en œuvre WebView"</string>
<string name="convert_to_file_encryption" msgid="3060156730651061223">"Convertir en chiffrement basé sur un fichier"</string>
<string name="convert_to_file_encryption_enabled" msgid="2861258671151428346">"Convertir…"</string>
<string name="convert_to_file_encryption_done" msgid="7859766358000523953">"Déjà chiffré via un fichier"</string>
diff --git a/packages/SettingsLib/res/values-gl-rES/strings.xml b/packages/SettingsLib/res/values-gl-rES/strings.xml
index df58657..9bcf770 100644
--- a/packages/SettingsLib/res/values-gl-rES/strings.xml
+++ b/packages/SettingsLib/res/values-gl-rES/strings.xml
@@ -273,6 +273,8 @@
<string name="night_mode_no" msgid="9171772244775838901">"Desactivado"</string>
<string name="night_mode_yes" msgid="2218157265997633432">"Sempre activada"</string>
<string name="night_mode_auto" msgid="7508348175804304327">"Automático"</string>
+ <string name="select_webview_provider_title" msgid="4628592979751918907">"Implementación de WebView"</string>
+ <string name="select_webview_provider_dialog_title" msgid="4370551378720004872">"Definir implementación de WebView"</string>
<string name="convert_to_file_encryption" msgid="3060156730651061223">"Converter no encriptado baseado en ficheiros"</string>
<string name="convert_to_file_encryption_enabled" msgid="2861258671151428346">"Converter..."</string>
<string name="convert_to_file_encryption_done" msgid="7859766358000523953">"Xa se encriptou o ficheiro"</string>
diff --git a/packages/SettingsLib/res/values-gu-rIN/strings.xml b/packages/SettingsLib/res/values-gu-rIN/strings.xml
index d4db078..6dff8fa 100644
--- a/packages/SettingsLib/res/values-gu-rIN/strings.xml
+++ b/packages/SettingsLib/res/values-gu-rIN/strings.xml
@@ -273,6 +273,8 @@
<string name="night_mode_no" msgid="9171772244775838901">"અક્ષમ કરેલ"</string>
<string name="night_mode_yes" msgid="2218157265997633432">"હંમેશાં ચાલુ"</string>
<string name="night_mode_auto" msgid="7508348175804304327">"સ્વચલિત"</string>
+ <string name="select_webview_provider_title" msgid="4628592979751918907">"WebView અમલીકરણ"</string>
+ <string name="select_webview_provider_dialog_title" msgid="4370551378720004872">"WebView અમલીકરણ સેટ કરો"</string>
<string name="convert_to_file_encryption" msgid="3060156730651061223">"ફાઇલ એન્ક્રિપ્શનમાં રૂપાંતરિત કરો"</string>
<string name="convert_to_file_encryption_enabled" msgid="2861258671151428346">"રૂપાંતરિત કરો..."</string>
<string name="convert_to_file_encryption_done" msgid="7859766358000523953">"ફાઇલ પહેલેથી જ એન્ક્રિપ્ટ કરેલ છે"</string>
diff --git a/packages/SettingsLib/res/values-hi/strings.xml b/packages/SettingsLib/res/values-hi/strings.xml
index b6e5d7d..9ce83fa 100644
--- a/packages/SettingsLib/res/values-hi/strings.xml
+++ b/packages/SettingsLib/res/values-hi/strings.xml
@@ -273,6 +273,8 @@
<string name="night_mode_no" msgid="9171772244775838901">"अक्षम"</string>
<string name="night_mode_yes" msgid="2218157265997633432">"हमेशा चालू"</string>
<string name="night_mode_auto" msgid="7508348175804304327">"स्वचालित"</string>
+ <string name="select_webview_provider_title" msgid="4628592979751918907">"WebView कार्यान्वयन"</string>
+ <string name="select_webview_provider_dialog_title" msgid="4370551378720004872">"WebView कार्यान्वयन सेट करें"</string>
<string name="convert_to_file_encryption" msgid="3060156730651061223">"फ़ाइल एन्क्रिप्शन में रूपांतरित करें"</string>
<string name="convert_to_file_encryption_enabled" msgid="2861258671151428346">"रूपांतरित करें..."</string>
<string name="convert_to_file_encryption_done" msgid="7859766358000523953">"फ़ाइल पहले से एन्क्रिप्ट की हुई है"</string>
diff --git a/packages/SettingsLib/res/values-hr/strings.xml b/packages/SettingsLib/res/values-hr/strings.xml
index f8434eb..dd02171 100644
--- a/packages/SettingsLib/res/values-hr/strings.xml
+++ b/packages/SettingsLib/res/values-hr/strings.xml
@@ -273,6 +273,8 @@
<string name="night_mode_no" msgid="9171772244775838901">"Onemogućeno"</string>
<string name="night_mode_yes" msgid="2218157265997633432">"Uvijek uključeno"</string>
<string name="night_mode_auto" msgid="7508348175804304327">"Automatska"</string>
+ <string name="select_webview_provider_title" msgid="4628592979751918907">"Implementacija WebViewa"</string>
+ <string name="select_webview_provider_dialog_title" msgid="4370551378720004872">"Postavi implementaciju WebViewa"</string>
<string name="convert_to_file_encryption" msgid="3060156730651061223">"Pretvori u enkripciju datoteka"</string>
<string name="convert_to_file_encryption_enabled" msgid="2861258671151428346">"Pretvori…"</string>
<string name="convert_to_file_encryption_done" msgid="7859766358000523953">"Enkripcija datoteka već je izvršena"</string>
diff --git a/packages/SettingsLib/res/values-hu/strings.xml b/packages/SettingsLib/res/values-hu/strings.xml
index 636f88a..9b3a386 100644
--- a/packages/SettingsLib/res/values-hu/strings.xml
+++ b/packages/SettingsLib/res/values-hu/strings.xml
@@ -273,6 +273,8 @@
<string name="night_mode_no" msgid="9171772244775838901">"Kikapcsolva"</string>
<string name="night_mode_yes" msgid="2218157265997633432">"Mindig bekapcsolva"</string>
<string name="night_mode_auto" msgid="7508348175804304327">"Automatikus"</string>
+ <string name="select_webview_provider_title" msgid="4628592979751918907">"WebView-megvalósítás"</string>
+ <string name="select_webview_provider_dialog_title" msgid="4370551378720004872">"WebView-megvalósítás beállítása"</string>
<string name="convert_to_file_encryption" msgid="3060156730651061223">"Konvertálás fájlalapú titkosításra"</string>
<string name="convert_to_file_encryption_enabled" msgid="2861258671151428346">"Konvertálás…"</string>
<string name="convert_to_file_encryption_done" msgid="7859766358000523953">"Már fájlalapú titkosítást használ"</string>
diff --git a/packages/SettingsLib/res/values-hy-rAM/strings.xml b/packages/SettingsLib/res/values-hy-rAM/strings.xml
index a2b901a..27db9fe 100644
--- a/packages/SettingsLib/res/values-hy-rAM/strings.xml
+++ b/packages/SettingsLib/res/values-hy-rAM/strings.xml
@@ -273,6 +273,8 @@
<string name="night_mode_no" msgid="9171772244775838901">"Անջատված"</string>
<string name="night_mode_yes" msgid="2218157265997633432">"Միշտ միացված"</string>
<string name="night_mode_auto" msgid="7508348175804304327">"Ավտոմատ"</string>
+ <string name="select_webview_provider_title" msgid="4628592979751918907">"WebView-ի իրականացում"</string>
+ <string name="select_webview_provider_dialog_title" msgid="4370551378720004872">"Ընտրեք WebView-ի իրականացումը"</string>
<string name="convert_to_file_encryption" msgid="3060156730651061223">"Վերածել ֆայլային գաղտնագրման"</string>
<string name="convert_to_file_encryption_enabled" msgid="2861258671151428346">"Փոխարկել…"</string>
<string name="convert_to_file_encryption_done" msgid="7859766358000523953">"Ֆայլային գաղտնագրումն արդեն կատարվել է"</string>
diff --git a/packages/SettingsLib/res/values-in/strings.xml b/packages/SettingsLib/res/values-in/strings.xml
index f4fbe1a..0cea206 100644
--- a/packages/SettingsLib/res/values-in/strings.xml
+++ b/packages/SettingsLib/res/values-in/strings.xml
@@ -273,6 +273,8 @@
<string name="night_mode_no" msgid="9171772244775838901">"Dinonaktifkan"</string>
<string name="night_mode_yes" msgid="2218157265997633432">"Selalu aktif"</string>
<string name="night_mode_auto" msgid="7508348175804304327">"Otomatis"</string>
+ <string name="select_webview_provider_title" msgid="4628592979751918907">"Penerapan WebView"</string>
+ <string name="select_webview_provider_dialog_title" msgid="4370551378720004872">"Setel penerapan WebView"</string>
<string name="convert_to_file_encryption" msgid="3060156730651061223">"Konversi ke enkripsi file"</string>
<string name="convert_to_file_encryption_enabled" msgid="2861258671151428346">"Konversi..."</string>
<string name="convert_to_file_encryption_done" msgid="7859766358000523953">"Sudah dienkripsi berbasis file"</string>
diff --git a/packages/SettingsLib/res/values-is-rIS/strings.xml b/packages/SettingsLib/res/values-is-rIS/strings.xml
index 4139e6e..b3509ca 100644
--- a/packages/SettingsLib/res/values-is-rIS/strings.xml
+++ b/packages/SettingsLib/res/values-is-rIS/strings.xml
@@ -273,6 +273,8 @@
<string name="night_mode_no" msgid="9171772244775838901">"Óvirkt"</string>
<string name="night_mode_yes" msgid="2218157265997633432">"Alltaf kveikt"</string>
<string name="night_mode_auto" msgid="7508348175804304327">"Sjálfvirkt"</string>
+ <string name="select_webview_provider_title" msgid="4628592979751918907">"Innleiðing WebView"</string>
+ <string name="select_webview_provider_dialog_title" msgid="4370551378720004872">"Stilla innleiðingu WebView"</string>
<string name="convert_to_file_encryption" msgid="3060156730651061223">"Umbreyta í dulkóðun skráa"</string>
<string name="convert_to_file_encryption_enabled" msgid="2861258671151428346">"Umbreyta…"</string>
<string name="convert_to_file_encryption_done" msgid="7859766358000523953">"Þegar dulkóðað á grundvelli skráa"</string>
diff --git a/packages/SettingsLib/res/values-it/strings.xml b/packages/SettingsLib/res/values-it/strings.xml
index 33a15d4..9f897f7 100644
--- a/packages/SettingsLib/res/values-it/strings.xml
+++ b/packages/SettingsLib/res/values-it/strings.xml
@@ -273,6 +273,8 @@
<string name="night_mode_no" msgid="9171772244775838901">"Disattivato"</string>
<string name="night_mode_yes" msgid="2218157265997633432">"Sempre attivo"</string>
<string name="night_mode_auto" msgid="7508348175804304327">"Automatico"</string>
+ <string name="select_webview_provider_title" msgid="4628592979751918907">"Implementazione di WebView"</string>
+ <string name="select_webview_provider_dialog_title" msgid="4370551378720004872">"Imposta l\'implementazione di WebView"</string>
<string name="convert_to_file_encryption" msgid="3060156730651061223">"Converti in crittografia basata su file"</string>
<string name="convert_to_file_encryption_enabled" msgid="2861258671151428346">"Converti..."</string>
<string name="convert_to_file_encryption_done" msgid="7859766358000523953">"Crittografia su base file già eseguita"</string>
diff --git a/packages/SettingsLib/res/values-iw/strings.xml b/packages/SettingsLib/res/values-iw/strings.xml
index 4a0870e..2d44612 100644
--- a/packages/SettingsLib/res/values-iw/strings.xml
+++ b/packages/SettingsLib/res/values-iw/strings.xml
@@ -273,6 +273,8 @@
<string name="night_mode_no" msgid="9171772244775838901">"מושבת"</string>
<string name="night_mode_yes" msgid="2218157265997633432">"פועל תמיד"</string>
<string name="night_mode_auto" msgid="7508348175804304327">"באופן אוטומטי"</string>
+ <string name="select_webview_provider_title" msgid="4628592979751918907">"יישום WebView"</string>
+ <string name="select_webview_provider_dialog_title" msgid="4370551378720004872">"הגדרת יישום WebView"</string>
<string name="convert_to_file_encryption" msgid="3060156730651061223">"המר להצפנת קבצים"</string>
<string name="convert_to_file_encryption_enabled" msgid="2861258671151428346">"המר..."</string>
<string name="convert_to_file_encryption_done" msgid="7859766358000523953">"הצפנת קבצים כבר מוגדרת"</string>
diff --git a/packages/SettingsLib/res/values-ja/strings.xml b/packages/SettingsLib/res/values-ja/strings.xml
index 4c043ac..81a5eaf 100644
--- a/packages/SettingsLib/res/values-ja/strings.xml
+++ b/packages/SettingsLib/res/values-ja/strings.xml
@@ -273,6 +273,8 @@
<string name="night_mode_no" msgid="9171772244775838901">"無効"</string>
<string name="night_mode_yes" msgid="2218157265997633432">"常にON"</string>
<string name="night_mode_auto" msgid="7508348175804304327">"自動"</string>
+ <string name="select_webview_provider_title" msgid="4628592979751918907">"WebView の実装"</string>
+ <string name="select_webview_provider_dialog_title" msgid="4370551378720004872">"WebView の実装の設定"</string>
<string name="convert_to_file_encryption" msgid="3060156730651061223">"ファイル暗号化に変換する"</string>
<string name="convert_to_file_encryption_enabled" msgid="2861258671151428346">"変換…"</string>
<string name="convert_to_file_encryption_done" msgid="7859766358000523953">"ファイルは既に暗号化済みです"</string>
diff --git a/packages/SettingsLib/res/values-ka-rGE/strings.xml b/packages/SettingsLib/res/values-ka-rGE/strings.xml
index 1ea79bf..e2dfd7e 100644
--- a/packages/SettingsLib/res/values-ka-rGE/strings.xml
+++ b/packages/SettingsLib/res/values-ka-rGE/strings.xml
@@ -273,6 +273,8 @@
<string name="night_mode_no" msgid="9171772244775838901">"გამორთულია"</string>
<string name="night_mode_yes" msgid="2218157265997633432">"ყოველთვის ჩართული"</string>
<string name="night_mode_auto" msgid="7508348175804304327">"ავტომატური"</string>
+ <string name="select_webview_provider_title" msgid="4628592979751918907">"WebView რეალიზაცია"</string>
+ <string name="select_webview_provider_dialog_title" msgid="4370551378720004872">"WebView რეალიზაციის დაყენება"</string>
<string name="convert_to_file_encryption" msgid="3060156730651061223">"ფაილების დაშიფვრაზე გარდაქმნა"</string>
<string name="convert_to_file_encryption_enabled" msgid="2861258671151428346">"გარდაქმნა…"</string>
<string name="convert_to_file_encryption_done" msgid="7859766358000523953">"უკვე დაშიფრულია ფაილების დონეზე"</string>
diff --git a/packages/SettingsLib/res/values-kk-rKZ/strings.xml b/packages/SettingsLib/res/values-kk-rKZ/strings.xml
index c6a2768..2dbc375 100644
--- a/packages/SettingsLib/res/values-kk-rKZ/strings.xml
+++ b/packages/SettingsLib/res/values-kk-rKZ/strings.xml
@@ -273,6 +273,10 @@
<string name="night_mode_no" msgid="9171772244775838901">"Өшірілген"</string>
<string name="night_mode_yes" msgid="2218157265997633432">"Әрқашан қосулы"</string>
<string name="night_mode_auto" msgid="7508348175804304327">"Aвтоматты"</string>
+ <!-- no translation found for select_webview_provider_title (4628592979751918907) -->
+ <skip />
+ <!-- no translation found for select_webview_provider_dialog_title (4370551378720004872) -->
+ <skip />
<string name="convert_to_file_encryption" msgid="3060156730651061223">"Файлды шифрлауға түрлендіру"</string>
<string name="convert_to_file_encryption_enabled" msgid="2861258671151428346">"Түрлендіру..."</string>
<string name="convert_to_file_encryption_done" msgid="7859766358000523953">"Файл шифрланып қойылған"</string>
diff --git a/packages/SettingsLib/res/values-km-rKH/strings.xml b/packages/SettingsLib/res/values-km-rKH/strings.xml
index 3e91925..0186a68 100644
--- a/packages/SettingsLib/res/values-km-rKH/strings.xml
+++ b/packages/SettingsLib/res/values-km-rKH/strings.xml
@@ -273,6 +273,8 @@
<string name="night_mode_no" msgid="9171772244775838901">"បានបិទ"</string>
<string name="night_mode_yes" msgid="2218157265997633432">"បើកជានិច្ច"</string>
<string name="night_mode_auto" msgid="7508348175804304327">"ស្វ័យប្រវត្តិ"</string>
+ <string name="select_webview_provider_title" msgid="4628592979751918907">"ការប្រតិបត្តិ WebView"</string>
+ <string name="select_webview_provider_dialog_title" msgid="4370551378720004872">"កំណត់ការប្រតិបត្តិ WebView"</string>
<string name="convert_to_file_encryption" msgid="3060156730651061223">"បម្លែងទៅជាការអ៊ីនគ្រីបឯកសារ"</string>
<string name="convert_to_file_encryption_enabled" msgid="2861258671151428346">"បម្លែង…"</string>
<string name="convert_to_file_encryption_done" msgid="7859766358000523953">"បានអ៊ីនគ្រីបឯកសាររួចហើយ"</string>
diff --git a/packages/SettingsLib/res/values-kn-rIN/strings.xml b/packages/SettingsLib/res/values-kn-rIN/strings.xml
index 4d1f9e1..56f4006 100644
--- a/packages/SettingsLib/res/values-kn-rIN/strings.xml
+++ b/packages/SettingsLib/res/values-kn-rIN/strings.xml
@@ -273,6 +273,8 @@
<string name="night_mode_no" msgid="9171772244775838901">"ನಿಷ್ಕ್ರಿಯಗೊಳಿಸಲಾಗಿದೆ"</string>
<string name="night_mode_yes" msgid="2218157265997633432">"ಯಾವಾಗಲೂ ಆನ್"</string>
<string name="night_mode_auto" msgid="7508348175804304327">"ಸ್ವಯಂಚಾಲಿತ"</string>
+ <string name="select_webview_provider_title" msgid="4628592979751918907">"WebView ಅನುಷ್ಠಾನಗೊಳಿಸುವಿಕೆ"</string>
+ <string name="select_webview_provider_dialog_title" msgid="4370551378720004872">"WebView ಅನುಷ್ಠಾನಗೊಳಿಸುವಿಕೆಯನ್ನು ಹೊಂದಿಸಿ"</string>
<string name="convert_to_file_encryption" msgid="3060156730651061223">"ಫೈಲ್ ಎನ್ಕ್ರಿಪ್ಶನ್ಗೆ ಪರಿವರ್ತಿಸು"</string>
<string name="convert_to_file_encryption_enabled" msgid="2861258671151428346">"ಪರಿವರ್ತಿಸು…"</string>
<string name="convert_to_file_encryption_done" msgid="7859766358000523953">"ಫೈಲ್ ಈಗಾಗಲೇ ಎನ್ಕ್ರಿಪ್ಟ್ ಮಾಡಲಾಗಿದೆ"</string>
diff --git a/packages/SettingsLib/res/values-ko/strings.xml b/packages/SettingsLib/res/values-ko/strings.xml
index c4ab084..6ad3efd 100644
--- a/packages/SettingsLib/res/values-ko/strings.xml
+++ b/packages/SettingsLib/res/values-ko/strings.xml
@@ -273,6 +273,8 @@
<string name="night_mode_no" msgid="9171772244775838901">"사용 안함"</string>
<string name="night_mode_yes" msgid="2218157265997633432">"항상 사용"</string>
<string name="night_mode_auto" msgid="7508348175804304327">"자동"</string>
+ <string name="select_webview_provider_title" msgid="4628592979751918907">"WebView 구현"</string>
+ <string name="select_webview_provider_dialog_title" msgid="4370551378720004872">"WebView 구현 설정"</string>
<string name="convert_to_file_encryption" msgid="3060156730651061223">"파일 암호화로 변환"</string>
<string name="convert_to_file_encryption_enabled" msgid="2861258671151428346">"변환..."</string>
<string name="convert_to_file_encryption_done" msgid="7859766358000523953">"파일이 이미 암호화됨"</string>
diff --git a/packages/SettingsLib/res/values-ky-rKG/strings.xml b/packages/SettingsLib/res/values-ky-rKG/strings.xml
index 77c5ab8..bf37ac4 100644
--- a/packages/SettingsLib/res/values-ky-rKG/strings.xml
+++ b/packages/SettingsLib/res/values-ky-rKG/strings.xml
@@ -273,6 +273,8 @@
<string name="night_mode_no" msgid="9171772244775838901">"Өчүрүлгөн"</string>
<string name="night_mode_yes" msgid="2218157265997633432">"Ар дайым күйгүзүлгөн"</string>
<string name="night_mode_auto" msgid="7508348175804304327">"Автоматтык"</string>
+ <string name="select_webview_provider_title" msgid="4628592979751918907">"WebView аткарылышы"</string>
+ <string name="select_webview_provider_dialog_title" msgid="4370551378720004872">"WebView аткарылышын коюу"</string>
<string name="convert_to_file_encryption" msgid="3060156730651061223">"Файл шифрлөөсүнө айландыруу"</string>
<string name="convert_to_file_encryption_enabled" msgid="2861258671151428346">"Айландыруу…"</string>
<string name="convert_to_file_encryption_done" msgid="7859766358000523953">"Файл мурунтан эле шифрленген"</string>
diff --git a/packages/SettingsLib/res/values-lo-rLA/strings.xml b/packages/SettingsLib/res/values-lo-rLA/strings.xml
index 806e1c0..9e6a4b7 100644
--- a/packages/SettingsLib/res/values-lo-rLA/strings.xml
+++ b/packages/SettingsLib/res/values-lo-rLA/strings.xml
@@ -273,6 +273,8 @@
<string name="night_mode_no" msgid="9171772244775838901">"ປິດໃຊ້ງານແລ້ວ"</string>
<string name="night_mode_yes" msgid="2218157265997633432">"ເປີດຕະຫຼອດ"</string>
<string name="night_mode_auto" msgid="7508348175804304327">"ອັດຕະໂນມັດ"</string>
+ <string name="select_webview_provider_title" msgid="4628592979751918907">"ການຈັດຕັ້ງປະຕິບັດ WebView"</string>
+ <string name="select_webview_provider_dialog_title" msgid="4370551378720004872">"ຕັ້ງການຈັດຕັ້ງປະຕິບັດ WebView"</string>
<string name="convert_to_file_encryption" msgid="3060156730651061223">"ປ່ຽນເປັນການເຂົ້າລະຫັດໄຟລ໌"</string>
<string name="convert_to_file_encryption_enabled" msgid="2861258671151428346">"ປ່ຽນ..."</string>
<string name="convert_to_file_encryption_done" msgid="7859766358000523953">"ໄຟລ໌ເຂົ້າລະຫັດຮຽບຮ້ອຍແລ້ວ"</string>
diff --git a/packages/SettingsLib/res/values-lt/strings.xml b/packages/SettingsLib/res/values-lt/strings.xml
index 0b04f53..2019646 100644
--- a/packages/SettingsLib/res/values-lt/strings.xml
+++ b/packages/SettingsLib/res/values-lt/strings.xml
@@ -273,6 +273,8 @@
<string name="night_mode_no" msgid="9171772244775838901">"Išjungta"</string>
<string name="night_mode_yes" msgid="2218157265997633432">"Visada įjungta"</string>
<string name="night_mode_auto" msgid="7508348175804304327">"Automatinė"</string>
+ <string name="select_webview_provider_title" msgid="4628592979751918907">"„WebView“ diegimas"</string>
+ <string name="select_webview_provider_dialog_title" msgid="4370551378720004872">"„WebView“ diegimo nustatymas"</string>
<string name="convert_to_file_encryption" msgid="3060156730651061223">"Konvertuoti į failų šifruotę"</string>
<string name="convert_to_file_encryption_enabled" msgid="2861258671151428346">"Konvertuoti…"</string>
<string name="convert_to_file_encryption_done" msgid="7859766358000523953">"Jau konvertuota į failų šifruotę"</string>
diff --git a/packages/SettingsLib/res/values-lv/strings.xml b/packages/SettingsLib/res/values-lv/strings.xml
index b955162..2f9a83b 100644
--- a/packages/SettingsLib/res/values-lv/strings.xml
+++ b/packages/SettingsLib/res/values-lv/strings.xml
@@ -273,6 +273,8 @@
<string name="night_mode_no" msgid="9171772244775838901">"Atspējots"</string>
<string name="night_mode_yes" msgid="2218157265997633432">"Vienmēr ieslēgts"</string>
<string name="night_mode_auto" msgid="7508348175804304327">"Automātiski"</string>
+ <string name="select_webview_provider_title" msgid="4628592979751918907">"WebView ieviešana"</string>
+ <string name="select_webview_provider_dialog_title" msgid="4370551378720004872">"Iestatīt WebView ieviešanu"</string>
<string name="convert_to_file_encryption" msgid="3060156730651061223">"Pārvērst par failu šifrējumu"</string>
<string name="convert_to_file_encryption_enabled" msgid="2861258671151428346">"Pārvērst…"</string>
<string name="convert_to_file_encryption_done" msgid="7859766358000523953">"Jau šifrēts failu līmenī"</string>
diff --git a/packages/SettingsLib/res/values-mk-rMK/strings.xml b/packages/SettingsLib/res/values-mk-rMK/strings.xml
index f19b7b3..e01d00c 100644
--- a/packages/SettingsLib/res/values-mk-rMK/strings.xml
+++ b/packages/SettingsLib/res/values-mk-rMK/strings.xml
@@ -273,6 +273,8 @@
<string name="night_mode_no" msgid="9171772244775838901">"Оневозможено"</string>
<string name="night_mode_yes" msgid="2218157265997633432">"Секогаш вклучено"</string>
<string name="night_mode_auto" msgid="7508348175804304327">"Автоматски"</string>
+ <string name="select_webview_provider_title" msgid="4628592979751918907">"Воведување WebView"</string>
+ <string name="select_webview_provider_dialog_title" msgid="4370551378720004872">"Поставете воведување WebView"</string>
<string name="convert_to_file_encryption" msgid="3060156730651061223">"Конвертирајте до шифрирање датотеки"</string>
<string name="convert_to_file_encryption_enabled" msgid="2861258671151428346">"Конвертирај..."</string>
<string name="convert_to_file_encryption_done" msgid="7859766358000523953">"Датотеката е веќе шифрирана"</string>
diff --git a/packages/SettingsLib/res/values-ml-rIN/strings.xml b/packages/SettingsLib/res/values-ml-rIN/strings.xml
index 373a00d..f00103f 100644
--- a/packages/SettingsLib/res/values-ml-rIN/strings.xml
+++ b/packages/SettingsLib/res/values-ml-rIN/strings.xml
@@ -273,6 +273,8 @@
<string name="night_mode_no" msgid="9171772244775838901">"പ്രവർത്തനരഹിതമാക്കി"</string>
<string name="night_mode_yes" msgid="2218157265997633432">"എല്ലായ്പ്പോഴും ഓണാണ്"</string>
<string name="night_mode_auto" msgid="7508348175804304327">"ഓട്ടോമാറ്റിക്"</string>
+ <string name="select_webview_provider_title" msgid="4628592979751918907">"WebView നടപ്പാക്കൽ"</string>
+ <string name="select_webview_provider_dialog_title" msgid="4370551378720004872">"WebView നടപ്പാക്കൽ സജ്ജമാക്കുക"</string>
<string name="convert_to_file_encryption" msgid="3060156730651061223">"ഫയൽ എൻക്രിപ്ഷനിലേക്ക് പരിവർത്തിപ്പിക്കുക"</string>
<string name="convert_to_file_encryption_enabled" msgid="2861258671151428346">"പരിവർത്തിപ്പിക്കുക…"</string>
<string name="convert_to_file_encryption_done" msgid="7859766358000523953">"ഇതിനകം തന്നെ ഫയൽ എൻക്രിപ്റ്റ് ചെയ്തു"</string>
diff --git a/packages/SettingsLib/res/values-mn-rMN/strings.xml b/packages/SettingsLib/res/values-mn-rMN/strings.xml
index 62e2c39..cc0e7cb 100644
--- a/packages/SettingsLib/res/values-mn-rMN/strings.xml
+++ b/packages/SettingsLib/res/values-mn-rMN/strings.xml
@@ -273,6 +273,8 @@
<string name="night_mode_no" msgid="9171772244775838901">"Идэвхгүй"</string>
<string name="night_mode_yes" msgid="2218157265997633432">"Байнга асаалттай"</string>
<string name="night_mode_auto" msgid="7508348175804304327">"Автоматаар"</string>
+ <string name="select_webview_provider_title" msgid="4628592979751918907">"WebView хэрэгжилт"</string>
+ <string name="select_webview_provider_dialog_title" msgid="4370551378720004872">"WebView хэрэгжилтийг тохируулах"</string>
<string name="convert_to_file_encryption" msgid="3060156730651061223">"Файлын шифрлэлт болгон хөрвүүлэх"</string>
<string name="convert_to_file_encryption_enabled" msgid="2861258671151428346">"Хөрвүүлэх..."</string>
<string name="convert_to_file_encryption_done" msgid="7859766358000523953">"Аль хэдийнэ файл шифрлэгдсэн"</string>
diff --git a/packages/SettingsLib/res/values-mr-rIN/strings.xml b/packages/SettingsLib/res/values-mr-rIN/strings.xml
index cc3621a..b939ca0 100644
--- a/packages/SettingsLib/res/values-mr-rIN/strings.xml
+++ b/packages/SettingsLib/res/values-mr-rIN/strings.xml
@@ -273,6 +273,8 @@
<string name="night_mode_no" msgid="9171772244775838901">"अक्षम केले"</string>
<string name="night_mode_yes" msgid="2218157265997633432">"नेहमी चालू"</string>
<string name="night_mode_auto" msgid="7508348175804304327">"स्वयंचलित"</string>
+ <string name="select_webview_provider_title" msgid="4628592979751918907">"वेबदृश्य अंमलबजावणी"</string>
+ <string name="select_webview_provider_dialog_title" msgid="4370551378720004872">"वेबदृश्य अंमलबजावणी सेट करा"</string>
<string name="convert_to_file_encryption" msgid="3060156730651061223">"फाईल कूटबद्धीकरणावर रूपांतरित करा"</string>
<string name="convert_to_file_encryption_enabled" msgid="2861258671151428346">"रूपांतरित करा..."</string>
<string name="convert_to_file_encryption_done" msgid="7859766358000523953">"फाईल आधीपासून कूटबद्ध केली"</string>
diff --git a/packages/SettingsLib/res/values-ms-rMY/strings.xml b/packages/SettingsLib/res/values-ms-rMY/strings.xml
index babbefa..2e67752 100644
--- a/packages/SettingsLib/res/values-ms-rMY/strings.xml
+++ b/packages/SettingsLib/res/values-ms-rMY/strings.xml
@@ -273,6 +273,8 @@
<string name="night_mode_no" msgid="9171772244775838901">"Dilumpuhkan"</string>
<string name="night_mode_yes" msgid="2218157265997633432">"Sentiasa hidup"</string>
<string name="night_mode_auto" msgid="7508348175804304327">"Automatik"</string>
+ <string name="select_webview_provider_title" msgid="4628592979751918907">"Pelaksanaan WebView"</string>
+ <string name="select_webview_provider_dialog_title" msgid="4370551378720004872">"Tetapkan pelaksanaan WebView"</string>
<string name="convert_to_file_encryption" msgid="3060156730651061223">"Tukar kepada penyulitan fail"</string>
<string name="convert_to_file_encryption_enabled" msgid="2861258671151428346">"Tukar..."</string>
<string name="convert_to_file_encryption_done" msgid="7859766358000523953">"Sudah disulitkan fail"</string>
diff --git a/packages/SettingsLib/res/values-my-rMM/strings.xml b/packages/SettingsLib/res/values-my-rMM/strings.xml
index f293d4a..002aed1 100644
--- a/packages/SettingsLib/res/values-my-rMM/strings.xml
+++ b/packages/SettingsLib/res/values-my-rMM/strings.xml
@@ -273,6 +273,8 @@
<string name="night_mode_no" msgid="9171772244775838901">"ပိတ်ထား"</string>
<string name="night_mode_yes" msgid="2218157265997633432">"အမြဲတမ်း ဖွင့်ထားရန်"</string>
<string name="night_mode_auto" msgid="7508348175804304327">"အလိုအလျောက်"</string>
+ <string name="select_webview_provider_title" msgid="4628592979751918907">"WebView အကောင်အထည်ဖော်မှု"</string>
+ <string name="select_webview_provider_dialog_title" msgid="4370551378720004872">"WebView အကောင်အထည်ဖော်မှု သတ်မှတ်ပါ"</string>
<string name="convert_to_file_encryption" msgid="3060156730651061223">"ဖိုင်လုံခြုံအောင်ပြုလုပ်ခြင်းသို့ ပြောင်းပါ"</string>
<string name="convert_to_file_encryption_enabled" msgid="2861258671151428346">"ပြောင်းရန်…"</string>
<string name="convert_to_file_encryption_done" msgid="7859766358000523953">"ဖိုင်ကို လုံခြုံအောင်ပြုလုပ်ပြီးပါပြီ"</string>
diff --git a/packages/SettingsLib/res/values-nb/strings.xml b/packages/SettingsLib/res/values-nb/strings.xml
index 2b461e7..a496a21 100644
--- a/packages/SettingsLib/res/values-nb/strings.xml
+++ b/packages/SettingsLib/res/values-nb/strings.xml
@@ -273,6 +273,8 @@
<string name="night_mode_no" msgid="9171772244775838901">"Slått av"</string>
<string name="night_mode_yes" msgid="2218157265997633432">"Alltid på"</string>
<string name="night_mode_auto" msgid="7508348175804304327">"Automatisk"</string>
+ <string name="select_webview_provider_title" msgid="4628592979751918907">"WebView-implementering"</string>
+ <string name="select_webview_provider_dialog_title" msgid="4370551378720004872">"Angi WebView-implementering"</string>
<string name="convert_to_file_encryption" msgid="3060156730651061223">"Konvertér til kryptert fil"</string>
<string name="convert_to_file_encryption_enabled" msgid="2861258671151428346">"Konvertér …"</string>
<string name="convert_to_file_encryption_done" msgid="7859766358000523953">"Allerede kryptert og lagret som fil"</string>
diff --git a/packages/SettingsLib/res/values-ne-rNP/strings.xml b/packages/SettingsLib/res/values-ne-rNP/strings.xml
index f859287..5c85c25 100644
--- a/packages/SettingsLib/res/values-ne-rNP/strings.xml
+++ b/packages/SettingsLib/res/values-ne-rNP/strings.xml
@@ -273,6 +273,8 @@
<string name="night_mode_no" msgid="9171772244775838901">"असक्षम गरियो"</string>
<string name="night_mode_yes" msgid="2218157265997633432">"सधैं खुल्ला"</string>
<string name="night_mode_auto" msgid="7508348175804304327">"स्वचालित"</string>
+ <string name="select_webview_provider_title" msgid="4628592979751918907">"WebView कार्यान्वयन"</string>
+ <string name="select_webview_provider_dialog_title" msgid="4370551378720004872">"WebView कार्यान्वयन सेट गर्नुहोस्"</string>
<string name="convert_to_file_encryption" msgid="3060156730651061223">"फाइल इन्क्रिप्सनमा रूपान्तरण गर्नुहोस्"</string>
<string name="convert_to_file_encryption_enabled" msgid="2861258671151428346">"रुपान्तरण गर्नुहोस्…"</string>
<string name="convert_to_file_encryption_done" msgid="7859766358000523953">"पहिल्यै फाइल इन्क्रिप्ट गरिएको छ"</string>
diff --git a/packages/SettingsLib/res/values-nl/strings.xml b/packages/SettingsLib/res/values-nl/strings.xml
index 49242f9..1afa444 100644
--- a/packages/SettingsLib/res/values-nl/strings.xml
+++ b/packages/SettingsLib/res/values-nl/strings.xml
@@ -273,6 +273,8 @@
<string name="night_mode_no" msgid="9171772244775838901">"Uitgeschakeld"</string>
<string name="night_mode_yes" msgid="2218157265997633432">"Altijd aan"</string>
<string name="night_mode_auto" msgid="7508348175804304327">"Automatisch"</string>
+ <string name="select_webview_provider_title" msgid="4628592979751918907">"WebView-implementatie"</string>
+ <string name="select_webview_provider_dialog_title" msgid="4370551378720004872">"WebView-implementatie instellen"</string>
<string name="convert_to_file_encryption" msgid="3060156730651061223">"Converteren naar versleuteling op basis van bestanden"</string>
<string name="convert_to_file_encryption_enabled" msgid="2861258671151428346">"Converteren…"</string>
<string name="convert_to_file_encryption_done" msgid="7859766358000523953">"Al versleuteld op basis van bestanden"</string>
diff --git a/packages/SettingsLib/res/values-pa-rIN/strings.xml b/packages/SettingsLib/res/values-pa-rIN/strings.xml
index cbe196c..ad3d3ed 100644
--- a/packages/SettingsLib/res/values-pa-rIN/strings.xml
+++ b/packages/SettingsLib/res/values-pa-rIN/strings.xml
@@ -273,6 +273,8 @@
<string name="night_mode_no" msgid="9171772244775838901">"ਅਸਮਰੱਥ ਬਣਾਇਆ"</string>
<string name="night_mode_yes" msgid="2218157265997633432">"ਹਮੇਸ਼ਾ ਚਾਲੂ"</string>
<string name="night_mode_auto" msgid="7508348175804304327">"ਆਟੋਮੈਟਿਕ"</string>
+ <string name="select_webview_provider_title" msgid="4628592979751918907">"WebView ਅਮਲ"</string>
+ <string name="select_webview_provider_dialog_title" msgid="4370551378720004872">"WebView ਅਮਲ ਸੈੱਟ ਕਰੋ"</string>
<string name="convert_to_file_encryption" msgid="3060156730651061223">"ਫ਼ਾਈਲ ਇਨਕ੍ਰਿਪਸ਼ਨ ਵਿੱਚ ਤਬਦੀਲ ਕਰੋ"</string>
<string name="convert_to_file_encryption_enabled" msgid="2861258671151428346">"ਤਬਦੀਲ ਕਰੋ ..."</string>
<string name="convert_to_file_encryption_done" msgid="7859766358000523953">"ਫ਼ਾਈਲ ਪਹਿਲਾਂ ਤੋਂ ਇਨਕ੍ਰਿਪਟਡ ਹੈ"</string>
diff --git a/packages/SettingsLib/res/values-pl/strings.xml b/packages/SettingsLib/res/values-pl/strings.xml
index 2efb5c9..d58070e 100644
--- a/packages/SettingsLib/res/values-pl/strings.xml
+++ b/packages/SettingsLib/res/values-pl/strings.xml
@@ -273,6 +273,8 @@
<string name="night_mode_no" msgid="9171772244775838901">"Wyłączone"</string>
<string name="night_mode_yes" msgid="2218157265997633432">"Zawsze włączone"</string>
<string name="night_mode_auto" msgid="7508348175804304327">"Automatycznie"</string>
+ <string name="select_webview_provider_title" msgid="4628592979751918907">"Implementacja WebView"</string>
+ <string name="select_webview_provider_dialog_title" msgid="4370551378720004872">"Ustaw implementację WebView"</string>
<string name="convert_to_file_encryption" msgid="3060156730651061223">"Przekształć na szyfrowanie plików"</string>
<string name="convert_to_file_encryption_enabled" msgid="2861258671151428346">"Przekształć…"</string>
<string name="convert_to_file_encryption_done" msgid="7859766358000523953">"Pliki są już zaszyfrowane"</string>
diff --git a/packages/SettingsLib/res/values-pt-rBR/strings.xml b/packages/SettingsLib/res/values-pt-rBR/strings.xml
index 28017a9..3b28043 100644
--- a/packages/SettingsLib/res/values-pt-rBR/strings.xml
+++ b/packages/SettingsLib/res/values-pt-rBR/strings.xml
@@ -273,6 +273,8 @@
<string name="night_mode_no" msgid="9171772244775838901">"Desativada"</string>
<string name="night_mode_yes" msgid="2218157265997633432">"Sempre ativada"</string>
<string name="night_mode_auto" msgid="7508348175804304327">"Automático"</string>
+ <string name="select_webview_provider_title" msgid="4628592979751918907">"Implementação do WebView"</string>
+ <string name="select_webview_provider_dialog_title" msgid="4370551378720004872">"Configurar implementação do WebView"</string>
<string name="convert_to_file_encryption" msgid="3060156730651061223">"Converter para criptografia de arquivos"</string>
<string name="convert_to_file_encryption_enabled" msgid="2861258671151428346">"Converter..."</string>
<string name="convert_to_file_encryption_done" msgid="7859766358000523953">"Já criptografado com base em arquivos"</string>
diff --git a/packages/SettingsLib/res/values-pt-rPT/strings.xml b/packages/SettingsLib/res/values-pt-rPT/strings.xml
index 2a8402e..c5f94cb 100644
--- a/packages/SettingsLib/res/values-pt-rPT/strings.xml
+++ b/packages/SettingsLib/res/values-pt-rPT/strings.xml
@@ -273,6 +273,8 @@
<string name="night_mode_no" msgid="9171772244775838901">"Desativado"</string>
<string name="night_mode_yes" msgid="2218157265997633432">"Sempre ativado"</string>
<string name="night_mode_auto" msgid="7508348175804304327">"Automático"</string>
+ <string name="select_webview_provider_title" msgid="4628592979751918907">"Implementação WebView"</string>
+ <string name="select_webview_provider_dialog_title" msgid="4370551378720004872">"Definir implementação WebView"</string>
<string name="convert_to_file_encryption" msgid="3060156730651061223">"Converter para a encriptação de ficheiros"</string>
<string name="convert_to_file_encryption_enabled" msgid="2861258671151428346">"Converter..."</string>
<string name="convert_to_file_encryption_done" msgid="7859766358000523953">"Os ficheiros já estão encriptados"</string>
diff --git a/packages/SettingsLib/res/values-pt/strings.xml b/packages/SettingsLib/res/values-pt/strings.xml
index 28017a9..3b28043 100644
--- a/packages/SettingsLib/res/values-pt/strings.xml
+++ b/packages/SettingsLib/res/values-pt/strings.xml
@@ -273,6 +273,8 @@
<string name="night_mode_no" msgid="9171772244775838901">"Desativada"</string>
<string name="night_mode_yes" msgid="2218157265997633432">"Sempre ativada"</string>
<string name="night_mode_auto" msgid="7508348175804304327">"Automático"</string>
+ <string name="select_webview_provider_title" msgid="4628592979751918907">"Implementação do WebView"</string>
+ <string name="select_webview_provider_dialog_title" msgid="4370551378720004872">"Configurar implementação do WebView"</string>
<string name="convert_to_file_encryption" msgid="3060156730651061223">"Converter para criptografia de arquivos"</string>
<string name="convert_to_file_encryption_enabled" msgid="2861258671151428346">"Converter..."</string>
<string name="convert_to_file_encryption_done" msgid="7859766358000523953">"Já criptografado com base em arquivos"</string>
diff --git a/packages/SettingsLib/res/values-ro/strings.xml b/packages/SettingsLib/res/values-ro/strings.xml
index c45ede5..c76faf0 100644
--- a/packages/SettingsLib/res/values-ro/strings.xml
+++ b/packages/SettingsLib/res/values-ro/strings.xml
@@ -273,6 +273,8 @@
<string name="night_mode_no" msgid="9171772244775838901">"Dezactivată"</string>
<string name="night_mode_yes" msgid="2218157265997633432">"Activată permanent"</string>
<string name="night_mode_auto" msgid="7508348175804304327">"Automat"</string>
+ <string name="select_webview_provider_title" msgid="4628592979751918907">"Implementare WebView"</string>
+ <string name="select_webview_provider_dialog_title" msgid="4370551378720004872">"Setați implementarea WebView"</string>
<string name="convert_to_file_encryption" msgid="3060156730651061223">"Faceți conversia la criptarea bazată pe sistemul de fișiere"</string>
<string name="convert_to_file_encryption_enabled" msgid="2861258671151428346">"Convertiți…"</string>
<string name="convert_to_file_encryption_done" msgid="7859766358000523953">"Criptarea bazată pe sistemul de fișiere este finalizată"</string>
diff --git a/packages/SettingsLib/res/values-ru/strings.xml b/packages/SettingsLib/res/values-ru/strings.xml
index 3c30b26..f1cd182 100644
--- a/packages/SettingsLib/res/values-ru/strings.xml
+++ b/packages/SettingsLib/res/values-ru/strings.xml
@@ -273,6 +273,8 @@
<string name="night_mode_no" msgid="9171772244775838901">"Отключено"</string>
<string name="night_mode_yes" msgid="2218157265997633432">"Всегда включено"</string>
<string name="night_mode_auto" msgid="7508348175804304327">"Автоматическое переключение"</string>
+ <string name="select_webview_provider_title" msgid="4628592979751918907">"Сервис WebView"</string>
+ <string name="select_webview_provider_dialog_title" msgid="4370551378720004872">"Настройки сервиса WebView"</string>
<string name="convert_to_file_encryption" msgid="3060156730651061223">"Переход к шифрованию файлов"</string>
<string name="convert_to_file_encryption_enabled" msgid="2861258671151428346">"Перейти…"</string>
<string name="convert_to_file_encryption_done" msgid="7859766358000523953">"Шифрование файлов уже включено"</string>
diff --git a/packages/SettingsLib/res/values-si-rLK/strings.xml b/packages/SettingsLib/res/values-si-rLK/strings.xml
index 1ffd840..eb13ce4 100644
--- a/packages/SettingsLib/res/values-si-rLK/strings.xml
+++ b/packages/SettingsLib/res/values-si-rLK/strings.xml
@@ -273,6 +273,8 @@
<string name="night_mode_no" msgid="9171772244775838901">"අබලයි"</string>
<string name="night_mode_yes" msgid="2218157265997633432">"සැමවිට ක්රියාත්මක"</string>
<string name="night_mode_auto" msgid="7508348175804304327">"ස්වයංක්රීය"</string>
+ <string name="select_webview_provider_title" msgid="4628592979751918907">"WebView ක්රියාත්මක කිරීම"</string>
+ <string name="select_webview_provider_dialog_title" msgid="4370551378720004872">"WebView ක්රියාත්මක කිරීම සකසන්න"</string>
<string name="convert_to_file_encryption" msgid="3060156730651061223">"ගොනු සංකේතනයට පරිවර්තනය කරන්න"</string>
<string name="convert_to_file_encryption_enabled" msgid="2861258671151428346">"පරිවර්තනය කරන්න..."</string>
<string name="convert_to_file_encryption_done" msgid="7859766358000523953">"දැනටමත් ගොනුව සංකේතනය කර ඇත"</string>
diff --git a/packages/SettingsLib/res/values-sk/strings.xml b/packages/SettingsLib/res/values-sk/strings.xml
index 9b851b9..b5b6d2a 100644
--- a/packages/SettingsLib/res/values-sk/strings.xml
+++ b/packages/SettingsLib/res/values-sk/strings.xml
@@ -273,6 +273,8 @@
<string name="night_mode_no" msgid="9171772244775838901">"Vypnuté"</string>
<string name="night_mode_yes" msgid="2218157265997633432">"Vždy zapnuté"</string>
<string name="night_mode_auto" msgid="7508348175804304327">"Automatický"</string>
+ <string name="select_webview_provider_title" msgid="4628592979751918907">"Implementácia komponenta WebView"</string>
+ <string name="select_webview_provider_dialog_title" msgid="4370551378720004872">"Nastavenie implementácie komponenta WebView"</string>
<string name="convert_to_file_encryption" msgid="3060156730651061223">"Konvertovať na šifrovanie súborov"</string>
<string name="convert_to_file_encryption_enabled" msgid="2861258671151428346">"Konvertovať…"</string>
<string name="convert_to_file_encryption_done" msgid="7859766358000523953">"Súbory sú už šifrované"</string>
diff --git a/packages/SettingsLib/res/values-sl/strings.xml b/packages/SettingsLib/res/values-sl/strings.xml
index 04918e0..cb107d7 100644
--- a/packages/SettingsLib/res/values-sl/strings.xml
+++ b/packages/SettingsLib/res/values-sl/strings.xml
@@ -273,6 +273,8 @@
<string name="night_mode_no" msgid="9171772244775838901">"Onemogočeno"</string>
<string name="night_mode_yes" msgid="2218157265997633432">"Vedno vklopljeno"</string>
<string name="night_mode_auto" msgid="7508348175804304327">"Samodejno"</string>
+ <string name="select_webview_provider_title" msgid="4628592979751918907">"Izvedba spletnega pogleda"</string>
+ <string name="select_webview_provider_dialog_title" msgid="4370551378720004872">"Nastavitev izvedbe spletnega pogleda"</string>
<string name="convert_to_file_encryption" msgid="3060156730651061223">"Preklop na šifriranje podatkov"</string>
<string name="convert_to_file_encryption_enabled" msgid="2861258671151428346">"Preklop …"</string>
<string name="convert_to_file_encryption_done" msgid="7859766358000523953">"Šifriranje podatkov je že uveljavljeno"</string>
diff --git a/packages/SettingsLib/res/values-sq-rAL/strings.xml b/packages/SettingsLib/res/values-sq-rAL/strings.xml
index 3bc769c..c176f18 100644
--- a/packages/SettingsLib/res/values-sq-rAL/strings.xml
+++ b/packages/SettingsLib/res/values-sq-rAL/strings.xml
@@ -273,6 +273,8 @@
<string name="night_mode_no" msgid="9171772244775838901">"Çaktivizuar"</string>
<string name="night_mode_yes" msgid="2218157265997633432">"Gjithmonë aktive"</string>
<string name="night_mode_auto" msgid="7508348175804304327">"Automatike"</string>
+ <string name="select_webview_provider_title" msgid="4628592979751918907">"Zbatimi i WebView"</string>
+ <string name="select_webview_provider_dialog_title" msgid="4370551378720004872">"Cakto zbatimin e WebView"</string>
<string name="convert_to_file_encryption" msgid="3060156730651061223">"Konverto në enkriptimin e skedarit"</string>
<string name="convert_to_file_encryption_enabled" msgid="2861258671151428346">"Konverto..."</string>
<string name="convert_to_file_encryption_done" msgid="7859766358000523953">"Enkriptimi i skedarit është kryer tashmë"</string>
diff --git a/packages/SettingsLib/res/values-sr/strings.xml b/packages/SettingsLib/res/values-sr/strings.xml
index 036622f..6b66094 100644
--- a/packages/SettingsLib/res/values-sr/strings.xml
+++ b/packages/SettingsLib/res/values-sr/strings.xml
@@ -273,6 +273,8 @@
<string name="night_mode_no" msgid="9171772244775838901">"Онемогућено"</string>
<string name="night_mode_yes" msgid="2218157265997633432">"Увек укључено"</string>
<string name="night_mode_auto" msgid="7508348175804304327">"Аутоматски"</string>
+ <string name="select_webview_provider_title" msgid="4628592979751918907">"Примена WebView-а"</string>
+ <string name="select_webview_provider_dialog_title" msgid="4370551378720004872">"Подесите примену WebView-а"</string>
<string name="convert_to_file_encryption" msgid="3060156730651061223">"Конвертуј у шифровање датотека"</string>
<string name="convert_to_file_encryption_enabled" msgid="2861258671151428346">"Конвертуј..."</string>
<string name="convert_to_file_encryption_done" msgid="7859766358000523953">"Већ се користи шифровање датотека"</string>
diff --git a/packages/SettingsLib/res/values-sv/strings.xml b/packages/SettingsLib/res/values-sv/strings.xml
index 2f95b06..40e1593 100644
--- a/packages/SettingsLib/res/values-sv/strings.xml
+++ b/packages/SettingsLib/res/values-sv/strings.xml
@@ -273,6 +273,8 @@
<string name="night_mode_no" msgid="9171772244775838901">"Inaktiverad"</string>
<string name="night_mode_yes" msgid="2218157265997633432">"Alltid på"</string>
<string name="night_mode_auto" msgid="7508348175804304327">"Automatiskt"</string>
+ <string name="select_webview_provider_title" msgid="4628592979751918907">"WebView-implementering"</string>
+ <string name="select_webview_provider_dialog_title" msgid="4370551378720004872">"Ange WebView-implementering"</string>
<string name="convert_to_file_encryption" msgid="3060156730651061223">"Konvertera till filkryptering"</string>
<string name="convert_to_file_encryption_enabled" msgid="2861258671151428346">"Konvertera …"</string>
<string name="convert_to_file_encryption_done" msgid="7859766358000523953">"Filkryptering används redan"</string>
diff --git a/packages/SettingsLib/res/values-sw/strings.xml b/packages/SettingsLib/res/values-sw/strings.xml
index c4adcc9..a014e1d 100644
--- a/packages/SettingsLib/res/values-sw/strings.xml
+++ b/packages/SettingsLib/res/values-sw/strings.xml
@@ -273,6 +273,8 @@
<string name="night_mode_no" msgid="9171772244775838901">"Imezimwa"</string>
<string name="night_mode_yes" msgid="2218157265997633432">"Imewashwa kila wakati"</string>
<string name="night_mode_auto" msgid="7508348175804304327">"Otomatiki"</string>
+ <string name="select_webview_provider_title" msgid="4628592979751918907">"Utekelezaji wa WebView"</string>
+ <string name="select_webview_provider_dialog_title" msgid="4370551378720004872">"Weka utekelezaji wa WebView"</string>
<string name="convert_to_file_encryption" msgid="3060156730651061223">"Badilisha kuwa usimbaji fiche wa faili"</string>
<string name="convert_to_file_encryption_enabled" msgid="2861258671151428346">"Badilisha..."</string>
<string name="convert_to_file_encryption_done" msgid="7859766358000523953">"Tayari faili imesimbwa kwa njia fiche"</string>
diff --git a/packages/SettingsLib/res/values-ta-rIN/strings.xml b/packages/SettingsLib/res/values-ta-rIN/strings.xml
index f4fee1f..4f6ea57 100644
--- a/packages/SettingsLib/res/values-ta-rIN/strings.xml
+++ b/packages/SettingsLib/res/values-ta-rIN/strings.xml
@@ -273,6 +273,8 @@
<string name="night_mode_no" msgid="9171772244775838901">"முடக்கப்பட்டது"</string>
<string name="night_mode_yes" msgid="2218157265997633432">"எப்போதும் இயக்கத்தில் வை"</string>
<string name="night_mode_auto" msgid="7508348175804304327">"தானியங்கு"</string>
+ <string name="select_webview_provider_title" msgid="4628592979751918907">"WebView செயல்படுத்தல்"</string>
+ <string name="select_webview_provider_dialog_title" msgid="4370551378720004872">"WebView செயல்படுத்தலை அமை"</string>
<string name="convert_to_file_encryption" msgid="3060156730651061223">"கோப்பு முறைமையாக்கத்திற்கு மாற்று"</string>
<string name="convert_to_file_encryption_enabled" msgid="2861258671151428346">"மாற்று…"</string>
<string name="convert_to_file_encryption_done" msgid="7859766358000523953">"ஏற்கனவே கோப்பு முறைமையாக்கப்பட்டது"</string>
diff --git a/packages/SettingsLib/res/values-te-rIN/strings.xml b/packages/SettingsLib/res/values-te-rIN/strings.xml
index ce7fbf2..a6d2140 100644
--- a/packages/SettingsLib/res/values-te-rIN/strings.xml
+++ b/packages/SettingsLib/res/values-te-rIN/strings.xml
@@ -273,6 +273,8 @@
<string name="night_mode_no" msgid="9171772244775838901">"నిలిపివేయబడింది"</string>
<string name="night_mode_yes" msgid="2218157265997633432">"ఎల్లప్పుడూ ఆన్లో ఉంచు"</string>
<string name="night_mode_auto" msgid="7508348175804304327">"స్వయంచాలకం"</string>
+ <string name="select_webview_provider_title" msgid="4628592979751918907">"వెబ్ వీక్షణ అమలు"</string>
+ <string name="select_webview_provider_dialog_title" msgid="4370551378720004872">"వెబ్ వీక్షణ అమలుని సెట్ చేయండి"</string>
<string name="convert_to_file_encryption" msgid="3060156730651061223">"ఫైల్ గుప్తీకరణకు మార్చు"</string>
<string name="convert_to_file_encryption_enabled" msgid="2861258671151428346">"మార్చండి…"</string>
<string name="convert_to_file_encryption_done" msgid="7859766358000523953">"ఫైల్ ఇప్పటికే గుప్తీకరించబడింది"</string>
diff --git a/packages/SettingsLib/res/values-th/strings.xml b/packages/SettingsLib/res/values-th/strings.xml
index c91bb42..de2f33d 100644
--- a/packages/SettingsLib/res/values-th/strings.xml
+++ b/packages/SettingsLib/res/values-th/strings.xml
@@ -273,6 +273,8 @@
<string name="night_mode_no" msgid="9171772244775838901">"ปิดใช้แล้ว"</string>
<string name="night_mode_yes" msgid="2218157265997633432">"เปิดใช้เสมอ"</string>
<string name="night_mode_auto" msgid="7508348175804304327">"อัตโนมัติ"</string>
+ <string name="select_webview_provider_title" msgid="4628592979751918907">"การใช้งาน WebView"</string>
+ <string name="select_webview_provider_dialog_title" msgid="4370551378720004872">"ตั้งค่าการใช้งาน WebView"</string>
<string name="convert_to_file_encryption" msgid="3060156730651061223">"แปลงเป็นการเข้ารหัสไฟล์"</string>
<string name="convert_to_file_encryption_enabled" msgid="2861258671151428346">"แปลง…"</string>
<string name="convert_to_file_encryption_done" msgid="7859766358000523953">"เข้ารหัสไฟล์แล้ว"</string>
diff --git a/packages/SettingsLib/res/values-tl/strings.xml b/packages/SettingsLib/res/values-tl/strings.xml
index a389feb..231079b 100644
--- a/packages/SettingsLib/res/values-tl/strings.xml
+++ b/packages/SettingsLib/res/values-tl/strings.xml
@@ -273,6 +273,8 @@
<string name="night_mode_no" msgid="9171772244775838901">"Naka-disable"</string>
<string name="night_mode_yes" msgid="2218157265997633432">"Palaging naka-on"</string>
<string name="night_mode_auto" msgid="7508348175804304327">"Awtomatiko"</string>
+ <string name="select_webview_provider_title" msgid="4628592979751918907">"Pagpapatupad sa WebView"</string>
+ <string name="select_webview_provider_dialog_title" msgid="4370551378720004872">"Itakda ang pagpapatupad sa WebView"</string>
<string name="convert_to_file_encryption" msgid="3060156730651061223">"I-convert at gawing pag-encrypt ng file"</string>
<string name="convert_to_file_encryption_enabled" msgid="2861258671151428346">"I-convert..."</string>
<string name="convert_to_file_encryption_done" msgid="7859766358000523953">"Na-encrypt na ang file"</string>
diff --git a/packages/SettingsLib/res/values-tr/strings.xml b/packages/SettingsLib/res/values-tr/strings.xml
index 48ed867..c44d7f8 100644
--- a/packages/SettingsLib/res/values-tr/strings.xml
+++ b/packages/SettingsLib/res/values-tr/strings.xml
@@ -273,6 +273,8 @@
<string name="night_mode_no" msgid="9171772244775838901">"Devre dışı"</string>
<string name="night_mode_yes" msgid="2218157265997633432">"Her zaman açık"</string>
<string name="night_mode_auto" msgid="7508348175804304327">"Otomatik"</string>
+ <string name="select_webview_provider_title" msgid="4628592979751918907">"WebView kullanımı"</string>
+ <string name="select_webview_provider_dialog_title" msgid="4370551378720004872">"WebView kullanımını ayarla"</string>
<string name="convert_to_file_encryption" msgid="3060156730651061223">"Dosya şifrelemeye dönüştür"</string>
<string name="convert_to_file_encryption_enabled" msgid="2861258671151428346">"Dönüştür…"</string>
<string name="convert_to_file_encryption_done" msgid="7859766358000523953">"Dosya şifreleme zaten uygulandı"</string>
diff --git a/packages/SettingsLib/res/values-uk/strings.xml b/packages/SettingsLib/res/values-uk/strings.xml
index 5a0ddd0..d5dbe90 100644
--- a/packages/SettingsLib/res/values-uk/strings.xml
+++ b/packages/SettingsLib/res/values-uk/strings.xml
@@ -273,6 +273,8 @@
<string name="night_mode_no" msgid="9171772244775838901">"Вимкнено"</string>
<string name="night_mode_yes" msgid="2218157265997633432">"Завжди ввімкнено"</string>
<string name="night_mode_auto" msgid="7508348175804304327">"Автоматично"</string>
+ <string name="select_webview_provider_title" msgid="4628592979751918907">"Застосування WebView"</string>
+ <string name="select_webview_provider_dialog_title" msgid="4370551378720004872">"Налаштувати застосування WebView"</string>
<string name="convert_to_file_encryption" msgid="3060156730651061223">"Конвертувати в зашифрований файл"</string>
<string name="convert_to_file_encryption_enabled" msgid="2861258671151428346">"Конвертація…"</string>
<string name="convert_to_file_encryption_done" msgid="7859766358000523953">"Уже конвертовано в зашифрований файл"</string>
diff --git a/packages/SettingsLib/res/values-ur-rPK/strings.xml b/packages/SettingsLib/res/values-ur-rPK/strings.xml
index 3f6563b..4c9c178 100644
--- a/packages/SettingsLib/res/values-ur-rPK/strings.xml
+++ b/packages/SettingsLib/res/values-ur-rPK/strings.xml
@@ -273,6 +273,8 @@
<string name="night_mode_no" msgid="9171772244775838901">"غیر فعال"</string>
<string name="night_mode_yes" msgid="2218157265997633432">"ہمیشہ آن"</string>
<string name="night_mode_auto" msgid="7508348175804304327">"خودکار"</string>
+ <string name="select_webview_provider_title" msgid="4628592979751918907">"WebView کا نفاذ"</string>
+ <string name="select_webview_provider_dialog_title" msgid="4370551378720004872">"WebView کا نفاذ سیٹ کریں"</string>
<string name="convert_to_file_encryption" msgid="3060156730651061223">"فائل مرموز کاری میں بدلیں"</string>
<string name="convert_to_file_encryption_enabled" msgid="2861258671151428346">"بدلیں…"</string>
<string name="convert_to_file_encryption_done" msgid="7859766358000523953">"فائل پہلے ہی مرموز شدہ ہے"</string>
diff --git a/packages/SettingsLib/res/values-uz-rUZ/strings.xml b/packages/SettingsLib/res/values-uz-rUZ/strings.xml
index 1033bb19..d138a28 100644
--- a/packages/SettingsLib/res/values-uz-rUZ/strings.xml
+++ b/packages/SettingsLib/res/values-uz-rUZ/strings.xml
@@ -273,6 +273,8 @@
<string name="night_mode_no" msgid="9171772244775838901">"O‘chiq"</string>
<string name="night_mode_yes" msgid="2218157265997633432">"Har doim yoniq tursin"</string>
<string name="night_mode_auto" msgid="7508348175804304327">"Avtomatik"</string>
+ <string name="select_webview_provider_title" msgid="4628592979751918907">"WebView ta’minotchisi"</string>
+ <string name="select_webview_provider_dialog_title" msgid="4370551378720004872">"WebView ta’minotchisini sozlash"</string>
<string name="convert_to_file_encryption" msgid="3060156730651061223">"Faylli shifrga o‘girish"</string>
<string name="convert_to_file_encryption_enabled" msgid="2861258671151428346">"O‘girish…"</string>
<string name="convert_to_file_encryption_done" msgid="7859766358000523953">"Fayl allaqachon shifrlangan"</string>
diff --git a/packages/SettingsLib/res/values-vi/strings.xml b/packages/SettingsLib/res/values-vi/strings.xml
index e73f925..7ab8b02 100644
--- a/packages/SettingsLib/res/values-vi/strings.xml
+++ b/packages/SettingsLib/res/values-vi/strings.xml
@@ -273,6 +273,8 @@
<string name="night_mode_no" msgid="9171772244775838901">"Đã tắt"</string>
<string name="night_mode_yes" msgid="2218157265997633432">"Luôn bật"</string>
<string name="night_mode_auto" msgid="7508348175804304327">"Tự động"</string>
+ <string name="select_webview_provider_title" msgid="4628592979751918907">"Triển khai WebView"</string>
+ <string name="select_webview_provider_dialog_title" msgid="4370551378720004872">"Đặt triển khai WebView"</string>
<string name="convert_to_file_encryption" msgid="3060156730651061223">"Chuyển đổi sang mã hóa tệp"</string>
<string name="convert_to_file_encryption_enabled" msgid="2861258671151428346">"Chuyển đổi..."</string>
<string name="convert_to_file_encryption_done" msgid="7859766358000523953">"Đã mã hóa tệp"</string>
diff --git a/packages/SettingsLib/res/values-zh-rCN/strings.xml b/packages/SettingsLib/res/values-zh-rCN/strings.xml
index 886a7ce..509ff88 100644
--- a/packages/SettingsLib/res/values-zh-rCN/strings.xml
+++ b/packages/SettingsLib/res/values-zh-rCN/strings.xml
@@ -273,6 +273,8 @@
<string name="night_mode_no" msgid="9171772244775838901">"已停用"</string>
<string name="night_mode_yes" msgid="2218157265997633432">"始终开启"</string>
<string name="night_mode_auto" msgid="7508348175804304327">"自动"</string>
+ <string name="select_webview_provider_title" msgid="4628592979751918907">"WebView 实现"</string>
+ <string name="select_webview_provider_dialog_title" msgid="4370551378720004872">"设置 WebView 实现"</string>
<string name="convert_to_file_encryption" msgid="3060156730651061223">"转换为文件加密"</string>
<string name="convert_to_file_encryption_enabled" msgid="2861258671151428346">"转换…"</string>
<string name="convert_to_file_encryption_done" msgid="7859766358000523953">"文件已加密"</string>
diff --git a/packages/SettingsLib/res/values-zh-rHK/strings.xml b/packages/SettingsLib/res/values-zh-rHK/strings.xml
index afbdb31..f323667 100644
--- a/packages/SettingsLib/res/values-zh-rHK/strings.xml
+++ b/packages/SettingsLib/res/values-zh-rHK/strings.xml
@@ -273,6 +273,8 @@
<string name="night_mode_no" msgid="9171772244775838901">"已停用"</string>
<string name="night_mode_yes" msgid="2218157265997633432">"永遠開啟"</string>
<string name="night_mode_auto" msgid="7508348175804304327">"自動"</string>
+ <string name="select_webview_provider_title" msgid="4628592979751918907">"WebView 設置"</string>
+ <string name="select_webview_provider_dialog_title" msgid="4370551378720004872">"設定 WebView 設置"</string>
<string name="convert_to_file_encryption" msgid="3060156730651061223">"轉換為檔案加密"</string>
<string name="convert_to_file_encryption_enabled" msgid="2861258671151428346">"轉換…"</string>
<string name="convert_to_file_encryption_done" msgid="7859766358000523953">"已加密檔案"</string>
diff --git a/packages/SettingsLib/res/values-zh-rTW/strings.xml b/packages/SettingsLib/res/values-zh-rTW/strings.xml
index 88109a2..c6ad832 100644
--- a/packages/SettingsLib/res/values-zh-rTW/strings.xml
+++ b/packages/SettingsLib/res/values-zh-rTW/strings.xml
@@ -273,6 +273,8 @@
<string name="night_mode_no" msgid="9171772244775838901">"已停用"</string>
<string name="night_mode_yes" msgid="2218157265997633432">"一律開啟"</string>
<string name="night_mode_auto" msgid="7508348175804304327">"自動"</string>
+ <string name="select_webview_provider_title" msgid="4628592979751918907">"WebView 實作"</string>
+ <string name="select_webview_provider_dialog_title" msgid="4370551378720004872">"設定 WebView 實作"</string>
<string name="convert_to_file_encryption" msgid="3060156730651061223">"轉換成檔案加密"</string>
<string name="convert_to_file_encryption_enabled" msgid="2861258671151428346">"轉換..."</string>
<string name="convert_to_file_encryption_done" msgid="7859766358000523953">"已將檔案加密"</string>
diff --git a/packages/SettingsLib/res/values-zu/strings.xml b/packages/SettingsLib/res/values-zu/strings.xml
index 69c3c21..cb3670f 100644
--- a/packages/SettingsLib/res/values-zu/strings.xml
+++ b/packages/SettingsLib/res/values-zu/strings.xml
@@ -273,6 +273,8 @@
<string name="night_mode_no" msgid="9171772244775838901">"Kukhutshaziwe"</string>
<string name="night_mode_yes" msgid="2218157265997633432">"Njalo ivuliwe"</string>
<string name="night_mode_auto" msgid="7508348175804304327">"Okuzenzakalelayo"</string>
+ <string name="select_webview_provider_title" msgid="4628592979751918907">"Ukufakwa ke-WebView"</string>
+ <string name="select_webview_provider_dialog_title" msgid="4370551378720004872">"Sesba ukufakwa kwe-WebView"</string>
<string name="convert_to_file_encryption" msgid="3060156730651061223">"Phendulisela ekubethelweni kwefayela"</string>
<string name="convert_to_file_encryption_enabled" msgid="2861258671151428346">"Iyaphendulela..."</string>
<string name="convert_to_file_encryption_done" msgid="7859766358000523953">"Sekuvele kubethelwe ngefayela"</string>
diff --git a/packages/SettingsProvider/res/values-b+sr+Latn/defaults.xml b/packages/SettingsProvider/res/values-b+sr+Latn/defaults.xml
new file mode 100644
index 0000000..524132e
--- /dev/null
+++ b/packages/SettingsProvider/res/values-b+sr+Latn/defaults.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/**
+ * Copyright (c) 2009, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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="def_device_name" msgid="6309317409634339402">"%2$s %1$s"</string>
+ <string name="def_device_name_simple" msgid="9037785625140748221">"%1$s"</string>
+ <string name="def_nfc_payment_component" msgid="5861297439873026958"></string>
+</resources>
diff --git a/packages/SettingsProvider/res/values-b+sr+Latn/strings.xml b/packages/SettingsProvider/res/values-b+sr+Latn/strings.xml
new file mode 100644
index 0000000..337b18e
--- /dev/null
+++ b/packages/SettingsProvider/res/values-b+sr+Latn/strings.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/**
+ * Copyright (c) 2007, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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="app_label" msgid="4567566098528588863">"Podešavanja skladišta"</string>
+</resources>
diff --git a/packages/Shell/res/values-b+sr+Latn/strings.xml b/packages/Shell/res/values-b+sr+Latn/strings.xml
new file mode 100644
index 0000000..db1ebff
--- /dev/null
+++ b/packages/Shell/res/values-b+sr+Latn/strings.xml
@@ -0,0 +1,29 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="app_label" msgid="3701846017049540910">"Shell"</string>
+ <string name="bugreport_in_progress_title" msgid="7409917338223386637">"Izveštaj o grešci se generiše"</string>
+ <string name="bugreport_finished_title" msgid="2293711546892863898">"Izveštaj o grešci je snimljen"</string>
+ <string name="bugreport_finished_text" product="watch" msgid="8389172248433597683">"Prevucite ulevo da biste delili izveštaj o greškama"</string>
+ <string name="bugreport_finished_text" product="default" msgid="3559904746859400732">"Dodirnite da biste delili izveštaj o grešci"</string>
+ <string name="bugreport_confirm" msgid="5130698467795669780">"Izveštaji o greškama sadrže podatke iz različitih sistemskih datoteka evidencije, uključujući lične i privatne podatke. Delite izveštaje o greškama samo sa aplikacijama i ljudima u koje imate poverenja."</string>
+ <string name="bugreport_confirm_repeat" msgid="4926842460688645058">"Prikaži ovu poruku sledeći put"</string>
+ <string name="bugreport_storage_title" msgid="5332488144740527109">"Izveštaji o greškama"</string>
+ <string name="bugreport_unreadable_text" msgid="586517851044535486">"Datoteka izveštaja o grešci ne može da se pročita"</string>
+ <string name="bugreport_unnamed" msgid="2800582406842092709">"neimenovano"</string>
+</resources>
diff --git a/packages/SystemUI/res/drawable/recents_dismiss_dark.xml b/packages/SystemUI/res/drawable/recents_dismiss_dark.xml
index ba09ebe..951269b 100644
--- a/packages/SystemUI/res/drawable/recents_dismiss_dark.xml
+++ b/packages/SystemUI/res/drawable/recents_dismiss_dark.xml
@@ -19,6 +19,6 @@
android:viewportWidth="48.0"
android:viewportHeight="48.0">
<path
- android:fillColor="@color/recents_task_bar_dark_dismiss_color"
+ android:fillColor="@color/recents_task_bar_dark_icon_color"
android:pathData="M38.000000,12.800000l-2.799999,-2.800000 -11.200001,11.200001 -11.200000,-11.200001 -2.800000,2.800000 11.200001,11.200000 -11.200001,11.200001 2.800000,2.799999 11.200000,-11.200001 11.200001,11.200001 2.799999,-2.799999 -11.200001,-11.200001z"/>
</vector>
\ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/recents_dismiss_light.xml b/packages/SystemUI/res/drawable/recents_dismiss_light.xml
index be0825c..1f44c1c 100644
--- a/packages/SystemUI/res/drawable/recents_dismiss_light.xml
+++ b/packages/SystemUI/res/drawable/recents_dismiss_light.xml
@@ -19,6 +19,6 @@
android:viewportWidth="48.0"
android:viewportHeight="48.0">
<path
- android:fillColor="@color/recents_task_bar_light_dismiss_color"
+ android:fillColor="@color/recents_task_bar_light_icon_color"
android:pathData="M38.000000,12.800000l-2.799999,-2.800000 -11.200001,11.200001 -11.200000,-11.200001 -2.800000,2.800000 11.200001,11.200000 -11.200001,11.200001 2.800000,2.799999 11.200000,-11.200001 11.200001,11.200001 2.799999,-2.799999 -11.200001,-11.200001z"/>
</vector>
\ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/vector_drawable_place_top_left.xml b/packages/SystemUI/res/drawable/recents_move_task_freeform_dark.xml
similarity index 93%
rename from packages/SystemUI/res/drawable/vector_drawable_place_top_left.xml
rename to packages/SystemUI/res/drawable/recents_move_task_freeform_dark.xml
index feb612c..ce07b2d 100644
--- a/packages/SystemUI/res/drawable/vector_drawable_place_top_left.xml
+++ b/packages/SystemUI/res/drawable/recents_move_task_freeform_dark.xml
@@ -19,6 +19,6 @@
android:viewportWidth="24.0"
android:viewportHeight="24.0">
<path
- android:fillColor="#FF000000"
+ android:fillColor="@color/recents_task_bar_dark_icon_color"
android:pathData="M0.0,0.0l0.0,24.0l24.0,0.0L24.0,0.0L0.0,0.0zM4.0,4.0l10.0,0.0l0.0,10.0L4.0,14.0L4.0,4.0z"/>
</vector>
diff --git a/packages/SystemUI/res/drawable/vector_drawable_place_top_left.xml b/packages/SystemUI/res/drawable/recents_move_task_freeform_light.xml
similarity index 92%
copy from packages/SystemUI/res/drawable/vector_drawable_place_top_left.xml
copy to packages/SystemUI/res/drawable/recents_move_task_freeform_light.xml
index feb612c..bf452ae 100644
--- a/packages/SystemUI/res/drawable/vector_drawable_place_top_left.xml
+++ b/packages/SystemUI/res/drawable/recents_move_task_freeform_light.xml
@@ -19,6 +19,6 @@
android:viewportWidth="24.0"
android:viewportHeight="24.0">
<path
- android:fillColor="#FF000000"
+ android:fillColor="@color/recents_task_bar_light_icon_color"
android:pathData="M0.0,0.0l0.0,24.0l24.0,0.0L24.0,0.0L0.0,0.0zM4.0,4.0l10.0,0.0l0.0,10.0L4.0,14.0L4.0,4.0z"/>
</vector>
diff --git a/packages/SystemUI/res/drawable/vector_drawable_place_fullscreen.xml b/packages/SystemUI/res/drawable/recents_move_task_fullscreen_dark.xml
similarity index 81%
copy from packages/SystemUI/res/drawable/vector_drawable_place_fullscreen.xml
copy to packages/SystemUI/res/drawable/recents_move_task_fullscreen_dark.xml
index aee0b7f..4160efb 100644
--- a/packages/SystemUI/res/drawable/vector_drawable_place_fullscreen.xml
+++ b/packages/SystemUI/res/drawable/recents_move_task_fullscreen_dark.xml
@@ -19,15 +19,15 @@
android:viewportWidth="24.0"
android:viewportHeight="24.0">
<path
- android:fillColor="#FF000000"
+ android:fillColor="@color/recents_task_bar_dark_icon_color"
android:pathData="M0.0,8.0l4.0,0.0 0.0,-4.0 4.0,0.0 0.0,-4.0 -8.0,0.0z"/>
<path
- android:fillColor="#FF000000"
+ android:fillColor="@color/recents_task_bar_dark_icon_color"
android:pathData="M4.0,16.0l-4.0,0.0 0.0,8.0 8.0,0.0 0.0,-4.0 -4.0,0.0z"/>
<path
- android:fillColor="#FF000000"
+ android:fillColor="@color/recents_task_bar_dark_icon_color"
android:pathData="M16.0,0.0l0.0,4.0 4.0,0.0 0.0,4.0 4.0,0.0 0.0,-8.0z"/>
<path
- android:fillColor="#FF000000"
+ android:fillColor="@color/recents_task_bar_dark_icon_color"
android:pathData="M20.0,20.0l-4.0,0.0 0.0,4.0 8.0,0.0 0.0,-8.0 -4.0,0.0z"/>
</vector>
diff --git a/packages/SystemUI/res/drawable/vector_drawable_place_fullscreen.xml b/packages/SystemUI/res/drawable/recents_move_task_fullscreen_light.xml
similarity index 81%
rename from packages/SystemUI/res/drawable/vector_drawable_place_fullscreen.xml
rename to packages/SystemUI/res/drawable/recents_move_task_fullscreen_light.xml
index aee0b7f..f424bf6 100644
--- a/packages/SystemUI/res/drawable/vector_drawable_place_fullscreen.xml
+++ b/packages/SystemUI/res/drawable/recents_move_task_fullscreen_light.xml
@@ -19,15 +19,15 @@
android:viewportWidth="24.0"
android:viewportHeight="24.0">
<path
- android:fillColor="#FF000000"
+ android:fillColor="@color/recents_task_bar_light_icon_color"
android:pathData="M0.0,8.0l4.0,0.0 0.0,-4.0 4.0,0.0 0.0,-4.0 -8.0,0.0z"/>
<path
- android:fillColor="#FF000000"
+ android:fillColor="@color/recents_task_bar_light_icon_color"
android:pathData="M4.0,16.0l-4.0,0.0 0.0,8.0 8.0,0.0 0.0,-4.0 -4.0,0.0z"/>
<path
- android:fillColor="#FF000000"
+ android:fillColor="@color/recents_task_bar_light_icon_color"
android:pathData="M16.0,0.0l0.0,4.0 4.0,0.0 0.0,4.0 4.0,0.0 0.0,-8.0z"/>
<path
- android:fillColor="#FF000000"
+ android:fillColor="@color/recents_task_bar_light_icon_color"
android:pathData="M20.0,20.0l-4.0,0.0 0.0,4.0 8.0,0.0 0.0,-8.0 -4.0,0.0z"/>
</vector>
diff --git a/packages/SystemUI/res/drawable/vector_drawable_place_bottom.xml b/packages/SystemUI/res/drawable/vector_drawable_place_bottom.xml
deleted file mode 100644
index 14f1981..0000000
--- a/packages/SystemUI/res/drawable/vector_drawable_place_bottom.xml
+++ /dev/null
@@ -1,24 +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.
--->
-<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="#FF000000"
- android:pathData="M0.0,0.0l0.0,24.0l24.0,0.0L24.0,0.0L0.0,0.0zM4.0,10.0l16.0,0.0l0.0,10.0L4.0,20.0L4.0,10.0z"/>
-</vector>
diff --git a/packages/SystemUI/res/drawable/vector_drawable_place_bottom_left.xml b/packages/SystemUI/res/drawable/vector_drawable_place_bottom_left.xml
deleted file mode 100644
index cea6324..0000000
--- a/packages/SystemUI/res/drawable/vector_drawable_place_bottom_left.xml
+++ /dev/null
@@ -1,24 +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.
--->
-<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="#FF000000"
- android:pathData="M24.0,24.0L24.0,0.0L0.0,0.0l0.0,24.0L24.0,24.0zM4.0,10.0l10.0,0.0l0.0,10.0L4.0,20.0L4.0,10.0z"/>
-</vector>
diff --git a/packages/SystemUI/res/drawable/vector_drawable_place_bottom_right.xml b/packages/SystemUI/res/drawable/vector_drawable_place_bottom_right.xml
deleted file mode 100644
index c2ae9c8..0000000
--- a/packages/SystemUI/res/drawable/vector_drawable_place_bottom_right.xml
+++ /dev/null
@@ -1,24 +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.
--->
-<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="#FF000000"
- android:pathData="M24.0,24.0L24.0,0.0L0.0,0.0l0.0,24.0L24.0,24.0zM20.0,20.0L10.0,20.0L10.0,10.0l10.0,0.0L20.0,20.0z"/>
-</vector>
diff --git a/packages/SystemUI/res/drawable/vector_drawable_place_dock_bottom.xml b/packages/SystemUI/res/drawable/vector_drawable_place_dock_bottom.xml
deleted file mode 100644
index f11b690..0000000
--- a/packages/SystemUI/res/drawable/vector_drawable_place_dock_bottom.xml
+++ /dev/null
@@ -1,24 +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.
--->
-<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="#FF0000FF"
- android:pathData="M0.0,0.0l0.0,24.0l24.0,0.0L24.0,0.0L0.0,0.0zM4.0,10.0l16.0,0.0l0.0,10.0L4.0,20.0L4.0,10.0z"/>
-</vector>
diff --git a/packages/SystemUI/res/drawable/vector_drawable_place_dock_left.xml b/packages/SystemUI/res/drawable/vector_drawable_place_dock_left.xml
deleted file mode 100644
index 79ade42..0000000
--- a/packages/SystemUI/res/drawable/vector_drawable_place_dock_left.xml
+++ /dev/null
@@ -1,24 +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.
--->
-<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="#FF0000FF"
- android:pathData="M24.0,0.0L0.0,0.0l0.0,24.0l24.0,0.0L24.0,0.0zM14.0,4.0l0.0,16.0L4.0,20.0L4.0,4.0L14.0,4.0z"/>
-</vector>
diff --git a/packages/SystemUI/res/drawable/vector_drawable_place_dock_right.xml b/packages/SystemUI/res/drawable/vector_drawable_place_dock_right.xml
deleted file mode 100644
index 49c2a38..0000000
--- a/packages/SystemUI/res/drawable/vector_drawable_place_dock_right.xml
+++ /dev/null
@@ -1,24 +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.
--->
-<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="#FF0000FF"
- android:pathData="M0.0,24.0l24.0,0.0L24.0,0.0L0.0,0.0L0.0,24.0zM10.0,20.0L10.0,4.0l10.0,0.0l0.0,16.0L10.0,20.0z"/>
-</vector>
diff --git a/packages/SystemUI/res/drawable/vector_drawable_place_dock_top.xml b/packages/SystemUI/res/drawable/vector_drawable_place_dock_top.xml
deleted file mode 100644
index c3abec2..0000000
--- a/packages/SystemUI/res/drawable/vector_drawable_place_dock_top.xml
+++ /dev/null
@@ -1,24 +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.
--->
-<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="#FF0000FF"
- android:pathData="M24.0,24.0L24.0,0.0L0.0,0.0l0.0,24.0L24.0,24.0zM20.0,14.0L4.0,14.0L4.0,4.0l16.0,0.0L20.0,14.0z"/>
-</vector>
diff --git a/packages/SystemUI/res/drawable/vector_drawable_place_right.xml b/packages/SystemUI/res/drawable/vector_drawable_place_right.xml
deleted file mode 100644
index 86730db..0000000
--- a/packages/SystemUI/res/drawable/vector_drawable_place_right.xml
+++ /dev/null
@@ -1,24 +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.
--->
-<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="#FF000000"
- android:pathData="M0.0,24.0l24.0,0.0L24.0,0.0L0.0,0.0L0.0,24.0zM10.0,20.0L10.0,4.0l10.0,0.0l0.0,16.0L10.0,20.0z"/>
-</vector>
diff --git a/packages/SystemUI/res/drawable/vector_drawable_place_top.xml b/packages/SystemUI/res/drawable/vector_drawable_place_top.xml
deleted file mode 100644
index 92e01af..0000000
--- a/packages/SystemUI/res/drawable/vector_drawable_place_top.xml
+++ /dev/null
@@ -1,24 +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.
--->
-<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="#FF000000"
- android:pathData="M24.0,24.0L24.0,0.0L0.0,0.0l0.0,24.0L24.0,24.0zM20.0,14.0L4.0,14.0L4.0,4.0l16.0,0.0L20.0,14.0z"/>
-</vector>
diff --git a/packages/SystemUI/res/drawable/vector_drawable_place_top_right.xml b/packages/SystemUI/res/drawable/vector_drawable_place_top_right.xml
deleted file mode 100644
index 9f4ee49..0000000
--- a/packages/SystemUI/res/drawable/vector_drawable_place_top_right.xml
+++ /dev/null
@@ -1,24 +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.
--->
-<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="#FF000000"
- android:pathData="M0.0,0.0l0.0,24.0l24.0,0.0L24.0,0.0L0.0,0.0zM20.0,14.0L10.0,14.0L10.0,4.0l10.0,0.0L20.0,14.0z"/>
-</vector>
diff --git a/packages/SystemUI/res/layout-land/recents_task_resize_dialog.xml b/packages/SystemUI/res/layout-land/recents_task_resize_dialog.xml
deleted file mode 100644
index 0a4c086..0000000
--- a/packages/SystemUI/res/layout-land/recents_task_resize_dialog.xml
+++ /dev/null
@@ -1,65 +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.
--->
-
-<LinearLayout
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:padding="16dp"
- android:orientation="vertical"
- android:descendantFocusability="beforeDescendants"
- android:focusableInTouchMode="true">
- <LinearLayout
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:orientation="horizontal">
- <Button
- android:id="@+id/place_dock_left"
- android:layout_width="36dp"
- android:layout_height="36dp"
- android:layout_weight="1"
- android:layout_margin="10dp"
- android:background="@drawable/vector_drawable_place_dock_left" />
- <Button
- android:id="@+id/place_dock_right"
- android:layout_width="36dp"
- android:layout_height="36dp"
- android:layout_weight="1"
- android:layout_margin="10dp"
- android:background="@drawable/vector_drawable_place_dock_right" />
- <Button
- android:id="@+id/place_left"
- android:layout_width="36dp"
- android:layout_height="36dp"
- android:layout_weight="1"
- android:layout_margin="10dp"
- android:background="@drawable/vector_drawable_place_left" />
- <Button
- android:id="@+id/place_right"
- android:layout_width="36dp"
- android:layout_height="36dp"
- android:layout_weight="1"
- android:layout_margin="10dp"
- android:background="@drawable/vector_drawable_place_right" />
- <Button
- android:id="@+id/place_full"
- android:layout_width="36dp"
- android:layout_height="36dp"
- android:layout_weight="1"
- android:layout_margin="10dp"
- android:background="@drawable/vector_drawable_place_fullscreen" />
- </LinearLayout>
-</LinearLayout>
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout-port/recents_task_resize_dialog.xml b/packages/SystemUI/res/layout-port/recents_task_resize_dialog.xml
deleted file mode 100644
index bf5207a..0000000
--- a/packages/SystemUI/res/layout-port/recents_task_resize_dialog.xml
+++ /dev/null
@@ -1,65 +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.
--->
-
-<LinearLayout
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:padding="16dp"
- android:orientation="vertical"
- android:descendantFocusability="beforeDescendants"
- android:focusableInTouchMode="true">
- <LinearLayout
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:orientation="horizontal">
- <Button
- android:id="@+id/place_dock_top"
- android:layout_width="36dp"
- android:layout_height="36dp"
- android:layout_weight="1"
- android:layout_margin="10dp"
- android:background="@drawable/vector_drawable_place_dock_top" />
- <Button
- android:id="@+id/place_dock_bottom"
- android:layout_width="36dp"
- android:layout_height="36dp"
- android:layout_weight="1"
- android:layout_margin="10dp"
- android:background="@drawable/vector_drawable_place_dock_bottom" />
- <Button
- android:id="@+id/place_top"
- android:layout_width="36dp"
- android:layout_height="36dp"
- android:layout_weight="1"
- android:layout_margin="10dp"
- android:background="@drawable/vector_drawable_place_top" />
- <Button
- android:id="@+id/place_bottom"
- android:layout_width="36dp"
- android:layout_height="36dp"
- android:layout_weight="1"
- android:layout_margin="10dp"
- android:background="@drawable/vector_drawable_place_bottom" />
- <Button
- android:id="@+id/place_full"
- android:layout_width="36dp"
- android:layout_height="36dp"
- android:layout_weight="1"
- android:layout_margin="10dp"
- android:background="@drawable/vector_drawable_place_fullscreen" />
- </LinearLayout>
-</LinearLayout>
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout-sw600dp-land/recents_task_resize_dialog.xml b/packages/SystemUI/res/layout-sw600dp-land/recents_task_resize_dialog.xml
deleted file mode 100644
index 6e92afc..0000000
--- a/packages/SystemUI/res/layout-sw600dp-land/recents_task_resize_dialog.xml
+++ /dev/null
@@ -1,93 +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.
--->
-
-<LinearLayout
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:padding="16dp"
- android:orientation="vertical"
- android:descendantFocusability="beforeDescendants"
- android:focusableInTouchMode="true">
- <LinearLayout
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:orientation="horizontal">
- <Button
- android:id="@+id/place_dock_left"
- android:layout_width="36dp"
- android:layout_height="36dp"
- android:layout_weight="1"
- android:layout_margin="10dp"
- android:background="@drawable/vector_drawable_place_dock_left" />
- <Button
- android:id="@+id/place_dock_right"
- android:layout_width="36dp"
- android:layout_height="36dp"
- android:layout_weight="1"
- android:layout_margin="10dp"
- android:background="@drawable/vector_drawable_place_dock_right" />
- <Button
- android:id="@+id/place_left"
- android:layout_width="36dp"
- android:layout_height="36dp"
- android:layout_weight="1"
- android:layout_margin="10dp"
- android:background="@drawable/vector_drawable_place_left" />
- <Button
- android:id="@+id/place_right"
- android:layout_width="36dp"
- android:layout_height="36dp"
- android:layout_weight="1"
- android:layout_margin="10dp"
- android:background="@drawable/vector_drawable_place_right" />
- <Button
- android:id="@+id/place_top_left"
- android:layout_width="36dp"
- android:layout_height="36dp"
- android:layout_weight="1"
- android:layout_margin="10dp"
- android:background="@drawable/vector_drawable_place_top_left" />
- <Button
- android:id="@+id/place_top_right"
- android:layout_width="36dp"
- android:layout_height="36dp"
- android:layout_weight="1"
- android:layout_margin="10dp"
- android:background="@drawable/vector_drawable_place_top_right" />
- <Button
- android:id="@+id/place_bottom_left"
- android:layout_width="36dp"
- android:layout_height="36dp"
- android:layout_weight="1"
- android:layout_margin="10dp"
- android:background="@drawable/vector_drawable_place_bottom_left" />
- <Button
- android:id="@+id/place_bottom_right"
- android:layout_width="36dp"
- android:layout_height="36dp"
- android:layout_weight="1"
- android:layout_margin="10dp"
- android:background="@drawable/vector_drawable_place_bottom_right" />
- <Button
- android:id="@+id/place_full"
- android:layout_width="36dp"
- android:layout_height="36dp"
- android:layout_weight="1"
- android:layout_margin="10dp"
- android:background="@drawable/vector_drawable_place_fullscreen" />
- </LinearLayout>
-</LinearLayout>
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout-sw600dp-port/recents_task_resize_dialog.xml b/packages/SystemUI/res/layout-sw600dp-port/recents_task_resize_dialog.xml
deleted file mode 100644
index faa5f4b..0000000
--- a/packages/SystemUI/res/layout-sw600dp-port/recents_task_resize_dialog.xml
+++ /dev/null
@@ -1,93 +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.
--->
-
-<LinearLayout
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:padding="16dp"
- android:orientation="vertical"
- android:descendantFocusability="beforeDescendants"
- android:focusableInTouchMode="true">
- <LinearLayout
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:orientation="horizontal">
- <Button
- android:id="@+id/place_dock_top"
- android:layout_width="36dp"
- android:layout_height="36dp"
- android:layout_weight="1"
- android:layout_margin="10dp"
- android:background="@drawable/vector_drawable_place_dock_top" />
- <Button
- android:id="@+id/place_dock_bottom"
- android:layout_width="36dp"
- android:layout_height="36dp"
- android:layout_weight="1"
- android:layout_margin="10dp"
- android:background="@drawable/vector_drawable_place_dock_bottom" />
- <Button
- android:id="@+id/place_top"
- android:layout_width="36dp"
- android:layout_height="36dp"
- android:layout_weight="1"
- android:layout_margin="10dp"
- android:background="@drawable/vector_drawable_place_top" />
- <Button
- android:id="@+id/place_bottom"
- android:layout_width="36dp"
- android:layout_height="36dp"
- android:layout_weight="1"
- android:layout_margin="10dp"
- android:background="@drawable/vector_drawable_place_bottom" />
- <Button
- android:id="@+id/place_top_left"
- android:layout_width="36dp"
- android:layout_height="36dp"
- android:layout_weight="1"
- android:layout_margin="10dp"
- android:background="@drawable/vector_drawable_place_top_left" />
- <Button
- android:id="@+id/place_top_right"
- android:layout_width="36dp"
- android:layout_height="36dp"
- android:layout_weight="1"
- android:layout_margin="10dp"
- android:background="@drawable/vector_drawable_place_top_right" />
- <Button
- android:id="@+id/place_bottom_left"
- android:layout_width="36dp"
- android:layout_height="36dp"
- android:layout_weight="1"
- android:layout_margin="10dp"
- android:background="@drawable/vector_drawable_place_bottom_left" />
- <Button
- android:id="@+id/place_bottom_right"
- android:layout_width="36dp"
- android:layout_height="36dp"
- android:layout_weight="1"
- android:layout_margin="10dp"
- android:background="@drawable/vector_drawable_place_bottom_right" />
- <Button
- android:id="@+id/place_full"
- android:layout_width="36dp"
- android:layout_height="36dp"
- android:layout_weight="1"
- android:layout_margin="10dp"
- android:background="@drawable/vector_drawable_place_fullscreen" />
- </LinearLayout>
-</LinearLayout>
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/recents.xml b/packages/SystemUI/res/layout/recents.xml
index 2c010dd..16ff14c 100644
--- a/packages/SystemUI/res/layout/recents.xml
+++ b/packages/SystemUI/res/layout/recents.xml
@@ -33,12 +33,6 @@
android:layout_height="match_parent">
</com.android.systemui.recents.views.RecentsView>
- <!-- Empty View -->
- <ViewStub android:id="@+id/empty_view_stub"
- android:layout="@layout/recents_empty"
- android:layout_width="match_parent"
- android:layout_height="match_parent" />
-
<!-- History View -->
<ViewStub android:id="@+id/history_view_stub"
android:layout="@layout/recents_history"
diff --git a/packages/SystemUI/res/layout/recents_history_button.xml b/packages/SystemUI/res/layout/recents_history_button.xml
index 601c5ed..8c96fc6 100644
--- a/packages/SystemUI/res/layout/recents_history_button.xml
+++ b/packages/SystemUI/res/layout/recents_history_button.xml
@@ -26,5 +26,4 @@
android:shadowDx="0"
android:shadowDy="2"
android:shadowRadius="5"
- android:fontFamily="sans-serif-medium"
- android:visibility="invisible" />
\ No newline at end of file
+ android:fontFamily="sans-serif-medium" />
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/recents_task_view_header.xml b/packages/SystemUI/res/layout/recents_task_view_header.xml
index 2168e8b..b8caf23 100644
--- a/packages/SystemUI/res/layout/recents_task_view_header.xml
+++ b/packages/SystemUI/res/layout/recents_task_view_header.xml
@@ -34,7 +34,7 @@
android:layout_height="wrap_content"
android:layout_gravity="center_vertical|start"
android:layout_marginStart="64dp"
- android:layout_marginEnd="64dp"
+ android:layout_marginEnd="112dp"
android:textSize="16sp"
android:textColor="#ffffffff"
android:text="@string/recents_empty_message"
diff --git a/packages/SystemUI/res/values-af/strings.xml b/packages/SystemUI/res/values-af/strings.xml
index a08c9ca..0cc09e7 100644
--- a/packages/SystemUI/res/values-af/strings.xml
+++ b/packages/SystemUI/res/values-af/strings.xml
@@ -301,7 +301,7 @@
<string name="recents_search_bar_label" msgid="8074997400187836677">"soek"</string>
<string name="recents_launch_error_message" msgid="2969287838120550506">"Kon nie <xliff:g id="APP">%s</xliff:g> begin nie."</string>
<string name="recents_show_history_button_label" msgid="7062088196449747245">"Meer"</string>
- <string name="recents_history_label" msgid="3076213823382198287">"Geskiedenis"</string>
+ <string name="recents_history_label_format" msgid="6337155608055062429">"<xliff:g id="NUMBER">%d</xliff:g> meer"</string>
<string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="8848514474543427332">"Verdeel horisontaal"</string>
<string name="recents_multistack_add_stack_dialog_split_vertical" msgid="9075292233696180813">"Verdeel vertikaal"</string>
<string name="recents_multistack_add_stack_dialog_split_custom" msgid="4177837597513701943">"Verdeel gepasmaak"</string>
diff --git a/packages/SystemUI/res/values-am/strings.xml b/packages/SystemUI/res/values-am/strings.xml
index 4591f24..a6c1376 100644
--- a/packages/SystemUI/res/values-am/strings.xml
+++ b/packages/SystemUI/res/values-am/strings.xml
@@ -301,7 +301,8 @@
<string name="recents_search_bar_label" msgid="8074997400187836677">"ፈልግ"</string>
<string name="recents_launch_error_message" msgid="2969287838120550506">"<xliff:g id="APP">%s</xliff:g>ን መጀመር አልተቻለም።"</string>
<string name="recents_show_history_button_label" msgid="7062088196449747245">"ተጨማሪ"</string>
- <string name="recents_history_label" msgid="3076213823382198287">"ታሪክ"</string>
+ <!-- no translation found for recents_history_label_format (6337155608055062429) -->
+ <skip />
<string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="8848514474543427332">"አግድም ክፈል"</string>
<string name="recents_multistack_add_stack_dialog_split_vertical" msgid="9075292233696180813">"ቁልቁል ክፈል"</string>
<string name="recents_multistack_add_stack_dialog_split_custom" msgid="4177837597513701943">"በብጁ ክፈል"</string>
diff --git a/packages/SystemUI/res/values-ar/strings.xml b/packages/SystemUI/res/values-ar/strings.xml
index beeb923..bb8602b 100644
--- a/packages/SystemUI/res/values-ar/strings.xml
+++ b/packages/SystemUI/res/values-ar/strings.xml
@@ -305,7 +305,8 @@
<string name="recents_search_bar_label" msgid="8074997400187836677">"بحث"</string>
<string name="recents_launch_error_message" msgid="2969287838120550506">"تعذر بدء <xliff:g id="APP">%s</xliff:g>."</string>
<string name="recents_show_history_button_label" msgid="7062088196449747245">"المزيد"</string>
- <string name="recents_history_label" msgid="3076213823382198287">"السجلّ"</string>
+ <!-- no translation found for recents_history_label_format (6337155608055062429) -->
+ <skip />
<string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="8848514474543427332">"تقسيم أفقي"</string>
<string name="recents_multistack_add_stack_dialog_split_vertical" msgid="9075292233696180813">"تقسيم رأسي"</string>
<string name="recents_multistack_add_stack_dialog_split_custom" msgid="4177837597513701943">"تقسيم مخصص"</string>
diff --git a/packages/SystemUI/res/values-az-rAZ/strings.xml b/packages/SystemUI/res/values-az-rAZ/strings.xml
index b2026a8..f6a5f0f 100644
--- a/packages/SystemUI/res/values-az-rAZ/strings.xml
+++ b/packages/SystemUI/res/values-az-rAZ/strings.xml
@@ -301,7 +301,8 @@
<string name="recents_search_bar_label" msgid="8074997400187836677">"axtarış"</string>
<string name="recents_launch_error_message" msgid="2969287838120550506">"<xliff:g id="APP">%s</xliff:g> başlana bilmir."</string>
<string name="recents_show_history_button_label" msgid="7062088196449747245">"Daha çox"</string>
- <string name="recents_history_label" msgid="3076213823382198287">"Tarixçə"</string>
+ <!-- no translation found for recents_history_label_format (6337155608055062429) -->
+ <skip />
<string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="8848514474543427332">"Üfüqi Böl"</string>
<string name="recents_multistack_add_stack_dialog_split_vertical" msgid="9075292233696180813">"Şaquli Böl"</string>
<string name="recents_multistack_add_stack_dialog_split_custom" msgid="4177837597513701943">"Fərdi Böl"</string>
diff --git a/packages/SystemUI/res/values-b+sr+Latn-land/strings.xml b/packages/SystemUI/res/values-b+sr+Latn-land/strings.xml
new file mode 100644
index 0000000..8ebbb0b
--- /dev/null
+++ b/packages/SystemUI/res/values-b+sr+Latn-land/strings.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/**
+ * 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.
+ */
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="toast_rotation_locked" msgid="7609673011431556092">"Ekran je sada zaključan u vertikalnom položaju."</string>
+</resources>
diff --git a/packages/SystemUI/res/values-b+sr+Latn/strings.xml b/packages/SystemUI/res/values-b+sr+Latn/strings.xml
new file mode 100644
index 0000000..f14b644
--- /dev/null
+++ b/packages/SystemUI/res/values-b+sr+Latn/strings.xml
@@ -0,0 +1,454 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/**
+ * Copyright (c) 2009, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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="app_label" msgid="7164937344850004466">"UI sistema"</string>
+ <string name="status_bar_clear_all_button" msgid="7774721344716731603">"Obriši"</string>
+ <string name="status_bar_recent_remove_item_title" msgid="6026395868129852968">"Uklanjanje sa liste"</string>
+ <string name="status_bar_recent_inspect_item_title" msgid="7793624864528818569">"Informacije o aplikaciji"</string>
+ <string name="status_bar_no_recent_apps" msgid="7374907845131203189">"Nedavni ekrani se pojavljuju ovde"</string>
+ <string name="status_bar_accessibility_dismiss_recents" msgid="4576076075226540105">"Odbaci nedavne aplikacije"</string>
+ <plurals name="status_bar_accessibility_recent_apps" formatted="false" msgid="9138535907802238759">
+ <item quantity="one">%d ekran u Pregledu</item>
+ <item quantity="few">%d ekrana u Pregledu</item>
+ <item quantity="other">%d ekrana u Pregledu</item>
+ </plurals>
+ <string name="status_bar_no_notifications_title" msgid="4755261167193833213">"Nema obaveštenja"</string>
+ <string name="status_bar_ongoing_events_title" msgid="1682504513316879202">"Tekuće"</string>
+ <string name="status_bar_latest_events_title" msgid="6594767438577593172">"Obaveštenja"</string>
+ <string name="battery_low_title" msgid="6456385927409742437">"Nivo napunjenosti baterije je nizak"</string>
+ <string name="battery_low_percent_format" msgid="2900940511201380775">"Još <xliff:g id="PERCENTAGE">%s</xliff:g>"</string>
+ <string name="battery_low_percent_format_saver_started" msgid="6859235584035338833">"Još <xliff:g id="PERCENTAGE">%s</xliff:g>. Uključena je štednja baterije."</string>
+ <string name="invalid_charger" msgid="4549105996740522523">"Punjenje preko USB-a nije podržano.\nKoristite samo priloženi punjač."</string>
+ <string name="invalid_charger_title" msgid="3515740382572798460">"Punjenje preko USB-a nije podržano."</string>
+ <string name="invalid_charger_text" msgid="5474997287953892710">"Koristite samo punjač koji ste dobili."</string>
+ <string name="battery_low_why" msgid="4553600287639198111">"Podešavanja"</string>
+ <string name="battery_saver_confirmation_title" msgid="5299585433050361634">"Želite li da uključite štednju baterije?"</string>
+ <string name="battery_saver_confirmation_ok" msgid="7507968430447930257">"Uključi"</string>
+ <string name="battery_saver_start_action" msgid="5576697451677486320">"Uključi štednju baterije"</string>
+ <string name="status_bar_settings_settings_button" msgid="3023889916699270224">"Podešavanja"</string>
+ <string name="status_bar_settings_wifi_button" msgid="1733928151698311923">"Wi-Fi"</string>
+ <string name="status_bar_settings_auto_rotation" msgid="3790482541357798421">"Automatsko rotiranje ekrana"</string>
+ <string name="status_bar_settings_mute_label" msgid="554682549917429396">"UGASI"</string>
+ <string name="status_bar_settings_auto_brightness_label" msgid="511453614962324674">"AUTOM."</string>
+ <string name="status_bar_settings_notifications" msgid="397146176280905137">"Obaveštenja"</string>
+ <string name="bluetooth_tethered" msgid="7094101612161133267">"Veza preko Bluetooth-a"</string>
+ <string name="status_bar_input_method_settings_configure_input_methods" msgid="3504292471512317827">"Podesi metode unosa"</string>
+ <string name="status_bar_use_physical_keyboard" msgid="7551903084416057810">"Fizička tastatura"</string>
+ <string name="usb_device_permission_prompt" msgid="834698001271562057">"Želite li da dozvolite aplikaciji <xliff:g id="APPLICATION">%1$s</xliff:g> da pristupa USB uređaju?"</string>
+ <string name="usb_accessory_permission_prompt" msgid="5171775411178865750">"Želite li da dozvolite aplikaciji <xliff:g id="APPLICATION">%1$s</xliff:g> da pristupa USB pomoćnom uređaju?"</string>
+ <string name="usb_device_confirm_prompt" msgid="5161205258635253206">"Želite li da se otvori <xliff:g id="ACTIVITY">%1$s</xliff:g> kada se priključi ovaj USB uređaj?"</string>
+ <string name="usb_accessory_confirm_prompt" msgid="3808984931830229888">"Želite li da se otvori <xliff:g id="ACTIVITY">%1$s</xliff:g> kada se priključi ovaj USB dodatak?"</string>
+ <string name="usb_accessory_uri_prompt" msgid="513450621413733343">"Instalirane aplikacije ne funkcionišu sa ovim USB pomoćnim uređajem. Saznajte više o njemu na adresi <xliff:g id="URL">%1$s</xliff:g>"</string>
+ <string name="title_usb_accessory" msgid="4966265263465181372">"USB pomoćni uređaj"</string>
+ <string name="label_view" msgid="6304565553218192990">"Prikaži"</string>
+ <string name="always_use_device" msgid="1450287437017315906">"Koristi podrazumevano za ovaj USB uređaj"</string>
+ <string name="always_use_accessory" msgid="1210954576979621596">"Koristi podrazumevano za ovaj USB dodatak"</string>
+ <string name="usb_debugging_title" msgid="4513918393387141949">"Želite li da dozvolite otklanjanje USB grešaka?"</string>
+ <string name="usb_debugging_message" msgid="2220143855912376496">"Digitalni otisak RSA ključa ovog računara je:\n<xliff:g id="FINGERPRINT">%1$s</xliff:g>"</string>
+ <string name="usb_debugging_always" msgid="303335496705863070">"Uvek dozvoli sa ovog računara"</string>
+ <string name="usb_debugging_secondary_user_title" msgid="6353808721761220421">"Otklanjanje grešaka na USB-u nije dozvoljeno"</string>
+ <string name="usb_debugging_secondary_user_message" msgid="8572228137833020196">"Korisnik koji je trenutno prijavljen na ovaj uređaj ne može da uključi otklanjanje grešaka na USB-u. Da biste koristili ovu funkciju, prebacite na korisnika sa administratorskim pravima."</string>
+ <string name="compat_mode_on" msgid="6623839244840638213">"Zumiraj na celom ekranu"</string>
+ <string name="compat_mode_off" msgid="4434467572461327898">"Razvuci na ceo ekran"</string>
+ <string name="screenshot_saving_ticker" msgid="7403652894056693515">"Čuvanje snimka ekrana..."</string>
+ <string name="screenshot_saving_title" msgid="8242282144535555697">"Čuvanje snimka ekrana..."</string>
+ <string name="screenshot_saving_text" msgid="2419718443411738818">"Snimak ekrana se čuva."</string>
+ <string name="screenshot_saved_title" msgid="6461865960961414961">"Snimak ekrana je napravljen."</string>
+ <string name="screenshot_saved_text" msgid="1152839647677558815">"Dodirnite da biste videli snimak ekrana."</string>
+ <string name="screenshot_failed_title" msgid="705781116746922771">"Nije moguće napraviti snimak ekrana."</string>
+ <string name="screenshot_failed_text" msgid="1260203058661337274">"Nije moguće snimiti ekran zbog nedovoljne memorije ili to ne dozvoljava aplikacija ili organizacija."</string>
+ <string name="usb_preference_title" msgid="6551050377388882787">"Opcije USB prenosa datoteka"</string>
+ <string name="use_mtp_button_title" msgid="4333504413563023626">"Priključi kao medija plejer (MTP)"</string>
+ <string name="use_ptp_button_title" msgid="7517127540301625751">"Priključi kao kameru (PTP)"</string>
+ <string name="installer_cd_button_title" msgid="2312667578562201583">"Instaliraj Android prebacivanje datoteka za Mac"</string>
+ <string name="accessibility_back" msgid="567011538994429120">"Nazad"</string>
+ <string name="accessibility_home" msgid="8217216074895377641">"Početna"</string>
+ <string name="accessibility_menu" msgid="316839303324695949">"Meni"</string>
+ <string name="accessibility_recent" msgid="5208608566793607626">"Pregled"</string>
+ <string name="accessibility_search_light" msgid="1103867596330271848">"Pretražite"</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">"Glasovna pomoć"</string>
+ <string name="accessibility_unlock_button" msgid="128158454631118828">"Otključajte"</string>
+ <string name="accessibility_unlock_button_fingerprint" msgid="8214125623493923751">"Dugme za otključavanje, čeka se na otisak prsta"</string>
+ <string name="accessibility_unlock_without_fingerprint" msgid="7541705575183694446">"Otključaj bez korišćenja otiska prsta"</string>
+ <string name="unlock_label" msgid="8779712358041029439">"otključaj"</string>
+ <string name="phone_label" msgid="2320074140205331708">"otvori telefon"</string>
+ <string name="voice_assist_label" msgid="3956854378310019854">"otvori glasovnu pomoć"</string>
+ <string name="camera_label" msgid="7261107956054836961">"otvori kameru"</string>
+ <string name="recents_caption_resize" msgid="3517056471774958200">"Izaberi novi raspored zadataka"</string>
+ <string name="cancel" msgid="6442560571259935130">"Otkaži"</string>
+ <string name="accessibility_compatibility_zoom_button" msgid="8461115318742350699">"Dugme Zum kompatibilnosti."</string>
+ <string name="accessibility_compatibility_zoom_example" msgid="4220687294564945780">"Zumiranje sa manjeg na veći ekran."</string>
+ <string name="accessibility_bluetooth_connected" msgid="2707027633242983370">"Bluetooth je priključen."</string>
+ <string name="accessibility_bluetooth_disconnected" msgid="7416648669976870175">"Bluetooth je isključen."</string>
+ <string name="accessibility_no_battery" msgid="358343022352820946">"Nema baterije."</string>
+ <string name="accessibility_battery_one_bar" msgid="7774887721891057523">"Baterija od jedne crte."</string>
+ <string name="accessibility_battery_two_bars" msgid="8500650438735009973">"Baterija od dve crte."</string>
+ <string name="accessibility_battery_three_bars" msgid="2302983330865040446">"Baterija od tri crte."</string>
+ <string name="accessibility_battery_full" msgid="8909122401720158582">"Baterija je puna."</string>
+ <string name="accessibility_no_phone" msgid="4894708937052611281">"Nema telefona."</string>
+ <string name="accessibility_phone_one_bar" msgid="687699278132664115">"Signal telefona ima jednu crtu."</string>
+ <string name="accessibility_phone_two_bars" msgid="8384905382804815201">"Signal telefona od dve crte."</string>
+ <string name="accessibility_phone_three_bars" msgid="8521904843919971885">"Signal telefona od tri crte."</string>
+ <string name="accessibility_phone_signal_full" msgid="6471834868580757898">"Signal telefona je pun."</string>
+ <string name="accessibility_no_data" msgid="4791966295096867555">"Nema podataka."</string>
+ <string name="accessibility_data_one_bar" msgid="1415625833238273628">"Signal za podatke ima jednu crtu."</string>
+ <string name="accessibility_data_two_bars" msgid="6166018492360432091">"Signal za podatke od dve crte."</string>
+ <string name="accessibility_data_three_bars" msgid="9167670452395038520">"Signal za podatke od tri crte."</string>
+ <string name="accessibility_data_signal_full" msgid="2708384608124519369">"Signal za podatke je najjači."</string>
+ <string name="accessibility_wifi_name" msgid="7202151365171148501">"Povezani ste sa <xliff:g id="WIFI">%s</xliff:g>."</string>
+ <string name="accessibility_bluetooth_name" msgid="8441517146585531676">"Povezani ste sa <xliff:g id="BLUETOOTH">%s</xliff:g>."</string>
+ <string name="accessibility_no_wimax" msgid="4329180129727630368">"Nema WiMAX signala."</string>
+ <string name="accessibility_wimax_one_bar" msgid="4170994299011863648">"WiMAX signal ima jednu crtu."</string>
+ <string name="accessibility_wimax_two_bars" msgid="9176236858336502288">"WiMAX signal ima dve crte."</string>
+ <string name="accessibility_wimax_three_bars" msgid="6116551636752103927">"WiMAX signal ima tri crte."</string>
+ <string name="accessibility_wimax_signal_full" msgid="2768089986795579558">"WiMAX signal je najjači."</string>
+ <string name="accessibility_ethernet_disconnected" msgid="5896059303377589469">"Veza sa eternetom je prekinuta."</string>
+ <string name="accessibility_ethernet_connected" msgid="2692130313069182636">"Eternet je povezan."</string>
+ <string name="accessibility_no_signal" msgid="7064645320782585167">"Nema signala."</string>
+ <string name="accessibility_not_connected" msgid="6395326276213402883">"Nije povezano."</string>
+ <string name="accessibility_zero_bars" msgid="3806060224467027887">"Nijedna crta."</string>
+ <string name="accessibility_one_bar" msgid="1685730113192081895">"Jedna crta."</string>
+ <string name="accessibility_two_bars" msgid="6437363648385206679">"Dve crte."</string>
+ <string name="accessibility_three_bars" msgid="2648241415119396648">"Tri crte."</string>
+ <string name="accessibility_signal_full" msgid="9122922886519676839">"Signal je najjači."</string>
+ <string name="accessibility_desc_on" msgid="2385254693624345265">"Uključeno."</string>
+ <string name="accessibility_desc_off" msgid="6475508157786853157">"Isključeno."</string>
+ <string name="accessibility_desc_connected" msgid="8366256693719499665">"Povezano je."</string>
+ <string name="accessibility_desc_connecting" msgid="3812924520316280149">"Povezivanje."</string>
+ <string name="accessibility_data_connection_gprs" msgid="1606477224486747751">"GPRS"</string>
+ <string name="accessibility_data_connection_1x" msgid="994133468120244018">"1 X"</string>
+ <string name="accessibility_data_connection_hspa" msgid="2032328855462645198">"HSPA"</string>
+ <string name="accessibility_data_connection_3g" msgid="8628562305003568260">"3G"</string>
+ <string name="accessibility_data_connection_3.5g" msgid="8664845609981692001">"3.5G"</string>
+ <string name="accessibility_data_connection_4g" msgid="7741000750630089612">"4G"</string>
+ <string name="accessibility_data_connection_lte" msgid="5413468808637540658">"LTE"</string>
+ <string name="accessibility_data_connection_cdma" msgid="6132648193978823023">"CDMA"</string>
+ <string name="accessibility_data_connection_roaming" msgid="5977362333466556094">"Roming"</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">"Nema SIM kartice."</string>
+ <string name="accessibility_bluetooth_tether" msgid="4102784498140271969">"Bluetooth privezivanje."</string>
+ <string name="accessibility_airplane_mode" msgid="834748999790763092">"Režim rada u avionu."</string>
+ <string name="accessibility_no_sims" msgid="3957997018324995781">"Nema SIM kartice."</string>
+ <string name="accessibility_carrier_network_change_mode" msgid="4017301580441304305">"Promena mreže mobilnog operatera."</string>
+ <string name="accessibility_battery_level" msgid="7451474187113371965">"Baterija je na <xliff:g id="NUMBER">%d</xliff:g> posto."</string>
+ <string name="accessibility_settings_button" msgid="799583911231893380">"Sistemska podešavanja."</string>
+ <string name="accessibility_notifications_button" msgid="4498000369779421892">"Obaveštenja."</string>
+ <string name="accessibility_remove_notification" msgid="3603099514902182350">"Obriši obaveštenje."</string>
+ <string name="accessibility_gps_enabled" msgid="3511469499240123019">"GPS je omogućen."</string>
+ <string name="accessibility_gps_acquiring" msgid="8959333351058967158">"Učitavanje GPS-a."</string>
+ <string name="accessibility_tty_enabled" msgid="4613200365379426561">"TeleTypewriter je omogućen."</string>
+ <string name="accessibility_ringer_vibrate" msgid="666585363364155055">"Vibracija zvona."</string>
+ <string name="accessibility_ringer_silent" msgid="9061243307939135383">"Nečujno zvono."</string>
+ <!-- no translation found for accessibility_casting (6887382141726543668) -->
+ <skip />
+ <string name="accessibility_work_mode" msgid="2478631941714607225">"Režim rada"</string>
+ <string name="accessibility_recents_item_will_be_dismissed" msgid="395770242498031481">"Odbacite <xliff:g id="APP">%s</xliff:g>."</string>
+ <string name="accessibility_recents_item_dismissed" msgid="6803574935084867070">"Aplikacija <xliff:g id="APP">%s</xliff:g> je odbačena."</string>
+ <string name="accessibility_recents_all_items_dismissed" msgid="4464697366179168836">"Sve nedavno korišćene aplikacije su odbačene."</string>
+ <string name="accessibility_recents_item_launched" msgid="7616039892382525203">"Pokrećemo <xliff:g id="APP">%s</xliff:g>."</string>
+ <string name="accessibility_recents_task_header" msgid="1437183540924535457">"<xliff:g id="APP">%1$s</xliff:g> <xliff:g id="ACTIVITY_LABEL">%2$s</xliff:g>"</string>
+ <string name="accessibility_notification_dismissed" msgid="854211387186306927">"Obaveštenje je odbačeno."</string>
+ <string name="accessibility_desc_notification_shade" msgid="4690274844447504208">"Prozor sa obaveštenjima."</string>
+ <string name="accessibility_desc_quick_settings" msgid="6186378411582437046">"Brza podešavanja."</string>
+ <string name="accessibility_desc_lock_screen" msgid="5625143713611759164">"Zaključani ekran."</string>
+ <string name="accessibility_desc_settings" msgid="3417884241751434521">"Podešavanja"</string>
+ <string name="accessibility_desc_recent_apps" msgid="4876900986661819788">"Pregled."</string>
+ <string name="accessibility_desc_close" msgid="7479755364962766729">"Zatvori"</string>
+ <string name="accessibility_quick_settings_user" msgid="1104846699869476855">"Korisnik: <xliff:g id="USER">%s</xliff:g>."</string>
+ <string name="accessibility_quick_settings_wifi" msgid="5518210213118181692">"<xliff:g id="SIGNAL">%1$s</xliff:g>."</string>
+ <string name="accessibility_quick_settings_wifi_changed_off" msgid="8716484460897819400">"Wi-Fi je isključen."</string>
+ <string name="accessibility_quick_settings_wifi_changed_on" msgid="6440117170789528622">"Wi-Fi je uključen."</string>
+ <string name="accessibility_quick_settings_mobile" msgid="4876806564086241341">"Mobilna mreža: <xliff:g id="SIGNAL">%1$s</xliff:g>. <xliff:g id="TYPE">%2$s</xliff:g>. <xliff:g id="NETWORK">%3$s</xliff:g>."</string>
+ <string name="accessibility_quick_settings_battery" msgid="1480931583381408972">"Baterija: <xliff:g id="STATE">%s</xliff:g>."</string>
+ <string name="accessibility_quick_settings_airplane_off" msgid="7786329360056634412">"Režim rada u avionu je isključen."</string>
+ <string name="accessibility_quick_settings_airplane_on" msgid="6406141469157599296">"Režim rada u avionu je uključen."</string>
+ <string name="accessibility_quick_settings_airplane_changed_off" msgid="66846307818850664">"Režim rada u avionu je isključen."</string>
+ <string name="accessibility_quick_settings_airplane_changed_on" msgid="8983005603505087728">"Režim rada u avionu je uključen."</string>
+ <string name="accessibility_quick_settings_dnd_priority_on" msgid="1448402297221249355">"Podešavanje Ne uznemiravaj je uključeno, samo prioritetni prekidi."</string>
+ <string name="accessibility_quick_settings_dnd_none_on" msgid="6882582132662613537">"Podešavanje Ne uznemiravaj je uključeno, potpuna tišina."</string>
+ <string name="accessibility_quick_settings_dnd_alarms_on" msgid="9152834845587554157">"Podešavanje Ne uznemiravaj je uključeno, samo alarmi."</string>
+ <string name="accessibility_quick_settings_dnd_off" msgid="2371832603753738581">"Podešavanje Ne uznemiravaj je isključeno."</string>
+ <string name="accessibility_quick_settings_dnd_changed_off" msgid="898107593453022935">"Podešavanje Ne uznemiravaj je isključeno."</string>
+ <string name="accessibility_quick_settings_dnd_changed_on" msgid="4483780856613561039">"Podešavanje Ne uznemiravaj je uključeno."</string>
+ <string name="accessibility_quick_settings_bluetooth_off" msgid="2133631372372064339">"Bluetooth je isključen."</string>
+ <string name="accessibility_quick_settings_bluetooth_on" msgid="7681999166216621838">"Bluetooth je uključen."</string>
+ <string name="accessibility_quick_settings_bluetooth_connecting" msgid="6953242966685343855">"Bluetooth se povezuje."</string>
+ <string name="accessibility_quick_settings_bluetooth_connected" msgid="4306637793614573659">"Bluetooth je povezan."</string>
+ <string name="accessibility_quick_settings_bluetooth_changed_off" msgid="2730003763480934529">"Bluetooth je isključen."</string>
+ <string name="accessibility_quick_settings_bluetooth_changed_on" msgid="8722351798763206577">"Bluetooth je uključen."</string>
+ <string name="accessibility_quick_settings_location_off" msgid="5119080556976115520">"Izveštavanje o lokaciji je isključeno."</string>
+ <string name="accessibility_quick_settings_location_on" msgid="5809937096590102036">"Izveštavanje o lokaciji je uključeno."</string>
+ <string name="accessibility_quick_settings_location_changed_off" msgid="8526845571503387376">"Izveštavanje o lokaciji je isključeno."</string>
+ <string name="accessibility_quick_settings_location_changed_on" msgid="339403053079338468">"Izveštavanje o lokaciji je uključeno."</string>
+ <string name="accessibility_quick_settings_alarm" msgid="3959908972897295660">"Alarm je podešen za <xliff:g id="TIME">%s</xliff:g>."</string>
+ <string name="accessibility_quick_settings_close" msgid="3115847794692516306">"Zatvorite tablu."</string>
+ <string name="accessibility_quick_settings_more_time" msgid="3659274935356197708">"Više vremena."</string>
+ <string name="accessibility_quick_settings_less_time" msgid="2404728746293515623">"Manje vremena."</string>
+ <string name="accessibility_quick_settings_flashlight_off" msgid="4936432000069786988">"Baterijska lampa je isključena."</string>
+ <string name="accessibility_quick_settings_flashlight_on" msgid="2003479320007841077">"Baterijska lampa je uključena."</string>
+ <string name="accessibility_quick_settings_flashlight_changed_off" msgid="3303701786768224304">"Baterijska lampa je isključena."</string>
+ <string name="accessibility_quick_settings_flashlight_changed_on" msgid="6531793301533894686">"Baterijska lampa je uključena."</string>
+ <string name="accessibility_quick_settings_color_inversion_changed_off" msgid="4406577213290173911">"Inverzija boja je isključena."</string>
+ <string name="accessibility_quick_settings_color_inversion_changed_on" msgid="6897462320184911126">"Inverzija boja je uključena."</string>
+ <string name="accessibility_quick_settings_hotspot_changed_off" msgid="5004708003447561394">"Mobilni hotspot je isključen."</string>
+ <string name="accessibility_quick_settings_hotspot_changed_on" msgid="2890951609226476206">"Mobilni hotspot je uključen."</string>
+ <string name="accessibility_casting_turned_off" msgid="1430668982271976172">"Prebacivanje ekrana je zaustavljeno."</string>
+ <string name="accessibility_quick_settings_work_mode_off" msgid="7045417396436552890">"Režim rada je isključen."</string>
+ <string name="accessibility_quick_settings_work_mode_on" msgid="7650588553988014341">"Režim rada je uključen."</string>
+ <string name="accessibility_quick_settings_work_mode_changed_off" msgid="5605534876107300711">"Režim rada je isključen."</string>
+ <string name="accessibility_quick_settings_work_mode_changed_on" msgid="249840330756998612">"Režim rada je uključen."</string>
+ <string name="accessibility_brightness" msgid="8003681285547803095">"Osvetljenost ekrana"</string>
+ <string name="data_usage_disabled_dialog_3g_title" msgid="5281770593459841889">"2G–3G podaci su pauzirani"</string>
+ <string name="data_usage_disabled_dialog_4g_title" msgid="1601769736881078016">"4G podaci su pauzirani"</string>
+ <string name="data_usage_disabled_dialog_mobile_title" msgid="4651001290947318931">"Mobilni podaci su pauzirani"</string>
+ <string name="data_usage_disabled_dialog_title" msgid="3932437232199671967">"Podaci su pauzirani"</string>
+ <string name="data_usage_disabled_dialog" msgid="8453242888903772524">"Zbog toga što ste dostigli podešeno ograničenje za podatke, uređaj je pauzirao korišćenje podataka tokom ostatka ovog ciklusa.\n\nAko nastavite, mobilni operater može da vam naplati dodatne troškove."</string>
+ <string name="data_usage_disabled_dialog_enable" msgid="1412395410306390593">"Nastavi"</string>
+ <string name="status_bar_settings_signal_meter_disconnected" msgid="1940231521274147771">"Nema internet veze"</string>
+ <string name="status_bar_settings_signal_meter_wifi_nossid" msgid="6557486452774597820">"Wi-Fi je povezan"</string>
+ <string name="gps_notification_searching_text" msgid="8574247005642736060">"Traži se GPS"</string>
+ <string name="gps_notification_found_text" msgid="4619274244146446464">"Lokaciju je podesio GPS"</string>
+ <string name="accessibility_location_active" msgid="2427290146138169014">"Ima aktivnih zahteva za lokaciju"</string>
+ <string name="accessibility_clear_all" msgid="5235938559247164925">"Obriši sva obaveštenja."</string>
+ <string name="status_bar_notification_inspect_item_title" msgid="5668348142410115323">"Podešavanja obaveštenja"</string>
+ <string name="status_bar_notification_app_settings_title" msgid="5525260160341558869">"Podešavanja za <xliff:g id="APP_NAME">%s</xliff:g>"</string>
+ <string name="accessibility_rotation_lock_off" msgid="4062780228931590069">"Ekran će se automatski rotirati."</string>
+ <string name="accessibility_rotation_lock_on_landscape" msgid="6731197337665366273">"Ekran je zaključan u horizontalnom položaju."</string>
+ <string name="accessibility_rotation_lock_on_portrait" msgid="5809367521644012115">"Ekran je zaključan u vertikalnom položaju."</string>
+ <string name="accessibility_rotation_lock_off_changed" msgid="8134601071026305153">"Ekran će se sada automatski rotirati."</string>
+ <string name="accessibility_rotation_lock_on_landscape_changed" msgid="3135965553707519743">"Ekran je sada zaključan u vertikalnom položaju."</string>
+ <string name="accessibility_rotation_lock_on_portrait_changed" msgid="8922481981834012126">"Ekran je sada zaključan u horizontalnom položaju."</string>
+ <string name="dessert_case" msgid="1295161776223959221">"Vitrina sa poslasticama"</string>
+ <string name="start_dreams" msgid="7219575858348719790">"Sanjarenje"</string>
+ <string name="ethernet_label" msgid="7967563676324087464">"Eternet"</string>
+ <string name="quick_settings_dnd_label" msgid="8735855737575028208">"Ne uznemiravaj"</string>
+ <string name="quick_settings_dnd_priority_label" msgid="483232950670692036">"Samo prioritetni prekidi"</string>
+ <string name="quick_settings_dnd_alarms_label" msgid="2559229444312445858">"Samo alarmi"</string>
+ <string name="quick_settings_dnd_none_label" msgid="5025477807123029478">"Potpuna tišina"</string>
+ <string name="quick_settings_bluetooth_label" msgid="6304190285170721401">"Bluetooth"</string>
+ <string name="quick_settings_bluetooth_multiple_devices_label" msgid="3912245565613684735">"Bluetooth (<xliff:g id="NUMBER">%d</xliff:g> uređaja)"</string>
+ <string name="quick_settings_bluetooth_off_label" msgid="8159652146149219937">"Bluetooth isključen"</string>
+ <string name="quick_settings_bluetooth_detail_empty_text" msgid="4910015762433302860">"Nije dostupan nijedan upareni uređaj"</string>
+ <string name="quick_settings_brightness_label" msgid="6968372297018755815">"Osvetljenost"</string>
+ <string name="quick_settings_rotation_unlocked_label" msgid="7305323031808150099">"Automatska rotacija"</string>
+ <string name="quick_settings_rotation_locked_label" msgid="6359205706154282377">"Rotacija je zaključana"</string>
+ <string name="quick_settings_rotation_locked_portrait_label" msgid="5102691921442135053">"Vertikalni prikaz"</string>
+ <string name="quick_settings_rotation_locked_landscape_label" msgid="8553157770061178719">"Horizontalni prikaz"</string>
+ <string name="quick_settings_ime_label" msgid="7073463064369468429">"Metod unosa"</string>
+ <string name="quick_settings_location_label" msgid="5011327048748762257">"Lokacija"</string>
+ <string name="quick_settings_location_off_label" msgid="7464544086507331459">"Lokacija je isključena"</string>
+ <string name="quick_settings_media_device_label" msgid="1302906836372603762">"Medijski uređaj"</string>
+ <string name="quick_settings_rssi_label" msgid="7725671335550695589">"RSSI"</string>
+ <string name="quick_settings_rssi_emergency_only" msgid="2713774041672886750">"Samo hitni pozivi"</string>
+ <string name="quick_settings_settings_label" msgid="5326556592578065401">"Podešavanja"</string>
+ <string name="quick_settings_time_label" msgid="4635969182239736408">"Vreme"</string>
+ <string name="quick_settings_user_label" msgid="5238995632130897840">"Ja"</string>
+ <string name="quick_settings_user_title" msgid="4467690427642392403">"Korisnik"</string>
+ <string name="quick_settings_user_new_user" msgid="9030521362023479778">"Novi korisnik"</string>
+ <string name="quick_settings_wifi_label" msgid="9135344704899546041">"Wi-Fi"</string>
+ <string name="quick_settings_wifi_not_connected" msgid="7171904845345573431">"Veza nije uspostavljena"</string>
+ <string name="quick_settings_wifi_no_network" msgid="2221993077220856376">"Nema mreže"</string>
+ <string name="quick_settings_wifi_off_label" msgid="7558778100843885864">"Wi-Fi je isključen"</string>
+ <string name="quick_settings_wifi_detail_empty_text" msgid="269990350383909226">"Nije dostupna nijedna Wi-Fi mreža"</string>
+ <string name="quick_settings_cast_title" msgid="7709016546426454729">"Prebacivanje"</string>
+ <string name="quick_settings_casting" msgid="6601710681033353316">"Prebacivanje"</string>
+ <string name="quick_settings_cast_device_default_name" msgid="5367253104742382945">"Neimenovani uređaj"</string>
+ <string name="quick_settings_cast_device_default_description" msgid="2484573682378634413">"Spremno za prebacivanje"</string>
+ <string name="quick_settings_cast_detail_empty_text" msgid="311785821261640623">"Nije dostupan nijedan uređaj"</string>
+ <string name="quick_settings_brightness_dialog_title" msgid="8599674057673605368">"Osvetljenost"</string>
+ <string name="quick_settings_brightness_dialog_auto_brightness_label" msgid="5064982743784071218">"AUTOMATSKA"</string>
+ <string name="quick_settings_inversion_label" msgid="8790919884718619648">"Obrni boje"</string>
+ <string name="quick_settings_color_space_label" msgid="853443689745584770">"Režim korekcije boje"</string>
+ <string name="quick_settings_more_settings" msgid="326112621462813682">"Još podešavanja"</string>
+ <string name="quick_settings_done" msgid="3402999958839153376">"Gotovo"</string>
+ <string name="quick_settings_connected" msgid="1722253542984847487">"Povezan"</string>
+ <string name="quick_settings_connecting" msgid="47623027419264404">"Povezuje se..."</string>
+ <string name="quick_settings_tethering_label" msgid="7153452060448575549">"Povezivanje"</string>
+ <string name="quick_settings_hotspot_label" msgid="6046917934974004879">"Hotspot"</string>
+ <string name="quick_settings_notifications_label" msgid="4818156442169154523">"Obaveštenja"</string>
+ <string name="quick_settings_flashlight_label" msgid="2133093497691661546">"Lampa"</string>
+ <string name="quick_settings_cellular_detail_title" msgid="8575062783675171695">"Podaci za mobilne uređaje"</string>
+ <string name="quick_settings_cellular_detail_data_usage" msgid="1964260360259312002">"Potrošnja podataka"</string>
+ <string name="quick_settings_cellular_detail_remaining_data" msgid="722715415543541249">"Preostala količina podataka"</string>
+ <string name="quick_settings_cellular_detail_over_limit" msgid="967669665390990427">"Preko ograničenja"</string>
+ <string name="quick_settings_cellular_detail_data_used" msgid="1476810587475761478">"Iskoristili ste <xliff:g id="DATA_USED">%s</xliff:g>"</string>
+ <string name="quick_settings_cellular_detail_data_limit" msgid="56011158504994128">"Ograničenje od <xliff:g id="DATA_LIMIT">%s</xliff:g>"</string>
+ <string name="quick_settings_cellular_detail_data_warning" msgid="2440098045692399009">"Upozorenje za <xliff:g id="DATA_LIMIT">%s</xliff:g>"</string>
+ <string name="quick_settings_work_mode_label" msgid="6244915274350490429">"Režim rada"</string>
+ <string name="recents_empty_message" msgid="8682129509540827999">"Nedavni ekrani se pojavljuju ovde"</string>
+ <string name="recents_app_info_button_label" msgid="2890317189376000030">"Informacije o aplikaciji"</string>
+ <string name="recents_lock_to_app_button_label" msgid="6942899049072506044">"kačenje ekrana"</string>
+ <string name="recents_search_bar_label" msgid="8074997400187836677">"pretraži"</string>
+ <string name="recents_launch_error_message" msgid="2969287838120550506">"Pokretanje aplikacije <xliff:g id="APP">%s</xliff:g> nije uspelo."</string>
+ <string name="recents_show_history_button_label" msgid="7062088196449747245">"Još"</string>
+ <!-- no translation found for recents_history_label_format (6337155608055062429) -->
+ <skip />
+ <string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="8848514474543427332">"Podeli horizontalno"</string>
+ <string name="recents_multistack_add_stack_dialog_split_vertical" msgid="9075292233696180813">"Podeli vertikalno"</string>
+ <string name="recents_multistack_add_stack_dialog_split_custom" msgid="4177837597513701943">"Prilagođeno deljenje"</string>
+ <string name="expanded_header_battery_charged" msgid="5945855970267657951">"Napunjena je"</string>
+ <string name="expanded_header_battery_charging" msgid="205623198487189724">"Punjenje"</string>
+ <string name="expanded_header_battery_charging_with_time" msgid="457559884275395376">"<xliff:g id="CHARGING_TIME">%s</xliff:g> dok se ne napuni"</string>
+ <string name="expanded_header_battery_not_charging" msgid="4798147152367049732">"Ne puni se"</string>
+ <string name="ssl_ca_cert_warning" msgid="9005954106902053641">"Mreža se možda\nnadgleda"</string>
+ <string name="description_target_search" msgid="3091587249776033139">"Pretraga"</string>
+ <string name="description_direction_up" msgid="7169032478259485180">"Prevucite nagore za <xliff:g id="TARGET_DESCRIPTION">%s</xliff:g>."</string>
+ <string name="description_direction_left" msgid="7207478719805562165">"Prevucite ulevo za <xliff:g id="TARGET_DESCRIPTION">%s</xliff:g>."</string>
+ <string name="zen_priority_introduction" msgid="3070506961866919502">"Neće vas uznemiravati zvukovi i vibracije, osim za alarme, podsetnike, događaje i pozivaoce koje izaberete."</string>
+ <string name="zen_priority_customize_button" msgid="7948043278226955063">"Prilagodi"</string>
+ <string name="zen_silence_introduction_voice" msgid="2284540992298200729">"Ovo blokira SVE zvukove i vibracije uključujući alarme, muziku, video snimke i igre. I dalje ćete moći da upućujete pozive."</string>
+ <string name="zen_silence_introduction" msgid="3137882381093271568">"Ovo blokira SVE zvukove i vibracije uključujući alarme, muziku, video snimke i igre."</string>
+ <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">"Manje hitna obaveštenja su u nastavku"</string>
+ <string name="notification_tap_again" msgid="8524949573675922138">"Dodirnite ponovo da biste otvorili"</string>
+ <string name="keyguard_unlock" msgid="8043466894212841998">"Prevucite nagore da biste otključali"</string>
+ <string name="phone_hint" msgid="4872890986869209950">"Prevucite od ikone za telefon"</string>
+ <string name="voice_hint" msgid="8939888732119726665">"Prevucite od ikone za glasovnu pomoć"</string>
+ <string name="camera_hint" msgid="7939688436797157483">"Prevucite od ikone za kameru"</string>
+ <string name="interruption_level_none_with_warning" msgid="5114872171614161084">"Potpuna tišina. I čitači ekrana će biti isključeni."</string>
+ <string name="interruption_level_none" msgid="6000083681244492992">"Potpuna tišina"</string>
+ <string name="interruption_level_priority" msgid="6426766465363855505">"Samo prioritetni prekidi"</string>
+ <string name="interruption_level_alarms" msgid="5226306993448328896">"Samo alarmi"</string>
+ <string name="interruption_level_none_twoline" msgid="3957581548190765889">"Potpuna\ntišina"</string>
+ <string name="interruption_level_priority_twoline" msgid="1564715335217164124">"Samo\npriorit. prekidi"</string>
+ <string name="interruption_level_alarms_twoline" msgid="3266909566410106146">"Samo\nalarmi"</string>
+ <string name="keyguard_indication_charging_time" msgid="1757251776872835768">"Punjenje (pun je za <xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g>)"</string>
+ <string name="keyguard_indication_charging_time_fast" msgid="9018981952053914986">"Brzo se puni (napuniće se za <xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g>)"</string>
+ <string name="keyguard_indication_charging_time_slowly" msgid="955252797961724952">"Sporo se puni (napuniće se za <xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g>)"</string>
+ <string name="accessibility_multi_user_switch_switcher" msgid="7305948938141024937">"Zameni korisnika"</string>
+ <string name="accessibility_multi_user_switch_switcher_with_current" msgid="8434880595284601601">"Promenite korisnika, aktuelni korisnik je <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string>
+ <string name="accessibility_multi_user_switch_inactive" msgid="1424081831468083402">"Aktuelni korisnik <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string>
+ <string name="accessibility_multi_user_switch_quick_contact" msgid="3020367729287990475">"Prikaži profil"</string>
+ <string name="user_add_user" msgid="5110251524486079492">"Dodaj korisnika"</string>
+ <string name="user_new_user_name" msgid="426540612051178753">"Novi korisnik"</string>
+ <string name="guest_nickname" msgid="8059989128963789678">"Gost"</string>
+ <string name="guest_new_guest" msgid="600537543078847803">"Dodaj gosta"</string>
+ <string name="guest_exit_guest" msgid="7187359342030096885">"Ukloni gosta"</string>
+ <string name="guest_exit_guest_dialog_title" msgid="8480693520521766688">"Želite li da uklonite gosta?"</string>
+ <string name="guest_exit_guest_dialog_message" msgid="4155503224769676625">"Sve aplikacije i podaci u ovoj sesiji će biti izbrisani."</string>
+ <string name="guest_exit_guest_dialog_remove" msgid="7402231963862520531">"Ukloni"</string>
+ <string name="guest_wipe_session_title" msgid="6419439912885956132">"Dobro došli nazad, goste!"</string>
+ <string name="guest_wipe_session_message" msgid="8476238178270112811">"Želite li da nastavite sesiju?"</string>
+ <string name="guest_wipe_session_wipe" msgid="5065558566939858884">"Počni iz početka"</string>
+ <string name="guest_wipe_session_dontwipe" msgid="1401113462524894716">"Da, nastavi"</string>
+ <string name="guest_notification_title" msgid="1585278533840603063">"Gost"</string>
+ <string name="guest_notification_text" msgid="335747957734796689">"Da biste izbrisali aplikacije i podatke, uklonite gosta"</string>
+ <string name="guest_notification_remove_action" msgid="8820670703892101990">"UKLONI GOSTA"</string>
+ <string name="user_logout_notification_title" msgid="1453960926437240727">"Odjavljivanje korisnika"</string>
+ <string name="user_logout_notification_text" msgid="3350262809611876284">"Odjavite aktuelnog korisnika"</string>
+ <string name="user_logout_notification_action" msgid="1195428991423425062">"ODJAVI KORISNIKA"</string>
+ <string name="user_add_user_title" msgid="4553596395824132638">"Dodajete novog korisnika?"</string>
+ <string name="user_add_user_message_short" msgid="2161624834066214559">"Kada dodate novog korisnika, ta osoba treba da podesi sopstveni prostor.\n\nSvaki korisnik može da ažurira aplikacije za sve ostale korisnike."</string>
+ <string name="user_remove_user_title" msgid="4681256956076895559">"Želite li da uklonite korisnika?"</string>
+ <string name="user_remove_user_message" msgid="1453218013959498039">"Sve aplikacije i podaci ovog korisnika će biti izbrisani."</string>
+ <string name="user_remove_user_remove" msgid="7479275741742178297">"Ukloni"</string>
+ <string name="battery_saver_notification_title" msgid="237918726750955859">"Štednja baterije je uključena"</string>
+ <string name="battery_saver_notification_text" msgid="820318788126672692">"Smanjuje performanse i pozadinske podatke"</string>
+ <string name="battery_saver_notification_action_text" msgid="109158658238110382">"Isključi štednju baterije"</string>
+ <string name="notification_hidden_text" msgid="1135169301897151909">"Sadržaj je sakriven"</string>
+ <string name="media_projection_dialog_text" msgid="3071431025448218928">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> će početi da snima sve što se prikazuje na ekranu."</string>
+ <string name="media_projection_remember_text" msgid="3103510882172746752">"Ne prikazuj ponovo"</string>
+ <string name="clear_all_notifications_text" msgid="814192889771462828">"Obriši sve"</string>
+ <string name="media_projection_action_text" msgid="8470872969457985954">"Započni odmah"</string>
+ <string name="empty_shade_text" msgid="708135716272867002">"Nema obaveštenja"</string>
+ <string name="device_owned_footer" msgid="3802752663326030053">"Uređaj se možda nadgleda"</string>
+ <string name="profile_owned_footer" msgid="8021888108553696069">"Profil se možda nadgleda"</string>
+ <string name="vpn_footer" msgid="2388611096129106812">"Mreža se možda nadgleda"</string>
+ <string name="monitoring_title_device_owned" msgid="7121079311903859610">"Nadgledanje uređaja"</string>
+ <string name="monitoring_title_profile_owned" msgid="6790109874733501487">"Nadgledanje profila"</string>
+ <string name="monitoring_title" msgid="169206259253048106">"Nadgledanje mreže"</string>
+ <string name="disable_vpn" msgid="4435534311510272506">"Onemogući VPN"</string>
+ <string name="disconnect_vpn" msgid="1324915059568548655">"Prekini vezu sa VPN-om"</string>
+ <string name="monitoring_description_device_owned" msgid="5780988291898461883">"Uređajem upravlja <xliff:g id="ORGANIZATION">%1$s</xliff:g>.\n\nAdministrator može da nadgleda podešavanja, korporativni pristup, aplikacije, podatke povezane sa uređajem i informacije o lokaciji uređaja, kao i da upravlja njima. Više informacija potražite od administratora."</string>
+ <string name="monitoring_description_vpn" msgid="4445150119515393526">"Dali ste dozvolu aplikaciji da podešava VPN vezu.\n\nTa aplikacija može da nadgleda aktivnosti na uređaju i mreži, uključujući imejlove, aplikacije i veb-sajtove."</string>
+ <string name="monitoring_description_vpn_device_owned" msgid="3090670777499161246">"Uređajem upravlja <xliff:g id="ORGANIZATION">%1$s</xliff:g>.\n\nAdministrator može da nadgleda podešavanja, korporativni pristup, aplikacije, podatke povezane sa uređajem i informacije o lokaciji uređaja, kao i da upravlja njima.\n\nPovezani ste na VPN, koji može da nadgleda aktivnosti na mreži, uključujući imejlove, aplikacije i bezbedne veb-sajtove.\n\nViše informacija potražite od administratora."</string>
+ <string name="monitoring_description_vpn_profile_owned" msgid="2054949132145039290">"Profilom za Work upravlja <xliff:g id="ORGANIZATION">%1$s</xliff:g>.\n\nAdministrator može da nadgleda aktivnosti na mreži, uključujući imejlove, aplikacije i veb-sajtove.\n\nViše informacija potražite od administratora.\n\nPovezani ste i na VPN, koji može da nadgleda aktivnosti na ličnoj mreži."</string>
+ <string name="legacy_vpn_name" msgid="6604123105765737830">"VPN"</string>
+ <string name="monitoring_description_app" msgid="6259179342284742878">"Povezani ste sa aplikacijom <xliff:g id="APPLICATION">%1$s</xliff:g>, koja može da nadgleda aktivnosti na mreži, uključujući imejlove, aplikacije i veb-sajtove."</string>
+ <string name="monitoring_description_app_personal" msgid="484599052118316268">"Povezani ste sa aplikacijom <xliff:g id="APPLICATION">%1$s</xliff:g>, koja može da nadgleda aktivnosti na ličnoj mreži, uključujući imejlove, aplikacije i veb-sajtove."</string>
+ <string name="monitoring_description_app_work" msgid="1754325860918060897">"Profilom za Work upravlja <xliff:g id="ORGANIZATION">%1$s</xliff:g>. Povezan je sa aplikacijom <xliff:g id="APPLICATION">%2$s</xliff:g>, koja može da nadgleda aktivnosti na poslovnoj mreži, uključujući imejlove, aplikacije i veb-sajtove.\n\nViše informacija potražite od administratora."</string>
+ <string name="monitoring_description_app_personal_work" msgid="4946600443852045903">"Profilom za Work upravlja <xliff:g id="ORGANIZATION">%1$s</xliff:g>. Povezan je sa aplikacijom <xliff:g id="APPLICATION_WORK">%2$s</xliff:g>, koja može da nadgleda aktivnosti na poslovnoj mreži, uključujući imejlove, aplikacije i veb-sajtove.\n\nPovezani ste i sa aplikacijom <xliff:g id="APPLICATION_PERSONAL">%3$s</xliff:g>, koja može da nadgleda aktivnosti na ličnoj mreži."</string>
+ <string name="monitoring_description_vpn_app_device_owned" msgid="4970443827043261703">"Uređajem upravlja <xliff:g id="ORGANIZATION">%1$s</xliff:g>.\n\nAdministrator može da nadgleda podešavanja, korporativni pristup, aplikacije, podatke povezane sa uređajem i informacije o lokaciji uređaja, kao i da upravlja njima.\n\nPovezani ste sa aplikacijom <xliff:g id="APPLICATION">%2$s</xliff:g>, koja može da nadgleda aktivnosti na mreži, uključujući imejlove, aplikacije i veb-sajtove.\n\nViše informacija potražite od administratora."</string>
+ <string name="keyguard_indication_trust_disabled" msgid="7412534203633528135">"Uređaj će ostati zaključan dok ga ne otključate ručno"</string>
+ <string name="hidden_notifications_title" msgid="7139628534207443290">"Brže dobijajte obaveštenja"</string>
+ <string name="hidden_notifications_text" msgid="2326409389088668981">"Pregledajte ih pre otključavanja"</string>
+ <string name="hidden_notifications_cancel" msgid="3690709735122344913">"Ne, hvala"</string>
+ <string name="hidden_notifications_setup" msgid="41079514801976810">"Podesi"</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 odmah"</string>
+ <string name="accessibility_volume_expand" msgid="5946812790999244205">"Proširi"</string>
+ <string name="accessibility_volume_collapse" msgid="3609549593031810875">"Skupi"</string>
+ <string name="screen_pinning_title" msgid="3273740381976175811">"Ekran je zakačen"</string>
+ <string name="screen_pinning_description" msgid="3577937698406151604">"Zbog toga se on stalno prikazuje dok ga ne otkačite. Dodirnite i zadržite Nazad da biste ga otkačili."</string>
+ <string name="screen_pinning_positive" msgid="3783985798366751226">"Važi"</string>
+ <string name="screen_pinning_negative" msgid="3741602308343880268">"Ne, hvala"</string>
+ <string name="quick_settings_reset_confirmation_title" msgid="748792586749897883">"Želite li da sakrijete <xliff:g id="TILE_LABEL">%1$s</xliff:g>?"</string>
+ <string name="quick_settings_reset_confirmation_message" msgid="2235970126803317374">"Ovo će se ponovo pojaviti kada ga sledeći put budete uključili u podešavanjima."</string>
+ <string name="quick_settings_reset_confirmation_button" msgid="2660339101868367515">"Sakrij"</string>
+ <string name="volumeui_prompt_message" msgid="918680947433389110">"<xliff:g id="APP_NAME">%1$s</xliff:g> želi da bude dijalog za jačinu zvuka."</string>
+ <string name="volumeui_prompt_allow" msgid="7954396902482228786">"Dozvoli"</string>
+ <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> je dijalog za jačinu zvuka"</string>
+ <string name="volumeui_notification_text" msgid="1826889705095768656">"Dodirnite da biste vratili original."</string>
+ <string name="group_summary_concadenation" msgid="6846402378100148789">", "</string>
+ <string name="managed_profile_foreground_toast" msgid="5421487114739245972">"Koristite profil za Work"</string>
+ <string name="system_ui_tuner" msgid="708224127392452018">"Tjuner za korisnički interfejs sistema"</string>
+ <string name="show_battery_percentage" msgid="5444136600512968798">"Prikazuj ugrađeni procenat baterije"</string>
+ <string name="show_battery_percentage_summary" msgid="3215025775576786037">"Prikazivanje nivoa napunjenosti baterije u procentima unutar ikone na statusnoj traci kada se baterija ne puni"</string>
+ <string name="quick_settings" msgid="10042998191725428">"Brza podešavanja"</string>
+ <string name="status_bar" msgid="4877645476959324760">"Statusna traka"</string>
+ <string name="overview" msgid="4018602013895926956">"Pregled"</string>
+ <string name="demo_mode" msgid="2389163018533514619">"Režim demonstracije"</string>
+ <string name="enable_demo_mode" msgid="4844205668718636518">"Omogući režim demonstracije"</string>
+ <string name="show_demo_mode" msgid="2018336697782464029">"Prikaži režim demonstracije"</string>
+ <string name="status_bar_ethernet" msgid="5044290963549500128">"Eternet"</string>
+ <string name="status_bar_alarm" msgid="8536256753575881818">"Alarm"</string>
+ <string name="status_bar_work" msgid="6022553324802866373">"Profil za Work"</string>
+ <string name="status_bar_airplane" msgid="7057575501472249002">"Režim rada u avionu"</string>
+ <string name="add_tile" msgid="2995389510240786221">"Dodaj pločicu"</string>
+ <string name="broadcast_tile" msgid="3894036511763289383">"Pločica za emitovanje"</string>
+ <string name="zen_alarm_warning_indef" msgid="3482966345578319605">"Nećete čuti sledeći alarm u <xliff:g id="WHEN">%1$s</xliff:g> ako ne isključite ovo pre toga"</string>
+ <string name="zen_alarm_warning" msgid="444533119582244293">"Nećete čuti sledeći alarm u <xliff:g id="WHEN">%1$s</xliff:g>"</string>
+ <string name="alarm_template" msgid="3980063409350522735">"u <xliff:g id="WHEN">%1$s</xliff:g>"</string>
+ <string name="alarm_template_far" msgid="4242179982586714810">"u <xliff:g id="WHEN">%1$s</xliff:g>"</string>
+ <string name="accessibility_quick_settings_detail" msgid="2579369091672902101">"Brza podešavanja, <xliff:g id="TITLE">%s</xliff:g>."</string>
+ <string name="accessibility_status_bar_hotspot" msgid="4099381329956402865">"Hotspot"</string>
+ <string name="accessibility_managed_profile" msgid="6613641363112584120">"Profil za Work"</string>
+ <string name="tuner_warning_title" msgid="7094689930793031682">"Zabava za neke, ali ne za sve"</string>
+ <string name="tuner_warning" msgid="8730648121973575701">"Tjuner za korisnički interfejs sistema vam pruža dodatne načine za podešavanje i prilagođavanje Android korisničkog interfejsa. Ove eksperimentalne funkcije mogu da se promene, otkažu ili nestanu u budućim izdanjima. Budite oprezni."</string>
+ <string name="tuner_persistent_warning" msgid="8597333795565621795">"Ove eksperimentalne funkcije mogu da se promene, otkažu ili nestanu u budućim izdanjima. Budite oprezni."</string>
+ <string name="got_it" msgid="2239653834387972602">"Važi"</string>
+ <string name="tuner_toast" msgid="603429811084428439">"Čestitamo! Tjuner za korisnički interfejs sistema je dodat u Podešavanja"</string>
+ <string name="remove_from_settings" msgid="8389591916603406378">"Ukloni iz Podešavanja"</string>
+ <string name="remove_from_settings_prompt" msgid="6069085993355887748">"Želite li da uklonite Tjuner za korisnički interfejs sistema iz Podešavanja i da prestanete da koristite sve njegove funkcije?"</string>
+ <string name="activity_not_found" msgid="348423244327799974">"Aplikacija nije instalirana na uređaju"</string>
+ <string name="clock_seconds" msgid="7689554147579179507">"Prikazuj sekunde na satu"</string>
+ <string name="clock_seconds_desc" msgid="6282693067130470675">"Sekunde na satu se prikazuju na statusnoj traci. To može da utiče na trajanje baterije."</string>
+ <string name="qs_rearrange" msgid="8060918697551068765">"Preuredi Brza podešavanja"</string>
+ <string name="show_brightness" msgid="6613930842805942519">"Prikaži osvetljenost u Brzim podešavanjima"</string>
+ <string name="experimental" msgid="6198182315536726162">"Eksperimentalno"</string>
+ <string name="enable_bluetooth_title" msgid="5027037706500635269">"Želite li da uključite Bluetooth?"</string>
+ <string name="enable_bluetooth_message" msgid="9106595990708985385">"Da biste povezali tastaturu sa tabletom, prvo morate da uključite Bluetooth."</string>
+ <string name="enable_bluetooth_confirmation_ok" msgid="6258074250948309715">"Uključi"</string>
+</resources>
diff --git a/packages/SystemUI/res/values-bg/strings.xml b/packages/SystemUI/res/values-bg/strings.xml
index 91a5c4c..fcbbeef 100644
--- a/packages/SystemUI/res/values-bg/strings.xml
+++ b/packages/SystemUI/res/values-bg/strings.xml
@@ -301,7 +301,8 @@
<string name="recents_search_bar_label" msgid="8074997400187836677">"търсене"</string>
<string name="recents_launch_error_message" msgid="2969287838120550506">"<xliff:g id="APP">%s</xliff:g> не можа да стартира."</string>
<string name="recents_show_history_button_label" msgid="7062088196449747245">"Още"</string>
- <string name="recents_history_label" msgid="3076213823382198287">"История"</string>
+ <!-- no translation found for recents_history_label_format (6337155608055062429) -->
+ <skip />
<string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="8848514474543427332">"Хоризонтално разделяне"</string>
<string name="recents_multistack_add_stack_dialog_split_vertical" msgid="9075292233696180813">"Вертикално разделяне"</string>
<string name="recents_multistack_add_stack_dialog_split_custom" msgid="4177837597513701943">"Персонализирано разделяне"</string>
diff --git a/packages/SystemUI/res/values-bn-rBD/strings.xml b/packages/SystemUI/res/values-bn-rBD/strings.xml
index 1c79542..48d3f46 100644
--- a/packages/SystemUI/res/values-bn-rBD/strings.xml
+++ b/packages/SystemUI/res/values-bn-rBD/strings.xml
@@ -301,7 +301,8 @@
<string name="recents_search_bar_label" msgid="8074997400187836677">"অনুসন্ধান"</string>
<string name="recents_launch_error_message" msgid="2969287838120550506">"<xliff:g id="APP">%s</xliff:g> শুরু করা যায়নি৷"</string>
<string name="recents_show_history_button_label" msgid="7062088196449747245">"আরো"</string>
- <string name="recents_history_label" msgid="3076213823382198287">"ইতিহাস"</string>
+ <!-- no translation found for recents_history_label_format (6337155608055062429) -->
+ <skip />
<string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="8848514474543427332">"অনুভূমিক স্প্লিট"</string>
<string name="recents_multistack_add_stack_dialog_split_vertical" msgid="9075292233696180813">"উল্লম্ব স্প্লিট"</string>
<string name="recents_multistack_add_stack_dialog_split_custom" msgid="4177837597513701943">"কাস্টম স্প্লিট করুন"</string>
diff --git a/packages/SystemUI/res/values-ca/strings.xml b/packages/SystemUI/res/values-ca/strings.xml
index 1fc41a4..a83404c 100644
--- a/packages/SystemUI/res/values-ca/strings.xml
+++ b/packages/SystemUI/res/values-ca/strings.xml
@@ -301,7 +301,7 @@
<string name="recents_search_bar_label" msgid="8074997400187836677">"cerca"</string>
<string name="recents_launch_error_message" msgid="2969287838120550506">"No s\'ha pogut iniciar <xliff:g id="APP">%s</xliff:g>."</string>
<string name="recents_show_history_button_label" msgid="7062088196449747245">"Més"</string>
- <string name="recents_history_label" msgid="3076213823382198287">"Historial"</string>
+ <string name="recents_history_label_format" msgid="6337155608055062429">"<xliff:g id="NUMBER">%d</xliff:g> més"</string>
<string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="8848514474543427332">"Divisió horitzontal"</string>
<string name="recents_multistack_add_stack_dialog_split_vertical" msgid="9075292233696180813">"Divisió vertical"</string>
<string name="recents_multistack_add_stack_dialog_split_custom" msgid="4177837597513701943">"Divisió personalitzada"</string>
diff --git a/packages/SystemUI/res/values-cs/strings.xml b/packages/SystemUI/res/values-cs/strings.xml
index f405f10..7895951 100644
--- a/packages/SystemUI/res/values-cs/strings.xml
+++ b/packages/SystemUI/res/values-cs/strings.xml
@@ -303,7 +303,8 @@
<string name="recents_search_bar_label" msgid="8074997400187836677">"vyhledat"</string>
<string name="recents_launch_error_message" msgid="2969287838120550506">"Aplikaci <xliff:g id="APP">%s</xliff:g> nelze spustit."</string>
<string name="recents_show_history_button_label" msgid="7062088196449747245">"Další"</string>
- <string name="recents_history_label" msgid="3076213823382198287">"Historie"</string>
+ <!-- no translation found for recents_history_label_format (6337155608055062429) -->
+ <skip />
<string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="8848514474543427332">"Vodorovné rozdělení"</string>
<string name="recents_multistack_add_stack_dialog_split_vertical" msgid="9075292233696180813">"Vertikální rozdělení"</string>
<string name="recents_multistack_add_stack_dialog_split_custom" msgid="4177837597513701943">"Vlastní rozdělení"</string>
diff --git a/packages/SystemUI/res/values-da/strings.xml b/packages/SystemUI/res/values-da/strings.xml
index 6e85d8c..e69d069 100644
--- a/packages/SystemUI/res/values-da/strings.xml
+++ b/packages/SystemUI/res/values-da/strings.xml
@@ -301,7 +301,8 @@
<string name="recents_search_bar_label" msgid="8074997400187836677">"søg"</string>
<string name="recents_launch_error_message" msgid="2969287838120550506">"<xliff:g id="APP">%s</xliff:g> kunne ikke startes."</string>
<string name="recents_show_history_button_label" msgid="7062088196449747245">"Mere"</string>
- <string name="recents_history_label" msgid="3076213823382198287">"Historik"</string>
+ <!-- no translation found for recents_history_label_format (6337155608055062429) -->
+ <skip />
<string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="8848514474543427332">"Opdel vandret"</string>
<string name="recents_multistack_add_stack_dialog_split_vertical" msgid="9075292233696180813">"Opdel lodret"</string>
<string name="recents_multistack_add_stack_dialog_split_custom" msgid="4177837597513701943">"Opdel brugerdefineret"</string>
diff --git a/packages/SystemUI/res/values-de/strings.xml b/packages/SystemUI/res/values-de/strings.xml
index cdc1f2a..dc62e65 100644
--- a/packages/SystemUI/res/values-de/strings.xml
+++ b/packages/SystemUI/res/values-de/strings.xml
@@ -301,7 +301,8 @@
<string name="recents_search_bar_label" msgid="8074997400187836677">"Suche"</string>
<string name="recents_launch_error_message" msgid="2969287838120550506">"<xliff:g id="APP">%s</xliff:g> konnte nicht gestartet werden."</string>
<string name="recents_show_history_button_label" msgid="7062088196449747245">"Mehr"</string>
- <string name="recents_history_label" msgid="3076213823382198287">"Verlauf"</string>
+ <!-- no translation found for recents_history_label_format (6337155608055062429) -->
+ <skip />
<string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="8848514474543427332">"Geteilte Schaltfläche – horizontal"</string>
<string name="recents_multistack_add_stack_dialog_split_vertical" msgid="9075292233696180813">"Geteilte Schaltfläche – vertikal"</string>
<string name="recents_multistack_add_stack_dialog_split_custom" msgid="4177837597513701943">"Geteilte Schaltfläche – benutzerdefiniert"</string>
diff --git a/packages/SystemUI/res/values-el/strings.xml b/packages/SystemUI/res/values-el/strings.xml
index 015eeae..9cfad85 100644
--- a/packages/SystemUI/res/values-el/strings.xml
+++ b/packages/SystemUI/res/values-el/strings.xml
@@ -301,7 +301,8 @@
<string name="recents_search_bar_label" msgid="8074997400187836677">"αναζήτηση"</string>
<string name="recents_launch_error_message" msgid="2969287838120550506">"Δεν ήταν δυνατή η εκκίνηση της εφαρμογής <xliff:g id="APP">%s</xliff:g>."</string>
<string name="recents_show_history_button_label" msgid="7062088196449747245">"Περισσότερα"</string>
- <string name="recents_history_label" msgid="3076213823382198287">"Ιστορικό"</string>
+ <!-- no translation found for recents_history_label_format (6337155608055062429) -->
+ <skip />
<string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="8848514474543427332">"Οριζόντιος διαχωρισμός"</string>
<string name="recents_multistack_add_stack_dialog_split_vertical" msgid="9075292233696180813">"Κάθετος διαχωρισμός"</string>
<string name="recents_multistack_add_stack_dialog_split_custom" msgid="4177837597513701943">"Προσαρμοσμένος διαχωρισμός"</string>
diff --git a/packages/SystemUI/res/values-en-rAU/strings.xml b/packages/SystemUI/res/values-en-rAU/strings.xml
index f3061d7..30a333a 100644
--- a/packages/SystemUI/res/values-en-rAU/strings.xml
+++ b/packages/SystemUI/res/values-en-rAU/strings.xml
@@ -301,7 +301,7 @@
<string name="recents_search_bar_label" msgid="8074997400187836677">"search"</string>
<string name="recents_launch_error_message" msgid="2969287838120550506">"Could not start <xliff:g id="APP">%s</xliff:g>."</string>
<string name="recents_show_history_button_label" msgid="7062088196449747245">"More"</string>
- <string name="recents_history_label" msgid="3076213823382198287">"History"</string>
+ <string name="recents_history_label_format" msgid="6337155608055062429">"<xliff:g id="NUMBER">%d</xliff:g> More"</string>
<string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="8848514474543427332">"Split Horizontal"</string>
<string name="recents_multistack_add_stack_dialog_split_vertical" msgid="9075292233696180813">"Split Vertical"</string>
<string name="recents_multistack_add_stack_dialog_split_custom" msgid="4177837597513701943">"Split Customised"</string>
diff --git a/packages/SystemUI/res/values-en-rGB/strings.xml b/packages/SystemUI/res/values-en-rGB/strings.xml
index f3061d7..30a333a 100644
--- a/packages/SystemUI/res/values-en-rGB/strings.xml
+++ b/packages/SystemUI/res/values-en-rGB/strings.xml
@@ -301,7 +301,7 @@
<string name="recents_search_bar_label" msgid="8074997400187836677">"search"</string>
<string name="recents_launch_error_message" msgid="2969287838120550506">"Could not start <xliff:g id="APP">%s</xliff:g>."</string>
<string name="recents_show_history_button_label" msgid="7062088196449747245">"More"</string>
- <string name="recents_history_label" msgid="3076213823382198287">"History"</string>
+ <string name="recents_history_label_format" msgid="6337155608055062429">"<xliff:g id="NUMBER">%d</xliff:g> More"</string>
<string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="8848514474543427332">"Split Horizontal"</string>
<string name="recents_multistack_add_stack_dialog_split_vertical" msgid="9075292233696180813">"Split Vertical"</string>
<string name="recents_multistack_add_stack_dialog_split_custom" msgid="4177837597513701943">"Split Customised"</string>
diff --git a/packages/SystemUI/res/values-en-rIN/strings.xml b/packages/SystemUI/res/values-en-rIN/strings.xml
index f3061d7..30a333a 100644
--- a/packages/SystemUI/res/values-en-rIN/strings.xml
+++ b/packages/SystemUI/res/values-en-rIN/strings.xml
@@ -301,7 +301,7 @@
<string name="recents_search_bar_label" msgid="8074997400187836677">"search"</string>
<string name="recents_launch_error_message" msgid="2969287838120550506">"Could not start <xliff:g id="APP">%s</xliff:g>."</string>
<string name="recents_show_history_button_label" msgid="7062088196449747245">"More"</string>
- <string name="recents_history_label" msgid="3076213823382198287">"History"</string>
+ <string name="recents_history_label_format" msgid="6337155608055062429">"<xliff:g id="NUMBER">%d</xliff:g> More"</string>
<string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="8848514474543427332">"Split Horizontal"</string>
<string name="recents_multistack_add_stack_dialog_split_vertical" msgid="9075292233696180813">"Split Vertical"</string>
<string name="recents_multistack_add_stack_dialog_split_custom" msgid="4177837597513701943">"Split Customised"</string>
diff --git a/packages/SystemUI/res/values-es-rUS/strings.xml b/packages/SystemUI/res/values-es-rUS/strings.xml
index 345b3bf..3ff2492 100644
--- a/packages/SystemUI/res/values-es-rUS/strings.xml
+++ b/packages/SystemUI/res/values-es-rUS/strings.xml
@@ -301,7 +301,8 @@
<string name="recents_search_bar_label" msgid="8074997400187836677">"buscar"</string>
<string name="recents_launch_error_message" msgid="2969287838120550506">"No se pudo iniciar <xliff:g id="APP">%s</xliff:g>."</string>
<string name="recents_show_history_button_label" msgid="7062088196449747245">"Más"</string>
- <string name="recents_history_label" msgid="3076213823382198287">"Historial"</string>
+ <!-- no translation found for recents_history_label_format (6337155608055062429) -->
+ <skip />
<string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="8848514474543427332">"División horizontal"</string>
<string name="recents_multistack_add_stack_dialog_split_vertical" msgid="9075292233696180813">"División vertical"</string>
<string name="recents_multistack_add_stack_dialog_split_custom" msgid="4177837597513701943">"División personalizada"</string>
diff --git a/packages/SystemUI/res/values-es/strings.xml b/packages/SystemUI/res/values-es/strings.xml
index d29eeff..662422b 100644
--- a/packages/SystemUI/res/values-es/strings.xml
+++ b/packages/SystemUI/res/values-es/strings.xml
@@ -301,7 +301,8 @@
<string name="recents_search_bar_label" msgid="8074997400187836677">"buscar"</string>
<string name="recents_launch_error_message" msgid="2969287838120550506">"No se ha podido iniciar <xliff:g id="APP">%s</xliff:g>."</string>
<string name="recents_show_history_button_label" msgid="7062088196449747245">"Más"</string>
- <string name="recents_history_label" msgid="3076213823382198287">"Historial"</string>
+ <!-- no translation found for recents_history_label_format (6337155608055062429) -->
+ <skip />
<string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="8848514474543427332">"División horizontal"</string>
<string name="recents_multistack_add_stack_dialog_split_vertical" msgid="9075292233696180813">"División vertical"</string>
<string name="recents_multistack_add_stack_dialog_split_custom" msgid="4177837597513701943">"División personalizada"</string>
diff --git a/packages/SystemUI/res/values-et-rEE/strings.xml b/packages/SystemUI/res/values-et-rEE/strings.xml
index 12490b1..0fc88c4 100644
--- a/packages/SystemUI/res/values-et-rEE/strings.xml
+++ b/packages/SystemUI/res/values-et-rEE/strings.xml
@@ -301,7 +301,8 @@
<string name="recents_search_bar_label" msgid="8074997400187836677">"otsing"</string>
<string name="recents_launch_error_message" msgid="2969287838120550506">"Rakendust <xliff:g id="APP">%s</xliff:g> ei saanud käivitada."</string>
<string name="recents_show_history_button_label" msgid="7062088196449747245">"Rohkem"</string>
- <string name="recents_history_label" msgid="3076213823382198287">"Ajalugu"</string>
+ <!-- no translation found for recents_history_label_format (6337155608055062429) -->
+ <skip />
<string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="8848514474543427332">"Horisontaalne poolitamine"</string>
<string name="recents_multistack_add_stack_dialog_split_vertical" msgid="9075292233696180813">"Vertikaalne poolitamine"</string>
<string name="recents_multistack_add_stack_dialog_split_custom" msgid="4177837597513701943">"Kohandatud poolitamine"</string>
diff --git a/packages/SystemUI/res/values-eu-rES/strings.xml b/packages/SystemUI/res/values-eu-rES/strings.xml
index 2938bd4..3b06137 100644
--- a/packages/SystemUI/res/values-eu-rES/strings.xml
+++ b/packages/SystemUI/res/values-eu-rES/strings.xml
@@ -301,7 +301,8 @@
<string name="recents_search_bar_label" msgid="8074997400187836677">"bilatu"</string>
<string name="recents_launch_error_message" msgid="2969287838120550506">"Ezin izan da hasi <xliff:g id="APP">%s</xliff:g>."</string>
<string name="recents_show_history_button_label" msgid="7062088196449747245">"Gehiago"</string>
- <string name="recents_history_label" msgid="3076213823382198287">"Historia"</string>
+ <!-- no translation found for recents_history_label_format (6337155608055062429) -->
+ <skip />
<string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="8848514474543427332">"Banaketa horizontala"</string>
<string name="recents_multistack_add_stack_dialog_split_vertical" msgid="9075292233696180813">"Banaketa bertikala"</string>
<string name="recents_multistack_add_stack_dialog_split_custom" msgid="4177837597513701943">"Banaketa pertsonalizatua"</string>
diff --git a/packages/SystemUI/res/values-fa/strings.xml b/packages/SystemUI/res/values-fa/strings.xml
index 20c85ad..b8c4256 100644
--- a/packages/SystemUI/res/values-fa/strings.xml
+++ b/packages/SystemUI/res/values-fa/strings.xml
@@ -301,7 +301,8 @@
<string name="recents_search_bar_label" msgid="8074997400187836677">"جستجو"</string>
<string name="recents_launch_error_message" msgid="2969287838120550506">"<xliff:g id="APP">%s</xliff:g> شروع نشد."</string>
<string name="recents_show_history_button_label" msgid="7062088196449747245">"بیشتر"</string>
- <string name="recents_history_label" msgid="3076213823382198287">"سابقه"</string>
+ <!-- no translation found for recents_history_label_format (6337155608055062429) -->
+ <skip />
<string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="8848514474543427332">"تقسیم افقی"</string>
<string name="recents_multistack_add_stack_dialog_split_vertical" msgid="9075292233696180813">"تقسیم عمودی"</string>
<string name="recents_multistack_add_stack_dialog_split_custom" msgid="4177837597513701943">"سفارشی کردن تقسیم"</string>
diff --git a/packages/SystemUI/res/values-fi/strings.xml b/packages/SystemUI/res/values-fi/strings.xml
index e12c86e..a32232f 100644
--- a/packages/SystemUI/res/values-fi/strings.xml
+++ b/packages/SystemUI/res/values-fi/strings.xml
@@ -301,7 +301,8 @@
<string name="recents_search_bar_label" msgid="8074997400187836677">"haku"</string>
<string name="recents_launch_error_message" msgid="2969287838120550506">"Sovelluksen <xliff:g id="APP">%s</xliff:g> käynnistäminen epäonnistui."</string>
<string name="recents_show_history_button_label" msgid="7062088196449747245">"Lisää"</string>
- <string name="recents_history_label" msgid="3076213823382198287">"Historia"</string>
+ <!-- no translation found for recents_history_label_format (6337155608055062429) -->
+ <skip />
<string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="8848514474543427332">"Vaakasuuntainen jako"</string>
<string name="recents_multistack_add_stack_dialog_split_vertical" msgid="9075292233696180813">"Pystysuuntainen jako"</string>
<string name="recents_multistack_add_stack_dialog_split_custom" msgid="4177837597513701943">"Muokattu jako"</string>
diff --git a/packages/SystemUI/res/values-fr-rCA/strings.xml b/packages/SystemUI/res/values-fr-rCA/strings.xml
index 63cd500..5b55188 100644
--- a/packages/SystemUI/res/values-fr-rCA/strings.xml
+++ b/packages/SystemUI/res/values-fr-rCA/strings.xml
@@ -301,7 +301,8 @@
<string name="recents_search_bar_label" msgid="8074997400187836677">"rechercher"</string>
<string name="recents_launch_error_message" msgid="2969287838120550506">"Impossible de lancer <xliff:g id="APP">%s</xliff:g>."</string>
<string name="recents_show_history_button_label" msgid="7062088196449747245">"Plus"</string>
- <string name="recents_history_label" msgid="3076213823382198287">"Historique"</string>
+ <!-- no translation found for recents_history_label_format (6337155608055062429) -->
+ <skip />
<string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="8848514474543427332">"Séparation horizontale"</string>
<string name="recents_multistack_add_stack_dialog_split_vertical" msgid="9075292233696180813">"Séparation verticale"</string>
<string name="recents_multistack_add_stack_dialog_split_custom" msgid="4177837597513701943">"Séparation personnalisée"</string>
diff --git a/packages/SystemUI/res/values-fr/strings.xml b/packages/SystemUI/res/values-fr/strings.xml
index 04ba842..dd2d5f4 100644
--- a/packages/SystemUI/res/values-fr/strings.xml
+++ b/packages/SystemUI/res/values-fr/strings.xml
@@ -301,7 +301,8 @@
<string name="recents_search_bar_label" msgid="8074997400187836677">"rechercher"</string>
<string name="recents_launch_error_message" msgid="2969287838120550506">"Impossible de lancer <xliff:g id="APP">%s</xliff:g>."</string>
<string name="recents_show_history_button_label" msgid="7062088196449747245">"Plus"</string>
- <string name="recents_history_label" msgid="3076213823382198287">"Historique"</string>
+ <!-- no translation found for recents_history_label_format (6337155608055062429) -->
+ <skip />
<string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="8848514474543427332">"Séparation horizontale"</string>
<string name="recents_multistack_add_stack_dialog_split_vertical" msgid="9075292233696180813">"Séparation verticale"</string>
<string name="recents_multistack_add_stack_dialog_split_custom" msgid="4177837597513701943">"Séparation personnalisée"</string>
diff --git a/packages/SystemUI/res/values-gl-rES/strings.xml b/packages/SystemUI/res/values-gl-rES/strings.xml
index 87753d1..22ac72e 100644
--- a/packages/SystemUI/res/values-gl-rES/strings.xml
+++ b/packages/SystemUI/res/values-gl-rES/strings.xml
@@ -301,7 +301,8 @@
<string name="recents_search_bar_label" msgid="8074997400187836677">"buscar"</string>
<string name="recents_launch_error_message" msgid="2969287838120550506">"Non foi posible iniciar <xliff:g id="APP">%s</xliff:g>."</string>
<string name="recents_show_history_button_label" msgid="7062088196449747245">"Máis"</string>
- <string name="recents_history_label" msgid="3076213823382198287">"Historial"</string>
+ <!-- no translation found for recents_history_label_format (6337155608055062429) -->
+ <skip />
<string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="8848514474543427332">"Dividir en horizontal"</string>
<string name="recents_multistack_add_stack_dialog_split_vertical" msgid="9075292233696180813">"Dividir en vertical"</string>
<string name="recents_multistack_add_stack_dialog_split_custom" msgid="4177837597513701943">"Dividir de xeito personalizado"</string>
diff --git a/packages/SystemUI/res/values-gu-rIN/strings.xml b/packages/SystemUI/res/values-gu-rIN/strings.xml
index a10f0d5..d72529e 100644
--- a/packages/SystemUI/res/values-gu-rIN/strings.xml
+++ b/packages/SystemUI/res/values-gu-rIN/strings.xml
@@ -301,7 +301,7 @@
<string name="recents_search_bar_label" msgid="8074997400187836677">"શોધ"</string>
<string name="recents_launch_error_message" msgid="2969287838120550506">"<xliff:g id="APP">%s</xliff:g> પ્રારંભ કરી શકાયું નથી."</string>
<string name="recents_show_history_button_label" msgid="7062088196449747245">"વધુ"</string>
- <string name="recents_history_label" msgid="3076213823382198287">"ઇતિહાસ"</string>
+ <string name="recents_history_label_format" msgid="6337155608055062429">"<xliff:g id="NUMBER">%d</xliff:g> વધુ"</string>
<string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="8848514474543427332">"આડું વિભક્ત કરો"</string>
<string name="recents_multistack_add_stack_dialog_split_vertical" msgid="9075292233696180813">"ઊભું વિભક્ત કરો"</string>
<string name="recents_multistack_add_stack_dialog_split_custom" msgid="4177837597513701943">"કસ્ટમ વિભક્ત કરો"</string>
diff --git a/packages/SystemUI/res/values-hi/strings.xml b/packages/SystemUI/res/values-hi/strings.xml
index a3c8958..9a3ab2b 100644
--- a/packages/SystemUI/res/values-hi/strings.xml
+++ b/packages/SystemUI/res/values-hi/strings.xml
@@ -301,7 +301,7 @@
<string name="recents_search_bar_label" msgid="8074997400187836677">"खोज"</string>
<string name="recents_launch_error_message" msgid="2969287838120550506">"<xliff:g id="APP">%s</xliff:g> प्रारंभ नहीं किया जा सका."</string>
<string name="recents_show_history_button_label" msgid="7062088196449747245">"अधिक"</string>
- <string name="recents_history_label" msgid="3076213823382198287">"इतिहास"</string>
+ <string name="recents_history_label_format" msgid="6337155608055062429">"<xliff:g id="NUMBER">%d</xliff:g> अधिक"</string>
<string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="8848514474543427332">"क्षैतिज रूप से विभाजित करें"</string>
<string name="recents_multistack_add_stack_dialog_split_vertical" msgid="9075292233696180813">"लम्बवत रूप से विभाजित करें"</string>
<string name="recents_multistack_add_stack_dialog_split_custom" msgid="4177837597513701943">"कस्टम रूप से विभाजित करें"</string>
diff --git a/packages/SystemUI/res/values-hr/strings.xml b/packages/SystemUI/res/values-hr/strings.xml
index 0e171ea..1ed890c 100644
--- a/packages/SystemUI/res/values-hr/strings.xml
+++ b/packages/SystemUI/res/values-hr/strings.xml
@@ -302,7 +302,8 @@
<string name="recents_search_bar_label" msgid="8074997400187836677">"pretraži"</string>
<string name="recents_launch_error_message" msgid="2969287838120550506">"Aplikacija <xliff:g id="APP">%s</xliff:g> nije pokrenuta."</string>
<string name="recents_show_history_button_label" msgid="7062088196449747245">"Više"</string>
- <string name="recents_history_label" msgid="3076213823382198287">"Povijest"</string>
+ <!-- no translation found for recents_history_label_format (6337155608055062429) -->
+ <skip />
<string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="8848514474543427332">"Podijeli vodoravno"</string>
<string name="recents_multistack_add_stack_dialog_split_vertical" msgid="9075292233696180813">"Podijeli okomito"</string>
<string name="recents_multistack_add_stack_dialog_split_custom" msgid="4177837597513701943">"Podijeli prilagođeno"</string>
diff --git a/packages/SystemUI/res/values-hu/strings.xml b/packages/SystemUI/res/values-hu/strings.xml
index 39336c8..8978152 100644
--- a/packages/SystemUI/res/values-hu/strings.xml
+++ b/packages/SystemUI/res/values-hu/strings.xml
@@ -301,7 +301,8 @@
<string name="recents_search_bar_label" msgid="8074997400187836677">"keresés"</string>
<string name="recents_launch_error_message" msgid="2969287838120550506">"Nem lehet elindítani a következőt: <xliff:g id="APP">%s</xliff:g>."</string>
<string name="recents_show_history_button_label" msgid="7062088196449747245">"Továbbiak"</string>
- <string name="recents_history_label" msgid="3076213823382198287">"Előzmények"</string>
+ <!-- no translation found for recents_history_label_format (6337155608055062429) -->
+ <skip />
<string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="8848514474543427332">"Osztott vízszintes"</string>
<string name="recents_multistack_add_stack_dialog_split_vertical" msgid="9075292233696180813">"Osztott függőleges"</string>
<string name="recents_multistack_add_stack_dialog_split_custom" msgid="4177837597513701943">"Osztott egyéni"</string>
diff --git a/packages/SystemUI/res/values-hy-rAM/strings.xml b/packages/SystemUI/res/values-hy-rAM/strings.xml
index 0d50b9c..c201b09 100644
--- a/packages/SystemUI/res/values-hy-rAM/strings.xml
+++ b/packages/SystemUI/res/values-hy-rAM/strings.xml
@@ -301,7 +301,8 @@
<string name="recents_search_bar_label" msgid="8074997400187836677">"որոնել"</string>
<string name="recents_launch_error_message" msgid="2969287838120550506">"Հնարավոր չէ գործարկել <xliff:g id="APP">%s</xliff:g>-ը:"</string>
<string name="recents_show_history_button_label" msgid="7062088196449747245">"Ավելին"</string>
- <string name="recents_history_label" msgid="3076213823382198287">"Պատմություն"</string>
+ <!-- no translation found for recents_history_label_format (6337155608055062429) -->
+ <skip />
<string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="8848514474543427332">"Հորիզոնական տրոհում"</string>
<string name="recents_multistack_add_stack_dialog_split_vertical" msgid="9075292233696180813">"Ուղղահայաց տրոհում"</string>
<string name="recents_multistack_add_stack_dialog_split_custom" msgid="4177837597513701943">"Հատուկ տրոհում"</string>
diff --git a/packages/SystemUI/res/values-in/strings.xml b/packages/SystemUI/res/values-in/strings.xml
index 7ec4685..ce44227 100644
--- a/packages/SystemUI/res/values-in/strings.xml
+++ b/packages/SystemUI/res/values-in/strings.xml
@@ -301,7 +301,7 @@
<string name="recents_search_bar_label" msgid="8074997400187836677">"telusuri"</string>
<string name="recents_launch_error_message" msgid="2969287838120550506">"Tidak dapat memulai <xliff:g id="APP">%s</xliff:g>."</string>
<string name="recents_show_history_button_label" msgid="7062088196449747245">"Lainnya"</string>
- <string name="recents_history_label" msgid="3076213823382198287">"Riwayat"</string>
+ <string name="recents_history_label_format" msgid="6337155608055062429">"<xliff:g id="NUMBER">%d</xliff:g> Lagi"</string>
<string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="8848514474543427332">"Pisahkan Horizontal"</string>
<string name="recents_multistack_add_stack_dialog_split_vertical" msgid="9075292233696180813">"Pisahkan Vertikal"</string>
<string name="recents_multistack_add_stack_dialog_split_custom" msgid="4177837597513701943">"Pisahkan Khusus"</string>
diff --git a/packages/SystemUI/res/values-is-rIS/strings.xml b/packages/SystemUI/res/values-is-rIS/strings.xml
index c17e174..cb35e83 100644
--- a/packages/SystemUI/res/values-is-rIS/strings.xml
+++ b/packages/SystemUI/res/values-is-rIS/strings.xml
@@ -301,7 +301,8 @@
<string name="recents_search_bar_label" msgid="8074997400187836677">"leita"</string>
<string name="recents_launch_error_message" msgid="2969287838120550506">"Ekki var hægt að ræsa <xliff:g id="APP">%s</xliff:g>."</string>
<string name="recents_show_history_button_label" msgid="7062088196449747245">"Meira"</string>
- <string name="recents_history_label" msgid="3076213823382198287">"Ferill"</string>
+ <!-- no translation found for recents_history_label_format (6337155608055062429) -->
+ <skip />
<string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="8848514474543427332">"Lárétt skipting"</string>
<string name="recents_multistack_add_stack_dialog_split_vertical" msgid="9075292233696180813">"Lóðrétt skipting"</string>
<string name="recents_multistack_add_stack_dialog_split_custom" msgid="4177837597513701943">"Sérsniðin skipting"</string>
diff --git a/packages/SystemUI/res/values-it/strings.xml b/packages/SystemUI/res/values-it/strings.xml
index 866f872..147752d 100644
--- a/packages/SystemUI/res/values-it/strings.xml
+++ b/packages/SystemUI/res/values-it/strings.xml
@@ -301,7 +301,8 @@
<string name="recents_search_bar_label" msgid="8074997400187836677">"cerca"</string>
<string name="recents_launch_error_message" msgid="2969287838120550506">"Impossibile avviare <xliff:g id="APP">%s</xliff:g>."</string>
<string name="recents_show_history_button_label" msgid="7062088196449747245">"Altro"</string>
- <string name="recents_history_label" msgid="3076213823382198287">"Cronologia"</string>
+ <!-- no translation found for recents_history_label_format (6337155608055062429) -->
+ <skip />
<string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="8848514474543427332">"Divisione in orizzontale"</string>
<string name="recents_multistack_add_stack_dialog_split_vertical" msgid="9075292233696180813">"Divisione in verticale"</string>
<string name="recents_multistack_add_stack_dialog_split_custom" msgid="4177837597513701943">"Divisione personalizzata"</string>
diff --git a/packages/SystemUI/res/values-iw/strings.xml b/packages/SystemUI/res/values-iw/strings.xml
index 8d3da117..70bea55 100644
--- a/packages/SystemUI/res/values-iw/strings.xml
+++ b/packages/SystemUI/res/values-iw/strings.xml
@@ -303,7 +303,8 @@
<string name="recents_search_bar_label" msgid="8074997400187836677">"חפש"</string>
<string name="recents_launch_error_message" msgid="2969287838120550506">"לא ניתן היה להפעיל את <xliff:g id="APP">%s</xliff:g>."</string>
<string name="recents_show_history_button_label" msgid="7062088196449747245">"עוד"</string>
- <string name="recents_history_label" msgid="3076213823382198287">"היסטוריה"</string>
+ <!-- no translation found for recents_history_label_format (6337155608055062429) -->
+ <skip />
<string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="8848514474543427332">"פיצול אופקי"</string>
<string name="recents_multistack_add_stack_dialog_split_vertical" msgid="9075292233696180813">"פיצול אנכי"</string>
<string name="recents_multistack_add_stack_dialog_split_custom" msgid="4177837597513701943">"פיצול מותאם אישית"</string>
diff --git a/packages/SystemUI/res/values-ja/strings.xml b/packages/SystemUI/res/values-ja/strings.xml
index ae57a32..d91c3b5 100644
--- a/packages/SystemUI/res/values-ja/strings.xml
+++ b/packages/SystemUI/res/values-ja/strings.xml
@@ -301,7 +301,8 @@
<string name="recents_search_bar_label" msgid="8074997400187836677">"検索"</string>
<string name="recents_launch_error_message" msgid="2969287838120550506">"<xliff:g id="APP">%s</xliff:g>を開始できません。"</string>
<string name="recents_show_history_button_label" msgid="7062088196449747245">"もっと見る"</string>
- <string name="recents_history_label" msgid="3076213823382198287">"履歴"</string>
+ <!-- no translation found for recents_history_label_format (6337155608055062429) -->
+ <skip />
<string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="8848514474543427332">"横に分割"</string>
<string name="recents_multistack_add_stack_dialog_split_vertical" msgid="9075292233696180813">"縦に分割"</string>
<string name="recents_multistack_add_stack_dialog_split_custom" msgid="4177837597513701943">"分割(カスタム)"</string>
diff --git a/packages/SystemUI/res/values-ka-rGE/strings.xml b/packages/SystemUI/res/values-ka-rGE/strings.xml
index 18d6f16..51405cb 100644
--- a/packages/SystemUI/res/values-ka-rGE/strings.xml
+++ b/packages/SystemUI/res/values-ka-rGE/strings.xml
@@ -301,7 +301,8 @@
<string name="recents_search_bar_label" msgid="8074997400187836677">"ძიება"</string>
<string name="recents_launch_error_message" msgid="2969287838120550506">"<xliff:g id="APP">%s</xliff:g>-ის გამოძახება ვერ მოხერხდა."</string>
<string name="recents_show_history_button_label" msgid="7062088196449747245">"მეტი"</string>
- <string name="recents_history_label" msgid="3076213823382198287">"ისტორია"</string>
+ <!-- no translation found for recents_history_label_format (6337155608055062429) -->
+ <skip />
<string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="8848514474543427332">"ჰორიზონტალური გაყოფა"</string>
<string name="recents_multistack_add_stack_dialog_split_vertical" msgid="9075292233696180813">"ვერტიკალური გაყოფა"</string>
<string name="recents_multistack_add_stack_dialog_split_custom" msgid="4177837597513701943">"ინდივიდუალური გაყობა"</string>
diff --git a/packages/SystemUI/res/values-kk-rKZ/strings.xml b/packages/SystemUI/res/values-kk-rKZ/strings.xml
index 0d4b618..cde4dd8 100644
--- a/packages/SystemUI/res/values-kk-rKZ/strings.xml
+++ b/packages/SystemUI/res/values-kk-rKZ/strings.xml
@@ -301,7 +301,8 @@
<string name="recents_search_bar_label" msgid="8074997400187836677">"іздеу"</string>
<string name="recents_launch_error_message" msgid="2969287838120550506">"<xliff:g id="APP">%s</xliff:g> іске қосу мүмкін болмады."</string>
<string name="recents_show_history_button_label" msgid="7062088196449747245">"Қосымша"</string>
- <string name="recents_history_label" msgid="3076213823382198287">"Тарих"</string>
+ <!-- no translation found for recents_history_label_format (6337155608055062429) -->
+ <skip />
<string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="8848514474543427332">"Бөлінген көлденең"</string>
<string name="recents_multistack_add_stack_dialog_split_vertical" msgid="9075292233696180813">"Бөлінген тік"</string>
<string name="recents_multistack_add_stack_dialog_split_custom" msgid="4177837597513701943">"Бөлінген теңшелетін"</string>
diff --git a/packages/SystemUI/res/values-km-rKH/strings.xml b/packages/SystemUI/res/values-km-rKH/strings.xml
index 8a72493..c5e0f85 100644
--- a/packages/SystemUI/res/values-km-rKH/strings.xml
+++ b/packages/SystemUI/res/values-km-rKH/strings.xml
@@ -301,7 +301,8 @@
<string name="recents_search_bar_label" msgid="8074997400187836677">"ស្វែងរក"</string>
<string name="recents_launch_error_message" msgid="2969287838120550506">"មិនអាចចាប់ផ្ដើម <xliff:g id="APP">%s</xliff:g> ទេ។"</string>
<string name="recents_show_history_button_label" msgid="7062088196449747245">"ច្រើនទៀត"</string>
- <string name="recents_history_label" msgid="3076213823382198287">"ប្រវត្តិ"</string>
+ <!-- no translation found for recents_history_label_format (6337155608055062429) -->
+ <skip />
<string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="8848514474543427332">"បំបែកផ្តេក"</string>
<string name="recents_multistack_add_stack_dialog_split_vertical" msgid="9075292233696180813">"បំបែកបញ្ឈរ"</string>
<string name="recents_multistack_add_stack_dialog_split_custom" msgid="4177837597513701943">"បំបែកផ្ទាល់ខ្លួន"</string>
diff --git a/packages/SystemUI/res/values-kn-rIN/strings.xml b/packages/SystemUI/res/values-kn-rIN/strings.xml
index d20f0394..6e76b61 100644
--- a/packages/SystemUI/res/values-kn-rIN/strings.xml
+++ b/packages/SystemUI/res/values-kn-rIN/strings.xml
@@ -301,7 +301,8 @@
<string name="recents_search_bar_label" msgid="8074997400187836677">"ಹುಡುಕಾಟ"</string>
<string name="recents_launch_error_message" msgid="2969287838120550506">"<xliff:g id="APP">%s</xliff:g> ಪ್ರಾರಂಭಿಸಲು ಸಾದ್ಯವಿಲ್ಲ."</string>
<string name="recents_show_history_button_label" msgid="7062088196449747245">"ಇನ್ನಷ್ಟು"</string>
- <string name="recents_history_label" msgid="3076213823382198287">"ಇತಿಹಾಸ"</string>
+ <!-- no translation found for recents_history_label_format (6337155608055062429) -->
+ <skip />
<string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="8848514474543427332">"ಅಡ್ಡಲಾಗಿ ವಿಭಜಿಸಿದ"</string>
<string name="recents_multistack_add_stack_dialog_split_vertical" msgid="9075292233696180813">"ಲಂಬವಾಗಿ ವಿಭಜಿಸಿದ"</string>
<string name="recents_multistack_add_stack_dialog_split_custom" msgid="4177837597513701943">"ಕಸ್ಟಮ್ ವಿಭಜಿಸಿದ"</string>
diff --git a/packages/SystemUI/res/values-ko/strings.xml b/packages/SystemUI/res/values-ko/strings.xml
index 2d61eab..04fba52 100644
--- a/packages/SystemUI/res/values-ko/strings.xml
+++ b/packages/SystemUI/res/values-ko/strings.xml
@@ -301,7 +301,8 @@
<string name="recents_search_bar_label" msgid="8074997400187836677">"검색"</string>
<string name="recents_launch_error_message" msgid="2969287838120550506">"<xliff:g id="APP">%s</xliff:g>을(를) 시작할 수 없습니다."</string>
<string name="recents_show_history_button_label" msgid="7062088196449747245">"더보기"</string>
- <string name="recents_history_label" msgid="3076213823382198287">"기록"</string>
+ <!-- no translation found for recents_history_label_format (6337155608055062429) -->
+ <skip />
<string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="8848514474543427332">"수평 분할"</string>
<string name="recents_multistack_add_stack_dialog_split_vertical" msgid="9075292233696180813">"수직 분할"</string>
<string name="recents_multistack_add_stack_dialog_split_custom" msgid="4177837597513701943">"맞춤 분할"</string>
diff --git a/packages/SystemUI/res/values-ky-rKG/strings.xml b/packages/SystemUI/res/values-ky-rKG/strings.xml
index 6d624d1..254843c 100644
--- a/packages/SystemUI/res/values-ky-rKG/strings.xml
+++ b/packages/SystemUI/res/values-ky-rKG/strings.xml
@@ -301,7 +301,8 @@
<string name="recents_search_bar_label" msgid="8074997400187836677">"издөө"</string>
<string name="recents_launch_error_message" msgid="2969287838120550506">"<xliff:g id="APP">%s</xliff:g> баштай алган жок."</string>
<string name="recents_show_history_button_label" msgid="7062088196449747245">"Дагы"</string>
- <string name="recents_history_label" msgid="3076213823382198287">"Таржымал"</string>
+ <!-- no translation found for recents_history_label_format (6337155608055062429) -->
+ <skip />
<string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="8848514474543427332">"Туурасынан бөлүү"</string>
<string name="recents_multistack_add_stack_dialog_split_vertical" msgid="9075292233696180813">"Тигинен бөлүү"</string>
<string name="recents_multistack_add_stack_dialog_split_custom" msgid="4177837597513701943">"Ыңгайлаштырылган бөлүү"</string>
diff --git a/packages/SystemUI/res/values-lo-rLA/strings.xml b/packages/SystemUI/res/values-lo-rLA/strings.xml
index 35a6e1b..91aee1c 100644
--- a/packages/SystemUI/res/values-lo-rLA/strings.xml
+++ b/packages/SystemUI/res/values-lo-rLA/strings.xml
@@ -301,7 +301,8 @@
<string name="recents_search_bar_label" msgid="8074997400187836677">"ຊອກຫາ"</string>
<string name="recents_launch_error_message" msgid="2969287838120550506">"ບໍ່ສາມາດເລີ່ມ <xliff:g id="APP">%s</xliff:g> ໄດ້."</string>
<string name="recents_show_history_button_label" msgid="7062088196449747245">"ເພີ່ມເຕີມ"</string>
- <string name="recents_history_label" msgid="3076213823382198287">"ປະຫວັດ"</string>
+ <!-- no translation found for recents_history_label_format (6337155608055062429) -->
+ <skip />
<string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="8848514474543427332">"ການແຍກລວງຂວາງ"</string>
<string name="recents_multistack_add_stack_dialog_split_vertical" msgid="9075292233696180813">"ການແຍກລວງຕັ້ງ"</string>
<string name="recents_multistack_add_stack_dialog_split_custom" msgid="4177837597513701943">"ການແຍກກຳນົດເອງ"</string>
diff --git a/packages/SystemUI/res/values-lt/strings.xml b/packages/SystemUI/res/values-lt/strings.xml
index 43d5f06..488405f 100644
--- a/packages/SystemUI/res/values-lt/strings.xml
+++ b/packages/SystemUI/res/values-lt/strings.xml
@@ -303,7 +303,8 @@
<string name="recents_search_bar_label" msgid="8074997400187836677">"paieška"</string>
<string name="recents_launch_error_message" msgid="2969287838120550506">"Nepavyko paleisti <xliff:g id="APP">%s</xliff:g>."</string>
<string name="recents_show_history_button_label" msgid="7062088196449747245">"Daugiau"</string>
- <string name="recents_history_label" msgid="3076213823382198287">"Istorija"</string>
+ <!-- no translation found for recents_history_label_format (6337155608055062429) -->
+ <skip />
<string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="8848514474543427332">"Horizontalus skaidymas"</string>
<string name="recents_multistack_add_stack_dialog_split_vertical" msgid="9075292233696180813">"Vertikalus skaidymas"</string>
<string name="recents_multistack_add_stack_dialog_split_custom" msgid="4177837597513701943">"Tinkintas skaidymas"</string>
diff --git a/packages/SystemUI/res/values-lv/strings.xml b/packages/SystemUI/res/values-lv/strings.xml
index 76bc30f..da976e8 100644
--- a/packages/SystemUI/res/values-lv/strings.xml
+++ b/packages/SystemUI/res/values-lv/strings.xml
@@ -302,7 +302,8 @@
<string name="recents_search_bar_label" msgid="8074997400187836677">"Meklēt"</string>
<string name="recents_launch_error_message" msgid="2969287838120550506">"Nevarēja palaist lietotni <xliff:g id="APP">%s</xliff:g>."</string>
<string name="recents_show_history_button_label" msgid="7062088196449747245">"Vairāk"</string>
- <string name="recents_history_label" msgid="3076213823382198287">"Vēsture"</string>
+ <!-- no translation found for recents_history_label_format (6337155608055062429) -->
+ <skip />
<string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="8848514474543427332">"Horizontāls dalījums"</string>
<string name="recents_multistack_add_stack_dialog_split_vertical" msgid="9075292233696180813">"Vertikāls dalījums"</string>
<string name="recents_multistack_add_stack_dialog_split_custom" msgid="4177837597513701943">"Pielāgots dalījums"</string>
diff --git a/packages/SystemUI/res/values-mk-rMK/strings.xml b/packages/SystemUI/res/values-mk-rMK/strings.xml
index c4db311..191dd78 100644
--- a/packages/SystemUI/res/values-mk-rMK/strings.xml
+++ b/packages/SystemUI/res/values-mk-rMK/strings.xml
@@ -301,7 +301,8 @@
<string name="recents_search_bar_label" msgid="8074997400187836677">"пребарај"</string>
<string name="recents_launch_error_message" msgid="2969287838120550506">"<xliff:g id="APP">%s</xliff:g> не може да се вклучи."</string>
<string name="recents_show_history_button_label" msgid="7062088196449747245">"Повеќе"</string>
- <string name="recents_history_label" msgid="3076213823382198287">"Историја"</string>
+ <!-- no translation found for recents_history_label_format (6337155608055062429) -->
+ <skip />
<string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="8848514474543427332">"Раздели хоризонтално"</string>
<string name="recents_multistack_add_stack_dialog_split_vertical" msgid="9075292233696180813">"Раздели вертикално"</string>
<string name="recents_multistack_add_stack_dialog_split_custom" msgid="4177837597513701943">"Раздели прилагодено"</string>
diff --git a/packages/SystemUI/res/values-ml-rIN/strings.xml b/packages/SystemUI/res/values-ml-rIN/strings.xml
index 20ca46e..c57b9ff7 100644
--- a/packages/SystemUI/res/values-ml-rIN/strings.xml
+++ b/packages/SystemUI/res/values-ml-rIN/strings.xml
@@ -301,7 +301,8 @@
<string name="recents_search_bar_label" msgid="8074997400187836677">"തിരയുക"</string>
<string name="recents_launch_error_message" msgid="2969287838120550506">"<xliff:g id="APP">%s</xliff:g> ആരംഭിക്കാനായില്ല."</string>
<string name="recents_show_history_button_label" msgid="7062088196449747245">"കൂടുതൽ"</string>
- <string name="recents_history_label" msgid="3076213823382198287">"ചരിത്രം"</string>
+ <!-- no translation found for recents_history_label_format (6337155608055062429) -->
+ <skip />
<string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="8848514474543427332">"തിരശ്ചീനമായി വേർതിരിക്കുക"</string>
<string name="recents_multistack_add_stack_dialog_split_vertical" msgid="9075292233696180813">"ലംബമായി വേർതിരിക്കുക"</string>
<string name="recents_multistack_add_stack_dialog_split_custom" msgid="4177837597513701943">"ഇഷ്ടാനുസൃതമായി വേർതിരിക്കുക"</string>
diff --git a/packages/SystemUI/res/values-mn-rMN/strings.xml b/packages/SystemUI/res/values-mn-rMN/strings.xml
index 1a15870..5797dc7 100644
--- a/packages/SystemUI/res/values-mn-rMN/strings.xml
+++ b/packages/SystemUI/res/values-mn-rMN/strings.xml
@@ -299,7 +299,8 @@
<string name="recents_search_bar_label" msgid="8074997400187836677">"хайх"</string>
<string name="recents_launch_error_message" msgid="2969287838120550506">"<xliff:g id="APP">%s</xliff:g>-г эхлүүлж чадсангүй."</string>
<string name="recents_show_history_button_label" msgid="7062088196449747245">"Илүү"</string>
- <string name="recents_history_label" msgid="3076213823382198287">"Түүх"</string>
+ <!-- no translation found for recents_history_label_format (6337155608055062429) -->
+ <skip />
<string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="8848514474543427332">"Хэвтээ чиглэлд хуваах"</string>
<string name="recents_multistack_add_stack_dialog_split_vertical" msgid="9075292233696180813">"Босоо чиглэлд хуваах"</string>
<string name="recents_multistack_add_stack_dialog_split_custom" msgid="4177837597513701943">"Хүссэн хэлбэрээр хуваах"</string>
diff --git a/packages/SystemUI/res/values-mr-rIN/strings.xml b/packages/SystemUI/res/values-mr-rIN/strings.xml
index 4729a04..2d460d9 100644
--- a/packages/SystemUI/res/values-mr-rIN/strings.xml
+++ b/packages/SystemUI/res/values-mr-rIN/strings.xml
@@ -301,7 +301,8 @@
<string name="recents_search_bar_label" msgid="8074997400187836677">"शोधा"</string>
<string name="recents_launch_error_message" msgid="2969287838120550506">"<xliff:g id="APP">%s</xliff:g> प्रारंभ करणे शक्य झाले नाही."</string>
<string name="recents_show_history_button_label" msgid="7062088196449747245">"अधिक"</string>
- <string name="recents_history_label" msgid="3076213823382198287">"इतिहास"</string>
+ <!-- no translation found for recents_history_label_format (6337155608055062429) -->
+ <skip />
<string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="8848514474543427332">"क्षैतिज विभाजित करा"</string>
<string name="recents_multistack_add_stack_dialog_split_vertical" msgid="9075292233696180813">"अनुलंब विभाजित करा"</string>
<string name="recents_multistack_add_stack_dialog_split_custom" msgid="4177837597513701943">"सानुकूल विभाजित करा"</string>
diff --git a/packages/SystemUI/res/values-ms-rMY/strings.xml b/packages/SystemUI/res/values-ms-rMY/strings.xml
index 9a0a0c0..3fc1d05 100644
--- a/packages/SystemUI/res/values-ms-rMY/strings.xml
+++ b/packages/SystemUI/res/values-ms-rMY/strings.xml
@@ -301,7 +301,8 @@
<string name="recents_search_bar_label" msgid="8074997400187836677">"cari"</string>
<string name="recents_launch_error_message" msgid="2969287838120550506">"Tidak dapat memulakan <xliff:g id="APP">%s</xliff:g>."</string>
<string name="recents_show_history_button_label" msgid="7062088196449747245">"Lagi"</string>
- <string name="recents_history_label" msgid="3076213823382198287">"Sejarah"</string>
+ <!-- no translation found for recents_history_label_format (6337155608055062429) -->
+ <skip />
<string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="8848514474543427332">"Mendatar Terpisah"</string>
<string name="recents_multistack_add_stack_dialog_split_vertical" msgid="9075292233696180813">"Menegak Terpisah"</string>
<string name="recents_multistack_add_stack_dialog_split_custom" msgid="4177837597513701943">"Tersuai Terpisah"</string>
diff --git a/packages/SystemUI/res/values-my-rMM/strings.xml b/packages/SystemUI/res/values-my-rMM/strings.xml
index 3f1b8a7..69dcc77 100644
--- a/packages/SystemUI/res/values-my-rMM/strings.xml
+++ b/packages/SystemUI/res/values-my-rMM/strings.xml
@@ -301,7 +301,8 @@
<string name="recents_search_bar_label" msgid="8074997400187836677">"ရှာဖွေရန်"</string>
<string name="recents_launch_error_message" msgid="2969287838120550506">"<xliff:g id="APP">%s</xliff:g> ကို မစနိုင်ပါ။"</string>
<string name="recents_show_history_button_label" msgid="7062088196449747245">"နောက်ထပ်"</string>
- <string name="recents_history_label" msgid="3076213823382198287">"မှတ်တမ်း"</string>
+ <!-- no translation found for recents_history_label_format (6337155608055062429) -->
+ <skip />
<string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="8848514474543427332">"ရေပြင်ညီ ပိုင်းမည်"</string>
<string name="recents_multistack_add_stack_dialog_split_vertical" msgid="9075292233696180813">"ဒေါင်လိုက်ပိုင်းမည်"</string>
<string name="recents_multistack_add_stack_dialog_split_custom" msgid="4177837597513701943">"စိတ်ကြိုက် ပိုင်းမည်"</string>
diff --git a/packages/SystemUI/res/values-nb/strings.xml b/packages/SystemUI/res/values-nb/strings.xml
index 8bcf35e..94653cf 100644
--- a/packages/SystemUI/res/values-nb/strings.xml
+++ b/packages/SystemUI/res/values-nb/strings.xml
@@ -301,7 +301,8 @@
<string name="recents_search_bar_label" msgid="8074997400187836677">"Søk"</string>
<string name="recents_launch_error_message" msgid="2969287838120550506">"Kunne ikke starte <xliff:g id="APP">%s</xliff:g>."</string>
<string name="recents_show_history_button_label" msgid="7062088196449747245">"Mer"</string>
- <string name="recents_history_label" msgid="3076213823382198287">"Logg"</string>
+ <!-- no translation found for recents_history_label_format (6337155608055062429) -->
+ <skip />
<string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="8848514474543427332">"Del horisontalt"</string>
<string name="recents_multistack_add_stack_dialog_split_vertical" msgid="9075292233696180813">"Del vertikalt"</string>
<string name="recents_multistack_add_stack_dialog_split_custom" msgid="4177837597513701943">"Del tilpasset"</string>
diff --git a/packages/SystemUI/res/values-ne-rNP/strings.xml b/packages/SystemUI/res/values-ne-rNP/strings.xml
index 49413b3..bc4f1a9 100644
--- a/packages/SystemUI/res/values-ne-rNP/strings.xml
+++ b/packages/SystemUI/res/values-ne-rNP/strings.xml
@@ -301,7 +301,7 @@
<string name="recents_search_bar_label" msgid="8074997400187836677">"खोजी गर्नुहोस्"</string>
<string name="recents_launch_error_message" msgid="2969287838120550506">"सुरु गर्न सकिएन <xliff:g id="APP">%s</xliff:g>।"</string>
<string name="recents_show_history_button_label" msgid="7062088196449747245">"थप"</string>
- <string name="recents_history_label" msgid="3076213823382198287">"इतिहास"</string>
+ <string name="recents_history_label_format" msgid="6337155608055062429">"<xliff:g id="NUMBER">%d</xliff:g> थप"</string>
<string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="8848514474543427332">"तेर्सो रूपमा विभाजन गर्नुहोस्"</string>
<string name="recents_multistack_add_stack_dialog_split_vertical" msgid="9075292233696180813">"ठाडो रूपमा विभाजन गर्नुहोस्"</string>
<string name="recents_multistack_add_stack_dialog_split_custom" msgid="4177837597513701943">"अनुकूलन विभाजन गर्नुहोस्"</string>
diff --git a/packages/SystemUI/res/values-nl/strings.xml b/packages/SystemUI/res/values-nl/strings.xml
index 7ded71c..62cef0f 100644
--- a/packages/SystemUI/res/values-nl/strings.xml
+++ b/packages/SystemUI/res/values-nl/strings.xml
@@ -301,7 +301,8 @@
<string name="recents_search_bar_label" msgid="8074997400187836677">"zoeken"</string>
<string name="recents_launch_error_message" msgid="2969287838120550506">"Kan <xliff:g id="APP">%s</xliff:g> niet starten."</string>
<string name="recents_show_history_button_label" msgid="7062088196449747245">"Meer"</string>
- <string name="recents_history_label" msgid="3076213823382198287">"Geschiedenis"</string>
+ <!-- no translation found for recents_history_label_format (6337155608055062429) -->
+ <skip />
<string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="8848514474543427332">"Horizontaal splitsen"</string>
<string name="recents_multistack_add_stack_dialog_split_vertical" msgid="9075292233696180813">"Verticaal splitsen"</string>
<string name="recents_multistack_add_stack_dialog_split_custom" msgid="4177837597513701943">"Aangepast splitsen"</string>
diff --git a/packages/SystemUI/res/values-pa-rIN/strings.xml b/packages/SystemUI/res/values-pa-rIN/strings.xml
index 3a6fa0f..d7fe0a1 100644
--- a/packages/SystemUI/res/values-pa-rIN/strings.xml
+++ b/packages/SystemUI/res/values-pa-rIN/strings.xml
@@ -301,7 +301,8 @@
<string name="recents_search_bar_label" msgid="8074997400187836677">"ਖੋਜੋ"</string>
<string name="recents_launch_error_message" msgid="2969287838120550506">"<xliff:g id="APP">%s</xliff:g> ਨੂੰ ਚਾਲੂ ਨਹੀਂ ਕਰ ਸਕਿਆ।"</string>
<string name="recents_show_history_button_label" msgid="7062088196449747245">"ਹੋਰ"</string>
- <string name="recents_history_label" msgid="3076213823382198287">"ਇਤਿਹਾਸ"</string>
+ <!-- no translation found for recents_history_label_format (6337155608055062429) -->
+ <skip />
<string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="8848514474543427332">"ਹੌਰੀਜ਼ੌਂਟਲ ਸਪਲਿਟ"</string>
<string name="recents_multistack_add_stack_dialog_split_vertical" msgid="9075292233696180813">"ਵਰਟੀਕਲ ਸਪਲਿਟ"</string>
<string name="recents_multistack_add_stack_dialog_split_custom" msgid="4177837597513701943">"ਕਸਟਮ ਸਪਲਿਟ"</string>
diff --git a/packages/SystemUI/res/values-pl/strings.xml b/packages/SystemUI/res/values-pl/strings.xml
index 42c8f6e..360e15d 100644
--- a/packages/SystemUI/res/values-pl/strings.xml
+++ b/packages/SystemUI/res/values-pl/strings.xml
@@ -303,7 +303,8 @@
<string name="recents_search_bar_label" msgid="8074997400187836677">"szukaj"</string>
<string name="recents_launch_error_message" msgid="2969287838120550506">"Nie udało się uruchomić aplikacji <xliff:g id="APP">%s</xliff:g>."</string>
<string name="recents_show_history_button_label" msgid="7062088196449747245">"Więcej"</string>
- <string name="recents_history_label" msgid="3076213823382198287">"Historia"</string>
+ <!-- no translation found for recents_history_label_format (6337155608055062429) -->
+ <skip />
<string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="8848514474543427332">"Podziel poziomo"</string>
<string name="recents_multistack_add_stack_dialog_split_vertical" msgid="9075292233696180813">"Podziel pionowo"</string>
<string name="recents_multistack_add_stack_dialog_split_custom" msgid="4177837597513701943">"Podziel niestandardowo"</string>
diff --git a/packages/SystemUI/res/values-pt-rBR/strings.xml b/packages/SystemUI/res/values-pt-rBR/strings.xml
index e009cea..1a3212e 100644
--- a/packages/SystemUI/res/values-pt-rBR/strings.xml
+++ b/packages/SystemUI/res/values-pt-rBR/strings.xml
@@ -301,7 +301,8 @@
<string name="recents_search_bar_label" msgid="8074997400187836677">"pesquisar"</string>
<string name="recents_launch_error_message" msgid="2969287838120550506">"Não foi possível iniciar <xliff:g id="APP">%s</xliff:g>."</string>
<string name="recents_show_history_button_label" msgid="7062088196449747245">"Mais"</string>
- <string name="recents_history_label" msgid="3076213823382198287">"Histórico"</string>
+ <!-- no translation found for recents_history_label_format (6337155608055062429) -->
+ <skip />
<string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="8848514474543427332">"Divisão horizontal"</string>
<string name="recents_multistack_add_stack_dialog_split_vertical" msgid="9075292233696180813">"Divisão vertical"</string>
<string name="recents_multistack_add_stack_dialog_split_custom" msgid="4177837597513701943">"Divisão personalizada"</string>
diff --git a/packages/SystemUI/res/values-pt-rPT/strings.xml b/packages/SystemUI/res/values-pt-rPT/strings.xml
index 47949e0..c95242f 100644
--- a/packages/SystemUI/res/values-pt-rPT/strings.xml
+++ b/packages/SystemUI/res/values-pt-rPT/strings.xml
@@ -301,7 +301,8 @@
<string name="recents_search_bar_label" msgid="8074997400187836677">"pesquisar"</string>
<string name="recents_launch_error_message" msgid="2969287838120550506">"Não foi possível iniciar o <xliff:g id="APP">%s</xliff:g>."</string>
<string name="recents_show_history_button_label" msgid="7062088196449747245">"Mais"</string>
- <string name="recents_history_label" msgid="3076213823382198287">"Histórico"</string>
+ <!-- no translation found for recents_history_label_format (6337155608055062429) -->
+ <skip />
<string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="8848514474543427332">"Divisão horizontal"</string>
<string name="recents_multistack_add_stack_dialog_split_vertical" msgid="9075292233696180813">"Divisão vertical"</string>
<string name="recents_multistack_add_stack_dialog_split_custom" msgid="4177837597513701943">"Divisão personalizada"</string>
diff --git a/packages/SystemUI/res/values-pt/strings.xml b/packages/SystemUI/res/values-pt/strings.xml
index e009cea..1a3212e 100644
--- a/packages/SystemUI/res/values-pt/strings.xml
+++ b/packages/SystemUI/res/values-pt/strings.xml
@@ -301,7 +301,8 @@
<string name="recents_search_bar_label" msgid="8074997400187836677">"pesquisar"</string>
<string name="recents_launch_error_message" msgid="2969287838120550506">"Não foi possível iniciar <xliff:g id="APP">%s</xliff:g>."</string>
<string name="recents_show_history_button_label" msgid="7062088196449747245">"Mais"</string>
- <string name="recents_history_label" msgid="3076213823382198287">"Histórico"</string>
+ <!-- no translation found for recents_history_label_format (6337155608055062429) -->
+ <skip />
<string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="8848514474543427332">"Divisão horizontal"</string>
<string name="recents_multistack_add_stack_dialog_split_vertical" msgid="9075292233696180813">"Divisão vertical"</string>
<string name="recents_multistack_add_stack_dialog_split_custom" msgid="4177837597513701943">"Divisão personalizada"</string>
diff --git a/packages/SystemUI/res/values-ro/strings.xml b/packages/SystemUI/res/values-ro/strings.xml
index 03b9556..3f0b2a4 100644
--- a/packages/SystemUI/res/values-ro/strings.xml
+++ b/packages/SystemUI/res/values-ro/strings.xml
@@ -302,7 +302,8 @@
<string name="recents_search_bar_label" msgid="8074997400187836677">"căutare"</string>
<string name="recents_launch_error_message" msgid="2969287838120550506">"<xliff:g id="APP">%s</xliff:g> nu a putut porni."</string>
<string name="recents_show_history_button_label" msgid="7062088196449747245">"Mai mult"</string>
- <string name="recents_history_label" msgid="3076213823382198287">"Istoric"</string>
+ <!-- no translation found for recents_history_label_format (6337155608055062429) -->
+ <skip />
<string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="8848514474543427332">"Divizare pe orizontală"</string>
<string name="recents_multistack_add_stack_dialog_split_vertical" msgid="9075292233696180813">"Divizare pe verticală"</string>
<string name="recents_multistack_add_stack_dialog_split_custom" msgid="4177837597513701943">"Divizare personalizată"</string>
diff --git a/packages/SystemUI/res/values-ru/strings.xml b/packages/SystemUI/res/values-ru/strings.xml
index a196ed6..b92dadc 100644
--- a/packages/SystemUI/res/values-ru/strings.xml
+++ b/packages/SystemUI/res/values-ru/strings.xml
@@ -303,7 +303,8 @@
<string name="recents_search_bar_label" msgid="8074997400187836677">"поиск"</string>
<string name="recents_launch_error_message" msgid="2969287838120550506">"Не удалось запустить приложение \"<xliff:g id="APP">%s</xliff:g>\""</string>
<string name="recents_show_history_button_label" msgid="7062088196449747245">"Ещё"</string>
- <string name="recents_history_label" msgid="3076213823382198287">"Журнал"</string>
+ <!-- no translation found for recents_history_label_format (6337155608055062429) -->
+ <skip />
<string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="8848514474543427332">"Разделить по горизонтали"</string>
<string name="recents_multistack_add_stack_dialog_split_vertical" msgid="9075292233696180813">"Разделить по вертикали"</string>
<string name="recents_multistack_add_stack_dialog_split_custom" msgid="4177837597513701943">"Разделить по-другому"</string>
diff --git a/packages/SystemUI/res/values-si-rLK/strings.xml b/packages/SystemUI/res/values-si-rLK/strings.xml
index 297ef78..4a18f6e 100644
--- a/packages/SystemUI/res/values-si-rLK/strings.xml
+++ b/packages/SystemUI/res/values-si-rLK/strings.xml
@@ -301,7 +301,8 @@
<string name="recents_search_bar_label" msgid="8074997400187836677">"සෙවීම"</string>
<string name="recents_launch_error_message" msgid="2969287838120550506">"<xliff:g id="APP">%s</xliff:g> ආරම්භ කළ නොහැක."</string>
<string name="recents_show_history_button_label" msgid="7062088196449747245">"තවත්"</string>
- <string name="recents_history_label" msgid="3076213823382198287">"ඉතිහාසය"</string>
+ <!-- no translation found for recents_history_label_format (6337155608055062429) -->
+ <skip />
<string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="8848514474543427332">"තිරස්ව වෙන් කරන්න"</string>
<string name="recents_multistack_add_stack_dialog_split_vertical" msgid="9075292233696180813">"සිරස්ව වෙන් කරන්න"</string>
<string name="recents_multistack_add_stack_dialog_split_custom" msgid="4177837597513701943">"අභිමත ලෙස වෙන් කරන්න"</string>
diff --git a/packages/SystemUI/res/values-sk/strings.xml b/packages/SystemUI/res/values-sk/strings.xml
index 8c77eaf..51a61fe 100644
--- a/packages/SystemUI/res/values-sk/strings.xml
+++ b/packages/SystemUI/res/values-sk/strings.xml
@@ -303,7 +303,8 @@
<string name="recents_search_bar_label" msgid="8074997400187836677">"hľadať"</string>
<string name="recents_launch_error_message" msgid="2969287838120550506">"Aplikáciu <xliff:g id="APP">%s</xliff:g> sa nepodarilo spustiť"</string>
<string name="recents_show_history_button_label" msgid="7062088196449747245">"Viac"</string>
- <string name="recents_history_label" msgid="3076213823382198287">"História"</string>
+ <!-- no translation found for recents_history_label_format (6337155608055062429) -->
+ <skip />
<string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="8848514474543427332">"Rozdeliť vodorovné"</string>
<string name="recents_multistack_add_stack_dialog_split_vertical" msgid="9075292233696180813">"Rozdeliť zvislé"</string>
<string name="recents_multistack_add_stack_dialog_split_custom" msgid="4177837597513701943">"Rozdeliť vlastné"</string>
diff --git a/packages/SystemUI/res/values-sl/strings.xml b/packages/SystemUI/res/values-sl/strings.xml
index 9578dee..cdfa2e9 100644
--- a/packages/SystemUI/res/values-sl/strings.xml
+++ b/packages/SystemUI/res/values-sl/strings.xml
@@ -303,7 +303,8 @@
<string name="recents_search_bar_label" msgid="8074997400187836677">"iskanje"</string>
<string name="recents_launch_error_message" msgid="2969287838120550506">"Aplikacije <xliff:g id="APP">%s</xliff:g> ni bilo mogoče zagnati."</string>
<string name="recents_show_history_button_label" msgid="7062088196449747245">"Več"</string>
- <string name="recents_history_label" msgid="3076213823382198287">"Zgodovina"</string>
+ <!-- no translation found for recents_history_label_format (6337155608055062429) -->
+ <skip />
<string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="8848514474543427332">"Razdeli vodoravno"</string>
<string name="recents_multistack_add_stack_dialog_split_vertical" msgid="9075292233696180813">"Razdeli navpično"</string>
<string name="recents_multistack_add_stack_dialog_split_custom" msgid="4177837597513701943">"Razdeli po meri"</string>
diff --git a/packages/SystemUI/res/values-sq-rAL/strings.xml b/packages/SystemUI/res/values-sq-rAL/strings.xml
index 8b19bd8..ed0ad1e 100644
--- a/packages/SystemUI/res/values-sq-rAL/strings.xml
+++ b/packages/SystemUI/res/values-sq-rAL/strings.xml
@@ -301,7 +301,8 @@
<string name="recents_search_bar_label" msgid="8074997400187836677">"kërko"</string>
<string name="recents_launch_error_message" msgid="2969287838120550506">"<xliff:g id="APP">%s</xliff:g> nuk mundi të nisej."</string>
<string name="recents_show_history_button_label" msgid="7062088196449747245">"Më shumë"</string>
- <string name="recents_history_label" msgid="3076213823382198287">"Historiku"</string>
+ <!-- no translation found for recents_history_label_format (6337155608055062429) -->
+ <skip />
<string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="8848514474543427332">"Ndaje horizontalisht"</string>
<string name="recents_multistack_add_stack_dialog_split_vertical" msgid="9075292233696180813">"Ndaj vertikalisht"</string>
<string name="recents_multistack_add_stack_dialog_split_custom" msgid="4177837597513701943">"Ndaj të personalizuarën"</string>
diff --git a/packages/SystemUI/res/values-sr/strings.xml b/packages/SystemUI/res/values-sr/strings.xml
index f22fa4e..26c966a 100644
--- a/packages/SystemUI/res/values-sr/strings.xml
+++ b/packages/SystemUI/res/values-sr/strings.xml
@@ -302,7 +302,8 @@
<string name="recents_search_bar_label" msgid="8074997400187836677">"претражи"</string>
<string name="recents_launch_error_message" msgid="2969287838120550506">"Покретање апликације <xliff:g id="APP">%s</xliff:g> није успело."</string>
<string name="recents_show_history_button_label" msgid="7062088196449747245">"Још"</string>
- <string name="recents_history_label" msgid="3076213823382198287">"Историја"</string>
+ <!-- no translation found for recents_history_label_format (6337155608055062429) -->
+ <skip />
<string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="8848514474543427332">"Подели хоризонтално"</string>
<string name="recents_multistack_add_stack_dialog_split_vertical" msgid="9075292233696180813">"Подели вертикално"</string>
<string name="recents_multistack_add_stack_dialog_split_custom" msgid="4177837597513701943">"Прилагођено дељење"</string>
diff --git a/packages/SystemUI/res/values-sv/strings.xml b/packages/SystemUI/res/values-sv/strings.xml
index 269881a..f1d691e 100644
--- a/packages/SystemUI/res/values-sv/strings.xml
+++ b/packages/SystemUI/res/values-sv/strings.xml
@@ -301,7 +301,8 @@
<string name="recents_search_bar_label" msgid="8074997400187836677">"sök"</string>
<string name="recents_launch_error_message" msgid="2969287838120550506">"Det gick inte att starta appen <xliff:g id="APP">%s</xliff:g>."</string>
<string name="recents_show_history_button_label" msgid="7062088196449747245">"Mer"</string>
- <string name="recents_history_label" msgid="3076213823382198287">"Historik"</string>
+ <!-- no translation found for recents_history_label_format (6337155608055062429) -->
+ <skip />
<string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="8848514474543427332">"Dela horisontellt"</string>
<string name="recents_multistack_add_stack_dialog_split_vertical" msgid="9075292233696180813">"Dela vertikalt"</string>
<string name="recents_multistack_add_stack_dialog_split_custom" msgid="4177837597513701943">"Dela anpassad"</string>
diff --git a/packages/SystemUI/res/values-sw/strings.xml b/packages/SystemUI/res/values-sw/strings.xml
index 2696242..4101ba7 100644
--- a/packages/SystemUI/res/values-sw/strings.xml
+++ b/packages/SystemUI/res/values-sw/strings.xml
@@ -301,7 +301,8 @@
<string name="recents_search_bar_label" msgid="8074997400187836677">"tafuta"</string>
<string name="recents_launch_error_message" msgid="2969287838120550506">"Haikuweza kuanzisha <xliff:g id="APP">%s</xliff:g>."</string>
<string name="recents_show_history_button_label" msgid="7062088196449747245">"Zaidi"</string>
- <string name="recents_history_label" msgid="3076213823382198287">"Historia"</string>
+ <!-- no translation found for recents_history_label_format (6337155608055062429) -->
+ <skip />
<string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="8848514474543427332">"Gawanya Mlalo"</string>
<string name="recents_multistack_add_stack_dialog_split_vertical" msgid="9075292233696180813">"Gawanya Wima"</string>
<string name="recents_multistack_add_stack_dialog_split_custom" msgid="4177837597513701943">"Maalum Iliyogawanywa"</string>
diff --git a/packages/SystemUI/res/values-ta-rIN/strings.xml b/packages/SystemUI/res/values-ta-rIN/strings.xml
index aa7266e..e3bed08 100644
--- a/packages/SystemUI/res/values-ta-rIN/strings.xml
+++ b/packages/SystemUI/res/values-ta-rIN/strings.xml
@@ -301,7 +301,8 @@
<string name="recents_search_bar_label" msgid="8074997400187836677">"தேடு"</string>
<string name="recents_launch_error_message" msgid="2969287838120550506">"<xliff:g id="APP">%s</xliff:g>ஐத் தொடங்க முடியவில்லை."</string>
<string name="recents_show_history_button_label" msgid="7062088196449747245">"மேலும்"</string>
- <string name="recents_history_label" msgid="3076213823382198287">"வரலாறு"</string>
+ <!-- no translation found for recents_history_label_format (6337155608055062429) -->
+ <skip />
<string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="8848514474543427332">"கிடைமட்டமாகப் பிரி"</string>
<string name="recents_multistack_add_stack_dialog_split_vertical" msgid="9075292233696180813">"செங்குத்தாகப் பிரி"</string>
<string name="recents_multistack_add_stack_dialog_split_custom" msgid="4177837597513701943">"தனிவிருப்பத்தில் பிரி"</string>
diff --git a/packages/SystemUI/res/values-te-rIN/strings.xml b/packages/SystemUI/res/values-te-rIN/strings.xml
index 06b7ccf3..e725d95 100644
--- a/packages/SystemUI/res/values-te-rIN/strings.xml
+++ b/packages/SystemUI/res/values-te-rIN/strings.xml
@@ -301,7 +301,7 @@
<string name="recents_search_bar_label" msgid="8074997400187836677">"శోధించు"</string>
<string name="recents_launch_error_message" msgid="2969287838120550506">"<xliff:g id="APP">%s</xliff:g>ని ప్రారంభించడం సాధ్యపడలేదు."</string>
<string name="recents_show_history_button_label" msgid="7062088196449747245">"మరింత"</string>
- <string name="recents_history_label" msgid="3076213823382198287">"చరిత్ర"</string>
+ <string name="recents_history_label_format" msgid="6337155608055062429">"మరో <xliff:g id="NUMBER">%d</xliff:g>"</string>
<string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="8848514474543427332">"సమతలంగా విభజించు"</string>
<string name="recents_multistack_add_stack_dialog_split_vertical" msgid="9075292233696180813">"లంబంగా విభజించు"</string>
<string name="recents_multistack_add_stack_dialog_split_custom" msgid="4177837597513701943">"అనుకూలంగా విభజించు"</string>
diff --git a/packages/SystemUI/res/values-th/strings.xml b/packages/SystemUI/res/values-th/strings.xml
index eb4eb27..a60157f 100644
--- a/packages/SystemUI/res/values-th/strings.xml
+++ b/packages/SystemUI/res/values-th/strings.xml
@@ -301,7 +301,8 @@
<string name="recents_search_bar_label" msgid="8074997400187836677">"ค้นหา"</string>
<string name="recents_launch_error_message" msgid="2969287838120550506">"ไม่สามารถเริ่มใช้ <xliff:g id="APP">%s</xliff:g>"</string>
<string name="recents_show_history_button_label" msgid="7062088196449747245">"เพิ่มเติม"</string>
- <string name="recents_history_label" msgid="3076213823382198287">"ประวัติ"</string>
+ <!-- no translation found for recents_history_label_format (6337155608055062429) -->
+ <skip />
<string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="8848514474543427332">"แยกในแนวนอน"</string>
<string name="recents_multistack_add_stack_dialog_split_vertical" msgid="9075292233696180813">"แยกในแนวตั้ง"</string>
<string name="recents_multistack_add_stack_dialog_split_custom" msgid="4177837597513701943">"แยกแบบกำหนดเอง"</string>
diff --git a/packages/SystemUI/res/values-tl/strings.xml b/packages/SystemUI/res/values-tl/strings.xml
index a04d66b..a19c94d 100644
--- a/packages/SystemUI/res/values-tl/strings.xml
+++ b/packages/SystemUI/res/values-tl/strings.xml
@@ -301,7 +301,8 @@
<string name="recents_search_bar_label" msgid="8074997400187836677">"maghanap"</string>
<string name="recents_launch_error_message" msgid="2969287838120550506">"Hindi masimulan <xliff:g id="APP">%s</xliff:g>."</string>
<string name="recents_show_history_button_label" msgid="7062088196449747245">"Higit pa"</string>
- <string name="recents_history_label" msgid="3076213823382198287">"History"</string>
+ <!-- no translation found for recents_history_label_format (6337155608055062429) -->
+ <skip />
<string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="8848514474543427332">"Split Horizontal"</string>
<string name="recents_multistack_add_stack_dialog_split_vertical" msgid="9075292233696180813">"Split Vertical"</string>
<string name="recents_multistack_add_stack_dialog_split_custom" msgid="4177837597513701943">"Split Custom"</string>
diff --git a/packages/SystemUI/res/values-tr/strings.xml b/packages/SystemUI/res/values-tr/strings.xml
index c549798..f210783 100644
--- a/packages/SystemUI/res/values-tr/strings.xml
+++ b/packages/SystemUI/res/values-tr/strings.xml
@@ -301,7 +301,8 @@
<string name="recents_search_bar_label" msgid="8074997400187836677">"ara"</string>
<string name="recents_launch_error_message" msgid="2969287838120550506">"<xliff:g id="APP">%s</xliff:g> başlatılamadı."</string>
<string name="recents_show_history_button_label" msgid="7062088196449747245">"Diğer"</string>
- <string name="recents_history_label" msgid="3076213823382198287">"Geçmiş"</string>
+ <!-- no translation found for recents_history_label_format (6337155608055062429) -->
+ <skip />
<string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="8848514474543427332">"Yatay Ayırma"</string>
<string name="recents_multistack_add_stack_dialog_split_vertical" msgid="9075292233696180813">"Dikey Ayırma"</string>
<string name="recents_multistack_add_stack_dialog_split_custom" msgid="4177837597513701943">"Özel Ayırma"</string>
diff --git a/packages/SystemUI/res/values-uk/strings.xml b/packages/SystemUI/res/values-uk/strings.xml
index a58c13c..8a1ec97 100644
--- a/packages/SystemUI/res/values-uk/strings.xml
+++ b/packages/SystemUI/res/values-uk/strings.xml
@@ -303,7 +303,7 @@
<string name="recents_search_bar_label" msgid="8074997400187836677">"пошук"</string>
<string name="recents_launch_error_message" msgid="2969287838120550506">"Не вдалося запустити <xliff:g id="APP">%s</xliff:g>."</string>
<string name="recents_show_history_button_label" msgid="7062088196449747245">"Більше"</string>
- <string name="recents_history_label" msgid="3076213823382198287">"Історія"</string>
+ <string name="recents_history_label_format" msgid="6337155608055062429">"Ще <xliff:g id="NUMBER">%d</xliff:g>"</string>
<string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="8848514474543427332">"Розділити горизонтально"</string>
<string name="recents_multistack_add_stack_dialog_split_vertical" msgid="9075292233696180813">"Розділити вертикально"</string>
<string name="recents_multistack_add_stack_dialog_split_custom" msgid="4177837597513701943">"Розділити (власний варіант)"</string>
diff --git a/packages/SystemUI/res/values-ur-rPK/strings.xml b/packages/SystemUI/res/values-ur-rPK/strings.xml
index 9e3a6b5..ea7a780 100644
--- a/packages/SystemUI/res/values-ur-rPK/strings.xml
+++ b/packages/SystemUI/res/values-ur-rPK/strings.xml
@@ -301,7 +301,8 @@
<string name="recents_search_bar_label" msgid="8074997400187836677">"تلاش کریں"</string>
<string name="recents_launch_error_message" msgid="2969287838120550506">"<xliff:g id="APP">%s</xliff:g> کو شروع نہیں کیا جا سکا۔"</string>
<string name="recents_show_history_button_label" msgid="7062088196449747245">"مزید"</string>
- <string name="recents_history_label" msgid="3076213823382198287">"سرگزشت"</string>
+ <!-- no translation found for recents_history_label_format (6337155608055062429) -->
+ <skip />
<string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="8848514474543427332">"بلحاظ افقی الگ کریں"</string>
<string name="recents_multistack_add_stack_dialog_split_vertical" msgid="9075292233696180813">"بلحاظ عمودی الگ کریں"</string>
<string name="recents_multistack_add_stack_dialog_split_custom" msgid="4177837597513701943">"بلحاظ حسب ضرورت الگ کریں"</string>
diff --git a/packages/SystemUI/res/values-uz-rUZ/strings.xml b/packages/SystemUI/res/values-uz-rUZ/strings.xml
index d011169..249de95 100644
--- a/packages/SystemUI/res/values-uz-rUZ/strings.xml
+++ b/packages/SystemUI/res/values-uz-rUZ/strings.xml
@@ -29,7 +29,7 @@
<item quantity="other">Umumiy ma’lumot bo‘limida %d ta ekran bor</item>
<item quantity="one">Umumiy ma’lumot bo‘limida 1 ta ekran bor</item>
</plurals>
- <string name="status_bar_no_notifications_title" msgid="4755261167193833213">"Eslatmalar - yo‘q"</string>
+ <string name="status_bar_no_notifications_title" msgid="4755261167193833213">"Bildirishnomalar yo‘q"</string>
<string name="status_bar_ongoing_events_title" msgid="1682504513316879202">"Joriy"</string>
<string name="status_bar_latest_events_title" msgid="6594767438577593172">"Eslatmalar"</string>
<string name="battery_low_title" msgid="6456385927409742437">"Batareya quvvati kam"</string>
@@ -232,10 +232,10 @@
<string name="accessibility_clear_all" msgid="5235938559247164925">"Barcha eslatmalarni tozalash."</string>
<string name="status_bar_notification_inspect_item_title" msgid="5668348142410115323">"Bildirishnoma sozlamalari"</string>
<string name="status_bar_notification_app_settings_title" msgid="5525260160341558869">"<xliff:g id="APP_NAME">%s</xliff:g> sozlamalari"</string>
- <string name="accessibility_rotation_lock_off" msgid="4062780228931590069">"Ekran avtomatik ravishda aylanadi."</string>
+ <string name="accessibility_rotation_lock_off" msgid="4062780228931590069">"Ekran avtomatik buriladi."</string>
<string name="accessibility_rotation_lock_on_landscape" msgid="6731197337665366273">"Ekran eniga holatida qulflandi."</string>
<string name="accessibility_rotation_lock_on_portrait" msgid="5809367521644012115">"Ekran bo‘yiga holatida qulflandi."</string>
- <string name="accessibility_rotation_lock_off_changed" msgid="8134601071026305153">"Ekran endi avtomatik ravishda aylanadi."</string>
+ <string name="accessibility_rotation_lock_off_changed" msgid="8134601071026305153">"Ekran endi avtomatik buriladi."</string>
<string name="accessibility_rotation_lock_on_landscape_changed" msgid="3135965553707519743">"Ekran yotiq holatda aylanmaydigan qilindi."</string>
<string name="accessibility_rotation_lock_on_portrait_changed" msgid="8922481981834012126">"Ekran tik holatda aylanmaydigan qilindi."</string>
<string name="dessert_case" msgid="1295161776223959221">"Dessert Case"</string>
@@ -250,7 +250,7 @@
<string name="quick_settings_bluetooth_off_label" msgid="8159652146149219937">"Bluetooth o‘chirilgan"</string>
<string name="quick_settings_bluetooth_detail_empty_text" msgid="4910015762433302860">"Ulangan qurilmalar topilmadi"</string>
<string name="quick_settings_brightness_label" msgid="6968372297018755815">"Yorqinlik"</string>
- <string name="quick_settings_rotation_unlocked_label" msgid="7305323031808150099">"Auto-aylantirish"</string>
+ <string name="quick_settings_rotation_unlocked_label" msgid="7305323031808150099">"Avtomatik burish"</string>
<string name="quick_settings_rotation_locked_label" msgid="6359205706154282377">"Aylanmaydigan qilingan"</string>
<string name="quick_settings_rotation_locked_portrait_label" msgid="5102691921442135053">"Bo‘yiga"</string>
<string name="quick_settings_rotation_locked_landscape_label" msgid="8553157770061178719">"Eniga"</string>
@@ -301,7 +301,8 @@
<string name="recents_search_bar_label" msgid="8074997400187836677">"qidirish"</string>
<string name="recents_launch_error_message" msgid="2969287838120550506">"“<xliff:g id="APP">%s</xliff:g>” ilovasini ishga tushirib bo‘lmadi."</string>
<string name="recents_show_history_button_label" msgid="7062088196449747245">"Ko‘proq"</string>
- <string name="recents_history_label" msgid="3076213823382198287">"Jurnal"</string>
+ <!-- no translation found for recents_history_label_format (6337155608055062429) -->
+ <skip />
<string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="8848514474543427332">"Gorizontal yo‘nalishda bo‘lish"</string>
<string name="recents_multistack_add_stack_dialog_split_vertical" msgid="9075292233696180813">"Vertikal yo‘nalishda bo‘lish"</string>
<string name="recents_multistack_add_stack_dialog_split_custom" msgid="4177837597513701943">"Boshqa usulda bo‘lish"</string>
diff --git a/packages/SystemUI/res/values-vi/strings.xml b/packages/SystemUI/res/values-vi/strings.xml
index 81b5129..4d08000 100644
--- a/packages/SystemUI/res/values-vi/strings.xml
+++ b/packages/SystemUI/res/values-vi/strings.xml
@@ -301,7 +301,8 @@
<string name="recents_search_bar_label" msgid="8074997400187836677">"tìm kiếm"</string>
<string name="recents_launch_error_message" msgid="2969287838120550506">"Không thể khởi động <xliff:g id="APP">%s</xliff:g>."</string>
<string name="recents_show_history_button_label" msgid="7062088196449747245">"Thêm"</string>
- <string name="recents_history_label" msgid="3076213823382198287">"Lịch sử"</string>
+ <!-- no translation found for recents_history_label_format (6337155608055062429) -->
+ <skip />
<string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="8848514474543427332">"Phân tách ngang"</string>
<string name="recents_multistack_add_stack_dialog_split_vertical" msgid="9075292233696180813">"Phân tách dọc"</string>
<string name="recents_multistack_add_stack_dialog_split_custom" msgid="4177837597513701943">"Tùy chỉnh phân tách"</string>
diff --git a/packages/SystemUI/res/values-zh-rCN/strings.xml b/packages/SystemUI/res/values-zh-rCN/strings.xml
index e06052e..e30320d 100644
--- a/packages/SystemUI/res/values-zh-rCN/strings.xml
+++ b/packages/SystemUI/res/values-zh-rCN/strings.xml
@@ -301,7 +301,8 @@
<string name="recents_search_bar_label" msgid="8074997400187836677">"搜索"</string>
<string name="recents_launch_error_message" msgid="2969287838120550506">"无法启动<xliff:g id="APP">%s</xliff:g>。"</string>
<string name="recents_show_history_button_label" msgid="7062088196449747245">"更多"</string>
- <string name="recents_history_label" msgid="3076213823382198287">"历史记录"</string>
+ <!-- no translation found for recents_history_label_format (6337155608055062429) -->
+ <skip />
<string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="8848514474543427332">"水平分割"</string>
<string name="recents_multistack_add_stack_dialog_split_vertical" msgid="9075292233696180813">"垂直分割"</string>
<string name="recents_multistack_add_stack_dialog_split_custom" msgid="4177837597513701943">"自定义分割"</string>
diff --git a/packages/SystemUI/res/values-zh-rHK/strings.xml b/packages/SystemUI/res/values-zh-rHK/strings.xml
index 37d3ca6..94548ba 100644
--- a/packages/SystemUI/res/values-zh-rHK/strings.xml
+++ b/packages/SystemUI/res/values-zh-rHK/strings.xml
@@ -301,7 +301,8 @@
<string name="recents_search_bar_label" msgid="8074997400187836677">"搜尋"</string>
<string name="recents_launch_error_message" msgid="2969287838120550506">"無法啟動「<xliff:g id="APP">%s</xliff:g>」。"</string>
<string name="recents_show_history_button_label" msgid="7062088196449747245">"更多"</string>
- <string name="recents_history_label" msgid="3076213823382198287">"記錄"</string>
+ <!-- no translation found for recents_history_label_format (6337155608055062429) -->
+ <skip />
<string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="8848514474543427332">"水平分割"</string>
<string name="recents_multistack_add_stack_dialog_split_vertical" msgid="9075292233696180813">"垂直分割"</string>
<string name="recents_multistack_add_stack_dialog_split_custom" msgid="4177837597513701943">"自訂分割"</string>
diff --git a/packages/SystemUI/res/values-zh-rTW/strings.xml b/packages/SystemUI/res/values-zh-rTW/strings.xml
index 531b3cd..c4035e1 100644
--- a/packages/SystemUI/res/values-zh-rTW/strings.xml
+++ b/packages/SystemUI/res/values-zh-rTW/strings.xml
@@ -301,7 +301,8 @@
<string name="recents_search_bar_label" msgid="8074997400187836677">"搜尋"</string>
<string name="recents_launch_error_message" msgid="2969287838120550506">"無法啟動「<xliff:g id="APP">%s</xliff:g>」。"</string>
<string name="recents_show_history_button_label" msgid="7062088196449747245">"更多"</string>
- <string name="recents_history_label" msgid="3076213823382198287">"紀錄"</string>
+ <!-- no translation found for recents_history_label_format (6337155608055062429) -->
+ <skip />
<string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="8848514474543427332">"水平分割"</string>
<string name="recents_multistack_add_stack_dialog_split_vertical" msgid="9075292233696180813">"垂直分割"</string>
<string name="recents_multistack_add_stack_dialog_split_custom" msgid="4177837597513701943">"自訂分割"</string>
diff --git a/packages/SystemUI/res/values-zu/strings.xml b/packages/SystemUI/res/values-zu/strings.xml
index b8e6a0f..87b6aaf 100644
--- a/packages/SystemUI/res/values-zu/strings.xml
+++ b/packages/SystemUI/res/values-zu/strings.xml
@@ -301,7 +301,8 @@
<string name="recents_search_bar_label" msgid="8074997400187836677">"sesha"</string>
<string name="recents_launch_error_message" msgid="2969287838120550506">"Ayikwazanga ukuqala i-<xliff:g id="APP">%s</xliff:g>."</string>
<string name="recents_show_history_button_label" msgid="7062088196449747245">"Okuningi"</string>
- <string name="recents_history_label" msgid="3076213823382198287">"Umlando"</string>
+ <!-- no translation found for recents_history_label_format (6337155608055062429) -->
+ <skip />
<string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="8848514474543427332">"Hlukanisa okuvundlile"</string>
<string name="recents_multistack_add_stack_dialog_split_vertical" msgid="9075292233696180813">"Hlukanisa okumile"</string>
<string name="recents_multistack_add_stack_dialog_split_custom" msgid="4177837597513701943">"Hlukanisa kwezifiso"</string>
diff --git a/packages/SystemUI/res/values/colors.xml b/packages/SystemUI/res/values/colors.xml
index 94350d9..76c08f6 100644
--- a/packages/SystemUI/res/values/colors.xml
+++ b/packages/SystemUI/res/values/colors.xml
@@ -59,9 +59,9 @@
<!-- The recents task bar dark text color to be drawn on top of light backgrounds. -->
<color name="recents_task_bar_dark_text_color">#cc000000</color>
<!-- The recents task bar light dismiss icon color to be drawn on top of dark backgrounds. -->
- <color name="recents_task_bar_light_dismiss_color">#ffeeeeee</color>
+ <color name="recents_task_bar_light_icon_color">#ffeeeeee</color>
<!-- The recents task bar dark dismiss icon color to be drawn on top of light backgrounds. -->
- <color name="recents_task_bar_dark_dismiss_color">#99000000</color>
+ <color name="recents_task_bar_dark_icon_color">#99000000</color>
<!-- The recents task bar highlight color. -->
<color name="recents_task_bar_highlight_color">#28ffffff</color>
<!-- The lock to task button background color. -->
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index 666a024..f71a71a 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -713,8 +713,8 @@
<string name="recents_launch_error_message">Could not start <xliff:g id="app" example="Calendar">%s</xliff:g>.</string>
<!-- Recents: Show history string. [CHAR LIMIT=NONE] -->
<string name="recents_show_history_button_label">More</string>
- <!-- Recents: The history of recents. [CHAR LIMIT=NONE] -->
- <string name="recents_history_label">History</string>
+ <!-- Recents: A format string to set the number of availabe historical tasks in recents. [CHAR LIMIT=NONE] -->
+ <string name="recents_history_label_format"><xliff:g id="number">%d</xliff:g> More</string>
<!-- Recents: MultiStack add stack split horizontal radio button. [CHAR LIMIT=NONE] -->
<string name="recents_multistack_add_stack_dialog_split_horizontal">Split Horizontal</string>
@@ -1171,31 +1171,11 @@
<!-- Option to use new paging layout in quick settings [CHAR LIMIT=60] -->
<string name="qs_paging" translatable="false">Use the new Quick Settings</string>
- <!-- Toggles paging recents via the recents button. DO NOT TRANSLATE -->
- <string name="overview_page_on_toggle">Enable paging</string>
- <!-- Description for the toggle for fast-toggling recents via the recents button. DO NOT TRANSLATE -->
- <string name="overview_page_on_toggle_desc">Enable paging via the Overview button</string>
-
<!-- Toggles fast-toggling recents via the recents button. DO NOT TRANSLATE -->
<string name="overview_fast_toggle_via_button">Enable fast toggle</string>
<!-- Description for the toggle for fast-toggling recents via the recents button. DO NOT TRANSLATE -->
<string name="overview_fast_toggle_via_button_desc">Enable launch timeout while paging</string>
- <!-- Toggles fullscreen screenshots. DO NOT TRANSLATE -->
- <string name="overview_fullscreen_thumbnails">Enable fullscreen screenshots</string>
- <!-- Description for the toggle for fullscreen screenshots. DO NOT TRANSLATE -->
- <string name="overview_fullscreen_thumbnails_desc">Enable fullscreen screenshots in Overview. Restart required.</string>
-
- <!-- Toggle to enable the Overview nav bar gesture. DO NOT TRANSLATE -->
- <string name="overview_nav_bar_gesture">Enable navigation bar gesture</string>
- <!-- Description for the toggle to enable the Overview nav bar gesture. DO NOT TRANSLATE -->
- <string name="overview_nav_bar_gesture_desc">Enables the gesture to enter Overview by swiping up on the Nav bar</string>
-
- <!-- Toggle to show the history view in Overview. DO NOT TRANSLATE -->
- <string name="overview_show_history">Show History</string>
- <!-- Description for the toggle to show the history view in Overview. DO NOT TRANSLATE -->
- <string name="overview_show_history_desc">Enables the history view to see more recent tasks</string>
-
<!-- Toggle to set the initial scroll state to be paging or stack. DO NOT TRANSLATE -->
<string name="overview_initial_state_paging">Initialize to paging</string>
<!-- Description for the toggle to set the initial scroll state to be paging or stack. DO NOT TRANSLATE -->
diff --git a/packages/SystemUI/res/xml/tuner_prefs.xml b/packages/SystemUI/res/xml/tuner_prefs.xml
index e31927e..43359b3 100644
--- a/packages/SystemUI/res/xml/tuner_prefs.xml
+++ b/packages/SystemUI/res/xml/tuner_prefs.xml
@@ -73,11 +73,6 @@
android:title="@string/overview" >
<com.android.systemui.tuner.TunerSwitch
- android:key="overview_page_on_toggle"
- android:title="@string/overview_page_on_toggle"
- android:summary="@string/overview_page_on_toggle_desc" />
-
- <com.android.systemui.tuner.TunerSwitch
android:key="overview_initial_state_paging"
android:title="@string/overview_initial_state_paging"
android:summary="@string/overview_initial_state_paging_desc" />
@@ -87,21 +82,6 @@
android:title="@string/overview_fast_toggle_via_button"
android:summary="@string/overview_fast_toggle_via_button_desc" />
- <com.android.systemui.tuner.TunerSwitch
- android:key="overview_nav_bar_gesture"
- android:title="@string/overview_nav_bar_gesture"
- android:summary="@string/overview_nav_bar_gesture_desc" />
-
- <com.android.systemui.tuner.TunerSwitch
- android:key="overview_fullscreen_thumbnails"
- android:title="@string/overview_fullscreen_thumbnails"
- android:summary="@string/overview_fullscreen_thumbnails_desc" />
-
- <com.android.systemui.tuner.TunerSwitch
- android:key="overview_show_history"
- android:title="@string/overview_show_history"
- android:summary="@string/overview_show_history_desc" />
-
</PreferenceScreen>
<SwitchPreference
diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java
index 384b86f..9f0ac35 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java
@@ -56,7 +56,6 @@
import com.android.systemui.recents.events.component.ScreenPinningRequestEvent;
import com.android.systemui.recents.events.ui.AllTaskViewsDismissedEvent;
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.StackViewScrolledEvent;
import com.android.systemui.recents.events.ui.UpdateFreeformTaskViewVisibilityEvent;
@@ -87,6 +86,9 @@
private final static String TAG = "RecentsActivity";
private final static boolean DEBUG = false;
+ private final static String KEY_SAVED_STATE_HISTORY_VISIBLE =
+ "saved_instance_state_history_visible";
+
public final static int EVENT_BUS_PRIORITY = Recents.EVENT_BUS_PRIORITY + 1;
private RecentsPackageMonitor mPackageMonitor;
@@ -97,14 +99,9 @@
// Top level views
private RecentsView mRecentsView;
private SystemBarScrimViews mScrimViews;
- private ViewStub mEmptyViewStub;
- private View mEmptyView;
private ViewStub mHistoryViewStub;
private RecentsHistoryView mHistoryView;
- // Resize task debug
- private RecentsResizeTaskDialog mResizeTaskDebugDialog;
-
// Search AppWidget
private AppWidgetProviderInfo mSearchWidgetInfo;
private RecentsAppWidgetHost mAppWidgetHost;
@@ -197,7 +194,6 @@
loader.loadTasks(this, plan, loadOpts);
TaskStack stack = plan.getTaskStack();
- launchState.launchedWithNoRecentTasks = !plan.hasTasks();
mRecentsView.setTaskStack(stack);
// Mark the task that is the launch target
@@ -215,30 +211,13 @@
}
}
- // Update the top level view's visibilities
- if (launchState.launchedWithNoRecentTasks) {
- if (mEmptyView == null) {
- mEmptyView = mEmptyViewStub.inflate();
- }
- mEmptyView.setVisibility(View.VISIBLE);
- if (!RecentsDebugFlags.Static.DisableSearchBar) {
- mRecentsView.setSearchBarVisibility(View.GONE);
- }
- } else {
- if (mEmptyView != null) {
- mEmptyView.setVisibility(View.GONE);
- }
- if (!RecentsDebugFlags.Static.DisableSearchBar) {
- if (mRecentsView.hasValidSearchBar()) {
- mRecentsView.setSearchBarVisibility(View.VISIBLE);
- } else {
- refreshSearchWidgetView();
- }
- }
- }
-
// Animate the SystemUI scrims into view
- mScrimViews.prepareEnterRecentsAnimation();
+ boolean hasStatusBarScrim = stack.getStackTaskCount() > 0;
+ boolean animateStatusBarScrim = launchState.launchedFromHome;
+ boolean hasNavBarScrim = (stack.getStackTaskCount() > 0) && !config.hasTransposedNavBar;
+ boolean animateNavBarScrim = true;
+ mScrimViews.prepareEnterRecentsAnimation(hasStatusBarScrim, animateStatusBarScrim, hasNavBarScrim,
+ animateNavBarScrim);
// Keep track of whether we launched from the nav bar button or via alt-tab
if (launchState.launchedWithAltTab) {
@@ -265,7 +244,10 @@
boolean dismissHistory() {
// Try and hide the history view first
if (mHistoryView != null && mHistoryView.isVisible()) {
- EventBus.getDefault().send(new HideHistoryEvent(true /* animate */));
+ ReferenceCountedTrigger t = new ReferenceCountedTrigger(this);
+ t.increment();
+ EventBus.getDefault().send(new HideHistoryEvent(true /* animate */, t));
+ t.decrement();
return true;
}
return false;
@@ -374,7 +356,6 @@
mRecentsView.setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_STABLE |
View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN |
View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION);
- mEmptyViewStub = (ViewStub) findViewById(R.id.empty_view_stub);
mHistoryViewStub = (ViewStub) findViewById(R.id.history_view_stub);
mScrimViews = new SystemBarScrimViews(this);
getWindow().getAttributes().privateFlags |=
@@ -456,7 +437,10 @@
// Reset some states
mIgnoreAltTabRelease = false;
if (mHistoryView != null) {
- EventBus.getDefault().send(new HideHistoryEvent(false /* animate */));
+ ReferenceCountedTrigger t = new ReferenceCountedTrigger(this);
+ t.increment();
+ EventBus.getDefault().send(new HideHistoryEvent(false /* animate */, t));
+ t.decrement();
}
// Notify that recents is now hidden
@@ -515,6 +499,25 @@
}
@Override
+ protected void onSaveInstanceState(Bundle outState) {
+ super.onSaveInstanceState(outState);
+ outState.putBoolean(KEY_SAVED_STATE_HISTORY_VISIBLE,
+ (mHistoryView != null) && mHistoryView.isVisible());
+ }
+
+ @Override
+ protected void onRestoreInstanceState(Bundle savedInstanceState) {
+ super.onRestoreInstanceState(savedInstanceState);
+ if (savedInstanceState.getBoolean(KEY_SAVED_STATE_HISTORY_VISIBLE, false)) {
+ ReferenceCountedTrigger postHideStackAnimationTrigger =
+ new ReferenceCountedTrigger(this);
+ postHideStackAnimationTrigger.increment();
+ EventBus.getDefault().send(new ShowHistoryEvent(postHideStackAnimationTrigger));
+ postHideStackAnimationTrigger.decrement();
+ }
+ }
+
+ @Override
public void onTrimMemory(int level) {
RecentsTaskLoader loader = Recents.getTaskLoader();
if (loader != null) {
@@ -580,15 +583,6 @@
EventBus.getDefault().send(new ToggleRecentsEvent());
}
- /**** RecentsResizeTaskDialog ****/
-
- private RecentsResizeTaskDialog getResizeTaskDebugDialog() {
- if (mResizeTaskDebugDialog == null) {
- mResizeTaskDebugDialog = new RecentsResizeTaskDialog(getFragmentManager(), this);
- }
- return mResizeTaskDebugDialog;
- }
-
/**** EventBus events ****/
public final void onBusEvent(ToggleRecentsEvent event) {
@@ -603,16 +597,18 @@
}
public final void onBusEvent(IterateRecentsEvent event) {
- // Focus the next task
- EventBus.getDefault().send(new FocusNextTaskViewEvent());
+ if (!dismissHistory()) {
+ // Focus the next task
+ EventBus.getDefault().send(new FocusNextTaskViewEvent());
- // Start dozing after the recents button is clicked
- RecentsDebugFlags debugFlags = Recents.getDebugFlags();
- if (debugFlags.isFastToggleRecentsEnabled()) {
- if (!mIterateTrigger.isDozing()) {
- mIterateTrigger.startDozing();
- } else {
- mIterateTrigger.poke();
+ // Start dozing after the recents button is clicked
+ RecentsDebugFlags debugFlags = Recents.getDebugFlags();
+ if (debugFlags.isFastToggleRecentsEnabled()) {
+ if (!mIterateTrigger.isDozing()) {
+ mIterateTrigger.startDozing();
+ } else {
+ mIterateTrigger.poke();
+ }
}
}
}
@@ -630,7 +626,7 @@
} else if (event.triggeredFromHomeKey) {
// Otherwise, dismiss Recents to Home
if (mHistoryView != null && mHistoryView.isVisible()) {
- ReferenceCountedTrigger t = new ReferenceCountedTrigger(this, null, null, null);
+ ReferenceCountedTrigger t = new ReferenceCountedTrigger(this);
t.increment();
t.addLastDecrementRunnable(new Runnable() {
@Override
@@ -651,7 +647,7 @@
public final void onBusEvent(EnterRecentsWindowAnimationCompletedEvent event) {
// Try and start the enter animation (or restart it on configuration changed)
- ReferenceCountedTrigger t = new ReferenceCountedTrigger(this, null, null, null);
+ ReferenceCountedTrigger t = new ReferenceCountedTrigger(this);
ViewAnimation.TaskViewEnterContext ctx = new ViewAnimation.TaskViewEnterContext(t);
ctx.postAnimationTrigger.increment();
if (mSearchWidgetInfo != null) {
@@ -724,17 +720,18 @@
}
public final void onBusEvent(AllTaskViewsDismissedEvent event) {
- // Just go straight home (no animation necessary because there are no more task views)
- dismissRecentsToHome(false /* animated */);
+ SystemServicesProxy ssp = Recents.getSystemServices();
+ if (ssp.hasDockedTask()) {
+ mRecentsView.showEmptyView();
+ } else {
+ // Just go straight home (no animation necessary because there are no more task views)
+ dismissRecentsToHome(false /* animated */);
+ }
// Keep track of all-deletions
MetricsLogger.count(this, "overview_task_all_dismissed", 1);
}
- public final void onBusEvent(ResizeTaskEvent event) {
- getResizeTaskDebugDialog().showResizeTaskDialog(event.task, mRecentsView);
- }
-
public final void onBusEvent(LaunchTaskSucceededEvent event) {
MetricsLogger.histogram(this, "overview_task_launch_index", event.taskIndexFromStackFront);
}
@@ -769,13 +766,11 @@
// provided.
mHistoryView.setSystemInsets(mRecentsView.getSystemInsets());
}
- mHistoryView.show(mRecentsView.getTaskStack());
+ mHistoryView.show(mRecentsView.getTaskStack(), event.postHideStackAnimationTrigger);
}
public final void onBusEvent(HideHistoryEvent event) {
- if (mHistoryView != null) {
- mHistoryView.hide(event.animate, event.postAnimationTrigger);
- }
+ mHistoryView.hide(event.animate, event.postHideHistoryAnimationTrigger);
}
private void refreshSearchWidgetView() {
diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsActivityLaunchState.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsActivityLaunchState.java
index e0bd59b..4ab0740 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/RecentsActivityLaunchState.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsActivityLaunchState.java
@@ -28,7 +28,6 @@
public class RecentsActivityLaunchState {
public boolean launchedWithAltTab;
- public boolean launchedWithNoRecentTasks;
public boolean launchedFromAppWithThumbnail;
public boolean launchedFromHome;
public boolean launchedFromSearchHome;
@@ -49,67 +48,31 @@
launchedViaDragGesture = false;
}
- /** Returns whether the status bar scrim should be animated when shown for the first time. */
- public boolean shouldAnimateStatusBarScrim() {
- return true;
- }
-
- /** Returns whether the status bar scrim should be visible. */
- public boolean hasStatusBarScrim() {
- return !launchedWithNoRecentTasks;
- }
-
- /** Returns whether the nav bar scrim should be animated when shown for the first time. */
- public boolean shouldAnimateNavBarScrim() {
- return true;
- }
-
- /** 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
- RecentsConfiguration config = Recents.getConfiguration();
- return !launchedWithNoRecentTasks && !config.hasTransposedNavBar;
- }
-
/**
* Returns the task to focus given the current launch state.
*/
public int getInitialFocusTaskIndex(int numTasks) {
RecentsDebugFlags flags = Recents.getDebugFlags();
- if (flags.isPageOnToggleEnabled() && !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 (launchedWithAltTab) {
+ if (launchedFromAppWithThumbnail) {
+ // If alt-tabbing from another app, focus the next task
+ return numTasks - 2;
+ } else {
+ // If alt-tabbing from home, focus the first task
+ return numTasks - 1;
+ }
+ } else {
if (launchedFromHome) {
return numTasks - 1;
} else {
if (flags.isFastToggleRecentsEnabled() || !flags.isInitialStatePaging()) {
+ // If we are not fast-toggling or are starting in the non-focused mode, then
+ // we should assume the front most task has focus
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/RecentsConfiguration.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsConfiguration.java
index 53c10b7..7547570f 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/RecentsConfiguration.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsConfiguration.java
@@ -143,16 +143,13 @@
int rightInset, Rect searchBarBounds, Rect taskStackBounds) {
if (hasTransposedNavBar) {
// In landscape phones, the search bar appears on the left, but we overlay it on top
- int swInset = getInsetToSmallestWidth(windowBounds.right - rightInset -
- windowBounds.left);
- taskStackBounds.set(windowBounds.left + swInset, windowBounds.top + topInset,
- windowBounds.right - swInset - rightInset, windowBounds.bottom);
+ taskStackBounds.set(windowBounds.left, windowBounds.top + topInset,
+ windowBounds.right - rightInset, windowBounds.bottom);
} else {
// In portrait, the search bar appears on the top (which already has the inset)
- int swInset = getInsetToSmallestWidth(windowBounds.right - windowBounds.left);
int top = searchBarBounds.isEmpty() ? topInset : 0;
- taskStackBounds.set(windowBounds.left + swInset, searchBarBounds.bottom + top,
- windowBounds.right - swInset - rightInset, windowBounds.bottom);
+ taskStackBounds.set(windowBounds.left, searchBarBounds.bottom + top,
+ windowBounds.right - rightInset, windowBounds.bottom);
}
}
@@ -173,15 +170,4 @@
windowBounds.right, windowBounds.top + topInset + searchBarSize);
}
}
-
- /**
- * Constrain the width of the landscape stack to the smallest width of the device.
- */
- private int getInsetToSmallestWidth(int availableWidth) {
- RecentsDebugFlags debugFlags = Recents.getDebugFlags();
- if (!debugFlags.isFullscreenThumbnailsEnabled() && (availableWidth > smallestWidth)) {
- return (availableWidth - smallestWidth) / 2;
- }
- return 0;
- }
}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsDebugFlags.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsDebugFlags.java
index d778886..40c84ba 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/RecentsDebugFlags.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsDebugFlags.java
@@ -26,10 +26,7 @@
*/
public class RecentsDebugFlags implements TunerService.Tunable {
- private static final String KEY_FAST_TOGGLE = "overview_fast_toggle";
- private static final String KEY_PAGE_ON_TOGGLE = "overview_page_on_toggle";
- private static final String KEY_FULLSCREEN_THUMBNAILS = "overview_fullscreen_thumbnails";
- private static final String KEY_SHOW_HISTORY = "overview_show_history";
+ private static final String KEY_FAST_TOGGLE = "overview_fast_toggle_via_button";
private static final String KEY_INITIAL_STATE_PAGING = "overview_initial_state_paging";
public static class Static {
@@ -52,9 +49,6 @@
}
private boolean mFastToggleRecents;
- private boolean mPageOnToggle;
- private boolean mUseFullscreenThumbnails;
- private boolean mShowHistory;
private boolean mInitialStatePaging;
/**
@@ -64,36 +58,14 @@
public RecentsDebugFlags(Context context) {
// Register all our flags, this will also call onTuningChanged() for each key, which will
// initialize the current state of each flag
- TunerService.get(context).addTunable(this, KEY_FAST_TOGGLE, KEY_PAGE_ON_TOGGLE,
- KEY_FULLSCREEN_THUMBNAILS, KEY_SHOW_HISTORY, KEY_INITIAL_STATE_PAGING);
+ TunerService.get(context).addTunable(this, KEY_FAST_TOGGLE, KEY_INITIAL_STATE_PAGING);
}
/**
* @return whether we are enabling fast toggling.
*/
public boolean isFastToggleRecentsEnabled() {
- return mPageOnToggle && mFastToggleRecents;
- }
-
- /**
- * @return whether the recents button toggles pages.
- */
- public boolean isPageOnToggleEnabled() {
- return mPageOnToggle;
- }
-
- /**
- * @return whether we should show fullscreen thumbnails
- */
- public boolean isFullscreenThumbnailsEnabled() {
- return mUseFullscreenThumbnails;
- }
-
- /**
- * @return whether we should show the history
- */
- public boolean isHistoryEnabled() {
- return mShowHistory;
+ return mFastToggleRecents;
}
/**
@@ -110,18 +82,6 @@
mFastToggleRecents = (newValue != null) &&
(Integer.parseInt(newValue) != 0);
break;
- case KEY_PAGE_ON_TOGGLE:
- mPageOnToggle = (newValue != null) &&
- (Integer.parseInt(newValue) != 0);
- break;
- case KEY_FULLSCREEN_THUMBNAILS:
- mUseFullscreenThumbnails = (newValue != null) &&
- (Integer.parseInt(newValue) != 0);
- break;
- case KEY_SHOW_HISTORY:
- mShowHistory = (newValue != null) &&
- (Integer.parseInt(newValue) != 0);
- break;
case KEY_INITIAL_STATE_PAGING:
mInitialStatePaging = (newValue != null) &&
(Integer.parseInt(newValue) != 0);
diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java
index 2a4017a..0678aae 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java
@@ -333,7 +333,7 @@
RecentsConfiguration config = Recents.getConfiguration();
RecentsActivityLaunchState launchState = config.getLaunchState();
RecentsDebugFlags flags = Recents.getDebugFlags();
- if (flags.isPageOnToggleEnabled() && !launchState.launchedWithAltTab) {
+ if (!launchState.launchedWithAltTab) {
// Notify recents to move onto the next task
EventBus.getDefault().post(new IterateRecentsEvent());
} else {
diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsResizeTaskDialog.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsResizeTaskDialog.java
deleted file mode 100644
index d415845..0000000
--- a/packages/SystemUI/src/com/android/systemui/recents/RecentsResizeTaskDialog.java
+++ /dev/null
@@ -1,286 +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;
-
-import android.app.ActivityManager;
-import android.app.AlertDialog;
-import android.app.Dialog;
-import android.app.DialogFragment;
-import android.app.FragmentManager;
-import android.content.DialogInterface;
-import android.graphics.Rect;
-import android.os.Bundle;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.widget.Button;
-import android.widget.Toast;
-import com.android.systemui.R;
-import com.android.systemui.recents.misc.SystemServicesProxy;
-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.
- */
-public class RecentsResizeTaskDialog extends DialogFragment {
-
- static final String TAG = "RecentsResizeTaskDialog";
-
- // The various window arrangements we can handle.
- private static final int PLACE_LEFT = 1;
- private static final int PLACE_RIGHT = 2;
- private static final int PLACE_TOP = 3;
- private static final int PLACE_BOTTOM = 4;
- private static final int PLACE_TOP_LEFT = 5;
- private static final int PLACE_TOP_RIGHT = 6;
- private static final int PLACE_BOTTOM_LEFT = 7;
- private static final int PLACE_BOTTOM_RIGHT = 8;
- private static final int PLACE_FULL = 9;
- private static final int PLACE_DOCK_LEFT = 10;
- private static final int PLACE_DOCK_RIGHT = 11;
- private static final int PLACE_DOCK_TOP = 12;
- private static final int PLACE_DOCK_BOTTOM = 13;
-
- // The button resource ID combined with the arrangement command.
- private static final int[][] BUTTON_DEFINITIONS =
- {{R.id.place_dock_left, PLACE_DOCK_LEFT},
- {R.id.place_dock_right, PLACE_DOCK_RIGHT},
- {R.id.place_dock_top, PLACE_DOCK_TOP},
- {R.id.place_dock_bottom, PLACE_DOCK_BOTTOM},
- {R.id.place_left, PLACE_LEFT},
- {R.id.place_right, PLACE_RIGHT},
- {R.id.place_top, PLACE_TOP},
- {R.id.place_bottom, PLACE_BOTTOM},
- {R.id.place_top_left, PLACE_TOP_LEFT},
- {R.id.place_top_right, PLACE_TOP_RIGHT},
- {R.id.place_bottom_left, PLACE_BOTTOM_LEFT},
- {R.id.place_bottom_right, PLACE_BOTTOM_RIGHT},
- {R.id.place_full, PLACE_FULL}};
-
- // The task we want to resize.
- private FragmentManager mFragmentManager;
- private View mResizeTaskDialogContent;
- private RecentsActivity mRecentsActivity;
- private RecentsView mRecentsView;
- private Rect[] mBounds = {new Rect(), new Rect(), new Rect(), new Rect()};
- private Task[] mTasks = {null, null, null, null};
-
- /**
- * Called by FragmentManager
- */
- public RecentsResizeTaskDialog() {
- }
-
- public RecentsResizeTaskDialog(FragmentManager mgr, RecentsActivity activity) {
- mFragmentManager = mgr;
- mRecentsActivity = activity;
- }
-
- /** Shows the resize-task dialog. */
- void showResizeTaskDialog(Task mainTask, RecentsView rv) {
- mTasks[0] = mainTask;
- mRecentsView = rv;
- showAllowingStateLoss(mFragmentManager, TAG);
- }
-
- /** Creates a new resize-task dialog. */
- private void createResizeTaskDialog(LayoutInflater inflater, AlertDialog.Builder builder) {
- builder.setTitle(R.string.recents_caption_resize);
- mResizeTaskDialogContent =
- inflater.inflate(R.layout.recents_task_resize_dialog, null, false);
-
- for (int i = 0; i < BUTTON_DEFINITIONS.length; i++) {
- Button b = (Button)mResizeTaskDialogContent.findViewById(BUTTON_DEFINITIONS[i][0]);
- if (b != null) {
- final int action = BUTTON_DEFINITIONS[i][1];
- b.setOnClickListener(
- new View.OnClickListener() {
- public void onClick(View v) {
- switch (action) {
- case PLACE_DOCK_LEFT:
- case PLACE_DOCK_RIGHT:
- case PLACE_DOCK_TOP:
- case PLACE_DOCK_BOTTOM:
- placeDockTasks(action);
- break;
- default:
- placeTasks(action);
- break;
- }
- }
- });
- }
- }
-
- builder.setNegativeButton(R.string.cancel, new DialogInterface.OnClickListener() {
- @Override
- public void onClick(DialogInterface dialog, int which) {
- dismissAllowingStateLoss();
- }
- });
-
- builder.setView(mResizeTaskDialogContent);
- }
-
- /** Helper function to place window(s) on the display according to an arrangement request. */
- private void placeTasks(int arrangement) {
- SystemServicesProxy ssp = Recents.getSystemServices();
- Rect rect = ssp.getDisplayRect();
- for (int i = 0; i < mBounds.length; ++i) {
- mBounds[i].set(rect);
- if (i != 0) {
- mTasks[i] = null;
- }
- }
- int additionalTasks = 0;
- switch (arrangement) {
- case PLACE_LEFT:
- mBounds[0].right = mBounds[0].centerX();
- mBounds[1].left = mBounds[0].right;
- additionalTasks = 1;
- break;
- case PLACE_RIGHT:
- mBounds[1].right = mBounds[1].centerX();
- mBounds[0].left = mBounds[1].right;
- additionalTasks = 1;
- break;
- case PLACE_TOP:
- mBounds[0].bottom = mBounds[0].centerY();
- mBounds[1].top = mBounds[0].bottom;
- additionalTasks = 1;
- break;
- case PLACE_BOTTOM:
- mBounds[1].bottom = mBounds[1].centerY();
- mBounds[0].top = mBounds[1].bottom;
- additionalTasks = 1;
- break;
- case PLACE_TOP_LEFT: // TL, TR, BL, BR
- mBounds[0].right = mBounds[0].centerX();
- mBounds[0].bottom = mBounds[0].centerY();
- mBounds[1].left = mBounds[0].right;
- mBounds[1].bottom = mBounds[0].bottom;
- mBounds[2].right = mBounds[0].right;
- mBounds[2].top = mBounds[0].bottom;
- mBounds[3].left = mBounds[0].right;
- mBounds[3].top = mBounds[0].bottom;
- additionalTasks = 3;
- break;
- case PLACE_TOP_RIGHT: // TR, TL, BR, BL
- mBounds[0].left = mBounds[0].centerX();
- mBounds[0].bottom = mBounds[0].centerY();
- mBounds[1].right = mBounds[0].left;
- mBounds[1].bottom = mBounds[0].bottom;
- mBounds[2].left = mBounds[0].left;
- mBounds[2].top = mBounds[0].bottom;
- mBounds[3].right = mBounds[0].left;
- mBounds[3].top = mBounds[0].bottom;
- additionalTasks = 3;
- break;
- case PLACE_BOTTOM_LEFT: // BL, BR, TL, TR
- mBounds[0].right = mBounds[0].centerX();
- mBounds[0].top = mBounds[0].centerY();
- mBounds[1].left = mBounds[0].right;
- mBounds[1].top = mBounds[0].top;
- mBounds[2].right = mBounds[0].right;
- mBounds[2].bottom = mBounds[0].top;
- mBounds[3].left = mBounds[0].right;
- mBounds[3].bottom = mBounds[0].top;
- additionalTasks = 3;
- break;
- case PLACE_BOTTOM_RIGHT: // BR, BL, TR, TL
- mBounds[0].left = mBounds[0].centerX();
- mBounds[0].top = mBounds[0].centerY();
- mBounds[1].right = mBounds[0].left;
- mBounds[1].top = mBounds[0].top;
- mBounds[2].left = mBounds[0].left;
- mBounds[2].bottom = mBounds[0].top;
- mBounds[3].right = mBounds[0].left;
- mBounds[3].bottom = mBounds[0].top;
- additionalTasks = 3;
- break;
- case PLACE_FULL:
- // Nothing to change.
- mBounds[0] = new Rect();
- break;
- }
-
- // Get the other tasks.
- for (int i = 1; i <= additionalTasks && mTasks[i - 1] != null; ++i) {
- mTasks[i] = mRecentsView.getNextTaskOrTopTask(mTasks[i - 1]);
- // Do stop if we circled back to the first item.
- if (mTasks[i] == mTasks[0]) {
- mTasks[i] = null;
- }
- }
-
- // Get rid of the dialog.
- dismissAllowingStateLoss();
- mRecentsActivity.dismissRecentsToHomeWithoutTransitionAnimation();
-
- // 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], FREEFORM_WORKSPACE_STACK_ID);
- }
- }
- }
-
- /**
- * Helper function to place docked window(s) on the display according to an arrangement request.
- */
- private void placeDockTasks(int arrangement) {
- int createMode = ActivityManager.DOCKED_STACK_CREATE_MODE_TOP_OR_LEFT;
- switch (arrangement) {
- case PLACE_DOCK_LEFT:
- createMode = ActivityManager.DOCKED_STACK_CREATE_MODE_TOP_OR_LEFT;
- break;
- case PLACE_DOCK_TOP:
- createMode = ActivityManager.DOCKED_STACK_CREATE_MODE_TOP_OR_LEFT;
- break;
- case PLACE_DOCK_RIGHT:
- createMode = ActivityManager.DOCKED_STACK_CREATE_MODE_BOTTOM_OR_RIGHT;
- break;
- case PLACE_DOCK_BOTTOM:
- createMode = ActivityManager.DOCKED_STACK_CREATE_MODE_BOTTOM_OR_RIGHT;
- break;
- }
-
- // Dismiss the dialog before trying to launch the task
- dismissAllowingStateLoss();
-
- if (mTasks[0].key.stackId != DOCKED_STACK_ID) {
- int taskId = mTasks[0].key.id;
- 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);
- }
- }
-
- @Override
- public Dialog onCreateDialog(Bundle args) {
- LayoutInflater inflater = getActivity().getLayoutInflater();
- AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
- createResizeTaskDialog(inflater, builder);
- return builder.create();
- }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/events/activity/HideHistoryEvent.java b/packages/SystemUI/src/com/android/systemui/recents/events/activity/HideHistoryEvent.java
index 34c35a7..3412852 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/events/activity/HideHistoryEvent.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/events/activity/HideHistoryEvent.java
@@ -25,14 +25,14 @@
public class HideHistoryEvent extends EventBus.Event {
public final boolean animate;
- public final ReferenceCountedTrigger postAnimationTrigger;
+ public final ReferenceCountedTrigger postHideHistoryAnimationTrigger;
- public HideHistoryEvent(boolean animate) {
- this(animate, null);
- }
-
- public HideHistoryEvent(boolean animate, ReferenceCountedTrigger postAnimationTrigger) {
+ /**
+ * @param postHideHistoryAnimationTrigger the trigger that gets called when all the history animations are finished
+ * when transitioning from the history view
+ */
+ public HideHistoryEvent(boolean animate, ReferenceCountedTrigger postHideHistoryAnimationTrigger) {
this.animate = animate;
- this.postAnimationTrigger = postAnimationTrigger;
+ this.postHideHistoryAnimationTrigger = postHideHistoryAnimationTrigger;
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/events/activity/LaunchTaskEvent.java b/packages/SystemUI/src/com/android/systemui/recents/events/activity/LaunchTaskEvent.java
new file mode 100644
index 0000000..457d81e
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/recents/events/activity/LaunchTaskEvent.java
@@ -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.
+ */
+
+package com.android.systemui.recents.events.activity;
+
+import android.graphics.Rect;
+import com.android.systemui.recents.events.EventBus;
+import com.android.systemui.recents.model.Task;
+import com.android.systemui.recents.views.TaskView;
+
+/**
+ * This is sent to launch a task from Recents.
+ */
+public class LaunchTaskEvent extends EventBus.Event {
+
+ public final TaskView taskView;
+ public final Task task;
+ public final Rect targetTaskBounds;
+ public final int targetTaskStack;
+ public final boolean screenPinningRequested;
+
+ public LaunchTaskEvent(TaskView taskView, Task task, Rect targetTaskBounds, int targetTaskStack,
+ boolean screenPinningRequested) {
+ this.taskView = taskView;
+ this.task = task;
+ this.targetTaskBounds = targetTaskBounds;
+ this.targetTaskStack = targetTaskStack;
+ this.screenPinningRequested = screenPinningRequested;
+ }
+
+}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/events/activity/ShowHistoryEvent.java b/packages/SystemUI/src/com/android/systemui/recents/events/activity/ShowHistoryEvent.java
index 870119d..c91752e 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/events/activity/ShowHistoryEvent.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/events/activity/ShowHistoryEvent.java
@@ -17,10 +17,20 @@
package com.android.systemui.recents.events.activity;
import com.android.systemui.recents.events.EventBus;
+import com.android.systemui.recents.misc.ReferenceCountedTrigger;
/**
* This is sent when the history view button is clicked.
*/
public class ShowHistoryEvent extends EventBus.Event {
- // Simple event
+
+ public final ReferenceCountedTrigger postHideStackAnimationTrigger;
+
+ /**
+ * @param postHideStackAnimationTrigger the trigger that gets called when all the task animations are finished when
+ * transitioning to the history view
+ */
+ public ShowHistoryEvent(ReferenceCountedTrigger postHideStackAnimationTrigger) {
+ this.postHideStackAnimationTrigger = postHideStackAnimationTrigger;
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/history/RecentsHistoryAdapter.java b/packages/SystemUI/src/com/android/systemui/recents/history/RecentsHistoryAdapter.java
index 2eee1da..06265bd 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/history/RecentsHistoryAdapter.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/history/RecentsHistoryAdapter.java
@@ -110,7 +110,9 @@
}
}
+ private Context mContext;
private LayoutInflater mLayoutInflater;
+ private final List<Task> mTasks = new ArrayList<>();
private final List<Row> mRows = new ArrayList<>();
public RecentsHistoryAdapter(Context context) {
@@ -121,6 +123,10 @@
* Updates this adapter with the given tasks.
*/
public void updateTasks(Context context, List<Task> tasks) {
+ mContext = context;
+ mTasks.clear();
+ mTasks.addAll(tasks);
+
final Locale l = context.getResources().getConfiguration().locale;
final String dateFormatStr = DateFormat.getBestDateTimePattern(l, "EEEEMMMMd");
final List<Task> tasksMostRecent = new ArrayList<>(tasks);
@@ -144,6 +150,24 @@
notifyDataSetChanged();
}
+ /**
+ * Removes historical tasks beloning to the specified package and user.
+ */
+ public void removeTasks(String packageName, int userId) {
+ boolean packagesRemoved = false;
+ for (int i = mTasks.size() - 1; i >= 0; i--) {
+ Task task = mTasks.get(i);
+ String taskPackage = task.key.getComponent().getPackageName();
+ if (task.key.userId == userId && taskPackage.equals(packageName)) {
+ mTasks.remove(i);
+ packagesRemoved = true;
+ }
+ }
+ if (packagesRemoved) {
+ updateTasks(mContext, new ArrayList<Task>(mTasks));
+ }
+ }
+
@Override
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
switch (viewType) {
diff --git a/packages/SystemUI/src/com/android/systemui/recents/history/RecentsHistoryView.java b/packages/SystemUI/src/com/android/systemui/recents/history/RecentsHistoryView.java
index 5851111..1163f14 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/history/RecentsHistoryView.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/history/RecentsHistoryView.java
@@ -16,6 +16,7 @@
package com.android.systemui.recents.history;
+import android.content.ComponentName;
import android.content.Context;
import android.content.res.Resources;
import android.graphics.Rect;
@@ -29,9 +30,18 @@
import android.widget.LinearLayout;
import com.android.systemui.R;
import com.android.systemui.recents.Recents;
+import com.android.systemui.recents.RecentsActivity;
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.misc.ReferenceCountedTrigger;
+import com.android.systemui.recents.misc.SystemServicesProxy;
+import com.android.systemui.recents.model.Task;
import com.android.systemui.recents.model.TaskStack;
+import com.android.systemui.recents.views.TaskView;
+
+import java.util.ArrayList;
+import java.util.HashSet;
/**
* A list of the recent tasks that are not in the stack.
@@ -77,17 +87,21 @@
/**
* Updates this history view with the recent tasks, and then shows it.
*/
- public void show(TaskStack stack) {
+ public void show(TaskStack stack, ReferenceCountedTrigger postHideAnimationTrigger) {
setVisibility(View.VISIBLE);
setAlpha(0f);
- animate()
- .alpha(1f)
- .setDuration(mHistoryTransitionDuration)
- .setInterpolator(mFastOutSlowInInterpolator)
- .withLayer()
- .start();
-
- mAdapter.updateTasks(getContext(), stack.computeAllTasksList());
+ postHideAnimationTrigger.addLastDecrementRunnable(new Runnable() {
+ @Override
+ public void run() {
+ animate()
+ .alpha(1f)
+ .setDuration(mHistoryTransitionDuration)
+ .setInterpolator(mFastOutSlowInInterpolator)
+ .withLayer()
+ .start();
+ }
+ });
+ mAdapter.updateTasks(getContext(), stack.getHistoricalTasks());
mIsVisible = true;
}
@@ -165,8 +179,27 @@
}
@Override
+ protected void onAttachedToWindow() {
+ SystemServicesProxy ssp = Recents.getSystemServices();
+ EventBus.getDefault().register(this, RecentsActivity.EVENT_BUS_PRIORITY + 1);
+ super.onAttachedToWindow();
+ }
+
+ @Override
+ protected void onDetachedFromWindow() {
+ super.onDetachedFromWindow();
+ EventBus.getDefault().unregister(this);
+ }
+
+ @Override
public WindowInsets onApplyWindowInsets(WindowInsets insets) {
setSystemInsets(insets.getSystemWindowInsets());
return insets;
}
+
+ /**** EventBus Events ****/
+
+ public final void onBusEvent(PackagesChangedEvent event) {
+ mAdapter.removeTasks(event.packageName, event.userId);
+ }
}
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 401b448..b06539a 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/misc/ReferenceCountedTrigger.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/misc/ReferenceCountedTrigger.java
@@ -51,6 +51,10 @@
}
};
+ public ReferenceCountedTrigger(Context context) {
+ this(context, null, null, null);
+ }
+
public ReferenceCountedTrigger(Context context, Runnable firstIncRunnable,
Runnable lastDecRunnable, Runnable errorRunanable) {
mContext = context;
@@ -97,7 +101,7 @@
if (mErrorRunnable != null) {
mErrorRunnable.run();
} else {
- Log.e(TAG, "Invalid ref count");
+ throw new RuntimeException("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 8979843..5888b30 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java
@@ -65,7 +65,6 @@
import com.android.internal.os.BackgroundThread;
import com.android.systemui.Prefs;
import com.android.systemui.R;
-import com.android.systemui.recents.Recents;
import com.android.systemui.recents.RecentsDebugFlags;
import com.android.systemui.recents.RecentsImpl;
@@ -127,7 +126,7 @@
mUm = UserManager.get(context);
mDisplay = mWm.getDefaultDisplay();
mRecentsPackage = context.getPackageName();
- mHasFreeformWorkspaceSupport = false &&
+ mHasFreeformWorkspaceSupport =
mPm.hasSystemFeature(PackageManager.FEATURE_FREEFORM_WINDOW_MANAGEMENT);
// Get the dummy thumbnail width/heights
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 1845bf9..dae6e94 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/model/RecentsTaskLoadPlan.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/model/RecentsTaskLoadPlan.java
@@ -152,14 +152,12 @@
// This task is only shown in the stack if it statisfies the historical time or min
// number of tasks constraints. Freeform tasks are also always shown.
boolean isStackTask = true;
- if (debugFlags.isHistoryEnabled()) {
- boolean isFreeformTask = SystemServicesProxy.isFreeformStack(t.stackId);
- isStackTask = isFreeformTask || (!isHistoricalTask(t) ||
- (t.lastActiveTime >= lastStackActiveTime &&
- i >= (taskCount - MIN_NUM_TASKS)));
- if (isStackTask && newLastStackActiveTime < 0) {
- newLastStackActiveTime = t.lastActiveTime;
- }
+ boolean isFreeformTask = SystemServicesProxy.isFreeformStack(t.stackId);
+ isStackTask = isFreeformTask || (!isHistoricalTask(t) ||
+ (t.lastActiveTime >= lastStackActiveTime &&
+ i >= (taskCount - MIN_NUM_TASKS)));
+ if (isStackTask && newLastStackActiveTime < 0) {
+ newLastStackActiveTime = t.lastActiveTime;
}
// Load the label, icon, and color
@@ -192,7 +190,7 @@
stackTasks.add(task);
}
}
- if (debugFlags.isHistoryEnabled() && newLastStackActiveTime != -1) {
+ if (newLastStackActiveTime != -1) {
Prefs.putLong(mContext, Prefs.Key.OVERVIEW_LAST_STACK_TASK_ACTIVE_TIME,
newLastStackActiveTime);
}
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 512effa..3aed3f3 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/model/Task.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/model/Task.java
@@ -39,7 +39,6 @@
public void onTaskDataLoaded();
/* Notifies when a task has been unbound */
public void onTaskDataUnloaded();
-
/* Notifies when a task's stack id has changed. */
public void onTaskStackIdChanged();
}
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 13ab392..5d07b10 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/model/TaskStack.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/model/TaskStack.java
@@ -572,7 +572,7 @@
Collections.sort(tasks, new Comparator<Task>() {
@Override
public int compare(Task task, Task task2) {
- return (int) (task.key.firstActiveTime - task2.key.firstActiveTime);
+ return Long.compare(task.key.firstActiveTime, task2.key.firstActiveTime);
}
});
// Create groups when sequential packages are the same
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 f646a92..c0b8a9d 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/AnimateableViewBounds.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/AnimateableViewBounds.java
@@ -55,7 +55,7 @@
* Resets the right and bottom clip for this view.
*/
public void reset() {
- mClipRect.setEmpty();
+ mClipRect.set(-1, -1, -1, -1);
updateClipBounds();
}
@@ -90,9 +90,9 @@
}
private void updateClipBounds() {
- mClipBounds.set(mClipRect.left, mClipRect.top,
- mSourceView.getWidth() - mClipRect.right,
- mSourceView.getHeight() - mClipRect.bottom);
+ mClipBounds.set(Math.max(0, mClipRect.left), Math.max(0, mClipRect.top),
+ mSourceView.getWidth() - Math.max(0, mClipRect.right),
+ mSourceView.getHeight() - Math.max(0, mClipRect.bottom));
mSourceView.setClipBounds(mClipBounds);
mSourceView.invalidateOutline();
}
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 36acc28..21c08d1 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java
@@ -32,8 +32,8 @@
import android.view.animation.AnimationUtils;
import android.view.animation.Interpolator;
import android.widget.FrameLayout;
-
import com.android.internal.logging.MetricsLogger;
+import android.widget.TextView;
import com.android.systemui.R;
import com.android.systemui.recents.Recents;
import com.android.systemui.recents.RecentsActivity;
@@ -47,6 +47,7 @@
import com.android.systemui.recents.events.activity.DismissRecentsToHomeAnimationStarted;
import com.android.systemui.recents.events.activity.HideHistoryButtonEvent;
import com.android.systemui.recents.events.activity.HideHistoryEvent;
+import com.android.systemui.recents.events.activity.LaunchTaskEvent;
import com.android.systemui.recents.events.activity.ShowHistoryButtonEvent;
import com.android.systemui.recents.events.activity.ShowHistoryEvent;
import com.android.systemui.recents.events.component.RecentsVisibilityChangedEvent;
@@ -55,6 +56,7 @@
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.ReferenceCountedTrigger;
import com.android.systemui.recents.misc.SystemServicesProxy;
import com.android.systemui.recents.model.Task;
import com.android.systemui.recents.model.TaskStack;
@@ -70,36 +72,34 @@
* This view is the the top level layout that contains TaskStacks (which are laid out according
* to their SpaceNode bounds.
*/
-public class RecentsView extends FrameLayout implements TaskStackView.TaskStackViewCallbacks {
+public class RecentsView extends FrameLayout {
private static final String TAG = "RecentsView";
private static final boolean DEBUG = false;
- Handler mHandler;
+ private final Handler mHandler;
- TaskStack mStack;
- TaskStackView mTaskStackView;
- RecentsAppWidgetHostView mSearchBar;
- View mHistoryButton;
- boolean mAwaitingFirstLayout = true;
- boolean mLastTaskLaunchedWasFreeform;
+ private TaskStack mStack;
+ private TaskStackView mTaskStackView;
+ private RecentsAppWidgetHostView mSearchBar;
+ private TextView mHistoryButton;
+ private View mEmptyView;
+ private boolean mAwaitingFirstLayout = true;
+ private boolean mLastTaskLaunchedWasFreeform;
+ private Rect mSystemInsets = new Rect();
- RecentsTransitionHelper mTransitionHelper;
- RecentsViewTouchHandler mTouchHandler;
- TaskStack.DockState[] mVisibleDockStates = {
+ private RecentsTransitionHelper mTransitionHelper;
+ private RecentsViewTouchHandler mTouchHandler;
+ private TaskStack.DockState[] mVisibleDockStates = {
TaskStack.DockState.LEFT,
TaskStack.DockState.TOP,
TaskStack.DockState.RIGHT,
TaskStack.DockState.BOTTOM,
};
- private Interpolator mFastOutSlowInInterpolator;
- private Interpolator mFastOutLinearInInterpolator;
- private int mHistoryTransitionDuration;
-
- Rect mSystemInsets = new Rect();
-
- final FlingAnimationUtils mFlingAnimationUtils;
+ private final Interpolator mFastOutSlowInInterpolator;
+ private final Interpolator mFastOutLinearInInterpolator;
+ private final FlingAnimationUtils mFlingAnimationUtils;
public RecentsView(Context context) {
this(context, null);
@@ -123,26 +123,32 @@
com.android.internal.R.interpolator.fast_out_slow_in);
mFastOutLinearInInterpolator = AnimationUtils.loadInterpolator(context,
com.android.internal.R.interpolator.fast_out_linear_in);
- mHistoryTransitionDuration = res.getInteger(R.integer.recents_history_transition_duration);
mTouchHandler = new RecentsViewTouchHandler(this);
mFlingAnimationUtils = new FlingAnimationUtils(context, 0.3f);
LayoutInflater inflater = LayoutInflater.from(context);
- mHistoryButton = inflater.inflate(R.layout.recents_history_button, this, false);
+ mHistoryButton = (TextView) inflater.inflate(R.layout.recents_history_button, this, false);
mHistoryButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
- EventBus.getDefault().send(new ShowHistoryEvent());
+ ReferenceCountedTrigger postHideStackAnimationTrigger = new ReferenceCountedTrigger(v.getContext());
+ postHideStackAnimationTrigger.increment();
+ EventBus.getDefault().send(new ShowHistoryEvent(postHideStackAnimationTrigger));
+ postHideStackAnimationTrigger.decrement();
}
});
+ addView(mHistoryButton);
+ mEmptyView = inflater.inflate(R.layout.recents_empty, this, false);
+ addView(mEmptyView);
}
/** Set/get the bsp root node */
public void setTaskStack(TaskStack stack) {
RecentsConfiguration config = Recents.getConfiguration();
+ RecentsActivityLaunchState launchState = config.getLaunchState();
mStack = stack;
// Disable reusing task stack views until the visibility bug is fixed. b/25998134
- if (false && config.getLaunchState().launchedReuseTaskStackViews) {
+ if (false && launchState.launchedReuseTaskStackViews) {
if (mTaskStackView != null) {
// If onRecentsHidden is not triggered, we need to the stack view again here
mTaskStackView.reset();
@@ -151,7 +157,6 @@
addView(mTaskStackView);
} else {
mTaskStackView = new TaskStackView(getContext(), stack);
- mTaskStackView.setCallbacks(this);
addView(mTaskStackView);
}
} else {
@@ -159,13 +164,14 @@
removeView(mTaskStackView);
}
mTaskStackView = new TaskStackView(getContext(), stack);
- mTaskStackView.setCallbacks(this);
addView(mTaskStackView);
}
- if (indexOfChild(mHistoryButton) == -1) {
- addView(mHistoryButton);
+
+ // Update the top level view's visibilities
+ if (stack.getStackTaskCount() > 0) {
+ hideEmptyView();
} else {
- mHistoryButton.bringToFront();
+ showEmptyView();
}
// Trigger a new layout
@@ -216,8 +222,8 @@
Task task = mTaskStackView.getFocusedTask();
if (task != null) {
TaskView taskView = mTaskStackView.getChildViewForTask(task);
- onTaskViewClicked(mTaskStackView, taskView, stack, task, false, null,
- INVALID_STACK_ID);
+ EventBus.getDefault().send(new LaunchTaskEvent(taskView, task, null,
+ INVALID_STACK_ID, false));
return true;
}
}
@@ -231,8 +237,8 @@
Task task = stack.getLaunchTarget();
if (task != null) {
TaskView taskView = mTaskStackView.getChildViewForTask(task);
- onTaskViewClicked(mTaskStackView, taskView, stack, task, false, null,
- INVALID_STACK_ID);
+ EventBus.getDefault().send(new LaunchTaskEvent(taskView, task, null,
+ INVALID_STACK_ID, false));
return true;
}
}
@@ -249,8 +255,8 @@
for (int j = 0; j < taskViewCount; j++) {
TaskView tv = taskViews.get(j);
if (tv.getTask() == task) {
- onTaskViewClicked(mTaskStackView, tv, stack, task, false,
- taskBounds, destinationStack);
+ EventBus.getDefault().send(new LaunchTaskEvent(tv, task, taskBounds,
+ destinationStack, false));
return true;
}
}
@@ -309,13 +315,33 @@
return mSearchBar != null && !mSearchBar.isReinflateRequired();
}
- /** Sets the visibility of the search bar */
- public void setSearchBarVisibility(int visibility) {
+ /**
+ * Hides the task stack and shows the empty view.
+ */
+ public void showEmptyView() {
+ if (!RecentsDebugFlags.Static.DisableSearchBar && (mSearchBar != null)) {
+ mSearchBar.setVisibility(View.INVISIBLE);
+ }
+ mTaskStackView.setVisibility(View.INVISIBLE);
+ mEmptyView.setVisibility(View.VISIBLE);
+ mEmptyView.bringToFront();
+ mHistoryButton.bringToFront();
+ }
+
+ /**
+ * Shows the task stack and hides the empty view.
+ */
+ public void hideEmptyView() {
+ mEmptyView.setVisibility(View.INVISIBLE);
+ mTaskStackView.setVisibility(View.VISIBLE);
+ if (!RecentsDebugFlags.Static.DisableSearchBar && (mSearchBar != null)) {
+ mSearchBar.setVisibility(View.VISIBLE);
+ }
+ mTaskStackView.bringToFront();
if (mSearchBar != null) {
- mSearchBar.setVisibility(visibility);
- // Always bring the search bar to the top
mSearchBar.bringToFront();
}
+ mHistoryButton.bringToFront();
}
/**
@@ -366,6 +392,10 @@
mTaskStackView.measure(widthMeasureSpec, heightMeasureSpec);
}
+ // Measure the empty view
+ measureChild(mEmptyView, MeasureSpec.makeMeasureSpec(width, MeasureSpec.EXACTLY),
+ MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY));
+
// Measure the history button with the full space above the stack, but width-constrained
// to the stack
Rect historyButtonRect = mTaskStackView.mLayoutAlgorithm.mHistoryButtonRect;
@@ -373,6 +403,7 @@
MeasureSpec.makeMeasureSpec(historyButtonRect.width(), MeasureSpec.EXACTLY),
MeasureSpec.makeMeasureSpec(historyButtonRect.height(),
MeasureSpec.EXACTLY));
+
setMeasuredDimension(width, height);
}
@@ -397,6 +428,9 @@
mTaskStackView.layout(left, top, left + getMeasuredWidth(), top + getMeasuredHeight());
}
+ // Layout the empty view
+ mEmptyView.layout(left, top, right, bottom);
+
// Layout the history button left-aligned with the stack, but offset from the top of the
// view
Rect historyButtonRect = mTaskStackView.mLayoutAlgorithm.mHistoryButtonRect;
@@ -456,19 +490,14 @@
return super.verifyDrawable(who);
}
- /**** TaskStackView.TaskStackCallbacks Implementation ****/
-
- @Override
- public void onTaskViewClicked(final TaskStackView stackView, final TaskView tv,
- final TaskStack stack, final Task task, final boolean lockToTask,
- final Rect bounds, int destinationStack) {
- mLastTaskLaunchedWasFreeform = task.isFreeformTask();
- mTransitionHelper.launchTaskFromRecents(stack, task, stackView, tv, lockToTask, bounds,
- destinationStack);
- }
-
/**** EventBus Events ****/
+ public final void onBusEvent(LaunchTaskEvent event) {
+ mLastTaskLaunchedWasFreeform = event.task.isFreeformTask();
+ mTransitionHelper.launchTaskFromRecents(mStack, event.task, mTaskStackView, event.taskView,
+ event.screenPinningRequested, event.targetTaskBounds, event.targetTaskStack);
+ }
+
public final void onBusEvent(DragStartEvent event) {
updateVisibleDockRegions(mTouchHandler.getDockStatesForCurrentOrientation(),
TaskStack.DockState.NONE.viewState.dockAreaAlpha);
@@ -545,12 +574,21 @@
public final void onBusEvent(ShowHistoryEvent event) {
// Hide the history button when the history view is shown
- hideHistoryButton(mHistoryTransitionDuration);
+ hideHistoryButton(getResources().getInteger(R.integer.recents_history_transition_duration),
+ event.postHideStackAnimationTrigger);
+ event.postHideStackAnimationTrigger.addLastDecrementRunnable(new Runnable() {
+ @Override
+ public void run() {
+ setAlpha(0f);
+ }
+ });
}
public final void onBusEvent(HideHistoryEvent event) {
// Show the history button when the history view is hidden
- showHistoryButton(mHistoryTransitionDuration);
+ setAlpha(1f);
+ showHistoryButton(getResources().getInteger(R.integer.recents_history_transition_duration),
+ event.postHideHistoryAnimationTrigger);
}
public final void onBusEvent(ShowHistoryButtonEvent event) {
@@ -561,35 +599,47 @@
hideHistoryButton(100);
}
- public final void onBusEvent(DebugFlagsChangedEvent event) {
- RecentsDebugFlags debugFlags = Recents.getDebugFlags();
- if (!debugFlags.isHistoryEnabled()) {
- hideHistoryButton(100);
- }
- }
-
/**
* Shows the history button.
*/
- private void showHistoryButton(int duration) {
- RecentsDebugFlags debugFlags = Recents.getDebugFlags();
- if (!debugFlags.isHistoryEnabled()) {
- return;
- }
+ private void showHistoryButton(final int duration) {
+ ReferenceCountedTrigger postAnimationTrigger = new ReferenceCountedTrigger(getContext());
+ postAnimationTrigger.increment();
+ showHistoryButton(duration, postAnimationTrigger);
+ postAnimationTrigger.decrement();
+ }
+ private void showHistoryButton(final int duration,
+ final ReferenceCountedTrigger postHideHistoryAnimationTrigger) {
mHistoryButton.setVisibility(View.VISIBLE);
- mHistoryButton.animate()
- .alpha(1f)
- .setDuration(duration)
- .setInterpolator(mFastOutSlowInInterpolator)
- .withLayer()
- .start();
+ mHistoryButton.setAlpha(0f);
+ mHistoryButton.setText(getContext().getString(R.string.recents_history_label_format,
+ mStack.getHistoricalTasks().size()));
+ postHideHistoryAnimationTrigger.addLastDecrementRunnable(new Runnable() {
+ @Override
+ public void run() {
+ mHistoryButton.animate()
+ .alpha(1f)
+ .setDuration(duration)
+ .setInterpolator(mFastOutSlowInInterpolator)
+ .withLayer()
+ .start();
+ }
+ });
}
/**
* Hides the history button.
*/
private void hideHistoryButton(int duration) {
+ ReferenceCountedTrigger postAnimationTrigger = new ReferenceCountedTrigger(getContext());
+ postAnimationTrigger.increment();
+ hideHistoryButton(duration, postAnimationTrigger);
+ postAnimationTrigger.decrement();
+ }
+
+ private void hideHistoryButton(int duration,
+ final ReferenceCountedTrigger postHideStackAnimationTrigger) {
mHistoryButton.animate()
.alpha(0f)
.setDuration(duration)
@@ -598,10 +648,12 @@
@Override
public void run() {
mHistoryButton.setVisibility(View.INVISIBLE);
+ postHideStackAnimationTrigger.decrement();
}
})
.withLayer()
.start();
+ postHideStackAnimationTrigger.increment();
}
/**
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 2920295..37a0194 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/RecentsViewTouchHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/RecentsViewTouchHandler.java
@@ -189,7 +189,7 @@
case MotionEvent.ACTION_CANCEL: {
if (mDragging) {
ReferenceCountedTrigger postAnimationTrigger = new ReferenceCountedTrigger(
- mRv.getContext(), null, null, null);
+ mRv.getContext());
postAnimationTrigger.increment();
EventBus.getDefault().send(new DragEndEvent(mDragTask, mTaskView,
mLastDropTarget, postAnimationTrigger));
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 5a09ee4..f84eb53 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/SystemBarScrimViews.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/SystemBarScrimViews.java
@@ -62,13 +62,14 @@
* Prepares the scrim views for animating when entering Recents. This will be called before
* the first draw.
*/
- public void prepareEnterRecentsAnimation() {
+ public void prepareEnterRecentsAnimation(boolean hasStatusBarScrim, boolean animateStatusBarScrim,
+ boolean hasNavBarScrim, boolean animateNavBarScrim) {
RecentsConfiguration config = Recents.getConfiguration();
RecentsActivityLaunchState launchState = config.getLaunchState();
- mHasNavBarScrim = launchState.hasNavBarScrim();
- mShouldAnimateNavBarScrim = launchState.shouldAnimateNavBarScrim();
- mHasStatusBarScrim = launchState.hasStatusBarScrim();
- mShouldAnimateStatusBarScrim = launchState.shouldAnimateStatusBarScrim();
+ mHasNavBarScrim = hasStatusBarScrim;
+ mShouldAnimateStatusBarScrim = animateStatusBarScrim;
+ mHasStatusBarScrim = hasNavBarScrim;
+ mShouldAnimateNavBarScrim = animateNavBarScrim;
mNavBarScrimView.setVisibility(mHasNavBarScrim && !mShouldAnimateNavBarScrim ?
View.VISIBLE : View.INVISIBLE);
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackLayoutAlgorithm.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackLayoutAlgorithm.java
index f599f52..de944fc 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackLayoutAlgorithm.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackLayoutAlgorithm.java
@@ -355,9 +355,7 @@
/ (taskStackBounds.height() - mSystemInsets.bottom);
int width = mStackRect.width();
int minHeight = mStackRect.height() - mFocusedPeekHeight - mStackBottomOffset;
- int height = debugFlags.isFullscreenThumbnailsEnabled()
- ? (int) Math.min(width / aspect, minHeight)
- : width;
+ int height = (int) Math.min(width / aspect, minHeight);
mTaskRect.set(mStackRect.left, mStackRect.top,
mStackRect.left + width, mStackRect.top + height);
@@ -499,8 +497,7 @@
public float getDefaultFocusState() {
RecentsActivityLaunchState launchState = Recents.getConfiguration().getLaunchState();
RecentsDebugFlags debugFlags = Recents.getDebugFlags();
- if (launchState.launchedWithAltTab ||
- (debugFlags.isPageOnToggleEnabled() && debugFlags.isInitialStatePaging())) {
+ if (launchState.launchedWithAltTab || debugFlags.isInitialStatePaging()) {
return 1f;
}
return 0f;
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 350bc2b..4e75d5a 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java
@@ -27,6 +27,7 @@
import android.graphics.drawable.Drawable;
import android.graphics.drawable.GradientDrawable;
import android.os.Bundle;
+import android.os.Parcelable;
import android.util.IntProperty;
import android.util.Log;
import android.util.Property;
@@ -50,6 +51,7 @@
import com.android.systemui.recents.events.activity.HideHistoryButtonEvent;
import com.android.systemui.recents.events.activity.HideHistoryEvent;
import com.android.systemui.recents.events.activity.IterateRecentsEvent;
+import com.android.systemui.recents.events.activity.LaunchTaskEvent;
import com.android.systemui.recents.events.activity.PackagesChangedEvent;
import com.android.systemui.recents.events.activity.ShowHistoryButtonEvent;
import com.android.systemui.recents.events.activity.ShowHistoryEvent;
@@ -91,6 +93,12 @@
private final static String TAG = "TaskStackView";
private final static boolean DEBUG = false;
+ private final static String KEY_SAVED_STATE_SUPER = "saved_instance_state_super";
+ private final static String KEY_SAVED_STATE_LAYOUT_FOCUSED_STATE =
+ "saved_instance_state_layout_focused_state";
+ private final static String KEY_SAVED_STATE_LAYOUT_STACK_SCROLL =
+ "saved_instance_state_layout_stack_scroll";
+
// The thresholds at which to show/hide the history button.
private static final float SHOW_HISTORY_BUTTON_SCROLL_THRESHOLD = 0.3f;
private static final float HIDE_HISTORY_BUTTON_SCROLL_THRESHOLD = 0.3f;
@@ -108,17 +116,10 @@
}
};
- /** The TaskView callbacks */
- interface TaskStackViewCallbacks {
- public void onTaskViewClicked(TaskStackView stackView, TaskView tv, TaskStack stack, Task t,
- boolean lockToTask, Rect bounds, int destinationStack);
- }
-
TaskStack mStack;
TaskStackLayoutAlgorithm mLayoutAlgorithm;
TaskStackViewScroller mStackScroller;
TaskStackViewTouchHandler mTouchHandler;
- TaskStackViewCallbacks mCb;
GradientDrawable mFreeformWorkspaceBackground;
ObjectAnimator mFreeformWorkspaceBackgroundAnimator;
ViewPool<TaskView, Task> mViewPool;
@@ -216,11 +217,6 @@
}
}
- /** Sets the callbacks */
- void setCallbacks(TaskStackViewCallbacks cb) {
- mCb = cb;
- }
-
@Override
protected void onAttachedToWindow() {
SystemServicesProxy ssp = Recents.getSystemServices();
@@ -673,12 +669,7 @@
if (scrollToTask) {
// TODO: Center the newly focused task view, only if not freeform
- RecentsDebugFlags debugFlags = Recents.getDebugFlags();
float newScroll = mLayoutAlgorithm.getStackScrollForTask(newFocusedTask);
- if (!debugFlags.isFullscreenThumbnailsEnabled()) {
- newScroll -= 0.5f;
- }
- newScroll = mStackScroller.getBoundedStackScroll(newScroll);
if (Float.compare(newScroll, mStackScroller.getStackScroll()) != 0) {
mStackScroller.animateScroll(mStackScroller.getStackScroll(), newScroll,
focusTaskRunnable);
@@ -828,6 +819,24 @@
}
@Override
+ protected Parcelable onSaveInstanceState() {
+ Bundle savedState = new Bundle();
+ savedState.putParcelable(KEY_SAVED_STATE_SUPER, super.onSaveInstanceState());
+ savedState.putFloat(KEY_SAVED_STATE_LAYOUT_FOCUSED_STATE, mLayoutAlgorithm.getFocusState());
+ savedState.putFloat(KEY_SAVED_STATE_LAYOUT_STACK_SCROLL, mStackScroller.getStackScroll());
+ return super.onSaveInstanceState();
+ }
+
+ @Override
+ protected void onRestoreInstanceState(Parcelable state) {
+ Bundle savedState = (Bundle) state;
+ super.onRestoreInstanceState(savedState.getParcelable(KEY_SAVED_STATE_SUPER));
+
+ mLayoutAlgorithm.setFocusState(savedState.getFloat(KEY_SAVED_STATE_LAYOUT_FOCUSED_STATE));
+ mStackScroller.setStackScroll(savedState.getFloat(KEY_SAVED_STATE_LAYOUT_STACK_SCROLL));
+ }
+
+ @Override
public CharSequence getAccessibilityClassName() {
return TaskStackView.class.getName();
}
@@ -1040,8 +1049,11 @@
}
// Update the history button visibility
- if (mStackScroller.getStackScroll() < SHOW_HISTORY_BUTTON_SCROLL_THRESHOLD) {
+ if (shouldShowHistoryButton() &&
+ mStackScroller.getStackScroll() < SHOW_HISTORY_BUTTON_SCROLL_THRESHOLD) {
EventBus.getDefault().send(new ShowHistoryButtonEvent());
+ } else {
+ EventBus.getDefault().send(new HideHistoryButtonEvent());
}
// Start dozing
@@ -1177,7 +1189,8 @@
public boolean launchFreeformTasks() {
Task frontTask = mStack.getStackFrontMostTask();
if (frontTask != null && frontTask.isFreeformTask()) {
- onTaskViewClicked(getChildViewForTask(frontTask), frontTask, false);
+ EventBus.getDefault().send(new LaunchTaskEvent(getChildViewForTask(frontTask),
+ frontTask, null, INVALID_STACK_ID, false));
return true;
}
return false;
@@ -1348,16 +1361,6 @@
/**** TaskViewCallbacks Implementation ****/
@Override
- public void onTaskViewClicked(TaskView tv, Task task, boolean lockToTask) {
- // Cancel any doze triggers
- mUIDozeTrigger.stopDozing();
-
- if (mCb != null) {
- mCb.onTaskViewClicked(this, tv, mStack, task, lockToTask, null, INVALID_STACK_ID);
- }
- }
-
- @Override
public void onTaskViewClipStateChanged(TaskView tv) {
requestUpdateStackViewsClip();
}
@@ -1370,7 +1373,8 @@
requestSynchronizeStackViewsWithModel();
postInvalidateOnAnimation();
- if (prevScroll > SHOW_HISTORY_BUTTON_SCROLL_THRESHOLD &&
+ if (shouldShowHistoryButton() &&
+ prevScroll > SHOW_HISTORY_BUTTON_SCROLL_THRESHOLD &&
curScroll <= SHOW_HISTORY_BUTTON_SCROLL_THRESHOLD) {
EventBus.getDefault().send(new ShowHistoryButtonEvent());
} else if (prevScroll < HIDE_HISTORY_BUTTON_SCROLL_THRESHOLD &&
@@ -1408,6 +1412,11 @@
}
}
+ public final void onBusEvent(LaunchTaskEvent event) {
+ // Cancel any doze triggers once a task is launched
+ mUIDozeTrigger.stopDozing();
+ }
+
public final void onBusEvent(DismissTaskViewEvent event) {
removeTaskViewFromStack(event.taskView);
}
@@ -1517,6 +1526,10 @@
}
public final void onBusEvent(IterateRecentsEvent event) {
+ if (!mEnterAnimationComplete) {
+ // Cancel the previous task's window transition before animating the focused state
+ EventBus.getDefault().send(new CancelEnterRecentsWindowAnimationEvent(null));
+ }
mLayoutAlgorithm.animateFocusState(mLayoutAlgorithm.getDefaultFocusState());
}
@@ -1537,32 +1550,47 @@
}
public final void onBusEvent(ShowHistoryEvent event) {
+ // The history view's animation will be deferred until all the stack task views are animated
+ // away
+ int historyTransitionDuration =
+ getResources().getInteger(R.integer.recents_history_transition_duration);
List<TaskView> taskViews = getTaskViews();
int taskViewCount = taskViews.size();
for (int i = taskViewCount - 1; i >= 0; i--) {
TaskView tv = taskViews.get(i);
tv.animate()
.alpha(0f)
- .setDuration(200)
+ .setDuration(historyTransitionDuration)
.setUpdateListener(null)
.setListener(null)
.withLayer()
+ .withEndAction(event.postHideStackAnimationTrigger.decrementAsRunnable())
.start();
+ event.postHideStackAnimationTrigger.increment();
}
}
public final void onBusEvent(HideHistoryEvent event) {
+ // The stack task view animations will be deferred until the history view has been animated
+ // away
+ final int historyTransitionDuration =
+ getResources().getInteger(R.integer.recents_history_transition_duration);
List<TaskView> taskViews = getTaskViews();
int taskViewCount = taskViews.size();
for (int i = taskViewCount - 1; i >= 0; i--) {
- TaskView tv = taskViews.get(i);
- tv.animate()
- .alpha(1f)
- .setDuration(200)
- .setUpdateListener(null)
- .setListener(null)
- .withLayer()
- .start();
+ final TaskView tv = taskViews.get(i);
+ event.postHideHistoryAnimationTrigger.addLastDecrementRunnable(new Runnable() {
+ @Override
+ public void run() {
+ tv.animate()
+ .alpha(1f)
+ .setDuration(historyTransitionDuration)
+ .setUpdateListener(null)
+ .setListener(null)
+ .withLayer()
+ .start();
+ }
+ });
}
}
@@ -1626,4 +1654,11 @@
}
return -1;
}
+
+ /**
+ * @return whether the history button should be visible
+ */
+ private boolean shouldShowHistoryButton() {
+ return !mStack.getHistoricalTasks().isEmpty();
+ }
}
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 1e2227e..ab51d5f 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java
@@ -45,6 +45,7 @@
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.LaunchTaskEvent;
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;
@@ -54,6 +55,8 @@
import com.android.systemui.recents.model.TaskStack;
import com.android.systemui.statusbar.phone.PhoneStatusBar;
+import static android.app.ActivityManager.StackId.INVALID_STACK_ID;
+
/* A task view */
public class TaskView extends FrameLayout implements Task.TaskCallbacks,
View.OnClickListener, View.OnLongClickListener {
@@ -63,8 +66,7 @@
/** The TaskView callbacks */
interface TaskViewCallbacks {
- public void onTaskViewClicked(TaskView tv, Task task, boolean lockToTask);
- public void onTaskViewClipStateChanged(TaskView tv);
+ void onTaskViewClipStateChanged(TaskView tv);
}
float mTaskProgress;
@@ -278,7 +280,6 @@
/** Resets this view's properties */
void resetViewProperties() {
setDim(0);
- setLayerType(View.LAYER_TYPE_NONE, null);
setVisibility(View.VISIBLE);
getViewBounds().reset();
TaskViewTransform.reset(this);
@@ -723,13 +724,14 @@
@Override
public void onClick(final View v) {
+ boolean screenPinningRequested = false;
if (v == mActionButtonView) {
// Reset the translation of the action button before we animate it out
mActionButtonView.setTranslationZ(0f);
+ screenPinningRequested = true;
}
- if (mCb != null) {
- mCb.onTaskViewClicked(this, mTask, (v == mActionButtonView));
- }
+ EventBus.getDefault().send(new LaunchTaskEvent(this, mTask, null, INVALID_STACK_ID,
+ screenPinningRequested));
}
/**** View.OnLongClickListener Implementation ****/
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 d8220fd..0271ccd 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewHeader.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewHeader.java
@@ -16,25 +16,20 @@
package com.android.systemui.recents.views;
-import android.animation.ObjectAnimator;
import android.content.Context;
import android.content.res.ColorStateList;
import android.graphics.Canvas;
import android.graphics.Color;
-import android.graphics.Outline;
import android.graphics.Paint;
import android.graphics.PorterDuff;
-import android.graphics.PorterDuffColorFilter;
import android.graphics.PorterDuffXfermode;
import android.graphics.Rect;
import android.graphics.drawable.ColorDrawable;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.GradientDrawable;
import android.graphics.drawable.RippleDrawable;
-import android.graphics.drawable.ShapeDrawable;
import android.util.AttributeSet;
import android.view.View;
-import android.view.ViewOutlineProvider;
import android.view.animation.AnimationUtils;
import android.view.animation.Interpolator;
import android.widget.FrameLayout;
@@ -45,12 +40,16 @@
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.ui.ResizeTaskEvent;
+import com.android.systemui.recents.events.activity.LaunchTaskEvent;
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.Task;
+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 task bar view */
public class TaskViewHeader extends FrameLayout
@@ -63,6 +62,7 @@
ImageView mDismissButton;
ImageView mApplicationIcon;
TextView mActivityDescription;
+ int mMoveTaskTargetStackId = INVALID_STACK_ID;
// Header drawables
Rect mTaskViewRect = new Rect();
@@ -157,24 +157,55 @@
*/
public void onTaskViewSizeChanged(int width, int height) {
mTaskViewRect.set(0, 0, width, height);
- if (mDismissButton.getMeasuredWidth() > (width - mApplicationIcon.getMeasuredWidth())) {
- mDismissButton.setAlpha(0f);
- } else {
- mDismissButton.setAlpha(1f);
- if (mDismissButton != null) {
- mDismissButton.setTranslationX(width - getMeasuredWidth());
+ boolean updateMoveTaskButton = mMoveTaskButton.getVisibility() != View.GONE;
+ int appIconWidth = mApplicationIcon.getMeasuredWidth();
+ int activityDescWidth = mActivityDescription.getMeasuredWidth();
+ int dismissIconWidth = mDismissButton.getMeasuredWidth();
+ int moveTaskIconWidth = mMoveTaskButton.getVisibility() == View.VISIBLE
+ ? mMoveTaskButton.getMeasuredWidth()
+ : 0;
+
+ // Priority-wise, we show the activity icon first, the dismiss icon if there is room, the
+ // move-task icon if there is room, and then finally, the activity label if there is room
+ if (width < (appIconWidth + dismissIconWidth)) {
+ mActivityDescription.setVisibility(View.INVISIBLE);
+ if (updateMoveTaskButton) {
+ mMoveTaskButton.setVisibility(View.INVISIBLE);
}
- }
- if (mActivityDescription.getMeasuredWidth() > (width -
- (mApplicationIcon.getMeasuredWidth() + mDismissButton.getMeasuredWidth()))) {
- mActivityDescription.setAlpha(0f);
+ mDismissButton.setVisibility(View.INVISIBLE);
+ } else if (width < (appIconWidth + dismissIconWidth + moveTaskIconWidth)) {
+ mActivityDescription.setVisibility(View.INVISIBLE);
+ if (updateMoveTaskButton) {
+ mMoveTaskButton.setVisibility(View.INVISIBLE);
+ }
+ mDismissButton.setVisibility(View.VISIBLE);
+ } else if (width < (appIconWidth + dismissIconWidth + moveTaskIconWidth +
+ activityDescWidth)) {
+ mActivityDescription.setVisibility(View.INVISIBLE);
+ if (updateMoveTaskButton) {
+ mMoveTaskButton.setVisibility(View.VISIBLE);
+ }
+ mDismissButton.setVisibility(View.VISIBLE);
} else {
- mActivityDescription.setAlpha(1f);
+ mActivityDescription.setVisibility(View.VISIBLE);
+ if (updateMoveTaskButton) {
+ mMoveTaskButton.setVisibility(View.VISIBLE);
+ }
+ mDismissButton.setVisibility(View.VISIBLE);
}
+ if (updateMoveTaskButton) {
+ mMoveTaskButton.setTranslationX(width - getMeasuredWidth());
+ }
+ mDismissButton.setTranslationX(width - getMeasuredWidth());
invalidate();
}
@Override
+ protected void onSizeChanged(int w, int h, int oldw, int oldh) {
+ onTaskViewSizeChanged(mTaskViewRect.width(), mTaskViewRect.height());
+ }
+
+ @Override
protected void onDraw(Canvas canvas) {
// Draw the highlight at the top edge (but put the bottom edge just out of view)
float offset = (float) Math.ceil(mHighlightHeight / 2f);
@@ -197,6 +228,7 @@
/** Binds the bar view to the task */
public void rebindToTask(Task t) {
+ SystemServicesProxy ssp = Recents.getSystemServices();
mTask = t;
// If an activity icon is defined, then we use that as the primary icon to show in the bar,
@@ -228,12 +260,26 @@
mLightDismissDrawable : mDarkDismissDrawable);
mDismissButton.setContentDescription(String.format(mDismissContentDescription,
t.contentDescription));
- updateResizeTaskBarIcon(t);
- mMoveTaskButton.setVisibility(View.VISIBLE);
- mMoveTaskButton.setOnClickListener(this);
+
+ // When freeform workspaces are enabled, then update the move-task button depending on the
+ // current task
+ if (ssp.hasFreeformWorkspaceSupport()) {
+ if (t.isFreeformTask()) {
+ mMoveTaskTargetStackId = FULLSCREEN_WORKSPACE_STACK_ID;
+ mMoveTaskButton.setImageResource(t.useLightOnPrimaryColor
+ ? R.drawable.recents_move_task_fullscreen_light
+ : R.drawable.recents_move_task_fullscreen_dark);
+ } else {
+ mMoveTaskTargetStackId = FREEFORM_WORKSPACE_STACK_ID;
+ mMoveTaskButton.setImageResource(t.useLightOnPrimaryColor
+ ? R.drawable.recents_move_task_freeform_light
+ : R.drawable.recents_move_task_freeform_dark);
+ }
+ mMoveTaskButton.setVisibility(View.VISIBLE);
+ mMoveTaskButton.setOnClickListener(this);
+ }
// In accessibility, a single click on the focused app info button will show it
- SystemServicesProxy ssp = Recents.getSystemServices();
if (ssp.isTouchExplorationEnabled()) {
mApplicationIcon.setOnClickListener(this);
}
@@ -247,40 +293,6 @@
mMoveTaskButton.setOnClickListener(null);
}
- /** Updates the resize task bar button. */
- void updateResizeTaskBarIcon(Task t) {
- 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;
- } else {
- boolean top = display.top == taskRect.top;
- boolean bottom = display.bottom == taskRect.bottom;
- boolean left = display.left == taskRect.left;
- boolean right = display.right == taskRect.right;
- if (top && bottom && left) {
- resId = R.drawable.vector_drawable_place_left;
- } else if (top && bottom && right) {
- resId = R.drawable.vector_drawable_place_right;
- } else if (top && left && right) {
- resId = R.drawable.vector_drawable_place_top;
- } else if (bottom && left && right) {
- resId = R.drawable.vector_drawable_place_bottom;
- } else if (top && right) {
- resId = R.drawable.vector_drawable_place_top_right;
- } else if (top && left) {
- resId = R.drawable.vector_drawable_place_top_left;
- } else if (bottom && right) {
- resId = R.drawable.vector_drawable_place_bottom_right;
- } else if (bottom && left) {
- resId = R.drawable.vector_drawable_place_bottom_left;
- }
- }
- mMoveTaskButton.setImageResource(resId);
- }
-
/** Animates this task bar dismiss button when launching a task. */
void startLaunchTaskDismissAnimation(final Runnable postAnimationRunanble) {
if (mDismissButton.getVisibility() == View.VISIBLE) {
@@ -356,7 +368,12 @@
MetricsLogger.histogram(getContext(), "overview_task_dismissed_source",
Constants.Metrics.DismissSourceHeaderButton);
} else if (v == mMoveTaskButton) {
- EventBus.getDefault().send(new ResizeTaskEvent(mTask));
+ TaskView tv = Utilities.findParent(this, TaskView.class);
+ Rect bounds = mMoveTaskTargetStackId == FREEFORM_WORKSPACE_STACK_ID
+ ? new Rect(mTaskViewRect)
+ : new Rect();
+ EventBus.getDefault().send(new LaunchTaskEvent(tv, mTask, bounds,
+ mMoveTaskTargetStackId, false));
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/stackdivider/DividerView.java b/packages/SystemUI/src/com/android/systemui/stackdivider/DividerView.java
index 93264ff..c01f170 100644
--- a/packages/SystemUI/src/com/android/systemui/stackdivider/DividerView.java
+++ b/packages/SystemUI/src/com/android/systemui/stackdivider/DividerView.java
@@ -30,6 +30,7 @@
import android.view.Display;
import android.view.DisplayInfo;
import android.view.MotionEvent;
+import android.view.PointerIcon;
import android.view.VelocityTracker;
import android.view.View;
import android.view.View.OnTouchListener;
@@ -118,8 +119,8 @@
updateDisplayInfo();
boolean landscape = getResources().getConfiguration().orientation
== Configuration.ORIENTATION_LANDSCAPE;
- mHandle.setPointerShape(
- landscape ? STYLE_HORIZONTAL_DOUBLE_ARROW : STYLE_VERTICAL_DOUBLE_ARROW);
+ mHandle.setPointerIcon(PointerIcon.getSystemIcon(getContext(),
+ landscape ? STYLE_HORIZONTAL_DOUBLE_ARROW : STYLE_VERTICAL_DOUBLE_ARROW));
getViewTreeObserver().addOnComputeInternalInsetsListener(this);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
index 39a33a5..6d4dc872 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
@@ -165,6 +165,8 @@
protected NotificationGroupManager mGroupManager = new NotificationGroupManager();
+ protected RemoteInputController mRemoteInputController;
+
// for heads up notifications
protected HeadsUpManager mHeadsUpManager;
@@ -1417,6 +1419,7 @@
parent, false);
row.setExpansionLogger(this, entry.notification.getKey());
row.setGroupManager(mGroupManager);
+ row.setRemoteInputController(mRemoteInputController);
row.setOnExpandClickListener(this);
}
@@ -1587,7 +1590,6 @@
}
row.setUserLocked(userLocked);
row.onNotificationUpdated(entry);
- applyRemoteInput(entry);
return true;
}
@@ -1630,78 +1632,6 @@
}
}
- private void applyRemoteInput(final Entry entry) {
- if (!ENABLE_REMOTE_INPUT) return;
-
- boolean hasRemoteInput = false;
-
- Notification.Action[] actions = entry.notification.getNotification().actions;
- if (actions != null) {
- for (Notification.Action a : actions) {
- if (a.getRemoteInputs() != null) {
- for (RemoteInput ri : a.getRemoteInputs()) {
- if (ri.getAllowFreeFormInput()) {
- hasRemoteInput = true;
- break;
- }
- }
- }
- }
- }
-
- View bigContentView = entry.getExpandedContentView();
- if (bigContentView != null) {
- applyRemoteInput(bigContentView, entry, hasRemoteInput);
- }
- View headsUpContentView = entry.getHeadsUpContentView();
- if (headsUpContentView != null) {
- applyRemoteInput(headsUpContentView, entry, hasRemoteInput);
- }
-
- }
-
- private RemoteInputView applyRemoteInput(View view, Entry entry, boolean hasRemoteInput) {
- View actionContainerCandidate = view.findViewById(
- com.android.internal.R.id.actions_container);
- if (actionContainerCandidate instanceof FrameLayout) {
- RemoteInputView existing = (RemoteInputView)
- view.findViewWithTag(RemoteInputView.VIEW_TAG);
-
- if (hasRemoteInput) {
- if (existing != null) {
- existing.onNotificationUpdate();
- return existing;
- }
-
- ViewGroup actionContainer = (FrameLayout) actionContainerCandidate;
- RemoteInputView riv = inflateRemoteInputView(actionContainer, entry);
- if (riv != null) {
- riv.setVisibility(View.INVISIBLE);
- actionContainer.addView(riv, new FrameLayout.LayoutParams(
- ViewGroup.LayoutParams.MATCH_PARENT,
- ViewGroup.LayoutParams.MATCH_PARENT)
- );
- int color = entry.notification.getNotification().color;
- if (color == Notification.COLOR_DEFAULT) {
- color = mContext.getColor(R.color.default_remote_input_background);
- }
- riv.setBackgroundColor(color);
- return riv;
- }
- } else {
- if (existing != null) {
- existing.onNotificationUpdate();
- return null;
- }
- }
- }
- return null;
- }
-
- protected RemoteInputView inflateRemoteInputView(ViewGroup root, Entry entry) {
- return null;
- }
-
public void startPendingIntentDismissingKeyguard(final PendingIntent intent) {
if (!isDeviceProvisioned()) return;
@@ -2230,8 +2160,6 @@
entry.row.onNotificationUpdated(entry);
entry.row.resetHeight();
-
- applyRemoteInput(entry);
}
protected void notifyHeadsUpScreenOff() {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java
index ed4c774..874b76a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java
@@ -199,8 +199,8 @@
public void onNotificationUpdated(NotificationData.Entry entry) {
mEntry = entry;
mStatusBarNotification = entry.notification;
- mPrivateLayout.onNotificationUpdated(entry.notification);
- mPublicLayout.onNotificationUpdated(entry.notification);
+ mPrivateLayout.onNotificationUpdated(entry);
+ mPublicLayout.onNotificationUpdated(entry);
updateVetoButton();
if (mIsSummaryWithChildren) {
recreateNotificationHeader();
@@ -254,6 +254,10 @@
mPrivateLayout.setGroupManager(groupManager);
}
+ public void setRemoteInputController(RemoteInputController r) {
+ mPrivateLayout.setRemoteInputController(r);
+ }
+
public void addChildNotification(ExpandableNotificationRow row) {
addChildNotification(row, -1);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationContentView.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationContentView.java
index 2944c4f..6d90329 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationContentView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationContentView.java
@@ -16,6 +16,8 @@
package com.android.systemui.statusbar;
+import android.app.Notification;
+import android.app.RemoteInput;
import android.content.Context;
import android.graphics.Outline;
import android.graphics.Paint;
@@ -37,6 +39,7 @@
import com.android.systemui.statusbar.notification.HybridNotificationView;
import com.android.systemui.statusbar.notification.HybridNotificationViewManager;
import com.android.systemui.statusbar.phone.NotificationGroupManager;
+import com.android.systemui.statusbar.policy.RemoteInputView;
/**
* A frame layout containing the actual payload of the notification, including the contracted,
@@ -79,6 +82,7 @@
private int mHeadsUpHeight;
private StatusBarNotification mStatusBarNotification;
private NotificationGroupManager mGroupManager;
+ private RemoteInputController mRemoteInputController;
private final ViewTreeObserver.OnPreDrawListener mEnableAnimationPredrawListener
= new ViewTreeObserver.OnPreDrawListener() {
@@ -484,9 +488,10 @@
updateSingleLineView();
}
- public void onNotificationUpdated(StatusBarNotification statusBarNotification) {
- mStatusBarNotification = statusBarNotification;
+ public void onNotificationUpdated(NotificationData.Entry entry) {
+ mStatusBarNotification = entry.notification;
updateSingleLineView();
+ applyRemoteInput(entry);
selectLayout(false /* animate */, true /* force */);
if (mContractedChild != null) {
mContractedWrapper.notifyContentUpdated();
@@ -508,10 +513,75 @@
}
}
+ private void applyRemoteInput(final NotificationData.Entry entry) {
+ if (mRemoteInputController == null) {
+ return;
+ }
+
+ boolean hasRemoteInput = false;
+
+ Notification.Action[] actions = entry.notification.getNotification().actions;
+ if (actions != null) {
+ for (Notification.Action a : actions) {
+ if (a.getRemoteInputs() != null) {
+ for (RemoteInput ri : a.getRemoteInputs()) {
+ if (ri.getAllowFreeFormInput()) {
+ hasRemoteInput = true;
+ break;
+ }
+ }
+ }
+ }
+ }
+
+ View bigContentView = mExpandedChild;
+ if (bigContentView != null) {
+ applyRemoteInput(bigContentView, entry, hasRemoteInput);
+ }
+ View headsUpContentView = mHeadsUpChild;
+ if (headsUpContentView != null) {
+ applyRemoteInput(headsUpContentView, entry, hasRemoteInput);
+ }
+ }
+
+ private void applyRemoteInput(View view, NotificationData.Entry entry, boolean hasRemoteInput) {
+ View actionContainerCandidate = view.findViewById(
+ com.android.internal.R.id.actions_container);
+ if (actionContainerCandidate instanceof FrameLayout) {
+ RemoteInputView existing = (RemoteInputView)
+ view.findViewWithTag(RemoteInputView.VIEW_TAG);
+
+ if (existing != null) {
+ existing.onNotificationUpdate();
+ }
+
+ if (existing == null && hasRemoteInput) {
+ ViewGroup actionContainer = (FrameLayout) actionContainerCandidate;
+ RemoteInputView riv = RemoteInputView.inflate(
+ mContext, actionContainer, entry, mRemoteInputController);
+
+ riv.setVisibility(View.INVISIBLE);
+ actionContainer.addView(riv, new LayoutParams(
+ ViewGroup.LayoutParams.MATCH_PARENT,
+ ViewGroup.LayoutParams.MATCH_PARENT)
+ );
+ int color = entry.notification.getNotification().color;
+ if (color == Notification.COLOR_DEFAULT) {
+ color = mContext.getColor(R.color.default_remote_input_background);
+ }
+ riv.setBackgroundColor(color);
+ }
+ }
+ }
+
public void setGroupManager(NotificationGroupManager groupManager) {
mGroupManager = groupManager;
}
+ public void setRemoteInputController(RemoteInputController r) {
+ mRemoteInputController = r;
+ }
+
public void setExpandClickListener(OnClickListener expandClickListener) {
mExpandClickListener = expandClickListener;
}
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 8f7c95e..80fcba60e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
@@ -311,8 +311,6 @@
StatusBarIconController mIconController;
- private RemoteInputController mRemoteInputController;
-
// expanded notifications
NotificationPanelView mNotificationPanel; // the sliding/resizing panel within the notification window
View mExpandedContents;
@@ -1100,11 +1098,6 @@
return mStatusBarWindow;
}
- @Override
- protected RemoteInputView inflateRemoteInputView(ViewGroup root, Entry entry) {
- return RemoteInputView.inflate(mContext, root, entry, mRemoteInputController);
- }
-
public int getStatusBarHeight() {
if (mNaturalBarHeight < 0) {
final Resources res = mContext.getResources();
diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialog.java b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialog.java
index da19b06..9d4ec10 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialog.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialog.java
@@ -128,6 +128,7 @@
private boolean mPendingRecheckAll;
private long mCollapseTime;
private boolean mHovering = false;
+ private int mLastActiveStream;
public VolumeDialog(Context context, int windowType, VolumeDialogController controller,
ZenModeController zenModeController, Callback callback) {
@@ -283,10 +284,14 @@
@Override
public void onLayoutChange(View v, int left, int top, int right, int bottom,
int oldLeft, int oldTop, int oldRight, int oldBottom) {
- final boolean moved = oldLeft != left || oldTop != top;
+ final boolean moved = mLastActiveStream != mActiveStream ||
+ oldLeft != left || oldTop != top;
if (D.BUG) Log.d(TAG, "onLayoutChange moved=" + moved
+ " old=" + new Rect(oldLeft, oldTop, oldRight, oldBottom).toShortString()
- + " new=" + new Rect(left,top,right,bottom).toShortString());
+ + "," + mLastActiveStream
+ + " new=" + new Rect(left,top,right,bottom).toShortString()
+ + "," + mActiveStream);
+ mLastActiveStream = mActiveStream;
if (moved) {
for (int i = 0; i < mDialogContentView.getChildCount(); i++) {
final View c = mDialogContentView.getChildAt(i);
diff --git a/packages/VpnDialogs/res/values-b+sr+Latn/strings.xml b/packages/VpnDialogs/res/values-b+sr+Latn/strings.xml
new file mode 100644
index 0000000..173cd03
--- /dev/null
+++ b/packages/VpnDialogs/res/values-b+sr+Latn/strings.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Copyright (C) 2011 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="prompt" msgid="3183836924226407828">"Zahtev za povezivanje"</string>
+ <string name="warning" msgid="809658604548412033">"<xliff:g id="APP">%s</xliff:g> želi da podesi VPN vezu koja omogućava praćenje saobraćaja na mreži. Prihvatite samo ako verujete izvoru. <br /> <br /> <img src=vpn_icon /> se prikazuje u vrhu ekrana kada je VPN aktivan."</string>
+ <string name="legacy_title" msgid="192936250066580964">"VPN je povezan"</string>
+ <string name="configure" msgid="4905518375574791375">"Konfiguriši"</string>
+ <string name="disconnect" msgid="971412338304200056">"Prekini vezu"</string>
+ <string name="session" msgid="6470628549473641030">"Sesija:"</string>
+ <string name="duration" msgid="3584782459928719435">"Trajanje:"</string>
+ <string name="data_transmitted" msgid="7988167672982199061">"Poslato:"</string>
+ <string name="data_received" msgid="4062776929376067820">"Primljeno:"</string>
+ <string name="data_value_format" msgid="2192466557826897580">"<xliff:g id="NUMBER_0">%1$s</xliff:g> bajt(ov)a / <xliff:g id="NUMBER_1">%2$s</xliff:g> paketa"</string>
+</resources>
diff --git a/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java b/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
index 25fef18..df18d3e 100644
--- a/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
+++ b/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
@@ -37,6 +37,7 @@
import android.content.pm.IPackageManager;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
+import android.content.pm.PackageManager.NameNotFoundException;
import android.content.pm.ParceledListSlice;
import android.content.pm.ResolveInfo;
import android.content.pm.ServiceInfo;
@@ -44,7 +45,10 @@
import android.content.res.Resources;
import android.content.res.TypedArray;
import android.content.res.XmlResourceParser;
+import android.graphics.Bitmap;
+import android.graphics.Canvas;
import android.graphics.Point;
+import android.graphics.drawable.Drawable;
import android.net.Uri;
import android.os.Binder;
import android.os.Bundle;
@@ -79,6 +83,7 @@
import com.android.internal.util.FastXmlSerializer;
import com.android.internal.widget.IRemoteViewsAdapterConnection;
import com.android.internal.widget.IRemoteViewsFactory;
+import com.android.internal.R;
import com.android.server.LocalServices;
import com.android.server.WidgetBackupProvider;
@@ -149,6 +154,14 @@
} else if (Intent.ACTION_USER_STOPPED.equals(action)) {
onUserStopped(intent.getIntExtra(Intent.EXTRA_USER_HANDLE,
UserHandle.USER_NULL));
+ } else if (Intent.ACTION_USER_SWITCHED.equals(action)) {
+ refreshProfileWidgetsMaskedState(intent.getIntExtra(Intent.EXTRA_USER_HANDLE,
+ UserHandle.USER_NULL));
+ } else if (Intent.ACTION_MANAGED_PROFILE_AVAILABILITY_CHANGED.equals(action)) {
+ UserHandle profile = (UserHandle)intent.getParcelableExtra(Intent.EXTRA_USER);
+ if (profile != null) {
+ refreshWidgetMaskedState(profile.getIdentifier());
+ }
} else {
onPackageBroadcastReceived(intent, intent.getIntExtra(
Intent.EXTRA_USER_HANDLE, UserHandle.USER_NULL));
@@ -208,6 +221,7 @@
mCallbackHandler = new CallbackHandler(mContext.getMainLooper());
mBackupRestoreController = new BackupRestoreController();
mSecurityPolicy = new SecurityPolicy();
+
computeMaximumWidgetBitmapMemory();
registerBroadcastReceiver();
registerOnCrossProfileProvidersChangedListener();
@@ -251,8 +265,14 @@
IntentFilter userFilter = new IntentFilter();
userFilter.addAction(Intent.ACTION_USER_STARTED);
userFilter.addAction(Intent.ACTION_USER_STOPPED);
+ userFilter.addAction(Intent.ACTION_USER_SWITCHED);
mContext.registerReceiverAsUser(mBroadcastReceiver, UserHandle.ALL,
userFilter, null, null);
+
+ IntentFilter offModeFilter = new IntentFilter();
+ offModeFilter.addAction(Intent.ACTION_MANAGED_PROFILE_AVAILABILITY_CHANGED);
+ mContext.registerReceiverAsUser(mBroadcastReceiver, UserHandle.ALL,
+ offModeFilter, null, null);
}
private void registerOnCrossProfileProvidersChangedListener() {
@@ -395,6 +415,60 @@
}
}
+ /**
+ * Refresh the masked state for all profiles under the given user.
+ */
+ private void refreshProfileWidgetsMaskedState(int userId) {
+ if (userId == UserHandle.USER_NULL) {
+ return;
+ }
+ List<UserInfo> profiles = mUserManager.getEnabledProfiles(userId);
+ if (profiles != null) {
+ for (int i = 0; i < profiles.size(); i++) {
+ UserInfo user = profiles.get(i);
+ refreshWidgetMaskedState(user.id);
+ }
+ }
+ }
+
+ /**
+ * Mask/unmask widgets in the given profile, depending on the quiet state of the profile.
+ */
+ private void refreshWidgetMaskedState(int profileId) {
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ UserInfo user = mUserManager.getUserInfo(profileId);
+ if (!user.isManagedProfile()) {
+ return;
+ }
+ boolean shouldMask = user.isQuietModeEnabled();
+ final int iconSize = (int) mContext.getResources().getDimension(
+ android.R.dimen.app_icon_size);
+ synchronized (mLock) {
+ final int N = mProviders.size();
+ for (int i = 0; i < N; i++) {
+ Provider provider = mProviders.get(i);
+ int providerUserId = provider.getUserId();
+ if (providerUserId == profileId) {
+ final int widgetCount = provider.widgets.size();
+ for (int j = 0; j < widgetCount; j++) {
+ Widget widget = provider.widgets.get(j);
+ if (shouldMask) {
+ widget.replaceWithMaskedViewsLocked(mContext, iconSize);
+ } else {
+ widget.clearMaskedViewsLocked();
+ }
+ scheduleNotifyUpdateAppWidgetLocked(widget,
+ widget.getEffectiveViewsLocked());
+ }
+ }
+ }
+ }
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ }
+
private void resolveHostUidLocked(String pkg, int uid) {
final int N = mHosts.size();
for (int i = 0; i < N; i++) {
@@ -516,7 +590,7 @@
for (int i = 0; i < N; i++) {
Widget widget = instances.get(i);
updatedIds[i] = widget.appWidgetId;
- updatedViews.add(cloneIfLocalBinder(widget.views));
+ updatedViews.add(cloneIfLocalBinder(widget.getEffectiveViewsLocked()));
}
return updatedIds;
@@ -1128,7 +1202,7 @@
Binder.getCallingUid(), callingPackage);
if (widget != null) {
- return cloneIfLocalBinder(widget.views);
+ return cloneIfLocalBinder(widget.getEffectiveViewsLocked());
}
return null;
@@ -1554,8 +1628,7 @@
// For a full update we replace the RemoteViews completely.
widget.views = views;
}
-
- scheduleNotifyUpdateAppWidgetLocked(widget, views);
+ scheduleNotifyUpdateAppWidgetLocked(widget, widget.getEffectiveViewsLocked());
}
}
@@ -3536,6 +3609,7 @@
int restoredId; // tracking & remapping any restored state
Provider provider;
RemoteViews views;
+ RemoteViews maskedViews;
Bundle options;
Host host;
@@ -3543,6 +3617,34 @@
public String toString() {
return "AppWidgetId{" + appWidgetId + ':' + host + ':' + provider + '}';
}
+
+ public void replaceWithMaskedViewsLocked(Context context, int iconSize) {
+ if (maskedViews != null) {
+ return;
+ }
+ maskedViews = new RemoteViews(context.getPackageName(), R.layout.work_widget_mask_view);
+ try {
+ Drawable icon = context.getPackageManager().getApplicationIcon(
+ provider.info.provider.getPackageName());
+ final int width = iconSize;
+ final int height = iconSize;
+ Bitmap iconBitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
+ Canvas canvas = new Canvas(iconBitmap);
+ icon.setBounds(0, 0, width, height);
+ icon.draw(canvas);
+ maskedViews.setImageViewBitmap(R.id.work_widget_app_icon, iconBitmap);
+ } catch (NameNotFoundException e) {
+ Slog.e(TAG, "Fail to get application icon", e);
+ }
+ }
+
+ public void clearMaskedViewsLocked() {
+ maskedViews = null;
+ }
+
+ public RemoteViews getEffectiveViewsLocked() {
+ return maskedViews != null ? maskedViews : views;
+ }
}
/**
diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java
index ed64c2b..65a27c8 100644
--- a/services/core/java/com/android/server/ConnectivityService.java
+++ b/services/core/java/com/android/server/ConnectivityService.java
@@ -255,11 +255,6 @@
private static final int EVENT_APPLY_GLOBAL_HTTP_PROXY = 9;
/**
- * used internally to send a sticky broadcast delayed.
- */
- private static final int EVENT_SEND_STICKY_BROADCAST_INTENT = 11;
-
- /**
* PAC manager has received new port.
*/
private static final int EVENT_PROXY_HAS_CHANGED = 16;
@@ -2515,11 +2510,6 @@
handleDeprecatedGlobalHttpProxy();
break;
}
- case EVENT_SEND_STICKY_BROADCAST_INTENT: {
- Intent intent = (Intent)msg.obj;
- sendStickyBroadcast(intent);
- break;
- }
case EVENT_PROXY_HAS_CHANGED: {
handleApplyDefaultProxy((ProxyInfo)msg.obj);
break;
diff --git a/services/core/java/com/android/server/InputMethodManagerService.java b/services/core/java/com/android/server/InputMethodManagerService.java
index ef79cfe..04abcca 100644
--- a/services/core/java/com/android/server/InputMethodManagerService.java
+++ b/services/core/java/com/android/server/InputMethodManagerService.java
@@ -166,6 +166,7 @@
static final int MSG_SET_ACTIVE = 3020;
static final int MSG_SET_INTERACTIVE = 3030;
static final int MSG_SET_USER_ACTION_NOTIFICATION_SEQUENCE_NUMBER = 3040;
+ static final int MSG_SWITCH_IME = 3050;
static final int MSG_HARD_KEYBOARD_SWITCH_CHANGED = 4000;
@@ -2851,6 +2852,9 @@
case MSG_SET_INTERACTIVE:
handleSetInteractive(msg.arg1 != 0);
return true;
+ case MSG_SWITCH_IME:
+ handleSwitchInputMethod(msg.arg1 != 0);
+ return true;
case MSG_SET_USER_ACTION_NOTIFICATION_SEQUENCE_NUMBER: {
final int sequenceNumber = msg.arg1;
final ClientState clientState = (ClientState)msg.obj;
@@ -2887,6 +2891,18 @@
}
}
+ private void handleSwitchInputMethod(final boolean forwardDirection) {
+ synchronized (mMethodMap) {
+ // TODO: Support forwardDirection.
+ final ImeSubtypeListItem nextSubtype = mSwitchingController.getNextInputMethodLocked(
+ false, mMethodMap.get(mCurMethodId), mCurrentSubtype);
+ if (nextSubtype == null) {
+ return;
+ }
+ setInputMethodLocked(nextSubtype.mImi.getId(), nextSubtype.mSubtypeId);
+ }
+ }
+
private boolean chooseNewDefaultIMELocked() {
final InputMethodInfo imi = InputMethodUtils.getMostApplicableDefaultIME(
mSettings.getEnabledInputMethodListLocked());
@@ -3734,6 +3750,13 @@
mHandler.sendMessage(mHandler.obtainMessage(MSG_SET_INTERACTIVE,
interactive ? 1 : 0, 0));
}
+
+ @Override
+ public void switchInputMethod(boolean forwardDirection) {
+ // Do everything in handler so as not to block the caller.
+ mHandler.sendMessage(mHandler.obtainMessage(MSG_SWITCH_IME,
+ forwardDirection ? 1 : 0, 0));
+ }
}
@Override
diff --git a/services/core/java/com/android/server/LockSettingsService.java b/services/core/java/com/android/server/LockSettingsService.java
index 033a4b8..1a21f5c 100644
--- a/services/core/java/com/android/server/LockSettingsService.java
+++ b/services/core/java/com/android/server/LockSettingsService.java
@@ -356,6 +356,10 @@
}
}
+ if (LockPatternUtils.LEGACY_LOCK_PATTERN_ENABLED.equals(key)) {
+ key = Settings.Secure.LOCK_PATTERN_ENABLED;
+ }
+
return mStorage.readKeyValue(key, defaultValue, userId);
}
diff --git a/services/core/java/com/android/server/accounts/AccountManagerService.java b/services/core/java/com/android/server/accounts/AccountManagerService.java
index 502d61a..ca5212a 100644
--- a/services/core/java/com/android/server/accounts/AccountManagerService.java
+++ b/services/core/java/com/android/server/accounts/AccountManagerService.java
@@ -2449,7 +2449,7 @@
if (sessionBundle != null) {
String accountType = sessionBundle.getString(AccountManager.KEY_ACCOUNT_TYPE);
if (TextUtils.isEmpty(accountType)
- && !mAccountType.equalsIgnoreCase(mAccountType)) {
+ || !mAccountType.equalsIgnoreCase(accountType)) {
Log.w(TAG, "Account type in session bundle doesn't match request.");
}
// Add accountType info to session bundle. This will
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index be1fd58..927a7b6 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -252,6 +252,7 @@
import static android.app.ActivityManager.StackId.INVALID_STACK_ID;
import static android.app.ActivityManager.StackId.PINNED_STACK_ID;
import static android.content.pm.PackageManager.FEATURE_FREEFORM_WINDOW_MANAGEMENT;
+import static android.content.pm.PackageManager.FEATURE_PICTURE_IN_PICTURE;
import static android.content.pm.PackageManager.PERMISSION_GRANTED;
import static android.provider.Settings.Global.ALWAYS_FINISH_ACTIVITIES;
import static android.provider.Settings.Global.DEBUG_APP;
@@ -491,7 +492,10 @@
// Used to indicate that a task is removed it should also be removed from recents.
private static final boolean REMOVE_FROM_RECENTS = true;
// Used to indicate that an app transition should be animated.
- private static final boolean ANIMATE = true;
+ static final boolean ANIMATE = true;
+
+ // Determines whether to take full screen screenshots
+ static final boolean TAKE_FULLSCREEN_SCREENSHOTS = true;
private static native int nativeMigrateToBoost();
private static native int nativeMigrateFromBoost();
@@ -1276,7 +1280,7 @@
boolean mAlwaysFinishActivities = false;
boolean mForceResizableActivities;
boolean mSupportsFreeformWindowManagement;
- boolean mTakeFullscreenScreenshots;
+ boolean mSupportsPictureInPicture;
IActivityController mController = null;
String mProfileApp = null;
ProcessRecord mProfileProc = null;
@@ -1434,6 +1438,7 @@
static final int CONTENT_PROVIDER_PUBLISH_TIMEOUT_MSG = 59;
static final int IDLE_UIDS_MSG = 60;
static final int SYSTEM_USER_UNLOCK_MSG = 61;
+ static final int LOG_STACK_STATE = 62;
static final int FIRST_ACTIVITY_STACK_MSG = 100;
static final int FIRST_BROADCAST_QUEUE_MSG = 200;
@@ -2143,6 +2148,11 @@
case IDLE_UIDS_MSG: {
idleUids();
} break;
+ case LOG_STACK_STATE: {
+ synchronized (ActivityManagerService.this) {
+ mStackSupervisor.logStackState();
+ }
+ } break;
}
}
};
@@ -3668,9 +3678,9 @@
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
intent.setComponent(new ComponentName(
ri.activityInfo.packageName, ri.activityInfo.name));
- mStackSupervisor.startActivityLocked(null, intent, null, ri.activityInfo,
- null, null, null, null, 0, 0, 0, null, 0, 0, 0, null, false, false,
- null, null, null);
+ mStackSupervisor.startActivityLocked(null, intent, null /*ephemeralIntent*/,
+ null, ri.activityInfo, null /*rInfo*/, null, null, null, null, 0, 0, 0,
+ null, 0, 0, 0, null, false, false, null, null, null);
}
}
}
@@ -4279,9 +4289,10 @@
final long origId = Binder.clearCallingIdentity();
int res = mStackSupervisor.startActivityLocked(r.app.thread, intent,
- r.resolvedType, aInfo, null, null, resultTo != null ? resultTo.appToken : null,
- resultWho, requestCode, -1, r.launchedFromUid, r.launchedFromPackage,
- -1, r.launchedFromUid, 0, options, false, false, null, null, null);
+ null /*ephemeralIntent*/, r.resolvedType, aInfo, null /*rInfo*/, null,
+ null, resultTo != null ? resultTo.appToken : null, resultWho, requestCode, -1,
+ r.launchedFromUid, r.launchedFromPackage, -1, r.launchedFromUid, 0, options,
+ false, false, null, null, null);
Binder.restoreCallingIdentity(origId);
r.finishing = wasFinishing;
@@ -9391,6 +9402,11 @@
enforceCallingPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS,
"moveTopActivityToPinnedStack()");
synchronized (this) {
+ if (!mSupportsPictureInPicture) {
+ throw new IllegalStateException("moveTopActivityToPinnedStack:"
+ + "Device doesn't support picture-in-pciture mode");
+ }
+
long ident = Binder.clearCallingIdentity();
try {
return mStackSupervisor.moveTopStackActivityToPinnedStackLocked(stackId, bounds);
@@ -10902,6 +10918,7 @@
/** Notifies all listeners when the task stack has changed. */
void notifyTaskStackChangedLocked() {
+ mHandler.sendEmptyMessage(LOG_STACK_STATE);
mHandler.removeMessages(NOTIFY_TASK_STACK_CHANGE_LISTENERS_MSG);
Message nmsg = mHandler.obtainMessage(NOTIFY_TASK_STACK_CHANGE_LISTENERS_MSG);
mHandler.sendMessageDelayed(nmsg, NOTIFY_TASK_STACK_CHANGE_LISTENERS_DELAY);
@@ -12004,15 +12021,13 @@
final ContentResolver resolver = mContext.getContentResolver();
final boolean freeformWindowManagement =
mContext.getPackageManager().hasSystemFeature(FEATURE_FREEFORM_WINDOW_MANAGEMENT);
+ final boolean supportsPictureInPicture =
+ mContext.getPackageManager().hasSystemFeature(FEATURE_PICTURE_IN_PICTURE);
final String debugApp = Settings.Global.getString(resolver, DEBUG_APP);
- final String fsScreenshots = Settings.Secure.getString(resolver,
- "overview_fullscreen_thumbnails");
final boolean waitForDebugger = Settings.Global.getInt(resolver, WAIT_FOR_DEBUGGER, 0) != 0;
final boolean alwaysFinishActivities =
Settings.Global.getInt(resolver, ALWAYS_FINISH_ACTIVITIES, 0) != 0;
- final boolean takeFullscreenScreenshots = fsScreenshots != null &&
- Integer.parseInt(fsScreenshots) != 0;
final boolean forceRtl = Settings.Global.getInt(resolver, DEVELOPMENT_FORCE_RTL, 0) != 0;
final int defaultForceResizable = Build.IS_DEBUGGABLE ? 1 : 0;
final boolean forceResizable = Settings.Global.getInt(
@@ -12033,21 +12048,21 @@
mAlwaysFinishActivities = alwaysFinishActivities;
mForceResizableActivities = forceResizable;
mSupportsFreeformWindowManagement = freeformWindowManagement || forceResizable;
- mTakeFullscreenScreenshots = takeFullscreenScreenshots;
+ mSupportsPictureInPicture = supportsPictureInPicture || forceResizable;
// This happens before any activities are started, so we can
// change mConfiguration in-place.
updateConfigurationLocked(configuration, null, true);
if (DEBUG_CONFIGURATION) Slog.v(TAG_CONFIGURATION,
"Initial config: " + mConfiguration);
- }
- }
- /** Loads resources after the current configuration has been set. */
- private void loadResourcesOnSystemReady() {
- final Resources res = mContext.getResources();
- mHasRecents = res.getBoolean(com.android.internal.R.bool.config_hasRecents);
- mThumbnailWidth = res.getDimensionPixelSize(com.android.internal.R.dimen.thumbnail_width);
- mThumbnailHeight = res.getDimensionPixelSize(com.android.internal.R.dimen.thumbnail_height);
+ // Load resources only after the current configuration has been set.
+ final Resources res = mContext.getResources();
+ mHasRecents = res.getBoolean(com.android.internal.R.bool.config_hasRecents);
+ mThumbnailWidth = res.getDimensionPixelSize(
+ com.android.internal.R.dimen.thumbnail_width);
+ mThumbnailHeight = res.getDimensionPixelSize(
+ com.android.internal.R.dimen.thumbnail_height);
+ }
}
public boolean testIsSystemReady() {
@@ -12363,7 +12378,6 @@
}
retrieveSettings();
- loadResourcesOnSystemReady();
final int currentUserId;
synchronized (this) {
currentUserId = mUserController.getCurrentUserIdLocked();
diff --git a/services/core/java/com/android/server/am/ActivityMetricsLogger.java b/services/core/java/com/android/server/am/ActivityMetricsLogger.java
new file mode 100644
index 0000000..64f423c
--- /dev/null
+++ b/services/core/java/com/android/server/am/ActivityMetricsLogger.java
@@ -0,0 +1,72 @@
+package com.android.server.am;
+
+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 android.app.ActivityManager.StackId;
+import android.content.Context;
+import android.os.SystemClock;
+
+import com.android.internal.logging.MetricsLogger;
+
+/**
+ * Handles logging into Tron.
+ */
+class ActivityMetricsLogger {
+ // Window modes we are interested in logging. If we ever introduce a new type, we need to add
+ // a value here and increase the {@link #TRON_WINDOW_STATE_VARZ_STRINGS} array.
+ private static final int WINDOW_STATE_STANDARD = 0;
+ private static final int WINDOW_STATE_SIDE_BY_SIDE = 1;
+ private static final int WINDOW_STATE_FREEFORM = 2;
+ private static final int WINDOW_STATE_INVALID = -1;
+
+ // Preallocated strings we are sending to tron, so we don't have to allocate a new one every
+ // time we log.
+ private static final String[] TRON_WINDOW_STATE_VARZ_STRINGS = {
+ "tron_varz_window_time_0", "tron_varz_window_time_1", "tron_varz_window_time_2"};
+
+ private int mWindowState = WINDOW_STATE_STANDARD;
+ private long mLastLogTimeSecs;
+ private final ActivityStackSupervisor mSupervisor;
+ private final Context mContext;
+
+ ActivityMetricsLogger(ActivityStackSupervisor supervisor, Context context) {
+ mLastLogTimeSecs = SystemClock.elapsedRealtime() / 1000;
+ mSupervisor = supervisor;
+ mContext = context;
+ }
+
+ void logWindowState() {
+ final long now = SystemClock.elapsedRealtime() / 1000;
+ if (mWindowState != WINDOW_STATE_INVALID) {
+ // We log even if the window state hasn't changed, because the user might remain in
+ // home/fullscreen move forever and we would like to track this kind of behavior
+ // too.
+ MetricsLogger.count(mContext, TRON_WINDOW_STATE_VARZ_STRINGS[mWindowState],
+ (int) (now - mLastLogTimeSecs));
+ }
+ mLastLogTimeSecs = now;
+
+ mWindowState = WINDOW_STATE_INVALID;
+ ActivityStack stack = mSupervisor.getStack(DOCKED_STACK_ID);
+ if (stack != null && stack.isStackVisibleLocked()) {
+ mWindowState = WINDOW_STATE_SIDE_BY_SIDE;
+ }
+ if (mWindowState == WINDOW_STATE_INVALID) {
+ stack = mSupervisor.getFocusedStack();
+ if (stack.mStackId == HOME_STACK_ID
+ || stack.mStackId == FULLSCREEN_WORKSPACE_STACK_ID) {
+ mWindowState = WINDOW_STATE_STANDARD;
+ } else if (stack.mStackId == DOCKED_STACK_ID) {
+ throw new IllegalStateException("Docked stack shouldn't be the focused stack, "
+ + "because it reported not being visible.");
+ } else if (stack.mStackId == FREEFORM_WORKSPACE_STACK_ID) {
+ mWindowState = WINDOW_STATE_FREEFORM;
+ } else if (StackId.isStaticStack(stack.mStackId)) {
+ throw new IllegalStateException("Unknown stack=" + stack);
+ }
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/am/ActivityStack.java b/services/core/java/com/android/server/am/ActivityStack.java
index ca9f28e..bfd17b2 100644
--- a/services/core/java/com/android/server/am/ActivityStack.java
+++ b/services/core/java/com/android/server/am/ActivityStack.java
@@ -862,7 +862,7 @@
// When this flag is set, we currently take the fullscreen screenshot of the activity
// but scaled to half the size. This gives us a "good-enough" fullscreen thumbnail to
// use within SystemUI while keeping memory usage low.
- if (mService.mTakeFullscreenScreenshots) {
+ if (ActivityManagerService.TAKE_FULLSCREEN_SCREENSHOTS) {
w = h = -1;
scale = 0.5f;
}
@@ -1334,7 +1334,7 @@
}
/** Returns true if the stack is considered visible. */
- private boolean isStackVisibleLocked() {
+ boolean isStackVisibleLocked() {
if (!isAttached()) {
return false;
}
@@ -3349,9 +3349,10 @@
ActivityInfo aInfo = AppGlobals.getPackageManager().getActivityInfo(
destIntent.getComponent(), 0, srec.userId);
int res = mStackSupervisor.startActivityLocked(srec.app.thread, destIntent,
- null, aInfo, null, null, parent.appToken, null,
- 0, -1, parent.launchedFromUid, parent.launchedFromPackage,
- -1, parent.launchedFromUid, 0, null, false, true, null, null, null);
+ null /*ephemeralIntent*/, null, aInfo, null /*rInfo*/, null, null,
+ parent.appToken, null, 0, -1, parent.launchedFromUid,
+ parent.launchedFromPackage, -1, parent.launchedFromUid, 0, null,
+ false, true, null, null, null);
foundParentInTask = res == ActivityManager.START_SUCCESS;
} catch (RemoteException e) {
foundParentInTask = false;
diff --git a/services/core/java/com/android/server/am/ActivityStackSupervisor.java b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
index 9fff0c8..9117806 100644
--- a/services/core/java/com/android/server/am/ActivityStackSupervisor.java
+++ b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
@@ -17,7 +17,11 @@
package com.android.server.am;
import static android.Manifest.permission.START_ANY_ACTIVITY;
-import static android.app.ActivityManager.*;
+import static android.app.ActivityManager.LOCK_TASK_MODE_LOCKED;
+import static android.app.ActivityManager.LOCK_TASK_MODE_NONE;
+import static android.app.ActivityManager.LOCK_TASK_MODE_PINNED;
+import static android.app.ActivityManager.RESIZE_MODE_FORCED;
+import static android.app.ActivityManager.RESIZE_MODE_SYSTEM;
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;
@@ -29,18 +33,61 @@
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_LAUNCH_TO_SIDE;
+import static android.content.Intent.FLAG_ACTIVITY_MULTIPLE_TASK;
import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
import static android.content.Intent.FLAG_ACTIVITY_TASK_ON_HOME;
import static android.content.pm.ActivityInfo.FLAG_SHOW_FOR_ALL_USERS;
import static android.content.pm.PackageManager.PERMISSION_GRANTED;
import static android.os.Trace.TRACE_TAG_ACTIVITY_MANAGER;
-import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_SHOW_FOR_ALL_USERS;
-import static com.android.server.am.ActivityManagerDebugConfig.*;
+import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_ALL;
+import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_CONFIGURATION;
+import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_CONTAINERS;
+import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_FOCUS;
+import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_IDLE;
+import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_LOCKSCREEN;
+import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_LOCKTASK;
+import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_PAUSE;
+import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_PERMISSIONS_REVIEW;
+import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_RECENTS;
+import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_RELEASE;
+import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_RESULTS;
+import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_STACK;
+import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_STATES;
+import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_SWITCH;
+import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_TASKS;
+import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_USER_LEAVING;
+import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_VISIBLE_BEHIND;
+import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_CONFIGURATION;
+import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_CONTAINERS;
+import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_FOCUS;
+import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_IDLE;
+import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_LOCKTASK;
+import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_PAUSE;
+import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_RECENTS;
+import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_RELEASE;
+import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_RESULTS;
+import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_STACK;
+import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_STATES;
+import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_SWITCH;
+import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_TASKS;
+import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_USER_LEAVING;
+import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_VISIBLE_BEHIND;
+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.ANIMATE;
import static com.android.server.am.ActivityManagerService.FIRST_SUPERVISOR_STACK_MSG;
+import static com.android.server.am.ActivityRecord.APPLICATION_ACTIVITY_TYPE;
import static com.android.server.am.ActivityRecord.HOME_ACTIVITY_TYPE;
import static com.android.server.am.ActivityRecord.RECENTS_ACTIVITY_TYPE;
-import static com.android.server.am.ActivityRecord.APPLICATION_ACTIVITY_TYPE;
-import static com.android.server.am.ActivityStack.ActivityState.*;
+import static com.android.server.am.ActivityStack.ActivityState.DESTROYED;
+import static com.android.server.am.ActivityStack.ActivityState.DESTROYING;
+import static com.android.server.am.ActivityStack.ActivityState.INITIALIZING;
+import static com.android.server.am.ActivityStack.ActivityState.PAUSED;
+import static com.android.server.am.ActivityStack.ActivityState.PAUSING;
+import static com.android.server.am.ActivityStack.ActivityState.RESUMED;
+import static com.android.server.am.ActivityStack.ActivityState.STOPPED;
+import static com.android.server.am.ActivityStack.ActivityState.STOPPING;
import static com.android.server.am.TaskRecord.LOCK_TASK_AUTH_DONT_LOCK;
import static com.android.server.am.TaskRecord.LOCK_TASK_AUTH_LAUNCHABLE;
import static com.android.server.am.TaskRecord.LOCK_TASK_AUTH_LAUNCHABLE_PRIV;
@@ -50,6 +97,7 @@
import android.Manifest;
import android.app.Activity;
import android.app.ActivityManager;
+import android.app.ActivityManager.RunningTaskInfo;
import android.app.ActivityManager.StackId;
import android.app.ActivityManager.StackInfo;
import android.app.ActivityOptions;
@@ -58,12 +106,11 @@
import android.app.IActivityContainer;
import android.app.IActivityContainerCallback;
import android.app.IActivityManager;
+import android.app.IActivityManager.WaitResult;
import android.app.IApplicationThread;
import android.app.KeyguardManager;
import android.app.PendingIntent;
import android.app.ProfilerInfo;
-import android.app.ActivityManager.RunningTaskInfo;
-import android.app.IActivityManager.WaitResult;
import android.app.ResultInfo;
import android.app.StatusBarManager;
import android.app.admin.IDevicePolicyManager;
@@ -76,7 +123,6 @@
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
-import android.content.pm.PackageManagerInternal;
import android.content.pm.ResolveInfo;
import android.content.pm.UserInfo;
import android.content.res.Configuration;
@@ -107,7 +153,6 @@
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;
@@ -117,19 +162,16 @@
import android.util.EventLog;
import android.util.Slog;
import android.util.SparseArray;
-
import android.util.SparseIntArray;
import android.view.Display;
import android.view.DisplayInfo;
import android.view.InputEvent;
import android.view.Surface;
-import android.widget.Toast;
import com.android.internal.app.HeavyWeightSwitcherActivity;
import com.android.internal.app.IVoiceInteractor;
import com.android.internal.content.ReferrerIntent;
import com.android.internal.os.TransferPipe;
-import com.android.internal.R;
import com.android.internal.statusbar.IStatusBarService;
import com.android.internal.util.ArrayUtils;
import com.android.internal.widget.LockPatternUtils;
@@ -374,6 +416,8 @@
// Whether tasks have moved and we need to rank the tasks before next OOM scoring
private boolean mTaskLayersChanged = true;
+ private final ActivityMetricsLogger mActivityMetricsLogger;
+
/**
* Description of a request to start a new activity, which has been held
* due to app switches being disabled.
@@ -410,6 +454,7 @@
mService = service;
mRecentTasks = recentTasks;
mHandler = new ActivityStackSupervisorHandler(mService.mHandler.getLooper());
+ mActivityMetricsLogger = new ActivityMetricsLogger(this, mService.mContext);
}
/**
@@ -921,21 +966,9 @@
}
}
- ActivityInfo resolveActivity(Intent intent, String resolvedType, int startFlags,
- ProfilerInfo profilerInfo, int userId) {
- // Collect information about the target of the Intent.
- ActivityInfo aInfo;
- try {
- ResolveInfo rInfo =
- AppGlobals.getPackageManager().resolveIntent(
- intent, resolvedType,
- PackageManager.MATCH_DEFAULT_ONLY
- | ActivityManagerService.STOCK_PM_FLAGS, userId);
- aInfo = rInfo != null ? rInfo.activityInfo : null;
- } catch (RemoteException e) {
- aInfo = null;
- }
-
+ ActivityInfo resolveActivity(Intent intent, ResolveInfo rInfo, int startFlags,
+ ProfilerInfo profilerInfo) {
+ final ActivityInfo aInfo = rInfo != null ? rInfo.activityInfo : null;
if (aInfo != null) {
// Store the found target back into the intent, because now that
// we have it we never want to do this again. For example, if the
@@ -962,15 +995,31 @@
return aInfo;
}
+ ResolveInfo resolveIntent(Intent intent, String resolvedType, int userId) {
+ try {
+ return AppGlobals.getPackageManager().resolveIntent(intent, resolvedType,
+ PackageManager.MATCH_DEFAULT_ONLY
+ | ActivityManagerService.STOCK_PM_FLAGS, userId);
+ } catch (RemoteException e) {
+ }
+ return null;
+ }
+
+ ActivityInfo resolveActivity(Intent intent, String resolvedType, int startFlags,
+ ProfilerInfo profilerInfo, int userId) {
+ final ResolveInfo rInfo = resolveIntent(intent, resolvedType, userId);
+ return resolveActivity(intent, rInfo, startFlags, profilerInfo);
+ }
+
void startHomeActivity(Intent intent, ActivityInfo aInfo, String reason) {
moveHomeStackTaskToTop(HOME_ACTIVITY_TYPE, reason);
- startActivityLocked(null /* caller */, intent, null /* resolvedType */, aInfo,
- null /* voiceSession */, null /* voiceInteractor */, null /* resultTo */,
- null /* resultWho */, 0 /* requestCode */, 0 /* callingPid */, 0 /* callingUid */,
- null /* callingPackage */, 0 /* realCallingPid */, 0 /* realCallingUid */,
- 0 /* startFlags */, null /* options */, false /* ignoreTargetSecurity */,
- false /* componentSpecified */,
- null /* outActivity */, null /* container */, null /* inTask */);
+ startActivityLocked(null /*caller*/, intent, null /*ephemeralIntent*/,
+ null /*resolvedType*/, aInfo, null /*rInfo*/, null /*voiceSession*/,
+ null /*voiceInteractor*/, null /*resultTo*/, null /*resultWho*/,
+ 0 /*requestCode*/, 0 /*callingPid*/, 0 /*callingUid*/, null /*callingPackage*/,
+ 0 /*realCallingPid*/, 0 /*realCallingUid*/, 0 /*startFlags*/, null /*options*/,
+ false /*ignoreTargetSecurity*/, false /*componentSpecified*/, null /*outActivity*/,
+ null /*container*/, null /*inTask*/);
if (inResumeTopActivity) {
// If we are in resume section already, home activity will be initialized, but not
// resumed (to avoid recursive resume) and will stay that way until something pokes it
@@ -992,12 +1041,14 @@
}
boolean componentSpecified = intent.getComponent() != null;
+ // Save a copy in case ephemeral needs it
+ final Intent ephemeralIntent = new Intent(intent);
// Don't modify the client's object!
intent = new Intent(intent);
+ ResolveInfo rInfo = resolveIntent(intent, resolvedType, userId);
// Collect information about the target of the Intent.
- ActivityInfo aInfo =
- resolveActivity(intent, resolvedType, startFlags, profilerInfo, userId);
+ ActivityInfo aInfo = resolveActivity(intent, rInfo, startFlags, profilerInfo);
ActivityOptions options = ActivityOptions.fromBundle(bOptions);
ActivityContainer container = (ActivityContainer)iContainer;
@@ -1085,26 +1136,20 @@
callingUid = Binder.getCallingUid();
callingPid = Binder.getCallingPid();
componentSpecified = true;
- try {
- ResolveInfo rInfo =
- AppGlobals.getPackageManager().resolveIntent(
- intent, null,
- PackageManager.MATCH_DEFAULT_ONLY
- | ActivityManagerService.STOCK_PM_FLAGS, userId);
- aInfo = rInfo != null ? rInfo.activityInfo : null;
+ rInfo = resolveIntent(intent, null /*resolvedType*/, userId);
+ aInfo = rInfo != null ? rInfo.activityInfo : null;
+ if (aInfo != null) {
aInfo = mService.getActivityInfoForUser(aInfo, userId);
- } catch (RemoteException e) {
- aInfo = null;
}
}
}
}
- int res = startActivityLocked(caller, intent, resolvedType, aInfo,
- voiceSession, voiceInteractor, resultTo, resultWho,
- requestCode, callingPid, callingUid, callingPackage,
- realCallingPid, realCallingUid, startFlags, options, ignoreTargetSecurity,
- componentSpecified, null, container, inTask);
+ int res = startActivityLocked(caller, intent, ephemeralIntent, resolvedType,
+ aInfo, rInfo, voiceSession, voiceInteractor,
+ resultTo, resultWho, requestCode, callingPid,
+ callingUid, callingPackage, realCallingPid, realCallingUid, startFlags,
+ options, ignoreTargetSecurity, componentSpecified, null, container, inTask);
Binder.restoreCallingIdentity(origId);
@@ -1212,10 +1257,10 @@
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, options, false, componentSpecified, outActivity, null, null);
+ int res = startActivityLocked(caller, intent, null /*ephemeralIntent*/,
+ resolvedTypes[i], aInfo, null /*rInfo*/, null, null, resultTo, null, -1,
+ callingPid, callingUid, callingPackage, callingPid, callingUid, 0,
+ options, false, componentSpecified, outActivity, null, null);
if (res < 0) {
return res;
}
@@ -1449,14 +1494,13 @@
"activity", r.intent.getComponent(), false, false, true);
}
- final int startActivityLocked(IApplicationThread caller,
- Intent intent, String resolvedType, ActivityInfo aInfo,
+ final int startActivityLocked(IApplicationThread caller, Intent intent, Intent ephemeralIntent,
+ String resolvedType, ActivityInfo aInfo, ResolveInfo rInfo,
IVoiceInteractionSession voiceSession, IVoiceInteractor voiceInteractor,
- IBinder resultTo, String resultWho, int requestCode,
- int callingPid, int callingUid, String callingPackage,
- int realCallingPid, int realCallingUid, int startFlags, ActivityOptions options,
- boolean ignoreTargetSecurity, boolean componentSpecified, ActivityRecord[] outActivity,
- ActivityContainer container, TaskRecord inTask) {
+ IBinder resultTo, String resultWho, int requestCode, int callingPid, int callingUid,
+ String callingPackage, int realCallingPid, int realCallingUid, int startFlags,
+ ActivityOptions options, boolean ignoreTargetSecurity, boolean componentSpecified,
+ ActivityRecord[] outActivity, ActivityContainer container, TaskRecord inTask) {
int err = ActivityManager.START_SUCCESS;
ProcessRecord callerApp = null;
@@ -1549,7 +1593,7 @@
// to ensure that it is safe to do so. If the upcoming activity will also
// be part of the voice session, we can only launch it if it has explicitly
// said it supports the VOICE category, or it is a part of the calling app.
- if ((launchFlags & Intent.FLAG_ACTIVITY_NEW_TASK) == 0
+ if ((launchFlags & FLAG_ACTIVITY_NEW_TASK) == 0
&& sourceRecord.info.applicationInfo.uid != aInfo.applicationInfo.uid) {
try {
intent.addCategory(Intent.CATEGORY_VOICE);
@@ -1596,63 +1640,9 @@
return err;
}
- boolean abort = false;
-
- final int startAnyPerm = mService.checkPermission(
- START_ANY_ACTIVITY, callingPid, callingUid);
-
- if (startAnyPerm != PERMISSION_GRANTED) {
- final int componentRestriction = getComponentRestrictionForCallingPackage(
- aInfo, callingPackage, callingPid, callingUid, ignoreTargetSecurity);
- final int actionRestriction = getActionRestrictionForCallingPackage(
- intent.getAction(), callingPackage, callingPid, callingUid);
-
- if (componentRestriction == ACTIVITY_RESTRICTION_PERMISSION
- || actionRestriction == ACTIVITY_RESTRICTION_PERMISSION) {
- if (resultRecord != null) {
- resultStack.sendActivityResultLocked(-1,
- resultRecord, resultWho, requestCode,
- Activity.RESULT_CANCELED, null);
- }
- String msg;
- if (actionRestriction == ACTIVITY_RESTRICTION_PERMISSION) {
- msg = "Permission Denial: starting " + intent.toString()
- + " from " + callerApp + " (pid=" + callingPid
- + ", uid=" + callingUid + ")" + " with revoked permission "
- + ACTION_TO_RUNTIME_PERMISSION.get(intent.getAction());
- } else if (!aInfo.exported) {
- msg = "Permission Denial: starting " + intent.toString()
- + " from " + callerApp + " (pid=" + callingPid
- + ", uid=" + callingUid + ")"
- + " not exported from uid " + aInfo.applicationInfo.uid;
- } else {
- msg = "Permission Denial: starting " + intent.toString()
- + " from " + callerApp + " (pid=" + callingPid
- + ", uid=" + callingUid + ")"
- + " requires " + aInfo.permission;
- }
- Slog.w(TAG, msg);
- throw new SecurityException(msg);
- }
-
- if (actionRestriction == ACTIVITY_RESTRICTION_APPOP) {
- String message = "Appop Denial: starting " + intent.toString()
- + " from " + callerApp + " (pid=" + callingPid
- + ", uid=" + callingUid + ")"
- + " requires " + AppOpsManager.permissionToOp(
- ACTION_TO_RUNTIME_PERMISSION.get(intent.getAction()));
- Slog.w(TAG, message);
- abort = true;
- } else if (componentRestriction == ACTIVITY_RESTRICTION_APPOP) {
- String message = "Appop Denial: starting " + intent.toString()
- + " from " + callerApp + " (pid=" + callingPid
- + ", uid=" + callingUid + ")"
- + " requires appop " + AppOpsManager.permissionToOp(aInfo.permission);
- Slog.w(TAG, message);
- abort = true;
- }
- }
-
+ boolean abort = !checkStartAnyActivityPermission(intent, aInfo, resultWho, requestCode,
+ callingPid, callingUid, callingPackage, ignoreTargetSecurity, callerApp,
+ resultRecord, resultStack);
abort |= !mService.mIntentFirewall.checkStartActivity(intent, callingUid,
callingPid, resolvedType, aInfo.applicationInfo);
@@ -1680,22 +1670,24 @@
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);
+ final int flags = intent.getFlags();
+ final Intent newIntent = km.createConfirmDeviceCredentialIntent(null, null, user.id);
+ if (newIntent != null) {
+ intent = newIntent;
+ intent.setFlags(flags
+ | 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));
- resolvedType = null;
- callingUid = realCallingUid;
- callingPid = realCallingPid;
+ 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);
+ UserInfo parent = UserManager.get(mService.mContext).getProfileParent(userId);
+ rInfo = resolveIntent(intent, resolvedType, parent.id);
+ aInfo = resolveActivity(intent, rInfo, startFlags, null /*profilerInfo*/);
+ }
}
if (abort) {
@@ -1721,22 +1713,23 @@
new String[]{resolvedType}, PendingIntent.FLAG_CANCEL_CURRENT
| PendingIntent.FLAG_ONE_SHOT, null);
+ final int flags = intent.getFlags();
Intent newIntent = new Intent(Intent.ACTION_REVIEW_PERMISSIONS);
- newIntent.addFlags(Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
+ newIntent.setFlags(flags
+ | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
newIntent.putExtra(Intent.EXTRA_PACKAGE_NAME, aInfo.packageName);
newIntent.putExtra(Intent.EXTRA_INTENT, new IntentSender(target));
if (resultRecord != null) {
newIntent.putExtra(Intent.EXTRA_RESULT_NEEDED, true);
}
- newIntent.setFlags(intent.getFlags());
intent = newIntent;
resolvedType = null;
callingUid = realCallingUid;
callingPid = realCallingPid;
- aInfo = resolveActivity(intent, null, PackageManager.MATCH_DEFAULT_ONLY
- | ActivityManagerService.STOCK_PM_FLAGS, null, userId);
+ rInfo = resolveIntent(intent, resolvedType, userId);
+ aInfo = resolveActivity(intent, rInfo, startFlags, null /*profilerInfo*/);
if (DEBUG_PERMISSIONS_REVIEW) {
Slog.i(TAG, "START u" + userId + " {" + intent.toShortString(true, true,
@@ -1749,6 +1742,47 @@
}
}
+ // If we have an ephemeral app, abort the process of launching the resolved intent.
+ // Instead, launch the ephemeral installer. Once the installer is finished, it
+ // starts either the intent we resolved here [on install error] or the ephemeral
+ // app [on install success].
+ if (rInfo != null && rInfo.ephemeralResolveInfo != null) {
+ // Create a pending intent to start the intent resolved here.
+ final IIntentSender failureTarget = 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);
+
+ // Create a pending intent to start the ephemeral application; force it to be
+ // directed to the ephemeral package.
+ ephemeralIntent.setPackage(rInfo.ephemeralResolveInfo.getPackageName());
+ final IIntentSender ephemeralTarget = mService.getIntentSenderLocked(
+ ActivityManager.INTENT_SENDER_ACTIVITY, callingPackage,
+ Binder.getCallingUid(), userId, null, null, 0, new Intent[]{ ephemeralIntent },
+ new String[]{ resolvedType },
+ PendingIntent.FLAG_CANCEL_CURRENT | PendingIntent.FLAG_ONE_SHOT
+ | PendingIntent.FLAG_IMMUTABLE, null);
+
+ int flags = intent.getFlags();
+ intent = new Intent();
+ intent.setFlags(flags
+ | Intent.FLAG_ACTIVITY_NEW_TASK
+ | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
+ intent.putExtra(Intent.EXTRA_PACKAGE_NAME,
+ rInfo.ephemeralResolveInfo.getPackageName());
+ intent.putExtra(Intent.EXTRA_EPHEMERAL_FAILURE, new IntentSender(failureTarget));
+ intent.putExtra(Intent.EXTRA_EPHEMERAL_SUCCESS, new IntentSender(ephemeralTarget));
+
+ resolvedType = null;
+ callingUid = realCallingUid;
+ callingPid = realCallingPid;
+
+ rInfo = rInfo.ephemeralInstaller;
+ aInfo = resolveActivity(intent, rInfo, startFlags, null /*profilerInfo*/);
+ }
+
ActivityRecord r = new ActivityRecord(mService, callerApp, callingUid, callingPackage,
intent, resolvedType, aInfo, mService.mConfiguration, resultRecord, resultWho,
requestCode, componentSpecified, voiceSession != null, this, container, options);
@@ -1801,6 +1835,66 @@
return err;
}
+ private boolean checkStartAnyActivityPermission(Intent intent, ActivityInfo aInfo,
+ String resultWho, int requestCode, int callingPid, int callingUid,
+ String callingPackage, boolean ignoreTargetSecurity, ProcessRecord callerApp,
+ ActivityRecord resultRecord, ActivityStack resultStack) {
+ final int startAnyPerm = mService.checkPermission(START_ANY_ACTIVITY, callingPid,
+ callingUid);
+ if (startAnyPerm == PERMISSION_GRANTED) {
+ return true;
+ }
+ final int componentRestriction = getComponentRestrictionForCallingPackage(
+ aInfo, callingPackage, callingPid, callingUid, ignoreTargetSecurity);
+ final int actionRestriction = getActionRestrictionForCallingPackage(
+ intent.getAction(), callingPackage, callingPid, callingUid);
+ if (componentRestriction == ACTIVITY_RESTRICTION_PERMISSION
+ || actionRestriction == ACTIVITY_RESTRICTION_PERMISSION) {
+ if (resultRecord != null) {
+ resultStack.sendActivityResultLocked(-1,
+ resultRecord, resultWho, requestCode,
+ Activity.RESULT_CANCELED, null);
+ }
+ final String msg;
+ if (actionRestriction == ACTIVITY_RESTRICTION_PERMISSION) {
+ msg = "Permission Denial: starting " + intent.toString()
+ + " from " + callerApp + " (pid=" + callingPid
+ + ", uid=" + callingUid + ")" + " with revoked permission "
+ + ACTION_TO_RUNTIME_PERMISSION.get(intent.getAction());
+ } else if (!aInfo.exported) {
+ msg = "Permission Denial: starting " + intent.toString()
+ + " from " + callerApp + " (pid=" + callingPid
+ + ", uid=" + callingUid + ")"
+ + " not exported from uid " + aInfo.applicationInfo.uid;
+ } else {
+ msg = "Permission Denial: starting " + intent.toString()
+ + " from " + callerApp + " (pid=" + callingPid
+ + ", uid=" + callingUid + ")"
+ + " requires " + aInfo.permission;
+ }
+ Slog.w(TAG, msg);
+ throw new SecurityException(msg);
+ }
+
+ if (actionRestriction == ACTIVITY_RESTRICTION_APPOP) {
+ final String message = "Appop Denial: starting " + intent.toString()
+ + " from " + callerApp + " (pid=" + callingPid
+ + ", uid=" + callingUid + ")"
+ + " requires " + AppOpsManager.permissionToOp(
+ ACTION_TO_RUNTIME_PERMISSION.get(intent.getAction()));
+ Slog.w(TAG, message);
+ return false;
+ } else if (componentRestriction == ACTIVITY_RESTRICTION_APPOP) {
+ final String message = "Appop Denial: starting " + intent.toString()
+ + " from " + callerApp + " (pid=" + callingPid
+ + ", uid=" + callingUid + ")"
+ + " requires appop " + AppOpsManager.permissionToOp(aInfo.permission);
+ Slog.w(TAG, message);
+ return false;
+ }
+ return true;
+ }
+
private UserInfo getUserInfo(int userId) {
final long identity = Binder.clearCallingIdentity();
try {
@@ -1879,14 +1973,17 @@
return ACTIVITY_RESTRICTION_NONE;
}
- ActivityStack computeStackFocus(ActivityRecord r, boolean newTask, Rect bounds) {
+ private ActivityStack computeStackFocus(ActivityRecord r, boolean newTask, Rect bounds,
+ int launchFlags) {
final TaskRecord task = r.task;
-
if (!(r.isApplicationActivity() || (task != null && task.isApplicationTask()))) {
return mHomeStack;
}
- ActivityStack stack;
+ ActivityStack stack = getLaunchToSideStack(r, launchFlags, task);
+ if (stack != null) {
+ return stack;
+ }
if (task != null && task.stack != null) {
stack = task.stack;
@@ -1894,10 +1991,10 @@
if (mFocusedStack != stack) {
if (DEBUG_FOCUS || DEBUG_STACK) Slog.d(TAG_FOCUS,
"computeStackFocus: Setting " + "focused stack to r=" + r
- + " task=" + task);
+ + " task=" + task);
} else {
if (DEBUG_FOCUS || DEBUG_STACK) Slog.d(TAG_FOCUS,
- "computeStackFocus: Focused stack already=" + mFocusedStack);
+ "computeStackFocus: Focused stack already=" + mFocusedStack);
}
}
return stack;
@@ -1912,11 +2009,13 @@
// 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,
+ // If the freeform or docked stack has focus, and the activity to be launched is resizeable,
// we can also put it in the focused stack.
+ final int focusedStackId = mFocusedStack.mStackId;
final boolean canUseFocusedStack =
- mFocusedStack.mStackId == FULLSCREEN_WORKSPACE_STACK_ID
- || mFocusedStack.mStackId == FREEFORM_WORKSPACE_STACK_ID && r.info.resizeable;
+ focusedStackId == FULLSCREEN_WORKSPACE_STACK_ID
+ || focusedStackId == DOCKED_STACK_ID
+ || (focusedStackId == FREEFORM_WORKSPACE_STACK_ID && r.info.resizeable);
if (canUseFocusedStack
&& (!newTask || mFocusedStack.mActivityContainer.isEligibleForNewTasks())) {
if (DEBUG_FOCUS || DEBUG_STACK) Slog.d(TAG_FOCUS,
@@ -1945,6 +2044,33 @@
return stack;
}
+ private ActivityStack getLaunchToSideStack(ActivityRecord r, int launchFlags, TaskRecord task) {
+ if ((launchFlags & FLAG_ACTIVITY_LAUNCH_TO_SIDE) == 0) {
+ return null;
+ }
+ // The parent activity doesn't want to launch the activity on top of itself, but
+ // instead tries to put it onto other side in side-by-side mode.
+ final ActivityStack parentStack = task != null ? task.stack
+ : r.mInitialActivityContainer != null ? r.mInitialActivityContainer.mStack
+ : mFocusedStack;
+ if (parentStack != null && parentStack.mStackId == DOCKED_STACK_ID) {
+ // If parent was in docked stack, the natural place to launch another activity
+ // will be fullscreen, so it can appear alongside the docked window.
+ return getStack(FULLSCREEN_WORKSPACE_STACK_ID, CREATE_IF_NEEDED, ON_TOP);
+ } else {
+ // If the parent is not in the docked stack, we check if there is docked window
+ // and if yes, we will launch into that stack. If not, we just put the new
+ // activity into parent's stack, because we can't find a better place.
+ final ActivityStack stack = getStack(DOCKED_STACK_ID);
+ if (stack != null && !stack.isStackVisibleLocked()) {
+ // There is a docked stack, but it isn't visible, so we can't launch into that.
+ return null;
+ } else {
+ return stack;
+ }
+ }
+ }
+
boolean setFocusedStack(ActivityRecord r, String reason) {
if (r == null) {
// Not sure what you are trying to do, but it is not going to work...
@@ -1994,7 +2120,7 @@
Slog.i(TAG, "Ignoring FLAG_ACTIVITY_NEW_DOCUMENT, launchMode is " +
"\"singleInstance\" or \"singleTask\"");
launchFlags &=
- ~(Intent.FLAG_ACTIVITY_NEW_DOCUMENT | Intent.FLAG_ACTIVITY_MULTIPLE_TASK);
+ ~(Intent.FLAG_ACTIVITY_NEW_DOCUMENT | FLAG_ACTIVITY_MULTIPLE_TASK);
} else {
switch (r.info.documentLaunchMode) {
case ActivityInfo.DOCUMENT_LAUNCH_NONE:
@@ -2006,7 +2132,7 @@
launchFlags |= Intent.FLAG_ACTIVITY_NEW_DOCUMENT;
break;
case ActivityInfo.DOCUMENT_LAUNCH_NEVER:
- launchFlags &= ~Intent.FLAG_ACTIVITY_MULTIPLE_TASK;
+ launchFlags &= ~FLAG_ACTIVITY_MULTIPLE_TASK;
break;
}
}
@@ -2015,7 +2141,7 @@
&& !launchSingleTask && !launchSingleInstance
&& (launchFlags & Intent.FLAG_ACTIVITY_NEW_DOCUMENT) != 0;
- if (r.resultTo != null && (launchFlags & Intent.FLAG_ACTIVITY_NEW_TASK) != 0
+ if (r.resultTo != null && (launchFlags & FLAG_ACTIVITY_NEW_TASK) != 0
&& r.resultTo.task.stack != null) {
// For whatever reason this activity is being launched into a new
// task... yet the caller has requested a result back. Well, that
@@ -2030,15 +2156,15 @@
}
if ((launchFlags & Intent.FLAG_ACTIVITY_NEW_DOCUMENT) != 0 && r.resultTo == null) {
- launchFlags |= Intent.FLAG_ACTIVITY_NEW_TASK;
+ launchFlags |= FLAG_ACTIVITY_NEW_TASK;
}
// If we are actually going to launch in to a new task, there are some cases where
// we further want to do multiple task.
- if ((launchFlags & Intent.FLAG_ACTIVITY_NEW_TASK) != 0) {
+ if ((launchFlags & FLAG_ACTIVITY_NEW_TASK) != 0) {
if (launchTaskBehind
|| r.info.documentLaunchMode == ActivityInfo.DOCUMENT_LAUNCH_ALWAYS) {
- launchFlags |= Intent.FLAG_ACTIVITY_MULTIPLE_TASK;
+ launchFlags |= FLAG_ACTIVITY_MULTIPLE_TASK;
}
}
@@ -2107,8 +2233,8 @@
// If task is empty, then adopt the interesting intent launch flags in to the
// activity being started.
if (root == null) {
- final int flagsOfInterest = Intent.FLAG_ACTIVITY_NEW_TASK
- | Intent.FLAG_ACTIVITY_MULTIPLE_TASK | Intent.FLAG_ACTIVITY_NEW_DOCUMENT
+ final int flagsOfInterest = FLAG_ACTIVITY_NEW_TASK
+ | FLAG_ACTIVITY_MULTIPLE_TASK | Intent.FLAG_ACTIVITY_NEW_DOCUMENT
| Intent.FLAG_ACTIVITY_RETAIN_IN_RECENTS;
launchFlags = (launchFlags&~flagsOfInterest)
| (baseIntent.getFlags()&flagsOfInterest);
@@ -2119,7 +2245,7 @@
// If the task is not empty and the caller is asking to start it as the root
// of a new task, then we don't actually want to start this on the task. We
// will bring the task to the front, and possibly give it a new intent.
- } else if ((launchFlags & Intent.FLAG_ACTIVITY_NEW_TASK) != 0) {
+ } else if ((launchFlags & FLAG_ACTIVITY_NEW_TASK) != 0) {
addingToTask = false;
} else {
@@ -2143,26 +2269,26 @@
if (sourceRecord == null) {
// This activity is not being started from another... in this
// case we -always- start a new task.
- if ((launchFlags & Intent.FLAG_ACTIVITY_NEW_TASK) == 0 && inTask == null) {
+ if ((launchFlags & FLAG_ACTIVITY_NEW_TASK) == 0 && inTask == null) {
Slog.w(TAG, "startActivity called from non-Activity context; forcing " +
"Intent.FLAG_ACTIVITY_NEW_TASK for: " + intent);
- launchFlags |= Intent.FLAG_ACTIVITY_NEW_TASK;
+ launchFlags |= FLAG_ACTIVITY_NEW_TASK;
}
} else if (sourceRecord.launchMode == ActivityInfo.LAUNCH_SINGLE_INSTANCE) {
// The original activity who is starting us is running as a single
// instance... this new activity it is starting must go on its
// own task.
- launchFlags |= Intent.FLAG_ACTIVITY_NEW_TASK;
+ launchFlags |= FLAG_ACTIVITY_NEW_TASK;
} else if (launchSingleInstance || launchSingleTask) {
// The activity being started is a single instance... it always
// gets launched into its own task.
- launchFlags |= Intent.FLAG_ACTIVITY_NEW_TASK;
+ launchFlags |= FLAG_ACTIVITY_NEW_TASK;
}
}
ActivityInfo newTaskInfo = null;
Intent newTaskIntent = null;
- ActivityStack sourceStack;
+ final ActivityStack sourceStack;
if (sourceRecord != null) {
if (sourceRecord.finishing) {
// If the source is finishing, we can't further count it as our source. This
@@ -2170,10 +2296,10 @@
// so we don't want to blindly throw it in to that task. Instead we will take
// the NEW_TASK flow and try to find a task for it. But save the task information
// so it can be used when creating the new task.
- if ((launchFlags & Intent.FLAG_ACTIVITY_NEW_TASK) == 0) {
+ if ((launchFlags & FLAG_ACTIVITY_NEW_TASK) == 0) {
Slog.w(TAG, "startActivity called from finishing " + sourceRecord
+ "; forcing " + "Intent.FLAG_ACTIVITY_NEW_TASK for: " + intent);
- launchFlags |= Intent.FLAG_ACTIVITY_NEW_TASK;
+ launchFlags |= FLAG_ACTIVITY_NEW_TASK;
newTaskInfo = sourceRecord.info;
newTaskIntent = sourceRecord.task.intent;
}
@@ -2192,216 +2318,199 @@
intent.setFlags(launchFlags);
final boolean noAnimation = (launchFlags & Intent.FLAG_ACTIVITY_NO_ANIMATION) != 0;
- // We may want to try to place the new activity in to an existing task. We always
- // do this if the target activity is singleTask or singleInstance; we will also do
- // this if NEW_TASK has been requested, and there is not an additional qualifier telling
- // us to still place it in a new task: multi task, always doc mode, or being asked to
- // launch this as a new task behind the current one.
- if (((launchFlags & Intent.FLAG_ACTIVITY_NEW_TASK) != 0 &&
- (launchFlags & Intent.FLAG_ACTIVITY_MULTIPLE_TASK) == 0)
- || launchSingleInstance || launchSingleTask) {
- // If bring to front is requested, and no result is requested and we have not
- // been given an explicit task to launch in to, and
- // we can find a task that was started with this same
- // component, then instead of launching bring that one to the front.
- if (inTask == null && r.resultTo == null) {
- // See if there is a task to bring to the front. If this is
- // a SINGLE_INSTANCE activity, there can be one and only one
- // instance of it in the history, and it is always in its own
- // unique task, so we do a special search.
- ActivityRecord intentActivity = !launchSingleInstance ?
- findTaskLocked(r) : findActivityLocked(intent, r.info);
- if (intentActivity != null) {
- // When the flags NEW_TASK and CLEAR_TASK are set, then the task gets reused
- // but still needs to be a lock task mode violation since the task gets
- // cleared out and the device would otherwise leave the locked task.
- if (isLockTaskModeViolation(intentActivity.task,
- (launchFlags & (FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_CLEAR_TASK))
- == (FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_CLEAR_TASK))) {
- showLockTaskToast();
- Slog.e(TAG, "startActivityUnchecked: Attempt to violate Lock Task Mode");
- return ActivityManager.START_RETURN_LOCK_TASK_MODE_VIOLATION;
- }
- if (r.task == null) {
- r.task = intentActivity.task;
- }
- if (intentActivity.task.intent == null) {
- // This task was started because of movement of
- // the activity based on affinity... now that we
- // are actually launching it, we can assign the
- // base intent.
- intentActivity.task.setIntent(r);
- }
- targetStack = intentActivity.task.stack;
- targetStack.mLastPausedActivity = null;
- // If the target task is not in the front, then we need
- // to bring it to the front... except... well, with
- // SINGLE_TASK_LAUNCH it's not entirely clear. We'd like
- // to have the same behavior as if a new instance was
- // being started, which means not bringing it to the front
- // if the caller is not itself in the front.
- final ActivityStack focusStack = getFocusedStack();
- ActivityRecord curTop = (focusStack == null)
- ? null : focusStack.topRunningNonDelayedActivityLocked(notTop);
- boolean movedToFront = false;
- if (curTop != null && (curTop.task != intentActivity.task ||
- curTop.task != focusStack.topTask())) {
- r.intent.addFlags(Intent.FLAG_ACTIVITY_BROUGHT_TO_FRONT);
- if (sourceRecord == null || (sourceStack.topActivity() != null &&
- sourceStack.topActivity().task == sourceRecord.task)) {
- // We really do want to push this one into the user's face, right now.
- if (launchTaskBehind && sourceRecord != null) {
- intentActivity.setTaskToAffiliateWith(sourceRecord.task);
- }
- movedHome = true;
- targetStack.moveTaskToFrontLocked(intentActivity.task, noAnimation,
- options, r.appTimeTracker, "bringingFoundTaskToFront");
- movedToFront = true;
- if ((launchFlags &
- (FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_TASK_ON_HOME))
- == (FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_TASK_ON_HOME)) {
- // Caller wants to appear on home activity.
- intentActivity.task.setTaskToReturnTo(HOME_ACTIVITY_TYPE);
- }
- options = null;
- }
- }
- if (!movedToFront && doResume) {
- if (DEBUG_TASKS) Slog.d(TAG_TASKS, "Bring to front target: " + targetStack
- + " from " + intentActivity);
- targetStack.moveToFront("intentActivityFound");
- }
+ ActivityRecord intentActivity = getReusableIntentActivity(r, inTask, intent,
+ launchSingleInstance, launchSingleTask, launchFlags);
+ if (intentActivity != null) {
+ // When the flags NEW_TASK and CLEAR_TASK are set, then the task gets reused
+ // but still needs to be a lock task mode violation since the task gets
+ // cleared out and the device would otherwise leave the locked task.
+ if (isLockTaskModeViolation(intentActivity.task,
+ (launchFlags & (FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_CLEAR_TASK))
+ == (FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_CLEAR_TASK))) {
+ showLockTaskToast();
+ Slog.e(TAG, "startActivityUnchecked: Attempt to violate Lock Task Mode");
+ return ActivityManager.START_RETURN_LOCK_TASK_MODE_VIOLATION;
+ }
+ if (r.task == null) {
+ r.task = intentActivity.task;
+ }
+ if (intentActivity.task.intent == null) {
+ // This task was started because of movement of the activity based on affinity...
+ // Now that we are actually launching it, we can assign the base intent.
+ intentActivity.task.setIntent(r);
+ }
- // If the caller has requested that the target task be
- // reset, then do so.
- if ((launchFlags&Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED) != 0) {
- intentActivity = targetStack.resetTaskIfNeededLocked(intentActivity, r);
+ targetStack = intentActivity.task.stack;
+ targetStack.mLastPausedActivity = null;
+ // If the target task is not in the front, then we need
+ // to bring it to the front... except... well, with
+ // SINGLE_TASK_LAUNCH it's not entirely clear. We'd like
+ // to have the same behavior as if a new instance was
+ // being started, which means not bringing it to the front
+ // if the caller is not itself in the front.
+ final ActivityStack focusStack = getFocusedStack();
+ ActivityRecord curTop = (focusStack == null)
+ ? null : focusStack.topRunningNonDelayedActivityLocked(notTop);
+ boolean movedToFront = false;
+ if (curTop != null && (curTop.task != intentActivity.task ||
+ curTop.task != focusStack.topTask())) {
+ r.intent.addFlags(Intent.FLAG_ACTIVITY_BROUGHT_TO_FRONT);
+ if (sourceRecord == null || (sourceStack.topActivity() != null &&
+ sourceStack.topActivity().task == sourceRecord.task)) {
+ // We really do want to push this one into the user's face, right now.
+ if (launchTaskBehind && sourceRecord != null) {
+ intentActivity.setTaskToAffiliateWith(sourceRecord.task);
}
- if ((startFlags & ActivityManager.START_FLAG_ONLY_IF_NEEDED) != 0) {
- // We don't need to start a new activity, and
- // the client said not to do anything if that
- // is the case, so this is it! And for paranoia, make
- // sure we have correctly resumed the top activity.
- if (doResume) {
- resumeTopActivitiesLocked(targetStack, null, options);
-
- // Make sure to notify Keyguard as well if we are not running an app
- // transition later.
- if (!movedToFront) {
- notifyActivityDrawnForKeyguard();
- }
- } else {
- ActivityOptions.abort(options);
- }
- updateUserStackLocked(r.userId, targetStack);
- return ActivityManager.START_RETURN_INTENT_TO_CALLER;
+ movedHome = true;
+ final ActivityStack sideStack = getLaunchToSideStack(r, launchFlags, r.task);
+ if (sideStack == null || sideStack == targetStack) {
+ // We only want to move to the front, if we aren't going to launch on a
+ // different stack. If we launch on a different stack, we will put the
+ // task on top there.
+ targetStack.moveTaskToFrontLocked(intentActivity.task, noAnimation,
+ options, r.appTimeTracker, "bringingFoundTaskToFront");
+ movedToFront = true;
}
- if ((launchFlags & (FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_CLEAR_TASK))
- == (FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_CLEAR_TASK)) {
- // The caller has requested to completely replace any
- // existing task with its new activity. Well that should
- // not be too hard...
- reuseTask = intentActivity.task;
- reuseTask.performClearTaskLocked();
- reuseTask.setIntent(r);
- } else if ((launchFlags & FLAG_ACTIVITY_CLEAR_TOP) != 0
- || launchSingleInstance || launchSingleTask) {
- // In this situation we want to remove all activities
- // from the task up to the one being started. In most
- // cases this means we are resetting the task to its
- // initial state.
- ActivityRecord top =
- intentActivity.task.performClearTaskLocked(r, launchFlags);
- if (top != null) {
- if (top.frontOfTask) {
- // Activity aliases may mean we use different
- // intents for the top activity, so make sure
- // the task now has the identity of the new
- // intent.
- top.task.setIntent(r);
- }
- ActivityStack.logStartActivity(EventLogTags.AM_NEW_INTENT,
- r, top.task);
- top.deliverNewIntentLocked(callingUid, r.intent, r.launchedFromPackage);
- } else {
- // A special case: we need to start the activity because it is not
- // currently running, and the caller has asked to clear the current
- // task to have this activity at the top.
- addingToTask = true;
- // Now pretend like this activity is being started by the top of its
- // task, so it is put in the right place.
- sourceRecord = intentActivity;
- TaskRecord task = sourceRecord.task;
- if (task != null && task.stack == null) {
- // Target stack got cleared when we all activities were removed
- // above. Go ahead and reset it.
- targetStack = computeStackFocus(
- sourceRecord, false /* newTask */, null /* bounds */);
- targetStack.addTask(task,
- !launchTaskBehind /* toTop */, "startActivityUnchecked");
- }
-
- }
- } else if (r.realActivity.equals(intentActivity.task.realActivity)) {
- // In this case the top activity on the task is the
- // same as the one being launched, so we take that
- // as a request to bring the task to the foreground.
- // If the top activity in the task is the root
- // activity, deliver this new intent to it if it
- // desires.
- if (((launchFlags&Intent.FLAG_ACTIVITY_SINGLE_TOP) != 0 || launchSingleTop)
- && intentActivity.realActivity.equals(r.realActivity)) {
- ActivityStack.logStartActivity(EventLogTags.AM_NEW_INTENT, r,
- intentActivity.task);
- if (intentActivity.frontOfTask) {
- intentActivity.task.setIntent(r);
- }
- intentActivity.deliverNewIntentLocked(callingUid, r.intent,
- r.launchedFromPackage);
- } else if (!r.intent.filterEquals(intentActivity.task.intent)) {
- // In this case we are launching the root activity
- // of the task, but with a different intent. We
- // should start a new instance on top.
- addingToTask = true;
- sourceRecord = intentActivity;
- }
- } else if ((launchFlags&Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED) == 0) {
- // In this case an activity is being launched in to an
- // existing task, without resetting that task. This
- // is typically the situation of launching an activity
- // from a notification or shortcut. We want to place
- // the new activity on top of the current task.
- addingToTask = true;
- sourceRecord = intentActivity;
- } else if (!intentActivity.task.rootWasReset) {
- // In this case we are launching in to an existing task
- // that has not yet been started from its front door.
- // The current task has been brought to the front.
- // Ideally, we'd probably like to place this new task
- // at the bottom of its stack, but that's a little hard
- // to do with the current organization of the code so
- // for now we'll just drop it.
- intentActivity.task.setIntent(r);
+ if ((launchFlags & (FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_TASK_ON_HOME))
+ == (FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_TASK_ON_HOME)) {
+ // Caller wants to appear on home activity.
+ intentActivity.task.setTaskToReturnTo(HOME_ACTIVITY_TYPE);
}
- if (!addingToTask && reuseTask == null) {
- // We didn't do anything... but it was needed (a.k.a., client
- // don't use that intent!) And for paranoia, make
- // sure we have correctly resumed the top activity.
- if (doResume) {
- targetStack.resumeTopActivityLocked(null, options);
- if (!movedToFront) {
- // Make sure to notify Keyguard as well if we are not running an app
- // transition later.
- notifyActivityDrawnForKeyguard();
- }
- } else {
- ActivityOptions.abort(options);
- }
- updateUserStackLocked(r.userId, targetStack);
- return ActivityManager.START_TASK_TO_FRONT;
- }
+ options = null;
}
}
+ if (!movedToFront && doResume) {
+ if (DEBUG_TASKS) Slog.d(TAG_TASKS, "Bring to front target: " + targetStack
+ + " from " + intentActivity);
+ targetStack.moveToFront("intentActivityFound");
+ }
+
+ // If the caller has requested that the target task be
+ // reset, then do so.
+ if ((launchFlags & Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED) != 0) {
+ intentActivity = targetStack.resetTaskIfNeededLocked(intentActivity, r);
+ }
+ if ((startFlags & ActivityManager.START_FLAG_ONLY_IF_NEEDED) != 0) {
+ // We don't need to start a new activity, and
+ // the client said not to do anything if that
+ // is the case, so this is it! And for paranoia, make
+ // sure we have correctly resumed the top activity.
+ if (doResume) {
+ resumeTopActivitiesLocked(targetStack, null, options);
+
+ // Make sure to notify Keyguard as well if we are not running an app
+ // transition later.
+ if (!movedToFront) {
+ notifyActivityDrawnForKeyguard();
+ }
+ } else {
+ ActivityOptions.abort(options);
+ }
+ updateUserStackLocked(r.userId, targetStack);
+ return ActivityManager.START_RETURN_INTENT_TO_CALLER;
+ }
+ if ((launchFlags & (FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_CLEAR_TASK))
+ == (FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_CLEAR_TASK)) {
+ // The caller has requested to completely replace any
+ // existing task with its new activity. Well that should
+ // not be too hard...
+ reuseTask = intentActivity.task;
+ reuseTask.performClearTaskLocked();
+ reuseTask.setIntent(r);
+ } else if ((launchFlags & FLAG_ACTIVITY_CLEAR_TOP) != 0
+ || launchSingleInstance || launchSingleTask) {
+ // In this situation we want to remove all activities
+ // from the task up to the one being started. In most
+ // cases this means we are resetting the task to its
+ // initial state.
+ ActivityRecord top = intentActivity.task.performClearTaskLocked(r, launchFlags);
+ if (top != null) {
+ if (top.frontOfTask) {
+ // Activity aliases may mean we use different
+ // intents for the top activity, so make sure
+ // the task now has the identity of the new
+ // intent.
+ top.task.setIntent(r);
+ }
+ ActivityStack.logStartActivity(EventLogTags.AM_NEW_INTENT, r, top.task);
+ top.deliverNewIntentLocked(callingUid, r.intent, r.launchedFromPackage);
+ } else {
+ // A special case: we need to start the activity because it is not
+ // currently running, and the caller has asked to clear the current
+ // task to have this activity at the top.
+ addingToTask = true;
+ // Now pretend like this activity is being started by the top of its
+ // task, so it is put in the right place.
+ sourceRecord = intentActivity;
+ TaskRecord task = sourceRecord.task;
+ if (task != null && task.stack == null) {
+ // Target stack got cleared when we all activities were removed
+ // above. Go ahead and reset it.
+ targetStack = computeStackFocus(
+ sourceRecord, false /* newTask */, null /* bounds */, launchFlags);
+ targetStack.addTask(task,
+ !launchTaskBehind /* toTop */, "startActivityUnchecked");
+ }
+
+ }
+ } else if (r.realActivity.equals(intentActivity.task.realActivity)) {
+ // In this case the top activity on the task is the
+ // same as the one being launched, so we take that
+ // as a request to bring the task to the foreground.
+ // If the top activity in the task is the root
+ // activity, deliver this new intent to it if it
+ // desires.
+ if (((launchFlags&Intent.FLAG_ACTIVITY_SINGLE_TOP) != 0 || launchSingleTop)
+ && intentActivity.realActivity.equals(r.realActivity)) {
+ ActivityStack.logStartActivity(EventLogTags.AM_NEW_INTENT, r,
+ intentActivity.task);
+ if (intentActivity.frontOfTask) {
+ intentActivity.task.setIntent(r);
+ }
+ intentActivity.deliverNewIntentLocked(callingUid, r.intent,
+ r.launchedFromPackage);
+ } else if (!r.intent.filterEquals(intentActivity.task.intent)) {
+ // In this case we are launching the root activity
+ // of the task, but with a different intent. We
+ // should start a new instance on top.
+ addingToTask = true;
+ sourceRecord = intentActivity;
+ }
+ } else if ((launchFlags&Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED) == 0) {
+ // In this case an activity is being launched in to an
+ // existing task, without resetting that task. This
+ // is typically the situation of launching an activity
+ // from a notification or shortcut. We want to place
+ // the new activity on top of the current task.
+ addingToTask = true;
+ sourceRecord = intentActivity;
+ } else if (!intentActivity.task.rootWasReset) {
+ // In this case we are launching in to an existing task
+ // that has not yet been started from its front door.
+ // The current task has been brought to the front.
+ // Ideally, we'd probably like to place this new task
+ // at the bottom of its stack, but that's a little hard
+ // to do with the current organization of the code so
+ // for now we'll just drop it.
+ intentActivity.task.setIntent(r);
+ }
+ if (!addingToTask && reuseTask == null) {
+ // We didn't do anything... but it was needed (a.k.a., client
+ // don't use that intent!) And for paranoia, make
+ // sure we have correctly resumed the top activity.
+ if (doResume) {
+ targetStack.resumeTopActivityLocked(null, options);
+ if (!movedToFront) {
+ // Make sure to notify Keyguard as well if we are not running an app
+ // transition later.
+ notifyActivityDrawnForKeyguard();
+ }
+ } else {
+ ActivityOptions.abort(options);
+ }
+ updateUserStackLocked(r.userId, targetStack);
+ return ActivityManager.START_TASK_TO_FRONT;
+ }
}
//String uri = r.intent.toURI();
@@ -2416,33 +2525,27 @@
// once.
ActivityStack topStack = mFocusedStack;
ActivityRecord top = topStack.topRunningNonDelayedActivityLocked(notTop);
- if (top != null && r.resultTo == null) {
- if (top.realActivity.equals(r.realActivity) && top.userId == r.userId) {
- if (top.app != null && top.app.thread != null) {
- if ((launchFlags & Intent.FLAG_ACTIVITY_SINGLE_TOP) != 0
- || launchSingleTop || launchSingleTask) {
- ActivityStack.logStartActivity(EventLogTags.AM_NEW_INTENT, top,
- top.task);
- // For paranoia, make sure we have correctly
- // resumed the top activity.
- topStack.mLastPausedActivity = null;
- if (doResume) {
- resumeTopActivitiesLocked();
- }
- ActivityOptions.abort(options);
- if ((startFlags&ActivityManager.START_FLAG_ONLY_IF_NEEDED) != 0) {
- // We don't need to start a new activity, and
- // the client said not to do anything if that
- // is the case, so this is it!
- return ActivityManager.START_RETURN_INTENT_TO_CALLER;
- }
- top.deliverNewIntentLocked(callingUid, r.intent, r.launchedFromPackage);
- return ActivityManager.START_DELIVERED_TO_TOP;
- }
- }
+ final boolean dontStart = top != null && r.resultTo == null
+ && top.realActivity.equals(r.realActivity) && top.userId == r.userId
+ && top.app != null && top.app.thread != null
+ && ((launchFlags & Intent.FLAG_ACTIVITY_SINGLE_TOP) != 0
+ || launchSingleTop || launchSingleTask);
+ if (dontStart) {
+ ActivityStack.logStartActivity(EventLogTags.AM_NEW_INTENT, top, top.task);
+ // For paranoia, make sure we have correctly resumed the top activity.
+ topStack.mLastPausedActivity = null;
+ if (doResume) {
+ resumeTopActivitiesLocked();
}
+ ActivityOptions.abort(options);
+ if ((startFlags & ActivityManager.START_FLAG_ONLY_IF_NEEDED) != 0) {
+ // We don't need to start a new activity, and the client said not to do
+ // anything if that is the case, so this is it!
+ return ActivityManager.START_RETURN_INTENT_TO_CALLER;
+ }
+ top.deliverNewIntentLocked(callingUid, r.intent, r.launchedFromPackage);
+ return ActivityManager.START_DELIVERED_TO_TOP;
}
-
} else {
if (r.resultTo != null && r.resultTo.task.stack != null) {
r.resultTo.task.stack.sendActivityResultLocked(-1, r.resultTo, r.resultWho,
@@ -2460,9 +2563,9 @@
// Should this be considered a new task?
if (r.resultTo == null && inTask == null && !addingToTask
- && (launchFlags & Intent.FLAG_ACTIVITY_NEW_TASK) != 0) {
+ && (launchFlags & FLAG_ACTIVITY_NEW_TASK) != 0) {
newTask = true;
- targetStack = computeStackFocus(r, newTask, newBounds);
+ targetStack = computeStackFocus(r, newTask, newBounds, launchFlags);
if (doResume) {
targetStack.moveToFront("startingNewTask");
}
@@ -2500,7 +2603,19 @@
Slog.e(TAG, "Attempted Lock Task Mode violation r=" + r);
return ActivityManager.START_RETURN_LOCK_TASK_MODE_VIOLATION;
}
- targetStack = sourceTask.stack;
+ targetStack = null;
+ if (sourceTask.stack.topTask() != sourceTask) {
+ // We only want to allow changing stack if the target task is not the top one,
+ // otherwise we would move the launching task to the other side, rather than show
+ // two side by side.
+ targetStack = getLaunchToSideStack(r, launchFlags, r.task);
+ }
+ if (targetStack == null) {
+ targetStack = sourceTask.stack;
+ } else if (targetStack != sourceTask.stack) {
+ moveTaskToStackLocked(sourceTask.taskId, targetStack.mStackId, ON_TOP,
+ FORCE_FOCUS, "launchToSide", !ANIMATE);
+ }
if (doResume) {
targetStack.moveToFront("sourceStackToFront");
}
@@ -2605,7 +2720,7 @@
// This not being started from an existing activity, and not part
// of a new task... just put it in the top task, though these days
// this case should never happen.
- targetStack = computeStackFocus(r, newTask, null /* bounds */);
+ targetStack = computeStackFocus(r, newTask, null /* bounds */, launchFlags);
if (doResume) {
targetStack.moveToFront("addingToTopTask");
}
@@ -2646,6 +2761,37 @@
return ActivityManager.START_SUCCESS;
}
+ /**
+ * Decide whether the new activity should be inserted into an existing task. Returns null if not
+ * or an ActivityRecord with the task into which the new activity should be added.
+ */
+ private ActivityRecord getReusableIntentActivity(ActivityRecord r, TaskRecord inTask, Intent intent,
+ boolean launchSingleInstance, boolean launchSingleTask, int launchFlags) {
+ // We may want to try to place the new activity in to an existing task. We always
+ // do this if the target activity is singleTask or singleInstance; we will also do
+ // this if NEW_TASK has been requested, and there is not an additional qualifier telling
+ // us to still place it in a new task: multi task, always doc mode, or being asked to
+ // launch this as a new task behind the current one.
+ boolean putIntoExistingTask = ((launchFlags & FLAG_ACTIVITY_NEW_TASK) != 0 &&
+ (launchFlags & FLAG_ACTIVITY_MULTIPLE_TASK) == 0)
+ || launchSingleInstance || launchSingleTask;
+ // If bring to front is requested, and no result is requested and we have not
+ // been given an explicit task to launch in to, and
+ // we can find a task that was started with this same
+ // component, then instead of launching bring that one to the front.
+ putIntoExistingTask &= inTask == null && r.resultTo == null;
+ ActivityRecord intentActivity = null;
+ if (putIntoExistingTask) {
+ // See if there is a task to bring to the front. If this is
+ // a SINGLE_INSTANCE activity, there can be one and only one
+ // instance of it in the history, and it is always in its own
+ // unique task, so we do a special search.
+ intentActivity = launchSingleInstance ?
+ findActivityLocked(intent, r.info) : findTaskLocked(r);
+ }
+ return intentActivity;
+ }
+
final void doPendingActivityLaunchesLocked(boolean doResume) {
while (!mPendingActivityLaunches.isEmpty()) {
PendingActivityLaunch pal = mPendingActivityLaunches.remove(0);
@@ -3124,6 +3270,14 @@
Trace.traceBegin(TRACE_TAG_ACTIVITY_MANAGER, "am.resizeStack_" + stackId);
mWindowManager.deferSurfaceLayout();
try {
+
+ if (bounds != null && mWindowManager.isFullscreenBounds(stackId, bounds)) {
+ // The bounds passed in corresponds to the fullscreen bounds which we normally
+ // represent with null. Go ahead and set it to null so that all tasks configuration
+ // can have the right fullscreen state.
+ bounds = null;
+ }
+
ActivityRecord r = stack.topRunningActivityLocked();
mTmpBounds.clear();
@@ -3462,10 +3616,10 @@
return false;
}
- if (!r.info.supportsPip) {
+ if (!mService.mForceResizableActivities && !r.info.supportsPip) {
Slog.w(TAG,
"moveTopStackActivityToPinnedStackLocked: Picture-In-Picture not supported for "
- + " r=" + r);
+ + " r=" + r);
return false;
}
@@ -4521,6 +4675,10 @@
return mLockTaskModeState;
}
+ void logStackState() {
+ mActivityMetricsLogger.logWindowState();
+ }
+
private final class ActivityStackSupervisorHandler extends Handler {
public ActivityStackSupervisorHandler(Looper looper) {
@@ -4700,8 +4858,8 @@
}
class ActivityContainer extends android.app.IActivityContainer.Stub {
- final static int FORCE_NEW_TASK_FLAGS = Intent.FLAG_ACTIVITY_NEW_TASK |
- Intent.FLAG_ACTIVITY_MULTIPLE_TASK | Intent.FLAG_ACTIVITY_NO_ANIMATION;
+ final static int FORCE_NEW_TASK_FLAGS = FLAG_ACTIVITY_NEW_TASK |
+ FLAG_ACTIVITY_MULTIPLE_TASK | Intent.FLAG_ACTIVITY_NO_ANIMATION;
final int mStackId;
IActivityContainerCallback mCallback = null;
final ActivityStack mStack;
diff --git a/services/core/java/com/android/server/display/AutomaticBrightnessController.java b/services/core/java/com/android/server/display/AutomaticBrightnessController.java
index e15bca6..1908f72 100644
--- a/services/core/java/com/android/server/display/AutomaticBrightnessController.java
+++ b/services/core/java/com/android/server/display/AutomaticBrightnessController.java
@@ -49,23 +49,12 @@
// If true, enables the use of the screen auto-brightness adjustment setting.
private static final boolean USE_SCREEN_AUTO_BRIGHTNESS_ADJUSTMENT = true;
- // The maximum range of gamma adjustment possible using the screen
- // auto-brightness adjustment setting.
- private static final float SCREEN_AUTO_BRIGHTNESS_ADJUSTMENT_MAX_GAMMA = 3.0f;
-
- // Period of time in which to consider light samples in milliseconds.
- private static final int AMBIENT_LIGHT_HORIZON = 10000;
-
// Hysteresis constraints for brightening or darkening.
// The recent lux must have changed by at least this fraction relative to the
// current ambient lux before a change will be considered.
private static final float BRIGHTENING_LIGHT_HYSTERESIS = 0.10f;
private static final float DARKENING_LIGHT_HYSTERESIS = 0.20f;
- // The intercept used for the weighting calculation. This is used in order to keep all possible
- // weighting values positive.
- private static final int WEIGHTING_INTERCEPT = AMBIENT_LIGHT_HORIZON;
-
// How long the current sensor reading is assumed to be valid beyond the current time.
// This provides a bit of prediction, as well as ensures that the weight for the last sample is
// non-zero, which in turn ensures that the total weight is non-zero.
@@ -132,6 +121,13 @@
// and only then decide whether to change brightness.
private final boolean mResetAmbientLuxAfterWarmUpConfig;
+ // Period of time in which to consider light samples in milliseconds.
+ private final int mAmbientLightHorizon;
+
+ // The intercept used for the weighting calculation. This is used in order to keep all possible
+ // weighting values positive.
+ private final int mWeightingIntercept;
+
// Amount of time to delay auto-brightness after screen on while waiting for
// the light sensor to warm-up in milliseconds.
// May be 0 if no warm-up is required.
@@ -179,6 +175,10 @@
// The screen auto-brightness adjustment factor in the range -1 (dimmer) to 1 (brighter)
private float mScreenAutoBrightnessAdjustment = 0.0f;
+ // The maximum range of gamma adjustment possible using the screen
+ // auto-brightness adjustment setting.
+ private float mScreenAutoBrightnessAdjustmentMaxGamma;
+
// The last screen auto-brightness gamma. (For printing in dump() only.)
private float mLastScreenAutoBrightnessGamma = 1.0f;
@@ -197,7 +197,8 @@
SensorManager sensorManager, Spline autoBrightnessSpline, int lightSensorWarmUpTime,
int brightnessMin, int brightnessMax, float dozeScaleFactor,
int lightSensorRate, long brighteningLightDebounceConfig,
- long darkeningLightDebounceConfig, boolean resetAmbientLuxAfterWarmUpConfig) {
+ long darkeningLightDebounceConfig, boolean resetAmbientLuxAfterWarmUpConfig,
+ int ambientLightHorizon, float autoBrightnessAdjustmentMaxGamma ) {
mCallbacks = callbacks;
mTwilight = LocalServices.getService(TwilightManager.class);
mSensorManager = sensorManager;
@@ -210,9 +211,12 @@
mBrighteningLightDebounceConfig = brighteningLightDebounceConfig;
mDarkeningLightDebounceConfig = darkeningLightDebounceConfig;
mResetAmbientLuxAfterWarmUpConfig = resetAmbientLuxAfterWarmUpConfig;
+ mAmbientLightHorizon = ambientLightHorizon;
+ mWeightingIntercept = ambientLightHorizon;
+ mScreenAutoBrightnessAdjustmentMaxGamma = autoBrightnessAdjustmentMaxGamma;
mHandler = new AutomaticBrightnessHandler(looper);
- mAmbientLightRingBuffer = new AmbientLightRingBuffer(mLightSensorRate);
+ mAmbientLightRingBuffer = new AmbientLightRingBuffer(mLightSensorRate, mAmbientLightHorizon);
if (!DEBUG_PRETEND_LIGHT_SENSOR_ABSENT) {
mLightSensor = mSensorManager.getDefaultSensor(Sensor.TYPE_LIGHT);
@@ -266,6 +270,7 @@
pw.println(" mLightSensorEnabled=" + mLightSensorEnabled);
pw.println(" mLightSensorEnableTime=" + TimeUtils.formatUptime(mLightSensorEnableTime));
pw.println(" mAmbientLux=" + mAmbientLux);
+ pw.println(" mAmbientLightHorizon=" + mAmbientLightHorizon);
pw.println(" mBrighteningLuxThreshold=" + mBrighteningLuxThreshold);
pw.println(" mDarkeningLuxThreshold=" + mDarkeningLuxThreshold);
pw.println(" mLastObservedLux=" + mLastObservedLux);
@@ -274,6 +279,7 @@
pw.println(" mAmbientLightRingBuffer=" + mAmbientLightRingBuffer);
pw.println(" mScreenAutoBrightness=" + mScreenAutoBrightness);
pw.println(" mScreenAutoBrightnessAdjustment=" + mScreenAutoBrightnessAdjustment);
+ pw.println(" mScreenAutoBrightnessAdjustmentMaxGamma=" + mScreenAutoBrightnessAdjustmentMaxGamma);
pw.println(" mLastScreenAutoBrightnessGamma=" + mLastScreenAutoBrightnessGamma);
pw.println(" mDozing=" + mDozing);
}
@@ -309,7 +315,7 @@
private void applyLightSensorMeasurement(long time, float lux) {
mRecentLightSamples++;
- mAmbientLightRingBuffer.prune(time - AMBIENT_LIGHT_HORIZON);
+ mAmbientLightRingBuffer.prune(time - mAmbientLightHorizon);
mAmbientLightRingBuffer.push(time, lux);
// Remember this sample value.
@@ -360,14 +366,14 @@
return sum / totalWeight;
}
- private static float calculateWeight(long startDelta, long endDelta) {
+ private float calculateWeight(long startDelta, long endDelta) {
return weightIntegral(endDelta) - weightIntegral(startDelta);
}
- // Evaluates the integral of y = x + WEIGHTING_INTERCEPT. This is always positive for the
+ // Evaluates the integral of y = x + mWeightingIntercept. This is always positive for the
// horizon we're looking at and provides a non-linear weighting for light samples.
- private static float weightIntegral(long x) {
- return x * (x * 0.5f + WEIGHTING_INTERCEPT);
+ private float weightIntegral(long x) {
+ return x * (x * 0.5f + mWeightingIntercept);
}
private long nextAmbientLightBrighteningTransition(long time) {
@@ -396,7 +402,7 @@
private void updateAmbientLux() {
long time = SystemClock.uptimeMillis();
- mAmbientLightRingBuffer.prune(time - AMBIENT_LIGHT_HORIZON);
+ mAmbientLightRingBuffer.prune(time - mAmbientLightHorizon);
updateAmbientLux(time);
}
@@ -470,7 +476,7 @@
if (USE_SCREEN_AUTO_BRIGHTNESS_ADJUSTMENT
&& mScreenAutoBrightnessAdjustment != 0.0f) {
- final float adjGamma = MathUtils.pow(SCREEN_AUTO_BRIGHTNESS_ADJUSTMENT_MAX_GAMMA,
+ final float adjGamma = MathUtils.pow(mScreenAutoBrightnessAdjustmentMaxGamma,
Math.min(1.0f, Math.max(-1.0f, -mScreenAutoBrightnessAdjustment)));
gamma *= adjGamma;
if (DEBUG) {
@@ -653,8 +659,8 @@
private int mEnd;
private int mCount;
- public AmbientLightRingBuffer(long lightSensorRate) {
- mCapacity = (int) Math.ceil(AMBIENT_LIGHT_HORIZON * BUFFER_SLACK / lightSensorRate);
+ public AmbientLightRingBuffer(long lightSensorRate, int ambientLightHorizon) {
+ mCapacity = (int) Math.ceil(ambientLightHorizon * BUFFER_SLACK / lightSensorRate);
mRingLux = new float[mCapacity];
mRingTime = new long[mCapacity];
}
diff --git a/services/core/java/com/android/server/display/DisplayPowerController.java b/services/core/java/com/android/server/display/DisplayPowerController.java
index 7b49530..433d887 100644
--- a/services/core/java/com/android/server/display/DisplayPowerController.java
+++ b/services/core/java/com/android/server/display/DisplayPowerController.java
@@ -311,6 +311,11 @@
com.android.internal.R.integer.config_autoBrightnessDarkeningLightDebounce);
boolean autoBrightnessResetAmbientLuxAfterWarmUp = resources.getBoolean(
com.android.internal.R.bool.config_autoBrightnessResetAmbientLuxAfterWarmUp);
+ int ambientLightHorizon = resources.getInteger(
+ com.android.internal.R.integer.config_autoBrightnessAmbientLightHorizon);
+ float autoBrightnessAdjustmentMaxGamma = resources.getFraction(
+ com.android.internal.R.fraction.config_autoBrightnessAdjustmentMaxGamma,
+ 1, 1);
if (mUseSoftwareAutoBrightnessConfig) {
int[] lux = resources.getIntArray(
@@ -348,7 +353,8 @@
lightSensorWarmUpTimeConfig, screenBrightnessRangeMinimum,
mScreenBrightnessRangeMaximum, dozeScaleFactor, lightSensorRate,
brighteningLightDebounce, darkeningLightDebounce,
- autoBrightnessResetAmbientLuxAfterWarmUp);
+ autoBrightnessResetAmbientLuxAfterWarmUp,
+ ambientLightHorizon, autoBrightnessAdjustmentMaxGamma);
}
}
diff --git a/services/core/java/com/android/server/fingerprint/FingerprintService.java b/services/core/java/com/android/server/fingerprint/FingerprintService.java
index e0a9975..7ac3c4b 100644
--- a/services/core/java/com/android/server/fingerprint/FingerprintService.java
+++ b/services/core/java/com/android/server/fingerprint/FingerprintService.java
@@ -189,7 +189,7 @@
protected void handleEnumerate(long deviceId, int[] fingerIds, int[] groupIds) {
if (fingerIds.length != groupIds.length) {
Slog.w(TAG, "fingerIds and groupIds differ in length: f[]="
- + fingerIds + ", g[]=" + groupIds);
+ + Arrays.toString(fingerIds) + ", g[]=" + Arrays.toString(groupIds));
return;
}
if (DEBUG) Slog.w(TAG, "Enumerate: f[]=" + fingerIds + ", g[]=" + groupIds);
diff --git a/services/core/java/com/android/server/input/InputManagerService.java b/services/core/java/com/android/server/input/InputManagerService.java
index f2d0031..68b3817 100644
--- a/services/core/java/com/android/server/input/InputManagerService.java
+++ b/services/core/java/com/android/server/input/InputManagerService.java
@@ -213,6 +213,7 @@
private static native void nativeMonitor(long ptr);
private static native void nativeSetPointerIconShape(long ptr, int iconId);
private static native void nativeReloadPointerIcons(long ptr);
+ private static native void nativeSetCustomPointerIcon(long ptr, PointerIcon icon);
// Input event injection constants defined in InputDispatcher.h.
private static final int INPUT_EVENT_INJECTION_SUCCEEDED = 0;
@@ -319,12 +320,13 @@
public void onReceive(Context context, Intent intent) {
updatePointerSpeedFromSettings();
updateShowTouchesFromSettings();
- nativeReloadPointerIcons(mPtr);
+ updateAccessibilityLargePointerFromSettings();
}
}, new IntentFilter(Intent.ACTION_USER_SWITCHED), null, mHandler);
updatePointerSpeedFromSettings();
updateShowTouchesFromSettings();
+ updateAccessibilityLargePointerFromSettings();
}
// TODO(BT) Pass in paramter for bluetooth system
@@ -1365,13 +1367,21 @@
}, UserHandle.USER_ALL);
}
+ public void updateAccessibilityLargePointerFromSettings() {
+ final int accessibilityConfig = Settings.Secure.getIntForUser(
+ mContext.getContentResolver(), Settings.Secure.ACCESSIBILITY_LARGE_POINTER_ICON,
+ 0, UserHandle.USER_CURRENT);
+ PointerIcon.sUseLargeIcons = (accessibilityConfig == 1);
+ nativeReloadPointerIcons(mPtr);
+ }
+
private void registerAccessibilityLargePointerSettingObserver() {
mContext.getContentResolver().registerContentObserver(
Settings.Secure.getUriFor(Settings.Secure.ACCESSIBILITY_LARGE_POINTER_ICON), true,
new ContentObserver(mHandler) {
@Override
public void onChange(boolean selfChange) {
- nativeReloadPointerIcons(mPtr);
+ updateAccessibilityLargePointerFromSettings();
}
}, UserHandle.USER_ALL);
}
@@ -1451,6 +1461,12 @@
nativeSetPointerIconShape(mPtr, iconId);
}
+ // Binder call
+ @Override
+ public void setCustomPointerIcon(PointerIcon icon) {
+ nativeSetCustomPointerIcon(mPtr, icon);
+ }
+
@Override
public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
if (mContext.checkCallingOrSelfPermission(Manifest.permission.DUMP)
diff --git a/services/core/java/com/android/server/location/GpsLocationProvider.java b/services/core/java/com/android/server/location/GpsLocationProvider.java
index bc9f520..88ab2c6 100644
--- a/services/core/java/com/android/server/location/GpsLocationProvider.java
+++ b/services/core/java/com/android/server/location/GpsLocationProvider.java
@@ -88,6 +88,7 @@
import java.io.StringReader;
import java.net.InetAddress;
import java.net.UnknownHostException;
+import java.util.Arrays;
import java.util.Date;
import java.util.Map.Entry;
import java.util.Properties;
@@ -1599,7 +1600,7 @@
switch (status) {
case GPS_REQUEST_AGPS_DATA_CONN:
if (DEBUG) Log.d(TAG, "GPS_REQUEST_AGPS_DATA_CONN");
- Log.v(TAG, "Received SUPL IP addr[]: " + ipaddr);
+ Log.v(TAG, "Received SUPL IP addr[]: " + Arrays.toString(ipaddr));
InetAddress connectionIpAddress = null;
if (ipaddr != null) {
try {
diff --git a/services/core/java/com/android/server/net/NetworkStatsAccess.java b/services/core/java/com/android/server/net/NetworkStatsAccess.java
new file mode 100644
index 0000000..53ba718
--- /dev/null
+++ b/services/core/java/com/android/server/net/NetworkStatsAccess.java
@@ -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
+ */
+
+package com.android.server.net;
+
+import static android.Manifest.permission.READ_NETWORK_USAGE_HISTORY;
+import static android.net.TrafficStats.UID_REMOVED;
+import static android.net.TrafficStats.UID_TETHERING;
+
+import android.Manifest;
+import android.annotation.IntDef;
+import android.app.AppOpsManager;
+import android.app.admin.DeviceAdminInfo;
+import android.app.admin.DevicePolicyManagerInternal;
+import android.content.Context;
+import android.content.pm.PackageManager;
+import android.os.UserHandle;
+import android.telephony.TelephonyManager;
+
+import com.android.server.LocalServices;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/** Utility methods for controlling access to network stats APIs. */
+public final class NetworkStatsAccess {
+ private NetworkStatsAccess() {}
+
+ /**
+ * Represents an access level for the network usage history and statistics APIs.
+ *
+ * <p>Access levels are in increasing order; that is, it is reasonable to check access by
+ * verifying that the caller's access level is at least the minimum required level.
+ */
+ @IntDef({
+ Level.DEFAULT,
+ Level.USER,
+ Level.DEVICE,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface Level {
+ /**
+ * Default, unprivileged access level.
+ *
+ * <p>Can only access usage for one's own UID.
+ *
+ * <p>Every app will have at least this access level.
+ */
+ int DEFAULT = 0;
+
+ /**
+ * Access level for apps which can access usage for any app running in the same user.
+ *
+ * <p>Granted to:
+ * <ul>
+ * <li>Apps with the PACKAGE_USAGE_STATS permission granted. Note that this is an AppOps bit
+ * so it is not necessarily sufficient to declare this in the manifest.
+ * <li>Apps with the (signature/privileged) READ_NETWORK_USAGE_HISTORY permission.
+ * <li>Profile owners.
+ * </ul>
+ */
+ int USER = 1;
+
+ /**
+ * Access level for apps which can access usage for any app on the device, including apps
+ * running on other users/profiles.
+ *
+ * <p>Granted to:
+ * <ul>
+ * <li>Device owners.
+ * <li>Carrier-privileged applications.
+ * <li>The system UID.
+ * </ul>
+ */
+ int DEVICE = 2;
+ }
+
+ /** Returns the {@link NetworkStatsAccess.Level} for the given caller. */
+ public static @NetworkStatsAccess.Level int checkAccessLevel(
+ Context context, int callingUid, String callingPackage) {
+ final DevicePolicyManagerInternal dpmi = LocalServices.getService(
+ DevicePolicyManagerInternal.class);
+ final TelephonyManager tm = (TelephonyManager)
+ context.getSystemService(Context.TELEPHONY_SERVICE);
+ boolean hasCarrierPrivileges = tm != null &&
+ tm.checkCarrierPrivilegesForPackage(callingPackage) ==
+ TelephonyManager.CARRIER_PRIVILEGE_STATUS_HAS_ACCESS;
+ boolean isDeviceOwner = dpmi != null && dpmi.isActiveAdminWithPolicy(callingUid,
+ DeviceAdminInfo.USES_POLICY_DEVICE_OWNER);
+ if (hasCarrierPrivileges || isDeviceOwner
+ || UserHandle.getAppId(callingUid) == android.os.Process.SYSTEM_UID) {
+ // Carrier-privileged apps and device owners, and the system can access data usage for
+ // all apps on the device.
+ return NetworkStatsAccess.Level.DEVICE;
+ }
+
+ boolean isProfileOwner = dpmi != null && dpmi.isActiveAdminWithPolicy(callingUid,
+ DeviceAdminInfo.USES_POLICY_PROFILE_OWNER);
+ if (hasAppOpsPermission(context, callingUid, callingPackage) || isProfileOwner
+ || context.checkCallingOrSelfPermission(READ_NETWORK_USAGE_HISTORY) ==
+ PackageManager.PERMISSION_GRANTED) {
+ // Apps with the AppOps permission, profile owners, and apps with the privileged
+ // permission can access data usage for all apps in this user/profile.
+ return NetworkStatsAccess.Level.USER;
+ }
+
+ // Everyone else gets default access (only to their own UID).
+ return NetworkStatsAccess.Level.DEFAULT;
+ }
+
+ /**
+ * Returns whether the given caller should be able to access the given UID when the caller has
+ * the given {@link NetworkStatsAccess.Level}.
+ */
+ public static boolean isAccessibleToUser(int uid, int callerUid,
+ @NetworkStatsAccess.Level int accessLevel) {
+ switch (accessLevel) {
+ case NetworkStatsAccess.Level.DEVICE:
+ // Device-level access - can access usage for any uid.
+ return true;
+ case NetworkStatsAccess.Level.USER:
+ // User-level access - can access usage for any app running in the same user, along
+ // with some special uids (system, removed, or tethering).
+ return uid == android.os.Process.SYSTEM_UID || uid == UID_REMOVED
+ || uid == UID_TETHERING
+ || UserHandle.getUserId(uid) == UserHandle.getUserId(callerUid);
+ case NetworkStatsAccess.Level.DEFAULT:
+ default:
+ // Default access level - can only access one's own usage.
+ return uid == callerUid;
+ }
+ }
+
+ private static boolean hasAppOpsPermission(
+ Context context, int callingUid, String callingPackage) {
+ if (callingPackage != null) {
+ AppOpsManager appOps = (AppOpsManager) context.getSystemService(
+ Context.APP_OPS_SERVICE);
+
+ final int mode = appOps.checkOp(AppOpsManager.OP_GET_USAGE_STATS,
+ callingUid, callingPackage);
+ if (mode == AppOpsManager.MODE_DEFAULT) {
+ // The default behavior here is to check if PackageManager has given the app
+ // permission.
+ final int permissionCheck = context.checkCallingPermission(
+ Manifest.permission.PACKAGE_USAGE_STATS);
+ return permissionCheck == PackageManager.PERMISSION_GRANTED;
+ }
+ return (mode == AppOpsManager.MODE_ALLOWED);
+ }
+ return false;
+ }
+}
diff --git a/services/core/java/com/android/server/net/NetworkStatsCollection.java b/services/core/java/com/android/server/net/NetworkStatsCollection.java
index 15b68c7..102695e 100644
--- a/services/core/java/com/android/server/net/NetworkStatsCollection.java
+++ b/services/core/java/com/android/server/net/NetworkStatsCollection.java
@@ -22,24 +22,18 @@
import static android.net.NetworkStats.TAG_NONE;
import static android.net.NetworkStats.UID_ALL;
import static android.net.TrafficStats.UID_REMOVED;
-import static android.net.TrafficStats.UID_TETHERING;
-import static android.text.format.DateUtils.SECOND_IN_MILLIS;
import static android.text.format.DateUtils.WEEK_IN_MILLIS;
-import android.net.ConnectivityManager;
import android.net.NetworkIdentity;
import android.net.NetworkStats;
import android.net.NetworkStatsHistory;
import android.net.NetworkTemplate;
import android.net.TrafficStats;
import android.os.Binder;
-import android.os.UserHandle;
import android.util.ArrayMap;
import android.util.AtomicFile;
import android.util.IntArray;
-import libcore.io.IoUtils;
-
import com.android.internal.util.ArrayUtils;
import com.android.internal.util.FileRotator;
import com.android.internal.util.IndentingPrintWriter;
@@ -47,6 +41,8 @@
import com.google.android.collect.Lists;
import com.google.android.collect.Maps;
+import libcore.io.IoUtils;
+
import java.io.BufferedInputStream;
import java.io.DataInputStream;
import java.io.DataOutputStream;
@@ -136,12 +132,12 @@
return mStartMillis == Long.MAX_VALUE && mEndMillis == Long.MIN_VALUE;
}
- public int[] getRelevantUids() {
+ public int[] getRelevantUids(@NetworkStatsAccess.Level int accessLevel) {
final int callerUid = Binder.getCallingUid();
IntArray uids = new IntArray();
for (int i = 0; i < mStats.size(); i++) {
final Key key = mStats.keyAt(i);
- if (isAccessibleToUser(key.uid, callerUid)) {
+ if (NetworkStatsAccess.isAccessibleToUser(key.uid, callerUid, accessLevel)) {
int j = uids.binarySearch(key.uid);
if (j < 0) {
@@ -158,8 +154,10 @@
* the requested parameters.
*/
public NetworkStatsHistory getHistory(
- NetworkTemplate template, int uid, int set, int tag, int fields) {
- return getHistory(template, uid, set, tag, fields, Long.MIN_VALUE, Long.MAX_VALUE);
+ NetworkTemplate template, int uid, int set, int tag, int fields,
+ @NetworkStatsAccess.Level int accessLevel) {
+ return getHistory(template, uid, set, tag, fields, Long.MIN_VALUE, Long.MAX_VALUE,
+ accessLevel);
}
/**
@@ -167,9 +165,10 @@
* the requested parameters.
*/
public NetworkStatsHistory getHistory(
- NetworkTemplate template, int uid, int set, int tag, int fields, long start, long end) {
+ NetworkTemplate template, int uid, int set, int tag, int fields, long start, long end,
+ @NetworkStatsAccess.Level int accessLevel) {
final int callerUid = Binder.getCallingUid();
- if (!isAccessibleToUser(uid, callerUid)) {
+ if (!NetworkStatsAccess.isAccessibleToUser(uid, callerUid, accessLevel)) {
throw new SecurityException("Network stats history of uid " + uid
+ " is forbidden for caller " + callerUid);
}
@@ -195,7 +194,8 @@
* Summarize all {@link NetworkStatsHistory} in this collection which match
* the requested parameters.
*/
- public NetworkStats getSummary(NetworkTemplate template, long start, long end) {
+ public NetworkStats getSummary(NetworkTemplate template, long start, long end,
+ @NetworkStatsAccess.Level int accessLevel) {
final long now = System.currentTimeMillis();
final NetworkStats stats = new NetworkStats(end - start, 24);
@@ -208,7 +208,8 @@
final int callerUid = Binder.getCallingUid();
for (int i = 0; i < mStats.size(); i++) {
final Key key = mStats.keyAt(i);
- if (templateMatches(template, key.ident) && isAccessibleToUser(key.uid, callerUid)
+ if (templateMatches(template, key.ident)
+ && NetworkStatsAccess.isAccessibleToUser(key.uid, callerUid, accessLevel)
&& key.set < NetworkStats.SET_DEBUG_START) {
final NetworkStatsHistory value = mStats.valueAt(i);
historyEntry = value.getValues(start, end, now, historyEntry);
@@ -570,12 +571,6 @@
}
}
- private static boolean isAccessibleToUser(int uid, int callerUid) {
- return UserHandle.getAppId(callerUid) == android.os.Process.SYSTEM_UID ||
- uid == android.os.Process.SYSTEM_UID || uid == UID_REMOVED || uid == UID_TETHERING
- || UserHandle.getUserId(uid) == UserHandle.getUserId(callerUid);
- }
-
/**
* Test if given {@link NetworkTemplate} matches any {@link NetworkIdentity}
* in the given {@link NetworkIdentitySet}.
diff --git a/services/core/java/com/android/server/net/NetworkStatsRecorder.java b/services/core/java/com/android/server/net/NetworkStatsRecorder.java
index 6490865..c091960 100644
--- a/services/core/java/com/android/server/net/NetworkStatsRecorder.java
+++ b/services/core/java/com/android/server/net/NetworkStatsRecorder.java
@@ -116,7 +116,8 @@
}
public NetworkStats.Entry getTotalSinceBootLocked(NetworkTemplate template) {
- return mSinceBoot.getSummary(template, Long.MIN_VALUE, Long.MAX_VALUE).getTotal(null);
+ return mSinceBoot.getSummary(template, Long.MIN_VALUE, Long.MAX_VALUE,
+ NetworkStatsAccess.Level.DEVICE).getTotal(null);
}
/**
diff --git a/services/core/java/com/android/server/net/NetworkStatsService.java b/services/core/java/com/android/server/net/NetworkStatsService.java
index 8449348..b1d6f89 100644
--- a/services/core/java/com/android/server/net/NetworkStatsService.java
+++ b/services/core/java/com/android/server/net/NetworkStatsService.java
@@ -62,13 +62,9 @@
import static com.android.server.NetworkManagementSocketTagger.resetKernelUidStats;
import static com.android.server.NetworkManagementSocketTagger.setKernelCounterSet;
-import android.Manifest;
import android.app.AlarmManager;
-import android.app.AppOpsManager;
import android.app.IAlarmManager;
import android.app.PendingIntent;
-import android.app.admin.DeviceAdminInfo;
-import android.app.admin.DevicePolicyManagerInternal;
import android.content.BroadcastReceiver;
import android.content.ContentResolver;
import android.content.Context;
@@ -97,9 +93,7 @@
import android.os.INetworkManagementService;
import android.os.Message;
import android.os.PowerManager;
-import android.os.Process;
import android.os.RemoteException;
-import android.os.ServiceManager;
import android.os.SystemClock;
import android.os.UserHandle;
import android.provider.Settings;
@@ -122,7 +116,6 @@
import com.android.internal.util.FileRotator;
import com.android.internal.util.IndentingPrintWriter;
import com.android.server.EventLogTags;
-import com.android.server.LocalServices;
import com.android.server.connectivity.Tethering;
import java.io.File;
@@ -484,18 +477,22 @@
@Override
public int[] getRelevantUids() {
- enforcePermissionForManagedAdmin(mCallingPackage);
- return getUidComplete().getRelevantUids();
+ return getUidComplete().getRelevantUids(checkAccessLevel(mCallingPackage));
}
@Override
public NetworkStats getDeviceSummaryForNetwork(NetworkTemplate template, long start,
long end) {
- enforcePermission(mCallingPackage);
+ @NetworkStatsAccess.Level int accessLevel = checkAccessLevel(mCallingPackage);
+ if (accessLevel < NetworkStatsAccess.Level.DEVICE) {
+ throw new SecurityException("Calling package " + mCallingPackage
+ + " cannot access device-level network stats");
+ }
NetworkStats result = new NetworkStats(end - start, 1);
final long ident = Binder.clearCallingIdentity();
try {
- result.combineAllValues(internalGetSummaryForNetwork(template, start, end));
+ result.combineAllValues(
+ internalGetSummaryForNetwork(template, start, end, accessLevel));
} finally {
Binder.restoreCallingIdentity(ident);
}
@@ -505,23 +502,25 @@
@Override
public NetworkStats getSummaryForNetwork(
NetworkTemplate template, long start, long end) {
- enforcePermission(mCallingPackage);
- return internalGetSummaryForNetwork(template, start, end);
+ @NetworkStatsAccess.Level int accessLevel = checkAccessLevel(mCallingPackage);
+ return internalGetSummaryForNetwork(template, start, end, accessLevel);
}
@Override
public NetworkStatsHistory getHistoryForNetwork(NetworkTemplate template, int fields) {
- return internalGetHistoryForNetwork(template, fields);
+ @NetworkStatsAccess.Level int accessLevel = checkAccessLevel(mCallingPackage);
+ return internalGetHistoryForNetwork(template, fields, accessLevel);
}
@Override
public NetworkStats getSummaryForAllUid(
NetworkTemplate template, long start, long end, boolean includeTags) {
- enforcePermissionForManagedAdmin(mCallingPackage);
- final NetworkStats stats = getUidComplete().getSummary(template, start, end);
+ @NetworkStatsAccess.Level int accessLevel = checkAccessLevel(mCallingPackage);
+ final NetworkStats stats =
+ getUidComplete().getSummary(template, start, end, accessLevel);
if (includeTags) {
final NetworkStats tagStats = getUidTagComplete()
- .getSummary(template, start, end);
+ .getSummary(template, start, end, accessLevel);
stats.combineAllValues(tagStats);
}
return stats;
@@ -530,11 +529,13 @@
@Override
public NetworkStatsHistory getHistoryForUid(
NetworkTemplate template, int uid, int set, int tag, int fields) {
- enforcePermissionForManagedAdmin(mCallingPackage);
+ @NetworkStatsAccess.Level int accessLevel = checkAccessLevel(mCallingPackage);
if (tag == TAG_NONE) {
- return getUidComplete().getHistory(template, uid, set, tag, fields);
+ return getUidComplete().getHistory(template, uid, set, tag, fields,
+ accessLevel);
} else {
- return getUidTagComplete().getHistory(template, uid, set, tag, fields);
+ return getUidTagComplete().getHistory(template, uid, set, tag, fields,
+ accessLevel);
}
}
@@ -542,12 +543,13 @@
public NetworkStatsHistory getHistoryIntervalForUid(
NetworkTemplate template, int uid, int set, int tag, int fields,
long start, long end) {
- enforcePermissionForManagedAdmin(mCallingPackage);
+ @NetworkStatsAccess.Level int accessLevel = checkAccessLevel(mCallingPackage);
if (tag == TAG_NONE) {
- return getUidComplete().getHistory(template, uid, set, tag, fields, start, end);
+ return getUidComplete().getHistory(template, uid, set, tag, fields, start, end,
+ accessLevel);
} else {
return getUidTagComplete().getHistory(template, uid, set, tag, fields,
- start, end);
+ start, end, accessLevel);
}
}
@@ -559,80 +561,42 @@
};
}
- private boolean hasAppOpsPermission(String callingPackage) {
- final int callingUid = Binder.getCallingUid();
- boolean appOpsAllow = false;
- if (callingPackage != null) {
- AppOpsManager appOps = (AppOpsManager) mContext.getSystemService(
- Context.APP_OPS_SERVICE);
-
- final int mode = appOps.checkOp(AppOpsManager.OP_GET_USAGE_STATS,
- callingUid, callingPackage);
- if (mode == AppOpsManager.MODE_DEFAULT) {
- // The default behavior here is to check if PackageManager has given the app
- // permission.
- final int permissionCheck = mContext.checkCallingPermission(
- Manifest.permission.PACKAGE_USAGE_STATS);
- appOpsAllow = permissionCheck == PackageManager.PERMISSION_GRANTED;
- }
- appOpsAllow = (mode == AppOpsManager.MODE_ALLOWED);
- }
- return appOpsAllow;
+ private @NetworkStatsAccess.Level int checkAccessLevel(String callingPackage) {
+ return NetworkStatsAccess.checkAccessLevel(
+ mContext, Binder.getCallingUid(), callingPackage);
}
- private void enforcePermissionForManagedAdmin(String callingPackage) {
- boolean hasPermission = hasAppOpsPermission(callingPackage);
- if (!hasPermission) {
- // Profile and device owners are exempt from permission checking.
- final int callingUid = Binder.getCallingUid();
- final DevicePolicyManagerInternal dpmi = LocalServices.getService(
- DevicePolicyManagerInternal.class);
-
- // Device owners are also profile owners so it is enough to check for that.
- if (dpmi != null && dpmi.isActiveAdminWithPolicy(callingUid,
- DeviceAdminInfo.USES_POLICY_PROFILE_OWNER)) {
- return;
- }
- }
- if (!hasPermission) {
- mContext.enforceCallingOrSelfPermission(READ_NETWORK_USAGE_HISTORY, TAG);
- }
- }
-
- private void enforcePermission(String callingPackage) {
- boolean appOpsAllow = hasAppOpsPermission(callingPackage);
- if (!appOpsAllow) {
- mContext.enforceCallingOrSelfPermission(READ_NETWORK_USAGE_HISTORY, TAG);
- }
- }
-
-
/**
* Return network summary, splicing between DEV and XT stats when
* appropriate.
*/
private NetworkStats internalGetSummaryForNetwork(
- NetworkTemplate template, long start, long end) {
+ NetworkTemplate template, long start, long end,
+ @NetworkStatsAccess.Level int accessLevel) {
// We've been using pure XT stats long enough that we no longer need to
// splice DEV and XT together.
- return mXtStatsCached.getSummary(template, start, end);
+ return mXtStatsCached.getSummary(template, start, end, accessLevel);
}
/**
* Return network history, splicing between DEV and XT stats when
* appropriate.
*/
- private NetworkStatsHistory internalGetHistoryForNetwork(NetworkTemplate template, int fields) {
+ private NetworkStatsHistory internalGetHistoryForNetwork(NetworkTemplate template, int fields,
+ @NetworkStatsAccess.Level int accessLevel) {
// We've been using pure XT stats long enough that we no longer need to
// splice DEV and XT together.
- return mXtStatsCached.getHistory(template, UID_ALL, SET_ALL, TAG_NONE, fields);
+ return mXtStatsCached.getHistory(template, UID_ALL, SET_ALL, TAG_NONE, fields, accessLevel);
}
@Override
public long getNetworkTotalBytes(NetworkTemplate template, long start, long end) {
+ // Special case - since this is for internal use only, don't worry about a full access level
+ // check and just require the signature/privileged permission.
mContext.enforceCallingOrSelfPermission(READ_NETWORK_USAGE_HISTORY, TAG);
assertBandwidthControlEnabled();
- return internalGetSummaryForNetwork(template, start, end).getTotalBytes();
+ return internalGetSummaryForNetwork(template, start, end, NetworkStatsAccess.Level.DEVICE)
+ .getTotalBytes();
}
@Override
diff --git a/services/core/java/com/android/server/notification/ManagedServices.java b/services/core/java/com/android/server/notification/ManagedServices.java
index c7551c50..d5773690 100644
--- a/services/core/java/com/android/server/notification/ManagedServices.java
+++ b/services/core/java/com/android/server/notification/ManagedServices.java
@@ -454,7 +454,7 @@
newEnabled.addAll(userComponents);
for (int j = 0; j < userComponents.size(); j++) {
- final ComponentName component = userComponents.valueAt(i);
+ final ComponentName component = userComponents.valueAt(j);
newPackages.add(component.getPackageName());
}
}
diff --git a/services/core/java/com/android/server/pm/EphemeralResolverConnection.java b/services/core/java/com/android/server/pm/EphemeralResolverConnection.java
index 628ad0e..fe6fb1f 100644
--- a/services/core/java/com/android/server/pm/EphemeralResolverConnection.java
+++ b/services/core/java/com/android/server/pm/EphemeralResolverConnection.java
@@ -20,6 +20,7 @@
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
+import android.content.pm.EphemeralResolveInfo;
import android.os.Build;
import android.os.Bundle;
import android.os.IBinder;
@@ -30,7 +31,6 @@
import android.util.TimedRemoteCaller;
import com.android.internal.app.EphemeralResolverService;
-import com.android.internal.app.EphemeralResolveInfo;
import com.android.internal.app.IEphemeralResolver;
import java.io.FileDescriptor;
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 3a8a988..cad3b3f 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -105,6 +105,7 @@
import android.content.pm.ActivityInfo;
import android.content.pm.ApplicationInfo;
import android.content.pm.AppsQueryHelper;
+import android.content.pm.EphemeralResolveInfo;
import android.content.pm.EphemeralApplicationInfo;
import android.content.pm.FeatureInfo;
import android.content.pm.IOnPermissionsChangeListener;
@@ -144,6 +145,7 @@
import android.content.pm.VerificationParams;
import android.content.pm.VerifierDeviceIdentity;
import android.content.pm.VerifierInfo;
+import android.content.pm.EphemeralResolveInfo.EphemeralResolveIntentInfo;
import android.content.res.Resources;
import android.graphics.Bitmap;
import android.hardware.display.DisplayManager;
@@ -210,7 +212,7 @@
import libcore.util.EmptyArray;
import com.android.internal.R;
-import com.android.internal.app.EphemeralResolveInfo;
+import com.android.internal.annotations.GuardedBy;
import com.android.internal.app.IMediaContainerService;
import com.android.internal.app.ResolverActivity;
import com.android.internal.content.NativeLibraryHelper;
@@ -436,12 +438,6 @@
final String[] mSeparateProcesses;
final boolean mIsUpgrade;
- // This is where all application persistent data goes.
- final File mAppDataDir;
-
- // This is where all application persistent data goes for secondary users.
- final File mUserAppDataDir;
-
/** The location for ASEC container files on internal storage. */
final String mAsecInternalPath;
@@ -953,7 +949,7 @@
// Recordkeeping of restore-after-install operations that are currently in flight
// between the Package Manager and the Backup Manager
- class PostInstallData {
+ static class PostInstallData {
public InstallArgs args;
public PackageInstalledInfo res;
@@ -1070,7 +1066,7 @@
}
long timeInMillis;
try {
- timeInMillis = Long.parseLong(timeInMillisString.toString());
+ timeInMillis = Long.parseLong(timeInMillisString);
} catch (NumberFormatException e) {
throw new IOException("Failed to parse " + timeInMillisString
+ " as a long.", e);
@@ -1987,12 +1983,10 @@
Watchdog.getInstance().addThread(mHandler, WATCHDOG_TIMEOUT);
File dataDir = Environment.getDataDirectory();
- mAppDataDir = new File(dataDir, "data");
mAppInstallDir = new File(dataDir, "app");
mAppLib32InstallDir = new File(dataDir, "app-lib");
mEphemeralInstallDir = new File(dataDir, "app-ephemeral");
mAsecInternalPath = new File(dataDir, "app-asec").getPath();
- mUserAppDataDir = new File(dataDir, "user");
mDrmAppPrivateInstallDir = new File(dataDir, "app-private");
sUserManager = new UserManagerService(context, this, mPackages);
@@ -3368,14 +3362,6 @@
}
}
- private void checkValidCaller(int uid, int userId) {
- if (UserHandle.getUserId(uid) == userId || uid == Process.SYSTEM_UID || uid == 0)
- return;
-
- throw new SecurityException("Caller uid=" + uid
- + " is not privileged to communicate with user=" + userId);
- }
-
@Override
public int checkPermission(String permName, String pkgName, int userId) {
if (!sUserManager.exists(userId)) {
@@ -4102,10 +4088,12 @@
synchronized (mPackages) {
if (mProtectedBroadcasts.contains(actionName)) {
return true;
- } else if (actionName != null
- && actionName.startsWith("android.net.netmon.lingerExpired")) {
- // TODO: remove this terrible hack
- return true;
+ } else if (actionName != null) {
+ // TODO: remove these terrible hacks
+ if (actionName.startsWith("android.net.netmon.lingerExpired")
+ || actionName.startsWith("com.android.server.sip.SipWakeupTimer")) {
+ return true;
+ }
}
}
return false;
@@ -4432,7 +4420,21 @@
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);
+ final ResolveInfo bestChoice =
+ chooseBestActivity(intent, resolvedType, flags, query, userId);
+
+ if (isEphemeralAllowed(intent, query, userId)) {
+ final EphemeralResolveInfo ai =
+ getEphemeralResolveInfo(intent, resolvedType, userId);
+ if (ai != null) {
+ if (DEBUG_EPHEMERAL) {
+ Slog.v(TAG, "Returning an EphemeralResolveInfo");
+ }
+ bestChoice.ephemeralInstaller = mEphemeralInstallerInfo;
+ bestChoice.ephemeralResolveInfo = ai;
+ }
+ }
+ return bestChoice;
}
@Override
@@ -4467,13 +4469,61 @@
false, false, false, userId);
}
- private boolean isEphemeralAvailable(Intent intent, String resolvedType, int userId) {
+
+ private boolean isEphemeralAllowed(
+ Intent intent, List<ResolveInfo> resolvedActivites, int userId) {
+ // Short circuit and return early if possible.
+ final int callingUser = UserHandle.getCallingUserId();
+ if (callingUser != UserHandle.USER_SYSTEM) {
+ return false;
+ }
+ if (mEphemeralResolverConnection == null) {
+ return false;
+ }
+ if (intent.getComponent() != null) {
+ return false;
+ }
+ if (intent.getPackage() != null) {
+ return false;
+ }
+ final boolean isWebUri = hasWebURI(intent);
+ if (!isWebUri) {
+ return false;
+ }
+ // Deny ephemeral apps if the user chose _ALWAYS or _ALWAYS_ASK for intent resolution.
+ synchronized (mPackages) {
+ final int count = resolvedActivites.size();
+ for (int n = 0; n < count; n++) {
+ ResolveInfo info = resolvedActivites.get(n);
+ String packageName = info.activityInfo.packageName;
+ PackageSetting ps = mSettings.mPackages.get(packageName);
+ if (ps != null) {
+ // Try to get the status from User settings first
+ long packedStatus = getDomainVerificationStatusLPr(ps, userId);
+ int status = (int) (packedStatus >> 32);
+ if (status == INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS
+ || status == INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS_ASK) {
+ if (DEBUG_EPHEMERAL) {
+ Slog.v(TAG, "DENY ephemeral apps;"
+ + " pkg: " + packageName + ", status: " + status);
+ }
+ return false;
+ }
+ }
+ }
+ }
+ // We've exhausted all ways to deny ephemeral application; let the system look for them.
+ return true;
+ }
+
+ private EphemeralResolveInfo getEphemeralResolveInfo(Intent intent, String resolvedType,
+ int userId) {
MessageDigest digest = null;
try {
digest = MessageDigest.getInstance(EphemeralResolveInfo.SHA_ALGORITHM);
} catch (NoSuchAlgorithmException e) {
// If we can't create a digest, ignore ephemeral apps.
- return false;
+ return null;
}
final byte[] hostBytes = intent.getData().getHost().getBytes();
@@ -4487,7 +4537,7 @@
mEphemeralResolverConnection.getEphemeralResolveInfoList(shaPrefix);
if (ephemeralResolveInfoList == null || ephemeralResolveInfoList.size() == 0) {
// No hash prefix match; there are no ephemeral apps for this domain.
- return false;
+ return null;
}
for (int i = ephemeralResolveInfoList.size() - 1; i >= 0; --i) {
EphemeralResolveInfo ephemeralApplication = ephemeralResolveInfoList.get(i);
@@ -4502,62 +4552,22 @@
// We have a domain match; resolve the filters to see if anything matches.
final EphemeralIntentResolver ephemeralResolver = new EphemeralIntentResolver();
for (int j = filters.size() - 1; j >= 0; --j) {
- ephemeralResolver.addFilter(filters.get(j));
+ final EphemeralResolveIntentInfo intentInfo =
+ new EphemeralResolveIntentInfo(filters.get(j), ephemeralApplication);
+ ephemeralResolver.addFilter(intentInfo);
}
- List<ResolveInfo> ephemeralResolveList = ephemeralResolver.queryIntent(
+ List<EphemeralResolveInfo> matchedResolveInfoList = ephemeralResolver.queryIntent(
intent, resolvedType, false /*defaultOnly*/, userId);
- return !ephemeralResolveList.isEmpty();
+ if (!matchedResolveInfoList.isEmpty()) {
+ return matchedResolveInfoList.get(0);
+ }
}
// Hash or filter mis-match; no ephemeral apps for this domain.
- return false;
+ return null;
}
private ResolveInfo chooseBestActivity(Intent intent, String resolvedType,
int flags, List<ResolveInfo> query, int userId) {
- final boolean isWebUri = hasWebURI(intent);
- // Check whether or not an ephemeral app exists to handle the URI.
- if (isWebUri && mEphemeralResolverConnection != null) {
- // Deny ephemeral apps if the user choose _ALWAYS or _ALWAYS_ASK for intent resolution.
- boolean hasAlwaysHandler = false;
- synchronized (mPackages) {
- final int count = query.size();
- for (int n=0; n<count; n++) {
- ResolveInfo info = query.get(n);
- String packageName = info.activityInfo.packageName;
- PackageSetting ps = mSettings.mPackages.get(packageName);
- if (ps != null) {
- // Try to get the status from User settings first
- long packedStatus = getDomainVerificationStatusLPr(ps, userId);
- int status = (int) (packedStatus >> 32);
- if (status == INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS
- || status == INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS_ASK) {
- hasAlwaysHandler = true;
- break;
- }
- }
- }
- }
-
- // Only consider installing an ephemeral app if there isn't already a verified handler.
- // We've determined that there's an ephemeral app available for the URI, ignore any
- // ResolveInfo's and just return the ephemeral installer
- if (!hasAlwaysHandler && isEphemeralAvailable(intent, resolvedType, userId)) {
- if (DEBUG_EPHEMERAL) {
- Slog.v(TAG, "Resolving to the ephemeral installer");
- }
- // ditch the result and return a ResolveInfo to launch the ephemeral installer
- ResolveInfo ri = new ResolveInfo(mEphemeralInstallerInfo);
- ri.activityInfo = new ActivityInfo(ri.activityInfo);
- // make a deep copy of the applicationInfo
- ri.activityInfo.applicationInfo = new ApplicationInfo(
- ri.activityInfo.applicationInfo);
- if (userId != 0) {
- ri.activityInfo.applicationInfo.uid = UserHandle.getUid(userId,
- UserHandle.getAppId(ri.activityInfo.applicationInfo.uid));
- }
- return ri;
- }
- }
if (query != null) {
final int N = query.size();
if (N == 1) {
@@ -4573,7 +4583,7 @@
+ r1.activityInfo.name + "=" + r1.priority);
}
// If the first activity has a higher priority, or a different
- // default, then it is always desireable to pick it.
+ // default, then it is always desirable to pick it.
if (r0.priority != r1.priority
|| r0.preferredOrder != r1.preferredOrder
|| r0.isDefault != r1.isDefault) {
@@ -9751,23 +9761,24 @@
}
private static final class EphemeralIntentResolver
- extends IntentResolver<IntentFilter, ResolveInfo> {
+ extends IntentResolver<EphemeralResolveIntentInfo, EphemeralResolveInfo> {
@Override
- protected IntentFilter[] newArray(int size) {
- return new IntentFilter[size];
+ protected EphemeralResolveIntentInfo[] newArray(int size) {
+ return new EphemeralResolveIntentInfo[size];
}
@Override
- protected boolean isPackageForFilter(String packageName, IntentFilter info) {
+ protected boolean isPackageForFilter(String packageName, EphemeralResolveIntentInfo info) {
return true;
}
@Override
- protected ResolveInfo newResult(IntentFilter info, int match, int userId) {
- if (!sUserManager.exists(userId)) return null;
- final ResolveInfo res = new ResolveInfo();
- res.filter = info;
- return res;
+ protected EphemeralResolveInfo newResult(EphemeralResolveIntentInfo info, int match,
+ int userId) {
+ if (!sUserManager.exists(userId)) {
+ return null;
+ }
+ return info.getEphemeralResolveInfo();
}
}
@@ -10497,7 +10508,7 @@
ArrayList<IntentFilter> result = new ArrayList<>();
for (int n=0; n<count; n++) {
PackageParser.Activity activity = pkg.activities.get(n);
- if (activity.intents != null || activity.intents.size() > 0) {
+ if (activity.intents != null && activity.intents.size() > 0) {
result.addAll(activity.intents);
}
}
@@ -10916,7 +10927,7 @@
}
}
- class MoveInfo {
+ static class MoveInfo {
final int moveId;
final String fromUuid;
final String toUuid;
@@ -12222,7 +12233,7 @@
}
}
- class PackageInstalledInfo {
+ static class PackageInstalledInfo {
String name;
int uid;
// The set of users that originally had this package installed.
@@ -13163,10 +13174,6 @@
}
}
- private static boolean isMultiArch(PackageSetting ps) {
- return (ps.pkgFlags & ApplicationInfo.FLAG_MULTIARCH) != 0;
- }
-
private static boolean isMultiArch(ApplicationInfo info) {
return (info.flags & ApplicationInfo.FLAG_MULTIARCH) != 0;
}
@@ -13179,10 +13186,6 @@
return (ps.pkgFlags & ApplicationInfo.FLAG_EXTERNAL_STORAGE) != 0;
}
- private static boolean isExternal(ApplicationInfo info) {
- return (info.flags & ApplicationInfo.FLAG_EXTERNAL_STORAGE) != 0;
- }
-
private static boolean isEphemeral(PackageParser.Package pkg) {
return pkg.applicationInfo.isEphemeralApp();
}
@@ -13866,7 +13869,7 @@
return ret;
}
- private final class ClearStorageConnection implements ServiceConnection {
+ private final static class ClearStorageConnection implements ServiceConnection {
IMediaContainerService mContainerService;
@Override
@@ -15116,7 +15119,9 @@
// First, verify that this is a valid class name.
PackageParser.Package pkg = pkgSetting.pkg;
if (pkg == null || !pkg.hasComponentClassName(className)) {
- if (pkg.applicationInfo.targetSdkVersion >= Build.VERSION_CODES.JELLY_BEAN) {
+ if (pkg != null &&
+ pkg.applicationInfo.targetSdkVersion >=
+ Build.VERSION_CODES.JELLY_BEAN) {
throw new IllegalArgumentException("Component class " + className
+ " does not exist in " + packageName);
} else {
@@ -17326,7 +17331,7 @@
}
}
- private final class OnPermissionChangeListeners extends Handler {
+ private final static class OnPermissionChangeListeners extends Handler {
private static final int MSG_ON_PERMISSIONS_CHANGED = 1;
private final RemoteCallbackList<IOnPermissionsChangeListener> mPermissionListeners =
diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java
index 0b59c16..f5da103 100644
--- a/services/core/java/com/android/server/pm/UserManagerService.java
+++ b/services/core/java/com/android/server/pm/UserManagerService.java
@@ -1123,7 +1123,7 @@
*/
private static final void checkManageUsersPermission(String message) {
final int uid = Binder.getCallingUid();
- if (uid != Process.SYSTEM_UID && uid != 0
+ if (!UserHandle.isSameApp(uid, Process.SYSTEM_UID) && uid != Process.ROOT_UID
&& ActivityManager.checkComponentPermission(
android.Manifest.permission.MANAGE_USERS,
uid, -1, true) != PackageManager.PERMISSION_GRANTED) {
@@ -1131,6 +1131,20 @@
}
}
+ /**
+ * Enforces that only the system UID or root's UID (on any user) can make certain calls to the
+ * UserManager.
+ *
+ * @param message used as message if SecurityException is thrown
+ * @throws SecurityException if the caller is not system or root
+ */
+ private static void checkSystemOrRoot(String message) {
+ final int uid = Binder.getCallingUid();
+ if (!UserHandle.isSameApp(uid, Process.SYSTEM_UID) && uid != Process.ROOT_UID) {
+ throw new SecurityException("Only system may: " + message);
+ }
+ }
+
private void writeBitmapLP(UserInfo info, Bitmap bitmap) {
try {
File dir = new File(mUsersDir, Integer.toString(info.id));
@@ -2071,7 +2085,7 @@
public Bundle getApplicationRestrictionsForUser(String packageName, int userId) {
if (UserHandle.getCallingUserId() != userId
|| !UserHandle.isSameApp(Binder.getCallingUid(), getUidForPackage(packageName))) {
- checkManageUsersPermission("get application restrictions for other users/apps");
+ checkSystemOrRoot("get application restrictions for other users/apps");
}
synchronized (mPackagesLock) {
// Read the restrictions from XML
@@ -2082,7 +2096,7 @@
@Override
public void setApplicationRestrictions(String packageName, Bundle restrictions,
int userId) {
- checkManageUsersPermission("set application restrictions");
+ checkSystemOrRoot("set application restrictions");
synchronized (mPackagesLock) {
if (restrictions == null || restrictions.isEmpty()) {
cleanAppRestrictionsForPackage(packageName, userId);
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index b9a9d6e..dff6e3f 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -3056,13 +3056,22 @@
hideRecentApps(true, false);
}
- // Handle keyboard language switching.
+ // Handle keyboard layout switching.
+ // TODO: Deprecate this behavior when we fully migrate to IME subtype-based layout rotation.
+ if (down && repeatCount == 0 && keyCode == KeyEvent.KEYCODE_SPACE
+ && ((metaState & KeyEvent.META_CTRL_MASK) != 0)) {
+ int direction = (metaState & KeyEvent.META_SHIFT_MASK) != 0 ? -1 : 1;
+ mWindowManagerFuncs.switchKeyboardLayout(event.getDeviceId(), direction);
+ return -1;
+ }
+
+ // Handle input method switching.
if (down && repeatCount == 0
&& (keyCode == KeyEvent.KEYCODE_LANGUAGE_SWITCH
|| (keyCode == KeyEvent.KEYCODE_SPACE
- && (metaState & KeyEvent.META_CTRL_MASK) != 0))) {
- int direction = (metaState & KeyEvent.META_SHIFT_MASK) != 0 ? -1 : 1;
- mWindowManagerFuncs.switchKeyboardLayout(event.getDeviceId(), direction);
+ && (metaState & KeyEvent.META_META_MASK) != 0))) {
+ final boolean forwardDirection = (metaState & KeyEvent.META_SHIFT_MASK) == 0;
+ mWindowManagerFuncs.switchInputMethod(forwardDirection);
return -1;
}
if (mLanguageSwitchKeyPressed && !down
diff --git a/services/core/java/com/android/server/trust/TrustManagerService.java b/services/core/java/com/android/server/trust/TrustManagerService.java
index 4b6db99..d888c56 100644
--- a/services/core/java/com/android/server/trust/TrustManagerService.java
+++ b/services/core/java/com/android/server/trust/TrustManagerService.java
@@ -53,7 +53,6 @@
import android.os.SystemClock;
import android.os.UserHandle;
import android.os.UserManager;
-import android.os.storage.StorageManager;
import android.provider.Settings;
import android.service.trust.TrustAgentService;
import android.util.ArraySet;
@@ -681,7 +680,9 @@
public boolean isDeviceSecure(int userId) throws RemoteException {
userId = ActivityManager.handleIncomingUser(getCallingPid(), getCallingUid(), userId,
false /* allowAll */, true /* requireFull */, "isDeviceSecure", null);
- userId = resolveProfileParent(userId);
+ if (!LockPatternUtils.isSeparateWorkChallengeEnabled()) {
+ userId = resolveProfileParent(userId);
+ }
long token = Binder.clearCallingIdentity();
try {
diff --git a/services/core/java/com/android/server/wm/AppWindowToken.java b/services/core/java/com/android/server/wm/AppWindowToken.java
index fd5c704..b49641f 100644
--- a/services/core/java/com/android/server/wm/AppWindowToken.java
+++ b/services/core/java/com/android/server/wm/AppWindowToken.java
@@ -19,6 +19,7 @@
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_STARTING;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ANIM;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_APP_TRANSITIONS;
+import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ADD_REMOVE;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_VISIBILITY;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_WINDOW_MOVEMENT;
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
@@ -122,21 +123,6 @@
// True if the windows associated with this token should be cropped to their stack bounds.
boolean mCropWindowsToStack;
- // 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 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) {
super(_service, _token.asBinder(),
@@ -392,6 +378,62 @@
}
}
+ void setReplacingWindows(boolean animate) {
+ if (DEBUG_ADD_REMOVE) Slog.d(TAG_WM, "Marking app token " + appWindowToken
+ + " with replacing windows.");
+
+ for (int i = allAppWindows.size() - 1; i >= 0; i--) {
+ final WindowState w = allAppWindows.get(i);
+ w.setReplacing(animate);
+ }
+ if (animate) {
+ // Set-up dummy animation so we can start treating windows associated with this
+ // token like they are in transition before the new app window is ready for us to
+ // run the real transition animation.
+ if (DEBUG_APP_TRANSITIONS) Slog.v(TAG_WM,
+ "setReplacingWindow() Setting dummy animation on: " + this);
+ mAppAnimator.setDummyAnimation();
+ }
+ }
+
+ void addWindow(WindowState w) {
+ for (int i = allAppWindows.size() - 1; i >= 0; i--) {
+ WindowState candidate = allAppWindows.get(i);
+ if (candidate.mWillReplaceWindow && candidate.mReplacingWindow == null &&
+ candidate.getWindowTag().equals(w.getWindowTag().toString())) {
+ candidate.mReplacingWindow = w;
+ }
+ }
+ allAppWindows.add(w);
+ }
+
+ boolean waitingForReplacement() {
+ for (int i = allAppWindows.size() -1; i >= 0; i--) {
+ WindowState candidate = allAppWindows.get(i);
+ if (candidate.mWillReplaceWindow) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ void clearTimedoutReplaceesLocked() {
+ for (int i = allAppWindows.size() - 1; i >= 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.
+ i = Math.min(i - 1, allAppWindows.size() - 1)) {
+ WindowState candidate = allAppWindows.get(i);
+ if (candidate.mWillReplaceWindow == false) {
+ continue;
+ }
+ candidate.mWillReplaceWindow = false;
+ service.removeWindowLocked(candidate);
+ }
+ }
+
@Override
void dump(PrintWriter pw, String prefix) {
super.dump(pw, prefix);
diff --git a/services/core/java/com/android/server/wm/TaskStack.java b/services/core/java/com/android/server/wm/TaskStack.java
index 49d9efe..b961879 100644
--- a/services/core/java/com/android/server/wm/TaskStack.java
+++ b/services/core/java/com/android/server/wm/TaskStack.java
@@ -144,6 +144,14 @@
return true;
}
+ boolean isFullscreenBounds(Rect bounds) {
+ if (mDisplayContent == null || bounds == null) {
+ return true;
+ }
+ mDisplayContent.getLogicalDisplayRect(mTmpRect);
+ return mTmpRect.equals(bounds);
+ }
+
private boolean setBounds(Rect bounds) {
boolean oldFullscreen = mFullscreen;
int rotation = Surface.ROTATION_0;
diff --git a/services/core/java/com/android/server/wm/WallpaperController.java b/services/core/java/com/android/server/wm/WallpaperController.java
index 3db9ae0..2e424d0 100644
--- a/services/core/java/com/android/server/wm/WallpaperController.java
+++ b/services/core/java/com/android/server/wm/WallpaperController.java
@@ -506,7 +506,7 @@
inFreeformSpace = stack != null && stack.mStackId == FREEFORM_WORKSPACE_STACK_ID;
}
- replacing = replacing || (w.mAppToken != null && w.mAppToken.mWillReplaceWindow);
+ replacing = replacing || w.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.
diff --git a/services/core/java/com/android/server/wm/WindowAnimator.java b/services/core/java/com/android/server/wm/WindowAnimator.java
index a6523a4..6a5183f 100644
--- a/services/core/java/com/android/server/wm/WindowAnimator.java
+++ b/services/core/java/com/android/server/wm/WindowAnimator.java
@@ -918,10 +918,6 @@
}
void requestRemovalOfReplacedWindows(WindowState win) {
- final AppWindowToken token = win.mAppToken;
- if (token != null && token.mWillReplaceWindow && token.mReplacingWindow == win) {
- token.mHasReplacedWindow = true;
- }
mRemoveReplacedWindows = true;
}
diff --git a/services/core/java/com/android/server/wm/WindowManagerDebugConfig.java b/services/core/java/com/android/server/wm/WindowManagerDebugConfig.java
index a49bb31..845100d 100644
--- a/services/core/java/com/android/server/wm/WindowManagerDebugConfig.java
+++ b/services/core/java/com/android/server/wm/WindowManagerDebugConfig.java
@@ -36,7 +36,7 @@
static final boolean DEBUG_RESIZE = false;
static final boolean DEBUG = false;
static final boolean DEBUG_ADD_REMOVE = false;
- static final boolean DEBUG_FOCUS = false;
+ static final boolean DEBUG_FOCUS = true;
static final boolean DEBUG_FOCUS_LIGHT = DEBUG_FOCUS || false;
static final boolean DEBUG_ANIM = false;
static final boolean DEBUG_KEYGUARD = false;
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 816cab7..456c416 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -186,6 +186,7 @@
import android.view.WindowManagerPolicy;
import android.view.WindowManagerPolicy.PointerEventListener;
import android.view.animation.Animation;
+import android.view.inputmethod.InputMethodManagerInternal;
import android.widget.Toast;
import com.android.internal.R;
@@ -268,6 +269,9 @@
/** Amount of time (in milliseconds) to delay before declaring a window freeze timeout. */
static final int WINDOW_FREEZE_TIMEOUT_DURATION = 2000;
+ /** Amount of time (in milliseconds) to delay before declaring a window replacement timeout. */
+ static final int WINDOW_REPLACEMENT_TIMEOUT_DURATION = 2000;
+
/** Amount of time to allow a last ANR message to exist before freeing the memory. */
static final int LAST_ANR_LIFETIME_DURATION_MSECS = 2 * 60 * 60 * 1000; // Two hours
/**
@@ -458,7 +462,6 @@
EmulatorDisplayOverlay mEmulatorDisplayOverlay;
final float[] mTmpFloats = new float[9];
- final Rect mTmpContentRect = new Rect();
boolean mDisplayReady;
boolean mSafeMode;
@@ -1350,10 +1353,7 @@
final AppWindowToken appToken = win.mAppToken;
if (appToken != null) {
if (addToToken) {
- appToken.allAppWindows.add(win);
- }
- if (appToken.mWillReplaceWindow) {
- appToken.mReplacingWindow = win;
+ appToken.addWindow(win);
}
}
}
@@ -2088,14 +2088,15 @@
}
private void prepareWindowReplacementTransition(AppWindowToken atoken) {
- if (atoken == null || !atoken.mWillReplaceWindow || !atoken.mAnimateReplacingWindow) {
+ if (atoken == null) {
return;
}
atoken.allDrawn = false;
WindowState replacedWindow = null;
for (int i = atoken.windows.size() - 1; i >= 0 && replacedWindow == null; i--) {
WindowState candidate = atoken.windows.get(i);
- if (candidate.mExiting) {
+ if (candidate.mExiting && candidate.mWillReplaceWindow
+ && candidate.mAnimateReplacingWindow) {
replacedWindow = candidate;
}
}
@@ -2195,7 +2196,7 @@
+ " app-animation="
+ (win.mAppToken != null ? win.mAppToken.mAppAnimator.animation : null)
+ " mWillReplaceWindow="
- + (win.mAppToken != null ? win.mAppToken.mWillReplaceWindow : false)
+ + win.mWillReplaceWindow
+ " inPendingTransaction="
+ (win.mAppToken != null ? win.mAppToken.inPendingTransaction : false)
+ " mDisplayFrozen=" + mDisplayFrozen);
@@ -2207,13 +2208,13 @@
// animation wouldn't be seen.
if (win.mHasSurface && okToDisplay()) {
final AppWindowToken appToken = win.mAppToken;
- if (appToken != null && appToken.mWillReplaceWindow) {
+ if (win.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_WM, "Preserving " + win + " until the new one is "
+ "added");
win.mExiting = true;
- appToken.mReplacingRemoveRequested = true;
+ win.mReplacingRemoveRequested = true;
Binder.restoreCallingIdentity(origId);
return;
}
@@ -4052,7 +4053,7 @@
// transition animation
// * or this is an opening app and windows are being replaced.
if (wtoken.hidden == visible || (wtoken.hidden && wtoken.mIsExiting) ||
- (visible && wtoken.mWillReplaceWindow)) {
+ (visible && wtoken.waitingForReplacement())) {
boolean changed = false;
if (DEBUG_APP_TRANSITIONS) Slog.v(
TAG_WM, "Changing app " + wtoken + " hidden=" + wtoken.hidden
@@ -4829,6 +4830,17 @@
}
}
+ /** Returns true if the input bounds corresponds to the fullscreen bounds the stack is on. */
+ public boolean isFullscreenBounds(int stackId, Rect bounds) {
+ synchronized (mWindowMap) {
+ final TaskStack stack = mStackIdToStack.get(stackId);
+ if (stack == null || bounds == null) {
+ return true;
+ }
+ return stack.isFullscreenBounds(bounds);
+ }
+ }
+
/**
* Re-sizes a stack and its containing tasks.
* @param stackId Id of stack to resize.
@@ -5297,6 +5309,16 @@
// Called by window manager policy. Not exposed externally.
@Override
+ public void switchInputMethod(boolean forwardDirection) {
+ final InputMethodManagerInternal inputMethodManagerInternal =
+ LocalServices.getService(InputMethodManagerInternal.class);
+ if (inputMethodManagerInternal != null) {
+ inputMethodManagerInternal.switchInputMethod(forwardDirection);
+ }
+ }
+
+ // Called by window manager policy. Not exposed externally.
+ @Override
public void shutdown(boolean confirm) {
ShutdownThread.shutdown(mContext, PowerManager.SHUTDOWN_USER_REQUESTED, confirm);
}
@@ -7493,6 +7515,8 @@
public static final int TWO_FINGER_SCROLL_START = 45;
public static final int SHOW_NON_RESIZEABLE_DOCK_TOAST = 46;
+ public static final int WINDOW_REPLACEMENT_TIMEOUT = 47;
+
/**
* Used to denote that an integer field in a message will not be used.
*/
@@ -8075,6 +8099,13 @@
toast.show();
}
break;
+ case WINDOW_REPLACEMENT_TIMEOUT: {
+ final AppWindowToken token = (AppWindowToken) msg.obj;
+ synchronized (mWindowMap) {
+ token.clearTimedoutReplaceesLocked();
+ }
+ }
+ break;
}
if (DEBUG_WINDOW_TRACE) {
Slog.v(TAG_WM, "handleMessage: exit");
@@ -8597,7 +8628,7 @@
final int numTokens = tokens.size();
for (int tokenNdx = 0; tokenNdx < numTokens; ++tokenNdx) {
final AppWindowToken wtoken = tokens.get(tokenNdx);
- if (wtoken.mIsExiting && !wtoken.mWillReplaceWindow) {
+ if (wtoken.mIsExiting && !wtoken.waitingForReplacement()) {
continue;
}
i = reAddAppWindowsLocked(displayContent, i, wtoken);
@@ -8706,8 +8737,8 @@
private void forceHigherLayerIfNeeded(WindowState w, WindowStateAnimator winAnimator,
AppWindowToken wtoken) {
boolean force = false;
- if (wtoken.mWillReplaceWindow && wtoken.mReplacingWindow != w
- && wtoken.mAnimateReplacingWindow) {
+
+ if (w.mWillReplaceWindow) {
// 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.
@@ -10223,26 +10254,20 @@
* @param token Application token for which the activity will be relaunched.
*/
public void setReplacingWindow(IBinder token, boolean animate) {
+ AppWindowToken appWindowToken = null;
synchronized (mWindowMap) {
- AppWindowToken appWindowToken = findAppWindowToken(token);
+ appWindowToken = findAppWindowToken(token);
if (appWindowToken == null || !appWindowToken.isVisible()) {
Slog.w(TAG_WM, "Attempted to set replacing window on non-existing app token " + token);
return;
}
- if (DEBUG_ADD_REMOVE) Slog.d(TAG_WM, "Marking app token " + appWindowToken
- + " as replacing window.");
- appWindowToken.mWillReplaceWindow = true;
- appWindowToken.mHasReplacedWindow = false;
- appWindowToken.mAnimateReplacingWindow = animate;
+ appWindowToken.setReplacingWindows(animate);
+ }
- if (animate) {
- // Set-up dummy animation so we can start treating windows associated with this
- // token like they are in transition before the new app window is ready for us to
- // run the real transition animation.
- if (DEBUG_APP_TRANSITIONS) Slog.v(TAG_WM,
- "setReplacingWindow() Setting dummy animation on: " + appWindowToken);
- appWindowToken.mAppAnimator.setDummyAnimation();
- }
+ if (appWindowToken != null) {
+ mH.removeMessages(H.WINDOW_REPLACEMENT_TIMEOUT);
+ mH.sendMessageDelayed(mH.obtainMessage(H.WINDOW_REPLACEMENT_TIMEOUT, appWindowToken),
+ WINDOW_REPLACEMENT_TIMEOUT_DURATION);
}
}
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index 5a589e3..e4a6806 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -75,6 +75,7 @@
import static android.view.WindowManager.LayoutParams.MATCH_PARENT;
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.PRIVATE_FLAG_WILL_NOT_REPLACE_ON_RELAUNCH;
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;
@@ -403,6 +404,18 @@
// used to start an entering animation earlier.
public boolean mSurfaceSaved = false;
+ // This window will be 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 mWillReplaceWindow = false;
+ // If true, the replaced window was already requested to be removed.
+ boolean mReplacingRemoveRequested = false;
+ // Whether the replacement of the window should trigger app transition animation.
+ boolean mAnimateReplacingWindow = false;
+ // 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 = null;
+
/**
* Wake lock for drawing.
* Even though it's slightly more expensive to do so, we will use a separate wake lock
@@ -580,8 +593,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.mWillReplaceWindow
- && (mExiting || !mAppToken.mReplacingRemoveRequested)) {
+ if (mWillReplaceWindow && (mExiting || !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
// want to apply any more changes to it, so it remains in this state until new window
@@ -1343,17 +1355,17 @@
}
void maybeRemoveReplacedWindow() {
- AppWindowToken token = mAppToken;
- 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--) {
- final WindowState win = token.allAppWindows.get(i);
+ if (mAppToken == null) {
+ return;
+ }
+ for (int i = mAppToken.allAppWindows.size() - 1; i >= 0; i--) {
+ final WindowState win = mAppToken.allAppWindows.get(i);
+ if (DEBUG_ADD_REMOVE) Slog.d(TAG, "Removing replaced window: " + win);
+ if (win.mWillReplaceWindow && win.mReplacingWindow == this) {
+ win.mWillReplaceWindow = false;
+ win.mAnimateReplacingWindow = false;
+ win.mReplacingRemoveRequested = false;
+ win.mReplacingWindow = null;
if (win.mExiting) {
mService.removeWindowInnerLocked(win);
}
@@ -2161,7 +2173,7 @@
+ " " + getWindowTag();
}
- private CharSequence getWindowTag() {
+ CharSequence getWindowTag() {
CharSequence tag = mAttrs.getTitle();
if (tag == null || tag.length() <= 0) {
tag = mAttrs.packageName;
@@ -2259,4 +2271,12 @@
boolean isChildWindow() {
return mAttachedWindow != null;
}
+
+ void setReplacing(boolean animate) {
+ if ((mAttrs.privateFlags & PRIVATE_FLAG_WILL_NOT_REPLACE_ON_RELAUNCH) == 0) {
+ mWillReplaceWindow = true;
+ mReplacingWindow = null;
+ mAnimateReplacingWindow = animate;
+ }
+ }
}
diff --git a/services/core/java/com/android/server/wm/WindowStateAnimator.java b/services/core/java/com/android/server/wm/WindowStateAnimator.java
index cd82a5f..d4001cd 100644
--- a/services/core/java/com/android/server/wm/WindowStateAnimator.java
+++ b/services/core/java/com/android/server/wm/WindowStateAnimator.java
@@ -1192,7 +1192,7 @@
// 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 (isAnimating() && (appToken.mWillReplaceWindow || w.inDockedWorkspace()
+ if (isAnimating() && (w.mWillReplaceWindow || w.inDockedWorkspace()
|| w.inFreeformWorkspace())) {
return;
}
diff --git a/services/core/java/com/android/server/wm/WindowSurfaceController.java b/services/core/java/com/android/server/wm/WindowSurfaceController.java
index bd2912d..9675d2f 100644
--- a/services/core/java/com/android/server/wm/WindowSurfaceController.java
+++ b/services/core/java/com/android/server/wm/WindowSurfaceController.java
@@ -131,13 +131,9 @@
}
void destroyInTransaction() {
- if (SHOW_TRANSACTIONS || SHOW_SURFACE_ALLOC) {
- RuntimeException e = null;
- if (!HIDE_STACK_CRAWLS) {
- e = new RuntimeException();
- e.fillInStackTrace();
- }
- }
+ // if (SHOW_TRANSACTIONS || SHOW_SURFACE_ALLOC) {
+ Slog.i(TAG, "Destroying surface " + this + " called by " + Debug.getCallers(3));
+ // }
try {
mSurfaceControl.destroy();
mSurfaceShown = false;
diff --git a/services/core/java/com/android/server/wm/WindowSurfacePlacer.java b/services/core/java/com/android/server/wm/WindowSurfacePlacer.java
index 50bdf25..160c97f 100644
--- a/services/core/java/com/android/server/wm/WindowSurfacePlacer.java
+++ b/services/core/java/com/android/server/wm/WindowSurfacePlacer.java
@@ -96,6 +96,7 @@
private long mUserActivityTimeout = -1;
private boolean mUpdateRotation = false;
private final Rect mTmpStartRect = new Rect();
+ private final Rect mTmpContentRect = new Rect();
// Set to true when the display contains content to show the user.
// When false, the display manager may choose to mirror or blank the display.
@@ -862,8 +863,8 @@
mService.mScreenRect.set(0, 0, dw, dh);
}
- mService.mPolicy.getContentRectLw(mService.mTmpContentRect);
- displayContent.resize(mService.mTmpContentRect);
+ mService.mPolicy.getContentRectLw(mTmpContentRect);
+ displayContent.resize(mTmpContentRect);
int seq = mService.mLayoutSeq+1;
if (seq < 0) seq = 0;
@@ -1192,7 +1193,7 @@
// 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) {
+ if (!win.mWillReplaceWindow) {
win.mExiting = false;
}
if (win.mWinAnimator.mAnimLayer > layer) {
diff --git a/services/core/jni/com_android_server_input_InputManagerService.cpp b/services/core/jni/com_android_server_input_InputManagerService.cpp
index 9c9a5da..4c8474a 100644
--- a/services/core/jni/com_android_server_input_InputManagerService.cpp
+++ b/services/core/jni/com_android_server_input_InputManagerService.cpp
@@ -206,6 +206,7 @@
void reloadCalibration();
void setPointerIconShape(int32_t iconId);
void reloadPointerIcons();
+ void setCustomPointerIcon(const SpriteIcon& icon);
/* --- InputReaderPolicyInterface implementation --- */
@@ -248,6 +249,7 @@
virtual void loadAdditionalMouseResources(std::map<int32_t, SpriteIcon>* outResources,
std::map<int32_t, PointerAnimation>* outAnimationResources);
virtual int32_t getDefaultPointerIconId();
+ virtual int32_t getCustomPointerIconId();
private:
sp<InputManager> mInputManager;
@@ -790,6 +792,14 @@
}
}
+void NativeInputManager::setCustomPointerIcon(const SpriteIcon& icon) {
+ AutoMutex _l(mLock);
+ sp<PointerController> controller = mLocked.pointerController.promote();
+ if (controller != NULL) {
+ controller->setCustomPointerIcon(icon);
+ }
+}
+
TouchAffineTransformation NativeInputManager::getTouchAffineTransformation(
JNIEnv *env, jfloatArray matrixArr) {
ScopedFloatArrayRO matrix(env, matrixArr);
@@ -1090,6 +1100,10 @@
return POINTER_ICON_STYLE_ARROW;
}
+int32_t NativeInputManager::getCustomPointerIconId() {
+ return POINTER_ICON_STYLE_CUSTOM;
+}
+
// ----------------------------------------------------------------------------
static jlong nativeInit(JNIEnv* env, jclass /* clazz */,
@@ -1437,6 +1451,20 @@
im->reloadPointerIcons();
}
+static void nativeSetCustomPointerIcon(JNIEnv* env, jclass /* clazz */,
+ jlong ptr, jobject iconObj) {
+ NativeInputManager* im = reinterpret_cast<NativeInputManager*>(ptr);
+
+ PointerIcon pointerIcon;
+ android_view_PointerIcon_getLoadedIcon(env, iconObj, &pointerIcon);
+
+ SpriteIcon spriteIcon;
+ pointerIcon.bitmap.copyTo(&spriteIcon.bitmap, kN32_SkColorType);
+ spriteIcon.hotSpotX = pointerIcon.hotSpotX;
+ spriteIcon.hotSpotY = pointerIcon.hotSpotY;
+ im->setCustomPointerIcon(spriteIcon);
+}
+
// ----------------------------------------------------------------------------
static const JNINativeMethod gInputManagerMethods[] = {
@@ -1499,6 +1527,8 @@
(void*) nativeSetPointerIconShape },
{ "nativeReloadPointerIcons", "(J)V",
(void*) nativeReloadPointerIcons },
+ { "nativeSetCustomPointerIcon", "(JLandroid/view/PointerIcon;)V",
+ (void*) nativeSetCustomPointerIcon },
};
#define FIND_CLASS(var, className) \
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index c540e05..bdaf0ab 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -3681,7 +3681,7 @@
public void choosePrivateKeyAlias(final int uid, final Uri uri, final String alias,
final IBinder response) {
// Caller UID needs to be trusted, so we restrict this method to SYSTEM_UID callers.
- if (UserHandle.getAppId(mInjector.binderGetCallingUid()) != Process.SYSTEM_UID) {
+ if (!UserHandle.isSameApp(mInjector.binderGetCallingUid(), Process.SYSTEM_UID)) {
return;
}
@@ -4991,7 +4991,7 @@
mContext.enforceCallingOrSelfPermission(
android.Manifest.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS, null);
if (hasUserSetupCompleted(userHandle)
- && UserHandle.getAppId(callingUid) != Process.SYSTEM_UID) {
+ && !UserHandle.isSameApp(callingUid, Process.SYSTEM_UID)) {
throw new IllegalStateException("Cannot set the profile owner on a user which is "
+ "already set-up");
}
@@ -5051,7 +5051,8 @@
private void enforceManageUsers() {
final int callingUid = mInjector.binderGetCallingUid();
- if (!(UserHandle.isSameApp(callingUid, Process.SYSTEM_UID) || callingUid == 0)) {
+ if (!(UserHandle.isSameApp(callingUid, Process.SYSTEM_UID)
+ || callingUid == Process.ROOT_UID)) {
mContext.enforceCallingOrSelfPermission(android.Manifest.permission.MANAGE_USERS, null);
}
}
@@ -5062,7 +5063,8 @@
}
final int callingUid = mInjector.binderGetCallingUid();
if (userHandle == UserHandle.getUserId(callingUid)) return;
- if (!(UserHandle.isSameApp(callingUid, Process.SYSTEM_UID) || callingUid == 0)) {
+ if (!(UserHandle.isSameApp(callingUid, Process.SYSTEM_UID)
+ || callingUid == Process.ROOT_UID)) {
mContext.enforceCallingOrSelfPermission(
android.Manifest.permission.INTERACT_ACROSS_USERS_FULL, "Must be system or have"
+ " INTERACT_ACROSS_USERS_FULL permission");
@@ -5292,7 +5294,7 @@
@Override
public ComponentName getRestrictionsProvider(int userHandle) {
synchronized (this) {
- if (mInjector.binderGetCallingUid() != Process.SYSTEM_UID) {
+ if (!UserHandle.isSameApp(mInjector.binderGetCallingUid(), Process.SYSTEM_UID)) {
throw new SecurityException("Only the system can query the permission provider");
}
DevicePolicyData userData = getUserData(userHandle);
@@ -6303,7 +6305,7 @@
@Override
public void notifyLockTaskModeChanged(boolean isEnabled, String pkg, int userHandle) {
- if (mInjector.binderGetCallingUid() != Process.SYSTEM_UID) {
+ if (!UserHandle.isSameApp(mInjector.binderGetCallingUid(), Process.SYSTEM_UID)) {
throw new SecurityException("notifyLockTaskModeChanged can only be called by system");
}
synchronized (this) {
diff --git a/services/tests/servicestests/src/com/android/server/NetworkStatsServiceTest.java b/services/tests/servicestests/src/com/android/server/NetworkStatsServiceTest.java
index c12f978..204bc2e 100644
--- a/services/tests/servicestests/src/com/android/server/NetworkStatsServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/NetworkStatsServiceTest.java
@@ -18,7 +18,6 @@
import static android.content.Intent.ACTION_UID_REMOVED;
import static android.content.Intent.EXTRA_UID;
-import static android.net.ConnectivityManager.CONNECTIVITY_ACTION;
import static android.net.ConnectivityManager.TYPE_MOBILE;
import static android.net.ConnectivityManager.TYPE_WIFI;
import static android.net.ConnectivityManager.TYPE_WIMAX;
@@ -49,6 +48,7 @@
import static org.easymock.EasyMock.isA;
import android.app.AlarmManager;
+import android.app.IAlarmListener;
import android.app.IAlarmManager;
import android.app.PendingIntent;
import android.content.Intent;
@@ -70,6 +70,7 @@
import android.test.suitebuilder.annotation.Suppress;
import android.util.TrustedTime;
+import com.android.internal.net.VpnInfo;
import com.android.server.net.NetworkStatsService;
import com.android.server.net.NetworkStatsService.NetworkStatsSettings;
import com.android.server.net.NetworkStatsService.NetworkStatsSettings.Config;
@@ -83,6 +84,9 @@
/**
* Tests for {@link NetworkStatsService}.
+ *
+ * TODO: This test is really brittle, largely due to overly-strict use of Easymock.
+ * Rewrite w/ Mockito.
*/
@LargeTest
public class NetworkStatsServiceTest extends AndroidTestCase {
@@ -143,7 +147,6 @@
expectCurrentTime();
expectDefaultSettings();
- expectNetworkStatsSummary(buildEmptyStats());
expectNetworkStatsUidDetail(buildEmptyStats());
expectSystemReady();
@@ -190,9 +193,10 @@
expectNetworkStatsSummary(buildEmptyStats());
expectNetworkStatsUidDetail(buildEmptyStats());
expectNetworkStatsPoll();
+ expectBandwidthControlCheck();
replay();
- mServiceContext.sendBroadcast(new Intent(CONNECTIVITY_ACTION));
+ mService.forceUpdateIfaces();
// verify service has empty history for wifi
assertNetworkTotal(sTemplateWifi, 0L, 0L, 0L, 0L, 0);
@@ -244,9 +248,10 @@
expectNetworkStatsSummary(buildEmptyStats());
expectNetworkStatsUidDetail(buildEmptyStats());
expectNetworkStatsPoll();
+ expectBandwidthControlCheck();
replay();
- mServiceContext.sendBroadcast(new Intent(CONNECTIVITY_ACTION));
+ mService.forceUpdateIfaces();
// verify service has empty history for wifi
assertNetworkTotal(sTemplateWifi, 0L, 0L, 0L, 0L, 0);
@@ -295,7 +300,6 @@
// boot through serviceReady() again
expectCurrentTime();
expectDefaultSettings();
- expectNetworkStatsSummary(buildEmptyStats());
expectNetworkStatsUidDetail(buildEmptyStats());
expectSystemReady();
@@ -335,9 +339,10 @@
expectNetworkStatsSummary(buildEmptyStats());
expectNetworkStatsUidDetail(buildEmptyStats());
expectNetworkStatsPoll();
+ expectBandwidthControlCheck();
replay();
- mServiceContext.sendBroadcast(new Intent(CONNECTIVITY_ACTION));
+ mService.forceUpdateIfaces();
verifyAndReset();
// modify some number on wifi, and trigger poll event
@@ -387,9 +392,10 @@
expectNetworkStatsSummary(buildEmptyStats());
expectNetworkStatsUidDetail(buildEmptyStats());
expectNetworkStatsPoll();
+ expectBandwidthControlCheck();
replay();
- mServiceContext.sendBroadcast(new Intent(CONNECTIVITY_ACTION));
+ mService.forceUpdateIfaces();
verifyAndReset();
// create some traffic on first network
@@ -429,9 +435,10 @@
.addValues(TEST_IFACE, UID_RED, SET_DEFAULT, 0xF00D, 512L, 4L, 512L, 4L, 0L)
.addValues(TEST_IFACE, UID_BLUE, SET_DEFAULT, TAG_NONE, 512L, 4L, 0L, 0L, 0L));
expectNetworkStatsPoll();
+ expectBandwidthControlCheck();
replay();
- mServiceContext.sendBroadcast(new Intent(CONNECTIVITY_ACTION));
+ mService.forceUpdateIfaces();
mServiceContext.sendBroadcast(new Intent(ACTION_NETWORK_STATS_POLL));
verifyAndReset();
@@ -475,9 +482,10 @@
expectNetworkStatsSummary(buildEmptyStats());
expectNetworkStatsUidDetail(buildEmptyStats());
expectNetworkStatsPoll();
+ expectBandwidthControlCheck();
replay();
- mServiceContext.sendBroadcast(new Intent(CONNECTIVITY_ACTION));
+ mService.forceUpdateIfaces();
verifyAndReset();
// create some traffic
@@ -544,9 +552,10 @@
expectNetworkStatsSummary(buildEmptyStats());
expectNetworkStatsUidDetail(buildEmptyStats());
expectNetworkStatsPoll();
+ expectBandwidthControlCheck();
replay();
- mServiceContext.sendBroadcast(new Intent(CONNECTIVITY_ACTION));
+ mService.forceUpdateIfaces();
verifyAndReset();
// create some traffic
@@ -578,9 +587,10 @@
.addValues(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, 1024L, 8L, 1024L, 8L, 0L)
.addValues(TEST_IFACE, UID_RED, SET_DEFAULT, 0xF00D, 512L, 4L, 512L, 4L, 0L));
expectNetworkStatsPoll();
+ expectBandwidthControlCheck();
replay();
- mServiceContext.sendBroadcast(new Intent(CONNECTIVITY_ACTION));
+ mService.forceUpdateIfaces();
mServiceContext.sendBroadcast(new Intent(ACTION_NETWORK_STATS_POLL));
verifyAndReset();
@@ -615,9 +625,10 @@
expectNetworkStatsSummary(buildEmptyStats());
expectNetworkStatsUidDetail(buildEmptyStats());
expectNetworkStatsPoll();
+ expectBandwidthControlCheck();
replay();
- mServiceContext.sendBroadcast(new Intent(CONNECTIVITY_ACTION));
+ mService.forceUpdateIfaces();
verifyAndReset();
// create some traffic for two apps
@@ -681,9 +692,10 @@
expectNetworkStatsSummary(buildEmptyStats());
expectNetworkStatsUidDetail(buildEmptyStats());
expectNetworkStatsPoll();
+ expectBandwidthControlCheck();
replay();
- mServiceContext.sendBroadcast(new Intent(CONNECTIVITY_ACTION));
+ mService.forceUpdateIfaces();
verifyAndReset();
// create some initial traffic
@@ -746,9 +758,10 @@
expectNetworkStatsSummary(buildEmptyStats());
expectNetworkStatsUidDetail(buildEmptyStats());
expectNetworkStatsPoll();
+ expectBandwidthControlCheck();
replay();
- mServiceContext.sendBroadcast(new Intent(CONNECTIVITY_ACTION));
+ mService.forceUpdateIfaces();
verifyAndReset();
// create some tethering traffic
@@ -778,65 +791,6 @@
}
- public void testReportXtOverDev() throws Exception {
- // bring mobile network online
- expectCurrentTime();
- expectDefaultSettings();
- expectNetworkState(buildMobile3gState(IMSI_1));
- expectNetworkStatsSummary(buildEmptyStats());
- expectNetworkStatsUidDetail(buildEmptyStats());
- expectNetworkStatsPoll();
-
- replay();
- mServiceContext.sendBroadcast(new Intent(CONNECTIVITY_ACTION));
- verifyAndReset();
-
- // create some traffic, but only for DEV, and across 1.5 buckets
- incrementCurrentTime(90 * MINUTE_IN_MILLIS);
- expectCurrentTime();
- expectDefaultSettings();
- expectNetworkStatsSummaryDev(new NetworkStats(getElapsedRealtime(), 1)
- .addIfaceValues(TEST_IFACE, 6000L, 60L, 3000L, 30L));
- expectNetworkStatsSummaryXt(buildEmptyStats());
- expectNetworkStatsUidDetail(buildEmptyStats());
- expectNetworkStatsPoll();
-
- replay();
- mServiceContext.sendBroadcast(new Intent(ACTION_NETWORK_STATS_POLL));
-
- // verify service recorded history:
- // 4000(dev) + 2000(dev)
- assertNetworkTotal(sTemplateImsi1, 6000L, 60L, 3000L, 30L, 0);
- verifyAndReset();
-
- // create traffic on both DEV and XT, across two buckets
- incrementCurrentTime(2 * HOUR_IN_MILLIS);
- expectCurrentTime();
- expectDefaultSettings();
- expectNetworkStatsSummaryDev(new NetworkStats(getElapsedRealtime(), 1)
- .addIfaceValues(TEST_IFACE, 6004L, 64L, 3004L, 34L));
- expectNetworkStatsSummaryXt(new NetworkStats(getElapsedRealtime(), 1)
- .addIfaceValues(TEST_IFACE, 10240L, 0L, 0L, 0L));
- expectNetworkStatsUidDetail(buildEmptyStats());
- expectNetworkStatsPoll();
-
- replay();
- mServiceContext.sendBroadcast(new Intent(ACTION_NETWORK_STATS_POLL));
-
- // verify that we switching reporting at the first atomic XT bucket,
- // which should give us:
- // 4000(dev) + 2000(dev) + 1(dev) + 5120(xt) + 2560(xt)
- assertNetworkTotal(sTemplateImsi1, 13681L, 61L, 3001L, 31L, 0);
-
- // also test pure-DEV and pure-XT ranges
- assertNetworkTotal(sTemplateImsi1, startTimeMillis(),
- startTimeMillis() + 2 * HOUR_IN_MILLIS, 6001L, 61L, 3001L, 31L, 0);
- assertNetworkTotal(sTemplateImsi1, startTimeMillis() + 2 * HOUR_IN_MILLIS,
- startTimeMillis() + 4 * HOUR_IN_MILLIS, 7680L, 0L, 0L, 0L, 0);
-
- verifyAndReset();
- }
-
private void assertNetworkTotal(NetworkTemplate template, long rxBytes, long rxPackets,
long txBytes, long txPackets, int operations) throws Exception {
assertNetworkTotal(template, Long.MIN_VALUE, Long.MAX_VALUE, rxBytes, rxPackets, txBytes,
@@ -876,19 +830,21 @@
}
private void expectSystemReady() throws Exception {
- mAlarmManager.remove(isA(PendingIntent.class), null);
+ mAlarmManager.remove(isA(PendingIntent.class), EasyMock.<IAlarmListener>isNull());
expectLastCall().anyTimes();
- mAlarmManager.set(getContext().getPackageName(),
+ mAlarmManager.set(eq(getContext().getPackageName()),
eq(AlarmManager.ELAPSED_REALTIME), anyLong(), anyLong(), anyLong(),
- anyInt(), isA(PendingIntent.class), null, null, isA(WorkSource.class),
- isA(AlarmManager.AlarmClockInfo.class));
- expectLastCall().atLeastOnce();
+ anyInt(), isA(PendingIntent.class), EasyMock.<IAlarmListener>isNull(),
+ EasyMock.<String>isNull(), EasyMock.<WorkSource>isNull(),
+ EasyMock.<AlarmManager.AlarmClockInfo>isNull());
+ expectLastCall().anyTimes();
mNetManager.setGlobalAlert(anyLong());
expectLastCall().atLeastOnce();
- expect(mNetManager.isBandwidthControlEnabled()).andReturn(true).atLeastOnce();
+ expectNetworkStatsSummary(buildEmptyStats());
+ expectBandwidthControlCheck();
}
private void expectNetworkState(NetworkState... state) throws Exception {
@@ -899,6 +855,8 @@
}
private void expectNetworkStatsSummary(NetworkStats summary) throws Exception {
+ expect(mConnManager.getAllVpnInfo()).andReturn(new VpnInfo[0]).atLeastOnce();
+
expectNetworkStatsSummaryDev(summary);
expectNetworkStatsSummaryXt(summary);
}
@@ -961,6 +919,10 @@
expectLastCall().anyTimes();
}
+ private void expectBandwidthControlCheck() throws Exception {
+ expect(mNetManager.isBandwidthControlEnabled()).andReturn(true).atLeastOnce();
+ }
+
private void assertStatsFilesExist(boolean exist) {
final File basePath = new File(mStatsDir, "netstats");
if (exist) {
diff --git a/services/tests/servicestests/src/com/android/server/net/NetworkStatsAccessTest.java b/services/tests/servicestests/src/com/android/server/net/NetworkStatsAccessTest.java
new file mode 100644
index 0000000..bb8f9d1
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/net/NetworkStatsAccessTest.java
@@ -0,0 +1,178 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.net;
+
+import static org.mockito.Mockito.when;
+
+import android.Manifest;
+import android.Manifest.permission;
+import android.app.AppOpsManager;
+import android.app.admin.DeviceAdminInfo;
+import android.app.admin.DevicePolicyManagerInternal;
+import android.content.Context;
+import android.content.pm.PackageManager;
+import android.telephony.TelephonyManager;
+
+import com.android.server.LocalServices;
+
+import junit.framework.TestCase;
+
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+public class NetworkStatsAccessTest extends TestCase {
+ private static final String TEST_PKG = "com.example.test";
+ private static final int TEST_UID = 12345;
+
+ @Mock private Context mContext;
+ @Mock private DevicePolicyManagerInternal mDpmi;
+ @Mock private TelephonyManager mTm;
+ @Mock private AppOpsManager mAppOps;
+
+ // Hold the real service so we can restore it when tearing down the test.
+ private DevicePolicyManagerInternal mSystemDpmi;
+
+ @Override
+ public void setUp() throws Exception {
+ super.setUp();
+ MockitoAnnotations.initMocks(this);
+
+ mSystemDpmi = LocalServices.getService(DevicePolicyManagerInternal.class);
+ LocalServices.removeServiceForTest(DevicePolicyManagerInternal.class);
+ LocalServices.addService(DevicePolicyManagerInternal.class, mDpmi);
+
+ when(mContext.getSystemService(Context.TELEPHONY_SERVICE)).thenReturn(mTm);
+ when(mContext.getSystemService(Context.APP_OPS_SERVICE)).thenReturn(mAppOps);
+ }
+
+ @Override
+ public void tearDown() throws Exception {
+ LocalServices.removeServiceForTest(DevicePolicyManagerInternal.class);
+ LocalServices.addService(DevicePolicyManagerInternal.class, mSystemDpmi);
+ super.tearDown();
+ }
+
+ public void testCheckAccessLevel_hasCarrierPrivileges() throws Exception {
+ setHasCarrierPrivileges(true);
+ setIsDeviceOwner(false);
+ setIsProfileOwner(false);
+ setHasAppOpsPermission(AppOpsManager.MODE_DEFAULT, false);
+ setHasReadHistoryPermission(false);
+ assertEquals(NetworkStatsAccess.Level.DEVICE,
+ NetworkStatsAccess.checkAccessLevel(mContext, TEST_UID, TEST_PKG));
+ }
+
+ public void testCheckAccessLevel_isDeviceOwner() throws Exception {
+ setHasCarrierPrivileges(false);
+ setIsDeviceOwner(true);
+ setIsProfileOwner(false);
+ setHasAppOpsPermission(AppOpsManager.MODE_DEFAULT, false);
+ setHasReadHistoryPermission(false);
+ assertEquals(NetworkStatsAccess.Level.DEVICE,
+ NetworkStatsAccess.checkAccessLevel(mContext, TEST_UID, TEST_PKG));
+ }
+
+ public void testCheckAccessLevel_isProfileOwner() throws Exception {
+ setHasCarrierPrivileges(false);
+ setIsDeviceOwner(false);
+ setIsProfileOwner(true);
+ setHasAppOpsPermission(AppOpsManager.MODE_DEFAULT, false);
+ setHasReadHistoryPermission(false);
+ assertEquals(NetworkStatsAccess.Level.USER,
+ NetworkStatsAccess.checkAccessLevel(mContext, TEST_UID, TEST_PKG));
+ }
+
+ public void testCheckAccessLevel_hasAppOpsBitAllowed() throws Exception {
+ setHasCarrierPrivileges(false);
+ setIsDeviceOwner(false);
+ setIsProfileOwner(true);
+ setHasAppOpsPermission(AppOpsManager.MODE_ALLOWED, false);
+ setHasReadHistoryPermission(false);
+ assertEquals(NetworkStatsAccess.Level.USER,
+ NetworkStatsAccess.checkAccessLevel(mContext, TEST_UID, TEST_PKG));
+ }
+
+ public void testCheckAccessLevel_hasAppOpsBitDefault_grantedPermission() throws Exception {
+ setHasCarrierPrivileges(false);
+ setIsDeviceOwner(false);
+ setIsProfileOwner(true);
+ setHasAppOpsPermission(AppOpsManager.MODE_DEFAULT, true);
+ setHasReadHistoryPermission(false);
+ assertEquals(NetworkStatsAccess.Level.USER,
+ NetworkStatsAccess.checkAccessLevel(mContext, TEST_UID, TEST_PKG));
+ }
+
+ public void testCheckAccessLevel_hasReadHistoryPermission() throws Exception {
+ setHasCarrierPrivileges(false);
+ setIsDeviceOwner(false);
+ setIsProfileOwner(true);
+ setHasAppOpsPermission(AppOpsManager.MODE_DEFAULT, false);
+ setHasReadHistoryPermission(true);
+ assertEquals(NetworkStatsAccess.Level.USER,
+ NetworkStatsAccess.checkAccessLevel(mContext, TEST_UID, TEST_PKG));
+ }
+
+ public void testCheckAccessLevel_deniedAppOpsBit() throws Exception {
+ setHasCarrierPrivileges(false);
+ setIsDeviceOwner(false);
+ setIsProfileOwner(false);
+ setHasAppOpsPermission(AppOpsManager.MODE_ERRORED, true);
+ setHasReadHistoryPermission(false);
+ assertEquals(NetworkStatsAccess.Level.DEFAULT,
+ NetworkStatsAccess.checkAccessLevel(mContext, TEST_UID, TEST_PKG));
+ }
+
+ public void testCheckAccessLevel_deniedAppOpsBit_deniedPermission() throws Exception {
+ setHasCarrierPrivileges(false);
+ setIsDeviceOwner(false);
+ setIsProfileOwner(false);
+ setHasAppOpsPermission(AppOpsManager.MODE_DEFAULT, false);
+ setHasReadHistoryPermission(false);
+ assertEquals(NetworkStatsAccess.Level.DEFAULT,
+ NetworkStatsAccess.checkAccessLevel(mContext, TEST_UID, TEST_PKG));
+ }
+
+ private void setHasCarrierPrivileges(boolean hasPrivileges) {
+ when(mTm.checkCarrierPrivilegesForPackage(TEST_PKG)).thenReturn(
+ hasPrivileges ? TelephonyManager.CARRIER_PRIVILEGE_STATUS_HAS_ACCESS
+ : TelephonyManager.CARRIER_PRIVILEGE_STATUS_NO_ACCESS);
+ }
+
+ private void setIsDeviceOwner(boolean isOwner) {
+ when(mDpmi.isActiveAdminWithPolicy(TEST_UID, DeviceAdminInfo.USES_POLICY_DEVICE_OWNER))
+ .thenReturn(isOwner);
+ }
+
+ private void setIsProfileOwner(boolean isOwner) {
+ when(mDpmi.isActiveAdminWithPolicy(TEST_UID, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER))
+ .thenReturn(isOwner);
+ }
+
+ private void setHasAppOpsPermission(int appOpsMode, boolean hasPermission) {
+ when(mAppOps.checkOp(AppOpsManager.OP_GET_USAGE_STATS, TEST_UID, TEST_PKG))
+ .thenReturn(appOpsMode);
+ when(mContext.checkCallingPermission(Manifest.permission.PACKAGE_USAGE_STATS)).thenReturn(
+ hasPermission ? PackageManager.PERMISSION_GRANTED
+ : PackageManager.PERMISSION_DENIED);
+ }
+
+ private void setHasReadHistoryPermission(boolean hasPermission) {
+ when(mContext.checkCallingOrSelfPermission(permission.READ_NETWORK_USAGE_HISTORY))
+ .thenReturn(hasPermission ? PackageManager.PERMISSION_GRANTED
+ : PackageManager.PERMISSION_DENIED);
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/net/NetworkStatsCollectionTest.java b/services/tests/servicestests/src/com/android/server/net/NetworkStatsCollectionTest.java
index 1a6c289..6026644 100644
--- a/services/tests/servicestests/src/com/android/server/net/NetworkStatsCollectionTest.java
+++ b/services/tests/servicestests/src/com/android/server/net/NetworkStatsCollectionTest.java
@@ -16,6 +16,7 @@
package com.android.server.net;
+import static android.net.ConnectivityManager.TYPE_MOBILE;
import static android.net.NetworkStats.SET_DEFAULT;
import static android.net.NetworkStats.TAG_NONE;
import static android.net.NetworkStats.UID_ALL;
@@ -24,9 +25,14 @@
import static android.text.format.DateUtils.MINUTE_IN_MILLIS;
import android.content.res.Resources;
+import android.net.NetworkIdentity;
import android.net.NetworkStats;
import android.net.NetworkTemplate;
+import android.os.Process;
+import android.os.UserHandle;
+import android.telephony.TelephonyManager;
import android.test.AndroidTestCase;
+import android.test.MoreAsserts;
import android.test.suitebuilder.annotation.MediumTest;
import com.android.frameworks.servicestests.R;
@@ -68,7 +74,7 @@
// verify that history read correctly
assertSummaryTotal(collection, buildTemplateMobileAll(TEST_IMSI),
- 636016770L, 709306L, 88038768L, 518836L);
+ 636016770L, 709306L, 88038768L, 518836L, NetworkStatsAccess.Level.DEVICE);
// now export into a unified format
final ByteArrayOutputStream bos = new ByteArrayOutputStream();
@@ -77,12 +83,12 @@
// clear structure completely
collection.reset();
assertSummaryTotal(collection, buildTemplateMobileAll(TEST_IMSI),
- 0L, 0L, 0L, 0L);
+ 0L, 0L, 0L, 0L, NetworkStatsAccess.Level.DEVICE);
// and read back into structure, verifying that totals are same
collection.read(new ByteArrayInputStream(bos.toByteArray()));
assertSummaryTotal(collection, buildTemplateMobileAll(TEST_IMSI),
- 636016770L, 709306L, 88038768L, 518836L);
+ 636016770L, 709306L, 88038768L, 518836L, NetworkStatsAccess.Level.DEVICE);
}
public void testReadLegacyUid() throws Exception {
@@ -94,7 +100,7 @@
// verify that history read correctly
assertSummaryTotal(collection, buildTemplateMobileAll(TEST_IMSI),
- 637076152L, 711413L, 88343717L, 521022L);
+ 637076152L, 711413L, 88343717L, 521022L, NetworkStatsAccess.Level.DEVICE);
// now export into a unified format
final ByteArrayOutputStream bos = new ByteArrayOutputStream();
@@ -103,12 +109,12 @@
// clear structure completely
collection.reset();
assertSummaryTotal(collection, buildTemplateMobileAll(TEST_IMSI),
- 0L, 0L, 0L, 0L);
+ 0L, 0L, 0L, 0L, NetworkStatsAccess.Level.DEVICE);
// and read back into structure, verifying that totals are same
collection.read(new ByteArrayInputStream(bos.toByteArray()));
assertSummaryTotal(collection, buildTemplateMobileAll(TEST_IMSI),
- 637076152L, 711413L, 88343717L, 521022L);
+ 637076152L, 711413L, 88343717L, 521022L, NetworkStatsAccess.Level.DEVICE);
}
public void testReadLegacyUidTags() throws Exception {
@@ -151,6 +157,66 @@
assertEquals(2 * HOUR_IN_MILLIS, collection.getEndMillis());
}
+ public void testAccessLevels() throws Exception {
+ final NetworkStatsCollection collection = new NetworkStatsCollection(HOUR_IN_MILLIS);
+ final NetworkStats.Entry entry = new NetworkStats.Entry();
+ final NetworkIdentitySet identSet = new NetworkIdentitySet();
+ identSet.add(new NetworkIdentity(TYPE_MOBILE, TelephonyManager.NETWORK_TYPE_UNKNOWN,
+ TEST_IMSI, null, false));
+
+ int myUid = Process.myUid();
+ int otherUidInSameUser = Process.myUid() + 1;
+ int uidInDifferentUser = Process.myUid() + UserHandle.PER_USER_RANGE;
+
+ // Record one entry for the current UID.
+ entry.rxBytes = 32;
+ collection.recordData(identSet, myUid, SET_DEFAULT, TAG_NONE, 0, 60 * MINUTE_IN_MILLIS,
+ entry);
+
+ // Record one entry for another UID in this user.
+ entry.rxBytes = 64;
+ collection.recordData(identSet, otherUidInSameUser, SET_DEFAULT, TAG_NONE, 0,
+ 60 * MINUTE_IN_MILLIS, entry);
+
+ // Record one entry for the system UID.
+ entry.rxBytes = 128;
+ collection.recordData(identSet, Process.SYSTEM_UID, SET_DEFAULT, TAG_NONE, 0,
+ 60 * MINUTE_IN_MILLIS, entry);
+
+ // Record one entry for a UID in a different user.
+ entry.rxBytes = 256;
+ collection.recordData(identSet, uidInDifferentUser, SET_DEFAULT, TAG_NONE, 0,
+ 60 * MINUTE_IN_MILLIS, entry);
+
+ // Verify the set of relevant UIDs for each access level.
+ MoreAsserts.assertEquals(new int[] { myUid },
+ collection.getRelevantUids(NetworkStatsAccess.Level.DEFAULT));
+ MoreAsserts.assertEquals(new int[] { Process.SYSTEM_UID, myUid, otherUidInSameUser },
+ collection.getRelevantUids(NetworkStatsAccess.Level.USER));
+ MoreAsserts.assertEquals(
+ new int[] { Process.SYSTEM_UID, myUid, otherUidInSameUser, uidInDifferentUser },
+ collection.getRelevantUids(NetworkStatsAccess.Level.DEVICE));
+
+ // Verify security check in getHistory.
+ assertNotNull(collection.getHistory(buildTemplateMobileAll(TEST_IMSI), myUid, SET_DEFAULT,
+ TAG_NONE, 0, NetworkStatsAccess.Level.DEFAULT));
+ try {
+ collection.getHistory(buildTemplateMobileAll(TEST_IMSI), otherUidInSameUser,
+ SET_DEFAULT, TAG_NONE, 0, NetworkStatsAccess.Level.DEFAULT);
+ fail("Should have thrown SecurityException for accessing different UID");
+ } catch (SecurityException e) {
+ // expected
+ }
+
+ // Verify appropriate aggregation in getSummary.
+ assertSummaryTotal(collection, buildTemplateMobileAll(TEST_IMSI), 32, 0, 0, 0,
+ NetworkStatsAccess.Level.DEFAULT);
+ assertSummaryTotal(collection, buildTemplateMobileAll(TEST_IMSI), 32 + 64 + 128, 0, 0, 0,
+ NetworkStatsAccess.Level.USER);
+ assertSummaryTotal(collection, buildTemplateMobileAll(TEST_IMSI), 32 + 64 + 128 + 256, 0, 0,
+ 0, NetworkStatsAccess.Level.DEVICE);
+ }
+
/**
* Copy a {@link Resources#openRawResource(int)} into {@link File} for
* testing purposes.
@@ -170,16 +236,19 @@
}
private static void assertSummaryTotal(NetworkStatsCollection collection,
- NetworkTemplate template, long rxBytes, long rxPackets, long txBytes, long txPackets) {
+ NetworkTemplate template, long rxBytes, long rxPackets, long txBytes, long txPackets,
+ @NetworkStatsAccess.Level int accessLevel) {
final NetworkStats.Entry entry = collection.getSummary(
- template, Long.MIN_VALUE, Long.MAX_VALUE).getTotal(null);
+ template, Long.MIN_VALUE, Long.MAX_VALUE, accessLevel)
+ .getTotal(null);
assertEntry(entry, rxBytes, rxPackets, txBytes, txPackets);
}
private static void assertSummaryTotalIncludingTags(NetworkStatsCollection collection,
NetworkTemplate template, long rxBytes, long rxPackets, long txBytes, long txPackets) {
final NetworkStats.Entry entry = collection.getSummary(
- template, Long.MIN_VALUE, Long.MAX_VALUE).getTotalIncludingTags(null);
+ template, Long.MIN_VALUE, Long.MAX_VALUE, NetworkStatsAccess.Level.DEVICE)
+ .getTotalIncludingTags(null);
assertEntry(entry, rxBytes, rxPackets, txBytes, txPackets);
}
diff --git a/tests/NetworkSecurityConfigTest/src/android/security/net/config/XmlConfigTests.java b/tests/NetworkSecurityConfigTest/src/android/security/net/config/XmlConfigTests.java
index 998bb68..35e3ef4 100644
--- a/tests/NetworkSecurityConfigTest/src/android/security/net/config/XmlConfigTests.java
+++ b/tests/NetworkSecurityConfigTest/src/android/security/net/config/XmlConfigTests.java
@@ -22,6 +22,7 @@
import android.util.ArraySet;
import android.util.Pair;
import java.io.IOException;
+import java.net.InetAddress;
import java.net.Socket;
import java.net.URL;
import java.security.KeyStore;
@@ -34,6 +35,7 @@
import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLHandshakeException;
+import javax.net.ssl.SSLSocket;
import javax.net.ssl.TrustManager;
import javax.net.ssl.TrustManagerFactory;
@@ -103,6 +105,15 @@
TestUtils.assertConnectionFails(context, "developer.android.com", 443);
TestUtils.assertUrlConnectionFails(context, "google.com", 443);
TestUtils.assertUrlConnectionSucceeds(context, "android.com", 443);
+ // Check that sockets created without the hostname fail with per-domain configs
+ SSLSocket socket = (SSLSocket) context.getSocketFactory()
+ .createSocket(InetAddress.getByName("android.com"), 443);
+ try {
+ socket.startHandshake();
+ socket.getInputStream();
+ fail();
+ } catch (IOException expected) {
+ }
}
public void testBasicPinning() throws Exception {
diff --git a/wifi/java/android/net/wifi/p2p/nsd/WifiP2pServiceResponse.java b/wifi/java/android/net/wifi/p2p/nsd/WifiP2pServiceResponse.java
index 0ddfa77..a0b5a6e 100644
--- a/wifi/java/android/net/wifi/p2p/nsd/WifiP2pServiceResponse.java
+++ b/wifi/java/android/net/wifi/p2p/nsd/WifiP2pServiceResponse.java
@@ -296,7 +296,7 @@
sbuf.append("serviceType:").append(mServiceType);
sbuf.append(" status:").append(Status.toString(mStatus));
sbuf.append(" srcAddr:").append(mDevice.deviceAddress);
- sbuf.append(" data:").append(mData);
+ sbuf.append(" data:").append(Arrays.toString(mData));
return sbuf.toString();
}