diff options
Diffstat (limited to 'core/java')
18 files changed, 209 insertions, 94 deletions
diff --git a/core/java/android/app/ActivityManagerNative.java b/core/java/android/app/ActivityManagerNative.java index 3035e3d..2bb4e76 100644 --- a/core/java/android/app/ActivityManagerNative.java +++ b/core/java/android/app/ActivityManagerNative.java @@ -2217,6 +2217,14 @@ public abstract class ActivityManagerNative extends Binder implements IActivityM return true; } + case IS_SCREEN_CAPTURE_ALLOWED_ON_CURRENT_ACTIVITY_TRANSACTION: { + data.enforceInterface(IActivityManager.descriptor); + boolean res = isScreenCaptureAllowedOnCurrentActivity(); + reply.writeNoException(); + reply.writeInt(res ? 1 : 0); + return true; + } + case KILL_UID_TRANSACTION: { data.enforceInterface(IActivityManager.descriptor); int uid = data.readInt(); @@ -5409,6 +5417,18 @@ class ActivityManagerProxy implements IActivityManager return res; } + public boolean isScreenCaptureAllowedOnCurrentActivity() throws RemoteException { + Parcel data = Parcel.obtain(); + Parcel reply = Parcel.obtain(); + data.writeInterfaceToken(IActivityManager.descriptor); + mRemote.transact(IS_SCREEN_CAPTURE_ALLOWED_ON_CURRENT_ACTIVITY_TRANSACTION, data, reply, 0); + reply.readException(); + boolean res = reply.readInt() != 0; + data.recycle(); + reply.recycle(); + return res; + } + public void killUid(int uid, String reason) throws RemoteException { Parcel data = Parcel.obtain(); Parcel reply = Parcel.obtain(); diff --git a/core/java/android/app/IActivityManager.java b/core/java/android/app/IActivityManager.java index 0328708..1423e4b 100644 --- a/core/java/android/app/IActivityManager.java +++ b/core/java/android/app/IActivityManager.java @@ -441,6 +441,8 @@ public interface IActivityManager extends IInterface { public boolean launchAssistIntent(Intent intent, int requestType, String hint, int userHandle, Bundle args) throws RemoteException; + public boolean isScreenCaptureAllowedOnCurrentActivity() throws RemoteException; + public void killUid(int uid, String reason) throws RemoteException; public void hang(IBinder who, boolean allowRestart) throws RemoteException; @@ -852,4 +854,6 @@ public interface IActivityManager extends IInterface { int KEYGUARD_GOING_AWAY_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+296; int REGISTER_UID_OBSERVER_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+297; int UNREGISTER_UID_OBSERVER_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+298; + int IS_SCREEN_CAPTURE_ALLOWED_ON_CURRENT_ACTIVITY_TRANSACTION + = IBinder.FIRST_CALL_TRANSACTION+299; } diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java index 83e06d6..d53157b 100644 --- a/core/java/android/app/admin/DevicePolicyManager.java +++ b/core/java/android/app/admin/DevicePolicyManager.java @@ -215,7 +215,7 @@ public class DevicePolicyManager { * <p>This component is set as device owner and active admin when device owner provisioning is * started by an intent with action {@link #ACTION_PROVISION_MANAGED_DEVICE} or by an NFC * message containing an NFC record with MIME type - * {@link #MIME_TYPE_PROVISIONING_NFC_V2}. For the NFC record, the component name should be + * {@link #MIME_TYPE_PROVISIONING_NFC}. For the NFC record, the component name should be * flattened to a string, via {@link ComponentName#flattenToShortString()}. * * @see DeviceAdminReceiver @@ -386,7 +386,7 @@ public class DevicePolicyManager { * {@link #EXTRA_PROVISIONING_DEVICE_ADMIN_PACKAGE_DOWNLOAD_LOCATION} if the version of the * installed package is less than this version code. * - * <p>Use in an NFC record with {@link #MIME_TYPE_PROVISIONING_NFC_V2} that starts device owner + * <p>Use in an NFC record with {@link #MIME_TYPE_PROVISIONING_NFC} that starts device owner * provisioning via an NFC bump. */ public static final String EXTRA_PROVISIONING_DEVICE_ADMIN_MINIMUM_VERSION_CODE @@ -461,7 +461,7 @@ public class DevicePolicyManager { * A boolean extra indicating whether device encryption can be skipped as part of Device Owner * provisioning. * - * <p>Use in an NFC record with {@link #MIME_TYPE_PROVISIONING_NFC_V2} or an intent with action + * <p>Use in an NFC record with {@link #MIME_TYPE_PROVISIONING_NFC} or an intent with action * {@link #ACTION_PROVISION_MANAGED_DEVICE} that starts device owner provisioning. */ public static final String EXTRA_PROVISIONING_SKIP_ENCRYPTION = @@ -558,12 +558,17 @@ public class DevicePolicyManager { = "android.app.extra.PROVISIONING_DEVICE_INITIALIZER_SIGNATURE_CHECKSUM"; /** - * This MIME type is used for starting the Device Owner provisioning that does not require - * provisioning features introduced in Android API level - * {@link android.os.Build.VERSION_CODES#MNC} or later levels. + * This MIME type is used for starting the Device Owner provisioning. * - * <p>For more information about the provisioning process see - * {@link #MIME_TYPE_PROVISIONING_NFC_V2}. + * <p>During device owner provisioning a device admin app is set as the owner of the device. + * A device owner has full control over the device. The device owner can not be modified by the + * user and the only way of resetting the device is if the device owner app calls a factory + * reset. + * + * <p> A typical use case would be a device that is owned by a company, but used by either an + * employee or client. + * + * <p> The NFC message should be send to an unprovisioned device. * * <p>The NFC record must contain a serialized {@link java.util.Properties} object which * contains the following properties: @@ -589,15 +594,13 @@ public class DevicePolicyManager { * {@link #EXTRA_PROVISIONING_DEVICE_ADMIN_COMPONENT_NAME} instead of * {@link #EXTRA_PROVISIONING_DEVICE_ADMIN_PACKAGE_NAME}, (although specifying only * {@link #EXTRA_PROVISIONING_DEVICE_ADMIN_PACKAGE_NAME} is still supported). - * - * @see #MIME_TYPE_PROVISIONING_NFC_V2 - * */ public static final String MIME_TYPE_PROVISIONING_NFC = "application/com.android.managedprovisioning"; /** + * @hide * This MIME type is used for starting the Device Owner provisioning that requires * new provisioning features introduced in API version * {@link android.os.Build.VERSION_CODES#MNC} in addition to those supported in earlier @@ -2402,6 +2405,9 @@ public class DevicePolicyManager { * <p>The calling device admin must be a device or profile owner. If it is not, a * security exception will be thrown. * + * <p>From version {@link android.os.Build.VERSION_CODES#MNC} disabling screen capture also + * blocks assist requests for all activities of the relevant user. + * * @param admin Which {@link DeviceAdminReceiver} this request is associated with. * @param disabled Whether screen capture is disabled or not. */ diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java index 83d6cb0..b1d80f0 100644 --- a/core/java/android/content/Intent.java +++ b/core/java/android/content/Intent.java @@ -3784,6 +3784,9 @@ public class Intent implements Parcelable, Cloneable { /** {@hide} */ public static final String EXTRA_REASON = "android.intent.extra.REASON"; + /** {@hide} */ + public static final String EXTRA_WIPE_EXTERNAL_STORAGE = "android.intent.extra.WIPE_EXTERNAL_STORAGE"; + /** * Optional {@link android.app.PendingIntent} extra used to deliver the result of the SIM * activation request. diff --git a/core/java/android/content/IntentFilter.java b/core/java/android/content/IntentFilter.java index e267b52..19329ce 100644 --- a/core/java/android/content/IntentFilter.java +++ b/core/java/android/content/IntentFilter.java @@ -518,7 +518,8 @@ public class IntentFilter implements Parcelable { } /** - * Return if this filter handle all HTTP or HTTPS data URI or not. + * Return if this filter handle all HTTP or HTTPS data URI or not. This is the + * core check for whether a given activity qualifies as a "browser". * * @return True if the filter handle all HTTP or HTTPS data URI. False otherwise. * @@ -533,23 +534,26 @@ public class IntentFilter implements Parcelable { */ public final boolean handleAllWebDataURI() { return hasCategory(Intent.CATEGORY_APP_BROWSER) || - (hasOnlyWebDataURI() && countDataAuthorities() == 0); + (handlesWebUris(false) && countDataAuthorities() == 0); } /** - * Return if this filter handles only HTTP or HTTPS data URIs. + * Return if this filter handles HTTP or HTTPS data URIs. * * @return True if the filter handles ACTION_VIEW/CATEGORY_BROWSABLE, - * has at least one HTTP or HTTPS data URI pattern defined, and does not - * define any non-http/https data URI patterns. + * has at least one HTTP or HTTPS data URI pattern defined, and optionally + * does not define any non-http/https data URI patterns. * * This will check if if the Intent action is {@link android.content.Intent#ACTION_VIEW} and * the Intent category is {@link android.content.Intent#CATEGORY_BROWSABLE} and the Intent * data scheme is "http" or "https". * + * @param onlyWebSchemes When true, requires that the intent filter declare + * that it handles *only* http: or https: schemes. This is a requirement for + * the intent filter's domain linkage being verifiable. * @hide */ - public final boolean hasOnlyWebDataURI() { + public final boolean handlesWebUris(boolean onlyWebSchemes) { // Require ACTION_VIEW, CATEGORY_BROWSEABLE, and at least one scheme if (!hasAction(Intent.ACTION_VIEW) || !hasCategory(Intent.CATEGORY_BROWSABLE) @@ -562,13 +566,28 @@ public class IntentFilter implements Parcelable { final int N = mDataSchemes.size(); for (int i = 0; i < N; i++) { final String scheme = mDataSchemes.get(i); - if (!SCHEME_HTTP.equals(scheme) && !SCHEME_HTTPS.equals(scheme)) { - return false; + final boolean isWebScheme = + SCHEME_HTTP.equals(scheme) || SCHEME_HTTPS.equals(scheme); + if (onlyWebSchemes) { + // If we're specifically trying to ensure that there are no non-web schemes + // declared in this filter, then if we ever see a non-http/https scheme then + // we know it's a failure. + if (!isWebScheme) { + return false; + } + } else { + // If we see any http/https scheme declaration in this case then the + // filter matches what we're looking for. + if (isWebScheme) { + return true; + } } } - // Everything passed, so it's an only-web-URIs filter - return true; + // We get here if: + // 1) onlyWebSchemes and no non-web schemes were found, i.e success; or + // 2) !onlyWebSchemes and no http/https schemes were found, i.e. failure. + return onlyWebSchemes; } /** @@ -585,7 +604,7 @@ public class IntentFilter implements Parcelable { * @hide */ public final boolean needsVerification() { - return getAutoVerify() && hasOnlyWebDataURI(); + return getAutoVerify() && handlesWebUris(true); } /** diff --git a/core/java/android/hardware/camera2/CameraCharacteristics.java b/core/java/android/hardware/camera2/CameraCharacteristics.java index a88b71c..c47498d 100644 --- a/core/java/android/hardware/camera2/CameraCharacteristics.java +++ b/core/java/android/hardware/camera2/CameraCharacteristics.java @@ -2738,35 +2738,13 @@ public final class CameraCharacteristics extends CameraMetadata<CameraCharacteri /** * <p>The maximum number of frames that can occur after a request * (different than the previous) has been submitted, and before the - * result's state becomes synchronized (by setting - * android.sync.frameNumber to a non-negative value).</p> + * result's state becomes synchronized.</p> * <p>This defines the maximum distance (in number of metadata results), - * between android.sync.frameNumber and the equivalent - * frame number for that result.</p> + * between the frame number of the request that has new controls to apply + * and the frame number of the result that has all the controls applied.</p> * <p>In other words this acts as an upper boundary for how many frames * must occur before the camera device knows for a fact that the new * submitted camera settings have been applied in outgoing frames.</p> - * <p>For example if the distance was 2,</p> - * <pre><code>initial request = X (repeating) - * request1 = X - * request2 = Y - * request3 = Y - * request4 = Y - * - * where requestN has frameNumber N, and the first of the repeating - * initial request's has frameNumber F (and F < 1). - * - * initial result = X' + { android.sync.frameNumber == F } - * result1 = X' + { android.sync.frameNumber == F } - * result2 = X' + { android.sync.frameNumber == CONVERGING } - * result3 = X' + { android.sync.frameNumber == CONVERGING } - * result4 = X' + { android.sync.frameNumber == 2 } - * - * where resultN has frameNumber N. - * </code></pre> - * <p>Since <code>result4</code> has a <code>frameNumber == 4</code> and - * <code>android.sync.frameNumber == 2</code>, the distance is clearly - * <code>4 - 2 = 2</code>.</p> * <p><b>Units</b>: Frame counts</p> * <p><b>Possible values:</b> * <ul> diff --git a/core/java/android/hardware/camera2/CameraDevice.java b/core/java/android/hardware/camera2/CameraDevice.java index 006030c..639c8b1 100644 --- a/core/java/android/hardware/camera2/CameraDevice.java +++ b/core/java/android/hardware/camera2/CameraDevice.java @@ -236,13 +236,16 @@ public abstract class CameraDevice implements AutoCloseable { * {@link CameraCaptureSession.StateCallback}'s * {@link CameraCaptureSession.StateCallback#onConfigured} callback will be called.</p> * - * <p>If a prior CameraCaptureSession already exists when a new one is created, the previous - * session is closed. Any in-progress capture requests made on the prior session will be - * completed before the new session is configured and is able to start capturing its own - * requests. To minimize the transition time, the {@link CameraCaptureSession#abortCaptures} - * call can be used to discard the remaining requests for the prior capture session before a new - * one is created. Note that once the new session is created, the old one can no longer have its - * captures aborted.</p> + * <p>If a prior CameraCaptureSession already exists when this method is called, the previous + * session will no longer be able to accept new capture requests and will be closed. Any + * in-progress capture requests made on the prior session will be completed before it's closed. + * {@link CameraCaptureSession.StateListener#onConfigured} for the new session may be invoked + * before {@link CameraCaptureSession.StateListener#onClosed} is invoked for the prior + * session. Once the new session is {@link CameraCaptureSession.StateListener#onConfigured + * configured}, it is able to start capturing its own requests. To minimize the transition time, + * the {@link CameraCaptureSession#abortCaptures} call can be used to discard the remaining + * requests for the prior capture session before a new one is created. Note that once the new + * session is created, the old one can no longer have its captures aborted.</p> * * <p>Using larger resolution outputs, or more outputs, can result in slower * output rate from the device.</p> diff --git a/core/java/android/hardware/camera2/CameraMetadata.java b/core/java/android/hardware/camera2/CameraMetadata.java index 5a80585..e8dbc5b 100644 --- a/core/java/android/hardware/camera2/CameraMetadata.java +++ b/core/java/android/hardware/camera2/CameraMetadata.java @@ -955,8 +955,6 @@ public abstract class CameraMetadata<TKey> { /** * <p>Every frame has the requests immediately applied.</p> - * <p>Furthermore for all results, - * <code>android.sync.frameNumber == {@link android.hardware.camera2.CaptureResult#getFrameNumber }</code></p> * <p>Changing controls over multiple requests one after another will * produce results that have those controls applied atomically * each frame.</p> diff --git a/core/java/android/os/Build.java b/core/java/android/os/Build.java index 50eed3e..b2ced7f 100644 --- a/core/java/android/os/Build.java +++ b/core/java/android/os/Build.java @@ -622,7 +622,7 @@ public class Build { /** * M comes after L. */ - public static final int MNC = CUR_DEVELOPMENT; + public static final int MNC = 23; } /** The type of build, like "user" or "eng". */ diff --git a/core/java/android/os/UserManager.java b/core/java/android/os/UserManager.java index 9770941..6384af3 100644 --- a/core/java/android/os/UserManager.java +++ b/core/java/android/os/UserManager.java @@ -449,6 +449,8 @@ public class UserManager { public static final String DISALLOW_RECORD_AUDIO = "no_record_audio"; /** + * Allows apps in the parent profile to handle web links from the managed profile. + * * This user restriction has an effect only in a managed profile. * If set: * Intent filters of activities in the parent profile with action @@ -462,7 +464,8 @@ public class UserManager { * @see #setUserRestrictions(Bundle) * @see #getUserRestrictions() */ - public static final String ALLOW_PARENT_APP_LINKING = "allow_parent_app_linking"; + public static final String ALLOW_PARENT_PROFILE_APP_LINKING + = "allow_parent_profile_app_linking"; /** * Application restriction key that is used to indicate the pending arrival diff --git a/core/java/android/speech/tts/TextToSpeech.java b/core/java/android/speech/tts/TextToSpeech.java index 3eeb04a..eae4329 100644 --- a/core/java/android/speech/tts/TextToSpeech.java +++ b/core/java/android/speech/tts/TextToSpeech.java @@ -1486,19 +1486,28 @@ public class TextToSpeech { // Get the default voice for the locale. String voiceName = service.getDefaultVoiceNameFor(language, country, variant); if (TextUtils.isEmpty(voiceName)) { - Log.w(TAG, "Couldn't find the default voice for " + language + "/" + - country + "/" + variant); + Log.w(TAG, "Couldn't find the default voice for " + language + "-" + + country + "-" + variant); return LANG_NOT_SUPPORTED; } // Load it. if (service.loadVoice(getCallerIdentity(), voiceName) == TextToSpeech.ERROR) { + Log.w(TAG, "The service claimed " + language + "-" + country + "-" + + variant + " was available with voice name " + voiceName + + " but loadVoice returned ERROR"); return LANG_NOT_SUPPORTED; } // Set the language/country/variant of the voice, so #getLanguage will return // the currently set voice locale when called. Voice voice = getVoice(service, voiceName); + if (voice == null) { + Log.w(TAG, "getDefaultVoiceNameFor returned " + voiceName + " for locale " + + language + "-" + country + "-" + variant + + " but getVoice returns null"); + return LANG_NOT_SUPPORTED; + } String voiceLanguage = ""; try { voiceLanguage = voice.getLocale().getISO3Language(); @@ -1682,6 +1691,7 @@ public class TextToSpeech { private Voice getVoice(ITextToSpeechService service, String voiceName) throws RemoteException { List<Voice> voices = service.getVoices(); if (voices == null) { + Log.w(TAG, "getVoices returned null"); return null; } for (Voice voice : voices) { @@ -1689,6 +1699,7 @@ public class TextToSpeech { return voice; } } + Log.w(TAG, "Could not find voice " + voiceName + " in voice list"); return null; } diff --git a/core/java/android/speech/tts/TextToSpeechService.java b/core/java/android/speech/tts/TextToSpeechService.java index ba98f27..fa015b2 100644 --- a/core/java/android/speech/tts/TextToSpeechService.java +++ b/core/java/android/speech/tts/TextToSpeechService.java @@ -293,7 +293,9 @@ public abstract class TextToSpeechService extends Service { } Set<String> features = onGetFeaturesForLanguage(locale.getISO3Language(), locale.getISO3Country(), locale.getVariant()); - voices.add(new Voice(locale.toLanguageTag(), locale, Voice.QUALITY_NORMAL, + String voiceName = onGetDefaultVoiceNameFor(locale.getISO3Language(), + locale.getISO3Country(), locale.getVariant()); + voices.add(new Voice(voiceName, locale, Voice.QUALITY_NORMAL, Voice.LATENCY_NORMAL, false, features)); } return voices; diff --git a/core/java/android/util/LocalLog.java b/core/java/android/util/LocalLog.java index cab5d19..4862f01 100644 --- a/core/java/android/util/LocalLog.java +++ b/core/java/android/util/LocalLog.java @@ -54,4 +54,18 @@ public final class LocalLog { pw.println(itr.next()); } } + + public static class ReadOnlyLocalLog { + private final LocalLog mLog; + ReadOnlyLocalLog(LocalLog log) { + mLog = log; + } + public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { + mLog.dump(fd, pw, args); + } + } + + public ReadOnlyLocalLog readOnlyLocalLog() { + return new ReadOnlyLocalLog(this); + } } diff --git a/core/java/android/widget/Editor.java b/core/java/android/widget/Editor.java index c96d39b..84e7db4 100644 --- a/core/java/android/widget/Editor.java +++ b/core/java/android/widget/Editor.java @@ -4171,10 +4171,17 @@ public class Editor { if (isExpanding) { // User is increasing the selection. if (!mInWord || currLine < mPrevLine) { - // We're not in a word, or we're on a different line so we'll expand by - // word. First ensure the user has at least entered the next word. - int offsetToWord = (end - start) / 2; - if (offset <= end - offsetToWord || currLine < mPrevLine) { + // Sometimes words can be broken across lines (Chinese, hyphenation). + // We still snap to the start of the word but we only use the letters on the + // current line to determine if the user is far enough into the word to snap. + int wordStartOnCurrLine = start; + if (layout != null && layout.getLineForOffset(start) != currLine) { + wordStartOnCurrLine = layout.getLineStart(currLine); + } + int offsetThresholdToSnap = end - ((end - wordStartOnCurrLine) / 2); + if (offset <= offsetThresholdToSnap || currLine < mPrevLine) { + // User is far enough into the word or on a different + // line so we expand by word. offset = start; } else { offset = mPreviousOffset; @@ -4352,10 +4359,17 @@ public class Editor { if (isExpanding) { // User is increasing the selection. if (!mInWord || currLine > mPrevLine) { - // We're not in a word, or we're on a different line so we'll expand by - // word. First ensure the user has at least entered the next word. - int midPoint = (end - start) / 2; - if (offset >= start + midPoint || currLine > mPrevLine) { + // Sometimes words can be broken across lines (Chinese, hyphenation). + // We still snap to the end of the word but we only use the letters on the + // current line to determine if the user is far enough into the word to snap. + int wordEndOnCurrLine = end; + if (layout != null && layout.getLineForOffset(end) != currLine) { + wordEndOnCurrLine = layout.getLineEnd(currLine); + } + final int offsetThresholdToSnap = start + ((wordEndOnCurrLine - start) / 2); + if (offset >= offsetThresholdToSnap || currLine > mPrevLine) { + // User is far enough into the word or on a different + // line so we expand by word. offset = end; } else { offset = mPreviousOffset; diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java index 57d0bcf..7b58b5b 100644 --- a/core/java/android/widget/TextView.java +++ b/core/java/android/widget/TextView.java @@ -292,6 +292,9 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener // New state used to change background based on whether this TextView is multiline. private static final int[] MULTILINE_STATE_SET = { R.attr.state_multiline }; + // Accessibility action to share selected text. + private static final int ACCESSIBILITY_ACTION_SHARE = 0x10000000; + // System wide time for last cut, copy or text changed action. static long sLastCutCopyOrTextChangedTime; @@ -7574,10 +7577,14 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener } String getSelectedText() { - if (hasSelection()) { - return String.valueOf(mText.subSequence(getSelectionStart(), getSelectionEnd())); + if (!hasSelection()) { + return null; } - return null; + + final int start = getSelectionStart(); + final int end = getSelectionEnd(); + return String.valueOf( + start > end ? mText.subSequence(end, start) : mText.subSequence(start, end)); } /** @@ -8845,6 +8852,11 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener if (canCut()) { info.addAction(AccessibilityNodeInfo.ACTION_CUT); } + if (canShare()) { + info.addAction(new AccessibilityNodeInfo.AccessibilityAction( + ACCESSIBILITY_ACTION_SHARE, + getResources().getString(com.android.internal.R.string.share))); + } } // Check for known input filter types. @@ -8951,6 +8963,13 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener ensureIterableTextForAccessibilitySelectable(); return super.performAccessibilityActionInternal(action, arguments); } + case ACCESSIBILITY_ACTION_SHARE: { + if (isFocused() && canShare()) { + if (onTextContextMenuItem(ID_SHARE)) { + return true; + } + } + } return false; default: { return super.performAccessibilityActionInternal(action, arguments); } diff --git a/core/java/com/android/internal/os/storage/ExternalStorageFormatter.java b/core/java/com/android/internal/os/storage/ExternalStorageFormatter.java index 1d0511f..0a01ae9 100644 --- a/core/java/com/android/internal/os/storage/ExternalStorageFormatter.java +++ b/core/java/com/android/internal/os/storage/ExternalStorageFormatter.java @@ -17,6 +17,10 @@ import com.android.internal.R; /** * Takes care of unmounting and formatting external storage. + * + * @deprecated Please use {@link Intent#ACTION_MASTER_CLEAR} broadcast with extra + * {@link Intent#EXTRA_WIPE_EXTERNAL_STORAGE} to wipe and factory reset, or call + * {@link StorageManager#wipeAdoptableDisks} directly to format external storages. */ public class ExternalStorageFormatter extends Service { static final String TAG = "ExternalStorageFormatter"; diff --git a/core/java/com/android/internal/view/FloatingActionMode.java b/core/java/com/android/internal/view/FloatingActionMode.java index 863506b..c869722 100644 --- a/core/java/com/android/internal/view/FloatingActionMode.java +++ b/core/java/com/android/internal/view/FloatingActionMode.java @@ -30,6 +30,8 @@ import com.android.internal.util.Preconditions; import com.android.internal.view.menu.MenuBuilder; import com.android.internal.widget.FloatingToolbar; +import java.util.Arrays; + public class FloatingActionMode extends ActionMode { private static final int MAX_HIDE_DURATION = 3000; @@ -42,7 +44,9 @@ public class FloatingActionMode extends ActionMode { private final Rect mContentRectOnWindow; private final Rect mPreviousContentRectOnWindow; private final int[] mViewPosition; + private final int[] mPreviousViewPosition; private final Rect mViewRect; + private final Rect mPreviousViewRect; private final Rect mScreenRect; private final View mOriginatingView; private final int mBottomAllowance; @@ -75,7 +79,9 @@ public class FloatingActionMode extends ActionMode { mContentRectOnWindow = new Rect(); mPreviousContentRectOnWindow = new Rect(); mViewPosition = new int[2]; + mPreviousViewPosition = new int[2]; mViewRect = new Rect(); + mPreviousViewRect = new Rect(); mScreenRect = new Rect(); mOriginatingView = Preconditions.checkNotNull(originatingView); mOriginatingView.getLocationInWindow(mViewPosition); @@ -129,9 +135,17 @@ public class FloatingActionMode extends ActionMode { public void updateViewLocationInWindow() { checkToolbarInitialized(); + mOriginatingView.getLocationInWindow(mViewPosition); mOriginatingView.getGlobalVisibleRect(mViewRect); - repositionToolbar(); + + if (!Arrays.equals(mViewPosition, mPreviousViewPosition) + || !mViewRect.equals(mPreviousViewRect)) { + repositionToolbar(); + mPreviousViewPosition[0] = mViewPosition[0]; + mPreviousViewPosition[1] = mViewPosition[1]; + mPreviousViewRect.set(mViewRect); + } } private void repositionToolbar() { diff --git a/core/java/com/android/internal/widget/FloatingToolbar.java b/core/java/com/android/internal/widget/FloatingToolbar.java index 32a145c..163b8ce 100644 --- a/core/java/com/android/internal/widget/FloatingToolbar.java +++ b/core/java/com/android/internal/widget/FloatingToolbar.java @@ -85,6 +85,7 @@ public final class FloatingToolbar { private final FloatingToolbarPopup mPopup; private final Rect mContentRect = new Rect(); + private final Rect mPreviousContentRect = new Rect(); private Menu mMenu; private List<Object> mShowingMenuItems = new ArrayList<Object>(); @@ -178,11 +179,13 @@ public final class FloatingToolbar { mPopup.layoutMenuItems(menuItems, mMenuItemClickListener, mSuggestedWidth); mShowingMenuItems = getShowingMenuItemsReferences(menuItems); } - mPopup.updateCoordinates(mContentRect); if (!mPopup.isShowing()) { mPopup.show(mContentRect); + } else if (!mPreviousContentRect.equals(mContentRect)) { + mPopup.updateCoordinates(mContentRect); } mWidthChanged = false; + mPreviousContentRect.set(mContentRect); return this; } @@ -318,24 +321,8 @@ public final class FloatingToolbar { }; private final AnimatorSet mDismissAnimation; private final AnimatorSet mHideAnimation; - private final AnimationSet mOpenOverflowAnimation = new AnimationSet(true) { - @Override - public void cancel() { - if (hasStarted() && !hasEnded()) { - super.cancel(); - setOverflowPanelAsContent(); - } - } - }; - private final AnimationSet mCloseOverflowAnimation = new AnimationSet(true) { - @Override - public void cancel() { - if (hasStarted() && !hasEnded()) { - super.cancel(); - setMainPanelAsContent(); - } - } - }; + private final AnimationSet mOpenOverflowAnimation = new AnimationSet(true); + private final AnimationSet mCloseOverflowAnimation = new AnimationSet(true); private final Runnable mOpenOverflow = new Runnable() { @Override @@ -657,8 +644,24 @@ public final class FloatingToolbar { } private void cancelOverflowAnimations() { - mOpenOverflowAnimation.cancel(); - mCloseOverflowAnimation.cancel(); + if (mOpenOverflowAnimation.hasStarted() + && !mOpenOverflowAnimation.hasEnded()) { + // Remove the animation listener, stop the animation, + // then trigger the lister explicitly so it is not posted + // to the message queue. + mOpenOverflowAnimation.setAnimationListener(null); + mContentContainer.clearAnimation(); + mOnOverflowOpened.onAnimationEnd(null); + } + if (mCloseOverflowAnimation.hasStarted() + && !mCloseOverflowAnimation.hasEnded()) { + // Remove the animation listener, stop the animation, + // then trigger the lister explicitly so it is not posted + // to the message queue. + mCloseOverflowAnimation.setAnimationListener(null); + mContentContainer.clearAnimation(); + mOnOverflowClosed.onAnimationEnd(null); + } } /** |