Merge "Add assert for required EGL extensions"
diff --git a/api/current.txt b/api/current.txt
index 4a1a458..55b5d52 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -30532,6 +30532,8 @@
field public static final int CAPABILITY_SIM_SUBSCRIPTION = 4; // 0x4
field public static final int CAPABILITY_VIDEO_CALLING = 8; // 0x8
field public static final android.os.Parcelable.Creator<android.telecom.PhoneAccount> CREATOR;
+ field public static final java.lang.String EXTRA_CALL_SUBJECT_CHARACTER_ENCODING = "android.telecom.extra.CALL_SUBJECT_CHARACTER_ENCODING";
+ field public static final java.lang.String EXTRA_CALL_SUBJECT_MAX_LENGTH = "android.telecom.extra.CALL_SUBJECT_MAX_LENGTH";
field public static final int NO_HIGHLIGHT_COLOR = 0; // 0x0
field public static final int NO_RESOURCE_ID = -1; // 0xffffffff
field public static final java.lang.String SCHEME_SIP = "sip";
@@ -35330,8 +35332,10 @@
field public static final int KEYCODE_CLEAR = 28; // 0x1c
field public static final int KEYCODE_COMMA = 55; // 0x37
field public static final int KEYCODE_CONTACTS = 207; // 0xcf
+ field public static final int KEYCODE_COPY = 278; // 0x116
field public static final int KEYCODE_CTRL_LEFT = 113; // 0x71
field public static final int KEYCODE_CTRL_RIGHT = 114; // 0x72
+ field public static final int KEYCODE_CUT = 277; // 0x115
field public static final int KEYCODE_D = 32; // 0x20
field public static final int KEYCODE_DEL = 67; // 0x43
field public static final int KEYCODE_DPAD_CENTER = 23; // 0x17
@@ -35449,6 +35453,7 @@
field public static final int KEYCODE_PAGE_DOWN = 93; // 0x5d
field public static final int KEYCODE_PAGE_UP = 92; // 0x5c
field public static final int KEYCODE_PAIRING = 225; // 0xe1
+ field public static final int KEYCODE_PASTE = 279; // 0x117
field public static final int KEYCODE_PERIOD = 56; // 0x38
field public static final int KEYCODE_PICTSYMBOLS = 94; // 0x5e
field public static final int KEYCODE_PLUS = 81; // 0x51
diff --git a/api/system-current.txt b/api/system-current.txt
index 8ccc706..06cbfa7 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -32734,6 +32734,8 @@
field public static final int CAPABILITY_SIM_SUBSCRIPTION = 4; // 0x4
field public static final int CAPABILITY_VIDEO_CALLING = 8; // 0x8
field public static final android.os.Parcelable.Creator<android.telecom.PhoneAccount> CREATOR;
+ field public static final java.lang.String EXTRA_CALL_SUBJECT_CHARACTER_ENCODING = "android.telecom.extra.CALL_SUBJECT_CHARACTER_ENCODING";
+ field public static final java.lang.String EXTRA_CALL_SUBJECT_MAX_LENGTH = "android.telecom.extra.CALL_SUBJECT_MAX_LENGTH";
field public static final int NO_HIGHLIGHT_COLOR = 0; // 0x0
field public static final int NO_RESOURCE_ID = -1; // 0xffffffff
field public static final java.lang.String SCHEME_SIP = "sip";
@@ -37627,8 +37629,10 @@
field public static final int KEYCODE_CLEAR = 28; // 0x1c
field public static final int KEYCODE_COMMA = 55; // 0x37
field public static final int KEYCODE_CONTACTS = 207; // 0xcf
+ field public static final int KEYCODE_COPY = 278; // 0x116
field public static final int KEYCODE_CTRL_LEFT = 113; // 0x71
field public static final int KEYCODE_CTRL_RIGHT = 114; // 0x72
+ field public static final int KEYCODE_CUT = 277; // 0x115
field public static final int KEYCODE_D = 32; // 0x20
field public static final int KEYCODE_DEL = 67; // 0x43
field public static final int KEYCODE_DPAD_CENTER = 23; // 0x17
@@ -37746,6 +37750,7 @@
field public static final int KEYCODE_PAGE_DOWN = 93; // 0x5d
field public static final int KEYCODE_PAGE_UP = 92; // 0x5c
field public static final int KEYCODE_PAIRING = 225; // 0xe1
+ field public static final int KEYCODE_PASTE = 279; // 0x117
field public static final int KEYCODE_PERIOD = 56; // 0x38
field public static final int KEYCODE_PICTSYMBOLS = 94; // 0x5e
field public static final int KEYCODE_PLUS = 81; // 0x51
diff --git a/core/java/android/hardware/input/InputManager.java b/core/java/android/hardware/input/InputManager.java
index df7e586..201afee 100644
--- a/core/java/android/hardware/input/InputManager.java
+++ b/core/java/android/hardware/input/InputManager.java
@@ -108,13 +108,14 @@
* of a key character map for a particular keyboard layout. The label on the receiver
* is used to name the collection of keyboard layouts provided by this receiver in the
* keyboard layout settings.
- * <pre></code>
+ * <pre><code>
* <?xml version="1.0" encoding="utf-8"?>
* <keyboard-layouts xmlns:android="http://schemas.android.com/apk/res/android">
* <keyboard-layout android:name="keyboard_layout_english_us"
* android:label="@string/keyboard_layout_english_us_label"
* android:keyboardLayout="@raw/keyboard_layout_english_us" />
* </keyboard-layouts>
+ * </pre></code>
* </p><p>
* The <code>android:name</code> attribute specifies an identifier by which
* the keyboard layout will be known in the package.
diff --git a/core/java/android/net/ConnectivityManager.java b/core/java/android/net/ConnectivityManager.java
index 444548f..ad9058f 100644
--- a/core/java/android/net/ConnectivityManager.java
+++ b/core/java/android/net/ConnectivityManager.java
@@ -901,8 +901,12 @@
* Tells the underlying networking system that the caller wants to
* begin using the named feature. The interpretation of {@code feature}
* is completely up to each networking implementation.
- * <p>This method requires the caller to hold the permission
- * {@link android.Manifest.permission#CHANGE_NETWORK_STATE}.
+ *
+ * <p>This method requires the caller to hold either the
+ * {@link android.Manifest.permission#CHANGE_NETWORK_STATE} permission
+ * or the ability to modify system settings as determined by
+ * {@link android.provider.Settings.System#canWrite}.</p>
+ *
* @param networkType specifies which network the request pertains to
* @param feature the name of the feature to be used
* @return an integer value representing the outcome of the request.
@@ -952,8 +956,12 @@
* Tells the underlying networking system that the caller is finished
* using the named feature. The interpretation of {@code feature}
* is completely up to each networking implementation.
- * <p>This method requires the caller to hold the permission
- * {@link android.Manifest.permission#CHANGE_NETWORK_STATE}.
+ *
+ * <p>This method requires the caller to hold either the
+ * {@link android.Manifest.permission#CHANGE_NETWORK_STATE} permission
+ * or the ability to modify system settings as determined by
+ * {@link android.provider.Settings.System#canWrite}.</p>
+ *
* @param networkType specifies which network the request pertains to
* @param feature the name of the feature that is no longer needed
* @return an integer value representing the outcome of the request.
@@ -1339,8 +1347,12 @@
* Ensure that a network route exists to deliver traffic to the specified
* host via the specified network interface. An attempt to add a route that
* already exists is ignored, but treated as successful.
- * <p>This method requires the caller to hold the permission
- * {@link android.Manifest.permission#CHANGE_NETWORK_STATE}.
+ *
+ * <p>This method requires the caller to hold either the
+ * {@link android.Manifest.permission#CHANGE_NETWORK_STATE} permission
+ * or the ability to modify system settings as determined by
+ * {@link android.provider.Settings.System#canWrite}.</p>
+ *
* @param networkType the type of the network over which traffic to the specified
* host is to be routed
* @param hostAddress the IP address of the host to which the route is desired
@@ -1360,8 +1372,12 @@
* Ensure that a network route exists to deliver traffic to the specified
* host via the specified network interface. An attempt to add a route that
* already exists is ignored, but treated as successful.
- * <p>This method requires the caller to hold the permission
- * {@link android.Manifest.permission#CHANGE_NETWORK_STATE}.
+ *
+ * <p>This method requires the caller to hold either the
+ * {@link android.Manifest.permission#CHANGE_NETWORK_STATE} permission
+ * or the ability to modify system settings as determined by
+ * {@link android.provider.Settings.System#canWrite}.</p>
+ *
* @param networkType the type of the network over which traffic to the specified
* host is to be routed
* @param hostAddress the IP address of the host to which the route is desired
@@ -1561,6 +1577,13 @@
return (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
}
+ /** {@hide} */
+ public static final void enforceChangePermission(Context context) {
+ int uid = Binder.getCallingUid();
+ Settings.checkAndNoteChangeNetworkStateOperation(context, uid, Settings
+ .getPackageNameForUid(context, uid), true /* throwException */);
+ }
+
/** {@hide */
public static final void enforceTetherChangePermission(Context context) {
if (context.getResources().getStringArray(
@@ -1571,8 +1594,8 @@
android.Manifest.permission.CONNECTIVITY_INTERNAL, "ConnectivityService");
} else {
int uid = Binder.getCallingUid();
- Settings.checkAndNoteChangeNetworkStateOperation(context, uid, Settings
- .getPackageNameForUid(context, uid), true);
+ Settings.checkAndNoteWriteSettingsOperation(context, uid, Settings
+ .getPackageNameForUid(context, uid), true /* throwException */);
}
}
@@ -1677,8 +1700,11 @@
* allowed between the tethered devices and this device, though upstream net
* access will of course fail until an upstream network interface becomes
* active.
- * <p>This method requires the caller to hold the permission
- * {@link android.Manifest.permission#CHANGE_NETWORK_STATE}.
+ *
+ * <p>This method requires the caller to hold either the
+ * {@link android.Manifest.permission#CHANGE_NETWORK_STATE} permission
+ * or the ability to modify system settings as determined by
+ * {@link android.provider.Settings.System#canWrite}.</p>
*
* @param iface the interface name to tether.
* @return error a {@code TETHER_ERROR} value indicating success or failure type
@@ -1695,8 +1721,11 @@
/**
* Stop tethering the named interface.
- * <p>This method requires the caller to hold the permission
- * {@link android.Manifest.permission#CHANGE_NETWORK_STATE}.
+ *
+ * <p>This method requires the caller to hold either the
+ * {@link android.Manifest.permission#CHANGE_NETWORK_STATE} permission
+ * or the ability to modify system settings as determined by
+ * {@link android.provider.Settings.System#canWrite}.</p>
*
* @param iface the interface name to untether.
* @return error a {@code TETHER_ERROR} value indicating success or failure type
@@ -1796,8 +1825,11 @@
* attempt to switch to Rndis and subsequently tether the resulting
* interface on {@code true} or turn off tethering and switch off
* Rndis on {@code false}.
- * <p>This method requires the caller to hold the permission
- * {@link android.Manifest.permission#CHANGE_NETWORK_STATE}.
+ *
+ * <p>This method requires the caller to hold either the
+ * {@link android.Manifest.permission#CHANGE_NETWORK_STATE} permission
+ * or the ability to modify system settings as determined by
+ * {@link android.provider.Settings.System#canWrite}.</p>
*
* @param enable a boolean - {@code true} to enable tethering
* @return error a {@code TETHER_ERROR} value indicating success or failure type
@@ -2467,8 +2499,11 @@
* network may never attain, and whether a network will attain these states
* is unknown prior to bringing up the network so the framework does not
* know how to go about satisfing a request with these capabilities.
- * <p>This method requires the caller to hold the permission
- * {@link android.Manifest.permission#CHANGE_NETWORK_STATE}.
+ *
+ * <p>This method requires the caller to hold either the
+ * {@link android.Manifest.permission#CHANGE_NETWORK_STATE} permission
+ * or the ability to modify system settings as determined by
+ * {@link android.provider.Settings.System#canWrite}.</p>
*
* @param request {@link NetworkRequest} describing this request.
* @param networkCallback The {@link NetworkCallback} to be utilized for this
@@ -2490,8 +2525,12 @@
* network is not found within the given time (in milliseconds) the
* {@link NetworkCallback#unavailable} callback is called. The request must
* still be released normally by calling {@link releaseNetworkRequest}.
- * <p>This method requires the caller to hold the permission
- * {@link android.Manifest.permission#CHANGE_NETWORK_STATE}.
+ *
+ * <p>This method requires the caller to hold either the
+ * {@link android.Manifest.permission#CHANGE_NETWORK_STATE} permission
+ * or the ability to modify system settings as determined by
+ * {@link android.provider.Settings.System#canWrite}.</p>
+ *
* @param request {@link NetworkRequest} describing this request.
* @param networkCallback The callbacks to be utilized for this request. Note
* the callbacks must not be shared - they uniquely specify
@@ -2564,8 +2603,12 @@
* network may never attain, and whether a network will attain these states
* is unknown prior to bringing up the network so the framework does not
* know how to go about satisfing a request with these capabilities.
- * <p>This method requires the caller to hold the permission
- * {@link android.Manifest.permission#CHANGE_NETWORK_STATE}.
+ *
+ * <p>This method requires the caller to hold either the
+ * {@link android.Manifest.permission#CHANGE_NETWORK_STATE} permission
+ * or the ability to modify system settings as determined by
+ * {@link android.provider.Settings.System#canWrite}.</p>
+ *
* @param request {@link NetworkRequest} describing this request.
* @param operation Action to perform when the network is available (corresponds
* to the {@link NetworkCallback#onAvailable} call. Typically
diff --git a/core/java/android/os/Parcel.java b/core/java/android/os/Parcel.java
index 1273772b..5852f5f 100644
--- a/core/java/android/os/Parcel.java
+++ b/core/java/android/os/Parcel.java
@@ -16,6 +16,7 @@
package android.os;
+import android.annotation.IntegerRes;
import android.text.TextUtils;
import android.util.ArrayMap;
import android.util.Log;
@@ -42,6 +43,8 @@
import java.util.Map;
import java.util.Set;
+import dalvik.system.VMRuntime;
+
/**
* Container for a message (data and object references) that can
* be sent through an IBinder. A Parcel can contain both flattened data
@@ -193,6 +196,7 @@
* indicating that we're responsible for its lifecycle.
*/
private boolean mOwnsNativeParcelObject;
+ private long mNativeSize;
private RuntimeException mStack;
@@ -244,7 +248,7 @@
private static native int nativeDataAvail(long nativePtr);
private static native int nativeDataPosition(long nativePtr);
private static native int nativeDataCapacity(long nativePtr);
- private static native void nativeSetDataSize(long nativePtr, int size);
+ private static native long nativeSetDataSize(long nativePtr, int size);
private static native void nativeSetDataPosition(long nativePtr, int pos);
private static native void nativeSetDataCapacity(long nativePtr, int size);
@@ -259,7 +263,7 @@
private static native void nativeWriteDouble(long nativePtr, double val);
private static native void nativeWriteString(long nativePtr, String val);
private static native void nativeWriteStrongBinder(long nativePtr, IBinder val);
- private static native void nativeWriteFileDescriptor(long nativePtr, FileDescriptor val);
+ private static native long nativeWriteFileDescriptor(long nativePtr, FileDescriptor val);
private static native byte[] nativeCreateByteArray(long nativePtr);
private static native byte[] nativeReadBlob(long nativePtr);
@@ -272,13 +276,13 @@
private static native FileDescriptor nativeReadFileDescriptor(long nativePtr);
private static native long nativeCreate();
- private static native void nativeFreeBuffer(long nativePtr);
+ private static native long nativeFreeBuffer(long nativePtr);
private static native void nativeDestroy(long nativePtr);
private static native byte[] nativeMarshall(long nativePtr);
- private static native void nativeUnmarshall(
+ private static native long nativeUnmarshall(
long nativePtr, byte[] data, int offset, int length);
- private static native void nativeAppendFrom(
+ private static native long nativeAppendFrom(
long thisNativePtr, long otherNativePtr, int offset, int length);
private static native boolean nativeHasFileDescriptors(long nativePtr);
private static native void nativeWriteInterfaceToken(long nativePtr, String interfaceName);
@@ -390,7 +394,7 @@
* @param size The new number of bytes in the Parcel.
*/
public final void setDataSize(int size) {
- nativeSetDataSize(mNativePtr, size);
+ updateNativeSize(nativeSetDataSize(mNativePtr, size));
}
/**
@@ -442,11 +446,11 @@
* Set the bytes in data to be the raw bytes of this Parcel.
*/
public final void unmarshall(byte[] data, int offset, int length) {
- nativeUnmarshall(mNativePtr, data, offset, length);
+ updateNativeSize(nativeUnmarshall(mNativePtr, data, offset, length));
}
public final void appendFrom(Parcel parcel, int offset, int length) {
- nativeAppendFrom(mNativePtr, parcel.mNativePtr, offset, length);
+ updateNativeSize(nativeAppendFrom(mNativePtr, parcel.mNativePtr, offset, length));
}
/**
@@ -599,7 +603,24 @@
* if {@link Parcelable#PARCELABLE_WRITE_RETURN_VALUE} is set.</p>
*/
public final void writeFileDescriptor(FileDescriptor val) {
- nativeWriteFileDescriptor(mNativePtr, val);
+ updateNativeSize(nativeWriteFileDescriptor(mNativePtr, val));
+ }
+
+ private void updateNativeSize(long newNativeSize) {
+ if (mOwnsNativeParcelObject) {
+ if (newNativeSize > Integer.MAX_VALUE) {
+ newNativeSize = Integer.MAX_VALUE;
+ }
+ if (newNativeSize != mNativeSize) {
+ int delta = (int) (newNativeSize - mNativeSize);
+ if (delta > 0) {
+ VMRuntime.getRuntime().registerNativeAllocation(delta);
+ } else {
+ VMRuntime.getRuntime().registerNativeFree(-delta);
+ }
+ mNativeSize = newNativeSize;
+ }
+ }
}
/**
@@ -2545,7 +2566,7 @@
private void freeBuffer() {
if (mOwnsNativeParcelObject) {
- nativeFreeBuffer(mNativePtr);
+ updateNativeSize(nativeFreeBuffer(mNativePtr));
}
}
@@ -2553,6 +2574,7 @@
if (mNativePtr != 0) {
if (mOwnsNativeParcelObject) {
nativeDestroy(mNativePtr);
+ updateNativeSize(0);
}
mNativePtr = 0;
}
diff --git a/core/java/android/os/RecoverySystem.java b/core/java/android/os/RecoverySystem.java
index 41de579..4535572 100644
--- a/core/java/android/os/RecoverySystem.java
+++ b/core/java/android/os/RecoverySystem.java
@@ -341,6 +341,10 @@
} finally {
uncryptFile.close();
}
+ // UNCRYPT_FILE needs to be readable by system server on bootup.
+ if (!UNCRYPT_FILE.setReadable(true, false)) {
+ Log.e(TAG, "Error setting readable for " + UNCRYPT_FILE.getCanonicalPath());
+ }
Log.w(TAG, "!!! REBOOTING TO INSTALL " + filename + " !!!");
// If the package is on the /data partition, write the block map file
@@ -501,6 +505,25 @@
Log.e(TAG, "Error reading recovery log", e);
}
+ if (UNCRYPT_FILE.exists()) {
+ String filename = null;
+ try {
+ filename = FileUtils.readTextFile(UNCRYPT_FILE, 0, null);
+ } catch (IOException e) {
+ Log.e(TAG, "Error reading uncrypt file", e);
+ }
+
+ // Remove the OTA package on /data that has been (possibly
+ // partially) processed. (Bug: 24973532)
+ if (filename != null && filename.startsWith("/data")) {
+ if (UNCRYPT_FILE.delete()) {
+ Log.i(TAG, "Deleted: " + filename);
+ } else {
+ Log.e(TAG, "Can't delete: " + filename);
+ }
+ }
+ }
+
// Delete everything in RECOVERY_DIR except those beginning
// with LAST_PREFIX
String[] names = RECOVERY_DIR.list();
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index d601831..11effd0 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -1434,25 +1434,6 @@
}
/**
- * An app can use this method to check if it is currently allowed to change the network
- * state. In order to be allowed to do so, an app must first declare either the
- * {@link android.Manifest.permission#CHANGE_NETWORK_STATE} or
- * {@link android.Manifest.permission#WRITE_SETTINGS} permission in its manifest. If it
- * is currently disallowed, it can prompt the user to grant it this capability through a
- * management UI by sending an Intent with action
- * {@link android.provider.Settings#ACTION_MANAGE_WRITE_SETTINGS}.
- *
- * @param context A context
- * @return true if the calling app can change the state of network, false otherwise.
- * @hide
- */
- public static boolean canChangeNetworkState(Context context) {
- int uid = Binder.getCallingUid();
- return Settings.isCallingPackageAllowedToChangeNetworkState(context, uid, Settings
- .getPackageNameForUid(context, uid), false);
- }
-
- /**
* System settings, containing miscellaneous system preferences. This
* table holds simple name/value pairs. There are convenience
* functions for accessing individual settings entries.
@@ -8384,7 +8365,7 @@
* write/modify system settings, as the condition differs for pre-M, M+, and
* privileged/preinstalled apps. If the provided uid does not match the
* callingPackage, a negative result will be returned. The caller is expected to have
- * either WRITE_SETTINGS or CHANGE_NETWORK_STATE permission declared.
+ * the WRITE_SETTINGS permission declared.
*
* Note: if the check is successful, the operation of this app will be updated to the
* current time.
@@ -8400,31 +8381,22 @@
/**
* Performs a strict and comprehensive check of whether a calling package is allowed to
* change the state of network, as the condition differs for pre-M, M+, and
- * privileged/preinstalled apps. If the provided uid does not match the
- * callingPackage, a negative result will be returned. The caller is expected to have
- * either of CHANGE_NETWORK_STATE or WRITE_SETTINGS permission declared.
- * @hide
- */
- public static boolean isCallingPackageAllowedToChangeNetworkState(Context context, int uid,
- String callingPackage, boolean throwException) {
- return isCallingPackageAllowedToPerformAppOpsProtectedOperation(context, uid,
- callingPackage, throwException, AppOpsManager.OP_WRITE_SETTINGS,
- PM_CHANGE_NETWORK_STATE, false);
- }
-
- /**
- * Performs a strict and comprehensive check of whether a calling package is allowed to
- * change the state of network, as the condition differs for pre-M, M+, and
- * privileged/preinstalled apps. If the provided uid does not match the
- * callingPackage, a negative result will be returned. The caller is expected to have
- * either CHANGE_NETWORK_STATE or WRITE_SETTINGS permission declared.
+ * privileged/preinstalled apps. The caller is expected to have either the
+ * CHANGE_NETWORK_STATE or the WRITE_SETTINGS permission declared. Either of these
+ * permissions allow changing network state; WRITE_SETTINGS is a runtime permission and
+ * can be revoked, but (except in M, excluding M MRs), CHANGE_NETWORK_STATE is a normal
+ * permission and cannot be revoked. See http://b/23597341
*
- * Note: if the check is successful, the operation of this app will be updated to the
- * current time.
+ * Note: if the check succeeds because the application holds WRITE_SETTINGS, the operation
+ * of this app will be updated to the current time.
* @hide
*/
public static boolean checkAndNoteChangeNetworkStateOperation(Context context, int uid,
String callingPackage, boolean throwException) {
+ if (context.checkCallingOrSelfPermission(android.Manifest.permission.CHANGE_NETWORK_STATE)
+ == PackageManager.PERMISSION_GRANTED) {
+ return true;
+ }
return isCallingPackageAllowedToPerformAppOpsProtectedOperation(context, uid,
callingPackage, throwException, AppOpsManager.OP_WRITE_SETTINGS,
PM_CHANGE_NETWORK_STATE, true);
diff --git a/core/java/android/view/KeyEvent.java b/core/java/android/view/KeyEvent.java
index 1c20cab..55cf56f 100644
--- a/core/java/android/view/KeyEvent.java
+++ b/core/java/android/view/KeyEvent.java
@@ -790,8 +790,14 @@
public static final int KEYCODE_MEDIA_STEP_BACKWARD = 275;
/** Key code constant: put device to sleep unless a wakelock is held. */
public static final int KEYCODE_SOFT_SLEEP = 276;
+ /** Key code constant: Cut key. */
+ public static final int KEYCODE_CUT = 277;
+ /** Key code constant: Copy key. */
+ public static final int KEYCODE_COPY = 278;
+ /** Key code constant: Paste key. */
+ public static final int KEYCODE_PASTE = 279;
- private static final int LAST_KEYCODE = KEYCODE_SOFT_SLEEP;
+ private static final int LAST_KEYCODE = KEYCODE_PASTE;
// NOTE: If you add a new keycode here you must also add it to:
// isSystem()
diff --git a/core/java/android/widget/Editor.java b/core/java/android/widget/Editor.java
index 24183be..2fabe33 100644
--- a/core/java/android/widget/Editor.java
+++ b/core/java/android/widget/Editor.java
@@ -16,12 +16,6 @@
package android.widget;
-import java.text.BreakIterator;
-import java.util.Arrays;
-import java.util.Comparator;
-import java.util.HashMap;
-import java.util.List;
-
import android.R;
import android.annotation.Nullable;
import android.app.PendingIntent;
@@ -112,6 +106,12 @@
import com.android.internal.util.Preconditions;
import com.android.internal.widget.EditableInputConnection;
+import java.text.BreakIterator;
+import java.util.Arrays;
+import java.util.Comparator;
+import java.util.HashMap;
+import java.util.List;
+
/**
* Helper class used by TextView to handle editable text views.
@@ -1004,8 +1004,7 @@
}
if (!handled && mTextActionMode != null) {
- // TODO: Fix dragging in extracted mode.
- if (touchPositionIsInSelection() && !mTextView.isInExtractedMode()) {
+ if (touchPositionIsInSelection()) {
// Start a drag
final int start = mTextView.getSelectionStart();
final int end = mTextView.getSelectionEnd();
@@ -4261,10 +4260,14 @@
positionAtCursorOffset(offset, false);
}
+ /**
+ * @param offset Cursor offset. Must be in [-1, length].
+ * @param parentScrolled If the parent has been scrolled or not.
+ */
@Override
protected void positionAtCursorOffset(int offset, boolean parentScrolled) {
super.positionAtCursorOffset(offset, parentScrolled);
- mInWord = !getWordIteratorWithText().isBoundary(offset);
+ mInWord = (offset != -1) && !getWordIteratorWithText().isBoundary(offset);
}
@Override
@@ -4497,10 +4500,14 @@
positionAtCursorOffset(offset, false);
}
+ /**
+ * @param offset Cursor offset. Must be in [-1, length].
+ * @param parentScrolled If the parent has been scrolled or not.
+ */
@Override
protected void positionAtCursorOffset(int offset, boolean parentScrolled) {
super.positionAtCursorOffset(offset, parentScrolled);
- mInWord = !getWordIteratorWithText().isBoundary(offset);
+ mInWord = (offset != -1) && !getWordIteratorWithText().isBoundary(offset);
}
@Override
@@ -4867,9 +4874,8 @@
mEndHandle.showAtLocation(endOffset);
// No longer the first dragging motion, reset.
- if (!(mTextView.isInExtractedMode())) {
- startSelectionActionMode();
- }
+ startSelectionActionMode();
+
mDragAcceleratorActive = false;
mStartOffset = -1;
mSwitchedLines = false;
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index 391022c..13b1c4b 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -16,6 +16,8 @@
package android.widget;
+import static android.os.Build.VERSION_CODES.JELLY_BEAN_MR1;
+
import android.R;
import android.annotation.ColorInt;
import android.annotation.DrawableRes;
@@ -118,14 +120,14 @@
import android.view.MotionEvent;
import android.view.PointerIcon;
import android.view.View;
-import android.view.ViewParent;
-import android.view.ViewStructure;
import android.view.ViewConfiguration;
import android.view.ViewDebug;
import android.view.ViewGroup.LayoutParams;
-import android.view.ViewRootImpl;
-import android.view.ViewTreeObserver;
import android.view.ViewHierarchyEncoder;
+import android.view.ViewParent;
+import android.view.ViewRootImpl;
+import android.view.ViewStructure;
+import android.view.ViewTreeObserver;
import android.view.accessibility.AccessibilityEvent;
import android.view.accessibility.AccessibilityManager;
import android.view.accessibility.AccessibilityNodeInfo;
@@ -153,8 +155,6 @@
import java.util.ArrayList;
import java.util.Locale;
-import static android.os.Build.VERSION_CODES.JELLY_BEAN_MR1;
-
/**
* Displays text to the user and optionally allows them to edit it. A TextView
* is a complete text editor, however the basic class is configured to not
@@ -5287,14 +5287,6 @@
mEditor.mCreatedWithASelection = false;
}
- // Phone specific code (there is no ExtractEditText on tablets).
- // ExtractEditText does not call onFocus when it is displayed, and mHasSelectionOnFocus can
- // not be set. Do the test here instead.
- if (isInExtractedMode() && hasSelection() && mEditor != null
- && mEditor.mTextActionMode == null && isShown() && hasWindowFocus()) {
- mEditor.startSelectionActionMode();
- }
-
unregisterForPreDraw();
return true;
diff --git a/core/java/com/android/internal/app/ChooserActivity.java b/core/java/com/android/internal/app/ChooserActivity.java
index 70d5f62..b7b7400 100644
--- a/core/java/com/android/internal/app/ChooserActivity.java
+++ b/core/java/com/android/internal/app/ChooserActivity.java
@@ -373,6 +373,11 @@
int targetsToQuery = 0;
for (int i = 0, N = adapter.getDisplayResolveInfoCount(); i < N; i++) {
final DisplayResolveInfo dri = adapter.getDisplayResolveInfo(i);
+ if (adapter.getScore(dri) == 0) {
+ // A score of 0 means the app hasn't been used in some time;
+ // don't query it as it's not likely to be relevant.
+ continue;
+ }
final ActivityInfo ai = dri.getResolveInfo().activityInfo;
final Bundle md = ai.metaData;
final String serviceName = md != null ? convertServiceName(ai.packageName,
diff --git a/core/jni/android_os_Parcel.cpp b/core/jni/android_os_Parcel.cpp
index a14afa0..41aa9ca 100644
--- a/core/jni/android_os_Parcel.cpp
+++ b/core/jni/android_os_Parcel.cpp
@@ -114,7 +114,7 @@
return parcel ? parcel->dataCapacity() : 0;
}
-static void android_os_Parcel_setDataSize(JNIEnv* env, jclass clazz, jlong nativePtr, jint size)
+static jlong android_os_Parcel_setDataSize(JNIEnv* env, jclass clazz, jlong nativePtr, jint size)
{
Parcel* parcel = reinterpret_cast<Parcel*>(nativePtr);
if (parcel != NULL) {
@@ -122,7 +122,9 @@
if (err != NO_ERROR) {
signalExceptionForError(env, clazz, err);
}
+ return parcel->getOpenAshmemSize();
}
+ return 0;
}
static void android_os_Parcel_setDataPosition(JNIEnv* env, jclass clazz, jlong nativePtr, jint pos)
@@ -304,7 +306,7 @@
}
}
-static void android_os_Parcel_writeFileDescriptor(JNIEnv* env, jclass clazz, jlong nativePtr, jobject object)
+static jlong android_os_Parcel_writeFileDescriptor(JNIEnv* env, jclass clazz, jlong nativePtr, jobject object)
{
Parcel* parcel = reinterpret_cast<Parcel*>(nativePtr);
if (parcel != NULL) {
@@ -313,7 +315,9 @@
if (err != NO_ERROR) {
signalExceptionForError(env, clazz, err);
}
+ return parcel->getOpenAshmemSize();
}
+ return 0;
}
static jbyteArray android_os_Parcel_createByteArray(JNIEnv* env, jclass clazz, jlong nativePtr)
@@ -546,12 +550,14 @@
return reinterpret_cast<jlong>(parcel);
}
-static void android_os_Parcel_freeBuffer(JNIEnv* env, jclass clazz, jlong nativePtr)
+static jlong android_os_Parcel_freeBuffer(JNIEnv* env, jclass clazz, jlong nativePtr)
{
Parcel* parcel = reinterpret_cast<Parcel*>(nativePtr);
if (parcel != NULL) {
parcel->freeData();
+ return parcel->getOpenAshmemSize();
}
+ return 0;
}
static void android_os_Parcel_destroy(JNIEnv* env, jclass clazz, jlong nativePtr)
@@ -589,12 +595,12 @@
return ret;
}
-static void android_os_Parcel_unmarshall(JNIEnv* env, jclass clazz, jlong nativePtr,
- jbyteArray data, jint offset, jint length)
+static jlong android_os_Parcel_unmarshall(JNIEnv* env, jclass clazz, jlong nativePtr,
+ jbyteArray data, jint offset, jint length)
{
Parcel* parcel = reinterpret_cast<Parcel*>(nativePtr);
if (parcel == NULL || length < 0) {
- return;
+ return 0;
}
jbyte* array = (jbyte*)env->GetPrimitiveArrayCritical(data, 0);
@@ -608,24 +614,26 @@
env->ReleasePrimitiveArrayCritical(data, array, 0);
}
+ return parcel->getOpenAshmemSize();
}
-static void android_os_Parcel_appendFrom(JNIEnv* env, jclass clazz, jlong thisNativePtr,
- jlong otherNativePtr, jint offset, jint length)
+static jlong android_os_Parcel_appendFrom(JNIEnv* env, jclass clazz, jlong thisNativePtr,
+ jlong otherNativePtr, jint offset, jint length)
{
Parcel* thisParcel = reinterpret_cast<Parcel*>(thisNativePtr);
if (thisParcel == NULL) {
- return;
+ return 0;
}
Parcel* otherParcel = reinterpret_cast<Parcel*>(otherNativePtr);
if (otherParcel == NULL) {
- return;
+ return thisParcel->getOpenAshmemSize();
}
status_t err = thisParcel->appendFrom(otherParcel, offset, length);
if (err != NO_ERROR) {
signalExceptionForError(env, clazz, err);
}
+ return thisParcel->getOpenAshmemSize();
}
static jboolean android_os_Parcel_hasFileDescriptors(JNIEnv* env, jclass clazz, jlong nativePtr)
@@ -718,7 +726,7 @@
{"nativeDataAvail", "(J)I", (void*)android_os_Parcel_dataAvail},
{"nativeDataPosition", "(J)I", (void*)android_os_Parcel_dataPosition},
{"nativeDataCapacity", "(J)I", (void*)android_os_Parcel_dataCapacity},
- {"nativeSetDataSize", "(JI)V", (void*)android_os_Parcel_setDataSize},
+ {"nativeSetDataSize", "(JI)J", (void*)android_os_Parcel_setDataSize},
{"nativeSetDataPosition", "(JI)V", (void*)android_os_Parcel_setDataPosition},
{"nativeSetDataCapacity", "(JI)V", (void*)android_os_Parcel_setDataCapacity},
@@ -733,7 +741,7 @@
{"nativeWriteDouble", "(JD)V", (void*)android_os_Parcel_writeDouble},
{"nativeWriteString", "(JLjava/lang/String;)V", (void*)android_os_Parcel_writeString},
{"nativeWriteStrongBinder", "(JLandroid/os/IBinder;)V", (void*)android_os_Parcel_writeStrongBinder},
- {"nativeWriteFileDescriptor", "(JLjava/io/FileDescriptor;)V", (void*)android_os_Parcel_writeFileDescriptor},
+ {"nativeWriteFileDescriptor", "(JLjava/io/FileDescriptor;)J", (void*)android_os_Parcel_writeFileDescriptor},
{"nativeCreateByteArray", "(J)[B", (void*)android_os_Parcel_createByteArray},
{"nativeReadBlob", "(J)[B", (void*)android_os_Parcel_readBlob},
@@ -751,12 +759,12 @@
{"clearFileDescriptor", "(Ljava/io/FileDescriptor;)V", (void*)android_os_Parcel_clearFileDescriptor},
{"nativeCreate", "()J", (void*)android_os_Parcel_create},
- {"nativeFreeBuffer", "(J)V", (void*)android_os_Parcel_freeBuffer},
+ {"nativeFreeBuffer", "(J)J", (void*)android_os_Parcel_freeBuffer},
{"nativeDestroy", "(J)V", (void*)android_os_Parcel_destroy},
{"nativeMarshall", "(J)[B", (void*)android_os_Parcel_marshall},
- {"nativeUnmarshall", "(J[BII)V", (void*)android_os_Parcel_unmarshall},
- {"nativeAppendFrom", "(JJII)V", (void*)android_os_Parcel_appendFrom},
+ {"nativeUnmarshall", "(J[BII)J", (void*)android_os_Parcel_unmarshall},
+ {"nativeAppendFrom", "(JJII)J", (void*)android_os_Parcel_appendFrom},
{"nativeHasFileDescriptors", "(J)Z", (void*)android_os_Parcel_hasFileDescriptors},
{"nativeWriteInterfaceToken", "(JLjava/lang/String;)V", (void*)android_os_Parcel_writeInterfaceToken},
{"nativeEnforceInterface", "(JLjava/lang/String;)V", (void*)android_os_Parcel_enforceInterface},
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 9f6e033..f300741 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -1715,12 +1715,12 @@
android:protectionLevel="signature|privileged" />
<!-- Allows applications to change network connectivity state.
- <p>Protection level: signature
+ <p>Protection level: normal
-->
<permission android:name="android.permission.CHANGE_NETWORK_STATE"
android:description="@string/permdesc_changeNetworkState"
android:label="@string/permlab_changeNetworkState"
- android:protectionLevel="signature|preinstalled|appop|pre23" />
+ android:protectionLevel="normal" />
<!-- Allows an application to clear the caches of all installed
applications on the device.
diff --git a/core/res/res/values-af/strings.xml b/core/res/res/values-af/strings.xml
index a58ec89..e7acf7e 100644
--- a/core/res/res/values-af/strings.xml
+++ b/core/res/res/values-af/strings.xml
@@ -650,7 +650,7 @@
<string name="keyguard_password_wrong_pin_code" msgid="2422225591006134936">"Verkeerde PIN-kode."</string>
<string name="keyguard_label_text" msgid="861796461028298424">"Om te ontsluit, druk Kieslys dan 0."</string>
<string name="emergency_call_dialog_number_for_display" msgid="696192103195090970">"Noodnommer"</string>
- <string name="lockscreen_carrier_default" msgid="8963839242565653192">"Geen diens nie."</string>
+ <string name="lockscreen_carrier_default" msgid="6169005837238288522">"Geen diens nie"</string>
<string name="lockscreen_screen_locked" msgid="7288443074806832904">"Skerm gesluit."</string>
<string name="lockscreen_instructions_when_pattern_enabled" msgid="46154051614126049">"Druk kieslys om oop te sluit of maak noodoproep."</string>
<string name="lockscreen_instructions_when_pattern_disabled" msgid="686260028797158364">"Druk kieslys om oop te maak."</string>
diff --git a/core/res/res/values-am/strings.xml b/core/res/res/values-am/strings.xml
index 80665a27..5bd9ccf 100644
--- a/core/res/res/values-am/strings.xml
+++ b/core/res/res/values-am/strings.xml
@@ -650,7 +650,7 @@
<string name="keyguard_password_wrong_pin_code" msgid="2422225591006134936">"ትክክል ያልሆነ ፒን ኮድ።"</string>
<string name="keyguard_label_text" msgid="861796461028298424">"ለመክፈት፣ምናሌ ተጫን ከዛ 0"</string>
<string name="emergency_call_dialog_number_for_display" msgid="696192103195090970">"የአደጋ ጊዜቁጥር"</string>
- <string name="lockscreen_carrier_default" msgid="8963839242565653192">"ከአገልግሎት መስጫ ክልል ውጪ"</string>
+ <string name="lockscreen_carrier_default" msgid="6169005837238288522">"ምንም አገልግሎት የለም"</string>
<string name="lockscreen_screen_locked" msgid="7288443074806832904">"ማሳያ መቆለፊያ።"</string>
<string name="lockscreen_instructions_when_pattern_enabled" msgid="46154051614126049">"ለመክፈት ምናሌ ተጫንወይም የአደጋ ጊዜ ጥሪ አድርግ።"</string>
<string name="lockscreen_instructions_when_pattern_disabled" msgid="686260028797158364">"ለመክፈት ምናሌ ተጫን"</string>
diff --git a/core/res/res/values-ar/strings.xml b/core/res/res/values-ar/strings.xml
index 22eaabe..4dd5144 100644
--- a/core/res/res/values-ar/strings.xml
+++ b/core/res/res/values-ar/strings.xml
@@ -654,7 +654,7 @@
<string name="keyguard_password_wrong_pin_code" msgid="2422225591006134936">"رمز PIN غير صحيح."</string>
<string name="keyguard_label_text" msgid="861796461028298424">"لإلغاء التأمين، اضغط على \"القائمة\" ثم على 0."</string>
<string name="emergency_call_dialog_number_for_display" msgid="696192103195090970">"رقم الطوارئ"</string>
- <string name="lockscreen_carrier_default" msgid="8963839242565653192">"لا تتوفر خدمة"</string>
+ <string name="lockscreen_carrier_default" msgid="6169005837238288522">"لا خدمة"</string>
<string name="lockscreen_screen_locked" msgid="7288443074806832904">"الشاشة مؤمّنة."</string>
<string name="lockscreen_instructions_when_pattern_enabled" msgid="46154051614126049">"اضغط على \"القائمة\" لإلغاء التأمين أو إجراء اتصال بالطوارئ."</string>
<string name="lockscreen_instructions_when_pattern_disabled" msgid="686260028797158364">"اضغط على \"القائمة\" لإلغاء التأمين."</string>
diff --git a/core/res/res/values-az-rAZ/strings.xml b/core/res/res/values-az-rAZ/strings.xml
index 401e5b8..c828fc4 100644
--- a/core/res/res/values-az-rAZ/strings.xml
+++ b/core/res/res/values-az-rAZ/strings.xml
@@ -650,7 +650,7 @@
<string name="keyguard_password_wrong_pin_code" msgid="2422225591006134936">"Yanlış PIN kodu."</string>
<string name="keyguard_label_text" msgid="861796461028298424">"Kilidi açmaq üçün Menyu, sonra 0 basın."</string>
<string name="emergency_call_dialog_number_for_display" msgid="696192103195090970">"Təcili nömrə"</string>
- <string name="lockscreen_carrier_default" msgid="8963839242565653192">"Xidmət yoxdur."</string>
+ <string name="lockscreen_carrier_default" msgid="6169005837238288522">"Xidmət yoxdur"</string>
<string name="lockscreen_screen_locked" msgid="7288443074806832904">"Ekran kilidlənib."</string>
<string name="lockscreen_instructions_when_pattern_enabled" msgid="46154051614126049">"Təcili zəng kilidini açmaq və ya yerləşdirmək üçün Menyu düyməsinə basın."</string>
<string name="lockscreen_instructions_when_pattern_disabled" msgid="686260028797158364">"Kilidi açmaq üçün Menyu düyməsinə basın."</string>
diff --git a/core/res/res/values-bg/strings.xml b/core/res/res/values-bg/strings.xml
index 6c3fa54..e474e7e 100644
--- a/core/res/res/values-bg/strings.xml
+++ b/core/res/res/values-bg/strings.xml
@@ -650,7 +650,7 @@
<string name="keyguard_password_wrong_pin_code" msgid="2422225591006134936">"Неправилен ПИН код."</string>
<string name="keyguard_label_text" msgid="861796461028298424">"За да отключите, натиснете „Меню“ и после 0."</string>
<string name="emergency_call_dialog_number_for_display" msgid="696192103195090970">"Спешен номер"</string>
- <string name="lockscreen_carrier_default" msgid="8963839242565653192">"Няма покритие."</string>
+ <string name="lockscreen_carrier_default" msgid="6169005837238288522">"Няма покритие"</string>
<string name="lockscreen_screen_locked" msgid="7288443074806832904">"Екранът е заключен."</string>
<string name="lockscreen_instructions_when_pattern_enabled" msgid="46154051614126049">"Натиснете „Меню“, за да отключите или да извършите спешно обаждане."</string>
<string name="lockscreen_instructions_when_pattern_disabled" msgid="686260028797158364">"Натиснете „Меню“, за да отключите."</string>
diff --git a/core/res/res/values-bn-rBD/strings.xml b/core/res/res/values-bn-rBD/strings.xml
index 3f986a0..1f365b9 100644
--- a/core/res/res/values-bn-rBD/strings.xml
+++ b/core/res/res/values-bn-rBD/strings.xml
@@ -650,7 +650,7 @@
<string name="keyguard_password_wrong_pin_code" msgid="2422225591006134936">"ভুল পিন কোড৷"</string>
<string name="keyguard_label_text" msgid="861796461028298424">"আনলক করতে, মেনু টিপুন তারপর ০ টিপুন৷"</string>
<string name="emergency_call_dialog_number_for_display" msgid="696192103195090970">"জরুরী নম্বর"</string>
- <string name="lockscreen_carrier_default" msgid="8963839242565653192">"কোনো পরিষেবা নেই৷"</string>
+ <string name="lockscreen_carrier_default" msgid="6169005837238288522">"কোনো পরিষেবা নেই"</string>
<string name="lockscreen_screen_locked" msgid="7288443074806832904">"স্ক্রীণ লক করা আছে৷"</string>
<string name="lockscreen_instructions_when_pattern_enabled" msgid="46154051614126049">"আনলক করতে বা জরুরী কল করতে মেনু টিপুন৷"</string>
<string name="lockscreen_instructions_when_pattern_disabled" msgid="686260028797158364">"আনলক করতে মেনু টিপুন৷"</string>
diff --git a/core/res/res/values-ca/strings.xml b/core/res/res/values-ca/strings.xml
index 8d004df..046ca14 100644
--- a/core/res/res/values-ca/strings.xml
+++ b/core/res/res/values-ca/strings.xml
@@ -650,7 +650,7 @@
<string name="keyguard_password_wrong_pin_code" msgid="2422225591006134936">"Codi PIN incorrecte."</string>
<string name="keyguard_label_text" msgid="861796461028298424">"Per desbloquejar-lo, premeu Menú i després 0."</string>
<string name="emergency_call_dialog_number_for_display" msgid="696192103195090970">"Número d\'emergència"</string>
- <string name="lockscreen_carrier_default" msgid="8963839242565653192">"Sense servei."</string>
+ <string name="lockscreen_carrier_default" msgid="6169005837238288522">"Sense servei"</string>
<string name="lockscreen_screen_locked" msgid="7288443074806832904">"Pantalla bloquejada."</string>
<string name="lockscreen_instructions_when_pattern_enabled" msgid="46154051614126049">"Premeu Menú per desbloquejar-lo o per fer una trucada d\'emergència."</string>
<string name="lockscreen_instructions_when_pattern_disabled" msgid="686260028797158364">"Premeu Menú per desbloquejar."</string>
diff --git a/core/res/res/values-cs/strings.xml b/core/res/res/values-cs/strings.xml
index 6ead72f..8f3976d 100644
--- a/core/res/res/values-cs/strings.xml
+++ b/core/res/res/values-cs/strings.xml
@@ -652,7 +652,7 @@
<string name="keyguard_password_wrong_pin_code" msgid="2422225591006134936">"Nesprávný kód PIN."</string>
<string name="keyguard_label_text" msgid="861796461028298424">"Chcete-li telefon odemknout, stiskněte Menu a poté 0."</string>
<string name="emergency_call_dialog_number_for_display" msgid="696192103195090970">"Číslo tísňové linky"</string>
- <string name="lockscreen_carrier_default" msgid="8963839242565653192">"Žádný signál"</string>
+ <string name="lockscreen_carrier_default" msgid="6169005837238288522">"Žádný signál"</string>
<string name="lockscreen_screen_locked" msgid="7288443074806832904">"Obrazovka uzamčena."</string>
<string name="lockscreen_instructions_when_pattern_enabled" msgid="46154051614126049">"Chcete-li odemknout telefon nebo provést tísňové volání, stiskněte Menu."</string>
<string name="lockscreen_instructions_when_pattern_disabled" msgid="686260028797158364">"Telefon odemknete stisknutím tlačítka Menu."</string>
diff --git a/core/res/res/values-da/strings.xml b/core/res/res/values-da/strings.xml
index 346cdb1..82b550b 100644
--- a/core/res/res/values-da/strings.xml
+++ b/core/res/res/values-da/strings.xml
@@ -219,7 +219,7 @@
<string name="global_actions_airplane_mode_off_status" msgid="5075070442854490296">"Flytilstand er slået FRA"</string>
<string name="global_action_settings" msgid="1756531602592545966">"Indstillinger"</string>
<string name="global_action_assist" msgid="3892832961594295030">"Assistance"</string>
- <string name="global_action_voice_assist" msgid="7751191495200504480">"Voice Assist"</string>
+ <string name="global_action_voice_assist" msgid="7751191495200504480">"Taleassistent"</string>
<string name="global_action_lockdown" msgid="8751542514724332873">"Lås nu"</string>
<string name="status_bar_notification_info_overflow" msgid="5301981741705354993">"999+"</string>
<string name="safeMode" msgid="2788228061547930246">"Sikker tilstand"</string>
@@ -650,7 +650,7 @@
<string name="keyguard_password_wrong_pin_code" msgid="2422225591006134936">"Forkert pinkode."</string>
<string name="keyguard_label_text" msgid="861796461028298424">"Tryk på Menu og dernæst på 0 for at låse op."</string>
<string name="emergency_call_dialog_number_for_display" msgid="696192103195090970">"Nødnummer"</string>
- <string name="lockscreen_carrier_default" msgid="8963839242565653192">"Ingen dækning."</string>
+ <string name="lockscreen_carrier_default" msgid="6169005837238288522">"Ingen dækning"</string>
<string name="lockscreen_screen_locked" msgid="7288443074806832904">"Skærmen er låst."</string>
<string name="lockscreen_instructions_when_pattern_enabled" msgid="46154051614126049">"Tryk på Menu for at låse op eller foretage et nødopkald."</string>
<string name="lockscreen_instructions_when_pattern_disabled" msgid="686260028797158364">"Tryk på Menu for at låse op."</string>
diff --git a/core/res/res/values-de/strings.xml b/core/res/res/values-de/strings.xml
index 4e8b455..9ae5473b 100644
--- a/core/res/res/values-de/strings.xml
+++ b/core/res/res/values-de/strings.xml
@@ -650,7 +650,7 @@
<string name="keyguard_password_wrong_pin_code" msgid="2422225591006134936">"Falscher PIN-Code"</string>
<string name="keyguard_label_text" msgid="861796461028298424">"Drücken Sie zum Entsperren die Menütaste und dann auf \"0\"."</string>
<string name="emergency_call_dialog_number_for_display" msgid="696192103195090970">"Notrufnummer"</string>
- <string name="lockscreen_carrier_default" msgid="8963839242565653192">"Kein Dienst"</string>
+ <string name="lockscreen_carrier_default" msgid="6169005837238288522">"Kein Dienst"</string>
<string name="lockscreen_screen_locked" msgid="7288443074806832904">"Display gesperrt"</string>
<string name="lockscreen_instructions_when_pattern_enabled" msgid="46154051614126049">"Drücken Sie die Menütaste, um das Telefon zu entsperren oder einen Notruf zu tätigen."</string>
<string name="lockscreen_instructions_when_pattern_disabled" msgid="686260028797158364">"Zum Entsperren die Menütaste drücken"</string>
diff --git a/core/res/res/values-el/strings.xml b/core/res/res/values-el/strings.xml
index 3f5e1af..56deed13 100644
--- a/core/res/res/values-el/strings.xml
+++ b/core/res/res/values-el/strings.xml
@@ -650,7 +650,7 @@
<string name="keyguard_password_wrong_pin_code" msgid="2422225591006134936">"Λανθασμένος κωδικός PIN."</string>
<string name="keyguard_label_text" msgid="861796461028298424">"Για ξεκλείδωμα, πατήστε το πλήκτρο Menu και, στη συνέχεια, το πλήκτρο 0."</string>
<string name="emergency_call_dialog_number_for_display" msgid="696192103195090970">"Αριθμός έκτακτης ανάγκης"</string>
- <string name="lockscreen_carrier_default" msgid="8963839242565653192">"Καμία υπηρεσία."</string>
+ <string name="lockscreen_carrier_default" msgid="6169005837238288522">"Δίκτυο μη διαθέσιμο"</string>
<string name="lockscreen_screen_locked" msgid="7288443074806832904">"Η οθόνη κλειδώθηκε."</string>
<string name="lockscreen_instructions_when_pattern_enabled" msgid="46154051614126049">"Πατήστε \"Menu\" για ξεκλείδωμα ή για κλήση έκτακτης ανάγκης."</string>
<string name="lockscreen_instructions_when_pattern_disabled" msgid="686260028797158364">"Πατήστε \"Μενού\" για ξεκλείδωμα."</string>
diff --git a/core/res/res/values-en-rAU/strings.xml b/core/res/res/values-en-rAU/strings.xml
index 22179ed..53adc2b 100644
--- a/core/res/res/values-en-rAU/strings.xml
+++ b/core/res/res/values-en-rAU/strings.xml
@@ -650,7 +650,7 @@
<string name="keyguard_password_wrong_pin_code" msgid="2422225591006134936">"Incorrect PIN code."</string>
<string name="keyguard_label_text" msgid="861796461028298424">"To unlock, press Menu, then 0."</string>
<string name="emergency_call_dialog_number_for_display" msgid="696192103195090970">"Emergency number"</string>
- <string name="lockscreen_carrier_default" msgid="8963839242565653192">"No service"</string>
+ <string name="lockscreen_carrier_default" msgid="6169005837238288522">"No service"</string>
<string name="lockscreen_screen_locked" msgid="7288443074806832904">"Screen locked."</string>
<string name="lockscreen_instructions_when_pattern_enabled" msgid="46154051614126049">"Press Menu to unlock or place emergency call."</string>
<string name="lockscreen_instructions_when_pattern_disabled" msgid="686260028797158364">"Press Menu to unlock."</string>
diff --git a/core/res/res/values-en-rGB/strings.xml b/core/res/res/values-en-rGB/strings.xml
index 22179ed..53adc2b 100644
--- a/core/res/res/values-en-rGB/strings.xml
+++ b/core/res/res/values-en-rGB/strings.xml
@@ -650,7 +650,7 @@
<string name="keyguard_password_wrong_pin_code" msgid="2422225591006134936">"Incorrect PIN code."</string>
<string name="keyguard_label_text" msgid="861796461028298424">"To unlock, press Menu, then 0."</string>
<string name="emergency_call_dialog_number_for_display" msgid="696192103195090970">"Emergency number"</string>
- <string name="lockscreen_carrier_default" msgid="8963839242565653192">"No service"</string>
+ <string name="lockscreen_carrier_default" msgid="6169005837238288522">"No service"</string>
<string name="lockscreen_screen_locked" msgid="7288443074806832904">"Screen locked."</string>
<string name="lockscreen_instructions_when_pattern_enabled" msgid="46154051614126049">"Press Menu to unlock or place emergency call."</string>
<string name="lockscreen_instructions_when_pattern_disabled" msgid="686260028797158364">"Press Menu to unlock."</string>
diff --git a/core/res/res/values-en-rIN/strings.xml b/core/res/res/values-en-rIN/strings.xml
index 22179ed..53adc2b 100644
--- a/core/res/res/values-en-rIN/strings.xml
+++ b/core/res/res/values-en-rIN/strings.xml
@@ -650,7 +650,7 @@
<string name="keyguard_password_wrong_pin_code" msgid="2422225591006134936">"Incorrect PIN code."</string>
<string name="keyguard_label_text" msgid="861796461028298424">"To unlock, press Menu, then 0."</string>
<string name="emergency_call_dialog_number_for_display" msgid="696192103195090970">"Emergency number"</string>
- <string name="lockscreen_carrier_default" msgid="8963839242565653192">"No service"</string>
+ <string name="lockscreen_carrier_default" msgid="6169005837238288522">"No service"</string>
<string name="lockscreen_screen_locked" msgid="7288443074806832904">"Screen locked."</string>
<string name="lockscreen_instructions_when_pattern_enabled" msgid="46154051614126049">"Press Menu to unlock or place emergency call."</string>
<string name="lockscreen_instructions_when_pattern_disabled" msgid="686260028797158364">"Press Menu to unlock."</string>
diff --git a/core/res/res/values-es-rUS/strings.xml b/core/res/res/values-es-rUS/strings.xml
index 08f513b..25ba6db8 100644
--- a/core/res/res/values-es-rUS/strings.xml
+++ b/core/res/res/values-es-rUS/strings.xml
@@ -650,7 +650,7 @@
<string name="keyguard_password_wrong_pin_code" msgid="2422225591006134936">"Código PIN incorrecto"</string>
<string name="keyguard_label_text" msgid="861796461028298424">"Para desbloquear, presiona el menú y luego 0."</string>
<string name="emergency_call_dialog_number_for_display" msgid="696192103195090970">"Número de emergencia"</string>
- <string name="lockscreen_carrier_default" msgid="8963839242565653192">"Sin servicio"</string>
+ <string name="lockscreen_carrier_default" msgid="6169005837238288522">"Sin servicio"</string>
<string name="lockscreen_screen_locked" msgid="7288443074806832904">"Pantalla bloqueada."</string>
<string name="lockscreen_instructions_when_pattern_enabled" msgid="46154051614126049">"Presiona el Menú para desbloquear o realizar una llamada de emergencia."</string>
<string name="lockscreen_instructions_when_pattern_disabled" msgid="686260028797158364">"Presionar Menú para desbloquear."</string>
diff --git a/core/res/res/values-es/strings.xml b/core/res/res/values-es/strings.xml
index 4d1500d..e3a4b66 100644
--- a/core/res/res/values-es/strings.xml
+++ b/core/res/res/values-es/strings.xml
@@ -650,7 +650,7 @@
<string name="keyguard_password_wrong_pin_code" msgid="2422225591006134936">"Código PIN incorrecto"</string>
<string name="keyguard_label_text" msgid="861796461028298424">"Para desbloquear el teléfono, pulsa la tecla de menú y, a continuación, pulsa 0."</string>
<string name="emergency_call_dialog_number_for_display" msgid="696192103195090970">"Número de emergencia"</string>
- <string name="lockscreen_carrier_default" msgid="8963839242565653192">"Sin servicio"</string>
+ <string name="lockscreen_carrier_default" msgid="6169005837238288522">"Sin servicio"</string>
<string name="lockscreen_screen_locked" msgid="7288443074806832904">"Pantalla bloqueada"</string>
<string name="lockscreen_instructions_when_pattern_enabled" msgid="46154051614126049">"Pulsa la tecla de menú para desbloquear el teléfono o realizar una llamada de emergencia."</string>
<string name="lockscreen_instructions_when_pattern_disabled" msgid="686260028797158364">"Pulsa la tecla de menú para desbloquear la pantalla."</string>
diff --git a/core/res/res/values-et-rEE/strings.xml b/core/res/res/values-et-rEE/strings.xml
index 09454d6..41474a4 100644
--- a/core/res/res/values-et-rEE/strings.xml
+++ b/core/res/res/values-et-rEE/strings.xml
@@ -650,7 +650,7 @@
<string name="keyguard_password_wrong_pin_code" msgid="2422225591006134936">"Vale PIN-kood."</string>
<string name="keyguard_label_text" msgid="861796461028298424">"Avamiseks vajutage menüüklahvi, seejärel klahvi 0."</string>
<string name="emergency_call_dialog_number_for_display" msgid="696192103195090970">"Hädaabinumber"</string>
- <string name="lockscreen_carrier_default" msgid="8963839242565653192">"Teenus puudub."</string>
+ <string name="lockscreen_carrier_default" msgid="6169005837238288522">"Teenus puudub"</string>
<string name="lockscreen_screen_locked" msgid="7288443074806832904">"Ekraan lukus."</string>
<string name="lockscreen_instructions_when_pattern_enabled" msgid="46154051614126049">"Vajutage avamiseks või hädaabikõne tegemiseks menüünuppu"</string>
<string name="lockscreen_instructions_when_pattern_disabled" msgid="686260028797158364">"Vajutage avamiseks menüüklahvi."</string>
diff --git a/core/res/res/values-eu-rES/strings.xml b/core/res/res/values-eu-rES/strings.xml
index 0815aeb..3a23fba 100644
--- a/core/res/res/values-eu-rES/strings.xml
+++ b/core/res/res/values-eu-rES/strings.xml
@@ -650,7 +650,7 @@
<string name="keyguard_password_wrong_pin_code" msgid="2422225591006134936">"PIN kode okerra."</string>
<string name="keyguard_label_text" msgid="861796461028298424">"Desblokeatzeko, sakatu Menua eta, ondoren, 0."</string>
<string name="emergency_call_dialog_number_for_display" msgid="696192103195090970">"Larrialdietarako zenbakia"</string>
- <string name="lockscreen_carrier_default" msgid="8963839242565653192">"Ez dago zerbitzurik."</string>
+ <string name="lockscreen_carrier_default" msgid="6169005837238288522">"Ez dago zerbitzurik"</string>
<string name="lockscreen_screen_locked" msgid="7288443074806832904">"Pantaila blokeatuta dago."</string>
<string name="lockscreen_instructions_when_pattern_enabled" msgid="46154051614126049">"Desblokeatzeko edo larrialdi-deia egiteko, sakatu Menua."</string>
<string name="lockscreen_instructions_when_pattern_disabled" msgid="686260028797158364">"Desblokeatzeko, sakatu Menua."</string>
diff --git a/core/res/res/values-fa/strings.xml b/core/res/res/values-fa/strings.xml
index 683afb4..ae6598b 100644
--- a/core/res/res/values-fa/strings.xml
+++ b/core/res/res/values-fa/strings.xml
@@ -650,7 +650,7 @@
<string name="keyguard_password_wrong_pin_code" msgid="2422225591006134936">"پین کد اشتباه است."</string>
<string name="keyguard_label_text" msgid="861796461028298424">"برای بازگشایی قفل، منو را فشار دهید و سپس 0 را فشار دهید."</string>
<string name="emergency_call_dialog_number_for_display" msgid="696192103195090970">"شماره اضطراری"</string>
- <string name="lockscreen_carrier_default" msgid="8963839242565653192">"سرویسی وجود ندارد."</string>
+ <string name="lockscreen_carrier_default" msgid="6169005837238288522">"بدون سرویس"</string>
<string name="lockscreen_screen_locked" msgid="7288443074806832904">"صفحه قفل شد."</string>
<string name="lockscreen_instructions_when_pattern_enabled" msgid="46154051614126049">"برای بازگشایی قفل یا انجام تماس اضطراری روی منو فشار دهید."</string>
<string name="lockscreen_instructions_when_pattern_disabled" msgid="686260028797158364">"برای بازگشایی قفل روی منو فشار دهید."</string>
diff --git a/core/res/res/values-fi/strings.xml b/core/res/res/values-fi/strings.xml
index f8fb76f7..d8778a8 100644
--- a/core/res/res/values-fi/strings.xml
+++ b/core/res/res/values-fi/strings.xml
@@ -650,7 +650,7 @@
<string name="keyguard_password_wrong_pin_code" msgid="2422225591006134936">"PIN-koodi väärin."</string>
<string name="keyguard_label_text" msgid="861796461028298424">"Poista lukitus painamalla Valikko-painiketta ja 0-näppäintä."</string>
<string name="emergency_call_dialog_number_for_display" msgid="696192103195090970">"Hätänumero"</string>
- <string name="lockscreen_carrier_default" msgid="8963839242565653192">"Ei yhteyttä."</string>
+ <string name="lockscreen_carrier_default" msgid="6169005837238288522">"Ei yhteyttä"</string>
<string name="lockscreen_screen_locked" msgid="7288443074806832904">"Näyttö lukittu."</string>
<string name="lockscreen_instructions_when_pattern_enabled" msgid="46154051614126049">"Poista lukitus tai soita hätäpuhelu painamalla Valikko-painiketta."</string>
<string name="lockscreen_instructions_when_pattern_disabled" msgid="686260028797158364">"Poista lukitus painamalla Valikko-painiketta."</string>
diff --git a/core/res/res/values-fr-rCA/strings.xml b/core/res/res/values-fr-rCA/strings.xml
index 286459a..6f59ee0 100644
--- a/core/res/res/values-fr-rCA/strings.xml
+++ b/core/res/res/values-fr-rCA/strings.xml
@@ -650,7 +650,7 @@
<string name="keyguard_password_wrong_pin_code" msgid="2422225591006134936">"NIP erroné."</string>
<string name="keyguard_label_text" msgid="861796461028298424">"Pour déverrouiller le téléphone, appuyez sur \"Menu\", puis sur 0."</string>
<string name="emergency_call_dialog_number_for_display" msgid="696192103195090970">"Numéro d\'urgence"</string>
- <string name="lockscreen_carrier_default" msgid="8963839242565653192">"Aucun service"</string>
+ <string name="lockscreen_carrier_default" msgid="6169005837238288522">"Aucun service"</string>
<string name="lockscreen_screen_locked" msgid="7288443074806832904">"Écran verrouillé"</string>
<string name="lockscreen_instructions_when_pattern_enabled" msgid="46154051614126049">"Appuyez sur \"Menu\" pour débloquer le téléphone ou appeler un numéro d\'urgence."</string>
<string name="lockscreen_instructions_when_pattern_disabled" msgid="686260028797158364">"Appuyez sur \"Menu\" pour déverrouiller l\'appareil."</string>
diff --git a/core/res/res/values-fr/strings.xml b/core/res/res/values-fr/strings.xml
index e4daeb1..9f08389 100644
--- a/core/res/res/values-fr/strings.xml
+++ b/core/res/res/values-fr/strings.xml
@@ -650,7 +650,7 @@
<string name="keyguard_password_wrong_pin_code" msgid="2422225591006134936">"Le code PIN est erroné."</string>
<string name="keyguard_label_text" msgid="861796461028298424">"Pour déverrouiller le clavier, appuyez sur \"Menu\" puis sur 0."</string>
<string name="emergency_call_dialog_number_for_display" msgid="696192103195090970">"Numéro d\'urgence"</string>
- <string name="lockscreen_carrier_default" msgid="8963839242565653192">"Aucun service"</string>
+ <string name="lockscreen_carrier_default" msgid="6169005837238288522">"Aucun service"</string>
<string name="lockscreen_screen_locked" msgid="7288443074806832904">"Écran verrouillé"</string>
<string name="lockscreen_instructions_when_pattern_enabled" msgid="46154051614126049">"Appuyez sur \"Menu\" pour déverrouiller le téléphone ou appeler un numéro d\'urgence"</string>
<string name="lockscreen_instructions_when_pattern_disabled" msgid="686260028797158364">"Appuyez sur \"Menu\" pour déverrouiller le téléphone."</string>
diff --git a/core/res/res/values-gl-rES/strings.xml b/core/res/res/values-gl-rES/strings.xml
index 2abd2be..21e6ee7 100644
--- a/core/res/res/values-gl-rES/strings.xml
+++ b/core/res/res/values-gl-rES/strings.xml
@@ -650,7 +650,7 @@
<string name="keyguard_password_wrong_pin_code" msgid="2422225591006134936">"Código PIN incorrecto"</string>
<string name="keyguard_label_text" msgid="861796461028298424">"Para desbloquear, preme Menú e, a continuación, 0."</string>
<string name="emergency_call_dialog_number_for_display" msgid="696192103195090970">"Número de emerxencia"</string>
- <string name="lockscreen_carrier_default" msgid="8963839242565653192">"Non hai servizo."</string>
+ <string name="lockscreen_carrier_default" msgid="6169005837238288522">"Sen servizo"</string>
<string name="lockscreen_screen_locked" msgid="7288443074806832904">"Pantalla bloqueada"</string>
<string name="lockscreen_instructions_when_pattern_enabled" msgid="46154051614126049">"Preme Menú para desbloquear ou realizar unha chamada de emerxencia."</string>
<string name="lockscreen_instructions_when_pattern_disabled" msgid="686260028797158364">"Preme Menú para desbloquear."</string>
diff --git a/core/res/res/values-gu-rIN/strings.xml b/core/res/res/values-gu-rIN/strings.xml
index 8f46194..6dc0371 100644
--- a/core/res/res/values-gu-rIN/strings.xml
+++ b/core/res/res/values-gu-rIN/strings.xml
@@ -650,7 +650,7 @@
<string name="keyguard_password_wrong_pin_code" msgid="2422225591006134936">"ખોટો PIN કોડ."</string>
<string name="keyguard_label_text" msgid="861796461028298424">"અનલૉક કરવા માટે, મેનૂ દબાવો તે પછી 0."</string>
<string name="emergency_call_dialog_number_for_display" msgid="696192103195090970">"ઇમરજન્સિ નંબર"</string>
- <string name="lockscreen_carrier_default" msgid="8963839242565653192">"કોઈ સેવા નથી."</string>
+ <string name="lockscreen_carrier_default" msgid="6169005837238288522">"કોઈ સેવા નથી"</string>
<string name="lockscreen_screen_locked" msgid="7288443074806832904">"સ્ક્રીન લૉક કરી."</string>
<string name="lockscreen_instructions_when_pattern_enabled" msgid="46154051614126049">"અનલૉક કરવા માટે અથવા કટોકટીનો કૉલ કરવા માટે મેનૂ દબાવો."</string>
<string name="lockscreen_instructions_when_pattern_disabled" msgid="686260028797158364">"અનલૉક કરવા માટે મેનૂ દબાવો."</string>
diff --git a/core/res/res/values-hi/strings.xml b/core/res/res/values-hi/strings.xml
index 61b21bd..6b1a91f 100644
--- a/core/res/res/values-hi/strings.xml
+++ b/core/res/res/values-hi/strings.xml
@@ -650,7 +650,7 @@
<string name="keyguard_password_wrong_pin_code" msgid="2422225591006134936">"गलत पिन कोड."</string>
<string name="keyguard_label_text" msgid="861796461028298424">"अनलॉक करने के लिए, मेनू दबाएं और फिर 0 दबाएं."</string>
<string name="emergency_call_dialog_number_for_display" msgid="696192103195090970">"आपातकालीन नंबर"</string>
- <string name="lockscreen_carrier_default" msgid="8963839242565653192">"कोई सेवा नहीं."</string>
+ <string name="lockscreen_carrier_default" msgid="6169005837238288522">"कोई सेवा नहीं"</string>
<string name="lockscreen_screen_locked" msgid="7288443074806832904">"स्क्रीन लॉक की गई है."</string>
<string name="lockscreen_instructions_when_pattern_enabled" msgid="46154051614126049">"अनलॉक करने के लिए मेनू दबाएं या आपातलकालीन कॉल करें."</string>
<string name="lockscreen_instructions_when_pattern_disabled" msgid="686260028797158364">"अनलॉक करने के लिए मेनू दबाएं."</string>
diff --git a/core/res/res/values-hr/strings.xml b/core/res/res/values-hr/strings.xml
index 680dabd..67d7110 100644
--- a/core/res/res/values-hr/strings.xml
+++ b/core/res/res/values-hr/strings.xml
@@ -651,7 +651,7 @@
<string name="keyguard_password_wrong_pin_code" msgid="2422225591006134936">"Netočan PIN kôd."</string>
<string name="keyguard_label_text" msgid="861796461028298424">"Za otključavanje pritisnite Izbornik pa 0."</string>
<string name="emergency_call_dialog_number_for_display" msgid="696192103195090970">"Broj hitne službe"</string>
- <string name="lockscreen_carrier_default" msgid="8963839242565653192">"Nema usluge."</string>
+ <string name="lockscreen_carrier_default" msgid="6169005837238288522">"Nema usluge"</string>
<string name="lockscreen_screen_locked" msgid="7288443074806832904">"Zaslon zaključan."</string>
<string name="lockscreen_instructions_when_pattern_enabled" msgid="46154051614126049">"Pritisnite Izbornik za otključavanje ili pozivanje hitnih službi."</string>
<string name="lockscreen_instructions_when_pattern_disabled" msgid="686260028797158364">"Pritisnite Izbornik za otključavanje."</string>
diff --git a/core/res/res/values-hu/strings.xml b/core/res/res/values-hu/strings.xml
index faed9b9..7e1f55b 100644
--- a/core/res/res/values-hu/strings.xml
+++ b/core/res/res/values-hu/strings.xml
@@ -650,7 +650,7 @@
<string name="keyguard_password_wrong_pin_code" msgid="2422225591006134936">"Helytelen PIN kód."</string>
<string name="keyguard_label_text" msgid="861796461028298424">"A feloldáshoz nyomja meg a Menü, majd a 0 gombot."</string>
<string name="emergency_call_dialog_number_for_display" msgid="696192103195090970">"Segélyhívó szám"</string>
- <string name="lockscreen_carrier_default" msgid="8963839242565653192">"Nincs szolgáltatás."</string>
+ <string name="lockscreen_carrier_default" msgid="6169005837238288522">"Nincs szolgáltatás"</string>
<string name="lockscreen_screen_locked" msgid="7288443074806832904">"A képernyő le van zárva."</string>
<string name="lockscreen_instructions_when_pattern_enabled" msgid="46154051614126049">"A feloldáshoz vagy segélyhívás kezdeményezéséhez nyomja meg a Menü gombot."</string>
<string name="lockscreen_instructions_when_pattern_disabled" msgid="686260028797158364">"A feloldáshoz nyomja meg a Menü gombot."</string>
diff --git a/core/res/res/values-hy-rAM/strings.xml b/core/res/res/values-hy-rAM/strings.xml
index 9a0e603..9259c06 100644
--- a/core/res/res/values-hy-rAM/strings.xml
+++ b/core/res/res/values-hy-rAM/strings.xml
@@ -650,7 +650,7 @@
<string name="keyguard_password_wrong_pin_code" msgid="2422225591006134936">"Սխալ PIN ծածկագիր:"</string>
<string name="keyguard_label_text" msgid="861796461028298424">"Ապակողպման համար սեղմեք Ցանկ, ապա 0:"</string>
<string name="emergency_call_dialog_number_for_display" msgid="696192103195090970">"Արտակարգ իրավիճակների հեռախոսահամար"</string>
- <string name="lockscreen_carrier_default" msgid="8963839242565653192">"Ծառայություն չկա:"</string>
+ <string name="lockscreen_carrier_default" msgid="6169005837238288522">"Ծառայություն չկա"</string>
<string name="lockscreen_screen_locked" msgid="7288443074806832904">"Էկրանը կողպված է:"</string>
<string name="lockscreen_instructions_when_pattern_enabled" msgid="46154051614126049">"Սեղմեք Ցանկ` ապակողպելու համար, կամ կատարեք արտակարգ իրավիճակների զանգ:"</string>
<string name="lockscreen_instructions_when_pattern_disabled" msgid="686260028797158364">"Ապակողպելու համար սեղմեք Ցանկը:"</string>
diff --git a/core/res/res/values-in/strings.xml b/core/res/res/values-in/strings.xml
index 1bbd020..0f6cd3e 100644
--- a/core/res/res/values-in/strings.xml
+++ b/core/res/res/values-in/strings.xml
@@ -650,7 +650,7 @@
<string name="keyguard_password_wrong_pin_code" msgid="2422225591006134936">"Kode PIN salah."</string>
<string name="keyguard_label_text" msgid="861796461028298424">"Untuk membuka, tekan Menu lalu 0."</string>
<string name="emergency_call_dialog_number_for_display" msgid="696192103195090970">"Nomor darurat"</string>
- <string name="lockscreen_carrier_default" msgid="8963839242565653192">"Tidak ada layanan."</string>
+ <string name="lockscreen_carrier_default" msgid="6169005837238288522">"Tidak ada layanan"</string>
<string name="lockscreen_screen_locked" msgid="7288443074806832904">"Layar terkunci."</string>
<string name="lockscreen_instructions_when_pattern_enabled" msgid="46154051614126049">"Tekan Menu untuk membuka atau melakukan panggilan darurat."</string>
<string name="lockscreen_instructions_when_pattern_disabled" msgid="686260028797158364">"Tekan Menu untuk membuka."</string>
diff --git a/core/res/res/values-is-rIS/strings.xml b/core/res/res/values-is-rIS/strings.xml
index 2a97573..29f7408 100644
--- a/core/res/res/values-is-rIS/strings.xml
+++ b/core/res/res/values-is-rIS/strings.xml
@@ -650,7 +650,7 @@
<string name="keyguard_password_wrong_pin_code" msgid="2422225591006134936">"Rangt PIN-númer."</string>
<string name="keyguard_label_text" msgid="861796461028298424">"Til að taka úr lás ýtirðu á valmyndartakkann og síðan 0."</string>
<string name="emergency_call_dialog_number_for_display" msgid="696192103195090970">"Neyðarnúmer"</string>
- <string name="lockscreen_carrier_default" msgid="8963839242565653192">"Ekkert símasamband."</string>
+ <string name="lockscreen_carrier_default" msgid="6169005837238288522">"Ekkert símasamband"</string>
<string name="lockscreen_screen_locked" msgid="7288443074806832904">"Skjár læstur."</string>
<string name="lockscreen_instructions_when_pattern_enabled" msgid="46154051614126049">"Ýttu á valmyndartakkann til að taka úr lás eða hringja neyðarsímtal."</string>
<string name="lockscreen_instructions_when_pattern_disabled" msgid="686260028797158364">"Ýttu á valmyndartakkann til að taka úr lás."</string>
diff --git a/core/res/res/values-it/strings.xml b/core/res/res/values-it/strings.xml
index c56b41b..ab541ea 100644
--- a/core/res/res/values-it/strings.xml
+++ b/core/res/res/values-it/strings.xml
@@ -650,7 +650,7 @@
<string name="keyguard_password_wrong_pin_code" msgid="2422225591006134936">"Codice PIN errato."</string>
<string name="keyguard_label_text" msgid="861796461028298424">"Per sbloccare, premi Menu, poi 0."</string>
<string name="emergency_call_dialog_number_for_display" msgid="696192103195090970">"Numero di emergenza"</string>
- <string name="lockscreen_carrier_default" msgid="8963839242565653192">"Nessun servizio."</string>
+ <string name="lockscreen_carrier_default" msgid="6169005837238288522">"Nessun servizio"</string>
<string name="lockscreen_screen_locked" msgid="7288443074806832904">"Schermo bloccato."</string>
<string name="lockscreen_instructions_when_pattern_enabled" msgid="46154051614126049">"Premi Menu per sbloccare o effettuare chiamate di emergenza."</string>
<string name="lockscreen_instructions_when_pattern_disabled" msgid="686260028797158364">"Premi Menu per sbloccare."</string>
diff --git a/core/res/res/values-iw/strings.xml b/core/res/res/values-iw/strings.xml
index 26fe698..de19821 100644
--- a/core/res/res/values-iw/strings.xml
+++ b/core/res/res/values-iw/strings.xml
@@ -652,7 +652,7 @@
<string name="keyguard_password_wrong_pin_code" msgid="2422225591006134936">"קוד PIN שגוי"</string>
<string name="keyguard_label_text" msgid="861796461028298424">"כדי לבטל את הנעילה, לחץ על \'תפריט\' ולאחר מכן על 0."</string>
<string name="emergency_call_dialog_number_for_display" msgid="696192103195090970">"מספר חירום"</string>
- <string name="lockscreen_carrier_default" msgid="8963839242565653192">"אין שירות"</string>
+ <string name="lockscreen_carrier_default" msgid="6169005837238288522">"אין שירות"</string>
<string name="lockscreen_screen_locked" msgid="7288443074806832904">"המסך נעול."</string>
<string name="lockscreen_instructions_when_pattern_enabled" msgid="46154051614126049">"לחץ על \'תפריט\' כדי לבטל את הנעילה או כדי לבצע שיחת חירום."</string>
<string name="lockscreen_instructions_when_pattern_disabled" msgid="686260028797158364">"לחץ על \'תפריט\' כדי לבטל את הנעילה."</string>
diff --git a/core/res/res/values-ja/strings.xml b/core/res/res/values-ja/strings.xml
index 6b1c833..45e82ac 100644
--- a/core/res/res/values-ja/strings.xml
+++ b/core/res/res/values-ja/strings.xml
@@ -650,7 +650,7 @@
<string name="keyguard_password_wrong_pin_code" msgid="2422225591006134936">"PINコードが正しくありません。"</string>
<string name="keyguard_label_text" msgid="861796461028298424">"MENU、0キーでロック解除"</string>
<string name="emergency_call_dialog_number_for_display" msgid="696192103195090970">"緊急通報番号"</string>
- <string name="lockscreen_carrier_default" msgid="8963839242565653192">"通信サービスはありません"</string>
+ <string name="lockscreen_carrier_default" msgid="6169005837238288522">"通信サービスはありません"</string>
<string name="lockscreen_screen_locked" msgid="7288443074806832904">"画面ロック中"</string>
<string name="lockscreen_instructions_when_pattern_enabled" msgid="46154051614126049">"MENUキーでロック解除(または緊急通報)"</string>
<string name="lockscreen_instructions_when_pattern_disabled" msgid="686260028797158364">"MENUキーでロック解除"</string>
diff --git a/core/res/res/values-ka-rGE/strings.xml b/core/res/res/values-ka-rGE/strings.xml
index e1221c4..7900f83 100644
--- a/core/res/res/values-ka-rGE/strings.xml
+++ b/core/res/res/values-ka-rGE/strings.xml
@@ -650,7 +650,7 @@
<string name="keyguard_password_wrong_pin_code" msgid="2422225591006134936">"არასწორი PIN კოდი."</string>
<string name="keyguard_label_text" msgid="861796461028298424">"განბლოკვისათვის დააჭირეთ მენიუს და შემდეგ 0-ს."</string>
<string name="emergency_call_dialog_number_for_display" msgid="696192103195090970">"გადაუდებელი დახმარების ნომრები"</string>
- <string name="lockscreen_carrier_default" msgid="8963839242565653192">"სერვისი არ არის."</string>
+ <string name="lockscreen_carrier_default" msgid="6169005837238288522">"სერვისი არ არის"</string>
<string name="lockscreen_screen_locked" msgid="7288443074806832904">"ეკრანი დაბლოკილია."</string>
<string name="lockscreen_instructions_when_pattern_enabled" msgid="46154051614126049">"განბლოკვისთვის ან გადაუდებელი ზარისთვის დააჭირეთ მენიუს."</string>
<string name="lockscreen_instructions_when_pattern_disabled" msgid="686260028797158364">"განბლოკვისთვის დააჭირეთ მენიუს."</string>
diff --git a/core/res/res/values-kk-rKZ/strings.xml b/core/res/res/values-kk-rKZ/strings.xml
index b3172ff..23ec612 100644
--- a/core/res/res/values-kk-rKZ/strings.xml
+++ b/core/res/res/values-kk-rKZ/strings.xml
@@ -650,7 +650,7 @@
<string name="keyguard_password_wrong_pin_code" msgid="2422225591006134936">"Қате PIN код"</string>
<string name="keyguard_label_text" msgid="861796461028298424">"Бекітпесін ашу үшін Мәзір, одан кейін 0 пернесін түртіңіз."</string>
<string name="emergency_call_dialog_number_for_display" msgid="696192103195090970">"Төтенше жағдай нөмірі"</string>
- <string name="lockscreen_carrier_default" msgid="8963839242565653192">"Қызмет көрсетілмейді."</string>
+ <string name="lockscreen_carrier_default" msgid="6169005837238288522">"Қызмет жоқ"</string>
<string name="lockscreen_screen_locked" msgid="7288443074806832904">"Экран бекітілген."</string>
<string name="lockscreen_instructions_when_pattern_enabled" msgid="46154051614126049">"Бекітпесін ашу үшін немесе төтенше қоңырауды табу үшін Мәзір тармағын басыңыз."</string>
<string name="lockscreen_instructions_when_pattern_disabled" msgid="686260028797158364">"Ашу үшін Мәзір пернесін басыңыз."</string>
diff --git a/core/res/res/values-km-rKH/strings.xml b/core/res/res/values-km-rKH/strings.xml
index 822a303..8ffa8b8 100644
--- a/core/res/res/values-km-rKH/strings.xml
+++ b/core/res/res/values-km-rKH/strings.xml
@@ -650,7 +650,7 @@
<string name="keyguard_password_wrong_pin_code" msgid="2422225591006134936">"កូដ PIN មិនត្រឹមត្រូវ។"</string>
<string name="keyguard_label_text" msgid="861796461028298424">"ដើម្បីដោះសោ ចុចម៉ឺនុយ បន្ទាប់មក 0 ។"</string>
<string name="emergency_call_dialog_number_for_display" msgid="696192103195090970">"លេខពេលអាសន្ន"</string>
- <string name="lockscreen_carrier_default" msgid="8963839242565653192">"គ្មានសេវា"</string>
+ <string name="lockscreen_carrier_default" msgid="6169005837238288522">"គ្មានសេវាទេ"</string>
<string name="lockscreen_screen_locked" msgid="7288443074806832904">"ចាក់អេក្រង់។"</string>
<string name="lockscreen_instructions_when_pattern_enabled" msgid="46154051614126049">"ចុចម៉ឺនុយ ដើម្បីដោះសោ ឬហៅពេលអាសន្ន។"</string>
<string name="lockscreen_instructions_when_pattern_disabled" msgid="686260028797158364">"ចុចម៉ឺនុយ ដើម្បីដោះសោ។"</string>
diff --git a/core/res/res/values-kn-rIN/strings.xml b/core/res/res/values-kn-rIN/strings.xml
index 73d7ffc..a840c7e 100644
--- a/core/res/res/values-kn-rIN/strings.xml
+++ b/core/res/res/values-kn-rIN/strings.xml
@@ -650,7 +650,7 @@
<string name="keyguard_password_wrong_pin_code" msgid="2422225591006134936">"ತಪ್ಪಾದ ಪಿನ್ ಕೋಡ್."</string>
<string name="keyguard_label_text" msgid="861796461028298424">"ಅನ್ಲಾಕ್ ಮಾಡಲು, ಮೆನು ನಂತರ 0 ಒತ್ತಿರಿ."</string>
<string name="emergency_call_dialog_number_for_display" msgid="696192103195090970">"ತುರ್ತು ಸಂಖ್ಯೆ"</string>
- <string name="lockscreen_carrier_default" msgid="8963839242565653192">"ಸೇವೆ ಇಲ್ಲ."</string>
+ <string name="lockscreen_carrier_default" msgid="6169005837238288522">"ಯಾವುದೇ ಸೇವೆಯಿಲ್ಲ"</string>
<string name="lockscreen_screen_locked" msgid="7288443074806832904">"ಪರದೆ ಲಾಕ್ ಆಗಿದೆ."</string>
<string name="lockscreen_instructions_when_pattern_enabled" msgid="46154051614126049">"ಅನ್ಲಾಕ್ ಮಾಡಲು ಮೆನು ಒತ್ತಿರಿ ಇಲ್ಲವೇ ತುರ್ತು ಕರೆಯನ್ನು ಮಾಡಿ."</string>
<string name="lockscreen_instructions_when_pattern_disabled" msgid="686260028797158364">"ಅನ್ಲಾಕ್ ಮಾಡಲು ಮೆನು ಒತ್ತಿರಿ."</string>
diff --git a/core/res/res/values-ko/strings.xml b/core/res/res/values-ko/strings.xml
index 0d76af2..59b0214 100644
--- a/core/res/res/values-ko/strings.xml
+++ b/core/res/res/values-ko/strings.xml
@@ -650,7 +650,7 @@
<string name="keyguard_password_wrong_pin_code" msgid="2422225591006134936">"PIN 코드가 잘못되었습니다."</string>
<string name="keyguard_label_text" msgid="861796461028298424">"잠금해제하려면 메뉴를 누른 다음 0을 누릅니다."</string>
<string name="emergency_call_dialog_number_for_display" msgid="696192103195090970">"비상 전화번호"</string>
- <string name="lockscreen_carrier_default" msgid="8963839242565653192">"서비스 불가"</string>
+ <string name="lockscreen_carrier_default" msgid="6169005837238288522">"서비스 불가"</string>
<string name="lockscreen_screen_locked" msgid="7288443074806832904">"화면 잠김"</string>
<string name="lockscreen_instructions_when_pattern_enabled" msgid="46154051614126049">"비상 전화를 걸거나 잠금해제하려면 메뉴를 누르세요."</string>
<string name="lockscreen_instructions_when_pattern_disabled" msgid="686260028797158364">"잠금해제하려면 메뉴를 누르세요."</string>
diff --git a/core/res/res/values-ky-rKG/strings.xml b/core/res/res/values-ky-rKG/strings.xml
index 22399b0..59c34dc 100644
--- a/core/res/res/values-ky-rKG/strings.xml
+++ b/core/res/res/values-ky-rKG/strings.xml
@@ -650,7 +650,7 @@
<string name="keyguard_password_wrong_pin_code" msgid="2422225591006134936">"PIN-код туура эмес."</string>
<string name="keyguard_label_text" msgid="861796461028298424">"Кулпусун ачуу үчүн, Менюна андан соң 0 баскычын басыңыз."</string>
<string name="emergency_call_dialog_number_for_display" msgid="696192103195090970">"Шашылыш чалуу номери"</string>
- <string name="lockscreen_carrier_default" msgid="8963839242565653192">"Байланыш жок."</string>
+ <string name="lockscreen_carrier_default" msgid="6169005837238288522">"Байланыш жок"</string>
<string name="lockscreen_screen_locked" msgid="7288443074806832904">"Экран кулпуланды."</string>
<string name="lockscreen_instructions_when_pattern_enabled" msgid="46154051614126049">"Кулпусун ачып же Шашылыш чалуу аткаруу үчүн менюну басыңыз."</string>
<string name="lockscreen_instructions_when_pattern_disabled" msgid="686260028797158364">"Бөгөттөн чыгаруу үчүн Менюну басыңыз."</string>
diff --git a/core/res/res/values-lo-rLA/strings.xml b/core/res/res/values-lo-rLA/strings.xml
index 1d39230..f131914 100644
--- a/core/res/res/values-lo-rLA/strings.xml
+++ b/core/res/res/values-lo-rLA/strings.xml
@@ -650,7 +650,7 @@
<string name="keyguard_password_wrong_pin_code" msgid="2422225591006134936">"ລະຫັດ PIN ບໍ່ຖືກຕ້ອງ."</string>
<string name="keyguard_label_text" msgid="861796461028298424">"ເພື່ອປົດລັອກ, ໃຫ້ກົດເມນູ ແລ້ວກົດ 0."</string>
<string name="emergency_call_dialog_number_for_display" msgid="696192103195090970">"ເບີໂທສຸກເສີນ"</string>
- <string name="lockscreen_carrier_default" msgid="8963839242565653192">"ບໍ່ມີບໍລິການ."</string>
+ <string name="lockscreen_carrier_default" msgid="6169005837238288522">"ບໍ່ມີບໍລິການ"</string>
<string name="lockscreen_screen_locked" msgid="7288443074806832904">"ລັອກໜ້າຈໍແລ້ວ."</string>
<string name="lockscreen_instructions_when_pattern_enabled" msgid="46154051614126049">"ກົດ ເມນູ ເພື່ອປົດລັອກ ຫຼື ໂທອອກຫາເບີສຸກເສີນ."</string>
<string name="lockscreen_instructions_when_pattern_disabled" msgid="686260028797158364">"ກົດ \"ເມນູ\" ເພື່ອປົດລັອກ."</string>
diff --git a/core/res/res/values-lt/strings.xml b/core/res/res/values-lt/strings.xml
index dbb8fbb..536a107 100644
--- a/core/res/res/values-lt/strings.xml
+++ b/core/res/res/values-lt/strings.xml
@@ -652,7 +652,7 @@
<string name="keyguard_password_wrong_pin_code" msgid="2422225591006134936">"Neteisingas PIN kodas."</string>
<string name="keyguard_label_text" msgid="861796461028298424">"Jei norite atrakinti, paspauskite „Meniu“ ir 0."</string>
<string name="emergency_call_dialog_number_for_display" msgid="696192103195090970">"Pagalbos numeris"</string>
- <string name="lockscreen_carrier_default" msgid="8963839242565653192">"Nėra paslaugos."</string>
+ <string name="lockscreen_carrier_default" msgid="6169005837238288522">"Nėra paslaugos"</string>
<string name="lockscreen_screen_locked" msgid="7288443074806832904">"Ekranas užrakintas."</string>
<string name="lockscreen_instructions_when_pattern_enabled" msgid="46154051614126049">"Paspauskite „Meniu“, kad atrakintumėte ar skambintumėte pagalbos numeriu."</string>
<string name="lockscreen_instructions_when_pattern_disabled" msgid="686260028797158364">"Paspauskite „Meniu“, jei norite atrakinti."</string>
diff --git a/core/res/res/values-lv/strings.xml b/core/res/res/values-lv/strings.xml
index eaf7b46..288493a 100644
--- a/core/res/res/values-lv/strings.xml
+++ b/core/res/res/values-lv/strings.xml
@@ -651,7 +651,7 @@
<string name="keyguard_password_wrong_pin_code" msgid="2422225591006134936">"PIN kods nav pareizs."</string>
<string name="keyguard_label_text" msgid="861796461028298424">"Lai atbloķētu, nospiediet Izvēlne, pēc tam 0."</string>
<string name="emergency_call_dialog_number_for_display" msgid="696192103195090970">"Ārkārtas numurs"</string>
- <string name="lockscreen_carrier_default" msgid="8963839242565653192">"Nav pakalpojuma."</string>
+ <string name="lockscreen_carrier_default" msgid="6169005837238288522">"Nav pakalpojuma"</string>
<string name="lockscreen_screen_locked" msgid="7288443074806832904">"Ekrāns ir bloķēts."</string>
<string name="lockscreen_instructions_when_pattern_enabled" msgid="46154051614126049">"Nospiediet Izvēlne, lai atbloķētu, vai veiciet ārkārtas zvanu."</string>
<string name="lockscreen_instructions_when_pattern_disabled" msgid="686260028797158364">"Lai atbloķētu, nospiediet vienumu Izvēlne."</string>
diff --git a/core/res/res/values-mk-rMK/strings.xml b/core/res/res/values-mk-rMK/strings.xml
index 17e0d49..eab72bf 100644
--- a/core/res/res/values-mk-rMK/strings.xml
+++ b/core/res/res/values-mk-rMK/strings.xml
@@ -650,7 +650,7 @@
<string name="keyguard_password_wrong_pin_code" msgid="2422225591006134936">"Погрешен ПИН код."</string>
<string name="keyguard_label_text" msgid="861796461028298424">"За да го отклучите, притиснете „Мени“ и потоа „0“."</string>
<string name="emergency_call_dialog_number_for_display" msgid="696192103195090970">"Број за итни случаи"</string>
- <string name="lockscreen_carrier_default" msgid="8963839242565653192">"Нема услуга."</string>
+ <string name="lockscreen_carrier_default" msgid="6169005837238288522">"Нема услуга"</string>
<string name="lockscreen_screen_locked" msgid="7288443074806832904">"Екранот е заклучен."</string>
<string name="lockscreen_instructions_when_pattern_enabled" msgid="46154051614126049">"Притисни „Мени“ да се отклучи или да направи итен повик."</string>
<string name="lockscreen_instructions_when_pattern_disabled" msgid="686260028797158364">"Притиснете „Мени“ за да се отклучи."</string>
diff --git a/core/res/res/values-ml-rIN/strings.xml b/core/res/res/values-ml-rIN/strings.xml
index 3df3c86..caaedda 100644
--- a/core/res/res/values-ml-rIN/strings.xml
+++ b/core/res/res/values-ml-rIN/strings.xml
@@ -650,7 +650,7 @@
<string name="keyguard_password_wrong_pin_code" msgid="2422225591006134936">"പിൻ കോഡ് തെറ്റാണ്."</string>
<string name="keyguard_label_text" msgid="861796461028298424">"അൺലോക്ക് ചെയ്യുന്നതിന് മെനു, 0 എന്നിവ അമർത്തുക."</string>
<string name="emergency_call_dialog_number_for_display" msgid="696192103195090970">"അടിയന്തര നമ്പർ"</string>
- <string name="lockscreen_carrier_default" msgid="8963839242565653192">"സേവനമില്ല"</string>
+ <string name="lockscreen_carrier_default" msgid="6169005837238288522">"സേവനമില്ല"</string>
<string name="lockscreen_screen_locked" msgid="7288443074806832904">"സ്ക്രീൻ ലോക്കുചെയ്തു."</string>
<string name="lockscreen_instructions_when_pattern_enabled" msgid="46154051614126049">"അൺലോക്ക് ചെയ്യുന്നതിനായി മെനു അമർത്തുക അല്ലെങ്കിൽ അടിയന്തര കോൾ വിളിക്കുക."</string>
<string name="lockscreen_instructions_when_pattern_disabled" msgid="686260028797158364">"അൺലോക്കുചെയ്യാൻ മെനു അമർത്തുക."</string>
diff --git a/core/res/res/values-mn-rMN/strings.xml b/core/res/res/values-mn-rMN/strings.xml
index ff8cc4d..c02760d 100644
--- a/core/res/res/values-mn-rMN/strings.xml
+++ b/core/res/res/values-mn-rMN/strings.xml
@@ -650,7 +650,7 @@
<string name="keyguard_password_wrong_pin_code" msgid="2422225591006134936">"Буруу PIN код."</string>
<string name="keyguard_label_text" msgid="861796461028298424">"Тайлах бол Цэсийг дараад 0."</string>
<string name="emergency_call_dialog_number_for_display" msgid="696192103195090970">"Яаралтай дугаар"</string>
- <string name="lockscreen_carrier_default" msgid="8963839242565653192">"Үйлчилгээ байхгүй."</string>
+ <string name="lockscreen_carrier_default" msgid="6169005837238288522">"Үйлчилгээ байхгүй"</string>
<string name="lockscreen_screen_locked" msgid="7288443074806832904">"Дэлгэц түгжигдсэн."</string>
<string name="lockscreen_instructions_when_pattern_enabled" msgid="46154051614126049">"Яаралтай дуудлага хийх буюу эсвэл түгжээг тайлах бол цэсийг дарна уу."</string>
<string name="lockscreen_instructions_when_pattern_disabled" msgid="686260028797158364">"Тайлах бол цэсийг дарна уу."</string>
diff --git a/core/res/res/values-mr-rIN/strings.xml b/core/res/res/values-mr-rIN/strings.xml
index 45bc3c5..d557661 100644
--- a/core/res/res/values-mr-rIN/strings.xml
+++ b/core/res/res/values-mr-rIN/strings.xml
@@ -650,7 +650,7 @@
<string name="keyguard_password_wrong_pin_code" msgid="2422225591006134936">"अयोग्य पिन कोड."</string>
<string name="keyguard_label_text" msgid="861796461028298424">"अनलॉक करण्यासाठी, मेनू दाबा नंतर 0."</string>
<string name="emergency_call_dialog_number_for_display" msgid="696192103195090970">"आणीबाणीचा नंबर"</string>
- <string name="lockscreen_carrier_default" msgid="8963839242565653192">"सेवा नाही."</string>
+ <string name="lockscreen_carrier_default" msgid="6169005837238288522">"सेवा नाही"</string>
<string name="lockscreen_screen_locked" msgid="7288443074806832904">"स्क्रीन लॉक केली."</string>
<string name="lockscreen_instructions_when_pattern_enabled" msgid="46154051614126049">"अनलॉक करण्यासाठी मेनू दाबा किंवा आणीबाणीचा कॉल करा."</string>
<string name="lockscreen_instructions_when_pattern_disabled" msgid="686260028797158364">"अनलॉक करण्यासाठी मेनू दाबा."</string>
diff --git a/core/res/res/values-ms-rMY/strings.xml b/core/res/res/values-ms-rMY/strings.xml
index 1508d0e..8e5113b 100644
--- a/core/res/res/values-ms-rMY/strings.xml
+++ b/core/res/res/values-ms-rMY/strings.xml
@@ -650,7 +650,7 @@
<string name="keyguard_password_wrong_pin_code" msgid="2422225591006134936">"Kod PIN salah."</string>
<string name="keyguard_label_text" msgid="861796461028298424">"Untuk membuka kunci, tekan Menu, kemudian 0."</string>
<string name="emergency_call_dialog_number_for_display" msgid="696192103195090970">"Nombor kecemasan"</string>
- <string name="lockscreen_carrier_default" msgid="8963839242565653192">"Tiada perkhidmatan."</string>
+ <string name="lockscreen_carrier_default" msgid="6169005837238288522">"Tiada perkhidmatan"</string>
<string name="lockscreen_screen_locked" msgid="7288443074806832904">"Skrin dikunci."</string>
<string name="lockscreen_instructions_when_pattern_enabled" msgid="46154051614126049">"Tekan Menu untuk menyahsekat atau membuat panggilan kecemasan."</string>
<string name="lockscreen_instructions_when_pattern_disabled" msgid="686260028797158364">"Tekan Menu untuk membuka kunci."</string>
diff --git a/core/res/res/values-my-rMM/strings.xml b/core/res/res/values-my-rMM/strings.xml
index a09093f..427e844 100644
--- a/core/res/res/values-my-rMM/strings.xml
+++ b/core/res/res/values-my-rMM/strings.xml
@@ -650,7 +650,7 @@
<string name="keyguard_password_wrong_pin_code" msgid="2422225591006134936">"ပင်နံပါတ်မှားနေပါသည်"</string>
<string name="keyguard_label_text" msgid="861796461028298424">"သော့ဖွင့်ရန် Menu ထိုနောက်0ကိုနှိပ်ပါ"</string>
<string name="emergency_call_dialog_number_for_display" msgid="696192103195090970">"အရေးပေါ်နံပါတ်"</string>
- <string name="lockscreen_carrier_default" msgid="8963839242565653192">"ဆားဗစ် မရှိပါ"</string>
+ <string name="lockscreen_carrier_default" msgid="6169005837238288522">"ဝန်ဆောင်မှု မရှိပါ"</string>
<string name="lockscreen_screen_locked" msgid="7288443074806832904">"မျက်နှာပြင်အားသော့ချထားသည်"</string>
<string name="lockscreen_instructions_when_pattern_enabled" msgid="46154051614126049">"ဖွင့်ရန်သို့မဟုတ်အရေးပေါ်ခေါ်ဆိုခြင်းပြုလုပ်ရန် မီနူးကိုနှိပ်ပါ"</string>
<string name="lockscreen_instructions_when_pattern_disabled" msgid="686260028797158364">"မီးနူးကို နှိပ်ခြင်းဖြင့် သော့ဖွင့်ပါ"</string>
diff --git a/core/res/res/values-nb/strings.xml b/core/res/res/values-nb/strings.xml
index c2d2b80..e231e2a 100644
--- a/core/res/res/values-nb/strings.xml
+++ b/core/res/res/values-nb/strings.xml
@@ -650,7 +650,7 @@
<string name="keyguard_password_wrong_pin_code" msgid="2422225591006134936">"Feil personlig kode."</string>
<string name="keyguard_label_text" msgid="861796461028298424">"For å låse opp, trykk på menyknappen og deretter 0."</string>
<string name="emergency_call_dialog_number_for_display" msgid="696192103195090970">"Nødnummer"</string>
- <string name="lockscreen_carrier_default" msgid="8963839242565653192">"Ingen tjeneste."</string>
+ <string name="lockscreen_carrier_default" msgid="6169005837238288522">"Ingen dekning"</string>
<string name="lockscreen_screen_locked" msgid="7288443074806832904">"Skjermen er låst"</string>
<string name="lockscreen_instructions_when_pattern_enabled" msgid="46154051614126049">"Trykk på menyknappen for å låse opp eller ringe et nødnummer."</string>
<string name="lockscreen_instructions_when_pattern_disabled" msgid="686260028797158364">"Trykk på menyknappen for å låse opp."</string>
diff --git a/core/res/res/values-ne-rNP/strings.xml b/core/res/res/values-ne-rNP/strings.xml
index e4b6c22..8181f6c 100644
--- a/core/res/res/values-ne-rNP/strings.xml
+++ b/core/res/res/values-ne-rNP/strings.xml
@@ -650,7 +650,7 @@
<string name="keyguard_password_wrong_pin_code" msgid="2422225591006134936">"गलत PIN कोड।"</string>
<string name="keyguard_label_text" msgid="861796461028298424">"अनलक गर्न मेनु थिच्नुहोस् र त्यसपछि ० थिच्नुहोस्।"</string>
<string name="emergency_call_dialog_number_for_display" msgid="696192103195090970">"आपतकालीन नम्बर"</string>
- <string name="lockscreen_carrier_default" msgid="8963839242565653192">"सेवा छैन।"</string>
+ <string name="lockscreen_carrier_default" msgid="6169005837238288522">"कुनै सेवा छैन"</string>
<string name="lockscreen_screen_locked" msgid="7288443074806832904">"स्क्रिन लक गरिएको।"</string>
<string name="lockscreen_instructions_when_pattern_enabled" msgid="46154051614126049">"अनलक वा आपतकालीन कल गर्न मेनु थिच्नुहोस्।"</string>
<string name="lockscreen_instructions_when_pattern_disabled" msgid="686260028797158364">"अनलक गर्न मेनु थिच्नुहोस्।"</string>
diff --git a/core/res/res/values-nl/strings.xml b/core/res/res/values-nl/strings.xml
index b208f48..361a816 100644
--- a/core/res/res/values-nl/strings.xml
+++ b/core/res/res/values-nl/strings.xml
@@ -650,7 +650,7 @@
<string name="keyguard_password_wrong_pin_code" msgid="2422225591006134936">"Onjuiste pincode."</string>
<string name="keyguard_label_text" msgid="861796461028298424">"Druk op \'Menu\' en vervolgens op 0 om te ontgrendelen."</string>
<string name="emergency_call_dialog_number_for_display" msgid="696192103195090970">"Alarmnummer"</string>
- <string name="lockscreen_carrier_default" msgid="8963839242565653192">"Geen service"</string>
+ <string name="lockscreen_carrier_default" msgid="6169005837238288522">"Geen service"</string>
<string name="lockscreen_screen_locked" msgid="7288443074806832904">"Scherm vergrendeld."</string>
<string name="lockscreen_instructions_when_pattern_enabled" msgid="46154051614126049">"Druk op \'Menu\' om te ontgrendelen of noodoproep te plaatsen."</string>
<string name="lockscreen_instructions_when_pattern_disabled" msgid="686260028797158364">"Druk op \'Menu\' om te ontgrendelen."</string>
diff --git a/core/res/res/values-pa-rIN/strings.xml b/core/res/res/values-pa-rIN/strings.xml
index 8272d71..0b80c4a 100644
--- a/core/res/res/values-pa-rIN/strings.xml
+++ b/core/res/res/values-pa-rIN/strings.xml
@@ -650,7 +650,7 @@
<string name="keyguard_password_wrong_pin_code" msgid="2422225591006134936">"ਗ਼ਲਤ PIN ਕੋਡ।"</string>
<string name="keyguard_label_text" msgid="861796461028298424">"ਅਨਲੌਕ ਕਰਨ ਲਈ, ਪਹਿਲਾਂ ਮੀਨੂ ਫਿਰ 0 ਦਬਾਓ।"</string>
<string name="emergency_call_dialog_number_for_display" msgid="696192103195090970">"ਐਮਰਜੈਂਸੀ ਨੰਬਰ"</string>
- <string name="lockscreen_carrier_default" msgid="8963839242565653192">"ਕੋਈ ਸੇਵਾ ਨਹੀਂ।"</string>
+ <string name="lockscreen_carrier_default" msgid="6169005837238288522">"ਕੋਈ ਸੇਵਾ ਨਹੀਂ"</string>
<string name="lockscreen_screen_locked" msgid="7288443074806832904">"ਸਕ੍ਰੀਨ ਲੌਕ ਕੀਤੀ।"</string>
<string name="lockscreen_instructions_when_pattern_enabled" msgid="46154051614126049">"ਅਨਲੌਕ ਕਰਨ ਲਈ ਮੀਨੂ ਦਬਾਓ ਜਾਂ ਐਮਰਜੈਂਸੀ ਕਾਲ ਕਰੋ।"</string>
<string name="lockscreen_instructions_when_pattern_disabled" msgid="686260028797158364">"ਅਨਲੌਕ ਕਰਨ ਲਈ ਮੀਨੂ ਦਬਾਓ।"</string>
diff --git a/core/res/res/values-pl/strings.xml b/core/res/res/values-pl/strings.xml
index 34d26dc..d72281b 100644
--- a/core/res/res/values-pl/strings.xml
+++ b/core/res/res/values-pl/strings.xml
@@ -652,7 +652,7 @@
<string name="keyguard_password_wrong_pin_code" msgid="2422225591006134936">"Błędny kod PIN"</string>
<string name="keyguard_label_text" msgid="861796461028298424">"Aby odblokować, naciśnij Menu, a następnie 0."</string>
<string name="emergency_call_dialog_number_for_display" msgid="696192103195090970">"Numer alarmowy"</string>
- <string name="lockscreen_carrier_default" msgid="8963839242565653192">"Brak usługi"</string>
+ <string name="lockscreen_carrier_default" msgid="6169005837238288522">"Brak usługi"</string>
<string name="lockscreen_screen_locked" msgid="7288443074806832904">"Ekran zablokowany."</string>
<string name="lockscreen_instructions_when_pattern_enabled" msgid="46154051614126049">"Naciśnij Menu, aby odblokować lub wykonać połączenie alarmowe."</string>
<string name="lockscreen_instructions_when_pattern_disabled" msgid="686260028797158364">"Naciśnij Menu, aby odblokować."</string>
diff --git a/core/res/res/values-pt-rBR/strings.xml b/core/res/res/values-pt-rBR/strings.xml
index bd1b2f0..daf17ef 100644
--- a/core/res/res/values-pt-rBR/strings.xml
+++ b/core/res/res/values-pt-rBR/strings.xml
@@ -650,7 +650,7 @@
<string name="keyguard_password_wrong_pin_code" msgid="2422225591006134936">"Código PIN incorreto."</string>
<string name="keyguard_label_text" msgid="861796461028298424">"Para desbloquear, pressione Menu e, em seguida, 0."</string>
<string name="emergency_call_dialog_number_for_display" msgid="696192103195090970">"Número de emergência"</string>
- <string name="lockscreen_carrier_default" msgid="8963839242565653192">"Sem serviço."</string>
+ <string name="lockscreen_carrier_default" msgid="6169005837238288522">"Sem serviço"</string>
<string name="lockscreen_screen_locked" msgid="7288443074806832904">"Tela bloqueada."</string>
<string name="lockscreen_instructions_when_pattern_enabled" msgid="46154051614126049">"Pressione Menu para desbloquear ou fazer uma chamada de emergência."</string>
<string name="lockscreen_instructions_when_pattern_disabled" msgid="686260028797158364">"Pressione Menu para desbloquear."</string>
diff --git a/core/res/res/values-pt-rPT/strings.xml b/core/res/res/values-pt-rPT/strings.xml
index 897dcd6..b98a1cd 100644
--- a/core/res/res/values-pt-rPT/strings.xml
+++ b/core/res/res/values-pt-rPT/strings.xml
@@ -650,7 +650,7 @@
<string name="keyguard_password_wrong_pin_code" msgid="2422225591006134936">"Código PIN incorreto."</string>
<string name="keyguard_label_text" msgid="861796461028298424">"Para desbloquear, prima Menu e, em seguida, 0."</string>
<string name="emergency_call_dialog_number_for_display" msgid="696192103195090970">"Número de emergência"</string>
- <string name="lockscreen_carrier_default" msgid="8963839242565653192">"Nenhum serviço"</string>
+ <string name="lockscreen_carrier_default" msgid="6169005837238288522">"Sem rede móvel"</string>
<string name="lockscreen_screen_locked" msgid="7288443074806832904">"Ecrã bloqueado."</string>
<string name="lockscreen_instructions_when_pattern_enabled" msgid="46154051614126049">"Prima Menu para desbloquear ou efectuar uma chamada de emergência."</string>
<string name="lockscreen_instructions_when_pattern_disabled" msgid="686260028797158364">"Prima Menu para desbloquear."</string>
diff --git a/core/res/res/values-pt/strings.xml b/core/res/res/values-pt/strings.xml
index bd1b2f0..daf17ef 100644
--- a/core/res/res/values-pt/strings.xml
+++ b/core/res/res/values-pt/strings.xml
@@ -650,7 +650,7 @@
<string name="keyguard_password_wrong_pin_code" msgid="2422225591006134936">"Código PIN incorreto."</string>
<string name="keyguard_label_text" msgid="861796461028298424">"Para desbloquear, pressione Menu e, em seguida, 0."</string>
<string name="emergency_call_dialog_number_for_display" msgid="696192103195090970">"Número de emergência"</string>
- <string name="lockscreen_carrier_default" msgid="8963839242565653192">"Sem serviço."</string>
+ <string name="lockscreen_carrier_default" msgid="6169005837238288522">"Sem serviço"</string>
<string name="lockscreen_screen_locked" msgid="7288443074806832904">"Tela bloqueada."</string>
<string name="lockscreen_instructions_when_pattern_enabled" msgid="46154051614126049">"Pressione Menu para desbloquear ou fazer uma chamada de emergência."</string>
<string name="lockscreen_instructions_when_pattern_disabled" msgid="686260028797158364">"Pressione Menu para desbloquear."</string>
diff --git a/core/res/res/values-ro/strings.xml b/core/res/res/values-ro/strings.xml
index 428e139..6c576ad 100644
--- a/core/res/res/values-ro/strings.xml
+++ b/core/res/res/values-ro/strings.xml
@@ -651,7 +651,7 @@
<string name="keyguard_password_wrong_pin_code" msgid="2422225591006134936">"Cod PIN incorect."</string>
<string name="keyguard_label_text" msgid="861796461028298424">"Pentru a debloca, apăsaţi Meniu, apoi 0."</string>
<string name="emergency_call_dialog_number_for_display" msgid="696192103195090970">"Număr de urgență"</string>
- <string name="lockscreen_carrier_default" msgid="8963839242565653192">"Fără serviciu."</string>
+ <string name="lockscreen_carrier_default" msgid="6169005837238288522">"Fără semnal"</string>
<string name="lockscreen_screen_locked" msgid="7288443074806832904">"Ecranul este blocat."</string>
<string name="lockscreen_instructions_when_pattern_enabled" msgid="46154051614126049">"Apăsați Meniu pentru a debloca sau pentru a efectua apeluri de urgență."</string>
<string name="lockscreen_instructions_when_pattern_disabled" msgid="686260028797158364">"Apăsaţi Meniu pentru deblocare."</string>
diff --git a/core/res/res/values-ru/strings.xml b/core/res/res/values-ru/strings.xml
index 525e088..15cbe4a 100644
--- a/core/res/res/values-ru/strings.xml
+++ b/core/res/res/values-ru/strings.xml
@@ -652,7 +652,7 @@
<string name="keyguard_password_wrong_pin_code" msgid="2422225591006134936">"Неверный PIN-код."</string>
<string name="keyguard_label_text" msgid="861796461028298424">"Для разблокировки нажмите \"Меню\", а затем 0."</string>
<string name="emergency_call_dialog_number_for_display" msgid="696192103195090970">"Экстренная служба"</string>
- <string name="lockscreen_carrier_default" msgid="8963839242565653192">"Нет сигнала"</string>
+ <string name="lockscreen_carrier_default" msgid="6169005837238288522">"Нет сигнала"</string>
<string name="lockscreen_screen_locked" msgid="7288443074806832904">"Экран заблокирован."</string>
<string name="lockscreen_instructions_when_pattern_enabled" msgid="46154051614126049">"Нажмите \"Меню\", чтобы разблокировать экран или вызвать службу экстренной помощи."</string>
<string name="lockscreen_instructions_when_pattern_disabled" msgid="686260028797158364">"Для разблокировки нажмите \"Меню\"."</string>
diff --git a/core/res/res/values-si-rLK/strings.xml b/core/res/res/values-si-rLK/strings.xml
index 0135884..5e6cfcd 100644
--- a/core/res/res/values-si-rLK/strings.xml
+++ b/core/res/res/values-si-rLK/strings.xml
@@ -650,7 +650,7 @@
<string name="keyguard_password_wrong_pin_code" msgid="2422225591006134936">"වැරදි PIN කේතයකි."</string>
<string name="keyguard_label_text" msgid="861796461028298424">"අගුළු ඇරීමට, මෙනුව ඔබා පසුව 0 ද ඔබන්න."</string>
<string name="emergency_call_dialog_number_for_display" msgid="696192103195090970">"හදිසි ඇමතුම් අංකය"</string>
- <string name="lockscreen_carrier_default" msgid="8963839242565653192">"සේවාව නැත."</string>
+ <string name="lockscreen_carrier_default" msgid="6169005837238288522">"සේවාව නැත"</string>
<string name="lockscreen_screen_locked" msgid="7288443074806832904">"තිරය අගුළු දමා ඇත."</string>
<string name="lockscreen_instructions_when_pattern_enabled" msgid="46154051614126049">"අගුළු හැරීමට මෙනුව ඔබන්න හෝ හදිසි ඇමතුම ලබාගන්න."</string>
<string name="lockscreen_instructions_when_pattern_disabled" msgid="686260028797158364">"අගුළු හැරීමට මෙනු ඔබන්න."</string>
diff --git a/core/res/res/values-sk/strings.xml b/core/res/res/values-sk/strings.xml
index 5bd6867..dd3cbca 100644
--- a/core/res/res/values-sk/strings.xml
+++ b/core/res/res/values-sk/strings.xml
@@ -652,7 +652,7 @@
<string name="keyguard_password_wrong_pin_code" msgid="2422225591006134936">"Nesprávny kód PIN."</string>
<string name="keyguard_label_text" msgid="861796461028298424">"Ak chcete telefón odomknúť, stlačte Menu a následne 0."</string>
<string name="emergency_call_dialog_number_for_display" msgid="696192103195090970">"Číslo tiesňového volania"</string>
- <string name="lockscreen_carrier_default" msgid="8963839242565653192">"Žiadny signál"</string>
+ <string name="lockscreen_carrier_default" msgid="6169005837238288522">"Žiadny signál"</string>
<string name="lockscreen_screen_locked" msgid="7288443074806832904">"Obrazovka je uzamknutá."</string>
<string name="lockscreen_instructions_when_pattern_enabled" msgid="46154051614126049">"Ak chcete odomknúť telefón alebo uskutočniť tiesňové volanie, stlačte Menu."</string>
<string name="lockscreen_instructions_when_pattern_disabled" msgid="686260028797158364">"Telefón odomknete stlačením tlačidla Menu."</string>
diff --git a/core/res/res/values-sl/strings.xml b/core/res/res/values-sl/strings.xml
index 02a748b3..5c554ab 100644
--- a/core/res/res/values-sl/strings.xml
+++ b/core/res/res/values-sl/strings.xml
@@ -652,7 +652,7 @@
<string name="keyguard_password_wrong_pin_code" msgid="2422225591006134936">"Napačna koda PIN."</string>
<string name="keyguard_label_text" msgid="861796461028298424">"Če želite telefon odkleniti, pritisnite meni in nato 0."</string>
<string name="emergency_call_dialog_number_for_display" msgid="696192103195090970">"Številka za klic v sili"</string>
- <string name="lockscreen_carrier_default" msgid="8963839242565653192">"Ni storitve."</string>
+ <string name="lockscreen_carrier_default" msgid="6169005837238288522">"Ni signala"</string>
<string name="lockscreen_screen_locked" msgid="7288443074806832904">"Zaslon je zaklenjen."</string>
<string name="lockscreen_instructions_when_pattern_enabled" msgid="46154051614126049">"Če želite odkleniti napravo ali opraviti klic v sili, pritisnite meni."</string>
<string name="lockscreen_instructions_when_pattern_disabled" msgid="686260028797158364">"Če želite odkleniti, pritisnite meni."</string>
diff --git a/core/res/res/values-sq-rAL/strings.xml b/core/res/res/values-sq-rAL/strings.xml
index 128dc23..45a4c68 100644
--- a/core/res/res/values-sq-rAL/strings.xml
+++ b/core/res/res/values-sq-rAL/strings.xml
@@ -650,7 +650,7 @@
<string name="keyguard_password_wrong_pin_code" msgid="2422225591006134936">"Kodi PIN është i pasaktë."</string>
<string name="keyguard_label_text" msgid="861796461028298424">"Për të shkyçur, shtyp \"Meny\" pastaj 0."</string>
<string name="emergency_call_dialog_number_for_display" msgid="696192103195090970">"Numri i urgjencës"</string>
- <string name="lockscreen_carrier_default" msgid="8963839242565653192">"Nuk ka shërbim."</string>
+ <string name="lockscreen_carrier_default" msgid="6169005837238288522">"Nuk ka shërbim"</string>
<string name="lockscreen_screen_locked" msgid="7288443074806832904">"Ekrani është i kyçur."</string>
<string name="lockscreen_instructions_when_pattern_enabled" msgid="46154051614126049">"Shtyp \"Meny\" për të shkyçur ose për të kryer telefonatë urgjence."</string>
<string name="lockscreen_instructions_when_pattern_disabled" msgid="686260028797158364">"Shtyp \"Meny\" për të shkyçur."</string>
diff --git a/core/res/res/values-sr/strings.xml b/core/res/res/values-sr/strings.xml
index aec317e..71538de 100644
--- a/core/res/res/values-sr/strings.xml
+++ b/core/res/res/values-sr/strings.xml
@@ -651,7 +651,7 @@
<string name="keyguard_password_wrong_pin_code" msgid="2422225591006134936">"PIN кôд је нетачан."</string>
<string name="keyguard_label_text" msgid="861796461028298424">"Да бисте откључали, притисните „Мени“, а затим 0."</string>
<string name="emergency_call_dialog_number_for_display" msgid="696192103195090970">"Број за хитне случајеве"</string>
- <string name="lockscreen_carrier_default" msgid="8963839242565653192">"Нема услуге."</string>
+ <string name="lockscreen_carrier_default" msgid="6169005837238288522">"Мобилна мрежа није доступна"</string>
<string name="lockscreen_screen_locked" msgid="7288443074806832904">"Екран је закључан."</string>
<string name="lockscreen_instructions_when_pattern_enabled" msgid="46154051614126049">"Притисните „Мени“ да бисте откључали телефон или упутите хитан позив."</string>
<string name="lockscreen_instructions_when_pattern_disabled" msgid="686260028797158364">"Притисните „Мени“ за откључавање."</string>
diff --git a/core/res/res/values-sv/strings.xml b/core/res/res/values-sv/strings.xml
index 1032768..49a3862 100644
--- a/core/res/res/values-sv/strings.xml
+++ b/core/res/res/values-sv/strings.xml
@@ -650,7 +650,7 @@
<string name="keyguard_password_wrong_pin_code" msgid="2422225591006134936">"Fel PIN-kod."</string>
<string name="keyguard_label_text" msgid="861796461028298424">"Tryck på Menu och sedan på 0 om du vill låsa upp."</string>
<string name="emergency_call_dialog_number_for_display" msgid="696192103195090970">"Nödsamtalsnummer"</string>
- <string name="lockscreen_carrier_default" msgid="8963839242565653192">"Ingen tjänst."</string>
+ <string name="lockscreen_carrier_default" msgid="6169005837238288522">"Ingen tjänst"</string>
<string name="lockscreen_screen_locked" msgid="7288443074806832904">"Skärmen har låsts."</string>
<string name="lockscreen_instructions_when_pattern_enabled" msgid="46154051614126049">"Tryck på Menu om du vill låsa upp eller ringa nödsamtal."</string>
<string name="lockscreen_instructions_when_pattern_disabled" msgid="686260028797158364">"Tryck på Menu om du vill låsa upp."</string>
diff --git a/core/res/res/values-sw/strings.xml b/core/res/res/values-sw/strings.xml
index 6cc7b13..1d54bcf 100644
--- a/core/res/res/values-sw/strings.xml
+++ b/core/res/res/values-sw/strings.xml
@@ -652,7 +652,7 @@
<string name="keyguard_password_wrong_pin_code" msgid="2422225591006134936">"Msimbo wa PIN usio sahihi."</string>
<string name="keyguard_label_text" msgid="861796461028298424">"Ili kufungua, bofya Menyu kisha 0."</string>
<string name="emergency_call_dialog_number_for_display" msgid="696192103195090970">"Nambari ya dharura"</string>
- <string name="lockscreen_carrier_default" msgid="8963839242565653192">"Hakuna huduma"</string>
+ <string name="lockscreen_carrier_default" msgid="6169005837238288522">"Hakuna huduma"</string>
<string name="lockscreen_screen_locked" msgid="7288443074806832904">"skrini imefungwa."</string>
<string name="lockscreen_instructions_when_pattern_enabled" msgid="46154051614126049">"Bonyeza Menyu ili kufungua au kupiga simu ya dharura."</string>
<string name="lockscreen_instructions_when_pattern_disabled" msgid="686260028797158364">"Bonyeza Menyu ili kufungua."</string>
diff --git a/core/res/res/values-ta-rIN/strings.xml b/core/res/res/values-ta-rIN/strings.xml
index 1cdd590..e520908 100644
--- a/core/res/res/values-ta-rIN/strings.xml
+++ b/core/res/res/values-ta-rIN/strings.xml
@@ -650,7 +650,7 @@
<string name="keyguard_password_wrong_pin_code" msgid="2422225591006134936">"தவறான பின் குறியீடு."</string>
<string name="keyguard_label_text" msgid="861796461028298424">"தடைநீக்க, மெனுவை அழுத்தி பின்பு 0 ஐ அழுத்தவும்."</string>
<string name="emergency_call_dialog_number_for_display" msgid="696192103195090970">"அவசர எண்"</string>
- <string name="lockscreen_carrier_default" msgid="8963839242565653192">"சேவை இல்லை."</string>
+ <string name="lockscreen_carrier_default" msgid="6169005837238288522">"சேவை இல்லை"</string>
<string name="lockscreen_screen_locked" msgid="7288443074806832904">"திரை பூட்டப்பட்டுள்ளது."</string>
<string name="lockscreen_instructions_when_pattern_enabled" msgid="46154051614126049">"தடைநீக்க மெனுவை அழுத்தவும் அல்லது அவசர அழைப்பை மேற்கொள்ளவும்."</string>
<string name="lockscreen_instructions_when_pattern_disabled" msgid="686260028797158364">"திறக்க, மெனுவை அழுத்தவும்."</string>
diff --git a/core/res/res/values-te-rIN/strings.xml b/core/res/res/values-te-rIN/strings.xml
index 11c382e..fd3d5f2 100644
--- a/core/res/res/values-te-rIN/strings.xml
+++ b/core/res/res/values-te-rIN/strings.xml
@@ -650,7 +650,7 @@
<string name="keyguard_password_wrong_pin_code" msgid="2422225591006134936">"చెల్లని పిన్ కోడ్."</string>
<string name="keyguard_label_text" msgid="861796461028298424">"అన్లాక్ చేయడానికి, మెను ఆపై 0ని నొక్కండి."</string>
<string name="emergency_call_dialog_number_for_display" msgid="696192103195090970">"అత్యవసర నంబర్"</string>
- <string name="lockscreen_carrier_default" msgid="8963839242565653192">"సేవ లేదు."</string>
+ <string name="lockscreen_carrier_default" msgid="6169005837238288522">"సేవ లేదు"</string>
<string name="lockscreen_screen_locked" msgid="7288443074806832904">"స్క్రీన్ లాక్ చేయబడింది."</string>
<string name="lockscreen_instructions_when_pattern_enabled" msgid="46154051614126049">"అన్లాక్ చేయడానికి లేదా అత్యవసర కాల్ చేయడానికి మెను నొక్కండి."</string>
<string name="lockscreen_instructions_when_pattern_disabled" msgid="686260028797158364">"అన్లాక్ చేయడానికి మెను నొక్కండి."</string>
diff --git a/core/res/res/values-th/strings.xml b/core/res/res/values-th/strings.xml
index d061873..4b4af30 100644
--- a/core/res/res/values-th/strings.xml
+++ b/core/res/res/values-th/strings.xml
@@ -650,7 +650,7 @@
<string name="keyguard_password_wrong_pin_code" msgid="2422225591006134936">"รหัส PIN ไม่ถูกต้อง"</string>
<string name="keyguard_label_text" msgid="861796461028298424">"หากต้องการปลดล็อก กด เมนู ตามด้วย 0"</string>
<string name="emergency_call_dialog_number_for_display" msgid="696192103195090970">"หมายเลขฉุกเฉิน"</string>
- <string name="lockscreen_carrier_default" msgid="8963839242565653192">"ไม่มีบริการ"</string>
+ <string name="lockscreen_carrier_default" msgid="6169005837238288522">"ไม่มีบริการ"</string>
<string name="lockscreen_screen_locked" msgid="7288443074806832904">"หน้าจอถูกล็อก"</string>
<string name="lockscreen_instructions_when_pattern_enabled" msgid="46154051614126049">"กด เมนู เพื่อปลดล็อกหรือโทรฉุกเฉิน"</string>
<string name="lockscreen_instructions_when_pattern_disabled" msgid="686260028797158364">"กด เมนู เพื่อปลดล็อก"</string>
diff --git a/core/res/res/values-tl/strings.xml b/core/res/res/values-tl/strings.xml
index d87d7a9..afc71b9 100644
--- a/core/res/res/values-tl/strings.xml
+++ b/core/res/res/values-tl/strings.xml
@@ -650,7 +650,7 @@
<string name="keyguard_password_wrong_pin_code" msgid="2422225591006134936">"Maling PIN code."</string>
<string name="keyguard_label_text" msgid="861796461028298424">"Upang i-unlock, pindutin ang Menu pagkatapos ay 0."</string>
<string name="emergency_call_dialog_number_for_display" msgid="696192103195090970">"Pang-emergency na numero"</string>
- <string name="lockscreen_carrier_default" msgid="8963839242565653192">"Walang serbisyo."</string>
+ <string name="lockscreen_carrier_default" msgid="6169005837238288522">"Walang serbisyo"</string>
<string name="lockscreen_screen_locked" msgid="7288443074806832904">"Naka-lock ang screen."</string>
<string name="lockscreen_instructions_when_pattern_enabled" msgid="46154051614126049">"Pindutin ang Menu upang i-unlock o magsagawa ng pang-emergency na tawag."</string>
<string name="lockscreen_instructions_when_pattern_disabled" msgid="686260028797158364">"Pindutin ang Menu upang i-unlock."</string>
diff --git a/core/res/res/values-tr/strings.xml b/core/res/res/values-tr/strings.xml
index a5917e5..7cbf3d2 100644
--- a/core/res/res/values-tr/strings.xml
+++ b/core/res/res/values-tr/strings.xml
@@ -650,7 +650,7 @@
<string name="keyguard_password_wrong_pin_code" msgid="2422225591006134936">"Yanlış PIN kodu."</string>
<string name="keyguard_label_text" msgid="861796461028298424">"Kilidi açmak için önce Menü\'ye, sonra 0\'a basın."</string>
<string name="emergency_call_dialog_number_for_display" msgid="696192103195090970">"Acil durum numarası"</string>
- <string name="lockscreen_carrier_default" msgid="8963839242565653192">"Hizmet yok."</string>
+ <string name="lockscreen_carrier_default" msgid="6169005837238288522">"Hizmet yok"</string>
<string name="lockscreen_screen_locked" msgid="7288443074806832904">"Ekran kilitli."</string>
<string name="lockscreen_instructions_when_pattern_enabled" msgid="46154051614126049">"Kilidi açmak veya acil çağrı yapmak için Menü\'ye basın."</string>
<string name="lockscreen_instructions_when_pattern_disabled" msgid="686260028797158364">"Kilidi açmak için Menü\'ye basın."</string>
diff --git a/core/res/res/values-uk/strings.xml b/core/res/res/values-uk/strings.xml
index 6f77952..9c4f867 100644
--- a/core/res/res/values-uk/strings.xml
+++ b/core/res/res/values-uk/strings.xml
@@ -652,7 +652,7 @@
<string name="keyguard_password_wrong_pin_code" msgid="2422225591006134936">"Неправильний PIN-код."</string>
<string name="keyguard_label_text" msgid="861796461028298424">"Щоб розбл., натисн. меню та 0."</string>
<string name="emergency_call_dialog_number_for_display" msgid="696192103195090970">"Аварійний номер"</string>
- <string name="lockscreen_carrier_default" msgid="8963839242565653192">"Зв’язку немає."</string>
+ <string name="lockscreen_carrier_default" msgid="6169005837238288522">"Немає зв’язку"</string>
<string name="lockscreen_screen_locked" msgid="7288443074806832904">"Екран заблоков."</string>
<string name="lockscreen_instructions_when_pattern_enabled" msgid="46154051614126049">"Натис. меню, щоб розбл. чи зробити авар. виклик."</string>
<string name="lockscreen_instructions_when_pattern_disabled" msgid="686260028797158364">"Натисн. меню, щоб розбл."</string>
diff --git a/core/res/res/values-ur-rPK/strings.xml b/core/res/res/values-ur-rPK/strings.xml
index 55e6efd..dd8aeca 100644
--- a/core/res/res/values-ur-rPK/strings.xml
+++ b/core/res/res/values-ur-rPK/strings.xml
@@ -650,7 +650,7 @@
<string name="keyguard_password_wrong_pin_code" msgid="2422225591006134936">"غلط PIN کوڈ۔"</string>
<string name="keyguard_label_text" msgid="861796461028298424">"غیر مقفل کرنے کیلئے، مینو پھر 0 دبائیں۔"</string>
<string name="emergency_call_dialog_number_for_display" msgid="696192103195090970">"ہنگامی نمبر"</string>
- <string name="lockscreen_carrier_default" msgid="8963839242565653192">"کوئی سروس نہیں ہے۔"</string>
+ <string name="lockscreen_carrier_default" msgid="6169005837238288522">"کوئی سروس نہیں ہے"</string>
<string name="lockscreen_screen_locked" msgid="7288443074806832904">"اسکرین مقفل ہے۔"</string>
<string name="lockscreen_instructions_when_pattern_enabled" msgid="46154051614126049">"غیر مقفل کرنے کیلئے مینو دبائیں یا ہنگامی کال کریں۔"</string>
<string name="lockscreen_instructions_when_pattern_disabled" msgid="686260028797158364">"غیر مقفل کرنے کیلئے مینو دبائیں۔"</string>
diff --git a/core/res/res/values-uz-rUZ/strings.xml b/core/res/res/values-uz-rUZ/strings.xml
index c76df70..ee7da4e 100644
--- a/core/res/res/values-uz-rUZ/strings.xml
+++ b/core/res/res/values-uz-rUZ/strings.xml
@@ -650,7 +650,7 @@
<string name="keyguard_password_wrong_pin_code" msgid="2422225591006134936">"Noto‘g‘ri PIN-kod."</string>
<string name="keyguard_label_text" msgid="861796461028298424">"Qulfni ochish uchun avval \"Menu\"ni, so‘ngra 0 raqamini bosing."</string>
<string name="emergency_call_dialog_number_for_display" msgid="696192103195090970">"Favqulodda raqam"</string>
- <string name="lockscreen_carrier_default" msgid="8963839242565653192">"Aloqa yo‘q."</string>
+ <string name="lockscreen_carrier_default" msgid="6169005837238288522">"Xizmat mavjud emas"</string>
<string name="lockscreen_screen_locked" msgid="7288443074806832904">"Ekran qulflangan."</string>
<string name="lockscreen_instructions_when_pattern_enabled" msgid="46154051614126049">"Qulfdan chiqarish yoki favqulodda qo‘ng‘iroqni amalga oshirish uchun \"Menyu\"ni bosing."</string>
<string name="lockscreen_instructions_when_pattern_disabled" msgid="686260028797158364">"Qulfni ochish uchun \"Menyu\"ga bosing."</string>
diff --git a/core/res/res/values-vi/strings.xml b/core/res/res/values-vi/strings.xml
index a74e10e..5ae3e1e 100644
--- a/core/res/res/values-vi/strings.xml
+++ b/core/res/res/values-vi/strings.xml
@@ -650,7 +650,7 @@
<string name="keyguard_password_wrong_pin_code" msgid="2422225591006134936">"Mã PIN không chính xác."</string>
<string name="keyguard_label_text" msgid="861796461028298424">"Để mở khóa, hãy nhấn vào Menu sau đó nhấn 0."</string>
<string name="emergency_call_dialog_number_for_display" msgid="696192103195090970">"Số khẩn cấp"</string>
- <string name="lockscreen_carrier_default" msgid="8963839242565653192">"Không có dịch vụ nào."</string>
+ <string name="lockscreen_carrier_default" msgid="6169005837238288522">"Không có dịch vụ nào"</string>
<string name="lockscreen_screen_locked" msgid="7288443074806832904">"Màn hình đã khóa."</string>
<string name="lockscreen_instructions_when_pattern_enabled" msgid="46154051614126049">"Nhấn vào Menu để mở khóa hoặc thực hiện cuộc gọi khẩn cấp."</string>
<string name="lockscreen_instructions_when_pattern_disabled" msgid="686260028797158364">"Nhấn vào Menu để mở khóa."</string>
diff --git a/core/res/res/values-zh-rCN/strings.xml b/core/res/res/values-zh-rCN/strings.xml
index 57c91a8..602f607 100644
--- a/core/res/res/values-zh-rCN/strings.xml
+++ b/core/res/res/values-zh-rCN/strings.xml
@@ -650,7 +650,8 @@
<string name="keyguard_password_wrong_pin_code" msgid="2422225591006134936">"PIN码有误。"</string>
<string name="keyguard_label_text" msgid="861796461028298424">"要解锁,请先按 MENU 再按 0。"</string>
<string name="emergency_call_dialog_number_for_display" msgid="696192103195090970">"急救或报警电话"</string>
- <string name="lockscreen_carrier_default" msgid="8963839242565653192">"无服务。"</string>
+ <!-- no translation found for lockscreen_carrier_default (6169005837238288522) -->
+ <skip />
<string name="lockscreen_screen_locked" msgid="7288443074806832904">"屏幕已锁定。"</string>
<string name="lockscreen_instructions_when_pattern_enabled" msgid="46154051614126049">"按 Menu 解锁或进行紧急呼救。"</string>
<string name="lockscreen_instructions_when_pattern_disabled" msgid="686260028797158364">"按 MENU 解锁。"</string>
diff --git a/core/res/res/values-zh-rHK/strings.xml b/core/res/res/values-zh-rHK/strings.xml
index 5b6b456..41952a7 100644
--- a/core/res/res/values-zh-rHK/strings.xml
+++ b/core/res/res/values-zh-rHK/strings.xml
@@ -650,7 +650,7 @@
<string name="keyguard_password_wrong_pin_code" msgid="2422225591006134936">"PIN 碼不正確。"</string>
<string name="keyguard_label_text" msgid="861796461028298424">"如要解鎖,請按選單鍵,然後按 0。"</string>
<string name="emergency_call_dialog_number_for_display" msgid="696192103195090970">"緊急電話號碼"</string>
- <string name="lockscreen_carrier_default" msgid="8963839242565653192">"沒有服務。"</string>
+ <string name="lockscreen_carrier_default" msgid="6169005837238288522">"沒有服務"</string>
<string name="lockscreen_screen_locked" msgid="7288443074806832904">"螢幕已鎖定。"</string>
<string name="lockscreen_instructions_when_pattern_enabled" msgid="46154051614126049">"按選單鍵解鎖或撥打緊急電話。"</string>
<string name="lockscreen_instructions_when_pattern_disabled" msgid="686260028797158364">"按選單鍵解鎖。"</string>
diff --git a/core/res/res/values-zh-rTW/strings.xml b/core/res/res/values-zh-rTW/strings.xml
index 387f8bb..48ea636 100644
--- a/core/res/res/values-zh-rTW/strings.xml
+++ b/core/res/res/values-zh-rTW/strings.xml
@@ -650,7 +650,7 @@
<string name="keyguard_password_wrong_pin_code" msgid="2422225591006134936">"PIN 碼不正確。"</string>
<string name="keyguard_label_text" msgid="861796461028298424">"如要解鎖,請按 Menu 鍵,然後按 0。"</string>
<string name="emergency_call_dialog_number_for_display" msgid="696192103195090970">"緊急電話號碼"</string>
- <string name="lockscreen_carrier_default" msgid="8963839242565653192">"沒有服務。"</string>
+ <string name="lockscreen_carrier_default" msgid="6169005837238288522">"沒有服務"</string>
<string name="lockscreen_screen_locked" msgid="7288443074806832904">"螢幕已鎖定。"</string>
<string name="lockscreen_instructions_when_pattern_enabled" msgid="46154051614126049">"按下 [Menu] 解鎖或撥打緊急電話。"</string>
<string name="lockscreen_instructions_when_pattern_disabled" msgid="686260028797158364">"按下 Menu 鍵解鎖。"</string>
diff --git a/core/res/res/values-zu/strings.xml b/core/res/res/values-zu/strings.xml
index a9b6d14..f597e5d 100644
--- a/core/res/res/values-zu/strings.xml
+++ b/core/res/res/values-zu/strings.xml
@@ -650,7 +650,7 @@
<string name="keyguard_password_wrong_pin_code" msgid="2422225591006134936">"Ikhodi ye-PIN engalungile!"</string>
<string name="keyguard_label_text" msgid="861796461028298424">"Ukuvula, chofoza Menyu bese 0."</string>
<string name="emergency_call_dialog_number_for_display" msgid="696192103195090970">"Inombolo ephuthumayo"</string>
- <string name="lockscreen_carrier_default" msgid="8963839242565653192">"Ayikho isevisi"</string>
+ <string name="lockscreen_carrier_default" msgid="6169005837238288522">"Ayikho isevisi"</string>
<string name="lockscreen_screen_locked" msgid="7288443074806832904">"Isikrini sivaliwe."</string>
<string name="lockscreen_instructions_when_pattern_enabled" msgid="46154051614126049">"Chofoza Menyu ukuvula noma ukwenza ikholi ephuthumayo."</string>
<string name="lockscreen_instructions_when_pattern_disabled" msgid="686260028797158364">"Chofoza Menyu ukuvula."</string>
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index 90306fd..8e36eb0 100644
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -1832,6 +1832,9 @@
<enum name="KEYCODE_MEDIA_STEP_FORWARD" value="274" />
<enum name="KEYCODE_MEDIA_STEP_BACKWARD" value="275" />
<enum name="KEYCODE_SOFT_SLEEP" value="276" />
+ <enum name="KEYCODE_CUT" value="277" />
+ <enum name="KEYCODE_COPY" value="278" />
+ <enum name="KEYCODE_PASTE" value="279" />
</attr>
<!-- ***************************************************************** -->
diff --git a/data/keyboards/Generic.kl b/data/keyboards/Generic.kl
index f10ba96..2a10bdd 100644
--- a/data/keyboards/Generic.kl
+++ b/data/keyboards/Generic.kl
@@ -152,11 +152,11 @@
# key 130 "KEY_PROPS"
# key 131 "KEY_UNDO"
# key 132 "KEY_FRONT"
-# key 133 "KEY_COPY"
+key 133 COPY
# key 134 "KEY_OPEN"
-# key 135 "KEY_PASTE"
+key 135 PASTE
# key 136 "KEY_FIND"
-# key 137 "KEY_CUT"
+key 137 CUT
# key 138 "KEY_HELP"
key 139 MENU
key 140 CALCULATOR
diff --git a/graphics/java/android/graphics/drawable/AnimatedStateListDrawable.java b/graphics/java/android/graphics/drawable/AnimatedStateListDrawable.java
index e235a99..3ed6a78 100644
--- a/graphics/java/android/graphics/drawable/AnimatedStateListDrawable.java
+++ b/graphics/java/android/graphics/drawable/AnimatedStateListDrawable.java
@@ -379,6 +379,7 @@
r, theme, attrs, R.styleable.AnimatedStateListDrawable);
super.inflateWithAttributes(r, parser, a, R.styleable.AnimatedStateListDrawable_visible);
updateStateFromTypedArray(a);
+ updateDensity(r);
a.recycle();
inflateChildElements(r, parser, attrs, theme);
diff --git a/graphics/java/android/graphics/drawable/AnimationDrawable.java b/graphics/java/android/graphics/drawable/AnimationDrawable.java
index 521c74b..6bf3afd 100644
--- a/graphics/java/android/graphics/drawable/AnimationDrawable.java
+++ b/graphics/java/android/graphics/drawable/AnimationDrawable.java
@@ -291,6 +291,7 @@
final TypedArray a = obtainAttributes(r, theme, attrs, R.styleable.AnimationDrawable);
super.inflateWithAttributes(r, parser, a, R.styleable.AnimationDrawable_visible);
updateStateFromTypedArray(a);
+ updateDensity(r);
a.recycle();
inflateChildElements(r, parser, attrs, theme);
diff --git a/graphics/java/android/graphics/drawable/DrawableContainer.java b/graphics/java/android/graphics/drawable/DrawableContainer.java
index 1f7d996..ac72734 100644
--- a/graphics/java/android/graphics/drawable/DrawableContainer.java
+++ b/graphics/java/android/graphics/drawable/DrawableContainer.java
@@ -29,6 +29,7 @@
import android.graphics.Rect;
import android.graphics.PorterDuff.Mode;
import android.os.SystemClock;
+import android.util.DisplayMetrics;
import android.util.LayoutDirection;
import android.util.SparseArray;
import android.view.View;
@@ -581,6 +582,17 @@
return mCurrDrawable;
}
+ /**
+ * Updates the source density based on the resources used to inflate
+ * density-dependent values. Implementing classes should call this method
+ * during inflation.
+ *
+ * @param res the resources used to inflate density-dependent values
+ */
+ final void updateDensity(Resources res) {
+ mDrawableContainerState.updateDensity(res);
+ }
+
@Override
public void applyTheme(Theme theme) {
mDrawableContainerState.applyTheme(theme);
@@ -638,22 +650,22 @@
*/
public abstract static class DrawableContainerState extends ConstantState {
final DrawableContainer mOwner;
- final Resources mRes;
- SparseArray<ConstantStateFuture> mDrawableFutures;
-
+ Resources mSourceRes;
+ int mDensity = DisplayMetrics.DENSITY_DEFAULT;
int mChangingConfigurations;
int mChildrenChangingConfigurations;
+ SparseArray<ConstantStateFuture> mDrawableFutures;
Drawable[] mDrawables;
int mNumChildren;
boolean mVariablePadding = false;
- boolean mPaddingChecked;
+ boolean mCheckedPadding;
Rect mConstantPadding;
boolean mConstantSize = false;
- boolean mComputedConstantSize;
+ boolean mCheckedConstantSize;
int mConstantWidth;
int mConstantHeight;
int mConstantMinimumWidth;
@@ -689,7 +701,13 @@
DrawableContainerState(DrawableContainerState orig, DrawableContainer owner,
Resources res) {
mOwner = owner;
- mRes = res != null ? res : orig != null ? orig.mRes : null;
+
+ final Resources sourceRes = res != null ? res : (orig != null ? orig.mSourceRes : null);
+ mSourceRes = sourceRes;
+
+ final int densityDpi = sourceRes == null ? 0 : sourceRes.getDisplayMetrics().densityDpi;
+ final int sourceDensity = densityDpi == 0 ? DisplayMetrics.DENSITY_DEFAULT : densityDpi;
+ mDensity = sourceDensity;
if (orig != null) {
mChangingConfigurations = orig.mChangingConfigurations;
@@ -713,21 +731,30 @@
mHasTintList = orig.mHasTintList;
mHasTintMode = orig.mHasTintMode;
- // Cloning the following values may require creating futures.
- mConstantPadding = orig.getConstantPadding();
- mPaddingChecked = true;
+ if (orig.mDensity == sourceDensity) {
+ if (orig.mCheckedPadding) {
+ mConstantPadding = new Rect(orig.mConstantPadding);
+ mCheckedPadding = true;
+ }
- mConstantWidth = orig.getConstantWidth();
- mConstantHeight = orig.getConstantHeight();
- mConstantMinimumWidth = orig.getConstantMinimumWidth();
- mConstantMinimumHeight = orig.getConstantMinimumHeight();
- mComputedConstantSize = true;
+ if (orig.mCheckedConstantSize) {
+ mConstantWidth = orig.mConstantWidth;
+ mConstantHeight = orig.mConstantHeight;
+ mConstantMinimumWidth = orig.mConstantMinimumWidth;
+ mConstantMinimumHeight = orig.mConstantMinimumHeight;
+ mCheckedConstantSize = true;
+ }
+ }
- mOpacity = orig.getOpacity();
- mCheckedOpacity = true;
+ if (orig.mCheckedOpacity) {
+ mOpacity = orig.mOpacity;
+ mCheckedOpacity = true;
+ }
- mStateful = orig.isStateful();
- mCheckedStateful = true;
+ if (orig.mCheckedStateful) {
+ mStateful = orig.mStateful;
+ mCheckedStateful = true;
+ }
// Postpone cloning children and futures until we're absolutely
// sure that we're done computing values for the original state.
@@ -783,8 +810,8 @@
mCheckedOpacity = false;
mConstantPadding = null;
- mPaddingChecked = false;
- mComputedConstantSize = false;
+ mCheckedPadding = false;
+ mCheckedConstantSize = false;
return pos;
}
@@ -863,6 +890,31 @@
return changed;
}
+ /**
+ * Updates the source density based on the resources used to inflate
+ * density-dependent values.
+ *
+ * @param res the resources used to inflate density-dependent values
+ */
+ final void updateDensity(Resources res) {
+ if (mSourceRes != null) {
+ mSourceRes = res;
+ }
+
+ // The density may have changed since the last update (if any). Any
+ // dimension-type attributes will need their default values scaled.
+ final int densityDpi = res.getDisplayMetrics().densityDpi;
+ final int newSourceDensity = densityDpi == 0 ?
+ DisplayMetrics.DENSITY_DEFAULT : densityDpi;
+ final int oldSourceDensity = mDensity;
+ mDensity = newSourceDensity;
+
+ if (oldSourceDensity != newSourceDensity) {
+ mCheckedConstantSize = false;
+ mCheckedPadding = false;
+ }
+ }
+
final void applyTheme(Theme theme) {
if (theme != null) {
createAllFutures();
@@ -877,6 +929,8 @@
mChildrenChangingConfigurations |= drawables[i].getChangingConfigurations();
}
}
+
+ updateDensity(theme.getResources());
}
}
@@ -941,7 +995,7 @@
return null;
}
- if ((mConstantPadding != null) || mPaddingChecked) {
+ if ((mConstantPadding != null) || mCheckedPadding) {
return mConstantPadding;
}
@@ -961,7 +1015,7 @@
}
}
- mPaddingChecked = true;
+ mCheckedPadding = true;
return (mConstantPadding = r);
}
@@ -974,7 +1028,7 @@
}
public final int getConstantWidth() {
- if (!mComputedConstantSize) {
+ if (!mCheckedConstantSize) {
computeConstantSize();
}
@@ -982,7 +1036,7 @@
}
public final int getConstantHeight() {
- if (!mComputedConstantSize) {
+ if (!mCheckedConstantSize) {
computeConstantSize();
}
@@ -990,7 +1044,7 @@
}
public final int getConstantMinimumWidth() {
- if (!mComputedConstantSize) {
+ if (!mCheckedConstantSize) {
computeConstantSize();
}
@@ -998,7 +1052,7 @@
}
public final int getConstantMinimumHeight() {
- if (!mComputedConstantSize) {
+ if (!mCheckedConstantSize) {
computeConstantSize();
}
@@ -1006,7 +1060,7 @@
}
protected void computeConstantSize() {
- mComputedConstantSize = true;
+ mCheckedConstantSize = true;
createAllFutures();
@@ -1146,10 +1200,10 @@
*/
public Drawable get(DrawableContainerState state) {
final Drawable result;
- if (state.mRes == null) {
+ if (state.mSourceRes == null) {
result = mConstantState.newDrawable();
} else {
- result = mConstantState.newDrawable(state.mRes);
+ result = mConstantState.newDrawable(state.mSourceRes);
}
result.setLayoutDirection(state.mLayoutDirection);
result.setCallback(state.mOwner);
diff --git a/graphics/java/android/graphics/drawable/LevelListDrawable.java b/graphics/java/android/graphics/drawable/LevelListDrawable.java
index b01c643..4ce52d1 100644
--- a/graphics/java/android/graphics/drawable/LevelListDrawable.java
+++ b/graphics/java/android/graphics/drawable/LevelListDrawable.java
@@ -88,7 +88,13 @@
public void inflate(Resources r, XmlPullParser parser, AttributeSet attrs, Theme theme)
throws XmlPullParserException, IOException {
super.inflate(r, parser, attrs, theme);
+ updateDensity(r);
+ inflateChildElements(r, parser, attrs, theme);
+ }
+
+ private void inflateChildElements(Resources r, XmlPullParser parser, AttributeSet attrs,
+ Theme theme) throws XmlPullParserException, IOException {
int type;
int low = 0;
diff --git a/graphics/java/android/graphics/drawable/StateListDrawable.java b/graphics/java/android/graphics/drawable/StateListDrawable.java
index 758410a..64a9eb5 100644
--- a/graphics/java/android/graphics/drawable/StateListDrawable.java
+++ b/graphics/java/android/graphics/drawable/StateListDrawable.java
@@ -110,6 +110,7 @@
final TypedArray a = obtainAttributes(r, theme, attrs, R.styleable.StateListDrawable);
super.inflateWithAttributes(r, parser, a, R.styleable.StateListDrawable_visible);
updateStateFromTypedArray(a);
+ updateDensity(r);
a.recycle();
inflateChildElements(r, parser, attrs, theme);
diff --git a/graphics/java/android/graphics/drawable/VectorDrawable.java b/graphics/java/android/graphics/drawable/VectorDrawable.java
index f1eaa23..e161f3d 100644
--- a/graphics/java/android/graphics/drawable/VectorDrawable.java
+++ b/graphics/java/android/graphics/drawable/VectorDrawable.java
@@ -603,7 +603,7 @@
final int densityDpi = a.getResources().getDisplayMetrics().densityDpi;
final int newSourceDensity = densityDpi == 0 ? DisplayMetrics.DENSITY_DEFAULT : densityDpi;
final int oldSourceDensity = pathRenderer.mSourceDensity;
- final float densityScale = oldSourceDensity / (float) newSourceDensity;
+ final float densityScale = newSourceDensity / (float) oldSourceDensity;
pathRenderer.mSourceDensity = newSourceDensity;
final int tintMode = a.getInt(R.styleable.VectorDrawable_tintMode, -1);
diff --git a/libs/hwui/BakedOpRenderer.cpp b/libs/hwui/BakedOpRenderer.cpp
index 4d9f9b4..94806ca 100644
--- a/libs/hwui/BakedOpRenderer.cpp
+++ b/libs/hwui/BakedOpRenderer.cpp
@@ -74,53 +74,64 @@
#endif
}
-void BakedOpRenderer::onRenderNodeOp(Info*, const RenderNodeOp&, const BakedOpState&) {
+void BakedOpRenderer::onRenderNodeOp(Info&, const RenderNodeOp&, const BakedOpState&) {
LOG_ALWAYS_FATAL("unsupported operation");
}
-void BakedOpRenderer::onBitmapOp(Info* info, const BitmapOp& op, const BakedOpState& state) {
- info->caches.textureState().activateTexture(0); // TODO: should this be automatic, and/or elsewhere?
- Texture* texture = info->getTexture(op.bitmap);
+void BakedOpRenderer::onBitmapOp(Info& info, const BitmapOp& op, const BakedOpState& state) {
+ info.caches.textureState().activateTexture(0); // TODO: should this be automatic, and/or elsewhere?
+ Texture* texture = info.getTexture(op.bitmap);
if (!texture) return;
const AutoTexture autoCleanup(texture);
const int textureFillFlags = (op.bitmap->colorType() == kAlpha_8_SkColorType)
? TextureFillFlags::IsAlphaMaskTexture : TextureFillFlags::None;
Glop glop;
- GlopBuilder(info->renderState, info->caches, &glop)
+ GlopBuilder(info.renderState, info.caches, &glop)
.setRoundRectClipState(state.roundRectClipState)
.setMeshTexturedUnitQuad(texture->uvMapper)
.setFillTexturePaint(*texture, textureFillFlags, op.paint, state.alpha)
.setTransform(state.computedState.transform, TransformFlags::None)
.setModelViewMapUnitToRectSnap(Rect(0, 0, texture->width, texture->height))
.build();
- info->renderGlop(state, glop);
+ info.renderGlop(state, glop);
}
-void BakedOpRenderer::onRectOp(Info* info, const RectOp& op, const BakedOpState& state) {
+void BakedOpRenderer::onRectOp(Info& info, const RectOp& op, const BakedOpState& state) {
Glop glop;
- GlopBuilder(info->renderState, info->caches, &glop)
+ GlopBuilder(info.renderState, info.caches, &glop)
.setRoundRectClipState(state.roundRectClipState)
.setMeshUnitQuad()
.setFillPaint(*op.paint, state.alpha)
.setTransform(state.computedState.transform, TransformFlags::None)
.setModelViewMapUnitToRect(op.unmappedBounds)
.build();
- info->renderGlop(state, glop);
+ info.renderGlop(state, glop);
}
-void BakedOpRenderer::onSimpleRectsOp(Info* info, const SimpleRectsOp& op, const BakedOpState& state) {
+void BakedOpRenderer::onSimpleRectsOp(Info& info, const SimpleRectsOp& op, const BakedOpState& state) {
Glop glop;
- GlopBuilder(info->renderState, info->caches, &glop)
+ GlopBuilder(info.renderState, info.caches, &glop)
.setRoundRectClipState(state.roundRectClipState)
.setMeshIndexedQuads(&op.vertices[0], op.vertexCount / 4)
.setFillPaint(*op.paint, state.alpha)
.setTransform(state.computedState.transform, TransformFlags::None)
.setModelViewOffsetRect(0, 0, op.unmappedBounds)
.build();
- info->renderGlop(state, glop);
+ info.renderGlop(state, glop);
}
+void BakedOpRenderer::onBeginLayerOp(Info& info, const BeginLayerOp& op, const BakedOpState& state) {
+ LOG_ALWAYS_FATAL("unsupported operation");
+}
+
+void BakedOpRenderer::onEndLayerOp(Info& info, const EndLayerOp& op, const BakedOpState& state) {
+ LOG_ALWAYS_FATAL("unsupported operation");
+}
+
+void BakedOpRenderer::onLayerOp(Info& info, const LayerOp& op, const BakedOpState& state) {
+ LOG_ALWAYS_FATAL("unsupported operation");
+}
} // namespace uirenderer
} // namespace android
diff --git a/libs/hwui/BakedOpRenderer.h b/libs/hwui/BakedOpRenderer.h
index b8b4426..f45dbe4 100644
--- a/libs/hwui/BakedOpRenderer.h
+++ b/libs/hwui/BakedOpRenderer.h
@@ -65,7 +65,7 @@
* These functions will perform the actual rendering of the individual operations in OpenGL,
* given the transform/clip and other state built into the BakedOpState object passed in.
*/
- #define BAKED_OP_RENDERER_METHOD(Type) static void on##Type(Info* info, const Type& op, const BakedOpState& state);
+ #define BAKED_OP_RENDERER_METHOD(Type) static void on##Type(Info& info, const Type& op, const BakedOpState& state);
MAP_OPS(BAKED_OP_RENDERER_METHOD);
};
diff --git a/libs/hwui/BakedOpState.h b/libs/hwui/BakedOpState.h
index e2201ca..ddb8c84 100644
--- a/libs/hwui/BakedOpState.h
+++ b/libs/hwui/BakedOpState.h
@@ -68,7 +68,7 @@
// resolvedClipRect = intersect(parentMatrix * localClip, parentClip)
clipRect = recordedOp.localClipRect;
snapshot.transform->mapRect(clipRect);
- clipRect.doIntersect(snapshot.getClipRect());
+ clipRect.doIntersect(snapshot.getRenderTargetClip());
clipRect.snapToPixelBoundaries();
// resolvedClippedBounds = intersect(resolvedMatrix * opBounds, resolvedClipRect)
diff --git a/libs/hwui/CanvasState.cpp b/libs/hwui/CanvasState.cpp
index eca71c6..6a6cc42 100644
--- a/libs/hwui/CanvasState.cpp
+++ b/libs/hwui/CanvasState.cpp
@@ -259,7 +259,7 @@
currentTransform()->mapRect(r);
r.snapGeometryToPixelBoundaries(snapOut);
- Rect clipRect(currentClipRect());
+ Rect clipRect(currentRenderTargetClip());
clipRect.snapToPixelBoundaries();
if (!clipRect.intersects(r)) return true;
@@ -287,7 +287,7 @@
currentTransform()->mapRect(r);
r.roundOut(); // rounded out to be conservative
- Rect clipRect(currentClipRect());
+ Rect clipRect(currentRenderTargetClip());
clipRect.snapToPixelBoundaries();
if (!clipRect.intersects(r)) return true;
diff --git a/libs/hwui/CanvasState.h b/libs/hwui/CanvasState.h
index be57f44..4709ef4 100644
--- a/libs/hwui/CanvasState.h
+++ b/libs/hwui/CanvasState.h
@@ -147,7 +147,7 @@
void setInvisible(bool value) { mSnapshot->invisible = value; }
inline const mat4* currentTransform() const { return currentSnapshot()->transform; }
- inline const Rect& currentClipRect() const { return currentSnapshot()->getClipRect(); }
+ inline const Rect& currentRenderTargetClip() const { return currentSnapshot()->getRenderTargetClip(); }
inline Region* currentRegion() const { return currentSnapshot()->region; }
inline int currentFlags() const { return currentSnapshot()->flags; }
const Vector3& currentLightCenter() const { return currentSnapshot()->getRelativeLightCenter(); }
diff --git a/libs/hwui/OpReorderer.cpp b/libs/hwui/OpReorderer.cpp
index 7c0e257..c1417c4 100644
--- a/libs/hwui/OpReorderer.cpp
+++ b/libs/hwui/OpReorderer.cpp
@@ -52,7 +52,8 @@
const std::vector<BakedOpState*>& getOps() const { return mOps; }
void dump() const {
- ALOGD(" Batch %p, merging %d, bounds " RECT_STRING, this, mMerging, RECT_ARGS(mBounds));
+ ALOGD(" Batch %p, id %d, merging %d, count %d, bounds " RECT_STRING,
+ this, mBatchId, mMerging, mOps.size(), RECT_ARGS(mBounds));
}
protected:
batchid_t mBatchId;
@@ -201,17 +202,106 @@
Rect mClipRect;
};
-class NullClient: public CanvasStateClient {
- void onViewportInitialized() override {}
- void onSnapshotRestored(const Snapshot& removed, const Snapshot& restored) {}
- GLuint getTargetFbo() const override { return 0; }
-};
-static NullClient sNullClient;
+// iterate back toward target to see if anything drawn since should overlap the new op
+// if no target, merging ops still interate to find similar batch to insert after
+void OpReorderer::LayerReorderer::locateInsertIndex(int batchId, const Rect& clippedBounds,
+ BatchBase** targetBatch, size_t* insertBatchIndex) const {
+ for (int i = mBatches.size() - 1; i >= 0; i--) {
+ BatchBase* overBatch = mBatches[i];
+
+ if (overBatch == *targetBatch) break;
+
+ // TODO: also consider shader shared between batch types
+ if (batchId == overBatch->getBatchId()) {
+ *insertBatchIndex = i + 1;
+ if (!*targetBatch) break; // found insert position, quit
+ }
+
+ if (overBatch->intersects(clippedBounds)) {
+ // NOTE: it may be possible to optimize for special cases where two operations
+ // of the same batch/paint could swap order, such as with a non-mergeable
+ // (clipped) and a mergeable text operation
+ *targetBatch = nullptr;
+ break;
+ }
+ }
+}
+
+void OpReorderer::LayerReorderer::deferUnmergeableOp(LinearAllocator& allocator,
+ BakedOpState* op, batchid_t batchId) {
+ OpBatch* targetBatch = mBatchLookup[batchId];
+
+ size_t insertBatchIndex = mBatches.size();
+ if (targetBatch) {
+ locateInsertIndex(batchId, op->computedState.clippedBounds,
+ (BatchBase**)(&targetBatch), &insertBatchIndex);
+ }
+
+ if (targetBatch) {
+ targetBatch->batchOp(op);
+ } else {
+ // new non-merging batch
+ targetBatch = new (allocator) OpBatch(batchId, op);
+ mBatchLookup[batchId] = targetBatch;
+ mBatches.insert(mBatches.begin() + insertBatchIndex, targetBatch);
+ }
+}
+
+// insertion point of a new batch, will hopefully be immediately after similar batch
+// (generally, should be similar shader)
+void OpReorderer::LayerReorderer::deferMergeableOp(LinearAllocator& allocator,
+ BakedOpState* op, batchid_t batchId, mergeid_t mergeId) {
+ MergingOpBatch* targetBatch = nullptr;
+
+ // Try to merge with any existing batch with same mergeId
+ auto getResult = mMergingBatchLookup[batchId].find(mergeId);
+ if (getResult != mMergingBatchLookup[batchId].end()) {
+ targetBatch = getResult->second;
+ if (!targetBatch->canMergeWith(op)) {
+ targetBatch = nullptr;
+ }
+ }
+
+ size_t insertBatchIndex = mBatches.size();
+ locateInsertIndex(batchId, op->computedState.clippedBounds,
+ (BatchBase**)(&targetBatch), &insertBatchIndex);
+
+ if (targetBatch) {
+ targetBatch->mergeOp(op);
+ } else {
+ // new merging batch
+ targetBatch = new (allocator) MergingOpBatch(batchId, op);
+ mMergingBatchLookup[batchId].insert(std::make_pair(mergeId, targetBatch));
+
+ mBatches.insert(mBatches.begin() + insertBatchIndex, targetBatch);
+ }
+}
+
+void OpReorderer::LayerReorderer::replayBakedOpsImpl(void* arg, BakedOpReceiver* receivers) const {
+ for (const BatchBase* batch : mBatches) {
+ // TODO: different behavior based on batch->isMerging()
+ for (const BakedOpState* op : batch->getOps()) {
+ receivers[op->op->opId](arg, *op->op, *op);
+ }
+ }
+}
+
+void OpReorderer::LayerReorderer::dump() const {
+ for (const BatchBase* batch : mBatches) {
+ batch->dump();
+ }
+}
OpReorderer::OpReorderer()
- : mCanvasState(sNullClient) {
+ : mCanvasState(*this) {
+ mLayerReorderers.emplace_back();
+ mLayerStack.push_back(0);
}
+void OpReorderer::onViewportInitialized() {}
+
+void OpReorderer::onSnapshotRestored(const Snapshot& removed, const Snapshot& restored) {}
+
void OpReorderer::defer(const SkRect& clip, int viewportWidth, int viewportHeight,
const std::vector< sp<RenderNode> >& nodes) {
mCanvasState.initializeSaveStack(viewportWidth, viewportHeight,
@@ -244,11 +334,11 @@
* This allows opIds embedded in the RecordedOps to be used for dispatching to these lambdas. E.g. a
* BitmapOp op then would be dispatched to OpReorderer::onBitmapOp(const BitmapOp&)
*/
-#define OP_RECIEVER(Type) \
+#define OP_RECEIVER(Type) \
[](OpReorderer& reorderer, const RecordedOp& op) { reorderer.on##Type(static_cast<const Type&>(op)); },
void OpReorderer::deferImpl(const DisplayList& displayList) {
static std::function<void(OpReorderer& reorderer, const RecordedOp&)> receivers[] = {
- MAP_OPS(OP_RECIEVER)
+ MAP_OPS(OP_RECEIVER)
};
for (const DisplayList::Chunk& chunk : displayList.getChunks()) {
for (size_t opIndex = chunk.beginOpIndex; opIndex < chunk.endOpIndex; opIndex++) {
@@ -260,23 +350,18 @@
void OpReorderer::replayBakedOpsImpl(void* arg, BakedOpReceiver* receivers) {
ATRACE_NAME("flush drawing commands");
- for (const BatchBase* batch : mBatches) {
- // TODO: different behavior based on batch->isMerging()
- for (const BakedOpState* op : batch->getOps()) {
- receivers[op->op->opId](arg, *op->op, *op);
- }
+ // Relay through layers in reverse order, since layers
+ // later in the list will be drawn by earlier ones
+ for (int i = mLayerReorderers.size() - 1; i >= 0; i--) {
+ mLayerReorderers[i].replayBakedOpsImpl(arg, receivers);
}
}
-BakedOpState* OpReorderer::bakeOpState(const RecordedOp& recordedOp) {
- return BakedOpState::tryConstruct(mAllocator, *mCanvasState.currentSnapshot(), recordedOp);
-}
-
void OpReorderer::onRenderNodeOp(const RenderNodeOp& op) {
if (op.renderNode->nothingToDraw()) {
return;
}
- mCanvasState.save(SkCanvas::kClip_SaveFlag | SkCanvas::kMatrix_SaveFlag);
+ int count = mCanvasState.save(SkCanvas::kClip_SaveFlag | SkCanvas::kMatrix_SaveFlag);
// apply state from RecordedOp
mCanvasState.concatMatrix(op.localMatrix);
@@ -285,10 +370,10 @@
// apply RenderProperties state
if (op.renderNode->applyViewProperties(mCanvasState)) {
- // not rejected do ops...
+ // if node not rejected based on properties, do ops...
deferImpl(op.renderNode->getDisplayList());
}
- mCanvasState.restore();
+ mCanvasState.restoreToCount(count);
}
static batchid_t tessellatedBatchId(const SkPaint& paint) {
@@ -298,104 +383,70 @@
}
void OpReorderer::onBitmapOp(const BitmapOp& op) {
- BakedOpState* bakedStateOp = bakeOpState(op);
+ BakedOpState* bakedStateOp = tryBakeOpState(op);
if (!bakedStateOp) return; // quick rejected
mergeid_t mergeId = (mergeid_t) op.bitmap->getGenerationID();
// TODO: AssetAtlas
-
- deferMergeableOp(bakedStateOp, OpBatchType::Bitmap, mergeId);
+ currentLayer().deferMergeableOp(mAllocator, bakedStateOp, OpBatchType::Bitmap, mergeId);
}
void OpReorderer::onRectOp(const RectOp& op) {
- BakedOpState* bakedStateOp = bakeOpState(op);
+ BakedOpState* bakedStateOp = tryBakeOpState(op);
if (!bakedStateOp) return; // quick rejected
- deferUnmergeableOp(bakedStateOp, tessellatedBatchId(*op.paint));
+ currentLayer().deferUnmergeableOp(mAllocator, bakedStateOp, tessellatedBatchId(*op.paint));
}
void OpReorderer::onSimpleRectsOp(const SimpleRectsOp& op) {
- BakedOpState* bakedStateOp = bakeOpState(op);
+ BakedOpState* bakedStateOp = tryBakeOpState(op);
if (!bakedStateOp) return; // quick rejected
- deferUnmergeableOp(bakedStateOp, OpBatchType::Vertices);
+ currentLayer().deferUnmergeableOp(mAllocator, bakedStateOp, OpBatchType::Vertices);
}
-// iterate back toward target to see if anything drawn since should overlap the new op
-// if no target, merging ops still interate to find similar batch to insert after
-void OpReorderer::locateInsertIndex(int batchId, const Rect& clippedBounds,
- BatchBase** targetBatch, size_t* insertBatchIndex) const {
- for (int i = mBatches.size() - 1; i >= mEarliestBatchIndex; i--) {
- BatchBase* overBatch = mBatches[i];
+// TODO: test rejection at defer time, where the bounds become empty
+void OpReorderer::onBeginLayerOp(const BeginLayerOp& op) {
+ mCanvasState.save(SkCanvas::kClip_SaveFlag | SkCanvas::kMatrix_SaveFlag);
+ mCanvasState.writableSnapshot()->transform->loadIdentity();
+ mCanvasState.writableSnapshot()->initializeViewport(
+ (int) op.unmappedBounds.getWidth(), (int) op.unmappedBounds.getHeight());
+ mCanvasState.writableSnapshot()->roundRectClipState = nullptr;
- if (overBatch == *targetBatch) break;
+ // create a new layer, and push its index on the stack
+ mLayerStack.push_back(mLayerReorderers.size());
+ mLayerReorderers.emplace_back();
+ mLayerReorderers.back().beginLayerOp = &op;
+}
- // TODO: also consider shader shared between batch types
- if (batchId == overBatch->getBatchId()) {
- *insertBatchIndex = i + 1;
- if (!*targetBatch) break; // found insert position, quit
- }
+void OpReorderer::onEndLayerOp(const EndLayerOp& /* ignored */) {
+ mCanvasState.restore();
- if (overBatch->intersects(clippedBounds)) {
- // NOTE: it may be possible to optimize for special cases where two operations
- // of the same batch/paint could swap order, such as with a non-mergeable
- // (clipped) and a mergeable text operation
- *targetBatch = nullptr;
- break;
- }
+ const BeginLayerOp& beginLayerOp = *currentLayer().beginLayerOp;
+
+ // pop finished layer off of the stack
+ int finishedLayerIndex = mLayerStack.back();
+ mLayerStack.pop_back();
+
+ // record the draw operation into the previous layer's list of draw commands
+ // uses state from the associated beginLayerOp, since it has all the state needed for drawing
+ LayerOp* drawLayerOp = new (mAllocator) LayerOp(
+ beginLayerOp.unmappedBounds,
+ beginLayerOp.localMatrix,
+ beginLayerOp.localClipRect,
+ beginLayerOp.paint);
+ BakedOpState* bakedOpState = tryBakeOpState(*drawLayerOp);
+
+ if (bakedOpState) {
+ // Layer will be drawn into parent layer (which is now current, since we popped mLayerStack)
+ currentLayer().deferUnmergeableOp(mAllocator, bakedOpState, OpBatchType::Bitmap);
+ } else {
+ // Layer won't be drawn - delete its drawing batches to prevent it from doing any work
+ mLayerReorderers[finishedLayerIndex].clear();
+ return;
}
}
-void OpReorderer::deferUnmergeableOp(BakedOpState* op, batchid_t batchId) {
- OpBatch* targetBatch = mBatchLookup[batchId];
-
- size_t insertBatchIndex = mBatches.size();
- if (targetBatch) {
- locateInsertIndex(batchId, op->computedState.clippedBounds,
- (BatchBase**)(&targetBatch), &insertBatchIndex);
- }
-
- if (targetBatch) {
- targetBatch->batchOp(op);
- } else {
- // new non-merging batch
- targetBatch = new (mAllocator) OpBatch(batchId, op);
- mBatchLookup[batchId] = targetBatch;
- mBatches.insert(mBatches.begin() + insertBatchIndex, targetBatch);
- }
-}
-
-// insertion point of a new batch, will hopefully be immediately after similar batch
-// (generally, should be similar shader)
-void OpReorderer::deferMergeableOp(BakedOpState* op, batchid_t batchId, mergeid_t mergeId) {
- MergingOpBatch* targetBatch = nullptr;
-
- // Try to merge with any existing batch with same mergeId
- auto getResult = mMergingBatches[batchId].find(mergeId);
- if (getResult != mMergingBatches[batchId].end()) {
- targetBatch = getResult->second;
- if (!targetBatch->canMergeWith(op)) {
- targetBatch = nullptr;
- }
- }
-
- size_t insertBatchIndex = mBatches.size();
- locateInsertIndex(batchId, op->computedState.clippedBounds,
- (BatchBase**)(&targetBatch), &insertBatchIndex);
-
- if (targetBatch) {
- targetBatch->mergeOp(op);
- } else {
- // new merging batch
- targetBatch = new (mAllocator) MergingOpBatch(batchId, op);
- mMergingBatches[batchId].insert(std::make_pair(mergeId, targetBatch));
-
- mBatches.insert(mBatches.begin() + insertBatchIndex, targetBatch);
- }
-}
-
-void OpReorderer::dump() {
- for (const BatchBase* batch : mBatches) {
- batch->dump();
- }
+void OpReorderer::onLayerOp(const LayerOp& op) {
+ LOG_ALWAYS_FATAL("unsupported");
}
} // namespace uirenderer
diff --git a/libs/hwui/OpReorderer.h b/libs/hwui/OpReorderer.h
index 6776a3c..73dc9af 100644
--- a/libs/hwui/OpReorderer.h
+++ b/libs/hwui/OpReorderer.h
@@ -54,19 +54,63 @@
};
}
-class OpReorderer {
+class OpReorderer : public CanvasStateClient {
+ typedef std::function<void(void*, const RecordedOp&, const BakedOpState&)> BakedOpReceiver;
+
+ /**
+ * Stores the deferred render operations and state used to compute ordering
+ * for a single FBO/layer.
+ */
+ class LayerReorderer {
+ public:
+ // iterate back toward target to see if anything drawn since should overlap the new op
+ // if no target, merging ops still iterate to find similar batch to insert after
+ void locateInsertIndex(int batchId, const Rect& clippedBounds,
+ BatchBase** targetBatch, size_t* insertBatchIndex) const;
+
+ void deferUnmergeableOp(LinearAllocator& allocator, BakedOpState* op, batchid_t batchId);
+
+ // insertion point of a new batch, will hopefully be immediately after similar batch
+ // (generally, should be similar shader)
+ void deferMergeableOp(LinearAllocator& allocator,
+ BakedOpState* op, batchid_t batchId, mergeid_t mergeId);
+
+ void replayBakedOpsImpl(void* arg, BakedOpReceiver* receivers) const;
+
+ void clear() {
+ mBatches.clear();
+ }
+
+ void dump() const;
+
+ const BeginLayerOp* beginLayerOp = nullptr;
+
+ private:
+ std::vector<BatchBase*> mBatches;
+
+ /**
+ * Maps the mergeid_t returned by an op's getMergeId() to the most recently seen
+ * MergingDrawBatch of that id. These ids are unique per draw type and guaranteed to not
+ * collide, which avoids the need to resolve mergeid collisions.
+ */
+ std::unordered_map<mergeid_t, MergingOpBatch*> mMergingBatchLookup[OpBatchType::Count];
+
+ // Maps batch ids to the most recent *non-merging* batch of that id
+ OpBatch* mBatchLookup[OpBatchType::Count] = { nullptr };
+
+ };
public:
OpReorderer();
+ virtual ~OpReorderer() {}
// TODO: not final, just presented this way for simplicity. Layers too?
void defer(const SkRect& clip, int viewportWidth, int viewportHeight,
const std::vector< sp<RenderNode> >& nodes);
void defer(int viewportWidth, int viewportHeight, const DisplayList& displayList);
- typedef std::function<void(void*, const RecordedOp&, const BakedOpState&)> BakedOpReceiver;
/**
- * replayBakedOps() is templated based on what class will recieve ops being replayed.
+ * replayBakedOps() is templated based on what class will receive ops being replayed.
*
* It constructs a lookup array of lambdas, which allows a recorded BakeOpState to use
* state->op->opId to lookup a receiver that will be called when the op is replayed.
@@ -77,19 +121,37 @@
*/
#define BAKED_OP_RECEIVER(Type) \
[](void* internalArg, const RecordedOp& op, const BakedOpState& state) { \
- StaticReceiver::on##Type(static_cast<Arg*>(internalArg), static_cast<const Type&>(op), state); \
+ StaticReceiver::on##Type(*(static_cast<Arg*>(internalArg)), static_cast<const Type&>(op), state); \
},
template <typename StaticReceiver, typename Arg>
- void replayBakedOps(Arg* arg) {
+ void replayBakedOps(Arg& arg) {
static BakedOpReceiver receivers[] = {
MAP_OPS(BAKED_OP_RECEIVER)
};
- StaticReceiver::startFrame(*arg);
- replayBakedOpsImpl((void*)arg, receivers);
- StaticReceiver::endFrame(*arg);
+ StaticReceiver::startFrame(arg);
+ replayBakedOpsImpl((void*)&arg, receivers);
+ StaticReceiver::endFrame(arg);
}
+
+ void dump() const {
+ for (auto&& layer : mLayerReorderers) {
+ layer.dump();
+ }
+ }
+
+ ///////////////////////////////////////////////////////////////////
+ /// CanvasStateClient interface
+ ///////////////////////////////////////////////////////////////////
+ virtual void onViewportInitialized() override;
+ virtual void onSnapshotRestored(const Snapshot& removed, const Snapshot& restored) override;
+ virtual GLuint getTargetFbo() const override { return 0; }
+
private:
- BakedOpState* bakeOpState(const RecordedOp& recordedOp);
+ LayerReorderer& currentLayer() { return mLayerReorderers[mLayerStack.back()]; }
+
+ BakedOpState* tryBakeOpState(const RecordedOp& recordedOp) {
+ return BakedOpState::tryConstruct(mAllocator, *mCanvasState.currentSnapshot(), recordedOp);
+ }
void deferImpl(const DisplayList& displayList);
@@ -105,36 +167,27 @@
void on##Type(const Type& op);
MAP_OPS(INTERNAL_OP_HANDLER)
- // iterate back toward target to see if anything drawn since should overlap the new op
- // if no target, merging ops still iterate to find similar batch to insert after
- void locateInsertIndex(int batchId, const Rect& clippedBounds,
- BatchBase** targetBatch, size_t* insertBatchIndex) const;
+ // List of every deferred layer's render state. Replayed in reverse order to render a frame.
+ std::vector<LayerReorderer> mLayerReorderers;
- void deferUnmergeableOp(BakedOpState* op, batchid_t batchId);
+ /*
+ * Stack of indices within mLayerReorderers representing currently active layers. If drawing
+ * layerA within a layerB, will contain, in order:
+ * - 0 (representing FBO 0, always present)
+ * - layerB's index
+ * - layerA's index
+ *
+ * Note that this doesn't vector doesn't always map onto all values of mLayerReorderers. When a
+ * layer is finished deferring, it will still be represented in mLayerReorderers, but it's index
+ * won't be in mLayerStack. This is because it can be replayed, but can't have any more drawing
+ * ops added to it.
+ */
+ std::vector<size_t> mLayerStack;
- // insertion point of a new batch, will hopefully be immediately after similar batch
- // (generally, should be similar shader)
- void deferMergeableOp(BakedOpState* op, batchid_t batchId, mergeid_t mergeId);
-
- void dump();
-
- std::vector<BatchBase*> mBatches;
-
- /**
- * Maps the mergeid_t returned by an op's getMergeId() to the most recently seen
- * MergingDrawBatch of that id. These ids are unique per draw type and guaranteed to not
- * collide, which avoids the need to resolve mergeid collisions.
- */
- std::unordered_map<mergeid_t, MergingOpBatch*> mMergingBatches[OpBatchType::Count];
-
- // Maps batch ids to the most recent *non-merging* batch of that id
- OpBatch* mBatchLookup[OpBatchType::Count] = { nullptr };
CanvasState mCanvasState;
// contains ResolvedOps and Batches
LinearAllocator mAllocator;
-
- int mEarliestBatchIndex = 0;
};
}; // namespace uirenderer
diff --git a/libs/hwui/OpenGLRenderer.cpp b/libs/hwui/OpenGLRenderer.cpp
index cd03ac4..d4f65b6 100644
--- a/libs/hwui/OpenGLRenderer.cpp
+++ b/libs/hwui/OpenGLRenderer.cpp
@@ -223,7 +223,7 @@
void OpenGLRenderer::callDrawGLFunction(Functor* functor, Rect& dirty) {
if (mState.currentlyIgnored()) return;
- Rect clip(mState.currentClipRect());
+ Rect clip(mState.currentRenderTargetClip());
clip.snapToPixelBoundaries();
// Since we don't know what the functor will draw, let's dirty
@@ -488,7 +488,7 @@
currentTransform()->mapRect(bounds);
// Layers only make sense if they are in the framebuffer's bounds
- bounds.doIntersect(mState.currentClipRect());
+ bounds.doIntersect(mState.currentRenderTargetClip());
if (!bounds.isEmpty()) {
// We cannot work with sub-pixels in this case
bounds.snapToPixelBoundaries();
@@ -1036,7 +1036,7 @@
}
void OpenGLRenderer::dirtyLayerUnchecked(Rect& bounds, Region* region) {
- bounds.doIntersect(mState.currentClipRect());
+ bounds.doIntersect(mState.currentRenderTargetClip());
if (!bounds.isEmpty()) {
bounds.snapToPixelBoundaries();
android::Rect dirty(bounds.left, bounds.top, bounds.right, bounds.bottom);
@@ -1084,7 +1084,7 @@
.setMeshIndexedQuads(&mesh[0], quadCount)
.setFillClear()
.setTransform(*currentSnapshot(), transformFlags)
- .setModelViewOffsetRect(0, 0, Rect(currentSnapshot()->getClipRect()))
+ .setModelViewOffsetRect(0, 0, Rect(currentSnapshot()->getRenderTargetClip()))
.build();
renderGlop(glop, GlopRenderType::LayerClear);
@@ -1099,7 +1099,7 @@
///////////////////////////////////////////////////////////////////////////////
bool OpenGLRenderer::storeDisplayState(DeferredDisplayState& state, int stateDeferFlags) {
- const Rect& currentClip = mState.currentClipRect();
+ const Rect& currentClip = mState.currentRenderTargetClip();
const mat4* currentMatrix = currentTransform();
if (stateDeferFlags & kStateDeferFlag_Draw) {
@@ -1187,7 +1187,7 @@
///////////////////////////////////////////////////////////////////////////////
void OpenGLRenderer::setScissorFromClip() {
- Rect clip(mState.currentClipRect());
+ Rect clip(mState.currentRenderTargetClip());
clip.snapToPixelBoundaries();
if (mRenderState.scissor().set(clip.left, getViewportHeight() - clip.bottom,
@@ -1430,7 +1430,7 @@
return;
}
- DeferredDisplayList deferredList(mState.currentClipRect());
+ DeferredDisplayList deferredList(mState.currentRenderTargetClip());
DeferStateStruct deferStruct(deferredList, *this, replayFlags);
renderNode->defer(deferStruct, 0);
@@ -1765,7 +1765,7 @@
// No need to check against the clip, we fill the clip region
if (mState.currentlyIgnored()) return;
- Rect clip(mState.currentClipRect());
+ Rect clip(mState.currentRenderTargetClip());
clip.snapToPixelBoundaries();
SkPaint paint;
@@ -2030,7 +2030,7 @@
}
fontRenderer.setTextureFiltering(linearFilter);
- const Rect& clip(pureTranslate ? writableSnapshot()->getClipRect() : writableSnapshot()->getLocalClip());
+ const Rect& clip(pureTranslate ? writableSnapshot()->getRenderTargetClip() : writableSnapshot()->getLocalClip());
Rect bounds(FLT_MAX / 2.0f, FLT_MAX / 2.0f, FLT_MIN / 2.0f, FLT_MIN / 2.0f);
TextDrawFunctor functor(this, x, y, pureTranslate, alpha, mode, paint);
@@ -2191,7 +2191,7 @@
fontRenderer.setTextureFiltering(linearFilter);
// TODO: Implement better clipping for scaled/rotated text
- const Rect* clip = !pureTranslate ? nullptr : &mState.currentClipRect();
+ const Rect* clip = !pureTranslate ? nullptr : &mState.currentRenderTargetClip();
Rect layerBounds(FLT_MAX / 2.0f, FLT_MAX / 2.0f, FLT_MIN / 2.0f, FLT_MIN / 2.0f);
bool status;
diff --git a/libs/hwui/RecordedOp.h b/libs/hwui/RecordedOp.h
index a69f030..dd01637 100644
--- a/libs/hwui/RecordedOp.h
+++ b/libs/hwui/RecordedOp.h
@@ -41,7 +41,10 @@
OP_FN(BitmapOp) \
OP_FN(RectOp) \
OP_FN(RenderNodeOp) \
- OP_FN(SimpleRectsOp)
+ OP_FN(SimpleRectsOp) \
+ OP_FN(BeginLayerOp) \
+ OP_FN(EndLayerOp) \
+ OP_FN(LayerOp)
// Generate OpId enum
#define IDENTITY_FN(Type) Type,
@@ -112,6 +115,31 @@
const size_t vertexCount;
};
+/**
+ * Stateful operation! denotes the creation of an off-screen layer,
+ * and that commands following will render into it.
+ */
+struct BeginLayerOp : RecordedOp {
+ BeginLayerOp(BASE_PARAMS)
+ : SUPER(BeginLayerOp) {}
+};
+
+/**
+ * Stateful operation! Denotes end of off-screen layer, and that
+ * commands since last BeginLayerOp should be drawn into parent FBO.
+ *
+ * State in this op is empty, it just serves to signal that a layer has been finished.
+ */
+struct EndLayerOp : RecordedOp {
+ EndLayerOp()
+ : RecordedOp(RecordedOpId::EndLayerOp, Rect(0, 0), Matrix4::identity(), Rect(0, 0), nullptr) {}
+};
+
+struct LayerOp : RecordedOp {
+ LayerOp(BASE_PARAMS)
+ : SUPER(LayerOp) {}
+};
+
}; // namespace uirenderer
}; // namespace android
diff --git a/libs/hwui/RecordingCanvas.cpp b/libs/hwui/RecordingCanvas.cpp
index 3b413aa..1f113bc 100644
--- a/libs/hwui/RecordingCanvas.cpp
+++ b/libs/hwui/RecordingCanvas.cpp
@@ -73,6 +73,20 @@
}
// ----------------------------------------------------------------------------
+// CanvasStateClient implementation
+// ----------------------------------------------------------------------------
+
+void RecordingCanvas::onViewportInitialized() {
+
+}
+
+void RecordingCanvas::onSnapshotRestored(const Snapshot& removed, const Snapshot& restored) {
+ if (removed.flags & Snapshot::kFlagIsFboLayer) {
+ addOp(new (alloc()) EndLayerOp());
+ }
+}
+
+// ----------------------------------------------------------------------------
// android/graphics/Canvas state operations
// ----------------------------------------------------------------------------
// Save (layer)
@@ -97,8 +111,66 @@
int RecordingCanvas::saveLayer(float left, float top, float right, float bottom, const SkPaint* paint,
SkCanvas::SaveFlags flags) {
- LOG_ALWAYS_FATAL("TODO");
- return 0;
+ if (!(flags & SkCanvas::kClipToLayer_SaveFlag)) {
+ LOG_ALWAYS_FATAL("unclipped layers not supported");
+ }
+ // force matrix/clip isolation for layer
+ flags |= SkCanvas::kClip_SaveFlag | SkCanvas::kMatrix_SaveFlag;
+
+
+ const Snapshot& previous = *mState.currentSnapshot();
+
+ // initialize the snapshot as though it almost represents an FBO layer so deferred draw
+ // operations will be able to store and restore the current clip and transform info, and
+ // quick rejection will be correct (for display lists)
+
+ const Rect untransformedBounds(left, top, right, bottom);
+
+ // determine clipped bounds relative to previous viewport.
+ Rect visibleBounds = untransformedBounds;
+ previous.transform->mapRect(visibleBounds);
+
+
+ visibleBounds.doIntersect(previous.getRenderTargetClip());
+ visibleBounds.snapToPixelBoundaries();
+
+ Rect previousViewport(0, 0, previous.getViewportWidth(), previous.getViewportHeight());
+ visibleBounds.doIntersect(previousViewport);
+
+ // Map visible bounds back to layer space, and intersect with parameter bounds
+ Rect layerBounds = visibleBounds;
+ Matrix4 inverse;
+ inverse.loadInverse(*previous.transform);
+ inverse.mapRect(layerBounds);
+ layerBounds.doIntersect(untransformedBounds);
+
+ int saveValue = mState.save((int) flags);
+ Snapshot& snapshot = *mState.writableSnapshot();
+
+ // layerBounds is now original bounds, but with clipped to clip
+ // and viewport to ensure it's minimal size.
+ if (layerBounds.isEmpty() || untransformedBounds.isEmpty()) {
+ // Don't bother recording layer, since it's been rejected
+ snapshot.resetClip(0, 0, 0, 0);
+ return saveValue;
+ }
+
+ snapshot.flags |= Snapshot::kFlagFboTarget | Snapshot::kFlagIsFboLayer;
+ snapshot.initializeViewport(untransformedBounds.getWidth(), untransformedBounds.getHeight());
+ snapshot.resetTransform(-untransformedBounds.left, -untransformedBounds.top, 0.0f);
+
+ Rect clip = layerBounds;
+ clip.translate(-untransformedBounds.left, -untransformedBounds.top);
+ snapshot.resetClip(clip.left, clip.top, clip.right, clip.bottom);
+ snapshot.roundRectClipState = nullptr;
+
+ addOp(new (alloc()) BeginLayerOp(
+ Rect(left, top, right, bottom),
+ *previous.transform, // transform to *draw* with
+ previous.getRenderTargetClip(), // clip to *draw* with
+ refPaint(paint)));
+
+ return saveValue;
}
// Matrix
diff --git a/libs/hwui/RecordingCanvas.h b/libs/hwui/RecordingCanvas.h
index 2179e4c..9c32b1a 100644
--- a/libs/hwui/RecordingCanvas.h
+++ b/libs/hwui/RecordingCanvas.h
@@ -52,8 +52,8 @@
// ----------------------------------------------------------------------------
// CanvasStateClient interface
// ----------------------------------------------------------------------------
- virtual void onViewportInitialized() override {}
- virtual void onSnapshotRestored(const Snapshot& removed, const Snapshot& restored) override {}
+ virtual void onViewportInitialized() override;
+ virtual void onSnapshotRestored(const Snapshot& removed, const Snapshot& restored) override;
virtual GLuint getTargetFbo() const override { return -1; }
// ----------------------------------------------------------------------------
diff --git a/libs/hwui/RenderNode.cpp b/libs/hwui/RenderNode.cpp
index 894a2bd..351fbaa 100644
--- a/libs/hwui/RenderNode.cpp
+++ b/libs/hwui/RenderNode.cpp
@@ -929,7 +929,7 @@
const RenderProperties& backgroundProps = backgroundOp->renderNode->properties();
renderer.translate(backgroundProps.getTranslationX(), backgroundProps.getTranslationY());
- // If the projection reciever has an outline, we mask projected content to it
+ // If the projection receiver has an outline, we mask projected content to it
// (which we know, apriori, are all tessellated paths)
renderer.setProjectionPathMask(alloc, projectionReceiverOutline);
diff --git a/libs/hwui/RenderProperties.h b/libs/hwui/RenderProperties.h
index f824cc0..abef806 100644
--- a/libs/hwui/RenderProperties.h
+++ b/libs/hwui/RenderProperties.h
@@ -203,8 +203,8 @@
return RP_SET(mPrimitiveFields.mProjectBackwards, shouldProject);
}
- bool setProjectionReceiver(bool shouldRecieve) {
- return RP_SET(mPrimitiveFields.mProjectionReceiver, shouldRecieve);
+ bool setProjectionReceiver(bool shouldReceive) {
+ return RP_SET(mPrimitiveFields.mProjectionReceiver, shouldReceive);
}
bool isProjectionReceiver() const {
diff --git a/libs/hwui/Snapshot.h b/libs/hwui/Snapshot.h
index aeeda96..4789b33 100644
--- a/libs/hwui/Snapshot.h
+++ b/libs/hwui/Snapshot.h
@@ -158,13 +158,12 @@
/**
* Returns the current clip in render target coordinates.
*/
- const Rect& getRenderTargetClip() { return mClipArea->getClipRect(); }
+ const Rect& getRenderTargetClip() const { return mClipArea->getClipRect(); }
/*
* Accessor functions so that the clip area can stay private
*/
bool clipIsEmpty() const { return mClipArea->isEmpty(); }
- const Rect& getClipRect() const { return mClipArea->getClipRect(); }
const SkRegion& getClipRegion() const { return mClipArea->getClipRegion(); }
bool clipIsSimple() const { return mClipArea->isSimple(); }
const ClipArea& getClipArea() const { return *mClipArea; }
diff --git a/libs/hwui/microbench/OpReordererBench.cpp b/libs/hwui/microbench/OpReordererBench.cpp
index 4c8dedf..cf96d44 100644
--- a/libs/hwui/microbench/OpReordererBench.cpp
+++ b/libs/hwui/microbench/OpReordererBench.cpp
@@ -65,7 +65,7 @@
MicroBench::DoNotOptimize(&reorderer);
BakedOpRenderer::Info info(caches, renderState, 200, 200, true);
- reorderer.replayBakedOps<BakedOpRenderer>(&info);
+ reorderer.replayBakedOps<BakedOpRenderer>(info);
}
StopBenchmarkTiming();
});
diff --git a/libs/hwui/renderthread/CanvasContext.cpp b/libs/hwui/renderthread/CanvasContext.cpp
index 238cf06..f571426 100644
--- a/libs/hwui/renderthread/CanvasContext.cpp
+++ b/libs/hwui/renderthread/CanvasContext.cpp
@@ -323,7 +323,7 @@
BakedOpRenderer::Info info(Caches::getInstance(), mRenderThread.renderState(),
frame.width(), frame.height(), mOpaque);
// TODO: profiler().draw(mCanvas);
- reorderer.replayBakedOps<BakedOpRenderer>(&info);
+ reorderer.replayBakedOps<BakedOpRenderer>(info);
bool drew = info.didDraw;
diff --git a/libs/hwui/unit_tests/OpReordererTests.cpp b/libs/hwui/unit_tests/OpReordererTests.cpp
index e1249fb..d02f89d 100644
--- a/libs/hwui/unit_tests/OpReordererTests.cpp
+++ b/libs/hwui/unit_tests/OpReordererTests.cpp
@@ -27,26 +27,69 @@
namespace android {
namespace uirenderer {
-#define UNSUPPORTED_OP(Info, Type) \
- static void on##Type(Info*, const Type&, const BakedOpState&) { FAIL(); }
+/**
+ * Class that redirects static operation dispatch to virtual methods on a Client class.
+ *
+ * The client is recreated for every op (so data cannot be persisted between operations), but the
+ * virtual dispatch allows for default behaviors to be specified without enumerating each operation
+ * for every test.
+ *
+ * onXXXOp methods fail by default - tests should override ops they expect
+ * startFrame/endFrame do nothing by default - tests should override to intercept
+ */
+template<class CustomClient, class Arg>
+class TestReceiver {
+public:
+#define CLIENT_METHOD(Type) \
+ virtual void on##Type(Arg&, const Type&, const BakedOpState&) { FAIL(); }
+ class Client {
+ public:
+ virtual ~Client() {};
+ MAP_OPS(CLIENT_METHOD)
+
+ virtual void startFrame(Arg& info) {}
+ virtual void endFrame(Arg& info) {}
+ };
+
+#define DISPATCHER_METHOD(Type) \
+ static void on##Type(Arg& arg, const Type& op, const BakedOpState& state) { \
+ CustomClient client; client.on##Type(arg, op, state); \
+ }
+ MAP_OPS(DISPATCHER_METHOD)
+
+ static void startFrame(Arg& info) {
+ CustomClient client;
+ client.startFrame(info);
+ }
+
+ static void endFrame(Arg& info) {
+ CustomClient client;
+ client.endFrame(info);
+ }
+};
class Info {
public:
int index = 0;
};
-class SimpleReceiver {
+// Receiver class which will fail if it receives any ops
+class FailReceiver : public TestReceiver<FailReceiver, Info>::Client {};
+
+class SimpleReceiver : public TestReceiver<SimpleReceiver, Info>::Client {
public:
- static void onBitmapOp(Info* info, const BitmapOp& op, const BakedOpState& state) {
- EXPECT_EQ(1, info->index++);
+ void startFrame(Info& info) override {
+ EXPECT_EQ(0, info.index++);
}
- static void onRectOp(Info* info, const RectOp& op, const BakedOpState& state) {
- EXPECT_EQ(0, info->index++);
+ void onRectOp(Info& info, const RectOp& op, const BakedOpState& state) override {
+ EXPECT_EQ(1, info.index++);
}
- UNSUPPORTED_OP(Info, RenderNodeOp)
- UNSUPPORTED_OP(Info, SimpleRectsOp)
- static void startFrame(Info& info) {}
- static void endFrame(Info& info) {}
+ void onBitmapOp(Info& info, const BitmapOp& op, const BakedOpState& state) override {
+ EXPECT_EQ(2, info.index++);
+ }
+ void endFrame(Info& info) override {
+ EXPECT_EQ(3, info.index++);
+ }
};
TEST(OpReorderer, simple) {
auto dl = TestUtils::createDisplayList<RecordingCanvas>(100, 200, [](RecordingCanvas& canvas) {
@@ -54,28 +97,39 @@
canvas.drawRect(0, 0, 100, 200, SkPaint());
canvas.drawBitmap(bitmap, 10, 10, nullptr);
});
-
OpReorderer reorderer;
reorderer.defer(200, 200, *dl);
Info info;
- reorderer.replayBakedOps<SimpleReceiver>(&info);
+ reorderer.replayBakedOps<TestReceiver<SimpleReceiver, Info>>(info);
+ EXPECT_EQ(4, info.index); // 2 ops + start + end
+}
+
+
+TEST(OpReorderer, simpleRejection) {
+ auto dl = TestUtils::createDisplayList<RecordingCanvas>(200, 200, [](RecordingCanvas& canvas) {
+ canvas.save(SkCanvas::kMatrix_SaveFlag | SkCanvas::kClip_SaveFlag);
+ canvas.clipRect(200, 200, 400, 400, SkRegion::kIntersect_Op); // intersection should be empty
+ canvas.drawRect(0, 0, 400, 400, SkPaint());
+ canvas.restore();
+ });
+ OpReorderer reorderer;
+ reorderer.defer(200, 200, *dl);
+
+ Info info;
+ reorderer.replayBakedOps<TestReceiver<FailReceiver, Info>>(info);
}
static int SIMPLE_BATCHING_LOOPS = 5;
-class SimpleBatchingReceiver {
+class SimpleBatchingReceiver : public TestReceiver<SimpleBatchingReceiver, Info>::Client {
public:
- static void onBitmapOp(Info* info, const BitmapOp& op, const BakedOpState& state) {
- EXPECT_TRUE(info->index++ >= SIMPLE_BATCHING_LOOPS);
+ void onBitmapOp(Info& info, const BitmapOp& op, const BakedOpState& state) override {
+ EXPECT_TRUE(info.index++ >= SIMPLE_BATCHING_LOOPS);
}
- static void onRectOp(Info* info, const RectOp& op, const BakedOpState& state) {
- EXPECT_TRUE(info->index++ < SIMPLE_BATCHING_LOOPS);
+ void onRectOp(Info& info, const RectOp& op, const BakedOpState& state) override {
+ EXPECT_TRUE(info.index++ < SIMPLE_BATCHING_LOOPS);
}
- UNSUPPORTED_OP(Info, RenderNodeOp)
- UNSUPPORTED_OP(Info, SimpleRectsOp)
- static void startFrame(Info& info) {}
- static void endFrame(Info& info) {}
};
TEST(OpReorderer, simpleBatching) {
auto dl = TestUtils::createDisplayList<RecordingCanvas>(200, 200, [](RecordingCanvas& canvas) {
@@ -96,15 +150,14 @@
reorderer.defer(200, 200, *dl);
Info info;
- reorderer.replayBakedOps<SimpleBatchingReceiver>(&info);
+ reorderer.replayBakedOps<TestReceiver<SimpleBatchingReceiver, Info>>(info);
EXPECT_EQ(2 * SIMPLE_BATCHING_LOOPS, info.index); // 2 x loops ops, because no merging (TODO: force no merging)
}
-class RenderNodeReceiver {
+class RenderNodeReceiver : public TestReceiver<RenderNodeReceiver, Info>::Client {
public:
- UNSUPPORTED_OP(Info, BitmapOp)
- static void onRectOp(Info* info, const RectOp& op, const BakedOpState& state) {
- switch(info->index++) {
+ void onRectOp(Info& info, const RectOp& op, const BakedOpState& state) override {
+ switch(info.index++) {
case 0:
EXPECT_EQ(Rect(0, 0, 200, 200), state.computedState.clippedBounds);
EXPECT_EQ(SK_ColorDKGRAY, op.paint->getColor());
@@ -117,10 +170,6 @@
FAIL();
}
}
- UNSUPPORTED_OP(Info, RenderNodeOp)
- UNSUPPORTED_OP(Info, SimpleRectsOp)
- static void startFrame(Info& info) {}
- static void endFrame(Info& info) {}
};
TEST(OpReorderer, renderNode) {
sp<RenderNode> child = TestUtils::createNode<RecordingCanvas>(10, 10, 110, 110, [](RecordingCanvas& canvas) {
@@ -151,22 +200,17 @@
reorderer.defer(SkRect::MakeWH(200, 200), 200, 200, nodes);
Info info;
- reorderer.replayBakedOps<RenderNodeReceiver>(&info);
+ reorderer.replayBakedOps<TestReceiver<RenderNodeReceiver, Info>>(info);
}
-class ClippedReceiver {
+class ClippedReceiver : public TestReceiver<ClippedReceiver, Info>::Client {
public:
- static void onBitmapOp(Info* info, const BitmapOp& op, const BakedOpState& state) {
- EXPECT_EQ(0, info->index++);
+ void onBitmapOp(Info& info, const BitmapOp& op, const BakedOpState& state) override {
+ EXPECT_EQ(0, info.index++);
EXPECT_EQ(Rect(10, 20, 30, 40), state.computedState.clippedBounds);
EXPECT_EQ(Rect(10, 20, 30, 40), state.computedState.clipRect);
EXPECT_TRUE(state.computedState.transform.isIdentity());
}
- UNSUPPORTED_OP(Info, RectOp)
- UNSUPPORTED_OP(Info, RenderNodeOp)
- UNSUPPORTED_OP(Info, SimpleRectsOp)
- static void startFrame(Info& info) {}
- static void endFrame(Info& info) {}
};
TEST(OpReorderer, clipped) {
sp<RenderNode> node = TestUtils::createNode<RecordingCanvas>(0, 0, 200, 200, [](RecordingCanvas& canvas) {
@@ -182,8 +226,106 @@
200, 200, nodes);
Info info;
- reorderer.replayBakedOps<ClippedReceiver>(&info);
+ reorderer.replayBakedOps<TestReceiver<ClippedReceiver, Info>>(info);
}
+
+class SaveLayerSimpleReceiver : public TestReceiver<SaveLayerSimpleReceiver, Info>::Client {
+public:
+ void onRectOp(Info& info, const RectOp& op, const BakedOpState& state) override {
+ EXPECT_EQ(0, info.index++);
+ EXPECT_EQ(Rect(10, 10, 190, 190), op.unmappedBounds);
+ EXPECT_EQ(Rect(0, 0, 180, 180), state.computedState.clippedBounds);
+ EXPECT_EQ(Rect(0, 0, 180, 180), state.computedState.clipRect);
+
+ Matrix4 expectedTransform;
+ expectedTransform.loadTranslate(-10, -10, 0);
+ EXPECT_MATRIX_APPROX_EQ(expectedTransform, state.computedState.transform);
+ }
+ void onLayerOp(Info& info, const LayerOp& op, const BakedOpState& state) override {
+ EXPECT_EQ(1, info.index++);
+ EXPECT_EQ(Rect(10, 10, 190, 190), state.computedState.clippedBounds);
+ EXPECT_EQ(Rect(0, 0, 200, 200), state.computedState.clipRect);
+ EXPECT_TRUE(state.computedState.transform.isIdentity());
+ }
+};
+TEST(OpReorderer, saveLayerSimple) {
+ auto dl = TestUtils::createDisplayList<RecordingCanvas>(200, 200, [](RecordingCanvas& canvas) {
+ canvas.saveLayerAlpha(10, 10, 190, 190, 128, SkCanvas::kClipToLayer_SaveFlag);
+ canvas.drawRect(10, 10, 190, 190, SkPaint());
+ canvas.restore();
+ });
+
+ OpReorderer reorderer;
+ reorderer.defer(200, 200, *dl);
+
+ Info info;
+ reorderer.replayBakedOps<TestReceiver<SaveLayerSimpleReceiver, Info>>(info);
+ EXPECT_EQ(2, info.index);
}
+
+
+// saveLayer1 {rect1, saveLayer2 { rect2 } } will play back as rect2, rect1, layerOp2, layerOp1
+class SaveLayerNestedReceiver : public TestReceiver<SaveLayerNestedReceiver, Info>::Client {
+public:
+ void onRectOp(Info& info, const RectOp& op, const BakedOpState& state) override {
+ const int index = info.index++;
+ if (index == 0) {
+ EXPECT_EQ(Rect(0, 0, 400, 400), op.unmappedBounds); // inner rect
+ } else if (index == 1) {
+ EXPECT_EQ(Rect(0, 0, 800, 800), op.unmappedBounds); // outer rect
+ } else { FAIL(); }
+ }
+ void onLayerOp(Info& info, const LayerOp& op, const BakedOpState& state) override {
+ const int index = info.index++;
+ if (index == 2) {
+ EXPECT_EQ(Rect(0, 0, 400, 400), op.unmappedBounds); // inner layer
+ } else if (index == 3) {
+ EXPECT_EQ(Rect(0, 0, 800, 800), op.unmappedBounds); // outer layer
+ } else { FAIL(); }
+ }
+};
+TEST(OpReorderer, saveLayerNested) {
+ auto dl = TestUtils::createDisplayList<RecordingCanvas>(800, 800, [](RecordingCanvas& canvas) {
+ canvas.saveLayerAlpha(0, 0, 800, 800, 128, SkCanvas::kClipToLayer_SaveFlag);
+ {
+ canvas.drawRect(0, 0, 800, 800, SkPaint());
+ canvas.saveLayerAlpha(0, 0, 400, 400, 128, SkCanvas::kClipToLayer_SaveFlag);
+ {
+ canvas.drawRect(0, 0, 400, 400, SkPaint());
+ }
+ canvas.restore();
+ }
+ canvas.restore();
+ });
+
+ OpReorderer reorderer;
+ reorderer.defer(800, 800, *dl);
+
+ Info info;
+ reorderer.replayBakedOps<TestReceiver<SaveLayerNestedReceiver, Info>>(info);
+ EXPECT_EQ(4, info.index);
}
+
+TEST(OpReorderer, saveLayerContentRejection) {
+ auto dl = TestUtils::createDisplayList<RecordingCanvas>(200, 200, [](RecordingCanvas& canvas) {
+ canvas.save(SkCanvas::kMatrix_SaveFlag | SkCanvas::kClip_SaveFlag);
+ canvas.clipRect(200, 200, 400, 400, SkRegion::kIntersect_Op);
+ canvas.saveLayerAlpha(200, 200, 400, 400, 128, SkCanvas::kClipToLayer_SaveFlag);
+
+ // draw within save layer may still be recorded, but shouldn't be drawn
+ canvas.drawRect(200, 200, 400, 400, SkPaint());
+
+ canvas.restore();
+ canvas.restore();
+ });
+ OpReorderer reorderer;
+ reorderer.defer(200, 200, *dl);
+ Info info;
+
+ // should see no ops, even within the layer, since the layer should be rejected
+ reorderer.replayBakedOps<TestReceiver<FailReceiver, Info>>(info);
+}
+
+} // namespace uirenderer
+} // namespace android
diff --git a/libs/hwui/unit_tests/RecordingCanvasTests.cpp b/libs/hwui/unit_tests/RecordingCanvasTests.cpp
index ce25fc6..c023123 100644
--- a/libs/hwui/unit_tests/RecordingCanvasTests.cpp
+++ b/libs/hwui/unit_tests/RecordingCanvasTests.cpp
@@ -24,11 +24,11 @@
namespace uirenderer {
static void playbackOps(const DisplayList& displayList,
- std::function<void(const RecordedOp&)> opReciever) {
+ std::function<void(const RecordedOp&)> opReceiver) {
for (const DisplayList::Chunk& chunk : displayList.getChunks()) {
for (size_t opIndex = chunk.beginOpIndex; opIndex < chunk.endOpIndex; opIndex++) {
RecordedOp* op = displayList.getOps()[opIndex];
- opReciever(*op);
+ opReceiver(*op);
}
}
}
@@ -109,5 +109,123 @@
ASSERT_EQ(2, count); // two draws observed
}
+TEST(RecordingCanvas, saveLayerSimple) {
+ auto dl = TestUtils::createDisplayList<RecordingCanvas>(200, 200, [](RecordingCanvas& canvas) {
+ canvas.saveLayerAlpha(10, 20, 190, 180, 128, SkCanvas::kARGB_ClipLayer_SaveFlag);
+ canvas.drawRect(10, 20, 190, 180, SkPaint());
+ canvas.restore();
+ });
+ int count = 0;
+ playbackOps(*dl, [&count](const RecordedOp& op) {
+ Matrix4 expectedMatrix;
+ switch(count++) {
+ case 0:
+ EXPECT_EQ(RecordedOpId::BeginLayerOp, op.opId);
+ // TODO: add asserts
+ break;
+ case 1:
+ EXPECT_EQ(RecordedOpId::RectOp, op.opId);
+ EXPECT_EQ(Rect(0, 0, 180, 160), op.localClipRect);
+ EXPECT_EQ(Rect(10, 20, 190, 180), op.unmappedBounds);
+ expectedMatrix.loadTranslate(-10, -20, 0);
+ EXPECT_MATRIX_APPROX_EQ(expectedMatrix, op.localMatrix);
+ break;
+ case 2:
+ EXPECT_EQ(RecordedOpId::EndLayerOp, op.opId);
+ // TODO: add asserts
+ break;
+ default:
+ FAIL();
+ }
+ });
+ EXPECT_EQ(3, count);
}
+
+TEST(RecordingCanvas, saveLayerViewportCrop) {
+ auto dl = TestUtils::createDisplayList<RecordingCanvas>(200, 200, [](RecordingCanvas& canvas) {
+ // shouldn't matter, since saveLayer will clip to its bounds
+ canvas.clipRect(-1000, -1000, 1000, 1000, SkRegion::kReplace_Op);
+
+ canvas.saveLayerAlpha(100, 100, 300, 300, 128, SkCanvas::kARGB_ClipLayer_SaveFlag);
+ canvas.drawRect(0, 0, 400, 400, SkPaint());
+ canvas.restore();
+ });
+ int count = 0;
+ playbackOps(*dl, [&count](const RecordedOp& op) {
+ if (count++ == 1) {
+ Matrix4 expectedMatrix;
+ EXPECT_EQ(RecordedOpId::RectOp, op.opId);
+
+ // recorded clip rect should be intersection of
+ // viewport and saveLayer bounds, in layer space
+ EXPECT_EQ(Rect(0, 0, 100, 100), op.localClipRect);
+ EXPECT_EQ(Rect(0, 0, 400, 400), op.unmappedBounds);
+ expectedMatrix.loadTranslate(-100, -100, 0);
+ EXPECT_MATRIX_APPROX_EQ(expectedMatrix, op.localMatrix);
+ }
+ });
+ EXPECT_EQ(3, count);
}
+
+TEST(RecordingCanvas, saveLayerRotateUnclipped) {
+ auto dl = TestUtils::createDisplayList<RecordingCanvas>(200, 200, [](RecordingCanvas& canvas) {
+ canvas.save(SkCanvas::kMatrix_SaveFlag | SkCanvas::kClip_SaveFlag);
+ canvas.translate(100, 100);
+ canvas.rotate(45);
+ canvas.translate(-50, -50);
+
+ canvas.saveLayerAlpha(0, 0, 100, 100, 128, SkCanvas::kARGB_ClipLayer_SaveFlag);
+ canvas.drawRect(0, 0, 100, 100, SkPaint());
+ canvas.restore();
+
+ canvas.restore();
+ });
+ int count = 0;
+ playbackOps(*dl, [&count](const RecordedOp& op) {
+ if (count++ == 1) {
+ Matrix4 expectedMatrix;
+ EXPECT_EQ(RecordedOpId::RectOp, op.opId);
+
+ // recorded rect doesn't see rotate, since recorded relative to saveLayer bounds
+ EXPECT_EQ(Rect(0, 0, 100, 100), op.localClipRect);
+ EXPECT_EQ(Rect(0, 0, 100, 100), op.unmappedBounds);
+ expectedMatrix.loadIdentity();
+ EXPECT_MATRIX_APPROX_EQ(expectedMatrix, op.localMatrix);
+ }
+ });
+ EXPECT_EQ(3, count);
+}
+
+TEST(RecordingCanvas, saveLayerRotateClipped) {
+ auto dl = TestUtils::createDisplayList<RecordingCanvas>(200, 200, [](RecordingCanvas& canvas) {
+ canvas.save(SkCanvas::kMatrix_SaveFlag | SkCanvas::kClip_SaveFlag);
+ canvas.translate(100, 100);
+ canvas.rotate(45);
+ canvas.translate(-200, -200);
+
+ // area of saveLayer will be clipped to parent viewport, so we ask for 400x400...
+ canvas.saveLayerAlpha(0, 0, 400, 400, 128, SkCanvas::kARGB_ClipLayer_SaveFlag);
+ canvas.drawRect(0, 0, 400, 400, SkPaint());
+ canvas.restore();
+
+ canvas.restore();
+ });
+ int count = 0;
+ playbackOps(*dl, [&count](const RecordedOp& op) {
+ if (count++ == 1) {
+ Matrix4 expectedMatrix;
+ EXPECT_EQ(RecordedOpId::RectOp, op.opId);
+
+ // ...and get about 58.6, 58.6, 341.4 341.4, because the bounds are clipped by
+ // the parent 200x200 viewport, but prior to rotation
+ EXPECT_RECT_APPROX_EQ(Rect(58.57864, 58.57864, 341.42136, 341.42136), op.localClipRect);
+ EXPECT_EQ(Rect(0, 0, 400, 400), op.unmappedBounds);
+ expectedMatrix.loadIdentity();
+ EXPECT_MATRIX_APPROX_EQ(expectedMatrix, op.localMatrix);
+ }
+ });
+ EXPECT_EQ(3, count);
+}
+
+} // namespace uirenderer
+} // namespace android
diff --git a/libs/hwui/unit_tests/TestUtils.h b/libs/hwui/unit_tests/TestUtils.h
index 80d83a2..99ecc9b 100644
--- a/libs/hwui/unit_tests/TestUtils.h
+++ b/libs/hwui/unit_tests/TestUtils.h
@@ -31,6 +31,12 @@
#define EXPECT_MATRIX_APPROX_EQ(a, b) \
EXPECT_TRUE(TestUtils::matricesAreApproxEqual(a, b))
+#define EXPECT_RECT_APPROX_EQ(a, b) \
+ EXPECT_TRUE(MathUtils::areEqual(a.left, b.left) \
+ && MathUtils::areEqual(a.top, b.top) \
+ && MathUtils::areEqual(a.right, b.right) \
+ && MathUtils::areEqual(a.bottom, b.bottom));
+
class TestUtils {
public:
static bool matricesAreApproxEqual(const Matrix4& a, const Matrix4& b) {
diff --git a/libs/input/PointerController.cpp b/libs/input/PointerController.cpp
index 0f86bc6..4a1d7e7 100644
--- a/libs/input/PointerController.cpp
+++ b/libs/input/PointerController.cpp
@@ -78,7 +78,7 @@
mLocked.pointerAlpha = 0.0f; // pointer is initially faded
mLocked.pointerSprite = mSpriteController->createSprite();
mLocked.pointerIconChanged = false;
- mLocked.requestedPointerShape = 0;
+ mLocked.requestedPointerShape = mPolicy->getDefaultPointerIconId();
mLocked.buttonState = 0;
@@ -512,7 +512,7 @@
if (mLocked.pointerIconChanged || mLocked.presentationChanged) {
if (mLocked.presentation == PRESENTATION_POINTER) {
- if (mLocked.requestedPointerShape == 0) {
+ if (mLocked.requestedPointerShape == mPolicy->getDefaultPointerIconId()) {
mLocked.pointerSprite->setIcon(mLocked.pointerIcon);
} else {
std::map<int, SpriteIcon>::const_iterator iter =
diff --git a/libs/input/PointerController.h b/libs/input/PointerController.h
index 308ff12..24a1681 100644
--- a/libs/input/PointerController.h
+++ b/libs/input/PointerController.h
@@ -58,7 +58,8 @@
public:
virtual void loadPointerResources(PointerResources* outResources) = 0;
- virtual void loadAdditionalMouseResources(std::map<int, SpriteIcon>* outResources) = 0;
+ virtual void loadAdditionalMouseResources(std::map<int32_t, SpriteIcon>* outResources) = 0;
+ virtual int32_t getDefaultPointerIconId() = 0;
};
diff --git a/packages/DocumentsUI/res/color/item_doc_list_background_activated.xml b/packages/DocumentsUI/res/color/item_doc_list_background_activated.xml
index 90e2b7e..7d7a110 100644
--- a/packages/DocumentsUI/res/color/item_doc_list_background_activated.xml
+++ b/packages/DocumentsUI/res/color/item_doc_list_background_activated.xml
@@ -16,10 +16,6 @@
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item
- android:state_focused="true"
- android:color="@color/platform_blue_a200"
- android:alpha="0.1" />
- <item
android:state_activated="true"
android:color="?android:attr/colorAccent"
android:alpha="0.1" />
diff --git a/packages/DocumentsUI/res/layout-sw720dp-land/item_doc_list.xml b/packages/DocumentsUI/res/layout-sw720dp-land/item_doc_list.xml
index d124320..231e110 100644
--- a/packages/DocumentsUI/res/layout-sw720dp-land/item_doc_list.xml
+++ b/packages/DocumentsUI/res/layout-sw720dp-land/item_doc_list.xml
@@ -14,10 +14,16 @@
limitations under the License.
-->
-<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+<com.android.documentsui.ListItem xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:background="@drawable/item_doc_list_background">
+ android:background="@drawable/item_doc_list_background"
+ android:orientation="horizontal">
+
+ <View
+ android:id="@+id/focus_indicator"
+ android:layout_width="4dp"
+ android:layout_height="match_parent" />
<LinearLayout
android:layout_width="match_parent"
@@ -121,4 +127,4 @@
</LinearLayout>
-</FrameLayout>
+</com.android.documentsui.ListItem>
diff --git a/packages/DocumentsUI/res/layout/item_doc_list.xml b/packages/DocumentsUI/res/layout/item_doc_list.xml
index c576669..085df35 100644
--- a/packages/DocumentsUI/res/layout/item_doc_list.xml
+++ b/packages/DocumentsUI/res/layout/item_doc_list.xml
@@ -14,10 +14,16 @@
limitations under the License.
-->
-<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+<com.android.documentsui.DocListItem xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:background="@drawable/item_doc_list_background">
+ android:background="@drawable/item_doc_list_background"
+ android:orientation="horizontal">
+
+ <View
+ android:id="@+id/focus_indicator"
+ android:layout_width="4dp"
+ android:layout_height="match_parent" />
<LinearLayout
android:layout_width="match_parent"
@@ -131,4 +137,4 @@
</LinearLayout>
-</FrameLayout>
+</com.android.documentsui.DocListItem>
diff --git a/packages/DocumentsUI/src/com/android/documentsui/DirectoryFragment.java b/packages/DocumentsUI/src/com/android/documentsui/DirectoryFragment.java
index 7662689..45a8907 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/DirectoryFragment.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/DirectoryFragment.java
@@ -68,6 +68,7 @@
import android.support.v7.widget.RecyclerView.OnItemTouchListener;
import android.support.v7.widget.RecyclerView.RecyclerListener;
import android.support.v7.widget.RecyclerView.ViewHolder;
+import android.support.v7.widget.SimpleItemAnimator;
import android.text.TextUtils;
import android.text.format.DateUtils;
import android.text.format.Formatter;
@@ -79,12 +80,12 @@
import android.view.ActionMode;
import android.view.DragEvent;
import android.view.GestureDetector;
+import android.view.KeyEvent;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuItem;
import android.view.MotionEvent;
import android.view.View;
-import android.view.View.OnLayoutChangeListener;
import android.view.ViewGroup;
import android.view.ViewParent;
import android.widget.ImageView;
@@ -97,10 +98,12 @@
import com.android.documentsui.model.DocumentInfo;
import com.android.documentsui.model.DocumentStack;
import com.android.documentsui.model.RootInfo;
+import com.android.internal.annotations.GuardedBy;
import com.google.common.collect.Lists;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.Collections;
import java.util.List;
@@ -134,6 +137,7 @@
private Model mModel;
private MultiSelectManager mSelectionManager;
private Model.UpdateListener mModelUpdateListener = new ModelUpdateListener();
+ private ItemClickListener mItemClickListener = new ItemClickListener();
private View mEmptyView;
private RecyclerView mRecView;
@@ -238,7 +242,7 @@
// TODO: Rather than update columns on layout changes, push this
// code (or something like it) into GridLayoutManager.
mRecView.addOnLayoutChangeListener(
- new OnLayoutChangeListener() {
+ new View.OnLayoutChangeListener() {
@Override
public void onLayoutChange(
@@ -251,6 +255,9 @@
}
});
+ // TODO: Restore transition animations. See b/24802917.
+ ((SimpleItemAnimator) mRecView.getItemAnimator()).setSupportsChangeAnimations(false);
+
// TODO: Add a divider between views (which might use RecyclerView.ItemDecoration).
if (DEBUG_ENABLE_DND) {
setupDragAndDropOnDirectoryView(mRecView);
@@ -450,7 +457,10 @@
}
private boolean onSingleTapUp(MotionEvent e) {
- if (Events.isTouchEvent(e) && mSelectionManager.getSelection().isEmpty()) {
+ // Only respond to touch events. Single-click mouse events are selection events and are
+ // handled by the selection manager. Tap events that occur while the selection manager is
+ // active are also selection events.
+ if (Events.isTouchEvent(e) && !mSelectionManager.hasSelection()) {
int position = getEventAdapterPosition(e);
if (position != RecyclerView.NO_POSITION) {
return handleViewItem(position);
@@ -825,7 +835,7 @@
Snackbars.makeSnackbar(activity, message, Snackbar.LENGTH_LONG)
.setAction(
R.string.undo,
- new android.view.View.OnClickListener() {
+ new View.OnClickListener() {
@Override
public void onClick(View view) {}
})
@@ -889,10 +899,16 @@
// Provide a reference to the views for each data item
// Complex data items may need more than one view per item, and
// you provide access to all the views for a data item in a view holder
- private static final class DocumentHolder extends RecyclerView.ViewHolder {
+ private static final class DocumentHolder
+ extends RecyclerView.ViewHolder
+ implements View.OnKeyListener
+ {
// each data item is just a string in this case
public View view;
public String docId; // The stable document id.
+ private ClickListener mClickListener;
+ private View.OnKeyListener mKeyListener;
+
public DocumentHolder(View view) {
super(view);
this.view = view;
@@ -900,6 +916,38 @@
// So we set it here. Note that touch mode focus is a separate issue - see
// View.setFocusableInTouchMode and View.isInTouchMode for more info.
this.view.setFocusable(true);
+ this.view.setOnKeyListener(this);
+ }
+
+ @Override
+ public boolean onKey(View v, int keyCode, KeyEvent event) {
+ // Intercept enter key-up events, and treat them as clicks. Forward other events.
+ if (event.getAction() == KeyEvent.ACTION_UP &&
+ keyCode == KeyEvent.KEYCODE_ENTER) {
+ if (mClickListener != null) {
+ mClickListener.onClick(this);
+ }
+ return true;
+ } else if (mKeyListener != null) {
+ return mKeyListener.onKey(v, keyCode, event);
+ }
+ return false;
+ }
+
+ public void addClickListener(ClickListener listener) {
+ // Just handle one for now; switch to a list if necessary.
+ checkState(mClickListener == null);
+ mClickListener = listener;
+ }
+
+ public void addOnKeyListener(View.OnKeyListener listener) {
+ // Just handle one for now; switch to a list if necessary.
+ checkState(mKeyListener == null);
+ mKeyListener = listener;
+ }
+
+ interface ClickListener {
+ public void onClick(DocumentHolder doc);
}
}
@@ -952,10 +1000,11 @@
default:
throw new IllegalStateException("Unsupported layout mode.");
}
- // Key event bubbling doesn't work properly, so instead of setting one key listener on
- // the RecyclerView, we have to set it on each Item. See b/24865023.
- item.setOnKeyListener(mSelectionManager);
- return new DocumentHolder(item);
+
+ DocumentHolder holder = new DocumentHolder(item);
+ holder.addClickListener(mItemClickListener);
+ holder.addOnKeyListener(mSelectionManager);
+ return holder;
}
@Override
@@ -1696,6 +1745,9 @@
private Context mContext;
private int mCursorCount;
private boolean mIsLoading;
+ @GuardedBy("mPendingDelete")
+ private Boolean mPendingDelete = false;
+ @GuardedBy("mPendingDelete")
private SparseBooleanArray mMarkedForDeletion = new SparseBooleanArray();
private UpdateListener mUpdateListener;
@Nullable private Cursor mCursor;
@@ -1740,35 +1792,39 @@
}
int getItemCount() {
- return mCursorCount - mMarkedForDeletion.size();
+ synchronized(mPendingDelete) {
+ return mCursorCount - mMarkedForDeletion.size();
+ }
}
Cursor getItem(int position) {
- // Items marked for deletion are masked out of the UI. To do this, for every marked
- // item whose position is less than the requested item position, advance the requested
- // position by 1.
- final int originalPos = position;
- final int size = mMarkedForDeletion.size();
- for (int i = 0; i < size; ++i) {
- // It'd be more concise, but less efficient, to iterate over positions while calling
- // mMarkedForDeletion.get. Instead, iterate over deleted entries.
- if (mMarkedForDeletion.keyAt(i) <= position && mMarkedForDeletion.valueAt(i)) {
- ++position;
+ synchronized(mPendingDelete) {
+ // Items marked for deletion are masked out of the UI. To do this, for every marked
+ // item whose position is less than the requested item position, advance the requested
+ // position by 1.
+ final int originalPos = position;
+ final int size = mMarkedForDeletion.size();
+ for (int i = 0; i < size; ++i) {
+ // It'd be more concise, but less efficient, to iterate over positions while calling
+ // mMarkedForDeletion.get. Instead, iterate over deleted entries.
+ if (mMarkedForDeletion.keyAt(i) <= position && mMarkedForDeletion.valueAt(i)) {
+ ++position;
+ }
}
- }
- if (DEBUG && position != originalPos) {
- Log.d(TAG, "Item position adjusted for deletion. Original: " + originalPos
- + " Adjusted: " + position);
- }
+ if (DEBUG && position != originalPos) {
+ Log.d(TAG, "Item position adjusted for deletion. Original: " + originalPos
+ + " Adjusted: " + position);
+ }
- if (position >= mCursorCount) {
- throw new IndexOutOfBoundsException("Attempt to retrieve " + position + " of " +
- mCursorCount + " items");
- }
+ if (position >= mCursorCount) {
+ throw new IndexOutOfBoundsException("Attempt to retrieve " + position + " of " +
+ mCursorCount + " items");
+ }
- mCursor.moveToPosition(position);
- return mCursor;
+ mCursor.moveToPosition(position);
+ return mCursor;
+ }
}
private boolean isEmpty() {
@@ -1801,17 +1857,19 @@
}
List<DocumentInfo> getDocumentsMarkedForDeletion() {
- final int size = mMarkedForDeletion.size();
- List<DocumentInfo> docs = new ArrayList<>(size);
+ synchronized (mPendingDelete) {
+ final int size = mMarkedForDeletion.size();
+ List<DocumentInfo> docs = new ArrayList<>(size);
- for (int i = 0; i < size; ++i) {
- final int position = mMarkedForDeletion.keyAt(i);
- checkState(position < mCursorCount);
- mCursor.moveToPosition(position);
- final DocumentInfo doc = DocumentInfo.fromDirectoryCursor(mCursor);
- docs.add(doc);
+ for (int i = 0; i < size; ++i) {
+ final int position = mMarkedForDeletion.keyAt(i);
+ checkState(position < mCursorCount);
+ mCursor.moveToPosition(position);
+ final DocumentInfo doc = DocumentInfo.fromDirectoryCursor(mCursor);
+ docs.add(doc);
+ }
+ return docs;
}
- return docs;
}
/**
@@ -1822,17 +1880,23 @@
* @param selected A selection representing the files to delete.
*/
void markForDeletion(Selection selected) {
- // Only one deletion operation at a time.
- checkState(mMarkedForDeletion.size() == 0);
- // There should never be more to delete than what exists.
- checkState(mCursorCount >= selected.size());
+ synchronized (mPendingDelete) {
+ mPendingDelete = true;
+ // Only one deletion operation at a time.
+ checkState(mMarkedForDeletion.size() == 0);
+ // There should never be more to delete than what exists.
+ checkState(mCursorCount >= selected.size());
- final int size = selected.size();
- for (int i = 0; i < size; ++i) {
- int position = selected.get(i);
- if (DEBUG) Log.d(TAG, "Marked position " + position + " for deletion");
- mMarkedForDeletion.append(position, true);
- mViewAdapter.notifyItemRemoved(position);
+ int[] positions = selected.getAll();
+ Arrays.sort(positions);
+
+ // Walk backwards through the set, since we're removing positions.
+ // Otherwise, positions would change after the first modification.
+ for (int p = positions.length - 1; p >= 0; p--) {
+ mMarkedForDeletion.append(positions[p], true);
+ mViewAdapter.notifyItemRemoved(positions[p]);
+ if (DEBUG) Log.d(TAG, "Scheduled " + positions[p] + " for delete.");
+ }
}
}
@@ -1841,17 +1905,24 @@
* unmarked, and restored in the UI. See {@link #markForDeletion(Selection)}.
*/
void undoDeletion() {
- // Iterate over deleted items, temporarily marking them false in the deletion list, and
- // re-adding them to the UI.
- final int size = mMarkedForDeletion.size();
- for (int i = 0; i < size; ++i) {
- final int position = mMarkedForDeletion.keyAt(i);
- mMarkedForDeletion.put(position, false);
- mViewAdapter.notifyItemInserted(position);
+ synchronized (mPendingDelete) {
+ // Iterate over deleted items, temporarily marking them false in the deletion list, and
+ // re-adding them to the UI.
+ final int size = mMarkedForDeletion.size();
+ for (int i = 0; i < size; ++i) {
+ final int position = mMarkedForDeletion.keyAt(i);
+ mMarkedForDeletion.put(position, false);
+ mViewAdapter.notifyItemInserted(position);
+ }
+ resetDeleteData();
}
+ }
- // Then, clear the deletion list.
- mMarkedForDeletion.clear();
+ private void resetDeleteData() {
+ synchronized (mPendingDelete) {
+ mPendingDelete = false;
+ mMarkedForDeletion.clear();
+ }
}
/**
@@ -1862,9 +1933,16 @@
* snackbars) for errors, info, etc.
*/
void finalizeDeletion(DeletionListener listener) {
- final ContentResolver resolver = mContext.getContentResolver();
- DeleteFilesTask task = new DeleteFilesTask(resolver, listener);
- task.execute();
+ synchronized (mPendingDelete) {
+ if (mPendingDelete) {
+ // Necessary to avoid b/25072545. Even when that's resolved, this
+ // is a nice safe thing to day.
+ mPendingDelete = false;
+ final ContentResolver resolver = mContext.getContentResolver();
+ DeleteFilesTask task = new DeleteFilesTask(resolver, listener);
+ task.execute();
+ }
+ }
}
/**
@@ -1921,7 +1999,7 @@
} else {
if (DEBUG) Log.d(TAG, "Deletion task completed successfully.");
}
- mMarkedForDeletion.clear();
+ resetDeleteData();
mListener.onCompletion();
}
@@ -1957,6 +2035,18 @@
}
}
+ private class ItemClickListener implements DocumentHolder.ClickListener {
+ @Override
+ public void onClick(DocumentHolder doc) {
+ final int position = doc.getAdapterPosition();
+ if (mSelectionManager.hasSelection()) {
+ mSelectionManager.toggleSelection(position);
+ } else {
+ handleViewItem(position);
+ }
+ }
+ }
+
private class ModelUpdateListener extends Model.UpdateListener {
@Override
public void onModelUpdate(Model model) {
diff --git a/packages/DocumentsUI/src/com/android/documentsui/ListItem.java b/packages/DocumentsUI/src/com/android/documentsui/ListItem.java
new file mode 100644
index 0000000..5c40f1b
--- /dev/null
+++ b/packages/DocumentsUI/src/com/android/documentsui/ListItem.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.documentsui;
+
+import android.content.Context;
+import android.graphics.Rect;
+import android.util.AttributeSet;
+import android.util.TypedValue;
+import android.view.View;
+import android.widget.LinearLayout;
+
+/**
+ * Layout for a single item in List mode. This class overrides the default focus listener in order
+ * to light up a focus indicator when it is focused.
+ */
+public class ListItem extends LinearLayout
+{
+ public ListItem(Context context) {
+ super(context);
+ }
+
+ public ListItem(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ }
+
+ @Override
+ protected void onFocusChanged(boolean gainFocus, int direction, Rect previouslyFocusedRect) {
+ View indicator = findViewById(R.id.focus_indicator);
+ if (gainFocus) {
+ TypedValue color = new TypedValue();
+ getContext().getTheme().resolveAttribute(android.R.attr.colorAccent, color, true);
+ indicator.setBackgroundColor(color.data);
+ } else {
+ indicator.setBackgroundColor(android.R.color.transparent);
+ }
+ super.onFocusChanged(gainFocus, direction, previouslyFocusedRect);
+ }
+}
diff --git a/packages/DocumentsUI/src/com/android/documentsui/MultiSelectManager.java b/packages/DocumentsUI/src/com/android/documentsui/MultiSelectManager.java
index 87c037d..ef53d53 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/MultiSelectManager.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/MultiSelectManager.java
@@ -183,6 +183,10 @@
mCallbacks.add(callback);
}
+ public boolean hasSelection() {
+ return !mSelection.isEmpty();
+ }
+
/**
* Returns a Selection object that provides a live view
* on the current selection.
@@ -217,7 +221,7 @@
*/
@VisibleForTesting
public boolean setItemSelected(int position, boolean selected) {
- if (mSingleSelect && !mSelection.isEmpty()) {
+ if (mSingleSelect && hasSelection()) {
clearSelectionQuietly();
}
return setItemsSelected(position, 1, selected);
@@ -263,7 +267,7 @@
private void clearSelectionQuietly() {
mRanger = null;
- if (mSelection.isEmpty()) {
+ if (!hasSelection()) {
return;
}
if (mIntermediateSelection == null) {
@@ -292,7 +296,7 @@
@VisibleForTesting
boolean onSingleTapUp(InputEvent input) {
if (DEBUG) Log.d(TAG, "Processing tap event.");
- if (mSelection.isEmpty()) {
+ if (!hasSelection()) {
// if this is a mouse click on an item, start selection mode.
// TODO: && input.isPrimaryButtonPressed(), but it is returning false.
if (input.isOverItem() && input.isMouseEvent()) {
@@ -335,7 +339,7 @@
*
* @param position
*/
- private void toggleSelection(int position) {
+ void toggleSelection(int position) {
// Position may be special "no position" during certain
// transitional phases. If so, skip handling of the event.
if (position == RecyclerView.NO_POSITION) {
@@ -351,7 +355,7 @@
if (!canSelect) {
return;
}
- if (mSingleSelect && !mSelection.isEmpty()) {
+ if (mSingleSelect && hasSelection()) {
clearSelectionQuietly();
}
@@ -398,7 +402,7 @@
if (selected) {
boolean canSelect = notifyBeforeItemStateChange(i, true);
if (canSelect) {
- if (mSingleSelect && !mSelection.isEmpty()) {
+ if (mSingleSelect && hasSelection()) {
clearSelectionQuietly();
}
selectAndNotify(i);
@@ -597,6 +601,14 @@
mTotalSelection = new SparseBooleanArray();
}
+ @VisibleForTesting
+ public Selection(int... positions) {
+ this();
+ for (int i = 0; i < positions.length; i++) {
+ add(positions[i]);
+ }
+ }
+
/**
* @param position
* @return true if the position is currently selected.
@@ -620,6 +632,18 @@
}
/**
+ * Returns an unordered array of selected positions.
+ */
+ public int[] getAll() {
+ final int size = size();
+ int[] positions = new int[size];
+ for (int i = 0; i < size; i++) {
+ positions[i] = get(i);
+ }
+ return positions;
+ }
+
+ /**
* @return size of the selection.
*/
public int size() {
@@ -1959,7 +1983,11 @@
}
if (searchDir != -1) {
View targetView = view.focusSearch(searchDir);
- target = mEnvironment.getAdapterPositionForChildView(targetView);
+ // TargetView can be null, for example, if the user pressed <down> at the bottom of
+ // the list.
+ if (targetView != null) {
+ target = mEnvironment.getAdapterPositionForChildView(targetView);
+ }
}
}
@@ -1972,18 +2000,13 @@
mEnvironment.focusItem(target);
if (event.isShiftPressed()) {
- if (mSelection.isEmpty()) {
+ if (!hasSelection()) {
// If there is no selection, start a selection when the user presses shift-arrow.
toggleSelection(mEnvironment.getAdapterPositionForChildView(view));
- } else {
- // Deal with b/24802917 (selected items can't be focused) by adjusting the
- // selection sorted the focused item isn't in the selection.
- target -= Integer.signum(target - mRanger.mBegin);
- mRanger.snapSelection(target);
}
- } else if (!event.isShiftPressed() && !mSelection.isEmpty()) {
- // If there is a selection, clear it if the user presses arrow with no shift.
- clearSelection();
+
+ mRanger.snapSelection(target);
+ notifySelectionChanged();
}
return true;
diff --git a/packages/DocumentsUI/tests/src/com/android/documentsui/DirectoryFragmentModelTest.java b/packages/DocumentsUI/tests/src/com/android/documentsui/DirectoryFragmentModelTest.java
index ace9e27..36d880a 100644
--- a/packages/DocumentsUI/tests/src/com/android/documentsui/DirectoryFragmentModelTest.java
+++ b/packages/DocumentsUI/tests/src/com/android/documentsui/DirectoryFragmentModelTest.java
@@ -32,10 +32,6 @@
import com.android.documentsui.model.DocumentInfo;
import java.util.List;
-import java.util.concurrent.CountDownLatch;
-import java.util.concurrent.TimeUnit;
-
-
public class DirectoryFragmentModelTest extends AndroidTestCase {
@@ -117,15 +113,6 @@
assertEquals("0", docs.get(0).documentId);
assertEquals("1", docs.get(1).documentId);
assertEquals("4", docs.get(2).documentId);
-
- TestDeletionListener testListener = new TestDeletionListener();
- model.finalizeDeletion(testListener);
- testListener.waitForDone();
-
- docs = getDocumentInfo(0, 1, 2);
- assertEquals("0", docs.get(0).documentId);
- assertEquals("1", docs.get(1).documentId);
- assertEquals("2", docs.get(2).documentId);
}
// Tests that Model.getItem returns the right items after a deletion is undone.
@@ -149,20 +136,12 @@
};
}
- private void delete(int... items) {
- Selection sel = new Selection();
- for (int item: items) {
- sel.add(item);
- }
- model.markForDeletion(sel);
+ private void delete(int... positions) {
+ model.markForDeletion(new Selection(positions));
}
- private List<DocumentInfo> getDocumentInfo(int... items) {
- Selection sel = new Selection();
- for (int item: items) {
- sel.add(item);
- }
- return model.getDocuments(sel);
+ private List<DocumentInfo> getDocumentInfo(int... positions) {
+ return model.getDocuments(new Selection(positions));
}
private static class DummyListener extends Model.UpdateListener {
@@ -177,20 +156,4 @@
return null;
}
}
-
- private static class TestDeletionListener extends Model.DeletionListener {
- final CountDownLatch mSignal = new CountDownLatch(1);
-
- @Override
- public void onCompletion() {
- mSignal.countDown();
- }
-
- public void waitForDone() {
- try {
- boolean timeout = mSignal.await(10, TimeUnit.SECONDS);
- assertTrue("Timed out waiting for deletion completion", timeout);
- } catch (InterruptedException e) {}
- }
- }
}
diff --git a/packages/MtpDocumentsProvider/src/com/android/mtp/CursorHelper.java b/packages/MtpDocumentsProvider/src/com/android/mtp/CursorHelper.java
index cc510a9..9e874bd 100644
--- a/packages/MtpDocumentsProvider/src/com/android/mtp/CursorHelper.java
+++ b/packages/MtpDocumentsProvider/src/com/android/mtp/CursorHelper.java
@@ -18,6 +18,7 @@
import android.content.res.Resources;
import android.database.MatrixCursor;
+import android.media.MediaFile;
import android.mtp.MtpConstants;
import android.mtp.MtpObjectInfo;
import android.provider.DocumentsContract;
@@ -70,30 +71,18 @@
}
static String formatTypeToMimeType(int format) {
- // TODO: Add complete list of mime types.
- switch (format) {
- case MtpConstants.FORMAT_ASSOCIATION:
- return DocumentsContract.Document.MIME_TYPE_DIR;
- case MtpConstants.FORMAT_MP3:
- return "audio/mp3";
- case MtpConstants.FORMAT_EXIF_JPEG:
- return "image/jpeg";
- default:
- return "application/octet-stream";
+ if (format == MtpConstants.FORMAT_ASSOCIATION) {
+ return DocumentsContract.Document.MIME_TYPE_DIR;
+ } else {
+ return MediaFile.getMimeTypeForFormatCode(format);
}
}
- static int mimeTypeToFormatType(String mimeType) {
- // TODO: Add complete list of mime types.
- switch (mimeType.toLowerCase()) {
- case Document.MIME_TYPE_DIR:
- return MtpConstants.FORMAT_ASSOCIATION;
- case "audio/mp3":
- return MtpConstants.FORMAT_MP3;
- case "image/jpeg":
- return MtpConstants.FORMAT_EXIF_JPEG;
- default:
- return MtpConstants.FORMAT_UNDEFINED;
+ static int mimeTypeToFormatType(String fileName, String mimeType) {
+ if (Document.MIME_TYPE_DIR.equals(mimeType)) {
+ return MtpConstants.FORMAT_ASSOCIATION;
+ } else {
+ return MediaFile.getFormatCode(fileName, mimeType);
}
}
}
diff --git a/packages/MtpDocumentsProvider/src/com/android/mtp/MtpDocumentsProvider.java b/packages/MtpDocumentsProvider/src/com/android/mtp/MtpDocumentsProvider.java
index 9d5850b..7883e61 100644
--- a/packages/MtpDocumentsProvider/src/com/android/mtp/MtpDocumentsProvider.java
+++ b/packages/MtpDocumentsProvider/src/com/android/mtp/MtpDocumentsProvider.java
@@ -242,7 +242,7 @@
new MtpObjectInfo.Builder()
.setStorageId(parentId.mStorageId)
.setParent(parentId.mObjectHandle)
- .setFormat(CursorHelper.mimeTypeToFormatType(mimeType))
+ .setFormat(CursorHelper.mimeTypeToFormatType(displayName, mimeType))
.setName(displayName)
.build(), pipe[1]);
final String documentId = new Identifier(parentId.mDeviceId, parentId.mStorageId,
diff --git a/packages/SystemUI/Android.mk b/packages/SystemUI/Android.mk
index 314b3c4..0cc2a67 100644
--- a/packages/SystemUI/Android.mk
+++ b/packages/SystemUI/Android.mk
@@ -3,7 +3,7 @@
LOCAL_MODULE_TAGS := optional
-LOCAL_SRC_FILES := $(call all-java-files-under, src) $(call all-proto-files-under,src) \
+LOCAL_SRC_FILES := $(call all-java-files-under, src) $(call all-proto-files-under,src) $(call all-Iaidl-files-under, src) \
src/com/android/systemui/EventLogTags.logtags
LOCAL_STATIC_JAVA_LIBRARIES := Keyguard
diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml
index bbb099e..5d622a0 100644
--- a/packages/SystemUI/AndroidManifest.xml
+++ b/packages/SystemUI/AndroidManifest.xml
@@ -199,6 +199,11 @@
android:value="com.android.settings.category.system" />
</activity>
+ <!-- Service used by secondary users to register themselves with the system user. -->
+ <service android:name=".recents.RecentsSystemUserService"
+ android:exported="false"
+ android:permission="com.android.systemui.permission.SELF" />
+
<!-- Alternate Recents -->
<activity android:name=".recents.RecentsActivity"
android:label="@string/accessibility_desc_recent_apps"
@@ -215,17 +220,6 @@
</intent-filter>
</activity>
- <receiver android:name=".recents.RecentsUserEventProxyReceiver"
- android:exported="false">
- <intent-filter>
- <action android:name="com.android.systemui.recents.action.SHOW_RECENTS_FOR_USER" />
- <action android:name="com.android.systemui.recents.action.HIDE_RECENTS_FOR_USER" />
- <action android:name="com.android.systemui.recents.action.TOGGLE_RECENTS_FOR_USER" />
- <action android:name="com.android.systemui.recents.action.PRELOAD_RECENTS_FOR_USER" />
- <action android:name="com.android.systemui.recents.action.CONFIG_CHANGED_FOR_USER" />
- </intent-filter>
- </receiver>
-
<!-- Callback for dismissing screenshot notification after a share target is picked -->
<receiver android:name=".screenshot.GlobalScreenshot$TargetChosenReceiver"
android:process=":screenshot"
diff --git a/packages/SystemUI/res/layout/qs_customize_panel.xml b/packages/SystemUI/res/layout/qs_customize_panel.xml
index 59fed5b..f430fa5 100644
--- a/packages/SystemUI/res/layout/qs_customize_panel.xml
+++ b/packages/SystemUI/res/layout/qs_customize_panel.xml
@@ -19,14 +19,13 @@
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
- android:paddingBottom="@dimen/navigation_bar_size"
android:background="@drawable/qs_customizer_background"
android:gravity="center_horizontal">
<FrameLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:background="?android:attr/colorPrimary">
+ android:background="@drawable/notification_header_bg">
<LinearLayout
android:id="@+id/drag_buttons"
@@ -72,8 +71,8 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:navigationContentDescription="@*android:string/action_bar_up_description"
- style="?android:attr/toolbarStyle"
- android:background="?android:attr/colorPrimary" />
+ android:background="@drawable/notification_header_bg"
+ style="?android:attr/toolbarStyle" />
</FrameLayout>
<com.android.systemui.tuner.AutoScrollView
@@ -105,4 +104,10 @@
android:elevation="@dimen/fab_elevation"
android:background="@drawable/fab_background" />
+ <View
+ android:layout_width="match_parent"
+ android:layout_height="@dimen/navigation_bar_size"
+ android:layout_gravity="bottom"
+ android:background="#ff000000" />
+
</com.android.systemui.qs.customize.QSCustomizer>
diff --git a/packages/SystemUI/res/layout/quick_status_bar_expanded_header.xml b/packages/SystemUI/res/layout/quick_status_bar_expanded_header.xml
index 0b8da83..6187070 100644
--- a/packages/SystemUI/res/layout/quick_status_bar_expanded_header.xml
+++ b/packages/SystemUI/res/layout/quick_status_bar_expanded_header.xml
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
-** Copyright 2015, The Android Open Source Project
+** Copyright 2012, The Android Open Source Project
**
** Licensed under the Apache License, Version 2.0 (the "License");
** you may not use this file except in compliance with the License.
@@ -32,55 +32,21 @@
android:focusable="true"
>
+ <com.android.systemui.qs.QuickQSPanel
+ android:id="@+id/quick_qs_panel"
+ android:background="#0000"
+ android:layout_width="142dp"
+ android:layout_height="match_parent"
+ android:layout_alignParentEnd="true" />
+
<LinearLayout
android:id="@+id/expanded_group"
- android:layout_width="match_parent"
+ android:layout_width="wrap_content"
android:layout_height="match_parent"
- android:paddingEnd="12dp"
- android:orientation="horizontal">
-
- <com.android.systemui.statusbar.AlphaOptimizedButton android:id="@+id/alarm_status"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_marginBottom="4dp"
- android:drawablePadding="6dp"
- android:drawableStart="@drawable/ic_access_alarms_small"
- android:textColor="#64ffffff"
- android:textAppearance="@style/TextAppearance.StatusBar.Expanded.Date"
- android:paddingEnd="6dp"
- android:paddingStart="6dp"
- android:paddingTop="16dp"
- android:paddingBottom="16dp"
- android:background="?android:attr/selectableItemBackground"
- android:visibility="gone"
- />
-
- <com.android.systemui.statusbar.policy.DateView android:id="@+id/date"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_marginStart="16dp"
- android:singleLine="true"
- android:textAppearance="@style/TextAppearance.StatusBar.Expanded.Date"
- android:layout_below="@id/clock"
- systemui:datePattern="EEE"
- android:gravity="center_vertical"
- android:textSize="20sp"
- android:paddingTop="16dp"
- android:layout_marginBottom="@dimen/clock_collapsed_bottom_margin" />
-
- <include layout="@layout/split_clock_view"
- android:layout_width="wrap_content"
- android:layout_height="match_parent"
- android:layout_marginStart="16dp"
- android:layout_marginTop="16dp"
- android:id="@+id/clock"
- />
-
- <Space
- android:layout_width="0dp"
- android:layout_height="match_parent"
- android:layout_weight="1" />
-
+ android:clipChildren="false"
+ android:clipToPadding="false"
+ android:orientation="horizontal"
+ android:layout_alignParentEnd="true">
<com.android.systemui.statusbar.AlphaOptimizedFrameLayout
android:id="@+id/settings_button_container"
android:layout_width="48dp"
@@ -105,8 +71,57 @@
android:src="@drawable/tuner" />
</com.android.systemui.statusbar.AlphaOptimizedFrameLayout>
+
+ <ImageView
+ android:layout_width="48dp"
+ android:layout_height="match_parent"
+ android:src="@drawable/ic_expand_less"
+ android:tint="@android:color/white" />
</LinearLayout>
+ <FrameLayout
+ android:id="@+id/date_group"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginBottom="@dimen/clock_collapsed_bottom_margin"
+ android:layout_alignParentBottom="true">
+ <com.android.systemui.statusbar.policy.DateView android:id="@+id/date_collapsed"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginStart="16dp"
+ android:singleLine="true"
+ android:textAppearance="@style/TextAppearance.StatusBar.Expanded.Date"
+ android:layout_below="@id/clock"
+ systemui:datePattern="@string/abbrev_wday_month_day_no_year_alarm"
+ />
+ </FrameLayout>
+
+ <include layout="@layout/split_clock_view"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginStart="16dp"
+ android:layout_above="@id/date_group"
+ android:id="@+id/clock"
+ />
+
+ <com.android.systemui.statusbar.AlphaOptimizedButton android:id="@+id/alarm_status"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_alignParentBottom="true"
+ android:layout_toEndOf="@id/date_group"
+ android:layout_marginBottom="4dp"
+ android:drawablePadding="6dp"
+ android:drawableStart="@drawable/ic_access_alarms_small"
+ android:textColor="#64ffffff"
+ android:textAppearance="@style/TextAppearance.StatusBar.Expanded.Date"
+ android:paddingEnd="6dp"
+ android:paddingStart="6dp"
+ android:paddingTop="16dp"
+ android:paddingBottom="16dp"
+ android:background="?android:attr/selectableItemBackground"
+ android:visibility="gone"
+ />
+
<include
android:id="@+id/qs_detail_header"
layout="@layout/qs_detail_header"
@@ -115,12 +130,6 @@
android:layout_alignParentBottom="true"
/>
- <com.android.systemui.qs.QuickQSPanel
- android:id="@+id/quick_qs_panel"
- android:background="#0000"
- android:layout_width="match_parent"
- android:layout_height="match_parent" />
-
<com.android.systemui.statusbar.AlphaOptimizedImageView
android:id="@+id/qs_detail_header_progress"
android:src="@drawable/indeterminate_anim"
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index 5661657..cbc92f2 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -125,6 +125,7 @@
<dimen name="pull_span_min">25dp</dimen>
<dimen name="qs_tile_height">88dp</dimen>
+ <dimen name="qs_new_tile_height">100dp</dimen>
<dimen name="qs_quick_actions_height">88dp</dimen>
<dimen name="qs_quick_actions_padding">25dp</dimen>
<dimen name="qs_page_indicator_size">12dp</dimen>
diff --git a/packages/SystemUI/src/com/android/systemui/keyboard/KeyboardUI.java b/packages/SystemUI/src/com/android/systemui/keyboard/KeyboardUI.java
index a43d520..96ee397 100644
--- a/packages/SystemUI/src/com/android/systemui/keyboard/KeyboardUI.java
+++ b/packages/SystemUI/src/com/android/systemui/keyboard/KeyboardUI.java
@@ -23,6 +23,7 @@
import android.bluetooth.le.BluetoothLeScanner;
import android.bluetooth.le.ScanCallback;
import android.bluetooth.le.ScanFilter;
+import android.bluetooth.le.ScanRecord;
import android.bluetooth.le.ScanResult;
import android.bluetooth.le.ScanSettings;
import android.content.ContentResolver;
@@ -230,17 +231,19 @@
}
CachedBluetoothDevice device = getPairedKeyboard();
- if ((mState == STATE_WAITING_FOR_TABLET_MODE_EXIT || mState == STATE_WAITING_FOR_BLUETOOTH)
- && device != null) {
- // If we're just coming out of tablet mode or BT just turned on,
- // then we want to go ahead and automatically connect to the
- // keyboard. We want to avoid this in other cases because we might
- // be spuriously called after the user has manually disconnected
- // the keyboard, meaning we shouldn't try to automtically connect
- // it again.
- mState = STATE_PAIRED;
- device.connect(false);
- return;
+ if (mState == STATE_WAITING_FOR_TABLET_MODE_EXIT || mState == STATE_WAITING_FOR_BLUETOOTH) {
+ if (device != null) {
+ // If we're just coming out of tablet mode or BT just turned on,
+ // then we want to go ahead and automatically connect to the
+ // keyboard. We want to avoid this in other cases because we might
+ // be spuriously called after the user has manually disconnected
+ // the keyboard, meaning we shouldn't try to automtically connect
+ // it again.
+ mState = STATE_PAIRED;
+ device.connect(false);
+ return;
+ }
+ mCachedDeviceManager.clearNonBondedDevices();
}
device = getDiscoveredKeyboard();
@@ -459,21 +462,36 @@
}
private final class KeyboardScanCallback extends ScanCallback {
+
+ private boolean isDeviceDiscoverable(ScanResult result) {
+ final ScanRecord scanRecord = result.getScanRecord();
+ final int flags = scanRecord.getAdvertiseFlags();
+ final int BT_DISCOVERABLE_MASK = 0x03;
+
+ return (flags & BT_DISCOVERABLE_MASK) != 0;
+ }
+
@Override
public void onBatchScanResults(List<ScanResult> results) {
if (DEBUG) {
Slog.d(TAG, "onBatchScanResults(" + results.size() + ")");
}
- if (!results.isEmpty()) {
- BluetoothDevice bestDevice = results.get(0).getDevice();
- int bestRssi = results.get(0).getRssi();
- final int N = results.size();
- for (int i = 0; i < N; i++) {
- ScanResult r = results.get(i);
- if (r.getRssi() > bestRssi) {
- bestDevice = r.getDevice();
- }
+
+ BluetoothDevice bestDevice = null;
+ int bestRssi = Integer.MIN_VALUE;
+
+ for (ScanResult result : results) {
+ if (DEBUG) {
+ Slog.d(TAG, "onBatchScanResults: considering " + result);
}
+
+ if (isDeviceDiscoverable(result) && result.getRssi() > bestRssi) {
+ bestDevice = result.getDevice();
+ bestRssi = result.getRssi();
+ }
+ }
+
+ if (bestDevice != null) {
mHandler.obtainMessage(MSG_ON_BLUETOOTH_DEVICE_ADDED, bestDevice).sendToTarget();
}
}
@@ -491,8 +509,14 @@
if (DEBUG) {
Slog.d(TAG, "onScanResult(" + callbackType + ", " + result + ")");
}
- mHandler.obtainMessage(MSG_ON_BLUETOOTH_DEVICE_ADDED,
- result.getDevice()).sendToTarget();
+
+ if (isDeviceDiscoverable(result)) {
+ mHandler.obtainMessage(MSG_ON_BLUETOOTH_DEVICE_ADDED,
+ result.getDevice()).sendToTarget();
+ } else if (DEBUG) {
+ Slog.d(TAG, "onScanResult: device " + result.getDevice() +
+ " is not discoverable, ignoring");
+ }
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/PagedTileLayout.java b/packages/SystemUI/src/com/android/systemui/qs/PagedTileLayout.java
index 32c906e..0e4a4e5 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/PagedTileLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/PagedTileLayout.java
@@ -75,8 +75,6 @@
@Override
public void setTileVisibility(TileRecord tile, int visibility) {
tile.tileView.setVisibility(visibility);
-// // TODO: Do something smarter here.
-// distributeTiles();
}
@Override
@@ -104,8 +102,7 @@
for (int i = 0; i < NT; i++) {
TileRecord tile = mTiles.get(i);
if (tile.tile.getTileType() == QSTileView.QS_TYPE_QUICK) {
- tile.tileView.setType(QSTileView.QS_TYPE_QUICK);
- mFirstPage.mQuickQuickTiles.addView(tile.tileView);
+ // Don't show any quick tiles for now.
continue;
}
if (mPages.get(index).isFull()) {
@@ -161,6 +158,7 @@
protected void onFinishInflate() {
super.onFinishInflate();
mQuickQuickTiles = (LinearLayout) findViewById(R.id.quick_tile_layout);
+ mQuickQuickTiles.setVisibility(View.GONE);
mTilePage = (TilePage) findViewById(R.id.tile_page);
// Less rows on first page, because it needs room for the quick tiles.
mTilePage.mMaxRows = 3;
@@ -176,7 +174,7 @@
}
public static class TilePage extends TileLayout {
- private int mMaxRows = 4;
+ private int mMaxRows = 3;
public TilePage(Context context, AttributeSet attrs) {
super(context, attrs);
@@ -188,6 +186,11 @@
mMaxRows = maxRows;
}
+ @Override
+ protected int getCellHeight() {
+ return mContext.getResources().getDimensionPixelSize(R.dimen.qs_new_tile_height);
+ }
+
private void clear() {
if (DEBUG) Log.d(TAG, "Clearing page");
removeAllViews();
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanel.java b/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanel.java
index 53d0402..a2d9ef0 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanel.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanel.java
@@ -60,7 +60,7 @@
if (tile.getTileType() == QSTileView.QS_TYPE_QUICK) {
quickTiles.add(tile);
}
- if (quickTiles.size() == 4) {
+ if (quickTiles.size() == 2) {
break;
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/TileLayout.java b/packages/SystemUI/src/com/android/systemui/qs/TileLayout.java
index b8342e2..12a099d 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/TileLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/TileLayout.java
@@ -67,7 +67,7 @@
public void updateResources() {
final Resources res = mContext.getResources();
final int columns = Math.max(1, res.getInteger(R.integer.quick_settings_num_columns));
- mCellHeight = res.getDimensionPixelSize(R.dimen.qs_tile_height);
+ mCellHeight = getCellHeight();
mCellWidth = (int) (mCellHeight * TILE_ASPECT);
mLargeCellHeight = mAllowDual ? res.getDimensionPixelSize(R.dimen.qs_dual_tile_height)
: mCellHeight;
@@ -79,6 +79,10 @@
}
}
+ protected int getCellHeight() {
+ return mContext.getResources().getDimensionPixelSize(R.dimen.qs_tile_height);
+ }
+
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
final int width = MeasureSpec.getSize(widthMeasureSpec);
diff --git a/packages/SystemUI/src/com/android/systemui/qs/customize/NonPagedTileLayout.java b/packages/SystemUI/src/com/android/systemui/qs/customize/NonPagedTileLayout.java
index 012633c..1669278 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/customize/NonPagedTileLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/customize/NonPagedTileLayout.java
@@ -61,6 +61,7 @@
protected void onFinishInflate() {
super.onFinishInflate();
mQuickTiles = (QuickTileLayout) findViewById(R.id.quick_tile_layout);
+ mQuickTiles.setVisibility(View.GONE);
TilePage page = (PagedTileLayout.TilePage) findViewById(R.id.tile_page);
page.setMaxRows(3 /* First page only gets 3 */);
mPages.add(page);
@@ -107,12 +108,12 @@
for (int i = 0; i < NT; i++) {
TileRecord tile = mTiles.get(i);
if (tile.tile.getTileType() == QSTileView.QS_TYPE_QUICK) {
- tile.tileView.setType(QSTileView.QS_TYPE_QUICK);
- mQuickTiles.addView(tile.tileView);
+ // Ignore quick tiles for now.
continue;
}
mPages.get(index).addTile(tile);
- if (mPages.get(index).isFull()) {
+ // Keep everything in one layout for now.
+ if (false && mPages.get(index).isFull()) {
if (++index == mPages.size()) {
LayoutInflater inflater = LayoutInflater.from(mContext);
inflater.inflate(R.layout.horizontal_divider, this);
diff --git a/packages/SystemUI/src/com/android/systemui/qs/customize/QSCustomizer.java b/packages/SystemUI/src/com/android/systemui/qs/customize/QSCustomizer.java
index 601961b..fe8d78b 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/customize/QSCustomizer.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/customize/QSCustomizer.java
@@ -84,7 +84,7 @@
TypedValue value = new TypedValue();
mContext.getTheme().resolveAttribute(android.R.attr.homeAsUpIndicator, value, true);
mToolbar.setNavigationIcon(
- getResources().getDrawable(value.resourceId, mContext.getTheme()));
+ getResources().getDrawable(R.drawable.ic_close_white, mContext.getTheme()));
mToolbar.setNavigationOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
@@ -193,7 +193,8 @@
@Override
public void onClick(View v) {
if (mFab == v) {
- // TODO: Show list of tiles.
+ SystemUIDialog dialog = new SystemUIDialog(mContext);
+ dialog.show();
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/IRecentsNonSystemUserCallbacks.aidl b/packages/SystemUI/src/com/android/systemui/recents/IRecentsNonSystemUserCallbacks.aidl
new file mode 100644
index 0000000..79eca30d
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/recents/IRecentsNonSystemUserCallbacks.aidl
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.recents;
+
+/**
+ * Due to the fact that RecentsActivity is per-user, we need to establish an
+ * interface (this) for the system user to callback to the secondary users in
+ * response to UI events coming in from the system user's SystemUI.
+ */
+oneway interface IRecentsNonSystemUserCallbacks {
+ void preloadRecents();
+ void cancelPreloadingRecents();
+ void showRecents(boolean triggeredFromAltTab);
+ void hideRecents(boolean triggeredFromAltTab, boolean triggeredFromHomeKey);
+ void toggleRecents();
+ void onConfigurationChanged();
+}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/IRecentsSystemUserCallbacks.aidl b/packages/SystemUI/src/com/android/systemui/recents/IRecentsSystemUserCallbacks.aidl
new file mode 100644
index 0000000..6b49195
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/recents/IRecentsSystemUserCallbacks.aidl
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.recents;
+
+/**
+ * Due to the fact that RecentsActivity is per-user, we need to establish an
+ * interface (this) for the non-system user to register itself for callbacks and to
+ * callback to the system user to update internal state.
+ */
+oneway interface IRecentsSystemUserCallbacks {
+ void registerNonSystemUserCallbacks(IBinder nonSystemUserCallbacks, int userId);
+
+ void updateRecentsVisibility(boolean visible);
+ void startScreenPinning();
+}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/Recents.java b/packages/SystemUI/src/com/android/systemui/recents/Recents.java
index aee3623..ae79fe2 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/Recents.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/Recents.java
@@ -16,908 +16,385 @@
package com.android.systemui.recents;
-import android.app.Activity;
-import android.app.ActivityManager;
-import android.app.ActivityOptions;
-import android.app.ITaskStackListener;
-import android.content.ActivityNotFoundException;
-import android.content.BroadcastReceiver;
+import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
-import android.content.IntentFilter;
+import android.content.ServiceConnection;
import android.content.res.Configuration;
-import android.content.res.Resources;
-import android.graphics.Bitmap;
-import android.graphics.Canvas;
-import android.graphics.Rect;
-import android.os.AsyncTask;
import android.os.Handler;
-import android.os.SystemClock;
+import android.os.IBinder;
+import android.os.RemoteException;
import android.os.UserHandle;
-import android.util.MutableBoolean;
+import android.util.Log;
import android.view.Display;
-import android.view.LayoutInflater;
import android.view.View;
-import com.android.internal.logging.MetricsLogger;
-import com.android.systemui.Prefs;
-import com.android.systemui.R;
import com.android.systemui.RecentsComponent;
import com.android.systemui.SystemUI;
-import com.android.systemui.SystemUIApplication;
-import com.android.systemui.recents.misc.Console;
+import com.android.systemui.recents.events.EventBus;
+import com.android.systemui.recents.events.component.RecentsVisibilityChangedEvent;
+import com.android.systemui.recents.events.component.ScreenPinningRequestEvent;
import com.android.systemui.recents.misc.SystemServicesProxy;
-import com.android.systemui.recents.model.RecentsTaskLoadPlan;
-import com.android.systemui.recents.model.RecentsTaskLoader;
-import com.android.systemui.recents.model.Task;
-import com.android.systemui.recents.model.TaskGrouping;
-import com.android.systemui.recents.model.TaskStack;
-import com.android.systemui.recents.views.TaskStackView;
-import com.android.systemui.recents.views.TaskStackViewLayoutAlgorithm;
-import com.android.systemui.recents.views.TaskViewHeader;
-import com.android.systemui.recents.views.TaskViewTransform;
-import com.android.systemui.statusbar.phone.PhoneStatusBar;
import java.util.ArrayList;
-/**
- * Annotation for a method that is only called from the system user's SystemUI process and will be
- * proxied to the current user.
- */
-@interface ProxyFromSystemToCurrentUser {}
-/**
- * Annotation for a method that may be called from any user's SystemUI process and will be proxied
- * to the system user.
- */
-@interface ProxyFromAnyToSystemUser {}
-/** A proxy implementation for the recents component */
+/**
+ * An implementation of the SystemUI recents component, which supports both system and secondary
+ * users.
+ */
public class Recents extends SystemUI
- implements ActivityOptions.OnAnimationStartedListener, RecentsComponent {
+ implements RecentsComponent {
+
+ private final static String TAG = "Recents";
+ private final static boolean DEBUG = false;
public final static int EVENT_BUS_PRIORITY = 1;
+ public final static int BIND_TO_SYSTEM_USER_RETRY_DELAY = 5000;
- final public static String EXTRA_TRIGGERED_FROM_ALT_TAB = "triggeredFromAltTab";
- final public static String EXTRA_TRIGGERED_FROM_HOME_KEY = "triggeredFromHomeKey";
- final public static String EXTRA_RECENTS_VISIBILITY = "recentsVisibility";
+ private SystemServicesProxy mSystemServicesProxy;
+ private Handler mHandler;
+ private RecentsImpl mImpl;
- // Owner proxy events
- final public static String ACTION_PROXY_NOTIFY_RECENTS_VISIBLITY_TO_OWNER =
- "action_notify_recents_visibility_change";
- final public static String ACTION_PROXY_SCREEN_PINNING_REQUEST_TO_OWNER =
- "action_screen_pinning_request";
+ // Only For system user, this is the callbacks instance we return to each secondary user
+ private RecentsSystemUser mSystemUserCallbacks;
- final public static String ACTION_START_ENTER_ANIMATION = "action_start_enter_animation";
- final public static String ACTION_TOGGLE_RECENTS_ACTIVITY = "action_toggle_recents_activity";
- final public static String ACTION_HIDE_RECENTS_ACTIVITY = "action_hide_recents_activity";
+ // Only for secondary users, this is the callbacks instance provided by the system user to make
+ // calls back
+ private IRecentsSystemUserCallbacks mCallbacksToSystemUser;
- final static int sMinToggleDelay = 350;
+ // The set of runnables to run after binding to the system user's service.
+ private final ArrayList<Runnable> mOnConnectRunnables = new ArrayList<>();
- public final static String sToggleRecentsAction = "com.android.systemui.recents.SHOW_RECENTS";
- public final static String sRecentsPackage = "com.android.systemui";
- public final static String sRecentsActivity = "com.android.systemui.recents.RecentsActivity";
-
- /**
- * An implementation of ITaskStackListener, that allows us to listen for changes to the system
- * task stacks and update recents accordingly.
- */
- class TaskStackListenerImpl extends ITaskStackListener.Stub implements Runnable {
- Handler mHandler;
-
- public TaskStackListenerImpl(Handler handler) {
- mHandler = handler;
- }
-
+ // Only for secondary users, this is the death handler for the binder from the system user
+ private final IBinder.DeathRecipient mCallbacksToSystemUserDeathRcpt = new IBinder.DeathRecipient() {
@Override
- public void onTaskStackChanged() {
- // Debounce any task stack changes
- mHandler.removeCallbacks(this);
- mHandler.post(this);
- }
+ public void binderDied() {
+ mCallbacksToSystemUser = null;
- /** Preloads the next task */
- public void run() {
- // TODO: Temporarily skip this if multi stack is enabled
- /*
- RecentsConfiguration config = RecentsConfiguration.getInstance();
- if (config.svelteLevel == RecentsConfiguration.SVELTE_NONE) {
- RecentsTaskLoader loader = RecentsTaskLoader.getInstance();
- SystemServicesProxy ssp = loader.getSystemServicesProxy();
- ActivityManager.RunningTaskInfo runningTaskInfo = ssp.getTopMostTask();
-
- // Load the next task only if we aren't svelte
- RecentsTaskLoadPlan plan = loader.createLoadPlan(mContext);
- loader.preloadTasks(plan, true);
- RecentsTaskLoadPlan.Options launchOpts = new RecentsTaskLoadPlan.Options();
- // This callback is made when a new activity is launched and the old one is paused
- // so ignore the current activity and try and preload the thumbnail for the
- // previous one.
- if (runningTaskInfo != null) {
- launchOpts.runningTaskId = runningTaskInfo.id;
+ // Retry after a fixed duration
+ mHandler.postDelayed(new Runnable() {
+ @Override
+ public void run() {
+ registerWithSystemUser();
}
- launchOpts.numVisibleTasks = 2;
- launchOpts.numVisibleTaskThumbnails = 2;
- launchOpts.onlyLoadForCache = true;
- launchOpts.onlyLoadPausedActivities = true;
- loader.loadTasks(mContext, plan, launchOpts);
- }
- */
+ }, BIND_TO_SYSTEM_USER_RETRY_DELAY);
}
- }
+ };
- /**
- * A proxy for Recents events which happens strictly for the owner.
- */
- class RecentsOwnerEventProxyReceiver extends BroadcastReceiver {
+ // Only for secondary users, this is the service connection we use to connect to the system user
+ private final ServiceConnection mServiceConnectionToSystemUser = new ServiceConnection() {
@Override
- public void onReceive(Context context, Intent intent) {
- switch (intent.getAction()) {
- case ACTION_PROXY_NOTIFY_RECENTS_VISIBLITY_TO_OWNER:
- visibilityChanged(context,
- intent.getBooleanExtra(EXTRA_RECENTS_VISIBILITY, false));
- break;
- case ACTION_PROXY_SCREEN_PINNING_REQUEST_TO_OWNER:
- onStartScreenPinning(context);
- break;
+ public void onServiceConnected(ComponentName name, IBinder service) {
+ if (service != null) {
+ mCallbacksToSystemUser = IRecentsSystemUserCallbacks.Stub.asInterface(
+ service);
+
+ // Listen for system user's death, so that we can reconnect later
+ try {
+ service.linkToDeath(mCallbacksToSystemUserDeathRcpt, 0);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Lost connection to (System) SystemUI", e);
+ }
+
+ // Run each of the queued runnables
+ runAndFlushOnConnectRunnables();
}
+
+ // Unbind ourselves now that we've registered our callbacks. The
+ // binder to the system user are still valid at this point.
+ mContext.unbindService(this);
}
- }
- static RecentsTaskLoadPlan sInstanceLoadPlan;
- static Recents sInstance;
-
- SystemServicesProxy mSystemServicesProxy;
- Handler mHandler;
- TaskStackListenerImpl mTaskStackListener;
- RecentsOwnerEventProxyReceiver mProxyBroadcastReceiver;
- RecentsAppWidgetHost mAppWidgetHost;
- boolean mBootCompleted;
- boolean mStartAnimationTriggered;
- boolean mCanReuseTaskStackViews = true;
-
- // Task launching
- RecentsConfiguration mConfig;
- Rect mSearchBarBounds = new Rect();
- Rect mTaskStackBounds = new Rect();
- Rect mLastTaskViewBounds = new Rect();
- TaskViewTransform mTmpTransform = new TaskViewTransform();
- int mStatusBarHeight;
- int mNavBarHeight;
- int mNavBarWidth;
- int mTaskBarHeight;
-
- // Header (for transition)
- TaskViewHeader mHeaderBar;
- final Object mHeaderBarLock = new Object();
- TaskStackView mDummyStackView;
-
- // Variables to keep track of if we need to start recents after binding
- boolean mTriggeredFromAltTab;
- long mLastToggleTime;
-
- Bitmap mThumbnailTransitionBitmapCache;
- Task mThumbnailTransitionBitmapCacheKey;
-
- public Recents() {
- }
+ @Override
+ public void onServiceDisconnected(ComponentName name) {
+ // Do nothing
+ }
+ };
/**
- * Gets the singleton instance and starts it if needed. On the primary user on the device, this
- * component gets started as a normal {@link SystemUI} component. On a secondary user, this
- * lifecycle doesn't exist, so we need to start it manually here if needed.
+ * Returns the callbacks interface that non-system users can call.
*/
- public static Recents getInstanceAndStartIfNeeded(Context ctx) {
- if (sInstance == null) {
- sInstance = new Recents();
- sInstance.mContext = ctx;
- sInstance.start();
- sInstance.onBootCompleted();
- }
- return sInstance;
+ public IBinder getSystemUserCallbacks() {
+ return mSystemUserCallbacks;
}
- /** Creates a new broadcast intent */
- static Intent createLocalBroadcastIntent(Context context, String action) {
- Intent intent = new Intent(action);
- intent.setPackage(context.getPackageName());
- intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT |
- Intent.FLAG_RECEIVER_FOREGROUND);
- return intent;
- }
-
- /** Initializes the Recents. */
- @ProxyFromSystemToCurrentUser
@Override
public void start() {
- if (sInstance == null) {
- sInstance = this;
- }
- Resources res = mContext.getResources();
- RecentsTaskLoader.initialize(mContext);
- LayoutInflater inflater = LayoutInflater.from(mContext);
mSystemServicesProxy = new SystemServicesProxy(mContext);
mHandler = new Handler();
- mAppWidgetHost = new RecentsAppWidgetHost(mContext, Constants.Values.App.AppWidgetHostId);
+ mImpl = new RecentsImpl(mContext);
- // Register the task stack listener
- mTaskStackListener = new TaskStackListenerImpl(mHandler);
- mSystemServicesProxy.registerTaskStackListener(mTaskStackListener);
+ // Register with the event bus
+ EventBus.getDefault().register(this, EVENT_BUS_PRIORITY);
- // Only the owner has the callback to update the SysUI visibility flags, so all non-owner
- // instances of RecentsComponent needs to notify the owner when the visibility
- // changes.
- if (mSystemServicesProxy.isForegroundUserSystem()) {
- mProxyBroadcastReceiver = new RecentsOwnerEventProxyReceiver();
- IntentFilter filter = new IntentFilter();
- filter.addAction(Recents.ACTION_PROXY_NOTIFY_RECENTS_VISIBLITY_TO_OWNER);
- filter.addAction(Recents.ACTION_PROXY_SCREEN_PINNING_REQUEST_TO_OWNER);
- mContext.registerReceiverAsUser(mProxyBroadcastReceiver, UserHandle.CURRENT, filter,
- null, mHandler);
+ // Due to the fact that RecentsActivity is per-user, we need to establish and interface for
+ // the system user's Recents component to pass events (like show/hide/toggleRecents) to the
+ // secondary user, and vice versa (like visibility change, screen pinning).
+ final int processUser = mSystemServicesProxy.getProcessUser();
+ if (mSystemServicesProxy.isSystemUser(processUser)) {
+ // For the system user, initialize an instance of the interface that we can pass to the
+ // secondary user
+ mSystemUserCallbacks = new RecentsSystemUser(mContext, mImpl);
+ } else {
+ // For the secondary user, bind to the primary user's service to get a persistent
+ // interface to register its implementation and to later update its state
+ registerWithSystemUser();
}
-
- // Initialize the static configuration resources
- mConfig = RecentsConfiguration.initialize(mContext, mSystemServicesProxy);
- mStatusBarHeight = res.getDimensionPixelSize(com.android.internal.R.dimen.status_bar_height);
- mNavBarHeight = res.getDimensionPixelSize(com.android.internal.R.dimen.navigation_bar_height);
- mNavBarWidth = res.getDimensionPixelSize(com.android.internal.R.dimen.navigation_bar_width);
- mTaskBarHeight = res.getDimensionPixelSize(R.dimen.recents_task_bar_height);
- mDummyStackView = new TaskStackView(mContext, new TaskStack());
- mHeaderBar = (TaskViewHeader) inflater.inflate(R.layout.recents_task_view_header,
- null, false);
- reloadHeaderBarLayout(true /* tryAndBindSearchWidget */);
-
- // When we start, preload the data associated with the previous recent tasks.
- // We can use a new plan since the caches will be the same.
- RecentsTaskLoader loader = RecentsTaskLoader.getInstance();
- RecentsTaskLoadPlan plan = loader.createLoadPlan(mContext);
- loader.preloadTasks(plan, true /* isTopTaskHome */);
- RecentsTaskLoadPlan.Options launchOpts = new RecentsTaskLoadPlan.Options();
- launchOpts.numVisibleTasks = loader.getApplicationIconCacheSize();
- launchOpts.numVisibleTaskThumbnails = loader.getThumbnailCacheSize();
- launchOpts.onlyLoadForCache = true;
- loader.loadTasks(mContext, plan, launchOpts);
putComponent(Recents.class, this);
}
@Override
public void onBootCompleted() {
- mBootCompleted = true;
- reloadHeaderBarLayout(true /* tryAndBindSearchWidget */);
+ mImpl.onBootCompleted();
}
- /** Shows the Recents. */
- @ProxyFromSystemToCurrentUser
+ /**
+ * Shows the Recents.
+ */
@Override
public void showRecents(boolean triggeredFromAltTab, View statusBarView) {
- if (mSystemServicesProxy.isForegroundUserSystem()) {
- showRecentsInternal(triggeredFromAltTab);
+ int currentUser = mSystemServicesProxy.getCurrentUser();
+ if (mSystemServicesProxy.isSystemUser(currentUser)) {
+ mImpl.showRecents(triggeredFromAltTab);
} else {
- Intent intent = createLocalBroadcastIntent(mContext,
- RecentsUserEventProxyReceiver.ACTION_PROXY_SHOW_RECENTS_TO_USER);
- intent.putExtra(EXTRA_TRIGGERED_FROM_ALT_TAB, triggeredFromAltTab);
- mContext.sendBroadcastAsUser(intent, UserHandle.CURRENT);
+ if (mSystemUserCallbacks != null) {
+ IRecentsNonSystemUserCallbacks callbacks =
+ mSystemUserCallbacks.getNonSystemUserRecentsForUser(currentUser);
+ if (callbacks != null) {
+ try {
+ callbacks.showRecents(triggeredFromAltTab);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Callback failed", e);
+ }
+ } else {
+ Log.e(TAG, "No SystemUI callbacks found for user: " + currentUser);
+ }
+ }
}
}
- void showRecentsInternal(boolean triggeredFromAltTab) {
- mTriggeredFromAltTab = triggeredFromAltTab;
-
- try {
- showRecentsActivity();
- } catch (ActivityNotFoundException e) {
- Console.logRawError("Failed to launch RecentAppsIntent", e);
- }
- }
-
- /** Hides the Recents. */
- @ProxyFromSystemToCurrentUser
+ /**
+ * Hides the Recents.
+ */
@Override
public void hideRecents(boolean triggeredFromAltTab, boolean triggeredFromHomeKey) {
- if (mSystemServicesProxy.isForegroundUserSystem()) {
- hideRecentsInternal(triggeredFromAltTab, triggeredFromHomeKey);
+ int currentUser = mSystemServicesProxy.getCurrentUser();
+ if (mSystemServicesProxy.isSystemUser(currentUser)) {
+ mImpl.hideRecents(triggeredFromAltTab, triggeredFromHomeKey);
} else {
- Intent intent = createLocalBroadcastIntent(mContext,
- RecentsUserEventProxyReceiver.ACTION_PROXY_HIDE_RECENTS_TO_USER);
- intent.putExtra(EXTRA_TRIGGERED_FROM_ALT_TAB, triggeredFromAltTab);
- intent.putExtra(EXTRA_TRIGGERED_FROM_HOME_KEY, triggeredFromHomeKey);
- mContext.sendBroadcastAsUser(intent, UserHandle.CURRENT);
+ if (mSystemUserCallbacks != null) {
+ IRecentsNonSystemUserCallbacks callbacks =
+ mSystemUserCallbacks.getNonSystemUserRecentsForUser(currentUser);
+ if (callbacks != null) {
+ try {
+ callbacks.hideRecents(triggeredFromAltTab, triggeredFromHomeKey);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Callback failed", e);
+ }
+ } else {
+ Log.e(TAG, "No SystemUI callbacks found for user: " + currentUser);
+ }
+ }
}
}
- void hideRecentsInternal(boolean triggeredFromAltTab, boolean triggeredFromHomeKey) {
- if (mBootCompleted) {
- // Defer to the activity to handle hiding recents, if it handles it, then it must still
- // be visible
- Intent intent = createLocalBroadcastIntent(mContext, ACTION_HIDE_RECENTS_ACTIVITY);
- intent.putExtra(EXTRA_TRIGGERED_FROM_ALT_TAB, triggeredFromAltTab);
- intent.putExtra(EXTRA_TRIGGERED_FROM_HOME_KEY, triggeredFromHomeKey);
- mContext.sendBroadcastAsUser(intent, UserHandle.CURRENT);
- }
- }
-
- /** Toggles the Recents activity. */
- @ProxyFromSystemToCurrentUser
+ /**
+ * Toggles the Recents activity.
+ */
@Override
public void toggleRecents(Display display, int layoutDirection, View statusBarView) {
- if (mSystemServicesProxy.isForegroundUserSystem()) {
- toggleRecentsInternal();
+ int currentUser = mSystemServicesProxy.getCurrentUser();
+ if (mSystemServicesProxy.isSystemUser(currentUser)) {
+ mImpl.toggleRecents();
} else {
- Intent intent = createLocalBroadcastIntent(mContext,
- RecentsUserEventProxyReceiver.ACTION_PROXY_TOGGLE_RECENTS_TO_USER);
- mContext.sendBroadcastAsUser(intent, UserHandle.CURRENT);
+ if (mSystemUserCallbacks != null) {
+ IRecentsNonSystemUserCallbacks callbacks =
+ mSystemUserCallbacks.getNonSystemUserRecentsForUser(currentUser);
+ if (callbacks != null) {
+ try {
+ callbacks.toggleRecents();
+ } catch (RemoteException e) {
+ Log.e(TAG, "Callback failed", e);
+ }
+ } else {
+ Log.e(TAG, "No SystemUI callbacks found for user: " + currentUser);
+ }
+ }
}
}
- void toggleRecentsInternal() {
- mTriggeredFromAltTab = false;
-
- try {
- toggleRecentsActivity();
- } catch (ActivityNotFoundException e) {
- Console.logRawError("Failed to launch RecentAppsIntent", e);
- }
- }
-
- /** Preloads info for the Recents activity. */
- @ProxyFromSystemToCurrentUser
+ /**
+ * Preloads info for the Recents activity.
+ */
@Override
public void preloadRecents() {
- if (mSystemServicesProxy.isForegroundUserSystem()) {
- preloadRecentsInternal();
+ int currentUser = mSystemServicesProxy.getCurrentUser();
+ if (mSystemServicesProxy.isSystemUser(currentUser)) {
+ mImpl.preloadRecents();
} else {
- Intent intent = createLocalBroadcastIntent(mContext,
- RecentsUserEventProxyReceiver.ACTION_PROXY_PRELOAD_RECENTS_TO_USER);
- mContext.sendBroadcastAsUser(intent, UserHandle.CURRENT);
- }
- }
-
- void preloadRecentsInternal() {
- // Preload only the raw task list into a new load plan (which will be consumed by the
- // RecentsActivity) only if there is a task to animate to.
- ActivityManager.RunningTaskInfo topTask = mSystemServicesProxy.getTopMostTask();
- MutableBoolean topTaskHome = new MutableBoolean(true);
- RecentsTaskLoader loader = RecentsTaskLoader.getInstance();
- sInstanceLoadPlan = loader.createLoadPlan(mContext);
- if (topTask != null && !mSystemServicesProxy.isRecentsTopMost(topTask, topTaskHome)) {
- sInstanceLoadPlan.preloadRawTasks(topTaskHome.value);
- loader.preloadTasks(sInstanceLoadPlan, topTaskHome.value);
- TaskStack stack = sInstanceLoadPlan.getTaskStack();
- if (stack.getTaskCount() > 0) {
- preCacheThumbnailTransitionBitmapAsync(topTask, stack, mDummyStackView,
- topTaskHome.value);
+ if (mSystemUserCallbacks != null) {
+ IRecentsNonSystemUserCallbacks callbacks =
+ mSystemUserCallbacks.getNonSystemUserRecentsForUser(currentUser);
+ if (callbacks != null) {
+ try {
+ callbacks.preloadRecents();
+ } catch (RemoteException e) {
+ Log.e(TAG, "Callback failed", e);
+ }
+ } else {
+ Log.e(TAG, "No SystemUI callbacks found for user: " + currentUser);
+ }
}
}
}
@Override
public void cancelPreloadingRecents() {
- // Do nothing
- }
-
- void showRelativeAffiliatedTask(boolean showNextTask) {
- // Return early if there is no focused stack
- int focusedStackId = mSystemServicesProxy.getFocusedStack();
- RecentsTaskLoader loader = RecentsTaskLoader.getInstance();
- RecentsTaskLoadPlan plan = loader.createLoadPlan(mContext);
- loader.preloadTasks(plan, true /* isTopTaskHome */);
- TaskStack focusedStack = plan.getTaskStack();
-
- // Return early if there are no tasks in the focused stack
- if (focusedStack == null || focusedStack.getTaskCount() == 0) return;
-
- ActivityManager.RunningTaskInfo runningTask = mSystemServicesProxy.getTopMostTask();
- // Return early if there is no running task (can't determine affiliated tasks in this case)
- if (runningTask == null) return;
- // Return early if the running task is in the home stack (optimization)
- if (mSystemServicesProxy.isInHomeStack(runningTask.id)) return;
-
- // Find the task in the recents list
- ArrayList<Task> tasks = focusedStack.getTasks();
- Task toTask = null;
- ActivityOptions launchOpts = null;
- int taskCount = tasks.size();
- int numAffiliatedTasks = 0;
- for (int i = 0; i < taskCount; i++) {
- Task task = tasks.get(i);
- if (task.key.id == runningTask.id) {
- TaskGrouping group = task.group;
- Task.TaskKey toTaskKey;
- if (showNextTask) {
- toTaskKey = group.getNextTaskInGroup(task);
- launchOpts = ActivityOptions.makeCustomAnimation(mContext,
- R.anim.recents_launch_next_affiliated_task_target,
- R.anim.recents_launch_next_affiliated_task_source);
- } else {
- toTaskKey = group.getPrevTaskInGroup(task);
- launchOpts = ActivityOptions.makeCustomAnimation(mContext,
- R.anim.recents_launch_prev_affiliated_task_target,
- R.anim.recents_launch_prev_affiliated_task_source);
- }
- if (toTaskKey != null) {
- toTask = focusedStack.findTaskWithId(toTaskKey.id);
- }
- numAffiliatedTasks = group.getTaskCount();
- break;
- }
- }
-
- // Return early if there is no next task
- if (toTask == null) {
- if (numAffiliatedTasks > 1) {
- if (showNextTask) {
- mSystemServicesProxy.startInPlaceAnimationOnFrontMostApplication(
- ActivityOptions.makeCustomInPlaceAnimation(mContext,
- R.anim.recents_launch_next_affiliated_task_bounce));
- } else {
- mSystemServicesProxy.startInPlaceAnimationOnFrontMostApplication(
- ActivityOptions.makeCustomInPlaceAnimation(mContext,
- R.anim.recents_launch_prev_affiliated_task_bounce));
- }
- }
- return;
- }
-
- // Keep track of actually launched affiliated tasks
- MetricsLogger.count(mContext, "overview_affiliated_task_launch", 1);
-
- // Launch the task
- if (toTask.isActive) {
- // Bring an active task to the foreground
- mSystemServicesProxy.moveTaskToFront(toTask.key.id, launchOpts);
+ int currentUser = mSystemServicesProxy.getCurrentUser();
+ if (mSystemServicesProxy.isSystemUser(currentUser)) {
+ mImpl.cancelPreloadingRecents();
} else {
- mSystemServicesProxy.startActivityFromRecents(mContext, toTask.key.id,
- toTask.activityLabel, launchOpts);
+ if (mSystemUserCallbacks != null) {
+ IRecentsNonSystemUserCallbacks callbacks =
+ mSystemUserCallbacks.getNonSystemUserRecentsForUser(currentUser);
+ if (callbacks != null) {
+ try {
+ callbacks.cancelPreloadingRecents();
+ } catch (RemoteException e) {
+ Log.e(TAG, "Callback failed", e);
+ }
+ } else {
+ Log.e(TAG, "No SystemUI callbacks found for user: " + currentUser);
+ }
+ }
}
}
@Override
public void showNextAffiliatedTask() {
- // Keep track of when the affiliated task is triggered
- MetricsLogger.count(mContext, "overview_affiliated_task_next", 1);
- showRelativeAffiliatedTask(true);
+ mImpl.showNextAffiliatedTask();
}
@Override
public void showPrevAffiliatedTask() {
- // Keep track of when the affiliated task is triggered
- MetricsLogger.count(mContext, "overview_affiliated_task_prev", 1);
- showRelativeAffiliatedTask(false);
+ mImpl.showPrevAffiliatedTask();
}
- /** Updates on configuration change. */
- @ProxyFromSystemToCurrentUser
+ /**
+ * Updates on configuration change.
+ */
public void onConfigurationChanged(Configuration newConfig) {
- if (mSystemServicesProxy.isForegroundUserSystem()) {
- configurationChanged();
+ int currentUser = mSystemServicesProxy.getCurrentUser();
+ if (mSystemServicesProxy.isSystemUser(currentUser)) {
+ mImpl.onConfigurationChanged();
} else {
- Intent intent = createLocalBroadcastIntent(mContext,
- RecentsUserEventProxyReceiver.ACTION_PROXY_CONFIG_CHANGE_TO_USER);
- mContext.sendBroadcastAsUser(intent, UserHandle.CURRENT);
- }
- }
- void configurationChanged() {
- // Don't reuse task stack views if the configuration changes
- mCanReuseTaskStackViews = false;
- mConfig.updateOnConfigurationChange();
- }
-
- /**
- * Prepares the header bar layout for the next transition, if the task view bounds has changed
- * since the last call, it will attempt to re-measure and layout the header bar to the new size.
- *
- * @param tryAndBindSearchWidget if set, will attempt to fetch and bind the search widget if one
- * is not already bound (can be expensive)
- */
- void reloadHeaderBarLayout(boolean tryAndBindSearchWidget) {
- Rect windowRect = mSystemServicesProxy.getWindowRect();
-
- // Update the configuration for the current state
- mConfig.update(mContext, mSystemServicesProxy, mSystemServicesProxy.getWindowRect());
-
- if (tryAndBindSearchWidget) {
- // Try and pre-emptively bind the search widget on startup to ensure that we
- // have the right thumbnail bounds to animate to.
- // Note: We have to reload the widget id before we get the task stack bounds below
- if (mSystemServicesProxy.getOrBindSearchAppWidget(mContext, mAppWidgetHost) != null) {
- mConfig.getSearchBarBounds(windowRect,
- mStatusBarHeight, mSearchBarBounds);
- }
- }
- Rect systemInsets = new Rect(0, mStatusBarHeight,
- (mConfig.hasTransposedNavBar ? mNavBarWidth : 0),
- (mConfig.hasTransposedNavBar ? 0 : mNavBarHeight));
- mConfig.getTaskStackBounds(windowRect, systemInsets.top, systemInsets.right,
- mSearchBarBounds, mTaskStackBounds);
-
- // Rebind the header bar and draw it for the transition
- TaskStackViewLayoutAlgorithm algo = mDummyStackView.getStackAlgorithm();
- Rect taskStackBounds = new Rect(mTaskStackBounds);
- algo.setSystemInsets(systemInsets);
- algo.computeRects(windowRect.width(), windowRect.height(), taskStackBounds);
- Rect taskViewBounds = algo.getUntransformedTaskViewBounds();
- if (!taskViewBounds.equals(mLastTaskViewBounds)) {
- mLastTaskViewBounds.set(taskViewBounds);
-
- int taskViewWidth = taskViewBounds.width();
- synchronized (mHeaderBarLock) {
- mHeaderBar.measure(
- View.MeasureSpec.makeMeasureSpec(taskViewWidth, View.MeasureSpec.EXACTLY),
- View.MeasureSpec.makeMeasureSpec(mTaskBarHeight, View.MeasureSpec.EXACTLY));
- mHeaderBar.layout(0, 0, taskViewWidth, mTaskBarHeight);
- }
- }
- }
-
- /** Toggles the recents activity */
- void toggleRecentsActivity() {
- // If the user has toggled it too quickly, then just eat up the event here (it's better than
- // showing a janky screenshot).
- // NOTE: Ideally, the screenshot mechanism would take the window transform into account
- if ((SystemClock.elapsedRealtime() - mLastToggleTime) < sMinToggleDelay) {
- return;
- }
-
- // If Recents is the front most activity, then we should just communicate with it directly
- // to launch the first task or dismiss itself
- ActivityManager.RunningTaskInfo topTask = mSystemServicesProxy.getTopMostTask();
- MutableBoolean isTopTaskHome = new MutableBoolean(true);
- if (topTask != null && mSystemServicesProxy.isRecentsTopMost(topTask, isTopTaskHome)) {
- // Notify recents to toggle itself
- Intent intent = createLocalBroadcastIntent(mContext, ACTION_TOGGLE_RECENTS_ACTIVITY);
- mContext.sendBroadcastAsUser(intent, UserHandle.CURRENT);
- mLastToggleTime = SystemClock.elapsedRealtime();
- return;
- } else {
- // Otherwise, start the recents activity
- showRecentsActivity(topTask, isTopTaskHome.value);
- }
- }
-
- /** Shows the recents activity if it is not already running */
- void showRecentsActivity() {
- // Check if the top task is in the home stack, and start the recents activity
- ActivityManager.RunningTaskInfo topTask = mSystemServicesProxy.getTopMostTask();
- MutableBoolean isTopTaskHome = new MutableBoolean(true);
- if (topTask == null || !mSystemServicesProxy.isRecentsTopMost(topTask, isTopTaskHome)) {
- showRecentsActivity(topTask, isTopTaskHome.value);
- }
- }
-
- /**
- * Creates the activity options for a unknown state->recents transition.
- */
- ActivityOptions getUnknownTransitionActivityOptions() {
- mStartAnimationTriggered = false;
- return ActivityOptions.makeCustomAnimation(mContext,
- R.anim.recents_from_unknown_enter,
- R.anim.recents_from_unknown_exit,
- mHandler, this);
- }
-
- /**
- * Creates the activity options for a home->recents transition.
- */
- ActivityOptions getHomeTransitionActivityOptions(boolean fromSearchHome) {
- mStartAnimationTriggered = false;
- if (fromSearchHome) {
- return ActivityOptions.makeCustomAnimation(mContext,
- R.anim.recents_from_search_launcher_enter,
- R.anim.recents_from_search_launcher_exit,
- mHandler, this);
- }
- return ActivityOptions.makeCustomAnimation(mContext,
- R.anim.recents_from_launcher_enter,
- R.anim.recents_from_launcher_exit,
- mHandler, this);
- }
-
- /**
- * Creates the activity options for an app->recents transition.
- */
- ActivityOptions getThumbnailTransitionActivityOptions(ActivityManager.RunningTaskInfo topTask,
- TaskStack stack, TaskStackView stackView) {
-
- // Update the destination rect
- Task toTask = new Task();
- TaskViewTransform toTransform = getThumbnailTransitionTransform(stack, stackView,
- topTask.id, toTask);
- Rect toTaskRect = toTransform.rect;
- Bitmap thumbnail;
- if (mThumbnailTransitionBitmapCacheKey != null
- && mThumbnailTransitionBitmapCacheKey.key != null
- && mThumbnailTransitionBitmapCacheKey.key.equals(toTask.key)) {
- thumbnail = mThumbnailTransitionBitmapCache;
- mThumbnailTransitionBitmapCacheKey = null;
- mThumbnailTransitionBitmapCache = null;
- } else {
- preloadIcon(topTask);
- thumbnail = drawThumbnailTransitionBitmap(toTask, toTransform);
- }
- if (thumbnail != null) {
- mStartAnimationTriggered = false;
- return ActivityOptions.makeThumbnailAspectScaleDownAnimation(mDummyStackView,
- thumbnail, toTaskRect.left, toTaskRect.top, toTaskRect.width(),
- toTaskRect.height(), mHandler, this);
- }
-
- // If both the screenshot and thumbnail fails, then just fall back to the default transition
- return getUnknownTransitionActivityOptions();
- }
-
- /**
- * Preloads the icon of a task.
- */
- void preloadIcon(ActivityManager.RunningTaskInfo task) {
-
- // Ensure that we load the running task's icon
- RecentsTaskLoadPlan.Options launchOpts = new RecentsTaskLoadPlan.Options();
- launchOpts.runningTaskId = task.id;
- launchOpts.loadThumbnails = false;
- launchOpts.onlyLoadForCache = true;
- RecentsTaskLoader.getInstance().loadTasks(mContext, sInstanceLoadPlan, launchOpts);
- }
-
- /**
- * Caches the header thumbnail used for a window animation asynchronously into
- * {@link #mThumbnailTransitionBitmapCache}.
- */
- void preCacheThumbnailTransitionBitmapAsync(ActivityManager.RunningTaskInfo topTask,
- TaskStack stack, TaskStackView stackView, boolean isTopTaskHome) {
- preloadIcon(topTask);
-
- // Update the header bar if necessary
- reloadHeaderBarLayout(false /* tryAndBindSearchWidget */);
-
- // Update the destination rect
- mDummyStackView.updateMinMaxScrollForStack(stack);
- final Task toTask = new Task();
- final TaskViewTransform toTransform = getThumbnailTransitionTransform(stack, stackView,
- topTask.id, toTask);
- new AsyncTask<Void, Void, Bitmap>() {
- @Override
- protected Bitmap doInBackground(Void... params) {
- return drawThumbnailTransitionBitmap(toTask, toTransform);
- }
-
- @Override
- protected void onPostExecute(Bitmap bitmap) {
- mThumbnailTransitionBitmapCache = bitmap;
- mThumbnailTransitionBitmapCacheKey = toTask;
- }
- }.execute();
- }
-
- /**
- * Draws the header of a task used for the window animation into a bitmap.
- */
- Bitmap drawThumbnailTransitionBitmap(Task toTask, TaskViewTransform toTransform) {
- if (toTransform != null && toTask.key != null) {
- Bitmap thumbnail;
- synchronized (mHeaderBarLock) {
- int toHeaderWidth = (int) (mHeaderBar.getMeasuredWidth() * toTransform.scale);
- int toHeaderHeight = (int) (mHeaderBar.getMeasuredHeight() * toTransform.scale);
- thumbnail = Bitmap.createBitmap(toHeaderWidth, toHeaderHeight,
- Bitmap.Config.ARGB_8888);
- if (Constants.DebugFlags.App.EnableTransitionThumbnailDebugMode) {
- thumbnail.eraseColor(0xFFff0000);
- } else {
- Canvas c = new Canvas(thumbnail);
- c.scale(toTransform.scale, toTransform.scale);
- mHeaderBar.rebindToTask(toTask);
- mHeaderBar.draw(c);
- c.setBitmap(null);
- }
- }
- return thumbnail.createAshmemBitmap();
- }
- return null;
- }
-
- /** Returns the transition rect for the given task id. */
- TaskViewTransform getThumbnailTransitionTransform(TaskStack stack, TaskStackView stackView,
- int runningTaskId, Task runningTaskOut) {
- // Find the running task in the TaskStack
- Task task = null;
- ArrayList<Task> tasks = stack.getTasks();
- if (runningTaskId != -1) {
- // Otherwise, try and find the task with the
- int taskCount = tasks.size();
- for (int i = taskCount - 1; i >= 0; i--) {
- Task t = tasks.get(i);
- if (t.key.id == runningTaskId) {
- task = t;
- runningTaskOut.copyFrom(t);
- break;
- }
- }
- }
- if (task == null) {
- // If no task is specified or we can not find the task just use the front most one
- task = tasks.get(tasks.size() - 1);
- runningTaskOut.copyFrom(task);
- }
-
- // Get the transform for the running task
- stackView.getScroller().setStackScrollToInitialState();
- mTmpTransform = stackView.getStackAlgorithm().getStackTransform(task,
- stackView.getScroller().getStackScroll(), mTmpTransform, null);
- return mTmpTransform;
- }
-
- /** Shows the recents activity */
- void showRecentsActivity(ActivityManager.RunningTaskInfo topTask, boolean isTopTaskHome) {
- RecentsTaskLoader loader = RecentsTaskLoader.getInstance();
-
- // Update the header bar if necessary
- reloadHeaderBarLayout(false /* tryAndBindSearchWidget */);
-
- if (sInstanceLoadPlan == null) {
- // Create a new load plan if onPreloadRecents() was never triggered
- sInstanceLoadPlan = loader.createLoadPlan(mContext);
- }
-
- if (!sInstanceLoadPlan.hasTasks()) {
- loader.preloadTasks(sInstanceLoadPlan, isTopTaskHome);
- }
- TaskStack stack = sInstanceLoadPlan.getTaskStack();
-
- // Prepare the dummy stack for the transition
- mDummyStackView.updateMinMaxScrollForStack(stack);
- TaskStackViewLayoutAlgorithm.VisibilityReport stackVr =
- mDummyStackView.computeStackVisibilityReport();
- boolean hasRecentTasks = stack.getTaskCount() > 0;
- boolean useThumbnailTransition = (topTask != null) && !isTopTaskHome && hasRecentTasks;
-
- if (useThumbnailTransition) {
- // Try starting with a thumbnail transition
- ActivityOptions opts = getThumbnailTransitionActivityOptions(topTask, stack,
- mDummyStackView);
- if (opts != null) {
- startRecentsActivity(topTask, opts, false /* fromHome */,
- false /* fromSearchHome */, true /* fromThumbnail */, stackVr);
- } else {
- // Fall through below to the non-thumbnail transition
- useThumbnailTransition = false;
- }
- }
-
- if (!useThumbnailTransition) {
- // If there is no thumbnail transition, but is launching from home into recents, then
- // use a quick home transition and do the animation from home
- if (hasRecentTasks) {
- String homeActivityPackage = mSystemServicesProxy.getHomeActivityPackageName();
- String searchWidgetPackage =
- Prefs.getString(mContext, Prefs.Key.SEARCH_APP_WIDGET_PACKAGE, null);
-
- // Determine whether we are coming from a search owned home activity
- boolean fromSearchHome = (homeActivityPackage != null) &&
- homeActivityPackage.equals(searchWidgetPackage);
- ActivityOptions opts = getHomeTransitionActivityOptions(fromSearchHome);
- startRecentsActivity(topTask, opts, true /* fromHome */, fromSearchHome,
- false /* fromThumbnail */, stackVr);
- } else {
- // Otherwise we do the normal fade from an unknown source
- ActivityOptions opts = getUnknownTransitionActivityOptions();
- startRecentsActivity(topTask, opts, true /* fromHome */,
- false /* fromSearchHome */, false /* fromThumbnail */, stackVr);
- }
- }
- mLastToggleTime = SystemClock.elapsedRealtime();
- }
-
- /** Starts the recents activity */
- void startRecentsActivity(ActivityManager.RunningTaskInfo topTask,
- ActivityOptions opts, boolean fromHome, boolean fromSearchHome, boolean fromThumbnail,
- TaskStackViewLayoutAlgorithm.VisibilityReport vr) {
- // Update the configuration based on the launch options
- RecentsActivityLaunchState launchState = mConfig.getLaunchState();
- launchState.launchedFromHome = fromSearchHome || fromHome;
- launchState.launchedFromSearchHome = fromSearchHome;
- launchState.launchedFromAppWithThumbnail = fromThumbnail;
- launchState.launchedToTaskId = (topTask != null) ? topTask.id : -1;
- launchState.launchedWithAltTab = mTriggeredFromAltTab;
- launchState.launchedReuseTaskStackViews = mCanReuseTaskStackViews;
- launchState.launchedNumVisibleTasks = vr.numVisibleTasks;
- launchState.launchedNumVisibleThumbnails = vr.numVisibleThumbnails;
- launchState.launchedHasConfigurationChanged = false;
-
- Intent intent = new Intent(sToggleRecentsAction);
- intent.setClassName(sRecentsPackage, sRecentsActivity);
- intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK
- | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS
- | Intent.FLAG_ACTIVITY_TASK_ON_HOME);
- if (opts != null) {
- mContext.startActivityAsUser(intent, opts.toBundle(), UserHandle.CURRENT);
- } else {
- mContext.startActivityAsUser(intent, UserHandle.CURRENT);
- }
- mCanReuseTaskStackViews = true;
- }
-
- /** Notifies the callbacks that the visibility of Recents has changed. */
- @ProxyFromAnyToSystemUser
- public static void notifyVisibilityChanged(Context context, SystemServicesProxy ssp,
- boolean visible) {
- if (ssp.isForegroundUserSystem()) {
- visibilityChanged(context, visible);
- } else {
- Intent intent = createLocalBroadcastIntent(context,
- ACTION_PROXY_NOTIFY_RECENTS_VISIBLITY_TO_OWNER);
- intent.putExtra(EXTRA_RECENTS_VISIBILITY, visible);
- context.sendBroadcastAsUser(intent, UserHandle.SYSTEM);
- }
- }
- static void visibilityChanged(Context context, boolean visible) {
- // For the primary user, the context for the SystemUI component is the SystemUIApplication
- SystemUIApplication app = (SystemUIApplication)
- getInstanceAndStartIfNeeded(context.getApplicationContext()).mContext;
- PhoneStatusBar statusBar = app.getComponent(PhoneStatusBar.class);
- if (statusBar != null) {
- statusBar.updateRecentsVisibility(visible);
- }
- }
-
- /** Notifies the status bar to trigger screen pinning. */
- @ProxyFromAnyToSystemUser
- public static void startScreenPinning(Context context, SystemServicesProxy ssp) {
- if (ssp.isForegroundUserSystem()) {
- onStartScreenPinning(context);
- } else {
- Intent intent = createLocalBroadcastIntent(context,
- ACTION_PROXY_SCREEN_PINNING_REQUEST_TO_OWNER);
- context.sendBroadcastAsUser(intent, UserHandle.SYSTEM);
- }
- }
- static void onStartScreenPinning(Context context) {
- // For the primary user, the context for the SystemUI component is the SystemUIApplication
- SystemUIApplication app = (SystemUIApplication)
- getInstanceAndStartIfNeeded(context.getApplicationContext()).mContext;
- PhoneStatusBar statusBar = app.getComponent(PhoneStatusBar.class);
- if (statusBar != null) {
- statusBar.showScreenPinningRequest(false);
- }
- }
-
- /**
- * Returns the preloaded load plan and invalidates it.
- */
- public static RecentsTaskLoadPlan consumeInstanceLoadPlan() {
- RecentsTaskLoadPlan plan = sInstanceLoadPlan;
- sInstanceLoadPlan = null;
- return plan;
- }
-
- /**** OnAnimationStartedListener Implementation ****/
-
- @Override
- public void onAnimationStarted() {
- // Notify recents to start the enter animation
- if (!mStartAnimationTriggered) {
- // There can be a race condition between the start animation callback and
- // the start of the new activity (where we register the receiver that listens
- // to this broadcast, so we add our own receiver and if that gets called, then
- // we know the activity has not yet started and we can retry sending the broadcast.
- BroadcastReceiver fallbackReceiver = new BroadcastReceiver() {
- @Override
- public void onReceive(Context context, Intent intent) {
- if (getResultCode() == Activity.RESULT_OK) {
- mStartAnimationTriggered = true;
- return;
+ if (mSystemUserCallbacks != null) {
+ IRecentsNonSystemUserCallbacks callbacks =
+ mSystemUserCallbacks.getNonSystemUserRecentsForUser(currentUser);
+ if (callbacks != null) {
+ try {
+ callbacks.onConfigurationChanged();
+ } catch (RemoteException e) {
+ Log.e(TAG, "Callback failed", e);
}
-
- // Schedule for the broadcast to be sent again after some time
- mHandler.postDelayed(new Runnable() {
- @Override
- public void run() {
- onAnimationStarted();
- }
- }, 25);
+ } else {
+ Log.e(TAG, "No SystemUI callbacks found for user: " + currentUser);
}
- };
-
- // Send the broadcast to notify Recents that the animation has started
- Intent intent = createLocalBroadcastIntent(mContext, ACTION_START_ENTER_ANIMATION);
- mContext.sendOrderedBroadcastAsUser(intent, UserHandle.CURRENT, null,
- fallbackReceiver, null, Activity.RESULT_CANCELED, null, null);
+ }
}
}
+
+ /**
+ * Handle Recents activity visibility changed.
+ */
+ public final void onBusEvent(final RecentsVisibilityChangedEvent event) {
+ int processUser = event.systemServicesProxy.getProcessUser();
+ if (event.systemServicesProxy.isSystemUser(processUser)) {
+ mImpl.onVisibilityChanged(event.applicationContext, event.visible);
+ } else {
+ postToSystemUser(new Runnable() {
+ @Override
+ public void run() {
+ try {
+ mCallbacksToSystemUser.updateRecentsVisibility(event.visible);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Callback failed", e);
+ }
+ }
+ });
+ }
+ }
+
+ /**
+ * Handle screen pinning request.
+ */
+ public final void onBusEvent(final ScreenPinningRequestEvent event) {
+ int processUser = event.systemServicesProxy.getProcessUser();
+ if (event.systemServicesProxy.isSystemUser(processUser)) {
+ mImpl.onStartScreenPinning(event.applicationContext);
+ } else {
+ postToSystemUser(new Runnable() {
+ @Override
+ public void run() {
+ try {
+ mCallbacksToSystemUser.startScreenPinning();
+ } catch (RemoteException e) {
+ Log.e(TAG, "Callback failed", e);
+ }
+ }
+ });
+ }
+ }
+
+ /**
+ * Attempts to register with the system user.
+ */
+ private void registerWithSystemUser() {
+ final int processUser = mSystemServicesProxy.getProcessUser();
+ postToSystemUser(new Runnable() {
+ @Override
+ public void run() {
+ try {
+ mCallbacksToSystemUser.registerNonSystemUserCallbacks(mImpl, processUser);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Failed to register", e);
+ }
+ }
+ });
+ }
+
+ /**
+ * Runs the runnable in the system user's Recents context, connecting to the service if
+ * necessary.
+ */
+ private void postToSystemUser(final Runnable onConnectRunnable) {
+ mOnConnectRunnables.add(onConnectRunnable);
+ if (mCallbacksToSystemUser == null) {
+ Intent systemUserServiceIntent = new Intent();
+ systemUserServiceIntent.setClass(mContext, RecentsSystemUserService.class);
+ boolean bound = mContext.bindServiceAsUser(systemUserServiceIntent,
+ mServiceConnectionToSystemUser, Context.BIND_AUTO_CREATE, UserHandle.SYSTEM);
+ if (!bound) {
+ // Retry after a fixed duration
+ mHandler.postDelayed(new Runnable() {
+ @Override
+ public void run() {
+ registerWithSystemUser();
+ }
+ }, BIND_TO_SYSTEM_USER_RETRY_DELAY);
+ }
+ } else {
+ runAndFlushOnConnectRunnables();
+ }
+ }
+
+ /**
+ * Runs all the queued runnables after a service connection is made.
+ */
+ private void runAndFlushOnConnectRunnables() {
+ for (Runnable r : mOnConnectRunnables) {
+ r.run();
+ }
+ mOnConnectRunnables.clear();
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java
index e647c1f..fa1c887 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java
@@ -39,6 +39,8 @@
import com.android.systemui.R;
import com.android.systemui.recents.events.EventBus;
import com.android.systemui.recents.events.activity.AppWidgetProviderChangedEvent;
+import com.android.systemui.recents.events.component.RecentsVisibilityChangedEvent;
+import com.android.systemui.recents.events.component.ScreenPinningRequestEvent;
import com.android.systemui.recents.events.ui.DismissTaskEvent;
import com.android.systemui.recents.events.ui.ResizeTaskEvent;
import com.android.systemui.recents.events.ui.ShowApplicationInfoEvent;
@@ -127,20 +129,20 @@
@Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
- if (action.equals(Recents.ACTION_HIDE_RECENTS_ACTIVITY)) {
- if (intent.getBooleanExtra(Recents.EXTRA_TRIGGERED_FROM_ALT_TAB, false)) {
+ if (action.equals(RecentsImpl.ACTION_HIDE_RECENTS_ACTIVITY)) {
+ if (intent.getBooleanExtra(RecentsImpl.EXTRA_TRIGGERED_FROM_ALT_TAB, false)) {
// If we are hiding from releasing Alt-Tab, dismiss Recents to the focused app
dismissRecentsToFocusedTaskOrHome(false);
- } else if (intent.getBooleanExtra(Recents.EXTRA_TRIGGERED_FROM_HOME_KEY, false)) {
+ } else if (intent.getBooleanExtra(RecentsImpl.EXTRA_TRIGGERED_FROM_HOME_KEY, false)) {
// Otherwise, dismiss Recents to Home
dismissRecentsToHome(true);
} else {
// Do nothing
}
- } else if (action.equals(Recents.ACTION_TOGGLE_RECENTS_ACTIVITY)) {
+ } else if (action.equals(RecentsImpl.ACTION_TOGGLE_RECENTS_ACTIVITY)) {
// If we are toggling Recents, then first unfilter any filtered stacks first
dismissRecentsToFocusedTaskOrHome(true);
- } else if (action.equals(Recents.ACTION_START_ENTER_ANIMATION)) {
+ } else if (action.equals(RecentsImpl.ACTION_START_ENTER_ANIMATION)) {
// Trigger the enter animation
onEnterAnimationTriggered();
// Notify the fallback receiver that we have successfully got the broadcast
@@ -174,7 +176,7 @@
// If AlternateRecentsComponent has preloaded a load plan, then use that to prevent
// reconstructing the task stack
RecentsTaskLoader loader = RecentsTaskLoader.getInstance();
- RecentsTaskLoadPlan plan = Recents.consumeInstanceLoadPlan();
+ RecentsTaskLoadPlan plan = RecentsImpl.consumeInstanceLoadPlan();
if (plan == null) {
plan = loader.createLoadPlan(this);
}
@@ -371,13 +373,15 @@
MetricsLogger.visible(this, MetricsLogger.OVERVIEW_ACTIVITY);
RecentsTaskLoader loader = RecentsTaskLoader.getInstance();
SystemServicesProxy ssp = loader.getSystemServicesProxy();
- Recents.notifyVisibilityChanged(this, ssp, true);
+
+ // Notify that recents is now visible
+ EventBus.getDefault().send(new RecentsVisibilityChangedEvent(this, ssp, true));
// Register the broadcast receiver to handle messages from our service
IntentFilter filter = new IntentFilter();
- filter.addAction(Recents.ACTION_HIDE_RECENTS_ACTIVITY);
- filter.addAction(Recents.ACTION_TOGGLE_RECENTS_ACTIVITY);
- filter.addAction(Recents.ACTION_START_ENTER_ANIMATION);
+ filter.addAction(RecentsImpl.ACTION_HIDE_RECENTS_ACTIVITY);
+ filter.addAction(RecentsImpl.ACTION_TOGGLE_RECENTS_ACTIVITY);
+ filter.addAction(RecentsImpl.ACTION_START_ENTER_ANIMATION);
registerReceiver(mServiceBroadcastReceiver, filter);
// Register any broadcast receivers for the task loader
@@ -415,7 +419,9 @@
MetricsLogger.hidden(this, MetricsLogger.OVERVIEW_ACTIVITY);
RecentsTaskLoader loader = RecentsTaskLoader.getInstance();
SystemServicesProxy ssp = loader.getSystemServicesProxy();
- Recents.notifyVisibilityChanged(this, ssp, false);
+
+ // Notify that recents is now hidden
+ EventBus.getDefault().send(new RecentsVisibilityChangedEvent(this, ssp, false));
// Notify the views that we are no longer visible
mRecentsView.onRecentsHidden();
@@ -564,7 +570,7 @@
public void onScreenPinningRequest() {
RecentsTaskLoader loader = RecentsTaskLoader.getInstance();
SystemServicesProxy ssp = loader.getSystemServicesProxy();
- Recents.startScreenPinning(this, ssp);
+ EventBus.getDefault().send(new ScreenPinningRequestEvent(this, ssp));
MetricsLogger.count(this, "overview_screen_pinned", 1);
}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java
new file mode 100644
index 0000000..2c76087
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java
@@ -0,0 +1,769 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.Activity;
+import android.app.ActivityManager;
+import android.app.ActivityOptions;
+import android.app.ITaskStackListener;
+import android.content.ActivityNotFoundException;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.res.Resources;
+import android.graphics.Bitmap;
+import android.graphics.Canvas;
+import android.graphics.Rect;
+import android.os.AsyncTask;
+import android.os.Handler;
+import android.os.SystemClock;
+import android.os.UserHandle;
+import android.util.MutableBoolean;
+import android.view.LayoutInflater;
+import android.view.View;
+import com.android.internal.logging.MetricsLogger;
+import com.android.systemui.Prefs;
+import com.android.systemui.R;
+import com.android.systemui.SystemUIApplication;
+import com.android.systemui.recents.events.component.RecentsVisibilityChangedEvent;
+import com.android.systemui.recents.events.component.ScreenPinningRequestEvent;
+import com.android.systemui.recents.misc.Console;
+import com.android.systemui.recents.misc.SystemServicesProxy;
+import com.android.systemui.recents.model.RecentsTaskLoadPlan;
+import com.android.systemui.recents.model.RecentsTaskLoader;
+import com.android.systemui.recents.model.Task;
+import com.android.systemui.recents.model.TaskGrouping;
+import com.android.systemui.recents.model.TaskStack;
+import com.android.systemui.recents.views.TaskStackView;
+import com.android.systemui.recents.views.TaskStackViewLayoutAlgorithm;
+import com.android.systemui.recents.views.TaskViewHeader;
+import com.android.systemui.recents.views.TaskViewTransform;
+import com.android.systemui.statusbar.phone.PhoneStatusBar;
+
+import java.util.ArrayList;
+
+/**
+ * An implementation of the Recents component for the current user. For secondary users, this can
+ * be called remotely from the system user.
+ */
+public class RecentsImpl extends IRecentsNonSystemUserCallbacks.Stub
+ implements ActivityOptions.OnAnimationStartedListener {
+
+ private final static String TAG = "RecentsImpl";
+
+ final public static String EXTRA_TRIGGERED_FROM_ALT_TAB = "triggeredFromAltTab";
+ final public static String EXTRA_TRIGGERED_FROM_HOME_KEY = "triggeredFromHomeKey";
+
+ final public static String ACTION_START_ENTER_ANIMATION = "action_start_enter_animation";
+ final public static String ACTION_TOGGLE_RECENTS_ACTIVITY = "action_toggle_recents_activity";
+ final public static String ACTION_HIDE_RECENTS_ACTIVITY = "action_hide_recents_activity";
+
+ final static int sMinToggleDelay = 350;
+
+ public final static String RECENTS_PACKAGE = "com.android.systemui";
+ public final static String RECENTS_ACTIVITY = "com.android.systemui.recents.RecentsActivity";
+
+
+ /**
+ * An implementation of ITaskStackListener, that allows us to listen for changes to the system
+ * task stacks and update recents accordingly.
+ */
+ class TaskStackListenerImpl extends ITaskStackListener.Stub implements Runnable {
+ Handler mHandler;
+
+ public TaskStackListenerImpl(Handler handler) {
+ mHandler = handler;
+ }
+
+ @Override
+ public void onTaskStackChanged() {
+ // Debounce any task stack changes
+ mHandler.removeCallbacks(this);
+ mHandler.post(this);
+ }
+
+ /** Preloads the next task */
+ public void run() {
+ // TODO: Temporarily skip this if multi stack is enabled
+ /*
+ RecentsConfiguration config = RecentsConfiguration.getInstance();
+ if (config.svelteLevel == RecentsConfiguration.SVELTE_NONE) {
+ RecentsTaskLoader loader = RecentsTaskLoader.getInstance();
+ SystemServicesProxy ssp = loader.getSystemServicesProxy();
+ ActivityManager.RunningTaskInfo runningTaskInfo = ssp.getTopMostTask();
+
+ // Load the next task only if we aren't svelte
+ RecentsTaskLoadPlan plan = loader.createLoadPlan(mContext);
+ loader.preloadTasks(plan, true);
+ RecentsTaskLoadPlan.Options launchOpts = new RecentsTaskLoadPlan.Options();
+ // This callback is made when a new activity is launched and the old one is paused
+ // so ignore the current activity and try and preload the thumbnail for the
+ // previous one.
+ if (runningTaskInfo != null) {
+ launchOpts.runningTaskId = runningTaskInfo.id;
+ }
+ launchOpts.numVisibleTasks = 2;
+ launchOpts.numVisibleTaskThumbnails = 2;
+ launchOpts.onlyLoadForCache = true;
+ launchOpts.onlyLoadPausedActivities = true;
+ loader.loadTasks(mContext, plan, launchOpts);
+ }
+ */
+ }
+ }
+
+ static RecentsTaskLoadPlan sInstanceLoadPlan;
+
+ Context mContext;
+ SystemServicesProxy mSystemServicesProxy;
+ Handler mHandler;
+ TaskStackListenerImpl mTaskStackListener;
+ RecentsAppWidgetHost mAppWidgetHost;
+ boolean mBootCompleted;
+ boolean mStartAnimationTriggered;
+ boolean mCanReuseTaskStackViews = true;
+
+ // Task launching
+ RecentsConfiguration mConfig;
+ Rect mSearchBarBounds = new Rect();
+ Rect mTaskStackBounds = new Rect();
+ Rect mLastTaskViewBounds = new Rect();
+ TaskViewTransform mTmpTransform = new TaskViewTransform();
+ int mStatusBarHeight;
+ int mNavBarHeight;
+ int mNavBarWidth;
+ int mTaskBarHeight;
+
+ // Header (for transition)
+ TaskViewHeader mHeaderBar;
+ final Object mHeaderBarLock = new Object();
+ TaskStackView mDummyStackView;
+
+ // Variables to keep track of if we need to start recents after binding
+ boolean mTriggeredFromAltTab;
+ long mLastToggleTime;
+
+ Bitmap mThumbnailTransitionBitmapCache;
+ Task mThumbnailTransitionBitmapCacheKey;
+
+
+ public RecentsImpl(Context context) {
+ mContext = context;
+ mSystemServicesProxy = new SystemServicesProxy(mContext);
+ mHandler = new Handler();
+ mAppWidgetHost = new RecentsAppWidgetHost(mContext, Constants.Values.App.AppWidgetHostId);
+ Resources res = mContext.getResources();
+ RecentsTaskLoader.initialize(mContext);
+ LayoutInflater inflater = LayoutInflater.from(mContext);
+
+ // Register the task stack listener
+ mTaskStackListener = new TaskStackListenerImpl(mHandler);
+ mSystemServicesProxy.registerTaskStackListener(mTaskStackListener);
+
+ // Initialize the static configuration resources
+ mConfig = RecentsConfiguration.initialize(mContext, mSystemServicesProxy);
+ mStatusBarHeight = res.getDimensionPixelSize(com.android.internal.R.dimen.status_bar_height);
+ mNavBarHeight = res.getDimensionPixelSize(com.android.internal.R.dimen.navigation_bar_height);
+ mNavBarWidth = res.getDimensionPixelSize(com.android.internal.R.dimen.navigation_bar_width);
+ mTaskBarHeight = res.getDimensionPixelSize(R.dimen.recents_task_bar_height);
+ mDummyStackView = new TaskStackView(mContext, new TaskStack());
+ mHeaderBar = (TaskViewHeader) inflater.inflate(R.layout.recents_task_view_header,
+ null, false);
+ reloadHeaderBarLayout(true /* tryAndBindSearchWidget */);
+
+ // When we start, preload the data associated with the previous recent tasks.
+ // We can use a new plan since the caches will be the same.
+ RecentsTaskLoader loader = RecentsTaskLoader.getInstance();
+ RecentsTaskLoadPlan plan = loader.createLoadPlan(mContext);
+ loader.preloadTasks(plan, true /* isTopTaskHome */);
+ RecentsTaskLoadPlan.Options launchOpts = new RecentsTaskLoadPlan.Options();
+ launchOpts.numVisibleTasks = loader.getApplicationIconCacheSize();
+ launchOpts.numVisibleTaskThumbnails = loader.getThumbnailCacheSize();
+ launchOpts.onlyLoadForCache = true;
+ loader.loadTasks(mContext, plan, launchOpts);
+ }
+
+ public void onBootCompleted() {
+ mBootCompleted = true;
+ reloadHeaderBarLayout(true /* tryAndBindSearchWidget */);
+ }
+
+ @Override
+ public void onConfigurationChanged() {
+ // Don't reuse task stack views if the configuration changes
+ mCanReuseTaskStackViews = false;
+ mConfig.updateOnConfigurationChange();
+ }
+
+ /**
+ * This is only called from the system user's Recents. Secondary users will instead proxy their
+ * visibility change events through to the system user via
+ * {@link Recents#onBusEvent(RecentsVisibilityChangedEvent)}.
+ */
+ public void onVisibilityChanged(Context context, boolean visible) {
+ SystemUIApplication app = (SystemUIApplication) context;
+ PhoneStatusBar statusBar = app.getComponent(PhoneStatusBar.class);
+ if (statusBar != null) {
+ statusBar.updateRecentsVisibility(visible);
+ }
+ }
+
+ /**
+ * This is only called from the system user's Recents. Secondary users will instead proxy their
+ * visibility change events through to the system user via
+ * {@link Recents#onBusEvent(ScreenPinningRequestEvent)}.
+ */
+ public void onStartScreenPinning(Context context) {
+ SystemUIApplication app = (SystemUIApplication) context;
+ PhoneStatusBar statusBar = app.getComponent(PhoneStatusBar.class);
+ if (statusBar != null) {
+ statusBar.showScreenPinningRequest(false);
+ }
+ }
+
+ @Override
+ public void showRecents(boolean triggeredFromAltTab) {
+ mTriggeredFromAltTab = triggeredFromAltTab;
+
+ try {
+ // Check if the top task is in the home stack, and start the recents activity
+ ActivityManager.RunningTaskInfo topTask = mSystemServicesProxy.getTopMostTask();
+ MutableBoolean isTopTaskHome = new MutableBoolean(true);
+ if (topTask == null || !mSystemServicesProxy.isRecentsTopMost(topTask, isTopTaskHome)) {
+ startRecentsActivity(topTask, isTopTaskHome.value);
+ }
+ } catch (ActivityNotFoundException e) {
+ Console.logRawError("Failed to launch RecentAppsIntent", e);
+ }
+ }
+
+ @Override
+ public void hideRecents(boolean triggeredFromAltTab, boolean triggeredFromHomeKey) {
+ if (mBootCompleted) {
+ // Defer to the activity to handle hiding recents, if it handles it, then it must still
+ // be visible
+ Intent intent = createLocalBroadcastIntent(mContext, ACTION_HIDE_RECENTS_ACTIVITY);
+ intent.putExtra(EXTRA_TRIGGERED_FROM_ALT_TAB, triggeredFromAltTab);
+ intent.putExtra(EXTRA_TRIGGERED_FROM_HOME_KEY, triggeredFromHomeKey);
+ mContext.sendBroadcastAsUser(intent, UserHandle.CURRENT);
+ }
+ }
+
+ @Override
+ public void toggleRecents() {
+ mTriggeredFromAltTab = false;
+
+ try {
+ // If the user has toggled it too quickly, then just eat up the event here (it's better
+ // than showing a janky screenshot).
+ // NOTE: Ideally, the screenshot mechanism would take the window transform into account
+ if ((SystemClock.elapsedRealtime() - mLastToggleTime) < sMinToggleDelay) {
+ return;
+ }
+
+ // If Recents is the front most activity, then we should just communicate with it
+ // directly to launch the first task or dismiss itself
+ ActivityManager.RunningTaskInfo topTask = mSystemServicesProxy.getTopMostTask();
+ MutableBoolean isTopTaskHome = new MutableBoolean(true);
+ if (topTask != null && mSystemServicesProxy.isRecentsTopMost(topTask, isTopTaskHome)) {
+ // Notify recents to toggle itself
+ Intent intent = createLocalBroadcastIntent(mContext, ACTION_TOGGLE_RECENTS_ACTIVITY);
+ mContext.sendBroadcastAsUser(intent, UserHandle.CURRENT);
+ mLastToggleTime = SystemClock.elapsedRealtime();
+ return;
+ } else {
+ // Otherwise, start the recents activity
+ startRecentsActivity(topTask, isTopTaskHome.value);
+ }
+ } catch (ActivityNotFoundException e) {
+ Console.logRawError("Failed to launch RecentAppsIntent", e);
+ }
+ }
+
+ @Override
+ public void preloadRecents() {
+ // Preload only the raw task list into a new load plan (which will be consumed by the
+ // RecentsActivity) only if there is a task to animate to.
+ ActivityManager.RunningTaskInfo topTask = mSystemServicesProxy.getTopMostTask();
+ MutableBoolean topTaskHome = new MutableBoolean(true);
+ RecentsTaskLoader loader = RecentsTaskLoader.getInstance();
+ sInstanceLoadPlan = loader.createLoadPlan(mContext);
+ if (topTask != null && !mSystemServicesProxy.isRecentsTopMost(topTask, topTaskHome)) {
+ sInstanceLoadPlan.preloadRawTasks(topTaskHome.value);
+ loader.preloadTasks(sInstanceLoadPlan, topTaskHome.value);
+ TaskStack stack = sInstanceLoadPlan.getTaskStack();
+ if (stack.getTaskCount() > 0) {
+ preCacheThumbnailTransitionBitmapAsync(topTask, stack, mDummyStackView);
+ }
+ }
+ }
+
+ @Override
+ public void cancelPreloadingRecents() {
+ // Do nothing
+ }
+
+ public void showRelativeAffiliatedTask(boolean showNextTask) {
+ // Return early if there is no focused stack
+ int focusedStackId = mSystemServicesProxy.getFocusedStack();
+ RecentsTaskLoader loader = RecentsTaskLoader.getInstance();
+ RecentsTaskLoadPlan plan = loader.createLoadPlan(mContext);
+ loader.preloadTasks(plan, true /* isTopTaskHome */);
+ TaskStack focusedStack = plan.getTaskStack();
+
+ // Return early if there are no tasks in the focused stack
+ if (focusedStack == null || focusedStack.getTaskCount() == 0) return;
+
+ ActivityManager.RunningTaskInfo runningTask = mSystemServicesProxy.getTopMostTask();
+ // Return early if there is no running task (can't determine affiliated tasks in this case)
+ if (runningTask == null) return;
+ // Return early if the running task is in the home stack (optimization)
+ if (mSystemServicesProxy.isInHomeStack(runningTask.id)) return;
+
+ // Find the task in the recents list
+ ArrayList<Task> tasks = focusedStack.getTasks();
+ Task toTask = null;
+ ActivityOptions launchOpts = null;
+ int taskCount = tasks.size();
+ int numAffiliatedTasks = 0;
+ for (int i = 0; i < taskCount; i++) {
+ Task task = tasks.get(i);
+ if (task.key.id == runningTask.id) {
+ TaskGrouping group = task.group;
+ Task.TaskKey toTaskKey;
+ if (showNextTask) {
+ toTaskKey = group.getNextTaskInGroup(task);
+ launchOpts = ActivityOptions.makeCustomAnimation(mContext,
+ R.anim.recents_launch_next_affiliated_task_target,
+ R.anim.recents_launch_next_affiliated_task_source);
+ } else {
+ toTaskKey = group.getPrevTaskInGroup(task);
+ launchOpts = ActivityOptions.makeCustomAnimation(mContext,
+ R.anim.recents_launch_prev_affiliated_task_target,
+ R.anim.recents_launch_prev_affiliated_task_source);
+ }
+ if (toTaskKey != null) {
+ toTask = focusedStack.findTaskWithId(toTaskKey.id);
+ }
+ numAffiliatedTasks = group.getTaskCount();
+ break;
+ }
+ }
+
+ // Return early if there is no next task
+ if (toTask == null) {
+ if (numAffiliatedTasks > 1) {
+ if (showNextTask) {
+ mSystemServicesProxy.startInPlaceAnimationOnFrontMostApplication(
+ ActivityOptions.makeCustomInPlaceAnimation(mContext,
+ R.anim.recents_launch_next_affiliated_task_bounce));
+ } else {
+ mSystemServicesProxy.startInPlaceAnimationOnFrontMostApplication(
+ ActivityOptions.makeCustomInPlaceAnimation(mContext,
+ R.anim.recents_launch_prev_affiliated_task_bounce));
+ }
+ }
+ return;
+ }
+
+ // Keep track of actually launched affiliated tasks
+ MetricsLogger.count(mContext, "overview_affiliated_task_launch", 1);
+
+ // Launch the task
+ if (toTask.isActive) {
+ // Bring an active task to the foreground
+ mSystemServicesProxy.moveTaskToFront(toTask.key.id, launchOpts);
+ } else {
+ mSystemServicesProxy.startActivityFromRecents(mContext, toTask.key.id,
+ toTask.activityLabel, launchOpts);
+ }
+ }
+
+ public void showNextAffiliatedTask() {
+ // Keep track of when the affiliated task is triggered
+ MetricsLogger.count(mContext, "overview_affiliated_task_next", 1);
+ showRelativeAffiliatedTask(true);
+ }
+
+ public void showPrevAffiliatedTask() {
+ // Keep track of when the affiliated task is triggered
+ MetricsLogger.count(mContext, "overview_affiliated_task_prev", 1);
+ showRelativeAffiliatedTask(false);
+ }
+
+ /**
+ * Returns the preloaded load plan and invalidates it.
+ */
+ public static RecentsTaskLoadPlan consumeInstanceLoadPlan() {
+ RecentsTaskLoadPlan plan = sInstanceLoadPlan;
+ sInstanceLoadPlan = null;
+ return plan;
+ }
+
+ /**
+ * Prepares the header bar layout for the next transition, if the task view bounds has changed
+ * since the last call, it will attempt to re-measure and layout the header bar to the new size.
+ *
+ * @param tryAndBindSearchWidget if set, will attempt to fetch and bind the search widget if one
+ * is not already bound (can be expensive)
+ */
+ private void reloadHeaderBarLayout(boolean tryAndBindSearchWidget) {
+ Rect windowRect = mSystemServicesProxy.getWindowRect();
+
+ // Update the configuration for the current state
+ mConfig.update(mContext, mSystemServicesProxy, mSystemServicesProxy.getWindowRect());
+
+ if (tryAndBindSearchWidget) {
+ // Try and pre-emptively bind the search widget on startup to ensure that we
+ // have the right thumbnail bounds to animate to.
+ // Note: We have to reload the widget id before we get the task stack bounds below
+ if (mSystemServicesProxy.getOrBindSearchAppWidget(mContext, mAppWidgetHost) != null) {
+ mConfig.getSearchBarBounds(windowRect,
+ mStatusBarHeight, mSearchBarBounds);
+ }
+ }
+ Rect systemInsets = new Rect(0, mStatusBarHeight,
+ (mConfig.hasTransposedNavBar ? mNavBarWidth : 0),
+ (mConfig.hasTransposedNavBar ? 0 : mNavBarHeight));
+ mConfig.getTaskStackBounds(windowRect, systemInsets.top, systemInsets.right,
+ mSearchBarBounds, mTaskStackBounds);
+
+ // Rebind the header bar and draw it for the transition
+ TaskStackViewLayoutAlgorithm algo = mDummyStackView.getStackAlgorithm();
+ Rect taskStackBounds = new Rect(mTaskStackBounds);
+ algo.setSystemInsets(systemInsets);
+ algo.computeRects(windowRect.width(), windowRect.height(), taskStackBounds);
+ Rect taskViewBounds = algo.getUntransformedTaskViewBounds();
+ if (!taskViewBounds.equals(mLastTaskViewBounds)) {
+ mLastTaskViewBounds.set(taskViewBounds);
+
+ int taskViewWidth = taskViewBounds.width();
+ synchronized (mHeaderBarLock) {
+ mHeaderBar.measure(
+ View.MeasureSpec.makeMeasureSpec(taskViewWidth, View.MeasureSpec.EXACTLY),
+ View.MeasureSpec.makeMeasureSpec(mTaskBarHeight, View.MeasureSpec.EXACTLY));
+ mHeaderBar.layout(0, 0, taskViewWidth, mTaskBarHeight);
+ }
+ }
+ }
+
+ /**
+ * Preloads the icon of a task.
+ */
+ private void preloadIcon(ActivityManager.RunningTaskInfo task) {
+
+ // Ensure that we load the running task's icon
+ RecentsTaskLoadPlan.Options launchOpts = new RecentsTaskLoadPlan.Options();
+ launchOpts.runningTaskId = task.id;
+ launchOpts.loadThumbnails = false;
+ launchOpts.onlyLoadForCache = true;
+ RecentsTaskLoader.getInstance().loadTasks(mContext, sInstanceLoadPlan, launchOpts);
+ }
+
+ /**
+ * Caches the header thumbnail used for a window animation asynchronously into
+ * {@link #mThumbnailTransitionBitmapCache}.
+ */
+ private void preCacheThumbnailTransitionBitmapAsync(ActivityManager.RunningTaskInfo topTask,
+ TaskStack stack, TaskStackView stackView) {
+ preloadIcon(topTask);
+
+ // Update the header bar if necessary
+ reloadHeaderBarLayout(false /* tryAndBindSearchWidget */);
+
+ // Update the destination rect
+ mDummyStackView.updateMinMaxScrollForStack(stack);
+ final Task toTask = new Task();
+ final TaskViewTransform toTransform = getThumbnailTransitionTransform(stack, stackView,
+ topTask.id, toTask);
+ new AsyncTask<Void, Void, Bitmap>() {
+ @Override
+ protected Bitmap doInBackground(Void... params) {
+ return drawThumbnailTransitionBitmap(toTask, toTransform);
+ }
+
+ @Override
+ protected void onPostExecute(Bitmap bitmap) {
+ mThumbnailTransitionBitmapCache = bitmap;
+ mThumbnailTransitionBitmapCacheKey = toTask;
+ }
+ }.execute();
+ }
+
+ /**
+ * Creates the activity options for a unknown state->recents transition.
+ */
+ private ActivityOptions getUnknownTransitionActivityOptions() {
+ mStartAnimationTriggered = false;
+ return ActivityOptions.makeCustomAnimation(mContext,
+ R.anim.recents_from_unknown_enter,
+ R.anim.recents_from_unknown_exit,
+ mHandler, this);
+ }
+
+ /**
+ * Creates the activity options for a home->recents transition.
+ */
+ private ActivityOptions getHomeTransitionActivityOptions(boolean fromSearchHome) {
+ mStartAnimationTriggered = false;
+ if (fromSearchHome) {
+ return ActivityOptions.makeCustomAnimation(mContext,
+ R.anim.recents_from_search_launcher_enter,
+ R.anim.recents_from_search_launcher_exit,
+ mHandler, this);
+ }
+ return ActivityOptions.makeCustomAnimation(mContext,
+ R.anim.recents_from_launcher_enter,
+ R.anim.recents_from_launcher_exit,
+ mHandler, this);
+ }
+
+ /**
+ * Creates the activity options for an app->recents transition.
+ */
+ private ActivityOptions getThumbnailTransitionActivityOptions(
+ ActivityManager.RunningTaskInfo topTask, TaskStack stack, TaskStackView stackView) {
+
+ // Update the destination rect
+ Task toTask = new Task();
+ TaskViewTransform toTransform = getThumbnailTransitionTransform(stack, stackView,
+ topTask.id, toTask);
+ Rect toTaskRect = toTransform.rect;
+ Bitmap thumbnail;
+ if (mThumbnailTransitionBitmapCacheKey != null
+ && mThumbnailTransitionBitmapCacheKey.key != null
+ && mThumbnailTransitionBitmapCacheKey.key.equals(toTask.key)) {
+ thumbnail = mThumbnailTransitionBitmapCache;
+ mThumbnailTransitionBitmapCacheKey = null;
+ mThumbnailTransitionBitmapCache = null;
+ } else {
+ preloadIcon(topTask);
+ thumbnail = drawThumbnailTransitionBitmap(toTask, toTransform);
+ }
+ if (thumbnail != null) {
+ mStartAnimationTriggered = false;
+ return ActivityOptions.makeThumbnailAspectScaleDownAnimation(mDummyStackView,
+ thumbnail, toTaskRect.left, toTaskRect.top, toTaskRect.width(),
+ toTaskRect.height(), mHandler, this);
+ }
+
+ // If both the screenshot and thumbnail fails, then just fall back to the default transition
+ return getUnknownTransitionActivityOptions();
+ }
+
+ /**
+ * Returns the transition rect for the given task id.
+ */
+ private TaskViewTransform getThumbnailTransitionTransform(TaskStack stack,
+ TaskStackView stackView, int runningTaskId, Task runningTaskOut) {
+ // Find the running task in the TaskStack
+ Task task = null;
+ ArrayList<Task> tasks = stack.getTasks();
+ if (runningTaskId != -1) {
+ // Otherwise, try and find the task with the
+ int taskCount = tasks.size();
+ for (int i = taskCount - 1; i >= 0; i--) {
+ Task t = tasks.get(i);
+ if (t.key.id == runningTaskId) {
+ task = t;
+ runningTaskOut.copyFrom(t);
+ break;
+ }
+ }
+ }
+ if (task == null) {
+ // If no task is specified or we can not find the task just use the front most one
+ task = tasks.get(tasks.size() - 1);
+ runningTaskOut.copyFrom(task);
+ }
+
+ // Get the transform for the running task
+ stackView.getScroller().setStackScrollToInitialState();
+ mTmpTransform = stackView.getStackAlgorithm().getStackTransform(task,
+ stackView.getScroller().getStackScroll(), mTmpTransform, null);
+ return mTmpTransform;
+ }
+
+ /**
+ * Draws the header of a task used for the window animation into a bitmap.
+ */
+ private Bitmap drawThumbnailTransitionBitmap(Task toTask, TaskViewTransform toTransform) {
+ if (toTransform != null && toTask.key != null) {
+ Bitmap thumbnail;
+ synchronized (mHeaderBarLock) {
+ int toHeaderWidth = (int) (mHeaderBar.getMeasuredWidth() * toTransform.scale);
+ int toHeaderHeight = (int) (mHeaderBar.getMeasuredHeight() * toTransform.scale);
+ thumbnail = Bitmap.createBitmap(toHeaderWidth, toHeaderHeight,
+ Bitmap.Config.ARGB_8888);
+ if (Constants.DebugFlags.App.EnableTransitionThumbnailDebugMode) {
+ thumbnail.eraseColor(0xFFff0000);
+ } else {
+ Canvas c = new Canvas(thumbnail);
+ c.scale(toTransform.scale, toTransform.scale);
+ mHeaderBar.rebindToTask(toTask);
+ mHeaderBar.draw(c);
+ c.setBitmap(null);
+ }
+ }
+ return thumbnail.createAshmemBitmap();
+ }
+ return null;
+ }
+
+ /**
+ * Shows the recents activity
+ */
+ private void startRecentsActivity(ActivityManager.RunningTaskInfo topTask,
+ boolean isTopTaskHome) {
+ RecentsTaskLoader loader = RecentsTaskLoader.getInstance();
+
+ // Update the header bar if necessary
+ reloadHeaderBarLayout(false /* tryAndBindSearchWidget */);
+
+ if (sInstanceLoadPlan == null) {
+ // Create a new load plan if onPreloadRecents() was never triggered
+ sInstanceLoadPlan = loader.createLoadPlan(mContext);
+ }
+
+ if (!sInstanceLoadPlan.hasTasks()) {
+ loader.preloadTasks(sInstanceLoadPlan, isTopTaskHome);
+ }
+ TaskStack stack = sInstanceLoadPlan.getTaskStack();
+
+ // Prepare the dummy stack for the transition
+ mDummyStackView.updateMinMaxScrollForStack(stack);
+ TaskStackViewLayoutAlgorithm.VisibilityReport stackVr =
+ mDummyStackView.computeStackVisibilityReport();
+ boolean hasRecentTasks = stack.getTaskCount() > 0;
+ boolean useThumbnailTransition = (topTask != null) && !isTopTaskHome && hasRecentTasks;
+
+ if (useThumbnailTransition) {
+ // Try starting with a thumbnail transition
+ ActivityOptions opts = getThumbnailTransitionActivityOptions(topTask, stack,
+ mDummyStackView);
+ if (opts != null) {
+ startRecentsActivity(topTask, opts, false /* fromHome */,
+ false /* fromSearchHome */, true /* fromThumbnail */, stackVr);
+ } else {
+ // Fall through below to the non-thumbnail transition
+ useThumbnailTransition = false;
+ }
+ }
+
+ if (!useThumbnailTransition) {
+ // If there is no thumbnail transition, but is launching from home into recents, then
+ // use a quick home transition and do the animation from home
+ if (hasRecentTasks) {
+ String homeActivityPackage = mSystemServicesProxy.getHomeActivityPackageName();
+ String searchWidgetPackage =
+ Prefs.getString(mContext, Prefs.Key.SEARCH_APP_WIDGET_PACKAGE, null);
+
+ // Determine whether we are coming from a search owned home activity
+ boolean fromSearchHome = (homeActivityPackage != null) &&
+ homeActivityPackage.equals(searchWidgetPackage);
+ ActivityOptions opts = getHomeTransitionActivityOptions(fromSearchHome);
+ startRecentsActivity(topTask, opts, true /* fromHome */, fromSearchHome,
+ false /* fromThumbnail */, stackVr);
+ } else {
+ // Otherwise we do the normal fade from an unknown source
+ ActivityOptions opts = getUnknownTransitionActivityOptions();
+ startRecentsActivity(topTask, opts, true /* fromHome */,
+ false /* fromSearchHome */, false /* fromThumbnail */, stackVr);
+ }
+ }
+ mLastToggleTime = SystemClock.elapsedRealtime();
+ }
+
+ /**
+ * Starts the recents activity.
+ */
+ private void startRecentsActivity(ActivityManager.RunningTaskInfo topTask,
+ ActivityOptions opts, boolean fromHome, boolean fromSearchHome, boolean fromThumbnail,
+ TaskStackViewLayoutAlgorithm.VisibilityReport vr) {
+ // Update the configuration based on the launch options
+ RecentsActivityLaunchState launchState = mConfig.getLaunchState();
+ launchState.launchedFromHome = fromSearchHome || fromHome;
+ launchState.launchedFromSearchHome = fromSearchHome;
+ launchState.launchedFromAppWithThumbnail = fromThumbnail;
+ launchState.launchedToTaskId = (topTask != null) ? topTask.id : -1;
+ launchState.launchedWithAltTab = mTriggeredFromAltTab;
+ launchState.launchedReuseTaskStackViews = mCanReuseTaskStackViews;
+ launchState.launchedNumVisibleTasks = vr.numVisibleTasks;
+ launchState.launchedNumVisibleThumbnails = vr.numVisibleThumbnails;
+ launchState.launchedHasConfigurationChanged = false;
+
+ Intent intent = new Intent();
+ intent.setClassName(RECENTS_PACKAGE, RECENTS_ACTIVITY);
+ intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK
+ | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS
+ | Intent.FLAG_ACTIVITY_TASK_ON_HOME);
+ if (opts != null) {
+ mContext.startActivityAsUser(intent, opts.toBundle(), UserHandle.CURRENT);
+ } else {
+ mContext.startActivityAsUser(intent, UserHandle.CURRENT);
+ }
+ mCanReuseTaskStackViews = true;
+ }
+
+ /**
+ * Creates a new broadcast intent to send to the Recents activity.
+ * TODO: Use EventBus
+ */
+ private Intent createLocalBroadcastIntent(Context context, String action) {
+ Intent intent = new Intent(action);
+ intent.setPackage(context.getPackageName());
+ intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT |
+ Intent.FLAG_RECEIVER_FOREGROUND);
+ return intent;
+ }
+
+ /**** OnAnimationStartedListener Implementation ****/
+
+ @Override
+ public void onAnimationStarted() {
+ // Notify recents to start the enter animation
+ // TODO: Use EventBus
+ if (!mStartAnimationTriggered) {
+ // There can be a race condition between the start animation callback and
+ // the start of the new activity (where we register the receiver that listens
+ // to this broadcast, so we add our own receiver and if that gets called, then
+ // we know the activity has not yet started and we can retry sending the broadcast.
+ BroadcastReceiver fallbackReceiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ if (getResultCode() == Activity.RESULT_OK) {
+ mStartAnimationTriggered = true;
+ return;
+ }
+
+ // Schedule for the broadcast to be sent again after some time
+ mHandler.postDelayed(new Runnable() {
+ @Override
+ public void run() {
+ onAnimationStarted();
+ }
+ }, 25);
+ }
+ };
+
+ // Send the broadcast to notify Recents that the animation has started
+ Intent intent = createLocalBroadcastIntent(mContext, ACTION_START_ENTER_ANIMATION);
+ mContext.sendOrderedBroadcastAsUser(intent, UserHandle.CURRENT, null,
+ fallbackReceiver, null, Activity.RESULT_CANCELED, null, null);
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsSystemUser.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsSystemUser.java
new file mode 100644
index 0000000..fb21500
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsSystemUser.java
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.recents;
+
+import android.content.Context;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.util.Log;
+import android.util.SparseArray;
+
+/**
+ * An implementation of the system user's Recents interface to be called remotely by secondary
+ * users.
+ */
+public class RecentsSystemUser extends IRecentsSystemUserCallbacks.Stub {
+
+ private static final String TAG = "RecentsSystemUser";
+
+ private Context mContext;
+ private RecentsImpl mImpl;
+ private final SparseArray<IRecentsNonSystemUserCallbacks> mNonSystemUserRecents =
+ new SparseArray<>();
+
+ public RecentsSystemUser(Context context, RecentsImpl impl) {
+ mContext = context;
+ mImpl = impl;
+ }
+
+ @Override
+ public void registerNonSystemUserCallbacks(final IBinder nonSystemUserCallbacks, int userId) {
+ try {
+ final IRecentsNonSystemUserCallbacks callback =
+ IRecentsNonSystemUserCallbacks.Stub.asInterface(nonSystemUserCallbacks);
+ nonSystemUserCallbacks.linkToDeath(new IBinder.DeathRecipient() {
+ @Override
+ public void binderDied() {
+ mNonSystemUserRecents.removeAt(mNonSystemUserRecents.indexOfValue(callback));
+ }
+ }, 0);
+ mNonSystemUserRecents.put(userId, callback);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Failed to register NonSystemUserCallbacks", e);
+ }
+ }
+
+ public IRecentsNonSystemUserCallbacks getNonSystemUserRecentsForUser(int userId) {
+ return mNonSystemUserRecents.get(userId);
+ }
+
+ @Override
+ public void updateRecentsVisibility(boolean visible) {
+ mImpl.onVisibilityChanged(mContext, visible);
+ }
+
+ @Override
+ public void startScreenPinning() {
+ mImpl.onStartScreenPinning(mContext);
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsSystemUserService.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsSystemUserService.java
new file mode 100644
index 0000000..39d0d59
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsSystemUserService.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.recents;
+
+import android.app.Service;
+import android.content.Intent;
+import android.os.IBinder;
+import android.util.Log;
+import com.android.systemui.SystemUIApplication;
+
+/**
+ * A strictly system-user service that is started by the secondary user's Recents (with a limited
+ * lifespan), to get the interface that the secondary user's Recents can call through to the system
+ * user's Recents.
+ */
+public class RecentsSystemUserService extends Service {
+
+ private static final String TAG = "RecentsSystemUserService";
+ private static final boolean DEBUG = false;
+
+ @Override
+ public void onCreate() {
+ super.onCreate();
+ }
+
+ @Override
+ public IBinder onBind(Intent intent) {
+ SystemUIApplication app = (SystemUIApplication) getApplication();
+ Recents recents = app.getComponent(Recents.class);
+ if (DEBUG) {
+ Log.d(TAG, "onBind: " + recents);
+ }
+ if (recents != null) {
+ return recents.getSystemUserCallbacks();
+ }
+ return null;
+ }
+}
+
diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsUserEventProxyReceiver.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsUserEventProxyReceiver.java
deleted file mode 100644
index 5eefbc7..0000000
--- a/packages/SystemUI/src/com/android/systemui/recents/RecentsUserEventProxyReceiver.java
+++ /dev/null
@@ -1,67 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.recents;
-
-import android.content.BroadcastReceiver;
-import android.content.Context;
-import android.content.Intent;
-
-/**
- * A proxy for Recents events which happens strictly for non-owner users.
- */
-public class RecentsUserEventProxyReceiver extends BroadcastReceiver {
- final public static String ACTION_PROXY_SHOW_RECENTS_TO_USER =
- "com.android.systemui.recents.action.SHOW_RECENTS_FOR_USER";
- final public static String ACTION_PROXY_HIDE_RECENTS_TO_USER =
- "com.android.systemui.recents.action.HIDE_RECENTS_FOR_USER";
- final public static String ACTION_PROXY_TOGGLE_RECENTS_TO_USER =
- "com.android.systemui.recents.action.TOGGLE_RECENTS_FOR_USER";
- final public static String ACTION_PROXY_PRELOAD_RECENTS_TO_USER =
- "com.android.systemui.recents.action.PRELOAD_RECENTS_FOR_USER";
- final public static String ACTION_PROXY_CONFIG_CHANGE_TO_USER =
- "com.android.systemui.recents.action.CONFIG_CHANGED_FOR_USER";
-
- @Override
- public void onReceive(Context context, Intent intent) {
- Recents recents = Recents.getInstanceAndStartIfNeeded(context);
- switch (intent.getAction()) {
- case ACTION_PROXY_SHOW_RECENTS_TO_USER: {
- boolean triggeredFromAltTab = intent.getBooleanExtra(
- Recents.EXTRA_TRIGGERED_FROM_ALT_TAB, false);
- recents.showRecentsInternal(triggeredFromAltTab);
- break;
- }
- case ACTION_PROXY_HIDE_RECENTS_TO_USER: {
- boolean triggeredFromAltTab = intent.getBooleanExtra(
- Recents.EXTRA_TRIGGERED_FROM_ALT_TAB, false);
- boolean triggeredFromHome = intent.getBooleanExtra(
- Recents.EXTRA_TRIGGERED_FROM_HOME_KEY, false);
- recents.hideRecentsInternal(triggeredFromAltTab, triggeredFromHome);
- break;
- }
- case ACTION_PROXY_TOGGLE_RECENTS_TO_USER:
- recents.toggleRecentsInternal();
- break;
- case ACTION_PROXY_PRELOAD_RECENTS_TO_USER:
- recents.preloadRecentsInternal();
- break;
- case ACTION_PROXY_CONFIG_CHANGE_TO_USER:
- recents.configurationChanged();
- break;
- }
- }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/events/component/RecentsVisibilityChangedEvent.java b/packages/SystemUI/src/com/android/systemui/recents/events/component/RecentsVisibilityChangedEvent.java
new file mode 100644
index 0000000..898d1fc
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/recents/events/component/RecentsVisibilityChangedEvent.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.recents.events.component;
+
+import android.content.Context;
+import com.android.systemui.recents.events.EventBus;
+import com.android.systemui.recents.misc.SystemServicesProxy;
+
+/**
+ * This is sent when the visibility of the RecentsActivity for the current user changes.
+ */
+public class RecentsVisibilityChangedEvent extends EventBus.Event {
+
+ public final Context applicationContext;
+ public final SystemServicesProxy systemServicesProxy;
+ public final boolean visible;
+
+ public RecentsVisibilityChangedEvent(Context context, SystemServicesProxy systemServicesProxy,
+ boolean visible) {
+ this.applicationContext = context.getApplicationContext();
+ this.systemServicesProxy = systemServicesProxy;
+ this.visible = visible;
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/events/component/ScreenPinningRequestEvent.java b/packages/SystemUI/src/com/android/systemui/recents/events/component/ScreenPinningRequestEvent.java
new file mode 100644
index 0000000..5cb4ccf
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/recents/events/component/ScreenPinningRequestEvent.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.recents.events.component;
+
+import android.content.Context;
+import com.android.systemui.recents.events.EventBus;
+import com.android.systemui.recents.misc.SystemServicesProxy;
+
+/**
+ * This is sent when we want to start screen pinning.
+ */
+public class ScreenPinningRequestEvent extends EventBus.Event {
+
+ public final Context applicationContext;
+ public final SystemServicesProxy systemServicesProxy;
+
+ public ScreenPinningRequestEvent(Context context, SystemServicesProxy systemServicesProxy) {
+ this.applicationContext = context.getApplicationContext();
+ this.systemServicesProxy = systemServicesProxy;
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java b/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java
index 568d2b1..dab2c65 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java
@@ -16,9 +16,6 @@
package com.android.systemui.recents.misc;
-import static android.app.ActivityManager.DOCKED_STACK_ID;
-import static android.app.ActivityManager.INVALID_STACK_ID;
-
import android.app.ActivityManager;
import android.app.ActivityManagerNative;
import android.app.ActivityOptions;
@@ -55,6 +52,7 @@
import android.os.RemoteException;
import android.os.SystemProperties;
import android.os.UserHandle;
+import android.os.UserManager;
import android.provider.Settings;
import android.util.Log;
import android.util.MutableBoolean;
@@ -66,7 +64,7 @@
import com.android.systemui.Prefs;
import com.android.systemui.R;
import com.android.systemui.recents.Constants;
-import com.android.systemui.recents.Recents;
+import com.android.systemui.recents.RecentsImpl;
import java.io.IOException;
import java.util.ArrayList;
@@ -74,6 +72,9 @@
import java.util.List;
import java.util.Random;
+import static android.app.ActivityManager.DOCKED_STACK_ID;
+import static android.app.ActivityManager.INVALID_STACK_ID;
+
/**
* Acts as a shim around the real system services that we need to access data from, and provides
* a point of injection when testing UI.
@@ -100,6 +101,7 @@
IPackageManager mIpm;
AssistUtils mAssistUtils;
WindowManager mWm;
+ UserManager mUm;
Display mDisplay;
String mRecentsPackage;
ComponentName mAssistComponent;
@@ -122,6 +124,7 @@
mIpm = AppGlobals.getPackageManager();
mAssistUtils = new AssistUtils(context);
mWm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
+ mUm = UserManager.get(context);
mDisplay = mWm.getDefaultDisplay();
mRecentsPackage = context.getPackageName();
mBgThreadHandler = new Handler(sBgThread.getLooper());
@@ -244,8 +247,8 @@
ComponentName topActivity = topTask.topActivity;
// Check if the front most activity is recents
- if (topActivity.getPackageName().equals(Recents.sRecentsPackage) &&
- topActivity.getClassName().equals(Recents.sRecentsActivity)) {
+ if (topActivity.getPackageName().equals(RecentsImpl.RECENTS_PACKAGE) &&
+ topActivity.getClassName().equals(RecentsImpl.RECENTS_ACTIVITY)) {
if (isHomeTopMost != null) {
isHomeTopMost.value = false;
}
@@ -552,12 +555,27 @@
}
/**
- * Returns whether the foreground user is the owner.
+ * Returns whether the provided {@param userId} represents the system user.
*/
- public boolean isForegroundUserSystem() {
- if (mAm == null) return false;
+ public boolean isSystemUser(int userId) {
+ return userId == UserHandle.USER_SYSTEM;
+ }
- return mAm.getCurrentUser() == UserHandle.USER_SYSTEM;
+ /**
+ * Returns the current user id.
+ */
+ public int getCurrentUser() {
+ if (mAm == null) return 0;
+
+ return mAm.getCurrentUser();
+ }
+
+ /**
+ * Returns the processes user id.
+ */
+ public int getProcessUser() {
+ if (mUm == null) return 0;
+ return mUm.getUserHandle();
}
/**
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewTouchHandler.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewTouchHandler.java
index 4322f1a..3673131 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewTouchHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewTouchHandler.java
@@ -395,8 +395,10 @@
// The user intentionally tapped on the background, which is like a tap on the "desktop".
// Hide recents and transition to the launcher.
+ /* TODO: Use EventBus for this later
Recents recents = Recents.getInstanceAndStartIfNeeded(mSv.getContext());
- recents.hideRecents(false /* altTab */, true /* homeKey */);
+ recents.hideRecents(false, true);
+ */
}
/** Handles generic motion events */
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java
index 540b9d0..fa9c4bb 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java
@@ -109,6 +109,14 @@
}
};
+ private Runnable mRemoveCastIconRunnable = new Runnable() {
+ @Override
+ public void run() {
+ if (DEBUG) Log.v(TAG, "updateCast: hiding icon NOW");
+ mService.setIconVisibility(SLOT_CAST, false);
+ }
+ };
+
public PhoneStatusBarPolicy(Context context, CastController cast, HotspotController hotspot,
UserInfoController userInfoController, BluetoothController bluetooth) {
mContext = context;
@@ -328,11 +336,17 @@
}
}
if (DEBUG) Log.v(TAG, "updateCast: isCasting: " + isCasting);
+ mHandler.removeCallbacks(mRemoveCastIconRunnable);
if (isCasting) {
mService.setIcon(SLOT_CAST, R.drawable.stat_sys_cast, 0,
mContext.getString(R.string.accessibility_casting));
+ mService.setIconVisibility(SLOT_CAST, true);
+ } else {
+ // don't turn off the screen-record icon for a few seconds, just to make sure the user
+ // has seen it
+ if (DEBUG) Log.v(TAG, "updateCast: hiding icon in 3 sec...");
+ mHandler.postDelayed(mRemoveCastIconRunnable, 3000);
}
- mService.setIconVisibility(SLOT_CAST, isCasting);
}
private void profileChanged(int userId) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickStatusBarHeader.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickStatusBarHeader.java
index 01f0667..662dbd9 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickStatusBarHeader.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickStatusBarHeader.java
@@ -20,7 +20,9 @@
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
+import android.graphics.Rect;
import android.graphics.drawable.Animatable;
+import android.graphics.drawable.RippleDrawable;
import android.util.AttributeSet;
import android.view.View;
import android.view.ViewGroup;
@@ -28,6 +30,7 @@
import android.widget.Switch;
import android.widget.TextView;
import android.widget.Toast;
+
import com.android.systemui.R;
import com.android.systemui.qs.QSPanel;
import com.android.systemui.qs.QSTile;
@@ -86,6 +89,20 @@
mQsDetailHeaderTitle = (TextView) mQsDetailHeader.findViewById(android.R.id.title);
mQsDetailHeaderSwitch = (Switch) mQsDetailHeader.findViewById(android.R.id.toggle);
mQsDetailHeaderProgress = (ImageView) findViewById(R.id.qs_detail_header_progress);
+
+ // RenderThread is doing more harm than good when touching the header (to expand quick
+ // settings), so disable it for this view
+ ((RippleDrawable) getBackground()).setForceSoftware(true);
+ ((RippleDrawable) mSettingsButton.getBackground()).setForceSoftware(true);
+
+ addOnLayoutChangeListener(new View.OnLayoutChangeListener() {
+ @Override
+ public void onLayoutChange(View v, int left, int top, int right,
+ int bottom, int oldLeft, int oldTop, int oldRight, int oldBottom) {
+ setClipBounds(new Rect(getPaddingLeft(), 0, getWidth() - getPaddingRight(),
+ getHeight()));
+ }
+ });
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManager.java
index 56f6036..a515f23 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManager.java
@@ -602,6 +602,13 @@
@Override
public int compareTo(HeadsUpEntry o) {
+ boolean selfFullscreen = hasFullScreenIntent(entry);
+ boolean otherFullscreen = hasFullScreenIntent(o.entry);
+ if (selfFullscreen && !otherFullscreen) {
+ return -1;
+ } else if (!selfFullscreen && otherFullscreen) {
+ return 1;
+ }
return postTime < o.postTime ? 1
: postTime == o.postTime ? entry.key.compareTo(o.entry.key)
: -1;
diff --git a/packages/SystemUI/tests/Android.mk b/packages/SystemUI/tests/Android.mk
index d6d17cb..fdc2543 100644
--- a/packages/SystemUI/tests/Android.mk
+++ b/packages/SystemUI/tests/Android.mk
@@ -22,7 +22,9 @@
LOCAL_PROTO_JAVA_OUTPUT_PARAMS := optional_field_style=accessors
LOCAL_AAPT_FLAGS := --auto-add-overlay --extra-packages com.android.systemui:com.android.keyguard
+
LOCAL_SRC_FILES := $(call all-java-files-under, src) \
+ $(call all-Iaidl-files-under, src) \
$(call all-java-files-under, ../src) \
$(call all-proto-files-under, ../src) \
src/com/android/systemui/EventLogTags.logtags
diff --git a/packages/SystemUI/tests/src/com/android/systemui/recents/IRecentsNonSystemUserCallbacks.aidl b/packages/SystemUI/tests/src/com/android/systemui/recents/IRecentsNonSystemUserCallbacks.aidl
new file mode 120000
index 0000000..0ea3e91
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/recents/IRecentsNonSystemUserCallbacks.aidl
@@ -0,0 +1 @@
+../../../../../../src/com/android/systemui/recents/IRecentsNonSystemUserCallbacks.aidl
\ No newline at end of file
diff --git a/packages/SystemUI/tests/src/com/android/systemui/recents/IRecentsSystemUserCallbacks.aidl b/packages/SystemUI/tests/src/com/android/systemui/recents/IRecentsSystemUserCallbacks.aidl
new file mode 120000
index 0000000..b1a0963
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/recents/IRecentsSystemUserCallbacks.aidl
@@ -0,0 +1 @@
+../../../../../../src/com/android/systemui/recents/IRecentsSystemUserCallbacks.aidl
\ No newline at end of file
diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java
index b9759dd..c712a56 100644
--- a/services/core/java/com/android/server/ConnectivityService.java
+++ b/services/core/java/com/android/server/ConnectivityService.java
@@ -1450,10 +1450,7 @@
}
private void enforceChangePermission() {
- int uid = Binder.getCallingUid();
- Settings.checkAndNoteChangeNetworkStateOperation(mContext, uid, Settings
- .getPackageNameForUid(mContext, uid), true);
-
+ ConnectivityManager.enforceChangePermission(mContext);
}
private void enforceTetherAccessPermission() {
diff --git a/services/core/java/com/android/server/MountService.java b/services/core/java/com/android/server/MountService.java
index 4d32599..85187c7 100644
--- a/services/core/java/com/android/server/MountService.java
+++ b/services/core/java/com/android/server/MountService.java
@@ -2442,8 +2442,13 @@
}
try {
- mCryptConnector.execute("cryptfs", "enablecrypto", "inplace", CRYPTO_TYPES[type],
- new SensitiveArg(password));
+ if (type == StorageManager.CRYPT_TYPE_DEFAULT) {
+ mCryptConnector.execute("cryptfs", "enablecrypto", "inplace",
+ CRYPTO_TYPES[type]);
+ } else {
+ mCryptConnector.execute("cryptfs", "enablecrypto", "inplace",
+ CRYPTO_TYPES[type], new SensitiveArg(password));
+ }
} catch (NativeDaemonConnectorException e) {
// Encryption failed
return e.getCode();
diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java
index 4949138..465118c2 100755
--- a/services/core/java/com/android/server/am/ActiveServices.java
+++ b/services/core/java/com/android/server/am/ActiveServices.java
@@ -1314,6 +1314,15 @@
if (!mRestartingServices.contains(r)) {
return;
}
+ if (!isServiceNeeded(r, false, false)) {
+ // Paranoia: is this service actually needed? In theory a service that is not
+ // needed should never remain on the restart list. In practice... well, there
+ // have been bugs where this happens, and bad things happen because the process
+ // ends up just being cached, so quickly killed, then restarted again and again.
+ // Let's not let that happen.
+ Slog.wtf(TAG, "Restarting service that is not needed: " + r);
+ return;
+ }
try {
bringUpServiceLocked(r, r.intent.getIntent().getFlags(), r.createdFromFg, true);
} catch (TransactionTooLargeException e) {
@@ -2043,6 +2052,13 @@
mAm.mProcessStats);
realStartServiceLocked(sr, proc, sr.createdFromFg);
didSomething = true;
+ if (!isServiceNeeded(sr, false, false)) {
+ // We were waiting for this service to start, but it is actually no
+ // longer needed. This could happen because bringDownServiceIfNeeded
+ // won't bring down a service that is pending... so now the pending
+ // is done, so let's drop it.
+ bringDownServiceLocked(sr);
+ }
}
} catch (RemoteException e) {
Slog.w(TAG, "Exception in new application when starting service "
@@ -2055,7 +2071,7 @@
// be weird to bring up the process but arbitrarily not let the services
// run at this point just because their restart time hasn't come up.
if (mRestartingServices.size() > 0) {
- ServiceRecord sr = null;
+ ServiceRecord sr;
for (int i=0; i<mRestartingServices.size(); i++) {
sr = mRestartingServices.get(i);
if (proc != sr.isolatedProc && (proc.uid != sr.appInfo.uid
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 7df50d5..bb1b6b8 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -5794,7 +5794,7 @@
}
boolean didSomething = killPackageProcessesLocked(packageName, appId, userId,
- -100, callerWillRestart, true, doit, evenPersistent,
+ ProcessList.INVALID_ADJ, callerWillRestart, true, doit, evenPersistent,
packageName == null ? ("stop user " + userId) : ("stop " + packageName));
if (mStackSupervisor.finishDisabledPackageActivitiesLocked(
@@ -6110,7 +6110,7 @@
EventLog.writeEvent(EventLogTags.AM_PROC_BOUND, app.userId, app.pid, app.processName);
app.makeActive(thread, mProcessStats);
- app.curAdj = app.setAdj = -100;
+ app.curAdj = app.setAdj = ProcessList.INVALID_ADJ;
app.curSchedGroup = app.setSchedGroup = Process.THREAD_GROUP_DEFAULT;
app.forcingToForeground = null;
updateProcessForegroundLocked(app, false, false);
@@ -18057,6 +18057,7 @@
// Examine all activities if not already foreground.
if (!foregroundActivities && activitiesSize > 0) {
+ int minLayer = ProcessList.VISIBLE_APP_LAYER_MAX;
for (int j = 0; j < activitiesSize; j++) {
final ActivityRecord r = app.activities.get(j);
if (r.app != app) {
@@ -18077,6 +18078,12 @@
app.cached = false;
app.empty = false;
foregroundActivities = true;
+ if (r.task != null && minLayer > 0) {
+ final int layer = r.task.mLayerRank;
+ if (layer >= 0 && minLayer > layer) {
+ minLayer = layer;
+ }
+ }
break;
} else if (r.state == ActivityState.PAUSING || r.state == ActivityState.PAUSED) {
if (adj > ProcessList.PERCEPTIBLE_APP_ADJ) {
@@ -18117,6 +18124,9 @@
}
}
}
+ if (adj == ProcessList.VISIBLE_APP_ADJ) {
+ adj += minLayer;
+ }
}
if (adj > ProcessList.PERCEPTIBLE_APP_ADJ) {
@@ -18326,11 +18336,11 @@
&& clientAdj < ProcessList.PERCEPTIBLE_APP_ADJ
&& adj > ProcessList.PERCEPTIBLE_APP_ADJ) {
adj = ProcessList.PERCEPTIBLE_APP_ADJ;
- } else if (clientAdj > ProcessList.VISIBLE_APP_ADJ) {
+ } else if (clientAdj >= ProcessList.PERCEPTIBLE_APP_ADJ) {
adj = clientAdj;
} else {
if (adj > ProcessList.VISIBLE_APP_ADJ) {
- adj = ProcessList.VISIBLE_APP_ADJ;
+ adj = Math.max(clientAdj, ProcessList.VISIBLE_APP_ADJ);
}
}
if (!client.cached) {
@@ -19368,6 +19378,8 @@
uidRec.reset();
}
+ mStackSupervisor.rankTaskLayersIfNeeded();
+
mAdjSeq++;
mNewNumServiceProcs = 0;
mNewNumAServiceProcs = 0;
diff --git a/services/core/java/com/android/server/am/ActivityStack.java b/services/core/java/com/android/server/am/ActivityStack.java
index a59f7ef..1721470 100644
--- a/services/core/java/com/android/server/am/ActivityStack.java
+++ b/services/core/java/com/android/server/am/ActivityStack.java
@@ -993,10 +993,13 @@
r.userId, System.identityHashCode(r), r.shortComponentName,
mPausingActivity != null
? mPausingActivity.shortComponentName : "(none)");
- if (r.finishing && r.state == ActivityState.PAUSING) {
- if (DEBUG_PAUSE) Slog.v(TAG,
- "Executing finish of failed to pause activity: " + r);
- finishCurrentActivityLocked(r, FINISH_AFTER_VISIBLE, false);
+ if (r.state == ActivityState.PAUSING) {
+ r.state = ActivityState.PAUSED;
+ if (r.finishing) {
+ if (DEBUG_PAUSE) Slog.v(TAG,
+ "Executing finish of failed to pause activity: " + r);
+ finishCurrentActivityLocked(r, FINISH_AFTER_VISIBLE, false);
+ }
}
}
}
@@ -1375,6 +1378,20 @@
return true;
}
+ final int rankTaskLayers(int baseLayer) {
+ int layer = 0;
+ for (int taskNdx = mTaskHistory.size() - 1; taskNdx >= 0; --taskNdx) {
+ final TaskRecord task = mTaskHistory.get(taskNdx);
+ ActivityRecord r = task.topRunningActivityLocked();
+ if (r == null || r.finishing || !r.visible) {
+ task.mLayerRank = -1;
+ } else {
+ task.mLayerRank = baseLayer + layer++;
+ }
+ }
+ return layer;
+ }
+
/**
* Make sure that all activities that need to be visible (that is, they
* currently can be seen by the user) actually are.
@@ -3781,6 +3798,7 @@
task.mLastTimeMoved *= -1;
}
}
+ mStackSupervisor.invalidateTaskLayers();
}
void moveHomeStackTaskToTop(int homeStackTaskType) {
diff --git a/services/core/java/com/android/server/am/ActivityStackSupervisor.java b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
index 2103b60..54ac58a 100644
--- a/services/core/java/com/android/server/am/ActivityStackSupervisor.java
+++ b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
@@ -357,6 +357,9 @@
// It will be calculated when the default display gets added.
private int mDefaultMinimalSizeOfResizeableTask = -1;
+ // Whether tasks have moved and we need to rank the tasks before next OOM scoring
+ private boolean mTaskLayersChanged = true;
+
/**
* Description of a request to start a new activity, which has been held
* due to app switches being disabled.
@@ -3613,6 +3616,24 @@
}
}
+ void invalidateTaskLayers() {
+ mTaskLayersChanged = true;
+ }
+
+ void rankTaskLayersIfNeeded() {
+ if (!mTaskLayersChanged) {
+ return;
+ }
+ mTaskLayersChanged = false;
+ for (int displayNdx = 0; displayNdx < mActivityDisplays.size(); displayNdx++) {
+ final ArrayList<ActivityStack> stacks = mActivityDisplays.valueAt(displayNdx).mStacks;
+ int baseLayer = 0;
+ for (int stackNdx = stacks.size() - 1; stackNdx >= 0; --stackNdx) {
+ baseLayer += stacks.get(stackNdx).rankTaskLayers(baseLayer);
+ }
+ }
+ }
+
void clearOtherAppTimeTrackers(AppTimeTracker except) {
for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
final ArrayList<ActivityStack> stacks = mActivityDisplays.valueAt(displayNdx).mStacks;
diff --git a/services/core/java/com/android/server/am/BroadcastQueue.java b/services/core/java/com/android/server/am/BroadcastQueue.java
index 960cbf1..6de8579 100644
--- a/services/core/java/com/android/server/am/BroadcastQueue.java
+++ b/services/core/java/com/android/server/am/BroadcastQueue.java
@@ -44,6 +44,7 @@
import android.os.UserHandle;
import android.util.EventLog;
import android.util.Slog;
+import android.util.TimeUtils;
import com.android.server.DeviceIdleController;
import static com.android.server.am.ActivityManagerDebugConfig.*;
@@ -1284,6 +1285,7 @@
final boolean dumpLocked(FileDescriptor fd, PrintWriter pw, String[] args,
int opti, boolean dumpAll, String dumpPackage, boolean needSep) {
+ SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
if (mParallelBroadcasts.size() > 0 || mOrderedBroadcasts.size() > 0
|| mPendingBroadcast != null) {
boolean printed = false;
@@ -1301,7 +1303,7 @@
pw.println(" Active broadcasts [" + mQueueName + "]:");
}
pw.println(" Active Broadcast " + mQueueName + " #" + i + ":");
- br.dump(pw, " ");
+ br.dump(pw, " ", sdf);
}
printed = false;
needSep = true;
@@ -1319,7 +1321,7 @@
pw.println(" Active ordered broadcasts [" + mQueueName + "]:");
}
pw.println(" Active Ordered Broadcast " + mQueueName + " #" + i + ":");
- mOrderedBroadcasts.get(i).dump(pw, " ");
+ mOrderedBroadcasts.get(i).dump(pw, " ", sdf);
}
if (dumpPackage == null || (mPendingBroadcast != null
&& dumpPackage.equals(mPendingBroadcast.callerPackage))) {
@@ -1328,7 +1330,7 @@
}
pw.println(" Pending broadcast [" + mQueueName + "]:");
if (mPendingBroadcast != null) {
- mPendingBroadcast.dump(pw, " ");
+ mPendingBroadcast.dump(pw, " ", sdf);
} else {
pw.println(" (null)");
}
@@ -1366,7 +1368,7 @@
if (dumpAll) {
pw.print(" Historical Broadcast " + mQueueName + " #");
pw.print(i); pw.println(":");
- r.dump(pw, " ");
+ r.dump(pw, " ", sdf);
} else {
pw.print(" #"); pw.print(i); pw.print(": "); pw.println(r);
pw.print(" ");
@@ -1400,7 +1402,6 @@
}
// done skipping; dump the remainder of the ring. 'i' is still the ordinal within
// the overall broadcast history.
- SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
do {
ringIndex = ringAdvance(ringIndex, -1, MAX_BROADCAST_SUMMARY_HISTORY);
Intent intent = mBroadcastSummaryHistory[ringIndex];
@@ -1422,9 +1423,19 @@
i++;
pw.print(" #"); pw.print(i); pw.print(": ");
pw.println(intent.toShortString(false, true, true, false));
- pw.print(" enq="); pw.print(sdf.format(new Date(mSummaryHistoryEnqueueTime[ringIndex])));
- pw.print(" disp="); pw.print(sdf.format(new Date(mSummaryHistoryDispatchTime[ringIndex])));
- pw.print(" fin="); pw.println(sdf.format(new Date(mSummaryHistoryFinishTime[ringIndex])));
+ pw.print(" ");
+ TimeUtils.formatDuration(mSummaryHistoryDispatchTime[ringIndex]
+ - mSummaryHistoryEnqueueTime[ringIndex], pw);
+ pw.print(" dispatch ");
+ TimeUtils.formatDuration(mSummaryHistoryFinishTime[ringIndex]
+ - mSummaryHistoryDispatchTime[ringIndex], pw);
+ pw.println(" finish");
+ pw.print(" enq=");
+ pw.print(sdf.format(new Date(mSummaryHistoryEnqueueTime[ringIndex])));
+ pw.print(" disp=");
+ pw.print(sdf.format(new Date(mSummaryHistoryDispatchTime[ringIndex])));
+ pw.print(" fin=");
+ pw.println(sdf.format(new Date(mSummaryHistoryFinishTime[ringIndex])));
Bundle bundle = intent.getExtras();
if (bundle != null) {
pw.print(" extras: "); pw.println(bundle.toString());
diff --git a/services/core/java/com/android/server/am/BroadcastRecord.java b/services/core/java/com/android/server/am/BroadcastRecord.java
index 1fbfd9f..b42bcff 100644
--- a/services/core/java/com/android/server/am/BroadcastRecord.java
+++ b/services/core/java/com/android/server/am/BroadcastRecord.java
@@ -32,6 +32,7 @@
import android.util.TimeUtils;
import java.io.PrintWriter;
+import java.text.SimpleDateFormat;
import java.util.Arrays;
import java.util.Date;
import java.util.List;
@@ -88,7 +89,7 @@
ComponentName curComponent; // the receiver class that is currently running.
ActivityInfo curReceiver; // info about the receiver that is currently running.
- void dump(PrintWriter pw, String prefix) {
+ void dump(PrintWriter pw, String prefix, SimpleDateFormat sdf) {
final long now = SystemClock.uptimeMillis();
pw.print(prefix); pw.print(this); pw.print(" to user "); pw.println(userId);
@@ -114,13 +115,19 @@
pw.print(prefix); pw.print("options="); pw.println(options.toBundle());
}
pw.print(prefix); pw.print("enqueueClockTime=");
- pw.print(new Date(enqueueClockTime));
+ pw.print(sdf.format(new Date(enqueueClockTime)));
pw.print(" dispatchClockTime=");
- pw.println(new Date(dispatchClockTime));
+ pw.println(sdf.format(new Date(dispatchClockTime)));
pw.print(prefix); pw.print("dispatchTime=");
TimeUtils.formatDuration(dispatchTime, now, pw);
+ pw.print(" (");
+ TimeUtils.formatDuration(dispatchClockTime-enqueueClockTime, pw);
+ pw.print(" since enq)");
if (finishTime != 0) {
pw.print(" finishTime="); TimeUtils.formatDuration(finishTime, now, pw);
+ pw.print(" (");
+ TimeUtils.formatDuration(finishTime-dispatchTime, pw);
+ pw.print(" since disp)");
} else {
pw.print(" receiverTime="); TimeUtils.formatDuration(receiverTime, now, pw);
}
diff --git a/services/core/java/com/android/server/am/ProcessList.java b/services/core/java/com/android/server/am/ProcessList.java
index 0e24952..b49370b 100644
--- a/services/core/java/com/android/server/am/ProcessList.java
+++ b/services/core/java/com/android/server/am/ProcessList.java
@@ -49,19 +49,22 @@
// OOM adjustments for processes in various states:
+ // Uninitialized value for any major or minor adj fields
+ static final int INVALID_ADJ = -10000;
+
// Adjustment used in certain places where we don't know it yet.
// (Generally this is something that is going to be cached, but we
// don't know the exact value in the cached range to assign yet.)
- static final int UNKNOWN_ADJ = 16;
+ static final int UNKNOWN_ADJ = 1001;
// This is a process only hosting activities that are not visible,
// so it can be killed without any disruption.
- static final int CACHED_APP_MAX_ADJ = 15;
- static final int CACHED_APP_MIN_ADJ = 9;
+ static final int CACHED_APP_MAX_ADJ = 906;
+ static final int CACHED_APP_MIN_ADJ = 900;
// The B list of SERVICE_ADJ -- these are the old and decrepit
// services that aren't as shiny and interesting as the ones in the A list.
- static final int SERVICE_B_ADJ = 8;
+ static final int SERVICE_B_ADJ = 800;
// This is the process of the previous application that the user was in.
// This process is kept above other things, because it is very common to
@@ -69,34 +72,35 @@
// task switch (toggling between the two top recent apps) as well as normal
// UI flow such as clicking on a URI in the e-mail app to view in the browser,
// and then pressing back to return to e-mail.
- static final int PREVIOUS_APP_ADJ = 7;
+ static final int PREVIOUS_APP_ADJ = 700;
// This is a process holding the home application -- we want to try
// avoiding killing it, even if it would normally be in the background,
// because the user interacts with it so much.
- static final int HOME_APP_ADJ = 6;
+ static final int HOME_APP_ADJ = 600;
// This is a process holding an application service -- killing it will not
// have much of an impact as far as the user is concerned.
- static final int SERVICE_ADJ = 5;
+ static final int SERVICE_ADJ = 500;
// This is a process with a heavy-weight application. It is in the
// background, but we want to try to avoid killing it. Value set in
// system/rootdir/init.rc on startup.
- static final int HEAVY_WEIGHT_APP_ADJ = 4;
+ static final int HEAVY_WEIGHT_APP_ADJ = 400;
// This is a process currently hosting a backup operation. Killing it
// is not entirely fatal but is generally a bad idea.
- static final int BACKUP_APP_ADJ = 3;
+ static final int BACKUP_APP_ADJ = 300;
// This is a process only hosting components that are perceptible to the
// user, and we really want to avoid killing them, but they are not
// immediately visible. An example is background music playback.
- static final int PERCEPTIBLE_APP_ADJ = 2;
+ static final int PERCEPTIBLE_APP_ADJ = 200;
// This is a process only hosting activities that are visible to the
// user, so we'd prefer they don't disappear.
- static final int VISIBLE_APP_ADJ = 1;
+ static final int VISIBLE_APP_ADJ = 100;
+ static final int VISIBLE_APP_LAYER_MAX = PERCEPTIBLE_APP_ADJ - VISIBLE_APP_ADJ - 1;
// This is the process running the current foreground app. We'd really
// rather not kill it!
@@ -104,18 +108,18 @@
// This is a process that the system or a persistent process has bound to,
// and indicated it is important.
- static final int PERSISTENT_SERVICE_ADJ = -11;
+ static final int PERSISTENT_SERVICE_ADJ = -700;
// This is a system persistent process, such as telephony. Definitely
// don't want to kill it, but doing so is not completely fatal.
- static final int PERSISTENT_PROC_ADJ = -12;
+ static final int PERSISTENT_PROC_ADJ = -800;
// The system process runs at the default adjustment.
- static final int SYSTEM_ADJ = -16;
+ static final int SYSTEM_ADJ = -900;
// Special code for native processes that are not being managed by the system (so
// don't have an oom adj assigned by the system).
- static final int NATIVE_ADJ = -17;
+ static final int NATIVE_ADJ = -1000;
// Memory pages are 4K.
static final int PAGE_SIZE = 4*1024;
@@ -159,7 +163,7 @@
// These must be kept in sync with the definitions in lmkd.c
//
// LMK_TARGET <minfree> <minkillprio> ... (up to 6 pairs)
- // LMK_PROCPRIO <pid> <prio>
+ // LMK_PROCPRIO <pid> <uid> <prio>
// LMK_PROCREMOVE <pid>
static final byte LMK_TARGET = 0;
static final byte LMK_PROCPRIO = 1;
diff --git a/services/core/java/com/android/server/am/ProcessRecord.java b/services/core/java/com/android/server/am/ProcessRecord.java
index bd31a21..08203c55b 100644
--- a/services/core/java/com/android/server/am/ProcessRecord.java
+++ b/services/core/java/com/android/server/am/ProcessRecord.java
@@ -171,10 +171,10 @@
boolean debugging; // was app launched for debugging?
boolean waitedForDebugger; // has process show wait for debugger dialog?
Dialog waitDialog; // current wait for debugger dialog
-
+
String shortStringName; // caching of toShortString() result.
String stringName; // caching of toString() result.
-
+
// These reports are generated & stored when an app gets into an error condition.
// They will be "null" when all is OK.
ActivityManager.ProcessErrorStateInfo crashingReport;
@@ -402,7 +402,7 @@
}
}
}
-
+
ProcessRecord(BatteryStatsImpl _batteryStats, ApplicationInfo _info,
String _processName, int _uid) {
mBatteryStats = _batteryStats;
@@ -413,8 +413,8 @@
processName = _processName;
pkgList.put(_info.packageName, new ProcessStats.ProcessStateHolder(_info.versionCode));
maxAdj = ProcessList.UNKNOWN_ADJ;
- curRawAdj = setRawAdj = -100;
- curAdj = setAdj = -100;
+ curRawAdj = setRawAdj = ProcessList.INVALID_ADJ;
+ curAdj = setAdj = ProcessList.INVALID_ADJ;
persistent = false;
removed = false;
lastStateTime = lastPssTime = nextPssTime = SystemClock.uptimeMillis();
@@ -560,7 +560,7 @@
toShortString(sb);
return shortStringName = sb.toString();
}
-
+
void toShortString(StringBuilder sb) {
sb.append(pid);
sb.append(':');
@@ -585,7 +585,7 @@
}
}
}
-
+
public String toString() {
if (stringName != null) {
return stringName;
@@ -695,7 +695,7 @@
pkgList.put(info.packageName, new ProcessStats.ProcessStateHolder(info.versionCode));
}
}
-
+
public String[] getPackageList() {
int size = pkgList.size();
if (size == 0) {
diff --git a/services/core/java/com/android/server/am/TaskRecord.java b/services/core/java/com/android/server/am/TaskRecord.java
index 1999f49..fe87a93 100644
--- a/services/core/java/com/android/server/am/TaskRecord.java
+++ b/services/core/java/com/android/server/am/TaskRecord.java
@@ -221,6 +221,10 @@
// default minimal size.
final int mMinimalSize;
+ // Ranking (from top) of this task among all visible tasks. (-1 means it's not visible)
+ // This number will be assigned when we evaluate OOM scores for all visible tasks.
+ int mLayerRank = -1;
+
Configuration mOverrideConfig = Configuration.EMPTY;
TaskRecord(ActivityManagerService service, int _taskId, ActivityInfo info, Intent _intent,
diff --git a/services/core/java/com/android/server/notification/EventConditionProvider.java b/services/core/java/com/android/server/notification/EventConditionProvider.java
index 88ef366..a4d5bce 100644
--- a/services/core/java/com/android/server/notification/EventConditionProvider.java
+++ b/services/core/java/com/android/server/notification/EventConditionProvider.java
@@ -176,7 +176,7 @@
}
mTrackers.clear();
for (UserHandle user : UserManager.get(mContext).getUserProfiles()) {
- final Context context = user.isOwner() ? mContext : getContextForUser(mContext, user);
+ final Context context = user.isSystem() ? mContext : getContextForUser(mContext, user);
if (context == null) {
Slog.w(TAG, "Unable to create context for user " + user.getIdentifier());
continue;
diff --git a/services/core/java/com/android/server/pm/DefaultPermissionGrantPolicy.java b/services/core/java/com/android/server/pm/DefaultPermissionGrantPolicy.java
index 8176aff..8abc8fc 100644
--- a/services/core/java/com/android/server/pm/DefaultPermissionGrantPolicy.java
+++ b/services/core/java/com/android/server/pm/DefaultPermissionGrantPolicy.java
@@ -586,6 +586,8 @@
grantRuntimePermissionsLPw(wearHomePackage, PHONE_PERMISSIONS, true, userId);
grantRuntimePermissionsLPw(wearHomePackage, MICROPHONE_PERMISSIONS, false,
userId);
+ grantRuntimePermissionsLPw(wearHomePackage, LOCATION_PERMISSIONS, false,
+ userId);
}
}
@@ -596,7 +598,10 @@
private void grantDefaultPermissionsToDefaultSystemDialerAppLPr(
PackageParser.Package dialerPackage, int userId) {
if (doesPackageSupportRuntimePermissions(dialerPackage)) {
- grantRuntimePermissionsLPw(dialerPackage, PHONE_PERMISSIONS, userId);
+ boolean isPhonePermFixed =
+ mService.hasSystemFeature(PackageManager.FEATURE_WATCH);
+ grantRuntimePermissionsLPw(
+ dialerPackage, PHONE_PERMISSIONS, isPhonePermFixed, userId);
grantRuntimePermissionsLPw(dialerPackage, CONTACTS_PERMISSIONS, userId);
grantRuntimePermissionsLPw(dialerPackage, SMS_PERMISSIONS, userId);
grantRuntimePermissionsLPw(dialerPackage, MICROPHONE_PERMISSIONS, userId);
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 6553dc3..496653e 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -2321,7 +2321,7 @@
+ mSdkVersion + "; regranting permissions for internal storage");
updateFlags |= UPDATE_PERMISSIONS_REPLACE_PKG | UPDATE_PERMISSIONS_REPLACE_ALL;
}
- updatePermissionsLPw(null, null, updateFlags);
+ updatePermissionsLPw(null, null, StorageManager.UUID_PRIVATE_INTERNAL, updateFlags);
ver.sdkVersion = mSdkVersion;
// If this is the first boot or an update from pre-M, and it is a normal
@@ -8365,8 +8365,14 @@
static final int UPDATE_PERMISSIONS_REPLACE_PKG = 1<<1;
static final int UPDATE_PERMISSIONS_REPLACE_ALL = 1<<2;
+ private void updatePermissionsLPw(String changingPkg, PackageParser.Package pkgInfo,
+ int flags) {
+ final String volumeUuid = (pkgInfo != null) ? getVolumeUuidForPackage(pkgInfo) : null;
+ updatePermissionsLPw(changingPkg, pkgInfo, volumeUuid, flags);
+ }
+
private void updatePermissionsLPw(String changingPkg,
- PackageParser.Package pkgInfo, int flags) {
+ PackageParser.Package pkgInfo, String replaceVolumeUuid, int flags) {
// Make sure there are no dangling permission trees.
Iterator<BasePermission> it = mSettings.mPermissionTrees.values().iterator();
while (it.hasNext()) {
@@ -8435,14 +8441,21 @@
if ((flags&UPDATE_PERMISSIONS_ALL) != 0) {
for (PackageParser.Package pkg : mPackages.values()) {
if (pkg != pkgInfo) {
- grantPermissionsLPw(pkg, (flags&UPDATE_PERMISSIONS_REPLACE_ALL) != 0,
- changingPkg);
+ // Only replace for packages on requested volume
+ final String volumeUuid = getVolumeUuidForPackage(pkg);
+ final boolean replace = ((flags & UPDATE_PERMISSIONS_REPLACE_ALL) != 0)
+ && Objects.equals(replaceVolumeUuid, volumeUuid);
+ grantPermissionsLPw(pkg, replace, changingPkg);
}
}
}
if (pkgInfo != null) {
- grantPermissionsLPw(pkgInfo, (flags&UPDATE_PERMISSIONS_REPLACE_PKG) != 0, changingPkg);
+ // Only replace for packages on requested volume
+ final String volumeUuid = getVolumeUuidForPackage(pkgInfo);
+ final boolean replace = ((flags & UPDATE_PERMISSIONS_REPLACE_PKG) != 0)
+ && Objects.equals(replaceVolumeUuid, volumeUuid);
+ grantPermissionsLPw(pkgInfo, replace, changingPkg);
}
}
@@ -12861,6 +12874,18 @@
return installFlags;
}
+ private String getVolumeUuidForPackage(PackageParser.Package pkg) {
+ if (isExternal(pkg)) {
+ if (TextUtils.isEmpty(pkg.volumeUuid)) {
+ return StorageManager.UUID_PRIMARY_PHYSICAL;
+ } else {
+ return pkg.volumeUuid;
+ }
+ } else {
+ return StorageManager.UUID_PRIVATE_INTERNAL;
+ }
+ }
+
private VersionInfo getSettingsVersionForPackage(PackageParser.Package pkg) {
if (isExternal(pkg)) {
if (TextUtils.isEmpty(pkg.volumeUuid)) {
@@ -15731,7 +15756,7 @@
if (isMounted) {
if (DEBUG_SD_INSTALL)
Log.i(TAG, "Loading packages");
- loadMediaPackages(processCids, uidArr);
+ loadMediaPackages(processCids, uidArr, externalStorage);
startCleaningPackages();
mInstallerService.onSecureContainersAvailable();
} else {
@@ -15786,7 +15811,8 @@
* the cid is added to list of removeCids. We currently don't delete stale
* containers.
*/
- private void loadMediaPackages(ArrayMap<AsecInstallArgs, String> processCids, int[] uidArr) {
+ private void loadMediaPackages(ArrayMap<AsecInstallArgs, String> processCids, int[] uidArr,
+ boolean externalStorage) {
ArrayList<String> pkgList = new ArrayList<String>();
Set<AsecInstallArgs> keys = processCids.keySet();
@@ -15859,7 +15885,10 @@
// cases get permissions that the user didn't initially explicitly
// allow... it would be nice to have some better way to handle
// this situation.
- final VersionInfo ver = mSettings.getExternalVersion();
+ final VersionInfo ver = externalStorage ? mSettings.getExternalVersion()
+ : mSettings.getInternalVersion();
+ final String volumeUuid = externalStorage ? StorageManager.UUID_PRIMARY_PHYSICAL
+ : StorageManager.UUID_PRIVATE_INTERNAL;
int updateFlags = UPDATE_PERMISSIONS_ALL;
if (ver.sdkVersion != mSdkVersion) {
@@ -15867,7 +15896,7 @@
+ mSdkVersion + "; regranting permissions for external");
updateFlags |= UPDATE_PERMISSIONS_REPLACE_PKG | UPDATE_PERMISSIONS_REPLACE_ALL;
}
- updatePermissionsLPw(null, null, updateFlags);
+ updatePermissionsLPw(null, null, volumeUuid, updateFlags);
// Yay, everything is now upgraded
ver.forceCurrent();
@@ -16001,7 +16030,7 @@
+ mSdkVersion + "; regranting permissions for " + vol.fsUuid);
updateFlags |= UPDATE_PERMISSIONS_REPLACE_PKG | UPDATE_PERMISSIONS_REPLACE_ALL;
}
- updatePermissionsLPw(null, null, updateFlags);
+ updatePermissionsLPw(null, null, vol.fsUuid, updateFlags);
// Yay, everything is now upgraded
ver.forceCurrent();
diff --git a/services/core/java/com/android/server/pm/Settings.java b/services/core/java/com/android/server/pm/Settings.java
index 61d2676..d148a4f 100644
--- a/services/core/java/com/android/server/pm/Settings.java
+++ b/services/core/java/com/android/server/pm/Settings.java
@@ -514,7 +514,18 @@
ArrayList<String> removeStage = new ArrayList<String>();
for (Map.Entry<String,SharedUserSetting> entry : mSharedUsers.entrySet()) {
final SharedUserSetting sus = entry.getValue();
- if (sus == null || sus.packages.size() == 0) {
+ if (sus == null) {
+ removeStage.add(entry.getKey());
+ continue;
+ }
+ // remove packages that are no longer installed
+ for (Iterator<PackageSetting> iter = sus.packages.iterator(); iter.hasNext();) {
+ PackageSetting ps = iter.next();
+ if (mPackages.get(ps.name) == null) {
+ iter.remove();
+ }
+ }
+ if (sus.packages.size() == 0) {
removeStage.add(entry.getKey());
}
}
diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java
index 80697ed..b36a22e 100644
--- a/services/core/java/com/android/server/pm/UserManagerService.java
+++ b/services/core/java/com/android/server/pm/UserManagerService.java
@@ -598,8 +598,6 @@
@Override
public Bundle getUserRestrictions(int userId) {
- // checkManageUsersPermission("getUserRestrictions");
-
synchronized (mPackagesLock) {
Bundle restrictions = mUserRestrictions.get(userId);
return restrictions != null ? new Bundle(restrictions) : new Bundle();
@@ -1588,7 +1586,7 @@
public Bundle getApplicationRestrictionsForUser(String packageName, int userId) {
if (UserHandle.getCallingUserId() != userId
|| !UserHandle.isSameApp(Binder.getCallingUid(), getUidForPackage(packageName))) {
- checkManageUsersPermission("Only system can get restrictions for other users/apps");
+ checkManageUsersPermission("get application restrictions for other users/apps");
}
synchronized (mPackagesLock) {
// Read the restrictions from XML
@@ -1599,10 +1597,7 @@
@Override
public void setApplicationRestrictions(String packageName, Bundle restrictions,
int userId) {
- if (UserHandle.getCallingUserId() != userId
- || !UserHandle.isSameApp(Binder.getCallingUid(), getUidForPackage(packageName))) {
- checkManageUsersPermission("Only system can set restrictions for other users/apps");
- }
+ checkManageUsersPermission("set application restrictions");
synchronized (mPackagesLock) {
if (restrictions == null || restrictions.isEmpty()) {
cleanAppRestrictionsForPackage(packageName, userId);
@@ -1623,7 +1618,7 @@
@Override
public void removeRestrictions() {
- checkManageUsersPermission("Only system can remove restrictions");
+ checkManageUsersPermission("remove restrictions");
final int userHandle = UserHandle.getCallingUserId();
removeRestrictionsForUser(userHandle, true);
}
diff --git a/services/core/java/com/android/server/wm/WindowStateAnimator.java b/services/core/java/com/android/server/wm/WindowStateAnimator.java
index ee7c88a..ecc1f2c 100644
--- a/services/core/java/com/android/server/wm/WindowStateAnimator.java
+++ b/services/core/java/com/android/server/wm/WindowStateAnimator.java
@@ -1431,8 +1431,14 @@
if (appToken != null && appToken.mCropWindowsToStack && !appToken.mReplacingWindow) {
TaskStack stack = w.getTask().mStack;
stack.getBounds(mTmpStackBounds);
- final int frameX = w.mFrame.left + mWin.mXOffset - w.getAttrs().surfaceInsets.left;
- final int frameY = w.mFrame.top + mWin.mYOffset - w.getAttrs().surfaceInsets.top;
+ // When we resize we use the big surface approach, which means we can't trust the
+ // window frame bounds anymore. Instead, the window will be placed at 0, 0, but to avoid
+ // hardcoding it, we use surface coordinates.
+ final boolean isResizing = w.isDragResizing();
+ final int frameX = isResizing ? (int) mSurfaceX :
+ w.mFrame.left + mWin.mXOffset - w.getAttrs().surfaceInsets.left;
+ final int frameY = isResizing ? (int) mSurfaceY :
+ w.mFrame.top + mWin.mYOffset - w.getAttrs().surfaceInsets.top;
// We need to do some acrobatics with surface position, because their clip region is
// relative to the inside of the surface, but the stack bounds aren't.
clipRect.left = Math.max(0,
diff --git a/services/core/jni/com_android_server_input_InputManagerService.cpp b/services/core/jni/com_android_server_input_InputManagerService.cpp
index 8cb0a13..be190cb 100644
--- a/services/core/jni/com_android_server_input_InputManagerService.cpp
+++ b/services/core/jni/com_android_server_input_InputManagerService.cpp
@@ -238,7 +238,8 @@
/* --- PointerControllerPolicyInterface implementation --- */
virtual void loadPointerResources(PointerResources* outResources);
- virtual void loadAdditionalMouseResources(std::map<int, SpriteIcon>* outResources);
+ virtual void loadAdditionalMouseResources(std::map<int32_t, SpriteIcon>* outResources);
+ virtual int32_t getDefaultPointerIconId();
private:
sp<InputManager> mInputManager;
@@ -786,7 +787,7 @@
sp<PointerController> controller = mLocked.pointerController.promote();
if (controller != NULL) {
// Use 0 (the default icon) for ARROW.
- controller->updatePointerShape((iconId == POINTER_ICON_STYLE_ARROW) ? 0 : iconId);
+ controller->updatePointerShape(iconId);
}
}
@@ -1040,15 +1041,19 @@
&outResources->spotAnchor);
}
-void NativeInputManager::loadAdditionalMouseResources(std::map<int, SpriteIcon>* outResources) {
+void NativeInputManager::loadAdditionalMouseResources(std::map<int32_t, SpriteIcon>* outResources) {
JNIEnv* env = jniEnv();
for (int iconId = POINTER_ICON_STYLE_CONTEXT_MENU; iconId <= POINTER_ICON_STYLE_GRABBING;
++iconId) {
loadSystemIconAsSprite(env, mContextObj, iconId, &((*outResources)[iconId]));
}
+ loadSystemIconAsSprite(env, mContextObj, POINTER_ICON_STYLE_NULL, &((*outResources)[POINTER_ICON_STYLE_NULL]));
}
+int32_t NativeInputManager::getDefaultPointerIconId() {
+ return POINTER_ICON_STYLE_ARROW;
+}
// ----------------------------------------------------------------------------
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index 4748c96..8385685 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -3494,7 +3494,7 @@
final UserHandle caller = mInjector.binderGetCallingUserHandle();
// If there is a profile owner, redirect to that; otherwise query the device owner.
ComponentName aliasChooser = getProfileOwner(caller.getIdentifier());
- if (aliasChooser == null && caller.isOwner()) {
+ if (aliasChooser == null && caller.isSystem()) {
ActiveAdmin deviceOwnerAdmin = getDeviceOwnerAdminLocked();
if (deviceOwnerAdmin != null) {
aliasChooser = deviceOwnerAdmin.info.getComponent();
diff --git a/telecomm/java/android/telecom/PhoneAccount.java b/telecomm/java/android/telecom/PhoneAccount.java
index 703564c..5ecd2b5 100644
--- a/telecomm/java/android/telecom/PhoneAccount.java
+++ b/telecomm/java/android/telecom/PhoneAccount.java
@@ -52,6 +52,32 @@
public final class PhoneAccount implements Parcelable {
/**
+ * {@link PhoneAccount} extras key (see {@link PhoneAccount#getExtras()}) which determines the
+ * maximum permitted length of a call subject specified via the
+ * {@link TelecomManager#EXTRA_CALL_SUBJECT} extra on an
+ * {@link android.content.Intent#ACTION_CALL} intent. Ultimately a {@link ConnectionService} is
+ * responsible for enforcing the maximum call subject length when sending the message, however
+ * this extra is provided so that the user interface can proactively limit the length of the
+ * call subject as the user types it.
+ */
+ public static final String EXTRA_CALL_SUBJECT_MAX_LENGTH =
+ "android.telecom.extra.CALL_SUBJECT_MAX_LENGTH";
+
+ /**
+ * {@link PhoneAccount} extras key (see {@link PhoneAccount#getExtras()}) which determines the
+ * character encoding to be used when determining the length of messages.
+ * The user interface can use this when determining the number of characters the user may type
+ * in a call subject. If empty-string, the call subject message size limit will be enforced on
+ * a 1:1 basis. That is, each character will count towards the messages size limit as a single
+ * character. If a character encoding is specified, the message size limit will be based on the
+ * number of bytes in the message per the specified encoding. See
+ * {@link #EXTRA_CALL_SUBJECT_MAX_LENGTH} for more information on the call subject maximum
+ * length.
+ */
+ public static final String EXTRA_CALL_SUBJECT_CHARACTER_ENCODING =
+ "android.telecom.extra.CALL_SUBJECT_CHARACTER_ENCODING";
+
+ /**
* Flag indicating that this {@code PhoneAccount} can act as a connection manager for
* other connections. The {@link ConnectionService} associated with this {@code PhoneAccount}
* will be allowed to manage phone calls including using its own proprietary phone-call
@@ -213,6 +239,7 @@
mSupportedUriSchemes.addAll(phoneAccount.getSupportedUriSchemes());
mIcon = phoneAccount.getIcon();
mIsEnabled = phoneAccount.isEnabled();
+ mExtras = phoneAccount.getExtras();
}
/**
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index d22727d..6b1b6296 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -2874,7 +2874,7 @@
/**
* Returns all observed cell information from all radios on the
* device including the primary and neighboring cells. This does
- * not cause or change the rate of PhoneStateListner#onCellInfoChanged.
+ * not cause or change the rate of PhoneStateListener#onCellInfoChanged.
*<p>
* The list can include one or more of {@link android.telephony.CellInfoGsm CellInfoGsm},
* {@link android.telephony.CellInfoCdma CellInfoCdma},
@@ -2888,6 +2888,9 @@
* devices this may return null in which case getCellLocation should
* be called.
*<p>
+ * This API will return valid data for registered cells on devices with
+ * {@link android.content.pm.PackageManager#FEATURE_TELEPHONY}
+ *<p>
* @return List of CellInfo or null if info unavailable.
*
* <p>Requires Permission: {@link android.Manifest.permission#ACCESS_COARSE_LOCATION}