diff options
Diffstat (limited to 'core/java')
127 files changed, 3124 insertions, 1058 deletions
diff --git a/core/java/android/accessibilityservice/AccessibilityService.java b/core/java/android/accessibilityservice/AccessibilityService.java index 1e1b33f..a9eaf29 100644 --- a/core/java/android/accessibilityservice/AccessibilityService.java +++ b/core/java/android/accessibilityservice/AccessibilityService.java @@ -24,11 +24,9 @@ import android.os.Looper; import android.os.Message; import android.os.RemoteException; import android.util.Log; -import android.view.Display; import android.view.KeyEvent; -import android.view.View; -import android.view.ViewGroup; import android.view.WindowManager; +import android.view.WindowManagerGlobal; import android.view.accessibility.AccessibilityEvent; import android.view.accessibility.AccessibilityInteractionClient; import android.view.accessibility.AccessibilityNodeInfo; @@ -620,18 +618,6 @@ public abstract class AccessibilityService extends Service { } } - @Override - public Object getSystemService(String name) { - if (Context.WINDOW_SERVICE.equals(name)) { - if (mWindowManager == null) { - WindowManager wrapped = (WindowManager) super.getSystemService(name); - mWindowManager = new LocalWindowManager(wrapped); - } - return mWindowManager; - } - return super.getSystemService(name); - } - /** * Implement to return the implementation of the internal accessibility * service interface. @@ -658,6 +644,9 @@ public abstract class AccessibilityService extends Service { public void init(int connectionId, IBinder windowToken) { mConnectionId = connectionId; mWindowToken = windowToken; + + // Let the window manager know about our shiny new token. + WindowManagerGlobal.getInstance().setDefaultToken(mWindowToken); } @Override @@ -812,53 +801,4 @@ public abstract class AccessibilityService extends Service { } } } - - private class LocalWindowManager implements WindowManager { - private final WindowManager mImpl; - - private LocalWindowManager(WindowManager impl) { - mImpl = impl; - } - - @Override - public Display getDefaultDisplay() { - return mImpl.getDefaultDisplay(); - } - - @Override - public void addView(View view, ViewGroup.LayoutParams params) { - if (!(params instanceof WindowManager.LayoutParams)) { - throw new IllegalArgumentException("Params must be WindowManager.LayoutParams"); - } - WindowManager.LayoutParams windowParams = (WindowManager.LayoutParams) params; - if (windowParams.type == LayoutParams.TYPE_ACCESSIBILITY_OVERLAY - && windowParams.token == null) { - windowParams.token = mWindowToken; - } - mImpl.addView(view, params); - } - - @Override - public void updateViewLayout(View view, ViewGroup.LayoutParams params) { - if (!(params instanceof WindowManager.LayoutParams)) { - throw new IllegalArgumentException("Params must be WindowManager.LayoutParams"); - } - WindowManager.LayoutParams windowParams = (WindowManager.LayoutParams) params; - if (windowParams.type == LayoutParams.TYPE_ACCESSIBILITY_OVERLAY - && windowParams.token == null) { - windowParams.token = mWindowToken; - } - mImpl.updateViewLayout(view, params); - } - - @Override - public void removeViewImmediate(View view) { - mImpl.removeViewImmediate(view); - } - - @Override - public void removeView(View view) { - mImpl.removeView(view); - } - } } diff --git a/core/java/android/animation/ObjectAnimator.java b/core/java/android/animation/ObjectAnimator.java index 500634c..59daaab 100644 --- a/core/java/android/animation/ObjectAnimator.java +++ b/core/java/android/animation/ObjectAnimator.java @@ -885,7 +885,8 @@ public final class ObjectAnimator extends ValueAnimator { } /** - * Sets the target object whose property will be animated by this animation + * Sets the target object whose property will be animated by this animation. If the + * animator has been started, it will be canceled. * * @param target The object being animated */ @@ -893,6 +894,9 @@ public final class ObjectAnimator extends ValueAnimator { public void setTarget(@Nullable Object target) { final Object oldTarget = getTarget(); if (oldTarget != target) { + if (isStarted()) { + cancel(); + } mTarget = target == null ? null : new WeakReference<Object>(target); // New target should cause re-initialization prior to starting mInitialized = false; diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java index 4b705dd..148527f 100644 --- a/core/java/android/app/Activity.java +++ b/core/java/android/app/Activity.java @@ -693,6 +693,7 @@ public class Activity extends ContextThemeWrapper /*package*/ String mEmbeddedID; private Application mApplication; /*package*/ Intent mIntent; + /*package*/ String mReferrer; private ComponentName mComponent; /*package*/ ActivityInfo mActivityInfo; /*package*/ ActivityThread mMainThread; @@ -4448,6 +4449,39 @@ public class Activity extends ContextThemeWrapper } /** + * Return information about who launched this activity. If the launching Intent + * contains an {@link android.content.Intent#EXTRA_REFERRER Intent.EXTRA_REFERRER}, + * that will be returned as-is; otherwise, if known, an + * {@link Intent#URI_ANDROID_APP_SCHEME android-app:} referrer URI containing the + * package name that started the Intent will be returned. This may return null if no + * referrer can be identified -- it is neither explicitly specified, nor is it known which + * application package was involved. + * + * <p>If called while inside the handling of {@link #onNewIntent}, this function will + * return the referrer that submitted that new intent to the activity. Otherwise, it + * always returns the referrer of the original Intent.</p> + * + * <p>Note that this is <em>not</em> a security feature -- you can not trust the + * referrer information, applications can spoof it.</p> + */ + @Nullable + public Uri getReferrer() { + Intent intent = getIntent(); + Uri referrer = intent.getParcelableExtra(Intent.EXTRA_REFERRER); + if (referrer != null) { + return referrer; + } + String referrerName = intent.getStringExtra(Intent.EXTRA_REFERRER_NAME); + if (referrerName != null) { + return Uri.parse(referrerName); + } + if (mReferrer != null) { + return new Uri.Builder().scheme("android-app").authority(mReferrer).build(); + } + return null; + } + + /** * Return the name of the package that invoked this activity. This is who * the data in {@link #setResult setResult()} will be sent to. You can * use this information to validate that the recipient is allowed to @@ -5868,7 +5902,7 @@ public class Activity extends ContextThemeWrapper Application application, Intent intent, ActivityInfo info, CharSequence title, Activity parent, String id, NonConfigurationInstances lastNonConfigurationInstances, - Configuration config, IVoiceInteractor voiceInteractor) { + Configuration config, String referrer, IVoiceInteractor voiceInteractor) { attachBaseContext(context); mFragments.attachActivity(this, mContainer, null); @@ -5891,6 +5925,7 @@ public class Activity extends ContextThemeWrapper mIdent = ident; mApplication = application; mIntent = intent; + mReferrer = referrer; mComponent = intent.getComponent(); mActivityInfo = info; mTitle = title; diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java index 37e8aa4..7a636db 100644 --- a/core/java/android/app/ActivityManager.java +++ b/core/java/android/app/ActivityManager.java @@ -213,6 +213,13 @@ public class ActivityManager { public static final int BROADCAST_STICKY_CANT_HAVE_PERMISSION = -1; /** + * Result for IActivityManager.broadcastIntent: trying to send a broadcast + * to a stopped user. Fail. + * @hide + */ + public static final int BROADCAST_FAILED_USER_STOPPED = -2; + + /** * Type for IActivityManaqer.getIntentSender: this PendingIntent is * for a sendBroadcast operation. * @hide @@ -1243,26 +1250,16 @@ public class ActivityManager { } /** - * If set, the process of the root activity of the task will be killed - * as part of removing the task. - * @hide - */ - public static final int REMOVE_TASK_KILL_PROCESS = 0x0001; - - /** * Completely remove the given task. * * @param taskId Identifier of the task to be removed. - * @param flags Additional operational flags. May be 0 or - * {@link #REMOVE_TASK_KILL_PROCESS}. * @return Returns true if the given task was found and removed. * * @hide */ - public boolean removeTask(int taskId, int flags) - throws SecurityException { + public boolean removeTask(int taskId) throws SecurityException { try { - return ActivityManagerNative.getDefault().removeTask(taskId, flags); + return ActivityManagerNative.getDefault().removeTask(taskId); } catch (RemoteException e) { // System dead, we will be dead too soon! return false; diff --git a/core/java/android/app/ActivityManagerNative.java b/core/java/android/app/ActivityManagerNative.java index 4e2ff0b..c3028b7 100644 --- a/core/java/android/app/ActivityManagerNative.java +++ b/core/java/android/app/ActivityManagerNative.java @@ -1884,8 +1884,7 @@ public abstract class ActivityManagerNative extends Binder implements IActivityM { data.enforceInterface(IActivityManager.descriptor); int taskId = data.readInt(); - int fl = data.readInt(); - boolean result = removeTask(taskId, fl); + boolean result = removeTask(taskId); reply.writeNoException(); reply.writeInt(result ? 1 : 0); return true; @@ -2280,6 +2279,20 @@ public abstract class ActivityManagerNative extends Binder implements IActivityM return true; } + case START_IN_PLACE_ANIMATION_TRANSACTION: { + data.enforceInterface(IActivityManager.descriptor); + final Bundle bundle; + if (data.readInt() == 0) { + bundle = null; + } else { + bundle = data.readBundle(); + } + final ActivityOptions options = bundle == null ? null : new ActivityOptions(bundle); + startInPlaceAnimationOnFrontMostApplication(options); + reply.writeNoException(); + return true; + } + case REQUEST_VISIBLE_BEHIND_TRANSACTION: { data.enforceInterface(IActivityManager.descriptor); IBinder token = data.readStrongBinder(); @@ -4778,12 +4791,11 @@ class ActivityManagerProxy implements IActivityManager return result; } - public boolean removeTask(int taskId, int flags) throws RemoteException { + public boolean removeTask(int taskId) throws RemoteException { Parcel data = Parcel.obtain(); Parcel reply = Parcel.obtain(); data.writeInterfaceToken(IActivityManager.descriptor); data.writeInt(taskId); - data.writeInt(flags); mRemote.transact(REMOVE_TASK_TRANSACTION, data, reply, 0); reply.readException(); boolean result = reply.readInt() != 0; @@ -5300,6 +5312,24 @@ class ActivityManagerProxy implements IActivityManager } @Override + public void startInPlaceAnimationOnFrontMostApplication(ActivityOptions options) + throws RemoteException { + Parcel data = Parcel.obtain(); + Parcel reply = Parcel.obtain(); + data.writeInterfaceToken(IActivityManager.descriptor); + if (options == null) { + data.writeInt(0); + } else { + data.writeInt(1); + data.writeBundle(options.toBundle()); + } + mRemote.transact(START_IN_PLACE_ANIMATION_TRANSACTION, data, reply, IBinder.FLAG_ONEWAY); + reply.readException(); + data.recycle(); + reply.recycle(); + } + + @Override public boolean requestVisibleBehind(IBinder token, boolean visible) throws RemoteException { Parcel data = Parcel.obtain(); Parcel reply = Parcel.obtain(); diff --git a/core/java/android/app/ActivityOptions.java b/core/java/android/app/ActivityOptions.java index cd6a4f5..3d390bf 100644 --- a/core/java/android/app/ActivityOptions.java +++ b/core/java/android/app/ActivityOptions.java @@ -63,6 +63,12 @@ public class ActivityOptions { public static final String KEY_ANIM_EXIT_RES_ID = "android:animExitRes"; /** + * Custom in-place animation resource ID. + * @hide + */ + public static final String KEY_ANIM_IN_PLACE_RES_ID = "android:animInPlaceRes"; + + /** * Bitmap for thumbnail animation. * @hide */ @@ -132,11 +138,14 @@ public class ActivityOptions { public static final int ANIM_THUMBNAIL_ASPECT_SCALE_UP = 8; /** @hide */ public static final int ANIM_THUMBNAIL_ASPECT_SCALE_DOWN = 9; + /** @hide */ + public static final int ANIM_CUSTOM_IN_PLACE = 10; private String mPackageName; private int mAnimationType = ANIM_NONE; private int mCustomEnterResId; private int mCustomExitResId; + private int mCustomInPlaceResId; private Bitmap mThumbnail; private int mStartX; private int mStartY; @@ -198,6 +207,30 @@ public class ActivityOptions { return opts; } + /** + * Creates an ActivityOptions specifying a custom animation to run in place on an existing + * activity. + * + * @param context Who is defining this. This is the application that the + * animation resources will be loaded from. + * @param animId A resource ID of the animation resource to use for + * the incoming activity. + * @return Returns a new ActivityOptions object that you can use to + * supply these options as the options Bundle when running an in-place animation. + * @hide + */ + public static ActivityOptions makeCustomInPlaceAnimation(Context context, int animId) { + if (animId == 0) { + throw new RuntimeException("You must specify a valid animation."); + } + + ActivityOptions opts = new ActivityOptions(); + opts.mPackageName = context.getPackageName(); + opts.mAnimationType = ANIM_CUSTOM_IN_PLACE; + opts.mCustomInPlaceResId = animId; + return opts; + } + private void setOnAnimationStartedListener(Handler handler, OnAnimationStartedListener listener) { if (listener != null) { @@ -540,6 +573,10 @@ public class ActivityOptions { opts.getBinder(KEY_ANIM_START_LISTENER)); break; + case ANIM_CUSTOM_IN_PLACE: + mCustomInPlaceResId = opts.getInt(KEY_ANIM_IN_PLACE_RES_ID, 0); + break; + case ANIM_SCALE_UP: mStartX = opts.getInt(KEY_ANIM_START_X, 0); mStartY = opts.getInt(KEY_ANIM_START_Y, 0); @@ -592,6 +629,11 @@ public class ActivityOptions { } /** @hide */ + public int getCustomInPlaceResId() { + return mCustomInPlaceResId; + } + + /** @hide */ public Bitmap getThumbnail() { return mThumbnail; } @@ -689,6 +731,9 @@ public class ActivityOptions { } mAnimationStartedListener = otherOptions.mAnimationStartedListener; break; + case ANIM_CUSTOM_IN_PLACE: + mCustomInPlaceResId = otherOptions.mCustomInPlaceResId; + break; case ANIM_SCALE_UP: mStartX = otherOptions.mStartX; mStartY = otherOptions.mStartY; @@ -756,6 +801,9 @@ public class ActivityOptions { b.putBinder(KEY_ANIM_START_LISTENER, mAnimationStartedListener != null ? mAnimationStartedListener.asBinder() : null); break; + case ANIM_CUSTOM_IN_PLACE: + b.putInt(KEY_ANIM_IN_PLACE_RES_ID, mCustomInPlaceResId); + break; case ANIM_SCALE_UP: b.putInt(KEY_ANIM_START_X, mStartX); b.putInt(KEY_ANIM_START_Y, mStartY); diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java index fa15ad7..5f21d75 100644 --- a/core/java/android/app/ActivityThread.java +++ b/core/java/android/app/ActivityThread.java @@ -94,6 +94,7 @@ import android.renderscript.RenderScript; import android.security.AndroidKeyStoreProvider; import com.android.internal.app.IVoiceInteractor; +import com.android.internal.content.ReferrerIntent; import com.android.internal.os.BinderInternal; import com.android.internal.os.RuntimeInit; import com.android.internal.os.SamplingProfilerIntegration; @@ -268,6 +269,7 @@ public final class ActivityThread { IBinder token; int ident; Intent intent; + String referrer; IVoiceInteractor voiceInteractor; Bundle state; PersistableBundle persistentState; @@ -290,7 +292,7 @@ public final class ActivityThread { LoadedApk packageInfo; List<ResultInfo> pendingResults; - List<Intent> pendingIntents; + List<ReferrerIntent> pendingIntents; boolean startsNotResumed; boolean isForward; @@ -348,7 +350,7 @@ public final class ActivityThread { } static final class NewIntentData { - List<Intent> intents; + List<ReferrerIntent> intents; IBinder token; public String toString() { return "NewIntentData{intents=" + intents + " token=" + token + "}"; @@ -605,9 +607,9 @@ public final class ActivityThread { // activity itself back to the activity manager. (matters more with ipc) public final void scheduleLaunchActivity(Intent intent, IBinder token, int ident, ActivityInfo info, Configuration curConfig, CompatibilityInfo compatInfo, - IVoiceInteractor voiceInteractor, int procState, Bundle state, + String referrer, IVoiceInteractor voiceInteractor, int procState, Bundle state, PersistableBundle persistentState, List<ResultInfo> pendingResults, - List<Intent> pendingNewIntents, boolean notResumed, boolean isForward, + List<ReferrerIntent> pendingNewIntents, boolean notResumed, boolean isForward, ProfilerInfo profilerInfo) { updateProcessState(procState, false); @@ -617,6 +619,7 @@ public final class ActivityThread { r.token = token; r.ident = ident; r.intent = intent; + r.referrer = referrer; r.voiceInteractor = voiceInteractor; r.activityInfo = info; r.compatInfo = compatInfo; @@ -637,13 +640,13 @@ public final class ActivityThread { } public final void scheduleRelaunchActivity(IBinder token, - List<ResultInfo> pendingResults, List<Intent> pendingNewIntents, + List<ResultInfo> pendingResults, List<ReferrerIntent> pendingNewIntents, int configChanges, boolean notResumed, Configuration config) { requestRelaunchActivity(token, pendingResults, pendingNewIntents, configChanges, notResumed, config, true); } - public final void scheduleNewIntent(List<Intent> intents, IBinder token) { + public final void scheduleNewIntent(List<ReferrerIntent> intents, IBinder token) { NewIntentData data = new NewIntentData(); data.intents = intents; data.token = token; @@ -1742,6 +1745,12 @@ public final class ActivityThread { new LoadedApk(this, aInfo, compatInfo, baseLoader, securityViolation, includeCode && (aInfo.flags&ApplicationInfo.FLAG_HAS_CODE) != 0, registerPackage); + + if (mSystemThread && "android".equals(aInfo.packageName)) { + packageInfo.installSystemApplicationInfo(aInfo, + getSystemContext().mPackageInfo.getClassLoader()); + } + if (includeCode) { mPackages.put(aInfo.packageName, new WeakReference<LoadedApk>(packageInfo)); @@ -1802,10 +1811,6 @@ public final class ActivityThread { synchronized (this) { getSystemContext().installSystemApplicationInfo(info, classLoader); - // The code package for "android" in the system server needs - // to be the system context's package. - mPackages.put("android", new WeakReference<LoadedApk>(getSystemContext().mPackageInfo)); - // give ourselves a default profiler mProfiler = new Profiler(); } @@ -2232,7 +2237,7 @@ public final class ActivityThread { activity.attach(appContext, this, getInstrumentation(), r.token, r.ident, app, r.intent, r.activityInfo, title, r.parent, r.embeddedID, r.lastNonConfigurationInstances, config, - r.voiceInteractor); + r.referrer, r.voiceInteractor); if (customIntent != null) { activity.mIntent = customIntent; @@ -2419,8 +2424,7 @@ public final class ActivityThread { } } - private void deliverNewIntents(ActivityClientRecord r, - List<Intent> intents) { + private void deliverNewIntents(ActivityClientRecord r, List<ReferrerIntent> intents) { final int N = intents.size(); for (int i=0; i<N; i++) { Intent intent = intents.get(i); @@ -2431,8 +2435,7 @@ public final class ActivityThread { } } - public final void performNewIntents(IBinder token, - List<Intent> intents) { + public final void performNewIntents(IBinder token, List<ReferrerIntent> intents) { ActivityClientRecord r = mActivities.get(token); if (r != null) { final boolean resumed = !r.paused; @@ -3750,7 +3753,7 @@ public final class ActivityThread { } public final void requestRelaunchActivity(IBinder token, - List<ResultInfo> pendingResults, List<Intent> pendingNewIntents, + List<ResultInfo> pendingResults, List<ReferrerIntent> pendingNewIntents, int configChanges, boolean notResumed, Configuration config, boolean fromServer) { ActivityClientRecord target = null; diff --git a/core/java/android/app/ActivityTransitionCoordinator.java b/core/java/android/app/ActivityTransitionCoordinator.java index 82b6e35..9062892 100644 --- a/core/java/android/app/ActivityTransitionCoordinator.java +++ b/core/java/android/app/ActivityTransitionCoordinator.java @@ -638,6 +638,7 @@ abstract class ActivityTransitionCoordinator extends ResultReceiver { if (decorView != null) { decorView.getLocationOnScreen(decorLoc); } + Matrix tempMatrix = new Matrix(); for (String name: names) { Bundle sharedElementBundle = state.getBundle(name); if (sharedElementBundle != null) { @@ -647,7 +648,7 @@ abstract class ActivityTransitionCoordinator extends ResultReceiver { snapshot = mListener.onCreateSnapshotView(context, parcelable); } if (snapshot != null) { - setSharedElementState(snapshot, name, state, null, null, decorLoc); + setSharedElementState(snapshot, name, state, tempMatrix, null, decorLoc); } snapshots.add(snapshot); } diff --git a/core/java/android/app/ApplicationPackageManager.java b/core/java/android/app/ApplicationPackageManager.java index 854719d..967e97e 100644 --- a/core/java/android/app/ApplicationPackageManager.java +++ b/core/java/android/app/ApplicationPackageManager.java @@ -1609,6 +1609,18 @@ final class ApplicationPackageManager extends PackageManager { return null; } + /** + * @hide + */ + @Override + public boolean isUpgrade() { + try { + return mPM.isUpgrade(); + } catch (RemoteException e) { + return false; + } + } + @Override public PackageInstaller getPackageInstaller() { synchronized (mLock) { diff --git a/core/java/android/app/ApplicationThreadNative.java b/core/java/android/app/ApplicationThreadNative.java index 0123e16..d1b77b9 100644 --- a/core/java/android/app/ApplicationThreadNative.java +++ b/core/java/android/app/ApplicationThreadNative.java @@ -36,6 +36,7 @@ import android.os.IBinder; import android.os.Parcel; import android.os.ParcelFileDescriptor; import com.android.internal.app.IVoiceInteractor; +import com.android.internal.content.ReferrerIntent; import java.io.FileDescriptor; import java.io.IOException; @@ -140,19 +141,21 @@ public abstract class ApplicationThreadNative extends Binder ActivityInfo info = ActivityInfo.CREATOR.createFromParcel(data); Configuration curConfig = Configuration.CREATOR.createFromParcel(data); CompatibilityInfo compatInfo = CompatibilityInfo.CREATOR.createFromParcel(data); + String referrer = data.readString(); IVoiceInteractor voiceInteractor = IVoiceInteractor.Stub.asInterface( data.readStrongBinder()); int procState = data.readInt(); Bundle state = data.readBundle(); PersistableBundle persistentState = data.readPersistableBundle(); List<ResultInfo> ri = data.createTypedArrayList(ResultInfo.CREATOR); - List<Intent> pi = data.createTypedArrayList(Intent.CREATOR); + List<ReferrerIntent> pi = data.createTypedArrayList(ReferrerIntent.CREATOR); boolean notResumed = data.readInt() != 0; boolean isForward = data.readInt() != 0; ProfilerInfo profilerInfo = data.readInt() != 0 ? ProfilerInfo.CREATOR.createFromParcel(data) : null; - scheduleLaunchActivity(intent, b, ident, info, curConfig, compatInfo, voiceInteractor, - procState, state, persistentState, ri, pi, notResumed, isForward, profilerInfo); + scheduleLaunchActivity(intent, b, ident, info, curConfig, compatInfo, referrer, + voiceInteractor, procState, state, persistentState, ri, pi, + notResumed, isForward, profilerInfo); return true; } @@ -161,7 +164,7 @@ public abstract class ApplicationThreadNative extends Binder data.enforceInterface(IApplicationThread.descriptor); IBinder b = data.readStrongBinder(); List<ResultInfo> ri = data.createTypedArrayList(ResultInfo.CREATOR); - List<Intent> pi = data.createTypedArrayList(Intent.CREATOR); + List<ReferrerIntent> pi = data.createTypedArrayList(ReferrerIntent.CREATOR); int configChanges = data.readInt(); boolean notResumed = data.readInt() != 0; Configuration config = null; @@ -175,7 +178,7 @@ public abstract class ApplicationThreadNative extends Binder case SCHEDULE_NEW_INTENT_TRANSACTION: { data.enforceInterface(IApplicationThread.descriptor); - List<Intent> pi = data.createTypedArrayList(Intent.CREATOR); + List<ReferrerIntent> pi = data.createTypedArrayList(ReferrerIntent.CREATOR); IBinder b = data.readStrongBinder(); scheduleNewIntent(pi, b); return true; @@ -764,9 +767,9 @@ class ApplicationThreadProxy implements IApplicationThread { public final void scheduleLaunchActivity(Intent intent, IBinder token, int ident, ActivityInfo info, Configuration curConfig, CompatibilityInfo compatInfo, - IVoiceInteractor voiceInteractor, int procState, Bundle state, + String referrer, IVoiceInteractor voiceInteractor, int procState, Bundle state, PersistableBundle persistentState, List<ResultInfo> pendingResults, - List<Intent> pendingNewIntents, boolean notResumed, boolean isForward, + List<ReferrerIntent> pendingNewIntents, boolean notResumed, boolean isForward, ProfilerInfo profilerInfo) throws RemoteException { Parcel data = Parcel.obtain(); data.writeInterfaceToken(IApplicationThread.descriptor); @@ -776,6 +779,7 @@ class ApplicationThreadProxy implements IApplicationThread { info.writeToParcel(data, 0); curConfig.writeToParcel(data, 0); compatInfo.writeToParcel(data, 0); + data.writeString(referrer); data.writeStrongBinder(voiceInteractor != null ? voiceInteractor.asBinder() : null); data.writeInt(procState); data.writeBundle(state); @@ -796,7 +800,7 @@ class ApplicationThreadProxy implements IApplicationThread { } public final void scheduleRelaunchActivity(IBinder token, - List<ResultInfo> pendingResults, List<Intent> pendingNewIntents, + List<ResultInfo> pendingResults, List<ReferrerIntent> pendingNewIntents, int configChanges, boolean notResumed, Configuration config) throws RemoteException { Parcel data = Parcel.obtain(); @@ -817,7 +821,7 @@ class ApplicationThreadProxy implements IApplicationThread { data.recycle(); } - public void scheduleNewIntent(List<Intent> intents, IBinder token) + public void scheduleNewIntent(List<ReferrerIntent> intents, IBinder token) throws RemoteException { Parcel data = Parcel.obtain(); data.writeInterfaceToken(IApplicationThread.descriptor); diff --git a/core/java/android/app/BackStackRecord.java b/core/java/android/app/BackStackRecord.java index 0092ee7..2784d44 100644 --- a/core/java/android/app/BackStackRecord.java +++ b/core/java/android/app/BackStackRecord.java @@ -38,6 +38,7 @@ import android.view.ViewTreeObserver; import java.io.FileDescriptor; import java.io.PrintWriter; import java.util.ArrayList; +import java.util.List; final class BackStackState implements Parcelable { final int[] mOps; @@ -1055,7 +1056,7 @@ final class BackStackRecord extends FragmentTransaction implements } private static ArrayList<View> captureExitingViews(Transition exitTransition, - Fragment outFragment, ArrayMap<String, View> namedViews) { + Fragment outFragment, ArrayMap<String, View> namedViews, View nonExistentView) { ArrayList<View> viewList = null; if (exitTransition != null) { viewList = new ArrayList<View>(); @@ -1064,7 +1065,10 @@ final class BackStackRecord extends FragmentTransaction implements if (namedViews != null) { viewList.removeAll(namedViews.values()); } - addTargets(exitTransition, viewList); + if (!viewList.isEmpty()) { + viewList.add(nonExistentView); + addTargets(exitTransition, viewList); + } } return viewList; } @@ -1132,11 +1136,8 @@ final class BackStackRecord extends FragmentTransaction implements namedViews = mapSharedElementsIn(state, isBack, inFragment); removeTargets(sharedElementTransition, sharedElementTargets); sharedElementTargets.clear(); - if (namedViews.isEmpty()) { - sharedElementTargets.add(state.nonExistentView); - } else { - sharedElementTargets.addAll(namedViews.values()); - } + sharedElementTargets.add(state.nonExistentView); + sharedElementTargets.addAll(namedViews.values()); addTargets(sharedElementTransition, sharedElementTargets); @@ -1153,6 +1154,9 @@ final class BackStackRecord extends FragmentTransaction implements if (namedViews != null) { enteringViews.removeAll(namedViews.values()); } + enteringViews.add(state.nonExistentView); + // We added this earlier to prevent any views being targeted. + enterTransition.removeTarget(state.nonExistentView); addTargets(enterTransition, enteringViews); } setSharedElementEpicenter(enterTransition, state); @@ -1293,11 +1297,8 @@ final class BackStackRecord extends FragmentTransaction implements ArrayList<View> sharedElementTargets = new ArrayList<View>(); if (sharedElementTransition != null) { namedViews = remapSharedElements(state, outFragment, isBack); - if (namedViews.isEmpty()) { - sharedElementTargets.add(state.nonExistentView); - } else { - sharedElementTargets.addAll(namedViews.values()); - } + sharedElementTargets.add(state.nonExistentView); + sharedElementTargets.addAll(namedViews.values()); addTargets(sharedElementTransition, sharedElementTargets); // Notify the start of the transition. @@ -1310,7 +1311,7 @@ final class BackStackRecord extends FragmentTransaction implements } ArrayList<View> exitingViews = captureExitingViews(exitTransition, outFragment, - namedViews); + namedViews, state.nonExistentView); if (exitingViews == null || exitingViews.isEmpty()) { exitTransition = null; } @@ -1388,20 +1389,69 @@ final class BackStackRecord extends FragmentTransaction implements } } - private static void removeTargets(Transition transition, ArrayList<View> views) { - int numViews = views.size(); - for (int i = 0; i < numViews; i++) { - transition.removeTarget(views.get(i)); + /** + * This method removes the views from transitions that target ONLY those views. + * The views list should match those added in addTargets and should contain + * one view that is not in the view hierarchy (state.nonExistentView). + */ + public static void removeTargets(Transition transition, ArrayList<View> views) { + if (transition instanceof TransitionSet) { + TransitionSet set = (TransitionSet) transition; + int numTransitions = set.getTransitionCount(); + for (int i = 0; i < numTransitions; i++) { + Transition child = set.getTransitionAt(i); + removeTargets(child, views); + } + } else if (!hasSimpleTarget(transition)) { + List<View> targets = transition.getTargets(); + if (targets != null && targets.size() == views.size() && + targets.containsAll(views)) { + // We have an exact match. We must have added these earlier in addTargets + for (int i = views.size() - 1; i >= 0; i--) { + transition.removeTarget(views.get(i)); + } + } } } - private static void addTargets(Transition transition, ArrayList<View> views) { - int numViews = views.size(); - for (int i = 0; i < numViews; i++) { - transition.addTarget(views.get(i)); + /** + * This method adds views as targets to the transition, but only if the transition + * doesn't already have a target. It is best for views to contain one View object + * that does not exist in the view hierarchy (state.nonExistentView) so that + * when they are removed later, a list match will suffice to remove the targets. + * Otherwise, if you happened to have targeted the exact views for the transition, + * the removeTargets call will remove them unexpectedly. + */ + public static void addTargets(Transition transition, ArrayList<View> views) { + if (transition instanceof TransitionSet) { + TransitionSet set = (TransitionSet) transition; + int numTransitions = set.getTransitionCount(); + for (int i = 0; i < numTransitions; i++) { + Transition child = set.getTransitionAt(i); + addTargets(child, views); + } + } else if (!hasSimpleTarget(transition)) { + List<View> targets = transition.getTargets(); + if (isNullOrEmpty(targets)) { + // We can just add the target views + int numViews = views.size(); + for (int i = 0; i < numViews; i++) { + transition.addTarget(views.get(i)); + } + } } } + private static boolean hasSimpleTarget(Transition transition) { + return !isNullOrEmpty(transition.getTargetIds()) || + !isNullOrEmpty(transition.getTargetNames()) || + !isNullOrEmpty(transition.getTargetTypes()); + } + + private static boolean isNullOrEmpty(List list) { + return list == null || list.isEmpty(); + } + /** * Remaps a name-to-View map, substituting different names for keys. * diff --git a/core/java/android/app/EnterTransitionCoordinator.java b/core/java/android/app/EnterTransitionCoordinator.java index 7894887..ecf19c7 100644 --- a/core/java/android/app/EnterTransitionCoordinator.java +++ b/core/java/android/app/EnterTransitionCoordinator.java @@ -133,16 +133,17 @@ class EnterTransitionCoordinator extends ActivityTransitionCoordinator { return; } mAreViewsReady = true; + final ViewGroup decor = getDecor(); // Ensure the views have been laid out before capturing the views -- we need the epicenter. - if (sharedElements.isEmpty() || !sharedElements.valueAt(0).isLayoutRequested()) { + if (decor == null || (decor.isAttachedToWindow() && + (sharedElements.isEmpty() || !sharedElements.valueAt(0).isLayoutRequested()))) { viewsReady(sharedElements); } else { - final View sharedElement = sharedElements.valueAt(0); - sharedElement.getViewTreeObserver() + decor.getViewTreeObserver() .addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() { @Override public boolean onPreDraw() { - sharedElement.getViewTreeObserver().removeOnPreDrawListener(this); + decor.getViewTreeObserver().removeOnPreDrawListener(this); viewsReady(sharedElements); return true; } diff --git a/core/java/android/app/IActivityManager.java b/core/java/android/app/IActivityManager.java index be26f30..6433f3f 100644 --- a/core/java/android/app/IActivityManager.java +++ b/core/java/android/app/IActivityManager.java @@ -373,7 +373,7 @@ public interface IActivityManager extends IInterface { public boolean isUserRunning(int userid, boolean orStopping) throws RemoteException; public int[] getRunningUserIds() throws RemoteException; - public boolean removeTask(int taskId, int flags) throws RemoteException; + public boolean removeTask(int taskId) throws RemoteException; public void registerProcessObserver(IProcessObserver observer) throws RemoteException; public void unregisterProcessObserver(IProcessObserver observer) throws RemoteException; @@ -456,6 +456,9 @@ public interface IActivityManager extends IInterface { throws RemoteException; public Bitmap getTaskDescriptionIcon(String filename) throws RemoteException; + public void startInPlaceAnimationOnFrontMostApplication(ActivityOptions opts) + throws RemoteException; + public boolean requestVisibleBehind(IBinder token, boolean visible) throws RemoteException; public boolean isBackgroundVisibleBehind(IBinder token) throws RemoteException; public void backgroundResourcesReleased(IBinder token) throws RemoteException; @@ -781,4 +784,5 @@ public interface IActivityManager extends IInterface { int BOOT_ANIMATION_COMPLETE_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+237; int GET_TASK_DESCRIPTION_ICON_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+238; int LAUNCH_ASSIST_INTENT_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+239; + int START_IN_PLACE_ANIMATION_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+240; } diff --git a/core/java/android/app/IApplicationThread.java b/core/java/android/app/IApplicationThread.java index f53075c..42acbc6 100644 --- a/core/java/android/app/IApplicationThread.java +++ b/core/java/android/app/IApplicationThread.java @@ -35,6 +35,7 @@ import android.os.IBinder; import android.os.IInterface; import android.service.voice.IVoiceInteractionSession; import com.android.internal.app.IVoiceInteractor; +import com.android.internal.content.ReferrerIntent; import java.io.FileDescriptor; import java.util.List; @@ -59,14 +60,14 @@ public interface IApplicationThread extends IInterface { void scheduleSendResult(IBinder token, List<ResultInfo> results) throws RemoteException; void scheduleLaunchActivity(Intent intent, IBinder token, int ident, ActivityInfo info, Configuration curConfig, CompatibilityInfo compatInfo, - IVoiceInteractor voiceInteractor, int procState, Bundle state, + String referrer, IVoiceInteractor voiceInteractor, int procState, Bundle state, PersistableBundle persistentState, List<ResultInfo> pendingResults, - List<Intent> pendingNewIntents, boolean notResumed, boolean isForward, + List<ReferrerIntent> pendingNewIntents, boolean notResumed, boolean isForward, ProfilerInfo profilerInfo) throws RemoteException; void scheduleRelaunchActivity(IBinder token, List<ResultInfo> pendingResults, - List<Intent> pendingNewIntents, int configChanges, + List<ReferrerIntent> pendingNewIntents, int configChanges, boolean notResumed, Configuration config) throws RemoteException; - void scheduleNewIntent(List<Intent> intent, IBinder token) throws RemoteException; + void scheduleNewIntent(List<ReferrerIntent> intent, IBinder token) throws RemoteException; void scheduleDestroyActivity(IBinder token, boolean finished, int configChanges) throws RemoteException; void scheduleReceiver(Intent intent, ActivityInfo info, CompatibilityInfo compatInfo, diff --git a/core/java/android/app/Instrumentation.java b/core/java/android/app/Instrumentation.java index 60a013e..d96153a 100644 --- a/core/java/android/app/Instrumentation.java +++ b/core/java/android/app/Instrumentation.java @@ -45,6 +45,7 @@ import android.view.KeyEvent; import android.view.MotionEvent; import android.view.ViewConfiguration; import android.view.Window; +import com.android.internal.content.ReferrerIntent; import java.io.File; import java.util.ArrayList; @@ -1042,7 +1043,7 @@ public class Instrumentation { activity.attach(context, aThread, this, token, 0, application, intent, info, title, parent, id, (Activity.NonConfigurationInstances)lastNonConfigurationInstance, - new Configuration(), null); + new Configuration(), null, null); return activity; } @@ -1207,7 +1208,17 @@ public class Instrumentation { * @param intent The new intent being received. */ public void callActivityOnNewIntent(Activity activity, Intent intent) { - activity.onNewIntent(intent); + final String oldReferrer = activity.mReferrer; + try { + try { + activity.mReferrer = ((ReferrerIntent)intent).mReferrer; + } catch (ClassCastException e) { + activity.mReferrer = null; + } + activity.onNewIntent(intent); + } finally { + activity.mReferrer = oldReferrer; + } } /** diff --git a/core/java/android/app/LocalActivityManager.java b/core/java/android/app/LocalActivityManager.java index b654a6a..873e337 100644 --- a/core/java/android/app/LocalActivityManager.java +++ b/core/java/android/app/LocalActivityManager.java @@ -22,6 +22,7 @@ import android.os.Binder; import android.os.Bundle; import android.util.Log; import android.view.Window; +import com.android.internal.content.ReferrerIntent; import java.util.ArrayList; import java.util.HashMap; @@ -310,8 +311,8 @@ public class LocalActivityManager { if (aInfo.launchMode != ActivityInfo.LAUNCH_MULTIPLE || (intent.getFlags()&Intent.FLAG_ACTIVITY_SINGLE_TOP) != 0) { // The activity wants onNewIntent() called. - ArrayList<Intent> intents = new ArrayList<Intent>(1); - intents.add(intent); + ArrayList<ReferrerIntent> intents = new ArrayList<>(1); + intents.add(new ReferrerIntent(intent, mParent.getPackageName())); if (localLOGV) Log.v(TAG, r.id + ": new intent"); mActivityThread.performNewIntents(r, intents); r.intent = intent; diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java index 9849c51..dfe5cf5 100644 --- a/core/java/android/app/Notification.java +++ b/core/java/android/app/Notification.java @@ -1113,7 +1113,11 @@ public class Notification implements Parcelable /** Notification action extra which contains wearable extensions */ private static final String EXTRA_WEARABLE_EXTENSIONS = "android.wearable.EXTENSIONS"; + // Keys within EXTRA_WEARABLE_EXTENSIONS for wearable options. private static final String KEY_FLAGS = "flags"; + private static final String KEY_IN_PROGRESS_LABEL = "inProgressLabel"; + private static final String KEY_CONFIRM_LABEL = "confirmLabel"; + private static final String KEY_CANCEL_LABEL = "cancelLabel"; // Flags bitwise-ored to mFlags private static final int FLAG_AVAILABLE_OFFLINE = 0x1; @@ -1123,6 +1127,10 @@ public class Notification implements Parcelable private int mFlags = DEFAULT_FLAGS; + private CharSequence mInProgressLabel; + private CharSequence mConfirmLabel; + private CharSequence mCancelLabel; + /** * Create a {@link android.app.Notification.Action.WearableExtender} with default * options. @@ -1139,6 +1147,9 @@ public class Notification implements Parcelable Bundle wearableBundle = action.getExtras().getBundle(EXTRA_WEARABLE_EXTENSIONS); if (wearableBundle != null) { mFlags = wearableBundle.getInt(KEY_FLAGS, DEFAULT_FLAGS); + mInProgressLabel = wearableBundle.getCharSequence(KEY_IN_PROGRESS_LABEL); + mConfirmLabel = wearableBundle.getCharSequence(KEY_CONFIRM_LABEL); + mCancelLabel = wearableBundle.getCharSequence(KEY_CANCEL_LABEL); } } @@ -1154,6 +1165,15 @@ public class Notification implements Parcelable if (mFlags != DEFAULT_FLAGS) { wearableBundle.putInt(KEY_FLAGS, mFlags); } + if (mInProgressLabel != null) { + wearableBundle.putCharSequence(KEY_IN_PROGRESS_LABEL, mInProgressLabel); + } + if (mConfirmLabel != null) { + wearableBundle.putCharSequence(KEY_CONFIRM_LABEL, mConfirmLabel); + } + if (mCancelLabel != null) { + wearableBundle.putCharSequence(KEY_CANCEL_LABEL, mCancelLabel); + } builder.getExtras().putBundle(EXTRA_WEARABLE_EXTENSIONS, wearableBundle); return builder; @@ -1163,6 +1183,9 @@ public class Notification implements Parcelable public WearableExtender clone() { WearableExtender that = new WearableExtender(); that.mFlags = this.mFlags; + that.mInProgressLabel = this.mInProgressLabel; + that.mConfirmLabel = this.mConfirmLabel; + that.mCancelLabel = this.mCancelLabel; return that; } @@ -1194,6 +1217,72 @@ public class Notification implements Parcelable mFlags &= ~mask; } } + + /** + * Set a label to display while the wearable is preparing to automatically execute the + * action. This is usually a 'ing' verb ending in ellipsis like "Sending..." + * + * @param label the label to display while the action is being prepared to execute + * @return this object for method chaining + */ + public WearableExtender setInProgressLabel(CharSequence label) { + mInProgressLabel = label; + return this; + } + + /** + * Get the label to display while the wearable is preparing to automatically execute + * the action. This is usually a 'ing' verb ending in ellipsis like "Sending..." + * + * @return the label to display while the action is being prepared to execute + */ + public CharSequence getInProgressLabel() { + return mInProgressLabel; + } + + /** + * Set a label to display to confirm that the action should be executed. + * This is usually an imperative verb like "Send". + * + * @param label the label to confirm the action should be executed + * @return this object for method chaining + */ + public WearableExtender setConfirmLabel(CharSequence label) { + mConfirmLabel = label; + return this; + } + + /** + * Get the label to display to confirm that the action should be executed. + * This is usually an imperative verb like "Send". + * + * @return the label to confirm the action should be executed + */ + public CharSequence getConfirmLabel() { + return mConfirmLabel; + } + + /** + * Set a label to display to cancel the action. + * This is usually an imperative verb, like "Cancel". + * + * @param label the label to display to cancel the action + * @return this object for method chaining + */ + public WearableExtender setCancelLabel(CharSequence label) { + mCancelLabel = label; + return this; + } + + /** + * Get the label to display to cancel the action. + * This is usually an imperative verb like "Cancel". + * + * @return the label to display to cancel the action + */ + public CharSequence getCancelLabel() { + return mCancelLabel; + } } } @@ -4340,10 +4429,23 @@ public class Notification implements Parcelable */ public static final int SIZE_FULL_SCREEN = 5; + /** + * Sentinel value for use with {@link #setHintScreenTimeout} to keep the screen on for a + * short amount of time when this notification is displayed on the screen. This + * is the default value. + */ + public static final int SCREEN_TIMEOUT_SHORT = 0; + + /** + * Sentinel value for use with {@link #setHintScreenTimeout} to keep the screen on + * for a longer amount of time when this notification is displayed on the screen. + */ + public static final int SCREEN_TIMEOUT_LONG = -1; + /** Notification extra which contains wearable extensions */ private static final String EXTRA_WEARABLE_EXTENSIONS = "android.wearable.EXTENSIONS"; - // Keys within EXTRA_WEARABLE_OPTIONS for wearable options. + // Keys within EXTRA_WEARABLE_EXTENSIONS for wearable options. private static final String KEY_ACTIONS = "actions"; private static final String KEY_FLAGS = "flags"; private static final String KEY_DISPLAY_INTENT = "displayIntent"; @@ -4355,12 +4457,14 @@ public class Notification implements Parcelable private static final String KEY_CUSTOM_SIZE_PRESET = "customSizePreset"; private static final String KEY_CUSTOM_CONTENT_HEIGHT = "customContentHeight"; private static final String KEY_GRAVITY = "gravity"; + private static final String KEY_HINT_SCREEN_TIMEOUT = "hintScreenTimeout"; // Flags bitwise-ored to mFlags private static final int FLAG_CONTENT_INTENT_AVAILABLE_OFFLINE = 0x1; private static final int FLAG_HINT_HIDE_ICON = 1 << 1; private static final int FLAG_HINT_SHOW_BACKGROUND_ONLY = 1 << 2; private static final int FLAG_START_SCROLL_BOTTOM = 1 << 3; + private static final int FLAG_HINT_AVOID_BACKGROUND_CLIPPING = 1 << 4; // Default value for flags integer private static final int DEFAULT_FLAGS = FLAG_CONTENT_INTENT_AVAILABLE_OFFLINE; @@ -4379,6 +4483,7 @@ public class Notification implements Parcelable private int mCustomSizePreset = SIZE_DEFAULT; private int mCustomContentHeight; private int mGravity = DEFAULT_GRAVITY; + private int mHintScreenTimeout; /** * Create a {@link android.app.Notification.WearableExtender} with default @@ -4414,6 +4519,7 @@ public class Notification implements Parcelable SIZE_DEFAULT); mCustomContentHeight = wearableBundle.getInt(KEY_CUSTOM_CONTENT_HEIGHT); mGravity = wearableBundle.getInt(KEY_GRAVITY, DEFAULT_GRAVITY); + mHintScreenTimeout = wearableBundle.getInt(KEY_HINT_SCREEN_TIMEOUT); } } @@ -4461,6 +4567,9 @@ public class Notification implements Parcelable if (mGravity != DEFAULT_GRAVITY) { wearableBundle.putInt(KEY_GRAVITY, mGravity); } + if (mHintScreenTimeout != 0) { + wearableBundle.putInt(KEY_HINT_SCREEN_TIMEOUT, mHintScreenTimeout); + } builder.getExtras().putBundle(EXTRA_WEARABLE_EXTENSIONS, wearableBundle); return builder; @@ -4480,6 +4589,7 @@ public class Notification implements Parcelable that.mCustomSizePreset = this.mCustomSizePreset; that.mCustomContentHeight = this.mCustomContentHeight; that.mGravity = this.mGravity; + that.mHintScreenTimeout = this.mHintScreenTimeout; return that; } @@ -4875,6 +4985,48 @@ public class Notification implements Parcelable return (mFlags & FLAG_HINT_SHOW_BACKGROUND_ONLY) != 0; } + /** + * Set a hint that this notification's background should not be clipped if possible. + * @param hintAvoidBackgroundClipping {@code true} to avoid clipping if possible. + * @return this object for method chaining + */ + public WearableExtender setHintAvoidBackgroundClipping( + boolean hintAvoidBackgroundClipping) { + setFlag(FLAG_HINT_AVOID_BACKGROUND_CLIPPING, hintAvoidBackgroundClipping); + return this; + } + + /** + * Get a hint that this notification's background should not be clipped if possible. + * @return {@code true} if it's ok if the background is clipped on the screen, false + * otherwise. The default value is {@code false} if this was never set. + */ + public boolean getHintAvoidBackgroundClipping() { + return (mFlags & FLAG_HINT_AVOID_BACKGROUND_CLIPPING) != 0; + } + + /** + * Set a hint that the screen should remain on for at least this duration when + * this notification is displayed on the screen. + * @param timeout The requested screen timeout in milliseconds. Can also be either + * {@link #SCREEN_TIMEOUT_SHORT} or {@link #SCREEN_TIMEOUT_LONG}. + * @return this object for method chaining + */ + public WearableExtender setHintScreenTimeout(int timeout) { + mHintScreenTimeout = timeout; + return this; + } + + /** + * Get the duration, in milliseconds, that the screen should remain on for + * when this notification is displayed. + * @return the duration in milliseconds if > 0, or either one of the sentinel values + * {@link #SCREEN_TIMEOUT_SHORT} or {@link #SCREEN_TIMEOUT_LONG}. + */ + public int getHintScreenTimeout() { + return mHintScreenTimeout; + } + private void setFlag(int mask, boolean value) { if (value) { mFlags |= mask; diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java index a30ae57..9157b1b 100644 --- a/core/java/android/app/admin/DevicePolicyManager.java +++ b/core/java/android/app/admin/DevicePolicyManager.java @@ -31,6 +31,7 @@ import android.content.pm.ResolveInfo; import android.net.ProxyInfo; import android.os.Bundle; import android.os.Handler; +import android.os.PersistableBundle; import android.os.Process; import android.os.RemoteCallback; import android.os.RemoteException; @@ -40,6 +41,7 @@ import android.os.UserManager; import android.provider.Settings; import android.security.Credentials; import android.service.restrictions.RestrictionsReceiver; +import android.service.trust.TrustAgentService; import android.util.Log; import com.android.org.conscrypt.TrustedCertificateStore; @@ -60,13 +62,16 @@ import java.util.Collections; import java.util.List; /** - * Public interface for managing policies enforced on a device. Most clients - * of this class must have published a {@link DeviceAdminReceiver} that the user - * has currently enabled. + * Public interface for managing policies enforced on a device. Most clients of this class must be + * registered with the system as a + * <a href={@docRoot}guide/topics/admin/device-admin.html">device administrator</a>. Additionally, + * a device administrator may be registered as either a profile or device owner. A given method is + * accessible to all device administrators unless the documentation for that method specifies that + * it is restricted to either device or profile owners. * * <div class="special reference"> * <h3>Developer Guides</h3> - * <p>For more information about managing policies for device adminstration, read the + * <p>For more information about managing policies for device administration, read the * <a href="{@docRoot}guide/topics/admin/device-admin.html">Device Administration</a> * developer guide.</p> * </div> @@ -2584,6 +2589,10 @@ public class DevicePolicyManager { * <p>The application restrictions are only made visible to the target application and the * profile or device owner. * + * <p>If the restrictions are not available yet, but may be applied in the near future, + * the admin can notify the target application of that by adding + * {@link UserManager#KEY_RESTRICTIONS_PENDING} to the settings parameter. + * * <p>The calling device admin must be a profile or device owner; if it is not, a security * exception will be thrown. * @@ -2591,6 +2600,8 @@ public class DevicePolicyManager { * @param packageName The name of the package to update restricted settings for. * @param settings A {@link Bundle} to be parsed by the receiving application, conveying a new * set of active restrictions. + * + * @see UserManager#KEY_RESTRICTIONS_PENDING */ public void setApplicationRestrictions(ComponentName admin, String packageName, Bundle settings) { @@ -2604,25 +2615,29 @@ public class DevicePolicyManager { } /** - * Sets a list of features to enable for a TrustAgent component. This is meant to be - * used in conjunction with {@link #KEYGUARD_DISABLE_TRUST_AGENTS}, which will disable all - * trust agents but those with features enabled by this function call. + * Sets a list of configuration features to enable for a TrustAgent component. This is meant + * to be used in conjunction with {@link #KEYGUARD_DISABLE_TRUST_AGENTS}, which disables all + * trust agents but those enabled by this function call. If flag + * {@link #KEYGUARD_DISABLE_TRUST_AGENTS} is not set, then this call has no effect. * * <p>The calling device admin must have requested * {@link DeviceAdminInfo#USES_POLICY_DISABLE_KEYGUARD_FEATURES} to be able to call - * this method; if it has not, a security exception will be thrown. + * this method; if not, a security exception will be thrown. * * @param admin Which {@link DeviceAdminReceiver} this request is associated with. - * @param agent Which component to enable features for. - * @param features List of features to enable. Consult specific TrustAgent documentation for - * the feature list. - * @hide + * @param target Component name of the agent to be enabled. + * @param options TrustAgent-specific feature bundle. If null for any admin, agent + * will be strictly disabled according to the state of the + * {@link #KEYGUARD_DISABLE_TRUST_AGENTS} flag. + * <p>If {@link #KEYGUARD_DISABLE_TRUST_AGENTS} is set and options is not null for all admins, + * then it's up to the TrustAgent itself to aggregate the values from all device admins. + * <p>Consult documentation for the specific TrustAgent to determine legal options parameters. */ - public void setTrustAgentFeaturesEnabled(ComponentName admin, ComponentName agent, - List<String> features) { + public void setTrustAgentConfiguration(ComponentName admin, ComponentName target, + PersistableBundle options) { if (mService != null) { try { - mService.setTrustAgentFeaturesEnabled(admin, agent, features, UserHandle.myUserId()); + mService.setTrustAgentConfiguration(admin, target, options, UserHandle.myUserId()); } catch (RemoteException e) { Log.w(TAG, "Failed talking with device policy service", e); } @@ -2630,24 +2645,30 @@ public class DevicePolicyManager { } /** - * Gets list of enabled features for the given TrustAgent component. If admin is - * null, this will return the intersection of all features enabled for the given agent by all - * admins. + * Gets configuration for the given trust agent based on aggregating all calls to + * {@link #setTrustAgentConfiguration(ComponentName, ComponentName, PersistableBundle)} for + * all device admins. * * @param admin Which {@link DeviceAdminReceiver} this request is associated with. * @param agent Which component to get enabled features for. - * @return List of enabled features. - * @hide + * @return configuration for the given trust agent. */ - public List<String> getTrustAgentFeaturesEnabled(ComponentName admin, ComponentName agent) { + public List<PersistableBundle> getTrustAgentConfiguration(ComponentName admin, + ComponentName agent) { + return getTrustAgentConfiguration(admin, agent, UserHandle.myUserId()); + } + + /** @hide per-user version */ + public List<PersistableBundle> getTrustAgentConfiguration(ComponentName admin, + ComponentName agent, int userHandle) { if (mService != null) { try { - return mService.getTrustAgentFeaturesEnabled(admin, agent, UserHandle.myUserId()); + return mService.getTrustAgentConfiguration(admin, agent, userHandle); } catch (RemoteException e) { Log.w(TAG, "Failed talking with device policy service", e); } } - return new ArrayList<String>(); // empty list + return new ArrayList<PersistableBundle>(); // empty list } /** @@ -3133,9 +3154,10 @@ public class DevicePolicyManager { } /** - * Called by a profile owner to disable account management for a specific type of account. + * Called by a device owner or profile owner to disable account management for a specific type + * of account. * - * <p>The calling device admin must be a profile owner. If it is not, a + * <p>The calling device admin must be a device owner or profile owner. If it is not, a * security exception will be thrown. * * <p>When account management is disabled for an account type, adding or removing an account diff --git a/core/java/android/app/admin/IDevicePolicyManager.aidl b/core/java/android/app/admin/IDevicePolicyManager.aidl index c8e1780..07aa800 100644 --- a/core/java/android/app/admin/IDevicePolicyManager.aidl +++ b/core/java/android/app/admin/IDevicePolicyManager.aidl @@ -22,6 +22,7 @@ import android.content.Intent; import android.content.IntentFilter; import android.net.ProxyInfo; import android.os.Bundle; +import android.os.PersistableBundle; import android.os.RemoteCallback; import android.os.UserHandle; import java.util.List; @@ -183,8 +184,10 @@ interface IDevicePolicyManager { boolean getCrossProfileCallerIdDisabled(in ComponentName who); boolean getCrossProfileCallerIdDisabledForUser(int userId); - void setTrustAgentFeaturesEnabled(in ComponentName admin, in ComponentName agent, in List<String> features, int userId); - List<String> getTrustAgentFeaturesEnabled(in ComponentName admin, in ComponentName agent, int userId); + void setTrustAgentConfiguration(in ComponentName admin, in ComponentName agent, + in PersistableBundle args, int userId); + List<PersistableBundle> getTrustAgentConfiguration(in ComponentName admin, + in ComponentName agent, int userId); boolean addCrossProfileWidgetProvider(in ComponentName admin, String packageName); boolean removeCrossProfileWidgetProvider(in ComponentName admin, String packageName); diff --git a/core/java/android/app/backup/IBackupManager.aidl b/core/java/android/app/backup/IBackupManager.aidl index 8a44c8e..0a2d4f5 100644 --- a/core/java/android/app/backup/IBackupManager.aidl +++ b/core/java/android/app/backup/IBackupManager.aidl @@ -291,4 +291,16 @@ interface IBackupManager { * {@hide} */ void opComplete(int token); + + /** + * Make the device's backup and restore machinery (in)active. When it is inactive, + * the device will not perform any backup operations, nor will it deliver data for + * restore, although clients can still safely call BackupManager methods. + * + * @param whichUser User handle of the defined user whose backup active state + * is to be adjusted. + * @param makeActive {@code true} when backup services are to be made active; + * {@code false} otherwise. + */ + void setBackupServiceActive(int whichUser, boolean makeActive); } diff --git a/core/java/android/bluetooth/BluetoothAdapter.java b/core/java/android/bluetooth/BluetoothAdapter.java index f2e03cf..c262bae 100644 --- a/core/java/android/bluetooth/BluetoothAdapter.java +++ b/core/java/android/bluetooth/BluetoothAdapter.java @@ -1445,7 +1445,7 @@ public final class BluetoothAdapter { if (VDBG) Log.d(TAG, "onBluetoothServiceDown: " + mService); synchronized (mManagerCallback) { mService = null; - mLeScanClients.clear(); + if (mLeScanClients != null) mLeScanClients.clear(); if (sBluetoothLeAdvertiser != null) sBluetoothLeAdvertiser.cleanup(); if (sBluetoothLeScanner != null) sBluetoothLeScanner.cleanup(); for (IBluetoothManagerCallback cb : mProxyServiceStateCallbacks ){ diff --git a/core/java/android/bluetooth/IBluetooth.aidl b/core/java/android/bluetooth/IBluetooth.aidl index 992f601..cd4535a 100644 --- a/core/java/android/bluetooth/IBluetooth.aidl +++ b/core/java/android/bluetooth/IBluetooth.aidl @@ -98,4 +98,7 @@ interface IBluetooth boolean isActivityAndEnergyReportingSupported(); void getActivityEnergyInfoFromController(); BluetoothActivityEnergyInfo reportActivityInfo(); + + // for dumpsys support + String dump(); } diff --git a/core/java/android/content/ContentProvider.java b/core/java/android/content/ContentProvider.java index 2853c58..c8f9b7d 100644 --- a/core/java/android/content/ContentProvider.java +++ b/core/java/android/content/ContentProvider.java @@ -515,7 +515,10 @@ public abstract class ContentProvider implements ComponentCallbacks2 { } // last chance, check against any uri grants - if (context.checkUriPermission(uri, pid, uid, Intent.FLAG_GRANT_READ_URI_PERMISSION) + final int callingUserId = UserHandle.getUserId(uid); + final Uri userUri = (mSingleUser && !UserHandle.isSameUser(mMyUid, uid)) + ? maybeAddUserId(uri, callingUserId) : uri; + if (context.checkUriPermission(userUri, pid, uid, Intent.FLAG_GRANT_READ_URI_PERMISSION) == PERMISSION_GRANTED) { return; } diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java index 7676e4b..57f6028 100644 --- a/core/java/android/content/Intent.java +++ b/core/java/android/content/Intent.java @@ -41,6 +41,7 @@ import android.os.Bundle; import android.os.IBinder; import android.os.Parcel; import android.os.Parcelable; +import android.os.Process; import android.os.StrictMode; import android.os.UserHandle; import android.provider.DocumentsContract; @@ -1400,14 +1401,36 @@ public class Intent implements Parcelable, Cloneable { = "android.intent.extra.ORIGINATING_URI"; /** - * Used as a URI extra field with {@link #ACTION_INSTALL_PACKAGE} and - * {@link #ACTION_VIEW} to indicate the HTTP referrer URI associated with the Intent - * data field or {@link #EXTRA_ORIGINATING_URI}. + * This extra can be used with any Intent used to launch an activity, supplying information + * about who is launching that activity. This field contains a {@link android.net.Uri} + * object, typically an http: or https: URI of the web site that the referral came from; + * it can also use the {@link #URI_ANDROID_APP_SCHEME android-app:} scheme to identify + * a native application that it came from. + * + * <p>To retrieve this value in a client, use {@link android.app.Activity#getReferrer} + * instead of directly retrieving the extra. It is also valid for applications to + * instead supply {@link #EXTRA_REFERRER_NAME} for cases where they can only create + * a string, not a Uri; the field here, if supplied, will always take precedence, + * however.</p> + * + * @see #EXTRA_REFERRER_NAME */ public static final String EXTRA_REFERRER = "android.intent.extra.REFERRER"; /** + * Alternate version of {@link #EXTRA_REFERRER} that supplies the URI as a String rather + * than a {@link android.net.Uri} object. Only for use in cases where Uri objects can + * not be created, in particular when Intent extras are supplied through the + * {@link #URI_INTENT_SCHEME intent:} or {@link #URI_ANDROID_APP_SCHEME android-app:} + * schemes. + * + * @see #EXTRA_REFERRER + */ + public static final String EXTRA_REFERRER_NAME + = "android.intent.extra.REFERRER_NAME"; + + /** * Used as an int extra field with {@link #ACTION_INSTALL_PACKAGE} and * {@link} #ACTION_VIEW} to indicate the uid of the package that initiated the install * @hide @@ -3918,6 +3941,75 @@ public class Intent implements Parcelable, Cloneable { */ public static final int URI_INTENT_SCHEME = 1<<0; + /** + * Flag for use with {@link #toUri} and {@link #parseUri}: the URI string + * always has the "android-app:" scheme. This is a variation of + * {@link #URI_INTENT_SCHEME} whose format is simpler for the case of an + * http/https URI being delivered to a specific package name. The format + * is: + * + * <pre class="prettyprint"> + * android-app://{package_id}/{scheme}/{host}/{path}{#Intent;...}</pre> + * + * <p>In this scheme, only the <code>pacakge_id</code> is required, and all + * other components can be included as desired. Note that this can not be + * used with intents that have a {@link #setSelector}, since the base intent + * will always have an explicit package name.</p> + * + * <p>Some examples of how this scheme maps to Intent objects:</p> + * <table border="2" width="85%" align="center" frame="hsides" rules="rows"> + * <colgroup align="left" /> + * <colgroup align="left" /> + * <thead> + * <tr><th>URI</th> <th>Intent</th></tr> + * </thead> + * + * <tbody> + * <tr><td><code>android-app://com.example.app</code></td> + * <td><table style="margin:0;border:0;cellpadding:0;cellspacing:0"> + * <tr><td>Action: </td><td>{@link #ACTION_MAIN}</td></tr> + * <tr><td>Package: </td><td><code>com.example.app</code></td></tr> + * </table></td> + * </tr> + * <tr><td><code>android-app://com.example.app/http/example.com</code></td> + * <td><table style="margin:0;border:0;cellpadding:0;cellspacing:0"> + * <tr><td>Action: </td><td>{@link #ACTION_VIEW}</td></tr> + * <tr><td>Data: </td><td><code>http://example.com/</code></td></tr> + * <tr><td>Package: </td><td><code>com.example.app</code></td></tr> + * </table></td> + * </tr> + * <tr><td><code>android-app://com.example.app/http/example.com/foo?1234</code></td> + * <td><table style="margin:0;border:0;cellpadding:0;cellspacing:0"> + * <tr><td>Action: </td><td>{@link #ACTION_VIEW}</td></tr> + * <tr><td>Data: </td><td><code>http://example.com/foo?1234</code></td></tr> + * <tr><td>Package: </td><td><code>com.example.app</code></td></tr> + * </table></td> + * </tr> + * <tr><td><code>android-app://com.example.app/<br />#Intent;action=com.example.MY_ACTION;end</code></td> + * <td><table style="margin:0;border:0;cellpadding:0;cellspacing:0"> + * <tr><td>Action: </td><td><code>com.example.MY_ACTION</code></td></tr> + * <tr><td>Package: </td><td><code>com.example.app</code></td></tr> + * </table></td> + * </tr> + * <tr><td><code>android-app://com.example.app/http/example.com/foo?1234<br />#Intent;action=com.example.MY_ACTION;end</code></td> + * <td><table style="margin:0;border:0;cellpadding:0;cellspacing:0"> + * <tr><td>Action: </td><td><code>com.example.MY_ACTION</code></td></tr> + * <tr><td>Data: </td><td><code>http://example.com/foo?1234</code></td></tr> + * <tr><td>Package: </td><td><code>com.example.app</code></td></tr> + * </table></td> + * </tr> + * <tr><td><code>android-app://com.example.app/<br />#Intent;action=com.example.MY_ACTION;<br />i.some_int=100;S.some_str=hello;end</code></td> + * <td><table border="" style="margin:0" > + * <tr><td>Action: </td><td><code>com.example.MY_ACTION</code></td></tr> + * <tr><td>Package: </td><td><code>com.example.app</code></td></tr> + * <tr><td>Extras: </td><td><code>some_int=(int)100<br />some_str=(String)hello</code></td></tr> + * </table></td> + * </tr> + * </tbody> + * </table> + */ + public static final int URI_ANDROID_APP_SCHEME = 1<<1; + // --------------------------------------------------------------------- private String mAction; @@ -4178,8 +4270,8 @@ public class Intent implements Parcelable, Cloneable { * the scheme and full path. * * @param uri The URI to turn into an Intent. - * @param flags Additional processing flags. Either 0 or - * {@link #URI_INTENT_SCHEME}. + * @param flags Additional processing flags. Either 0, + * {@link #URI_INTENT_SCHEME}, or {@link #URI_ANDROID_APP_SCHEME}. * * @return Intent The newly created Intent object. * @@ -4192,9 +4284,11 @@ public class Intent implements Parcelable, Cloneable { public static Intent parseUri(String uri, int flags) throws URISyntaxException { int i = 0; try { - // Validate intent scheme for if requested. - if ((flags&URI_INTENT_SCHEME) != 0) { - if (!uri.startsWith("intent:")) { + final boolean androidApp = uri.startsWith("android-app:"); + + // Validate intent scheme if requested. + if ((flags&(URI_INTENT_SCHEME|URI_ANDROID_APP_SCHEME)) != 0) { + if (!uri.startsWith("intent:") && !androidApp) { Intent intent = new Intent(ACTION_VIEW); try { intent.setData(Uri.parse(uri)); @@ -4205,24 +4299,40 @@ public class Intent implements Parcelable, Cloneable { } } - // simple case i = uri.lastIndexOf("#"); - if (i == -1) return new Intent(ACTION_VIEW, Uri.parse(uri)); + // simple case + if (i == -1) { + if (!androidApp) { + return new Intent(ACTION_VIEW, Uri.parse(uri)); + } // old format Intent URI - if (!uri.startsWith("#Intent;", i)) return getIntentOld(uri); + } else if (!uri.startsWith("#Intent;", i)) { + if (!androidApp) { + return getIntentOld(uri); + } else { + i = -1; + } + } // new format Intent intent = new Intent(ACTION_VIEW); Intent baseIntent = intent; + boolean explicitAction = false; + boolean inSelector = false; // fetch data part, if present - String data = i >= 0 ? uri.substring(0, i) : null; String scheme = null; - i += "#Intent;".length(); + String data; + if (i >= 0) { + data = uri.substring(0, i); + i += 8; // length of "#Intent;" + } else { + data = uri; + } // loop over contents of Intent, all name=value; - while (!uri.startsWith("end", i)) { + while (i >= 0 && !uri.startsWith("end", i)) { int eq = uri.indexOf('=', i); if (eq < 0) eq = i-1; int semi = uri.indexOf(';', i); @@ -4231,6 +4341,9 @@ public class Intent implements Parcelable, Cloneable { // action if (uri.startsWith("action=", i)) { intent.setAction(value); + if (!inSelector) { + explicitAction = true; + } } // categories @@ -4260,7 +4373,11 @@ public class Intent implements Parcelable, Cloneable { // scheme else if (uri.startsWith("scheme=", i)) { - scheme = value; + if (inSelector) { + intent.mData = Uri.parse(value); + } else { + scheme = value; + } } // source bounds @@ -4271,6 +4388,7 @@ public class Intent implements Parcelable, Cloneable { // selector else if (semi == (i+3) && uri.startsWith("SEL", i)) { intent = new Intent(); + inSelector = true; } // extra @@ -4296,9 +4414,11 @@ public class Intent implements Parcelable, Cloneable { i = semi + 1; } - if (intent != baseIntent) { + if (inSelector) { // The Intent had a selector; fix it up. - baseIntent.setSelector(intent); + if (baseIntent.mPackage == null) { + baseIntent.setSelector(intent); + } intent = baseIntent; } @@ -4308,6 +4428,47 @@ public class Intent implements Parcelable, Cloneable { if (scheme != null) { data = scheme + ':' + data; } + } else if (data.startsWith("android-app:")) { + if (data.charAt(12) == '/' && data.charAt(13) == '/') { + // Correctly formed android-app, first part is package name. + int end = data.indexOf('/', 14); + if (end < 0) { + // All we have is a package name. + intent.mPackage = data.substring(14); + if (!explicitAction) { + intent.setAction(ACTION_MAIN); + } + data = ""; + } else { + // Target the Intent at the given package name always. + String authority = null; + intent.mPackage = data.substring(14, end); + int newEnd; + if (end < data.length() && (newEnd=data.indexOf('/', end+1)) >= 0) { + // Found a scheme, remember it. + scheme = data.substring(end+1, newEnd); + end = newEnd; + if (end < data.length() && (newEnd=data.indexOf('/', end+1)) >= 0) { + // Found a authority, remember it. + authority = data.substring(end+1, newEnd); + end = newEnd; + } + } + if (scheme == null) { + // If there was no scheme, then this just targets the package. + if (!explicitAction) { + intent.setAction(ACTION_MAIN); + } + data = ""; + } else if (authority == null) { + data = scheme + ":"; + } else { + data = scheme + "://" + authority + data.substring(end); + } + } + } else { + data = ""; + } } if (data.length() > 0) { @@ -7083,14 +7244,53 @@ public class Intent implements Parcelable, Cloneable { * <p>You can convert the returned string back to an Intent with * {@link #getIntent}. * - * @param flags Additional operating flags. Either 0 or - * {@link #URI_INTENT_SCHEME}. + * @param flags Additional operating flags. Either 0, + * {@link #URI_INTENT_SCHEME}, or {@link #URI_ANDROID_APP_SCHEME}. * * @return Returns a URI encoding URI string describing the entire contents * of the Intent. */ public String toUri(int flags) { StringBuilder uri = new StringBuilder(128); + if ((flags&URI_ANDROID_APP_SCHEME) != 0) { + if (mPackage == null) { + throw new IllegalArgumentException( + "Intent must include an explicit package name to build an android-app: " + + this); + } + uri.append("android-app://"); + uri.append(mPackage); + String scheme = null; + if (mData != null) { + scheme = mData.getScheme(); + if (scheme != null) { + uri.append('/'); + uri.append(scheme); + String authority = mData.getEncodedAuthority(); + if (authority != null) { + uri.append('/'); + uri.append(authority); + String path = mData.getEncodedPath(); + if (path != null) { + uri.append(path); + } + String queryParams = mData.getEncodedQuery(); + if (queryParams != null) { + uri.append('?'); + uri.append(queryParams); + } + String fragment = mData.getEncodedFragment(); + if (fragment != null) { + uri.append('#'); + uri.append(fragment); + } + } + } + } + toUriFragment(uri, null, scheme == null ? Intent.ACTION_MAIN : Intent.ACTION_VIEW, + mPackage, flags); + return uri.toString(); + } String scheme = null; if (mData != null) { String data = mData.toString(); @@ -7120,27 +7320,38 @@ public class Intent implements Parcelable, Cloneable { uri.append("intent:"); } - uri.append("#Intent;"); + toUriFragment(uri, scheme, Intent.ACTION_VIEW, null, flags); + + return uri.toString(); + } + + private void toUriFragment(StringBuilder uri, String scheme, String defAction, + String defPackage, int flags) { + StringBuilder frag = new StringBuilder(128); - toUriInner(uri, scheme, flags); + toUriInner(frag, scheme, defAction, defPackage, flags); if (mSelector != null) { uri.append("SEL;"); // Note that for now we are not going to try to handle the // data part; not clear how to represent this as a URI, and // not much utility in it. - mSelector.toUriInner(uri, null, flags); + mSelector.toUriInner(frag, mSelector.mData != null ? mSelector.mData.getScheme() : null, + null, null, flags); } - uri.append("end"); - - return uri.toString(); + if (frag.length() > 0) { + uri.append("#Intent;"); + uri.append(frag); + uri.append("end"); + } } - private void toUriInner(StringBuilder uri, String scheme, int flags) { + private void toUriInner(StringBuilder uri, String scheme, String defAction, + String defPackage, int flags) { if (scheme != null) { uri.append("scheme=").append(scheme).append(';'); } - if (mAction != null) { + if (mAction != null && !mAction.equals(defAction)) { uri.append("action=").append(Uri.encode(mAction)).append(';'); } if (mCategories != null) { @@ -7154,7 +7365,7 @@ public class Intent implements Parcelable, Cloneable { if (mFlags != 0) { uri.append("launchFlags=0x").append(Integer.toHexString(mFlags)).append(';'); } - if (mPackage != null) { + if (mPackage != null && !mPackage.equals(defPackage)) { uri.append("package=").append(Uri.encode(mPackage)).append(';'); } if (mComponent != null) { @@ -7498,8 +7709,10 @@ public class Intent implements Parcelable, Cloneable { */ public void prepareToEnterProcess() { if (mContentUserHint != UserHandle.USER_CURRENT) { - fixUris(mContentUserHint); - mContentUserHint = UserHandle.USER_CURRENT; + if (UserHandle.getAppId(Process.myUid()) != Process.SYSTEM_UID) { + fixUris(mContentUserHint); + mContentUserHint = UserHandle.USER_CURRENT; + } } } diff --git a/core/java/android/content/pm/IPackageManager.aidl b/core/java/android/content/pm/IPackageManager.aidl index c37534a..0dc86ad 100644 --- a/core/java/android/content/pm/IPackageManager.aidl +++ b/core/java/android/content/pm/IPackageManager.aidl @@ -436,6 +436,7 @@ interface IPackageManager { boolean isFirstBoot(); boolean isOnlyCoreApps(); + boolean isUpgrade(); void setPermissionEnforced(String permission, boolean enforced); boolean isPermissionEnforced(String permission); diff --git a/core/java/android/content/pm/LabeledIntent.aidl b/core/java/android/content/pm/LabeledIntent.aidl new file mode 100644 index 0000000..ad96759 --- /dev/null +++ b/core/java/android/content/pm/LabeledIntent.aidl @@ -0,0 +1,19 @@ +/* + * Copyright 2014, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.content.pm; + +parcelable LabeledIntent; diff --git a/core/java/android/content/pm/LauncherApps.java b/core/java/android/content/pm/LauncherApps.java index 5ee0b67..c164340 100644 --- a/core/java/android/content/pm/LauncherApps.java +++ b/core/java/android/content/pm/LauncherApps.java @@ -499,20 +499,4 @@ public class LauncherApps { obtainMessage(MSG_UNAVAILABLE, info).sendToTarget(); } } - - /** - * TODO Remove after 2014-09-22 - * @hide - */ - public void addCallback(Callback callback) { - registerCallback(callback); - } - - /** - * TODO Remove after 2014-09-22 - * @hide - */ - public void removeCallback(Callback callback) { - unregisterCallback(callback); - } } diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java index ab90b66..e9f7c50 100644 --- a/core/java/android/content/pm/PackageManager.java +++ b/core/java/android/content/pm/PackageManager.java @@ -3867,6 +3867,13 @@ public abstract class PackageManager { public abstract VerifierDeviceIdentity getVerifierDeviceIdentity(); /** + * Returns true if the device is upgrading, such as first boot after OTA. + * + * @hide + */ + public abstract boolean isUpgrade(); + + /** * Return interface that offers the ability to install, upgrade, and remove * applications on the device. */ diff --git a/core/java/android/content/res/Configuration.java b/core/java/android/content/res/Configuration.java index acdd87e..14af584 100644 --- a/core/java/android/content/res/Configuration.java +++ b/core/java/android/content/res/Configuration.java @@ -1371,7 +1371,7 @@ public final class Configuration implements Parcelable, Comparable<Configuration } } - if (!config.locale.getLanguage().isEmpty()) { + if (config.locale != null && !config.locale.getLanguage().isEmpty()) { parts.add(localeToResourceQualifier(config.locale)); } diff --git a/core/java/android/content/res/Resources.java b/core/java/android/content/res/Resources.java index 6e9efe1..0145e05 100644 --- a/core/java/android/content/res/Resources.java +++ b/core/java/android/content/res/Resources.java @@ -18,6 +18,7 @@ package android.content.res; import android.animation.Animator; import android.animation.StateListAnimator; +import android.annotation.NonNull; import android.util.Pools.SynchronizedPool; import android.view.ViewDebug; import com.android.internal.util.XmlUtils; @@ -1548,20 +1549,21 @@ public class Resources { * contents of the typed array are ultimately filled in by * {@link Resources#getValue}. * - * @param values The base set of attribute values, must be equal - * in length to {@code attrs} or {@code null}. All values - * must be of type {@link TypedValue#TYPE_ATTRIBUTE}. + * @param values The base set of attribute values, must be equal in + * length to {@code attrs}. All values must be of type + * {@link TypedValue#TYPE_ATTRIBUTE}. * @param attrs The desired attributes to be retrieved. * @return Returns a TypedArray holding an array of the attribute * values. Be sure to call {@link TypedArray#recycle()} * when done with it. * @hide */ - public TypedArray resolveAttributes(int[] values, int[] attrs) { + @NonNull + public TypedArray resolveAttributes(@NonNull int[] values, @NonNull int[] attrs) { final int len = attrs.length; - if (values != null && len != values.length) { + if (values == null || len != values.length) { throw new IllegalArgumentException( - "Base attribute values must be null or the same length as attrs"); + "Base attribute values must the same length as attrs"); } final TypedArray array = TypedArray.obtain(Resources.this, len); diff --git a/core/java/android/content/res/TypedArray.java b/core/java/android/content/res/TypedArray.java index 73b93c6..02602fb 100644 --- a/core/java/android/content/res/TypedArray.java +++ b/core/java/android/content/res/TypedArray.java @@ -807,6 +807,9 @@ public class TypedArray { /** * Determines whether there is an attribute at <var>index</var>. + * <p> + * <strong>Note:</strong> If the attribute was set to {@code @empty} or + * {@code @undefined}, this method returns {@code false}. * * @param index Index of attribute to retrieve. * @@ -824,6 +827,27 @@ public class TypedArray { } /** + * Determines whether there is an attribute at <var>index</var>, returning + * {@code true} if the attribute was explicitly set to {@code @empty} and + * {@code false} only if the attribute was undefined. + * + * @param index Index of attribute to retrieve. + * + * @return True if the attribute has a value or is empty, false otherwise. + */ + public boolean hasValueOrEmpty(int index) { + if (mRecycled) { + throw new RuntimeException("Cannot make calls to a recycled instance!"); + } + + index *= AssetManager.STYLE_NUM_ENTRIES; + final int[] data = mData; + final int type = data[index+AssetManager.STYLE_TYPE]; + return type != TypedValue.TYPE_NULL + || data[index+AssetManager.STYLE_DATA] == TypedValue.DATA_NULL_EMPTY; + } + + /** * Retrieve the raw TypedValue for the attribute at <var>index</var> * and return a temporary object holding its data. This object is only * valid until the next call on to {@link TypedArray}. diff --git a/core/java/android/hardware/display/DisplayManagerInternal.java b/core/java/android/hardware/display/DisplayManagerInternal.java index 8447dde..bb162153 100644 --- a/core/java/android/hardware/display/DisplayManagerInternal.java +++ b/core/java/android/hardware/display/DisplayManagerInternal.java @@ -172,9 +172,12 @@ public abstract class DisplayManagerInternal { // If true, enables automatic brightness control. public boolean useAutoBrightness; - //If true, scales the brightness to half of desired. + // If true, scales the brightness to half of desired. public boolean lowPowerMode; + // If true, applies a brightness boost. + public boolean boostScreenBrightness; + // If true, prevents the screen from completely turning on if it is currently off. // The display does not enter a "ready" state if this flag is true and screen on is // blocked. The window manager policy blocks screen on while it prepares the keyguard to @@ -216,6 +219,7 @@ public abstract class DisplayManagerInternal { useAutoBrightness = other.useAutoBrightness; blockScreenOn = other.blockScreenOn; lowPowerMode = other.lowPowerMode; + boostScreenBrightness = other.boostScreenBrightness; dozeScreenBrightness = other.dozeScreenBrightness; dozeScreenState = other.dozeScreenState; } @@ -235,6 +239,7 @@ public abstract class DisplayManagerInternal { && useAutoBrightness == other.useAutoBrightness && blockScreenOn == other.blockScreenOn && lowPowerMode == other.lowPowerMode + && boostScreenBrightness == other.boostScreenBrightness && dozeScreenBrightness == other.dozeScreenBrightness && dozeScreenState == other.dozeScreenState; } @@ -253,6 +258,7 @@ public abstract class DisplayManagerInternal { + ", useAutoBrightness=" + useAutoBrightness + ", blockScreenOn=" + blockScreenOn + ", lowPowerMode=" + lowPowerMode + + ", boostScreenBrightness=" + boostScreenBrightness + ", dozeScreenBrightness=" + dozeScreenBrightness + ", dozeScreenState=" + Display.stateToString(dozeScreenState); } diff --git a/core/java/android/hardware/hdmi/HdmiRecordListener.java b/core/java/android/hardware/hdmi/HdmiRecordListener.java index 29f6cfc..90b7768 100644 --- a/core/java/android/hardware/hdmi/HdmiRecordListener.java +++ b/core/java/android/hardware/hdmi/HdmiRecordListener.java @@ -39,6 +39,8 @@ public abstract class HdmiRecordListener { /** * Called when one touch record is started or failed during initialization. * + * @param recorderAddress An address of recorder that reports result of one touch record + * request * @param result result code. For more details, please look at all constants starting with * "ONE_TOUCH_RECORD_". Only * {@link HdmiControlManager#ONE_TOUCH_RECORD_RECORDING_CURRENTLY_SELECTED_SOURCE}, @@ -47,15 +49,17 @@ public abstract class HdmiRecordListener { * {@link HdmiControlManager#ONE_TOUCH_RECORD_RECORDING_EXTERNAL_INPUT} mean normal * start of recording; otherwise, describes failure. */ - public void onOneTouchRecordResult(int result) { + public void onOneTouchRecordResult(int recorderAddress, int result) { } /** * Called when timer recording is started or failed during initialization. * + * @param recorderAddress An address of recorder that reports result of timer recording + * request * @param data timer status data. For more details, look at {@link TimerStatusData}. */ - public void onTimerRecordingResult(TimerStatusData data) { + public void onTimerRecordingResult(int recorderAddress, TimerStatusData data) { } /** @@ -230,6 +234,8 @@ public abstract class HdmiRecordListener { /** * Called when receiving result for clear timer recording request. * + * @param recorderAddress An address of recorder that reports result of clear timer recording + * request * @param result result of clear timer. It should be one of * {@link HdmiControlManager#CLEAR_TIMER_STATUS_TIMER_NOT_CLEARED_RECORDING} * {@link HdmiControlManager#CLEAR_TIMER_STATUS_TIMER_NOT_CLEARED_NO_MATCHING}, @@ -239,6 +245,6 @@ public abstract class HdmiRecordListener { * {@link HdmiControlManager#CLEAR_TIMER_STATUS_FAIL_TO_CLEAR_SELECTED_SOURCE}, * {@link HdmiControlManager#CLEAR_TIMER_STATUS_CEC_DISABLE}. */ - public void onClearTimerRecordingResult(int result) { + public void onClearTimerRecordingResult(int recorderAddress, int result) { } } diff --git a/core/java/android/hardware/hdmi/HdmiTvClient.java b/core/java/android/hardware/hdmi/HdmiTvClient.java index dbfb4ef..cef17dd 100644 --- a/core/java/android/hardware/hdmi/HdmiTvClient.java +++ b/core/java/android/hardware/hdmi/HdmiTvClient.java @@ -226,19 +226,19 @@ public final class HdmiTvClient extends HdmiClient { } @Override - public void onOneTouchRecordResult(int result) { - callback.onOneTouchRecordResult(result); + public void onOneTouchRecordResult(int recorderAddress, int result) { + callback.onOneTouchRecordResult(recorderAddress, result); } @Override - public void onTimerRecordingResult(int result) { - callback.onTimerRecordingResult( + public void onTimerRecordingResult(int recorderAddress, int result) { + callback.onTimerRecordingResult(recorderAddress, HdmiRecordListener.TimerStatusData.parseFrom(result)); } @Override - public void onClearTimerRecordingResult(int result) { - callback.onClearTimerRecordingResult(result); + public void onClearTimerRecordingResult(int recorderAddress, int result) { + callback.onClearTimerRecordingResult(recorderAddress, result); } }; } diff --git a/core/java/android/hardware/hdmi/IHdmiRecordListener.aidl b/core/java/android/hardware/hdmi/IHdmiRecordListener.aidl index 44d9065..d2deb38 100644 --- a/core/java/android/hardware/hdmi/IHdmiRecordListener.aidl +++ b/core/java/android/hardware/hdmi/IHdmiRecordListener.aidl @@ -31,19 +31,25 @@ package android.hardware.hdmi; /** * Called when one touch record is started or failed during initialization. * + * @param recorderAddress An address of recorder that reports result of one touch record + * request * @param result result code for one touch record */ - void onOneTouchRecordResult(int result); + void onOneTouchRecordResult(int recorderAddress, int result); /** * Called when timer recording is started or failed during initialization. - + * + * @param recorderAddress An address of recorder that reports result of timer recording + * request * @param result result code for timer recording */ - void onTimerRecordingResult(int result); + void onTimerRecordingResult(int recorderAddress, int result); /** * Called when receiving result for clear timer recording request. * - * @param result result of clear timer. + * @param recorderAddress An address of recorder that reports result of clear timer recording + * request + * @param result result of clear timer */ - void onClearTimerRecordingResult(int result); + void onClearTimerRecordingResult(int recorderAddress, int result); }
\ No newline at end of file diff --git a/core/java/android/net/ConnectivityManager.java b/core/java/android/net/ConnectivityManager.java index 9194ca8..1c9f4c6 100644 --- a/core/java/android/net/ConnectivityManager.java +++ b/core/java/android/net/ConnectivityManager.java @@ -1012,60 +1012,57 @@ public class ConnectivityManager { return null; } + /** + * Guess what the network request was trying to say so that the resulting + * network is accessible via the legacy (deprecated) API such as + * requestRouteToHost. + * This means we should try to be fairly preceise about transport and + * capability but ignore things such as networkSpecifier. + * If the request has more than one transport or capability it doesn't + * match the old legacy requests (they selected only single transport/capability) + * so this function cannot map the request to a single legacy type and + * the resulting network will not be available to the legacy APIs. + * + * TODO - This should be removed when the legacy APIs are removed. + */ private int inferLegacyTypeForNetworkCapabilities(NetworkCapabilities netCap) { if (netCap == null) { return TYPE_NONE; } + if (!netCap.hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR)) { return TYPE_NONE; } + + String type = null; + int result = TYPE_NONE; + if (netCap.hasCapability(NetworkCapabilities.NET_CAPABILITY_CBS)) { - if (netCap.equals(networkCapabilitiesForFeature(TYPE_MOBILE, "enableCBS"))) { - return TYPE_MOBILE_CBS; - } else { - return TYPE_NONE; - } - } - if (netCap.hasCapability(NetworkCapabilities.NET_CAPABILITY_IMS)) { - if (netCap.equals(networkCapabilitiesForFeature(TYPE_MOBILE, "enableIMS"))) { - return TYPE_MOBILE_IMS; - } else { - return TYPE_NONE; - } - } - if (netCap.hasCapability(NetworkCapabilities.NET_CAPABILITY_FOTA)) { - if (netCap.equals(networkCapabilitiesForFeature(TYPE_MOBILE, "enableFOTA"))) { - return TYPE_MOBILE_FOTA; - } else { - return TYPE_NONE; - } - } - if (netCap.hasCapability(NetworkCapabilities.NET_CAPABILITY_DUN)) { - if (netCap.equals(networkCapabilitiesForFeature(TYPE_MOBILE, "enableDUN"))) { - return TYPE_MOBILE_DUN; - } else { - return TYPE_NONE; - } - } - if (netCap.hasCapability(NetworkCapabilities.NET_CAPABILITY_SUPL)) { - if (netCap.equals(networkCapabilitiesForFeature(TYPE_MOBILE, "enableSUPL"))) { - return TYPE_MOBILE_SUPL; - } else { - return TYPE_NONE; - } - } - if (netCap.hasCapability(NetworkCapabilities.NET_CAPABILITY_MMS)) { - if (netCap.equals(networkCapabilitiesForFeature(TYPE_MOBILE, "enableMMS"))) { - return TYPE_MOBILE_MMS; - } else { - return TYPE_NONE; - } - } - if (netCap.hasCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)) { - if (netCap.equals(networkCapabilitiesForFeature(TYPE_MOBILE, "enableHIPRI"))) { - return TYPE_MOBILE_HIPRI; - } else { - return TYPE_NONE; + type = "enableCBS"; + result = TYPE_MOBILE_CBS; + } else if (netCap.hasCapability(NetworkCapabilities.NET_CAPABILITY_IMS)) { + type = "enableIMS"; + result = TYPE_MOBILE_IMS; + } else if (netCap.hasCapability(NetworkCapabilities.NET_CAPABILITY_FOTA)) { + type = "enableFOTA"; + result = TYPE_MOBILE_FOTA; + } else if (netCap.hasCapability(NetworkCapabilities.NET_CAPABILITY_DUN)) { + type = "enableDUN"; + result = TYPE_MOBILE_DUN; + } else if (netCap.hasCapability(NetworkCapabilities.NET_CAPABILITY_SUPL)) { + type = "enableSUPL"; + result = TYPE_MOBILE_SUPL; + } else if (netCap.hasCapability(NetworkCapabilities.NET_CAPABILITY_MMS)) { + type = "enableMMS"; + result = TYPE_MOBILE_MMS; + } else if (netCap.hasCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)) { + type = "enableHIPRI"; + result = TYPE_MOBILE_HIPRI; + } + if (type != null) { + NetworkCapabilities testCap = networkCapabilitiesForFeature(TYPE_MOBILE, type); + if (testCap.equalsNetCapabilities(netCap) && testCap.equalsTransportTypes(netCap)) { + return result; } } return TYPE_NONE; @@ -2381,17 +2378,15 @@ public class ConnectivityManager { /** * The lookup key for a {@link Network} object included with the intent after - * succesfully finding a network for the applications request. Retrieve it with + * successfully finding a network for the applications request. Retrieve it with * {@link android.content.Intent#getParcelableExtra(String)}. - * @hide */ public static final String EXTRA_NETWORK_REQUEST_NETWORK = "networkRequestNetwork"; /** * The lookup key for a {@link NetworkRequest} object included with the intent after - * succesfully finding a network for the applications request. Retrieve it with + * successfully finding a network for the applications request. Retrieve it with * {@link android.content.Intent#getParcelableExtra(String)}. - * @hide */ public static final String EXTRA_NETWORK_REQUEST_NETWORK_REQUEST = "networkRequestNetworkRequest"; @@ -2400,7 +2395,7 @@ public class ConnectivityManager { /** * Request a network to satisfy a set of {@link NetworkCapabilities}. * - * This function behavies identically to the version that takes a NetworkCallback, but instead + * This function behaves identically to the version that takes a NetworkCallback, but instead * of {@link NetworkCallback} a {@link PendingIntent} is used. This means * the request may outlive the calling application and get called back when a suitable * network is found. @@ -2421,21 +2416,46 @@ public class ConnectivityManager { * two Intents defined by {@link Intent#filterEquals}), then it will be removed and * replaced by this one, effectively releasing the previous {@link NetworkRequest}. * <p> - * The request may be released normally by calling {@link #unregisterNetworkCallback}. + * The request may be released normally by calling + * {@link #releaseNetworkRequest(android.app.PendingIntent)}. * * @param request {@link NetworkRequest} describing this request. * @param operation Action to perform when the network is available (corresponds * to the {@link NetworkCallback#onAvailable} call. Typically - * comes from {@link PendingIntent#getBroadcast}. - * @hide + * comes from {@link PendingIntent#getBroadcast}. Cannot be null. */ public void requestNetwork(NetworkRequest request, PendingIntent operation) { + checkPendingIntent(operation); try { mService.pendingRequestForNetwork(request.networkCapabilities, operation); } catch (RemoteException e) {} } /** + * Removes a request made via {@link #requestNetwork(NetworkRequest, android.app.PendingIntent)} + * <p> + * This method has the same behavior as {@link #unregisterNetworkCallback} with respect to + * releasing network resources and disconnecting. + * + * @param operation A PendingIntent equal (as defined by {@link Intent#filterEquals}) to the + * PendingIntent passed to + * {@link #requestNetwork(NetworkRequest, android.app.PendingIntent)} with the + * corresponding NetworkRequest you'd like to remove. Cannot be null. + */ + public void releaseNetworkRequest(PendingIntent operation) { + checkPendingIntent(operation); + try { + mService.releasePendingNetworkRequest(operation); + } catch (RemoteException e) {} + } + + private void checkPendingIntent(PendingIntent intent) { + if (intent == null) { + throw new IllegalArgumentException("PendingIntent cannot be null."); + } + } + + /** * Registers to receive notifications about all networks which satisfy the given * {@link NetworkRequest}. The callbacks will continue to be called until * either the application exits or {@link #unregisterNetworkCallback} is called @@ -2451,7 +2471,7 @@ public class ConnectivityManager { /** * Unregisters callbacks about and possibly releases networks originating from * {@link #requestNetwork} and {@link #registerNetworkCallback} calls. If the - * given {@code NetworkCallback} had previosuly been used with {@code #requestNetwork}, + * given {@code NetworkCallback} had previously been used with {@code #requestNetwork}, * any networks that had been connected to only to satisfy that request will be * disconnected. * diff --git a/core/java/android/net/DhcpResults.java b/core/java/android/net/DhcpResults.java index 71df60a..6159e1e 100644 --- a/core/java/android/net/DhcpResults.java +++ b/core/java/android/net/DhcpResults.java @@ -200,7 +200,7 @@ public class DhcpResults extends StaticIpConfiguration { vendorInfo = info; } - public void setDomains(String domains) { - domains = domains; + public void setDomains(String newDomains) { + domains = newDomains; } } diff --git a/core/java/android/net/IConnectivityManager.aidl b/core/java/android/net/IConnectivityManager.aidl index a983d88..a7bbc53 100644 --- a/core/java/android/net/IConnectivityManager.aidl +++ b/core/java/android/net/IConnectivityManager.aidl @@ -156,6 +156,8 @@ interface IConnectivityManager NetworkRequest pendingRequestForNetwork(in NetworkCapabilities networkCapabilities, in PendingIntent operation); + void releasePendingNetworkRequest(in PendingIntent operation); + NetworkRequest listenForNetwork(in NetworkCapabilities networkCapabilities, in Messenger messenger, in IBinder binder); diff --git a/core/java/android/net/LinkAddress.java b/core/java/android/net/LinkAddress.java index c387055..384ab1c 100644 --- a/core/java/android/net/LinkAddress.java +++ b/core/java/android/net/LinkAddress.java @@ -21,12 +21,14 @@ import android.os.Parcelable; import android.util.Pair; import java.net.Inet4Address; +import java.net.Inet6Address; import java.net.InetAddress; import java.net.InterfaceAddress; import java.net.UnknownHostException; import static android.system.OsConstants.IFA_F_DADFAILED; import static android.system.OsConstants.IFA_F_DEPRECATED; +import static android.system.OsConstants.IFA_F_OPTIMISTIC; import static android.system.OsConstants.IFA_F_TENTATIVE; import static android.system.OsConstants.RT_SCOPE_HOST; import static android.system.OsConstants.RT_SCOPE_LINK; @@ -93,6 +95,20 @@ public class LinkAddress implements Parcelable { } /** + * Utility function to check if |address| is a Unique Local IPv6 Unicast Address + * (a.k.a. "ULA"; RFC 4193). + * + * Per RFC 4193 section 8, fc00::/7 identifies these addresses. + */ + private boolean isIPv6ULA() { + if (address != null && address instanceof Inet6Address) { + byte[] bytes = address.getAddress(); + return ((bytes[0] & (byte)0xfc) == (byte)0xfc); + } + return false; + } + + /** * Utility function for the constructors. */ private void init(InetAddress address, int prefixLength, int flags, int scope) { @@ -268,8 +284,16 @@ public class LinkAddress implements Parcelable { * @hide */ public boolean isGlobalPreferred() { + /** + * Note that addresses flagged as IFA_F_OPTIMISTIC are + * simultaneously flagged as IFA_F_TENTATIVE (when the tentative + * state has cleared either DAD has succeeded or failed, and both + * flags are cleared regardless). + */ return (scope == RT_SCOPE_UNIVERSE && - (flags & (IFA_F_DADFAILED | IFA_F_DEPRECATED | IFA_F_TENTATIVE)) == 0L); + !isIPv6ULA() && + (flags & (IFA_F_DADFAILED | IFA_F_DEPRECATED)) == 0L && + ((flags & IFA_F_TENTATIVE) == 0L || (flags & IFA_F_OPTIMISTIC) != 0L)); } /** diff --git a/core/java/android/net/NetworkCapabilities.java b/core/java/android/net/NetworkCapabilities.java index 1efe478..ce7ad65 100644 --- a/core/java/android/net/NetworkCapabilities.java +++ b/core/java/android/net/NetworkCapabilities.java @@ -235,7 +235,8 @@ public final class NetworkCapabilities implements Parcelable { return ((nc.mNetworkCapabilities & this.mNetworkCapabilities) == this.mNetworkCapabilities); } - private boolean equalsNetCapabilities(NetworkCapabilities nc) { + /** @hide */ + public boolean equalsNetCapabilities(NetworkCapabilities nc) { return (nc.mNetworkCapabilities == this.mNetworkCapabilities); } @@ -344,7 +345,8 @@ public final class NetworkCapabilities implements Parcelable { return ((this.mTransportTypes == 0) || ((this.mTransportTypes & nc.mTransportTypes) != 0)); } - private boolean equalsTransportTypes(NetworkCapabilities nc) { + /** @hide */ + public boolean equalsTransportTypes(NetworkCapabilities nc) { return (nc.mTransportTypes == this.mTransportTypes); } diff --git a/core/java/android/net/NetworkScoreManager.java b/core/java/android/net/NetworkScoreManager.java index 3f68a44..a939cce 100644 --- a/core/java/android/net/NetworkScoreManager.java +++ b/core/java/android/net/NetworkScoreManager.java @@ -41,10 +41,10 @@ import android.os.UserHandle; * <ul> * <li>Declares the {@link android.Manifest.permission#SCORE_NETWORKS} permission. * <li>Includes a receiver for {@link #ACTION_SCORE_NETWORKS} guarded by the - * {@link android.Manifest.permission#BROADCAST_SCORE_NETWORKS} permission which scores networks - * and (eventually) calls {@link #updateScores} with the results. If this receiver specifies an - * android:label attribute, this label will be used when referring to the application throughout - * system settings; otherwise, the application label will be used. + * {@link android.Manifest.permission#BROADCAST_NETWORK_PRIVILEGED} permission which scores + * networks and (eventually) calls {@link #updateScores} with the results. If this receiver + * specifies an android:label attribute, this label will be used when referring to the + * application throughout system settings; otherwise, the application label will be used. * </ul> * * <p>The system keeps track of an active scorer application; at any time, only this application @@ -192,12 +192,15 @@ public class NetworkScoreManager { /** * Set the active scorer to a new package and clear existing scores. * + * <p>Should never be called directly without obtaining user consent. This can be done by using + * the {@link #ACTION_CHANGE_ACTIVE} broadcast, or using a custom configuration activity. + * * @return true if the operation succeeded, or false if the new package is not a valid scorer. * @throws SecurityException if the caller does not hold the - * {@link android.Manifest.permission#BROADCAST_SCORE_NETWORKS} permission indicating - * that it can manage scorer applications. + * {@link android.Manifest.permission#SCORE_NETWORKS} permission. * @hide */ + @SystemApi public boolean setActiveScorer(String packageName) throws SecurityException { try { return mService.setActiveScorer(packageName); @@ -228,7 +231,7 @@ public class NetworkScoreManager { * * @return true if the broadcast was sent, or false if there is no active scorer. * @throws SecurityException if the caller does not hold the - * {@link android.Manifest.permission#BROADCAST_SCORE_NETWORKS} permission. + * {@link android.Manifest.permission#BROADCAST_NETWORK_PRIVILEGED} permission. * @hide */ public boolean requestScores(NetworkKey[] networks) throws SecurityException { @@ -252,7 +255,7 @@ public class NetworkScoreManager { * @param networkType the type of network this cache can handle. See {@link NetworkKey#type}. * @param scoreCache implementation of {@link INetworkScoreCache} to store the scores. * @throws SecurityException if the caller does not hold the - * {@link android.Manifest.permission#BROADCAST_SCORE_NETWORKS} permission. + * {@link android.Manifest.permission#BROADCAST_NETWORK_PRIVILEGED} permission. * @throws IllegalArgumentException if a score cache is already registered for this type. * @hide */ diff --git a/core/java/android/net/NetworkScorerAppManager.java b/core/java/android/net/NetworkScorerAppManager.java index c33f5ec..46f7194 100644 --- a/core/java/android/net/NetworkScorerAppManager.java +++ b/core/java/android/net/NetworkScorerAppManager.java @@ -79,7 +79,7 @@ public final class NetworkScorerAppManager { * <ul> * <li>Declares the {@link android.Manifest.permission#SCORE_NETWORKS} permission. * <li>Includes a receiver for {@link NetworkScoreManager#ACTION_SCORE_NETWORKS} guarded by the - * {@link android.Manifest.permission#BROADCAST_SCORE_NETWORKS} permission. + * {@link android.Manifest.permission#BROADCAST_NETWORK_PRIVILEGED} permission. * </ul> * * @return the list of scorers, or the empty list if there are no valid scorers. @@ -98,8 +98,8 @@ public final class NetworkScorerAppManager { // Should never happen with queryBroadcastReceivers, but invalid nonetheless. continue; } - if (!permission.BROADCAST_SCORE_NETWORKS.equals(receiverInfo.permission)) { - // Receiver doesn't require the BROADCAST_SCORE_NETWORKS permission, which means + if (!permission.BROADCAST_NETWORK_PRIVILEGED.equals(receiverInfo.permission)) { + // Receiver doesn't require the BROADCAST_NETWORK_PRIVILEGED permission, which means // anyone could trigger network scoring and flood the framework with score requests. continue; } diff --git a/core/java/android/net/PskKeyManager.java b/core/java/android/net/PskKeyManager.java index d162282..f82e635 100644 --- a/core/java/android/net/PskKeyManager.java +++ b/core/java/android/net/PskKeyManager.java @@ -81,6 +81,13 @@ import javax.net.ssl.SSLEngine; * Subclasses should normally provide their own implementation of {@code getKey} because the default * implementation returns no key, which aborts the handshake. * + * <h3>Known issues</h3> + * The implementation of {@code ECDHE_PSK} cipher suites in API Level 21 contains a bug which breaks + * compatibility with other implementations. {@code ECDHE_PSK} cipher suites are enabled by default + * on platforms with API Level 21 when an {@code SSLContext} is initialized with a + * {@code PskKeyManager}. A workaround is to disable {@code ECDHE_PSK} cipher suites on platforms + * with API Level 21. + * * <h3>Example</h3> * The following example illustrates how to create an {@code SSLContext} which enables the use of * TLS-PSK in {@code SSLSocket}, {@code SSLServerSocket} and {@code SSLEngine} instances obtained diff --git a/core/java/android/net/RssiCurve.java b/core/java/android/net/RssiCurve.java index f653f37..8ebe9e8 100644 --- a/core/java/android/net/RssiCurve.java +++ b/core/java/android/net/RssiCurve.java @@ -27,8 +27,8 @@ import java.util.Objects; * A curve defining the network score over a range of RSSI values. * * <p>For each RSSI bucket, the score may be any byte. Scores have no absolute meaning and are only - * considered relative to other scores assigned by the same scorer. Networks with no score are all - * considered equivalent and ranked below any network with a score. + * considered relative to other scores assigned by the same scorer. Networks with no score are + * treated equivalently to a network with score {@link Byte#MIN_VALUE}, and will not be used. * * <p>For example, consider a curve starting at -110 dBm with a bucket width of 10 and the * following buckets: {@code [-20, -10, 0, 10, 20, 30, 40, 50, 60, 70, 80, 90, 100, 110, 120]}. @@ -52,6 +52,7 @@ import java.util.Objects; */ @SystemApi public class RssiCurve implements Parcelable { + private static final int DEFAULT_ACTIVE_NETWORK_RSSI_BOOST = 25; /** The starting dBm of the curve. */ public final int start; @@ -63,6 +64,15 @@ public class RssiCurve implements Parcelable { public final byte[] rssiBuckets; /** + * The RSSI boost to give this network when active, in dBm. + * + * <p>When the system is connected to this network, it will pretend that the network has this + * much higher of an RSSI. This is to avoid switching networks when another network has only a + * slightly higher score. + */ + public final int activeNetworkRssiBoost; + + /** * Construct a new {@link RssiCurve}. * * @param start the starting dBm of the curve. @@ -70,12 +80,25 @@ public class RssiCurve implements Parcelable { * @param rssiBuckets the score for each RSSI bucket. */ public RssiCurve(int start, int bucketWidth, byte[] rssiBuckets) { + this(start, bucketWidth, rssiBuckets, DEFAULT_ACTIVE_NETWORK_RSSI_BOOST); + } + + /** + * Construct a new {@link RssiCurve}. + * + * @param start the starting dBm of the curve. + * @param bucketWidth the width of each RSSI bucket, in dBm. + * @param rssiBuckets the score for each RSSI bucket. + * @param activeNetworkRssiBoost the RSSI boost to apply when this network is active, in dBm. + */ + public RssiCurve(int start, int bucketWidth, byte[] rssiBuckets, int activeNetworkRssiBoost) { this.start = start; this.bucketWidth = bucketWidth; if (rssiBuckets == null || rssiBuckets.length == 0) { throw new IllegalArgumentException("rssiBuckets must be at least one element large."); } this.rssiBuckets = rssiBuckets; + this.activeNetworkRssiBoost = activeNetworkRssiBoost; } private RssiCurve(Parcel in) { @@ -84,6 +107,7 @@ public class RssiCurve implements Parcelable { int bucketCount = in.readInt(); rssiBuckets = new byte[bucketCount]; in.readByteArray(rssiBuckets); + activeNetworkRssiBoost = in.readInt(); } @Override @@ -97,6 +121,7 @@ public class RssiCurve implements Parcelable { out.writeInt(bucketWidth); out.writeInt(rssiBuckets.length); out.writeByteArray(rssiBuckets); + out.writeInt(activeNetworkRssiBoost); } /** @@ -108,6 +133,23 @@ public class RssiCurve implements Parcelable { * @return the score for the given RSSI. */ public byte lookupScore(int rssi) { + return lookupScore(rssi, false /* isActiveNetwork */); + } + + /** + * Lookup the score for a given RSSI value. + * + * @param rssi The RSSI to lookup. If the RSSI falls below the start of the curve, the score at + * the start of the curve will be returned. If it falls after the end of the curve, the + * score at the end of the curve will be returned. + * @param isActiveNetwork Whether this network is currently active. + * @return the score for the given RSSI. + */ + public byte lookupScore(int rssi, boolean isActiveNetwork) { + if (isActiveNetwork) { + rssi += activeNetworkRssiBoost; + } + int index = (rssi - start) / bucketWidth; // Snap the index to the closest bucket if it falls outside the curve. @@ -136,12 +178,13 @@ public class RssiCurve implements Parcelable { return start == rssiCurve.start && bucketWidth == rssiCurve.bucketWidth && - Arrays.equals(rssiBuckets, rssiCurve.rssiBuckets); + Arrays.equals(rssiBuckets, rssiCurve.rssiBuckets) && + activeNetworkRssiBoost == rssiCurve.activeNetworkRssiBoost; } @Override public int hashCode() { - return Objects.hash(start, bucketWidth, rssiBuckets); + return Objects.hash(start, bucketWidth, rssiBuckets, activeNetworkRssiBoost); } @Override @@ -150,7 +193,9 @@ public class RssiCurve implements Parcelable { sb.append("RssiCurve[start=") .append(start) .append(",bucketWidth=") - .append(bucketWidth); + .append(bucketWidth) + .append(",activeNetworkRssiBoost=") + .append(activeNetworkRssiBoost); sb.append(",buckets="); for (int i = 0; i < rssiBuckets.length; i++) { diff --git a/core/java/android/net/StaticIpConfiguration.java b/core/java/android/net/StaticIpConfiguration.java index 5a273cf..598a503 100644 --- a/core/java/android/net/StaticIpConfiguration.java +++ b/core/java/android/net/StaticIpConfiguration.java @@ -107,6 +107,7 @@ public class StaticIpConfiguration implements Parcelable { for (InetAddress dns : dnsServers) { lp.addDnsServer(dns); } + lp.setDomains(domains); return lp; } diff --git a/core/java/android/os/Debug.java b/core/java/android/os/Debug.java index 084ca30..3f42d25 100644 --- a/core/java/android/os/Debug.java +++ b/core/java/android/os/Debug.java @@ -165,7 +165,7 @@ public final class Debug public int otherSwappedOut; /** @hide */ - public static final int NUM_OTHER_STATS = 16; + public static final int NUM_OTHER_STATS = 17; /** @hide */ public static final int NUM_DVK_STATS = 5; @@ -296,23 +296,24 @@ public final class Debug case 1: return "Stack"; case 2: return "Cursor"; case 3: return "Ashmem"; - case 4: return "Other dev"; - case 5: return ".so mmap"; - case 6: return ".jar mmap"; - case 7: return ".apk mmap"; - case 8: return ".ttf mmap"; - case 9: return ".dex mmap"; - case 10: return "code mmap"; - case 11: return "image mmap"; - case 12: return "Other mmap"; - case 13: return "Graphics"; - case 14: return "GL"; - case 15: return "Memtrack"; - case 16: return ".Heap"; - case 17: return ".LOS"; - case 18: return ".LinearAlloc"; - case 19: return ".GC"; - case 20: return ".JITCache"; + case 4: return "Gfx driver"; + case 5: return "Other dev"; + case 6: return ".so mmap"; + case 7: return ".jar mmap"; + case 8: return ".apk mmap"; + case 9: return ".ttf mmap"; + case 10: return ".dex mmap"; + case 11: return ".oat mmap"; + case 12: return ".art mmap"; + case 13: return "Other mmap"; + case 14: return "Graphics"; + case 15: return "GL"; + case 16: return "Memtrack"; + case 17: return ".Heap"; + case 18: return ".LOS"; + case 19: return ".LinearAlloc"; + case 20: return ".GC"; + case 21: return ".JITCache"; default: return "????"; } } diff --git a/core/java/android/os/INetworkManagementService.aidl b/core/java/android/os/INetworkManagementService.aidl index 16250c7..5d5d2b3 100644 --- a/core/java/android/os/INetworkManagementService.aidl +++ b/core/java/android/os/INetworkManagementService.aidl @@ -92,6 +92,11 @@ interface INetworkManagementService void enableIpv6(String iface); /** + * Enables or enables IPv6 ND offload. + */ + void setInterfaceIpv6NdOffload(String iface, boolean enable); + + /** * Retrieves the network routes currently configured on the specified * interface */ @@ -336,19 +341,19 @@ interface INetworkManagementService void removeVpnUidRanges(int netId, in UidRange[] ranges); /** - * Start the clatd (464xlat) service + * Start the clatd (464xlat) service on the given interface. */ void startClatd(String interfaceName); /** - * Stop the clatd (464xlat) service + * Stop the clatd (464xlat) service on the given interface. */ - void stopClatd(); + void stopClatd(String interfaceName); /** - * Determine whether the clatd (464xlat) service has been started + * Determine whether the clatd (464xlat) service has been started on the given interface. */ - boolean isClatdStarted(); + boolean isClatdStarted(String interfaceName); /** * Start listening for mobile activity state changes. diff --git a/core/java/android/os/IPowerManager.aidl b/core/java/android/os/IPowerManager.aidl index 182dbee..16dac7d 100644 --- a/core/java/android/os/IPowerManager.aidl +++ b/core/java/android/os/IPowerManager.aidl @@ -49,7 +49,7 @@ interface IPowerManager void crash(String message); void setStayOnSetting(int val); - void setMaximumScreenOffTimeoutFromDeviceAdmin(int timeMs); + void boostScreenBrightness(long time); // temporarily overrides the screen brightness settings to allow the user to // see the effect of a settings change without applying it immediately diff --git a/core/java/android/os/PowerManager.java b/core/java/android/os/PowerManager.java index 3b6ce53..8307d9b 100644 --- a/core/java/android/os/PowerManager.java +++ b/core/java/android/os/PowerManager.java @@ -669,6 +669,28 @@ public final class PowerManager { } /** + * Boosts the brightness of the screen to maximum for a predetermined + * period of time. This is used to make the screen more readable in bright + * daylight for a short duration. + * <p> + * Requires the {@link android.Manifest.permission#DEVICE_POWER} permission. + * </p> + * + * @param time The time when the request to boost was issued, in the + * {@link SystemClock#uptimeMillis()} time base. This timestamp is used to correctly + * order the boost request with other power management functions. It should be set + * to the timestamp of the input event that caused the request to boost. + * + * @hide Requires signature permission. + */ + public void boostScreenBrightness(long time) { + try { + mService.boostScreenBrightness(time); + } catch (RemoteException e) { + } + } + + /** * Sets the brightness of the backlights (screen, keyboard, button). * <p> * Requires the {@link android.Manifest.permission#DEVICE_POWER} permission. diff --git a/core/java/android/os/PowerManagerInternal.java b/core/java/android/os/PowerManagerInternal.java index 14f4a83..9d78360 100644 --- a/core/java/android/os/PowerManagerInternal.java +++ b/core/java/android/os/PowerManagerInternal.java @@ -56,6 +56,13 @@ public abstract class PowerManagerInternal { public abstract void setUserActivityTimeoutOverrideFromWindowManager(long timeoutMillis); /** + * Used by device administration to set the maximum screen off timeout. + * + * This method must only be called by the device administration policy manager. + */ + public abstract void setMaximumScreenOffTimeoutFromDeviceAdmin(int timeMs); + + /** * Used by the dream manager to override certain properties while dozing. * * @param screenState The overridden screen state, or {@link Display.STATE_UNKNOWN} diff --git a/core/java/android/os/UserManager.java b/core/java/android/os/UserManager.java index 3234e77..bd6eeea 100644 --- a/core/java/android/os/UserManager.java +++ b/core/java/android/os/UserManager.java @@ -367,10 +367,27 @@ public class UserManager { * <p/>Type: Boolean * @see #setUserRestrictions(Bundle) * @see #getUserRestrictions() - * @hide */ public static final String DISALLOW_OUTGOING_BEAM = "no_outgoing_beam"; + /** + * Application restriction key that is used to indicate the pending arrival + * of real restrictions for the app. + * + * <p> + * Applications that support restrictions should check for the presence of this key. + * A <code>true</code> value indicates that restrictions may be applied in the near + * future but are not available yet. It is the responsibility of any + * management application that sets this flag to update it when the final + * restrictions are enforced. + * + * <p/>Key for application restrictions. + * <p/>Type: Boolean + * @see android.app.admin.DevicePolicyManager#addApplicationRestriction() + * @see android.app.admin.DevicePolicyManager#getApplicationRestriction() + */ + public static final String KEY_RESTRICTIONS_PENDING = "restrictions_pending"; + /** @hide */ public static final int PIN_VERIFICATION_FAILED_INCORRECT = -3; /** @hide */ diff --git a/core/java/android/print/IPrintDocumentAdapter.aidl b/core/java/android/print/IPrintDocumentAdapter.aidl index 9d384fb..8f33e0b 100644 --- a/core/java/android/print/IPrintDocumentAdapter.aidl +++ b/core/java/android/print/IPrintDocumentAdapter.aidl @@ -37,4 +37,5 @@ oneway interface IPrintDocumentAdapter { void write(in PageRange[] pages, in ParcelFileDescriptor fd, IWriteResultCallback callback, int sequence); void finish(); + void kill(String reason); } diff --git a/core/java/android/print/PrintManager.java b/core/java/android/print/PrintManager.java index bf8ac65..3fb812e 100644 --- a/core/java/android/print/PrintManager.java +++ b/core/java/android/print/PrintManager.java @@ -634,6 +634,17 @@ public final class PrintManager { } @Override + public void kill(String reason) { + synchronized (mLock) { + // If destroyed the handler is null. + if (!isDestroyedLocked()) { + mHandler.obtainMessage(MyHandler.MSG_ON_KILL, + reason).sendToTarget(); + } + } + } + + @Override public void onActivityPaused(Activity activity) { /* do nothing */ } @@ -719,6 +730,7 @@ public final class PrintManager { public static final int MSG_ON_LAYOUT = 2; public static final int MSG_ON_WRITE = 3; public static final int MSG_ON_FINISH = 4; + public static final int MSG_ON_KILL = 5; public MyHandler(Looper looper) { super(looper, null, true); @@ -794,6 +806,15 @@ public final class PrintManager { } } break; + case MSG_ON_KILL: { + if (DEBUG) { + Log.i(LOG_TAG, "onKill()"); + } + + String reason = (String) message.obj; + throw new RuntimeException(reason); + } + default: { throw new IllegalArgumentException("Unknown message: " + message.what); diff --git a/core/java/android/provider/CallLog.java b/core/java/android/provider/CallLog.java index 3e80ed0..3ec45e9 100644 --- a/core/java/android/provider/CallLog.java +++ b/core/java/android/provider/CallLog.java @@ -24,13 +24,17 @@ import android.content.Context; import android.content.Intent; import android.content.pm.UserInfo; import android.database.Cursor; +import android.location.Country; +import android.location.CountryDetector; import android.net.Uri; import android.os.UserHandle; import android.os.UserManager; import android.provider.ContactsContract.CommonDataKinds.Callable; import android.provider.ContactsContract.CommonDataKinds.Phone; +import android.provider.ContactsContract.Data; import android.provider.ContactsContract.DataUsageFeedback; import android.telecom.PhoneAccountHandle; +import android.telephony.PhoneNumberUtils; import android.text.TextUtils; import com.android.internal.telephony.CallerInfo; @@ -404,7 +408,6 @@ public class CallLog { * @param accountHandle The accountHandle object identifying the provider of the call * @param start time stamp for the call in milliseconds * @param duration call duration in seconds - * @param subId the subscription id. * @param dataUsage data usage for the call in bytes, null if data usage was not tracked for * the call. * @param addForAllUsers If true, the call is added to the call log of all currently @@ -503,12 +506,13 @@ public class CallLog { if (cursor != null) { try { if (cursor.getCount() > 0 && cursor.moveToFirst()) { - final Uri feedbackUri = DataUsageFeedback.FEEDBACK_URI.buildUpon() - .appendPath(cursor.getString(0)) - .appendQueryParameter(DataUsageFeedback.USAGE_TYPE, - DataUsageFeedback.USAGE_TYPE_CALL) - .build(); - resolver.update(feedbackUri, new ContentValues(), null, null); + final String dataId = cursor.getString(0); + updateDataUsageStatForData(resolver, dataId); + if (duration >= MIN_DURATION_FOR_NORMALIZED_NUMBER_UPDATE_MS + && callType == Calls.OUTGOING_TYPE + && TextUtils.isEmpty(ci.normalizedNumber)) { + updateNormalizedNumber(context, resolver, dataId, number); + } } } finally { cursor.close(); @@ -581,5 +585,50 @@ public class CallLog { + " LIMIT -1 OFFSET 500)", null); return result; } + + private static void updateDataUsageStatForData(ContentResolver resolver, String dataId) { + final Uri feedbackUri = DataUsageFeedback.FEEDBACK_URI.buildUpon() + .appendPath(dataId) + .appendQueryParameter(DataUsageFeedback.USAGE_TYPE, + DataUsageFeedback.USAGE_TYPE_CALL) + .build(); + resolver.update(feedbackUri, new ContentValues(), null, null); + } + + /* + * Update the normalized phone number for the given dataId in the ContactsProvider, based + * on the user's current country. + */ + private static void updateNormalizedNumber(Context context, ContentResolver resolver, + String dataId, String number) { + if (TextUtils.isEmpty(number) || TextUtils.isEmpty(dataId)) { + return; + } + final String countryIso = getCurrentCountryIso(context); + if (TextUtils.isEmpty(countryIso)) { + return; + } + final String normalizedNumber = PhoneNumberUtils.formatNumberToE164(number, + getCurrentCountryIso(context)); + if (TextUtils.isEmpty(normalizedNumber)) { + return; + } + final ContentValues values = new ContentValues(); + values.put(Phone.NORMALIZED_NUMBER, normalizedNumber); + resolver.update(Data.CONTENT_URI, values, Data._ID + "=?", new String[] {dataId}); + } + + private static String getCurrentCountryIso(Context context) { + String countryIso = null; + final CountryDetector detector = (CountryDetector) context.getSystemService( + Context.COUNTRY_DETECTOR); + if (detector != null) { + final Country country = detector.detectCountry(); + if (country != null) { + countryIso = country.getCountryIso(); + } + } + return countryIso; + } } } diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java index 4eeb852..1f45f0a 100644 --- a/core/java/android/provider/Settings.java +++ b/core/java/android/provider/Settings.java @@ -787,11 +787,10 @@ public final class Settings { * <p> * Output: Nothing. * @see android.service.notification.NotificationListenerService - * @hide */ @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION) public static final String ACTION_NOTIFICATION_LISTENER_SETTINGS - = "android.settings.NOTIFICATION_LISTENER_SETTINGS"; + = "android.settings.ACTION_NOTIFICATION_LISTENER_SETTINGS"; /** * @hide @@ -873,8 +872,9 @@ public final class Settings { /** * Activity Action: Show battery saver settings. - * - * @hide + * <p> + * In some cases, a matching Activity may not exist, so ensure you safeguard + * against this. */ @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION) public static final String ACTION_BATTERY_SAVER_SETTINGS @@ -2625,12 +2625,6 @@ public final class Settings { public static final String LOCK_TO_APP_ENABLED = "lock_to_app_enabled"; /** - * Whether lock-to-app will lock the keyguard when exiting. - * @hide - */ - public static final String LOCK_TO_APP_EXIT_LOCKED = "lock_to_app_exit_locked"; - - /** * I am the lolrus. * <p> * Nonzero values indicate that the user has a bukkit. @@ -2704,6 +2698,7 @@ public final class Settings { POINTER_SPEED, VIBRATE_WHEN_RINGING, RINGTONE, + LOCK_TO_APP_ENABLED, NOTIFICATION_SOUND }; @@ -3666,6 +3661,12 @@ public final class Settings { "lock_biometric_weak_flags"; /** + * Whether lock-to-app will lock the keyguard when exiting. + * @hide + */ + public static final String LOCK_TO_APP_EXIT_LOCKED = "lock_to_app_exit_locked"; + + /** * Whether autolock is enabled (0 = false, 1 = true) */ public static final String LOCK_PATTERN_ENABLED = "lock_pattern_autolock"; @@ -4587,13 +4588,6 @@ public final class Settings { public static final String ANR_SHOW_BACKGROUND = "anr_show_background"; /** - * (Experimental). If nonzero, WebView uses data reduction proxy to save network - * bandwidth. Otherwise, WebView does not use data reduction proxy. - * @hide - */ - public static final String WEBVIEW_DATA_REDUCTION_PROXY = "webview_data_reduction_proxy"; - - /** * The {@link ComponentName} string of the service to be used as the voice recognition * service. * @@ -4738,8 +4732,8 @@ public final class Settings { public static final String SMS_DEFAULT_APPLICATION = "sms_default_application"; /** - * Name of a package that the current user has explicitly allowed to see all of that - * user's notifications. + * Names of the packages that the current user has explicitly allowed to + * see all of the user's notifications, separated by ':'. * * @hide */ @@ -6637,7 +6631,8 @@ public final class Settings { WIFI_NUM_OPEN_NETWORKS_KEPT, EMERGENCY_TONE, CALL_AUTO_RETRY, - DOCK_AUDIO_MEDIA_ENABLED + DOCK_AUDIO_MEDIA_ENABLED, + LOW_POWER_MODE_TRIGGER_LEVEL }; // Populated lazily, guarded by class object: diff --git a/core/java/android/service/notification/ZenModeConfig.java b/core/java/android/service/notification/ZenModeConfig.java index 9a84a1e..36401eb 100644 --- a/core/java/android/service/notification/ZenModeConfig.java +++ b/core/java/android/service/notification/ZenModeConfig.java @@ -78,6 +78,7 @@ public class ZenModeConfig implements Parcelable { private static final String ALLOW_ATT_EVENTS = "events"; private static final String SLEEP_TAG = "sleep"; private static final String SLEEP_ATT_MODE = "mode"; + private static final String SLEEP_ATT_NONE = "none"; private static final String SLEEP_ATT_START_HR = "startHour"; private static final String SLEEP_ATT_START_MIN = "startMin"; @@ -107,6 +108,7 @@ public class ZenModeConfig implements Parcelable { public int sleepStartMinute; // 0-59 public int sleepEndHour; public int sleepEndMinute; + public boolean sleepNone; // false = priority, true = none public ComponentName[] conditionComponents; public Uri[] conditionIds; public Condition exitCondition; @@ -125,6 +127,7 @@ public class ZenModeConfig implements Parcelable { sleepStartMinute = source.readInt(); sleepEndHour = source.readInt(); sleepEndMinute = source.readInt(); + sleepNone = source.readInt() == 1; int len = source.readInt(); if (len > 0) { conditionComponents = new ComponentName[len]; @@ -155,6 +158,7 @@ public class ZenModeConfig implements Parcelable { dest.writeInt(sleepStartMinute); dest.writeInt(sleepEndHour); dest.writeInt(sleepEndMinute); + dest.writeInt(sleepNone ? 1 : 0); if (conditionComponents != null && conditionComponents.length > 0) { dest.writeInt(conditionComponents.length); dest.writeTypedArray(conditionComponents, 0); @@ -182,6 +186,7 @@ public class ZenModeConfig implements Parcelable { .append(",sleepMode=").append(sleepMode) .append(",sleepStart=").append(sleepStartHour).append('.').append(sleepStartMinute) .append(",sleepEnd=").append(sleepEndHour).append('.').append(sleepEndMinute) + .append(",sleepNone=").append(sleepNone) .append(",conditionComponents=") .append(conditionComponents == null ? null : TextUtils.join(",", conditionComponents)) .append(",conditionIds=") @@ -214,6 +219,7 @@ public class ZenModeConfig implements Parcelable { && other.allowFrom == allowFrom && other.allowEvents == allowEvents && Objects.equals(other.sleepMode, sleepMode) + && other.sleepNone == sleepNone && other.sleepStartHour == sleepStartHour && other.sleepStartMinute == sleepStartMinute && other.sleepEndHour == sleepEndHour @@ -226,7 +232,7 @@ public class ZenModeConfig implements Parcelable { @Override public int hashCode() { - return Objects.hash(allowCalls, allowMessages, allowFrom, allowEvents, sleepMode, + return Objects.hash(allowCalls, allowMessages, allowFrom, allowEvents, sleepMode, sleepNone, sleepStartHour, sleepStartMinute, sleepEndHour, sleepEndMinute, Arrays.hashCode(conditionComponents), Arrays.hashCode(conditionIds), exitCondition, exitConditionComponent); @@ -302,6 +308,7 @@ public class ZenModeConfig implements Parcelable { } else if (SLEEP_TAG.equals(tag)) { final String mode = parser.getAttributeValue(null, SLEEP_ATT_MODE); rt.sleepMode = isValidSleepMode(mode)? mode : null; + rt.sleepNone = safeBoolean(parser, SLEEP_ATT_NONE, false); final int startHour = safeInt(parser, SLEEP_ATT_START_HR, 0); final int startMinute = safeInt(parser, SLEEP_ATT_START_MIN, 0); final int endHour = safeInt(parser, SLEEP_ATT_END_HR, 0); @@ -345,6 +352,7 @@ public class ZenModeConfig implements Parcelable { if (sleepMode != null) { out.attribute(null, SLEEP_ATT_MODE, sleepMode); } + out.attribute(null, SLEEP_ATT_NONE, Boolean.toString(sleepNone)); out.attribute(null, SLEEP_ATT_START_HR, Integer.toString(sleepStartHour)); out.attribute(null, SLEEP_ATT_START_MIN, Integer.toString(sleepStartMinute)); out.attribute(null, SLEEP_ATT_END_HR, Integer.toString(sleepEndHour)); @@ -498,7 +506,7 @@ public class ZenModeConfig implements Parcelable { } // For built-in conditions - private static final String SYSTEM_AUTHORITY = "android"; + public static final String SYSTEM_AUTHORITY = "android"; // Built-in countdown conditions, e.g. condition://android/countdown/1399917958951 private static final String COUNTDOWN_PATH = "countdown"; diff --git a/core/java/android/service/trust/ITrustAgentService.aidl b/core/java/android/service/trust/ITrustAgentService.aidl index bd80a3f..bb0c2b2 100644 --- a/core/java/android/service/trust/ITrustAgentService.aidl +++ b/core/java/android/service/trust/ITrustAgentService.aidl @@ -15,7 +15,7 @@ */ package android.service.trust; -import android.os.Bundle; +import android.os.PersistableBundle; import android.service.trust.ITrustAgentServiceCallback; /** @@ -25,6 +25,6 @@ import android.service.trust.ITrustAgentServiceCallback; interface ITrustAgentService { oneway void onUnlockAttempt(boolean successful); oneway void onTrustTimeout(); + oneway void onConfigure(in List<PersistableBundle> options, IBinder token); oneway void setCallback(ITrustAgentServiceCallback callback); - oneway void setTrustAgentFeaturesEnabled(in Bundle options, IBinder token); } diff --git a/core/java/android/service/trust/ITrustAgentServiceCallback.aidl b/core/java/android/service/trust/ITrustAgentServiceCallback.aidl index b107bcc..76b2be0 100644 --- a/core/java/android/service/trust/ITrustAgentServiceCallback.aidl +++ b/core/java/android/service/trust/ITrustAgentServiceCallback.aidl @@ -27,5 +27,5 @@ oneway interface ITrustAgentServiceCallback { void grantTrust(CharSequence message, long durationMs, boolean initiatedByUser); void revokeTrust(); void setManagingTrust(boolean managingTrust); - void onSetTrustAgentFeaturesEnabledCompleted(boolean result, IBinder token); + void onConfigureCompleted(boolean result, IBinder token); } diff --git a/core/java/android/service/trust/TrustAgentService.java b/core/java/android/service/trust/TrustAgentService.java index 3ef5b37..d6c997f 100644 --- a/core/java/android/service/trust/TrustAgentService.java +++ b/core/java/android/service/trust/TrustAgentService.java @@ -29,11 +29,14 @@ import android.os.Bundle; import android.os.Handler; import android.os.IBinder; import android.os.Message; +import android.os.PersistableBundle; import android.os.RemoteException; import android.os.SystemClock; import android.util.Log; import android.util.Slog; +import java.util.List; + /** * A service that notifies the system about whether it believes the environment of the device * to be trusted. @@ -86,17 +89,22 @@ public class TrustAgentService extends Service { */ public static final String TRUST_AGENT_META_DATA = "android.service.trust.trustagent"; - /** - * A white list of features that the given trust agent should support when otherwise disabled - * by device policy. - * @hide - */ - public static final String KEY_FEATURES = "trust_agent_features"; - private static final int MSG_UNLOCK_ATTEMPT = 1; - private static final int MSG_SET_TRUST_AGENT_FEATURES_ENABLED = 2; + private static final int MSG_CONFIGURE = 2; private static final int MSG_TRUST_TIMEOUT = 3; + /** + * Class containing raw data for a given configuration request. + */ + private static final class ConfigurationData { + final IBinder token; + final List<PersistableBundle> options; + ConfigurationData(List<PersistableBundle> opts, IBinder t) { + options = opts; + token = t; + } + } + private ITrustAgentServiceCallback mCallback; private Runnable mPendingGrantTrustTask; @@ -112,13 +120,12 @@ public class TrustAgentService extends Service { case MSG_UNLOCK_ATTEMPT: onUnlockAttempt(msg.arg1 != 0); break; - case MSG_SET_TRUST_AGENT_FEATURES_ENABLED: - Bundle features = msg.peekData(); - IBinder token = (IBinder) msg.obj; - boolean result = onSetTrustAgentFeaturesEnabled(features); + case MSG_CONFIGURE: + ConfigurationData data = (ConfigurationData) msg.obj; + boolean result = onConfigure(data.options); try { synchronized (mLock) { - mCallback.onSetTrustAgentFeaturesEnabledCompleted(result, token); + mCallback.onConfigureCompleted(result, data.token); } } catch (RemoteException e) { onError("calling onSetTrustAgentFeaturesEnabledCompleted()"); @@ -171,23 +178,16 @@ public class TrustAgentService extends Service { } /** - * Called when device policy wants to restrict features in the agent in response to - * {@link DevicePolicyManager#setTrustAgentFeaturesEnabled(ComponentName, ComponentName, java.util.List) }. - * Agents that support this feature should overload this method and return 'true'. + * Called when device policy admin wants to enable specific options for agent in response to + * {@link DevicePolicyManager#setKeyguardDisabledFeatures(ComponentName, int)} and + * {@link DevicePolicyManager#setTrustAgentConfiguration(ComponentName, ComponentName, + * PersistableBundle)}. + * <p>Agents that support configuration options should overload this method and return 'true'. * - * The list of options can be obtained by calling - * options.getStringArrayList({@link #KEY_FEATURES}). Presence of a feature string in the list - * means it should be enabled ("white-listed"). Absence of the feature means it should be - * disabled. An empty list means all features should be disabled. - * - * This function is only called if {@link DevicePolicyManager#KEYGUARD_DISABLE_TRUST_AGENTS} is - * set. - * - * @param options Option feature bundle. - * @return true if the {@link TrustAgentService} supports this feature. - * @hide + * @param options bundle containing all options or null if none. + * @return true if the {@link TrustAgentService} supports configuration options. */ - public boolean onSetTrustAgentFeaturesEnabled(Bundle options) { + public boolean onConfigure(List<PersistableBundle> options) { return false; } @@ -295,6 +295,12 @@ public class TrustAgentService extends Service { } @Override /* Binder API */ + public void onConfigure(List<PersistableBundle> args, IBinder token) { + mHandler.obtainMessage(MSG_CONFIGURE, new ConfigurationData(args, token)) + .sendToTarget(); + } + + @Override /* Binder API */ public void setCallback(ITrustAgentServiceCallback callback) { synchronized (mLock) { mCallback = callback; @@ -313,13 +319,6 @@ public class TrustAgentService extends Service { } } } - - @Override /* Binder API */ - public void setTrustAgentFeaturesEnabled(Bundle features, IBinder token) { - Message msg = mHandler.obtainMessage(MSG_SET_TRUST_AGENT_FEATURES_ENABLED, token); - msg.setData(features); - msg.sendToTarget(); - } } } diff --git a/core/java/android/service/wallpaper/WallpaperService.java b/core/java/android/service/wallpaper/WallpaperService.java index 26e9a30..ceaf5f8 100644 --- a/core/java/android/service/wallpaper/WallpaperService.java +++ b/core/java/android/service/wallpaper/WallpaperService.java @@ -675,7 +675,8 @@ public abstract class WallpaperService extends Service { com.android.internal.R.style.Animation_Wallpaper; mInputChannel = new InputChannel(); if (mSession.addToDisplay(mWindow, mWindow.mSeq, mLayout, View.VISIBLE, - Display.DEFAULT_DISPLAY, mContentInsets, mInputChannel) < 0) { + Display.DEFAULT_DISPLAY, mContentInsets, mStableInsets, + mInputChannel) < 0) { Log.w(TAG, "Failed to add window while updating wallpaper surface."); return; } diff --git a/core/java/android/text/format/DateFormat.java b/core/java/android/text/format/DateFormat.java index 9fec9a1..933bcee 100755 --- a/core/java/android/text/format/DateFormat.java +++ b/core/java/android/text/format/DateFormat.java @@ -60,27 +60,45 @@ import libcore.icu.LocaleData; * {@code SimpleDateFormat}. */ public class DateFormat { - /** @deprecated Use a literal {@code '} instead. */ + /** + * @deprecated Use a literal {@code '} instead. + * @removed + */ @Deprecated public static final char QUOTE = '\''; - /** @deprecated Use a literal {@code 'a'} instead. */ + /** + * @deprecated Use a literal {@code 'a'} instead. + * @removed + */ @Deprecated public static final char AM_PM = 'a'; - /** @deprecated Use a literal {@code 'a'} instead; 'A' was always equivalent to 'a'. */ + /** + * @deprecated Use a literal {@code 'a'} instead; 'A' was always equivalent to 'a'. + * @removed + */ @Deprecated public static final char CAPITAL_AM_PM = 'A'; - /** @deprecated Use a literal {@code 'd'} instead. */ + /** + * @deprecated Use a literal {@code 'd'} instead. + * @removed + */ @Deprecated public static final char DATE = 'd'; - /** @deprecated Use a literal {@code 'E'} instead. */ + /** + * @deprecated Use a literal {@code 'E'} instead. + * @removed + */ @Deprecated public static final char DAY = 'E'; - /** @deprecated Use a literal {@code 'h'} instead. */ + /** + * @deprecated Use a literal {@code 'h'} instead. + * @removed + */ @Deprecated public static final char HOUR = 'h'; @@ -88,31 +106,51 @@ public class DateFormat { * @deprecated Use a literal {@code 'H'} (for compatibility with {@link SimpleDateFormat} * and Unicode) or {@code 'k'} (for compatibility with Android releases up to and including * Jelly Bean MR-1) instead. Note that the two are incompatible. + * + * @removed */ @Deprecated public static final char HOUR_OF_DAY = 'k'; - /** @deprecated Use a literal {@code 'm'} instead. */ + /** + * @deprecated Use a literal {@code 'm'} instead. + * @removed + */ @Deprecated public static final char MINUTE = 'm'; - /** @deprecated Use a literal {@code 'M'} instead. */ + /** + * @deprecated Use a literal {@code 'M'} instead. + * @removed + */ @Deprecated public static final char MONTH = 'M'; - /** @deprecated Use a literal {@code 'L'} instead. */ + /** + * @deprecated Use a literal {@code 'L'} instead. + * @removed + */ @Deprecated public static final char STANDALONE_MONTH = 'L'; - /** @deprecated Use a literal {@code 's'} instead. */ + /** + * @deprecated Use a literal {@code 's'} instead. + * @removed + */ @Deprecated public static final char SECONDS = 's'; - /** @deprecated Use a literal {@code 'z'} instead. */ + /** + * @deprecated Use a literal {@code 'z'} instead. + * @removed + */ @Deprecated public static final char TIME_ZONE = 'z'; - /** @deprecated Use a literal {@code 'y'} instead. */ + /** + * @deprecated Use a literal {@code 'y'} instead. + * @removed + */ @Deprecated public static final char YEAR = 'y'; @@ -306,9 +344,9 @@ public class DateFormat { } /** - * Gets the current date format stored as a char array. The array will contain - * 3 elements ({@link #DATE}, {@link #MONTH}, and {@link #YEAR}) in the order - * specified by the user's format preference. Note that this order is + * Gets the current date format stored as a char array. Returns a 3 element + * array containing the day ({@code 'd'}), month ({@code 'M'}), and year ({@code 'y'})) + * in the order specified by the user's format preference. Note that this order is * <i>only</i> appropriate for all-numeric dates; spelled-out (MEDIUM and LONG) * dates will generally contain other punctuation, spaces, or words, * not just the day, month, and year, and not necessarily in the same diff --git a/core/java/android/text/format/Formatter.java b/core/java/android/text/format/Formatter.java index b0cbcd2..b467f5a 100644 --- a/core/java/android/text/format/Formatter.java +++ b/core/java/android/text/format/Formatter.java @@ -110,6 +110,7 @@ public final class Formatter { private static final int SECONDS_PER_MINUTE = 60; private static final int SECONDS_PER_HOUR = 60 * 60; private static final int SECONDS_PER_DAY = 24 * 60 * 60; + private static final int MILLIS_PER_MINUTE = 1000 * 60; /** * Returns elapsed time for the given millis, in the following format: @@ -171,4 +172,24 @@ public final class Formatter { return context.getString(com.android.internal.R.string.durationSeconds, seconds); } } + + /** + * Returns elapsed time for the given millis, in the following format: + * 1 day 5 hrs; will include at most two units, can go down to minutes precision. + * @param context the application context + * @param millis the elapsed time in milli seconds + * @return the formatted elapsed time + * @hide + */ + public static String formatShortElapsedTimeRoundingUpToMinutes(Context context, long millis) { + long minutesRoundedUp = (millis + MILLIS_PER_MINUTE - 1) / MILLIS_PER_MINUTE; + + if (minutesRoundedUp == 0) { + return context.getString(com.android.internal.R.string.durationMinutes, 0); + } else if (minutesRoundedUp == 1) { + return context.getString(com.android.internal.R.string.durationMinute, 1); + } + + return formatShortElapsedTime(context, minutesRoundedUp * MILLIS_PER_MINUTE); + } } diff --git a/core/java/android/transition/ChangeTransform.java b/core/java/android/transition/ChangeTransform.java index 3fd28a6..a159b40 100644 --- a/core/java/android/transition/ChangeTransform.java +++ b/core/java/android/transition/ChangeTransform.java @@ -17,11 +17,14 @@ package android.transition; import android.animation.Animator; import android.animation.AnimatorListenerAdapter; +import android.animation.FloatArrayEvaluator; import android.animation.ObjectAnimator; -import android.animation.ValueAnimator; +import android.animation.PropertyValuesHolder; import android.content.Context; import android.content.res.TypedArray; import android.graphics.Matrix; +import android.graphics.Path; +import android.graphics.PointF; import android.util.AttributeSet; import android.util.Property; import android.view.GhostView; @@ -56,16 +59,35 @@ public class ChangeTransform extends Transition { PROPNAME_PARENT_MATRIX, }; - private static final Property<View, Matrix> ANIMATION_MATRIX_PROPERTY = - new Property<View, Matrix>(Matrix.class, "animationMatrix") { + /** + * This property sets the animation matrix properties that are not translations. + */ + private static final Property<PathAnimatorMatrix, float[]> NON_TRANSLATIONS_PROPERTY = + new Property<PathAnimatorMatrix, float[]>(float[].class, "nonTranslations") { @Override - public Matrix get(View object) { + public float[] get(PathAnimatorMatrix object) { return null; } @Override - public void set(View object, Matrix value) { - object.setAnimationMatrix(value); + public void set(PathAnimatorMatrix object, float[] value) { + object.setValues(value); + } + }; + + /** + * This property sets the translation animation matrix properties. + */ + private static final Property<PathAnimatorMatrix, PointF> TRANSLATIONS_PROPERTY = + new Property<PathAnimatorMatrix, PointF>(PointF.class, "translations") { + @Override + public PointF get(PathAnimatorMatrix object) { + return null; + } + + @Override + public void set(PathAnimatorMatrix object, PointF value) { + object.setTranslation(value); } }; @@ -261,8 +283,23 @@ public class ChangeTransform extends Transition { final View view = endValues.view; setIdentityTransforms(view); - ObjectAnimator animator = ObjectAnimator.ofObject(view, ANIMATION_MATRIX_PROPERTY, - new TransitionUtils.MatrixEvaluator(), startMatrix, endMatrix); + final float[] startMatrixValues = new float[9]; + startMatrix.getValues(startMatrixValues); + final float[] endMatrixValues = new float[9]; + endMatrix.getValues(endMatrixValues); + final PathAnimatorMatrix pathAnimatorMatrix = + new PathAnimatorMatrix(view, startMatrixValues); + + PropertyValuesHolder valuesProperty = PropertyValuesHolder.ofObject( + NON_TRANSLATIONS_PROPERTY, new FloatArrayEvaluator(new float[9]), + startMatrixValues, endMatrixValues); + Path path = getPathMotion().getPath(startMatrixValues[Matrix.MTRANS_X], + startMatrixValues[Matrix.MTRANS_Y], endMatrixValues[Matrix.MTRANS_X], + endMatrixValues[Matrix.MTRANS_Y]); + PropertyValuesHolder translationProperty = PropertyValuesHolder.ofObject( + TRANSLATIONS_PROPERTY, null, path); + ObjectAnimator animator = ObjectAnimator.ofPropertyValuesHolder(pathAnimatorMatrix, + valuesProperty, translationProperty); final Matrix finalEndMatrix = endMatrix; @@ -285,14 +322,13 @@ public class ChangeTransform extends Transition { view.setTagInternal(R.id.parentMatrix, null); } } - ANIMATION_MATRIX_PROPERTY.set(view, null); + view.setAnimationMatrix(null); transforms.restore(view); } @Override public void onAnimationPause(Animator animation) { - ValueAnimator animator = (ValueAnimator) animation; - Matrix currentMatrix = (Matrix) animator.getAnimatedValue(); + Matrix currentMatrix = pathAnimatorMatrix.getMatrix(); setCurrentMatrix(currentMatrix); } @@ -457,4 +493,47 @@ public class ChangeTransform extends Transition { mGhostView.setVisibility(View.VISIBLE); } } + + /** + * PathAnimatorMatrix allows the translations and the rest of the matrix to be set + * separately. This allows the PathMotion to affect the translations while scale + * and rotation are evaluated separately. + */ + private static class PathAnimatorMatrix { + private final Matrix mMatrix = new Matrix(); + private final View mView; + private final float[] mValues; + private float mTranslationX; + private float mTranslationY; + + public PathAnimatorMatrix(View view, float[] values) { + mView = view; + mValues = values.clone(); + mTranslationX = mValues[Matrix.MTRANS_X]; + mTranslationY = mValues[Matrix.MTRANS_Y]; + setAnimationMatrix(); + } + + public void setValues(float[] values) { + System.arraycopy(values, 0, mValues, 0, values.length); + setAnimationMatrix(); + } + + public void setTranslation(PointF translation) { + mTranslationX = translation.x; + mTranslationY = translation.y; + setAnimationMatrix(); + } + + private void setAnimationMatrix() { + mValues[Matrix.MTRANS_X] = mTranslationX; + mValues[Matrix.MTRANS_Y] = mTranslationY; + mMatrix.setValues(mValues); + mView.setAnimationMatrix(mMatrix); + } + + public Matrix getMatrix() { + return mMatrix; + } + } } diff --git a/core/java/android/transition/Transition.java b/core/java/android/transition/Transition.java index e99c2cf..2705bcf 100644 --- a/core/java/android/transition/Transition.java +++ b/core/java/android/transition/Transition.java @@ -84,8 +84,8 @@ import com.android.internal.R; * * <p>Custom transition classes may be instantiated with a <code>transition</code> tag:</p> * <pre><transition class="my.app.transition.CustomTransition"/></pre> - * <p>Custom transition classes loaded from XML must have a public nullary (no argument) - * constructor.</p> + * <p>Custom transition classes loaded from XML should have a public constructor taking + * a {@link android.content.Context} and {@link android.util.AttributeSet}.</p> * * <p>Note that attributes for the transition are not required, just as they are * optional when declared in code; Transitions created from XML resources will use @@ -955,7 +955,7 @@ public abstract class Transition implements Cloneable { * Views with different IDs, or no IDs whatsoever, will be ignored. * * <p>Note that using ids to specify targets implies that ids should be unique - * within the view hierarchy underneat the scene root.</p> + * within the view hierarchy underneath the scene root.</p> * * @see View#getId() * @param targetId The id of a target view, must be a positive number. diff --git a/core/java/android/util/TypedValue.java b/core/java/android/util/TypedValue.java index 931fb81..74d4245 100644 --- a/core/java/android/util/TypedValue.java +++ b/core/java/android/util/TypedValue.java @@ -139,6 +139,17 @@ public class TypedValue { /* ------------------------------------------------------------ */ /** + * {@link #TYPE_NULL} data indicating the value was not specified. + */ + public static final int DATA_NULL_UNDEFINED = 0; + /** + * {@link #TYPE_NULL} data indicating the value was explicitly set to null. + */ + public static final int DATA_NULL_EMPTY = 1; + + /* ------------------------------------------------------------ */ + + /** * If {@link #density} is equal to this value, then the density should be * treated as the system's default density value: {@link DisplayMetrics#DENSITY_DEFAULT}. */ @@ -301,6 +312,18 @@ public class TypedValue { } /** + * Return the complex unit type for this value. For example, a dimen type + * with value 12sp will return {@link #COMPLEX_UNIT_SP}. Only use for values + * whose type is {@link #TYPE_DIMENSION}. + * + * @return The complex unit type. + */ + public int getComplexUnit() + { + return COMPLEX_UNIT_MASK & (data>>TypedValue.COMPLEX_UNIT_SHIFT); + } + + /** * Converts an unpacked complex data value holding a dimension to its final floating * point value. The two parameters <var>unit</var> and <var>value</var> * are as in {@link #TYPE_DIMENSION}. diff --git a/core/java/android/view/AccessibilityInteractionController.java b/core/java/android/view/AccessibilityInteractionController.java index 1cadf69..5e05683 100644 --- a/core/java/android/view/AccessibilityInteractionController.java +++ b/core/java/android/view/AccessibilityInteractionController.java @@ -1109,15 +1109,17 @@ final class AccessibilityInteractionController { || accessibilityViewId == providerHost.getAccessibilityViewId()) { final AccessibilityNodeInfo parent; if (virtualDescendantId != AccessibilityNodeInfo.UNDEFINED_ITEM_ID) { - parent = provider.createAccessibilityNodeInfo( - virtualDescendantId); + parent = provider.createAccessibilityNodeInfo(virtualDescendantId); } else { - parent= provider.createAccessibilityNodeInfo( + parent = provider.createAccessibilityNodeInfo( AccessibilityNodeProvider.HOST_VIEW_ID); } - if (parent != null) { - outInfos.add(parent); + if (parent == null) { + // Couldn't obtain the parent, which means we have a + // disconnected sub-tree. Abort prefetch immediately. + return; } + outInfos.add(parent); parentNodeId = parent.getParentNodeId(); accessibilityViewId = AccessibilityNodeInfo.getAccessibilityViewId( parentNodeId); diff --git a/core/java/android/view/GLES20Canvas.java b/core/java/android/view/GLES20Canvas.java index 1b57c24..b86455a 100644 --- a/core/java/android/view/GLES20Canvas.java +++ b/core/java/android/view/GLES20Canvas.java @@ -242,7 +242,7 @@ class GLES20Canvas extends HardwareCanvas { void drawHardwareLayer(HardwareLayer layer, float x, float y, Paint paint) { layer.setLayerPaint(paint); - nDrawLayer(mRenderer, layer.getLayer(), x, y); + nDrawLayer(mRenderer, layer.getLayerHandle(), x, y); } private static native void nDrawLayer(long renderer, long layer, float x, float y); diff --git a/core/java/android/view/HardwareLayer.java b/core/java/android/view/HardwareLayer.java index 0c2e944..a130bda 100644 --- a/core/java/android/view/HardwareLayer.java +++ b/core/java/android/view/HardwareLayer.java @@ -126,8 +126,8 @@ final class HardwareLayer { mRenderer.detachSurfaceTexture(mFinalizer.get()); } - public long getLayer() { - return nGetLayer(mFinalizer.get()); + public long getLayerHandle() { + return mFinalizer.get(); } public void setSurfaceTexture(SurfaceTexture surface) { @@ -153,6 +153,5 @@ final class HardwareLayer { private static native void nUpdateRenderLayer(long layerUpdater, long displayList, int left, int top, int right, int bottom); - private static native long nGetLayer(long layerUpdater); private static native int nGetTexName(long layerUpdater); } diff --git a/core/java/android/view/IWindow.aidl b/core/java/android/view/IWindow.aidl index 3e7aae0..9fc80fc 100644 --- a/core/java/android/view/IWindow.aidl +++ b/core/java/android/view/IWindow.aidl @@ -85,4 +85,9 @@ oneway interface IWindow { * is done. */ void doneAnimating(); + + /** + * Called for non-application windows when the enter animation has completed. + */ + void dispatchWindowShown(); } diff --git a/core/java/android/view/IWindowManager.aidl b/core/java/android/view/IWindowManager.aidl index 6aa86c7..7b20e72 100644 --- a/core/java/android/view/IWindowManager.aidl +++ b/core/java/android/view/IWindowManager.aidl @@ -96,6 +96,7 @@ interface IWindowManager void overridePendingAppTransitionAspectScaledThumb(in Bitmap srcThumb, int startX, int startY, int targetWidth, int targetHeight, IRemoteCallback startedCallback, boolean scaleUp); + void overridePendingAppTransitionInPlace(String packageName, int anim); void executeAppTransition(); void setAppStartingWindow(IBinder token, String pkg, int theme, in CompatibilityInfo compatInfo, CharSequence nonLocalizedLabel, int labelRes, diff --git a/core/java/android/view/IWindowSession.aidl b/core/java/android/view/IWindowSession.aidl index 037ed28..7b13e84 100644 --- a/core/java/android/view/IWindowSession.aidl +++ b/core/java/android/view/IWindowSession.aidl @@ -36,15 +36,16 @@ import android.view.Surface; */ interface IWindowSession { int add(IWindow window, int seq, in WindowManager.LayoutParams attrs, - in int viewVisibility, out Rect outContentInsets, + in int viewVisibility, out Rect outContentInsets, out Rect outStableInsets, out InputChannel outInputChannel); int addToDisplay(IWindow window, int seq, in WindowManager.LayoutParams attrs, in int viewVisibility, in int layerStackId, out Rect outContentInsets, - out InputChannel outInputChannel); + out Rect outStableInsets, out InputChannel outInputChannel); int addWithoutInputChannel(IWindow window, int seq, in WindowManager.LayoutParams attrs, - in int viewVisibility, out Rect outContentInsets); + in int viewVisibility, out Rect outContentInsets, out Rect outStableInsets); int addToDisplayWithoutInputChannel(IWindow window, int seq, in WindowManager.LayoutParams attrs, - in int viewVisibility, in int layerStackId, out Rect outContentInsets); + in int viewVisibility, in int layerStackId, out Rect outContentInsets, + out Rect outStableInsets); void remove(IWindow window); /** diff --git a/core/java/android/view/RenderNodeAnimator.java b/core/java/android/view/RenderNodeAnimator.java index debf45d..b95f9a4 100644 --- a/core/java/android/view/RenderNodeAnimator.java +++ b/core/java/android/view/RenderNodeAnimator.java @@ -219,7 +219,7 @@ public class RenderNodeAnimator extends Animator { @Override public void cancel() { - if (mState != STATE_FINISHED) { + if (mState != STATE_PREPARE && mState != STATE_FINISHED) { if (mState == STATE_DELAYED) { getHelper().removeDelayedAnimation(this); notifyStartListeners(); diff --git a/core/java/android/view/Surface.java b/core/java/android/view/Surface.java index 562d138..19142b8 100644 --- a/core/java/android/view/Surface.java +++ b/core/java/android/view/Surface.java @@ -319,6 +319,7 @@ public class Surface implements Parcelable { * @return A canvas for drawing into the surface. * * @throws IllegalStateException If the canvas cannot be locked. + * @hide */ public Canvas lockHardwareCanvas() { synchronized (mLock) { @@ -603,15 +604,6 @@ public class Surface implements Parcelable { mHwuiRenderer = 0; } } - - @Override - protected void finalize() throws Throwable { - try { - destroy(); - } finally { - super.finalize(); - } - } } private static native long nHwuiCreate(long rootNode, long surface); diff --git a/core/java/android/view/SurfaceView.java b/core/java/android/view/SurfaceView.java index afc804c..49be57d 100644 --- a/core/java/android/view/SurfaceView.java +++ b/core/java/android/view/SurfaceView.java @@ -496,7 +496,8 @@ public class SurfaceView extends View { mLayout.type = mWindowType; mLayout.gravity = Gravity.START|Gravity.TOP; mSession.addToDisplayWithoutInputChannel(mWindow, mWindow.mSeq, mLayout, - mVisible ? VISIBLE : GONE, display.getDisplayId(), mContentInsets); + mVisible ? VISIBLE : GONE, display.getDisplayId(), mContentInsets, + mStableInsets); } boolean realSizeChanged; diff --git a/core/java/android/view/ThreadedRenderer.java b/core/java/android/view/ThreadedRenderer.java index 0711aed..5579c13 100644 --- a/core/java/android/view/ThreadedRenderer.java +++ b/core/java/android/view/ThreadedRenderer.java @@ -66,6 +66,8 @@ public class ThreadedRenderer extends HardwareRenderer { private static final int SYNC_OK = 0; // Needs a ViewRoot invalidate private static final int SYNC_INVALIDATE_REQUIRED = 1 << 0; + // Spoiler: the reward is GPU-accelerated drawing, better find that Surface! + private static final int SYNC_LOST_SURFACE_REWARD_IF_FOUND = 1 << 1; private static final String[] VISUALIZERS = { PROFILE_PROPERTY_VISUALIZE_BARS, @@ -191,7 +193,8 @@ public class ThreadedRenderer extends HardwareRenderer { final float lightX = width / 2.0f; mWidth = width; mHeight = height; - if (surfaceInsets != null && !surfaceInsets.isEmpty()) { + if (surfaceInsets != null && (surfaceInsets.left != 0 || surfaceInsets.right != 0 + || surfaceInsets.top != 0 || surfaceInsets.bottom != 0)) { mHasInsets = true; mInsetLeft = surfaceInsets.left; mInsetTop = surfaceInsets.top; @@ -335,6 +338,12 @@ public class ThreadedRenderer extends HardwareRenderer { int syncResult = nSyncAndDrawFrame(mNativeProxy, frameTimeNanos, recordDuration, view.getResources().getDisplayMetrics().density); + if ((syncResult & SYNC_LOST_SURFACE_REWARD_IF_FOUND) != 0) { + setEnabled(false); + // Invalidate since we failed to draw. This should fetch a Surface + // if it is still needed or do nothing if we are no longer drawing + attachInfo.mViewRootImpl.invalidate(); + } if ((syncResult & SYNC_INVALIDATE_REQUIRED) != 0) { attachInfo.mViewRootImpl.invalidate(); } diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java index e7b98ca..1d09696 100644 --- a/core/java/android/view/View.java +++ b/core/java/android/view/View.java @@ -3109,6 +3109,16 @@ public class View implements Drawable.Callback, KeyEvent.Callback, private MatchLabelForPredicate mMatchLabelForPredicate; /** + * Specifies a view before which this one is visited in accessibility traversal. + */ + private int mAccessibilityTraversalBeforeId = NO_ID; + + /** + * Specifies a view after which this one is visited in accessibility traversal. + */ + private int mAccessibilityTraversalAfterId = NO_ID; + + /** * Predicate for matching a view by its id. */ private MatchIdPredicate mMatchIdPredicate; @@ -3229,6 +3239,8 @@ public class View implements Drawable.Callback, KeyEvent.Callback, */ private ArrayList<OnLayoutChangeListener> mOnLayoutChangeListeners; + protected OnScrollChangeListener mOnScrollChangeListener; + /** * Listeners for attach events. */ @@ -3886,6 +3898,12 @@ public class View implements Drawable.Callback, KeyEvent.Callback, case com.android.internal.R.styleable.View_contentDescription: setContentDescription(a.getString(attr)); break; + case com.android.internal.R.styleable.View_accessibilityTraversalBefore: + setAccessibilityTraversalBefore(a.getResourceId(attr, NO_ID)); + break; + case com.android.internal.R.styleable.View_accessibilityTraversalAfter: + setAccessibilityTraversalAfter(a.getResourceId(attr, NO_ID)); + break; case com.android.internal.R.styleable.View_labelFor: setLabelFor(a.getResourceId(attr, NO_ID)); break; @@ -4606,6 +4624,17 @@ public class View implements Drawable.Callback, KeyEvent.Callback, } /** + * Register a callback to be invoked when the scroll position of this view + * changed. + * + * @param l The callback that will run. + * @hide Only used internally. + */ + public void setOnScrollChangeListener(OnScrollChangeListener l) { + getListenerInfo().mOnScrollChangeListener = l; + } + + /** * Register a callback to be invoked when focus of this view changed. * * @param l The callback that will run. @@ -5598,6 +5627,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, if (rootView == null) { rootView = this; } + View label = rootView.findLabelForView(this, mID); if (label != null) { info.setLabeledBy(label); @@ -5626,6 +5656,30 @@ public class View implements Drawable.Callback, KeyEvent.Callback, } } + if (mAccessibilityTraversalBeforeId != View.NO_ID) { + View rootView = getRootView(); + if (rootView == null) { + rootView = this; + } + View next = rootView.findViewInsideOutShouldExist(this, + mAccessibilityTraversalBeforeId); + if (next != null) { + info.setTraversalBefore(next); + } + } + + if (mAccessibilityTraversalAfterId != View.NO_ID) { + View rootView = getRootView(); + if (rootView == null) { + rootView = this; + } + View next = rootView.findViewInsideOutShouldExist(this, + mAccessibilityTraversalAfterId); + if (next != null) { + info.setTraversalAfter(next); + } + } + info.setVisibleToUser(isVisibleToUser()); info.setPackageName(mContext.getPackageName()); @@ -6030,6 +6084,94 @@ public class View implements Drawable.Callback, KeyEvent.Callback, } /** + * Sets the id of a view before which this one is visited in accessibility traversal. + * A screen-reader must visit the content of this view before the content of the one + * it precedes. For example, if view B is set to be before view A, then a screen-reader + * will traverse the entire content of B before traversing the entire content of A, + * regardles of what traversal strategy it is using. + * <p> + * Views that do not have specified before/after relationships are traversed in order + * determined by the screen-reader. + * </p> + * <p> + * Setting that this view is before a view that is not important for accessibility + * or if this view is not important for accessibility will have no effect as the + * screen-reader is not aware of unimportant views. + * </p> + * + * @param beforeId The id of a view this one precedes in accessibility traversal. + * + * @attr ref android.R.styleable#View_accessibilityTraversalBefore + * + * @see #setImportantForAccessibility(int) + */ + @RemotableViewMethod + public void setAccessibilityTraversalBefore(int beforeId) { + if (mAccessibilityTraversalBeforeId == beforeId) { + return; + } + mAccessibilityTraversalBeforeId = beforeId; + notifyViewAccessibilityStateChangedIfNeeded( + AccessibilityEvent.CONTENT_CHANGE_TYPE_UNDEFINED); + } + + /** + * Gets the id of a view before which this one is visited in accessibility traversal. + * + * @return The id of a view this one precedes in accessibility traversal if + * specified, otherwise {@link #NO_ID}. + * + * @see #setAccessibilityTraversalBefore(int) + */ + public int getAccessibilityTraversalBefore() { + return mAccessibilityTraversalBeforeId; + } + + /** + * Sets the id of a view after which this one is visited in accessibility traversal. + * A screen-reader must visit the content of the other view before the content of this + * one. For example, if view B is set to be after view A, then a screen-reader + * will traverse the entire content of A before traversing the entire content of B, + * regardles of what traversal strategy it is using. + * <p> + * Views that do not have specified before/after relationships are traversed in order + * determined by the screen-reader. + * </p> + * <p> + * Setting that this view is after a view that is not important for accessibility + * or if this view is not important for accessibility will have no effect as the + * screen-reader is not aware of unimportant views. + * </p> + * + * @param afterId The id of a view this one succedees in accessibility traversal. + * + * @attr ref android.R.styleable#View_accessibilityTraversalAfter + * + * @see #setImportantForAccessibility(int) + */ + @RemotableViewMethod + public void setAccessibilityTraversalAfter(int afterId) { + if (mAccessibilityTraversalAfterId == afterId) { + return; + } + mAccessibilityTraversalAfterId = afterId; + notifyViewAccessibilityStateChangedIfNeeded( + AccessibilityEvent.CONTENT_CHANGE_TYPE_UNDEFINED); + } + + /** + * Gets the id of a view after which this one is visited in accessibility traversal. + * + * @return The id of a view this one succeedes in accessibility traversal if + * specified, otherwise {@link #NO_ID}. + * + * @see #setAccessibilityTraversalAfter(int) + */ + public int getAccessibilityTraversalAfter() { + return mAccessibilityTraversalAfterId; + } + + /** * Gets the id of a view for which this view serves as a label for * accessibility purposes. * @@ -6048,11 +6190,16 @@ public class View implements Drawable.Callback, KeyEvent.Callback, */ @RemotableViewMethod public void setLabelFor(int id) { + if (mLabelForId == id) { + return; + } mLabelForId = id; if (mLabelForId != View.NO_ID && mID == View.NO_ID) { mID = generateViewId(); } + notifyViewAccessibilityStateChangedIfNeeded( + AccessibilityEvent.CONTENT_CHANGE_TYPE_UNDEFINED); } /** @@ -9794,6 +9941,29 @@ public class View implements Drawable.Callback, KeyEvent.Callback, if (ai != null) { ai.mViewScrollChanged = true; } + + if (mListenerInfo != null && mListenerInfo.mOnScrollChangeListener != null) { + mListenerInfo.mOnScrollChangeListener.onScrollChange(this, l, t, oldl, oldt); + } + } + + /** + * Interface definition for a callback to be invoked when the scroll + * position of a view changes. + * + * @hide Only used internally. + */ + public interface OnScrollChangeListener { + /** + * Called when the scroll position of a view changes. + * + * @param v The view whose scroll position has changed. + * @param scrollX Current horizontal scroll origin. + * @param scrollY Current vertical scroll origin. + * @param oldScrollX Previous horizontal scroll origin. + * @param oldScrollY Previous vertical scroll origin. + */ + void onScrollChange(View v, int scrollX, int scrollY, int oldScrollX, int oldScrollY); } /** @@ -14062,7 +14232,6 @@ public class View implements Drawable.Callback, KeyEvent.Callback, } else { draw(canvas); } - drawAccessibilityFocus(canvas); } } finally { renderNode.end(canvas); @@ -14357,7 +14526,6 @@ public class View implements Drawable.Callback, KeyEvent.Callback, } else { draw(canvas); } - drawAccessibilityFocus(canvas); canvas.restoreToCount(restoreCount); canvas.setBitmap(null); @@ -14432,7 +14600,6 @@ public class View implements Drawable.Callback, KeyEvent.Callback, } else { draw(canvas); } - drawAccessibilityFocus(canvas); mPrivateFlags = flags; @@ -15030,13 +15197,9 @@ public class View implements Drawable.Callback, KeyEvent.Callback, if ((mPrivateFlags & PFLAG_SKIP_DRAW) == PFLAG_SKIP_DRAW) { mPrivateFlags &= ~PFLAG_DIRTY_MASK; dispatchDraw(canvas); - if (mOverlay != null && !mOverlay.isEmpty()) { - mOverlay.getOverlayView().draw(canvas); - } } else { draw(canvas); } - drawAccessibilityFocus(canvas); } else { mPrivateFlags &= ~PFLAG_DIRTY_MASK; ((HardwareCanvas) canvas).drawRenderNode(renderNode, null, flags); @@ -15288,50 +15451,6 @@ public class View implements Drawable.Callback, KeyEvent.Callback, } /** - * Draws the accessibility focus rect onto the specified canvas. - * - * @param canvas Canvas on which to draw the focus rect - */ - private void drawAccessibilityFocus(Canvas canvas) { - if (mAttachInfo == null) { - return; - } - - final Rect bounds = mAttachInfo.mTmpInvalRect; - final ViewRootImpl viewRoot = getViewRootImpl(); - if (viewRoot == null || viewRoot.getAccessibilityFocusedHost() != this) { - return; - } - - final AccessibilityManager manager = AccessibilityManager.getInstance(mContext); - if (!manager.isEnabled() || !manager.isTouchExplorationEnabled()) { - return; - } - - final Drawable drawable = viewRoot.getAccessibilityFocusedDrawable(); - if (drawable == null) { - return; - } - - final AccessibilityNodeInfo virtualView = viewRoot.getAccessibilityFocusedVirtualView(); - if (virtualView != null) { - virtualView.getBoundsInScreen(bounds); - final int[] offset = mAttachInfo.mTmpLocation; - getLocationOnScreen(offset); - bounds.offset(-offset[0], -offset[1]); - } else { - bounds.set(0, 0, mRight - mLeft, mBottom - mTop); - } - - canvas.save(); - canvas.translate(mScrollX, mScrollY); - canvas.clipRect(bounds, Region.Op.REPLACE); - drawable.setBounds(bounds); - drawable.draw(canvas); - canvas.restore(); - } - - /** * Draws the background onto the specified canvas. * * @param canvas Canvas on which to draw the background @@ -16353,6 +16472,12 @@ public class View implements Drawable.Callback, KeyEvent.Callback, if (tintInfo.mHasTintMode) { mBackground.setTintMode(tintInfo.mTintMode); } + + // The drawable (or one of its children) may not have been + // stateful before applying the tint, so let's try again. + if (mBackground.isStateful()) { + mBackground.setState(getDrawableState()); + } } } } diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java index 7c7e3e7..5c433c1 100644 --- a/core/java/android/view/ViewGroup.java +++ b/core/java/android/view/ViewGroup.java @@ -2582,15 +2582,21 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager * Returns true if this ViewGroup should be considered as a single entity for removal * when executing an Activity transition. If this is false, child elements will move * individually during the transition. + * * @return True if the ViewGroup should be acted on together during an Activity transition. - * The default value is false when the background is null and true when the background - * is not null or if {@link #getTransitionName()} is not null. + * The default value is true when there is a non-null background or if + * {@link #getTransitionName()} is not null or if a + * non-null {@link android.view.ViewOutlineProvider} other than + * {@link android.view.ViewOutlineProvider#BACKGROUND} was given to + * {@link #setOutlineProvider(ViewOutlineProvider)} and false otherwise. */ public boolean isTransitionGroup() { if ((mGroupFlags & FLAG_IS_TRANSITION_GROUP_SET) != 0) { return ((mGroupFlags & FLAG_IS_TRANSITION_GROUP) != 0); } else { - return getBackground() != null || getTransitionName() != null; + final ViewOutlineProvider outlineProvider = getOutlineProvider(); + return getBackground() != null || getTransitionName() != null || + (outlineProvider != null && outlineProvider != ViewOutlineProvider.BACKGROUND); } } @@ -4250,9 +4256,7 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager clearChildFocus = true; } - if (view.isAccessibilityFocused()) { - view.clearAccessibilityFocus(); - } + view.clearAccessibilityFocus(); cancelTouchTarget(view); cancelHoverTarget(view); @@ -4345,9 +4349,7 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager clearChildFocus = true; } - if (view.isAccessibilityFocused()) { - view.clearAccessibilityFocus(); - } + view.clearAccessibilityFocus(); cancelTouchTarget(view); cancelHoverTarget(view); @@ -4432,9 +4434,7 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager clearChildFocus = true; } - if (view.isAccessibilityFocused()) { - view.clearAccessibilityFocus(); - } + view.clearAccessibilityFocus(); cancelTouchTarget(view); cancelHoverTarget(view); diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java index 81fc966..5d2a24b 100644 --- a/core/java/android/view/ViewRootImpl.java +++ b/core/java/android/view/ViewRootImpl.java @@ -526,7 +526,7 @@ public final class ViewRootImpl implements ViewParent, collectViewAttributes(); res = mWindowSession.addToDisplay(mWindow, mSeq, mWindowAttributes, getHostVisibility(), mDisplay.getDisplayId(), - mAttachInfo.mContentInsets, mInputChannel); + mAttachInfo.mContentInsets, mAttachInfo.mStableInsets, mInputChannel); } catch (RemoteException e) { mAdded = false; mView = null; @@ -727,7 +727,10 @@ public final class ViewRootImpl implements ViewParent, mAttachInfo.mHardwareRenderer.destroy(); } - final boolean translucent = attrs.format != PixelFormat.OPAQUE; + final Rect insets = attrs.surfaceInsets; + final boolean hasSurfaceInsets = insets.left != 0 || insets.right != 0 + || insets.top != 0 || insets.bottom != 0; + final boolean translucent = attrs.format != PixelFormat.OPAQUE || hasSurfaceInsets; mAttachInfo.mHardwareRenderer = HardwareRenderer.create(mContext, translucent); if (mAttachInfo.mHardwareRenderer != null) { mAttachInfo.mHardwareRenderer.setName(attrs.getTitle().toString()); @@ -1646,6 +1649,9 @@ public final class ViewRootImpl implements ViewParent, mLastScrolledFocus.clear(); } mScrollY = mCurScrollY = 0; + if (mView instanceof RootViewSurfaceTaker) { + ((RootViewSurfaceTaker) mView).onRootViewScrollYChanged(mCurScrollY); + } if (mScroller != null) { mScroller.abortAnimation(); } @@ -1740,8 +1746,8 @@ public final class ViewRootImpl implements ViewParent, if (hwInitialized || mWidth != mAttachInfo.mHardwareRenderer.getWidth() || mHeight != mAttachInfo.mHardwareRenderer.getHeight()) { - final Rect surfaceInsets = params != null ? params.surfaceInsets : null; - mAttachInfo.mHardwareRenderer.setup(mWidth, mHeight, surfaceInsets); + mAttachInfo.mHardwareRenderer.setup( + mWidth, mHeight, mWindowAttributes.surfaceInsets); if (!hwInitialized) { mAttachInfo.mHardwareRenderer.invalidate(mSurface); mFullRedrawNeeded = true; @@ -2255,6 +2261,7 @@ public final class ViewRootImpl implements ViewParent, canvas.drawHardwareLayer(mResizeBuffer, mHardwareXOffset, mHardwareYOffset, mResizePaint); } + drawAccessibilityFocusedDrawableIfNeeded(canvas); } /** @@ -2415,6 +2422,9 @@ public final class ViewRootImpl implements ViewParent, if (mCurScrollY != curScrollY) { mCurScrollY = curScrollY; fullRedrawNeeded = true; + if (mView instanceof RootViewSurfaceTaker) { + ((RootViewSurfaceTaker) mView).onRootViewScrollYChanged(mCurScrollY); + } } final float appScale = mAttachInfo.mApplicationScale; @@ -2474,18 +2484,38 @@ public final class ViewRootImpl implements ViewParent, dirty.offset(surfaceInsets.left, surfaceInsets.right); } - if (!dirty.isEmpty() || mIsAnimating) { + boolean accessibilityFocusDirty = false; + final Drawable drawable = mAttachInfo.mAccessibilityFocusDrawable; + if (drawable != null) { + final Rect bounds = mAttachInfo.mTmpInvalRect; + final boolean hasFocus = getAccessibilityFocusedRect(bounds); + if (!hasFocus) { + bounds.setEmpty(); + } + if (!bounds.equals(drawable.getBounds())) { + accessibilityFocusDirty = true; + } + } + + if (!dirty.isEmpty() || mIsAnimating || accessibilityFocusDirty) { if (mAttachInfo.mHardwareRenderer != null && mAttachInfo.mHardwareRenderer.isEnabled()) { + // If accessibility focus moved, always invalidate the root. + boolean invalidateRoot = accessibilityFocusDirty; + // Draw with hardware renderer. mIsAnimating = false; - boolean invalidateRoot = false; + if (mHardwareYOffset != yOffset || mHardwareXOffset != xOffset) { mHardwareYOffset = yOffset; mHardwareXOffset = xOffset; - mAttachInfo.mHardwareRenderer.invalidateRoot(); + invalidateRoot = true; } mResizeAlpha = resizeAlpha; + if (invalidateRoot) { + mAttachInfo.mHardwareRenderer.invalidateRoot(); + } + dirty.setEmpty(); mBlockResizeBuffer = false; @@ -2604,6 +2634,8 @@ public final class ViewRootImpl implements ViewParent, attachInfo.mSetIgnoreDirtyState = false; mView.draw(canvas); + + drawAccessibilityFocusedDrawableIfNeeded(canvas); } finally { if (!attachInfo.mSetIgnoreDirtyState) { // Only clear the flag if it was not set during the mView.draw() call @@ -2627,7 +2659,56 @@ public final class ViewRootImpl implements ViewParent, return true; } - Drawable getAccessibilityFocusedDrawable() { + /** + * We want to draw a highlight around the current accessibility focused. + * Since adding a style for all possible view is not a viable option we + * have this specialized drawing method. + * + * Note: We are doing this here to be able to draw the highlight for + * virtual views in addition to real ones. + * + * @param canvas The canvas on which to draw. + */ + private void drawAccessibilityFocusedDrawableIfNeeded(Canvas canvas) { + final Rect bounds = mAttachInfo.mTmpInvalRect; + if (getAccessibilityFocusedRect(bounds)) { + final Drawable drawable = getAccessibilityFocusedDrawable(); + if (drawable != null) { + drawable.setBounds(bounds); + drawable.draw(canvas); + } + } else if (mAttachInfo.mAccessibilityFocusDrawable != null) { + mAttachInfo.mAccessibilityFocusDrawable.setBounds(0, 0, 0, 0); + } + } + + private boolean getAccessibilityFocusedRect(Rect bounds) { + final AccessibilityManager manager = AccessibilityManager.getInstance(mView.mContext); + if (!manager.isEnabled() || !manager.isTouchExplorationEnabled()) { + return false; + } + + final View host = mAccessibilityFocusedHost; + if (host == null || host.mAttachInfo == null) { + return false; + } + + final AccessibilityNodeProvider provider = host.getAccessibilityNodeProvider(); + if (provider == null) { + host.getBoundsOnScreen(bounds); + } else if (mAccessibilityFocusedVirtualView != null) { + mAccessibilityFocusedVirtualView.getBoundsInScreen(bounds); + } else { + return false; + } + + final AttachInfo attachInfo = mAttachInfo; + bounds.offset(-attachInfo.mWindowLeft, -attachInfo.mWindowTop); + bounds.intersect(0, 0, attachInfo.mViewRootImpl.mWidth, attachInfo.mViewRootImpl.mHeight); + return !bounds.isEmpty(); + } + + private Drawable getAccessibilityFocusedDrawable() { // Lazily load the accessibility focus drawable. if (mAttachInfo.mAccessibilityFocusDrawable == null) { final TypedValue value = new TypedValue(); @@ -3014,6 +3095,7 @@ public final class ViewRootImpl implements ViewParent, private final static int MSG_INVALIDATE_WORLD = 23; private final static int MSG_WINDOW_MOVED = 24; private final static int MSG_SYNTHESIZE_INPUT_EVENT = 25; + private final static int MSG_DISPATCH_WINDOW_SHOWN = 26; final class ViewRootHandler extends Handler { @Override @@ -3063,6 +3145,8 @@ public final class ViewRootImpl implements ViewParent, return "MSG_WINDOW_MOVED"; case MSG_SYNTHESIZE_INPUT_EVENT: return "MSG_SYNTHESIZE_INPUT_EVENT"; + case MSG_DISPATCH_WINDOW_SHOWN: + return "MSG_DISPATCH_WINDOW_SHOWN"; } return super.getMessageName(message); } @@ -3291,6 +3375,9 @@ public final class ViewRootImpl implements ViewParent, invalidateWorld(mView); } } break; + case MSG_DISPATCH_WINDOW_SHOWN: { + handleDispatchWindowShown(); + } } } } @@ -5137,6 +5224,10 @@ public final class ViewRootImpl implements ViewParent, } } + public void handleDispatchWindowShown() { + mAttachInfo.mTreeObserver.dispatchOnWindowShown(); + } + public void getLastTouchPoint(Point outLocation) { outLocation.x = (int) mLastTouchPoint.x; outLocation.y = (int) mLastTouchPoint.y; @@ -5997,6 +6088,10 @@ public final class ViewRootImpl implements ViewParent, mHandler.sendMessage(msg); } + public void dispatchWindowShown() { + mHandler.sendEmptyMessage(MSG_DISPATCH_WINDOW_SHOWN); + } + public void dispatchCloseSystemDialogs(String reason) { Message msg = Message.obtain(); msg.what = MSG_CLOSE_SYSTEM_DIALOGS; @@ -6507,6 +6602,14 @@ public final class ViewRootImpl implements ViewParent, viewAncestor.dispatchDoneAnimating(); } } + + @Override + public void dispatchWindowShown() { + final ViewRootImpl viewAncestor = mViewAncestor.get(); + if (viewAncestor != null) { + viewAncestor.dispatchWindowShown(); + } + } } public static final class CalledFromWrongThreadException extends AndroidRuntimeException { diff --git a/core/java/android/view/ViewTreeObserver.java b/core/java/android/view/ViewTreeObserver.java index a9444b4..b85fec8 100644 --- a/core/java/android/view/ViewTreeObserver.java +++ b/core/java/android/view/ViewTreeObserver.java @@ -44,10 +44,15 @@ public final class ViewTreeObserver { private CopyOnWriteArray<OnComputeInternalInsetsListener> mOnComputeInternalInsetsListeners; private CopyOnWriteArray<OnScrollChangedListener> mOnScrollChangedListeners; private CopyOnWriteArray<OnPreDrawListener> mOnPreDrawListeners; + private CopyOnWriteArray<OnWindowShownListener> mOnWindowShownListeners; // These listeners cannot be mutated during dispatch private ArrayList<OnDrawListener> mOnDrawListeners; + /** Remains false until #dispatchOnWindowShown() is called. If a listener registers after + * that the listener will be immediately called. */ + private boolean mWindowShown; + private boolean mAlive = true; /** @@ -174,6 +179,19 @@ public final class ViewTreeObserver { } /** + * Interface definition for a callback noting when a system window has been displayed. + * This is only used for non-Activity windows. Activity windows can use + * Activity.onEnterAnimationComplete() to get the same signal. + * @hide + */ + public interface OnWindowShownListener { + /** + * Callback method to be invoked when a non-activity window is fully shown. + */ + void onWindowShown(); + } + + /** * Parameters used with OnComputeInternalInsetsListener. * * We are not yet ready to commit to this API and support it, so @@ -375,6 +393,14 @@ public final class ViewTreeObserver { } } + if (observer.mOnWindowShownListeners != null) { + if (mOnWindowShownListeners != null) { + mOnWindowShownListeners.addAll(observer.mOnWindowShownListeners); + } else { + mOnWindowShownListeners = observer.mOnWindowShownListeners; + } + } + observer.kill(); } @@ -568,6 +594,45 @@ public final class ViewTreeObserver { } /** + * Register a callback to be invoked when the view tree window has been shown + * + * @param listener The callback to add + * + * @throws IllegalStateException If {@link #isAlive()} returns false + * @hide + */ + public void addOnWindowShownListener(OnWindowShownListener listener) { + checkIsAlive(); + + if (mOnWindowShownListeners == null) { + mOnWindowShownListeners = new CopyOnWriteArray<OnWindowShownListener>(); + } + + mOnWindowShownListeners.add(listener); + if (mWindowShown) { + listener.onWindowShown(); + } + } + + /** + * Remove a previously installed window shown callback + * + * @param victim The callback to remove + * + * @throws IllegalStateException If {@link #isAlive()} returns false + * + * @see #addOnWindowShownListener(OnWindowShownListener) + * @hide + */ + public void removeOnWindowShownListener(OnWindowShownListener victim) { + checkIsAlive(); + if (mOnWindowShownListeners == null) { + return; + } + mOnWindowShownListeners.remove(victim); + } + + /** * <p>Register a callback to be invoked when the view tree is about to be drawn.</p> * <p><strong>Note:</strong> this method <strong>cannot</strong> be invoked from * {@link android.view.ViewTreeObserver.OnDrawListener#onDraw()}.</p> @@ -854,6 +919,27 @@ public final class ViewTreeObserver { } /** + * Notifies registered listeners that the window is now shown + * @hide + */ + @SuppressWarnings("unchecked") + public final void dispatchOnWindowShown() { + mWindowShown = true; + final CopyOnWriteArray<OnWindowShownListener> listeners = mOnWindowShownListeners; + if (listeners != null && listeners.size() > 0) { + CopyOnWriteArray.Access<OnWindowShownListener> access = listeners.start(); + try { + int count = access.size(); + for (int i = 0; i < count; i++) { + access.get(i).onWindowShown(); + } + } finally { + listeners.end(); + } + } + } + + /** * Notifies registered listeners that the drawing pass is about to start. */ public final void dispatchOnDraw() { diff --git a/core/java/android/view/Window.java b/core/java/android/view/Window.java index 20edeb8..0076abf 100644 --- a/core/java/android/view/Window.java +++ b/core/java/android/view/Window.java @@ -1074,17 +1074,36 @@ public abstract class Window { public abstract void onConfigurationChanged(Configuration newConfig); /** + * Sets the window elevation. + * + * @param elevation The window elevation. + * @see View#setElevation(float) + * @see android.R.styleable#Window_windowElevation + */ + public void setElevation(float elevation) {} + + /** + * Sets whether window content should be clipped to the outline of the + * window background. + * + * @param clipToOutline Whether window content should be clipped to the + * outline of the window background. + * @see View#setClipToOutline(boolean) + * @see android.R.styleable#Window_windowClipToOutline + */ + public void setClipToOutline(boolean clipToOutline) {} + + /** * Change the background of this window to a Drawable resource. Setting the * background to null will make the window be opaque. To make the window * transparent, you can use an empty drawable (for instance a ColorDrawable * with the color 0 or the system drawable android:drawable/empty.) * - * @param resid The resource identifier of a drawable resource which will be - * installed as the new background. + * @param resId The resource identifier of a drawable resource which will + * be installed as the new background. */ - public void setBackgroundDrawableResource(int resid) - { - setBackgroundDrawable(mContext.getDrawable(resid)); + public void setBackgroundDrawableResource(int resId) { + setBackgroundDrawable(mContext.getDrawable(resId)); } /** diff --git a/core/java/android/view/WindowManager.java b/core/java/android/view/WindowManager.java index 5b48c0d..f4f047e 100644 --- a/core/java/android/view/WindowManager.java +++ b/core/java/android/view/WindowManager.java @@ -1324,7 +1324,7 @@ public interface WindowManager extends ViewManager { * * @hide */ - public Rect surfaceInsets = new Rect(); + public final Rect surfaceInsets = new Rect(); /** * The desired bitmap format. May be one of the constants in diff --git a/core/java/android/view/WindowManagerGlobal.java b/core/java/android/view/WindowManagerGlobal.java index 5926d5f..82b1073 100644 --- a/core/java/android/view/WindowManagerGlobal.java +++ b/core/java/android/view/WindowManagerGlobal.java @@ -118,6 +118,9 @@ public final class WindowManagerGlobal { private Runnable mSystemPropertyUpdater; + /** Default token to apply to added views. */ + private IBinder mDefaultToken; + private WindowManagerGlobal() { } @@ -169,6 +172,17 @@ public final class WindowManagerGlobal { } } + /** + * Sets the default token to use in {@link #addView} when no parent window + * token is available and no token has been explicitly set in the view's + * layout params. + * + * @param token Default window token to apply to added views. + */ + public void setDefaultToken(IBinder token) { + mDefaultToken = token; + } + public String[] getViewRootNames() { synchronized (mLock) { final int numRoots = mRoots.size(); @@ -216,6 +230,10 @@ public final class WindowManagerGlobal { } } + if (wparams.token == null && mDefaultToken != null) { + wparams.token = mDefaultToken; + } + ViewRootImpl root; View panelParentView = null; diff --git a/core/java/android/view/WindowManagerPolicy.java b/core/java/android/view/WindowManagerPolicy.java index 673f075..b8e94ee 100644 --- a/core/java/android/view/WindowManagerPolicy.java +++ b/core/java/android/view/WindowManagerPolicy.java @@ -865,12 +865,15 @@ public interface WindowManagerPolicy { * Return the insets for the areas covered by system windows. These values * are computed on the most recent layout, so they are not guaranteed to * be correct. - * + * * @param attrs The LayoutParams of the window. - * @param contentInset The areas covered by system windows, expressed as positive insets - * + * @param outContentInsets The areas covered by system windows, expressed as positive insets. + * @param outStableInsets The areas covered by stable system windows irrespective of their + * current visibility. Expressed as positive insets. + * */ - public void getContentInsetHintLw(WindowManager.LayoutParams attrs, Rect contentInset); + public void getInsetHintLw(WindowManager.LayoutParams attrs, Rect outContentInsets, + Rect outStableInsets); /** * Called when layout of the windows is finished. After this function has diff --git a/core/java/android/view/accessibility/AccessibilityNodeInfo.java b/core/java/android/view/accessibility/AccessibilityNodeInfo.java index 3987fbc..b5afdf7 100644 --- a/core/java/android/view/accessibility/AccessibilityNodeInfo.java +++ b/core/java/android/view/accessibility/AccessibilityNodeInfo.java @@ -547,6 +547,8 @@ public class AccessibilityNodeInfo implements Parcelable { private long mParentNodeId = ROOT_NODE_ID; private long mLabelForId = ROOT_NODE_ID; private long mLabeledById = ROOT_NODE_ID; + private long mTraversalBefore = ROOT_NODE_ID; + private long mTraversalAfter = ROOT_NODE_ID; private int mBooleanProperties; private final Rect mBoundsInParent = new Rect(); @@ -1046,6 +1048,126 @@ public class AccessibilityNodeInfo implements Parcelable { } /** + * Gets the node before which this one is visited during traversal. A screen-reader + * must visit the content of this node before the content of the one it precedes. + * + * @return The succeeding node if such or <code>null</code>. + * + * @see #setTraversalBefore(android.view.View) + * @see #setTraversalBefore(android.view.View, int) + */ + public AccessibilityNodeInfo getTraversalBefore() { + enforceSealed(); + return getNodeForAccessibilityId(mTraversalBefore); + } + + /** + * Sets the view before whose node this one should be visited during traversal. A + * screen-reader must visit the content of this node before the content of the one + * it precedes. + * <p> + * <strong>Note:</strong> Cannot be called from an + * {@link android.accessibilityservice.AccessibilityService}. + * This class is made immutable before being delivered to an AccessibilityService. + * </p> + * + * @param view The view providing the preceding node. + * + * @see #getTraversalBefore() + */ + public void setTraversalBefore(View view) { + setTraversalBefore(view, UNDEFINED_ITEM_ID); + } + + /** + * Sets the node before which this one is visited during traversal. A screen-reader + * must visit the content of this node before the content of the one it precedes. + * The successor is a virtual descendant of the given <code>root</code>. If + * <code>virtualDescendantId</code> equals to {@link View#NO_ID} the root is set + * as the successor. + * <p> + * A virtual descendant is an imaginary View that is reported as a part of the view + * hierarchy for accessibility purposes. This enables custom views that draw complex + * content to report them selves as a tree of virtual views, thus conveying their + * logical structure. + * </p> + * <p> + * <strong>Note:</strong> Cannot be called from an + * {@link android.accessibilityservice.AccessibilityService}. + * This class is made immutable before being delivered to an AccessibilityService. + * </p> + * + * @param root The root of the virtual subtree. + * @param virtualDescendantId The id of the virtual descendant. + */ + public void setTraversalBefore(View root, int virtualDescendantId) { + enforceNotSealed(); + final int rootAccessibilityViewId = (root != null) + ? root.getAccessibilityViewId() : UNDEFINED_ITEM_ID; + mTraversalBefore = makeNodeId(rootAccessibilityViewId, virtualDescendantId); + } + + /** + * Gets the node after which this one is visited in accessibility traversal. + * A screen-reader must visit the content of the other node before the content + * of this one. + * + * @return The succeeding node if such or <code>null</code>. + * + * @see #setTraversalAfter(android.view.View) + * @see #setTraversalAfter(android.view.View, int) + */ + public AccessibilityNodeInfo getTraversalAfter() { + enforceSealed(); + return getNodeForAccessibilityId(mTraversalAfter); + } + + /** + * Sets the view whose node is visited after this one in accessibility traversal. + * A screen-reader must visit the content of the other node before the content + * of this one. + * <p> + * <strong>Note:</strong> Cannot be called from an + * {@link android.accessibilityservice.AccessibilityService}. + * This class is made immutable before being delivered to an AccessibilityService. + * </p> + * + * @param view The previous view. + * + * @see #getTraversalAfter() + */ + public void setTraversalAfter(View view) { + setTraversalAfter(view, UNDEFINED_ITEM_ID); + } + + /** + * Sets the node after which this one is visited in accessibility traversal. + * A screen-reader must visit the content of the other node before the content + * of this one. If <code>virtualDescendantId</code> equals to {@link View#NO_ID} + * the root is set as the predecessor. + * <p> + * A virtual descendant is an imaginary View that is reported as a part of the view + * hierarchy for accessibility purposes. This enables custom views that draw complex + * content to report them selves as a tree of virtual views, thus conveying their + * logical structure. + * </p> + * <p> + * <strong>Note:</strong> Cannot be called from an + * {@link android.accessibilityservice.AccessibilityService}. + * This class is made immutable before being delivered to an AccessibilityService. + * </p> + * + * @param root The root of the virtual subtree. + * @param virtualDescendantId The id of the virtual descendant. + */ + public void setTraversalAfter(View root, int virtualDescendantId) { + enforceNotSealed(); + final int rootAccessibilityViewId = (root != null) + ? root.getAccessibilityViewId() : UNDEFINED_ITEM_ID; + mTraversalAfter = makeNodeId(rootAccessibilityViewId, virtualDescendantId); + } + + /** * Sets the maximum text length, or -1 for no limit. * <p> * Typically used to indicate that an editable text field has a limit on @@ -1229,13 +1351,7 @@ public class AccessibilityNodeInfo implements Parcelable { */ public AccessibilityNodeInfo getParent() { enforceSealed(); - if (!canPerformRequestOverConnection(mParentNodeId)) { - return null; - } - AccessibilityInteractionClient client = AccessibilityInteractionClient.getInstance(); - return client.findAccessibilityNodeInfoByAccessibilityId(mConnectionId, - mWindowId, mParentNodeId, false, FLAG_PREFETCH_PREDECESSORS - | FLAG_PREFETCH_DESCENDANTS | FLAG_PREFETCH_SIBLINGS); + return getNodeForAccessibilityId(mParentNodeId); } /** @@ -2055,13 +2171,7 @@ public class AccessibilityNodeInfo implements Parcelable { */ public AccessibilityNodeInfo getLabelFor() { enforceSealed(); - if (!canPerformRequestOverConnection(mLabelForId)) { - return null; - } - AccessibilityInteractionClient client = AccessibilityInteractionClient.getInstance(); - return client.findAccessibilityNodeInfoByAccessibilityId(mConnectionId, - mWindowId, mLabelForId, false, FLAG_PREFETCH_PREDECESSORS - | FLAG_PREFETCH_DESCENDANTS | FLAG_PREFETCH_SIBLINGS); + return getNodeForAccessibilityId(mLabelForId); } /** @@ -2113,13 +2223,7 @@ public class AccessibilityNodeInfo implements Parcelable { */ public AccessibilityNodeInfo getLabeledBy() { enforceSealed(); - if (!canPerformRequestOverConnection(mLabeledById)) { - return null; - } - AccessibilityInteractionClient client = AccessibilityInteractionClient.getInstance(); - return client.findAccessibilityNodeInfoByAccessibilityId(mConnectionId, - mWindowId, mLabeledById, false, FLAG_PREFETCH_PREDECESSORS - | FLAG_PREFETCH_DESCENDANTS | FLAG_PREFETCH_SIBLINGS); + return getNodeForAccessibilityId(mLabeledById); } /** @@ -2453,6 +2557,9 @@ public class AccessibilityNodeInfo implements Parcelable { parcel.writeLong(mParentNodeId); parcel.writeLong(mLabelForId); parcel.writeLong(mLabeledById); + parcel.writeLong(mTraversalBefore); + parcel.writeLong(mTraversalAfter); + parcel.writeInt(mConnectionId); final LongArray childIds = mChildNodeIds; @@ -2571,6 +2678,8 @@ public class AccessibilityNodeInfo implements Parcelable { mParentNodeId = other.mParentNodeId; mLabelForId = other.mLabelForId; mLabeledById = other.mLabeledById; + mTraversalBefore = other.mTraversalBefore; + mTraversalAfter = other.mTraversalAfter; mWindowId = other.mWindowId; mConnectionId = other.mConnectionId; mBoundsInParent.set(other.mBoundsInParent); @@ -2633,6 +2742,9 @@ public class AccessibilityNodeInfo implements Parcelable { mParentNodeId = parcel.readLong(); mLabelForId = parcel.readLong(); mLabeledById = parcel.readLong(); + mTraversalBefore = parcel.readLong(); + mTraversalAfter = parcel.readLong(); + mConnectionId = parcel.readInt(); final int childrenSize = parcel.readInt(); @@ -2725,6 +2837,8 @@ public class AccessibilityNodeInfo implements Parcelable { mParentNodeId = ROOT_NODE_ID; mLabelForId = ROOT_NODE_ID; mLabeledById = ROOT_NODE_ID; + mTraversalBefore = ROOT_NODE_ID; + mTraversalAfter = ROOT_NODE_ID; mWindowId = UNDEFINED_ITEM_ID; mConnectionId = UNDEFINED_CONNECTION_ID; mMaxTextLength = -1; @@ -2911,6 +3025,8 @@ public class AccessibilityNodeInfo implements Parcelable { builder.append("; accessibilityViewId: " + getAccessibilityViewId(mSourceNodeId)); builder.append("; virtualDescendantId: " + getVirtualDescendantId(mSourceNodeId)); builder.append("; mParentNodeId: " + mParentNodeId); + builder.append("; traversalBefore: ").append(mTraversalBefore); + builder.append("; traversalAfter: ").append(mTraversalAfter); int granularities = mMovementGranularities; builder.append("; MovementGranularities: ["); @@ -2963,6 +3079,16 @@ public class AccessibilityNodeInfo implements Parcelable { return builder.toString(); } + private AccessibilityNodeInfo getNodeForAccessibilityId(long accessibilityId) { + if (!canPerformRequestOverConnection(accessibilityId)) { + return null; + } + AccessibilityInteractionClient client = AccessibilityInteractionClient.getInstance(); + return client.findAccessibilityNodeInfoByAccessibilityId(mConnectionId, + mWindowId, accessibilityId, false, FLAG_PREFETCH_PREDECESSORS + | FLAG_PREFETCH_DESCENDANTS | FLAG_PREFETCH_SIBLINGS); + } + /** * A class defining an action that can be performed on an {@link AccessibilityNodeInfo}. * Each action has a unique id that is mandatory and optional data. diff --git a/core/java/android/widget/AbsListView.java b/core/java/android/widget/AbsListView.java index 4aebaae..6927660 100644 --- a/core/java/android/widget/AbsListView.java +++ b/core/java/android/widget/AbsListView.java @@ -2663,7 +2663,7 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te * @return True if the selector should be shown */ boolean shouldShowSelector() { - return (!isInTouchMode()) || (touchModeDrawsInPressedState() && isPressed()); + return (isFocused() && !isInTouchMode()) || (touchModeDrawsInPressedState() && isPressed()); } private void drawSelector(Canvas canvas) { @@ -4910,9 +4910,7 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te if (position >= headerViewsCount && position < footerViewsStart) { // The view will be rebound to new data, clear any // system-managed transient state. - if (child.isAccessibilityFocused()) { - child.clearAccessibilityFocus(); - } + child.clearAccessibilityFocus(); mRecycler.addScrapView(child, position); } } @@ -4933,9 +4931,7 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te if (position >= headerViewsCount && position < footerViewsStart) { // The view will be rebound to new data, clear any // system-managed transient state. - if (child.isAccessibilityFocused()) { - child.clearAccessibilityFocus(); - } + child.clearAccessibilityFocus(); mRecycler.addScrapView(child, position); } } @@ -6776,9 +6772,7 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te } private void clearAccessibilityFromScrap(View view) { - if (view.isAccessibilityFocused()) { - view.clearAccessibilityFocus(); - } + view.clearAccessibilityFocus(); view.setAccessibilityDelegate(null); } diff --git a/core/java/android/widget/AbsSeekBar.java b/core/java/android/widget/AbsSeekBar.java index b2cfdf7..d39960f 100644 --- a/core/java/android/widget/AbsSeekBar.java +++ b/core/java/android/widget/AbsSeekBar.java @@ -267,6 +267,12 @@ public abstract class AbsSeekBar extends ProgressBar { if (mHasThumbTintMode) { mThumb.setTintMode(mThumbTintMode); } + + // The drawable (or one of its children) may not have been + // stateful before applying the tint, so let's try again. + if (mThumb.isStateful()) { + mThumb.setState(getDrawableState()); + } } } diff --git a/core/java/android/widget/ActionMenuView.java b/core/java/android/widget/ActionMenuView.java index 7198e52..0a8a01f 100644 --- a/core/java/android/widget/ActionMenuView.java +++ b/core/java/android/widget/ActionMenuView.java @@ -429,7 +429,7 @@ public class ActionMenuView extends LinearLayout implements MenuBuilder.ItemInvo } final int childCount = getChildCount(); - final int midVertical = (top + bottom) / 2; + final int midVertical = (bottom - top) / 2; final int dividerWidth = getDividerWidth(); int overflowWidth = 0; int nonOverflowWidth = 0; diff --git a/core/java/android/widget/CheckedTextView.java b/core/java/android/widget/CheckedTextView.java index eb8e8aa..69969a9 100644 --- a/core/java/android/widget/CheckedTextView.java +++ b/core/java/android/widget/CheckedTextView.java @@ -267,6 +267,12 @@ public class CheckedTextView extends TextView implements Checkable { if (mHasCheckMarkTintMode) { mCheckMarkDrawable.setTintMode(mCheckMarkTintMode); } + + // The drawable (or one of its children) may not have been + // stateful before applying the tint, so let's try again. + if (mCheckMarkDrawable.isStateful()) { + mCheckMarkDrawable.setState(getDrawableState()); + } } } diff --git a/core/java/android/widget/CompoundButton.java b/core/java/android/widget/CompoundButton.java index 092e31c..447ccc2 100644 --- a/core/java/android/widget/CompoundButton.java +++ b/core/java/android/widget/CompoundButton.java @@ -315,6 +315,12 @@ public abstract class CompoundButton extends Button implements Checkable { if (mHasButtonTintMode) { mButtonDrawable.setTintMode(mButtonTintMode); } + + // The drawable (or one of its children) may not have been + // stateful before applying the tint, so let's try again. + if (mButtonDrawable.isStateful()) { + mButtonDrawable.setState(getDrawableState()); + } } } diff --git a/core/java/android/widget/DatePickerCalendarDelegate.java b/core/java/android/widget/DatePickerCalendarDelegate.java index 64c81e0..cf3dbab 100644 --- a/core/java/android/widget/DatePickerCalendarDelegate.java +++ b/core/java/android/widget/DatePickerCalendarDelegate.java @@ -183,8 +183,11 @@ class DatePickerCalendarDelegate extends DatePicker.AbstractDatePickerDelegate i mHeaderYearTextView.getTextColors(), R.attr.state_selected, headerSelectedTextColor)); - mDayPickerView = new DayPickerView(mContext, this); + mDayPickerView = new DayPickerView(mContext); + mDayPickerView.setFirstDayOfWeek(mFirstDayOfWeek); mDayPickerView.setRange(mMinDate, mMaxDate); + mDayPickerView.setDay(mCurrentDate); + mDayPickerView.setOnDaySelectedListener(mOnDaySelectedListener); mYearPickerView = new YearPickerView(mContext); mYearPickerView.init(this); @@ -333,7 +336,7 @@ class DatePickerCalendarDelegate extends DatePicker.AbstractDatePickerDelegate i switch (viewIndex) { case MONTH_AND_DAY_VIEW: - mDayPickerView.onDateChanged(); + mDayPickerView.setDay(getSelectedDay()); if (mCurrentView != viewIndex) { mMonthAndDayLayout.setSelected(true); mHeaderYearTextView.setSelected(false); @@ -445,6 +448,8 @@ class DatePickerCalendarDelegate extends DatePicker.AbstractDatePickerDelegate i @Override public void setFirstDayOfWeek(int firstDayOfWeek) { mFirstDayOfWeek = firstDayOfWeek; + + mDayPickerView.setFirstDayOfWeek(firstDayOfWeek); } @Override @@ -606,19 +611,12 @@ class DatePickerCalendarDelegate extends DatePicker.AbstractDatePickerDelegate i } } - @Override - public void onDayOfMonthSelected(int year, int month, int day) { - mCurrentDate.set(Calendar.YEAR, year); - mCurrentDate.set(Calendar.MONTH, month); - mCurrentDate.set(Calendar.DAY_OF_MONTH, day); - updatePickers(); - updateDisplay(true); - } - private void updatePickers() { for (OnDateChangedListener listener : mListeners) { listener.onDateChanged(); } + + mDayPickerView.setDay(getSelectedDay()); } @Override @@ -627,11 +625,6 @@ class DatePickerCalendarDelegate extends DatePicker.AbstractDatePickerDelegate i } @Override - public void unregisterOnDateChangedListener(OnDateChangedListener listener) { - mListeners.remove(listener); - } - - @Override public Calendar getSelectedDay() { return mCurrentDate; } @@ -652,6 +645,22 @@ class DatePickerCalendarDelegate extends DatePicker.AbstractDatePickerDelegate i } /** + * Listener called when the user selects a day in the day picker view. + */ + private final DayPickerView.OnDaySelectedListener + mOnDaySelectedListener = new DayPickerView.OnDaySelectedListener() { + @Override + public void onDaySelected(DayPickerView view, Calendar day) { + mCurrentDate.setTimeInMillis(day.getTimeInMillis()); + + updatePickers(); + updateDisplay(true); + + tryVibrate(); + } + }; + + /** * Class for managing state storing/restoring. */ private static class SavedState extends View.BaseSavedState { diff --git a/core/java/android/widget/DatePickerController.java b/core/java/android/widget/DatePickerController.java index ea6ec61..8f809ba 100644 --- a/core/java/android/widget/DatePickerController.java +++ b/core/java/android/widget/DatePickerController.java @@ -27,16 +27,9 @@ interface DatePickerController { void onYearSelected(int year); - void onDayOfMonthSelected(int year, int month, int day); - void registerOnDateChangedListener(OnDateChangedListener listener); - void unregisterOnDateChangedListener(OnDateChangedListener listener); - Calendar getSelectedDay(); - void setFirstDayOfWeek(int firstDayOfWeek); - int getFirstDayOfWeek(); - void tryVibrate(); } diff --git a/core/java/android/widget/DateTimeView.java b/core/java/android/widget/DateTimeView.java index 45d1403..443884a 100644 --- a/core/java/android/widget/DateTimeView.java +++ b/core/java/android/widget/DateTimeView.java @@ -32,6 +32,7 @@ import android.widget.RemoteViews.RemoteView; import java.text.DateFormat; import java.text.SimpleDateFormat; +import java.util.ArrayList; import java.util.Date; // @@ -62,8 +63,8 @@ public class DateTimeView extends TextView { int mLastDisplay = -1; DateFormat mLastFormat; - private boolean mAttachedToWindow; private long mUpdateTimeMillis; + private static final ThreadLocal<ReceiverInfo> sReceiverInfo = new ThreadLocal<ReceiverInfo>(); public DateTimeView(Context context) { super(context); @@ -76,15 +77,21 @@ public class DateTimeView extends TextView { @Override protected void onAttachedToWindow() { super.onAttachedToWindow(); - registerReceivers(); - mAttachedToWindow = true; + ReceiverInfo ri = sReceiverInfo.get(); + if (ri == null) { + ri = new ReceiverInfo(); + sReceiverInfo.set(ri); + } + ri.addView(this); } @Override protected void onDetachedFromWindow() { super.onDetachedFromWindow(); - unregisterReceivers(); - mAttachedToWindow = false; + final ReceiverInfo ri = sReceiverInfo.get(); + if (ri != null) { + ri.removeView(this); + } } @android.view.RemotableViewMethod @@ -204,49 +211,86 @@ public class DateTimeView extends TextView { } } - private void registerReceivers() { - Context context = getContext(); + void clearFormatAndUpdate() { + mLastFormat = null; + update(); + } - IntentFilter filter = new IntentFilter(); - filter.addAction(Intent.ACTION_TIME_TICK); - filter.addAction(Intent.ACTION_TIME_CHANGED); - filter.addAction(Intent.ACTION_CONFIGURATION_CHANGED); - filter.addAction(Intent.ACTION_TIMEZONE_CHANGED); - context.registerReceiver(mBroadcastReceiver, filter); + private static class ReceiverInfo { + private final ArrayList<DateTimeView> mAttachedViews = new ArrayList<DateTimeView>(); + private final BroadcastReceiver mReceiver = new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + String action = intent.getAction(); + if (Intent.ACTION_TIME_TICK.equals(action)) { + if (System.currentTimeMillis() < getSoonestUpdateTime()) { + // The update() function takes a few milliseconds to run because of + // all of the time conversions it needs to do, so we can't do that + // every minute. + return; + } + } + // ACTION_TIME_CHANGED can also signal a change of 12/24 hr. format. + updateAll(); + } + }; - Uri uri = Settings.System.getUriFor(Settings.System.DATE_FORMAT); - context.getContentResolver().registerContentObserver(uri, true, mContentObserver); - } + private final ContentObserver mObserver = new ContentObserver(new Handler()) { + @Override + public void onChange(boolean selfChange) { + updateAll(); + } + }; - private void unregisterReceivers() { - Context context = getContext(); - context.unregisterReceiver(mBroadcastReceiver); - context.getContentResolver().unregisterContentObserver(mContentObserver); - } + public void addView(DateTimeView v) { + final boolean register = mAttachedViews.isEmpty(); + mAttachedViews.add(v); + if (register) { + register(v.getContext().getApplicationContext()); + } + } - private BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() { - @Override - public void onReceive(Context context, Intent intent) { - String action = intent.getAction(); - if (Intent.ACTION_TIME_TICK.equals(action)) { - if (System.currentTimeMillis() < mUpdateTimeMillis) { - // The update() function takes a few milliseconds to run because of - // all of the time conversions it needs to do, so we can't do that - // every minute. - return; + public void removeView(DateTimeView v) { + mAttachedViews.remove(v); + if (mAttachedViews.isEmpty()) { + unregister(v.getContext().getApplicationContext()); + } + } + + void updateAll() { + final int count = mAttachedViews.size(); + for (int i = 0; i < count; i++) { + mAttachedViews.get(i).clearFormatAndUpdate(); + } + } + + long getSoonestUpdateTime() { + long result = Long.MAX_VALUE; + final int count = mAttachedViews.size(); + for (int i = 0; i < count; i++) { + final long time = mAttachedViews.get(i).mUpdateTimeMillis; + if (time < result) { + result = time; } } - // ACTION_TIME_CHANGED can also signal a change of 12/24 hr. format. - mLastFormat = null; - update(); + return result; } - }; - private ContentObserver mContentObserver = new ContentObserver(new Handler()) { - @Override - public void onChange(boolean selfChange) { - mLastFormat = null; - update(); + void register(Context context) { + final IntentFilter filter = new IntentFilter(); + filter.addAction(Intent.ACTION_TIME_TICK); + filter.addAction(Intent.ACTION_TIME_CHANGED); + filter.addAction(Intent.ACTION_CONFIGURATION_CHANGED); + filter.addAction(Intent.ACTION_TIMEZONE_CHANGED); + context.registerReceiver(mReceiver, filter); + + final Uri uri = Settings.System.getUriFor(Settings.System.DATE_FORMAT); + context.getContentResolver().registerContentObserver(uri, true, mObserver); } - }; + + void unregister(Context context) { + context.unregisterReceiver(mReceiver); + context.getContentResolver().unregisterContentObserver(mObserver); + } + } } diff --git a/core/java/android/widget/DayPickerView.java b/core/java/android/widget/DayPickerView.java index fcf66f6..6cb1c9d 100644 --- a/core/java/android/widget/DayPickerView.java +++ b/core/java/android/widget/DayPickerView.java @@ -16,14 +16,10 @@ package android.widget; -import android.annotation.SuppressLint; import android.content.Context; import android.content.res.ColorStateList; import android.content.res.Configuration; -import android.os.Build; import android.os.Bundle; -import android.os.Handler; -import android.util.AttributeSet; import android.util.Log; import android.util.MathUtils; import android.view.View; @@ -38,9 +34,7 @@ import java.util.Locale; /** * This displays a list of months in a calendar format with selectable days. */ -class DayPickerView extends ListView implements AbsListView.OnScrollListener, - OnDateChangedListener { - +class DayPickerView extends ListView implements AbsListView.OnScrollListener { private static final String TAG = "DayPickerView"; // How long the GoTo fling animation should last @@ -49,12 +43,14 @@ class DayPickerView extends ListView implements AbsListView.OnScrollListener, // How long to wait after receiving an onScrollStateChanged notification before acting on it private static final int SCROLL_CHANGE_DELAY = 40; - private static int LIST_TOP_OFFSET = -1; // so that the top line will be under the separator + // so that the top line will be under the separator + private static final int LIST_TOP_OFFSET = -1; - private SimpleDateFormat mYearFormat = new SimpleDateFormat("yyyy", Locale.getDefault()); + private final SimpleMonthAdapter mAdapter = new SimpleMonthAdapter(getContext()); + + private final ScrollStateRunnable mScrollStateChangedRunnable = new ScrollStateRunnable(this); - // These affect the scroll speed and feel - private float mFriction = 1.0f; + private SimpleDateFormat mYearFormat = new SimpleDateFormat("yyyy", Locale.getDefault()); // highlighted time private Calendar mSelectedDay = Calendar.getInstance(); @@ -62,7 +58,7 @@ class DayPickerView extends ListView implements AbsListView.OnScrollListener, private Calendar mMinDate = Calendar.getInstance(); private Calendar mMaxDate = Calendar.getInstance(); - private SimpleMonthAdapter mAdapter; + private OnDaySelectedListener mOnDaySelectedListener; // which month should be displayed/highlighted [0-11] private int mCurrentMonthDisplayed; @@ -71,34 +67,27 @@ class DayPickerView extends ListView implements AbsListView.OnScrollListener, // used for tracking what state listview is in private int mCurrentScrollState = OnScrollListener.SCROLL_STATE_IDLE; - private DatePickerController mController; private boolean mPerformingScroll; - private ScrollStateRunnable mScrollStateChangedRunnable = new ScrollStateRunnable(this); - - public DayPickerView(Context context, DatePickerController controller) { + public DayPickerView(Context context) { super(context); - init(); - setController(controller); - } - - public void setController(DatePickerController controller) { - if (mController != null) { - mController.unregisterOnDateChangedListener(this); - } - mController = controller; - mController.registerOnDateChangedListener(this); - setUpAdapter(); setAdapter(mAdapter); - onDateChanged(); - } - - public void init() { setLayoutParams(new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT)); setDrawSelectorOnTop(false); - setUpListView(); + + goTo(mSelectedDay, false, true, true); + + mAdapter.setOnDaySelectedListener(mProxyOnDaySelectedListener); + } + + public void setDay(Calendar day) { + goTo(day, false, true, true); + } + + public void setFirstDayOfWeek(int firstDayOfWeek) { + mAdapter.setFirstDayOfWeek(firstDayOfWeek); } public void setRange(Calendar minDate, Calendar maxDate) { @@ -107,60 +96,25 @@ class DayPickerView extends ListView implements AbsListView.OnScrollListener, mAdapter.setRange(mMinDate, mMaxDate); - if (constrainCalendar(mSelectedDay, mMinDate, mMaxDate)) { - goTo(mSelectedDay, false, true, true); - } + // Changing the min/max date changes the selection position since we + // don't really have stable IDs. + goTo(mSelectedDay, false, true, true); } /** - * Constrains the supplied calendar to stay within the min and max - * calendars, returning <code>true</code> if the supplied calendar - * was modified. + * Sets the listener to call when the user selects a day. * - * @param value The calendar to constrain - * @param min The minimum calendar - * @param max The maximum calendar - * @return True if <code>value</code> was modified - */ - private boolean constrainCalendar(Calendar value, Calendar min, Calendar max) { - if (value.compareTo(min) < 0) { - value.setTimeInMillis(min.getTimeInMillis()); - return true; - } - - if (value.compareTo(max) > 0) { - value.setTimeInMillis(max.getTimeInMillis()); - return true; - } - - return false; - } - - public void onChange() { - setUpAdapter(); - setAdapter(mAdapter); - } - - /** - * Creates a new adapter if necessary and sets up its parameters. Override - * this method to provide a custom adapter. + * @param listener The listener to call. */ - protected void setUpAdapter() { - if (mAdapter == null) { - mAdapter = new SimpleMonthAdapter(getContext(), mController); - } else { - mAdapter.setSelectedDay(mSelectedDay); - mAdapter.notifyDataSetChanged(); - } - // refresh the view with the new parameters - mAdapter.notifyDataSetChanged(); + public void setOnDaySelectedListener(OnDaySelectedListener listener) { + mOnDaySelectedListener = listener; } /* * Sets all the required fields for the list view. Override this method to * set a different list view behavior. */ - protected void setUpListView() { + private void setUpListView() { // Transparent background on scroll setCacheColorHint(0); // No dividers @@ -173,7 +127,7 @@ class DayPickerView extends ListView implements AbsListView.OnScrollListener, setOnScrollListener(this); setFadingEdgeLength(0); // Make the scrolling behavior nicer - setFriction(ViewConfiguration.getScrollFriction() * mFriction); + setFriction(ViewConfiguration.getScrollFriction()); } private int getDiffMonths(Calendar start, Calendar end) { @@ -203,7 +157,7 @@ class DayPickerView extends ListView implements AbsListView.OnScrollListener, * visible * @return Whether or not the view animated to the new location */ - public boolean goTo(Calendar day, boolean animate, boolean setSelected, boolean forceScroll) { + private boolean goTo(Calendar day, boolean animate, boolean setSelected, boolean forceScroll) { // Set the selected day if (setSelected) { @@ -392,11 +346,6 @@ class DayPickerView extends ListView implements AbsListView.OnScrollListener, return firstPosition + mostVisibleIndex; } - @Override - public void onDateChanged() { - goTo(mController.getSelectedDay(), false, true, true); - } - /** * Attempts to return the date that has accessibility focus. * @@ -529,4 +478,18 @@ class DayPickerView extends ListView implements AbsListView.OnScrollListener, mPerformingScroll = true; return true; } + + public interface OnDaySelectedListener { + public void onDaySelected(DayPickerView view, Calendar day); + } + + private final SimpleMonthAdapter.OnDaySelectedListener + mProxyOnDaySelectedListener = new SimpleMonthAdapter.OnDaySelectedListener() { + @Override + public void onDaySelected(SimpleMonthAdapter adapter, Calendar day) { + if (mOnDaySelectedListener != null) { + mOnDaySelectedListener.onDaySelected(DayPickerView.this, day); + } + } + }; } diff --git a/core/java/android/widget/FrameLayout.java b/core/java/android/widget/FrameLayout.java index e317524..d974c29 100644 --- a/core/java/android/widget/FrameLayout.java +++ b/core/java/android/widget/FrameLayout.java @@ -384,6 +384,12 @@ public class FrameLayout extends ViewGroup { if (mHasForegroundTintMode) { mForeground.setTintMode(mForegroundTintMode); } + + // The drawable (or one of its children) may not have been + // stateful before applying the tint, so let's try again. + if (mForeground.isStateful()) { + mForeground.setState(getDrawableState()); + } } } diff --git a/core/java/android/widget/ImageView.java b/core/java/android/widget/ImageView.java index 75dfcca..c68bfca 100644 --- a/core/java/android/widget/ImageView.java +++ b/core/java/android/widget/ImageView.java @@ -386,21 +386,21 @@ public class ImageView extends View { */ @android.view.RemotableViewMethod public void setImageResource(int resId) { - if (mUri != null || mResource != resId) { - final int oldWidth = mDrawableWidth; - final int oldHeight = mDrawableHeight; + // The resource configuration may have changed, so we should always + // try to load the resource even if the resId hasn't changed. + final int oldWidth = mDrawableWidth; + final int oldHeight = mDrawableHeight; - updateDrawable(null); - mResource = resId; - mUri = null; + updateDrawable(null); + mResource = resId; + mUri = null; - resolveUri(); + resolveUri(); - if (oldWidth != mDrawableWidth || oldHeight != mDrawableHeight) { - requestLayout(); - } - invalidate(); + if (oldWidth != mDrawableWidth || oldHeight != mDrawableHeight) { + requestLayout(); } + invalidate(); } /** @@ -527,6 +527,12 @@ public class ImageView extends View { if (mHasDrawableTintMode) { mDrawable.setTintMode(mDrawableTintMode); } + + // The drawable (or one of its children) may not have been + // stateful before applying the tint, so let's try again. + if (mDrawable.isStateful()) { + mDrawable.setState(getDrawableState()); + } } } @@ -820,6 +826,7 @@ public class ImageView extends View { mDrawableHeight = d.getIntrinsicHeight(); applyImageTint(); applyColorMod(); + configureBounds(); } else { mDrawableWidth = mDrawableHeight = -1; diff --git a/core/java/android/widget/ListPopupWindow.java b/core/java/android/widget/ListPopupWindow.java index 9f540c0..a31d37e 100644 --- a/core/java/android/widget/ListPopupWindow.java +++ b/core/java/android/widget/ListPopupWindow.java @@ -1252,14 +1252,7 @@ public class ListPopupWindow { final boolean wasForwarding = mForwarding; final boolean forwarding; if (wasForwarding) { - if (mWasLongPress) { - // If we started forwarding as a result of a long-press, - // just silently stop forwarding events so that the window - // stays open. - forwarding = onTouchForwarded(event); - } else { - forwarding = onTouchForwarded(event) || !onForwardingStopped(); - } + forwarding = onTouchForwarded(event) || !onForwardingStopped(); } else { forwarding = onTouchObserved(event) && onForwardingStarted(); diff --git a/core/java/android/widget/OverScroller.java b/core/java/android/widget/OverScroller.java index 7b3dd31..a40d4f8 100644 --- a/core/java/android/widget/OverScroller.java +++ b/core/java/android/widget/OverScroller.java @@ -904,6 +904,10 @@ public class OverScroller { final long time = AnimationUtils.currentAnimationTimeMillis(); final long currentTime = time - mStartTime; + if (currentTime == 0) { + // Skip work but report that we're still going if we have a nonzero duration. + return mDuration > 0; + } if (currentTime > mDuration) { return false; } diff --git a/core/java/android/widget/PopupWindow.java b/core/java/android/widget/PopupWindow.java index 54a7940..396c0b9 100644 --- a/core/java/android/widget/PopupWindow.java +++ b/core/java/android/widget/PopupWindow.java @@ -198,54 +198,17 @@ public class PopupWindow { mWindowManager = (WindowManager)context.getSystemService(Context.WINDOW_SERVICE); final TypedArray a = context.obtainStyledAttributes( - attrs, com.android.internal.R.styleable.PopupWindow, defStyleAttr, defStyleRes); - - mBackground = a.getDrawable(R.styleable.PopupWindow_popupBackground); + attrs, R.styleable.PopupWindow, defStyleAttr, defStyleRes); + final Drawable bg = a.getDrawable(R.styleable.PopupWindow_popupBackground); mElevation = a.getDimension(R.styleable.PopupWindow_popupElevation, 0); mOverlapAnchor = a.getBoolean(R.styleable.PopupWindow_overlapAnchor, false); final int animStyle = a.getResourceId(R.styleable.PopupWindow_popupAnimationStyle, -1); - mAnimationStyle = animStyle == com.android.internal.R.style.Animation_PopupWindow ? -1 : - animStyle; + mAnimationStyle = animStyle == R.style.Animation_PopupWindow ? -1 : animStyle; - // If this is a StateListDrawable, try to find and store the drawable to be - // used when the drop-down is placed above its anchor view, and the one to be - // used when the drop-down is placed below its anchor view. We extract - // the drawables ourselves to work around a problem with using refreshDrawableState - // that it will take into account the padding of all drawables specified in a - // StateListDrawable, thus adding superfluous padding to drop-down views. - // - // We assume a StateListDrawable will have a drawable for ABOVE_ANCHOR_STATE_SET and - // at least one other drawable, intended for the 'below-anchor state'. - if (mBackground instanceof StateListDrawable) { - StateListDrawable background = (StateListDrawable) mBackground; - - // Find the above-anchor view - this one's easy, it should be labeled as such. - int aboveAnchorStateIndex = background.getStateDrawableIndex(ABOVE_ANCHOR_STATE_SET); - - // Now, for the below-anchor view, look for any other drawable specified in the - // StateListDrawable which is not for the above-anchor state and use that. - int count = background.getStateCount(); - int belowAnchorStateIndex = -1; - for (int i = 0; i < count; i++) { - if (i != aboveAnchorStateIndex) { - belowAnchorStateIndex = i; - break; - } - } - - // Store the drawables we found, if we found them. Otherwise, set them both - // to null so that we'll just use refreshDrawableState. - if (aboveAnchorStateIndex != -1 && belowAnchorStateIndex != -1) { - mAboveAnchorBackgroundDrawable = background.getStateDrawable(aboveAnchorStateIndex); - mBelowAnchorBackgroundDrawable = background.getStateDrawable(belowAnchorStateIndex); - } else { - mBelowAnchorBackgroundDrawable = null; - mAboveAnchorBackgroundDrawable = null; - } - } - a.recycle(); + + setBackgroundDrawable(bg); } /** @@ -346,6 +309,43 @@ public class PopupWindow { */ public void setBackgroundDrawable(Drawable background) { mBackground = background; + + // If this is a StateListDrawable, try to find and store the drawable to be + // used when the drop-down is placed above its anchor view, and the one to be + // used when the drop-down is placed below its anchor view. We extract + // the drawables ourselves to work around a problem with using refreshDrawableState + // that it will take into account the padding of all drawables specified in a + // StateListDrawable, thus adding superfluous padding to drop-down views. + // + // We assume a StateListDrawable will have a drawable for ABOVE_ANCHOR_STATE_SET and + // at least one other drawable, intended for the 'below-anchor state'. + if (mBackground instanceof StateListDrawable) { + StateListDrawable stateList = (StateListDrawable) mBackground; + + // Find the above-anchor view - this one's easy, it should be labeled as such. + int aboveAnchorStateIndex = stateList.getStateDrawableIndex(ABOVE_ANCHOR_STATE_SET); + + // Now, for the below-anchor view, look for any other drawable specified in the + // StateListDrawable which is not for the above-anchor state and use that. + int count = stateList.getStateCount(); + int belowAnchorStateIndex = -1; + for (int i = 0; i < count; i++) { + if (i != aboveAnchorStateIndex) { + belowAnchorStateIndex = i; + break; + } + } + + // Store the drawables we found, if we found them. Otherwise, set them both + // to null so that we'll just use refreshDrawableState. + if (aboveAnchorStateIndex != -1 && belowAnchorStateIndex != -1) { + mAboveAnchorBackgroundDrawable = stateList.getStateDrawable(aboveAnchorStateIndex); + mBelowAnchorBackgroundDrawable = stateList.getStateDrawable(belowAnchorStateIndex); + } else { + mBelowAnchorBackgroundDrawable = null; + mAboveAnchorBackgroundDrawable = null; + } + } } /** diff --git a/core/java/android/widget/ProgressBar.java b/core/java/android/widget/ProgressBar.java index 1c190c3..887a93b 100644 --- a/core/java/android/widget/ProgressBar.java +++ b/core/java/android/widget/ProgressBar.java @@ -673,6 +673,12 @@ public class ProgressBar extends View { if (tintInfo.mHasIndeterminateTintMode) { mIndeterminateDrawable.setTintMode(tintInfo.mIndeterminateTintMode); } + + // The drawable (or one of its children) may not have been + // stateful before applying the tint, so let's try again. + if (mIndeterminateDrawable.isStateful()) { + mIndeterminateDrawable.setState(getDrawableState()); + } } } } @@ -781,6 +787,12 @@ public class ProgressBar extends View { if (mProgressTintInfo.mHasProgressTintMode) { target.setTintMode(mProgressTintInfo.mProgressTintMode); } + + // The drawable (or one of its children) may not have been + // stateful before applying the tint, so let's try again. + if (target.isStateful()) { + target.setState(getDrawableState()); + } } } } @@ -800,6 +812,12 @@ public class ProgressBar extends View { if (mProgressTintInfo.mHasProgressBackgroundTintMode) { target.setTintMode(mProgressTintInfo.mProgressBackgroundTintMode); } + + // The drawable (or one of its children) may not have been + // stateful before applying the tint, so let's try again. + if (target.isStateful()) { + target.setState(getDrawableState()); + } } } } @@ -819,6 +837,12 @@ public class ProgressBar extends View { if (mProgressTintInfo.mHasSecondaryProgressTintMode) { target.setTintMode(mProgressTintInfo.mSecondaryProgressTintMode); } + + // The drawable (or one of its children) may not have been + // stateful before applying the tint, so let's try again. + if (target.isStateful()) { + target.setState(getDrawableState()); + } } } } diff --git a/core/java/android/widget/RemoteViews.java b/core/java/android/widget/RemoteViews.java index 80f364b..dd7fa18 100644 --- a/core/java/android/widget/RemoteViews.java +++ b/core/java/android/widget/RemoteViews.java @@ -2528,6 +2528,26 @@ public class RemoteViews implements Parcelable, Filter { } /** + * Equivalent to calling {@link android.view.View#setAccessibilityTraversalBefore(int)}. + * + * @param viewId The id of the view whose before view in accessibility traversal to set. + * @param nextId The id of the next in the accessibility traversal. + **/ + public void setAccessibilityTraversalBefore(int viewId, int nextId) { + setInt(viewId, "setAccessibilityTraversalBefore", nextId); + } + + /** + * Equivalent to calling {@link android.view.View#setAccessibilityTraversalAfter(int)}. + * + * @param viewId The id of the view whose after view in accessibility traversal to set. + * @param nextId The id of the next in the accessibility traversal. + **/ + public void setAccessibilityTraversalAfter(int viewId, int nextId) { + setInt(viewId, "setAccessibilityTraversalAfter", nextId); + } + + /** * Equivalent to calling View.setLabelFor(int). * * @param viewId The id of the view whose property to set. diff --git a/core/java/android/widget/SimpleMonthAdapter.java b/core/java/android/widget/SimpleMonthAdapter.java index 5aa78c8..ecd2912 100644 --- a/core/java/android/widget/SimpleMonthAdapter.java +++ b/core/java/android/widget/SimpleMonthAdapter.java @@ -20,29 +20,28 @@ import android.content.Context; import android.content.res.ColorStateList; import android.view.View; import android.view.ViewGroup; +import android.widget.SimpleMonthView.OnDayClickListener; import java.util.Calendar; -import java.util.HashMap; /** * An adapter for a list of {@link android.widget.SimpleMonthView} items. */ -class SimpleMonthAdapter extends BaseAdapter implements SimpleMonthView.OnDayClickListener { +class SimpleMonthAdapter extends BaseAdapter { private final Calendar mMinDate = Calendar.getInstance(); private final Calendar mMaxDate = Calendar.getInstance(); private final Context mContext; - private final DatePickerController mController; private Calendar mSelectedDay; private ColorStateList mCalendarTextColors; + private OnDaySelectedListener mOnDaySelectedListener; - public SimpleMonthAdapter(Context context, DatePickerController controller) { - mContext = context; - mController = controller; + private int mFirstDayOfWeek; - init(); - setSelectedDay(mController.getSelectedDay()); + public SimpleMonthAdapter(Context context) { + mContext = context; + mSelectedDay = Calendar.getInstance(); } public void setRange(Calendar min, Calendar max) { @@ -52,27 +51,34 @@ class SimpleMonthAdapter extends BaseAdapter implements SimpleMonthView.OnDayCli notifyDataSetInvalidated(); } + public void setFirstDayOfWeek(int firstDayOfWeek) { + mFirstDayOfWeek = firstDayOfWeek; + + notifyDataSetInvalidated(); + } + /** * Updates the selected day and related parameters. * * @param day The day to highlight */ public void setSelectedDay(Calendar day) { - if (mSelectedDay != day) { - mSelectedDay = day; - notifyDataSetChanged(); - } - } + mSelectedDay = day; - void setCalendarTextColor(ColorStateList colors) { - mCalendarTextColors = colors; + notifyDataSetChanged(); } /** - * Set up the gesture detector and selected time + * Sets the listener to call when the user selects a day. + * + * @param listener The listener to call. */ - protected void init() { - mSelectedDay = Calendar.getInstance(); + public void setOnDaySelectedListener(OnDaySelectedListener listener) { + mOnDaySelectedListener = listener; + } + + void setCalendarTextColor(ColorStateList colors) { + mCalendarTextColors = colors; } @Override @@ -111,7 +117,7 @@ class SimpleMonthAdapter extends BaseAdapter implements SimpleMonthView.OnDayCli AbsListView.LayoutParams.MATCH_PARENT, AbsListView.LayoutParams.MATCH_PARENT); v.setLayoutParams(params); v.setClickable(true); - v.setOnDayClickListener(this); + v.setOnDayClickListener(mOnDayClickListener); if (mCalendarTextColors != null) { v.setTextColor(mCalendarTextColors); @@ -148,7 +154,7 @@ class SimpleMonthAdapter extends BaseAdapter implements SimpleMonthView.OnDayCli enabledDayRangeEnd = 31; } - v.setMonthParams(selectedDay, month, year, mController.getFirstDayOfWeek(), + v.setMonthParams(selectedDay, month, year, mFirstDayOfWeek, enabledDayRangeStart, enabledDayRangeEnd); v.invalidate(); @@ -159,27 +165,24 @@ class SimpleMonthAdapter extends BaseAdapter implements SimpleMonthView.OnDayCli return mSelectedDay.get(Calendar.YEAR) == year && mSelectedDay.get(Calendar.MONTH) == month; } - @Override - public void onDayClick(SimpleMonthView view, Calendar day) { - if (day != null && isCalendarInRange(day)) { - onDaySelected(day); - } - } - private boolean isCalendarInRange(Calendar value) { return value.compareTo(mMinDate) >= 0 && value.compareTo(mMaxDate) <= 0; } - /** - * Maintains the same hour/min/sec but moves the day to the tapped day. - * - * @param day The day that was tapped - */ - private void onDaySelected(Calendar day) { - mController.tryVibrate(); - mController.onDayOfMonthSelected(day.get(Calendar.YEAR), day.get(Calendar.MONTH), - day.get(Calendar.DAY_OF_MONTH)); + private final OnDayClickListener mOnDayClickListener = new OnDayClickListener() { + @Override + public void onDayClick(SimpleMonthView view, Calendar day) { + if (day != null && isCalendarInRange(day)) { + setSelectedDay(day); + + if (mOnDaySelectedListener != null) { + mOnDaySelectedListener.onDaySelected(SimpleMonthAdapter.this, day); + } + } + } + }; - setSelectedDay(day); + public interface OnDaySelectedListener { + public void onDaySelected(SimpleMonthAdapter view, Calendar day); } } diff --git a/core/java/android/widget/TimePickerClockDelegate.java b/core/java/android/widget/TimePickerClockDelegate.java index 78ee247..7d01321 100644 --- a/core/java/android/widget/TimePickerClockDelegate.java +++ b/core/java/android/widget/TimePickerClockDelegate.java @@ -607,23 +607,32 @@ class TimePickerClockDelegate extends TimePicker.AbstractTimePickerDelegate impl */ @Override public void onValueSelected(int pickerIndex, int newValue, boolean autoAdvance) { - if (pickerIndex == HOUR_INDEX) { - if (mAllowAutoAdvance && autoAdvance) { - updateHeaderHour(newValue, false); - setCurrentItemShowing(MINUTE_INDEX, true, false); - mDelegator.announceForAccessibility(newValue + ". " + mSelectMinutes); - } else { - updateHeaderHour(newValue, true); - } - } else if (pickerIndex == MINUTE_INDEX){ - updateHeaderMinute(newValue, true); - } else if (pickerIndex == AMPM_INDEX) { - updateAmPmLabelStates(newValue); - } else if (pickerIndex == ENABLE_PICKER_INDEX) { - if (!isTypedTimeFullyLegal()) { - mTypedTimes.clear(); - } - finishKbMode(); + switch (pickerIndex) { + case HOUR_INDEX: + if (mAllowAutoAdvance && autoAdvance) { + updateHeaderHour(newValue, false); + setCurrentItemShowing(MINUTE_INDEX, true, false); + mDelegator.announceForAccessibility(newValue + ". " + mSelectMinutes); + } else { + updateHeaderHour(newValue, true); + } + break; + case MINUTE_INDEX: + updateHeaderMinute(newValue, true); + break; + case AMPM_INDEX: + updateAmPmLabelStates(newValue); + break; + case ENABLE_PICKER_INDEX: + if (!isTypedTimeFullyLegal()) { + mTypedTimes.clear(); + } + finishKbMode(); + break; + } + + if (mOnTimeChangedListener != null) { + mOnTimeChangedListener.onTimeChanged(mDelegator, getCurrentHour(), getCurrentMinute()); } } diff --git a/core/java/android/widget/Toolbar.java b/core/java/android/widget/Toolbar.java index d8e39e3..f90d64a 100644 --- a/core/java/android/widget/Toolbar.java +++ b/core/java/android/widget/Toolbar.java @@ -104,6 +104,7 @@ public class Toolbar extends ViewGroup { private ImageView mLogoView; private Drawable mCollapseIcon; + private CharSequence mCollapseDescription; private ImageButton mCollapseButtonView; View mExpandedActionView; @@ -238,6 +239,7 @@ public class Toolbar extends ViewGroup { } mCollapseIcon = a.getDrawable(R.styleable.Toolbar_collapseIcon); + mCollapseDescription = a.getText(R.styleable.Toolbar_collapseContentDescription); final CharSequence title = a.getText(R.styleable.Toolbar_title); if (!TextUtils.isEmpty(title)) { @@ -998,6 +1000,7 @@ public class Toolbar extends ViewGroup { if (mCollapseButtonView == null) { mCollapseButtonView = new ImageButton(getContext(), null, 0, mNavButtonStyle); mCollapseButtonView.setImageDrawable(mCollapseIcon); + mCollapseButtonView.setContentDescription(mCollapseDescription); final LayoutParams lp = generateDefaultLayoutParams(); lp.gravity = Gravity.START | (mButtonGravity & Gravity.VERTICAL_GRAVITY_MASK); lp.mViewType = LayoutParams.EXPANDED; @@ -1749,6 +1752,17 @@ public class Toolbar extends ViewGroup { } /** + * Accessor to enable LayoutLib to get ActionMenuPresenter directly. + */ + ActionMenuPresenter getOuterActionMenuPresenter() { + return mOuterActionMenuPresenter; + } + + Context getPopupContext() { + return mPopupContext; + } + + /** * Interface responsible for receiving menu item click events if the items themselves * do not have individual item click listeners. */ diff --git a/core/java/com/android/internal/app/AlertController.java b/core/java/com/android/internal/app/AlertController.java index 0183e45..35e03c3 100644 --- a/core/java/com/android/internal/app/AlertController.java +++ b/core/java/com/android/internal/app/AlertController.java @@ -26,7 +26,6 @@ import android.content.DialogInterface; import android.content.res.TypedArray; import android.database.Cursor; import android.graphics.drawable.Drawable; -import android.os.Build; import android.os.Handler; import android.os.Message; import android.text.TextUtils; @@ -38,9 +37,12 @@ import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.view.ViewGroup.LayoutParams; +import android.view.ViewParent; +import android.view.ViewTreeObserver; import android.view.Window; import android.view.WindowInsets; import android.view.WindowManager; +import android.widget.AbsListView; import android.widget.AdapterView; import android.widget.AdapterView.OnItemClickListener; import android.widget.ArrayAdapter; @@ -449,11 +451,11 @@ public class AlertController { } private void setupView() { - final LinearLayout contentPanel = (LinearLayout) mWindow.findViewById(R.id.contentPanel); + final ViewGroup contentPanel = (ViewGroup) mWindow.findViewById(R.id.contentPanel); setupContent(contentPanel); final boolean hasButtons = setupButtons(); - final LinearLayout topPanel = (LinearLayout) mWindow.findViewById(R.id.topPanel); + final ViewGroup topPanel = (ViewGroup) mWindow.findViewById(R.id.topPanel); final TypedArray a = mContext.obtainStyledAttributes( null, R.styleable.AlertDialog, R.attr.alertDialogStyle, 0); final boolean hasTitle = setupTitle(topPanel); @@ -521,13 +523,13 @@ public class AlertController { a.recycle(); } - private boolean setupTitle(LinearLayout topPanel) { + private boolean setupTitle(ViewGroup topPanel) { boolean hasTitle = true; if (mCustomTitleView != null) { // Add the custom title view directly to the topPanel layout - LinearLayout.LayoutParams lp = new LinearLayout.LayoutParams( - LinearLayout.LayoutParams.MATCH_PARENT, LinearLayout.LayoutParams.WRAP_CONTENT); + LayoutParams lp = new LayoutParams( + LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT); topPanel.addView(mCustomTitleView, 0, lp); @@ -571,7 +573,7 @@ public class AlertController { return hasTitle; } - private void setupContent(LinearLayout contentPanel) { + private void setupContent(ViewGroup contentPanel) { mScrollView = (ScrollView) mWindow.findViewById(R.id.scrollView); mScrollView.setFocusable(false); @@ -588,14 +590,77 @@ public class AlertController { mScrollView.removeView(mMessageView); if (mListView != null) { - contentPanel.removeView(mWindow.findViewById(R.id.scrollView)); - contentPanel.addView(mListView, - new LinearLayout.LayoutParams(MATCH_PARENT, MATCH_PARENT)); - contentPanel.setLayoutParams(new LinearLayout.LayoutParams(MATCH_PARENT, 0, 1.0f)); + final ViewGroup scrollParent = (ViewGroup) mScrollView.getParent(); + final int childIndex = scrollParent.indexOfChild(mScrollView); + scrollParent.removeViewAt(childIndex); + scrollParent.addView(mListView, childIndex, + new LayoutParams(MATCH_PARENT, MATCH_PARENT)); } else { contentPanel.setVisibility(View.GONE); } } + + // Set up scroll indicators (if present). + final View indicatorUp = mWindow.findViewById(R.id.scrollIndicatorUp); + final View indicatorDown = mWindow.findViewById(R.id.scrollIndicatorDown); + if (indicatorUp != null || indicatorDown != null) { + if (mMessage != null) { + // We're just showing the ScrollView, set up listener. + mScrollView.setOnScrollChangeListener(new View.OnScrollChangeListener() { + @Override + public void onScrollChange(View v, int scrollX, int scrollY, + int oldScrollX, int oldScrollY) { + manageScrollIndicators(v, indicatorUp, indicatorDown); + } + }); + // Set up the indicators following layout. + mScrollView.post(new Runnable() { + @Override + public void run() { + manageScrollIndicators(mScrollView, indicatorUp, indicatorDown); + } + }); + + } else if (mListView != null) { + // We're just showing the AbsListView, set up listener. + mListView.setOnScrollListener(new AbsListView.OnScrollListener() { + @Override + public void onScrollStateChanged(AbsListView view, int scrollState) { + // That's cool, I guess? + } + + @Override + public void onScroll(AbsListView v, int firstVisibleItem, + int visibleItemCount, int totalItemCount) { + manageScrollIndicators(v, indicatorUp, indicatorDown); + } + }); + // Set up the indicators following layout. + mListView.post(new Runnable() { + @Override + public void run() { + manageScrollIndicators(mListView, indicatorUp, indicatorDown); + } + }); + } else { + // We don't have any content to scroll, remove the indicators. + if (indicatorUp != null) { + contentPanel.removeView(indicatorUp); + } + if (indicatorDown != null) { + contentPanel.removeView(indicatorDown); + } + } + } + } + + private static void manageScrollIndicators(View v, View upIndicator, View downIndicator) { + if (upIndicator != null) { + upIndicator.setVisibility(v.canScrollVertically(-1) ? View.VISIBLE : View.INVISIBLE); + } + if (downIndicator != null) { + downIndicator.setVisibility(v.canScrollVertically(1) ? View.VISIBLE : View.INVISIBLE); + } } private boolean setupButtons() { diff --git a/core/java/com/android/internal/app/ChooserActivity.java b/core/java/com/android/internal/app/ChooserActivity.java index 0bc1a8d..64bd6b6 100644 --- a/core/java/com/android/internal/app/ChooserActivity.java +++ b/core/java/com/android/internal/app/ChooserActivity.java @@ -20,6 +20,7 @@ import android.app.Activity; import android.content.ComponentName; import android.content.Intent; import android.content.IntentSender; +import android.content.pm.ActivityInfo; import android.os.Bundle; import android.os.Parcelable; import android.util.Log; @@ -75,16 +76,21 @@ public class ChooserActivity extends ResolverActivity { } @Override - public Intent getReplacementIntent(String packageName, Intent defIntent) { + public Intent getReplacementIntent(ActivityInfo aInfo, Intent defIntent) { + Intent result = defIntent; if (mReplacementExtras != null) { - final Bundle replExtras = mReplacementExtras.getBundle(packageName); + final Bundle replExtras = mReplacementExtras.getBundle(aInfo.packageName); if (replExtras != null) { - final Intent result = new Intent(defIntent); + result = new Intent(defIntent); result.putExtras(replExtras); - return result; } } - return defIntent; + if (aInfo.name.equals(IntentForwarderActivity.FORWARD_INTENT_TO_USER_OWNER) + || aInfo.name.equals(IntentForwarderActivity.FORWARD_INTENT_TO_MANAGED_PROFILE)) { + result = Intent.createChooser(result, + getIntent().getCharSequenceExtra(Intent.EXTRA_TITLE)); + } + return result; } @Override diff --git a/core/java/com/android/internal/app/IntentForwarderActivity.java b/core/java/com/android/internal/app/IntentForwarderActivity.java index 6e2f84a..9656a21 100644 --- a/core/java/com/android/internal/app/IntentForwarderActivity.java +++ b/core/java/com/android/internal/app/IntentForwarderActivity.java @@ -58,21 +58,22 @@ public class IntentForwarderActivity extends Activity { Intent intentReceived = getIntent(); String className = intentReceived.getComponent().getClassName(); - final UserHandle userDest; + final int targetUserId; final int userMessageId; if (className.equals(FORWARD_INTENT_TO_USER_OWNER)) { userMessageId = com.android.internal.R.string.forward_intent_to_owner; - userDest = UserHandle.OWNER; + targetUserId = UserHandle.USER_OWNER; } else if (className.equals(FORWARD_INTENT_TO_MANAGED_PROFILE)) { userMessageId = com.android.internal.R.string.forward_intent_to_work; - userDest = getManagedProfile(); + targetUserId = getManagedProfile(); } else { Slog.wtf(TAG, IntentForwarderActivity.class.getName() + " cannot be called directly"); userMessageId = -1; - userDest = null; + targetUserId = UserHandle.USER_NULL; } - if (userDest == null) { // This covers the case where there is no managed profile. + if (targetUserId == UserHandle.USER_NULL) { + // This covers the case where there is no managed profile. finish(); return; } @@ -83,31 +84,24 @@ public class IntentForwarderActivity extends Activity { newIntent.addFlags(Intent.FLAG_ACTIVITY_FORWARD_RESULT |Intent.FLAG_ACTIVITY_PREVIOUS_IS_TOP); int callingUserId = getUserId(); - IPackageManager ipm = AppGlobals.getPackageManager(); - String resolvedType = newIntent.resolveTypeIfNeeded(getContentResolver()); - boolean canForward = false; - Intent selector = newIntent.getSelector(); - if (selector == null) { - selector = newIntent; - } - try { - canForward = ipm.canForwardTo(selector, resolvedType, callingUserId, - userDest.getIdentifier()); - } catch (RemoteException e) { - Slog.e(TAG, "PackageManagerService is dead?"); - } - if (canForward) { - newIntent.setContentUserHint(callingUserId); + + if (canForward(newIntent, targetUserId)) { + if (newIntent.getAction().equals(Intent.ACTION_CHOOSER)) { + Intent innerIntent = (Intent) newIntent.getParcelableExtra(Intent.EXTRA_INTENT); + innerIntent.setContentUserHint(callingUserId); + } else { + newIntent.setContentUserHint(callingUserId); + } final android.content.pm.ResolveInfo ri = getPackageManager().resolveActivityAsUser( - newIntent, MATCH_DEFAULT_ONLY, userDest.getIdentifier()); + newIntent, MATCH_DEFAULT_ONLY, targetUserId); // Only show a disclosure if this is a normal (non-OS) app final boolean shouldShowDisclosure = !UserHandle.isSameApp(ri.activityInfo.applicationInfo.uid, Process.SYSTEM_UID); try { - startActivityAsCaller(newIntent, null, userDest.getIdentifier()); + startActivityAsCaller(newIntent, null, targetUserId); } catch (RuntimeException e) { int launchedFromUid = -1; String launchedFromPackage = "?"; @@ -129,26 +123,55 @@ public class IntentForwarderActivity extends Activity { } } else { Slog.wtf(TAG, "the intent: " + newIntent + "cannot be forwarded from user " - + callingUserId + " to user " + userDest.getIdentifier()); + + callingUserId + " to user " + targetUserId); } finish(); } + boolean canForward(Intent intent, int targetUserId) { + IPackageManager ipm = AppGlobals.getPackageManager(); + if (intent.getAction().equals(Intent.ACTION_CHOOSER)) { + // The EXTRA_INITIAL_INTENTS may not be allowed to be forwarded. + if (intent.hasExtra(Intent.EXTRA_INITIAL_INTENTS)) { + Slog.wtf(TAG, "An chooser intent with extra initial intents cannot be forwarded to" + + " a different user"); + return false; + } + if (intent.hasExtra(Intent.EXTRA_REPLACEMENT_EXTRAS)) { + Slog.wtf(TAG, "A chooser intent with replacement extras cannot be forwarded to a" + + " different user"); + return false; + } + intent = (Intent) intent.getParcelableExtra(Intent.EXTRA_INTENT); + } + String resolvedType = intent.resolveTypeIfNeeded(getContentResolver()); + if (intent.getSelector() != null) { + intent = intent.getSelector(); + } + try { + return ipm.canForwardTo(intent, resolvedType, getUserId(), + targetUserId); + } catch (RemoteException e) { + Slog.e(TAG, "PackageManagerService is dead?"); + return false; + } + } + /** - * Returns the managed profile for this device or null if there is no managed - * profile. + * Returns the userId of the managed profile for this device or UserHandle.USER_NULL if there is + * no managed profile. * * TODO: Remove the assumption that there is only one managed profile * on the device. */ - private UserHandle getManagedProfile() { + private int getManagedProfile() { UserManager userManager = (UserManager) getSystemService(Context.USER_SERVICE); List<UserInfo> relatedUsers = userManager.getProfiles(UserHandle.USER_OWNER); for (UserInfo userInfo : relatedUsers) { - if (userInfo.isManagedProfile()) return new UserHandle(userInfo.id); + if (userInfo.isManagedProfile()) return userInfo.id; } Slog.wtf(TAG, FORWARD_INTENT_TO_MANAGED_PROFILE + " has been called, but there is no managed profile"); - return null; + return UserHandle.USER_NULL; } } diff --git a/core/java/com/android/internal/app/ResolverActivity.java b/core/java/com/android/internal/app/ResolverActivity.java index ccffa19..376db6e 100644 --- a/core/java/com/android/internal/app/ResolverActivity.java +++ b/core/java/com/android/internal/app/ResolverActivity.java @@ -100,6 +100,7 @@ public class ResolverActivity extends Activity implements AdapterView.OnItemClic private int mMaxColumns; private int mLastSelected = ListView.INVALID_POSITION; private boolean mResolvingHome = false; + private int mProfileSwitchMessageId = -1; private UsageStatsManager mUsm; private Map<String, UsageStats> mStats; @@ -200,6 +201,11 @@ public class ResolverActivity extends Activity implements AdapterView.OnItemClic List<ResolveInfo> rList, boolean alwaysUseOption) { setTheme(R.style.Theme_DeviceDefault_Resolver); super.onCreate(savedInstanceState); + + // Determine whether we should show that intent is forwarded + // from managed profile to owner or other way around. + setProfileSwitchMessageId(intent.getContentUserHint()); + try { mLaunchedFromUid = ActivityManagerNative.getDefault().getLaunchedFromUid( getActivityToken()); @@ -278,9 +284,9 @@ public class ResolverActivity extends Activity implements AdapterView.OnItemClic final ResolverDrawerLayout rdl = (ResolverDrawerLayout) findViewById(R.id.contentPanel); if (rdl != null) { - rdl.setOnClickOutsideListener(new View.OnClickListener() { + rdl.setOnDismissedListener(new ResolverDrawerLayout.OnDismissedListener() { @Override - public void onClick(View v) { + public void onDismissed() { finish(); } }); @@ -320,6 +326,22 @@ public class ResolverActivity extends Activity implements AdapterView.OnItemClic } } + private void setProfileSwitchMessageId(int contentUserHint) { + if (contentUserHint != UserHandle.USER_CURRENT && + contentUserHint != UserHandle.myUserId()) { + UserManager userManager = (UserManager) getSystemService(Context.USER_SERVICE); + UserInfo originUserInfo = userManager.getUserInfo(contentUserHint); + boolean originIsManaged = originUserInfo != null ? originUserInfo.isManagedProfile() + : false; + boolean targetIsManaged = userManager.isManagedProfile(); + if (originIsManaged && !targetIsManaged) { + mProfileSwitchMessageId = com.android.internal.R.string.forward_intent_to_owner; + } else if (!originIsManaged && targetIsManaged) { + mProfileSwitchMessageId = com.android.internal.R.string.forward_intent_to_work; + } + } + } + /** * Turn on launch mode that is safe to use when forwarding intents received from * applications and running in system processes. This mode uses Activity.startActivityAsCaller @@ -529,7 +551,7 @@ public class ResolverActivity extends Activity implements AdapterView.OnItemClic /** * Replace me in subclasses! */ - public Intent getReplacementIntent(String packageName, Intent defIntent) { + public Intent getReplacementIntent(ActivityInfo aInfo, Intent defIntent) { return defIntent; } @@ -642,6 +664,11 @@ public class ResolverActivity extends Activity implements AdapterView.OnItemClic } public void safelyStartActivity(Intent intent) { + // 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); @@ -943,7 +970,7 @@ public class ResolverActivity extends Activity implements AdapterView.OnItemClic DisplayResolveInfo dri = filtered ? getItem(position) : mList.get(position); Intent intent = new Intent(dri.origIntent != null ? dri.origIntent : - getReplacementIntent(dri.ri.activityInfo.packageName, mIntent)); + getReplacementIntent(dri.ri.activityInfo, mIntent)); intent.addFlags(Intent.FLAG_ACTIVITY_FORWARD_RESULT |Intent.FLAG_ACTIVITY_PREVIOUS_IS_TOP); ActivityInfo ai = dri.ri.activityInfo; diff --git a/core/java/com/android/internal/app/ToolbarActionBar.java b/core/java/com/android/internal/app/ToolbarActionBar.java index 4410f25..34b9dcb 100644 --- a/core/java/com/android/internal/app/ToolbarActionBar.java +++ b/core/java/com/android/internal/app/ToolbarActionBar.java @@ -40,7 +40,6 @@ import com.android.internal.widget.ToolbarWidgetWrapper; import java.util.ArrayList; public class ToolbarActionBar extends ActionBar { - private Toolbar mToolbar; private DecorToolbar mDecorToolbar; private boolean mToolbarMenuPrepared; private Window.Callback mWindowCallback; @@ -66,7 +65,6 @@ public class ToolbarActionBar extends ActionBar { }; public ToolbarActionBar(Toolbar toolbar, CharSequence title, Window.Callback windowCallback) { - mToolbar = toolbar; mDecorToolbar = new ToolbarWidgetWrapper(toolbar, false); mWindowCallback = new ToolbarCallbackWrapper(windowCallback); mDecorToolbar.setWindowCallback(mWindowCallback); @@ -91,8 +89,8 @@ public class ToolbarActionBar extends ActionBar { @Override public void setCustomView(int resId) { - final LayoutInflater inflater = LayoutInflater.from(mToolbar.getContext()); - setCustomView(inflater.inflate(resId, mToolbar, false)); + final LayoutInflater inflater = LayoutInflater.from(mDecorToolbar.getContext()); + setCustomView(inflater.inflate(resId, mDecorToolbar.getViewGroup(), false)); } @Override @@ -132,17 +130,17 @@ public class ToolbarActionBar extends ActionBar { @Override public void setElevation(float elevation) { - mToolbar.setElevation(elevation); + mDecorToolbar.getViewGroup().setElevation(elevation); } @Override public float getElevation() { - return mToolbar.getElevation(); + return mDecorToolbar.getViewGroup().getElevation(); } @Override public Context getThemedContext() { - return mToolbar.getContext(); + return mDecorToolbar.getContext(); } @Override @@ -152,12 +150,12 @@ public class ToolbarActionBar extends ActionBar { @Override public void setHomeAsUpIndicator(Drawable indicator) { - mToolbar.setNavigationIcon(indicator); + mDecorToolbar.setNavigationIcon(indicator); } @Override public void setHomeAsUpIndicator(int resId) { - mToolbar.setNavigationIcon(resId); + mDecorToolbar.setNavigationIcon(resId); } @Override @@ -280,7 +278,7 @@ public class ToolbarActionBar extends ActionBar { @Override public void setBackgroundDrawable(@Nullable Drawable d) { - mToolbar.setBackground(d); + mDecorToolbar.setBackgroundDrawable(d); } @Override @@ -290,12 +288,12 @@ public class ToolbarActionBar extends ActionBar { @Override public CharSequence getTitle() { - return mToolbar.getTitle(); + return mDecorToolbar.getTitle(); } @Override public CharSequence getSubtitle() { - return mToolbar.getSubtitle(); + return mDecorToolbar.getSubtitle(); } @Override @@ -389,44 +387,44 @@ public class ToolbarActionBar extends ActionBar { @Override public int getHeight() { - return mToolbar.getHeight(); + return mDecorToolbar.getHeight(); } @Override public void show() { // TODO: Consider a better transition for this. // Right now use no automatic transition so that the app can supply one if desired. - mToolbar.setVisibility(View.VISIBLE); + mDecorToolbar.setVisibility(View.VISIBLE); } @Override public void hide() { // TODO: Consider a better transition for this. // Right now use no automatic transition so that the app can supply one if desired. - mToolbar.setVisibility(View.GONE); + mDecorToolbar.setVisibility(View.GONE); } @Override public boolean isShowing() { - return mToolbar.getVisibility() == View.VISIBLE; + return mDecorToolbar.getVisibility() == View.VISIBLE; } @Override public boolean openOptionsMenu() { - return mToolbar.showOverflowMenu(); + return mDecorToolbar.showOverflowMenu(); } @Override public boolean invalidateOptionsMenu() { - mToolbar.removeCallbacks(mMenuInvalidator); - mToolbar.postOnAnimation(mMenuInvalidator); + mDecorToolbar.getViewGroup().removeCallbacks(mMenuInvalidator); + mDecorToolbar.getViewGroup().postOnAnimation(mMenuInvalidator); return true; } @Override public boolean collapseActionView() { - if (mToolbar.hasExpandedActionView()) { - mToolbar.collapseActionView(); + if (mDecorToolbar.hasExpandedActionView()) { + mDecorToolbar.collapseActionView(); return true; } return false; @@ -434,10 +432,10 @@ public class ToolbarActionBar extends ActionBar { void populateOptionsMenu() { if (!mMenuCallbackSet) { - mToolbar.setMenuCallbacks(new ActionMenuPresenterCallback(), new MenuBuilderCallback()); + mDecorToolbar.setMenuCallbacks(new ActionMenuPresenterCallback(), new MenuBuilderCallback()); mMenuCallbackSet = true; } - final Menu menu = mToolbar.getMenu(); + final Menu menu = mDecorToolbar.getMenu(); final MenuBuilder mb = menu instanceof MenuBuilder ? (MenuBuilder) menu : null; if (mb != null) { mb.stopDispatchingItemsChanged(); @@ -518,7 +516,7 @@ public class ToolbarActionBar extends ActionBar { } mClosingActionMenu = true; - mToolbar.dismissPopupMenus(); + mDecorToolbar.dismissPopupMenus(); if (mWindowCallback != null) { mWindowCallback.onPanelClosed(Window.FEATURE_ACTION_BAR, menu); } @@ -536,7 +534,7 @@ public class ToolbarActionBar extends ActionBar { @Override public void onMenuModeChange(MenuBuilder menu) { if (mWindowCallback != null) { - if (mToolbar.isOverflowMenuShowing()) { + if (mDecorToolbar.isOverflowMenuShowing()) { mWindowCallback.onPanelClosed(Window.FEATURE_ACTION_BAR, menu); } else if (mWindowCallback.onPreparePanel(Window.FEATURE_OPTIONS_PANEL, null, menu)) { diff --git a/core/java/com/android/internal/app/WindowDecorActionBar.java b/core/java/com/android/internal/app/WindowDecorActionBar.java index 2377c22..d95f0e5 100644 --- a/core/java/com/android/internal/app/WindowDecorActionBar.java +++ b/core/java/com/android/internal/app/WindowDecorActionBar.java @@ -496,7 +496,7 @@ public class WindowDecorActionBar extends ActionBar implements mOverlayLayout.setHideOnContentScrollEnabled(false); mContextView.killMode(); - ActionModeImpl mode = new ActionModeImpl(callback); + ActionModeImpl mode = new ActionModeImpl(mContextView.getContext(), callback); if (mode.dispatchOnCreate()) { mode.invalidate(); mContextView.initForMode(mode); @@ -876,7 +876,7 @@ public class WindowDecorActionBar extends ActionBar implements currentTheme.resolveAttribute(com.android.internal.R.attr.actionBarWidgetTheme, outValue, true); final int targetThemeRes = outValue.resourceId; - + if (targetThemeRes != 0 && mContext.getThemeResId() != targetThemeRes) { mThemedContext = new ContextThemeWrapper(mContext, targetThemeRes); } else { @@ -885,7 +885,7 @@ public class WindowDecorActionBar extends ActionBar implements } return mThemedContext; } - + @Override public boolean isTitleTruncated() { return mDecorToolbar != null && mDecorToolbar.isTitleTruncated(); @@ -933,23 +933,26 @@ public class WindowDecorActionBar extends ActionBar implements } /** - * @hide + * @hide */ public class ActionModeImpl extends ActionMode implements MenuBuilder.Callback { + private final Context mActionModeContext; + private final MenuBuilder mMenu; + private ActionMode.Callback mCallback; - private MenuBuilder mMenu; private WeakReference<View> mCustomView; - - public ActionModeImpl(ActionMode.Callback callback) { + + public ActionModeImpl(Context context, ActionMode.Callback callback) { + mActionModeContext = context; mCallback = callback; - mMenu = new MenuBuilder(getThemedContext()) + mMenu = new MenuBuilder(context) .setDefaultShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM); mMenu.setCallback(this); } @Override public MenuInflater getMenuInflater() { - return new MenuInflater(getThemedContext()); + return new MenuInflater(mActionModeContext); } @Override @@ -1042,7 +1045,7 @@ public class WindowDecorActionBar extends ActionBar implements public CharSequence getSubtitle() { return mContextView.getSubtitle(); } - + @Override public void setTitleOptionalHint(boolean titleOptional) { super.setTitleOptionalHint(titleOptional); diff --git a/core/java/com/android/internal/content/ReferrerIntent.aidl b/core/java/com/android/internal/content/ReferrerIntent.aidl new file mode 100644 index 0000000..7cf6774 --- /dev/null +++ b/core/java/com/android/internal/content/ReferrerIntent.aidl @@ -0,0 +1,19 @@ +/* + * Copyright 2014, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.internal.content; + +parcelable ReferrerIntent; diff --git a/core/java/com/android/internal/content/ReferrerIntent.java b/core/java/com/android/internal/content/ReferrerIntent.java new file mode 100644 index 0000000..8d9a1cf --- /dev/null +++ b/core/java/com/android/internal/content/ReferrerIntent.java @@ -0,0 +1,51 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.internal.content; + +import android.content.Intent; +import android.os.Parcel; + +/** + * Subclass of Intent that also contains referrer (as a package name) information. + */ +public class ReferrerIntent extends Intent { + public final String mReferrer; + + public ReferrerIntent(Intent baseIntent, String referrer) { + super(baseIntent); + mReferrer = referrer; + } + + public void writeToParcel(Parcel dest, int parcelableFlags) { + super.writeToParcel(dest, parcelableFlags); + dest.writeString(mReferrer); + } + + ReferrerIntent(Parcel in) { + readFromParcel(in); + mReferrer = in.readString(); + } + + public static final Creator<ReferrerIntent> CREATOR = new Creator<ReferrerIntent>() { + public ReferrerIntent createFromParcel(Parcel source) { + return new ReferrerIntent(source); + } + public ReferrerIntent[] newArray(int size) { + return new ReferrerIntent[size]; + } + }; +} diff --git a/core/java/com/android/internal/inputmethod/InputMethodUtils.java b/core/java/com/android/internal/inputmethod/InputMethodUtils.java index ac915d1..183527c 100644 --- a/core/java/com/android/internal/inputmethod/InputMethodUtils.java +++ b/core/java/com/android/internal/inputmethod/InputMethodUtils.java @@ -16,6 +16,8 @@ package com.android.internal.inputmethod; +import android.annotation.NonNull; +import android.annotation.Nullable; import android.app.AppOpsManager; import android.content.ContentResolver; import android.content.Context; @@ -34,7 +36,9 @@ import android.view.textservice.SpellCheckerInfo; import android.view.textservice.TextServicesManager; import java.util.ArrayList; +import java.util.Arrays; import java.util.HashMap; +import java.util.LinkedHashSet; import java.util.List; import java.util.Locale; @@ -115,8 +119,8 @@ public class InputMethodUtils { } /** - * @deprecated Use {@link Locale} returned from - * {@link #getFallbackLocaleForDefaultIme(ArrayList)} instead. + * @deprecated Use {@link #isSystemImeThatHasSubtypeOf(InputMethodInfo, Context, boolean, + * Locale, boolean, String)} instead. */ @Deprecated public static boolean isSystemImeThatHasEnglishKeyboardSubtype(InputMethodInfo imi) { @@ -126,25 +130,60 @@ public class InputMethodUtils { return containsSubtypeOf(imi, ENGLISH_LOCALE.getLanguage(), SUBTYPE_MODE_KEYBOARD); } + private static boolean isSystemImeThatHasSubtypeOf(final InputMethodInfo imi, + final Context context, final boolean checkDefaultAttribute, + @Nullable final Locale requiredLocale, final boolean checkCountry, + final String requiredSubtypeMode) { + if (!isSystemIme(imi)) { + return false; + } + if (checkDefaultAttribute && !imi.isDefault(context)) { + return false; + } + if (!containsSubtypeOf(imi, requiredLocale, checkCountry, requiredSubtypeMode)) { + return false; + } + return true; + } + + @Nullable public static Locale getFallbackLocaleForDefaultIme(final ArrayList<InputMethodInfo> imis, final Context context) { + // At first, find the fallback locale from the IMEs that are declared as "default" in the + // current locale. Note that IME developers can declare an IME as "default" only for + // some particular locales but "not default" for other locales. for (final Locale fallbackLocale : SEARCH_ORDER_OF_FALLBACK_LOCALES) { for (int i = 0; i < imis.size(); ++i) { - final InputMethodInfo imi = imis.get(i); - if (isSystemIme(imi) && imi.isDefault(context) && - containsSubtypeOf(imi, fallbackLocale, false /* ignoreCountry */, - SUBTYPE_MODE_KEYBOARD)) { + if (isSystemImeThatHasSubtypeOf(imis.get(i), context, + true /* checkDefaultAttribute */, fallbackLocale, + true /* checkCountry */, SUBTYPE_MODE_KEYBOARD)) { + return fallbackLocale; + } + } + } + // If no fallback locale is found in the above condition, find fallback locales regardless + // of the "default" attribute as a last resort. + for (final Locale fallbackLocale : SEARCH_ORDER_OF_FALLBACK_LOCALES) { + for (int i = 0; i < imis.size(); ++i) { + if (isSystemImeThatHasSubtypeOf(imis.get(i), context, + false /* checkDefaultAttribute */, fallbackLocale, + true /* checkCountry */, SUBTYPE_MODE_KEYBOARD)) { return fallbackLocale; } } } + Slog.w(TAG, "Found no fallback locale. imis=" + Arrays.toString(imis.toArray())); return null; } - private static boolean isSystemAuxilialyImeThatHasAutomaticSubtype(InputMethodInfo imi) { + private static boolean isSystemAuxilialyImeThatHasAutomaticSubtype(final InputMethodInfo imi, + final Context context, final boolean checkDefaultAttribute) { if (!isSystemIme(imi)) { return false; } + if (checkDefaultAttribute && !imi.isDefault(context)) { + return false; + } if (!imi.isAuxiliaryIme()) { return false; } @@ -166,98 +205,184 @@ public class InputMethodUtils { } } - public static ArrayList<InputMethodInfo> getDefaultEnabledImes( - Context context, boolean isSystemReady, ArrayList<InputMethodInfo> imis) { - // OK to store null in fallbackLocale because isImeThatHasSubtypeOf() is null-tolerant. - final Locale fallbackLocale = getFallbackLocaleForDefaultIme(imis, context); + private static final class InputMethodListBuilder { + // Note: We use LinkedHashSet instead of android.util.ArraySet because the enumeration + // order can have non-trivial effect in the call sites. + @NonNull + private final LinkedHashSet<InputMethodInfo> mInputMethodSet = new LinkedHashSet<>(); - if (!isSystemReady) { - final ArrayList<InputMethodInfo> retval = new ArrayList<>(); + public InputMethodListBuilder fillImes(final ArrayList<InputMethodInfo> imis, + final Context context, final boolean checkDefaultAttribute, + @Nullable final Locale locale, final boolean checkCountry, + final String requiredSubtypeMode) { for (int i = 0; i < imis.size(); ++i) { final InputMethodInfo imi = imis.get(i); - // TODO: We should check isAsciiCapable instead of relying on fallbackLocale. - if (isSystemIme(imi) && imi.isDefault(context) && - isImeThatHasSubtypeOf(imi, fallbackLocale, false /* ignoreCountry */, - SUBTYPE_MODE_KEYBOARD)) { - retval.add(imi); + if (isSystemImeThatHasSubtypeOf(imi, context, checkDefaultAttribute, locale, + checkCountry, requiredSubtypeMode)) { + mInputMethodSet.add(imi); } } - return retval; + return this; } - // OK to store null in fallbackLocale because isImeThatHasSubtypeOf() is null-tolerant. - final Locale systemLocale = getSystemLocaleFromContext(context); - // TODO: Use LinkedHashSet to simplify the code. - final ArrayList<InputMethodInfo> retval = new ArrayList<>(); - boolean systemLocaleKeyboardImeFound = false; - - // First, try to find IMEs with taking the system locale country into consideration. - for (int i = 0; i < imis.size(); ++i) { - final InputMethodInfo imi = imis.get(i); - if (!isSystemIme(imi) || !imi.isDefault(context)) { - continue; - } - final boolean isSystemLocaleKeyboardIme = isImeThatHasSubtypeOf(imi, systemLocale, - false /* ignoreCountry */, SUBTYPE_MODE_KEYBOARD); - // TODO: We should check isAsciiCapable instead of relying on fallbackLocale. - // TODO: Use LinkedHashSet to simplify the code. - if (isSystemLocaleKeyboardIme || - isImeThatHasSubtypeOf(imi, fallbackLocale, false /* ignoreCountry */, - SUBTYPE_MODE_ANY)) { - retval.add(imi); + // TODO: The behavior of InputMethodSubtype#overridesImplicitlyEnabledSubtype() should be + // documented more clearly. + public InputMethodListBuilder fillAuxiliaryImes(final ArrayList<InputMethodInfo> imis, + final Context context) { + // If one or more auxiliary input methods are available, OK to stop populating the list. + for (final InputMethodInfo imi : mInputMethodSet) { + if (imi.isAuxiliaryIme()) { + return this; + } } - systemLocaleKeyboardImeFound |= isSystemLocaleKeyboardIme; - } - - // System locale country doesn't match any IMEs, try to find IMEs in a country-agnostic - // way. - if (!systemLocaleKeyboardImeFound) { + boolean added = false; for (int i = 0; i < imis.size(); ++i) { final InputMethodInfo imi = imis.get(i); - if (!isSystemIme(imi) || !imi.isDefault(context)) { - continue; - } - if (isImeThatHasSubtypeOf(imi, fallbackLocale, false /* ignoreCountry */, - SUBTYPE_MODE_KEYBOARD)) { - // IMEs that have fallback locale are already added in the previous loop. We - // don't need to add them again here. - // TODO: Use LinkedHashSet to simplify the code. - continue; + if (isSystemAuxilialyImeThatHasAutomaticSubtype(imi, context, + true /* checkDefaultAttribute */)) { + mInputMethodSet.add(imi); + added = true; } - if (isImeThatHasSubtypeOf(imi, systemLocale, true /* ignoreCountry */, - SUBTYPE_MODE_ANY)) { - retval.add(imi); + } + if (added) { + return this; + } + for (int i = 0; i < imis.size(); ++i) { + final InputMethodInfo imi = imis.get(i); + if (isSystemAuxilialyImeThatHasAutomaticSubtype(imi, context, + false /* checkDefaultAttribute */)) { + mInputMethodSet.add(imi); } } + return this; } - // If one or more auxiliary input methods are available, OK to stop populating the list. - for (int i = 0; i < retval.size(); ++i) { - if (retval.get(i).isAuxiliaryIme()) { - return retval; - } + public boolean isEmpty() { + return mInputMethodSet.isEmpty(); } - for (int i = 0; i < imis.size(); ++i) { - final InputMethodInfo imi = imis.get(i); - if (isSystemAuxilialyImeThatHasAutomaticSubtype(imi)) { - retval.add(imi); - } + + @NonNull + public ArrayList<InputMethodInfo> build() { + return new ArrayList<>(mInputMethodSet); } - return retval; } - public static boolean isImeThatHasSubtypeOf(final InputMethodInfo imi, - final Locale locale, final boolean ignoreCountry, final String mode) { - if (locale == null) { - return false; - } - return containsSubtypeOf(imi, locale, ignoreCountry, mode); + private static InputMethodListBuilder getMinimumKeyboardSetWithoutSystemLocale( + final ArrayList<InputMethodInfo> imis, final Context context, + @Nullable final Locale fallbackLocale) { + // Before the system becomes ready, we pick up at least one keyboard in the following order. + // The first user (device owner) falls into this category. + // 1. checkDefaultAttribute: true, locale: fallbackLocale, checkCountry: true + // 2. checkDefaultAttribute: false, locale: fallbackLocale, checkCountry: true + // 3. checkDefaultAttribute: true, locale: fallbackLocale, checkCountry: false + // 4. checkDefaultAttribute: false, locale: fallbackLocale, checkCountry: false + // TODO: We should check isAsciiCapable instead of relying on fallbackLocale. + + final InputMethodListBuilder builder = new InputMethodListBuilder(); + builder.fillImes(imis, context, true /* checkDefaultAttribute */, fallbackLocale, + true /* checkCountry */, SUBTYPE_MODE_KEYBOARD); + if (!builder.isEmpty()) { + return builder; + } + builder.fillImes(imis, context, false /* checkDefaultAttribute */, fallbackLocale, + true /* checkCountry */, SUBTYPE_MODE_KEYBOARD); + if (!builder.isEmpty()) { + return builder; + } + builder.fillImes(imis, context, true /* checkDefaultAttribute */, fallbackLocale, + false /* checkCountry */, SUBTYPE_MODE_KEYBOARD); + if (!builder.isEmpty()) { + return builder; + } + builder.fillImes(imis, context, false /* checkDefaultAttribute */, fallbackLocale, + false /* checkCountry */, SUBTYPE_MODE_KEYBOARD); + if (!builder.isEmpty()) { + return builder; + } + Slog.w(TAG, "No software keyboard is found. imis=" + Arrays.toString(imis.toArray()) + + " fallbackLocale=" + fallbackLocale); + return builder; + } + + private static InputMethodListBuilder getMinimumKeyboardSetWithSystemLocale( + final ArrayList<InputMethodInfo> imis, final Context context, + @Nullable final Locale systemLocale, @Nullable final Locale fallbackLocale) { + // Once the system becomes ready, we pick up at least one keyboard in the following order. + // Secondary users fall into this category in general. + // 1. checkDefaultAttribute: true, locale: systemLocale, checkCountry: true + // 2. checkDefaultAttribute: true, locale: systemLocale, checkCountry: false + // 3. checkDefaultAttribute: true, locale: fallbackLocale, checkCountry: true + // 4. checkDefaultAttribute: true, locale: fallbackLocale, checkCountry: false + // 5. checkDefaultAttribute: false, locale: fallbackLocale, checkCountry: true + // 6. checkDefaultAttribute: false, locale: fallbackLocale, checkCountry: false + // TODO: We should check isAsciiCapable instead of relying on fallbackLocale. + + final InputMethodListBuilder builder = new InputMethodListBuilder(); + builder.fillImes(imis, context, true /* checkDefaultAttribute */, systemLocale, + true /* checkCountry */, SUBTYPE_MODE_KEYBOARD); + if (!builder.isEmpty()) { + return builder; + } + builder.fillImes(imis, context, true /* checkDefaultAttribute */, systemLocale, + false /* checkCountry */, SUBTYPE_MODE_KEYBOARD); + if (!builder.isEmpty()) { + return builder; + } + builder.fillImes(imis, context, true /* checkDefaultAttribute */, fallbackLocale, + true /* checkCountry */, SUBTYPE_MODE_KEYBOARD); + if (!builder.isEmpty()) { + return builder; + } + builder.fillImes(imis, context, true /* checkDefaultAttribute */, fallbackLocale, + false /* checkCountry */, SUBTYPE_MODE_KEYBOARD); + if (!builder.isEmpty()) { + return builder; + } + builder.fillImes(imis, context, false /* checkDefaultAttribute */, fallbackLocale, + true /* checkCountry */, SUBTYPE_MODE_KEYBOARD); + if (!builder.isEmpty()) { + return builder; + } + builder.fillImes(imis, context, false /* checkDefaultAttribute */, fallbackLocale, + false /* checkCountry */, SUBTYPE_MODE_KEYBOARD); + if (!builder.isEmpty()) { + return builder; + } + Slog.w(TAG, "No software keyboard is found. imis=" + Arrays.toString(imis.toArray()) + + " systemLocale=" + systemLocale + " fallbackLocale=" + fallbackLocale); + return builder; + } + + public static ArrayList<InputMethodInfo> getDefaultEnabledImes(final Context context, + final boolean isSystemReady, final ArrayList<InputMethodInfo> imis) { + final Locale fallbackLocale = getFallbackLocaleForDefaultIme(imis, context); + if (!isSystemReady) { + // When the system is not ready, the system locale is not stable and reliable. Hence + // we will pick up IMEs that support software keyboard based on the fallback locale. + // Also pick up suitable IMEs regardless of the software keyboard support. + // (e.g. Voice IMEs) + return getMinimumKeyboardSetWithoutSystemLocale(imis, context, fallbackLocale) + .fillImes(imis, context, true /* checkDefaultAttribute */, fallbackLocale, + true /* checkCountry */, SUBTYPE_MODE_ANY) + .build(); + } + + // When the system is ready, we will primarily rely on the system locale, but also keep + // relying on the fallback locale as a last resort. + // Also pick up suitable IMEs regardless of the software keyboard support (e.g. Voice IMEs), + // then pick up suitable auxiliary IMEs when necessary (e.g. Voice IMEs with "automatic" + // subtype) + final Locale systemLocale = getSystemLocaleFromContext(context); + return getMinimumKeyboardSetWithSystemLocale(imis, context, systemLocale, fallbackLocale) + .fillImes(imis, context, true /* checkDefaultAttribute */, systemLocale, + true /* checkCountry */, SUBTYPE_MODE_ANY) + .fillAuxiliaryImes(imis, context) + .build(); } /** - * @deprecated Use {@link #isSystemIme(InputMethodInfo)} and - * {@link InputMethodInfo#isDefault(Context)} and - * {@link #isImeThatHasSubtypeOf(InputMethodInfo, Locale, boolean, String))} instead. + * @deprecated Use {@link #isSystemImeThatHasSubtypeOf(InputMethodInfo, Context, boolean, + * Locale, boolean, String)} instead. */ @Deprecated public static boolean isValidSystemDefaultIme( @@ -285,22 +410,25 @@ public class InputMethodUtils { } public static boolean containsSubtypeOf(final InputMethodInfo imi, - final Locale locale, final boolean ignoreCountry, final String mode) { + @Nullable final Locale locale, final boolean checkCountry, final String mode) { + if (locale == null) { + return false; + } final int N = imi.getSubtypeCount(); for (int i = 0; i < N; ++i) { final InputMethodSubtype subtype = imi.getSubtypeAt(i); - if (ignoreCountry) { - final Locale subtypeLocale = new Locale(getLanguageFromLocaleString( - subtype.getLocale())); - if (!subtypeLocale.getLanguage().equals(locale.getLanguage())) { - continue; - } - } else { + if (checkCountry) { // TODO: Use {@link Locale#toLanguageTag()} and // {@link Locale#forLanguageTag(languageTag)} instead. if (!TextUtils.equals(subtype.getLocale(), locale.toString())) { continue; } + } else { + final Locale subtypeLocale = new Locale(getLanguageFromLocaleString( + subtype.getLocale())); + if (!subtypeLocale.getLanguage().equals(locale.getLanguage())) { + continue; + } } if (mode == SUBTYPE_MODE_ANY || TextUtils.isEmpty(mode) || mode.equalsIgnoreCase(subtype.getMode())) { @@ -465,19 +593,9 @@ public class InputMethodUtils { return applicableSubtypes; } - private static List<InputMethodSubtype> getEnabledInputMethodSubtypeList( - Context context, InputMethodInfo imi, List<InputMethodSubtype> enabledSubtypes, - boolean allowsImplicitlySelectedSubtypes) { - if (allowsImplicitlySelectedSubtypes && enabledSubtypes.isEmpty()) { - enabledSubtypes = InputMethodUtils.getImplicitlyApplicableSubtypesLocked( - context.getResources(), imi); - } - return InputMethodSubtype.sort(context, 0, imi, enabledSubtypes); - } - /** * Returns the language component of a given locale string. - * TODO: Use {@link Locale#toLanguageTag()} and {@link Locale#forLanguageTag(languageTag)} + * TODO: Use {@link Locale#toLanguageTag()} and {@link Locale#forLanguageTag(String)} */ public static String getLanguageFromLocaleString(String locale) { final int idx = locale.indexOf('_'); diff --git a/core/java/com/android/internal/os/ProcessCpuTracker.java b/core/java/com/android/internal/os/ProcessCpuTracker.java index 86f580d..b5338df 100644 --- a/core/java/com/android/internal/os/ProcessCpuTracker.java +++ b/core/java/com/android/internal/os/ProcessCpuTracker.java @@ -23,8 +23,11 @@ import android.os.Process; import android.os.StrictMode; import android.os.SystemClock; import android.util.Slog; + import com.android.internal.util.FastPrintWriter; +import libcore.io.IoUtils; + import java.io.File; import java.io.FileInputStream; import java.io.PrintWriter; @@ -325,7 +328,12 @@ public class ProcessCpuTracker { mBaseIdleTime = idletime; } - mCurPids = collectStats("/proc", -1, mFirst, mCurPids, mProcStats); + final StrictMode.ThreadPolicy savedPolicy = StrictMode.allowThreadDiskReads(); + try { + mCurPids = collectStats("/proc", -1, mFirst, mCurPids, mProcStats); + } finally { + StrictMode.setThreadPolicy(savedPolicy); + } final float[] loadAverages = mLoadAverageData; if (Process.readProcFile("/proc/loadavg", LOAD_AVERAGE_FORMAT, @@ -847,12 +855,7 @@ public class ProcessCpuTracker { } catch (java.io.FileNotFoundException e) { } catch (java.io.IOException e) { } finally { - if (is != null) { - try { - is.close(); - } catch (java.io.IOException e) { - } - } + IoUtils.closeQuietly(is); StrictMode.setThreadPolicy(savedPolicy); } return null; diff --git a/core/java/com/android/internal/statusbar/IStatusBar.aidl b/core/java/com/android/internal/statusbar/IStatusBar.aidl index 57472f8..a3c0db4 100644 --- a/core/java/com/android/internal/statusbar/IStatusBar.aidl +++ b/core/java/com/android/internal/statusbar/IStatusBar.aidl @@ -42,5 +42,6 @@ oneway interface IStatusBar void toggleRecentApps(); void preloadRecentApps(); void cancelPreloadRecentApps(); + void showScreenPinningRequest(); } diff --git a/core/java/com/android/internal/statusbar/IStatusBarService.aidl b/core/java/com/android/internal/statusbar/IStatusBarService.aidl index e6bcea1..5e610ed 100644 --- a/core/java/com/android/internal/statusbar/IStatusBarService.aidl +++ b/core/java/com/android/internal/statusbar/IStatusBarService.aidl @@ -51,7 +51,7 @@ interface IStatusBarService void onNotificationVisibilityChanged( in String[] newlyVisibleKeys, in String[] noLongerVisibleKeys); void onNotificationExpansionChanged(in String key, in boolean userAction, in boolean expanded); - void setSystemUiVisibility(int vis, int mask); + void setSystemUiVisibility(int vis, int mask, String cause); void setWindowState(int window, int state); void showRecentApps(boolean triggeredFromAltTab); diff --git a/core/java/com/android/internal/util/MemInfoReader.java b/core/java/com/android/internal/util/MemInfoReader.java index 1dd9464..b71fa06 100644 --- a/core/java/com/android/internal/util/MemInfoReader.java +++ b/core/java/com/android/internal/util/MemInfoReader.java @@ -106,4 +106,8 @@ public final class MemInfoReader { public long getZramTotalSizeKb() { return mInfos[Debug.MEMINFO_ZRAM_TOTAL]; } + + public long[] getRawInfo() { + return mInfos; + } } diff --git a/core/java/com/android/internal/util/StateMachine.java b/core/java/com/android/internal/util/StateMachine.java index d26f79e..7ad3470 100644 --- a/core/java/com/android/internal/util/StateMachine.java +++ b/core/java/com/android/internal/util/StateMachine.java @@ -1940,13 +1940,19 @@ public class StateMachine { * @param args */ public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { - pw.println(getName() + ":"); - pw.println(" total records=" + getLogRecCount()); + pw.println(this.toString()); + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + sb.append(getName() + ":\n"); + sb.append(" total records=" + getLogRecCount() + "\n"); for (int i = 0; i < getLogRecSize(); i++) { - pw.printf(" rec[%d]: %s\n", i, getLogRec(i).toString()); - pw.flush(); + sb.append(" rec[" + i + "]: " + getLogRec(i).toString() + "\n"); } - pw.println("curState=" + getCurrentState().getName()); + sb.append("curState=" + getCurrentState().getName()); + return sb.toString(); } /** diff --git a/core/java/com/android/internal/view/BaseIWindow.java b/core/java/com/android/internal/view/BaseIWindow.java index 50a7a5e..993ab58 100644 --- a/core/java/com/android/internal/view/BaseIWindow.java +++ b/core/java/com/android/internal/view/BaseIWindow.java @@ -102,4 +102,8 @@ public class BaseIWindow extends IWindow.Stub { @Override public void doneAnimating() { } + + @Override + public void dispatchWindowShown() { + } } diff --git a/core/java/com/android/internal/view/RootViewSurfaceTaker.java b/core/java/com/android/internal/view/RootViewSurfaceTaker.java index 9c1b558..433ec73 100644 --- a/core/java/com/android/internal/view/RootViewSurfaceTaker.java +++ b/core/java/com/android/internal/view/RootViewSurfaceTaker.java @@ -1,3 +1,18 @@ +/* + * Copyright (C) 2010 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ package com.android.internal.view; import android.view.InputQueue; @@ -10,4 +25,5 @@ public interface RootViewSurfaceTaker { void setSurfaceFormat(int format); void setSurfaceKeepScreenOn(boolean keepOn); InputQueue.Callback willYouTakeTheInputQueue(); + void onRootViewScrollYChanged(int scrollY); } diff --git a/core/java/com/android/internal/widget/ActionBarContextView.java b/core/java/com/android/internal/widget/ActionBarContextView.java index 062a9b1..7c671e8 100644 --- a/core/java/com/android/internal/widget/ActionBarContextView.java +++ b/core/java/com/android/internal/widget/ActionBarContextView.java @@ -241,7 +241,7 @@ public class ActionBarContextView extends AbsActionBarView implements AnimatorLi if (!mSplitActionBar) { menu.addMenuPresenter(mActionMenuPresenter, mPopupContext); mMenuView = (ActionMenuView) mActionMenuPresenter.getMenuView(this); - mMenuView.setBackgroundDrawable(null); + mMenuView.setBackground(null); addView(mMenuView, layoutParams); } else { // Allow full screen width in split mode. diff --git a/core/java/com/android/internal/widget/ActionBarView.java b/core/java/com/android/internal/widget/ActionBarView.java index b9a85e5..654d08b 100644 --- a/core/java/com/android/internal/widget/ActionBarView.java +++ b/core/java/com/android/internal/widget/ActionBarView.java @@ -1341,6 +1341,22 @@ public class ActionBarView extends AbsActionBarView implements DecorToolbar { updateHomeAccessibility(mUpGoerFive.isEnabled()); } + @Override + public void setMenuCallbacks(MenuPresenter.Callback presenterCallback, + MenuBuilder.Callback menuBuilderCallback) { + if (mActionMenuPresenter != null) { + mActionMenuPresenter.setCallback(presenterCallback); + } + if (mOptionsMenu != null) { + mOptionsMenu.setCallback(menuBuilderCallback); + } + } + + @Override + public Menu getMenu() { + return mOptionsMenu; + } + static class SavedState extends BaseSavedState { int expandedMenuItemId; boolean isOverflowOpen; diff --git a/core/java/com/android/internal/widget/DecorToolbar.java b/core/java/com/android/internal/widget/DecorToolbar.java index f89f0b7..fb413b5 100644 --- a/core/java/com/android/internal/widget/DecorToolbar.java +++ b/core/java/com/android/internal/widget/DecorToolbar.java @@ -27,6 +27,8 @@ import android.view.ViewGroup; import android.view.Window; import android.widget.AdapterView; import android.widget.SpinnerAdapter; + +import com.android.internal.view.menu.MenuBuilder; import com.android.internal.view.menu.MenuPresenter; /** @@ -93,4 +95,11 @@ public interface DecorToolbar { void setDefaultNavigationIcon(Drawable icon); void saveHierarchyState(SparseArray<Parcelable> toolbarStates); void restoreHierarchyState(SparseArray<Parcelable> toolbarStates); + void setBackgroundDrawable(Drawable d); + int getHeight(); + void setVisibility(int visible); + int getVisibility(); + void setMenuCallbacks(MenuPresenter.Callback presenterCallback, + MenuBuilder.Callback menuBuilderCallback); + Menu getMenu(); } diff --git a/core/java/com/android/internal/widget/ILockSettingsObserver.aidl b/core/java/com/android/internal/widget/ILockSettingsObserver.aidl index 6c354d8..edf8f0e 100644 --- a/core/java/com/android/internal/widget/ILockSettingsObserver.aidl +++ b/core/java/com/android/internal/widget/ILockSettingsObserver.aidl @@ -18,5 +18,14 @@ package com.android.internal.widget; /** {@hide} */ oneway interface ILockSettingsObserver { + /** + * Called when a lock setting has changed. + * + * Note: Impementations of this should do as little work as possible, because this may be + * called synchronously while writing a setting. + * + * @param key the key of the setting that has changed or {@code null} if any may have changed. + * @param userId the user whose setting has changed. + */ void onLockSettingChanged(in String key, in int userId); } diff --git a/core/java/com/android/internal/widget/LockPatternUtils.java b/core/java/com/android/internal/widget/LockPatternUtils.java index d6885da..a4b8380 100644 --- a/core/java/com/android/internal/widget/LockPatternUtils.java +++ b/core/java/com/android/internal/widget/LockPatternUtils.java @@ -65,6 +65,13 @@ public class LockPatternUtils { private static final boolean DEBUG = false; /** + * If true, LockPatternUtils will cache its values in-process. While this leads to faster reads, + * it can cause problems because writes to to the settings are no longer synchronous + * across all processes. + */ + private static final boolean ENABLE_CLIENT_CACHE = false; + + /** * The maximum number of incorrect attempts before the user is prevented * from trying again for {@link #FAILED_ATTEMPT_TIMEOUT_MS}. */ @@ -207,8 +214,13 @@ public class LockPatternUtils { private ILockSettings getLockSettings() { if (mLockSettingsService == null) { - mLockSettingsService = LockPatternUtilsCache.getInstance( - ILockSettings.Stub.asInterface(ServiceManager.getService("lock_settings"))); + ILockSettings service = ILockSettings.Stub.asInterface( + ServiceManager.getService("lock_settings")); + if (ENABLE_CLIENT_CACHE) { + mLockSettingsService = LockPatternUtilsCache.getInstance(service); + } else { + mLockSettingsService = service; + } } return mLockSettingsService; } diff --git a/core/java/com/android/internal/widget/LockPatternUtilsCache.java b/core/java/com/android/internal/widget/LockPatternUtilsCache.java index 624f67c..a9524ff 100644 --- a/core/java/com/android/internal/widget/LockPatternUtilsCache.java +++ b/core/java/com/android/internal/widget/LockPatternUtilsCache.java @@ -18,7 +18,9 @@ package com.android.internal.widget; import android.os.IBinder; import android.os.RemoteException; +import android.os.UserHandle; import android.util.ArrayMap; +import android.util.Log; /** * A decorator for {@link ILockSettings} that caches the key-value responses in memory. @@ -28,9 +30,11 @@ import android.util.ArrayMap; */ public class LockPatternUtilsCache implements ILockSettings { - private static final String HAS_LOCK_PATTERN_CACHE_KEY + private static final String TAG = "LockPatternUtilsCache"; + + public static final String HAS_LOCK_PATTERN_CACHE_KEY = "LockPatternUtils.Cache.HasLockPatternCacheKey"; - private static final String HAS_LOCK_PASSWORD_CACHE_KEY + public static final String HAS_LOCK_PASSWORD_CACHE_KEY = "LockPatternUtils.Cache.HasLockPasswordCacheKey"; private static LockPatternUtilsCache sInstance; @@ -53,7 +57,7 @@ public class LockPatternUtilsCache implements ILockSettings { // ILockSettings - private LockPatternUtilsCache(ILockSettings service) { + public LockPatternUtilsCache(ILockSettings service) { mService = service; try { service.registerObserver(mObserver); @@ -186,6 +190,7 @@ public class LockPatternUtilsCache implements ILockSettings { // Caching private Object peekCache(String key, int userId) { + if (!validateUserId(userId)) return null; synchronized (mCache) { // Safe to reuse mCacheKey, because it is not stored in the map. return mCache.get(mCacheKey.set(key, userId)); @@ -193,6 +198,7 @@ public class LockPatternUtilsCache implements ILockSettings { } private void putCache(String key, int userId, Object value) { + if (!validateUserId(userId)) return; synchronized (mCache) { // Create a new key, because this will be stored in the map. mCache.put(new CacheKey().set(key, userId), value); @@ -200,9 +206,14 @@ public class LockPatternUtilsCache implements ILockSettings { } private void invalidateCache(String key, int userId) { + if (!validateUserId(userId)) return; synchronized (mCache) { - // Safe to reuse mCacheKey, because it is not stored in the map. - mCache.remove(mCacheKey.set(key, userId)); + if (key != null) { + // Safe to reuse mCacheKey, because it is not stored in the map. + mCache.remove(mCacheKey.set(key, userId)); + } else { + mCache.clear(); + } } } @@ -213,6 +224,14 @@ public class LockPatternUtilsCache implements ILockSettings { } }; + private final boolean validateUserId(int userId) { + if (userId < UserHandle.USER_OWNER) { + Log.e(TAG, "User " + userId + " not supported: Must be a concrete user."); + return false; + } + return true; + } + private static final class CacheKey { String key; int userId; diff --git a/core/java/com/android/internal/widget/ResolverDrawerLayout.java b/core/java/com/android/internal/widget/ResolverDrawerLayout.java index 375822f..25b4945 100644 --- a/core/java/com/android/internal/widget/ResolverDrawerLayout.java +++ b/core/java/com/android/internal/widget/ResolverDrawerLayout.java @@ -63,18 +63,22 @@ public class ResolverDrawerLayout extends ViewGroup { private float mCollapseOffset; private int mCollapsibleHeight; + private int mUncollapsibleHeight; private int mTopOffset; private boolean mIsDragging; private boolean mOpenOnClick; private boolean mOpenOnLayout; + private boolean mDismissOnScrollerFinished; private final int mTouchSlop; private final float mMinFlingVelocity; private final OverScroller mScroller; private final VelocityTracker mVelocityTracker; - private OnClickListener mClickOutsideListener; + private OnDismissedListener mOnDismissedListener; + private RunOnDismissedListener mRunOnDismissedListener; + private float mInitialTouchX; private float mInitialTouchY; private float mLastTouchY; @@ -143,8 +147,8 @@ public class ResolverDrawerLayout extends ViewGroup { return isSmallCollapsed() ? mMaxCollapsedHeightSmall : mMaxCollapsedHeight; } - public void setOnClickOutsideListener(OnClickListener listener) { - mClickOutsideListener = listener; + public void setOnDismissedListener(OnDismissedListener listener) { + mOnDismissedListener = listener; } @Override @@ -194,7 +198,7 @@ public class ResolverDrawerLayout extends ViewGroup { } if (mIsDragging) { - mScroller.abortAnimation(); + abortAnimation(); } return mIsDragging || mOpenOnClick; } @@ -213,12 +217,9 @@ public class ResolverDrawerLayout extends ViewGroup { mInitialTouchX = x; mInitialTouchY = mLastTouchY = y; mActivePointerId = ev.getPointerId(0); - if (findChildUnder(mInitialTouchX, mInitialTouchY) == null && - mClickOutsideListener != null) { - mIsDragging = handled = true; - } - handled |= mCollapsibleHeight > 0; - mScroller.abortAnimation(); + mIsDragging = findChildUnder(mInitialTouchX, mInitialTouchY) != null; + handled = (!mIsDragging && mOnDismissedListener != null) || mCollapsibleHeight > 0; + abortAnimation(); } break; @@ -264,11 +265,12 @@ public class ResolverDrawerLayout extends ViewGroup { break; case MotionEvent.ACTION_UP: { + final boolean wasDragging = mIsDragging; mIsDragging = false; - if (!mIsDragging && findChildUnder(mInitialTouchX, mInitialTouchY) == null && + if (!wasDragging && findChildUnder(mInitialTouchX, mInitialTouchY) == null && findChildUnder(ev.getX(), ev.getY()) == null) { - if (mClickOutsideListener != null) { - mClickOutsideListener.onClick(this); + if (mOnDismissedListener != null) { + dispatchOnDismissed(); resetTouch(); return true; } @@ -281,7 +283,13 @@ public class ResolverDrawerLayout extends ViewGroup { mVelocityTracker.computeCurrentVelocity(1000); final float yvel = mVelocityTracker.getYVelocity(mActivePointerId); if (Math.abs(yvel) > mMinFlingVelocity) { - smoothScrollTo(yvel < 0 ? 0 : mCollapsibleHeight, yvel); + if (mOnDismissedListener != null + && yvel > 0 && mCollapseOffset > mCollapsibleHeight) { + smoothScrollTo(mCollapsibleHeight + mUncollapsibleHeight, yvel); + mDismissOnScrollerFinished = true; + } else { + smoothScrollTo(yvel < 0 ? 0 : mCollapsibleHeight, yvel); + } } else { smoothScrollTo( mCollapseOffset < mCollapsibleHeight / 2 ? 0 : mCollapsibleHeight, 0); @@ -327,17 +335,27 @@ public class ResolverDrawerLayout extends ViewGroup { @Override public void computeScroll() { super.computeScroll(); - if (!mScroller.isFinished()) { - final boolean keepGoing = mScroller.computeScrollOffset(); + if (mScroller.computeScrollOffset()) { + final boolean keepGoing = !mScroller.isFinished(); performDrag(mScroller.getCurrY() - mCollapseOffset); if (keepGoing) { postInvalidateOnAnimation(); + } else if (mDismissOnScrollerFinished && mOnDismissedListener != null) { + mRunOnDismissedListener = new RunOnDismissedListener(); + post(mRunOnDismissedListener); } } } + private void abortAnimation() { + mScroller.abortAnimation(); + mRunOnDismissedListener = null; + mDismissOnScrollerFinished = false; + } + private float performDrag(float dy) { - final float newPos = Math.max(0, Math.min(mCollapseOffset + dy, mCollapsibleHeight)); + final float newPos = Math.max(0, Math.min(mCollapseOffset + dy, + mCollapsibleHeight + mUncollapsibleHeight)); if (newPos != mCollapseOffset) { dy = newPos - mCollapseOffset; final int childCount = getChildCount(); @@ -356,11 +374,18 @@ public class ResolverDrawerLayout extends ViewGroup { return 0; } - private void smoothScrollTo(int yOffset, float velocity) { - if (getMaxCollapsedHeight() == 0) { - return; + void dispatchOnDismissed() { + if (mOnDismissedListener != null) { + mOnDismissedListener.onDismissed(); } - mScroller.abortAnimation(); + if (mRunOnDismissedListener != null) { + removeCallbacks(mRunOnDismissedListener); + mRunOnDismissedListener = null; + } + } + + private void smoothScrollTo(int yOffset, float velocity) { + abortAnimation(); final int sy = (int) mCollapseOffset; int dy = yOffset - sy; if (dy == 0) { @@ -490,6 +515,7 @@ public class ResolverDrawerLayout extends ViewGroup { protected void onDetachedFromWindow() { super.onDetachedFromWindow(); getViewTreeObserver().removeOnTouchModeChangeListener(mTouchModeChangeListener); + abortAnimation(); } @Override @@ -585,6 +611,7 @@ public class ResolverDrawerLayout extends ViewGroup { mCollapsibleHeight = Math.max(0, heightUsed - alwaysShowHeight - getMaxCollapsedHeight()); + mUncollapsibleHeight = heightUsed - mCollapsibleHeight; if (isLaidOut()) { mCollapseOffset = Math.min(mCollapseOffset, mCollapsibleHeight); @@ -734,4 +761,15 @@ public class ResolverDrawerLayout extends ViewGroup { } }; } + + public interface OnDismissedListener { + public void onDismissed(); + } + + private class RunOnDismissedListener implements Runnable { + @Override + public void run() { + dispatchOnDismissed(); + } + } } diff --git a/core/java/com/android/internal/widget/ToolbarWidgetWrapper.java b/core/java/com/android/internal/widget/ToolbarWidgetWrapper.java index 324a6c9..054ca30 100644 --- a/core/java/com/android/internal/widget/ToolbarWidgetWrapper.java +++ b/core/java/com/android/internal/widget/ToolbarWidgetWrapper.java @@ -657,4 +657,36 @@ public class ToolbarWidgetWrapper implements DecorToolbar { mToolbar.restoreHierarchyState(toolbarStates); } + @Override + public void setBackgroundDrawable(Drawable d) { + //noinspection deprecation + mToolbar.setBackgroundDrawable(d); + } + + @Override + public int getHeight() { + return mToolbar.getHeight(); + } + + @Override + public void setVisibility(int visible) { + mToolbar.setVisibility(visible); + } + + @Override + public int getVisibility() { + return mToolbar.getVisibility(); + } + + @Override + public void setMenuCallbacks(MenuPresenter.Callback presenterCallback, + MenuBuilder.Callback menuBuilderCallback) { + mToolbar.setMenuCallbacks(presenterCallback, menuBuilderCallback); + } + + @Override + public Menu getMenu() { + return mToolbar.getMenu(); + } + } |