diff options
Diffstat (limited to 'core/java')
54 files changed, 2276 insertions, 491 deletions
diff --git a/core/java/android/animation/AnimatorInflater.java b/core/java/android/animation/AnimatorInflater.java index df5a4cb..224e8e9 100644 --- a/core/java/android/animation/AnimatorInflater.java +++ b/core/java/android/animation/AnimatorInflater.java @@ -698,7 +698,7 @@ public class AnimatorInflater { int ordering = a.getInt(R.styleable.AnimatorSet_ordering, TOGETHER); createAnimatorFromXml(res, theme, parser, attrs, (AnimatorSet) anim, ordering, pixelSize); - final int hint = a.getInt(R.styleable.Animator_durationScaleHint, + final int hint = a.getInt(R.styleable.AnimatorSet_durationScaleHint, HINT_NO_SCALE); anim.setDurationScaleHint(hint, res); a.recycle(); diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java index a0d59d9..6cf6481 100644 --- a/core/java/android/app/Activity.java +++ b/core/java/android/app/Activity.java @@ -89,6 +89,7 @@ import android.view.MenuInflater; import android.view.MenuItem; import android.view.MotionEvent; import android.view.PhoneWindow; +import android.view.SearchEvent; import android.view.View; import android.view.View.OnCreateContextMenuListener; import android.view.ViewGroup; @@ -787,6 +788,8 @@ public class Activity extends ContextThemeWrapper private TranslucentConversionListener mTranslucentCallback; private boolean mChangeCanvasToTranslucent; + private SearchEvent mSearchEvent; + private boolean mTitleReady = false; private int mActionModeTypeStarting = ActionMode.TYPE_PRIMARY; @@ -3547,12 +3550,23 @@ public class Activity extends ContextThemeWrapper * implementation changes to simply return false and you must supply your own custom * implementation if you want to support search.</p> * + * @param searchEvent The {@link SearchEvent} that signaled this search. * @return Returns {@code true} if search launched, and {@code false} if the activity does * not respond to search. The default implementation always returns {@code true}, except * when in {@link Configuration#UI_MODE_TYPE_TELEVISION} mode where it returns false. * * @see android.app.SearchManager */ + public boolean onSearchRequested(@Nullable SearchEvent searchEvent) { + mSearchEvent = searchEvent; + boolean result = onSearchRequested(); + mSearchEvent = null; + return result; + } + + /** + * @see #onSearchRequested(SearchEvent) + */ public boolean onSearchRequested() { if ((getResources().getConfiguration().uiMode&Configuration.UI_MODE_TYPE_MASK) != Configuration.UI_MODE_TYPE_TELEVISION) { @@ -3564,6 +3578,17 @@ public class Activity extends ContextThemeWrapper } /** + * During the onSearchRequested() callbacks, this function will return the + * {@link SearchEvent} that triggered the callback, if it exists. + * + * @return SearchEvent The SearchEvent that triggered the {@link + * #onSearchRequested} callback. + */ + public final SearchEvent getSearchEvent() { + return mSearchEvent; + } + + /** * This hook is called to launch the search UI. * * <p>It is typically called from onSearchRequested(), either directly from diff --git a/core/java/android/app/BackStackRecord.java b/core/java/android/app/BackStackRecord.java index 83451aa..8fb048b 100644 --- a/core/java/android/app/BackStackRecord.java +++ b/core/java/android/app/BackStackRecord.java @@ -1368,7 +1368,6 @@ final class BackStackRecord extends FragmentTransaction implements public boolean onPreDraw() { sceneRoot.getViewTreeObserver().removeOnPreDrawListener(this); if (enterTransition != null) { - enterTransition.removeTarget(nonExistingView); removeTargets(enterTransition, enteringViews); } if (exitTransition != null) { diff --git a/core/java/android/app/Dialog.java b/core/java/android/app/Dialog.java index 786a52f..d049104 100644 --- a/core/java/android/app/Dialog.java +++ b/core/java/android/app/Dialog.java @@ -49,6 +49,7 @@ import android.view.Menu; import android.view.MenuItem; import android.view.MotionEvent; import android.view.PhoneWindow; +import android.view.SearchEvent; import android.view.View; import android.view.View.OnCreateContextMenuListener; import android.view.ViewGroup; @@ -123,6 +124,8 @@ public class Dialog implements DialogInterface, Window.Callback, private Handler mListenersHandler; + private SearchEvent mSearchEvent; + private ActionMode mActionMode; private int mActionModeTypeStarting = ActionMode.TYPE_PRIMARY; @@ -995,6 +998,14 @@ public class Dialog implements DialogInterface, Window.Callback, /** * This hook is called when the user signals the desire to start a search. */ + public boolean onSearchRequested(SearchEvent searchEvent) { + mSearchEvent = searchEvent; + return onSearchRequested(); + } + + /** + * This hook is called when the user signals the desire to start a search. + */ public boolean onSearchRequested() { final SearchManager searchManager = (SearchManager) mContext .getSystemService(Context.SEARCH_SERVICE); @@ -1011,6 +1022,17 @@ public class Dialog implements DialogInterface, Window.Callback, } /** + * During the onSearchRequested() callbacks, this function will return the + * {@link SearchEvent} that triggered the callback, if it exists. + * + * @return SearchEvent The SearchEvent that triggered the {@link + * #onSearchRequested} callback. + */ + public final SearchEvent getSearchEvent() { + return mSearchEvent; + } + + /** * {@inheritDoc} */ @Override diff --git a/core/java/android/app/IUserSwitchObserver.aidl b/core/java/android/app/IUserSwitchObserver.aidl index 845897b..caee14f 100644 --- a/core/java/android/app/IUserSwitchObserver.aidl +++ b/core/java/android/app/IUserSwitchObserver.aidl @@ -22,4 +22,5 @@ import android.os.IRemoteCallback; oneway interface IUserSwitchObserver { void onUserSwitching(int newUserId, IRemoteCallback reply); void onUserSwitchComplete(int newUserId); + void onForegroundProfileSwitch(int newProfileId); } diff --git a/core/java/android/app/KeyguardManager.java b/core/java/android/app/KeyguardManager.java index ddd21e6..56cd53e 100644 --- a/core/java/android/app/KeyguardManager.java +++ b/core/java/android/app/KeyguardManager.java @@ -253,6 +253,32 @@ public class KeyguardManager { } /** + * Returns whether the device is secured with a PIN, pattern or + * password. + * + * @return true if a PIN, pattern or password was set. + */ + public boolean isDeviceSecure() { + return isDeviceSecure(UserHandle.getCallingUserId()); + } + + /** + * Returns whether the device is secured with a PIN, pattern or + * password. + * + * @param userId the user for which the secure state should be reported. + * @return true if a PIN, pattern or password was set. + * @hide + */ + public boolean isDeviceSecure(int userId) { + try { + return mTrustManager.isDeviceSecure(userId); + } catch (RemoteException e) { + return false; + } + } + + /** * @deprecated Use {@link android.view.WindowManager.LayoutParams#FLAG_DISMISS_KEYGUARD} * and/or {@link android.view.WindowManager.LayoutParams#FLAG_SHOW_WHEN_LOCKED} * instead; this allows you to seamlessly hide the keyguard as your application diff --git a/core/java/android/app/SharedElementCallback.java b/core/java/android/app/SharedElementCallback.java index e58b7fb..bac84a4 100644 --- a/core/java/android/app/SharedElementCallback.java +++ b/core/java/android/app/SharedElementCallback.java @@ -51,9 +51,23 @@ public abstract class SharedElementCallback { }; /** - * Called immediately after the start state is set for the shared element. - * The shared element will start at the size and position of the shared element - * in the launching Activity or Fragment. + * In Activity Transitions, onSharedElementStart is called immediately before + * capturing the start of the shared element state on enter and reenter transitions and + * immediately before capturing the end of the shared element state for exit and return + * transitions. + * <p> + * In Fragment Transitions, onSharedElementStart is called immediately before capturing the + * start state of all shared element transitions. + * <p> + * This call can be used to adjust the transition start state by modifying the shared + * element Views. Note that no layout step will be executed between onSharedElementStart + * and the transition state capture. + * <p> + * For Activity Transitions, any changes made in {@link #onSharedElementEnd(List, List, List)} + * that are not updated during by layout should be corrected in onSharedElementStart for exit and + * return transitions. For example, rotation or scale will not be affected by layout and + * if changed in {@link #onSharedElementEnd(List, List, List)}, it will also have to be reset + * in onSharedElementStart again to correct the end state. * * @param sharedElementNames The names of the shared elements that were accepted into * the View hierarchy. @@ -68,17 +82,23 @@ public abstract class SharedElementCallback { List<View> sharedElements, List<View> sharedElementSnapshots) {} /** - * Called after the end state is set for the shared element, but before the end state - * is captured by the shared element transition. + * In Activity Transitions, onSharedElementEnd is called immediately before + * capturing the end of the shared element state on enter and reenter transitions and + * immediately before capturing the start of the shared element state for exit and return + * transitions. * <p> - * Any customization done in - * {@link #onSharedElementStart(java.util.List, java.util.List, java.util.List)} - * may need to be modified to the final state of the shared element if it is not - * automatically corrected by layout. For example, rotation or scale will not - * be affected by layout and if changed in {@link #onSharedElementStart(java.util.List, - * java.util.List, java.util.List)}, it will also have to be set here again to correct - * the end state. - * </p> + * In Fragment Transitions, onSharedElementEnd is called immediately before capturing the + * end state of all shared element transitions. + * <p> + * This call can be used to adjust the transition end state by modifying the shared + * element Views. Note that no layout step will be executed between onSharedElementEnd + * and the transition state capture. + * <p> + * Any changes made in {@link #onSharedElementStart(List, List, List)} that are not updated + * during layout should be corrected in onSharedElementEnd. For example, rotation or scale + * will not be affected by layout and if changed in + * {@link #onSharedElementStart(List, List, List)}, it will also have to be reset in + * onSharedElementEnd again to correct the end state. * * @param sharedElementNames The names of the shared elements that were accepted into * the View hierarchy. diff --git a/core/java/android/app/admin/DeviceInitializerStatus.java b/core/java/android/app/admin/DeviceInitializerStatus.java new file mode 100644 index 0000000..b58711c --- /dev/null +++ b/core/java/android/app/admin/DeviceInitializerStatus.java @@ -0,0 +1,177 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.app.admin; + +/** + * Defines constants designating device provisioning status used with {@link + * android.app.admin.DevicePolicyManager#sendDeviceInitializerStatus(int,String)}. + * + * This class contains flag constants that define special status codes: + * <ul> + * <li>{@link #FLAG_STATUS_ERROR} is used to define provisioning error status codes + * <li>{@link #FLAG_STATUS_CUSTOM} is used to define custom status codes + * <li>{@link #FLAG_STATUS_HIGH_PRIORITY} is used to define high priority status codes + * </ul> + * + * <p>Status codes used by ManagedProvisioning are also defined in this class. These status codes + * include provisioning errors and status codes. + * <ul> + * <li>{@link #STATUS_ERROR_CONNECT_WIFI} + * <li>{@link #STATUS_ERROR_RESET_PROTECTION_BLOCKING_PROVISIONING} + * <li>{@link #STATUS_ERROR_DOWNLOAD_PACKAGE} + * <li>{@link #STATUS_ERROR_INSTALL_PACKAGE} + * <li>{@link #STATUS_ERROR_SET_DEVICE_POLICY} + * <li>{@link #STATUS_ERROR_DELETE_APPS} + * <li>{@link #STATUS_ERROR_DOUBLE_BUMP} + * <li>{@link #STATUS_STATE_CONNECT_BLUETOOTH_PROXY} + * <li>{@link #STATUS_STATE_DISCONNECT_BLUETOOTH_PROXY} + * <li>{@link #STATUS_STATE_DEVICE_PROVISIONED} + * </ul> + */ +public class DeviceInitializerStatus { + /** + * A flag used to designate an error status. + * + * <p>This flag is used with {@code statusCode} values sent through + * {@link android.app.admin.DevicePolicyManager#sendDeviceInitializerStatus(int,String)} + * @see #isErrorStatus(int) + */ + public static final int FLAG_STATUS_ERROR = 0x01000000; + + /** + * A flag used to designate a custom status. Custom status codes will be defined by device + * initializer agents. + * + * <p>This flag is used with {@code statusCode} values sent through + * {@link android.app.admin.DevicePolicyManager#sendDeviceInitializerStatus(int,String)} + * @see #isCustomStatus(int) + */ + public static final int FLAG_STATUS_CUSTOM = 0x02000000; + + /** + * A bit flag used to designate a reserved status. Reserved status codes will not be defined + * in AOSP. + * + * <p>This flag is used with {@code statusCode} values sent through + * {@link android.app.admin.DevicePolicyManager#sendDeviceInitializerStatus(int,String)} + */ + public static final int FLAG_STATUS_RESERVED = 0x04000000; + + /** + * A flag used to indicate that a status message is high priority. + * + * <p>This flag is used with {@code statusCode} values sent through + * {@link android.app.admin.DevicePolicyManager#sendDeviceInitializerStatus(int,String)} + * @see #isHighPriority(int) + */ + public static final int FLAG_STATUS_HIGH_PRIORITY = 0x08000000; + + /** + * Device provisioning status code that indicates that a device is connecting to establish + * a Bluetooth network proxy. + */ + public static final int STATUS_STATE_CONNECT_BLUETOOTH_PROXY = FLAG_STATUS_HIGH_PRIORITY | 8; + + /** + * Device provisioning status code that indicates that a connected Bluetooth network proxy + * is being shut down. + */ + public static final int STATUS_STATE_DISCONNECT_BLUETOOTH_PROXY = FLAG_STATUS_HIGH_PRIORITY | 9; + + /** + * Device provisioning status code that indicates that a device has been successfully + * provisioned. + */ + public static final int STATUS_STATE_DEVICE_PROVISIONED = FLAG_STATUS_HIGH_PRIORITY | 10; + + /** + * Device provisioning error status code that indicates that a device could not connect to + * a Wi-Fi network. + */ + public static final int STATUS_ERROR_CONNECT_WIFI = FLAG_STATUS_ERROR | 21; + + /** + * Device provisioning error status indicating that factory reset protection is enabled on + * the provisioned device and cannot be disabled with the provided data. + */ + public static final int STATUS_ERROR_RESET_PROTECTION_BLOCKING_PROVISIONING = + FLAG_STATUS_ERROR | 22; + + /** + * Device provisioning error status indicating that device administrator and device initializer + * packages could not be downloaded and verified successfully. + */ + public static final int STATUS_ERROR_DOWNLOAD_PACKAGE = FLAG_STATUS_ERROR | 23; + + /** + * Device provisioning error status indicating that device owner and device initializer packages + * could not be installed. + */ + public static final int STATUS_ERROR_INSTALL_PACKAGE = FLAG_STATUS_ERROR | 24; + + /** + * Device provisioning error status indicating that the device owner or device initializer + * components could not be set. + */ + public static final int STATUS_ERROR_SET_DEVICE_POLICY = FLAG_STATUS_ERROR | 25; + + /** + * Device provisioning error status indicating that deleting non-required applications during + * provisioning failed. + */ + public static final int STATUS_ERROR_DELETE_APPS = FLAG_STATUS_ERROR | 26; + + /** + * Device provisioning error status code that indicates that a provisioning attempt has failed + * because the device has already been provisioned or that provisioning has already started. + */ + public static final int STATUS_ERROR_DOUBLE_BUMP = FLAG_STATUS_ERROR | 30; + + /** + * Determine if the specified status code represents an error status. + * @param statusCode status code to check + * @return {@code true} if the status code is an error status code + */ + public static boolean isErrorStatus(int statusCode) { + return isFlagSet(statusCode, FLAG_STATUS_ERROR); + } + + /** + * Determine if the specified status code is a custom status. Custom status codes are defined + * and sent by device initialization agents. + * @param statusCode status code to check + * @return {@code true} if the status code is a custom status code + */ + public static boolean isCustomStatus(int statusCode) { + return isFlagSet(statusCode, FLAG_STATUS_CUSTOM); + } + + /** + * Determine if the specified status code is a high priority status code. + * @param statusCode status code to check + * @return {@code true} if the status code is a high priority status code + */ + public static boolean isHighPriority(int statusCode) { + return isFlagSet(statusCode, FLAG_STATUS_HIGH_PRIORITY); + } + + private static boolean isFlagSet(int statusCode, int flag) { + return (statusCode & flag) != 0; + } + + private DeviceInitializerStatus() {} +} diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java index 68f4707..a0a6c4c 100644 --- a/core/java/android/app/admin/DevicePolicyManager.java +++ b/core/java/android/app/admin/DevicePolicyManager.java @@ -651,6 +651,45 @@ public class DevicePolicyManager { = "android.app.action.SET_PROFILE_OWNER"; /** + * Protected broadcast action that will be sent to managed provisioning to notify it that a + * status update has been reported by the device initializer. The status update will be + * reported to the remote setup device over Bluetooth. + * + * <p>Broadcasts with this action must supply a + * {@linkplain DeviceInitializerStatus#isCustomStatus(int) custom} status code in the + * {@link EXTRA_DEVICE_INITIALIZER_STATUS_CODE} extra. + * + * <p>Broadcasts may optionally contain a description in the + * {@link EXTRA_DEVICE_INITIALIZER_STATUS_DESCRIPTION} extra. + * @hide + */ + @SystemApi + public static final String ACTION_SEND_DEVICE_INITIALIZER_STATUS + = "android.app.action.SEND_DEVICE_INITIALIZER_STATUS"; + + /** + * An integer extra that contains the status code that defines a status update. This extra must + * sent as part of a broadcast with an action of {@code ACTION_SEND_DEVICE_INITIALIZER_STATUS}. + * + * <p>The status code sent with this extra must be a custom status code as defined by + * {@link DeviceInitializerStatus#isCustomStatus(int)}. + * @hide + */ + @SystemApi + public static final String EXTRA_DEVICE_INITIALIZER_STATUS_CODE + = "android.app.extra.DEVICE_INITIALIZER_STATUS_CODE"; + + /** + * A {@code String} extra that contains an optional description accompanying a status update. + * This extra my be sent as part of a broadcast with an action of + * {@code ACTION_SEND_DEVICE_INITIALIZER_STATUS}. + * @hide + */ + @SystemApi + public static final String EXTRA_DEVICE_INITIALIZER_STATUS_DESCRIPTION + = "android.app.extra.DEVICE_INITIALIZER_STATUS_DESCRIPTION"; + + /** * @hide * Name of the profile owner admin that controls the user. */ @@ -715,6 +754,14 @@ public class DevicePolicyManager { public static final int FLAG_MANAGED_CAN_ACCESS_PARENT = 0x0002; /** + * Broadcast action: notify that a new local OTA policy has been set by the device owner. + * The new policy can be retrieved by {@link #getOtaPolicy()}. + */ + @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) + public static final String ACTION_OTA_POLICY_CHANGED = "android.app.action.OTA_POLICY_CHANGED"; + + + /** * Return true if the given administrator component is currently * active (enabled) in the system. */ @@ -2170,7 +2217,8 @@ public class DevicePolicyManager { * Called by a device or profile owner to install a certificate and private key pair. The * keypair will be visible to all apps within the profile. * - * @param who Which {@link DeviceAdminReceiver} this request is associated with. + * @param who Which {@link DeviceAdminReceiver} this request is associated with. Use + * <code>null</code> if calling from a delegated certificate installer. * @param privKey The private key to install. * @param cert The certificate to install. * @param alias The private key alias under which to install the certificate. If a certificate @@ -2207,9 +2255,9 @@ public class DevicePolicyManager { /** * Called by a profile owner or device owner to grant access to privileged certificate - * manipulation APIs to a third-party CA certificate installer app. Granted APIs include + * manipulation APIs to a third-party certificate installer app. Granted APIs include * {@link #getInstalledCaCerts}, {@link #hasCaCertInstalled}, {@link #installCaCert}, - * {@link #uninstallCaCert} and {@link #uninstallAllUserCaCerts}. + * {@link #uninstallCaCert}, {@link #uninstallAllUserCaCerts} and {@link #installKeyPair}. * <p> * Delegated certificate installer is a per-user state. The delegated access is persistent until * it is later cleared by calling this method with a null value or uninstallling the certificate @@ -3214,6 +3262,73 @@ public class DevicePolicyManager { } /** + * Called by a profile owner of a managed profile to set whether bluetooth + * devices can access enterprise contacts. + * <p> + * The calling device admin must be a profile owner. If it is not, a + * security exception will be thrown. + * <p> + * This API works on managed profile only. + * + * @param who Which {@link DeviceAdminReceiver} this request is associated + * with. + * @param disabled If true, bluetooth devices cannot access enterprise + * contacts. + */ + public void setBluetoothContactSharingDisabled(ComponentName who, boolean disabled) { + if (mService != null) { + try { + mService.setBluetoothContactSharingDisabled(who, disabled); + } catch (RemoteException e) { + Log.w(TAG, "Failed talking with device policy service", e); + } + } + } + + /** + * Called by a profile owner of a managed profile to determine whether or + * not Bluetooth devices cannot access enterprise contacts. + * <p> + * The calling device admin must be a profile owner. If it is not, a + * security exception will be thrown. + * <p> + * This API works on managed profile only. + * + * @param who Which {@link DeviceAdminReceiver} this request is associated + * with. + */ + public boolean getBluetoothContactSharingDisabled(ComponentName who) { + if (mService != null) { + try { + return mService.getBluetoothContactSharingDisabled(who); + } catch (RemoteException e) { + Log.w(TAG, "Failed talking with device policy service", e); + } + } + return true; + } + + /** + * Determine whether or not Bluetooth devices cannot access contacts. + * <p> + * This API works on managed profile UserHandle only. + * + * @param userHandle The user for whom to check the caller-id permission + * @hide + */ + public boolean getBluetoothContactSharingDisabled(UserHandle userHandle) { + if (mService != null) { + try { + return mService.getBluetoothContactSharingDisabledForUser(userHandle + .getIdentifier()); + } catch (RemoteException e) { + Log.w(TAG, "Failed talking with device policy service", e); + } + } + return true; + } + + /** * Called by the profile owner of a managed profile so that some intents sent in the managed * profile can also be resolved in the parent, or vice versa. * Only activity intents are supported. @@ -4012,4 +4127,87 @@ public class DevicePolicyManager { Log.w(TAG, "Could not set the user icon ", re); } } + + /** + * Called by device initializer to send a provisioning status update to the remote setup device. + * + * @param statusCode a custom status code value as defined by + * {@link DeviceInitializerStatus#isCustomStatus(int)}. + * @param description custom description of the status code sent + */ + public void sendDeviceInitializerStatus(int statusCode, String description) { + try { + mService.sendDeviceInitializerStatus(statusCode, description); + } catch (RemoteException re) { + Log.w(TAG, "Could not send device initializer status", re); + } + } + + /* + * Called by device owners to set a local OTA update policy. When a new OTA policy is set, + * {@link #ACTION_OTA_POLICY_CHANGED} is broadcasted. + * + * @param who Which {@link DeviceAdminReceiver} this request is associated with. All components + * in the device owner package can set OTA policies and the most recent policy takes effect. + * @param policy the new OTA policy, or null to clear the current policy. + * @see OtaPolicy + */ + public void setOtaPolicy(ComponentName who, OtaPolicy policy) { + if (mService != null) { + try { + if (policy != null) { + mService.setOtaPolicy(who, policy.getPolicyBundle()); + } else { + mService.setOtaPolicy(who, null); + } + } catch (RemoteException re) { + Log.w(TAG, "Error calling setOtaPolicy", re); + } + } + } + + /** + * Retrieve a local OTA update policy set previously by {@link #setOtaPolicy}. + * + * @return The current OTA policy object, or null if no policy is set or the system does not + * support managed OTA. + */ + public OtaPolicy getOtaPolicy() { + if (mService != null) { + try { + PersistableBundle bundle = mService.getOtaPolicy(); + if (bundle != null) { + return new OtaPolicy(bundle); + } else { + return null; + } + } catch (RemoteException re) { + Log.w(TAG, "Error calling getOtaPolicy", re); + } + } + return null; + } + + /** + * Called by a device owner to disable the keyguard altogether. + * + * <p>Setting the keyguard to disabled has the same effect as choosing "None" as the screen + * lock type. However, this call has no effect if a password, pin or pattern is currently set. + * If a password, pin or pattern is set after the keyguard was disabled, the keyguard stops + * being disabled. + * + * @param admin Which {@link DeviceAdminReceiver} this request is associated with. + * @param enabled New state of the keyguard. + * + * @return {@code false} if attempting to disable the keyguard while a lock password was in + * place. {@code true} otherwise." + */ + public boolean setKeyguardEnabledState(ComponentName admin, boolean enabled) { + try { + return mService.setKeyguardEnabledState(admin, enabled); + } catch (RemoteException re) { + Log.w(TAG, "Failed talking with device policy service", re); + return false; + } + } } diff --git a/core/java/android/app/admin/IDevicePolicyManager.aidl b/core/java/android/app/admin/IDevicePolicyManager.aidl index c68311e..131b99c 100644 --- a/core/java/android/app/admin/IDevicePolicyManager.aidl +++ b/core/java/android/app/admin/IDevicePolicyManager.aidl @@ -191,6 +191,10 @@ interface IDevicePolicyManager { boolean getCrossProfileCallerIdDisabledForUser(int userId); void startManagedQuickContact(String lookupKey, long contactId, in Intent originalIntent); + void setBluetoothContactSharingDisabled(in ComponentName who, boolean disabled); + boolean getBluetoothContactSharingDisabled(in ComponentName who); + boolean getBluetoothContactSharingDisabledForUser(int userId); + void setTrustAgentConfiguration(in ComponentName admin, in ComponentName agent, in PersistableBundle args); List<PersistableBundle> getTrustAgentConfiguration(in ComponentName admin, @@ -213,4 +217,10 @@ interface IDevicePolicyManager { ComponentName getDeviceInitializerComponent(); void setUserIcon(in ComponentName admin, in Bitmap icon); + + void sendDeviceInitializerStatus(int statusCode, String description); + void setOtaPolicy(in ComponentName who, in PersistableBundle policy); + PersistableBundle getOtaPolicy(); + + boolean setKeyguardEnabledState(in ComponentName admin, boolean enabled); } diff --git a/core/java/android/app/admin/OtaPolicy.java b/core/java/android/app/admin/OtaPolicy.java new file mode 100644 index 0000000..98581a7 --- /dev/null +++ b/core/java/android/app/admin/OtaPolicy.java @@ -0,0 +1,179 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.app.admin; + +import android.annotation.IntDef; +import android.os.PersistableBundle; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +/** + * A class that represents a local OTA policy set by the device owner. + * + * @see DevicePolicyManager#setOtaPolicy + * @see DevicePolicyManager#getOtaPolicy + */ +public class OtaPolicy { + + /** @hide */ + @IntDef({ + TYPE_INSTALL_AUTOMATIC, + TYPE_INSTALL_WINDOWED, + TYPE_POSTPONE}) + @Retention(RetentionPolicy.SOURCE) + @interface OtaPolicyType {} + + /** + * Install OTA update automatically as soon as one is available. + */ + public static final int TYPE_INSTALL_AUTOMATIC = 1; + + /** + * Install OTA update automatically within a daily maintenance window, for a maximum of two-week + * period. After that period the OTA will be installed automatically. + */ + public static final int TYPE_INSTALL_WINDOWED = 2; + + /** + * Incoming OTA will be blocked for a maximum of two weeks, after which it will be installed + * automatically. + */ + public static final int TYPE_POSTPONE = 3; + + private static final String KEY_POLICY_TYPE = "policy_type"; + private static final String KEY_INSTALL_WINDOW_START = "install_window_start"; + private static final String KEY_INSTALL_WINDOW_END = "install_window_end"; + + private PersistableBundle mPolicy; + + public OtaPolicy() { + mPolicy = new PersistableBundle(); + } + + /** + * Construct an OtaPolicy object from a bundle. + * @hide + */ + public OtaPolicy(PersistableBundle in) { + mPolicy = new PersistableBundle(in); + } + + /** + * Retrieve the underlying bundle where the policy is stored. + * @hide + */ + public PersistableBundle getPolicyBundle() { + return new PersistableBundle(mPolicy); + } + + /** + * Set the OTA policy to: install OTA update automatically as soon as one is available. + */ + public void setAutomaticInstallPolicy() { + mPolicy.clear(); + mPolicy.putInt(KEY_POLICY_TYPE, TYPE_INSTALL_AUTOMATIC); + } + + /** + * Set the OTA policy to: new OTA update will only be installed automatically when the system + * clock is inside a daily maintenance window. If the start and end times are the same, the + * window is considered to include the WHOLE 24 hours, that is, OTAs can install at any time. If + * the given window in invalid, a {@link OtaPolicy.InvalidWindowException} will be thrown. If + * start time is later than end time, the window is considered spanning midnight, i.e. end time + * donates a time on the next day. The maintenance window will last for two weeks, after which + * the OTA will be installed automatically. + * + * @param startTime the start of the maintenance window, measured as the number of minutes from + * midnight in the device's local time. Must be in the range of [0, 1440). + * @param endTime the end of the maintenance window, measured as the number of minutes from + * midnight in the device's local time. Must be in the range of [0, 1440). + */ + public void setWindowedInstallPolicy(int startTime, int endTime) throws InvalidWindowException{ + if (startTime < 0 || startTime >= 1440 || endTime < 0 || endTime >= 1440) { + throw new InvalidWindowException("startTime and endTime must be inside [0, 1440)"); + } + mPolicy.clear(); + mPolicy.putInt(KEY_POLICY_TYPE, TYPE_INSTALL_WINDOWED); + mPolicy.putInt(KEY_INSTALL_WINDOW_START, startTime); + mPolicy.putInt(KEY_INSTALL_WINDOW_END, endTime); + } + + /** + * Set the OTA policy to: block installation for a maximum period of two weeks. After the + * block expires the OTA will be installed automatically. + */ + public void setPostponeInstallPolicy() { + mPolicy.clear(); + mPolicy.putInt(KEY_POLICY_TYPE, TYPE_POSTPONE); + } + + /** + * Returns the type of OTA policy. + * + * @return an integer, either one of {@link #TYPE_INSTALL_AUTOMATIC}, + * {@link #TYPE_INSTALL_WINDOWED} and {@link #TYPE_POSTPONE}, or -1 if no policy has been set. + */ + @OtaPolicyType + public int getPolicyType() { + return mPolicy.getInt(KEY_POLICY_TYPE, -1); + } + + /** + * Get the start of the maintenance window. + * + * @return the start of the maintenance window measured as the number of minutes from midnight, + * or -1 if the policy does not have a maintenance window. + */ + public int getInstallWindowStart() { + if (getPolicyType() == TYPE_INSTALL_WINDOWED) { + return mPolicy.getInt(KEY_INSTALL_WINDOW_START, -1); + } else { + return -1; + } + } + + /** + * Get the end of the maintenance window. + * + * @return the end of the maintenance window measured as the number of minutes from midnight, + * or -1 if the policy does not have a maintenance window. + */ + public int getInstallWindowEnd() { + if (getPolicyType() == TYPE_INSTALL_WINDOWED) { + return mPolicy.getInt(KEY_INSTALL_WINDOW_END, -1); + } else { + return -1; + } + } + + @Override + public String toString() { + return mPolicy.toString(); + } + + /** + * Exception thrown by {@link OtaPolicy#setWindowedInstallPolicy(int, int)} in case the + * specified window is invalid. + */ + public static class InvalidWindowException extends Exception { + public InvalidWindowException(String reason) { + super(reason); + } + } +} + diff --git a/core/java/android/app/backup/BackupTransport.java b/core/java/android/app/backup/BackupTransport.java index 1131ff9..9540eb1 100644 --- a/core/java/android/app/backup/BackupTransport.java +++ b/core/java/android/app/backup/BackupTransport.java @@ -399,6 +399,13 @@ public class BackupTransport { * operation will be skipped (and {@link #finishBackup() invoked} with no data for that * package being passed to {@link #sendBackupData}. * + * <p class="note">The platform does no size-based rejection of full backup attempts on + * its own: it is always the responsibility of the transport to implement its own policy. + * In particular, even if the preflighted payload size is zero, the platform will still call + * this method and will proceed to back up an archive metadata header with no file content + * if this method returns TRANSPORT_OK. To avoid storing such payloads the transport + * must recognize this case and return TRANSPORT_PACKAGE_REJECTED. + * * Added in MNC (API 23). * * @param size The estimated size of the full-data payload for this app. This includes diff --git a/core/java/android/app/trust/ITrustManager.aidl b/core/java/android/app/trust/ITrustManager.aidl index 68ea0aa..17cff5c 100644 --- a/core/java/android/app/trust/ITrustManager.aidl +++ b/core/java/android/app/trust/ITrustManager.aidl @@ -31,4 +31,5 @@ interface ITrustManager { void unregisterTrustListener(in ITrustListener trustListener); void reportKeyguardShowingChanged(); boolean isDeviceLocked(int userId); + boolean isDeviceSecure(int userId); } diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java index eea47b7..54fe786 100644 --- a/core/java/android/content/Intent.java +++ b/core/java/android/content/Intent.java @@ -3311,6 +3311,14 @@ public class Intent implements Parcelable, Cloneable { public static final String EXTRA_INITIAL_INTENTS = "android.intent.extra.INITIAL_INTENTS"; /** + * A Parcelable[] of {@link android.service.chooser.ChooserTarget ChooserTarget} objects + * as set with {@link #putExtra(String, Parcelable[])} representing additional app-specific + * targets to place at the front of the list of choices. Shown to the user with + * {@link #ACTION_CHOOSER}. + */ + public static final String EXTRA_CHOOSER_TARGETS = "android.intent.extra.CHOOSER_TARGETS"; + + /** * A Bundle forming a mapping of potential target package names to different extras Bundles * to add to the default intent extras in {@link #EXTRA_INTENT} when used with * {@link #ACTION_CHOOSER}. Each key should be a package name. The package need not diff --git a/core/java/android/hardware/camera2/CameraCharacteristics.java b/core/java/android/hardware/camera2/CameraCharacteristics.java index f01c540..8f7aed4 100644 --- a/core/java/android/hardware/camera2/CameraCharacteristics.java +++ b/core/java/android/hardware/camera2/CameraCharacteristics.java @@ -639,7 +639,8 @@ public final class CameraCharacteristics extends CameraMetadata<CameraCharacteri /** * <p>Whether the camera device supports {@link CaptureRequest#CONTROL_AE_LOCK android.control.aeLock}</p> - * <p>LIMITED or FULL devices will always list <code>true</code></p> + * <p>Devices with MANUAL_SENSOR capability or BURST_CAPTURE capability will always + * list <code>true</code>. This includes FULL devices.</p> * <p>This key is available on all devices.</p> * * @see CaptureRequest#CONTROL_AE_LOCK @@ -650,7 +651,8 @@ public final class CameraCharacteristics extends CameraMetadata<CameraCharacteri /** * <p>Whether the camera device supports {@link CaptureRequest#CONTROL_AWB_LOCK android.control.awbLock}</p> - * <p>LIMITED or FULL devices will always list <code>true</code></p> + * <p>Devices with MANUAL_POST_PROCESSING capability or BURST_CAPTURE capability will + * always list <code>true</code>. This includes FULL devices.</p> * <p>This key is available on all devices.</p> * * @see CaptureRequest#CONTROL_AWB_LOCK diff --git a/core/java/android/hardware/camera2/CameraMetadata.java b/core/java/android/hardware/camera2/CameraMetadata.java index 2192ab7..4a5bd08 100644 --- a/core/java/android/hardware/camera2/CameraMetadata.java +++ b/core/java/android/hardware/camera2/CameraMetadata.java @@ -347,6 +347,10 @@ public abstract class CameraMetadata<TKey> { * <li>{@link CaptureRequest#BLACK_LEVEL_LOCK android.blackLevel.lock}</li> * </ul> * </li> + * <li>Auto exposure lock<ul> + * <li>{@link CaptureRequest#CONTROL_AE_LOCK android.control.aeLock}</li> + * </ul> + * </li> * </ul> * <p>If any of the above 3A algorithms are enabled, then the camera * device will accurately report the values applied by 3A in the @@ -358,6 +362,7 @@ public abstract class CameraMetadata<TKey> { * zero for each supported size-format combination.</p> * * @see CaptureRequest#BLACK_LEVEL_LOCK + * @see CaptureRequest#CONTROL_AE_LOCK * @see CameraCharacteristics#SCALER_STREAM_CONFIGURATION_MAP * @see CaptureRequest#SENSOR_EXPOSURE_TIME * @see CaptureRequest#SENSOR_FRAME_DURATION @@ -403,6 +408,10 @@ public abstract class CameraMetadata<TKey> { * <li>{@link CameraCharacteristics#COLOR_CORRECTION_AVAILABLE_ABERRATION_MODES android.colorCorrection.availableAberrationModes}</li> * </ul> * </li> + * <li>Auto white balance lock<ul> + * <li>{@link CaptureRequest#CONTROL_AWB_LOCK android.control.awbLock}</li> + * </ul> + * </li> * </ul> * <p>If auto white balance is enabled, then the camera device * will accurately report the values applied by AWB in the result.</p> @@ -413,6 +422,7 @@ public abstract class CameraMetadata<TKey> { * @see CameraCharacteristics#COLOR_CORRECTION_AVAILABLE_ABERRATION_MODES * @see CaptureRequest#COLOR_CORRECTION_GAINS * @see CaptureRequest#COLOR_CORRECTION_TRANSFORM + * @see CaptureRequest#CONTROL_AWB_LOCK * @see CaptureRequest#SHADING_MODE * @see CaptureRequest#STATISTICS_LENS_SHADING_MAP_MODE * @see CaptureRequest#TONEMAP_CURVE @@ -530,9 +540,15 @@ public abstract class CameraMetadata<TKey> { * YUV_420_888 format.</p> * </blockquote> * <p>In addition, the {@link CameraCharacteristics#SYNC_MAX_LATENCY android.sync.maxLatency} field is - * guaranted to have a value between 0 and 4, inclusive.</p> + * guaranted to have a value between 0 and 4, inclusive. + * {@link CameraCharacteristics#CONTROL_AE_LOCK_AVAILABLE android.control.aeLockAvailable} and + * {@link CameraCharacteristics#CONTROL_AWB_LOCK_AVAILABLE android.control.awbLockAvailable} are also guaranteed + * to be <code>true</code> so burst capture with these two locks ON + * yields consistent image output.</p> * * @see CameraCharacteristics#CONTROL_AE_AVAILABLE_TARGET_FPS_RANGES + * @see CameraCharacteristics#CONTROL_AE_LOCK_AVAILABLE + * @see CameraCharacteristics#CONTROL_AWB_LOCK_AVAILABLE * @see CameraCharacteristics#SCALER_STREAM_CONFIGURATION_MAP * @see CameraCharacteristics#SYNC_MAX_LATENCY * @see CameraCharacteristics#REQUEST_AVAILABLE_CAPABILITIES diff --git a/core/java/android/hardware/location/GeofenceHardwareImpl.java b/core/java/android/hardware/location/GeofenceHardwareImpl.java index 4696b2a..5d40e94 100644 --- a/core/java/android/hardware/location/GeofenceHardwareImpl.java +++ b/core/java/android/hardware/location/GeofenceHardwareImpl.java @@ -53,6 +53,7 @@ public final class GeofenceHardwareImpl { private IFusedGeofenceHardware mFusedService; private IGpsGeofenceHardware mGpsService; + private int mCapabilities; private int[] mSupportedMonitorTypes = new int[GeofenceHardware.NUM_MONITORS]; @@ -89,6 +90,9 @@ public final class GeofenceHardwareImpl { private static final int RESOLUTION_LEVEL_COARSE = 2; private static final int RESOLUTION_LEVEL_FINE = 3; + // Capability constant corresponding to fused_location.h entry when geofencing supports GNNS. + private static final int CAPABILITY_GNSS = 1; + public synchronized static GeofenceHardwareImpl getInstance(Context context) { if (sInstance == null) { sInstance = new GeofenceHardwareImpl(context); @@ -141,7 +145,9 @@ public final class GeofenceHardwareImpl { private void updateFusedHardwareAvailability() { boolean fusedSupported; try { - fusedSupported = (mFusedService != null ? mFusedService.isSupported() : false); + fusedSupported = (mFusedService != null + ? mFusedService.isSupported() && (mCapabilities & CAPABILITY_GNSS) != 0 + : false); } catch (RemoteException e) { Log.e(TAG, "RemoteException calling LocationManagerService"); fusedSupported = false; @@ -166,6 +172,11 @@ public final class GeofenceHardwareImpl { } } + public void onCapabilities(int capabilities) { + mCapabilities = capabilities; + updateFusedHardwareAvailability(); + } + public void setFusedGeofenceHardware(IFusedGeofenceHardware service) { if(mFusedService == null) { mFusedService = service; @@ -212,6 +223,20 @@ public final class GeofenceHardwareImpl { } } + public int getCapabilitiesForMonitoringType(int monitoringType) { + switch (mSupportedMonitorTypes[monitoringType]) { + case GeofenceHardware.MONITOR_CURRENTLY_AVAILABLE: + switch (monitoringType) { + case GeofenceHardware.MONITORING_TYPE_GPS_HARDWARE: + return CAPABILITY_GNSS; + case GeofenceHardware.MONITORING_TYPE_FUSED_HARDWARE: + return mCapabilities; + } + break; + } + return 0; + } + public boolean addCircularFence( int monitoringType, GeofenceHardwareRequestParcelable request, diff --git a/core/java/android/hardware/location/GeofenceHardwareService.java b/core/java/android/hardware/location/GeofenceHardwareService.java index 4816c5f..c0bcb27 100644 --- a/core/java/android/hardware/location/GeofenceHardwareService.java +++ b/core/java/android/hardware/location/GeofenceHardwareService.java @@ -65,14 +65,17 @@ public class GeofenceHardwareService extends Service { } private IBinder mBinder = new IGeofenceHardware.Stub() { + @Override public void setGpsGeofenceHardware(IGpsGeofenceHardware service) { mGeofenceHardwareImpl.setGpsHardwareGeofence(service); } + @Override public void setFusedGeofenceHardware(IFusedGeofenceHardware service) { mGeofenceHardwareImpl.setFusedGeofenceHardware(service); } + @Override public int[] getMonitoringTypes() { mContext.enforceCallingPermission(Manifest.permission.LOCATION_HARDWARE, "Location Hardware permission not granted to access hardware geofence"); @@ -80,12 +83,15 @@ public class GeofenceHardwareService extends Service { return mGeofenceHardwareImpl.getMonitoringTypes(); } + @Override public int getStatusOfMonitoringType(int monitoringType) { mContext.enforceCallingPermission(Manifest.permission.LOCATION_HARDWARE, "Location Hardware permission not granted to access hardware geofence"); return mGeofenceHardwareImpl.getStatusOfMonitoringType(monitoringType); } + + @Override public boolean addCircularFence( int monitoringType, GeofenceHardwareRequestParcelable request, @@ -96,6 +102,7 @@ public class GeofenceHardwareService extends Service { return mGeofenceHardwareImpl.addCircularFence(monitoringType, request, callback); } + @Override public boolean removeGeofence(int id, int monitoringType) { mContext.enforceCallingPermission(Manifest.permission.LOCATION_HARDWARE, "Location Hardware permission not granted to access hardware geofence"); @@ -104,6 +111,7 @@ public class GeofenceHardwareService extends Service { return mGeofenceHardwareImpl.removeGeofence(id, monitoringType); } + @Override public boolean pauseGeofence(int id, int monitoringType) { mContext.enforceCallingPermission(Manifest.permission.LOCATION_HARDWARE, "Location Hardware permission not granted to access hardware geofence"); @@ -112,6 +120,7 @@ public class GeofenceHardwareService extends Service { return mGeofenceHardwareImpl.pauseGeofence(id, monitoringType); } + @Override public boolean resumeGeofence(int id, int monitoringType, int monitorTransitions) { mContext.enforceCallingPermission(Manifest.permission.LOCATION_HARDWARE, "Location Hardware permission not granted to access hardware geofence"); @@ -120,6 +129,7 @@ public class GeofenceHardwareService extends Service { return mGeofenceHardwareImpl.resumeGeofence(id, monitoringType, monitorTransitions); } + @Override public boolean registerForMonitorStateChangeCallback(int monitoringType, IGeofenceHardwareMonitorCallback callback) { mContext.enforceCallingPermission(Manifest.permission.LOCATION_HARDWARE, @@ -130,6 +140,7 @@ public class GeofenceHardwareService extends Service { callback); } + @Override public boolean unregisterForMonitorStateChangeCallback(int monitoringType, IGeofenceHardwareMonitorCallback callback) { mContext.enforceCallingPermission(Manifest.permission.LOCATION_HARDWARE, diff --git a/core/java/android/hardware/location/IFusedLocationHardware.aidl b/core/java/android/hardware/location/IFusedLocationHardware.aidl index 382c12c..3de766a 100644 --- a/core/java/android/hardware/location/IFusedLocationHardware.aidl +++ b/core/java/android/hardware/location/IFusedLocationHardware.aidl @@ -32,21 +32,21 @@ interface IFusedLocationHardware { * * @param eventSink The sink to register. */ - void registerSink(in IFusedLocationHardwareSink eventSink); + void registerSink(in IFusedLocationHardwareSink eventSink) = 0; /** * Unregisters a sink with the Location Hardware object. * * @param eventSink The sink to unregister. */ - void unregisterSink(in IFusedLocationHardwareSink eventSink); + void unregisterSink(in IFusedLocationHardwareSink eventSink) = 1; /** * Provides access to the batch size available in Hardware. * * @return The batch size the hardware supports. */ - int getSupportedBatchSize(); + int getSupportedBatchSize() = 2; /** * Requests the Hardware to start batching locations. @@ -56,7 +56,7 @@ interface IFusedLocationHardware { * * @throws RuntimeException if the request Id exists. */ - void startBatching(in int id, in FusedBatchOptions batchOptions); + void startBatching(in int id, in FusedBatchOptions batchOptions) = 3; /** * Requests the Hardware to stop batching for the given Id. @@ -64,7 +64,7 @@ interface IFusedLocationHardware { * @param id The request that needs to be stopped. * @throws RuntimeException if the request Id is unknown. */ - void stopBatching(in int id); + void stopBatching(in int id) = 4; /** * Updates a batching operation in progress. @@ -74,7 +74,7 @@ interface IFusedLocationHardware { * * @throws RuntimeException if the Id of the request is unknown. */ - void updateBatchingOptions(in int id, in FusedBatchOptions batchOptions); + void updateBatchingOptions(in int id, in FusedBatchOptions batchOptions) = 5; /** * Requests the most recent locations available in Hardware. @@ -83,14 +83,14 @@ interface IFusedLocationHardware { * * @param batchSizeRequested The number of locations requested. */ - void requestBatchOfLocations(in int batchSizeRequested); + void requestBatchOfLocations(in int batchSizeRequested) = 6; /** * Flags if the Hardware supports injection of diagnostic data. * * @return True if data injection is supported, false otherwise. */ - boolean supportsDiagnosticDataInjection(); + boolean supportsDiagnosticDataInjection() = 7; /** * Injects diagnostic data into the Hardware subsystem. @@ -98,14 +98,14 @@ interface IFusedLocationHardware { * @param data The data to inject. * @throws RuntimeException if injection is not supported. */ - void injectDiagnosticData(in String data); + void injectDiagnosticData(in String data) = 8; /** * Flags if the Hardware supports injection of device context information. * * @return True if device context injection is supported, false otherwise. */ - boolean supportsDeviceContextInjection(); + boolean supportsDeviceContextInjection() = 9; /** * Injects device context information into the Hardware subsystem. @@ -113,5 +113,12 @@ interface IFusedLocationHardware { * @param deviceEnabledContext The context to inject. * @throws RuntimeException if injection is not supported. */ - void injectDeviceContext(in int deviceEnabledContext); + void injectDeviceContext(in int deviceEnabledContext) = 10; + + /** + * Requests all batched locations currently available in Hardware + * and clears the buffer. Any subsequent calls will not return any + * of the locations returned in this call. + */ + void flushBatchedLocations() = 11; } diff --git a/core/java/android/hardware/location/IFusedLocationHardwareSink.aidl b/core/java/android/hardware/location/IFusedLocationHardwareSink.aidl index a11d8ab..c99cb0c 100644 --- a/core/java/android/hardware/location/IFusedLocationHardwareSink.aidl +++ b/core/java/android/hardware/location/IFusedLocationHardwareSink.aidl @@ -30,12 +30,24 @@ interface IFusedLocationHardwareSink { * * @param locations The batch of location information available. */ - void onLocationAvailable(in Location[] locations); + void onLocationAvailable(in Location[] locations) = 0; /** * Event generated from FLP HAL to provide diagnostic data to the platform. * * @param data The diagnostic data provided by FLP HAL. */ - void onDiagnosticDataAvailable(in String data); + void onDiagnosticDataAvailable(in String data) = 1; + + /** + * Event generated from FLP HAL to provide a mask of supported + * capabilities. Should be called immediatly after init. + */ + void onCapabilities(int capabilities) = 2; + + /** + * Event generated from FLP HAL when the status of location batching + * changes (location is successful/unsuccessful). + */ + void onStatusChanged(int status) = 3; }
\ No newline at end of file diff --git a/core/java/android/net/IpPrefix.java b/core/java/android/net/IpPrefix.java index b268986..6b4f2d5 100644 --- a/core/java/android/net/IpPrefix.java +++ b/core/java/android/net/IpPrefix.java @@ -170,6 +170,21 @@ public final class IpPrefix implements Parcelable { } /** + * Determines whether the prefix contains the specified address. + * + * @param address An {@link InetAddress} to test. + * @return {@code true} if the prefix covers the given address. + */ + public boolean contains(InetAddress address) { + byte[] addrBytes = (address == null) ? null : address.getAddress(); + if (addrBytes == null || addrBytes.length != this.address.length) { + return false; + } + NetworkUtils.maskRawAddress(addrBytes, prefixLength); + return Arrays.equals(this.address, addrBytes); + } + + /** * Returns a string representation of this {@code IpPrefix}. * * @return a string such as {@code "192.0.2.0/24"} or {@code "2001:db8:1:2::/64"}. diff --git a/core/java/android/net/RouteInfo.java b/core/java/android/net/RouteInfo.java index cfd20a0..90a2460 100644 --- a/core/java/android/net/RouteInfo.java +++ b/core/java/android/net/RouteInfo.java @@ -367,13 +367,7 @@ public final class RouteInfo implements Parcelable { * @return {@code true} if the destination and prefix length cover the given address. */ public boolean matches(InetAddress destination) { - if (destination == null) return false; - - // match the route destination and destination with prefix length - InetAddress dstNet = NetworkUtils.getNetworkPart(destination, - mDestination.getPrefixLength()); - - return mDestination.getAddress().equals(dstNet); + return mDestination.contains(destination); } /** diff --git a/core/java/android/os/IHardwareService.aidl b/core/java/android/os/IHardwareService.aidl deleted file mode 100644 index 38abfc0..0000000 --- a/core/java/android/os/IHardwareService.aidl +++ /dev/null @@ -1,26 +0,0 @@ -/** - * Copyright (c) 2007, The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package android.os; - -/** {@hide} */ -interface IHardwareService -{ - // obsolete flashlight support - boolean getFlashlightEnabled(); - void setFlashlightEnabled(boolean on); -} - diff --git a/core/java/android/os/UserManager.java b/core/java/android/os/UserManager.java index afd9950..b9e307f 100644 --- a/core/java/android/os/UserManager.java +++ b/core/java/android/os/UserManager.java @@ -1327,9 +1327,9 @@ public class UserManager { * calling user. * @return creation time in milliseconds since Epoch time. */ - public long getUserCreationTime(int userHandle) { + public long getUserCreationTime(UserHandle userHandle) { try { - return mService.getUserCreationTime(userHandle); + return mService.getUserCreationTime(userHandle.getIdentifier()); } catch (RemoteException re) { Log.w(TAG, "Could not get user creation time", re); return 0; diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java index 3087e1d..9edf6ad 100644 --- a/core/java/android/provider/Settings.java +++ b/core/java/android/provider/Settings.java @@ -889,6 +889,15 @@ public final class Settings { = "android.settings.ZEN_MODE_SCHEDULE_RULE_SETTINGS"; /** + * Activity Action: Show Zen Mode external rule configuration settings. + * + * @hide + */ + @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION) + public static final String ACTION_ZEN_MODE_EXTERNAL_RULE_SETTINGS + = "android.settings.ZEN_MODE_EXTERNAL_RULE_SETTINGS"; + + /** * Activity Action: Show the regulatory information screen for the device. * <p> * In some cases, a matching Activity may not exist, so ensure you safeguard diff --git a/core/java/android/provider/VoicemailContract.java b/core/java/android/provider/VoicemailContract.java index 4712f97..879f26c 100644 --- a/core/java/android/provider/VoicemailContract.java +++ b/core/java/android/provider/VoicemailContract.java @@ -19,7 +19,6 @@ package android.provider; import android.Manifest; import android.annotation.SdkConstant; import android.annotation.SdkConstant.SdkConstantType; -import android.annotation.SystemApi; import android.content.ContentResolver; import android.content.ContentValues; import android.content.Context; @@ -258,7 +257,6 @@ public class VoicemailContract { * * @hide */ - @SystemApi public static Uri insert(Context context, Voicemail voicemail) { ContentResolver contentResolver = context.getContentResolver(); ContentValues contentValues = getContentValues(voicemail); @@ -274,7 +272,6 @@ public class VoicemailContract { * * @hide */ - @SystemApi public static int insert(Context context, List<Voicemail> voicemails) { ContentResolver contentResolver = context.getContentResolver(); int count = voicemails.size(); @@ -293,7 +290,6 @@ public class VoicemailContract { * * @hide */ - @SystemApi public static int deleteAll(Context context) { return context.getContentResolver().delete( buildSourceUri(context.getPackageName()), "", new String[0]); @@ -452,7 +448,6 @@ public class VoicemailContract { * * @hide */ - @SystemApi public static void setStatus(Context context, PhoneAccountHandle accountHandle, int configurationState, int dataChannelState, int notificationChannelState) { ContentResolver contentResolver = context.getContentResolver(); diff --git a/core/java/android/security/keymaster/KeyCharacteristics.java b/core/java/android/security/keymaster/KeyCharacteristics.java index 0f1d422..b3a3aad 100644 --- a/core/java/android/security/keymaster/KeyCharacteristics.java +++ b/core/java/android/security/keymaster/KeyCharacteristics.java @@ -19,6 +19,10 @@ package android.security.keymaster; import android.os.Parcel; import android.os.Parcelable; +import java.util.ArrayList; +import java.util.Date; +import java.util.List; + /** * @hide */ @@ -28,10 +32,12 @@ public class KeyCharacteristics implements Parcelable { public static final Parcelable.Creator<KeyCharacteristics> CREATOR = new Parcelable.Creator<KeyCharacteristics>() { + @Override public KeyCharacteristics createFromParcel(Parcel in) { return new KeyCharacteristics(in); } + @Override public KeyCharacteristics[] newArray(int length) { return new KeyCharacteristics[length]; } @@ -48,6 +54,7 @@ public class KeyCharacteristics implements Parcelable { return 0; } + @Override public void writeToParcel(Parcel out, int flags) { swEnforced.writeToParcel(out, flags); hwEnforced.writeToParcel(out, flags); @@ -57,5 +64,53 @@ public class KeyCharacteristics implements Parcelable { swEnforced = KeymasterArguments.CREATOR.createFromParcel(in); hwEnforced = KeymasterArguments.CREATOR.createFromParcel(in); } + + public Integer getInteger(int tag) { + if (hwEnforced.containsTag(tag)) { + return hwEnforced.getInt(tag, -1); + } else if (swEnforced.containsTag(tag)) { + return swEnforced.getInt(tag, -1); + } else { + return null; + } + } + + public int getInt(int tag, int defaultValue) { + Integer result = getInteger(tag); + return (result != null) ? result : defaultValue; + } + + public List<Integer> getInts(int tag) { + List<Integer> result = new ArrayList<Integer>(); + result.addAll(hwEnforced.getInts(tag)); + result.addAll(swEnforced.getInts(tag)); + return result; + } + + public Date getDate(int tag) { + Date result = hwEnforced.getDate(tag, null); + if (result == null) { + result = swEnforced.getDate(tag, null); + } + return result; + } + + public Date getDate(int tag, Date defaultValue) { + if (hwEnforced.containsTag(tag)) { + return hwEnforced.getDate(tag, null); + } else if (hwEnforced.containsTag(tag)) { + return swEnforced.getDate(tag, null); + } else { + return defaultValue; + } + } + + public boolean getBoolean(KeyCharacteristics keyCharacteristics, int tag) { + if (keyCharacteristics.hwEnforced.containsTag(tag)) { + return keyCharacteristics.hwEnforced.getBoolean(tag, false); + } else { + return keyCharacteristics.swEnforced.getBoolean(tag, false); + } + } } diff --git a/core/java/android/security/keymaster/KeymasterArguments.java b/core/java/android/security/keymaster/KeymasterArguments.java index b5fd4bd..8ed288c 100644 --- a/core/java/android/security/keymaster/KeymasterArguments.java +++ b/core/java/android/security/keymaster/KeymasterArguments.java @@ -34,9 +34,12 @@ public class KeymasterArguments implements Parcelable { public static final Parcelable.Creator<KeymasterArguments> CREATOR = new Parcelable.Creator<KeymasterArguments>() { + @Override public KeymasterArguments createFromParcel(Parcel in) { return new KeymasterArguments(in); } + + @Override public KeymasterArguments[] newArray(int size) { return new KeymasterArguments[size]; } @@ -54,6 +57,12 @@ public class KeymasterArguments implements Parcelable { mArguments.add(new KeymasterIntArgument(tag, value)); } + public void addInts(int tag, int... values) { + for (int value : values) { + addInt(tag, value); + } + } + public void addBoolean(int tag) { mArguments.add(new KeymasterBooleanArgument(tag)); } diff --git a/core/java/android/service/chooser/ChooserTarget.java b/core/java/android/service/chooser/ChooserTarget.java index 7fd1d10..d21cc3c 100644 --- a/core/java/android/service/chooser/ChooserTarget.java +++ b/core/java/android/service/chooser/ChooserTarget.java @@ -17,6 +17,7 @@ package android.service.chooser; +import android.app.Activity; import android.app.PendingIntent; import android.content.ComponentName; import android.content.Context; @@ -24,8 +25,10 @@ import android.content.Intent; import android.content.IntentFilter; import android.content.IntentSender; import android.graphics.Bitmap; +import android.os.Bundle; import android.os.Parcel; import android.os.Parcelable; +import android.os.UserHandle; import android.util.Log; /** @@ -55,6 +58,12 @@ public final class ChooserTarget implements Parcelable { private IntentSender mIntentSender; /** + * A raw intent provided in lieu of an IntentSender. Will be filled in and sent + * by {@link #sendIntent(Context, Intent)}. + */ + private Intent mIntent; + + /** * The score given to this item. It can be normalized. */ private float mScore; @@ -135,6 +144,17 @@ public final class ChooserTarget implements Parcelable { mIntentSender = intentSender; } + public ChooserTarget(CharSequence title, Bitmap icon, float score, Intent intent) { + mTitle = title; + mIcon = icon; + if (score > 1.f || score < 0.f) { + throw new IllegalArgumentException("Score " + score + " out of range; " + + "must be between 0.0f and 1.0f"); + } + mScore = score; + mIntent = intent; + } + ChooserTarget(Parcel in) { mTitle = in.readCharSequence(); if (in.readInt() != 0) { @@ -144,6 +164,9 @@ public final class ChooserTarget implements Parcelable { } mScore = in.readFloat(); mIntentSender = IntentSender.readIntentSenderOrNullFromParcel(in); + if (in.readInt() != 0) { + mIntent = Intent.CREATOR.createFromParcel(in); + } } /** @@ -179,6 +202,7 @@ public final class ChooserTarget implements Parcelable { /** * Returns the raw IntentSender supplied by the ChooserTarget's creator. + * This may be null if the creator specified a regular Intent instead. * * <p>To fill in and send the intent, see {@link #sendIntent(Context, Intent)}.</p> * @@ -189,6 +213,18 @@ public final class ChooserTarget implements Parcelable { } /** + * Returns the Intent supplied by the ChooserTarget's creator. + * This may be null if the creator specified an IntentSender or PendingIntent instead. + * + * <p>To fill in and send the intent, see {@link #sendIntent(Context, Intent)}.</p> + * + * @return the Intent supplied by the ChooserTarget's creator + */ + public Intent getIntent() { + return mIntent; + } + + /** * Fill in the IntentSender supplied by the ChooserTarget's creator and send it. * * @param context the sending Context; generally the Activity presenting the chooser UI @@ -200,18 +236,109 @@ public final class ChooserTarget implements Parcelable { fillInIntent.migrateExtraStreamToClipData(); fillInIntent.prepareToLeaveProcess(); } - try { - mIntentSender.sendIntent(context, 0, fillInIntent, null, null); - return true; - } catch (IntentSender.SendIntentException e) { - Log.e(TAG, "sendIntent " + this + " failed", e); + if (mIntentSender != null) { + try { + mIntentSender.sendIntent(context, 0, fillInIntent, null, null); + return true; + } catch (IntentSender.SendIntentException e) { + Log.e(TAG, "sendIntent " + this + " failed", e); + return false; + } + } else if (mIntent != null) { + try { + final Intent toSend = new Intent(mIntent); + toSend.fillIn(fillInIntent, 0); + context.startActivity(toSend); + return true; + } catch (Exception e) { + Log.e(TAG, "sendIntent " + this + " failed", e); + return false; + } + } else { + Log.e(TAG, "sendIntent " + this + " failed - no IntentSender or Intent to send"); + return false; + } + } + + /** + * Same as {@link #sendIntent(Context, Intent)}, but offers a userId field to use + * for launching the {@link #getIntent() intent} using + * {@link Activity#startActivityAsCaller(Intent, Bundle, int)} if the + * {@link #getIntentSender() IntentSender} is not present. If the IntentSender is present, + * it will be invoked as usual with its own calling identity. + * + * @hide internal use only. + */ + public boolean sendIntentAsCaller(Activity context, Intent fillInIntent, int userId) { + if (fillInIntent != null) { + fillInIntent.migrateExtraStreamToClipData(); + fillInIntent.prepareToLeaveProcess(); + } + if (mIntentSender != null) { + try { + mIntentSender.sendIntent(context, 0, fillInIntent, null, null); + return true; + } catch (IntentSender.SendIntentException e) { + Log.e(TAG, "sendIntent " + this + " failed", e); + return false; + } + } else if (mIntent != null) { + try { + final Intent toSend = new Intent(mIntent); + toSend.fillIn(fillInIntent, 0); + context.startActivityAsCaller(toSend, null, userId); + return true; + } catch (Exception e) { + Log.e(TAG, "sendIntent " + this + " failed", e); + return false; + } + } else { + Log.e(TAG, "sendIntent " + this + " failed - no IntentSender or Intent to send"); + return false; + } + } + + /** + * The UserHandle is only used if we're launching a raw intent. The IntentSender will be + * launched with its associated identity. + * + * @hide Internal use only + */ + public boolean sendIntentAsUser(Activity context, Intent fillInIntent, UserHandle user) { + if (fillInIntent != null) { + fillInIntent.migrateExtraStreamToClipData(); + fillInIntent.prepareToLeaveProcess(); + } + if (mIntentSender != null) { + try { + mIntentSender.sendIntent(context, 0, fillInIntent, null, null); + return true; + } catch (IntentSender.SendIntentException e) { + Log.e(TAG, "sendIntent " + this + " failed", e); + return false; + } + } else if (mIntent != null) { + try { + final Intent toSend = new Intent(mIntent); + toSend.fillIn(fillInIntent, 0); + context.startActivityAsUser(toSend, user); + return true; + } catch (Exception e) { + Log.e(TAG, "sendIntent " + this + " failed", e); + return false; + } + } else { + Log.e(TAG, "sendIntent " + this + " failed - no IntentSender or Intent to send"); return false; } } @Override public String toString() { - return "ChooserTarget{" + mIntentSender.getCreatorPackage() + "'" + mTitle + return "ChooserTarget{" + + (mIntentSender != null ? mIntentSender.getCreatorPackage() : mIntent) + + ", " + + "'" + mTitle + "', " + mScore + "}"; } diff --git a/core/java/android/service/chooser/ChooserTargetService.java b/core/java/android/service/chooser/ChooserTargetService.java index 9188806..699bd0a 100644 --- a/core/java/android/service/chooser/ChooserTargetService.java +++ b/core/java/android/service/chooser/ChooserTargetService.java @@ -24,6 +24,7 @@ import android.content.Intent; import android.content.IntentFilter; import android.os.IBinder; import android.os.RemoteException; +import android.util.Log; import java.util.List; @@ -77,9 +78,26 @@ public abstract class ChooserTargetService extends Service { private final String TAG = ChooserTargetService.class.getSimpleName() + '[' + getClass().getSimpleName() + ']'; + private static final boolean DEBUG = false; + + /** + * The Intent action that a ChooserTargetService must respond to + */ @SdkConstant(SdkConstant.SdkConstantType.SERVICE_ACTION) public static final String SERVICE_INTERFACE = "android.service.chooser.ChooserTargetService"; + /** + * The name of the <code>meta-data</code> element that must be present on an + * <code>activity</code> element in a manifest to link it to a ChooserTargetService + */ + public static final String META_DATA_NAME = "android.service.chooser.chooser_target_service"; + + /** + * The permission that a ChooserTargetService must require in order to bind to it. + * If this permission is not enforced the system will skip that ChooserTargetService. + */ + public static final String BIND_PERMISSION = "android.permission.BIND_CHOOSER_TARGET_SERVICE"; + private IChooserTargetServiceWrapper mWrapper = null; /** @@ -105,7 +123,9 @@ public abstract class ChooserTargetService extends Service { @Override public IBinder onBind(Intent intent) { + if (DEBUG) Log.d(TAG, "onBind " + intent); if (!SERVICE_INTERFACE.equals(intent.getAction())) { + if (DEBUG) Log.d(TAG, "bad intent action " + intent.getAction() + "; returning null"); return null; } @@ -121,9 +141,14 @@ public abstract class ChooserTargetService extends Service { IntentFilter matchedFilter, IChooserTargetResult result) throws RemoteException { List<ChooserTarget> targets = null; try { + if (DEBUG) { + Log.d(TAG, "getChooserTargets calling onGetChooserTargets; " + + targetComponentName + " filter: " + matchedFilter); + } targets = onGetChooserTargets(targetComponentName, matchedFilter); } finally { result.sendResult(targets); + if (DEBUG) Log.d(TAG, "Sent results"); } } } diff --git a/core/java/android/service/dreams/DreamService.java b/core/java/android/service/dreams/DreamService.java index 822bfcc..29aaf30 100644 --- a/core/java/android/service/dreams/DreamService.java +++ b/core/java/android/service/dreams/DreamService.java @@ -41,6 +41,7 @@ import android.view.Menu; import android.view.MenuItem; import android.view.MotionEvent; import android.view.PhoneWindow; +import android.view.SearchEvent; import android.view.View; import android.view.ViewGroup; import android.view.Window; @@ -332,6 +333,12 @@ public class DreamService extends Service implements Window.Callback { /** {@inheritDoc} */ @Override + public boolean onSearchRequested(SearchEvent event) { + return onSearchRequested(); + } + + /** {@inheritDoc} */ + @Override public boolean onSearchRequested() { return false; } diff --git a/core/java/android/service/notification/ZenModeConfig.java b/core/java/android/service/notification/ZenModeConfig.java index 56eb510..1ed4779 100644 --- a/core/java/android/service/notification/ZenModeConfig.java +++ b/core/java/android/service/notification/ZenModeConfig.java @@ -654,8 +654,7 @@ public class ZenModeConfig implements Parcelable { } String summary = ""; for (ZenRule automaticRule : config.automaticRules.values()) { - if (automaticRule.enabled && !automaticRule.snoozing - && automaticRule.isTrueOrUnknown()) { + if (automaticRule.isAutomaticActive()) { if (summary.isEmpty()) { summary = automaticRule.name; } else { @@ -745,9 +744,13 @@ public class ZenModeConfig implements Parcelable { component); } + public boolean isAutomaticActive() { + return enabled && !snoozing && component != null && isTrueOrUnknown(); + } + public boolean isTrueOrUnknown() { - return condition == null || condition.state == Condition.STATE_TRUE - || condition.state == Condition.STATE_UNKNOWN; + return condition != null && (condition.state == Condition.STATE_TRUE + || condition.state == Condition.STATE_UNKNOWN); } public static final Parcelable.Creator<ZenRule> CREATOR diff --git a/core/java/android/view/PhoneWindow.java b/core/java/android/view/PhoneWindow.java index 38f4d1c..5af2832 100644 --- a/core/java/android/view/PhoneWindow.java +++ b/core/java/android/view/PhoneWindow.java @@ -1916,7 +1916,7 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback { break; } if (event.isTracking() && !event.isCanceled()) { - launchDefaultSearch(); + launchDefaultSearch(event); } return true; } @@ -4245,14 +4245,19 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback { * * @return true if search window opened */ - private boolean launchDefaultSearch() { + private boolean launchDefaultSearch(KeyEvent event) { boolean result; final Callback cb = getCallback(); if (cb == null || isDestroyed()) { result = false; } else { sendCloseSystemWindows("search"); - result = cb.onSearchRequested(); + int deviceId = event.getDeviceId(); + SearchEvent searchEvent = null; + if (deviceId != 0) { + searchEvent = new SearchEvent(InputDevice.getDevice(deviceId)); + } + result = cb.onSearchRequested(searchEvent); } if (!result && (getContext().getResources().getConfiguration().uiMode & Configuration.UI_MODE_TYPE_MASK) == Configuration.UI_MODE_TYPE_TELEVISION) { diff --git a/core/java/android/view/SearchEvent.java b/core/java/android/view/SearchEvent.java new file mode 100644 index 0000000..ef51e7d --- /dev/null +++ b/core/java/android/view/SearchEvent.java @@ -0,0 +1,40 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.view; + +import android.view.InputDevice; + +/** + * Class that contains information about an event that triggers a search. + */ +public class SearchEvent { + + private InputDevice mInputDevice; + + /** @hide */ + public SearchEvent(InputDevice inputDevice) { + mInputDevice = inputDevice; + } + + /** + * Returns the {@link InputDevice} that triggered the search. + * @return InputDevice the InputDevice that triggered the search. + */ + public InputDevice getInputDevice() { + return mInputDevice; + } +} diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java index 60d2ceb..3fa8c81 100644 --- a/core/java/android/view/View.java +++ b/core/java/android/view/View.java @@ -15087,7 +15087,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * Utility function, called by draw(canvas, parent, drawingTime) to handle the less common * case of an active Animation being run on the view. */ - private boolean drawAnimation(ViewGroup parent, long drawingTime, + private boolean applyLegacyAnimation(ViewGroup parent, long drawingTime, Animation a, boolean scalingRequired) { Transformation invalidationTransform; final int flags = parent.mGroupFlags; @@ -15204,23 +15204,13 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Transformation transformToApply = null; boolean concatMatrix = false; - boolean scalingRequired = false; - boolean caching; + boolean scalingRequired = mAttachInfo != null && mAttachInfo.mScalingRequired; int layerType = getLayerType(); - final boolean hardwareAccelerated = canvas.isHardwareAccelerated(); - if ((flags & ViewGroup.FLAG_CHILDREN_DRAWN_WITH_CACHE) != 0 || - (flags & ViewGroup.FLAG_ALWAYS_DRAWN_WITH_CACHE) != 0) { - caching = true; - // Auto-scaled apps are not hw-accelerated, no need to set scaling flag on DisplayList - if (mAttachInfo != null) scalingRequired = mAttachInfo.mScalingRequired; - } else { - caching = (layerType != LAYER_TYPE_NONE) || hardwareAccelerated; - } final Animation a = getAnimation(); if (a != null) { - more = drawAnimation(parent, drawingTime, a, scalingRequired); + more = applyLegacyAnimation(parent, drawingTime, a, scalingRequired); concatMatrix = a.willChangeTransformationMatrix(); if (concatMatrix) { mPrivateFlags3 |= PFLAG3_VIEW_IS_ANIMATING_TRANSFORM; @@ -15270,34 +15260,32 @@ public class View implements Drawable.Callback, KeyEvent.Callback, RenderNode renderNode = null; Bitmap cache = null; boolean hasDisplayList = false; - if (caching) { - if (!hardwareAccelerated) { - if (layerType != LAYER_TYPE_NONE) { - layerType = LAYER_TYPE_SOFTWARE; - buildDrawingCache(true); - } - cache = getDrawingCache(true); - } else { - switch (layerType) { - case LAYER_TYPE_SOFTWARE: - if (usingRenderNodeProperties) { - hasDisplayList = canHaveDisplayList(); - } else { - buildDrawingCache(true); - cache = getDrawingCache(true); - } - break; - case LAYER_TYPE_HARDWARE: - if (usingRenderNodeProperties) { - hasDisplayList = canHaveDisplayList(); - } - break; - case LAYER_TYPE_NONE: - // Delay getting the display list until animation-driven alpha values are - // set up and possibly passed on to the view + if (!hardwareAccelerated) { + if (layerType != LAYER_TYPE_NONE) { + layerType = LAYER_TYPE_SOFTWARE; + buildDrawingCache(true); + } + cache = getDrawingCache(true); + } else { + switch (layerType) { + case LAYER_TYPE_SOFTWARE: + if (usingRenderNodeProperties) { hasDisplayList = canHaveDisplayList(); - break; - } + } else { + buildDrawingCache(true); + cache = getDrawingCache(true); + } + break; + case LAYER_TYPE_HARDWARE: + if (usingRenderNodeProperties) { + hasDisplayList = canHaveDisplayList(); + } + break; + case LAYER_TYPE_NONE: + // Delay getting the display list until animation-driven alpha values are + // set up and possibly passed on to the view + hasDisplayList = canHaveDisplayList(); + break; } } usingRenderNodeProperties &= hasDisplayList; diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java index 8d06ce2..98b895d 100644 --- a/core/java/android/view/ViewGroup.java +++ b/core/java/android/view/ViewGroup.java @@ -245,8 +245,7 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager // to clip it, even if FLAG_CLIP_TO_PADDING is set private static final int FLAG_PADDING_NOT_NULL = 0x20; - // When set, this ViewGroup caches its children in a Bitmap before starting a layout animation - // Set by default + /** @deprecated - functionality removed */ private static final int FLAG_ANIMATION_CACHE = 0x40; // When set, this ViewGroup converts calls to invalidate(Rect) to invalidate() during a @@ -292,16 +291,11 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager */ private static final int FLAG_ADD_STATES_FROM_CHILDREN = 0x2000; - /** - * When set, this ViewGroup tries to always draw its children using their drawing cache. - */ - static final int FLAG_ALWAYS_DRAWN_WITH_CACHE = 0x4000; + /** @deprecated functionality removed */ + private static final int FLAG_ALWAYS_DRAWN_WITH_CACHE = 0x4000; - /** - * When set, and if FLAG_ALWAYS_DRAWN_WITH_CACHE is not set, this ViewGroup will try to - * draw its children with their drawing cache. - */ - static final int FLAG_CHILDREN_DRAWN_WITH_CACHE = 0x8000; + /** @deprecated functionality removed */ + private static final int FLAG_CHILDREN_DRAWN_WITH_CACHE = 0x8000; /** * When set, this group will go through its list of children to notify them of @@ -584,8 +578,6 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager mGroupFlags |= FLAG_CLIP_CHILDREN; mGroupFlags |= FLAG_CLIP_TO_PADDING; mGroupFlags |= FLAG_ANIMATION_DONE; - mGroupFlags |= FLAG_ANIMATION_CACHE; - mGroupFlags |= FLAG_ALWAYS_DRAWN_WITH_CACHE; if (mContext.getApplicationInfo().targetSdkVersion >= Build.VERSION_CODES.HONEYCOMB) { mGroupFlags |= FLAG_SPLIT_MOTION_EVENTS; @@ -3088,44 +3080,6 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager } @Override - protected void onAnimationStart() { - super.onAnimationStart(); - - // When this ViewGroup's animation starts, build the cache for the children - if ((mGroupFlags & FLAG_ANIMATION_CACHE) == FLAG_ANIMATION_CACHE) { - final int count = mChildrenCount; - final View[] children = mChildren; - final boolean buildCache = !isHardwareAccelerated(); - - for (int i = 0; i < count; i++) { - final View child = children[i]; - if ((child.mViewFlags & VISIBILITY_MASK) == VISIBLE) { - child.setDrawingCacheEnabled(true); - if (buildCache) { - child.buildDrawingCache(true); - } - } - } - - mGroupFlags |= FLAG_CHILDREN_DRAWN_WITH_CACHE; - } - } - - @Override - protected void onAnimationEnd() { - super.onAnimationEnd(); - - // When this ViewGroup's animation ends, destroy the cache of the children - if ((mGroupFlags & FLAG_ANIMATION_CACHE) == FLAG_ANIMATION_CACHE) { - mGroupFlags &= ~FLAG_CHILDREN_DRAWN_WITH_CACHE; - - if ((mPersistentDrawingCache & PERSISTENT_ANIMATION_CACHE) == 0) { - setChildrenDrawingCacheEnabled(false); - } - } - } - - @Override Bitmap createSnapshot(Bitmap.Config quality, int backgroundColor, boolean skipChildren) { int count = mChildrenCount; int[] visibilities = null; @@ -3295,8 +3249,6 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager int flags = mGroupFlags; if ((flags & FLAG_RUN_ANIMATION) != 0 && canAnimate()) { - final boolean cache = (mGroupFlags & FLAG_ANIMATION_CACHE) == FLAG_ANIMATION_CACHE; - final boolean buildCache = !isHardwareAccelerated(); for (int i = 0; i < childrenCount; i++) { final View child = children[i]; @@ -3304,12 +3256,6 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager final LayoutParams params = child.getLayoutParams(); attachLayoutAnimationParameters(child, params, i, childrenCount); bindLayoutAnimation(child); - if (cache) { - child.setDrawingCacheEnabled(true); - if (buildCache) { - child.buildDrawingCache(true); - } - } } } @@ -3323,10 +3269,6 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager mGroupFlags &= ~FLAG_RUN_ANIMATION; mGroupFlags &= ~FLAG_ANIMATION_DONE; - if (cache) { - mGroupFlags |= FLAG_CHILDREN_DRAWN_WITH_CACHE; - } - if (mAnimationListener != null) { mAnimationListener.onAnimationStart(controller.getAnimation()); } @@ -3504,13 +3446,6 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager post(end); } - if ((mGroupFlags & FLAG_ANIMATION_CACHE) == FLAG_ANIMATION_CACHE) { - mGroupFlags &= ~FLAG_CHILDREN_DRAWN_WITH_CACHE; - if ((mPersistentDrawingCache & PERSISTENT_ANIMATION_CACHE) == 0) { - setChildrenDrawingCacheEnabled(false); - } - } - invalidate(true); } @@ -5278,8 +5213,10 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager * * @see #setAnimationCacheEnabled(boolean) * @see View#setDrawingCacheEnabled(boolean) + * + * @deprecated As of {@link android.os.Build.VERSION_CODES#MNC}, this property is ignored. + * Caching behavior of children may be controlled through {@link View#setLayerType(int, Paint)}. */ - @ViewDebug.ExportedProperty public boolean isAnimationCacheEnabled() { return (mGroupFlags & FLAG_ANIMATION_CACHE) == FLAG_ANIMATION_CACHE; } @@ -5294,6 +5231,9 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager * * @see #isAnimationCacheEnabled() * @see View#setDrawingCacheEnabled(boolean) + * + * @deprecated As of {@link android.os.Build.VERSION_CODES#MNC}, this property is ignored. + * Caching behavior of children may be controlled through {@link View#setLayerType(int, Paint)}. */ public void setAnimationCacheEnabled(boolean enabled) { setBooleanFlag(FLAG_ANIMATION_CACHE, enabled); @@ -5308,8 +5248,10 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager * @see #setAlwaysDrawnWithCacheEnabled(boolean) * @see #setChildrenDrawnWithCacheEnabled(boolean) * @see View#setDrawingCacheEnabled(boolean) + * + * @deprecated As of {@link android.os.Build.VERSION_CODES#MNC}, this property is ignored. + * Child views may no longer have their caching behavior disabled by parents. */ - @ViewDebug.ExportedProperty(category = "drawing") public boolean isAlwaysDrawnWithCacheEnabled() { return (mGroupFlags & FLAG_ALWAYS_DRAWN_WITH_CACHE) == FLAG_ALWAYS_DRAWN_WITH_CACHE; } @@ -5330,6 +5272,9 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager * @see #setChildrenDrawnWithCacheEnabled(boolean) * @see View#setDrawingCacheEnabled(boolean) * @see View#setDrawingCacheQuality(int) + * + * @deprecated As of {@link android.os.Build.VERSION_CODES#MNC}, this property is ignored. + * Child views may no longer have their caching behavior disabled by parents. */ public void setAlwaysDrawnWithCacheEnabled(boolean always) { setBooleanFlag(FLAG_ALWAYS_DRAWN_WITH_CACHE, always); @@ -5343,8 +5288,11 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager * * @see #setAlwaysDrawnWithCacheEnabled(boolean) * @see #setChildrenDrawnWithCacheEnabled(boolean) + * + * @deprecated As of {@link android.os.Build.VERSION_CODES#MNC}, this property is ignored. + * Child views may no longer be forced to cache their rendering state by their parents. + * Use {@link View#setLayerType(int, Paint)} on individual Views instead. */ - @ViewDebug.ExportedProperty(category = "drawing") protected boolean isChildrenDrawnWithCacheEnabled() { return (mGroupFlags & FLAG_CHILDREN_DRAWN_WITH_CACHE) == FLAG_CHILDREN_DRAWN_WITH_CACHE; } @@ -5361,6 +5309,10 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager * * @see #setAlwaysDrawnWithCacheEnabled(boolean) * @see #isChildrenDrawnWithCacheEnabled() + * + * @deprecated As of {@link android.os.Build.VERSION_CODES#MNC}, this property is ignored. + * Child views may no longer be forced to cache their rendering state by their parents. + * Use {@link View#setLayerType(int, Paint)} on individual Views instead. */ protected void setChildrenDrawnWithCacheEnabled(boolean enabled) { setBooleanFlag(FLAG_CHILDREN_DRAWN_WITH_CACHE, enabled); diff --git a/core/java/android/view/Window.java b/core/java/android/view/Window.java index 36f047e..9d0d5ff 100644 --- a/core/java/android/view/Window.java +++ b/core/java/android/view/Window.java @@ -414,6 +414,15 @@ public abstract class Window { public boolean onSearchRequested(); /** + * Called when the user signals the desire to start a search. + * + * @param searchEvent A {@link SearchEvent} describing the signal to + * start a search. + * @return true if search launched, false if activity refuses (blocks) + */ + public boolean onSearchRequested(SearchEvent searchEvent); + + /** * Called when an action mode is being started for this window. Gives the * callback an opportunity to handle the action mode in its own unique and * beautiful way. If this method returns null the system can choose a way diff --git a/core/java/android/view/WindowCallbackWrapper.java b/core/java/android/view/WindowCallbackWrapper.java index 979ee95..8ce1f8c 100644 --- a/core/java/android/view/WindowCallbackWrapper.java +++ b/core/java/android/view/WindowCallbackWrapper.java @@ -122,6 +122,11 @@ public class WindowCallbackWrapper implements Window.Callback { } @Override + public boolean onSearchRequested(SearchEvent searchEvent) { + return mWrapped.onSearchRequested(searchEvent); + } + + @Override public boolean onSearchRequested() { return mWrapped.onSearchRequested(); } diff --git a/core/java/android/widget/ActionMenuPresenter.java b/core/java/android/widget/ActionMenuPresenter.java index f951dc2..e0b0e1f 100644 --- a/core/java/android/widget/ActionMenuPresenter.java +++ b/core/java/android/widget/ActionMenuPresenter.java @@ -40,7 +40,6 @@ import android.view.ViewGroup; import android.view.ViewTreeObserver; import android.view.accessibility.AccessibilityNodeInfo; import android.widget.ListPopupWindow.ForwardingListener; -import com.android.internal.transition.ActionBarTransition; import com.android.internal.view.ActionBarPolicy; import com.android.internal.view.menu.ActionMenuItemView; import com.android.internal.view.menu.BaseMenuPresenter; @@ -99,7 +98,30 @@ public class ActionMenuPresenter extends BaseMenuPresenter // The list of currently running animations on menu items. private List<ItemAnimationInfo> mRunningItemAnimations = new ArrayList<ItemAnimationInfo>(); + private ViewTreeObserver.OnPreDrawListener mItemAnimationPreDrawListener = + new ViewTreeObserver.OnPreDrawListener() { + @Override + public boolean onPreDraw() { + computeMenuItemAnimationInfo(false); + ((View) mMenuView).getViewTreeObserver().removeOnPreDrawListener(this); + runItemAnimations(); + return true; + } + }; + private View.OnAttachStateChangeListener mAttachStateChangeListener = + new View.OnAttachStateChangeListener() { + @Override + public void onViewAttachedToWindow(View v) { + } + @Override + public void onViewDetachedFromWindow(View v) { + ((View) mMenuView).getViewTreeObserver().removeOnPreDrawListener( + mItemAnimationPreDrawListener); + mPreLayoutItems.clear(); + mPostLayoutItems.clear(); + } + }; public ActionMenuPresenter(Context context) { @@ -177,8 +199,15 @@ public class ActionMenuPresenter extends BaseMenuPresenter @Override public MenuView getMenuView(ViewGroup root) { + MenuView oldMenuView = mMenuView; MenuView result = super.getMenuView(root); - ((ActionMenuView) result).setPresenter(this); + if (oldMenuView != result) { + ((ActionMenuView) result).setPresenter(this); + if (oldMenuView != null) { + ((View) oldMenuView).removeOnAttachStateChangeListener(mAttachStateChangeListener); + } + ((View) result).addOnAttachStateChangeListener(mAttachStateChangeListener); + } return result; } @@ -226,11 +255,11 @@ public class ActionMenuPresenter extends BaseMenuPresenter * into the MenuItemLayoutInfo structure to store the appropriate position values. */ private void computeMenuItemAnimationInfo(boolean preLayout) { - final ViewGroup menuViewParent = (ViewGroup) mMenuView; - final int count = menuViewParent.getChildCount(); + final ViewGroup menuView = (ViewGroup) mMenuView; + final int count = menuView.getChildCount(); SparseArray items = preLayout ? mPreLayoutItems : mPostLayoutItems; for (int i = 0; i < count; ++i) { - View child = menuViewParent.getChildAt(i); + View child = menuView.getChildAt(i); final int id = child.getId(); if (id > 0 && child.getWidth() != 0 && child.getHeight() != 0) { MenuItemLayoutInfo info = new MenuItemLayoutInfo(child, preLayout); @@ -377,28 +406,16 @@ public class ActionMenuPresenter extends BaseMenuPresenter * which is then fed into runItemAnimations() */ private void setupItemAnimations() { - final ViewGroup menuViewParent = (ViewGroup) mMenuView; computeMenuItemAnimationInfo(true); - final ViewTreeObserver observer = menuViewParent.getViewTreeObserver(); - if (observer != null) { - observer.addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() { - @Override - public boolean onPreDraw() { - computeMenuItemAnimationInfo(false); - observer.removeOnPreDrawListener(this); - runItemAnimations(); - return true; - } - }); - } + ((View) mMenuView).getViewTreeObserver(). + addOnPreDrawListener(mItemAnimationPreDrawListener); } @Override public void updateMenuView(boolean cleared) { final ViewGroup menuViewParent = (ViewGroup) ((View) mMenuView).getParent(); if (menuViewParent != null) { -// setupItemAnimations(); - ActionBarTransition.beginDelayedTransition(menuViewParent); + setupItemAnimations(); } super.updateMenuView(cleared); @@ -736,8 +753,14 @@ public class ActionMenuPresenter extends BaseMenuPresenter } public void setMenuView(ActionMenuView menuView) { - mMenuView = menuView; - menuView.initialize(mMenu); + if (menuView != mMenuView) { + if (mMenuView != null) { + ((View) mMenuView).removeOnAttachStateChangeListener(mAttachStateChangeListener); + } + mMenuView = menuView; + menuView.initialize(mMenu); + menuView.addOnAttachStateChangeListener(mAttachStateChangeListener); + } } public void setOverflowTintList(ColorStateList tint) { diff --git a/core/java/android/widget/DatePickerCalendarDelegate.java b/core/java/android/widget/DatePickerCalendarDelegate.java index a157087..06a5bd2 100755 --- a/core/java/android/widget/DatePickerCalendarDelegate.java +++ b/core/java/android/widget/DatePickerCalendarDelegate.java @@ -534,22 +534,23 @@ class DatePickerCalendarDelegate extends DatePicker.AbstractDatePickerDelegate { @Override public void onRestoreInstanceState(Parcelable state) { - SavedState ss = (SavedState) state; + final SavedState ss = (SavedState) state; // TODO: Move instance state into DayPickerView, YearPickerView. mCurrentDate.set(ss.getSelectedYear(), ss.getSelectedMonth(), ss.getSelectedDay()); - mCurrentView = ss.getCurrentView(); mMinDate.setTimeInMillis(ss.getMinDate()); mMaxDate.setTimeInMillis(ss.getMaxDate()); onCurrentDateChanged(false); - setCurrentView(mCurrentView); + + final int currentView = ss.getCurrentView(); + setCurrentView(currentView); final int listPosition = ss.getListPosition(); if (listPosition != -1) { - if (mCurrentView == VIEW_MONTH_DAY) { + if (currentView == VIEW_MONTH_DAY) { mDayPickerView.setCurrentItem(listPosition); - } else if (mCurrentView == VIEW_YEAR) { + } else if (currentView == VIEW_YEAR) { final int listPositionOffset = ss.getListPositionOffset(); mYearPickerView.setSelectionFromTop(listPosition, listPositionOffset); } @@ -601,7 +602,6 @@ class DatePickerCalendarDelegate extends DatePicker.AbstractDatePickerDelegate { * Class for managing state storing/restoring. */ private static class SavedState extends View.BaseSavedState { - private final int mSelectedYear; private final int mSelectedMonth; private final int mSelectedDay; diff --git a/core/java/android/widget/DayPickerView.java b/core/java/android/widget/DayPickerView.java index ec2528f..0e0b2d3 100644 --- a/core/java/android/widget/DayPickerView.java +++ b/core/java/android/widget/DayPickerView.java @@ -22,9 +22,12 @@ import com.android.internal.R; import android.content.Context; import android.content.res.ColorStateList; import android.content.res.TypedArray; +import android.graphics.drawable.Drawable; import android.util.AttributeSet; import android.util.MathUtils; +import android.view.View; +import java.util.ArrayList; import java.util.Calendar; import java.util.Locale; @@ -41,6 +44,8 @@ class DayPickerView extends ViewPager { private final Calendar mMinDate = Calendar.getInstance(); private final Calendar mMaxDate = Calendar.getInstance(); + private final ArrayList<View> mMatchParentChildren = new ArrayList<>(1); + private final DayPickerAdapter mAdapter; /** Temporary calendar used for date calculations. */ @@ -140,6 +145,93 @@ class DayPickerView extends ViewPager { }); } + @Override + protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + populate(); + + // Everything below is mostly copied from FrameLayout. + int count = getChildCount(); + + final boolean measureMatchParentChildren = + MeasureSpec.getMode(widthMeasureSpec) != MeasureSpec.EXACTLY || + MeasureSpec.getMode(heightMeasureSpec) != MeasureSpec.EXACTLY; + + int maxHeight = 0; + int maxWidth = 0; + int childState = 0; + + for (int i = 0; i < count; i++) { + final View child = getChildAt(i); + if (child.getVisibility() != GONE) { + measureChild(child, widthMeasureSpec, heightMeasureSpec); + final LayoutParams lp = (LayoutParams) child.getLayoutParams(); + maxWidth = Math.max(maxWidth, child.getMeasuredWidth()); + maxHeight = Math.max(maxHeight, child.getMeasuredHeight()); + childState = combineMeasuredStates(childState, child.getMeasuredState()); + if (measureMatchParentChildren) { + if (lp.width == LayoutParams.MATCH_PARENT || + lp.height == LayoutParams.MATCH_PARENT) { + mMatchParentChildren.add(child); + } + } + } + } + + // Account for padding too + maxWidth += getPaddingLeft() + getPaddingRight(); + maxHeight += getPaddingTop() + getPaddingBottom(); + + // Check against our minimum height and width + maxHeight = Math.max(maxHeight, getSuggestedMinimumHeight()); + maxWidth = Math.max(maxWidth, getSuggestedMinimumWidth()); + + // Check against our foreground's minimum height and width + final Drawable drawable = getForeground(); + if (drawable != null) { + maxHeight = Math.max(maxHeight, drawable.getMinimumHeight()); + maxWidth = Math.max(maxWidth, drawable.getMinimumWidth()); + } + + setMeasuredDimension(resolveSizeAndState(maxWidth, widthMeasureSpec, childState), + resolveSizeAndState(maxHeight, heightMeasureSpec, + childState << MEASURED_HEIGHT_STATE_SHIFT)); + + count = mMatchParentChildren.size(); + if (count > 1) { + for (int i = 0; i < count; i++) { + final View child = mMatchParentChildren.get(i); + + final LayoutParams lp = (LayoutParams) child.getLayoutParams(); + final int childWidthMeasureSpec; + final int childHeightMeasureSpec; + + if (lp.width == LayoutParams.MATCH_PARENT) { + childWidthMeasureSpec = MeasureSpec.makeMeasureSpec( + getMeasuredWidth() - getPaddingLeft() - getPaddingRight(), + MeasureSpec.EXACTLY); + } else { + childWidthMeasureSpec = getChildMeasureSpec(widthMeasureSpec, + getPaddingLeft() + getPaddingRight(), + lp.width); + } + + if (lp.height == LayoutParams.MATCH_PARENT) { + childHeightMeasureSpec = MeasureSpec.makeMeasureSpec( + getMeasuredHeight() - getPaddingTop() - getPaddingBottom(), + MeasureSpec.EXACTLY); + } else { + childHeightMeasureSpec = getChildMeasureSpec(heightMeasureSpec, + getPaddingTop() + getPaddingBottom(), + lp.height); + } + + child.measure(childWidthMeasureSpec, childHeightMeasureSpec); + } + } + + mMatchParentChildren.clear(); + } + public void setDayOfWeekTextAppearance(int resId) { mAdapter.setDayOfWeekTextAppearance(resId); } diff --git a/core/java/android/widget/SimpleMonthView.java b/core/java/android/widget/SimpleMonthView.java index d9f1f0e..aa7f0b6 100644 --- a/core/java/android/widget/SimpleMonthView.java +++ b/core/java/android/widget/SimpleMonthView.java @@ -585,7 +585,6 @@ class SimpleMonthView extends View { mToday = day; } } - mNumWeeks = calculateNumRows(); // Invalidate the old title. mTitle = null; @@ -616,18 +615,6 @@ class SimpleMonthView extends View { } } - public void reuse() { - mNumWeeks = MAX_WEEKS_IN_MONTH; - requestLayout(); - } - - private int calculateNumRows() { - final int offset = findDayOffset(); - final int dividend = (offset + mDaysInMonth) / DAYS_IN_WEEK; - final int remainder = (offset + mDaysInMonth) % DAYS_IN_WEEK; - return dividend + (remainder > 0 ? 1 : 0); - } - private boolean sameDay(int day, Calendar today) { return mYear == today.get(Calendar.YEAR) && mMonth == today.get(Calendar.MONTH) && day == today.get(Calendar.DAY_OF_MONTH); @@ -635,8 +622,9 @@ class SimpleMonthView extends View { @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { - final int preferredHeight = mDesiredDayHeight * mNumWeeks + mDesiredDayOfWeekHeight - + mDesiredMonthHeight + getPaddingTop() + getPaddingBottom(); + final int preferredHeight = mDesiredDayHeight * MAX_WEEKS_IN_MONTH + + mDesiredDayOfWeekHeight + mDesiredMonthHeight + + getPaddingTop() + getPaddingBottom(); final int preferredWidth = mDesiredCellWidth * DAYS_IN_WEEK + getPaddingStart() + getPaddingEnd(); final int resolvedWidth = resolveSize(preferredWidth, widthMeasureSpec); diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java index 5d60f99..9bbf375 100644 --- a/core/java/android/widget/TextView.java +++ b/core/java/android/widget/TextView.java @@ -540,6 +540,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener private Layout mLayout; private boolean mLocaleChanged = false; + @ViewDebug.ExportedProperty(category = "text") private int mGravity = Gravity.TOP | Gravity.START; private boolean mHorizontallyScrolling; diff --git a/core/java/android/widget/YearPickerView.java b/core/java/android/widget/YearPickerView.java index 7182414..89e59f9 100644 --- a/core/java/android/widget/YearPickerView.java +++ b/core/java/android/widget/YearPickerView.java @@ -178,24 +178,29 @@ class YearPickerView extends ListView { @Override public View getView(int position, View convertView, ViewGroup parent) { - if (convertView == null) { - convertView = mInflater.inflate(ITEM_LAYOUT, parent, false); + final TextView v; + final boolean hasNewView = convertView == null; + if (hasNewView) { + v = (TextView) mInflater.inflate(ITEM_LAYOUT, parent, false); + } else { + v = (TextView) convertView; } final int year = getYearForPosition(position); final boolean activated = mActivatedYear == year; - final int textAppearanceResId; - if (activated && ITEM_TEXT_ACTIVATED_APPEARANCE != 0) { - textAppearanceResId = ITEM_TEXT_ACTIVATED_APPEARANCE; - } else { - textAppearanceResId = ITEM_TEXT_APPEARANCE; + if (hasNewView || v.isActivated() != activated) { + final int textAppearanceResId; + if (activated && ITEM_TEXT_ACTIVATED_APPEARANCE != 0) { + textAppearanceResId = ITEM_TEXT_ACTIVATED_APPEARANCE; + } else { + textAppearanceResId = ITEM_TEXT_APPEARANCE; + } + v.setTextAppearance(textAppearanceResId); + v.setActivated(activated); } - final TextView v = (TextView) convertView; - v.setText("" + year); - v.setTextAppearance(v.getContext(), textAppearanceResId); - v.setActivated(activated); + v.setText(Integer.toString(year)); return v; } diff --git a/core/java/com/android/internal/app/AlertController.java b/core/java/com/android/internal/app/AlertController.java index 9dabb4e..b8110e3 100644 --- a/core/java/com/android/internal/app/AlertController.java +++ b/core/java/com/android/internal/app/AlertController.java @@ -170,9 +170,8 @@ public class AlertController { } private static boolean shouldCenterSingleButton(Context context) { - TypedValue outValue = new TypedValue(); - context.getTheme().resolveAttribute(com.android.internal.R.attr.alertDialogCenterButtons, - outValue, true); + final TypedValue outValue = new TypedValue(); + context.getTheme().resolveAttribute(R.attr.alertDialogCenterButtons, outValue, true); return outValue.data != 0; } @@ -182,27 +181,25 @@ public class AlertController { mWindow = window; mHandler = new ButtonHandler(di); - TypedArray a = context.obtainStyledAttributes(null, - com.android.internal.R.styleable.AlertDialog, - com.android.internal.R.attr.alertDialogStyle, 0); + final TypedArray a = context.obtainStyledAttributes(null, + R.styleable.AlertDialog, R.attr.alertDialogStyle, 0); - mAlertDialogLayout = a.getResourceId(com.android.internal.R.styleable.AlertDialog_layout, - com.android.internal.R.layout.alert_dialog); + mAlertDialogLayout = a.getResourceId( + R.styleable.AlertDialog_layout, R.layout.alert_dialog); mButtonPanelSideLayout = a.getResourceId( - com.android.internal.R.styleable.AlertDialog_buttonPanelSideLayout, 0); - + R.styleable.AlertDialog_buttonPanelSideLayout, 0); mListLayout = a.getResourceId( - com.android.internal.R.styleable.AlertDialog_listLayout, - com.android.internal.R.layout.select_dialog); + R.styleable.AlertDialog_listLayout, R.layout.select_dialog); + mMultiChoiceItemLayout = a.getResourceId( - com.android.internal.R.styleable.AlertDialog_multiChoiceItemLayout, - com.android.internal.R.layout.select_dialog_multichoice); + R.styleable.AlertDialog_multiChoiceItemLayout, + R.layout.select_dialog_multichoice); mSingleChoiceItemLayout = a.getResourceId( - com.android.internal.R.styleable.AlertDialog_singleChoiceItemLayout, - com.android.internal.R.layout.select_dialog_singlechoice); + R.styleable.AlertDialog_singleChoiceItemLayout, + R.layout.select_dialog_singlechoice); mListItemLayout = a.getResourceId( - com.android.internal.R.styleable.AlertDialog_listItemLayout, - com.android.internal.R.layout.select_dialog_item); + R.styleable.AlertDialog_listItemLayout, + R.layout.select_dialog_item); a.recycle(); } @@ -1067,9 +1064,9 @@ public class AlertController { } private void createListView(final AlertController dialog) { - final RecycleListView listView = (RecycleListView) - mInflater.inflate(dialog.mListLayout, null); - ListAdapter adapter; + final RecycleListView listView = + (RecycleListView) mInflater.inflate(dialog.mListLayout, null); + final ListAdapter adapter; if (mIsMultiChoice) { if (mCursor == null) { @@ -1115,14 +1112,20 @@ public class AlertController { }; } } else { - int layout = mIsSingleChoice - ? dialog.mSingleChoiceItemLayout : dialog.mListItemLayout; - if (mCursor == null) { - adapter = (mAdapter != null) ? mAdapter - : new CheckedItemAdapter(mContext, layout, R.id.text1, mItems); + final int layout; + if (mIsSingleChoice) { + layout = dialog.mSingleChoiceItemLayout; + } else { + layout = dialog.mListItemLayout; + } + + if (mCursor != null) { + adapter = new SimpleCursorAdapter(mContext, layout, mCursor, + new String[] { mLabelColumn }, new int[] { R.id.text1 }); + } else if (mAdapter != null) { + adapter = mAdapter; } else { - adapter = new SimpleCursorAdapter(mContext, layout, - mCursor, new String[]{mLabelColumn}, new int[]{R.id.text1}); + adapter = new CheckedItemAdapter(mContext, layout, R.id.text1, mItems); } } diff --git a/core/java/com/android/internal/app/ChooserActivity.java b/core/java/com/android/internal/app/ChooserActivity.java index 64bd6b6..8403e77 100644 --- a/core/java/com/android/internal/app/ChooserActivity.java +++ b/core/java/com/android/internal/app/ChooserActivity.java @@ -18,20 +18,87 @@ package com.android.internal.app; import android.app.Activity; import android.content.ComponentName; +import android.content.Context; import android.content.Intent; import android.content.IntentSender; +import android.content.ServiceConnection; import android.content.pm.ActivityInfo; +import android.content.pm.PackageManager; +import android.content.pm.PackageManager.NameNotFoundException; +import android.content.pm.ResolveInfo; +import android.graphics.drawable.BitmapDrawable; +import android.graphics.drawable.Drawable; import android.os.Bundle; +import android.os.Handler; +import android.os.IBinder; +import android.os.Message; import android.os.Parcelable; +import android.os.RemoteException; +import android.os.UserHandle; +import android.service.chooser.ChooserTarget; +import android.service.chooser.ChooserTargetService; +import android.service.chooser.IChooserTargetResult; +import android.service.chooser.IChooserTargetService; +import android.text.TextUtils; import android.util.Log; import android.util.Slog; +import android.view.View; +import android.view.ViewGroup; + +import java.util.ArrayList; +import java.util.List; public class ChooserActivity extends ResolverActivity { private static final String TAG = "ChooserActivity"; + private static final boolean DEBUG = false; + + private static final int QUERY_TARGET_LIMIT = 5; + private static final int WATCHDOG_TIMEOUT_MILLIS = 5000; + private Bundle mReplacementExtras; private IntentSender mChosenComponentSender; + private ChooserTarget[] mCallerChooserTargets; + + private final List<ChooserTargetServiceConnection> mServiceConnections = new ArrayList<>(); + + private static final int CHOOSER_TARGET_SERVICE_RESULT = 1; + private static final int CHOOSER_TARGET_SERVICE_WATCHDOG_TIMEOUT = 2; + + private Handler mTargetResultHandler = new Handler() { + @Override + public void handleMessage(Message msg) { + switch (msg.what) { + case CHOOSER_TARGET_SERVICE_RESULT: + if (DEBUG) Log.d(TAG, "CHOOSER_TARGET_SERVICE_RESULT"); + if (isDestroyed()) break; + final ServiceResultInfo sri = (ServiceResultInfo) msg.obj; + if (!mServiceConnections.contains(sri.connection)) { + Log.w(TAG, "ChooserTargetServiceConnection " + sri.connection + + " returned after being removed from active connections." + + " Have you considered returning results faster?"); + break; + } + final ChooserListAdapter cla = (ChooserListAdapter) getAdapter(); + cla.addServiceResults(sri.originalTarget, sri.resultTargets); + unbindService(sri.connection); + mServiceConnections.remove(sri.connection); + break; + + case CHOOSER_TARGET_SERVICE_WATCHDOG_TIMEOUT: + if (DEBUG) { + Log.d(TAG, "CHOOSER_TARGET_SERVICE_WATCHDOG_TIMEOUT; unbinding services"); + } + unbindRemainingServices(); + break; + + default: + super.handleMessage(msg); + } + } + }; + @Override protected void onCreate(Bundle savedInstanceState) { Intent intent = getIntent(); @@ -42,7 +109,7 @@ public class ChooserActivity extends ResolverActivity { super.onCreate(null); return; } - Intent target = (Intent)targetParcelable; + Intent target = (Intent) targetParcelable; if (target != null) { modifyTargetIntent(target); } @@ -68,6 +135,22 @@ public class ChooserActivity extends ResolverActivity { initialIntents[i] = in; } } + + pa = intent.getParcelableArrayExtra(Intent.EXTRA_CHOOSER_TARGETS); + if (pa != null) { + final ChooserTarget[] targets = new ChooserTarget[pa.length]; + for (int i = 0; i < pa.length; i++) { + if (!(pa[i] instanceof ChooserTarget)) { + Log.w("ChooserActivity", "Chooser target #" + i + " is not a ChooserTarget: " + + pa[i]); + finish(); + super.onCreate(null); + return; + } + targets[i] = (ChooserTarget) pa[i]; + } + mCallerChooserTargets = targets; + } mChosenComponentSender = intent.getParcelableExtra( Intent.EXTRA_CHOSEN_COMPONENT_INTENT_SENDER); setSafeForwardingMode(true); @@ -94,9 +177,9 @@ public class ChooserActivity extends ResolverActivity { } @Override - public void onActivityStarted(Intent intent) { + void onActivityStarted(TargetInfo cti) { if (mChosenComponentSender != null) { - final ComponentName target = intent.getComponent(); + final ComponentName target = cti.getResolvedComponentName(); if (target != null) { final Intent fillIn = new Intent().putExtra(Intent.EXTRA_CHOSEN_COMPONENT, target); try { @@ -109,6 +192,16 @@ public class ChooserActivity extends ResolverActivity { } } + @Override + int getLayoutResource() { + return com.android.internal.R.layout.chooser_grid; + } + + @Override + boolean shouldGetActivityMetadata() { + return true; + } + private void modifyTargetIntent(Intent in) { final String action = in.getAction(); if (Intent.ACTION_SEND.equals(action) || @@ -117,4 +210,297 @@ public class ChooserActivity extends ResolverActivity { Intent.FLAG_ACTIVITY_MULTIPLE_TASK); } } + + void queryTargetServices(ChooserListAdapter adapter) { + final PackageManager pm = getPackageManager(); + int targetsToQuery = 0; + for (int i = 0, N = adapter.getDisplayResolveInfoCount(); i < N; i++) { + final DisplayResolveInfo dri = adapter.getDisplayResolveInfo(i); + final ActivityInfo ai = dri.getResolveInfo().activityInfo; + final Bundle md = ai.metaData; + final String serviceName = md != null ? convertServiceName(ai.packageName, + md.getString(ChooserTargetService.META_DATA_NAME)) : null; + if (serviceName != null) { + final ComponentName serviceComponent = new ComponentName( + ai.packageName, serviceName); + final Intent serviceIntent = new Intent(ChooserTargetService.SERVICE_INTERFACE) + .setComponent(serviceComponent); + + if (DEBUG) { + Log.d(TAG, "queryTargets found target with service " + serviceComponent); + } + + try { + final String perm = pm.getServiceInfo(serviceComponent, 0).permission; + if (!ChooserTargetService.BIND_PERMISSION.equals(perm)) { + Log.w(TAG, "ChooserTargetService " + serviceComponent + " does not require" + + " permission " + ChooserTargetService.BIND_PERMISSION + + " - this service will not be queried for ChooserTargets." + + " add android:permission=\"" + + ChooserTargetService.BIND_PERMISSION + "\"" + + " to the <service> tag for " + serviceComponent + + " in the manifest."); + continue; + } + } catch (NameNotFoundException e) { + Log.e(TAG, "Could not look up service " + serviceComponent, e); + continue; + } + + final ChooserTargetServiceConnection conn = new ChooserTargetServiceConnection(dri); + if (bindServiceAsUser(serviceIntent, conn, BIND_AUTO_CREATE | BIND_NOT_FOREGROUND, + UserHandle.CURRENT)) { + if (DEBUG) { + Log.d(TAG, "Binding service connection for target " + dri + + " intent " + serviceIntent); + } + mServiceConnections.add(conn); + targetsToQuery++; + } + } + if (targetsToQuery >= QUERY_TARGET_LIMIT) { + if (DEBUG) Log.d(TAG, "queryTargets hit query target limit " + QUERY_TARGET_LIMIT); + break; + } + } + + if (!mServiceConnections.isEmpty()) { + if (DEBUG) Log.d(TAG, "queryTargets setting watchdog timer for " + + WATCHDOG_TIMEOUT_MILLIS + "ms"); + mTargetResultHandler.sendEmptyMessageDelayed(CHOOSER_TARGET_SERVICE_WATCHDOG_TIMEOUT, + WATCHDOG_TIMEOUT_MILLIS); + } + } + + private String convertServiceName(String packageName, String serviceName) { + if (TextUtils.isEmpty(serviceName)) { + return null; + } + + final String fullName; + if (serviceName.startsWith(".")) { + // Relative to the app package. Prepend the app package name. + fullName = packageName + serviceName; + } else if (serviceName.indexOf('.') >= 0) { + // Fully qualified package name. + fullName = serviceName; + } else { + fullName = null; + } + return fullName; + } + + void unbindRemainingServices() { + if (DEBUG) { + Log.d(TAG, "unbindRemainingServices, " + mServiceConnections.size() + " left"); + } + for (int i = 0, N = mServiceConnections.size(); i < N; i++) { + final ChooserTargetServiceConnection conn = mServiceConnections.get(i); + if (DEBUG) Log.d(TAG, "unbinding " + conn); + unbindService(conn); + } + mServiceConnections.clear(); + mTargetResultHandler.removeMessages(CHOOSER_TARGET_SERVICE_WATCHDOG_TIMEOUT); + } + + @Override + ResolveListAdapter createAdapter(Context context, Intent[] initialIntents, + List<ResolveInfo> rList, int launchedFromUid, boolean filterLastUsed) { + final ChooserListAdapter adapter = new ChooserListAdapter(context, initialIntents, rList, + launchedFromUid, filterLastUsed); + if (DEBUG) Log.d(TAG, "Adapter created; querying services"); + queryTargetServices(adapter); + return adapter; + } + + class ChooserTargetInfo implements TargetInfo { + private final TargetInfo mSourceInfo; + private final ChooserTarget mChooserTarget; + private final Drawable mDisplayIcon; + + public ChooserTargetInfo(TargetInfo sourceInfo, ChooserTarget chooserTarget) { + mSourceInfo = sourceInfo; + mChooserTarget = chooserTarget; + mDisplayIcon = new BitmapDrawable(getResources(), chooserTarget.getIcon()); + } + + @Override + public Intent getResolvedIntent() { + final Intent targetIntent = mChooserTarget.getIntent(); + return targetIntent != null ? targetIntent : mSourceInfo.getResolvedIntent(); + } + + @Override + public ComponentName getResolvedComponentName() { + return mSourceInfo.getResolvedComponentName(); + } + + @Override + public boolean start(Activity activity, Bundle options) { + return mChooserTarget.sendIntent(activity, mSourceInfo.getResolvedIntent()); + } + + @Override + public boolean startAsCaller(Activity activity, Bundle options, int userId) { + return mChooserTarget.sendIntentAsCaller(activity, mSourceInfo.getResolvedIntent(), + userId); + } + + @Override + public boolean startAsUser(Activity activity, Bundle options, UserHandle user) { + return mChooserTarget.sendIntentAsUser(activity, mSourceInfo.getResolvedIntent(), user); + } + + @Override + public ResolveInfo getResolveInfo() { + return mSourceInfo.getResolveInfo(); + } + + @Override + public CharSequence getDisplayLabel() { + return mChooserTarget.getTitle(); + } + + @Override + public CharSequence getExtendedInfo() { + return mSourceInfo.getExtendedInfo(); + } + + @Override + public Drawable getDisplayIcon() { + return mDisplayIcon; + } + } + + public class ChooserListAdapter extends ResolveListAdapter { + private final List<ChooserTargetInfo> mServiceTargets = new ArrayList<>(); + + public ChooserListAdapter(Context context, Intent[] initialIntents, List<ResolveInfo> rList, + int launchedFromUid, boolean filterLastUsed) { + super(context, initialIntents, rList, launchedFromUid, filterLastUsed); + } + + @Override + public boolean showsExtendedInfo(TargetInfo info) { + // Reserve space to show extended info if any one of the items in the adapter has + // extended info. This keeps grid item sizes uniform. + return hasExtendedInfo(); + } + + @Override + public View createView(ViewGroup parent) { + return mInflater.inflate( + com.android.internal.R.layout.resolve_grid_item, parent, false); + } + + @Override + public void onListRebuilt() { + if (mServiceTargets != null) { + pruneServiceTargets(); + } + } + + @Override + public int getCount() { + int count = super.getCount(); + if (mServiceTargets != null) { + count += mServiceTargets.size(); + } + return count; + } + + @Override + public TargetInfo getItem(int position) { + int offset = 0; + if (mServiceTargets != null) { + final int serviceTargetCount = mServiceTargets.size(); + if (position < serviceTargetCount) { + return mServiceTargets.get(position); + } + offset += serviceTargetCount; + } + return super.getItem(position - offset); + } + + public void addServiceResults(DisplayResolveInfo origTarget, List<ChooserTarget> targets) { + if (DEBUG) Log.d(TAG, "addServiceResults " + origTarget + ", " + targets.size() + + " targets"); + for (int i = 0, N = targets.size(); i < N; i++) { + mServiceTargets.add(new ChooserTargetInfo(origTarget, targets.get(i))); + } + + // TODO: Maintain sort by ranking scores. + + notifyDataSetChanged(); + } + + private void pruneServiceTargets() { + if (DEBUG) Log.d(TAG, "pruneServiceTargets"); + for (int i = mServiceTargets.size() - 1; i >= 0; i--) { + final ChooserTargetInfo cti = mServiceTargets.get(i); + if (!hasResolvedTarget(cti.getResolveInfo())) { + if (DEBUG) Log.d(TAG, " => " + i + " " + cti); + mServiceTargets.remove(i); + } + } + } + } + + class ChooserTargetServiceConnection implements ServiceConnection { + private final DisplayResolveInfo mOriginalTarget; + + private final IChooserTargetResult mChooserTargetResult = new IChooserTargetResult.Stub() { + @Override + public void sendResult(List<ChooserTarget> targets) throws RemoteException { + final Message msg = Message.obtain(); + msg.what = CHOOSER_TARGET_SERVICE_RESULT; + msg.obj = new ServiceResultInfo(mOriginalTarget, targets, + ChooserTargetServiceConnection.this); + mTargetResultHandler.sendMessage(msg); + } + }; + + public ChooserTargetServiceConnection(DisplayResolveInfo dri) { + mOriginalTarget = dri; + } + + @Override + public void onServiceConnected(ComponentName name, IBinder service) { + if (DEBUG) Log.d(TAG, "onServiceConnected: " + name); + final IChooserTargetService icts = IChooserTargetService.Stub.asInterface(service); + try { + icts.getChooserTargets(mOriginalTarget.getResolvedComponentName(), + mOriginalTarget.getResolveInfo().filter, mChooserTargetResult); + } catch (RemoteException e) { + Log.e(TAG, "Querying ChooserTargetService " + name + " failed.", e); + unbindService(this); + mServiceConnections.remove(this); + } + } + + @Override + public void onServiceDisconnected(ComponentName name) { + if (DEBUG) Log.d(TAG, "onServiceDisconnected: " + name); + unbindService(this); + mServiceConnections.remove(this); + } + + @Override + public String toString() { + return mOriginalTarget.getResolveInfo().activityInfo.toString(); + } + } + + static class ServiceResultInfo { + public final DisplayResolveInfo originalTarget; + public final List<ChooserTarget> resultTargets; + public final ChooserTargetServiceConnection connection; + + public ServiceResultInfo(DisplayResolveInfo ot, List<ChooserTarget> rt, + ChooserTargetServiceConnection c) { + originalTarget = ot; + resultTargets = rt; + connection = c; + } + } } diff --git a/core/java/com/android/internal/app/ResolverActivity.java b/core/java/com/android/internal/app/ResolverActivity.java index 6b35f3f..3cd69a1 100644 --- a/core/java/com/android/internal/app/ResolverActivity.java +++ b/core/java/com/android/internal/app/ResolverActivity.java @@ -25,6 +25,7 @@ import android.provider.Settings; import android.text.TextUtils; import android.util.Slog; import android.widget.AbsListView; +import android.widget.GridView; import com.android.internal.R; import com.android.internal.content.PackageMonitor; @@ -91,15 +92,14 @@ public class ResolverActivity extends Activity implements AdapterView.OnItemClic private PackageManager mPm; private boolean mSafeForwardingMode; private boolean mAlwaysUseOption; - private boolean mShowExtended; + private AbsListView mAdapterView; private ListView mListView; + private GridView mGridView; private Button mAlwaysButton; private Button mOnceButton; private View mProfileView; private int mIconDpi; - private int mIconSize; - private int mMaxColumns; - private int mLastSelected = ListView.INVALID_POSITION; + private int mLastSelected = AbsListView.INVALID_POSITION; private boolean mResolvingHome = false; private int mProfileSwitchMessageId = -1; private Intent mIntent; @@ -192,7 +192,7 @@ public class ResolverActivity extends Activity implements AdapterView.OnItemClic } /** - * Compatibility version for other bundled services that use this ocerload without + * Compatibility version for other bundled services that use this overload without * a default title resource */ protected void onCreate(Bundle savedInstanceState, Intent intent, @@ -223,18 +223,14 @@ public class ResolverActivity extends Activity implements AdapterView.OnItemClic final long sinceTime = System.currentTimeMillis() - USAGE_STATS_PERIOD; mStats = mUsm.queryAndAggregateUsageStats(sinceTime, System.currentTimeMillis()); - mMaxColumns = getResources().getInteger(R.integer.config_maxResolverActivityColumns); - mPackageMonitor.register(this, getMainLooper(), false); mRegistered = true; final ActivityManager am = (ActivityManager) getSystemService(ACTIVITY_SERVICE); mIconDpi = am.getLauncherLargeIconDensity(); - mIconSize = am.getLauncherLargeIconSize(); mIntent = new Intent(intent); - mAdapter = new ResolveListAdapter(this, initialIntents, rList, - mLaunchedFromUid, alwaysUseOption); + mAdapter = createAdapter(this, initialIntents, rList, mLaunchedFromUid, alwaysUseOption); final int layoutId; final boolean useHeader; @@ -244,7 +240,7 @@ public class ResolverActivity extends Activity implements AdapterView.OnItemClic useHeader = true; } else { useHeader = false; - layoutId = R.layout.resolver_list; + layoutId = getLayoutResource(); } mAlwaysUseOption = alwaysUseOption; @@ -257,21 +253,30 @@ public class ResolverActivity extends Activity implements AdapterView.OnItemClic int count = mAdapter.mList.size(); if (count > 1 || (count == 1 && mAdapter.getOtherProfile() != null)) { setContentView(layoutId); - mListView = (ListView) findViewById(R.id.resolver_list); - mListView.setAdapter(mAdapter); - mListView.setOnItemClickListener(this); - mListView.setOnItemLongClickListener(new ItemLongClickListener()); + mAdapterView = (AbsListView) findViewById(R.id.resolver_list); + mAdapterView.setAdapter(mAdapter); + mAdapterView.setOnItemClickListener(this); + mAdapterView.setOnItemLongClickListener(new ItemLongClickListener()); + + // Initialize the different types of collection views we may have. Depending + // on which ones are initialized later we'll configure different properties. + if (mAdapterView instanceof ListView) { + mListView = (ListView) mAdapterView; + } + if (mAdapterView instanceof GridView) { + mGridView = (GridView) mAdapterView; + } if (alwaysUseOption) { - mListView.setChoiceMode(ListView.CHOICE_MODE_SINGLE); + mAdapterView.setChoiceMode(AbsListView.CHOICE_MODE_SINGLE); } - if (useHeader) { + if (useHeader && mListView != null) { mListView.addHeaderView(LayoutInflater.from(this).inflate( R.layout.resolver_different_item_header, mListView, false)); } } else if (count == 1) { - safelyStartActivity(mAdapter.intentForPosition(0, false)); + safelyStartActivity(mAdapter.targetInfoForPosition(0, false)); mPackageMonitor.unregister(); mRegistered = false; finish(); @@ -282,8 +287,8 @@ public class ResolverActivity extends Activity implements AdapterView.OnItemClic final TextView empty = (TextView) findViewById(R.id.empty); empty.setVisibility(View.VISIBLE); - mListView = (ListView) findViewById(R.id.resolver_list); - mListView.setVisibility(View.GONE); + mAdapterView = (AbsListView) findViewById(R.id.resolver_list); + mAdapterView.setVisibility(View.GONE); } // Prevent the Resolver window from becoming the top fullscreen window and thus from taking // control of the system bars. @@ -308,12 +313,30 @@ public class ResolverActivity extends Activity implements AdapterView.OnItemClic titleView.setText(title); } setTitle(title); + + // Try to initialize the title icon if we have a view for it and a title to match + final ImageView titleIcon = (ImageView) findViewById(R.id.title_icon); + if (titleIcon != null) { + final String referrerPackage = getReferrerPackageName(); + ApplicationInfo ai = null; + try { + if (!TextUtils.isEmpty(referrerPackage)) { + ai = mPm.getApplicationInfo(referrerPackage, 0); + } + } catch (NameNotFoundException e) { + Log.e(TAG, "Could not find referrer package " + referrerPackage); + } + + if (ai != null) { + titleIcon.setImageDrawable(ai.loadIcon(mPm)); + } + } } final ImageView iconView = (ImageView) findViewById(R.id.icon); final DisplayResolveInfo iconInfo = mAdapter.getFilteredItem(); if (iconView != null && iconInfo != null) { - new LoadIconIntoViewTask(iconView).execute(iconInfo); + new LoadIconIntoViewTask(iconInfo, iconView).execute(); } if (alwaysUseOption || mAdapter.hasFilteredItem()) { @@ -345,8 +368,7 @@ public class ResolverActivity extends Activity implements AdapterView.OnItemClic // Do not show the profile switch message anymore. mProfileSwitchMessageId = -1; - final Intent intent = intentForDisplayResolveInfo(dri); - onIntentSelected(dri.ri, intent, false); + onTargetSelected(dri, false); finish(); } }); @@ -354,17 +376,29 @@ public class ResolverActivity extends Activity implements AdapterView.OnItemClic } } + private String getReferrerPackageName() { + final Uri referrer = getReferrer(); + if (referrer != null && "android-app".equals(referrer.getScheme())) { + return referrer.getHost(); + } + return null; + } + + int getLayoutResource() { + return R.layout.resolver_list; + } + void bindProfileView() { final DisplayResolveInfo dri = mAdapter.getOtherProfile(); if (dri != null) { mProfileView.setVisibility(View.VISIBLE); final ImageView icon = (ImageView) mProfileView.findViewById(R.id.icon); final TextView text = (TextView) mProfileView.findViewById(R.id.text1); - if (dri.displayIcon == null) { - new LoadIconTask().execute(dri); + if (!dri.hasDisplayIcon()) { + new LoadIconIntoViewTask(dri, icon).execute(); } - icon.setImageDrawable(dri.displayIcon); - text.setText(dri.displayLabel); + icon.setImageDrawable(dri.getDisplayIcon()); + text.setText(dri.getDisplayLabel()); } else { mProfileView.setVisibility(View.GONE); } @@ -408,8 +442,9 @@ public class ResolverActivity extends Activity implements AdapterView.OnItemClic if (title == ActionTitle.DEFAULT && defaultTitleRes != 0) { return getString(defaultTitleRes); } else { - return named ? getString(title.namedTitleRes, mAdapter.getFilteredItem().displayLabel) : - getString(title.titleRes); + return named + ? getString(title.namedTitleRes, mAdapter.getFilteredItem().getDisplayLabel()) + : getString(title.titleRes); } } @@ -490,31 +525,33 @@ public class ResolverActivity extends Activity implements AdapterView.OnItemClic protected void onRestoreInstanceState(Bundle savedInstanceState) { super.onRestoreInstanceState(savedInstanceState); if (mAlwaysUseOption) { - final int checkedPos = mListView.getCheckedItemPosition(); + final int checkedPos = mAdapterView.getCheckedItemPosition(); final boolean hasValidSelection = checkedPos != ListView.INVALID_POSITION; mLastSelected = checkedPos; setAlwaysButtonEnabled(hasValidSelection, checkedPos, true); mOnceButton.setEnabled(hasValidSelection); if (hasValidSelection) { - mListView.setSelection(checkedPos); + mAdapterView.setSelection(checkedPos); } } } @Override public void onItemClick(AdapterView<?> parent, View view, int position, long id) { - position -= mListView.getHeaderViewsCount(); + if (mListView != null) { + position -= mListView.getHeaderViewsCount(); + } if (position < 0) { // Header views don't count. return; } - final int checkedPos = mListView.getCheckedItemPosition(); + final int checkedPos = mAdapterView.getCheckedItemPosition(); final boolean hasValidSelection = checkedPos != ListView.INVALID_POSITION; if (mAlwaysUseOption && (!hasValidSelection || mLastSelected != checkedPos)) { setAlwaysButtonEnabled(hasValidSelection, checkedPos, true); mOnceButton.setEnabled(hasValidSelection); if (hasValidSelection) { - mListView.smoothScrollToPosition(checkedPos); + mAdapterView.smoothScrollToPosition(checkedPos); } mLastSelected = checkedPos; } else { @@ -570,7 +607,7 @@ public class ResolverActivity extends Activity implements AdapterView.OnItemClic public void onButtonClick(View v) { final int id = v.getId(); startSelected(mAlwaysUseOption ? - mListView.getCheckedItemPosition() : mAdapter.getFilteredPosition(), + mAdapterView.getCheckedItemPosition() : mAdapter.getFilteredPosition(), id == R.id.button_always, mAlwaysUseOption); } @@ -588,8 +625,8 @@ public class ResolverActivity extends Activity implements AdapterView.OnItemClic return; } - Intent intent = mAdapter.intentForPosition(which, filtered); - onIntentSelected(ri, intent, always); + TargetInfo target = mAdapter.targetInfoForPosition(which, filtered); + onTargetSelected(target, always); finish(); } @@ -600,8 +637,12 @@ public class ResolverActivity extends Activity implements AdapterView.OnItemClic return defIntent; } - protected void onIntentSelected(ResolveInfo ri, Intent intent, boolean alwaysCheck) { - if ((mAlwaysUseOption || mAdapter.hasFilteredItem()) && mAdapter.mOrigResolveList != null) { + protected void onTargetSelected(TargetInfo target, boolean alwaysCheck) { + final ResolveInfo ri = target.getResolveInfo(); + final Intent intent = target != null ? target.getResolvedIntent() : null; + + if (intent != null && (mAlwaysUseOption || mAdapter.hasFilteredItem()) + && mAdapter.mOrigResolveList != null) { // Build a reasonable intent filter, based on what matched. IntentFilter filter = new IntentFilter(); String action = intent.getAction(); @@ -617,7 +658,7 @@ public class ResolverActivity extends Activity implements AdapterView.OnItemClic } filter.addCategory(Intent.CATEGORY_DEFAULT); - int cat = ri.match&IntentFilter.MATCH_CATEGORY_MASK; + int cat = ri.match & IntentFilter.MATCH_CATEGORY_MASK; Uri data = intent.getData(); if (cat == IntentFilter.MATCH_CATEGORY_TYPE) { String mimeType = intent.resolveType(this); @@ -726,25 +767,27 @@ public class ResolverActivity extends Activity implements AdapterView.OnItemClic } } - if (intent != null) { - safelyStartActivity(intent); + if (target != null) { + safelyStartActivity(target); } } - public void safelyStartActivity(Intent intent) { + void safelyStartActivity(TargetInfo cti) { // If needed, show that intent is forwarded // from managed profile to owner or other way around. if (mProfileSwitchMessageId != -1) { Toast.makeText(this, getString(mProfileSwitchMessageId), Toast.LENGTH_LONG).show(); } if (!mSafeForwardingMode) { - startActivity(intent); - onActivityStarted(intent); + if (cti.start(this, null)) { + onActivityStarted(cti); + } return; } try { - startActivityAsCaller(intent, null, UserHandle.USER_NULL); - onActivityStarted(intent); + if (cti.startAsCaller(this, null, UserHandle.USER_NULL)) { + onActivityStarted(cti); + } } catch (RuntimeException e) { String launchedFromPackage; try { @@ -759,51 +802,197 @@ public class ResolverActivity extends Activity implements AdapterView.OnItemClic } } - public void onActivityStarted(Intent intent) { + void onActivityStarted(TargetInfo cti) { // Do nothing } + boolean shouldGetActivityMetadata() { + return false; + } + void showAppDetails(ResolveInfo ri) { Intent in = new Intent().setAction(Settings.ACTION_APPLICATION_DETAILS_SETTINGS) .setData(Uri.fromParts("package", ri.activityInfo.packageName, null)) - .addFlags(Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET); + .addFlags(Intent.FLAG_ACTIVITY_NEW_DOCUMENT); startActivity(in); } - Intent intentForDisplayResolveInfo(DisplayResolveInfo dri) { - Intent intent = new Intent(dri.origIntent != null ? dri.origIntent : - getReplacementIntent(dri.ri.activityInfo, mIntent)); - intent.addFlags(Intent.FLAG_ACTIVITY_FORWARD_RESULT - |Intent.FLAG_ACTIVITY_PREVIOUS_IS_TOP); - ActivityInfo ai = dri.ri.activityInfo; - intent.setComponent(new ComponentName( - ai.applicationInfo.packageName, ai.name)); - return intent; + ResolveListAdapter createAdapter(Context context, Intent[] initialIntents, + List<ResolveInfo> rList, int launchedFromUid, boolean filterLastUsed) { + return new ResolveListAdapter(context, initialIntents, rList, launchedFromUid, + filterLastUsed); + } + + ResolveListAdapter getAdapter() { + return mAdapter; } - private final class DisplayResolveInfo { - ResolveInfo ri; - CharSequence displayLabel; - Drawable displayIcon; - CharSequence extendedInfo; - Intent origIntent; + final class DisplayResolveInfo implements TargetInfo { + private final ResolveInfo mResolveInfo; + private final CharSequence mDisplayLabel; + private Drawable mDisplayIcon; + private final CharSequence mExtendedInfo; + private final Intent mResolvedIntent; DisplayResolveInfo(ResolveInfo pri, CharSequence pLabel, CharSequence pInfo, Intent pOrigIntent) { - ri = pri; - displayLabel = pLabel; - extendedInfo = pInfo; - origIntent = pOrigIntent; + mResolveInfo = pri; + mDisplayLabel = pLabel; + mExtendedInfo = pInfo; + + final Intent intent = new Intent(pOrigIntent != null ? pOrigIntent : + getReplacementIntent(pri.activityInfo, mIntent)); + intent.addFlags(Intent.FLAG_ACTIVITY_FORWARD_RESULT + | Intent.FLAG_ACTIVITY_PREVIOUS_IS_TOP); + final ActivityInfo ai = mResolveInfo.activityInfo; + intent.setComponent(new ComponentName(ai.applicationInfo.packageName, ai.name)); + + mResolvedIntent = intent; + } + + public ResolveInfo getResolveInfo() { + return mResolveInfo; + } + + public CharSequence getDisplayLabel() { + return mDisplayLabel; + } + + public Drawable getDisplayIcon() { + return mDisplayIcon; + } + + public void setDisplayIcon(Drawable icon) { + mDisplayIcon = icon; + } + + public boolean hasDisplayIcon() { + return mDisplayIcon != null; + } + + public CharSequence getExtendedInfo() { + return mExtendedInfo; + } + + public Intent getResolvedIntent() { + return mResolvedIntent; + } + + @Override + public ComponentName getResolvedComponentName() { + return new ComponentName(mResolveInfo.activityInfo.packageName, + mResolveInfo.activityInfo.name); + } + + @Override + public boolean start(Activity activity, Bundle options) { + activity.startActivity(mResolvedIntent, options); + return true; } + + @Override + public boolean startAsCaller(Activity activity, Bundle options, int userId) { + activity.startActivityAsCaller(mResolvedIntent, options, userId); + return true; + } + + @Override + public boolean startAsUser(Activity activity, Bundle options, UserHandle user) { + activity.startActivityAsUser(mResolvedIntent, options, user); + return false; + } + } + + /** + * A single target as represented in the chooser. + */ + public interface TargetInfo { + /** + * Get the resolved intent that represents this target. Note that this may not be the + * intent that will be launched by calling one of the <code>start</code> methods provided; + * this is the intent that will be credited with the launch. + * + * @return the resolved intent for this target + */ + public Intent getResolvedIntent(); + + /** + * Get the resolved component name that represents this target. Note that this may not + * be the component that will be directly launched by calling one of the <code>start</code> + * methods provided; this is the component that will be credited with the launch. + * + * @return the resolved ComponentName for this target + */ + public ComponentName getResolvedComponentName(); + + /** + * Start the activity referenced by this target. + * + * @param activity calling Activity performing the launch + * @param options ActivityOptions bundle + * @return true if the start completed successfully + */ + public boolean start(Activity activity, Bundle options); + + /** + * Start the activity referenced by this target as if the ResolverActivity's caller + * was performing the start operation. + * + * @param activity calling Activity (actually) performing the launch + * @param options ActivityOptions bundle + * @param userId userId to start as or {@link UserHandle#USER_NULL} for activity's caller + * @return true if the start completed successfully + */ + public boolean startAsCaller(Activity activity, Bundle options, int userId); + + /** + * Start the activity referenced by this target as a given user. + * + * @param activity calling activity performing the launch + * @param options ActivityOptions bundle + * @param user handle for the user to start the activity as + * @return true if the start completed successfully + */ + public boolean startAsUser(Activity activity, Bundle options, UserHandle user); + + /** + * Return the ResolveInfo about how and why this target matched the original query + * for available targets. + * + * @return ResolveInfo representing this target's match + */ + public ResolveInfo getResolveInfo(); + + /** + * Return the human-readable text label for this target. + * + * @return user-visible target label + */ + public CharSequence getDisplayLabel(); + + /** + * Return any extended info for this target. This may be used to disambiguate + * otherwise identical targets. + * + * @return human-readable disambig string or null if none present + */ + public CharSequence getExtendedInfo(); + + /** + * @return The drawable that should be used to represent this target + */ + public Drawable getDisplayIcon(); } - private final class ResolveListAdapter extends BaseAdapter { + class ResolveListAdapter extends BaseAdapter { private final Intent[] mInitialIntents; private final List<ResolveInfo> mBaseResolveList; private ResolveInfo mLastChosen; private DisplayResolveInfo mOtherProfile; private final int mLaunchedFromUid; - private final LayoutInflater mInflater; + private boolean mHasExtendedInfo; + + protected final LayoutInflater mInflater; List<DisplayResolveInfo> mList; List<ResolveInfo> mOrigResolveList; @@ -817,7 +1006,7 @@ public class ResolverActivity extends Activity implements AdapterView.OnItemClic mBaseResolveList = rList; mLaunchedFromUid = launchedFromUid; mInflater = LayoutInflater.from(context); - mList = new ArrayList<DisplayResolveInfo>(); + mList = new ArrayList<>(); mFilterLastUsed = filterLastUsed; rebuildList(); } @@ -871,9 +1060,11 @@ public class ResolverActivity extends Activity implements AdapterView.OnItemClic if (mBaseResolveList != null) { currentResolveList = mOrigResolveList = mBaseResolveList; } else { - currentResolveList = mOrigResolveList = mPm.queryIntentActivities( - mIntent, PackageManager.MATCH_DEFAULT_ONLY - | (mFilterLastUsed ? PackageManager.GET_RESOLVED_FILTER : 0)); + currentResolveList = mOrigResolveList = mPm.queryIntentActivities(mIntent, + PackageManager.MATCH_DEFAULT_ONLY + | (mFilterLastUsed ? PackageManager.GET_RESOLVED_FILTER : 0) + | (shouldGetActivityMetadata() ? PackageManager.GET_META_DATA : 0) + ); // Filter out any activities that the launched uid does not // have permission for. We don't do this when we have an explicit // list of resolved activities, because that only happens when @@ -961,7 +1152,7 @@ public class ResolverActivity extends Activity implements AdapterView.OnItemClic r0 = currentResolveList.get(0); int start = 0; CharSequence r0Label = r0.loadLabel(mPm); - mShowExtended = false; + mHasExtendedInfo = false; for (int i = 1; i < N; i++) { if (r0Label == null) { r0Label = r0.activityInfo.packageName; @@ -989,6 +1180,12 @@ public class ResolverActivity extends Activity implements AdapterView.OnItemClic mLastChosenPosition = -1; mFilterLastUsed = false; } + + onListRebuilt(); + } + + public void onListRebuilt() { + // This space for rent } private void processGroup(List<ResolveInfo> rList, int start, int end, ResolveInfo ro, @@ -1000,7 +1197,7 @@ public class ResolverActivity extends Activity implements AdapterView.OnItemClic addResolveInfo(new DisplayResolveInfo(ro, roLabel, null, null)); updateLastChosenPosition(ro); } else { - mShowExtended = true; + mHasExtendedInfo = true; boolean usePkg = false; CharSequence startApp = ro.activityInfo.applicationInfo.loadLabel(mPm); if (startApp == null) { @@ -1049,7 +1246,7 @@ public class ResolverActivity extends Activity implements AdapterView.OnItemClic } private void addResolveInfo(DisplayResolveInfo dri) { - if (dri.ri.targetUserId != UserHandle.USER_CURRENT && mOtherProfile == null) { + if (dri.mResolveInfo.targetUserId != UserHandle.USER_CURRENT && mOtherProfile == null) { // So far we only support a single other profile at a time. // The first one we see gets special treatment. mOtherProfile = dri; @@ -1059,12 +1256,11 @@ public class ResolverActivity extends Activity implements AdapterView.OnItemClic } public ResolveInfo resolveInfoForPosition(int position, boolean filtered) { - return (filtered ? getItem(position) : mList.get(position)).ri; + return (filtered ? getItem(position) : mList.get(position)).getResolveInfo(); } - public Intent intentForPosition(int position, boolean filtered) { - DisplayResolveInfo dri = filtered ? getItem(position) : mList.get(position); - return intentForDisplayResolveInfo(dri); + public TargetInfo targetInfoForPosition(int position, boolean filtered) { + return filtered ? getItem(position) : mList.get(position); } public int getCount() { @@ -1075,7 +1271,7 @@ public class ResolverActivity extends Activity implements AdapterView.OnItemClic return result; } - public DisplayResolveInfo getItem(int position) { + public TargetInfo getItem(int position) { if (mFilterLastUsed && mLastChosenPosition >= 0 && position >= mLastChosenPosition) { position++; } @@ -1086,11 +1282,31 @@ public class ResolverActivity extends Activity implements AdapterView.OnItemClic return position; } - public View getView(int position, View convertView, ViewGroup parent) { + public boolean hasExtendedInfo() { + return mHasExtendedInfo; + } + + public boolean hasResolvedTarget(ResolveInfo info) { + for (int i = 0, N = mList.size(); i < N; i++) { + if (info.equals(mList.get(i).getResolveInfo())) { + return true; + } + } + return false; + } + + protected int getDisplayResolveInfoCount() { + return mList.size(); + } + + protected DisplayResolveInfo getDisplayResolveInfo(int index) { + return mList.get(index); + } + + public final View getView(int position, View convertView, ViewGroup parent) { View view = convertView; if (view == null) { - view = mInflater.inflate( - com.android.internal.R.layout.resolve_list_item, parent, false); + view = createView(parent); final ViewHolder holder = new ViewHolder(view); view.setTag(holder); @@ -1099,19 +1315,29 @@ public class ResolverActivity extends Activity implements AdapterView.OnItemClic return view; } - private final void bindView(View view, DisplayResolveInfo info) { + public View createView(ViewGroup parent) { + return mInflater.inflate( + com.android.internal.R.layout.resolve_list_item, parent, false); + } + + public boolean showsExtendedInfo(TargetInfo info) { + return !TextUtils.isEmpty(info.getExtendedInfo()); + } + + private final void bindView(View view, TargetInfo info) { final ViewHolder holder = (ViewHolder) view.getTag(); - holder.text.setText(info.displayLabel); - if (mShowExtended) { + holder.text.setText(info.getDisplayLabel()); + if (showsExtendedInfo(info)) { holder.text2.setVisibility(View.VISIBLE); - holder.text2.setText(info.extendedInfo); + holder.text2.setText(info.getExtendedInfo()); } else { holder.text2.setVisibility(View.GONE); } - if (info.displayIcon == null) { - new LoadIconTask().execute(info); + if (info instanceof DisplayResolveInfo + && !((DisplayResolveInfo) info).hasDisplayIcon()) { + new LoadAdapterIconTask((DisplayResolveInfo) info).execute(); } - holder.icon.setImageDrawable(info.displayIcon); + holder.icon.setImageDrawable(info.getDisplayIcon()); } } @@ -1131,7 +1357,9 @@ public class ResolverActivity extends Activity implements AdapterView.OnItemClic @Override public boolean onItemLongClick(AdapterView<?> parent, View view, int position, long id) { - position -= mListView.getHeaderViewsCount(); + if (mListView != null) { + position -= mListView.getHeaderViewsCount(); + } if (position < 0) { // Header views don't count. return false; @@ -1143,44 +1371,53 @@ public class ResolverActivity extends Activity implements AdapterView.OnItemClic } - class LoadIconTask extends AsyncTask<DisplayResolveInfo, Void, DisplayResolveInfo> { + abstract class LoadIconTask extends AsyncTask<Void, Void, Drawable> { + protected final DisplayResolveInfo mDisplayResolveInfo; + private final ResolveInfo mResolveInfo; + + public LoadIconTask(DisplayResolveInfo dri) { + mDisplayResolveInfo = dri; + mResolveInfo = dri.getResolveInfo(); + } + @Override - protected DisplayResolveInfo doInBackground(DisplayResolveInfo... params) { - final DisplayResolveInfo info = params[0]; - if (info.displayIcon == null) { - info.displayIcon = loadIconForResolveInfo(info.ri); - } - return info; + protected Drawable doInBackground(Void... params) { + return loadIconForResolveInfo(mResolveInfo); + } + + @Override + protected void onPostExecute(Drawable d) { + mDisplayResolveInfo.setDisplayIcon(d); + } + } + + class LoadAdapterIconTask extends LoadIconTask { + public LoadAdapterIconTask(DisplayResolveInfo dri) { + super(dri); } @Override - protected void onPostExecute(DisplayResolveInfo info) { - if (mProfileView != null && mAdapter.getOtherProfile() == info) { + protected void onPostExecute(Drawable d) { + super.onPostExecute(d); + if (mProfileView != null && mAdapter.getOtherProfile() == mDisplayResolveInfo) { bindProfileView(); } mAdapter.notifyDataSetChanged(); } } - class LoadIconIntoViewTask extends AsyncTask<DisplayResolveInfo, Void, DisplayResolveInfo> { - final ImageView mTargetView; + class LoadIconIntoViewTask extends LoadIconTask { + private final ImageView mTargetView; - public LoadIconIntoViewTask(ImageView target) { + public LoadIconIntoViewTask(DisplayResolveInfo dri, ImageView target) { + super(dri); mTargetView = target; } @Override - protected DisplayResolveInfo doInBackground(DisplayResolveInfo... params) { - final DisplayResolveInfo info = params[0]; - if (info.displayIcon == null) { - info.displayIcon = loadIconForResolveInfo(info.ri); - } - return info; - } - - @Override - protected void onPostExecute(DisplayResolveInfo info) { - mTargetView.setImageDrawable(info.displayIcon); + protected void onPostExecute(Drawable d) { + super.onPostExecute(d); + mTargetView.setImageDrawable(d); } } diff --git a/core/java/com/android/internal/backup/LocalTransport.java b/core/java/com/android/internal/backup/LocalTransport.java index e32a3a2..22d35f2 100644 --- a/core/java/com/android/internal/backup/LocalTransport.java +++ b/core/java/com/android/internal/backup/LocalTransport.java @@ -45,8 +45,7 @@ import java.io.FileOutputStream; import java.io.IOException; import java.util.ArrayList; import java.util.Collections; -import java.util.HashSet; -import static android.system.OsConstants.*; +import static android.system.OsConstants.SEEK_CUR; /** * Backup transport for stashing stuff into a known location on disk, and @@ -284,8 +283,10 @@ public class LocalTransport extends BackupTransport { private int tearDownFullBackup() { if (mSocket != null) { try { - mFullBackupOutputStream.flush(); - mFullBackupOutputStream.close(); + if (mFullBackupOutputStream != null) { + mFullBackupOutputStream.flush(); + mFullBackupOutputStream.close(); + } mSocketInputStream = null; mFullTargetPackage = null; mSocket.close(); @@ -296,6 +297,7 @@ public class LocalTransport extends BackupTransport { return TRANSPORT_ERROR; } finally { mSocket = null; + mFullBackupOutputStream = null; } } return TRANSPORT_OK; @@ -311,6 +313,18 @@ public class LocalTransport extends BackupTransport { } @Override + public int checkFullBackupSize(long size) { + // Decline zero-size "backups" + final int result = (size > 0) ? TRANSPORT_OK : TRANSPORT_PACKAGE_REJECTED; + if (result != TRANSPORT_OK) { + if (DEBUG) { + Log.v(TAG, "Declining backup of size " + size); + } + } + return result; + } + + @Override public int performFullBackup(PackageInfo targetPackage, ParcelFileDescriptor socket) { if (mSocket != null) { Log.e(TAG, "Attempt to initiate full backup while one is in progress"); @@ -333,22 +347,14 @@ public class LocalTransport extends BackupTransport { } mFullTargetPackage = targetPackage.packageName; - FileOutputStream tarstream; - try { - File tarball = tarballFile(mFullTargetPackage); - tarstream = new FileOutputStream(tarball); - } catch (FileNotFoundException e) { - return TRANSPORT_ERROR; - } - mFullBackupOutputStream = new BufferedOutputStream(tarstream); mFullBackupBuffer = new byte[4096]; return TRANSPORT_OK; } @Override - public int sendBackupData(int numBytes) { - if (mFullBackupBuffer == null) { + public int sendBackupData(final int numBytes) { + if (mSocket == null) { Log.w(TAG, "Attempted sendBackupData before performFullBackup"); return TRANSPORT_ERROR; } @@ -356,16 +362,29 @@ public class LocalTransport extends BackupTransport { if (numBytes > mFullBackupBuffer.length) { mFullBackupBuffer = new byte[numBytes]; } - while (numBytes > 0) { + + if (mFullBackupOutputStream == null) { + FileOutputStream tarstream; + try { + File tarball = tarballFile(mFullTargetPackage); + tarstream = new FileOutputStream(tarball); + } catch (FileNotFoundException e) { + return TRANSPORT_ERROR; + } + mFullBackupOutputStream = new BufferedOutputStream(tarstream); + } + + int bytesLeft = numBytes; + while (bytesLeft > 0) { try { - int nRead = mSocketInputStream.read(mFullBackupBuffer, 0, numBytes); + int nRead = mSocketInputStream.read(mFullBackupBuffer, 0, bytesLeft); if (nRead < 0) { // Something went wrong if we expect data but saw EOD Log.w(TAG, "Unexpected EOD; failing backup"); return TRANSPORT_ERROR; } mFullBackupOutputStream.write(mFullBackupBuffer, 0, nRead); - numBytes -= nRead; + bytesLeft -= nRead; } catch (IOException e) { Log.e(TAG, "Error handling backup data for " + mFullTargetPackage); return TRANSPORT_ERROR; diff --git a/core/java/com/android/internal/logging/MetricsLogger.java b/core/java/com/android/internal/logging/MetricsLogger.java index 6be6389..24b5d0d 100644 --- a/core/java/com/android/internal/logging/MetricsLogger.java +++ b/core/java/com/android/internal/logging/MetricsLogger.java @@ -26,9 +26,10 @@ import android.os.Build; */ public class MetricsLogger implements MetricsConstants { // These constants are temporary, they should migrate to MetricsConstants. - // next value is 145; + // next value is 146; public static final int NOTIFICATION_ZEN_MODE_SCHEDULE_RULE = 144; + public static final int NOTIFICATION_ZEN_MODE_EXTERNAL_RULE = 145; public static void visible(Context context, int category) throws IllegalArgumentException { if (Build.IS_DEBUGGABLE && category == VIEW_UNKNOWN) { diff --git a/core/java/com/android/internal/os/PowerProfile.java b/core/java/com/android/internal/os/PowerProfile.java index 7e6706c..1efa565 100644 --- a/core/java/com/android/internal/os/PowerProfile.java +++ b/core/java/com/android/internal/os/PowerProfile.java @@ -316,7 +316,7 @@ public class PowerProfile { final Double[] values = (Double[]) data; if (values.length > level && level >= 0) { return values[level]; - } else if (level < 0) { + } else if (level < 0 || values.length == 0) { return 0; } else { return values[values.length - 1]; diff --git a/core/java/com/android/internal/transition/ActionBarTransition.java b/core/java/com/android/internal/transition/ActionBarTransition.java deleted file mode 100644 index c1065e7..0000000 --- a/core/java/com/android/internal/transition/ActionBarTransition.java +++ /dev/null @@ -1,58 +0,0 @@ -/* - * Copyright (C) 2013 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - - -package com.android.internal.transition; - -import android.transition.ChangeBounds; -import android.transition.Fade; -import android.transition.ChangeText; -import android.transition.Transition; -import android.transition.TransitionManager; -import android.transition.TransitionSet; -import android.view.ViewGroup; - -public class ActionBarTransition { - - private static boolean TRANSITIONS_ENABLED = false; - - private static final int TRANSITION_DURATION = 120; // ms - - private static final Transition sTransition; - - static { - if (TRANSITIONS_ENABLED) { - final ChangeText tc = new ChangeText(); - tc.setChangeBehavior(ChangeText.CHANGE_BEHAVIOR_OUT_IN); - final TransitionSet inner = new TransitionSet(); - inner.addTransition(tc).addTransition(new ChangeBounds()); - final TransitionSet tg = new TransitionSet(); - tg.addTransition(new Fade(Fade.OUT)).addTransition(inner). - addTransition(new Fade(Fade.IN)); - tg.setOrdering(TransitionSet.ORDERING_SEQUENTIAL); - tg.setDuration(TRANSITION_DURATION); - sTransition = tg; - } else { - sTransition = null; - } - } - - public static void beginDelayedTransition(ViewGroup sceneRoot) { - if (TRANSITIONS_ENABLED) { - TransitionManager.beginDelayedTransition(sceneRoot, sTransition); - } - } -} diff --git a/core/java/com/android/internal/widget/ActionBarView.java b/core/java/com/android/internal/widget/ActionBarView.java index 88436f8..6b781c3 100644 --- a/core/java/com/android/internal/widget/ActionBarView.java +++ b/core/java/com/android/internal/widget/ActionBarView.java @@ -49,7 +49,6 @@ import android.widget.Spinner; import android.widget.SpinnerAdapter; import android.widget.TextView; import com.android.internal.R; -import com.android.internal.transition.ActionBarTransition; import com.android.internal.view.menu.ActionMenuItem; import com.android.internal.view.menu.MenuBuilder; import com.android.internal.view.menu.MenuItemImpl; @@ -459,9 +458,6 @@ public class ActionBarView extends AbsActionBarView implements DecorToolbar { public void setCustomView(View view) { final boolean showCustom = (mDisplayOptions & ActionBar.DISPLAY_SHOW_CUSTOM) != 0; - if (showCustom) { - ActionBarTransition.beginDelayedTransition(this); - } if (mCustomNavView != null && showCustom) { removeView(mCustomNavView); } @@ -499,7 +495,6 @@ public class ActionBarView extends AbsActionBarView implements DecorToolbar { } private void setTitleImpl(CharSequence title) { - ActionBarTransition.beginDelayedTransition(this); mTitle = title; if (mTitleView != null) { mTitleView.setText(title); @@ -519,7 +514,6 @@ public class ActionBarView extends AbsActionBarView implements DecorToolbar { } public void setSubtitle(CharSequence subtitle) { - ActionBarTransition.beginDelayedTransition(this); mSubtitle = subtitle; if (mSubtitleView != null) { mSubtitleView.setText(subtitle); @@ -604,7 +598,6 @@ public class ActionBarView extends AbsActionBarView implements DecorToolbar { mDisplayOptions = options; if ((flagsChanged & DISPLAY_RELAYOUT_MASK) != 0) { - ActionBarTransition.beginDelayedTransition(this); if ((flagsChanged & ActionBar.DISPLAY_HOME_AS_UP) != 0) { final boolean setUp = (options & ActionBar.DISPLAY_HOME_AS_UP) != 0; @@ -706,7 +699,6 @@ public class ActionBarView extends AbsActionBarView implements DecorToolbar { public void setNavigationMode(int mode) { final int oldMode = mNavigationMode; if (mode != oldMode) { - ActionBarTransition.beginDelayedTransition(this); switch (oldMode) { case ActionBar.NAVIGATION_MODE_LIST: if (mListNavLayout != null) { @@ -836,7 +828,6 @@ public class ActionBarView extends AbsActionBarView implements DecorToolbar { } } - ActionBarTransition.beginDelayedTransition(this); mUpGoerFive.addView(mTitleLayout); if (mExpandedActionView != null || (TextUtils.isEmpty(mTitle) && TextUtils.isEmpty(mSubtitle))) { @@ -1659,7 +1650,6 @@ public class ActionBarView extends AbsActionBarView implements DecorToolbar { @Override public boolean expandItemActionView(MenuBuilder menu, MenuItemImpl item) { - ActionBarTransition.beginDelayedTransition(ActionBarView.this); mExpandedActionView = item.getActionView(); mExpandedHomeLayout.setIcon(mIcon.getConstantState().newDrawable(getResources())); @@ -1688,7 +1678,6 @@ public class ActionBarView extends AbsActionBarView implements DecorToolbar { @Override public boolean collapseItemActionView(MenuBuilder menu, MenuItemImpl item) { - ActionBarTransition.beginDelayedTransition(ActionBarView.this); // Do this before detaching the actionview from the hierarchy, in case // it needs to dismiss the soft keyboard, etc. diff --git a/core/java/com/android/internal/widget/DialogViewAnimator.java b/core/java/com/android/internal/widget/DialogViewAnimator.java new file mode 100644 index 0000000..bdfc1af --- /dev/null +++ b/core/java/com/android/internal/widget/DialogViewAnimator.java @@ -0,0 +1,141 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.internal.widget; + +import android.content.Context; +import android.graphics.drawable.Drawable; +import android.util.AttributeSet; +import android.view.View; +import android.widget.ViewAnimator; + +import java.util.ArrayList; + +/** + * ViewAnimator with a more reasonable handling of MATCH_PARENT. + */ +public class DialogViewAnimator extends ViewAnimator { + private final ArrayList<View> mMatchParentChildren = new ArrayList<>(1); + + public DialogViewAnimator(Context context) { + super(context); + } + + public DialogViewAnimator(Context context, AttributeSet attrs) { + super(context, attrs); + } + + @Override + protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + final boolean measureMatchParentChildren = + MeasureSpec.getMode(widthMeasureSpec) != MeasureSpec.EXACTLY || + MeasureSpec.getMode(heightMeasureSpec) != MeasureSpec.EXACTLY; + + int maxHeight = 0; + int maxWidth = 0; + int childState = 0; + + // First measure all children and record maximum dimensions where the + // spec isn't MATCH_PARENT. + final int count = getChildCount(); + for (int i = 0; i < count; i++) { + final View child = getChildAt(i); + if (getMeasureAllChildren() || child.getVisibility() != GONE) { + final LayoutParams lp = (LayoutParams) child.getLayoutParams(); + final boolean matchWidth = lp.width == LayoutParams.MATCH_PARENT; + final boolean matchHeight = lp.height == LayoutParams.MATCH_PARENT; + if (measureMatchParentChildren && (matchWidth || matchHeight)) { + mMatchParentChildren.add(child); + } + + measureChildWithMargins(child, widthMeasureSpec, 0, heightMeasureSpec, 0); + + // Measured dimensions only count against the maximum + // dimensions if they're not MATCH_PARENT. + int state = 0; + + if (measureMatchParentChildren && !matchWidth) { + maxWidth = Math.max(maxWidth, child.getMeasuredWidth() + + lp.leftMargin + lp.rightMargin); + state |= child.getMeasuredWidthAndState() & MEASURED_STATE_MASK; + } + + if (measureMatchParentChildren && !matchHeight) { + maxHeight = Math.max(maxHeight, child.getMeasuredHeight() + + lp.topMargin + lp.bottomMargin); + state |= (child.getMeasuredHeightAndState() >> MEASURED_HEIGHT_STATE_SHIFT) + & (MEASURED_STATE_MASK >> MEASURED_HEIGHT_STATE_SHIFT); + } + + childState = combineMeasuredStates(childState, state); + } + } + + // Account for padding too. + maxWidth += getPaddingLeft() + getPaddingRight(); + maxHeight += getPaddingTop() + getPaddingBottom(); + + // Check against our minimum height and width. + maxHeight = Math.max(maxHeight, getSuggestedMinimumHeight()); + maxWidth = Math.max(maxWidth, getSuggestedMinimumWidth()); + + // Check against our foreground's minimum height and width. + final Drawable drawable = getForeground(); + if (drawable != null) { + maxHeight = Math.max(maxHeight, drawable.getMinimumHeight()); + maxWidth = Math.max(maxWidth, drawable.getMinimumWidth()); + } + + setMeasuredDimension(resolveSizeAndState(maxWidth, widthMeasureSpec, childState), + resolveSizeAndState(maxHeight, heightMeasureSpec, + childState << MEASURED_HEIGHT_STATE_SHIFT)); + + // Measure remaining MATCH_PARENT children again using real dimensions. + final int matchCount = mMatchParentChildren.size(); + for (int i = 0; i < matchCount; i++) { + final View child = mMatchParentChildren.get(i); + final MarginLayoutParams lp = (MarginLayoutParams) child.getLayoutParams(); + + final int childWidthMeasureSpec; + if (lp.width == LayoutParams.MATCH_PARENT) { + childWidthMeasureSpec = MeasureSpec.makeMeasureSpec( + getMeasuredWidth() - getPaddingLeft() - getPaddingRight() + - lp.leftMargin - lp.rightMargin, + MeasureSpec.EXACTLY); + } else { + childWidthMeasureSpec = getChildMeasureSpec(widthMeasureSpec, + getPaddingLeft() + getPaddingRight() + lp.leftMargin + lp.rightMargin, + lp.width); + } + + final int childHeightMeasureSpec; + if (lp.height == LayoutParams.MATCH_PARENT) { + childHeightMeasureSpec = MeasureSpec.makeMeasureSpec( + getMeasuredHeight() - getPaddingTop() - getPaddingBottom() + - lp.topMargin - lp.bottomMargin, + MeasureSpec.EXACTLY); + } else { + childHeightMeasureSpec = getChildMeasureSpec(heightMeasureSpec, + getPaddingTop() + getPaddingBottom() + lp.topMargin + lp.bottomMargin, + lp.height); + } + + child.measure(childWidthMeasureSpec, childHeightMeasureSpec); + } + + mMatchParentChildren.clear(); + } +} diff --git a/core/java/com/android/internal/widget/ViewPager.java b/core/java/com/android/internal/widget/ViewPager.java index 8d66191..5c08daf 100644 --- a/core/java/com/android/internal/widget/ViewPager.java +++ b/core/java/com/android/internal/widget/ViewPager.java @@ -889,7 +889,7 @@ public class ViewPager extends ViewGroup { } } - void populate() { + public void populate() { populate(mCurItem); } |
