diff options
179 files changed, 3137 insertions, 1652 deletions
diff --git a/api/current.txt b/api/current.txt index 4a1a4582e1b4..55b5d529d3f3 100644 --- a/api/current.txt +++ b/api/current.txt @@ -30532,6 +30532,8 @@ package android.telecom { 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 @@ package android.view { 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 @@ package android.view { 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 8ccc706bbcde..06cbfa7e3789 100644 --- a/api/system-current.txt +++ b/api/system-current.txt @@ -32734,6 +32734,8 @@ package android.telecom { 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 @@ package android.view { 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 @@ package android.view { 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 df7e586767c5..201afee58965 100644 --- a/core/java/android/hardware/input/InputManager.java +++ b/core/java/android/hardware/input/InputManager.java @@ -108,13 +108,14 @@ public final class InputManager { * 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 444548faad42..ad9058f35dad 100644 --- a/core/java/android/net/ConnectivityManager.java +++ b/core/java/android/net/ConnectivityManager.java @@ -901,8 +901,12 @@ public class ConnectivityManager { * 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 @@ public class ConnectivityManager { * 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 @@ public class ConnectivityManager { * 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 @@ public class ConnectivityManager { * 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 @@ public class ConnectivityManager { 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 @@ public class ConnectivityManager { 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 @@ public class ConnectivityManager { * 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 @@ public class ConnectivityManager { /** * 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 @@ public class ConnectivityManager { * 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 @@ public class ConnectivityManager { * 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 @@ public class ConnectivityManager { * 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 @@ public class ConnectivityManager { * 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 1273772bc4ec..5852f5fcd070 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.List; 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 @@ public final class Parcel { * indicating that we're responsible for its lifecycle. */ private boolean mOwnsNativeParcelObject; + private long mNativeSize; private RuntimeException mStack; @@ -244,7 +248,7 @@ public final class Parcel { 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 @@ public final class Parcel { 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 @@ public final class Parcel { 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 @@ public final class Parcel { * @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 @@ public final class Parcel { * 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 @@ public final class Parcel { * 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 @@ public final class Parcel { private void freeBuffer() { if (mOwnsNativeParcelObject) { - nativeFreeBuffer(mNativePtr); + updateNativeSize(nativeFreeBuffer(mNativePtr)); } } @@ -2553,6 +2574,7 @@ public final class Parcel { 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 41de579201a5..45355723149c 100644 --- a/core/java/android/os/RecoverySystem.java +++ b/core/java/android/os/RecoverySystem.java @@ -341,6 +341,10 @@ public class RecoverySystem { } 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 @@ public class RecoverySystem { 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 d601831b863c..11effd040ab6 100644 --- a/core/java/android/provider/Settings.java +++ b/core/java/android/provider/Settings.java @@ -1434,25 +1434,6 @@ public final class Settings { } /** - * 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 @@ public final class Settings { * 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 @@ public final class Settings { /** * 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 1c20cab55a5d..55cf56ff7c9f 100644 --- a/core/java/android/view/KeyEvent.java +++ b/core/java/android/view/KeyEvent.java @@ -790,8 +790,14 @@ public class KeyEvent extends InputEvent implements Parcelable { 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; - - private static final int LAST_KEYCODE = KEYCODE_SOFT_SLEEP; + /** 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_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 24183be5eabf..2fabe33ddf39 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.GrowingArrayUtils; 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 @@ public class Editor { } 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 @@ public class Editor { 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 @@ public class Editor { 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 @@ public class Editor { 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 391022cc0c61..13b1c4be4c62 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.KeyEvent; import android.view.MotionEvent; import android.view.PointerIcon; import android.view.View; -import android.view.ViewParent; -import android.view.ViewStructure; import android.view.ViewConfiguration; import android.view.ViewDebug; import android.view.ViewGroup.LayoutParams; +import android.view.ViewHierarchyEncoder; +import android.view.ViewParent; import android.view.ViewRootImpl; +import android.view.ViewStructure; import android.view.ViewTreeObserver; -import android.view.ViewHierarchyEncoder; import android.view.accessibility.AccessibilityEvent; import android.view.accessibility.AccessibilityManager; import android.view.accessibility.AccessibilityNodeInfo; @@ -153,8 +155,6 @@ import java.lang.ref.WeakReference; 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 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener 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 70d5f629c575..b7b74000380f 100644 --- a/core/java/com/android/internal/app/ChooserActivity.java +++ b/core/java/com/android/internal/app/ChooserActivity.java @@ -373,6 +373,11 @@ public class ChooserActivity extends ResolverActivity { 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 a14afa054c17..41aa9ca977fb 100644 --- a/core/jni/android_os_Parcel.cpp +++ b/core/jni/android_os_Parcel.cpp @@ -114,7 +114,7 @@ static jint android_os_Parcel_dataCapacity(JNIEnv* env, jclass clazz, jlong nati 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 @@ static void android_os_Parcel_setDataSize(JNIEnv* env, jclass clazz, jlong nativ 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_writeStrongBinder(JNIEnv* env, jclass clazz, jlong } } -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 @@ static void android_os_Parcel_writeFileDescriptor(JNIEnv* env, jclass clazz, jlo 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 @@ static jlong android_os_Parcel_create(JNIEnv* env, jclass clazz) 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 @@ static jbyteArray android_os_Parcel_marshall(JNIEnv* env, jclass clazz, jlong na 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 @@ static void android_os_Parcel_unmarshall(JNIEnv* env, jclass clazz, jlong native 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 @@ static const JNINativeMethod gParcelMethods[] = { {"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 @@ static const JNINativeMethod gParcelMethods[] = { {"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 @@ static const JNINativeMethod gParcelMethods[] = { {"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 9f6e0334bfc2..f3007411f765 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 a58ec896ced1..e7acf7e7125a 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 80665a272561..5bd9ccf8e949 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 22eaabee25c9..4dd5144e2467 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 401e5b825355..c828fc4d2681 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 6c3fa548eda2..e474e7ea7dd7 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 3f986a0f234f..1f365b933bc7 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 8d004df3bfb6..046ca143758d 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 6ead72f40144..8f3976d003de 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 346cdb177bde..82b550b43294 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 4e8b455aaead..9ae5473b2aa8 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 3f5e1afcbb66..56deed130980 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 22179edbe6ff..53adc2b09927 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 22179edbe6ff..53adc2b09927 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 22179edbe6ff..53adc2b09927 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 08f513b8a3ff..25ba6db84ef8 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 4d1500dbf6a4..e3a4b665f21d 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 09454d65e02b..41474a450fa2 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 0815aeb6914c..3a23fba08c7e 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 683afb403090..ae6598b5581b 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 f8fb76f783dd..d8778a8d722c 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 286459acaff7..6f59ee0495cf 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 e4daeb116f23..9f0838971300 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 2abd2be84911..21e6ee7a985d 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 8f461945c4c6..6dc03717e6ba 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 61b21bd6f027..6b1a91f59664 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 680dabd83350..67d711074c39 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 faed9b9eef20..7e1f55b65019 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 9a0e603ca2db..9259c06434d9 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 1bbd02084f30..0f6cd3ec9434 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 2a97573bd69e..29f7408c62b0 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 c56b41b6f257..ab541eab4e1b 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 26fe69831d68..de1982142f52 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 6b1c833b2457..45e82ace0360 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 e1221c4ce9c4..7900f83afced 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 b3172ff07e2f..23ec61240efc 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 822a30391c8d..8ffa8b82a79b 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 73d7ffcf1f2e..a840c7edbab9 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 0d76af25a091..59b0214c7e74 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 22399b0e2750..59c34dc73f34 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 1d392308092c..f1319149b4c8 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 dbb8fbb7f2b8..536a10704e12 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 eaf7b46fd3a0..288493a0d6e9 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 17e0d4902099..eab72bfc8a97 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 3df3c86568f4..caaeddace486 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 ff8cc4d1699d..c02760d973da 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 45bc3c580e94..d55766172637 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 1508d0e86778..8e5113b689ad 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 a09093f1f817..427e844218e2 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 c2d2b803d7df..e231e2a39683 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 e4b6c22e2174..8181f6c98449 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 b208f488c933..361a81678065 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 8272d71ffbbf..0b80c4af78bd 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 34d26dcbee32..d72281bd000c 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 bd1b2f035beb..daf17ef99341 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 897dcd6ad55c..b98a1cdbfbc0 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 bd1b2f035beb..daf17ef99341 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 428e13960221..6c576add300c 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 525e088d1e8d..15cbe4a0f66d 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 01358848099c..5e6cfcd968d8 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 5bd68671b7ee..dd3cbca34c2d 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 02a748b37080..5c554abdd04f 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 128dc232c331..45a4c6882c4a 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 aec317ef0946..71538de2712e 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 103276895d56..49a3862c875d 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 6cc7b137c14d..1d54bcf7ae6d 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 1cdd59045080..e52090813189 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 11c382ea62fa..fd3d5f2ff40c 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 d0618731a608..4b4af30a3389 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 d87d7a90689a..afc71b9fbdad 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 a5917e53c24e..7cbf3d24674a 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 6f77952f4ae5..9c4f86752c29 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 55e6efd5307f..dd8aeca70ab5 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 c76df7027c3b..ee7da4ee1b0c 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 a74e10e30eb1..5ae3e1ec4595 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 57c91a8704a6..602f60750676 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 5b6b456e782d..41952a77406e 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 387f8bbb720d..48ea63684213 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 a9b6d14798fe..f597e5dfe6e9 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 90306fd5367e..8e36eb052ee4 100644 --- a/core/res/res/values/attrs.xml +++ b/core/res/res/values/attrs.xml @@ -1832,6 +1832,9 @@ i <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 f10ba9680344..2a10bdd46d07 100644 --- a/data/keyboards/Generic.kl +++ b/data/keyboards/Generic.kl @@ -152,11 +152,11 @@ key 128 MEDIA_STOP # 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 e235a9983d5f..3ed6a788b640 100644 --- a/graphics/java/android/graphics/drawable/AnimatedStateListDrawable.java +++ b/graphics/java/android/graphics/drawable/AnimatedStateListDrawable.java @@ -379,6 +379,7 @@ public class AnimatedStateListDrawable extends StateListDrawable { 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 521c74b7ffc2..6bf3afd33846 100644 --- a/graphics/java/android/graphics/drawable/AnimationDrawable.java +++ b/graphics/java/android/graphics/drawable/AnimationDrawable.java @@ -291,6 +291,7 @@ public class AnimationDrawable extends DrawableContainer implements Runnable, An 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 1f7d9969c021..ac727344b8dd 100644 --- a/graphics/java/android/graphics/drawable/DrawableContainer.java +++ b/graphics/java/android/graphics/drawable/DrawableContainer.java @@ -29,6 +29,7 @@ import android.graphics.PixelFormat; 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 @@ public class DrawableContainer extends Drawable implements Drawable.Callback { 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 class DrawableContainer extends Drawable implements Drawable.Callback { */ 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 @@ public class DrawableContainer extends Drawable implements Drawable.Callback { 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 @@ public class DrawableContainer extends Drawable implements Drawable.Callback { 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 @@ public class DrawableContainer extends Drawable implements Drawable.Callback { mCheckedOpacity = false; mConstantPadding = null; - mPaddingChecked = false; - mComputedConstantSize = false; + mCheckedPadding = false; + mCheckedConstantSize = false; return pos; } @@ -863,6 +890,31 @@ public class DrawableContainer extends Drawable implements Drawable.Callback { 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 @@ public class DrawableContainer extends Drawable implements Drawable.Callback { mChildrenChangingConfigurations |= drawables[i].getChangingConfigurations(); } } + + updateDensity(theme.getResources()); } } @@ -941,7 +995,7 @@ public class DrawableContainer extends Drawable implements Drawable.Callback { return null; } - if ((mConstantPadding != null) || mPaddingChecked) { + if ((mConstantPadding != null) || mCheckedPadding) { return mConstantPadding; } @@ -961,7 +1015,7 @@ public class DrawableContainer extends Drawable implements Drawable.Callback { } } - mPaddingChecked = true; + mCheckedPadding = true; return (mConstantPadding = r); } @@ -974,7 +1028,7 @@ public class DrawableContainer extends Drawable implements Drawable.Callback { } public final int getConstantWidth() { - if (!mComputedConstantSize) { + if (!mCheckedConstantSize) { computeConstantSize(); } @@ -982,7 +1036,7 @@ public class DrawableContainer extends Drawable implements Drawable.Callback { } public final int getConstantHeight() { - if (!mComputedConstantSize) { + if (!mCheckedConstantSize) { computeConstantSize(); } @@ -990,7 +1044,7 @@ public class DrawableContainer extends Drawable implements Drawable.Callback { } public final int getConstantMinimumWidth() { - if (!mComputedConstantSize) { + if (!mCheckedConstantSize) { computeConstantSize(); } @@ -998,7 +1052,7 @@ public class DrawableContainer extends Drawable implements Drawable.Callback { } public final int getConstantMinimumHeight() { - if (!mComputedConstantSize) { + if (!mCheckedConstantSize) { computeConstantSize(); } @@ -1006,7 +1060,7 @@ public class DrawableContainer extends Drawable implements Drawable.Callback { } protected void computeConstantSize() { - mComputedConstantSize = true; + mCheckedConstantSize = true; createAllFutures(); @@ -1146,10 +1200,10 @@ public class DrawableContainer extends Drawable implements Drawable.Callback { */ 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 b01c643a2a01..4ce52d187dd4 100644 --- a/graphics/java/android/graphics/drawable/LevelListDrawable.java +++ b/graphics/java/android/graphics/drawable/LevelListDrawable.java @@ -88,7 +88,13 @@ public class LevelListDrawable extends DrawableContainer { 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 758410a58fa8..64a9eb5380cd 100644 --- a/graphics/java/android/graphics/drawable/StateListDrawable.java +++ b/graphics/java/android/graphics/drawable/StateListDrawable.java @@ -110,6 +110,7 @@ public class StateListDrawable extends DrawableContainer { 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 f1eaa23ef324..e161f3d1646c 100644 --- a/graphics/java/android/graphics/drawable/VectorDrawable.java +++ b/graphics/java/android/graphics/drawable/VectorDrawable.java @@ -603,7 +603,7 @@ public class VectorDrawable extends Drawable { 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 4d9f9b479343..94806cab2fc5 100644 --- a/libs/hwui/BakedOpRenderer.cpp +++ b/libs/hwui/BakedOpRenderer.cpp @@ -74,53 +74,64 @@ void BakedOpRenderer::endFrame(Info& info) { #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 b8b4426412e4..f45dbe41f308 100644 --- a/libs/hwui/BakedOpRenderer.h +++ b/libs/hwui/BakedOpRenderer.h @@ -65,7 +65,7 @@ public: * 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 e2201ca06a4b..ddb8c84e08f4 100644 --- a/libs/hwui/BakedOpState.h +++ b/libs/hwui/BakedOpState.h @@ -68,7 +68,7 @@ public: // 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 eca71c6e0e8d..6a6cc42043df 100644 --- a/libs/hwui/CanvasState.cpp +++ b/libs/hwui/CanvasState.cpp @@ -259,7 +259,7 @@ bool CanvasState::calculateQuickRejectForScissor(float left, float top, currentTransform()->mapRect(r); r.snapGeometryToPixelBoundaries(snapOut); - Rect clipRect(currentClipRect()); + Rect clipRect(currentRenderTargetClip()); clipRect.snapToPixelBoundaries(); if (!clipRect.intersects(r)) return true; @@ -287,7 +287,7 @@ bool CanvasState::quickRejectConservative(float left, float top, 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 be57f44210ef..4709ef41915f 100644 --- a/libs/hwui/CanvasState.h +++ b/libs/hwui/CanvasState.h @@ -147,7 +147,7 @@ public: 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 7c0e2570972a..c1417c451895 100644 --- a/libs/hwui/OpReorderer.cpp +++ b/libs/hwui/OpReorderer.cpp @@ -52,7 +52,8 @@ public: 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 @@ private: 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 @@ void OpReorderer::defer(int viewportWidth, int viewportHeight, const DisplayList * 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::deferImpl(const DisplayList& displayList) { 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 @@ void OpReorderer::onRenderNodeOp(const RenderNodeOp& op) { // 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 @@ static batchid_t tessellatedBatchId(const SkPaint& paint) { } 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); -} - -// 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]; - - 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; - } - } + currentLayer().deferUnmergeableOp(mAllocator, bakedStateOp, OpBatchType::Vertices); } -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); - } +// 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; + + // create a new layer, and push its index on the stack + mLayerStack.push_back(mLayerReorderers.size()); + mLayerReorderers.emplace_back(); + mLayerReorderers.back().beginLayerOp = &op; } -// 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)); +void OpReorderer::onEndLayerOp(const EndLayerOp& /* ignored */) { + mCanvasState.restore(); - mBatches.insert(mBatches.begin() + insertBatchIndex, targetBatch); + 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::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 6776a3c9fc18..73dc9af01f71 100644 --- a/libs/hwui/OpReorderer.h +++ b/libs/hwui/OpReorderer.h @@ -54,19 +54,63 @@ namespace OpBatchType { }; } -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 @@ public: */ #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 @@ private: 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; - - void deferUnmergeableOp(BakedOpState* op, batchid_t batchId); + // List of every deferred layer's render state. Replayed in reverse order to render a frame. + std::vector<LayerReorderer> mLayerReorderers; - // 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]; + /* + * 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; - // 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 cd03ac407d81..d4f65b635d4c 100644 --- a/libs/hwui/OpenGLRenderer.cpp +++ b/libs/hwui/OpenGLRenderer.cpp @@ -223,7 +223,7 @@ void OpenGLRenderer::resumeAfterLayer() { 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 @@ void OpenGLRenderer::calculateLayerBoundsAndClip(Rect& bounds, Rect& clip, bool 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::dirtyLayer(const float left, const float top, } 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 @@ void OpenGLRenderer::clearLayerRegions() { .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 @@ void OpenGLRenderer::clearLayerRegions() { /////////////////////////////////////////////////////////////////////////////// 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::setupMergedMultiDraw(const Rect* clipRect) { /////////////////////////////////////////////////////////////////////////////// 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 @@ void OpenGLRenderer::drawRenderNode(RenderNode* renderNode, Rect& dirty, int32_t return; } - DeferredDisplayList deferredList(mState.currentClipRect()); + DeferredDisplayList deferredList(mState.currentRenderTargetClip()); DeferStateStruct deferStruct(deferredList, *this, replayFlags); renderNode->defer(deferStruct, 0); @@ -1765,7 +1765,7 @@ void OpenGLRenderer::drawColor(int color, SkXfermode::Mode mode) { // 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 @@ void OpenGLRenderer::drawPosText(const char* text, int bytesCount, int count, } 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 @@ void OpenGLRenderer::drawText(const char* text, int bytesCount, int count, float 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 a69f0308ebc5..dd016372584a 100644 --- a/libs/hwui/RecordedOp.h +++ b/libs/hwui/RecordedOp.h @@ -41,7 +41,10 @@ struct Vertex; 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 @@ struct SimpleRectsOp : RecordedOp { // Filled, no AA (TODO: better name?) 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 3b413aaec0e0..1f113bc19ebb 100644 --- a/libs/hwui/RecordingCanvas.cpp +++ b/libs/hwui/RecordingCanvas.cpp @@ -73,6 +73,20 @@ SkCanvas* RecordingCanvas::asSkCanvas() { } // ---------------------------------------------------------------------------- +// 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 @@ void RecordingCanvas::restoreToCount(int saveCount) { 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 2179e4c24580..9c32b1a769dc 100644 --- a/libs/hwui/RecordingCanvas.h +++ b/libs/hwui/RecordingCanvas.h @@ -52,8 +52,8 @@ public: // ---------------------------------------------------------------------------- // 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 894a2bdf19df..351fbaa86a2a 100644 --- a/libs/hwui/RenderNode.cpp +++ b/libs/hwui/RenderNode.cpp @@ -929,7 +929,7 @@ void RenderNode::issueOperationsOfProjectedChildren(OpenGLRenderer& renderer, T& 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 f824cc020196..abef806a6975 100644 --- a/libs/hwui/RenderProperties.h +++ b/libs/hwui/RenderProperties.h @@ -203,8 +203,8 @@ public: 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 aeeda965c48f..4789b338d173 100644 --- a/libs/hwui/Snapshot.h +++ b/libs/hwui/Snapshot.h @@ -158,13 +158,12 @@ public: /** * 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 4c8dedfa5a15..cf96d44c286f 100644 --- a/libs/hwui/microbench/OpReordererBench.cpp +++ b/libs/hwui/microbench/OpReordererBench.cpp @@ -65,7 +65,7 @@ void BM_OpReorderer_deferAndRender::Run(int iters) { 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 238cf06e77f6..f5714265032a 100644 --- a/libs/hwui/renderthread/CanvasContext.cpp +++ b/libs/hwui/renderthread/CanvasContext.cpp @@ -323,7 +323,7 @@ void CanvasContext::draw() { 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 e1249fbd2e96..d02f89dd7088 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++); + } + 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++); } - UNSUPPORTED_OP(Info, RenderNodeOp) - UNSUPPORTED_OP(Info, SimpleRectsOp) - static void startFrame(Info& info) {} - static void endFrame(Info& info) {} }; TEST(OpReorderer, simple) { auto dl = TestUtils::createDisplayList<RecordingCanvas>(100, 200, [](RecordingCanvas& canvas) { @@ -54,28 +97,39 @@ TEST(OpReorderer, simple) { canvas.drawRect(0, 0, 100, 200, SkPaint()); canvas.drawBitmap(bitmap, 10, 10, nullptr); }); + OpReorderer reorderer; + reorderer.defer(200, 200, *dl); + Info 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<SimpleReceiver>(&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 @@ TEST(OpReorderer, simpleBatching) { 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 @@ public: 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 @@ TEST(OpReorderer, renderNode) { 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 @@ TEST(OpReorderer, clipped) { 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 ce25fc6189b4..c0231235027a 100644 --- a/libs/hwui/unit_tests/RecordingCanvasTests.cpp +++ b/libs/hwui/unit_tests/RecordingCanvasTests.cpp @@ -24,11 +24,11 @@ namespace android { 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 @@ TEST(RecordingCanvas, backgroundAndImage) { 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 80d83a2e47a1..99ecc9b99284 100644 --- a/libs/hwui/unit_tests/TestUtils.h +++ b/libs/hwui/unit_tests/TestUtils.h @@ -31,6 +31,12 @@ namespace uirenderer { #define EXPECT_MATRIX_APPROX_EQ(a, b) \ EXPECT_TRUE(TestUtils::matricesAreApproxEqual(a, b)) +#define EXPECT_RECT_APPROX_EQ(a, b) \ + EXPECT_TRUE(MathUtils::areEqual(a.left, b.left) \ + && MathUtils::areEqual(a.top, b.top) \ + && MathUtils::areEqual(a.right, b.right) \ + && MathUtils::areEqual(a.bottom, b.bottom)); + class TestUtils { public: static bool matricesAreApproxEqual(const Matrix4& a, const Matrix4& b) { diff --git a/libs/input/PointerController.cpp b/libs/input/PointerController.cpp index 0f86bc614b2b..4a1d7e7b5c64 100644 --- a/libs/input/PointerController.cpp +++ b/libs/input/PointerController.cpp @@ -78,7 +78,7 @@ PointerController::PointerController(const sp<PointerControllerPolicyInterface>& 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 @@ void PointerController::updatePointerLocked() { 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 308ff1242064..24a16819384e 100644 --- a/libs/input/PointerController.h +++ b/libs/input/PointerController.h @@ -58,7 +58,8 @@ protected: 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 90e2b7e653c6..7d7a110864d9 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 d1243200dd5d..231e110fa09d 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 c576669d8958..085df352bb4a 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 766268983ccc..45a890702c0f 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.LayoutManager; 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.util.TypedValue; import android.view.ActionMode; import android.view.DragEvent; import android.view.GestureDetector; +import android.view.KeyEvent; import android.view.LayoutInflater; import android.view.Menu; import android.view.MenuItem; import android.view.MotionEvent; import android.view.View; -import android.view.View.OnLayoutChangeListener; import android.view.ViewGroup; import android.view.ViewParent; import android.widget.ImageView; @@ -97,10 +98,12 @@ import com.android.documentsui.RecentsProvider.StateColumns; import com.android.documentsui.model.DocumentInfo; import com.android.documentsui.model.DocumentStack; import com.android.documentsui.model.RootInfo; +import com.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 @@ public class DirectoryFragment extends Fragment { 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 @@ public class DirectoryFragment extends Fragment { // 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 @@ public class DirectoryFragment extends Fragment { } }); + // 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 @@ public class DirectoryFragment extends Fragment { } 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 @@ public class DirectoryFragment extends Fragment { 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 @@ public class DirectoryFragment extends Fragment { // 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 @@ public class DirectoryFragment extends Fragment { // 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 @@ public class DirectoryFragment extends Fragment { 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 @@ public class DirectoryFragment extends Fragment { 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 @@ public class DirectoryFragment extends Fragment { } 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 @@ public class DirectoryFragment extends Fragment { } List<DocumentInfo> getDocumentsMarkedForDeletion() { - final int size = mMarkedForDeletion.size(); - List<DocumentInfo> docs = new ArrayList<>(size); - - for (int i = 0; i < size; ++i) { - final int position = mMarkedForDeletion.keyAt(i); - checkState(position < mCursorCount); - mCursor.moveToPosition(position); - final DocumentInfo doc = DocumentInfo.fromDirectoryCursor(mCursor); - docs.add(doc); + synchronized (mPendingDelete) { + final int size = mMarkedForDeletion.size(); + List<DocumentInfo> docs = new ArrayList<>(size); + + for (int i = 0; i < size; ++i) { + final int position = mMarkedForDeletion.keyAt(i); + checkState(position < mCursorCount); + mCursor.moveToPosition(position); + final DocumentInfo doc = DocumentInfo.fromDirectoryCursor(mCursor); + docs.add(doc); + } + return docs; } - return docs; } /** @@ -1822,17 +1880,23 @@ public class DirectoryFragment extends Fragment { * @param selected A selection representing the files to delete. */ void markForDeletion(Selection selected) { - // Only one deletion operation at a time. - checkState(mMarkedForDeletion.size() == 0); - // There should never be more to delete than what exists. - checkState(mCursorCount >= selected.size()); - - final int size = selected.size(); - for (int i = 0; i < size; ++i) { - int position = selected.get(i); - if (DEBUG) Log.d(TAG, "Marked position " + position + " for deletion"); - mMarkedForDeletion.append(position, true); - mViewAdapter.notifyItemRemoved(position); + synchronized (mPendingDelete) { + mPendingDelete = true; + // Only one deletion operation at a time. + checkState(mMarkedForDeletion.size() == 0); + // There should never be more to delete than what exists. + checkState(mCursorCount >= selected.size()); + + int[] positions = selected.getAll(); + Arrays.sort(positions); + + // Walk backwards through the set, since we're removing positions. + // Otherwise, positions would change after the first modification. + for (int p = positions.length - 1; p >= 0; p--) { + mMarkedForDeletion.append(positions[p], true); + mViewAdapter.notifyItemRemoved(positions[p]); + if (DEBUG) Log.d(TAG, "Scheduled " + positions[p] + " for delete."); + } } } @@ -1841,17 +1905,24 @@ public class DirectoryFragment extends Fragment { * 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 @@ public class DirectoryFragment extends Fragment { * 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 @@ public class DirectoryFragment extends Fragment { } else { if (DEBUG) Log.d(TAG, "Deletion task completed successfully."); } - mMarkedForDeletion.clear(); + resetDeleteData(); mListener.onCompletion(); } @@ -1957,6 +2035,18 @@ public class DirectoryFragment extends Fragment { } } + 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 000000000000..5c40f1b20d74 --- /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 87c037dcd738..ef53d53c2f78 100644 --- a/packages/DocumentsUI/src/com/android/documentsui/MultiSelectManager.java +++ b/packages/DocumentsUI/src/com/android/documentsui/MultiSelectManager.java @@ -183,6 +183,10 @@ public final class MultiSelectManager implements View.OnKeyListener { 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 @@ public final class MultiSelectManager implements View.OnKeyListener { */ @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 @@ public final class MultiSelectManager implements View.OnKeyListener { private void clearSelectionQuietly() { mRanger = null; - if (mSelection.isEmpty()) { + if (!hasSelection()) { return; } if (mIntermediateSelection == null) { @@ -292,7 +296,7 @@ public final class MultiSelectManager implements View.OnKeyListener { @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 @@ public final class MultiSelectManager implements View.OnKeyListener { * * @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 @@ public final class MultiSelectManager implements View.OnKeyListener { if (!canSelect) { return; } - if (mSingleSelect && !mSelection.isEmpty()) { + if (mSingleSelect && hasSelection()) { clearSelectionQuietly(); } @@ -398,7 +402,7 @@ public final class MultiSelectManager implements View.OnKeyListener { if (selected) { boolean canSelect = notifyBeforeItemStateChange(i, true); if (canSelect) { - if (mSingleSelect && !mSelection.isEmpty()) { + if (mSingleSelect && hasSelection()) { clearSelectionQuietly(); } selectAndNotify(i); @@ -597,6 +601,14 @@ public final class MultiSelectManager implements View.OnKeyListener { 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 @@ public final class MultiSelectManager implements View.OnKeyListener { } /** + * 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 @@ public final class MultiSelectManager implements View.OnKeyListener { } 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 @@ public final class MultiSelectManager implements View.OnKeyListener { 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 ace9e275c776..36d880a06892 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.MultiSelectManager.Selection; import com.android.documentsui.model.DocumentInfo; import java.util.List; -import java.util.concurrent.CountDownLatch; -import java.util.concurrent.TimeUnit; - - public class DirectoryFragmentModelTest extends AndroidTestCase { @@ -117,15 +113,6 @@ public class DirectoryFragmentModelTest extends AndroidTestCase { 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 @@ public class DirectoryFragmentModelTest extends AndroidTestCase { }; } - 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 @@ public class DirectoryFragmentModelTest extends AndroidTestCase { 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 cc510a949f51..9e874bda2979 100644 --- a/packages/MtpDocumentsProvider/src/com/android/mtp/CursorHelper.java +++ b/packages/MtpDocumentsProvider/src/com/android/mtp/CursorHelper.java @@ -18,6 +18,7 @@ package com.android.mtp; import android.content.res.Resources; import android.database.MatrixCursor; +import android.media.MediaFile; import android.mtp.MtpConstants; import android.mtp.MtpObjectInfo; import android.provider.DocumentsContract; @@ -70,30 +71,18 @@ final class CursorHelper { } 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 9d5850bd90ce..7883e6198bbf 100644 --- a/packages/MtpDocumentsProvider/src/com/android/mtp/MtpDocumentsProvider.java +++ b/packages/MtpDocumentsProvider/src/com/android/mtp/MtpDocumentsProvider.java @@ -242,7 +242,7 @@ public class MtpDocumentsProvider extends DocumentsProvider { 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 314b3c4bf326..0cc2a67a3be3 100644 --- a/packages/SystemUI/Android.mk +++ b/packages/SystemUI/Android.mk @@ -3,7 +3,7 @@ include $(CLEAR_VARS) 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 bbb099ec2fdc..5d622a069412 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 59fed5ba7d2a..f430fa516ffd 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 0b8da8374d77..61870707882b 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 5661657fe9d1..cbc92f2d985b 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 a43d520085ae..96ee39756513 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.BluetoothManager; import android.bluetooth.le.BluetoothLeScanner; import android.bluetooth.le.ScanCallback; import android.bluetooth.le.ScanFilter; +import android.bluetooth.le.ScanRecord; import android.bluetooth.le.ScanResult; import android.bluetooth.le.ScanSettings; import android.content.ContentResolver; @@ -230,17 +231,19 @@ public class KeyboardUI extends SystemUI implements InputManager.OnTabletModeCha } 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 @@ public class KeyboardUI extends SystemUI implements InputManager.OnTabletModeCha } 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 @@ public class KeyboardUI extends SystemUI implements InputManager.OnTabletModeCha 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 32c906e46afb..0e4a4e53a80b 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 @@ public class PagedTileLayout extends ViewPager implements QSTileLayout { @Override public void setTileVisibility(TileRecord tile, int visibility) { tile.tileView.setVisibility(visibility); -// // TODO: Do something smarter here. -// distributeTiles(); } @Override @@ -104,8 +102,7 @@ public class PagedTileLayout extends ViewPager implements QSTileLayout { 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 @@ public class PagedTileLayout extends ViewPager implements QSTileLayout { 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 class PagedTileLayout extends ViewPager implements QSTileLayout { } 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 @@ public class PagedTileLayout extends ViewPager implements QSTileLayout { 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 53d040270756..a2d9ef020985 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 @@ public class QuickQSPanel extends QSPanel { 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 b8342e2ada7c..12a099db5ee0 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 class TileLayout extends ViewGroup implements QSTileLayout { 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 @@ public class TileLayout extends ViewGroup implements QSTileLayout { } } + 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 012633c6860f..166927881969 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 @@ public class NonPagedTileLayout extends LinearLayout implements QSTileLayout, On 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 @@ public class NonPagedTileLayout extends LinearLayout implements QSTileLayout, On 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 601961b41a16..fe8d78bb6ff4 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 @@ public class QSCustomizer extends LinearLayout implements OnMenuItemClickListene 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 @@ public class QSCustomizer extends LinearLayout implements OnMenuItemClickListene @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 000000000000..79eca30d2c9a --- /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 000000000000..6b491953a45a --- /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 aee3623352ae..ae79fe28f44e 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. + * An implementation of the SystemUI recents component, which supports both system and secondary + * users. */ -@interface ProxyFromAnyToSystemUser {} - -/** A proxy implementation for the recents component */ public class Recents extends SystemUI - implements ActivityOptions.OnAnimationStartedListener, RecentsComponent { + implements RecentsComponent { - public final static int EVENT_BUS_PRIORITY = 1; + private final static String TAG = "Recents"; + private final static boolean DEBUG = false; - 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"; + public final static int EVENT_BUS_PRIORITY = 1; + public final static int BIND_TO_SYSTEM_USER_RETRY_DELAY = 5000; - // Owner proxy events - final public static String ACTION_PROXY_NOTIFY_RECENTS_VISIBLITY_TO_OWNER = - "action_notify_recents_visibility_change"; - final public static String ACTION_PROXY_SCREEN_PINNING_REQUEST_TO_OWNER = - "action_screen_pinning_request"; + private SystemServicesProxy mSystemServicesProxy; + private Handler mHandler; + private RecentsImpl mImpl; - 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 system user, this is the callbacks instance we return to each secondary user + private RecentsSystemUser mSystemUserCallbacks; - final static int sMinToggleDelay = 350; + // Only for secondary users, this is the callbacks instance provided by the system user to make + // calls back + private IRecentsSystemUserCallbacks mCallbacksToSystemUser; - 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"; + // The set of runnables to run after binding to the system user's service. + private final ArrayList<Runnable> mOnConnectRunnables = new ArrayList<>(); - /** - * An implementation of ITaskStackListener, that allows us to listen for changes to the system - * task stacks and update recents accordingly. - */ - class TaskStackListenerImpl extends ITaskStackListener.Stub implements Runnable { - Handler mHandler; + // Only for secondary users, this is the death handler for the binder from the system user + private final IBinder.DeathRecipient mCallbacksToSystemUserDeathRcpt = new IBinder.DeathRecipient() { + @Override + public void binderDied() { + mCallbacksToSystemUser = null; - public TaskStackListenerImpl(Handler handler) { - mHandler = handler; + // Retry after a fixed duration + mHandler.postDelayed(new Runnable() { + @Override + public void run() { + registerWithSystemUser(); + } + }, BIND_TO_SYSTEM_USER_RETRY_DELAY); } + }; + // 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 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; + 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); } - launchOpts.numVisibleTasks = 2; - launchOpts.numVisibleTaskThumbnails = 2; - launchOpts.onlyLoadForCache = true; - launchOpts.onlyLoadPausedActivities = true; - loader.loadTasks(mContext, plan, launchOpts); + + // 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); } - } - /** - * A proxy for Recents events which happens strictly for the owner. - */ - class RecentsOwnerEventProxyReceiver extends BroadcastReceiver { @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 onServiceDisconnected(ComponentName name) { + // Do nothing } - } - - 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() { - } + }; /** - * 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; - } - - /** Creates a new broadcast intent */ - static Intent createLocalBroadcastIntent(Context context, String action) { - Intent intent = new Intent(action); - intent.setPackage(context.getPackageName()); - intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT | - Intent.FLAG_RECEIVER_FOREGROUND); - return intent; + public IBinder getSystemUserCallbacks() { + return mSystemUserCallbacks; } - /** 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); - - // Register the task stack listener - mTaskStackListener = new TaskStackListenerImpl(mHandler); - mSystemServicesProxy.registerTaskStackListener(mTaskStackListener); - - // Only the owner has the callback to update the SysUI visibility flags, so all non-owner - // instances of RecentsComponent needs to notify the owner when the visibility - // changes. - if (mSystemServicesProxy.isForegroundUserSystem()) { - mProxyBroadcastReceiver = new RecentsOwnerEventProxyReceiver(); - IntentFilter filter = new IntentFilter(); - filter.addAction(Recents.ACTION_PROXY_NOTIFY_RECENTS_VISIBLITY_TO_OWNER); - filter.addAction(Recents.ACTION_PROXY_SCREEN_PINNING_REQUEST_TO_OWNER); - mContext.registerReceiverAsUser(mProxyBroadcastReceiver, UserHandle.CURRENT, filter, - null, mHandler); + mImpl = new RecentsImpl(mContext); + + // Register with the event bus + EventBus.getDefault().register(this, EVENT_BUS_PRIORITY); + + // 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); - } - } - - void showRecentsInternal(boolean triggeredFromAltTab) { - mTriggeredFromAltTab = triggeredFromAltTab; - - try { - showRecentsActivity(); - } catch (ActivityNotFoundException e) { - Console.logRawError("Failed to launch RecentAppsIntent", e); + 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); + } + } } } - /** 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); - } - } - - 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); + 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); + } + } } } - /** 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); - } - } - - void toggleRecentsInternal() { - mTriggeredFromAltTab = false; - - try { - toggleRecentsActivity(); - } catch (ActivityNotFoundException e) { - Console.logRawError("Failed to launch RecentAppsIntent", e); + 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); + } + } } } - /** 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)); + int currentUser = mSystemServicesProxy.getCurrentUser(); + if (mSystemServicesProxy.isSystemUser(currentUser)) { + mImpl.cancelPreloadingRecents(); + } else { + if (mSystemUserCallbacks != null) { + IRecentsNonSystemUserCallbacks callbacks = + mSystemUserCallbacks.getNonSystemUserRecentsForUser(currentUser); + if (callbacks != null) { + try { + callbacks.cancelPreloadingRecents(); + } catch (RemoteException e) { + Log.e(TAG, "Callback failed", e); + } } else { - mSystemServicesProxy.startInPlaceAnimationOnFrontMostApplication( - ActivityOptions.makeCustomInPlaceAnimation(mContext, - R.anim.recents_launch_prev_affiliated_task_bounce)); + Log.e(TAG, "No SystemUI callbacks found for user: " + currentUser); } } - return; - } - - // Keep track of actually launched affiliated tasks - MetricsLogger.count(mContext, "overview_affiliated_task_launch", 1); - - // Launch the task - if (toTask.isActive) { - // Bring an active task to the foreground - mSystemServicesProxy.moveTaskToFront(toTask.key.id, launchOpts); - } else { - mSystemServicesProxy.startActivityFromRecents(mContext, toTask.key.id, - toTask.activityLabel, launchOpts); } } @Override public void 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); - } - - /** Updates on configuration change. */ - @ProxyFromSystemToCurrentUser - public void onConfigurationChanged(Configuration newConfig) { - if (mSystemServicesProxy.isForegroundUserSystem()) { - configurationChanged(); - } 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(); + mImpl.showPrevAffiliatedTask(); } /** - * 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) + * Updates on configuration change. */ - 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; + public void onConfigurationChanged(Configuration newConfig) { + int currentUser = mSystemServicesProxy.getCurrentUser(); + if (mSystemServicesProxy.isSystemUser(currentUser)) { + mImpl.onConfigurationChanged(); } 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); + if (mSystemUserCallbacks != null) { + IRecentsNonSystemUserCallbacks callbacks = + mSystemUserCallbacks.getNonSystemUserRecentsForUser(currentUser); + if (callbacks != null) { + try { + callbacks.onConfigurationChanged(); + } catch (RemoteException e) { + Log.e(TAG, "Callback failed", e); + } + } else { + Log.e(TAG, "No SystemUI callbacks found for user: " + currentUser); + } + } } } /** - * 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. + * Handle Recents activity visibility changed. */ - 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); + 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); + } + } + }); } - 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. + * Handle screen pinning request. */ - 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; + public final void onBusEvent(final ScreenPinningRequestEvent event) { + int processUser = event.systemServicesProxy.getProcessUser(); + if (event.systemServicesProxy.isSystemUser(processUser)) { + mImpl.onStartScreenPinning(event.applicationContext); } 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); + postToSystemUser(new Runnable() { + @Override + public void run() { + try { + mCallbacksToSystemUser.startScreenPinning(); + } catch (RemoteException e) { + Log.e(TAG, "Callback failed", e); + } + } + }); } - - // If both the screenshot and thumbnail fails, then just fall back to the default transition - return getUnknownTransitionActivityOptions(); } /** - * Preloads the icon of a task. + * Attempts to register with the system user. */ - 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); - } - + private void registerWithSystemUser() { + final int processUser = mSystemServicesProxy.getProcessUser(); + postToSystemUser(new Runnable() { @Override - protected void onPostExecute(Bitmap bitmap) { - mThumbnailTransitionBitmapCache = bitmap; - mThumbnailTransitionBitmapCacheKey = toTask; + public void run() { + try { + mCallbacksToSystemUser.registerNonSystemUserCallbacks(mImpl, processUser); + } catch (RemoteException e) { + Log.e(TAG, "Failed to register", e); + } } - }.execute(); + }); } /** - * Draws the header of a task used for the window animation into a bitmap. + * Runs the runnable in the system user's Recents context, connecting to the service if + * necessary. */ - 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); + 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); } - } - 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); + runAndFlushOnConnectRunnables(); } } /** - * Returns the preloaded load plan and invalidates it. + * Runs all the queued runnables after a service connection is made. */ - 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; - } - - // 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); + 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 e647c1f6dcfe..fa1c88719764 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.internal.logging.MetricsLogger; import com.android.systemui.R; import com.android.systemui.recents.events.EventBus; import com.android.systemui.recents.events.activity.AppWidgetProviderChangedEvent; +import com.android.systemui.recents.events.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 @@ public class RecentsActivity extends Activity implements RecentsView.RecentsView @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 @@ public class RecentsActivity extends Activity implements RecentsView.RecentsView // 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 @@ public class RecentsActivity extends Activity implements RecentsView.RecentsView 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 @@ public class RecentsActivity extends Activity implements RecentsView.RecentsView 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 class RecentsActivity extends Activity implements RecentsView.RecentsView 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 000000000000..2c7608750628 --- /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 000000000000..fb215001f044 --- /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 000000000000..39d0d59ab76e --- /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 5eefbc71b03d..000000000000 --- 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 000000000000..898d1fcd3588 --- /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 000000000000..5cb4ccf173d2 --- /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 568d2b1e3f2e..dab2c65c50c3 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.ParcelFileDescriptor; import android.os.RemoteException; import android.os.SystemProperties; import android.os.UserHandle; +import android.os.UserManager; import android.provider.Settings; import android.util.Log; import android.util.MutableBoolean; @@ -66,7 +64,7 @@ import com.android.internal.app.AssistUtils; 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.Iterator; 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 @@ public class SystemServicesProxy { IPackageManager mIpm; AssistUtils mAssistUtils; WindowManager mWm; + UserManager mUm; Display mDisplay; String mRecentsPackage; ComponentName mAssistComponent; @@ -122,6 +124,7 @@ public class SystemServicesProxy { 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 @@ public class SystemServicesProxy { 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 @@ public class SystemServicesProxy { } /** - * 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 4322f1a3e5b9..36731319e9e0 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 @@ class TaskStackViewTouchHandler implements SwipeHelper.Callback { // 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 540b9d003a17..fa9c4bbcb485 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 @@ public class PhoneStatusBarPolicy implements Callback { } }; + 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 @@ public class PhoneStatusBarPolicy implements Callback { } } 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 01f06679cb30..662dbd909a37 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.AlarmManager; 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.ImageView; 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 @@ public class QuickStatusBarHeader extends BaseStatusBarHeader implements 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 56f6036d8ab6..a515f23f33ea 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 @@ public class HeadsUpManager implements ViewTreeObserver.OnComputeInternalInsetsL @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 d6d17cb10c2f..fdc254312157 100644 --- a/packages/SystemUI/tests/Android.mk +++ b/packages/SystemUI/tests/Android.mk @@ -22,7 +22,9 @@ LOCAL_PROTOC_FLAGS := -I$(LOCAL_PATH)/.. 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 000000000000..0ea3e9102d83 --- /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 000000000000..b1a0963e3673 --- /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 b9759dd2a479..c712a568c480 100644 --- a/services/core/java/com/android/server/ConnectivityService.java +++ b/services/core/java/com/android/server/ConnectivityService.java @@ -1450,10 +1450,7 @@ public class ConnectivityService extends IConnectivityManager.Stub } 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 4d325991c8d9..85187c776401 100644 --- a/services/core/java/com/android/server/MountService.java +++ b/services/core/java/com/android/server/MountService.java @@ -2442,8 +2442,13 @@ class MountService extends IMountService.Stub } 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 494913810d42..465118c2754c 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 @@ public final class ActiveServices { 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 @@ public final class ActiveServices { 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 @@ public final class ActiveServices { // 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 7df50d5cefa1..bb1b6b8f2aa9 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 @@ public final class ActivityManagerService extends ActivityManagerNative } 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 @@ public final class ActivityManagerService extends ActivityManagerNative 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 @@ public final class ActivityManagerService extends ActivityManagerNative // 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 @@ public final class ActivityManagerService extends ActivityManagerNative 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 @@ public final class ActivityManagerService extends ActivityManagerNative } } } + if (adj == ProcessList.VISIBLE_APP_ADJ) { + adj += minLayer; + } } if (adj > ProcessList.PERCEPTIBLE_APP_ADJ) { @@ -18326,11 +18336,11 @@ public final class ActivityManagerService extends ActivityManagerNative && 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 @@ public final class ActivityManagerService extends ActivityManagerNative 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 a59f7ef8bc48..172147070c81 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 @@ final class ActivityStack { 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 @@ final class ActivityStack { 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 @@ final class ActivityStack { 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 2103b60231d7..54ac58ab07d0 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 @@ public final class ActivityStackSupervisor implements DisplayListener { // 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 @@ public final class ActivityStackSupervisor implements DisplayListener { } } + 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 960cbf15a96e..6de85794c8ef 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.SystemClock; 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 @@ public final class BroadcastQueue { 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 @@ public final class BroadcastQueue { 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 @@ public final class BroadcastQueue { 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 @@ public final class BroadcastQueue { } pw.println(" Pending broadcast [" + mQueueName + "]:"); if (mPendingBroadcast != null) { - mPendingBroadcast.dump(pw, " "); + mPendingBroadcast.dump(pw, " ", sdf); } else { pw.println(" (null)"); } @@ -1366,7 +1368,7 @@ public final class BroadcastQueue { 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 @@ public final class BroadcastQueue { } // 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 @@ public final class BroadcastQueue { 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 1fbfd9feff8e..b42bcff15f83 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.PrintWriterPrinter; 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 @@ final class BroadcastRecord extends Binder { 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 @@ final class BroadcastRecord extends Binder { 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 0e249527a118..b49370b16c5d 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 @@ final class ProcessList { // 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 @@ final class ProcessList { // 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 @@ final class ProcessList { // 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 @@ final class ProcessList { // 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 bd31a218f4c1..08203c55b5d9 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 @@ final class ProcessRecord { 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 @@ final class ProcessRecord { } } } - + ProcessRecord(BatteryStatsImpl _batteryStats, ApplicationInfo _info, String _processName, int _uid) { mBatteryStats = _batteryStats; @@ -413,8 +413,8 @@ final class ProcessRecord { 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 @@ final class ProcessRecord { toShortString(sb); return shortStringName = sb.toString(); } - + void toShortString(StringBuilder sb) { sb.append(pid); sb.append(':'); @@ -585,7 +585,7 @@ final class ProcessRecord { } } } - + public String toString() { if (stringName != null) { return stringName; @@ -695,7 +695,7 @@ final class ProcessRecord { 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 1999f498e409..fe87a9332309 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 @@ final class TaskRecord { // 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 88ef366c43fb..a4d5bce3363c 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 @@ public class EventConditionProvider extends SystemConditionProviderService { } 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 8176aff89fc0..8abc8fcd3009 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 @@ final class DefaultPermissionGrantPolicy { grantRuntimePermissionsLPw(wearHomePackage, PHONE_PERMISSIONS, true, userId); grantRuntimePermissionsLPw(wearHomePackage, MICROPHONE_PERMISSIONS, false, userId); + grantRuntimePermissionsLPw(wearHomePackage, LOCATION_PERMISSIONS, false, + userId); } } @@ -596,7 +598,10 @@ final class DefaultPermissionGrantPolicy { 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 6553dc393194..496653ef106c 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 @@ public class PackageManagerService extends IPackageManager.Stub { + 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 @@ public class PackageManagerService extends IPackageManager.Stub { 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 @@ public class PackageManagerService extends IPackageManager.Stub { 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 @@ public class PackageManagerService extends IPackageManager.Stub { 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 @@ public class PackageManagerService extends IPackageManager.Stub { 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 @@ public class PackageManagerService extends IPackageManager.Stub { * 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 @@ public class PackageManagerService extends IPackageManager.Stub { // 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 @@ public class PackageManagerService extends IPackageManager.Stub { + 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 @@ public class PackageManagerService extends IPackageManager.Stub { + 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 61d26767cfb6..d148a4ff1f2e 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 @@ final class Settings { 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 80697edd50e5..b36a22e4461f 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 @@ public class UserManagerService extends IUserManager.Stub { @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 class UserManagerService extends IUserManager.Stub { 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 @@ public class UserManagerService extends IUserManager.Stub { @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 @@ public class UserManagerService extends IUserManager.Stub { @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 ee7c88a90ae1..ecc1f2c16598 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 @@ class WindowStateAnimator { 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 8cb0a1310ec5..be190cbef47d 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 @@ public: /* --- 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 @@ void NativeInputManager::setPointerIconShape(int32_t iconId) { 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 @@ void NativeInputManager::loadPointerResources(PointerResources* outResources) { &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 4748c96dcdda..8385685cfcdb 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 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { 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 703564c5d878..5ecd2b539935 100644 --- a/telecomm/java/android/telecom/PhoneAccount.java +++ b/telecomm/java/android/telecom/PhoneAccount.java @@ -52,6 +52,32 @@ import java.util.MissingResourceException; 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 @@ public final class PhoneAccount implements Parcelable { 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 d22727d14829..6b1b6296262a 100644 --- a/telephony/java/android/telephony/TelephonyManager.java +++ b/telephony/java/android/telephony/TelephonyManager.java @@ -2874,7 +2874,7 @@ public class TelephonyManager { /** * 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 @@ public class TelephonyManager { * 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} |