diff options
96 files changed, 2711 insertions, 642 deletions
diff --git a/core/java/android/content/pm/ILauncherApps.aidl b/core/java/android/content/pm/ILauncherApps.aidl index 27c9cfcdd05b..aa290404c001 100644 --- a/core/java/android/content/pm/ILauncherApps.aidl +++ b/core/java/android/content/pm/ILauncherApps.aidl @@ -25,6 +25,7 @@ import android.content.pm.ActivityInfo; import android.content.pm.ApplicationInfo; import android.content.pm.IOnAppsChangedListener; import android.content.pm.LauncherApps; +import android.content.pm.ShortcutQueryWrapper; import android.content.pm.IPackageInstallerCallback; import android.content.pm.IShortcutChangeCallback; import android.content.pm.PackageInstaller; @@ -67,9 +68,8 @@ interface ILauncherApps { LauncherApps.AppUsageLimit getAppUsageLimit(String callingPackage, String packageName, in UserHandle user); - ParceledListSlice getShortcuts(String callingPackage, long changedSince, String packageName, - in List shortcutIds, in List<LocusId> locusIds, in ComponentName componentName, - int flags, in UserHandle user); + ParceledListSlice getShortcuts(String callingPackage, in ShortcutQueryWrapper query, + in UserHandle user); void pinShortcuts(String callingPackage, String packageName, in List<String> shortcutIds, in UserHandle user); boolean startShortcut(String callingPackage, String packageName, String featureId, String id, @@ -93,9 +93,8 @@ interface ILauncherApps { in IPackageInstallerCallback callback); ParceledListSlice getAllSessions(String callingPackage); - void registerShortcutChangeCallback(String callingPackage, long changedSince, - String packageName, in List shortcutIds, in List<LocusId> locusIds, - in ComponentName componentName, int flags, in IShortcutChangeCallback callback); + void registerShortcutChangeCallback(String callingPackage, in ShortcutQueryWrapper query, + in IShortcutChangeCallback callback); void unregisterShortcutChangeCallback(String callingPackage, in IShortcutChangeCallback callback); diff --git a/core/java/android/content/pm/LauncherApps.java b/core/java/android/content/pm/LauncherApps.java index 6c161fcb8646..87dc0a17f41c 100644 --- a/core/java/android/content/pm/LauncherApps.java +++ b/core/java/android/content/pm/LauncherApps.java @@ -1046,8 +1046,7 @@ public class LauncherApps { // changed callback, but that only returns shortcuts with the "key" information, so // that won't return disabled message. return maybeUpdateDisabledMessage(mService.getShortcuts(mContext.getPackageName(), - query.mChangedSince, query.mPackage, query.mShortcutIds, query.mLocusIds, - query.mActivity, query.mQueryFlags, user) + new ShortcutQueryWrapper(query), user) .getList()); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); @@ -1826,8 +1825,7 @@ public class LauncherApps { mShortcutChangeCallbacks.put(callback, new Pair<>(executor, proxy)); try { mService.registerShortcutChangeCallback(mContext.getPackageName(), - query.mChangedSince, query.mPackage, query.mShortcutIds, query.mLocusIds, - query.mActivity, query.mQueryFlags, proxy); + new ShortcutQueryWrapper(query), proxy); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } diff --git a/core/java/android/content/pm/ShortcutQueryWrapper.aidl b/core/java/android/content/pm/ShortcutQueryWrapper.aidl new file mode 100644 index 000000000000..d02600acf232 --- /dev/null +++ b/core/java/android/content/pm/ShortcutQueryWrapper.aidl @@ -0,0 +1,20 @@ +/** + * Copyright (c) 2020, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.content.pm; + +parcelable ShortcutQueryWrapper; + diff --git a/core/java/android/content/pm/ShortcutQueryWrapper.java b/core/java/android/content/pm/ShortcutQueryWrapper.java new file mode 100644 index 000000000000..c6134416adbc --- /dev/null +++ b/core/java/android/content/pm/ShortcutQueryWrapper.java @@ -0,0 +1,190 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.content.pm; + +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.content.ComponentName; +import android.content.LocusId; +import android.os.Parcel; +import android.os.Parcelable; + +import com.android.internal.util.DataClass; + +import java.util.ArrayList; +import java.util.List; + +/** + * @hide + */ +@DataClass(genParcelable = true, genToString = true) +public final class ShortcutQueryWrapper extends LauncherApps.ShortcutQuery implements Parcelable { + + public ShortcutQueryWrapper(LauncherApps.ShortcutQuery query) { + this(); + mChangedSince = query.mChangedSince; + mPackage = query.mPackage; + mLocusIds = query.mLocusIds; + mShortcutIds = query.mShortcutIds; + mActivity = query.mActivity; + mQueryFlags = query.mQueryFlags; + } + + public long getChangedSince() { + return mChangedSince; + } + + @Nullable + public String getPackage() { + return mPackage; + } + + @Nullable + public List<LocusId> getLocusIds() { + return mLocusIds; + } + + @Nullable + public List<String> getShortcutIds() { + return mShortcutIds; + } + + @Nullable + public ComponentName getActivity() { + return mActivity; + } + + public int getQueryFlags() { + return mQueryFlags; + } + + // Code below generated by codegen v1.0.14. + // + // DO NOT MODIFY! + // CHECKSTYLE:OFF Generated code + // + // To regenerate run: + // $ codegen $ANDROID_BUILD_TOP/frameworks/base/core/java/android/content/pm/ShortcutQueryWrapper.java + // + // To exclude the generated code from IntelliJ auto-formatting enable (one-time): + // Settings > Editor > Code Style > Formatter Control + //@formatter:off + + + @DataClass.Generated.Member + public ShortcutQueryWrapper() { + + // onConstructed(); // You can define this method to get a callback + } + + @Override + @DataClass.Generated.Member + public String toString() { + // You can override field toString logic by defining methods like: + // String fieldNameToString() { ... } + + return "ShortcutQueryWrapper { " + + " }"; + } + + @Override + @DataClass.Generated.Member + public void writeToParcel(@NonNull Parcel dest, int flags) { + // You can override field parcelling by defining methods like: + // void parcelFieldName(Parcel dest, int flags) { ... } + + byte flg = 0; + if (mPackage != null) flg |= 0x2; + if (mShortcutIds != null) flg |= 0x4; + if (mLocusIds != null) flg |= 0x8; + if (mActivity != null) flg |= 0x10; + dest.writeByte(flg); + dest.writeLong(mChangedSince); + if (mPackage != null) dest.writeString(mPackage); + if (mShortcutIds != null) dest.writeStringList(mShortcutIds); + if (mLocusIds != null) dest.writeParcelableList(mLocusIds, flags); + if (mActivity != null) dest.writeTypedObject(mActivity, flags); + dest.writeInt(mQueryFlags); + } + + @Override + @DataClass.Generated.Member + public int describeContents() { return 0; } + + /** @hide */ + @SuppressWarnings({"unchecked", "RedundantCast"}) + @DataClass.Generated.Member + /* package-private */ ShortcutQueryWrapper(@NonNull Parcel in) { + // You can override field unparcelling by defining methods like: + // static FieldType unparcelFieldName(Parcel in) { ... } + + byte flg = in.readByte(); + long changedSince = in.readLong(); + String pkg = (flg & 0x2) == 0 ? null : in.readString(); + List<String> shortcutIds = null; + if ((flg & 0x4) != 0) { + shortcutIds = new ArrayList<>(); + in.readStringList(shortcutIds); + } + List<LocusId> locusIds = null; + if ((flg & 0x8) != 0) { + locusIds = new ArrayList<>(); + in.readParcelableList(locusIds, LocusId.class.getClassLoader()); + } + ComponentName activity = (flg & 0x10) == 0 ? null + : (ComponentName) in.readTypedObject(ComponentName.CREATOR); + int queryFlags = in.readInt(); + + this.mChangedSince = changedSince; + this.mPackage = pkg; + this.mShortcutIds = shortcutIds; + this.mLocusIds = locusIds; + this.mActivity = activity; + this.mQueryFlags = queryFlags; + com.android.internal.util.AnnotationValidations.validate( + QueryFlags.class, null, mQueryFlags); + + // onConstructed(); // You can define this method to get a callback + } + + @DataClass.Generated.Member + public static final @NonNull Parcelable.Creator<ShortcutQueryWrapper> CREATOR + = new Parcelable.Creator<ShortcutQueryWrapper>() { + @Override + public ShortcutQueryWrapper[] newArray(int size) { + return new ShortcutQueryWrapper[size]; + } + + @Override + public ShortcutQueryWrapper createFromParcel(@NonNull Parcel in) { + return new ShortcutQueryWrapper(in); + } + }; + + @DataClass.Generated( + time = 1582049937960L, + codegenVersion = "1.0.14", + sourceFile = "frameworks/base/core/java/android/content/pm/ShortcutQueryWrapper.java", + inputSignatures = "public long getChangedSince()\npublic @android.annotation.Nullable java.lang.String getPackage()\npublic @android.annotation.Nullable java.util.List<android.content.LocusId> getLocusIds()\npublic @android.annotation.Nullable java.util.List<java.lang.String> getShortcutIds()\npublic @android.annotation.Nullable android.content.ComponentName getActivity()\npublic int getQueryFlags()\nclass ShortcutQueryWrapper extends android.content.pm.LauncherApps.ShortcutQuery implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genParcelable=true, genToString=true)") + @Deprecated + private void __metadata() {} + + + //@formatter:on + // End of generated code + +} diff --git a/core/java/android/net/LinkProperties.java b/core/java/android/net/LinkProperties.java index 7ff954bdc1d2..651494d1c99c 100644 --- a/core/java/android/net/LinkProperties.java +++ b/core/java/android/net/LinkProperties.java @@ -1074,6 +1074,21 @@ public final class LinkProperties implements Parcelable { } /** + * Returns true if this link has an IPv4 unreachable default route. + * + * @return {@code true} if there is an IPv4 unreachable default route, {@code false} otherwise. + * @hide + */ + public boolean hasIpv4UnreachableDefaultRoute() { + for (RouteInfo r : mRoutes) { + if (r.isIPv4UnreachableDefault()) { + return true; + } + } + return false; + } + + /** * For backward compatibility. * This was annotated with @UnsupportedAppUsage in P, so we can't remove the method completely * just yet. @@ -1102,6 +1117,21 @@ public final class LinkProperties implements Parcelable { } /** + * Returns true if this link has an IPv6 unreachable default route. + * + * @return {@code true} if there is an IPv6 unreachable default route, {@code false} otherwise. + * @hide + */ + public boolean hasIpv6UnreachableDefaultRoute() { + for (RouteInfo r : mRoutes) { + if (r.isIPv6UnreachableDefault()) { + return true; + } + } + return false; + } + + /** * For backward compatibility. * This was annotated with @UnsupportedAppUsage in P, so we can't remove the method completely * just yet. diff --git a/core/java/android/net/RouteInfo.java b/core/java/android/net/RouteInfo.java index dbdaa4c2da67..e550f85e6b9a 100644 --- a/core/java/android/net/RouteInfo.java +++ b/core/java/android/net/RouteInfo.java @@ -426,6 +426,16 @@ public final class RouteInfo implements Parcelable { } /** + * Indicates if this route is an unreachable default route. + * + * @return {@code true} if it's an unreachable route with prefix length of 0. + * @hide + */ + private boolean isUnreachableDefaultRoute() { + return mType == RTN_UNREACHABLE && mDestination.getPrefixLength() == 0; + } + + /** * Indicates if this route is an IPv4 default route. * @hide */ @@ -434,6 +444,14 @@ public final class RouteInfo implements Parcelable { } /** + * Indicates if this route is an IPv4 unreachable default route. + * @hide + */ + public boolean isIPv4UnreachableDefault() { + return isUnreachableDefaultRoute() && mDestination.getAddress() instanceof Inet4Address; + } + + /** * Indicates if this route is an IPv6 default route. * @hide */ @@ -442,6 +460,14 @@ public final class RouteInfo implements Parcelable { } /** + * Indicates if this route is an IPv6 unreachable default route. + * @hide + */ + public boolean isIPv6UnreachableDefault() { + return isUnreachableDefaultRoute() && mDestination.getAddress() instanceof Inet6Address; + } + + /** * Indicates if this route is a host route (ie, matches only a single host address). * * @return {@code true} if the destination has a prefix length of 32 or 128 for IPv4 or IPv6, diff --git a/core/java/android/provider/DeviceConfig.java b/core/java/android/provider/DeviceConfig.java index fb81d675939c..e86aa62d00bc 100644 --- a/core/java/android/provider/DeviceConfig.java +++ b/core/java/android/provider/DeviceConfig.java @@ -266,6 +266,13 @@ public final class DeviceConfig { public static final String NAMESPACE_SCHEDULER = "scheduler"; /** + * Namespace for settings statistics features. + * + * @hide + */ + public static final String NAMESPACE_SETTINGS_STATS = "settings_stats"; + + /** * Namespace for storage-related features. * * @deprecated Replace storage namespace with storage_native_boot. diff --git a/core/tests/coretests/src/android/content/pm/ShortcutQueryWrapperTest.java b/core/tests/coretests/src/android/content/pm/ShortcutQueryWrapperTest.java new file mode 100644 index 000000000000..8f8488f8b287 --- /dev/null +++ b/core/tests/coretests/src/android/content/pm/ShortcutQueryWrapperTest.java @@ -0,0 +1,77 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.content.pm; + +import static org.junit.Assert.assertEquals; + +import android.content.ComponentName; +import android.content.LocusId; +import android.os.Parcel; +import android.platform.test.annotations.Presubmit; + +import androidx.test.runner.AndroidJUnit4; + +import com.google.android.collect.Lists; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; + +import java.util.List; +import java.util.concurrent.TimeUnit; + +@RunWith(AndroidJUnit4.class) +@Presubmit +public class ShortcutQueryWrapperTest { + + private static final long CHANGED_SINCE = TimeUnit.SECONDS.toMillis(1); + private static final String PACKAGE_NAME = "com.android.test"; + private static final List<String> SHORTCUT_IDS = Lists.newArrayList("s1", "s2", "s3"); + private static final List<LocusId> LOCUS_IDS = Lists.newArrayList( + new LocusId("id1"), new LocusId("id2"), new LocusId("id3")); + private static final ComponentName COMPONENT_NAME = new ComponentName( + PACKAGE_NAME, "ShortcutQueryTest"); + private static final int QUERY_FLAG = LauncherApps.ShortcutQuery.FLAG_MATCH_ALL_KINDS; + + private ShortcutQueryWrapper mShortcutQuery; + + @Before + public void setUp() throws Exception { + mShortcutQuery = new ShortcutQueryWrapper(new LauncherApps.ShortcutQuery() + .setChangedSince(CHANGED_SINCE) + .setPackage(PACKAGE_NAME) + .setShortcutIds(SHORTCUT_IDS) + .setLocusIds(LOCUS_IDS) + .setActivity(COMPONENT_NAME) + .setQueryFlags(QUERY_FLAG)); + } + + @Test + public void testWriteAndReadFromParcel() { + Parcel p = Parcel.obtain(); + mShortcutQuery.writeToParcel(p, 0); + p.setDataPosition(0); + ShortcutQueryWrapper q = ShortcutQueryWrapper.CREATOR.createFromParcel(p); + assertEquals("Changed since doesn't match!", CHANGED_SINCE, q.getChangedSince()); + assertEquals("Package name doesn't match!", PACKAGE_NAME, q.getPackage()); + assertEquals("Shortcut ids doesn't match", SHORTCUT_IDS, q.getShortcutIds()); + assertEquals("Locus ids doesn't match", LOCUS_IDS, q.getLocusIds()); + assertEquals("Component name doesn't match", COMPONENT_NAME, q.getActivity()); + assertEquals("Query flag doesn't match", QUERY_FLAG, q.getQueryFlags()); + p.recycle(); + } +} diff --git a/packages/SystemUI/res/drawable/ic_empty_bubble_overflow_dark.xml b/packages/SystemUI/res/drawable/ic_empty_bubble_overflow_dark.xml new file mode 100644 index 000000000000..8f8f1b664692 --- /dev/null +++ b/packages/SystemUI/res/drawable/ic_empty_bubble_overflow_dark.xml @@ -0,0 +1,162 @@ +<!-- +Copyright (C) 2020 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. +--> + +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="412dp" + android:height="300dp" + android:viewportWidth="412" + android:viewportHeight="300"> + <group> + <clip-path + android:pathData="M206,150m-150,0a150,150 0,1 1,300 0a150,150 0,1 1,-300 0"/> + <path + android:pathData="M296,105.2h-9.6l-3.1,-2.5l-3.1,2.5H116c-1.7,0 -3,1.3 -3,3v111.7c0,1.7 1.3,3 3,3h180c1.7,0 3,-1.3 3,-3V108.2C299,106.6 297.7,105.2 296,105.2C296,105.2 296,105.2 296,105.2z" + android:fillColor="#3C4043"/> + <path + android:strokeWidth="1" + android:pathData="M252.4,85.3m-12.4,0a12.4,12.4 0,1 1,24.8 0a12.4,12.4 0,1 1,-24.8 0" + android:fillColor="#D2E3FC" + android:strokeColor="#4285F4"/> + <path + android:pathData="M261.9,95.7m-4.5,0a4.5,4.5 0,1 1,9 0a4.5,4.5 0,1 1,-9 0" + android:fillColor="#4285F4"/> + <path + android:strokeWidth="1" + android:pathData="M160.6,85.3m-12.4,0a12.4,12.4 0,1 1,24.8 0a12.4,12.4 0,1 1,-24.8 0" + android:fillColor="#FAD2CF" + android:strokeColor="#EA4335"/> + <path + android:pathData="M170.1,95.7m-4.6,0a4.6,4.6 0,1 1,9.2 0a4.6,4.6 0,1 1,-9.2 0" + android:fillColor="#EA4335"/> + <path + android:strokeWidth="1" + android:pathData="M192.1,85.3m-12.4,0a12.4,12.4 0,1 1,24.8 0a12.4,12.4 0,1 1,-24.8 0" + android:fillColor="#FEEFC3" + android:strokeColor="#FBBC04"/> + <path + android:strokeWidth="1" + android:pathData="M221.8,85.4m-12.4,0a12.4,12.4 0,1 1,24.8 0a12.4,12.4 0,1 1,-24.8 0" + android:fillColor="#CEEAD6" + android:strokeColor="#34A853"/> + <path + android:pathData="M201.6,95.7m-4.6,0a4.6,4.6 0,1 1,9.2 0a4.6,4.6 0,1 1,-9.2 0" + android:fillColor="#FBBC04"/> + <path + android:pathData="M231.4,95.7m-4.6,0a4.6,4.6 0,1 1,9.2 0a4.6,4.6 0,1 1,-9.2 0" + android:fillColor="#34A853"/> + <path + android:pathData="M282.8,85.3m-12.4,0a12.4,12.4 0,1 1,24.8 0a12.4,12.4 0,1 1,-24.8 0" + android:fillColor="#3C4043"/> + <path + android:pathData="M278.7,85.7m-1.2,0a1.2,1.2 0,1 1,2.4 0a1.2,1.2 0,1 1,-2.4 0" + android:fillColor="#D2E3FC"/> + <path + android:pathData="M282.8,85.7m-1.2,0a1.2,1.2 0,1 1,2.4 0a1.2,1.2 0,1 1,-2.4 0" + android:fillColor="#D2E3FC"/> + <path + android:pathData="M286.9,85.7m-1.2,0a1.2,1.2 0,1 1,2.4 0a1.2,1.2 0,1 1,-2.4 0" + android:fillColor="#D2E3FC"/> + <path + android:pathData="M129.2,72.9h-3.4l0.3,1c1,-0.3 2.1,-0.4 3.2,-0.4v-1V72.9zM122.6,74.8c-0.5,0.3 -1,0.6 -1.4,1l0,0l0,0l0,0l0,0h-0.6l0,0l0,0l0,0l0,0l0,0l0,0c-0.2,0.2 -0.3,0.3 -0.4,0.5l0.8,0.7c0.7,-0.8 1.5,-1.5 2.4,-2.1l-0.5,-0.8L122.6,74.8zM118,80L118,80L118,80L118,80L118,80L118,80L118,80L118,80c-0.5,1 -0.8,2 -1,3l1,0.2c0.2,-1 0.5,-2 1,-3L118,80zM117.8,86.7l-1,0.1c0.1,0.6 0.2,1.1 0.3,1.7l0,0l0,0h0.1l0,0l0,0l0,0l0,0c0.1,0.5 0.3,0.9 0.5,1.4l0.9,-0.4c-0.4,-1 -0.7,-2 -0.8,-3.1L117.8,86.7zM120.2,92.5l-0.8,0.6l0.2,0.3l0,0l0,0l0,0l0,0h0.3l0,0l0,0l0,0l0,0l0,0l0,0l0,0l0,0l0.4,0.4l0,0l0,0l0,0l0,0h0.1l0,0l0,0l0,0l0,0l0,0h0.5l0,0l0,0l0,0l0,0l0,0l0,0l0.6,0.4l0.6,-0.8c-0.9,-0.6 -1.7,-1.4 -2.3,-2.2L120.2,92.5zM125.4,96.2l-0.3,0.9c1.1,0.4 2.2,0.6 3.4,0.7l0.1,-1C127.5,96.7 126.4,96.5 125.4,96.2zM134.7,95.4c-0.9,0.5 -2,0.9 -3,1.1l0.2,1h0.4c1,-0.3 2,-0.6 2.9,-1.2l-0.5,-0.9L134.7,95.4zM139.2,90.9c-0.5,0.9 -1.2,1.8 -1.9,2.5l0.7,0.7v-0.1h0.2l0,0l0,0c0.7,-0.7 1.3,-1.6 1.8,-2.4l-0.9,-0.5L139.2,90.9zM141.6,84.7h-1c0,0.2 0,0.4 0,0.6c0,0.9 -0.1,1.7 -0.3,2.6l1,0.2c0.1,-0.4 0.2,-0.8 0.2,-1.2l0,0v-0.1l0,0v-0.1l0,0l0,0l0,0l0,0l0,0l0,0l0,0l0,0l0,0l0,0l0,0l0,0l0,0c0,-0.2 0,-0.3 0,-0.5l0,0L141.6,84.7zM139.3,78.2l-0.8,0.6c0.6,0.9 1.1,1.8 1.5,2.8l0.9,-0.3c-0.1,-0.2 -0.2,-0.4 -0.2,-0.7l0,0l0,0h-0.1l0,0l0,0l0,0l0,0l0,0l0,0c-0.3,-0.7 -0.7,-1.4 -1.1,-2l0,0l0,0l0,0l0,0l0,0l0,0l0,0L139.3,78.2zM134,73.9l-0.4,0.9c1,0.4 1.9,1 2.7,1.6l0.6,-0.8l0,0l0,0l0,0l0,0l0,0c-0.3,-0.3 -0.7,-0.5 -1,-0.7l0,0h-0.1h-0.6c-0.4,-0.2 -0.8,-0.4 -1.2,-0.6L134,73.9zM129.2,72.9v1c0.4,0 0.9,0 1.3,0.1l0.1,-1l-0.9,-0.1L129.2,72.9L129.2,72.9z" + android:fillColor="#34A853"/> + <path + android:pathData="M206,252m-11.7,0a11.7,11.7 0,1 1,23.4 0a11.7,11.7 0,1 1,-23.4 0" + android:fillColor="#F1F3F4"/> + <path + android:pathData="M201.7,247.7L210.3,256.3" + android:strokeWidth="2" + android:fillColor="#00000000" + android:strokeColor="#202124" + android:strokeLineCap="round"/> + <path + android:pathData="M210.3,247.7L201.7,256.3" + android:strokeWidth="2" + android:fillColor="#00000000" + android:strokeColor="#202124" + android:strokeLineCap="round"/> + <path + android:strokeWidth="1" + android:pathData="M205.3,221.9m-10.4,0a10.4,10.4 0,1 1,20.8 0a10.4,10.4 0,1 1,-20.8 0" + android:fillColor="#CEEAD6" + android:strokeColor="#34A853"/> + <path + android:pathData="M481.4,292.2c48,58.3 119.8,125.8 58.6,162.9c-38.7,23.5 -53.9,24 -98.3,33.2c-43.8,9.1 -93.6,-89.8 -101.1,-134.5C329.6,288.6 452.6,257.2 481.4,292.2z" + android:fillColor="#D2E3FC"/> + <path + android:pathData="M458.2,320.7l-21.1,-71.4L400.5,193c-2.7,-5.1 -1.2,-11.4 3.5,-14.7l0,0c2.8,-2 6.6,-1.5 8.8,1.1c0,0 40.6,38.4 53.2,61.1l81.5,134.8l-69.9,-39.1L458.2,320.7z" + android:fillColor="#D2E3FC"/> + <path + android:pathData="M403.8,184.8l5.4,6.9c1.2,1.5 3.3,1.9 4.9,0.9l3,-1.8" + android:strokeLineJoin="bevel" + android:strokeWidth="1.75" + android:fillColor="#00000000" + android:strokeColor="#A0C2F9" + android:strokeLineCap="square"/> + <path + android:pathData="M420.9,325.8l-37.8,-88.6l-58.4,-37.8c-5.7,-5.4 -7.4,-13.8 -4.2,-21l0,0c2,-4.6 7.4,-6.7 12,-4.6c0.2,0.1 0.4,0.2 0.7,0.3c0,0 70.7,36.3 81.5,48.3l59.8,95.5l-49.9,24.9L420.9,325.8z" + android:fillColor="#D2E3FC"/> + <path + android:pathData="M324.6,183.9l8,6.2c2.1,1.7 5.2,1.4 7,-0.6l2.9,-3.3" + android:strokeLineJoin="bevel" + android:strokeWidth="1.75" + android:fillColor="#00000000" + android:strokeColor="#A0C2F9" + android:strokeLineCap="square"/> + <path + android:pathData="M392.4,231c3.8,-5.1 9.1,-8.9 15.1,-10.9" + android:strokeWidth="1.75" + android:fillColor="#00000000" + android:strokeColor="#A0C2F9"/> + <path + android:pathData="M401.3,283.8L405.8,292.6" + android:strokeWidth="1.75" + android:fillColor="#00000000" + android:strokeColor="#A0C2F9"/> + <path + android:pathData="M378.2,346.9l-34.7,-75.6l-60,-61.2c-6.3,-4.7 -9,-12.8 -6.7,-20.4l0,0c1.5,-4.8 6.5,-7.5 11.3,-6c0.2,0.1 0.4,0.1 0.7,0.2c0,0 73.5,48.2 82.6,61.7l64.1,95.7l-40.3,23.5L378.2,346.9z" + android:fillColor="#D2E3FC"/> + <path + android:pathData="M280.8,196.6l7.6,4.6c2.6,1.6 5.9,1.1 7.9,-1.1l4.1,-4.5" + android:strokeLineJoin="bevel" + android:strokeWidth="1.75" + android:fillColor="#00000000" + android:strokeColor="#A0C2F9" + android:strokeLineCap="square"/> + <path + android:pathData="M347.5,251c3.8,-5.1 9.1,-8.9 15.1,-10.9" + android:strokeWidth="1.75" + android:fillColor="#00000000" + android:strokeColor="#A0C2F9"/> + <path + android:pathData="M207.2,234.1c-8.8,-11 4.7,-31.5 19.8,-19c17.7,14.7 74.7,64.3 74.7,64.3l103.8,101.8c0,0 -36.4,53.8 -44.5,42.3C287.8,319.3 234.4,267.9 207.2,234.1z" + android:fillColor="#D2E3FC"/> + <path + android:pathData="M209.6,226.2l9.3,9.5c1,0.8 3,0.4 3.1,-1c0.2,-2.2 4.6,-6.2 7,-6.6c1.1,-0.3 1.7,-1.4 1.4,-2.4c-0.1,-0.2 -0.2,-0.4 -0.3,-0.6l-4.4,-3.9" + android:strokeLineJoin="bevel" + android:strokeWidth="1.75" + android:fillColor="#00000000" + android:strokeColor="#A0C2F9" + android:strokeLineCap="square"/> + <path + android:pathData="M284.1,296.2c3.1,-5.5 7.8,-10 13.5,-12.8" + android:strokeWidth="1.75" + android:fillColor="#00000000" + android:strokeColor="#A0C2F9"/> + </group> + <path + android:pathData="M206,4c80.6,0 146,65.4 146,146c0,38.7 -15.4,75.9 -42.8,103.2c-57,57 -149.5,57 -206.5,0s-57,-149.5 0,-206.5C130.1,19.3 167.3,3.9 206,4M206,0C123.2,0 56,67.2 56,150s67.2,150 150,150s150,-67.2 150,-150S288.8,0 206,0z" + android:fillColor="#D2E3FC"/> +</vector> diff --git a/packages/SystemUI/res/drawable/ic_empty_bubble_overflow_light.xml b/packages/SystemUI/res/drawable/ic_empty_bubble_overflow_light.xml new file mode 100644 index 000000000000..5e02f67700e7 --- /dev/null +++ b/packages/SystemUI/res/drawable/ic_empty_bubble_overflow_light.xml @@ -0,0 +1,162 @@ +<!-- +Copyright (C) 2020 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. +--> + +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="412dp" + android:height="300dp" + android:viewportWidth="412" + android:viewportHeight="300"> + <group> + <clip-path + android:pathData="M206,150m-150,0a150,150 0,1 1,300 0a150,150 0,1 1,-300 0"/> + <path + android:pathData="M296,105.2h-9.6l-3.1,-2.5l-3.1,2.5H116c-1.7,0 -3,1.3 -3,3v111.7c0,1.7 1.3,3 3,3h180c1.7,0 3,-1.3 3,-3V108.2C299,106.6 297.7,105.2 296,105.2L296,105.2z" + android:fillColor="#F1F3F4"/> + <path + android:strokeWidth="1" + android:pathData="M252.4,85.3m-12.4,0a12.4,12.4 0,1 1,24.8 0a12.4,12.4 0,1 1,-24.8 0" + android:fillColor="#D2E3FC" + android:strokeColor="#4285F4"/> + <path + android:pathData="M261.9,95.7m-4.5,0a4.5,4.5 0,1 1,9 0a4.5,4.5 0,1 1,-9 0" + android:fillColor="#4285F4"/> + <path + android:strokeWidth="1" + android:pathData="M160.6,85.3m-12.4,0a12.4,12.4 0,1 1,24.8 0a12.4,12.4 0,1 1,-24.8 0" + android:fillColor="#FAD2CF" + android:strokeColor="#EA4335"/> + <path + android:pathData="M170.1,95.7m-4.6,0a4.6,4.6 0,1 1,9.2 0a4.6,4.6 0,1 1,-9.2 0" + android:fillColor="#EA4335"/> + <path + android:strokeWidth="1" + android:pathData="M192.1,85.3m-12.4,0a12.4,12.4 0,1 1,24.8 0a12.4,12.4 0,1 1,-24.8 0" + android:fillColor="#FEEFC3" + android:strokeColor="#FBBC04"/> + <path + android:strokeWidth="1" + android:pathData="M221.8,85.4m-12.4,0a12.4,12.4 0,1 1,24.8 0a12.4,12.4 0,1 1,-24.8 0" + android:fillColor="#CEEAD6" + android:strokeColor="#34A853"/> + <path + android:pathData="M201.6,95.7m-4.6,0a4.6,4.6 0,1 1,9.2 0a4.6,4.6 0,1 1,-9.2 0" + android:fillColor="#FBBC04"/> + <path + android:pathData="M231.4,95.7m-4.6,0a4.6,4.6 0,1 1,9.2 0a4.6,4.6 0,1 1,-9.2 0" + android:fillColor="#34A853"/> + <path + android:pathData="M282.8,85.3m-12.4,0a12.4,12.4 0,1 1,24.8 0a12.4,12.4 0,1 1,-24.8 0" + android:fillColor="#F1F3F4"/> + <path + android:pathData="M278.7,85.7m-1.2,0a1.2,1.2 0,1 1,2.4 0a1.2,1.2 0,1 1,-2.4 0" + android:fillColor="#3474E0"/> + <path + android:pathData="M282.8,85.7m-1.2,0a1.2,1.2 0,1 1,2.4 0a1.2,1.2 0,1 1,-2.4 0" + android:fillColor="#3474E0"/> + <path + android:pathData="M286.9,85.7m-1.2,0a1.2,1.2 0,1 1,2.4 0a1.2,1.2 0,1 1,-2.4 0" + android:fillColor="#3474E0"/> + <path + android:pathData="M129.2,72.9h-3.4l0.3,1c1,-0.3 2.1,-0.4 3.2,-0.4v-1v0.4H129.2zM122.6,74.8c-0.5,0.3 -1,0.6 -1.4,1l0,0l0,0l0,0l0,0h-0.6l0,0l0,0l0,0l0,0l0,0l0,0c-0.2,0.2 -0.3,0.3 -0.4,0.5L121,77c0.7,-0.8 1.5,-1.5 2.4,-2.1l-0.5,-0.8L122.6,74.8zM118,80L118,80L118,80L118,80L118,80L118,80L118,80L118,80c-0.5,1 -0.8,2 -1,3l1,0.2c0.2,-1 0.5,-2 1,-3L118,80zM117.8,86.7l-1,0.1c0.1,0.6 0.2,1.1 0.3,1.7l0,0l0,0h0.1l0,0l0,0l0,0l0,0c0.1,0.5 0.3,0.9 0.5,1.4l0.9,-0.4c-0.4,-1 -0.7,-2 -0.8,-3.1V86.7zM120.2,92.5l-0.8,0.6l0.2,0.3l0,0l0,0l0,0l0,0h0.3l0,0l0,0l0,0l0,0l0,0l0,0l0,0l0,0l0.4,0.4l0,0l0,0l0,0l0,0h0.1l0,0l0,0l0,0l0,0l0,0h0.5l0,0l0,0l0,0l0,0l0,0l0,0l0.6,0.4l0.6,-0.8c-0.9,-0.6 -1.7,-1.4 -2.3,-2.2L120.2,92.5zM125.4,96.2l-0.3,0.9c1.1,0.4 2.2,0.6 3.4,0.7l0.1,-1C127.5,96.7 126.4,96.5 125.4,96.2zM134.7,95.4c-0.9,0.5 -2,0.9 -3,1.1l0.2,1h0.4c1,-0.3 2,-0.6 2.9,-1.2L134.7,95.4L134.7,95.4zM139.2,90.9c-0.5,0.9 -1.2,1.8 -1.9,2.5l0.7,0.7V94h0.2l0,0l0,0c0.7,-0.7 1.3,-1.6 1.8,-2.4l-0.9,-0.5L139.2,90.9zM141.6,84.7h-1c0,0.2 0,0.4 0,0.6c0,0.9 -0.1,1.7 -0.3,2.6l1,0.2c0.1,-0.4 0.2,-0.8 0.2,-1.2l0,0v-0.1l0,0v-0.1l0,0l0,0l0,0l0,0l0,0l0,0l0,0l0,0l0,0l0,0l0,0l0,0l0,0c0,-0.2 0,-0.3 0,-0.5l0,0L141.6,84.7zM139.3,78.2l-0.8,0.6c0.6,0.9 1.1,1.8 1.5,2.8l0.9,-0.3c-0.1,-0.2 -0.2,-0.4 -0.2,-0.7l0,0l0,0h-0.1l0,0l0,0l0,0l0,0l0,0l0,0c-0.3,-0.7 -0.7,-1.4 -1.1,-2l0,0l0,0l0,0l0,0l0,0l0,0l0,0L139.3,78.2zM134,73.9l-0.4,0.9c1,0.4 1.9,1 2.7,1.6l0.6,-0.8l0,0l0,0l0,0l0,0l0,0c-0.3,-0.3 -0.7,-0.5 -1,-0.7l0,0h-0.1h-0.6c-0.4,-0.2 -0.8,-0.4 -1.2,-0.6V73.9zM129.2,72.9v1c0.4,0 0.9,0 1.3,0.1l0.1,-1l-0.9,-0.1H129.2L129.2,72.9z" + android:fillColor="#34A853"/> + <path + android:pathData="M206,252m-11.7,0a11.7,11.7 0,1 1,23.4 0a11.7,11.7 0,1 1,-23.4 0" + android:fillColor="#9AA0A6"/> + <path + android:pathData="M201.7,247.7L210.3,256.3" + android:strokeWidth="2" + android:fillColor="#00000000" + android:strokeColor="#F1F3F4" + android:strokeLineCap="round"/> + <path + android:pathData="M210.3,247.7L201.7,256.3" + android:strokeWidth="2" + android:fillColor="#00000000" + android:strokeColor="#F1F3F4" + android:strokeLineCap="round"/> + <path + android:strokeWidth="1" + android:pathData="M205.3,221.9m-10.4,0a10.4,10.4 0,1 1,20.8 0a10.4,10.4 0,1 1,-20.8 0" + android:fillColor="#CEEAD6" + android:strokeColor="#34A853"/> + <path + android:pathData="M481.4,292.2c48,58.3 119.8,125.8 58.6,162.9c-38.7,23.5 -53.9,24 -98.3,33.2c-43.8,9.1 -93.6,-89.8 -101.1,-134.5C329.6,288.6 452.6,257.2 481.4,292.2z" + android:fillColor="#D2E3FC"/> + <path + android:pathData="M458.2,320.7l-21.1,-71.4L400.5,193c-2.7,-5.1 -1.2,-11.4 3.5,-14.7l0,0c2.8,-2 6.6,-1.5 8.8,1.1c0,0 40.6,38.4 53.2,61.1l81.5,134.8l-69.9,-39.1L458.2,320.7z" + android:fillColor="#D2E3FC"/> + <path + android:pathData="M403.8,184.8l5.4,6.9c1.2,1.5 3.3,1.9 4.9,0.9l3,-1.8" + android:strokeLineJoin="bevel" + android:strokeWidth="1.75" + android:fillColor="#00000000" + android:strokeColor="#A0C2F9" + android:strokeLineCap="square"/> + <path + android:pathData="M420.9,325.8l-37.8,-88.6l-58.4,-37.8c-5.7,-5.4 -7.4,-13.8 -4.2,-21l0,0c2,-4.6 7.4,-6.7 12,-4.6c0.2,0.1 0.4,0.2 0.7,0.3c0,0 70.7,36.3 81.5,48.3l59.8,95.5l-49.9,24.9L420.9,325.8z" + android:fillColor="#D2E3FC"/> + <path + android:pathData="M324.6,183.9l8,6.2c2.1,1.7 5.2,1.4 7,-0.6l2.9,-3.3" + android:strokeLineJoin="bevel" + android:strokeWidth="1.75" + android:fillColor="#00000000" + android:strokeColor="#A0C2F9" + android:strokeLineCap="square"/> + <path + android:pathData="M392.4,231c3.8,-5.1 9.1,-8.9 15.1,-10.9" + android:strokeWidth="1.75" + android:fillColor="#00000000" + android:strokeColor="#A0C2F9"/> + <path + android:pathData="M401.3,283.8L405.8,292.6" + android:strokeWidth="1.75" + android:fillColor="#00000000" + android:strokeColor="#A0C2F9"/> + <path + android:pathData="M378.2,346.9l-34.7,-75.6l-60,-61.2c-6.3,-4.7 -9,-12.8 -6.7,-20.4l0,0c1.5,-4.8 6.5,-7.5 11.3,-6c0.2,0.1 0.4,0.1 0.7,0.2c0,0 73.5,48.2 82.6,61.7l64.1,95.7l-40.3,23.5L378.2,346.9z" + android:fillColor="#D2E3FC"/> + <path + android:pathData="M280.8,196.6l7.6,4.6c2.6,1.6 5.9,1.1 7.9,-1.1l4.1,-4.5" + android:strokeLineJoin="bevel" + android:strokeWidth="1.75" + android:fillColor="#00000000" + android:strokeColor="#A0C2F9" + android:strokeLineCap="square"/> + <path + android:pathData="M347.5,251c3.8,-5.1 9.1,-8.9 15.1,-10.9" + android:strokeWidth="1.75" + android:fillColor="#00000000" + android:strokeColor="#A0C2F9"/> + <path + android:pathData="M207.2,234.1c-8.8,-11 4.7,-31.5 19.8,-19c17.7,14.7 74.7,64.3 74.7,64.3l103.8,101.8c0,0 -36.4,53.8 -44.5,42.3C287.8,319.3 234.4,267.9 207.2,234.1z" + android:fillColor="#D2E3FC"/> + <path + android:pathData="M209.6,226.2l9.3,9.5c1,0.8 3,0.4 3.1,-1c0.2,-2.2 4.6,-6.2 7,-6.6c1.1,-0.3 1.7,-1.4 1.4,-2.4c-0.1,-0.2 -0.2,-0.4 -0.3,-0.6l-4.4,-3.9" + android:strokeLineJoin="bevel" + android:strokeWidth="1.75" + android:fillColor="#00000000" + android:strokeColor="#A0C2F9" + android:strokeLineCap="square"/> + <path + android:pathData="M284.1,296.2c3.1,-5.5 7.8,-10 13.5,-12.8" + android:strokeWidth="1.75" + android:fillColor="#00000000" + android:strokeColor="#A0C2F9"/> + </group> + <path + android:pathData="M206,4c80.6,0 146,65.4 146,146c0,38.7 -15.4,75.9 -42.8,103.2c-57,57 -149.5,57 -206.5,0s-57,-149.5 0,-206.5C130.1,19.3 167.3,3.9 206,4M206,0C123.2,0 56,67.2 56,150s67.2,150 150,150s150,-67.2 150,-150S288.8,0 206,0z" + android:fillColor="#D2E3FC"/> +</vector> diff --git a/packages/SystemUI/res/layout/bubble_overflow_activity.xml b/packages/SystemUI/res/layout/bubble_overflow_activity.xml index b3c7cf74941a..306061911f8d 100644 --- a/packages/SystemUI/res/layout/bubble_overflow_activity.xml +++ b/packages/SystemUI/res/layout/bubble_overflow_activity.xml @@ -41,6 +41,13 @@ android:orientation="vertical" android:gravity="center"> + <ImageView + android:layout_width="@dimen/bubble_empty_overflow_image_height" + android:layout_height="@dimen/bubble_empty_overflow_image_height" + android:id="@+id/bubble_overflow_empty_state_image" + android:scaleType="fitCenter" + android:layout_gravity="center"/> + <TextView android:id="@+id/bubble_overflow_empty_title" android:text="@string/bubble_overflow_empty_title" @@ -59,6 +66,7 @@ android:text="@string/bubble_overflow_empty_subtitle" android:layout_width="match_parent" android:layout_height="wrap_content" + android:paddingBottom="@dimen/bubble_empty_overflow_subtitle_padding" android:gravity="center"/> </LinearLayout> </LinearLayout> diff --git a/packages/SystemUI/res/layout/qs_media_panel.xml b/packages/SystemUI/res/layout/qs_media_panel.xml index fe8557bfcce4..fc3bf941b27a 100644 --- a/packages/SystemUI/res/layout/qs_media_panel.xml +++ b/packages/SystemUI/res/layout/qs_media_panel.xml @@ -136,6 +136,47 @@ </LinearLayout> </LinearLayout> + <!-- Seek Bar --> + <SeekBar + android:id="@+id/media_progress_bar" + android:clickable="true" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:maxHeight="3dp" + android:paddingTop="24dp" + android:paddingBottom="24dp" + android:layout_marginBottom="-24dp" + android:layout_marginTop="-24dp" + android:splitTrack="false" + /> + + <FrameLayout + android:id="@+id/notification_media_progress_time" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_gravity="center" + > + <!-- width is set to "match_parent" to avoid extra layout calls --> + <TextView + android:id="@+id/media_elapsed_time" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_alignParentLeft="true" + android:fontFamily="@*android:string/config_bodyFontFamily" + android:textSize="14sp" + android:gravity="left" + /> + <TextView + android:id="@+id/media_total_time" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:fontFamily="@*android:string/config_bodyFontFamily" + android:layout_alignParentRight="true" + android:textSize="14sp" + android:gravity="right" + /> + </FrameLayout> + <!-- Controls --> <LinearLayout android:id="@+id/media_actions" diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml index 9074dad608ce..5b213edd5f0f 100644 --- a/packages/SystemUI/res/values/dimens.xml +++ b/packages/SystemUI/res/values/dimens.xml @@ -1155,6 +1155,10 @@ <dimen name="bubble_overflow_padding">15dp</dimen> <!-- Padding of label for bubble overflow view --> <dimen name="bubble_overflow_text_padding">7dp</dimen> + <!-- Height of bubble overflow empty state illustration --> + <dimen name="bubble_empty_overflow_image_height">200dp</dimen> + <!-- Padding of bubble overflow empty state subtitle --> + <dimen name="bubble_empty_overflow_subtitle_padding">50dp</dimen> <!-- Height of the triangle that points to the expanded bubble --> <dimen name="bubble_pointer_height">4dp</dimen> <!-- Width of the triangle that points to the expanded bubble --> diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleOverflowActivity.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleOverflowActivity.java index 3f985ef37746..37841f24a3cf 100644 --- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleOverflowActivity.java +++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleOverflowActivity.java @@ -21,18 +21,17 @@ import static com.android.systemui.bubbles.BubbleDebugConfig.TAG_BUBBLES; import static com.android.systemui.bubbles.BubbleDebugConfig.TAG_WITH_CLASS_NAME; import android.app.Activity; -import android.app.Notification; -import android.app.Person; +import android.content.res.Configuration; import android.content.res.Resources; import android.content.res.TypedArray; import android.graphics.Color; import android.os.Bundle; -import android.os.Parcelable; import android.util.DisplayMetrics; import android.util.Log; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; +import android.widget.ImageView; import android.widget.LinearLayout; import android.widget.TextView; @@ -55,6 +54,7 @@ public class BubbleOverflowActivity extends Activity { private static final String TAG = TAG_WITH_CLASS_NAME ? "BubbleOverflowActivity" : TAG_BUBBLES; private LinearLayout mEmptyState; + private ImageView mEmptyStateImage; private BubbleController mBubbleController; private BubbleOverflowAdapter mAdapter; private RecyclerView mRecyclerView; @@ -73,6 +73,7 @@ public class BubbleOverflowActivity extends Activity { mEmptyState = findViewById(R.id.bubble_overflow_empty_state); mRecyclerView = findViewById(R.id.bubble_overflow_recycler); + mEmptyStateImage = findViewById(R.id.bubble_overflow_empty_state_image); Resources res = getResources(); final int columns = res.getInteger(R.integer.bubbles_overflow_columns); @@ -98,6 +99,31 @@ public class BubbleOverflowActivity extends Activity { mBubbleController.setOverflowCallback(() -> { onDataChanged(mBubbleController.getOverflowBubbles()); }); + onThemeChanged(); + } + + /** + * Handle theme changes. + */ + void onThemeChanged() { + final int mode = + getResources().getConfiguration().uiMode & Configuration.UI_MODE_NIGHT_MASK; + switch (mode) { + case Configuration.UI_MODE_NIGHT_NO: + if (DEBUG_OVERFLOW) { + Log.d(TAG, "Set overflow UI to light mode"); + } + mEmptyStateImage.setImageDrawable( + getResources().getDrawable(R.drawable.ic_empty_bubble_overflow_light)); + break; + case Configuration.UI_MODE_NIGHT_YES: + if (DEBUG_OVERFLOW) { + Log.d(TAG, "Set overflow UI to dark mode"); + } + mEmptyStateImage.setImageDrawable( + getResources().getDrawable(R.drawable.ic_empty_bubble_overflow_dark)); + break; + } } void setBackgroundColor() { @@ -138,6 +164,7 @@ public class BubbleOverflowActivity extends Activity { @Override public void onResume() { super.onResume(); + onThemeChanged(); } @Override diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java index 6a7b0da0d8d8..eff693436451 100644 --- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java +++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java @@ -713,7 +713,7 @@ public class BubbleStackView extends FrameLayout { } else { mBubbleContainer.removeView(mBubbleOverflow.getBtn()); mBubbleOverflow.updateIcon(mContext, this); - overflowBtnIndex = mBubbleContainer.getChildCount() - 1; + overflowBtnIndex = mBubbleContainer.getChildCount(); } mBubbleContainer.addView(mBubbleOverflow.getBtn(), overflowBtnIndex, new FrameLayout.LayoutParams(WRAP_CONTENT, WRAP_CONTENT)); diff --git a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java index 6d99ef1829b2..fdd859373685 100644 --- a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java +++ b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java @@ -78,10 +78,13 @@ import android.widget.ImageView.ScaleType; import android.widget.TextView; import com.android.internal.R; +import com.android.internal.annotations.VisibleForTesting; import com.android.internal.colorextraction.ColorExtractor; import com.android.internal.colorextraction.ColorExtractor.GradientColors; import com.android.internal.colorextraction.drawable.ScrimDrawable; import com.android.internal.logging.MetricsLogger; +import com.android.internal.logging.UiEvent; +import com.android.internal.logging.UiEventLogger; import com.android.internal.logging.nano.MetricsProto.MetricsEvent; import com.android.internal.statusbar.IStatusBarService; import com.android.internal.util.EmergencyAffordanceManager; @@ -171,6 +174,7 @@ public class GlobalActionsDialog implements DialogInterface.OnDismissListener, private final IActivityManager mIActivityManager; private final TelecomManager mTelecomManager; private final MetricsLogger mMetricsLogger; + private final UiEventLogger mUiEventLogger; private final NotificationShadeDepthController mDepthController; private final BlurUtils mBlurUtils; @@ -203,6 +207,23 @@ public class GlobalActionsDialog implements DialogInterface.OnDismissListener, private final ControlsListingController mControlsListingController; private boolean mAnyControlsProviders = false; + @VisibleForTesting + public enum GlobalActionsEvent implements UiEventLogger.UiEventEnum { + @UiEvent(doc = "The global actions / power menu surface became visible on the screen.") + GA_POWER_MENU_OPEN(337); + + private final int mId; + + GlobalActionsEvent(int id) { + mId = id; + } + + @Override + public int getId() { + return mId; + } + } + /** * @param context everything needs a context :( */ @@ -223,7 +244,7 @@ public class GlobalActionsDialog implements DialogInterface.OnDismissListener, ControlsUiController controlsUiController, IWindowManager iWindowManager, @Background Executor backgroundExecutor, ControlsListingController controlsListingController, - ControlsController controlsController) { + ControlsController controlsController, UiEventLogger uiEventLogger) { mContext = new ContextThemeWrapper(context, com.android.systemui.R.style.qs_theme); mWindowManagerFuncs = windowManagerFuncs; mAudioManager = audioManager; @@ -240,6 +261,7 @@ public class GlobalActionsDialog implements DialogInterface.OnDismissListener, mIActivityManager = iActivityManager; mTelecomManager = telecomManager; mMetricsLogger = metricsLogger; + mUiEventLogger = uiEventLogger; mDepthController = depthController; mSysuiColorExtractor = colorExtractor; mStatusBarService = statusBarService; @@ -997,6 +1019,7 @@ public class GlobalActionsDialog implements DialogInterface.OnDismissListener, */ public void onShow(DialogInterface dialog) { mMetricsLogger.visible(MetricsEvent.POWER_MENU); + mUiEventLogger.log(GlobalActionsEvent.GA_POWER_MENU_OPEN); } /** diff --git a/packages/SystemUI/src/com/android/systemui/media/SeekBarObserver.kt b/packages/SystemUI/src/com/android/systemui/media/SeekBarObserver.kt new file mode 100644 index 000000000000..aa5ebaa22f2d --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/media/SeekBarObserver.kt @@ -0,0 +1,86 @@ +/* + * Copyright (C) 2020 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.media + +import android.content.res.ColorStateList +import android.text.format.DateUtils +import android.view.View +import android.widget.SeekBar +import android.widget.TextView +import androidx.annotation.UiThread +import androidx.lifecycle.Observer + +import com.android.systemui.R + +/** + * Observer for changes from SeekBarViewModel. + * + * <p>Updates the seek bar views in response to changes to the model. + */ +class SeekBarObserver(view: View) : Observer<SeekBarViewModel.Progress> { + + private val seekBarView: SeekBar + private val elapsedTimeView: TextView + private val totalTimeView: TextView + + init { + seekBarView = view.findViewById(R.id.media_progress_bar) + elapsedTimeView = view.findViewById(R.id.media_elapsed_time) + totalTimeView = view.findViewById(R.id.media_total_time) + } + + /** Updates seek bar views when the data model changes. */ + @UiThread + override fun onChanged(data: SeekBarViewModel.Progress) { + if (data.enabled && seekBarView.visibility == View.GONE) { + seekBarView.visibility = View.VISIBLE + elapsedTimeView.visibility = View.VISIBLE + totalTimeView.visibility = View.VISIBLE + } else if (!data.enabled && seekBarView.visibility == View.VISIBLE) { + seekBarView.visibility = View.GONE + elapsedTimeView.visibility = View.GONE + totalTimeView.visibility = View.GONE + return + } + + // TODO: update the style of the disabled progress bar + seekBarView.setEnabled(data.seekAvailable) + + data.color?.let { + var tintList = ColorStateList.valueOf(it) + seekBarView.setThumbTintList(tintList) + tintList = tintList.withAlpha(192) // 75% + seekBarView.setProgressTintList(tintList) + tintList = tintList.withAlpha(128) // 50% + seekBarView.setProgressBackgroundTintList(tintList) + elapsedTimeView.setTextColor(it) + totalTimeView.setTextColor(it) + } + + data.elapsedTime?.let { + seekBarView.setProgress(it) + elapsedTimeView.setText(DateUtils.formatElapsedTime( + it / DateUtils.SECOND_IN_MILLIS)) + } + + data.duration?.let { + seekBarView.setMax(it) + totalTimeView.setText(DateUtils.formatElapsedTime( + it / DateUtils.SECOND_IN_MILLIS)) + } + } +} diff --git a/packages/SystemUI/src/com/android/systemui/media/SeekBarViewModel.kt b/packages/SystemUI/src/com/android/systemui/media/SeekBarViewModel.kt new file mode 100644 index 000000000000..cf8f26841cf9 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/media/SeekBarViewModel.kt @@ -0,0 +1,152 @@ +/* + * Copyright (C) 2020 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.media + +import android.media.MediaMetadata +import android.media.session.MediaController +import android.media.session.PlaybackState +import android.view.MotionEvent +import android.view.View +import android.widget.SeekBar +import androidx.annotation.AnyThread +import androidx.annotation.WorkerThread +import androidx.lifecycle.MutableLiveData +import androidx.lifecycle.LiveData + +import com.android.systemui.util.concurrency.DelayableExecutor + +private const val POSITION_UPDATE_INTERVAL_MILLIS = 100L + +/** ViewModel for seek bar in QS media player. */ +class SeekBarViewModel(val bgExecutor: DelayableExecutor) { + + private val _progress = MutableLiveData<Progress>().apply { + postValue(Progress(false, false, null, null, null)) + } + val progress: LiveData<Progress> + get() = _progress + private var controller: MediaController? = null + private var playbackState: PlaybackState? = null + + /** Listening state (QS open or closed) is used to control polling of progress. */ + var listening = true + set(value) { + if (value) { + checkPlaybackPosition() + } + } + + /** + * Handle request to change the current position in the media track. + * @param position Place to seek to in the track. + */ + @WorkerThread + fun onSeek(position: Long) { + controller?.transportControls?.seekTo(position) + } + + /** + * Updates media information. + * @param mediaController controller for media session + * @param color foreground color for UI elements + */ + @WorkerThread + fun updateController(mediaController: MediaController?, color: Int) { + controller = mediaController + playbackState = controller?.playbackState + val mediaMetadata = controller?.metadata + val seekAvailable = ((playbackState?.actions ?: 0L) and PlaybackState.ACTION_SEEK_TO) != 0L + val position = playbackState?.position?.toInt() + val duration = mediaMetadata?.getLong(MediaMetadata.METADATA_KEY_DURATION)?.toInt() + val enabled = if (duration != null && duration <= 0) false else true + _progress.postValue(Progress(enabled, seekAvailable, position, duration, color)) + if (shouldPollPlaybackPosition()) { + checkPlaybackPosition() + } + } + + @AnyThread + private fun checkPlaybackPosition(): Runnable = bgExecutor.executeDelayed({ + val currentPosition = controller?.playbackState?.position?.toInt() + if (currentPosition != null && _progress.value!!.elapsedTime != currentPosition) { + _progress.postValue(_progress.value!!.copy(elapsedTime = currentPosition)) + } + if (shouldPollPlaybackPosition()) { + checkPlaybackPosition() + } + }, POSITION_UPDATE_INTERVAL_MILLIS) + + @WorkerThread + private fun shouldPollPlaybackPosition(): Boolean { + val state = playbackState?.state + val moving = if (state == null) false else + state == PlaybackState.STATE_PLAYING || + state == PlaybackState.STATE_BUFFERING || + state == PlaybackState.STATE_FAST_FORWARDING || + state == PlaybackState.STATE_REWINDING + return moving && listening + } + + /** Gets a listener to attach to the seek bar to handle seeking. */ + val seekBarListener: SeekBar.OnSeekBarChangeListener + get() { + return SeekBarChangeListener(this, bgExecutor) + } + + /** Gets a listener to attach to the seek bar to disable touch intercepting. */ + val seekBarTouchListener: View.OnTouchListener + get() { + return SeekBarTouchListener() + } + + private class SeekBarChangeListener( + val viewModel: SeekBarViewModel, + val bgExecutor: DelayableExecutor + ) : SeekBar.OnSeekBarChangeListener { + override fun onProgressChanged(bar: SeekBar, progress: Int, fromUser: Boolean) { + if (fromUser) { + bgExecutor.execute { + viewModel.onSeek(progress.toLong()) + } + } + } + override fun onStartTrackingTouch(bar: SeekBar) { + } + override fun onStopTrackingTouch(bar: SeekBar) { + val pos = bar.progress.toLong() + bgExecutor.execute { + viewModel.onSeek(pos) + } + } + } + + private class SeekBarTouchListener : View.OnTouchListener { + override fun onTouch(view: View, event: MotionEvent): Boolean { + view.parent.requestDisallowInterceptTouchEvent(true) + return view.onTouchEvent(event) + } + } + + /** State seen by seek bar UI. */ + data class Progress( + val enabled: Boolean, + val seekAvailable: Boolean, + val elapsedTime: Int?, + val duration: Int?, + val color: Int? + ) +} diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSMediaPlayer.java b/packages/SystemUI/src/com/android/systemui/qs/QSMediaPlayer.java index 8922e146cc50..339a408d501a 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/QSMediaPlayer.java +++ b/packages/SystemUI/src/com/android/systemui/qs/QSMediaPlayer.java @@ -16,11 +16,14 @@ package com.android.systemui.qs; +import static com.android.systemui.util.SysuiLifecycle.viewAttachLifecycle; + import android.app.Notification; import android.content.Context; import android.content.res.ColorStateList; import android.graphics.drawable.Drawable; import android.graphics.drawable.Icon; +import android.media.session.MediaController; import android.media.session.MediaSession; import android.util.Log; import android.view.View; @@ -28,12 +31,16 @@ import android.view.ViewGroup; import android.widget.ImageButton; import android.widget.ImageView; import android.widget.LinearLayout; +import android.widget.SeekBar; import android.widget.TextView; import com.android.settingslib.media.MediaDevice; import com.android.systemui.R; import com.android.systemui.media.MediaControlPanel; +import com.android.systemui.media.SeekBarObserver; +import com.android.systemui.media.SeekBarViewModel; import com.android.systemui.statusbar.NotificationMediaManager; +import com.android.systemui.util.concurrency.DelayableExecutor; import java.util.concurrent.Executor; @@ -54,6 +61,9 @@ public class QSMediaPlayer extends MediaControlPanel { }; private final QSPanel mParent; + private final DelayableExecutor mBackgroundExecutor; + private final SeekBarViewModel mSeekBarViewModel; + private final SeekBarObserver mSeekBarObserver; /** * Initialize quick shade version of player @@ -64,10 +74,20 @@ public class QSMediaPlayer extends MediaControlPanel { * @param backgroundExecutor */ public QSMediaPlayer(Context context, ViewGroup parent, NotificationMediaManager manager, - Executor foregroundExecutor, Executor backgroundExecutor) { + Executor foregroundExecutor, DelayableExecutor backgroundExecutor) { super(context, parent, manager, R.layout.qs_media_panel, QS_ACTION_IDS, foregroundExecutor, backgroundExecutor); mParent = (QSPanel) parent; + mBackgroundExecutor = backgroundExecutor; + mSeekBarViewModel = new SeekBarViewModel(backgroundExecutor); + mSeekBarObserver = new SeekBarObserver(getView()); + // Can't use the viewAttachLifecycle of media player because remove/add is used to adjust + // priority of players. As soon as it is removed, the lifecycle will end and the seek bar + // will stop updating. So, use the lifecycle of the parent instead. + mSeekBarViewModel.getProgress().observe(viewAttachLifecycle(parent), mSeekBarObserver); + SeekBar bar = getView().findViewById(R.id.media_progress_bar); + bar.setOnSeekBarChangeListener(mSeekBarViewModel.getSeekBarListener()); + bar.setOnTouchListener(mSeekBarViewModel.getSeekBarTouchListener()); } /** @@ -115,6 +135,11 @@ public class QSMediaPlayer extends MediaControlPanel { thisBtn.setVisibility(View.GONE); } + // Seek Bar + final MediaController controller = new MediaController(getContext(), token); + mBackgroundExecutor.execute( + () -> mSeekBarViewModel.updateController(controller, iconColor)); + // Set up long press menu View guts = mMediaNotifView.findViewById(R.id.media_guts); View options = mMediaNotifView.findViewById(R.id.qs_media_controls_options); @@ -155,4 +180,16 @@ public class QSMediaPlayer extends MediaControlPanel { return true; // consumed click }); } + + /** + * Sets the listening state of the player. + * + * Should be set to true when the QS panel is open. Otherwise, false. This is a signal to avoid + * unnecessary work when the QS panel is closed. + * + * @param listening True when player should be active. Otherwise, false. + */ + public void setListening(boolean listening) { + mSeekBarViewModel.setListening(listening); + } } diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java index 33cc086a8d9f..c8412fffd143 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java +++ b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java @@ -69,6 +69,7 @@ import com.android.systemui.statusbar.policy.BrightnessMirrorController; import com.android.systemui.statusbar.policy.BrightnessMirrorController.BrightnessMirrorListener; import com.android.systemui.tuner.TunerService; import com.android.systemui.tuner.TunerService.Tunable; +import com.android.systemui.util.concurrency.DelayableExecutor; import java.io.FileDescriptor; import java.io.PrintWriter; @@ -103,7 +104,7 @@ public class QSPanel extends LinearLayout implements Tunable, Callback, Brightne private final NotificationMediaManager mNotificationMediaManager; private final LocalBluetoothManager mLocalBluetoothManager; private final Executor mForegroundExecutor; - private final Executor mBackgroundExecutor; + private final DelayableExecutor mBackgroundExecutor; private LocalMediaManager mLocalMediaManager; private MediaDevice mDevice; private boolean mUpdateCarousel = false; @@ -166,7 +167,7 @@ public class QSPanel extends LinearLayout implements Tunable, Callback, Brightne QSLogger qsLogger, NotificationMediaManager notificationMediaManager, @Main Executor foregroundExecutor, - @Background Executor backgroundExecutor, + @Background DelayableExecutor backgroundExecutor, @Nullable LocalBluetoothManager localBluetoothManager ) { super(context, attrs); @@ -278,7 +279,7 @@ public class QSPanel extends LinearLayout implements Tunable, Callback, Brightne Log.d(TAG, "creating new player"); player = new QSMediaPlayer(mContext, this, mNotificationMediaManager, mForegroundExecutor, mBackgroundExecutor); - + player.setListening(mListening); if (player.isPlaying()) { mMediaCarousel.addView(player.getView(), 0, lp); // add in front } else { @@ -584,6 +585,9 @@ public class QSPanel extends LinearLayout implements Tunable, Callback, Brightne if (mListening) { refreshAllTiles(); } + for (QSMediaPlayer player : mMediaPlayers) { + player.setListening(mListening); + } } private String getTilesSpecs() { diff --git a/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanel.java b/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanel.java index be01d75552de..8fa64d3aaf0c 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanel.java +++ b/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanel.java @@ -43,6 +43,7 @@ import com.android.systemui.statusbar.NotificationMediaManager; import com.android.systemui.tuner.TunerService; import com.android.systemui.tuner.TunerService.Tunable; import com.android.systemui.util.Utils; +import com.android.systemui.util.concurrency.DelayableExecutor; import java.util.ArrayList; import java.util.Collection; @@ -81,7 +82,7 @@ public class QuickQSPanel extends QSPanel { QSLogger qsLogger, NotificationMediaManager notificationMediaManager, @Main Executor foregroundExecutor, - @Background Executor backgroundExecutor, + @Background DelayableExecutor backgroundExecutor, @Nullable LocalBluetoothManager localBluetoothManager ) { super(context, attrs, dumpManager, broadcastDispatcher, qsLogger, notificationMediaManager, diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java index fe2f1f3eefc5..1297f996b743 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java @@ -177,12 +177,32 @@ public class NotificationViewHierarchyManager implements DynamicPrivacyControlle currentUserId); ent.setSensitive(sensitive, deviceSensitive); ent.getRow().setNeedsRedaction(needsRedaction); - if (mGroupManager.isChildInGroupWithSummary(ent.getSbn())) { - NotificationEntry summary = mGroupManager.getGroupSummary(ent.getSbn()); - List<NotificationEntry> orderedChildren = mTmpChildOrderMap.get(summary); + boolean isChildInGroup = mGroupManager.isChildInGroupWithSummary(ent.getSbn()); + + boolean groupChangesAllowed = mVisualStabilityManager.areGroupChangesAllowed() + || !ent.hasFinishedInitialization(); + NotificationEntry parent = mGroupManager.getGroupSummary(ent.getSbn()); + if (!groupChangesAllowed) { + // We don't to change groups while the user is looking at them + boolean wasChildInGroup = ent.isChildInGroup(); + if (isChildInGroup && !wasChildInGroup) { + isChildInGroup = wasChildInGroup; + mVisualStabilityManager.addGroupChangesAllowedCallback(mEntryManager); + } else if (!isChildInGroup && wasChildInGroup) { + // We allow grouping changes if the group was collapsed + if (mGroupManager.isLogicalGroupExpanded(ent.getSbn())) { + isChildInGroup = wasChildInGroup; + parent = ent.getRow().getNotificationParent().getEntry(); + mVisualStabilityManager.addGroupChangesAllowedCallback(mEntryManager); + } + } + } + + if (isChildInGroup) { + List<NotificationEntry> orderedChildren = mTmpChildOrderMap.get(parent); if (orderedChildren == null) { orderedChildren = new ArrayList<>(); - mTmpChildOrderMap.put(summary, orderedChildren); + mTmpChildOrderMap.put(parent, orderedChildren); } orderedChildren.add(ent); } else { @@ -205,7 +225,7 @@ public class NotificationViewHierarchyManager implements DynamicPrivacyControlle } for (ExpandableNotificationRow viewToRemove : viewsToRemove) { - if (mGroupManager.isChildInGroupWithSummary(viewToRemove.getEntry().getSbn())) { + if (mEntryManager.getPendingOrActiveNotif(viewToRemove.getEntry().getKey()) != null) { // we are only transferring this notification to its parent, don't generate an // animation mListContainer.setChildTransferInProgress(true); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/ConversationNotifications.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/ConversationNotifications.kt index 7ef1d0eba3f1..1696f0715865 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/ConversationNotifications.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/ConversationNotifications.kt @@ -26,6 +26,7 @@ import com.android.internal.widget.ConversationLayout import com.android.systemui.statusbar.notification.collection.NotificationEntry import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow import com.android.systemui.statusbar.notification.row.NotificationContentView +import com.android.systemui.statusbar.phone.NotificationGroupManager import java.util.concurrent.ConcurrentHashMap import javax.inject.Inject import javax.inject.Singleton @@ -60,6 +61,7 @@ class ConversationNotificationProcessor @Inject constructor( @Singleton class ConversationNotificationManager @Inject constructor( private val notificationEntryManager: NotificationEntryManager, + private val notificationGroupManager: NotificationGroupManager, private val context: Context ) { // Need this state to be thread safe, since it's accessed from the ui thread @@ -81,10 +83,19 @@ class ConversationNotificationManager @Inject constructor( if (rankingMap.getRanking(entry.sbn.key, ranking) && ranking.isConversation) { val important = ranking.channel.isImportantConversation + var changed = false entry.row?.layouts?.asSequence() ?.flatMap(::getLayouts) ?.mapNotNull { it as? ConversationLayout } - ?.forEach { it.setIsImportantConversation(important) } + ?.forEach { + if (important != it.isImportantConversation) { + it.setIsImportantConversation(important) + changed = true + } + } + if (changed) { + notificationGroupManager.updateIsolation(entry) + } } } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java index 77376e595819..295adae9c9ee 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java @@ -252,7 +252,7 @@ public class NotificationEntryManager implements } @Override - public void onReorderingAllowed() { + public void onChangeAllowed() { updateNotifications("reordering is now allowed"); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/VisualStabilityManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/VisualStabilityManager.java index b357ada7bcf1..7ac59954cb57 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/VisualStabilityManager.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/VisualStabilityManager.java @@ -42,12 +42,14 @@ public class VisualStabilityManager implements OnHeadsUpChangedListener, Dumpabl private static final long TEMPORARY_REORDERING_ALLOWED_DURATION = 1000; - private final ArrayList<Callback> mCallbacks = new ArrayList<>(); + private final ArrayList<Callback> mReorderingAllowedCallbacks = new ArrayList<>(); + private final ArrayList<Callback> mGroupChangesAllowedCallbacks = new ArrayList<>(); private final Handler mHandler; private boolean mPanelExpanded; private boolean mScreenOn; private boolean mReorderingAllowed; + private boolean mGroupChangedAllowed; private boolean mIsTemporaryReorderingAllowed; private long mTemporaryReorderingStart; private VisibilityLocationProvider mVisibilityLocationProvider; @@ -83,13 +85,22 @@ public class VisualStabilityManager implements OnHeadsUpChangedListener, Dumpabl /** * Add a callback to invoke when reordering is allowed again. - * @param callback */ public void addReorderingAllowedCallback(Callback callback) { - if (mCallbacks.contains(callback)) { + if (mReorderingAllowedCallbacks.contains(callback)) { return; } - mCallbacks.add(callback); + mReorderingAllowedCallbacks.add(callback); + } + + /** + * Add a callback to invoke when group changes are allowed again. + */ + public void addGroupChangesAllowedCallback(Callback callback) { + if (mGroupChangesAllowedCallbacks.contains(callback)) { + return; + } + mGroupChangesAllowedCallbacks.add(callback); } /** @@ -97,7 +108,7 @@ public class VisualStabilityManager implements OnHeadsUpChangedListener, Dumpabl */ public void setPanelExpanded(boolean expanded) { mPanelExpanded = expanded; - updateReorderingAllowed(); + updateAllowedStates(); } /** @@ -105,7 +116,7 @@ public class VisualStabilityManager implements OnHeadsUpChangedListener, Dumpabl */ public void setScreenOn(boolean screenOn) { mScreenOn = screenOn; - updateReorderingAllowed(); + updateAllowedStates(); } /** @@ -116,25 +127,30 @@ public class VisualStabilityManager implements OnHeadsUpChangedListener, Dumpabl return; } mPulsing = pulsing; - updateReorderingAllowed(); + updateAllowedStates(); } - private void updateReorderingAllowed() { + private void updateAllowedStates() { boolean reorderingAllowed = (!mScreenOn || !mPanelExpanded || mIsTemporaryReorderingAllowed) && !mPulsing; boolean changedToTrue = reorderingAllowed && !mReorderingAllowed; mReorderingAllowed = reorderingAllowed; if (changedToTrue) { - notifyCallbacks(); + notifyChangeAllowed(mReorderingAllowedCallbacks); + } + boolean groupChangesAllowed = (!mScreenOn || !mPanelExpanded) && !mPulsing; + changedToTrue = groupChangesAllowed && !mGroupChangedAllowed; + mGroupChangedAllowed = groupChangesAllowed; + if (changedToTrue) { + notifyChangeAllowed(mGroupChangesAllowedCallbacks); } } - private void notifyCallbacks() { - for (int i = 0; i < mCallbacks.size(); i++) { - Callback callback = mCallbacks.get(i); - callback.onReorderingAllowed(); + private void notifyChangeAllowed(ArrayList<Callback> callbacks) { + for (int i = 0; i < callbacks.size(); i++) { + callbacks.get(i).onChangeAllowed(); } - mCallbacks.clear(); + callbacks.clear(); } /** @@ -145,6 +161,13 @@ public class VisualStabilityManager implements OnHeadsUpChangedListener, Dumpabl } /** + * @return whether changes in the grouping should be allowed right now. + */ + public boolean areGroupChangesAllowed() { + return mGroupChangedAllowed; + } + + /** * @return whether a specific notification is allowed to reorder. Certain notifications are * allowed to reorder even if {@link #isReorderingAllowed()} returns false, like newly added * notifications or heads-up notifications that are out of view. @@ -197,12 +220,12 @@ public class VisualStabilityManager implements OnHeadsUpChangedListener, Dumpabl mTemporaryReorderingStart = SystemClock.elapsedRealtime(); } mIsTemporaryReorderingAllowed = true; - updateReorderingAllowed(); + updateAllowedStates(); } private final Runnable mOnTemporaryReorderingExpired = () -> { mIsTemporaryReorderingAllowed = false; - updateReorderingAllowed(); + updateAllowedStates(); }; /** @@ -229,9 +252,9 @@ public class VisualStabilityManager implements OnHeadsUpChangedListener, Dumpabl public interface Callback { /** - * Called when reordering is allowed again. + * Called when changing is allowed again. */ - void onReorderingAllowed(); + void onChangeAllowed(); } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java index d22564b2a811..dd7be2775209 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java @@ -136,7 +136,6 @@ public final class NotificationEntry extends ListEntry { */ public EditedSuggestionInfo editedSuggestionInfo; - private NotificationEntry parent; // our parent (if we're in a group) private ExpandableNotificationRow row; // the outer expanded view private ExpandableNotificationRowController mRowController; @@ -710,7 +709,7 @@ public final class NotificationEntry extends ListEntry { } public boolean isChildInGroup() { - return parent == null; + return row != null && row.isChildInGroup(); } /** diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/ConversationCoordinator.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/ConversationCoordinator.kt new file mode 100644 index 000000000000..1bac938a9fca --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/ConversationCoordinator.kt @@ -0,0 +1,44 @@ +/* + * Copyright (C) 2020 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.notification.collection.coordinator + +import com.android.systemui.statusbar.notification.collection.NotifPipeline +import com.android.systemui.statusbar.notification.collection.NotificationEntry +import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifPromoter +import javax.inject.Inject +import javax.inject.Singleton + +/** + * A coordinator that elevates important conversation notifications + */ +@Singleton +class ConversationCoordinator @Inject constructor() : Coordinator { + + private val notificationPromoter = object : NotifPromoter(TAG) { + override fun shouldPromoteToTopLevel(entry: NotificationEntry): Boolean { + return entry.channel?.isImportantConversation == true + } + } + + override fun attach(pipeline: NotifPipeline) { + pipeline.addPromoter(notificationPromoter) + } + + companion object { + private const val TAG = "ConversationCoordinator" + } +} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/NotifCoordinators.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/NotifCoordinators.java index 03c0ae6fde50..2b279bbd553a 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/NotifCoordinators.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/NotifCoordinators.java @@ -56,6 +56,7 @@ public class NotifCoordinators implements Dumpable { DeviceProvisionedCoordinator deviceProvisionedCoordinator, BubbleCoordinator bubbleCoordinator, HeadsUpCoordinator headsUpCoordinator, + ConversationCoordinator conversationCoordinator, PreparationCoordinator preparationCoordinator) { dumpManager.registerDumpable(TAG, this); mCoordinators.add(new HideLocallyDismissedNotifsCoordinator()); @@ -66,6 +67,7 @@ public class NotifCoordinators implements Dumpable { mCoordinators.add(deviceProvisionedCoordinator); mCoordinators.add(bubbleCoordinator); if (featureFlags.isNewNotifPipelineRenderingEnabled()) { + mCoordinators.add(conversationCoordinator); mCoordinators.add(headsUpCoordinator); mCoordinators.add(preparationCoordinator); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/people/PeopleNotificationIdentifier.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/people/PeopleNotificationIdentifier.kt index be3873a5fd77..88cca43fd1a9 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/people/PeopleNotificationIdentifier.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/people/PeopleNotificationIdentifier.kt @@ -96,7 +96,7 @@ class PeopleNotificationIdentifierImpl @Inject constructor( return TYPE_NON_PERSON } - val childTypes = groupManager.getLogicalChildren(statusBarNotification) + val childTypes = groupManager.getChildren(statusBarNotification) ?.asSequence() ?.map { getPeopleNotificationType(it.sbn, it.ranking) } ?: return TYPE_NON_PERSON diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationConversationInfo.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationConversationInfo.java index 8e2bfb84e2dd..6fc1264d69e2 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationConversationInfo.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationConversationInfo.java @@ -20,7 +20,6 @@ import static android.app.Notification.EXTRA_IS_GROUP_CONVERSATION; import static android.app.NotificationManager.IMPORTANCE_DEFAULT; import static android.app.NotificationManager.IMPORTANCE_LOW; import static android.app.NotificationManager.IMPORTANCE_UNSPECIFIED; -import static android.provider.Settings.Secure.BUBBLE_IMPORTANT_CONVERSATIONS; import static com.android.systemui.Interpolators.FAST_OUT_SLOW_IN; @@ -44,7 +43,6 @@ import android.os.Handler; import android.os.Parcelable; import android.os.RemoteException; import android.os.UserHandle; -import android.provider.Settings; import android.service.notification.StatusBarNotification; import android.text.TextUtils; import android.transition.ChangeBounds; @@ -56,15 +54,12 @@ import android.util.Log; import android.util.Slog; import android.view.View; import android.view.accessibility.AccessibilityEvent; -import android.widget.Button; -import android.widget.ImageButton; import android.widget.ImageView; import android.widget.LinearLayout; import android.widget.TextView; import com.android.internal.annotations.VisibleForTesting; import com.android.settingslib.notification.ConversationIconFactory; -import com.android.settingslib.utils.ThreadUtils; import com.android.systemui.Dependency; import com.android.systemui.R; import com.android.systemui.statusbar.notification.VisualStabilityManager; @@ -517,7 +512,6 @@ public class NotificationConversationInfo extends LinearLayout implements bgHandler.post( new UpdateChannelRunnable(mINotificationManager, mPackageName, mAppUid, mSelectedAction, mNotificationChannel)); - mVisualStabilityManager.temporarilyAllowReordering(); } /** diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhone.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhone.java index 63fe7005e703..6b0df95f54dd 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhone.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhone.java @@ -337,7 +337,7 @@ public class HeadsUpManagerPhone extends HeadsUpManager implements Dumpable, // VisualStabilityManager.Callback overrides: @Override - public void onReorderingAllowed() { + public void onChangeAllowed() { mAnimationStateHandler.setHeadsUpGoingAwayAnimationsAllowed(false); for (NotificationEntry entry : mEntriesToRemoveWhenReorderingAllowed) { if (isAlerting(entry.getKey())) { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationGroupManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationGroupManager.java index ccf670708e44..84dd48b6eb6b 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationGroupManager.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationGroupManager.java @@ -17,6 +17,7 @@ package com.android.systemui.statusbar.phone; import android.annotation.Nullable; +import android.app.NotificationChannel; import android.service.notification.StatusBarNotification; import android.util.ArraySet; import android.util.Log; @@ -85,6 +86,17 @@ public class NotificationGroupManager implements OnHeadsUpChangedListener, State return group.expanded; } + /** + * @return if the group that this notification is associated with logically is expanded + */ + public boolean isLogicalGroupExpanded(StatusBarNotification sbn) { + NotificationGroup group = mGroupMap.get(sbn.getGroupKey()); + if (group == null) { + return false; + } + return group.expanded; + } + public void setGroupExpanded(StatusBarNotification sbn, boolean expanded) { NotificationGroup group = mGroupMap.get(getGroupKey(sbn)); if (group == null) { @@ -147,7 +159,15 @@ public class NotificationGroupManager implements OnHeadsUpChangedListener, State } } + /** + * Notify the group manager that a new entry was added + */ public void onEntryAdded(final NotificationEntry added) { + updateIsolation(added); + onEntryAddedInternal(added); + } + + private void onEntryAddedInternal(final NotificationEntry added) { if (added.isRowRemoved()) { added.setDebugThrowable(new Throwable()); } @@ -193,9 +213,7 @@ public class NotificationGroupManager implements OnHeadsUpChangedListener, State } private void onEntryBecomingChild(NotificationEntry entry) { - if (shouldIsolate(entry)) { - isolateNotification(entry); - } + updateIsolation(entry); } private void updateSuppression(NotificationGroup group) { @@ -242,15 +260,6 @@ public class NotificationGroupManager implements OnHeadsUpChangedListener, State return count; } - private NotificationEntry getIsolatedChild(String groupKey) { - for (StatusBarNotification sbn : mIsolatedEntries.values()) { - if (sbn.getGroupKey().equals(groupKey) && isIsolated(sbn.getKey())) { - return mGroupMap.get(sbn.getKey()).summary; - } - } - return null; - } - /** * Update an entry's group information * @param entry notification entry to update @@ -278,7 +287,7 @@ public class NotificationGroupManager implements OnHeadsUpChangedListener, State if (mGroupMap.get(getGroupKey(entry.getKey(), oldGroupKey)) != null) { onEntryRemovedInternal(entry, oldGroupKey, oldIsGroup, oldIsGroupSummary); } - onEntryAdded(entry); + onEntryAddedInternal(entry); mIsUpdatingUnchangedGroup = false; if (isIsolated(entry.getSbn().getKey())) { mIsolatedEntries.put(entry.getKey(), entry.getSbn()); @@ -413,14 +422,29 @@ public class NotificationGroupManager implements OnHeadsUpChangedListener, State return null; } ArrayList<NotificationEntry> children = new ArrayList<>(group.children.values()); - NotificationEntry isolatedChild = getIsolatedChild(summary.getGroupKey()); - if (isolatedChild != null) { - children.add(isolatedChild); + for (StatusBarNotification sbn : mIsolatedEntries.values()) { + if (sbn.getGroupKey().equals(summary.getGroupKey())) { + children.add(mGroupMap.get(sbn.getKey()).summary); + } } return children; } /** + * Get the children that are in the summary's group, not including those isolated. + * + * @param summary summary of a group + * @return list of the children + */ + public @Nullable ArrayList<NotificationEntry> getChildren(StatusBarNotification summary) { + NotificationGroup group = mGroupMap.get(summary.getGroupKey()); + if (group == null) { + return null; + } + return new ArrayList<>(group.children.values()); + } + + /** * If there is a {@link NotificationGroup} associated with the provided entry, this method * will update the suppression of that group. */ @@ -495,17 +519,7 @@ public class NotificationGroupManager implements OnHeadsUpChangedListener, State @Override public void onHeadsUpStateChanged(NotificationEntry entry, boolean isHeadsUp) { - onAlertStateChanged(entry, isHeadsUp); - } - - private void onAlertStateChanged(NotificationEntry entry, boolean isAlerting) { - if (isAlerting) { - if (shouldIsolate(entry)) { - isolateNotification(entry); - } - } else { - stopIsolatingNotification(entry); - } + updateIsolation(entry); } /** @@ -519,13 +533,17 @@ public class NotificationGroupManager implements OnHeadsUpChangedListener, State private boolean shouldIsolate(NotificationEntry entry) { StatusBarNotification sbn = entry.getSbn(); - NotificationGroup notificationGroup = mGroupMap.get(sbn.getGroupKey()); if (!sbn.isGroup() || sbn.getNotification().isGroupSummary()) { return false; } + NotificationChannel channel = entry.getChannel(); + if (channel != null && channel.isImportantConversation()) { + return true; + } if (mHeadsUpManager != null && !mHeadsUpManager.isAlerting(entry.getKey())) { return false; } + NotificationGroup notificationGroup = mGroupMap.get(sbn.getGroupKey()); return (sbn.getNotification().fullScreenIntent != null || notificationGroup == null || !notificationGroup.expanded @@ -545,7 +563,7 @@ public class NotificationGroupManager implements OnHeadsUpChangedListener, State mIsolatedEntries.put(sbn.getKey(), sbn); - onEntryAdded(entry); + onEntryAddedInternal(entry); // We also need to update the suppression of the old group, because this call comes // even before the groupManager knows about the notification at all. // When the notification gets added afterwards it is already isolated and therefore @@ -557,17 +575,31 @@ public class NotificationGroupManager implements OnHeadsUpChangedListener, State } /** + * Update the isolation of an entry, splitting it from the group. + */ + public void updateIsolation(NotificationEntry entry) { + boolean isIsolated = isIsolated(entry.getSbn().getKey()); + if (shouldIsolate(entry)) { + if (!isIsolated) { + isolateNotification(entry); + } + } else if (isIsolated) { + stopIsolatingNotification(entry); + } + } + + /** * Stop isolating a notification and re-group it with its original logical group. * * @param entry the notification to un-isolate */ private void stopIsolatingNotification(NotificationEntry entry) { StatusBarNotification sbn = entry.getSbn(); - if (mIsolatedEntries.containsKey(sbn.getKey())) { + if (isIsolated(sbn.getKey())) { // not isolated anymore, we need to update the groups onEntryRemovedInternal(entry, entry.getSbn()); mIsolatedEntries.remove(sbn.getKey()); - onEntryAdded(entry); + onEntryAddedInternal(entry); for (OnGroupChangeListener listener : mListeners) { listener.onGroupsChanged(); } diff --git a/packages/SystemUI/src/com/android/systemui/util/SysuiLifecycle.java b/packages/SystemUI/src/com/android/systemui/util/SysuiLifecycle.java index 711a0dfe1931..d73175310802 100644 --- a/packages/SystemUI/src/com/android/systemui/util/SysuiLifecycle.java +++ b/packages/SystemUI/src/com/android/systemui/util/SysuiLifecycle.java @@ -48,6 +48,9 @@ public class SysuiLifecycle { ViewLifecycle(View v) { v.addOnAttachStateChangeListener(this); + if (v.isAttachedToWindow()) { + mLifecycle.markState(RESUMED); + } } @NonNull diff --git a/packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsDialogTest.java b/packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsDialogTest.java new file mode 100644 index 000000000000..137a126f539d --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsDialogTest.java @@ -0,0 +1,145 @@ +/* + * Copyright (C) 2020 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.globalactions; + +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; + +import android.app.IActivityManager; +import android.app.admin.DevicePolicyManager; +import android.app.trust.TrustManager; +import android.content.ContentResolver; +import android.content.res.Resources; +import android.media.AudioManager; +import android.net.ConnectivityManager; +import android.os.UserManager; +import android.service.dreams.IDreamManager; +import android.telephony.TelephonyManager; +import android.testing.AndroidTestingRunner; +import android.testing.TestableLooper; +import android.view.IWindowManager; + +import androidx.test.filters.SmallTest; + +import com.android.internal.logging.MetricsLogger; +import com.android.internal.logging.UiEventLogger; +import com.android.internal.statusbar.IStatusBarService; +import com.android.internal.widget.LockPatternUtils; +import com.android.systemui.SysuiTestCase; +import com.android.systemui.broadcast.BroadcastDispatcher; +import com.android.systemui.colorextraction.SysuiColorExtractor; +import com.android.systemui.controls.controller.ControlsController; +import com.android.systemui.controls.management.ControlsListingController; +import com.android.systemui.controls.ui.ControlsUiController; +import com.android.systemui.plugins.ActivityStarter; +import com.android.systemui.plugins.GlobalActions; +import com.android.systemui.statusbar.BlurUtils; +import com.android.systemui.statusbar.NotificationShadeDepthController; +import com.android.systemui.statusbar.phone.NotificationShadeWindowController; +import com.android.systemui.statusbar.policy.ConfigurationController; +import com.android.systemui.statusbar.policy.KeyguardStateController; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; + +import java.util.concurrent.Executor; + +@SmallTest +@RunWith(AndroidTestingRunner.class) +@TestableLooper.RunWithLooper +public class GlobalActionsDialogTest extends SysuiTestCase { + private GlobalActionsDialog mGlobalActionsDialog; + + @Mock private GlobalActions.GlobalActionsManager mWindowManagerFuncs; + @Mock private AudioManager mAudioManager; + @Mock private IDreamManager mDreamManager; + @Mock private DevicePolicyManager mDevicePolicyManager; + @Mock private LockPatternUtils mLockPatternUtils; + @Mock private BroadcastDispatcher mBroadcastDispatcher; + @Mock private ConnectivityManager mConnectivityManager; + @Mock private TelephonyManager mTelephonyManager; + @Mock private ContentResolver mContentResolver; + @Mock private Resources mResources; + @Mock private ConfigurationController mConfigurationController; + @Mock private ActivityStarter mActivityStarter; + @Mock private KeyguardStateController mKeyguardStateController; + @Mock private UserManager mUserManager; + @Mock private TrustManager mTrustManager; + @Mock private IActivityManager mActivityManager; + @Mock private MetricsLogger mMetricsLogger; + @Mock private NotificationShadeDepthController mDepthController; + @Mock private SysuiColorExtractor mColorExtractor; + @Mock private IStatusBarService mStatusBarService; + @Mock private BlurUtils mBlurUtils; + @Mock private NotificationShadeWindowController mNotificationShadeWindowController; + @Mock private ControlsUiController mControlsUiController; + @Mock private IWindowManager mWindowManager; + @Mock private Executor mBackgroundExecutor; + @Mock private ControlsListingController mControlsListingController; + @Mock private ControlsController mControlsController; + @Mock private UiEventLogger mUiEventLogger; + + private TestableLooper mTestableLooper; + + @Before + public void setUp() throws Exception { + MockitoAnnotations.initMocks(this); + mTestableLooper = TestableLooper.get(this); + allowTestableLooperAsMainThread(); + mGlobalActionsDialog = new GlobalActionsDialog(mContext, + mWindowManagerFuncs, + mAudioManager, + mDreamManager, + mDevicePolicyManager, + mLockPatternUtils, + mBroadcastDispatcher, + mConnectivityManager, + mTelephonyManager, + mContentResolver, + null, + mResources, + mConfigurationController, + mActivityStarter, + mKeyguardStateController, + mUserManager, + mTrustManager, + mActivityManager, + null, + mMetricsLogger, + mDepthController, + mColorExtractor, + mStatusBarService, + mBlurUtils, + mNotificationShadeWindowController, + mControlsUiController, + mWindowManager, + mBackgroundExecutor, + mControlsListingController, + mControlsController, + mUiEventLogger + ); + } + @Test + public void testShouldLogVisibility() { + mGlobalActionsDialog.onShow(null); + verify(mUiEventLogger, times(1)) + .log(GlobalActionsDialog.GlobalActionsEvent.GA_POWER_MENU_OPEN); + } +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/SeekBarObserverTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/SeekBarObserverTest.kt new file mode 100644 index 000000000000..260f52070a70 --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/media/SeekBarObserverTest.kt @@ -0,0 +1,130 @@ +/* + * Copyright (C) 2020 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.media + +import android.graphics.Color +import android.content.res.ColorStateList +import android.testing.AndroidTestingRunner +import android.testing.TestableLooper +import android.view.View +import android.widget.SeekBar +import android.widget.TextView +import androidx.test.filters.SmallTest + +import com.android.systemui.R +import com.android.systemui.SysuiTestCase +import com.google.common.truth.Truth.assertThat + +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith +import org.mockito.Mock +import org.mockito.Mockito.mock +import org.mockito.Mockito.`when` as whenever + +@SmallTest +@RunWith(AndroidTestingRunner::class) +@TestableLooper.RunWithLooper +public class SeekBarObserverTest : SysuiTestCase() { + + private lateinit var observer: SeekBarObserver + @Mock private lateinit var mockView: View + private lateinit var seekBarView: SeekBar + private lateinit var elapsedTimeView: TextView + private lateinit var totalTimeView: TextView + + @Before + fun setUp() { + mockView = mock(View::class.java) + seekBarView = SeekBar(context) + elapsedTimeView = TextView(context) + totalTimeView = TextView(context) + whenever<SeekBar>( + mockView.findViewById(R.id.media_progress_bar)).thenReturn(seekBarView) + whenever<TextView>( + mockView.findViewById(R.id.media_elapsed_time)).thenReturn(elapsedTimeView) + whenever<TextView>(mockView.findViewById(R.id.media_total_time)).thenReturn(totalTimeView) + observer = SeekBarObserver(mockView) + } + + @Test + fun seekBarGone() { + // WHEN seek bar is disabled + val isEnabled = false + val data = SeekBarViewModel.Progress(isEnabled, false, null, null, null) + observer.onChanged(data) + // THEN seek bar visibility is set to GONE + assertThat(seekBarView.getVisibility()).isEqualTo(View.GONE) + assertThat(elapsedTimeView.getVisibility()).isEqualTo(View.GONE) + assertThat(totalTimeView.getVisibility()).isEqualTo(View.GONE) + } + + @Test + fun seekBarVisible() { + // WHEN seek bar is enabled + val isEnabled = true + val data = SeekBarViewModel.Progress(isEnabled, true, 3000, 12000, -1) + observer.onChanged(data) + // THEN seek bar is visible + assertThat(seekBarView.getVisibility()).isEqualTo(View.VISIBLE) + assertThat(elapsedTimeView.getVisibility()).isEqualTo(View.VISIBLE) + assertThat(totalTimeView.getVisibility()).isEqualTo(View.VISIBLE) + } + + @Test + fun seekBarProgress() { + // WHEN seek bar progress is about half + val data = SeekBarViewModel.Progress(true, true, 3000, 120000, -1) + observer.onChanged(data) + // THEN seek bar is visible + assertThat(seekBarView.progress).isEqualTo(100) + assertThat(seekBarView.max).isEqualTo(120000) + assertThat(elapsedTimeView.getText()).isEqualTo("00:03") + assertThat(totalTimeView.getText()).isEqualTo("02:00") + } + + @Test + fun seekBarDisabledWhenSeekNotAvailable() { + // WHEN seek is not available + val isSeekAvailable = false + val data = SeekBarViewModel.Progress(true, isSeekAvailable, 3000, 120000, -1) + observer.onChanged(data) + // THEN seek bar is not enabled + assertThat(seekBarView.isEnabled()).isFalse() + } + + @Test + fun seekBarEnabledWhenSeekNotAvailable() { + // WHEN seek is available + val isSeekAvailable = true + val data = SeekBarViewModel.Progress(true, isSeekAvailable, 3000, 120000, -1) + observer.onChanged(data) + // THEN seek bar is not enabled + assertThat(seekBarView.isEnabled()).isTrue() + } + + @Test + fun seekBarColor() { + // WHEN data included color + val data = SeekBarViewModel.Progress(true, true, 3000, 120000, Color.RED) + observer.onChanged(data) + // THEN seek bar is colored + val red = ColorStateList.valueOf(Color.RED) + assertThat(elapsedTimeView.getTextColors()).isEqualTo(red) + assertThat(totalTimeView.getTextColors()).isEqualTo(red) + } +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/SeekBarViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/SeekBarViewModelTest.kt new file mode 100644 index 000000000000..f316d0480fac --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/media/SeekBarViewModelTest.kt @@ -0,0 +1,375 @@ +/* + * Copyright (C) 2020 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.media + +import android.graphics.Color +import android.media.MediaMetadata +import android.media.session.MediaController +import android.media.session.PlaybackState +import android.testing.AndroidTestingRunner +import android.testing.TestableLooper +import android.widget.SeekBar +import androidx.arch.core.executor.ArchTaskExecutor +import androidx.arch.core.executor.TaskExecutor +import androidx.test.filters.SmallTest + +import com.android.systemui.SysuiTestCase +import com.android.systemui.util.concurrency.FakeExecutor +import com.android.systemui.util.time.FakeSystemClock +import com.google.common.truth.Truth.assertThat + +import org.junit.After +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith +import org.mockito.Mock +import org.mockito.Mockito.mock +import org.mockito.Mockito.never +import org.mockito.Mockito.verify +import org.mockito.Mockito.`when` as whenever + +@SmallTest +@RunWith(AndroidTestingRunner::class) +@TestableLooper.RunWithLooper +public class SeekBarViewModelTest : SysuiTestCase() { + + private lateinit var viewModel: SeekBarViewModel + private lateinit var fakeExecutor: FakeExecutor + private val taskExecutor: TaskExecutor = object : TaskExecutor() { + override fun executeOnDiskIO(runnable: Runnable) { + runnable.run() + } + override fun postToMainThread(runnable: Runnable) { + runnable.run() + } + override fun isMainThread(): Boolean { + return true + } + } + @Mock private lateinit var mockController: MediaController + @Mock private lateinit var mockTransport: MediaController.TransportControls + + @Before + fun setUp() { + fakeExecutor = FakeExecutor(FakeSystemClock()) + viewModel = SeekBarViewModel(fakeExecutor) + mockController = mock(MediaController::class.java) + mockTransport = mock(MediaController.TransportControls::class.java) + + // LiveData to run synchronously + ArchTaskExecutor.getInstance().setDelegate(taskExecutor) + } + + @After + fun tearDown() { + ArchTaskExecutor.getInstance().setDelegate(null) + } + + @Test + fun updateColor() { + viewModel.updateController(mockController, Color.RED) + assertThat(viewModel.progress.value!!.color).isEqualTo(Color.RED) + } + + @Test + fun updateDuration() { + // GIVEN that the duration is contained within the metadata + val duration = 12000L + val metadata = MediaMetadata.Builder().run { + putLong(MediaMetadata.METADATA_KEY_DURATION, duration) + build() + } + whenever(mockController.getMetadata()).thenReturn(metadata) + // WHEN the controller is updated + viewModel.updateController(mockController, Color.RED) + // THEN the duration is extracted + assertThat(viewModel.progress.value!!.duration).isEqualTo(duration) + assertThat(viewModel.progress.value!!.enabled).isTrue() + } + + @Test + fun updateDurationNegative() { + // GIVEN that the duration is negative + val duration = -1L + val metadata = MediaMetadata.Builder().run { + putLong(MediaMetadata.METADATA_KEY_DURATION, duration) + build() + } + whenever(mockController.getMetadata()).thenReturn(metadata) + // WHEN the controller is updated + viewModel.updateController(mockController, Color.RED) + // THEN the seek bar is disabled + assertThat(viewModel.progress.value!!.enabled).isFalse() + } + + @Test + fun updateDurationZero() { + // GIVEN that the duration is zero + val duration = 0L + val metadata = MediaMetadata.Builder().run { + putLong(MediaMetadata.METADATA_KEY_DURATION, duration) + build() + } + whenever(mockController.getMetadata()).thenReturn(metadata) + // WHEN the controller is updated + viewModel.updateController(mockController, Color.RED) + // THEN the seek bar is disabled + assertThat(viewModel.progress.value!!.enabled).isFalse() + } + + @Test + fun updateElapsedTime() { + // GIVEN that the PlaybackState contins the current position + val position = 200L + val state = PlaybackState.Builder().run { + setState(PlaybackState.STATE_PLAYING, position, 1f) + build() + } + whenever(mockController.getPlaybackState()).thenReturn(state) + // WHEN the controller is updated + viewModel.updateController(mockController, Color.RED) + // THEN elapsed time is captured + assertThat(viewModel.progress.value!!.elapsedTime).isEqualTo(200.toInt()) + } + + @Test + fun updateSeekAvailable() { + // GIVEN that seek is included in actions + val state = PlaybackState.Builder().run { + setActions(PlaybackState.ACTION_SEEK_TO) + build() + } + whenever(mockController.getPlaybackState()).thenReturn(state) + // WHEN the controller is updated + viewModel.updateController(mockController, Color.RED) + // THEN seek is available + assertThat(viewModel.progress.value!!.seekAvailable).isTrue() + } + + @Test + fun updateSeekNotAvailable() { + // GIVEN that seek is not included in actions + val state = PlaybackState.Builder().run { + setActions(PlaybackState.ACTION_PLAY) + build() + } + whenever(mockController.getPlaybackState()).thenReturn(state) + // WHEN the controller is updated + viewModel.updateController(mockController, Color.RED) + // THEN seek is not available + assertThat(viewModel.progress.value!!.seekAvailable).isFalse() + } + + @Test + fun handleSeek() { + whenever(mockController.getTransportControls()).thenReturn(mockTransport) + viewModel.updateController(mockController, Color.RED) + // WHEN user input is dispatched + val pos = 42L + viewModel.onSeek(pos) + fakeExecutor.runAllReady() + // THEN transport controls should be used + verify(mockTransport).seekTo(pos) + } + + @Test + fun handleProgressChangedUser() { + whenever(mockController.getTransportControls()).thenReturn(mockTransport) + viewModel.updateController(mockController, Color.RED) + // WHEN user starts dragging the seek bar + val pos = 42 + viewModel.seekBarListener.onProgressChanged(SeekBar(context), pos, true) + fakeExecutor.runAllReady() + // THEN transport controls should be used + verify(mockTransport).seekTo(pos.toLong()) + } + + @Test + fun handleProgressChangedOther() { + whenever(mockController.getTransportControls()).thenReturn(mockTransport) + viewModel.updateController(mockController, Color.RED) + // WHEN user starts dragging the seek bar + val pos = 42 + viewModel.seekBarListener.onProgressChanged(SeekBar(context), pos, false) + fakeExecutor.runAllReady() + // THEN transport controls should be used + verify(mockTransport, never()).seekTo(pos.toLong()) + } + + @Test + fun handleStartTrackingTouch() { + whenever(mockController.getTransportControls()).thenReturn(mockTransport) + viewModel.updateController(mockController, Color.RED) + // WHEN user starts dragging the seek bar + val pos = 42 + val bar = SeekBar(context).apply { + progress = pos + } + viewModel.seekBarListener.onStartTrackingTouch(bar) + fakeExecutor.runAllReady() + // THEN transport controls should be used + verify(mockTransport, never()).seekTo(pos.toLong()) + } + + @Test + fun handleStopTrackingTouch() { + whenever(mockController.getTransportControls()).thenReturn(mockTransport) + viewModel.updateController(mockController, Color.RED) + // WHEN user ends drag + val pos = 42 + val bar = SeekBar(context).apply { + progress = pos + } + viewModel.seekBarListener.onStopTrackingTouch(bar) + fakeExecutor.runAllReady() + // THEN transport controls should be used + verify(mockTransport).seekTo(pos.toLong()) + } + + @Test + fun queuePollTaskWhenPlaying() { + // GIVEN that the track is playing + val state = PlaybackState.Builder().run { + setState(PlaybackState.STATE_PLAYING, 100L, 1f) + build() + } + whenever(mockController.getPlaybackState()).thenReturn(state) + // WHEN the controller is updated + viewModel.updateController(mockController, Color.RED) + // THEN a task is queued + assertThat(fakeExecutor.numPending()).isEqualTo(1) + } + + @Test + fun noQueuePollTaskWhenStopped() { + // GIVEN that the playback state is stopped + val state = PlaybackState.Builder().run { + setState(PlaybackState.STATE_STOPPED, 200L, 1f) + build() + } + whenever(mockController.getPlaybackState()).thenReturn(state) + // WHEN updated + viewModel.updateController(mockController, Color.RED) + // THEN an update task is not queued + assertThat(fakeExecutor.numPending()).isEqualTo(0) + } + + @Test + fun queuePollTaskWhenListening() { + // GIVEN listening + viewModel.listening = true + with(fakeExecutor) { + advanceClockToNext() + runAllReady() + } + // AND the playback state is playing + val state = PlaybackState.Builder().run { + setState(PlaybackState.STATE_PLAYING, 200L, 1f) + build() + } + whenever(mockController.getPlaybackState()).thenReturn(state) + // WHEN updated + viewModel.updateController(mockController, Color.RED) + // THEN an update task is queued + assertThat(fakeExecutor.numPending()).isEqualTo(1) + } + + @Test + fun noQueuePollTaskWhenNotListening() { + // GIVEN not listening + viewModel.listening = false + with(fakeExecutor) { + advanceClockToNext() + runAllReady() + } + // AND the playback state is playing + val state = PlaybackState.Builder().run { + setState(PlaybackState.STATE_STOPPED, 200L, 1f) + build() + } + whenever(mockController.getPlaybackState()).thenReturn(state) + // WHEN updated + viewModel.updateController(mockController, Color.RED) + // THEN an update task is not queued + assertThat(fakeExecutor.numPending()).isEqualTo(0) + } + + @Test + fun pollTaskQueuesAnotherPollTaskWhenPlaying() { + // GIVEN that the track is playing + val state = PlaybackState.Builder().run { + setState(PlaybackState.STATE_PLAYING, 100L, 1f) + build() + } + whenever(mockController.getPlaybackState()).thenReturn(state) + viewModel.updateController(mockController, Color.RED) + // WHEN the next task runs + with(fakeExecutor) { + advanceClockToNext() + runAllReady() + } + // THEN another task is queued + assertThat(fakeExecutor.numPending()).isEqualTo(1) + } + + @Test + fun taskUpdatesProgress() { + // GIVEN that the PlaybackState contins the current position + val position = 200L + val state = PlaybackState.Builder().run { + setState(PlaybackState.STATE_PLAYING, position, 1f) + build() + } + whenever(mockController.getPlaybackState()).thenReturn(state) + viewModel.updateController(mockController, Color.RED) + // AND the playback state advances + val nextPosition = 300L + val nextState = PlaybackState.Builder().run { + setState(PlaybackState.STATE_PLAYING, nextPosition, 1f) + build() + } + whenever(mockController.getPlaybackState()).thenReturn(nextState) + // WHEN the task runs + with(fakeExecutor) { + advanceClockToNext() + runAllReady() + } + // THEN elapsed time is captured + assertThat(viewModel.progress.value!!.elapsedTime).isEqualTo(nextPosition.toInt()) + } + + @Test + fun startListeningQueuesPollTask() { + // GIVEN not listening + viewModel.listening = false + with(fakeExecutor) { + advanceClockToNext() + runAllReady() + } + // AND the playback state is playing + val state = PlaybackState.Builder().run { + setState(PlaybackState.STATE_STOPPED, 200L, 1f) + build() + } + whenever(mockController.getPlaybackState()).thenReturn(state) + viewModel.updateController(mockController, Color.RED) + // WHEN start listening + viewModel.listening = true + // THEN an update task is queued + assertThat(fakeExecutor.numPending()).isEqualTo(1) + } +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelTest.java index dbbbaac66554..862ebe13bd93 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelTest.java @@ -44,6 +44,7 @@ import com.android.systemui.qs.customize.QSCustomizer; import com.android.systemui.qs.logging.QSLogger; import com.android.systemui.qs.tileimpl.QSTileImpl; import com.android.systemui.statusbar.NotificationMediaManager; +import com.android.systemui.util.concurrency.DelayableExecutor; import org.junit.Before; import org.junit.Test; @@ -87,7 +88,7 @@ public class QSPanelTest extends SysuiTestCase { @Mock private Executor mForegroundExecutor; @Mock - private Executor mBackgroundExecutor; + private DelayableExecutor mBackgroundExecutor; @Mock private LocalBluetoothManager mLocalBluetoothManager; diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationViewHierarchyManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationViewHierarchyManagerTest.java index ea68516e639c..e55ea41d94e5 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationViewHierarchyManagerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationViewHierarchyManagerTest.java @@ -98,6 +98,8 @@ public class NotificationViewHierarchyManagerTest extends SysuiTestCase { mLockscreenUserManager); mDependency.injectTestDependency(NotificationGroupManager.class, mGroupManager); mDependency.injectTestDependency(VisualStabilityManager.class, mVisualStabilityManager); + when(mVisualStabilityManager.areGroupChangesAllowed()).thenReturn(true); + when(mVisualStabilityManager.isReorderingAllowed()).thenReturn(true); mHelper = new NotificationTestHelper(mContext, mDependency, TestableLooper.get(this)); diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/VisualStabilityManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/VisualStabilityManagerTest.java index 9079223649ff..3d06c57cac37 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/VisualStabilityManagerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/VisualStabilityManagerTest.java @@ -110,7 +110,7 @@ public class VisualStabilityManagerTest extends SysuiTestCase { mVisualStabilityManager.setScreenOn(true); mVisualStabilityManager.addReorderingAllowedCallback(mCallback); mVisualStabilityManager.setScreenOn(false); - verify(mCallback).onReorderingAllowed(); + verify(mCallback).onChangeAllowed(); } @Test @@ -119,7 +119,7 @@ public class VisualStabilityManagerTest extends SysuiTestCase { mVisualStabilityManager.setScreenOn(true); mVisualStabilityManager.addReorderingAllowedCallback(mCallback); mVisualStabilityManager.setPanelExpanded(false); - verify(mCallback).onReorderingAllowed(); + verify(mCallback).onChangeAllowed(); } @Test @@ -130,7 +130,7 @@ public class VisualStabilityManagerTest extends SysuiTestCase { mVisualStabilityManager.setScreenOn(false); mVisualStabilityManager.setScreenOn(true); mVisualStabilityManager.setScreenOn(false); - verify(mCallback).onReorderingAllowed(); + verify(mCallback).onChangeAllowed(); } @Test @@ -190,7 +190,7 @@ public class VisualStabilityManagerTest extends SysuiTestCase { mVisualStabilityManager.setPulsing(true); mVisualStabilityManager.addReorderingAllowedCallback(mCallback); mVisualStabilityManager.setPulsing(false); - verify(mCallback).onReorderingAllowed(); + verify(mCallback).onChangeAllowed(); } @Test @@ -204,7 +204,7 @@ public class VisualStabilityManagerTest extends SysuiTestCase { mVisualStabilityManager.temporarilyAllowReordering(); // THEN callbacks are notified that reordering is allowed - verify(mCallback).onReorderingAllowed(); + verify(mCallback).onChangeAllowed(); assertTrue(mVisualStabilityManager.isReorderingAllowed()); } @@ -218,7 +218,7 @@ public class VisualStabilityManagerTest extends SysuiTestCase { mVisualStabilityManager.temporarilyAllowReordering(); // THEN reordering is still not allowed - verify(mCallback, never()).onReorderingAllowed(); + verify(mCallback, never()).onChangeAllowed(); assertFalse(mVisualStabilityManager.isReorderingAllowed()); } diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/ConversationCoordinatorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/ConversationCoordinatorTest.kt new file mode 100644 index 000000000000..dfc627e14d8c --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/ConversationCoordinatorTest.kt @@ -0,0 +1,77 @@ +/* + * Copyright (C) 2020 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.notification.collection.coordinator + +import android.app.NotificationChannel +import android.testing.AndroidTestingRunner +import android.testing.TestableLooper +import androidx.test.filters.SmallTest +import com.android.systemui.SysuiTestCase +import com.android.systemui.statusbar.notification.collection.NotifPipeline +import com.android.systemui.statusbar.notification.collection.NotificationEntry +import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder +import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifPromoter +import org.junit.Assert.assertFalse +import org.junit.Assert.assertTrue +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith +import org.mockito.ArgumentCaptor +import org.mockito.Mock +import org.mockito.Mockito.verify +import org.mockito.MockitoAnnotations +import org.mockito.Mockito.`when` as whenever + +@SmallTest +@RunWith(AndroidTestingRunner::class) +@TestableLooper.RunWithLooper +class ConversationCoordinatorTest : SysuiTestCase() { + + private var coordinator: ConversationCoordinator = ConversationCoordinator() + + // captured listeners and pluggables: + private var promoter: NotifPromoter? = null + + @Mock + private val pipeline: NotifPipeline? = null + @Mock + private val channel: NotificationChannel? = null + private var entry: NotificationEntry? = null + + @Before + fun setUp() { + MockitoAnnotations.initMocks(this) + whenever(channel!!.isImportantConversation).thenReturn(true) + + coordinator.attach(pipeline!!) + + // capture arguments: + val notifPromoterCaptor = ArgumentCaptor.forClass(NotifPromoter::class.java) + verify(pipeline).addPromoter(notifPromoterCaptor.capture()) + promoter = notifPromoterCaptor.value + + entry = NotificationEntryBuilder().setChannel(channel).build() + } + + @Test + fun testPromotesCurrentHUN() { + + // only promote important conversations + assertTrue(promoter!!.shouldPromoteToTopLevel(entry)) + assertFalse(promoter!!.shouldPromoteToTopLevel(NotificationEntryBuilder().build())) + } +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationConversationInfoTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationConversationInfoTest.java index 6998edda3127..b6bd5e213dd4 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationConversationInfoTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationConversationInfoTest.java @@ -816,29 +816,4 @@ public class NotificationConversationInfoTest extends SysuiTestCase { verify(mMockINotificationManager, never()).createConversationNotificationChannelForPackage( anyString(), anyInt(), anyString(), any(), eq(CONVERSATION_ID)); } - - @Test - public void testAdjustImportanceTemporarilyAllowsReordering() { - mNotificationChannel.setImportance(IMPORTANCE_DEFAULT); - mConversationChannel.setImportance(IMPORTANCE_DEFAULT); - mNotificationInfo.bindNotification( - mShortcutManager, - mMockPackageManager, - mMockINotificationManager, - mVisualStabilityManager, - TEST_PACKAGE_NAME, - mNotificationChannel, - mEntry, - null, - null, - mIconFactory, - true); - - mNotificationInfo.findViewById(R.id.silence).performClick(); - mNotificationInfo.findViewById(R.id.done).performClick(); - - mTestableLooper.processAllMessages(); - - verify(mVisualStabilityManager).temporarilyAllowReordering(); - } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/SysuiLifecycleTest.java b/packages/SystemUI/tests/src/com/android/systemui/util/SysuiLifecycleTest.java index ce8085aa4862..486939d1f08e 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/util/SysuiLifecycleTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/util/SysuiLifecycleTest.java @@ -25,6 +25,8 @@ import static androidx.lifecycle.Lifecycle.Event.ON_STOP; import static com.android.systemui.util.SysuiLifecycle.viewAttachLifecycle; +import static com.google.common.truth.Truth.assertThat; + import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verify; @@ -35,12 +37,15 @@ import android.testing.TestableLooper.RunWithLooper; import android.testing.ViewUtils; import android.view.View; +import androidx.lifecycle.Lifecycle; import androidx.lifecycle.LifecycleEventObserver; import androidx.lifecycle.LifecycleOwner; import androidx.test.filters.SmallTest; import com.android.systemui.SysuiTestCase; +import org.junit.After; +import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; @@ -49,39 +54,122 @@ import org.junit.runner.RunWith; @SmallTest public class SysuiLifecycleTest extends SysuiTestCase { + private View mView; + + @Before + public void setUp() { + mView = new View(mContext); + } + + @After + public void tearDown() { + if (mView.isAttachedToWindow()) { + ViewUtils.detachView(mView); + TestableLooper.get(this).processAllMessages(); + } + } + @Test public void testAttach() { - View v = new View(mContext); LifecycleEventObserver observer = mock(LifecycleEventObserver.class); - LifecycleOwner lifecycle = viewAttachLifecycle(v); + LifecycleOwner lifecycle = viewAttachLifecycle(mView); lifecycle.getLifecycle().addObserver(observer); - ViewUtils.attachView(v); + ViewUtils.attachView(mView); TestableLooper.get(this).processAllMessages(); verify(observer).onStateChanged(eq(lifecycle), eq(ON_CREATE)); verify(observer).onStateChanged(eq(lifecycle), eq(ON_START)); verify(observer).onStateChanged(eq(lifecycle), eq(ON_RESUME)); - - ViewUtils.detachView(v); - TestableLooper.get(this).processAllMessages(); } @Test public void testDetach() { - View v = new View(mContext); LifecycleEventObserver observer = mock(LifecycleEventObserver.class); - LifecycleOwner lifecycle = viewAttachLifecycle(v); + LifecycleOwner lifecycle = viewAttachLifecycle(mView); lifecycle.getLifecycle().addObserver(observer); - ViewUtils.attachView(v); + ViewUtils.attachView(mView); TestableLooper.get(this).processAllMessages(); - ViewUtils.detachView(v); + ViewUtils.detachView(mView); TestableLooper.get(this).processAllMessages(); verify(observer).onStateChanged(eq(lifecycle), eq(ON_PAUSE)); verify(observer).onStateChanged(eq(lifecycle), eq(ON_STOP)); verify(observer).onStateChanged(eq(lifecycle), eq(ON_DESTROY)); } + + @Test + public void testStateBeforeAttach() { + // WHEN a lifecycle is obtained from a view + LifecycleOwner lifecycle = viewAttachLifecycle(mView); + // THEN the lifecycle state should be INITIAZED + assertThat(lifecycle.getLifecycle().getCurrentState()).isEqualTo( + Lifecycle.State.INITIALIZED); + } + + @Test + public void testStateAfterAttach() { + // WHEN a lifecycle is obtained from a view + LifecycleOwner lifecycle = viewAttachLifecycle(mView); + // AND the view is attached + ViewUtils.attachView(mView); + TestableLooper.get(this).processAllMessages(); + // THEN the lifecycle state should be RESUMED + assertThat(lifecycle.getLifecycle().getCurrentState()).isEqualTo(Lifecycle.State.RESUMED); + } + + @Test + public void testStateAfterDetach() { + // WHEN a lifecycle is obtained from a view + LifecycleOwner lifecycle = viewAttachLifecycle(mView); + // AND the view is detached + ViewUtils.attachView(mView); + TestableLooper.get(this).processAllMessages(); + ViewUtils.detachView(mView); + TestableLooper.get(this).processAllMessages(); + // THEN the lifecycle state should be DESTROYED + assertThat(lifecycle.getLifecycle().getCurrentState()).isEqualTo(Lifecycle.State.DESTROYED); + } + + @Test + public void testStateAfterReattach() { + // WHEN a lifecycle is obtained from a view + LifecycleOwner lifecycle = viewAttachLifecycle(mView); + // AND the view is re-attached + ViewUtils.attachView(mView); + TestableLooper.get(this).processAllMessages(); + ViewUtils.detachView(mView); + TestableLooper.get(this).processAllMessages(); + ViewUtils.attachView(mView); + TestableLooper.get(this).processAllMessages(); + // THEN the lifecycle state should still be DESTROYED, err RESUMED? + assertThat(lifecycle.getLifecycle().getCurrentState()).isEqualTo(Lifecycle.State.RESUMED); + } + + @Test + public void testStateWhenViewAlreadyAttached() { + // GIVEN that a view is already attached + ViewUtils.attachView(mView); + TestableLooper.get(this).processAllMessages(); + // WHEN a lifecycle is obtained from a view + LifecycleOwner lifecycle = viewAttachLifecycle(mView); + // THEN the lifecycle state should be RESUMED + assertThat(lifecycle.getLifecycle().getCurrentState()).isEqualTo(Lifecycle.State.RESUMED); + } + + @Test + public void testStateWhenViewAlreadyDetached() { + // GIVEN that a view is already detached + ViewUtils.attachView(mView); + TestableLooper.get(this).processAllMessages(); + ViewUtils.detachView(mView); + TestableLooper.get(this).processAllMessages(); + // WHEN a lifecycle is obtained from a view + LifecycleOwner lifecycle = viewAttachLifecycle(mView); + // THEN the lifecycle state should be INITIALIZED + assertThat(lifecycle.getLifecycle().getCurrentState()).isEqualTo( + Lifecycle.State.INITIALIZED); + } } diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java index 4ab035e7f44f..2eaa766ad32d 100644 --- a/services/core/java/com/android/server/ConnectivityService.java +++ b/services/core/java/com/android/server/ConnectivityService.java @@ -6313,7 +6313,8 @@ public class ConnectivityService extends IConnectivityManager.Stub && !nai.networkAgentConfig.allowBypass && nc.getOwnerUid() != Process.SYSTEM_UID && lp.getInterfaceName() != null - && (lp.hasIPv4DefaultRoute() || lp.hasIPv6DefaultRoute()); + && (lp.hasIPv4DefaultRoute() || lp.hasIpv4UnreachableDefaultRoute()) + && (lp.hasIPv6DefaultRoute() || lp.hasIpv6UnreachableDefaultRoute()); } private void updateUids(NetworkAgentInfo nai, NetworkCapabilities prevNc, diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java index e8d8ed7a462d..ed3b9f1fc265 100755 --- a/services/core/java/com/android/server/notification/NotificationManagerService.java +++ b/services/core/java/com/android/server/notification/NotificationManagerService.java @@ -6227,7 +6227,7 @@ public class NotificationManagerService extends SystemService { cancelNotificationLocked( r, mSendDelete, mReason, mRank, mCount, wasPosted, listenerName); cancelGroupChildrenLocked(r, mCallingUid, mCallingPid, listenerName, - mSendDelete, childrenFlagChecker); + mSendDelete, childrenFlagChecker, mReason); updateLightsLocked(); if (mShortcutHelper != null) { mShortcutHelper.maybeListenForShortcutChangesForBubbles(r, @@ -6687,7 +6687,7 @@ public class NotificationManagerService extends SystemService { // notification was a summary and its group key changed. if (oldIsSummary && (!isSummary || !oldGroup.equals(group))) { cancelGroupChildrenLocked(old, callingUid, callingPid, null, false /* sendDelete */, - null); + null, REASON_APP_CANCEL); } } @@ -7892,7 +7892,7 @@ public class NotificationManagerService extends SystemService { final int M = canceledNotifications.size(); for (int i = 0; i < M; i++) { cancelGroupChildrenLocked(canceledNotifications.get(i), callingUid, callingPid, - listenerName, false /* sendDelete */, flagChecker); + listenerName, false /* sendDelete */, flagChecker, reason); } updateLightsLocked(); } @@ -7963,7 +7963,7 @@ public class NotificationManagerService extends SystemService { // Warning: The caller is responsible for invoking updateLightsLocked(). @GuardedBy("mNotificationLock") private void cancelGroupChildrenLocked(NotificationRecord r, int callingUid, int callingPid, - String listenerName, boolean sendDelete, FlagChecker flagChecker) { + String listenerName, boolean sendDelete, FlagChecker flagChecker, int reason) { Notification n = r.getNotification(); if (!n.isGroupSummary()) { return; @@ -7977,30 +7977,33 @@ public class NotificationManagerService extends SystemService { } cancelGroupChildrenByListLocked(mNotificationList, r, callingUid, callingPid, listenerName, - sendDelete, true, flagChecker); + sendDelete, true, flagChecker, reason); cancelGroupChildrenByListLocked(mEnqueuedNotifications, r, callingUid, callingPid, - listenerName, sendDelete, false, flagChecker); + listenerName, sendDelete, false, flagChecker, reason); } @GuardedBy("mNotificationLock") private void cancelGroupChildrenByListLocked(ArrayList<NotificationRecord> notificationList, NotificationRecord parentNotification, int callingUid, int callingPid, - String listenerName, boolean sendDelete, boolean wasPosted, FlagChecker flagChecker) { + String listenerName, boolean sendDelete, boolean wasPosted, FlagChecker flagChecker, + int reason) { final String pkg = parentNotification.getSbn().getPackageName(); final int userId = parentNotification.getUserId(); - final int reason = REASON_GROUP_SUMMARY_CANCELED; + final int childReason = REASON_GROUP_SUMMARY_CANCELED; for (int i = notificationList.size() - 1; i >= 0; i--) { final NotificationRecord childR = notificationList.get(i); final StatusBarNotification childSbn = childR.getSbn(); if ((childSbn.isGroup() && !childSbn.getNotification().isGroupSummary()) && childR.getGroupKey().equals(parentNotification.getGroupKey()) && (childR.getFlags() & FLAG_FOREGROUND_SERVICE) == 0 - && (flagChecker == null || flagChecker.apply(childR.getFlags()))) { + && (flagChecker == null || flagChecker.apply(childR.getFlags())) + && (!childR.getChannel().isImportantConversation() + || reason != REASON_CANCEL)) { EventLogTags.writeNotificationCancel(callingUid, callingPid, pkg, childSbn.getId(), - childSbn.getTag(), userId, 0, 0, reason, listenerName); + childSbn.getTag(), userId, 0, 0, childReason, listenerName); notificationList.remove(i); mNotificationsByKey.remove(childR.getKey()); - cancelNotificationLocked(childR, sendDelete, reason, wasPosted, listenerName); + cancelNotificationLocked(childR, sendDelete, childReason, wasPosted, listenerName); } } } diff --git a/services/core/java/com/android/server/pm/LauncherAppsService.java b/services/core/java/com/android/server/pm/LauncherAppsService.java index 1b271a739777..1d5c30438870 100644 --- a/services/core/java/com/android/server/pm/LauncherAppsService.java +++ b/services/core/java/com/android/server/pm/LauncherAppsService.java @@ -47,6 +47,7 @@ import android.content.pm.PackageManagerInternal; import android.content.pm.ParceledListSlice; import android.content.pm.ResolveInfo; import android.content.pm.ShortcutInfo; +import android.content.pm.ShortcutQueryWrapper; import android.content.pm.ShortcutServiceInternal; import android.content.pm.ShortcutServiceInternal.ShortcutChangeListener; import android.content.pm.UserInfo; @@ -698,13 +699,19 @@ public class LauncherAppsService extends SystemService { } @Override - public ParceledListSlice getShortcuts(String callingPackage, long changedSince, - String packageName, List shortcutIds, List<LocusId> locusIds, - ComponentName componentName, int flags, UserHandle targetUser) { + public ParceledListSlice getShortcuts(@NonNull final String callingPackage, + @NonNull final ShortcutQueryWrapper query, @NonNull final UserHandle targetUser) { ensureShortcutPermission(callingPackage); if (!canAccessProfile(targetUser.getIdentifier(), "Cannot get shortcuts")) { return new ParceledListSlice<>(Collections.EMPTY_LIST); } + + final long changedSince = query.getChangedSince(); + final String packageName = query.getPackage(); + final List<String> shortcutIds = query.getShortcutIds(); + final List<LocusId> locusIds = query.getLocusIds(); + final ComponentName componentName = query.getActivity(); + final int flags = query.getQueryFlags(); if (shortcutIds != null && packageName == null) { throw new IllegalArgumentException( "To query by shortcut ID, package name must also be set"); @@ -723,16 +730,17 @@ public class LauncherAppsService extends SystemService { } @Override - public void registerShortcutChangeCallback(String callingPackage, long changedSince, - String packageName, List shortcutIds, List<LocusId> locusIds, - ComponentName componentName, int flags, IShortcutChangeCallback callback) { + public void registerShortcutChangeCallback(@NonNull final String callingPackage, + @NonNull final ShortcutQueryWrapper query, + @NonNull final IShortcutChangeCallback callback) { + ensureShortcutPermission(callingPackage); - if (shortcutIds != null && packageName == null) { + if (query.getShortcutIds() != null && query.getPackage() == null) { throw new IllegalArgumentException( "To query by shortcut ID, package name must also be set"); } - if (locusIds != null && packageName == null) { + if (query.getLocusIds() != null && query.getPackage() == null) { throw new IllegalArgumentException( "To query by locus ID, package name must also be set"); } @@ -744,10 +752,7 @@ public class LauncherAppsService extends SystemService { user = null; } - // TODO: When ShortcutQueryWrapper (ag/10323729) is available, pass that directly. - ShortcutChangeHandler.QueryInfo query = new ShortcutChangeHandler.QueryInfo( - changedSince, packageName, shortcutIds, locusIds, componentName, flags, user); - mShortcutChangeHandler.addShortcutChangeCallback(callback, query); + mShortcutChangeHandler.addShortcutChangeCallback(callback, query, user); } @Override @@ -1081,9 +1086,11 @@ public class LauncherAppsService extends SystemService { new RemoteCallbackList<>(); public synchronized void addShortcutChangeCallback(IShortcutChangeCallback callback, - QueryInfo query) { + ShortcutQueryWrapper query, UserHandle user) { mCallbacks.unregister(callback); - mCallbacks.register(callback, query); + mCallbacks.register(callback, new QueryInfo(query.getChangedSince(), + query.getPackage(), query.getShortcutIds(), query.getLocusIds(), + query.getActivity(), query.getQueryFlags(), user)); } public synchronized void removeShortcutChangeCallback( diff --git a/services/core/java/com/android/server/wm/ActivityStack.java b/services/core/java/com/android/server/wm/ActivityStack.java index 10b335e583b0..a446720c1c39 100644 --- a/services/core/java/com/android/server/wm/ActivityStack.java +++ b/services/core/java/com/android/server/wm/ActivityStack.java @@ -3553,9 +3553,8 @@ class ActivityStack extends Task { } } - void reparent(DisplayContent newParent, boolean onTop) { - // Real parent of stack is within display object, so we have to delegate re-parenting there. - newParent.moveStackToDisplay(this, onTop); + void reparent(TaskDisplayArea newParent, boolean onTop) { + reparent(newParent, onTop ? POSITION_TOP : POSITION_BOTTOM); } private void updateSurfaceBounds() { diff --git a/services/core/java/com/android/server/wm/ActivityStackSupervisor.java b/services/core/java/com/android/server/wm/ActivityStackSupervisor.java index 2c7ce9104c3c..8af862473386 100644 --- a/services/core/java/com/android/server/wm/ActivityStackSupervisor.java +++ b/services/core/java/com/android/server/wm/ActivityStackSupervisor.java @@ -412,7 +412,7 @@ public class ActivityStackSupervisor implements RecentTasks.Callbacks { final ActivityStack stack = (ActivityStack) task; stack.setWindowingMode(WINDOWING_MODE_FULLSCREEN); if (mToDisplay.getDisplayId() != stack.getDisplayId()) { - mToDisplay.moveStackToDisplay(stack, mOnTop); + stack.reparent(mToDisplay.getDefaultTaskDisplayArea(), mOnTop); } else if (mOnTop) { mToDisplay.mTaskContainers.positionStackAtTop(stack, false /* includingParents */); @@ -566,8 +566,8 @@ public class ActivityStackSupervisor implements RecentTasks.Callbacks { } void moveRecentsStackToFront(String reason) { - final ActivityStack recentsStack = mRootWindowContainer.getDefaultDisplay().getStack( - WINDOWING_MODE_UNDEFINED, ACTIVITY_TYPE_RECENTS); + final ActivityStack recentsStack = mRootWindowContainer.getDefaultTaskDisplayArea() + .getStack(WINDOWING_MODE_UNDEFINED, ACTIVITY_TYPE_RECENTS); if (recentsStack != null) { recentsStack.moveToFront(reason); } @@ -2613,7 +2613,7 @@ public class ActivityStackSupervisor implements RecentTasks.Callbacks { // from whatever is started from the recents activity, so move the home stack // forward. // TODO (b/115289124): Multi-display supports for recents. - mRootWindowContainer.getDefaultDisplay().mTaskContainers.moveHomeStackToFront( + mRootWindowContainer.getDefaultTaskDisplayArea().moveHomeStackToFront( "startActivityFromRecents"); } diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java index 7803c7370dac..0b1968765300 100644 --- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java +++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java @@ -4283,9 +4283,9 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { long ident = Binder.clearCallingIdentity(); try { synchronized (mGlobalLock) { - final DisplayContent dc = mRootWindowContainer.getDefaultDisplay(); - final Task primary = dc.getRootSplitScreenPrimaryTask(); - final Task secondary = dc.getTask(t -> t.mCreatedByOrganizer && t.isRootTask() + final TaskDisplayArea tc = mRootWindowContainer.getDefaultTaskDisplayArea(); + final Task primary = tc.getRootSplitScreenPrimaryTask(); + final Task secondary = tc.getTask(t -> t.mCreatedByOrganizer && t.isRootTask() && t.inSplitScreenSecondaryWindowingMode()); if (primary == null || secondary == null) { return; @@ -4301,7 +4301,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { if (otherRect == null) { // Temporary estimation... again this is just for tests. otherRect = new Rect(secondary.getBounds()); - if (dc.getBounds().width() > dc.getBounds().height()) { + if (tc.getBounds().width() > tc.getBounds().height()) { otherRect.left = primaryRect.right + 6; } else { otherRect.top = primaryRect.bottom + 6; diff --git a/services/core/java/com/android/server/wm/DisplayAreaPolicyBuilder.java b/services/core/java/com/android/server/wm/DisplayAreaPolicyBuilder.java index 682a14220dff..e8becfa27fac 100644 --- a/services/core/java/com/android/server/wm/DisplayAreaPolicyBuilder.java +++ b/services/core/java/com/android/server/wm/DisplayAreaPolicyBuilder.java @@ -54,7 +54,7 @@ import java.util.Map; * - DisplayArea.Root * - Magnification * - DisplayArea.Tokens (Wallpapers are attached here) - * - TaskContainers + * - TaskDisplayArea * - DisplayArea.Tokens (windows above Tasks up to IME are attached here) * - ImeContainers * - DisplayArea.Tokens (windows above IME up to TYPE_ACCESSIBILITY_OVERLAY attached here) diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java index 55b7be779690..85517a44d656 100644 --- a/services/core/java/com/android/server/wm/DisplayContent.java +++ b/services/core/java/com/android/server/wm/DisplayContent.java @@ -575,13 +575,6 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo private RootWindowContainer mRootWindowContainer; - /** - * All of the stacks on this display. Order matters, topmost stack is in front of all other - * stacks, bottommost behind. Accessed directly by ActivityManager package classes. Any calls - * changing the list should also call {@link #onStackOrderChanged()}. - */ - private ArrayList<OnStackOrderChangedListener> mStackOrderChangedCallbacks = new ArrayList<>(); - /** Array of all UIDs that are present on the display. */ private IntArray mDisplayAccessUIDs = new IntArray(); @@ -2062,23 +2055,6 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo return (mDisplay.getFlags() & FLAG_PRIVATE) != 0; } - ActivityStack getRootHomeTask() { - return mTaskContainers.getRootHomeTask(); - } - - /** @return The primary split-screen task, and {@code null} otherwise. */ - @Nullable ActivityStack getRootSplitScreenPrimaryTask() { - return mTaskContainers.getRootSplitScreenPrimaryTask(); - } - - ActivityStack getRootPinnedTask() { - return mTaskContainers.getRootPinnedTask(); - } - - boolean hasPinnedTask() { - return mTaskContainers.getRootPinnedTask() != null; - } - /** * Returns the topmost stack on the display that is compatible with the input windowing mode and * activity type. Null is no compatible stack on the display. @@ -2095,32 +2071,11 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo return mTaskContainers.mChildren.get(index); } - int getIndexOf(ActivityStack stack) { - return mTaskContainers.getIndexOf(stack); - } - - void removeStack(ActivityStack stack) { - mTaskContainers.removeChild(stack); - } - - @VisibleForTesting - WindowList<ActivityStack> getStacks() { - return mTaskContainers.mChildren; - } - @VisibleForTesting ActivityStack getTopStack() { return mTaskContainers.getTopStack(); } - ArrayList<Task> getVisibleTasks() { - return mTaskContainers.getVisibleTasks(); - } - - SurfaceControl getSplitScreenDividerAnchor() { - return mTaskContainers.getSplitScreenDividerAnchor(); - } - /** * The value is only valid in the scope {@link #onRequestedOverrideConfigurationChanged} of the * changing hierarchy and the {@link #onConfigurationChanged} of its children. @@ -2409,8 +2364,13 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo out.set(mDisplayFrames.mStable); } - void moveStackToDisplay(ActivityStack stack, boolean onTop) { - stack.reparent(mTaskContainers, onTop ? POSITION_TOP: POSITION_BOTTOM); + /** + * Get the default display area on the display dedicated to app windows. This one should be used + * only as a fallback location for activity launches when no target display area is specified, + * or for cases when multi-instance is not supported yet (like Split-screen, PiP or Recents). + */ + TaskDisplayArea getDefaultTaskDisplayArea() { + return mTaskContainers; } @Override @@ -2473,7 +2433,7 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo */ Task findTaskForResizePoint(int x, int y) { final int delta = dipToPixel(RESIZE_HANDLE_WIDTH_IN_DP, mDisplayMetrics); - return mTmpTaskForResizePointSearchResult.process(mTaskContainers, x, y, delta); + return mTmpTaskForResizePointSearchResult.process(getDefaultTaskDisplayArea(), x, y, delta); } void updateTouchExcludeRegion() { @@ -2512,8 +2472,9 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo mTouchExcludeRegion.op(mTmpRegion, Region.Op.UNION); } amendWindowTapExcludeRegion(mTouchExcludeRegion); - // TODO(multi-display): Support docked stacks on secondary displays. - if (mDisplayId == DEFAULT_DISPLAY && mTaskContainers.isSplitScreenModeActivated()) { + // TODO(multi-display): Support docked stacks on secondary displays & task containers. + if (mDisplayId == DEFAULT_DISPLAY + && getDefaultTaskDisplayArea().isSplitScreenModeActivated()) { mDividerControllerLocked.getTouchRegion(mTmpRect); mTmpRegion.set(mTmpRect); mTouchExcludeRegion.op(mTmpRegion, Op.UNION); @@ -2908,20 +2869,22 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo pw.println(); // Dump stack references - final ActivityStack homeStack = getRootHomeTask(); + final ActivityStack homeStack = getDefaultTaskDisplayArea().getRootHomeTask(); if (homeStack != null) { pw.println(prefix + "homeStack=" + homeStack.getName()); } - final ActivityStack pinnedStack = getRootPinnedTask(); + final ActivityStack pinnedStack = getDefaultTaskDisplayArea().getRootPinnedTask(); if (pinnedStack != null) { pw.println(prefix + "pinnedStack=" + pinnedStack.getName()); } - final ActivityStack splitScreenPrimaryStack = getRootSplitScreenPrimaryTask(); + final ActivityStack splitScreenPrimaryStack = getDefaultTaskDisplayArea() + .getRootSplitScreenPrimaryTask(); if (splitScreenPrimaryStack != null) { pw.println(prefix + "splitScreenPrimaryStack=" + splitScreenPrimaryStack.getName()); } - final ActivityStack recentsStack = - getStack(WINDOWING_MODE_UNDEFINED, ACTIVITY_TYPE_RECENTS); + // TODO: Support recents on non-default task containers + final ActivityStack recentsStack = getDefaultTaskDisplayArea().getStack( + WINDOWING_MODE_UNDEFINED, ACTIVITY_TYPE_RECENTS); if (recentsStack != null) { pw.println(prefix + "recentsStack=" + recentsStack.getName()); } @@ -2955,12 +2918,6 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo return "Display " + mDisplayId + " name=\"" + mDisplayInfo.name + "\""; } - /** Returns true if the stack in the windowing mode is visible. */ - boolean isStackVisible(int windowingMode) { - final ActivityStack stack = mTaskContainers.getTopStackInWindowingMode(windowingMode); - return stack != null && stack.isVisible(); - } - /** Find the visible, touch-deliverable window under the given point */ WindowState getTouchableWinAtPointLocked(float xf, float yf) { final int x = (int) xf; @@ -4367,7 +4324,7 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo // We skip IME windows so they're processed just above their target, except // in split-screen mode where we process the IME containers above the docked divider. return dc.mInputMethodTarget != null - && !dc.mTaskContainers.isSplitScreenModeActivated(); + && !dc.getDefaultTaskDisplayArea().isSplitScreenModeActivated(); } /** Like {@link #forAllWindows}, but ignores {@link #skipImeWindowsDuringTraversal} */ @@ -5262,7 +5219,7 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo // released (no more ActivityStack). But, we cannot release it at that moment or the // related WindowContainer will also be removed. So, we set display as removed after // reparenting stack finished. - final DisplayContent toDisplay = mRootWindowContainer.getDefaultDisplay(); + final TaskDisplayArea toTaskDisplayArea = mRootWindowContainer.getDefaultTaskDisplayArea(); mRootWindowContainer.mStackSupervisor.beginDeferResume(); try { int numStacks = getStackCount(); @@ -5276,10 +5233,10 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo // If default display is in split-window mode, set windowing mode of the stack // to split-screen secondary. Otherwise, set the windowing mode to undefined by // default to let stack inherited the windowing mode from the new display. - final int windowingMode = toDisplay.mTaskContainers.isSplitScreenModeActivated() + final int windowingMode = toTaskDisplayArea.isSplitScreenModeActivated() ? WINDOWING_MODE_SPLIT_SCREEN_SECONDARY : WINDOWING_MODE_UNDEFINED; - stack.reparent(toDisplay, true /* onTop */); + stack.reparent(toTaskDisplayArea, true /* onTop */); stack.setWindowingMode(windowingMode); lastReparentedStack = stack; } @@ -5392,34 +5349,6 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo mSleeping = asleep; } - /** - * Adds a listener to be notified whenever the stack order in the display changes. Currently - * only used by the {@link RecentsAnimation} to determine whether to interrupt and cancel the - * current animation when the system state changes. - */ - void registerStackOrderChangedListener(OnStackOrderChangedListener listener) { - if (!mStackOrderChangedCallbacks.contains(listener)) { - mStackOrderChangedCallbacks.add(listener); - } - } - - /** - * Removes a previously registered stack order change listener. - */ - void unregisterStackOrderChangedListener(OnStackOrderChangedListener listener) { - mStackOrderChangedCallbacks.remove(listener); - } - - /** - * Notifies of a stack order change - * @param stack The stack which triggered the order change - */ - void onStackOrderChanged(ActivityStack stack) { - for (int i = mStackOrderChangedCallbacks.size() - 1; i >= 0; i--) { - mStackOrderChangedCallbacks.get(i).onStackOrderChanged(stack); - } - } - void setDisplayToSingleTaskInstance() { final int childCount = getStackCount(); if (childCount > 1) { @@ -5448,22 +5377,6 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo } /** - * Callback for when the order of the stacks in the display changes. - */ - interface OnStackOrderChangedListener { - void onStackOrderChanged(ActivityStack stack); - } - - public void dumpStacks(PrintWriter pw) { - for (int i = getStackCount() - 1; i >= 0; --i) { - pw.print(getStackAt(i).getRootTaskId()); - if (i > 0) { - pw.print(","); - } - } - } - - /** * Similar to {@link RootWindowContainer#isAnyNonToastWindowVisibleForUid(int)}, but * used for pid. */ diff --git a/services/core/java/com/android/server/wm/DisplayPolicy.java b/services/core/java/com/android/server/wm/DisplayPolicy.java index 221258e94cb2..264da9fc681b 100644 --- a/services/core/java/com/android/server/wm/DisplayPolicy.java +++ b/services/core/java/com/android/server/wm/DisplayPolicy.java @@ -2653,8 +2653,8 @@ public class DisplayPolicy { if (mStatusBarController.setBarShowingLw(true)) { changes |= FINISH_LAYOUT_REDO_LAYOUT; } - } else if (topIsFullscreen - && !mDisplayContent.isStackVisible(WINDOWING_MODE_SPLIT_SCREEN_PRIMARY)) { + } else if (topIsFullscreen && !mDisplayContent.getDefaultTaskDisplayArea() + .isStackVisible(WINDOWING_MODE_SPLIT_SCREEN_PRIMARY)) { if (DEBUG_LAYOUT) Slog.v(TAG, "** HIDING status bar"); if (mStatusBarController.setBarShowingLw(false)) { changes |= FINISH_LAYOUT_REDO_LAYOUT; @@ -3462,10 +3462,10 @@ public class DisplayPolicy { } private Pair<Integer, WindowState> updateSystemBarsLw(WindowState win, int oldVis, int vis) { - final boolean dockedStackVisible = - mDisplayContent.isStackVisible(WINDOWING_MODE_SPLIT_SCREEN_PRIMARY); - final boolean freeformStackVisible = - mDisplayContent.isStackVisible(WINDOWING_MODE_FREEFORM); + final boolean dockedStackVisible = mDisplayContent.getDefaultTaskDisplayArea() + .isStackVisible(WINDOWING_MODE_SPLIT_SCREEN_PRIMARY); + final boolean freeformStackVisible = mDisplayContent.getDefaultTaskDisplayArea() + .isStackVisible(WINDOWING_MODE_FREEFORM); final boolean resizing = mDisplayContent.getDockedDividerController().isResizing(); // We need to force system bars when the docked stack is visible, when the freeform stack diff --git a/services/core/java/com/android/server/wm/DisplayRotation.java b/services/core/java/com/android/server/wm/DisplayRotation.java index af89a05bfa62..bef80f0a230a 100644 --- a/services/core/java/com/android/server/wm/DisplayRotation.java +++ b/services/core/java/com/android/server/wm/DisplayRotation.java @@ -594,7 +594,8 @@ public class DisplayRotation { // In the presence of the PINNED stack or System Alert windows we unfortunately can not // seamlessly rotate. - if (mDisplayContent.hasPinnedTask() || mDisplayContent.hasAlertWindowSurfaces()) { + if (mDisplayContent.getDefaultTaskDisplayArea().hasPinnedTask() + || mDisplayContent.hasAlertWindowSurfaces()) { return false; } diff --git a/services/core/java/com/android/server/wm/InsetsPolicy.java b/services/core/java/com/android/server/wm/InsetsPolicy.java index 007af249c580..e4e57168efe7 100644 --- a/services/core/java/com/android/server/wm/InsetsPolicy.java +++ b/services/core/java/com/android/server/wm/InsetsPolicy.java @@ -295,10 +295,10 @@ class InsetsPolicy { } private boolean forceShowsSystemBarsForWindowingMode() { - final boolean isDockedStackVisible = - mDisplayContent.isStackVisible(WINDOWING_MODE_SPLIT_SCREEN_PRIMARY); - final boolean isFreeformStackVisible = - mDisplayContent.isStackVisible(WINDOWING_MODE_FREEFORM); + final boolean isDockedStackVisible = mDisplayContent.getDefaultTaskDisplayArea() + .isStackVisible(WINDOWING_MODE_SPLIT_SCREEN_PRIMARY); + final boolean isFreeformStackVisible = mDisplayContent.getDefaultTaskDisplayArea() + .isStackVisible(WINDOWING_MODE_FREEFORM); final boolean isResizing = mDisplayContent.getDockedDividerController().isResizing(); // We need to force system bars when the docked stack is visible, when the freeform stack diff --git a/services/core/java/com/android/server/wm/KeyguardController.java b/services/core/java/com/android/server/wm/KeyguardController.java index a936e74bf694..57a54d0e84ef 100644 --- a/services/core/java/com/android/server/wm/KeyguardController.java +++ b/services/core/java/com/android/server/wm/KeyguardController.java @@ -406,11 +406,12 @@ class KeyguardController { // show on top of the lock screen. In this can we want to dismiss the docked // stack since it will be complicated/risky to try to put the activity on top // of the lock screen in the right fullscreen configuration. - final DisplayContent display = mRootWindowContainer.getDefaultDisplay(); - if (!display.mTaskContainers.isSplitScreenModeActivated()) { + final TaskDisplayArea taskDisplayArea = mRootWindowContainer + .getDefaultTaskDisplayArea(); + if (!taskDisplayArea.isSplitScreenModeActivated()) { return; } - display.mTaskContainers.onSplitScreenModeDismissed(); + taskDisplayArea.onSplitScreenModeDismissed(); } } diff --git a/services/core/java/com/android/server/wm/PinnedStackController.java b/services/core/java/com/android/server/wm/PinnedStackController.java index 66dbfd5f2a84..02a27410dc38 100644 --- a/services/core/java/com/android/server/wm/PinnedStackController.java +++ b/services/core/java/com/android/server/wm/PinnedStackController.java @@ -286,7 +286,8 @@ class PinnedStackController { } try { final Rect animatingBounds = new Rect(); - final ActivityStack pinnedStack = mDisplayContent.getRootPinnedTask(); + final ActivityStack pinnedStack = mDisplayContent.getDefaultTaskDisplayArea() + .getRootPinnedTask(); if (pinnedStack != null) { pinnedStack.getAnimationOrCurrentBounds(animatingBounds); } diff --git a/services/core/java/com/android/server/wm/RecentsAnimation.java b/services/core/java/com/android/server/wm/RecentsAnimation.java index 08b0abf5e9be..a031fe82d48b 100644 --- a/services/core/java/com/android/server/wm/RecentsAnimation.java +++ b/services/core/java/com/android/server/wm/RecentsAnimation.java @@ -52,14 +52,14 @@ import com.android.server.wm.RecentsAnimationController.RecentsAnimationCallback * cleanup. See {@link com.android.server.wm.RecentsAnimationController}. */ class RecentsAnimation implements RecentsAnimationCallbacks, - DisplayContent.OnStackOrderChangedListener { + TaskDisplayArea.OnStackOrderChangedListener { private static final String TAG = RecentsAnimation.class.getSimpleName(); private final ActivityTaskManagerService mService; private final ActivityStackSupervisor mStackSupervisor; private final ActivityStartController mActivityStartController; private final WindowManagerService mWindowManager; - private final DisplayContent mDefaultDisplay; + private final TaskDisplayArea mDefaultTaskDisplayArea; private final Intent mTargetIntent; private final ComponentName mRecentsComponent; private final @Nullable String mRecentsFeatureId; @@ -84,7 +84,7 @@ class RecentsAnimation implements RecentsAnimationCallbacks, int recentsUid, @Nullable WindowProcessController caller) { mService = atm; mStackSupervisor = stackSupervisor; - mDefaultDisplay = mService.mRootWindowContainer.getDefaultDisplay(); + mDefaultTaskDisplayArea = mService.mRootWindowContainer.getDefaultTaskDisplayArea(); mActivityStartController = activityStartController; mWindowManager = wm; mTargetIntent = targetIntent; @@ -107,7 +107,7 @@ class RecentsAnimation implements RecentsAnimationCallbacks, void preloadRecentsActivity() { ProtoLog.d(WM_DEBUG_RECENTS_ANIMATIONS, "Preload recents with %s", mTargetIntent); - ActivityStack targetStack = mDefaultDisplay.getStack(WINDOWING_MODE_UNDEFINED, + ActivityStack targetStack = mDefaultTaskDisplayArea.getStack(WINDOWING_MODE_UNDEFINED, mTargetActivityType); ActivityRecord targetActivity = getTargetActivity(targetStack); if (targetActivity != null) { @@ -128,7 +128,8 @@ class RecentsAnimation implements RecentsAnimationCallbacks, // Create the activity record. Because the activity is invisible, this doesn't really // start the client. startRecentsActivityInBackground("preloadRecents"); - targetStack = mDefaultDisplay.getStack(WINDOWING_MODE_UNDEFINED, mTargetActivityType); + targetStack = mDefaultTaskDisplayArea.getStack(WINDOWING_MODE_UNDEFINED, + mTargetActivityType); targetActivity = getTargetActivity(targetStack); if (targetActivity == null) { Slog.w(TAG, "Cannot start " + mTargetIntent); @@ -176,12 +177,11 @@ class RecentsAnimation implements RecentsAnimationCallbacks, } // If the activity is associated with the recents stack, then try and get that first - ActivityStack targetStack = mDefaultDisplay.getStack(WINDOWING_MODE_UNDEFINED, + ActivityStack targetStack = mDefaultTaskDisplayArea.getStack(WINDOWING_MODE_UNDEFINED, mTargetActivityType); ActivityRecord targetActivity = getTargetActivity(targetStack); final boolean hasExistingActivity = targetActivity != null; if (hasExistingActivity) { - final TaskDisplayArea taskDisplayArea = targetActivity.getDisplayArea(); mRestoreTargetBehindStack = getStackAbove(targetStack); if (mRestoreTargetBehindStack == null) { notifyAnimationCancelBeforeStart(recentsAnimationRunner); @@ -209,7 +209,7 @@ class RecentsAnimation implements RecentsAnimationCallbacks, try { if (hasExistingActivity) { // Move the recents activity into place for the animation if it is not top most - mDefaultDisplay.mTaskContainers.moveStackBehindBottomMostVisibleStack(targetStack); + mDefaultTaskDisplayArea.moveStackBehindBottomMostVisibleStack(targetStack); ProtoLog.d(WM_DEBUG_RECENTS_ANIMATIONS, "Moved stack=%s behind stack=%s", targetStack, getStackAbove(targetStack)); @@ -225,10 +225,10 @@ class RecentsAnimation implements RecentsAnimationCallbacks, startRecentsActivityInBackground("startRecentsActivity_noTargetActivity"); // Move the recents activity into place for the animation - targetStack = mDefaultDisplay.getStack(WINDOWING_MODE_UNDEFINED, + targetStack = mDefaultTaskDisplayArea.getStack(WINDOWING_MODE_UNDEFINED, mTargetActivityType); targetActivity = getTargetActivity(targetStack); - mDefaultDisplay.mTaskContainers.moveStackBehindBottomMostVisibleStack(targetStack); + mDefaultTaskDisplayArea.moveStackBehindBottomMostVisibleStack(targetStack); ProtoLog.d(WM_DEBUG_RECENTS_ANIMATIONS, "Moved stack=%s behind stack=%s", targetStack, getStackAbove(targetStack)); @@ -251,7 +251,7 @@ class RecentsAnimation implements RecentsAnimationCallbacks, mWindowManager.cancelRecentsAnimation(REORDER_MOVE_TO_ORIGINAL_POSITION, "startRecentsActivity"); mWindowManager.initializeRecentsAnimation(mTargetActivityType, recentsAnimationRunner, - this, mDefaultDisplay.getDisplayId(), + this, mDefaultTaskDisplayArea.getDisplayId(), mStackSupervisor.mRecentTasks.getRecentTaskIds(), targetActivity); // If we updated the launch-behind state, update the visibility of the activities after @@ -262,7 +262,7 @@ class RecentsAnimation implements RecentsAnimationCallbacks, START_TASK_TO_FRONT, targetActivity); // Register for stack order changes - mDefaultDisplay.registerStackOrderChangedListener(this); + mDefaultTaskDisplayArea.registerStackOrderChangedListener(this); } catch (Exception e) { Slog.e(TAG, "Failed to start recents activity", e); throw e; @@ -280,7 +280,7 @@ class RecentsAnimation implements RecentsAnimationCallbacks, mWindowManager.getRecentsAnimationController(), reorderMode); // Unregister for stack order changes - mDefaultDisplay.unregisterStackOrderChangedListener(this); + mDefaultTaskDisplayArea.unregisterStackOrderChangedListener(this); final RecentsAnimationController controller = mWindowManager.getRecentsAnimationController(); @@ -308,7 +308,7 @@ class RecentsAnimation implements RecentsAnimationCallbacks, try { mWindowManager.cleanupRecentsAnimation(reorderMode); - final ActivityStack targetStack = mDefaultDisplay.getStack( + final ActivityStack targetStack = mDefaultTaskDisplayArea.getStack( WINDOWING_MODE_UNDEFINED, mTargetActivityType); // Prefer to use the original target activity instead of top activity because // we may have moved another task to top (starting 3p launcher). @@ -422,7 +422,7 @@ class RecentsAnimation implements RecentsAnimationCallbacks, @Override public void onStackOrderChanged(ActivityStack stack) { ProtoLog.d(WM_DEBUG_RECENTS_ANIMATIONS, "onStackOrderChanged(): stack=%s", stack); - if (mDefaultDisplay.getIndexOf(stack) == -1 || !stack.shouldBeVisible(null)) { + if (mDefaultTaskDisplayArea.getIndexOf(stack) == -1 || !stack.shouldBeVisible(null)) { // The stack is not visible, so ignore this change return; } @@ -480,8 +480,8 @@ class RecentsAnimation implements RecentsAnimationCallbacks, * @return The top stack that is not always-on-top. */ private ActivityStack getTopNonAlwaysOnTopStack() { - for (int i = mDefaultDisplay.getStackCount() - 1; i >= 0; i--) { - final ActivityStack s = mDefaultDisplay.getStackAt(i); + for (int i = mDefaultTaskDisplayArea.getStackCount() - 1; i >= 0; i--) { + final ActivityStack s = mDefaultTaskDisplayArea.getStackAt(i); if (s.getWindowConfiguration().isAlwaysOnTop()) { continue; } diff --git a/services/core/java/com/android/server/wm/RecentsAnimationController.java b/services/core/java/com/android/server/wm/RecentsAnimationController.java index a30b70de267d..84229f003032 100644 --- a/services/core/java/com/android/server/wm/RecentsAnimationController.java +++ b/services/core/java/com/android/server/wm/RecentsAnimationController.java @@ -340,9 +340,11 @@ public class RecentsAnimationController implements DeathRecipient { // Make leashes for each of the visible/target tasks and add it to the recents animation to // be started - final ArrayList<Task> visibleTasks = mDisplayContent.getVisibleTasks(); - final ActivityStack targetStack = mDisplayContent.getStack(WINDOWING_MODE_UNDEFINED, - targetActivityType); + // TODO(multi-display-area): Support Recents on multiple task display areas + final ArrayList<Task> visibleTasks = mDisplayContent.getDefaultTaskDisplayArea() + .getVisibleTasks(); + final ActivityStack targetStack = mDisplayContent.getDefaultTaskDisplayArea() + .getStack(WINDOWING_MODE_UNDEFINED, targetActivityType); if (targetStack != null) { final PooledConsumer c = PooledLambda.obtainConsumer((t, outList) -> { if (!outList.contains(t)) outList.add(t); }, PooledLambda.__(Task.class), @@ -385,7 +387,8 @@ public class RecentsAnimationController implements DeathRecipient { } // Save the minimized home height - mMinimizedHomeBounds = mDisplayContent.getRootHomeTask().getBounds(); + mMinimizedHomeBounds = mDisplayContent.getDefaultTaskDisplayArea().getRootHomeTask() + .getBounds(); mService.mWindowPlacerLocked.performSurfacePlacement(); diff --git a/services/core/java/com/android/server/wm/ResetTargetTaskHelper.java b/services/core/java/com/android/server/wm/ResetTargetTaskHelper.java index 770c08889ab9..32de699eaae9 100644 --- a/services/core/java/com/android/server/wm/ResetTargetTaskHelper.java +++ b/services/core/java/com/android/server/wm/ResetTargetTaskHelper.java @@ -232,10 +232,11 @@ class ResetTargetTaskHelper { } final ActivityTaskManagerService atmService = mTargetStack.mAtmService; - DisplayContent display = mTargetStack.getDisplay(); - final boolean singleTaskInstanceDisplay = display.isSingleTaskInstance(); + TaskDisplayArea taskDisplayArea = mTargetStack.getDisplayArea(); + final boolean singleTaskInstanceDisplay = + taskDisplayArea.mDisplayContent.isSingleTaskInstance(); if (singleTaskInstanceDisplay) { - display = atmService.mRootWindowContainer.getDefaultDisplay(); + taskDisplayArea = atmService.mRootWindowContainer.getDefaultTaskDisplayArea(); } final int windowingMode = mTargetStack.getWindowingMode(); @@ -246,7 +247,7 @@ class ResetTargetTaskHelper { final boolean alwaysCreateTask = DisplayContent.alwaysCreateStack(windowingMode, activityType); final Task task = alwaysCreateTask - ? display.getBottomMostTask() : mTargetStack.getBottomMostTask(); + ? taskDisplayArea.getBottomMostTask() : mTargetStack.getBottomMostTask(); Task targetTask = null; if (task != null && r.taskAffinity.equals(task.affinity)) { // If the activity currently at the bottom has the same task affinity as @@ -257,7 +258,7 @@ class ResetTargetTaskHelper { } if (targetTask == null) { if (alwaysCreateTask) { - targetTask = display.mTaskContainers.getOrCreateStack(windowingMode, + targetTask = taskDisplayArea.getOrCreateStack(windowingMode, activityType, false /* onTop */); } else { targetTask = mTargetStack.reuseOrCreateTask(r.info, null /*intent*/, diff --git a/services/core/java/com/android/server/wm/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java index 2764b121cc00..2eeda4dcfeed 100644 --- a/services/core/java/com/android/server/wm/RootWindowContainer.java +++ b/services/core/java/com/android/server/wm/RootWindowContainer.java @@ -1369,11 +1369,11 @@ class RootWindowContainer extends WindowContainer<DisplayContent> } calculateDefaultMinimalSizeOfResizeableTasks(); - final DisplayContent defaultDisplay = getDefaultDisplay(); - - defaultDisplay.mTaskContainers.getOrCreateStack(WINDOWING_MODE_FULLSCREEN, - ACTIVITY_TYPE_HOME, ON_TOP); - positionChildAt(POSITION_TOP, defaultDisplay, false /* includingParents */); + final TaskDisplayArea defaultTaskDisplayArea = getDefaultTaskDisplayArea(); + defaultTaskDisplayArea.getOrCreateStack(WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_HOME, + ON_TOP); + positionChildAt(POSITION_TOP, defaultTaskDisplayArea.mDisplayContent, + false /* includingParents */); } // TODO(multi-display): Look at all callpoints to make sure they make sense in multi-display. @@ -1382,6 +1382,16 @@ class RootWindowContainer extends WindowContainer<DisplayContent> } /** + * Get the default display area on the device dedicated to app windows. This one should be used + * only as a fallback location for activity launches when no target display area is specified, + * or for cases when multi-instance is not supported yet (like Split-screen, Freeform, PiP or + * Recents). + */ + TaskDisplayArea getDefaultTaskDisplayArea() { + return mDefaultDisplay.getDefaultTaskDisplayArea(); + } + + /** * Get an existing instance of {@link DisplayContent} that has the given uniqueId. Unique ID is * defined in {@link DisplayInfo#uniqueId}. * @@ -1436,12 +1446,8 @@ class RootWindowContainer extends WindowContainer<DisplayContent> return displayContent; } - ActivityRecord getDefaultDisplayHomeActivity() { - return getDefaultDisplayHomeActivityForUser(mCurrentUser); - } - ActivityRecord getDefaultDisplayHomeActivityForUser(int userId) { - return getDisplayContent(DEFAULT_DISPLAY).mTaskContainers.getHomeActivityForUser(userId); + return getDefaultTaskDisplayArea().getHomeActivityForUser(userId); } boolean startHomeOnAllDisplays(int userId, String reason) { @@ -1972,8 +1978,8 @@ class RootWindowContainer extends WindowContainer<DisplayContent> final int focusStackId = topFocusedStack != null ? topFocusedStack.getRootTaskId() : INVALID_TASK_ID; // We dismiss the docked stack whenever we switch users. - if (getDefaultDisplay().mTaskContainers.isSplitScreenModeActivated()) { - getDefaultDisplay().mTaskContainers.onSplitScreenModeDismissed(); + if (getDefaultTaskDisplayArea().isSplitScreenModeActivated()) { + getDefaultTaskDisplayArea().onSplitScreenModeDismissed(); } // Also dismiss the pinned stack whenever we switch users. Removing the pinned stack will // also cause all tasks to be moved to the fullscreen stack at a position that is @@ -1995,7 +2001,7 @@ class RootWindowContainer extends WindowContainer<DisplayContent> final int restoreStackId = mUserStackInFront.get(userId); ActivityStack stack = getStack(restoreStackId); if (stack == null) { - stack = getDefaultDisplay().mTaskContainers.getOrCreateRootHomeTask(); + stack = getDefaultTaskDisplayArea().getOrCreateRootHomeTask(); } final boolean homeInFront = stack.isActivityTypeHome(); if (stack.isOnHomeDisplay()) { @@ -2018,7 +2024,7 @@ class RootWindowContainer extends WindowContainer<DisplayContent> void updateUserStack(int userId, ActivityStack stack) { if (userId != mCurrentUser) { if (stack == null) { - stack = getDefaultDisplay().mTaskContainers.getOrCreateRootHomeTask(); + stack = getDefaultTaskDisplayArea().getOrCreateRootHomeTask(); } mUserStackInFront.put(userId, stack.getRootTaskId()); @@ -2061,7 +2067,7 @@ class RootWindowContainer extends WindowContainer<DisplayContent> return; } - stack.reparent(displayContent.mDisplayContent, onTop); + stack.reparent(displayContent.getDefaultTaskDisplayArea(), onTop); // TODO(multi-display): resize stacks properly if moved from split-screen. } @@ -2155,8 +2161,8 @@ class RootWindowContainer extends WindowContainer<DisplayContent> // Looking up task on preferred display first final DisplayContent preferredDisplay = getDisplayContent(preferredDisplayId); if (preferredDisplay != null) { - preferredDisplay.mTaskContainers.findTaskLocked(r, true /* isPreferredDisplay */, - mTmpFindTaskResult); + preferredDisplay.getDefaultTaskDisplayArea().findTaskLocked(r, + true /* isPreferredDisplay */, mTmpFindTaskResult); if (mTmpFindTaskResult.mIdealMatch) { return mTmpFindTaskResult.mRecord; } @@ -2168,7 +2174,7 @@ class RootWindowContainer extends WindowContainer<DisplayContent> continue; } - display.mTaskContainers.findTaskLocked(r, false /* isPreferredDisplay */, + display.getDefaultTaskDisplayArea().findTaskLocked(r, false /* isPreferredDisplay */, mTmpFindTaskResult); if (mTmpFindTaskResult.mIdealMatch) { return mTmpFindTaskResult.mRecord; @@ -2788,8 +2794,10 @@ class RootWindowContainer extends WindowContainer<DisplayContent> } final DisplayContent display = getDisplayContentOrCreate(displayId); if (display != null) { - stack = display.mTaskContainers.getOrCreateStack(r, options, candidateTask, - activityType, onTop); + // Falling back to default task container + final TaskDisplayArea taskDisplayArea = display.getDefaultTaskDisplayArea(); + stack = taskDisplayArea.getOrCreateStack(r, options, candidateTask, activityType, + onTop); if (stack != null) { return stack; } @@ -2835,7 +2843,7 @@ class RootWindowContainer extends WindowContainer<DisplayContent> if (container == null || !canLaunchOnDisplay(r, container.mDisplayContent.mDisplayId)) { - container = getDefaultDisplay().mTaskContainers; + container = getDefaultTaskDisplayArea(); if (windowingMode == WindowConfiguration.WINDOWING_MODE_UNDEFINED) { windowingMode = container.resolveWindowingMode(r, options, candidateTask, activityType); @@ -2887,7 +2895,7 @@ class RootWindowContainer extends WindowContainer<DisplayContent> // it to the target display. if (candidateTask.isRootTask()) { final ActivityStack stack = candidateTask.getStack(); - displayContent.moveStackToDisplay(stack, true /* onTop */); + stack.reparent(displayContent.getDefaultTaskDisplayArea(), true /* onTop */); return stack; } } @@ -2918,7 +2926,8 @@ class RootWindowContainer extends WindowContainer<DisplayContent> final int activityType = options != null && options.getLaunchActivityType() != ACTIVITY_TYPE_UNDEFINED ? options.getLaunchActivityType() : r.getActivityType(); - return displayContent.createStack(windowingMode, activityType, true /*onTop*/); + final TaskDisplayArea taskDisplayArea = displayContent.getDefaultTaskDisplayArea(); + return taskDisplayArea.createStack(windowingMode, activityType, true /*onTop*/); } return null; @@ -2989,7 +2998,8 @@ class RootWindowContainer extends WindowContainer<DisplayContent> if (preferredDisplayArea == null) { // Stack is currently detached because it is being removed. Use the previous display it // was on. - preferredDisplayArea = getDisplayContent(currentFocus.mPrevDisplayId).mTaskContainers; + preferredDisplayArea = getDisplayContent(currentFocus.mPrevDisplayId) + .getDefaultTaskDisplayArea(); } final ActivityStack preferredFocusableStack = preferredDisplayArea.getNextFocusableStack( currentFocus, ignoreCurrent); @@ -3010,7 +3020,7 @@ class RootWindowContainer extends WindowContainer<DisplayContent> // We've already checked this one continue; } - final ActivityStack nextFocusableStack = display.mTaskContainers + final ActivityStack nextFocusableStack = display.getDefaultTaskDisplayArea() .getNextFocusableStack(currentFocus, ignoreCurrent); if (nextFocusableStack != null) { return nextFocusableStack; @@ -3020,31 +3030,6 @@ class RootWindowContainer extends WindowContainer<DisplayContent> return null; } - /** - * Get next valid stack for launching provided activity in the system. This will search across - * displays and stacks in last-focused order for a focusable and visible stack, except those - * that are on a currently focused display. - * - * @param r The activity that is being launched. - * @param currentFocus The display that previously had focus and thus needs to be ignored when - * searching for the next candidate. - * @return Next valid {@link ActivityStack}, null if not found. - */ - ActivityStack getNextValidLaunchStack(@NonNull ActivityRecord r, int currentFocus) { - for (int i = getChildCount() - 1; i >= 0; --i) { - final DisplayContent display = getChildAt(i); - if (display.mDisplayId == currentFocus) { - continue; - } - final ActivityStack stack = getValidLaunchStackOnDisplay(display.mDisplayId, r, - null /* options */, null /* launchParams */); - if (stack != null) { - return stack; - } - } - return null; - } - boolean handleAppDied(WindowProcessController app) { boolean hasVisibleActivities = false; for (int displayNdx = getChildCount() - 1; displayNdx >= 0; --displayNdx) { diff --git a/services/core/java/com/android/server/wm/TaskDisplayArea.java b/services/core/java/com/android/server/wm/TaskDisplayArea.java index 77ef01134292..13e4d8b038b1 100644 --- a/services/core/java/com/android/server/wm/TaskDisplayArea.java +++ b/services/core/java/com/android/server/wm/TaskDisplayArea.java @@ -127,6 +127,12 @@ final class TaskDisplayArea extends DisplayArea<ActivityStack> { * the new stack becomes resumed after which it will be set to current focused stack. */ ActivityStack mLastFocusedStack; + /** + * All of the stacks on this display. Order matters, topmost stack is in front of all other + * stacks, bottommost behind. Accessed directly by ActivityManager package classes. Any calls + * changing the list should also call {@link #onStackOrderChanged()}. + */ + private ArrayList<OnStackOrderChangedListener> mStackOrderChangedCallbacks = new ArrayList<>(); TaskDisplayArea(DisplayContent displayContent, WindowManagerService service) { super(service, Type.ANY, "TaskContainers", FEATURE_TASK_CONTAINER); @@ -336,9 +342,9 @@ final class TaskDisplayArea extends DisplayArea<ActivityStack> { // Since a stack could be repositioned while being one of the child, return // current index if that's the same stack we are positioning and it is always on // top. - final boolean sameStack = mDisplayContent.getStacks().get(i) == stack; + final boolean sameStack = mChildren.get(i) == stack; if ((sameStack && stack.isAlwaysOnTop()) - || (!sameStack && !mDisplayContent.getStacks().get(i).isAlwaysOnTop())) { + || (!sameStack && !mChildren.get(i).isAlwaysOnTop())) { belowAlwaysOnTopPosition = i; break; } @@ -352,7 +358,7 @@ final class TaskDisplayArea extends DisplayArea<ActivityStack> { if (stack.isAlwaysOnTop()) { if (hasPinnedTask()) { // Always-on-top stacks go below the pinned stack. - maxPosition = mDisplayContent.getStacks().indexOf(mRootPinnedTask) - 1; + maxPosition = mChildren.indexOf(mRootPinnedTask) - 1; } // Always-on-top stacks need to be above all other stacks. minPosition = belowAlwaysOnTopPosition @@ -375,7 +381,7 @@ final class TaskDisplayArea extends DisplayArea<ActivityStack> { targetPosition = Math.min(targetPosition, maxPosition); targetPosition = Math.max(targetPosition, minPosition); - int prevPosition = mDisplayContent.getStacks().indexOf(stack); + int prevPosition = mChildren.indexOf(stack); // The positions we calculated above (maxPosition, minPosition) do not take into // consideration the following edge cases. // 1) We need to adjust the position depending on the value "adding". @@ -471,7 +477,7 @@ final class TaskDisplayArea extends DisplayArea<ActivityStack> { @Override int getOrientation(int candidate) { - if (mDisplayContent.isStackVisible(WINDOWING_MODE_SPLIT_SCREEN_PRIMARY)) { + if (isStackVisible(WINDOWING_MODE_SPLIT_SCREEN_PRIMARY)) { // Apps and their containers are not allowed to specify an orientation while using // root tasks...except for the home stack if it is not resizable and currently // visible (top of) its root task. @@ -637,7 +643,7 @@ final class TaskDisplayArea extends DisplayArea<ActivityStack> { mPreferredTopFocusableStack = null; } mDisplayContent.releaseSelfIfNeeded(); - mDisplayContent.onStackOrderChanged(stack); + onStackOrderChanged(stack); } void positionStackAt(int position, ActivityStack child, boolean includingParents) { @@ -716,7 +722,7 @@ final class TaskDisplayArea extends DisplayArea<ActivityStack> { } } - mDisplayContent.onStackOrderChanged(stack); + onStackOrderChanged(stack); } ActivityStack getStack(int rootTaskId) { @@ -765,11 +771,11 @@ final class TaskDisplayArea extends DisplayArea<ActivityStack> { } else if (stack.getParent() != launchRootTask) { stack.reparent(launchRootTask, position); } - } else if (stack.getDisplay() != mDisplayContent || !stack.isRootTask()) { + } else if (stack.getDisplayArea() != this || !stack.isRootTask()) { if (stack.getParent() == null) { addStack(stack, position); } else { - stack.reparent(mDisplayContent, onTop); + stack.reparent(this, onTop); } } // Update windowing mode if necessary, e.g. moving a pinned task to fullscreen. @@ -832,7 +838,7 @@ final class TaskDisplayArea extends DisplayArea<ActivityStack> { // Create stack on default display instead since this display can only contain 1 stack. // TODO: Kinda a hack, but better that having the decision at each call point. Hoping // this goes away once ActivityView is no longer using virtual displays. - return mRootWindowContainer.getDefaultDisplay().mTaskContainers.createStack( + return mRootWindowContainer.getDefaultTaskDisplayArea().createStack( windowingMode, activityType, onTop, info, intent, createdByOrganizer); } @@ -1551,6 +1557,16 @@ final class TaskDisplayArea extends DisplayArea<ActivityStack> { return (index < wc.mChildren.size()) ? (ActivityStack) wc.mChildren.get(index) : null; } + /** Returns true if the stack in the windowing mode is visible. */ + boolean isStackVisible(int windowingMode) { + final ActivityStack stack = getTopStackInWindowingMode(windowingMode); + return stack != null && stack.isVisible(); + } + + void removeStack(ActivityStack stack) { + removeChild(stack); + } + int getDisplayId() { return mDisplayContent.getDisplayId(); } @@ -1558,4 +1574,39 @@ final class TaskDisplayArea extends DisplayArea<ActivityStack> { boolean isRemoved() { return mDisplayContent.isRemoved(); } + + /** + * Adds a listener to be notified whenever the stack order in the display changes. Currently + * only used by the {@link RecentsAnimation} to determine whether to interrupt and cancel the + * current animation when the system state changes. + */ + void registerStackOrderChangedListener(OnStackOrderChangedListener listener) { + if (!mStackOrderChangedCallbacks.contains(listener)) { + mStackOrderChangedCallbacks.add(listener); + } + } + + /** + * Removes a previously registered stack order change listener. + */ + void unregisterStackOrderChangedListener(OnStackOrderChangedListener listener) { + mStackOrderChangedCallbacks.remove(listener); + } + + /** + * Notifies of a stack order change + * @param stack The stack which triggered the order change + */ + void onStackOrderChanged(ActivityStack stack) { + for (int i = mStackOrderChangedCallbacks.size() - 1; i >= 0; i--) { + mStackOrderChangedCallbacks.get(i).onStackOrderChanged(stack); + } + } + + /** + * Callback for when the order of the stacks in the display changes. + */ + interface OnStackOrderChangedListener { + void onStackOrderChanged(ActivityStack stack); + } } diff --git a/services/core/java/com/android/server/wm/TaskOrganizerController.java b/services/core/java/com/android/server/wm/TaskOrganizerController.java index b641e4c391ce..9ffd8d244c75 100644 --- a/services/core/java/com/android/server/wm/TaskOrganizerController.java +++ b/services/core/java/com/android/server/wm/TaskOrganizerController.java @@ -277,7 +277,7 @@ class TaskOrganizerController extends ITaskOrganizerController.Stub { return null; } - final Task task = display.mTaskContainers.createStack(windowingMode, + final Task task = display.getDefaultTaskDisplayArea().createStack(windowingMode, ACTIVITY_TYPE_UNDEFINED, false /* onTop */, null /* info */, new Intent(), true /* createdByOrganizer */); RunningTaskInfo out = task.getTaskInfo(); diff --git a/services/core/java/com/android/server/wm/WallpaperController.java b/services/core/java/com/android/server/wm/WallpaperController.java index e43f4b485349..b9b6c0858031 100644 --- a/services/core/java/com/android/server/wm/WallpaperController.java +++ b/services/core/java/com/android/server/wm/WallpaperController.java @@ -510,7 +510,7 @@ class WallpaperController { private void findWallpaperTarget() { mFindResults.reset(); - if (mDisplayContent.isStackVisible(WINDOWING_MODE_FREEFORM)) { + if (mDisplayContent.getDefaultTaskDisplayArea().isStackVisible(WINDOWING_MODE_FREEFORM)) { // In freeform mode we set the wallpaper as its own target, so we don't need an // additional window to make it visible. mFindResults.setUseTopWallpaperAsTarget(true); diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java index dfaa0ec47155..687af64e9d4f 100644 --- a/services/core/java/com/android/server/wm/WindowManagerService.java +++ b/services/core/java/com/android/server/wm/WindowManagerService.java @@ -7318,8 +7318,9 @@ public class WindowManagerService extends IWindowManager.Stub @Override public boolean isStackVisibleLw(int windowingMode) { - final DisplayContent dc = getDefaultDisplayContentLocked(); - return dc.isStackVisible(windowingMode); + // TODO(multi-display-area): Support multiple task display areas & displays + final TaskDisplayArea tc = mRoot.getDefaultTaskDisplayArea(); + return tc.isStackVisible(windowingMode); } @Override diff --git a/services/core/java/com/android/server/wm/WindowOrganizerController.java b/services/core/java/com/android/server/wm/WindowOrganizerController.java index a332b6966291..3e2e9be24c4f 100644 --- a/services/core/java/com/android/server/wm/WindowOrganizerController.java +++ b/services/core/java/com/android/server/wm/WindowOrganizerController.java @@ -234,7 +234,7 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub if (task.getParent() != newParent) { if (newParent == null) { // Re-parent task to display as a root task. - dc.moveStackToDisplay(as, hop.getToTop()); + as.reparent(dc.getDefaultTaskDisplayArea(), hop.getToTop()); } else if (newParent.inMultiWindowMode() && !task.isResizeable() && task.isLeafTask()) { Slog.w(TAG, "Can't support task that doesn't support multi-window mode in" diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java index b87d18143fc7..8e7585ae4bfa 100644 --- a/services/core/java/com/android/server/wm/WindowState.java +++ b/services/core/java/com/android/server/wm/WindowState.java @@ -1516,7 +1516,8 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP // Some system windows (e.g. "Power off" dialog) don't have a task, but we would still // associate them with some stack to enable dimming. final DisplayContent dc = getDisplayContent(); - return mAttrs.type >= FIRST_SYSTEM_WINDOW && dc != null ? dc.getRootHomeTask() : null; + return mAttrs.type >= FIRST_SYSTEM_WINDOW + && dc != null ? dc.getDefaultTaskDisplayArea().getRootHomeTask() : null; } /** diff --git a/services/core/java/com/android/server/wm/WindowToken.java b/services/core/java/com/android/server/wm/WindowToken.java index 3c2b6ec9711d..7457a1d05335 100644 --- a/services/core/java/com/android/server/wm/WindowToken.java +++ b/services/core/java/com/android/server/wm/WindowToken.java @@ -397,7 +397,8 @@ class WindowToken extends WindowContainer<WindowState> { void assignLayer(SurfaceControl.Transaction t, int layer) { if (windowType == TYPE_DOCK_DIVIDER) { // See {@link DisplayContent#mSplitScreenDividerAnchor} - super.assignRelativeLayer(t, mDisplayContent.getSplitScreenDividerAnchor(), 1); + super.assignRelativeLayer(t, + mDisplayContent.getDefaultTaskDisplayArea().getSplitScreenDividerAnchor(), 1); } else if (mRoundedCornerOverlay) { super.assignLayer(t, WindowManagerPolicy.COLOR_FADE_LAYER + 1); } else { diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityDisplayTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityDisplayTests.java index 05604b2b9aeb..1debd8c9ffb1 100644 --- a/services/tests/wmtests/src/com/android/server/wm/ActivityDisplayTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/ActivityDisplayTests.java @@ -61,19 +61,20 @@ public class ActivityDisplayTests extends ActivityTestsBase { @Test public void testLastFocusedStackIsUpdatedWhenMovingStack() { // Create a stack at bottom. - final DisplayContent display = mRootWindowContainer.getDefaultDisplay(); + final TaskDisplayArea taskDisplayAreas = + mRootWindowContainer.getDefaultDisplay().getDefaultTaskDisplayArea(); final ActivityStack stack = new StackBuilder(mRootWindowContainer).setOnTop(!ON_TOP).build(); - final ActivityStack prevFocusedStack = display.getFocusedStack(); + final ActivityStack prevFocusedStack = taskDisplayAreas.getFocusedStack(); stack.moveToFront("moveStackToFront"); // After moving the stack to front, the previous focused should be the last focused. assertTrue(stack.isFocusedStackOnDisplay()); - assertEquals(prevFocusedStack, display.mTaskContainers.getLastFocusedStack()); + assertEquals(prevFocusedStack, taskDisplayAreas.getLastFocusedStack()); stack.moveToBack("moveStackToBack", null /* task */); // After moving the stack to back, the stack should be the last focused. - assertEquals(stack, display.mTaskContainers.getLastFocusedStack()); + assertEquals(stack, taskDisplayAreas.getLastFocusedStack()); } /** @@ -83,8 +84,8 @@ public class ActivityDisplayTests extends ActivityTestsBase { @Test public void testFullscreenStackCanBeFocusedWhenFocusablePinnedStackExists() { // Create a pinned stack and move to front. - final ActivityStack pinnedStack = mRootWindowContainer.getDefaultDisplay().createStack( - WINDOWING_MODE_PINNED, ACTIVITY_TYPE_STANDARD, ON_TOP); + final ActivityStack pinnedStack = mRootWindowContainer.getDefaultTaskDisplayArea() + .createStack(WINDOWING_MODE_PINNED, ACTIVITY_TYPE_STANDARD, ON_TOP); final Task pinnedTask = new TaskBuilder(mService.mStackSupervisor) .setStack(pinnedStack).build(); new ActivityBuilder(mService).setActivityFlags(FLAG_ALWAYS_FOCUSABLE) @@ -104,8 +105,8 @@ public class ActivityDisplayTests extends ActivityTestsBase { } /** - * Test {@link DisplayContent#mPreferredTopFocusableStack} will be cleared when the stack is - * removed or moved to back, and the focused stack will be according to z-order. + * Test {@link TaskDisplayArea#mPreferredTopFocusableStack} will be cleared when + * the stack is removed or moved to back, and the focused stack will be according to z-order. */ @Test public void testStackShouldNotBeFocusedAfterMovingToBackOrRemoving() { @@ -124,7 +125,7 @@ public class ActivityDisplayTests extends ActivityTestsBase { assertTrue(stack1.isFocusedStackOnDisplay()); // Stack2 should be focused after removing stack1. - display.removeStack(stack1); + stack1.getDisplayArea().removeStack(stack1); assertTrue(stack2.isFocusedStackOnDisplay()); } @@ -156,7 +157,7 @@ public class ActivityDisplayTests extends ActivityTestsBase { } private ActivityStack createFullscreenStackWithSimpleActivityAt(DisplayContent display) { - final ActivityStack fullscreenStack = display.createStack( + final ActivityStack fullscreenStack = display.getDefaultTaskDisplayArea().createStack( WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, ON_TOP); final Task fullscreenTask = new TaskBuilder(mService.mStackSupervisor) .setStack(fullscreenStack).build(); @@ -219,58 +220,56 @@ public class ActivityDisplayTests extends ActivityTestsBase { */ @Test public void testAlwaysOnTopStackLocation() { - final DisplayContent display = mRootWindowContainer.getDefaultDisplay(); - final ActivityStack alwaysOnTopStack = display.createStack(WINDOWING_MODE_FREEFORM, + final TaskDisplayArea taskDisplayArea = mRootWindowContainer.getDefaultTaskDisplayArea(); + final ActivityStack alwaysOnTopStack = taskDisplayArea.createStack(WINDOWING_MODE_FREEFORM, ACTIVITY_TYPE_STANDARD, true /* onTop */); final ActivityRecord activity = new ActivityBuilder(mService).setCreateTask(true) .setStack(alwaysOnTopStack).build(); alwaysOnTopStack.setAlwaysOnTop(true); - display.mTaskContainers.positionStackAtTop(alwaysOnTopStack, false /* includingParents */); + taskDisplayArea.positionStackAtTop(alwaysOnTopStack, false /* includingParents */); assertTrue(alwaysOnTopStack.isAlwaysOnTop()); // Ensure always on top state is synced to the children of the stack. assertTrue(alwaysOnTopStack.getTopNonFinishingActivity().isAlwaysOnTop()); - assertEquals(alwaysOnTopStack, display.getTopStack()); + assertEquals(alwaysOnTopStack, taskDisplayArea.getTopStack()); - final ActivityStack pinnedStack = display.createStack( + final ActivityStack pinnedStack = taskDisplayArea.createStack( WINDOWING_MODE_PINNED, ACTIVITY_TYPE_STANDARD, true /* onTop */); - assertEquals(pinnedStack, display.getRootPinnedTask()); - assertEquals(pinnedStack, display.getTopStack()); + assertEquals(pinnedStack, taskDisplayArea.getRootPinnedTask()); + assertEquals(pinnedStack, taskDisplayArea.getTopStack()); - final ActivityStack anotherAlwaysOnTopStack = display.createStack( + final ActivityStack anotherAlwaysOnTopStack = taskDisplayArea.createStack( WINDOWING_MODE_FREEFORM, ACTIVITY_TYPE_STANDARD, true /* onTop */); anotherAlwaysOnTopStack.setAlwaysOnTop(true); - display.mTaskContainers.positionStackAtTop(anotherAlwaysOnTopStack, - false /* includingParents */); + taskDisplayArea.positionStackAtTop(anotherAlwaysOnTopStack, false /* includingParents */); assertTrue(anotherAlwaysOnTopStack.isAlwaysOnTop()); - int topPosition = display.getStackCount() - 1; + int topPosition = taskDisplayArea.getStackCount() - 1; // Ensure the new alwaysOnTop stack is put below the pinned stack, but on top of the // existing alwaysOnTop stack. - assertEquals(anotherAlwaysOnTopStack, display.getStackAt(topPosition - 1)); + assertEquals(anotherAlwaysOnTopStack, taskDisplayArea.getStackAt(topPosition - 1)); - final ActivityStack nonAlwaysOnTopStack = display.createStack( + final ActivityStack nonAlwaysOnTopStack = taskDisplayArea.createStack( WINDOWING_MODE_FREEFORM, ACTIVITY_TYPE_STANDARD, true /* onTop */); - assertEquals(display, nonAlwaysOnTopStack.getDisplay()); - topPosition = display.getStackCount() - 1; + assertEquals(taskDisplayArea, nonAlwaysOnTopStack.getDisplayArea()); + topPosition = taskDisplayArea.getStackCount() - 1; // Ensure the non-alwaysOnTop stack is put below the three alwaysOnTop stacks, but above the // existing other non-alwaysOnTop stacks. - assertEquals(nonAlwaysOnTopStack, display.getStackAt(topPosition - 3)); + assertEquals(nonAlwaysOnTopStack, taskDisplayArea.getStackAt(topPosition - 3)); anotherAlwaysOnTopStack.setAlwaysOnTop(false); - display.mTaskContainers.positionStackAtTop(anotherAlwaysOnTopStack, - false /* includingParents */); + taskDisplayArea.positionStackAtTop(anotherAlwaysOnTopStack, false /* includingParents */); assertFalse(anotherAlwaysOnTopStack.isAlwaysOnTop()); // Ensure, when always on top is turned off for a stack, the stack is put just below all // other always on top stacks. - assertEquals(anotherAlwaysOnTopStack, display.getStackAt(topPosition - 2)); + assertEquals(anotherAlwaysOnTopStack, taskDisplayArea.getStackAt(topPosition - 2)); anotherAlwaysOnTopStack.setAlwaysOnTop(true); // Ensure always on top state changes properly when windowing mode changes. anotherAlwaysOnTopStack.setWindowingMode(WINDOWING_MODE_FULLSCREEN); assertFalse(anotherAlwaysOnTopStack.isAlwaysOnTop()); - assertEquals(anotherAlwaysOnTopStack, display.getStackAt(topPosition - 2)); + assertEquals(anotherAlwaysOnTopStack, taskDisplayArea.getStackAt(topPosition - 2)); anotherAlwaysOnTopStack.setWindowingMode(WINDOWING_MODE_FREEFORM); assertTrue(anotherAlwaysOnTopStack.isAlwaysOnTop()); - assertEquals(anotherAlwaysOnTopStack, display.getStackAt(topPosition - 1)); + assertEquals(anotherAlwaysOnTopStack, taskDisplayArea.getStackAt(topPosition - 1)); } @Test @@ -286,14 +285,14 @@ public class ActivityDisplayTests extends ActivityTestsBase { } private void removeStackTests(Runnable runnable) { - final DisplayContent display = mRootWindowContainer.getDefaultDisplay(); - final ActivityStack stack1 = display.createStack(WINDOWING_MODE_FULLSCREEN, + final TaskDisplayArea taskDisplayArea = mRootWindowContainer.getDefaultTaskDisplayArea(); + final ActivityStack stack1 = taskDisplayArea.createStack(WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, ON_TOP); - final ActivityStack stack2 = display.createStack(WINDOWING_MODE_FULLSCREEN, + final ActivityStack stack2 = taskDisplayArea.createStack(WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, ON_TOP); - final ActivityStack stack3 = display.createStack(WINDOWING_MODE_FULLSCREEN, + final ActivityStack stack3 = taskDisplayArea.createStack(WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, ON_TOP); - final ActivityStack stack4 = display.createStack(WINDOWING_MODE_FULLSCREEN, + final ActivityStack stack4 = taskDisplayArea.createStack(WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, ON_TOP); final Task task1 = new TaskBuilder(mService.mStackSupervisor).setStack(stack1).build(); final Task task2 = new TaskBuilder(mService.mStackSupervisor).setStack(stack2).build(); @@ -302,13 +301,13 @@ public class ActivityDisplayTests extends ActivityTestsBase { // Reordering stacks while removing stacks. doAnswer(invocation -> { - display.mTaskContainers.positionStackAtTop(stack3, false); + taskDisplayArea.positionStackAtTop(stack3, false); return true; }).when(mSupervisor).removeTask(eq(task4), anyBoolean(), anyBoolean(), any()); // Removing stacks from the display while removing stacks. doAnswer(invocation -> { - display.removeStack(stack2); + taskDisplayArea.removeStack(stack2); return true; }).when(mSupervisor).removeTask(eq(task2), anyBoolean(), anyBoolean(), any()); diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java index cff93cf8ef1d..08f6409cb902 100644 --- a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java @@ -1400,8 +1400,8 @@ public class ActivityRecordTests extends ActivityTestsBase { display = new TestDisplayContent.Builder(mService, 2000, 1000).setDensityDpi(300) .setPosition(DisplayContent.POSITION_TOP).build(); } - final ActivityStack stack = display.createStack(WINDOWING_MODE_UNDEFINED, - ACTIVITY_TYPE_STANDARD, true /* onTop */); + final ActivityStack stack = display.getDefaultTaskDisplayArea() + .createStack(WINDOWING_MODE_UNDEFINED, ACTIVITY_TYPE_STANDARD, true /* onTop */); final Task task = new TaskBuilder(mSupervisor).setStack(stack).build(); return new ActivityBuilder(mService).setTask(task).setUseProcess(process).build(); } diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityStackSupervisorTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityStackSupervisorTests.java index e8c0362c9f32..22d7fcbb4162 100644 --- a/services/tests/wmtests/src/com/android/server/wm/ActivityStackSupervisorTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/ActivityStackSupervisorTests.java @@ -60,7 +60,7 @@ public class ActivityStackSupervisorTests extends ActivityTestsBase { @Before public void setUp() throws Exception { - mFullscreenStack = mRootWindowContainer.getDefaultDisplay().createStack( + mFullscreenStack = mRootWindowContainer.getDefaultTaskDisplayArea().createStack( WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */); } diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityStackTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityStackTests.java index af5afff4bed1..3d15401cdfb9 100644 --- a/services/tests/wmtests/src/com/android/server/wm/ActivityStackTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/ActivityStackTests.java @@ -82,15 +82,15 @@ import org.junit.runner.RunWith; @Presubmit @RunWith(WindowTestRunner.class) public class ActivityStackTests extends ActivityTestsBase { - private DisplayContent mDefaultDisplay; + private TaskDisplayArea mDefaultTaskDisplayArea; private ActivityStack mStack; private Task mTask; @Before public void setUp() throws Exception { - mDefaultDisplay = mRootWindowContainer.getDefaultDisplay(); - mStack = mDefaultDisplay.createStack(WINDOWING_MODE_UNDEFINED, ACTIVITY_TYPE_STANDARD, - true /* onTop */); + mDefaultTaskDisplayArea = mRootWindowContainer.getDefaultTaskDisplayArea(); + mStack = mDefaultTaskDisplayArea.createStack(WINDOWING_MODE_UNDEFINED, + ACTIVITY_TYPE_STANDARD, true /* onTop */); spyOn(mStack); mTask = new TaskBuilder(mSupervisor).setStack(mStack).build(); } @@ -112,7 +112,7 @@ public class ActivityStackTests extends ActivityTestsBase { r.setState(RESUMED, "testResumedActivityFromTaskReparenting"); assertEquals(r, mStack.getResumedActivity()); - final ActivityStack destStack = mRootWindowContainer.getDefaultDisplay().createStack( + final ActivityStack destStack = mDefaultTaskDisplayArea.createStack( WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */); mTask.reparent(destStack, true /* toTop */, Task.REPARENT_KEEP_STACK_AT_FRONT, @@ -130,7 +130,7 @@ public class ActivityStackTests extends ActivityTestsBase { r.setState(RESUMED, "testResumedActivityFromActivityReparenting"); assertEquals(r, mStack.getResumedActivity()); - final ActivityStack destStack = mRootWindowContainer.getDefaultDisplay().createStack( + final ActivityStack destStack = mDefaultTaskDisplayArea.createStack( WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */); mTask.reparent(destStack, true /*toTop*/, REPARENT_MOVE_STACK_TO_FRONT, false, false, "testResumedActivityFromActivityReparenting"); @@ -143,7 +143,7 @@ public class ActivityStackTests extends ActivityTestsBase { public void testPrimarySplitScreenRestoresWhenMovedToBack() { // Create primary splitscreen stack. This will create secondary stacks and places the // existing fullscreen stack on the bottom. - final ActivityStack primarySplitScreen = mDefaultDisplay.createStack( + final ActivityStack primarySplitScreen = mDefaultTaskDisplayArea.createStack( WINDOWING_MODE_SPLIT_SCREEN_PRIMARY, ACTIVITY_TYPE_STANDARD, true /* onTop */); // Assert windowing mode. @@ -154,7 +154,7 @@ public class ActivityStackTests extends ActivityTestsBase { null /* task */); // Assert that stack is at the bottom. - assertEquals(0, mDefaultDisplay.getIndexOf(primarySplitScreen)); + assertEquals(0, mDefaultTaskDisplayArea.getIndexOf(primarySplitScreen)); // Ensure no longer in splitscreen. assertEquals(WINDOWING_MODE_FULLSCREEN, primarySplitScreen.getWindowingMode()); @@ -167,7 +167,7 @@ public class ActivityStackTests extends ActivityTestsBase { @Test public void testPrimarySplitScreenRestoresPreviousWhenMovedToBack() { // This time, start with a fullscreen activitystack - final ActivityStack primarySplitScreen = mDefaultDisplay.createStack( + final ActivityStack primarySplitScreen = mDefaultTaskDisplayArea.createStack( WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */); primarySplitScreen.setWindowingMode(WINDOWING_MODE_SPLIT_SCREEN_PRIMARY); @@ -180,7 +180,7 @@ public class ActivityStackTests extends ActivityTestsBase { null /* task */); // Assert that stack is at the bottom. - assertEquals(0, mDefaultDisplay.getIndexOf(primarySplitScreen)); + assertEquals(0, mDefaultTaskDisplayArea.getIndexOf(primarySplitScreen)); // Ensure that the override mode is restored to what it was (fullscreen) assertEquals(WINDOWING_MODE_FULLSCREEN, @@ -189,14 +189,14 @@ public class ActivityStackTests extends ActivityTestsBase { @Test public void testStackInheritsDisplayWindowingMode() { - final ActivityStack primarySplitScreen = mDefaultDisplay.createStack( + final ActivityStack primarySplitScreen = mDefaultTaskDisplayArea.createStack( WINDOWING_MODE_UNDEFINED, ACTIVITY_TYPE_STANDARD, true /* onTop */); assertEquals(WINDOWING_MODE_FULLSCREEN, primarySplitScreen.getWindowingMode()); assertEquals(WINDOWING_MODE_UNDEFINED, primarySplitScreen.getRequestedOverrideWindowingMode()); - mDefaultDisplay.setWindowingMode(WINDOWING_MODE_FREEFORM); + mDefaultTaskDisplayArea.setWindowingMode(WINDOWING_MODE_FREEFORM); assertEquals(WINDOWING_MODE_FREEFORM, primarySplitScreen.getWindowingMode()); assertEquals(WINDOWING_MODE_UNDEFINED, primarySplitScreen.getRequestedOverrideWindowingMode()); @@ -204,7 +204,7 @@ public class ActivityStackTests extends ActivityTestsBase { @Test public void testStackOverridesDisplayWindowingMode() { - final ActivityStack primarySplitScreen = mDefaultDisplay.createStack( + final ActivityStack primarySplitScreen = mDefaultTaskDisplayArea.createStack( WINDOWING_MODE_UNDEFINED, ACTIVITY_TYPE_STANDARD, true /* onTop */); assertEquals(WINDOWING_MODE_FULLSCREEN, primarySplitScreen.getWindowingMode()); @@ -216,7 +216,7 @@ public class ActivityStackTests extends ActivityTestsBase { assertEquals(WINDOWING_MODE_FULLSCREEN, primarySplitScreen.getRequestedOverrideWindowingMode()); - mDefaultDisplay.setWindowingMode(WINDOWING_MODE_FREEFORM); + mDefaultTaskDisplayArea.setWindowingMode(WINDOWING_MODE_FREEFORM); assertEquals(WINDOWING_MODE_FULLSCREEN, primarySplitScreen.getWindowingMode()); } @@ -283,10 +283,11 @@ public class ActivityStackTests extends ActivityTestsBase { @Test public void testMoveStackToBackIncludingParent() { - final DisplayContent display = addNewDisplayContentAt(DisplayContent.POSITION_TOP); - final ActivityStack stack1 = createStackForShouldBeVisibleTest(display, + final TaskDisplayArea taskDisplayArea = addNewDisplayContentAt(DisplayContent.POSITION_TOP) + .getDefaultTaskDisplayArea(); + final ActivityStack stack1 = createStackForShouldBeVisibleTest(taskDisplayArea, WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */); - final ActivityStack stack2 = createStackForShouldBeVisibleTest(display, + final ActivityStack stack2 = createStackForShouldBeVisibleTest(taskDisplayArea, WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */); // Do not move display to back because there is still another stack. @@ -294,16 +295,16 @@ public class ActivityStackTests extends ActivityTestsBase { verify(stack2).positionChildAtBottom(any(), eq(false) /* includingParents */); // Also move display to back because there is only one stack left. - display.removeStack(stack1); + taskDisplayArea.removeStack(stack1); stack2.moveToBack("testMoveStackToBackIncludingParent", stack2.getTopMostTask()); verify(stack2).positionChildAtBottom(any(), eq(true) /* includingParents */); } @Test public void testShouldBeVisible_Fullscreen() { - final ActivityStack homeStack = createStackForShouldBeVisibleTest(mDefaultDisplay, + final ActivityStack homeStack = createStackForShouldBeVisibleTest(mDefaultTaskDisplayArea, WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_HOME, true /* onTop */); - final ActivityStack pinnedStack = createStackForShouldBeVisibleTest(mDefaultDisplay, + final ActivityStack pinnedStack = createStackForShouldBeVisibleTest(mDefaultTaskDisplayArea, WINDOWING_MODE_PINNED, ACTIVITY_TYPE_STANDARD, true /* onTop */); // Add an activity to the pinned stack so it isn't considered empty for visibility check. final ActivityRecord pinnedActivity = new ActivityBuilder(mService) @@ -314,8 +315,9 @@ public class ActivityStackTests extends ActivityTestsBase { assertTrue(homeStack.shouldBeVisible(null /* starting */)); assertTrue(pinnedStack.shouldBeVisible(null /* starting */)); - final ActivityStack fullscreenStack = createStackForShouldBeVisibleTest(mDefaultDisplay, - WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */); + final ActivityStack fullscreenStack = createStackForShouldBeVisibleTest( + mDefaultTaskDisplayArea, WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, + true /* onTop */); // Home stack shouldn't be visible behind an opaque fullscreen stack, but pinned stack // should be visible since it is always on-top. doReturn(false).when(fullscreenStack).isTranslucent(any()); @@ -331,15 +333,15 @@ public class ActivityStackTests extends ActivityTestsBase { @Test public void testShouldBeVisible_SplitScreen() { - final ActivityStack homeStack = createStackForShouldBeVisibleTest(mDefaultDisplay, + final ActivityStack homeStack = createStackForShouldBeVisibleTest(mDefaultTaskDisplayArea, WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_HOME, true /* onTop */); // Home stack should always be fullscreen for this test. doReturn(false).when(homeStack).supportsSplitScreenWindowingMode(); final ActivityStack splitScreenPrimary = - createStackForShouldBeVisibleTest(mDefaultDisplay, + createStackForShouldBeVisibleTest(mDefaultTaskDisplayArea, WINDOWING_MODE_SPLIT_SCREEN_PRIMARY, ACTIVITY_TYPE_STANDARD, true /* onTop */); final ActivityStack splitScreenSecondary = - createStackForShouldBeVisibleTest(mDefaultDisplay, + createStackForShouldBeVisibleTest(mDefaultTaskDisplayArea, WINDOWING_MODE_SPLIT_SCREEN_SECONDARY, ACTIVITY_TYPE_STANDARD, true /* onTop */); // Home stack shouldn't be visible if both halves of split-screen are opaque. @@ -367,7 +369,7 @@ public class ActivityStackTests extends ActivityTestsBase { splitScreenSecondary.getVisibility(null /* starting */)); final ActivityStack splitScreenSecondary2 = - createStackForShouldBeVisibleTest(mDefaultDisplay, + createStackForShouldBeVisibleTest(mDefaultTaskDisplayArea, WINDOWING_MODE_SPLIT_SCREEN_SECONDARY, ACTIVITY_TYPE_STANDARD, true /* onTop */); // First split-screen secondary shouldn't be visible behind another opaque split-split // secondary. @@ -389,8 +391,9 @@ public class ActivityStackTests extends ActivityTestsBase { assertEquals(STACK_VISIBILITY_VISIBLE, splitScreenSecondary2.getVisibility(null /* starting */)); - final ActivityStack assistantStack = createStackForShouldBeVisibleTest(mDefaultDisplay, - WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_ASSISTANT, true /* onTop */); + final ActivityStack assistantStack = createStackForShouldBeVisibleTest( + mDefaultTaskDisplayArea, WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_ASSISTANT, + true /* onTop */); // Split-screen stacks shouldn't be visible behind an opaque fullscreen stack. doReturn(false).when(assistantStack).isTranslucent(any()); @@ -530,7 +533,7 @@ public class ActivityStackTests extends ActivityTestsBase { final ActivityStack translucentStack = createStandardStackForVisibilityTest(WINDOWING_MODE_FULLSCREEN, true /* translucent */); - final ActivityStack pinnedStack = createStackForShouldBeVisibleTest(mDefaultDisplay, + final ActivityStack pinnedStack = createStackForShouldBeVisibleTest(mDefaultTaskDisplayArea, WINDOWING_MODE_PINNED, ACTIVITY_TYPE_STANDARD, true /* onTop */); assertEquals(STACK_VISIBILITY_VISIBLE_BEHIND_TRANSLUCENT, @@ -547,7 +550,7 @@ public class ActivityStackTests extends ActivityTestsBase { @Test public void testShouldBeVisible_Finishing() { - final ActivityStack homeStack = createStackForShouldBeVisibleTest(mDefaultDisplay, + final ActivityStack homeStack = createStackForShouldBeVisibleTest(mDefaultTaskDisplayArea, WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_HOME, true /* onTop */); ActivityRecord topRunningHomeActivity = homeStack.topRunningActivity(); if (topRunningHomeActivity == null) { @@ -558,7 +561,7 @@ public class ActivityStackTests extends ActivityTestsBase { } final ActivityStack translucentStack = createStackForShouldBeVisibleTest( - mDefaultDisplay, WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, + mDefaultTaskDisplayArea, WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */); doReturn(true).when(translucentStack).isTranslucent(any()); @@ -580,7 +583,7 @@ public class ActivityStackTests extends ActivityTestsBase { @Test public void testShouldBeVisible_FullscreenBehindTranslucentInHomeStack() { - final ActivityStack homeStack = createStackForShouldBeVisibleTest(mDefaultDisplay, + final ActivityStack homeStack = createStackForShouldBeVisibleTest(mDefaultTaskDisplayArea, WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_HOME, true /* onTop */); final ActivityRecord firstActivity = new ActivityBuilder(mService) @@ -601,74 +604,77 @@ public class ActivityStackTests extends ActivityTestsBase { @Test public void testMoveHomeStackBehindBottomMostVisibleStack_NoMoveHomeBehindFullscreen() { - mDefaultDisplay.removeStack(mStack); + mDefaultTaskDisplayArea.removeStack(mStack); - final ActivityStack homeStack = createStackForShouldBeVisibleTest(mDefaultDisplay, + final ActivityStack homeStack = createStackForShouldBeVisibleTest(mDefaultTaskDisplayArea, WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_HOME, true /* onTop */); - final ActivityStack fullscreenStack = createStackForShouldBeVisibleTest(mDefaultDisplay, - WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */); + final ActivityStack fullscreenStack = createStackForShouldBeVisibleTest( + mDefaultTaskDisplayArea, WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, + true /* onTop */); doReturn(false).when(homeStack).isTranslucent(any()); doReturn(false).when(fullscreenStack).isTranslucent(any()); // Ensure that we don't move the home stack if it is already behind the top fullscreen stack - int homeStackIndex = mDefaultDisplay.getIndexOf(homeStack); + int homeStackIndex = mDefaultTaskDisplayArea.getIndexOf(homeStack); assertEquals(fullscreenStack, getStackAbove(homeStack)); - mDefaultDisplay.mTaskContainers.moveStackBehindBottomMostVisibleStack(homeStack); - assertEquals(homeStackIndex, mDefaultDisplay.getIndexOf(homeStack)); + mDefaultTaskDisplayArea.moveStackBehindBottomMostVisibleStack(homeStack); + assertEquals(homeStackIndex, mDefaultTaskDisplayArea.getIndexOf(homeStack)); } @Test public void testMoveHomeStackBehindBottomMostVisibleStack_NoMoveHomeBehindTranslucent() { - mDefaultDisplay.removeStack(mStack); + mDefaultTaskDisplayArea.removeStack(mStack); - final ActivityStack homeStack = createStackForShouldBeVisibleTest(mDefaultDisplay, + final ActivityStack homeStack = createStackForShouldBeVisibleTest(mDefaultTaskDisplayArea, WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_HOME, true /* onTop */); - final ActivityStack fullscreenStack = createStackForShouldBeVisibleTest(mDefaultDisplay, + final ActivityStack fullscreenStack = createStackForShouldBeVisibleTest( + mDefaultTaskDisplayArea, WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */); doReturn(false).when(homeStack).isTranslucent(any()); doReturn(true).when(fullscreenStack).isTranslucent(any()); // Ensure that we don't move the home stack if it is already behind the top fullscreen stack - int homeStackIndex = mDefaultDisplay.getIndexOf(homeStack); + int homeStackIndex = mDefaultTaskDisplayArea.getIndexOf(homeStack); assertEquals(fullscreenStack, getStackAbove(homeStack)); - mDefaultDisplay.mTaskContainers.moveStackBehindBottomMostVisibleStack(homeStack); - assertEquals(homeStackIndex, mDefaultDisplay.getIndexOf(homeStack)); + mDefaultTaskDisplayArea.moveStackBehindBottomMostVisibleStack(homeStack); + assertEquals(homeStackIndex, mDefaultTaskDisplayArea.getIndexOf(homeStack)); } @Test public void testMoveHomeStackBehindBottomMostVisibleStack_NoMoveHomeOnTop() { - mDefaultDisplay.removeStack(mStack); + mDefaultTaskDisplayArea.removeStack(mStack); - final ActivityStack fullscreenStack = createStackForShouldBeVisibleTest(mDefaultDisplay, - WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */); - final ActivityStack homeStack = createStackForShouldBeVisibleTest(mDefaultDisplay, + final ActivityStack fullscreenStack = createStackForShouldBeVisibleTest( + mDefaultTaskDisplayArea, WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, + true /* onTop */); + final ActivityStack homeStack = createStackForShouldBeVisibleTest(mDefaultTaskDisplayArea, WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_HOME, true /* onTop */); doReturn(false).when(homeStack).isTranslucent(any()); doReturn(false).when(fullscreenStack).isTranslucent(any()); // Ensure we don't move the home stack if it is already on top - int homeStackIndex = mDefaultDisplay.getIndexOf(homeStack); + int homeStackIndex = mDefaultTaskDisplayArea.getIndexOf(homeStack); assertNull(getStackAbove(homeStack)); - mDefaultDisplay.mTaskContainers.moveStackBehindBottomMostVisibleStack(homeStack); - assertEquals(homeStackIndex, mDefaultDisplay.getIndexOf(homeStack)); + mDefaultTaskDisplayArea.moveStackBehindBottomMostVisibleStack(homeStack); + assertEquals(homeStackIndex, mDefaultTaskDisplayArea.getIndexOf(homeStack)); } @Test public void testMoveHomeStackBehindBottomMostVisibleStack_MoveHomeBehindFullscreen() { - mDefaultDisplay.removeStack(mStack); + mDefaultTaskDisplayArea.removeStack(mStack); - final ActivityStack homeStack = createStackForShouldBeVisibleTest(mDefaultDisplay, + final ActivityStack homeStack = createStackForShouldBeVisibleTest(mDefaultTaskDisplayArea, WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_HOME, true /* onTop */); final ActivityStack fullscreenStack1 = createStackForShouldBeVisibleTest( - mDefaultDisplay, WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, + mDefaultTaskDisplayArea, WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */); final ActivityStack fullscreenStack2 = createStackForShouldBeVisibleTest( - mDefaultDisplay, WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, + mDefaultTaskDisplayArea, WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */); - final ActivityStack pinnedStack = createStackForShouldBeVisibleTest(mDefaultDisplay, + final ActivityStack pinnedStack = createStackForShouldBeVisibleTest(mDefaultTaskDisplayArea, WINDOWING_MODE_PINNED, ACTIVITY_TYPE_STANDARD, true /* onTop */); doReturn(false).when(homeStack).isTranslucent(any()); @@ -678,22 +684,22 @@ public class ActivityStackTests extends ActivityTestsBase { // Ensure that we move the home stack behind the bottom most fullscreen stack, ignoring the // pinned stack assertEquals(fullscreenStack1, getStackAbove(homeStack)); - mDefaultDisplay.mTaskContainers.moveStackBehindBottomMostVisibleStack(homeStack); + mDefaultTaskDisplayArea.moveStackBehindBottomMostVisibleStack(homeStack); assertEquals(fullscreenStack2, getStackAbove(homeStack)); } @Test public void testMoveHomeStackBehindBottomMostVisibleStack_MoveHomeBehindFullscreenAndTranslucent() { - mDefaultDisplay.removeStack(mStack); + mDefaultTaskDisplayArea.removeStack(mStack); - final ActivityStack homeStack = createStackForShouldBeVisibleTest(mDefaultDisplay, + final ActivityStack homeStack = createStackForShouldBeVisibleTest(mDefaultTaskDisplayArea, WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_HOME, true /* onTop */); final ActivityStack fullscreenStack1 = createStackForShouldBeVisibleTest( - mDefaultDisplay, WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, + mDefaultTaskDisplayArea, WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */); final ActivityStack fullscreenStack2 = createStackForShouldBeVisibleTest( - mDefaultDisplay, WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, + mDefaultTaskDisplayArea, WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */); doReturn(false).when(homeStack).isTranslucent(any()); @@ -703,21 +709,21 @@ public class ActivityStackTests extends ActivityTestsBase { // Ensure that we move the home stack behind the bottom most non-translucent fullscreen // stack assertEquals(fullscreenStack1, getStackAbove(homeStack)); - mDefaultDisplay.mTaskContainers.moveStackBehindBottomMostVisibleStack(homeStack); + mDefaultTaskDisplayArea.moveStackBehindBottomMostVisibleStack(homeStack); assertEquals(fullscreenStack1, getStackAbove(homeStack)); } @Test public void testMoveHomeStackBehindStack_BehindHomeStack() { - mDefaultDisplay.removeStack(mStack); + mDefaultTaskDisplayArea.removeStack(mStack); final ActivityStack fullscreenStack1 = createStackForShouldBeVisibleTest( - mDefaultDisplay, WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, + mDefaultTaskDisplayArea, WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */); final ActivityStack fullscreenStack2 = createStackForShouldBeVisibleTest( - mDefaultDisplay, WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, + mDefaultTaskDisplayArea, WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */); - final ActivityStack homeStack = createStackForShouldBeVisibleTest(mDefaultDisplay, + final ActivityStack homeStack = createStackForShouldBeVisibleTest(mDefaultTaskDisplayArea, WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_HOME, true /* onTop */); doReturn(false).when(homeStack).isTranslucent(any()); @@ -725,50 +731,50 @@ public class ActivityStackTests extends ActivityTestsBase { doReturn(false).when(fullscreenStack2).isTranslucent(any()); // Ensure we don't move the home stack behind itself - int homeStackIndex = mDefaultDisplay.getIndexOf(homeStack); - mDefaultDisplay.mTaskContainers.moveStackBehindStack(homeStack, homeStack); - assertEquals(homeStackIndex, mDefaultDisplay.getIndexOf(homeStack)); + int homeStackIndex = mDefaultTaskDisplayArea.getIndexOf(homeStack); + mDefaultTaskDisplayArea.moveStackBehindStack(homeStack, homeStack); + assertEquals(homeStackIndex, mDefaultTaskDisplayArea.getIndexOf(homeStack)); } @Test public void testMoveHomeStackBehindStack() { - mDefaultDisplay.removeStack(mStack); + mDefaultTaskDisplayArea.removeStack(mStack); final ActivityStack fullscreenStack1 = createStackForShouldBeVisibleTest( - mDefaultDisplay, WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, + mDefaultTaskDisplayArea, WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */); final ActivityStack fullscreenStack2 = createStackForShouldBeVisibleTest( - mDefaultDisplay, WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, + mDefaultTaskDisplayArea, WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */); final ActivityStack fullscreenStack3 = createStackForShouldBeVisibleTest( - mDefaultDisplay, WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, + mDefaultTaskDisplayArea, WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */); final ActivityStack fullscreenStack4 = createStackForShouldBeVisibleTest( - mDefaultDisplay, WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, + mDefaultTaskDisplayArea, WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */); - final ActivityStack homeStack = createStackForShouldBeVisibleTest(mDefaultDisplay, + final ActivityStack homeStack = createStackForShouldBeVisibleTest(mDefaultTaskDisplayArea, WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_HOME, true /* onTop */); - mDefaultDisplay.mTaskContainers.moveStackBehindStack(homeStack, fullscreenStack1); + mDefaultTaskDisplayArea.moveStackBehindStack(homeStack, fullscreenStack1); assertEquals(fullscreenStack1, getStackAbove(homeStack)); - mDefaultDisplay.mTaskContainers.moveStackBehindStack(homeStack, fullscreenStack2); + mDefaultTaskDisplayArea.moveStackBehindStack(homeStack, fullscreenStack2); assertEquals(fullscreenStack2, getStackAbove(homeStack)); - mDefaultDisplay.mTaskContainers.moveStackBehindStack(homeStack, fullscreenStack4); + mDefaultTaskDisplayArea.moveStackBehindStack(homeStack, fullscreenStack4); assertEquals(fullscreenStack4, getStackAbove(homeStack)); - mDefaultDisplay.mTaskContainers.moveStackBehindStack(homeStack, fullscreenStack2); + mDefaultTaskDisplayArea.moveStackBehindStack(homeStack, fullscreenStack2); assertEquals(fullscreenStack2, getStackAbove(homeStack)); } @Test public void testSetAlwaysOnTop() { - final ActivityStack homeStack = createStackForShouldBeVisibleTest(mDefaultDisplay, + final ActivityStack homeStack = createStackForShouldBeVisibleTest(mDefaultTaskDisplayArea, WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_HOME, true /* onTop */); - final ActivityStack pinnedStack = createStackForShouldBeVisibleTest(mDefaultDisplay, + final ActivityStack pinnedStack = createStackForShouldBeVisibleTest(mDefaultTaskDisplayArea, WINDOWING_MODE_PINNED, ACTIVITY_TYPE_STANDARD, true /* onTop */); assertEquals(pinnedStack, getStackAbove(homeStack)); final ActivityStack alwaysOnTopStack = createStackForShouldBeVisibleTest( - mDefaultDisplay, WINDOWING_MODE_FREEFORM, ACTIVITY_TYPE_STANDARD, + mDefaultTaskDisplayArea, WINDOWING_MODE_FREEFORM, ACTIVITY_TYPE_STANDARD, true /* onTop */); alwaysOnTopStack.setAlwaysOnTop(true); assertTrue(alwaysOnTopStack.isAlwaysOnTop()); @@ -776,13 +782,13 @@ public class ActivityStackTests extends ActivityTestsBase { assertEquals(pinnedStack, getStackAbove(alwaysOnTopStack)); final ActivityStack nonAlwaysOnTopStack = createStackForShouldBeVisibleTest( - mDefaultDisplay, WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, + mDefaultTaskDisplayArea, WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */); // Ensure non always on top stack is put below always on top stacks. assertEquals(alwaysOnTopStack, getStackAbove(nonAlwaysOnTopStack)); final ActivityStack alwaysOnTopStack2 = createStackForShouldBeVisibleTest( - mDefaultDisplay, WINDOWING_MODE_FREEFORM, ACTIVITY_TYPE_STANDARD, + mDefaultTaskDisplayArea, WINDOWING_MODE_FREEFORM, ACTIVITY_TYPE_STANDARD, true /* onTop */); alwaysOnTopStack2.setAlwaysOnTop(true); assertTrue(alwaysOnTopStack2.isAlwaysOnTop()); @@ -807,13 +813,14 @@ public class ActivityStackTests extends ActivityTestsBase { @Test public void testSplitScreenMoveToFront() { final ActivityStack splitScreenPrimary = createStackForShouldBeVisibleTest( - mDefaultDisplay, WINDOWING_MODE_SPLIT_SCREEN_PRIMARY, ACTIVITY_TYPE_STANDARD, - true /* onTop */); + mDefaultTaskDisplayArea, WINDOWING_MODE_SPLIT_SCREEN_PRIMARY, + ACTIVITY_TYPE_STANDARD, true /* onTop */); final ActivityStack splitScreenSecondary = createStackForShouldBeVisibleTest( - mDefaultDisplay, WINDOWING_MODE_SPLIT_SCREEN_SECONDARY, ACTIVITY_TYPE_STANDARD, + mDefaultTaskDisplayArea, WINDOWING_MODE_SPLIT_SCREEN_SECONDARY, + ACTIVITY_TYPE_STANDARD, true /* onTop */); + final ActivityStack assistantStack = createStackForShouldBeVisibleTest( + mDefaultTaskDisplayArea, WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_ASSISTANT, true /* onTop */); - final ActivityStack assistantStack = createStackForShouldBeVisibleTest(mDefaultDisplay, - WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_ASSISTANT, true /* onTop */); doReturn(false).when(splitScreenPrimary).isTranslucent(any()); doReturn(false).when(splitScreenSecondary).isTranslucent(any()); @@ -832,7 +839,7 @@ public class ActivityStackTests extends ActivityTestsBase { private ActivityStack createStandardStackForVisibilityTest(int windowingMode, boolean translucent) { - final ActivityStack stack = createStackForShouldBeVisibleTest(mDefaultDisplay, + final ActivityStack stack = createStackForShouldBeVisibleTest(mDefaultTaskDisplayArea, windowingMode, ACTIVITY_TYPE_STANDARD, true /* onTop */); doReturn(translucent).when(stack).isTranslucent(any()); return stack; @@ -840,20 +847,20 @@ public class ActivityStackTests extends ActivityTestsBase { @SuppressWarnings("TypeParameterUnusedInFormals") private ActivityStack createStackForShouldBeVisibleTest( - DisplayContent display, int windowingMode, int activityType, boolean onTop) { + TaskDisplayArea taskDisplayArea, int windowingMode, int activityType, boolean onTop) { final ActivityStack stack; if (activityType == ACTIVITY_TYPE_HOME) { // Home stack and activity are created in ActivityTestsBase#setupActivityManagerService - stack = mDefaultDisplay.getStack(WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_HOME); + stack = mDefaultTaskDisplayArea.getStack(WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_HOME); if (onTop) { - mDefaultDisplay.mTaskContainers.positionStackAtTop(stack, + mDefaultTaskDisplayArea.positionStackAtTop(stack, false /* includingParents */); } else { - mDefaultDisplay.mTaskContainers.positionStackAtBottom(stack); + mDefaultTaskDisplayArea.positionStackAtBottom(stack); } } else { stack = new StackBuilder(mRootWindowContainer) - .setDisplay(display) + .setTaskDisplayArea(taskDisplayArea) .setWindowingMode(windowingMode) .setActivityType(activityType) .setOnTop(onTop) @@ -1005,7 +1012,7 @@ public class ActivityStackTests extends ActivityTestsBase { @Test public void testWontFinishHomeStackImmediately() { - final ActivityStack homeStack = createStackForShouldBeVisibleTest(mDefaultDisplay, + final ActivityStack homeStack = createStackForShouldBeVisibleTest(mDefaultTaskDisplayArea, WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_HOME, true /* onTop */); ActivityRecord activity = homeStack.topRunningActivity(); @@ -1025,9 +1032,11 @@ public class ActivityStackTests extends ActivityTestsBase { public void testFinishCurrentActivity() { // Create 2 activities on a new display. final DisplayContent display = addNewDisplayContentAt(DisplayContent.POSITION_TOP); - final ActivityStack stack1 = createStackForShouldBeVisibleTest(display, + final ActivityStack stack1 = createStackForShouldBeVisibleTest( + display.getDefaultTaskDisplayArea(), WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */); - final ActivityStack stack2 = createStackForShouldBeVisibleTest(display, + final ActivityStack stack2 = createStackForShouldBeVisibleTest( + display.getDefaultTaskDisplayArea(), WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */); // There is still an activity1 in stack1 so the activity2 should be added to finishing list @@ -1075,26 +1084,26 @@ public class ActivityStackTests extends ActivityTestsBase { @Test public void testStackOrderChangedOnRemoveStack() { StackOrderChangedListener listener = new StackOrderChangedListener(); - mDefaultDisplay.registerStackOrderChangedListener(listener); + mDefaultTaskDisplayArea.registerStackOrderChangedListener(listener); try { - mDefaultDisplay.removeStack(mStack); + mDefaultTaskDisplayArea.removeStack(mStack); } finally { - mDefaultDisplay.unregisterStackOrderChangedListener(listener); + mDefaultTaskDisplayArea.unregisterStackOrderChangedListener(listener); } assertTrue(listener.mChanged); } @Test public void testStackOrderChangedOnAddPositionStack() { - mDefaultDisplay.removeStack(mStack); + mDefaultTaskDisplayArea.removeStack(mStack); StackOrderChangedListener listener = new StackOrderChangedListener(); - mDefaultDisplay.registerStackOrderChangedListener(listener); + mDefaultTaskDisplayArea.registerStackOrderChangedListener(listener); try { mStack.mReparenting = true; - mDefaultDisplay.mTaskContainers.addStack(mStack, 0); + mDefaultTaskDisplayArea.addStack(mStack, 0); } finally { - mDefaultDisplay.unregisterStackOrderChangedListener(listener); + mDefaultTaskDisplayArea.unregisterStackOrderChangedListener(listener); } assertTrue(listener.mChanged); } @@ -1104,12 +1113,12 @@ public class ActivityStackTests extends ActivityTestsBase { StackOrderChangedListener listener = new StackOrderChangedListener(); try { final ActivityStack fullscreenStack1 = createStackForShouldBeVisibleTest( - mDefaultDisplay, WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, + mDefaultTaskDisplayArea, WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */); - mDefaultDisplay.registerStackOrderChangedListener(listener); - mDefaultDisplay.mTaskContainers.positionStackAtBottom(fullscreenStack1); + mDefaultTaskDisplayArea.registerStackOrderChangedListener(listener); + mDefaultTaskDisplayArea.positionStackAtBottom(fullscreenStack1); } finally { - mDefaultDisplay.unregisterStackOrderChangedListener(listener); + mDefaultTaskDisplayArea.unregisterStackOrderChangedListener(listener); } assertTrue(listener.mChanged); } @@ -1189,7 +1198,7 @@ public class ActivityStackTests extends ActivityTestsBase { @Test public void testClearUnknownAppVisibilityBehindFullscreenActivity() { final UnknownAppVisibilityController unknownAppVisibilityController = - mDefaultDisplay.mDisplayContent.mUnknownAppVisibilityController; + mDefaultTaskDisplayArea.mDisplayContent.mUnknownAppVisibilityController; final KeyguardController keyguardController = mSupervisor.getKeyguardController(); doReturn(true).when(keyguardController).isKeyguardLocked(); @@ -1254,7 +1263,7 @@ public class ActivityStackTests extends ActivityTestsBase { } private static class StackOrderChangedListener - implements DisplayContent.OnStackOrderChangedListener { + implements TaskDisplayArea.OnStackOrderChangedListener { public boolean mChanged = false; @Override diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityStartControllerTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityStartControllerTests.java index 76a761ce0e10..27782f5b3683 100644 --- a/services/tests/wmtests/src/com/android/server/wm/ActivityStartControllerTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/ActivityStartControllerTests.java @@ -77,8 +77,8 @@ public class ActivityStartControllerTests extends ActivityTestsBase { .setCreateTask(true) .build(); final int startFlags = random.nextInt(); - final ActivityStack stack = mService.mRootWindowContainer.getDefaultDisplay().createStack( - WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */); + final ActivityStack stack = mService.mRootWindowContainer.getDefaultTaskDisplayArea() + .createStack(WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */); final WindowProcessController wpc = new WindowProcessController(mService, mService.mContext.getApplicationInfo(), "name", 12345, UserHandle.getUserId(12345), mock(Object.class), diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java index 1d952bfcef2a..1cca207d5336 100644 --- a/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java @@ -326,8 +326,9 @@ public class ActivityStarterTests extends ActivityTestsBase { if (mockGetLaunchStack) { // Instrument the stack and task used. - final ActivityStack stack = mRootWindowContainer.getDefaultDisplay().createStack( - WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */); + final ActivityStack stack = mRootWindowContainer.getDefaultTaskDisplayArea() + .createStack(WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, + true /* onTop */); // Direct starter to use spy stack. doReturn(stack).when(mRootWindowContainer) @@ -742,7 +743,7 @@ public class ActivityStarterTests extends ActivityTestsBase { final TestDisplayContent secondaryDisplay = new TestDisplayContent.Builder(mService, 1000, 1500) .setPosition(POSITION_BOTTOM).build(); - final TaskDisplayArea secondaryTaskContainer = secondaryDisplay.mTaskContainers; + final TaskDisplayArea secondaryTaskContainer = secondaryDisplay.getDefaultTaskDisplayArea(); final ActivityStack stack = secondaryTaskContainer.createStack( WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */); @@ -783,7 +784,7 @@ public class ActivityStarterTests extends ActivityTestsBase { new TestDisplayContent.Builder(mService, 1000, 1500).build(); mRootWindowContainer.positionChildAt(POSITION_TOP, secondaryDisplay, false /* includingParents */); - final TaskDisplayArea secondaryTaskContainer = secondaryDisplay.mTaskContainers; + final TaskDisplayArea secondaryTaskContainer = secondaryDisplay.getDefaultTaskDisplayArea(); final ActivityRecord singleTaskActivity = createSingleTaskActivityOn( secondaryTaskContainer.createStack(WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, false /* onTop */)); @@ -835,7 +836,7 @@ public class ActivityStarterTests extends ActivityTestsBase { // Create a secondary display at bottom. final TestDisplayContent secondaryDisplay = addNewDisplayContentAt(POSITION_BOTTOM); - final TaskDisplayArea secondaryTaskContainer = secondaryDisplay.mTaskContainers; + final TaskDisplayArea secondaryTaskContainer = secondaryDisplay.getDefaultTaskDisplayArea(); secondaryTaskContainer.createStack(WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */); @@ -951,7 +952,7 @@ public class ActivityStarterTests extends ActivityTestsBase { final ActivityStarter starter = prepareStarter(0 /* flags */); starter.mStartActivity = new ActivityBuilder(mService).build(); final Task task = new TaskBuilder(mService.mStackSupervisor) - .setStack(mService.mRootWindowContainer.getDefaultDisplay().createStack( + .setStack(mService.mRootWindowContainer.getDefaultTaskDisplayArea().createStack( WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */)) .setUserId(10) .build(); diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityTestsBase.java b/services/tests/wmtests/src/com/android/server/wm/ActivityTestsBase.java index 9240b2222cd6..67d4769522b0 100644 --- a/services/tests/wmtests/src/com/android/server/wm/ActivityTestsBase.java +++ b/services/tests/wmtests/src/com/android/server/wm/ActivityTestsBase.java @@ -372,7 +372,7 @@ class ActivityTestsBase extends SystemServiceTestsBase { Task build() { if (mStack == null && mCreateStack) { - mStack = mSupervisor.mRootWindowContainer.getDefaultDisplay().createStack( + mStack = mSupervisor.mRootWindowContainer.getDefaultTaskDisplayArea().createStack( WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */); spyOn(mStack); } @@ -408,6 +408,7 @@ class ActivityTestsBase extends SystemServiceTestsBase { static class StackBuilder { private final RootWindowContainer mRootWindowContainer; private DisplayContent mDisplay; + private TaskDisplayArea mTaskDisplayArea; private int mStackId = -1; private int mWindowingMode = WINDOWING_MODE_UNDEFINED; private int mActivityType = ACTIVITY_TYPE_STANDARD; @@ -419,6 +420,7 @@ class ActivityTestsBase extends SystemServiceTestsBase { StackBuilder(RootWindowContainer root) { mRootWindowContainer = root; mDisplay = mRootWindowContainer.getDefaultDisplay(); + mTaskDisplayArea = mDisplay.getDefaultTaskDisplayArea(); } StackBuilder setWindowingMode(int windowingMode) { @@ -436,8 +438,20 @@ class ActivityTestsBase extends SystemServiceTestsBase { return this; } + /** + * Set the parent {@link DisplayContent} and use the default task display area. Overrides + * the task display area, if was set before. + */ StackBuilder setDisplay(DisplayContent display) { mDisplay = display; + mTaskDisplayArea = mDisplay.getDefaultTaskDisplayArea(); + return this; + } + + /** Set the parent {@link TaskDisplayArea}. Overrides the display, if was set before. */ + StackBuilder setTaskDisplayArea(TaskDisplayArea taskDisplayArea) { + mTaskDisplayArea = taskDisplayArea; + mDisplay = mTaskDisplayArea.mDisplayContent; return this; } @@ -462,9 +476,8 @@ class ActivityTestsBase extends SystemServiceTestsBase { } ActivityStack build() { - final int stackId = mStackId >= 0 ? mStackId - : mDisplay.mTaskContainers.getNextStackId(); - final ActivityStack stack = mDisplay.mTaskContainers.createStackUnchecked( + final int stackId = mStackId >= 0 ? mStackId : mTaskDisplayArea.getNextStackId(); + final ActivityStack stack = mTaskDisplayArea.createStackUnchecked( mWindowingMode, mActivityType, stackId, mOnTop, mInfo, mIntent, false /* createdByOrganizer */); final ActivityStackSupervisor supervisor = mRootWindowContainer.mStackSupervisor; diff --git a/services/tests/wmtests/src/com/android/server/wm/AppChangeTransitionTests.java b/services/tests/wmtests/src/com/android/server/wm/AppChangeTransitionTests.java index 4cb50c7a9e4d..9b7ffd69da15 100644 --- a/services/tests/wmtests/src/com/android/server/wm/AppChangeTransitionTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/AppChangeTransitionTests.java @@ -147,7 +147,7 @@ public class AppChangeTransitionTests extends WindowTestsBase { // Reparenting to a display with different windowing mode may trigger // a change transition internally, but it should be cleaned-up once // the display change is complete. - mStack.reparent(mDisplayContent, true); + mStack.reparent(mDisplayContent.getDefaultTaskDisplayArea(), true); assertEquals(WINDOWING_MODE_FULLSCREEN, mTask.getWindowingMode()); diff --git a/services/tests/wmtests/src/com/android/server/wm/AppTransitionTests.java b/services/tests/wmtests/src/com/android/server/wm/AppTransitionTests.java index 8b91c7e5d5f3..8c8fd0516623 100644 --- a/services/tests/wmtests/src/com/android/server/wm/AppTransitionTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/AppTransitionTests.java @@ -169,7 +169,7 @@ public class AppTransitionTests extends WindowTestsBase { assertTrue(dc1.mOpeningApps.size() > 0); // Move stack to another display. - stack1.reparent(dc2, true); + stack1.reparent(dc2.getDefaultTaskDisplayArea(), true); // Verify if token are cleared from both pending transition list in former display. assertFalse(dc1.mOpeningApps.contains(activity1)); diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java index 38b3d76b447d..a901d1ebd890 100644 --- a/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java @@ -281,7 +281,7 @@ public class DisplayContentTests extends WindowTestsBase { assertEquals(dc, activity.getDisplayContent()); // Move stack to first display. - mDisplayContent.moveStackToDisplay(stack, true /* onTop */); + stack.reparent(mDisplayContent.getDefaultTaskDisplayArea(), true /* onTop */); assertEquals(mDisplayContent.getDisplayId(), stack.getDisplayContent().getDisplayId()); assertEquals(mDisplayContent, stack.getDisplayContent()); assertEquals(mDisplayContent, task.getDisplayContent()); @@ -753,7 +753,7 @@ public class DisplayContentTests extends WindowTestsBase { doReturn(true).when(freeformStack).isVisible(); freeformStack.getTopChild().setBounds(100, 100, 300, 400); - assertTrue(dc.isStackVisible(WINDOWING_MODE_FREEFORM)); + assertTrue(dc.getDefaultTaskDisplayArea().isStackVisible(WINDOWING_MODE_FREEFORM)); freeformStack.getTopNonFinishingActivity().setOrientation(SCREEN_ORIENTATION_LANDSCAPE); stack.getTopNonFinishingActivity().setOrientation(SCREEN_ORIENTATION_PORTRAIT); @@ -1096,8 +1096,7 @@ public class DisplayContentTests extends WindowTestsBase { @Test public void testGetOrCreateRootHomeTask_defaultDisplay() { - DisplayContent defaultDisplay = mWm.mRoot.getDisplayContent(DEFAULT_DISPLAY); - TaskDisplayArea defaultTaskDisplayArea = defaultDisplay.mTaskContainers; + TaskDisplayArea defaultTaskDisplayArea = mWm.mRoot.getDefaultTaskDisplayArea(); // Remove the current home stack if it exists so a new one can be created below. ActivityStack homeTask = defaultTaskDisplayArea.getRootHomeTask(); @@ -1116,7 +1115,7 @@ public class DisplayContentTests extends WindowTestsBase { doReturn(false).when(display).isUntrustedVirtualDisplay(); // Remove the current home stack if it exists so a new one can be created below. - TaskDisplayArea taskDisplayArea = display.mTaskContainers; + TaskDisplayArea taskDisplayArea = display.getDefaultTaskDisplayArea(); ActivityStack homeTask = taskDisplayArea.getRootHomeTask(); if (homeTask != null) { taskDisplayArea.removeChild(homeTask); @@ -1129,7 +1128,7 @@ public class DisplayContentTests extends WindowTestsBase { @Test public void testGetOrCreateRootHomeTask_unsupportedSystemDecorations() { DisplayContent display = createNewDisplay(); - TaskDisplayArea taskDisplayArea = display.mTaskContainers; + TaskDisplayArea taskDisplayArea = display.getDefaultTaskDisplayArea(); doReturn(false).when(display).supportsSystemDecorations(); assertNull(taskDisplayArea.getRootHomeTask()); @@ -1139,7 +1138,7 @@ public class DisplayContentTests extends WindowTestsBase { @Test public void testGetOrCreateRootHomeTask_untrustedVirtualDisplay() { DisplayContent display = createNewDisplay(); - TaskDisplayArea taskDisplayArea = display.mTaskContainers; + TaskDisplayArea taskDisplayArea = display.getDefaultTaskDisplayArea(); doReturn(true).when(display).isUntrustedVirtualDisplay(); assertNull(taskDisplayArea.getRootHomeTask()); diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayRotationTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayRotationTests.java index cf7411e67135..9b2a2db5d3a8 100644 --- a/services/tests/wmtests/src/com/android/server/wm/DisplayRotationTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/DisplayRotationTests.java @@ -860,6 +860,8 @@ public class DisplayRotationTests { mMockDisplayContent.isDefaultDisplay = mIsDefaultDisplay; when(mMockDisplayContent.calculateDisplayCutoutForRotation(anyInt())) .thenReturn(WmDisplayCutout.NO_CUTOUT); + when(mMockDisplayContent.getDefaultTaskDisplayArea()) + .thenReturn(mock(TaskDisplayArea.class)); mMockDisplayPolicy = mock(DisplayPolicy.class); diff --git a/services/tests/wmtests/src/com/android/server/wm/LaunchParamsPersisterTests.java b/services/tests/wmtests/src/com/android/server/wm/LaunchParamsPersisterTests.java index ae467c0c811d..6a71a7dd24dd 100644 --- a/services/tests/wmtests/src/com/android/server/wm/LaunchParamsPersisterTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/LaunchParamsPersisterTests.java @@ -114,8 +114,8 @@ public class LaunchParamsPersisterTests extends ActivityTestsBase { when(mRootWindowContainer.getDisplayContent(eq(mDisplayUniqueId))) .thenReturn(mTestDisplay); - ActivityStack stack = mTestDisplay.createStack(TEST_WINDOWING_MODE, - ACTIVITY_TYPE_STANDARD, /* onTop */ true); + ActivityStack stack = mTestDisplay.getDefaultTaskDisplayArea() + .createStack(TEST_WINDOWING_MODE, ACTIVITY_TYPE_STANDARD, /* onTop */ true); mTestTask = new TaskBuilder(mSupervisor).setComponent(TEST_COMPONENT).setStack(stack) .build(); mTestTask.mUserId = TEST_USER_ID; @@ -337,8 +337,8 @@ public class LaunchParamsPersisterTests extends ActivityTestsBase { public void testClearsRecordsOfTheUserOnUserCleanUp() { mTarget.saveTask(mTestTask); - ActivityStack stack = mTestDisplay.createStack(TEST_WINDOWING_MODE, - ACTIVITY_TYPE_STANDARD, /* onTop */ true); + ActivityStack stack = mTestDisplay.getDefaultTaskDisplayArea().createStack( + TEST_WINDOWING_MODE, ACTIVITY_TYPE_STANDARD, /* onTop */ true); final Task anotherTaskOfTheSameUser = new TaskBuilder(mSupervisor) .setComponent(ALTERNATIVE_COMPONENT) .setUserId(TEST_USER_ID) @@ -349,7 +349,7 @@ public class LaunchParamsPersisterTests extends ActivityTestsBase { anotherTaskOfTheSameUser.setHasBeenVisible(true); mTarget.saveTask(anotherTaskOfTheSameUser); - stack = mTestDisplay.createStack(TEST_WINDOWING_MODE, + stack = mTestDisplay.getDefaultTaskDisplayArea().createStack(TEST_WINDOWING_MODE, ACTIVITY_TYPE_STANDARD, /* onTop */ true); final Task anotherTaskOfDifferentUser = new TaskBuilder(mSupervisor) .setComponent(TEST_COMPONENT) diff --git a/services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java b/services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java index 071386fa9cbd..d9c3ace4589d 100644 --- a/services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java +++ b/services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java @@ -28,7 +28,6 @@ import static android.content.Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS; import static android.content.Intent.FLAG_ACTIVITY_MULTIPLE_TASK; import static android.content.Intent.FLAG_ACTIVITY_NEW_DOCUMENT; import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK; -import static android.view.Display.DEFAULT_DISPLAY; import static com.android.dx.mockito.inline.extended.ExtendedMockito.doNothing; import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn; @@ -119,7 +118,7 @@ public class RecentTasksTest extends ActivityTestsBase { public void setUp() throws Exception { mTaskPersister = new TestTaskPersister(mContext.getFilesDir()); spyOn(mTaskPersister); - mTaskContainer = mRootWindowContainer.getDisplayContent(DEFAULT_DISPLAY).mTaskContainers; + mTaskContainer = mRootWindowContainer.getDefaultTaskDisplayArea(); // Set the recent tasks we should use for testing in this class. mRecentTasks = new TestRecentTasks(mService, mTaskPersister); diff --git a/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationControllerTest.java index da07baca3ce1..6d2b7b1e86fe 100644 --- a/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationControllerTest.java +++ b/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationControllerTest.java @@ -105,7 +105,7 @@ public class RecentsAnimationControllerTest extends WindowTestsBase { mDefaultDisplay = mWm.mRoot.getDefaultDisplay(); mController = spy(new RecentsAnimationController(mWm, mMockRunner, mAnimationCallbacks, DEFAULT_DISPLAY)); - mRootHomeTask = mDefaultDisplay.getRootHomeTask(); + mRootHomeTask = mDefaultDisplay.getDefaultTaskDisplayArea().getRootHomeTask(); assertNotNull(mRootHomeTask); } diff --git a/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationTest.java b/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationTest.java index 6810f6442c66..881561f5750b 100644 --- a/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationTest.java +++ b/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationTest.java @@ -88,7 +88,7 @@ public class RecentsAnimationTest extends ActivityTestsBase { @Test public void testRecentsActivityVisiblility() { - TaskDisplayArea taskDisplayArea = mRootWindowContainer.getDefaultDisplay().mTaskContainers; + TaskDisplayArea taskDisplayArea = mRootWindowContainer.getDefaultTaskDisplayArea(); ActivityStack recentsStack = taskDisplayArea.createStack(WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_RECENTS, true /* onTop */); ActivityRecord recentActivity = new ActivityBuilder(mService) @@ -116,8 +116,7 @@ public class RecentsAnimationTest extends ActivityTestsBase { @Test public void testPreloadRecentsActivity() { - TaskDisplayArea defaultTaskDisplayArea = mRootWindowContainer.getDefaultDisplay() - .mTaskContainers; + TaskDisplayArea defaultTaskDisplayArea = mRootWindowContainer.getDefaultTaskDisplayArea(); final ActivityStack homeStack = defaultTaskDisplayArea.getStack(WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_HOME); defaultTaskDisplayArea.positionStackAtTop(homeStack, false /* includingParents */); @@ -178,8 +177,7 @@ public class RecentsAnimationTest extends ActivityTestsBase { @Test public void testRestartRecentsActivity() throws Exception { // Have a recents activity that is not attached to its process (ActivityRecord.app = null). - TaskDisplayArea defaultTaskDisplayArea = mRootWindowContainer.getDefaultDisplay() - .mTaskContainers; + TaskDisplayArea defaultTaskDisplayArea = mRootWindowContainer.getDefaultTaskDisplayArea(); ActivityStack recentsStack = defaultTaskDisplayArea.createStack(WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_RECENTS, true /* onTop */); ActivityRecord recentActivity = new ActivityBuilder(mService).setComponent( @@ -208,7 +206,7 @@ public class RecentsAnimationTest extends ActivityTestsBase { @Test public void testSetLaunchTaskBehindOfTargetActivity() { - TaskDisplayArea taskDisplayArea = mRootWindowContainer.getDefaultDisplay().mTaskContainers; + TaskDisplayArea taskDisplayArea = mRootWindowContainer.getDefaultTaskDisplayArea(); ActivityStack homeStack = taskDisplayArea.getRootHomeTask(); // Assume the home activity support recents. ActivityRecord targetActivity = homeStack.getTopNonFinishingActivity(); @@ -253,7 +251,7 @@ public class RecentsAnimationTest extends ActivityTestsBase { @Test public void testCancelAnimationOnVisibleStackOrderChange() { - TaskDisplayArea taskDisplayArea = mRootWindowContainer.getDefaultDisplay().mTaskContainers; + TaskDisplayArea taskDisplayArea = mRootWindowContainer.getDefaultTaskDisplayArea(); ActivityStack fullscreenStack = taskDisplayArea.createStack(WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */); new ActivityBuilder(mService) @@ -298,7 +296,7 @@ public class RecentsAnimationTest extends ActivityTestsBase { @Test public void testKeepAnimationOnHiddenStackOrderChange() { - TaskDisplayArea taskDisplayArea = mRootWindowContainer.getDefaultDisplay().mTaskContainers; + TaskDisplayArea taskDisplayArea = mRootWindowContainer.getDefaultTaskDisplayArea(); ActivityStack fullscreenStack = taskDisplayArea.createStack(WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */); new ActivityBuilder(mService) @@ -334,7 +332,8 @@ public class RecentsAnimationTest extends ActivityTestsBase { @Test public void testMultipleUserHomeActivity_findUserHomeTask() { - TaskDisplayArea taskDisplayArea = mRootWindowContainer.getDefaultDisplay().mTaskContainers; + TaskDisplayArea taskDisplayArea = mRootWindowContainer.getDefaultDisplay() + .getDefaultTaskDisplayArea(); ActivityStack homeStack = taskDisplayArea.getStack(WINDOWING_MODE_UNDEFINED, ACTIVITY_TYPE_HOME); ActivityRecord otherUserHomeActivity = new ActivityBuilder(mService) diff --git a/services/tests/wmtests/src/com/android/server/wm/RootActivityContainerTests.java b/services/tests/wmtests/src/com/android/server/wm/RootActivityContainerTests.java index 836310496d0b..48d4e705ff4b 100644 --- a/services/tests/wmtests/src/com/android/server/wm/RootActivityContainerTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/RootActivityContainerTests.java @@ -89,7 +89,7 @@ public class RootActivityContainerTests extends ActivityTestsBase { @Before public void setUp() throws Exception { - mFullscreenStack = mRootWindowContainer.getDefaultDisplay().createStack( + mFullscreenStack = mRootWindowContainer.getDefaultTaskDisplayArea().createStack( WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */); doNothing().when(mService).updateSleepIfNeededLocked(); } @@ -129,8 +129,8 @@ public class RootActivityContainerTests extends ActivityTestsBase { mRootWindowContainer.moveActivityToPinnedStack(firstActivity, sourceBounds, 0f /*aspectRatio*/, "initialMove"); - final DisplayContent display = mFullscreenStack.getDisplay(); - ActivityStack pinnedStack = display.getRootPinnedTask(); + final TaskDisplayArea taskDisplayArea = mFullscreenStack.getDisplayArea(); + ActivityStack pinnedStack = taskDisplayArea.getRootPinnedTask(); // Ensure a task has moved over. ensureStackPlacement(pinnedStack, firstActivity); ensureStackPlacement(mFullscreenStack, secondActivity); @@ -140,8 +140,9 @@ public class RootActivityContainerTests extends ActivityTestsBase { 0f /*aspectRatio*/, "secondMove"); // Need to get stacks again as a new instance might have been created. - pinnedStack = display.getRootPinnedTask(); - mFullscreenStack = display.getStack(WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD); + pinnedStack = taskDisplayArea.getRootPinnedTask(); + mFullscreenStack = taskDisplayArea.getStack(WINDOWING_MODE_FULLSCREEN, + ACTIVITY_TYPE_STANDARD); // Ensure stacks have swapped tasks. ensureStackPlacement(pinnedStack, secondActivity); ensureStackPlacement(mFullscreenStack, firstActivity); @@ -215,6 +216,8 @@ public class RootActivityContainerTests extends ActivityTestsBase { doReturn(isFocusedStack).when(stack).isFocusedStackOnDisplay(); doReturn(isFocusedStack ? stack : null).when(display).getFocusedStack(); + TaskDisplayArea defaultTaskDisplayArea = display.getDefaultTaskDisplayArea(); + doReturn(isFocusedStack ? stack : null).when(defaultTaskDisplayArea).getFocusedStack(); mRootWindowContainer.applySleepTokens(true); verify(stack, times(expectWakeFromSleep ? 1 : 0)).awakeFromSleepingLocked(); verify(stack, times(expectResumeTopActivity ? 1 : 0)).resumeTopActivityUncheckedLocked( @@ -226,26 +229,29 @@ public class RootActivityContainerTests extends ActivityTestsBase { */ @Test public void testRemovingStackOnAppCrash() { - final DisplayContent defaultDisplay = mRootWindowContainer.getDefaultDisplay(); - final int originalStackCount = defaultDisplay.getStackCount(); - final ActivityStack stack = mRootWindowContainer.getDefaultDisplay().createStack( + final TaskDisplayArea defaultTaskDisplayArea = mRootWindowContainer + .getDefaultTaskDisplayArea(); + final int originalStackCount = defaultTaskDisplayArea.getStackCount(); + final ActivityStack stack = defaultTaskDisplayArea.createStack( WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, false /* onTop */); final ActivityRecord firstActivity = new ActivityBuilder(mService).setCreateTask(true) .setStack(stack).build(); - assertEquals(originalStackCount + 1, defaultDisplay.getStackCount()); + assertEquals(originalStackCount + 1, defaultTaskDisplayArea.getStackCount()); // Let's pretend that the app has crashed. firstActivity.app.setThread(null); mRootWindowContainer.finishTopCrashedActivities(firstActivity.app, "test"); // Verify that the stack was removed. - assertEquals(originalStackCount, defaultDisplay.getStackCount()); + assertEquals(originalStackCount, defaultTaskDisplayArea.getStackCount()); } @Test public void testFocusability() { - final ActivityStack stack = mRootWindowContainer.getDefaultDisplay().createStack( + final TaskDisplayArea defaultTaskDisplayArea = mRootWindowContainer + .getDefaultTaskDisplayArea(); + final ActivityStack stack = defaultTaskDisplayArea.createStack( WINDOWING_MODE_SPLIT_SCREEN_PRIMARY, ACTIVITY_TYPE_STANDARD, true /* onTop */); final ActivityRecord activity = new ActivityBuilder(mService).setCreateTask(true) .setStack(stack).build(); @@ -259,7 +265,7 @@ public class RootActivityContainerTests extends ActivityTestsBase { assertFalse(stack.isTopActivityFocusable()); assertFalse(activity.isFocusable()); - final ActivityStack pinnedStack = mRootWindowContainer.getDefaultDisplay().createStack( + final ActivityStack pinnedStack = defaultTaskDisplayArea.createStack( WINDOWING_MODE_PINNED, ACTIVITY_TYPE_STANDARD, true /* onTop */); final ActivityRecord pinnedActivity = new ActivityBuilder(mService).setCreateTask(true) .setStack(pinnedStack).build(); @@ -288,7 +294,7 @@ public class RootActivityContainerTests extends ActivityTestsBase { @Test public void testSplitScreenPrimaryChosenWhenTopActivityLaunchedToSecondary() { // Create primary split-screen stack with a task and an activity. - final ActivityStack primaryStack = mRootWindowContainer.getDefaultDisplay() + final ActivityStack primaryStack = mRootWindowContainer.getDefaultTaskDisplayArea() .createStack(WINDOWING_MODE_SPLIT_SCREEN_PRIMARY, ACTIVITY_TYPE_STANDARD, true /* onTop */); final Task task = new TaskBuilder(mSupervisor).setStack(primaryStack).build(); @@ -311,7 +317,6 @@ public class RootActivityContainerTests extends ActivityTestsBase { @Test public void testFindTaskToMoveToFrontWhenRecentsOnTop() { // Create stack/task on default display. - final DisplayContent display = mRootWindowContainer.getDefaultDisplay(); final ActivityStack targetStack = new StackBuilder(mRootWindowContainer) .setOnTop(false) .build(); @@ -325,7 +330,7 @@ public class RootActivityContainerTests extends ActivityTestsBase { mSupervisor.findTaskToMoveToFront(targetTask, 0, ActivityOptions.makeBasic(), reason, false); - final TaskDisplayArea taskDisplayArea = display.mTaskContainers; + final TaskDisplayArea taskDisplayArea = mRootWindowContainer.getDefaultTaskDisplayArea(); verify(taskDisplayArea).moveHomeStackToFront(contains(reason)); } @@ -336,8 +341,8 @@ public class RootActivityContainerTests extends ActivityTestsBase { @Test public void testFindTaskToMoveToFrontWhenRecentsOnOtherDisplay() { // Create stack/task on default display. - final DisplayContent display = mRootWindowContainer.getDefaultDisplay(); - final ActivityStack targetStack = display.createStack(WINDOWING_MODE_FULLSCREEN, + final TaskDisplayArea taskDisplayArea = mRootWindowContainer.getDefaultTaskDisplayArea(); + final ActivityStack targetStack = taskDisplayArea.createStack(WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, false /* onTop */); final Task targetTask = new TaskBuilder(mSupervisor).setStack(targetStack).build(); @@ -353,7 +358,6 @@ public class RootActivityContainerTests extends ActivityTestsBase { mSupervisor.findTaskToMoveToFront(targetTask, 0, ActivityOptions.makeBasic(), reason, false); - final TaskDisplayArea taskDisplayArea = display.mTaskContainers; verify(taskDisplayArea, never()).moveHomeStackToFront(contains(reason)); } @@ -364,12 +368,12 @@ public class RootActivityContainerTests extends ActivityTestsBase { @Test public void testResumeActivityWhenNonTopmostStackIsTopFocused() { // Create a stack at bottom. - final DisplayContent display = mRootWindowContainer.getDefaultDisplay(); - final ActivityStack targetStack = spy(display.createStack(WINDOWING_MODE_FULLSCREEN, + final TaskDisplayArea taskDisplayArea = mRootWindowContainer.getDefaultTaskDisplayArea(); + final ActivityStack targetStack = spy(taskDisplayArea.createStack(WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, false /* onTop */)); final Task task = new TaskBuilder(mSupervisor).setStack(targetStack).build(); final ActivityRecord activity = new ActivityBuilder(mService).setTask(task).build(); - display.mTaskContainers.positionStackAtBottom(targetStack); + taskDisplayArea.positionStackAtBottom(targetStack); // Assume the stack is not at the topmost position (e.g. behind always-on-top stacks) but it // is the current top focused stack. @@ -392,10 +396,9 @@ public class RootActivityContainerTests extends ActivityTestsBase { @Test public void testResumeFocusedStacksStartsHomeActivity_NoActivities() { mFullscreenStack.removeIfPossible(); - mService.mRootWindowContainer.getDisplayContent(DEFAULT_DISPLAY).getRootHomeTask() - .removeIfPossible(); - mService.mRootWindowContainer.getDisplayContent(DEFAULT_DISPLAY) - .createStack(WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_HOME, ON_TOP); + final TaskDisplayArea taskDisplayArea = mRootWindowContainer.getDefaultTaskDisplayArea(); + taskDisplayArea.getRootHomeTask().removeIfPossible(); + taskDisplayArea.createStack(WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_HOME, ON_TOP); doReturn(true).when(mRootWindowContainer).resumeHomeActivity(any(), any(), anyInt()); @@ -415,16 +418,15 @@ public class RootActivityContainerTests extends ActivityTestsBase { @Test public void testResumeFocusedStacksStartsHomeActivity_ActivityOnSecondaryScreen() { mFullscreenStack.removeIfPossible(); - mService.mRootWindowContainer.getDisplayContent(DEFAULT_DISPLAY).getRootHomeTask() - .removeIfPossible(); - mService.mRootWindowContainer.getDisplayContent(DEFAULT_DISPLAY) - .createStack(WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_HOME, ON_TOP); + final TaskDisplayArea taskDisplayArea = mRootWindowContainer.getDefaultTaskDisplayArea(); + taskDisplayArea.getRootHomeTask().removeIfPossible(); + taskDisplayArea.createStack(WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_HOME, ON_TOP); // Create an activity on secondary display. final TestDisplayContent secondDisplay = addNewDisplayContentAt( DisplayContent.POSITION_TOP); - final ActivityStack stack = secondDisplay.createStack(WINDOWING_MODE_FULLSCREEN, - ACTIVITY_TYPE_STANDARD, true /* onTop */); + final ActivityStack stack = secondDisplay.getDefaultTaskDisplayArea() + .createStack(WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */); final Task task = new TaskBuilder(mSupervisor).setStack(stack).build(); new ActivityBuilder(mService).setTask(task).build(); @@ -446,8 +448,8 @@ public class RootActivityContainerTests extends ActivityTestsBase { @Test public void testResumeActivityLingeringTransition() { // Create a stack at top. - final DisplayContent display = mRootWindowContainer.getDefaultDisplay(); - final ActivityStack targetStack = spy(display.createStack(WINDOWING_MODE_FULLSCREEN, + final TaskDisplayArea taskDisplayArea = mRootWindowContainer.getDefaultTaskDisplayArea(); + final ActivityStack targetStack = spy(taskDisplayArea.createStack(WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, false /* onTop */)); final Task task = new TaskBuilder(mSupervisor).setStack(targetStack).build(); final ActivityRecord activity = new ActivityBuilder(mService).setTask(task).build(); @@ -466,13 +468,13 @@ public class RootActivityContainerTests extends ActivityTestsBase { @Test public void testResumeActivityLingeringTransition_notExecuted() { // Create a stack at bottom. - final DisplayContent display = mRootWindowContainer.getDefaultDisplay(); - final ActivityStack targetStack = spy(display.createStack(WINDOWING_MODE_FULLSCREEN, + final TaskDisplayArea taskDisplayArea = mRootWindowContainer.getDefaultTaskDisplayArea(); + final ActivityStack targetStack = spy(taskDisplayArea.createStack(WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, false /* onTop */)); final Task task = new TaskBuilder(mSupervisor).setStack(targetStack).build(); final ActivityRecord activity = new ActivityBuilder(mService).setTask(task).build(); activity.setState(ActivityState.RESUMED, "test"); - display.mTaskContainers.positionStackAtBottom(targetStack); + taskDisplayArea.positionStackAtBottom(targetStack); // Assume the stack is at the topmost position assertFalse(targetStack.isTopStackInDisplayArea()); @@ -809,20 +811,20 @@ public class RootActivityContainerTests extends ActivityTestsBase { public void testSwitchUser_missingHomeRootTask() { doReturn(mFullscreenStack).when(mRootWindowContainer).getTopDisplayFocusedStack(); - DisplayContent defaultDisplay = mRootWindowContainer.getDefaultDisplay(); - ActivityStack homeStack = defaultDisplay.getRootHomeTask(); + final TaskDisplayArea taskDisplayArea = mRootWindowContainer.getDefaultTaskDisplayArea(); + ActivityStack homeStack = taskDisplayArea.getRootHomeTask(); if (homeStack != null) { homeStack.removeImmediately(); } - assertNull(defaultDisplay.getRootHomeTask()); + assertNull(taskDisplayArea.getRootHomeTask()); int currentUser = mRootWindowContainer.mCurrentUser; int otherUser = currentUser + 1; mRootWindowContainer.switchUser(otherUser, null); - assertNotNull(defaultDisplay.getRootHomeTask()); - assertEquals(defaultDisplay.getTopStack(), defaultDisplay.getRootHomeTask()); + assertNotNull(taskDisplayArea.getRootHomeTask()); + assertEquals(taskDisplayArea.getTopStack(), taskDisplayArea.getRootHomeTask()); } /** diff --git a/services/tests/wmtests/src/com/android/server/wm/RunningTasksTest.java b/services/tests/wmtests/src/com/android/server/wm/RunningTasksTest.java index d6a67abc9e76..3d3a0f148db5 100644 --- a/services/tests/wmtests/src/com/android/server/wm/RunningTasksTest.java +++ b/services/tests/wmtests/src/com/android/server/wm/RunningTasksTest.java @@ -72,7 +72,8 @@ public class RunningTasksTest extends ActivityTestsBase { final int numTasks = 10; int activeTime = 0; for (int i = 0; i < numTasks; i++) { - createTask(display.getStackAt(i % numStacks), ".Task" + i, i, activeTime++, null); + createTask(display.getDefaultTaskDisplayArea().getStackAt(i % numStacks), + ".Task" + i, i, activeTime++, null); } // Ensure that the latest tasks were returned in order of decreasing last active time, diff --git a/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java b/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java index 893a14541c48..673469474709 100644 --- a/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java @@ -189,7 +189,7 @@ public class SizeCompatTests extends ActivityTestsBase { final int originalDpi = mActivity.getConfiguration().densityDpi; // Move the non-resizable activity to the new display. - mStack.reparent(newDisplay.mDisplayContent, true /* onTop */); + mStack.reparent(newDisplay.getDefaultTaskDisplayArea(), true /* onTop */); assertEquals(originalBounds.width(), mActivity.getBounds().width()); assertEquals(originalBounds.height(), mActivity.getBounds().height()); @@ -257,7 +257,7 @@ public class SizeCompatTests extends ActivityTestsBase { .setCanRotate(false).setNotch(notchHeight).build(); // Move the non-resizable activity to the new display. - mStack.reparent(newDisplay, true /* onTop */); + mStack.reparent(newDisplay.getDefaultTaskDisplayArea(), true /* onTop */); // The configuration bounds should keep the same. assertEquals(origWidth, configBounds.width()); assertEquals(origHeight, configBounds.height()); diff --git a/services/tests/wmtests/src/com/android/server/wm/SystemServicesTestRule.java b/services/tests/wmtests/src/com/android/server/wm/SystemServicesTestRule.java index af76e7fc0b76..af3ec38631ae 100644 --- a/services/tests/wmtests/src/com/android/server/wm/SystemServicesTestRule.java +++ b/services/tests/wmtests/src/com/android/server/wm/SystemServicesTestRule.java @@ -316,7 +316,7 @@ public class SystemServicesTestRule implements TestRule { // that the default display is in fullscreen mode. display.setDisplayWindowingMode(WINDOWING_MODE_FULLSCREEN); spyOn(display); - final TaskDisplayArea taskDisplayArea = display.mTaskContainers; + final TaskDisplayArea taskDisplayArea = display.getDefaultTaskDisplayArea(); spyOn(taskDisplayArea); final ActivityStack homeStack = taskDisplayArea.getStack( WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_HOME); diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskLaunchParamsModifierTests.java b/services/tests/wmtests/src/com/android/server/wm/TaskLaunchParamsModifierTests.java index a3446d16d9f3..1a38ff283477 100644 --- a/services/tests/wmtests/src/com/android/server/wm/TaskLaunchParamsModifierTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/TaskLaunchParamsModifierTests.java @@ -1312,14 +1312,14 @@ public class TaskLaunchParamsModifierTests extends ActivityTestsBase { } private ActivityRecord createSourceActivity(TestDisplayContent display) { - final ActivityStack stack = display.createStack(display.getWindowingMode(), - ACTIVITY_TYPE_STANDARD, true); + final ActivityStack stack = display.getDefaultTaskDisplayArea() + .createStack(display.getWindowingMode(), ACTIVITY_TYPE_STANDARD, true); return new ActivityBuilder(mService).setStack(stack).setCreateTask(true).build(); } private void addFreeformTaskTo(TestDisplayContent display, Rect bounds) { - final ActivityStack stack = display.createStack(display.getWindowingMode(), - ACTIVITY_TYPE_STANDARD, true); + final ActivityStack stack = display.getDefaultTaskDisplayArea() + .createStack(display.getWindowingMode(), ACTIVITY_TYPE_STANDARD, true); stack.setWindowingMode(WINDOWING_MODE_FREEFORM); final Task task = new TaskBuilder(mSupervisor).setStack(stack).build(); // Just work around the unnecessary adjustments for bounds. diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskRecordTests.java b/services/tests/wmtests/src/com/android/server/wm/TaskRecordTests.java index f76809b06510..50584c61cf92 100644 --- a/services/tests/wmtests/src/com/android/server/wm/TaskRecordTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/TaskRecordTests.java @@ -203,9 +203,9 @@ public class TaskRecordTests extends ActivityTestsBase { @Test public void testFitWithinBounds() { final Rect parentBounds = new Rect(10, 10, 200, 200); - DisplayContent display = mService.mRootWindowContainer.getDefaultDisplay(); - ActivityStack stack = display.createStack(WINDOWING_MODE_FREEFORM, ACTIVITY_TYPE_STANDARD, - true /* onTop */); + TaskDisplayArea taskDisplayArea = mService.mRootWindowContainer.getDefaultTaskDisplayArea(); + ActivityStack stack = taskDisplayArea.createStack(WINDOWING_MODE_FREEFORM, + ACTIVITY_TYPE_STANDARD, true /* onTop */); Task task = new TaskBuilder(mSupervisor).setStack(stack).build(); final Configuration parentConfig = stack.getConfiguration(); parentConfig.windowConfiguration.setBounds(parentBounds); @@ -438,9 +438,9 @@ public class TaskRecordTests extends ActivityTestsBase { @Test public void testInsetDisregardedWhenFreeformOverlapsNavBar() { - DisplayContent display = mService.mRootWindowContainer.getDefaultDisplay(); - ActivityStack stack = display.createStack(WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, - true /* onTop */); + TaskDisplayArea taskDisplayArea = mService.mRootWindowContainer.getDefaultTaskDisplayArea(); + ActivityStack stack = taskDisplayArea.createStack(WINDOWING_MODE_FULLSCREEN, + ACTIVITY_TYPE_STANDARD, true /* onTop */); DisplayInfo displayInfo = new DisplayInfo(); mService.mContext.getDisplay().getDisplayInfo(displayInfo); final int displayHeight = displayInfo.logicalHeight; @@ -959,8 +959,8 @@ public class TaskRecordTests extends ActivityTestsBase { private void testStackBoundsConfiguration(int windowingMode, Rect parentBounds, Rect bounds, Rect expectedConfigBounds) { - DisplayContent display = mService.mRootWindowContainer.getDefaultDisplay(); - ActivityStack stack = display.createStack(windowingMode, ACTIVITY_TYPE_STANDARD, + TaskDisplayArea taskDisplayArea = mService.mRootWindowContainer.getDefaultTaskDisplayArea(); + ActivityStack stack = taskDisplayArea.createStack(windowingMode, ACTIVITY_TYPE_STANDARD, true /* onTop */); Task task = new TaskBuilder(mSupervisor).setStack(stack).build(); diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskStackTests.java b/services/tests/wmtests/src/com/android/server/wm/TaskStackTests.java index 6387a3b7c474..d48e82723295 100644 --- a/services/tests/wmtests/src/com/android/server/wm/TaskStackTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/TaskStackTests.java @@ -171,7 +171,7 @@ public class TaskStackTests extends WindowTestsBase { // Reparent clearInvocations(task1); // reset the number of onDisplayChanged for task. - stack1.reparent(dc, true /* onTop */); + stack1.reparent(dc.getDefaultTaskDisplayArea(), true /* onTop */); assertEquals(dc, stack1.getDisplayContent()); final int stack1PositionInParent = stack1.getParent().mChildren.indexOf(stack1); final int stack2PositionInParent = stack1.getParent().mChildren.indexOf(stack2); diff --git a/services/tests/wmtests/src/com/android/server/wm/WallpaperControllerTests.java b/services/tests/wmtests/src/com/android/server/wm/WallpaperControllerTests.java index 900f014a0218..a4f1487dde1e 100644 --- a/services/tests/wmtests/src/com/android/server/wm/WallpaperControllerTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/WallpaperControllerTests.java @@ -254,7 +254,7 @@ public class WallpaperControllerTests extends WindowTestsBase { private WindowState createWallpaperTargetWindow(DisplayContent dc) { final ActivityRecord homeActivity = new ActivityTestsBase.ActivityBuilder(mWm.mAtmService) - .setStack(dc.getRootHomeTask()) + .setStack(dc.getDefaultTaskDisplayArea().getRootHomeTask()) .setCreateTask(true) .build(); homeActivity.setVisibility(true); diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowContainerTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowContainerTests.java index 27ea37dfeb19..118c2e4db208 100644 --- a/services/tests/wmtests/src/com/android/server/wm/WindowContainerTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/WindowContainerTests.java @@ -780,8 +780,8 @@ public class WindowContainerTests extends WindowTestsBase { WindowTestUtils.createActivityRecordInTask(mDisplayContent, task); final DisplayContent newDc = createNewDisplay(); - mDisplayContent.removeStack(stack); - newDc.mTaskContainers.addChild(stack, POSITION_TOP); + stack.getDisplayArea().removeStack(stack); + newDc.getDefaultTaskDisplayArea().addChild(stack, POSITION_TOP); verify(stack).onDisplayChanged(newDc); verify(task).onDisplayChanged(newDc); diff --git a/tests/net/common/java/android/net/LinkPropertiesTest.java b/tests/net/common/java/android/net/LinkPropertiesTest.java index 8de27e8eb281..0fc9be32f4cf 100644 --- a/tests/net/common/java/android/net/LinkPropertiesTest.java +++ b/tests/net/common/java/android/net/LinkPropertiesTest.java @@ -16,6 +16,8 @@ package android.net; +import static android.net.RouteInfo.RTN_UNREACHABLE; + import static com.android.testutils.ParcelUtilsKt.assertParcelSane; import static com.android.testutils.ParcelUtilsKt.assertParcelingIsLossless; import static com.android.testutils.ParcelUtilsKt.parcelingRoundTrip; @@ -46,6 +48,7 @@ import org.junit.Test; import org.junit.runner.RunWith; import java.net.Inet4Address; +import java.net.Inet6Address; import java.net.InetAddress; import java.util.ArrayList; import java.util.Arrays; @@ -1257,4 +1260,26 @@ public class LinkPropertiesTest { final LinkProperties Ipv6 = makeIpv6LinkProperties(); assertTrue(Ipv6.hasIpv6DnsServer()); } + + @Test @IgnoreUpTo(Build.VERSION_CODES.Q) + public void testHasIpv4UnreachableDefaultRoute() { + final LinkProperties lp = makeTestObject(); + assertFalse(lp.hasIpv4UnreachableDefaultRoute()); + assertFalse(lp.hasIpv6UnreachableDefaultRoute()); + + lp.addRoute(new RouteInfo(new IpPrefix(Inet4Address.ANY, 0), RTN_UNREACHABLE)); + assertTrue(lp.hasIpv4UnreachableDefaultRoute()); + assertFalse(lp.hasIpv6UnreachableDefaultRoute()); + } + + @Test @IgnoreUpTo(Build.VERSION_CODES.Q) + public void testHasIpv6UnreachableDefaultRoute() { + final LinkProperties lp = makeTestObject(); + assertFalse(lp.hasIpv6UnreachableDefaultRoute()); + assertFalse(lp.hasIpv4UnreachableDefaultRoute()); + + lp.addRoute(new RouteInfo(new IpPrefix(Inet6Address.ANY, 0), RTN_UNREACHABLE)); + assertTrue(lp.hasIpv6UnreachableDefaultRoute()); + assertFalse(lp.hasIpv4UnreachableDefaultRoute()); + } } diff --git a/tests/net/common/java/android/net/RouteInfoTest.java b/tests/net/common/java/android/net/RouteInfoTest.java index 1658262c17f6..8204b494bbb8 100644 --- a/tests/net/common/java/android/net/RouteInfoTest.java +++ b/tests/net/common/java/android/net/RouteInfoTest.java @@ -31,6 +31,7 @@ import static org.junit.Assert.fail; import android.os.Build; +import androidx.core.os.BuildCompat; import androidx.test.filters.SmallTest; import androidx.test.runner.AndroidJUnit4; @@ -62,6 +63,11 @@ public class RouteInfoTest { return new IpPrefix(prefix); } + private static boolean isAtLeastR() { + // BuildCompat.isAtLeastR is documented to return false on release SDKs (including R) + return Build.VERSION.SDK_INT > Build.VERSION_CODES.Q || BuildCompat.isAtLeastR(); + } + @Test public void testConstructor() { RouteInfo r; @@ -195,78 +201,130 @@ public class RouteInfoTest { assertTrue(r.isDefaultRoute()); assertTrue(r.isIPv4Default()); assertFalse(r.isIPv6Default()); + if (isAtLeastR()) { + assertFalse(r.isIPv4UnreachableDefault()); + assertFalse(r.isIPv6UnreachableDefault()); + } r = new RouteInfo(Prefix("::/0"), Address("::"), "wlan0"); assertFalse(r.isHostRoute()); assertTrue(r.isDefaultRoute()); assertFalse(r.isIPv4Default()); assertTrue(r.isIPv6Default()); + if (isAtLeastR()) { + assertFalse(r.isIPv4UnreachableDefault()); + assertFalse(r.isIPv6UnreachableDefault()); + } r = new RouteInfo(Prefix("192.0.2.0/24"), null, "wlan0"); assertFalse(r.isHostRoute()); assertFalse(r.isDefaultRoute()); assertFalse(r.isIPv4Default()); assertFalse(r.isIPv6Default()); + if (isAtLeastR()) { + assertFalse(r.isIPv4UnreachableDefault()); + assertFalse(r.isIPv6UnreachableDefault()); + } r = new RouteInfo(Prefix("2001:db8::/48"), null, "wlan0"); assertFalse(r.isHostRoute()); assertFalse(r.isDefaultRoute()); assertFalse(r.isIPv4Default()); assertFalse(r.isIPv6Default()); + if (isAtLeastR()) { + assertFalse(r.isIPv4UnreachableDefault()); + assertFalse(r.isIPv6UnreachableDefault()); + } r = new RouteInfo(Prefix("192.0.2.0/32"), Address("0.0.0.0"), "wlan0"); assertTrue(r.isHostRoute()); assertFalse(r.isDefaultRoute()); assertFalse(r.isIPv4Default()); assertFalse(r.isIPv6Default()); + if (isAtLeastR()) { + assertFalse(r.isIPv4UnreachableDefault()); + assertFalse(r.isIPv6UnreachableDefault()); + } r = new RouteInfo(Prefix("2001:db8::/128"), Address("::"), "wlan0"); assertTrue(r.isHostRoute()); assertFalse(r.isDefaultRoute()); assertFalse(r.isIPv4Default()); assertFalse(r.isIPv6Default()); + if (isAtLeastR()) { + assertFalse(r.isIPv4UnreachableDefault()); + assertFalse(r.isIPv6UnreachableDefault()); + } r = new RouteInfo(Prefix("192.0.2.0/32"), null, "wlan0"); assertTrue(r.isHostRoute()); assertFalse(r.isDefaultRoute()); assertFalse(r.isIPv4Default()); assertFalse(r.isIPv6Default()); + if (isAtLeastR()) { + assertFalse(r.isIPv4UnreachableDefault()); + assertFalse(r.isIPv6UnreachableDefault()); + } r = new RouteInfo(Prefix("2001:db8::/128"), null, "wlan0"); assertTrue(r.isHostRoute()); assertFalse(r.isDefaultRoute()); assertFalse(r.isIPv4Default()); assertFalse(r.isIPv6Default()); + if (isAtLeastR()) { + assertFalse(r.isIPv4UnreachableDefault()); + assertFalse(r.isIPv6UnreachableDefault()); + } r = new RouteInfo(Prefix("::/128"), Address("fe80::"), "wlan0"); assertTrue(r.isHostRoute()); assertFalse(r.isDefaultRoute()); assertFalse(r.isIPv4Default()); assertFalse(r.isIPv6Default()); + if (isAtLeastR()) { + assertFalse(r.isIPv4UnreachableDefault()); + assertFalse(r.isIPv6UnreachableDefault()); + } r = new RouteInfo(Prefix("0.0.0.0/32"), Address("192.0.2.1"), "wlan0"); assertTrue(r.isHostRoute()); assertFalse(r.isDefaultRoute()); assertFalse(r.isIPv4Default()); assertFalse(r.isIPv6Default()); + if (isAtLeastR()) { + assertFalse(r.isIPv4UnreachableDefault()); + assertFalse(r.isIPv6UnreachableDefault()); + } r = new RouteInfo(Prefix("0.0.0.0/32"), Address("192.0.2.1"), "wlan0"); assertTrue(r.isHostRoute()); assertFalse(r.isDefaultRoute()); assertFalse(r.isIPv4Default()); assertFalse(r.isIPv6Default()); + if (isAtLeastR()) { + assertFalse(r.isIPv4UnreachableDefault()); + assertFalse(r.isIPv6UnreachableDefault()); + } r = new RouteInfo(new IpPrefix(Inet4Address.ANY, 0), RTN_UNREACHABLE); assertFalse(r.isHostRoute()); assertFalse(r.isDefaultRoute()); assertFalse(r.isIPv4Default()); assertFalse(r.isIPv6Default()); + if (isAtLeastR()) { + assertTrue(r.isIPv4UnreachableDefault()); + assertFalse(r.isIPv6UnreachableDefault()); + } r = new RouteInfo(new IpPrefix(Inet6Address.ANY, 0), RTN_UNREACHABLE); assertFalse(r.isHostRoute()); assertFalse(r.isDefaultRoute()); assertFalse(r.isIPv4Default()); assertFalse(r.isIPv6Default()); + if (isAtLeastR()) { + assertFalse(r.isIPv4UnreachableDefault()); + assertTrue(r.isIPv6UnreachableDefault()); + } } @Test diff --git a/tests/net/java/com/android/server/ConnectivityServiceTest.java b/tests/net/java/com/android/server/ConnectivityServiceTest.java index b02398d1f1f5..912a27f08f30 100644 --- a/tests/net/java/com/android/server/ConnectivityServiceTest.java +++ b/tests/net/java/com/android/server/ConnectivityServiceTest.java @@ -6336,6 +6336,7 @@ public class ConnectivityServiceTest { LinkProperties lp = new LinkProperties(); lp.setInterfaceName("tun0"); lp.addRoute(new RouteInfo(new IpPrefix(Inet4Address.ANY, 0), null)); + lp.addRoute(new RouteInfo(new IpPrefix(Inet6Address.ANY, 0), RTN_UNREACHABLE)); // The uid range needs to cover the test app so the network is visible to it. final Set<UidRange> vpnRange = Collections.singleton(UidRange.createForUser(VPN_USER)); final TestNetworkAgentWrapper vpnNetworkAgent = establishVpn(lp, VPN_UID, vpnRange); @@ -6361,6 +6362,7 @@ public class ConnectivityServiceTest { public void testLegacyVpnDoesNotResultInInterfaceFilteringRule() throws Exception { LinkProperties lp = new LinkProperties(); lp.setInterfaceName("tun0"); + lp.addRoute(new RouteInfo(new IpPrefix(Inet6Address.ANY, 0), null)); lp.addRoute(new RouteInfo(new IpPrefix(Inet4Address.ANY, 0), null)); // The uid range needs to cover the test app so the network is visible to it. final Set<UidRange> vpnRange = Collections.singleton(UidRange.createForUser(VPN_USER)); @@ -6392,6 +6394,7 @@ public class ConnectivityServiceTest { LinkProperties lp = new LinkProperties(); lp.setInterfaceName("tun0"); lp.addRoute(new RouteInfo(new IpPrefix(Inet4Address.ANY, 0), null)); + lp.addRoute(new RouteInfo(new IpPrefix(Inet6Address.ANY, 0), null)); // The uid range needs to cover the test app so the network is visible to it. final Set<UidRange> vpnRange = Collections.singleton(UidRange.createForUser(VPN_USER)); final TestNetworkAgentWrapper vpnNetworkAgent = establishVpn(lp, VPN_UID, vpnRange); @@ -6428,6 +6431,7 @@ public class ConnectivityServiceTest { reset(mMockNetd); lp = new LinkProperties(); lp.setInterfaceName("tun1"); + lp.addRoute(new RouteInfo(new IpPrefix(Inet4Address.ANY, 0), RTN_UNREACHABLE)); lp.addRoute(new RouteInfo(new IpPrefix(Inet6Address.ANY, 0), null)); vpnNetworkAgent.sendLinkProperties(lp); waitForIdle(); @@ -6440,6 +6444,7 @@ public class ConnectivityServiceTest { public void testUidUpdateChangesInterfaceFilteringRule() throws Exception { LinkProperties lp = new LinkProperties(); lp.setInterfaceName("tun0"); + lp.addRoute(new RouteInfo(new IpPrefix(Inet4Address.ANY, 0), RTN_UNREACHABLE)); lp.addRoute(new RouteInfo(new IpPrefix(Inet6Address.ANY, 0), null)); // The uid range needs to cover the test app so the network is visible to it. final UidRange vpnRange = UidRange.createForUser(VPN_USER); diff --git a/wifi/java/android/net/wifi/WifiManager.java b/wifi/java/android/net/wifi/WifiManager.java index dfeea9f4b52e..f1be8b20eb53 100644 --- a/wifi/java/android/net/wifi/WifiManager.java +++ b/wifi/java/android/net/wifi/WifiManager.java @@ -1887,12 +1887,20 @@ public class WifiManager { * <li> If user reset network settings, all added suggestions will be discarded. Apps can use * {@link #getNetworkSuggestions()} to check if their suggestions are in the device.</li> * <li> In-place modification of existing suggestions are allowed. - * If the provided suggestions {@link WifiNetworkSuggestion#equals(Object)} any previously - * provided suggestions by the app. Previous suggestions will be updated</li> + * <li>If the provided suggestions includes any previously provided suggestions by the app, + * previous suggestions will be updated.</li> + * <li>If one of the provided suggestions marks a previously unmetered suggestion as metered and + * the device is currently connected to that suggested network, then the device will disconnect + * from that network. The system will immediately re-evaluate all the network candidates + * and possibly reconnect back to the same suggestion. This disconnect is to make sure that any + * traffic flowing over unmetered networks isn't accidentally continued over a metered network. + * </li> + * </li> * * @param networkSuggestions List of network suggestions provided by the app. * @return Status code for the operation. One of the STATUS_NETWORK_SUGGESTIONS_ values. * @throws {@link SecurityException} if the caller is missing required permissions. + * @see WifiNetworkSuggestion#equals(Object) */ @RequiresPermission(android.Manifest.permission.CHANGE_WIFI_STATE) public @NetworkSuggestionsStatusCode int addNetworkSuggestions( @@ -4152,6 +4160,10 @@ public class WifiManager { * * This function is used instead of a enableNetwork() and reconnect() * + * <li> This API will cause reconnect if the credentials of the current active + * connection has been changed.</li> + * <li> This API will cause reconnect if the current active connection is marked metered.</li> + * * @param networkId the ID of the network as returned by {@link #addNetwork} or {@link * getConfiguredNetworks}. * @param listener for callbacks on success or failure. Can be null. @@ -4180,8 +4192,9 @@ public class WifiManager { * * For an existing network, it accomplishes the task of updateNetwork() * - * This API will cause reconnect if the crecdentials of the current active - * connection has been changed. + * <li> This API will cause reconnect if the credentials of the current active + * connection has been changed.</li> + * <li> This API will cause disconnect if the current active connection is marked metered.</li> * * @param config the set of variables that describe the configuration, * contained in a {@link WifiConfiguration} object. diff --git a/wifi/java/android/net/wifi/WifiNetworkSuggestion.java b/wifi/java/android/net/wifi/WifiNetworkSuggestion.java index 4c524f49e4df..cedf9b0b872d 100644 --- a/wifi/java/android/net/wifi/WifiNetworkSuggestion.java +++ b/wifi/java/android/net/wifi/WifiNetworkSuggestion.java @@ -543,7 +543,7 @@ public final class WifiNetworkSuggestion implements Parcelable { wifiConfiguration.priority = mPriority; wifiConfiguration.meteredOverride = mIsMetered ? WifiConfiguration.METERED_OVERRIDE_METERED - : WifiConfiguration.METERED_OVERRIDE_NONE; + : WifiConfiguration.METERED_OVERRIDE_NOT_METERED; wifiConfiguration.carrierId = mCarrierId; wifiConfiguration.trusted = !mIsNetworkUntrusted; return wifiConfiguration; diff --git a/wifi/tests/src/android/net/wifi/WifiNetworkSuggestionTest.java b/wifi/tests/src/android/net/wifi/WifiNetworkSuggestionTest.java index ac2f6b26aa00..aca190910ed1 100644 --- a/wifi/tests/src/android/net/wifi/WifiNetworkSuggestionTest.java +++ b/wifi/tests/src/android/net/wifi/WifiNetworkSuggestionTest.java @@ -56,7 +56,7 @@ public class WifiNetworkSuggestionTest { .get(WifiConfiguration.KeyMgmt.NONE)); assertTrue(suggestion.isAppInteractionRequired); assertFalse(suggestion.isUserInteractionRequired); - assertEquals(WifiConfiguration.METERED_OVERRIDE_NONE, + assertEquals(WifiConfiguration.METERED_OVERRIDE_NOT_METERED, suggestion.wifiConfiguration.meteredOverride); assertEquals(-1, suggestion.wifiConfiguration.priority); assertFalse(suggestion.isUserAllowedToManuallyConnect); @@ -86,7 +86,7 @@ public class WifiNetworkSuggestionTest { suggestion.wifiConfiguration.preSharedKey); assertTrue(suggestion.isAppInteractionRequired); assertFalse(suggestion.isUserInteractionRequired); - assertEquals(WifiConfiguration.METERED_OVERRIDE_NONE, + assertEquals(WifiConfiguration.METERED_OVERRIDE_NOT_METERED, suggestion.wifiConfiguration.meteredOverride); assertEquals(0, suggestion.wifiConfiguration.priority); assertFalse(suggestion.isUserAllowedToManuallyConnect); |