summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--api/current.txt8
-rw-r--r--api/system-current.txt8
-rw-r--r--api/test-current.txt8
-rw-r--r--cmds/screencap/screencap.cpp4
-rw-r--r--core/java/android/os/RecoverySystem.java3
-rw-r--r--core/java/android/service/notification/INotificationListener.aidl2
-rw-r--r--core/java/android/service/notification/NotificationAssistantService.java14
-rw-r--r--core/java/android/service/notification/NotificationListenerService.java4
-rw-r--r--core/java/android/text/SpannableStringInternal.java47
-rw-r--r--core/java/android/view/SurfaceControl.java19
-rw-r--r--core/java/android/view/View.java43
-rw-r--r--core/java/android/view/ViewGroup.java19
-rw-r--r--core/java/android/webkit/WebViewClient.java3
-rw-r--r--core/java/android/widget/ActivityChooserView.java10
-rw-r--r--core/java/android/widget/AppSecurityPermissions.java6
-rw-r--r--core/java/android/widget/ArrayAdapter.java2
-rw-r--r--core/java/android/widget/CalendarViewLegacyDelegate.java6
-rwxr-xr-xcore/java/android/widget/DatePickerCalendarDelegate.java12
-rw-r--r--core/java/android/widget/DayPickerPagerAdapter.java2
-rw-r--r--core/java/android/widget/DayPickerViewPager.java9
-rw-r--r--core/java/android/widget/ListView.java70
-rw-r--r--core/java/android/widget/MediaController.java16
-rw-r--r--core/java/android/widget/RemoteViews.java8
-rw-r--r--core/java/android/widget/SuggestionsAdapter.java12
-rw-r--r--core/java/android/widget/TabHost.java6
-rw-r--r--core/java/android/widget/TimePickerSpinnerDelegate.java14
-rw-r--r--core/java/android/widget/Toast.java2
-rw-r--r--core/java/android/widget/ZoomButtonsController.java2
-rw-r--r--core/java/com/android/internal/widget/WatchHeaderListView.java7
-rw-r--r--core/jni/android_view_SurfaceControl.cpp10
-rw-r--r--core/res/res/values-mcc704-mnc01/config.xml27
-rwxr-xr-xcore/res/res/values-mcc708-mnc001/config.xml27
-rw-r--r--libs/androidfw/LoadedArsc.cpp21
-rw-r--r--libs/androidfw/include/androidfw/LoadedArsc.h1
-rw-r--r--libs/androidfw/tests/LoadedArsc_test.cpp30
-rw-r--r--libs/androidfw/tests/data/feature/feature.apkbin1373 -> 1373 bytes
-rw-r--r--libs/hwui/tests/common/scenes/SaveLayer2Animation.cpp72
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/SuggestionParser.java141
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/wifi/AccessPointPreference.java62
-rw-r--r--packages/SettingsLib/tests/robotests/src/com/android/settingslib/drawer/TileUtilsTest.java40
-rw-r--r--packages/SystemUI/src/com/android/systemui/recents/views/grid/TaskGridLayoutAlgorithm.java28
-rw-r--r--services/autofill/java/com/android/server/autofill/AutoFillManagerService.java12
-rw-r--r--services/autofill/java/com/android/server/autofill/AutoFillManagerServiceImpl.java502
-rw-r--r--services/autofill/java/com/android/server/autofill/AutoFillUI.java323
-rw-r--r--services/core/java/com/android/server/notification/NotificationManagerService.java6
-rw-r--r--services/core/java/com/android/server/pm/InstantAppRegistry.java2
-rw-r--r--services/core/java/com/android/server/wm/AppTransition.java6
-rw-r--r--services/core/java/com/android/server/wm/WindowManagerService.java3
-rw-r--r--services/core/java/com/android/server/wm/WindowStateAnimator.java11
-rw-r--r--services/core/java/com/android/server/wm/WindowSurfaceController.java35
-rw-r--r--tests/UiBench/AndroidManifest.xml16
-rw-r--r--tests/UiBench/src/com/android/test/uibench/FadingEdgeListActivity.java37
-rw-r--r--tests/UiBench/src/com/android/test/uibench/SaveLayerInterleaveActivity.java89
-rw-r--r--tests/UiBench/src/com/android/test/uibench/listview/CompatListActivity.java6
-rw-r--r--tests/UiBench/src/com/android/test/uibench/listview/FadingEdgeListFragment.java31
55 files changed, 1147 insertions, 757 deletions
diff --git a/api/current.txt b/api/current.txt
index 850ffaaa364e..0ca9875ead82 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -36128,7 +36128,7 @@ package android.service.notification {
method public void deleteNotificationChannel(java.lang.String, java.lang.String);
method public java.util.List<android.app.NotificationChannel> getNotificationChannels(java.lang.String);
method public final android.os.IBinder onBind(android.content.Intent);
- method public abstract android.service.notification.Adjustment onNotificationEnqueued(android.service.notification.StatusBarNotification, int, boolean);
+ method public abstract android.service.notification.Adjustment onNotificationEnqueued(android.service.notification.StatusBarNotification);
method public abstract void onNotificationSnoozedUntilContext(android.service.notification.StatusBarNotification, java.lang.String);
method public final void unsnoozeNotification(java.lang.String);
method public void updateNotificationChannel(java.lang.String, android.app.NotificationChannel);
@@ -44081,8 +44081,8 @@ package android.view {
method public void drawableHotspotChanged(float, float);
method protected void drawableStateChanged();
method public android.view.View findFocus();
- method public final android.view.View findViewById(int);
- method public final android.view.View findViewWithTag(java.lang.Object);
+ method public final <T extends android.view.View> T findViewById(int);
+ method public final <T extends android.view.View> T findViewWithTag(java.lang.Object);
method public void findViewsWithText(java.util.ArrayList<android.view.View>, java.lang.CharSequence, int);
method protected deprecated boolean fitSystemWindows(android.graphics.Rect);
method public android.view.View focusSearch(int);
@@ -49026,8 +49026,6 @@ package android.widget {
method public void addHeaderView(android.view.View);
method public boolean areFooterDividersEnabled();
method public boolean areHeaderDividersEnabled();
- method protected android.view.View findViewTraversal(int);
- method protected android.view.View findViewWithTagTraversal(java.lang.Object);
method public android.widget.ListAdapter getAdapter();
method public deprecated long[] getCheckItemIds();
method public android.graphics.drawable.Drawable getDivider();
diff --git a/api/system-current.txt b/api/system-current.txt
index 9050beac0ea5..594fa2ecac71 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -39157,7 +39157,7 @@ package android.service.notification {
method public void deleteNotificationChannel(java.lang.String, java.lang.String);
method public java.util.List<android.app.NotificationChannel> getNotificationChannels(java.lang.String);
method public final android.os.IBinder onBind(android.content.Intent);
- method public abstract android.service.notification.Adjustment onNotificationEnqueued(android.service.notification.StatusBarNotification, int, boolean);
+ method public abstract android.service.notification.Adjustment onNotificationEnqueued(android.service.notification.StatusBarNotification);
method public abstract void onNotificationSnoozedUntilContext(android.service.notification.StatusBarNotification, java.lang.String);
method public final void unsnoozeNotification(java.lang.String);
method public void updateNotificationChannel(java.lang.String, android.app.NotificationChannel);
@@ -47485,8 +47485,8 @@ package android.view {
method public void drawableHotspotChanged(float, float);
method protected void drawableStateChanged();
method public android.view.View findFocus();
- method public final android.view.View findViewById(int);
- method public final android.view.View findViewWithTag(java.lang.Object);
+ method public final <T extends android.view.View> T findViewById(int);
+ method public final <T extends android.view.View> T findViewWithTag(java.lang.Object);
method public void findViewsWithText(java.util.ArrayList<android.view.View>, java.lang.CharSequence, int);
method protected deprecated boolean fitSystemWindows(android.graphics.Rect);
method public android.view.View focusSearch(int);
@@ -52794,8 +52794,6 @@ package android.widget {
method public void addHeaderView(android.view.View);
method public boolean areFooterDividersEnabled();
method public boolean areHeaderDividersEnabled();
- method protected android.view.View findViewTraversal(int);
- method protected android.view.View findViewWithTagTraversal(java.lang.Object);
method public android.widget.ListAdapter getAdapter();
method public deprecated long[] getCheckItemIds();
method public android.graphics.drawable.Drawable getDivider();
diff --git a/api/test-current.txt b/api/test-current.txt
index 03942fdbc4b1..02616298a241 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -36263,7 +36263,7 @@ package android.service.notification {
method public void deleteNotificationChannel(java.lang.String, java.lang.String);
method public java.util.List<android.app.NotificationChannel> getNotificationChannels(java.lang.String);
method public final android.os.IBinder onBind(android.content.Intent);
- method public abstract android.service.notification.Adjustment onNotificationEnqueued(android.service.notification.StatusBarNotification, int, boolean);
+ method public abstract android.service.notification.Adjustment onNotificationEnqueued(android.service.notification.StatusBarNotification);
method public abstract void onNotificationSnoozedUntilContext(android.service.notification.StatusBarNotification, java.lang.String);
method public final void unsnoozeNotification(java.lang.String);
method public void updateNotificationChannel(java.lang.String, android.app.NotificationChannel);
@@ -44387,8 +44387,8 @@ package android.view {
method public void drawableHotspotChanged(float, float);
method protected void drawableStateChanged();
method public android.view.View findFocus();
- method public final android.view.View findViewById(int);
- method public final android.view.View findViewWithTag(java.lang.Object);
+ method public final <T extends android.view.View> T findViewById(int);
+ method public final <T extends android.view.View> T findViewWithTag(java.lang.Object);
method public void findViewsWithText(java.util.ArrayList<android.view.View>, java.lang.CharSequence, int);
method protected deprecated boolean fitSystemWindows(android.graphics.Rect);
method public android.view.View focusSearch(int);
@@ -49343,8 +49343,6 @@ package android.widget {
method public void addHeaderView(android.view.View);
method public boolean areFooterDividersEnabled();
method public boolean areHeaderDividersEnabled();
- method protected android.view.View findViewTraversal(int);
- method protected android.view.View findViewWithTagTraversal(java.lang.Object);
method public android.widget.ListAdapter getAdapter();
method public deprecated long[] getCheckItemIds();
method public android.graphics.drawable.Drawable getDivider();
diff --git a/cmds/screencap/screencap.cpp b/cmds/screencap/screencap.cpp
index 377e29d99576..000420f29c3b 100644
--- a/cmds/screencap/screencap.cpp
+++ b/cmds/screencap/screencap.cpp
@@ -162,7 +162,9 @@ int main(int argc, char** argv)
uint8_t displayOrientation = configs[activeConfig].orientation;
uint32_t captureOrientation = ORIENTATION_MAP[displayOrientation];
- status_t result = screenshot.update(display, Rect(), 0, 0, 0, -1U,
+ status_t result = screenshot.update(display, Rect(),
+ 0 /* reqWidth */, 0 /* reqHeight */,
+ INT32_MIN, INT32_MAX, /* all layers */
false, captureOrientation);
if (result == NO_ERROR) {
base = screenshot.getPixels();
diff --git a/core/java/android/os/RecoverySystem.java b/core/java/android/os/RecoverySystem.java
index a8822c5be2f0..ef5bc5cf9075 100644
--- a/core/java/android/os/RecoverySystem.java
+++ b/core/java/android/os/RecoverySystem.java
@@ -623,7 +623,8 @@ public class RecoverySystem {
final ConditionVariable condition = new ConditionVariable();
Intent intent = new Intent("android.intent.action.MASTER_CLEAR_NOTIFICATION");
- intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
+ intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND
+ | Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND);
context.sendOrderedBroadcastAsUser(intent, UserHandle.SYSTEM,
android.Manifest.permission.MASTER_CLEAR,
new BroadcastReceiver() {
diff --git a/core/java/android/service/notification/INotificationListener.aidl b/core/java/android/service/notification/INotificationListener.aidl
index 01d3391d32af..b26e32835aa1 100644
--- a/core/java/android/service/notification/INotificationListener.aidl
+++ b/core/java/android/service/notification/INotificationListener.aidl
@@ -34,6 +34,6 @@ oneway interface INotificationListener
void onInterruptionFilterChanged(int interruptionFilter);
// rankers only
- void onNotificationEnqueued(in IStatusBarNotificationHolder notificationHolder, int importance, boolean user);
+ void onNotificationEnqueued(in IStatusBarNotificationHolder notificationHolder);
void onNotificationSnoozedUntilContext(in IStatusBarNotificationHolder notificationHolder, String snoozeCriterionId);
}
diff --git a/core/java/android/service/notification/NotificationAssistantService.java b/core/java/android/service/notification/NotificationAssistantService.java
index cecdbee75b00..de86b2d2b6ed 100644
--- a/core/java/android/service/notification/NotificationAssistantService.java
+++ b/core/java/android/service/notification/NotificationAssistantService.java
@@ -77,12 +77,9 @@ public abstract class NotificationAssistantService extends NotificationListenerS
* A notification was posted by an app. Called before alert.
*
* @param sbn the new notification
- * @param importance the initial importance of the notification.
- * @param user true if the initial importance reflects an explicit user preference.
* @return an adjustment or null to take no action, within 100ms.
*/
- abstract public Adjustment onNotificationEnqueued(StatusBarNotification sbn,
- int importance, boolean user);
+ abstract public Adjustment onNotificationEnqueued(StatusBarNotification sbn);
/**
* Updates a notification. N.B. this won’t cause
@@ -202,8 +199,7 @@ public abstract class NotificationAssistantService extends NotificationListenerS
private class NotificationAssistantServiceWrapper extends NotificationListenerWrapper {
@Override
- public void onNotificationEnqueued(IStatusBarNotificationHolder sbnHolder,
- int importance, boolean user) {
+ public void onNotificationEnqueued(IStatusBarNotificationHolder sbnHolder) {
StatusBarNotification sbn;
try {
sbn = sbnHolder.get();
@@ -214,8 +210,6 @@ public abstract class NotificationAssistantService extends NotificationListenerS
SomeArgs args = SomeArgs.obtain();
args.arg1 = sbn;
- args.argi1 = importance;
- args.argi2 = user ? 1 : 0;
mHandler.obtainMessage(MyHandler.MSG_ON_NOTIFICATION_ENQUEUED,
args).sendToTarget();
}
@@ -254,10 +248,8 @@ public abstract class NotificationAssistantService extends NotificationListenerS
case MSG_ON_NOTIFICATION_ENQUEUED: {
SomeArgs args = (SomeArgs) msg.obj;
StatusBarNotification sbn = (StatusBarNotification) args.arg1;
- final int importance = args.argi1;
- final boolean user = args.argi2 == 1;
args.recycle();
- Adjustment adjustment = onNotificationEnqueued(sbn, importance, user);
+ Adjustment adjustment = onNotificationEnqueued(sbn);
if (adjustment != null) {
if (!isBound()) return;
try {
diff --git a/core/java/android/service/notification/NotificationListenerService.java b/core/java/android/service/notification/NotificationListenerService.java
index 22ad83acf684..e5abdac9e860 100644
--- a/core/java/android/service/notification/NotificationListenerService.java
+++ b/core/java/android/service/notification/NotificationListenerService.java
@@ -1100,8 +1100,8 @@ public abstract class NotificationListenerService extends Service {
}
@Override
- public void onNotificationEnqueued(IStatusBarNotificationHolder notificationHolder,
- int importance, boolean user) throws RemoteException {
+ public void onNotificationEnqueued(IStatusBarNotificationHolder notificationHolder)
+ throws RemoteException {
// no-op in the listener
}
diff --git a/core/java/android/text/SpannableStringInternal.java b/core/java/android/text/SpannableStringInternal.java
index 4b02df86c82e..60d8a0fc8894 100644
--- a/core/java/android/text/SpannableStringInternal.java
+++ b/core/java/android/text/SpannableStringInternal.java
@@ -53,12 +53,16 @@ import java.lang.reflect.Array;
* @param end End index in the source object.
*/
private final void copySpans(Spanned src, int start, int end) {
- Object[] spans = src.getSpans(start, end, Object.class);
+ final Object[] spans = src.getSpans(start, end, Object.class);
for (int i = 0; i < spans.length; i++) {
+ if (spans[i] instanceof NoCopySpan) {
+ continue;
+ }
+
int st = src.getSpanStart(spans[i]);
int en = src.getSpanEnd(spans[i]);
- int fl = src.getSpanFlags(spans[i]);
+ final int fl = src.getSpanFlags(spans[i]);
if (st < start)
st = start;
@@ -78,33 +82,42 @@ import java.lang.reflect.Array;
* @param end End index in the source object.
*/
private final void copySpans(SpannableStringInternal src, int start, int end) {
- if (start == 0 && end == src.length()) {
+ int count = 0;
+ boolean includesNoCopySpan = false;
+ final int[] srcData = src.mSpanData;
+ final Object[] srcSpans = src.mSpans;
+ final int limit = src.mSpanCount;
+
+ for (int i = 0; i < limit; i++) {
+ int spanStart = srcData[i * COLUMNS + START];
+ int spanEnd = srcData[i * COLUMNS + END];
+ if (isOutOfCopyRange(start, end, spanStart, spanEnd)) continue;
+ if (srcSpans[i] instanceof NoCopySpan) {
+ includesNoCopySpan = true;
+ continue;
+ }
+ count++;
+ }
+
+ if (count == 0) return;
+
+ if (!includesNoCopySpan && start == 0 && end == src.length()) {
mSpans = ArrayUtils.newUnpaddedObjectArray(src.mSpans.length);
mSpanData = new int[src.mSpanData.length];
mSpanCount = src.mSpanCount;
System.arraycopy(src.mSpans, 0, mSpans, 0, src.mSpans.length);
System.arraycopy(src.mSpanData, 0, mSpanData, 0, mSpanData.length);
} else {
- int count = 0;
- int[] srcData = src.mSpanData;
- int limit = src.mSpanCount;
- for (int i = 0; i < limit; i++) {
- int spanStart = srcData[i * COLUMNS + START];
- int spanEnd = srcData[i * COLUMNS + END];
- if (isOutOfCopyRange(start, end, spanStart, spanEnd)) continue;
- count++;
- }
-
- if (count == 0) return;
-
- Object[] srcSpans = src.mSpans;
mSpanCount = count;
mSpans = ArrayUtils.newUnpaddedObjectArray(mSpanCount);
mSpanData = new int[mSpans.length * COLUMNS];
for (int i = 0, j = 0; i < limit; i++) {
int spanStart = srcData[i * COLUMNS + START];
int spanEnd = srcData[i * COLUMNS + END];
- if (isOutOfCopyRange(start, end, spanStart, spanEnd)) continue;
+ if (isOutOfCopyRange(start, end, spanStart, spanEnd)
+ || srcSpans[i] instanceof NoCopySpan) {
+ continue;
+ }
if (spanStart < start) spanStart = start;
if (spanEnd > end) spanEnd = end;
diff --git a/core/java/android/view/SurfaceControl.java b/core/java/android/view/SurfaceControl.java
index 5bb577f661a4..cf8da179bab5 100644
--- a/core/java/android/view/SurfaceControl.java
+++ b/core/java/android/view/SurfaceControl.java
@@ -34,7 +34,7 @@ public class SurfaceControl {
private static final String TAG = "SurfaceControl";
private static native long nativeCreate(SurfaceSession session, String name,
- int w, int h, int format, int flags, long parentObject)
+ int w, int h, int format, int flags, long parentObject, int windowType, int ownerUid)
throws OutOfResourcesException;
private static native void nativeRelease(long nativeObject);
private static native void nativeDestroy(long nativeObject);
@@ -281,17 +281,25 @@ public class SurfaceControl {
* @param h The surface initial height.
* @param flags The surface creation flags. Should always include {@link #HIDDEN}
* in the creation flags.
+ * @param windowType The type of the window as specified in WindowManager.java.
+ * @param ownerUid A unique per-app ID.
*
* @throws throws OutOfResourcesException If the SurfaceControl cannot be created.
*/
public SurfaceControl(SurfaceSession session,
- String name, int w, int h, int format, int flags)
+ String name, int w, int h, int format, int flags, int windowType, int ownerUid)
throws OutOfResourcesException {
- this(session, name, w, h, format, flags, null);
+ this(session, name, w, h, format, flags, null, windowType, ownerUid);
}
public SurfaceControl(SurfaceSession session,
- String name, int w, int h, int format, int flags, SurfaceControl parent)
+ String name, int w, int h, int format, int flags)
+ throws OutOfResourcesException {
+ this(session, name, w, h, format, flags, null, -1, -1);
+ }
+
+ public SurfaceControl(SurfaceSession session, String name, int w, int h, int format, int flags,
+ SurfaceControl parent, int windowType, int ownerUid)
throws OutOfResourcesException {
if (session == null) {
throw new IllegalArgumentException("session must not be null");
@@ -310,7 +318,8 @@ public class SurfaceControl {
}
mName = name;
- mNativeObject = nativeCreate(session, name, w, h, format, flags, parent != null ? parent.mNativeObject : 0);
+ mNativeObject = nativeCreate(session, name, w, h, format, flags,
+ parent != null ? parent.mNativeObject : 0, windowType, ownerUid);
if (mNativeObject == 0) {
throw new OutOfResourcesException(
"Couldn't allocate SurfaceControl native object");
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index cbe43f7ba0a2..1292243d1b05 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -19987,38 +19987,39 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
}
/**
- * {@hide}
* @param id the id of the view to be found
* @return the view of the specified id, null if cannot be found
+ * @hide
*/
- protected View findViewTraversal(@IdRes int id) {
+ protected <T extends View> T findViewTraversal(@IdRes int id) {
if (id == mID) {
- return this;
+ return (T) this;
}
return null;
}
/**
- * {@hide}
* @param tag the tag of the view to be found
* @return the view of specified tag, null if cannot be found
+ * @hide
*/
- protected View findViewWithTagTraversal(Object tag) {
+ protected <T extends View> T findViewWithTagTraversal(Object tag) {
if (tag != null && tag.equals(mTag)) {
- return this;
+ return (T) this;
}
return null;
}
/**
- * {@hide}
* @param predicate The predicate to evaluate.
* @param childToSkip If not null, ignores this child during the recursive traversal.
* @return The first view that matches the predicate or null.
+ * @hide
*/
- protected View findViewByPredicateTraversal(Predicate<View> predicate, View childToSkip) {
+ protected <T extends View> T findViewByPredicateTraversal(Predicate<View> predicate,
+ View childToSkip) {
if (predicate.apply(this)) {
- return this;
+ return (T) this;
}
return null;
}
@@ -20031,7 +20032,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
* @return The view that has the given id in the hierarchy or null
*/
@Nullable
- public final View findViewById(@IdRes int id) {
+ public final <T extends View> T findViewById(@IdRes int id) {
if (id < 0) {
return null;
}
@@ -20044,11 +20045,11 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
* @param accessibilityId The searched accessibility id.
* @return The found view.
*/
- final View findViewByAccessibilityId(int accessibilityId) {
+ final <T extends View> T findViewByAccessibilityId(int accessibilityId) {
if (accessibilityId < 0) {
return null;
}
- View view = findViewByAccessibilityIdTraversal(accessibilityId);
+ T view = findViewByAccessibilityIdTraversal(accessibilityId);
if (view != null) {
return view.includeForAccessibility() ? view : null;
}
@@ -20067,12 +20068,11 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
*
* @param accessibilityId The accessibility id.
* @return The found view.
- *
* @hide
*/
- public View findViewByAccessibilityIdTraversal(int accessibilityId) {
+ public <T extends View> T findViewByAccessibilityIdTraversal(int accessibilityId) {
if (getAccessibilityViewId() == accessibilityId) {
- return this;
+ return (T) this;
}
return null;
}
@@ -20084,7 +20084,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
* @param tag The tag to search for, using "tag.equals(getTag())".
* @return The View that has the given tag in the hierarchy or null
*/
- public final View findViewWithTag(Object tag) {
+ public final <T extends View> T findViewWithTag(Object tag) {
if (tag == null) {
return null;
}
@@ -20092,19 +20092,18 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
}
/**
- * {@hide}
* Look for a child view that matches the specified predicate.
* If this view matches the predicate, return this view.
*
* @param predicate The predicate to evaluate.
* @return The first view that matches the predicate or null.
+ * @hide
*/
- public final View findViewByPredicate(Predicate<View> predicate) {
+ public final <T extends View> T findViewByPredicate(Predicate<View> predicate) {
return findViewByPredicateTraversal(predicate, null);
}
/**
- * {@hide}
* Look for a child view that matches the specified predicate,
* starting with the specified view and its descendents and then
* recusively searching the ancestors and siblings of that view
@@ -20118,11 +20117,13 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
* @param start The view to start from.
* @param predicate The predicate to evaluate.
* @return The first view that matches the predicate or null.
+ * @hide
*/
- public final View findViewByPredicateInsideOut(View start, Predicate<View> predicate) {
+ public final <T extends View> T findViewByPredicateInsideOut(
+ View start, Predicate<View> predicate) {
View childToSkip = null;
for (;;) {
- View view = start.findViewByPredicateTraversal(predicate, childToSkip);
+ T view = start.findViewByPredicateTraversal(predicate, childToSkip);
if (view != null || start == this) {
return view;
}
diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java
index 28b9dcc308b5..ab10ac17d01b 100644
--- a/core/java/android/view/ViewGroup.java
+++ b/core/java/android/view/ViewGroup.java
@@ -4211,9 +4211,9 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager
* {@hide}
*/
@Override
- protected View findViewTraversal(@IdRes int id) {
+ protected <T extends View> T findViewTraversal(@IdRes int id) {
if (id == mID) {
- return this;
+ return (T) this;
}
final View[] where = mChildren;
@@ -4226,7 +4226,7 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager
v = v.findViewById(id);
if (v != null) {
- return v;
+ return (T) v;
}
}
}
@@ -4238,9 +4238,9 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager
* {@hide}
*/
@Override
- protected View findViewWithTagTraversal(Object tag) {
+ protected <T extends View> T findViewWithTagTraversal(Object tag) {
if (tag != null && tag.equals(mTag)) {
- return this;
+ return (T) this;
}
final View[] where = mChildren;
@@ -4253,7 +4253,7 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager
v = v.findViewWithTag(tag);
if (v != null) {
- return v;
+ return (T) v;
}
}
}
@@ -4265,9 +4265,10 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager
* {@hide}
*/
@Override
- protected View findViewByPredicateTraversal(Predicate<View> predicate, View childToSkip) {
+ protected <T extends View> T findViewByPredicateTraversal(Predicate<View> predicate,
+ View childToSkip) {
if (predicate.apply(this)) {
- return this;
+ return (T) this;
}
final View[] where = mChildren;
@@ -4280,7 +4281,7 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager
v = v.findViewByPredicate(predicate);
if (v != null) {
- return v;
+ return (T) v;
}
}
}
diff --git a/core/java/android/webkit/WebViewClient.java b/core/java/android/webkit/WebViewClient.java
index 8703468a74a7..0a73e17dd6e4 100644
--- a/core/java/android/webkit/WebViewClient.java
+++ b/core/java/android/webkit/WebViewClient.java
@@ -488,7 +488,8 @@ public class WebViewClient {
* @param view The WebView which needs to be cleaned up.
* @param detail the reason why it exited.
* @return true if the host application handled the situation that process has
- * exited, otherwise, application will crash.
+ * exited, otherwise, application will crash if render process crashed,
+ * or be killed if render process was killed by the system.
*/
public boolean onRenderProcessGone(WebView view, RenderProcessGoneDetail detail) {
return false;
diff --git a/core/java/android/widget/ActivityChooserView.java b/core/java/android/widget/ActivityChooserView.java
index 51587a74987d..9a39a17ce223 100644
--- a/core/java/android/widget/ActivityChooserView.java
+++ b/core/java/android/widget/ActivityChooserView.java
@@ -250,7 +250,7 @@ public class ActivityChooserView extends ViewGroup implements ActivityChooserMod
mDefaultActivityButton = (FrameLayout) findViewById(R.id.default_activity_button);
mDefaultActivityButton.setOnClickListener(mCallbacks);
mDefaultActivityButton.setOnLongClickListener(mCallbacks);
- mDefaultActivityButtonImage = (ImageView) mDefaultActivityButton.findViewById(R.id.image);
+ mDefaultActivityButtonImage = mDefaultActivityButton.findViewById(R.id.image);
final FrameLayout expandButton = (FrameLayout) findViewById(R.id.expand_activities_button);
expandButton.setOnClickListener(mCallbacks);
@@ -282,7 +282,7 @@ public class ActivityChooserView extends ViewGroup implements ActivityChooserMod
mExpandActivityOverflowButton = expandButton;
mExpandActivityOverflowButtonImage =
- (ImageView) expandButton.findViewById(R.id.image);
+ expandButton.findViewById(R.id.image);
mExpandActivityOverflowButtonImage.setImageDrawable(expandActivityOverflowButtonDrawable);
mAdapter = new ActivityChooserViewAdapter();
@@ -760,7 +760,7 @@ public class ActivityChooserView extends ViewGroup implements ActivityChooserMod
convertView = LayoutInflater.from(getContext()).inflate(
R.layout.activity_chooser_view_list_item, parent, false);
convertView.setId(ITEM_VIEW_TYPE_FOOTER);
- TextView titleView = (TextView) convertView.findViewById(R.id.title);
+ TextView titleView = convertView.findViewById(R.id.title);
titleView.setText(mContext.getString(
R.string.activity_chooser_view_see_all));
}
@@ -772,11 +772,11 @@ public class ActivityChooserView extends ViewGroup implements ActivityChooserMod
}
PackageManager packageManager = mContext.getPackageManager();
// Set the icon
- ImageView iconView = (ImageView) convertView.findViewById(R.id.icon);
+ ImageView iconView = convertView.findViewById(R.id.icon);
ResolveInfo activity = (ResolveInfo) getItem(position);
iconView.setImageDrawable(activity.loadIcon(packageManager));
// Set the title.
- TextView titleView = (TextView) convertView.findViewById(R.id.title);
+ TextView titleView = convertView.findViewById(R.id.title);
titleView.setText(activity.loadLabel(packageManager));
// Highlight the default.
if (mShowDefaultActivity && position == 0 && mHighlightDefaultActivity) {
diff --git a/core/java/android/widget/AppSecurityPermissions.java b/core/java/android/widget/AppSecurityPermissions.java
index 68e6809a3f68..06d486813b1b 100644
--- a/core/java/android/widget/AppSecurityPermissions.java
+++ b/core/java/android/widget/AppSecurityPermissions.java
@@ -451,7 +451,7 @@ public class AppSecurityPermissions {
private View getPermissionsView(int which, boolean showRevokeUI) {
LinearLayout permsView = (LinearLayout) mInflater.inflate(R.layout.app_perms_summary, null);
- LinearLayout displayList = (LinearLayout) permsView.findViewById(R.id.perms_list);
+ LinearLayout displayList = permsView.findViewById(R.id.perms_list);
View noPermsView = permsView.findViewById(R.id.no_permissions);
displayPermissions(mPermGroupsList, displayList, which, showRevokeUI);
@@ -517,8 +517,8 @@ public class AppSecurityPermissions {
CharSequence grpName, CharSequence permList, boolean dangerous, Drawable icon) {
View permView = inflater.inflate(R.layout.app_permission_item_old, null);
- TextView permGrpView = (TextView) permView.findViewById(R.id.permission_group);
- TextView permDescView = (TextView) permView.findViewById(R.id.permission_list);
+ TextView permGrpView = permView.findViewById(R.id.permission_group);
+ TextView permDescView = permView.findViewById(R.id.permission_list);
ImageView imgView = (ImageView)permView.findViewById(R.id.perm_icon);
imgView.setImageDrawable(icon);
diff --git a/core/java/android/widget/ArrayAdapter.java b/core/java/android/widget/ArrayAdapter.java
index bbc50dafa576..81f0d3d0a352 100644
--- a/core/java/android/widget/ArrayAdapter.java
+++ b/core/java/android/widget/ArrayAdapter.java
@@ -388,7 +388,7 @@ public class ArrayAdapter<T> extends BaseAdapter implements Filterable, ThemedSp
text = (TextView) view;
} else {
// Otherwise, find the TextView field within the layout
- text = (TextView) view.findViewById(mFieldId);
+ text = view.findViewById(mFieldId);
if (text == null) {
throw new RuntimeException("Failed to find view with ID "
diff --git a/core/java/android/widget/CalendarViewLegacyDelegate.java b/core/java/android/widget/CalendarViewLegacyDelegate.java
index 557d41179921..1b899dbf6d03 100644
--- a/core/java/android/widget/CalendarViewLegacyDelegate.java
+++ b/core/java/android/widget/CalendarViewLegacyDelegate.java
@@ -316,9 +316,9 @@ class CalendarViewLegacyDelegate extends CalendarView.AbstractCalendarViewDelega
View content = layoutInflater.inflate(R.layout.calendar_view, null, false);
mDelegator.addView(content);
- mListView = (ListView) mDelegator.findViewById(R.id.list);
- mDayNamesHeader = (ViewGroup) content.findViewById(R.id.day_names);
- mMonthName = (TextView) content.findViewById(R.id.month_name);
+ mListView = mDelegator.findViewById(R.id.list);
+ mDayNamesHeader = content.findViewById(R.id.day_names);
+ mMonthName = content.findViewById(R.id.month_name);
setUpHeader();
setUpListView();
diff --git a/core/java/android/widget/DatePickerCalendarDelegate.java b/core/java/android/widget/DatePickerCalendarDelegate.java
index f712685ec3e0..907250aa5598 100755
--- a/core/java/android/widget/DatePickerCalendarDelegate.java
+++ b/core/java/android/widget/DatePickerCalendarDelegate.java
@@ -115,10 +115,10 @@ class DatePickerCalendarDelegate extends DatePicker.AbstractDatePickerDelegate {
mDelegator.addView(mContainer);
// Set up header views.
- final ViewGroup header = (ViewGroup) mContainer.findViewById(R.id.date_picker_header);
- mHeaderYear = (TextView) header.findViewById(R.id.date_picker_header_year);
+ final ViewGroup header = mContainer.findViewById(R.id.date_picker_header);
+ mHeaderYear = header.findViewById(R.id.date_picker_header_year);
mHeaderYear.setOnClickListener(mOnHeaderClickListener);
- mHeaderMonthDay = (TextView) header.findViewById(R.id.date_picker_header_date);
+ mHeaderMonthDay = header.findViewById(R.id.date_picker_header_date);
mHeaderMonthDay.setOnClickListener(mOnHeaderClickListener);
// For the sake of backwards compatibility, attempt to extract the text
@@ -154,10 +154,10 @@ class DatePickerCalendarDelegate extends DatePicker.AbstractDatePickerDelegate {
a.recycle();
// Set up picker container.
- mAnimator = (ViewAnimator) mContainer.findViewById(R.id.animator);
+ mAnimator = mContainer.findViewById(R.id.animator);
// Set up day picker view.
- mDayPickerView = (DayPickerView) mAnimator.findViewById(R.id.date_picker_day_picker);
+ mDayPickerView = mAnimator.findViewById(R.id.date_picker_day_picker);
mDayPickerView.setFirstDayOfWeek(mFirstDayOfWeek);
mDayPickerView.setMinDate(mMinDate.getTimeInMillis());
mDayPickerView.setMaxDate(mMaxDate.getTimeInMillis());
@@ -165,7 +165,7 @@ class DatePickerCalendarDelegate extends DatePicker.AbstractDatePickerDelegate {
mDayPickerView.setOnDaySelectedListener(mOnDaySelectedListener);
// Set up year picker view.
- mYearPickerView = (YearPickerView) mAnimator.findViewById(R.id.date_picker_year_picker);
+ mYearPickerView = mAnimator.findViewById(R.id.date_picker_year_picker);
mYearPickerView.setRange(mMinDate, mMaxDate);
mYearPickerView.setYear(mCurrentDate.get(Calendar.YEAR));
mYearPickerView.setOnYearSelectedListener(mOnYearSelectedListener);
diff --git a/core/java/android/widget/DayPickerPagerAdapter.java b/core/java/android/widget/DayPickerPagerAdapter.java
index 8d5bf8f7bce4..63621e123320 100644
--- a/core/java/android/widget/DayPickerPagerAdapter.java
+++ b/core/java/android/widget/DayPickerPagerAdapter.java
@@ -225,7 +225,7 @@ class DayPickerPagerAdapter extends PagerAdapter {
public Object instantiateItem(ViewGroup container, int position) {
final View itemView = mInflater.inflate(mLayoutResId, container, false);
- final SimpleMonthView v = (SimpleMonthView) itemView.findViewById(mCalendarViewId);
+ final SimpleMonthView v = itemView.findViewById(mCalendarViewId);
v.setOnDayClickListener(mOnDayClickListener);
v.setMonthTextAppearance(mMonthTextAppearance);
v.setDayOfWeekTextAppearance(mDayOfWeekTextAppearance);
diff --git a/core/java/android/widget/DayPickerViewPager.java b/core/java/android/widget/DayPickerViewPager.java
index 94022ae11b6d..058baa628d46 100644
--- a/core/java/android/widget/DayPickerViewPager.java
+++ b/core/java/android/widget/DayPickerViewPager.java
@@ -137,9 +137,10 @@ class DayPickerViewPager extends ViewPager {
}
@Override
- protected View findViewByPredicateTraversal(Predicate<View> predicate, View childToSkip) {
+ protected <T extends View> T findViewByPredicateTraversal(Predicate<View> predicate,
+ View childToSkip) {
if (predicate.apply(this)) {
- return this;
+ return (T) this;
}
// Always try the selected view first.
@@ -148,7 +149,7 @@ class DayPickerViewPager extends ViewPager {
if (current != childToSkip && current != null) {
final View v = current.findViewByPredicate(predicate);
if (v != null) {
- return v;
+ return (T) v;
}
}
@@ -160,7 +161,7 @@ class DayPickerViewPager extends ViewPager {
final View v = child.findViewByPredicate(predicate);
if (v != null) {
- return v;
+ return (T) v;
}
}
}
diff --git a/core/java/android/widget/ListView.java b/core/java/android/widget/ListView.java
index e52c13b19534..b7da04e9786a 100644
--- a/core/java/android/widget/ListView.java
+++ b/core/java/android/widget/ListView.java
@@ -3735,32 +3735,30 @@ public class ListView extends AbsListView {
}
}
- /* (non-Javadoc)
+ /**
* @see android.view.View#findViewById(int)
- * First look in our children, then in any header and footer views that may be scrolled off.
+ * @removed For internal use only. This should have been hidden.
*/
@Override
- protected View findViewTraversal(@IdRes int id) {
- View v;
- v = super.findViewTraversal(id);
+ protected <T extends View> T findViewTraversal(@IdRes int id) {
+ // First look in our children, then in any header and footer views that
+ // may be scrolled off.
+ View v = super.findViewTraversal(id);
if (v == null) {
v = findViewInHeadersOrFooters(mHeaderViewInfos, id);
if (v != null) {
- return v;
+ return (T) v;
}
v = findViewInHeadersOrFooters(mFooterViewInfos, id);
if (v != null) {
- return v;
+ return (T) v;
}
}
- return v;
+ return (T) v;
}
- /* (non-Javadoc)
- *
- * Look in the passed in list of headers or footers for the view.
- */
View findViewInHeadersOrFooters(ArrayList<FixedViewInfo> where, int id) {
+ // Look in the passed in list of headers or footers for the view.
if (where != null) {
int len = where.size();
View v;
@@ -3780,33 +3778,32 @@ public class ListView extends AbsListView {
return null;
}
- /* (non-Javadoc)
+ /**
* @see android.view.View#findViewWithTag(Object)
- * First look in our children, then in any header and footer views that may be scrolled off.
+ * @removed For internal use only. This should have been hidden.
*/
@Override
- protected View findViewWithTagTraversal(Object tag) {
- View v;
- v = super.findViewWithTagTraversal(tag);
+ protected <T extends View> T findViewWithTagTraversal(Object tag) {
+ // First look in our children, then in any header and footer views that
+ // may be scrolled off.
+ View v = super.findViewWithTagTraversal(tag);
if (v == null) {
v = findViewWithTagInHeadersOrFooters(mHeaderViewInfos, tag);
if (v != null) {
- return v;
+ return (T) v;
}
v = findViewWithTagInHeadersOrFooters(mFooterViewInfos, tag);
if (v != null) {
- return v;
+ return (T) v;
}
}
- return v;
+ return (T) v;
}
- /* (non-Javadoc)
- *
- * Look in the passed in list of headers or footers for the view with the tag.
- */
View findViewWithTagInHeadersOrFooters(ArrayList<FixedViewInfo> where, Object tag) {
+ // Look in the passed in list of headers or footers for the view with
+ // the tag.
if (where != null) {
int len = where.size();
View v;
@@ -3827,32 +3824,33 @@ public class ListView extends AbsListView {
}
/**
- * @hide
+ * First look in our children, then in any header and footer views that may
+ * be scrolled off.
+ *
* @see android.view.View#findViewByPredicate(Predicate)
- * First look in our children, then in any header and footer views that may be scrolled off.
+ * @hide
*/
@Override
- protected View findViewByPredicateTraversal(Predicate<View> predicate, View childToSkip) {
- View v;
- v = super.findViewByPredicateTraversal(predicate, childToSkip);
+ protected <T extends View> T findViewByPredicateTraversal(
+ Predicate<View> predicate, View childToSkip) {
+ View v = super.findViewByPredicateTraversal(predicate, childToSkip);
if (v == null) {
v = findViewByPredicateInHeadersOrFooters(mHeaderViewInfos, predicate, childToSkip);
if (v != null) {
- return v;
+ return (T) v;
}
v = findViewByPredicateInHeadersOrFooters(mFooterViewInfos, predicate, childToSkip);
if (v != null) {
- return v;
+ return (T) v;
}
}
- return v;
+ return (T) v;
}
- /* (non-Javadoc)
- *
- * Look in the passed in list of headers or footers for the first view that matches
- * the predicate.
+ /**
+ * Look in the passed in list of headers or footers for the first view that
+ * matches the predicate.
*/
View findViewByPredicateInHeadersOrFooters(ArrayList<FixedViewInfo> where,
Predicate<View> predicate, View childToSkip) {
diff --git a/core/java/android/widget/MediaController.java b/core/java/android/widget/MediaController.java
index 80086374ebd3..8e04f1cd9f5b 100644
--- a/core/java/android/widget/MediaController.java
+++ b/core/java/android/widget/MediaController.java
@@ -257,13 +257,13 @@ public class MediaController extends FrameLayout {
.getText(com.android.internal.R.string.lockscreen_transport_play_description);
mPauseDescription = res
.getText(com.android.internal.R.string.lockscreen_transport_pause_description);
- mPauseButton = (ImageButton) v.findViewById(com.android.internal.R.id.pause);
+ mPauseButton = v.findViewById(com.android.internal.R.id.pause);
if (mPauseButton != null) {
mPauseButton.requestFocus();
mPauseButton.setOnClickListener(mPauseListener);
}
- mFfwdButton = (ImageButton) v.findViewById(com.android.internal.R.id.ffwd);
+ mFfwdButton = v.findViewById(com.android.internal.R.id.ffwd);
if (mFfwdButton != null) {
mFfwdButton.setOnClickListener(mFfwdListener);
if (!mFromXml) {
@@ -271,7 +271,7 @@ public class MediaController extends FrameLayout {
}
}
- mRewButton = (ImageButton) v.findViewById(com.android.internal.R.id.rew);
+ mRewButton = v.findViewById(com.android.internal.R.id.rew);
if (mRewButton != null) {
mRewButton.setOnClickListener(mRewListener);
if (!mFromXml) {
@@ -280,16 +280,16 @@ public class MediaController extends FrameLayout {
}
// By default these are hidden. They will be enabled when setPrevNextListeners() is called
- mNextButton = (ImageButton) v.findViewById(com.android.internal.R.id.next);
+ mNextButton = v.findViewById(com.android.internal.R.id.next);
if (mNextButton != null && !mFromXml && !mListenersSet) {
mNextButton.setVisibility(View.GONE);
}
- mPrevButton = (ImageButton) v.findViewById(com.android.internal.R.id.prev);
+ mPrevButton = v.findViewById(com.android.internal.R.id.prev);
if (mPrevButton != null && !mFromXml && !mListenersSet) {
mPrevButton.setVisibility(View.GONE);
}
- mProgress = (ProgressBar) v.findViewById(com.android.internal.R.id.mediacontroller_progress);
+ mProgress = v.findViewById(com.android.internal.R.id.mediacontroller_progress);
if (mProgress != null) {
if (mProgress instanceof SeekBar) {
SeekBar seeker = (SeekBar) mProgress;
@@ -298,8 +298,8 @@ public class MediaController extends FrameLayout {
mProgress.setMax(1000);
}
- mEndTime = (TextView) v.findViewById(com.android.internal.R.id.time);
- mCurrentTime = (TextView) v.findViewById(com.android.internal.R.id.time_current);
+ mEndTime = v.findViewById(com.android.internal.R.id.time);
+ mCurrentTime = v.findViewById(com.android.internal.R.id.time_current);
mFormatBuilder = new StringBuilder();
mFormatter = new Formatter(mFormatBuilder, Locale.getDefault());
diff --git a/core/java/android/widget/RemoteViews.java b/core/java/android/widget/RemoteViews.java
index 359d04e71969..5505f2fef919 100644
--- a/core/java/android/widget/RemoteViews.java
+++ b/core/java/android/widget/RemoteViews.java
@@ -1578,7 +1578,7 @@ public class RemoteViews implements Parcelable, Filter {
@Override
public void apply(View root, ViewGroup rootParent, OnClickHandler handler) {
final Context context = root.getContext();
- final ViewGroup target = (ViewGroup) root.findViewById(viewId);
+ final ViewGroup target = root.findViewById(viewId);
if (target == null) return;
if (nestedViews != null) {
// Inflate nested views and add as children
@@ -1757,7 +1757,7 @@ public class RemoteViews implements Parcelable, Filter {
@Override
public void apply(View root, ViewGroup rootParent, OnClickHandler handler) {
- final TextView target = (TextView) root.findViewById(viewId);
+ final TextView target = root.findViewById(viewId);
if (target == null) return;
if (drawablesLoaded) {
if (isRelative) {
@@ -1857,7 +1857,7 @@ public class RemoteViews implements Parcelable, Filter {
@Override
public void apply(View root, ViewGroup rootParent, OnClickHandler handler) {
- final TextView target = (TextView) root.findViewById(viewId);
+ final TextView target = root.findViewById(viewId);
if (target == null) return;
target.setTextSize(units, size);
}
@@ -2045,7 +2045,7 @@ public class RemoteViews implements Parcelable, Filter {
@Override
public void apply(View root, ViewGroup rootParent, OnClickHandler handler) {
- final TextView target = (TextView) root.findViewById(viewId);
+ final TextView target = root.findViewById(viewId);
if (target == null) return;
Drawable[] drawables = isRelative
? target.getCompoundDrawablesRelative()
diff --git a/core/java/android/widget/SuggestionsAdapter.java b/core/java/android/widget/SuggestionsAdapter.java
index f833d1b4a9d6..fbb89930a035 100644
--- a/core/java/android/widget/SuggestionsAdapter.java
+++ b/core/java/android/widget/SuggestionsAdapter.java
@@ -286,7 +286,7 @@ class SuggestionsAdapter extends ResourceCursorAdapter implements OnClickListene
v.setTag(new ChildViewCache(v));
// Set up icon.
- final ImageView iconRefine = (ImageView) v.findViewById(R.id.edit_query);
+ final ImageView iconRefine = v.findViewById(R.id.edit_query);
iconRefine.setImageResource(mCommitIconResId);
return v;
@@ -304,11 +304,11 @@ class SuggestionsAdapter extends ResourceCursorAdapter implements OnClickListene
public final ImageView mIconRefine;
public ChildViewCache(View v) {
- mText1 = (TextView) v.findViewById(com.android.internal.R.id.text1);
- mText2 = (TextView) v.findViewById(com.android.internal.R.id.text2);
- mIcon1 = (ImageView) v.findViewById(com.android.internal.R.id.icon1);
- mIcon2 = (ImageView) v.findViewById(com.android.internal.R.id.icon2);
- mIconRefine = (ImageView) v.findViewById(com.android.internal.R.id.edit_query);
+ mText1 = v.findViewById(com.android.internal.R.id.text1);
+ mText2 = v.findViewById(com.android.internal.R.id.text2);
+ mIcon1 = v.findViewById(com.android.internal.R.id.icon1);
+ mIcon2 = v.findViewById(com.android.internal.R.id.icon2);
+ mIconRefine = v.findViewById(com.android.internal.R.id.edit_query);
}
}
diff --git a/core/java/android/widget/TabHost.java b/core/java/android/widget/TabHost.java
index 32418cdb8240..7e2cadfc8c45 100644
--- a/core/java/android/widget/TabHost.java
+++ b/core/java/android/widget/TabHost.java
@@ -619,7 +619,7 @@ mTabHost.addTab(TAB_TAG_1, "Hello, world!", "Tab 1");
mTabWidget, // tab widget is the parent
false); // no inflate params
- final TextView tv = (TextView) tabIndicator.findViewById(R.id.title);
+ final TextView tv = tabIndicator.findViewById(R.id.title);
tv.setText(mLabel);
if (context.getApplicationInfo().targetSdkVersion <= Build.VERSION_CODES.DONUT) {
@@ -653,8 +653,8 @@ mTabHost.addTab(TAB_TAG_1, "Hello, world!", "Tab 1");
mTabWidget, // tab widget is the parent
false); // no inflate params
- final TextView tv = (TextView) tabIndicator.findViewById(R.id.title);
- final ImageView iconView = (ImageView) tabIndicator.findViewById(R.id.icon);
+ final TextView tv = tabIndicator.findViewById(R.id.title);
+ final ImageView iconView = tabIndicator.findViewById(R.id.icon);
// when icon is gone by default, we're in exclusive mode
final boolean exclusive = iconView.getVisibility() == View.GONE;
diff --git a/core/java/android/widget/TimePickerSpinnerDelegate.java b/core/java/android/widget/TimePickerSpinnerDelegate.java
index 6a68f60b2c7a..4634631285e9 100644
--- a/core/java/android/widget/TimePickerSpinnerDelegate.java
+++ b/core/java/android/widget/TimePickerSpinnerDelegate.java
@@ -86,7 +86,7 @@ class TimePickerSpinnerDelegate extends TimePicker.AbstractTimePickerDelegate {
inflater.inflate(layoutResourceId, mDelegator, true);
// hour
- mHourSpinner = (NumberPicker) delegator.findViewById(R.id.hour);
+ mHourSpinner = delegator.findViewById(R.id.hour);
mHourSpinner.setOnValueChangedListener(new NumberPicker.OnValueChangeListener() {
public void onValueChange(NumberPicker spinner, int oldVal, int newVal) {
updateInputState();
@@ -100,17 +100,17 @@ class TimePickerSpinnerDelegate extends TimePicker.AbstractTimePickerDelegate {
onTimeChanged();
}
});
- mHourSpinnerInput = (EditText) mHourSpinner.findViewById(R.id.numberpicker_input);
+ mHourSpinnerInput = mHourSpinner.findViewById(R.id.numberpicker_input);
mHourSpinnerInput.setImeOptions(EditorInfo.IME_ACTION_NEXT);
// divider (only for the new widget style)
- mDivider = (TextView) mDelegator.findViewById(R.id.divider);
+ mDivider = mDelegator.findViewById(R.id.divider);
if (mDivider != null) {
setDividerText();
}
// minute
- mMinuteSpinner = (NumberPicker) mDelegator.findViewById(R.id.minute);
+ mMinuteSpinner = mDelegator.findViewById(R.id.minute);
mMinuteSpinner.setMinValue(0);
mMinuteSpinner.setMaxValue(59);
mMinuteSpinner.setOnLongPressUpdateInterval(100);
@@ -138,7 +138,7 @@ class TimePickerSpinnerDelegate extends TimePicker.AbstractTimePickerDelegate {
onTimeChanged();
}
});
- mMinuteSpinnerInput = (EditText) mMinuteSpinner.findViewById(R.id.numberpicker_input);
+ mMinuteSpinnerInput = mMinuteSpinner.findViewById(R.id.numberpicker_input);
mMinuteSpinnerInput.setImeOptions(EditorInfo.IME_ACTION_NEXT);
// Get the localized am/pm strings and use them in the spinner.
@@ -173,13 +173,13 @@ class TimePickerSpinnerDelegate extends TimePicker.AbstractTimePickerDelegate {
onTimeChanged();
}
});
- mAmPmSpinnerInput = (EditText) mAmPmSpinner.findViewById(R.id.numberpicker_input);
+ mAmPmSpinnerInput = mAmPmSpinner.findViewById(R.id.numberpicker_input);
mAmPmSpinnerInput.setImeOptions(EditorInfo.IME_ACTION_DONE);
}
if (isAmPmAtStart()) {
// Move the am/pm view to the beginning
- ViewGroup amPmParent = (ViewGroup) delegator.findViewById(R.id.timePickerLayout);
+ ViewGroup amPmParent = delegator.findViewById(R.id.timePickerLayout);
amPmParent.removeView(amPmView);
amPmParent.addView(amPmView, 0);
// Swap layout margins if needed. They may be not symmetrical (Old Standard Theme
diff --git a/core/java/android/widget/Toast.java b/core/java/android/widget/Toast.java
index 789e60b62799..bf0601d2851d 100644
--- a/core/java/android/widget/Toast.java
+++ b/core/java/android/widget/Toast.java
@@ -299,7 +299,7 @@ public class Toast {
if (mNextView == null) {
throw new RuntimeException("This Toast was not created with Toast.makeText()");
}
- TextView tv = (TextView) mNextView.findViewById(com.android.internal.R.id.message);
+ TextView tv = mNextView.findViewById(com.android.internal.R.id.message);
if (tv == null) {
throw new RuntimeException("This Toast was not created with Toast.makeText()");
}
diff --git a/core/java/android/widget/ZoomButtonsController.java b/core/java/android/widget/ZoomButtonsController.java
index 69b799716f02..1a3ca8667e39 100644
--- a/core/java/android/widget/ZoomButtonsController.java
+++ b/core/java/android/widget/ZoomButtonsController.java
@@ -264,7 +264,7 @@ public class ZoomButtonsController implements View.OnTouchListener {
.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
inflater.inflate(com.android.internal.R.layout.zoom_container, container);
- mControls = (ZoomControls) container.findViewById(com.android.internal.R.id.zoomControls);
+ mControls = container.findViewById(com.android.internal.R.id.zoomControls);
mControls.setOnZoomInClickListener(new OnClickListener() {
public void onClick(View v) {
dismissControlsDelayed(ZOOM_CONTROLS_TIMEOUT);
diff --git a/core/java/com/android/internal/widget/WatchHeaderListView.java b/core/java/com/android/internal/widget/WatchHeaderListView.java
index 4fd19c37bf47..53fa7ab93133 100644
--- a/core/java/com/android/internal/widget/WatchHeaderListView.java
+++ b/core/java/com/android/internal/widget/WatchHeaderListView.java
@@ -92,13 +92,14 @@ public class WatchHeaderListView extends ListView {
}
@Override
- protected View findViewByPredicateTraversal(Predicate<View> predicate, View childToSkip) {
+ protected <T extends View> T findViewByPredicateTraversal(
+ Predicate<View> predicate, View childToSkip) {
View v = super.findViewByPredicateTraversal(predicate, childToSkip);
if (v == null && mTopPanel != null && mTopPanel != childToSkip
&& !mTopPanel.isRootNamespace()) {
- return mTopPanel.findViewByPredicate(predicate);
+ return (T) mTopPanel.findViewByPredicate(predicate);
}
- return v;
+ return (T) v;
}
@Override
diff --git a/core/jni/android_view_SurfaceControl.cpp b/core/jni/android_view_SurfaceControl.cpp
index ab3e3113e1a2..0171562184dc 100644
--- a/core/jni/android_view_SurfaceControl.cpp
+++ b/core/jni/android_view_SurfaceControl.cpp
@@ -98,16 +98,18 @@ static struct {
// ----------------------------------------------------------------------------
static jlong nativeCreate(JNIEnv* env, jclass clazz, jobject sessionObj,
- jstring nameStr, jint w, jint h, jint format, jint flags, jlong parentObject) {
+ jstring nameStr, jint w, jint h, jint format, jint flags, jlong parentObject,
+ jint windowType, jint ownerUid) {
ScopedUtfChars name(env, nameStr);
sp<SurfaceComposerClient> client(android_view_SurfaceSession_getClient(env, sessionObj));
SurfaceControl *parent = reinterpret_cast<SurfaceControl*>(parentObject);
sp<SurfaceControl> surface = client->createSurface(
- String8(name.c_str()), w, h, format, flags, parent);
+ String8(name.c_str()), w, h, format, flags, parent, windowType, ownerUid);
if (surface == NULL) {
jniThrowException(env, OutOfResourcesException, NULL);
return 0;
}
+
surface->incStrong((void *)nativeCreate);
return reinterpret_cast<jlong>(surface.get());
}
@@ -260,7 +262,7 @@ static void nativeScreenshot(JNIEnv* env, jclass clazz, jobject displayTokenObj,
}
ScreenshotClient::capture(displayToken,
consumer->getIGraphicBufferProducer(), sourceCrop,
- width, height, uint32_t(minLayer), uint32_t(maxLayer),
+ width, height, minLayer, maxLayer,
useIdentityTransform);
}
}
@@ -742,7 +744,7 @@ static jobject nativeGetHdrCapabilities(JNIEnv* env, jclass clazz, jobject token
// ----------------------------------------------------------------------------
static const JNINativeMethod sSurfaceControlMethods[] = {
- {"nativeCreate", "(Landroid/view/SurfaceSession;Ljava/lang/String;IIIIJ)J",
+ {"nativeCreate", "(Landroid/view/SurfaceSession;Ljava/lang/String;IIIIJII)J",
(void*)nativeCreate },
{"nativeRelease", "(J)V",
(void*)nativeRelease },
diff --git a/core/res/res/values-mcc704-mnc01/config.xml b/core/res/res/values-mcc704-mnc01/config.xml
new file mode 100644
index 000000000000..10b647044c73
--- /dev/null
+++ b/core/res/res/values-mcc704-mnc01/config.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+** Copyright 2016, 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 my 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.
+*/
+-->
+
+<resources>
+ <!-- Do not translate. Defines the slots is Two Digit Number for dialing normally not USSD -->
+ <string-array name="config_twoDigitNumberPattern">
+ <item>"*1"</item>
+ <item>"*5"</item>
+ <item>"*9"</item>
+ </string-array>
+</resources>
diff --git a/core/res/res/values-mcc708-mnc001/config.xml b/core/res/res/values-mcc708-mnc001/config.xml
new file mode 100755
index 000000000000..7b7c48d46b14
--- /dev/null
+++ b/core/res/res/values-mcc708-mnc001/config.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+** Copyright 2016, 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 my 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.
+*/
+-->
+
+<!-- These resources are around just to allow their values to be customized
+ for different hardware and product builds. -->
+<resources>
+ <string-array translatable="false" name="config_twoDigitNumberPattern">
+ <item>"*1"</item>
+ <item>"*5"</item>
+ </string-array>
+</resources>
diff --git a/libs/androidfw/LoadedArsc.cpp b/libs/androidfw/LoadedArsc.cpp
index e17a3a6ff0a1..c7d0fa5b0cf3 100644
--- a/libs/androidfw/LoadedArsc.cpp
+++ b/libs/androidfw/LoadedArsc.cpp
@@ -116,7 +116,10 @@ bool LoadedPackage::FindEntry(uint8_t type_idx, uint16_t entry_idx, const ResTab
LoadedArscEntry* out_entry, ResTable_config* out_selected_config,
uint32_t* out_flags) const {
ATRACE_CALL();
- const TypeSpecPtr& ptr = type_specs_[type_idx];
+
+ // If the type IDs are offset in this package, we need to take that into account when searching
+ // for a type.
+ const TypeSpecPtr& ptr = type_specs_[type_idx - type_id_offset_];
if (ptr == nullptr) {
return false;
}
@@ -334,6 +337,15 @@ std::unique_ptr<LoadedPackage> LoadedPackage::Load(const Chunk& chunk) {
loaded_package->dynamic_ = true;
}
+ if (header->header.headerSize >= sizeof(ResTable_package)) {
+ uint32_t type_id_offset = dtohl(header->typeIdOffset);
+ if (type_id_offset > std::numeric_limits<uint8_t>::max()) {
+ LOG(ERROR) << "Type ID offset in RES_TABLE_PACKAGE_TYPE is too large.";
+ return {};
+ }
+ loaded_package->type_id_offset_ = static_cast<int>(type_id_offset);
+ }
+
util::ReadUtf16StringFromDevice(header->name, arraysize(header->name),
&loaded_package->package_name_);
@@ -385,7 +397,6 @@ std::unique_ptr<LoadedPackage> LoadedPackage::Load(const Chunk& chunk) {
LOG(ERROR) << "Too many type configurations, overflow detected.";
return {};
}
-
loaded_package->type_specs_.editItemAt(last_type_idx) = std::move(type_spec_ptr);
types_builder = {};
@@ -403,6 +414,12 @@ std::unique_ptr<LoadedPackage> LoadedPackage::Load(const Chunk& chunk) {
return {};
}
+ if (loaded_package->type_id_offset_ + static_cast<int>(type_spec->id) >
+ std::numeric_limits<uint8_t>::max()) {
+ LOG(ERROR) << "Chunk RES_TABLE_TYPE_SPEC_TYPE has out of range ID.";
+ return {};
+ }
+
// The data portion of this chunk contains entry_count 32bit entries,
// each one representing a set of flags.
// Here we only validate that the chunk is well formed.
diff --git a/libs/androidfw/include/androidfw/LoadedArsc.h b/libs/androidfw/include/androidfw/LoadedArsc.h
index 8362008eccc7..e8cb164ce201 100644
--- a/libs/androidfw/include/androidfw/LoadedArsc.h
+++ b/libs/androidfw/include/androidfw/LoadedArsc.h
@@ -94,6 +94,7 @@ class LoadedPackage {
std::string package_name_;
int package_id_ = -1;
bool dynamic_ = false;
+ int type_id_offset_ = 0;
ByteBucketArray<util::unique_cptr<TypeSpec>> type_specs_;
std::vector<DynamicPackageEntry> dynamic_package_map_;
diff --git a/libs/androidfw/tests/LoadedArsc_test.cpp b/libs/androidfw/tests/LoadedArsc_test.cpp
index 045507e2e277..f8aa61aa588a 100644
--- a/libs/androidfw/tests/LoadedArsc_test.cpp
+++ b/libs/androidfw/tests/LoadedArsc_test.cpp
@@ -139,6 +139,36 @@ TEST(LoadedArscTest, LoadAppAsSharedLibrary) {
EXPECT_EQ(0x7f, packages[0]->GetPackageId());
}
+TEST(LoadedArscTest, LoadFeatureSplit) {
+ std::string contents;
+ ASSERT_TRUE(ReadFileFromZipToString(GetTestDataPath() + "/feature/feature.apk", "resources.arsc",
+ &contents));
+ std::unique_ptr<LoadedArsc> loaded_arsc = LoadedArsc::Load(contents.data(), contents.size());
+ ASSERT_NE(nullptr, loaded_arsc);
+
+ ResTable_config desired_config;
+ memset(&desired_config, 0, sizeof(desired_config));
+
+ LoadedArscEntry entry;
+ ResTable_config selected_config;
+ uint32_t flags;
+
+ ASSERT_TRUE(loaded_arsc->FindEntry(basic::R::string::test3, desired_config, &entry,
+ &selected_config, &flags));
+
+ size_t len;
+ const char16_t* type_name16 = entry.type_string_ref.string16(&len);
+ ASSERT_NE(nullptr, type_name16);
+ ASSERT_NE(0u, len);
+
+ size_t utf8_len = utf16_to_utf8_length(type_name16, len);
+ std::string type_name;
+ type_name.resize(utf8_len);
+ utf16_to_utf8(type_name16, len, &*type_name.begin(), utf8_len + 1);
+
+ EXPECT_EQ(std::string("string"), type_name);
+}
+
// structs with size fields (like Res_value, ResTable_entry) should be
// backwards and forwards compatible (aka checking the size field against
// sizeof(Res_value) might not be backwards compatible.
diff --git a/libs/androidfw/tests/data/feature/feature.apk b/libs/androidfw/tests/data/feature/feature.apk
index 04940fb9bce2..767fed697034 100644
--- a/libs/androidfw/tests/data/feature/feature.apk
+++ b/libs/androidfw/tests/data/feature/feature.apk
Binary files differ
diff --git a/libs/hwui/tests/common/scenes/SaveLayer2Animation.cpp b/libs/hwui/tests/common/scenes/SaveLayer2Animation.cpp
new file mode 100644
index 000000000000..ad0b1f15abff
--- /dev/null
+++ b/libs/hwui/tests/common/scenes/SaveLayer2Animation.cpp
@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) 2017 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.
+ */
+
+#include "TestSceneBase.h"
+#include <string>
+#include <hwui/Paint.h>
+#include <minikin/Layout.h>
+
+class SaveLayer2Animation;
+
+static TestScene::Registrar _SaveLayer(TestScene::Info{
+ "savelayer2",
+ "Interleaving 20 drawText/drawRect ops with saveLayer"
+ "Tests the clipped saveLayer performance and FBO switching overhead.",
+ TestScene::simpleCreateScene<SaveLayer2Animation>
+});
+
+class SaveLayer2Animation : public TestScene {
+public:
+ Paint mBluePaint;
+ Paint mGreenPaint;
+
+ void createContent(int width, int height, Canvas& canvas) override {
+ canvas.drawColor(SkColorSetARGB(255, 255, 0, 0), SkBlendMode::kSrcOver);
+ SkIRect bounds = SkIRect::MakeWH(width, height);
+ int regions = 20;
+ int smallRectHeight = (bounds.height()/regions);
+ int padding = smallRectHeight / 4;
+ int top = bounds.fTop;
+
+ mBluePaint.setColor(SkColorSetARGB(255, 0, 0, 255));
+ mBluePaint.setTextSize(padding);
+ mGreenPaint.setColor(SkColorSetARGB(255, 0, 255, 0));
+ mGreenPaint.setTextSize(padding);
+
+ //interleave drawText and drawRect with saveLayer ops
+ for (int i = 0; i < regions; i++, top += smallRectHeight) {
+ canvas.saveLayer(bounds.fLeft, top, bounds.fRight, top + padding,
+ &mBluePaint, SaveFlags::ClipToLayer | SaveFlags::MatrixClip);
+ canvas.drawColor(SkColorSetARGB(255, 255, 255, 0), SkBlendMode::kSrcOver);
+ std::string stri = std::to_string(i);
+ std::string offscreen = "offscreen line " + stri;
+ std::unique_ptr<uint16_t[]> offtext = TestUtils::asciiToUtf16(offscreen.c_str());
+ canvas.drawText(offtext.get(), 0, offscreen.length(), offscreen.length(),
+ bounds.fLeft, top + padding, minikin::kBidi_Force_LTR, mBluePaint, nullptr);
+ canvas.restore();
+
+ canvas.drawRect(bounds.fLeft, top + padding, bounds.fRight,
+ top + smallRectHeight - padding, mBluePaint);
+ std::string onscreen = "onscreen line " + stri;
+ std::unique_ptr<uint16_t[]> ontext = TestUtils::asciiToUtf16(onscreen.c_str());
+ canvas.drawText(ontext.get(), 0, onscreen.length(), onscreen.length(), bounds.fLeft,
+ top + smallRectHeight - padding, minikin::kBidi_Force_LTR, mGreenPaint,
+ nullptr);
+ }
+ }
+ void doFrame(int frameNr) override {
+ }
+};
diff --git a/packages/SettingsLib/src/com/android/settingslib/SuggestionParser.java b/packages/SettingsLib/src/com/android/settingslib/SuggestionParser.java
index 21786c917763..13bbc3382ebf 100644
--- a/packages/SettingsLib/src/com/android/settingslib/SuggestionParser.java
+++ b/packages/SettingsLib/src/com/android/settingslib/SuggestionParser.java
@@ -15,21 +15,28 @@
*/
package com.android.settingslib;
+import android.Manifest;
+import android.accounts.Account;
+import android.accounts.AccountManager;
+import android.annotation.RequiresPermission;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
+import android.content.pm.PackageManager;
+import android.content.pm.UserInfo;
+import android.content.res.Resources;
+import android.net.ConnectivityManager;
+import android.net.NetworkInfo;
import android.os.UserHandle;
+import android.os.UserManager;
+import android.provider.Settings;
+import android.support.annotation.VisibleForTesting;
import android.text.TextUtils;
import android.util.ArrayMap;
import android.util.AttributeSet;
import android.util.Log;
import android.util.Pair;
import android.util.Xml;
-import android.provider.Settings;
-import android.accounts.Account;
-import android.accounts.AccountManager;
-import android.content.pm.PackageManager;
-import android.content.res.Resources;
import android.view.InflateException;
import com.android.settingslib.drawer.Tile;
import com.android.settingslib.drawer.TileUtils;
@@ -53,6 +60,20 @@ public class SuggestionParser {
// If defined and not true, do not should optional step.
private static final String META_DATA_IS_SUPPORTED = "com.android.settings.is_supported";
+ // If defined, only display this optional step if the current user is of that type.
+ private static final String META_DATA_REQUIRE_USER_TYPE =
+ "com.android.settings.require_user_type";
+
+ // If defined, only display this optional step if a connection is available.
+ private static final String META_DATA_IS_CONNECTION_REQUIRED =
+ "com.android.settings.require_connection";
+
+ // The valid values that setup wizard recognizes for differentiating user types.
+ private static final String META_DATA_PRIMARY_USER_TYPE_VALUE = "primary";
+ private static final String META_DATA_ADMIN_USER_TYPE_VALUE = "admin";
+ private static final String META_DATA_GUEST_USER_TYPE_VALUE = "guest";
+ private static final String META_DATA_RESTRICTED_USER_TYPE_VALUE = "restricted";
+
/**
* Allows suggestions to appear after a certain number of days, and to re-appear if dismissed.
* For instance:
@@ -75,7 +96,7 @@ public class SuggestionParser {
private final Context mContext;
private final List<SuggestionCategory> mSuggestionList;
- private final ArrayMap<Pair<String, String>, Tile> addCache = new ArrayMap<>();
+ private final ArrayMap<Pair<String, String>, Tile> mAddCache = new ArrayMap<>();
private final SharedPreferences mSharedPrefs;
public SuggestionParser(Context context, SharedPreferences sharedPrefs, int orderXml) {
@@ -85,6 +106,14 @@ public class SuggestionParser {
mSharedPrefs = sharedPrefs;
}
+ @VisibleForTesting
+ public SuggestionParser(Context context, SharedPreferences sharedPrefs) {
+ mContext = context;
+ mSuggestionList = new ArrayList<SuggestionCategory>();
+ mSharedPrefs = sharedPrefs;
+ Log.wtf(TAG, "Only use this constructor for testing");
+ }
+
public List<Tile> getSuggestions() {
List<Tile> suggestions = new ArrayList<>();
final int N = mSuggestionList.size();
@@ -111,23 +140,30 @@ public class SuggestionParser {
return false;
}
- private void readSuggestions(SuggestionCategory category, List<Tile> suggestions) {
- int countBefore = suggestions.size();
- Intent intent = new Intent(Intent.ACTION_MAIN);
- intent.addCategory(category.category);
- if (category.pkg != null) {
- intent.setPackage(category.pkg);
- }
- TileUtils.getTilesForIntent(mContext, new UserHandle(UserHandle.myUserId()), intent,
- addCache, null, suggestions, true, false);
+ @VisibleForTesting
+ public void filterSuggestions(List<Tile> suggestions, int countBefore) {
for (int i = countBefore; i < suggestions.size(); i++) {
if (!isAvailable(suggestions.get(i)) ||
!isSupported(suggestions.get(i)) ||
+ !satisifesRequiredUserType(suggestions.get(i)) ||
!satisfiesRequiredAccount(suggestions.get(i)) ||
+ !satisfiesConnectivity(suggestions.get(i)) ||
isDismissed(suggestions.get(i))) {
suggestions.remove(i--);
}
}
+ }
+
+ private void readSuggestions(SuggestionCategory category, List<Tile> suggestions) {
+ int countBefore = suggestions.size();
+ Intent intent = new Intent(Intent.ACTION_MAIN);
+ intent.addCategory(category.category);
+ if (category.pkg != null) {
+ intent.setPackage(category.pkg);
+ }
+ TileUtils.getTilesForIntent(mContext, new UserHandle(UserHandle.myUserId()), intent,
+ mAddCache, null, suggestions, true, false);
+ filterSuggestions(suggestions, countBefore);
if (!category.multiple && suggestions.size() > (countBefore + 1)) {
// If there are too many, remove them all and only re-add the one with the highest
// priority.
@@ -146,32 +182,77 @@ public class SuggestionParser {
}
private boolean isAvailable(Tile suggestion) {
- String featureRequired = suggestion.metaData.getString(META_DATA_REQUIRE_FEATURE);
- if (featureRequired != null) {
- return mContext.getPackageManager().hasSystemFeature(featureRequired);
+ final String featuresRequired = suggestion.metaData.getString(META_DATA_REQUIRE_FEATURE);
+ if (featuresRequired != null) {
+ for (String feature : featuresRequired.split(",")) {
+ if (TextUtils.isEmpty(feature)) {
+ Log.w(TAG, "Found empty substring when parsing required features: "
+ + featuresRequired);
+ } else if (!mContext.getPackageManager().hasSystemFeature(feature)) {
+ Log.i(TAG, suggestion.title + " requires unavailable feature " + feature);
+ return false;
+ }
+ }
+ }
+ return true;
+ }
+
+ @RequiresPermission(Manifest.permission.MANAGE_USERS)
+ private boolean satisifesRequiredUserType(Tile suggestion) {
+ final String requiredUser = suggestion.metaData.getString(META_DATA_REQUIRE_USER_TYPE);
+ if (requiredUser != null) {
+ final UserManager userManager = mContext.getSystemService(UserManager.class);
+ UserInfo userInfo = userManager.getUserInfo(UserHandle.myUserId());
+ for (String userType : requiredUser.split("\\|")) {
+ final boolean primaryUserCondtionMet = userInfo.isPrimary()
+ && META_DATA_PRIMARY_USER_TYPE_VALUE.equals(userType);
+ final boolean adminUserConditionMet = userInfo.isAdmin()
+ && META_DATA_ADMIN_USER_TYPE_VALUE.equals(userType);
+ final boolean guestUserCondtionMet = userInfo.isGuest()
+ && META_DATA_GUEST_USER_TYPE_VALUE.equals(userType);
+ final boolean restrictedUserCondtionMet = userInfo.isRestricted()
+ && META_DATA_RESTRICTED_USER_TYPE_VALUE.equals(userType);
+ if (primaryUserCondtionMet || adminUserConditionMet || guestUserCondtionMet
+ || restrictedUserCondtionMet) {
+ return true;
+ }
+ }
+ Log.i(TAG, suggestion.title + " requires user type " + requiredUser);
+ return false;
}
return true;
}
public boolean satisfiesRequiredAccount(Tile suggestion) {
- String requiredAccountType = suggestion.metaData.getString(META_DATA_REQUIRE_ACCOUNT);
+ final String requiredAccountType = suggestion.metaData.getString(META_DATA_REQUIRE_ACCOUNT);
if (requiredAccountType == null) {
return true;
}
AccountManager accountManager = AccountManager.get(mContext);
Account[] accounts = accountManager.getAccountsByType(requiredAccountType);
- return accounts.length > 0;
+ boolean satisfiesRequiredAccount = accounts.length > 0;
+ if (!satisfiesRequiredAccount) {
+ Log.i(TAG, suggestion.title + " requires unavailable account type "
+ + requiredAccountType);
+ }
+ return satisfiesRequiredAccount;
}
public boolean isSupported(Tile suggestion) {
- int isSupportedResource = suggestion.metaData.getInt(META_DATA_IS_SUPPORTED);
+ final int isSupportedResource = suggestion.metaData.getInt(META_DATA_IS_SUPPORTED);
try {
if (suggestion.intent == null) {
return false;
}
final Resources res = mContext.getPackageManager().getResourcesForActivity(
suggestion.intent.getComponent());
- return isSupportedResource != 0 ? res.getBoolean(isSupportedResource) : true;
+ boolean isSupported =
+ isSupportedResource != 0 ? res.getBoolean(isSupportedResource) : true;
+ if (!isSupported) {
+ Log.i(TAG, suggestion.title + " requires unsupported resource "
+ + isSupportedResource);
+ }
+ return isSupported;
} catch (PackageManager.NameNotFoundException e) {
Log.w(TAG, "Cannot find resources for " + suggestion.intent.getComponent());
return false;
@@ -181,6 +262,22 @@ public class SuggestionParser {
}
}
+ private boolean satisfiesConnectivity(Tile suggestion) {
+ final boolean isConnectionRequired =
+ suggestion.metaData.getBoolean(META_DATA_IS_CONNECTION_REQUIRED);
+ if (!isConnectionRequired) {
+ return true;
+ }
+ ConnectivityManager cm =
+ (ConnectivityManager) mContext.getSystemService(Context.CONNECTIVITY_SERVICE);
+ NetworkInfo netInfo = cm.getActiveNetworkInfo();
+ boolean satisfiesConnectivity = netInfo != null && netInfo.isConnectedOrConnecting();
+ if (!satisfiesConnectivity) {
+ Log.i(TAG, suggestion.title + " is missing required connection.");
+ }
+ return satisfiesConnectivity;
+ }
+
public boolean isCategoryDone(String category) {
String name = Settings.Secure.COMPLETED_CATEGORY_PREFIX + category;
return Settings.Secure.getInt(mContext.getContentResolver(), name, 0) != 0;
diff --git a/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPointPreference.java b/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPointPreference.java
index 703bc17c15c4..a77c31045662 100644
--- a/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPointPreference.java
+++ b/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPointPreference.java
@@ -13,7 +13,6 @@
* See the License for the specific language governing permissions and
* limitations under the License
*/
-
package com.android.settingslib.wifi;
import android.content.Context;
@@ -21,8 +20,8 @@ import android.content.pm.PackageManager;
import android.content.res.Resources;
import android.content.res.TypedArray;
import android.graphics.drawable.Drawable;
-import android.graphics.drawable.LayerDrawable;
import android.graphics.drawable.StateListDrawable;
+import android.net.NetworkBadging;
import android.net.ScoredNetwork;
import android.net.wifi.WifiConfiguration;
import android.os.Looper;
@@ -48,12 +47,9 @@ public class AccessPointPreference extends Preference {
R.attr.state_encrypted,
R.attr.state_saved
};
- private static final int[] STATE_NONE = {};
- private static final int[] wifi_signal_attributes = { R.attr.wifi_signal };
private static final int[] wifi_friction_attributes = { R.attr.wifi_friction };
- private final StateListDrawable mWifiSld;
private final StateListDrawable mFrictionSld;
private final int mBadgePadding;
private final UserBadgeCache mBadgeCache;
@@ -65,8 +61,6 @@ public class AccessPointPreference extends Preference {
private int mLevel;
private CharSequence mContentDescription;
private int mDefaultIconResId;
- private int mIconWidth;
- private int mIconHeight;
private int mWifiBadge = ScoredNetwork.BADGING_NONE;
static final int[] WIFI_CONNECTION_STRENGTH = {
@@ -79,7 +73,6 @@ public class AccessPointPreference extends Preference {
// Used for dummy pref.
public AccessPointPreference(Context context, AttributeSet attrs) {
super(context, attrs);
- mWifiSld = null;
mFrictionSld = null;
mBadgePadding = 0;
mBadgeCache = null;
@@ -95,11 +88,6 @@ public class AccessPointPreference extends Preference {
mAccessPoint.setTag(this);
mLevel = -1;
- mWifiSld = (StateListDrawable) context.getTheme()
- .obtainStyledAttributes(wifi_signal_attributes).getDrawable(0);
- // Save icon width and height to use for creating a badged icon
- setIconWidthAndHeight();
-
TypedArray frictionSld;
try {
frictionSld = context.getTheme().obtainStyledAttributes(wifi_friction_attributes);
@@ -126,11 +114,6 @@ public class AccessPointPreference extends Preference {
mLevel = -1;
mDefaultIconResId = iconResId;
- mWifiSld = (StateListDrawable) context.getTheme()
- .obtainStyledAttributes(wifi_signal_attributes).getDrawable(0);
- // Save icon width and height to use for creating a badged icon
- setIconWidthAndHeight();
-
TypedArray frictionSld;
try {
frictionSld = context.getTheme().obtainStyledAttributes(wifi_friction_attributes);
@@ -145,13 +128,6 @@ public class AccessPointPreference extends Preference {
.getDimensionPixelSize(R.dimen.wifi_preference_badge_padding);
}
- private void setIconWidthAndHeight() {
- // TODO(sghuman): Refactor this defined widths and heights into a dimension resource and
- // reference directly.
- mIconWidth = mWifiSld.getIntrinsicWidth();
- mIconHeight = mWifiSld.getIntrinsicHeight();
- }
-
public AccessPoint getAccessPoint() {
return mAccessPoint;
}
@@ -185,35 +161,15 @@ public class AccessPointPreference extends Preference {
protected void updateIcon(int level, Context context) {
if (level == -1) {
safeSetDefaultIcon();
+ return;
+ }
+ TronUtils.logWifiSettingsBadge(context, mWifiBadge);
+ Drawable drawable = NetworkBadging.getWifiIcon(level, mWifiBadge, getContext().getTheme());
+ if (!mForSavedNetworks && drawable != null) {
+ drawable.setTint(Utils.getColorAccent(getContext()));
+ setIcon(drawable);
} else {
- TronUtils.logWifiSettingsBadge(context, mWifiBadge);
- if (mWifiBadge != ScoredNetwork.BADGING_NONE) {
- // TODO(sghuman): Refactor this to reuse drawable to save memory and add to a
- // special subclass of AccessPointPreference
- LayerDrawable drawable = Utils.getBadgedWifiIcon(context, level, mWifiBadge);
- drawable.setLayerSize(0, mIconWidth, mIconHeight);
- drawable.setLayerSize(1, mIconWidth, mIconHeight);
- drawable.setTint(Utils.getColorAccent(getContext()));
- setIcon(drawable);
- return;
- }
- if (getIcon() == null) {
- // To avoid a drawing race condition, we first set the state (SECURE/NONE) and then
- // set the icon (drawable) to that state's drawable.
- // If sld is null then we are indexing and therefore do not have access to
- // (nor need to display) the drawable.
- if (mWifiSld != null) {
- mWifiSld.setState((mAccessPoint.getSecurity() != AccessPoint.SECURITY_NONE)
- ? STATE_SECURED
- : STATE_NONE);
- Drawable drawable = mWifiSld.getCurrent();
- if (!mForSavedNetworks && drawable != null) {
- setIcon(drawable);
- return;
- }
- }
- safeSetDefaultIcon();
- }
+ safeSetDefaultIcon();
}
}
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/drawer/TileUtilsTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/drawer/TileUtilsTest.java
index c95cac5c8c67..021a96cedd6d 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/drawer/TileUtilsTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/drawer/TileUtilsTest.java
@@ -34,9 +34,11 @@ import android.os.RemoteException;
import android.os.UserHandle;
import android.os.UserManager;
import android.provider.Settings.Global;
+import android.text.TextUtils;
import android.util.ArrayMap;
import android.util.Pair;
+import com.android.settingslib.SuggestionParser;
import com.android.settingslib.TestConfig;
import static org.mockito.Mockito.atLeastOnce;
@@ -156,6 +158,32 @@ public class TileUtilsTest {
}
@Test
+ public void getTilesForIntent_shouldSkipFilteredApps() {
+ final String testCategory = "category1";
+ Intent intent = new Intent();
+ Map<Pair<String, String>, Tile> addedCache = new ArrayMap<>();
+ List<Tile> outTiles = new ArrayList<>();
+ List<ResolveInfo> info = new ArrayList<>();
+ ResolveInfo resolveInfo = newInfo(true, null /* category */, null, URI_GET_ICON,
+ URI_GET_SUMMARY);
+ addMetadataToInfo(resolveInfo, "com.android.settings.require_account", "com.google");
+ addMetadataToInfo(resolveInfo, "com.android.settings.require_connection", "true");
+ info.add(resolveInfo);
+
+ when(mPackageManager.queryIntentActivitiesAsUser(eq(intent), anyInt(), anyInt()))
+ .thenReturn(info);
+
+ TileUtils.getTilesForIntent(mContext, UserHandle.CURRENT, intent, addedCache,
+ null /* defaultCategory */, outTiles, false /* usePriority */,
+ false /* checkCategory */);
+
+ assertThat(outTiles.size()).isEqualTo(1);
+ SuggestionParser parser = new SuggestionParser(mContext, null);
+ parser.filterSuggestions(outTiles, 0);
+ assertThat(outTiles.size()).isEqualTo(0);
+ }
+
+ @Test
public void getCategories_shouldHandleExtraIntentAction() {
final String testCategory = "category1";
final String testAction = "action1";
@@ -309,4 +337,16 @@ public class TileUtilsTest {
}
return info;
}
+
+ private void addMetadataToInfo(ResolveInfo info, String key, String value) {
+ if (!TextUtils.isEmpty(key)) {
+ if (info.activityInfo == null) {
+ info.activityInfo = new ActivityInfo();
+ }
+ if (info.activityInfo.metaData == null) {
+ info.activityInfo.metaData = new Bundle();
+ }
+ info.activityInfo.metaData.putString(key, value);
+ }
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/grid/TaskGridLayoutAlgorithm.java b/packages/SystemUI/src/com/android/systemui/recents/views/grid/TaskGridLayoutAlgorithm.java
index 02d1cc14fc41..4f175368aceb 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/grid/TaskGridLayoutAlgorithm.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/grid/TaskGridLayoutAlgorithm.java
@@ -74,11 +74,8 @@ public class TaskGridLayoutAlgorithm {
yOffsets = new int[taskCount];
int layoutTaskCount = Math.min(MAX_LAYOUT_TASK_COUNT, taskCount);
-
- tasksPerLine = layoutTaskCount < 2 ? 1 : (
- layoutTaskCount < 5 ? 2 : (
- layoutTaskCount < 7 ? 3 : 4));
- lines = layoutTaskCount < 3 ? 1 : 2;
+ tasksPerLine = getTasksPerLine(layoutTaskCount);
+ lines = layoutTaskCount < 4 ? 1 : 2;
// A couple of special cases.
boolean landscapeWindow = mWindowRect.width() > mWindowRect.height();
@@ -131,6 +128,27 @@ public class TaskGridLayoutAlgorithm {
emptySpaceY / 2 + mPaddingTopBottom + (taskHeight + mPaddingTaskView) * yIndex;
}
}
+
+ private int getTasksPerLine(int taskCount) {
+ switch(taskCount) {
+ case 0:
+ return 0;
+ case 1:
+ return 1;
+ case 2:
+ case 4:
+ return 2;
+ case 3:
+ case 5:
+ case 6:
+ return 3;
+ case 7:
+ case 8:
+ return 4;
+ default:
+ throw new IllegalArgumentException("Unsupported task count " + taskCount);
+ }
+ }
}
/**
diff --git a/services/autofill/java/com/android/server/autofill/AutoFillManagerService.java b/services/autofill/java/com/android/server/autofill/AutoFillManagerService.java
index 8ce52782d114..58edadcda257 100644
--- a/services/autofill/java/com/android/server/autofill/AutoFillManagerService.java
+++ b/services/autofill/java/com/android/server/autofill/AutoFillManagerService.java
@@ -36,6 +36,7 @@ import android.os.Handler;
import android.os.IBinder;
import android.os.Looper;
import android.os.Message;
+import android.os.Process;
import android.os.RemoteException;
import android.os.ResultReceiver;
import android.os.ShellCallback;
@@ -81,7 +82,6 @@ public final class AutoFillManagerService extends SystemService {
protected static final int MSG_REQUEST_AUTO_FILL = 3;
private final AutoFillManagerServiceStub mServiceStub;
- private final AutoFillUI mUi;
private final Context mContext;
private final ContentResolver mResolver;
@@ -146,7 +146,7 @@ public final class AutoFillManagerService extends SystemService {
mHandlerCaller = new HandlerCaller(null, Looper.getMainLooper(), mHandlerCallback, true);
mContext = context;
- mUi = new AutoFillUI(context, this, mLock);
+
mResolver = context.getContentResolver();
mServiceStub = new AutoFillManagerServiceStub();
}
@@ -186,7 +186,7 @@ public final class AutoFillManagerService extends SystemService {
if (DEBUG) Slog.d(TAG, "no service info for " + serviceComponent);
return null;
}
- return new AutoFillManagerServiceImpl(this, mUi, mContext, mLock, mRequestsHistory,
+ return new AutoFillManagerServiceImpl(this, mContext, mLock, mRequestsHistory,
FgThread.getHandler(), userId, serviceInfo.applicationInfo.uid, serviceComponent,
SERVICE_BINDING_LIFETIME_MS);
}
@@ -288,6 +288,11 @@ public final class AutoFillManagerService extends SystemService {
final IBinder activityToken = LocalServices.getService(ActivityManagerInternal.class)
.getTopVisibleActivity(uid);
if (activityToken == null) {
+ // TODO(b/33197203, b/34819567, b/34171325): figure out proper way to handle it
+ if (uid == Process.SYSTEM_UID) {
+ if (DEBUG) Log.w(TAG, "requestAutoFill(): ignoring call from system");
+ return;
+ }
throw new SecurityException("uid " + uid + " does not own the top activity");
}
@@ -326,7 +331,6 @@ public final class AutoFillManagerService extends SystemService {
}
}
}
- mUi.dump(pw);
pw.println("Requests history:");
mRequestsHistory.reverseDump(fd, pw, args);
}
diff --git a/services/autofill/java/com/android/server/autofill/AutoFillManagerServiceImpl.java b/services/autofill/java/com/android/server/autofill/AutoFillManagerServiceImpl.java
index 0dd891cd4235..2dcb31ccc229 100644
--- a/services/autofill/java/com/android/server/autofill/AutoFillManagerServiceImpl.java
+++ b/services/autofill/java/com/android/server/autofill/AutoFillManagerServiceImpl.java
@@ -19,7 +19,9 @@ package com.android.server.autofill;
import static android.service.autofill.AutoFillService.FLAG_AUTHENTICATION_ERROR;
import static android.service.autofill.AutoFillService.FLAG_AUTHENTICATION_REQUESTED;
import static android.service.autofill.AutoFillService.FLAG_AUTHENTICATION_SUCCESS;
+import static android.view.View.AUTO_FILL_FLAG_TYPE_FILL;
import static android.view.View.AUTO_FILL_FLAG_TYPE_SAVE;
+import static android.view.autofill.AutoFillManager.FLAG_UPDATE_UI_SHOW;
import static android.view.autofill.AutoFillManager.FLAG_UPDATE_UI_HIDE;
import static com.android.server.autofill.Helper.DEBUG;
@@ -56,6 +58,7 @@ import android.service.autofill.IAutoFillAppCallback;
import android.service.autofill.IAutoFillServerCallback;
import android.service.autofill.IAutoFillService;
import android.service.voice.VoiceInteractionSession;
+import android.util.ArrayMap;
import android.util.LocalLog;
import android.util.Log;
import android.util.PrintWriterPrinter;
@@ -63,6 +66,7 @@ import android.util.Slog;
import android.util.SparseArray;
import android.util.TimeUtils;
import android.view.autofill.AutoFillId;
+import android.view.autofill.AutoFillValue;
import android.view.autofill.Dataset;
import android.view.autofill.FillResponse;
@@ -74,6 +78,7 @@ import java.lang.ref.WeakReference;
import java.util.Arrays;
import java.util.LinkedList;
import java.util.List;
+import java.util.Map;
/**
* Bridge between the {@code system_server}'s {@link AutoFillManagerService} and the
@@ -96,12 +101,14 @@ final class AutoFillManagerServiceImpl {
private final Object mLock;
private final AutoFillServiceInfo mInfo;
private final AutoFillManagerService mManagerService;
- private final AutoFillUI mUi;
// Token used for fingerprint authentication
// TODO(b/33197203): create on demand?
private final IBinder mAuthToken = new Binder();
+ private final IFingerprintService mFingerprintService =
+ IFingerprintService.Stub.asInterface(ServiceManager.getService("fingerprint"));
+
@GuardedBy("mLock")
private final List<QueuedRequest> mQueuedRequests = new LinkedList<>();
@@ -204,11 +211,10 @@ final class AutoFillManagerServiceImpl {
// Estimated time when the service will be evicted from the cache.
long mEstimateTimeOfDeath;
- AutoFillManagerServiceImpl(AutoFillManagerService managerService, AutoFillUI ui,
- Context context, Object lock, LocalLog requestsHistory, Handler handler, int userId,
- int uid, ComponentName component, long ttl) {
+ AutoFillManagerServiceImpl(AutoFillManagerService managerService, Context context, Object lock,
+ LocalLog requestsHistory, Handler handler, int userId, int uid, ComponentName component,
+ long ttl) {
mManagerService = managerService;
- mUi = ui;
mContext = context;
mLock = lock;
mRequestsHistory = requestsHistory;
@@ -277,8 +283,8 @@ final class AutoFillManagerServiceImpl {
requestAutoFillLocked(activityToken, autoFillId, bounds, flags, true);
}
- private void requestAutoFillLocked(IBinder activityToken, AutoFillId autoFillId, Rect bounds,
- int flags, boolean queueIfNecessary) {
+ private void requestAutoFillLocked(IBinder activityToken, @Nullable AutoFillId autoFillId,
+ @Nullable Rect bounds, int flags, boolean queueIfNecessary) {
if (mService == null) {
if (!queueIfNecessary) {
Slog.w(TAG, "requestAutoFillLocked(): service is null");
@@ -294,56 +300,35 @@ final class AutoFillManagerServiceImpl {
return;
}
- final Session session = getSessionByTokenLocked(activityToken);
+ final String historyItem = "s=" + mComponentName + " u=" + mUserId + " f=" + flags
+ + " a=" + activityToken + " i=" + autoFillId + " b=" + bounds;
+ mRequestsHistory.log(historyItem);
- if (session != null) {
- // Session already exist, update UI instead...
- /*
- * TODO(b/33197203): currently, it's always reusing the session, regardless of the
- * requested autoFillId, but it should start a new session for views that
- * were not part of the initial auto-fill dataset returned by the service. For example:
- *
- * 1.Activity has 4 fields, `first_name`, `last_name`, and `address`.
- * 2.User taps `first_name`.
- * 3.Service returns a dataset with ids for `first_name` and `last_name`.
- * 4.When user taps `first_name` (again) or `last_name`, session should be reused, but
- * when user taps `address`, it should start a new session (since that field was
- * not part of the initial dataset).
- *
- * Similarly, once the activity is auto-filled, the flag logic should be reset (so if
- * the user taps the view again, a new auto-fill request is made)
- */
- if (DEBUG) {
- Slog.d(TAG, "requestAutoFillLocked(): reusing session for token "
- + activityToken + ", id " + autoFillId + " and flags " + flags);
- }
+ // TODO(b/33197203): Handle partitioning
+ Session session = getOrCreateSessionByTokenLocked(activityToken);
+ if (DEBUG) Slog.d(TAG, "using Session: " + session.mId);
- if ((flags & FLAG_UPDATE_UI_HIDE) != 0) {
- // TODO(b/33197203): handle it?
- if (DEBUG) Slog.d(TAG, "ignoring FLAG_UPDATE_UI_HIDE request for " + autoFillId);
+ session.updateAutoFillInput(flags, autoFillId, null, bounds);
+ }
- return;
+ private Session getOrCreateSessionByTokenLocked(IBinder activityToken) {
+ final int size = mSessions.size();
+ for (int i = 0; i < size; i++) {
+ final Session session = mSessions.valueAt(i);
+ if (activityToken.equals(session.mActivityToken.get())) {
+ return session;
}
-
- session.mCurrentAutoFillId = autoFillId;
- session.mCurrentBounds = bounds;
- mUi.showResponse(mUserId, session.mId, autoFillId, bounds, session.mCurrentResponse);
- return;
}
+ return createSessionByTokenLocked(activityToken);
+ }
+ private Session createSessionByTokenLocked(IBinder activityToken) {
final int sessionId = ++sSessionIdCounter;
- if (DEBUG) {
- Slog.d(TAG, "requestAutoFillLocked(): new session (id=" + sessionId + " for token "
- + activityToken + " and autoFillId " + autoFillId);
- }
+ if (DEBUG) Slog.d(TAG, "creating Session: " + sessionId);
- final Session newSession = new Session(sessionId, activityToken, autoFillId, bounds);
+ final Session newSession = new Session(sessionId, activityToken);
mSessions.put(sessionId, newSession);
- final String historyItem = "s=" + mComponentName + " u=" + mUserId + " f=" + flags
- + " a=" + activityToken + " i=" + autoFillId + " b=" + bounds;
- mRequestsHistory.log(historyItem);
-
/*
* TODO(b/33197203): apply security checks below:
* - checks if disabled by secure settings / device policy
@@ -353,7 +338,8 @@ final class AutoFillManagerServiceImpl {
*/
try {
// TODO(b/33197203): add MetricsLogger call
- if (!mAm.requestAutoFillData(mAssistReceiver, null, sessionId, activityToken, flags)) {
+ if (!mAm.requestAutoFillData(
+ mAssistReceiver, null, sessionId, activityToken, AUTO_FILL_FLAG_TYPE_FILL)) {
// TODO(b/33197203): might need a way to warn user (perhaps a new method on
// AutoFillService).
Slog.w(TAG, "failed to request auto-fill data for " + activityToken);
@@ -361,75 +347,7 @@ final class AutoFillManagerServiceImpl {
} catch (RemoteException e) {
// Should not happen, it's a local call.
}
- }
-
- /**
- * Called by UI to trigger a save request to the service.
- */
- void requestSaveLocked(int sessionId) {
- // TODO(b/33197203): add MetricsLogger call
- // TODO(b/33197203): use handler?
- // TODO(b/33197203): show error on UI on Slog.w situations below???
-
- if (mService == null) {
- Slog.w(TAG, "requestSave(): service is null");
- return;
- }
- final Session session = mSessions.get(sessionId);
- if (session == null) {
- Slog.w(TAG, "requestSave(): no session with id " + sessionId);
- return;
- }
- final IBinder activityToken = session.mActivityToken.get();
- if (activityToken == null) {
- Slog.w(TAG, "activity token for session " + sessionId + " already GCed");
- return;
- }
-
- /*
- * TODO(b/33197203): apply security checks below:
- * - checks if disabled by secure settings / device policy
- * - log operation using noteOp()
- * - check flags
- * - display disclosure if needed
- */
- try {
- /* TODO(b/33197203): refactor save logic so it uses a cached AssistStructure, and get
- the extras to be sent to the service based on the response / dataset in the session.
- Something like:
- final Bundle extras = (responseExtras == null && datasetExtras == null)
- ? null : new Bundle();
- if (responseExtras != null) {
- if (DEBUG) Slog.d(TAG, "response extras on save notification: " +
- bundleToString(responseExtras));
- extras.putBundle(AutoFillService.EXTRA_RESPONSE_EXTRAS, responseExtras);
- }
- if (datasetExtras != null) {
- if (DEBUG) Slog.d(TAG, "dataset extras on save notification: " +
- bundleToString(datasetExtras));
- extras.putBundle(AutoFillService.EXTRA_DATASET_EXTRAS, datasetExtras);
- }
-
- */
-
- if (!mAm.requestAutoFillData(mAssistReceiver, null, sessionId, activityToken,
- AUTO_FILL_FLAG_TYPE_SAVE)) {
- Slog.w(TAG, "failed to save for " + activityToken);
- }
- } catch (RemoteException e) {
- // Should not happen, it's a local call.
- }
- }
-
- private Session getSessionByTokenLocked(IBinder activityToken) {
- final int size = mSessions.size();
- for (int i = 0; i < size; i++) {
- final Session session = mSessions.valueAt(i);
- if (activityToken.equals(session.mActivityToken.get())) {
- return session;
- }
- }
- return null;
+ return newSession;
}
void stopLocked() {
@@ -459,35 +377,6 @@ final class AutoFillManagerServiceImpl {
}
}
- /**
- * Called by {@link AutoFillUI} to fill an activity after the user selected a dataset.
- */
- void autoFillApp(int sessionId, Dataset dataset) {
- // TODO(b/33197203): add MetricsLogger call
-
- if (dataset == null) {
- Slog.w(TAG, "autoFillApp(): no dataset for callback id " + sessionId);
- return;
- }
-
-
- final Session session;
- synchronized (mLock) {
- session = mSessions.get(sessionId);
- if (session == null) {
- Slog.w(TAG, "autoFillApp(): no session with id " + sessionId);
- return;
- }
- if (session.mAppCallback == null) {
- Slog.w(TAG, "autoFillApp(): no app callback for session " + sessionId);
- return;
- }
-
- // TODO(b/33197203): use a handler?
- session.autoFill(dataset);
- }
- }
-
void removeSessionLocked(int id) {
if (DEBUG) Slog.d(TAG, "Removing session " + id);
mSessions.remove(id);
@@ -495,43 +384,6 @@ final class AutoFillManagerServiceImpl {
// TODO(b/33197203): notify mService so it can invalidate the FillCallback / SaveCallback?
}
- /**
- * Notifies the result of a {@link FillResponse} authentication request to the service.
- *
- * <p>Typically called by the UI after user taps the "Tap to autofill" affordance, or after user
- * used the fingerprint sensors to authenticate.
- */
- void notifyResponseAuthenticationResult(Bundle extras, int flags) {
- if (DEBUG) Slog.d(TAG, "notifyResponseAuthenticationResult(): flags=" + flags
- + ", extras=" + bundleToString(extras));
-
- synchronized (mLock) {
- try {
- mService.authenticateFillResponse(extras, flags);
- } catch (RemoteException e) {
- Slog.w(TAG, "Error sending authentication result back to service: " + e);
- }
- }
- }
-
- /**
- * Notifies the result of a {@link Dataset} authentication request to the service.
- *
- * <p>Typically called by the UI after user taps the "Tap to autofill" affordance, or after
- * it gets the results from a fingerprint authentication.
- */
- void notifyDatasetAuthenticationResult(Bundle extras, int flags) {
- if (DEBUG) Slog.d(TAG, "notifyDatasetAuthenticationResult(): flags=" + flags
- + ", extras=" + bundleToString(extras));
- synchronized (mLock) {
- try {
- mService.authenticateDataset(extras, flags);
- } catch (RemoteException e) {
- Slog.w(TAG, "Error sending authentication result back to service: " + e);
- }
- }
- }
-
void dumpLocked(String prefix, PrintWriter pw) {
if (!mValid) {
pw.print(" NOT VALID: ");
@@ -612,8 +464,78 @@ final class AutoFillManagerServiceImpl {
}
/**
- * A bridge between the {@link AutoFillService} implementation and the activity being
- * auto-filled (represented through the {@link IAutoFillAppCallback}).
+ * State for a given view with a AutoFillId.
+ *
+ * <p>This class holds state about a view and calls its listener when the fill UI is ready to
+ * be displayed for the view.
+ */
+ static final class ViewState {
+ interface Listener {
+ /**
+ * Called when the fill UI is ready to be shown for this view.
+ */
+ void onFillReady(ViewState viewState, FillResponse fillResponse, Rect bounds,
+ @Nullable AutoFillValue value);
+ }
+
+ private final Listener mListener;
+ @Nullable
+ private FillResponse mResponse;
+ private AutoFillValue mAutoFillValue;
+ private Rect mBounds;
+
+ ViewState(Listener listener) {
+ mListener = listener;
+ }
+
+ /**
+ * Response should only be set once.
+ */
+ void setResponse(FillResponse response) {
+ if (mResponse != null) {
+ Slog.e(TAG, "ViewState response set more than once");
+ return;
+ }
+ mResponse = response;
+
+ maybeCallOnFillReady();
+ }
+
+ void update(@Nullable AutoFillValue autoFillValue, @Nullable Rect bounds) {
+ if (autoFillValue != null) {
+ mAutoFillValue = autoFillValue;
+ }
+ if (bounds != null) {
+ mBounds = bounds;
+ }
+
+ maybeCallOnFillReady();
+ }
+
+ /**
+ * Calls {@link Listener#onFillReady(ViewState, FillResponse, Rect, AutoFillValue)} if the
+ * fill UI is ready to be displayed (i.e. when response and bounds are set).
+ */
+ void maybeCallOnFillReady() {
+ if (mResponse != null && mBounds != null) {
+ mListener.onFillReady(this, mResponse, mBounds, mAutoFillValue);
+ }
+ }
+
+ @Override
+ public String toString() {
+ if (!DEBUG) return super.toString();
+
+ return "ViewState: [response=" + mResponse + ", value=" + mAutoFillValue
+ + ", bounds=" + mBounds + "]";
+ }
+ }
+
+ /**
+ * A session for a given activity.
+ *
+ * <p>This class manages the multiple {@link ViewState}s for each view it has, and keeps track
+ * of the current view session to display the appropriate UI.
*
* <p>Although the auto-fill requests and callbacks are stateless from the service's point of
* view, we need to keep state in the framework side for cases such as authentication. For
@@ -625,23 +547,23 @@ final class AutoFillManagerServiceImpl {
// - On all authentication scenarios.
// - When user does not interact back after a while.
// - When service is unbound.
- private final class Session {
+ final class Session implements ViewState.Listener {
- private final int mId;
+ private final AutoFillUI mUi;
+ final int mId;
private final WeakReference<IBinder> mActivityToken;
- private IAutoFillAppCallback mAppCallback;
-
- // Current view where the auto-fill bar is displayed
@GuardedBy("mLock")
- private AutoFillId mCurrentAutoFillId;
+ private final Map<AutoFillId, ViewState> mViewStates = new ArrayMap<>();
@GuardedBy("mLock")
- private Rect mCurrentBounds;
- @GuardedBy("mLock")
- private FillResponse mCurrentResponse;
+ @Nullable
+ private ViewState mCurrentViewState;
- private final IFingerprintService mFingerprintService;
+ private IAutoFillAppCallback mAppCallback;
+ // TODO(b/33197203): Get a response per view instead of per activity.
+ @GuardedBy("mLock")
+ private FillResponse mCurrentResponse;
@GuardedBy("mLock")
private FillResponse mResponseRequiringAuth;
@GuardedBy("mLock")
@@ -683,7 +605,7 @@ final class AutoFillManagerServiceImpl {
notifyDatasetAuthenticationResult(mDatasetRequiringAuth.getExtras(),
FLAG_AUTHENTICATION_SUCCESS);
} else {
- autoFillAppLocked(mDatasetRequiringAuth, true);
+ autoFillApp(mDatasetRequiringAuth);
}
} else if (mResponseRequiringAuth != null) {
final List<Dataset> datasets = mResponseRequiringAuth.getDatasets();
@@ -697,7 +619,7 @@ final class AutoFillManagerServiceImpl {
Slog.w(TAG, "onAuthenticationSucceeded(): no response or dataset");
}
- mUi.dismissFingerprintRequest(mUserId, true);
+ mUi.dismissFingerprintRequest(true);
}
@Override
@@ -721,7 +643,7 @@ final class AutoFillManagerServiceImpl {
Slog.w(TAG, "onError(): no response or dataset");
}
- mUi.dismissFingerprintRequest(mUserId, false);
+ mUi.dismissFingerprintRequest(false);
}
@Override
@@ -741,7 +663,6 @@ final class AutoFillManagerServiceImpl {
// TODO(b/33197203): add MetricsLogger call
if (response == null) {
if (DEBUG) Slog.d(TAG, "showResponse(): null response");
-
removeSelf();
return;
}
@@ -805,20 +726,16 @@ final class AutoFillManagerServiceImpl {
if (DEBUG) Log.d(TAG, "unlockDataset(): dataset=" + dataset + ", flags=" + flags);
if ((flags & FLAG_AUTHENTICATION_SUCCESS) != 0) {
- autoFillAppLocked(dataset != null ? dataset : mDatasetRequiringAuth, true);
+ autoFillApp(dataset != null ? dataset : mDatasetRequiringAuth);
return;
}
- removeSelf();
}
};
- private Session(int id, IBinder activityToken, AutoFillId autoFillId, Rect bounds) {
- this.mId = id;
- this.mActivityToken = new WeakReference<>(activityToken);
- this.mCurrentAutoFillId = autoFillId;
- this.mCurrentBounds = bounds;
- this.mFingerprintService = IFingerprintService.Stub
- .asInterface(ServiceManager.getService("fingerprint"));
+ private Session(int id, IBinder activityToken) {
+ mUi = new AutoFillUI(mContext, this);
+ mId = id;
+ mActivityToken = new WeakReference<>(activityToken);
}
void setAppCallback(IBinder appBinder) {
@@ -834,6 +751,54 @@ final class AutoFillManagerServiceImpl {
mAppCallback = IAutoFillAppCallback.Stub.asInterface(appBinder);
}
+ void updateAutoFillInput(int flags, AutoFillId autoFillId,
+ @Nullable AutoFillValue autoFillValue, @Nullable Rect bounds) {
+ synchronized (mLock) {
+ ViewState viewState = mViewStates.get(autoFillId);
+ if (viewState == null) {
+ viewState = new ViewState(this);
+ mViewStates.put(autoFillId, viewState);
+ }
+
+ if ((flags & FLAG_UPDATE_UI_SHOW) != 0) {
+ // Remove the UI if the ViewState has changed.
+ if (mCurrentViewState != viewState) {
+ mUi.hideFillUi();
+ mCurrentViewState = viewState;
+ }
+
+ // If the ViewState is ready to be displayed, onReady() will be called.
+ viewState.update(autoFillValue, bounds);
+
+ // TODO(b/33197203): Remove when there is a response per activity.
+ if (mCurrentResponse != null) {
+ viewState.setResponse(mCurrentResponse);
+ }
+ } else if ((flags & FLAG_UPDATE_UI_HIDE) != 0) {
+ if (mCurrentViewState == viewState) {
+ mUi.hideFillUi();
+ mCurrentViewState = null;
+ }
+ } else {
+ Slog.w(TAG, "unknown flags " + flags);
+ }
+ }
+ }
+
+ @Override
+ public void onFillReady(ViewState viewState, FillResponse response, Rect bounds,
+ @Nullable AutoFillValue value) {
+ String filterText = "";
+ if (value != null) {
+ // TODO(b/33197203): Handle other AutoFillValue types
+ final CharSequence text = value.getTextValue();
+ if (text != null) {
+ filterText = text.toString();
+ }
+ }
+ mUi.showFillUi(viewState, response.getDatasets(), bounds, filterText);
+ }
+
private void showResponseLocked(FillResponse response, boolean authRequired) {
if (DEBUG) Slog.d(TAG, "showResponse(directly=" + mAutoFillDirectly
+ ", authRequired=" + authRequired +"):" + response);
@@ -845,7 +810,7 @@ final class AutoFillManagerServiceImpl {
final Dataset dataset = datasets.get(0);
if (DEBUG) Slog.d(TAG, "auto-filling directly from auth: " + dataset);
- autoFillAppLocked(dataset, true);
+ autoFillApp(dataset);
return;
}
}
@@ -853,7 +818,10 @@ final class AutoFillManagerServiceImpl {
if (!authRequired) {
// TODO(b/33197203): add MetricsLogger call
mCurrentResponse = response;
- mUi.showResponse(mUserId, mId, mCurrentAutoFillId, mCurrentBounds, mCurrentResponse);
+ // TODO(b/33197203): Consider using mCurrentResponse, depends on partitioning design
+ if (mCurrentViewState != null) {
+ mCurrentViewState.setResponse(mCurrentResponse);
+ }
return;
}
@@ -869,7 +837,7 @@ final class AutoFillManagerServiceImpl {
scanFingerprint(response.getCryptoObjectOpId());
}
// Displays the message asking the user to tap (or fingerprint) for AutoFill.
- mUi.showFillResponseAuthenticationRequest(mUserId, mId, requiresFingerprint,
+ mUi.showFillResponseAuthenticationRequest(requiresFingerprint,
response.getExtras(), response.getFlags());
}
@@ -877,7 +845,7 @@ final class AutoFillManagerServiceImpl {
synchronized (mLock) {
// Autofill it directly...
if (!dataset.isAuthRequired()) {
- autoFillAppLocked(dataset, true);
+ autoFillApp(dataset);
return;
}
@@ -906,30 +874,114 @@ final class AutoFillManagerServiceImpl {
void dumpLocked(String prefix, PrintWriter pw) {
pw.print(prefix); pw.print("mId: "); pw.println(mId);
pw.print(prefix); pw.print("mActivityToken: "); pw.println(mActivityToken.get());
- pw.print(prefix); pw.print("mCurrentAutoFillId: "); pw.println(mCurrentAutoFillId);
- pw.print(prefix); pw.print("mCurrentBounds: "); pw.println(mCurrentBounds);
pw.print(prefix); pw.print("mCurrentResponse: "); pw.println(mCurrentResponse);
pw.print(prefix);
pw.print("mResponseRequiringAuth: "); pw.println(mResponseRequiringAuth);
pw.print(prefix);
pw.print("mDatasetRequiringAuth: "); pw.println(mDatasetRequiringAuth);
pw.print(prefix); pw.print("mAutoFillDirectly: "); pw.println(mAutoFillDirectly);
+ pw.print(prefix); pw.print("mCurrentViewStates: "); pw.println(mCurrentViewState);
+ pw.print(prefix); pw.print("mViewStates: "); pw.println(mViewStates.size());
+ final String prefix2 = prefix + " ";
+ for (Map.Entry<AutoFillId, ViewState> entry : mViewStates.entrySet()) {
+ pw.print(prefix2);
+ pw.print(entry.getKey()); pw.print(": " ); pw.println(entry.getValue());
+ }
}
- private void autoFillAppLocked(Dataset dataset, boolean removeSelf) {
- try {
- if (DEBUG) Slog.d(TAG, "autoFillApp(): the buck is on the app: " + dataset);
- mAppCallback.autoFill(dataset);
+ /**
+ * Notifies the result of a {@link FillResponse} authentication request to the service.
+ *
+ * <p>Typically called by the UI after user taps the "Tap to autofill" affordance, or after user
+ * used the fingerprint sensors to authenticate.
+ */
+ void notifyResponseAuthenticationResult(Bundle extras, int flags) {
+ if (DEBUG) Slog.d(TAG, "notifyResponseAuthenticationResult(): flags=" + flags
+ + ", extras=" + bundleToString(extras));
+ synchronized (mLock) {
+ try {
+ mService.authenticateFillResponse(extras, flags);
+ } catch (RemoteException e) {
+ Slog.w(TAG, "Error sending authentication result back to service: " + e);
+ }
+ }
+ }
- // TODO(b/33197203): temporarily hack: show the save notification after autofilled,
- // since save is not automatically detected yet.
- mUi.showSaveNotification(mUserId, mId); removeSelf = false;
+ /**
+ * Notifies the result of a {@link Dataset} authentication request to the service.
+ *
+ * <p>Typically called by the UI after user taps the "Tap to autofill" affordance, or after
+ * it gets the results from a fingerprint authentication.
+ */
+ void notifyDatasetAuthenticationResult(Bundle extras, int flags) {
+ if (DEBUG) Slog.d(TAG, "notifyDatasetAuthenticationResult(): flags=" + flags
+ + ", extras=" + bundleToString(extras));
+ synchronized (mLock) {
+ try {
+ mService.authenticateDataset(extras, flags);
+ } catch (RemoteException e) {
+ Slog.w(TAG, "Error sending authentication result back to service: " + e);
+ }
+ }
+ }
- } catch (RemoteException e) {
- Slog.w(TAG, "Error auto-filling activity: " + e);
+ void autoFillApp(Dataset dataset) {
+ synchronized (mLock) {
+ try {
+ if (DEBUG) Slog.d(TAG, "autoFillApp(): the buck is on the app: " + dataset);
+ mAppCallback.autoFill(dataset);
+ } catch (RemoteException e) {
+ Slog.w(TAG, "Error auto-filling activity: " + e);
+ }
}
- if (removeSelf) {
- removeSelf();
+ }
+
+ void requestSave() {
+ synchronized (mLock) {
+ requestSaveLocked(mId);
+ }
+ }
+
+ /**
+ * Called by UI to trigger a save request to the service.
+ */
+ void requestSaveLocked(int sessionId) {
+ // TODO(b/33197203): add MetricsLogger call
+ // TODO(b/33197203): use handler?
+ // TODO(b/33197203): show error on UI on Slog.w situations below???
+
+ if (mService == null) {
+ Slog.w(TAG, "requestSave(): service is null");
+ return;
+ }
+ final Session session = mSessions.get(sessionId);
+ if (session == null) {
+ Slog.w(TAG, "requestSave(): no session with id " + sessionId);
+ return;
+ }
+ final IBinder activityToken = session.mActivityToken.get();
+ if (activityToken == null) {
+ Slog.w(TAG, "activity token for session " + sessionId + " already GCed");
+ return;
+ }
+
+ /*
+ * TODO(b/33197203): apply security checks below:
+ * - checks if disabled by secure settings / device policy
+ * - log operation using noteOp()
+ * - check flags
+ * - display disclosure if needed
+ */
+ try {
+ /* TODO(b/33197203): refactor save logic so it uses a cached AssistStructure, and
+ get the extras to be sent to the service based on the response / dataset in the
+ session. */
+ if (!mAm.requestAutoFillData(mAssistReceiver, null, sessionId, activityToken,
+ AUTO_FILL_FLAG_TYPE_SAVE)) {
+ Slog.w(TAG, "failed to save for " + activityToken);
+ }
+ } catch (RemoteException e) {
+ // Should not happen, it's a local call.
}
}
diff --git a/services/autofill/java/com/android/server/autofill/AutoFillUI.java b/services/autofill/java/com/android/server/autofill/AutoFillUI.java
index 96f340840542..86e04ccd6a83 100644
--- a/services/autofill/java/com/android/server/autofill/AutoFillUI.java
+++ b/services/autofill/java/com/android/server/autofill/AutoFillUI.java
@@ -45,6 +45,8 @@ import android.widget.Toast;
import com.android.internal.annotations.GuardedBy;
import com.android.server.UiThread;
+import com.android.server.autofill.AutoFillManagerServiceImpl.Session;
+import com.android.server.autofill.AutoFillManagerServiceImpl.ViewState;
import java.io.PrintWriter;
import java.util.Arrays;
@@ -59,24 +61,25 @@ final class AutoFillUI {
private static final String TAG = "AutoFillUI";
private final Context mContext;
-
+ private final Session mSession;
private final WindowManager mWm;
- @Nullable
+ // Fill UI variables
private AnchoredWindow mFillWindow;
+ private DatasetPicker mFillView;
+ private ViewState mViewState;
+ private Rect mBounds;
+ private String mFilterText;
/**
* Custom snackbar UI used for saving autofill or other informational messages.
*/
private View mSnackbar;
- AutoFillUI(Context context, AutoFillManagerService service, Object lock) {
+ AutoFillUI(Context context, Session session) {
mContext = context;
+ mSession = session;
mWm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
- mService = service;
- mLock = lock;
-
- setNotificationListener();
}
/**
@@ -101,29 +104,63 @@ final class AutoFillUI {
}
/**
- * Shows the options from a {@link FillResponse} so the user can pick up the proper
- * {@link Dataset} (when the response has one) for a given view (identified by
- * {@code autoFillId}).
+ * Hides the fill UI.
*/
- void showResponse(int userId, int sessionId, AutoFillId autoFillId, Rect bounds,
- FillResponse response) {
- if (DEBUG) Slog.d(TAG, "showResponse: id=" + autoFillId + ", bounds=" + bounds);
-
+ void hideFillUi() {
UiThread.getHandler().runWithScissors(() -> {
if (mFillWindow != null) {
+ if (DEBUG) Slog.d(TAG, "remove FillUi remove " + mFillWindow);
mFillWindow.hide();
}
- final DatasetPicker fillView = new DatasetPicker(mContext, response.getDatasets(),
- (dataset) -> {
- mFillWindow.hide();
- onDatasetPicked(userId, dataset, sessionId);
- });
+ mViewState = null;
+ mBounds = null;
+ mFilterText = null;
+ mFillView = null;
+ mFillWindow = null;
+ }, 0);
+ }
+
+ /**
+ * Shows the fill UI, removing the previous fill UI if the has changed.
+ *
+ * @param viewState the view state, compared by reference to know if new UI should be shown
+ * @param response the response to show, not used if viewState is the same
+ * @param bounds bounds of the view to be filled, used if changed
+ * @param filterText text of the view to be filled, used if changed
+ */
+ void showFillUi(ViewState viewState, List<Dataset> datasets, Rect bounds,
+ String filterText) {
+ UiThread.getHandler().runWithScissors(() -> {
+ if (mViewState != viewState) {
+ // new
+ hideFillUi();
+
+ mViewState = viewState;
+
+ mFillView = new DatasetPicker(mContext, datasets,
+ (dataset) -> {
+ mSession.autoFillApp(dataset);
+ hideFillUi();
+ showSaveUi();
+ });
+ mFillWindow = new AnchoredWindow(
+ mWm, mFillView, 800, ViewGroup.LayoutParams.WRAP_CONTENT);
+
+ if (DEBUG) Slog.d(TAG, "show FillUi");
+ }
+
+ if (!bounds.equals(mBounds)) {
+ if (DEBUG) Slog.d(TAG, "update FillUi bounds: " + mBounds);
+ mBounds = bounds;
+ mFillWindow.show(mBounds);
+ }
- // TODO(b/33197203): request width/height properly.
- mFillWindow = new AnchoredWindow(mWm, fillView, 800,
- ViewGroup.LayoutParams.WRAP_CONTENT);
- mFillWindow.show(bounds != null ? bounds : new Rect());
+ if (!filterText.equals(mFilterText)) {
+ if (DEBUG) Slog.d(TAG, "update FillUi filter text: " + mFilterText);
+ mFilterText = filterText;
+ mFillView.update(mFilterText);
+ }
}, 0);
}
@@ -134,10 +171,10 @@ final class AutoFillUI {
* <p>It typically replaces the auto-fill bar with a message saying "Press fingerprint or tap to
* autofill" or "Tap to autofill", depending on the value of {@code usesFingerprint}.
*/
- void showFillResponseAuthenticationRequest(int userId, int sessionId, boolean usesFingerprint,
+ void showFillResponseAuthenticationRequest(boolean usesFingerprint,
Bundle extras, int flags) {
// TODO(b/33197203): proper implementation
- showAuthNotification(userId, sessionId, usesFingerprint, extras, flags);
+ showAuthNotification(usesFingerprint, extras, flags);
}
/**
@@ -161,15 +198,13 @@ final class AutoFillUI {
/**
* Shows the UI asking the user to save for auto-fill.
*/
- void showSaveUI(int userId, int sessionId) {
+ void showSaveUi() {
showSnackbar(new SavePrompt(mContext, new SavePrompt.OnSaveListener() {
@Override
public void onSaveClick() {
hideSnackbar();
- synchronized (mLock) {
- final AutoFillManagerServiceImpl service = getServiceLocked(userId);
- service.requestSaveLocked(sessionId);
- }
+
+ mSession.requestSave();
}
@Override
public void onCancelClick() {
@@ -181,10 +216,10 @@ final class AutoFillUI {
/**
* Called by service after the user user the fingerprint sensors to authenticate.
*/
- void dismissFingerprintRequest(int userId, boolean success) {
+ void dismissFingerprintRequest(boolean success) {
if (DEBUG) Slog.d(TAG, "dismissFingerprintRequest(): ok=" + success);
- dismissAuthNotification(userId);
+ dismissAuthNotification();
if (!success) {
// TODO(b/33197203): proper implementation (snack bar / i18n string)
@@ -198,48 +233,11 @@ final class AutoFillUI {
pw.println("AufoFill UI");
final String prefix = " ";
pw.print(prefix); pw.print("sResultCode: "); pw.println(sResultCode);
+ pw.print(prefix); pw.print("mSessionId: "); pw.println(mSession.mId);
pw.print(prefix); pw.print("mSnackBar: "); pw.println(mSnackbar);
- mFillWindow.dump(pw);
- }
-
- private AutoFillManagerServiceImpl getServiceLocked(int userId) {
- final AutoFillManagerServiceImpl service = mService.getServiceForUserLocked(userId);
- if (service == null) {
- Slog.w(TAG, "no auto-fill service for user " + userId);
- }
- return service;
- }
-
- private void onSaveRequested(int userId, int sessionId) {
- // TODO(b/33197203): displays the snack bar, until save notification is refactored
- showSaveUI(userId, sessionId);
- }
-
- private void onDatasetPicked(int userId, Dataset dataset, int sessionId) {
- synchronized (mLock) {
- final AutoFillManagerServiceImpl service = getServiceLocked(userId);
- if (service == null) return;
-
- service.autoFillApp(sessionId, dataset);
- }
- }
-
- private void onSessionDone(int userId, int sessionId) {
- synchronized (mLock) {
- final AutoFillManagerServiceImpl service = getServiceLocked(userId);
- if (service == null) return;
-
- service.removeSessionLocked(sessionId);
- }
- }
-
- private void onResponseAuthenticationRequested(int userId, Bundle extras, int flags) {
- synchronized (mLock) {
- final AutoFillManagerServiceImpl service = getServiceLocked(userId);
- if (service == null) return;
-
- service.notifyResponseAuthenticationResult(extras, flags);
- }
+ pw.print(prefix); pw.print("mViewState: "); pw.println(mViewState);
+ pw.print(prefix); pw.print("mBounds: "); pw.println(mBounds);
+ pw.print(prefix); pw.print("mFilterText: "); pw.println(mFilterText);
}
//similar to a snackbar, but can be a bit custom since it is more than just text. This will
@@ -289,16 +287,10 @@ final class AutoFillUI {
private static final String EXTRA_FLAGS = "flags";
private static final String TYPE_OPTIONS = "options";
- private static final String TYPE_FINISH_SESSION = "finish_session";
- private static final String TYPE_PICK_DATASET = "pick_dataset";
- private static final String TYPE_SAVE = "save";
private static final String TYPE_AUTH_RESPONSE = "auth_response";
- @GuardedBy("mServiceLock")
private BroadcastReceiver mNotificationReceiver;
- @GuardedBy("mServiceLock")
- private final AutoFillManagerService mService;
- private final Object mLock;
+ private final Object mLock = new Object();
// Hack used to generate unique pending intents
static int sResultCode = 0;
@@ -316,8 +308,6 @@ final class AutoFillUI {
final class NotificationReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
- final int userId = intent.getIntExtra(EXTRA_USER_ID, -1);
- final int sessionId = intent.getIntExtra(EXTRA_SESSION_ID, -1);
final String type = intent.getStringExtra(EXTRA_NOTIFICATION_TYPE);
if (type == null) {
Slog.wtf(TAG, "No extra " + EXTRA_NOTIFICATION_TYPE + " on intent " + intent);
@@ -326,26 +316,12 @@ final class AutoFillUI {
final Dataset dataset = intent.getParcelableExtra(EXTRA_DATASET);
final int flags = intent.getIntExtra(EXTRA_FLAGS, 0);
- if (DEBUG) Slog.d(TAG, "Notification received: type=" + type + ", userId=" + userId
- + ", sessionId=" + sessionId);
+ if (DEBUG) Slog.d(TAG, "Notification received: type=" + type
+ + ", sessionId=" + mSession.mId);
synchronized (mLock) {
switch (type) {
- case TYPE_SAVE:
- onSaveRequested(userId, sessionId);
- break;
- case TYPE_FINISH_SESSION:
- onSessionDone(userId, sessionId);
- break;
- case TYPE_PICK_DATASET:
- onDatasetPicked(userId, dataset, sessionId);
-
- // Must cancel notification because it might be comming from action
- if (DEBUG) Slog.d(TAG, "Cancelling notification");
- NotificationManager.from(mContext).cancel(TYPE_OPTIONS, userId);
-
- break;
case TYPE_AUTH_RESPONSE:
- onResponseAuthenticationRequested(userId,
+ mSession.notifyResponseAuthenticationResult(
intent.getBundleExtra(EXTRA_AUTH_REQUIRED_EXTRAS), flags);
break;
default: {
@@ -357,161 +333,28 @@ final class AutoFillUI {
}
}
- private static Intent newNotificationIntent(int userId, String type) {
+ private static Intent newNotificationIntent(String type) {
final Intent intent = new Intent(NOTIFICATION_AUTO_FILL_INTENT);
- intent.putExtra(EXTRA_USER_ID, userId);
intent.putExtra(EXTRA_NOTIFICATION_TYPE, type);
return intent;
}
- private PendingIntent newPickDatasetPI(int userId, int sessionId, FillResponse response,
- Dataset dataset) {
- final int resultCode = ++ sResultCode;
- if (DEBUG) Slog.d(TAG, "newPickDatasetPI: userId=" + userId + ", sessionId=" + sessionId
- + ", resultCode=" + resultCode);
-
- final Intent intent = newNotificationIntent(userId, TYPE_PICK_DATASET);
- intent.putExtra(EXTRA_SESSION_ID, sessionId);
- intent.putExtra(EXTRA_FILL_RESPONSE, response);
- intent.putExtra(EXTRA_DATASET, dataset);
- return PendingIntent.getBroadcast(mContext, resultCode, intent,
- PendingIntent.FLAG_ONE_SHOT);
- }
-
- /**
- * Shows a notification with the results of an auto-fill request, using notications actions
- * to emulate the auto-fill bar buttons displaying the dataset names.
- */
- private void showOptionsNotification(int userId, int callbackId, AutoFillId autoFillId,
- FillResponse response) {
- final long token = Binder.clearCallingIdentity();
- try {
- showOptionsNotificationAsSystem(userId, callbackId, autoFillId, response);
- } finally {
- Binder.restoreCallingIdentity(token);
- }
- }
-
- private void showOptionsNotificationAsSystem(int userId, int sessionId,
- AutoFillId autoFillId, FillResponse response) {
- // Make sure server callback is removed from cache if user cancels the notification.
- final Intent deleteIntent = newNotificationIntent(userId, TYPE_FINISH_SESSION)
- .putExtra(EXTRA_SESSION_ID, sessionId);
- final PendingIntent deletePendingIntent = PendingIntent.getBroadcast(mContext,
- ++sResultCode, deleteIntent, PendingIntent.FLAG_ONE_SHOT);
-
- final String title = "AutoFill Options";
-
- final Notification.Builder notification = newNotificationBuilder()
- .setOngoing(false)
- .setDeleteIntent(deletePendingIntent)
- .setContentTitle(title);
-
- boolean autoCancel = true;
- final String subTitle;
- final List<Dataset> datasets;
- final AutoFillId[] savableIds;
- if (response != null) {
- datasets = response.getDatasets();
- savableIds = response.getSavableIds();
- } else {
- datasets = null;
- savableIds = null;
- }
- boolean showSave = false;
- if (datasets == null ) {
- subTitle = "No options to auto-fill " + autoFillId;
- } else if (datasets.isEmpty()) {
- if (savableIds.length == 0) {
- subTitle = "No options to auto-fill " + autoFillId;
- } else {
- subTitle = "No options to auto-fill " + autoFillId
- + ", but provider can save ids:\n" + Arrays.toString(savableIds);
- showSave = true;
- }
- } else {
- final AutoFillManagerServiceImpl service = mService.getServiceForUserLocked(userId);
- if (service == null) {
- subTitle = "No auto-fill service for user " + userId;
- Slog.w(TAG, subTitle);
- } else {
- autoCancel = false;
- final int size = datasets.size();
- subTitle = "There are " + size + " option(s) to fill " + autoFillId + ".\n"
- + "Use the notification action(s) to select the proper one."
- + "Actions with (F) require fingerprint unlock, and with (P) require"
- + "provider authentication to unlock";
- for (Dataset dataset : datasets) {
- final StringBuilder name = new StringBuilder(dataset.getName());
- if (dataset.isAuthRequired()) {
- if (dataset.hasCryptoObject()) {
- name.append("(F)");
- } else {
- name.append("(P)");
- }
- }
- final PendingIntent pi = newPickDatasetPI(userId, sessionId, response, dataset);
- notification.addAction(new Action.Builder(null, name, pi).build());
- }
- }
- }
-
- notification.setAutoCancel(autoCancel);
- notification.setStyle(new Notification.BigTextStyle().bigText(subTitle));
-
- NotificationManager.from(mContext).notify(TYPE_OPTIONS, userId, notification.build());
-
- if (showSave) {
- showSaveNotification(userId, sessionId);
- }
- }
-
- void showSaveNotification(int userId, int sessionId) {
- final long token = Binder.clearCallingIdentity();
- try {
- showSaveNotificationAsSystem(userId, sessionId);
- } finally {
- Binder.restoreCallingIdentity(token);
- }
- }
-
- private void showSaveNotificationAsSystem(int userId, int sessionId) {
- final Intent saveIntent = newNotificationIntent(userId, TYPE_SAVE)
- .putExtra(EXTRA_SESSION_ID, sessionId);
-
- final PendingIntent savePendingIntent = PendingIntent.getBroadcast(mContext,
- ++sResultCode, saveIntent, PendingIntent.FLAG_ONE_SHOT);
-
- final String title = "AutoFill Save Emulation";
- final String subTitle = "Tap notification to launch the save snackbar.";
-
- final Notification notification = newNotificationBuilder()
- .setAutoCancel(true)
- .setOngoing(false)
- .setContentTitle(title)
- .setContentIntent(savePendingIntent)
- .setStyle(new Notification.BigTextStyle().bigText(subTitle))
- .build();
- NotificationManager.from(mContext).notify(TYPE_SAVE, userId, notification);
- }
-
- private void showAuthNotification(int userId, int sessionId, boolean usesFingerprint,
+ private void showAuthNotification(boolean usesFingerprint,
Bundle extras, int flags) {
final long token = Binder.clearCallingIdentity();
try {
- showAuthNotificationAsSystem(userId, sessionId, usesFingerprint, extras, flags);
+ showAuthNotificationAsSystem(usesFingerprint, extras, flags);
} finally {
Binder.restoreCallingIdentity(token);
}
}
- private void showAuthNotificationAsSystem(int userId, int sessionId,
+ private void showAuthNotificationAsSystem(
boolean usesFingerprint, Bundle extras, int flags) {
final String title = "AutoFill Authentication";
final StringBuilder subTitle = new StringBuilder("Provider require user authentication.\n");
- final Intent authIntent = newNotificationIntent(userId, TYPE_AUTH_RESPONSE)
- .putExtra(EXTRA_SESSION_ID, sessionId);
+ final Intent authIntent = newNotificationIntent(TYPE_AUTH_RESPONSE);
if (extras != null) {
authIntent.putExtra(EXTRA_AUTH_REQUIRED_EXTRAS, extras);
}
@@ -537,11 +380,11 @@ final class AutoFillUI {
if (authPendingIntent != null) {
notification.setContentIntent(authPendingIntent);
}
- NotificationManager.from(mContext).notify(TYPE_AUTH_RESPONSE, userId, notification.build());
+ NotificationManager.from(mContext).notify(mSession.mId, notification.build());
}
- private void dismissAuthNotification(int userId) {
- NotificationManager.from(mContext).cancel(TYPE_AUTH_RESPONSE, userId);
+ private void dismissAuthNotification() {
+ NotificationManager.from(mContext).cancel(mSession.mId);
}
private Notification.Builder newNotificationBuilder() {
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index b82999ec99b1..a83597672cc9 100644
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -4559,18 +4559,18 @@ public class NotificationManagerService extends SystemService {
mHandler.post(new Runnable() {
@Override
public void run() {
- notifyEnqueued(info, sbnToPost, importance, fromUser);
+ notifyEnqueued(info, sbnToPost);
}
});
}
}
private void notifyEnqueued(final ManagedServiceInfo info,
- final StatusBarNotification sbn, int importance, boolean fromUser) {
+ final StatusBarNotification sbn) {
final INotificationListener assistant = (INotificationListener) info.service;
StatusBarNotificationHolder sbnHolder = new StatusBarNotificationHolder(sbn);
try {
- assistant.onNotificationEnqueued(sbnHolder, importance, fromUser);
+ assistant.onNotificationEnqueued(sbnHolder);
} catch (RemoteException ex) {
Log.e(TAG, "unable to notify assistant (enqueued): " + assistant, ex);
}
diff --git a/services/core/java/com/android/server/pm/InstantAppRegistry.java b/services/core/java/com/android/server/pm/InstantAppRegistry.java
index 829c47363bb8..42934a43fb25 100644
--- a/services/core/java/com/android/server/pm/InstantAppRegistry.java
+++ b/services/core/java/com/android/server/pm/InstantAppRegistry.java
@@ -469,7 +469,7 @@ class InstantAppRegistry {
return;
}
final int appCount = uninstalledAppStates.size();
- for (int i = 0; i < appCount; i++) {
+ for (int i = appCount - 1; i >= 0; --i) {
UninstalledInstantAppState uninstalledAppState = uninstalledAppStates.get(i);
if (!criteria.test(uninstalledAppState)) {
continue;
diff --git a/services/core/java/com/android/server/wm/AppTransition.java b/services/core/java/com/android/server/wm/AppTransition.java
index 915951381ac7..3eb529b568f2 100644
--- a/services/core/java/com/android/server/wm/AppTransition.java
+++ b/services/core/java/com/android/server/wm/AppTransition.java
@@ -1131,8 +1131,10 @@ public class AppTransition implements Dump {
// We scale the width and clip to the top/left square
float scale = thumbWidth /
(appWidth - contentInsets.left - contentInsets.right);
- int unscaledThumbHeight = (int) (thumbHeight / scale);
- mTmpFromClipRect.bottom = mTmpFromClipRect.top + unscaledThumbHeight;
+ if (!mGridLayoutRecentsEnabled) {
+ int unscaledThumbHeight = (int) (thumbHeight / scale);
+ mTmpFromClipRect.bottom = mTmpFromClipRect.top + unscaledThumbHeight;
+ }
mNextAppTransitionInsets.set(contentInsets);
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index d62c62e7b328..9b96523af73f 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -2171,7 +2171,8 @@ public class WindowManagerService extends IWindowManager.Stub
if (!win.mHasSurface) {
result |= RELAYOUT_RES_SURFACE_CHANGED;
}
- WindowSurfaceController surfaceController = winAnimator.createSurfaceLocked();
+ WindowSurfaceController surfaceController = winAnimator.createSurfaceLocked(
+ win.mAttrs.type, win.mOwnerUid);
if (surfaceController != null) {
surfaceController.getSurface(outSurface);
if (SHOW_TRANSACTIONS) Slog.i(TAG_WM, " OUT SURFACE " + outSurface + ": copied");
diff --git a/services/core/java/com/android/server/wm/WindowStateAnimator.java b/services/core/java/com/android/server/wm/WindowStateAnimator.java
index 19ef44cace33..abce222bf33f 100644
--- a/services/core/java/com/android/server/wm/WindowStateAnimator.java
+++ b/services/core/java/com/android/server/wm/WindowStateAnimator.java
@@ -577,7 +577,7 @@ class WindowStateAnimator {
}
}
- WindowSurfaceController createSurfaceLocked() {
+ WindowSurfaceController createSurfaceLocked(int windowType, int ownerUid) {
final WindowState w = mWin;
if (w.restoreSavedSurface()) {
if (DEBUG_ANIM) Slog.i(TAG,
@@ -653,7 +653,7 @@ class WindowStateAnimator {
mSurfaceController = new WindowSurfaceController(mSession.mSurfaceSession,
attrs.getTitle().toString(),
- width, height, format, flags, this);
+ width, height, format, flags, this, windowType, ownerUid);
w.setHasSurface(true);
@@ -1321,8 +1321,8 @@ class WindowStateAnimator {
}
// We want to calculate the scaling based on the content area, not based on
// the entire surface, so that we scale in sync with windows that don't have insets.
- mExtraHScale = (mTmpClipRect.width() - hInsets) / (float)(surfaceWidth - hInsets);
- mExtraVScale = (mTmpClipRect.height() - vInsets) / (float)(surfaceHeight - vInsets);
+ mExtraHScale = (finalClipRect.width() - hInsets) / (float)(surfaceWidth - hInsets);
+ mExtraVScale = (finalClipRect.height() - vInsets) / (float)(surfaceHeight - vInsets);
// In the case of ForceScaleToCrop we scale entire tasks together,
// and so we need to scale our offsets relative to the task bounds
@@ -1345,8 +1345,7 @@ class WindowStateAnimator {
// Since we are scaled to fit in our previously desired crop, we can now
// expose the whole window in buffer space, and not risk extending
// past where the system would have cropped us
- mTmpClipRect.set(0, 0, (int)surfaceWidth, (int)surfaceHeight);
- mTmpFinalClipRect.setEmpty();
+ clipRect = null;
// Various surfaces in the scaled stack may resize at different times.
// We need to ensure for each surface, that we disable transformation matrix
diff --git a/services/core/java/com/android/server/wm/WindowSurfaceController.java b/services/core/java/com/android/server/wm/WindowSurfaceController.java
index c48a58514c3d..1096ede96316 100644
--- a/services/core/java/com/android/server/wm/WindowSurfaceController.java
+++ b/services/core/java/com/android/server/wm/WindowSurfaceController.java
@@ -79,8 +79,8 @@ class WindowSurfaceController {
private final WindowManagerService mService;
- public WindowSurfaceController(SurfaceSession s,
- String name, int w, int h, int format, int flags, WindowStateAnimator animator) {
+ public WindowSurfaceController(SurfaceSession s, String name, int w, int h, int format,
+ int flags, WindowStateAnimator animator, int windowType, int ownerUid) {
mAnimator = animator;
mSurfaceW = w;
@@ -98,13 +98,13 @@ class WindowSurfaceController {
animator.mWin.mSubLayer < 0 &&
animator.mWin.mAppToken != null) {
mSurfaceControl = new SurfaceControlWithBackground(s,
- name, w, h, format, flags, animator.mWin.mAppToken);
+ name, w, h, format, flags, animator.mWin.mAppToken, windowType, ownerUid);
} else if (DEBUG_SURFACE_TRACE) {
mSurfaceControl = new SurfaceTrace(
- s, name, w, h, format, flags);
+ s, name, w, h, format, flags, windowType, ownerUid);
} else {
mSurfaceControl = new SurfaceControl(
- s, name, w, h, format, flags);
+ s, name, w, h, format, flags, windowType, ownerUid);
}
if (mService.mRoot.mSurfaceTraceEnabled) {
@@ -569,9 +569,21 @@ class WindowSurfaceController {
private float mDsdx, mDtdx, mDsdy, mDtdy;
private final String mName;
+ public SurfaceTrace(SurfaceSession s, String name, int w, int h, int format, int flags,
+ int windowType, int ownerUid)
+ throws OutOfResourcesException {
+ super(s, name, w, h, format, flags, windowType, ownerUid);
+ mName = name != null ? name : "Not named";
+ mSize.set(w, h);
+ if (LOG_SURFACE_TRACE) Slog.v(SURFACE_TAG, "ctor: " + this + ". Called by "
+ + Debug.getCallers(3));
+ synchronized (sSurfaces) {
+ sSurfaces.add(0, this);
+ }
+ }
+
public SurfaceTrace(SurfaceSession s,
- String name, int w, int h, int format, int flags)
- throws OutOfResourcesException {
+ String name, int w, int h, int format, int flags) {
super(s, name, w, h, format, flags);
mName = name != null ? name : "Not named";
mSize.set(w, h);
@@ -806,11 +818,10 @@ class WindowSurfaceController {
public boolean mVisible = false;
public int mLayer = -1;
- public SurfaceControlWithBackground(SurfaceSession s,
- String name, int w, int h, int format, int flags,
- AppWindowToken token)
- throws OutOfResourcesException {
- super(s, name, w, h, format, flags);
+ public SurfaceControlWithBackground(SurfaceSession s, String name, int w, int h, int format,
+ int flags, AppWindowToken token, int windowType, int ownerUid)
+ throws OutOfResourcesException {
+ super(s, name, w, h, format, flags, windowType, ownerUid);
mBackgroundControl = new SurfaceControl(s, name, w, h,
PixelFormat.OPAQUE, flags | SurfaceControl.FX_SURFACE_DIM);
mOpaque = (flags & SurfaceControl.OPAQUE) != 0;
diff --git a/tests/UiBench/AndroidManifest.xml b/tests/UiBench/AndroidManifest.xml
index 07f7617828e5..c20be5141df5 100644
--- a/tests/UiBench/AndroidManifest.xml
+++ b/tests/UiBench/AndroidManifest.xml
@@ -100,6 +100,22 @@
</intent-filter>
</activity>
<activity
+ android:name=".FadingEdgeListActivity"
+ android:label="General/Fading Edge ListView" >
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="com.android.test.uibench.TEST" />
+ </intent-filter>
+ </activity>
+ <activity
+ android:name=".SaveLayerInterleaveActivity"
+ android:label="General/SaveLayer Animation" >
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="com.android.test.uibench.TEST" />
+ </intent-filter>
+ </activity>
+ <activity
android:name=".ClippedListActivity"
android:label="General/Clipped ListView"
android:theme="@style/NoActionBar">
diff --git a/tests/UiBench/src/com/android/test/uibench/FadingEdgeListActivity.java b/tests/UiBench/src/com/android/test/uibench/FadingEdgeListActivity.java
new file mode 100644
index 000000000000..3241e669fb1d
--- /dev/null
+++ b/tests/UiBench/src/com/android/test/uibench/FadingEdgeListActivity.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2017 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.test.uibench;
+
+import android.support.v4.app.ListFragment;
+import android.widget.ArrayAdapter;
+import android.widget.ListAdapter;
+
+import com.android.test.uibench.listview.CompatListActivity;
+import com.android.test.uibench.listview.FadingEdgeListFragment;
+
+public class FadingEdgeListActivity extends CompatListActivity {
+
+ @Override
+ protected ListAdapter createListAdapter() {
+ return new ArrayAdapter<>(this, android.R.layout.simple_list_item_1,
+ TextUtils.buildSimpleStringList(40));
+ }
+
+ @Override
+ protected ListFragment createListFragment() {
+ return (ListFragment)new FadingEdgeListFragment();
+ }
+}
diff --git a/tests/UiBench/src/com/android/test/uibench/SaveLayerInterleaveActivity.java b/tests/UiBench/src/com/android/test/uibench/SaveLayerInterleaveActivity.java
new file mode 100644
index 000000000000..eec91cb38066
--- /dev/null
+++ b/tests/UiBench/src/com/android/test/uibench/SaveLayerInterleaveActivity.java
@@ -0,0 +1,89 @@
+/*
+ * Copyright (C) 2017 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.test.uibench;
+
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.ColorFilter;
+import android.graphics.Paint;
+import android.graphics.PixelFormat;
+import android.graphics.Rect;
+import android.graphics.drawable.Drawable;
+import android.os.Bundle;
+import android.support.v7.app.AppCompatActivity;
+
+/**
+ * Test Canvas.saveLayer performance by interleaving drawText/drawRect with saveLayer.
+ * This test will be used to measure if drawing interleaved layers at the beginning of a frame will
+ * decrease FBO switching overhead (this is a future optimization in SkiaGL rendering pipeline).
+ */
+public class SaveLayerInterleaveActivity extends AppCompatActivity {
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ getWindow().setBackgroundDrawable(new Drawable() {
+ private final Paint mBluePaint = new Paint(Paint.ANTI_ALIAS_FLAG);
+ private final Paint mGreenPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
+
+ @Override
+ public void setAlpha(int alpha) {
+ }
+
+ @Override
+ public int getOpacity() {
+ return PixelFormat.OPAQUE;
+ }
+
+ @Override
+ public void setColorFilter(ColorFilter colorFilter) {
+ }
+
+ @Override
+ public void draw(Canvas canvas) {
+ canvas.drawColor(Color.RED);
+
+ Rect bounds = getBounds();
+ int regions = 20;
+ int smallRectHeight = (bounds.height()/regions);
+ int padding = smallRectHeight / 4;
+ int top = bounds.top;
+ mBluePaint.setColor(Color.BLUE);
+ mBluePaint.setTextSize(padding);
+ mGreenPaint.setColor(Color.GREEN);
+ mGreenPaint.setTextSize(padding);
+
+ //interleave drawText and drawRect with saveLayer ops
+ for (int i = 0; i < regions; i++, top += smallRectHeight) {
+ canvas.saveLayer(bounds.left, top, bounds.right, top + padding,
+ mBluePaint);
+ canvas.drawColor(Color.YELLOW);
+ canvas.drawText("offscreen line "+ i, bounds.left, top + padding,
+ mBluePaint);
+ canvas.restore();
+
+ Rect partX = new Rect(bounds.left, top + padding,
+ bounds.right,top + smallRectHeight - padding);
+ canvas.drawRect(partX, mBluePaint);
+ canvas.drawText("onscreen line "+ i, bounds.left,
+ top + smallRectHeight - padding, mGreenPaint);
+ }
+
+ invalidateSelf();
+ }
+ });
+ }
+}
diff --git a/tests/UiBench/src/com/android/test/uibench/listview/CompatListActivity.java b/tests/UiBench/src/com/android/test/uibench/listview/CompatListActivity.java
index 214c07463fd7..bb7f4a302f8c 100644
--- a/tests/UiBench/src/com/android/test/uibench/listview/CompatListActivity.java
+++ b/tests/UiBench/src/com/android/test/uibench/listview/CompatListActivity.java
@@ -29,11 +29,15 @@ public abstract class CompatListActivity extends AppCompatActivity {
FragmentManager fm = getSupportFragmentManager();
if (fm.findFragmentById(android.R.id.content) == null) {
- ListFragment listFragment = new ListFragment();
+ ListFragment listFragment = createListFragment();
listFragment.setListAdapter(createListAdapter());
fm.beginTransaction().add(android.R.id.content, listFragment).commit();
}
}
protected abstract ListAdapter createListAdapter();
+
+ protected ListFragment createListFragment() {
+ return new ListFragment();
+ }
}
diff --git a/tests/UiBench/src/com/android/test/uibench/listview/FadingEdgeListFragment.java b/tests/UiBench/src/com/android/test/uibench/listview/FadingEdgeListFragment.java
new file mode 100644
index 000000000000..a018b40e8528
--- /dev/null
+++ b/tests/UiBench/src/com/android/test/uibench/listview/FadingEdgeListFragment.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2017 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.test.uibench.listview;
+
+import android.os.Bundle;
+import android.support.v4.app.ListFragment;
+import android.widget.ListView;
+
+public class FadingEdgeListFragment extends ListFragment {
+
+ @Override
+ public void onActivityCreated(Bundle savedInstanceState) {
+ ListView listView = getListView();
+ listView.setVerticalFadingEdgeEnabled(true);
+ listView.setFadingEdgeLength(500);
+ super.onActivityCreated(savedInstanceState);
+ }
+}