Merge "Squishy ViewState no longer mutates global album art widgetState" into tm-dev
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index 315bd71..0a2b421 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -1811,10 +1811,6 @@
* #EXTRA_PROVISIONING_ALLOWED_PROVISIONING_MODES} array extra contain {@link
* #PROVISIONING_MODE_MANAGED_PROFILE} and {@link #PROVISIONING_MODE_FULLY_MANAGED_DEVICE}.
*
- * <p>Also, if this flag is set, the admin app's {@link #ACTION_GET_PROVISIONING_MODE} activity
- * will not receive the {@link #EXTRA_PROVISIONING_IMEI} and {@link
- * #EXTRA_PROVISIONING_SERIAL_NUMBER} extras.
- *
* <p>This flag can be combined with {@link #FLAG_SUPPORTED_MODES_PERSONALLY_OWNED}. In
* that case, the admin app's {@link #ACTION_GET_PROVISIONING_MODE} activity will have
* the {@link #EXTRA_PROVISIONING_ALLOWED_PROVISIONING_MODES} array extra contain {@link
@@ -1834,6 +1830,10 @@
* activity to have the {@link #EXTRA_PROVISIONING_ALLOWED_PROVISIONING_MODES} array extra
* contain only {@link #PROVISIONING_MODE_MANAGED_PROFILE}.
*
+ * <p>Also, if this flag is set, the admin app's {@link #ACTION_GET_PROVISIONING_MODE} activity
+ * will not receive the {@link #EXTRA_PROVISIONING_IMEI} and {@link
+ * #EXTRA_PROVISIONING_SERIAL_NUMBER} extras.
+ *
* <p>This flag can be combined with {@link #FLAG_SUPPORTED_MODES_ORGANIZATION_OWNED}. In
* that case, the admin app's {@link #ACTION_GET_PROVISIONING_MODE} activity will have the
* {@link #EXTRA_PROVISIONING_ALLOWED_PROVISIONING_MODES} array extra contain {@link
@@ -13249,8 +13249,9 @@
* Called by a device owner, profile owner of a managed profile or delegated app with
* {@link #DELEGATION_NETWORK_LOGGING} to control the network logging feature.
*
- * <p> When network logging is enabled by a profile owner, the network logs will only include
- * work profile network activity, not activity on the personal profile.
+ * <p> Supported for a device owner from Android 8. Supported for a profile owner of a managed
+ * profile from Android 12. When network logging is enabled by a profile owner, the network logs
+ * will only include work profile network activity, not activity on the personal profile.
*
* <p> Network logs contain DNS lookup and connect() library call events. The following library
* functions are recorded while network logging is active:
diff --git a/core/java/android/hardware/camera2/impl/CameraExtensionSessionImpl.java b/core/java/android/hardware/camera2/impl/CameraExtensionSessionImpl.java
index 1263da6..4d0ba63 100644
--- a/core/java/android/hardware/camera2/impl/CameraExtensionSessionImpl.java
+++ b/core/java/android/hardware/camera2/impl/CameraExtensionSessionImpl.java
@@ -72,7 +72,7 @@
import java.util.concurrent.Executor;
public final class CameraExtensionSessionImpl extends CameraExtensionSession {
- private static final int PREVIEW_QUEUE_SIZE = 3;
+ private static final int PREVIEW_QUEUE_SIZE = 10;
private static final String TAG = "CameraExtensionSessionImpl";
private final Executor mExecutor;
@@ -1057,15 +1057,8 @@
mClientRequest));
if (mCaptureResultHandler != null) {
- CameraMetadataNative captureResults = new CameraMetadataNative();
- for (CaptureResult.Key key : mSupportedResultKeys) {
- Object value = result.get(key);
- if (value != null) {
- captureResults.set(key, value);
- }
- }
mCaptureResultHandler.onCaptureCompleted(timestamp,
- captureResults);
+ initializeFilteredResults(result));
}
} finally {
Binder.restoreCallingIdentity(ident);
@@ -1127,6 +1120,11 @@
private class ImageCallback implements OnImageAvailableListener {
@Override
+ public void onImageDropped(long timestamp) {
+ notifyCaptureFailed();
+ }
+
+ @Override
public void onImageAvailable(ImageReader reader, Image img) {
if (mCaptureFailed) {
img.close();
@@ -1160,6 +1158,9 @@
private class ImageLoopbackCallback implements OnImageAvailableListener {
@Override
+ public void onImageDropped(long timestamp) { }
+
+ @Override
public void onImageAvailable(ImageReader reader, Image img) {
img.close();
}
@@ -1221,7 +1222,8 @@
}
private interface OnImageAvailableListener {
- public void onImageAvailable (ImageReader reader, Image img);
+ void onImageDropped(long timestamp);
+ void onImageAvailable (ImageReader reader, Image img);
}
private class CameraOutputImageCallback implements ImageReader.OnImageAvailableListener,
@@ -1263,6 +1265,29 @@
} else {
mImageListenerMap.put(img.getTimestamp(), new Pair<>(img, null));
}
+
+ notifyDroppedImages(timestamp);
+ }
+
+ private void notifyDroppedImages(long timestamp) {
+ Set<Long> timestamps = mImageListenerMap.keySet();
+ ArrayList<Long> removedTs = new ArrayList<>();
+ for (long ts : timestamps) {
+ if (ts < timestamp) {
+ Log.e(TAG, "Dropped image with ts: " + ts);
+ Pair<Image, OnImageAvailableListener> entry = mImageListenerMap.get(ts);
+ if (entry.second != null) {
+ entry.second.onImageDropped(ts);
+ }
+ if (entry.first != null) {
+ entry.first.close();
+ }
+ removedTs.add(ts);
+ }
+ }
+ for (long ts : removedTs) {
+ mImageListenerMap.remove(ts);
+ }
}
public void registerListener(Long timestamp, OnImageAvailableListener listener) {
@@ -1291,6 +1316,12 @@
entry.first.close();
}
}
+ for (long timestamp : mImageListenerMap.keySet()) {
+ Pair<Image, OnImageAvailableListener> entry = mImageListenerMap.get(timestamp);
+ if (entry.second != null) {
+ entry.second.onImageDropped(timestamp);
+ }
+ }
mImageListenerMap.clear();
}
}
@@ -1447,7 +1478,6 @@
} else {
notifyConfigurationFailure();
}
-
}
@Override
@@ -1536,7 +1566,16 @@
} else if (mPreviewProcessorType ==
IPreviewExtenderImpl.PROCESSOR_TYPE_IMAGE_PROCESSOR) {
int idx = mPendingResultMap.indexOfKey(timestamp);
- if (idx >= 0) {
+
+ if ((idx >= 0) && (mPendingResultMap.get(timestamp).first == null)) {
+ // Image was dropped before we can receive the capture results
+ if ((mCaptureResultHandler != null)) {
+ mCaptureResultHandler.onCaptureCompleted(timestamp,
+ initializeFilteredResults(result));
+ }
+ discardPendingRepeatingResults(idx, mPendingResultMap, false);
+ } else if (idx >= 0) {
+ // Image came before the capture results
ParcelImage parcelImage = initializeParcelImage(
mPendingResultMap.get(timestamp).first);
try {
@@ -1563,6 +1602,7 @@
}
discardPendingRepeatingResults(idx, mPendingResultMap, false);
} else {
+ // Image not yet available
notifyClient = false;
mPendingResultMap.put(timestamp,
new Pair<>(null,
@@ -1581,16 +1621,8 @@
mClientRequest));
if ((mCaptureResultHandler != null) && (mPreviewProcessorType !=
IPreviewExtenderImpl.PROCESSOR_TYPE_IMAGE_PROCESSOR)) {
- CameraMetadataNative captureResults =
- new CameraMetadataNative();
- for (CaptureResult.Key key : mSupportedResultKeys) {
- Object value = result.get(key);
- if (value != null) {
- captureResults.set(key, value);
- }
- }
mCaptureResultHandler.onCaptureCompleted(timestamp,
- captureResults);
+ initializeFilteredResults(result));
}
} else {
mExecutor.execute(
@@ -1657,19 +1689,24 @@
for (int i = idx; i >= 0; i--) {
if (previewMap.valueAt(i).first != null) {
previewMap.valueAt(i).first.close();
- } else {
- if (mClientNotificationsEnabled && ((i != idx) || notifyCurrentIndex)) {
- Log.w(TAG, "Preview frame drop with timestamp: " + previewMap.keyAt(i));
- final long ident = Binder.clearCallingIdentity();
- try {
- mExecutor.execute(
- () -> mCallbacks
- .onCaptureFailed(CameraExtensionSessionImpl.this,
- mClientRequest));
- } finally {
- Binder.restoreCallingIdentity(ident);
- }
+ } else if (mClientNotificationsEnabled && (previewMap.valueAt(i).second != null) &&
+ ((i != idx) || notifyCurrentIndex)) {
+ TotalCaptureResult result = previewMap.valueAt(i).second;
+ Long timestamp = result.get(CaptureResult.SENSOR_TIMESTAMP);
+ mCaptureResultHandler.onCaptureCompleted(timestamp,
+ initializeFilteredResults(result));
+
+ Log.w(TAG, "Preview frame drop with timestamp: " + previewMap.keyAt(i));
+ final long ident = Binder.clearCallingIdentity();
+ try {
+ mExecutor.execute(
+ () -> mCallbacks
+ .onCaptureFailed(CameraExtensionSessionImpl.this,
+ mClientRequest));
+ } finally {
+ Binder.restoreCallingIdentity(ident);
}
+
}
previewMap.removeAt(i);
}
@@ -1683,6 +1720,12 @@
}
@Override
+ public void onImageDropped(long timestamp) {
+ discardPendingRepeatingResults(mPendingResultMap.indexOfKey(timestamp),
+ mPendingResultMap, true);
+ }
+
+ @Override
public void onImageAvailable(ImageReader reader, Image img) {
if (img == null) {
Log.e(TAG, "Invalid image!");
@@ -1703,6 +1746,15 @@
private class ImageProcessCallback implements OnImageAvailableListener {
@Override
+ public void onImageDropped(long timestamp) {
+ discardPendingRepeatingResults(mPendingResultMap.indexOfKey(timestamp),
+ mPendingResultMap, true);
+ // Add an empty frame&results entry to flag that we dropped a frame
+ // and valid capture results can immediately return to client.
+ mPendingResultMap.put(timestamp, new Pair<>(null, null));
+ }
+
+ @Override
public void onImageAvailable(ImageReader reader, Image img) {
if (mPendingResultMap.size() + 1 >= PREVIEW_QUEUE_SIZE) {
// We reached the maximum acquired images limit. This is possible in case we
@@ -1768,6 +1820,17 @@
}
}
+ private CameraMetadataNative initializeFilteredResults(TotalCaptureResult result) {
+ CameraMetadataNative captureResults = new CameraMetadataNative();
+ for (CaptureResult.Key key : mSupportedResultKeys) {
+ Object value = result.get(key);
+ if (value != null) {
+ captureResults.set(key, value);
+ }
+ }
+ return captureResults;
+ }
+
private static Size findSmallestAspectMatchedSize(@NonNull List<Size> sizes,
@NonNull Size arSize) {
final float TOLL = .01f;
diff --git a/core/java/com/android/internal/util/ImageUtils.java b/core/java/com/android/internal/util/ImageUtils.java
index 397b2c0..62dea9d 100644
--- a/core/java/com/android/internal/util/ImageUtils.java
+++ b/core/java/com/android/internal/util/ImageUtils.java
@@ -137,6 +137,18 @@
*/
public static Bitmap buildScaledBitmap(Drawable drawable, int maxWidth,
int maxHeight) {
+ return buildScaledBitmap(drawable, maxWidth, maxHeight, false);
+ }
+
+ /**
+ * Convert a drawable to a bitmap, scaled to fit within maxWidth and maxHeight.
+ *
+ * @param allowUpscaling if true, the drawable will not only be scaled down, but also scaled up
+ * to fit within the maximum size given. This is useful for converting
+ * vectorized icons which usually have a very small intrinsic size.
+ */
+ public static Bitmap buildScaledBitmap(Drawable drawable, int maxWidth,
+ int maxHeight, boolean allowUpscaling) {
if (drawable == null) {
return null;
}
@@ -155,7 +167,9 @@
// a large notification icon if necessary
float ratio = Math.min((float) maxWidth / (float) originalWidth,
(float) maxHeight / (float) originalHeight);
- ratio = Math.min(1.0f, ratio);
+ if (!allowUpscaling) {
+ ratio = Math.min(1.0f, ratio);
+ }
int scaledWidth = (int) (ratio * originalWidth);
int scaledHeight = (int) (ratio * originalHeight);
Bitmap result = Bitmap.createBitmap(scaledWidth, scaledHeight, Config.ARGB_8888);
diff --git a/data/keyboards/Vendor_0957_Product_0001.kl b/data/keyboards/Vendor_0957_Product_0001.kl
index 13b4096..54f8808 100644
--- a/data/keyboards/Vendor_0957_Product_0001.kl
+++ b/data/keyboards/Vendor_0957_Product_0001.kl
@@ -44,33 +44,37 @@
key 11 0
# custom keys
-key usage 0x000c019C PROFILE_SWITCH
-key usage 0x000c01A2 ALL_APPS
key usage 0x000c01BB TV_INPUT
-key usage 0x000c022A BOOKMARK
-key usage 0x000c0096 SETTINGS
-key usage 0x000c009F NOTIFICATION
-key usage 0x000c008D GUIDE
-key usage 0x000c0089 TV
-key usage 0x000c009C CHANNEL_UP
-key usage 0x000c009D CHANNEL_DOWN
-key usage 0x000c00CD MEDIA_PLAY_PAUSE
-key usage 0x000c00B2 MEDIA_RECORD
-key usage 0x000c00B4 MEDIA_SKIP_BACKWARD
-key usage 0x000c00B3 MEDIA_SKIP_FORWARD
-key usage 0x000c0226 MEDIA_STOP
-key usage 0x000c0077 BUTTON_3 WAKE #YouTube
-key usage 0x000c0078 BUTTON_4 WAKE #Netflix
-key usage 0x000c0079 BUTTON_6 WAKE #Disney+
-key usage 0x000c007A BUTTON_7 WAKE #HBOmax
-
-key usage 0x00070037 PERIOD
-key usage 0x000c01BD INFO
-key usage 0x000c0061 CAPTIONS
key usage 0x000c0185 TV_TELETEXT
+key usage 0x000c0061 CAPTIONS
+
+key usage 0x000c01BD INFO
+key usage 0x000c0037 PERIOD
key usage 0x000c0069 PROG_RED
key usage 0x000c006A PROG_GREEN
+key usage 0x000c006C PROG_YELLOW
key usage 0x000c006B PROG_BLUE
-key usage 0x000c006C PROG_YELLOW
\ No newline at end of file
+key usage 0x000c00B4 MEDIA_SKIP_BACKWARD
+key usage 0x000c00CD MEDIA_PLAY_PAUSE
+key usage 0x000c00B2 MEDIA_RECORD
+key usage 0x000c00B3 MEDIA_SKIP_FORWARD
+
+key usage 0x000c022A BOOKMARK
+key usage 0x000c01A2 ALL_APPS
+key usage 0x000c019C PROFILE_SWITCH
+
+key usage 0x000c0096 SETTINGS
+key usage 0x000c009F NOTIFICATION
+
+key usage 0x000c008D GUIDE
+key usage 0x000c0089 TV
+
+key usage 0x000c009C CHANNEL_UP
+key usage 0x000c009D CHANNEL_DOWN
+
+key usage 0x000c0077 BUTTON_3 WAKE #YouTube
+key usage 0x000c0078 BUTTON_4 WAKE #Netflix
+key usage 0x000c0079 BUTTON_6 WAKE
+key usage 0x000c007A BUTTON_7 WAKE
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationController.java
index c3fbe55..8fa9f56 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationController.java
@@ -246,9 +246,13 @@
* {@link BackAnimationController}
*/
public void onMotionEvent(MotionEvent event, int action, @BackEvent.SwipeEdge int swipeEdge) {
- if (action == MotionEvent.ACTION_DOWN) {
- initAnimation(event);
- } else if (action == MotionEvent.ACTION_MOVE) {
+ if (action == MotionEvent.ACTION_MOVE) {
+ if (!mBackGestureStarted) {
+ // Let the animation initialized here to make sure the onPointerDownOutsideFocus
+ // could be happened when ACTION_DOWN, it may change the current focus that we
+ // would access it when startBackNavigation.
+ initAnimation(event);
+ }
onMove(event, swipeEdge);
} else if (action == MotionEvent.ACTION_UP || action == MotionEvent.ACTION_CANCEL) {
ProtoLog.d(WM_SHELL_BACK_PREVIEW,
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/TvPipModule.java b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/TvPipModule.java
index 145e527..dfd4362 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/TvPipModule.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/TvPipModule.java
@@ -143,8 +143,11 @@
@Provides
static TvPipNotificationController provideTvPipNotificationController(Context context,
PipMediaController pipMediaController,
+ PipParamsChangedForwarder pipParamsChangedForwarder,
+ TvPipBoundsState tvPipBoundsState,
@ShellMainThread Handler mainHandler) {
- return new TvPipNotificationController(context, pipMediaController, mainHandler);
+ return new TvPipNotificationController(context, pipMediaController,
+ pipParamsChangedForwarder, tvPipBoundsState, mainHandler);
}
@WMSingleton
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipMediaController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipMediaController.java
index 8a50f22..65a12d6 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipMediaController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipMediaController.java
@@ -32,6 +32,7 @@
import android.graphics.drawable.Icon;
import android.media.MediaMetadata;
import android.media.session.MediaController;
+import android.media.session.MediaSession;
import android.media.session.MediaSessionManager;
import android.media.session.PlaybackState;
import android.os.Handler;
@@ -64,7 +65,7 @@
*/
public interface ActionListener {
/**
- * Called when the media actions changes.
+ * Called when the media actions changed.
*/
void onMediaActionsChanged(List<RemoteAction> actions);
}
@@ -74,11 +75,21 @@
*/
public interface MetadataListener {
/**
- * Called when the media metadata changes.
+ * Called when the media metadata changed.
*/
void onMediaMetadataChanged(MediaMetadata metadata);
}
+ /**
+ * A listener interface to receive notification on changes to the media session token.
+ */
+ public interface TokenListener {
+ /**
+ * Called when the media session token changed.
+ */
+ void onMediaSessionTokenChanged(MediaSession.Token token);
+ }
+
private final Context mContext;
private final Handler mMainHandler;
private final HandlerExecutor mHandlerExecutor;
@@ -133,6 +144,7 @@
private final ArrayList<ActionListener> mActionListeners = new ArrayList<>();
private final ArrayList<MetadataListener> mMetadataListeners = new ArrayList<>();
+ private final ArrayList<TokenListener> mTokenListeners = new ArrayList<>();
public PipMediaController(Context context, Handler mainHandler) {
mContext = context;
@@ -204,6 +216,31 @@
mMetadataListeners.remove(listener);
}
+ /**
+ * Adds a new token listener.
+ */
+ public void addTokenListener(TokenListener listener) {
+ if (!mTokenListeners.contains(listener)) {
+ mTokenListeners.add(listener);
+ listener.onMediaSessionTokenChanged(getToken());
+ }
+ }
+
+ /**
+ * Removes a token listener.
+ */
+ public void removeTokenListener(TokenListener listener) {
+ listener.onMediaSessionTokenChanged(null);
+ mTokenListeners.remove(listener);
+ }
+
+ private MediaSession.Token getToken() {
+ if (mMediaController == null) {
+ return null;
+ }
+ return mMediaController.getSessionToken();
+ }
+
private MediaMetadata getMediaMetadata() {
return mMediaController != null ? mMediaController.getMetadata() : null;
}
@@ -294,6 +331,7 @@
}
notifyActionsChanged();
notifyMetadataChanged(getMediaMetadata());
+ notifyTokenChanged(getToken());
// TODO(winsonc): Consider if we want to close the PIP after a timeout (like on TV)
}
@@ -317,4 +355,10 @@
mMetadataListeners.forEach(l -> l.onMediaMetadataChanged(metadata));
}
}
+
+ private void notifyTokenChanged(MediaSession.Token token) {
+ if (!mTokenListeners.isEmpty()) {
+ mTokenListeners.forEach(l -> l.onMediaSessionTokenChanged(token));
+ }
+ }
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipController.java
index fcd1f95..7667794 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipController.java
@@ -293,6 +293,8 @@
}
mTvPipBoundsState.setTvPipManuallyCollapsed(!expanding);
mTvPipBoundsState.setTvPipExpanded(expanding);
+ mPipNotificationController.updateExpansionState();
+
updatePinnedStackBounds();
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipNotificationController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipNotificationController.java
index 4033f03..61a609d 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipNotificationController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipNotificationController.java
@@ -16,36 +16,47 @@
package com.android.wm.shell.pip.tv;
+import static android.app.Notification.Action.SEMANTIC_ACTION_DELETE;
+import static android.app.Notification.Action.SEMANTIC_ACTION_NONE;
+
import android.app.Notification;
import android.app.NotificationManager;
import android.app.PendingIntent;
+import android.app.RemoteAction;
import android.content.BroadcastReceiver;
+import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.graphics.Bitmap;
-import android.media.MediaMetadata;
+import android.graphics.drawable.Drawable;
+import android.graphics.drawable.Icon;
+import android.media.session.MediaSession;
+import android.os.Bundle;
import android.os.Handler;
import android.text.TextUtils;
import com.android.internal.messages.nano.SystemMessageProto.SystemMessage;
import com.android.internal.protolog.common.ProtoLog;
+import com.android.internal.util.ImageUtils;
import com.android.wm.shell.R;
import com.android.wm.shell.pip.PipMediaController;
+import com.android.wm.shell.pip.PipParamsChangedForwarder;
+import com.android.wm.shell.pip.PipUtils;
import com.android.wm.shell.protolog.ShellProtoLogGroup;
-import java.util.Objects;
+import java.util.ArrayList;
+import java.util.List;
/**
- * A notification that informs users that PIP is running and also provides PIP controls.
- * <p>Once it's created, it will manage the PIP notification UI by itself except for handling
- * configuration changes.
+ * A notification that informs users that PiP is running and also provides PiP controls.
+ * <p>Once it's created, it will manage the PiP notification UI by itself except for handling
+ * configuration changes and user initiated expanded PiP toggling.
*/
public class TvPipNotificationController {
private static final String TAG = "TvPipNotification";
- private static final boolean DEBUG = TvPipController.DEBUG;
// Referenced in com.android.systemui.util.NotificationChannels.
public static final String NOTIFICATION_CHANNEL = "TVPIP";
@@ -60,6 +71,8 @@
"com.android.wm.shell.pip.tv.notification.action.MOVE_PIP";
private static final String ACTION_TOGGLE_EXPANDED_PIP =
"com.android.wm.shell.pip.tv.notification.action.TOGGLE_EXPANDED_PIP";
+ private static final String ACTION_FULLSCREEN =
+ "com.android.wm.shell.pip.tv.notification.action.FULLSCREEN";
private final Context mContext;
private final PackageManager mPackageManager;
@@ -68,44 +81,88 @@
private final ActionBroadcastReceiver mActionBroadcastReceiver;
private final Handler mMainHandler;
private Delegate mDelegate;
+ private final TvPipBoundsState mTvPipBoundsState;
private String mDefaultTitle;
+ private final List<RemoteAction> mCustomActions = new ArrayList<>();
+ private final List<RemoteAction> mMediaActions = new ArrayList<>();
+ private RemoteAction mCustomCloseAction;
+
+ private MediaSession.Token mMediaSessionToken;
+
/** Package name for the application that owns PiP window. */
private String mPackageName;
- private boolean mNotified;
- private String mMediaTitle;
- private Bitmap mArt;
+
+ private boolean mIsNotificationShown;
+ private String mPipTitle;
+ private String mPipSubtitle;
+
+ private Bitmap mActivityIcon;
public TvPipNotificationController(Context context, PipMediaController pipMediaController,
+ PipParamsChangedForwarder pipParamsChangedForwarder, TvPipBoundsState tvPipBoundsState,
Handler mainHandler) {
mContext = context;
mPackageManager = context.getPackageManager();
mNotificationManager = context.getSystemService(NotificationManager.class);
mMainHandler = mainHandler;
+ mTvPipBoundsState = tvPipBoundsState;
mNotificationBuilder = new Notification.Builder(context, NOTIFICATION_CHANNEL)
.setLocalOnly(true)
- .setOngoing(false)
+ .setOngoing(true)
.setCategory(Notification.CATEGORY_SYSTEM)
.setShowWhen(true)
.setSmallIcon(R.drawable.pip_icon)
+ .setAllowSystemGeneratedContextualActions(false)
+ .setContentIntent(createPendingIntent(context, ACTION_FULLSCREEN))
+ .setDeleteIntent(getCloseAction().actionIntent)
.extend(new Notification.TvExtender()
.setContentIntent(createPendingIntent(context, ACTION_SHOW_PIP_MENU))
.setDeleteIntent(createPendingIntent(context, ACTION_CLOSE_PIP)));
mActionBroadcastReceiver = new ActionBroadcastReceiver();
- pipMediaController.addMetadataListener(this::onMediaMetadataChanged);
+ pipMediaController.addActionListener(this::onMediaActionsChanged);
+ pipMediaController.addTokenListener(this::onMediaSessionTokenChanged);
+
+ pipParamsChangedForwarder.addListener(
+ new PipParamsChangedForwarder.PipParamsChangedCallback() {
+ @Override
+ public void onExpandedAspectRatioChanged(float ratio) {
+ updateExpansionState();
+ }
+
+ @Override
+ public void onActionsChanged(List<RemoteAction> actions,
+ RemoteAction closeAction) {
+ mCustomActions.clear();
+ mCustomActions.addAll(actions);
+ mCustomCloseAction = closeAction;
+ updateNotificationContent();
+ }
+
+ @Override
+ public void onTitleChanged(String title) {
+ mPipTitle = title;
+ updateNotificationContent();
+ }
+
+ @Override
+ public void onSubtitleChanged(String subtitle) {
+ mPipSubtitle = subtitle;
+ updateNotificationContent();
+ }
+ });
onConfigurationChanged(context);
}
void setDelegate(Delegate delegate) {
- if (DEBUG) {
- ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
- "%s: setDelegate(), delegate=%s", TAG, delegate);
- }
+ ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE, "%s: setDelegate(), delegate=%s",
+ TAG, delegate);
+
if (mDelegate != null) {
throw new IllegalStateException(
"The delegate has already been set and should not change.");
@@ -118,90 +175,181 @@
}
void show(String packageName) {
+ ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE, "%s: show %s", TAG, packageName);
if (mDelegate == null) {
throw new IllegalStateException("Delegate is not set.");
}
+ mIsNotificationShown = true;
mPackageName = packageName;
- update();
+ mActivityIcon = getActivityIcon();
mActionBroadcastReceiver.register();
+
+ updateNotificationContent();
}
void dismiss() {
- mNotificationManager.cancel(NOTIFICATION_TAG, SystemMessage.NOTE_TV_PIP);
- mNotified = false;
+ ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE, "%s: dismiss()", TAG);
+
+ mIsNotificationShown = false;
mPackageName = null;
mActionBroadcastReceiver.unregister();
+
+ mNotificationManager.cancel(NOTIFICATION_TAG, SystemMessage.NOTE_TV_PIP);
}
- private void onMediaMetadataChanged(MediaMetadata metadata) {
- if (updateMediaControllerMetadata(metadata) && mNotified) {
- // update notification
- update();
+ private Notification.Action getToggleAction(boolean expanded) {
+ if (expanded) {
+ return createSystemAction(R.drawable.pip_ic_collapse,
+ R.string.pip_collapse, ACTION_TOGGLE_EXPANDED_PIP);
+ } else {
+ return createSystemAction(R.drawable.pip_ic_expand, R.string.pip_expand,
+ ACTION_TOGGLE_EXPANDED_PIP);
}
}
+ private Notification.Action createSystemAction(int iconRes, int titleRes, String action) {
+ Notification.Action.Builder builder = new Notification.Action.Builder(
+ Icon.createWithResource(mContext, iconRes),
+ mContext.getString(titleRes),
+ createPendingIntent(mContext, action));
+ builder.setContextual(true);
+ return builder.build();
+ }
+
+ private void onMediaActionsChanged(List<RemoteAction> actions) {
+ mMediaActions.clear();
+ mMediaActions.addAll(actions);
+ if (mCustomActions.isEmpty()) {
+ updateNotificationContent();
+ }
+ }
+
+ private void onMediaSessionTokenChanged(MediaSession.Token token) {
+ mMediaSessionToken = token;
+ updateNotificationContent();
+ }
+
+ private Notification.Action remoteToNotificationAction(RemoteAction action) {
+ return remoteToNotificationAction(action, SEMANTIC_ACTION_NONE);
+ }
+
+ private Notification.Action remoteToNotificationAction(RemoteAction action,
+ int semanticAction) {
+ Notification.Action.Builder builder = new Notification.Action.Builder(action.getIcon(),
+ action.getTitle(),
+ action.getActionIntent());
+ if (action.getContentDescription() != null) {
+ Bundle extras = new Bundle();
+ extras.putCharSequence(Notification.EXTRA_PICTURE_CONTENT_DESCRIPTION,
+ action.getContentDescription());
+ builder.addExtras(extras);
+ }
+ builder.setSemanticAction(semanticAction);
+ builder.setContextual(true);
+ return builder.build();
+ }
+
+ private Notification.Action[] getNotificationActions() {
+ final List<Notification.Action> actions = new ArrayList<>();
+
+ // 1. Fullscreen
+ actions.add(getFullscreenAction());
+ // 2. Close
+ actions.add(getCloseAction());
+ // 3. App actions
+ final List<RemoteAction> appActions =
+ mCustomActions.isEmpty() ? mMediaActions : mCustomActions;
+ for (RemoteAction appAction : appActions) {
+ if (PipUtils.remoteActionsMatch(mCustomCloseAction, appAction)
+ || !appAction.isEnabled()) {
+ continue;
+ }
+ actions.add(remoteToNotificationAction(appAction));
+ }
+ // 4. Move
+ actions.add(getMoveAction());
+ // 5. Toggle expansion (if expanded PiP enabled)
+ if (mTvPipBoundsState.getDesiredTvExpandedAspectRatio() > 0
+ && mTvPipBoundsState.isTvExpandedPipSupported()) {
+ actions.add(getToggleAction(mTvPipBoundsState.isTvPipExpanded()));
+ }
+ return actions.toArray(new Notification.Action[0]);
+ }
+
+ private Notification.Action getCloseAction() {
+ if (mCustomCloseAction == null) {
+ return createSystemAction(R.drawable.pip_ic_close_white, R.string.pip_close,
+ ACTION_CLOSE_PIP);
+ } else {
+ return remoteToNotificationAction(mCustomCloseAction, SEMANTIC_ACTION_DELETE);
+ }
+ }
+
+ private Notification.Action getFullscreenAction() {
+ return createSystemAction(R.drawable.pip_ic_fullscreen_white,
+ R.string.pip_fullscreen, ACTION_FULLSCREEN);
+ }
+
+ private Notification.Action getMoveAction() {
+ return createSystemAction(R.drawable.pip_ic_move_white, R.string.pip_move,
+ ACTION_MOVE_PIP);
+ }
+
/**
- * Called by {@link PipController} when the configuration is changed.
+ * Called by {@link TvPipController} when the configuration is changed.
*/
void onConfigurationChanged(Context context) {
mDefaultTitle = context.getResources().getString(R.string.pip_notification_unknown_title);
- if (mNotified) {
- // Update the notification.
- update();
- }
+ updateNotificationContent();
}
- private void update() {
- mNotified = true;
+ void updateExpansionState() {
+ updateNotificationContent();
+ }
+
+ private void updateNotificationContent() {
+ if (mPackageManager == null || !mIsNotificationShown) {
+ return;
+ }
+
+ Notification.Action[] actions = getNotificationActions();
+ ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
+ "%s: update(), title: %s, subtitle: %s, mediaSessionToken: %s, #actions: %s", TAG,
+ getNotificationTitle(), mPipSubtitle, mMediaSessionToken, actions.length);
+ for (Notification.Action action : actions) {
+ ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE, "%s: action: %s", TAG,
+ action.toString());
+ }
+
mNotificationBuilder
.setWhen(System.currentTimeMillis())
- .setContentTitle(getNotificationTitle());
- if (mArt != null) {
- mNotificationBuilder.setStyle(new Notification.BigPictureStyle()
- .bigPicture(mArt));
- } else {
- mNotificationBuilder.setStyle(null);
- }
+ .setContentTitle(getNotificationTitle())
+ .setContentText(mPipSubtitle)
+ .setSubText(getApplicationLabel(mPackageName))
+ .setActions(actions);
+ setPipIcon();
+
+ Bundle extras = new Bundle();
+ extras.putParcelable(Notification.EXTRA_MEDIA_SESSION, mMediaSessionToken);
+ mNotificationBuilder.setExtras(extras);
+
+ // TvExtender not recognized if not set last.
+ mNotificationBuilder.extend(new Notification.TvExtender()
+ .setContentIntent(createPendingIntent(mContext, ACTION_SHOW_PIP_MENU))
+ .setDeleteIntent(createPendingIntent(mContext, ACTION_CLOSE_PIP)));
mNotificationManager.notify(NOTIFICATION_TAG, SystemMessage.NOTE_TV_PIP,
mNotificationBuilder.build());
}
- private boolean updateMediaControllerMetadata(MediaMetadata metadata) {
- String title = null;
- Bitmap art = null;
- if (metadata != null) {
- title = metadata.getString(MediaMetadata.METADATA_KEY_DISPLAY_TITLE);
- if (TextUtils.isEmpty(title)) {
- title = metadata.getString(MediaMetadata.METADATA_KEY_TITLE);
- }
- art = metadata.getBitmap(MediaMetadata.METADATA_KEY_ALBUM_ART);
- if (art == null) {
- art = metadata.getBitmap(MediaMetadata.METADATA_KEY_ART);
- }
- }
-
- if (TextUtils.equals(title, mMediaTitle) && Objects.equals(art, mArt)) {
- return false;
- }
-
- mMediaTitle = title;
- mArt = art;
-
- return true;
- }
-
-
private String getNotificationTitle() {
- if (!TextUtils.isEmpty(mMediaTitle)) {
- return mMediaTitle;
+ if (!TextUtils.isEmpty(mPipTitle)) {
+ return mPipTitle;
}
-
final String applicationTitle = getApplicationLabel(mPackageName);
if (!TextUtils.isEmpty(applicationTitle)) {
return applicationTitle;
}
-
return mDefaultTitle;
}
@@ -214,10 +362,37 @@
}
}
+ private void setPipIcon() {
+ if (mActivityIcon != null) {
+ mNotificationBuilder.setLargeIcon(mActivityIcon);
+ return;
+ }
+ // Fallback: Picture-in-Picture icon
+ mNotificationBuilder.setLargeIcon(Icon.createWithResource(mContext, R.drawable.pip_icon));
+ }
+
+ private Bitmap getActivityIcon() {
+ if (mContext == null) return null;
+ ComponentName componentName = PipUtils.getTopPipActivity(mContext).first;
+ if (componentName == null) return null;
+
+ Drawable drawable;
+ try {
+ drawable = mPackageManager.getActivityIcon(componentName);
+ } catch (PackageManager.NameNotFoundException e) {
+ return null;
+ }
+ int width = mContext.getResources().getDimensionPixelSize(
+ android.R.dimen.notification_large_icon_width);
+ int height = mContext.getResources().getDimensionPixelSize(
+ android.R.dimen.notification_large_icon_height);
+ return ImageUtils.buildScaledBitmap(drawable, width, height, /* allowUpscaling */ true);
+ }
+
private static PendingIntent createPendingIntent(Context context, String action) {
return PendingIntent.getBroadcast(context, 0,
new Intent(action).setPackage(context.getPackageName()),
- PendingIntent.FLAG_CANCEL_CURRENT | PendingIntent.FLAG_IMMUTABLE);
+ PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE);
}
private class ActionBroadcastReceiver extends BroadcastReceiver {
@@ -228,6 +403,7 @@
mIntentFilter.addAction(ACTION_SHOW_PIP_MENU);
mIntentFilter.addAction(ACTION_MOVE_PIP);
mIntentFilter.addAction(ACTION_TOGGLE_EXPANDED_PIP);
+ mIntentFilter.addAction(ACTION_FULLSCREEN);
}
boolean mRegistered = false;
@@ -249,10 +425,8 @@
@Override
public void onReceive(Context context, Intent intent) {
final String action = intent.getAction();
- if (DEBUG) {
- ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
- "%s: on(Broadcast)Receive(), action=%s", TAG, action);
- }
+ ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
+ "%s: on(Broadcast)Receive(), action=%s", TAG, action);
if (ACTION_SHOW_PIP_MENU.equals(action)) {
mDelegate.showPictureInPictureMenu();
@@ -262,14 +436,21 @@
mDelegate.enterPipMovementMenu();
} else if (ACTION_TOGGLE_EXPANDED_PIP.equals(action)) {
mDelegate.togglePipExpansion();
+ } else if (ACTION_FULLSCREEN.equals(action)) {
+ mDelegate.movePipToFullscreen();
}
}
}
interface Delegate {
void showPictureInPictureMenu();
+
void closePip();
+
void enterPipMovementMenu();
+
void togglePipExpansion();
+
+ void movePipToFullscreen();
}
}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/BackAnimationControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/BackAnimationControllerTest.java
index 6cf8829..42b1014 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/BackAnimationControllerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/BackAnimationControllerTest.java
@@ -209,12 +209,11 @@
createNavigationInfo(animationTarget, null, null,
BackNavigationInfo.TYPE_RETURN_TO_HOME, null);
- // Check that back start is dispatched.
doMotionEvent(MotionEvent.ACTION_DOWN, 0);
- verify(mIOnBackInvokedCallback).onBackStarted();
- // Check that back progress is dispatched.
+ // Check that back start and progress is dispatched when first move.
doMotionEvent(MotionEvent.ACTION_MOVE, 100);
+ verify(mIOnBackInvokedCallback).onBackStarted();
ArgumentCaptor<BackEvent> backEventCaptor = ArgumentCaptor.forClass(BackEvent.class);
verify(mIOnBackInvokedCallback).onBackProgressed(backEventCaptor.capture());
assertEquals(animationTarget, backEventCaptor.getValue().getDepartingAnimationTarget());
diff --git a/media/java/android/media/ImageReader.java b/media/java/android/media/ImageReader.java
index 70d6810..472586b 100644
--- a/media/java/android/media/ImageReader.java
+++ b/media/java/android/media/ImageReader.java
@@ -643,6 +643,9 @@
/**
* <p>Return the frame to the ImageReader for reuse.</p>
+ *
+ * This method should only be called via {@link SurfaceImage#close} which ensures that image
+ * closing is atomic.
*/
private void releaseImage(Image i) {
if (! (i instanceof SurfaceImage) ) {
@@ -1125,6 +1128,8 @@
}
private class SurfaceImage extends android.media.Image {
+ private final Object mCloseLock = new Object();
+
public SurfaceImage(int format) {
mFormat = format;
mHardwareBufferFormat = ImageReader.this.mHardwareBufferFormat;
@@ -1139,7 +1144,9 @@
@Override
public void close() {
- ImageReader.this.releaseImage(this);
+ synchronized (this.mCloseLock) {
+ ImageReader.this.releaseImage(this);
+ }
}
public ImageReader getReader() {
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsDialogMeasureAdapter.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsDialogMeasureAdapter.java
index 2f09792..8de7213 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsDialogMeasureAdapter.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsDialogMeasureAdapter.java
@@ -154,7 +154,9 @@
//re-calculate the height of description
View description = mView.findViewById(R.id.description);
- totalHeight += measureDescription(description, displayHeight, width, totalHeight);
+ if (description != null && description.getVisibility() != View.GONE) {
+ totalHeight += measureDescription(description, displayHeight, width, totalHeight);
+ }
return new AuthDialog.LayoutParams(width, totalHeight);
}
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java
index 3bb4e64..3c373f4 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java
@@ -70,6 +70,7 @@
import android.graphics.PixelFormat;
import android.graphics.Rect;
import android.graphics.RectF;
+import android.graphics.Region;
import android.os.Binder;
import android.os.Bundle;
import android.os.Handler;
@@ -91,6 +92,8 @@
import android.view.Surface;
import android.view.View;
import android.view.ViewTreeObserver;
+import android.view.ViewTreeObserver.InternalInsetsInfo;
+import android.view.ViewTreeObserver.OnComputeInternalInsetsListener;
import android.view.WindowInsetsController.Appearance;
import android.view.WindowInsetsController.Behavior;
import android.view.WindowManager;
@@ -121,6 +124,7 @@
import com.android.systemui.navigationbar.buttons.DeadZone;
import com.android.systemui.navigationbar.buttons.KeyButtonView;
import com.android.systemui.navigationbar.buttons.RotationContextButton;
+import com.android.systemui.navigationbar.gestural.EdgeBackGestureHandler;
import com.android.systemui.navigationbar.gestural.QuickswitchOrientedNavHandle;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.recents.OverviewProxyService;
@@ -153,6 +157,7 @@
import java.io.PrintWriter;
import java.util.Locale;
+import java.util.Map;
import java.util.Optional;
import java.util.function.Consumer;
@@ -199,12 +204,14 @@
private final Optional<Recents> mRecentsOptional;
private final DeviceConfigProxy mDeviceConfigProxy;
private final NavigationBarTransitions mNavigationBarTransitions;
+ private final EdgeBackGestureHandler mEdgeBackGestureHandler;
private final Optional<BackAnimation> mBackAnimation;
private final Handler mHandler;
private final UiEventLogger mUiEventLogger;
private final NavBarHelper mNavBarHelper;
private final NotificationShadeDepthController mNotificationShadeDepthController;
private final UserContextProvider mUserContextProvider;
+ private final OnComputeInternalInsetsListener mOnComputeInternalInsetsListener;
private NavigationBarFrame mFrame;
private @WindowVisibleState int mNavigationBarWindowState = WINDOW_STATE_SHOWING;
@@ -516,6 +523,7 @@
DeadZone deadZone,
DeviceConfigProxy deviceConfigProxy,
NavigationBarTransitions navigationBarTransitions,
+ EdgeBackGestureHandler edgeBackGestureHandler,
Optional<BackAnimation> backAnimation,
UserContextProvider userContextProvider) {
super(navigationBarView);
@@ -542,6 +550,7 @@
mDeadZone = deadZone;
mDeviceConfigProxy = deviceConfigProxy;
mNavigationBarTransitions = navigationBarTransitions;
+ mEdgeBackGestureHandler = edgeBackGestureHandler;
mBackAnimation = backAnimation;
mHandler = mainHandler;
mUiEventLogger = uiEventLogger;
@@ -555,6 +564,29 @@
mInputMethodManager = inputMethodManager;
mUserContextProvider = userContextProvider;
+ mOnComputeInternalInsetsListener = info -> {
+ // When the nav bar is in 2-button or 3-button mode, or when IME is visible in fully
+ // gestural mode, the entire nav bar should be touchable.
+ if (!mEdgeBackGestureHandler.isHandlingGestures()) {
+ // We're in 2/3 button mode OR back button force-shown in SUW
+ if (!mImeVisible) {
+ // IME not showing, take all touches
+ info.setTouchableInsets(InternalInsetsInfo.TOUCHABLE_INSETS_FRAME);
+ }
+ if (!mView.isImeRenderingNavButtons()) {
+ // IME showing but not drawing any buttons, take all touches
+ info.setTouchableInsets(InternalInsetsInfo.TOUCHABLE_INSETS_FRAME);
+ }
+ }
+
+ // When in gestural and the IME is showing, don't use the nearest region since it will
+ // take gesture space away from the IME
+ info.setTouchableInsets(InternalInsetsInfo.TOUCHABLE_INSETS_REGION);
+ info.touchableRegion.set(getButtonLocations(false /* includeFloatingButtons */,
+ false /* inScreen */, false /* useNearestRegion */));
+ };
+
+ mView.setEdgeBackGestureHandler(mEdgeBackGestureHandler);
mNavBarMode = mNavigationModeController.addListener(mModeChangedListener);
}
@@ -569,6 +601,7 @@
mView.setBarTransitions(mNavigationBarTransitions);
mView.setTouchHandler(mTouchHandler);
mView.setNavBarMode(mNavBarMode);
+ mEdgeBackGestureHandler.setStateChangeCallback(mView::updateStates);
mView.updateRotationButton();
mView.setVisibility(
@@ -646,11 +679,14 @@
mView.setNavBarMode(mNavBarMode);
mView.setUpdateActiveTouchRegionsCallback(
() -> mOverviewProxyService.onActiveNavBarRegionChanges(
- mView.getButtonLocations(
+ getButtonLocations(
true /* includeFloatingButtons */,
true /* inScreen */,
true /* useNearestRegion */)));
+ mView.getViewTreeObserver().addOnComputeInternalInsetsListener(
+ mOnComputeInternalInsetsListener);
+
mNavBarHelper.registerNavTaskStateUpdater(mNavbarTaskbarStateUpdater);
mPipOptional.ifPresent(mView::addPipExclusionBoundsChangeListener);
@@ -721,6 +757,8 @@
mOrientationHandle.getViewTreeObserver().removeOnGlobalLayoutListener(
mOrientationHandleGlobalLayoutListener);
}
+ mView.getViewTreeObserver().removeOnComputeInternalInsetsListener(
+ mOnComputeInternalInsetsListener);
mHandler.removeCallbacks(mAutoDim);
mHandler.removeCallbacks(mOnVariableDurationHomeLongClick);
mHandler.removeCallbacks(mEnableLayoutTransitions);
@@ -1044,8 +1082,8 @@
}
private void handleTransientChanged() {
- mView.onTransientStateChanged(mTransientShown,
- mTransientShownFromGestureOnSystemBar);
+ mEdgeBackGestureHandler.onNavBarTransientStateChanged(mTransientShown);
+
final int transitionMode = transitionMode(mTransientShown, mAppearance);
if (updateTransitionMode(transitionMode) && mLightBarController != null) {
mLightBarController.onNavigationBarModeChanged(transitionMode);
@@ -1639,6 +1677,79 @@
mNavigationIconHints = hints;
}
+ /**
+ * @param includeFloatingButtons Whether to include the floating rotation and overlay button in
+ * the region for all the buttons
+ * @param inScreenSpace Whether to return values in screen space or window space
+ * @param useNearestRegion Whether to use the nearest region instead of the actual button bounds
+ * @return
+ */
+ Region getButtonLocations(boolean includeFloatingButtons, boolean inScreenSpace,
+ boolean useNearestRegion) {
+ if (useNearestRegion && !inScreenSpace) {
+ // We currently don't support getting the nearest region in anything but screen space
+ useNearestRegion = false;
+ }
+ Region region = new Region();
+ Map<View, Rect> touchRegionCache = mView.getButtonTouchRegionCache();
+ updateButtonLocation(
+ region, touchRegionCache, mView.getBackButton(), inScreenSpace, useNearestRegion);
+ updateButtonLocation(
+ region, touchRegionCache, mView.getHomeButton(), inScreenSpace, useNearestRegion);
+ updateButtonLocation(region, touchRegionCache, mView.getRecentsButton(), inScreenSpace,
+ useNearestRegion);
+ updateButtonLocation(region, touchRegionCache, mView.getImeSwitchButton(), inScreenSpace,
+ useNearestRegion);
+ updateButtonLocation(
+ region, touchRegionCache, mView.getAccessibilityButton(), inScreenSpace,
+ useNearestRegion);
+ if (includeFloatingButtons && mView.getFloatingRotationButton().isVisible()) {
+ // Note: this button is floating so the nearest region doesn't apply
+ updateButtonLocation(
+ region, mView.getFloatingRotationButton().getCurrentView(), inScreenSpace);
+ } else {
+ updateButtonLocation(region, touchRegionCache, mView.getRotateSuggestionButton(),
+ inScreenSpace, useNearestRegion);
+ }
+ return region;
+ }
+
+ private void updateButtonLocation(
+ Region region,
+ Map<View, Rect> touchRegionCache,
+ ButtonDispatcher button,
+ boolean inScreenSpace,
+ boolean useNearestRegion) {
+ if (button == null) {
+ return;
+ }
+ View view = button.getCurrentView();
+ if (view == null || !button.isVisible()) {
+ return;
+ }
+ // If the button is tappable from perspective of NearestTouchFrame, then we'll
+ // include the regions where the tap is valid instead of just the button layout location
+ if (useNearestRegion && touchRegionCache.containsKey(view)) {
+ region.op(touchRegionCache.get(view), Region.Op.UNION);
+ return;
+ }
+ updateButtonLocation(region, view, inScreenSpace);
+ }
+
+ private void updateButtonLocation(Region region, View view, boolean inScreenSpace) {
+ Rect bounds = new Rect();
+ if (inScreenSpace) {
+ view.getBoundsOnScreen(bounds);
+ } else {
+ int[] location = new int[2];
+ view.getLocationInWindow(location);
+ bounds.set(location[0], location[1],
+ location[0] + view.getWidth(),
+ location[1] + view.getHeight());
+ }
+ region.op(bounds, Region.Op.UNION);
+ }
+
private final ModeChangedListener mModeChangedListener = new ModeChangedListener() {
@Override
public void onNavigationModeChanged(int mode) {
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarModule.java b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarModule.java
index 93bf136..f6bfd6c 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarModule.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarModule.java
@@ -24,6 +24,7 @@
import com.android.systemui.R;
import com.android.systemui.dagger.qualifiers.DisplayId;
import com.android.systemui.navigationbar.NavigationBarComponent.NavigationBarScope;
+import com.android.systemui.navigationbar.gestural.EdgeBackGestureHandler;
import dagger.Module;
import dagger.Provides;
@@ -55,6 +56,14 @@
return barView.findViewById(R.id.navigation_bar_view);
}
+ /** */
+ @Provides
+ @NavigationBarScope
+ static EdgeBackGestureHandler provideEdgeBackGestureHandler(
+ EdgeBackGestureHandler.Factory factory, @DisplayId Context context) {
+ return factory.create(context);
+ }
+
/** A WindowManager specific to the display's context. */
@Provides
@NavigationBarScope
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarView.java b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarView.java
index 6e00ebc..a13c199 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarView.java
@@ -41,8 +41,6 @@
import android.graphics.Canvas;
import android.graphics.Point;
import android.graphics.Rect;
-import android.graphics.Region;
-import android.graphics.Region.Op;
import android.os.Bundle;
import android.util.AttributeSet;
import android.util.Log;
@@ -53,8 +51,6 @@
import android.view.Surface;
import android.view.View;
import android.view.ViewGroup;
-import android.view.ViewTreeObserver.InternalInsetsInfo;
-import android.view.ViewTreeObserver.OnComputeInternalInsetsListener;
import android.view.WindowInsets;
import android.view.WindowInsetsController.Behavior;
import android.view.WindowManager;
@@ -93,7 +89,6 @@
import com.android.wm.shell.pip.Pip;
import java.io.PrintWriter;
-import java.util.HashMap;
import java.util.Map;
import java.util.Optional;
import java.util.concurrent.Executor;
@@ -123,11 +118,6 @@
private int mNavBarMode;
private boolean mImeDrawsImeNavBar;
- private final Region mTmpRegion = new Region();
- private final int[] mTmpPosition = new int[2];
- private Rect mTmpBounds = new Rect();
- private Map<View, Rect> mButtonFullTouchableRegions = new HashMap<>();
-
private KeyButtonDrawable mBackIcon;
private KeyButtonDrawable mHomeDefaultIcon;
private KeyButtonDrawable mRecentIcon;
@@ -138,7 +128,6 @@
private EdgeBackGestureHandler mEdgeBackGestureHandler;
private final DeadZone mDeadZone;
- private boolean mDeadZoneConsuming = false;
private NavigationBarTransitions mBarTransitions;
@Nullable
private AutoHideController mAutoHideController;
@@ -152,7 +141,6 @@
private boolean mUseCarModeUi = false;
private boolean mInCarMode = false;
private boolean mDockedStackExists;
- private boolean mImeVisible;
private boolean mScreenOn = true;
private final SparseArray<ButtonDispatcher> mButtonDispatchers = new SparseArray<>();
@@ -272,31 +260,6 @@
}
};
- private final OnComputeInternalInsetsListener mOnComputeInternalInsetsListener = info -> {
- // When the nav bar is in 2-button or 3-button mode, or when the back button is force-shown
- // while in gesture nav in SUW, the entire nav bar should be touchable.
- if (!mEdgeBackGestureHandler.isHandlingGestures()) {
- // We're in 2/3 button mode OR back button force-shown in SUW
- if (!mImeVisible) {
- // IME not showing, take all touches
- info.setTouchableInsets(InternalInsetsInfo.TOUCHABLE_INSETS_FRAME);
- return;
- }
-
- if (!isImeRenderingNavButtons()) {
- // IME showing but not drawing any buttons, take all touches
- info.setTouchableInsets(InternalInsetsInfo.TOUCHABLE_INSETS_FRAME);
- return;
- }
- }
-
- // When in gestural and the IME is showing, don't use the nearest region since it will take
- // gesture space away from the IME
- info.setTouchableInsets(InternalInsetsInfo.TOUCHABLE_INSETS_REGION);
- info.touchableRegion.set(getButtonLocations(false /* includeFloatingButtons */,
- false /* inScreen */, false /* useNearestRegion */));
- };
-
private final RotationButtonUpdatesCallback mRotationButtonListener =
new RotationButtonUpdatesCallback() {
@Override
@@ -315,13 +278,6 @@
}
};
- private final Consumer<Boolean> mNavbarOverlayVisibilityChangeCallback = (visible) -> {
- if (visible && mAutoHideController != null) {
- mAutoHideController.touchAutoHide();
- }
- notifyActiveTouchRegions();
- };
-
public NavigationBarView(Context context, AttributeSet attrs) {
super(context, attrs);
@@ -380,9 +336,6 @@
mNavColorSampleMargin = getResources()
.getDimensionPixelSize(R.dimen.navigation_handle_sample_horizontal_margin);
- mEdgeBackGestureHandler = Dependency.get(EdgeBackGestureHandler.Factory.class)
- .create(mContext);
- mEdgeBackGestureHandler.setStateChangeCallback(this::updateStates);
Executor backgroundExecutor = Dependency.get(Dependency.BACKGROUND_EXECUTOR);
mRegionSamplingHelper = new RegionSamplingHelper(this,
new RegionSamplingHelper.SamplingCallback() {
@@ -409,6 +362,10 @@
}, backgroundExecutor);
}
+ public void setEdgeBackGestureHandler(EdgeBackGestureHandler edgeBackGestureHandler) {
+ mEdgeBackGestureHandler = edgeBackGestureHandler;
+ }
+
void setBarTransitions(NavigationBarTransitions navigationBarTransitions) {
mBarTransitions = navigationBarTransitions;
}
@@ -681,8 +638,7 @@
if (!visible) {
mTransitionListener.onBackAltCleared();
}
- mImeVisible = visible;
- mRotationButtonController.getRotationButton().setCanShowRotationButton(!mImeVisible);
+ mRotationButtonController.getRotationButton().setCanShowRotationButton(!visible);
}
void setDisabledFlags(int disabledFlags, SysUiState sysUiState) {
@@ -774,7 +730,7 @@
/**
* Returns whether the IME is currently visible and drawing the nav buttons.
*/
- private boolean isImeRenderingNavButtons() {
+ boolean isImeRenderingNavButtons() {
return mImeDrawsImeNavBar
&& mImeCanRenderGesturalNavButtons
&& (mNavigationIconHints & StatusBarManager.NAVIGATION_HINT_IME_SHOWN) != 0;
@@ -1003,75 +959,14 @@
notifyActiveTouchRegions();
}
- private void updateButtonTouchRegionCache() {
+ Map<View, Rect> getButtonTouchRegionCache() {
FrameLayout navBarLayout = mIsVertical
? mNavigationInflaterView.mVertical
: mNavigationInflaterView.mHorizontal;
- mButtonFullTouchableRegions = ((NearestTouchFrame) navBarLayout
+ return ((NearestTouchFrame) navBarLayout
.findViewById(R.id.nav_buttons)).getFullTouchableChildRegions();
}
- /**
- * @param includeFloatingButtons Whether to include the floating rotation and overlay button in
- * the region for all the buttons
- * @param inScreenSpace Whether to return values in screen space or window space
- * @param useNearestRegion Whether to use the nearest region instead of the actual button bounds
- * @return
- */
- Region getButtonLocations(boolean includeFloatingButtons, boolean inScreenSpace,
- boolean useNearestRegion) {
- // TODO: move this method to NavigationBar.
- // TODO: don't use member variables for temp storage like mTmpRegion.
- if (useNearestRegion && !inScreenSpace) {
- // We currently don't support getting the nearest region in anything but screen space
- useNearestRegion = false;
- }
- mTmpRegion.setEmpty();
- updateButtonTouchRegionCache();
- updateButtonLocation(getBackButton(), inScreenSpace, useNearestRegion);
- updateButtonLocation(getHomeButton(), inScreenSpace, useNearestRegion);
- updateButtonLocation(getRecentsButton(), inScreenSpace, useNearestRegion);
- updateButtonLocation(getImeSwitchButton(), inScreenSpace, useNearestRegion);
- updateButtonLocation(getAccessibilityButton(), inScreenSpace, useNearestRegion);
- if (includeFloatingButtons && mFloatingRotationButton.isVisible()) {
- // Note: this button is floating so the nearest region doesn't apply
- updateButtonLocation(mFloatingRotationButton.getCurrentView(), inScreenSpace);
- } else {
- updateButtonLocation(getRotateSuggestionButton(), inScreenSpace, useNearestRegion);
- }
- return mTmpRegion;
- }
-
- private void updateButtonLocation(ButtonDispatcher button, boolean inScreenSpace,
- boolean useNearestRegion) {
- if (button == null) {
- return;
- }
- View view = button.getCurrentView();
- if (view == null || !button.isVisible()) {
- return;
- }
- // If the button is tappable from perspective of NearestTouchFrame, then we'll
- // include the regions where the tap is valid instead of just the button layout location
- if (useNearestRegion && mButtonFullTouchableRegions.containsKey(view)) {
- mTmpRegion.op(mButtonFullTouchableRegions.get(view), Op.UNION);
- return;
- }
- updateButtonLocation(view, inScreenSpace);
- }
-
- private void updateButtonLocation(View view, boolean inScreenSpace) {
- if (inScreenSpace) {
- view.getBoundsOnScreen(mTmpBounds);
- } else {
- view.getLocationInWindow(mTmpPosition);
- mTmpBounds.set(mTmpPosition[0], mTmpPosition[1],
- mTmpPosition[0] + view.getWidth(),
- mTmpPosition[1] + view.getHeight());
- }
- mTmpRegion.op(mTmpBounds, Op.UNION);
- }
-
private void updateOrientationViews() {
mHorizontal = findViewById(R.id.horizontal);
mVertical = findViewById(R.id.vertical);
@@ -1272,7 +1167,6 @@
mRotationButtonController.registerListeners();
}
- getViewTreeObserver().addOnComputeInternalInsetsListener(mOnComputeInternalInsetsListener);
updateNavButtonIcons();
}
@@ -1288,8 +1182,6 @@
}
mEdgeBackGestureHandler.onNavBarDetached();
- getViewTreeObserver().removeOnComputeInternalInsetsListener(
- mOnComputeInternalInsetsListener);
}
public void dump(PrintWriter pw) {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarTest.java b/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarTest.java
index 7c30398..a526087 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarTest.java
@@ -60,6 +60,7 @@
import android.view.DisplayInfo;
import android.view.MotionEvent;
import android.view.View;
+import android.view.ViewTreeObserver;
import android.view.WindowInsets;
import android.view.WindowManager;
import android.view.WindowMetrics;
@@ -164,7 +165,7 @@
@Mock
private UiEventLogger mUiEventLogger;
@Mock
- EdgeBackGestureHandler.Factory mEdgeBackGestureHandlerFactory;
+ private ViewTreeObserver mViewTreeObserver;
@Mock
EdgeBackGestureHandler mEdgeBackGestureHandler;
NavBarHelper mNavBarHelper;
@@ -199,8 +200,6 @@
public void setup() throws Exception {
MockitoAnnotations.initMocks(this);
- when(mEdgeBackGestureHandlerFactory.create(any(Context.class)))
- .thenReturn(mEdgeBackGestureHandler);
when(mLightBarcontrollerFactory.create(any(Context.class))).thenReturn(mLightBarController);
when(mAutoHideControllerFactory.create(any(Context.class))).thenReturn(mAutoHideController);
when(mNavigationBarView.getHomeButton()).thenReturn(mHomeButton);
@@ -213,6 +212,7 @@
when(mNavigationBarTransitions.getLightTransitionsController())
.thenReturn(mLightBarTransitionsController);
when(mStatusBarKeyguardViewManager.isNavBarVisible()).thenReturn(true);
+ when(mNavigationBarView.getViewTreeObserver()).thenReturn(mViewTreeObserver);
when(mUserContextProvider.createCurrentUserContext(any(Context.class)))
.thenReturn(mContext);
setupSysuiDependency();
@@ -222,8 +222,6 @@
mDependency.injectMockDependency(KeyguardStateController.class);
mDependency.injectTestDependency(StatusBarStateController.class, mStatusBarStateController);
mDependency.injectMockDependency(NavigationBarController.class);
- mDependency.injectTestDependency(EdgeBackGestureHandler.Factory.class,
- mEdgeBackGestureHandlerFactory);
mDependency.injectTestDependency(OverviewProxyService.class, mOverviewProxyService);
mDependency.injectTestDependency(NavigationModeController.class, mNavigationModeController);
TestableLooper.get(this).runWithLooper(() -> {
@@ -463,6 +461,7 @@
mDeadZone,
mDeviceConfigProxyFake,
mNavigationBarTransitions,
+ mEdgeBackGestureHandler,
Optional.of(mock(BackAnimation.class)),
mUserContextProvider));
}
diff --git a/services/autofill/java/com/android/server/autofill/Session.java b/services/autofill/java/com/android/server/autofill/Session.java
index f18d13d..8e32a7a 100644
--- a/services/autofill/java/com/android/server/autofill/Session.java
+++ b/services/autofill/java/com/android/server/autofill/Session.java
@@ -1559,9 +1559,18 @@
Slog.e(TAG, "Error sending input show up notification", e);
}
}
+ }
+
+ // AutoFillUiCallback
+ @Override
+ public void requestFallbackFromFillDialog() {
+ setFillDialogDisabled();
synchronized (mLock) {
- // stop to show fill dialog
- mSessionFlags.mFillDialogDisabled = true;
+ if (mCurrentViewId == null) {
+ return;
+ }
+ final ViewState currentView = mViewStates.get(mCurrentViewId);
+ currentView.maybeCallOnFillReady(mFlags);
}
}
@@ -3208,17 +3217,20 @@
return;
}
- if (requestShowFillDialog(response, filledId, filterText, flags)) {
- synchronized (mLock) {
- final ViewState currentView = mViewStates.get(mCurrentViewId);
- currentView.setState(ViewState.STATE_FILL_DIALOG_SHOWN);
- mService.logDatasetShown(id, mClientState, UI_TYPE_DIALOG);
+ final AutofillId[] ids = response.getFillDialogTriggerIds();
+ if (ids != null && ArrayUtils.contains(ids, filledId)) {
+ if (requestShowFillDialog(response, filledId, filterText, flags)) {
+ synchronized (mLock) {
+ final ViewState currentView = mViewStates.get(mCurrentViewId);
+ currentView.setState(ViewState.STATE_FILL_DIALOG_SHOWN);
+ mService.logDatasetShown(id, mClientState, UI_TYPE_DIALOG);
+ }
+ return;
+ } else {
+ setFillDialogDisabled();
}
- return;
}
- setFillDialogDisabled();
-
if (response.supportsInlineSuggestions()) {
synchronized (mLock) {
if (requestShowInlineSuggestionsLocked(response, filterText)) {
@@ -3324,15 +3336,11 @@
return false;
}
- final AutofillId[] ids = response.getFillDialogTriggerIds();
- if (ids == null || !ArrayUtils.contains(ids, filledId)) {
- return false;
- }
-
final Drawable serviceIcon = getServiceIcon();
getUiForShowing().showFillDialog(filledId, response, filterText,
- mService.getServicePackageName(), mComponentName, serviceIcon, this);
+ mService.getServicePackageName(), mComponentName, serviceIcon, this,
+ id, mCompatMode);
return true;
}
diff --git a/services/autofill/java/com/android/server/autofill/ui/AutoFillUI.java b/services/autofill/java/com/android/server/autofill/ui/AutoFillUI.java
index 57768ef..3ab873d 100644
--- a/services/autofill/java/com/android/server/autofill/ui/AutoFillUI.java
+++ b/services/autofill/java/com/android/server/autofill/ui/AutoFillUI.java
@@ -97,6 +97,7 @@
void dispatchUnhandledKey(AutofillId id, KeyEvent keyEvent);
void cancelSession();
void requestShowSoftInput(AutofillId id);
+ void requestFallbackFromFillDialog();
}
public AutoFillUI(@NonNull Context context) {
@@ -388,13 +389,19 @@
public void showFillDialog(@NonNull AutofillId focusedId, @NonNull FillResponse response,
@Nullable String filterText, @Nullable String servicePackageName,
@NonNull ComponentName componentName, @Nullable Drawable serviceIcon,
- @NonNull AutoFillUiCallback callback) {
+ @NonNull AutoFillUiCallback callback, int sessionId, boolean compatMode) {
if (sVerbose) {
Slog.v(TAG, "showFillDialog for "
+ componentName.toShortString() + ": " + response);
}
- // TODO: enable LogMaker
+ final LogMaker log = Helper
+ .newLogMaker(MetricsEvent.AUTOFILL_FILL_UI, componentName, servicePackageName,
+ sessionId, compatMode)
+ .addTaggedData(MetricsEvent.FIELD_AUTOFILL_FILTERTEXT_LEN,
+ filterText == null ? 0 : filterText.length())
+ .addTaggedData(MetricsEvent.FIELD_AUTOFILL_NUM_DATASETS,
+ response.getDatasets() == null ? 0 : response.getDatasets().size());
mHandler.post(() -> {
if (callback != mCallback) {
@@ -406,6 +413,7 @@
mUiModeMgr.isNightMode(), new DialogFillUi.UiCallback() {
@Override
public void onResponsePicked(FillResponse response) {
+ log(MetricsEvent.TYPE_DETAIL);
hideFillDialogUiThread(callback);
if (mCallback != null) {
mCallback.authenticate(response.getRequestId(),
@@ -417,6 +425,7 @@
@Override
public void onDatasetPicked(Dataset dataset) {
+ log(MetricsEvent.TYPE_ACTION);
hideFillDialogUiThread(callback);
if (mCallback != null) {
final int datasetIndex = response.getDatasets().indexOf(dataset);
@@ -426,15 +435,29 @@
}
@Override
- public void onCanceled() {
+ public void onDismissed() {
+ log(MetricsEvent.TYPE_DISMISS);
hideFillDialogUiThread(callback);
callback.requestShowSoftInput(focusedId);
}
@Override
+ public void onCanceled() {
+ log(MetricsEvent.TYPE_CLOSE);
+ hideFillDialogUiThread(callback);
+ callback.requestShowSoftInput(focusedId);
+ callback.requestFallbackFromFillDialog();
+ }
+
+ @Override
public void startIntentSender(IntentSender intentSender) {
mCallback.startIntentSenderAndFinishSession(intentSender);
}
+
+ private void log(int type) {
+ log.setType(type);
+ mMetricsLogger.write(log);
+ }
});
});
}
diff --git a/services/autofill/java/com/android/server/autofill/ui/DialogFillUi.java b/services/autofill/java/com/android/server/autofill/ui/DialogFillUi.java
index f9f5289..5a1a1ae 100644
--- a/services/autofill/java/com/android/server/autofill/ui/DialogFillUi.java
+++ b/services/autofill/java/com/android/server/autofill/ui/DialogFillUi.java
@@ -81,6 +81,7 @@
interface UiCallback {
void onResponsePicked(@NonNull FillResponse response);
void onDatasetPicked(@NonNull Dataset dataset);
+ void onDismissed();
void onCanceled();
void startIntentSender(IntentSender intentSender);
}
@@ -144,6 +145,7 @@
mDialog = new Dialog(mContext, mThemeId);
mDialog.setContentView(decor);
setDialogParamsAsBottomSheet();
+ mDialog.setOnCancelListener((d) -> mCallback.onCanceled());
show();
}
@@ -220,7 +222,7 @@
final TextView noButton = decor.findViewById(R.id.autofill_dialog_no);
// set "No thinks" by default
noButton.setText(R.string.autofill_save_no);
- noButton.setOnClickListener((v) -> mCallback.onCanceled());
+ noButton.setOnClickListener((v) -> mCallback.onDismissed());
}
private void setContinueButton(View decor, View.OnClickListener listener) {
diff --git a/services/core/java/com/android/server/am/AppBatteryTracker.java b/services/core/java/com/android/server/am/AppBatteryTracker.java
index 6e28d8f..5a234f5 100644
--- a/services/core/java/com/android/server/am/AppBatteryTracker.java
+++ b/services/core/java/com/android/server/am/AppBatteryTracker.java
@@ -56,7 +56,6 @@
import android.os.BatteryStatsInternal;
import android.os.BatteryUsageStats;
import android.os.BatteryUsageStatsQuery;
-import android.os.Build;
import android.os.PowerExemptionManager;
import android.os.PowerExemptionManager.ReasonCode;
import android.os.SystemClock;
@@ -97,7 +96,7 @@
static final boolean DEBUG_BACKGROUND_BATTERY_TRACKER = false;
static final boolean DEBUG_BACKGROUND_BATTERY_TRACKER_VERBOSE =
- DEBUG_BACKGROUND_BATTERY_TRACKER | Build.IS_DEBUGGABLE;
+ DEBUG_BACKGROUND_BATTERY_TRACKER | false;
// As we don't support realtime per-UID battery usage stats yet, we're polling the stats
// in a regular time basis.
diff --git a/services/core/java/com/android/server/pm/Settings.java b/services/core/java/com/android/server/pm/Settings.java
index ba4d09f..6400502 100644
--- a/services/core/java/com/android/server/pm/Settings.java
+++ b/services/core/java/com/android/server/pm/Settings.java
@@ -4579,7 +4579,7 @@
pw.print(" (override=true)");
}
pw.println();
- if (ps.getPkg().getQueriesPackages().isEmpty()) {
+ if (!ps.getPkg().getQueriesPackages().isEmpty()) {
pw.append(prefix).append(" queriesPackages=")
.println(ps.getPkg().getQueriesPackages());
}
diff --git a/services/core/java/com/android/server/trust/TrustAgentWrapper.java b/services/core/java/com/android/server/trust/TrustAgentWrapper.java
index d374814..4b8c7c1 100644
--- a/services/core/java/com/android/server/trust/TrustAgentWrapper.java
+++ b/services/core/java/com/android/server/trust/TrustAgentWrapper.java
@@ -122,16 +122,9 @@
if (!TrustManagerService.ENABLE_ACTIVE_UNLOCK_FLAG) {
return;
}
- if (!mWaitingForTrustableDowngrade) {
- return;
- }
// are these the broadcasts we want to listen to
- if (Intent.ACTION_SCREEN_OFF.equals(intent.getAction())
- || Intent.ACTION_USER_PRESENT.equals(intent.getAction())) {
- mTrusted = false;
- mTrustable = true;
- mWaitingForTrustableDowngrade = false;
- mTrustManagerService.updateTrust(mUserId, 0);
+ if (Intent.ACTION_SCREEN_OFF.equals(intent.getAction())) {
+ downgradeToTrustable();
}
}
};
@@ -480,8 +473,7 @@
final String pathUri = mAlarmIntent.toUri(Intent.URI_INTENT_SCHEME);
alarmFilter.addDataPath(pathUri, PatternMatcher.PATTERN_LITERAL);
- IntentFilter trustableFilter = new IntentFilter(Intent.ACTION_USER_PRESENT);
- trustableFilter.addAction(Intent.ACTION_SCREEN_OFF);
+ IntentFilter trustableFilter = new IntentFilter(Intent.ACTION_SCREEN_OFF);
// Schedules a restart for when connecting times out. If the connection succeeds,
// the restart is canceled in mCallback's onConnected.
@@ -668,6 +660,19 @@
mTrustable = false;
}
+ /**
+ * Downgrades the trustagent to trustable as a result of a keyguard or screen related event, and
+ * then updates the trust state of the phone to reflect the change.
+ */
+ public void downgradeToTrustable() {
+ if (mWaitingForTrustableDowngrade) {
+ mWaitingForTrustableDowngrade = false;
+ mTrusted = false;
+ mTrustable = true;
+ mTrustManagerService.updateTrust(mUserId, 0);
+ }
+ }
+
public boolean isManagingTrust() {
return mManagingTrust && !mTrustDisabledByDpm;
}
diff --git a/services/core/java/com/android/server/trust/TrustManagerService.java b/services/core/java/com/android/server/trust/TrustManagerService.java
index 8f4ddea..80ce70d 100644
--- a/services/core/java/com/android/server/trust/TrustManagerService.java
+++ b/services/core/java/com/android/server/trust/TrustManagerService.java
@@ -1184,6 +1184,22 @@
return false;
}
+ /**
+ * We downgrade to trustable whenever keyguard changes its showing value.
+ * - becomes showing: something has caused the device to show keyguard which happens due to
+ * user intent to lock the device either through direct action or a timeout
+ * - becomes not showing: keyguard was dismissed and we no longer need to keep the device
+ * unlocked
+ * */
+ private void dispatchTrustableDowngrade() {
+ for (int i = 0; i < mActiveAgents.size(); i++) {
+ AgentInfo info = mActiveAgents.valueAt(i);
+ if (info.userId == mCurrentUser) {
+ info.agent.downgradeToTrustable();
+ }
+ }
+ }
+
private List<String> getTrustGrantedMessages(int userId) {
if (!mStrongAuthTracker.isTrustAllowedForUser(userId)) {
return new ArrayList<>();
@@ -1752,6 +1768,7 @@
refreshDeviceLockedForUser(UserHandle.USER_ALL);
break;
case MSG_KEYGUARD_SHOWING_CHANGED:
+ dispatchTrustableDowngrade();
refreshDeviceLockedForUser(mCurrentUser);
break;
case MSG_START_USER:
diff --git a/services/core/java/com/android/server/wm/ActivityStarter.java b/services/core/java/com/android/server/wm/ActivityStarter.java
index b97ee7e..b2f27eb 100644
--- a/services/core/java/com/android/server/wm/ActivityStarter.java
+++ b/services/core/java/com/android/server/wm/ActivityStarter.java
@@ -2732,8 +2732,8 @@
intentActivity.getTaskFragment().clearLastPausedActivity();
Task intentTask = intentActivity.getTask();
- // Only update the target-root-task when it is not indicated.
if (mTargetRootTask == null) {
+ // Update launch target task when it is not indicated.
if (mSourceRecord != null && mSourceRecord.mLaunchRootTask != null) {
// Inherit the target-root-task from source to ensure trampoline activities will be
// launched into the same root task.
@@ -2742,6 +2742,17 @@
mTargetRootTask = getOrCreateRootTask(mStartActivity, mLaunchFlags, intentTask,
mOptions);
}
+ } else {
+ // If a launch target indicated, and the matching task is already in the adjacent task
+ // of the launch target. Adjust to use the adjacent task as its launch target. So the
+ // existing task will be launched into the closer one and won't be reparent redundantly.
+ // TODO(b/231541706): Migrate the logic to wm-shell after having proper APIs to help
+ // resolve target task without actually starting the activity.
+ final Task adjacentTargetTask = mTargetRootTask.getAdjacentTaskFragment() != null
+ ? mTargetRootTask.getAdjacentTaskFragment().asTask() : null;
+ if (adjacentTargetTask != null && intentActivity.isDescendantOf(adjacentTargetTask)) {
+ mTargetRootTask = adjacentTargetTask;
+ }
}
// If the target task is not in the front, then we need to bring it to the front...
@@ -2771,7 +2782,7 @@
intentActivity.setTaskToAffiliateWith(mSourceRecord.getTask());
}
- if (mTargetRootTask == intentActivity.getRootTask()) {
+ if (intentActivity.isDescendantOf(mTargetRootTask)) {
// TODO(b/151572268): Figure out a better way to move tasks in above 2-levels
// tasks hierarchies.
if (mTargetRootTask != intentTask
@@ -2818,19 +2829,6 @@
mTargetRootTask = intentActivity.getRootTask();
mSupervisor.handleNonResizableTaskIfNeeded(intentTask, WINDOWING_MODE_UNDEFINED,
mRootWindowContainer.getDefaultTaskDisplayArea(), mTargetRootTask);
-
- // We need to check if there is a launch root task in TDA for this target root task.
- // If it exist, we need to reparent target root task from TDA to launch root task.
- final TaskDisplayArea tda = mTargetRootTask.getDisplayArea();
- final Task launchRootTask = tda.getLaunchRootTask(mTargetRootTask.getWindowingMode(),
- mTargetRootTask.getActivityType(), null /** options */, mSourceRootTask,
- mLaunchFlags);
- // If target root task is created by organizer, let organizer handle reparent itself.
- if (!mTargetRootTask.mCreatedByOrganizer && launchRootTask != null
- && launchRootTask != mTargetRootTask) {
- mTargetRootTask.reparent(launchRootTask, POSITION_TOP);
- mTargetRootTask = launchRootTask;
- }
}
private void resumeTargetRootTaskIfNeeded() {
diff --git a/services/core/java/com/android/server/wm/BackNavigationController.java b/services/core/java/com/android/server/wm/BackNavigationController.java
index b37f980..aaae82d 100644
--- a/services/core/java/com/android/server/wm/BackNavigationController.java
+++ b/services/core/java/com/android/server/wm/BackNavigationController.java
@@ -124,8 +124,7 @@
LocalServices.getService(WindowManagerInternal.class);
IBinder focusedWindowToken = windowManagerInternal.getFocusedWindowToken();
- window = wmService.windowForClientLocked(null, focusedWindowToken,
- false /* throwOnError */);
+ window = wmService.getFocusedWindowLocked();
if (window == null) {
EmbeddedWindowController.EmbeddedWindow embeddedWindow =
@@ -147,12 +146,6 @@
}
if (window == null) {
- window = wmService.getFocusedWindowLocked();
- ProtoLog.d(WM_DEBUG_BACK_PREVIEW,
- "Focused window found using wmService.getFocusedWindowLocked()");
- }
-
- if (window == null) {
// We don't have any focused window, fallback ont the top currentTask of the focused
// display.
ProtoLog.w(WM_DEBUG_BACK_PREVIEW,
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index 2975a95..d0baa23 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -2922,7 +2922,7 @@
}
DisplayCutout loadDisplayCutout(int displayWidth, int displayHeight) {
- if (mDisplayPolicy == null) {
+ if (mDisplayPolicy == null || mInitialDisplayCutout == null) {
return null;
}
return DisplayCutout.fromResourcesRectApproximation(
@@ -2931,7 +2931,7 @@
}
RoundedCorners loadRoundedCorners(int displayWidth, int displayHeight) {
- if (mDisplayPolicy == null) {
+ if (mDisplayPolicy == null || mInitialRoundedCorners == null) {
return null;
}
return RoundedCorners.fromResources(
diff --git a/services/core/java/com/android/server/wm/InputMonitor.java b/services/core/java/com/android/server/wm/InputMonitor.java
index 65062e5..ea72e12 100644
--- a/services/core/java/com/android/server/wm/InputMonitor.java
+++ b/services/core/java/com/android/server/wm/InputMonitor.java
@@ -573,7 +573,7 @@
recentsAnimationController.getTargetAppDisplayArea();
if (targetDA != null) {
mRecentsAnimationInputConsumer.reparent(mInputTransaction, targetDA);
- mRecentsAnimationInputConsumer.show(mInputTransaction, MAX_VALUE - 1);
+ mRecentsAnimationInputConsumer.show(mInputTransaction, MAX_VALUE - 2);
mAddRecentsAnimationInputConsumerHandle = false;
}
}
@@ -584,10 +584,14 @@
final Task rootTask = w.getTask().getRootTask();
mPipInputConsumer.mWindowHandle.replaceTouchableRegionWithCrop(
rootTask.getSurfaceControl());
+ final DisplayArea targetDA = rootTask.getDisplayArea();
// We set the layer to z=MAX-1 so that it's always on top.
- mPipInputConsumer.reparent(mInputTransaction, rootTask);
- mPipInputConsumer.show(mInputTransaction, MAX_VALUE - 1);
- mAddPipInputConsumerHandle = false;
+ if (targetDA != null) {
+ mPipInputConsumer.layout(mInputTransaction, rootTask.getBounds());
+ mPipInputConsumer.reparent(mInputTransaction, targetDA);
+ mPipInputConsumer.show(mInputTransaction, MAX_VALUE - 1);
+ mAddPipInputConsumerHandle = false;
+ }
}
}
diff --git a/services/core/java/com/android/server/wm/SurfaceAnimationRunner.java b/services/core/java/com/android/server/wm/SurfaceAnimationRunner.java
index 4068a97..c7f8a1e 100644
--- a/services/core/java/com/android/server/wm/SurfaceAnimationRunner.java
+++ b/services/core/java/com/android/server/wm/SurfaceAnimationRunner.java
@@ -158,7 +158,7 @@
void continueStartingAnimations() {
synchronized (mLock) {
mAnimationStartDeferred = false;
- if (!mPendingAnimations.isEmpty()) {
+ if (!mPendingAnimations.isEmpty() && mPreProcessingAnimations.isEmpty()) {
mChoreographer.postFrameCallback(this::startAnimations);
}
}
@@ -204,7 +204,7 @@
mPreProcessingAnimations.remove(animationLeash);
mPendingAnimations.put(animationLeash, runningAnim);
- if (!mAnimationStartDeferred) {
+ if (!mAnimationStartDeferred && mPreProcessingAnimations.isEmpty()) {
mChoreographer.postFrameCallback(this::startAnimations);
}
}
@@ -214,7 +214,7 @@
if (!requiresEdgeExtension) {
mPendingAnimations.put(animationLeash, runningAnim);
- if (!mAnimationStartDeferred) {
+ if (!mAnimationStartDeferred && mPreProcessingAnimations.isEmpty()) {
mChoreographer.postFrameCallback(this::startAnimations);
}
@@ -330,6 +330,14 @@
private void startAnimations(long frameTimeNanos) {
synchronized (mLock) {
+ if (!mPreProcessingAnimations.isEmpty()) {
+ // We only want to start running animations once all mPreProcessingAnimations have
+ // been processed to ensure preprocessed animations start in sync.
+ // NOTE: This means we might delay running animations that require preprocessing if
+ // new animations that also require preprocessing are requested before the previous
+ // ones have finished (see b/227449117).
+ return;
+ }
startPendingAnimationsLocked();
}
mPowerManagerInternal.setPowerBoost(Boost.INTERACTION, 0);
@@ -553,4 +561,4 @@
return mAnimationHandler;
}
}
-}
+}
\ No newline at end of file
diff --git a/services/core/java/com/android/server/wm/WindowToken.java b/services/core/java/com/android/server/wm/WindowToken.java
index d2e56fa..ed9b2f0 100644
--- a/services/core/java/com/android/server/wm/WindowToken.java
+++ b/services/core/java/com/android/server/wm/WindowToken.java
@@ -378,7 +378,7 @@
SurfaceControl.Builder makeSurface() {
final SurfaceControl.Builder builder = super.makeSurface();
if (mRoundedCornerOverlay) {
- builder.setParent(null);
+ builder.setParent(getDisplayContent().getSurfaceControl());
}
return builder;
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java
index f59ec42..b943275 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java
@@ -785,6 +785,34 @@
}
/**
+ * This test ensures that {@link ActivityStarter#setTargetRootTaskIfNeeded} will task the
+ * adjacent task of indicated launch target into account. So the existing task will be launched
+ * into closer target.
+ */
+ @Test
+ public void testAdjustLaunchTargetWithAdjacentTask() {
+ // Create adjacent tasks and put one activity under it
+ final Task parent = new TaskBuilder(mSupervisor).build();
+ final Task adjacentParent = new TaskBuilder(mSupervisor).build();
+ parent.setAdjacentTaskFragment(adjacentParent, true);
+ final ActivityRecord activity = new ActivityBuilder(mAtm)
+ .setParentTask(parent)
+ .setCreateTask(true).build();
+
+ // Launch the activity to its adjacent parent
+ final ActivityOptions options = ActivityOptions.makeBasic()
+ .setLaunchRootTask(adjacentParent.mRemoteToken.toWindowContainerToken());
+ prepareStarter(FLAG_ACTIVITY_NEW_TASK, false /* mockGetRootTask */)
+ .setReason("testAdjustLaunchTargetWithAdjacentTask")
+ .setIntent(activity.intent)
+ .setActivityOptions(options.toBundle())
+ .execute();
+
+ // Verify the activity will be launched into the original parent
+ assertTrue(activity.isDescendantOf(parent));
+ }
+
+ /**
* This test ensures that {@link ActivityStarter#setTargetRootTaskIfNeeded} will
* move the existing task to front if the current focused root task doesn't have running task.
*/
diff --git a/services/tests/wmtests/src/com/android/server/wm/BackNavigationControllerTests.java b/services/tests/wmtests/src/com/android/server/wm/BackNavigationControllerTests.java
index 49cd343..873d9f3 100644
--- a/services/tests/wmtests/src/com/android/server/wm/BackNavigationControllerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/BackNavigationControllerTests.java
@@ -306,6 +306,7 @@
if (focus) {
doReturn(window.getWindowInfo().token)
.when(mWindowManagerInternal).getFocusedWindowToken();
+ doReturn(window).when(mWm).getFocusedWindowLocked();
}
}
}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/ImeStateInitializeHelper.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/ImeStateInitializeHelper.kt
new file mode 100644
index 0000000..16c4c25
--- /dev/null
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/ImeStateInitializeHelper.kt
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2022 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.server.wm.flicker.helpers
+
+import android.app.Instrumentation
+import android.support.test.launcherhelper.ILauncherStrategy
+import android.support.test.launcherhelper.LauncherStrategyFactory
+import com.android.server.wm.flicker.testapp.ActivityOptions
+import com.android.server.wm.traces.common.FlickerComponentName
+import com.android.server.wm.traces.parser.toFlickerComponent
+
+class ImeStateInitializeHelper @JvmOverloads constructor(
+ instr: Instrumentation,
+ launcherName: String = ActivityOptions.IME_ACTIVITY_INITIALIZE_LAUNCHER_NAME,
+ component: FlickerComponentName =
+ ActivityOptions.IME_ACTIVITY_INITIALIZE_COMPONENT_NAME.toFlickerComponent(),
+ launcherStrategy: ILauncherStrategy = LauncherStrategyFactory
+ .getInstance(instr)
+ .launcherStrategy
+) : StandardAppHelper(instr, launcherName, component, launcherStrategy)
\ No newline at end of file
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/LaunchAppShowImeOnStartTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/LaunchAppShowImeOnStartTest.kt
index 9643909..b897ca2 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/LaunchAppShowImeOnStartTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/LaunchAppShowImeOnStartTest.kt
@@ -28,6 +28,7 @@
import com.android.server.wm.flicker.FlickerTestParameterFactory
import com.android.server.wm.flicker.dsl.FlickerBuilder
import com.android.server.wm.flicker.helpers.ImeAppAutoFocusHelper
+import com.android.server.wm.flicker.helpers.ImeStateInitializeHelper
import com.android.server.wm.flicker.helpers.setRotation
import com.android.server.wm.traces.common.FlickerComponentName
import org.junit.FixMethodOrder
@@ -71,17 +72,20 @@
class LaunchAppShowImeOnStartTest(private val testSpec: FlickerTestParameter) {
private val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation()
private val testApp = ImeAppAutoFocusHelper(instrumentation, testSpec.startRotation)
+ private val initializeApp = ImeStateInitializeHelper(instrumentation)
@FlickerBuilderProvider
fun buildFlicker(): FlickerBuilder {
return FlickerBuilder(instrumentation).apply {
setup {
eachRun {
+ initializeApp.launchViaIntent()
this.setRotation(testSpec.startRotation)
}
}
teardown {
eachRun {
+ initializeApp.exit()
testApp.exit()
}
}
diff --git a/tests/FlickerTests/test-apps/flickerapp/AndroidManifest.xml b/tests/FlickerTests/test-apps/flickerapp/AndroidManifest.xml
index 2842232..43aa4b1 100644
--- a/tests/FlickerTests/test-apps/flickerapp/AndroidManifest.xml
+++ b/tests/FlickerTests/test-apps/flickerapp/AndroidManifest.xml
@@ -52,6 +52,16 @@
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
</activity>
+ <activity android:name=".ImeStateInitializeActivity"
+ android:theme="@style/no_starting_window"
+ android:windowSoftInputMode="stateAlwaysHidden"
+ android:label="ImeStateInitializeActivity"
+ android:exported="true">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN"/>
+ <category android:name="android.intent.category.LAUNCHER"/>
+ </intent-filter>
+ </activity>
<activity android:name=".SeamlessRotationActivity"
android:taskAffinity="com.android.server.wm.flicker.testapp.SeamlessRotationActivity"
android:theme="@style/CutoutShortEdges"
diff --git a/tests/FlickerTests/test-apps/flickerapp/res/values/styles.xml b/tests/FlickerTests/test-apps/flickerapp/res/values/styles.xml
index 589df38..1d21fd5 100644
--- a/tests/FlickerTests/test-apps/flickerapp/res/values/styles.xml
+++ b/tests/FlickerTests/test-apps/flickerapp/res/values/styles.xml
@@ -42,4 +42,8 @@
<item name="android:backgroundDimEnabled">false</item>
<item name="android:windowSoftInputMode">stateUnchanged</item>
</style>
+
+ <style name="no_starting_window" parent="@android:style/Theme.DeviceDefault">
+ <item name="android:windowDisablePreview">true</item>
+ </style>
</resources>
diff --git a/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/ActivityOptions.java b/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/ActivityOptions.java
index e080709..6cda482 100644
--- a/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/ActivityOptions.java
+++ b/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/ActivityOptions.java
@@ -37,6 +37,11 @@
new ComponentName(FLICKER_APP_PACKAGE,
FLICKER_APP_PACKAGE + ".ImeActivity");
+ public static final String IME_ACTIVITY_INITIALIZE_LAUNCHER_NAME = "ImeStateInitializeActivity";
+ public static final ComponentName IME_ACTIVITY_INITIALIZE_COMPONENT_NAME =
+ new ComponentName(FLICKER_APP_PACKAGE,
+ FLICKER_APP_PACKAGE + ".ImeStateInitializeActivity");
+
public static final String SIMPLE_ACTIVITY_LAUNCHER_NAME = "SimpleApp";
public static final ComponentName SIMPLE_ACTIVITY_AUTO_FOCUS_COMPONENT_NAME =
new ComponentName(FLICKER_APP_PACKAGE,
diff --git a/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/ImeStateInitializeActivity.java b/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/ImeStateInitializeActivity.java
new file mode 100644
index 0000000..4be79c4
--- /dev/null
+++ b/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/ImeStateInitializeActivity.java
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2022 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.server.wm.flicker.testapp;
+
+import android.app.Activity;
+import android.graphics.Color;
+import android.os.Bundle;
+import android.view.View;
+import android.view.ViewGroup;
+
+/**
+ * A nop {@link Activity} to make sure that the test starts from a deterministic state.
+ *
+ * <p>Currently this {@link Activity} makes sure the following things</p>
+ * <li>
+ * <ul>Hide the software keyboard with
+ * {@link android.view.WindowManager.LayoutParams#SOFT_INPUT_STATE_ALWAYS_HIDDEN}</ul>
+ * <ul>Make sure that the navigation bar (if supported) is rendered with {@link Color#BLACK}.
+ * </ul>
+ * </li>
+ */
+public class ImeStateInitializeActivity extends Activity {
+ @Override
+ protected void onCreate(Bundle bundle) {
+ super.onCreate(bundle);
+ final View view = new View(this);
+ view.setBackgroundColor(Color.WHITE);
+ view.setLayoutParams(new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
+ ViewGroup.LayoutParams.MATCH_PARENT));
+
+ // Make sure that navigation bar is rendered with black (if supported).
+ getWindow().setNavigationBarColor(Color.BLACK);
+
+ setContentView(view);
+ }
+}
diff --git a/tests/TrustTests/src/android/trust/test/TemporaryAndRenewableTrustTest.kt b/tests/TrustTests/src/android/trust/test/TemporaryAndRenewableTrustTest.kt
index 3c6d54d..ae72247 100644
--- a/tests/TrustTests/src/android/trust/test/TemporaryAndRenewableTrustTest.kt
+++ b/tests/TrustTests/src/android/trust/test/TemporaryAndRenewableTrustTest.kt
@@ -29,7 +29,7 @@
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.platform.app.InstrumentationRegistry.getInstrumentation
import androidx.test.uiautomator.UiDevice
-import com.google.common.truth.Truth.assertThat
+import android.trust.test.lib.wait
import org.junit.Before
import org.junit.Rule
import org.junit.Test
@@ -74,9 +74,9 @@
uiDevice.sleep()
lockStateTrackingRule.assertLocked()
+ uiDevice.wakeUp()
trustAgentRule.agent.grantTrust(
GRANT_MESSAGE, 0, FLAG_GRANT_TRUST_TEMPORARY_AND_RENEWABLE) {}
- uiDevice.wakeUp()
lockStateTrackingRule.assertLocked()
}
@@ -98,9 +98,9 @@
lockStateTrackingRule.assertLocked()
+ uiDevice.wakeUp()
trustAgentRule.agent.grantTrust(
GRANT_MESSAGE, 0, FLAG_GRANT_TRUST_TEMPORARY_AND_RENEWABLE) {}
- uiDevice.wakeUp()
lockStateTrackingRule.assertUnlocked()
}
@@ -116,6 +116,7 @@
uiDevice.sleep()
lockStateTrackingRule.assertLocked()
+ uiDevice.wakeUp()
Log.i(TAG, "Renewing trust and unlocking")
var result: GrantTrustResult? = null
@@ -124,10 +125,9 @@
Log.i(TAG, "Callback received; status=${it.status}")
result = it
}
- uiDevice.wakeUp()
lockStateTrackingRule.assertUnlocked()
- assertThat(result?.status).isEqualTo(STATUS_UNLOCKED_BY_GRANT)
+ wait("callback triggered") { result?.status == STATUS_UNLOCKED_BY_GRANT }
}
@Test
@@ -141,7 +141,6 @@
trustAgentRule.agent.revokeTrust()
await(500)
uiDevice.wakeUp()
- await(500)
trustAgentRule.agent.grantTrust(
GRANT_MESSAGE, 0, FLAG_GRANT_TRUST_TEMPORARY_AND_RENEWABLE) {}