diff options
166 files changed, 3119 insertions, 1373 deletions
diff --git a/core/api/current.txt b/core/api/current.txt index 9a16390baf10..1d03370e698d 100644 --- a/core/api/current.txt +++ b/core/api/current.txt @@ -1298,7 +1298,7 @@ package android { field public static final int shortcutLongLabel = 16844074; // 0x101052a field public static final int shortcutShortLabel = 16844073; // 0x1010529 field public static final int shouldDisableView = 16843246; // 0x10101ee - field public static final int shouldUseDefaultDeviceStateChangeTransition; + field public static final int shouldUseDefaultDisplayStateChangeTransition; field public static final int showAsAction = 16843481; // 0x10102d9 field public static final int showDefault = 16843258; // 0x10101fa field public static final int showDividers = 16843561; // 0x1010329 @@ -6927,7 +6927,7 @@ package android.app { method public android.graphics.drawable.Drawable loadIcon(android.content.pm.PackageManager); method public CharSequence loadLabel(android.content.pm.PackageManager); method public android.graphics.drawable.Drawable loadThumbnail(android.content.pm.PackageManager); - method public boolean shouldUseDefaultDeviceStateChangeTransition(); + method public boolean shouldUseDefaultDisplayStateChangeTransition(); method public boolean supportsMultipleDisplays(); method public void writeToParcel(android.os.Parcel, int); field @NonNull public static final android.os.Parcelable.Creator<android.app.WallpaperInfo> CREATOR; diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java index 12025f98ada8..22091fcde86f 100644 --- a/core/java/android/app/Activity.java +++ b/core/java/android/app/Activity.java @@ -2507,12 +2507,11 @@ public class Activity extends ContextThemeWrapper * * <p>To get the voice interactor you need to call {@link #getVoiceInteractor()} * which would return non <code>null</code> only if there is an ongoing voice - * interaction session. You an also detect when the voice interactor is no + * interaction session. You can also detect when the voice interactor is no * longer valid because the voice interaction session that is backing is finished * by calling {@link VoiceInteractor#registerOnDestroyedCallback(Executor, Runnable)}. * - * <p>This method will be called only after {@link #onStart()} is being called and - * before {@link #onStop()} is being called. + * <p>This method will be called only after {@link #onStart()} and before {@link #onStop()}. * * <p>You should pass to the callback the currently supported direct actions which * cannot be <code>null</code> or contain <code>null</code> elements. diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java index 80554d7939f2..d0680f8c9268 100644 --- a/core/java/android/app/ActivityThread.java +++ b/core/java/android/app/ActivityThread.java @@ -526,9 +526,6 @@ public final class ActivityThread extends ClientTransactionHandler // A reusable token for other purposes, e.g. content capture, translation. It shouldn't be // used without security checks public IBinder shareableActivityToken; - // The token of the initial TaskFragment that embedded this activity. Do not rely on it - // after creation because the activity could be reparented. - @Nullable public IBinder mInitialTaskFragmentToken; int ident; @UnsupportedAppUsage Intent intent; @@ -622,8 +619,7 @@ public final class ActivityThread extends ClientTransactionHandler List<ReferrerIntent> pendingNewIntents, ActivityOptions activityOptions, boolean isForward, ProfilerInfo profilerInfo, ClientTransactionHandler client, IBinder assistToken, FixedRotationAdjustments fixedRotationAdjustments, - IBinder shareableActivityToken, boolean launchedFromBubble, - IBinder initialTaskFragmentToken) { + IBinder shareableActivityToken, boolean launchedFromBubble) { this.token = token; this.assistToken = assistToken; this.shareableActivityToken = shareableActivityToken; @@ -645,7 +641,6 @@ public final class ActivityThread extends ClientTransactionHandler mActivityOptions = activityOptions; mPendingFixedRotationAdjustments = fixedRotationAdjustments; mLaunchedFromBubble = launchedFromBubble; - mInitialTaskFragmentToken = initialTaskFragmentToken; init(); } diff --git a/core/java/android/app/ActivityTransitionCoordinator.java b/core/java/android/app/ActivityTransitionCoordinator.java index 4b87a647a80b..f5b3b40d88d6 100644 --- a/core/java/android/app/ActivityTransitionCoordinator.java +++ b/core/java/android/app/ActivityTransitionCoordinator.java @@ -871,6 +871,7 @@ abstract class ActivityTransitionCoordinator extends ResultReceiver { if (view.isAttachedToWindow()) { tempMatrix.reset(); mSharedElementParentMatrices.get(i).invert(tempMatrix); + decor.transformMatrixToLocal(tempMatrix); GhostView.addGhost(view, decor, tempMatrix); ViewGroup parent = (ViewGroup) view.getParent(); if (moveWithParent && !isInTransitionGroup(parent, decor)) { diff --git a/core/java/android/app/DirectAction.java b/core/java/android/app/DirectAction.java index b0ed490369ad..ac3868b2ece9 100644 --- a/core/java/android/app/DirectAction.java +++ b/core/java/android/app/DirectAction.java @@ -22,14 +22,13 @@ import android.os.Bundle; import android.os.IBinder; import android.os.Parcel; import android.os.Parcelable; -import android.view.accessibility.AccessibilityNodeInfo; import com.android.internal.util.Preconditions; import java.util.Objects; /** - * Represents a abstract action that can be perform on this app. This are requested from + * Represents an abstract action that can be perform on this app. This are requested from * outside the app's UI (eg by SystemUI or assistant). The semantics of these actions are * not specified by the OS. This allows open-ended and scalable approach for defining how * an app interacts with components that expose alternative interaction models to the user diff --git a/core/java/android/app/WallpaperInfo.java b/core/java/android/app/WallpaperInfo.java index c552cb62749b..a969b10c15a3 100644 --- a/core/java/android/app/WallpaperInfo.java +++ b/core/java/android/app/WallpaperInfo.java @@ -81,7 +81,7 @@ public final class WallpaperInfo implements Parcelable { final int mContextDescriptionResource; final boolean mShowMetadataInPreview; final boolean mSupportsAmbientMode; - final boolean mShouldUseDefaultDeviceStateChangeTransition; + final boolean mShouldUseDefaultDisplayStateChangeTransition; final String mSettingsSliceUri; final boolean mSupportMultipleDisplays; @@ -146,9 +146,9 @@ public final class WallpaperInfo implements Parcelable { mSupportsAmbientMode = sa.getBoolean( com.android.internal.R.styleable.Wallpaper_supportsAmbientMode, false); - mShouldUseDefaultDeviceStateChangeTransition = sa.getBoolean( + mShouldUseDefaultDisplayStateChangeTransition = sa.getBoolean( com.android.internal.R.styleable - .Wallpaper_shouldUseDefaultDeviceStateChangeTransition, true); + .Wallpaper_shouldUseDefaultDisplayStateChangeTransition, true); mSettingsSliceUri = sa.getString( com.android.internal.R.styleable.Wallpaper_settingsSliceUri); mSupportMultipleDisplays = sa.getBoolean( @@ -175,7 +175,7 @@ public final class WallpaperInfo implements Parcelable { mSupportsAmbientMode = source.readInt() != 0; mSettingsSliceUri = source.readString(); mSupportMultipleDisplays = source.readInt() != 0; - mShouldUseDefaultDeviceStateChangeTransition = source.readInt() != 0; + mShouldUseDefaultDisplayStateChangeTransition = source.readInt() != 0; mService = ResolveInfo.CREATOR.createFromParcel(source); } @@ -400,24 +400,24 @@ public final class WallpaperInfo implements Parcelable { /** * Returns whether this wallpaper should receive default zooming updates when the device - * changes its state (e.g. when folding or unfolding a foldable device). + * changes its display state (e.g. when folding or unfolding a foldable device). * If set to false the wallpaper will not receive zoom events when changing the device state, * so it can implement its own transition instead. * <p> * This corresponds to the value {@link - * android.R.styleable#Wallpaper_shouldUseDefaultDeviceStateChangeTransition} in the + * android.R.styleable#Wallpaper_shouldUseDefaultDisplayStateChangeTransition} in the * XML description of the wallpaper. * <p> * The default value is {@code true}. * - * @see android.R.styleable#Wallpaper_shouldUseDefaultDeviceStateChangeTransition + * @see android.R.styleable#Wallpaper_shouldUseDefaultDisplayStateChangeTransition * @return {@code true} if wallpaper should receive default device state change * transition updates * - * @attr ref android.R.styleable#Wallpaper_shouldUseDefaultDeviceStateChangeTransition + * @attr ref android.R.styleable#Wallpaper_shouldUseDefaultDisplayStateChangeTransition */ - public boolean shouldUseDefaultDeviceStateChangeTransition() { - return mShouldUseDefaultDeviceStateChangeTransition; + public boolean shouldUseDefaultDisplayStateChangeTransition() { + return mShouldUseDefaultDisplayStateChangeTransition; } public void dump(Printer pw, String prefix) { @@ -450,7 +450,7 @@ public final class WallpaperInfo implements Parcelable { dest.writeInt(mSupportsAmbientMode ? 1 : 0); dest.writeString(mSettingsSliceUri); dest.writeInt(mSupportMultipleDisplays ? 1 : 0); - dest.writeInt(mShouldUseDefaultDeviceStateChangeTransition ? 1 : 0); + dest.writeInt(mShouldUseDefaultDisplayStateChangeTransition ? 1 : 0); mService.writeToParcel(dest, flags); } diff --git a/core/java/android/app/servertransaction/ActivityResultItem.java b/core/java/android/app/servertransaction/ActivityResultItem.java index e059f177e344..27d104b59284 100644 --- a/core/java/android/app/servertransaction/ActivityResultItem.java +++ b/core/java/android/app/servertransaction/ActivityResultItem.java @@ -16,6 +16,8 @@ package android.app.servertransaction; +import static android.app.servertransaction.ActivityLifecycleItem.ON_RESUME; +import static android.app.servertransaction.ActivityLifecycleItem.UNDEFINED; import static android.os.Trace.TRACE_TAG_ACTIVITY_MANAGER; import android.annotation.NonNull; @@ -23,6 +25,9 @@ import android.annotation.Nullable; import android.app.ActivityThread.ActivityClientRecord; import android.app.ClientTransactionHandler; import android.app.ResultInfo; +import android.app.compat.CompatChanges; +import android.compat.annotation.ChangeId; +import android.compat.annotation.EnabledAfter; import android.compat.annotation.UnsupportedAppUsage; import android.os.Build; import android.os.Parcel; @@ -41,11 +46,19 @@ public class ActivityResultItem extends ActivityTransactionItem { @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) private List<ResultInfo> mResultInfoList; - /* TODO(b/78294732) + /** + * Correct the lifecycle of activity result after {@link android.os.Build.VERSION_CODES#S} to + * guarantee that an activity gets activity result just before resume. + */ + @ChangeId + @EnabledAfter(targetSdkVersion = Build.VERSION_CODES.S) + public static final long CALL_ACTIVITY_RESULT_BEFORE_RESUME = 78294732L; + @Override public int getPostExecutionState() { - return ON_RESUME; - }*/ + return CompatChanges.isChangeEnabled(CALL_ACTIVITY_RESULT_BEFORE_RESUME) + ? ON_RESUME : UNDEFINED; + } @Override public void execute(ClientTransactionHandler client, ActivityClientRecord r, diff --git a/core/java/android/app/servertransaction/LaunchActivityItem.java b/core/java/android/app/servertransaction/LaunchActivityItem.java index 37cbccb89735..34e4fcdb9140 100644 --- a/core/java/android/app/servertransaction/LaunchActivityItem.java +++ b/core/java/android/app/servertransaction/LaunchActivityItem.java @@ -73,7 +73,6 @@ public class LaunchActivityItem extends ClientTransactionItem { private IBinder mAssistToken; private IBinder mShareableActivityToken; private boolean mLaunchedFromBubble; - private IBinder mTaskFragmentToken; /** * It is only non-null if the process is the first time to launch activity. It is only an * optimization for quick look up of the interface so the field is ignored for comparison. @@ -87,7 +86,7 @@ public class LaunchActivityItem extends ClientTransactionItem { mOverrideConfig, mCompatInfo, mReferrer, mVoiceInteractor, mState, mPersistentState, mPendingResults, mPendingNewIntents, mActivityOptions, mIsForward, mProfilerInfo, client, mAssistToken, mFixedRotationAdjustments, mShareableActivityToken, - mLaunchedFromBubble, mTaskFragmentToken); + mLaunchedFromBubble); client.addLaunchingActivity(token, r); client.updateProcessState(mProcState, false); client.updatePendingConfiguration(mCurConfig); @@ -125,7 +124,7 @@ public class LaunchActivityItem extends ClientTransactionItem { boolean isForward, ProfilerInfo profilerInfo, IBinder assistToken, IActivityClientController activityClientController, FixedRotationAdjustments fixedRotationAdjustments, IBinder shareableActivityToken, - boolean launchedFromBubble, IBinder taskFragmentToken) { + boolean launchedFromBubble) { LaunchActivityItem instance = ObjectPool.obtain(LaunchActivityItem.class); if (instance == null) { instance = new LaunchActivityItem(); @@ -134,7 +133,7 @@ public class LaunchActivityItem extends ClientTransactionItem { voiceInteractor, procState, state, persistentState, pendingResults, pendingNewIntents, activityOptions, isForward, profilerInfo, assistToken, activityClientController, fixedRotationAdjustments, shareableActivityToken, - launchedFromBubble, taskFragmentToken); + launchedFromBubble); return instance; } @@ -142,7 +141,7 @@ public class LaunchActivityItem extends ClientTransactionItem { @Override public void recycle() { setValues(this, null, 0, null, null, null, null, null, null, 0, null, null, null, null, - null, false, null, null, null, null, null, false, null); + null, false, null, null, null, null, null, false); ObjectPool.recycle(this); } @@ -173,7 +172,6 @@ public class LaunchActivityItem extends ClientTransactionItem { dest.writeTypedObject(mFixedRotationAdjustments, flags); dest.writeStrongBinder(mShareableActivityToken); dest.writeBoolean(mLaunchedFromBubble); - dest.writeStrongBinder(mTaskFragmentToken); } /** Read from Parcel. */ @@ -192,8 +190,7 @@ public class LaunchActivityItem extends ClientTransactionItem { in.readStrongBinder(), IActivityClientController.Stub.asInterface(in.readStrongBinder()), in.readTypedObject(FixedRotationAdjustments.CREATOR), in.readStrongBinder(), - in.readBoolean(), - in.readStrongBinder()); + in.readBoolean()); } public static final @NonNull Creator<LaunchActivityItem> CREATOR = @@ -232,8 +229,7 @@ public class LaunchActivityItem extends ClientTransactionItem { && Objects.equals(mProfilerInfo, other.mProfilerInfo) && Objects.equals(mAssistToken, other.mAssistToken) && Objects.equals(mFixedRotationAdjustments, other.mFixedRotationAdjustments) - && Objects.equals(mShareableActivityToken, other.mShareableActivityToken) - && Objects.equals(mTaskFragmentToken, other.mTaskFragmentToken); + && Objects.equals(mShareableActivityToken, other.mShareableActivityToken); } @Override @@ -256,7 +252,6 @@ public class LaunchActivityItem extends ClientTransactionItem { result = 31 * result + Objects.hashCode(mAssistToken); result = 31 * result + Objects.hashCode(mFixedRotationAdjustments); result = 31 * result + Objects.hashCode(mShareableActivityToken); - result = 31 * result + Objects.hashCode(mTaskFragmentToken); return result; } @@ -306,7 +301,7 @@ public class LaunchActivityItem extends ClientTransactionItem { ActivityOptions activityOptions, boolean isForward, ProfilerInfo profilerInfo, IBinder assistToken, IActivityClientController activityClientController, FixedRotationAdjustments fixedRotationAdjustments, IBinder shareableActivityToken, - boolean launchedFromBubble, IBinder taskFragmentToken) { + boolean launchedFromBubble) { instance.mIntent = intent; instance.mIdent = ident; instance.mInfo = info; @@ -328,6 +323,5 @@ public class LaunchActivityItem extends ClientTransactionItem { instance.mFixedRotationAdjustments = fixedRotationAdjustments; instance.mShareableActivityToken = shareableActivityToken; instance.mLaunchedFromBubble = launchedFromBubble; - instance.mTaskFragmentToken = taskFragmentToken; } } diff --git a/core/java/android/hardware/display/AmbientDisplayConfiguration.java b/core/java/android/hardware/display/AmbientDisplayConfiguration.java index 518b22bd5e10..f5b2ac586bd1 100644 --- a/core/java/android/hardware/display/AmbientDisplayConfiguration.java +++ b/core/java/android/hardware/display/AmbientDisplayConfiguration.java @@ -24,6 +24,8 @@ import android.provider.Settings; import android.text.TextUtils; import android.util.Log; +import java.util.Arrays; + import com.android.internal.R; /** @@ -258,10 +260,11 @@ public class AmbientDisplayConfiguration { String defaultValue, int posture) { String sensorType = defaultValue; - if (posture < postureMapping.length) { + if (postureMapping != null && posture < postureMapping.length) { sensorType = postureMapping[posture]; } else { - Log.e(TAG, "Unsupported doze posture " + posture); + Log.e(TAG, "Unsupported doze posture " + posture + + " postureMapping=" + Arrays.toString(postureMapping)); } return TextUtils.isEmpty(sensorType) ? defaultValue : sensorType; diff --git a/core/java/android/hardware/display/BrightnessInfo.java b/core/java/android/hardware/display/BrightnessInfo.java index c5d37c2d0b90..0dc8f92967fb 100644 --- a/core/java/android/hardware/display/BrightnessInfo.java +++ b/core/java/android/hardware/display/BrightnessInfo.java @@ -60,12 +60,18 @@ public final class BrightnessInfo implements Parcelable { /** Brightness */ public final float brightness; + /** Brightness after {@link DisplayPowerController} adjustments */ + public final float adjustedBrightness; + /** Current minimum supported brightness. */ public final float brightnessMinimum; /** Current maximum supported brightness. */ public final float brightnessMaximum; + /** Brightness values greater than this point are only used in High Brightness Mode. */ + public final float highBrightnessTransitionPoint; + /** * Current state of high brightness mode. * Can be any of HIGH_BRIGHTNESS_MODE_* values. @@ -73,11 +79,20 @@ public final class BrightnessInfo implements Parcelable { public final int highBrightnessMode; public BrightnessInfo(float brightness, float brightnessMinimum, float brightnessMaximum, - @HighBrightnessMode int highBrightnessMode) { + @HighBrightnessMode int highBrightnessMode, float highBrightnessTransitionPoint) { + this(brightness, brightness, brightnessMinimum, brightnessMaximum, highBrightnessMode, + highBrightnessTransitionPoint); + } + + public BrightnessInfo(float brightness, float adjustedBrightness, float brightnessMinimum, + float brightnessMaximum, @HighBrightnessMode int highBrightnessMode, + float highBrightnessTransitionPoint) { this.brightness = brightness; + this.adjustedBrightness = adjustedBrightness; this.brightnessMinimum = brightnessMinimum; this.brightnessMaximum = brightnessMaximum; this.highBrightnessMode = highBrightnessMode; + this.highBrightnessTransitionPoint = highBrightnessTransitionPoint; } /** @@ -103,9 +118,11 @@ public final class BrightnessInfo implements Parcelable { @Override public void writeToParcel(Parcel dest, int flags) { dest.writeFloat(brightness); + dest.writeFloat(adjustedBrightness); dest.writeFloat(brightnessMinimum); dest.writeFloat(brightnessMaximum); dest.writeInt(highBrightnessMode); + dest.writeFloat(highBrightnessTransitionPoint); } public static final @android.annotation.NonNull Creator<BrightnessInfo> CREATOR = @@ -123,9 +140,11 @@ public final class BrightnessInfo implements Parcelable { private BrightnessInfo(Parcel source) { brightness = source.readFloat(); + adjustedBrightness = source.readFloat(); brightnessMinimum = source.readFloat(); brightnessMaximum = source.readFloat(); highBrightnessMode = source.readInt(); + highBrightnessTransitionPoint = source.readFloat(); } } diff --git a/core/java/android/text/TextLine.java b/core/java/android/text/TextLine.java index 1f11d10052fe..1a7ec7f99c95 100644 --- a/core/java/android/text/TextLine.java +++ b/core/java/android/text/TextLine.java @@ -782,7 +782,7 @@ public class TextLine { int spanStart = runStart; int spanLimit; - if (mSpanned == null) { + if (mSpanned == null || runStart == runLimit) { spanLimit = runLimit; } else { int target = after ? offset + 1 : offset; diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml index 3f530fe3432c..1255dad27eed 100644 --- a/core/res/AndroidManifest.xml +++ b/core/res/AndroidManifest.xml @@ -248,6 +248,8 @@ android:name="com.android.bluetooth.BluetoothMapContentObserver.action.MESSAGE_DELIVERY" /> <protected-broadcast android:name="android.bluetooth.pan.profile.action.CONNECTION_STATE_CHANGED" /> + <protected-broadcast android:name="android.bluetooth.action.LE_AUDIO_CONNECTION_STATE_CHANGED" /> + <protected-broadcast android:name="android.bluetooth.action.LE_AUDIO_ACTIVE_DEVICE_CHANGED" /> <protected-broadcast android:name="android.bluetooth.action.TETHERING_STATE_CHANGED" /> <protected-broadcast android:name="android.bluetooth.pbap.profile.action.CONNECTION_STATE_CHANGED" /> diff --git a/core/res/res/anim-ldrtl/task_close_exit.xml b/core/res/res/anim-ldrtl/task_close_exit.xml index 76fbdffd1760..0887019ca608 100644 --- a/core/res/res/anim-ldrtl/task_close_exit.xml +++ b/core/res/res/anim-ldrtl/task_close_exit.xml @@ -28,9 +28,4 @@ android:startOffset="0" android:duration="500"/> - <!-- This is needed to keep the animation running while task_open_enter completes --> - <alpha - android:fromAlpha="1.0" - android:toAlpha="1.0" - android:duration="600"/> -</set>
\ No newline at end of file +</set> diff --git a/core/res/res/anim-ldrtl/task_open_exit.xml b/core/res/res/anim-ldrtl/task_open_exit.xml index beb6fca5632a..88cdcceae415 100644 --- a/core/res/res/anim-ldrtl/task_open_exit.xml +++ b/core/res/res/anim-ldrtl/task_open_exit.xml @@ -28,9 +28,4 @@ android:startOffset="0" android:duration="500"/> - <!-- This is needed to keep the animation running while task_open_enter completes --> - <alpha - android:fromAlpha="1.0" - android:toAlpha="1.0" - android:duration="600"/> -</set>
\ No newline at end of file +</set> diff --git a/core/res/res/anim/task_close_exit.xml b/core/res/res/anim/task_close_exit.xml index 736f3f2083e5..3a8dd934cd17 100644 --- a/core/res/res/anim/task_close_exit.xml +++ b/core/res/res/anim/task_close_exit.xml @@ -30,9 +30,4 @@ android:startOffset="0" android:duration="500"/> - <!-- This is needed to keep the animation running while task_open_enter completes --> - <alpha - android:fromAlpha="1.0" - android:toAlpha="1.0" - android:duration="600"/> -</set>
\ No newline at end of file +</set> diff --git a/core/res/res/anim/task_open_exit.xml b/core/res/res/anim/task_open_exit.xml index d1703170e16f..21fec7f694bf 100644 --- a/core/res/res/anim/task_open_exit.xml +++ b/core/res/res/anim/task_open_exit.xml @@ -30,9 +30,4 @@ android:startOffset="0" android:duration="500"/> - <!-- This is needed to keep the animation running while task_open_enter completes --> - <alpha - android:fromAlpha="1.0" - android:toAlpha="1.0" - android:duration="600"/> -</set>
\ No newline at end of file +</set> diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml index 53396407904a..77820d1dfa38 100644 --- a/core/res/res/values/attrs.xml +++ b/core/res/res/values/attrs.xml @@ -8372,8 +8372,8 @@ <attr name="supportsAmbientMode" format="boolean" /> <!-- Indicates that this wallpaper service should receive zoom transition updates when - changing the device state (e.g. when folding or unfolding a foldable device). - When this value is set to true + changing the display state of the device (e.g. when folding or unfolding + a foldable device). When this value is set to true {@link android.service.wallpaper.WallpaperService.Engine} could receive zoom updates before or after changing the device state. Wallpapers receive zoom updates using {@link android.service.wallpaper.WallpaperService.Engine#onZoomChanged(float)} and @@ -8381,8 +8381,8 @@ {@link android.service.wallpaper.WallpaperService.Engine} is created and not destroyed. Default value is true. Corresponds to - {@link android.app.WallpaperInfo#shouldUseDefaultDeviceStateChangeTransition()} --> - <attr name="shouldUseDefaultDeviceStateChangeTransition" format="boolean" /> + {@link android.app.WallpaperInfo#shouldUseDefaultDisplayStateChangeTransition()} --> + <attr name="shouldUseDefaultDisplayStateChangeTransition" format="boolean" /> <!-- Uri that specifies a settings Slice for this wallpaper. --> <attr name="settingsSliceUri" format="string"/> diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml index e17daf09e4da..462b1883e29d 100644 --- a/core/res/res/values/public.xml +++ b/core/res/res/values/public.xml @@ -3221,7 +3221,7 @@ <eat-comment /> <staging-public-group type="attr" first-id="0x01ff0000"> - <public name="shouldUseDefaultDeviceStateChangeTransition" /> + <public name="shouldUseDefaultDisplayStateChangeTransition" /> </staging-public-group> <staging-public-group type="id" first-id="0x01fe0000"> diff --git a/core/tests/coretests/src/android/app/servertransaction/ObjectPoolTests.java b/core/tests/coretests/src/android/app/servertransaction/ObjectPoolTests.java index 50639be57f22..3e261a7113ac 100644 --- a/core/tests/coretests/src/android/app/servertransaction/ObjectPoolTests.java +++ b/core/tests/coretests/src/android/app/servertransaction/ObjectPoolTests.java @@ -157,7 +157,7 @@ public class ObjectPoolTests { .setPendingResults(resultInfoList()).setPendingNewIntents(referrerIntentList()) .setIsForward(true).setAssistToken(assistToken) .setShareableActivityToken(shareableActivityToken) - .setTaskFragmentToken(new Binder()).build(); + .build(); LaunchActivityItem emptyItem = new LaunchActivityItemBuilder().build(); LaunchActivityItem item = itemSupplier.get(); diff --git a/core/tests/coretests/src/android/app/servertransaction/TestUtils.java b/core/tests/coretests/src/android/app/servertransaction/TestUtils.java index 1173c9210ed5..75da0bfba581 100644 --- a/core/tests/coretests/src/android/app/servertransaction/TestUtils.java +++ b/core/tests/coretests/src/android/app/servertransaction/TestUtils.java @@ -112,7 +112,6 @@ class TestUtils { private IBinder mShareableActivityToken; private FixedRotationAdjustments mFixedRotationAdjustments; private boolean mLaunchedFromBubble; - private IBinder mTaskFragmentToken; LaunchActivityItemBuilder setIntent(Intent intent) { mIntent = intent; @@ -214,18 +213,13 @@ class TestUtils { return this; } - LaunchActivityItemBuilder setTaskFragmentToken(IBinder taskFragmentToken) { - mTaskFragmentToken = taskFragmentToken; - return this; - } - LaunchActivityItem build() { return LaunchActivityItem.obtain(mIntent, mIdent, mInfo, mCurConfig, mOverrideConfig, mCompatInfo, mReferrer, mVoiceInteractor, mProcState, mState, mPersistentState, mPendingResults, mPendingNewIntents, mActivityOptions, mIsForward, mProfilerInfo, mAssistToken, null /* activityClientController */, mFixedRotationAdjustments, - mShareableActivityToken, mLaunchedFromBubble, mTaskFragmentToken); + mShareableActivityToken, mLaunchedFromBubble); } } } diff --git a/core/tests/coretests/src/android/app/servertransaction/TransactionParcelTests.java b/core/tests/coretests/src/android/app/servertransaction/TransactionParcelTests.java index 98c9afd2eb6b..df0c64c810c6 100644 --- a/core/tests/coretests/src/android/app/servertransaction/TransactionParcelTests.java +++ b/core/tests/coretests/src/android/app/servertransaction/TransactionParcelTests.java @@ -209,7 +209,6 @@ public class TransactionParcelTests { .setPendingNewIntents(referrerIntentList()).setIsForward(true) .setAssistToken(new Binder()).setFixedRotationAdjustments(fixedRotationAdjustments) .setShareableActivityToken(new Binder()) - .setTaskFragmentToken(new Binder()) .build(); writeAndPrepareForReading(item); diff --git a/core/tests/mockingcoretests/src/android/app/activity/ActivityThreadClientTest.java b/core/tests/mockingcoretests/src/android/app/activity/ActivityThreadClientTest.java index 516a5d288c0f..269d8424a78f 100644 --- a/core/tests/mockingcoretests/src/android/app/activity/ActivityThreadClientTest.java +++ b/core/tests/mockingcoretests/src/android/app/activity/ActivityThreadClientTest.java @@ -299,7 +299,7 @@ public class ActivityThreadClientTest { null /* activityOptions */, true /* isForward */, null /* profilerInfo */, mThread /* client */, null /* asssitToken */, null /* fixedRotationAdjustments */, null /* shareableActivityToken */, - false /* launchedFromBubble */, null /* taskfragmentToken */); + false /* launchedFromBubble */); } @Override diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java index e1c8b11a835c..42b438041d7a 100644 --- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java +++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java @@ -172,7 +172,7 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen void handleActivityCreated(@NonNull Activity launchedActivity) { final List<EmbeddingRule> splitRules = getSplitRules(); final TaskFragmentContainer currentContainer = getContainerWithActivity( - launchedActivity.getActivityToken(), launchedActivity); + launchedActivity.getActivityToken()); // Check if the activity is configured to always be expanded. if (shouldExpand(launchedActivity, null, splitRules)) { @@ -262,29 +262,9 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen */ @Nullable TaskFragmentContainer getContainerWithActivity(@NonNull IBinder activityToken) { - return getContainerWithActivity(activityToken, null /* activityToAdd */); - } - - /** - * This method can only be called from {@link #onActivityCreated(Activity)}, use - * {@link #getContainerWithActivity(IBinder) } otherwise. - * - * Returns a container that this activity is registered with. The activity could be created - * before the container appeared, adding the activity to the container if so. - */ - @Nullable - private TaskFragmentContainer getContainerWithActivity(@NonNull IBinder activityToken, - Activity activityToAdd) { - final IBinder taskFragmentToken = ActivityThread.currentActivityThread().getActivityClient( - activityToken).mInitialTaskFragmentToken; for (TaskFragmentContainer container : mContainers) { if (container.hasActivity(activityToken)) { return container; - } else if (container.getTaskFragmentToken().equals(taskFragmentToken)) { - if (activityToAdd != null) { - container.addPendingAppearedActivity(activityToAdd); - } - return container; } } diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitPresenter.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitPresenter.java index 25292b905fe8..81be21cbd7aa 100644 --- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitPresenter.java +++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitPresenter.java @@ -252,7 +252,8 @@ class SplitPresenter extends JetpackTaskFragmentOrganizer { // Getting the parent bounds using the updated container - it will have the recent value. final Rect parentBounds = getParentContainerBounds(updatedContainer); final SplitRule rule = splitContainer.getSplitRule(); - final Activity activity = splitContainer.getPrimaryContainer().getTopNonFinishingActivity(); + final TaskFragmentContainer primaryContainer = splitContainer.getPrimaryContainer(); + final Activity activity = primaryContainer.getTopNonFinishingActivity(); if (activity == null) { return; } @@ -264,10 +265,12 @@ class SplitPresenter extends JetpackTaskFragmentOrganizer { // If the task fragments are not registered yet, the positions will be updated after they // are created again. - resizeTaskFragmentIfRegistered(wct, splitContainer.getPrimaryContainer(), - primaryRectBounds); - resizeTaskFragmentIfRegistered(wct, splitContainer.getSecondaryContainer(), - secondaryRectBounds); + resizeTaskFragmentIfRegistered(wct, primaryContainer, primaryRectBounds); + final TaskFragmentContainer secondaryContainer = splitContainer.getSecondaryContainer(); + resizeTaskFragmentIfRegistered(wct, secondaryContainer, secondaryRectBounds); + + setAdjacentTaskFragments(wct, primaryContainer.getTaskFragmentToken(), + secondaryContainer.getTaskFragmentToken(), rule); } /** diff --git a/libs/WindowManager/Shell/res/color/unfold_transition_background.xml b/libs/WindowManager/Shell/res/color/unfold_transition_background.xml new file mode 100644 index 000000000000..63289a3f75d9 --- /dev/null +++ b/libs/WindowManager/Shell/res/color/unfold_transition_background.xml @@ -0,0 +1,19 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2021 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. +--> +<selector xmlns:android="http://schemas.android.com/apk/res/android"> + <!-- Matches taskbar color --> + <item android:color="@android:color/system_neutral2_500" android:lStar="35" /> +</selector> diff --git a/libs/WindowManager/Shell/res/layout/split_outline.xml b/libs/WindowManager/Shell/res/layout/split_outline.xml index 4e2a77f213a0..13a30f5a0423 100644 --- a/libs/WindowManager/Shell/res/layout/split_outline.xml +++ b/libs/WindowManager/Shell/res/layout/split_outline.xml @@ -13,7 +13,7 @@ See the License for the specific language governing permissions and limitations under the License. --> -<com.android.wm.shell.splitscreen.OutlineRoot +<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent"> @@ -23,4 +23,4 @@ android:layout_height="match_parent" android:layout_width="match_parent" /> -</com.android.wm.shell.splitscreen.OutlineRoot> +</FrameLayout> diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/ShellInitImpl.java b/libs/WindowManager/Shell/src/com/android/wm/shell/ShellInitImpl.java index df4f2383c062..fa58fcda3d3b 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/ShellInitImpl.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/ShellInitImpl.java @@ -27,6 +27,8 @@ import com.android.wm.shell.common.ShellExecutor; import com.android.wm.shell.common.annotations.ExternalThread; import com.android.wm.shell.draganddrop.DragAndDropController; import com.android.wm.shell.freeform.FreeformTaskListener; +import com.android.wm.shell.fullscreen.FullscreenTaskListener; +import com.android.wm.shell.fullscreen.FullscreenUnfoldController; import com.android.wm.shell.legacysplitscreen.LegacySplitScreenController; import com.android.wm.shell.pip.phone.PipTouchHandler; import com.android.wm.shell.splitscreen.SplitScreenController; @@ -52,6 +54,7 @@ public class ShellInitImpl { private final Optional<AppPairsController> mAppPairsOptional; private final Optional<PipTouchHandler> mPipTouchHandlerOptional; private final FullscreenTaskListener mFullscreenTaskListener; + private final Optional<FullscreenUnfoldController> mFullscreenUnfoldController; private final Optional<FreeformTaskListener> mFreeformTaskListenerOptional; private final ShellExecutor mMainExecutor; private final Transitions mTransitions; @@ -71,6 +74,7 @@ public class ShellInitImpl { Optional<AppPairsController> appPairsOptional, Optional<PipTouchHandler> pipTouchHandlerOptional, FullscreenTaskListener fullscreenTaskListener, + Optional<FullscreenUnfoldController> fullscreenUnfoldTransitionController, Optional<Optional<FreeformTaskListener>> freeformTaskListenerOptional, Transitions transitions, StartingWindowController startingWindow, @@ -86,6 +90,7 @@ public class ShellInitImpl { mAppPairsOptional = appPairsOptional; mFullscreenTaskListener = fullscreenTaskListener; mPipTouchHandlerOptional = pipTouchHandlerOptional; + mFullscreenUnfoldController = fullscreenUnfoldTransitionController; mFreeformTaskListenerOptional = freeformTaskListenerOptional.flatMap(f -> f); mTransitions = transitions; mMainExecutor = mainExecutor; @@ -128,6 +133,8 @@ public class ShellInitImpl { mFreeformTaskListenerOptional.ifPresent(f -> mShellTaskOrganizer.addListenerForType( f, ShellTaskOrganizer.TASK_LISTENER_TYPE_FREEFORM)); + + mFullscreenUnfoldController.ifPresent(FullscreenUnfoldController::init); } @ExternalThread diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/apppairs/AppPair.java b/libs/WindowManager/Shell/src/com/android/wm/shell/apppairs/AppPair.java index c2cb72a530a3..10d7725b6184 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/apppairs/AppPair.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/apppairs/AppPair.java @@ -27,7 +27,6 @@ import static com.android.wm.shell.common.split.SplitLayout.SPLIT_POSITION_UNDEF import static com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_TASK_ORG; import android.app.ActivityManager; -import android.graphics.Rect; import android.view.SurfaceControl; import android.view.SurfaceSession; import android.window.WindowContainerToken; @@ -40,6 +39,7 @@ import com.android.internal.protolog.common.ProtoLog; import com.android.wm.shell.ShellTaskOrganizer; import com.android.wm.shell.common.DisplayController; import com.android.wm.shell.common.DisplayImeController; +import com.android.wm.shell.common.DisplayInsetsController; import com.android.wm.shell.common.SurfaceUtils; import com.android.wm.shell.common.SyncTransactionQueue; import com.android.wm.shell.common.split.SplitLayout; @@ -69,6 +69,7 @@ class AppPair implements ShellTaskOrganizer.TaskListener, SplitLayout.SplitLayou private final SyncTransactionQueue mSyncQueue; private final DisplayController mDisplayController; private final DisplayImeController mDisplayImeController; + private final DisplayInsetsController mDisplayInsetsController; private SplitLayout mSplitLayout; private final SplitWindowManager.ParentContainerCallbacks mParentContainerCallbacks = @@ -80,7 +81,12 @@ class AppPair implements ShellTaskOrganizer.TaskListener, SplitLayout.SplitLayou @Override public void onLeashReady(SurfaceControl leash) { - mSyncQueue.runInSync(t -> t.show(leash)); + mSyncQueue.runInSync(t -> t + .show(leash) + .setLayer(leash, SPLIT_DIVIDER_LAYER) + .setPosition(leash, + mSplitLayout.getDividerBounds().left, + mSplitLayout.getDividerBounds().top)); } }; @@ -89,6 +95,7 @@ class AppPair implements ShellTaskOrganizer.TaskListener, SplitLayout.SplitLayou mSyncQueue = controller.getSyncTransactionQueue(); mDisplayController = controller.getDisplayController(); mDisplayImeController = controller.getDisplayImeController(); + mDisplayInsetsController = controller.getDisplayInsetsController(); } int getRootTaskId() { @@ -125,6 +132,7 @@ class AppPair implements ShellTaskOrganizer.TaskListener, SplitLayout.SplitLayou mDisplayController.getDisplayContext(mRootTaskInfo.displayId), mRootTaskInfo.configuration, this /* layoutChangeListener */, mParentContainerCallbacks, mDisplayImeController, mController.getTaskOrganizer()); + mDisplayInsetsController.addInsetsChangedListener(mRootTaskInfo.displayId, mSplitLayout); final WindowContainerToken token1 = task1.token; final WindowContainerToken token2 = task2.token; @@ -190,22 +198,17 @@ class AppPair implements ShellTaskOrganizer.TaskListener, SplitLayout.SplitLayou if (mTaskLeash1 == null || mTaskLeash2 == null) return; mSplitLayout.init(); - final SurfaceControl dividerLeash = mSplitLayout.getDividerLeash(); - final Rect dividerBounds = mSplitLayout.getDividerBounds(); - - // TODO: Is there more we need to do here? - mSyncQueue.runInSync(t -> { - t.setLayer(dividerLeash, SPLIT_DIVIDER_LAYER) - .setPosition(mTaskLeash1, mTaskInfo1.positionInParent.x, - mTaskInfo1.positionInParent.y) - .setPosition(mTaskLeash2, mTaskInfo2.positionInParent.x, - mTaskInfo2.positionInParent.y) - .setPosition(dividerLeash, dividerBounds.left, dividerBounds.top) - .show(dividerLeash) - .show(mRootTaskLeash) - .show(mTaskLeash1) - .show(mTaskLeash2); - }); + + mSyncQueue.runInSync(t -> t + .show(mRootTaskLeash) + .show(mTaskLeash1) + .show(mTaskLeash2) + .setPosition(mTaskLeash1, + mTaskInfo1.positionInParent.x, + mTaskInfo1.positionInParent.y) + .setPosition(mTaskLeash2, + mTaskInfo2.positionInParent.x, + mTaskInfo2.positionInParent.y)); } @Override @@ -227,10 +230,9 @@ class AppPair implements ShellTaskOrganizer.TaskListener, SplitLayout.SplitLayou } mRootTaskInfo = taskInfo; - if (mSplitLayout != null) { - if (mSplitLayout.updateConfiguration(mRootTaskInfo.configuration)) { - onLayoutChanged(mSplitLayout); - } + if (mSplitLayout != null + && mSplitLayout.updateConfiguration(mRootTaskInfo.configuration)) { + onLayoutChanged(mSplitLayout); } } else if (taskInfo.taskId == getTaskId1()) { mTaskInfo1 = taskInfo; diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/apppairs/AppPairsController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/apppairs/AppPairsController.java index b159333e9a0e..53234ab971d6 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/apppairs/AppPairsController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/apppairs/AppPairsController.java @@ -29,6 +29,7 @@ import com.android.internal.protolog.common.ProtoLog; import com.android.wm.shell.ShellTaskOrganizer; import com.android.wm.shell.common.DisplayController; import com.android.wm.shell.common.DisplayImeController; +import com.android.wm.shell.common.DisplayInsetsController; import com.android.wm.shell.common.ShellExecutor; import com.android.wm.shell.common.SyncTransactionQueue; @@ -50,14 +51,17 @@ public class AppPairsController { private final SparseArray<AppPair> mActiveAppPairs = new SparseArray<>(); private final DisplayController mDisplayController; private final DisplayImeController mDisplayImeController; + private final DisplayInsetsController mDisplayInsetsController; public AppPairsController(ShellTaskOrganizer organizer, SyncTransactionQueue syncQueue, DisplayController displayController, ShellExecutor mainExecutor, - DisplayImeController displayImeController) { + DisplayImeController displayImeController, + DisplayInsetsController displayInsetsController) { mTaskOrganizer = organizer; mSyncQueue = syncQueue; mDisplayController = displayController; mDisplayImeController = displayImeController; + mDisplayInsetsController = displayInsetsController; mMainExecutor = mainExecutor; } @@ -148,6 +152,10 @@ public class AppPairsController { return mDisplayImeController; } + DisplayInsetsController getDisplayInsetsController() { + return mDisplayInsetsController; + } + public void dump(@NonNull PrintWriter pw, String prefix) { final String innerPrefix = prefix + " "; final String childPrefix = innerPrefix + " "; diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java index 1c308a3daf34..596a2f4467c3 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java @@ -189,24 +189,28 @@ public final class SplitLayout implements DisplayInsetsController.OnInsetsChange final int rotation = configuration.windowConfiguration.getRotation(); final Rect rootBounds = configuration.windowConfiguration.getBounds(); final int orientation = configuration.orientation; - if (rotation != mRotation || !mRootBounds.equals(rootBounds) - || orientation != mOrientation) { - mContext = mContext.createConfigurationContext(configuration); - mSplitWindowManager.setConfiguration(configuration); - mOrientation = orientation; - mTempRect.set(mRootBounds); - mRootBounds.set(rootBounds); - mDividerSnapAlgorithm = getSnapAlgorithm(mContext, mRootBounds); - initDividerPosition(mTempRect); - affectsLayout = true; + + if (mOrientation == orientation + && rotation == mRotation + && mRootBounds.equals(rootBounds)) { + return false; } + mContext = mContext.createConfigurationContext(configuration); + mSplitWindowManager.setConfiguration(configuration); + mOrientation = orientation; + mTempRect.set(mRootBounds); + mRootBounds.set(rootBounds); + mRotation = rotation; + mDividerSnapAlgorithm = getSnapAlgorithm(mContext, mRootBounds); + initDividerPosition(mTempRect); + if (mInitialized) { release(); init(); } - return affectsLayout; + return true; } private void initDividerPosition(Rect oldBounds) { diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/FullscreenTaskListener.java b/libs/WindowManager/Shell/src/com/android/wm/shell/fullscreen/FullscreenTaskListener.java index 006730d333eb..3f17f2ba9394 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/FullscreenTaskListener.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/fullscreen/FullscreenTaskListener.java @@ -14,25 +14,31 @@ * limitations under the License. */ -package com.android.wm.shell; +package com.android.wm.shell.fullscreen; + +import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME; import static com.android.wm.shell.ShellTaskOrganizer.TASK_LISTENER_TYPE_FULLSCREEN; import static com.android.wm.shell.ShellTaskOrganizer.taskListenerTypeToString; -import android.app.ActivityManager; +import android.app.ActivityManager.RunningTaskInfo; +import android.app.TaskInfo; import android.graphics.Point; import android.util.Slog; import android.util.SparseArray; +import android.util.SparseBooleanArray; import android.view.SurfaceControl; import androidx.annotation.NonNull; import com.android.internal.protolog.common.ProtoLog; +import com.android.wm.shell.ShellTaskOrganizer; import com.android.wm.shell.common.SyncTransactionQueue; import com.android.wm.shell.protolog.ShellProtoLogGroup; import com.android.wm.shell.transition.Transitions; import java.io.PrintWriter; +import java.util.Optional; /** * Organizes tasks presented in {@link android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN}. @@ -43,13 +49,17 @@ public class FullscreenTaskListener implements ShellTaskOrganizer.TaskListener { private final SyncTransactionQueue mSyncQueue; private final SparseArray<TaskData> mDataByTaskId = new SparseArray<>(); + private final AnimatableTasksListener mAnimatableTasksListener = new AnimatableTasksListener(); + private final FullscreenUnfoldController mFullscreenUnfoldController; - public FullscreenTaskListener(SyncTransactionQueue syncQueue) { + public FullscreenTaskListener(SyncTransactionQueue syncQueue, + Optional<FullscreenUnfoldController> unfoldController) { mSyncQueue = syncQueue; + mFullscreenUnfoldController = unfoldController.orElse(null); } @Override - public void onTaskAppeared(ActivityManager.RunningTaskInfo taskInfo, SurfaceControl leash) { + public void onTaskAppeared(RunningTaskInfo taskInfo, SurfaceControl leash) { if (mDataByTaskId.get(taskInfo.taskId) != null) { throw new IllegalStateException("Task appeared more than once: #" + taskInfo.taskId); } @@ -67,11 +77,16 @@ public class FullscreenTaskListener implements ShellTaskOrganizer.TaskListener { t.setMatrix(leash, 1, 0, 0, 1); t.show(leash); }); + + mAnimatableTasksListener.onTaskAppeared(taskInfo); } @Override - public void onTaskInfoChanged(ActivityManager.RunningTaskInfo taskInfo) { + public void onTaskInfoChanged(RunningTaskInfo taskInfo) { if (Transitions.ENABLE_SHELL_TRANSITIONS) return; + + mAnimatableTasksListener.onTaskInfoChanged(taskInfo); + final TaskData data = mDataByTaskId.get(taskInfo.taskId); final Point positionInParent = taskInfo.positionInParent; if (!positionInParent.equals(data.positionInParent)) { @@ -83,12 +98,15 @@ public class FullscreenTaskListener implements ShellTaskOrganizer.TaskListener { } @Override - public void onTaskVanished(ActivityManager.RunningTaskInfo taskInfo) { + public void onTaskVanished(RunningTaskInfo taskInfo) { if (mDataByTaskId.get(taskInfo.taskId) == null) { Slog.e(TAG, "Task already vanished: #" + taskInfo.taskId); return; } + + mAnimatableTasksListener.onTaskVanished(taskInfo); mDataByTaskId.remove(taskInfo.taskId); + ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TASK_ORG, "Fullscreen Task Vanished: #%d", taskInfo.taskId); } @@ -125,4 +143,65 @@ public class FullscreenTaskListener implements ShellTaskOrganizer.TaskListener { this.positionInParent = positionInParent; } } + + class AnimatableTasksListener { + private final SparseBooleanArray mTaskIds = new SparseBooleanArray(); + + public void onTaskAppeared(RunningTaskInfo taskInfo) { + final boolean isApplicable = isAnimatable(taskInfo); + if (isApplicable) { + mTaskIds.put(taskInfo.taskId, true); + + if (mFullscreenUnfoldController != null) { + SurfaceControl leash = mDataByTaskId.get(taskInfo.taskId).surface; + mFullscreenUnfoldController.onTaskAppeared(taskInfo, leash); + } + } + } + + public void onTaskInfoChanged(RunningTaskInfo taskInfo) { + final boolean isCurrentlyApplicable = mTaskIds.get(taskInfo.taskId); + final boolean isApplicable = isAnimatable(taskInfo); + + if (isCurrentlyApplicable) { + if (isApplicable) { + // Still applicable, send update + if (mFullscreenUnfoldController != null) { + mFullscreenUnfoldController.onTaskInfoChanged(taskInfo); + } + } else { + // Became inapplicable + if (mFullscreenUnfoldController != null) { + mFullscreenUnfoldController.onTaskVanished(taskInfo); + } + mTaskIds.put(taskInfo.taskId, false); + } + } else { + if (isApplicable) { + // Became applicable + mTaskIds.put(taskInfo.taskId, true); + + if (mFullscreenUnfoldController != null) { + SurfaceControl leash = mDataByTaskId.get(taskInfo.taskId).surface; + mFullscreenUnfoldController.onTaskAppeared(taskInfo, leash); + } + } + } + } + + public void onTaskVanished(RunningTaskInfo taskInfo) { + final boolean isCurrentlyApplicable = mTaskIds.get(taskInfo.taskId); + if (isCurrentlyApplicable && mFullscreenUnfoldController != null) { + mFullscreenUnfoldController.onTaskVanished(taskInfo); + } + mTaskIds.put(taskInfo.taskId, false); + } + + private boolean isAnimatable(TaskInfo taskInfo) { + // Filter all visible tasks that are not launcher tasks + // We do not animate launcher as it handles the animation by itself + return taskInfo != null && taskInfo.isVisible && taskInfo.getConfiguration() + .windowConfiguration.getActivityType() != ACTIVITY_TYPE_HOME; + } + } } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/fullscreen/FullscreenUnfoldController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/fullscreen/FullscreenUnfoldController.java new file mode 100644 index 000000000000..08ab85cab97b --- /dev/null +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/fullscreen/FullscreenUnfoldController.java @@ -0,0 +1,268 @@ +/* + * Copyright (C) 2021 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.wm.shell.fullscreen; + +import static android.graphics.Color.blue; +import static android.graphics.Color.green; +import static android.graphics.Color.red; +import static android.util.MathUtils.lerp; +import static android.view.Display.DEFAULT_DISPLAY; + +import android.animation.RectEvaluator; +import android.animation.TypeEvaluator; +import android.annotation.NonNull; +import android.app.ActivityManager; +import android.app.TaskInfo; +import android.content.Context; +import android.graphics.Matrix; +import android.graphics.Rect; +import android.util.SparseArray; +import android.view.InsetsSource; +import android.view.InsetsState; +import android.view.SurfaceControl; + +import com.android.internal.policy.ScreenDecorationsUtils; +import com.android.wm.shell.R; +import com.android.wm.shell.RootTaskDisplayAreaOrganizer; +import com.android.wm.shell.common.DisplayInsetsController; +import com.android.wm.shell.common.DisplayInsetsController.OnInsetsChangedListener; +import com.android.wm.shell.unfold.ShellUnfoldProgressProvider; +import com.android.wm.shell.unfold.ShellUnfoldProgressProvider.UnfoldListener; + +import java.util.concurrent.Executor; + +/** + * Controls full screen app unfold transition: animating cropping window and scaling when + * folding or unfolding a foldable device. + */ +public final class FullscreenUnfoldController implements UnfoldListener, + OnInsetsChangedListener { + + private static final float[] FLOAT_9 = new float[9]; + private static final TypeEvaluator<Rect> RECT_EVALUATOR = new RectEvaluator(new Rect()); + + private static final float HORIZONTAL_START_MARGIN = 0.08f; + private static final float VERTICAL_START_MARGIN = 0.03f; + private static final float END_SCALE = 1f; + private static final float START_SCALE = END_SCALE - VERTICAL_START_MARGIN * 2; + private static final int BACKGROUND_LAYER_Z_INDEX = -1; + + private final Context mContext; + private final Executor mExecutor; + private final ShellUnfoldProgressProvider mProgressProvider; + private final RootTaskDisplayAreaOrganizer mRootTaskDisplayAreaOrganizer; + private final DisplayInsetsController mDisplayInsetsController; + + private final SparseArray<AnimationContext> mAnimationContextByTaskId = new SparseArray<>(); + + private SurfaceControl mBackgroundLayer; + private InsetsSource mTaskbarInsetsSource; + + private final float mWindowCornerRadiusPx; + private final float[] mBackgroundColor; + private final float mExpandedTaskBarHeight; + + private final SurfaceControl.Transaction mTransaction = new SurfaceControl.Transaction(); + + public FullscreenUnfoldController( + @NonNull Context context, + @NonNull Executor executor, + @NonNull ShellUnfoldProgressProvider progressProvider, + @NonNull RootTaskDisplayAreaOrganizer rootTaskDisplayAreaOrganizer, + @NonNull DisplayInsetsController displayInsetsController + ) { + mContext = context; + mExecutor = executor; + mRootTaskDisplayAreaOrganizer = rootTaskDisplayAreaOrganizer; + mProgressProvider = progressProvider; + mDisplayInsetsController = displayInsetsController; + mWindowCornerRadiusPx = ScreenDecorationsUtils.getWindowCornerRadius(context); + mExpandedTaskBarHeight = context.getResources().getDimensionPixelSize( + com.android.internal.R.dimen.taskbar_frame_height); + mBackgroundColor = getBackgroundColor(); + } + + /** + * Initializes the controller + */ + public void init() { + mProgressProvider.addListener(mExecutor, this); + mDisplayInsetsController.addInsetsChangedListener(DEFAULT_DISPLAY, this); + } + + @Override + public void onStateChangeProgress(float progress) { + if (mAnimationContextByTaskId.size() == 0) return; + + ensureBackground(); + + for (int i = mAnimationContextByTaskId.size() - 1; i >= 0; i--) { + final AnimationContext context = mAnimationContextByTaskId.valueAt(i); + + context.mCurrentCropRect.set(RECT_EVALUATOR + .evaluate(progress, context.mStartCropRect, context.mEndCropRect)); + + float scale = lerp(START_SCALE, END_SCALE, progress); + context.mMatrix.setScale(scale, scale, context.mCurrentCropRect.exactCenterX(), + context.mCurrentCropRect.exactCenterY()); + + mTransaction.setWindowCrop(context.mLeash, context.mCurrentCropRect) + .setMatrix(context.mLeash, context.mMatrix, FLOAT_9) + .setCornerRadius(context.mLeash, mWindowCornerRadiusPx); + } + + mTransaction.apply(); + } + + @Override + public void onStateChangeFinished() { + for (int i = mAnimationContextByTaskId.size() - 1; i >= 0; i--) { + final AnimationContext context = mAnimationContextByTaskId.valueAt(i); + resetSurface(context); + } + + removeBackground(); + mTransaction.apply(); + } + + @Override + public void insetsChanged(InsetsState insetsState) { + mTaskbarInsetsSource = insetsState.getSource(InsetsState.ITYPE_EXTRA_NAVIGATION_BAR); + for (int i = mAnimationContextByTaskId.size() - 1; i >= 0; i--) { + AnimationContext context = mAnimationContextByTaskId.valueAt(i); + context.update(mTaskbarInsetsSource, context.mTaskInfo); + } + } + + /** + * Called when a new matching task appeared + */ + public void onTaskAppeared(ActivityManager.RunningTaskInfo taskInfo, SurfaceControl leash) { + AnimationContext animationContext = new AnimationContext(leash, mTaskbarInsetsSource, + taskInfo); + mAnimationContextByTaskId.put(taskInfo.taskId, animationContext); + } + + /** + * Called when matching task changed + */ + public void onTaskInfoChanged(ActivityManager.RunningTaskInfo taskInfo) { + AnimationContext animationContext = mAnimationContextByTaskId.get(taskInfo.taskId); + if (animationContext != null) { + animationContext.update(mTaskbarInsetsSource, taskInfo); + } + } + + /** + * Called when matching task vanished + */ + public void onTaskVanished(ActivityManager.RunningTaskInfo taskInfo) { + AnimationContext animationContext = mAnimationContextByTaskId.get(taskInfo.taskId); + if (animationContext != null) { + resetSurface(animationContext); + mAnimationContextByTaskId.remove(taskInfo.taskId); + } + + if (mAnimationContextByTaskId.size() == 0) { + removeBackground(); + } + + mTransaction.apply(); + } + + private void resetSurface(AnimationContext context) { + mTransaction + .setWindowCrop(context.mLeash, null) + .setCornerRadius(context.mLeash, 0.0F) + .setMatrix(context.mLeash, 1.0F, 0.0F, 0.0F, 1.0F) + .setPosition(context.mLeash, + (float) context.mTaskInfo.positionInParent.x, + (float) context.mTaskInfo.positionInParent.y); + } + + private void ensureBackground() { + if (mBackgroundLayer != null) return; + + SurfaceControl.Builder colorLayerBuilder = new SurfaceControl.Builder() + .setName("app-unfold-background") + .setCallsite("AppUnfoldTransitionController") + .setColorLayer(); + mRootTaskDisplayAreaOrganizer.attachToDisplayArea(DEFAULT_DISPLAY, colorLayerBuilder); + mBackgroundLayer = colorLayerBuilder.build(); + + mTransaction + .setColor(mBackgroundLayer, mBackgroundColor) + .show(mBackgroundLayer) + .setLayer(mBackgroundLayer, BACKGROUND_LAYER_Z_INDEX); + } + + private void removeBackground() { + if (mBackgroundLayer == null) return; + if (mBackgroundLayer.isValid()) { + mTransaction.remove(mBackgroundLayer); + } + mBackgroundLayer = null; + } + + private float[] getBackgroundColor() { + int colorInt = mContext.getResources().getColor(R.color.unfold_transition_background); + return new float[]{ + (float) red(colorInt) / 255.0F, + (float) green(colorInt) / 255.0F, + (float) blue(colorInt) / 255.0F + }; + } + + private class AnimationContext { + final SurfaceControl mLeash; + final Rect mStartCropRect = new Rect(); + final Rect mEndCropRect = new Rect(); + final Rect mCurrentCropRect = new Rect(); + final Matrix mMatrix = new Matrix(); + + TaskInfo mTaskInfo; + + private AnimationContext(SurfaceControl leash, + InsetsSource taskBarInsetsSource, + TaskInfo taskInfo) { + this.mLeash = leash; + update(taskBarInsetsSource, taskInfo); + } + + private void update(InsetsSource taskBarInsetsSource, TaskInfo taskInfo) { + mTaskInfo = taskInfo; + mStartCropRect.set(mTaskInfo.getConfiguration().windowConfiguration.getBounds()); + + if (taskBarInsetsSource != null) { + // Only insets the cropping window with task bar when it's expanded + if (taskBarInsetsSource.getFrame().height() >= mExpandedTaskBarHeight) { + mStartCropRect.inset(taskBarInsetsSource + .calculateVisibleInsets(mStartCropRect)); + } + } + + mEndCropRect.set(mStartCropRect); + + int horizontalMargin = (int) (mEndCropRect.width() * HORIZONTAL_START_MARGIN); + mStartCropRect.left = mEndCropRect.left + horizontalMargin; + mStartCropRect.right = mEndCropRect.right - horizontalMargin; + int verticalMargin = (int) (mEndCropRect.height() * VERTICAL_START_MARGIN); + mStartCropRect.top = mEndCropRect.top + verticalMargin; + mStartCropRect.bottom = mEndCropRect.bottom - verticalMargin; + } + } +} diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/OutlineManager.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/OutlineManager.java index 0b763f2d05f7..a459c8dbfa34 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/OutlineManager.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/OutlineManager.java @@ -22,19 +22,23 @@ import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_NO_MOVE_ANIMA import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_TRUSTED_OVERLAY; import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY; +import android.annotation.Nullable; import android.content.Context; import android.content.res.Configuration; import android.graphics.PixelFormat; import android.graphics.Rect; import android.os.Binder; import android.view.IWindow; +import android.view.InsetsSource; +import android.view.InsetsState; import android.view.LayoutInflater; import android.view.SurfaceControl; import android.view.SurfaceControlViewHost; -import android.view.WindowInsets; +import android.view.View; +import android.view.ViewGroup; import android.view.WindowManager; -import android.view.WindowMetrics; import android.view.WindowlessWindowManager; +import android.widget.FrameLayout; import com.android.wm.shell.R; @@ -45,17 +49,22 @@ import com.android.wm.shell.R; class OutlineManager extends WindowlessWindowManager { private static final String WINDOW_NAME = "SplitOutlineLayer"; private final Context mContext; - private final Rect mOutlineBounds = new Rect(); - private final Rect mTmpBounds = new Rect(); + private final Rect mRootBounds = new Rect(); + private final Rect mTempRect = new Rect(); + private final Rect mLastOutlineBounds = new Rect(); + private final InsetsState mInsetsState = new InsetsState(); + private final int mExpandedTaskBarHeight; + private OutlineView mOutlineView; private SurfaceControlViewHost mViewHost; private SurfaceControl mHostLeash; private SurfaceControl mLeash; - private int mOutlineColor; OutlineManager(Context context, Configuration configuration) { super(configuration, null /* rootSurface */, null /* hostInputToken */); mContext = context.createWindowContext(context.getDisplay(), TYPE_APPLICATION_OVERLAY, null /* options */); + mExpandedTaskBarHeight = mContext.getResources().getDimensionPixelSize( + com.android.internal.R.dimen.taskbar_frame_height); } @Override @@ -63,65 +72,110 @@ class OutlineManager extends WindowlessWindowManager { b.setParent(mHostLeash); } - boolean drawOutlineBounds(Rect rootBounds) { - if (mLeash == null || mViewHost == null) return false; - - computeOutlineBounds(mContext, rootBounds, mTmpBounds); - if (mOutlineBounds.equals(mTmpBounds)) { - return false; - } - mOutlineBounds.set(mTmpBounds); - - ((OutlineRoot) mViewHost.getView()).updateOutlineBounds(mOutlineBounds, mOutlineColor); - final WindowManager.LayoutParams lp = - (WindowManager.LayoutParams) mViewHost.getView().getLayoutParams(); - lp.width = rootBounds.width(); - lp.height = rootBounds.height(); - mViewHost.relayout(lp); - - return true; - } - - void inflate(SurfaceControl.Transaction t, SurfaceControl hostLeash, int color) { + void inflate(SurfaceControl rootLeash, Rect rootBounds) { if (mLeash != null || mViewHost != null) return; - mHostLeash = hostLeash; - mOutlineColor = color; + mHostLeash = rootLeash; + mRootBounds.set(rootBounds); mViewHost = new SurfaceControlViewHost(mContext, mContext.getDisplay(), this); - final OutlineRoot rootView = (OutlineRoot) LayoutInflater.from(mContext) + + final FrameLayout rootLayout = (FrameLayout) LayoutInflater.from(mContext) .inflate(R.layout.split_outline, null); + mOutlineView = rootLayout.findViewById(R.id.split_outline); final WindowManager.LayoutParams lp = new WindowManager.LayoutParams( 0 /* width */, 0 /* height */, TYPE_APPLICATION_OVERLAY, FLAG_NOT_FOCUSABLE | FLAG_NOT_TOUCHABLE, PixelFormat.TRANSLUCENT); + lp.width = mRootBounds.width(); + lp.height = mRootBounds.height(); lp.token = new Binder(); lp.setTitle(WINDOW_NAME); lp.privateFlags |= PRIVATE_FLAG_NO_MOVE_ANIMATION | PRIVATE_FLAG_TRUSTED_OVERLAY; // TODO(b/189839391): Set INPUT_FEATURE_NO_INPUT_CHANNEL after WM supports // TRUSTED_OVERLAY for windowless window without input channel. - mViewHost.setView(rootView, lp); + mViewHost.setView(rootLayout, lp); mLeash = getSurfaceControl(mViewHost.getWindowToken()); - t.setLayer(mLeash, Integer.MAX_VALUE); + + drawOutline(); } void release() { if (mViewHost != null) { mViewHost.release(); + mViewHost = null; } + mRootBounds.setEmpty(); + mLastOutlineBounds.setEmpty(); + mOutlineView = null; + mHostLeash = null; + mLeash = null; + } + + @Nullable + SurfaceControl getOutlineLeash() { + return mLeash; } - private static void computeOutlineBounds(Context context, Rect rootBounds, Rect outBounds) { - computeDisplayStableBounds(context, outBounds); - outBounds.intersect(rootBounds); + void setVisibility(boolean visible) { + if (mOutlineView != null) { + mOutlineView.setVisibility(visible ? View.VISIBLE : View.INVISIBLE); + } + } + + void setRootBounds(Rect rootBounds) { + if (mViewHost == null || mViewHost.getView() == null) { + return; + } + + if (!mRootBounds.equals(rootBounds)) { + WindowManager.LayoutParams lp = + (WindowManager.LayoutParams) mViewHost.getView().getLayoutParams(); + lp.width = rootBounds.width(); + lp.height = rootBounds.height(); + mViewHost.relayout(lp); + mRootBounds.set(rootBounds); + drawOutline(); + } + } + + void onInsetsChanged(InsetsState insetsState) { + if (!mInsetsState.equals(insetsState)) { + mInsetsState.set(insetsState); + drawOutline(); + } + } + + private void computeOutlineBounds(Rect rootBounds, InsetsState insetsState, Rect outBounds) { + outBounds.set(rootBounds); + final InsetsSource taskBarInsetsSource = + insetsState.getSource(InsetsState.ITYPE_EXTRA_NAVIGATION_BAR); + // Only insets the divider bar with task bar when it's expanded so that the rounded corners + // will be drawn against task bar. + if (taskBarInsetsSource.getFrame().height() >= mExpandedTaskBarHeight) { + outBounds.inset(taskBarInsetsSource.calculateVisibleInsets(outBounds)); + } + // Offset the coordinate from screen based to surface based. outBounds.offset(-rootBounds.left, -rootBounds.top); } - private static void computeDisplayStableBounds(Context context, Rect outBounds) { - final WindowMetrics windowMetrics = - context.getSystemService(WindowManager.class).getMaximumWindowMetrics(); - outBounds.set(windowMetrics.getBounds()); - outBounds.inset(windowMetrics.getWindowInsets().getInsets( - WindowInsets.Type.systemBars() | WindowInsets.Type.displayCutout())); + void drawOutline() { + if (mOutlineView == null) { + return; + } + + computeOutlineBounds(mRootBounds, mInsetsState, mTempRect); + if (mTempRect.equals(mLastOutlineBounds)) { + return; + } + + ViewGroup.MarginLayoutParams lp = + (ViewGroup.MarginLayoutParams) mOutlineView.getLayoutParams(); + lp.leftMargin = mTempRect.left; + lp.topMargin = mTempRect.top; + lp.width = mTempRect.width(); + lp.height = mTempRect.height(); + mOutlineView.setLayoutParams(lp); + mLastOutlineBounds.set(mTempRect); } } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/OutlineRoot.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/OutlineRoot.java deleted file mode 100644 index 71d48eeca71d..000000000000 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/OutlineRoot.java +++ /dev/null @@ -1,62 +0,0 @@ -/* - * Copyright (C) 2021 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.wm.shell.splitscreen; - -import android.content.Context; -import android.graphics.Rect; -import android.util.AttributeSet; -import android.widget.FrameLayout; - -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; - -import com.android.wm.shell.R; - -/** Root layout for holding split outline. */ -public class OutlineRoot extends FrameLayout { - public OutlineRoot(@NonNull Context context) { - super(context); - } - - public OutlineRoot(@NonNull Context context, - @Nullable AttributeSet attrs) { - super(context, attrs); - } - - public OutlineRoot(@NonNull Context context, @Nullable AttributeSet attrs, - int defStyleAttr) { - super(context, attrs, defStyleAttr); - } - - public OutlineRoot(@NonNull Context context, @Nullable AttributeSet attrs, - int defStyleAttr, - int defStyleRes) { - super(context, attrs, defStyleAttr, defStyleRes); - } - - private OutlineView mOutlineView; - - @Override - protected void onFinishInflate() { - super.onFinishInflate(); - mOutlineView = findViewById(R.id.split_outline); - } - - void updateOutlineBounds(Rect bounds, int color) { - mOutlineView.updateOutlineBounds(bounds, color); - } -} diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/OutlineView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/OutlineView.java index ea66180e3dd2..94dd9b24875a 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/OutlineView.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/OutlineView.java @@ -16,13 +16,17 @@ package com.android.wm.shell.splitscreen; +import static android.view.RoundedCorner.POSITION_BOTTOM_LEFT; +import static android.view.RoundedCorner.POSITION_BOTTOM_RIGHT; +import static android.view.RoundedCorner.POSITION_TOP_LEFT; +import static android.view.RoundedCorner.POSITION_TOP_RIGHT; + import android.content.Context; import android.graphics.Canvas; import android.graphics.Paint; import android.graphics.Path; -import android.graphics.Rect; -import android.graphics.Region; import android.util.AttributeSet; +import android.view.RoundedCorner; import android.view.View; import androidx.annotation.NonNull; @@ -33,44 +37,46 @@ import com.android.internal.R; /** View for drawing split outline. */ public class OutlineView extends View { private final Paint mPaint = new Paint(); - private final Rect mBounds = new Rect(); - - public OutlineView(@NonNull Context context) { - super(context); - } + private final Path mPath = new Path(); + private final float[] mRadii = new float[8]; - public OutlineView(@NonNull Context context, - @Nullable AttributeSet attrs) { + public OutlineView(@NonNull Context context, @Nullable AttributeSet attrs) { super(context, attrs); + mPaint.setStyle(Paint.Style.STROKE); + mPaint.setStrokeWidth( + getResources().getDimension(R.dimen.accessibility_focus_highlight_stroke_width)); + mPaint.setColor(getResources().getColor(R.color.system_accent1_100, null)); } - public OutlineView(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr) { - super(context, attrs, defStyleAttr); + @Override + protected void onAttachedToWindow() { + // TODO(b/200850654): match the screen corners with the actual display decor. + mRadii[0] = mRadii[1] = getCornerRadius(POSITION_TOP_LEFT); + mRadii[2] = mRadii[3] = getCornerRadius(POSITION_TOP_RIGHT); + mRadii[4] = mRadii[5] = getCornerRadius(POSITION_BOTTOM_RIGHT); + mRadii[6] = mRadii[7] = getCornerRadius(POSITION_BOTTOM_LEFT); } - public OutlineView(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr, - int defStyleRes) { - super(context, attrs, defStyleAttr, defStyleRes); + private int getCornerRadius(@RoundedCorner.Position int position) { + final RoundedCorner roundedCorner = getDisplay().getRoundedCorner(position); + return roundedCorner == null ? 0 : roundedCorner.getRadius(); } @Override - protected void onFinishInflate() { - super.onFinishInflate(); - mPaint.setStyle(Paint.Style.STROKE); - mPaint.setStrokeWidth(getResources() - .getDimension(R.dimen.accessibility_focus_highlight_stroke_width)); + protected void onLayout(boolean changed, int left, int top, int right, int bottom) { + if (changed) { + mPath.reset(); + mPath.addRoundRect(0, 0, getWidth(), getHeight(), mRadii, Path.Direction.CW); + } } - void updateOutlineBounds(Rect bounds, int color) { - if (mBounds.equals(bounds) && mPaint.getColor() == color) return; - mBounds.set(bounds); - mPaint.setColor(color); + @Override + protected void onDraw(Canvas canvas) { + canvas.drawPath(mPath, mPaint); } @Override - protected void onDraw(Canvas canvas) { - if (mBounds.isEmpty()) return; - final Path path = new Region(mBounds).getBoundaryPath(); - canvas.drawPath(path, mPaint); + public boolean hasOverlappingRendering() { + return false; } } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SideStage.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SideStage.java index 2b19bb965fed..0e7ccd3515c4 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SideStage.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SideStage.java @@ -17,15 +17,19 @@ package com.android.wm.shell.splitscreen; import android.annotation.CallSuper; +import android.annotation.Nullable; import android.app.ActivityManager; import android.content.Context; -import android.graphics.Color; import android.graphics.Rect; +import android.view.InsetsSourceControl; +import android.view.InsetsState; +import android.view.SurfaceControl; import android.view.SurfaceSession; import android.window.WindowContainerToken; import android.window.WindowContainerTransaction; import com.android.wm.shell.ShellTaskOrganizer; +import com.android.wm.shell.common.DisplayInsetsController; import com.android.wm.shell.common.SyncTransactionQueue; /** @@ -34,7 +38,8 @@ import com.android.wm.shell.common.SyncTransactionQueue; * * @see StageCoordinator */ -class SideStage extends StageTaskListener { +class SideStage extends StageTaskListener implements + DisplayInsetsController.OnInsetsChangedListener { private static final String TAG = SideStage.class.getSimpleName(); private final Context mContext; private OutlineManager mOutlineManager; @@ -77,33 +82,61 @@ class SideStage extends StageTaskListener { return true; } + @Nullable + public SurfaceControl getOutlineLeash() { + return mOutlineManager.getOutlineLeash(); + } + + @Override + @CallSuper + public void onTaskAppeared(ActivityManager.RunningTaskInfo taskInfo, SurfaceControl leash) { + super.onTaskAppeared(taskInfo, leash); + if (isRootTask(taskInfo)) { + mOutlineManager = new OutlineManager(mContext, taskInfo.configuration); + enableOutline(true); + } + } + + @Override + @CallSuper + public void onTaskInfoChanged(ActivityManager.RunningTaskInfo taskInfo) { + super.onTaskInfoChanged(taskInfo); + if (isRootTask(taskInfo)) { + mOutlineManager.setRootBounds(taskInfo.configuration.windowConfiguration.getBounds()); + } + } + + private boolean isRootTask(ActivityManager.RunningTaskInfo taskInfo) { + return mRootTaskInfo != null && mRootTaskInfo.taskId == taskInfo.taskId; + } + void enableOutline(boolean enable) { + if (mOutlineManager == null) { + return; + } + if (enable) { - if (mOutlineManager == null && mRootTaskInfo != null) { - mOutlineManager = new OutlineManager(mContext, mRootTaskInfo.configuration); - mSyncQueue.runInSync(t -> mOutlineManager.inflate(t, mRootLeash, Color.YELLOW)); - updateOutlineBounds(); + if (mRootTaskInfo != null) { + mOutlineManager.inflate(mRootLeash, + mRootTaskInfo.configuration.windowConfiguration.getBounds()); } } else { - if (mOutlineManager != null) { - mOutlineManager.release(); - mOutlineManager = null; - } + mOutlineManager.release(); } } - private void updateOutlineBounds() { - if (mOutlineManager == null || mRootTaskInfo == null || !mRootTaskInfo.isVisible) return; - mOutlineManager.drawOutlineBounds( - mRootTaskInfo.configuration.windowConfiguration.getBounds()); + void setOutlineVisibility(boolean visible) { + mOutlineManager.setVisibility(visible); } @Override - @CallSuper - public void onTaskInfoChanged(ActivityManager.RunningTaskInfo taskInfo) { - super.onTaskInfoChanged(taskInfo); - if (mRootTaskInfo != null && mRootTaskInfo.taskId == taskInfo.taskId) { - updateOutlineBounds(); - } + public void insetsChanged(InsetsState insetsState) { + mOutlineManager.onInsetsChanged(insetsState); + } + + @Override + public void insetsControlChanged(InsetsState insetsState, + InsetsSourceControl[] activeControls) { + insetsChanged(insetsState); } } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java index 6527cabb0d34..ac68b3b8a6a9 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java @@ -257,11 +257,6 @@ public class SplitScreenController implements DragAndDropPolicy.Starter, } } - final RemoteAnimationTarget divider = mStageCoordinator.getDividerBarLegacyTarget(); - if (divider.leash != null) { - t.show(divider.leash); - } - t.apply(); if (finishedCallback != null) { try { @@ -300,7 +295,9 @@ public class SplitScreenController implements DragAndDropPolicy.Starter, } transaction.apply(); transaction.close(); - return new RemoteAnimationTarget[]{mStageCoordinator.getDividerBarLegacyTarget()}; + return new RemoteAnimationTarget[]{ + mStageCoordinator.getDividerBarLegacyTarget(), + mStageCoordinator.getOutlineLegacyTarget()}; } /** diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java index a046c42b2391..d7a6cfff6c6f 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java @@ -201,6 +201,7 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler, mSurfaceSession); mDisplayImeController = displayImeController; mDisplayInsetsController = displayInsetsController; + mDisplayInsetsController.addInsetsChangedListener(mDisplayId, mSideStage); mRootTDAOrganizer.registerListener(displayId, this); final DeviceStateManager deviceStateManager = mContext.getSystemService(DeviceStateManager.class); @@ -682,6 +683,7 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler, t.setVisibility(mSideStage.mRootLeash, bothStageVisible) .setVisibility(mMainStage.mRootLeash, bothStageVisible); applyDividerVisibility(t); + applyOutlineVisibility(t); } }); } @@ -703,6 +705,19 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler, } } + private void applyOutlineVisibility(SurfaceControl.Transaction t) { + final SurfaceControl outlineLeash = mSideStage.getOutlineLeash(); + if (outlineLeash == null) { + return; + } + + if (mDividerVisible) { + t.show(outlineLeash).setLayer(outlineLeash, SPLIT_DIVIDER_LAYER); + } else { + t.hide(outlineLeash); + } + } + private void onStageHasChildrenChanged(StageListenerImpl stageListener) { final boolean hasChildren = stageListener.mHasChildren; final boolean isSideStage = stageListener == mSideStageListener; @@ -762,6 +777,7 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler, @Override public void onLayoutChanging(SplitLayout layout) { mSyncQueue.runInSync(t -> updateSurfaceBounds(layout, t)); + mSideStage.setOutlineVisibility(false); } @Override @@ -770,6 +786,7 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler, updateWindowBounds(layout, wct); mSyncQueue.queue(wct); mSyncQueue.runInSync(t -> updateSurfaceBounds(layout, t)); + mSideStage.setOutlineVisibility(true); mLogger.logResize(mSplitLayout.getDividerPositionAsFraction()); } @@ -1145,6 +1162,18 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler, null /* taskInfo */, false /* allowEnterPip */, TYPE_DOCK_DIVIDER); } + RemoteAnimationTarget getOutlineLegacyTarget() { + final Rect bounds = mSideStage.mRootTaskInfo.configuration.windowConfiguration.getBounds(); + // Leverage TYPE_DOCK_DIVIDER type when wrapping outline remote animation target in order to + // distinguish as a split auxiliary target in Launcher. + return new RemoteAnimationTarget(-1 /* taskId */, -1 /* mode */, + mSideStage.getOutlineLeash(), false /* isTranslucent */, null /* clipRect */, + null /* contentInsets */, Integer.MAX_VALUE /* prefixOrderIndex */, + new android.graphics.Point(0, 0) /* position */, bounds, bounds, + new WindowConfiguration(), true, null /* startLeash */, null /* startBounds */, + null /* taskInfo */, false /* allowEnterPip */, TYPE_DOCK_DIVIDER); + } + @Override public void dump(@NonNull PrintWriter pw, String prefix) { final String innerPrefix = prefix + " "; diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageTaskListener.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageTaskListener.java index 15b4ff970203..4140332f50a3 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageTaskListener.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageTaskListener.java @@ -138,8 +138,11 @@ class StageTaskListener implements ShellTaskOrganizer.TaskListener { mRootTaskInfo = taskInfo; mCallbacks.onRootTaskAppeared(); sendStatusChanged(); - mSyncQueue.runInSync(t -> mDimLayer = - SurfaceUtils.makeDimLayer(t, mRootLeash, "Dim layer", mSurfaceSession)); + mSyncQueue.runInSync(t -> { + t.hide(mRootLeash); + mDimLayer = + SurfaceUtils.makeDimLayer(t, mRootLeash, "Dim layer", mSurfaceSession); + }); } else if (taskInfo.parentTaskId == mRootTaskInfo.taskId) { final int taskId = taskInfo.taskId; mChildrenLeashes.put(taskId, leash); diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/unfold/ShellUnfoldProgressProvider.java b/libs/WindowManager/Shell/src/com/android/wm/shell/unfold/ShellUnfoldProgressProvider.java new file mode 100644 index 000000000000..74e48120bf1a --- /dev/null +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/unfold/ShellUnfoldProgressProvider.java @@ -0,0 +1,44 @@ +/* + * Copyright (C) 2021 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.wm.shell.unfold; + +import android.annotation.FloatRange; + +import java.util.concurrent.Executor; + +/** + * Wrapper interface for unfold transition progress provider for the Shell + * @see com.android.systemui.unfold.UnfoldTransitionProgressProvider + */ +public interface ShellUnfoldProgressProvider { + + /** + * Adds a transition listener + */ + void addListener(Executor executor, UnfoldListener listener); + + /** + * Listener for receiving unfold updates + */ + interface UnfoldListener { + default void onStateChangeStarted() {} + + default void onStateChangeProgress(@FloatRange(from = 0.0, to = 1.0) float progress) {} + + default void onStateChangeFinished() {} + } +} diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/apppairs/TestAppPairsController.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/apppairs/TestAppPairsController.java index 27c626170a4b..294bc1276291 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/apppairs/TestAppPairsController.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/apppairs/TestAppPairsController.java @@ -21,6 +21,7 @@ import static org.mockito.Mockito.mock; import com.android.wm.shell.ShellTaskOrganizer; import com.android.wm.shell.common.DisplayController; import com.android.wm.shell.common.DisplayImeController; +import com.android.wm.shell.common.DisplayInsetsController; import com.android.wm.shell.common.ShellExecutor; import com.android.wm.shell.common.SyncTransactionQueue; @@ -30,7 +31,7 @@ public class TestAppPairsController extends AppPairsController { public TestAppPairsController(ShellTaskOrganizer organizer, SyncTransactionQueue syncQueue, DisplayController displayController) { super(organizer, syncQueue, displayController, mock(ShellExecutor.class), - mock(DisplayImeController.class)); + mock(DisplayImeController.class), mock(DisplayInsetsController.class)); mPool = new TestAppPairsPool(this); setPairsPool(mPool); } diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/fullscreen/FullscreenTaskListenerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/fullscreen/FullscreenTaskListenerTest.java new file mode 100644 index 000000000000..d6f7e54ae369 --- /dev/null +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/fullscreen/FullscreenTaskListenerTest.java @@ -0,0 +1,151 @@ +/* + * Copyright (C) 2021 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.wm.shell.fullscreen; + +import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD; + +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.inOrder; +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyNoMoreInteractions; +import static org.mockito.Mockito.when; + +import android.app.ActivityManager.RunningTaskInfo; +import android.app.WindowConfiguration; +import android.content.res.Configuration; +import android.graphics.Point; +import android.view.SurfaceControl; + +import androidx.test.filters.SmallTest; + +import com.android.wm.shell.common.SyncTransactionQueue; + +import org.junit.Before; +import org.junit.Test; +import org.mockito.InOrder; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; + +import java.util.Optional; + +@SmallTest +public class FullscreenTaskListenerTest { + + @Mock + private SyncTransactionQueue mSyncQueue; + @Mock + private FullscreenUnfoldController mUnfoldController; + @Mock + private SurfaceControl mSurfaceControl; + + private Optional<FullscreenUnfoldController> mFullscreenUnfoldController; + + private FullscreenTaskListener mListener; + + @Before + public void setup() { + MockitoAnnotations.initMocks(this); + mFullscreenUnfoldController = Optional.of(mUnfoldController); + mListener = new FullscreenTaskListener(mSyncQueue, mFullscreenUnfoldController); + } + + @Test + public void testAnimatableTaskAppeared_notifiesUnfoldController() { + RunningTaskInfo info = createTaskInfo(/* visible */ true, /* taskId */ 0); + + mListener.onTaskAppeared(info, mSurfaceControl); + + verify(mUnfoldController).onTaskAppeared(eq(info), any()); + } + + @Test + public void testMultipleAnimatableTasksAppeared_notifiesUnfoldController() { + RunningTaskInfo animatable1 = createTaskInfo(/* visible */ true, /* taskId */ 0); + RunningTaskInfo animatable2 = createTaskInfo(/* visible */ true, /* taskId */ 1); + + mListener.onTaskAppeared(animatable1, mSurfaceControl); + mListener.onTaskAppeared(animatable2, mSurfaceControl); + + InOrder order = inOrder(mUnfoldController); + order.verify(mUnfoldController).onTaskAppeared(eq(animatable1), any()); + order.verify(mUnfoldController).onTaskAppeared(eq(animatable2), any()); + } + + @Test + public void testNonAnimatableTaskAppeared_doesNotNotifyUnfoldController() { + RunningTaskInfo info = createTaskInfo(/* visible */ false, /* taskId */ 0); + + mListener.onTaskAppeared(info, mSurfaceControl); + + verifyNoMoreInteractions(mUnfoldController); + } + + @Test + public void testNonAnimatableTaskChanged_doesNotNotifyUnfoldController() { + RunningTaskInfo info = createTaskInfo(/* visible */ false, /* taskId */ 0); + mListener.onTaskAppeared(info, mSurfaceControl); + + mListener.onTaskInfoChanged(info); + + verifyNoMoreInteractions(mUnfoldController); + } + + @Test + public void testNonAnimatableTaskVanished_doesNotNotifyUnfoldController() { + RunningTaskInfo info = createTaskInfo(/* visible */ false, /* taskId */ 0); + mListener.onTaskAppeared(info, mSurfaceControl); + + mListener.onTaskVanished(info); + + verifyNoMoreInteractions(mUnfoldController); + } + + @Test + public void testAnimatableTaskBecameInactive_notifiesUnfoldController() { + RunningTaskInfo animatableTask = createTaskInfo(/* visible */ true, /* taskId */ 0); + mListener.onTaskAppeared(animatableTask, mSurfaceControl); + RunningTaskInfo notAnimatableTask = createTaskInfo(/* visible */ false, /* taskId */ 0); + + mListener.onTaskInfoChanged(notAnimatableTask); + + verify(mUnfoldController).onTaskVanished(eq(notAnimatableTask)); + } + + @Test + public void testAnimatableTaskVanished_notifiesUnfoldController() { + RunningTaskInfo taskInfo = createTaskInfo(/* visible */ true, /* taskId */ 0); + mListener.onTaskAppeared(taskInfo, mSurfaceControl); + + mListener.onTaskVanished(taskInfo); + + verify(mUnfoldController).onTaskVanished(eq(taskInfo)); + } + + private RunningTaskInfo createTaskInfo(boolean visible, int taskId) { + final RunningTaskInfo info = spy(new RunningTaskInfo()); + info.isVisible = visible; + info.positionInParent = new Point(); + when(info.getWindowingMode()).thenReturn(WindowConfiguration.WINDOWING_MODE_FULLSCREEN); + final Configuration configuration = new Configuration(); + configuration.windowConfiguration.setActivityType(ACTIVITY_TYPE_STANDARD); + when(info.getConfiguration()).thenReturn(configuration); + info.taskId = taskId; + return info; + } +} diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SideStageTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SideStageTests.java index 69ead3ac9cf9..3a2516ec9366 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SideStageTests.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SideStageTests.java @@ -29,6 +29,7 @@ import android.view.SurfaceControl; import android.view.SurfaceSession; import android.window.WindowContainerTransaction; +import androidx.test.annotation.UiThreadTest; import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.filters.SmallTest; @@ -58,6 +59,7 @@ public class SideStageTests extends ShellTestCase { private SideStage mSideStage; @Before + @UiThreadTest public void setup() { MockitoAnnotations.initMocks(this); mRootTask = new TestRunningTaskInfoBuilder().build(); diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTransitionTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTransitionTests.java index d357e778fe35..8dce454eb078 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTransitionTests.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTransitionTests.java @@ -51,6 +51,7 @@ import android.window.TransitionInfo; import android.window.TransitionRequestInfo; import android.window.WindowContainerTransaction; +import androidx.test.annotation.UiThreadTest; import androidx.test.filters.SmallTest; import androidx.test.runner.AndroidJUnit4; @@ -96,6 +97,7 @@ public class SplitTransitionTests extends ShellTestCase { private ActivityManager.RunningTaskInfo mSideChild; @Before + @UiThreadTest public void setup() { MockitoAnnotations.initMocks(this); final ShellExecutor mockExecutor = mock(ShellExecutor.class); diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageTaskListenerTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageTaskListenerTests.java index 1a30f164f9a8..0916dd1f71bd 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageTaskListenerTests.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageTaskListenerTests.java @@ -62,6 +62,7 @@ public final class StageTaskListenerTests { @Mock private SyncTransactionQueue mSyncQueue; @Captor private ArgumentCaptor<SyncTransactionQueue.TransactionRunnable> mRunnableCaptor; private SurfaceSession mSurfaceSession = new SurfaceSession(); + private SurfaceControl mSurfaceControl; private ActivityManager.RunningTaskInfo mRootTask; private StageTaskListener mStageTaskListener; @@ -76,7 +77,8 @@ public final class StageTaskListenerTests { mSurfaceSession); mRootTask = new TestRunningTaskInfoBuilder().build(); mRootTask.parentTaskId = INVALID_TASK_ID; - mStageTaskListener.onTaskAppeared(mRootTask, new SurfaceControl()); + mSurfaceControl = new SurfaceControl.Builder(mSurfaceSession).setName("test").build(); + mStageTaskListener.onTaskAppeared(mRootTask, mSurfaceControl); } @Test @@ -103,7 +105,7 @@ public final class StageTaskListenerTests { final ActivityManager.RunningTaskInfo childTask = new TestRunningTaskInfoBuilder().setParentTaskId(mRootTask.taskId).build(); - mStageTaskListener.onTaskAppeared(childTask, new SurfaceControl()); + mStageTaskListener.onTaskAppeared(childTask, mSurfaceControl); assertThat(mStageTaskListener.mChildrenTaskInfo.contains(childTask.taskId)).isTrue(); verify(mCallbacks).onStatusChanged(eq(mRootTask.isVisible), eq(true)); diff --git a/packages/SettingsLib/Tile/src/com/android/settingslib/drawer/Tile.java b/packages/SettingsLib/Tile/src/com/android/settingslib/drawer/Tile.java index 8f3e4bd87aa7..220cf6b5c38a 100644 --- a/packages/SettingsLib/Tile/src/com/android/settingslib/drawer/Tile.java +++ b/packages/SettingsLib/Tile/src/com/android/settingslib/drawer/Tile.java @@ -18,6 +18,7 @@ package com.android.settingslib.drawer; import static com.android.settingslib.drawer.TileUtils.META_DATA_KEY_ORDER; import static com.android.settingslib.drawer.TileUtils.META_DATA_KEY_PROFILE; +import static com.android.settingslib.drawer.TileUtils.META_DATA_NEW_TASK; import static com.android.settingslib.drawer.TileUtils.META_DATA_PREFERENCE_ICON; import static com.android.settingslib.drawer.TileUtils.META_DATA_PREFERENCE_KEYHINT; import static com.android.settingslib.drawer.TileUtils.META_DATA_PREFERENCE_SUMMARY; @@ -329,6 +330,18 @@ public abstract class Tile implements Parcelable { } /** + * Whether the {@link Activity} should be launched in a separate task. + */ + public boolean isNewTask(Context context) { + ensureMetadataNotStale(context); + if (mMetaData != null + && mMetaData.containsKey(META_DATA_NEW_TASK)) { + return mMetaData.getBoolean(META_DATA_NEW_TASK); + } + return false; + } + + /** * Ensures metadata is not stale for this tile. */ private void ensureMetadataNotStale(Context context) { diff --git a/packages/SettingsLib/Tile/src/com/android/settingslib/drawer/TileUtils.java b/packages/SettingsLib/Tile/src/com/android/settingslib/drawer/TileUtils.java index a2bec334f206..acc0087f6dcf 100644 --- a/packages/SettingsLib/Tile/src/com/android/settingslib/drawer/TileUtils.java +++ b/packages/SettingsLib/Tile/src/com/android/settingslib/drawer/TileUtils.java @@ -230,6 +230,13 @@ public class TileUtils { public static final String META_DATA_KEY_PROFILE = "com.android.settings.profile"; /** + * Name of the meta-data item that should be set in the AndroidManifest.xml + * to specify whether the {@link android.app.Activity} should be launched in a separate task. + * This should be a boolean value {@code true} or {@code false}, set using {@code android:value} + */ + public static final String META_DATA_NEW_TASK = "com.android.settings.new_task"; + + /** * Build a list of DashboardCategory. */ public static List<DashboardCategory> getCategories(Context context, diff --git a/packages/SystemUI/res/layout/global_screenshot_static.xml b/packages/SystemUI/res/layout/global_screenshot_static.xml index 6a9254cad8f4..21c5ab04eb9a 100644 --- a/packages/SystemUI/res/layout/global_screenshot_static.xml +++ b/packages/SystemUI/res/layout/global_screenshot_static.xml @@ -130,13 +130,4 @@ app:layout_constraintStart_toStartOf="@id/global_screenshot_preview" app:layout_constraintTop_toTopOf="@id/global_screenshot_preview" android:elevation="@dimen/screenshot_preview_elevation"/> - <View - android:id="@+id/screenshot_transition_view" - android:layout_width="0dp" - android:layout_height="0dp" - android:visibility="invisible" - app:layout_constraintStart_toStartOf="@id/global_screenshot_preview" - app:layout_constraintTop_toTopOf="@id/global_screenshot_preview" - app:layout_constraintEnd_toEndOf="@id/global_screenshot_preview" - app:layout_constraintBottom_toBottomOf="@id/global_screenshot_preview"/> </androidx.constraintlayout.widget.ConstraintLayout> diff --git a/packages/SystemUI/res/values/config.xml b/packages/SystemUI/res/values/config.xml index c3ac19a46ee6..d0de876a6b27 100644 --- a/packages/SystemUI/res/values/config.xml +++ b/packages/SystemUI/res/values/config.xml @@ -224,15 +224,23 @@ display brightness, suitable to listen to while the device is asleep (e.g. during always-on display) --> <string-array name="doze_brightness_sensor_name_posture_mapping" translatable="false"> - <item></item> <!-- UNKNOWN --> - <item></item> <!-- CLOSED --> - <item></item> <!-- HALF_OPENED --> - <item></item> <!-- OPENED --> + <!-- UNKNOWN --> + <!-- CLOSED --> + <!-- HALF_OPENED --> + <!-- OPENED --> </string-array> <!-- Override value to use for proximity sensor. --> <string name="proximity_sensor_type" translatable="false"></string> + <!-- Sensor type per posture state to use for proximity sensor --> + <string-array name="proximity_sensor_posture_mapping" translatable="false"> + <!-- UNKNOWN --> + <!-- CLOSED --> + <!-- HALF_OPENED --> + <!-- OPENED --> + </string-array> + <!-- If using proximity_sensor_type, specifies a threshold value to distinguish near and far break points. A sensor value less than this is considered "near". --> <item name="proximity_sensor_threshold" translatable="false" format="float" type="dimen"></item> @@ -246,6 +254,15 @@ <!-- Override value to use for proximity sensor as confirmation for proximity_sensor_type. --> <string name="proximity_sensor_secondary_type" translatable="false"></string> + <!-- Sensor type per posture state to use for proximity sensor as a confirmation for + proximity_sensor_type. --> + <string-array name="proximity_sensor_secondary_posture_mapping" translatable="false"> + <!-- UNKNOWN --> + <!-- CLOSED --> + <!-- HALF_OPENED --> + <!-- OPENED --> + </string-array> + <!-- If using proximity_sensor_secondary_type, specifies a threshold value to distinguish near and far break points. A sensor value less than this is considered "near". --> <item name="proximity_sensor_secondary_threshold" translatable="false" format="float" diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java index 8f14cd858f1e..f81f0b9ca2c3 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java @@ -11,6 +11,7 @@ import android.util.AttributeSet; import android.util.TypedValue; import android.view.View; import android.view.ViewGroup; +import android.view.ViewTreeObserver.OnPreDrawListener; import android.widget.FrameLayout; import android.widget.RelativeLayout; @@ -93,6 +94,7 @@ public class KeyguardClockSwitch extends RelativeLayout { private int[] mColorPalette; private int mClockSwitchYAmount; + @VisibleForTesting boolean mChildrenAreLaidOut = false; public KeyguardClockSwitch(Context context, AttributeSet attrs) { super(context, attrs); @@ -284,11 +286,31 @@ public class KeyguardClockSwitch extends RelativeLayout { if (mDisplayedClockSize != null && clockSize == mDisplayedClockSize) { return false; } - animateClockChange(clockSize == LARGE); - mDisplayedClockSize = clockSize; + + // let's make sure clock is changed only after all views were laid out so we can + // translate them properly + if (mChildrenAreLaidOut) { + animateClockChange(clockSize == LARGE); + mDisplayedClockSize = clockSize; + } else { + getViewTreeObserver().addOnPreDrawListener(new OnPreDrawListener() { + @Override + public boolean onPreDraw() { + switchToClock(clockSize); + getViewTreeObserver().removeOnPreDrawListener(this); + return true; + } + }); + } return true; } + @Override + protected void onLayout(boolean changed, int l, int t, int r, int b) { + super.onLayout(changed, l, t, r, b); + mChildrenAreLaidOut = true; + } + public Paint getPaint() { return mClockView.getPaint(); } diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardMessageArea.java b/packages/SystemUI/src/com/android/keyguard/KeyguardMessageArea.java index 62411dbff5fd..11eeac2272ff 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardMessageArea.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardMessageArea.java @@ -153,6 +153,10 @@ public class KeyguardMessageArea extends TextView implements SecurityMessageDisp colorState = mNextMessageColorState; mNextMessageColorState = ColorStateList.valueOf(DEFAULT_COLOR); } + if (mAltBouncerShowing) { + // alt bouncer has a black scrim, so always show the text in white + colorState = ColorStateList.valueOf(Color.WHITE); + } setTextColor(colorState); } diff --git a/packages/SystemUI/src/com/android/systemui/classifier/FalsingCollectorImpl.java b/packages/SystemUI/src/com/android/systemui/classifier/FalsingCollectorImpl.java index 14e5991f35d2..be326da8d3bf 100644 --- a/packages/SystemUI/src/com/android/systemui/classifier/FalsingCollectorImpl.java +++ b/packages/SystemUI/src/com/android/systemui/classifier/FalsingCollectorImpl.java @@ -37,6 +37,7 @@ import com.android.systemui.statusbar.policy.KeyguardStateController; import com.android.systemui.util.concurrency.DelayableExecutor; import com.android.systemui.util.sensors.ProximitySensor; import com.android.systemui.util.sensors.ThresholdSensor; +import com.android.systemui.util.sensors.ThresholdSensorEvent; import com.android.systemui.util.time.SystemClock; import java.util.Collections; @@ -405,7 +406,7 @@ class FalsingCollectorImpl implements FalsingCollector { mProximitySensor.unregister(mSensorEventListener); } - private void onProximityEvent(ThresholdSensor.ThresholdSensorEvent proximityEvent) { + private void onProximityEvent(ThresholdSensorEvent proximityEvent) { // TODO: some of these classifiers might allow us to abort early, meaning we don't have to // make these calls. mFalsingManager.onProximityEvent(new ProximityEventImpl(proximityEvent)); @@ -423,9 +424,9 @@ class FalsingCollectorImpl implements FalsingCollector { } private static class ProximityEventImpl implements FalsingManager.ProximityEvent { - private ThresholdSensor.ThresholdSensorEvent mThresholdSensorEvent; + private ThresholdSensorEvent mThresholdSensorEvent; - ProximityEventImpl(ThresholdSensor.ThresholdSensorEvent thresholdSensorEvent) { + ProximityEventImpl(ThresholdSensorEvent thresholdSensorEvent) { mThresholdSensorEvent = thresholdSensorEvent; } @Override diff --git a/packages/SystemUI/src/com/android/systemui/dagger/DependencyProvider.java b/packages/SystemUI/src/com/android/systemui/dagger/DependencyProvider.java index 2d873f26c0c2..fa2384268b15 100644 --- a/packages/SystemUI/src/com/android/systemui/dagger/DependencyProvider.java +++ b/packages/SystemUI/src/com/android/systemui/dagger/DependencyProvider.java @@ -24,8 +24,6 @@ import android.app.INotificationManager; import android.content.Context; import android.content.SharedPreferences; import android.content.om.OverlayManager; -import android.hardware.SensorManager; -import android.hardware.devicestate.DeviceStateManager; import android.hardware.display.AmbientDisplayConfiguration; import android.hardware.display.ColorDisplayManager; import android.os.Handler; @@ -56,22 +54,18 @@ import com.android.systemui.dagger.qualifiers.Main; import com.android.systemui.doze.AlwaysOnDisplayPolicy; import com.android.systemui.dump.DumpManager; import com.android.systemui.keyguard.KeyguardViewMediator; -import com.android.systemui.keyguard.LifecycleScreenStatusProvider; import com.android.systemui.qs.ReduceBrightColorsController; import com.android.systemui.settings.UserTracker; import com.android.systemui.shared.system.ActivityManagerWrapper; import com.android.systemui.shared.system.DevicePolicyManagerWrapper; import com.android.systemui.shared.system.TaskStackChangeListeners; import com.android.systemui.shared.system.WindowManagerWrapper; +import com.android.systemui.statusbar.connectivity.NetworkController; import com.android.systemui.statusbar.phone.AutoHideController; import com.android.systemui.statusbar.phone.ConfigurationControllerImpl; import com.android.systemui.statusbar.policy.ConfigurationController; import com.android.systemui.statusbar.policy.DataSaverController; -import com.android.systemui.statusbar.policy.NetworkController; import com.android.systemui.theme.ThemeOverlayApplier; -import com.android.systemui.unfold.UnfoldTransitionFactory; -import com.android.systemui.unfold.UnfoldTransitionProgressProvider; -import com.android.systemui.unfold.config.UnfoldTransitionConfig; import com.android.systemui.util.leak.LeakDetector; import com.android.systemui.util.settings.SecureSettings; @@ -287,37 +281,6 @@ public class DependencyProvider { /** */ @Provides @SysUISingleton - public UnfoldTransitionProgressProvider provideUnfoldTransitionProgressProvider( - Context context, - UnfoldTransitionConfig config, - LifecycleScreenStatusProvider screenStatusProvider, - DeviceStateManager deviceStateManager, - SensorManager sensorManager, - @Main Executor executor, - @Main Handler handler - ) { - return UnfoldTransitionFactory - .createUnfoldTransitionProgressProvider( - context, - config, - screenStatusProvider, - deviceStateManager, - sensorManager, - handler, - executor - ); - } - - /** */ - @Provides - @SysUISingleton - public UnfoldTransitionConfig provideUnfoldTransitionConfig(Context context) { - return UnfoldTransitionFactory.createConfig(context); - } - - /** */ - @Provides - @SysUISingleton public Choreographer providesChoreographer() { return Choreographer.getInstance(); } diff --git a/packages/SystemUI/src/com/android/systemui/dagger/GlobalModule.java b/packages/SystemUI/src/com/android/systemui/dagger/GlobalModule.java index 648f345205db..18f85196aa0b 100644 --- a/packages/SystemUI/src/com/android/systemui/dagger/GlobalModule.java +++ b/packages/SystemUI/src/com/android/systemui/dagger/GlobalModule.java @@ -24,6 +24,7 @@ import com.android.internal.logging.UiEventLogger; import com.android.internal.logging.UiEventLoggerImpl; import com.android.systemui.dagger.qualifiers.TestHarness; import com.android.systemui.plugins.PluginsModule; +import com.android.systemui.unfold.UnfoldTransitionModule; import com.android.systemui.util.concurrency.GlobalConcurrencyModule; import javax.inject.Singleton; @@ -49,6 +50,7 @@ import dagger.Provides; @Module(includes = { FrameworkServicesModule.class, GlobalConcurrencyModule.class, + UnfoldTransitionModule.class, PluginsModule.class, }) public class GlobalModule { diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java b/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java index 1e7f0d9dc630..b17f078e24ef 100644 --- a/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java +++ b/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java @@ -46,6 +46,7 @@ import com.android.systemui.statusbar.policy.KeyguardStateController; import com.android.systemui.util.Assert; import com.android.systemui.util.concurrency.DelayableExecutor; import com.android.systemui.util.sensors.AsyncSensorManager; +import com.android.systemui.util.sensors.ProximityCheck; import com.android.systemui.util.sensors.ProximitySensor; import com.android.systemui.util.settings.SecureSettings; import com.android.systemui.util.wakelock.WakeLock; @@ -90,7 +91,7 @@ public class DozeTriggers implements DozeMachine.Part { private final TriggerReceiver mBroadcastReceiver = new TriggerReceiver(); private final DockEventListener mDockEventListener = new DockEventListener(); private final DockManager mDockManager; - private final ProximitySensor.ProximityCheck mProxCheck; + private final ProximityCheck mProxCheck; private final BroadcastDispatcher mBroadcastDispatcher; private final AuthController mAuthController; private final DelayableExecutor mMainExecutor; @@ -181,7 +182,8 @@ public class DozeTriggers implements DozeMachine.Part { AmbientDisplayConfiguration config, DozeParameters dozeParameters, AsyncSensorManager sensorManager, WakeLock wakeLock, DockManager dockManager, - ProximitySensor proximitySensor, ProximitySensor.ProximityCheck proxCheck, + ProximitySensor proximitySensor, + ProximityCheck proxCheck, DozeLog dozeLog, BroadcastDispatcher broadcastDispatcher, SecureSettings secureSettings, AuthController authController, @Main DelayableExecutor mainExecutor, diff --git a/packages/SystemUI/src/com/android/systemui/dump/DumpManager.kt b/packages/SystemUI/src/com/android/systemui/dump/DumpManager.kt index b0f3959119c8..21a1b75224b2 100644 --- a/packages/SystemUI/src/com/android/systemui/dump/DumpManager.kt +++ b/packages/SystemUI/src/com/android/systemui/dump/DumpManager.kt @@ -18,11 +18,11 @@ package com.android.systemui.dump import android.util.ArrayMap import com.android.systemui.Dumpable -import com.android.systemui.dagger.SysUISingleton import com.android.systemui.log.LogBuffer import java.io.FileDescriptor import java.io.PrintWriter import javax.inject.Inject +import javax.inject.Singleton /** * Maintains a registry of things that should be dumped when a bug report is taken @@ -33,7 +33,7 @@ import javax.inject.Inject * * See [DumpHandler] for more information on how and when this information is dumped. */ -@SysUISingleton +@Singleton open class DumpManager @Inject constructor() { private val dumpables: MutableMap<String, RegisteredDumpable<Dumpable>> = ArrayMap() private val buffers: MutableMap<String, RegisteredDumpable<LogBuffer>> = ArrayMap() diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/LifecycleScreenStatusProvider.kt b/packages/SystemUI/src/com/android/systemui/keyguard/LifecycleScreenStatusProvider.kt index f25ec5591e3a..044a57ced3fc 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/LifecycleScreenStatusProvider.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/LifecycleScreenStatusProvider.kt @@ -15,12 +15,12 @@ */ package com.android.systemui.keyguard -import com.android.systemui.dagger.SysUISingleton import com.android.systemui.unfold.updates.screen.ScreenStatusProvider import com.android.systemui.unfold.updates.screen.ScreenStatusProvider.ScreenListener import javax.inject.Inject +import javax.inject.Singleton -@SysUISingleton +@Singleton class LifecycleScreenStatusProvider @Inject constructor(screenLifecycle: ScreenLifecycle) : ScreenStatusProvider, ScreenLifecycle.Observer { diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ScreenLifecycle.java b/packages/SystemUI/src/com/android/systemui/keyguard/ScreenLifecycle.java index 30983aacd7d2..d17c39a81dae 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ScreenLifecycle.java +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ScreenLifecycle.java @@ -19,18 +19,18 @@ package com.android.systemui.keyguard; import android.os.Trace; import com.android.systemui.Dumpable; -import com.android.systemui.dagger.SysUISingleton; import com.android.systemui.dump.DumpManager; import java.io.FileDescriptor; import java.io.PrintWriter; import javax.inject.Inject; +import javax.inject.Singleton; /** * Tracks the screen lifecycle. */ -@SysUISingleton +@Singleton public class ScreenLifecycle extends Lifecycle<ScreenLifecycle.Observer> implements Dumpable { public static final int SCREEN_OFF = 0; diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarController.java b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarController.java index a1a630aca94c..7622d663301d 100644 --- a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarController.java +++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarController.java @@ -195,7 +195,8 @@ public class NavigationBarController implements mNavigationModeController.addListener(this); mTaskbarDelegate = taskbarDelegate; mTaskbarDelegate.setOverviewProxyService(commandQueue, overviewProxyService, - navigationBarA11yHelper, navigationModeController, sysUiFlagsContainer); + navigationBarA11yHelper, navigationModeController, sysUiFlagsContainer, + dumpManager); mIsTablet = isTablet(mContext); mUserTracker = userTracker; diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/TaskbarDelegate.java b/packages/SystemUI/src/com/android/systemui/navigationbar/TaskbarDelegate.java index fa6169210609..4d29612951cc 100644 --- a/packages/SystemUI/src/com/android/systemui/navigationbar/TaskbarDelegate.java +++ b/packages/SystemUI/src/com/android/systemui/navigationbar/TaskbarDelegate.java @@ -46,8 +46,12 @@ import android.view.InsetsVisibilities; import android.view.View; import android.view.WindowInsetsController.Behavior; +import androidx.annotation.NonNull; + import com.android.internal.view.AppearanceRegion; import com.android.systemui.Dependency; +import com.android.systemui.Dumpable; +import com.android.systemui.dump.DumpManager; import com.android.systemui.model.SysUiState; import com.android.systemui.navigationbar.gestural.EdgeBackGestureHandler; import com.android.systemui.recents.OverviewProxyService; @@ -55,13 +59,16 @@ import com.android.systemui.shared.recents.utilities.Utilities; import com.android.systemui.shared.system.ActivityManagerWrapper; import com.android.systemui.statusbar.CommandQueue; +import java.io.FileDescriptor; +import java.io.PrintWriter; + import javax.inject.Inject; import javax.inject.Singleton; @Singleton public class TaskbarDelegate implements CommandQueue.Callbacks, OverviewProxyService.OverviewProxyListener, NavigationModeController.ModeChangedListener, - ComponentCallbacks { + ComponentCallbacks, Dumpable { private final EdgeBackGestureHandler mEdgeBackGestureHandler; @@ -93,13 +100,14 @@ public class TaskbarDelegate implements CommandQueue.Callbacks, OverviewProxyService overviewProxyService, NavigationBarA11yHelper navigationBarA11yHelper, NavigationModeController navigationModeController, - SysUiState sysUiState) { + SysUiState sysUiState, DumpManager dumpManager) { // TODO: adding this in the ctor results in a dagger dependency cycle :( mCommandQueue = commandQueue; mOverviewProxyService = overviewProxyService; mNavigationBarA11yHelper = navigationBarA11yHelper; mNavigationModeController = navigationModeController; mSysUiState = sysUiState; + dumpManager.registerDumpable(this); } public void destroy() { @@ -220,4 +228,14 @@ public class TaskbarDelegate implements CommandQueue.Callbacks, @Override public void onLowMemory() {} + + @Override + public void dump(@NonNull FileDescriptor fd, @NonNull PrintWriter pw, @NonNull String[] args) { + pw.println("TaskbarDelegate (displayId=" + mDisplayId + "):"); + pw.println(" mNavigationIconHints=" + mNavigationIconHints); + pw.println(" mDisabledFlags=" + mDisabledFlags); + pw.println(" mTaskBarWindowState=" + mTaskBarWindowState); + pw.println(" mBehavior=" + mBehavior); + mEdgeBackGestureHandler.dump(pw); + } } diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java index bf1a98fa39e6..c6da3420ffe9 100644 --- a/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java +++ b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java @@ -101,8 +101,8 @@ public class EdgeBackGestureHandler extends CurrentUserTracker private static final int MAX_NUM_LOGGED_PREDICTIONS = 10; private static final int MAX_NUM_LOGGED_GESTURES = 10; - // Temporary log until b/176302696 is resolved - static final boolean DEBUG_MISSING_GESTURE = false; + // Temporary log until b/201642126 is resolved + static final boolean DEBUG_MISSING_GESTURE = true; static final String DEBUG_MISSING_GESTURE_TAG = "NoBackGesture"; private static final boolean ENABLE_PER_WINDOW_INPUT_ROTATION = diff --git a/packages/SystemUI/src/com/android/systemui/qs/carrier/QSCarrierGroupController.java b/packages/SystemUI/src/com/android/systemui/qs/carrier/QSCarrierGroupController.java index 953f9fb10657..fec61d911577 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/carrier/QSCarrierGroupController.java +++ b/packages/SystemUI/src/com/android/systemui/qs/carrier/QSCarrierGroupController.java @@ -42,8 +42,8 @@ import com.android.systemui.dagger.qualifiers.Background; import com.android.systemui.dagger.qualifiers.Main; import com.android.systemui.flags.FeatureFlags; import com.android.systemui.plugins.ActivityStarter; -import com.android.systemui.statusbar.policy.NetworkController; -import com.android.systemui.statusbar.policy.NetworkController.MobileDataIndicators; +import com.android.systemui.statusbar.connectivity.NetworkController; +import com.android.systemui.statusbar.connectivity.NetworkController.MobileDataIndicators; import com.android.systemui.util.CarrierConfigTracker; import java.util.function.Consumer; diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/CastTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/CastTile.java index 04f089d31664..9de6ceb2de4b 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/CastTile.java +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/CastTile.java @@ -51,13 +51,13 @@ import com.android.systemui.qs.QSDetailItems.Item; import com.android.systemui.qs.QSHost; import com.android.systemui.qs.logging.QSLogger; import com.android.systemui.qs.tileimpl.QSTileImpl; +import com.android.systemui.statusbar.connectivity.NetworkController; +import com.android.systemui.statusbar.connectivity.NetworkController.WifiIndicators; import com.android.systemui.statusbar.phone.SystemUIDialog; import com.android.systemui.statusbar.policy.CastController; import com.android.systemui.statusbar.policy.CastController.CastDevice; import com.android.systemui.statusbar.policy.HotspotController; import com.android.systemui.statusbar.policy.KeyguardStateController; -import com.android.systemui.statusbar.policy.NetworkController; -import com.android.systemui.statusbar.policy.NetworkController.WifiIndicators; import java.util.ArrayList; import java.util.LinkedHashMap; diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/CellularTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/CellularTile.java index b1af8416e06d..35dadd45eb3e 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/CellularTile.java +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/CellularTile.java @@ -56,11 +56,11 @@ import com.android.systemui.qs.QSHost; import com.android.systemui.qs.SignalTileView; import com.android.systemui.qs.logging.QSLogger; import com.android.systemui.qs.tileimpl.QSTileImpl; +import com.android.systemui.statusbar.connectivity.NetworkController; +import com.android.systemui.statusbar.connectivity.NetworkController.IconState; +import com.android.systemui.statusbar.connectivity.NetworkController.MobileDataIndicators; +import com.android.systemui.statusbar.connectivity.NetworkController.SignalCallback; import com.android.systemui.statusbar.phone.SystemUIDialog; -import com.android.systemui.statusbar.policy.NetworkController; -import com.android.systemui.statusbar.policy.NetworkController.IconState; -import com.android.systemui.statusbar.policy.NetworkController.MobileDataIndicators; -import com.android.systemui.statusbar.policy.NetworkController.SignalCallback; import javax.inject.Inject; diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/InternetTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/InternetTile.java index 530804ed09e7..98d0a72685ba 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/InternetTile.java +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/InternetTile.java @@ -52,13 +52,13 @@ import com.android.systemui.qs.QSHost; import com.android.systemui.qs.logging.QSLogger; import com.android.systemui.qs.tileimpl.QSTileImpl; import com.android.systemui.qs.tiles.dialog.InternetDialogFactory; -import com.android.systemui.statusbar.policy.NetworkController; -import com.android.systemui.statusbar.policy.NetworkController.AccessPointController; -import com.android.systemui.statusbar.policy.NetworkController.IconState; -import com.android.systemui.statusbar.policy.NetworkController.MobileDataIndicators; -import com.android.systemui.statusbar.policy.NetworkController.SignalCallback; -import com.android.systemui.statusbar.policy.NetworkController.WifiIndicators; -import com.android.systemui.statusbar.policy.WifiIcons; +import com.android.systemui.statusbar.connectivity.NetworkController; +import com.android.systemui.statusbar.connectivity.NetworkController.AccessPointController; +import com.android.systemui.statusbar.connectivity.NetworkController.IconState; +import com.android.systemui.statusbar.connectivity.NetworkController.MobileDataIndicators; +import com.android.systemui.statusbar.connectivity.NetworkController.SignalCallback; +import com.android.systemui.statusbar.connectivity.NetworkController.WifiIndicators; +import com.android.systemui.statusbar.connectivity.WifiIcons; import java.io.FileDescriptor; import java.io.PrintWriter; diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/WifiTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/WifiTile.java index 41a3020191a6..e6e7e21263bb 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/WifiTile.java +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/WifiTile.java @@ -52,11 +52,11 @@ import com.android.systemui.qs.QSHost; import com.android.systemui.qs.logging.QSLogger; import com.android.systemui.qs.tileimpl.QSIconViewImpl; import com.android.systemui.qs.tileimpl.QSTileImpl; -import com.android.systemui.statusbar.policy.NetworkController; -import com.android.systemui.statusbar.policy.NetworkController.AccessPointController; -import com.android.systemui.statusbar.policy.NetworkController.SignalCallback; -import com.android.systemui.statusbar.policy.NetworkController.WifiIndicators; -import com.android.systemui.statusbar.policy.WifiIcons; +import com.android.systemui.statusbar.connectivity.NetworkController; +import com.android.systemui.statusbar.connectivity.NetworkController.AccessPointController; +import com.android.systemui.statusbar.connectivity.NetworkController.SignalCallback; +import com.android.systemui.statusbar.connectivity.NetworkController.WifiIndicators; +import com.android.systemui.statusbar.connectivity.WifiIcons; import com.android.wifitrackerlib.WifiEntry; import java.util.List; diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogController.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogController.java index aaba5efc92f6..b9cd08e8ac77 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogController.java +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogController.java @@ -75,9 +75,9 @@ import com.android.systemui.broadcast.BroadcastDispatcher; import com.android.systemui.dagger.qualifiers.Background; import com.android.systemui.dagger.qualifiers.Main; import com.android.systemui.plugins.ActivityStarter; +import com.android.systemui.statusbar.connectivity.NetworkController; +import com.android.systemui.statusbar.connectivity.NetworkController.AccessPointController; import com.android.systemui.statusbar.policy.KeyguardStateController; -import com.android.systemui.statusbar.policy.NetworkController; -import com.android.systemui.statusbar.policy.NetworkController.AccessPointController; import com.android.systemui.toast.SystemUIToast; import com.android.systemui.toast.ToastFactory; import com.android.systemui.util.CarrierConfigTracker; diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/LongScreenshotActivity.java b/packages/SystemUI/src/com/android/systemui/screenshot/LongScreenshotActivity.java index 31d51f1d1a60..a42b34cf23d0 100644 --- a/packages/SystemUI/src/com/android/systemui/screenshot/LongScreenshotActivity.java +++ b/packages/SystemUI/src/com/android/systemui/screenshot/LongScreenshotActivity.java @@ -326,18 +326,11 @@ public class LongScreenshotActivity extends Activity { | Intent.FLAG_GRANT_WRITE_URI_PERMISSION); mTransitionView.setImageBitmap(mOutputBitmap); + mTransitionView.setVisibility(View.VISIBLE); mTransitionView.setTransitionName( ChooserActivity.FIRST_IMAGE_PREVIEW_TRANSITION_NAME); // TODO: listen for transition completing instead of finishing onStop mTransitionStarted = true; - int[] locationOnScreen = new int[2]; - mTransitionView.getLocationOnScreen(locationOnScreen); - int[] locationInWindow = new int[2]; - mTransitionView.getLocationInWindow(locationInWindow); - int deltaX = locationOnScreen[0] - locationInWindow[0]; - int deltaY = locationOnScreen[1] - locationInWindow[1]; - mTransitionView.setX(mTransitionView.getX() - deltaX); - mTransitionView.setY(mTransitionView.getY() - deltaY); startActivity(intent, ActivityOptions.makeSceneTransitionAnimation(this, mTransitionView, ChooserActivity.FIRST_IMAGE_PREVIEW_TRANSITION_NAME).toBundle()); diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java index 8def475c192c..5b4db1449b34 100644 --- a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java +++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java @@ -940,12 +940,10 @@ public class ScreenshotController { */ private Supplier<ActionTransition> getActionTransitionSupplier() { return () -> { - View preview = mScreenshotView.getTransitionView(); - preview.setX(preview.getX() - mScreenshotView.getStaticLeftMargin()); Pair<ActivityOptions, ExitTransitionCoordinator> transition = ActivityOptions.startSharedElementAnimation( mWindow, new ScreenshotExitTransitionCallbacksSupplier(true).get(), - null, Pair.create(mScreenshotView.getTransitionView(), + null, Pair.create(mScreenshotView.getScreenshotPreview(), ChooserActivity.FIRST_IMAGE_PREVIEW_TRANSITION_NAME)); transition.second.startExit(); diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotView.java b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotView.java index dfb39e300450..7222b0313fb4 100644 --- a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotView.java +++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotView.java @@ -137,13 +137,11 @@ public class ScreenshotView extends FrameLayout implements private int mNavMode; private boolean mOrientationPortrait; private boolean mDirectionLTR; - private int mStaticLeftMargin; private ScreenshotSelectorView mScreenshotSelectorView; private ImageView mScrollingScrim; private View mScreenshotStatic; private ImageView mScreenshotPreview; - private View mTransitionView; private View mScreenshotPreviewBorder; private ImageView mScrollablePreview; private ImageView mScreenshotFlash; @@ -341,7 +339,6 @@ public class ScreenshotView extends FrameLayout implements mScrollingScrim = requireNonNull(findViewById(R.id.screenshot_scrolling_scrim)); mScreenshotStatic = requireNonNull(findViewById(R.id.global_screenshot_static)); mScreenshotPreview = requireNonNull(findViewById(R.id.global_screenshot_preview)); - mTransitionView = requireNonNull(findViewById(R.id.screenshot_transition_view)); mScreenshotPreviewBorder = requireNonNull( findViewById(R.id.global_screenshot_preview_border)); mScreenshotPreview.setClipToOutline(true); @@ -387,12 +384,8 @@ public class ScreenshotView extends FrameLayout implements requestFocus(); } - View getTransitionView() { - return mTransitionView; - } - - int getStaticLeftMargin() { - return mStaticLeftMargin; + View getScreenshotPreview() { + return mScreenshotPreview; } /** @@ -442,7 +435,6 @@ public class ScreenshotView extends FrameLayout implements Math.max(navBarInsets.bottom, waterfall.bottom)); } } - mStaticLeftMargin = p.leftMargin; mScreenshotStatic.setLayoutParams(p); mScreenshotStatic.requestLayout(); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/OperatorNameViewController.java b/packages/SystemUI/src/com/android/systemui/statusbar/OperatorNameViewController.java index e49f48f2cf49..bcba5cc09c5d 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/OperatorNameViewController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/OperatorNameViewController.java @@ -26,7 +26,7 @@ import com.android.keyguard.KeyguardUpdateMonitor; import com.android.keyguard.KeyguardUpdateMonitorCallback; import com.android.systemui.demomode.DemoModeCommandReceiver; import com.android.systemui.plugins.DarkIconDispatcher; -import com.android.systemui.statusbar.policy.NetworkController; +import com.android.systemui.statusbar.connectivity.NetworkController; import com.android.systemui.tuner.TunerService; import com.android.systemui.util.ViewController; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/AccessPointControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/AccessPointControllerImpl.java index 6d6320e6962d..a23d73d7ed84 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/AccessPointControllerImpl.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/AccessPointControllerImpl.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.systemui.statusbar.policy; +package com.android.systemui.statusbar.connectivity; import android.content.Context; import android.content.Intent; @@ -55,6 +55,7 @@ import java.util.concurrent.Executor; import javax.inject.Inject; +/** */ public class AccessPointControllerImpl implements NetworkController.AccessPointController, WifiPickerTracker.WifiPickerTrackerCallback, LifecycleOwner { @@ -116,17 +117,19 @@ public class AccessPointControllerImpl super.finalize(); } + /** */ public boolean canConfigWifi() { return !mUserManager.hasUserRestriction(UserManager.DISALLOW_CONFIG_WIFI, new UserHandle(mCurrentUser)); } + /** */ public boolean canConfigMobileData() { return !mUserManager.hasUserRestriction(UserManager.DISALLOW_CONFIG_MOBILE_NETWORKS, UserHandle.of(mCurrentUser)) && mUserTracker.getUserInfo().isAdmin(); } - public void onUserSwitched(int newUserId) { + void onUserSwitched(int newUserId) { mCurrentUser = newUserId; } @@ -225,7 +228,7 @@ public class AccessPointControllerImpl } } - public void dump(PrintWriter pw) { + void dump(PrintWriter pw) { IndentingPrintWriter ipw = new IndentingPrintWriter(pw); ipw.println("AccessPointControllerImpl:"); ipw.increaseIndent(); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/CallbackHandler.java b/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/CallbackHandler.java index 7ac6d63430d6..052a789200e6 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/CallbackHandler.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/CallbackHandler.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.android.systemui.statusbar.policy; +package com.android.systemui.statusbar.connectivity; import android.os.Handler; import android.os.Looper; @@ -22,11 +22,11 @@ import android.telephony.SubscriptionInfo; import com.android.internal.annotations.VisibleForTesting; import com.android.systemui.dagger.qualifiers.Main; -import com.android.systemui.statusbar.policy.NetworkController.EmergencyListener; -import com.android.systemui.statusbar.policy.NetworkController.IconState; -import com.android.systemui.statusbar.policy.NetworkController.MobileDataIndicators; -import com.android.systemui.statusbar.policy.NetworkController.SignalCallback; -import com.android.systemui.statusbar.policy.NetworkController.WifiIndicators; +import com.android.systemui.statusbar.connectivity.NetworkController.EmergencyListener; +import com.android.systemui.statusbar.connectivity.NetworkController.IconState; +import com.android.systemui.statusbar.connectivity.NetworkController.MobileDataIndicators; +import com.android.systemui.statusbar.connectivity.NetworkController.SignalCallback; +import com.android.systemui.statusbar.connectivity.NetworkController.WifiIndicators; import java.io.PrintWriter; import java.text.SimpleDateFormat; @@ -235,7 +235,7 @@ public class CallbackHandler extends Handler implements EmergencyListener, Signa .append("icon=").append(icon) .toString(); recordLastCallback(log); - obtainMessage(MSG_ETHERNET_CHANGED, icon).sendToTarget();; + obtainMessage(MSG_ETHERNET_CHANGED, icon).sendToTarget(); } @Override @@ -252,14 +252,14 @@ public class CallbackHandler extends Handler implements EmergencyListener, Signa .toString(); recordLastCallback(log); } - obtainMessage(MSG_AIRPLANE_MODE_CHANGED, icon).sendToTarget();; + obtainMessage(MSG_AIRPLANE_MODE_CHANGED, icon).sendToTarget(); } - public void setListening(EmergencyListener listener, boolean listening) { + void setListening(EmergencyListener listener, boolean listening) { obtainMessage(MSG_ADD_REMOVE_EMERGENCY, listening ? 1 : 0, 0, listener).sendToTarget(); } - public void setListening(SignalCallback listener, boolean listening) { + void setListening(SignalCallback listener, boolean listening) { obtainMessage(MSG_ADD_REMOVE_SIGNAL, listening ? 1 : 0, 0, listener).sendToTarget(); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/EthernetIcons.java b/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/EthernetIcons.java index b391bd9cf651..196aad99dcb5 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/EthernetIcons.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/EthernetIcons.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.systemui.statusbar.policy; +package com.android.systemui.statusbar.connectivity; import com.android.systemui.R; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/EthernetSignalController.java b/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/EthernetSignalController.java index 80b75a740965..c9d40adde644 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/EthernetSignalController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/EthernetSignalController.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.android.systemui.statusbar.policy; +package com.android.systemui.statusbar.connectivity; import android.content.Context; import android.net.NetworkCapabilities; @@ -21,12 +21,12 @@ import android.net.NetworkCapabilities; import com.android.settingslib.AccessibilityContentDescriptions; import com.android.settingslib.SignalIcon.IconGroup; import com.android.settingslib.SignalIcon.State; -import com.android.systemui.statusbar.policy.NetworkController.IconState; -import com.android.systemui.statusbar.policy.NetworkController.SignalCallback; +import com.android.systemui.statusbar.connectivity.NetworkController.IconState; +import com.android.systemui.statusbar.connectivity.NetworkController.SignalCallback; import java.util.BitSet; - +/** */ public class EthernetSignalController extends SignalController<State, IconGroup> { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/MobileSignalController.java b/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/MobileSignalController.java index a543c7c95d16..20ef4eec0b97 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/MobileSignalController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/MobileSignalController.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.android.systemui.statusbar.policy; +package com.android.systemui.statusbar.connectivity; import static com.android.settingslib.mobile.MobileMappings.getDefaultIcons; import static com.android.settingslib.mobile.MobileMappings.getIconKey; @@ -58,9 +58,9 @@ import com.android.settingslib.mobile.TelephonyIcons; import com.android.settingslib.net.SignalStrengthUtil; import com.android.systemui.R; import com.android.systemui.flags.FeatureFlags; -import com.android.systemui.statusbar.policy.NetworkController.IconState; -import com.android.systemui.statusbar.policy.NetworkController.MobileDataIndicators; -import com.android.systemui.statusbar.policy.NetworkController.SignalCallback; +import com.android.systemui.statusbar.connectivity.NetworkController.IconState; +import com.android.systemui.statusbar.connectivity.NetworkController.MobileDataIndicators; +import com.android.systemui.statusbar.connectivity.NetworkController.SignalCallback; import com.android.systemui.util.CarrierConfigTracker; import java.io.PrintWriter; @@ -245,7 +245,7 @@ public class MobileSignalController extends SignalController<MobileState, Mobile mProviderModelSetting = featureFlags.isProviderModelSettingEnabled(); } - public void setConfiguration(Config config) { + void setConfiguration(Config config) { mConfig = config; updateInflateSignalStrength(); mNetworkToIconLookup = mapIconSets(mConfig); @@ -253,12 +253,12 @@ public class MobileSignalController extends SignalController<MobileState, Mobile updateTelephony(); } - public void setAirplaneMode(boolean airplaneMode) { + void setAirplaneMode(boolean airplaneMode) { mCurrentState.airplaneMode = airplaneMode; notifyListenersIfNecessary(); } - public void setUserSetupComplete(boolean userSetup) { + void setUserSetupComplete(boolean userSetup) { mCurrentState.userSetup = userSetup; notifyListenersIfNecessary(); } @@ -272,7 +272,7 @@ public class MobileSignalController extends SignalController<MobileState, Mobile notifyListenersIfNecessary(); } - public void setCarrierNetworkChangeMode(boolean carrierNetworkChangeMode) { + void setCarrierNetworkChangeMode(boolean carrierNetworkChangeMode) { mCurrentState.carrierNetworkChangeMode = carrierNetworkChangeMode; updateTelephony(); } @@ -413,7 +413,7 @@ public class MobileSignalController extends SignalController<MobileState, Mobile if (mCurrentState.dataSim) { // If using provider model behavior, only show QS icons if the state is also default if (pm && !mCurrentState.isDefault) { - return new QsInfo(qsTypeIcon, qsIcon, qsDescription); + return new QsInfo(qsTypeIcon, qsIcon, qsDescription); } if (mCurrentState.showQuickSettingsRatIcon() || mConfig.alwaysShowDataRatIcon) { @@ -501,7 +501,7 @@ public class MobileSignalController extends SignalController<MobileState, Mobile return mCurrentState.carrierNetworkChangeMode; } - public void handleBroadcast(Intent intent) { + void handleBroadcast(Intent intent) { String action = intent.getAction(); if (action.equals(TelephonyManager.ACTION_SERVICE_PROVIDERS_UPDATED)) { updateNetworkName(intent.getBooleanExtra(TelephonyManager.EXTRA_SHOW_SPN, false), @@ -740,10 +740,10 @@ public class MobileSignalController extends SignalController<MobileState, Mobile * mTelephonyDisplayInfo, and mSimState. It should be called any time one of these is updated. * This will call listeners if necessary. */ - private final void updateTelephony() { + private void updateTelephony() { if (Log.isLoggable(mTag, Log.DEBUG)) { - Log.d(mTag, "updateTelephonySignalStrength: hasService=" + - Utils.isInService(mServiceState) + " ss=" + mSignalStrength + Log.d(mTag, "updateTelephonySignalStrength: hasService=" + + Utils.isInService(mServiceState) + " ss=" + mSignalStrength + " displayInfo=" + mTelephonyDisplayInfo); } checkDefaultData(); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkController.java b/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/NetworkController.java index 9aec9038fadb..143309658779 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/NetworkController.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.systemui.statusbar.policy; +package com.android.systemui.statusbar.connectivity; import android.content.Context; import android.content.Intent; @@ -22,29 +22,44 @@ import android.telephony.SubscriptionInfo; import com.android.settingslib.net.DataUsageController; import com.android.systemui.demomode.DemoMode; -import com.android.systemui.statusbar.policy.NetworkController.SignalCallback; +import com.android.systemui.statusbar.connectivity.NetworkController.SignalCallback; +import com.android.systemui.statusbar.policy.CallbackController; +import com.android.systemui.statusbar.policy.DataSaverController; import com.android.wifitrackerlib.MergedCarrierEntry; import com.android.wifitrackerlib.WifiEntry; import java.util.List; +/** */ public interface NetworkController extends CallbackController<SignalCallback>, DemoMode { - + /** */ boolean hasMobileDataFeature(); + /** */ void setWifiEnabled(boolean enabled); + /** */ AccessPointController getAccessPointController(); + /** */ DataUsageController getMobileDataController(); + /** */ DataSaverController getDataSaverController(); + /** */ String getMobileDataNetworkName(); + /** */ boolean isMobileDataNetworkInService(); + /** */ int getNumberSubscriptions(); + /** */ boolean hasVoiceCallingFeature(); + /** */ void addEmergencyListener(EmergencyListener listener); + /** */ void removeEmergencyListener(EmergencyListener listener); + /** */ boolean hasEmergencyCryptKeeperText(); + /** */ boolean isRadioOn(); /** @@ -143,7 +158,8 @@ public interface NetworkController extends CallbackController<SignalCallback>, D } } - public interface SignalCallback { + /** */ + interface SignalCallback { /** * Callback for listeners to be able to update the state of any UI tracking connectivity of * WiFi networks. @@ -156,14 +172,19 @@ public interface NetworkController extends CallbackController<SignalCallback>, D */ default void setMobileDataIndicators(MobileDataIndicators mobileDataIndicators) {} + /** */ default void setSubs(List<SubscriptionInfo> subs) {} + /** */ default void setNoSims(boolean show, boolean simDetected) {} + /** */ default void setEthernetIndicators(IconState icon) {} + /** */ default void setIsAirplaneMode(IconState icon) {} + /** */ default void setMobileDataEnabled(boolean enabled) {} /** diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/NetworkControllerImpl.java index 156def5a6183..d9b0ee7e9921 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/NetworkControllerImpl.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.systemui.statusbar.policy; +package com.android.systemui.statusbar.connectivity; import static android.net.NetworkCapabilities.NET_CAPABILITY_VALIDATED; import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR; @@ -76,7 +76,12 @@ import com.android.systemui.flags.FeatureFlags; import com.android.systemui.qs.tiles.dialog.InternetDialogFactory; import com.android.systemui.qs.tiles.dialog.InternetDialogUtil; import com.android.systemui.settings.CurrentUserTracker; +import com.android.systemui.statusbar.policy.ConfigurationController; +import com.android.systemui.statusbar.policy.DataSaverController; +import com.android.systemui.statusbar.policy.DataSaverControllerImpl; +import com.android.systemui.statusbar.policy.DeviceProvisionedController; import com.android.systemui.statusbar.policy.DeviceProvisionedController.DeviceProvisionedListener; +import com.android.systemui.statusbar.policy.EncryptionHelper; import com.android.systemui.telephony.TelephonyListenerManager; import com.android.systemui.util.CarrierConfigTracker; @@ -376,11 +381,10 @@ public class NetworkControllerImpl extends BroadcastReceiver @Override public void onCapabilitiesChanged( - Network network, NetworkCapabilities networkCapabilities) { - boolean lastValidated = (mLastNetworkCapabilities != null) && - mLastNetworkCapabilities.hasCapability(NET_CAPABILITY_VALIDATED); - boolean validated = - networkCapabilities.hasCapability(NET_CAPABILITY_VALIDATED); + Network network, NetworkCapabilities networkCapabilities) { + boolean lastValidated = (mLastNetworkCapabilities != null) + && mLastNetworkCapabilities.hasCapability(NET_CAPABILITY_VALIDATED); + boolean validated = networkCapabilities.hasCapability(NET_CAPABILITY_VALIDATED); // This callback is invoked a lot (i.e. when RSSI changes), so avoid updating // icons when connectivity state has remained the same. @@ -544,19 +548,23 @@ public class NetworkControllerImpl extends BroadcastReceiver return mDataUsageController; } + /** */ public void addEmergencyListener(EmergencyListener listener) { mCallbackHandler.setListening(listener, true); mCallbackHandler.setEmergencyCallsOnly(isEmergencyOnly()); } + /** */ public void removeEmergencyListener(EmergencyListener listener) { mCallbackHandler.setListening(listener, false); } + /** */ public boolean hasMobileDataFeature() { return mHasMobileDataFeature; } + /** */ public boolean hasVoiceCallingFeature() { return mPhone.getPhoneType() != TelephonyManager.PHONE_TYPE_NONE; } @@ -661,7 +669,7 @@ public class NetworkControllerImpl extends BroadcastReceiver } } - public boolean isEmergencyOnly() { + boolean isEmergencyOnly() { if (mMobileSignalControllers.size() == 0) { // When there are no active subscriptions, determine emengency state from last // broadcast. @@ -690,8 +698,10 @@ public class NetworkControllerImpl extends BroadcastReceiver if (mMobileSignalControllers.size() == 1) { mEmergencySource = EMERGENCY_ASSUMED_VOICE_CONTROLLER + mMobileSignalControllers.keyAt(0); - if (DEBUG) Log.d(TAG, "Getting assumed emergency from " - + mMobileSignalControllers.keyAt(0)); + if (DEBUG) { + Log.d(TAG, "Getting assumed emergency from " + + mMobileSignalControllers.keyAt(0)); + } return mMobileSignalControllers.valueAt(0).getState().isEmergency; } if (DEBUG) Log.e(TAG, "Cannot find controller for voice sub: " + voiceSubId); @@ -915,7 +925,7 @@ public class NetworkControllerImpl extends BroadcastReceiver @GuardedBy("mLock") @VisibleForTesting - public void setCurrentSubscriptionsLocked(List<SubscriptionInfo> subscriptions) { + void setCurrentSubscriptionsLocked(List<SubscriptionInfo> subscriptions) { Collections.sort(subscriptions, new Comparator<SubscriptionInfo>() { @Override public int compare(SubscriptionInfo lhs, SubscriptionInfo rhs) { @@ -1125,6 +1135,7 @@ public class NetworkControllerImpl extends BroadcastReceiver mEthernetSignalController.updateConnectivity(mConnectedTransports, mValidatedTransports); } + /** */ public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { pw.println("NetworkController state:"); @@ -1178,7 +1189,7 @@ public class NetworkControllerImpl extends BroadcastReceiver mCallbackHandler.dump(pw); } - private static final String emergencyToString(int emergencySource) { + private static String emergencyToString(int emergencySource) { if (emergencySource > EMERGENCY_NO_SUB) { return "ASSUMED_VOICE_CONTROLLER(" + (emergencySource - EMERGENCY_VOICE_CONTROLLER) + ")"; @@ -1423,10 +1434,12 @@ public class NetworkControllerImpl extends BroadcastReceiver return info; } + /** */ public boolean hasEmergencyCryptKeeperText() { return EncryptionHelper.IS_DATA_ENCRYPTED; } + /** */ public boolean isRadioOn() { return !mAirplaneMode; } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/SignalController.java b/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/SignalController.java index 4b6722c17b85..d23dba579be6 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/SignalController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/SignalController.java @@ -13,9 +13,9 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.android.systemui.statusbar.policy; +package com.android.systemui.statusbar.connectivity; -import static com.android.systemui.statusbar.policy.NetworkControllerImpl.TAG; +import static com.android.systemui.statusbar.connectivity.NetworkControllerImpl.TAG; import android.annotation.NonNull; import android.content.Context; @@ -23,8 +23,8 @@ import android.util.Log; import com.android.settingslib.SignalIcon.IconGroup; import com.android.settingslib.SignalIcon.State; -import com.android.systemui.statusbar.policy.NetworkController.IconState; -import com.android.systemui.statusbar.policy.NetworkController.SignalCallback; +import com.android.systemui.statusbar.connectivity.NetworkController.IconState; +import com.android.systemui.statusbar.connectivity.NetworkController.SignalCallback; import java.io.PrintWriter; import java.util.BitSet; @@ -83,7 +83,7 @@ public abstract class SignalController<T extends State, I extends IconGroup> { return mCurrentState; } - public void updateConnectivity(BitSet connectedTransports, BitSet validatedTransports) { + void updateConnectivity(BitSet connectedTransports, BitSet validatedTransports) { mCurrentState.inetCondition = validatedTransports.get(mTransportType) ? 1 : 0; notifyListenersIfNecessary(); } @@ -114,7 +114,7 @@ public abstract class SignalController<T extends State, I extends IconGroup> { return false; } - public void saveLastState() { + void saveLastState() { if (RECORD_HISTORY) { recordLastState(); } @@ -161,7 +161,7 @@ public abstract class SignalController<T extends State, I extends IconGroup> { } } - public void notifyListenersIfNecessary() { + void notifyListenersIfNecessary() { if (isDirty()) { saveLastState(); notifyListeners(); @@ -192,7 +192,7 @@ public abstract class SignalController<T extends State, I extends IconGroup> { mHistoryIndex = (mHistoryIndex + 1) % HISTORY_SIZE; } - public void dump(PrintWriter pw) { + void dump(PrintWriter pw) { pw.println(" - " + mTag + " -----"); pw.println(" Current State: " + mCurrentState); if (RECORD_HISTORY) { @@ -210,7 +210,7 @@ public abstract class SignalController<T extends State, I extends IconGroup> { } } - public final void notifyListeners() { + final void notifyListeners() { notifyListeners(mCallbackHandler); } @@ -219,7 +219,7 @@ public abstract class SignalController<T extends State, I extends IconGroup> { * based on current state, and only need to be called in the scenario where * mCurrentState != mLastState. */ - public abstract void notifyListeners(SignalCallback callback); + abstract void notifyListeners(SignalCallback callback); /** * Generate a blank T. diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/WifiIcons.java b/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/WifiIcons.java index 577cc4fd16b6..3c449ad270ef 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/WifiIcons.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/WifiIcons.java @@ -14,12 +14,13 @@ * limitations under the License. */ -package com.android.systemui.statusbar.policy; +package com.android.systemui.statusbar.connectivity; import com.android.settingslib.AccessibilityContentDescriptions; import com.android.settingslib.R; import com.android.settingslib.SignalIcon.IconGroup; +/** */ public class WifiIcons { static final int[] WIFI_FULL_ICONS = { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/WifiSignalController.java b/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/WifiSignalController.java index 3f7ddc6c1538..3622a66767a3 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/WifiSignalController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/WifiSignalController.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.android.systemui.statusbar.policy; +package com.android.systemui.statusbar.connectivity; import static android.net.wifi.WifiManager.TrafficStateCallback.DATA_ACTIVITY_IN; import static android.net.wifi.WifiManager.TrafficStateCallback.DATA_ACTIVITY_INOUT; @@ -37,14 +37,15 @@ import com.android.settingslib.mobile.TelephonyIcons; import com.android.settingslib.wifi.WifiStatusTracker; import com.android.systemui.R; import com.android.systemui.flags.FeatureFlags; -import com.android.systemui.statusbar.policy.NetworkController.IconState; -import com.android.systemui.statusbar.policy.NetworkController.MobileDataIndicators; -import com.android.systemui.statusbar.policy.NetworkController.SignalCallback; -import com.android.systemui.statusbar.policy.NetworkController.WifiIndicators; +import com.android.systemui.statusbar.connectivity.NetworkController.IconState; +import com.android.systemui.statusbar.connectivity.NetworkController.MobileDataIndicators; +import com.android.systemui.statusbar.connectivity.NetworkController.SignalCallback; +import com.android.systemui.statusbar.connectivity.NetworkController.WifiIndicators; import java.io.PrintWriter; import java.util.Objects; +/** */ public class WifiSignalController extends SignalController<WifiSignalController.WifiState, IconGroup> { private final boolean mHasMobileDataFeature; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/dagger/StatusBarDependenciesModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/dagger/StatusBarDependenciesModule.java index ea00d920c9b0..6730afaafdef 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/dagger/StatusBarDependenciesModule.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/dagger/StatusBarDependenciesModule.java @@ -252,11 +252,12 @@ public interface StatusBarDependenciesModule { ActivityStarter activityStarter, @Main Executor mainExecutor, IActivityManager iActivityManager, - OngoingCallLogger logger) { + OngoingCallLogger logger, + DumpManager dumpManager) { OngoingCallController ongoingCallController = new OngoingCallController( notifCollection, featureFlags, systemClock, activityStarter, mainExecutor, - iActivityManager, logger); + iActivityManager, logger, dumpManager); ongoingCallController.init(); return ongoingCallController; } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java index 4400909d0bff..278f09b45b4c 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java @@ -254,6 +254,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable private boolean mExpandedInThisMotion; private boolean mShouldShowShelfOnly; protected boolean mScrollingEnabled; + private boolean mIsCurrentUserSetup; protected FooterView mFooterView; protected EmptyShadeView mEmptyShadeView; private boolean mDismissAllInProgress; @@ -693,6 +694,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable boolean showDismissView = mClearAllEnabled && mController.hasActiveClearableNotifications(ROWS_ALL); boolean showFooterView = (showDismissView || getVisibleNotificationCount() > 0) + && mIsCurrentUserSetup // see: b/193149550 && mStatusBarState != StatusBarState.KEYGUARD && !mUnlockedScreenOffAnimationController.isScreenOffAnimationPlaying() && !mIsRemoteInputActive; @@ -5621,6 +5623,16 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable } /** + * Sets whether the current user is set up, which is required to show the footer (b/193149550) + */ + public void setCurrentUserSetup(boolean isCurrentUserSetup) { + if (mIsCurrentUserSetup != isCurrentUserSetup) { + mIsCurrentUserSetup = isCurrentUserSetup; + updateFooter(); + } + } + + /** * A listener that is notified when the empty space below the notifications is clicked on */ @ShadeViewRefactor(RefactorComponent.SHADE_VIEW) diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java index 75eb88fbcfc8..9fe06a0ddbdc 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java @@ -119,6 +119,8 @@ import com.android.systemui.statusbar.phone.StatusBar; import com.android.systemui.statusbar.phone.dagger.StatusBarComponent; import com.android.systemui.statusbar.policy.ConfigurationController; import com.android.systemui.statusbar.policy.ConfigurationController.ConfigurationListener; +import com.android.systemui.statusbar.policy.DeviceProvisionedController; +import com.android.systemui.statusbar.policy.DeviceProvisionedController.DeviceProvisionedListener; import com.android.systemui.statusbar.policy.OnHeadsUpChangedListener; import com.android.systemui.statusbar.policy.ZenModeController; import com.android.systemui.tuner.TunerService; @@ -146,6 +148,7 @@ public class NotificationStackScrollLayoutController { private final HeadsUpManagerPhone mHeadsUpManager; private final NotificationRoundnessManager mNotificationRoundnessManager; private final TunerService mTunerService; + private final DeviceProvisionedController mDeviceProvisionedController; private final DynamicPrivacyController mDynamicPrivacyController; private final ConfigurationController mConfigurationController; private final ZenModeController mZenModeController; @@ -226,6 +229,28 @@ public class NotificationStackScrollLayoutController { } }; + private final DeviceProvisionedListener mDeviceProvisionedListener = + new DeviceProvisionedListener() { + @Override + public void onDeviceProvisionedChanged() { + updateCurrentUserIsSetup(); + } + + @Override + public void onUserSwitched() { + updateCurrentUserIsSetup(); + } + + @Override + public void onUserSetupChanged() { + updateCurrentUserIsSetup(); + } + + private void updateCurrentUserIsSetup() { + mView.setCurrentUserSetup(mDeviceProvisionedController.isCurrentUserSetup()); + } + }; + private final DynamicPrivacyController.Listener mDynamicPrivacyControllerListener = () -> { if (mView.isExpanded()) { // The bottom might change because we're using the final actual height of the view @@ -600,6 +625,7 @@ public class NotificationStackScrollLayoutController { HeadsUpManagerPhone headsUpManager, NotificationRoundnessManager notificationRoundnessManager, TunerService tunerService, + DeviceProvisionedController deviceProvisionedController, DynamicPrivacyController dynamicPrivacyController, ConfigurationController configurationController, SysuiStatusBarStateController statusBarStateController, @@ -636,6 +662,7 @@ public class NotificationStackScrollLayoutController { mHeadsUpManager = headsUpManager; mNotificationRoundnessManager = notificationRoundnessManager; mTunerService = tunerService; + mDeviceProvisionedController = deviceProvisionedController; mDynamicPrivacyController = dynamicPrivacyController; mConfigurationController = configurationController; mStatusBarStateController = statusBarStateController; @@ -787,6 +814,9 @@ public class NotificationStackScrollLayoutController { return Unit.INSTANCE; }); + // callback is invoked synchronously, updating mView immediately + mDeviceProvisionedController.addCallback(mDeviceProvisionedListener); + if (mView.isAttachedToWindow()) { mOnAttachStateChangeListener.onViewAttachedToWindow(mView); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragment.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragment.java index c16cc125aece..989f6b85668b 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragment.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragment.java @@ -46,6 +46,8 @@ import com.android.systemui.statusbar.DisableFlagsLogger.DisableState; import com.android.systemui.statusbar.OperatorNameView; import com.android.systemui.statusbar.OperatorNameViewController; import com.android.systemui.statusbar.StatusBarState; +import com.android.systemui.statusbar.connectivity.NetworkController; +import com.android.systemui.statusbar.connectivity.NetworkController.SignalCallback; import com.android.systemui.statusbar.events.SystemStatusAnimationCallback; import com.android.systemui.statusbar.events.SystemStatusAnimationScheduler; import com.android.systemui.statusbar.phone.StatusBarIconController.DarkIconManager; @@ -53,8 +55,6 @@ import com.android.systemui.statusbar.phone.ongoingcall.OngoingCallController; import com.android.systemui.statusbar.phone.ongoingcall.OngoingCallListener; import com.android.systemui.statusbar.policy.EncryptionHelper; import com.android.systemui.statusbar.policy.KeyguardStateController; -import com.android.systemui.statusbar.policy.NetworkController; -import com.android.systemui.statusbar.policy.NetworkController.SignalCallback; import org.jetbrains.annotations.NotNull; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewController.java index 893aa6d6bc45..5feb4053f833 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewController.java @@ -101,8 +101,8 @@ public class KeyguardStatusBarViewController extends ViewController<KeyguardStat @Override public void onOverlayChanged() { - KeyguardStatusBarViewController.this.onThemeChanged(); mView.onOverlayChanged(); + KeyguardStatusBarViewController.this.onThemeChanged(); } @Override diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java index 3bdc08be63e6..2130e028e863 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java @@ -199,6 +199,7 @@ import com.android.systemui.statusbar.PowerButtonReveal; import com.android.systemui.statusbar.PulseExpansionHandler; import com.android.systemui.statusbar.StatusBarState; import com.android.systemui.statusbar.SysuiStatusBarStateController; +import com.android.systemui.statusbar.connectivity.NetworkController; import com.android.systemui.statusbar.events.SystemStatusAnimationScheduler; import com.android.systemui.statusbar.notification.DynamicPrivacyController; import com.android.systemui.statusbar.notification.NotificationActivityStarter; @@ -226,7 +227,6 @@ import com.android.systemui.statusbar.policy.DeviceProvisionedController; import com.android.systemui.statusbar.policy.DeviceProvisionedController.DeviceProvisionedListener; import com.android.systemui.statusbar.policy.ExtensionController; import com.android.systemui.statusbar.policy.KeyguardStateController; -import com.android.systemui.statusbar.policy.NetworkController; import com.android.systemui.statusbar.policy.UserInfoControllerImpl; import com.android.systemui.statusbar.policy.UserSwitcherController; import com.android.systemui.tuner.TunerService; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java index 5bc97a5b316d..18fd9d3c6651 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java @@ -1149,7 +1149,6 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb public void showBouncerMessage(String message, ColorStateList colorState) { if (isShowingAlternateAuth()) { if (mKeyguardMessageAreaController != null) { - mKeyguardMessageAreaController.setNextMessageColor(colorState); mKeyguardMessageAreaController.setMessage(message); } } else { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarSignalPolicy.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarSignalPolicy.java index 71364320839c..fbd9ef7e3707 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarSignalPolicy.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarSignalPolicy.java @@ -26,11 +26,11 @@ import com.android.settingslib.mobile.TelephonyIcons; import com.android.systemui.R; import com.android.systemui.dagger.SysUISingleton; import com.android.systemui.flags.FeatureFlags; -import com.android.systemui.statusbar.policy.NetworkController; -import com.android.systemui.statusbar.policy.NetworkController.IconState; -import com.android.systemui.statusbar.policy.NetworkController.MobileDataIndicators; -import com.android.systemui.statusbar.policy.NetworkController.WifiIndicators; -import com.android.systemui.statusbar.policy.NetworkControllerImpl; +import com.android.systemui.statusbar.connectivity.NetworkController; +import com.android.systemui.statusbar.connectivity.NetworkController.IconState; +import com.android.systemui.statusbar.connectivity.NetworkController.MobileDataIndicators; +import com.android.systemui.statusbar.connectivity.NetworkController.WifiIndicators; +import com.android.systemui.statusbar.connectivity.NetworkControllerImpl; import com.android.systemui.statusbar.policy.SecurityController; import com.android.systemui.tuner.TunerService; import com.android.systemui.tuner.TunerService.Tunable; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarPhoneModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarPhoneModule.java index 0d43b9323a39..568970790a18 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarPhoneModule.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarPhoneModule.java @@ -62,6 +62,7 @@ import com.android.systemui.statusbar.NotificationViewHierarchyManager; import com.android.systemui.statusbar.OperatorNameViewController; import com.android.systemui.statusbar.PulseExpansionHandler; import com.android.systemui.statusbar.SysuiStatusBarStateController; +import com.android.systemui.statusbar.connectivity.NetworkController; import com.android.systemui.statusbar.events.SystemStatusAnimationScheduler; import com.android.systemui.statusbar.notification.DynamicPrivacyController; import com.android.systemui.statusbar.notification.NotificationEntryManager; @@ -104,7 +105,6 @@ import com.android.systemui.statusbar.policy.ConfigurationController; import com.android.systemui.statusbar.policy.DeviceProvisionedController; import com.android.systemui.statusbar.policy.ExtensionController; import com.android.systemui.statusbar.policy.KeyguardStateController; -import com.android.systemui.statusbar.policy.NetworkController; import com.android.systemui.statusbar.policy.UserInfoControllerImpl; import com.android.systemui.statusbar.policy.UserSwitcherController; import com.android.systemui.tuner.TunerService; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallController.kt index eeff010e0bdf..ab6ee89627ef 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallController.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallController.kt @@ -26,10 +26,12 @@ import android.util.Log import android.view.View import androidx.annotation.VisibleForTesting import com.android.internal.jank.InteractionJankMonitor +import com.android.systemui.Dumpable import com.android.systemui.R import com.android.systemui.animation.ActivityLaunchAnimator import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Main +import com.android.systemui.dump.DumpManager import com.android.systemui.plugins.ActivityStarter import com.android.systemui.flags.FeatureFlags import com.android.systemui.statusbar.notification.collection.NotificationEntry @@ -37,6 +39,8 @@ import com.android.systemui.statusbar.notification.collection.notifcollection.Co import com.android.systemui.statusbar.notification.collection.notifcollection.NotifCollectionListener import com.android.systemui.statusbar.policy.CallbackController import com.android.systemui.util.time.SystemClock +import java.io.FileDescriptor +import java.io.PrintWriter import java.util.concurrent.Executor import javax.inject.Inject @@ -51,13 +55,14 @@ class OngoingCallController @Inject constructor( private val activityStarter: ActivityStarter, @Main private val mainExecutor: Executor, private val iActivityManager: IActivityManager, - private val logger: OngoingCallLogger -) : CallbackController<OngoingCallListener> { + private val logger: OngoingCallLogger, + private val dumpManager: DumpManager, +) : CallbackController<OngoingCallListener>, Dumpable { /** Non-null if there's an active call notification. */ private var callNotificationInfo: CallNotificationInfo? = null /** True if the application managing the call is visible to the user. */ - private var isCallAppVisible: Boolean = true + private var isCallAppVisible: Boolean = false private var chipView: View? = null private var uidObserver: IUidObserver.Stub? = null @@ -103,16 +108,7 @@ class OngoingCallController @Inject constructor( } } - // Fix for b/199600334 - override fun onEntryCleanUp(entry: NotificationEntry) { - removeChipIfNeeded(entry) - } - override fun onEntryRemoved(entry: NotificationEntry, reason: Int) { - removeChipIfNeeded(entry) - } - - private fun removeChipIfNeeded(entry: NotificationEntry) { if (entry.sbn.key == callNotificationInfo?.key) { removeChip() } @@ -120,6 +116,7 @@ class OngoingCallController @Inject constructor( } fun init() { + dumpManager.registerDumpable(this) if (featureFlags.isOngoingCallStatusBarChipEnabled) { notifCollection.addCollectionListener(notifListener) } @@ -299,6 +296,11 @@ class OngoingCallController @Inject constructor( */ fun hasValidStartTime(): Boolean = callStartTime > 0 } + + override fun dump(fd: FileDescriptor, pw: PrintWriter, args: Array<out String>) { + pw.println("Active call notification: $callNotificationInfo") + pw.println("Call app visible: $isCallAppVisible") + } } private fun isCallNotification(entry: NotificationEntry): Boolean { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/DevicePostureController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/DevicePostureController.java index 85968753d54f..bbba19d61b5a 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/DevicePostureController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/DevicePostureController.java @@ -46,6 +46,7 @@ public interface DevicePostureController extends CallbackController<Callback> { int DEVICE_POSTURE_HALF_OPENED = 2; int DEVICE_POSTURE_OPENED = 3; int DEVICE_POSTURE_FLIPPED = 4; + int SUPPORTED_POSTURES_SIZE = DEVICE_POSTURE_FLIPPED + 1; /** Return the current device posture. */ @DevicePostureInt int getDevicePosture(); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/dagger/StatusBarPolicyModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/dagger/StatusBarPolicyModule.java index 1eec639670ee..92908790770a 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/dagger/StatusBarPolicyModule.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/dagger/StatusBarPolicyModule.java @@ -23,7 +23,9 @@ import com.android.internal.R; import com.android.systemui.dagger.SysUISingleton; import com.android.systemui.dagger.qualifiers.Main; import com.android.systemui.settings.UserTracker; -import com.android.systemui.statusbar.policy.AccessPointControllerImpl; +import com.android.systemui.statusbar.connectivity.AccessPointControllerImpl; +import com.android.systemui.statusbar.connectivity.NetworkController; +import com.android.systemui.statusbar.connectivity.NetworkControllerImpl; import com.android.systemui.statusbar.policy.BluetoothController; import com.android.systemui.statusbar.policy.BluetoothControllerImpl; import com.android.systemui.statusbar.policy.CastController; @@ -42,8 +44,6 @@ import com.android.systemui.statusbar.policy.KeyguardStateController; import com.android.systemui.statusbar.policy.KeyguardStateControllerImpl; import com.android.systemui.statusbar.policy.LocationController; import com.android.systemui.statusbar.policy.LocationControllerImpl; -import com.android.systemui.statusbar.policy.NetworkController; -import com.android.systemui.statusbar.policy.NetworkControllerImpl; import com.android.systemui.statusbar.policy.NextAlarmController; import com.android.systemui.statusbar.policy.NextAlarmControllerImpl; import com.android.systemui.statusbar.policy.RotationLockController; diff --git a/packages/SystemUI/src/com/android/systemui/unfold/ShellUnfoldProgressProvider.kt b/packages/SystemUI/src/com/android/systemui/unfold/ShellUnfoldProgressProvider.kt new file mode 100644 index 000000000000..4a884359d315 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/unfold/ShellUnfoldProgressProvider.kt @@ -0,0 +1,49 @@ +/* + * Copyright (C) 2021 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.systemui.unfold + +import com.android.systemui.unfold.UnfoldTransitionProgressProvider.TransitionProgressListener +import com.android.wm.shell.unfold.ShellUnfoldProgressProvider +import com.android.wm.shell.unfold.ShellUnfoldProgressProvider.UnfoldListener +import java.util.concurrent.Executor + +class ShellUnfoldProgressProvider( + private val unfoldProgressProvider: UnfoldTransitionProgressProvider +) : ShellUnfoldProgressProvider { + + override fun addListener(executor: Executor, listener: UnfoldListener) { + unfoldProgressProvider.addCallback(object : TransitionProgressListener { + override fun onTransitionStarted() { + executor.execute { + listener.onStateChangeStarted() + } + } + + override fun onTransitionProgress(progress: Float) { + executor.execute { + listener.onStateChangeProgress(progress) + } + } + + override fun onTransitionFinished() { + executor.execute { + listener.onStateChangeFinished() + } + } + }) + } +} diff --git a/packages/SystemUI/src/com/android/systemui/unfold/UnfoldTransitionModule.kt b/packages/SystemUI/src/com/android/systemui/unfold/UnfoldTransitionModule.kt new file mode 100644 index 000000000000..b23aa20dcaa4 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/unfold/UnfoldTransitionModule.kt @@ -0,0 +1,74 @@ +/* + * Copyright (C) 2021 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.systemui.unfold + +import android.content.Context +import android.hardware.SensorManager +import android.hardware.devicestate.DeviceStateManager +import android.os.Handler +import com.android.systemui.dagger.qualifiers.Main +import com.android.systemui.keyguard.LifecycleScreenStatusProvider +import com.android.systemui.unfold.config.UnfoldTransitionConfig +import com.android.wm.shell.unfold.ShellUnfoldProgressProvider +import dagger.Lazy +import dagger.Module +import dagger.Provides +import java.util.Optional +import java.util.concurrent.Executor +import javax.inject.Singleton + +@Module +class UnfoldTransitionModule { + + @Provides + @Singleton + fun provideUnfoldTransitionProgressProvider( + context: Context, + config: UnfoldTransitionConfig, + screenStatusProvider: LifecycleScreenStatusProvider, + deviceStateManager: DeviceStateManager, + sensorManager: SensorManager, + @Main executor: Executor, + @Main handler: Handler + ): UnfoldTransitionProgressProvider = + createUnfoldTransitionProgressProvider( + context, + config, + screenStatusProvider, + deviceStateManager, + sensorManager, + handler, + executor + ) + + @Provides + @Singleton + fun provideUnfoldTransitionConfig(context: Context): UnfoldTransitionConfig = + createConfig(context) + + @Provides + @Singleton + fun provideShellProgressProvider( + config: UnfoldTransitionConfig, + provider: Lazy<UnfoldTransitionProgressProvider> + ): Optional<ShellUnfoldProgressProvider> = + if (config.isEnabled) { + Optional.ofNullable(ShellUnfoldProgressProvider(provider.get())) + } else { + Optional.empty() + } +} diff --git a/packages/SystemUI/src/com/android/systemui/util/WallpaperController.kt b/packages/SystemUI/src/com/android/systemui/util/WallpaperController.kt index a9810458fe90..2c01a7003a16 100644 --- a/packages/SystemUI/src/com/android/systemui/util/WallpaperController.kt +++ b/packages/SystemUI/src/com/android/systemui/util/WallpaperController.kt @@ -40,8 +40,8 @@ class WallpaperController @Inject constructor(private val wallpaperManager: Wall this.wallpaperInfo = wallpaperInfo } - private val shouldUseDefaultDeviceStateChangeTransition: Boolean - get() = wallpaperInfo?.shouldUseDefaultDeviceStateChangeTransition() + private val shouldUseDefaultDisplayStateChangeTransition: Boolean + get() = wallpaperInfo?.shouldUseDefaultDisplayStateChangeTransition() ?: true fun setNotificationShadeZoom(zoomOut: Float) { @@ -50,7 +50,7 @@ class WallpaperController @Inject constructor(private val wallpaperManager: Wall } fun setUnfoldTransitionZoom(zoomOut: Float) { - if (shouldUseDefaultDeviceStateChangeTransition) { + if (shouldUseDefaultDisplayStateChangeTransition) { unfoldTransitionZoomOut = zoomOut updateZoom() } diff --git a/packages/SystemUI/src/com/android/systemui/util/sensors/PostureDependentProximitySensor.java b/packages/SystemUI/src/com/android/systemui/util/sensors/PostureDependentProximitySensor.java new file mode 100644 index 000000000000..28cba8e16970 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/util/sensors/PostureDependentProximitySensor.java @@ -0,0 +1,109 @@ +/* + * Copyright (C) 2021 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.systemui.util.sensors; + +import android.util.Log; + +import androidx.annotation.NonNull; + +import com.android.systemui.statusbar.policy.DevicePostureController; +import com.android.systemui.util.concurrency.DelayableExecutor; +import com.android.systemui.util.concurrency.Execution; + +import javax.inject.Inject; + +/** + * Proximity sensor that changes proximity sensor usage based on the current posture. + * Posture -> prox sensor mapping can be found in SystemUI config overlays at: + * - proximity_sensor_posture_mapping + * - proximity_sensor_secondary_posture_mapping. + * where the array indices correspond to the following postures: + * [UNKNOWN, CLOSED, HALF_OPENED, OPENED] + */ +class PostureDependentProximitySensor extends ProximitySensorImpl { + private final ThresholdSensor[] mPostureToPrimaryProxSensorMap; + private final ThresholdSensor[] mPostureToSecondaryProxSensorMap; + + @Inject + PostureDependentProximitySensor( + @PrimaryProxSensor ThresholdSensor[] postureToPrimaryProxSensorMap, + @SecondaryProxSensor ThresholdSensor[] postureToSecondaryProxSensorMap, + @NonNull DelayableExecutor delayableExecutor, + @NonNull Execution execution, + @NonNull DevicePostureController devicePostureController + ) { + super( + postureToPrimaryProxSensorMap[0], + postureToSecondaryProxSensorMap[0], + delayableExecutor, + execution + ); + mPostureToPrimaryProxSensorMap = postureToPrimaryProxSensorMap; + mPostureToSecondaryProxSensorMap = postureToSecondaryProxSensorMap; + mDevicePosture = devicePostureController.getDevicePosture(); + devicePostureController.addCallback(mDevicePostureCallback); + + chooseSensors(); + } + private void chooseSensors() { + if (mDevicePosture >= mPostureToPrimaryProxSensorMap.length + || mDevicePosture >= mPostureToSecondaryProxSensorMap.length) { + Log.e("PostureDependentProxSensor", + "unsupported devicePosture=" + mDevicePosture); + return; + } + + ThresholdSensor newPrimaryProx = mPostureToPrimaryProxSensorMap[mDevicePosture]; + ThresholdSensor newSecondaryProx = mPostureToSecondaryProxSensorMap[mDevicePosture]; + + if (newPrimaryProx != mPrimaryThresholdSensor + || newSecondaryProx != mSecondaryThresholdSensor) { + logDebug("Register new proximity sensors newPosture=" + + DevicePostureController.devicePostureToString(mDevicePosture)); + unregisterInternal(); + + if (mPrimaryThresholdSensor != null) { + mPrimaryThresholdSensor.unregister(mPrimaryEventListener); + } + if (mSecondaryThresholdSensor != null) { + mSecondaryThresholdSensor.unregister(mSecondaryEventListener); + } + + mPrimaryThresholdSensor = newPrimaryProx; + mSecondaryThresholdSensor = newSecondaryProx; + + mInitializedListeners = false; + registerInternal(); + } + } + + private final DevicePostureController.Callback mDevicePostureCallback = + posture -> { + if (mDevicePosture == posture) { + return; + } + + mDevicePosture = posture; + chooseSensors(); + }; + + @Override + public String toString() { + return String.format("{posture=%s, proximitySensor=%s}", + DevicePostureController.devicePostureToString(mDevicePosture), super.toString()); + } +} diff --git a/packages/SystemUI/src/com/android/systemui/util/sensors/ProximityCheck.java b/packages/SystemUI/src/com/android/systemui/util/sensors/ProximityCheck.java new file mode 100644 index 000000000000..a8a6341cc5ee --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/util/sensors/ProximityCheck.java @@ -0,0 +1,90 @@ +/* + * Copyright (C) 2021 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.systemui.util.sensors; + +import com.android.systemui.dagger.qualifiers.Main; +import com.android.systemui.util.concurrency.DelayableExecutor; + +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.function.Consumer; + +import javax.inject.Inject; + +/** + * Convenience class allowing for briefly checking the proximity sensor. + */ +public class ProximityCheck implements Runnable { + + private final ProximitySensor mSensor; + private final DelayableExecutor mDelayableExecutor; + private List<Consumer<Boolean>> mCallbacks = new ArrayList<>(); + private final ThresholdSensor.Listener mListener; + private final AtomicBoolean mRegistered = new AtomicBoolean(); + + @Inject + public ProximityCheck( + ProximitySensor sensor, + @Main DelayableExecutor delayableExecutor) { + mSensor = sensor; + mSensor.setTag("prox_check"); + mDelayableExecutor = delayableExecutor; + mListener = this::onProximityEvent; + } + + /** Set a descriptive tag for the sensors registration. */ + public void setTag(String tag) { + mSensor.setTag(tag); + } + + @Override + public void run() { + unregister(); + onProximityEvent(null); + } + + /** + * Query the proximity sensor, timing out if no result. + */ + public void check(long timeoutMs, Consumer<Boolean> callback) { + if (!mSensor.isLoaded()) { + callback.accept(null); + return; + } + mCallbacks.add(callback); + if (!mRegistered.getAndSet(true)) { + mSensor.register(mListener); + mDelayableExecutor.executeDelayed(this, timeoutMs); + } + } + + private void unregister() { + mSensor.unregister(mListener); + mRegistered.set(false); + } + + private void onProximityEvent(ThresholdSensorEvent proximityEvent) { + mCallbacks.forEach( + booleanConsumer -> + booleanConsumer.accept( + proximityEvent == null ? null : proximityEvent.getBelow())); + mCallbacks.clear(); + unregister(); + mRegistered.set(false); + } +} diff --git a/packages/SystemUI/src/com/android/systemui/util/sensors/ProximitySensor.java b/packages/SystemUI/src/com/android/systemui/util/sensors/ProximitySensor.java index bd1103982017..d3f1c93195a1 100644 --- a/packages/SystemUI/src/com/android/systemui/util/sensors/ProximitySensor.java +++ b/packages/SystemUI/src/com/android/systemui/util/sensors/ProximitySensor.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2019 The Android Open Source Project + * Copyright (C) 2021 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. @@ -16,149 +16,24 @@ package com.android.systemui.util.sensors; -import android.hardware.SensorManager; -import android.util.Log; - -import com.android.internal.annotations.VisibleForTesting; -import com.android.systemui.dagger.qualifiers.Main; -import com.android.systemui.util.concurrency.DelayableExecutor; -import com.android.systemui.util.concurrency.Execution; - -import java.util.ArrayList; -import java.util.List; -import java.util.concurrent.atomic.AtomicBoolean; -import java.util.function.Consumer; - -import javax.inject.Inject; - /** - * Wrapper around SensorManager customized for the Proximity sensor. - * - * The ProximitySensor supports the concept of a primary and a - * secondary hardware sensor. The primary sensor is used for a first - * pass check if the phone covered. When triggered, it then checks - * the secondary sensor for confirmation (if there is one). It does - * not send a proximity event until the secondary sensor confirms (or - * rejects) the reading. The secondary sensor is, in fact, the source - * of truth. - * - * This is necessary as sometimes keeping the secondary sensor on for - * extends periods is undesirable. It may, however, result in increased - * latency for proximity readings. - * - * Phones should configure this via a config.xml overlay. If no - * proximity sensor is set (primary or secondary) we fall back to the - * default Sensor.TYPE_PROXIMITY. If proximity_sensor_type is set in - * config.xml, that will be used as the primary sensor. If - * proximity_sensor_secondary_type is set, that will function as the - * secondary sensor. If no secondary is set, only the primary will be - * used. + * Wrapper class for a dual ProximitySensor which supports a secondary sensor gated upon the + * primary). */ -public class ProximitySensor implements ThresholdSensor { - private static final String TAG = "ProxSensor"; - private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG); - private static final long SECONDARY_PING_INTERVAL_MS = 5000; - - private final ThresholdSensor mPrimaryThresholdSensor; - private final ThresholdSensor mSecondaryThresholdSensor; - private final DelayableExecutor mDelayableExecutor; - private final Execution mExecution; - private final List<ThresholdSensor.Listener> mListeners = new ArrayList<>(); - private String mTag = null; - @VisibleForTesting protected boolean mPaused; - private ThresholdSensorEvent mLastPrimaryEvent; - @VisibleForTesting - ThresholdSensorEvent mLastEvent; - private boolean mRegistered; - private final AtomicBoolean mAlerting = new AtomicBoolean(); - private Runnable mCancelSecondaryRunnable; - private boolean mInitializedListeners = false; - private boolean mSecondarySafe = false; - - private final ThresholdSensor.Listener mPrimaryEventListener = this::onPrimarySensorEvent; - - private final ThresholdSensor.Listener mSecondaryEventListener = - new ThresholdSensor.Listener() { - @Override - public void onThresholdCrossed(ThresholdSensorEvent event) { - // If we no longer have a "below" signal and the secondary sensor is not - // considered "safe", then we need to turn it off. - if (!mSecondarySafe - && (mLastPrimaryEvent == null - || !mLastPrimaryEvent.getBelow() - || !event.getBelow())) { - chooseSensor(); - if (mLastPrimaryEvent == null || !mLastPrimaryEvent.getBelow()) { - // Only check the secondary as long as the primary thinks we're near. - if (mCancelSecondaryRunnable != null) { - mCancelSecondaryRunnable.run(); - mCancelSecondaryRunnable = null; - } - return; - } else { - // Check this sensor again in a moment. - mCancelSecondaryRunnable = mDelayableExecutor.executeDelayed(() -> { - // This is safe because we know that mSecondaryThresholdSensor - // is loaded, otherwise we wouldn't be here. - mPrimaryThresholdSensor.pause(); - mSecondaryThresholdSensor.resume(); - }, - SECONDARY_PING_INTERVAL_MS); - } - } - logDebug("Secondary sensor event: " + event.getBelow() + "."); - - if (!mPaused) { - onSensorEvent(event); - } - } - }; - - @Inject - public ProximitySensor( - @PrimaryProxSensor ThresholdSensor primary, - @SecondaryProxSensor ThresholdSensor secondary, - @Main DelayableExecutor delayableExecutor, - Execution execution) { - mPrimaryThresholdSensor = primary; - mSecondaryThresholdSensor = secondary; - mDelayableExecutor = delayableExecutor; - mExecution = execution; - } - - @Override - public void setTag(String tag) { - mTag = tag; - mPrimaryThresholdSensor.setTag(tag + ":primary"); - mSecondaryThresholdSensor.setTag(tag + ":secondary"); - } - - @Override - public void setDelay(int delay) { - mExecution.assertIsMainThread(); - mPrimaryThresholdSensor.setDelay(delay); - mSecondaryThresholdSensor.setDelay(delay); - } - +public interface ProximitySensor extends ThresholdSensor { /** - * Unregister with the {@link SensorManager} without unsetting listeners on this object. + * Returns true if we are registered with the SensorManager. */ - @Override - public void pause() { - mExecution.assertIsMainThread(); - mPaused = true; - unregisterInternal(); - } + boolean isRegistered(); /** - * Register with the {@link SensorManager}. No-op if no listeners are registered on this object. + * Whether the proximity sensor reports near. Can return null if no information has been + * received yet. */ - @Override - public void resume() { - mExecution.assertIsMainThread(); - mPaused = false; - registerInternal(); - } + Boolean isNear(); + + /** Update all listeners with the last value this class received from the sensor. */ + void alertListeners(); /** * Sets that it is safe to leave the secondary sensor on indefinitely. @@ -166,249 +41,5 @@ public class ProximitySensor implements ThresholdSensor { * The secondary sensor will be turned on if there are any registered listeners, regardless * of what is reported by the primary sensor. */ - public void setSecondarySafe(boolean safe) { - mSecondarySafe = mSecondaryThresholdSensor.isLoaded() && safe; - chooseSensor(); - } - - /** - * Returns true if we are registered with the SensorManager. - */ - public boolean isRegistered() { - return mRegistered; - } - - /** - * Returns {@code false} if a Proximity sensor is not available. - */ - @Override - public boolean isLoaded() { - return mPrimaryThresholdSensor.isLoaded(); - } - - /** - * Add a listener. - * - * Registers itself with the {@link SensorManager} if this is the first listener - * added. If the ProximitySensor is paused, it will be registered when resumed. - */ - @Override - public void register(ThresholdSensor.Listener listener) { - mExecution.assertIsMainThread(); - if (!isLoaded()) { - return; - } - - if (mListeners.contains(listener)) { - logDebug("ProxListener registered multiple times: " + listener); - } else { - mListeners.add(listener); - } - registerInternal(); - } - - protected void registerInternal() { - mExecution.assertIsMainThread(); - if (mRegistered || mPaused || mListeners.isEmpty()) { - return; - } - if (!mInitializedListeners) { - mPrimaryThresholdSensor.pause(); - mSecondaryThresholdSensor.pause(); - mPrimaryThresholdSensor.register(mPrimaryEventListener); - mSecondaryThresholdSensor.register(mSecondaryEventListener); - mInitializedListeners = true; - } - logDebug("Registering sensor listener"); - - mRegistered = true; - chooseSensor(); - } - - private void chooseSensor() { - mExecution.assertIsMainThread(); - if (!mRegistered || mPaused || mListeners.isEmpty()) { - return; - } - if (mSecondarySafe) { - mSecondaryThresholdSensor.resume(); - mPrimaryThresholdSensor.pause(); - } else { - mPrimaryThresholdSensor.resume(); - mSecondaryThresholdSensor.pause(); - } - } - - /** - * Remove a listener. - * - * If all listeners are removed from an instance of this class, - * it will unregister itself with the SensorManager. - */ - @Override - public void unregister(ThresholdSensor.Listener listener) { - mExecution.assertIsMainThread(); - mListeners.remove(listener); - if (mListeners.size() == 0) { - unregisterInternal(); - } - } - - protected void unregisterInternal() { - mExecution.assertIsMainThread(); - if (!mRegistered) { - return; - } - logDebug("unregistering sensor listener"); - mPrimaryThresholdSensor.pause(); - mSecondaryThresholdSensor.pause(); - if (mCancelSecondaryRunnable != null) { - mCancelSecondaryRunnable.run(); - mCancelSecondaryRunnable = null; - } - mLastPrimaryEvent = null; // Forget what we know. - mLastEvent = null; - mRegistered = false; - } - - public Boolean isNear() { - return isLoaded() && mLastEvent != null ? mLastEvent.getBelow() : null; - } - - /** Update all listeners with the last value this class received from the sensor. */ - public void alertListeners() { - mExecution.assertIsMainThread(); - if (mAlerting.getAndSet(true)) { - return; - } - if (mLastEvent != null) { - ThresholdSensorEvent lastEvent = mLastEvent; // Listeners can null out mLastEvent. - List<ThresholdSensor.Listener> listeners = new ArrayList<>(mListeners); - listeners.forEach(proximitySensorListener -> - proximitySensorListener.onThresholdCrossed(lastEvent)); - } - - mAlerting.set(false); - } - - private void onPrimarySensorEvent(ThresholdSensorEvent event) { - mExecution.assertIsMainThread(); - if (mLastPrimaryEvent != null && event.getBelow() == mLastPrimaryEvent.getBelow()) { - return; - } - - mLastPrimaryEvent = event; - - if (mSecondarySafe && mSecondaryThresholdSensor.isLoaded()) { - logDebug("Primary sensor reported " + (event.getBelow() ? "near" : "far") - + ". Checking secondary."); - if (mCancelSecondaryRunnable == null) { - mSecondaryThresholdSensor.resume(); - } - return; - } - - - if (!mSecondaryThresholdSensor.isLoaded()) { // No secondary - logDebug("Primary sensor event: " + event.getBelow() + ". No secondary."); - onSensorEvent(event); - } else if (event.getBelow()) { // Covered? Check secondary. - logDebug("Primary sensor event: " + event.getBelow() + ". Checking secondary."); - if (mCancelSecondaryRunnable != null) { - mCancelSecondaryRunnable.run(); - } - mSecondaryThresholdSensor.resume(); - } else { // Uncovered. Report immediately. - onSensorEvent(event); - } - } - - private void onSensorEvent(ThresholdSensorEvent event) { - mExecution.assertIsMainThread(); - if (mLastEvent != null && event.getBelow() == mLastEvent.getBelow()) { - return; - } - - if (!mSecondarySafe && !event.getBelow()) { - chooseSensor(); - } - - mLastEvent = event; - alertListeners(); - } - - @Override - public String toString() { - return String.format("{registered=%s, paused=%s, near=%s, primarySensor=%s, " - + "secondarySensor=%s secondarySafe=%s}", - isRegistered(), mPaused, isNear(), mPrimaryThresholdSensor, - mSecondaryThresholdSensor, mSecondarySafe); - } - - /** - * Convenience class allowing for briefly checking the proximity sensor. - */ - public static class ProximityCheck implements Runnable { - - private final ProximitySensor mSensor; - private final DelayableExecutor mDelayableExecutor; - private List<Consumer<Boolean>> mCallbacks = new ArrayList<>(); - private final ThresholdSensor.Listener mListener; - private final AtomicBoolean mRegistered = new AtomicBoolean(); - - @Inject - public ProximityCheck(ProximitySensor sensor, @Main DelayableExecutor delayableExecutor) { - mSensor = sensor; - mSensor.setTag("prox_check"); - mDelayableExecutor = delayableExecutor; - mListener = this::onProximityEvent; - } - - /** Set a descriptive tag for the sensors registration. */ - public void setTag(String tag) { - mSensor.setTag(tag); - } - - @Override - public void run() { - unregister(); - onProximityEvent(null); - } - - /** - * Query the proximity sensor, timing out if no result. - */ - public void check(long timeoutMs, Consumer<Boolean> callback) { - if (!mSensor.isLoaded()) { - callback.accept(null); - return; - } - mCallbacks.add(callback); - if (!mRegistered.getAndSet(true)) { - mSensor.register(mListener); - mDelayableExecutor.executeDelayed(this, timeoutMs); - } - } - - private void unregister() { - mSensor.unregister(mListener); - mRegistered.set(false); - } - - private void onProximityEvent(ThresholdSensorEvent proximityEvent) { - mCallbacks.forEach( - booleanConsumer -> - booleanConsumer.accept( - proximityEvent == null ? null : proximityEvent.getBelow())); - mCallbacks.clear(); - unregister(); - mRegistered.set(false); - } - } - - private void logDebug(String msg) { - if (DEBUG) { - Log.d(TAG, (mTag != null ? "[" + mTag + "] " : "") + msg); - } - } + void setSecondarySafe(boolean safe); } diff --git a/packages/SystemUI/src/com/android/systemui/util/sensors/ProximitySensorImpl.java b/packages/SystemUI/src/com/android/systemui/util/sensors/ProximitySensorImpl.java new file mode 100644 index 000000000000..e639313ac649 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/util/sensors/ProximitySensorImpl.java @@ -0,0 +1,360 @@ +/* + * Copyright (C) 2019 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.systemui.util.sensors; + +import android.hardware.SensorManager; +import android.util.Log; + +import com.android.internal.annotations.VisibleForTesting; +import com.android.systemui.dagger.qualifiers.Main; +import com.android.systemui.statusbar.policy.DevicePostureController; +import com.android.systemui.util.concurrency.DelayableExecutor; +import com.android.systemui.util.concurrency.Execution; + +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.atomic.AtomicBoolean; + +import javax.inject.Inject; + +/** + * Wrapper around SensorManager customized for the Proximity sensor. + * + * The ProximitySensor supports the concept of a primary and a + * secondary hardware sensor. The primary sensor is used for a first + * pass check if the phone covered. When triggered, it then checks + * the secondary sensor for confirmation (if there is one). It does + * not send a proximity event until the secondary sensor confirms (or + * rejects) the reading. The secondary sensor is, in fact, the source + * of truth. + * + * This is necessary as sometimes keeping the secondary sensor on for + * extends periods is undesirable. It may, however, result in increased + * latency for proximity readings. + * + * Phones should configure this via a config.xml overlay. If no + * proximity sensor is set (primary or secondary) we fall back to the + * default Sensor.TYPE_PROXIMITY. If proximity_sensor_type is set in + * config.xml, that will be used as the primary sensor. If + * proximity_sensor_secondary_type is set, that will function as the + * secondary sensor. If no secondary is set, only the primary will be + * used. + */ +class ProximitySensorImpl implements ProximitySensor { + private static final String TAG = "ProxSensor"; + private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG); + private static final long SECONDARY_PING_INTERVAL_MS = 5000; + + ThresholdSensor mPrimaryThresholdSensor; + ThresholdSensor mSecondaryThresholdSensor; + private final DelayableExecutor mDelayableExecutor; + private final Execution mExecution; + private final List<ThresholdSensor.Listener> mListeners = new ArrayList<>(); + private String mTag = null; + @VisibleForTesting protected boolean mPaused; + private ThresholdSensorEvent mLastPrimaryEvent; + @VisibleForTesting + ThresholdSensorEvent mLastEvent; + private boolean mRegistered; + private final AtomicBoolean mAlerting = new AtomicBoolean(); + private Runnable mCancelSecondaryRunnable; + boolean mInitializedListeners = false; + private boolean mSecondarySafe = false; // safe to skip primary sensor check and use secondary + protected @DevicePostureController.DevicePostureInt int mDevicePosture; + + final ThresholdSensor.Listener mPrimaryEventListener = this::onPrimarySensorEvent; + + final ThresholdSensor.Listener mSecondaryEventListener = + new ThresholdSensor.Listener() { + @Override + public void onThresholdCrossed(ThresholdSensorEvent event) { + // If we no longer have a "below" signal and the secondary sensor is not + // considered "safe", then we need to turn it off. + if (!mSecondarySafe + && (mLastPrimaryEvent == null + || !mLastPrimaryEvent.getBelow() + || !event.getBelow())) { + chooseSensor(); + if (mLastPrimaryEvent == null || !mLastPrimaryEvent.getBelow()) { + // Only check the secondary as long as the primary thinks we're near. + if (mCancelSecondaryRunnable != null) { + mCancelSecondaryRunnable.run(); + mCancelSecondaryRunnable = null; + } + return; + } else { + // Check this sensor again in a moment. + mCancelSecondaryRunnable = mDelayableExecutor.executeDelayed(() -> { + // This is safe because we know that mSecondaryThresholdSensor + // is loaded, otherwise we wouldn't be here. + mPrimaryThresholdSensor.pause(); + mSecondaryThresholdSensor.resume(); + }, + SECONDARY_PING_INTERVAL_MS); + } + } + logDebug("Secondary sensor event: " + event.getBelow() + "."); + + if (!mPaused) { + onSensorEvent(event); + } + } + }; + + @Inject + ProximitySensorImpl( + @PrimaryProxSensor ThresholdSensor primary, + @SecondaryProxSensor ThresholdSensor secondary, + @Main DelayableExecutor delayableExecutor, + Execution execution) { + mPrimaryThresholdSensor = primary; + mSecondaryThresholdSensor = secondary; + mDelayableExecutor = delayableExecutor; + mExecution = execution; + } + + @Override + public void setTag(String tag) { + mTag = tag; + mPrimaryThresholdSensor.setTag(tag + ":primary"); + mSecondaryThresholdSensor.setTag(tag + ":secondary"); + } + + @Override + public void setDelay(int delay) { + mExecution.assertIsMainThread(); + mPrimaryThresholdSensor.setDelay(delay); + mSecondaryThresholdSensor.setDelay(delay); + } + + /** + * Unregister with the {@link SensorManager} without unsetting listeners on this object. + */ + @Override + public void pause() { + mExecution.assertIsMainThread(); + mPaused = true; + unregisterInternal(); + } + + /** + * Register with the {@link SensorManager}. No-op if no listeners are registered on this object. + */ + @Override + public void resume() { + mExecution.assertIsMainThread(); + mPaused = false; + registerInternal(); + } + + @Override + public void setSecondarySafe(boolean safe) { + mSecondarySafe = mSecondaryThresholdSensor.isLoaded() && safe; + chooseSensor(); + } + + /** + * Returns true if we are registered with the SensorManager. + */ + @Override + public boolean isRegistered() { + return mRegistered; + } + + /** + * Returns {@code false} if a Proximity sensor is not available. + */ + @Override + public boolean isLoaded() { + return mPrimaryThresholdSensor.isLoaded(); + } + + /** + * Add a listener. + * + * Registers itself with the {@link SensorManager} if this is the first listener + * added. If the ProximitySensor is paused, it will be registered when resumed. + */ + @Override + public void register(ThresholdSensor.Listener listener) { + mExecution.assertIsMainThread(); + if (!isLoaded()) { + return; + } + + if (mListeners.contains(listener)) { + logDebug("ProxListener registered multiple times: " + listener); + } else { + mListeners.add(listener); + } + registerInternal(); + } + + protected void registerInternal() { + mExecution.assertIsMainThread(); + if (mRegistered || mPaused || mListeners.isEmpty()) { + return; + } + if (!mInitializedListeners) { + mPrimaryThresholdSensor.pause(); + mSecondaryThresholdSensor.pause(); + mPrimaryThresholdSensor.register(mPrimaryEventListener); + mSecondaryThresholdSensor.register(mSecondaryEventListener); + mInitializedListeners = true; + } + + mRegistered = true; + chooseSensor(); + } + + private void chooseSensor() { + mExecution.assertIsMainThread(); + if (!mRegistered || mPaused || mListeners.isEmpty()) { + return; + } + if (mSecondarySafe) { + mSecondaryThresholdSensor.resume(); + mPrimaryThresholdSensor.pause(); + } else { + mPrimaryThresholdSensor.resume(); + mSecondaryThresholdSensor.pause(); + } + } + + /** + * Remove a listener. + * + * If all listeners are removed from an instance of this class, + * it will unregister itself with the SensorManager. + */ + @Override + public void unregister(ThresholdSensor.Listener listener) { + mExecution.assertIsMainThread(); + mListeners.remove(listener); + if (mListeners.size() == 0) { + unregisterInternal(); + } + } + + @Override + public String getName() { + return mPrimaryThresholdSensor.getName(); + } + + @Override + public String getType() { + return mPrimaryThresholdSensor.getType(); + } + + protected void unregisterInternal() { + mExecution.assertIsMainThread(); + if (!mRegistered) { + return; + } + logDebug("unregistering sensor listener"); + mPrimaryThresholdSensor.pause(); + mSecondaryThresholdSensor.pause(); + if (mCancelSecondaryRunnable != null) { + mCancelSecondaryRunnable.run(); + mCancelSecondaryRunnable = null; + } + mLastPrimaryEvent = null; // Forget what we know. + mLastEvent = null; + mRegistered = false; + } + + @Override + public Boolean isNear() { + return isLoaded() && mLastEvent != null ? mLastEvent.getBelow() : null; + } + + @Override + public void alertListeners() { + mExecution.assertIsMainThread(); + if (mAlerting.getAndSet(true)) { + return; + } + if (mLastEvent != null) { + ThresholdSensorEvent lastEvent = mLastEvent; // Listeners can null out mLastEvent. + List<ThresholdSensor.Listener> listeners = new ArrayList<>(mListeners); + listeners.forEach(proximitySensorListener -> + proximitySensorListener.onThresholdCrossed(lastEvent)); + } + + mAlerting.set(false); + } + + private void onPrimarySensorEvent(ThresholdSensorEvent event) { + mExecution.assertIsMainThread(); + if (mLastPrimaryEvent != null && event.getBelow() == mLastPrimaryEvent.getBelow()) { + return; + } + + mLastPrimaryEvent = event; + + if (mSecondarySafe && mSecondaryThresholdSensor.isLoaded()) { + logDebug("Primary sensor reported " + (event.getBelow() ? "near" : "far") + + ". Checking secondary."); + if (mCancelSecondaryRunnable == null) { + mSecondaryThresholdSensor.resume(); + } + return; + } + + + if (!mSecondaryThresholdSensor.isLoaded()) { // No secondary + logDebug("Primary sensor event: " + event.getBelow() + ". No secondary."); + onSensorEvent(event); + } else if (event.getBelow()) { // Covered? Check secondary. + logDebug("Primary sensor event: " + event.getBelow() + ". Checking secondary."); + if (mCancelSecondaryRunnable != null) { + mCancelSecondaryRunnable.run(); + } + mSecondaryThresholdSensor.resume(); + } else { // Uncovered. Report immediately. + onSensorEvent(event); + } + } + + private void onSensorEvent(ThresholdSensorEvent event) { + mExecution.assertIsMainThread(); + if (mLastEvent != null && event.getBelow() == mLastEvent.getBelow()) { + return; + } + + if (!mSecondarySafe && !event.getBelow()) { + chooseSensor(); + } + + mLastEvent = event; + alertListeners(); + } + + @Override + public String toString() { + return String.format("{registered=%s, paused=%s, near=%s, posture=%s, primarySensor=%s, " + + "secondarySensor=%s secondarySafe=%s}", + isRegistered(), mPaused, isNear(), mDevicePosture, mPrimaryThresholdSensor, + mSecondaryThresholdSensor, mSecondarySafe); + } + + void logDebug(String msg) { + if (DEBUG) { + Log.d(TAG, (mTag != null ? "[" + mTag + "] " : "") + msg); + } + } +} diff --git a/packages/SystemUI/src/com/android/systemui/util/sensors/SensorModule.java b/packages/SystemUI/src/com/android/systemui/util/sensors/SensorModule.java index 11e7df8bd85f..0be6068c22a4 100644 --- a/packages/SystemUI/src/com/android/systemui/util/sensors/SensorModule.java +++ b/packages/SystemUI/src/com/android/systemui/util/sensors/SensorModule.java @@ -16,11 +16,23 @@ package com.android.systemui.util.sensors; +import android.content.res.Resources; import android.hardware.Sensor; import android.hardware.SensorManager; +import android.text.TextUtils; +import android.util.Log; + +import androidx.annotation.NonNull; import com.android.systemui.R; +import com.android.systemui.dagger.qualifiers.Main; +import com.android.systemui.statusbar.policy.DevicePostureController; +import com.android.systemui.util.concurrency.DelayableExecutor; + +import java.util.HashMap; +import java.util.Map; +import dagger.Lazy; import dagger.Module; import dagger.Provides; @@ -31,8 +43,10 @@ import dagger.Provides; public class SensorModule { @Provides @PrimaryProxSensor - static ThresholdSensor providePrimaryProxSensor(SensorManager sensorManager, - ThresholdSensorImpl.Builder thresholdSensorBuilder) { + static ThresholdSensor providePrimaryProximitySensor( + SensorManager sensorManager, + ThresholdSensorImpl.Builder thresholdSensorBuilder + ) { try { return thresholdSensorBuilder .setSensorDelay(SensorManager.SENSOR_DELAY_NORMAL) @@ -52,8 +66,9 @@ public class SensorModule { @Provides @SecondaryProxSensor - static ThresholdSensor provideSecondaryProxSensor( - ThresholdSensorImpl.Builder thresholdSensorBuilder) { + static ThresholdSensor provideSecondaryProximitySensor( + ThresholdSensorImpl.Builder thresholdSensorBuilder + ) { try { return thresholdSensorBuilder .setSensorResourceId(R.string.proximity_sensor_secondary_type, true) @@ -64,4 +79,153 @@ public class SensorModule { return thresholdSensorBuilder.setSensor(null).setThresholdValue(0).build(); } } + + /** + * If postures are supported on the device, returns a posture dependent proximity sensor + * which switches proximity sensors based on the current posture. + * + * If postures are not supported the regular {@link ProximitySensorImpl} will be returned. + */ + @Provides + static ProximitySensor provideProximitySensor( + @Main Resources resources, + Lazy<PostureDependentProximitySensor> postureDependentProximitySensorProvider, + Lazy<ProximitySensorImpl> proximitySensorProvider + ) { + if (hasPostureSupport( + resources.getStringArray(R.array.proximity_sensor_posture_mapping))) { + return postureDependentProximitySensorProvider.get(); + } else { + return proximitySensorProvider.get(); + } + } + + @Provides + static ProximityCheck provideProximityCheck( + ProximitySensor proximitySensor, + @Main DelayableExecutor delayableExecutor + ) { + return new ProximityCheck( + proximitySensor, + delayableExecutor + ); + } + + @Provides + @PrimaryProxSensor + @NonNull + static ThresholdSensor[] providePostureToProximitySensorMapping( + ThresholdSensorImpl.BuilderFactory thresholdSensorImplBuilderFactory, + @Main Resources resources + ) { + return createPostureToSensorMapping( + thresholdSensorImplBuilderFactory, + resources.getStringArray(R.array.proximity_sensor_posture_mapping), + R.dimen.proximity_sensor_threshold, + R.dimen.proximity_sensor_threshold_latch + ); + } + + @Provides + @SecondaryProxSensor + @NonNull + static ThresholdSensor[] providePostureToSecondaryProximitySensorMapping( + ThresholdSensorImpl.BuilderFactory thresholdSensorImplBuilderFactory, + @Main Resources resources + ) { + return createPostureToSensorMapping( + thresholdSensorImplBuilderFactory, + resources.getStringArray(R.array.proximity_sensor_secondary_posture_mapping), + R.dimen.proximity_sensor_secondary_threshold, + R.dimen.proximity_sensor_secondary_threshold_latch + ); + } + + /** + * Builds sensors to use per posture. + * + * @param sensorTypes an array where the index represents + * {@link DevicePostureController.DevicePostureInt} and the value + * at the given index is the sensorType. Empty values represent + * no sensor desired. + * @param proximitySensorThresholdResourceId resource id for the threshold for all sensor + * postures. This currently only supports one value. + * This needs to be updated in the future if postures + * use different sensors with differing thresholds. + * @param proximitySensorThresholdLatchResourceId resource id for the latch for all sensor + * postures. This currently only supports one + * value. This needs to be updated in the future + * if postures use different sensors with + * differing latches. + * @return an array where the index represents the device posture + * {@link DevicePostureController.DevicePostureInt} and the value at the index is the sensor to + * use when the device is in that posture. + */ + @NonNull + private static ThresholdSensor[] createPostureToSensorMapping( + ThresholdSensorImpl.BuilderFactory thresholdSensorImplBuilderFactory, + String[] sensorTypes, + int proximitySensorThresholdResourceId, + int proximitySensorThresholdLatchResourceId + + ) { + ThresholdSensor noProxSensor = thresholdSensorImplBuilderFactory + .createBuilder() + .setSensor(null).setThresholdValue(0).build(); + + + // length and index of sensorMap correspond to DevicePostureController.DevicePostureInt: + final ThresholdSensor[] sensorMap = + new ThresholdSensor[DevicePostureController.SUPPORTED_POSTURES_SIZE]; + for (int i = 0; i < DevicePostureController.SUPPORTED_POSTURES_SIZE; i++) { + sensorMap[i] = noProxSensor; + } + + if (!hasPostureSupport(sensorTypes)) { + Log.e("SensorModule", "config doesn't support postures," + + " but attempting to retrieve proxSensorMapping"); + return sensorMap; + } + + // Map of sensorType => Sensor, so we reuse the same sensor if it's the same between + // postures + Map<String, ThresholdSensor> typeToSensorMap = new HashMap<>(); + for (int i = 0; i < sensorTypes.length; i++) { + try { + final String sensorType = sensorTypes[i]; + if (typeToSensorMap.containsKey(sensorType)) { + sensorMap[i] = typeToSensorMap.get(sensorType); + } else { + sensorMap[i] = thresholdSensorImplBuilderFactory + .createBuilder() + .setSensorType(sensorTypes[i], true) + .setThresholdResourceId(proximitySensorThresholdResourceId) + .setThresholdLatchResourceId(proximitySensorThresholdLatchResourceId) + .build(); + typeToSensorMap.put(sensorType, sensorMap[i]); + } + } catch (IllegalStateException e) { + // do nothing, sensor at this posture is already set to noProxSensor + } + } + + return sensorMap; + } + + /** + * Returns true if there's at least one non-empty sensor type in the given array. + */ + private static boolean hasPostureSupport(String[] postureToSensorTypeMapping) { + if (postureToSensorTypeMapping == null || postureToSensorTypeMapping.length == 0) { + return false; + } + + for (String sensorType : postureToSensorTypeMapping) { + if (!TextUtils.isEmpty(sensorType)) { + return true; + } + } + + return false; + } } diff --git a/packages/SystemUI/src/com/android/systemui/util/sensors/ThresholdSensor.java b/packages/SystemUI/src/com/android/systemui/util/sensors/ThresholdSensor.java index 363a734a6ae5..d81a8d5991d1 100644 --- a/packages/SystemUI/src/com/android/systemui/util/sensors/ThresholdSensor.java +++ b/packages/SystemUI/src/com/android/systemui/util/sensors/ThresholdSensor.java @@ -16,8 +16,6 @@ package com.android.systemui.util.sensors; -import java.util.Locale; - /** * A wrapper class for sensors that have a boolean state - above/below. */ @@ -77,6 +75,16 @@ public interface ThresholdSensor { void unregister(Listener listener); /** + * Name of the sensor. + */ + String getName(); + + /** + * Type of the sensor. + */ + String getType(); + + /** * Interface for listening to events on {@link ThresholdSensor} */ interface Listener { @@ -85,34 +93,4 @@ public interface ThresholdSensor { */ void onThresholdCrossed(ThresholdSensorEvent event); } - - /** - * Returned when the below/above state of a {@link ThresholdSensor} changes. - */ - class ThresholdSensorEvent { - private final boolean mBelow; - private final long mTimestampNs; - - public ThresholdSensorEvent(boolean below, long timestampNs) { - mBelow = below; - mTimestampNs = timestampNs; - } - - public boolean getBelow() { - return mBelow; - } - - public long getTimestampNs() { - return mTimestampNs; - } - - public long getTimestampMs() { - return mTimestampNs / 1000000; - } - - @Override - public String toString() { - return String.format((Locale) null, "{near=%s, timestamp_ns=%d}", mBelow, mTimestampNs); - } - } } diff --git a/packages/SystemUI/src/com/android/systemui/util/sensors/ThresholdSensorEvent.java b/packages/SystemUI/src/com/android/systemui/util/sensors/ThresholdSensorEvent.java new file mode 100644 index 000000000000..afce09fbe6d0 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/util/sensors/ThresholdSensorEvent.java @@ -0,0 +1,49 @@ +/* + * Copyright (C) 2021 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.systemui.util.sensors; + +import java.util.Locale; + +/** + * Returned when the below/above state of a {@link ThresholdSensor} changes. + */ +public class ThresholdSensorEvent { + private final boolean mBelow; + private final long mTimestampNs; + + public ThresholdSensorEvent(boolean below, long timestampNs) { + mBelow = below; + mTimestampNs = timestampNs; + } + + public boolean getBelow() { + return mBelow; + } + + public long getTimestampNs() { + return mTimestampNs; + } + + public long getTimestampMs() { + return mTimestampNs / 1000000; + } + + @Override + public String toString() { + return String.format((Locale) null, "{near=%s, timestamp_ns=%d}", mBelow, mTimestampNs); + } +} diff --git a/packages/SystemUI/src/com/android/systemui/util/sensors/ThresholdSensorImpl.java b/packages/SystemUI/src/com/android/systemui/util/sensors/ThresholdSensorImpl.java index d10cf9b180c3..a9086b140a3c 100644 --- a/packages/SystemUI/src/com/android/systemui/util/sensors/ThresholdSensorImpl.java +++ b/packages/SystemUI/src/com/android/systemui/util/sensors/ThresholdSensorImpl.java @@ -21,6 +21,7 @@ import android.hardware.Sensor; import android.hardware.SensorEvent; import android.hardware.SensorEventListener; import android.hardware.SensorManager; +import android.text.TextUtils; import android.util.Log; import com.android.internal.annotations.VisibleForTesting; @@ -32,7 +33,10 @@ import java.util.List; import javax.inject.Inject; -class ThresholdSensorImpl implements ThresholdSensor { +/** + * Sensor that will only trigger beyond some lower and upper threshold. + */ +public class ThresholdSensorImpl implements ThresholdSensor { private static final String TAG = "ThresholdSensor"; private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG); @@ -198,6 +202,15 @@ class ThresholdSensorImpl implements ThresholdSensor { alertListenersInternal(belowThreshold, timestampNs); } + @Override + public String getName() { + return mSensor != null ? mSensor.getName() : null; + } + + @Override + public String getType() { + return mSensor != null ? mSensor.getStringType() : null; + } @Override public String toString() { @@ -211,7 +224,12 @@ class ThresholdSensorImpl implements ThresholdSensor { } } - static class Builder { + /** + * Use to build a ThresholdSensor. Should only be used once per sensor built, since + * parameters are not reset after calls to build(). For ease of retrievingnew Builders, use + * {@link BuilderFactory}. + */ + public static class Builder { private final Resources mResources; private final AsyncSensorManager mSensorManager; private final Execution mExecution; @@ -318,7 +336,7 @@ class ThresholdSensorImpl implements ThresholdSensor { @VisibleForTesting Sensor findSensorByType(String sensorType, boolean requireWakeUp) { - if (sensorType.isEmpty()) { + if (TextUtils.isEmpty(sensorType)) { return null; } @@ -336,4 +354,29 @@ class ThresholdSensorImpl implements ThresholdSensor { return sensor; } } + + /** + * Factory that creates a new ThresholdSensorImpl.Builder. In general, Builders should not be + * reused after creating a ThresholdSensor or else there may be default threshold and sensor + * values set from the previous built sensor. + */ + public static class BuilderFactory { + private final Resources mResources; + private final AsyncSensorManager mSensorManager; + private final Execution mExecution; + + @Inject + BuilderFactory( + @Main Resources resources, + AsyncSensorManager sensorManager, + Execution execution) { + mResources = resources; + mSensorManager = sensorManager; + mExecution = execution; + } + + ThresholdSensorImpl.Builder createBuilder() { + return new Builder(mResources, mSensorManager, mExecution); + } + } } diff --git a/packages/SystemUI/src/com/android/systemui/wmshell/WMShellBaseModule.java b/packages/SystemUI/src/com/android/systemui/wmshell/WMShellBaseModule.java index f420a85c8bf6..ff263109d555 100644 --- a/packages/SystemUI/src/com/android/systemui/wmshell/WMShellBaseModule.java +++ b/packages/SystemUI/src/com/android/systemui/wmshell/WMShellBaseModule.java @@ -28,7 +28,6 @@ import com.android.internal.logging.UiEventLogger; import com.android.internal.statusbar.IStatusBarService; import com.android.systemui.dagger.WMComponent; import com.android.systemui.dagger.WMSingleton; -import com.android.wm.shell.FullscreenTaskListener; import com.android.wm.shell.RootTaskDisplayAreaOrganizer; import com.android.wm.shell.ShellCommandHandler; import com.android.wm.shell.ShellCommandHandlerImpl; @@ -57,6 +56,8 @@ import com.android.wm.shell.common.annotations.ShellMainThread; import com.android.wm.shell.common.annotations.ShellSplashscreenThread; import com.android.wm.shell.draganddrop.DragAndDropController; import com.android.wm.shell.freeform.FreeformTaskListener; +import com.android.wm.shell.fullscreen.FullscreenTaskListener; +import com.android.wm.shell.fullscreen.FullscreenUnfoldController; import com.android.wm.shell.hidedisplaycutout.HideDisplayCutout; import com.android.wm.shell.hidedisplaycutout.HideDisplayCutoutController; import com.android.wm.shell.legacysplitscreen.LegacySplitScreen; @@ -79,6 +80,7 @@ import com.android.wm.shell.tasksurfacehelper.TaskSurfaceHelper; import com.android.wm.shell.tasksurfacehelper.TaskSurfaceHelperController; import com.android.wm.shell.transition.ShellTransitions; import com.android.wm.shell.transition.Transitions; +import com.android.wm.shell.unfold.ShellUnfoldProgressProvider; import java.util.Optional; @@ -218,8 +220,28 @@ public abstract class WMShellBaseModule { @WMSingleton @Provides - static FullscreenTaskListener provideFullscreenTaskListener(SyncTransactionQueue syncQueue) { - return new FullscreenTaskListener(syncQueue); + static FullscreenTaskListener provideFullscreenTaskListener( + SyncTransactionQueue syncQueue, Optional<FullscreenUnfoldController> controller) { + return new FullscreenTaskListener(syncQueue, controller); + } + + // + // Unfold transition + // + + @WMSingleton + @Provides + static Optional<FullscreenUnfoldController> provideFullscreenUnfoldController( + Context context, + Optional<ShellUnfoldProgressProvider> progressProvider, + RootTaskDisplayAreaOrganizer rootTaskDisplayAreaOrganizer, + DisplayInsetsController displayInsetsController, + @ShellMainThread ShellExecutor mainExecutor + ) { + return progressProvider.map(shellUnfoldTransitionProgressProvider -> + new FullscreenUnfoldController(context, mainExecutor, + shellUnfoldTransitionProgressProvider, rootTaskDisplayAreaOrganizer, + displayInsetsController)); } // @@ -474,6 +496,7 @@ public abstract class WMShellBaseModule { Optional<AppPairsController> appPairsOptional, Optional<PipTouchHandler> pipTouchHandlerOptional, FullscreenTaskListener fullscreenTaskListener, + Optional<FullscreenUnfoldController> appUnfoldTransitionController, Optional<Optional<FreeformTaskListener>> freeformTaskListener, Transitions transitions, StartingWindowController startingWindow, @@ -489,6 +512,7 @@ public abstract class WMShellBaseModule { appPairsOptional, pipTouchHandlerOptional, fullscreenTaskListener, + appUnfoldTransitionController, freeformTaskListener, transitions, startingWindow, diff --git a/packages/SystemUI/src/com/android/systemui/wmshell/WMShellModule.java b/packages/SystemUI/src/com/android/systemui/wmshell/WMShellModule.java index 83c2a9b1be33..a7c5ad2e3716 100644 --- a/packages/SystemUI/src/com/android/systemui/wmshell/WMShellModule.java +++ b/packages/SystemUI/src/com/android/systemui/wmshell/WMShellModule.java @@ -126,9 +126,10 @@ public class WMShellModule { static AppPairsController provideAppPairs(ShellTaskOrganizer shellTaskOrganizer, SyncTransactionQueue syncQueue, DisplayController displayController, @ShellMainThread ShellExecutor mainExecutor, - DisplayImeController displayImeController) { + DisplayImeController displayImeController, + DisplayInsetsController displayInsetsController) { return new AppPairsController(shellTaskOrganizer, syncQueue, displayController, - mainExecutor, displayImeController); + mainExecutor, displayImeController, displayInsetsController); } // diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchTest.java index ce02b8339422..e4336fe07dbb 100644 --- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchTest.java +++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchTest.java @@ -97,6 +97,7 @@ public class KeyguardClockSwitchTest extends SysuiTestCase { mLargeClockFrame = mKeyguardClockSwitch.findViewById(R.id.lockscreen_clock_view_large); mLargeClockView = mKeyguardClockSwitch.findViewById(R.id.animatable_clock_view_large); mBigClock = new TextClock(getContext()); + mKeyguardClockSwitch.mChildrenAreLaidOut = true; MockitoAnnotations.initMocks(this); } diff --git a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeTriggersTest.java b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeTriggersTest.java index b688fcc50373..31fa3f841b19 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeTriggersTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeTriggersTest.java @@ -53,7 +53,8 @@ import com.android.systemui.util.sensors.AsyncSensorManager; import com.android.systemui.util.sensors.FakeProximitySensor; import com.android.systemui.util.sensors.FakeSensorManager; import com.android.systemui.util.sensors.FakeThresholdSensor; -import com.android.systemui.util.sensors.ProximitySensor; +import com.android.systemui.util.sensors.ProximityCheck; +import com.android.systemui.util.sensors.ThresholdSensorEvent; import com.android.systemui.util.settings.FakeSettings; import com.android.systemui.util.time.FakeSystemClock; import com.android.systemui.util.wakelock.WakeLock; @@ -80,7 +81,7 @@ public class DozeTriggersTest extends SysuiTestCase { @Mock private DockManager mDockManager; @Mock - private ProximitySensor.ProximityCheck mProximityCheck; + private ProximityCheck mProximityCheck; @Mock private AuthController mAuthController; @Mock @@ -136,14 +137,14 @@ public class DozeTriggersTest extends SysuiTestCase { mTriggers.transitionTo(DozeMachine.State.INITIALIZED, DozeMachine.State.DOZE); clearInvocations(mMachine); - mProximitySensor.setLastEvent(new ProximitySensor.ThresholdSensorEvent(true, 1)); + mProximitySensor.setLastEvent(new ThresholdSensorEvent(true, 1)); captor.getValue().onNotificationAlerted(null /* pulseSuppressedListener */); mProximitySensor.alertListeners(); verify(mMachine, never()).requestState(any()); verify(mMachine, never()).requestPulse(anyInt()); - mProximitySensor.setLastEvent(new ProximitySensor.ThresholdSensorEvent(false, 2)); + mProximitySensor.setLastEvent(new ThresholdSensorEvent(false, 2)); mProximitySensor.alertListeners(); waitForSensorManager(); captor.getValue().onNotificationAlerted(null /* pulseSuppressedListener */); diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/carrier/QSCarrierGroupControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/carrier/QSCarrierGroupControllerTest.java index a1b72104509b..8ae7100e2e60 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/qs/carrier/QSCarrierGroupControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/carrier/QSCarrierGroupControllerTest.java @@ -39,8 +39,8 @@ import androidx.test.filters.SmallTest; import com.android.keyguard.CarrierTextManager; import com.android.systemui.flags.FeatureFlags; import com.android.systemui.plugins.ActivityStarter; -import com.android.systemui.statusbar.policy.NetworkController; -import com.android.systemui.statusbar.policy.NetworkController.MobileDataIndicators; +import com.android.systemui.statusbar.connectivity.NetworkController; +import com.android.systemui.statusbar.connectivity.NetworkController.MobileDataIndicators; import com.android.systemui.util.CarrierConfigTracker; import com.android.systemui.utils.leaks.LeakCheckedTest; import com.android.systemui.utils.os.FakeHandler; diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/CastTileTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/CastTileTest.java index e939411e4a2a..f0bd06571eb9 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/CastTileTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/CastTileTest.java @@ -43,12 +43,12 @@ import com.android.systemui.plugins.ActivityStarter; import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.qs.QSTileHost; import com.android.systemui.qs.logging.QSLogger; +import com.android.systemui.statusbar.connectivity.NetworkController; +import com.android.systemui.statusbar.connectivity.NetworkController.WifiIndicators; import com.android.systemui.statusbar.policy.CastController; import com.android.systemui.statusbar.policy.CastController.CastDevice; import com.android.systemui.statusbar.policy.HotspotController; import com.android.systemui.statusbar.policy.KeyguardStateController; -import com.android.systemui.statusbar.policy.NetworkController; -import com.android.systemui.statusbar.policy.NetworkController.WifiIndicators; import org.junit.Before; import org.junit.Test; diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDialogControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDialogControllerTest.java index f9d5be6c4807..fe328395b89f 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDialogControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDialogControllerTest.java @@ -48,9 +48,9 @@ import com.android.systemui.SysuiTestCase; import com.android.systemui.broadcast.BroadcastDispatcher; import com.android.systemui.dagger.qualifiers.Main; import com.android.systemui.plugins.ActivityStarter; +import com.android.systemui.statusbar.connectivity.NetworkController; +import com.android.systemui.statusbar.connectivity.NetworkController.AccessPointController; import com.android.systemui.statusbar.policy.KeyguardStateController; -import com.android.systemui.statusbar.policy.NetworkController; -import com.android.systemui.statusbar.policy.NetworkController.AccessPointController; import com.android.systemui.toast.SystemUIToast; import com.android.systemui.toast.ToastFactory; import com.android.systemui.util.CarrierConfigTracker; diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/AccessPointControllerImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/connectivity/AccessPointControllerImplTest.kt index 4068f93f5ee3..7896a26badbe 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/AccessPointControllerImplTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/connectivity/AccessPointControllerImplTest.kt @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.systemui.statusbar.policy +package com.android.systemui.statusbar.connectivity import android.os.UserManager import android.test.suitebuilder.annotation.SmallTest diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/CallbackHandlerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/connectivity/CallbackHandlerTest.java index 3c55df934f76..11a53c55c024 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/CallbackHandlerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/connectivity/CallbackHandlerTest.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.android.systemui.statusbar.policy; +package com.android.systemui.statusbar.connectivity; import static junit.framework.Assert.assertEquals; import static junit.framework.Assert.assertTrue; @@ -28,11 +28,11 @@ import androidx.test.runner.AndroidJUnit4; import com.android.settingslib.mobile.TelephonyIcons; import com.android.systemui.SysuiTestCase; -import com.android.systemui.statusbar.policy.NetworkController.EmergencyListener; -import com.android.systemui.statusbar.policy.NetworkController.IconState; -import com.android.systemui.statusbar.policy.NetworkController.MobileDataIndicators; -import com.android.systemui.statusbar.policy.NetworkController.SignalCallback; -import com.android.systemui.statusbar.policy.NetworkController.WifiIndicators; +import com.android.systemui.statusbar.connectivity.NetworkController.EmergencyListener; +import com.android.systemui.statusbar.connectivity.NetworkController.IconState; +import com.android.systemui.statusbar.connectivity.NetworkController.MobileDataIndicators; +import com.android.systemui.statusbar.connectivity.NetworkController.SignalCallback; +import com.android.systemui.statusbar.connectivity.NetworkController.WifiIndicators; import com.android.systemui.tests.R; import org.junit.Before; @@ -182,7 +182,8 @@ public class CallbackHandlerTest extends SysuiTestCase { @Test public void testSignalCallback_setIsAirplaneMode() { - IconState state = new IconState(true, R.drawable.stat_sys_airplane_mode, "Test Description"); + IconState state = + new IconState(true, R.drawable.stat_sys_airplane_mode, "Test Description"); mHandler.setIsAirplaneMode(state); waitForCallbacks(); diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerBaseTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/connectivity/NetworkControllerBaseTest.java index c488ee95aa3c..67cab7413f1f 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerBaseTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/connectivity/NetworkControllerBaseTest.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.systemui.statusbar.policy; +package com.android.systemui.statusbar.connectivity; import static android.telephony.AccessNetworkConstants.TRANSPORT_TYPE_WWAN; import static android.telephony.NetworkRegistrationInfo.DOMAIN_PS; @@ -72,10 +72,11 @@ import com.android.systemui.broadcast.BroadcastDispatcher; import com.android.systemui.demomode.DemoModeController; import com.android.systemui.dump.DumpManager; import com.android.systemui.flags.FeatureFlags; +import com.android.systemui.statusbar.connectivity.NetworkController.IconState; +import com.android.systemui.statusbar.connectivity.NetworkController.MobileDataIndicators; +import com.android.systemui.statusbar.connectivity.NetworkController.SignalCallback; +import com.android.systemui.statusbar.policy.DeviceProvisionedController; import com.android.systemui.statusbar.policy.DeviceProvisionedController.DeviceProvisionedListener; -import com.android.systemui.statusbar.policy.NetworkController.IconState; -import com.android.systemui.statusbar.policy.NetworkController.MobileDataIndicators; -import com.android.systemui.statusbar.policy.NetworkController.SignalCallback; import com.android.systemui.telephony.TelephonyListenerManager; import com.android.systemui.util.CarrierConfigTracker; import com.android.systemui.util.concurrency.FakeExecutor; @@ -268,7 +269,7 @@ public class NetworkControllerBaseTest extends SysuiTestCase { setSubscriptions(mSubId); mMobileSignalController = mNetworkController.mMobileSignalControllers.get(mSubId); ArgumentCaptor<ConnectivityManager.NetworkCallback> callbackArg = - ArgumentCaptor.forClass(ConnectivityManager.NetworkCallback.class); + ArgumentCaptor.forClass(ConnectivityManager.NetworkCallback.class); verify(mMockCm, atLeastOnce()) .registerDefaultNetworkCallback(callbackArg.capture(), isA(Handler.class)); int captureSize = callbackArg.getAllValues().size(); @@ -404,7 +405,7 @@ public class NetworkControllerBaseTest extends SysuiTestCase { } private static void setConnectivityCommon(NetworkCapabilities.Builder builder, - int networkType, boolean validated, boolean isConnected){ + int networkType, boolean validated, boolean isConnected) { // TODO: Separate out into several NetworkCapabilities. if (isConnected) { builder.addTransportType(networkType); @@ -538,7 +539,7 @@ public class NetworkControllerBaseTest extends SysuiTestCase { } protected void verifyLastMobileDataIndicators(boolean visible, int icon, int typeIcon, - boolean roaming, boolean inet) { + boolean roaming, boolean inet) { ArgumentCaptor<MobileDataIndicators> indicatorsArg = ArgumentCaptor.forClass(MobileDataIndicators.class); @@ -646,7 +647,7 @@ public class NetworkControllerBaseTest extends SysuiTestCase { } protected void assertNetworkNameEquals(String expected) { - assertEquals("Network name", expected, mMobileSignalController.getState().networkName); + assertEquals("Network name", expected, mMobileSignalController.getState().networkName); } protected void assertDataNetworkNameEquals(String expected) { diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerDataTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/connectivity/NetworkControllerDataTest.java index 3433a14f54e7..00dedd961074 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerDataTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/connectivity/NetworkControllerDataTest.java @@ -1,4 +1,20 @@ -package com.android.systemui.statusbar.policy; +/* + * Copyright (C) 2021 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.systemui.statusbar.connectivity; import static android.telephony.AccessNetworkConstants.TRANSPORT_TYPE_WWAN; import static android.telephony.NetworkRegistrationInfo.DOMAIN_PS; @@ -22,6 +38,7 @@ import android.testing.TestableLooper.RunWithLooper; import com.android.settingslib.mobile.TelephonyIcons; import com.android.settingslib.net.DataUsageController; import com.android.systemui.dump.DumpManager; +import com.android.systemui.statusbar.policy.DeviceProvisionedController; import com.android.systemui.util.CarrierConfigTracker; import org.junit.Test; diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerEthernetTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/connectivity/NetworkControllerEthernetTest.java index 6aab9c762a95..675d755ad3e3 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerEthernetTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/connectivity/NetworkControllerEthernetTest.java @@ -1,4 +1,20 @@ -package com.android.systemui.statusbar.policy; +/* + * Copyright (C) 2021 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.systemui.statusbar.connectivity; import static junit.framework.Assert.assertEquals; @@ -7,7 +23,7 @@ import android.test.suitebuilder.annotation.SmallTest; import android.testing.AndroidTestingRunner; import android.testing.TestableLooper.RunWithLooper; -import com.android.systemui.statusbar.policy.NetworkController.IconState; +import com.android.systemui.statusbar.connectivity.NetworkController.IconState; import org.junit.Test; import org.junit.runner.RunWith; diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerSignalTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/connectivity/NetworkControllerSignalTest.java index 4ff13011567b..73eddd166f88 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerSignalTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/connectivity/NetworkControllerSignalTest.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.android.systemui.statusbar.policy; +package com.android.systemui.statusbar.connectivity; import static junit.framework.Assert.assertEquals; import static junit.framework.Assert.assertFalse; @@ -42,6 +42,7 @@ import com.android.settingslib.mobile.TelephonyIcons; import com.android.settingslib.net.DataUsageController; import com.android.systemui.R; import com.android.systemui.dump.DumpManager; +import com.android.systemui.statusbar.policy.DeviceProvisionedController; import com.android.systemui.util.CarrierConfigTracker; import org.junit.Test; @@ -280,7 +281,7 @@ public class NetworkControllerSignalTest extends NetworkControllerBaseTest { // TODO: Put this somewhere else, maybe in its own file. @Test public void testHasCorrectMobileControllers() { - int[] testSubscriptions = new int[] { 1, 5, 3 }; + int[] testSubscriptions = new int[]{1, 5, 3}; int notTestSubscription = 0; MobileSignalController mobileSignalController = Mockito.mock(MobileSignalController.class); @@ -312,8 +313,8 @@ public class NetworkControllerSignalTest extends NetworkControllerBaseTest { // We will not add one subscription to make sure it's controller gets removed. int indexToSkipSubscription = 1; - int[] testSubscriptions = new int[] { 1, 5, 3 }; - MobileSignalController[] mobileSignalControllers = new MobileSignalController[] { + int[] testSubscriptions = new int[]{1, 5, 3}; + MobileSignalController[] mobileSignalControllers = new MobileSignalController[]{ Mockito.mock(MobileSignalController.class), Mockito.mock(MobileSignalController.class), Mockito.mock(MobileSignalController.class), @@ -401,24 +402,24 @@ public class NetworkControllerSignalTest extends NetworkControllerBaseTest { @Test public void testOnReceive_stringsUpdatedAction_bothFalse() { Intent intent = createStringsUpdatedIntent(false /* showSpn */, - "Irrelevant" /* spn */, - false /* showPlmn */, - "Irrelevant" /* plmn */); + "Irrelevant" /* spn */, + false /* showPlmn */, + "Irrelevant" /* plmn */); mNetworkController.onReceive(mContext, intent); String defaultNetworkName = mMobileSignalController .getTextIfExists( - com.android.internal.R.string.lockscreen_carrier_default).toString(); + com.android.internal.R.string.lockscreen_carrier_default).toString(); assertNetworkNameEquals(defaultNetworkName); } @Test public void testOnReceive_stringsUpdatedAction_bothTrueAndNull() { Intent intent = createStringsUpdatedIntent(true /* showSpn */, - null /* spn */, - true /* showPlmn */, - null /* plmn */); + null /* spn */, + true /* showPlmn */, + null /* plmn */); mNetworkController.onReceive(mContext, intent); @@ -433,15 +434,15 @@ public class NetworkControllerSignalTest extends NetworkControllerBaseTest { String plmn = "Test2"; Intent intent = createStringsUpdatedIntent(true /* showSpn */, - spn /* spn */, - true /* showPlmn */, - plmn /* plmn */); + spn /* spn */, + true /* showPlmn */, + plmn /* plmn */); mNetworkController.onReceive(mContext, intent); assertNetworkNameEquals(plmn + mMobileSignalController.getTextIfExists( - R.string.status_bar_network_name_separator).toString() + R.string.status_bar_network_name_separator).toString() + spn); } @@ -477,145 +478,149 @@ public class NetworkControllerSignalTest extends NetworkControllerBaseTest { @Test public void testOnUpdateDataActivity_dataOut() { - setupDefaultSignal(); + setupDefaultSignal(); - updateDataActivity(TelephonyManager.DATA_ACTIVITY_OUT); + updateDataActivity(TelephonyManager.DATA_ACTIVITY_OUT); - verifyLastQsMobileDataIndicators(true /* visible */, - DEFAULT_LEVEL /* icon */, - DEFAULT_QS_ICON /* typeIcon */, - false /* dataIn */, - true /* dataOut */); + verifyLastQsMobileDataIndicators(true /* visible */, + DEFAULT_LEVEL /* icon */, + DEFAULT_QS_ICON /* typeIcon */, + false /* dataIn */, + true /* dataOut */); } @Test public void testOnUpdateDataActivity_dataInOut() { - setupDefaultSignal(); + setupDefaultSignal(); - updateDataActivity(TelephonyManager.DATA_ACTIVITY_INOUT); + updateDataActivity(TelephonyManager.DATA_ACTIVITY_INOUT); - verifyLastQsMobileDataIndicators(true /* visible */, - DEFAULT_LEVEL /* icon */, - DEFAULT_QS_ICON /* typeIcon */, - true /* dataIn */, - true /* dataOut */); + verifyLastQsMobileDataIndicators(true /* visible */, + DEFAULT_LEVEL /* icon */, + DEFAULT_QS_ICON /* typeIcon */, + true /* dataIn */, + true /* dataOut */); } @Test public void testOnUpdateDataActivity_dataActivityNone() { - setupDefaultSignal(); + setupDefaultSignal(); - updateDataActivity(TelephonyManager.DATA_ACTIVITY_NONE); + updateDataActivity(TelephonyManager.DATA_ACTIVITY_NONE); - verifyLastQsMobileDataIndicators(true /* visible */, - DEFAULT_LEVEL /* icon */, - DEFAULT_QS_ICON /* typeIcon */, - false /* dataIn */, - false /* dataOut */); + verifyLastQsMobileDataIndicators(true /* visible */, + DEFAULT_LEVEL /* icon */, + DEFAULT_QS_ICON /* typeIcon */, + false /* dataIn */, + false /* dataOut */); } @Test public void testCarrierNetworkChange_carrierNetworkChange() { - int strength = SignalStrength.SIGNAL_STRENGTH_GREAT; + int strength = SignalStrength.SIGNAL_STRENGTH_GREAT; - setupDefaultSignal(); - setLevel(strength); + setupDefaultSignal(); + setLevel(strength); - // Verify baseline - verifyLastMobileDataIndicators(true /* visible */, - strength /* strengthIcon */, - DEFAULT_ICON /* typeIcon */); + // Verify baseline + verifyLastMobileDataIndicators(true /* visible */, + strength /* strengthIcon */, + DEFAULT_ICON /* typeIcon */); - // API call is made - setCarrierNetworkChange(true /* enabled */); + // API call is made + setCarrierNetworkChange(true /* enabled */); - // Carrier network change is true, show special indicator - verifyLastMobileDataIndicators(true /* visible */, - SignalDrawable.getCarrierChangeState(CellSignalStrength.getNumSignalStrengthLevels()), - 0 /* typeIcon */); + // Carrier network change is true, show special indicator + verifyLastMobileDataIndicators(true /* visible */, + SignalDrawable.getCarrierChangeState( + CellSignalStrength.getNumSignalStrengthLevels()), + 0 /* typeIcon */); - // Revert back - setCarrierNetworkChange(false /* enabled */); + // Revert back + setCarrierNetworkChange(false /* enabled */); - // Verify back in previous state - verifyLastMobileDataIndicators(true /* visible */, - strength /* strengthIcon */, - DEFAULT_ICON /* typeIcon */); + // Verify back in previous state + verifyLastMobileDataIndicators(true /* visible */, + strength /* strengthIcon */, + DEFAULT_ICON /* typeIcon */); } @Test public void testCarrierNetworkChange_roamingBeforeNetworkChange() { - int strength = SignalStrength.SIGNAL_STRENGTH_GREAT; + int strength = SignalStrength.SIGNAL_STRENGTH_GREAT; - setupDefaultSignal(); - setLevel(strength); - setGsmRoaming(true); + setupDefaultSignal(); + setLevel(strength); + setGsmRoaming(true); - // Verify baseline - verifyLastMobileDataIndicators(true /* visible */, - strength /* strengthIcon */, - DEFAULT_ICON /* typeIcon */, - true /* roaming */); + // Verify baseline + verifyLastMobileDataIndicators(true /* visible */, + strength /* strengthIcon */, + DEFAULT_ICON /* typeIcon */, + true /* roaming */); - // API call is made - setCarrierNetworkChange(true /* enabled */); + // API call is made + setCarrierNetworkChange(true /* enabled */); - // Carrier network change is true, show special indicator, no roaming. - verifyLastMobileDataIndicators(true /* visible */, - SignalDrawable.getCarrierChangeState(CellSignalStrength.getNumSignalStrengthLevels()), - 0 /* typeIcon */, - false /* roaming */); + // Carrier network change is true, show special indicator, no roaming. + verifyLastMobileDataIndicators(true /* visible */, + SignalDrawable.getCarrierChangeState( + CellSignalStrength.getNumSignalStrengthLevels()), + 0 /* typeIcon */, + false /* roaming */); - // Revert back - setCarrierNetworkChange(false /* enabled */); + // Revert back + setCarrierNetworkChange(false /* enabled */); - // Verify back in previous state - verifyLastMobileDataIndicators(true /* visible */, - strength /* strengthIcon */, - DEFAULT_ICON /* typeIcon */, - true /* roaming */); + // Verify back in previous state + verifyLastMobileDataIndicators(true /* visible */, + strength /* strengthIcon */, + DEFAULT_ICON /* typeIcon */, + true /* roaming */); } @Test public void testCarrierNetworkChange_roamingAfterNetworkChange() { - int strength = SignalStrength.SIGNAL_STRENGTH_GREAT; - - setupDefaultSignal(); - setLevel(strength); - - // Verify baseline - verifyLastMobileDataIndicators(true /* visible */, - strength /* strengthIcon */, - DEFAULT_ICON /* typeIcon */, - false /* roaming */); - - // API call is made - setCarrierNetworkChange(true /* enabled */); - - // Carrier network change is true, show special indicator, no roaming. - verifyLastMobileDataIndicators(true /* visible */, - SignalDrawable.getCarrierChangeState(CellSignalStrength.getNumSignalStrengthLevels()), - 0 /* typeIcon */, - false /* roaming */); - - setGsmRoaming(true); - - // Roaming should not show. - verifyLastMobileDataIndicators(true /* visible */, - SignalDrawable.getCarrierChangeState(CellSignalStrength.getNumSignalStrengthLevels()), - 0 /* typeIcon */, - false /* roaming */); - - // Revert back - setCarrierNetworkChange(false /* enabled */); - - // Verify back in previous state - verifyLastMobileDataIndicators(true /* visible */, - strength /* strengthIcon */, - DEFAULT_ICON /* typeIcon */, - true /* roaming */); + int strength = SignalStrength.SIGNAL_STRENGTH_GREAT; + + setupDefaultSignal(); + setLevel(strength); + + // Verify baseline + verifyLastMobileDataIndicators(true /* visible */, + strength /* strengthIcon */, + DEFAULT_ICON /* typeIcon */, + false /* roaming */); + + // API call is made + setCarrierNetworkChange(true /* enabled */); + + // Carrier network change is true, show special indicator, no roaming. + verifyLastMobileDataIndicators(true /* visible */, + SignalDrawable.getCarrierChangeState( + CellSignalStrength.getNumSignalStrengthLevels()), + 0 /* typeIcon */, + false /* roaming */); + + setGsmRoaming(true); + + // Roaming should not show. + verifyLastMobileDataIndicators(true /* visible */, + SignalDrawable.getCarrierChangeState( + CellSignalStrength.getNumSignalStrengthLevels()), + 0 /* typeIcon */, + false /* roaming */); + + // Revert back + setCarrierNetworkChange(false /* enabled */); + + // Verify back in previous state + verifyLastMobileDataIndicators(true /* visible */, + strength /* strengthIcon */, + DEFAULT_ICON /* typeIcon */, + true /* roaming */); } private void verifyEmergencyOnly(boolean isEmergencyOnly) { diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerWifiTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/connectivity/NetworkControllerWifiTest.java index 4a5770d12239..ffeaf207942b 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerWifiTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/connectivity/NetworkControllerWifiTest.java @@ -1,10 +1,25 @@ -package com.android.systemui.statusbar.policy; +/* + * Copyright (C) 2021 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.systemui.statusbar.connectivity; import static junit.framework.Assert.assertEquals; import static org.junit.Assert.assertTrue; import static org.mockito.ArgumentMatchers.anyLong; -import static org.mockito.Matchers.anyLong; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; @@ -21,7 +36,7 @@ import android.testing.AndroidTestingRunner; import android.testing.TestableLooper.RunWithLooper; import com.android.settingslib.mobile.TelephonyIcons; -import com.android.systemui.statusbar.policy.NetworkController.WifiIndicators; +import com.android.systemui.statusbar.connectivity.NetworkController.WifiIndicators; import org.junit.Before; import org.junit.Test; @@ -165,7 +180,7 @@ public class NetworkControllerWifiTest extends NetworkControllerBaseTest { } @Test - public void testWifiIconDisconnectedViaCallback(){ + public void testWifiIconDisconnectedViaCallback() { // Setup normal connection String testSsid = "Test SSID"; int testLevel = 2; @@ -183,7 +198,7 @@ public class NetworkControllerWifiTest extends NetworkControllerBaseTest { } @Test - public void testVpnWithUnderlyingWifi(){ + public void testVpnWithUnderlyingWifi() { String testSsid = "Test SSID"; int testLevel = 2; setWifiEnabled(true); @@ -299,7 +314,7 @@ public class NetworkControllerWifiTest extends NetworkControllerBaseTest { protected void setWifiLevel(int level) { float amountPerLevel = (MAX_RSSI - MIN_RSSI) / (WifiIcons.WIFI_LEVEL_COUNT - 1); - int rssi = (int)(MIN_RSSI + level * amountPerLevel); + int rssi = (int) (MIN_RSSI + level * amountPerLevel); // Put RSSI in the middle of the range. rssi += amountPerLevel / 2; when(mWifiInfo.getRssi()).thenReturn(rssi); diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutControllerTest.java index 07ebaea8bdda..baed694e6fa0 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutControllerTest.java @@ -75,6 +75,7 @@ import com.android.systemui.statusbar.phone.ScrimController; import com.android.systemui.statusbar.phone.ShadeController; import com.android.systemui.statusbar.phone.StatusBar; import com.android.systemui.statusbar.policy.ConfigurationController; +import com.android.systemui.statusbar.policy.DeviceProvisionedController; import com.android.systemui.statusbar.policy.ZenModeController; import com.android.systemui.tuner.TunerService; @@ -99,6 +100,7 @@ public class NotificationStackScrollLayoutControllerTest extends SysuiTestCase { @Mock private HeadsUpManagerPhone mHeadsUpManager; @Mock private NotificationRoundnessManager mNotificationRoundnessManager; @Mock private TunerService mTunerService; + @Mock private DeviceProvisionedController mDeviceProvisionedController; @Mock private DynamicPrivacyController mDynamicPrivacyController; @Mock private ConfigurationController mConfigurationController; @Mock private NotificationStackScrollLayout mNotificationStackScrollLayout; @@ -152,6 +154,7 @@ public class NotificationStackScrollLayoutControllerTest extends SysuiTestCase { mHeadsUpManager, mNotificationRoundnessManager, mTunerService, + mDeviceProvisionedController, mDynamicPrivacyController, mConfigurationController, mSysuiStatusBarStateController, diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java index 4e76b1695f0b..9f42fa4d2ae7 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java @@ -280,6 +280,8 @@ public class NotificationStackScrollLayoutTest extends SysuiTestCase { @Test public void testUpdateFooter_noNotifications() { setBarStateForTest(StatusBarState.SHADE); + mStackScroller.setCurrentUserSetup(true); + FooterView view = mock(FooterView.class); mStackScroller.setFooterView(view); mStackScroller.updateFooter(); @@ -289,6 +291,7 @@ public class NotificationStackScrollLayoutTest extends SysuiTestCase { @Test public void testUpdateFooter_remoteInput() { setBarStateForTest(StatusBarState.SHADE); + mStackScroller.setCurrentUserSetup(true); ExpandableNotificationRow row = mock(ExpandableNotificationRow.class); when(row.canViewBeDismissed()).thenReturn(true); @@ -308,6 +311,7 @@ public class NotificationStackScrollLayoutTest extends SysuiTestCase { @Test public void testUpdateFooter_oneClearableNotification() { setBarStateForTest(StatusBarState.SHADE); + mStackScroller.setCurrentUserSetup(true); when(mEmptyShadeView.getVisibility()).thenReturn(GONE); when(mStackScrollLayoutController.hasActiveClearableNotifications(ROWS_ALL)) @@ -321,8 +325,25 @@ public class NotificationStackScrollLayoutTest extends SysuiTestCase { } @Test + public void testUpdateFooter_oneClearableNotification_beforeUserSetup() { + setBarStateForTest(StatusBarState.SHADE); + mStackScroller.setCurrentUserSetup(false); + + when(mEmptyShadeView.getVisibility()).thenReturn(GONE); + when(mStackScrollLayoutController.hasActiveClearableNotifications(ROWS_ALL)) + .thenReturn(true); + when(mStackScrollLayoutController.hasActiveNotifications()).thenReturn(true); + + FooterView view = mock(FooterView.class); + mStackScroller.setFooterView(view); + mStackScroller.updateFooter(); + verify(mStackScroller).updateFooterView(false, true, true); + } + + @Test public void testUpdateFooter_oneNonClearableNotification() { setBarStateForTest(StatusBarState.SHADE); + mStackScroller.setCurrentUserSetup(true); ExpandableNotificationRow row = mock(ExpandableNotificationRow.class); when(row.canViewBeDismissed()).thenReturn(false); @@ -341,6 +362,8 @@ public class NotificationStackScrollLayoutTest extends SysuiTestCase { @Test public void testUpdateFooter_atEnd() { + mStackScroller.setCurrentUserSetup(true); + // add footer mStackScroller.inflateFooterView(); diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragmentTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragmentTest.java index b08dbee687e6..f23f14801484 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragmentTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragmentTest.java @@ -45,10 +45,10 @@ import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.statusbar.CommandQueue; import com.android.systemui.statusbar.DisableFlagsLogger; import com.android.systemui.statusbar.OperatorNameViewController; +import com.android.systemui.statusbar.connectivity.NetworkController; import com.android.systemui.statusbar.events.SystemStatusAnimationScheduler; import com.android.systemui.statusbar.phone.ongoingcall.OngoingCallController; import com.android.systemui.statusbar.policy.KeyguardStateController; -import com.android.systemui.statusbar.policy.NetworkController; import org.junit.Before; import org.junit.Ignore; diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java index fdda76df64d7..6868e1855c65 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java @@ -112,6 +112,7 @@ import com.android.systemui.statusbar.OperatorNameViewController; import com.android.systemui.statusbar.PulseExpansionHandler; import com.android.systemui.statusbar.StatusBarState; import com.android.systemui.statusbar.StatusBarStateControllerImpl; +import com.android.systemui.statusbar.connectivity.NetworkController; import com.android.systemui.statusbar.events.SystemStatusAnimationScheduler; import com.android.systemui.statusbar.notification.DynamicPrivacyController; import com.android.systemui.statusbar.notification.NotificationEntryManager; @@ -137,7 +138,6 @@ import com.android.systemui.statusbar.policy.DeviceProvisionedController; import com.android.systemui.statusbar.policy.ExtensionController; import com.android.systemui.statusbar.policy.HeadsUpManager; import com.android.systemui.statusbar.policy.KeyguardStateController; -import com.android.systemui.statusbar.policy.NetworkController; import com.android.systemui.statusbar.policy.UserInfoControllerImpl; import com.android.systemui.statusbar.policy.UserSwitcherController; import com.android.systemui.tuner.TunerService; diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallControllerTest.kt index efe2c1774b08..fe7ec68f77b8 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallControllerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallControllerTest.kt @@ -33,6 +33,7 @@ import androidx.test.filters.SmallTest import com.android.internal.logging.testing.UiEventLoggerFake import com.android.systemui.R import com.android.systemui.SysuiTestCase +import com.android.systemui.dump.DumpManager import com.android.systemui.plugins.ActivityStarter import com.android.systemui.flags.FeatureFlags import com.android.systemui.statusbar.notification.collection.NotificationEntry @@ -105,7 +106,9 @@ class OngoingCallControllerTest : SysuiTestCase() { mockActivityStarter, mainExecutor, mockIActivityManager, - OngoingCallLogger(uiEventLoggerFake)) + OngoingCallLogger(uiEventLoggerFake), + DumpManager(), + ) controller.init() controller.addCallback(mockOngoingCallListener) controller.setChipView(chipView) @@ -221,18 +224,6 @@ class OngoingCallControllerTest : SysuiTestCase() { verify(mockOngoingCallListener).onOngoingCallStateChanged(anyBoolean()) } - /** Regression test for b/201097913. */ - @Test - fun onEntryCleanUp_callNotifAddedThenRemoved_listenerNotified() { - val ongoingCallNotifEntry = createOngoingCallNotifEntry() - notifCollectionListener.onEntryAdded(ongoingCallNotifEntry) - reset(mockOngoingCallListener) - - notifCollectionListener.onEntryCleanUp(ongoingCallNotifEntry) - - verify(mockOngoingCallListener).onOngoingCallStateChanged(anyBoolean()) - } - /** Regression test for b/188491504. */ @Test fun onEntryRemoved_removedNotifHasSameKeyAsAddedNotif_listenerNotified() { diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/WallpaperControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/util/WallpaperControllerTest.kt index de86821c0535..b54aadb8228f 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/util/WallpaperControllerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/util/WallpaperControllerTest.kt @@ -138,7 +138,7 @@ class WallpaperControllerTest : SysuiTestCase() { private fun createWallpaperInfo(useDefaultTransition: Boolean = true): WallpaperInfo { val info = mock(WallpaperInfo::class.java) - whenever(info.shouldUseDefaultDeviceStateChangeTransition()) + whenever(info.shouldUseDefaultDisplayStateChangeTransition()) .thenReturn(useDefaultTransition) return info } diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/sensors/FakeProximitySensor.java b/packages/SystemUI/tests/src/com/android/systemui/util/sensors/FakeProximitySensor.java index 50947ab0ee86..22cf744c726b 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/util/sensors/FakeProximitySensor.java +++ b/packages/SystemUI/tests/src/com/android/systemui/util/sensors/FakeProximitySensor.java @@ -19,14 +19,21 @@ package com.android.systemui.util.sensors; import com.android.systemui.util.concurrency.DelayableExecutor; import com.android.systemui.util.concurrency.FakeExecution; -public class FakeProximitySensor extends ProximitySensor { +public class FakeProximitySensor extends ProximitySensorImpl { private boolean mAvailable; private boolean mRegistered; - public FakeProximitySensor(ThresholdSensor primary, ThresholdSensor secondary, - DelayableExecutor delayableExecutor) { - super(primary, secondary == null ? new FakeThresholdSensor() : secondary, - delayableExecutor, new FakeExecution()); + public FakeProximitySensor( + ThresholdSensor primary, + ThresholdSensor secondary, + DelayableExecutor delayableExecutor + ) { + super( + primary, + secondary == null ? new FakeThresholdSensor() : secondary, + delayableExecutor, + new FakeExecution() + ); mAvailable = true; } diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/sensors/FakeThresholdSensor.java b/packages/SystemUI/tests/src/com/android/systemui/util/sensors/FakeThresholdSensor.java index d9f978944cde..0d4a6c7023fb 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/util/sensors/FakeThresholdSensor.java +++ b/packages/SystemUI/tests/src/com/android/systemui/util/sensors/FakeThresholdSensor.java @@ -59,6 +59,16 @@ public class FakeThresholdSensor implements ThresholdSensor { mListeners.remove(listener); } + @Override + public String getName() { + return "FakeThresholdSensorName"; + } + + @Override + public String getType() { + return "FakeThresholdSensorType"; + } + public void setLoaded(boolean loaded) { mIsLoaded = loaded; } diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/sensors/PostureDependentProximitySensorTest.java b/packages/SystemUI/tests/src/com/android/systemui/util/sensors/PostureDependentProximitySensorTest.java new file mode 100644 index 000000000000..075f393df15a --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/util/sensors/PostureDependentProximitySensorTest.java @@ -0,0 +1,114 @@ +/* + * Copyright (C) 2021 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.systemui.util.sensors; + +import static org.junit.Assert.assertEquals; +import static org.mockito.Mockito.verify; + +import android.content.res.Resources; +import android.testing.AndroidTestingRunner; +import android.testing.TestableLooper; + +import androidx.test.filters.SmallTest; + +import com.android.systemui.SysuiTestCase; +import com.android.systemui.statusbar.policy.DevicePostureController; +import com.android.systemui.util.concurrency.FakeExecution; +import com.android.systemui.util.concurrency.FakeExecutor; +import com.android.systemui.util.time.FakeSystemClock; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.ArgumentCaptor; +import org.mockito.Captor; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; + +@SmallTest +@RunWith(AndroidTestingRunner.class) +@TestableLooper.RunWithLooper +public class PostureDependentProximitySensorTest extends SysuiTestCase { + @Mock private Resources mResources; + @Mock private DevicePostureController mDevicePostureController; + @Mock private AsyncSensorManager mSensorManager; + + @Captor private ArgumentCaptor<DevicePostureController.Callback> mPostureListenerCaptor = + ArgumentCaptor.forClass(DevicePostureController.Callback.class); + private DevicePostureController.Callback mPostureListener; + + private PostureDependentProximitySensor mProximitySensor; + private FakeExecutor mFakeExecutor = new FakeExecutor(new FakeSystemClock()); + + @Before + public void setUp() throws Exception { + MockitoAnnotations.initMocks(this); + allowTestableLooperAsMainThread(); + + mProximitySensor = new PostureDependentProximitySensor( + new ThresholdSensor[DevicePostureController.SUPPORTED_POSTURES_SIZE], + new ThresholdSensor[DevicePostureController.SUPPORTED_POSTURES_SIZE], + mFakeExecutor, + new FakeExecution(), + mDevicePostureController + ); + } + + @Test + public void testPostureChangeListenerAdded() { + capturePostureListener(); + } + + @Test + public void testPostureChangeListenerUpdatesPosture() { + // GIVEN posture listener is registered + capturePostureListener(); + + // WHEN the posture changes to DEVICE_POSTURE_OPENED + mPostureListener.onPostureChanged(DevicePostureController.DEVICE_POSTURE_OPENED); + + // THEN device posture is updated to DEVICE_POSTURE_OPENED + assertEquals(DevicePostureController.DEVICE_POSTURE_OPENED, + mProximitySensor.mDevicePosture); + + // WHEN the posture changes to DEVICE_POSTURE_CLOSED + mPostureListener.onPostureChanged(DevicePostureController.DEVICE_POSTURE_CLOSED); + + // THEN device posture is updated to DEVICE_POSTURE_CLOSED + assertEquals(DevicePostureController.DEVICE_POSTURE_CLOSED, + mProximitySensor.mDevicePosture); + + // WHEN the posture changes to DEVICE_POSTURE_FLIPPED + mPostureListener.onPostureChanged(DevicePostureController.DEVICE_POSTURE_FLIPPED); + + // THEN device posture is updated to DEVICE_POSTURE_FLIPPED + assertEquals(DevicePostureController.DEVICE_POSTURE_FLIPPED, + mProximitySensor.mDevicePosture); + + // WHEN the posture changes to DEVICE_POSTURE_HALF_OPENED + mPostureListener.onPostureChanged(DevicePostureController.DEVICE_POSTURE_HALF_OPENED); + + // THEN device posture is updated to DEVICE_POSTURE_HALF_OPENED + assertEquals(DevicePostureController.DEVICE_POSTURE_HALF_OPENED, + mProximitySensor.mDevicePosture); + } + + private void capturePostureListener() { + verify(mDevicePostureController).addCallback(mPostureListenerCaptor.capture()); + mPostureListener = mPostureListenerCaptor.getValue(); + } +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/sensors/ProximityCheckTest.java b/packages/SystemUI/tests/src/com/android/systemui/util/sensors/ProximityCheckTest.java index 242fe9f5fffe..19dbf9aa3c13 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/util/sensors/ProximityCheckTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/util/sensors/ProximityCheckTest.java @@ -49,7 +49,7 @@ public class ProximityCheckTest extends SysuiTestCase { private TestableCallback mTestableCallback = new TestableCallback(); - private ProximitySensor.ProximityCheck mProximityCheck; + private ProximityCheck mProximityCheck; @Before public void setUp() throws Exception { @@ -58,7 +58,7 @@ public class ProximityCheckTest extends SysuiTestCase { thresholdSensor.setLoaded(true); mFakeProximitySensor = new FakeProximitySensor(thresholdSensor, null, mFakeExecutor); - mProximityCheck = new ProximitySensor.ProximityCheck(mFakeProximitySensor, mFakeExecutor); + mProximityCheck = new ProximityCheck(mFakeProximitySensor, mFakeExecutor); } @Test @@ -67,7 +67,7 @@ public class ProximityCheckTest extends SysuiTestCase { assertNull(mTestableCallback.mLastResult); - mFakeProximitySensor.setLastEvent(new ProximitySensor.ThresholdSensorEvent(true, 0)); + mFakeProximitySensor.setLastEvent(new ThresholdSensorEvent(true, 0)); mFakeProximitySensor.alertListeners(); assertTrue(mTestableCallback.mLastResult); @@ -103,7 +103,7 @@ public class ProximityCheckTest extends SysuiTestCase { mProximityCheck.check(100, mTestableCallback); - mFakeProximitySensor.setLastEvent(new ProximitySensor.ThresholdSensorEvent(true, 0)); + mFakeProximitySensor.setLastEvent(new ThresholdSensorEvent(true, 0)); mFakeProximitySensor.alertListeners(); assertThat(mTestableCallback.mLastResult).isNotNull(); @@ -123,7 +123,7 @@ public class ProximityCheckTest extends SysuiTestCase { assertNull(mTestableCallback.mLastResult); - mFakeProximitySensor.setLastEvent(new ProximitySensor.ThresholdSensorEvent(true, 0)); + mFakeProximitySensor.setLastEvent(new ThresholdSensorEvent(true, 0)); mFakeProximitySensor.alertListeners(); assertTrue(mTestableCallback.mLastResult); diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/sensors/ProximitySensorDualTest.java b/packages/SystemUI/tests/src/com/android/systemui/util/sensors/ProximitySensorImplDualTest.java index 0e9d96c61e54..5e7557896145 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/util/sensors/ProximitySensorDualTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/util/sensors/ProximitySensorImplDualTest.java @@ -42,7 +42,7 @@ import org.mockito.MockitoAnnotations; @SmallTest @RunWith(AndroidTestingRunner.class) @TestableLooper.RunWithLooper -public class ProximitySensorDualTest extends SysuiTestCase { +public class ProximitySensorImplDualTest extends SysuiTestCase { private ProximitySensor mProximitySensor; private FakeExecutor mFakeExecutor = new FakeExecutor(new FakeSystemClock()); private FakeThresholdSensor mThresholdSensorPrimary; @@ -57,7 +57,7 @@ public class ProximitySensorDualTest extends SysuiTestCase { mThresholdSensorSecondary = new FakeThresholdSensor(); mThresholdSensorSecondary.setLoaded(true); - mProximitySensor = new ProximitySensor( + mProximitySensor = new ProximitySensorImpl( mThresholdSensorPrimary, mThresholdSensorSecondary, mFakeExecutor, new FakeExecution()); } @@ -430,11 +430,11 @@ public class ProximitySensorDualTest extends SysuiTestCase { } private static class TestableListener implements ThresholdSensor.Listener { - ThresholdSensor.ThresholdSensorEvent mLastEvent; + ThresholdSensorEvent mLastEvent; int mCallCount = 0; @Override - public void onThresholdCrossed(ThresholdSensor.ThresholdSensorEvent proximityEvent) { + public void onThresholdCrossed(ThresholdSensorEvent proximityEvent) { mLastEvent = proximityEvent; mCallCount++; } diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/sensors/ProximitySensorSingleTest.java b/packages/SystemUI/tests/src/com/android/systemui/util/sensors/ProximitySensorImplSingleTest.java index 6c6d355d7866..752cd3211161 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/util/sensors/ProximitySensorSingleTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/util/sensors/ProximitySensorImplSingleTest.java @@ -42,7 +42,7 @@ import org.mockito.MockitoAnnotations; @SmallTest @RunWith(AndroidTestingRunner.class) @TestableLooper.RunWithLooper -public class ProximitySensorSingleTest extends SysuiTestCase { +public class ProximitySensorImplSingleTest extends SysuiTestCase { private ProximitySensor mProximitySensor; private FakeExecutor mFakeExecutor = new FakeExecutor(new FakeSystemClock()); private FakeThresholdSensor mThresholdSensor; @@ -54,7 +54,7 @@ public class ProximitySensorSingleTest extends SysuiTestCase { mThresholdSensor = new FakeThresholdSensor(); mThresholdSensor.setLoaded(true); - mProximitySensor = new ProximitySensor( + mProximitySensor = new ProximitySensorImpl( mThresholdSensor, new FakeThresholdSensor(), mFakeExecutor, new FakeExecution()); } @@ -215,7 +215,7 @@ public class ProximitySensorSingleTest extends SysuiTestCase { public void testPreventRecursiveAlert() { TestableListener listenerA = new TestableListener() { @Override - public void onThresholdCrossed(ProximitySensor.ThresholdSensorEvent proximityEvent) { + public void onThresholdCrossed(ThresholdSensorEvent proximityEvent) { super.onThresholdCrossed(proximityEvent); if (mCallCount < 2) { mProximitySensor.alertListeners(); @@ -231,11 +231,11 @@ public class ProximitySensorSingleTest extends SysuiTestCase { } private static class TestableListener implements ThresholdSensor.Listener { - ThresholdSensor.ThresholdSensorEvent mLastEvent; + ThresholdSensorEvent mLastEvent; int mCallCount = 0; @Override - public void onThresholdCrossed(ThresholdSensor.ThresholdSensorEvent proximityEvent) { + public void onThresholdCrossed(ThresholdSensorEvent proximityEvent) { mLastEvent = proximityEvent; mCallCount++; } diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/sensors/ThresholdSensorImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/util/sensors/ThresholdSensorImplTest.java index 125063a7adc4..b10f16c963ed 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/util/sensors/ThresholdSensorImplTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/util/sensors/ThresholdSensorImplTest.java @@ -380,7 +380,7 @@ public class ThresholdSensorImplTest extends SysuiTestCase { int mCallCount; @Override - public void onThresholdCrossed(ThresholdSensor.ThresholdSensorEvent event) { + public void onThresholdCrossed(ThresholdSensorEvent event) { mBelow = event.getBelow(); mTimestampNs = event.getTimestampNs(); mCallCount++; diff --git a/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeNetworkController.java b/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeNetworkController.java index e7acfae24f30..8ea9da6f4d0e 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeNetworkController.java +++ b/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeNetworkController.java @@ -18,9 +18,9 @@ import android.os.Bundle; import android.testing.LeakCheck; import com.android.settingslib.net.DataUsageController; +import com.android.systemui.statusbar.connectivity.NetworkController; +import com.android.systemui.statusbar.connectivity.NetworkController.SignalCallback; import com.android.systemui.statusbar.policy.DataSaverController; -import com.android.systemui.statusbar.policy.NetworkController; -import com.android.systemui.statusbar.policy.NetworkController.SignalCallback; public class FakeNetworkController extends BaseLeakChecker<SignalCallback> implements NetworkController { diff --git a/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/LeakCheckedTest.java b/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/LeakCheckedTest.java index fedc08d93bc7..dc6a8fb9a4c4 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/LeakCheckedTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/LeakCheckedTest.java @@ -19,6 +19,7 @@ import android.util.ArrayMap; import com.android.systemui.SysuiTestCase; import com.android.systemui.shared.plugins.PluginManager; +import com.android.systemui.statusbar.connectivity.NetworkController; import com.android.systemui.statusbar.phone.ManagedProfileController; import com.android.systemui.statusbar.phone.StatusBarIconController; import com.android.systemui.statusbar.policy.BatteryController; @@ -29,7 +30,6 @@ import com.android.systemui.statusbar.policy.FlashlightController; import com.android.systemui.statusbar.policy.HotspotController; import com.android.systemui.statusbar.policy.KeyguardStateController; import com.android.systemui.statusbar.policy.LocationController; -import com.android.systemui.statusbar.policy.NetworkController; import com.android.systemui.statusbar.policy.NextAlarmController; import com.android.systemui.statusbar.policy.RotationLockController; import com.android.systemui.statusbar.policy.SecurityController; diff --git a/services/contentcapture/java/com/android/server/contentcapture/ContentCapturePerUserService.java b/services/contentcapture/java/com/android/server/contentcapture/ContentCapturePerUserService.java index 904def0af2cf..6bd1fa6c335d 100644 --- a/services/contentcapture/java/com/android/server/contentcapture/ContentCapturePerUserService.java +++ b/services/contentcapture/java/com/android/server/contentcapture/ContentCapturePerUserService.java @@ -190,6 +190,9 @@ final class ContentCapturePerUserService Slog.w(TAG, "remote service died: " + service); synchronized (mLock) { mZombie = true; + writeServiceEvent( + FrameworkStatsLog.CONTENT_CAPTURE_SERVICE_EVENTS__EVENT__ON_REMOTE_SERVICE_DIED, + getServiceComponentName()); } } diff --git a/services/core/java/com/android/server/display/DisplayModeDirector.java b/services/core/java/com/android/server/display/DisplayModeDirector.java index 5fc301e60b9d..0a22f2fdfdf8 100644 --- a/services/core/java/com/android/server/display/DisplayModeDirector.java +++ b/services/core/java/com/android/server/display/DisplayModeDirector.java @@ -17,6 +17,7 @@ package com.android.server.display; import static android.hardware.display.DisplayManagerInternal.REFRESH_RATE_LIMIT_HIGH_BRIGHTNESS_MODE; +import static android.os.PowerManager.BRIGHTNESS_INVALID; import android.annotation.NonNull; import android.annotation.Nullable; @@ -40,6 +41,7 @@ import android.os.IThermalEventListener; import android.os.IThermalService; import android.os.Looper; import android.os.Message; +import android.os.PowerManager; import android.os.RemoteException; import android.os.ServiceManager; import android.os.SystemClock; @@ -60,6 +62,7 @@ import android.view.DisplayInfo; import com.android.internal.R; import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; +import com.android.internal.display.BrightnessSynchronizer; import com.android.internal.os.BackgroundThread; import com.android.server.LocalServices; import com.android.server.display.utils.AmbientFilter; @@ -155,7 +158,7 @@ public class DisplayModeDirector { mAppRequestObserver = new AppRequestObserver(); mSettingsObserver = new SettingsObserver(context, handler); mDisplayObserver = new DisplayObserver(context, handler); - mBrightnessObserver = new BrightnessObserver(context, handler); + mBrightnessObserver = new BrightnessObserver(context, handler, injector); mUdfpsObserver = new UdfpsObserver(); final BallotBox ballotBox = (displayId, priority, vote) -> { synchronized (mLock) { @@ -1427,8 +1430,6 @@ public class DisplayModeDirector { @Override public void onDisplayChanged(int displayId) { updateDisplayModes(displayId); - // TODO: Break the coupling between DisplayObserver and BrightnessObserver. - mBrightnessObserver.onDisplayChanged(displayId); } private void updateDisplayModes(int displayId) { @@ -1465,7 +1466,7 @@ public class DisplayModeDirector { * {@link R.array#config_ambientThresholdsOfPeakRefreshRate}. */ @VisibleForTesting - public class BrightnessObserver extends ContentObserver { + public class BrightnessObserver implements DisplayManager.DisplayListener { private final static int LIGHT_SENSOR_RATE_MS = 250; private int[] mLowDisplayBrightnessThresholds; private int[] mLowAmbientBrightnessThresholds; @@ -1488,6 +1489,8 @@ public class DisplayModeDirector { private int mBrightness = -1; private final Context mContext; + private final Injector mInjector; + private final Handler mHandler; // Enable light sensor only when mShouldObserveAmbientLowChange is true or // mShouldObserveAmbientHighChange is true, screen is on, peak refresh rate @@ -1500,9 +1503,11 @@ public class DisplayModeDirector { private int mRefreshRateInLowZone; private int mRefreshRateInHighZone; - BrightnessObserver(Context context, Handler handler) { - super(handler); + BrightnessObserver(Context context, Handler handler, Injector injector) { mContext = context; + mHandler = handler; + mInjector = injector; + mLowDisplayBrightnessThresholds = context.getResources().getIntArray( R.array.config_brightnessThresholdsOfPeakRefreshRate); mLowAmbientBrightnessThresholds = context.getResources().getIntArray( @@ -1569,8 +1574,7 @@ public class DisplayModeDirector { public void observe(SensorManager sensorManager) { mSensorManager = sensorManager; final ContentResolver cr = mContext.getContentResolver(); - mBrightness = Settings.System.getIntForUser(cr, - Settings.System.SCREEN_BRIGHTNESS, -1 /*default*/, cr.getUserId()); + mBrightness = getBrightness(Display.DEFAULT_DISPLAY); // DeviceConfig is accessible after system ready. int[] lowDisplayBrightnessThresholds = @@ -1603,6 +1607,10 @@ public class DisplayModeDirector { restartObserver(); mDeviceConfigDisplaySettings.startListening(); + + mInjector.registerDisplayListener(this, mHandler, + DisplayManager.EVENT_FLAG_DISPLAY_CHANGED | + DisplayManager.EVENT_FLAG_DISPLAY_BRIGHTNESS); } public void setLoggingEnabled(boolean loggingEnabled) { @@ -1718,28 +1726,30 @@ public class DisplayModeDirector { } } + @Override + public void onDisplayAdded(int displayId) {} + + @Override + public void onDisplayRemoved(int displayId) {} + + @Override public void onDisplayChanged(int displayId) { if (displayId == Display.DEFAULT_DISPLAY) { updateDefaultDisplayState(); - } - } - @Override - public void onChange(boolean selfChange, Uri uri, int userId) { - synchronized (mLock) { - final ContentResolver cr = mContext.getContentResolver(); - int brightness = Settings.System.getIntForUser(cr, - Settings.System.SCREEN_BRIGHTNESS, -1 /*default*/, cr.getUserId()); - if (brightness != mBrightness) { - mBrightness = brightness; - onBrightnessChangedLocked(); + // We don't support multiple display blocking zones yet, so only handle + // brightness changes for the default display for now. + int brightness = getBrightness(displayId); + synchronized (mLock) { + if (brightness != mBrightness) { + mBrightness = brightness; + onBrightnessChangedLocked(); + } } } } private void restartObserver() { - final ContentResolver cr = mContext.getContentResolver(); - if (mRefreshRateInLowZone > 0) { mShouldObserveDisplayLowChange = hasValidThreshold( mLowDisplayBrightnessThresholds); @@ -1760,15 +1770,6 @@ public class DisplayModeDirector { mShouldObserveAmbientHighChange = false; } - if (mShouldObserveDisplayLowChange || mShouldObserveDisplayHighChange) { - // Content Service does not check if an listener has already been registered. - // To ensure only one listener is registered, force an unregistration first. - mInjector.unregisterBrightnessObserver(cr, this); - mInjector.registerBrightnessObserver(cr, this); - } else { - mInjector.unregisterBrightnessObserver(cr, this); - } - if (mShouldObserveAmbientLowChange || mShouldObserveAmbientHighChange) { Resources resources = mContext.getResources(); String lightSensorType = resources.getString( @@ -1968,6 +1969,15 @@ public class DisplayModeDirector { return mDefaultDisplayState == Display.STATE_ON; } + private int getBrightness(int displayId) { + final BrightnessInfo info = mInjector.getBrightnessInfo(displayId); + if (info != null) { + return BrightnessSynchronizer.brightnessFloatToInt(info.adjustedBrightness); + } + + return BRIGHTNESS_INVALID; + } + private final class LightSensorEventListener implements SensorEventListener { final private static int INJECT_EVENTS_INTERVAL_MS = LIGHT_SENSOR_RATE_MS; private float mLastSensorData; @@ -2283,6 +2293,7 @@ public class DisplayModeDirector { private final BallotBox mBallotBox; private final Handler mHandler; private final SparseIntArray mHbmMode = new SparseIntArray(); + private final SparseBooleanArray mHbmActive = new SparseBooleanArray(); private final Injector mInjector; private final DeviceConfigDisplaySettings mDeviceConfigDisplaySettings; private int mRefreshRateInHbmSunlight; @@ -2351,6 +2362,7 @@ public class DisplayModeDirector { public void onDisplayRemoved(int displayId) { mBallotBox.vote(displayId, Vote.PRIORITY_HIGH_BRIGHTNESS_MODE, null); mHbmMode.delete(displayId); + mHbmActive.delete(displayId); } @Override @@ -2360,12 +2372,17 @@ public class DisplayModeDirector { // Display no longer there. Assume we'll get an onDisplayRemoved very soon. return; } + final int hbmMode = info.highBrightnessMode; - if (hbmMode == mHbmMode.get(displayId)) { + final boolean isHbmActive = hbmMode != BrightnessInfo.HIGH_BRIGHTNESS_MODE_OFF && + info.adjustedBrightness > info.highBrightnessTransitionPoint; + if (hbmMode == mHbmMode.get(displayId) && + isHbmActive == mHbmActive.get(displayId)) { // no change, ignore. return; } mHbmMode.put(displayId, hbmMode); + mHbmActive.put(displayId, isHbmActive); recalculateVotesForDisplay(displayId); } @@ -2379,28 +2396,36 @@ public class DisplayModeDirector { } private void recalculateVotesForDisplay(int displayId) { - final int hbmMode = mHbmMode.get(displayId, BrightnessInfo.HIGH_BRIGHTNESS_MODE_OFF); Vote vote = null; - if (hbmMode == BrightnessInfo.HIGH_BRIGHTNESS_MODE_SUNLIGHT) { - // Device resource properties take priority over DisplayDeviceConfig - if (mRefreshRateInHbmSunlight > 0) { - vote = Vote.forRefreshRates(mRefreshRateInHbmSunlight, - mRefreshRateInHbmSunlight); - } else { - final List<RefreshRateLimitation> limits = - mDisplayManagerInternal.getRefreshRateLimitations(displayId); - for (int i = 0; limits != null && i < limits.size(); i++) { - final RefreshRateLimitation limitation = limits.get(i); - if (limitation.type == REFRESH_RATE_LIMIT_HIGH_BRIGHTNESS_MODE) { - vote = Vote.forRefreshRates(limitation.range.min, limitation.range.max); - break; + if (mHbmActive.get(displayId, false)) { + final int hbmMode = + mHbmMode.get(displayId, BrightnessInfo.HIGH_BRIGHTNESS_MODE_OFF); + if (hbmMode == BrightnessInfo.HIGH_BRIGHTNESS_MODE_SUNLIGHT) { + // Device resource properties take priority over DisplayDeviceConfig + if (mRefreshRateInHbmSunlight > 0) { + vote = Vote.forRefreshRates(mRefreshRateInHbmSunlight, + mRefreshRateInHbmSunlight); + } else { + final List<RefreshRateLimitation> limits = + mDisplayManagerInternal.getRefreshRateLimitations(displayId); + for (int i = 0; limits != null && i < limits.size(); i++) { + final RefreshRateLimitation limitation = limits.get(i); + if (limitation.type == REFRESH_RATE_LIMIT_HIGH_BRIGHTNESS_MODE) { + vote = Vote.forRefreshRates(limitation.range.min, + limitation.range.max); + break; + } } } + } else if (hbmMode == BrightnessInfo.HIGH_BRIGHTNESS_MODE_HDR && + mRefreshRateInHbmHdr > 0) { + // HBM for HDR vote isn't supported through DisplayDeviceConfig yet, so look for + // a vote from Device properties + vote = Vote.forRefreshRates(mRefreshRateInHbmHdr, mRefreshRateInHbmHdr); + } else { + Slog.w(TAG, "Unexpected HBM mode " + hbmMode + " for display ID " + displayId); } - } - if (hbmMode == BrightnessInfo.HIGH_BRIGHTNESS_MODE_HDR - && mRefreshRateInHbmHdr > 0) { - vote = Vote.forRefreshRates(mRefreshRateInHbmHdr, mRefreshRateInHbmHdr); + } mBallotBox.vote(displayId, Vote.PRIORITY_HIGH_BRIGHTNESS_MODE, vote); } @@ -2408,6 +2433,7 @@ public class DisplayModeDirector { void dumpLocked(PrintWriter pw) { pw.println(" HbmObserver"); pw.println(" mHbmMode: " + mHbmMode); + pw.println(" mHbmActive: " + mHbmActive); pw.println(" mRefreshRateInHbmSunlight: " + mRefreshRateInHbmSunlight); pw.println(" mRefreshRateInHbmHdr: " + mRefreshRateInHbmHdr); } @@ -2630,19 +2656,11 @@ public class DisplayModeDirector { } interface Injector { - // TODO: brightnessfloat: change this to the float setting - Uri DISPLAY_BRIGHTNESS_URI = Settings.System.getUriFor(Settings.System.SCREEN_BRIGHTNESS); Uri PEAK_REFRESH_RATE_URI = Settings.System.getUriFor(Settings.System.PEAK_REFRESH_RATE); @NonNull DeviceConfigInterface getDeviceConfig(); - void registerBrightnessObserver(@NonNull ContentResolver cr, - @NonNull ContentObserver observer); - - void unregisterBrightnessObserver(@NonNull ContentResolver cr, - @NonNull ContentObserver observer); - void registerPeakRefreshRateObserver(@NonNull ContentResolver cr, @NonNull ContentObserver observer); @@ -2672,19 +2690,6 @@ public class DisplayModeDirector { } @Override - public void registerBrightnessObserver(@NonNull ContentResolver cr, - @NonNull ContentObserver observer) { - cr.registerContentObserver(DISPLAY_BRIGHTNESS_URI, false /*notifyDescendants*/, - observer, UserHandle.USER_SYSTEM); - } - - @Override - public void unregisterBrightnessObserver(@NonNull ContentResolver cr, - @NonNull ContentObserver observer) { - cr.unregisterContentObserver(observer); - } - - @Override public void registerPeakRefreshRateObserver(@NonNull ContentResolver cr, @NonNull ContentObserver observer) { cr.registerContentObserver(PEAK_REFRESH_RATE_URI, false /*notifyDescendants*/, diff --git a/services/core/java/com/android/server/display/DisplayPowerController.java b/services/core/java/com/android/server/display/DisplayPowerController.java index 1063481d6788..bf5208aed3f7 100644 --- a/services/core/java/com/android/server/display/DisplayPowerController.java +++ b/services/core/java/com/android/server/display/DisplayPowerController.java @@ -1259,10 +1259,6 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call putScreenBrightnessSetting(brightnessState, /* updateCurrent */ true); } - // We save the brightness info *after* the brightness setting has been changed so that - // the brightness info reflects the latest value. - saveBrightnessInfo(getScreenBrightnessSetting()); - // Apply dimming by at least some minimum amount when user activity // timeout is about to expire. if (mPowerRequest.policy == DisplayPowerRequest.POLICY_DIM) { @@ -1393,6 +1389,11 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call hadUserBrightnessPoint); } + // We save the brightness info *after* the brightness setting has been changed and + // adjustments made so that the brightness info reflects the latest value. + saveBrightnessInfo(getScreenBrightnessSetting(), animateValue); + } else { + saveBrightnessInfo(getScreenBrightnessSetting()); } // Log any changes to what is currently driving the brightness setting. @@ -1509,18 +1510,27 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call synchronized (mCachedBrightnessInfo) { return new BrightnessInfo( mCachedBrightnessInfo.brightness, + mCachedBrightnessInfo.adjustedBrightness, mCachedBrightnessInfo.brightnessMin, mCachedBrightnessInfo.brightnessMax, - mCachedBrightnessInfo.hbmMode); + mCachedBrightnessInfo.hbmMode, + mCachedBrightnessInfo.highBrightnessTransitionPoint); } } private void saveBrightnessInfo(float brightness) { + saveBrightnessInfo(brightness, brightness); + } + + private void saveBrightnessInfo(float brightness, float adjustedBrightness) { synchronized (mCachedBrightnessInfo) { mCachedBrightnessInfo.brightness = brightness; + mCachedBrightnessInfo.adjustedBrightness = adjustedBrightness; mCachedBrightnessInfo.brightnessMin = mHbmController.getCurrentBrightnessMin(); mCachedBrightnessInfo.brightnessMax = mHbmController.getCurrentBrightnessMax(); mCachedBrightnessInfo.hbmMode = mHbmController.getHighBrightnessMode(); + mCachedBrightnessInfo.highBrightnessTransitionPoint = + mHbmController.getTransitionPoint(); } } @@ -2195,6 +2205,18 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call pw.println(" mSkipScreenOnBrightnessRamp=" + mSkipScreenOnBrightnessRamp); pw.println(" mColorFadeFadesConfig=" + mColorFadeFadesConfig); pw.println(" mColorFadeEnabled=" + mColorFadeEnabled); + synchronized (mCachedBrightnessInfo) { + pw.println(" mCachedBrightnessInfo.brightness=" + mCachedBrightnessInfo.brightness); + pw.println(" mCachedBrightnessInfo.adjustedBrightness=" + + mCachedBrightnessInfo.adjustedBrightness); + pw.println(" mCachedBrightnessInfo.brightnessMin=" + + mCachedBrightnessInfo.brightnessMin); + pw.println(" mCachedBrightnessInfo.brightnessMax=" + + mCachedBrightnessInfo.brightnessMax); + pw.println(" mCachedBrightnessInfo.hbmMode=" + mCachedBrightnessInfo.hbmMode); + pw.println(" mCachedBrightnessInfo.highBrightnessTransitionPoint=" + + mCachedBrightnessInfo.highBrightnessTransitionPoint); + } pw.println(" mDisplayBlanksAfterDozeConfig=" + mDisplayBlanksAfterDozeConfig); pw.println(" mBrightnessBucketsInDozeConfig=" + mBrightnessBucketsInDozeConfig); @@ -2606,8 +2628,10 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call static class CachedBrightnessInfo { public float brightness; + public float adjustedBrightness; public float brightnessMin; public float brightnessMax; public int hbmMode; + public float highBrightnessTransitionPoint; } } diff --git a/services/core/java/com/android/server/display/HighBrightnessModeController.java b/services/core/java/com/android/server/display/HighBrightnessModeController.java index 2791f6a409be..1e1cfeb5b9dd 100644 --- a/services/core/java/com/android/server/display/HighBrightnessModeController.java +++ b/services/core/java/com/android/server/display/HighBrightnessModeController.java @@ -59,6 +59,9 @@ class HighBrightnessModeController { private static final float HDR_PERCENT_OF_SCREEN_REQUIRED = 0.50f; + @VisibleForTesting + static final float HBM_TRANSITION_POINT_INVALID = Float.POSITIVE_INFINITY; + private final float mBrightnessMin; private final float mBrightnessMax; private final Handler mHandler; @@ -214,6 +217,14 @@ class HighBrightnessModeController { return mHbmMode; } + float getTransitionPoint() { + if (deviceSupportsHbm()) { + return mHbmData.transitionPoint; + } else { + return HBM_TRANSITION_POINT_INVALID; + } + } + void stop() { registerHdrListener(null /*displayToken*/); mSkinThermalStatusObserver.stopObserving(); diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java index dd2583a0ce1a..5acff2b6c743 100644 --- a/services/core/java/com/android/server/policy/PhoneWindowManager.java +++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java @@ -1816,7 +1816,11 @@ public class PhoneWindowManager implements WindowManagerPolicy { @Override public int onAppTransitionStartingLocked(boolean keyguardGoingAway, long duration, long statusBarAnimationStartTime, long statusBarAnimationDuration) { - return handleStartTransitionForKeyguardLw(keyguardGoingAway, duration); + // When remote animation is enabled for KEYGUARD_GOING_AWAY transition, SysUI + // receives IRemoteAnimationRunner#onAnimationStart to start animation, so we don't + // need to call IKeyguardService#keyguardGoingAway here. + return handleStartTransitionForKeyguardLw(keyguardGoingAway + && !WindowManagerService.sEnableRemoteKeyguardGoingAwayAnimation, duration); } @Override @@ -3064,7 +3068,7 @@ public class PhoneWindowManager implements WindowManagerPolicy { private int handleStartTransitionForKeyguardLw(boolean keyguardGoingAway, long duration) { final int res = applyKeyguardOcclusionChange(); if (res != 0) return res; - if (!WindowManagerService.sEnableRemoteKeyguardGoingAwayAnimation && keyguardGoingAway) { + if (keyguardGoingAway) { if (DEBUG_KEYGUARD) Slog.d(TAG, "Starting keyguard exit animation"); startKeyguardExitAnimation(SystemClock.uptimeMillis(), duration); } diff --git a/services/core/java/com/android/server/policy/keyguard/KeyguardServiceDelegate.java b/services/core/java/com/android/server/policy/keyguard/KeyguardServiceDelegate.java index d1906785994a..86ff33e8cc42 100644 --- a/services/core/java/com/android/server/policy/keyguard/KeyguardServiceDelegate.java +++ b/services/core/java/com/android/server/policy/keyguard/KeyguardServiceDelegate.java @@ -410,8 +410,7 @@ public class KeyguardServiceDelegate { } public void startKeyguardExitAnimation(long startTime, long fadeoutDuration) { - if (!WindowManagerService.sEnableRemoteKeyguardGoingAwayAnimation - && mKeyguardService != null) { + if (mKeyguardService != null) { mKeyguardService.startKeyguardExitAnimation(startTime, fadeoutDuration); } } diff --git a/services/core/java/com/android/server/wm/ActivityClientController.java b/services/core/java/com/android/server/wm/ActivityClientController.java index cc0db1d12e7b..ee72fc8622a5 100644 --- a/services/core/java/com/android/server/wm/ActivityClientController.java +++ b/services/core/java/com/android/server/wm/ActivityClientController.java @@ -1072,7 +1072,7 @@ class ActivityClientController extends IActivityClientController.Stub { r.mDisplayContent.mAppTransition.overridePendingAppTransition( packageName, enterAnim, exitAnim, null, null, r.mOverrideTaskTransition); - mService.getTransitionController().setOverrideAnimation( + r.mTransitionController.setOverrideAnimation( TransitionInfo.AnimationOptions.makeCustomAnimOptions(packageName, enterAnim, exitAnim, r.mOverrideTaskTransition), null /* startCallback */, null /* finishCallback */); diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java index c1fcf71b38d2..3b1da1d0053d 100644 --- a/services/core/java/com/android/server/wm/ActivityRecord.java +++ b/services/core/java/com/android/server/wm/ActivityRecord.java @@ -1531,7 +1531,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A } // TODO(b/169035022): move to a more-appropriate place. - mAtmService.getTransitionController().collect(this); + mTransitionController.collect(this); if (prevDc.mOpeningApps.remove(this)) { // Transfer opening transition to new display. mDisplayContent.mOpeningApps.add(this); @@ -3096,9 +3096,9 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A mAtmService.deferWindowLayout(); try { - final Transition newTransition = (!mAtmService.getTransitionController().isCollecting() - && mAtmService.getTransitionController().getTransitionPlayer() != null) - ? mAtmService.getTransitionController().createTransition(TRANSIT_CLOSE) : null; + final Transition newTransition = (!mTransitionController.isCollecting() + && mTransitionController.getTransitionPlayer() != null) + ? mTransitionController.createTransition(TRANSIT_CLOSE) : null; mTaskSupervisor.mNoHistoryActivities.remove(this); makeFinishingLocked(); // Make a local reference to its task since this.task could be set to null once this @@ -3131,7 +3131,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A final boolean endTask = task.getTopNonFinishingActivity() == null && !task.isClearingToReuseTask(); if (newTransition != null) { - mAtmService.getTransitionController().requestStartTransition(newTransition, + mTransitionController.requestStartTransition(newTransition, endTask ? task : null, null /* remote */); } if (isState(RESUMED)) { @@ -3559,12 +3559,12 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A if (stopped) { abortAndClearOptionsAnimation(); } - if (mAtmService.getTransitionController().isCollecting()) { + if (mTransitionController.isCollecting()) { // We don't want the finishing to change the transition ready state since there will not // be corresponding setReady for finishing. - mAtmService.getTransitionController().collectExistenceChange(this); + mTransitionController.collectExistenceChange(this); } else { - mAtmService.getTransitionController().requestTransitionIfNeeded(TRANSIT_CLOSE, this); + mTransitionController.requestTransitionIfNeeded(TRANSIT_CLOSE, this); } } @@ -3816,7 +3816,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A } else if (getDisplayContent().mAppTransition.isTransitionSet()) { getDisplayContent().mClosingApps.add(this); delayed = true; - } else if (mAtmService.getTransitionController().inTransition()) { + } else if (mTransitionController.inTransition()) { delayed = true; } @@ -3828,7 +3828,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A } // TODO(b/169035022): move to a more-appropriate place. - mAtmService.getTransitionController().collect(this); + mTransitionController.collect(this); ProtoLog.v(WM_DEBUG_APP_TRANSITIONS, "Removing app %s delayed=%b animation=%s animating=%b", this, delayed, @@ -4029,7 +4029,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A ProtoLog.v(WM_DEBUG_ADD_REMOVE, "Removing starting %s from %s", tStartingWindow, fromActivity); - mAtmService.getTransitionController().collect(tStartingWindow); + mTransitionController.collect(tStartingWindow); tStartingWindow.reparent(this, POSITION_TOP); // Propagate other interesting state between the tokens. If the old token is displayed, @@ -4055,7 +4055,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A // the token we transfer the animation over. Thus, set this flag to indicate // we've transferred the animation. mUseTransferredAnimation = true; - } else if (mAtmService.getTransitionController().getTransitionPlayer() != null) { + } else if (mTransitionController.getTransitionPlayer() != null) { // In the new transit system, just set this every time we transfer the window mUseTransferredAnimation = true; } @@ -4552,8 +4552,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A } if (options != null) { - mAtmService.getTransitionController().setOverrideAnimation(options, - startCallback, finishCallback); + mTransitionController.setOverrideAnimation(options, startCallback, finishCallback); } } @@ -4784,7 +4783,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A Debug.getCallers(6)); // Before setting mVisibleRequested so we can track changes. - mAtmService.getTransitionController().collect(this); + mTransitionController.collect(this); onChildVisibilityRequested(visible); @@ -4856,7 +4855,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A } // If in a transition, defer commits for activities that are going invisible - if (!visible && mAtmService.getTransitionController().inTransition(this)) { + if (!visible && inTransition()) { return; } // If we are preparing an app transition, then delay changing @@ -4983,8 +4982,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A * this has become invisible. */ private void postApplyAnimation(boolean visible) { - final boolean usingShellTransitions = - mAtmService.getTransitionController().getTransitionPlayer() != null; + final boolean usingShellTransitions = mTransitionController.isShellTransitionsEnabled(); final boolean delayed = isAnimating(TRANSITION | PARENTS | CHILDREN, ANIMATION_TYPE_APP_TRANSITION | ANIMATION_TYPE_WINDOW_ANIMATION | ANIMATION_TYPE_RECENTS); @@ -5434,7 +5432,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A // returns. Just need to confirm this reasoning makes sense. final boolean deferHidingClient = canEnterPictureInPicture && !isState(STARTED, STOPPING, STOPPED, PAUSED); - if (!mAtmService.getTransitionController().isShellTransitionsEnabled() + if (!mTransitionController.isShellTransitionsEnabled() && deferHidingClient && pictureInPictureArgs.isAutoEnterEnabled()) { // Go ahead and just put the activity in pip if it supports auto-pip. mAtmService.enterPictureInPictureMode(this, pictureInPictureArgs); @@ -5956,7 +5954,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A } void startFreezingScreen(int overrideOriginalDisplayRotation) { - if (mAtmService.getTransitionController().isShellTransitionsEnabled()) { + if (mTransitionController.isShellTransitionsEnabled()) { return; } ProtoLog.i(WM_DEBUG_ORIENTATION, @@ -7602,7 +7600,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A } boolean isInTransition() { - return mAtmService.getTransitionController().inTransition() // Shell transitions. + return mTransitionController.inTransition() // Shell transitions. || isAnimating(PARENTS | TRANSITION); // Legacy transitions. } diff --git a/services/core/java/com/android/server/wm/ActivityStarter.java b/services/core/java/com/android/server/wm/ActivityStarter.java index 980ebf0646e8..170789569b3f 100644 --- a/services/core/java/com/android/server/wm/ActivityStarter.java +++ b/services/core/java/com/android/server/wm/ActivityStarter.java @@ -1564,14 +1564,15 @@ class ActivityStarter { // startActivityInner. Otherwise, logic in startActivityInner could start a different // transition based on a sub-action. // Only do the create here (and defer requestStart) since startActivityInner might abort. - final Transition newTransition = (!mService.getTransitionController().isCollecting() - && mService.getTransitionController().getTransitionPlayer() != null) - ? mService.getTransitionController().createTransition(TRANSIT_OPEN) : null; + final TransitionController transitionController = r.mTransitionController; + final Transition newTransition = (!transitionController.isCollecting() + && transitionController.getTransitionPlayer() != null) + ? transitionController.createTransition(TRANSIT_OPEN) : null; RemoteTransition remoteTransition = r.takeRemoteTransition(); if (newTransition != null && remoteTransition != null) { newTransition.setRemoteTransition(remoteTransition); } - mService.getTransitionController().collect(r); + transitionController.collect(r); final boolean isTransient = r.getOptions() != null && r.getOptions().getTransientLaunch(); try { mService.deferWindowLayout(); @@ -1618,19 +1619,19 @@ class ActivityStarter { if (started) { // The activity is started new rather than just brought forward, so record // it as an existence change. - mService.getTransitionController().collectExistenceChange(r); + transitionController.collectExistenceChange(r); } if (isTransient) { // `r` isn't guaranteed to be the actual relevant activity, so we must wait // until after we launched to identify the relevant activity. - mService.getTransitionController().setTransientLaunch(mLastStartActivityRecord); + transitionController.setTransientLaunch(mLastStartActivityRecord); } if (newTransition != null) { - mService.getTransitionController().requestStartTransition(newTransition, + transitionController.requestStartTransition(newTransition, mTargetTask, remoteTransition); } else if (started) { // Make the collecting transition wait until this request is ready. - mService.getTransitionController().setReady(r, false); + transitionController.setReady(r, false); } } } @@ -2769,7 +2770,7 @@ class ActivityStarter { mNewTaskInfo != null ? mNewTaskInfo : mStartActivity.info, mNewTaskIntent != null ? mNewTaskIntent : mIntent, mVoiceSession, mVoiceInteractor, toTop, mStartActivity, mSourceRecord, mOptions); - mService.getTransitionController().collectExistenceChange(task); + task.mTransitionController.collectExistenceChange(task); addOrReparentStartingActivity(task, "setTaskFromReuseOrCreateNewTask - mReuseTask"); ProtoLog.v(WM_DEBUG_TASKS, "Starting new activity %s in new task %s", diff --git a/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java b/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java index 11936b21bd4e..ba305929d808 100644 --- a/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java +++ b/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java @@ -853,10 +853,6 @@ public class ActivityTaskSupervisor implements RecentTasks.Callbacks { proc.getThread(), r.appToken); final boolean isTransitionForward = r.isTransitionForward(); - IBinder fragmentToken = null; - if (r.getTaskFragment().getTaskFragmentOrganizerPid() == r.getPid()) { - fragmentToken = r.getTaskFragment().getFragmentToken(); - } clientTransaction.addCallback(LaunchActivityItem.obtain(new Intent(r.intent), System.identityHashCode(r), r.info, // TODO: Have this take the merged configuration instead of separate global @@ -868,7 +864,7 @@ public class ActivityTaskSupervisor implements RecentTasks.Callbacks { results, newIntents, r.takeOptions(), isTransitionForward, proc.createProfilerInfoIfNeeded(), r.assistToken, activityClientController, r.createFixedRotationAdjustmentsIfNeeded(), r.shareableActivityToken, - r.getLaunchedFromBubble(), fragmentToken)); + r.getLaunchedFromBubble())); // Set desired final state. final ActivityLifecycleItem lifecycleItem; @@ -1391,7 +1387,7 @@ public class ActivityTaskSupervisor implements RecentTasks.Callbacks { mUserLeaving = true; } - mService.getTransitionController().requestTransitionIfNeeded(TRANSIT_TO_FRONT, + task.mTransitionController.requestTransitionIfNeeded(TRANSIT_TO_FRONT, 0 /* flags */, task, task /* readyGroupRef */, options != null ? options.getRemoteTransition() : null); reason = reason + " findTaskToMoveToFront"; @@ -1567,17 +1563,17 @@ public class ActivityTaskSupervisor implements RecentTasks.Callbacks { return; } if (task.isVisible()) { - if (mService.getTransitionController().isCollecting()) { + if (task.mTransitionController.isCollecting()) { // We don't want the finishing to change the transition ready state since there will // not be corresponding setReady for finishing. - mService.getTransitionController().collectExistenceChange(task); + task.mTransitionController.collectExistenceChange(task); } else { - mService.getTransitionController().requestTransitionIfNeeded(TRANSIT_CLOSE, task); + task.mTransitionController.requestTransitionIfNeeded(TRANSIT_CLOSE, task); } } else { // Removing a non-visible task doesn't require a transition, but if there is one // collecting, this should be a member just in case. - mService.getTransitionController().collect(task); + task.mTransitionController.collect(task); } task.mInRemoveTask = true; try { @@ -1891,7 +1887,7 @@ public class ActivityTaskSupervisor implements RecentTasks.Callbacks { final ActivityRecord s = mStoppingActivities.get(i); final boolean animating = s.isAnimating(TRANSITION | PARENTS, ANIMATION_TYPE_APP_TRANSITION | ANIMATION_TYPE_RECENTS) - || mService.getTransitionController().inTransition(s); + || s.inTransition(); ProtoLog.v(WM_DEBUG_STATES, "Stopping %s: nowVisible=%b animating=%b " + "finishing=%s", s, s.nowVisible, animating, s.finishing); if (!animating || mService.mShuttingDown) { @@ -2192,7 +2188,7 @@ public class ActivityTaskSupervisor implements RecentTasks.Callbacks { } if (!task.supportsSplitScreenWindowingMode() || forceNonResizable) { - if (mService.getTransitionController().getTransitionPlayer() != null) return; + if (task.mTransitionController.isShellTransitionsEnabled()) return; // Dismiss docked root task. If task appeared to be in docked root task but is not // resizable - we need to move it to top of fullscreen root task, otherwise it will // be covered. diff --git a/services/core/java/com/android/server/wm/AppTransition.java b/services/core/java/com/android/server/wm/AppTransition.java index 929ac56f740e..e21a00b4aec6 100644 --- a/services/core/java/com/android/server/wm/AppTransition.java +++ b/services/core/java/com/android/server/wm/AppTransition.java @@ -1547,7 +1547,7 @@ public class AppTransition implements Dump { } boolean prepareAppTransition(@TransitionType int transit, @TransitionFlags int flags) { - if (mService.mAtmService.getTransitionController().getTransitionPlayer() != null) { + if (mDisplayContent.mTransitionController.isShellTransitionsEnabled()) { return false; } mNextAppTransitionRequests.add(transit); diff --git a/services/core/java/com/android/server/wm/AppTransitionController.java b/services/core/java/com/android/server/wm/AppTransitionController.java index 9561de09971b..0e04c426ce1e 100644 --- a/services/core/java/com/android/server/wm/AppTransitionController.java +++ b/services/core/java/com/android/server/wm/AppTransitionController.java @@ -1108,6 +1108,11 @@ public class AppTransitionController { // the same transition. for (int i = rootTasks.size() - 1; i >= 0; i--) { final Task rootTask = rootTasks.valueAt(i); + if (rootTask == null) { + // It is possible that one activity may have been removed from the hierarchy. No + // need to check for this case. + continue; + } final boolean notReady = rootTask.forAllLeafTaskFragments(taskFragment -> { if (!taskFragment.isReadyToTransit()) { ProtoLog.v(WM_DEBUG_APP_TRANSITIONS, "Organized TaskFragment is not ready= %s", diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java index f800f0e395de..1db4c1da2023 100644 --- a/services/core/java/com/android/server/wm/DisplayContent.java +++ b/services/core/java/com/android/server/wm/DisplayContent.java @@ -1064,7 +1064,7 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp mAppTransition = new AppTransition(mWmService.mContext, mWmService, this); mAppTransition.registerListenerLocked(mWmService.mActivityManagerAppTransitionNotifier); - mAtmService.getTransitionController().registerLegacyListener( + mTransitionController.registerLegacyListener( mWmService.mActivityManagerAppTransitionNotifier); mAppTransition.registerListenerLocked(mFixedRotationTransitionListener); mAppTransitionController = new AppTransitionController(mWmService, this); @@ -1395,7 +1395,7 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp if (configChanged) { mWaitingForConfig = true; - if (mAtmService.getTransitionController().isShellTransitionsEnabled()) { + if (mTransitionController.isShellTransitionsEnabled()) { requestChangeTransitionIfNeeded(changes); } else { mWmService.startFreezingDisplay(0 /* exitAnim */, 0 /* enterAnim */, this); @@ -1515,7 +1515,7 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp } else if (currentConfig != null // If waiting for a remote rotation, don't prematurely update configuration. && !(mDisplayRotation.isWaitingForRemoteRotation() - || mAtmService.getTransitionController().isCollecting(this))) { + || mTransitionController.isCollecting(this))) { // No obvious action we need to take, but if our current state mismatches the // activity manager's, update it, disregarding font scale, which should remain set // to the value of the previous configuration. @@ -1914,8 +1914,7 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp */ private void applyRotation(final int oldRotation, final int rotation) { mDisplayRotation.applyCurrentRotation(rotation); - final boolean shellTransitions = - mWmService.mAtmService.getTransitionController().getTransitionPlayer() != null; + final boolean shellTransitions = mTransitionController.getTransitionPlayer() != null; final boolean rotateSeamlessly = mDisplayRotation.isRotatingSeamlessly() && !shellTransitions; final Transaction transaction = @@ -3070,7 +3069,7 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp if (isAnimating(TRANSITION | PARENTS) // isAnimating is a legacy transition query and will be removed, so also add a // check for whether this is in a shell-transition when not using legacy. - || mAtmService.getTransitionController().inTransition()) { + || mTransitionController.inTransition()) { mDeferredRemoval = true; return; } @@ -3176,7 +3175,7 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp * be non-zero. This method is no-op if the display has been collected. */ void requestChangeTransitionIfNeeded(@ActivityInfo.Config int changes) { - final TransitionController controller = mAtmService.getTransitionController(); + final TransitionController controller = mTransitionController; if (controller.isCollecting()) { if (!controller.isCollecting(this)) { controller.collect(this); @@ -3215,8 +3214,8 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp screenRotationAnimation.dumpDebug(proto, SCREEN_ROTATION_ANIMATION); } mDisplayFrames.dumpDebug(proto, DISPLAY_FRAMES); - if (mAtmService.getTransitionController().isShellTransitionsEnabled()) { - mAtmService.getTransitionController().dumpDebugLegacy(proto, APP_TRANSITION); + if (mTransitionController.isShellTransitionsEnabled()) { + mTransitionController.dumpDebugLegacy(proto, APP_TRANSITION); } else { mAppTransition.dumpDebug(proto, APP_TRANSITION); } @@ -5109,7 +5108,7 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp void requestTransitionAndLegacyPrepare(@WindowManager.TransitionType int transit, @WindowManager.TransitionFlags int flags) { prepareAppTransition(transit, flags); - mAtmService.getTransitionController().requestTransitionIfNeeded(transit, flags, + mTransitionController.requestTransitionIfNeeded(transit, flags, null /* trigger */, this); } @@ -5117,12 +5116,12 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp void requestTransitionAndLegacyPrepare(@WindowManager.TransitionType int transit, @Nullable WindowContainer trigger) { prepareAppTransition(transit); - mAtmService.getTransitionController().requestTransitionIfNeeded(transit, 0 /* flags */, + mTransitionController.requestTransitionIfNeeded(transit, 0 /* flags */, trigger, this); } void executeAppTransition() { - mAtmService.getTransitionController().setReady(this); + mTransitionController.setReady(this); if (mAppTransition.isTransitionSet()) { ProtoLog.w(WM_DEBUG_APP_TRANSITIONS, "Execute app transition: %s, displayId: %d Callers=%s", @@ -5171,9 +5170,9 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp /** Check if pending app transition is for activity / task launch. */ boolean isNextTransitionForward() { // TODO(b/191375840): decouple "forwardness" from transition system. - if (mAtmService.getTransitionController().isShellTransitionsEnabled()) { + if (mTransitionController.isShellTransitionsEnabled()) { @WindowManager.TransitionType int type = - mAtmService.getTransitionController().getCollectingTransitionType(); + mTransitionController.getCollectingTransitionType(); return type == TRANSIT_OPEN || type == TRANSIT_TO_FRONT; } return mAppTransition.containsTransitRequest(TRANSIT_OPEN) @@ -5748,7 +5747,7 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp } mWmService.mDisplayNotificationController.dispatchDisplayChanged( this, getConfiguration()); - if (isReady() && mAtmService.getTransitionController().isShellTransitionsEnabled()) { + if (isReady() && mTransitionController.isShellTransitionsEnabled()) { requestChangeTransitionIfNeeded(changes); } } diff --git a/services/core/java/com/android/server/wm/DisplayPolicy.java b/services/core/java/com/android/server/wm/DisplayPolicy.java index aa26d4faf854..ccfb174e3d16 100644 --- a/services/core/java/com/android/server/wm/DisplayPolicy.java +++ b/services/core/java/com/android/server/wm/DisplayPolicy.java @@ -641,8 +641,7 @@ public class DisplayPolicy { } }; displayContent.mAppTransition.registerListenerLocked(mAppTransitionListener); - mService.mAtmService.getTransitionController().registerLegacyListener( - mAppTransitionListener); + displayContent.mTransitionController.registerLegacyListener(mAppTransitionListener); mImmersiveModeConfirmation = new ImmersiveModeConfirmation(mContext, looper, mService.mVrModeEnabled); diff --git a/services/core/java/com/android/server/wm/DisplayRotation.java b/services/core/java/com/android/server/wm/DisplayRotation.java index 225a6ea20f3d..34e81498b1c3 100644 --- a/services/core/java/com/android/server/wm/DisplayRotation.java +++ b/services/core/java/com/android/server/wm/DisplayRotation.java @@ -413,7 +413,7 @@ public class DisplayRotation { */ boolean updateRotationUnchecked(boolean forceUpdate) { final boolean useShellTransitions = - mService.mAtmService.getTransitionController().getTransitionPlayer() != null; + mDisplayContent.mTransitionController.isShellTransitionsEnabled(); final int displayId = mDisplayContent.getDisplayId(); if (!forceUpdate && !useShellTransitions) { @@ -586,17 +586,17 @@ public class DisplayRotation { mService.mH.removeCallbacks(mDisplayRotationHandlerTimeout); mIsWaitingForRemoteRotation = false; - if (mService.mAtmService.getTransitionController().getTransitionPlayer() != null) { - if (!mService.mAtmService.getTransitionController().isCollecting()) { + if (mDisplayContent.mTransitionController.isShellTransitionsEnabled()) { + if (!mDisplayContent.mTransitionController.isCollecting()) { throw new IllegalStateException("Trying to rotate outside a transition"); } - mService.mAtmService.getTransitionController().collect(mDisplayContent); + mDisplayContent.mTransitionController.collect(mDisplayContent); // Go through all tasks and collect them before the rotation // TODO(shell-transitions): move collect() to onConfigurationChange once wallpaper // handling is synchronized. mDisplayContent.forAllTasks(task -> { if (task.isVisible()) { - mService.mAtmService.getTransitionController().collect(task); + mDisplayContent.mTransitionController.collect(task); } }); mDisplayContent.getInsetsStateController().addProvidersToTransition(); diff --git a/services/core/java/com/android/server/wm/EnsureActivitiesVisibleHelper.java b/services/core/java/com/android/server/wm/EnsureActivitiesVisibleHelper.java index 6560d15ca744..cddb1e7edb3b 100644 --- a/services/core/java/com/android/server/wm/EnsureActivitiesVisibleHelper.java +++ b/services/core/java/com/android/server/wm/EnsureActivitiesVisibleHelper.java @@ -135,7 +135,7 @@ class EnsureActivitiesVisibleHelper { setActivityVisibilityState(child.asActivityRecord(), starting, resumeTopActivity); } } - if (mTaskFragment.mAtmService.getTransitionController().getTransitionPlayer() != null) { + if (mTaskFragment.mTransitionController.isShellTransitionsEnabled()) { mTaskFragment.getDisplayContent().mWallpaperController.adjustWallpaperWindows(); } } diff --git a/services/core/java/com/android/server/wm/InsetsStateController.java b/services/core/java/com/android/server/wm/InsetsStateController.java index 2c4adcbf1404..c4ca8e364011 100644 --- a/services/core/java/com/android/server/wm/InsetsStateController.java +++ b/services/core/java/com/android/server/wm/InsetsStateController.java @@ -254,7 +254,7 @@ class InsetsStateController { if (p == null) continue; final WindowContainer wc = p.mWin; if (wc == null) continue; - mDisplayContent.mAtmService.getTransitionController().collect(wc); + mDisplayContent.mTransitionController.collect(wc); } } diff --git a/services/core/java/com/android/server/wm/PinnedTaskController.java b/services/core/java/com/android/server/wm/PinnedTaskController.java index 6014a87ae8f7..b4963c5b9f1c 100644 --- a/services/core/java/com/android/server/wm/PinnedTaskController.java +++ b/services/core/java/com/android/server/wm/PinnedTaskController.java @@ -211,7 +211,7 @@ class PinnedTaskController { } mFreezingTaskConfig = true; mDestRotatedBounds = new Rect(bounds); - if (!mService.mAtmService.getTransitionController().isShellTransitionsEnabled()) { + if (!mDisplayContent.mTransitionController.isShellTransitionsEnabled()) { continueOrientationChange(); } } diff --git a/services/core/java/com/android/server/wm/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java index 40207882d73c..97ea41c2f228 100644 --- a/services/core/java/com/android/server/wm/RootWindowContainer.java +++ b/services/core/java/com/android/server/wm/RootWindowContainer.java @@ -2211,7 +2211,7 @@ class RootWindowContainer extends WindowContainer<DisplayContent> // display area, so reparent. rootTask.reparent(taskDisplayArea, true /* onTop */); } - mService.getTransitionController().requestTransitionIfNeeded(TRANSIT_PIP, rootTask); + rootTask.mTransitionController.requestTransitionIfNeeded(TRANSIT_PIP, rootTask); // Defer the windowing mode change until after the transition to prevent the activity // from doing work and changing the activity visuals while animating diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java index d89d212bab1f..0819549ad8df 100644 --- a/services/core/java/com/android/server/wm/Task.java +++ b/services/core/java/com/android/server/wm/Task.java @@ -4582,7 +4582,7 @@ class Task extends TaskFragment { // From fullscreen to PiP. if (topActivity != null && currentMode == WINDOWING_MODE_FULLSCREEN && windowingMode == WINDOWING_MODE_PINNED - && !mAtmService.getTransitionController().isShellTransitionsEnabled()) { + && !mTransitionController.isShellTransitionsEnabled()) { mDisplayContent.mPinnedTaskController .deferOrientationChangeForEnteringPipFromFullScreenIfNeeded(); } diff --git a/services/core/java/com/android/server/wm/TaskFragment.java b/services/core/java/com/android/server/wm/TaskFragment.java index 2b5a8203d33b..44b22c643347 100644 --- a/services/core/java/com/android/server/wm/TaskFragment.java +++ b/services/core/java/com/android/server/wm/TaskFragment.java @@ -1394,7 +1394,8 @@ class TaskFragment extends WindowContainer<WindowContainer> { ProtoLog.v(WM_DEBUG_STATES, "Moving to PAUSING: %s", prev); mPausingActivity = prev; mLastPausedActivity = prev; - if (prev.isNoHistory() && !mTaskSupervisor.mNoHistoryActivities.contains(prev)) { + if (!prev.finishing && prev.isNoHistory() + && !mTaskSupervisor.mNoHistoryActivities.contains(prev)) { mTaskSupervisor.mNoHistoryActivities.add(prev); } prev.setState(PAUSING, "startPausingLocked"); @@ -1478,7 +1479,7 @@ class TaskFragment extends WindowContainer<WindowContainer> { } else { prev.schedulePauseTimeout(); // Unset readiness since we now need to wait until this pause is complete. - mAtmService.getTransitionController().setReady(this, false /* ready */); + mTransitionController.setReady(this, false /* ready */); return true; } @@ -2151,10 +2152,6 @@ class TaskFragment extends WindowContainer<WindowContainer> { } } - int getTaskFragmentOrganizerPid() { - return mTaskFragmentOrganizerPid; - } - /** * Returns a {@link TaskFragmentInfo} with information from this TaskFragment. Should not be * called from {@link Task}. @@ -2286,7 +2283,7 @@ class TaskFragment extends WindowContainer<WindowContainer> { return false; } return isAnimating(TRANSITION | CHILDREN, WindowState.EXIT_ANIMATING_TYPES) - || mAtmService.getTransitionController().inTransition(this); + || inTransition(); } @Override diff --git a/services/core/java/com/android/server/wm/Transition.java b/services/core/java/com/android/server/wm/Transition.java index 1909875565f6..e50e8ef56778 100644 --- a/services/core/java/com/android/server/wm/Transition.java +++ b/services/core/java/com/android/server/wm/Transition.java @@ -758,8 +758,13 @@ class Transition extends Binder implements BLASTSyncEngine.TransactionReadyListe (flags & TRANSIT_FLAG_KEYGUARD_GOING_AWAY_WITH_WALLPAPER) != 0, (flags & TRANSIT_FLAG_KEYGUARD_GOING_AWAY_TO_SHADE) != 0, (flags & TRANSIT_FLAG_KEYGUARD_GOING_AWAY_SUBTLE_ANIMATION) != 0); - mController.mAtm.mWindowManager.mPolicy.startKeyguardExitAnimation( - SystemClock.uptimeMillis(), 0 /* duration */); + if (!WindowManagerService.sEnableRemoteKeyguardGoingAwayAnimation) { + // When remote animation is enabled for KEYGUARD_GOING_AWAY transition, SysUI + // receives IRemoteAnimationRunner#onAnimationStart to start animation, so we don't + // need to call IKeyguardService#keyguardGoingAway here. + mController.mAtm.mWindowManager.mPolicy.startKeyguardExitAnimation( + SystemClock.uptimeMillis(), 0 /* duration */); + } } if ((flags & TRANSIT_FLAG_KEYGUARD_LOCKED) != 0) { mController.mAtm.mWindowManager.mPolicy.applyKeyguardOcclusionChange(); diff --git a/services/core/java/com/android/server/wm/WallpaperController.java b/services/core/java/com/android/server/wm/WallpaperController.java index 7893612b5725..0909462585af 100644 --- a/services/core/java/com/android/server/wm/WallpaperController.java +++ b/services/core/java/com/android/server/wm/WallpaperController.java @@ -128,7 +128,7 @@ class WallpaperController { } mFindResults.resetTopWallpaper = true; - if (mService.mAtmService.getTransitionController().getTransitionPlayer() == null) { + if (!w.mTransitionController.isShellTransitionsEnabled()) { if (w.mActivityRecord != null && !w.mActivityRecord.isVisible() && !w.mActivityRecord.isAnimating(TRANSITION | PARENTS)) { // If this window's app token is hidden and not animating, it is of no interest. diff --git a/services/core/java/com/android/server/wm/WallpaperWindowToken.java b/services/core/java/com/android/server/wm/WallpaperWindowToken.java index b54e8b7a7b4e..75c84c44c48e 100644 --- a/services/core/java/com/android/server/wm/WallpaperWindowToken.java +++ b/services/core/java/com/android/server/wm/WallpaperWindowToken.java @@ -112,7 +112,7 @@ class WallpaperWindowToken extends WindowToken { setVisibility(visible); } final WallpaperController wallpaperController = mDisplayContent.mWallpaperController; - if (mWmService.mAtmService.getTransitionController().getTransitionPlayer() != null) { + if (mTransitionController.isShellTransitionsEnabled()) { return; } @@ -157,12 +157,12 @@ class WallpaperWindowToken extends WindowToken { */ void setVisibility(boolean visible) { // Before setting mVisibleRequested so we can track changes. - mWmService.mAtmService.getTransitionController().collect(this); + mTransitionController.collect(this); setVisibleRequested(visible); // If in a transition, defer commits for activities that are going invisible - if (!visible && (mWmService.mAtmService.getTransitionController().inTransition() + if (!visible && (mTransitionController.inTransition() || getDisplayContent().mAppTransition.isRunning())) { return; } diff --git a/services/core/java/com/android/server/wm/WindowContainer.java b/services/core/java/com/android/server/wm/WindowContainer.java index 38e20555f236..841783d6b8cd 100644 --- a/services/core/java/com/android/server/wm/WindowContainer.java +++ b/services/core/java/com/android/server/wm/WindowContainer.java @@ -197,10 +197,10 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer< * Applied as part of the animation pass in "prepareSurfaces". */ protected final SurfaceAnimator mSurfaceAnimator; - private boolean mAnyParentAnimating; final SurfaceFreezer mSurfaceFreezer; protected final WindowManagerService mWmService; + final TransitionController mTransitionController; /** * Sources which triggered a surface animation on this container. An animation target can be @@ -331,6 +331,7 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer< WindowContainer(WindowManagerService wms) { mWmService = wms; + mTransitionController = mWmService.mAtmService.getTransitionController(); mPendingTransaction = wms.mTransactionFactory.get(); mSyncTransaction = wms.mTransactionFactory.get(); mSurfaceAnimator = new SurfaceAnimator(this, this::onAnimationFinished, wms); @@ -1008,7 +1009,7 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer< } boolean inTransition() { - return mWmService.mAtmService.getTransitionController().inTransition(this); + return mTransitionController.inTransition(this); } void sendAppVisibilityToClients() { @@ -2310,7 +2311,7 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer< void assignLayer(Transaction t, int layer) { // Don't assign layers while a transition animation is playing // TODO(b/173528115): establish robust best-practices around z-order fighting. - if (mWmService.mAtmService.getTransitionController().isPlaying()) return; + if (mTransitionController.isPlaying()) return; final boolean changed = layer != mLastLayer || mLastRelativeToLayer != null; if (mSurfaceControl != null && changed) { setLayer(t, layer); diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java index 0e99c0f1bad0..c77cd509ca4f 100644 --- a/services/core/java/com/android/server/wm/WindowManagerService.java +++ b/services/core/java/com/android/server/wm/WindowManagerService.java @@ -1804,7 +1804,7 @@ public class WindowManagerService extends IWindowManager.Stub winAnimator.mEnterAnimationPending = true; winAnimator.mEnteringAnimation = true; // Check if we need to prepare a transition for replacing window first. - if (mAtmService.getTransitionController().getTransitionPlayer() == null + if (!win.mTransitionController.isShellTransitionsEnabled() && activity != null && activity.isVisible() && !prepareWindowReplacementTransition(activity)) { // If not, check if need to set up a dummy transition during display freeze @@ -2569,7 +2569,7 @@ public class WindowManagerService extends IWindowManager.Stub if (win.mAttrs.type == TYPE_APPLICATION_STARTING) { transit = WindowManagerPolicy.TRANSIT_PREVIEW_DONE; } - if (mAtmService.getTransitionController().inTransition(win)) { + if (win.inTransition()) { focusMayChange = true; win.mAnimatingExit = true; } else if (win.isWinVisibleLw() && winAnimator.applyAnimationLocked(transit, false)) { @@ -4051,7 +4051,7 @@ public class WindowManagerService extends IWindowManager.Stub final boolean pendingRemoteRotation = rotationChanged && (displayContent.getDisplayRotation().isWaitingForRemoteRotation() - || mAtmService.getTransitionController().isCollecting()); + || displayContent.mTransitionController.isCollecting()); // Even if alwaysSend, we are waiting for a transition or remote to provide // rotated configuration, so we can't update configuration yet. if (!pendingRemoteRotation) { diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java index 3adecf3474eb..5f9d4f5182bc 100644 --- a/services/core/java/com/android/server/wm/WindowState.java +++ b/services/core/java/com/android/server/wm/WindowState.java @@ -1175,8 +1175,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP if (WindowManager.LayoutParams.isSystemAlertWindowType(mAttrs.type)) { return TouchOcclusionMode.USE_OPACITY; } - if (isAnimating(PARENTS | TRANSITION, ANIMATION_TYPE_ALL) - || mWmService.mAtmService.getTransitionController().inTransition(this)) { + if (isAnimating(PARENTS | TRANSITION, ANIMATION_TYPE_ALL) || inTransition()) { return TouchOcclusionMode.USE_OPACITY; } return TouchOcclusionMode.BLOCK_UNTRUSTED; @@ -2326,7 +2325,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP && (mWindowFrames.mRelFrame.top != mWindowFrames.mLastRelFrame.top || mWindowFrames.mRelFrame.left != mWindowFrames.mLastRelFrame.left) && (!mIsChildWindow || !getParentWindow().hasMoved()) - && !mWmService.mAtmService.getTransitionController().isCollecting(); + && !mTransitionController.isCollecting(); } boolean isObscuringDisplay() { @@ -2712,8 +2711,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP // Don't allow transient-launch activities to take IME. if (rootTask != null && mActivityRecord != null - && mWmService.mAtmService.getTransitionController().isTransientLaunch( - mActivityRecord)) { + && mTransitionController.isTransientLaunch(mActivityRecord)) { return false; } @@ -5679,9 +5677,11 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP } boolean needsRelativeLayeringToIme() { - // We only use the relative layering mode in split screen, as part of elevating the IME - // and windows above it's target above the docked divider. - if (!inSplitScreenWindowingMode()) { + // We use the relative layering when IME isn't attached to the app. Such as part of + // elevating the IME and windows above it's target above the docked divider in + // split-screen, or make the popupMenu to be above the IME when the parent window is the + // IME layering target in bubble/freeform mode. + if (mDisplayContent.isImeAttachedToApp()) { return false; } @@ -6073,7 +6073,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP } if (mActivityRecord != null - && mWmService.mAtmService.getTransitionController().isShellTransitionsEnabled() + && mTransitionController.isShellTransitionsEnabled() && mAttrs.type == TYPE_APPLICATION_STARTING) { mWmService.mAtmService.mTaskSupervisor.getActivityMetricsLogger() .notifyStartingWindowDrawn(mActivityRecord); diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java index 02497a4de250..14970ceb13d2 100644 --- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java +++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java @@ -10632,19 +10632,21 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { } } final String adminPkg = admin.getPackageName(); - try { - // Install the profile owner if not present. - if (!mIPackageManager.isPackageAvailable(adminPkg, userId)) { - mIPackageManager.installExistingPackageAsUser(adminPkg, userId, - PackageManager.INSTALL_ALL_WHITELIST_RESTRICTED_PERMISSIONS, - PackageManager.INSTALL_REASON_POLICY, - /* allowlistedRestrictedPermissions= */ null); + mInjector.binderWithCleanCallingIdentity(() -> { + try { + // Install the profile owner if not present. + if (!mIPackageManager.isPackageAvailable(adminPkg, userId)) { + mIPackageManager.installExistingPackageAsUser(adminPkg, userId, + PackageManager.INSTALL_ALL_WHITELIST_RESTRICTED_PERMISSIONS, + PackageManager.INSTALL_REASON_POLICY, + /* allowlistedRestrictedPermissions= */ null); + } + } catch (RemoteException e) { + // Does not happen, same process + Slogf.wtf(LOG_TAG, e, "Failed to install admin package %s for user %d", + adminPkg, userId); } - } catch (RemoteException e) { - // Does not happen, same process - Slogf.wtf(LOG_TAG, e, "Failed to install admin package %s for user %d", - adminPkg, userId); - } + }); // Set admin. setActiveAdmin(profileOwner, /* refreshing= */ true, userId); diff --git a/services/tests/servicestests/src/com/android/server/display/DisplayModeDirectorTest.java b/services/tests/servicestests/src/com/android/server/display/DisplayModeDirectorTest.java index 0dd5c61121db..418831f47c1a 100644 --- a/services/tests/servicestests/src/com/android/server/display/DisplayModeDirectorTest.java +++ b/services/tests/servicestests/src/com/android/server/display/DisplayModeDirectorTest.java @@ -26,6 +26,7 @@ import static android.hardware.display.DisplayManager.DeviceConfig.KEY_REFRESH_R import static android.hardware.display.DisplayManager.DeviceConfig.KEY_REFRESH_RATE_IN_LOW_ZONE; import static com.android.server.display.DisplayModeDirector.Vote.INVALID_SIZE; +import static com.android.server.display.HighBrightnessModeController.HBM_TRANSITION_POINT_INVALID; import static com.google.common.truth.Truth.assertThat; @@ -74,6 +75,7 @@ import androidx.test.core.app.ApplicationProvider; import androidx.test.filters.SmallTest; import androidx.test.runner.AndroidJUnit4; +import com.android.internal.display.BrightnessSynchronizer; import com.android.internal.util.Preconditions; import com.android.internal.util.test.FakeSettingsProvider; import com.android.internal.util.test.FakeSettingsProviderRule; @@ -110,6 +112,7 @@ public class DisplayModeDirectorTest { private static final boolean DEBUG = false; private static final float FLOAT_TOLERANCE = 0.01f; private static final int DISPLAY_ID = 0; + private static final float TRANSITION_POINT = 0.763f; private Context mContext; private FakesInjector mInjector; @@ -751,19 +754,27 @@ public class DisplayModeDirectorTest { director.start(sensorManager); - ArgumentCaptor<SensorEventListener> listenerCaptor = + ArgumentCaptor<DisplayListener> displayListenerCaptor = + ArgumentCaptor.forClass(DisplayListener.class); + verify(mInjector).registerDisplayListener(displayListenerCaptor.capture(), + any(Handler.class), + eq(DisplayManager.EVENT_FLAG_DISPLAY_CHANGED + | DisplayManager.EVENT_FLAG_DISPLAY_BRIGHTNESS)); + DisplayListener displayListener = displayListenerCaptor.getValue(); + + ArgumentCaptor<SensorEventListener> sensorListenerCaptor = ArgumentCaptor.forClass(SensorEventListener.class); Mockito.verify(sensorManager, Mockito.timeout(TimeUnit.SECONDS.toMillis(1))) .registerListener( - listenerCaptor.capture(), + sensorListenerCaptor.capture(), eq(lightSensor), anyInt(), any(Handler.class)); - SensorEventListener listener = listenerCaptor.getValue(); + SensorEventListener sensorListener = sensorListenerCaptor.getValue(); - setBrightness(10); + setBrightness(10, 10, displayListener); // Sensor reads 20 lux, - listener.onSensorChanged(TestUtils.createSensorEvent(lightSensor, 20 /*lux*/)); + sensorListener.onSensorChanged(TestUtils.createSensorEvent(lightSensor, 20 /*lux*/)); Vote vote = director.getVote(Display.DEFAULT_DISPLAY, Vote.PRIORITY_FLICKER_REFRESH_RATE); assertVoteForRefreshRate(vote, 90 /*fps*/); @@ -771,9 +782,11 @@ public class DisplayModeDirectorTest { assertThat(vote).isNotNull(); assertThat(vote.disableRefreshRateSwitching).isTrue(); - setBrightness(125); + // We expect DisplayModeDirector to act on BrightnessInfo.adjustedBrightness; set only this + // parameter to the necessary threshold + setBrightness(10, 125, displayListener); // Sensor reads 1000 lux, - listener.onSensorChanged(TestUtils.createSensorEvent(lightSensor, 1000 /*lux*/)); + sensorListener.onSensorChanged(TestUtils.createSensorEvent(lightSensor, 1000 /*lux*/)); vote = director.getVote(Display.DEFAULT_DISPLAY, Vote.PRIORITY_FLICKER_REFRESH_RATE); assertThat(vote).isNull(); @@ -799,6 +812,14 @@ public class DisplayModeDirectorTest { director.start(sensorManager); + ArgumentCaptor<DisplayListener> displayListenerCaptor = + ArgumentCaptor.forClass(DisplayListener.class); + verify(mInjector).registerDisplayListener(displayListenerCaptor.capture(), + any(Handler.class), + eq(DisplayManager.EVENT_FLAG_DISPLAY_CHANGED + | DisplayManager.EVENT_FLAG_DISPLAY_BRIGHTNESS)); + DisplayListener displayListener = displayListenerCaptor.getValue(); + ArgumentCaptor<SensorEventListener> listenerCaptor = ArgumentCaptor.forClass(SensorEventListener.class); verify(sensorManager, Mockito.timeout(TimeUnit.SECONDS.toMillis(1))) @@ -807,20 +828,22 @@ public class DisplayModeDirectorTest { eq(lightSensor), anyInt(), any(Handler.class)); - SensorEventListener listener = listenerCaptor.getValue(); + SensorEventListener sensorListener = listenerCaptor.getValue(); - setBrightness(100); + setBrightness(100, 100, displayListener); // Sensor reads 2000 lux, - listener.onSensorChanged(TestUtils.createSensorEvent(lightSensor, 2000)); + sensorListener.onSensorChanged(TestUtils.createSensorEvent(lightSensor, 2000)); Vote vote = director.getVote(Display.DEFAULT_DISPLAY, Vote.PRIORITY_FLICKER_REFRESH_RATE); assertThat(vote).isNull(); vote = director.getVote(Display.DEFAULT_DISPLAY, Vote.PRIORITY_FLICKER_REFRESH_RATE_SWITCH); assertThat(vote).isNull(); - setBrightness(255); + // We expect DisplayModeDirector to act on BrightnessInfo.adjustedBrightness; set only this + // parameter to the necessary threshold + setBrightness(100, 255, displayListener); // Sensor reads 9000 lux, - listener.onSensorChanged(TestUtils.createSensorEvent(lightSensor, 9000)); + sensorListener.onSensorChanged(TestUtils.createSensorEvent(lightSensor, 9000)); vote = director.getVote(Display.DEFAULT_DISPLAY, Vote.PRIORITY_FLICKER_REFRESH_RATE); assertVoteForRefreshRate(vote, 60 /*fps*/); @@ -1435,16 +1458,58 @@ public class DisplayModeDirectorTest { Vote vote = director.getVote(DISPLAY_ID, Vote.PRIORITY_HIGH_BRIGHTNESS_MODE); assertNull(vote); - // Turn on HBM + // Turn on HBM, with brightness in the HBM range when(mInjector.getBrightnessInfo(DISPLAY_ID)).thenReturn( - new BrightnessInfo(0.45f, 0.0f, 1.0f, BrightnessInfo.HIGH_BRIGHTNESS_MODE_HDR)); + new BrightnessInfo(TRANSITION_POINT + FLOAT_TOLERANCE, 0.0f, 1.0f, + BrightnessInfo.HIGH_BRIGHTNESS_MODE_HDR, TRANSITION_POINT)); listener.onDisplayChanged(DISPLAY_ID); vote = director.getVote(DISPLAY_ID, Vote.PRIORITY_HIGH_BRIGHTNESS_MODE); assertVoteForRefreshRate(vote, hbmRefreshRate); + // Turn on HBM, with brightness below the HBM range + when(mInjector.getBrightnessInfo(DISPLAY_ID)).thenReturn( + new BrightnessInfo(TRANSITION_POINT - FLOAT_TOLERANCE, 0.0f, 1.0f, + BrightnessInfo.HIGH_BRIGHTNESS_MODE_HDR, TRANSITION_POINT)); + listener.onDisplayChanged(DISPLAY_ID); + vote = director.getVote(DISPLAY_ID, Vote.PRIORITY_HIGH_BRIGHTNESS_MODE); + assertNull(vote); + // Turn off HBM when(mInjector.getBrightnessInfo(DISPLAY_ID)).thenReturn( - new BrightnessInfo(0.45f, 0.0f, 1.0f, BrightnessInfo.HIGH_BRIGHTNESS_MODE_OFF)); + new BrightnessInfo(0.45f, 0.0f, 1.0f, BrightnessInfo.HIGH_BRIGHTNESS_MODE_OFF, + TRANSITION_POINT)); + listener.onDisplayChanged(DISPLAY_ID); + vote = director.getVote(DISPLAY_ID, Vote.PRIORITY_HIGH_BRIGHTNESS_MODE); + assertNull(vote); + + // Turn on HBM, with brightness in the HBM range + when(mInjector.getBrightnessInfo(DISPLAY_ID)).thenReturn( + new BrightnessInfo(TRANSITION_POINT + FLOAT_TOLERANCE, 0.0f, 1.0f, + BrightnessInfo.HIGH_BRIGHTNESS_MODE_HDR, TRANSITION_POINT)); + listener.onDisplayChanged(DISPLAY_ID); + vote = director.getVote(DISPLAY_ID, Vote.PRIORITY_HIGH_BRIGHTNESS_MODE); + assertVoteForRefreshRate(vote, hbmRefreshRate); + + // Turn off HBM + when(mInjector.getBrightnessInfo(DISPLAY_ID)).thenReturn( + new BrightnessInfo(0.45f, 0.0f, 1.0f, BrightnessInfo.HIGH_BRIGHTNESS_MODE_OFF, + TRANSITION_POINT)); + listener.onDisplayChanged(DISPLAY_ID); + vote = director.getVote(DISPLAY_ID, Vote.PRIORITY_HIGH_BRIGHTNESS_MODE); + assertNull(vote); + + // Turn on HBM, with brightness below the HBM range + when(mInjector.getBrightnessInfo(DISPLAY_ID)).thenReturn( + new BrightnessInfo(TRANSITION_POINT - FLOAT_TOLERANCE, 0.0f, 1.0f, + BrightnessInfo.HIGH_BRIGHTNESS_MODE_HDR, TRANSITION_POINT)); + listener.onDisplayChanged(DISPLAY_ID); + vote = director.getVote(DISPLAY_ID, Vote.PRIORITY_HIGH_BRIGHTNESS_MODE); + assertNull(vote); + + // Turn off HBM + when(mInjector.getBrightnessInfo(DISPLAY_ID)).thenReturn( + new BrightnessInfo(0.45f, 0.0f, 1.0f, BrightnessInfo.HIGH_BRIGHTNESS_MODE_OFF, + TRANSITION_POINT)); listener.onDisplayChanged(DISPLAY_ID); vote = director.getVote(DISPLAY_ID, Vote.PRIORITY_HIGH_BRIGHTNESS_MODE); assertNull(vote); @@ -1514,7 +1579,8 @@ public class DisplayModeDirectorTest { // Turn on HBM when(mInjector.getBrightnessInfo(DISPLAY_ID)).thenReturn( - new BrightnessInfo(1.0f, 0.0f, 1.0f, BrightnessInfo.HIGH_BRIGHTNESS_MODE_SUNLIGHT)); + new BrightnessInfo(1.0f, 0.0f, 1.0f, BrightnessInfo.HIGH_BRIGHTNESS_MODE_SUNLIGHT, + TRANSITION_POINT)); listener.onDisplayChanged(DISPLAY_ID); vote = director.getVote(DISPLAY_ID, Vote.PRIORITY_HIGH_BRIGHTNESS_MODE); assertVoteForRefreshRate(vote, initialRefreshRate); @@ -1531,14 +1597,16 @@ public class DisplayModeDirectorTest { // Turn off HBM when(mInjector.getBrightnessInfo(DISPLAY_ID)).thenReturn( - new BrightnessInfo(0.43f, 0.1f, 0.8f, BrightnessInfo.HIGH_BRIGHTNESS_MODE_OFF)); + new BrightnessInfo(0.43f, 0.1f, 0.8f, BrightnessInfo.HIGH_BRIGHTNESS_MODE_OFF, + TRANSITION_POINT)); listener.onDisplayChanged(DISPLAY_ID); vote = director.getVote(DISPLAY_ID, Vote.PRIORITY_HIGH_BRIGHTNESS_MODE); assertNull(vote); // Turn HBM on again and ensure the updated vote value stuck when(mInjector.getBrightnessInfo(DISPLAY_ID)).thenReturn( - new BrightnessInfo(1.0f, 0.0f, 1.0f, BrightnessInfo.HIGH_BRIGHTNESS_MODE_SUNLIGHT)); + new BrightnessInfo(1.0f, 0.0f, 1.0f, BrightnessInfo.HIGH_BRIGHTNESS_MODE_SUNLIGHT, + TRANSITION_POINT)); listener.onDisplayChanged(DISPLAY_ID); vote = director.getVote(DISPLAY_ID, Vote.PRIORITY_HIGH_BRIGHTNESS_MODE); assertVoteForRefreshRate(vote, updatedRefreshRate); @@ -1553,7 +1621,8 @@ public class DisplayModeDirectorTest { // Turn off HBM when(mInjector.getBrightnessInfo(DISPLAY_ID)).thenReturn( - new BrightnessInfo(0.43f, 0.1f, 0.8f, BrightnessInfo.HIGH_BRIGHTNESS_MODE_OFF)); + new BrightnessInfo(0.43f, 0.1f, 0.8f, BrightnessInfo.HIGH_BRIGHTNESS_MODE_OFF, + TRANSITION_POINT)); listener.onDisplayChanged(DISPLAY_ID); vote = director.getVote(DISPLAY_ID, Vote.PRIORITY_HIGH_BRIGHTNESS_MODE); assertNull(vote); @@ -1584,14 +1653,82 @@ public class DisplayModeDirectorTest { // Turn on HBM when(mInjector.getBrightnessInfo(DISPLAY_ID)).thenReturn( - new BrightnessInfo(1.0f, 0.0f, 1.0f, BrightnessInfo.HIGH_BRIGHTNESS_MODE_SUNLIGHT)); + new BrightnessInfo(1.0f, 0.0f, 1.0f, BrightnessInfo.HIGH_BRIGHTNESS_MODE_SUNLIGHT, + TRANSITION_POINT)); + listener.onDisplayChanged(DISPLAY_ID); + vote = director.getVote(DISPLAY_ID, Vote.PRIORITY_HIGH_BRIGHTNESS_MODE); + assertNull(vote); + + // Turn off HBM + when(mInjector.getBrightnessInfo(DISPLAY_ID)).thenReturn( + new BrightnessInfo(0.43f, 0.1f, 0.8f, BrightnessInfo.HIGH_BRIGHTNESS_MODE_OFF, + TRANSITION_POINT)); + listener.onDisplayChanged(DISPLAY_ID); + vote = director.getVote(DISPLAY_ID, Vote.PRIORITY_HIGH_BRIGHTNESS_MODE); + assertNull(vote); + } + + @Test + public void testHbmVoting_HbmUnsupported() { + DisplayModeDirector director = + createDirectorFromRefreshRateArray(new float[] {60.0f, 90.0f}, 0); + director.start(createMockSensorManager()); + + ArgumentCaptor<DisplayListener> captor = + ArgumentCaptor.forClass(DisplayListener.class); + verify(mInjector).registerDisplayListener(captor.capture(), any(Handler.class), + eq(DisplayManager.EVENT_FLAG_DISPLAY_BRIGHTNESS + | DisplayManager.EVENT_FLAG_DISPLAY_REMOVED)); + DisplayListener listener = captor.getValue(); + + // Specify Limitation + when(mDisplayManagerInternalMock.getRefreshRateLimitations(DISPLAY_ID)).thenReturn( + List.of(new RefreshRateLimitation( + DisplayManagerInternal.REFRESH_RATE_LIMIT_HIGH_BRIGHTNESS_MODE, + 60.0f, 60.0f))); + + // Verify that there is no HBM vote initially + Vote vote = director.getVote(DISPLAY_ID, Vote.PRIORITY_HIGH_BRIGHTNESS_MODE); + assertNull(vote); + + // Turn on HBM when HBM is supported; expect a valid transition point and a vote. + when(mInjector.getBrightnessInfo(DISPLAY_ID)).thenReturn( + new BrightnessInfo(1.0f, 0.0f, 1.0f, BrightnessInfo.HIGH_BRIGHTNESS_MODE_SUNLIGHT, + TRANSITION_POINT)); + listener.onDisplayChanged(DISPLAY_ID); + vote = director.getVote(DISPLAY_ID, Vote.PRIORITY_HIGH_BRIGHTNESS_MODE); + assertVoteForRefreshRate(vote, 60.0f); + + // Turn off HBM + when(mInjector.getBrightnessInfo(DISPLAY_ID)).thenReturn( + new BrightnessInfo(1.0f, 0.0f, 1.0f, BrightnessInfo.HIGH_BRIGHTNESS_MODE_OFF, + TRANSITION_POINT)); + listener.onDisplayChanged(DISPLAY_ID); + vote = director.getVote(DISPLAY_ID, Vote.PRIORITY_HIGH_BRIGHTNESS_MODE); + assertNull(vote); + + // Turn on Sunlight HBM when HBM is unsupported; expect an invalid transition point and + // no vote. + when(mInjector.getBrightnessInfo(DISPLAY_ID)).thenReturn( + new BrightnessInfo(1.0f, 0.0f, 1.0f, BrightnessInfo.HIGH_BRIGHTNESS_MODE_SUNLIGHT, + HBM_TRANSITION_POINT_INVALID)); + listener.onDisplayChanged(DISPLAY_ID); + vote = director.getVote(DISPLAY_ID, Vote.PRIORITY_HIGH_BRIGHTNESS_MODE); + assertNull(vote); + + // Turn on HDR HBM when HBM is unsupported; expect an invalid transition point and + // no vote. + when(mInjector.getBrightnessInfo(DISPLAY_ID)).thenReturn( + new BrightnessInfo(1.0f, 0.0f, 1.0f, BrightnessInfo.HIGH_BRIGHTNESS_MODE_HDR, + HBM_TRANSITION_POINT_INVALID)); listener.onDisplayChanged(DISPLAY_ID); vote = director.getVote(DISPLAY_ID, Vote.PRIORITY_HIGH_BRIGHTNESS_MODE); assertNull(vote); // Turn off HBM when(mInjector.getBrightnessInfo(DISPLAY_ID)).thenReturn( - new BrightnessInfo(0.43f, 0.1f, 0.8f, BrightnessInfo.HIGH_BRIGHTNESS_MODE_OFF)); + new BrightnessInfo(1.0f, 0.0f, 1.0f, BrightnessInfo.HIGH_BRIGHTNESS_MODE_OFF, + TRANSITION_POINT)); listener.onDisplayChanged(DISPLAY_ID); vote = director.getVote(DISPLAY_ID, Vote.PRIORITY_HIGH_BRIGHTNESS_MODE); assertNull(vote); @@ -1600,7 +1737,7 @@ public class DisplayModeDirectorTest { private void setHbmAndAssertRefreshRate( DisplayModeDirector director, DisplayListener listener, int mode, float rr) { when(mInjector.getBrightnessInfo(DISPLAY_ID)) - .thenReturn(new BrightnessInfo(1.0f, 0.0f, 1.0f, mode)); + .thenReturn(new BrightnessInfo(1.0f, 0.0f, 1.0f, mode, TRANSITION_POINT)); listener.onDisplayChanged(DISPLAY_ID); final Vote vote = director.getVote(DISPLAY_ID, Vote.PRIORITY_HIGH_BRIGHTNESS_MODE); @@ -1679,7 +1816,8 @@ public class DisplayModeDirectorTest { // Turn on HBM when(mInjector.getBrightnessInfo(DISPLAY_ID)).thenReturn( - new BrightnessInfo(1.0f, 0.0f, 1.0f, BrightnessInfo.HIGH_BRIGHTNESS_MODE_SUNLIGHT)); + new BrightnessInfo(1.0f, 0.0f, 1.0f, BrightnessInfo.HIGH_BRIGHTNESS_MODE_SUNLIGHT, + TRANSITION_POINT)); listener.onDisplayChanged(DISPLAY_ID); vote = director.getVote(DISPLAY_ID, Vote.PRIORITY_HIGH_BRIGHTNESS_MODE); assertVoteForRefreshRate(vote, 60.f); @@ -1834,11 +1972,14 @@ public class DisplayModeDirectorTest { } } - private void setBrightness(int brightness) { - Settings.System.putInt(mContext.getContentResolver(), Settings.System.SCREEN_BRIGHTNESS, - brightness); - mInjector.notifyBrightnessChanged(); - waitForIdleSync(); + private void setBrightness(int brightness, int adjustedBrightness, DisplayListener listener) { + float floatBri = BrightnessSynchronizer.brightnessIntToFloat(brightness); + float floatAdjBri = BrightnessSynchronizer.brightnessIntToFloat(adjustedBrightness); + + when(mInjector.getBrightnessInfo(DISPLAY_ID)).thenReturn( + new BrightnessInfo(floatBri, floatAdjBri, 0.0f, 1.0f, + BrightnessInfo.HIGH_BRIGHTNESS_MODE_OFF, TRANSITION_POINT)); + listener.onDisplayChanged(DISPLAY_ID); } private void setPeakRefreshRate(float fps) { @@ -1902,27 +2043,6 @@ public class DisplayModeDirectorTest { } @Override - public void registerBrightnessObserver(@NonNull ContentResolver cr, - @NonNull ContentObserver observer) { - if (mBrightnessObserver != null) { - throw new IllegalStateException("Tried to register a second brightness observer"); - } - mBrightnessObserver = observer; - } - - @Override - public void unregisterBrightnessObserver(@NonNull ContentResolver cr, - @NonNull ContentObserver observer) { - mBrightnessObserver = null; - } - - void notifyBrightnessChanged() { - if (mBrightnessObserver != null) { - mBrightnessObserver.dispatchChange(false /*selfChange*/, DISPLAY_BRIGHTNESS_URI); - } - } - - @Override public void registerPeakRefreshRateObserver(@NonNull ContentResolver cr, @NonNull ContentObserver observer) { mPeakRefreshRateObserver = observer; diff --git a/services/tests/servicestests/src/com/android/server/display/HighBrightnessModeControllerTest.java b/services/tests/servicestests/src/com/android/server/display/HighBrightnessModeControllerTest.java index cc3591c89560..aca863299b33 100644 --- a/services/tests/servicestests/src/com/android/server/display/HighBrightnessModeControllerTest.java +++ b/services/tests/servicestests/src/com/android/server/display/HighBrightnessModeControllerTest.java @@ -20,6 +20,8 @@ import static android.hardware.display.BrightnessInfo.HIGH_BRIGHTNESS_MODE_HDR; import static android.hardware.display.BrightnessInfo.HIGH_BRIGHTNESS_MODE_OFF; import static android.hardware.display.BrightnessInfo.HIGH_BRIGHTNESS_MODE_SUNLIGHT; +import static com.android.server.display.HighBrightnessModeController.HBM_TRANSITION_POINT_INVALID; + import static org.junit.Assert.assertEquals; import static org.mockito.Mockito.eq; import static org.mockito.Mockito.spy; @@ -124,6 +126,7 @@ public class HighBrightnessModeControllerTest { mInjectorMock, mHandler, DISPLAY_WIDTH, DISPLAY_HEIGHT, mDisplayToken, DEFAULT_MIN, DEFAULT_MAX, null, () -> {}, mContextSpy); assertState(hbmc, DEFAULT_MIN, DEFAULT_MAX, HIGH_BRIGHTNESS_MODE_OFF); + assertEquals(hbmc.getTransitionPoint(), HBM_TRANSITION_POINT_INVALID, 0.0f); } @Test @@ -135,6 +138,7 @@ public class HighBrightnessModeControllerTest { hbmc.setAutoBrightnessEnabled(true); hbmc.onAmbientLuxChange(MINIMUM_LUX - 1); // below allowed range assertState(hbmc, DEFAULT_MIN, DEFAULT_MAX, HIGH_BRIGHTNESS_MODE_OFF); + assertEquals(hbmc.getTransitionPoint(), HBM_TRANSITION_POINT_INVALID, 0.0f); } @Test diff --git a/services/tests/wmtests/src/com/android/server/wm/AppTransitionControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/AppTransitionControllerTest.java index 5fa76bb2e25b..82140f4d965c 100644 --- a/services/tests/wmtests/src/com/android/server/wm/AppTransitionControllerTest.java +++ b/services/tests/wmtests/src/com/android/server/wm/AppTransitionControllerTest.java @@ -922,6 +922,40 @@ public class AppTransitionControllerTest extends WindowTestsBase { verify(mDisplayContent.mAppTransition).goodToGo(anyInt(), any()); } + @Test + public void testTransitionGoodToGoForTaskFragments_detachedApp() { + final TaskFragmentOrganizer organizer = new TaskFragmentOrganizer(Runnable::run); + final Task task = createTask(mDisplayContent); + final TaskFragment changeTaskFragment = + createTaskFragmentWithEmbeddedActivity(task, organizer); + final TaskFragment emptyTaskFragment = new TaskFragmentBuilder(mAtm) + .setParentTask(task) + .setOrganizer(organizer) + .build(); + changeTaskFragment.getTopMostActivity().allDrawn = true; + // To make sure that having a detached activity won't cause any issue. + final ActivityRecord detachedActivity = createActivityRecord(task); + detachedActivity.removeImmediately(); + assertNull(detachedActivity.getRootTask()); + spyOn(mDisplayContent.mAppTransition); + spyOn(emptyTaskFragment); + + prepareAndTriggerAppTransition( + null /* openingActivity */, detachedActivity, changeTaskFragment); + + // Transition not ready because there is an empty non-finishing TaskFragment. + verify(mDisplayContent.mAppTransition, never()).goodToGo(anyInt(), any()); + + doReturn(true).when(emptyTaskFragment).hasChild(); + emptyTaskFragment.remove(false /* withTransition */, "test"); + + mDisplayContent.mAppTransitionController.handleAppTransitionReady(); + + // Transition ready because the empty (no running activity) TaskFragment is requested to be + // removed. + verify(mDisplayContent.mAppTransition).goodToGo(anyInt(), any()); + } + /** Registers remote animation for the organizer. */ private void setupTaskFragmentRemoteAnimation(TaskFragmentOrganizer organizer, RemoteAnimationAdapter adapter) { diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java index b17ea5e48db5..e6ad68aafaec 100644 --- a/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java @@ -18,6 +18,7 @@ package com.android.server.wm; import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD; import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN; +import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW; import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED; import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY; import static android.view.InsetsState.ITYPE_IME; @@ -835,8 +836,7 @@ public class WindowStateTests extends WindowTestsBase { WindowState sameTokenWindow = createWindow(null, TYPE_BASE_APPLICATION, mAppWindow.mToken, "SameTokenWindow"); mDisplayContent.setImeLayeringTarget(mAppWindow); - sameTokenWindow.mActivityRecord.getRootTask().setWindowingMode( - WINDOWING_MODE_SPLIT_SCREEN_PRIMARY); + sameTokenWindow.mActivityRecord.getRootTask().setWindowingMode(WINDOWING_MODE_MULTI_WINDOW); assertTrue(sameTokenWindow.needsRelativeLayeringToIme()); sameTokenWindow.removeImmediately(); assertFalse(sameTokenWindow.needsRelativeLayeringToIme()); @@ -848,8 +848,7 @@ public class WindowStateTests extends WindowTestsBase { WindowState sameTokenWindow = createWindow(null, TYPE_APPLICATION_STARTING, mAppWindow.mToken, "SameTokenWindow"); mDisplayContent.setImeLayeringTarget(mAppWindow); - sameTokenWindow.mActivityRecord.getRootTask().setWindowingMode( - WINDOWING_MODE_SPLIT_SCREEN_PRIMARY); + sameTokenWindow.mActivityRecord.getRootTask().setWindowingMode(WINDOWING_MODE_MULTI_WINDOW); assertFalse(sameTokenWindow.needsRelativeLayeringToIme()); } diff --git a/services/tests/wmtests/src/com/android/server/wm/ZOrderingTests.java b/services/tests/wmtests/src/com/android/server/wm/ZOrderingTests.java index d967891fdb76..22ea3d5be71b 100644 --- a/services/tests/wmtests/src/com/android/server/wm/ZOrderingTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/ZOrderingTests.java @@ -20,6 +20,7 @@ import static android.app.WindowConfiguration.ACTIVITY_TYPE_ASSISTANT; import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME; import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD; import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN; +import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW; import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED; import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY; import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_SECONDARY; @@ -37,6 +38,7 @@ import static android.view.WindowManager.LayoutParams.TYPE_SYSTEM_OVERLAY; import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn; import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify; import static com.android.server.wm.WindowStateAnimator.PRESERVED_SURFACE_LAYER; import static com.google.common.truth.Truth.assertThat; @@ -493,4 +495,27 @@ public class ZOrderingTests extends WindowTestsBase { assertZOrderGreaterThan(mTransaction, mNavBarWindow.mToken.getSurfaceControl(), mDisplayContent.getImeContainer().getSurfaceControl()); } + + @Test + public void testPopupWindowAndParentIsImeTarget_expectHigherThanIme_inMultiWindow() { + // Simulate the app window is in multi windowing mode and being IME target + mAppWindow.getConfiguration().windowConfiguration.setWindowingMode( + WINDOWING_MODE_MULTI_WINDOW); + mDisplayContent.setImeLayeringTarget(mAppWindow); + mDisplayContent.setImeInputTarget(mAppWindow); + + // Create a popupWindow + assertWindowHigher(mImeWindow, mAppWindow); + final WindowState popupWindow = createWindow(mAppWindow, TYPE_APPLICATION_PANEL, + mDisplayContent, "PopupWindow"); + spyOn(popupWindow); + + mDisplayContent.assignChildLayers(mTransaction); + + // Verify the surface layer of the popupWindow should higher than IME + verify(popupWindow).needsRelativeLayeringToIme(); + assertThat(popupWindow.needsRelativeLayeringToIme()).isTrue(); + assertZOrderGreaterThan(mTransaction, popupWindow.getSurfaceControl(), + mDisplayContent.getImeContainer().getSurfaceControl()); + } } |