Merge "Revert "Move brightnessMaxReason to DisplayBrightnessState"" into main
diff --git a/core/java/android/util/FeatureFlagUtils.java b/core/java/android/util/FeatureFlagUtils.java
index 0714285..d8a88b8 100644
--- a/core/java/android/util/FeatureFlagUtils.java
+++ b/core/java/android/util/FeatureFlagUtils.java
@@ -138,12 +138,6 @@
"settings_show_stylus_preferences";
/**
- * Flag to enable/disable biometrics enrollment v2
- * @hide
- */
- public static final String SETTINGS_BIOMETRICS2_ENROLLMENT = "settings_biometrics2_enrollment";
-
- /**
* Flag to enable/disable FingerprintSettings v2
* @hide
*/
@@ -223,7 +217,6 @@
DEFAULT_FLAGS.put(SETTINGS_ENABLE_SPA_METRICS, "true");
DEFAULT_FLAGS.put(SETTINGS_ADB_METRICS_WRITER, "false");
DEFAULT_FLAGS.put(SETTINGS_SHOW_STYLUS_PREFERENCES, "true");
- DEFAULT_FLAGS.put(SETTINGS_BIOMETRICS2_ENROLLMENT, "false");
DEFAULT_FLAGS.put(SETTINGS_PREFER_ACCESSIBILITY_MENU_IN_SYSTEM, "false");
DEFAULT_FLAGS.put(SETTINGS_AUDIO_ROUTING, "false");
DEFAULT_FLAGS.put(SETTINGS_FLASH_NOTIFICATIONS, "true");
diff --git a/core/java/android/window/ActivityWindowInfo.java b/core/java/android/window/ActivityWindowInfo.java
index 946bb82..71c500c 100644
--- a/core/java/android/window/ActivityWindowInfo.java
+++ b/core/java/android/window/ActivityWindowInfo.java
@@ -18,6 +18,8 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.app.Activity;
+import android.app.ActivityThread;
import android.graphics.Rect;
import android.os.Parcel;
import android.os.Parcelable;
@@ -144,4 +146,15 @@
+ ", taskFragmentBounds=" + mTaskFragmentBounds
+ "}";
}
+
+ /** Gets the {@link ActivityWindowInfo} of the given activity. */
+ @Nullable
+ public static ActivityWindowInfo getActivityWindowInfo(@NonNull Activity activity) {
+ if (activity.isFinishing()) {
+ return null;
+ }
+ final ActivityThread.ActivityClientRecord record = ActivityThread.currentActivityThread()
+ .getActivityClient(activity.getActivityToken());
+ return record != null ? record.getActivityWindowInfo() : null;
+ }
}
diff --git a/core/java/android/window/ITaskFragmentOrganizerController.aidl b/core/java/android/window/ITaskFragmentOrganizerController.aidl
index 2c64b8e..ac57c00 100644
--- a/core/java/android/window/ITaskFragmentOrganizerController.aidl
+++ b/core/java/android/window/ITaskFragmentOrganizerController.aidl
@@ -39,12 +39,6 @@
void unregisterOrganizer(in ITaskFragmentOrganizer organizer);
/**
- * Checks if an activity organized by a {@link android.window.TaskFragmentOrganizer} and
- * only occupies a portion of Task bounds.
- */
- boolean isActivityEmbedded(in IBinder activityToken);
-
- /**
* Notifies the server that the organizer has finished handling the given transaction. The
* server should apply the given {@link WindowContainerTransaction} for the necessary changes.
*/
diff --git a/core/java/android/window/TaskFragmentOrganizer.java b/core/java/android/window/TaskFragmentOrganizer.java
index 15f1258..8e429cb 100644
--- a/core/java/android/window/TaskFragmentOrganizer.java
+++ b/core/java/android/window/TaskFragmentOrganizer.java
@@ -21,6 +21,7 @@
import static android.view.WindowManager.TRANSIT_FIRST_CUSTOM;
import static android.view.WindowManager.TRANSIT_NONE;
import static android.view.WindowManager.TRANSIT_OPEN;
+import static android.window.ActivityWindowInfo.getActivityWindowInfo;
import android.annotation.CallSuper;
import android.annotation.FlaggedApi;
@@ -29,6 +30,7 @@
import android.annotation.Nullable;
import android.annotation.RequiresPermission;
import android.annotation.TestApi;
+import android.app.Activity;
import android.os.Bundle;
import android.os.IBinder;
import android.os.RemoteException;
@@ -38,6 +40,7 @@
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
+import java.util.Objects;
import java.util.concurrent.Executor;
/**
@@ -324,16 +327,15 @@
}
/**
- * Checks if an activity organized by a {@link android.window.TaskFragmentOrganizer} and
+ * Checks if an activity is organized by a {@link android.window.TaskFragmentOrganizer} and
* only occupies a portion of Task bounds.
+ *
+ * @see ActivityWindowInfo for additional window info.
* @hide
*/
- // TODO(b/287582673): cleanup
- public boolean isActivityEmbedded(@NonNull IBinder activityToken) {
- try {
- return getController().isActivityEmbedded(activityToken);
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- }
+ public static boolean isActivityEmbedded(@NonNull Activity activity) {
+ Objects.requireNonNull(activity);
+ final ActivityWindowInfo activityWindowInfo = getActivityWindowInfo(activity);
+ return activityWindowInfo != null && activityWindowInfo.isEmbedded();
}
}
diff --git a/core/java/android/window/flags/large_screen_experiences_app_compat.aconfig b/core/java/android/window/flags/large_screen_experiences_app_compat.aconfig
index cc880e1..48fb2b3 100644
--- a/core/java/android/window/flags/large_screen_experiences_app_compat.aconfig
+++ b/core/java/android/window/flags/large_screen_experiences_app_compat.aconfig
@@ -91,6 +91,13 @@
}
flag {
+ name: "camera_compat_fullscreen_pick_same_task_activity"
+ namespace: "large_screen_experiences_app_compat"
+ description: "Limit undo of camera compat treatment to the same task that started the treatment."
+ bug: "350495350"
+}
+
+flag {
name: "app_compat_refactoring"
namespace: "large_screen_experiences_app_compat"
description: "Whether the changes about app compat refactoring are enabled./n"
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java
index b120723..8e1fde0 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java
@@ -22,6 +22,7 @@
import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
import static android.view.Display.DEFAULT_DISPLAY;
import static android.view.WindowManager.TRANSIT_CLOSE;
+import static android.window.ActivityWindowInfo.getActivityWindowInfo;
import static android.window.TaskFragmentOperation.OP_TYPE_REPARENT_ACTIVITY_TO_TASK_FRAGMENT;
import static android.window.TaskFragmentOperation.OP_TYPE_START_ACTIVITY_IN_TASK_FRAGMENT;
import static android.window.TaskFragmentOrganizer.KEY_ERROR_CALLBACK_OP_TYPE;
@@ -80,6 +81,7 @@
import android.window.TaskFragmentAnimationParams;
import android.window.TaskFragmentInfo;
import android.window.TaskFragmentOperation;
+import android.window.TaskFragmentOrganizer;
import android.window.TaskFragmentParentInfo;
import android.window.TaskFragmentTransaction;
import android.window.WindowContainerTransaction;
@@ -2553,9 +2555,9 @@
return ActivityThread.currentActivityThread().getActivity(activityToken);
}
- @VisibleForTesting
@Nullable
- ActivityThread.ActivityClientRecord getActivityClientRecord(@NonNull Activity activity) {
+ private ActivityThread.ActivityClientRecord getActivityClientRecord(
+ @NonNull Activity activity) {
return ActivityThread.currentActivityThread()
.getActivityClient(activity.getActivityToken());
}
@@ -3092,10 +3094,8 @@
*/
@Override
public boolean isActivityEmbedded(@NonNull Activity activity) {
- Objects.requireNonNull(activity);
synchronized (mLock) {
- final ActivityWindowInfo activityWindowInfo = getActivityWindowInfo(activity);
- return activityWindowInfo != null && activityWindowInfo.isEmbedded();
+ return TaskFragmentOrganizer.isActivityEmbedded(activity);
}
}
@@ -3165,15 +3165,6 @@
}
}
- @Nullable
- private ActivityWindowInfo getActivityWindowInfo(@NonNull Activity activity) {
- if (activity.isFinishing()) {
- return null;
- }
- final ActivityThread.ActivityClientRecord record = getActivityClientRecord(activity);
- return record != null ? record.getActivityWindowInfo() : null;
- }
-
@NonNull
private static EmbeddedActivityWindowInfo translateActivityWindowInfo(
@NonNull Activity activity, @NonNull ActivityWindowInfo activityWindowInfo) {
diff --git a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/SplitControllerTest.java b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/SplitControllerTest.java
index 99c0ee2..d852204 100644
--- a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/SplitControllerTest.java
+++ b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/SplitControllerTest.java
@@ -165,6 +165,7 @@
private Consumer<List<SplitInfo>> mEmbeddingCallback;
private List<SplitInfo> mSplitInfos;
private TransactionManager mTransactionManager;
+ private ActivityThread mCurrentActivityThread;
@Before
public void setUp() {
@@ -181,10 +182,12 @@
};
mSplitController.setSplitInfoCallback(mEmbeddingCallback);
mTransactionManager = mSplitController.mTransactionManager;
+ mCurrentActivityThread = ActivityThread.currentActivityThread();
spyOn(mSplitController);
spyOn(mSplitPresenter);
spyOn(mEmbeddingCallback);
spyOn(mTransactionManager);
+ spyOn(mCurrentActivityThread);
doNothing().when(mSplitPresenter).applyTransaction(any(), anyInt(), anyBoolean());
final Configuration activityConfig = new Configuration();
activityConfig.windowConfiguration.setBounds(TASK_BOUNDS);
@@ -1668,7 +1671,8 @@
final IBinder activityToken = new Binder();
doReturn(activityToken).when(activity).getActivityToken();
doReturn(activity).when(mSplitController).getActivity(activityToken);
- doReturn(activityClientRecord).when(mSplitController).getActivityClientRecord(activity);
+ doReturn(activityClientRecord).when(mCurrentActivityThread).getActivityClient(
+ activityToken);
doReturn(taskId).when(activity).getTaskId();
doReturn(new ActivityInfo()).when(activity).getActivityInfo();
doReturn(DEFAULT_DISPLAY).when(activity).getDisplayId();
diff --git a/packages/SettingsLib/Spa/gallery/AndroidManifest.xml b/packages/SettingsLib/Spa/gallery/AndroidManifest.xml
index df5644b..2645360 100644
--- a/packages/SettingsLib/Spa/gallery/AndroidManifest.xml
+++ b/packages/SettingsLib/Spa/gallery/AndroidManifest.xml
@@ -46,20 +46,6 @@
</intent-filter>
</provider>
- <provider android:name="com.android.settingslib.spa.slice.SpaSliceProvider"
- android:authorities="com.android.spa.gallery.slice.provider"
- android:exported="true" >
- <intent-filter>
- <action android:name="android.intent.action.VIEW" />
- <category android:name="android.app.slice.category.SLICE" />
- </intent-filter>
- </provider>
-
- <receiver
- android:name="com.android.settingslib.spa.slice.SpaSliceBroadcastReceiver"
- android:exported="false">
- </receiver>
-
<activity
android:name="com.android.settingslib.spa.debug.BlankActivity"
android:exported="true">
diff --git a/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/GallerySpaEnvironment.kt b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/GallerySpaEnvironment.kt
index 91bd791..ffd2879 100644
--- a/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/GallerySpaEnvironment.kt
+++ b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/GallerySpaEnvironment.kt
@@ -55,7 +55,6 @@
import com.android.settingslib.spa.gallery.ui.CopyablePageProvider
import com.android.settingslib.spa.gallery.scaffold.ScrollablePagerPageProvider
import com.android.settingslib.spa.gallery.ui.SpinnerPageProvider
-import com.android.settingslib.spa.slice.SpaSliceBroadcastReceiver
/**
* Enum to define all SPP name here.
@@ -120,9 +119,7 @@
override val logger = DebugLogger()
override val browseActivityClass = GalleryMainActivity::class.java
- override val sliceBroadcastReceiverClass = SpaSliceBroadcastReceiver::class.java
// For debugging
override val searchProviderAuthorities = "com.android.spa.gallery.search.provider"
- override val sliceProviderAuthorities = "com.android.spa.gallery.slice.provider"
}
diff --git a/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/preference/PreferencePageProvider.kt b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/preference/PreferencePageProvider.kt
index 96de1a7..6d1d346 100644
--- a/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/preference/PreferencePageProvider.kt
+++ b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/preference/PreferencePageProvider.kt
@@ -27,7 +27,6 @@
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.tooling.preview.Preview
import com.android.settingslib.spa.framework.common.EntrySearchData
-import com.android.settingslib.spa.framework.common.EntrySliceData
import com.android.settingslib.spa.framework.common.EntryStatusData
import com.android.settingslib.spa.framework.common.SettingsEntry
import com.android.settingslib.spa.framework.common.SettingsEntryBuilder
@@ -35,10 +34,8 @@
import com.android.settingslib.spa.framework.common.SpaEnvironmentFactory
import com.android.settingslib.spa.framework.common.createSettingsPage
import com.android.settingslib.spa.framework.theme.SettingsTheme
-import com.android.settingslib.spa.framework.util.createIntent
import com.android.settingslib.spa.gallery.R
import com.android.settingslib.spa.gallery.SettingsPageProviderEnum
-import com.android.settingslib.spa.gallery.preference.PreferencePageModel.Companion.ASYNC_PREFERENCE_SUMMARY
import com.android.settingslib.spa.gallery.preference.PreferencePageModel.Companion.ASYNC_PREFERENCE_TITLE
import com.android.settingslib.spa.gallery.preference.PreferencePageModel.Companion.AUTO_UPDATE_PREFERENCE_TITLE
import com.android.settingslib.spa.gallery.preference.PreferencePageModel.Companion.DISABLE_PREFERENCE_SUMMARY
@@ -48,15 +45,10 @@
import com.android.settingslib.spa.gallery.preference.PreferencePageModel.Companion.SIMPLE_PREFERENCE_KEYWORDS
import com.android.settingslib.spa.gallery.preference.PreferencePageModel.Companion.SIMPLE_PREFERENCE_SUMMARY
import com.android.settingslib.spa.gallery.preference.PreferencePageModel.Companion.SIMPLE_PREFERENCE_TITLE
-import com.android.settingslib.spa.slice.createBrowsePendingIntent
-import com.android.settingslib.spa.slice.provider.createDemoActionSlice
-import com.android.settingslib.spa.slice.provider.createDemoBrowseSlice
-import com.android.settingslib.spa.slice.provider.createDemoSlice
import com.android.settingslib.spa.widget.preference.Preference
import com.android.settingslib.spa.widget.preference.PreferenceModel
import com.android.settingslib.spa.widget.preference.SimplePreferenceMacro
import com.android.settingslib.spa.widget.ui.SettingsIcon
-import kotlinx.coroutines.delay
private const val TAG = "PreferencePage"
@@ -139,26 +131,6 @@
override val enabled = { model.asyncEnable.value }
}
)
- }
- .setSliceDataFn { sliceUri, _ ->
- val createSliceImpl = { s: String ->
- createDemoBrowseSlice(
- sliceUri = sliceUri,
- title = ASYNC_PREFERENCE_TITLE,
- summary = s,
- )
- }
- return@setSliceDataFn object : EntrySliceData() {
- init {
- postValue(createSliceImpl("(loading)"))
- }
-
- override suspend fun asyncRunner() {
- spaLogger.message(TAG, "Async entry loading")
- delay(2000L)
- postValue(createSliceImpl(ASYNC_PREFERENCE_SUMMARY))
- }
- }
}.build()
)
entryList.add(
@@ -176,28 +148,6 @@
}
}
)
- }
- .setSliceDataFn { sliceUri, args ->
- val createSliceImpl = { v: Int ->
- createDemoActionSlice(
- sliceUri = sliceUri,
- title = MANUAL_UPDATE_PREFERENCE_TITLE,
- summary = "manual update value $v",
- )
- }
-
- return@setSliceDataFn object : EntrySliceData() {
- private var tick = args?.getString("init")?.toInt() ?: 0
-
- init {
- postValue(createSliceImpl(tick))
- }
-
- override suspend fun asyncAction() {
- tick++
- postValue(createSliceImpl(tick))
- }
- }
}.build()
)
entryList.add(
@@ -216,33 +166,6 @@
}
}
)
- }
- .setSliceDataFn { sliceUri, args ->
- val createSliceImpl = { v: Int ->
- createDemoBrowseSlice(
- sliceUri = sliceUri,
- title = AUTO_UPDATE_PREFERENCE_TITLE,
- summary = "auto update value $v",
- )
- }
-
- return@setSliceDataFn object : EntrySliceData() {
- private var tick = args?.getString("init")?.toInt() ?: 0
-
- init {
- postValue(createSliceImpl(tick))
- }
-
- override suspend fun asyncRunner() {
- spaLogger.message(TAG, "autoUpdater.active")
- while (true) {
- delay(1000L)
- tick++
- spaLogger.message(TAG, "autoUpdater.value $tick")
- postValue(createSliceImpl(tick))
- }
- }
- }
}.build()
)
@@ -272,22 +195,6 @@
clickRoute = SettingsPageProviderEnum.PREFERENCE.name
)
}
- .setSliceDataFn { sliceUri, _ ->
- val intent = owner.createIntent()?.createBrowsePendingIntent()
- ?: return@setSliceDataFn null
- return@setSliceDataFn object : EntrySliceData() {
- init {
- postValue(
- createDemoSlice(
- sliceUri = sliceUri,
- title = PAGE_TITLE,
- summary = "Injected Entry",
- intent = intent,
- )
- )
- }
- }
- }
}
override fun getTitle(arguments: Bundle?): String {
diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/common/SpaEnvironment.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/common/SpaEnvironment.kt
index 2d956d5..6e5132b 100644
--- a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/common/SpaEnvironment.kt
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/common/SpaEnvironment.kt
@@ -17,12 +17,10 @@
package com.android.settingslib.spa.framework.common
import android.app.Activity
-import android.content.BroadcastReceiver
import android.content.Context
import android.util.Log
import androidx.compose.runtime.Composable
import androidx.compose.ui.platform.LocalContext
-import com.android.settingslib.spa.slice.SettingsSliceDataRepository
private const val TAG = "SpaEnvironment"
@@ -69,8 +67,6 @@
val entryRepository = lazy { SettingsEntryRepository(pageProviderRepository.value) }
- val sliceDataRepository = lazy { SettingsSliceDataRepository(entryRepository.value) }
-
// The application context. Use local context as fallback when applicationContext is not
// available (e.g. in Robolectric test).
val appContext: Context = context.applicationContext ?: context
@@ -81,11 +77,9 @@
// Specify class name of browse activity and slice broadcast receiver, which is used to
// generate the necessary intents.
open val browseActivityClass: Class<out Activity>? = null
- open val sliceBroadcastReceiverClass: Class<out BroadcastReceiver>? = null
// Specify provider authorities for debugging purpose.
open val searchProviderAuthorities: String? = null
- open val sliceProviderAuthorities: String? = null
// TODO: add other environment setup here.
companion object {
diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/slice/SettingsSliceDataRepository.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/slice/SettingsSliceDataRepository.kt
deleted file mode 100644
index 7a4750d..0000000
--- a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/slice/SettingsSliceDataRepository.kt
+++ /dev/null
@@ -1,57 +0,0 @@
-/*
- * Copyright (C) 2022 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.settingslib.spa.slice
-
-import android.net.Uri
-import android.util.Log
-import com.android.settingslib.spa.framework.common.EntrySliceData
-import com.android.settingslib.spa.framework.common.SettingsEntryRepository
-import com.android.settingslib.spa.framework.util.getEntryId
-
-private const val TAG = "SliceDataRepository"
-
-class SettingsSliceDataRepository(private val entryRepository: SettingsEntryRepository) {
- // The map of slice uri to its EntrySliceData, a.k.a. LiveData<Slice?>
- private val sliceDataMap: MutableMap<String, EntrySliceData> = mutableMapOf()
-
- // Note: mark this function synchronized, so that we can get the same livedata during the
- // whole lifecycle of a Slice.
- @Synchronized
- fun getOrBuildSliceData(sliceUri: Uri): EntrySliceData? {
- val sliceString = sliceUri.getSliceId() ?: return null
- return sliceDataMap[sliceString] ?: buildLiveDataImpl(sliceUri)?.let {
- sliceDataMap[sliceString] = it
- it
- }
- }
-
- fun getActiveSliceData(sliceUri: Uri): EntrySliceData? {
- val sliceString = sliceUri.getSliceId() ?: return null
- val sliceData = sliceDataMap[sliceString] ?: return null
- return if (sliceData.isActive()) sliceData else null
- }
-
- private fun buildLiveDataImpl(sliceUri: Uri): EntrySliceData? {
- Log.d(TAG, "buildLiveData: $sliceUri")
-
- val entryId = sliceUri.getEntryId() ?: return null
- val entry = entryRepository.getEntry(entryId) ?: return null
- if (!entry.hasSliceSupport) return null
- val arguments = sliceUri.getRuntimeArguments()
- return entry.getSliceData(runtimeArguments = arguments, sliceUri = sliceUri)
- }
-}
diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/slice/SliceUtil.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/slice/SliceUtil.kt
index f362890..ec89c7c 100644
--- a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/slice/SliceUtil.kt
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/slice/SliceUtil.kt
@@ -16,23 +16,10 @@
package com.android.settingslib.spa.slice
-import android.app.Activity
-import android.app.PendingIntent
-import android.content.BroadcastReceiver
-import android.content.ComponentName
-import android.content.Context
-import android.content.Intent
import android.net.Uri
import android.os.Bundle
-import com.android.settingslib.spa.framework.common.SettingsEntry
-import com.android.settingslib.spa.framework.common.SpaEnvironmentFactory
import com.android.settingslib.spa.framework.util.KEY_DESTINATION
import com.android.settingslib.spa.framework.util.KEY_HIGHLIGHT_ENTRY
-import com.android.settingslib.spa.framework.util.SESSION_SLICE
-import com.android.settingslib.spa.framework.util.SPA_INTENT_RESERVED_KEYS
-import com.android.settingslib.spa.framework.util.appendSpaParams
-import com.android.settingslib.spa.framework.util.getDestination
-import com.android.settingslib.spa.framework.util.getEntryId
// Defines SliceUri, which contains special query parameters:
// -- KEY_DESTINATION: The route that this slice is navigated to.
@@ -45,25 +32,6 @@
return getQueryParameter(KEY_HIGHLIGHT_ENTRY)
}
-fun SliceUri.getDestination(): String? {
- return getQueryParameter(KEY_DESTINATION)
-}
-
-fun SliceUri.getRuntimeArguments(): Bundle {
- val params = Bundle()
- for (queryName in queryParameterNames) {
- if (SPA_INTENT_RESERVED_KEYS.contains(queryName)) continue
- params.putString(queryName, getQueryParameter(queryName))
- }
- return params
-}
-
-fun SliceUri.getSliceId(): String? {
- val entryId = getEntryId() ?: return null
- val params = getRuntimeArguments()
- return "${entryId}_$params"
-}
-
fun Uri.Builder.appendSpaParams(
destination: String? = null,
entryId: String? = null,
@@ -79,72 +47,3 @@
return this
}
-fun Uri.Builder.fromEntry(
- entry: SettingsEntry,
- authority: String?,
- runtimeArguments: Bundle? = null
-): Uri.Builder {
- if (authority == null) return this
- val sp = entry.containerPage()
- return scheme("content").authority(authority).appendSpaParams(
- destination = sp.buildRoute(),
- entryId = entry.id,
- runtimeArguments = runtimeArguments
- )
-}
-
-fun SliceUri.createBroadcastPendingIntent(): PendingIntent? {
- val context = SpaEnvironmentFactory.instance.appContext
- val sliceBroadcastClass =
- SpaEnvironmentFactory.instance.sliceBroadcastReceiverClass ?: return null
- val entryId = getEntryId() ?: return null
- return createBroadcastPendingIntent(context, sliceBroadcastClass, entryId)
-}
-
-fun SliceUri.createBrowsePendingIntent(): PendingIntent? {
- val context = SpaEnvironmentFactory.instance.appContext
- val browseActivityClass = SpaEnvironmentFactory.instance.browseActivityClass ?: return null
- val destination = getDestination() ?: return null
- val entryId = getEntryId()
- return createBrowsePendingIntent(context, browseActivityClass, destination, entryId)
-}
-
-fun Intent.createBrowsePendingIntent(): PendingIntent? {
- val context = SpaEnvironmentFactory.instance.appContext
- val browseActivityClass = SpaEnvironmentFactory.instance.browseActivityClass ?: return null
- val destination = getDestination() ?: return null
- val entryId = getEntryId()
- return createBrowsePendingIntent(context, browseActivityClass, destination, entryId)
-}
-
-private fun createBrowsePendingIntent(
- context: Context,
- browseActivityClass: Class<out Activity>,
- destination: String,
- entryId: String?
-): PendingIntent {
- val intent = Intent().setComponent(ComponentName(context, browseActivityClass))
- .appendSpaParams(destination, entryId, SESSION_SLICE)
- .apply {
- // Set both extra and data (which is a Uri) in Slice Intent:
- // 1) extra is used in SPA navigation framework
- // 2) data is used in Slice framework
- data = Uri.Builder().appendSpaParams(destination, entryId).build()
- flags = Intent.FLAG_ACTIVITY_NEW_TASK
- }
-
- return PendingIntent.getActivity(context, 0, intent, PendingIntent.FLAG_IMMUTABLE)
-}
-
-private fun createBroadcastPendingIntent(
- context: Context,
- sliceBroadcastClass: Class<out BroadcastReceiver>,
- entryId: String
-): PendingIntent {
- val intent = Intent().setComponent(ComponentName(context, sliceBroadcastClass))
- .apply { data = Uri.Builder().appendSpaParams(entryId = entryId).build() }
- return PendingIntent.getBroadcast(
- context, 0 /* requestCode */, intent,
- PendingIntent.FLAG_CANCEL_CURRENT or PendingIntent.FLAG_MUTABLE
- )
-}
diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/slice/SpaSliceBroadcastReceiver.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/slice/SpaSliceBroadcastReceiver.kt
deleted file mode 100644
index 39cb431..0000000
--- a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/slice/SpaSliceBroadcastReceiver.kt
+++ /dev/null
@@ -1,31 +0,0 @@
-/*
- * Copyright (C) 2022 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.settingslib.spa.slice
-
-import android.content.BroadcastReceiver
-import android.content.Context
-import android.content.Intent
-import com.android.settingslib.spa.framework.common.SpaEnvironmentFactory
-
-class SpaSliceBroadcastReceiver : BroadcastReceiver() {
- override fun onReceive(context: Context?, intent: Intent?) {
- val sliceRepository by SpaEnvironmentFactory.instance.sliceDataRepository
- val sliceUri = intent?.data ?: return
- val sliceData = sliceRepository.getActiveSliceData(sliceUri) ?: return
- sliceData.doAction()
- }
-}
diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/slice/SpaSliceProvider.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/slice/SpaSliceProvider.kt
deleted file mode 100644
index 3496f02..0000000
--- a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/slice/SpaSliceProvider.kt
+++ /dev/null
@@ -1,77 +0,0 @@
-/*
- * Copyright (C) 2022 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.settingslib.spa.slice
-
-import android.net.Uri
-import android.util.Log
-import androidx.lifecycle.Observer
-import androidx.slice.Slice
-import androidx.slice.SliceProvider
-import com.android.settingslib.spa.framework.common.EntrySliceData
-import com.android.settingslib.spa.framework.common.SpaEnvironmentFactory
-import kotlinx.coroutines.Dispatchers
-import kotlinx.coroutines.runBlocking
-import kotlinx.coroutines.withContext
-
-private const val TAG = "SpaSliceProvider"
-
-class SpaSliceProvider : SliceProvider(), Observer<Slice?> {
- private fun getOrPutSliceData(sliceUri: Uri): EntrySliceData? {
- if (!SpaEnvironmentFactory.isReady()) return null
- val sliceRepository by SpaEnvironmentFactory.instance.sliceDataRepository
- return sliceRepository.getOrBuildSliceData(sliceUri)
- }
-
- override fun onBindSlice(sliceUri: Uri): Slice? {
- if (context == null) return null
- Log.d(TAG, "onBindSlice: $sliceUri")
- return getOrPutSliceData(sliceUri)?.value
- }
-
- override fun onSlicePinned(sliceUri: Uri) {
- Log.d(TAG, "onSlicePinned: $sliceUri")
- super.onSlicePinned(sliceUri)
- val sliceLiveData = getOrPutSliceData(sliceUri) ?: return
- runBlocking {
- withContext(Dispatchers.Main) {
- sliceLiveData.observeForever(this@SpaSliceProvider)
- }
- }
- }
-
- override fun onSliceUnpinned(sliceUri: Uri) {
- Log.d(TAG, "onSliceUnpinned: $sliceUri")
- super.onSliceUnpinned(sliceUri)
- val sliceLiveData = getOrPutSliceData(sliceUri) ?: return
- runBlocking {
- withContext(Dispatchers.Main) {
- sliceLiveData.removeObserver(this@SpaSliceProvider)
- }
- }
- }
-
- override fun onChanged(value: Slice?) {
- val uri = value?.uri ?: return
- Log.d(TAG, "onChanged: $uri")
- context?.contentResolver?.notifyChange(uri, null)
- }
-
- override fun onCreateSliceProvider(): Boolean {
- Log.d(TAG, "onCreateSliceProvider")
- return true
- }
-}
diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/slice/presenter/Demo.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/slice/presenter/Demo.kt
deleted file mode 100644
index 007f47b..0000000
--- a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/slice/presenter/Demo.kt
+++ /dev/null
@@ -1,47 +0,0 @@
-/*
- * Copyright (C) 2022 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.settingslib.spa.slice.presenter
-
-import android.net.Uri
-import androidx.compose.material3.HorizontalDivider
-import androidx.compose.runtime.Composable
-import androidx.compose.runtime.remember
-import androidx.compose.ui.platform.LocalContext
-import androidx.compose.ui.viewinterop.AndroidView
-import androidx.lifecycle.compose.LocalLifecycleOwner
-import androidx.slice.widget.SliceLiveData
-import androidx.slice.widget.SliceView
-
-@Composable
-fun SliceDemo(sliceUri: Uri) {
- val context = LocalContext.current
- val lifecycleOwner = LocalLifecycleOwner.current
- val sliceData = remember {
- SliceLiveData.fromUri(context, sliceUri)
- }
-
- HorizontalDivider()
- AndroidView(
- factory = { localContext ->
- val view = SliceView(localContext)
- view.setShowTitleItems(true)
- view.isScrollable = false
- view
- },
- update = { view -> sliceData.observe(lifecycleOwner, view) }
- )
-}
diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/slice/provider/Demo.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/slice/provider/Demo.kt
deleted file mode 100644
index e4a7386..0000000
--- a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/slice/provider/Demo.kt
+++ /dev/null
@@ -1,60 +0,0 @@
-/*
- * Copyright (C) 2022 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.settingslib.spa.slice.provider
-
-import android.app.PendingIntent
-import android.content.Context
-import android.net.Uri
-import androidx.core.R
-import androidx.core.graphics.drawable.IconCompat
-import androidx.slice.Slice
-import androidx.slice.SliceManager
-import androidx.slice.builders.ListBuilder
-import androidx.slice.builders.SliceAction
-import com.android.settingslib.spa.framework.common.SpaEnvironmentFactory
-import com.android.settingslib.spa.slice.createBroadcastPendingIntent
-import com.android.settingslib.spa.slice.createBrowsePendingIntent
-
-fun createDemoBrowseSlice(sliceUri: Uri, title: String, summary: String): Slice? {
- val intent = sliceUri.createBrowsePendingIntent() ?: return null
- return createDemoSlice(sliceUri, title, summary, intent)
-}
-
-fun createDemoActionSlice(sliceUri: Uri, title: String, summary: String): Slice? {
- val intent = sliceUri.createBroadcastPendingIntent() ?: return null
- return createDemoSlice(sliceUri, title, summary, intent)
-}
-
-fun createDemoSlice(sliceUri: Uri, title: String, summary: String, intent: PendingIntent): Slice? {
- val context = SpaEnvironmentFactory.instance.appContext
- if (!SliceManager.getInstance(context).pinnedSlices.contains(sliceUri)) return null
- return ListBuilder(context, sliceUri, ListBuilder.INFINITY)
- .addRow(ListBuilder.RowBuilder().apply {
- setPrimaryAction(createSliceAction(context, intent))
- setTitle(title)
- setSubtitle(summary)
- }).build()
-}
-
-private fun createSliceAction(context: Context, intent: PendingIntent): SliceAction {
- return SliceAction.create(
- intent,
- IconCompat.createWithResource(context, R.drawable.notification_action_background),
- ListBuilder.ICON_IMAGE,
- "Enter app"
- )
-}
diff --git a/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/slice/SettingsSliceDataRepositoryTest.kt b/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/slice/SettingsSliceDataRepositoryTest.kt
deleted file mode 100644
index 341a4a5..0000000
--- a/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/slice/SettingsSliceDataRepositoryTest.kt
+++ /dev/null
@@ -1,97 +0,0 @@
-/*
- * Copyright (C) 2022 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.settingslib.spa.slice
-
-import android.content.Context
-import android.net.Uri
-import androidx.arch.core.executor.testing.InstantTaskExecutorRule
-import androidx.lifecycle.Observer
-import androidx.slice.Slice
-import androidx.test.core.app.ApplicationProvider
-import androidx.test.ext.junit.runners.AndroidJUnit4
-import com.android.settingslib.spa.framework.common.SpaEnvironmentFactory
-import com.android.settingslib.spa.framework.common.createSettingsPage
-import com.android.settingslib.spa.framework.util.genEntryId
-import com.android.settingslib.spa.tests.testutils.SpaEnvironmentForTest
-import com.android.settingslib.spa.tests.testutils.SppHome
-import com.android.settingslib.spa.tests.testutils.SppLayer2
-import com.google.common.truth.Truth.assertThat
-import org.junit.Rule
-import org.junit.Test
-import org.junit.runner.RunWith
-
-@RunWith(AndroidJUnit4::class)
-class SettingsSliceDataRepositoryTest {
- @get:Rule val instantTaskExecutorRule = InstantTaskExecutorRule()
-
- private val context: Context = ApplicationProvider.getApplicationContext()
- private val spaEnvironment =
- SpaEnvironmentForTest(context, listOf(SppHome.createSettingsPage()))
- private val sliceDataRepository by spaEnvironment.sliceDataRepository
-
- @Test
- fun getOrBuildSliceDataTest() {
- SpaEnvironmentFactory.reset(spaEnvironment)
-
- // Slice empty
- assertThat(sliceDataRepository.getOrBuildSliceData(Uri.EMPTY)).isNull()
-
- // Slice supported
- val page = SppLayer2.createSettingsPage()
- val entryId = genEntryId("Layer2Entry1", page)
- val sliceUri = Uri.Builder().appendSpaParams(page.buildRoute(), entryId).build()
- assertThat(sliceUri.getDestination()).isEqualTo("SppLayer2")
- assertThat(sliceUri.getSliceId()).isEqualTo("${entryId}_Bundle[{}]")
- val sliceData = sliceDataRepository.getOrBuildSliceData(sliceUri)
- assertThat(sliceData).isNotNull()
- assertThat(sliceDataRepository.getOrBuildSliceData(sliceUri)).isSameInstanceAs(sliceData)
-
- // Slice unsupported
- val entryId2 = genEntryId("Layer2Entry2", page)
- val sliceUri2 = Uri.Builder().appendSpaParams(page.buildRoute(), entryId2).build()
- assertThat(sliceUri2.getDestination()).isEqualTo("SppLayer2")
- assertThat(sliceUri2.getSliceId()).isEqualTo("${entryId2}_Bundle[{}]")
- assertThat(sliceDataRepository.getOrBuildSliceData(sliceUri2)).isNull()
- }
-
- @Test
- fun getActiveSliceDataTest() {
- SpaEnvironmentFactory.reset(spaEnvironment)
-
- val page = SppLayer2.createSettingsPage()
- val entryId = genEntryId("Layer2Entry1", page)
- val sliceUri = Uri.Builder().appendSpaParams(page.buildRoute(), entryId).build()
-
- // build slice data first
- val sliceData = sliceDataRepository.getOrBuildSliceData(sliceUri)
-
- // slice data is inactive
- assertThat(sliceData!!.isActive()).isFalse()
- assertThat(sliceDataRepository.getActiveSliceData(sliceUri)).isNull()
-
- // slice data is active
- val observer = Observer<Slice?> { }
- sliceData.observeForever(observer)
- assertThat(sliceData.isActive()).isTrue()
- assertThat(sliceDataRepository.getActiveSliceData(sliceUri)).isSameInstanceAs(sliceData)
-
- // slice data is inactive again
- sliceData.removeObserver(observer)
- assertThat(sliceData.isActive()).isFalse()
- assertThat(sliceDataRepository.getActiveSliceData(sliceUri)).isNull()
- }
-}
diff --git a/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/slice/SliceUtilTest.kt b/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/slice/SliceUtilTest.kt
index d1c4e51..b489afd 100644
--- a/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/slice/SliceUtilTest.kt
+++ b/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/slice/SliceUtilTest.kt
@@ -16,91 +16,27 @@
package com.android.settingslib.spa.slice
-import android.content.Context
-import android.content.Intent
import android.net.Uri
import androidx.core.os.bundleOf
-import androidx.test.core.app.ApplicationProvider
import androidx.test.ext.junit.runners.AndroidJUnit4
-import com.android.settingslib.spa.framework.common.SpaEnvironmentFactory
-import com.android.settingslib.spa.tests.testutils.SpaEnvironmentForTest
import com.google.common.truth.Truth.assertThat
import org.junit.Test
import org.junit.runner.RunWith
@RunWith(AndroidJUnit4::class)
class SliceUtilTest {
- private val context: Context = ApplicationProvider.getApplicationContext()
- private val spaEnvironment = SpaEnvironmentForTest(context)
-
@Test
fun sliceUriTest() {
assertThat(Uri.EMPTY.getEntryId()).isNull()
- assertThat(Uri.EMPTY.getDestination()).isNull()
- assertThat(Uri.EMPTY.getRuntimeArguments().size()).isEqualTo(0)
- assertThat(Uri.EMPTY.getSliceId()).isNull()
// valid slice uri
val dest = "myRoute"
val entryId = "myEntry"
val sliceUriWithoutParams = Uri.Builder().appendSpaParams(dest, entryId).build()
assertThat(sliceUriWithoutParams.getEntryId()).isEqualTo(entryId)
- assertThat(sliceUriWithoutParams.getDestination()).isEqualTo(dest)
- assertThat(sliceUriWithoutParams.getRuntimeArguments().size()).isEqualTo(0)
- assertThat(sliceUriWithoutParams.getSliceId()).isEqualTo("${entryId}_Bundle[{}]")
val sliceUriWithParams =
Uri.Builder().appendSpaParams(dest, entryId, bundleOf("p1" to "v1")).build()
assertThat(sliceUriWithParams.getEntryId()).isEqualTo(entryId)
- assertThat(sliceUriWithParams.getDestination()).isEqualTo(dest)
- assertThat(sliceUriWithParams.getRuntimeArguments().size()).isEqualTo(1)
- assertThat(sliceUriWithParams.getSliceId()).isEqualTo("${entryId}_Bundle[{p1=v1}]")
- }
-
- @Test
- fun createBroadcastPendingIntentTest() {
- SpaEnvironmentFactory.reset(spaEnvironment)
-
- // Empty Slice Uri
- assertThat(Uri.EMPTY.createBroadcastPendingIntent()).isNull()
-
- // Valid Slice Uri
- val dest = "myRoute"
- val entryId = "myEntry"
- val sliceUriWithoutParams = Uri.Builder().appendSpaParams(dest, entryId).build()
- val pendingIntent = sliceUriWithoutParams.createBroadcastPendingIntent()
- assertThat(pendingIntent).isNotNull()
- assertThat(pendingIntent!!.isBroadcast).isTrue()
- assertThat(pendingIntent.isImmutable).isFalse()
- }
-
- @Test
- fun createBrowsePendingIntentTest() {
- SpaEnvironmentFactory.reset(spaEnvironment)
-
- // Empty Slice Uri
- assertThat(Uri.EMPTY.createBrowsePendingIntent()).isNull()
-
- // Empty Intent
- assertThat(Intent().createBrowsePendingIntent()).isNull()
-
- // Valid Slice Uri
- val dest = "myRoute"
- val entryId = "myEntry"
- val sliceUri = Uri.Builder().appendSpaParams(dest, entryId).build()
- val pendingIntent = sliceUri.createBrowsePendingIntent()
- assertThat(pendingIntent).isNotNull()
- assertThat(pendingIntent!!.isActivity).isTrue()
- assertThat(pendingIntent.isImmutable).isTrue()
-
- // Valid Intent
- val intent = Intent().apply {
- putExtra("spaActivityDestination", dest)
- putExtra("highlightEntry", entryId)
- }
- val pendingIntent2 = intent.createBrowsePendingIntent()
- assertThat(pendingIntent2).isNotNull()
- assertThat(pendingIntent2!!.isActivity).isTrue()
- assertThat(pendingIntent2.isImmutable).isTrue()
}
}
\ No newline at end of file
diff --git a/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/tests/testutils/SpaEnvironmentForTest.kt b/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/tests/testutils/SpaEnvironmentForTest.kt
index 22a5ca3..4f8fd79 100644
--- a/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/tests/testutils/SpaEnvironmentForTest.kt
+++ b/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/tests/testutils/SpaEnvironmentForTest.kt
@@ -216,8 +216,6 @@
context: Context,
rootPages: List<SettingsPage> = emptyList(),
override val browseActivityClass: Class<out Activity>? = BlankActivity::class.java,
- override val sliceBroadcastReceiverClass: Class<out BroadcastReceiver>? =
- BlankSliceBroadcastReceiver::class.java,
override val logger: SpaLogger = object : SpaLogger {}
) : SpaEnvironment(context) {
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/DraggableHandler.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/DraggableHandler.kt
index d95b388..20b1303 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/DraggableHandler.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/DraggableHandler.kt
@@ -184,31 +184,33 @@
): Swipes {
val fromSource =
startedPosition?.let { position ->
- layoutImpl.swipeSourceDetector.source(
- fromScene.targetSize,
- position.round(),
- layoutImpl.density,
- orientation,
- )
+ layoutImpl.swipeSourceDetector
+ .source(
+ fromScene.targetSize,
+ position.round(),
+ layoutImpl.density,
+ orientation,
+ )
+ ?.resolve(layoutImpl.layoutDirection)
}
val upOrLeft =
- Swipe(
+ Swipe.Resolved(
direction =
when (orientation) {
- Orientation.Horizontal -> SwipeDirection.Left
- Orientation.Vertical -> SwipeDirection.Up
+ Orientation.Horizontal -> SwipeDirection.Resolved.Left
+ Orientation.Vertical -> SwipeDirection.Resolved.Up
},
pointerCount = pointersDown,
fromSource = fromSource,
)
val downOrRight =
- Swipe(
+ Swipe.Resolved(
direction =
when (orientation) {
- Orientation.Horizontal -> SwipeDirection.Right
- Orientation.Vertical -> SwipeDirection.Down
+ Orientation.Horizontal -> SwipeDirection.Resolved.Right
+ Orientation.Vertical -> SwipeDirection.Resolved.Down
},
pointerCount = pointersDown,
fromSource = fromSource,
@@ -833,10 +835,10 @@
/** The [Swipe] associated to a given fromScene, startedPosition and pointersDown. */
private class Swipes(
- val upOrLeft: Swipe?,
- val downOrRight: Swipe?,
- val upOrLeftNoSource: Swipe?,
- val downOrRightNoSource: Swipe?,
+ val upOrLeft: Swipe.Resolved?,
+ val downOrRight: Swipe.Resolved?,
+ val upOrLeftNoSource: Swipe.Resolved?,
+ val downOrRightNoSource: Swipe.Resolved?,
) {
/** The [UserActionResult] associated to up and down swipes. */
var upOrLeftResult: UserActionResult? = null
@@ -844,7 +846,7 @@
fun computeSwipesResults(fromScene: Scene): Pair<UserActionResult?, UserActionResult?> {
val userActions = fromScene.userActions
- fun result(swipe: Swipe?): UserActionResult? {
+ fun result(swipe: Swipe.Resolved?): UserActionResult? {
return userActions[swipe ?: return null]
}
@@ -940,25 +942,27 @@
when {
amount < 0f -> {
val actionUpOrLeft =
- Swipe(
+ Swipe.Resolved(
direction =
when (orientation) {
- Orientation.Horizontal -> SwipeDirection.Left
- Orientation.Vertical -> SwipeDirection.Up
+ Orientation.Horizontal -> SwipeDirection.Resolved.Left
+ Orientation.Vertical -> SwipeDirection.Resolved.Up
},
pointerCount = pointersInfo().pointersDown,
+ fromSource = null,
)
fromScene.userActions[actionUpOrLeft]
}
amount > 0f -> {
val actionDownOrRight =
- Swipe(
+ Swipe.Resolved(
direction =
when (orientation) {
- Orientation.Horizontal -> SwipeDirection.Right
- Orientation.Vertical -> SwipeDirection.Down
+ Orientation.Horizontal -> SwipeDirection.Resolved.Right
+ Orientation.Vertical -> SwipeDirection.Resolved.Down
},
pointerCount = pointersInfo().pointersDown,
+ fromSource = null,
)
fromScene.userActions[actionDownOrRight]
}
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/EdgeDetector.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/EdgeDetector.kt
index b0dc3a1..97c0cef 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/EdgeDetector.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/EdgeDetector.kt
@@ -21,14 +21,28 @@
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.IntOffset
import androidx.compose.ui.unit.IntSize
+import androidx.compose.ui.unit.LayoutDirection
import androidx.compose.ui.unit.dp
/** The edge of a [SceneTransitionLayout]. */
-enum class Edge : SwipeSource {
- Left,
- Right,
- Top,
- Bottom,
+enum class Edge(private val resolveEdge: (LayoutDirection) -> Resolved) : SwipeSource {
+ Top(resolveEdge = { Resolved.Top }),
+ Bottom(resolveEdge = { Resolved.Bottom }),
+ Left(resolveEdge = { Resolved.Left }),
+ Right(resolveEdge = { Resolved.Right }),
+ Start(resolveEdge = { if (it == LayoutDirection.Ltr) Resolved.Left else Resolved.Right }),
+ End(resolveEdge = { if (it == LayoutDirection.Ltr) Resolved.Right else Resolved.Left });
+
+ override fun resolve(layoutDirection: LayoutDirection): Resolved {
+ return resolveEdge(layoutDirection)
+ }
+
+ enum class Resolved : SwipeSource.Resolved {
+ Left,
+ Right,
+ Top,
+ Bottom,
+ }
}
val DefaultEdgeDetector = FixedSizeEdgeDetector(40.dp)
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/Scene.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/Scene.kt
index 936f4ba..a49f1af 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/Scene.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/Scene.kt
@@ -37,7 +37,7 @@
val key: SceneKey,
layoutImpl: SceneTransitionLayoutImpl,
content: @Composable SceneScope.() -> Unit,
- actions: Map<UserAction, UserActionResult>,
+ actions: Map<UserAction.Resolved, UserActionResult>,
zIndex: Float,
) {
internal val scope = SceneScopeImpl(layoutImpl, this)
@@ -54,8 +54,8 @@
}
private fun checkValid(
- userActions: Map<UserAction, UserActionResult>
- ): Map<UserAction, UserActionResult> {
+ userActions: Map<UserAction.Resolved, UserActionResult>
+ ): Map<UserAction.Resolved, UserActionResult> {
userActions.forEach { (action, result) ->
if (key == result.toScene) {
error(
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayout.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayout.kt
index 45758c5..0c467b1 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayout.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayout.kt
@@ -28,10 +28,13 @@
import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.input.nestedscroll.NestedScrollConnection
import androidx.compose.ui.platform.LocalDensity
+import androidx.compose.ui.platform.LocalLayoutDirection
import androidx.compose.ui.unit.Density
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.IntOffset
import androidx.compose.ui.unit.IntSize
+import androidx.compose.ui.unit.LayoutDirection
+import com.android.compose.animation.scene.UserAction.Resolved
/**
* [SceneTransitionLayout] is a container that automatically animates its content whenever its state
@@ -344,34 +347,71 @@
@Stable @ElementDsl interface MovableElementContentScope : BaseSceneScope, ElementBoxScope
/** An action performed by the user. */
-sealed interface UserAction {
+sealed class UserAction {
infix fun to(scene: SceneKey): Pair<UserAction, UserActionResult> {
return this to UserActionResult(toScene = scene)
}
+
+ /** Resolve this into a [Resolved] user action given [layoutDirection]. */
+ internal abstract fun resolve(layoutDirection: LayoutDirection): Resolved
+
+ /** A resolved [UserAction] that does not depend on the layout direction. */
+ internal sealed class Resolved
}
/** The user navigated back, either using a gesture or by triggering a KEYCODE_BACK event. */
-data object Back : UserAction
+data object Back : UserAction() {
+ override fun resolve(layoutDirection: LayoutDirection): Resolved = Resolved
+
+ internal object Resolved : UserAction.Resolved()
+}
/** The user swiped on the container. */
data class Swipe(
val direction: SwipeDirection,
val pointerCount: Int = 1,
val fromSource: SwipeSource? = null,
-) : UserAction {
+) : UserAction() {
companion object {
val Left = Swipe(SwipeDirection.Left)
val Up = Swipe(SwipeDirection.Up)
val Right = Swipe(SwipeDirection.Right)
val Down = Swipe(SwipeDirection.Down)
+ val Start = Swipe(SwipeDirection.Start)
+ val End = Swipe(SwipeDirection.End)
}
+
+ override fun resolve(layoutDirection: LayoutDirection): UserAction.Resolved {
+ return Resolved(
+ direction = direction.resolve(layoutDirection),
+ pointerCount = pointerCount,
+ fromSource = fromSource?.resolve(layoutDirection),
+ )
+ }
+
+ /** A resolved [Swipe] that does not depend on the layout direction. */
+ internal data class Resolved(
+ val direction: SwipeDirection.Resolved,
+ val pointerCount: Int,
+ val fromSource: SwipeSource.Resolved?,
+ ) : UserAction.Resolved()
}
-enum class SwipeDirection(val orientation: Orientation) {
- Up(Orientation.Vertical),
- Down(Orientation.Vertical),
- Left(Orientation.Horizontal),
- Right(Orientation.Horizontal),
+enum class SwipeDirection(internal val resolve: (LayoutDirection) -> Resolved) {
+ Up(resolve = { Resolved.Up }),
+ Down(resolve = { Resolved.Down }),
+ Left(resolve = { Resolved.Left }),
+ Right(resolve = { Resolved.Right }),
+ Start(resolve = { if (it == LayoutDirection.Ltr) Resolved.Left else Resolved.Right }),
+ End(resolve = { if (it == LayoutDirection.Ltr) Resolved.Right else Resolved.Left });
+
+ /** A resolved [SwipeDirection] that does not depend on the layout direction. */
+ internal enum class Resolved(val orientation: Orientation) {
+ Up(Orientation.Vertical),
+ Down(Orientation.Vertical),
+ Left(Orientation.Horizontal),
+ Right(Orientation.Horizontal),
+ }
}
/**
@@ -386,6 +426,16 @@
override fun equals(other: Any?): Boolean
override fun hashCode(): Int
+
+ /** Resolve this into a [Resolved] swipe source given [layoutDirection]. */
+ fun resolve(layoutDirection: LayoutDirection): Resolved
+
+ /** A resolved [SwipeSource] that does not depend on the layout direction. */
+ interface Resolved {
+ override fun equals(other: Any?): Boolean
+
+ override fun hashCode(): Int
+ }
}
interface SwipeSourceDetector {
@@ -460,11 +510,13 @@
scenes: SceneTransitionLayoutScope.() -> Unit,
) {
val density = LocalDensity.current
+ val layoutDirection = LocalLayoutDirection.current
val coroutineScope = rememberCoroutineScope()
val layoutImpl = remember {
SceneTransitionLayoutImpl(
state = state as BaseSceneTransitionLayoutState,
density = density,
+ layoutDirection = layoutDirection,
swipeSourceDetector = swipeSourceDetector,
transitionInterceptionThreshold = transitionInterceptionThreshold,
builder = scenes,
@@ -475,7 +527,7 @@
// TODO(b/317014852): Move this into the SideEffect {} again once STLImpl.scenes is not a
// SnapshotStateMap anymore.
- layoutImpl.updateScenes(scenes)
+ layoutImpl.updateScenes(scenes, layoutDirection)
SideEffect {
if (state != layoutImpl.state) {
@@ -486,6 +538,7 @@
}
layoutImpl.density = density
+ layoutImpl.layoutDirection = layoutDirection
layoutImpl.swipeSourceDetector = swipeSourceDetector
layoutImpl.transitionInterceptionThreshold = transitionInterceptionThreshold
}
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayoutImpl.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayoutImpl.kt
index 6095419..3e48c42 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayoutImpl.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayoutImpl.kt
@@ -33,6 +33,7 @@
import androidx.compose.ui.unit.Constraints
import androidx.compose.ui.unit.Density
import androidx.compose.ui.unit.IntSize
+import androidx.compose.ui.unit.LayoutDirection
import androidx.compose.ui.util.fastForEach
import androidx.compose.ui.util.fastForEachReversed
import com.android.compose.ui.util.lerp
@@ -45,6 +46,7 @@
internal class SceneTransitionLayoutImpl(
internal val state: BaseSceneTransitionLayoutState,
internal var density: Density,
+ internal var layoutDirection: LayoutDirection,
internal var swipeSourceDetector: SwipeSourceDetector,
internal var transitionInterceptionThreshold: Float,
builder: SceneTransitionLayoutScope.() -> Unit,
@@ -114,7 +116,7 @@
private set
init {
- updateScenes(builder)
+ updateScenes(builder, layoutDirection)
// DraggableHandlerImpl must wait for the scenes to be initialized, in order to access the
// current scene (required for SwipeTransition).
@@ -147,7 +149,10 @@
return scenes[key] ?: error("Scene $key is not configured")
}
- internal fun updateScenes(builder: SceneTransitionLayoutScope.() -> Unit) {
+ internal fun updateScenes(
+ builder: SceneTransitionLayoutScope.() -> Unit,
+ layoutDirection: LayoutDirection,
+ ) {
// Keep a reference of the current scenes. After processing [builder], the scenes that were
// not configured will be removed.
val scenesToRemove = scenes.keys.toMutableSet()
@@ -163,11 +168,13 @@
) {
scenesToRemove.remove(key)
+ val resolvedUserActions =
+ userActions.mapKeys { it.key.resolve(layoutDirection) }
val scene = scenes[key]
if (scene != null) {
// Update an existing scene.
scene.content = content
- scene.userActions = userActions
+ scene.userActions = resolvedUserActions
scene.zIndex = zIndex
} else {
// New scene.
@@ -176,7 +183,7 @@
key,
this@SceneTransitionLayoutImpl,
content,
- userActions,
+ resolvedUserActions,
zIndex,
)
}
@@ -213,7 +220,7 @@
@Composable
private fun BackHandler() {
val targetSceneForBack =
- scene(state.transitionState.currentScene).userActions[Back]?.toScene
+ scene(state.transitionState.currentScene).userActions[Back.Resolved]?.toScene
PredictiveBackHandler(state, coroutineScope, targetSceneForBack)
}
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SwipeToScene.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SwipeToScene.kt
index 171e243..aeb6262 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SwipeToScene.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SwipeToScene.kt
@@ -98,7 +98,9 @@
/** Whether swipe should be enabled in the given [orientation]. */
private fun Scene.shouldEnableSwipes(orientation: Orientation): Boolean {
- return userActions.keys.any { it is Swipe && it.direction.orientation == orientation }
+ return userActions.keys.any {
+ it is Swipe.Resolved && it.direction.orientation == orientation
+ }
}
private fun startDragImmediately(startedPosition: Offset): Boolean {
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/EdgeTranslate.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/EdgeTranslate.kt
index aa8dc38..7daefd0 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/EdgeTranslate.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/EdgeTranslate.kt
@@ -44,26 +44,26 @@
return value
}
- return when (edge) {
- Edge.Top ->
+ return when (edge.resolve(layoutImpl.layoutDirection)) {
+ Edge.Resolved.Top ->
if (startsOutsideLayoutBounds) {
Offset(value.x, -elementSize.height.toFloat())
} else {
Offset(value.x, 0f)
}
- Edge.Left ->
+ Edge.Resolved.Left ->
if (startsOutsideLayoutBounds) {
Offset(-elementSize.width.toFloat(), value.y)
} else {
Offset(0f, value.y)
}
- Edge.Bottom ->
+ Edge.Resolved.Bottom ->
if (startsOutsideLayoutBounds) {
Offset(value.x, sceneSize.height.toFloat())
} else {
Offset(value.x, (sceneSize.height - elementSize.height).toFloat())
}
- Edge.Right ->
+ Edge.Resolved.Right ->
if (startsOutsideLayoutBounds) {
Offset(sceneSize.width.toFloat(), value.y)
} else {
diff --git a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/DraggableHandlerTest.kt b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/DraggableHandlerTest.kt
index ff83d4b..7a5a84e 100644
--- a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/DraggableHandlerTest.kt
+++ b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/DraggableHandlerTest.kt
@@ -25,6 +25,7 @@
import androidx.compose.ui.input.nestedscroll.NestedScrollSource
import androidx.compose.ui.unit.Density
import androidx.compose.ui.unit.IntSize
+import androidx.compose.ui.unit.LayoutDirection
import androidx.compose.ui.unit.Velocity
import androidx.test.ext.junit.runners.AndroidJUnit4
import com.android.compose.animation.scene.NestedScrollBehavior.DuringTransitionBetweenScenes
@@ -61,8 +62,24 @@
canChangeScene = { canChangeScene(it) },
)
- val mutableUserActionsA = mutableMapOf(Swipe.Up to SceneB, Swipe.Down to SceneC)
- val mutableUserActionsB = mutableMapOf(Swipe.Up to SceneC, Swipe.Down to SceneA)
+ var layoutDirection = LayoutDirection.Rtl
+ set(value) {
+ field = value
+ layoutImpl.updateScenes(scenesBuilder, layoutDirection)
+ }
+
+ var mutableUserActionsA = mapOf(Swipe.Up to SceneB, Swipe.Down to SceneC)
+ set(value) {
+ field = value
+ layoutImpl.updateScenes(scenesBuilder, layoutDirection)
+ }
+
+ var mutableUserActionsB = mapOf(Swipe.Up to SceneC, Swipe.Down to SceneA)
+ set(value) {
+ field = value
+ layoutImpl.updateScenes(scenesBuilder, layoutDirection)
+ }
+
private val scenesBuilder: SceneTransitionLayoutScope.() -> Unit = {
scene(
key = SceneA,
@@ -94,6 +111,7 @@
SceneTransitionLayoutImpl(
state = layoutState,
density = Density(1f),
+ layoutDirection = LayoutDirection.Ltr,
swipeSourceDetector = DefaultEdgeDetector,
transitionInterceptionThreshold = transitionInterceptionThreshold,
builder = scenesBuilder,
@@ -466,10 +484,8 @@
dragController1.onDragStopped(velocity = -velocityThreshold)
assertTransition(currentScene = SceneB, fromScene = SceneA, toScene = SceneB)
- mutableUserActionsA.remove(Swipe.Up)
- mutableUserActionsA.remove(Swipe.Down)
- mutableUserActionsB.remove(Swipe.Up)
- mutableUserActionsB.remove(Swipe.Down)
+ mutableUserActionsA = emptyMap()
+ mutableUserActionsB = emptyMap()
// start accelaratedScroll and scroll over to B -> null
val dragController2 = onDragStartedImmediately()
@@ -495,7 +511,7 @@
val dragController1 = onDragStarted(overSlop = up(fractionOfScreen = 0.1f))
assertTransition(fromScene = SceneA, toScene = SceneB, progress = 0.1f)
- mutableUserActionsA[Swipe.Up] = UserActionResult(SceneC)
+ mutableUserActionsA += Swipe.Up to UserActionResult(SceneC)
dragController1.onDragDelta(pixels = up(fractionOfScreen = 0.1f))
// target stays B even though UserActions changed
assertTransition(fromScene = SceneA, toScene = SceneB, progress = 0.2f)
@@ -512,7 +528,7 @@
val dragController1 = onDragStarted(overSlop = up(fractionOfScreen = 0.1f))
assertTransition(fromScene = SceneA, toScene = SceneB, progress = 0.1f)
- mutableUserActionsA[Swipe.Up] = UserActionResult(SceneC)
+ mutableUserActionsA += Swipe.Up to UserActionResult(SceneC)
dragController1.onDragDelta(pixels = up(fractionOfScreen = 0.1f))
dragController1.onDragStopped(velocity = down(fractionOfScreen = 0.1f))
@@ -1149,8 +1165,7 @@
overscroll(SceneA, Orientation.Vertical) { fade(TestElements.Foo) }
}
- mutableUserActionsA.clear()
- mutableUserActionsA[Swipe.Up] = UserActionResult(SceneB)
+ mutableUserActionsA = mapOf(Swipe.Up to UserActionResult(SceneB))
val middle = Offset(SCREEN_SIZE / 2f, SCREEN_SIZE / 2f)
val dragController = onDragStarted(startedPosition = middle, overSlop = down(1f))
@@ -1178,8 +1193,7 @@
overscroll(SceneA, Orientation.Vertical) { fade(TestElements.Foo) }
}
- mutableUserActionsA.clear()
- mutableUserActionsA[Swipe.Down] = UserActionResult(SceneC)
+ mutableUserActionsA = mapOf(Swipe.Down to UserActionResult(SceneC))
val middle = Offset(SCREEN_SIZE / 2f, SCREEN_SIZE / 2f)
val dragController = onDragStarted(startedPosition = middle, overSlop = up(1f))
@@ -1220,7 +1234,8 @@
@Test
fun requireFullDistanceSwipe() = runGestureTest {
- mutableUserActionsA[Swipe.Up] = UserActionResult(SceneB, requiresFullDistanceSwipe = true)
+ mutableUserActionsA +=
+ Swipe.Up to UserActionResult(SceneB, requiresFullDistanceSwipe = true)
val controller = onDragStarted(overSlop = up(fractionOfScreen = 0.9f))
assertTransition(fromScene = SceneA, toScene = SceneB, progress = 0.9f)
diff --git a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/SwipeToSceneTest.kt b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/SwipeToSceneTest.kt
index 25ea2ee..0766e00 100644
--- a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/SwipeToSceneTest.kt
+++ b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/SwipeToSceneTest.kt
@@ -23,11 +23,13 @@
import androidx.compose.foundation.layout.offset
import androidx.compose.foundation.layout.size
import androidx.compose.runtime.Composable
+import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.geometry.Offset
+import androidx.compose.ui.platform.LocalLayoutDirection
import androidx.compose.ui.platform.LocalViewConfiguration
import androidx.compose.ui.platform.testTag
import androidx.compose.ui.test.assertPositionInRootIsEqualTo
@@ -37,10 +39,12 @@
import androidx.compose.ui.test.swipeWithVelocity
import androidx.compose.ui.unit.Density
import androidx.compose.ui.unit.IntSize
+import androidx.compose.ui.unit.LayoutDirection
import androidx.compose.ui.unit.dp
import androidx.test.ext.junit.runners.AndroidJUnit4
import com.android.compose.animation.scene.TestScenes.SceneA
import com.android.compose.animation.scene.TestScenes.SceneB
+import com.android.compose.animation.scene.TestScenes.SceneC
import com.android.compose.animation.scene.subjects.assertThat
import com.google.common.truth.Truth.assertThat
import org.junit.Rule
@@ -634,4 +638,152 @@
// Foo should be translated by (20dp, 30dp).
rule.onNode(isElement(TestElements.Foo)).assertPositionInRootIsEqualTo(20.dp, 30.dp)
}
+
+ @Test
+ fun startEnd_ltrLayout() {
+ val state =
+ rule.runOnUiThread {
+ MutableSceneTransitionLayoutState(
+ initialScene = SceneA,
+ transitions =
+ transitions {
+ from(SceneA, to = SceneB) {
+ // We go to B by swiping to the start (left in LTR), so we make
+ // scene B appear from the end (right) edge.
+ translate(SceneB.rootElementKey, Edge.End)
+ }
+
+ from(SceneA, to = SceneC) {
+ // We go to C by swiping to the end (right in LTR), so we make
+ // scene C appear from the start (left) edge.
+ translate(SceneC.rootElementKey, Edge.Start)
+ }
+ },
+ )
+ }
+
+ val layoutSize = 200.dp
+ var touchSlop = 0f
+ rule.setContent {
+ touchSlop = LocalViewConfiguration.current.touchSlop
+ SceneTransitionLayout(state, Modifier.size(layoutSize)) {
+ scene(SceneA, userActions = mapOf(Swipe.Start to SceneB, Swipe.End to SceneC)) {
+ Box(Modifier.fillMaxSize())
+ }
+ scene(SceneB) { Box(Modifier.element(SceneB.rootElementKey).fillMaxSize()) }
+ scene(SceneC) { Box(Modifier.element(SceneC.rootElementKey).fillMaxSize()) }
+ }
+ }
+
+ // Swipe to the left (start).
+ rule.onRoot().performTouchInput {
+ val middle = (layoutSize / 2).toPx()
+ down(Offset(middle, middle))
+ moveBy(Offset(-touchSlop, 0f), delayMillis = 1_000)
+ }
+
+ // Scene B should come from the right (end) edge.
+ var transition = assertThat(state.transitionState).isTransition()
+ assertThat(transition).hasFromScene(SceneA)
+ assertThat(transition).hasToScene(SceneB)
+ rule
+ .onNode(isElement(SceneB.rootElementKey))
+ .assertPositionInRootIsEqualTo(layoutSize, 0.dp)
+
+ // Release to go back to A.
+ rule.onRoot().performTouchInput { up() }
+ rule.waitForIdle()
+ assertThat(state.transitionState).isIdle()
+ assertThat(state.transitionState).hasCurrentScene(SceneA)
+
+ // Swipe to the right (end).
+ rule.onRoot().performTouchInput {
+ val middle = (layoutSize / 2).toPx()
+ down(Offset(middle, middle))
+ moveBy(Offset(touchSlop, 0f), delayMillis = 1_000)
+ }
+
+ // Scene C should come from the left (start) edge.
+ transition = assertThat(state.transitionState).isTransition()
+ assertThat(transition).hasFromScene(SceneA)
+ assertThat(transition).hasToScene(SceneC)
+ rule
+ .onNode(isElement(SceneC.rootElementKey))
+ .assertPositionInRootIsEqualTo(-layoutSize, 0.dp)
+ }
+
+ @Test
+ fun startEnd_rtlLayout() {
+ val state =
+ rule.runOnUiThread {
+ MutableSceneTransitionLayoutState(
+ initialScene = SceneA,
+ transitions =
+ transitions {
+ from(SceneA, to = SceneB) {
+ // We go to B by swiping to the start (right in RTL), so we make
+ // scene B appear from the end (left) edge.
+ translate(SceneB.rootElementKey, Edge.End)
+ }
+
+ from(SceneA, to = SceneC) {
+ // We go to C by swiping to the end (left in RTL), so we make
+ // scene C appear from the start (right) edge.
+ translate(SceneC.rootElementKey, Edge.Start)
+ }
+ },
+ )
+ }
+
+ val layoutSize = 200.dp
+ var touchSlop = 0f
+ rule.setContent {
+ touchSlop = LocalViewConfiguration.current.touchSlop
+ CompositionLocalProvider(LocalLayoutDirection provides LayoutDirection.Rtl) {
+ SceneTransitionLayout(state, Modifier.size(layoutSize)) {
+ scene(SceneA, userActions = mapOf(Swipe.Start to SceneB, Swipe.End to SceneC)) {
+ Box(Modifier.fillMaxSize())
+ }
+ scene(SceneB) { Box(Modifier.element(SceneB.rootElementKey).fillMaxSize()) }
+ scene(SceneC) { Box(Modifier.element(SceneC.rootElementKey).fillMaxSize()) }
+ }
+ }
+ }
+
+ // Swipe to the left (end).
+ rule.onRoot().performTouchInput {
+ val middle = (layoutSize / 2).toPx()
+ down(Offset(middle, middle))
+ moveBy(Offset(-touchSlop, 0f), delayMillis = 1_000)
+ }
+
+ // Scene C should come from the right (start) edge.
+ var transition = assertThat(state.transitionState).isTransition()
+ assertThat(transition).hasFromScene(SceneA)
+ assertThat(transition).hasToScene(SceneC)
+ rule
+ .onNode(isElement(SceneC.rootElementKey))
+ .assertPositionInRootIsEqualTo(layoutSize, 0.dp)
+
+ // Release to go back to A.
+ rule.onRoot().performTouchInput { up() }
+ rule.waitForIdle()
+ assertThat(state.transitionState).isIdle()
+ assertThat(state.transitionState).hasCurrentScene(SceneA)
+
+ // Swipe to the right (start).
+ rule.onRoot().performTouchInput {
+ val middle = (layoutSize / 2).toPx()
+ down(Offset(middle, middle))
+ moveBy(Offset(touchSlop, 0f), delayMillis = 1_000)
+ }
+
+ // Scene C should come from the left (end) edge.
+ transition = assertThat(state.transitionState).isTransition()
+ assertThat(transition).hasFromScene(SceneA)
+ assertThat(transition).hasToScene(SceneB)
+ rule
+ .onNode(isElement(SceneB.rootElementKey))
+ .assertPositionInRootIsEqualTo(-layoutSize, 0.dp)
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/KeyboardShortcuts.java b/packages/SystemUI/src/com/android/systemui/statusbar/KeyboardShortcuts.java
index da89eea..766c391 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/KeyboardShortcuts.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/KeyboardShortcuts.java
@@ -526,7 +526,7 @@
keyboardShortcutInfoAppItems.add(new KeyboardShortcutInfo(
mContext.getString(R.string.keyboard_shortcut_group_applications_assist),
assistIcon,
- KeyEvent.KEYCODE_UNKNOWN,
+ KeyEvent.KEYCODE_A,
KeyEvent.META_META_ON));
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBypassController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBypassController.kt
index 97791ac..316e1f1 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBypassController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBypassController.kt
@@ -22,6 +22,7 @@
import android.hardware.biometrics.BiometricSourceType
import android.provider.Settings
import com.android.app.tracing.ListenersTracing.forEachTraced
+import com.android.app.tracing.coroutines.launch
import com.android.systemui.Dumpable
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
@@ -159,7 +160,7 @@
}
fun listenForQsExpandedChange() =
- applicationScope.launch {
+ applicationScope.launch("listenForQsExpandedChange") {
shadeInteractorLazy.get().qsExpansion.map { it > 0f }.distinctUntilChanged()
.collect { isQsExpanded ->
val changed = qsExpanded != isQsExpanded
diff --git a/services/core/java/com/android/server/inputmethod/AdditionalSubtypeMapRepository.java b/services/core/java/com/android/server/inputmethod/AdditionalSubtypeMapRepository.java
index 9968590..8ca0458 100644
--- a/services/core/java/com/android/server/inputmethod/AdditionalSubtypeMapRepository.java
+++ b/services/core/java/com/android/server/inputmethod/AdditionalSubtypeMapRepository.java
@@ -220,6 +220,11 @@
}
@AnyThread
+ static void onUserCreated(@UserIdInt int userId) {
+ sWriter.onUserCreated(userId);
+ }
+
+ @AnyThread
static void remove(@UserIdInt int userId, @NonNull Handler ioHandler) {
sWriter.onUserRemoved(userId);
ioHandler.post(() -> {
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
index e342c78..f5faeef 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
@@ -176,7 +176,6 @@
import com.android.server.EventLogTags;
import com.android.server.LocalServices;
import com.android.server.ServiceThread;
-import com.android.server.SystemServerInitThreadPool;
import com.android.server.SystemService;
import com.android.server.companion.virtual.VirtualDeviceManagerInternal;
import com.android.server.input.InputManagerInternal;
@@ -1013,7 +1012,9 @@
@Override
public void onUserCreated(UserInfo user, @Nullable Object token) {
// Called directly from UserManagerService. Do not block the calling thread.
- initializeUsersAsync(new int[user.id]);
+ final int userId = user.id;
+ AdditionalSubtypeMapRepository.onUserCreated(userId);
+ initializeUsersAsync(new int[userId]);
}
@Override
@@ -1390,9 +1391,7 @@
getPackageManagerForUser(mContext, currentUserId),
newSettings.getEnabledInputMethodList());
- final var unused = SystemServerInitThreadPool.submit(
- AdditionalSubtypeMapRepository::startWriterThread,
- "Start AdditionalSubtypeMapRepository's writer thread");
+ AdditionalSubtypeMapRepository.startWriterThread();
if (mConcurrentMultiUserModeEnabled) {
for (int userId : mUserManagerInternal.getUserIds()) {
diff --git a/services/core/java/com/android/server/om/OverlayManagerService.java b/services/core/java/com/android/server/om/OverlayManagerService.java
index 56e4590..46585a5 100644
--- a/services/core/java/com/android/server/om/OverlayManagerService.java
+++ b/services/core/java/com/android/server/om/OverlayManagerService.java
@@ -80,6 +80,7 @@
import android.util.Slog;
import android.util.SparseArray;
+import com.android.internal.annotations.KeepForWeakReference;
import com.android.internal.content.PackageMonitor;
import com.android.internal.content.om.OverlayConfig;
import com.android.internal.util.ArrayUtils;
@@ -261,6 +262,7 @@
private final OverlayActorEnforcer mActorEnforcer;
+ @KeepForWeakReference
private final PackageMonitor mPackageMonitor = new OverlayManagerPackageMonitor();
private int mPrevStartedUserId = -1;
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index 8768074..3076b87 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -7173,7 +7173,7 @@
Slog.v(TAG, "Eval win " + w + ": isDrawn=" + w.isDrawn()
+ ", isAnimationSet=" + isAnimationSet);
if (!w.isDrawn()) {
- Slog.v(TAG, "Not displayed: s=" + winAnimator.mSurfaceController
+ Slog.v(TAG, "Not displayed: s=" + winAnimator.mSurfaceControl
+ " pv=" + w.isVisibleByPolicy()
+ " mDrawState=" + winAnimator.drawStateToString()
+ " ph=" + w.isParentWindowHidden() + " th=" + mVisibleRequested
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
index 59b5da8..ff46b33 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
@@ -4709,6 +4709,10 @@
// Update stored global config and notify everyone about the change.
mRootWindowContainer.onConfigurationChanged(mTempConfig);
Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
+ if ((changes & ActivityInfo.CONFIG_ORIENTATION) != 0) {
+ FrameworkStatsLog.write(FrameworkStatsLog.DEVICE_ORIENTATION_CHANGED,
+ values.orientation);
+ }
Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
return changes;
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index 3652c4d..9371149 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -4082,12 +4082,12 @@
final Transaction t = mWmService.mTransactionFactory.get();
forAllWindows(w -> {
final WindowStateAnimator wsa = w.mWinAnimator;
- if (wsa.mSurfaceController == null) {
+ if (wsa.mSurfaceControl == null) {
return;
}
if (!mWmService.mSessions.contains(wsa.mSession)) {
Slog.w(TAG_WM, "LEAKED SURFACE (session doesn't exist): "
- + w + " surface=" + wsa.mSurfaceController
+ + w + " surface=" + wsa.mSurfaceControl
+ " token=" + w.mToken
+ " pid=" + w.mSession.mPid
+ " uid=" + w.mSession.mUid);
@@ -4096,7 +4096,7 @@
mTmpWindow = w;
} else if (w.mActivityRecord != null && !w.mActivityRecord.isClientVisible()) {
Slog.w(TAG_WM, "LEAKED SURFACE (app token hidden): "
- + w + " surface=" + wsa.mSurfaceController
+ + w + " surface=" + wsa.mSurfaceControl
+ " token=" + w.mActivityRecord);
ProtoLog.i(WM_SHOW_TRANSACTIONS, "SURFACE LEAK DESTROY: %s", w);
wsa.destroySurface(t);
diff --git a/services/core/java/com/android/server/wm/InputMonitor.java b/services/core/java/com/android/server/wm/InputMonitor.java
index 74dbd15..b496a65 100644
--- a/services/core/java/com/android/server/wm/InputMonitor.java
+++ b/services/core/java/com/android/server/wm/InputMonitor.java
@@ -623,7 +623,7 @@
// occlusion detection depending on the type or if it's a trusted overlay.
populateOverlayInputInfo(inputWindowHandle, w);
setInputWindowInfoIfNeeded(mInputTransaction,
- w.mWinAnimator.mSurfaceController.mSurfaceControl, inputWindowHandle);
+ w.mWinAnimator.mSurfaceControl, inputWindowHandle);
return;
}
// Skip this window because it cannot possibly receive input.
@@ -687,7 +687,7 @@
if (w.mWinAnimator.hasSurface()) {
populateInputWindowHandle(inputWindowHandle, w);
setInputWindowInfoIfNeeded(mInputTransaction,
- w.mWinAnimator.mSurfaceController.mSurfaceControl, inputWindowHandle);
+ w.mWinAnimator.mSurfaceControl, inputWindowHandle);
}
}
}
diff --git a/services/core/java/com/android/server/wm/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java
index 99697de..f2ccbc4 100644
--- a/services/core/java/com/android/server/wm/RootWindowContainer.java
+++ b/services/core/java/com/android/server/wm/RootWindowContainer.java
@@ -667,7 +667,7 @@
boolean reclaimSomeSurfaceMemory(WindowStateAnimator winAnimator, String operation,
boolean secure) {
- final WindowSurfaceController surfaceController = winAnimator.mSurfaceController;
+ final SurfaceControl surfaceControl = winAnimator.mSurfaceControl;
boolean leakedSurface = false;
boolean killedApps = false;
EventLogTags.writeWmNoSurfaceMemory(winAnimator.mWin.toString(),
@@ -692,7 +692,7 @@
return;
}
final WindowStateAnimator wsa = w.mWinAnimator;
- if (wsa.mSurfaceController != null) {
+ if (wsa.mSurfaceControl != null) {
pidCandidates.append(wsa.mSession.mPid, wsa.mSession.mPid);
}
}, false /* traverseTopToBottom */);
@@ -717,7 +717,7 @@
// app to request another one.
Slog.w(TAG_WM,
"Looks like we have reclaimed some memory, clearing surface for retry.");
- if (surfaceController != null) {
+ if (surfaceControl != null) {
ProtoLog.i(WM_SHOW_SURFACE_ALLOC,
"SURFACE RECOVER DESTROY: %s", winAnimator.mWin);
SurfaceControl.Transaction t = mWmService.mTransactionFactory.get();
diff --git a/services/core/java/com/android/server/wm/ScreenRotationAnimation.java b/services/core/java/com/android/server/wm/ScreenRotationAnimation.java
index 31fda77..db0374e 100644
--- a/services/core/java/com/android/server/wm/ScreenRotationAnimation.java
+++ b/services/core/java/com/android/server/wm/ScreenRotationAnimation.java
@@ -324,7 +324,7 @@
if (!w.mToken.mRoundedCornerOverlay || !w.isVisible() || !w.mWinAnimator.hasSurface()) {
return;
}
- t.setSkipScreenshot(w.mWinAnimator.mSurfaceController.mSurfaceControl, skipScreenshot);
+ t.setSkipScreenshot(w.mWinAnimator.mSurfaceControl, skipScreenshot);
}, false);
if (!skipScreenshot) {
// Use sync apply to apply the change immediately, so that the next
diff --git a/services/core/java/com/android/server/wm/Session.java b/services/core/java/com/android/server/wm/Session.java
index 75e3e65..f5108f5b 100644
--- a/services/core/java/com/android/server/wm/Session.java
+++ b/services/core/java/com/android/server/wm/Session.java
@@ -109,8 +109,8 @@
private final String mStringName;
SurfaceSession mSurfaceSession;
private final ArrayList<WindowState> mAddedWindows = new ArrayList<>();
- /** Set of visible alert/app-overlay window surfaces connected to this session. */
- private final ArraySet<WindowSurfaceController> mAlertWindowSurfaces = new ArraySet<>();
+ /** Set of visible alert/app-overlay windows connected to this session. */
+ private final ArraySet<WindowState> mAlertWindows = new ArraySet<>();
private final DragDropController mDragDropController;
final boolean mCanAddInternalSystemWindow;
boolean mCanForceShowingInsets;
@@ -769,9 +769,8 @@
return !mAddedWindows.isEmpty();
}
- void onWindowSurfaceVisibilityChanged(WindowSurfaceController surfaceController,
- boolean visible, int type) {
-
+ void onWindowSurfaceVisibilityChanged(WindowState window, boolean visible) {
+ final int type = window.mAttrs.type;
if (!isSystemAlertWindowType(type)) {
return;
}
@@ -782,7 +781,7 @@
final boolean noSystemOverlayPermission =
!mCanAddInternalSystemWindow && !mCanCreateSystemApplicationOverlay;
if (visible) {
- changed = mAlertWindowSurfaces.add(surfaceController);
+ changed = mAlertWindows.add(window);
if (type == TYPE_APPLICATION_OVERLAY) {
MetricsLoggerWrapper.logAppOverlayEnter(mUid, mPackageName, changed, type,
false /* set false to only log for TYPE_APPLICATION_OVERLAY */);
@@ -791,7 +790,7 @@
true /* only log for non-TYPE_APPLICATION_OVERLAY */);
}
} else {
- changed = mAlertWindowSurfaces.remove(surfaceController);
+ changed = mAlertWindows.remove(window);
if (type == TYPE_APPLICATION_OVERLAY) {
MetricsLoggerWrapper.logAppOverlayExit(mUid, mPackageName, changed, type,
false /* set false to only log for TYPE_APPLICATION_OVERLAY */);
@@ -802,7 +801,7 @@
}
if (changed && noSystemOverlayPermission) {
- if (mAlertWindowSurfaces.isEmpty()) {
+ if (mAlertWindows.isEmpty()) {
cancelAlertWindowNotification();
} else if (mAlertWindowNotification == null && !isSatellitePointingUiPackage()) {
mAlertWindowNotification = new AlertWindowNotification(mService, mPackageName);
@@ -815,7 +814,7 @@
if (changed && mPid != WindowManagerService.MY_PID) {
// Notify activity manager that the process contains overlay/alert windows, so it can
// adjust the importance score for the process.
- setHasOverlayUi(!mAlertWindowSurfaces.isEmpty());
+ setHasOverlayUi(!mAlertWindows.isEmpty());
}
}
@@ -859,7 +858,7 @@
}
mSurfaceSession = null;
mAddedWindows.clear();
- mAlertWindowSurfaces.clear();
+ mAlertWindows.clear();
setHasOverlayUi(false);
cancelAlertWindowNotification();
}
@@ -880,7 +879,7 @@
void dump(PrintWriter pw, String prefix) {
pw.print(prefix); pw.print("numWindow="); pw.print(mAddedWindows.size());
pw.print(" mCanAddInternalSystemWindow="); pw.print(mCanAddInternalSystemWindow);
- pw.print(" mAlertWindowSurfaces="); pw.print(mAlertWindowSurfaces);
+ pw.print(" mAlertWindows="); pw.print(mAlertWindows);
pw.print(" mClientDead="); pw.print(mClientDead);
pw.print(" mSurfaceSession="); pw.println(mSurfaceSession);
pw.print(prefix); pw.print("mPackageName="); pw.println(mPackageName);
@@ -896,9 +895,9 @@
/** @return {@code true} if there is an alert window surface on the given display. */
boolean hasAlertWindowSurfaces(DisplayContent displayContent) {
- for (int i = mAlertWindowSurfaces.size() - 1; i >= 0; i--) {
- final WindowSurfaceController surfaceController = mAlertWindowSurfaces.valueAt(i);
- if (surfaceController.mAnimator.mWin.getDisplayContent() == displayContent) {
+ for (int i = mAlertWindows.size() - 1; i >= 0; i--) {
+ final WindowState window = mAlertWindows.valueAt(i);
+ if (window.mDisplayContent == displayContent) {
return true;
}
}
diff --git a/services/core/java/com/android/server/wm/TaskFragmentOrganizerController.java b/services/core/java/com/android/server/wm/TaskFragmentOrganizerController.java
index 439c7bb..561ff7d 100644
--- a/services/core/java/com/android/server/wm/TaskFragmentOrganizerController.java
+++ b/services/core/java/com/android/server/wm/TaskFragmentOrganizerController.java
@@ -1169,16 +1169,6 @@
}
}
- @Override
- public boolean isActivityEmbedded(IBinder activityToken) {
- synchronized (mGlobalLock) {
- final ActivityRecord activity = ActivityRecord.forTokenLocked(activityToken);
- return activity != null
- ? activity.isEmbeddedInHostContainer()
- : false;
- }
- }
-
@VisibleForTesting
@NonNull
IApplicationThread getAppThread(int pid, int uid) {
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 700c069..ebbf6e3 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -2570,7 +2570,7 @@
// surface, let the client use that, but don't create new surface at this
// point.
Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "relayoutWindow: getSurface");
- winAnimator.mSurfaceController.getSurfaceControl(outSurfaceControl);
+ winAnimator.getSurfaceControl(outSurfaceControl);
Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
} else {
if (DEBUG_VISIBILITY) Slog.i(TAG_WM, "Releasing surface in: " + win);
@@ -2766,15 +2766,15 @@
result |= RELAYOUT_RES_SURFACE_CHANGED;
}
- WindowSurfaceController surfaceController;
+ SurfaceControl surfaceControl;
try {
Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "createSurfaceControl");
- surfaceController = winAnimator.createSurfaceLocked();
+ surfaceControl = winAnimator.createSurfaceLocked();
} finally {
Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
}
- if (surfaceController != null) {
- surfaceController.getSurfaceControl(outSurfaceControl);
+ if (surfaceControl != null) {
+ winAnimator.getSurfaceControl(outSurfaceControl);
ProtoLog.i(WM_SHOW_TRANSACTIONS, "OUT SURFACE %s: copied", outSurfaceControl);
} else {
@@ -6773,11 +6773,11 @@
if (windowState == null) {
return false;
}
- WindowSurfaceController surfaceController = windowState.mWinAnimator.mSurfaceController;
- if (surfaceController == null) {
+ final SurfaceControl surfaceControl = windowState.mWinAnimator.mSurfaceControl;
+ if (surfaceControl == null) {
return false;
}
- return surfaceController.clearWindowContentFrameStats();
+ return surfaceControl.clearContentFrameStats();
}
}
@@ -6792,15 +6792,15 @@
if (windowState == null) {
return null;
}
- WindowSurfaceController surfaceController = windowState.mWinAnimator.mSurfaceController;
- if (surfaceController == null) {
+ final SurfaceControl surfaceControl = windowState.mWinAnimator.mSurfaceControl;
+ if (surfaceControl == null) {
return null;
}
if (mTempWindowRenderStats == null) {
mTempWindowRenderStats = new WindowContentFrameStats();
}
WindowContentFrameStats stats = mTempWindowRenderStats;
- if (!surfaceController.getWindowContentFrameStats(stats)) {
+ if (!surfaceControl.getContentFrameStats(stats)) {
return null;
}
return stats;
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index 390e34c..9ebb89d 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -1501,7 +1501,7 @@
if (isDrawn()) {
ProtoLog.v(WM_DEBUG_ORIENTATION,
"Orientation not waiting for draw in %s, surfaceController %s", this,
- winAnimator.mSurfaceController);
+ winAnimator.mSurfaceControl);
setOrientationChanging(false);
mLastFreezeDuration = (int)(SystemClock.elapsedRealtime()
- mWmService.mDisplayFreezeTime);
@@ -2425,7 +2425,7 @@
ProtoLog.v(WM_DEBUG_FOCUS, "Remove client=%x, surfaceController=%s Callers=%s",
System.identityHashCode(mClient.asBinder()),
- mWinAnimator.mSurfaceController,
+ mWinAnimator.mSurfaceControl,
Debug.getCallers(5));
final DisplayContent displayContent = getDisplayContent();
@@ -2436,10 +2436,10 @@
mOnBackInvokedCallbackInfo = null;
ProtoLog.v(WM_DEBUG_APP_TRANSITIONS,
- "Remove %s: mSurfaceController=%s mAnimatingExit=%b mRemoveOnExit=%b "
+ "Remove %s: mSurfaceControl=%s mAnimatingExit=%b mRemoveOnExit=%b "
+ "mHasSurface=%b surfaceShowing=%b animating=%b app-animation=%b "
+ "mDisplayFrozen=%b callers=%s",
- this, mWinAnimator.mSurfaceController, mAnimatingExit, mRemoveOnExit,
+ this, mWinAnimator.mSurfaceControl, mAnimatingExit, mRemoveOnExit,
mHasSurface, mWinAnimator.getShown(),
isAnimating(TRANSITION | PARENTS),
mActivityRecord != null && mActivityRecord.isAnimating(PARENTS | TRANSITION),
@@ -2616,7 +2616,7 @@
+ isVisibleRequestedOrAdding() + " isVisible: " + (isVisible()
&& mActivityRecord != null && mActivityRecord.isVisible()));
if (!isVisibleRequestedOrAdding()) {
- Slog.i(TAG_WM, " mSurfaceController=" + mWinAnimator.mSurfaceController
+ Slog.i(TAG_WM, " mSurfaceControl=" + mWinAnimator.mSurfaceControl
+ " relayoutCalled=" + mRelayoutCalled
+ " viewVis=" + mViewVisibility
+ " policyVis=" + isVisibleByPolicy()
@@ -4457,7 +4457,7 @@
for (int i = mChildren.size() - 1; i >= 0; --i) {
final WindowState c = mChildren.get(i);
- if (c.mWinAnimator.mSurfaceController != null) {
+ if (c.mWinAnimator.mSurfaceControl != null) {
c.performShowLocked();
// It hadn't been shown, which means layout not performed on it, so now we
// want to make sure to do a layout. If called from within the transaction
@@ -4914,7 +4914,7 @@
Slog.v(TAG, "Win " + this + ": isDrawn=" + isDrawn()
+ ", animating=" + isAnimating(TRANSITION | PARENTS));
if (!isDrawn()) {
- Slog.v(TAG, "Not displayed: s=" + mWinAnimator.mSurfaceController
+ Slog.v(TAG, "Not displayed: s=" + mWinAnimator.mSurfaceControl
+ " pv=" + isVisibleByPolicy()
+ " mDrawState=" + mWinAnimator.mDrawState
+ " ph=" + isParentWindowHidden()
@@ -5535,13 +5535,13 @@
// been defined and so we can use static layers and leave it that way.
if (w.mAttrs.type == TYPE_APPLICATION_MEDIA) {
if (mWinAnimator.hasSurface()) {
- w.assignRelativeLayer(t, mWinAnimator.mSurfaceController.mSurfaceControl, -2);
+ w.assignRelativeLayer(t, mWinAnimator.mSurfaceControl, -2);
} else {
w.assignLayer(t, -2);
}
} else if (w.mAttrs.type == TYPE_APPLICATION_MEDIA_OVERLAY) {
if (mWinAnimator.hasSurface()) {
- w.assignRelativeLayer(t, mWinAnimator.mSurfaceController.mSurfaceControl, -1);
+ w.assignRelativeLayer(t, mWinAnimator.mSurfaceControl, -1);
} else {
w.assignLayer(t, -1);
}
@@ -5717,7 +5717,7 @@
}
SurfaceControl getClientViewRootSurface() {
- return mWinAnimator.getSurfaceControl();
+ return mWinAnimator.mSurfaceControl;
}
/** Drops a buffer for this window's view-root from a transaction */
@@ -6117,11 +6117,10 @@
}
getPendingTransaction().setSecure(mSurfaceControl, isSecure);
} else {
- if (mWinAnimator.mSurfaceController == null
- || mWinAnimator.mSurfaceController.mSurfaceControl == null) {
+ if (mWinAnimator.mSurfaceControl == null) {
return;
}
- getPendingTransaction().setSecure(mWinAnimator.mSurfaceController.mSurfaceControl,
+ getPendingTransaction().setSecure(mWinAnimator.mSurfaceControl,
isSecure);
}
if (mDisplayContent != null) {
diff --git a/services/core/java/com/android/server/wm/WindowStateAnimator.java b/services/core/java/com/android/server/wm/WindowStateAnimator.java
index 397a6357..24a2a62 100644
--- a/services/core/java/com/android/server/wm/WindowStateAnimator.java
+++ b/services/core/java/com/android/server/wm/WindowStateAnimator.java
@@ -16,6 +16,10 @@
package com.android.server.wm;
+import static android.os.Trace.TRACE_TAG_WINDOW_MANAGER;
+import static android.view.SurfaceControl.METADATA_OWNER_PID;
+import static android.view.SurfaceControl.METADATA_OWNER_UID;
+import static android.view.SurfaceControl.METADATA_WINDOW_TYPE;
import static android.view.WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED;
import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_IS_ROUNDED_CORNERS_OVERLAY;
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_STARTING;
@@ -44,6 +48,7 @@
import static com.android.server.wm.WindowStateAnimatorProto.DRAW_STATE;
import static com.android.server.wm.WindowStateAnimatorProto.SURFACE;
import static com.android.server.wm.WindowStateAnimatorProto.SYSTEM_DECOR_RECT;
+import static com.android.server.wm.WindowSurfaceControllerProto.SHOWN;
import static com.android.window.flags.Flags.secureWindowState;
import static com.android.window.flags.Flags.setScPropertiesInClient;
@@ -52,6 +57,7 @@
import android.graphics.Rect;
import android.os.Debug;
import android.os.Trace;
+import android.util.EventLog;
import android.util.Slog;
import android.util.proto.ProtoOutputStream;
import android.view.Surface.OutOfResourcesException;
@@ -95,12 +101,13 @@
final Session mSession;
final WindowManagerPolicy mPolicy;
final Context mContext;
- final boolean mIsWallpaper;
private final WallpaperController mWallpaperControllerLocked;
boolean mAnimationIsEntrance;
- WindowSurfaceController mSurfaceController;
+ SurfaceControl mSurfaceControl;
+ private boolean mSurfaceShown;
+ private String mTitle;
float mShownAlpha = 0;
float mAlpha = 0;
@@ -164,7 +171,6 @@
mWin = win;
mSession = win.mSession;
mAttrType = win.mAttrs.type;
- mIsWallpaper = win.mIsWallpaper;
mWallpaperControllerLocked = win.getDisplayContent().mWallpaperController;
}
@@ -198,14 +204,30 @@
}
void hide(SurfaceControl.Transaction transaction, String reason) {
- if (!mLastHidden) {
- //dump();
- mLastHidden = true;
-
- if (mSurfaceController != null) {
- mSurfaceController.hide(transaction, reason);
- }
+ if (mLastHidden) {
+ return;
}
+ mLastHidden = true;
+ if (mSurfaceControl == null || !mSurfaceShown) {
+ return;
+ }
+ ProtoLog.i(WM_SHOW_TRANSACTIONS, "SURFACE HIDE ( %s ): %s", reason, mTitle);
+
+ setShown(false);
+ transaction.hide(mSurfaceControl);
+ if (mWin.mIsWallpaper) {
+ final DisplayContent dc = mWin.getDisplayContent();
+ EventLog.writeEvent(EventLogTags.WM_WALLPAPER_SURFACE,
+ dc.mDisplayId, 0 /* request hidden */,
+ String.valueOf(dc.mWallpaperController.getWallpaperTarget()));
+ }
+ }
+
+ private void setShown(boolean surfaceShown) {
+ mSurfaceShown = surfaceShown;
+ mService.updateNonSystemOverlayWindowsVisibilityIfNeeded(mWin, surfaceShown);
+ mWin.onSurfaceShownChanged(surfaceShown);
+ mSession.onWindowSurfaceVisibilityChanged(mWin, mSurfaceShown);
}
boolean finishDrawingLocked(SurfaceControl.Transaction postDrawTransaction) {
@@ -221,7 +243,7 @@
if (mDrawState == DRAW_PENDING) {
ProtoLog.v(WM_DEBUG_DRAW,
"finishDrawingLocked: mDrawState=COMMIT_DRAW_PENDING %s in %s", mWin,
- mSurfaceController);
+ mSurfaceControl);
if (startingWindow) {
ProtoLog.v(WM_DEBUG_STARTING_WINDOW, "Draw state now committed in %s", mWin);
}
@@ -248,7 +270,7 @@
return false;
}
ProtoLog.i(WM_DEBUG_ANIM, "commitFinishDrawingLocked: mDrawState=READY_TO_SHOW %s",
- mSurfaceController);
+ mSurfaceControl);
mDrawState = READY_TO_SHOW;
boolean result = false;
final ActivityRecord activity = mWin.mActivityRecord;
@@ -271,11 +293,11 @@
}
}
- WindowSurfaceController createSurfaceLocked() {
+ SurfaceControl createSurfaceLocked() {
final WindowState w = mWin;
- if (mSurfaceController != null) {
- return mSurfaceController;
+ if (mSurfaceControl != null) {
+ return mSurfaceControl;
}
w.setHasSurface(false);
@@ -312,10 +334,22 @@
final boolean isHwAccelerated = (attrs.flags & FLAG_HARDWARE_ACCELERATED) != 0;
final int format = isHwAccelerated ? PixelFormat.TRANSLUCENT : attrs.format;
- mSurfaceController = new WindowSurfaceController(attrs.getTitle().toString(), format,
- flags, this, attrs.type);
+ mTitle = attrs.getTitle().toString();
+ Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "new SurfaceControl");
+ mSurfaceControl = mWin.makeSurface()
+ .setParent(mWin.mSurfaceControl)
+ .setName(mTitle)
+ .setFormat(format)
+ .setFlags(flags)
+ .setMetadata(METADATA_WINDOW_TYPE, attrs.type)
+ .setMetadata(METADATA_OWNER_UID, mSession.mUid)
+ .setMetadata(METADATA_OWNER_PID, mSession.mPid)
+ .setCallsite("WindowSurfaceController")
+ .setBLASTLayer().build();
+ Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
+
if (!setScPropertiesInClient()) {
- mSurfaceController.setColorSpaceAgnostic(w.getPendingTransaction(),
+ setColorSpaceAgnosticLocked(
(attrs.privateFlags & LayoutParams.PRIVATE_FLAG_COLOR_SPACE_AGNOSTIC) != 0);
}
@@ -326,7 +360,7 @@
ProtoLog.i(WM_SHOW_SURFACE_ALLOC,
" CREATE SURFACE %s IN SESSION %s: pid=%d format=%d flags=0x%x / %s",
- mSurfaceController, mSession.mSurfaceSession, mSession.mPid, attrs.format,
+ mSurfaceControl, mSession.mSurfaceSession, mSession.mPid, attrs.format,
flags, this);
} catch (OutOfResourcesException e) {
Slog.w(TAG, "OutOfResourcesException creating surface");
@@ -340,7 +374,7 @@
}
if (DEBUG) {
- Slog.v(TAG, "Got surface: " + mSurfaceController
+ Slog.v(TAG, "Got surface: " + mSurfaceControl
+ ", set left=" + w.getFrame().left + " top=" + w.getFrame().top);
}
@@ -353,15 +387,19 @@
mLastHidden = true;
if (DEBUG) Slog.v(TAG, "Created surface " + this);
- return mSurfaceController;
+ return mSurfaceControl;
}
boolean hasSurface() {
- return mSurfaceController != null && mSurfaceController.hasSurface();
+ return mSurfaceControl != null;
+ }
+
+ void getSurfaceControl(SurfaceControl outSurfaceControl) {
+ outSurfaceControl.copyFrom(mSurfaceControl, "WindowStateAnimator.getSurfaceControl");
}
void destroySurfaceLocked(SurfaceControl.Transaction t) {
- if (mSurfaceController == null) {
+ if (mSurfaceControl == null) {
return;
}
@@ -370,7 +408,7 @@
try {
if (DEBUG_VISIBILITY) {
logWithStack(TAG, "Window " + this + " destroying surface "
- + mSurfaceController + ", session " + mSession);
+ + mSurfaceControl + ", session " + mSession);
}
ProtoLog.i(WM_SHOW_SURFACE_ALLOC, "SURFACE DESTROY: %s. %s",
mWin, new RuntimeException().fillInStackTrace());
@@ -384,23 +422,13 @@
}
} catch (RuntimeException e) {
Slog.w(TAG, "Exception thrown when destroying Window " + this
- + " surface " + mSurfaceController + " session " + mSession + ": "
+ + " surface " + mSurfaceControl + " session " + mSession + ": "
+ e.toString());
}
-
- // Whether the surface was preserved (and copied to mPendingDestroySurface) or not, it
- // needs to be cleared to match the WindowState.mHasSurface state. It is also necessary
- // so it can be recreated successfully in mPendingDestroySurface case.
- mWin.setHasSurface(false);
- if (mSurfaceController != null) {
- mSurfaceController.setShown(false);
- }
- mSurfaceController = null;
- mDrawState = NO_SURFACE;
}
void computeShownFrameLocked() {
- if (mIsWallpaper && mService.mRoot.mWallpaperActionPending) {
+ if (mWin.mIsWallpaper && mService.mRoot.mWallpaperActionPending) {
return;
} else if (mWin.isDragResizeChanged()) {
// This window is awaiting a relayout because user just started (or ended)
@@ -454,14 +482,13 @@
mLastAlpha = mShownAlpha;
ProtoLog.i(WM_SHOW_TRANSACTIONS,
"SURFACE controller=%s alpha=%f HScale=%f, VScale=%f: %s",
- mSurfaceController, mShownAlpha, w.mHScale, w.mVScale, w);
+ mSurfaceControl, mShownAlpha, w.mHScale, w.mVScale, w);
- boolean prepared =
- mSurfaceController.prepareToShowInTransaction(t, mShownAlpha);
+ t.setAlpha(mSurfaceControl, mShownAlpha);
- if (prepared && mDrawState == HAS_DRAWN) {
+ if (mDrawState == HAS_DRAWN) {
if (mLastHidden) {
- mSurfaceController.showRobustly(t);
+ showRobustly(t);
mLastHidden = false;
final DisplayContent displayContent = w.getDisplayContent();
if (!displayContent.getLastHasContent()) {
@@ -494,18 +521,38 @@
}
}
- void setOpaqueLocked(boolean isOpaque) {
- if (mSurfaceController == null) {
+ private void showRobustly(SurfaceControl.Transaction t) {
+ if (mSurfaceShown) {
return;
}
- mSurfaceController.setOpaque(isOpaque);
+
+ ProtoLog.i(WM_SHOW_TRANSACTIONS, "SURFACE SHOW (performLayout): %s", mTitle);
+ if (DEBUG_VISIBILITY) Slog.v(TAG, "Showing " + this + " during relayout");
+ setShown(true);
+ t.show(mSurfaceControl);
+ if (mWin.mIsWallpaper) {
+ final DisplayContent dc = mWin.mDisplayContent;
+ EventLog.writeEvent(EventLogTags.WM_WALLPAPER_SURFACE,
+ dc.mDisplayId, 1 /* request shown */,
+ String.valueOf(dc.mWallpaperController.getWallpaperTarget()));
+ }
+ }
+
+ void setOpaqueLocked(boolean isOpaque) {
+ if (mSurfaceControl == null) {
+ return;
+ }
+ ProtoLog.i(WM_SHOW_TRANSACTIONS, "SURFACE isOpaque=%b: %s", isOpaque, mTitle);
+ mWin.getPendingTransaction().setOpaque(mSurfaceControl, isOpaque);
+ mService.scheduleAnimationLocked();
}
void setColorSpaceAgnosticLocked(boolean agnostic) {
- if (mSurfaceController == null) {
+ if (mSurfaceControl == null) {
return;
}
- mSurfaceController.setColorSpaceAgnostic(mWin.getPendingTransaction(), agnostic);
+ ProtoLog.i(WM_SHOW_TRANSACTIONS, "SURFACE isColorSpaceAgnostic=%b: %s", agnostic, mTitle);
+ mWin.getPendingTransaction().setColorSpaceAgnostic(mSurfaceControl, agnostic);
}
void applyEnterAnimationLocked() {
@@ -521,7 +568,7 @@
// should be controlled by ActivityRecord in general. Wallpaper is also excluded because
// WallpaperController should handle it. Also skip play enter animation for the window
// below starting window.
- if (mAttrType != TYPE_BASE_APPLICATION && !mIsWallpaper
+ if (mAttrType != TYPE_BASE_APPLICATION && !mWin.mIsWallpaper
&& !(mWin.mActivityRecord != null && mWin.mActivityRecord.hasStartingWindow())) {
applyAnimationLocked(transit, true);
}
@@ -614,8 +661,10 @@
void dumpDebug(ProtoOutputStream proto, long fieldId) {
final long token = proto.start(fieldId);
- if (mSurfaceController != null) {
- mSurfaceController.dumpDebug(proto, SURFACE);
+ if (mSurfaceControl != null) {
+ final long dumpToken = proto.start(SURFACE);
+ proto.write(SHOWN, mSurfaceShown);
+ proto.end(dumpToken);
}
proto.write(DRAW_STATE, mDrawState);
mSystemDecorRect.dumpDebug(proto, SYSTEM_DECOR_RECT);
@@ -626,8 +675,11 @@
if (mAnimationIsEntrance) {
pw.print(prefix); pw.print(" mAnimationIsEntrance="); pw.print(mAnimationIsEntrance);
}
- if (mSurfaceController != null) {
- mSurfaceController.dump(pw, prefix, dumpAll);
+ if (mSurfaceControl != null) {
+ if (dumpAll) {
+ pw.print(prefix); pw.print("mSurface="); pw.println(mSurfaceControl);
+ }
+ pw.print(prefix); pw.print("Surface: shown="); pw.print(mSurfaceShown);
}
if (dumpAll) {
pw.print(prefix); pw.print("mDrawState="); pw.print(drawStateToString());
@@ -659,31 +711,24 @@
}
boolean getShown() {
- if (mSurfaceController != null) {
- return mSurfaceController.getShown();
- }
- return false;
+ return mSurfaceControl != null && mSurfaceShown;
}
void destroySurface(SurfaceControl.Transaction t) {
- try {
- if (mSurfaceController != null) {
- mSurfaceController.destroy(t);
- }
- } catch (RuntimeException e) {
- Slog.w(TAG, "Exception thrown when destroying surface " + this
- + " surface " + mSurfaceController + " session " + mSession + ": " + e);
- } finally {
- mWin.setHasSurface(false);
- mSurfaceController = null;
- mDrawState = NO_SURFACE;
+ if (mSurfaceControl == null) {
+ return;
}
- }
-
- SurfaceControl getSurfaceControl() {
- if (!hasSurface()) {
- return null;
+ ProtoLog.i(WM_SHOW_SURFACE_ALLOC,
+ "Destroying surface %s called by %s", this, Debug.getCallers(8));
+ if (mWin.mIsWallpaper && !mWin.mWindowRemovalAllowed && !mWin.mRemoveOnExit) {
+ // The wallpaper surface should have the same lifetime as its window.
+ Slog.e(TAG, "Unexpected removing wallpaper surface of " + mWin
+ + " by " + Debug.getCallers(8));
}
- return mSurfaceController.mSurfaceControl;
+ t.remove(mSurfaceControl);
+ setShown(false);
+ mSurfaceControl = null;
+ mWin.setHasSurface(false);
+ mDrawState = NO_SURFACE;
}
}
diff --git a/services/core/java/com/android/server/wm/WindowSurfaceController.java b/services/core/java/com/android/server/wm/WindowSurfaceController.java
deleted file mode 100644
index d9766e0..0000000
--- a/services/core/java/com/android/server/wm/WindowSurfaceController.java
+++ /dev/null
@@ -1,237 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.server.wm;
-
-import static android.os.Trace.TRACE_TAG_WINDOW_MANAGER;
-import static android.view.SurfaceControl.METADATA_OWNER_PID;
-import static android.view.SurfaceControl.METADATA_OWNER_UID;
-import static android.view.SurfaceControl.METADATA_WINDOW_TYPE;
-
-import static com.android.internal.protolog.ProtoLogGroup.WM_SHOW_SURFACE_ALLOC;
-import static com.android.internal.protolog.ProtoLogGroup.WM_SHOW_TRANSACTIONS;
-import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_VISIBILITY;
-import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
-import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
-import static com.android.server.wm.WindowSurfaceControllerProto.SHOWN;
-
-import android.os.Debug;
-import android.os.Trace;
-import android.util.EventLog;
-import android.util.Slog;
-import android.util.proto.ProtoOutputStream;
-import android.view.SurfaceControl;
-import android.view.WindowContentFrameStats;
-
-import com.android.internal.protolog.ProtoLog;
-
-import java.io.PrintWriter;
-
-class WindowSurfaceController {
- static final String TAG = TAG_WITH_CLASS_NAME ? "WindowSurfaceController" : TAG_WM;
-
- final WindowStateAnimator mAnimator;
-
- SurfaceControl mSurfaceControl;
-
- // Should only be set from within setShown().
- private boolean mSurfaceShown = false;
-
- private final String title;
-
- private final WindowManagerService mService;
-
- private final int mWindowType;
- private final Session mWindowSession;
-
-
- WindowSurfaceController(String name, int format, int flags, WindowStateAnimator animator,
- int windowType) {
- mAnimator = animator;
-
- title = name;
-
- mService = animator.mService;
- final WindowState win = animator.mWin;
- mWindowType = windowType;
- mWindowSession = win.mSession;
-
- Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "new SurfaceControl");
- mSurfaceControl = win.makeSurface()
- .setParent(win.getSurfaceControl())
- .setName(name)
- .setFormat(format)
- .setFlags(flags)
- .setMetadata(METADATA_WINDOW_TYPE, windowType)
- .setMetadata(METADATA_OWNER_UID, mWindowSession.mUid)
- .setMetadata(METADATA_OWNER_PID, mWindowSession.mPid)
- .setCallsite("WindowSurfaceController")
- .setBLASTLayer().build();
-
- Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
- }
-
- void hide(SurfaceControl.Transaction transaction, String reason) {
- ProtoLog.i(WM_SHOW_TRANSACTIONS, "SURFACE HIDE ( %s ): %s", reason, title);
-
- if (mSurfaceShown) {
- hideSurface(transaction);
- }
- }
-
- private void hideSurface(SurfaceControl.Transaction transaction) {
- if (mSurfaceControl == null) {
- return;
- }
- setShown(false);
- try {
- transaction.hide(mSurfaceControl);
- if (mAnimator.mIsWallpaper) {
- final DisplayContent dc = mAnimator.mWin.getDisplayContent();
- EventLog.writeEvent(EventLogTags.WM_WALLPAPER_SURFACE,
- dc.mDisplayId, 0 /* request hidden */,
- String.valueOf(dc.mWallpaperController.getWallpaperTarget()));
- }
- } catch (RuntimeException e) {
- Slog.w(TAG, "Exception hiding surface in " + this);
- }
- }
-
- void destroy(SurfaceControl.Transaction t) {
- ProtoLog.i(WM_SHOW_SURFACE_ALLOC,
- "Destroying surface %s called by %s", this, Debug.getCallers(8));
- try {
- if (mSurfaceControl != null) {
- if (mAnimator.mIsWallpaper && !mAnimator.mWin.mWindowRemovalAllowed
- && !mAnimator.mWin.mRemoveOnExit) {
- // The wallpaper surface should have the same lifetime as its window.
- Slog.e(TAG, "Unexpected removing wallpaper surface of " + mAnimator.mWin
- + " by " + Debug.getCallers(8));
- }
- t.remove(mSurfaceControl);
- }
- } catch (RuntimeException e) {
- Slog.w(TAG, "Error destroying surface in: " + this, e);
- } finally {
- setShown(false);
- mSurfaceControl = null;
- }
- }
-
- boolean prepareToShowInTransaction(SurfaceControl.Transaction t, float alpha) {
- if (mSurfaceControl == null) {
- return false;
- }
-
- t.setAlpha(mSurfaceControl, alpha);
- return true;
- }
-
- void setOpaque(boolean isOpaque) {
- ProtoLog.i(WM_SHOW_TRANSACTIONS, "SURFACE isOpaque=%b: %s", isOpaque, title);
-
- if (mSurfaceControl == null) {
- return;
- }
-
- mAnimator.mWin.getPendingTransaction().setOpaque(mSurfaceControl, isOpaque);
- mService.scheduleAnimationLocked();
- }
-
- void setColorSpaceAgnostic(SurfaceControl.Transaction t, boolean agnostic) {
- ProtoLog.i(WM_SHOW_TRANSACTIONS, "SURFACE isColorSpaceAgnostic=%b: %s", agnostic, title);
-
- if (mSurfaceControl == null) {
- return;
- }
- t.setColorSpaceAgnostic(mSurfaceControl, agnostic);
- }
-
- void showRobustly(SurfaceControl.Transaction t) {
- ProtoLog.i(WM_SHOW_TRANSACTIONS, "SURFACE SHOW (performLayout): %s", title);
- if (DEBUG_VISIBILITY) Slog.v(TAG, "Showing " + this
- + " during relayout");
-
- if (mSurfaceShown) {
- return;
- }
-
- setShown(true);
- t.show(mSurfaceControl);
- if (mAnimator.mIsWallpaper) {
- final DisplayContent dc = mAnimator.mWin.getDisplayContent();
- EventLog.writeEvent(EventLogTags.WM_WALLPAPER_SURFACE,
- dc.mDisplayId, 1 /* request shown */,
- String.valueOf(dc.mWallpaperController.getWallpaperTarget()));
- }
- }
-
- boolean clearWindowContentFrameStats() {
- if (mSurfaceControl == null) {
- return false;
- }
- return mSurfaceControl.clearContentFrameStats();
- }
-
- boolean getWindowContentFrameStats(WindowContentFrameStats outStats) {
- if (mSurfaceControl == null) {
- return false;
- }
- return mSurfaceControl.getContentFrameStats(outStats);
- }
-
- boolean hasSurface() {
- return mSurfaceControl != null;
- }
-
- void getSurfaceControl(SurfaceControl outSurfaceControl) {
- outSurfaceControl.copyFrom(mSurfaceControl, "WindowSurfaceController.getSurfaceControl");
- }
-
- boolean getShown() {
- return mSurfaceShown;
- }
-
- void setShown(boolean surfaceShown) {
- mSurfaceShown = surfaceShown;
-
- mService.updateNonSystemOverlayWindowsVisibilityIfNeeded(mAnimator.mWin, surfaceShown);
-
- mAnimator.mWin.onSurfaceShownChanged(surfaceShown);
-
- if (mWindowSession != null) {
- mWindowSession.onWindowSurfaceVisibilityChanged(this, mSurfaceShown, mWindowType);
- }
- }
-
- void dumpDebug(ProtoOutputStream proto, long fieldId) {
- final long token = proto.start(fieldId);
- proto.write(SHOWN, mSurfaceShown);
- proto.end(token);
- }
-
- public void dump(PrintWriter pw, String prefix, boolean dumpAll) {
- if (dumpAll) {
- pw.print(prefix); pw.print("mSurface="); pw.println(mSurfaceControl);
- }
- pw.print(prefix); pw.print("Surface: shown="); pw.print(mSurfaceShown);
- }
-
- @Override
- public String toString() {
- return mSurfaceControl.toString();
- }
-}
diff --git a/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/InputMethodManagerServiceTestBase.java b/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/InputMethodManagerServiceTestBase.java
index bacde10..c2a069d 100644
--- a/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/InputMethodManagerServiceTestBase.java
+++ b/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/InputMethodManagerServiceTestBase.java
@@ -68,7 +68,6 @@
import com.android.internal.view.IInputMethodManager;
import com.android.server.LocalServices;
import com.android.server.ServiceThread;
-import com.android.server.SystemServerInitThreadPool;
import com.android.server.SystemService;
import com.android.server.input.InputManagerInternal;
import com.android.server.pm.UserManagerInternal;
@@ -161,7 +160,7 @@
.strictness(Strictness.LENIENT)
.spyStatic(InputMethodUtils.class)
.mockStatic(ServiceManager.class)
- .mockStatic(SystemServerInitThreadPool.class)
+ .spyStatic(AdditionalSubtypeMapRepository.class)
.startMocking();
mContext = spy(InstrumentationRegistry.getInstrumentation().getContext());
@@ -234,9 +233,8 @@
doNothing().when(() -> InputMethodUtils.setNonSelectedSystemImesDisabledUntilUsed(
any(PackageManager.class), anyList()));
- // Used by lazy initializing draw IMS nav bar at InputMethodManagerService#systemRunning(),
- // which is ok to be mocked out for now.
- doReturn(null).when(() -> SystemServerInitThreadPool.submit(any(), anyString()));
+ // The background writer thread in AdditionalSubtypeMapRepository should be stubbed out.
+ doNothing().when(AdditionalSubtypeMapRepository::startWriterThread);
mServiceThread =
new ServiceThread(
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 c1be5ca..63e3e5c 100644
--- a/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationControllerTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationControllerTest.java
@@ -714,7 +714,7 @@
// Simulate when the window is exiting and cleanupAnimation invoked
// (e.g. screen off during RecentsAnimation animating), will expect the window receives
// onExitAnimationDone to destroy the surface when the removal is allowed.
- win1.mWinAnimator.mSurfaceController = mock(WindowSurfaceController.class);
+ win1.mWinAnimator.mSurfaceControl = mock(SurfaceControl.class);
win1.mHasSurface = true;
win1.mAnimatingExit = true;
win1.mRemoveOnExit = true;
diff --git a/services/tests/wmtests/src/com/android/server/wm/RemoteAnimationControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/RemoteAnimationControllerTest.java
index 11d9629..0bf27d1 100644
--- a/services/tests/wmtests/src/com/android/server/wm/RemoteAnimationControllerTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/RemoteAnimationControllerTest.java
@@ -43,8 +43,6 @@
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.anyBoolean;
-import static org.mockito.ArgumentMatchers.anyFloat;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.mock;
@@ -758,12 +756,9 @@
// Simulating win1 has shown IME and being IME layering/input target
mDisplayContent.setImeLayeringTarget(win1);
mDisplayContent.setImeInputTarget(win1);
- mImeWindow.mWinAnimator.mSurfaceController = mock(WindowSurfaceController.class);
mImeWindow.mWinAnimator.hide(mDisplayContent.getPendingTransaction(), "test");
spyOn(mDisplayContent);
- doReturn(true).when(mImeWindow.mWinAnimator.mSurfaceController).hasSurface();
- doReturn(true).when(mImeWindow.mWinAnimator.mSurfaceController)
- .prepareToShowInTransaction(any(), anyFloat());
+ mImeWindow.mWinAnimator.mSurfaceControl = mock(SurfaceControl.class);
makeWindowVisibleAndDrawn(mImeWindow);
assertTrue(mImeWindow.isOnScreen());
assertFalse(mImeWindow.isParentWindowHidden());
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 9b48cb9..c65b76e 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WallpaperControllerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WallpaperControllerTests.java
@@ -31,7 +31,6 @@
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
-import static com.android.dx.mockito.inline.extended.ExtendedMockito.when;
import static com.android.window.flags.Flags.multiCrop;
import static com.google.common.truth.Truth.assertThat;
@@ -551,10 +550,9 @@
}
private static void makeWallpaperWindowShown(WindowState w) {
- final WindowSurfaceController windowSurfaceController = mock(WindowSurfaceController.class);
- w.mWinAnimator.mSurfaceController = windowSurfaceController;
w.mWinAnimator.mLastAlpha = 1;
- when(windowSurfaceController.getShown()).thenReturn(true);
+ spyOn(w.mWinAnimator);
+ doReturn(true).when(w.mWinAnimator).getShown();
}
private WindowState createWallpaperWindow(DisplayContent dc, int width, int height) {
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceTests.java
index 4958b90..89abe2f 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceTests.java
@@ -253,22 +253,18 @@
final Session session = createTestSession(mAtm, wpc.getPid(), wpc.mUid);
spyOn(session);
assertTrue(session.mCanAddInternalSystemWindow);
- final WindowSurfaceController winSurface = mock(WindowSurfaceController.class);
- session.onWindowSurfaceVisibilityChanged(winSurface, true /* visible */,
- LayoutParams.TYPE_PHONE);
+ final WindowState window = createWindow(null, LayoutParams.TYPE_PHONE, "win");
+ session.onWindowSurfaceVisibilityChanged(window, true /* visible */);
verify(session).setHasOverlayUi(true);
- session.onWindowSurfaceVisibilityChanged(winSurface, false /* visible */,
- LayoutParams.TYPE_PHONE);
+ session.onWindowSurfaceVisibilityChanged(window, false /* visible */);
verify(session).setHasOverlayUi(false);
}
@Test
public void testRelayoutExitingWindow() {
final WindowState win = createWindow(null, TYPE_BASE_APPLICATION, "appWin");
- final WindowSurfaceController surfaceController = mock(WindowSurfaceController.class);
- win.mWinAnimator.mSurfaceController = surfaceController;
win.mWinAnimator.mDrawState = WindowStateAnimator.HAS_DRAWN;
- doReturn(true).when(surfaceController).hasSurface();
+ win.mWinAnimator.mSurfaceControl = mock(SurfaceControl.class);
spyOn(win.mTransitionController);
doReturn(true).when(win.mTransitionController).isShellTransitionsEnabled();
doReturn(true).when(win.mTransitionController).inTransition(
@@ -306,7 +302,7 @@
// and WMS#tryStartExitingAnimation() will destroy the surface directly.
assertFalse(win.mAnimatingExit);
assertFalse(win.mHasSurface);
- assertNull(win.mWinAnimator.mSurfaceController);
+ assertNull(win.mWinAnimator.mSurfaceControl);
// Invisible requested activity should not get the last config even if its view is visible.
mWm.relayoutWindow(win.mSession, win.mClient, win.mAttrs, w, h, View.VISIBLE, 0, 0, 0,