diff options
Diffstat (limited to 'core')
116 files changed, 4158 insertions, 1680 deletions
diff --git a/core/java/android/animation/AnimatorInflater.java b/core/java/android/animation/AnimatorInflater.java index 427ecce..435d5ab 100644 --- a/core/java/android/animation/AnimatorInflater.java +++ b/core/java/android/animation/AnimatorInflater.java @@ -108,7 +108,7 @@ public class AnimatorInflater { float pathErrorScale) throws NotFoundException { final ConfigurationBoundResourceCache<Animator> animatorCache = resources .getAnimatorCache(); - Animator animator = animatorCache.get(id, theme); + Animator animator = animatorCache.getInstance(id, theme); if (animator != null) { if (DBG_ANIMATOR_INFLATER) { Log.d(TAG, "loaded animator from cache, " + resources.getResourceName(id)); @@ -157,7 +157,7 @@ public class AnimatorInflater { final ConfigurationBoundResourceCache<StateListAnimator> cache = resources .getStateListAnimatorCache(); final Theme theme = context.getTheme(); - StateListAnimator animator = cache.get(id, theme); + StateListAnimator animator = cache.getInstance(id, theme); if (animator != null) { return animator; } diff --git a/core/java/android/animation/LayoutTransition.java b/core/java/android/animation/LayoutTransition.java index 5790682..cdd72be 100644 --- a/core/java/android/animation/LayoutTransition.java +++ b/core/java/android/animation/LayoutTransition.java @@ -28,6 +28,7 @@ import java.util.Collection; import java.util.HashMap; import java.util.LinkedHashMap; import java.util.List; +import java.util.Map; /** * This class enables automatic animations on layout changes in ViewGroup objects. To enable @@ -757,7 +758,7 @@ public class LayoutTransition { // reset the inter-animation delay, in case we use it later staggerDelay = 0; - final ViewTreeObserver observer = parent.getViewTreeObserver(); // used for later cleanup + final ViewTreeObserver observer = parent.getViewTreeObserver(); if (!observer.isAlive()) { // If the observer's not in a good state, skip the transition return; @@ -790,21 +791,9 @@ public class LayoutTransition { // This is the cleanup step. When we get this rendering event, we know that all of // the appropriate animations have been set up and run. Now we can clear out the // layout listeners. - observer.addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() { - public boolean onPreDraw() { - parent.getViewTreeObserver().removeOnPreDrawListener(this); - int count = layoutChangeListenerMap.size(); - if (count > 0) { - Collection<View> views = layoutChangeListenerMap.keySet(); - for (View view : views) { - View.OnLayoutChangeListener listener = layoutChangeListenerMap.get(view); - view.removeOnLayoutChangeListener(listener); - } - } - layoutChangeListenerMap.clear(); - return true; - } - }); + CleanupCallback callback = new CleanupCallback(layoutChangeListenerMap, parent); + observer.addOnPreDrawListener(callback); + parent.addOnAttachStateChangeListener(callback); } /** @@ -1499,4 +1488,50 @@ public class LayoutTransition { View view, int transitionType); } + /** + * Utility class to clean up listeners after animations are setup. Cleanup happens + * when either the OnPreDrawListener method is called or when the parent is detached, + * whichever comes first. + */ + private static final class CleanupCallback implements ViewTreeObserver.OnPreDrawListener, + View.OnAttachStateChangeListener { + + final Map<View, View.OnLayoutChangeListener> layoutChangeListenerMap; + final ViewGroup parent; + + CleanupCallback(Map<View, View.OnLayoutChangeListener> listenerMap, ViewGroup parent) { + this.layoutChangeListenerMap = listenerMap; + this.parent = parent; + } + + private void cleanup() { + parent.getViewTreeObserver().removeOnPreDrawListener(this); + parent.removeOnAttachStateChangeListener(this); + int count = layoutChangeListenerMap.size(); + if (count > 0) { + Collection<View> views = layoutChangeListenerMap.keySet(); + for (View view : views) { + View.OnLayoutChangeListener listener = layoutChangeListenerMap.get(view); + view.removeOnLayoutChangeListener(listener); + } + layoutChangeListenerMap.clear(); + } + } + + @Override + public void onViewAttachedToWindow(View v) { + } + + @Override + public void onViewDetachedFromWindow(View v) { + cleanup(); + } + + @Override + public boolean onPreDraw() { + cleanup(); + return true; + } + }; + } diff --git a/core/java/android/app/ActionBar.java b/core/java/android/app/ActionBar.java index 94e3b66..4d34349 100644 --- a/core/java/android/app/ActionBar.java +++ b/core/java/android/app/ActionBar.java @@ -33,6 +33,7 @@ import android.view.KeyEvent; import android.view.View; import android.view.ViewDebug; import android.view.ViewGroup; +import android.view.ViewHierarchyEncoder; import android.view.Window; import android.widget.SpinnerAdapter; import java.lang.annotation.Retention; @@ -1373,5 +1374,13 @@ public abstract class ActionBar { * version of the SDK an app can end up statically linking to the new MarginLayoutParams * overload, causing a crash when running on older platform versions with no other changes. */ + + /** @hide */ + @Override + protected void encodeProperties(@NonNull ViewHierarchyEncoder encoder) { + super.encodeProperties(encoder); + + encoder.addProperty("gravity", gravity); + } } } diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java index 7260d10..9968dbb 100644 --- a/core/java/android/app/Activity.java +++ b/core/java/android/app/Activity.java @@ -89,7 +89,7 @@ import android.view.Menu; import android.view.MenuInflater; import android.view.MenuItem; import android.view.MotionEvent; -import android.view.PhoneWindow; +import com.android.internal.policy.PhoneWindow; import android.view.SearchEvent; import android.view.View; import android.view.View.OnCreateContextMenuListener; diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java index da6d8c5..f506d59 100644 --- a/core/java/android/app/ActivityThread.java +++ b/core/java/android/app/ActivityThread.java @@ -38,6 +38,7 @@ import android.content.res.AssetManager; import android.content.res.CompatibilityInfo; import android.content.res.Configuration; import android.content.res.Resources; +import android.content.res.Resources.Theme; import android.database.sqlite.SQLiteDatabase; import android.database.sqlite.SQLiteDebug; import android.database.sqlite.SQLiteDebug.DbStats; @@ -570,8 +571,6 @@ public final class ActivityThread { private native void dumpGraphicsInfo(FileDescriptor fd); private class ApplicationThread extends ApplicationThreadNative { - private static final String ONE_COUNT_COLUMN = "%21s %8d"; - private static final String TWO_COUNT_COLUMNS = "%21s %8d %21s %8d"; private static final String DB_INFO_FORMAT = " %8s %8s %14s %14s %s"; private int mLastProcessState = -1; @@ -971,18 +970,18 @@ public final class ActivityThread { @Override public void dumpMemInfo(FileDescriptor fd, Debug.MemoryInfo mem, boolean checkin, - boolean dumpFullInfo, boolean dumpDalvik, String[] args) { + boolean dumpFullInfo, boolean dumpDalvik, boolean dumpSummaryOnly, String[] args) { FileOutputStream fout = new FileOutputStream(fd); PrintWriter pw = new FastPrintWriter(fout); try { - dumpMemInfo(pw, mem, checkin, dumpFullInfo, dumpDalvik); + dumpMemInfo(pw, mem, checkin, dumpFullInfo, dumpDalvik, dumpSummaryOnly); } finally { pw.flush(); } } private void dumpMemInfo(PrintWriter pw, Debug.MemoryInfo memInfo, boolean checkin, - boolean dumpFullInfo, boolean dumpDalvik) { + boolean dumpFullInfo, boolean dumpDalvik, boolean dumpSummaryOnly) { long nativeMax = Debug.getNativeHeapSize() / 1024; long nativeAllocated = Debug.getNativeHeapAllocatedSize() / 1024; long nativeFree = Debug.getNativeHeapFreeSize() / 1024; @@ -1006,7 +1005,8 @@ public final class ActivityThread { long openSslSocketCount = Debug.countInstancesOfClass(OpenSSLSocketImpl.class); SQLiteDebug.PagerStats stats = SQLiteDebug.getDatabaseInfo(); - dumpMemInfoTable(pw, memInfo, checkin, dumpFullInfo, dumpDalvik, Process.myPid(), + dumpMemInfoTable(pw, memInfo, checkin, dumpFullInfo, dumpDalvik, dumpSummaryOnly, + Process.myPid(), (mBoundApplication != null) ? mBoundApplication.processName : "unknown", nativeMax, nativeAllocated, nativeFree, dalvikMax, dalvikAllocated, dalvikFree); @@ -1934,6 +1934,9 @@ public final class ActivityThread { = "%13s %8s %8s %8s %8s %8s %8s %8s %8s %8s %8s"; private static final String HEAP_COLUMN = "%13s %8s %8s %8s %8s %8s %8s %8s"; + private static final String ONE_COUNT_COLUMN = "%21s %8d"; + private static final String TWO_COUNT_COLUMNS = "%21s %8d %21s %8d"; + private static final String ONE_COUNT_COLUMN_HEADER = "%21s %8s"; // Formatting for checkin service - update version if row format changes private static final int ACTIVITY_THREAD_CHECKIN_VERSION = 3; @@ -1943,7 +1946,8 @@ public final class ActivityThread { } public static void dumpMemInfoTable(PrintWriter pw, Debug.MemoryInfo memInfo, boolean checkin, - boolean dumpFullInfo, boolean dumpDalvik, int pid, String processName, + boolean dumpFullInfo, boolean dumpDalvik, boolean dumpSummaryOnly, + int pid, String processName, long nativeMax, long nativeAllocated, long nativeFree, long dalvikMax, long dalvikAllocated, long dalvikFree) { @@ -2024,106 +2028,50 @@ public final class ActivityThread { return; } - // otherwise, show human-readable format - if (dumpFullInfo) { - printRow(pw, HEAP_FULL_COLUMN, "", "Pss", "Pss", "Shared", "Private", - "Shared", "Private", "Swapped", "Heap", "Heap", "Heap"); - printRow(pw, HEAP_FULL_COLUMN, "", "Total", "Clean", "Dirty", "Dirty", - "Clean", "Clean", "Dirty", "Size", "Alloc", "Free"); - printRow(pw, HEAP_FULL_COLUMN, "", "------", "------", "------", "------", - "------", "------", "------", "------", "------", "------"); - printRow(pw, HEAP_FULL_COLUMN, "Native Heap", memInfo.nativePss, - memInfo.nativeSwappablePss, memInfo.nativeSharedDirty, - memInfo.nativePrivateDirty, memInfo.nativeSharedClean, - memInfo.nativePrivateClean, memInfo.nativeSwappedOut, - nativeMax, nativeAllocated, nativeFree); - printRow(pw, HEAP_FULL_COLUMN, "Dalvik Heap", memInfo.dalvikPss, - memInfo.dalvikSwappablePss, memInfo.dalvikSharedDirty, - memInfo.dalvikPrivateDirty, memInfo.dalvikSharedClean, - memInfo.dalvikPrivateClean, memInfo.dalvikSwappedOut, - dalvikMax, dalvikAllocated, dalvikFree); - } else { - printRow(pw, HEAP_COLUMN, "", "Pss", "Private", - "Private", "Swapped", "Heap", "Heap", "Heap"); - printRow(pw, HEAP_COLUMN, "", "Total", "Dirty", - "Clean", "Dirty", "Size", "Alloc", "Free"); - printRow(pw, HEAP_COLUMN, "", "------", "------", "------", - "------", "------", "------", "------", "------"); - printRow(pw, HEAP_COLUMN, "Native Heap", memInfo.nativePss, - memInfo.nativePrivateDirty, - memInfo.nativePrivateClean, memInfo.nativeSwappedOut, - nativeMax, nativeAllocated, nativeFree); - printRow(pw, HEAP_COLUMN, "Dalvik Heap", memInfo.dalvikPss, - memInfo.dalvikPrivateDirty, - memInfo.dalvikPrivateClean, memInfo.dalvikSwappedOut, - dalvikMax, dalvikAllocated, dalvikFree); - } - - int otherPss = memInfo.otherPss; - int otherSwappablePss = memInfo.otherSwappablePss; - int otherSharedDirty = memInfo.otherSharedDirty; - int otherPrivateDirty = memInfo.otherPrivateDirty; - int otherSharedClean = memInfo.otherSharedClean; - int otherPrivateClean = memInfo.otherPrivateClean; - int otherSwappedOut = memInfo.otherSwappedOut; - - for (int i=0; i<Debug.MemoryInfo.NUM_OTHER_STATS; i++) { - final int myPss = memInfo.getOtherPss(i); - final int mySwappablePss = memInfo.getOtherSwappablePss(i); - final int mySharedDirty = memInfo.getOtherSharedDirty(i); - final int myPrivateDirty = memInfo.getOtherPrivateDirty(i); - final int mySharedClean = memInfo.getOtherSharedClean(i); - final int myPrivateClean = memInfo.getOtherPrivateClean(i); - final int mySwappedOut = memInfo.getOtherSwappedOut(i); - if (myPss != 0 || mySharedDirty != 0 || myPrivateDirty != 0 - || mySharedClean != 0 || myPrivateClean != 0 || mySwappedOut != 0) { - if (dumpFullInfo) { - printRow(pw, HEAP_FULL_COLUMN, Debug.MemoryInfo.getOtherLabel(i), - myPss, mySwappablePss, mySharedDirty, myPrivateDirty, - mySharedClean, myPrivateClean, mySwappedOut, "", "", ""); - } else { - printRow(pw, HEAP_COLUMN, Debug.MemoryInfo.getOtherLabel(i), - myPss, myPrivateDirty, - myPrivateClean, mySwappedOut, "", "", ""); - } - otherPss -= myPss; - otherSwappablePss -= mySwappablePss; - otherSharedDirty -= mySharedDirty; - otherPrivateDirty -= myPrivateDirty; - otherSharedClean -= mySharedClean; - otherPrivateClean -= myPrivateClean; - otherSwappedOut -= mySwappedOut; - } - } - - if (dumpFullInfo) { - printRow(pw, HEAP_FULL_COLUMN, "Unknown", otherPss, otherSwappablePss, - otherSharedDirty, otherPrivateDirty, otherSharedClean, otherPrivateClean, - otherSwappedOut, "", "", ""); - printRow(pw, HEAP_FULL_COLUMN, "TOTAL", memInfo.getTotalPss(), - memInfo.getTotalSwappablePss(), - memInfo.getTotalSharedDirty(), memInfo.getTotalPrivateDirty(), - memInfo.getTotalSharedClean(), memInfo.getTotalPrivateClean(), - memInfo.getTotalSwappedOut(), nativeMax+dalvikMax, - nativeAllocated+dalvikAllocated, nativeFree+dalvikFree); - } else { - printRow(pw, HEAP_COLUMN, "Unknown", otherPss, - otherPrivateDirty, otherPrivateClean, otherSwappedOut, - "", "", ""); - printRow(pw, HEAP_COLUMN, "TOTAL", memInfo.getTotalPss(), - memInfo.getTotalPrivateDirty(), - memInfo.getTotalPrivateClean(), - memInfo.getTotalSwappedOut(), - nativeMax+dalvikMax, - nativeAllocated+dalvikAllocated, nativeFree+dalvikFree); - } - - if (dumpDalvik) { - pw.println(" "); - pw.println(" Dalvik Details"); + if (!dumpSummaryOnly) { + if (dumpFullInfo) { + printRow(pw, HEAP_FULL_COLUMN, "", "Pss", "Pss", "Shared", "Private", + "Shared", "Private", "Swapped", "Heap", "Heap", "Heap"); + printRow(pw, HEAP_FULL_COLUMN, "", "Total", "Clean", "Dirty", "Dirty", + "Clean", "Clean", "Dirty", "Size", "Alloc", "Free"); + printRow(pw, HEAP_FULL_COLUMN, "", "------", "------", "------", "------", + "------", "------", "------", "------", "------", "------"); + printRow(pw, HEAP_FULL_COLUMN, "Native Heap", memInfo.nativePss, + memInfo.nativeSwappablePss, memInfo.nativeSharedDirty, + memInfo.nativePrivateDirty, memInfo.nativeSharedClean, + memInfo.nativePrivateClean, memInfo.nativeSwappedOut, + nativeMax, nativeAllocated, nativeFree); + printRow(pw, HEAP_FULL_COLUMN, "Dalvik Heap", memInfo.dalvikPss, + memInfo.dalvikSwappablePss, memInfo.dalvikSharedDirty, + memInfo.dalvikPrivateDirty, memInfo.dalvikSharedClean, + memInfo.dalvikPrivateClean, memInfo.dalvikSwappedOut, + dalvikMax, dalvikAllocated, dalvikFree); + } else { + printRow(pw, HEAP_COLUMN, "", "Pss", "Private", + "Private", "Swapped", "Heap", "Heap", "Heap"); + printRow(pw, HEAP_COLUMN, "", "Total", "Dirty", + "Clean", "Dirty", "Size", "Alloc", "Free"); + printRow(pw, HEAP_COLUMN, "", "------", "------", "------", + "------", "------", "------", "------", "------"); + printRow(pw, HEAP_COLUMN, "Native Heap", memInfo.nativePss, + memInfo.nativePrivateDirty, + memInfo.nativePrivateClean, memInfo.nativeSwappedOut, + nativeMax, nativeAllocated, nativeFree); + printRow(pw, HEAP_COLUMN, "Dalvik Heap", memInfo.dalvikPss, + memInfo.dalvikPrivateDirty, + memInfo.dalvikPrivateClean, memInfo.dalvikSwappedOut, + dalvikMax, dalvikAllocated, dalvikFree); + } + + int otherPss = memInfo.otherPss; + int otherSwappablePss = memInfo.otherSwappablePss; + int otherSharedDirty = memInfo.otherSharedDirty; + int otherPrivateDirty = memInfo.otherPrivateDirty; + int otherSharedClean = memInfo.otherSharedClean; + int otherPrivateClean = memInfo.otherPrivateClean; + int otherSwappedOut = memInfo.otherSwappedOut; - for (int i=Debug.MemoryInfo.NUM_OTHER_STATS; - i<Debug.MemoryInfo.NUM_OTHER_STATS + Debug.MemoryInfo.NUM_DVK_STATS; i++) { + for (int i=0; i<Debug.MemoryInfo.NUM_OTHER_STATS; i++) { final int myPss = memInfo.getOtherPss(i); final int mySwappablePss = memInfo.getOtherSwappablePss(i); final int mySharedDirty = memInfo.getOtherSharedDirty(i); @@ -2132,7 +2080,7 @@ public final class ActivityThread { final int myPrivateClean = memInfo.getOtherPrivateClean(i); final int mySwappedOut = memInfo.getOtherSwappedOut(i); if (myPss != 0 || mySharedDirty != 0 || myPrivateDirty != 0 - || mySharedClean != 0 || myPrivateClean != 0) { + || mySharedClean != 0 || myPrivateClean != 0 || mySwappedOut != 0) { if (dumpFullInfo) { printRow(pw, HEAP_FULL_COLUMN, Debug.MemoryInfo.getOtherLabel(i), myPss, mySwappablePss, mySharedDirty, myPrivateDirty, @@ -2142,9 +2090,89 @@ public final class ActivityThread { myPss, myPrivateDirty, myPrivateClean, mySwappedOut, "", "", ""); } + otherPss -= myPss; + otherSwappablePss -= mySwappablePss; + otherSharedDirty -= mySharedDirty; + otherPrivateDirty -= myPrivateDirty; + otherSharedClean -= mySharedClean; + otherPrivateClean -= myPrivateClean; + otherSwappedOut -= mySwappedOut; + } + } + + if (dumpFullInfo) { + printRow(pw, HEAP_FULL_COLUMN, "Unknown", otherPss, otherSwappablePss, + otherSharedDirty, otherPrivateDirty, otherSharedClean, otherPrivateClean, + otherSwappedOut, "", "", ""); + printRow(pw, HEAP_FULL_COLUMN, "TOTAL", memInfo.getTotalPss(), + memInfo.getTotalSwappablePss(), + memInfo.getTotalSharedDirty(), memInfo.getTotalPrivateDirty(), + memInfo.getTotalSharedClean(), memInfo.getTotalPrivateClean(), + memInfo.getTotalSwappedOut(), nativeMax+dalvikMax, + nativeAllocated+dalvikAllocated, nativeFree+dalvikFree); + } else { + printRow(pw, HEAP_COLUMN, "Unknown", otherPss, + otherPrivateDirty, otherPrivateClean, otherSwappedOut, + "", "", ""); + printRow(pw, HEAP_COLUMN, "TOTAL", memInfo.getTotalPss(), + memInfo.getTotalPrivateDirty(), + memInfo.getTotalPrivateClean(), + memInfo.getTotalSwappedOut(), + nativeMax+dalvikMax, + nativeAllocated+dalvikAllocated, nativeFree+dalvikFree); + } + + if (dumpDalvik) { + pw.println(" "); + pw.println(" Dalvik Details"); + + for (int i=Debug.MemoryInfo.NUM_OTHER_STATS; + i<Debug.MemoryInfo.NUM_OTHER_STATS + Debug.MemoryInfo.NUM_DVK_STATS; i++) { + final int myPss = memInfo.getOtherPss(i); + final int mySwappablePss = memInfo.getOtherSwappablePss(i); + final int mySharedDirty = memInfo.getOtherSharedDirty(i); + final int myPrivateDirty = memInfo.getOtherPrivateDirty(i); + final int mySharedClean = memInfo.getOtherSharedClean(i); + final int myPrivateClean = memInfo.getOtherPrivateClean(i); + final int mySwappedOut = memInfo.getOtherSwappedOut(i); + if (myPss != 0 || mySharedDirty != 0 || myPrivateDirty != 0 + || mySharedClean != 0 || myPrivateClean != 0) { + if (dumpFullInfo) { + printRow(pw, HEAP_FULL_COLUMN, Debug.MemoryInfo.getOtherLabel(i), + myPss, mySwappablePss, mySharedDirty, myPrivateDirty, + mySharedClean, myPrivateClean, mySwappedOut, "", "", ""); + } else { + printRow(pw, HEAP_COLUMN, Debug.MemoryInfo.getOtherLabel(i), + myPss, myPrivateDirty, + myPrivateClean, mySwappedOut, "", "", ""); + } + } } } } + + pw.println(" "); + pw.println(" App Summary"); + printRow(pw, ONE_COUNT_COLUMN_HEADER, "", "Pss(KB)"); + printRow(pw, ONE_COUNT_COLUMN_HEADER, "", "------"); + printRow(pw, ONE_COUNT_COLUMN, + "Java Heap:", memInfo.getSummaryJavaHeap()); + printRow(pw, ONE_COUNT_COLUMN, + "Native Heap:", memInfo.getSummaryNativeHeap()); + printRow(pw, ONE_COUNT_COLUMN, + "Code:", memInfo.getSummaryCode()); + printRow(pw, ONE_COUNT_COLUMN, + "Stack:", memInfo.getSummaryStack()); + printRow(pw, ONE_COUNT_COLUMN, + "Graphics:", memInfo.getSummaryGraphics()); + printRow(pw, ONE_COUNT_COLUMN, + "Private Other:", memInfo.getSummaryPrivateOther()); + printRow(pw, ONE_COUNT_COLUMN, + "System:", memInfo.getSummarySystem()); + pw.println(" "); + printRow(pw, TWO_COUNT_COLUMNS, + "TOTAL:", memInfo.getSummaryTotalPss(), + "TOTAL SWAP (KB):", memInfo.getSummaryTotalSwap()); } public void registerOnActivityPausedListener(Activity activity, @@ -4186,9 +4214,14 @@ public final class ActivityThread { if (!mConfiguration.isOtherSeqNewer(config) && compat == null) { return; } - configDiff = mConfiguration.diff(config); - mConfiguration.updateFrom(config); + + configDiff = mConfiguration.updateFrom(config); config = applyCompatConfiguration(mCurDefaultDisplayDpi); + + final Theme systemTheme = getSystemContext().getTheme(); + if ((systemTheme.getChangingConfigurations() & configDiff) != 0) { + systemTheme.rebase(); + } } ArrayList<ComponentCallbacks2> callbacks = collectComponentCallbacks(false, config); diff --git a/core/java/android/app/ActivityTransitionCoordinator.java b/core/java/android/app/ActivityTransitionCoordinator.java index 2939322..968c956 100644 --- a/core/java/android/app/ActivityTransitionCoordinator.java +++ b/core/java/android/app/ActivityTransitionCoordinator.java @@ -476,9 +476,8 @@ abstract class ActivityTransitionCoordinator extends ResultReceiver { tempRect.set(0, 0, width, height); view.getMatrix().mapRect(tempRect); - ViewGroup parent = (ViewGroup) view.getParent(); - left = leftInParent - tempRect.left + parent.getScrollX(); - top = topInParent - tempRect.top + parent.getScrollY(); + left = leftInParent - tempRect.left; + top = topInParent - tempRect.top; right = left + width; bottom = top + height; } @@ -506,7 +505,7 @@ abstract class ActivityTransitionCoordinator extends ResultReceiver { ViewGroup parent = (ViewGroup) view.getParent(); Matrix matrix = new Matrix(); parent.transformMatrixToLocal(matrix); - + matrix.postTranslate(parent.getScrollX(), parent.getScrollY()); mSharedElementParentMatrices.add(matrix); } } @@ -521,6 +520,7 @@ abstract class ActivityTransitionCoordinator extends ResultReceiver { // Find the location in the view's parent ViewGroup parent = (ViewGroup) viewParent; parent.transformMatrixToLocal(matrix); + matrix.postTranslate(parent.getScrollX(), parent.getScrollY()); } } else { // The indices of mSharedElementParentMatrices matches the diff --git a/core/java/android/app/Application.java b/core/java/android/app/Application.java index 75e4bab..1174387 100644 --- a/core/java/android/app/Application.java +++ b/core/java/android/app/Application.java @@ -18,6 +18,7 @@ package android.app; import java.util.ArrayList; +import android.annotation.CallSuper; import android.content.ComponentCallbacks; import android.content.ComponentCallbacks2; import android.content.Context; @@ -89,6 +90,7 @@ public class Application extends ContextWrapper implements ComponentCallbacks2 { * service, or receiver in a process. * If you override this method, be sure to call super.onCreate(). */ + @CallSuper public void onCreate() { } @@ -98,9 +100,11 @@ public class Application extends ContextWrapper implements ComponentCallbacks2 { * removed by simply killing them; no user code (including this callback) * is executed when doing so. */ + @CallSuper public void onTerminate() { } + @CallSuper public void onConfigurationChanged(Configuration newConfig) { Object[] callbacks = collectComponentCallbacks(); if (callbacks != null) { @@ -110,6 +114,7 @@ public class Application extends ContextWrapper implements ComponentCallbacks2 { } } + @CallSuper public void onLowMemory() { Object[] callbacks = collectComponentCallbacks(); if (callbacks != null) { @@ -119,6 +124,7 @@ public class Application extends ContextWrapper implements ComponentCallbacks2 { } } + @CallSuper public void onTrimMemory(int level) { Object[] callbacks = collectComponentCallbacks(); if (callbacks != null) { diff --git a/core/java/android/app/ApplicationThreadNative.java b/core/java/android/app/ApplicationThreadNative.java index b6989ab..1461380 100644 --- a/core/java/android/app/ApplicationThreadNative.java +++ b/core/java/android/app/ApplicationThreadNative.java @@ -529,10 +529,12 @@ public abstract class ApplicationThreadNative extends Binder boolean checkin = data.readInt() != 0; boolean dumpInfo = data.readInt() != 0; boolean dumpDalvik = data.readInt() != 0; + boolean dumpSummaryOnly = data.readInt() != 0; String[] args = data.readStringArray(); if (fd != null) { try { - dumpMemInfo(fd.getFileDescriptor(), mi, checkin, dumpInfo, dumpDalvik, args); + dumpMemInfo(fd.getFileDescriptor(), mi, checkin, dumpInfo, + dumpDalvik, dumpSummaryOnly, args); } finally { try { fd.close(); @@ -1248,7 +1250,7 @@ class ApplicationThreadProxy implements IApplicationThread { } public void dumpMemInfo(FileDescriptor fd, Debug.MemoryInfo mem, boolean checkin, - boolean dumpInfo, boolean dumpDalvik, String[] args) throws RemoteException { + boolean dumpInfo, boolean dumpDalvik, boolean dumpSummaryOnly, String[] args) throws RemoteException { Parcel data = Parcel.obtain(); Parcel reply = Parcel.obtain(); data.writeInterfaceToken(IApplicationThread.descriptor); @@ -1257,6 +1259,7 @@ class ApplicationThreadProxy implements IApplicationThread { data.writeInt(checkin ? 1 : 0); data.writeInt(dumpInfo ? 1 : 0); data.writeInt(dumpDalvik ? 1 : 0); + data.writeInt(dumpSummaryOnly ? 1 : 0); data.writeStringArray(args); mRemote.transact(DUMP_MEM_INFO_TRANSACTION, data, reply, 0); reply.readException(); diff --git a/core/java/android/app/AssistStructure.java b/core/java/android/app/AssistStructure.java index 3abbb5b..a06bc31 100644 --- a/core/java/android/app/AssistStructure.java +++ b/core/java/android/app/AssistStructure.java @@ -35,6 +35,7 @@ import android.util.Log; import android.view.View; import android.view.ViewAssistStructure; import android.view.ViewRootImpl; +import android.view.WindowManager; import android.view.WindowManagerGlobal; import java.util.ArrayList; @@ -140,7 +141,14 @@ final public class AssistStructure implements Parcelable { mTitle = root.getTitle(); mRoot = new ViewNode(); ViewNodeBuilder builder = new ViewNodeBuilder(assist, mRoot, false); - view.dispatchProvideAssistStructure(builder); + if ((root.getWindowFlags()&WindowManager.LayoutParams.FLAG_SECURE) != 0) { + // This is a secure window, so it doesn't want a screenshot, and that + // means we should also not copy out its view hierarchy. + view.onProvideStructure(builder); + builder.setAssistBlocked(true); + return; + } + view.dispatchProvideStructure(builder); } WindowNode(Parcel in, PooledStringReader preader) { @@ -652,7 +660,7 @@ final public class AssistStructure implements Parcelable { } @Override - public Bundle editExtras() { + public Bundle getExtras() { if (mNode.mExtras != null) { return mNode.mExtras; } @@ -661,8 +669,8 @@ final public class AssistStructure implements Parcelable { } @Override - public void clearExtras() { - mNode.mExtras = null; + public boolean hasExtras() { + return mNode.mExtras != null; } @Override diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java index 81a78f6..cb20cb8 100644 --- a/core/java/android/app/ContextImpl.java +++ b/core/java/android/app/ContextImpl.java @@ -237,8 +237,11 @@ class ContextImpl extends Context { } @Override - public void setTheme(int resid) { - mThemeResource = resid; + public void setTheme(int resId) { + if (mThemeResource != resId) { + mThemeResource = resId; + initializeTheme(); + } } @Override @@ -248,13 +251,22 @@ class ContextImpl extends Context { @Override public Resources.Theme getTheme() { + if (mTheme != null) { + return mTheme; + } + + mThemeResource = Resources.selectDefaultTheme(mThemeResource, + getOuterContext().getApplicationInfo().targetSdkVersion); + initializeTheme(); + + return mTheme; + } + + private void initializeTheme() { if (mTheme == null) { - mThemeResource = Resources.selectDefaultTheme(mThemeResource, - getOuterContext().getApplicationInfo().targetSdkVersion); mTheme = mResources.newTheme(); - mTheme.applyStyle(mThemeResource, true); } - return mTheme; + mTheme.applyStyle(mThemeResource, true); } @Override diff --git a/core/java/android/app/Dialog.java b/core/java/android/app/Dialog.java index d049104..f6e0e1e 100644 --- a/core/java/android/app/Dialog.java +++ b/core/java/android/app/Dialog.java @@ -48,7 +48,7 @@ import android.view.LayoutInflater; import android.view.Menu; import android.view.MenuItem; import android.view.MotionEvent; -import android.view.PhoneWindow; +import com.android.internal.policy.PhoneWindow; import android.view.SearchEvent; import android.view.View; import android.view.View.OnCreateContextMenuListener; diff --git a/core/java/android/app/IApplicationThread.java b/core/java/android/app/IApplicationThread.java index 3fb82f6..185578f 100644 --- a/core/java/android/app/IApplicationThread.java +++ b/core/java/android/app/IApplicationThread.java @@ -131,7 +131,7 @@ public interface IApplicationThread extends IInterface { void updatePackageCompatibilityInfo(String pkg, CompatibilityInfo info) throws RemoteException; void scheduleTrimMemory(int level) throws RemoteException; void dumpMemInfo(FileDescriptor fd, Debug.MemoryInfo mem, boolean checkin, boolean dumpInfo, - boolean dumpDalvik, String[] args) throws RemoteException; + boolean dumpDalvik, boolean dumpSummaryOnly, String[] args) throws RemoteException; void dumpGfxInfo(FileDescriptor fd, String[] args) throws RemoteException; void dumpDbInfo(FileDescriptor fd, String[] args) throws RemoteException; void unstableProviderDied(IBinder provider) throws RemoteException; diff --git a/core/java/android/app/INotificationManager.aidl b/core/java/android/app/INotificationManager.aidl index ac8d5d8..63ff005 100644 --- a/core/java/android/app/INotificationManager.aidl +++ b/core/java/android/app/INotificationManager.aidl @@ -75,7 +75,7 @@ interface INotificationManager void requestInterruptionFilterFromListener(in INotificationListener token, int interruptionFilter); int getInterruptionFilterFromListener(in INotificationListener token); void setOnNotificationPostedTrimFromListener(in INotificationListener token, int trim); - NotificationManager.Policy.Token getPolicyTokenFromListener(in INotificationListener listener); + void setInterruptionFilter(String pkg, int interruptionFilter); ComponentName getEffectsSuppressor(); boolean matchesCallFilter(in Bundle extras); @@ -87,10 +87,13 @@ interface INotificationManager oneway void setZenMode(int mode, in Uri conditionId, String reason); oneway void notifyConditions(String pkg, in IConditionProvider provider, in Condition[] conditions); oneway void requestZenModeConditions(in IConditionListener callback, int relevance); - oneway void requestNotificationPolicyToken(String pkg, in INotificationManagerCallback callback); - boolean isNotificationPolicyTokenValid(String pkg, in NotificationManager.Policy.Token token); - NotificationManager.Policy getNotificationPolicy(in NotificationManager.Policy.Token token); - void setNotificationPolicy(in NotificationManager.Policy.Token token, in NotificationManager.Policy policy); + oneway void requestNotificationPolicyAccess(String pkg, in INotificationManagerCallback callback); + boolean isNotificationPolicyAccessGranted(String pkg); + NotificationManager.Policy getNotificationPolicy(String pkg); + void setNotificationPolicy(String pkg, in NotificationManager.Policy policy); + String[] getPackagesRequestingNotificationPolicyAccess(); + boolean isNotificationPolicyAccessGrantedForPackage(String pkg); + void setNotificationPolicyAccessGranted(String pkg, boolean granted); byte[] getBackupPayload(int user); void applyRestore(in byte[] payload, int user); diff --git a/core/java/android/app/INotificationManagerCallback.aidl b/core/java/android/app/INotificationManagerCallback.aidl index b9414ca..9929745 100644 --- a/core/java/android/app/INotificationManagerCallback.aidl +++ b/core/java/android/app/INotificationManagerCallback.aidl @@ -20,5 +20,5 @@ import android.app.NotificationManager; /** @hide */ oneway interface INotificationManagerCallback { - void onPolicyToken(in NotificationManager.Policy.Token token); + void onPolicyRequestResult(boolean granted); } diff --git a/core/java/android/app/NotificationManager.aidl b/core/java/android/app/NotificationManager.aidl index 8380b8d..a5d5671 100644 --- a/core/java/android/app/NotificationManager.aidl +++ b/core/java/android/app/NotificationManager.aidl @@ -17,4 +17,3 @@ package android.app; parcelable NotificationManager.Policy; -parcelable NotificationManager.Policy.Token;
\ No newline at end of file diff --git a/core/java/android/app/NotificationManager.java b/core/java/android/app/NotificationManager.java index 0a59026..e4bbe27 100644 --- a/core/java/android/app/NotificationManager.java +++ b/core/java/android/app/NotificationManager.java @@ -20,7 +20,6 @@ import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.SdkConstant; import android.app.Notification.Builder; -import android.app.NotificationManager.Policy.Token; import android.content.ComponentName; import android.content.Context; import android.content.pm.ParceledListSlice; @@ -38,6 +37,7 @@ import android.provider.Settings.Global; import android.service.notification.IConditionListener; import android.service.notification.StatusBarNotification; import android.service.notification.ZenModeConfig; +import android.util.ArraySet; import android.util.Log; import java.util.Objects; @@ -107,6 +107,43 @@ public class NotificationManager public static final String ACTION_NOTIFICATION_POLICY_CHANGED = "android.app.action.NOTIFICATION_POLICY_CHANGED"; + /** + * Intent that is broadcast when the state of getCurrentInterruptionFilter() changes. + * This broadcast is only sent to registered receivers. + */ + @SdkConstant(SdkConstant.SdkConstantType.BROADCAST_INTENT_ACTION) + public static final String ACTION_INTERRUPTION_FILTER_CHANGED + = "android.app.action.INTERRUPTION_FILTER_CHANGED"; + + /** + * {@link #getCurrentInterruptionFilter() Interruption filter} constant - + * Normal interruption filter. + */ + public static final int INTERRUPTION_FILTER_ALL = 1; + + /** + * {@link #getCurrentInterruptionFilter() Interruption filter} constant - + * Priority interruption filter. + */ + public static final int INTERRUPTION_FILTER_PRIORITY = 2; + + /** + * {@link #getCurrentInterruptionFilter() Interruption filter} constant - + * No interruptions filter. + */ + public static final int INTERRUPTION_FILTER_NONE = 3; + + /** + * {@link #getCurrentInterruptionFilter() Interruption filter} constant - + * Alarms only interruption filter. + */ + public static final int INTERRUPTION_FILTER_ALARMS = 4; + + /** {@link #getCurrentInterruptionFilter() Interruption filter} constant - returned when + * the value is unavailable for any reason. + */ + public static final int INTERRUPTION_FILTER_UNKNOWN = 0; + private static INotificationManager sService; /** @hide */ @@ -357,29 +394,29 @@ public class NotificationManager } /** - * Requests a notification policy token for the calling package. + * Requests the ability to read/modify notification policy for the calling package. * - * @param callback required, used to receive the granted token or the deny signal. + * @param callback required, used to receive the granted or the denied signal. * @param handler The handler used when receiving the result. * If null, the current thread is used. */ - public void requestNotificationPolicyToken(@NonNull final Policy.Token.RequestCallback callback, + public void requestPolicyAccess(@NonNull final NotificationPolicyAccessRequestCallback callback, @Nullable Handler handler) { checkRequired("callback", callback); final Handler h = handler != null ? handler : new Handler(); INotificationManager service = getService(); try { - service.requestNotificationPolicyToken(mContext.getOpPackageName(), + service.requestNotificationPolicyAccess(mContext.getOpPackageName(), new INotificationManagerCallback.Stub() { @Override - public void onPolicyToken(final Token token) throws RemoteException { + public void onPolicyRequestResult(final boolean granted) throws RemoteException { h.post(new Runnable() { @Override public void run() { - if (token != null) { - callback.onTokenGranted(token); + if (granted) { + callback.onAccessGranted(); } else { - callback.onTokenDenied(); + callback.onAccessDenied(); } } }); @@ -389,16 +426,38 @@ public class NotificationManager } } + /** Callback for receiving the result of a policy access request. */ + public static abstract class NotificationPolicyAccessRequestCallback { + /** + * Received if the request was granted for this package. + */ + public abstract void onAccessGranted(); + + /** + * Received if the request was denied for this package. + */ + public abstract void onAccessDenied(); + } + /** - * Checks a given notification policy token. + * Checks the ability to read/modify notification policy for the calling package. * - * Returns true if the token is still valid for managing policy. + * Returns true if the calling package can read/modify notification policy. */ - public boolean isNotificationPolicyTokenValid(@NonNull Policy.Token token) { - if (token == null) return false; + public boolean isNotificationPolicyAccessGranted() { INotificationManager service = getService(); try { - return service.isNotificationPolicyTokenValid(mContext.getOpPackageName(), token); + return service.isNotificationPolicyAccessGranted(mContext.getOpPackageName()); + } catch (RemoteException e) { + } + return false; + } + + /** @hide */ + public boolean isNotificationPolicyAccessGrantedForPackage(String pkg) { + INotificationManager service = getService(); + try { + return service.isNotificationPolicyAccessGrantedForPackage(pkg); } catch (RemoteException e) { } return false; @@ -407,13 +466,13 @@ public class NotificationManager /** * Gets the current notification policy. * - * @param token A valid notification policy token is required to access the current policy. + * <p> + * Only available if policy access is granted. */ - public Policy getNotificationPolicy(@NonNull Policy.Token token) { - checkRequired("token", token); + public Policy getNotificationPolicy() { INotificationManager service = getService(); try { - return service.getNotificationPolicy(token); + return service.getNotificationPolicy(mContext.getOpPackageName()); } catch (RemoteException e) { } return null; @@ -422,19 +481,46 @@ public class NotificationManager /** * Sets the current notification policy. * - * @param token A valid notification policy token is required to modify the current policy. + * <p> + * Only available if policy access is granted. + * * @param policy The new desired policy. */ - public void setNotificationPolicy(@NonNull Policy.Token token, @NonNull Policy policy) { - checkRequired("token", token); + public void setNotificationPolicy(@NonNull Policy policy) { checkRequired("policy", policy); INotificationManager service = getService(); try { - service.setNotificationPolicy(token, policy); + service.setNotificationPolicy(mContext.getOpPackageName(), policy); + } catch (RemoteException e) { + } + } + + /** @hide */ + public void setNotificationPolicyAccessGranted(String pkg, boolean granted) { + INotificationManager service = getService(); + try { + service.setNotificationPolicyAccessGranted(pkg, granted); } catch (RemoteException e) { } } + /** @hide */ + public ArraySet<String> getPackagesRequestingNotificationPolicyAccess() { + INotificationManager service = getService(); + try { + final String[] pkgs = service.getPackagesRequestingNotificationPolicyAccess(); + if (pkgs != null && pkgs.length > 0) { + final ArraySet<String> rt = new ArraySet<>(pkgs.length); + for (int i = 0; i < pkgs.length; i++) { + rt.add(pkgs[i]); + } + return rt; + } + } catch (RemoteException e) { + } + return new ArraySet<String>(); + } + private Context mContext; private static void checkRequired(String name, Object value) { @@ -477,24 +563,30 @@ public class NotificationManager /** Notification categories to prioritize. Bitmask of PRIORITY_CATEGORY_* constants. */ public final int priorityCategories; - /** Notification senders to prioritize. One of: + /** Notification senders to prioritize for calls. One of: + * PRIORITY_SENDERS_ANY, PRIORITY_SENDERS_CONTACTS, PRIORITY_SENDERS_STARRED */ + public final int priorityCallSenders; + + /** Notification senders to prioritize for messages. One of: * PRIORITY_SENDERS_ANY, PRIORITY_SENDERS_CONTACTS, PRIORITY_SENDERS_STARRED */ - public final int prioritySenders; + public final int priorityMessageSenders; - public Policy(int priorityCategories, int prioritySenders) { + public Policy(int priorityCategories, int priorityCallSenders, int priorityMessageSenders) { this.priorityCategories = priorityCategories; - this.prioritySenders = prioritySenders; + this.priorityCallSenders = priorityCallSenders; + this.priorityMessageSenders = priorityMessageSenders; } /** @hide */ public Policy(Parcel source) { - this(source.readInt(), source.readInt()); + this(source.readInt(), source.readInt(), source.readInt()); } @Override public void writeToParcel(Parcel dest, int flags) { dest.writeInt(priorityCategories); - dest.writeInt(prioritySenders); + dest.writeInt(priorityCallSenders); + dest.writeInt(priorityMessageSenders); } @Override @@ -504,7 +596,7 @@ public class NotificationManager @Override public int hashCode() { - return Objects.hash(priorityCategories, prioritySenders); + return Objects.hash(priorityCategories, priorityCallSenders, priorityMessageSenders); } @Override @@ -513,14 +605,16 @@ public class NotificationManager if (o == this) return true; final Policy other = (Policy) o; return other.priorityCategories == priorityCategories - && other.prioritySenders == prioritySenders; + && other.priorityCallSenders == priorityCallSenders + && other.priorityMessageSenders == priorityMessageSenders; } @Override public String toString() { return "NotificationManager.Policy[" + "priorityCategories=" + priorityCategoriesToString(priorityCategories) - + ",prioritySenders=" + prioritySendersToString(prioritySenders) + + ",priorityCallSenders=" + prioritySendersToString(priorityCallSenders) + + ",priorityMessageSenders=" + prioritySendersToString(priorityMessageSenders) + "]"; } @@ -574,75 +668,6 @@ public class NotificationManager } }; - /** - * Represents a client-specific token required to manage notification policy. - */ - public static class Token implements Parcelable { - private final IBinder mBinder; - - /** @hide */ - public Token(IBinder binder) { - if (binder == null) throw new IllegalArgumentException("Binder required for token"); - mBinder = binder; - } - - @Override - public int describeContents() { - return 0; - } - - @Override - public int hashCode() { - return Objects.hash(mBinder); - } - - @Override - public boolean equals(Object o) { - if (!(o instanceof Token)) return false; - if (o == this) return true; - final Token other = (Token) o; - return Objects.equals(other.mBinder, mBinder); - } - - @Override - public String toString() { - return String.format("NotificationManager.Token[0x%08x]", - System.identityHashCode(mBinder)); - } - - @Override - public void writeToParcel(Parcel dest, int flags) { - dest.writeStrongBinder(mBinder); - } - - public static final Parcelable.Creator<Token> CREATOR - = new Parcelable.Creator<Token>() { - @Override - public Token createFromParcel(Parcel in) { - return new Token(in.readStrongBinder()); - } - - @Override - public Token[] newArray(int size) { - return new Token[size]; - } - }; - - /** Callback for receiving the result of a token request. */ - public static abstract class RequestCallback { - /** - * Received if the request was granted for this package. - * - * @param token can be used to manage notification policy. - */ - public abstract void onTokenGranted(Policy.Token token); - - /** - * Received if the request was denied for this package. - */ - public abstract void onTokenDenied(); - } - } } /** @@ -671,4 +696,69 @@ public class NotificationManager } return new StatusBarNotification[0]; } + + /** + * Gets the current notification interruption filter. + * + * <p> + * The interruption filter defines which notifications are allowed to interrupt the user + * (e.g. via sound & vibration) and is applied globally. + * @return One of the INTERRUPTION_FILTER_ constants, or INTERRUPTION_FILTER_UNKNOWN when + * unavailable. + * + * <p> + * Only available if policy access is granted. + */ + public final int getCurrentInterruptionFilter() { + final INotificationManager service = getService(); + try { + return zenModeToInterruptionFilter(service.getZenMode()); + } catch (RemoteException e) { + Log.e(TAG, "Unable to talk to notification manager. Woe!", e); + } + return INTERRUPTION_FILTER_UNKNOWN; + } + + /** + * Sets the current notification interruption filter. + * + * <p> + * The interruption filter defines which notifications are allowed to interrupt the user + * (e.g. via sound & vibration) and is applied globally. + * @return One of the INTERRUPTION_FILTER_ constants, or INTERRUPTION_FILTER_UNKNOWN when + * unavailable. + * + * <p> + * Only available if policy access is granted. + */ + public final void setInterruptionFilter(int interruptionFilter) { + final INotificationManager service = getService(); + try { + service.setInterruptionFilter(mContext.getOpPackageName(), interruptionFilter); + } catch (RemoteException e) { + Log.e(TAG, "Unable to talk to notification manager. Woe!", e); + } + } + + /** @hide */ + public static int zenModeToInterruptionFilter(int zen) { + switch (zen) { + case Global.ZEN_MODE_OFF: return INTERRUPTION_FILTER_ALL; + case Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS: return INTERRUPTION_FILTER_PRIORITY; + case Global.ZEN_MODE_ALARMS: return INTERRUPTION_FILTER_ALARMS; + case Global.ZEN_MODE_NO_INTERRUPTIONS: return INTERRUPTION_FILTER_NONE; + default: return INTERRUPTION_FILTER_UNKNOWN; + } + } + + /** @hide */ + public static int zenModeFromInterruptionFilter(int interruptionFilter, int defValue) { + switch (interruptionFilter) { + case INTERRUPTION_FILTER_ALL: return Global.ZEN_MODE_OFF; + case INTERRUPTION_FILTER_PRIORITY: return Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS; + case INTERRUPTION_FILTER_ALARMS: return Global.ZEN_MODE_ALARMS; + case INTERRUPTION_FILTER_NONE: return Global.ZEN_MODE_NO_INTERRUPTIONS; + default: return defValue; + } + } } diff --git a/core/java/android/app/SystemServiceRegistry.java b/core/java/android/app/SystemServiceRegistry.java index 46da025..391131a 100644 --- a/core/java/android/app/SystemServiceRegistry.java +++ b/core/java/android/app/SystemServiceRegistry.java @@ -91,7 +91,6 @@ import android.os.IPowerManager; import android.os.IUserManager; import android.os.PowerManager; import android.os.Process; -import android.os.RemoteException; import android.os.ServiceManager; import android.os.SystemVibrator; import android.os.UserHandle; @@ -111,7 +110,7 @@ import android.telephony.TelephonyManager; import android.util.Log; import android.view.ContextThemeWrapper; import android.view.LayoutInflater; -import android.view.PhoneLayoutInflater; +import com.android.internal.policy.PhoneLayoutInflater; import android.view.WindowManager; import android.view.WindowManagerImpl; import android.view.accessibility.AccessibilityManager; diff --git a/core/java/android/app/admin/DeviceInitializerStatus.java b/core/java/android/app/admin/DeviceInitializerStatus.java index b58711c..7de518b 100644 --- a/core/java/android/app/admin/DeviceInitializerStatus.java +++ b/core/java/android/app/admin/DeviceInitializerStatus.java @@ -37,8 +37,8 @@ package android.app.admin; * <li>{@link #STATUS_ERROR_SET_DEVICE_POLICY} * <li>{@link #STATUS_ERROR_DELETE_APPS} * <li>{@link #STATUS_ERROR_DOUBLE_BUMP} - * <li>{@link #STATUS_STATE_CONNECT_BLUETOOTH_PROXY} - * <li>{@link #STATUS_STATE_DISCONNECT_BLUETOOTH_PROXY} + * <li>{@link #STATUS_STATE_CONNECTING_BLUETOOTH_PROXY} + * <li>{@link #STATUS_STATE_DISCONNECTING_BLUETOOTH_PROXY} * <li>{@link #STATUS_STATE_DEVICE_PROVISIONED} * </ul> */ @@ -84,13 +84,13 @@ public class DeviceInitializerStatus { * Device provisioning status code that indicates that a device is connecting to establish * a Bluetooth network proxy. */ - public static final int STATUS_STATE_CONNECT_BLUETOOTH_PROXY = FLAG_STATUS_HIGH_PRIORITY | 8; + public static final int STATUS_STATE_CONNECTING_BLUETOOTH_PROXY = FLAG_STATUS_HIGH_PRIORITY | 8; /** * Device provisioning status code that indicates that a connected Bluetooth network proxy * is being shut down. */ - public static final int STATUS_STATE_DISCONNECT_BLUETOOTH_PROXY = FLAG_STATUS_HIGH_PRIORITY | 9; + public static final int STATUS_STATE_DISCONNECTING_BLUETOOTH_PROXY = FLAG_STATUS_HIGH_PRIORITY | 9; /** * Device provisioning status code that indicates that a device has been successfully @@ -141,37 +141,5 @@ public class DeviceInitializerStatus { */ public static final int STATUS_ERROR_DOUBLE_BUMP = FLAG_STATUS_ERROR | 30; - /** - * Determine if the specified status code represents an error status. - * @param statusCode status code to check - * @return {@code true} if the status code is an error status code - */ - public static boolean isErrorStatus(int statusCode) { - return isFlagSet(statusCode, FLAG_STATUS_ERROR); - } - - /** - * Determine if the specified status code is a custom status. Custom status codes are defined - * and sent by device initialization agents. - * @param statusCode status code to check - * @return {@code true} if the status code is a custom status code - */ - public static boolean isCustomStatus(int statusCode) { - return isFlagSet(statusCode, FLAG_STATUS_CUSTOM); - } - - /** - * Determine if the specified status code is a high priority status code. - * @param statusCode status code to check - * @return {@code true} if the status code is a high priority status code - */ - public static boolean isHighPriority(int statusCode) { - return isFlagSet(statusCode, FLAG_STATUS_HIGH_PRIORITY); - } - - private static boolean isFlagSet(int statusCode, int flag) { - return (statusCode & flag) != 0; - } - private DeviceInitializerStatus() {} } diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java index 8009b6c..ae07206 100644 --- a/core/java/android/app/admin/DevicePolicyManager.java +++ b/core/java/android/app/admin/DevicePolicyManager.java @@ -702,7 +702,7 @@ public class DevicePolicyManager { * reported to the remote setup device over Bluetooth. * * <p>Broadcasts with this action must supply a - * {@linkplain DeviceInitializerStatus#isCustomStatus(int) custom} status code in the + * {@linkplain DeviceInitializerStatus#FLAG_STATUS_CUSTOM custom} status code in the * {@link EXTRA_DEVICE_INITIALIZER_STATUS_CODE} extra. * * <p>Broadcasts may optionally contain a description in the @@ -718,7 +718,7 @@ public class DevicePolicyManager { * sent as part of a broadcast with an action of {@code ACTION_SEND_DEVICE_INITIALIZER_STATUS}. * * <p>The status code sent with this extra must be a custom status code as defined by - * {@link DeviceInitializerStatus#isCustomStatus(int)}. + * {@link DeviceInitializerStatus#FLAG_STATUS_CUSTOM}. * @hide */ @SystemApi @@ -1653,9 +1653,9 @@ public class DevicePolicyManager { } /** - * Queries whether {@link #DO_NOT_ASK_CREDENTIALS_ON_BOOT} flag is set. + * Queries whether {@link #RESET_PASSWORD_DO_NOT_ASK_CREDENTIALS_ON_BOOT} flag is set. * - * @return true if DO_NOT_ASK_CREDENTIALS_ON_BOOT flag is set. + * @return true if RESET_PASSWORD_DO_NOT_ASK_CREDENTIALS_ON_BOOT flag is set. * @hide */ public boolean getDoNotAskCredentialsOnBoot() { @@ -1753,7 +1753,7 @@ public class DevicePolicyManager { * is ignored. Once the flag is set, it cannot be reverted back without resetting the * device to factory defaults. */ - public static final int DO_NOT_ASK_CREDENTIALS_ON_BOOT = 0x0002; + public static final int RESET_PASSWORD_DO_NOT_ASK_CREDENTIALS_ON_BOOT = 0x0002; /** * Force a new device unlock password (the password needed to access the @@ -1779,7 +1779,7 @@ public class DevicePolicyManager { * * @param password The new password for the user. Null or empty clears the password. * @param flags May be 0 or combination of {@link #RESET_PASSWORD_REQUIRE_ENTRY} and - * {@link #DO_NOT_ASK_CREDENTIALS_ON_BOOT}. + * {@link #RESET_PASSWORD_DO_NOT_ASK_CREDENTIALS_ON_BOOT}. * @return Returns true if the password was applied, or false if it is * not acceptable for the current constraints. */ @@ -4229,7 +4229,7 @@ public class DevicePolicyManager { * Called by device initializer to send a provisioning status update to the remote setup device. * * @param statusCode a custom status code value as defined by - * {@link DeviceInitializerStatus#isCustomStatus(int)}. + * {@link DeviceInitializerStatus#FLAG_STATUS_CUSTOM}. * @param description custom description of the status code sent */ public void sendDeviceInitializerStatus(int statusCode, String description) { diff --git a/core/java/android/bluetooth/BluetoothAdapter.java b/core/java/android/bluetooth/BluetoothAdapter.java index 0a77868..ec6f18d 100644 --- a/core/java/android/bluetooth/BluetoothAdapter.java +++ b/core/java/android/bluetooth/BluetoothAdapter.java @@ -28,14 +28,11 @@ import android.bluetooth.le.ScanRecord; import android.bluetooth.le.ScanResult; import android.bluetooth.le.ScanSettings; import android.content.Context; +import android.os.Binder; import android.os.IBinder; import android.os.ParcelUuid; import android.os.RemoteException; import android.os.ServiceManager; -import android.app.ActivityThread; -import android.os.SystemProperties; -import android.provider.Settings; -import android.os.Binder; import android.util.Log; import android.util.Pair; @@ -1255,7 +1252,7 @@ public final class BluetoothAdapter { * @return true if chipset supports on-chip filtering */ public boolean isOffloadedFilteringSupported() { - if (getState() != STATE_ON) return false; + if (!getLeAccess()) return false; try { return mService.isOffloadedFilteringSupported(); } catch (RemoteException e) { @@ -1270,7 +1267,7 @@ public final class BluetoothAdapter { * @return true if chipset supports on-chip scan batching */ public boolean isOffloadedScanBatchingSupported() { - if (getState() != STATE_ON) return false; + if (!getLeAccess()) return false; try { return mService.isOffloadedScanBatchingSupported(); } catch (RemoteException e) { @@ -1286,7 +1283,7 @@ public final class BluetoothAdapter { * @hide */ public boolean isHardwareTrackingFiltersAvailable() { - if (getState() != STATE_ON) return false; + if (!getLeAccess()) return false; try { IBluetoothGatt iGatt = mManagerService.getBluetoothGatt(); if (iGatt == null) { diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java index 80c6306..6f543a8 100644 --- a/core/java/android/content/Intent.java +++ b/core/java/android/content/Intent.java @@ -17,6 +17,7 @@ package android.content; import android.content.pm.ApplicationInfo; +import android.os.ResultReceiver; import android.provider.MediaStore; import android.util.ArraySet; @@ -3276,11 +3277,79 @@ public class Intent implements Parcelable, Cloneable { /** * An Intent describing the choices you would like shown with - * {@link #ACTION_PICK_ACTIVITY}. + * {@link #ACTION_PICK_ACTIVITY} or {@link #ACTION_CHOOSER}. */ public static final String EXTRA_INTENT = "android.intent.extra.INTENT"; /** + * An Intent[] describing additional, alternate choices you would like shown with + * {@link #ACTION_CHOOSER}. + * + * <p>An app may be capable of providing several different payload types to complete a + * user's intended action. For example, an app invoking {@link #ACTION_SEND} to share photos + * with another app may use EXTRA_ALTERNATE_INTENTS to have the chooser transparently offer + * several different supported sending mechanisms for sharing, such as the actual "image/*" + * photo data or a hosted link where the photos can be viewed.</p> + * + * <p>The intent present in {@link #EXTRA_INTENT} will be treated as the + * first/primary/preferred intent in the set. Additional intents specified in + * this extra are ordered; by default intents that appear earlier in the array will be + * preferred over intents that appear later in the array as matches for the same + * target component. To alter this preference, a calling app may also supply + * {@link #EXTRA_CHOOSER_REFINEMENT_INTENT_SENDER}.</p> + */ + public static final String EXTRA_ALTERNATE_INTENTS = "android.intent.extra.ALTERNATE_INTENTS"; + + /** + * An {@link IntentSender} for an Activity that will be invoked when the user makes a selection + * from the chooser activity presented by {@link #ACTION_CHOOSER}. + * + * <p>An app preparing an action for another app to complete may wish to allow the user to + * disambiguate between several options for completing the action based on the chosen target + * or otherwise refine the action before it is invoked. + * </p> + * + * <p>When sent, this IntentSender may be filled in with the following extras:</p> + * <ul> + * <li>{@link #EXTRA_INTENT} The first intent that matched the user's chosen target</li> + * <li>{@link #EXTRA_ALTERNATE_INTENTS} Any additional intents that also matched the user's + * chosen target beyond the first</li> + * <li>{@link #EXTRA_RESULT_RECEIVER} A {@link ResultReceiver} that the refinement activity + * should fill in and send once the disambiguation is complete</li> + * </ul> + */ + public static final String EXTRA_CHOOSER_REFINEMENT_INTENT_SENDER + = "android.intent.extra.CHOOSER_REFINEMENT_INTENT_SENDER"; + + /** + * A {@link ResultReceiver} used to return data back to the sender. + * + * <p>Used to complete an app-specific + * {@link #EXTRA_CHOOSER_REFINEMENT_INTENT_SENDER refinement} for {@link #ACTION_CHOOSER}.</p> + * + * <p>If {@link #EXTRA_CHOOSER_REFINEMENT_INTENT_SENDER} is present in the intent + * used to start a {@link #ACTION_CHOOSER} activity this extra will be + * {@link #fillIn(Intent, int) filled in} to that {@link IntentSender} and sent + * when the user selects a target component from the chooser. It is up to the recipient + * to send a result to this ResultReceiver to signal that disambiguation is complete + * and that the chooser should invoke the user's choice.</p> + * + * <p>The disambiguator should provide a Bundle to the ResultReceiver with an intent + * assigned to the key {@link #EXTRA_INTENT}. This supplied intent will be used by the chooser + * to match and fill in the final Intent or ChooserTarget before starting it. + * The supplied intent must {@link #filterEquals(Intent) match} one of the intents from + * {@link #EXTRA_INTENT} or {@link #EXTRA_ALTERNATE_INTENTS} passed to + * {@link #EXTRA_CHOOSER_REFINEMENT_INTENT_SENDER} to be accepted.</p> + * + * <p>The result code passed to the ResultReceiver should be + * {@link android.app.Activity#RESULT_OK} if the refinement succeeded and the supplied intent's + * target in the chooser should be started, or {@link android.app.Activity#RESULT_CANCELED} if + * the chooser should finish without starting a target.</p> + */ + public static final String EXTRA_RESULT_RECEIVER + = "android.intent.extra.RESULT_RECEIVER"; + + /** * A CharSequence dialog title to provide to the user when used with a * {@link #ACTION_CHOOSER}. */ diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java index 7ff6ec3..51fa075 100644 --- a/core/java/android/content/pm/PackageManager.java +++ b/core/java/android/content/pm/PackageManager.java @@ -1067,6 +1067,13 @@ public abstract class PackageManager { public static final String FEATURE_AUDIO_OUTPUT = "android.hardware.audio.output"; /** + * Feature for {@link #getSystemAvailableFeatures} and {@link #hasSystemFeature}: + * The device has professional audio level of functionality, performance, and acoustics. + */ + @SdkConstant(SdkConstantType.FEATURE) + public static final String FEATURE_AUDIO_PRO = "android.hardware.audio.pro"; + + /** * Feature for {@link #getSystemAvailableFeatures} and * {@link #hasSystemFeature}: The device is capable of communicating with * other devices via Bluetooth. diff --git a/core/java/android/content/res/AssetManager.java b/core/java/android/content/res/AssetManager.java index 525059f..8d96f5c 100644 --- a/core/java/android/content/res/AssetManager.java +++ b/core/java/android/content/res/AssetManager.java @@ -785,6 +785,7 @@ public final class AssetManager implements AutoCloseable { private native final void deleteTheme(long theme); /*package*/ native static final void applyThemeStyle(long theme, int styleRes, boolean force); /*package*/ native static final void copyTheme(long dest, long source); + /*package*/ native static final void clearTheme(long theme); /*package*/ native static final int loadThemeAttributeValue(long theme, int ident, TypedValue outValue, boolean resolve); diff --git a/core/java/android/content/res/ConfigurationBoundResourceCache.java b/core/java/android/content/res/ConfigurationBoundResourceCache.java index cde7e84..fecda87 100644 --- a/core/java/android/content/res/ConfigurationBoundResourceCache.java +++ b/core/java/android/content/res/ConfigurationBoundResourceCache.java @@ -1,138 +1,58 @@ /* -* 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 android.content.res; + * 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. + */ -import android.util.ArrayMap; -import android.util.LongSparseArray; -import java.lang.ref.WeakReference; +package android.content.res; /** * A Cache class which can be used to cache resource objects that are easy to clone but more * expensive to inflate. - * @hide + * + * @hide For internal use only. */ -public class ConfigurationBoundResourceCache<T> { - - private final ArrayMap<String, LongSparseArray<WeakReference<ConstantState<T>>>> mCache = - new ArrayMap<String, LongSparseArray<WeakReference<ConstantState<T>>>>(); - - final Resources mResources; +public class ConfigurationBoundResourceCache<T> extends ThemedResourceCache<ConstantState<T>> { + private final Resources mResources; /** - * Creates a Resource cache for the given Resources instance. + * Creates a cache for the given Resources instance. * - * @param resources The Resource which can be used when creating new instances. + * @param resources the resources to use when creating new instances */ public ConfigurationBoundResourceCache(Resources resources) { mResources = resources; } /** - * Adds a new item to the cache. - * - * @param key A custom key that uniquely identifies the resource. - * @param theme The Theme instance where this resource was loaded. - * @param constantState The constant state that can create new instances of the resource. + * If the resource is cached, creates and returns a new instance of it. * + * @param key a key that uniquely identifies the drawable resource + * @param theme the theme where the resource will be used + * @return a new instance of the resource, or {@code null} if not in + * the cache */ - public void put(long key, Resources.Theme theme, ConstantState<T> constantState) { - if (constantState == null) { - return; - } - final String themeKey = theme == null ? "" : theme.getKey(); - LongSparseArray<WeakReference<ConstantState<T>>> themedCache; - synchronized (this) { - themedCache = mCache.get(themeKey); - if (themedCache == null) { - themedCache = new LongSparseArray<WeakReference<ConstantState<T>>>(1); - mCache.put(themeKey, themedCache); - } - themedCache.put(key, new WeakReference<ConstantState<T>>(constantState)); - } - } - - /** - * If the resource is cached, creates a new instance of it and returns. - * - * @param key The long key which can be used to uniquely identify the resource. - * @param theme The The Theme instance where we want to load this resource. - * - * @return If this resources was loaded before, returns a new instance of it. Otherwise, returns - * null. - */ - public T get(long key, Resources.Theme theme) { - final String themeKey = theme != null ? theme.getKey() : ""; - final LongSparseArray<WeakReference<ConstantState<T>>> themedCache; - final WeakReference<ConstantState<T>> wr; - synchronized (this) { - themedCache = mCache.get(themeKey); - if (themedCache == null) { - return null; - } - wr = themedCache.get(key); - } - if (wr == null) { - return null; - } - final ConstantState entry = wr.get(); + public T getInstance(long key, Resources.Theme theme) { + final ConstantState<T> entry = get(key, theme); if (entry != null) { - return (T) entry.newInstance(mResources, theme); - } else { // our entry has been purged - synchronized (this) { - // there is a potential race condition here where this entry may be put in - // another thread. But we prefer it to minimize lock duration - themedCache.delete(key); - } + return entry.newInstance(mResources, theme); } - return null; - } - /** - * Users of ConfigurationBoundResourceCache must call this method whenever a configuration - * change happens. On this callback, the cache invalidates all resources that are not valid - * anymore. - * - * @param configChanges The configuration changes - */ - public void onConfigurationChange(final int configChanges) { - synchronized (this) { - final int size = mCache.size(); - for (int i = size - 1; i >= 0; i--) { - final LongSparseArray<WeakReference<ConstantState<T>>> - themeCache = mCache.valueAt(i); - onConfigurationChangeInt(themeCache, configChanges); - if (themeCache.size() == 0) { - mCache.removeAt(i); - } - } - } + return null; } - private void onConfigurationChangeInt( - final LongSparseArray<WeakReference<ConstantState<T>>> themeCache, - final int configChanges) { - final int size = themeCache.size(); - for (int i = size - 1; i >= 0; i--) { - final WeakReference<ConstantState<T>> wr = themeCache.valueAt(i); - final ConstantState<T> constantState = wr.get(); - if (constantState == null || Configuration.needNewResources( - configChanges, constantState.getChangingConfigurations())) { - themeCache.removeAt(i); - } - } + @Override + public boolean shouldInvalidateEntry(ConstantState<T> entry, int configChanges) { + return Configuration.needNewResources(configChanges, entry.getChangingConfigurations()); } - } diff --git a/core/java/android/content/res/DrawableCache.java b/core/java/android/content/res/DrawableCache.java new file mode 100644 index 0000000..fc70bc6 --- /dev/null +++ b/core/java/android/content/res/DrawableCache.java @@ -0,0 +1,57 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.content.res; + +import android.graphics.drawable.Drawable; + +/** + * Class which can be used to cache Drawable resources against a theme. + */ +class DrawableCache extends ThemedResourceCache<Drawable.ConstantState> { + private final Resources mResources; + + /** + * Creates a cache for the given Resources instance. + * + * @param resources the resources to use when creating new instances + */ + public DrawableCache(Resources resources) { + mResources = resources; + } + + /** + * If the resource is cached, creates and returns a new instance of it. + * + * @param key a key that uniquely identifies the drawable resource + * @param theme the theme where the resource will be used + * @return a new instance of the resource, or {@code null} if not in + * the cache + */ + public Drawable getInstance(long key, Resources.Theme theme) { + final Drawable.ConstantState entry = get(key, theme); + if (entry != null) { + return entry.newDrawable(mResources, theme); + } + + return null; + } + + @Override + public boolean shouldInvalidateEntry(Drawable.ConstantState entry, int configChanges) { + return false; + } +} diff --git a/core/java/android/content/res/Resources.java b/core/java/android/content/res/Resources.java index ae41b69..a572590 100644 --- a/core/java/android/content/res/Resources.java +++ b/core/java/android/content/res/Resources.java @@ -20,6 +20,7 @@ import android.annotation.AttrRes; import android.annotation.ColorInt; import android.annotation.StyleRes; import android.annotation.StyleableRes; +import com.android.internal.util.GrowingArrayUtils; import com.android.internal.util.XmlUtils; import org.xmlpull.v1.XmlPullParser; @@ -60,6 +61,7 @@ import android.util.Pools.SynchronizedPool; import android.util.Slog; import android.util.TypedValue; import android.view.ViewDebug; +import android.view.ViewHierarchyEncoder; import java.io.IOException; import java.io.InputStream; @@ -132,10 +134,8 @@ public class Resources { // These are protected by mAccessLock. private final Object mAccessLock = new Object(); private final Configuration mTmpConfig = new Configuration(); - private final ArrayMap<String, LongSparseArray<WeakReference<ConstantState>>> mDrawableCache = - new ArrayMap<>(); - private final ArrayMap<String, LongSparseArray<WeakReference<ConstantState>>> mColorDrawableCache = - new ArrayMap<>(); + private final DrawableCache mDrawableCache = new DrawableCache(this); + private final DrawableCache mColorDrawableCache = new DrawableCache(this); private final ConfigurationBoundResourceCache<ColorStateList> mColorStateListCache = new ConfigurationBoundResourceCache<>(this); private final ConfigurationBoundResourceCache<Animator> mAnimatorCache = @@ -1441,7 +1441,7 @@ public class Resources { AssetManager.applyThemeStyle(mTheme, resId, force); mThemeResId = resId; - mKey += Integer.toHexString(resId) + (force ? "! " : " "); + mKey.append(resId, force); } /** @@ -1457,7 +1457,7 @@ public class Resources { AssetManager.copyTheme(mTheme, other.mTheme); mThemeResId = other.mThemeResId; - mKey = other.mKey; + mKey.setTo(other.getKey()); } /** @@ -1765,6 +1765,9 @@ public class Resources { mTheme = mAssets.createTheme(); } + /** Unique key for the series of styles applied to this theme. */ + private final ThemeKey mKey = new ThemeKey(); + @SuppressWarnings("hiding") private final AssetManager mAssets; private final long mTheme; @@ -1772,9 +1775,6 @@ public class Resources { /** Resource identifier for the theme. */ private int mThemeResId = 0; - /** Unique key for the series of styles applied to this theme. */ - private String mKey = ""; - // Needed by layoutlib. /*package*/ long getNativeTheme() { return mTheme; @@ -1784,7 +1784,7 @@ public class Resources { return mThemeResId; } - /*package*/ String getKey() { + /*package*/ ThemeKey getKey() { return mKey; } @@ -1793,29 +1793,133 @@ public class Resources { } /** - * Parses {@link #mKey} and returns a String array that holds pairs of adjacent Theme data: - * resource name followed by whether or not it was forced, as specified by - * {@link #applyStyle(int, boolean)}. + * Parses {@link #mKey} and returns a String array that holds pairs of + * adjacent Theme data: resource name followed by whether or not it was + * forced, as specified by {@link #applyStyle(int, boolean)}. * * @hide */ @ViewDebug.ExportedProperty(category = "theme", hasAdjacentMapping = true) public String[] getTheme() { - String[] themeData = mKey.split(" "); - String[] themes = new String[themeData.length * 2]; - String theme; - boolean forced; - - for (int i = 0, j = themeData.length - 1; i < themes.length; i += 2, --j) { - theme = themeData[j]; - forced = theme.endsWith("!"); - themes[i] = forced ? - getResourceNameFromHexString(theme.substring(0, theme.length() - 1)) : - getResourceNameFromHexString(theme); + final int N = mKey.mCount; + final String[] themes = new String[N * 2]; + for (int i = 0, j = N - 1; i < themes.length; i += 2, --j) { + final int resId = mKey.mResId[j]; + final boolean forced = mKey.mForce[j]; + try { + themes[i] = getResourceName(resId); + } catch (NotFoundException e) { + themes[i] = Integer.toHexString(i); + } themes[i + 1] = forced ? "forced" : "not forced"; } return themes; } + + /** @hide */ + public void encode(@NonNull ViewHierarchyEncoder encoder) { + encoder.beginObject(this); + final String[] properties = getTheme(); + for (int i = 0; i < properties.length; i += 2) { + encoder.addProperty(properties[i], properties[i+1]); + } + encoder.endObject(); + } + + /** + * Rebases the theme against the parent Resource object's current + * configuration by re-applying the styles passed to + * {@link #applyStyle(int, boolean)}. + * + * @hide + */ + public void rebase() { + AssetManager.clearTheme(mTheme); + + // Reapply the same styles in the same order. + for (int i = 0; i < mKey.mCount; i++) { + final int resId = mKey.mResId[i]; + final boolean force = mKey.mForce[i]; + AssetManager.applyThemeStyle(mTheme, resId, force); + } + } + } + + static class ThemeKey implements Cloneable { + int[] mResId; + boolean[] mForce; + int mCount; + + private int mHashCode = 0; + + public void append(int resId, boolean force) { + if (mResId == null) { + mResId = new int[4]; + } + + if (mForce == null) { + mForce = new boolean[4]; + } + + mResId = GrowingArrayUtils.append(mResId, mCount, resId); + mForce = GrowingArrayUtils.append(mForce, mCount, force); + mCount++; + + mHashCode = 31 * (31 * mHashCode + resId) + (force ? 1 : 0); + } + + /** + * Sets up this key as a deep copy of another key. + * + * @param other the key to deep copy into this key + */ + public void setTo(ThemeKey other) { + mResId = other.mResId == null ? null : other.mResId.clone(); + mForce = other.mForce == null ? null : other.mForce.clone(); + mCount = other.mCount; + } + + @Override + public int hashCode() { + return mHashCode; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + + if (o == null || getClass() != o.getClass() || hashCode() != o.hashCode()) { + return false; + } + + final ThemeKey t = (ThemeKey) o; + if (mCount != t.mCount) { + return false; + } + + final int N = mCount; + for (int i = 0; i < N; i++) { + if (mResId[i] != t.mResId[i] || mForce[i] != t.mForce[i]) { + return false; + } + } + + return true; + } + + /** + * @return a shallow copy of this key + */ + @Override + public ThemeKey clone() { + final ThemeKey other = new ThemeKey(); + other.mResId = mResId; + other.mForce = mForce; + other.mCount = mCount; + return other; + } } /** @@ -1944,8 +2048,8 @@ public class Resources { + " final compat is " + mCompatibilityInfo); } - clearDrawableCachesLocked(mDrawableCache, configChanges); - clearDrawableCachesLocked(mColorDrawableCache, configChanges); + mDrawableCache.onConfigurationChange(configChanges); + mColorDrawableCache.onConfigurationChange(configChanges); mColorStateListCache.onConfigurationChange(configChanges); mAnimatorCache.onConfigurationChange(configChanges); mStateListAnimatorCache.onConfigurationChange(configChanges); @@ -1983,48 +2087,6 @@ public class Resources { return configChanges; } - private void clearDrawableCachesLocked( - ArrayMap<String, LongSparseArray<WeakReference<ConstantState>>> caches, - int configChanges) { - final int N = caches.size(); - for (int i = 0; i < N; i++) { - clearDrawableCacheLocked(caches.valueAt(i), configChanges); - } - } - - private void clearDrawableCacheLocked( - LongSparseArray<WeakReference<ConstantState>> cache, int configChanges) { - if (DEBUG_CONFIG) { - Log.d(TAG, "Cleaning up drawables config changes: 0x" - + Integer.toHexString(configChanges)); - } - final int N = cache.size(); - for (int i = 0; i < N; i++) { - final WeakReference<ConstantState> ref = cache.valueAt(i); - if (ref != null) { - final ConstantState cs = ref.get(); - if (cs != null) { - if (Configuration.needNewResources( - configChanges, cs.getChangingConfigurations())) { - if (DEBUG_CONFIG) { - Log.d(TAG, "FLUSHING #0x" - + Long.toHexString(cache.keyAt(i)) - + " / " + cs + " with changes: 0x" - + Integer.toHexString(cs.getChangingConfigurations())); - } - cache.setValueAt(i, null); - } else if (DEBUG_CONFIG) { - Log.d(TAG, "(Keeping #0x" - + Long.toHexString(cache.keyAt(i)) - + " / " + cs + " with changes: 0x" - + Integer.toHexString(cs.getChangingConfigurations()) - + ")"); - } - } - } - } - } - /** * {@code Locale.toLanguageTag} will transform the obsolete (and deprecated) * language codes "in", "ji" and "iw" to "id", "yi" and "he" respectively. @@ -2436,7 +2498,7 @@ public class Resources { } final boolean isColorDrawable; - final ArrayMap<String, LongSparseArray<WeakReference<ConstantState>>> caches; + final DrawableCache caches; final long key; if (value.type >= TypedValue.TYPE_FIRST_COLOR_INT && value.type <= TypedValue.TYPE_LAST_COLOR_INT) { @@ -2452,7 +2514,7 @@ public class Resources { // First, check whether we have a cached version of this drawable // that was inflated against the specified theme. if (!mPreloading) { - final Drawable cachedDrawable = getCachedDrawable(caches, key, theme); + final Drawable cachedDrawable = caches.getInstance(key, theme); if (cachedDrawable != null) { return cachedDrawable; } @@ -2479,13 +2541,8 @@ public class Resources { // Determine if the drawable has unresolved theme attributes. If it // does, we'll need to apply a theme and store it in a theme-specific // cache. - final String cacheKey; - if (!dr.canApplyTheme()) { - cacheKey = CACHE_NOT_THEMED; - } else if (theme == null) { - cacheKey = CACHE_NULL_THEME; - } else { - cacheKey = theme.getKey(); + final boolean canApplyTheme = dr.canApplyTheme(); + if (canApplyTheme && theme != null) { dr = dr.mutate(); dr.applyTheme(theme); dr.clearMutated(); @@ -2495,15 +2552,14 @@ public class Resources { // cache: preload, not themed, null theme, or theme-specific. if (dr != null) { dr.setChangingConfigurations(value.changingConfigurations); - cacheDrawable(value, isColorDrawable, caches, cacheKey, key, dr); + cacheDrawable(value, isColorDrawable, caches, theme, canApplyTheme, key, dr); } return dr; } - private void cacheDrawable(TypedValue value, boolean isColorDrawable, - ArrayMap<String, LongSparseArray<WeakReference<ConstantState>>> caches, - String cacheKey, long key, Drawable dr) { + private void cacheDrawable(TypedValue value, boolean isColorDrawable, DrawableCache caches, + Theme theme, boolean usesTheme, long key, Drawable dr) { final ConstantState cs = dr.getConstantState(); if (cs == null) { return; @@ -2531,54 +2587,12 @@ public class Resources { } } else { synchronized (mAccessLock) { - LongSparseArray<WeakReference<ConstantState>> themedCache = caches.get(cacheKey); - if (themedCache == null) { - // Clean out the caches before we add more. This shouldn't - // happen very often. - pruneCaches(caches); - themedCache = new LongSparseArray<>(1); - caches.put(cacheKey, themedCache); - } - themedCache.put(key, new WeakReference<>(cs)); - } - } - } - - /** - * Prunes empty caches from the cache map. - * - * @param caches The map of caches to prune. - */ - private void pruneCaches(ArrayMap<String, - LongSparseArray<WeakReference<ConstantState>>> caches) { - final int N = caches.size(); - for (int i = N - 1; i >= 0; i--) { - final LongSparseArray<WeakReference<ConstantState>> cache = caches.valueAt(i); - if (pruneCache(cache)) { - caches.removeAt(i); + caches.put(key, theme, cs, usesTheme); } } } /** - * Prunes obsolete weak references from a cache, returning {@code true} if - * the cache is empty and should be removed. - * - * @param cache The cache of weak references to prune. - * @return {@code true} if the cache is empty and should be removed. - */ - private boolean pruneCache(LongSparseArray<WeakReference<ConstantState>> cache) { - final int N = cache.size(); - for (int i = N - 1; i >= 0; i--) { - final WeakReference entry = cache.valueAt(i); - if (entry == null || entry.get() == null) { - cache.removeAt(i); - } - } - return cache.size() == 0; - } - - /** * Loads a drawable from XML or resources stream. */ private Drawable loadDrawableForCookie(TypedValue value, int id, Theme theme) { @@ -2631,51 +2645,6 @@ public class Resources { return dr; } - private Drawable getCachedDrawable( - ArrayMap<String, LongSparseArray<WeakReference<ConstantState>>> caches, - long key, Theme theme) { - synchronized (mAccessLock) { - // First search theme-agnostic cache. - final Drawable unthemedDrawable = getCachedDrawableLocked( - caches, key, CACHE_NOT_THEMED); - if (unthemedDrawable != null) { - return unthemedDrawable; - } - - // Next search theme-specific cache. - final String themeKey = theme != null ? theme.getKey() : CACHE_NULL_THEME; - return getCachedDrawableLocked(caches, key, themeKey); - } - } - - private Drawable getCachedDrawableLocked( - ArrayMap<String, LongSparseArray<WeakReference<ConstantState>>> caches, - long key, String themeKey) { - final LongSparseArray<WeakReference<ConstantState>> cache = caches.get(themeKey); - if (cache != null) { - final ConstantState entry = getConstantStateLocked(cache, key); - if (entry != null) { - return entry.newDrawable(this); - } - } - return null; - } - - private ConstantState getConstantStateLocked( - LongSparseArray<WeakReference<ConstantState>> drawableCache, long key) { - final WeakReference<ConstantState> wr = drawableCache.get(key); - if (wr != null) { - final ConstantState entry = wr.get(); - if (entry != null) { - return entry; - } else { - // Our entry has been purged. - drawableCache.delete(key); - } - } - return null; - } - @Nullable ColorStateList loadColorStateList(TypedValue value, int id, Theme theme) throws NotFoundException { @@ -2713,8 +2682,7 @@ public class Resources { } final ConfigurationBoundResourceCache<ColorStateList> cache = mColorStateListCache; - - csl = cache.get(key, theme); + csl = cache.getInstance(key, theme); if (csl != null) { return csl; } diff --git a/core/java/android/content/res/ThemedResourceCache.java b/core/java/android/content/res/ThemedResourceCache.java new file mode 100644 index 0000000..9a2d06147 --- /dev/null +++ b/core/java/android/content/res/ThemedResourceCache.java @@ -0,0 +1,233 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.content.res; + +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.content.res.Resources.Theme; +import android.content.res.Resources.ThemeKey; +import android.util.LongSparseArray; +import android.util.ArrayMap; + +import java.lang.ref.WeakReference; + +/** + * Data structure used for caching data against themes. + * + * @param <T> type of data to cache + */ +abstract class ThemedResourceCache<T> { + private ArrayMap<ThemeKey, LongSparseArray<WeakReference<T>>> mThemedEntries; + private LongSparseArray<WeakReference<T>> mUnthemedEntries; + private LongSparseArray<WeakReference<T>> mNullThemedEntries; + + /** + * Adds a new theme-dependent entry to the cache. + * + * @param key a key that uniquely identifies the entry + * @param theme the theme against which this entry was inflated, or + * {@code null} if the entry has no theme applied + * @param entry the entry to cache + */ + public void put(long key, @Nullable Theme theme, @NonNull T entry) { + put(key, theme, entry, true); + } + + /** + * Adds a new entry to the cache. + * + * @param key a key that uniquely identifies the entry + * @param theme the theme against which this entry was inflated, or + * {@code null} if the entry has no theme applied + * @param entry the entry to cache + * @param usesTheme {@code true} if the entry is affected theme changes, + * {@code false} otherwise + */ + public void put(long key, @Nullable Theme theme, @NonNull T entry, boolean usesTheme) { + if (entry == null) { + return; + } + + synchronized (this) { + final LongSparseArray<WeakReference<T>> entries; + if (!usesTheme) { + entries = getUnthemedLocked(true); + } else { + entries = getThemedLocked(theme, true); + } + if (entries != null) { + entries.put(key, new WeakReference<>(entry)); + } + } + } + + /** + * Returns an entry from the cache. + * + * @param key a key that uniquely identifies the entry + * @param theme the theme where the entry will be used + * @return a cached entry, or {@code null} if not in the cache + */ + @Nullable + public T get(long key, @Nullable Theme theme) { + // The themed (includes null-themed) and unthemed caches are mutually + // exclusive, so we'll give priority to whichever one we think we'll + // hit first. Since most of the framework drawables are themed, that's + // probably going to be the themed cache. + synchronized (this) { + final LongSparseArray<WeakReference<T>> themedEntries = getThemedLocked(theme, false); + if (themedEntries != null) { + final WeakReference<T> themedEntry = themedEntries.get(key); + if (themedEntry != null) { + return themedEntry.get(); + } + } + + final LongSparseArray<WeakReference<T>> unthemedEntries = getUnthemedLocked(false); + if (unthemedEntries != null) { + final WeakReference<T> unthemedEntry = unthemedEntries.get(key); + if (unthemedEntry != null) { + return unthemedEntry.get(); + } + } + } + + return null; + } + + /** + * Prunes cache entries that have been invalidated by a configuration + * change. + * + * @param configChanges a bitmask of configuration changes + */ + public void onConfigurationChange(int configChanges) { + prune(configChanges); + } + + /** + * Returns whether a cached entry has been invalidated by a configuration + * change. + * + * @param entry a cached entry + * @param configChanges a non-zero bitmask of configuration changes + * @return {@code true} if the entry is invalid, {@code false} otherwise + */ + protected abstract boolean shouldInvalidateEntry(@NonNull T entry, int configChanges); + + /** + * Returns the cached data for the specified theme, optionally creating a + * new entry if one does not already exist. + * + * @param t the theme for which to return cached data + * @param create {@code true} to create an entry if one does not already + * exist, {@code false} otherwise + * @return the cached data for the theme, or {@code null} if the cache is + * empty and {@code create} was {@code false} + */ + @Nullable + private LongSparseArray<WeakReference<T>> getThemedLocked(@Nullable Theme t, boolean create) { + if (t == null) { + if (mNullThemedEntries == null && create) { + mNullThemedEntries = new LongSparseArray<>(1); + } + return mNullThemedEntries; + } + + if (mThemedEntries == null) { + if (create) { + mThemedEntries = new ArrayMap<>(1); + } else { + return null; + } + } + + final ThemeKey key = t.getKey(); + LongSparseArray<WeakReference<T>> cache = mThemedEntries.get(key); + if (cache == null && create) { + cache = new LongSparseArray<>(1); + + final ThemeKey keyClone = key.clone(); + mThemedEntries.put(keyClone, cache); + } + + return cache; + } + + /** + * Returns the theme-agnostic cached data. + * + * @param create {@code true} to create an entry if one does not already + * exist, {@code false} otherwise + * @return the theme-agnostic cached data, or {@code null} if the cache is + * empty and {@code create} was {@code false} + */ + @Nullable + private LongSparseArray<WeakReference<T>> getUnthemedLocked(boolean create) { + if (mUnthemedEntries == null && create) { + mUnthemedEntries = new LongSparseArray<>(1); + } + return mUnthemedEntries; + } + + /** + * Prunes cache entries affected by configuration changes or where weak + * references have expired. + * + * @param configChanges a bitmask of configuration changes, or {@code 0} to + * simply prune missing weak references + * @return {@code true} if the cache is completely empty after pruning + */ + private boolean prune(int configChanges) { + synchronized (this) { + if (mThemedEntries != null) { + for (int i = mThemedEntries.size() - 1; i >= 0; i--) { + if (pruneEntriesLocked(mThemedEntries.valueAt(i), configChanges)) { + mThemedEntries.removeAt(i); + } + } + } + + pruneEntriesLocked(mNullThemedEntries, configChanges); + pruneEntriesLocked(mUnthemedEntries, configChanges); + + return mThemedEntries == null && mNullThemedEntries == null + && mUnthemedEntries == null; + } + } + + private boolean pruneEntriesLocked(@Nullable LongSparseArray<WeakReference<T>> entries, + int configChanges) { + if (entries == null) { + return true; + } + + for (int i = entries.size() - 1; i >= 0; i--) { + final WeakReference<T> ref = entries.valueAt(i); + if (ref == null || pruneEntryLocked(ref.get(), configChanges)) { + entries.removeAt(i); + } + } + + return entries.size() == 0; + } + + private boolean pruneEntryLocked(@Nullable T entry, int configChanges) { + return entry == null || (configChanges != 0 + && shouldInvalidateEntry(entry, configChanges)); + } +} diff --git a/core/java/android/ddm/DdmHandleViewDebug.java b/core/java/android/ddm/DdmHandleViewDebug.java index 3a36b0a..be48633 100644 --- a/core/java/android/ddm/DdmHandleViewDebug.java +++ b/core/java/android/ddm/DdmHandleViewDebug.java @@ -229,15 +229,25 @@ public class DdmHandleViewDebug extends ChunkHandler { private Chunk dumpHierarchy(View rootView, ByteBuffer in) { boolean skipChildren = in.getInt() > 0; boolean includeProperties = in.getInt() > 0; + boolean v2 = in.hasRemaining() && in.getInt() > 0; - ByteArrayOutputStream b = new ByteArrayOutputStream(1024); + long start = System.currentTimeMillis(); + + ByteArrayOutputStream b = new ByteArrayOutputStream(2*1024*1024); try { - ViewDebug.dump(rootView, skipChildren, includeProperties, b); - } catch (IOException e) { + if (v2) { + ViewDebug.dumpv2(rootView, b); + } else { + ViewDebug.dump(rootView, skipChildren, includeProperties, b); + } + } catch (IOException | InterruptedException e) { return createFailChunk(1, "Unexpected error while obtaining view hierarchy: " + e.getMessage()); } + long end = System.currentTimeMillis(); + Log.d(TAG, "Time to obtain view hierarchy (ms): " + (end - start)); + byte[] data = b.toByteArray(); return new Chunk(CHUNK_VURT, data, 0, data.length); } diff --git a/core/java/android/hardware/camera2/CameraCaptureSession.java b/core/java/android/hardware/camera2/CameraCaptureSession.java index aeddf03..ef71c42 100644 --- a/core/java/android/hardware/camera2/CameraCaptureSession.java +++ b/core/java/android/hardware/camera2/CameraCaptureSession.java @@ -212,8 +212,7 @@ public abstract class CameraCaptureSession implements AutoCloseable { * <p>All capture sessions can be used for capturing images from the camera but only capture * sessions created by * {@link CameraDevice#createReprocessibleCaptureSession createReprocessibleCaptureSession} - * can submit reprocess capture requests. The list of requests must all be capturing images from - * the camera or all be reprocess capture requests. Submitting a reprocess request to a regular + * can submit reprocess capture requests. Submitting a reprocess request to a regular * capture session will result in an {@link IllegalArgumentException}.</p> * * @param requests the list of settings for this burst capture @@ -236,9 +235,7 @@ public abstract class CameraCaptureSession implements AutoCloseable { * @throws IllegalArgumentException If the requests target no Surfaces, or the requests target * Surfaces not currently configured as outputs; or a reprocess * capture request is submitted in a non-reprocessible capture - * session; or the list of requests contains both requests to - * capture images from the camera and reprocess capture - * requests; or one of the reprocess capture requests was + * session; or one of the reprocess capture requests was * created with a {@link TotalCaptureResult} from a different * session; or one of the captures targets a Surface in the * middle of being {@link #prepare prepared}; or if the handler diff --git a/core/java/android/hardware/camera2/impl/CameraCaptureSessionImpl.java b/core/java/android/hardware/camera2/impl/CameraCaptureSessionImpl.java index 3c19529..dff6227 100644 --- a/core/java/android/hardware/camera2/impl/CameraCaptureSessionImpl.java +++ b/core/java/android/hardware/camera2/impl/CameraCaptureSessionImpl.java @@ -177,26 +177,20 @@ public class CameraCaptureSessionImpl extends CameraCaptureSession { public synchronized int captureBurst(List<CaptureRequest> requests, CaptureCallback callback, Handler handler) throws CameraAccessException { if (requests == null) { - throw new IllegalArgumentException("requests must not be null"); + throw new IllegalArgumentException("Requests must not be null"); } else if (requests.isEmpty()) { - throw new IllegalArgumentException("requests must have at least one element"); - } - - boolean reprocess = requests.get(0).isReprocess(); - if (reprocess && !isReprocessible()) { - throw new IllegalArgumentException("this capture session cannot handle reprocess " + - "requests"); - } else if (reprocess && requests.get(0).getReprocessibleSessionId() != mId) { - throw new IllegalArgumentException("capture request was created for another session"); + throw new IllegalArgumentException("Requests must have at least one element"); } - for (int i = 1; i < requests.size(); i++) { - if (requests.get(i).isReprocess() != reprocess) { - throw new IllegalArgumentException("cannot mix regular and reprocess capture " + - " requests"); - } else if (reprocess && requests.get(i).getReprocessibleSessionId() != mId) { - throw new IllegalArgumentException("capture request was created for another " + - "session"); + for (CaptureRequest request : requests) { + if (request.isReprocess()) { + if (!isReprocessible()) { + throw new IllegalArgumentException("This capture session cannot handle " + + "reprocess requests"); + } else if (request.getReprocessibleSessionId() != mId) { + throw new IllegalArgumentException("Capture request was created for another " + + "session"); + } } } diff --git a/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java b/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java index ff4ad79..e84b46a 100644 --- a/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java +++ b/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java @@ -94,11 +94,11 @@ public class CameraDeviceImpl extends CameraDevice { private final int mTotalPartialCount; /** - * A list tracking request and its expected last frame. - * Updated when calling ICameraDeviceUser methods. + * A list tracking request and its expected last regular frame number and last reprocess frame + * number. Updated when calling ICameraDeviceUser methods. */ - private final List<SimpleEntry</*frameNumber*/Long, /*requestId*/Integer>> - mFrameNumberRequestPairs = new ArrayList<SimpleEntry<Long, Integer>>(); + private final List<RequestLastFrameNumbersHolder> mRequestLastFrameNumbersList = + new ArrayList<>(); /** * An object tracking received frame numbers. @@ -653,8 +653,8 @@ public class CameraDeviceImpl extends CameraDevice { * * <p>If lastFrameNumber is NO_FRAMES_CAPTURED, it means that the request was never * sent to HAL. Then onCaptureSequenceAborted is immediately triggered. - * If lastFrameNumber is non-negative, then the requestId and lastFrameNumber pair - * is added to the list mFrameNumberRequestPairs.</p> + * If lastFrameNumber is non-negative, then the requestId and lastFrameNumber as the last + * regular frame number will be added to the list mRequestLastFrameNumbersList.</p> * * @param requestId the request ID of the current repeating request. * @@ -693,10 +693,6 @@ public class CameraDeviceImpl extends CameraDevice { "early trigger sequence complete for request %d", requestId)); } - if (lastFrameNumber < Integer.MIN_VALUE - || lastFrameNumber > Integer.MAX_VALUE) { - throw new AssertionError(lastFrameNumber + " cannot be cast to int"); - } holder.getCallback().onCaptureSequenceAborted( CameraDeviceImpl.this, requestId); @@ -710,9 +706,11 @@ public class CameraDeviceImpl extends CameraDevice { requestId)); } } else { - mFrameNumberRequestPairs.add( - new SimpleEntry<Long, Integer>(lastFrameNumber, - requestId)); + // This function is only called for regular request so lastFrameNumber is the last + // regular frame number. + mRequestLastFrameNumbersList.add(new RequestLastFrameNumbersHolder(requestId, + lastFrameNumber)); + // It is possible that the last frame has already arrived, so we need to check // for sequence completion right away checkAndFireSequenceComplete(); @@ -779,8 +777,8 @@ public class CameraDeviceImpl extends CameraDevice { } mRepeatingRequestId = requestId; } else { - mFrameNumberRequestPairs.add( - new SimpleEntry<Long, Integer>(lastFrameNumber, requestId)); + mRequestLastFrameNumbersList.add(new RequestLastFrameNumbersHolder(requestList, + requestId, lastFrameNumber)); } if (mIdle) { @@ -1146,7 +1144,101 @@ public class CameraDeviceImpl extends CameraDevice { public int getSessionId() { return mSessionId; } + } + + /** + * This class holds a capture ID and its expected last regular frame number and last reprocess + * frame number. + */ + static class RequestLastFrameNumbersHolder { + // request ID + private final int mRequestId; + // The last regular frame number for this request ID. It's + // CaptureCallback.NO_FRAMES_CAPTURED if the request ID has no regular request. + private final long mLastRegularFrameNumber; + // The last reprocess frame number for this request ID. It's + // CaptureCallback.NO_FRAMES_CAPTURED if the request ID has no reprocess request. + private final long mLastReprocessFrameNumber; + + /** + * Create a request-last-frame-numbers holder with a list of requests, request ID, and + * the last frame number returned by camera service. + */ + public RequestLastFrameNumbersHolder(List<CaptureRequest> requestList, int requestId, + long lastFrameNumber) { + long lastRegularFrameNumber = CaptureCallback.NO_FRAMES_CAPTURED; + long lastReprocessFrameNumber = CaptureCallback.NO_FRAMES_CAPTURED; + long frameNumber = lastFrameNumber; + + if (lastFrameNumber < requestList.size() - 1) { + throw new IllegalArgumentException("lastFrameNumber: " + lastFrameNumber + + " should be at least " + (requestList.size() - 1) + " for the number of " + + " requests in the list: " + requestList.size()); + } + + // find the last regular frame number and the last reprocess frame number + for (int i = requestList.size() - 1; i >= 0; i--) { + CaptureRequest request = requestList.get(i); + if (request.isReprocess() && lastReprocessFrameNumber == + CaptureCallback.NO_FRAMES_CAPTURED) { + lastReprocessFrameNumber = frameNumber; + } else if (!request.isReprocess() && lastRegularFrameNumber == + CaptureCallback.NO_FRAMES_CAPTURED) { + lastRegularFrameNumber = frameNumber; + } + + if (lastReprocessFrameNumber != CaptureCallback.NO_FRAMES_CAPTURED && + lastRegularFrameNumber != CaptureCallback.NO_FRAMES_CAPTURED) { + break; + } + + frameNumber--; + } + + mLastRegularFrameNumber = lastRegularFrameNumber; + mLastReprocessFrameNumber = lastReprocessFrameNumber; + mRequestId = requestId; + } + + /** + * Create a request-last-frame-numbers holder with a request ID and last regular frame + * number. + */ + public RequestLastFrameNumbersHolder(int requestId, long lastRegularFrameNumber) { + mLastRegularFrameNumber = lastRegularFrameNumber; + mLastReprocessFrameNumber = CaptureCallback.NO_FRAMES_CAPTURED; + mRequestId = requestId; + } + + /** + * Return the last regular frame number. Return CaptureCallback.NO_FRAMES_CAPTURED if + * it contains no regular request. + */ + public long getLastRegularFrameNumber() { + return mLastRegularFrameNumber; + } + + /** + * Return the last reprocess frame number. Return CaptureCallback.NO_FRAMES_CAPTURED if + * it contains no reprocess request. + */ + public long getLastReprocessFrameNumber() { + return mLastReprocessFrameNumber; + } + /** + * Return the last frame number overall. + */ + public long getLastFrameNumber() { + return Math.max(mLastRegularFrameNumber, mLastReprocessFrameNumber); + } + + /** + * Return the request ID. + */ + public int getRequestId() { + return mRequestId; + } } /** @@ -1154,8 +1246,8 @@ public class CameraDeviceImpl extends CameraDevice { */ public class FrameNumberTracker { - private long mCompletedFrameNumber = -1; - private long mCompletedReprocessFrameNumber = -1; + private long mCompletedFrameNumber = CaptureCallback.NO_FRAMES_CAPTURED; + private long mCompletedReprocessFrameNumber = CaptureCallback.NO_FRAMES_CAPTURED; /** the skipped frame numbers that belong to regular results */ private final LinkedList<Long> mSkippedRegularFrameNumbers = new LinkedList<Long>(); /** the skipped frame numbers that belong to reprocess results */ @@ -1360,11 +1452,11 @@ public class CameraDeviceImpl extends CameraDevice { long completedFrameNumber = mFrameNumberTracker.getCompletedFrameNumber(); long completedReprocessFrameNumber = mFrameNumberTracker.getCompletedReprocessFrameNumber(); boolean isReprocess = false; - Iterator<SimpleEntry<Long, Integer> > iter = mFrameNumberRequestPairs.iterator(); + Iterator<RequestLastFrameNumbersHolder> iter = mRequestLastFrameNumbersList.iterator(); while (iter.hasNext()) { - final SimpleEntry<Long, Integer> frameNumberRequestPair = iter.next(); + final RequestLastFrameNumbersHolder requestLastFrameNumbers = iter.next(); boolean sequenceCompleted = false; - final int requestId = frameNumberRequestPair.getValue(); + final int requestId = requestLastFrameNumbers.getRequestId(); final CaptureCallbackHolder holder; synchronized(mInterfaceLock) { if (mRemoteDevice == null) { @@ -1376,19 +1468,22 @@ public class CameraDeviceImpl extends CameraDevice { holder = (index >= 0) ? mCaptureCallbackMap.valueAt(index) : null; if (holder != null) { - isReprocess = holder.getRequest().isReprocess(); + long lastRegularFrameNumber = + requestLastFrameNumbers.getLastRegularFrameNumber(); + long lastReprocessFrameNumber = + requestLastFrameNumbers.getLastReprocessFrameNumber(); + // check if it's okay to remove request from mCaptureCallbackMap - if ((isReprocess && frameNumberRequestPair.getKey() <= - completedReprocessFrameNumber) || (!isReprocess && - frameNumberRequestPair.getKey() <= completedFrameNumber)) { + if (lastRegularFrameNumber <= completedFrameNumber && + lastReprocessFrameNumber <= completedReprocessFrameNumber) { sequenceCompleted = true; mCaptureCallbackMap.removeAt(index); if (DEBUG) { Log.v(TAG, String.format( - "remove holder for requestId %d, " - + "because lastFrame %d is <= %d", - requestId, frameNumberRequestPair.getKey(), - completedFrameNumber)); + "Remove holder for requestId %d, because lastRegularFrame %d " + + "is <= %d and lastReprocessFrame %d is <= %d", requestId, + lastRegularFrameNumber, completedFrameNumber, + lastReprocessFrameNumber, completedReprocessFrameNumber)); } } } @@ -1412,16 +1507,10 @@ public class CameraDeviceImpl extends CameraDevice { requestId)); } - long lastFrameNumber = frameNumberRequestPair.getKey(); - if (lastFrameNumber < Integer.MIN_VALUE - || lastFrameNumber > Integer.MAX_VALUE) { - throw new AssertionError(lastFrameNumber - + " cannot be cast to int"); - } holder.getCallback().onCaptureSequenceCompleted( CameraDeviceImpl.this, requestId, - lastFrameNumber); + requestLastFrameNumbers.getLastFrameNumber()); } } }; diff --git a/core/java/android/hardware/camera2/legacy/RequestThreadManager.java b/core/java/android/hardware/camera2/legacy/RequestThreadManager.java index a4d6be0..691798f 100644 --- a/core/java/android/hardware/camera2/legacy/RequestThreadManager.java +++ b/core/java/android/hardware/camera2/legacy/RequestThreadManager.java @@ -326,9 +326,6 @@ public class RequestThreadManager { } try { - startPreview(); // If preview is not running (i.e. after a JPEG capture), we need to - // explicitely start and stop preview before setting preview surface. - // null. stopPreview(); } catch (RuntimeException e) { Log.e(TAG, "Received device exception in configure call: ", e); diff --git a/core/java/android/os/Build.java b/core/java/android/os/Build.java index 36fc4f9..50eed3e 100644 --- a/core/java/android/os/Build.java +++ b/core/java/android/os/Build.java @@ -173,6 +173,27 @@ public class Build { "ro.build.version.sdk", 0); /** + * The developer preview revision of a prerelease SDK. This value will always + * be <code>0</code> on production platform builds/devices. + * + * <p>When this value is nonzero, any new API added since the last + * officially published {@link #SDK_INT API level} is only guaranteed to be present + * on that specific preview revision. For example, an API <code>Activity.fooBar()</code> + * might be present in preview revision 1 but renamed or removed entirely in + * preview revision 2, which may cause an app attempting to call it to crash + * at runtime.</p> + * + * <p>Experimental apps targeting preview APIs should check this value for + * equality (<code>==</code>) with the preview SDK revision they were built for + * before using any prerelease platform APIs. Apps that detect a preview SDK revision + * other than the specific one they expect should fall back to using APIs from + * the previously published API level only to avoid unwanted runtime exceptions. + * </p> + */ + public static final int PREVIEW_SDK_INT = SystemProperties.getInt( + "ro.build.version.preview_sdk", 0); + + /** * The current development codename, or the string "REL" if this is * a release build. */ diff --git a/core/java/android/os/Debug.java b/core/java/android/os/Debug.java index 4aff7a1..19c8fa9 100644 --- a/core/java/android/os/Debug.java +++ b/core/java/android/os/Debug.java @@ -169,6 +169,65 @@ public final class Debug public int otherSwappedOut; /** @hide */ + public static final int HEAP_UNKNOWN = 0; + /** @hide */ + public static final int HEAP_DALVIK = 1; + /** @hide */ + public static final int HEAP_NATIVE = 2; + + /** @hide */ + public static final int OTHER_DALVIK_OTHER = 0; + /** @hide */ + public static final int OTHER_STACK = 1; + /** @hide */ + public static final int OTHER_CURSOR = 2; + /** @hide */ + public static final int OTHER_ASHMEM = 3; + /** @hide */ + public static final int OTHER_GL_DEV = 4; + /** @hide */ + public static final int OTHER_UNKNOWN_DEV = 5; + /** @hide */ + public static final int OTHER_SO = 6; + /** @hide */ + public static final int OTHER_JAR = 7; + /** @hide */ + public static final int OTHER_APK = 8; + /** @hide */ + public static final int OTHER_TTF = 9; + /** @hide */ + public static final int OTHER_DEX = 10; + /** @hide */ + public static final int OTHER_OAT = 11; + /** @hide */ + public static final int OTHER_ART = 12; + /** @hide */ + public static final int OTHER_UNKNOWN_MAP = 13; + /** @hide */ + public static final int OTHER_GRAPHICS = 14; + /** @hide */ + public static final int OTHER_GL = 15; + /** @hide */ + public static final int OTHER_OTHER_MEMTRACK = 16; + + /** @hide */ + public static final int OTHER_DALVIK_NORMAL = 17; + /** @hide */ + public static final int OTHER_DALVIK_LARGE = 18; + /** @hide */ + public static final int OTHER_DALVIK_LINEARALLOC = 19; + /** @hide */ + public static final int OTHER_DALVIK_ACCOUNTING = 20; + /** @hide */ + public static final int OTHER_DALVIK_CODE_CACHE = 21; + /** @hide */ + public static final int OTHER_DALVIK_ZYGOTE = 22; + /** @hide */ + public static final int OTHER_DALVIK_NON_MOVING = 23; + /** @hide */ + public static final int OTHER_DALVIK_INDIRECT_REFERENCE_TABLE = 24; + + /** @hide */ public static final int NUM_OTHER_STATS = 17; /** @hide */ @@ -284,6 +343,11 @@ public final class Debug } /** @hide */ + public int getOtherPrivate(int which) { + return getOtherPrivateClean(which) + getOtherPrivateDirty(which); + } + + /** @hide */ public int getOtherSharedClean(int which) { return otherStats[which*NUM_CATEGORIES + offsetSharedClean]; } @@ -296,35 +360,157 @@ public final class Debug /** @hide */ public static String getOtherLabel(int which) { switch (which) { - case 0: return "Dalvik Other"; - case 1: return "Stack"; - case 2: return "Cursor"; - case 3: return "Ashmem"; - case 4: return "Gfx dev"; - 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 "EGL mtrack"; - case 15: return "GL mtrack"; - case 16: return "Other mtrack"; - case 17: return ".Heap"; - case 18: return ".LOS"; - case 19: return ".LinearAlloc"; - case 20: return ".GC"; - case 21: return ".JITCache"; - case 22: return ".Zygote"; - case 23: return ".NonMoving"; - case 24: return ".IndirectRef"; + case OTHER_DALVIK_OTHER: return "Dalvik Other"; + case OTHER_STACK: return "Stack"; + case OTHER_CURSOR: return "Cursor"; + case OTHER_ASHMEM: return "Ashmem"; + case OTHER_GL_DEV: return "Gfx dev"; + case OTHER_UNKNOWN_DEV: return "Other dev"; + case OTHER_SO: return ".so mmap"; + case OTHER_JAR: return ".jar mmap"; + case OTHER_APK: return ".apk mmap"; + case OTHER_TTF: return ".ttf mmap"; + case OTHER_DEX: return ".dex mmap"; + case OTHER_OAT: return ".oat mmap"; + case OTHER_ART: return ".art mmap"; + case OTHER_UNKNOWN_MAP: return "Other mmap"; + case OTHER_GRAPHICS: return "EGL mtrack"; + case OTHER_GL: return "GL mtrack"; + case OTHER_OTHER_MEMTRACK: return "Other mtrack"; + case OTHER_DALVIK_NORMAL: return ".Heap"; + case OTHER_DALVIK_LARGE: return ".LOS"; + case OTHER_DALVIK_LINEARALLOC: return ".LinearAlloc"; + case OTHER_DALVIK_ACCOUNTING: return ".GC"; + case OTHER_DALVIK_CODE_CACHE: return ".JITCache"; + case OTHER_DALVIK_ZYGOTE: return ".Zygote"; + case OTHER_DALVIK_NON_MOVING: return ".NonMoving"; + case OTHER_DALVIK_INDIRECT_REFERENCE_TABLE: return ".IndirectRef"; default: return "????"; } } + /** + * Pss of Java Heap bytes in KB due to the application. + * Notes: + * * OTHER_ART is the boot image. Anything private here is blamed on + * the application, not the system. + * * dalvikPrivateDirty includes private zygote, which means the + * application dirtied something allocated by the zygote. We blame + * the application for that memory, not the system. + * * Does not include OTHER_DALVIK_OTHER, which is considered VM + * Overhead and lumped into Private Other. + * * We don't include dalvikPrivateClean, because there should be no + * such thing as private clean for the Java Heap. + * @hide + */ + public int getSummaryJavaHeap() { + return dalvikPrivateDirty + getOtherPrivate(OTHER_ART); + } + + /** + * Pss of Native Heap bytes in KB due to the application. + * Notes: + * * Includes private dirty malloc space. + * * We don't include nativePrivateClean, because there should be no + * such thing as private clean for the Native Heap. + * @hide + */ + public int getSummaryNativeHeap() { + return nativePrivateDirty; + } + + /** + * Pss of code and other static resource bytes in KB due to + * the application. + * @hide + */ + public int getSummaryCode() { + return getOtherPrivate(OTHER_SO) + + getOtherPrivate(OTHER_JAR) + + getOtherPrivate(OTHER_APK) + + getOtherPrivate(OTHER_TTF) + + getOtherPrivate(OTHER_DEX) + + getOtherPrivate(OTHER_OAT); + } + + /** + * Pss in KB of the stack due to the application. + * Notes: + * * Includes private dirty stack, which includes both Java and Native + * stack. + * * Does not include private clean stack, because there should be no + * such thing as private clean for the stack. + * @hide + */ + public int getSummaryStack() { + return getOtherPrivateDirty(OTHER_STACK); + } + + /** + * Pss in KB of graphics due to the application. + * Notes: + * * Includes private Gfx, EGL, and GL. + * * Warning: These numbers can be misreported by the graphics drivers. + * * We don't include shared graphics. It may make sense to, because + * shared graphics are likely buffers due to the application + * anyway, but it's simpler to implement to just group all shared + * memory into the System category. + * @hide + */ + public int getSummaryGraphics() { + return getOtherPrivate(OTHER_GL_DEV) + + getOtherPrivate(OTHER_GRAPHICS) + + getOtherPrivate(OTHER_GL); + } + + /** + * Pss in KB due to the application that haven't otherwise been + * accounted for. + * @hide + */ + public int getSummaryPrivateOther() { + return getTotalPrivateClean() + + getTotalPrivateDirty() + - getSummaryJavaHeap() + - getSummaryNativeHeap() + - getSummaryCode() + - getSummaryStack() + - getSummaryGraphics(); + } + + /** + * Pss in KB due to the system. + * Notes: + * * Includes all shared memory. + * @hide + */ + public int getSummarySystem() { + return getTotalPss() + - getTotalPrivateClean() + - getTotalPrivateDirty(); + } + + /** + * Total Pss in KB. + * @hide + */ + public int getSummaryTotalPss() { + return getTotalPss(); + } + + /** + * Total Swap in KB. + * Notes: + * * Some of this memory belongs in other categories, but we don't + * know if the Swap memory is shared or private, so we don't know + * what to blame on the application and what on the system. + * For now, just lump all the Swap in one place. + * @hide + */ + public int getSummaryTotalSwap() { + return getTotalSwappedOut(); + } + public int describeContents() { return 0; } diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java index 293cf6f..dc70d7b 100644 --- a/core/java/android/provider/Settings.java +++ b/core/java/android/provider/Settings.java @@ -324,23 +324,7 @@ public final class Settings { "android.settings.BLUETOOTH_SETTINGS"; /** - * Activity Action: Show settings to allow configuration of Wifi Displays. - * <p> - * In some cases, a matching Activity may not exist, so ensure you - * safeguard against this. - * <p> - * Input: Nothing. - * <p> - * Output: Nothing. - * @hide - */ - @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION) - public static final String ACTION_WIFI_DISPLAY_SETTINGS = - "android.settings.WIFI_DISPLAY_SETTINGS"; - - /** - * Activity Action: Show settings to allow configuration of - * {@link android.media.routing.MediaRouteService media route providers}. + * Activity Action: Show settings to allow configuration of cast endpoints. * <p> * In some cases, a matching Activity may not exist, so ensure you * safeguard against this. @@ -825,6 +809,18 @@ public final class Settings { = "android.settings.ACTION_NOTIFICATION_LISTENER_SETTINGS"; /** + * Activity Action: Show Do Not Disturb access settings. + * <p> + * In some cases, a matching Activity may not exist, so ensure you safeguard against this. + * <p> + * Input: Nothing. + * <p> + * Output: Nothing. + */ + @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION) + public static final String ACTION_ZEN_ACCESS_SETTINGS = "android.settings.ZEN_ACCESS_SETTINGS"; + + /** * @hide */ @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION) @@ -5951,6 +5947,12 @@ public final class Settings { "wireless_charging_started_sound"; /** + * Whether to play a sound for charging events. + * @hide + */ + public static final String CHARGING_SOUNDS_ENABLED = "charging_sounds_enabled"; + + /** * Whether we keep the device on while the device is plugged in. * Supported values are: * <ul> @@ -6166,6 +6168,17 @@ public final class Settings { */ public static final String MOBILE_DATA = "mobile_data"; + /** + * Whether the mobile data connection should remain active even when higher + * priority networks like WiFi are active, to help make network switching faster. + * + * See ConnectivityService for more info. + * + * (0 = disabled, 1 = enabled) + * @hide + */ + public static final String MOBILE_DATA_ALWAYS_ON = "mobile_data_always_on"; + /** {@hide} */ public static final String NETSTATS_ENABLED = "netstats_enabled"; /** {@hide} */ diff --git a/core/java/android/security/IKeystoreService.aidl b/core/java/android/security/IKeystoreService.aidl index 579cdbe..4809050 100644 --- a/core/java/android/security/IKeystoreService.aidl +++ b/core/java/android/security/IKeystoreService.aidl @@ -37,9 +37,9 @@ interface IKeystoreService { int exist(String name, int uid); String[] saw(String namePrefix, int uid); int reset(); - int password(String password); + int onUserPasswordChanged(int userId, String newPassword); int lock(); - int unlock(String password); + int unlock(int userId, String userPassword); int zero(); int generate(String name, int uid, int keyType, int keySize, int flags, in KeystoreArguments args); diff --git a/core/java/android/service/dreams/DreamService.java b/core/java/android/service/dreams/DreamService.java index 29aaf30..0171869 100644 --- a/core/java/android/service/dreams/DreamService.java +++ b/core/java/android/service/dreams/DreamService.java @@ -40,7 +40,7 @@ import android.view.KeyEvent; import android.view.Menu; import android.view.MenuItem; import android.view.MotionEvent; -import android.view.PhoneWindow; +import com.android.internal.policy.PhoneWindow; import android.view.SearchEvent; import android.view.View; import android.view.ViewGroup; diff --git a/core/java/android/service/notification/NotificationListenerService.java b/core/java/android/service/notification/NotificationListenerService.java index 35b8819..7956a3f 100644 --- a/core/java/android/service/notification/NotificationListenerService.java +++ b/core/java/android/service/notification/NotificationListenerService.java @@ -21,7 +21,7 @@ import android.annotation.SdkConstant; import android.app.INotificationManager; import android.app.Notification; import android.app.Notification.Builder; -import android.app.NotificationManager.Policy; +import android.app.NotificationManager; import android.app.Service; import android.content.ComponentName; import android.content.Context; @@ -64,25 +64,29 @@ public abstract class NotificationListenerService extends Service { * {@link #getCurrentInterruptionFilter() Interruption filter} constant - * Normal interruption filter. */ - public static final int INTERRUPTION_FILTER_ALL = 1; + public static final int INTERRUPTION_FILTER_ALL + = NotificationManager.INTERRUPTION_FILTER_ALL; /** * {@link #getCurrentInterruptionFilter() Interruption filter} constant - * Priority interruption filter. */ - public static final int INTERRUPTION_FILTER_PRIORITY = 2; + public static final int INTERRUPTION_FILTER_PRIORITY + = NotificationManager.INTERRUPTION_FILTER_PRIORITY; /** * {@link #getCurrentInterruptionFilter() Interruption filter} constant - * No interruptions filter. */ - public static final int INTERRUPTION_FILTER_NONE = 3; + public static final int INTERRUPTION_FILTER_NONE + = NotificationManager.INTERRUPTION_FILTER_NONE; /** * {@link #getCurrentInterruptionFilter() Interruption filter} constant - * Alarms only interruption filter. */ - public static final int INTERRUPTION_FILTER_ALARMS = 4; + public static final int INTERRUPTION_FILTER_ALARMS + = NotificationManager.INTERRUPTION_FILTER_ALARMS; /** {@link #getCurrentInterruptionFilter() Interruption filter} constant - returned when * the value is unavailable for any reason. For example, before the notification listener @@ -90,7 +94,8 @@ public abstract class NotificationListenerService extends Service { * * {@see #onListenerConnected()} */ - public static final int INTERRUPTION_FILTER_UNKNOWN = 0; + public static final int INTERRUPTION_FILTER_UNKNOWN + = NotificationManager.INTERRUPTION_FILTER_UNKNOWN; /** {@link #getCurrentListenerHints() Listener hints} constant - the primary device UI * should disable notification sound, vibrating and other visual or aural effects. @@ -516,22 +521,6 @@ public abstract class NotificationListenerService extends Service { } /** - * Gets the notification policy token associated with this listener. - * - * <p> - * Returns null if this listener is not currently active. - */ - public final Policy.Token getNotificationPolicyToken() { - if (!isBound()) return null; - try { - return getNotificationInterface().getPolicyTokenFromListener(mWrapper); - } catch (android.os.RemoteException ex) { - Log.v(TAG, "Unable to contact notification manager", ex); - return null; - } - } - - /** * Sets the desired {@link #getCurrentListenerHints() listener hints}. * * <p> diff --git a/core/java/android/service/notification/ZenModeConfig.java b/core/java/android/service/notification/ZenModeConfig.java index 26bd10f..599ac74 100644 --- a/core/java/android/service/notification/ZenModeConfig.java +++ b/core/java/android/service/notification/ZenModeConfig.java @@ -538,7 +538,7 @@ public class ZenModeConfig implements Parcelable { } priorityCallSenders = sourceToPrioritySenders(allowCallsFrom, priorityCallSenders); priorityMessageSenders = sourceToPrioritySenders(allowMessagesFrom, priorityMessageSenders); - return new Policy(priorityCategories, priorityCallSenders); + return new Policy(priorityCategories, priorityCallSenders, priorityMessageSenders); } private static int sourceToPrioritySenders(int source, int def) { @@ -567,7 +567,9 @@ public class ZenModeConfig implements Parcelable { allowReminders = (policy.priorityCategories & Policy.PRIORITY_CATEGORY_REMINDERS) != 0; allowRepeatCallers = (policy.priorityCategories & Policy.PRIORITY_CATEGORY_REPEAT_CALLERS) != 0; - allowCallsFrom = prioritySendersToSource(policy.prioritySenders, allowCallsFrom); + allowCallsFrom = prioritySendersToSource(policy.priorityCallSenders, allowCallsFrom); + allowMessagesFrom = prioritySendersToSource(policy.priorityMessageSenders, + allowMessagesFrom); } public static Condition toTimeCondition(Context context, int minutesFromNow, int userHandle) { @@ -733,17 +735,20 @@ public class ZenModeConfig implements Parcelable { && conditionId.getPathSegments().get(0).equals(EVENT_PATH); if (!isEvent) return null; final EventInfo rt = new EventInfo(); - rt.calendar = tryParseLong(conditionId.getQueryParameter("calendar"), 0L); + rt.calendar = tryParseLong(conditionId.getQueryParameter("calendar"), + EventInfo.ANY_CALENDAR); rt.reply = tryParseInt(conditionId.getQueryParameter("reply"), 0); return rt; } public static class EventInfo { + public static final long ANY_CALENDAR = 0; + public static final int REPLY_ANY_EXCEPT_NO = 0; public static final int REPLY_YES_OR_MAYBE = 1; public static final int REPLY_YES = 2; - public long calendar; // CalendarContract.Calendars._ID, or 0 for any + public long calendar = ANY_CALENDAR; // CalendarContract.Calendars._ID, or ANY_CALENDAR public int reply; @Override diff --git a/core/java/android/speech/tts/TextToSpeech.java b/core/java/android/speech/tts/TextToSpeech.java index cf29310..3eeb04a 100644 --- a/core/java/android/speech/tts/TextToSpeech.java +++ b/core/java/android/speech/tts/TextToSpeech.java @@ -1481,14 +1481,8 @@ public class TextToSpeech { // interface). // Sanitize locale using isLanguageAvailable. - int result = service.isLanguageAvailable( language, country, variant); - if (result >= LANG_AVAILABLE){ - if (result < LANG_COUNTRY_VAR_AVAILABLE) { - variant = ""; - if (result < LANG_COUNTRY_AVAILABLE) { - country = ""; - } - } + int result = service.isLanguageAvailable(language, country, variant); + if (result >= LANG_AVAILABLE) { // Get the default voice for the locale. String voiceName = service.getDefaultVoiceNameFor(language, country, variant); if (TextUtils.isEmpty(voiceName)) { @@ -1502,10 +1496,28 @@ public class TextToSpeech { return LANG_NOT_SUPPORTED; } + // Set the language/country/variant of the voice, so #getLanguage will return + // the currently set voice locale when called. + Voice voice = getVoice(service, voiceName); + String voiceLanguage = ""; + try { + voiceLanguage = voice.getLocale().getISO3Language(); + } catch (MissingResourceException e) { + Log.w(TAG, "Couldn't retrieve ISO 639-2/T language code for locale: " + + voice.getLocale(), e); + } + + String voiceCountry = ""; + try { + voiceCountry = voice.getLocale().getISO3Country(); + } catch (MissingResourceException e) { + Log.w(TAG, "Couldn't retrieve ISO 3166 country code for locale: " + + voice.getLocale(), e); + } mParams.putString(Engine.KEY_PARAM_VOICE_NAME, voiceName); - mParams.putString(Engine.KEY_PARAM_LANGUAGE, language); - mParams.putString(Engine.KEY_PARAM_COUNTRY, country); - mParams.putString(Engine.KEY_PARAM_VARIANT, variant); + mParams.putString(Engine.KEY_PARAM_LANGUAGE, voiceLanguage); + mParams.putString(Engine.KEY_PARAM_COUNTRY, voiceCountry); + mParams.putString(Engine.KEY_PARAM_VARIANT, voice.getLocale().getVariant()); } return result; } @@ -1654,20 +1666,32 @@ public class TextToSpeech { if (TextUtils.isEmpty(voiceName)) { return null; } - List<Voice> voices = service.getVoices(); - if (voices == null) { - return null; - } - for (Voice voice : voices) { - if (voice.getName().equals(voiceName)) { - return voice; - } - } - return null; + return getVoice(service, voiceName); } }, null, "getVoice"); } + + /** + * Returns a Voice instance of the voice with the given voice name. + * + * @return Voice instance with the given voice name, or {@code null} if not set or on error. + * + * @see Voice + */ + private Voice getVoice(ITextToSpeechService service, String voiceName) throws RemoteException { + List<Voice> voices = service.getVoices(); + if (voices == null) { + return null; + } + for (Voice voice : voices) { + if (voice.getName().equals(voiceName)) { + return voice; + } + } + return null; + } + /** * Returns a Voice instance that's the default voice for the default Text-to-speech language. * @return The default voice instance for the default language, or {@code null} if not set or @@ -1690,14 +1714,7 @@ public class TextToSpeech { // Sanitize the locale using isLanguageAvailable. int result = service.isLanguageAvailable(language, country, variant); - if (result >= LANG_AVAILABLE){ - if (result < LANG_COUNTRY_VAR_AVAILABLE) { - variant = ""; - if (result < LANG_COUNTRY_AVAILABLE) { - country = ""; - } - } - } else { + if (result < LANG_AVAILABLE) { // The default language is not supported. return null; } diff --git a/core/java/android/text/StaticLayout.java b/core/java/android/text/StaticLayout.java index 451abea..59c7c6d 100644 --- a/core/java/android/text/StaticLayout.java +++ b/core/java/android/text/StaticLayout.java @@ -91,6 +91,7 @@ public class StaticLayout extends Layout { b.mEllipsizedWidth = width; b.mEllipsize = null; b.mMaxLines = Integer.MAX_VALUE; + b.mBreakStrategy = Layout.BREAK_STRATEGY_SIMPLE; b.mMeasuredText = MeasuredText.obtain(); return b; @@ -100,6 +101,8 @@ public class StaticLayout extends Layout { b.mPaint = null; b.mText = null; MeasuredText.recycle(b.mMeasuredText); + b.mMeasuredText = null; + nFinishBuilder(b.mNativePtr); sPool.release(b); } diff --git a/core/java/android/text/method/WordIterator.java b/core/java/android/text/method/WordIterator.java index 11226a9..c4dc5ed 100644 --- a/core/java/android/text/method/WordIterator.java +++ b/core/java/android/text/method/WordIterator.java @@ -95,6 +95,45 @@ public class WordIterator implements Selection.PositionIterator { } while (true); } + /** {@inheritDoc} */ + public boolean isBoundary(int offset) { + int shiftedOffset = offset - mOffsetShift; + checkOffsetIsValid(shiftedOffset); + return mIterator.isBoundary(shiftedOffset); + } + + /** + * Returns the position of next boundary after the given offset. Returns + * {@code DONE} if there is no boundary after the given offset. + * + * @param offset the given start position to search from. + * @return the position of the last boundary preceding the given offset. + */ + public int nextBoundary(int offset) { + int shiftedOffset = offset - mOffsetShift; + shiftedOffset = mIterator.following(shiftedOffset); + if (shiftedOffset == BreakIterator.DONE) { + return BreakIterator.DONE; + } + return shiftedOffset + mOffsetShift; + } + + /** + * Returns the position of boundary preceding the given offset or + * {@code DONE} if the given offset specifies the starting position. + * + * @param offset the given start position to search from. + * @return the position of the last boundary preceding the given offset. + */ + public int prevBoundary(int offset) { + int shiftedOffset = offset - mOffsetShift; + shiftedOffset = mIterator.preceding(shiftedOffset); + if (shiftedOffset == BreakIterator.DONE) { + return BreakIterator.DONE; + } + return shiftedOffset + mOffsetShift; + } + /** If <code>offset</code> is within a word, returns the index of the first character of that * word, otherwise returns BreakIterator.DONE. * diff --git a/core/java/android/transition/Transition.java b/core/java/android/transition/Transition.java index ebc2aac..1b25505 100644 --- a/core/java/android/transition/Transition.java +++ b/core/java/android/transition/Transition.java @@ -1639,6 +1639,7 @@ public abstract class Transition implements Cloneable { for (int i = 0; i < count; i++) { TransitionValues values = lookIn.get(i); if (values == null) { + // Null values are always added to the end of the list, so we know to stop now. return null; } if (values.view == view) { @@ -1742,6 +1743,9 @@ public abstract class Transition implements Cloneable { View oldView = oldInfo.view; TransitionValues startValues = getTransitionValues(oldView, true); TransitionValues endValues = getMatchedTransitionValues(oldView, true); + if (startValues == null && endValues == null) { + endValues = mEndValues.viewValues.get(oldView); + } boolean cancel = (startValues != null || endValues != null) && oldInfo.transition.areValuesChanged(oldValues, endValues); if (cancel) { diff --git a/core/java/android/util/FloatMath.java b/core/java/android/util/FloatMath.java index 8f488af..bb7d15f 100644 --- a/core/java/android/util/FloatMath.java +++ b/core/java/android/util/FloatMath.java @@ -25,6 +25,8 @@ package android.util; * {@link java.lang.Math}. {@link java.lang.Math} should be used in * preference. * + * <p>All methods were removed from the public API in version 23. + * * @deprecated Use {@link java.lang.Math} instead. */ @Deprecated @@ -39,6 +41,7 @@ public class FloatMath { * * @param value to be converted * @return the floor of value + * @removed */ public static float floor(float value) { return (float) Math.floor(value); @@ -50,6 +53,7 @@ public class FloatMath { * * @param value to be converted * @return the ceiling of value + * @removed */ public static float ceil(float value) { return (float) Math.ceil(value); @@ -60,6 +64,7 @@ public class FloatMath { * * @param angle to compute the cosine of, in radians * @return the sine of angle + * @removed */ public static float sin(float angle) { return (float) Math.sin(angle); @@ -70,6 +75,7 @@ public class FloatMath { * * @param angle to compute the cosine of, in radians * @return the cosine of angle + * @removed */ public static float cos(float angle) { return (float) Math.cos(angle); @@ -81,6 +87,7 @@ public class FloatMath { * * @param value to compute sqrt of * @return the square root of value + * @removed */ public static float sqrt(float value) { return (float) Math.sqrt(value); @@ -92,6 +99,7 @@ public class FloatMath { * * @param value to compute the exponential of * @return the exponential of value + * @removed */ public static float exp(float value) { return (float) Math.exp(value); @@ -104,6 +112,7 @@ public class FloatMath { * @param x the base of the operation. * @param y the exponent of the operation. * @return {@code x} to the power of {@code y}. + * @removed */ public static float pow(float x, float y) { return (float) Math.pow(x, y); @@ -116,6 +125,7 @@ public class FloatMath { * @param x a float number * @param y a float number * @return the hypotenuse + * @removed */ public static float hypot(float x, float y) { return (float) Math.hypot(x, y); diff --git a/core/java/android/view/ContextThemeWrapper.java b/core/java/android/view/ContextThemeWrapper.java index 9047b1d..ce50091 100644 --- a/core/java/android/view/ContextThemeWrapper.java +++ b/core/java/android/view/ContextThemeWrapper.java @@ -87,9 +87,12 @@ public class ContextThemeWrapper extends ContextWrapper { } } - @Override public void setTheme(int resid) { - mThemeResource = resid; - initializeTheme(); + @Override + public void setTheme(int resid) { + if (mThemeResource != resid) { + mThemeResource = resid; + initializeTheme(); + } } /** @hide */ diff --git a/core/java/android/view/GhostView.java b/core/java/android/view/GhostView.java index bc38e1a..41502b6 100644 --- a/core/java/android/view/GhostView.java +++ b/core/java/android/view/GhostView.java @@ -40,8 +40,7 @@ public class GhostView extends View { mView.mGhostView = this; final ViewGroup parent = (ViewGroup) mView.getParent(); setGhostedVisibility(View.INVISIBLE); - parent.mRecreateDisplayList = true; - parent.updateDisplayListIfDirty(); + parent.invalidate(); } @Override @@ -83,8 +82,7 @@ public class GhostView extends View { mView.mGhostView = null; final ViewGroup parent = (ViewGroup) mView.getParent(); if (parent != null) { - parent.mRecreateDisplayList = true; - parent.updateDisplayListIfDirty(); + parent.invalidate(); } } } diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java index f62e6a2..e1f1816 100644 --- a/core/java/android/view/View.java +++ b/core/java/android/view/View.java @@ -633,7 +633,6 @@ import java.util.concurrent.atomic.AtomicInteger; * </p> * * @attr ref android.R.styleable#View_alpha - * @attr ref android.R.styleable#View_assistBlocked * @attr ref android.R.styleable#View_background * @attr ref android.R.styleable#View_clickable * @attr ref android.R.styleable#View_contentDescription @@ -2533,7 +2532,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, PFLAG3_SCROLL_INDICATOR_END >> SCROLL_INDICATORS_TO_PFLAGS3_LSHIFT; /** - * <p>Indicates that we are allowing {@link android.view.ViewAssistStructure} to traverse + * <p>Indicates that we are allowing {@link ViewStructure} to traverse * into this view.<p> */ static final int PFLAG3_ASSIST_BLOCKED = 0x100; @@ -4050,11 +4049,6 @@ public class View implements Drawable.Callback, KeyEvent.Callback, viewFlagMasks |= SAVE_DISABLED_MASK; } break; - case com.android.internal.R.styleable.View_assistBlocked: - if (a.getBoolean(attr, false)) { - mPrivateFlags3 |= PFLAG3_ASSIST_BLOCKED; - } - break; case com.android.internal.R.styleable.View_duplicateParentState: if (a.getBoolean(attr, false)) { viewFlagValues |= DUPLICATE_PARENT_STATE; @@ -6163,7 +6157,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * @param structure Fill in with structured view data. The default implementation * fills in all data that can be inferred from the view itself. */ - public void onProvideAssistStructure(ViewAssistStructure structure) { + public void onProvideStructure(ViewStructure structure) { final int id = mID; if (id > 0 && (id&0xff000000) != 0 && (id&0x00ff0000) != 0 && (id&0x0000ffff) != 0) { @@ -6217,6 +6211,11 @@ public class View implements Drawable.Callback, KeyEvent.Callback, structure.setContentDescription(getContentDescription()); } + /** @hide */ + public void onProvideAssistStructure(ViewStructure structure) { + onProvideStructure(structure); + } + /** * Called when assist structure is being retrieved from a view as part of * {@link android.app.Activity#onProvideAssistData Activity.onProvideAssistData} to @@ -6225,19 +6224,24 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * view's virtual accessibility nodes, if any. You can override this for a more * optimal implementation providing this data. */ - public void onProvideVirtualAssistStructure(ViewAssistStructure structure) { + public void onProvideVirtualStructure(ViewStructure structure) { AccessibilityNodeProvider provider = getAccessibilityNodeProvider(); if (provider != null) { AccessibilityNodeInfo info = createAccessibilityNodeInfo(); Log.i("View", "Provider of " + this + ": children=" + info.getChildCount()); structure.setChildCount(1); - ViewAssistStructure root = structure.newChild(0); - populateVirtualAssistStructure(root, provider, info); + ViewStructure root = structure.newChild(0); + populateVirtualStructure(root, provider, info); info.recycle(); } } - private void populateVirtualAssistStructure(ViewAssistStructure structure, + /** @hide */ + public void onProvideVirtualAssistStructure(ViewStructure structure) { + onProvideVirtualStructure(structure); + } + + private void populateVirtualStructure(ViewStructure structure, AccessibilityNodeProvider provider, AccessibilityNodeInfo info) { structure.setId(AccessibilityNodeInfo.getVirtualDescendantId(info.getSourceNodeId()), null, null, null); @@ -6288,19 +6292,19 @@ public class View implements Drawable.Callback, KeyEvent.Callback, for (int i=0; i<NCHILDREN; i++) { AccessibilityNodeInfo cinfo = provider.createAccessibilityNodeInfo( AccessibilityNodeInfo.getVirtualDescendantId(info.getChildId(i))); - ViewAssistStructure child = structure.newChild(i); - populateVirtualAssistStructure(child, provider, cinfo); + ViewStructure child = structure.newChild(i); + populateVirtualStructure(child, provider, cinfo); cinfo.recycle(); } } } /** - * Dispatch creation of {@link ViewAssistStructure} down the hierarchy. The default - * implementation calls {@link #onProvideAssistStructure} and - * {@link #onProvideVirtualAssistStructure}. + * Dispatch creation of {@link ViewStructure} down the hierarchy. The default + * implementation calls {@link #onProvideStructure} and + * {@link #onProvideVirtualStructure}. */ - public void dispatchProvideAssistStructure(ViewAssistStructure structure) { + public void dispatchProvideStructure(ViewStructure structure) { if (!isAssistBlocked()) { onProvideAssistStructure(structure); onProvideVirtualAssistStructure(structure); @@ -7904,8 +7908,9 @@ public class View implements Drawable.Callback, KeyEvent.Callback, } /** + * @hide * Indicates whether this view will participate in data collection through - * {@link android.view.ViewAssistStructure}. If true, it will not provide any data + * {@link ViewStructure}. If true, it will not provide any data * for itself or its children. If false, the normal data collection will be allowed. * * @return Returns false if assist data collection is not blocked, else true. @@ -7918,17 +7923,18 @@ public class View implements Drawable.Callback, KeyEvent.Callback, } /** + * @hide * Controls whether assist data collection from this view and its children is enabled - * (that is, whether {@link #onProvideAssistStructure} and - * {@link #onProvideVirtualAssistStructure} will be called). The default value is false, + * (that is, whether {@link #onProvideStructure} and + * {@link #onProvideVirtualStructure} will be called). The default value is false, * allowing normal assist collection. Setting this to false will disable assist collection. * * @param enabled Set to true to <em>disable</em> assist data collection, or false * (the default) to allow it. * * @see #isAssistBlocked() - * @see #onProvideAssistStructure - * @see #onProvideVirtualAssistStructure + * @see #onProvideStructure + * @see #onProvideVirtualStructure * @attr ref android.R.styleable#View_assistBlocked */ public void setAssistBlocked(boolean enabled) { @@ -22353,4 +22359,138 @@ public class View implements Drawable.Callback, KeyEvent.Callback, final String output = bits + " " + name; found.put(key, output); } + + /** {@hide} */ + public void encode(@NonNull ViewHierarchyEncoder stream) { + stream.beginObject(this); + encodeProperties(stream); + stream.endObject(); + } + + /** {@hide} */ + @CallSuper + protected void encodeProperties(@NonNull ViewHierarchyEncoder stream) { + Object resolveId = ViewDebug.resolveId(getContext(), mID); + if (resolveId instanceof String) { + stream.addProperty("id", (String) resolveId); + } else { + stream.addProperty("id", mID); + } + + stream.addProperty("misc:transformation.alpha", + mTransformationInfo != null ? mTransformationInfo.mAlpha : 0); + stream.addProperty("misc:transitionName", getTransitionName()); + + // layout + stream.addProperty("layout:left", mLeft); + stream.addProperty("layout:right", mRight); + stream.addProperty("layout:top", mTop); + stream.addProperty("layout:bottom", mBottom); + stream.addProperty("layout:width", getWidth()); + stream.addProperty("layout:height", getHeight()); + stream.addProperty("layout:layoutDirection", getLayoutDirection()); + stream.addProperty("layout:layoutRtl", isLayoutRtl()); + stream.addProperty("layout:hasTransientState", hasTransientState()); + stream.addProperty("layout:baseline", getBaseline()); + + // layout params + ViewGroup.LayoutParams layoutParams = getLayoutParams(); + if (layoutParams != null) { + stream.addPropertyKey("layoutParams"); + layoutParams.encode(stream); + } + + // scrolling + stream.addProperty("scrolling:scrollX", mScrollX); + stream.addProperty("scrolling:scrollY", mScrollY); + + // padding + stream.addProperty("padding:paddingLeft", mPaddingLeft); + stream.addProperty("padding:paddingRight", mPaddingRight); + stream.addProperty("padding:paddingTop", mPaddingTop); + stream.addProperty("padding:paddingBottom", mPaddingBottom); + stream.addProperty("padding:userPaddingRight", mUserPaddingRight); + stream.addProperty("padding:userPaddingLeft", mUserPaddingLeft); + stream.addProperty("padding:userPaddingBottom", mUserPaddingBottom); + stream.addProperty("padding:userPaddingStart", mUserPaddingStart); + stream.addProperty("padding:userPaddingEnd", mUserPaddingEnd); + + // measurement + stream.addProperty("measurement:minHeight", mMinHeight); + stream.addProperty("measurement:minWidth", mMinWidth); + stream.addProperty("measurement:measuredWidth", mMeasuredWidth); + stream.addProperty("measurement:measuredHeight", mMeasuredHeight); + + // drawing + stream.addProperty("drawing:elevation", getElevation()); + stream.addProperty("drawing:translationX", getTranslationX()); + stream.addProperty("drawing:translationY", getTranslationY()); + stream.addProperty("drawing:translationZ", getTranslationZ()); + stream.addProperty("drawing:rotation", getRotation()); + stream.addProperty("drawing:rotationX", getRotationX()); + stream.addProperty("drawing:rotationY", getRotationY()); + stream.addProperty("drawing:scaleX", getScaleX()); + stream.addProperty("drawing:scaleY", getScaleY()); + stream.addProperty("drawing:pivotX", getPivotX()); + stream.addProperty("drawing:pivotY", getPivotY()); + stream.addProperty("drawing:opaque", isOpaque()); + stream.addProperty("drawing:alpha", getAlpha()); + stream.addProperty("drawing:transitionAlpha", getTransitionAlpha()); + stream.addProperty("drawing:shadow", hasShadow()); + stream.addProperty("drawing:solidColor", getSolidColor()); + stream.addProperty("drawing:layerType", mLayerType); + stream.addProperty("drawing:willNotDraw", willNotDraw()); + stream.addProperty("drawing:hardwareAccelerated", isHardwareAccelerated()); + stream.addProperty("drawing:willNotCacheDrawing", willNotCacheDrawing()); + stream.addProperty("drawing:drawingCacheEnabled", isDrawingCacheEnabled()); + stream.addProperty("drawing:overlappingRendering", hasOverlappingRendering()); + + // focus + stream.addProperty("focus:hasFocus", hasFocus()); + stream.addProperty("focus:isFocused", isFocused()); + stream.addProperty("focus:isFocusable", isFocusable()); + stream.addProperty("focus:isFocusableInTouchMode", isFocusableInTouchMode()); + + stream.addProperty("misc:clickable", isClickable()); + stream.addProperty("misc:pressed", isPressed()); + stream.addProperty("misc:selected", isSelected()); + stream.addProperty("misc:touchMode", isInTouchMode()); + stream.addProperty("misc:hovered", isHovered()); + stream.addProperty("misc:activated", isActivated()); + + stream.addProperty("misc:visibility", getVisibility()); + stream.addProperty("misc:fitsSystemWindows", getFitsSystemWindows()); + stream.addProperty("misc:filterTouchesWhenObscured", getFilterTouchesWhenObscured()); + + stream.addProperty("misc:enabled", isEnabled()); + stream.addProperty("misc:soundEffectsEnabled", isSoundEffectsEnabled()); + stream.addProperty("misc:hapticFeedbackEnabled", isHapticFeedbackEnabled()); + + // theme attributes + Resources.Theme theme = getContext().getTheme(); + if (theme != null) { + stream.addPropertyKey("theme"); + theme.encode(stream); + } + + // view attribute information + int n = mAttributes != null ? mAttributes.length : 0; + stream.addProperty("meta:__attrCount__", n/2); + for (int i = 0; i < n; i += 2) { + stream.addProperty("meta:__attr__" + mAttributes[i], mAttributes[i+1]); + } + + stream.addProperty("misc:scrollBarStyle", getScrollBarStyle()); + + // text + stream.addProperty("text:textDirection", getTextDirection()); + stream.addProperty("text:textAlignment", getTextAlignment()); + + // accessibility + CharSequence contentDescription = getContentDescription(); + stream.addProperty("accessibility:contentDescription", + contentDescription == null ? "" : contentDescription.toString()); + stream.addProperty("accessibility:labelFor", getLabelFor()); + stream.addProperty("accessibility:importantForAccessibility", getImportantForAccessibility()); + } } diff --git a/core/java/android/view/ViewAssistStructure.java b/core/java/android/view/ViewAssistStructure.java index fccfbb8..a66d93c 100644 --- a/core/java/android/view/ViewAssistStructure.java +++ b/core/java/android/view/ViewAssistStructure.java @@ -16,70 +16,9 @@ package android.view; -import android.graphics.Rect; -import android.os.Bundle; -import android.text.TextPaint; - /** - * Container for storing additional per-view data generated by {@link View#onProvideAssistStructure - * View.onProvideAssistStructure}. + * @deprecated Temporary until old apps can move off this. */ -public abstract class ViewAssistStructure { - public abstract void setId(int id, String packageName, String typeName, String entryName); - - public abstract void setDimens(int left, int top, int scrollX, int scrollY, int width, - int height); - - public abstract void setVisibility(int visibility); - - public abstract void setAssistBlocked(boolean state); - - public abstract void setEnabled(boolean state); - - public abstract void setClickable(boolean state); - - public abstract void setLongClickable(boolean state); - - public abstract void setStylusButtonPressable(boolean state); - - public abstract void setFocusable(boolean state); - - public abstract void setFocused(boolean state); - - public abstract void setAccessibilityFocused(boolean state); - - public abstract void setCheckable(boolean state); - - public abstract void setChecked(boolean state); - - public abstract void setSelected(boolean state); - - public abstract void setActivated(boolean state); - - public abstract void setClassName(String className); - - public abstract void setContentDescription(CharSequence contentDescription); - - public abstract void setText(CharSequence text); - public abstract void setText(CharSequence text, int selectionStart, int selectionEnd); - public abstract void setTextPaint(TextPaint paint); - public abstract void setHint(CharSequence hint); - - public abstract CharSequence getText(); - public abstract int getTextSelectionStart(); - public abstract int getTextSelectionEnd(); - public abstract CharSequence getHint(); - - public abstract Bundle editExtras(); - public abstract void clearExtras(); - - public abstract void setChildCount(int num); - public abstract int getChildCount(); - public abstract ViewAssistStructure newChild(int index); - - public abstract ViewAssistStructure asyncNewChild(int index); - public abstract void asyncCommit(); - - /** @hide */ - public abstract Rect getTempRect(); +@Deprecated +public abstract class ViewAssistStructure extends ViewStructure { } diff --git a/core/java/android/view/ViewDebug.java b/core/java/android/view/ViewDebug.java index 27304f5..8bf53a8 100644 --- a/core/java/android/view/ViewDebug.java +++ b/core/java/android/view/ViewDebug.java @@ -16,6 +16,7 @@ package android.view; +import android.annotation.NonNull; import android.content.Context; import android.content.res.Resources; import android.graphics.Bitmap; @@ -800,6 +801,7 @@ public class ViewDebug { /** * Dumps the view hierarchy starting from the given view. + * @deprecated See {@link #dumpv2(View, ByteArrayOutputStream)} below. * @hide */ public static void dump(View root, boolean skipChildren, boolean includeProperties, @@ -825,6 +827,28 @@ public class ViewDebug { } /** + * Dumps the view hierarchy starting from the given view. + * Rather than using reflection, it uses View's encode method to obtain all the properties. + * @hide + */ + public static void dumpv2(@NonNull final View view, @NonNull ByteArrayOutputStream out) + throws InterruptedException { + final ViewHierarchyEncoder encoder = new ViewHierarchyEncoder(out); + final CountDownLatch latch = new CountDownLatch(1); + + view.post(new Runnable() { + @Override + public void run() { + view.encode(encoder); + latch.countDown(); + } + }); + + latch.await(2, TimeUnit.SECONDS); + encoder.endStream(); + } + + /** * Dumps the theme attributes from the given View. * @hide */ diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java index babb4e9..51c4760 100644 --- a/core/java/android/view/ViewGroup.java +++ b/core/java/android/view/ViewGroup.java @@ -2878,12 +2878,12 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager } /** - * Dispatch creation of {@link ViewAssistStructure} down the hierarchy. This implementation + * Dispatch creation of {@link ViewStructure} down the hierarchy. This implementation * adds in all child views of the view group, in addition to calling the default View * implementation. */ - public void dispatchProvideAssistStructure(ViewAssistStructure structure) { - super.dispatchProvideAssistStructure(structure); + public void dispatchProvideStructure(ViewStructure structure) { + super.dispatchProvideStructure(structure); if (!isAssistBlocked()) { if (structure.getChildCount() == 0) { final int childrenCount = getChildCount(); @@ -2898,8 +2898,8 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager ? getChildDrawingOrder(childrenCount, i) : i; final View child = (preorderedList == null) ? children[childIndex] : preorderedList.get(childIndex); - ViewAssistStructure cstructure = structure.newChild(i); - child.dispatchProvideAssistStructure(cstructure); + ViewStructure cstructure = structure.newChild(i); + child.dispatchProvideStructure(cstructure); } } } @@ -6861,6 +6861,19 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager } return String.valueOf(size); } + + /** @hide */ + void encode(@NonNull ViewHierarchyEncoder encoder) { + encoder.beginObject(this); + encodeProperties(encoder); + encoder.endObject(); + } + + /** @hide */ + protected void encodeProperties(@NonNull ViewHierarchyEncoder encoder) { + encoder.addProperty("width", width); + encoder.addProperty("height", height); + } } /** @@ -7329,6 +7342,18 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager bottomMargin, paint); } + + /** @hide */ + @Override + protected void encodeProperties(@NonNull ViewHierarchyEncoder encoder) { + super.encodeProperties(encoder); + encoder.addProperty("leftMargin", leftMargin); + encoder.addProperty("topMargin", topMargin); + encoder.addProperty("rightMargin", rightMargin); + encoder.addProperty("bottomMargin", bottomMargin); + encoder.addProperty("startMargin", startMargin); + encoder.addProperty("endMargin", endMargin); + } } /* Describes a touched view and the ids of the pointers that it has captured. @@ -7665,4 +7690,23 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager canvas.drawLines(sDebugLines, paint); } + + /** @hide */ + @Override + protected void encodeProperties(@NonNull ViewHierarchyEncoder encoder) { + super.encodeProperties(encoder); + + encoder.addProperty("focus:descendantFocusability", getDescendantFocusability()); + encoder.addProperty("drawing:clipChildren", getClipChildren()); + encoder.addProperty("drawing:clipToPadding", getClipToPadding()); + encoder.addProperty("drawing:childrenDrawingOrderEnabled", isChildrenDrawingOrderEnabled()); + encoder.addProperty("drawing:persistentDrawingCache", getPersistentDrawingCache()); + + int n = getChildCount(); + encoder.addProperty("meta:__childCount__", (short)n); + for (int i = 0; i < n; i++) { + encoder.addPropertyKey("meta:__child__" + i); + getChildAt(i).encode(encoder); + } + } } diff --git a/core/java/android/view/ViewHierarchyEncoder.java b/core/java/android/view/ViewHierarchyEncoder.java new file mode 100644 index 0000000..8770216 --- /dev/null +++ b/core/java/android/view/ViewHierarchyEncoder.java @@ -0,0 +1,201 @@ +package android.view; + +import android.annotation.NonNull; +import android.annotation.Nullable; + +import java.io.ByteArrayOutputStream; +import java.io.DataOutputStream; +import java.io.IOException; +import java.nio.charset.Charset; +import java.util.HashMap; +import java.util.Map; + +/** + * {@link ViewHierarchyEncoder} is a serializer that is tailored towards writing out + * view hierarchies (the view tree, along with the properties for each view) to a stream. + * + * It is typically used as follows: + * <pre> + * ViewHierarchyEncoder e = new ViewHierarchyEncoder(); + * + * for (View view : views) { + * e.beginObject(view); + * e.addProperty("prop1", value); + * ... + * e.endObject(); + * } + * + * // repeat above snippet for each view, finally end with: + * e.endStream(); + * </pre> + * + * <p>On the stream, a snippet such as the above gets encoded as a series of Map's (one + * corresponding to each view) with the property name as the key and the property value + * as the value. + * + * <p>Since the property names are practically the same across all views, rather than using + * the property name directly as the key, we use a short integer id corresponding to each + * property name as the key. A final map is added at the end which contains the mapping + * from the integer to its property name. + * + * <p>A value is encoded as a single byte type identifier followed by the encoding of the + * value. Only primitive types are supported as values, in addition to the Map type. + * + * @hide + */ +public class ViewHierarchyEncoder { + // Prefixes for simple primitives. These match the JNI definitions. + private static final byte SIG_BOOLEAN = 'Z'; + private static final byte SIG_BYTE = 'B'; + private static final byte SIG_SHORT = 'S'; + private static final byte SIG_INT = 'I'; + private static final byte SIG_LONG = 'J'; + private static final byte SIG_FLOAT = 'F'; + private static final byte SIG_DOUBLE = 'D'; + + // Prefixes for some commonly used objects + private static final byte SIG_STRING = 'R'; + + private static final byte SIG_MAP = 'M'; // a map with an short key + private static final short SIG_END_MAP = 0; + + private final DataOutputStream mStream; + + private final Map<String,Short> mPropertyNames = new HashMap<String, Short>(200); + private short mPropertyId = 1; + private Charset mCharset = Charset.forName("utf-8"); + + public ViewHierarchyEncoder(@NonNull ByteArrayOutputStream stream) { + mStream = new DataOutputStream(stream); + } + + public void beginObject(@NonNull Object o) { + startPropertyMap(); + addProperty("meta:__name__", o.getClass().getName()); + addProperty("meta:__hash__", o.hashCode()); + } + + public void endObject() { + endPropertyMap(); + } + + public void endStream() { + // write out the string table + startPropertyMap(); + addProperty("__name__", "propertyIndex"); + for (Map.Entry<String,Short> entry : mPropertyNames.entrySet()) { + writeShort(entry.getValue()); + writeString(entry.getKey()); + } + endPropertyMap(); + } + + public void addProperty(@NonNull String name, boolean v) { + writeShort(createPropertyIndex(name)); + writeBoolean(v); + } + + public void addProperty(@NonNull String name, short s) { + writeShort(createPropertyIndex(name)); + writeShort(s); + } + + public void addProperty(@NonNull String name, int v) { + writeShort(createPropertyIndex(name)); + writeInt(v); + } + + public void addProperty(@NonNull String name, float v) { + writeShort(createPropertyIndex(name)); + writeFloat(v); + } + + public void addProperty(@NonNull String name, @Nullable String s) { + writeShort(createPropertyIndex(name)); + writeString(s); + } + + /** + * Writes the given name as the property name, and leaves it to the callee + * to fill in value for this property. + */ + public void addPropertyKey(@NonNull String name) { + writeShort(createPropertyIndex(name)); + } + + private short createPropertyIndex(@NonNull String name) { + Short index = mPropertyNames.get(name); + if (index == null) { + index = mPropertyId++; + mPropertyNames.put(name, index); + } + + return index; + } + + private void startPropertyMap() { + try { + mStream.write(SIG_MAP); + } catch (IOException e) { + // does not happen since the stream simply wraps a ByteArrayOutputStream + } + } + + private void endPropertyMap() { + writeShort(SIG_END_MAP); + } + + private void writeBoolean(boolean v) { + try { + mStream.write(SIG_BOOLEAN); + mStream.write(v ? 1 : 0); + } catch (IOException e) { + // does not happen since the stream simply wraps a ByteArrayOutputStream + } + } + + private void writeShort(short s) { + try { + mStream.write(SIG_SHORT); + mStream.writeShort(s); + } catch (IOException e) { + // does not happen since the stream simply wraps a ByteArrayOutputStream + } + } + + private void writeInt(int i) { + try { + mStream.write(SIG_INT); + mStream.writeInt(i); + } catch (IOException e) { + // does not happen since the stream simply wraps a ByteArrayOutputStream + } + } + + private void writeFloat(float v) { + try { + mStream.write(SIG_FLOAT); + mStream.writeFloat(v); + } catch (IOException e) { + // does not happen since the stream simply wraps a ByteArrayOutputStream + } + } + + private void writeString(@Nullable String s) { + if (s == null) { + s = ""; + } + + try { + mStream.write(SIG_STRING); + byte[] bytes = s.getBytes(mCharset); + + short len = (short)Math.min(bytes.length, Short.MAX_VALUE); + mStream.writeShort(len); + + mStream.write(bytes, 0, len); + } catch (IOException e) { + // does not happen since the stream simply wraps a ByteArrayOutputStream + } + } +} diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java index fda6e63..ea1dadb 100644 --- a/core/java/android/view/ViewRootImpl.java +++ b/core/java/android/view/ViewRootImpl.java @@ -76,6 +76,7 @@ import android.widget.Scroller; import com.android.internal.R; import com.android.internal.os.SomeArgs; +import com.android.internal.policy.PhoneFallbackEventHandler; import com.android.internal.util.ScreenShapeHelper; import com.android.internal.view.BaseSurfaceHolder; import com.android.internal.view.RootViewSurfaceTaker; @@ -653,6 +654,10 @@ public final class ViewRootImpl implements ViewParent, return (mWindowAttributes.flags & WindowManager.LayoutParams.FLAG_LOCAL_FOCUS_MODE) != 0; } + public int getWindowFlags() { + return mWindowAttributes.flags; + } + public CharSequence getTitle() { return mWindowAttributes.getTitle(); } diff --git a/core/java/android/view/ViewStructure.java b/core/java/android/view/ViewStructure.java new file mode 100644 index 0000000..8dc49ac --- /dev/null +++ b/core/java/android/view/ViewStructure.java @@ -0,0 +1,86 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.view; + +import android.graphics.Rect; +import android.os.Bundle; +import android.text.TextPaint; + +/** + * Container for storing additional per-view data generated by {@link View#onProvideStructure + * View.onProvideStructure}. + */ +public abstract class ViewStructure { + public abstract void setId(int id, String packageName, String typeName, String entryName); + + public abstract void setDimens(int left, int top, int scrollX, int scrollY, int width, + int height); + + public abstract void setVisibility(int visibility); + + /** @hide */ + public abstract void setAssistBlocked(boolean state); + + public abstract void setEnabled(boolean state); + + public abstract void setClickable(boolean state); + + public abstract void setLongClickable(boolean state); + + public abstract void setStylusButtonPressable(boolean state); + + public abstract void setFocusable(boolean state); + + public abstract void setFocused(boolean state); + + public abstract void setAccessibilityFocused(boolean state); + + public abstract void setCheckable(boolean state); + + public abstract void setChecked(boolean state); + + public abstract void setSelected(boolean state); + + public abstract void setActivated(boolean state); + + public abstract void setClassName(String className); + + public abstract void setContentDescription(CharSequence contentDescription); + + public abstract void setText(CharSequence text); + public abstract void setText(CharSequence text, int selectionStart, int selectionEnd); + public abstract void setTextPaint(TextPaint paint); + public abstract void setHint(CharSequence hint); + + public abstract CharSequence getText(); + public abstract int getTextSelectionStart(); + public abstract int getTextSelectionEnd(); + public abstract CharSequence getHint(); + + public abstract Bundle getExtras(); + public abstract boolean hasExtras(); + + public abstract void setChildCount(int num); + public abstract int getChildCount(); + public abstract ViewAssistStructure newChild(int index); + + public abstract ViewAssistStructure asyncNewChild(int index); + public abstract void asyncCommit(); + + /** @hide */ + public abstract Rect getTempRect(); +} diff --git a/core/java/android/view/WindowManager.java b/core/java/android/view/WindowManager.java index e983910..2797b6e 100644 --- a/core/java/android/view/WindowManager.java +++ b/core/java/android/view/WindowManager.java @@ -16,11 +16,11 @@ package android.view; +import android.annotation.NonNull; import android.annotation.SystemApi; import android.app.Presentation; import android.content.Context; import android.content.pm.ActivityInfo; -import android.graphics.Insets; import android.graphics.PixelFormat; import android.graphics.Rect; import android.os.IBinder; @@ -1119,6 +1119,15 @@ public interface WindowManager extends ViewManager { public static final int PRIVATE_FLAG_DISABLE_WALLPAPER_TOUCH_EVENTS = 0x00000800; /** + * Flag to force the status bar window to be visible all the time. If the bar is hidden when + * this flag is set it will be shown again and the bar will have a transparent background. + * This can only be set by {@link LayoutParams#TYPE_STATUS_BAR}. + * + * {@hide} + */ + public static final int PRIVATE_FLAG_FORCE_STATUS_BAR_VISIBLE_TRANSPARENT = 0x00001000; + + /** * Control flags that are private to the platform. * @hide */ @@ -2066,5 +2075,18 @@ public interface WindowManager extends ViewManager { } private CharSequence mTitle = ""; + + /** @hide */ + @Override + protected void encodeProperties(@NonNull ViewHierarchyEncoder encoder) { + super.encodeProperties(encoder); + + encoder.addProperty("x", x); + encoder.addProperty("y", y); + encoder.addProperty("horizontalWeight", horizontalWeight); + encoder.addProperty("verticalWeight", verticalWeight); + encoder.addProperty("type", type); + encoder.addProperty("flags", flags); + } } } diff --git a/core/java/android/view/inputmethod/InputMethodManager.java b/core/java/android/view/inputmethod/InputMethodManager.java index 78604bf..040fd37 100644 --- a/core/java/android/view/inputmethod/InputMethodManager.java +++ b/core/java/android/view/inputmethod/InputMethodManager.java @@ -247,6 +247,13 @@ public final class InputMethodManager { /** @hide */ public static final int DISPATCH_HANDLED = 1; + /** @hide */ + public static final int SHOW_IM_PICKER_MODE_AUTO = 0; + /** @hide */ + public static final int SHOW_IM_PICKER_MODE_INCLUDE_AUXILIARY_SUBTYPES = 1; + /** @hide */ + public static final int SHOW_IM_PICKER_MODE_EXCLUDE_AUXILIARY_SUBTYPES = 2; + final IInputMethodManager mService; final Looper mMainLooper; @@ -1890,9 +1897,28 @@ public final class InputMethodManager { } } + /** + * Shows the input method chooser dialog. + * + * @param showAuxiliarySubtypes Set true to show auxiliary input methods. + * @hide + */ + public void showInputMethodPicker(boolean showAuxiliarySubtypes) { + synchronized (mH) { + try { + final int mode = showAuxiliarySubtypes ? + SHOW_IM_PICKER_MODE_INCLUDE_AUXILIARY_SUBTYPES: + SHOW_IM_PICKER_MODE_EXCLUDE_AUXILIARY_SUBTYPES; + mService.showInputMethodPickerFromClient(mClient, mode); + } catch (RemoteException e) { + Log.w(TAG, "IME died: " + mCurId, e); + } + } + } + private void showInputMethodPickerLocked() { try { - mService.showInputMethodPickerFromClient(mClient); + mService.showInputMethodPickerFromClient(mClient, SHOW_IM_PICKER_MODE_AUTO); } catch (RemoteException e) { Log.w(TAG, "IME died: " + mCurId, e); } diff --git a/core/java/android/webkit/WebView.java b/core/java/android/webkit/WebView.java index 7ab5aaa..e27e253 100644 --- a/core/java/android/webkit/WebView.java +++ b/core/java/android/webkit/WebView.java @@ -16,6 +16,7 @@ package android.webkit; +import android.annotation.NonNull; import android.annotation.SystemApi; import android.annotation.Widget; import android.content.Context; @@ -40,9 +41,10 @@ import android.util.Log; import android.view.KeyEvent; import android.view.MotionEvent; import android.view.View; -import android.view.ViewAssistStructure; +import android.view.ViewStructure; import android.view.ViewDebug; import android.view.ViewGroup; +import android.view.ViewHierarchyEncoder; import android.view.ViewTreeObserver; import android.view.accessibility.AccessibilityEvent; import android.view.accessibility.AccessibilityNodeInfo; @@ -2426,7 +2428,7 @@ public class WebView extends AbsoluteLayout } @Override - public void onProvideVirtualAssistStructure(ViewAssistStructure structure) { + public void onProvideVirtualStructure(ViewStructure structure) { mProvider.getViewDelegate().onProvideVirtualAssistStructure(structure); } @@ -2576,4 +2578,18 @@ public class WebView extends AbsoluteLayout super.onFinishTemporaryDetach(); mProvider.getViewDelegate().onFinishTemporaryDetach(); } + + /** @hide */ + @Override + protected void encodeProperties(@NonNull ViewHierarchyEncoder encoder) { + super.encodeProperties(encoder); + + checkThread(); + encoder.addProperty("webview:contentHeight", mProvider.getContentHeight()); + encoder.addProperty("webview:contentWidth", mProvider.getContentWidth()); + encoder.addProperty("webview:scale", mProvider.getScale()); + encoder.addProperty("webview:title", mProvider.getTitle()); + encoder.addProperty("webview:url", mProvider.getUrl()); + encoder.addProperty("webview:originalUrl", mProvider.getOriginalUrl()); + } } diff --git a/core/java/android/webkit/WebViewProvider.java b/core/java/android/webkit/WebViewProvider.java index d5787de..e367192 100644 --- a/core/java/android/webkit/WebViewProvider.java +++ b/core/java/android/webkit/WebViewProvider.java @@ -32,7 +32,7 @@ import android.print.PrintDocumentAdapter; import android.view.KeyEvent; import android.view.MotionEvent; import android.view.View; -import android.view.ViewAssistStructure; +import android.view.ViewStructure; import android.view.ViewGroup.LayoutParams; import android.view.accessibility.AccessibilityEvent; import android.view.accessibility.AccessibilityNodeInfo; @@ -299,7 +299,7 @@ public interface WebViewProvider { interface ViewDelegate { public boolean shouldDelayChildPressedState(); - public void onProvideVirtualAssistStructure(ViewAssistStructure structure); + public void onProvideVirtualAssistStructure(ViewStructure structure); public AccessibilityNodeProvider getAccessibilityNodeProvider(); diff --git a/core/java/android/widget/AbsListView.java b/core/java/android/widget/AbsListView.java index c57a53a..9903b7e 100644 --- a/core/java/android/widget/AbsListView.java +++ b/core/java/android/widget/AbsListView.java @@ -18,6 +18,7 @@ package android.widget; import android.annotation.ColorInt; import android.annotation.DrawableRes; +import android.annotation.NonNull; import android.content.Context; import android.content.Intent; import android.content.res.TypedArray; @@ -56,6 +57,7 @@ import android.view.View; import android.view.ViewConfiguration; import android.view.ViewDebug; import android.view.ViewGroup; +import android.view.ViewHierarchyEncoder; import android.view.ViewParent; import android.view.ViewTreeObserver; import android.view.accessibility.AccessibilityEvent; @@ -6330,6 +6332,16 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te public LayoutParams(ViewGroup.LayoutParams source) { super(source); } + + /** @hide */ + @Override + protected void encodeProperties(@NonNull ViewHierarchyEncoder encoder) { + super.encodeProperties(encoder); + + encoder.addProperty("list:viewType", viewType); + encoder.addProperty("list:recycledHeaderFooter", recycledHeaderFooter); + encoder.addProperty("list:forceAdd", forceAdd); + } } /** @@ -6912,6 +6924,25 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te } } + /** @hide */ + @Override + protected void encodeProperties(@NonNull ViewHierarchyEncoder encoder) { + super.encodeProperties(encoder); + + encoder.addProperty("drawing:cacheColorHint", getCacheColorHint()); + encoder.addProperty("list:fastScrollEnabled", isFastScrollEnabled()); + encoder.addProperty("list:scrollingCacheEnabled", isScrollingCacheEnabled()); + encoder.addProperty("list:smoothScrollbarEnabled", isSmoothScrollbarEnabled()); + encoder.addProperty("list:stackFromBottom", isStackFromBottom()); + encoder.addProperty("list:textFilterEnabled", isTextFilterEnabled()); + + View selectedView = getSelectedView(); + if (selectedView != null) { + encoder.addPropertyKey("selectedView"); + selectedView.encode(encoder); + } + } + /** * Abstract positon scroller used to handle smooth scrolling. */ diff --git a/core/java/android/widget/ActionMenuPresenter.java b/core/java/android/widget/ActionMenuPresenter.java index e0b0e1f..f08141c 100644 --- a/core/java/android/widget/ActionMenuPresenter.java +++ b/core/java/android/widget/ActionMenuPresenter.java @@ -61,6 +61,7 @@ public class ActionMenuPresenter extends BaseMenuPresenter implements ActionProvider.SubUiVisibilityListener { private static final String TAG = "ActionMenuPresenter"; private static final int ITEM_ANIMATION_DURATION = 150; + private static final boolean ACTIONBAR_ANIMATIONS_ENABLED = false; private OverflowMenuButton mOverflowButton; private boolean mReserveOverflow; @@ -414,7 +415,7 @@ public class ActionMenuPresenter extends BaseMenuPresenter @Override public void updateMenuView(boolean cleared) { final ViewGroup menuViewParent = (ViewGroup) ((View) mMenuView).getParent(); - if (menuViewParent != null) { + if (menuViewParent != null && ACTIONBAR_ANIMATIONS_ENABLED) { setupItemAnimations(); } super.updateMenuView(cleared); diff --git a/core/java/android/widget/ActionMenuView.java b/core/java/android/widget/ActionMenuView.java index d6f2276..278a8fb 100644 --- a/core/java/android/widget/ActionMenuView.java +++ b/core/java/android/widget/ActionMenuView.java @@ -15,6 +15,7 @@ */ package android.widget; +import android.annotation.NonNull; import android.annotation.StyleRes; import android.content.Context; import android.content.res.ColorStateList; @@ -28,6 +29,7 @@ import android.view.MenuItem; import android.view.View; import android.view.ViewDebug; import android.view.ViewGroup; +import android.view.ViewHierarchyEncoder; import android.view.accessibility.AccessibilityEvent; import com.android.internal.view.menu.ActionMenuItemView; import com.android.internal.view.menu.MenuBuilder; @@ -835,5 +837,17 @@ public class ActionMenuView extends LinearLayout implements MenuBuilder.ItemInvo super(width, height); this.isOverflowButton = isOverflowButton; } + + /** @hide */ + @Override + protected void encodeProperties(@NonNull ViewHierarchyEncoder encoder) { + super.encodeProperties(encoder); + + encoder.addProperty("layout:overFlowButton", isOverflowButton); + encoder.addProperty("layout:cellsUsed", cellsUsed); + encoder.addProperty("layout:extraPixels", extraPixels); + encoder.addProperty("layout:expandable", expandable); + encoder.addProperty("layout:preventEdgeOffset", preventEdgeOffset); + } } } diff --git a/core/java/android/widget/AdapterView.java b/core/java/android/widget/AdapterView.java index 72cb0b5..54e3996 100644 --- a/core/java/android/widget/AdapterView.java +++ b/core/java/android/widget/AdapterView.java @@ -16,6 +16,7 @@ package android.widget; +import android.annotation.NonNull; import android.annotation.Nullable; import android.content.Context; import android.database.DataSetObserver; @@ -29,6 +30,7 @@ import android.view.SoundEffectConstants; import android.view.View; import android.view.ViewDebug; import android.view.ViewGroup; +import android.view.ViewHierarchyEncoder; import android.view.accessibility.AccessibilityEvent; import android.view.accessibility.AccessibilityManager; import android.view.accessibility.AccessibilityNodeInfo; @@ -1245,4 +1247,16 @@ public abstract class AdapterView<T extends Adapter> extends ViewGroup { } } } + + /** @hide */ + @Override + protected void encodeProperties(@NonNull ViewHierarchyEncoder encoder) { + super.encodeProperties(encoder); + + encoder.addProperty("scrolling:firstPosition", mFirstPosition); + encoder.addProperty("list:nextSelectedPosition", mNextSelectedPosition); + encoder.addProperty("list:nextSelectedRowId", mNextSelectedRowId); + encoder.addProperty("list:selectedPosition", mSelectedPosition); + encoder.addProperty("list:itemCount", mItemCount); + } } diff --git a/core/java/android/widget/CheckedTextView.java b/core/java/android/widget/CheckedTextView.java index 22e079c..6b4b2c7 100644 --- a/core/java/android/widget/CheckedTextView.java +++ b/core/java/android/widget/CheckedTextView.java @@ -16,6 +16,8 @@ package android.widget; +import android.annotation.NonNull; +import android.view.ViewHierarchyEncoder; import com.android.internal.R; import android.annotation.DrawableRes; @@ -459,4 +461,11 @@ public class CheckedTextView extends TextView implements Checkable { info.setCheckable(true); info.setChecked(mChecked); } + + /** @hide */ + @Override + protected void encodeProperties(@NonNull ViewHierarchyEncoder stream) { + super.encodeProperties(stream); + stream.addProperty("text:checked", isChecked()); + } } diff --git a/core/java/android/widget/CompoundButton.java b/core/java/android/widget/CompoundButton.java index f2afeeb..770077d 100644 --- a/core/java/android/widget/CompoundButton.java +++ b/core/java/android/widget/CompoundButton.java @@ -17,8 +17,10 @@ package android.widget; import android.annotation.DrawableRes; +import android.annotation.NonNull; import android.annotation.Nullable; import android.graphics.PorterDuff; +import android.view.ViewHierarchyEncoder; import com.android.internal.R; import android.content.Context; @@ -530,9 +532,16 @@ public abstract class CompoundButton extends Button implements Checkable { @Override public void onRestoreInstanceState(Parcelable state) { SavedState ss = (SavedState) state; - + super.onRestoreInstanceState(ss.getSuperState()); setChecked(ss.checked); requestLayout(); } + + /** @hide */ + @Override + protected void encodeProperties(@NonNull ViewHierarchyEncoder stream) { + super.encodeProperties(stream); + stream.addProperty("checked", isChecked()); + } } diff --git a/core/java/android/widget/Editor.java b/core/java/android/widget/Editor.java index 712fdba..78c418b 100644 --- a/core/java/android/widget/Editor.java +++ b/core/java/android/widget/Editor.java @@ -688,44 +688,101 @@ public class Editor { private int getWordStart(int offset) { // FIXME - For this and similar methods we're not doing anything to check if there's // a LocaleSpan in the text, this may be something we should try handling or checking for. - int retOffset = getWordIteratorWithText().getBeginning(offset); - if (retOffset == BreakIterator.DONE) retOffset = offset; + int retOffset = getWordIteratorWithText().prevBoundary(offset); + if (isPunctBoundaryBehind(retOffset, true /* isStart */)) { + // If we're on a punctuation boundary we should continue to get the + // previous offset until we're not longer on a punctuation boundary. + retOffset = getWordIteratorWithText().prevBoundary(retOffset); + while (!isPunctBoundaryBehind(retOffset, false /* isStart */) + && retOffset != BreakIterator.DONE) { + retOffset = getWordIteratorWithText().prevBoundary(retOffset); + } + } + if (retOffset == BreakIterator.DONE) { + return offset; + } return retOffset; } - private int getWordEnd(int offset, boolean includePunctuation) { - int retOffset = getWordIteratorWithText().getEnd(offset); + private int getWordEnd(int offset) { + int retOffset = getWordIteratorWithText().nextBoundary(offset); + if (isPunctBoundaryForward(retOffset, true /* isStart */)) { + // If we're on a punctuation boundary we should continue to get the + // next offset until we're no longer on a punctuation boundary. + retOffset = getWordIteratorWithText().nextBoundary(retOffset); + while (!isPunctBoundaryForward(retOffset, false /* isStart */) + && retOffset != BreakIterator.DONE) { + retOffset = getWordIteratorWithText().nextBoundary(retOffset); + } + } if (retOffset == BreakIterator.DONE) { - retOffset = offset; - } else if (includePunctuation) { - retOffset = handlePunctuation(retOffset); + return offset; } return retOffset; } - private boolean isEndBoundary(int offset) { - int thisEnd = getWordEnd(offset, false); - return offset == thisEnd; - } + /** + * Checks for punctuation boundaries for the provided offset and the + * previous character. + * + * @param offset The offset to check from. + * @param isStart Whether the boundary being checked for is at the start or + * end of a punctuation sequence. + * @return Whether this is a punctuation boundary. + */ + private boolean isPunctBoundaryBehind(int offset, boolean isStart) { + CharSequence text = mTextView.getText(); + if (offset == BreakIterator.DONE || offset > text.length() || offset == 0) { + return false; + } + int cp = Character.codePointAt(text, offset); + int prevCp = Character.codePointBefore(text, offset); - private boolean isStartBoundary(int offset) { - int thisStart = getWordStart(offset); - return thisStart == offset; + if (isPunctuation(cp)) { + // If it's the start, the current cp and the prev cp are + // punctuation. If it's at the end of a punctuation sequence the + // current is punctuation and the prev is not. + return isStart ? isPunctuation(prevCp) : !isPunctuation(prevCp); + } + return false; } - private int handlePunctuation(int offset) { - // FIXME - Check with UX how repeated ending punctuation should be handled. - // FIXME - Check with UX if / how we would handle non sentence ending characters. - // FIXME - Consider punctuation in different languages. + /** + * Checks for punctuation boundaries for the provided offset and the next + * character. + * + * @param offset The offset to check from. + * @param isStart Whether the boundary being checked for is at the start or + * end of a punctuation sequence. + * @return Whether this is a punctuation boundary. + */ + private boolean isPunctBoundaryForward(int offset, boolean isStart) { CharSequence text = mTextView.getText(); - if (offset < text.length()) { - int c = Character.codePointAt(text, offset); - if (c == 0x002e /* period */|| c == 0x003f /* question mark */ - || c == 0x0021 /* exclamation mark */) { - offset = Character.offsetByCodePoints(text, offset, 1); - } + if (offset == BreakIterator.DONE || offset > text.length() || offset == 0) { + return false; + } + int cp = Character.codePointBefore(text, offset); + int nextCpOffset = Math.min(offset + Character.charCount(cp), text.length() - 1); + int nextCp = Character.codePointBefore(text, nextCpOffset); + + if (isPunctuation(cp)) { + // If it's the start, the current cp and the next cp are + // punctuation. If it's at the end of a punctuation sequence the + // current is punctuation and the next is not. + return isStart ? isPunctuation(nextCp) : !isPunctuation(nextCp); } - return offset; + return false; + } + + private boolean isPunctuation(int cp) { + int type = Character.getType(cp); + return (type == Character.CONNECTOR_PUNCTUATION || + type == Character.DASH_PUNCTUATION || + type == Character.END_PUNCTUATION || + type == Character.FINAL_QUOTE_PUNCTUATION || + type == Character.INITIAL_QUOTE_PUNCTUATION || + type == Character.OTHER_PUNCTUATION || + type == Character.START_PUNCTUATION); } /** @@ -788,7 +845,7 @@ public class Editor { if (selectionStart == BreakIterator.DONE || selectionEnd == BreakIterator.DONE || selectionStart == selectionEnd) { // Possible when the word iterator does not properly handle the text's language - long range = getCharRange(minOffset); + long range = getCharClusterRange(minOffset); selectionStart = TextUtils.unpackRangeStartFromLong(range); selectionEnd = TextUtils.unpackRangeEndFromLong(range); } @@ -831,29 +888,25 @@ public class Editor { return mWordIteratorWithText; } - private long getCharRange(int offset) { + private int getNextCursorOffset(int offset, boolean findAfterGivenOffset) { + final Layout layout = mTextView.getLayout(); + if (layout == null) return offset; + final CharSequence text = mTextView.getText(); + final int nextOffset = layout.getPaint().getTextRunCursor(text, 0, text.length(), + layout.isRtlCharAt(offset) ? Paint.DIRECTION_RTL : Paint.DIRECTION_LTR, + offset, findAfterGivenOffset ? Paint.CURSOR_AFTER : Paint.CURSOR_BEFORE); + return nextOffset == -1 ? offset : nextOffset; + } + + private long getCharClusterRange(int offset) { final int textLength = mTextView.getText().length(); - if (offset + 1 < textLength) { - final char currentChar = mTextView.getText().charAt(offset); - final char nextChar = mTextView.getText().charAt(offset + 1); - if (Character.isSurrogatePair(currentChar, nextChar)) { - return TextUtils.packRangeInLong(offset, offset + 2); - } - } if (offset < textLength) { - return TextUtils.packRangeInLong(offset, offset + 1); - } - if (offset - 2 >= 0) { - final char previousChar = mTextView.getText().charAt(offset - 1); - final char previousPreviousChar = mTextView.getText().charAt(offset - 2); - if (Character.isSurrogatePair(previousPreviousChar, previousChar)) { - return TextUtils.packRangeInLong(offset - 2, offset); - } + return TextUtils.packRangeInLong(offset, getNextCursorOffset(offset, true)); } if (offset - 1 >= 0) { - return TextUtils.packRangeInLong(offset - 1, offset); + return TextUtils.packRangeInLong(getNextCursorOffset(offset, false), offset); } - return TextUtils.packRangeInLong(offset, offset); + return TextUtils.packRangeInLong(offset, offset); } private boolean touchPositionIsInSelection() { @@ -3903,13 +3956,9 @@ public class Editor { public void updatePosition(float x, float y) { final int trueOffset = mTextView.getOffsetForPosition(x, y); final int currLine = mTextView.getLineAtCoordinate(y); - - // Don't select white space on different lines. - if (isWhitespaceLine(mPrevLine, currLine, trueOffset)) return; - boolean positionCursor = false; int offset = trueOffset; - int end = getWordEnd(offset, true); + int end = getWordEnd(offset); int start = getWordStart(offset); if (offset < mPreviousOffset) { @@ -3925,16 +3974,16 @@ public class Editor { } } mTouchWordOffset = Math.max(trueOffset - offset, 0); - mInWord = !isStartBoundary(offset); positionCursor = true; - } else if (offset - mTouchWordOffset > mPreviousOffset) { + } else if (offset - mTouchWordOffset > mPreviousOffset || currLine > mPrevLine) { // User is shrinking the selection. if (currLine > mPrevLine) { // We're on a different line, so we'll snap to word boundaries. - offset = end; + offset = start; + mTouchWordOffset = Math.max(trueOffset - offset, 0); + } else { + offset -= mTouchWordOffset; } - offset -= mTouchWordOffset; - mInWord = !isEndBoundary(offset); positionCursor = true; } @@ -3946,11 +3995,13 @@ public class Editor { int alteredOffset = mTextView.getOffsetAtCoordinate(mPrevLine, x); if (alteredOffset >= selectionEnd) { // Can't pass the other drag handle. - offset = Math.max(0, selectionEnd - 1); + offset = getNextCursorOffset(selectionEnd, false); } else { offset = alteredOffset; } + mTouchWordOffset = 0; } + mInWord = !getWordIteratorWithText().isBoundary(offset); positionAtCursorOffset(offset, false); } } @@ -4005,14 +4056,9 @@ public class Editor { public void updatePosition(float x, float y) { final int trueOffset = mTextView.getOffsetForPosition(x, y); final int currLine = mTextView.getLineAtCoordinate(y); - - // Don't select white space on different lines. - if (isWhitespaceLine(mPrevLine, currLine, trueOffset)) return; - int offset = trueOffset; boolean positionCursor = false; - - int end = getWordEnd(offset, true); + int end = getWordEnd(offset); int start = getWordStart(offset); if (offset > mPreviousOffset) { @@ -4028,17 +4074,17 @@ public class Editor { } } mTouchWordOffset = Math.max(offset - trueOffset, 0); - mInWord = !isEndBoundary(offset); positionCursor = true; - } else if (offset + mTouchWordOffset < mPreviousOffset) { + } else if (offset + mTouchWordOffset < mPreviousOffset || currLine < mPrevLine) { // User is shrinking the selection. if (currLine < mPrevLine) { // We're on a different line, so we'll snap to word boundaries. - offset = start; + offset = end; + mTouchWordOffset = Math.max(offset - trueOffset, 0); + } else { + offset += mTouchWordOffset; } - offset += mTouchWordOffset; positionCursor = true; - mInWord = !isStartBoundary(offset); } if (positionCursor) { @@ -4049,11 +4095,13 @@ public class Editor { int length = mTextView.getText().length(); if (alteredOffset <= selectionStart) { // Can't pass the other drag handle. - offset = Math.min(selectionStart + 1, length); + offset = getNextCursorOffset(selectionStart, true); } else { offset = Math.min(alteredOffset, length); } + mTouchWordOffset = 0; } + mInWord = !getWordIteratorWithText().isBoundary(offset); positionAtCursorOffset(offset, false); } } @@ -4070,36 +4118,6 @@ public class Editor { } /** - * Checks whether selection is happening on a different line than previous and - * if that line only contains whitespace up to the touch location. - * - * @param prevLine The previous line the selection was on. - * @param currLine The current line being selected. - * @param offset The offset in the text where the touch occurred. - * @return Whether or not it was just a white space line being selected. - */ - private boolean isWhitespaceLine(int prevLine, int currLine, int offset) { - if (prevLine == currLine) { - // Same line; don't care. - return false; - } - CharSequence text = mTextView.getText(); - if (offset == text.length()) { - // No character at the last position. - return false; - } - int lineEndOffset = mTextView.getLayout().getLineEnd(currLine); - for (int cp, i = offset; i < lineEndOffset; i += Character.charCount(cp)) { - cp = Character.codePointAt(text, i); - if (!Character.isSpaceChar(cp) && !Character.isWhitespace(cp)) { - // There are non white space chars on the line. - return false; - } - } - return true; - } - - /** * A CursorController instance can be used to control a cursor in the text. */ private interface CursorController extends ViewTreeObserver.OnTouchModeChangeListener { @@ -4178,8 +4196,6 @@ public class Editor { private int mStartOffset = -1; // Indicates whether the user is selecting text and using the drag accelerator. private boolean mDragAcceleratorActive; - // Indicates the line of text the drag accelerator is on. - private int mPrevLine = -1; SelectionModifierCursorController() { resetTouchOffsets(); @@ -4272,8 +4288,6 @@ public class Editor { } } - // New selection, reset line. - mPrevLine = mTextView.getLineAtCoordinate(y); mDownPositionX = x; mDownPositionY = y; mGestureStayedInTapRegion = true; @@ -4318,7 +4332,7 @@ public class Editor { // We don't start "dragging" until the user is past the initial word that // gets selected on long press. int firstWordStart = getWordStart(mStartOffset); - int firstWordEnd = getWordEnd(mStartOffset, false); + int firstWordEnd = getWordEnd(mStartOffset); if (offset > firstWordEnd || offset < firstWordStart) { // Basically the goal in the below code is to have the highlight be @@ -4330,13 +4344,6 @@ public class Editor { if (my > fingerOffset) my -= fingerOffset; offset = mTextView.getOffsetForPosition(mx, my); - int currLine = mTextView.getLineAtCoordinate(my); - - // Don't select white space on different lines. - if (isWhitespaceLine(mPrevLine, currLine, offset)) return; - - mPrevLine = currLine; - // Perform the check for closeness at edge of view, if we're very close // don't adjust the offset to be in front of the finger - otherwise the // user can't select words at the edge. @@ -4365,7 +4372,7 @@ public class Editor { // Need to adjust start offset based on direction of movement. int newStart = mStartOffset < offset ? getWordStart(mStartOffset) - : getWordEnd(mStartOffset, true); + : getWordEnd(mStartOffset); Selection.setSelection((Spannable) mTextView.getText(), newStart, offset); } diff --git a/core/java/android/widget/FrameLayout.java b/core/java/android/widget/FrameLayout.java index 0602944..7ca450a 100644 --- a/core/java/android/widget/FrameLayout.java +++ b/core/java/android/widget/FrameLayout.java @@ -33,6 +33,7 @@ import android.view.Gravity; import android.view.View; import android.view.ViewDebug; import android.view.ViewGroup; +import android.view.ViewHierarchyEncoder; import android.widget.RemoteViews.RemoteView; import com.android.internal.R; @@ -407,6 +408,18 @@ public class FrameLayout extends ViewGroup { return FrameLayout.class.getName(); } + /** @hide */ + @Override + protected void encodeProperties(@NonNull ViewHierarchyEncoder encoder) { + super.encodeProperties(encoder); + + encoder.addProperty("measurement:measureAllChildren", mMeasureAllChildren); + encoder.addProperty("padding:foregroundPaddingLeft", mForegroundPaddingLeft); + encoder.addProperty("padding:foregroundPaddingTop", mForegroundPaddingTop); + encoder.addProperty("padding:foregroundPaddingRight", mForegroundPaddingRight); + encoder.addProperty("padding:foregroundPaddingBottom", mForegroundPaddingBottom); + } + /** * Per-child layout information for layouts that support margins. * See {@link android.R.styleable#FrameLayout_Layout FrameLayout Layout Attributes} diff --git a/core/java/android/widget/GridView.java b/core/java/android/widget/GridView.java index c959774..dcaafa5 100644 --- a/core/java/android/widget/GridView.java +++ b/core/java/android/widget/GridView.java @@ -17,6 +17,7 @@ package android.widget; import android.annotation.IntDef; +import android.annotation.NonNull; import android.content.Context; import android.content.Intent; import android.content.res.TypedArray; @@ -31,6 +32,7 @@ import android.view.SoundEffectConstants; import android.view.View; import android.view.ViewDebug; import android.view.ViewGroup; +import android.view.ViewHierarchyEncoder; import android.view.ViewRootImpl; import android.view.accessibility.AccessibilityNodeInfo; import android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction; @@ -2420,4 +2422,11 @@ public class GridView extends AbsListView { row, 1, column, 1, isHeading, isSelected); info.setCollectionItemInfo(itemInfo); } + + /** @hide */ + @Override + protected void encodeProperties(@NonNull ViewHierarchyEncoder encoder) { + super.encodeProperties(encoder); + encoder.addProperty("numColumns", getNumColumns()); + } } diff --git a/core/java/android/widget/HorizontalScrollView.java b/core/java/android/widget/HorizontalScrollView.java index 0879c5d..cf67905 100644 --- a/core/java/android/widget/HorizontalScrollView.java +++ b/core/java/android/widget/HorizontalScrollView.java @@ -16,6 +16,7 @@ package android.widget; +import android.annotation.NonNull; import android.content.Context; import android.content.res.TypedArray; import android.graphics.Canvas; @@ -35,6 +36,7 @@ import android.view.View; import android.view.ViewConfiguration; import android.view.ViewDebug; import android.view.ViewGroup; +import android.view.ViewHierarchyEncoder; import android.view.ViewParent; import android.view.accessibility.AccessibilityEvent; import android.view.accessibility.AccessibilityNodeInfo; @@ -1695,6 +1697,13 @@ public class HorizontalScrollView extends FrameLayout { return ss; } + /** @hide */ + @Override + protected void encodeProperties(@NonNull ViewHierarchyEncoder encoder) { + super.encodeProperties(encoder); + encoder.addProperty("layout:fillViewPort", mFillViewport); + } + static class SavedState extends BaseSavedState { public int scrollPosition; public boolean isLayoutRtl; diff --git a/core/java/android/widget/ImageView.java b/core/java/android/widget/ImageView.java index 6d2f368..05059bc 100644 --- a/core/java/android/widget/ImageView.java +++ b/core/java/android/widget/ImageView.java @@ -17,6 +17,7 @@ package android.widget; import android.annotation.DrawableRes; +import android.annotation.NonNull; import android.annotation.Nullable; import android.content.ContentResolver; import android.content.Context; @@ -43,6 +44,7 @@ import android.util.Log; import android.view.RemotableViewMethod; import android.view.View; import android.view.ViewDebug; +import android.view.ViewHierarchyEncoder; import android.view.accessibility.AccessibilityEvent; import android.widget.RemoteViews.RemoteView; @@ -1431,4 +1433,11 @@ public class ImageView extends View { public CharSequence getAccessibilityClassName() { return ImageView.class.getName(); } + + /** @hide */ + @Override + protected void encodeProperties(@NonNull ViewHierarchyEncoder stream) { + super.encodeProperties(stream); + stream.addProperty("layout:baseline", getBaseline()); + } } diff --git a/core/java/android/widget/LinearLayout.java b/core/java/android/widget/LinearLayout.java index 72f51c9..f153ce5 100644 --- a/core/java/android/widget/LinearLayout.java +++ b/core/java/android/widget/LinearLayout.java @@ -19,6 +19,7 @@ package android.widget; import com.android.internal.R; import android.annotation.IntDef; +import android.annotation.NonNull; import android.annotation.Nullable; import android.content.Context; import android.content.res.TypedArray; @@ -29,6 +30,7 @@ import android.view.Gravity; import android.view.View; import android.view.ViewDebug; import android.view.ViewGroup; +import android.view.ViewHierarchyEncoder; import android.widget.RemoteViews.RemoteView; import java.lang.annotation.Retention; @@ -1813,6 +1815,20 @@ public class LinearLayout extends ViewGroup { return LinearLayout.class.getName(); } + /** @hide */ + @Override + protected void encodeProperties(@NonNull ViewHierarchyEncoder encoder) { + super.encodeProperties(encoder); + encoder.addProperty("layout:baselineAligned", mBaselineAligned); + encoder.addProperty("layout:baselineAlignedChildIndex", mBaselineAlignedChildIndex); + encoder.addProperty("measurement:baselineChildTop", mBaselineChildTop); + encoder.addProperty("measurement:orientation", mOrientation); + encoder.addProperty("measurement:gravity", mGravity); + encoder.addProperty("measurement:totalLength", mTotalLength); + encoder.addProperty("layout:totalLength", mTotalLength); + encoder.addProperty("layout:useLargestChild", mUseLargestChild); + } + /** * Per-child layout information associated with ViewLinearLayout. * @@ -1921,5 +1937,14 @@ public class LinearLayout extends ViewGroup { return output + "LinearLayout.LayoutParams={width=" + sizeToString(width) + ", height=" + sizeToString(height) + " weight=" + weight + "}"; } + + /** @hide */ + @Override + protected void encodeProperties(@NonNull ViewHierarchyEncoder encoder) { + super.encodeProperties(encoder); + + encoder.addProperty("layout:weight", weight); + encoder.addProperty("layout:gravity", gravity); + } } } diff --git a/core/java/android/widget/ListPopupWindow.java b/core/java/android/widget/ListPopupWindow.java index 05866f0..94b9416 100644 --- a/core/java/android/widget/ListPopupWindow.java +++ b/core/java/android/widget/ListPopupWindow.java @@ -1791,8 +1791,9 @@ public class ListPopupWindow { private class ResizePopupRunnable implements Runnable { public void run() { - if (mDropDownList != null && mDropDownList.getCount() > mDropDownList.getChildCount() && - mDropDownList.getChildCount() <= mListItemExpandMaximum) { + if (mDropDownList != null && mDropDownList.isAttachedToWindow() + && mDropDownList.getCount() > mDropDownList.getChildCount() + && mDropDownList.getChildCount() <= mListItemExpandMaximum) { mPopup.setInputMethodMode(PopupWindow.INPUT_METHOD_NOT_NEEDED); show(); } diff --git a/core/java/android/widget/ListView.java b/core/java/android/widget/ListView.java index a79c8e8..7dcaa1f 100644 --- a/core/java/android/widget/ListView.java +++ b/core/java/android/widget/ListView.java @@ -23,6 +23,7 @@ import com.android.internal.util.Predicate; import com.google.android.collect.Lists; import android.annotation.IdRes; +import android.annotation.NonNull; import android.content.Context; import android.content.Intent; import android.content.res.TypedArray; @@ -40,6 +41,7 @@ import android.view.SoundEffectConstants; import android.view.View; import android.view.ViewDebug; import android.view.ViewGroup; +import android.view.ViewHierarchyEncoder; import android.view.ViewParent; import android.view.ViewRootImpl; import android.view.accessibility.AccessibilityNodeInfo; @@ -3938,4 +3940,12 @@ public class ListView extends AbsListView { position, 1, 0, 1, isHeading, isSelected); info.setCollectionItemInfo(itemInfo); } + + /** @hide */ + @Override + protected void encodeProperties(@NonNull ViewHierarchyEncoder encoder) { + super.encodeProperties(encoder); + + encoder.addProperty("recycleOnMeasure", recycleOnMeasure()); + } } diff --git a/core/java/android/widget/MediaController.java b/core/java/android/widget/MediaController.java index 8d8b3a3..97348e30 100644 --- a/core/java/android/widget/MediaController.java +++ b/core/java/android/widget/MediaController.java @@ -28,7 +28,7 @@ import android.view.Gravity; import android.view.KeyEvent; import android.view.LayoutInflater; import android.view.MotionEvent; -import android.view.PhoneWindow; +import com.android.internal.policy.PhoneWindow; import android.view.View; import android.view.ViewGroup; import android.view.Window; diff --git a/core/java/android/widget/ProgressBar.java b/core/java/android/widget/ProgressBar.java index b59ae17..639a09c 100644 --- a/core/java/android/widget/ProgressBar.java +++ b/core/java/android/widget/ProgressBar.java @@ -16,6 +16,7 @@ package android.widget; +import android.annotation.NonNull; import android.annotation.Nullable; import android.graphics.PorterDuff; @@ -49,6 +50,7 @@ import android.view.Gravity; import android.view.RemotableViewMethod; import android.view.View; import android.view.ViewDebug; +import android.view.ViewHierarchyEncoder; import android.view.accessibility.AccessibilityEvent; import android.view.accessibility.AccessibilityManager; import android.view.animation.AlphaAnimation; @@ -1893,6 +1895,17 @@ public class ProgressBar extends View { postDelayed(mAccessibilityEventSender, TIMEOUT_SEND_ACCESSIBILITY_EVENT); } + /** @hide */ + @Override + protected void encodeProperties(@NonNull ViewHierarchyEncoder stream) { + super.encodeProperties(stream); + + stream.addProperty("progress:max", getMax()); + stream.addProperty("progress:progress", getProgress()); + stream.addProperty("progress:secondaryProgress", getSecondaryProgress()); + stream.addProperty("progress:indeterminate", isIndeterminate()); + } + /** * Command for sending an accessibility event. */ diff --git a/core/java/android/widget/RelativeLayout.java b/core/java/android/widget/RelativeLayout.java index d12739f..affc5da 100644 --- a/core/java/android/widget/RelativeLayout.java +++ b/core/java/android/widget/RelativeLayout.java @@ -16,6 +16,7 @@ package android.widget; +import android.annotation.NonNull; import android.util.ArrayMap; import com.android.internal.R; @@ -36,6 +37,7 @@ import android.view.Gravity; import android.view.View; import android.view.ViewDebug; import android.view.ViewGroup; +import android.view.ViewHierarchyEncoder; import android.view.accessibility.AccessibilityEvent; import android.widget.RemoteViews.RemoteView; @@ -1616,6 +1618,13 @@ public class RelativeLayout extends ViewGroup { // This will set the layout direction super.resolveLayoutDirection(layoutDirection); } + + /** @hide */ + @Override + protected void encodeProperties(@NonNull ViewHierarchyEncoder encoder) { + super.encodeProperties(encoder); + encoder.addProperty("layout:alignWithParent", alignWithParent); + } } private static class DependencyGraph { diff --git a/core/java/android/widget/ScrollView.java b/core/java/android/widget/ScrollView.java index 98d61d3..2709f25 100644 --- a/core/java/android/widget/ScrollView.java +++ b/core/java/android/widget/ScrollView.java @@ -16,6 +16,7 @@ package android.widget; +import android.annotation.NonNull; import android.os.Build; import android.os.Parcel; import android.os.Parcelable; @@ -38,6 +39,7 @@ import android.view.View; import android.view.ViewConfiguration; import android.view.ViewDebug; import android.view.ViewGroup; +import android.view.ViewHierarchyEncoder; import android.view.ViewParent; import android.view.accessibility.AccessibilityEvent; import android.view.accessibility.AccessibilityNodeInfo; @@ -1787,6 +1789,13 @@ public class ScrollView extends FrameLayout { return ss; } + /** @hide */ + @Override + protected void encodeProperties(@NonNull ViewHierarchyEncoder encoder) { + super.encodeProperties(encoder); + encoder.addProperty("fillViewport", mFillViewport); + } + static class SavedState extends BaseSavedState { public int scrollPosition; diff --git a/core/java/android/widget/Switch.java b/core/java/android/widget/Switch.java index f94f97c..ff587c2 100644 --- a/core/java/android/widget/Switch.java +++ b/core/java/android/widget/Switch.java @@ -26,13 +26,11 @@ import android.content.res.Resources; import android.content.res.TypedArray; import android.graphics.Canvas; import android.graphics.Insets; -import android.graphics.Paint; import android.graphics.PorterDuff; import android.graphics.Rect; import android.graphics.Typeface; import android.graphics.Region.Op; import android.graphics.drawable.Drawable; -import android.os.Bundle; import android.text.Layout; import android.text.StaticLayout; import android.text.TextPaint; @@ -46,7 +44,7 @@ import android.view.Gravity; import android.view.MotionEvent; import android.view.SoundEffectConstants; import android.view.VelocityTracker; -import android.view.ViewAssistStructure; +import android.view.ViewStructure; import android.view.ViewConfiguration; import android.view.accessibility.AccessibilityEvent; import android.view.accessibility.AccessibilityNodeInfo; @@ -1363,8 +1361,8 @@ public class Switch extends CompoundButton { } @Override - public void onProvideAssistStructure(ViewAssistStructure structure) { - super.onProvideAssistStructure(structure); + public void onProvideStructure(ViewStructure structure) { + super.onProvideStructure(structure); CharSequence switchText = isChecked() ? mTextOn : mTextOff; if (!TextUtils.isEmpty(switchText)) { CharSequence oldText = structure.getText(); diff --git a/core/java/android/widget/TableRow.java b/core/java/android/widget/TableRow.java index f73ee49..d4288d6 100644 --- a/core/java/android/widget/TableRow.java +++ b/core/java/android/widget/TableRow.java @@ -16,6 +16,7 @@ package android.widget; +import android.annotation.NonNull; import android.content.Context; import android.content.res.TypedArray; import android.util.AttributeSet; @@ -24,7 +25,7 @@ import android.view.Gravity; import android.view.View; import android.view.ViewDebug; import android.view.ViewGroup; - +import android.view.ViewHierarchyEncoder; /** * <p>A layout that arranges its children horizontally. A TableRow should @@ -509,6 +510,14 @@ public class TableRow extends LinearLayout { height = WRAP_CONTENT; } } + + /** @hide */ + @Override + protected void encodeProperties(@NonNull ViewHierarchyEncoder encoder) { + super.encodeProperties(encoder); + encoder.addProperty("layout:column", column); + encoder.addProperty("layout:span", span); + } } // special transparent hierarchy change listener diff --git a/core/java/android/widget/TextClock.java b/core/java/android/widget/TextClock.java index e2acaac..5d7b569 100644 --- a/core/java/android/widget/TextClock.java +++ b/core/java/android/widget/TextClock.java @@ -16,6 +16,7 @@ package android.widget; +import android.annotation.NonNull; import android.app.ActivityManager; import android.content.BroadcastReceiver; import android.content.ContentResolver; @@ -32,6 +33,7 @@ import android.provider.Settings; import android.text.format.DateFormat; import android.util.AttributeSet; import android.view.RemotableViewMethod; +import android.view.ViewHierarchyEncoder; import com.android.internal.R; @@ -546,4 +548,18 @@ public class TextClock extends TextView { mTime.setTimeInMillis(System.currentTimeMillis()); setText(DateFormat.format(mFormat, mTime)); } + + /** @hide */ + @Override + protected void encodeProperties(@NonNull ViewHierarchyEncoder stream) { + super.encodeProperties(stream); + + CharSequence s = getFormat12Hour(); + stream.addProperty("format12Hour", s == null ? null : s.toString()); + + s = getFormat24Hour(); + stream.addProperty("format24Hour", s == null ? null : s.toString()); + stream.addProperty("format", mFormat == null ? null : mFormat.toString()); + stream.addProperty("hasSeconds", mHasSeconds); + } } diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java index 449173f..68c49cd 100644 --- a/core/java/android/widget/TextView.java +++ b/core/java/android/widget/TextView.java @@ -114,12 +114,13 @@ import android.view.KeyCharacterMap; import android.view.KeyEvent; import android.view.MotionEvent; import android.view.View; -import android.view.ViewAssistStructure; +import android.view.ViewStructure; import android.view.ViewConfiguration; import android.view.ViewDebug; import android.view.ViewGroup.LayoutParams; import android.view.ViewRootImpl; import android.view.ViewTreeObserver; +import android.view.ViewHierarchyEncoder; import android.view.accessibility.AccessibilityEvent; import android.view.accessibility.AccessibilityManager; import android.view.accessibility.AccessibilityNodeInfo; @@ -2844,7 +2845,8 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener @ViewDebug.IntToString(from = Typeface.BOLD_ITALIC, to = "BOLD_ITALIC") }) public int getTypefaceStyle() { - return mTextPaint.getTypeface().getStyle(); + Typeface typeface = mTextPaint.getTypeface(); + return typeface != null ? typeface.getStyle() : Typeface.NORMAL; } /** @@ -8713,8 +8715,8 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener } @Override - public void onProvideAssistStructure(ViewAssistStructure structure) { - super.onProvideAssistStructure(structure); + public void onProvideStructure(ViewStructure structure) { + super.onProvideStructure(structure); final boolean isPassword = hasPasswordTransformationMethod(); if (!isPassword) { structure.setText(getText(), getSelectionStart(), getSelectionEnd()); @@ -9556,6 +9558,23 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener } } + /** @hide */ + @Override + protected void encodeProperties(@NonNull ViewHierarchyEncoder stream) { + super.encodeProperties(stream); + + TruncateAt ellipsize = getEllipsize(); + stream.addProperty("text:ellipsize", ellipsize == null ? null : ellipsize.name()); + stream.addProperty("text:textSize", getTextSize()); + stream.addProperty("text:scaledTextSize", getScaledTextSize()); + stream.addProperty("text:typefaceStyle", getTypefaceStyle()); + stream.addProperty("text:selectionStart", getSelectionStart()); + stream.addProperty("text:selectionEnd", getSelectionEnd()); + stream.addProperty("text:curTextColor", mCurTextColor); + stream.addProperty("text:text", mText == null ? null : mText.toString()); + stream.addProperty("text:gravity", mGravity); + } + /** * User interface state that is stored by TextView for implementing * {@link View#onSaveInstanceState}. diff --git a/core/java/com/android/internal/app/ChooserActivity.java b/core/java/com/android/internal/app/ChooserActivity.java index e347faa..62ca1f0 100644 --- a/core/java/com/android/internal/app/ChooserActivity.java +++ b/core/java/com/android/internal/app/ChooserActivity.java @@ -21,6 +21,7 @@ import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.content.IntentSender; +import android.content.IntentSender.SendIntentException; import android.content.ServiceConnection; import android.content.pm.ActivityInfo; import android.content.pm.PackageManager; @@ -34,6 +35,7 @@ import android.os.IBinder; import android.os.Message; import android.os.Parcelable; import android.os.RemoteException; +import android.os.ResultReceiver; import android.os.UserHandle; import android.service.chooser.ChooserTarget; import android.service.chooser.ChooserTargetService; @@ -53,11 +55,13 @@ public class ChooserActivity extends ResolverActivity { private static final boolean DEBUG = false; - private static final int QUERY_TARGET_LIMIT = 5; + private static final int QUERY_TARGET_SERVICE_LIMIT = 5; private static final int WATCHDOG_TIMEOUT_MILLIS = 5000; private Bundle mReplacementExtras; private IntentSender mChosenComponentSender; + private IntentSender mRefinementIntentSender; + private RefinementResultReceiver mRefinementResultReceiver; private ChooserTarget[] mCallerChooserTargets; @@ -113,6 +117,32 @@ public class ChooserActivity extends ResolverActivity { if (target != null) { modifyTargetIntent(target); } + Parcelable[] targetsParcelable + = intent.getParcelableArrayExtra(Intent.EXTRA_ALTERNATE_INTENTS); + if (targetsParcelable != null) { + final boolean offset = target == null; + Intent[] additionalTargets = + new Intent[offset ? targetsParcelable.length - 1 : targetsParcelable.length]; + for (int i = 0; i < targetsParcelable.length; i++) { + if (!(targetsParcelable[i] instanceof Intent)) { + Log.w(TAG, "EXTRA_ALTERNATE_INTENTS array entry #" + i + " is not an Intent: " + + targetsParcelable[i]); + finish(); + super.onCreate(null); + return; + } + final Intent additionalTarget = (Intent) targetsParcelable[i]; + if (i == 0 && target == null) { + target = additionalTarget; + modifyTargetIntent(target); + } else { + additionalTargets[offset ? i - 1 : i] = additionalTarget; + modifyTargetIntent(additionalTarget); + } + } + setAdditionalTargets(additionalTargets); + } + mReplacementExtras = intent.getBundleExtra(Intent.EXTRA_REPLACEMENT_EXTRAS); CharSequence title = intent.getCharSequenceExtra(Intent.EXTRA_TITLE); int defaultTitleRes = 0; @@ -125,7 +155,7 @@ public class ChooserActivity extends ResolverActivity { initialIntents = new Intent[pa.length]; for (int i=0; i<pa.length; i++) { if (!(pa[i] instanceof Intent)) { - Log.w("ChooserActivity", "Initial intent #" + i + " not an Intent: " + pa[i]); + Log.w(TAG, "Initial intent #" + i + " not an Intent: " + pa[i]); finish(); super.onCreate(null); return; @@ -141,8 +171,7 @@ public class ChooserActivity extends ResolverActivity { final ChooserTarget[] targets = new ChooserTarget[pa.length]; for (int i = 0; i < pa.length; i++) { if (!(pa[i] instanceof ChooserTarget)) { - Log.w("ChooserActivity", "Chooser target #" + i + " is not a ChooserTarget: " + - pa[i]); + Log.w(TAG, "Chooser target #" + i + " is not a ChooserTarget: " + pa[i]); finish(); super.onCreate(null); return; @@ -153,12 +182,23 @@ public class ChooserActivity extends ResolverActivity { } mChosenComponentSender = intent.getParcelableExtra( Intent.EXTRA_CHOSEN_COMPONENT_INTENT_SENDER); + mRefinementIntentSender = intent.getParcelableExtra( + Intent.EXTRA_CHOOSER_REFINEMENT_INTENT_SENDER); setSafeForwardingMode(true); super.onCreate(savedInstanceState, target, title, defaultTitleRes, initialIntents, null, false); } @Override + protected void onDestroy() { + super.onDestroy(); + if (mRefinementResultReceiver != null) { + mRefinementResultReceiver.destroy(); + mRefinementResultReceiver = null; + } + } + + @Override public Intent getReplacementIntent(ActivityInfo aInfo, Intent defIntent) { Intent result = defIntent; if (mReplacementExtras != null) { @@ -211,6 +251,37 @@ public class ChooserActivity extends ResolverActivity { } } + @Override + protected boolean onTargetSelected(TargetInfo target, boolean alwaysCheck) { + if (mRefinementIntentSender != null) { + final Intent fillIn = new Intent(); + final List<Intent> sourceIntents = target.getAllSourceIntents(); + if (!sourceIntents.isEmpty()) { + fillIn.putExtra(Intent.EXTRA_INTENT, sourceIntents.get(0)); + if (sourceIntents.size() > 1) { + final Intent[] alts = new Intent[sourceIntents.size() - 1]; + for (int i = 1, N = sourceIntents.size(); i < N; i++) { + alts[i - 1] = sourceIntents.get(i); + } + fillIn.putExtra(Intent.EXTRA_ALTERNATE_INTENTS, alts); + } + if (mRefinementResultReceiver != null) { + mRefinementResultReceiver.destroy(); + } + mRefinementResultReceiver = new RefinementResultReceiver(this, target, null); + fillIn.putExtra(Intent.EXTRA_RESULT_RECEIVER, + mRefinementResultReceiver); + try { + mRefinementIntentSender.sendIntent(this, 0, fillIn, null, null); + return false; + } catch (SendIntentException e) { + Log.e(TAG, "Refinement IntentSender failed to send", e); + } + } + } + return super.onTargetSelected(target, alwaysCheck); + } + void queryTargetServices(ChooserListAdapter adapter) { final PackageManager pm = getPackageManager(); int targetsToQuery = 0; @@ -258,8 +329,9 @@ public class ChooserActivity extends ResolverActivity { targetsToQuery++; } } - if (targetsToQuery >= QUERY_TARGET_LIMIT) { - if (DEBUG) Log.d(TAG, "queryTargets hit query target limit " + QUERY_TARGET_LIMIT); + if (targetsToQuery >= QUERY_TARGET_SERVICE_LIMIT) { + if (DEBUG) Log.d(TAG, "queryTargets hit query target limit " + + QUERY_TARGET_SERVICE_LIMIT); break; } } @@ -303,6 +375,43 @@ public class ChooserActivity extends ResolverActivity { mTargetResultHandler.removeMessages(CHOOSER_TARGET_SERVICE_WATCHDOG_TIMEOUT); } + void onRefinementResult(TargetInfo selectedTarget, Intent matchingIntent) { + if (mRefinementResultReceiver != null) { + mRefinementResultReceiver.destroy(); + mRefinementResultReceiver = null; + } + + if (selectedTarget == null) { + Log.e(TAG, "Refinement result intent did not match any known targets; canceling"); + } else if (!checkTargetSourceIntent(selectedTarget, matchingIntent)) { + Log.e(TAG, "onRefinementResult: Selected target " + selectedTarget + + " cannot match refined source intent " + matchingIntent); + } else if (super.onTargetSelected(selectedTarget.cloneFilledIn(matchingIntent, 0), false)) { + finish(); + return; + } + onRefinementCanceled(); + } + + void onRefinementCanceled() { + if (mRefinementResultReceiver != null) { + mRefinementResultReceiver.destroy(); + mRefinementResultReceiver = null; + } + finish(); + } + + boolean checkTargetSourceIntent(TargetInfo target, Intent matchingIntent) { + final List<Intent> targetIntents = target.getAllSourceIntents(); + for (int i = 0, N = targetIntents.size(); i < N; i++) { + final Intent targetIntent = targetIntents.get(i); + if (targetIntent.filterEquals(matchingIntent)) { + return true; + } + } + return false; + } + @Override ResolveListAdapter createAdapter(Context context, Intent[] initialIntents, List<ResolveInfo> rList, int launchedFromUid, boolean filterLastUsed) { @@ -313,17 +422,19 @@ public class ChooserActivity extends ResolverActivity { return adapter; } - class ChooserTargetInfo implements TargetInfo { - private final TargetInfo mSourceInfo; + final class ChooserTargetInfo implements TargetInfo { + private final DisplayResolveInfo mSourceInfo; private final ResolveInfo mBackupResolveInfo; private final ChooserTarget mChooserTarget; private final Drawable mDisplayIcon; + private final Intent mFillInIntent; + private final int mFillInFlags; public ChooserTargetInfo(ChooserTarget target) { this(null, target); } - public ChooserTargetInfo(TargetInfo sourceInfo, ChooserTarget chooserTarget) { + public ChooserTargetInfo(DisplayResolveInfo sourceInfo, ChooserTarget chooserTarget) { mSourceInfo = sourceInfo; mChooserTarget = chooserTarget; mDisplayIcon = new BitmapDrawable(getResources(), chooserTarget.getIcon()); @@ -333,6 +444,18 @@ public class ChooserActivity extends ResolverActivity { } else { mBackupResolveInfo = getPackageManager().resolveActivity(getResolvedIntent(), 0); } + + mFillInIntent = null; + mFillInFlags = 0; + } + + private ChooserTargetInfo(ChooserTargetInfo other, Intent fillInIntent, int flags) { + mSourceInfo = other.mSourceInfo; + mBackupResolveInfo = other.mBackupResolveInfo; + mChooserTarget = other.mChooserTarget; + mDisplayIcon = other.mDisplayIcon; + mFillInIntent = fillInIntent; + mFillInFlags = flags; } @Override @@ -358,22 +481,42 @@ public class ChooserActivity extends ResolverActivity { } private Intent getFillInIntent() { - return mSourceInfo != null ? mSourceInfo.getResolvedIntent() : getTargetIntent(); + Intent result = mSourceInfo != null + ? mSourceInfo.getResolvedIntent() : getTargetIntent(); + if (result == null) { + Log.e(TAG, "ChooserTargetInfo#getFillInIntent: no fillIn intent available"); + } else if (mFillInIntent != null) { + result = new Intent(result); + result.fillIn(mFillInIntent, mFillInFlags); + } + return result; } @Override public boolean start(Activity activity, Bundle options) { - return mChooserTarget.sendIntent(activity, getFillInIntent()); + final Intent intent = getFillInIntent(); + if (intent == null) { + return false; + } + return mChooserTarget.sendIntent(activity, intent); } @Override public boolean startAsCaller(Activity activity, Bundle options, int userId) { - return mChooserTarget.sendIntentAsCaller(activity, getFillInIntent(), userId); + final Intent intent = getFillInIntent(); + if (intent == null) { + return false; + } + return mChooserTarget.sendIntentAsCaller(activity, intent, userId); } @Override public boolean startAsUser(Activity activity, Bundle options, UserHandle user) { - return mChooserTarget.sendIntentAsUser(activity, getFillInIntent(), user); + final Intent intent = getFillInIntent(); + if (intent == null) { + return false; + } + return mChooserTarget.sendIntentAsUser(activity, intent, user); } @Override @@ -395,6 +538,21 @@ public class ChooserActivity extends ResolverActivity { public Drawable getDisplayIcon() { return mDisplayIcon; } + + @Override + public TargetInfo cloneFilledIn(Intent fillInIntent, int flags) { + return new ChooserTargetInfo(this, fillInIntent, flags); + } + + @Override + public List<Intent> getAllSourceIntents() { + final List<Intent> results = new ArrayList<>(); + if (mSourceInfo != null) { + // We only queried the service for the first one in our sourceinfo. + results.add(mSourceInfo.getAllSourceIntents().get(0)); + } + return results; + } } public class ChooserListAdapter extends ResolveListAdapter { @@ -542,4 +700,53 @@ public class ChooserActivity extends ResolverActivity { connection = c; } } + + static class RefinementResultReceiver extends ResultReceiver { + private ChooserActivity mChooserActivity; + private TargetInfo mSelectedTarget; + + public RefinementResultReceiver(ChooserActivity host, TargetInfo target, + Handler handler) { + super(handler); + mChooserActivity = host; + mSelectedTarget = target; + } + + @Override + protected void onReceiveResult(int resultCode, Bundle resultData) { + if (mChooserActivity == null) { + Log.e(TAG, "Destroyed RefinementResultReceiver received a result"); + return; + } + if (resultData == null) { + Log.e(TAG, "RefinementResultReceiver received null resultData"); + return; + } + + switch (resultCode) { + case RESULT_CANCELED: + mChooserActivity.onRefinementCanceled(); + break; + case RESULT_OK: + Parcelable intentParcelable = resultData.getParcelable(Intent.EXTRA_INTENT); + if (intentParcelable instanceof Intent) { + mChooserActivity.onRefinementResult(mSelectedTarget, + (Intent) intentParcelable); + } else { + Log.e(TAG, "RefinementResultReceiver received RESULT_OK but no Intent" + + " in resultData with key Intent.EXTRA_INTENT"); + } + break; + default: + Log.w(TAG, "Unknown result code " + resultCode + + " sent to RefinementResultReceiver"); + break; + } + } + + public void destroy() { + mChooserActivity = null; + mSelectedTarget = null; + } + } } diff --git a/core/java/com/android/internal/app/ResolverActivity.java b/core/java/com/android/internal/app/ResolverActivity.java index 8dd7836..2048664 100644 --- a/core/java/com/android/internal/app/ResolverActivity.java +++ b/core/java/com/android/internal/app/ResolverActivity.java @@ -102,7 +102,7 @@ public class ResolverActivity extends Activity implements AdapterView.OnItemClic private int mLastSelected = AbsListView.INVALID_POSITION; private boolean mResolvingHome = false; private int mProfileSwitchMessageId = -1; - private Intent mIntent; + private final ArrayList<Intent> mIntents = new ArrayList<>(); private UsageStatsManager mUsm; private Map<String, UsageStats> mStats; @@ -229,7 +229,7 @@ public class ResolverActivity extends Activity implements AdapterView.OnItemClic final ActivityManager am = (ActivityManager) getSystemService(ACTIVITY_SERVICE); mIconDpi = am.getLauncherLargeIconDensity(); - mIntent = new Intent(intent); + mIntents.add(0, new Intent(intent)); mAdapter = createAdapter(this, initialIntents, rList, mLaunchedFromUid, alwaysUseOption); final int layoutId; @@ -250,7 +250,7 @@ public class ResolverActivity extends Activity implements AdapterView.OnItemClic return; } - int count = mAdapter.mList.size(); + int count = mAdapter.mDisplayList.size(); if (count > 1 || (count == 1 && mAdapter.getOtherProfile() != null)) { setContentView(layoutId); mAdapterView = (AbsListView) findViewById(R.id.resolver_list); @@ -376,8 +376,16 @@ public class ResolverActivity extends Activity implements AdapterView.OnItemClic } } + protected final void setAdditionalTargets(Intent[] intents) { + if (intents != null) { + for (Intent intent : intents) { + mIntents.add(intent); + } + } + } + public Intent getTargetIntent() { - return mIntent; + return mIntents.isEmpty() ? null : mIntents.get(0); } private String getReferrerPackageName() { @@ -630,8 +638,9 @@ public class ResolverActivity extends Activity implements AdapterView.OnItemClic } TargetInfo target = mAdapter.targetInfoForPosition(which, filtered); - onTargetSelected(target, always); - finish(); + if (onTargetSelected(target, always)) { + finish(); + } } /** @@ -641,7 +650,7 @@ public class ResolverActivity extends Activity implements AdapterView.OnItemClic return defIntent; } - protected void onTargetSelected(TargetInfo target, boolean alwaysCheck) { + protected boolean onTargetSelected(TargetInfo target, boolean alwaysCheck) { final ResolveInfo ri = target.getResolveInfo(); final Intent intent = target != null ? target.getResolvedIntent() : null; @@ -728,7 +737,7 @@ public class ResolverActivity extends Activity implements AdapterView.OnItemClic ComponentName[] set = new ComponentName[N]; int bestMatch = 0; for (int i=0; i<N; i++) { - ResolveInfo r = mAdapter.mOrigResolveList.get(i); + ResolveInfo r = mAdapter.mOrigResolveList.get(i).getResolveInfoAt(0); set[i] = new ComponentName(r.activityInfo.packageName, r.activityInfo.name); if (r.match > bestMatch) bestMatch = r.match; @@ -774,6 +783,7 @@ public class ResolverActivity extends Activity implements AdapterView.OnItemClic if (target != null) { safelyStartActivity(target); } + return true; } void safelyStartActivity(TargetInfo cti) { @@ -837,15 +847,17 @@ public class ResolverActivity extends Activity implements AdapterView.OnItemClic private Drawable mDisplayIcon; private final CharSequence mExtendedInfo; private final Intent mResolvedIntent; + private final List<Intent> mSourceIntents = new ArrayList<>(); - DisplayResolveInfo(ResolveInfo pri, CharSequence pLabel, + DisplayResolveInfo(Intent originalIntent, ResolveInfo pri, CharSequence pLabel, CharSequence pInfo, Intent pOrigIntent) { + mSourceIntents.add(originalIntent); mResolveInfo = pri; mDisplayLabel = pLabel; mExtendedInfo = pInfo; final Intent intent = new Intent(pOrigIntent != null ? pOrigIntent : - getReplacementIntent(pri.activityInfo, mIntent)); + getReplacementIntent(pri.activityInfo, getTargetIntent())); intent.addFlags(Intent.FLAG_ACTIVITY_FORWARD_RESULT | Intent.FLAG_ACTIVITY_PREVIOUS_IS_TOP); final ActivityInfo ai = mResolveInfo.activityInfo; @@ -854,6 +866,16 @@ public class ResolverActivity extends Activity implements AdapterView.OnItemClic mResolvedIntent = intent; } + private DisplayResolveInfo(DisplayResolveInfo other, Intent fillInIntent, int flags) { + mSourceIntents.addAll(other.getAllSourceIntents()); + mResolveInfo = other.mResolveInfo; + mDisplayLabel = other.mDisplayLabel; + mDisplayIcon = other.mDisplayIcon; + mExtendedInfo = other.mExtendedInfo; + mResolvedIntent = new Intent(other.mResolvedIntent); + mResolvedIntent.fillIn(fillInIntent, flags); + } + public ResolveInfo getResolveInfo() { return mResolveInfo; } @@ -866,6 +888,20 @@ public class ResolverActivity extends Activity implements AdapterView.OnItemClic return mDisplayIcon; } + @Override + public TargetInfo cloneFilledIn(Intent fillInIntent, int flags) { + return new DisplayResolveInfo(this, fillInIntent, flags); + } + + @Override + public List<Intent> getAllSourceIntents() { + return mSourceIntents; + } + + public void addAlternateSourceIntent(Intent alt) { + mSourceIntents.add(alt); + } + public void setDisplayIcon(Drawable icon) { mDisplayIcon = icon; } @@ -986,6 +1022,16 @@ public class ResolverActivity extends Activity implements AdapterView.OnItemClic * @return The drawable that should be used to represent this target */ public Drawable getDisplayIcon(); + + /** + * Clone this target with the given fill-in information. + */ + public TargetInfo cloneFilledIn(Intent fillInIntent, int flags); + + /** + * @return the list of supported source intents deduped against this single target + */ + public List<Intent> getAllSourceIntents(); } class ResolveListAdapter extends BaseAdapter { @@ -998,8 +1044,8 @@ public class ResolverActivity extends Activity implements AdapterView.OnItemClic protected final LayoutInflater mInflater; - List<DisplayResolveInfo> mList; - List<ResolveInfo> mOrigResolveList; + List<DisplayResolveInfo> mDisplayList; + List<ResolvedComponentInfo> mOrigResolveList; private int mLastChosenPosition = -1; private boolean mFilterLastUsed; @@ -1010,7 +1056,7 @@ public class ResolverActivity extends Activity implements AdapterView.OnItemClic mBaseResolveList = rList; mLaunchedFromUid = launchedFromUid; mInflater = LayoutInflater.from(context); - mList = new ArrayList<>(); + mDisplayList = new ArrayList<>(); mFilterLastUsed = filterLastUsed; rebuildList(); } @@ -1027,7 +1073,7 @@ public class ResolverActivity extends Activity implements AdapterView.OnItemClic public DisplayResolveInfo getFilteredItem() { if (mFilterLastUsed && mLastChosenPosition >= 0) { // Not using getItem since it offsets to dodge this position for the list - return mList.get(mLastChosenPosition); + return mDisplayList.get(mLastChosenPosition); } return null; } @@ -1048,11 +1094,12 @@ public class ResolverActivity extends Activity implements AdapterView.OnItemClic } private void rebuildList() { - List<ResolveInfo> currentResolveList; + List<ResolvedComponentInfo> currentResolveList = null; try { + final Intent primaryIntent = getTargetIntent(); mLastChosen = AppGlobals.getPackageManager().getLastChosenActivity( - mIntent, mIntent.resolveTypeIfNeeded(getContentResolver()), + primaryIntent, primaryIntent.resolveTypeIfNeeded(getContentResolver()), PackageManager.MATCH_DEFAULT_ONLY); } catch (RemoteException re) { Log.d(TAG, "Error calling setLastChosenActivity\n" + re); @@ -1060,15 +1107,27 @@ public class ResolverActivity extends Activity implements AdapterView.OnItemClic // Clear the value of mOtherProfile from previous call. mOtherProfile = null; - mList.clear(); + mDisplayList.clear(); if (mBaseResolveList != null) { - currentResolveList = mOrigResolveList = mBaseResolveList; + currentResolveList = mOrigResolveList = new ArrayList<>(); + addResolveListDedupe(currentResolveList, getTargetIntent(), mBaseResolveList); } else { - currentResolveList = mOrigResolveList = mPm.queryIntentActivities(mIntent, - PackageManager.MATCH_DEFAULT_ONLY - | (shouldGetResolvedFilter() ? PackageManager.GET_RESOLVED_FILTER : 0) - | (shouldGetActivityMetadata() ? PackageManager.GET_META_DATA : 0) - ); + final boolean shouldGetResolvedFilter = shouldGetResolvedFilter(); + final boolean shouldGetActivityMetadata = shouldGetActivityMetadata(); + for (int i = 0, N = mIntents.size(); i < N; i++) { + final Intent intent = mIntents.get(i); + final List<ResolveInfo> infos = mPm.queryIntentActivities(intent, + PackageManager.MATCH_DEFAULT_ONLY + | (shouldGetResolvedFilter ? PackageManager.GET_RESOLVED_FILTER : 0) + | (shouldGetActivityMetadata ? PackageManager.GET_META_DATA : 0)); + if (infos != null) { + if (currentResolveList == null) { + currentResolveList = mOrigResolveList = new ArrayList<>(); + } + addResolveListDedupe(currentResolveList, intent, infos); + } + } + // Filter out any activities that the launched uid does not // have permission for. We don't do this when we have an explicit // list of resolved activities, because that only happens when @@ -1076,14 +1135,15 @@ public class ResolverActivity extends Activity implements AdapterView.OnItemClic // they gave us. if (currentResolveList != null) { for (int i=currentResolveList.size()-1; i >= 0; i--) { - ActivityInfo ai = currentResolveList.get(i).activityInfo; + ActivityInfo ai = currentResolveList.get(i) + .getResolveInfoAt(0).activityInfo; int granted = ActivityManager.checkComponentPermission( ai.permission, mLaunchedFromUid, ai.applicationInfo.uid, ai.exported); if (granted != PackageManager.PERMISSION_GRANTED) { // Access not allowed! if (mOrigResolveList == currentResolveList) { - mOrigResolveList = new ArrayList<ResolveInfo>(mOrigResolveList); + mOrigResolveList = new ArrayList<>(mOrigResolveList); } currentResolveList.remove(i); } @@ -1094,9 +1154,10 @@ public class ResolverActivity extends Activity implements AdapterView.OnItemClic if ((currentResolveList != null) && ((N = currentResolveList.size()) > 0)) { // Only display the first matches that are either of equal // priority or have asked to be default options. - ResolveInfo r0 = currentResolveList.get(0); + ResolvedComponentInfo rci0 = currentResolveList.get(0); + ResolveInfo r0 = rci0.getResolveInfoAt(0); for (int i=1; i<N; i++) { - ResolveInfo ri = currentResolveList.get(i); + ResolveInfo ri = currentResolveList.get(i).getResolveInfoAt(0); if (DEBUG) Log.v( TAG, r0.activityInfo.name + "=" + @@ -1107,7 +1168,7 @@ public class ResolverActivity extends Activity implements AdapterView.OnItemClic r0.isDefault != ri.isDefault) { while (i < N) { if (mOrigResolveList == currentResolveList) { - mOrigResolveList = new ArrayList<ResolveInfo>(mOrigResolveList); + mOrigResolveList = new ArrayList<>(mOrigResolveList); } currentResolveList.remove(i); N--; @@ -1115,9 +1176,8 @@ public class ResolverActivity extends Activity implements AdapterView.OnItemClic } } if (N > 1) { - Comparator<ResolveInfo> rComparator = - new ResolverComparator(ResolverActivity.this, mIntent); - Collections.sort(currentResolveList, rComparator); + Collections.sort(currentResolveList, + new ResolverComparator(ResolverActivity.this, getTargetIntent())); } // First put the initial items at the top. if (mInitialIntents != null) { @@ -1146,14 +1206,15 @@ public class ResolverActivity extends Activity implements AdapterView.OnItemClic ri.nonLocalizedLabel = li.getNonLocalizedLabel(); ri.icon = li.getIconResource(); } - addResolveInfo(new DisplayResolveInfo(ri, + addResolveInfo(new DisplayResolveInfo(ii, ri, ri.loadLabel(getPackageManager()), null, ii)); } } // Check for applications with same name and use application name or // package name if necessary - r0 = currentResolveList.get(0); + rci0 = currentResolveList.get(0); + r0 = rci0.getResolveInfoAt(0); int start = 0; CharSequence r0Label = r0.loadLabel(mPm); mHasExtendedInfo = false; @@ -1161,7 +1222,8 @@ public class ResolverActivity extends Activity implements AdapterView.OnItemClic if (r0Label == null) { r0Label = r0.activityInfo.packageName; } - ResolveInfo ri = currentResolveList.get(i); + ResolvedComponentInfo rci = currentResolveList.get(i); + ResolveInfo ri = rci.getResolveInfoAt(0); CharSequence riLabel = ri.loadLabel(mPm); if (riLabel == null) { riLabel = ri.activityInfo.packageName; @@ -1169,13 +1231,14 @@ public class ResolverActivity extends Activity implements AdapterView.OnItemClic if (riLabel.equals(r0Label)) { continue; } - processGroup(currentResolveList, start, (i-1), r0, r0Label); + processGroup(currentResolveList, start, (i-1), rci0, r0Label); + rci0 = rci; r0 = ri; r0Label = riLabel; start = i; } // Process last group - processGroup(currentResolveList, start, (N-1), r0, r0Label); + processGroup(currentResolveList, start, (N-1), rci0, r0Label); } // Layout doesn't handle both profile button and last chosen @@ -1188,6 +1251,36 @@ public class ResolverActivity extends Activity implements AdapterView.OnItemClic onListRebuilt(); } + private void addResolveListDedupe(List<ResolvedComponentInfo> into, Intent intent, + List<ResolveInfo> from) { + final int fromCount = from.size(); + final int intoCount = into.size(); + for (int i = 0; i < fromCount; i++) { + final ResolveInfo newInfo = from.get(i); + boolean found = false; + // Only loop to the end of into as it was before we started; no dupes in from. + for (int j = 0; j < intoCount; j++) { + final ResolvedComponentInfo rci = into.get(i); + if (isSameResolvedComponent(newInfo, rci)) { + found = true; + rci.add(intent, newInfo); + break; + } + } + if (!found) { + into.add(new ResolvedComponentInfo(new ComponentName( + newInfo.activityInfo.packageName, newInfo.activityInfo.name), + intent, newInfo)); + } + } + } + + private boolean isSameResolvedComponent(ResolveInfo a, ResolvedComponentInfo b) { + final ActivityInfo ai = a.activityInfo; + return ai.packageName.equals(b.name.getPackageName()) + && ai.name.equals(b.name.getClassName()); + } + public void onListRebuilt() { // This space for rent } @@ -1196,18 +1289,18 @@ public class ResolverActivity extends Activity implements AdapterView.OnItemClic return mFilterLastUsed; } - private void processGroup(List<ResolveInfo> rList, int start, int end, ResolveInfo ro, - CharSequence roLabel) { + private void processGroup(List<ResolvedComponentInfo> rList, int start, int end, + ResolvedComponentInfo ro, CharSequence roLabel) { // Process labels from start to i int num = end - start+1; if (num == 1) { // No duplicate labels. Use label for entry at start - addResolveInfo(new DisplayResolveInfo(ro, roLabel, null, null)); - updateLastChosenPosition(ro); + addResolveInfoWithAlternates(ro, null, roLabel); } else { mHasExtendedInfo = true; boolean usePkg = false; - CharSequence startApp = ro.activityInfo.applicationInfo.loadLabel(mPm); + CharSequence startApp = ro.getResolveInfoAt(0).activityInfo.applicationInfo + .loadLabel(mPm); if (startApp == null) { usePkg = true; } @@ -1217,7 +1310,7 @@ public class ResolverActivity extends Activity implements AdapterView.OnItemClic new HashSet<CharSequence>(); duplicates.add(startApp); for (int j = start+1; j <= end ; j++) { - ResolveInfo jRi = rList.get(j); + ResolveInfo jRi = rList.get(j).getResolveInfoAt(0); CharSequence jApp = jRi.activityInfo.applicationInfo.loadLabel(mPm); if ( (jApp == null) || (duplicates.contains(jApp))) { usePkg = true; @@ -1230,26 +1323,46 @@ public class ResolverActivity extends Activity implements AdapterView.OnItemClic duplicates.clear(); } for (int k = start; k <= end; k++) { - ResolveInfo add = rList.get(k); + final ResolvedComponentInfo rci = rList.get(k); + final ResolveInfo add = rci.getResolveInfoAt(0); + final CharSequence extraInfo; if (usePkg) { - // Use application name for all entries from start to end-1 - addResolveInfo(new DisplayResolveInfo(add, roLabel, - add.activityInfo.packageName, null)); - } else { // Use package name for all entries from start to end-1 - addResolveInfo(new DisplayResolveInfo(add, roLabel, - add.activityInfo.applicationInfo.loadLabel(mPm), null)); + extraInfo = add.activityInfo.packageName; + } else { + // Use application name for all entries from start to end-1 + extraInfo = add.activityInfo.applicationInfo.loadLabel(mPm); } - updateLastChosenPosition(add); + addResolveInfoWithAlternates(rci, extraInfo, roLabel); + } + } + } + + private void addResolveInfoWithAlternates(ResolvedComponentInfo rci, + CharSequence extraInfo, CharSequence roLabel) { + final int count = rci.getCount(); + final Intent intent = rci.getIntentAt(0); + final ResolveInfo add = rci.getResolveInfoAt(0); + final Intent replaceIntent = getReplacementIntent(add.activityInfo, intent); + final DisplayResolveInfo dri = new DisplayResolveInfo(intent, add, roLabel, + extraInfo, replaceIntent); + addResolveInfo(dri); + if (replaceIntent == intent) { + // Only add alternates if we didn't get a specific replacement from + // the caller. If we have one it trumps potential alternates. + for (int i = 1, N = count; i < N; i++) { + final Intent altIntent = rci.getIntentAt(i); + dri.addAlternateSourceIntent(altIntent); } } + updateLastChosenPosition(add); } private void updateLastChosenPosition(ResolveInfo info) { if (mLastChosen != null && mLastChosen.activityInfo.packageName.equals(info.activityInfo.packageName) && mLastChosen.activityInfo.name.equals(info.activityInfo.name)) { - mLastChosenPosition = mList.size() - 1; + mLastChosenPosition = mDisplayList.size() - 1; } } @@ -1259,20 +1372,21 @@ public class ResolverActivity extends Activity implements AdapterView.OnItemClic // The first one we see gets special treatment. mOtherProfile = dri; } else { - mList.add(dri); + mDisplayList.add(dri); } } public ResolveInfo resolveInfoForPosition(int position, boolean filtered) { - return (filtered ? getItem(position) : mList.get(position)).getResolveInfo(); + return (filtered ? getItem(position) : mDisplayList.get(position)) + .getResolveInfo(); } public TargetInfo targetInfoForPosition(int position, boolean filtered) { - return filtered ? getItem(position) : mList.get(position); + return filtered ? getItem(position) : mDisplayList.get(position); } public int getCount() { - int result = mList.size(); + int result = mDisplayList.size(); if (mFilterLastUsed && mLastChosenPosition >= 0) { result--; } @@ -1283,7 +1397,7 @@ public class ResolverActivity extends Activity implements AdapterView.OnItemClic if (mFilterLastUsed && mLastChosenPosition >= 0 && position >= mLastChosenPosition) { position++; } - return mList.get(position); + return mDisplayList.get(position); } public long getItemId(int position) { @@ -1295,8 +1409,8 @@ public class ResolverActivity extends Activity implements AdapterView.OnItemClic } public boolean hasResolvedTarget(ResolveInfo info) { - for (int i = 0, N = mList.size(); i < N; i++) { - if (info.equals(mList.get(i).getResolveInfo())) { + for (int i = 0, N = mDisplayList.size(); i < N; i++) { + if (info.equals(mDisplayList.get(i).getResolveInfo())) { return true; } } @@ -1304,11 +1418,12 @@ public class ResolverActivity extends Activity implements AdapterView.OnItemClic } protected int getDisplayResolveInfoCount() { - return mList.size(); + return mDisplayList.size(); } protected DisplayResolveInfo getDisplayResolveInfo(int index) { - return mList.get(index); + // Used to query services. We only query services for primary targets, not alternates. + return mDisplayList.get(index); } public final View getView(int position, View convertView, ViewGroup parent) { @@ -1349,6 +1464,52 @@ public class ResolverActivity extends Activity implements AdapterView.OnItemClic } } + static final class ResolvedComponentInfo { + public final ComponentName name; + private final List<Intent> mIntents = new ArrayList<>(); + private final List<ResolveInfo> mResolveInfos = new ArrayList<>(); + + public ResolvedComponentInfo(ComponentName name, Intent intent, ResolveInfo info) { + this.name = name; + add(intent, info); + } + + public void add(Intent intent, ResolveInfo info) { + mIntents.add(intent); + mResolveInfos.add(info); + } + + public int getCount() { + return mIntents.size(); + } + + public Intent getIntentAt(int index) { + return index >= 0 ? mIntents.get(index) : null; + } + + public ResolveInfo getResolveInfoAt(int index) { + return index >= 0 ? mResolveInfos.get(index) : null; + } + + public int findIntent(Intent intent) { + for (int i = 0, N = mIntents.size(); i < N; i++) { + if (intent.equals(mIntents.get(i))) { + return i; + } + } + return -1; + } + + public int findResolveInfo(ResolveInfo info) { + for (int i = 0, N = mResolveInfos.size(); i < N; i++) { + if (info.equals(mResolveInfos.get(i))) { + return i; + } + } + return -1; + } + } + static class ViewHolder { public TextView text; public TextView text2; @@ -1435,7 +1596,7 @@ public class ResolverActivity extends Activity implements AdapterView.OnItemClic && match <= IntentFilter.MATCH_CATEGORY_PATH; } - class ResolverComparator implements Comparator<ResolveInfo> { + class ResolverComparator implements Comparator<ResolvedComponentInfo> { private final Collator mCollator; private final boolean mHttp; @@ -1446,7 +1607,10 @@ public class ResolverActivity extends Activity implements AdapterView.OnItemClic } @Override - public int compare(ResolveInfo lhs, ResolveInfo rhs) { + public int compare(ResolvedComponentInfo lhsp, ResolvedComponentInfo rhsp) { + final ResolveInfo lhs = lhsp.getResolveInfoAt(0); + final ResolveInfo rhs = rhsp.getResolveInfoAt(0); + // We want to put the one targeted to another user at the end of the dialog. if (lhs.targetUserId != UserHandle.USER_CURRENT) { return 1; @@ -1487,7 +1651,6 @@ public class ResolverActivity extends Activity implements AdapterView.OnItemClic if (stats != null) { return stats.getTotalTimeInForeground(); } - } return 0; } diff --git a/core/java/com/android/internal/inputmethod/InputMethodSubtypeSwitchingController.java b/core/java/com/android/internal/inputmethod/InputMethodSubtypeSwitchingController.java index 52485dd..ce94727 100644 --- a/core/java/com/android/internal/inputmethod/InputMethodSubtypeSwitchingController.java +++ b/core/java/com/android/internal/inputmethod/InputMethodSubtypeSwitchingController.java @@ -196,7 +196,7 @@ public class InputMethodSubtypeSwitchingController { } public List<ImeSubtypeListItem> getSortedInputMethodAndSubtypeList( - boolean showSubtypes, boolean inputShown, boolean isScreenLocked) { + boolean showSubtypes, boolean includeAuxiliarySubtypes, boolean isScreenLocked) { final ArrayList<ImeSubtypeListItem> imList = new ArrayList<ImeSubtypeListItem>(); final HashMap<InputMethodInfo, List<InputMethodSubtype>> immis = @@ -205,6 +205,12 @@ public class InputMethodSubtypeSwitchingController { if (immis == null || immis.size() == 0) { return Collections.emptyList(); } + if (isScreenLocked && includeAuxiliarySubtypes) { + if (DEBUG) { + Slog.w(TAG, "Auxiliary subtypes are not allowed to be shown in lock screen."); + } + includeAuxiliarySubtypes = false; + } mSortedImmis.clear(); mSortedImmis.putAll(immis); for (InputMethodInfo imi : mSortedImmis.keySet()) { @@ -227,7 +233,7 @@ public class InputMethodSubtypeSwitchingController { final String subtypeHashCode = String.valueOf(subtype.hashCode()); // We show all enabled IMEs and subtypes when an IME is shown. if (enabledSubtypeSet.contains(subtypeHashCode) - && ((inputShown && !isScreenLocked) || !subtype.isAuxiliary())) { + && (includeAuxiliarySubtypes || !subtype.isAuxiliary())) { final CharSequence subtypeLabel = subtype.overridesImplicitlyEnabledSubtype() ? null : subtype .getDisplayName(mContext, imi.getPackageName(), @@ -516,8 +522,8 @@ public class InputMethodSubtypeSwitchingController { } public List<ImeSubtypeListItem> getSortedInputMethodAndSubtypeListLocked(boolean showSubtypes, - boolean inputShown, boolean isScreenLocked) { + boolean includingAuxiliarySubtypes, boolean isScreenLocked) { return mSubtypeList.getSortedInputMethodAndSubtypeList( - showSubtypes, inputShown, isScreenLocked); + showSubtypes, includingAuxiliarySubtypes, isScreenLocked); } } diff --git a/core/java/com/android/internal/logging/MetricsLogger.java b/core/java/com/android/internal/logging/MetricsLogger.java index 6173832..9277f9b 100644 --- a/core/java/com/android/internal/logging/MetricsLogger.java +++ b/core/java/com/android/internal/logging/MetricsLogger.java @@ -18,6 +18,7 @@ package com.android.internal.logging; import android.content.Context; import android.os.Build; +import android.view.View; /** * Log all the things. @@ -33,6 +34,10 @@ public class MetricsLogger implements MetricsConstants { public static final int ACTION_BAN_APP_NOTES = 146; public static final int NOTIFICATION_ZEN_MODE_EVENT_RULE = 147; public static final int ACTION_DISMISS_ALL_NOTES = 148; + public static final int QS_DND_DETAILS = 149; + public static final int QS_BLUETOOTH_DETAILS = 150; + public static final int QS_CAST_DETAILS = 151; + public static final int QS_WIFI_DETAILS = 152; public static void visible(Context context, int category) throws IllegalArgumentException { if (Build.IS_DEBUGGABLE && category == VIEW_UNKNOWN) { @@ -41,13 +46,27 @@ public class MetricsLogger implements MetricsConstants { EventLogTags.writeSysuiViewVisibility(category, 100); } - public static void hidden(Context context, int category) { + public static void hidden(Context context, int category) throws IllegalArgumentException { if (Build.IS_DEBUGGABLE && category == VIEW_UNKNOWN) { throw new IllegalArgumentException("Must define metric category"); } EventLogTags.writeSysuiViewVisibility(category, 0); } + public static void visibility(Context context, int category, boolean visibile) + throws IllegalArgumentException { + if (visibile) { + visible(context, category); + } else { + hidden(context, category); + } + } + + public static void visibility(Context context, int category, int vis) + throws IllegalArgumentException { + visibility(context, category, vis == View.VISIBLE); + } + public static void action(Context context, int category) { action(context, category, ""); } diff --git a/core/java/android/view/PhoneFallbackEventHandler.java b/core/java/com/android/internal/policy/PhoneFallbackEventHandler.java index 350650d..2cb9c25 100644 --- a/core/java/android/view/PhoneFallbackEventHandler.java +++ b/core/java/com/android/internal/policy/PhoneFallbackEventHandler.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package android.view; +package com.android.internal.policy; import android.app.KeyguardManager; import android.app.SearchManager; @@ -28,10 +28,11 @@ import android.os.UserHandle; import android.provider.Settings; import android.telephony.TelephonyManager; import android.util.Log; -import android.view.View; -import android.view.HapticFeedbackConstants; import android.view.FallbackEventHandler; +import android.view.HapticFeedbackConstants; import android.view.KeyEvent; +import android.view.View; +import com.android.internal.policy.PhoneWindow; /** * @hide diff --git a/core/java/android/view/PhoneLayoutInflater.java b/core/java/com/android/internal/policy/PhoneLayoutInflater.java index 7d89a0b..991b6bb 100644 --- a/core/java/android/view/PhoneLayoutInflater.java +++ b/core/java/com/android/internal/policy/PhoneLayoutInflater.java @@ -14,10 +14,12 @@ * limitations under the License. */ -package android.view; +package com.android.internal.policy; import android.content.Context; import android.util.AttributeSet; +import android.view.LayoutInflater; +import android.view.View; /** * @hide diff --git a/core/java/android/view/PhoneWindow.java b/core/java/com/android/internal/policy/PhoneWindow.java index a3e7a10..a578a6e 100644 --- a/core/java/android/view/PhoneWindow.java +++ b/core/java/com/android/internal/policy/PhoneWindow.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package android.view; +package com.android.internal.policy; import static android.view.View.MeasureSpec.AT_MOST; import static android.view.View.MeasureSpec.EXACTLY; @@ -27,6 +27,34 @@ import android.app.ActivityManagerNative; import android.app.SearchManager; import android.os.UserHandle; +import android.view.ActionMode; +import android.view.ContextThemeWrapper; +import android.view.Display; +import android.view.Gravity; +import android.view.IRotationWatcher.Stub; +import android.view.IWindowManager; +import android.view.InputDevice; +import android.view.InputEvent; +import android.view.InputQueue; +import android.view.KeyCharacterMap; +import android.view.KeyEvent; +import android.view.LayoutInflater; +import android.view.Menu; +import android.view.MenuItem; +import android.view.MotionEvent; +import android.view.SearchEvent; +import android.view.SurfaceHolder.Callback2; +import android.view.View; +import android.view.ViewConfiguration; +import android.view.ViewGroup; +import android.view.ViewManager; +import android.view.ViewParent; +import android.view.ViewRootImpl; +import android.view.ViewStub; +import android.view.ViewTreeObserver.OnPreDrawListener; +import android.view.Window; +import android.view.WindowInsets; +import android.view.WindowManager; import com.android.internal.R; import com.android.internal.util.ScreenShapeHelper; import com.android.internal.view.FloatingActionMode; @@ -67,7 +95,6 @@ import android.os.Parcel; import android.os.Parcelable; import android.os.RemoteException; import android.os.ServiceManager; -import android.os.SystemProperties; import android.transition.Scene; import android.transition.Transition; import android.transition.TransitionInflater; @@ -140,7 +167,7 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback { private ViewGroup mContentRoot; - SurfaceHolder.Callback2 mTakeSurfaceCallback; + Callback2 mTakeSurfaceCallback; InputQueue.Callback mTakeInputQueueCallback; @@ -427,7 +454,7 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback { } @Override - public void takeSurface(SurfaceHolder.Callback2 callback) { + public void takeSurface(Callback2 callback) { mTakeSurfaceCallback = callback; } @@ -2181,7 +2208,7 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback { private ActionBarContextView mPrimaryActionModeView; private PopupWindow mPrimaryActionModePopup; private Runnable mShowPrimaryActionModePopup; - private ViewTreeObserver.OnPreDrawListener mFloatingToolbarPreDrawListener; + private OnPreDrawListener mFloatingToolbarPreDrawListener; private View mFloatingActionModeOriginatingView; private FloatingToolbar mFloatingToolbar; @@ -3354,7 +3381,7 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback { mContext, callback, originatingView, mFloatingToolbar); mFloatingActionModeOriginatingView = originatingView; mFloatingToolbarPreDrawListener = - new ViewTreeObserver.OnPreDrawListener() { + new OnPreDrawListener() { @Override public boolean onPreDraw() { mode.updateViewLocationInWindow(); @@ -4718,7 +4745,7 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback { } - static class RotationWatcher extends IRotationWatcher.Stub { + static class RotationWatcher extends Stub { private Handler mHandler; private final Runnable mRotationChanged = new Runnable() { public void run() { diff --git a/core/java/com/android/internal/transition/EpicenterClipReveal.java b/core/java/com/android/internal/transition/EpicenterClipReveal.java deleted file mode 100644 index 1a6736a..0000000 --- a/core/java/com/android/internal/transition/EpicenterClipReveal.java +++ /dev/null @@ -1,233 +0,0 @@ -/* - * Copyright (C) 2015 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.android.internal.transition; - -import android.animation.Animator; -import android.animation.AnimatorListenerAdapter; -import android.animation.AnimatorSet; -import android.animation.ObjectAnimator; -import android.animation.RectEvaluator; -import android.animation.TimeInterpolator; -import android.content.Context; -import android.content.res.TypedArray; -import android.graphics.Rect; -import android.transition.TransitionValues; -import android.transition.Visibility; -import android.util.AttributeSet; -import android.util.Property; -import android.view.View; -import android.view.ViewGroup; -import android.view.animation.AnimationUtils; -import android.view.animation.PathInterpolator; - -import com.android.internal.R; - -/** - * EpicenterClipReveal captures the {@link View#getClipBounds()} before and - * after the scene change and animates between those and the epicenter bounds - * during a visibility transition. - */ -public class EpicenterClipReveal extends Visibility { - private static final String PROPNAME_CLIP = "android:epicenterReveal:clip"; - private static final String PROPNAME_BOUNDS = "android:epicenterReveal:bounds"; - - private final TimeInterpolator mInterpolatorX; - private final TimeInterpolator mInterpolatorY; - private final boolean mCenterClipBounds; - - public EpicenterClipReveal() { - mInterpolatorX = null; - mInterpolatorY = null; - mCenterClipBounds = false; - } - - public EpicenterClipReveal(Context context, AttributeSet attrs) { - super(context, attrs); - - final TypedArray a = context.obtainStyledAttributes(attrs, - R.styleable.EpicenterClipReveal, 0, 0); - - mCenterClipBounds = a.getBoolean(R.styleable.EpicenterClipReveal_centerClipBounds, false); - - final int interpolatorX = a.getResourceId(R.styleable.EpicenterClipReveal_interpolatorX, 0); - if (interpolatorX != 0) { - mInterpolatorX = AnimationUtils.loadInterpolator(context, interpolatorX); - } else { - mInterpolatorX = TransitionConstants.LINEAR_OUT_SLOW_IN; - } - - final int interpolatorY = a.getResourceId(R.styleable.EpicenterClipReveal_interpolatorY, 0); - if (interpolatorY != 0) { - mInterpolatorY = AnimationUtils.loadInterpolator(context, interpolatorY); - } else { - mInterpolatorY = TransitionConstants.FAST_OUT_SLOW_IN; - } - - a.recycle(); - } - - @Override - public void captureStartValues(TransitionValues transitionValues) { - super.captureStartValues(transitionValues); - captureValues(transitionValues); - } - - @Override - public void captureEndValues(TransitionValues transitionValues) { - super.captureEndValues(transitionValues); - captureValues(transitionValues); - } - - private void captureValues(TransitionValues values) { - final View view = values.view; - if (view.getVisibility() == View.GONE) { - return; - } - - final Rect clip = view.getClipBounds(); - values.values.put(PROPNAME_CLIP, clip); - - if (clip == null) { - final Rect bounds = new Rect(0, 0, view.getWidth(), view.getHeight()); - values.values.put(PROPNAME_BOUNDS, bounds); - } - } - - @Override - public Animator onAppear(ViewGroup sceneRoot, View view, - TransitionValues startValues, TransitionValues endValues) { - if (endValues == null) { - return null; - } - - final Rect end = getBestRect(endValues); - final Rect start = getEpicenterOrCenter(end); - - // Prepare the view. - view.setClipBounds(start); - - return createRectAnimator(view, start, end, endValues, mInterpolatorX, mInterpolatorY); - } - - @Override - public Animator onDisappear(ViewGroup sceneRoot, View view, - TransitionValues startValues, TransitionValues endValues) { - if (startValues == null) { - return null; - } - - final Rect start = getBestRect(startValues); - final Rect end = getEpicenterOrCenter(start); - - // Prepare the view. - view.setClipBounds(start); - - return createRectAnimator(view, start, end, endValues, mInterpolatorX, mInterpolatorY); - } - - private Rect getEpicenterOrCenter(Rect bestRect) { - final Rect epicenter = getEpicenter(); - if (epicenter != null) { - // Translate the clip bounds to be centered within the target bounds. - if (mCenterClipBounds) { - final int offsetX = bestRect.centerX() - epicenter.centerX(); - final int offsetY = bestRect.centerY() - epicenter.centerY(); - epicenter.offset(offsetX, offsetY); - } - return epicenter; - } - - final int centerX = bestRect.centerX(); - final int centerY = bestRect.centerY(); - return new Rect(centerX, centerY, centerX, centerY); - } - - private Rect getBestRect(TransitionValues values) { - final Rect clipRect = (Rect) values.values.get(PROPNAME_CLIP); - if (clipRect == null) { - return (Rect) values.values.get(PROPNAME_BOUNDS); - } - return clipRect; - } - - private static Animator createRectAnimator(final View view, Rect start, Rect end, - TransitionValues endValues, TimeInterpolator interpolatorX, - TimeInterpolator interpolatorY) { - final RectEvaluator evaluator = new RectEvaluator(new Rect()); - final Rect terminalClip = (Rect) endValues.values.get(PROPNAME_CLIP); - - final ClipDimenProperty propX = new ClipDimenProperty(ClipDimenProperty.TARGET_X); - final ObjectAnimator animX = ObjectAnimator.ofObject(view, propX, evaluator, start, end); - if (interpolatorX != null) { - animX.setInterpolator(interpolatorX); - } - - final ClipDimenProperty propY = new ClipDimenProperty(ClipDimenProperty.TARGET_Y); - final ObjectAnimator animY = ObjectAnimator.ofObject(view, propY, evaluator, start, end); - if (interpolatorY != null) { - animY.setInterpolator(interpolatorY); - } - - final AnimatorSet animSet = new AnimatorSet(); - animSet.playTogether(animX, animY); - animSet.addListener(new AnimatorListenerAdapter() { - @Override - public void onAnimationEnd(Animator animation) { - view.setClipBounds(terminalClip); - } - }); - return animSet; - } - - private static class ClipDimenProperty extends Property<View, Rect> { - public static final char TARGET_X = 'x'; - public static final char TARGET_Y = 'y'; - - private final Rect mTempRect = new Rect(); - - private final int mTargetDimension; - - public ClipDimenProperty(char targetDimension) { - super(Rect.class, "clip_bounds_" + targetDimension); - - mTargetDimension = targetDimension; - } - - @Override - public Rect get(View object) { - final Rect tempRect = mTempRect; - if (!object.getClipBounds(tempRect)) { - tempRect.setEmpty(); - } - return tempRect; - } - - @Override - public void set(View object, Rect value) { - final Rect tempRect = mTempRect; - if (object.getClipBounds(tempRect)) { - if (mTargetDimension == TARGET_X) { - tempRect.left = value.left; - tempRect.right = value.right; - } else { - tempRect.top = value.top; - tempRect.bottom = value.bottom; - } - object.setClipBounds(tempRect); - } - } - } -} diff --git a/core/java/com/android/internal/transition/EpicenterTranslate.java b/core/java/com/android/internal/transition/EpicenterTranslate.java deleted file mode 100644 index eac3ff8..0000000 --- a/core/java/com/android/internal/transition/EpicenterTranslate.java +++ /dev/null @@ -1,191 +0,0 @@ -/* - * Copyright (C) 2015 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.android.internal.transition; - -import com.android.internal.R; - -import android.animation.Animator; -import android.animation.AnimatorSet; -import android.animation.ObjectAnimator; -import android.animation.TimeInterpolator; -import android.content.Context; -import android.content.res.TypedArray; -import android.graphics.Rect; -import android.transition.TransitionValues; -import android.transition.Visibility; -import android.util.AttributeSet; -import android.view.View; -import android.view.ViewGroup; -import android.view.animation.AnimationUtils; - -/** - * EpicenterTranslate captures the {@link View#getTranslationX()} and - * {@link View#getTranslationY()} before and after the scene change and - * animates between those and the epicenter's center during a visibility - * transition. - */ -public class EpicenterTranslate extends Visibility { - private static final String PROPNAME_BOUNDS = "android:epicenterReveal:bounds"; - private static final String PROPNAME_TRANSLATE_X = "android:epicenterReveal:translateX"; - private static final String PROPNAME_TRANSLATE_Y = "android:epicenterReveal:translateY"; - private static final String PROPNAME_TRANSLATE_Z = "android:epicenterReveal:translateZ"; - private static final String PROPNAME_Z = "android:epicenterReveal:z"; - - private final TimeInterpolator mInterpolatorX; - private final TimeInterpolator mInterpolatorY; - private final TimeInterpolator mInterpolatorZ; - - public EpicenterTranslate() { - mInterpolatorX = null; - mInterpolatorY = null; - mInterpolatorZ = null; - } - - public EpicenterTranslate(Context context, AttributeSet attrs) { - super(context, attrs); - - final TypedArray a = context.obtainStyledAttributes(attrs, - R.styleable.EpicenterTranslate, 0, 0); - - final int interpolatorX = a.getResourceId(R.styleable.EpicenterTranslate_interpolatorX, 0); - if (interpolatorX != 0) { - mInterpolatorX = AnimationUtils.loadInterpolator(context, interpolatorX); - } else { - mInterpolatorX = TransitionConstants.FAST_OUT_SLOW_IN; - } - - final int interpolatorY = a.getResourceId(R.styleable.EpicenterTranslate_interpolatorY, 0); - if (interpolatorY != 0) { - mInterpolatorY = AnimationUtils.loadInterpolator(context, interpolatorY); - } else { - mInterpolatorY = TransitionConstants.FAST_OUT_SLOW_IN; - } - - final int interpolatorZ = a.getResourceId(R.styleable.EpicenterTranslate_interpolatorZ, 0); - if (interpolatorZ != 0) { - mInterpolatorZ = AnimationUtils.loadInterpolator(context, interpolatorZ); - } else { - mInterpolatorZ = TransitionConstants.FAST_OUT_SLOW_IN; - } - - a.recycle(); - } - - @Override - public void captureStartValues(TransitionValues transitionValues) { - super.captureStartValues(transitionValues); - captureValues(transitionValues); - } - - @Override - public void captureEndValues(TransitionValues transitionValues) { - super.captureEndValues(transitionValues); - captureValues(transitionValues); - } - - private void captureValues(TransitionValues values) { - final View view = values.view; - if (view.getVisibility() == View.GONE) { - return; - } - - final Rect bounds = new Rect(0, 0, view.getWidth(), view.getHeight()); - values.values.put(PROPNAME_BOUNDS, bounds); - values.values.put(PROPNAME_TRANSLATE_X, view.getTranslationX()); - values.values.put(PROPNAME_TRANSLATE_Y, view.getTranslationY()); - values.values.put(PROPNAME_TRANSLATE_Z, view.getTranslationZ()); - values.values.put(PROPNAME_Z, view.getZ()); - } - - @Override - public Animator onAppear(ViewGroup sceneRoot, View view, - TransitionValues startValues, TransitionValues endValues) { - if (endValues == null) { - return null; - } - - final Rect end = (Rect) endValues.values.get(PROPNAME_BOUNDS); - final Rect start = getEpicenterOrCenter(end); - final float startX = start.centerX() - end.centerX(); - final float startY = start.centerY() - end.centerY(); - final float startZ = 0 - (float) endValues.values.get(PROPNAME_Z); - - // Translate the view to be centered on the epicenter. - view.setTranslationX(startX); - view.setTranslationY(startY); - view.setTranslationZ(startZ); - - final float endX = (float) endValues.values.get(PROPNAME_TRANSLATE_X); - final float endY = (float) endValues.values.get(PROPNAME_TRANSLATE_Y); - final float endZ = (float) endValues.values.get(PROPNAME_TRANSLATE_Z); - return createAnimator(view, startX, startY, startZ, endX, endY, endZ, - mInterpolatorX, mInterpolatorY, mInterpolatorZ); - } - - @Override - public Animator onDisappear(ViewGroup sceneRoot, View view, - TransitionValues startValues, TransitionValues endValues) { - if (startValues == null) { - return null; - } - - final Rect start = (Rect) endValues.values.get(PROPNAME_BOUNDS); - final Rect end = getEpicenterOrCenter(start); - final float endX = end.centerX() - start.centerX(); - final float endY = end.centerY() - start.centerY(); - final float endZ = 0 - (float) startValues.values.get(PROPNAME_Z); - - final float startX = (float) endValues.values.get(PROPNAME_TRANSLATE_X); - final float startY = (float) endValues.values.get(PROPNAME_TRANSLATE_Y); - final float startZ = (float) endValues.values.get(PROPNAME_TRANSLATE_Z); - return createAnimator(view, startX, startY, startZ, endX, endY, endZ, - mInterpolatorX, mInterpolatorY, mInterpolatorZ); - } - - private Rect getEpicenterOrCenter(Rect bestRect) { - final Rect epicenter = getEpicenter(); - if (epicenter != null) { - return epicenter; - } - - final int centerX = bestRect.centerX(); - final int centerY = bestRect.centerY(); - return new Rect(centerX, centerY, centerX, centerY); - } - - private static Animator createAnimator(final View view, float startX, float startY, - float startZ, float endX, float endY, float endZ, TimeInterpolator interpolatorX, - TimeInterpolator interpolatorY, TimeInterpolator interpolatorZ) { - final ObjectAnimator animX = ObjectAnimator.ofFloat(view, View.TRANSLATION_X, startX, endX); - if (interpolatorX != null) { - animX.setInterpolator(interpolatorX); - } - - final ObjectAnimator animY = ObjectAnimator.ofFloat(view, View.TRANSLATION_Y, startY, endY); - if (interpolatorY != null) { - animY.setInterpolator(interpolatorY); - } - - final ObjectAnimator animZ = ObjectAnimator.ofFloat(view, View.TRANSLATION_Z, startZ, endZ); - if (interpolatorZ != null) { - animZ.setInterpolator(interpolatorZ); - } - - final AnimatorSet animSet = new AnimatorSet(); - animSet.playTogether(animX, animY, animZ); - return animSet; - } -} diff --git a/core/java/com/android/internal/transition/EpicenterTranslateClipReveal.java b/core/java/com/android/internal/transition/EpicenterTranslateClipReveal.java new file mode 100644 index 0000000..2c10297 --- /dev/null +++ b/core/java/com/android/internal/transition/EpicenterTranslateClipReveal.java @@ -0,0 +1,328 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.internal.transition; + +import android.animation.Animator; +import android.animation.AnimatorListenerAdapter; +import android.animation.AnimatorSet; +import android.animation.ObjectAnimator; +import android.animation.TimeInterpolator; +import android.animation.TypeEvaluator; +import android.content.Context; +import android.content.res.TypedArray; +import android.graphics.Rect; +import android.transition.TransitionValues; +import android.transition.Visibility; +import android.util.AttributeSet; +import android.util.Property; +import android.view.View; +import android.view.ViewGroup; +import android.view.animation.AnimationUtils; + +import com.android.internal.R; + +/** + * EpicenterTranslateClipReveal captures the clip bounds and translation values + * before and after the scene change and animates between those and the + * epicenter bounds during a visibility transition. + */ +public class EpicenterTranslateClipReveal extends Visibility { + private static final String PROPNAME_CLIP = "android:epicenterReveal:clip"; + private static final String PROPNAME_BOUNDS = "android:epicenterReveal:bounds"; + private static final String PROPNAME_TRANSLATE_X = "android:epicenterReveal:translateX"; + private static final String PROPNAME_TRANSLATE_Y = "android:epicenterReveal:translateY"; + private static final String PROPNAME_TRANSLATE_Z = "android:epicenterReveal:translateZ"; + private static final String PROPNAME_Z = "android:epicenterReveal:z"; + + private final TimeInterpolator mInterpolatorX; + private final TimeInterpolator mInterpolatorY; + private final TimeInterpolator mInterpolatorZ; + + public EpicenterTranslateClipReveal() { + mInterpolatorX = null; + mInterpolatorY = null; + mInterpolatorZ = null; + } + + public EpicenterTranslateClipReveal(Context context, AttributeSet attrs) { + super(context, attrs); + + final TypedArray a = context.obtainStyledAttributes(attrs, + R.styleable.EpicenterTranslateClipReveal, 0, 0); + + final int interpolatorX = a.getResourceId( + R.styleable.EpicenterTranslateClipReveal_interpolatorX, 0); + if (interpolatorX != 0) { + mInterpolatorX = AnimationUtils.loadInterpolator(context, interpolatorX); + } else { + mInterpolatorX = TransitionConstants.LINEAR_OUT_SLOW_IN; + } + + final int interpolatorY = a.getResourceId( + R.styleable.EpicenterTranslateClipReveal_interpolatorY, 0); + if (interpolatorY != 0) { + mInterpolatorY = AnimationUtils.loadInterpolator(context, interpolatorY); + } else { + mInterpolatorY = TransitionConstants.FAST_OUT_SLOW_IN; + } + + final int interpolatorZ = a.getResourceId( + R.styleable.EpicenterTranslateClipReveal_interpolatorZ, 0); + if (interpolatorZ != 0) { + mInterpolatorZ = AnimationUtils.loadInterpolator(context, interpolatorZ); + } else { + mInterpolatorZ = TransitionConstants.FAST_OUT_SLOW_IN; + } + + a.recycle(); + } + + @Override + public void captureStartValues(TransitionValues transitionValues) { + super.captureStartValues(transitionValues); + captureValues(transitionValues); + } + + @Override + public void captureEndValues(TransitionValues transitionValues) { + super.captureEndValues(transitionValues); + captureValues(transitionValues); + } + + private void captureValues(TransitionValues values) { + final View view = values.view; + if (view.getVisibility() == View.GONE) { + return; + } + + final Rect bounds = new Rect(0, 0, view.getWidth(), view.getHeight()); + values.values.put(PROPNAME_BOUNDS, bounds); + values.values.put(PROPNAME_TRANSLATE_X, view.getTranslationX()); + values.values.put(PROPNAME_TRANSLATE_Y, view.getTranslationY()); + values.values.put(PROPNAME_TRANSLATE_Z, view.getTranslationZ()); + values.values.put(PROPNAME_Z, view.getZ()); + + final Rect clip = view.getClipBounds(); + values.values.put(PROPNAME_CLIP, clip); + } + + @Override + public Animator onAppear(ViewGroup sceneRoot, View view, + TransitionValues startValues, TransitionValues endValues) { + if (endValues == null) { + return null; + } + + final Rect endBounds = (Rect) endValues.values.get(PROPNAME_BOUNDS); + final Rect startBounds = getEpicenterOrCenter(endBounds); + final float startX = startBounds.centerX() - endBounds.centerX(); + final float startY = startBounds.centerY() - endBounds.centerY(); + final float startZ = 0 - (float) endValues.values.get(PROPNAME_Z); + + // Translate the view to be centered on the epicenter. + view.setTranslationX(startX); + view.setTranslationY(startY); + view.setTranslationZ(startZ); + + final float endX = (float) endValues.values.get(PROPNAME_TRANSLATE_X); + final float endY = (float) endValues.values.get(PROPNAME_TRANSLATE_Y); + final float endZ = (float) endValues.values.get(PROPNAME_TRANSLATE_Z); + + final Rect endClip = getBestRect(endValues); + final Rect startClip = getEpicenterOrCenter(endClip); + + // Prepare the view. + view.setClipBounds(startClip); + + final State startStateX = new State(startClip.left, startClip.right, startX); + final State endStateX = new State(endClip.left, endClip.right, endX); + final State startStateY = new State(startClip.top, startClip.bottom, startY); + final State endStateY = new State(endClip.top, endClip.bottom, endY); + + return createRectAnimator(view, startStateX, startStateY, startZ, endStateX, endStateY, + endZ, endValues, mInterpolatorX, mInterpolatorY, mInterpolatorZ); + } + + @Override + public Animator onDisappear(ViewGroup sceneRoot, View view, + TransitionValues startValues, TransitionValues endValues) { + if (startValues == null) { + return null; + } + + final Rect startBounds = (Rect) endValues.values.get(PROPNAME_BOUNDS); + final Rect endBounds = getEpicenterOrCenter(startBounds); + final float endX = endBounds.centerX() - startBounds.centerX(); + final float endY = endBounds.centerY() - startBounds.centerY(); + final float endZ = 0 - (float) startValues.values.get(PROPNAME_Z); + + final float startX = (float) endValues.values.get(PROPNAME_TRANSLATE_X); + final float startY = (float) endValues.values.get(PROPNAME_TRANSLATE_Y); + final float startZ = (float) endValues.values.get(PROPNAME_TRANSLATE_Z); + + final Rect startClip = getBestRect(startValues); + final Rect endClip = getEpicenterOrCenter(startClip); + + // Prepare the view. + view.setClipBounds(startClip); + + final State startStateX = new State(startClip.left, startClip.right, startX); + final State endStateX = new State(endClip.left, endClip.right, endX); + final State startStateY = new State(startClip.top, startClip.bottom, startY); + final State endStateY = new State(endClip.top, endClip.bottom, endY); + + return createRectAnimator(view, startStateX, startStateY, startZ, endStateX, endStateY, + endZ, endValues, mInterpolatorX, mInterpolatorY, mInterpolatorZ); + } + + private Rect getEpicenterOrCenter(Rect bestRect) { + final Rect epicenter = getEpicenter(); + if (epicenter != null) { + return epicenter; + } + + final int centerX = bestRect.centerX(); + final int centerY = bestRect.centerY(); + return new Rect(centerX, centerY, centerX, centerY); + } + + private Rect getBestRect(TransitionValues values) { + final Rect clipRect = (Rect) values.values.get(PROPNAME_CLIP); + if (clipRect == null) { + return (Rect) values.values.get(PROPNAME_BOUNDS); + } + return clipRect; + } + + private static Animator createRectAnimator(final View view, State startX, State startY, + float startZ, State endX, State endY, float endZ, TransitionValues endValues, + TimeInterpolator interpolatorX, TimeInterpolator interpolatorY, + TimeInterpolator interpolatorZ) { + final StateEvaluator evaluator = new StateEvaluator(); + + final ObjectAnimator animZ = ObjectAnimator.ofFloat(view, View.TRANSLATION_Z, startZ, endZ); + if (interpolatorZ != null) { + animZ.setInterpolator(interpolatorZ); + } + + final StateProperty propX = new StateProperty(StateProperty.TARGET_X); + final ObjectAnimator animX = ObjectAnimator.ofObject(view, propX, evaluator, startX, endX); + if (interpolatorX != null) { + animX.setInterpolator(interpolatorX); + } + + final StateProperty propY = new StateProperty(StateProperty.TARGET_Y); + final ObjectAnimator animY = ObjectAnimator.ofObject(view, propY, evaluator, startY, endY); + if (interpolatorY != null) { + animY.setInterpolator(interpolatorY); + } + + final Rect terminalClip = (Rect) endValues.values.get(PROPNAME_CLIP); + final AnimatorListenerAdapter animatorListener = new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animation) { + view.setClipBounds(terminalClip); + } + }; + + final AnimatorSet animSet = new AnimatorSet(); + animSet.playTogether(animX, animY, animZ); + animSet.addListener(animatorListener); + return animSet; + } + + private static class State { + int lower; + int upper; + float trans; + + public State() {} + + public State(int lower, int upper, float trans) { + this.lower = lower; + this.upper = upper; + this.trans = trans; + } + } + + private static class StateEvaluator implements TypeEvaluator<State> { + private final State mTemp = new State(); + + @Override + public State evaluate(float fraction, State startValue, State endValue) { + mTemp.upper = startValue.upper + (int) ((endValue.upper - startValue.upper) * fraction); + mTemp.lower = startValue.lower + (int) ((endValue.lower - startValue.lower) * fraction); + mTemp.trans = startValue.trans + (int) ((endValue.trans - startValue.trans) * fraction); + return mTemp; + } + } + + private static class StateProperty extends Property<View, State> { + public static final char TARGET_X = 'x'; + public static final char TARGET_Y = 'y'; + + private final Rect mTempRect = new Rect(); + private final State mTempState = new State(); + + private final int mTargetDimension; + + public StateProperty(char targetDimension) { + super(State.class, "state_" + targetDimension); + + mTargetDimension = targetDimension; + } + + @Override + public State get(View object) { + final Rect tempRect = mTempRect; + if (!object.getClipBounds(tempRect)) { + tempRect.setEmpty(); + } + final State tempState = mTempState; + if (mTargetDimension == TARGET_X) { + tempState.trans = object.getTranslationX(); + tempState.lower = tempRect.left + (int) tempState.trans; + tempState.upper = tempRect.right + (int) tempState.trans; + } else { + tempState.trans = object.getTranslationY(); + tempState.lower = tempRect.top + (int) tempState.trans; + tempState.upper = tempRect.bottom + (int) tempState.trans; + } + return tempState; + } + + @Override + public void set(View object, State value) { + final Rect tempRect = mTempRect; + if (object.getClipBounds(tempRect)) { + if (mTargetDimension == TARGET_X) { + tempRect.left = value.lower - (int) value.trans; + tempRect.right = value.upper - (int) value.trans; + } else { + tempRect.top = value.lower - (int) value.trans; + tempRect.bottom = value.upper - (int) value.trans; + } + object.setClipBounds(tempRect); + } + + if (mTargetDimension == TARGET_X) { + object.setTranslationX(value.trans); + } else { + object.setTranslationY(value.trans); + } + } + } +} diff --git a/core/java/com/android/internal/util/AsyncChannel.java b/core/java/com/android/internal/util/AsyncChannel.java index 34f62ba..bd0e6ce 100644 --- a/core/java/com/android/internal/util/AsyncChannel.java +++ b/core/java/com/android/internal/util/AsyncChannel.java @@ -462,10 +462,8 @@ public class AsyncChannel { } catch(Exception e) { } // Tell source we're disconnected. - if (mSrcHandler != null) { - replyDisconnected(STATUS_SUCCESSFUL); - mSrcHandler = null; - } + replyDisconnected(STATUS_SUCCESSFUL); + mSrcHandler = null; // Unlink only when bindService isn't used if (mConnection == null && mDstMessenger != null && mDeathMonitor!= null) { mDstMessenger.getBinder().unlinkToDeath(mDeathMonitor, 0); @@ -871,6 +869,8 @@ public class AsyncChannel { * @param status to be stored in msg.arg1 */ private void replyDisconnected(int status) { + // Can't reply if already disconnected. Avoid NullPointerException. + if (mSrcHandler == null) return; Message msg = mSrcHandler.obtainMessage(CMD_CHANNEL_DISCONNECTED); msg.arg1 = status; msg.obj = this; diff --git a/core/java/com/android/internal/util/CallbackRegistry.java b/core/java/com/android/internal/util/CallbackRegistry.java new file mode 100644 index 0000000..0f228d4 --- /dev/null +++ b/core/java/com/android/internal/util/CallbackRegistry.java @@ -0,0 +1,395 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.internal.util; + +import java.util.ArrayList; +import java.util.List; + +/** + * Tracks callbacks for the event. This class supports reentrant modification + * of the callbacks during notification without adversely disrupting notifications. + * A common pattern for callbacks is to receive a notification and then remove + * themselves. This class handles this behavior with constant memory under + * most circumstances. + * + * <p>A subclass of {@link CallbackRegistry.NotifierCallback} must be passed to + * the constructor to define how notifications should be called. That implementation + * does the actual notification on the listener.</p> + * + * <p>This class supports only callbacks with at most two parameters. + * Typically, these are the notification originator and a parameter, but these may + * be used as required. If more than two parameters are required or primitive types + * must be used, <code>A</code> should be some kind of containing structure that + * the subclass may reuse between notifications.</p> + * + * @param <C> The callback type. + * @param <T> The notification sender type. Typically this is the containing class. + * @param <A> Opaque argument used to pass additional data beyond an int. + */ +public class CallbackRegistry<C, T, A> implements Cloneable { + private static final String TAG = "CallbackRegistry"; + + /** An ordered collection of listeners waiting to be notified. */ + private List<C> mCallbacks = new ArrayList<C>(); + + /** + * A bit flag for the first 64 listeners that are removed during notification. + * The lowest significant bit corresponds to the 0th index into mCallbacks. + * For a small number of callbacks, no additional array of objects needs to + * be allocated. + */ + private long mFirst64Removed = 0x0; + + /** + * Bit flags for the remaining callbacks that are removed during notification. + * When there are more than 64 callbacks and one is marked for removal, a dynamic + * array of bits are allocated for the callbacks. + */ + private long[] mRemainderRemoved; + + /** + * The reentrancy level of the notification. When we notify a callback, it may cause + * further notifications. The reentrancy level must be tracked to let us clean up + * the callback state when all notifications have been processed. + */ + private int mNotificationLevel; + + /** The notification mechanism for notifying an event. */ + private final NotifierCallback<C, T, A> mNotifier; + + /** + * Creates an EventRegistry that notifies the event with notifier. + * @param notifier The class to use to notify events. + */ + public CallbackRegistry(NotifierCallback<C, T, A> notifier) { + mNotifier = notifier; + } + + /** + * Notify all callbacks. + * + * @param sender The originator. This is an opaque parameter passed to + * {@link CallbackRegistry.NotifierCallback#onNotifyCallback(Object, Object, int, A)} + * @param arg An opaque parameter passed to + * {@link CallbackRegistry.NotifierCallback#onNotifyCallback(Object, Object, int, A)} + * @param arg2 An opaque parameter passed to + * {@link CallbackRegistry.NotifierCallback#onNotifyCallback(Object, Object, int, A)} + */ + public synchronized void notifyCallbacks(T sender, int arg, A arg2) { + mNotificationLevel++; + notifyRecurseLocked(sender, arg, arg2); + mNotificationLevel--; + if (mNotificationLevel == 0) { + if (mRemainderRemoved != null) { + for (int i = mRemainderRemoved.length - 1; i >= 0; i--) { + final long removedBits = mRemainderRemoved[i]; + if (removedBits != 0) { + removeRemovedCallbacks((i + 1) * Long.SIZE, removedBits); + mRemainderRemoved[i] = 0; + } + } + } + if (mFirst64Removed != 0) { + removeRemovedCallbacks(0, mFirst64Removed); + mFirst64Removed = 0; + } + } + } + + /** + * Notify up to the first Long.SIZE callbacks that don't have a bit set in <code>removed</code>. + * + * @param sender The originator. This is an opaque parameter passed to + * {@link CallbackRegistry.NotifierCallback#onNotifyCallback(Object, Object, int, A)} + * @param arg An opaque parameter passed to + * {@link CallbackRegistry.NotifierCallback#onNotifyCallback(Object, Object, int, A)} + * @param arg2 An opaque parameter passed to + * {@link CallbackRegistry.NotifierCallback#onNotifyCallback(Object, Object, int, A)} + */ + private void notifyFirst64Locked(T sender, int arg, A arg2) { + final int maxNotified = Math.min(Long.SIZE, mCallbacks.size()); + notifyCallbacksLocked(sender, arg, arg2, 0, maxNotified, mFirst64Removed); + } + + /** + * Notify all callbacks using a recursive algorithm to avoid allocating on the heap. + * This part captures the callbacks beyond Long.SIZE that have no bits allocated for + * removal before it recurses into {@link #notifyRemainderLocked(Object, int, A, int)}. + * <p> + * Recursion is used to avoid allocating temporary state on the heap. Each stack has one + * long (64 callbacks) worth of information of which has been removed. + * + * @param sender The originator. This is an opaque parameter passed to + * {@link CallbackRegistry.NotifierCallback#onNotifyCallback(Object, Object, int, A)} + * @param arg An opaque parameter passed to + * {@link CallbackRegistry.NotifierCallback#onNotifyCallback(Object, Object, int, A)} + * @param arg2 An opaque parameter passed to + * {@link CallbackRegistry.NotifierCallback#onNotifyCallback(Object, Object, int, A)} + */ + private void notifyRecurseLocked(T sender, int arg, A arg2) { + final int callbackCount = mCallbacks.size(); + final int remainderIndex = mRemainderRemoved == null ? -1 : mRemainderRemoved.length - 1; + + // Now we've got all callbacks that have no mRemainderRemoved value, so notify the + // others. + notifyRemainderLocked(sender, arg, arg2, remainderIndex); + + // notifyRemainderLocked notifies all at maxIndex, so we'd normally start at maxIndex + 1 + // However, we must also keep track of those in mFirst64Removed, so we add 2 instead: + final int startCallbackIndex = (remainderIndex + 2) * Long.SIZE; + + // The remaining have no bit set + notifyCallbacksLocked(sender, arg, arg2, startCallbackIndex, callbackCount, 0); + } + + /** + * Notify callbacks that have mRemainderRemoved bits set for remainderIndex. If + * remainderIndex is -1, the first 64 will be notified instead. + * + * @param sender The originator. This is an opaque parameter passed to + * {@link CallbackRegistry.NotifierCallback#onNotifyCallback(Object, Object, int, A)} + * @param arg An opaque parameter passed to + * {@link CallbackRegistry.NotifierCallback#onNotifyCallback(Object, Object, int, A)} + * @param arg2 An opaque parameter passed to + * {@link CallbackRegistry.NotifierCallback#onNotifyCallback(Object, Object, int, A)} + * @param remainderIndex The index into mRemainderRemoved that should be notified. + */ + private void notifyRemainderLocked(T sender, int arg, A arg2, int remainderIndex) { + if (remainderIndex < 0) { + notifyFirst64Locked(sender, arg, arg2); + } else { + final long bits = mRemainderRemoved[remainderIndex]; + final int startIndex = (remainderIndex + 1) * Long.SIZE; + final int endIndex = Math.min(mCallbacks.size(), startIndex + Long.SIZE); + notifyRemainderLocked(sender, arg, arg2, remainderIndex - 1); + notifyCallbacksLocked(sender, arg, arg2, startIndex, endIndex, bits); + } + } + + /** + * Notify callbacks from startIndex to endIndex, using bits as the bit status + * for whether they have been removed or not. bits should be from mRemainderRemoved or + * mFirst64Removed. bits set to 0 indicates that all callbacks from startIndex to + * endIndex should be notified. + * + * @param sender The originator. This is an opaque parameter passed to + * {@link CallbackRegistry.NotifierCallback#onNotifyCallback(Object, Object, int, A)} + * @param arg An opaque parameter passed to + * {@link CallbackRegistry.NotifierCallback#onNotifyCallback(Object, Object, int, A)} + * @param arg2 An opaque parameter passed to + * {@link CallbackRegistry.NotifierCallback#onNotifyCallback(Object, Object, int, A)} + * @param startIndex The index into the mCallbacks to start notifying. + * @param endIndex One past the last index into mCallbacks to notify. + * @param bits A bit field indicating which callbacks have been removed and shouldn't + * be notified. + */ + private void notifyCallbacksLocked(T sender, int arg, A arg2, final int startIndex, + final int endIndex, final long bits) { + long bitMask = 1; + for (int i = startIndex; i < endIndex; i++) { + if ((bits & bitMask) == 0) { + mNotifier.onNotifyCallback(mCallbacks.get(i), sender, arg, arg2); + } + bitMask <<= 1; + } + } + + /** + * Add a callback to be notified. If the callback is already in the list, another won't + * be added. This does not affect current notifications. + * @param callback The callback to add. + */ + public synchronized void add(C callback) { + int index = mCallbacks.lastIndexOf(callback); + if (index < 0 || isRemovedLocked(index)) { + mCallbacks.add(callback); + } + } + + /** + * Returns true if the callback at index has been marked for removal. + * + * @param index The index into mCallbacks to check. + * @return true if the callback at index has been marked for removal. + */ + private boolean isRemovedLocked(int index) { + if (index < Long.SIZE) { + // It is in the first 64 callbacks, just check the bit. + final long bitMask = 1L << index; + return (mFirst64Removed & bitMask) != 0; + } else if (mRemainderRemoved == null) { + // It is after the first 64 callbacks, but nothing else was marked for removal. + return false; + } else { + final int maskIndex = (index / Long.SIZE) - 1; + if (maskIndex >= mRemainderRemoved.length) { + // There are some items in mRemainderRemoved, but nothing at the given index. + return false; + } else { + // There is something marked for removal, so we have to check the bit. + final long bits = mRemainderRemoved[maskIndex]; + final long bitMask = 1L << (index % Long.SIZE); + return (bits & bitMask) != 0; + } + } + } + + /** + * Removes callbacks from startIndex to startIndex + Long.SIZE, based + * on the bits set in removed. + * @param startIndex The index into the mCallbacks to start removing callbacks. + * @param removed The bits indicating removal, where each bit is set for one callback + * to be removed. + */ + private void removeRemovedCallbacks(int startIndex, long removed) { + // The naive approach should be fine. There may be a better bit-twiddling approach. + final int endIndex = startIndex + Long.SIZE; + + long bitMask = 1L << (Long.SIZE - 1); + for (int i = endIndex - 1; i >= startIndex; i--) { + if ((removed & bitMask) != 0) { + mCallbacks.remove(i); + } + bitMask >>>= 1; + } + } + + /** + * Remove a callback. This callback won't be notified after this call completes. + * @param callback The callback to remove. + */ + public synchronized void remove(C callback) { + if (mNotificationLevel == 0) { + mCallbacks.remove(callback); + } else { + int index = mCallbacks.lastIndexOf(callback); + if (index >= 0) { + setRemovalBitLocked(index); + } + } + } + + private void setRemovalBitLocked(int index) { + if (index < Long.SIZE) { + // It is in the first 64 callbacks, just check the bit. + final long bitMask = 1L << index; + mFirst64Removed |= bitMask; + } else { + final int remainderIndex = (index / Long.SIZE) - 1; + if (mRemainderRemoved == null) { + mRemainderRemoved = new long[mCallbacks.size() / Long.SIZE]; + } else if (mRemainderRemoved.length < remainderIndex) { + // need to make it bigger + long[] newRemainders = new long[mCallbacks.size() / Long.SIZE]; + System.arraycopy(mRemainderRemoved, 0, newRemainders, 0, mRemainderRemoved.length); + mRemainderRemoved = newRemainders; + } + final long bitMask = 1L << (index % Long.SIZE); + mRemainderRemoved[remainderIndex] |= bitMask; + } + } + + /** + * Makes a copy of the registered callbacks and returns it. + * + * @return a copy of the registered callbacks. + */ + public synchronized ArrayList<C> copyListeners() { + ArrayList<C> callbacks = new ArrayList<C>(mCallbacks.size()); + int numListeners = mCallbacks.size(); + for (int i = 0; i < numListeners; i++) { + if (!isRemovedLocked(i)) { + callbacks.add(mCallbacks.get(i)); + } + } + return callbacks; + } + + /** + * Returns true if there are no registered callbacks or false otherwise. + * + * @return true if there are no registered callbacks or false otherwise. + */ + public synchronized boolean isEmpty() { + if (mCallbacks.isEmpty()) { + return true; + } else if (mNotificationLevel == 0) { + return false; + } else { + int numListeners = mCallbacks.size(); + for (int i = 0; i < numListeners; i++) { + if (!isRemovedLocked(i)) { + return false; + } + } + return true; + } + } + + /** + * Removes all callbacks from the list. + */ + public synchronized void clear() { + if (mNotificationLevel == 0) { + mCallbacks.clear(); + } else if (!mCallbacks.isEmpty()) { + for (int i = mCallbacks.size() - 1; i >= 0; i--) { + setRemovalBitLocked(i); + } + } + } + + public synchronized CallbackRegistry<C, T, A> clone() { + CallbackRegistry<C, T, A> clone = null; + try { + clone = (CallbackRegistry<C, T, A>) super.clone(); + clone.mFirst64Removed = 0; + clone.mRemainderRemoved = null; + clone.mNotificationLevel = 0; + clone.mCallbacks = new ArrayList<C>(); + final int numListeners = mCallbacks.size(); + for (int i = 0; i < numListeners; i++) { + if (!isRemovedLocked(i)) { + clone.mCallbacks.add(mCallbacks.get(i)); + } + } + } catch (CloneNotSupportedException e) { + e.printStackTrace(); + } + return clone; + } + + /** + * Class used to notify events from CallbackRegistry. + * + * @param <C> The callback type. + * @param <T> The notification sender type. Typically this is the containing class. + * @param <A> An opaque argument to pass to the notifier + */ + public abstract static class NotifierCallback<C, T, A> { + /** + * Used to notify the callback. + * + * @param callback The callback to notify. + * @param sender The opaque sender object. + * @param arg The opaque notification parameter. + * @param arg2 An opaque argument passed in + * {@link CallbackRegistry#notifyCallbacks} + * @see CallbackRegistry#CallbackRegistry(CallbackRegistry.NotifierCallback) + */ + public abstract void onNotifyCallback(C callback, T sender, int arg, A arg2); + } +} diff --git a/core/java/com/android/internal/view/IInputMethodManager.aidl b/core/java/com/android/internal/view/IInputMethodManager.aidl index 6f104dd..60c5e42 100644 --- a/core/java/com/android/internal/view/IInputMethodManager.aidl +++ b/core/java/com/android/internal/view/IInputMethodManager.aidl @@ -59,7 +59,8 @@ interface IInputMethodManager { int controlFlags, int softInputMode, int windowFlags, in EditorInfo attribute, IInputContext inputContext); - void showInputMethodPickerFromClient(in IInputMethodClient client); + void showInputMethodPickerFromClient(in IInputMethodClient client, + int auxiliarySubtypeMode); void showInputMethodAndSubtypeEnablerFromClient(in IInputMethodClient client, String topId); void setInputMethod(in IBinder token, String id); void setInputMethodAndSubtype(in IBinder token, String id, in InputMethodSubtype subtype); diff --git a/core/java/com/android/server/BootReceiver.java b/core/java/com/android/server/BootReceiver.java index 155f5d3..92d5aea 100644 --- a/core/java/com/android/server/BootReceiver.java +++ b/core/java/com/android/server/BootReceiver.java @@ -144,10 +144,6 @@ public class BootReceiver extends BroadcastReceiver { -LOG_SIZE, "SYSTEM_RECOVERY_LOG"); addFileToDropBox(db, prefs, headers, "/cache/recovery/last_kmsg", -LOG_SIZE, "SYSTEM_RECOVERY_KMSG"); - addFileToDropBox(db, prefs, headers, "/data/dontpanic/apanic_console", - -LOG_SIZE, "APANIC_CONSOLE"); - addFileToDropBox(db, prefs, headers, "/data/dontpanic/apanic_threads", - -LOG_SIZE, "APANIC_THREADS"); addAuditErrorsToDropBox(db, prefs, headers, -LOG_SIZE, "SYSTEM_AUDIT"); addFsckErrorsToDropBox(db, prefs, headers, -LOG_SIZE, "SYSTEM_FSCK"); } else { diff --git a/core/jni/android/graphics/Bitmap.cpp b/core/jni/android/graphics/Bitmap.cpp index 8ae2e3b..2785c48 100755 --- a/core/jni/android/graphics/Bitmap.cpp +++ b/core/jni/android/graphics/Bitmap.cpp @@ -188,18 +188,43 @@ size_t Bitmap::rowBytes() const { return mPixelRef->rowBytes(); } -SkPixelRef* Bitmap::pixelRef() const { +SkPixelRef* Bitmap::peekAtPixelRef() const { assertValid(); return mPixelRef.get(); } +SkPixelRef* Bitmap::refPixelRef() { + assertValid(); + android::AutoMutex _lock(mLock); + return refPixelRefLocked(); +} + +SkPixelRef* Bitmap::refPixelRefLocked() { + mPixelRef->ref(); + if (mPixelRef->unique()) { + // We just restored this from 0, pin the pixels and inc the strong count + // Note that there *might be* an incoming onStrongRefDestroyed from whatever + // last unref'd + pinPixelsLocked(); + mPinnedRefCount++; + } + return mPixelRef.get(); +} + void Bitmap::reconfigure(const SkImageInfo& info, size_t rowBytes, SkColorTable* ctable) { + { + android::AutoMutex _lock(mLock); + if (mPinnedRefCount) { + ALOGW("Called reconfigure on a bitmap that is in use! This may" + " cause graphical corruption!"); + } + } mPixelRef->reconfigure(info, rowBytes, ctable); } void Bitmap::reconfigure(const SkImageInfo& info) { - mPixelRef->reconfigure(info, mPixelRef->rowBytes(), mPixelRef->colorTable()); + reconfigure(info, info.minRowBytes(), nullptr); } void Bitmap::detachFromJava() { @@ -287,18 +312,10 @@ void Bitmap::unpinPixelsLocked() { void Bitmap::getSkBitmap(SkBitmap* outBitmap) { assertValid(); android::AutoMutex _lock(mLock); - mPixelRef->ref(); - if (mPixelRef->unique()) { - // We just restored this from 0, pin the pixels and inc the strong count - // Note that there *might be* an incoming onStrongRefDestroyed from whatever - // last unref'd - pinPixelsLocked(); - mPinnedRefCount++; - } // Safe because mPixelRef is a WrappedPixelRef type, otherwise rowBytes() // would require locking the pixels first. outBitmap->setInfo(mPixelRef->info(), mPixelRef->rowBytes()); - outBitmap->setPixelRef(mPixelRef.get())->unref(); + outBitmap->setPixelRef(refPixelRefLocked())->unref(); outBitmap->setHasHardwareMipMap(hasHardwareMipMap()); } @@ -323,7 +340,7 @@ public: } void* pixels() { - return mBitmap->pixelRef()->pixels(); + return mBitmap->peekAtPixelRef()->pixels(); } bool valid() { @@ -780,7 +797,7 @@ static jint Bitmap_config(JNIEnv* env, jobject, jlong bitmapHandle) { static jint Bitmap_getGenerationId(JNIEnv* env, jobject, jlong bitmapHandle) { LocalScopedBitmap bitmap(bitmapHandle); - return static_cast<jint>(bitmap->pixelRef()->getGenerationID()); + return static_cast<jint>(bitmap->peekAtPixelRef()->getGenerationID()); } static jboolean Bitmap_isPremultiplied(JNIEnv* env, jobject, jlong bitmapHandle) { @@ -800,10 +817,10 @@ static void Bitmap_setHasAlpha(JNIEnv* env, jobject, jlong bitmapHandle, jboolean hasAlpha, jboolean requestPremul) { LocalScopedBitmap bitmap(bitmapHandle); if (hasAlpha) { - bitmap->pixelRef()->changeAlphaType( + bitmap->peekAtPixelRef()->changeAlphaType( requestPremul ? kPremul_SkAlphaType : kUnpremul_SkAlphaType); } else { - bitmap->pixelRef()->changeAlphaType(kOpaque_SkAlphaType); + bitmap->peekAtPixelRef()->changeAlphaType(kOpaque_SkAlphaType); } } @@ -812,9 +829,9 @@ static void Bitmap_setPremultiplied(JNIEnv* env, jobject, jlong bitmapHandle, LocalScopedBitmap bitmap(bitmapHandle); if (!bitmap->info().isOpaque()) { if (isPremul) { - bitmap->pixelRef()->changeAlphaType(kPremul_SkAlphaType); + bitmap->peekAtPixelRef()->changeAlphaType(kPremul_SkAlphaType); } else { - bitmap->pixelRef()->changeAlphaType(kUnpremul_SkAlphaType); + bitmap->peekAtPixelRef()->changeAlphaType(kUnpremul_SkAlphaType); } } } @@ -1164,7 +1181,7 @@ static jboolean Bitmap_sameAs(JNIEnv* env, jobject, jlong bm0Handle, static jlong Bitmap_refPixelRef(JNIEnv* env, jobject, jlong bitmapHandle) { LocalScopedBitmap bitmap(bitmapHandle); - SkPixelRef* pixelRef = bitmap.valid() ? bitmap->pixelRef() : nullptr; + SkPixelRef* pixelRef = bitmap.valid() ? bitmap->peekAtPixelRef() : nullptr; SkSafeRef(pixelRef); return reinterpret_cast<jlong>(pixelRef); } diff --git a/core/jni/android/graphics/Bitmap.h b/core/jni/android/graphics/Bitmap.h index d6e5c61..efeb898 100644 --- a/core/jni/android/graphics/Bitmap.h +++ b/core/jni/android/graphics/Bitmap.h @@ -62,7 +62,8 @@ public: int width() const { return info().width(); } int height() const { return info().height(); } size_t rowBytes() const; - SkPixelRef* pixelRef() const; + SkPixelRef* peekAtPixelRef() const; + SkPixelRef* refPixelRef(); bool valid() const { return mPixelStorageType != PixelStorageType::Invalid; } void reconfigure(const SkImageInfo& info, size_t rowBytes, SkColorTable* ctable); @@ -88,6 +89,7 @@ private: JNIEnv* jniEnv(); bool shouldDisposeSelfLocked(); void assertValid() const; + SkPixelRef* refPixelRefLocked(); android::Mutex mLock; int mPinnedRefCount = 0; diff --git a/core/jni/android/graphics/BitmapFactory.cpp b/core/jni/android/graphics/BitmapFactory.cpp index cdd397d..3ca4e72 100644 --- a/core/jni/android/graphics/BitmapFactory.cpp +++ b/core/jni/android/graphics/BitmapFactory.cpp @@ -184,7 +184,7 @@ public: } mBitmap->reconfigure(info, bitmap->rowBytes(), ctable); - bitmap->setPixelRef(mBitmap->pixelRef()); + bitmap->setPixelRef(mBitmap->refPixelRef())->unref(); // since we're already allocated, we lockPixels right away // HeapAllocator/JavaPixelAllocator behaves this way too @@ -258,7 +258,7 @@ static jobject doDecode(JNIEnv* env, SkStreamRewindable* stream, jobject padding unsigned int existingBufferSize = 0; if (javaBitmap != NULL) { reuseBitmap = GraphicsJNI::getBitmap(env, javaBitmap); - if (reuseBitmap->pixelRef()->isImmutable()) { + if (reuseBitmap->peekAtPixelRef()->isImmutable()) { ALOGW("Unable to reuse an immutable bitmap as an image decoder target."); javaBitmap = NULL; reuseBitmap = nullptr; diff --git a/core/jni/android/graphics/Graphics.cpp b/core/jni/android/graphics/Graphics.cpp index 0deb8cc..1c6f7de 100644 --- a/core/jni/android/graphics/Graphics.cpp +++ b/core/jni/android/graphics/Graphics.cpp @@ -352,8 +352,8 @@ void GraphicsJNI::getSkBitmap(JNIEnv* env, jobject bitmap, SkBitmap* outBitmap) getBitmap(env, bitmap)->getSkBitmap(outBitmap); } -SkPixelRef* GraphicsJNI::getSkPixelRef(JNIEnv* env, jobject bitmap) { - return getBitmap(env, bitmap)->pixelRef(); +SkPixelRef* GraphicsJNI::refSkPixelRef(JNIEnv* env, jobject bitmap) { + return getBitmap(env, bitmap)->refPixelRef(); } SkColorType GraphicsJNI::getNativeBitmapColorType(JNIEnv* env, jobject jconfig) { diff --git a/core/jni/android/graphics/GraphicsJNI.h b/core/jni/android/graphics/GraphicsJNI.h index e748bac..ef9c2a9 100644 --- a/core/jni/android/graphics/GraphicsJNI.h +++ b/core/jni/android/graphics/GraphicsJNI.h @@ -52,7 +52,7 @@ public: static android::Canvas* getNativeCanvas(JNIEnv*, jobject canvas); static android::Bitmap* getBitmap(JNIEnv*, jobject bitmap); static void getSkBitmap(JNIEnv*, jobject bitmap, SkBitmap* outBitmap); - static SkPixelRef* getSkPixelRef(JNIEnv*, jobject bitmap); + static SkPixelRef* refSkPixelRef(JNIEnv*, jobject bitmap); static SkRegion* getNativeRegion(JNIEnv*, jobject region); // Given the 'native' long held by the Rasterizer.java object, return a diff --git a/core/jni/android_util_AssetManager.cpp b/core/jni/android_util_AssetManager.cpp index db495dd..74a9e4e 100644 --- a/core/jni/android_util_AssetManager.cpp +++ b/core/jni/android_util_AssetManager.cpp @@ -976,6 +976,12 @@ static void android_content_AssetManager_copyTheme(JNIEnv* env, jobject clazz, dest->setTo(*src); } +static void android_content_AssetManager_clearTheme(JNIEnv* env, jobject clazz, jlong themeHandle) +{ + ResTable::Theme* theme = reinterpret_cast<ResTable::Theme*>(themeHandle); + theme->clear(); +} + static jint android_content_AssetManager_loadThemeAttributeValue( JNIEnv* env, jobject clazz, jlong themeHandle, jint ident, jobject outValue, jboolean resolve) { @@ -2108,6 +2114,8 @@ static JNINativeMethod gAssetManagerMethods[] = { (void*) android_content_AssetManager_applyThemeStyle }, { "copyTheme", "(JJ)V", (void*) android_content_AssetManager_copyTheme }, + { "clearTheme", "(J)V", + (void*) android_content_AssetManager_clearTheme }, { "loadThemeAttributeValue", "(JILandroid/util/TypedValue;Z)I", (void*) android_content_AssetManager_loadThemeAttributeValue }, { "getThemeChangingConfigurations", "(J)I", diff --git a/core/jni/android_view_SurfaceControl.cpp b/core/jni/android_view_SurfaceControl.cpp index 1965cd3..77af341 100644 --- a/core/jni/android_view_SurfaceControl.cpp +++ b/core/jni/android_view_SurfaceControl.cpp @@ -180,7 +180,7 @@ static jobject nativeScreenshotBitmap(JNIEnv* env, jclass clazz, (void*) screenshot->getPixels(), (void*) screenshot.get(), DeleteScreenshot, screenshotInfo, rowBytes, nullptr); screenshot.detach(); - bitmap->pixelRef()->setImmutable(); + bitmap->peekAtPixelRef()->setImmutable(); return GraphicsJNI::createBitmap(env, bitmap, GraphicsJNI::kBitmapCreateFlag_Premultiplied, NULL); diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml index 0c5c1b2..7b53578 100644 --- a/core/res/AndroidManifest.xml +++ b/core/res/AndroidManifest.xml @@ -2352,11 +2352,6 @@ <permission android:name="android.permission.BIND_CONDITION_PROVIDER_SERVICE" android:protectionLevel="signature" /> - <!-- Must be required by a {@link android.media.routing.MediaRouteService}, - to ensure that only the system can bind to it. --> - <permission android:name="android.permission.BIND_MEDIA_ROUTE_SERVICE" - android:protectionLevel="signature" /> - <!-- Must be required by an {@link android.service.dreams.DreamService}, to ensure that only the system can bind to it. --> <permission android:name="android.permission.BIND_DREAM_SERVICE" diff --git a/core/res/res/drawable-hdpi/text_cursor_mtrl_alpha.9.png b/core/res/res/drawable-hdpi/text_cursor_mtrl_alpha.9.png Binary files differdeleted file mode 100644 index 0179433..0000000 --- a/core/res/res/drawable-hdpi/text_cursor_mtrl_alpha.9.png +++ /dev/null diff --git a/core/res/res/drawable-mdpi/text_cursor_mtrl_alpha.9.png b/core/res/res/drawable-mdpi/text_cursor_mtrl_alpha.9.png Binary files differdeleted file mode 100644 index e5760be..0000000 --- a/core/res/res/drawable-mdpi/text_cursor_mtrl_alpha.9.png +++ /dev/null diff --git a/core/res/res/drawable-xhdpi/text_cursor_mtrl_alpha.9.png b/core/res/res/drawable-xhdpi/text_cursor_mtrl_alpha.9.png Binary files differdeleted file mode 100644 index 3939214..0000000 --- a/core/res/res/drawable-xhdpi/text_cursor_mtrl_alpha.9.png +++ /dev/null diff --git a/core/res/res/drawable-xxhdpi/text_cursor_mtrl_alpha.9.png b/core/res/res/drawable-xxhdpi/text_cursor_mtrl_alpha.9.png Binary files differdeleted file mode 100644 index 432c385..0000000 --- a/core/res/res/drawable-xxhdpi/text_cursor_mtrl_alpha.9.png +++ /dev/null diff --git a/core/res/res/drawable/text_cursor_material.xml b/core/res/res/drawable/text_cursor_material.xml index a350c47..0bedaa9 100644 --- a/core/res/res/drawable/text_cursor_material.xml +++ b/core/res/res/drawable/text_cursor_material.xml @@ -14,6 +14,15 @@ limitations under the License. --> -<nine-patch xmlns:android="http://schemas.android.com/apk/res/android" - android:src="@drawable/text_cursor_mtrl_alpha" - android:tint="?attr/colorControlActivated" /> +<inset xmlns:android="http://schemas.android.com/apk/res/android" + android:inset="2dp"> + <shape + android:tint="?attr/colorControlActivated" + android:shape="rectangle"> + <size + android:height="2dp" + android:width="2dp" /> + <solid + android:color="@color/white" /> + </shape> +</inset> diff --git a/core/res/res/layout-land/time_picker_material.xml b/core/res/res/layout-land/time_picker_material.xml index 89c3749..4b544d2 100644 --- a/core/res/res/layout-land/time_picker_material.xml +++ b/core/res/res/layout-land/time_picker_material.xml @@ -16,7 +16,6 @@ --> <GridLayout xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent"> @@ -27,8 +26,7 @@ android:layout_column="0" android:layout_row="0" android:layout_rowSpan="3" - android:layout_gravity="center|fill" - tools:background="@color/accent_material_light" /> + android:layout_gravity="center|fill" /> <RelativeLayout android:layout_width="wrap_content" @@ -56,20 +54,14 @@ android:textAppearance="@style/TextAppearance.Material.TimePicker.TimeLabel" android:singleLine="true" android:ellipsize="none" - android:gravity="right" - tools:text="23" - tools:textSize="@dimen/timepicker_time_label_size" - tools:textColor="@color/white" /> + android:gravity="right" /> <TextView android:id="@+id/separator" android:layout_width="wrap_content" android:layout_height="wrap_content" android:textAppearance="@style/TextAppearance.Material.TimePicker.TimeLabel" - android:importantForAccessibility="no" - tools:text=":" - tools:textSize="@dimen/timepicker_time_label_size" - tools:textColor="@color/white" /> + android:importantForAccessibility="no" /> <!-- The minutes should always be to the right of the separator, regardless of the current locale's layout direction. --> @@ -80,10 +72,7 @@ android:textAppearance="@style/TextAppearance.Material.TimePicker.TimeLabel" android:singleLine="true" android:ellipsize="none" - android:gravity="left" - tools:text="59" - tools:textSize="@dimen/timepicker_time_label_size" - tools:textColor="@color/white" /> + android:gravity="left" /> </LinearLayout> <!-- The layout alignment of this view will switch between toRightOf @@ -106,10 +95,7 @@ android:paddingTop="@dimen/timepicker_am_top_padding" android:lines="1" android:ellipsize="none" - android:includeFontPadding="false" - tools:text="AM" - tools:textSize="@dimen/timepicker_ampm_label_size" - tools:textColor="@color/white" /> + android:includeFontPadding="false" /> <CheckedTextView android:id="@+id/pm_label" @@ -121,10 +107,7 @@ android:paddingTop="@dimen/timepicker_pm_top_padding" android:lines="1" android:ellipsize="none" - android:includeFontPadding="false" - tools:text="PM" - tools:textSize="@dimen/timepicker_ampm_label_size" - tools:textColor="@color/white" /> + android:includeFontPadding="false" /> </LinearLayout> </RelativeLayout> diff --git a/core/res/res/layout/date_picker_header_material.xml b/core/res/res/layout/date_picker_header_material.xml index 2150341..a4388f6 100644 --- a/core/res/res/layout/date_picker_header_material.xml +++ b/core/res/res/layout/date_picker_header_material.xml @@ -16,17 +16,13 @@ --> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:tools="http://schemas.android.com/tools" android:id="@+id/date_picker_header" android:layout_width="match_parent" android:layout_height="wrap_content" android:paddingBottom="18dp" android:paddingStart="?attr/dialogPreferredPadding" android:paddingEnd="?attr/dialogPreferredPadding" - android:orientation="vertical" - tools:background="@color/accent_material_light" - tools:paddingStart="24dp" - tools:paddingEnd="24dp"> + android:orientation="vertical"> <!-- Top padding should stay on this view so that the touch target is a bit larger. --> @@ -35,10 +31,7 @@ android:layout_width="match_parent" android:layout_height="wrap_content" android:paddingTop="16dp" - android:textAppearance="@style/TextAppearance.Material.DatePicker.YearLabel" - tools:text="2015" - tools:textSize="@dimen/date_picker_year_label_size" - tools:textColor="@color/white" /> + android:textAppearance="@style/TextAppearance.Material.DatePicker.YearLabel" /> <TextView android:id="@+id/date_picker_header_date" @@ -47,9 +40,6 @@ android:textAppearance="@style/TextAppearance.Material.DatePicker.DateLabel" android:gravity="start" android:maxLines="2" - android:ellipsize="none" - tools:text="Thu, Sep 30" - tools:textSize="@dimen/date_picker_date_label_size" - tools:textColor="@color/white" /> + android:ellipsize="none" /> </LinearLayout> diff --git a/core/res/res/layout/time_picker_header_material.xml b/core/res/res/layout/time_picker_header_material.xml index be9e443..3f5e300 100644 --- a/core/res/res/layout/time_picker_header_material.xml +++ b/core/res/res/layout/time_picker_header_material.xml @@ -18,13 +18,11 @@ <!-- This layout is duplicated in land/time_picker_material.xml, so any changes made here need to be manually copied over. --> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:tools="http://schemas.android.com/tools" android:id="@+id/time_header" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="horizontal" - android:padding="@dimen/timepicker_separator_padding" - tools:background="@color/accent_material_light"> + android:padding="@dimen/timepicker_separator_padding"> <!-- The hour should always be to the left of the separator, regardless of the current locale's layout direction. --> @@ -37,10 +35,7 @@ android:textAppearance="@style/TextAppearance.Material.TimePicker.TimeLabel" android:singleLine="true" android:ellipsize="none" - android:gravity="right" - tools:text="23" - tools:textSize="@dimen/timepicker_time_label_size" - tools:textColor="@color/white" /> + android:gravity="right" /> <TextView android:id="@+id/separator" @@ -50,10 +45,7 @@ android:layout_marginRight="@dimen/timepicker_separator_padding" android:layout_centerInParent="true" android:textAppearance="@style/TextAppearance.Material.TimePicker.TimeLabel" - android:importantForAccessibility="no" - tools:text=":" - tools:textSize="@dimen/timepicker_time_label_size" - tools:textColor="@color/white" /> + android:importantForAccessibility="no" /> <!-- The minutes should always be to the left of the separator, regardless of the current locale's layout direction. --> @@ -66,10 +58,7 @@ android:textAppearance="@style/TextAppearance.Material.TimePicker.TimeLabel" android:singleLine="true" android:ellipsize="none" - android:gravity="left" - tools:text="59" - tools:textSize="@dimen/timepicker_time_label_size" - tools:textColor="@color/white" /> + android:gravity="left" /> <!-- The layout alignment of this view will switch between toRightOf @id/minutes and toLeftOf @id/hours depending on the locale. --> @@ -90,10 +79,7 @@ android:paddingTop="@dimen/timepicker_am_top_padding" android:textAppearance="@style/TextAppearance.Material.TimePicker.AmPmLabel" android:lines="1" - android:ellipsize="none" - tools:text="AM" - tools:textSize="@dimen/timepicker_ampm_label_size" - tools:textColor="@color/white" /> + android:ellipsize="none" /> <CheckedTextView android:id="@+id/pm_label" android:layout_width="wrap_content" @@ -103,9 +89,6 @@ android:paddingTop="@dimen/timepicker_pm_top_padding" android:textAppearance="@style/TextAppearance.Material.TimePicker.AmPmLabel" android:lines="1" - android:ellipsize="none" - tools:text="PM" - tools:textSize="@dimen/timepicker_ampm_label_size" - tools:textColor="@color/white" /> + android:ellipsize="none" /> </LinearLayout> </RelativeLayout> diff --git a/core/res/res/transition/popup_window_enter.xml b/core/res/res/transition/popup_window_enter.xml index 38c41f0..c4c8dac 100644 --- a/core/res/res/transition/popup_window_enter.xml +++ b/core/res/res/transition/popup_window_enter.xml @@ -16,17 +16,14 @@ <transitionSet xmlns:android="http://schemas.android.com/apk/res/android" android:transitionOrdering="together"> - <!-- Start from location of epicenter, move to popup location. --> - <transition - class="com.android.internal.transition.EpicenterTranslate" - android:duration="300" /> - <!-- Start from size of epicenter, expand to full width/height. --> <transition - class="com.android.internal.transition.EpicenterClipReveal" - android:centerClipBounds="true" - android:duration="300" /> + class="com.android.internal.transition.EpicenterTranslateClipReveal" + android:duration="250" /> <!-- Quickly fade in. --> - <fade android:duration="100" /> + <fade + android:duration="100" + android:fromAlpha="0.1" + android:toAlpha="1.0" /> </transitionSet> diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml index 1c4b5f7..0b96d22 100644 --- a/core/res/res/values/attrs.xml +++ b/core/res/res/values/attrs.xml @@ -2347,11 +2347,6 @@ be saved. --> <attr name="saveEnabled" format="boolean" /> - <!-- If true, no {@link android.view.ViewAssistStructure} data will be collected from - this view or any of its children. The default is false, allowing assist structure - to be reported by it. --> - <attr name="assistBlocked" format="boolean" /> - <!-- Specifies whether to filter touches when the view's window is obscured by another visible window. When set to true, the view will not receive touches whenever a toast, dialog or other window appears above the view's window. @@ -5877,16 +5872,9 @@ </declare-styleable> <!-- @hide For internal use only. Use only as directed. --> - <declare-styleable name="EpicenterClipReveal"> - <attr name="centerClipBounds" format="boolean" /> + <declare-styleable name="EpicenterTranslateClipReveal"> <attr name="interpolatorX" format="reference" /> <attr name="interpolatorY" format="reference" /> - </declare-styleable> - - <!-- @hide For internal use only. Use only as directed. --> - <declare-styleable name="EpicenterTranslate"> - <attr name="interpolatorX" /> - <attr name="interpolatorY" /> <attr name="interpolatorZ" format="reference" /> </declare-styleable> diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml index 297b302..e403a16 100644 --- a/core/res/res/values/public.xml +++ b/core/res/res/values/public.xml @@ -2682,7 +2682,9 @@ <!-- NFC CardEmulation: dynamically load service resources --> <public type="attr" name="dynamicResources" /> - <public type="attr" name="assistBlocked" /> + <attr name="__reserved0" format="boolean" /> + <public type="attr" name="__reserved0" /> + <public type="attr" name="stylusButtonPressable" /> <public type="attr" name="supportsLaunchVoiceAssistFromKeyguard" /> <public type="attr" name="scrollIndicators" /> diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml index 55b32e1..a3a6105 100644 --- a/core/res/res/values/strings.xml +++ b/core/res/res/values/strings.xml @@ -4068,10 +4068,13 @@ <string name="zen_mode_downtime_feature_name">Downtime</string> <!-- Zen mode - name of default automatic schedule for weeknights. [CHAR LIMIT=40] --> - <string name="zen_mode_default_weeknights_name">Weeknights</string> + <string name="zen_mode_default_weeknights_name">Weeknight</string> <!-- Zen mode - name of default automatic schedule for weekends. [CHAR LIMIT=40] --> - <string name="zen_mode_default_weekends_name">Weekends</string> + <string name="zen_mode_default_weekends_name">Weekend</string> + + <!-- Zen mode - name of default automatic calendar event-based rule. [CHAR LIMIT=40] --> + <string name="zen_mode_default_events_name">Event</string> <!-- Indication that the current volume and other effects (vibration) are being suppressed by a third party, such as a notification listener. [CHAR LIMIT=30] --> <string name="muted_by">Muted by <xliff:g id="third_party">%1$s</xliff:g></string> diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml index 28ffbfa..801a98a 100755 --- a/core/res/res/values/symbols.xml +++ b/core/res/res/values/symbols.xml @@ -2065,6 +2065,7 @@ <java-symbol type="string" name="zen_mode_downtime_feature_name" /> <java-symbol type="string" name="zen_mode_default_weeknights_name" /> <java-symbol type="string" name="zen_mode_default_weekends_name" /> + <java-symbol type="string" name="zen_mode_default_events_name" /> <java-symbol type="array" name="config_system_condition_providers" /> <java-symbol type="string" name="muted_by" /> diff --git a/core/tests/coretests/src/android/util/FloatMathTest.java b/core/tests/coretests/src/android/util/FloatMathTest.java deleted file mode 100644 index f479e2b..0000000 --- a/core/tests/coretests/src/android/util/FloatMathTest.java +++ /dev/null @@ -1,55 +0,0 @@ -/* - * Copyright (C) 2007 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package android.util; - -import junit.framework.TestCase; -import android.test.suitebuilder.annotation.SmallTest; - -public class FloatMathTest extends TestCase { - - @SmallTest - public void testSqrt() { - assertEquals(7, FloatMath.sqrt(49), 0); - assertEquals(10, FloatMath.sqrt(100), 0); - assertEquals(0, FloatMath.sqrt(0), 0); - assertEquals(1, FloatMath.sqrt(1), 0); - } - - @SmallTest - public void testFloor() { - assertEquals(78, FloatMath.floor(78.89f), 0); - assertEquals(-79, FloatMath.floor(-78.89f), 0); - } - - @SmallTest - public void testCeil() { - assertEquals(79, FloatMath.ceil(78.89f), 0); - assertEquals(-78, FloatMath.ceil(-78.89f), 0); - } - - @SmallTest - public void testSin() { - assertEquals(0.0, FloatMath.sin(0), 0); - assertEquals(0.8414709848078965f, FloatMath.sin(1), 0); - } - - @SmallTest - public void testCos() { - assertEquals(1.0f, FloatMath.cos(0), 0); - assertEquals(0.5403023058681398f, FloatMath.cos(1), 0); - } -} diff --git a/core/tests/coretests/src/com/android/internal/util/CallbackRegistryTest.java b/core/tests/coretests/src/com/android/internal/util/CallbackRegistryTest.java new file mode 100644 index 0000000..c53f4cc --- /dev/null +++ b/core/tests/coretests/src/com/android/internal/util/CallbackRegistryTest.java @@ -0,0 +1,305 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.internal.util; + +import junit.framework.TestCase; + +import org.junit.Test; + +import java.util.ArrayList; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; + +public class CallbackRegistryTest extends TestCase { + + final Integer callback1 = 1; + final Integer callback2 = 2; + final Integer callback3 = 3; + CallbackRegistry<Integer, CallbackRegistryTest, Integer> registry; + int notify1; + int notify2; + int notify3; + int[] deepNotifyCount = new int[300]; + Integer argValue; + + private void addNotifyCount(Integer callback) { + if (callback == callback1) { + notify1++; + } else if (callback == callback2) { + notify2++; + } else if (callback == callback3) { + notify3++; + } + deepNotifyCount[callback]++; + } + + public void testAddListener() { + CallbackRegistry.NotifierCallback<Integer, CallbackRegistryTest, Integer> notifier = + new CallbackRegistry.NotifierCallback<Integer, CallbackRegistryTest, Integer>() { + @Override + public void onNotifyCallback(Integer callback, CallbackRegistryTest sender, + int arg, Integer arg2) { + } + }; + registry = new CallbackRegistry<Integer, CallbackRegistryTest, Integer>(notifier); + Integer callback = 0; + + assertNotNull(registry.copyListeners()); + assertEquals(0, registry.copyListeners().size()); + + registry.add(callback); + ArrayList<Integer> callbacks = registry.copyListeners(); + assertEquals(1, callbacks.size()); + assertEquals(callback, callbacks.get(0)); + + registry.add(callback); + callbacks = registry.copyListeners(); + assertEquals(1, callbacks.size()); + assertEquals(callback, callbacks.get(0)); + + Integer otherListener = 1; + registry.add(otherListener); + callbacks = registry.copyListeners(); + assertEquals(2, callbacks.size()); + assertEquals(callback, callbacks.get(0)); + assertEquals(otherListener, callbacks.get(1)); + + registry.remove(callback); + registry.add(callback); + callbacks = registry.copyListeners(); + assertEquals(2, callbacks.size()); + assertEquals(callback, callbacks.get(1)); + assertEquals(otherListener, callbacks.get(0)); + } + + public void testSimpleNotify() { + CallbackRegistry.NotifierCallback<Integer, CallbackRegistryTest, Integer> notifier = + new CallbackRegistry.NotifierCallback<Integer, CallbackRegistryTest, Integer>() { + @Override + public void onNotifyCallback(Integer callback, CallbackRegistryTest sender, + int arg1, Integer arg) { + assertEquals(arg1, (int) arg); + addNotifyCount(callback); + argValue = arg; + } + }; + registry = new CallbackRegistry<Integer, CallbackRegistryTest, Integer>(notifier); + registry.add(callback2); + Integer arg = 1; + registry.notifyCallbacks(this, arg, arg); + assertEquals(arg, argValue); + assertEquals(1, notify2); + } + + public void testRemoveWhileNotifying() { + CallbackRegistry.NotifierCallback<Integer, CallbackRegistryTest, Integer> notifier = + new CallbackRegistry.NotifierCallback<Integer, CallbackRegistryTest, Integer>() { + @Override + public void onNotifyCallback(Integer callback, CallbackRegistryTest sender, + int arg1, Integer arg) { + addNotifyCount(callback); + if (callback == callback1) { + registry.remove(callback1); + registry.remove(callback2); + } + } + }; + registry = new CallbackRegistry<Integer, CallbackRegistryTest, Integer>(notifier); + registry.add(callback1); + registry.add(callback2); + registry.add(callback3); + registry.notifyCallbacks(this, 0, null); + assertEquals(1, notify1); + assertEquals(1, notify2); + assertEquals(1, notify3); + + ArrayList<Integer> callbacks = registry.copyListeners(); + assertEquals(1, callbacks.size()); + assertEquals(callback3, callbacks.get(0)); + } + + public void testDeepRemoveWhileNotifying() { + CallbackRegistry.NotifierCallback<Integer, CallbackRegistryTest, Integer> notifier = + new CallbackRegistry.NotifierCallback<Integer, CallbackRegistryTest, Integer>() { + @Override + public void onNotifyCallback(Integer callback, CallbackRegistryTest sender, + int arg1, Integer arg) { + addNotifyCount(callback); + registry.remove(callback); + registry.notifyCallbacks(CallbackRegistryTest.this, arg1, null); + } + }; + registry = new CallbackRegistry<Integer, CallbackRegistryTest, Integer>(notifier); + registry.add(callback1); + registry.add(callback2); + registry.add(callback3); + registry.notifyCallbacks(this, 0, null); + assertEquals(1, notify1); + assertEquals(2, notify2); + assertEquals(3, notify3); + + ArrayList<Integer> callbacks = registry.copyListeners(); + assertEquals(0, callbacks.size()); + } + + public void testAddRemovedListener() { + + CallbackRegistry.NotifierCallback<Integer, CallbackRegistryTest, Integer> notifier = + new CallbackRegistry.NotifierCallback<Integer, CallbackRegistryTest, Integer>() { + @Override + public void onNotifyCallback(Integer callback, CallbackRegistryTest sender, + int arg1, Integer arg) { + addNotifyCount(callback); + if (callback == callback1) { + registry.remove(callback2); + } else if (callback == callback3) { + registry.add(callback2); + } + } + }; + registry = new CallbackRegistry<Integer, CallbackRegistryTest, Integer>(notifier); + + registry.add(callback1); + registry.add(callback2); + registry.add(callback3); + registry.notifyCallbacks(this, 0, null); + + ArrayList<Integer> callbacks = registry.copyListeners(); + assertEquals(3, callbacks.size()); + assertEquals(callback1, callbacks.get(0)); + assertEquals(callback3, callbacks.get(1)); + assertEquals(callback2, callbacks.get(2)); + assertEquals(1, notify1); + assertEquals(1, notify2); + assertEquals(1, notify3); + } + + public void testVeryDeepRemoveWhileNotifying() { + final Integer[] callbacks = new Integer[deepNotifyCount.length]; + for (int i = 0; i < callbacks.length; i++) { + callbacks[i] = i; + } + CallbackRegistry.NotifierCallback<Integer, CallbackRegistryTest, Integer> notifier = + new CallbackRegistry.NotifierCallback<Integer, CallbackRegistryTest, Integer>() { + @Override + public void onNotifyCallback(Integer callback, CallbackRegistryTest sender, + int arg1, Integer arg) { + addNotifyCount(callback); + registry.remove(callback); + registry.remove(callbacks[callbacks.length - callback - 1]); + registry.notifyCallbacks(CallbackRegistryTest.this, arg1, null); + } + }; + registry = new CallbackRegistry<Integer, CallbackRegistryTest, Integer>(notifier); + for (int i = 0; i < callbacks.length; i++) { + registry.add(callbacks[i]); + } + registry.notifyCallbacks(this, 0, null); + for (int i = 0; i < deepNotifyCount.length; i++) { + int expectedCount = Math.min(i + 1, deepNotifyCount.length - i); + assertEquals(expectedCount, deepNotifyCount[i]); + } + + ArrayList<Integer> callbackList = registry.copyListeners(); + assertEquals(0, callbackList.size()); + } + + public void testClear() { + CallbackRegistry.NotifierCallback<Integer, CallbackRegistryTest, Integer> notifier = + new CallbackRegistry.NotifierCallback<Integer, CallbackRegistryTest, Integer>() { + @Override + public void onNotifyCallback(Integer callback, CallbackRegistryTest sender, + int arg1, Integer arg) { + addNotifyCount(callback); + } + }; + registry = new CallbackRegistry<Integer, CallbackRegistryTest, Integer>(notifier); + for (int i = 0; i < deepNotifyCount.length; i++) { + registry.add(i); + } + registry.clear(); + + ArrayList<Integer> callbackList = registry.copyListeners(); + assertEquals(0, callbackList.size()); + + registry.notifyCallbacks(this, 0, null); + for (int i = 0; i < deepNotifyCount.length; i++) { + assertEquals(0, deepNotifyCount[i]); + } + } + + public void testNestedClear() { + CallbackRegistry.NotifierCallback<Integer, CallbackRegistryTest, Integer> notifier = + new CallbackRegistry.NotifierCallback<Integer, CallbackRegistryTest, Integer>() { + @Override + public void onNotifyCallback(Integer callback, CallbackRegistryTest sender, + int arg1, Integer arg) { + addNotifyCount(callback); + registry.clear(); + } + }; + registry = new CallbackRegistry<Integer, CallbackRegistryTest, Integer>(notifier); + for (int i = 0; i < deepNotifyCount.length; i++) { + registry.add(i); + } + registry.notifyCallbacks(this, 0, null); + for (int i = 0; i < deepNotifyCount.length; i++) { + assertEquals(1, deepNotifyCount[i]); + } + + ArrayList<Integer> callbackList = registry.copyListeners(); + assertEquals(0, callbackList.size()); + } + + public void testIsEmpty() throws Exception { + CallbackRegistry.NotifierCallback<Integer, CallbackRegistryTest, Integer> notifier = + new CallbackRegistry.NotifierCallback<Integer, CallbackRegistryTest, Integer>() { + @Override + public void onNotifyCallback(Integer callback, CallbackRegistryTest sender, + int arg, Integer arg2) { + } + }; + registry = new CallbackRegistry<Integer, CallbackRegistryTest, Integer>(notifier); + Integer callback = 0; + + assertTrue(registry.isEmpty()); + registry.add(callback); + assertFalse(registry.isEmpty()); + } + + public void testClone() throws Exception { + CallbackRegistry.NotifierCallback<Integer, CallbackRegistryTest, Integer> notifier = + new CallbackRegistry.NotifierCallback<Integer, CallbackRegistryTest, Integer>() { + @Override + public void onNotifyCallback(Integer callback, CallbackRegistryTest sender, + int arg, Integer arg2) { + } + }; + registry = new CallbackRegistry<Integer, CallbackRegistryTest, Integer>(notifier); + + assertTrue(registry.isEmpty()); + CallbackRegistry<Integer, CallbackRegistryTest, Integer> registry2 = registry.clone(); + Integer callback = 0; + registry.add(callback); + assertFalse(registry.isEmpty()); + assertTrue(registry2.isEmpty()); + registry2 = registry.clone(); + assertFalse(registry2.isEmpty()); + } +} |