diff options
Diffstat (limited to 'core/java/android')
28 files changed, 537 insertions, 485 deletions
diff --git a/core/java/android/animation/AnimatorSet.java b/core/java/android/animation/AnimatorSet.java index 7c13dbe..0aa8fdd 100644 --- a/core/java/android/animation/AnimatorSet.java +++ b/core/java/android/animation/AnimatorSet.java @@ -636,6 +636,7 @@ public final class AnimatorSet extends Animator { anim.mNodes = new ArrayList<Node>(); anim.mSortedNodes = new ArrayList<Node>(); anim.mReversible = mReversible; + anim.mSetListener = null; // Walk through the old nodes list, cloning each node and adding it to the new nodemap. // One problem is that the old node dependencies point to nodes in the old AnimatorSet. diff --git a/core/java/android/app/ActivityManagerNative.java b/core/java/android/app/ActivityManagerNative.java index 394b183..677fcef 100644 --- a/core/java/android/app/ActivityManagerNative.java +++ b/core/java/android/app/ActivityManagerNative.java @@ -2295,6 +2295,13 @@ public abstract class ActivityManagerNative extends Binder implements IActivityM reply.writeNoException(); return true; } + + case BOOT_ANIMATION_COMPLETE_TRANSACTION: { + data.enforceInterface(IActivityManager.descriptor); + bootAnimationComplete(); + reply.writeNoException(); + return true; + } } return super.onTransact(code, data, reply, flags); @@ -5301,5 +5308,16 @@ class ActivityManagerProxy implements IActivityManager reply.recycle(); } + @Override + public void bootAnimationComplete() throws RemoteException { + Parcel data = Parcel.obtain(); + Parcel reply = Parcel.obtain(); + data.writeInterfaceToken(IActivityManager.descriptor); + mRemote.transact(BOOT_ANIMATION_COMPLETE_TRANSACTION, data, reply, 0); + reply.readException(); + data.recycle(); + reply.recycle(); + } + private IBinder mRemote; } diff --git a/core/java/android/app/ApplicationPackageManager.java b/core/java/android/app/ApplicationPackageManager.java index 404268c..d746745 100644 --- a/core/java/android/app/ApplicationPackageManager.java +++ b/core/java/android/app/ApplicationPackageManager.java @@ -1645,30 +1645,6 @@ final class ApplicationPackageManager extends PackageManager { /** * @hide */ - public void addCrossProfileIntentsForPackage(String packageName, - int sourceUserId, int targetUserId) { - try { - mPM.addCrossProfileIntentsForPackage(packageName, sourceUserId, targetUserId); - } catch (RemoteException e) { - // Should never happen! - } - } - - /** - * @hide - */ - public void removeCrossProfileIntentsForPackage(String packageName, - int sourceUserId, int targetUserId) { - try { - mPM.removeCrossProfileIntentsForPackage(packageName, sourceUserId, targetUserId); - } catch (RemoteException e) { - // Should never happen! - } - } - - /** - * @hide - */ @Override public void clearCrossProfileIntentFilters(int sourceUserId) { try { diff --git a/core/java/android/app/BackStackRecord.java b/core/java/android/app/BackStackRecord.java index 8227915..b09f169 100644 --- a/core/java/android/app/BackStackRecord.java +++ b/core/java/android/app/BackStackRecord.java @@ -38,7 +38,6 @@ import android.view.ViewTreeObserver; import java.io.FileDescriptor; import java.io.PrintWriter; import java.util.ArrayList; -import java.util.Collection; final class BackStackState implements Parcelable { final int[] mOps; @@ -745,13 +744,8 @@ final class BackStackRecord extends FragmentTransaction implements SparseArray<Fragment> firstOutFragments = new SparseArray<Fragment>(); SparseArray<Fragment> lastInFragments = new SparseArray<Fragment>(); - calculateFragments(firstOutFragments, lastInFragments); - - TransitionState state = null; - if (firstOutFragments.size() != 0 || lastInFragments.size() != 0) { - state = beginTransition(firstOutFragments, lastInFragments, false); - } + beginTransition(firstOutFragments, lastInFragments, false); Op op = mHead; while (op != null) { @@ -842,10 +836,6 @@ final class BackStackRecord extends FragmentTransaction implements if (mAddToBackStack) { mManager.addBackStackState(this); } - - if (state != null) { - updateTransitionEndState(state, firstOutFragments, lastInFragments, false); - } } private static void setFirstOut(SparseArray<Fragment> fragments, Fragment fragment) { @@ -920,43 +910,6 @@ final class BackStackRecord extends FragmentTransaction implements op = op.next; } - - if (!haveTransitions(firstOutFragments, lastInFragments, false)) { - firstOutFragments.clear(); - lastInFragments.clear(); - } - } - - /** - * @return true if custom transitions exist on any fragment in firstOutFragments or - * lastInFragments or false otherwise. - */ - private static boolean haveTransitions(SparseArray<Fragment> firstOutFragments, - SparseArray<Fragment> lastInFragments, boolean isBack) { - for (int i = firstOutFragments.size() - 1; i >= 0; i--) { - Fragment f = firstOutFragments.valueAt(i); - if (isBack) { - if (f.getReturnTransition() != null || - f.getSharedElementReturnTransition() != null) { - return true; - } - } else if (f.getExitTransition() != null) { - return true; - } - } - - for (int i = lastInFragments.size() - 1; i >= 0; i--) { - Fragment f = lastInFragments.valueAt(i); - if (isBack) { - if (f.getReenterTransition() != null) { - return true; - } - } else if (f.getEnterTransition() != null || - f.getSharedElementEnterTransition() != null) { - return true; - } - } - return false; } /** @@ -1003,11 +956,6 @@ final class BackStackRecord extends FragmentTransaction implements op = op.next; } - - if (!haveTransitions(firstOutFragments, lastInFragments, true)) { - firstOutFragments.clear(); - lastInFragments.clear(); - } } /** @@ -1038,8 +986,8 @@ final class BackStackRecord extends FragmentTransaction implements * @param isBack true if this is popping the back stack or false if this is a * forward operation. * @return The TransitionState used to complete the operation of the transition - * in {@link #updateTransitionEndState(android.app.BackStackRecord.TransitionState, - * android.util.SparseArray, android.util.SparseArray, boolean)}. + * in {@link #setNameOverrides(android.app.BackStackRecord.TransitionState, java.util.ArrayList, + * java.util.ArrayList)}. */ private TransitionState beginTransition(SparseArray<Fragment> firstOutFragments, SparseArray<Fragment> lastInFragments, boolean isBack) { @@ -1050,16 +998,11 @@ final class BackStackRecord extends FragmentTransaction implements // add any, then no views will be targeted. state.nonExistentView = new View(mManager.mActivity); - ArrayMap<String, View> tempViews1 = new ArrayMap<String, View>(); - ArrayMap<String, View> tempViews2 = new ArrayMap<String, View>(); - ArrayList<String> tempNames = new ArrayList<String>(); - ArrayList<View> tempViewList = new ArrayList<View>(); - // Go over all leaving fragments. for (int i = 0; i < firstOutFragments.size(); i++) { int containerId = firstOutFragments.keyAt(i); configureTransitions(containerId, state, isBack, firstOutFragments, - lastInFragments, tempViews1, tempViews2, tempNames, tempViewList); + lastInFragments); } // Now go over all entering fragments that didn't have a leaving fragment. @@ -1067,28 +1010,33 @@ final class BackStackRecord extends FragmentTransaction implements int containerId = lastInFragments.keyAt(i); if (firstOutFragments.get(containerId) == null) { configureTransitions(containerId, state, isBack, firstOutFragments, - lastInFragments, tempViews1, tempViews2, tempNames, tempViewList); + lastInFragments); } } + return state; + } - if (state.overallTransitions.size() == 0) { - state = null; + private static Transition cloneTransition(Transition transition) { + if (transition != null) { + transition = transition.clone(); } - return state; + return transition; } private static Transition getEnterTransition(Fragment inFragment, boolean isBack) { if (inFragment == null) { return null; } - return isBack ? inFragment.getReenterTransition() : inFragment.getEnterTransition(); + return cloneTransition(isBack ? inFragment.getReenterTransition() : + inFragment.getEnterTransition()); } private static Transition getExitTransition(Fragment outFragment, boolean isBack) { if (outFragment == null) { return null; } - return isBack ? outFragment.getReturnTransition() : outFragment.getExitTransition(); + return cloneTransition(isBack ? outFragment.getReturnTransition() : + outFragment.getExitTransition()); } private static Transition getSharedElementTransition(Fragment inFragment, Fragment outFragment, @@ -1096,34 +1044,32 @@ final class BackStackRecord extends FragmentTransaction implements if (inFragment == null || outFragment == null) { return null; } - return isBack ? outFragment.getSharedElementReturnTransition() : - inFragment.getSharedElementEnterTransition(); + return cloneTransition(isBack ? outFragment.getSharedElementReturnTransition() : + inFragment.getSharedElementEnterTransition()); } - private static Transition captureExitingViews(Transition exitTransition, Fragment outFragment, - ArrayList<View> viewList) { + private static ArrayList<View> captureExitingViews(Transition exitTransition, + Fragment outFragment) { + ArrayList<View> viewList = null; if (exitTransition != null) { + viewList = new ArrayList<View>(); View root = outFragment.getView(); - viewList.clear(); root.captureTransitioningViews(viewList); - if (viewList.isEmpty()) { - exitTransition = null; - } else { - addTransitioningViews(exitTransition, viewList); - } + addTargets(exitTransition, viewList); } - return exitTransition; + return viewList; } private ArrayMap<String, View> remapSharedElements(TransitionState state, Fragment outFragment, - ArrayMap<String, View> namedViews, ArrayMap<String, View> tempViews2, boolean isBack) { + boolean isBack) { + ArrayMap<String, View> namedViews = new ArrayMap<String, View>(); if (mSharedElementSourceNames != null) { outFragment.getView().findNamedViews(namedViews); if (isBack) { namedViews.retainAll(mSharedElementTargetNames); } else { namedViews = remapNames(mSharedElementSourceNames, mSharedElementTargetNames, - namedViews, tempViews2); + namedViews); } } @@ -1147,41 +1093,94 @@ final class BackStackRecord extends FragmentTransaction implements * We will add to the views before the end state of the transition is captured so that the * views will appear. At the start of the transition, we clear the list of targets so that * we can restore the state of the transition and use it again. + * + * <p>The shared element transition maps its shared elements immediately prior to + * capturing the final state of the Transition.</p> */ - private void prepareEnterTransition(TransitionState state, final Transition enterTransition, - final View container, final Fragment inFragment) { - if (enterTransition != null) { - final ArrayList<View> enteringViews = new ArrayList<View>(); - final View nonExistentView = state.nonExistentView; - enterTransition.addTarget(state.nonExistentView); - enterTransition.addListener(new Transition.TransitionListenerAdapter() { - @Override - public void onTransitionStart(Transition transition) { - transition.removeListener(this); - transition.removeTarget(nonExistentView); - int numViews = enteringViews.size(); - for (int i = 0; i < numViews; i++) { - transition.removeTarget(enteringViews.get(i)); - } - } - }); - container.getViewTreeObserver().addOnPreDrawListener( - new ViewTreeObserver.OnPreDrawListener() { - @Override - public boolean onPreDraw() { - container.getViewTreeObserver().removeOnPreDrawListener(this); + private ArrayList<View> addTransitionTargets(final TransitionState state, + final Transition enterTransition, final Transition sharedElementTransition, + final Transition overallTransition, final View container, + final Fragment inFragment, final Fragment outFragment, + final ArrayList<View> hiddenFragmentViews, final boolean isBack) { + if (enterTransition == null && sharedElementTransition == null && + overallTransition == null) { + return null; + } + final ArrayList<View> enteringViews = new ArrayList<View>(); + container.getViewTreeObserver().addOnPreDrawListener( + new ViewTreeObserver.OnPreDrawListener() { + @Override + public boolean onPreDraw() { + container.getViewTreeObserver().removeOnPreDrawListener(this); + + // Don't include any newly-hidden fragments in the transition. + excludeHiddenFragments(hiddenFragmentViews, inFragment.mContainerId, + overallTransition); + + if (sharedElementTransition != null) { + ArrayMap<String, View> namedViews = mapSharedElementsIn( + state, isBack, inFragment); + + setEpicenterIn(namedViews, state); + + callSharedElementEnd(state, inFragment, outFragment, isBack, + namedViews); + } + + if (enterTransition != null) { View view = inFragment.getView(); if (view != null) { view.captureTransitioningViews(enteringViews); - addTransitioningViews(enterTransition, enteringViews); + addTargets(enterTransition, enteringViews); } - return true; + setSharedElementEpicenter(enterTransition, state); } - }); - setSharedElementEpicenter(enterTransition, state); + return true; + } + }); + return enteringViews; + } + + private void callSharedElementEnd(TransitionState state, Fragment inFragment, + Fragment outFragment, boolean isBack, ArrayMap<String, View> namedViews) { + SharedElementCallback sharedElementCallback = isBack ? + outFragment.mEnterTransitionCallback : + inFragment.mEnterTransitionCallback; + ArrayList<String> names = new ArrayList<String>(namedViews.keySet()); + ArrayList<View> views = new ArrayList<View>(namedViews.values()); + sharedElementCallback.onSharedElementEnd(names, views, null); + } + + private void setEpicenterIn(ArrayMap<String, View> namedViews, TransitionState state) { + if (mSharedElementTargetNames != null && !namedViews.isEmpty()) { + // now we know the epicenter of the entering transition. + View epicenter = namedViews + .get(mSharedElementTargetNames.get(0)); + if (epicenter != null) { + state.enteringEpicenterView = epicenter; + } } } + private ArrayMap<String, View> mapSharedElementsIn(TransitionState state, + boolean isBack, Fragment inFragment) { + // Now map the shared elements in the incoming fragment + ArrayMap<String, View> namedViews = mapEnteringSharedElements(state, inFragment, isBack); + + // remap shared elements and set the name mapping used + // in the shared element transition. + if (isBack) { + inFragment.mExitTransitionCallback.onMapSharedElements( + mSharedElementTargetNames, namedViews); + setBackNameOverrides(state, namedViews, true); + } else { + inFragment.mEnterTransitionCallback.onMapSharedElements( + mSharedElementTargetNames, namedViews); + setNameOverrides(state, namedViews, true); + } + return namedViews; + } + private static Transition mergeTransitions(Transition enterTransition, Transition exitTransition, Transition sharedElementTransition, Fragment inFragment, boolean isBack) { @@ -1209,26 +1208,16 @@ final class BackStackRecord extends FragmentTransaction implements * Configures custom transitions for a specific fragment container. * * @param containerId The container ID of the fragments to configure the transition for. - * @param state The Transition State to be shared with {@link #updateTransitionEndState( - * android.app.BackStackRecord.TransitionState, android.util.SparseArray, - * android.util.SparseArray, boolean)} later. + * @param state The Transition State keeping track of the executing transitions. * @param firstOutFragments The list of first fragments to be removed, keyed on the * container ID. * @param lastInFragments The list of last fragments to be added, keyed on the * container ID. * @param isBack true if this is popping the back stack or false if this is a * forward operation. - * @param tempViews1 A temporary mapping of names to Views, used to avoid allocation - * inside a loop. - * @param tempViews2 A temporary mapping of names to Views, used to avoid allocation - * inside a loop. - * @param tempNames A temporary list of Strings, used to avoid allocation inside a loop. - * @param tempViewList A temporary list of Views, used to avoid allocation inside a loop. */ private void configureTransitions(int containerId, TransitionState state, boolean isBack, - SparseArray<Fragment> firstOutFragments, SparseArray<Fragment> lastInFragments, - ArrayMap<String, View> tempViews1, ArrayMap<String, View> tempViews2, - ArrayList<String> tempNames, ArrayList<View> tempViewList) { + SparseArray<Fragment> firstOutFragments, SparseArray<Fragment> lastInFragments) { ViewGroup sceneRoot = (ViewGroup) mManager.mContainer.findViewById(containerId); if (sceneRoot != null) { Fragment inFragment = lastInFragments.get(containerId); @@ -1238,146 +1227,150 @@ final class BackStackRecord extends FragmentTransaction implements Transition sharedElementTransition = getSharedElementTransition(inFragment, outFragment, isBack); Transition exitTransition = getExitTransition(outFragment, isBack); - exitTransition = captureExitingViews(exitTransition, outFragment, tempViewList); - ArrayMap<String, View> namedViews = tempViews1; - namedViews.clear(); - if (sharedElementTransition != null) { - namedViews = remapSharedElements(state, - outFragment, namedViews, tempViews2, isBack); + if (enterTransition == null && sharedElementTransition == null && + exitTransition == null) { + return; // no transitions! + } + ArrayList<View> exitingViews = captureExitingViews(exitTransition, outFragment); + if (exitingViews == null || exitingViews.isEmpty()) { + exitTransition = null; } - // Notify the start of the transition. - SharedElementCallback callback = isBack ? - outFragment.mEnterTransitionCallback : - inFragment.mEnterTransitionCallback; - tempNames.clear(); - tempNames.addAll(namedViews.keySet()); - tempViewList.clear(); - tempViewList.addAll(namedViews.values()); - callback.onSharedElementStart(tempNames, tempViewList, null); + ArrayMap<String, View> namedViews = null; + if (sharedElementTransition != null) { + namedViews = remapSharedElements(state, outFragment, isBack); + + // Notify the start of the transition. + SharedElementCallback callback = isBack ? + outFragment.mEnterTransitionCallback : + inFragment.mEnterTransitionCallback; + ArrayList<String> names = new ArrayList<String>(namedViews.keySet()); + ArrayList<View> views = new ArrayList<View>(namedViews.values()); + callback.onSharedElementStart(names, views, null); + } // Set the epicenter of the exit transition - if (mSharedElementTargetNames != null && exitTransition != null) { + if (mSharedElementTargetNames != null && exitTransition != null && namedViews != null) { View epicenterView = namedViews.get(mSharedElementTargetNames.get(0)); if (epicenterView != null) { setEpicenter(exitTransition, epicenterView); } } - prepareEnterTransition(state, enterTransition, sceneRoot, inFragment); - Transition transition = mergeTransitions(enterTransition, exitTransition, sharedElementTransition, inFragment, isBack); if (transition != null) { - state.overallTransitions.put(containerId, transition); + ArrayList<View> hiddenFragments = new ArrayList<View>(); + ArrayList<View> enteringViews = addTransitionTargets(state, enterTransition, + sharedElementTransition, transition, sceneRoot, inFragment, outFragment, + hiddenFragments, isBack); + transition.setNameOverrides(state.nameOverrides); // We want to exclude hidden views later, so we need a non-null list in the // transition now. transition.excludeTarget(state.nonExistentView, true); // Now exclude all currently hidden fragments. - excludeHiddenFragments(state, containerId, transition); - cleanupHiddenFragments(transition, state); + excludeHiddenFragments(hiddenFragments, containerId, transition); TransitionManager.beginDelayedTransition(sceneRoot, transition); + // Remove the view targeting after the transition starts + removeTargetedViewsFromTransitions(sceneRoot, state.nonExistentView, + enterTransition, enteringViews, exitTransition, exitingViews, + transition, hiddenFragments); } } } /** + * After the transition has started, remove all targets that we added to the transitions + * so that the transitions are left in a clean state. + */ + private void removeTargetedViewsFromTransitions( + final ViewGroup sceneRoot, final View nonExistingView, + final Transition enterTransition, final ArrayList<View> enteringViews, + final Transition exitTransition, final ArrayList<View> exitingViews, + final Transition overallTransition, final ArrayList<View> hiddenViews) { + if (enterTransition != null || exitTransition != null) { + sceneRoot.getViewTreeObserver().addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() { + @Override + public boolean onPreDraw() { + sceneRoot.getViewTreeObserver().removeOnPreDrawListener(this); + if (enterTransition != null) { + enterTransition.removeTarget(nonExistingView); + removeTargets(enterTransition, enteringViews); + } + if (exitTransition != null) { + removeTargets(exitTransition, exitingViews); + } + int numViews = hiddenViews.size(); + for (int i = 0; i < numViews; i++) { + overallTransition.excludeTarget(hiddenViews.get(i), false); + } + overallTransition.excludeTarget(nonExistingView, false); + return true; + } + }); + } + } + + 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)); + } + } + + 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)); + } + } + + /** * Remaps a name-to-View map, substituting different names for keys. * * @param inMap A list of keys found in the map, in the order in toGoInMap * @param toGoInMap A list of keys to use for the new map, in the order of inMap * @param namedViews The current mapping - * @param tempMap A temporary mapping that will be filled with the new values. - * @return tempMap after it has been mapped with the new names as keys. + * @return a new Map after it has been mapped with the new names as keys. */ private static ArrayMap<String, View> remapNames(ArrayList<String> inMap, - ArrayList<String> toGoInMap, ArrayMap<String, View> namedViews, - ArrayMap<String, View> tempMap) { - tempMap.clear(); + ArrayList<String> toGoInMap, ArrayMap<String, View> namedViews) { + ArrayMap<String, View> remappedViews = new ArrayMap<String, View>(); if (!namedViews.isEmpty()) { int numKeys = inMap.size(); for (int i = 0; i < numKeys; i++) { View view = namedViews.get(inMap.get(i)); + if (view != null) { - tempMap.put(toGoInMap.get(i), view); + remappedViews.put(toGoInMap.get(i), view); } } } - return tempMap; + return remappedViews; } /** - * After making all fragment changes, this updates the custom transitions to take into - * account the entering views and any remapping. + * Maps shared elements to views in the entering fragment. * * @param state The transition State as returned from {@link #beginTransition( * android.util.SparseArray, android.util.SparseArray, boolean)}. - * @param outFragments The list of first fragments to be removed, keyed on the - * container ID. - * @param inFragments The list of last fragments to be added, keyed on the - * container ID. + * @param inFragment The last fragment to be added. * @param isBack true if this is popping the back stack or false if this is a * forward operation. */ - private void updateTransitionEndState(TransitionState state, SparseArray<Fragment> outFragments, - SparseArray<Fragment> inFragments, boolean isBack) { - ArrayMap<String, View> tempViews1 = new ArrayMap<String, View>(); - ArrayMap<String, View> tempViews2 = new ArrayMap<String, View>(); - ArrayList<String> tempNames = new ArrayList<String>(); - ArrayList<View> tempViews = new ArrayList<View>(); - - int numInFragments = inFragments.size(); - for (int i = 0; i < numInFragments; i++) { - Fragment inFragment = inFragments.valueAt(i); - tempViews1.clear(); - ArrayMap<String, View> namedViews = mapEnteringSharedElements(inFragment, tempViews1, - tempViews2, isBack); - // remap shared elements and set the name mapping used in the shared element transition. - if (isBack) { - inFragment.mExitTransitionCallback.onMapSharedElements( - mSharedElementTargetNames, namedViews); - setBackNameOverrides(state, namedViews, true); - } else { - inFragment.mEnterTransitionCallback.onMapSharedElements( - mSharedElementTargetNames, namedViews); - setNameOverrides(state, namedViews, true); - } - - if (mSharedElementTargetNames != null && !namedViews.isEmpty()) { - // now we know the epicenter of the entering transition. - View epicenter = namedViews.get(mSharedElementTargetNames.get(0)); - if (epicenter != null) { - state.enteringEpicenterView = epicenter; - } - } - - int containerId = inFragments.keyAt(i); - SharedElementCallback sharedElementCallback = isBack ? - outFragments.get(containerId).mEnterTransitionCallback : - inFragment.mEnterTransitionCallback; - tempNames.clear(); - tempNames.addAll(namedViews.keySet()); - tempViews.clear(); - tempViews.addAll(namedViews.values()); - sharedElementCallback.onSharedElementEnd(tempNames, tempViews, null); - } - - // Don't include any newly-hidden fragments in the transition. - excludeHiddenFragments(state); - } - - private ArrayMap<String, View> mapEnteringSharedElements(Fragment inFragment, - ArrayMap<String, View> namedViews, ArrayMap<String, View> tempViews2, boolean isBack) { + private ArrayMap<String, View> mapEnteringSharedElements(TransitionState state, + Fragment inFragment, boolean isBack) { + ArrayMap<String, View> namedViews = new ArrayMap<String, View>(); View root = inFragment.getView(); if (root != null) { if (mSharedElementSourceNames != null) { root.findNamedViews(namedViews); if (isBack) { namedViews = remapNames(mSharedElementSourceNames, - mSharedElementTargetNames, namedViews, tempViews2); + mSharedElementTargetNames, namedViews); } else { namedViews.retainAll(mSharedElementTargetNames); } @@ -1386,21 +1379,7 @@ final class BackStackRecord extends FragmentTransaction implements return namedViews; } - private static void cleanupHiddenFragments(Transition transition, TransitionState state) { - final ArrayList<View> hiddenViews = state.hiddenFragmentViews; - transition.addListener(new Transition.TransitionListenerAdapter() { - @Override - public void onTransitionStart(Transition transition) { - transition.removeListener(this); - int numViews = hiddenViews.size(); - for (int i = 0; i < numViews; i++) { - transition.excludeTarget(hiddenViews.get(i), false); - } - } - }); - } - - private void excludeHiddenFragments(TransitionState state, int containerId, + private void excludeHiddenFragments(final ArrayList<View> hiddenFragmentViews, int containerId, Transition transition) { if (mManager.mAdded != null) { for (int i = 0; i < mManager.mAdded.size(); i++) { @@ -1408,44 +1387,19 @@ final class BackStackRecord extends FragmentTransaction implements if (fragment.mView != null && fragment.mContainer != null && fragment.mContainerId == containerId) { if (fragment.mHidden) { - if (!state.hiddenFragmentViews.contains(fragment.mView)) { + if (!hiddenFragmentViews.contains(fragment.mView)) { transition.excludeTarget(fragment.mView, true); - state.hiddenFragmentViews.add(fragment.mView); + hiddenFragmentViews.add(fragment.mView); } } else { transition.excludeTarget(fragment.mView, false); - state.hiddenFragmentViews.remove(fragment.mView); + hiddenFragmentViews.remove(fragment.mView); } } } } } - private void excludeHiddenFragments(TransitionState state) { - int numTransitions = state.overallTransitions.size(); - for (int i = 0; i < numTransitions; i++) { - Transition transition = state.overallTransitions.valueAt(i); - int containerId = state.overallTransitions.keyAt(i); - excludeHiddenFragments(state, containerId, transition); - } - } - - private static void addTransitioningViews(Transition transition, final Collection<View> views) { - for (View view : views) { - transition.addTarget(view); - } - - transition.addListener(new Transition.TransitionListenerAdapter() { - @Override - public void onTransitionStart(Transition transition) { - transition.removeListener(this); - for (View view : views) { - transition.removeTarget(view); - } - } - }); - } - private static void setEpicenter(Transition transition, View view) { final Rect epicenter = new Rect(); view.getBoundsOnScreen(epicenter); @@ -1566,10 +1520,7 @@ final class BackStackRecord extends FragmentTransaction implements if (doStateMove) { mManager.moveToState(mManager.mCurState, FragmentManagerImpl.reverseTransit(mTransition), mTransitionStyle, true); - if (state != null) { - updateTransitionEndState(state, firstOutFragments, lastInFragments, true); - state = null; - } + state = null; } if (mIndex >= 0) { @@ -1609,11 +1560,14 @@ final class BackStackRecord extends FragmentTransaction implements for (int i = 0; i < count; i++) { String source = mSharedElementSourceNames.get(i); String originalTarget = mSharedElementTargetNames.get(i); - String target = namedViews.get(originalTarget).getTransitionName(); - if (isEnd) { - setNameOverride(state.nameOverrides, source, target); - } else { - setNameOverride(state.nameOverrides, target, source); + View view = namedViews.get(originalTarget); + if (view != null) { + String target = view.getTransitionName(); + if (isEnd) { + setNameOverride(state.nameOverrides, source, target); + } else { + setNameOverride(state.nameOverrides, target, source); + } } } } @@ -1649,10 +1603,7 @@ final class BackStackRecord extends FragmentTransaction implements } public class TransitionState { - public SparseArray<Transition> overallTransitions = new SparseArray<Transition>(); public ArrayMap<String, String> nameOverrides = new ArrayMap<String, String>(); - public ArrayList<View> hiddenFragmentViews = new ArrayList<View>(); - public View enteringEpicenterView; public View nonExistentView; } diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java index 91a0aed..64eafb0 100644 --- a/core/java/android/app/ContextImpl.java +++ b/core/java/android/app/ContextImpl.java @@ -2237,7 +2237,6 @@ class ContextImpl extends Context { mUser = user; mPackageInfo = packageInfo; - mContentResolver = new ApplicationContentResolver(this, mainThread, user); mResourcesManager = ResourcesManager.getInstance(); mDisplay = display; mOverrideConfiguration = overrideConfiguration; @@ -2284,6 +2283,8 @@ class ContextImpl extends Context { mOpPackageName = mBasePackageName; } } + + mContentResolver = new ApplicationContentResolver(this, mainThread, user); } void installSystemApplicationInfo(ApplicationInfo info, ClassLoader classLoader) { diff --git a/core/java/android/app/EnterTransitionCoordinator.java b/core/java/android/app/EnterTransitionCoordinator.java index 9c7728e..922561d 100644 --- a/core/java/android/app/EnterTransitionCoordinator.java +++ b/core/java/android/app/EnterTransitionCoordinator.java @@ -136,11 +136,12 @@ class EnterTransitionCoordinator extends ActivityTransitionCoordinator { if (sharedElements.isEmpty() || !sharedElements.valueAt(0).isLayoutRequested()) { viewsReady(sharedElements); } else { - sharedElements.valueAt(0).getViewTreeObserver() + final View sharedElement = sharedElements.valueAt(0); + sharedElement.getViewTreeObserver() .addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() { @Override public boolean onPreDraw() { - sharedElements.valueAt(0).getViewTreeObserver().removeOnPreDrawListener(this); + sharedElement.getViewTreeObserver().removeOnPreDrawListener(this); viewsReady(sharedElements); return true; } diff --git a/core/java/android/app/IActivityManager.java b/core/java/android/app/IActivityManager.java index 9483680..8fa1fd5 100644 --- a/core/java/android/app/IActivityManager.java +++ b/core/java/android/app/IActivityManager.java @@ -327,6 +327,8 @@ public interface IActivityManager extends IInterface { public void notifyActivityDrawn(IBinder token) throws RemoteException; public ActivityOptions getActivityOptions(IBinder token) throws RemoteException; + public void bootAnimationComplete() throws RemoteException; + public void setImmersive(IBinder token, boolean immersive) throws RemoteException; public boolean isImmersive(IBinder token) throws RemoteException; public boolean isTopActivityImmersive() throws RemoteException; @@ -772,4 +774,5 @@ public interface IActivityManager extends IInterface { int GET_APP_TASK_THUMBNAIL_SIZE_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+234; int RELEASE_ACTIVITY_INSTANCE_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+235; int RELEASE_SOME_ACTIVITIES_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+236; + int BOOT_ANIMATION_COMPLETE_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+237; } diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java index ea041e8..8f1343d 100644 --- a/core/java/android/app/admin/DevicePolicyManager.java +++ b/core/java/android/app/admin/DevicePolicyManager.java @@ -3245,11 +3245,15 @@ public class DevicePolicyManager { * Called by profile or device owners to update {@link Settings.Secure} settings. Validation * that the value of the setting is in the correct form for the setting type should be performed * by the caller. - * <p>The settings that can be updated with this method are: + * <p>The settings that can be updated by a profile or device owner with this method are: * <ul> * <li>{@link Settings.Secure#DEFAULT_INPUT_METHOD}</li> * <li>{@link Settings.Secure#SKIP_FIRST_USE_HINTS}</li> * </ul> + * <p>A device owner can additionally update the following settings: + * <ul> + * <li>{@link Settings.Secure#LOCATION_MODE}</li> + * </ul> * @param admin Which {@link DeviceAdminReceiver} this request is associated with. * @param setting The name of the setting to update. * @param value The value to update the setting to. diff --git a/core/java/android/app/job/JobParameters.java b/core/java/android/app/job/JobParameters.java index 724856a..62734f2 100644 --- a/core/java/android/app/job/JobParameters.java +++ b/core/java/android/app/job/JobParameters.java @@ -32,12 +32,15 @@ public class JobParameters implements Parcelable { private final int jobId; private final PersistableBundle extras; private final IBinder callback; + private final boolean overrideDeadlineExpired; /** @hide */ - public JobParameters(int jobId, PersistableBundle extras, IBinder callback) { + public JobParameters(IBinder callback, int jobId, PersistableBundle extras, + boolean overrideDeadlineExpired) { this.jobId = jobId; this.extras = extras; this.callback = callback; + this.overrideDeadlineExpired = overrideDeadlineExpired; } /** @@ -56,6 +59,16 @@ public class JobParameters implements Parcelable { return extras; } + /** + * For jobs with {@link android.app.job.JobInfo.Builder#setOverrideDeadline(long)} set, this + * provides an easy way to tell whether the job is being executed due to the deadline + * expiring. Note: If the job is running because its deadline expired, it implies that its + * constraints will not be met. + */ + public boolean isOverrideDeadlineExpired() { + return overrideDeadlineExpired; + } + /** @hide */ public IJobCallback getCallback() { return IJobCallback.Stub.asInterface(callback); @@ -65,6 +78,7 @@ public class JobParameters implements Parcelable { jobId = in.readInt(); extras = in.readPersistableBundle(); callback = in.readStrongBinder(); + overrideDeadlineExpired = in.readInt() == 1; } @Override @@ -77,6 +91,7 @@ public class JobParameters implements Parcelable { dest.writeInt(jobId); dest.writePersistableBundle(extras); dest.writeStrongBinder(callback); + dest.writeInt(overrideDeadlineExpired ? 1 : 0); } public static final Creator<JobParameters> CREATOR = new Creator<JobParameters>() { diff --git a/core/java/android/content/ContentResolver.java b/core/java/android/content/ContentResolver.java index b2b48e8..a09fee9 100644 --- a/core/java/android/content/ContentResolver.java +++ b/core/java/android/content/ContentResolver.java @@ -1763,7 +1763,7 @@ public abstract class ContentResolver { * @param extras any extras to pass to the SyncAdapter. */ public static void requestSync(Account account, String authority, Bundle extras) { - requestSyncAsUser(account, authority, UserHandle.getCallingUserId(), extras); + requestSyncAsUser(account, authority, UserHandle.myUserId(), extras); } /** @@ -1938,7 +1938,7 @@ public abstract class ContentResolver { * @param sync true if the provider should be synced when tickles are received for it */ public static void setSyncAutomatically(Account account, String authority, boolean sync) { - setSyncAutomaticallyAsUser(account, authority, sync, UserHandle.getCallingUserId()); + setSyncAutomaticallyAsUser(account, authority, sync, UserHandle.myUserId()); } /** @@ -2165,7 +2165,7 @@ public abstract class ContentResolver { * @param sync the master auto-sync setting that applies to all the providers and accounts */ public static void setMasterSyncAutomatically(boolean sync) { - setMasterSyncAutomaticallyAsUser(sync, UserHandle.getCallingUserId()); + setMasterSyncAutomaticallyAsUser(sync, UserHandle.myUserId()); } /** @@ -2297,7 +2297,7 @@ public abstract class ContentResolver { * @return true if there is a pending sync with the matching account and authority */ public static boolean isSyncPending(Account account, String authority) { - return isSyncPendingAsUser(account, authority, UserHandle.getCallingUserId()); + return isSyncPendingAsUser(account, authority, UserHandle.myUserId()); } /** diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java index f979a0c..61dd747 100644 --- a/core/java/android/content/Context.java +++ b/core/java/android/content/Context.java @@ -3062,6 +3062,14 @@ public abstract class Context { * "content://foo". It will not remove any prefix grants that exist at a * higher level. * + * <p>Prior to {@link android.os.Build.VERSION_CODES#L}, if you did not have + * regular permission access to a Uri, but had received access to it through + * a specific Uri permission grant, you could not revoke that grant with this + * function and a {@link SecurityException} would be thrown. As of + * {@link android.os.Build.VERSION_CODES#L}, this function will not throw a security exception, + * but will remove whatever permission grants to the Uri had been given to the app + * (or none).</p> + * * @param uri The Uri you would like to revoke access to. * @param modeFlags The desired access modes. Any combination of * {@link Intent#FLAG_GRANT_READ_URI_PERMISSION diff --git a/core/java/android/content/pm/IPackageManager.aidl b/core/java/android/content/pm/IPackageManager.aidl index 3e1f60a..6d9c58b 100644 --- a/core/java/android/content/pm/IPackageManager.aidl +++ b/core/java/android/content/pm/IPackageManager.aidl @@ -259,12 +259,6 @@ interface IPackageManager { void addCrossProfileIntentFilter(in IntentFilter intentFilter, String ownerPackage, int ownerUserId, int sourceUserId, int targetUserId, int flags); - void addCrossProfileIntentsForPackage(in String packageName, int sourceUserId, - int targetUserId); - - void removeCrossProfileIntentsForPackage(String packageName, int sourceUserId, - int targetUserId); - void clearCrossProfileIntentFilters(int sourceUserId, String ownerPackage, int ownerUserId); /** diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java index 5ce968b..8f3d90f 100644 --- a/core/java/android/content/pm/PackageManager.java +++ b/core/java/android/content/pm/PackageManager.java @@ -3893,22 +3893,6 @@ public abstract class PackageManager { public abstract void clearCrossProfileIntentFilters(int sourceUserId); /** - * Forwards all intents for {@link packageName} for user {@link sourceUserId} to user - * {@link targetUserId}. - * @hide - */ - public abstract void addCrossProfileIntentsForPackage(String packageName, - int sourceUserId, int targetUserId); - - /** - * Removes all intents for {@link packageName} for user {@link sourceUserId} to user - * {@link targetUserId}. - * @hide - */ - public abstract void removeCrossProfileIntentsForPackage(String packageName, - int sourceUserId, int targetUserId); - - /** * @hide */ public abstract Drawable loadItemIcon(PackageItemInfo itemInfo, ApplicationInfo appInfo); diff --git a/core/java/android/net/ConnectivityManager.java b/core/java/android/net/ConnectivityManager.java index 70b402d..7c69a7d 100644 --- a/core/java/android/net/ConnectivityManager.java +++ b/core/java/android/net/ConnectivityManager.java @@ -48,6 +48,8 @@ import java.net.InetAddress; import java.util.concurrent.atomic.AtomicInteger; import java.util.HashMap; +import libcore.net.event.NetworkEventDispatcher; + /** * Class that answers queries about the state of network connectivity. It also * notifies applications when network connectivity changes. Get an instance @@ -2467,7 +2469,20 @@ public class ConnectivityManager { * @return {@code true} on success, {@code false} if the {@link Network} is no longer valid. */ public static boolean setProcessDefaultNetwork(Network network) { - return NetworkUtils.bindProcessToNetwork(network == null ? NETID_UNSET : network.netId); + int netId = (network == null) ? NETID_UNSET : network.netId; + if (netId == NetworkUtils.getNetworkBoundToProcess()) { + return true; + } + if (NetworkUtils.bindProcessToNetwork(netId)) { + // Must flush DNS cache as new network may have different DNS resolutions. + InetAddress.clearDnsCache(); + // Must flush socket pool as idle sockets will be bound to previous network and may + // cause subsequent fetches to be performed on old network. + NetworkEventDispatcher.getInstance().onNetworkConfigurationChanged(); + return true; + } else { + return false; + } } /** diff --git a/core/java/android/net/Network.java b/core/java/android/net/Network.java index e686be7..58f0fc0 100644 --- a/core/java/android/net/Network.java +++ b/core/java/android/net/Network.java @@ -37,6 +37,8 @@ import javax.net.SocketFactory; import com.android.okhttp.ConnectionPool; import com.android.okhttp.HostResolver; +import com.android.okhttp.HttpHandler; +import com.android.okhttp.HttpsHandler; import com.android.okhttp.OkHttpClient; /** @@ -58,7 +60,10 @@ public class Network implements Parcelable { // Objects used to perform per-network operations such as getSocketFactory // and openConnection, and a lock to protect access to them. private volatile NetworkBoundSocketFactory mNetworkBoundSocketFactory = null; - private volatile OkHttpClient mOkHttpClient = null; + // mLock should be used to control write access to mConnectionPool and mHostResolver. + // maybeInitHttpClient() must be called prior to reading either variable. + private volatile ConnectionPool mConnectionPool = null; + private volatile HostResolver mHostResolver = null; private Object mLock = new Object(); // Default connection pool values. These are evaluated at startup, just @@ -195,37 +200,34 @@ public class Network implements Parcelable { return mNetworkBoundSocketFactory; } - // TODO: This creates an OkHttpClient with its own connection pool for + // TODO: This creates a connection pool and host resolver for // every Network object, instead of one for every NetId. This is // suboptimal, because an app could potentially have more than one // Network object for the same NetId, causing increased memory footprint // and performance penalties due to lack of connection reuse (connection // setup time, congestion window growth time, etc.). // - // Instead, investigate only having one OkHttpClient for every NetId, - // perhaps by using a static HashMap of NetIds to OkHttpClient objects. The - // tricky part is deciding when to remove an OkHttpClient; a WeakHashMap - // shouldn't be used because whether a Network is referenced doesn't - // correlate with whether a new Network will be instantiated in the near - // future with the same NetID. A good solution would involve purging empty - // (or when all connections are timed out) ConnectionPools. + // Instead, investigate only having one connection pool and host resolver + // for every NetId, perhaps by using a static HashMap of NetIds to + // connection pools and host resolvers. The tricky part is deciding when + // to remove a map entry; a WeakHashMap shouldn't be used because whether + // a Network is referenced doesn't correlate with whether a new Network + // will be instantiated in the near future with the same NetID. A good + // solution would involve purging empty (or when all connections are timed + // out) ConnectionPools. private void maybeInitHttpClient() { - if (mOkHttpClient == null) { - synchronized (mLock) { - if (mOkHttpClient == null) { - HostResolver hostResolver = new HostResolver() { - @Override - public InetAddress[] getAllByName(String host) throws UnknownHostException { - return Network.this.getAllByName(host); - } - }; - ConnectionPool pool = new ConnectionPool(httpMaxConnections, - httpKeepAliveDurationMs); - mOkHttpClient = new OkHttpClient() - .setSocketFactory(getSocketFactory()) - .setHostResolver(hostResolver) - .setConnectionPool(pool); - } + synchronized (mLock) { + if (mHostResolver == null) { + mHostResolver = new HostResolver() { + @Override + public InetAddress[] getAllByName(String host) throws UnknownHostException { + return Network.this.getAllByName(host); + } + }; + } + if (mConnectionPool == null) { + mConnectionPool = new ConnectionPool(httpMaxConnections, + httpKeepAliveDurationMs); } } } @@ -242,13 +244,23 @@ public class Network implements Parcelable { public URLConnection openConnection(URL url) throws IOException { maybeInitHttpClient(); String protocol = url.getProtocol(); - URLStreamHandler handler = mOkHttpClient.createURLStreamHandler(protocol); - if (handler == null) { + OkHttpClient client; + // TODO: HttpHandler creates OkHttpClients that share the default ResponseCache. + // Could this cause unexpected behavior? + // TODO: Should the network's proxy be specified? + if (protocol.equals("http")) { + client = HttpHandler.createHttpOkHttpClient(null /* proxy */); + } else if (protocol.equals("https")) { + client = HttpsHandler.createHttpsOkHttpClient(null /* proxy */); + } else { // OkHttpClient only supports HTTP and HTTPS and returns a null URLStreamHandler if // passed another protocol. throw new MalformedURLException("Invalid URL or unrecognized protocol " + protocol); } - return new URL(url, "", handler).openConnection(); + return client.setSocketFactory(getSocketFactory()) + .setHostResolver(mHostResolver) + .setConnectionPool(mConnectionPool) + .open(url); } /** diff --git a/core/java/android/os/IUserManager.aidl b/core/java/android/os/IUserManager.aidl index 3286627..b5295fb 100644 --- a/core/java/android/os/IUserManager.aidl +++ b/core/java/android/os/IUserManager.aidl @@ -41,6 +41,7 @@ interface IUserManager { int getUserSerialNumber(int userHandle); int getUserHandle(int userSerialNumber); Bundle getUserRestrictions(int userHandle); + boolean hasUserRestriction(in String restrictionKey, int userHandle); void setUserRestrictions(in Bundle restrictions, int userHandle); void setApplicationRestrictions(in String packageName, in Bundle restrictions, int userHandle); diff --git a/core/java/android/os/UserManager.java b/core/java/android/os/UserManager.java index 33fda4a..c76ff11 100644 --- a/core/java/android/os/UserManager.java +++ b/core/java/android/os/UserManager.java @@ -630,7 +630,13 @@ public class UserManager { * @param userHandle the UserHandle of the user for whom to retrieve the restrictions. */ public boolean hasUserRestriction(String restrictionKey, UserHandle userHandle) { - return getUserRestrictions(userHandle).getBoolean(restrictionKey, false); + try { + return mService.hasUserRestriction(restrictionKey, + userHandle.getIdentifier()); + } catch (RemoteException re) { + Log.w(TAG, "Could not check user restrictions", re); + return false; + } } /** diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java index 01fda47..440b1ec 100644 --- a/core/java/android/provider/Settings.java +++ b/core/java/android/provider/Settings.java @@ -3150,6 +3150,11 @@ public final class Settings { /** @hide */ public static boolean putStringForUser(ContentResolver resolver, String name, String value, int userHandle) { + if (LOCATION_MODE.equals(name)) { + // HACK ALERT: temporary hack to work around b/10491283. + // TODO: once b/10491283 fixed, remove this hack + return setLocationModeForUser(resolver, Integer.parseInt(value), userHandle); + } if (MOVED_TO_GLOBAL.contains(name)) { Log.w(TAG, "Setting " + name + " has moved from android.provider.Settings.System" + " to android.provider.Settings.Global"); @@ -3265,11 +3270,6 @@ public final class Settings { /** @hide */ public static boolean putIntForUser(ContentResolver cr, String name, int value, int userHandle) { - if (LOCATION_MODE.equals(name)) { - // HACK ALERT: temporary hack to work around b/10491283. - // TODO: once b/10491283 fixed, remove this hack - return setLocationModeForUser(cr, value, userHandle); - } return putStringForUser(cr, name, Integer.toString(value), userHandle); } diff --git a/core/java/android/speech/tts/TextToSpeech.java b/core/java/android/speech/tts/TextToSpeech.java index a4b6e92..9be220e 100644 --- a/core/java/android/speech/tts/TextToSpeech.java +++ b/core/java/android/speech/tts/TextToSpeech.java @@ -46,7 +46,6 @@ import java.util.Locale; import java.util.Map; import java.util.MissingResourceException; import java.util.Set; -import java.util.TreeSet; /** * @@ -1567,10 +1566,10 @@ public class TextToSpeech { @Override public Set<Locale> run(ITextToSpeechService service) throws RemoteException { List<Voice> voices = service.getVoices(); - if (voices != null) { - return new TreeSet<Locale>(); + if (voices == null) { + return new HashSet<Locale>(); } - TreeSet<Locale> locales = new TreeSet<Locale>(); + HashSet<Locale> locales = new HashSet<Locale>(); for (Voice voice : voices) { locales.add(voice.getLocale()); } @@ -1593,7 +1592,7 @@ public class TextToSpeech { @Override public Set<Voice> run(ITextToSpeechService service) throws RemoteException { List<Voice> voices = service.getVoices(); - return (voices != null) ? new TreeSet<Voice>(voices) : new TreeSet<Voice>(); + return (voices != null) ? new HashSet<Voice>(voices) : new HashSet<Voice>(); } }, null, "getVoices"); } diff --git a/core/java/android/transition/Visibility.java b/core/java/android/transition/Visibility.java index d648ca6..d87d0f9 100644 --- a/core/java/android/transition/Visibility.java +++ b/core/java/android/transition/Visibility.java @@ -277,6 +277,18 @@ public abstract class Visibility extends Transition { if ((mMode & MODE_IN) != MODE_IN || endValues == null) { return null; } + if (startValues == null) { + VisibilityInfo parentVisibilityInfo = null; + View endParent = (View) endValues.view.getParent(); + TransitionValues startParentValues = getMatchedTransitionValues(endParent, + false); + TransitionValues endParentValues = getTransitionValues(endParent, false); + parentVisibilityInfo = + getVisibilityChangeInfo(startParentValues, endParentValues); + if (parentVisibilityInfo.visibilityChange) { + return null; + } + } return onAppear(sceneRoot, endValues.view, startValues, endValues); } diff --git a/core/java/android/util/SizeF.java b/core/java/android/util/SizeF.java index ac4f187..2edc4a7 100644 --- a/core/java/android/util/SizeF.java +++ b/core/java/android/util/SizeF.java @@ -16,6 +16,7 @@ package android.util; +import static com.android.internal.util.Preconditions.checkNotNull; import static com.android.internal.util.Preconditions.checkArgumentFinite; /** @@ -95,6 +96,61 @@ public final class SizeF { return mWidth + "x" + mHeight; } + private static NumberFormatException invalidSizeF(String s) { + throw new NumberFormatException("Invalid SizeF: \"" + s + "\""); + } + + /** + * Parses the specified string as a size value. + * <p> + * The ASCII characters {@code \}{@code u002a} ('*') and + * {@code \}{@code u0078} ('x') are recognized as separators between + * the width and height.</p> + * <p> + * For any {@code SizeF s}: {@code SizeF.parseSizeF(s.toString()).equals(s)}. + * However, the method also handles sizes expressed in the + * following forms:</p> + * <p> + * "<i>width</i>{@code x}<i>height</i>" or + * "<i>width</i>{@code *}<i>height</i>" {@code => new SizeF(width, height)}, + * where <i>width</i> and <i>height</i> are string floats potentially + * containing a sign, such as "-10.3", "+7" or "5.2", but not containing + * an {@code 'x'} (such as a float in hexadecimal string format).</p> + * + * <pre>{@code + * SizeF.parseSizeF("3.2*+6").equals(new SizeF(3.2f, 6.0f)) == true + * SizeF.parseSizeF("-3x-6").equals(new SizeF(-3.0f, -6.0f)) == true + * SizeF.parseSizeF("4 by 3") => throws NumberFormatException + * }</pre> + * + * @param string the string representation of a size value. + * @return the size value represented by {@code string}. + * + * @throws NumberFormatException if {@code string} cannot be parsed + * as a size value. + * @throws NullPointerException if {@code string} was {@code null} + */ + public static SizeF parseSizeF(String string) + throws NumberFormatException { + checkNotNull(string, "string must not be null"); + + int sep_ix = string.indexOf('*'); + if (sep_ix < 0) { + sep_ix = string.indexOf('x'); + } + if (sep_ix < 0) { + throw invalidSizeF(string); + } + try { + return new SizeF(Float.parseFloat(string.substring(0, sep_ix)), + Float.parseFloat(string.substring(sep_ix + 1))); + } catch (NumberFormatException e) { + throw invalidSizeF(string); + } catch (IllegalArgumentException e) { + throw invalidSizeF(string); + } + } + /** * {@inheritDoc} */ diff --git a/core/java/android/view/ViewDebug.java b/core/java/android/view/ViewDebug.java index a94f973..12a49d5 100644 --- a/core/java/android/view/ViewDebug.java +++ b/core/java/android/view/ViewDebug.java @@ -875,6 +875,11 @@ public class ViewDebug { data[i + 1] = theme.resolveAttribute(attributeId, outValue, true) ? outValue.coerceToString().toString() : nullString; i += 2; + + // attempt to replace reference data with its name + if (outValue.type == TypedValue.TYPE_REFERENCE) { + data[i - 1] = resources.getResourceName(outValue.resourceId); + } } catch (Resources.NotFoundException e) { // ignore resources we can't resolve } diff --git a/core/java/android/webkit/WebChromeClient.java b/core/java/android/webkit/WebChromeClient.java index 547acfa..46a7fd0 100644 --- a/core/java/android/webkit/WebChromeClient.java +++ b/core/java/android/webkit/WebChromeClient.java @@ -416,38 +416,6 @@ public class WebChromeClient { } /** - * UploadHelper simplifies file upload operations by providing helper methods that - * would handle most common file picker/media capture requests. The application - * can use the helper to build an intent to start a file picker, and then parse - * the result returned by the activity. - * - * How to use: - * 1. Create a helper using {@link FileChooserParams#getUploadHelper} - * 2. Build an intent using {@link UploadHelper#buildIntent} - * 3. Fire the intent using {@link android.app.Activity#startActivityForResult}. - * 4. Check for ActivityNotFoundException and take a user friendly action if thrown. - * 5. Listen the result using {@link android.app.Activity#onActivityResult} - * 6. Parse the result using {@link UploadHelper#parseResult} - * 7. Send the result using filePathCallback of {@link WebChromeClient#onShowFileChooser} - */ - public static abstract class UploadHelper { - /** - * Returns an intent that would start a file picker for file selection/media capture. - */ - public abstract Intent buildIntent(); - - /** - * Parses the result returned by the file picker activity. - * - * @param resultCode the integer result code returned by the file picker activity. - * @param data the intent returned by the file picker activity. - * @return the Uris of selected file(s) or null if the resultCode indicates - * activity canceled or any other error. - */ - public abstract Uri[] parseResult(int resultCode, Intent data); - } - - /** * Parameters used in the {@link #onShowFileChooser} method. */ public static abstract class FileChooserParams { @@ -464,11 +432,17 @@ public class WebChromeClient { public static final int MODE_SAVE = 3; /** - * Returns a helper to simplify choosing and uploading files. The helper builds a default - * intent that the application can send using startActivityForResult and processes the - * results. + * Parse the result returned by the file picker activity. This method should be used with + * {@link #createIntent}. Refer to {@link #createIntent} for how to use it. + * + * @param resultCode the integer result code returned by the file picker activity. + * @param data the intent returned by the file picker activity. + * @return the Uris of selected file(s) or null if the resultCode indicates + * activity canceled or any other error. */ - public abstract UploadHelper getUploadHelper(); + public static Uri[] parseResult(int resultCode, Intent data) { + return WebViewFactory.getProvider().getStatics().parseFileChooserResult(resultCode, data); + } /** * Returns file chooser mode. @@ -500,7 +474,28 @@ public class WebChromeClient { * The file name of a default selection if specified, or null. */ public abstract String getFilenameHint(); - }; + + /** + * Creates an intent that would start a file picker for file selection. + * The Intent supports choosing files from simple file sources available + * on the device. Some advanced sources (for example, live media capture) + * may not be supported and applications wishing to support these sources + * or more advanced file operations should build their own Intent. + * + * <pre> + * How to use: + * 1. Build an intent using {@link #createIntent} + * 2. Fire the intent using {@link android.app.Activity#startActivityForResult}. + * 3. Check for ActivityNotFoundException and take a user friendly action if thrown. + * 4. Listen the result using {@link android.app.Activity#onActivityResult} + * 5. Parse the result using {@link #parseResult} only if media capture was not requested. + * 6. Send the result using filePathCallback of {@link WebChromeClient#onShowFileChooser} + * </pre> + * + * @return an Intent that supports basic file chooser sources. + */ + public abstract Intent createIntent(); + } /** * Tell the client to open a file chooser. diff --git a/core/java/android/webkit/WebViewFactoryProvider.java b/core/java/android/webkit/WebViewFactoryProvider.java index 20bb932..d37d217 100644 --- a/core/java/android/webkit/WebViewFactoryProvider.java +++ b/core/java/android/webkit/WebViewFactoryProvider.java @@ -17,6 +17,8 @@ package android.webkit; import android.content.Context; +import android.content.Intent; +import android.net.Uri; /** * This is the main entry-point into the WebView back end implementations, which the WebView @@ -64,6 +66,12 @@ public interface WebViewFactoryProvider { * {@link android.webkit.WebView#setSlowWholeDocumentDrawEnabled(boolean) } */ void enableSlowWholeDocumentDraw(); + + /** + * Implement the API method + * {@link android.webkit.WebChromeClient.FileChooserParams#parseResult(int, Intent)} + */ + Uri[] parseFileChooserResult(int resultCode, Intent intent); } Statics getStatics(); diff --git a/core/java/android/widget/Editor.java b/core/java/android/widget/Editor.java index 3f168e8..128a06c 100644 --- a/core/java/android/widget/Editor.java +++ b/core/java/android/widget/Editor.java @@ -1860,9 +1860,8 @@ public class Editor { } final int originalLength = mTextView.getText().length(); - long minMax = mTextView.prepareSpacesAroundPaste(offset, offset, content); - int min = TextUtils.unpackRangeStartFromLong(minMax); - int max = TextUtils.unpackRangeEndFromLong(minMax); + int min = offset; + int max = offset; Selection.setSelection((Spannable) mTextView.getText(), max); mTextView.replaceText_internal(min, max, content); diff --git a/core/java/android/widget/SearchView.java b/core/java/android/widget/SearchView.java index 0289ccc..dfdf606 100644 --- a/core/java/android/widget/SearchView.java +++ b/core/java/android/widget/SearchView.java @@ -262,7 +262,7 @@ public class SearchView extends LinearLayout implements CollapsibleActionView { attrs, R.styleable.SearchView, defStyleAttr, defStyleRes); final LayoutInflater inflater = (LayoutInflater) context.getSystemService( Context.LAYOUT_INFLATER_SERVICE); - final int layoutResId = a.getResourceId(R.styleable.SearchView_layout, 0); + final int layoutResId = a.getResourceId(R.styleable.SearchView_layout, R.layout.search_view); inflater.inflate(layoutResId, this, true); mQueryTextView = (SearchAutoComplete) findViewById(R.id.search_src_text); @@ -288,7 +288,8 @@ public class SearchView extends LinearLayout implements CollapsibleActionView { mSearchHintIcon.setImageDrawable(a.getDrawable(R.styleable.SearchView_searchIcon)); // Extract dropdown layout resource IDs for later use. - mSuggestionRowLayout = a.getResourceId(R.styleable.SearchView_suggestionRowLayout, 0); + mSuggestionRowLayout = a.getResourceId(R.styleable.SearchView_suggestionRowLayout, + R.layout.search_dropdown_item_icons_2line); mSuggestionCommitIconResId = a.getResourceId(R.styleable.SearchView_commitIcon, 0); mSearchButton.setOnClickListener(mOnClickListener); diff --git a/core/java/android/widget/SimpleMonthView.java b/core/java/android/widget/SimpleMonthView.java index 27763eb..a76241e 100644 --- a/core/java/android/widget/SimpleMonthView.java +++ b/core/java/android/widget/SimpleMonthView.java @@ -31,6 +31,7 @@ import android.text.format.DateFormat; import android.text.format.DateUtils; import android.text.format.Time; import android.util.AttributeSet; +import android.util.MathUtils; import android.view.MotionEvent; import android.view.View; import android.view.accessibility.AccessibilityEvent; @@ -300,7 +301,11 @@ class SimpleMonthView extends View { } private static boolean isValidDayOfWeek(int day) { - return (day >= Time.SUNDAY && day <= Time.SATURDAY); + return day >= Calendar.SUNDAY && day <= Calendar.SATURDAY; + } + + private static boolean isValidMonth(int month) { + return month >= Calendar.JANUARY && month <= Calendar.DECEMBER; } /** @@ -312,8 +317,8 @@ class SimpleMonthView extends View { * @param selectedDay the selected day of the month, or -1 for no selection. * @param month the month. * @param year the year. - * @param weekStart which day the week should start on. {@link Time#SUNDAY} through - * {@link Time#SATURDAY}. + * @param weekStart which day the week should start on. {@link Calendar#SUNDAY} through + * {@link Calendar#SATURDAY}. * @param enabledDayStart the first enabled day. * @param enabledDayEnd the last enabled day. */ @@ -325,7 +330,7 @@ class SimpleMonthView extends View { mSelectedDay = selectedDay; - if (month >= Calendar.JANUARY && month <= Calendar.DECEMBER) { + if (isValidMonth(month)) { mMonth = month; } mYear = year; diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java index 80ea6ea..a81ff97 100644 --- a/core/java/android/widget/TextView.java +++ b/core/java/android/widget/TextView.java @@ -17,6 +17,7 @@ package android.widget; import android.R; +import android.annotation.NonNull; import android.annotation.Nullable; import android.content.ClipData; import android.content.ClipboardManager; @@ -1976,23 +1977,34 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener } /** - * Sets the Drawables (if any) to appear to the left of, above, - * to the right of, and below the text. Use null if you do not - * want a Drawable there. The Drawables must already have had + * Sets the Drawables (if any) to appear to the left of, above, to the + * right of, and below the text. Use {@code null} if you do not want a + * Drawable there. The Drawables must already have had * {@link Drawable#setBounds} called. + * <p> + * Calling this method will overwrite any Drawables previously set using + * {@link #setCompoundDrawablesRelative} or related methods. * * @attr ref android.R.styleable#TextView_drawableLeft * @attr ref android.R.styleable#TextView_drawableTop * @attr ref android.R.styleable#TextView_drawableRight * @attr ref android.R.styleable#TextView_drawableBottom */ - public void setCompoundDrawables(Drawable left, Drawable top, - Drawable right, Drawable bottom) { + public void setCompoundDrawables(@Nullable Drawable left, @Nullable Drawable top, + @Nullable Drawable right, @Nullable Drawable bottom) { Drawables dr = mDrawables; - final boolean drawables = left != null || top != null - || right != null || bottom != null; + // We're switching to absolute, discard relative. + if (dr != null) { + if (dr.mDrawableStart != null) dr.mDrawableStart.setCallback(null); + dr.mDrawableStart = null; + if (dr.mDrawableEnd != null) dr.mDrawableEnd.setCallback(null); + dr.mDrawableEnd = null; + dr.mDrawableSizeStart = dr.mDrawableHeightStart = 0; + dr.mDrawableSizeEnd = dr.mDrawableHeightEnd = 0; + } + final boolean drawables = left != null || top != null || right != null || bottom != null; if (!drawables) { // Clearing drawables... can we free the data structure? if (dr != null) { @@ -2101,10 +2113,12 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener } /** - * Sets the Drawables (if any) to appear to the left of, above, - * to the right of, and below the text. Use 0 if you do not - * want a Drawable there. The Drawables' bounds will be set to - * their intrinsic bounds. + * Sets the Drawables (if any) to appear to the left of, above, to the + * right of, and below the text. Use 0 if you do not want a Drawable there. + * The Drawables' bounds will be set to their intrinsic bounds. + * <p> + * Calling this method will overwrite any Drawables previously set using + * {@link #setCompoundDrawablesRelative} or related methods. * * @param left Resource identifier of the left Drawable. * @param top Resource identifier of the top Drawable. @@ -2126,18 +2140,21 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener } /** - * Sets the Drawables (if any) to appear to the left of, above, - * to the right of, and below the text. Use null if you do not - * want a Drawable there. The Drawables' bounds will be set to - * their intrinsic bounds. + * Sets the Drawables (if any) to appear to the left of, above, to the + * right of, and below the text. Use {@code null} if you do not want a + * Drawable there. The Drawables' bounds will be set to their intrinsic + * bounds. + * <p> + * Calling this method will overwrite any Drawables previously set using + * {@link #setCompoundDrawablesRelative} or related methods. * * @attr ref android.R.styleable#TextView_drawableLeft * @attr ref android.R.styleable#TextView_drawableTop * @attr ref android.R.styleable#TextView_drawableRight * @attr ref android.R.styleable#TextView_drawableBottom */ - public void setCompoundDrawablesWithIntrinsicBounds(Drawable left, Drawable top, - Drawable right, Drawable bottom) { + public void setCompoundDrawablesWithIntrinsicBounds(@Nullable Drawable left, + @Nullable Drawable top, @Nullable Drawable right, @Nullable Drawable bottom) { if (left != null) { left.setBounds(0, 0, left.getIntrinsicWidth(), left.getIntrinsicHeight()); @@ -2155,20 +2172,33 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener } /** - * Sets the Drawables (if any) to appear to the start of, above, - * to the end of, and below the text. Use null if you do not - * want a Drawable there. The Drawables must already have had - * {@link Drawable#setBounds} called. + * Sets the Drawables (if any) to appear to the start of, above, to the end + * of, and below the text. Use {@code null} if you do not want a Drawable + * there. The Drawables must already have had {@link Drawable#setBounds} + * called. + * <p> + * Calling this method will overwrite any Drawables previously set using + * {@link #setCompoundDrawables} or related methods. * * @attr ref android.R.styleable#TextView_drawableStart * @attr ref android.R.styleable#TextView_drawableTop * @attr ref android.R.styleable#TextView_drawableEnd * @attr ref android.R.styleable#TextView_drawableBottom */ - public void setCompoundDrawablesRelative(Drawable start, Drawable top, - Drawable end, Drawable bottom) { + public void setCompoundDrawablesRelative(@Nullable Drawable start, @Nullable Drawable top, + @Nullable Drawable end, @Nullable Drawable bottom) { Drawables dr = mDrawables; + // We're switching to relative, discard absolute. + if (dr != null) { + if (dr.mDrawableLeft != null) dr.mDrawableLeft.setCallback(null); + dr.mDrawableLeft = dr.mDrawableLeftInitial = null; + if (dr.mDrawableRight != null) dr.mDrawableRight.setCallback(null); + dr.mDrawableRight = dr.mDrawableRightInitial = null; + dr.mDrawableSizeLeft = dr.mDrawableHeightLeft = 0; + dr.mDrawableSizeRight = dr.mDrawableHeightRight = 0; + } + final boolean drawables = start != null || top != null || end != null || bottom != null; @@ -2274,10 +2304,12 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener } /** - * Sets the Drawables (if any) to appear to the start of, above, - * to the end of, and below the text. Use 0 if you do not - * want a Drawable there. The Drawables' bounds will be set to - * their intrinsic bounds. + * Sets the Drawables (if any) to appear to the start of, above, to the end + * of, and below the text. Use 0 if you do not want a Drawable there. The + * Drawables' bounds will be set to their intrinsic bounds. + * <p> + * Calling this method will overwrite any Drawables previously set using + * {@link #setCompoundDrawables} or related methods. * * @param start Resource identifier of the start Drawable. * @param top Resource identifier of the top Drawable. @@ -2301,18 +2333,20 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener } /** - * Sets the Drawables (if any) to appear to the start of, above, - * to the end of, and below the text. Use null if you do not - * want a Drawable there. The Drawables' bounds will be set to - * their intrinsic bounds. + * Sets the Drawables (if any) to appear to the start of, above, to the end + * of, and below the text. Use {@code null} if you do not want a Drawable + * there. The Drawables' bounds will be set to their intrinsic bounds. + * <p> + * Calling this method will overwrite any Drawables previously set using + * {@link #setCompoundDrawables} or related methods. * * @attr ref android.R.styleable#TextView_drawableStart * @attr ref android.R.styleable#TextView_drawableTop * @attr ref android.R.styleable#TextView_drawableEnd * @attr ref android.R.styleable#TextView_drawableBottom */ - public void setCompoundDrawablesRelativeWithIntrinsicBounds(Drawable start, Drawable top, - Drawable end, Drawable bottom) { + public void setCompoundDrawablesRelativeWithIntrinsicBounds(@Nullable Drawable start, + @Nullable Drawable top, @Nullable Drawable end, @Nullable Drawable bottom) { if (start != null) { start.setBounds(0, 0, start.getIntrinsicWidth(), start.getIntrinsicHeight()); @@ -2337,6 +2371,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener * @attr ref android.R.styleable#TextView_drawableRight * @attr ref android.R.styleable#TextView_drawableBottom */ + @NonNull public Drawable[] getCompoundDrawables() { final Drawables dr = mDrawables; if (dr != null) { @@ -2356,6 +2391,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener * @attr ref android.R.styleable#TextView_drawableEnd * @attr ref android.R.styleable#TextView_drawableBottom */ + @NonNull public Drawable[] getCompoundDrawablesRelative() { final Drawables dr = mDrawables; if (dr != null) { @@ -8754,57 +8790,6 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener } /** - * Prepare text so that there are not zero or two spaces at beginning and end of region defined - * by [min, max] when replacing this region by paste. - * Note that if there were two spaces (or more) at that position before, they are kept. We just - * make sure we do not add an extra one from the paste content. - */ - long prepareSpacesAroundPaste(int min, int max, CharSequence paste) { - if (paste.length() > 0) { - if (min > 0) { - final char charBefore = mTransformed.charAt(min - 1); - final char charAfter = paste.charAt(0); - - if (Character.isSpaceChar(charBefore) && Character.isSpaceChar(charAfter)) { - // Two spaces at beginning of paste: remove one - final int originalLength = mText.length(); - deleteText_internal(min - 1, min); - // Due to filters, there is no guarantee that exactly one character was - // removed: count instead. - final int delta = mText.length() - originalLength; - min += delta; - max += delta; - } else if (!Character.isSpaceChar(charBefore) && charBefore != '\n' && - !Character.isSpaceChar(charAfter) && charAfter != '\n') { - // No space at beginning of paste: add one - final int originalLength = mText.length(); - replaceText_internal(min, min, " "); - // Taking possible filters into account as above. - final int delta = mText.length() - originalLength; - min += delta; - max += delta; - } - } - - if (max < mText.length()) { - final char charBefore = paste.charAt(paste.length() - 1); - final char charAfter = mTransformed.charAt(max); - - if (Character.isSpaceChar(charBefore) && Character.isSpaceChar(charAfter)) { - // Two spaces at end of paste: remove one - deleteText_internal(max, max + 1); - } else if (!Character.isSpaceChar(charBefore) && charBefore != '\n' && - !Character.isSpaceChar(charAfter) && charAfter != '\n') { - // No space at end of paste: add one - replaceText_internal(max, max, " "); - } - } - } - - return TextUtils.packRangeInLong(min, max); - } - - /** * Paste clipboard content between min and max positions. */ private void paste(int min, int max) { @@ -8817,9 +8802,6 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener CharSequence paste = clip.getItemAt(i).coerceToStyledText(getContext()); if (paste != null) { if (!didFirst) { - long minMax = prepareSpacesAroundPaste(min, max, paste); - min = TextUtils.unpackRangeStartFromLong(minMax); - max = TextUtils.unpackRangeEndFromLong(minMax); Selection.setSelection((Spannable) mText, max); ((Editable) mText).replace(min, max, paste); didFirst = true; |
