summaryrefslogtreecommitdiffstats
path: root/core
diff options
context:
space:
mode:
Diffstat (limited to 'core')
-rw-r--r--core/java/android/animation/AnimatorInflater.java4
-rw-r--r--core/java/android/animation/LayoutTransition.java67
-rw-r--r--core/java/android/app/ActionBar.java9
-rw-r--r--core/java/android/app/Activity.java2
-rw-r--r--core/java/android/app/ActivityThread.java251
-rw-r--r--core/java/android/app/ActivityTransitionCoordinator.java8
-rw-r--r--core/java/android/app/Application.java6
-rw-r--r--core/java/android/app/ApplicationThreadNative.java7
-rw-r--r--core/java/android/app/AssistStructure.java16
-rw-r--r--core/java/android/app/ContextImpl.java24
-rw-r--r--core/java/android/app/Dialog.java2
-rw-r--r--core/java/android/app/IApplicationThread.java2
-rw-r--r--core/java/android/app/INotificationManager.aidl13
-rw-r--r--core/java/android/app/INotificationManagerCallback.aidl2
-rw-r--r--core/java/android/app/NotificationManager.aidl1
-rw-r--r--core/java/android/app/NotificationManager.java290
-rw-r--r--core/java/android/app/SystemServiceRegistry.java3
-rw-r--r--core/java/android/app/admin/DeviceInitializerStatus.java40
-rw-r--r--core/java/android/app/admin/DevicePolicyManager.java14
-rw-r--r--core/java/android/bluetooth/BluetoothAdapter.java11
-rw-r--r--core/java/android/content/Intent.java71
-rw-r--r--core/java/android/content/pm/PackageManager.java7
-rw-r--r--core/java/android/content/res/AssetManager.java1
-rw-r--r--core/java/android/content/res/ConfigurationBoundResourceCache.java146
-rw-r--r--core/java/android/content/res/DrawableCache.java57
-rw-r--r--core/java/android/content/res/Resources.java310
-rw-r--r--core/java/android/content/res/ThemedResourceCache.java233
-rw-r--r--core/java/android/ddm/DdmHandleViewDebug.java16
-rw-r--r--core/java/android/hardware/camera2/CameraCaptureSession.java7
-rw-r--r--core/java/android/hardware/camera2/impl/CameraCaptureSessionImpl.java28
-rw-r--r--core/java/android/hardware/camera2/impl/CameraDeviceImpl.java159
-rw-r--r--core/java/android/hardware/camera2/legacy/RequestThreadManager.java3
-rw-r--r--core/java/android/os/Build.java21
-rw-r--r--core/java/android/os/Debug.java236
-rw-r--r--core/java/android/provider/Settings.java47
-rw-r--r--core/java/android/security/IKeystoreService.aidl4
-rw-r--r--core/java/android/service/dreams/DreamService.java2
-rw-r--r--core/java/android/service/notification/NotificationListenerService.java33
-rw-r--r--core/java/android/service/notification/ZenModeConfig.java13
-rw-r--r--core/java/android/speech/tts/TextToSpeech.java75
-rw-r--r--core/java/android/text/StaticLayout.java3
-rw-r--r--core/java/android/text/method/WordIterator.java39
-rw-r--r--core/java/android/transition/Transition.java4
-rw-r--r--core/java/android/util/FloatMath.java10
-rw-r--r--core/java/android/view/ContextThemeWrapper.java9
-rw-r--r--core/java/android/view/GhostView.java6
-rw-r--r--core/java/android/view/View.java186
-rw-r--r--core/java/android/view/ViewAssistStructure.java67
-rw-r--r--core/java/android/view/ViewDebug.java24
-rw-r--r--core/java/android/view/ViewGroup.java54
-rw-r--r--core/java/android/view/ViewHierarchyEncoder.java201
-rw-r--r--core/java/android/view/ViewRootImpl.java5
-rw-r--r--core/java/android/view/ViewStructure.java86
-rw-r--r--core/java/android/view/WindowManager.java24
-rw-r--r--core/java/android/view/inputmethod/InputMethodManager.java28
-rw-r--r--core/java/android/webkit/WebView.java20
-rw-r--r--core/java/android/webkit/WebViewProvider.java4
-rw-r--r--core/java/android/widget/AbsListView.java31
-rw-r--r--core/java/android/widget/ActionMenuPresenter.java3
-rw-r--r--core/java/android/widget/ActionMenuView.java14
-rw-r--r--core/java/android/widget/AdapterView.java14
-rw-r--r--core/java/android/widget/CheckedTextView.java9
-rw-r--r--core/java/android/widget/CompoundButton.java11
-rw-r--r--core/java/android/widget/Editor.java227
-rw-r--r--core/java/android/widget/FrameLayout.java13
-rw-r--r--core/java/android/widget/GridView.java9
-rw-r--r--core/java/android/widget/HorizontalScrollView.java9
-rw-r--r--core/java/android/widget/ImageView.java9
-rw-r--r--core/java/android/widget/LinearLayout.java25
-rw-r--r--core/java/android/widget/ListPopupWindow.java5
-rw-r--r--core/java/android/widget/ListView.java10
-rw-r--r--core/java/android/widget/MediaController.java2
-rw-r--r--core/java/android/widget/ProgressBar.java13
-rw-r--r--core/java/android/widget/RelativeLayout.java9
-rw-r--r--core/java/android/widget/ScrollView.java9
-rw-r--r--core/java/android/widget/Switch.java8
-rw-r--r--core/java/android/widget/TableRow.java11
-rw-r--r--core/java/android/widget/TextClock.java16
-rw-r--r--core/java/android/widget/TextView.java27
-rw-r--r--core/java/com/android/internal/app/ChooserActivity.java233
-rw-r--r--core/java/com/android/internal/app/ResolverActivity.java289
-rw-r--r--core/java/com/android/internal/inputmethod/InputMethodSubtypeSwitchingController.java14
-rw-r--r--core/java/com/android/internal/logging/MetricsLogger.java21
-rw-r--r--core/java/com/android/internal/policy/PhoneFallbackEventHandler.java (renamed from core/java/android/view/PhoneFallbackEventHandler.java)7
-rw-r--r--core/java/com/android/internal/policy/PhoneLayoutInflater.java (renamed from core/java/android/view/PhoneLayoutInflater.java)4
-rw-r--r--core/java/com/android/internal/policy/PhoneWindow.java (renamed from core/java/android/view/PhoneWindow.java)41
-rw-r--r--core/java/com/android/internal/transition/EpicenterClipReveal.java233
-rw-r--r--core/java/com/android/internal/transition/EpicenterTranslate.java191
-rw-r--r--core/java/com/android/internal/transition/EpicenterTranslateClipReveal.java328
-rw-r--r--core/java/com/android/internal/util/AsyncChannel.java8
-rw-r--r--core/java/com/android/internal/util/CallbackRegistry.java395
-rw-r--r--core/java/com/android/internal/view/IInputMethodManager.aidl3
-rw-r--r--core/java/com/android/server/BootReceiver.java4
-rwxr-xr-xcore/jni/android/graphics/Bitmap.cpp53
-rw-r--r--core/jni/android/graphics/Bitmap.h4
-rw-r--r--core/jni/android/graphics/BitmapFactory.cpp4
-rw-r--r--core/jni/android/graphics/Graphics.cpp4
-rw-r--r--core/jni/android/graphics/GraphicsJNI.h2
-rw-r--r--core/jni/android_util_AssetManager.cpp8
-rw-r--r--core/jni/android_view_SurfaceControl.cpp2
-rw-r--r--core/res/AndroidManifest.xml5
-rw-r--r--core/res/res/drawable-hdpi/text_cursor_mtrl_alpha.9.pngbin108 -> 0 bytes
-rw-r--r--core/res/res/drawable-mdpi/text_cursor_mtrl_alpha.9.pngbin106 -> 0 bytes
-rw-r--r--core/res/res/drawable-xhdpi/text_cursor_mtrl_alpha.9.pngbin105 -> 0 bytes
-rw-r--r--core/res/res/drawable-xxhdpi/text_cursor_mtrl_alpha.9.pngbin134 -> 0 bytes
-rw-r--r--core/res/res/drawable/text_cursor_material.xml15
-rw-r--r--core/res/res/layout-land/time_picker_material.xml29
-rw-r--r--core/res/res/layout/date_picker_header_material.xml16
-rw-r--r--core/res/res/layout/time_picker_header_material.xml29
-rw-r--r--core/res/res/transition/popup_window_enter.xml15
-rw-r--r--core/res/res/values/attrs.xml14
-rw-r--r--core/res/res/values/public.xml4
-rw-r--r--core/res/res/values/strings.xml7
-rwxr-xr-xcore/res/res/values/symbols.xml1
-rw-r--r--core/tests/coretests/src/android/util/FloatMathTest.java55
-rw-r--r--core/tests/coretests/src/com/android/internal/util/CallbackRegistryTest.java305
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 &amp; 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 &amp; 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
deleted file mode 100644
index 0179433..0000000
--- a/core/res/res/drawable-hdpi/text_cursor_mtrl_alpha.9.png
+++ /dev/null
Binary files differ
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
deleted file mode 100644
index e5760be..0000000
--- a/core/res/res/drawable-mdpi/text_cursor_mtrl_alpha.9.png
+++ /dev/null
Binary files differ
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
deleted file mode 100644
index 3939214..0000000
--- a/core/res/res/drawable-xhdpi/text_cursor_mtrl_alpha.9.png
+++ /dev/null
Binary files differ
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
deleted file mode 100644
index 432c385..0000000
--- a/core/res/res/drawable-xxhdpi/text_cursor_mtrl_alpha.9.png
+++ /dev/null
Binary files differ
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());
+ }
+}