Snap for 12078078 from 01b577432a to 24Q4-release
Change-Id: I7dcaf5c208a4e559f35f4d6f2889afed445a7e08
This commit is contained in:
+6
-2
@@ -190,7 +190,7 @@ android_app {
|
||||
],
|
||||
|
||||
optimize: {
|
||||
proguard_flags_files: ["proguard.flags"],
|
||||
proguard_flags_files: [":launcher-proguard-rules"],
|
||||
// Proguard is disable for testing. Derivarive prjects to keep proguard enabled
|
||||
enabled: false,
|
||||
},
|
||||
@@ -302,7 +302,9 @@ android_app {
|
||||
|
||||
static_libs: ["Launcher3QuickStepLib"],
|
||||
optimize: {
|
||||
enabled: false,
|
||||
proguard_flags_files: [":launcher-proguard-rules"],
|
||||
enabled: true,
|
||||
shrink_resources: true,
|
||||
},
|
||||
|
||||
platform_apis: true,
|
||||
@@ -349,6 +351,7 @@ android_app {
|
||||
optimize: {
|
||||
proguard_flags_files: ["proguard.flags"],
|
||||
enabled: true,
|
||||
shrink_resources: true,
|
||||
},
|
||||
|
||||
privileged: true,
|
||||
@@ -385,6 +388,7 @@ android_app {
|
||||
optimize: {
|
||||
proguard_flags_files: ["proguard.flags"],
|
||||
enabled: true,
|
||||
shrink_resources: true,
|
||||
},
|
||||
|
||||
privileged: true,
|
||||
|
||||
@@ -28,7 +28,7 @@
|
||||
launcher:focusBorderColor="?androidprv:attr/materialColorOutline"
|
||||
launcher:hoverBorderColor="?androidprv:attr/materialColorPrimary">
|
||||
|
||||
<include layout="@layout/task_thumbnail" />
|
||||
<include layout="@layout/task_thumbnail_deprecated" />
|
||||
|
||||
<!-- Filtering affects only alpha instead of the visibility since visibility can be altered
|
||||
separately through RecentsView#resetFromSplitSelectionState() -->
|
||||
|
||||
@@ -33,9 +33,9 @@
|
||||
launcher:focusBorderColor="?androidprv:attr/materialColorOutline"
|
||||
launcher:hoverBorderColor="?androidprv:attr/materialColorPrimary">
|
||||
|
||||
<include layout="@layout/task_thumbnail"/>
|
||||
<include layout="@layout/task_thumbnail_deprecated"/>
|
||||
|
||||
<include layout="@layout/task_thumbnail"
|
||||
<include layout="@layout/task_thumbnail_deprecated"
|
||||
android:id="@+id/bottomright_snapshot" />
|
||||
|
||||
<!-- Filtering affects only alpha instead of the visibility since visibility can be altered
|
||||
|
||||
@@ -13,8 +13,29 @@
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
-->
|
||||
<com.android.quickstep.views.TaskThumbnailViewDeprecated
|
||||
<com.android.quickstep.task.thumbnail.TaskThumbnailView
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:id="@+id/snapshot"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent" />
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/task_thumbnail"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:importantForAccessibility="no"
|
||||
android:visibility="gone"/>
|
||||
|
||||
<com.android.quickstep.task.thumbnail.LiveTileView
|
||||
android:id="@+id/task_thumbnail_live_tile"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:visibility="gone"/>
|
||||
|
||||
<View
|
||||
android:id="@+id/task_thumbnail_scrim"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:background="@color/overview_foreground_scrim_color"
|
||||
android:alpha="0" />
|
||||
|
||||
</com.android.quickstep.task.thumbnail.TaskThumbnailView>
|
||||
@@ -0,0 +1,20 @@
|
||||
<?xml version="1.0" encoding="utf-8"?><!--
|
||||
Copyright (C) 2024 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.
|
||||
-->
|
||||
<com.android.quickstep.views.TaskThumbnailViewDeprecated
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:id="@+id/snapshot"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent" />
|
||||
@@ -20,6 +20,7 @@ import static android.content.ClipDescription.MIMETYPE_TEXT_INTENT;
|
||||
import static android.view.WindowInsets.Type.navigationBars;
|
||||
import static android.view.WindowInsets.Type.statusBars;
|
||||
|
||||
import static com.android.launcher3.Flags.enablePredictiveBackGesture;
|
||||
import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
|
||||
import static com.android.launcher3.util.Executors.MODEL_EXECUTOR;
|
||||
|
||||
@@ -33,6 +34,9 @@ import android.util.Log;
|
||||
import android.view.View;
|
||||
import android.view.WindowInsetsController;
|
||||
import android.view.WindowManager;
|
||||
import android.window.BackEvent;
|
||||
import android.window.OnBackAnimationCallback;
|
||||
import android.window.OnBackInvokedDispatcher;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
@@ -124,6 +128,8 @@ public class WidgetPickerActivity extends BaseActivity {
|
||||
/** A set of user ids that should be filtered out from the selected widgets. */
|
||||
@NonNull
|
||||
Set<Integer> mFilteredUserIds = new HashSet<>();
|
||||
@Nullable
|
||||
private WidgetsFullSheet mWidgetSheet;
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
@@ -148,6 +154,18 @@ public class WidgetPickerActivity extends BaseActivity {
|
||||
refreshAndBindWidgets();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void registerBackDispatcher() {
|
||||
if (!enablePredictiveBackGesture()) {
|
||||
super.registerBackDispatcher();
|
||||
return;
|
||||
}
|
||||
|
||||
getOnBackInvokedDispatcher().registerOnBackInvokedCallback(
|
||||
OnBackInvokedDispatcher.PRIORITY_DEFAULT,
|
||||
new BackAnimationCallback());
|
||||
}
|
||||
|
||||
private void parseIntentExtras() {
|
||||
mTitle = getIntent().getStringExtra(EXTRA_PICKER_TITLE);
|
||||
mDescription = getIntent().getStringExtra(EXTRA_PICKER_DESCRIPTION);
|
||||
@@ -293,12 +311,12 @@ public class WidgetPickerActivity extends BaseActivity {
|
||||
MAIN_EXECUTOR.execute(() -> mPopupDataProvider.setAllWidgets(widgets));
|
||||
}
|
||||
|
||||
private void openWidgetsSheet() {
|
||||
private void openWidgetsSheet() {
|
||||
MAIN_EXECUTOR.execute(() -> {
|
||||
WidgetsFullSheet widgetSheet = WidgetsFullSheet.show(this, true);
|
||||
widgetSheet.mayUpdateTitleAndDescription(mTitle, mDescription);
|
||||
widgetSheet.disableNavBarScrim(true);
|
||||
widgetSheet.addOnCloseListener(this::finish);
|
||||
mWidgetSheet = WidgetsFullSheet.show(this, true);
|
||||
mWidgetSheet.mayUpdateTitleAndDescription(mTitle, mDescription);
|
||||
mWidgetSheet.disableNavBarScrim(true);
|
||||
mWidgetSheet.addOnCloseListener(this::finish);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -317,6 +335,51 @@ public class WidgetPickerActivity extends BaseActivity {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Animation callback for different predictive back animation states for the widget picker.
|
||||
*/
|
||||
private class BackAnimationCallback implements OnBackAnimationCallback {
|
||||
@Nullable
|
||||
OnBackAnimationCallback mActiveOnBackAnimationCallback;
|
||||
|
||||
@Override
|
||||
public void onBackStarted(@NonNull BackEvent backEvent) {
|
||||
if (mActiveOnBackAnimationCallback != null) {
|
||||
mActiveOnBackAnimationCallback.onBackCancelled();
|
||||
}
|
||||
if (mWidgetSheet != null) {
|
||||
mActiveOnBackAnimationCallback = mWidgetSheet;
|
||||
mActiveOnBackAnimationCallback.onBackStarted(backEvent);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBackInvoked() {
|
||||
if (mActiveOnBackAnimationCallback == null) {
|
||||
return;
|
||||
}
|
||||
mActiveOnBackAnimationCallback.onBackInvoked();
|
||||
mActiveOnBackAnimationCallback = null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBackProgressed(@NonNull BackEvent backEvent) {
|
||||
if (mActiveOnBackAnimationCallback == null) {
|
||||
return;
|
||||
}
|
||||
mActiveOnBackAnimationCallback.onBackProgressed(backEvent);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBackCancelled() {
|
||||
if (mActiveOnBackAnimationCallback == null) {
|
||||
return;
|
||||
}
|
||||
mActiveOnBackAnimationCallback.onBackCancelled();
|
||||
mActiveOnBackAnimationCallback = null;
|
||||
}
|
||||
};
|
||||
|
||||
private WidgetAcceptabilityVerdict isWidgetAcceptable(WidgetItem widget) {
|
||||
final AppWidgetProviderInfo info = widget.widgetInfo;
|
||||
if (info == null) {
|
||||
|
||||
@@ -42,6 +42,7 @@ import com.android.launcher3.statehandlers.DesktopVisibilityController;
|
||||
import com.android.launcher3.statemanager.BaseState;
|
||||
import com.android.launcher3.taskbar.TaskbarUIController;
|
||||
import com.android.launcher3.util.DisplayController;
|
||||
import com.android.launcher3.util.WindowBounds;
|
||||
import com.android.launcher3.views.ScrimView;
|
||||
import com.android.quickstep.orientation.RecentsPagedOrientationHandler;
|
||||
import com.android.quickstep.util.ActivityInitListener;
|
||||
@@ -51,6 +52,7 @@ import com.android.quickstep.views.RecentsViewContainer;
|
||||
import com.android.systemui.shared.recents.model.ThumbnailData;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
@@ -269,8 +271,11 @@ public abstract class BaseContainerInterface<STATE_TYPE extends BaseState<STATE_
|
||||
} else {
|
||||
Rect portraitInsets = dp.getInsets();
|
||||
DisplayController displayController = DisplayController.INSTANCE.get(context);
|
||||
Rect deviceRotationInsets = displayController.getInfo().getCurrentBounds().get(
|
||||
orientationHandler.getRotation()).insets;
|
||||
@Nullable List<WindowBounds> windowBounds =
|
||||
displayController.getInfo().getCurrentBounds();
|
||||
Rect deviceRotationInsets = windowBounds != null
|
||||
? windowBounds.get(orientationHandler.getRotation()).insets
|
||||
: new Rect();
|
||||
// Obtain the landscape/seascape insets, and rotate it to portrait perspective.
|
||||
orientationHandler.rotateInsets(deviceRotationInsets, outRect);
|
||||
// Then combine with portrait's insets to leave space for status bar/nav bar in
|
||||
|
||||
@@ -33,6 +33,7 @@ import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_A
|
||||
import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_BUBBLES_EXPANDED;
|
||||
import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_DEVICE_DREAMING;
|
||||
import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_DIALOG_SHOWING;
|
||||
import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_DISABLE_GESTURE_PIP_ANIMATING;
|
||||
import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_DISABLE_GESTURE_SPLIT_INVOCATION;
|
||||
import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_HOME_DISABLED;
|
||||
import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_IME_SHOWING;
|
||||
@@ -416,7 +417,8 @@ public class RecentsAnimationDeviceState implements DisplayInfoChangeListener, E
|
||||
| SYSUI_STATE_QUICK_SETTINGS_EXPANDED
|
||||
| SYSUI_STATE_MAGNIFICATION_OVERLAP
|
||||
| SYSUI_STATE_DEVICE_DREAMING
|
||||
| SYSUI_STATE_DISABLE_GESTURE_SPLIT_INVOCATION;
|
||||
| SYSUI_STATE_DISABLE_GESTURE_SPLIT_INVOCATION
|
||||
| SYSUI_STATE_DISABLE_GESTURE_PIP_ANIMATING;
|
||||
return (gestureDisablingStates & mSystemUiStateFlags) == 0 && homeOrOverviewEnabled;
|
||||
}
|
||||
|
||||
|
||||
@@ -33,6 +33,7 @@ import android.os.UserHandle;
|
||||
import android.text.TextUtils;
|
||||
import android.util.SparseArray;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.WorkerThread;
|
||||
|
||||
import com.android.launcher3.R;
|
||||
@@ -48,6 +49,7 @@ import com.android.launcher3.util.DisplayController.DisplayInfoChangeListener;
|
||||
import com.android.launcher3.util.DisplayController.Info;
|
||||
import com.android.launcher3.util.FlagOp;
|
||||
import com.android.launcher3.util.Preconditions;
|
||||
import com.android.quickstep.task.thumbnail.data.TaskIconDataSource;
|
||||
import com.android.quickstep.util.TaskKeyLruCache;
|
||||
import com.android.quickstep.util.TaskVisualsChangeListener;
|
||||
import com.android.systemui.shared.recents.model.Task;
|
||||
@@ -59,7 +61,7 @@ import java.util.concurrent.Executor;
|
||||
/**
|
||||
* Manages the caching of task icons and related data.
|
||||
*/
|
||||
public class TaskIconCache implements DisplayInfoChangeListener {
|
||||
public class TaskIconCache implements TaskIconDataSource, DisplayInfoChangeListener {
|
||||
|
||||
private final Executor mBgExecutor;
|
||||
|
||||
@@ -102,7 +104,8 @@ public class TaskIconCache implements DisplayInfoChangeListener {
|
||||
* @param callback The callback to receive the task after its data has been populated.
|
||||
* @return A cancelable handle to the request
|
||||
*/
|
||||
public CancellableTask getIconInBackground(Task task, GetTaskIconCallback callback) {
|
||||
@Override
|
||||
public CancellableTask getIconInBackground(Task task, @NonNull GetTaskIconCallback callback) {
|
||||
Preconditions.assertUIThread();
|
||||
if (task.icon != null) {
|
||||
// Nothing to load, the icon is already loaded
|
||||
|
||||
@@ -16,7 +16,8 @@
|
||||
|
||||
package com.android.quickstep.recents.data
|
||||
|
||||
import com.android.quickstep.TaskIconCache
|
||||
import android.graphics.drawable.Drawable
|
||||
import com.android.quickstep.task.thumbnail.data.TaskIconDataSource
|
||||
import com.android.quickstep.task.thumbnail.data.TaskThumbnailDataSource
|
||||
import com.android.quickstep.util.GroupTask
|
||||
import com.android.systemui.shared.recents.model.Task
|
||||
@@ -38,7 +39,7 @@ import kotlinx.coroutines.suspendCancellableCoroutine
|
||||
class TasksRepository(
|
||||
private val recentsModel: RecentTasksDataSource,
|
||||
private val taskThumbnailDataSource: TaskThumbnailDataSource,
|
||||
private val taskIconCache: TaskIconCache,
|
||||
private val taskIconDataSource: TaskIconDataSource,
|
||||
) : RecentTasksRepository {
|
||||
private val groupedTaskData = MutableStateFlow(emptyList<GroupTask>())
|
||||
private val _taskData =
|
||||
@@ -46,10 +47,19 @@ class TasksRepository(
|
||||
private val visibleTaskIds = MutableStateFlow(emptySet<Int>())
|
||||
|
||||
private val taskData: Flow<List<Task>> =
|
||||
combine(_taskData, getThumbnailQueryResults()) { tasks, results ->
|
||||
combine(_taskData, getThumbnailQueryResults(), getIconQueryResults()) {
|
||||
tasks,
|
||||
thumbnailQueryResults,
|
||||
iconQueryResults ->
|
||||
tasks.forEach { task ->
|
||||
// Add retrieved thumbnails + remove unnecessary thumbnails
|
||||
task.thumbnail = results[task.key.id]
|
||||
task.thumbnail = thumbnailQueryResults[task.key.id]
|
||||
|
||||
// TODO(b/352331675) don't load icons for DesktopTaskView
|
||||
// Add retrieved icons + remove unnecessary icons
|
||||
task.icon = iconQueryResults[task.key.id]?.icon
|
||||
task.titleDescription = iconQueryResults[task.key.id]?.contentDescription
|
||||
task.title = iconQueryResults[task.key.id]?.title
|
||||
}
|
||||
tasks
|
||||
}
|
||||
@@ -79,7 +89,6 @@ class TasksRepository(
|
||||
suspendCancellableCoroutine { continuation ->
|
||||
val cancellableTask =
|
||||
taskThumbnailDataSource.getThumbnailInBackground(task) {
|
||||
task.thumbnail = it
|
||||
continuation.resume(it)
|
||||
}
|
||||
continuation.invokeOnCancellation { cancellableTask?.cancel() }
|
||||
@@ -109,6 +118,59 @@ class TasksRepository(
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** Flow wrapper for [TaskThumbnailDataSource.getThumbnailInBackground] api */
|
||||
private fun getIconDataRequest(task: Task): IconDataRequest =
|
||||
flow {
|
||||
emit(task.key.id to task.getTaskIconQueryResponse())
|
||||
val iconDataResponse: TaskIconQueryResponse? =
|
||||
suspendCancellableCoroutine { continuation ->
|
||||
val cancellableTask =
|
||||
taskIconDataSource.getIconInBackground(task) {
|
||||
icon,
|
||||
contentDescription,
|
||||
title ->
|
||||
continuation.resume(
|
||||
TaskIconQueryResponse(icon, contentDescription, title)
|
||||
)
|
||||
}
|
||||
continuation.invokeOnCancellation { cancellableTask?.cancel() }
|
||||
}
|
||||
emit(task.key.id to iconDataResponse)
|
||||
}
|
||||
.distinctUntilChanged()
|
||||
|
||||
private fun getIconQueryResults(): Flow<Map<Int, TaskIconQueryResponse?>> {
|
||||
val visibleTasks =
|
||||
combine(_taskData, visibleTaskIds) { tasks, visibleIds ->
|
||||
tasks.filter { it.key.id in visibleIds }
|
||||
}
|
||||
val visibleIconDataRequests: Flow<List<IconDataRequest>> =
|
||||
visibleTasks.map { visibleTasksList -> visibleTasksList.map(::getIconDataRequest) }
|
||||
return visibleIconDataRequests.flatMapLatest { iconRequestFlows: List<IconDataRequest> ->
|
||||
if (iconRequestFlows.isEmpty()) {
|
||||
flowOf(emptyMap())
|
||||
} else {
|
||||
combine(iconRequestFlows) { it.toMap() }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
typealias ThumbnailDataRequest = Flow<Pair<Int, ThumbnailData?>>
|
||||
private data class TaskIconQueryResponse(
|
||||
val icon: Drawable,
|
||||
val contentDescription: String,
|
||||
val title: String
|
||||
)
|
||||
|
||||
private fun Task.getTaskIconQueryResponse(): TaskIconQueryResponse? {
|
||||
val iconVal = icon ?: return null
|
||||
val titleDescriptionVal = titleDescription ?: return null
|
||||
val titleVal = title ?: return null
|
||||
|
||||
return TaskIconQueryResponse(iconVal, titleDescriptionVal, titleVal)
|
||||
}
|
||||
|
||||
private typealias ThumbnailDataRequest = Flow<Pair<Int, ThumbnailData?>>
|
||||
|
||||
private typealias IconDataRequest = Flow<Pair<Int, TaskIconQueryResponse?>>
|
||||
|
||||
@@ -0,0 +1,46 @@
|
||||
/*
|
||||
* Copyright (C) 2024 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.quickstep.task.thumbnail
|
||||
|
||||
import android.content.Context
|
||||
import android.graphics.Canvas
|
||||
import android.graphics.Paint
|
||||
import android.graphics.PorterDuff
|
||||
import android.graphics.PorterDuffXfermode
|
||||
import android.util.AttributeSet
|
||||
import android.view.View
|
||||
|
||||
class LiveTileView : View {
|
||||
constructor(context: Context) : super(context)
|
||||
|
||||
constructor(context: Context, attrs: AttributeSet?) : super(context, attrs)
|
||||
|
||||
constructor(
|
||||
context: Context,
|
||||
attrs: AttributeSet?,
|
||||
defStyleAttr: Int,
|
||||
) : super(context, attrs, defStyleAttr)
|
||||
|
||||
override fun onDraw(canvas: Canvas) {
|
||||
canvas.drawRect(0f, 0f, measuredWidth.toFloat(), measuredHeight.toFloat(), CLEAR_PAINT)
|
||||
}
|
||||
|
||||
companion object {
|
||||
private val CLEAR_PAINT =
|
||||
Paint().apply { xfermode = PorterDuffXfermode(PorterDuff.Mode.CLEAR) }
|
||||
}
|
||||
}
|
||||
@@ -18,17 +18,17 @@ package com.android.quickstep.task.thumbnail
|
||||
|
||||
import android.content.Context
|
||||
import android.content.res.Configuration
|
||||
import android.graphics.Canvas
|
||||
import android.graphics.Color
|
||||
import android.graphics.Outline
|
||||
import android.graphics.Paint
|
||||
import android.graphics.PorterDuff
|
||||
import android.graphics.PorterDuffXfermode
|
||||
import android.graphics.Rect
|
||||
import android.util.AttributeSet
|
||||
import android.view.View
|
||||
import android.view.ViewOutlineProvider
|
||||
import android.widget.FrameLayout
|
||||
import android.widget.ImageView
|
||||
import androidx.annotation.ColorInt
|
||||
import androidx.core.view.isVisible
|
||||
import com.android.launcher3.R
|
||||
import com.android.launcher3.Utilities
|
||||
import com.android.launcher3.util.ViewPool
|
||||
import com.android.quickstep.task.thumbnail.TaskThumbnailUiState.BackgroundOnly
|
||||
@@ -43,7 +43,7 @@ import com.android.systemui.shared.system.QuickStepContract
|
||||
import kotlinx.coroutines.MainScope
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
class TaskThumbnailView : View, ViewPool.Reusable {
|
||||
class TaskThumbnailView : FrameLayout, ViewPool.Reusable {
|
||||
// TODO(b/335649589): Ideally create and obtain this from DI. This ViewModel should be scoped
|
||||
// to [TaskView], and also shared between [TaskView] and [TaskThumbnailView]
|
||||
// This is using a lazy for now because the dependencies cannot be obtained without DI.
|
||||
@@ -59,12 +59,12 @@ class TaskThumbnailView : View, ViewPool.Reusable {
|
||||
)
|
||||
}
|
||||
|
||||
private var uiState: TaskThumbnailUiState = Uninitialized
|
||||
private var inheritedScale: Float = 1f
|
||||
private var dimProgress: Float = 0f
|
||||
private val scrimView: View by lazy { findViewById(R.id.task_thumbnail_scrim) }
|
||||
private val liveTileView: LiveTileView by lazy { findViewById(R.id.task_thumbnail_live_tile) }
|
||||
private val thumbnail: ImageView by lazy { findViewById(R.id.task_thumbnail) }
|
||||
|
||||
private var inheritedScale: Float = 1f
|
||||
|
||||
private val backgroundPaint = Paint(Paint.ANTI_ALIAS_FLAG)
|
||||
private val scrimPaint = Paint().apply { color = Color.BLACK }
|
||||
private val _measuredBounds = Rect()
|
||||
private val measuredBounds: Rect
|
||||
get() {
|
||||
@@ -75,12 +75,12 @@ class TaskThumbnailView : View, ViewPool.Reusable {
|
||||
private var overviewCornerRadius: Float = TaskCornerRadius.get(context)
|
||||
private var fullscreenCornerRadius: Float = QuickStepContract.getWindowCornerRadius(context)
|
||||
|
||||
constructor(context: Context?) : super(context)
|
||||
constructor(context: Context) : super(context)
|
||||
|
||||
constructor(context: Context?, attrs: AttributeSet?) : super(context, attrs)
|
||||
constructor(context: Context, attrs: AttributeSet?) : super(context, attrs)
|
||||
|
||||
constructor(
|
||||
context: Context?,
|
||||
context: Context,
|
||||
attrs: AttributeSet?,
|
||||
defStyleAttr: Int,
|
||||
) : super(context, attrs, defStyleAttr)
|
||||
@@ -90,15 +90,19 @@ class TaskThumbnailView : View, ViewPool.Reusable {
|
||||
// TODO(b/335396935) replace MainScope with shorter lifecycle.
|
||||
MainScope().launch {
|
||||
viewModel.uiState.collect { viewModelUiState ->
|
||||
uiState = viewModelUiState
|
||||
invalidate()
|
||||
resetViews()
|
||||
when (viewModelUiState) {
|
||||
is Uninitialized -> {}
|
||||
is LiveTile -> drawLiveWindow()
|
||||
is Snapshot -> drawSnapshot(viewModelUiState)
|
||||
is BackgroundOnly -> drawBackground(viewModelUiState.backgroundColor)
|
||||
}
|
||||
}
|
||||
}
|
||||
MainScope().launch {
|
||||
viewModel.dimProgress.collect { dimProgress ->
|
||||
// TODO(b/348195366) Add fade in/out for scrim
|
||||
this@TaskThumbnailView.dimProgress = dimProgress
|
||||
invalidate()
|
||||
scrimView.alpha = dimProgress * MAX_SCRIM_ALPHA
|
||||
}
|
||||
}
|
||||
MainScope().launch { viewModel.cornerRadiusProgress.collect { invalidateOutline() } }
|
||||
@@ -120,25 +124,6 @@ class TaskThumbnailView : View, ViewPool.Reusable {
|
||||
|
||||
override fun onRecycle() {
|
||||
// Do nothing
|
||||
uiState = Uninitialized
|
||||
}
|
||||
|
||||
override fun onDraw(canvas: Canvas) {
|
||||
when (val uiStateVal = uiState) {
|
||||
is Uninitialized -> drawBackgroundOnly(canvas, Color.BLACK)
|
||||
is LiveTile -> drawTransparentUiState(canvas)
|
||||
is Snapshot -> drawSnapshotState(canvas, uiStateVal)
|
||||
is BackgroundOnly -> drawBackgroundOnly(canvas, uiStateVal.backgroundColor)
|
||||
}
|
||||
|
||||
if (dimProgress > 0) {
|
||||
drawScrim(canvas)
|
||||
}
|
||||
}
|
||||
|
||||
private fun drawBackgroundOnly(canvas: Canvas, @ColorInt backgroundColor: Int) {
|
||||
backgroundPaint.color = backgroundColor
|
||||
canvas.drawRect(measuredBounds, backgroundPaint)
|
||||
}
|
||||
|
||||
override fun onConfigurationChanged(newConfig: Configuration?) {
|
||||
@@ -149,18 +134,25 @@ class TaskThumbnailView : View, ViewPool.Reusable {
|
||||
invalidateOutline()
|
||||
}
|
||||
|
||||
private fun drawTransparentUiState(canvas: Canvas) {
|
||||
canvas.drawRect(measuredBounds, CLEAR_PAINT)
|
||||
private fun resetViews() {
|
||||
liveTileView.isVisible = false
|
||||
thumbnail.isVisible = false
|
||||
scrimView.alpha = 0f
|
||||
setBackgroundColor(Color.BLACK)
|
||||
}
|
||||
|
||||
private fun drawSnapshotState(canvas: Canvas, snapshot: Snapshot) {
|
||||
drawBackgroundOnly(canvas, snapshot.backgroundColor)
|
||||
canvas.drawBitmap(snapshot.bitmap, snapshot.drawnRect, measuredBounds, null)
|
||||
private fun drawBackground(@ColorInt background: Int) {
|
||||
setBackgroundColor(background)
|
||||
}
|
||||
|
||||
private fun drawScrim(canvas: Canvas) {
|
||||
scrimPaint.alpha = (dimProgress * MAX_SCRIM_ALPHA).toInt()
|
||||
canvas.drawRect(measuredBounds, scrimPaint)
|
||||
private fun drawLiveWindow() {
|
||||
liveTileView.isVisible = true
|
||||
}
|
||||
|
||||
private fun drawSnapshot(snapshot: Snapshot) {
|
||||
drawBackground(snapshot.backgroundColor)
|
||||
thumbnail.setImageBitmap(snapshot.bitmap)
|
||||
thumbnail.isVisible = true
|
||||
}
|
||||
|
||||
private fun getCurrentCornerRadius() =
|
||||
@@ -170,9 +162,7 @@ class TaskThumbnailView : View, ViewPool.Reusable {
|
||||
fullscreenCornerRadius
|
||||
) / inheritedScale
|
||||
|
||||
companion object {
|
||||
private val CLEAR_PAINT =
|
||||
Paint().apply { xfermode = PorterDuffXfermode(PorterDuff.Mode.CLEAR) }
|
||||
private const val MAX_SCRIM_ALPHA = (0.4f * 255).toInt()
|
||||
private companion object {
|
||||
const val MAX_SCRIM_ALPHA = 0.4f
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
/*
|
||||
* Copyright (C) 2024 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.quickstep.task.thumbnail.data
|
||||
|
||||
import com.android.launcher3.util.CancellableTask
|
||||
import com.android.quickstep.TaskIconCache.GetTaskIconCallback
|
||||
import com.android.systemui.shared.recents.model.Task
|
||||
|
||||
interface TaskIconDataSource {
|
||||
fun getIconInBackground(task: Task, callback: GetTaskIconCallback): CancellableTask<*>?
|
||||
}
|
||||
@@ -24,6 +24,7 @@ import android.graphics.drawable.ShapeDrawable
|
||||
import android.graphics.drawable.shapes.RoundRectShape
|
||||
import android.util.AttributeSet
|
||||
import android.util.Log
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.core.view.updateLayoutParams
|
||||
@@ -36,7 +37,6 @@ import com.android.launcher3.util.ViewPool
|
||||
import com.android.launcher3.util.rects.set
|
||||
import com.android.quickstep.BaseContainerInterface
|
||||
import com.android.quickstep.TaskOverlayFactory
|
||||
import com.android.quickstep.task.thumbnail.TaskThumbnailView
|
||||
import com.android.quickstep.util.RecentsOrientedState
|
||||
import com.android.systemui.shared.recents.model.Task
|
||||
|
||||
@@ -54,7 +54,7 @@ class DesktopTaskView @JvmOverloads constructor(context: Context, attrs: Attribu
|
||||
ViewPool<TaskThumbnailViewDeprecated>(
|
||||
context,
|
||||
this,
|
||||
R.layout.task_thumbnail,
|
||||
R.layout.task_thumbnail_deprecated,
|
||||
VIEW_POOL_MAX_SIZE,
|
||||
VIEW_POOL_INITIAL_SIZE
|
||||
)
|
||||
@@ -108,22 +108,21 @@ class DesktopTaskView @JvmOverloads constructor(context: Context, attrs: Attribu
|
||||
tasks.map { task ->
|
||||
val snapshotView =
|
||||
if (enableRefactorTaskThumbnail()) {
|
||||
TaskThumbnailView(context)
|
||||
} else {
|
||||
taskThumbnailViewDeprecatedPool.view
|
||||
}
|
||||
.also { snapshotView ->
|
||||
addView(
|
||||
snapshotView,
|
||||
// Add snapshotView to the front after initial views e.g. icon and
|
||||
// background.
|
||||
childCountAtInflation,
|
||||
LayoutParams(
|
||||
ViewGroup.LayoutParams.WRAP_CONTENT,
|
||||
ViewGroup.LayoutParams.WRAP_CONTENT
|
||||
)
|
||||
)
|
||||
}
|
||||
LayoutInflater.from(context).inflate(R.layout.task_thumbnail, this, false)
|
||||
} else {
|
||||
taskThumbnailViewDeprecatedPool.view
|
||||
}
|
||||
|
||||
addView(
|
||||
snapshotView,
|
||||
// Add snapshotView to the front after initial views e.g. icon and
|
||||
// background.
|
||||
childCountAtInflation,
|
||||
LayoutParams(
|
||||
ViewGroup.LayoutParams.WRAP_CONTENT,
|
||||
ViewGroup.LayoutParams.WRAP_CONTENT
|
||||
)
|
||||
)
|
||||
TaskContainer(
|
||||
this,
|
||||
task,
|
||||
|
||||
@@ -31,6 +31,7 @@ import android.util.AttributeSet
|
||||
import android.util.FloatProperty
|
||||
import android.util.Log
|
||||
import android.view.Display
|
||||
import android.view.LayoutInflater
|
||||
import android.view.MotionEvent
|
||||
import android.view.View
|
||||
import android.view.View.OnClickListener
|
||||
@@ -666,9 +667,8 @@ constructor(
|
||||
if (enableRefactorTaskThumbnail()) {
|
||||
thumbnailViewDeprecated.visibility = GONE
|
||||
val indexOfSnapshotView = indexOfChild(thumbnailViewDeprecated)
|
||||
TaskThumbnailView(context).apply {
|
||||
layoutParams = thumbnailViewDeprecated.layoutParams
|
||||
addView(this, indexOfSnapshotView)
|
||||
LayoutInflater.from(context).inflate(R.layout.task_thumbnail, this, false).also {
|
||||
addView(it, indexOfSnapshotView, thumbnailViewDeprecated.layoutParams)
|
||||
}
|
||||
} else {
|
||||
thumbnailViewDeprecated
|
||||
|
||||
+58
@@ -0,0 +1,58 @@
|
||||
/*
|
||||
* Copyright (C) 2024 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.quickstep.recents.data
|
||||
|
||||
import android.graphics.drawable.Drawable
|
||||
import com.android.launcher3.util.CancellableTask
|
||||
import com.android.quickstep.TaskIconCache
|
||||
import com.android.quickstep.task.thumbnail.data.TaskIconDataSource
|
||||
import com.android.systemui.shared.recents.model.Task
|
||||
import com.google.common.truth.Truth.assertThat
|
||||
import org.mockito.kotlin.mock
|
||||
|
||||
class FakeTaskIconDataSource : TaskIconDataSource {
|
||||
|
||||
val taskIdToDrawable: Map<Int, Drawable> = (0..10).associateWith { mock() }
|
||||
val taskIdToUpdatingTask: MutableMap<Int, () -> Unit> = mutableMapOf()
|
||||
var shouldLoadSynchronously: Boolean = true
|
||||
|
||||
/** Retrieves and sets an icon on [task] from [taskIdToDrawable]. */
|
||||
override fun getIconInBackground(
|
||||
task: Task,
|
||||
callback: TaskIconCache.GetTaskIconCallback
|
||||
): CancellableTask<*>? {
|
||||
val wrappedCallback = {
|
||||
callback.onTaskIconReceived(
|
||||
taskIdToDrawable.getValue(task.key.id),
|
||||
"content desc ${task.key.id}",
|
||||
"title ${task.key.id}"
|
||||
)
|
||||
}
|
||||
if (shouldLoadSynchronously) {
|
||||
wrappedCallback()
|
||||
} else {
|
||||
taskIdToUpdatingTask[task.key.id] = wrappedCallback
|
||||
}
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
fun Task.assertHasIconDataFromSource(fakeTaskIconDataSource: FakeTaskIconDataSource) {
|
||||
assertThat(icon).isEqualTo(fakeTaskIconDataSource.taskIdToDrawable[key.id])
|
||||
assertThat(titleDescription).isEqualTo("content desc ${key.id}")
|
||||
assertThat(title).isEqualTo("title ${key.id}")
|
||||
}
|
||||
+49
-8
@@ -18,7 +18,6 @@ package com.android.quickstep.recents.data
|
||||
|
||||
import android.content.ComponentName
|
||||
import android.content.Intent
|
||||
import com.android.quickstep.TaskIconCache
|
||||
import com.android.quickstep.util.DesktopTask
|
||||
import com.android.quickstep.util.GroupTask
|
||||
import com.android.systemui.shared.recents.model.Task
|
||||
@@ -31,7 +30,6 @@ import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.test.UnconfinedTestDispatcher
|
||||
import kotlinx.coroutines.test.runTest
|
||||
import org.junit.Test
|
||||
import org.mockito.kotlin.mock
|
||||
|
||||
@OptIn(ExperimentalCoroutinesApi::class)
|
||||
class TasksRepositoryTest {
|
||||
@@ -44,10 +42,10 @@ class TasksRepositoryTest {
|
||||
)
|
||||
private val recentsModel = FakeRecentTasksDataSource()
|
||||
private val taskThumbnailDataSource = FakeTaskThumbnailDataSource()
|
||||
private val taskIconCache = mock<TaskIconCache>()
|
||||
private val taskIconDataSource = FakeTaskIconDataSource()
|
||||
|
||||
private val systemUnderTest =
|
||||
TasksRepository(recentsModel, taskThumbnailDataSource, taskIconCache)
|
||||
TasksRepository(recentsModel, taskThumbnailDataSource, taskIconDataSource)
|
||||
|
||||
@Test
|
||||
fun getAllTaskDataReturnsFlattenedListOfTasks() = runTest {
|
||||
@@ -80,6 +78,22 @@ class TasksRepositoryTest {
|
||||
.isEqualTo(bitmap2)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun setVisibleTasksPopulatesIcons() = runTest {
|
||||
recentsModel.seedTasks(defaultTaskList)
|
||||
systemUnderTest.getAllTaskData(forceRefresh = true)
|
||||
|
||||
systemUnderTest.setVisibleTasks(listOf(1, 2))
|
||||
|
||||
// .drop(1) to ignore initial null content before from thumbnail was loaded.
|
||||
systemUnderTest
|
||||
.getTaskDataById(1)
|
||||
.drop(1)
|
||||
.first()!!
|
||||
.assertHasIconDataFromSource(taskIconDataSource)
|
||||
systemUnderTest.getTaskDataById(2).first()!!.assertHasIconDataFromSource(taskIconDataSource)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun changingVisibleTasksContainsAlreadyPopulatedThumbnails() = runTest {
|
||||
recentsModel.seedTasks(defaultTaskList)
|
||||
@@ -101,7 +115,28 @@ class TasksRepositoryTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
fun retrievedThumbnailsAreDiscardedWhenTaskBecomesInvisible() = runTest {
|
||||
fun changingVisibleTasksContainsAlreadyPopulatedIcons() = runTest {
|
||||
recentsModel.seedTasks(defaultTaskList)
|
||||
systemUnderTest.getAllTaskData(forceRefresh = true)
|
||||
|
||||
systemUnderTest.setVisibleTasks(listOf(1, 2))
|
||||
|
||||
// .drop(1) to ignore initial null content before from icon was loaded.
|
||||
systemUnderTest
|
||||
.getTaskDataById(2)
|
||||
.drop(1)
|
||||
.first()!!
|
||||
.assertHasIconDataFromSource(taskIconDataSource)
|
||||
|
||||
// Prevent new loading of Drawables
|
||||
taskThumbnailDataSource.shouldLoadSynchronously = false
|
||||
systemUnderTest.setVisibleTasks(listOf(2, 3))
|
||||
|
||||
systemUnderTest.getTaskDataById(2).first()!!.assertHasIconDataFromSource(taskIconDataSource)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun retrievedImagesAreDiscardedWhenTaskBecomesInvisible() = runTest {
|
||||
recentsModel.seedTasks(defaultTaskList)
|
||||
val bitmap2 = taskThumbnailDataSource.taskIdToBitmap[2]
|
||||
systemUnderTest.getAllTaskData(forceRefresh = true)
|
||||
@@ -109,14 +144,20 @@ class TasksRepositoryTest {
|
||||
systemUnderTest.setVisibleTasks(listOf(1, 2))
|
||||
|
||||
// .drop(1) to ignore initial null content before from thumbnail was loaded.
|
||||
assertThat(systemUnderTest.getTaskDataById(2).drop(1).first()!!.thumbnail!!.thumbnail)
|
||||
.isEqualTo(bitmap2)
|
||||
val task2 = systemUnderTest.getTaskDataById(2).drop(1).first()!!
|
||||
assertThat(task2.thumbnail!!.thumbnail).isEqualTo(bitmap2)
|
||||
task2.assertHasIconDataFromSource(taskIconDataSource)
|
||||
|
||||
// Prevent new loading of Bitmaps
|
||||
taskThumbnailDataSource.shouldLoadSynchronously = false
|
||||
taskIconDataSource.shouldLoadSynchronously = false
|
||||
systemUnderTest.setVisibleTasks(listOf(0, 1))
|
||||
|
||||
assertThat(systemUnderTest.getTaskDataById(2).first()!!.thumbnail).isNull()
|
||||
val task2AfterVisibleTasksChanged = systemUnderTest.getTaskDataById(2).first()!!
|
||||
assertThat(task2AfterVisibleTasksChanged.thumbnail).isNull()
|
||||
assertThat(task2AfterVisibleTasksChanged.icon).isNull()
|
||||
assertThat(task2AfterVisibleTasksChanged.titleDescription).isNull()
|
||||
assertThat(task2AfterVisibleTasksChanged.title).isNull()
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
@@ -1,42 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
~ 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.
|
||||
-->
|
||||
|
||||
<set xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:shareInterpolator="false"
|
||||
android:showBackdrop="true">
|
||||
|
||||
<alpha
|
||||
android:fromAlpha="0.0"
|
||||
android:toAlpha="1.0"
|
||||
android:fillEnabled="true"
|
||||
android:fillBefore="true"
|
||||
android:fillAfter="true"
|
||||
android:interpolator="@interpolator/standard_decelerate_interpolator"
|
||||
android:startOffset="100"
|
||||
android:duration="350" />
|
||||
|
||||
<translate
|
||||
android:fromXDelta="-25%"
|
||||
android:toXDelta="0"
|
||||
android:fillEnabled="true"
|
||||
android:fillBefore="true"
|
||||
android:fillAfter="true"
|
||||
android:interpolator="@interpolator/emphasized_interpolator"
|
||||
android:startOffset="0"
|
||||
android:duration="450" />
|
||||
|
||||
</set>
|
||||
@@ -1,41 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
~ 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.
|
||||
-->
|
||||
|
||||
<set xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:shareInterpolator="false">
|
||||
|
||||
<alpha
|
||||
android:fromAlpha="1.0"
|
||||
android:toAlpha="0.0"
|
||||
android:fillEnabled="true"
|
||||
android:fillBefore="true"
|
||||
android:fillAfter="true"
|
||||
android:interpolator="@interpolator/standard_accelerate_interpolator"
|
||||
android:startOffset="0"
|
||||
android:duration="100" />
|
||||
|
||||
<translate
|
||||
android:fromXDelta="0"
|
||||
android:toXDelta="25%"
|
||||
android:fillEnabled="true"
|
||||
android:fillBefore="true"
|
||||
android:fillAfter="true"
|
||||
android:interpolator="@interpolator/emphasized_interpolator"
|
||||
android:startOffset="0"
|
||||
android:duration="450" />
|
||||
|
||||
</set>
|
||||
@@ -1,42 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
~ 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.
|
||||
-->
|
||||
|
||||
<set xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:shareInterpolator="false"
|
||||
android:showBackdrop="true">
|
||||
|
||||
<alpha
|
||||
android:fromAlpha="0.0"
|
||||
android:toAlpha="1.0"
|
||||
android:fillEnabled="true"
|
||||
android:fillBefore="true"
|
||||
android:fillAfter="true"
|
||||
android:interpolator="@interpolator/standard_decelerate_interpolator"
|
||||
android:startOffset="100"
|
||||
android:duration="350" />
|
||||
|
||||
<translate
|
||||
android:fromXDelta="25%"
|
||||
android:toXDelta="0"
|
||||
android:fillEnabled="true"
|
||||
android:fillBefore="true"
|
||||
android:fillAfter="true"
|
||||
android:interpolator="@interpolator/emphasized_interpolator"
|
||||
android:startOffset="0"
|
||||
android:duration="450" />
|
||||
|
||||
</set>
|
||||
@@ -1,41 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
~ 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.
|
||||
-->
|
||||
|
||||
<set xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:shareInterpolator="false">
|
||||
|
||||
<alpha
|
||||
android:fromAlpha="1.0"
|
||||
android:toAlpha="0.0"
|
||||
android:fillEnabled="true"
|
||||
android:fillBefore="true"
|
||||
android:fillAfter="true"
|
||||
android:interpolator="@interpolator/standard_accelerate_interpolator"
|
||||
android:startOffset="0"
|
||||
android:duration="100" />
|
||||
|
||||
<translate
|
||||
android:fromXDelta="0"
|
||||
android:toXDelta="-25%"
|
||||
android:fillEnabled="true"
|
||||
android:fillBefore="true"
|
||||
android:fillAfter="true"
|
||||
android:interpolator="@interpolator/emphasized_interpolator"
|
||||
android:startOffset="0"
|
||||
android:duration="450" />
|
||||
|
||||
</set>
|
||||
@@ -1,40 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
-->
|
||||
|
||||
<resources>
|
||||
<style name="HomeSettings.Theme" parent="@android:style/Theme.DeviceDefault.Settings">
|
||||
<item name="android:listPreferredItemPaddingEnd">16dp</item>
|
||||
<item name="android:listPreferredItemPaddingStart">24dp</item>
|
||||
<item name="android:navigationBarColor">@android:color/transparent</item>
|
||||
<item name="android:statusBarColor">@android:color/transparent</item>
|
||||
<item name="android:switchStyle">@style/SwitchStyle</item>
|
||||
<item name="android:textAppearanceListItem">@style/HomeSettings.PreferenceTitle</item>
|
||||
<item name="android:windowActionBar">false</item>
|
||||
<item name="android:windowNoTitle">true</item>
|
||||
<item name="preferenceTheme">@style/HomeSettings.PreferenceTheme</item>
|
||||
<item name="android:windowAnimationStyle">@style/Animation.SharedBackground</item>
|
||||
</style>
|
||||
|
||||
<style name="Animation.SharedBackground" parent="@android:style/Animation.Activity">
|
||||
<item name="android:activityOpenEnterAnimation">@anim/shared_x_axis_activity_open_enter</item>
|
||||
<item name="android:activityOpenExitAnimation">@anim/shared_x_axis_activity_open_exit</item>
|
||||
<item name="android:activityCloseEnterAnimation">@anim/shared_x_axis_activity_close_enter</item>
|
||||
<item name="android:activityCloseExitAnimation">@anim/shared_x_axis_activity_close_exit</item>
|
||||
</style>
|
||||
</resources>
|
||||
@@ -508,6 +508,7 @@ public class LauncherPreviewRenderer extends ContextWrapper
|
||||
&& !SHOULD_SHOW_FIRST_PAGE_WIDGET) {
|
||||
CellLayout firstScreen = mWorkspaceScreens.get(FIRST_SCREEN_ID);
|
||||
View qsb = mHomeElementInflater.inflate(R.layout.qsb_preview, firstScreen, false);
|
||||
// TODO: set bgHandler on qsb when it is BaseTemplateCard, which requires API changes.
|
||||
CellLayoutLayoutParams lp = new CellLayoutLayoutParams(
|
||||
0, 0, firstScreen.getCountX(), 1);
|
||||
lp.canReorder = false;
|
||||
|
||||
@@ -49,6 +49,7 @@ import android.util.Log;
|
||||
import android.view.Display;
|
||||
|
||||
import androidx.annotation.AnyThread;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.annotation.UiThread;
|
||||
import androidx.annotation.VisibleForTesting;
|
||||
|
||||
@@ -513,9 +514,8 @@ public class DisplayController implements ComponentCallbacks, SafeCloseable {
|
||||
return Collections.unmodifiableSet(mPerDisplayBounds.keySet());
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns all {@link WindowBounds}s for the current display.
|
||||
*/
|
||||
/** Returns all {@link WindowBounds}s for the current display. */
|
||||
@Nullable
|
||||
public List<WindowBounds> getCurrentBounds() {
|
||||
return mPerDisplayBounds.get(normalizedDisplayInfo);
|
||||
}
|
||||
|
||||
@@ -167,6 +167,7 @@ public class TaplTwoPanelWorkspaceTest extends AbstractLauncherUiTest<Launcher>
|
||||
|
||||
@Test
|
||||
@PortraitLandscape
|
||||
@ScreenRecordRule.ScreenRecord // b/352130094
|
||||
public void testDragIconToPage2() {
|
||||
Workspace workspace = mLauncher.getWorkspace();
|
||||
|
||||
|
||||
Reference in New Issue
Block a user