Snap for 11822896 from 8b0469b096 to 24Q3-release

Change-Id: I6c1641f22ba081857db9af1e337009afe8d9b88a
This commit is contained in:
Android Build Coastguard Worker
2024-05-09 23:26:35 +00:00
67 changed files with 666 additions and 321 deletions
@@ -121,6 +121,17 @@
android:layout_weight="1"
android:visibility="gone" />
</LinearLayout>
<!-- Unused. Included only for compatibility with parent class. -->
<LinearLayout
android:id="@+id/group_action_buttons"
android:layout_width="match_parent"
android:layout_height="@dimen/overview_actions_height"
android:layout_gravity="top|center_horizontal"
android:orientation="horizontal"
android:visibility="gone">
<Button
android:id="@+id/action_save_app_pair"
style="@style/GoOverviewActionButton"
@@ -128,8 +139,8 @@
android:layout_height="wrap_content"
android:drawableStart="@drawable/ic_save_app_pair_up_down"
android:text="@string/action_save_app_pair"
android:theme="@style/ThemeControlHighlightWorkspaceColor"
android:visibility="gone" />
android:theme="@style/ThemeControlHighlightWorkspaceColor" />
</LinearLayout>
</com.android.quickstep.views.GoOverviewActionsView>
@@ -45,14 +45,24 @@
android:theme="@style/ThemeControlHighlightWorkspaceColor"
android:visibility="gone" />
</LinearLayout>
<LinearLayout
android:id="@+id/group_action_buttons"
android:layout_width="wrap_content"
android:layout_height="@dimen/overview_actions_height"
android:layout_gravity="bottom|center_horizontal"
android:orientation="horizontal"
android:visibility="gone">
<Button
android:id="@+id/action_save_app_pair"
style="@style/OverviewActionButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/action_save_app_pair"
android:theme="@style/ThemeControlHighlightWorkspaceColor"
android:visibility="gone" />
android:theme="@style/ThemeControlHighlightWorkspaceColor" />
</LinearLayout>
</com.android.quickstep.views.OverviewActionsView>
+1 -4
View File
@@ -28,10 +28,7 @@
launcher:focusBorderColor="?androidprv:attr/materialColorOutline"
launcher:hoverBorderColor="?androidprv:attr/materialColorPrimary">
<com.android.quickstep.views.TaskThumbnailViewDeprecated
android:id="@+id/snapshot"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
<include layout="@layout/task_thumbnail" />
<!-- Filtering affects only alpha instead of the visibility since visibility can be altered
separately through RecentsView#resetFromSplitSelectionState() -->
+1 -4
View File
@@ -42,10 +42,7 @@
views that do not inherint from TaskView only or create a generic TaskView that have
N number of tasks.
-->
<com.android.quickstep.views.TaskThumbnailViewDeprecated
android:id="@+id/snapshot"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
<include layout="@layout/task_thumbnail"
android:visibility="gone" />
<ViewStub
+3 -8
View File
@@ -33,15 +33,10 @@
launcher:focusBorderColor="?androidprv:attr/materialColorOutline"
launcher:hoverBorderColor="?androidprv:attr/materialColorPrimary">
<com.android.quickstep.views.TaskThumbnailViewDeprecated
android:id="@+id/snapshot"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
<include layout="@layout/task_thumbnail"/>
<com.android.quickstep.views.TaskThumbnailViewDeprecated
android:id="@+id/bottomright_snapshot"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
<include layout="@layout/task_thumbnail"
android:id="@+id/bottomright_snapshot" />
<!-- Filtering affects only alpha instead of the visibility since visibility can be altered
separately through RecentsView#resetFromSplitSelectionState() -->
+20
View File
@@ -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" />
+1 -1
View File
@@ -95,7 +95,7 @@
<string name="allset_navigation_settings" msgid="4713404605961476027"><annotation id="link">"Einstellungen der Systemsteuerung"</annotation></string>
<string name="action_share" msgid="2648470652637092375">"Teilen"</string>
<string name="action_screenshot" msgid="8171125848358142917">"Screenshot"</string>
<string name="action_split" msgid="2098009717623550676">"Teilen"</string>
<string name="action_split" msgid="2098009717623550676">"Splitscreen"</string>
<string name="action_save_app_pair" msgid="5974823919237645229">"App-Paar speichern"</string>
<string name="toast_split_select_app" msgid="8464310533320556058">"Für Splitscreen auf weitere App tippen"</string>
<string name="toast_contextual_split_select_app" msgid="433510957123687090">"Für Splitscreen andere App auswählen"</string>
+1 -1
View File
@@ -118,7 +118,7 @@
<string name="taskbar_edu_pinning_title" msgid="210102174154211712">"Mostrar siempre la barra de tareas"</string>
<string name="taskbar_edu_pinning_standalone" msgid="2636919474366410467">"Para mostrar siempre la barra de tareas en la parte inferior, mantén pulsada la línea divisoria"</string>
<string name="taskbar_search_edu_title" msgid="5569194922234364530">"Mantén pulsada la tecla de acción para buscar lo que ves en pantalla"</string>
<string name="taskbar_edu_search_disclosure" msgid="8734536088447779686">"El producto usa la parte seleccionada de tu pantalla para hacer búsquedas. Se aplican la <xliff:g id="BEGIN_PRIVACY_LINK">&lt;a href="%1$s"&gt;</xliff:g>Política de Privacidad<xliff:g id="END_PRIVACY_LINK">&lt;/a&gt;</xliff:g> y los <xliff:g id="BEGIN_TOS_LINK">&lt;a href="%2$s"&gt;</xliff:g>Términos del Servicio<xliff:g id="END_TOS_LINK">&lt;/a&gt;</xliff:g> de Google."</string>
<string name="taskbar_edu_search_disclosure" msgid="8734536088447779686">"Este producto usa la parte seleccionada de tu pantalla para hacer búsquedas. Se aplican la <xliff:g id="BEGIN_PRIVACY_LINK">&lt;a href="%1$s"&gt;</xliff:g>Política de Privacidad<xliff:g id="END_PRIVACY_LINK">&lt;/a&gt;</xliff:g> y los <xliff:g id="BEGIN_TOS_LINK">&lt;a href="%2$s"&gt;</xliff:g>Términos del Servicio<xliff:g id="END_TOS_LINK">&lt;/a&gt;</xliff:g> de Google."</string>
<string name="taskbar_edu_close" msgid="887022990168191073">"Cerrar"</string>
<string name="taskbar_edu_done" msgid="6880178093977704569">"Hecho"</string>
<string name="taskbar_button_home" msgid="2151398979630664652">"Inicio"</string>
+1 -1
View File
@@ -95,7 +95,7 @@
<string name="allset_navigation_settings" msgid="4713404605961476027"><annotation id="link">"Süsteemi navigeerimisseaded"</annotation></string>
<string name="action_share" msgid="2648470652637092375">"Jaga"</string>
<string name="action_screenshot" msgid="8171125848358142917">"Ekraanipilt"</string>
<string name="action_split" msgid="2098009717623550676">"Eralda"</string>
<string name="action_split" msgid="2098009717623550676">"Jaga pooleks"</string>
<string name="action_save_app_pair" msgid="5974823919237645229">"Salvesta rakendusepaar"</string>
<string name="toast_split_select_app" msgid="8464310533320556058">"Jagatud ekraanikuva kasutamiseks puudutage muud rakendust"</string>
<string name="toast_contextual_split_select_app" msgid="433510957123687090">"Valige jagatud ekraanikuva jaoks muu rakendus."</string>
+1 -1
View File
@@ -95,7 +95,7 @@
<string name="allset_navigation_settings" msgid="4713404605961476027"><annotation id="link">"Järjestelmän navigointiasetukset"</annotation></string>
<string name="action_share" msgid="2648470652637092375">"Jaa"</string>
<string name="action_screenshot" msgid="8171125848358142917">"Kuvakaappaus"</string>
<string name="action_split" msgid="2098009717623550676">"Jaa"</string>
<string name="action_split" msgid="2098009717623550676">"Jaettu näyttö"</string>
<string name="action_save_app_pair" msgid="5974823919237645229">"Tallenna pari"</string>
<string name="toast_split_select_app" msgid="8464310533320556058">"Avaa jaettu näyttö napauttamalla toista sovellusta"</string>
<string name="toast_contextual_split_select_app" msgid="433510957123687090">"Käytä jaettua näyttöä valitsemalla toinen sovellus"</string>
+1 -1
View File
@@ -95,7 +95,7 @@
<string name="allset_navigation_settings" msgid="4713404605961476027"><annotation id="link">"စနစ် လမ်းညွှန် ဆက်တင်များ"</annotation></string>
<string name="action_share" msgid="2648470652637092375">"မျှဝေရန်"</string>
<string name="action_screenshot" msgid="8171125848358142917">"ဖန်သားပြင်ဓာတ်ပုံ"</string>
<string name="action_split" msgid="2098009717623550676">"ခွဲထုတ်ရန်"</string>
<string name="action_split" msgid="2098009717623550676">"ခွဲရန်"</string>
<string name="action_save_app_pair" msgid="5974823919237645229">"အက်ပ်အတွဲ သိမ်းရန်"</string>
<string name="toast_split_select_app" msgid="8464310533320556058">"မျက်နှာပြင် ခွဲ၍ပြသရန် အက်ပ်နောက်တစ်ခုကို တို့ပါ"</string>
<string name="toast_contextual_split_select_app" msgid="433510957123687090">"မျက်နှာပြင် ခွဲ၍ပြသခြင်းသုံးရန် နောက်အက်ပ်တစ်ခုရွေးပါ"</string>
+1 -1
View File
@@ -95,7 +95,7 @@
<string name="allset_navigation_settings" msgid="4713404605961476027"><annotation id="link">"系统导航设置"</annotation></string>
<string name="action_share" msgid="2648470652637092375">"分享"</string>
<string name="action_screenshot" msgid="8171125848358142917">"屏幕截图"</string>
<string name="action_split" msgid="2098009717623550676">"分"</string>
<string name="action_split" msgid="2098009717623550676">"分"</string>
<string name="action_save_app_pair" msgid="5974823919237645229">"保存应用组合"</string>
<string name="toast_split_select_app" msgid="8464310533320556058">"点按另一个应用即可使用分屏"</string>
<string name="toast_contextual_split_select_app" msgid="433510957123687090">"另外选择一个应用才可使用分屏模式"</string>
@@ -965,7 +965,9 @@ public class TaskbarActivityContext extends BaseTaskbarContext {
mWindowLayoutParams.paramsForRotation[rot].height = size;
}
}
mControllers.taskbarInsetsController.onTaskbarOrBubblebarWindowHeightOrInsetsChanged();
mControllers.runAfterInit(
mControllers.taskbarInsetsController
::onTaskbarOrBubblebarWindowHeightOrInsetsChanged);
notifyUpdateLayoutParams();
}
@@ -1451,7 +1453,7 @@ public class TaskbarActivityContext extends BaseTaskbarContext {
});
}
protected boolean isUserSetupComplete() {
public boolean isUserSetupComplete() {
return mIsUserSetupComplete;
}
@@ -24,8 +24,10 @@ import android.widget.FrameLayout
import android.widget.ImageView
import android.widget.LinearLayout
import android.widget.Space
import com.android.launcher3.DeviceProfile
import com.android.launcher3.R
import com.android.launcher3.Utilities
import com.android.launcher3.taskbar.TaskbarActivityContext
import com.android.launcher3.taskbar.navbutton.NavButtonLayoutFactory.NavButtonLayoutter
/**
@@ -73,6 +75,23 @@ abstract class AbstractNavButtonLayoutter(
return params
}
fun adjustForSetupInPhoneMode(
navButtonsLayoutParams: FrameLayout.LayoutParams,
navButtonsViewLayoutParams: FrameLayout.LayoutParams,
deviceProfile: DeviceProfile
) {
val phoneOrPortraitSetupMargin =
resources.getDimensionPixelSize(R.dimen.taskbar_contextual_button_suw_margin)
navButtonsLayoutParams.marginStart = phoneOrPortraitSetupMargin
navButtonsLayoutParams.bottomMargin =
if (!deviceProfile.isLandscape) 0
else
phoneOrPortraitSetupMargin -
resources.getDimensionPixelSize(R.dimen.taskbar_nav_buttons_size) / 2
navButtonsViewLayoutParams.height =
resources.getDimensionPixelSize(R.dimen.taskbar_contextual_button_suw_height)
}
open fun repositionContextualContainer(
contextualContainer: ViewGroup,
buttonSize: Int,
@@ -116,6 +116,7 @@ class NavButtonLayoutFactory {
isPhoneGestureMode -> {
PhoneGestureLayoutter(
resources,
navButtonsView,
navButtonContainer,
endContextualContainer,
startContextualContainer,
@@ -17,15 +17,19 @@
package com.android.launcher3.taskbar.navbutton
import android.content.res.Resources
import android.view.Gravity
import android.view.ViewGroup
import android.widget.FrameLayout
import android.widget.ImageView
import android.widget.LinearLayout
import android.widget.Space
import com.android.launcher3.DeviceProfile
import com.android.launcher3.taskbar.TaskbarActivityContext
/** Layoutter for showing gesture navigation on phone screen. No buttons here, no-op container */
class PhoneGestureLayoutter(
resources: Resources,
navButtonsView: NearestTouchFrame,
navBarContainer: LinearLayout,
endContextualContainer: ViewGroup,
startContextualContainer: ViewGroup,
@@ -42,8 +46,31 @@ class PhoneGestureLayoutter(
a11yButton,
space
) {
private val mNavButtonsView = navButtonsView
override fun layoutButtons(context: TaskbarActivityContext, isA11yButtonPersistent: Boolean) {
// TODO: look into if we should use SetupNavLayoutter instead.
if (!context.isUserSetupComplete) {
// Since setup wizard only has back button enabled, it looks strange to be
// end-aligned, so start-align instead.
val navButtonsLayoutParams = navButtonContainer.layoutParams as FrameLayout.LayoutParams
val navButtonsViewLayoutParams =
mNavButtonsView.layoutParams as FrameLayout.LayoutParams
val deviceProfile: DeviceProfile = context.deviceProfile
navButtonsLayoutParams.marginEnd = 0
navButtonsLayoutParams.gravity = Gravity.START
context.setTaskbarWindowSize(context.setupWindowSize)
adjustForSetupInPhoneMode(
navButtonsLayoutParams,
navButtonsViewLayoutParams,
deviceProfile
)
mNavButtonsView.layoutParams = navButtonsViewLayoutParams
navButtonContainer.layoutParams = navButtonsLayoutParams
}
endContextualContainer.removeAllViews()
startContextualContainer.removeAllViews()
}
@@ -77,16 +77,11 @@ class SetupNavLayoutter(
navButtonsLayoutParams.height =
resources.getDimensionPixelSize(R.dimen.taskbar_back_button_suw_height)
} else {
val phoneOrPortraitSetupMargin =
resources.getDimensionPixelSize(R.dimen.taskbar_contextual_button_suw_margin)
navButtonsLayoutParams.marginStart = phoneOrPortraitSetupMargin
navButtonsLayoutParams.bottomMargin =
if (!deviceProfile.isLandscape) 0
else
phoneOrPortraitSetupMargin -
resources.getDimensionPixelSize(R.dimen.taskbar_nav_buttons_size) / 2
navButtonsViewLayoutParams.height =
resources.getDimensionPixelSize(R.dimen.taskbar_contextual_button_suw_height)
adjustForSetupInPhoneMode(
navButtonsLayoutParams,
navButtonsViewLayoutParams,
deviceProfile
)
}
mNavButtonsView.layoutParams = navButtonsViewLayoutParams
navButtonContainer.layoutParams = navButtonsLayoutParams
@@ -21,7 +21,6 @@ import static com.android.launcher3.LauncherState.OVERVIEW;
import static com.android.launcher3.LauncherState.OVERVIEW_ACTIONS;
import static com.android.launcher3.LauncherState.OVERVIEW_SPLIT_SELECT;
import static com.android.launcher3.states.StateAnimationConfig.ANIM_OVERVIEW_ACTIONS_FADE;
import static com.android.launcher3.util.MultiPropertyFactory.MULTI_PROPERTY_VALUE;
import static com.android.quickstep.views.RecentsView.CONTENT_ALPHA;
import static com.android.quickstep.views.RecentsView.FULLSCREEN_PROGRESS;
import static com.android.quickstep.views.RecentsView.TASK_MODALNESS;
@@ -50,6 +49,7 @@ import com.android.quickstep.util.AnimUtils;
import com.android.quickstep.util.SplitAnimationTimings;
import com.android.quickstep.views.ClearAllButton;
import com.android.quickstep.views.LauncherRecentsView;
import com.android.quickstep.views.OverviewActionsView;
import com.android.quickstep.views.RecentsView;
/**
@@ -167,8 +167,8 @@ public final class RecentsViewStateController extends
propertySetter.setFloat(mRecentsView.getClearAllButton(), ClearAllButton.VISIBILITY_ALPHA,
clearAllButtonAlpha, LINEAR);
float overviewButtonAlpha = state.areElementsVisible(mLauncher, OVERVIEW_ACTIONS) ? 1 : 0;
propertySetter.setFloat(mLauncher.getActionsView().getVisibilityAlpha(),
MULTI_PROPERTY_VALUE, overviewButtonAlpha, config.getInterpolator(
propertySetter.setFloat(mLauncher.getActionsView().getVisibilityAlphaSetter(),
OverviewActionsView.FLOAT_SETTER, overviewButtonAlpha, config.getInterpolator(
ANIM_OVERVIEW_ACTIONS_FADE, LINEAR));
}
@@ -248,7 +248,7 @@ public class NoButtonQuickSwitchTouchController implements TouchController,
TASK_THUMBNAIL_SPLASH_ALPHA.set(mRecentsView, fromState.showTaskThumbnailSplash() ? 1f : 0);
mRecentsView.setContentAlpha(1);
mRecentsView.setFullscreenProgress(fromState.getOverviewFullscreenProgress());
mLauncher.getActionsView().getVisibilityAlpha().setValue(
mLauncher.getActionsView().getVisibilityAlphaSetter().accept(
(fromState.getVisibleElements(mLauncher) & OVERVIEW_ACTIONS) != 0 ? 1f : 0f);
mRecentsView.setTaskIconScaledDown(true);
@@ -154,7 +154,7 @@ class DeviceConfigWrapper private constructor(propReader: PropReader) {
}
companion object {
val configHelper by lazy { DeviceConfigHelper(::DeviceConfigWrapper) }
@JvmStatic val configHelper by lazy { DeviceConfigHelper(::DeviceConfigWrapper) }
@JvmStatic fun get() = configHelper.config
}
@@ -65,7 +65,6 @@ import com.android.systemui.shared.recents.view.AppTransitionAnimationSpecsFutur
import com.android.systemui.shared.recents.view.RecentsTransition;
import com.android.systemui.shared.system.ActivityManagerWrapper;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.function.Function;
@@ -336,7 +335,7 @@ public interface TaskShortcutFactory {
recentsView.isTaskInExpectedScrollPosition(recentsView.indexOfChild(taskView));
boolean shouldShowActionsButtonInstead =
isLargeTileFocusedTask && isInExpectedScrollPosition;
boolean hasUnpinnableApp = Arrays.stream(taskView.getTaskContainers())
boolean hasUnpinnableApp = taskView.getTaskContainers().stream()
.anyMatch(att -> att != null && att.getItemInfo() != null
&& ((att.getItemInfo().runtimeStatusFlags
& ItemInfoWithIcon.FLAG_NOT_PINNABLE) != 0));
@@ -46,12 +46,10 @@ import com.android.launcher3.anim.PendingAnimation;
import com.android.launcher3.anim.PropertySetter;
import com.android.launcher3.statemanager.StateManager.StateHandler;
import com.android.launcher3.states.StateAnimationConfig;
import com.android.launcher3.util.MultiPropertyFactory;
import com.android.quickstep.RecentsActivity;
import com.android.quickstep.views.ClearAllButton;
import com.android.quickstep.views.OverviewActionsView;
import com.android.quickstep.views.RecentsView;
import com.android.quickstep.views.RecentsViewContainer;
/**
* State controller for fallback recents activity
@@ -98,8 +96,8 @@ public class FallbackRecentsStateController implements StateHandler<RecentsState
setter.setFloat(mRecentsView.getClearAllButton(), ClearAllButton.VISIBILITY_ALPHA,
clearAllButtonAlpha, LINEAR);
float overviewButtonAlpha = state.hasOverviewActions() ? 1 : 0;
setter.setFloat(mActivity.getActionsView().getVisibilityAlpha(),
MultiPropertyFactory.MULTI_PROPERTY_VALUE, overviewButtonAlpha, LINEAR);
setter.setFloat(mActivity.getActionsView().getVisibilityAlphaSetter(),
OverviewActionsView.FLOAT_SETTER, overviewButtonAlpha, LINEAR);
float[] scaleAndOffset = state.getOverviewScaleAndOffset(mActivity);
setter.setFloat(mRecentsView, RECENTS_SCALE_PROPERTY, scaleAndOffset[0],
@@ -0,0 +1,27 @@
/*
* 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.viewmodel
import kotlinx.coroutines.flow.MutableStateFlow
// This is far from complete but serves the purpose of enabling refactoring in other areas
class RecentsViewData {
val fullscreenProgress = MutableStateFlow(1f)
// This is typically a View concern but it is used to invalidate rendering in other Views
val scale = MutableStateFlow(1f)
}
@@ -17,22 +17,44 @@
package com.android.quickstep.task.thumbnail
import android.content.Context
import android.content.res.Configuration
import android.graphics.Canvas
import android.graphics.Outline
import android.graphics.Paint
import android.graphics.PorterDuff
import android.graphics.PorterDuffXfermode
import android.util.AttributeSet
import android.view.View
import com.android.quickstep.task.thumbnail.TaskThumbnailUiState.*
import android.view.ViewOutlineProvider
import com.android.launcher3.Utilities
import com.android.quickstep.task.thumbnail.TaskThumbnailUiState.LiveTile
import com.android.quickstep.task.thumbnail.TaskThumbnailUiState.Uninitialized
import com.android.quickstep.util.TaskCornerRadius
import com.android.quickstep.views.RecentsView
import com.android.quickstep.views.RecentsViewContainer
import com.android.quickstep.views.TaskView
import com.android.systemui.shared.system.QuickStepContract
import kotlinx.coroutines.MainScope
import kotlinx.coroutines.launch
class TaskThumbnailView : View {
// TODO(b/335649589): Ideally create and obtain this from DI. This ViewModel should be scoped
// to [TaskView], and also shared between [TaskView] and [TaskThumbnailView]
val viewModel = TaskThumbnailViewModel()
// This is using a lazy for now because the dependencies cannot be obtained without DI.
val viewModel by lazy {
TaskThumbnailViewModel(
RecentsViewContainer.containerFromContext<RecentsViewContainer>(context)
.getOverviewPanel<RecentsView<*, *>>()
.mRecentsViewData,
(parent as TaskView).mTaskViewData
)
}
private var uiState: TaskThumbnailUiState = Uninitialized
private var inheritedScale: Float = 1f
private var cornerRadius: Float = TaskCornerRadius.get(context)
private var fullscreenCornerRadius: Float = QuickStepContract.getWindowCornerRadius(context)
constructor(context: Context?) : super(context)
constructor(context: Context?, attrs: AttributeSet?) : super(context, attrs)
@@ -51,6 +73,27 @@ class TaskThumbnailView : View {
invalidate()
}
}
MainScope().launch { viewModel.recentsFullscreenProgress.collect { invalidateOutline() } }
MainScope().launch {
viewModel.inheritedScale.collect { viewModelInheritedScale ->
inheritedScale = viewModelInheritedScale
invalidateOutline()
}
}
clipToOutline = true
outlineProvider =
object : ViewOutlineProvider() {
override fun getOutline(view: View, outline: Outline) {
outline.setRoundRect(
0,
0,
view.measuredWidth,
view.measuredHeight,
getCurrentCornerRadius()
)
}
}
}
override fun onDraw(canvas: Canvas) {
@@ -60,19 +103,25 @@ class TaskThumbnailView : View {
}
}
private fun drawTransparentUiState(canvas: Canvas) {
canvas.drawRoundRect(
0f,
0f,
measuredWidth.toFloat(),
measuredHeight.toFloat(),
// TODO(b/334826840) add rounded corners
0f,
0f,
CLEAR_PAINT
)
override fun onConfigurationChanged(newConfig: Configuration?) {
super.onConfigurationChanged(newConfig)
cornerRadius = TaskCornerRadius.get(context)
fullscreenCornerRadius = QuickStepContract.getWindowCornerRadius(context)
invalidateOutline()
}
private fun drawTransparentUiState(canvas: Canvas) {
canvas.drawRect(0f, 0f, measuredWidth.toFloat(), measuredHeight.toFloat(), CLEAR_PAINT)
}
private fun getCurrentCornerRadius() =
Utilities.mapRange(
viewModel.recentsFullscreenProgress.value,
cornerRadius,
fullscreenCornerRadius
) / inheritedScale
companion object {
private val CLEAR_PAINT =
Paint().apply { xfermode = PorterDuffXfermode(PorterDuff.Mode.CLEAR) }
@@ -16,20 +16,32 @@
package com.android.quickstep.task.thumbnail
import com.android.quickstep.recents.viewmodel.RecentsViewData
import com.android.quickstep.task.thumbnail.TaskThumbnailUiState.LiveTile
import com.android.quickstep.task.thumbnail.TaskThumbnailUiState.Uninitialized
import com.android.quickstep.task.viewmodel.TaskViewData
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.map
class TaskThumbnailViewModel {
private val _uiState: MutableStateFlow<TaskThumbnailUiState> =
MutableStateFlow(TaskThumbnailUiState.Uninitialized)
val uiState: StateFlow<TaskThumbnailUiState> = _uiState
class TaskThumbnailViewModel(recentsViewData: RecentsViewData, taskViewData: TaskViewData) {
private val task = MutableStateFlow<TaskThumbnail?>(null)
val recentsFullscreenProgress = recentsViewData.fullscreenProgress
val inheritedScale =
combine(recentsViewData.scale, taskViewData.scale) { recentsScale, taskScale ->
recentsScale * taskScale
}
val uiState =
task.map { taskVal ->
when {
taskVal == null -> Uninitialized
taskVal.isRunning -> LiveTile
else -> Uninitialized
}
}
fun bind(task: TaskThumbnail) {
_uiState.value =
if (task.isRunning) {
TaskThumbnailUiState.LiveTile
} else {
TaskThumbnailUiState.Uninitialized
}
this.task.value = task
}
}
@@ -0,0 +1,24 @@
/*
* 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.viewmodel
import kotlinx.coroutines.flow.MutableStateFlow
class TaskViewData {
// This is typically a View concern but it is used to invalidate rendering in other Views
val scale = MutableStateFlow(1f)
}
@@ -116,9 +116,9 @@ public class AppPairsController {
*/
public void saveAppPair(GroupedTaskView gtv) {
InteractionJankMonitorWrapper.begin(gtv, Cuj.CUJ_LAUNCHER_SAVE_APP_PAIR);
TaskView.TaskContainer[] containers = gtv.getTaskContainers();
WorkspaceItemInfo recentsInfo1 = containers[0].getItemInfo();
WorkspaceItemInfo recentsInfo2 = containers[1].getItemInfo();
List<TaskView.TaskContainer> containers = gtv.getTaskContainers();
WorkspaceItemInfo recentsInfo1 = containers.get(0).getItemInfo();
WorkspaceItemInfo recentsInfo2 = containers.get(1).getItemInfo();
WorkspaceItemInfo app1 = lookupLaunchableItem(recentsInfo1.getComponentKey());
WorkspaceItemInfo app2 = lookupLaunchableItem(recentsInfo2.getComponentKey());
@@ -48,12 +48,14 @@ class DeviceConfigHelper<ConfigType>(private val factory: (PropReader) -> Config
PropReader(
object : PropProvider {
override fun <T : Any> get(key: String, fallback: T): T {
if (fallback is Int)
if (fallback is Int) {
allKeys.add(key)
return DeviceConfig.getInt(NAMESPACE_LAUNCHER, key, fallback) as T
else if (fallback is Boolean)
} else if (fallback is Boolean) {
allKeys.add(key)
return DeviceConfig.getBoolean(NAMESPACE_LAUNCHER, key, fallback)
as T
else return fallback
} else return fallback
}
}
)
@@ -16,7 +16,7 @@
package com.android.quickstep.views;
import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT;
import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT;
import static com.android.launcher3.LauncherState.BACKGROUND_APP;
import static com.android.launcher3.util.SplitConfigurationOptions.STAGE_POSITION_UNDEFINED;
@@ -42,6 +42,7 @@ import com.android.launcher3.Utilities;
import com.android.launcher3.desktop.DesktopRecentsTransitionController;
import com.android.launcher3.util.CancellableTask;
import com.android.launcher3.util.RunnableList;
import com.android.launcher3.util.ViewPool;
import com.android.quickstep.BaseContainerInterface;
import com.android.quickstep.RecentsModel;
import com.android.quickstep.TaskThumbnailCache;
@@ -69,8 +70,6 @@ public class DesktopTaskView extends TaskView {
private static final boolean DEBUG = false;
private final ArrayList<TaskThumbnailViewDeprecated> mSnapshotViews = new ArrayList<>();
private final ArrayList<CancellableTask<?>> mPendingThumbnailRequests = new ArrayList<>();
private final TaskView.FullscreenDrawParams mSnapshotDrawParams;
@@ -81,6 +80,8 @@ public class DesktopTaskView extends TaskView {
private final PointF mTempPointF = new PointF();
private final ViewPool<TaskThumbnailViewDeprecated> mTaskthumbnailViewPool;
public DesktopTaskView(Context context) {
this(context, null);
}
@@ -103,6 +104,10 @@ public class DesktopTaskView extends TaskView {
return QuickStepContract.getWindowCornerRadius(context);
}
};
// As DesktopTaskView is inflated in background, use initialSize=0 to avoid initPool.
mTaskthumbnailViewPool = new ViewPool<>(context, this, R.layout.task_thumbnail,
/* maxSize= */10, /* initialSize= */ 0);
mTaskContainers = new ArrayList<>();
}
@Override
@@ -163,45 +168,36 @@ public class DesktopTaskView extends TaskView {
}
cancelPendingLoadTasks();
mTaskContainers = new TaskContainer[tasks.size()];
// Ensure there are equal number of snapshot views and tasks.
// More tasks than views, add views. More views than tasks, remove views.
// TODO(b/251586230): use a ViewPool for creating TaskThumbnailViews
if (mSnapshotViews.size() > tasks.size()) {
int diff = mSnapshotViews.size() - tasks.size();
for (int i = 0; i < diff; i++) {
TaskThumbnailViewDeprecated snapshotView = mSnapshotViews.remove(0);
removeView(snapshotView);
}
} else if (mSnapshotViews.size() < tasks.size()) {
int diff = tasks.size() - mSnapshotViews.size();
for (int i = 0; i < diff; i++) {
TaskThumbnailViewDeprecated snapshotView =
new TaskThumbnailViewDeprecated(getContext());
mSnapshotViews.add(snapshotView);
// Add snapshots from to position after the initial child views.
addView(snapshotView, mChildCountAtInflation,
new LayoutParams(WRAP_CONTENT, WRAP_CONTENT));
}
}
((ArrayList<TaskContainer>) mTaskContainers).ensureCapacity(tasks.size());
for (int i = 0; i < tasks.size(); i++) {
Task task = tasks.get(i);
TaskThumbnailViewDeprecated thumbnailView = mSnapshotViews.get(i);
TaskThumbnailViewDeprecated thumbnailView;
if (i >= mTaskContainers.size()) {
thumbnailView = mTaskthumbnailViewPool.getView();
// Add thumbnailView from to position after the initial child views.
addView(thumbnailView, mChildCountAtInflation,
new LayoutParams(WRAP_CONTENT, WRAP_CONTENT));
} else {
thumbnailView = mTaskContainers.get(i).getThumbnailView();
}
thumbnailView.bind(task);
mTaskContainers[i] = createAttributeContainer(task, thumbnailView);
TaskContainer taskContainer = new TaskContainer(task, thumbnailView, mIconView,
STAGE_POSITION_UNDEFINED, /*digitalWellBeingToast=*/ null);
if (i >= mTaskContainers.size()) {
mTaskContainers.add(taskContainer);
} else {
mTaskContainers.set(i, taskContainer);
}
}
while (mTaskContainers.size() > tasks.size()) {
TaskContainer taskContainer = mTaskContainers.remove(mTaskContainers.size() - 1);
removeView(taskContainer.getThumbnailView());
mTaskthumbnailViewPool.recycle(taskContainer.getThumbnailView());
}
setOrientationState(orientedState);
}
private TaskContainer createAttributeContainer(Task task,
TaskThumbnailViewDeprecated thumbnailView) {
return new TaskContainer(task, thumbnailView, mIconView,
STAGE_POSITION_UNDEFINED, /*digitalWellBeingToast=*/ null);
}
@Override
public void onTaskListVisibilityChanged(boolean visible, int changes) {
cancelPendingLoadTasks();
@@ -314,7 +310,7 @@ public class DesktopTaskView extends TaskView {
int thumbnailTopMarginPx = mContainer.getDeviceProfile().overviewTaskThumbnailTopMarginPx;
containerHeight -= thumbnailTopMarginPx;
if (mTaskContainers.length == 0) {
if (mTaskContainers.isEmpty()) {
return;
}
@@ -98,7 +98,7 @@ public class GroupedTaskView extends TaskView {
@Deprecated
@Nullable
private Task getSecondTask() {
return mTaskContainers.length > 1 ? mTaskContainers[1].getTask() : null;
return mTaskContainers.size() > 1 ? mTaskContainers.get(1).getTask() : null;
}
@Override
@@ -150,11 +150,11 @@ public class GroupedTaskView extends TaskView {
public void bind(Task primary, Task secondary, RecentsOrientedState orientedState,
@Nullable SplitBounds splitBoundsConfig) {
super.bind(primary, orientedState);
mTaskContainers = new TaskContainer[]{
mTaskContainers[0],
mTaskContainers = Arrays.asList(
mTaskContainers.get(0),
new TaskContainer(secondary, findViewById(R.id.bottomright_snapshot),
mIconView2, STAGE_POSITION_BOTTOM_OR_RIGHT, mDigitalWellBeingToast2)};
mTaskContainers[0].setStagePosition(
mIconView2, STAGE_POSITION_BOTTOM_OR_RIGHT, mDigitalWellBeingToast2));
mTaskContainers.get(0).setStagePosition(
SplitConfigurationOptions.STAGE_POSITION_TOP_OR_LEFT);
mSnapshotView2.bind(secondary);
mSplitBoundsConfig = splitBoundsConfig;
@@ -176,12 +176,12 @@ public class GroupedTaskView extends TaskView {
public void setUpShowAllInstancesListener() {
// sets up the listener for the left/top task
super.setUpShowAllInstancesListener();
if (mTaskContainers.length < 2) {
if (mTaskContainers.size() < 2) {
return;
}
// right/bottom task's base package name
String taskPackageName = mTaskContainers[1].getTask().key.getPackageName();
String taskPackageName = mTaskContainers.get(1).getTask().key.getPackageName();
// icon of the right/bottom task
View showWindowsView = findViewById(R.id.show_windows_right);
@@ -279,7 +279,7 @@ public class GroupedTaskView extends TaskView {
@Nullable
@Override
public RunnableList launchTaskAnimated() {
if (mTaskContainers.length == 0) {
if (mTaskContainers.isEmpty()) {
return null;
}
@@ -407,9 +407,8 @@ public class GroupedTaskView extends TaskView {
} else {
// Currently being split with this taskView, let the non-split selected thumbnail
// take up full thumbnail area
Optional<TaskContainer> nonSplitContainer = Arrays.stream(
mTaskContainers).filter(
container -> container.getTask().key.id != initSplitTaskId).findAny();
Optional<TaskContainer> nonSplitContainer = mTaskContainers.stream().filter(
container -> container.getTask().key.id != initSplitTaskId).findAny();
nonSplitContainer.ifPresent(
taskIdAttributeContainer -> taskIdAttributeContainer.getThumbnailView().measure(
widthMeasureSpec, MeasureSpec.makeMeasureSpec(
@@ -20,6 +20,7 @@ import android.content.Context;
import android.content.res.Configuration;
import android.graphics.Rect;
import android.util.AttributeSet;
import android.util.FloatProperty;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
@@ -33,9 +34,7 @@ import com.android.launcher3.DeviceProfile;
import com.android.launcher3.Flags;
import com.android.launcher3.Insettable;
import com.android.launcher3.R;
import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.util.DisplayController;
import com.android.launcher3.util.MultiPropertyFactory.MultiProperty;
import com.android.launcher3.util.MultiValueAlpha;
import com.android.launcher3.util.NavigationMode;
import com.android.quickstep.TaskOverlayFactory.OverlayUICallbacks;
@@ -43,12 +42,26 @@ import com.android.quickstep.util.LayoutUtils;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.Arrays;
import java.util.function.Consumer;
/**
* View for showing action buttons in Overview
*/
public class OverviewActionsView<T extends OverlayUICallbacks> extends FrameLayout
implements OnClickListener, Insettable {
public static final FloatProperty<Consumer<Float>> FLOAT_SETTER =
new FloatProperty<>("floatSetter") {
@Override
public void setValue(Consumer<Float> consumer, float v) {
consumer.accept(v);
}
@Override
public Float get(Consumer<Float> consumer) {
return -1f;
}
};
private final Rect mInsets = new Rect();
@@ -89,30 +102,24 @@ public class OverviewActionsView<T extends OverlayUICallbacks> extends FrameLayo
private static final int INDEX_HIDDEN_FLAGS_ALPHA = 3;
private static final int INDEX_SHARE_TARGET_ALPHA = 4;
private static final int INDEX_SCROLL_ALPHA = 5;
private static final int NUM_ALPHAS = 6;
public @interface ScreenshotButtonHiddenFlags { }
public static final int FLAG_MULTIPLE_TASKS_HIDE_SCREENSHOT = 1 << 0;
private static final int INDEX_GROUPED_ALPHA = 6;
private static final int INDEX_3P_LAUNCHER = 7;
private static final int NUM_ALPHAS = 8;
public @interface SplitButtonHiddenFlags { }
public static final int FLAG_SMALL_SCREEN_HIDE_SPLIT = 1 << 0;
public static final int FLAG_MULTIPLE_TASKS_HIDE_SPLIT = 1 << 1;
public @interface SplitButtonDisabledFlags { }
public static final int FLAG_SINGLE_TASK_DISABLE_SPLIT = 1 << 0;
public @interface AppPairButtonHiddenFlags { }
public static final int FLAG_SINGLE_TASK_HIDE_APP_PAIR = 1 << 0;
public static final int FLAG_SMALL_SCREEN_HIDE_APP_PAIR = 1 << 1;
public static final int FLAG_3P_LAUNCHER_HIDE_APP_PAIR = 1 << 2;
private MultiValueAlpha mMultiValueAlpha;
/** Holds MultiValueAlpha values for all actions buttons */
private final MultiValueAlpha[] mMultiValueAlphas = new MultiValueAlpha[2];
/** Index used for single-task actions in the mMultiValueAlphas array */
private static final int ACTIONS_ALPHAS = 0;
/** Index used for grouped-task actions in the mMultiValueAlphas array */
private static final int GROUP_ACTIONS_ALPHAS = 1;
/** Container for the action buttons below a focused, non-split Overview tile. */
protected LinearLayout mActionButtons;
// The screenshot button is implemented as a Button in launcher3 and NexusLauncher, but is an
// ImageButton in go launcher (does not share a common class with Button). Take care when
// casting this.
private View mScreenshotButton;
/** Container for the action buttons below a focused, split Overview tile. */
protected LinearLayout mGroupActionButtons;
private Button mSplitButton;
private Button mSaveAppPairButton;
@@ -122,21 +129,16 @@ public class OverviewActionsView<T extends OverlayUICallbacks> extends FrameLayo
@ActionsDisabledFlags
protected int mDisabledFlags;
@ScreenshotButtonHiddenFlags
private int mScreenshotButtonHiddenFlags;
@SplitButtonHiddenFlags
private int mSplitButtonHiddenFlags;
@AppPairButtonHiddenFlags
private int mAppPairButtonHiddenFlags;
@Nullable
protected T mCallbacks;
@Nullable
protected DeviceProfile mDp;
private final Rect mTaskSize = new Rect();
private boolean mIsGroupedTask = false;
public OverviewActionsView(Context context) {
this(context, null);
@@ -153,12 +155,21 @@ public class OverviewActionsView<T extends OverlayUICallbacks> extends FrameLayo
@Override
protected void onFinishInflate() {
super.onFinishInflate();
// Initialize 2 view containers: one for single tasks, one for grouped tasks.
// These will take up the same space on the screen and alternate visibility as needed.
mActionButtons = findViewById(R.id.action_buttons);
mMultiValueAlpha = new MultiValueAlpha(mActionButtons, NUM_ALPHAS);
mMultiValueAlpha.setUpdateVisibility(true);
mGroupActionButtons = findViewById(R.id.group_action_buttons);
// Initialize a list to set alpha on mActionButtons and mGroupActionButtons simultaneously.
mMultiValueAlphas[ACTIONS_ALPHAS] = new MultiValueAlpha(mActionButtons, NUM_ALPHAS);
mMultiValueAlphas[GROUP_ACTIONS_ALPHAS] =
new MultiValueAlpha(mGroupActionButtons, NUM_ALPHAS);
Arrays.stream(mMultiValueAlphas).forEach(a -> a.setUpdateVisibility(true));
mScreenshotButton = findViewById(R.id.action_screenshot);
mScreenshotButton.setOnClickListener(this);
// The screenshot button is implemented as a Button in launcher3 and NexusLauncher, but is
// an ImageButton in go launcher (does not share a common class with Button). Take care when
// casting this.
View screenshotButton = findViewById(R.id.action_screenshot);
screenshotButton.setOnClickListener(this);
mSplitButton = findViewById(R.id.action_split);
mSplitButton.setOnClickListener(this);
mSaveAppPairButton = findViewById(R.id.action_save_app_pair);
@@ -209,7 +220,7 @@ public class OverviewActionsView<T extends OverlayUICallbacks> extends FrameLayo
mHiddenFlags &= ~visibilityFlags;
}
boolean isHidden = mHiddenFlags != 0;
mMultiValueAlpha.get(INDEX_HIDDEN_FLAGS_ALPHA).setValue(isHidden ? 0 : 1);
setActionsAlpha(INDEX_HIDDEN_FLAGS_ALPHA, isHidden ? 0 : 1);
}
/**
@@ -236,12 +247,8 @@ public class OverviewActionsView<T extends OverlayUICallbacks> extends FrameLayo
* @param isGroupedTask True if the focused task is a grouped task.
*/
public void updateForGroupedTask(boolean isGroupedTask) {
// Update flags to see if split button should be hidden.
updateSplitButtonHiddenFlags(FLAG_MULTIPLE_TASKS_HIDE_SPLIT, isGroupedTask);
// Update flags to see if screenshot button should be hidden.
updateScreenshotButtonHiddenFlags(FLAG_MULTIPLE_TASKS_HIDE_SCREENSHOT, isGroupedTask);
// Update flags to see if save app pair button should be hidden.
updateAppPairButtonHiddenFlags(FLAG_SINGLE_TASK_HIDE_APP_PAIR, !isGroupedTask);
mIsGroupedTask = isGroupedTask;
updateActionButtonsVisibility();
}
/**
@@ -251,36 +258,30 @@ public class OverviewActionsView<T extends OverlayUICallbacks> extends FrameLayo
assert mDp != null;
// Update flags to see if split button should be hidden.
updateSplitButtonHiddenFlags(FLAG_SMALL_SCREEN_HIDE_SPLIT, !mDp.isTablet);
// Update flags to see if save app pair button should be hidden.
updateAppPairButtonHiddenFlags(FLAG_SMALL_SCREEN_HIDE_APP_PAIR, !mDp.isTablet);
updateActionButtonsVisibility();
}
private void updateActionButtonsVisibility() {
assert mDp != null;
boolean showSingleTaskActions = !mIsGroupedTask;
boolean showGroupActions = mIsGroupedTask && mDp.isTablet;
getActionsAlphas().get(INDEX_GROUPED_ALPHA).setValue(showSingleTaskActions ? 1 : 0);
getGroupActionsAlphas().get(INDEX_GROUPED_ALPHA).setValue(showGroupActions ? 1 : 0);
}
/**
* Updates flags to hide and show actions buttons for 1p/3p launchers.
*/
public void updateFor3pLauncher(boolean is3pLauncher) {
updateAppPairButtonHiddenFlags(FLAG_3P_LAUNCHER_HIDE_APP_PAIR, is3pLauncher);
getGroupActionsAlphas().get(INDEX_3P_LAUNCHER).setValue(is3pLauncher ? 0 : 1);
}
/**
* Updates the proper flags to indicate whether the "Screenshot" button should be hidden.
*
* @param flag The flag to update.
* @param enable Whether to enable the hidden flag: True will cause view to be hidden.
*/
private void updateScreenshotButtonHiddenFlags(@ScreenshotButtonHiddenFlags int flag,
boolean enable) {
if (mScreenshotButton == null) return;
if (enable) {
mScreenshotButtonHiddenFlags |= flag;
} else {
mScreenshotButtonHiddenFlags &= ~flag;
}
int desiredVisibility = mScreenshotButtonHiddenFlags == 0 ? VISIBLE : GONE;
if (mScreenshotButton.getVisibility() != desiredVisibility) {
mScreenshotButton.setVisibility(desiredVisibility);
mActionButtons.requestLayout();
}
private MultiValueAlpha getActionsAlphas() {
return mMultiValueAlphas[ACTIONS_ALPHAS];
}
private MultiValueAlpha getGroupActionsAlphas() {
return mMultiValueAlphas[GROUP_ACTIONS_ALPHAS];
}
/**
@@ -304,56 +305,36 @@ public class OverviewActionsView<T extends OverlayUICallbacks> extends FrameLayo
}
}
/**
* Updates the proper flags to indicate whether the "Save app pair" button should be disabled.
*
* @param flag The flag to update.
* @param enable Whether to enable the hidden flag: True will cause view to be hidden.
*/
private void updateAppPairButtonHiddenFlags(
@AppPairButtonHiddenFlags int flag, boolean enable) {
if (!FeatureFlags.enableAppPairs()) {
return;
}
if (mSaveAppPairButton == null) return;
if (enable) {
mAppPairButtonHiddenFlags |= flag;
} else {
mAppPairButtonHiddenFlags &= ~flag;
}
int desiredVisibility = mAppPairButtonHiddenFlags == 0 ? VISIBLE : GONE;
if (mSaveAppPairButton.getVisibility() != desiredVisibility) {
mSaveAppPairButton.setVisibility(desiredVisibility);
mActionButtons.requestLayout();
}
private void setActionsAlpha(int index, float value) {
Arrays.stream(mMultiValueAlphas).forEach(a -> a.get(index).setValue(value));
}
public MultiProperty getContentAlpha() {
return mMultiValueAlpha.get(INDEX_CONTENT_ALPHA);
public Consumer<Float> getContentAlphaSetter() {
return v -> setActionsAlpha(INDEX_CONTENT_ALPHA, v);
}
public MultiProperty getVisibilityAlpha() {
return mMultiValueAlpha.get(INDEX_VISIBILITY_ALPHA);
public Consumer<Float> getVisibilityAlphaSetter() {
return v -> setActionsAlpha(INDEX_VISIBILITY_ALPHA, v);
}
public MultiProperty getFullscreenAlpha() {
return mMultiValueAlpha.get(INDEX_FULLSCREEN_ALPHA);
public Consumer<Float> getFullscreenAlphaSetter() {
return v -> setActionsAlpha(INDEX_FULLSCREEN_ALPHA, v);
}
public MultiProperty getShareTargetAlpha() {
return mMultiValueAlpha.get(INDEX_SHARE_TARGET_ALPHA);
public Consumer<Float> getShareTargetAlphaSetter() {
return v -> setActionsAlpha(INDEX_SHARE_TARGET_ALPHA, v);
}
public MultiProperty getIndexScrollAlpha() {
return mMultiValueAlpha.get(INDEX_SCROLL_ALPHA);
public Consumer<Float> getIndexScrollAlphaSetter() {
return v -> setActionsAlpha(INDEX_SCROLL_ALPHA, v);
}
/**
* Returns the visibility of the overview actions buttons.
*/
public @Visibility int getActionsButtonVisibility() {
return mActionButtons.getVisibility();
public boolean areActionsButtonsVisible() {
return mActionButtons.getVisibility() == View.VISIBLE
|| mGroupActionButtons.getVisibility() == View.VISIBLE;
}
/**
@@ -366,10 +347,17 @@ public class OverviewActionsView<T extends OverlayUICallbacks> extends FrameLayo
/** Updates vertical margins for different navigation mode or configuration changes. */
public void updateVerticalMargin(NavigationMode mode) {
updateActionBarPosition(mActionButtons);
updateActionBarPosition(mGroupActionButtons);
}
/** Positions actions buttons according to device settings and insets. */
private void updateActionBarPosition(LinearLayout actionBar) {
if (mDp == null) {
return;
}
LayoutParams actionParams = (LayoutParams) mActionButtons.getLayoutParams();
LayoutParams actionParams = (LayoutParams) actionBar.getLayoutParams();
actionParams.setMargins(
actionParams.leftMargin, mDp.overviewActionsTopMarginPx,
actionParams.rightMargin, getBottomMargin());
@@ -35,6 +35,7 @@ import static com.android.launcher3.AbstractFloatingView.TYPE_TASK_MENU;
import static com.android.launcher3.AbstractFloatingView.getTopOpenViewWithType;
import static com.android.launcher3.BaseActivity.STATE_HANDLER_INVISIBILITY_FLAGS;
import static com.android.launcher3.Flags.enableGridOnlyOverview;
import static com.android.launcher3.Flags.enableRefactorTaskThumbnail;
import static com.android.launcher3.LauncherAnimUtils.SUCCESS_TRANSITION_PROGRESS;
import static com.android.launcher3.LauncherAnimUtils.VIEW_ALPHA;
import static com.android.launcher3.LauncherState.BACKGROUND_APP;
@@ -186,6 +187,7 @@ import com.android.quickstep.TaskViewUtils;
import com.android.quickstep.TopTaskTracker;
import com.android.quickstep.ViewUtils;
import com.android.quickstep.orientation.RecentsPagedOrientationHandler;
import com.android.quickstep.recents.viewmodel.RecentsViewData;
import com.android.quickstep.util.ActiveGestureErrorDetector;
import com.android.quickstep.util.ActiveGestureLog;
import com.android.quickstep.util.AnimUtils;
@@ -376,6 +378,9 @@ public abstract class RecentsView<CONTAINER_TYPE extends Context & RecentsViewCo
public void setValue(RecentsView view, float scale) {
view.setScaleX(scale);
view.setScaleY(scale);
if (enableRefactorTaskThumbnail()) {
view.mRecentsViewData.getScale().setValue(scale);
}
view.mLastComputedTaskStartPushOutDistance = null;
view.mLastComputedTaskEndPushOutDistance = null;
view.runActionOnRemoteHandles(new Consumer<RemoteTargetHandle>() {
@@ -446,6 +451,8 @@ public abstract class RecentsView<CONTAINER_TYPE extends Context & RecentsViewCo
private static final float FOREGROUND_SCRIM_TINT = 0.32f;
public final RecentsViewData mRecentsViewData = new RecentsViewData();
protected final RecentsOrientedState mOrientationState;
protected final BaseContainerInterface<STATE_TYPE, CONTAINER_TYPE> mSizeStrategy;
@Nullable
@@ -2012,6 +2019,9 @@ public abstract class RecentsView<CONTAINER_TYPE extends Context & RecentsViewCo
public void setFullscreenProgress(float fullscreenProgress) {
mFullscreenProgress = fullscreenProgress;
if (enableRefactorTaskThumbnail()) {
mRecentsViewData.getFullscreenProgress().setValue(mFullscreenProgress);
}
int taskCount = getTaskViewCount();
for (int i = 0; i < taskCount; i++) {
requireTaskViewAt(i).setFullscreenProgress(mFullscreenProgress);
@@ -2019,7 +2029,7 @@ public abstract class RecentsView<CONTAINER_TYPE extends Context & RecentsViewCo
mClearAllButton.setFullscreenProgress(fullscreenProgress);
// Fade out the actions view quickly (0.1 range)
mActionsView.getFullscreenAlpha().setValue(
mActionsView.getFullscreenAlphaSetter().accept(
mapToRange(fullscreenProgress, 0, 0.1f, 1f, 0f, LINEAR));
}
@@ -2270,8 +2280,8 @@ public abstract class RecentsView<CONTAINER_TYPE extends Context & RecentsViewCo
}
private void animateActionsViewAlpha(float alphaValue, long duration) {
mActionsViewAlphaAnimator = ObjectAnimator.ofFloat(
mActionsView.getVisibilityAlpha(), MULTI_PROPERTY_VALUE, alphaValue);
mActionsViewAlphaAnimator = ObjectAnimator.ofFloat(mActionsView.getVisibilityAlphaSetter(),
OverviewActionsView.FLOAT_SETTER, alphaValue);
mActionsViewAlphaAnimatorFinalValue = alphaValue;
mActionsViewAlphaAnimator.setDuration(duration);
// Set autocancel to prevent race-conditiony setting of alpha from other animations
@@ -2290,7 +2300,7 @@ public abstract class RecentsView<CONTAINER_TYPE extends Context & RecentsViewCo
mClearAllButton.onRecentsViewScroll(scroll, mOverviewGridEnabled);
// Clear all button alpha was set by the previous line.
mActionsView.getIndexScrollAlpha().setValue(1 - mClearAllButton.getScrollAlpha());
mActionsView.getIndexScrollAlphaSetter().accept(1 - mClearAllButton.getScrollAlpha());
}
@Override
@@ -2354,8 +2364,8 @@ public abstract class RecentsView<CONTAINER_TYPE extends Context & RecentsViewCo
// Update the task data for the in/visible children
for (int i = 0; i < getTaskViewCount(); i++) {
TaskView taskView = requireTaskViewAt(i);
TaskContainer[] containers = taskView.getTaskContainers();
if (containers.length == 0) {
List<TaskContainer> containers = taskView.getTaskContainers();
if (containers.isEmpty()) {
continue;
}
int index = indexOfChild(taskView);
@@ -2367,7 +2377,7 @@ public abstract class RecentsView<CONTAINER_TYPE extends Context & RecentsViewCo
}
if (visible) {
// Default update all non-null tasks, then remove running ones
List<Task> tasksToUpdate = Arrays.stream(containers).filter(Objects::nonNull)
List<Task> tasksToUpdate = containers.stream()
.map(TaskContainer::getTask)
.collect(Collectors.toCollection(ArrayList::new));
if (mTmpRunningTasks != null) {
@@ -4295,7 +4305,7 @@ public abstract class RecentsView<CONTAINER_TYPE extends Context & RecentsViewCo
int alphaInt = Math.round(alpha * 255);
mEmptyMessagePaint.setAlpha(alphaInt);
mEmptyIcon.setAlpha(alphaInt);
mActionsView.getContentAlpha().setValue(mContentAlpha);
mActionsView.getContentAlphaSetter().accept(mContentAlpha);
if (alpha > 0) {
setVisibility(VISIBLE);
@@ -4801,7 +4811,7 @@ public abstract class RecentsView<CONTAINER_TYPE extends Context & RecentsViewCo
boolean primaryTaskSelected = mSplitHiddenTaskView.getTaskIds()[0]
== mSplitSelectStateController.getInitialTaskId();
TaskContainer taskContainer = mSplitHiddenTaskView
.getTaskContainers()[primaryTaskSelected ? 1 : 0];
.getTaskContainers().get(primaryTaskSelected ? 1 : 0);
TaskThumbnailViewDeprecated thumbnail = taskContainer.getThumbnailView();
mSplitSelectStateController.getSplitAnimationController()
.addInitialSplitFromPair(taskContainer, builder,
@@ -18,8 +18,6 @@ package com.android.quickstep.views;
import static com.android.app.animation.Interpolators.EMPHASIZED;
import static com.android.launcher3.Flags.enableOverviewIconMenu;
import static com.android.launcher3.testing.shared.TestProtocol.TEST_TAPL_OVERVIEW_ACTIONS_MENU_FAILURE;
import static com.android.launcher3.testing.shared.TestProtocol.testLogD;
import static com.android.launcher3.util.MultiPropertyFactory.MULTI_PROPERTY_VALUE;
import static com.android.launcher3.util.SplitConfigurationOptions.STAGE_POSITION_BOTTOM_OR_RIGHT;
import static com.android.quickstep.views.TaskThumbnailViewDeprecated.DIM_ALPHA;
@@ -307,7 +305,6 @@ public class TaskMenuView extends AbstractFloatingView {
private void animateOpenOrClosed(boolean closing) {
if (mOpenCloseAnimator != null && mOpenCloseAnimator.isRunning()) {
testLogD(TEST_TAPL_OVERVIEW_ACTIONS_MENU_FAILURE, "getting canceled");
mOpenCloseAnimator.cancel();
}
mOpenCloseAnimator = new AnimatorSet();
@@ -364,14 +361,10 @@ public class TaskMenuView extends AbstractFloatingView {
iconAppChip.getMenuTranslationX(),
MULTI_PROPERTY_VALUE, closing ? 0 : -additionalTranslationX);
menuTranslationXAnim.setInterpolator(EMPHASIZED);
testLogD(TEST_TAPL_OVERVIEW_ACTIONS_MENU_FAILURE,
"TaskMenuView.java.animateOpenOrClosed: running translation animations");
mOpenCloseAnimator.playTogether(translationYAnim, translationXAnim,
menuTranslationXAnim, menuTranslationYAnim);
}
testLogD(TEST_TAPL_OVERVIEW_ACTIONS_MENU_FAILURE,
"TaskMenuView.java.animateOpenOrClosed: running animation 2");
mOpenCloseAnimator.playTogether(mRevealAnimator,
ObjectAnimator.ofFloat(
mTaskContainer.getThumbnailView(), DIM_ALPHA,
@@ -380,25 +373,14 @@ public class TaskMenuView extends AbstractFloatingView {
mOpenCloseAnimator.addListener(new AnimationSuccessListener() {
@Override
public void onAnimationStart(Animator animation) {
testLogD(TEST_TAPL_OVERVIEW_ACTIONS_MENU_FAILURE,
"TaskMenuView.java.animateOpenOrClosed: onAnimationStart");
setVisibility(VISIBLE);
if (closing && mOnClosingStartCallback != null) {
mOnClosingStartCallback.run();
}
}
@Override
public void onAnimationCancel(Animator animation) {
super.onAnimationCancel(animation);
testLogD(TEST_TAPL_OVERVIEW_ACTIONS_MENU_FAILURE,
"TaskMenuView.java.animateOpenOrClosed: onAnimationCancel");
}
@Override
public void onAnimationSuccess(Animator animator) {
testLogD(TEST_TAPL_OVERVIEW_ACTIONS_MENU_FAILURE,
"TaskMenuView.java.animateOpenOrClosed: onAnimationSuccess");
if (closing) {
closeComplete();
}
@@ -409,7 +391,6 @@ public class TaskMenuView extends AbstractFloatingView {
}
private void closeComplete() {
testLogD(TEST_TAPL_OVERVIEW_ACTIONS_MENU_FAILURE, "TaskMenuView.java.closeComplete");
mIsOpen = false;
mContainer.getDragLayer().removeView(this);
mRevealAnimator = null;
@@ -53,6 +53,7 @@ import com.android.launcher3.Utilities;
import com.android.launcher3.util.MainThreadInitializedObject;
import com.android.launcher3.util.SystemUiController;
import com.android.launcher3.util.SystemUiController.SystemUiControllerFlags;
import com.android.launcher3.util.ViewPool;
import com.android.quickstep.TaskOverlayFactory.TaskOverlay;
import com.android.quickstep.orientation.RecentsPagedOrientationHandler;
import com.android.quickstep.views.TaskView.FullscreenDrawParams;
@@ -66,7 +67,7 @@ import com.android.systemui.shared.recents.utilities.PreviewPositionHelper;
* @deprecated This class will be replaced by the new [TaskThumbnailView].
*/
@Deprecated
public class TaskThumbnailViewDeprecated extends View {
public class TaskThumbnailViewDeprecated extends View implements ViewPool.Reusable {
private static final MainThreadInitializedObject<FullscreenDrawParams> TEMP_PARAMS =
new MainThreadInitializedObject<>(FullscreenDrawParams::new);
@@ -606,4 +607,9 @@ public class TaskThumbnailViewDeprecated extends View {
}
return mThumbnailData.isRealSnapshot && !mTask.isLocked;
}
@Override
public void onRecycle() {
// Do nothing
}
}
@@ -107,6 +107,7 @@ import com.android.quickstep.TaskViewUtils;
import com.android.quickstep.orientation.RecentsPagedOrientationHandler;
import com.android.quickstep.task.thumbnail.TaskThumbnail;
import com.android.quickstep.task.thumbnail.TaskThumbnailView;
import com.android.quickstep.task.viewmodel.TaskViewData;
import com.android.quickstep.util.ActiveGestureLog;
import com.android.quickstep.util.BorderAnimator;
import com.android.quickstep.util.RecentsOrientedState;
@@ -326,6 +327,7 @@ public class TaskView extends FrameLayout implements Reusable {
}
};
public TaskViewData mTaskViewData = new TaskViewData();
protected TaskThumbnailViewDeprecated mTaskThumbnailViewDeprecated;
protected TaskThumbnailView mTaskThumbnailView;
protected TaskViewIcon mIconView;
@@ -368,7 +370,7 @@ public class TaskView extends FrameLayout implements Reusable {
private float mStableAlpha = 1;
private int mTaskViewId = -1;
protected TaskContainer[] mTaskContainers = new TaskContainer[0];
protected List<TaskContainer> mTaskContainers = Collections.emptyList();
private boolean mShowScreenshot;
private boolean mBorderEnabled;
@@ -496,7 +498,7 @@ public class TaskView extends FrameLayout implements Reusable {
public void notifyIsRunningTaskUpdated() {
// TODO(b/335649589): TaskView's VM will already have access to TaskThumbnailView's VM
// so there will be no need to access TaskThumbnailView's VM through the TaskThumbnailView
if (mTaskContainers.length > 0) {
if (!mTaskContainers.isEmpty()) {
bindTaskThumbnailView();
}
}
@@ -685,9 +687,9 @@ public class TaskView extends FrameLayout implements Reusable {
*/
public void bind(Task task, RecentsOrientedState orientedState) {
cancelPendingLoadTasks();
mTaskContainers = new TaskContainer[]{
mTaskContainers = Collections.singletonList(
new TaskContainer(task, mTaskThumbnailViewDeprecated, mIconView,
STAGE_POSITION_UNDEFINED, mDigitalWellBeingToast)};
STAGE_POSITION_UNDEFINED, mDigitalWellBeingToast));
if (enableRefactorTaskThumbnail()) {
bindTaskThumbnailView();
} else {
@@ -706,10 +708,10 @@ public class TaskView extends FrameLayout implements Reusable {
* Sets up an on-click listener and the visibility for show_windows icon on top of the task.
*/
public void setUpShowAllInstancesListener() {
if (mTaskContainers.length == 0) {
if (mTaskContainers.isEmpty()) {
return;
}
String taskPackageName = mTaskContainers[0].getTask().key.getPackageName();
String taskPackageName = mTaskContainers.get(0).getTask().key.getPackageName();
// icon of the top/left task
View showWindowsView = findViewById(R.id.show_windows);
@@ -752,9 +754,9 @@ public class TaskView extends FrameLayout implements Reusable {
}
/**
* Returns an array of all TaskContainers in the TaskView.
* Returns a list of all TaskContainers in the TaskView.
*/
public TaskContainer[] getTaskContainers() {
public List<TaskContainer> getTaskContainers() {
return mTaskContainers;
}
@@ -766,7 +768,7 @@ public class TaskView extends FrameLayout implements Reusable {
@Deprecated
@Nullable
public Task getFirstTask() {
return mTaskContainers.length > 0 ? mTaskContainers[0].getTask() : null;
return !mTaskContainers.isEmpty() ? mTaskContainers.get(0).getTask() : null;
}
/**
@@ -780,12 +782,12 @@ public class TaskView extends FrameLayout implements Reusable {
* Returns a copy of integer array containing taskIds of all tasks in the TaskView.
*/
public int[] getTaskIds() {
return Arrays.stream(mTaskContainers).mapToInt(
return mTaskContainers.stream().mapToInt(
container -> container.getTask().key.id).toArray();
}
public boolean containsMultipleTasks() {
return mTaskContainers.length > 1;
return mTaskContainers.size() > 1;
}
/**
@@ -794,7 +796,7 @@ public class TaskView extends FrameLayout implements Reusable {
*/
@Nullable
public TaskContainer getTaskContainerById(int taskId) {
return Arrays.stream(mTaskContainers).filter(
return mTaskContainers.stream().filter(
container -> container.getTask().key.id == taskId).findFirst().orElse(null);
}
@@ -805,7 +807,7 @@ public class TaskView extends FrameLayout implements Reusable {
*/
@Deprecated
public TaskThumbnailViewDeprecated getFirstThumbnailView() {
return mTaskContainers.length > 0 ? mTaskContainers[0].getThumbnailView()
return !mTaskContainers.isEmpty() ? mTaskContainers.get(0).getThumbnailView()
: mTaskThumbnailViewDeprecated;
}
@@ -826,7 +828,7 @@ public class TaskView extends FrameLayout implements Reusable {
}
public TaskThumbnailViewDeprecated[] getThumbnailViews() {
return Arrays.stream(mTaskContainers).map(
return mTaskContainers.stream().map(
TaskContainer::getThumbnailView).toArray(
TaskThumbnailViewDeprecated[]::new);
}
@@ -839,7 +841,7 @@ public class TaskView extends FrameLayout implements Reusable {
@Deprecated
@Nullable
public TaskViewIcon getFirstIconView() {
return mTaskContainers.length > 0 ? mTaskContainers[0].getIconView() : null;
return !mTaskContainers.isEmpty() ? mTaskContainers.get(0).getIconView() : null;
}
@Override
@@ -892,10 +894,10 @@ public class TaskView extends FrameLayout implements Reusable {
*/
protected boolean confirmSecondSplitSelectApp() {
int index = getLastSelectedChildTaskIndex();
if (index >= mTaskContainers.length) {
if (index >= mTaskContainers.size()) {
return false;
}
TaskContainer container = mTaskContainers[index];
TaskContainer container = mTaskContainers.get(index);
if (container != null) {
return getRecentsView().confirmSplitSelect(this, container.getTask(),
container.getIconView().getDrawable(), container.getThumbnailView(),
@@ -1217,9 +1219,8 @@ public class TaskView extends FrameLayout implements Reusable {
}
protected boolean showTaskMenuWithContainer(TaskViewIcon iconView) {
Optional<TaskContainer> menuContainer = Arrays.stream(
mTaskContainers).filter(
container -> container.getIconView() == iconView).findAny();
Optional<TaskContainer> menuContainer = mTaskContainers.stream().filter(
container -> container.getIconView() == iconView).findAny();
if (menuContainer.isEmpty()) {
return false;
}
@@ -1462,6 +1463,9 @@ public class TaskView extends FrameLayout implements Reusable {
scale *= mDismissScale;
setScaleX(scale);
setScaleY(scale);
if (enableRefactorTaskThumbnail()) {
mTaskViewData.getScale().setValue(scale);
}
updateSnapshotRadius();
}
@@ -1768,10 +1772,7 @@ public class TaskView extends FrameLayout implements Reusable {
progress = Utilities.boundToRange(progress, 0, 1);
mFullscreenProgress = progress;
mIconView.setVisibility(progress < 1 ? VISIBLE : INVISIBLE);
if (!enableRefactorTaskThumbnail()) {
// TODO(b/334826840) Add corner rounding to new TTV
mTaskThumbnailViewDeprecated.getTaskOverlay().setFullscreenProgress(progress);
}
mTaskThumbnailViewDeprecated.getTaskOverlay().setFullscreenProgress(progress);
RecentsView recentsView = mContainer.getOverviewPanel();
// Animate icons and DWB banners in/out, except in QuickSwitch state, when tiles are
@@ -1785,10 +1786,7 @@ public class TaskView extends FrameLayout implements Reusable {
protected void updateSnapshotRadius() {
updateCurrentFullscreenParams();
if (!enableRefactorTaskThumbnail()) {
// TODO(b/334826840) Add corner rounding to new TTV
mTaskThumbnailViewDeprecated.setFullscreenParams(mCurrentFullscreenParams);
}
mTaskThumbnailViewDeprecated.setFullscreenParams(mCurrentFullscreenParams);
}
void updateCurrentFullscreenParams() {
@@ -1799,8 +1797,8 @@ public class TaskView extends FrameLayout implements Reusable {
if (getRecentsView() == null) {
return;
}
fullscreenParams.setProgress(mFullscreenProgress, getRecentsView().getScaleX(),
getScaleX());
fullscreenParams.setProgress(
mFullscreenProgress, getRecentsView().getScaleX(), getScaleX());
}
/**
@@ -17,38 +17,63 @@
package com.android.quickstep.task.thumbnail
import androidx.test.ext.junit.runners.AndroidJUnit4
import com.android.quickstep.recents.viewmodel.RecentsViewData
import com.android.quickstep.task.viewmodel.TaskViewData
import com.android.systemui.shared.recents.model.Task
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.test.runTest
import org.junit.Test
import org.junit.runner.RunWith
@RunWith(AndroidJUnit4::class)
class TaskThumbnailViewModelTest {
private val systemUnderTest = TaskThumbnailViewModel()
private val recentsViewData = RecentsViewData()
private val taskViewData = TaskViewData()
private val systemUnderTest = TaskThumbnailViewModel(recentsViewData, taskViewData)
@Test
fun initialStateIsUninitialized() {
assertThat(systemUnderTest.uiState.value).isEqualTo(TaskThumbnailUiState.Uninitialized)
fun initialStateIsUninitialized() = runTest {
assertThat(systemUnderTest.uiState.first()).isEqualTo(TaskThumbnailUiState.Uninitialized)
}
@Test
fun bindRunningTask_thenStateIs_LiveTile() {
fun bindRunningTask_thenStateIs_LiveTile() = runTest {
val taskThumbnail = TaskThumbnail(Task(), isRunning = true)
systemUnderTest.bind(taskThumbnail)
assertThat(systemUnderTest.uiState.value).isEqualTo(TaskThumbnailUiState.LiveTile)
assertThat(systemUnderTest.uiState.first()).isEqualTo(TaskThumbnailUiState.LiveTile)
}
@Test
fun bindRunningTaskThenStoppedTask_thenStateIs_Uninitialized() {
fun setRecentsFullscreenProgress_thenProgressIsPassedThrough() = runTest {
recentsViewData.fullscreenProgress.value = 0.5f
assertThat(systemUnderTest.recentsFullscreenProgress.first()).isEqualTo(0.5f)
recentsViewData.fullscreenProgress.value = 0.6f
assertThat(systemUnderTest.recentsFullscreenProgress.first()).isEqualTo(0.6f)
}
@Test
fun setAncestorScales_thenScaleIsCalculated() = runTest {
recentsViewData.scale.value = 0.5f
taskViewData.scale.value = 0.6f
assertThat(systemUnderTest.inheritedScale.first()).isEqualTo(0.3f)
}
@Test
fun bindRunningTaskThenStoppedTask_thenStateIs_Uninitialized() = runTest {
// TODO(b/334825222): Change the expectation here when snapshot state is implemented
val task = Task()
val runningTask = TaskThumbnail(task, isRunning = true)
val stoppedTask = TaskThumbnail(task, isRunning = false)
systemUnderTest.bind(runningTask)
assertThat(systemUnderTest.uiState.value).isEqualTo(TaskThumbnailUiState.LiveTile)
assertThat(systemUnderTest.uiState.first()).isEqualTo(TaskThumbnailUiState.LiveTile)
systemUnderTest.bind(stoppedTask)
assertThat(systemUnderTest.uiState.value).isEqualTo(TaskThumbnailUiState.Uninitialized)
assertThat(systemUnderTest.uiState.first()).isEqualTo(TaskThumbnailUiState.Uninitialized)
}
}
@@ -35,6 +35,7 @@ import com.android.launcher3.util.TestUtil;
import com.android.launcher3.util.rule.ScreenRecordRule;
import org.junit.After;
import org.junit.Ignore;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -167,6 +168,7 @@ public class TaplPrivateSpaceTest extends AbstractQuickStepTest {
@Test
@ScreenRecordRule.ScreenRecord // b/334946529
@Ignore("b/339179262")
public void testPrivateSpaceLockingBehaviour() throws IOException {
// Scroll to the bottom of All Apps
executeOnLauncher(launcher -> launcher.getAppsView().resetAndScrollToPrivateSpaceHeader());
@@ -130,7 +130,7 @@ public class TaplTestsSplitscreen extends AbstractQuickStepTest {
.tapMenu()
.hasMenuItem("Save app pair"));
} else {
overview.getOverviewActions().assertHasAction("Save app pair");
overview.getOverviewGroupActions().assertHasAction("Save app pair");
}
}
@@ -91,7 +91,7 @@ class SplitAnimationControllerTest {
whenever(mockThumbnailView.thumbnail).thenReturn(mockBitmap)
whenever(mockTaskContainer.iconView).thenReturn(mockIconView)
whenever(mockIconView.drawable).thenReturn(mockTaskViewDrawable)
whenever(mockTaskView.taskContainers).thenReturn(Array(1) { mockTaskContainer })
whenever(mockTaskView.taskContainers).thenReturn(List(1) { mockTaskContainer })
whenever(splitSelectSource.drawable).thenReturn(mockSplitSourceDrawable)
whenever(splitSelectSource.view).thenReturn(mockSplitSourceView)
@@ -184,7 +184,7 @@ class SplitAnimationControllerTest {
whenever(mockTask.getKey()).thenReturn(mockTaskKey)
whenever(mockTaskKey.getId()).thenReturn(taskId)
whenever(mockSplitSelectStateController.initialTaskId).thenReturn(taskId)
whenever(mockGroupedTaskView.taskContainers).thenReturn(Array(1) { mockTaskContainer })
whenever(mockGroupedTaskView.taskContainers).thenReturn(List(1) { mockTaskContainer })
val splitAnimInitProps: SplitAnimationController.Companion.SplitAnimInitProps =
splitAnimationController.getFirstAnimInitViews(
{ mockGroupedTaskView },
+1 -1
View File
@@ -44,7 +44,7 @@
<string name="add_to_home_screen" msgid="9168649446635919791">"Dodaj na početni ekran"</string>
<string name="added_to_home_screen_accessibility_text" msgid="4451545765448884415">"Dodali ste vidžet <xliff:g id="WIDGET_NAME">%1$s</xliff:g> na početni ekran"</string>
<string name="suggested_widgets_header_title" msgid="1844314680798145222">"Predlozi"</string>
<string name="productivity_widget_recommendation_category_label" msgid="3811812719618323750">"Neophodne aplikacije"</string>
<string name="productivity_widget_recommendation_category_label" msgid="3811812719618323750">"Osnovno"</string>
<string name="news_widget_recommendation_category_label" msgid="6756167867113741310">"Novosti i časopisi"</string>
<string name="social_and_entertainment_widget_recommendation_category_label" msgid="2923840997302308191">"Zona za opuštanje"</string>
<string name="entertainment_widget_recommendation_category_label" msgid="3973107268630717874">"Zabava"</string>
+1 -1
View File
@@ -44,7 +44,7 @@
<string name="add_to_home_screen" msgid="9168649446635919791">"Přidat na plochu"</string>
<string name="added_to_home_screen_accessibility_text" msgid="4451545765448884415">"Widget <xliff:g id="WIDGET_NAME">%1$s</xliff:g> byl přidán na plochu"</string>
<string name="suggested_widgets_header_title" msgid="1844314680798145222">"Návrhy"</string>
<string name="productivity_widget_recommendation_category_label" msgid="3811812719618323750">"Základní"</string>
<string name="productivity_widget_recommendation_category_label" msgid="3811812719618323750">"Nejdůležitější aplikace"</string>
<string name="news_widget_recommendation_category_label" msgid="6756167867113741310">"Zprávy a časopisy"</string>
<string name="social_and_entertainment_widget_recommendation_category_label" msgid="2923840997302308191">"Vaše klidová zóna"</string>
<string name="entertainment_widget_recommendation_category_label" msgid="3973107268630717874">"Zábava"</string>
+1 -1
View File
@@ -48,7 +48,7 @@
<string name="news_widget_recommendation_category_label" msgid="6756167867113741310">"Noticias y revistas"</string>
<string name="social_and_entertainment_widget_recommendation_category_label" msgid="2923840997302308191">"Zona de descanso"</string>
<string name="entertainment_widget_recommendation_category_label" msgid="3973107268630717874">"Entretenimiento"</string>
<string name="social_widget_recommendation_category_label" msgid="689147679536384717">"Social"</string>
<string name="social_widget_recommendation_category_label" msgid="689147679536384717">"Redes sociales"</string>
<string name="fitness_widget_recommendation_category_label" msgid="2756483898236585324">"Salud y bienestar"</string>
<string name="weather_widget_recommendation_category_label" msgid="3059715991930798039">"Clima"</string>
<string name="others_widget_recommendation_category_label" msgid="5555987036267226245">"Sugerencias para ti"</string>
+1 -1
View File
@@ -48,7 +48,7 @@
<string name="news_widget_recommendation_category_label" msgid="6756167867113741310">"Noticias y revistas"</string>
<string name="social_and_entertainment_widget_recommendation_category_label" msgid="2923840997302308191">"Tu zona de descanso"</string>
<string name="entertainment_widget_recommendation_category_label" msgid="3973107268630717874">"Entretenimiento"</string>
<string name="social_widget_recommendation_category_label" msgid="689147679536384717">"Social"</string>
<string name="social_widget_recommendation_category_label" msgid="689147679536384717">"Redes sociales"</string>
<string name="fitness_widget_recommendation_category_label" msgid="2756483898236585324">"Salud y actividad física"</string>
<string name="weather_widget_recommendation_category_label" msgid="3059715991930798039">"El tiempo"</string>
<string name="others_widget_recommendation_category_label" msgid="5555987036267226245">"Sugerencias para ti"</string>
+1 -1
View File
@@ -44,7 +44,7 @@
<string name="add_to_home_screen" msgid="9168649446635919791">"Ajouter à l\'écran d\'accueil"</string>
<string name="added_to_home_screen_accessibility_text" msgid="4451545765448884415">"Widget <xliff:g id="WIDGET_NAME">%1$s</xliff:g> ajouté à l\'écran d\'accueil"</string>
<string name="suggested_widgets_header_title" msgid="1844314680798145222">"Suggestions"</string>
<string name="productivity_widget_recommendation_category_label" msgid="3811812719618323750">"Les bases"</string>
<string name="productivity_widget_recommendation_category_label" msgid="3811812719618323750">"Indispensables"</string>
<string name="news_widget_recommendation_category_label" msgid="6756167867113741310">"Actualités et magazines"</string>
<string name="social_and_entertainment_widget_recommendation_category_label" msgid="2923840997302308191">"Votre espace détente"</string>
<string name="entertainment_widget_recommendation_category_label" msgid="3973107268630717874">"Divertissement"</string>
+1 -1
View File
@@ -44,7 +44,7 @@
<string name="add_to_home_screen" msgid="9168649446635919791">"Dodaj do ekranu głównego"</string>
<string name="added_to_home_screen_accessibility_text" msgid="4451545765448884415">"Widżet <xliff:g id="WIDGET_NAME">%1$s</xliff:g> został dodany do ekranu głównego"</string>
<string name="suggested_widgets_header_title" msgid="1844314680798145222">"Sugestie"</string>
<string name="productivity_widget_recommendation_category_label" msgid="3811812719618323750">"Niezbędne"</string>
<string name="productivity_widget_recommendation_category_label" msgid="3811812719618323750">"Najbardziej przydatne"</string>
<string name="news_widget_recommendation_category_label" msgid="6756167867113741310">"Wiadomości i czasopisma"</string>
<string name="social_and_entertainment_widget_recommendation_category_label" msgid="2923840997302308191">"Strefa relaksu"</string>
<string name="entertainment_widget_recommendation_category_label" msgid="3973107268630717874">"Rozrywka"</string>
+1 -1
View File
@@ -44,7 +44,7 @@
<string name="add_to_home_screen" msgid="9168649446635919791">"Додај на почетни екран"</string>
<string name="added_to_home_screen_accessibility_text" msgid="4451545765448884415">"Додали сте виџет <xliff:g id="WIDGET_NAME">%1$s</xliff:g> на почетни екран"</string>
<string name="suggested_widgets_header_title" msgid="1844314680798145222">"Предлози"</string>
<string name="productivity_widget_recommendation_category_label" msgid="3811812719618323750">"Неопходне апликације"</string>
<string name="productivity_widget_recommendation_category_label" msgid="3811812719618323750">"Основно"</string>
<string name="news_widget_recommendation_category_label" msgid="6756167867113741310">"Новости и часописи"</string>
<string name="social_and_entertainment_widget_recommendation_category_label" msgid="2923840997302308191">"Зона за опуштање"</string>
<string name="entertainment_widget_recommendation_category_label" msgid="3973107268630717874">"Забава"</string>
+1 -1
View File
@@ -44,7 +44,7 @@
<string name="add_to_home_screen" msgid="9168649446635919791">"添加到主屏幕"</string>
<string name="added_to_home_screen_accessibility_text" msgid="4451545765448884415">"已将“<xliff:g id="WIDGET_NAME">%1$s</xliff:g>”微件添加到主屏幕"</string>
<string name="suggested_widgets_header_title" msgid="1844314680798145222">"建议"</string>
<string name="productivity_widget_recommendation_category_label" msgid="3811812719618323750">"必备"</string>
<string name="productivity_widget_recommendation_category_label" msgid="3811812719618323750">"必备之选"</string>
<string name="news_widget_recommendation_category_label" msgid="6756167867113741310">"新闻与杂志"</string>
<string name="social_and_entertainment_widget_recommendation_category_label" msgid="2923840997302308191">"您的休闲区"</string>
<string name="entertainment_widget_recommendation_category_label" msgid="3973107268630717874">"娱乐"</string>
+25
View File
@@ -70,6 +70,7 @@ import android.util.TypedValue;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewConfiguration;
import android.view.ViewGroup;
import android.view.animation.Interpolator;
import androidx.annotation.ChecksSdkIntAtLeast;
@@ -104,6 +105,7 @@ import java.lang.reflect.Method;
import java.util.Collections;
import java.util.List;
import java.util.Locale;
import java.util.function.Predicate;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
@@ -835,4 +837,27 @@ public final class Utilities {
// No-Op
}
}
/**
* Does a depth-first search through the View hierarchy starting at root, to find a view that
* matches the predicate. Returns null if no View was found. View has a findViewByPredicate
* member function but it is currently a @hide API.
*/
@Nullable
public static <T extends View> T findViewByPredicate(@NonNull View root,
@NonNull Predicate<View> predicate) {
if (predicate.test(root)) {
return (T) root;
}
if (root instanceof ViewGroup parent) {
int count = parent.getChildCount();
for (int i = 0; i < count; i++) {
View view = findViewByPredicate(parent.getChildAt(i), predicate);
if (view != null) {
return (T) view;
}
}
}
return null;
}
}
@@ -17,6 +17,8 @@ package com.android.launcher3.widget;
import static com.android.app.animation.Interpolators.EMPHASIZED;
import static com.android.launcher3.Flags.enableWidgetTapToAdd;
import static com.android.launcher3.LauncherState.NORMAL;
import static com.android.launcher3.anim.AnimatorListeners.forSuccessCallback;
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_WIDGET_ADD_BUTTON_TAP;
import android.content.Context;
@@ -42,6 +44,7 @@ import com.android.launcher3.Insettable;
import com.android.launcher3.Launcher;
import com.android.launcher3.PendingAddItemInfo;
import com.android.launcher3.R;
import com.android.launcher3.model.WidgetItem;
import com.android.launcher3.popup.PopupDataProvider;
import com.android.launcher3.testing.TestLogging;
import com.android.launcher3.testing.shared.TestProtocol;
@@ -74,6 +77,7 @@ public abstract class BaseWidgetSheet extends AbstractSlideInView<BaseActivity>
private boolean mDisableNavBarScrim = false;
@Nullable private WidgetCell mWidgetCellWithAddButton = null;
@Nullable private WidgetItem mLastSelectedWidgetItem = null;
public BaseWidgetSheet(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
@@ -161,6 +165,11 @@ public abstract class BaseWidgetSheet extends AbstractSlideInView<BaseActivity>
}
mWidgetCellWithAddButton = mWidgetCellWithAddButton != wc ? wc : null;
if (mWidgetCellWithAddButton != null) {
mLastSelectedWidgetItem = mWidgetCellWithAddButton.getWidgetItem();
} else {
mLastSelectedWidgetItem = null;
}
} else {
mActivityContext.getItemOnClickListener().onClick(wc);
}
@@ -174,7 +183,8 @@ public abstract class BaseWidgetSheet extends AbstractSlideInView<BaseActivity>
}
/**
* Click handler for tap to add button.
* Click handler for tap to add button. This handler assumes we are in the Launcher activity and
* should not be used when the widget sheet is displayed elsewhere.
*/
private void addWidget(@NonNull PendingAddItemInfo info) {
// Using a boolean flag here to make sure the callback is only run once. This should never
@@ -182,19 +192,23 @@ public abstract class BaseWidgetSheet extends AbstractSlideInView<BaseActivity>
// needed.
final AtomicBoolean hasRun = new AtomicBoolean(false);
addOnCloseListener(() -> {
if (!hasRun.get()) {
Launcher.getLauncher(mActivityContext).getAccessibilityDelegate().addToWorkspace(
info, /*accessibility=*/ false,
if (hasRun.get()) return;
hasRun.set(true);
// Going to NORMAL state will also dismiss the All Apps view if it is showing.
Launcher launcher = Launcher.getLauncher(mActivityContext);
launcher.getStateManager().goToState(NORMAL, forSuccessCallback(() -> {
launcher.getAccessibilityDelegate().addToWorkspace(info,
/*accessibility=*/ false,
/*finishCallback=*/ (success) -> {
mActivityContext.getStatsLogManager()
.logger()
.withItemInfo(info)
.log(LAUNCHER_WIDGET_ADD_BUTTON_TAP);
});
hasRun.set(true);
}
}));
});
handleClose(true);
close(/* animate= */ true);
}
/**
@@ -243,6 +257,14 @@ public abstract class BaseWidgetSheet extends AbstractSlideInView<BaseActivity>
return 0;
}
/**
* Returns the component of the widget that is currently showing an add button, if any.
*/
@Nullable
protected WidgetItem getLastSelectedWidgetItem() {
return mLastSelectedWidgetItem;
}
@Override
public boolean onLongClick(View v) {
TestLogging.recordEvent(TestProtocol.SEQUENCE_MAIN, "Widgets.onLongClick");
@@ -503,6 +503,15 @@ public class WidgetCell extends LinearLayout {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
super.onLayout(changed, l, t, r, b);
if (changed && isShowingAddButton()) {
post(this::setupIconOrTextButton);
}
}
/**
* Loads a high resolution package icon to show next to the widget title.
*/
@@ -627,4 +636,19 @@ public class WidgetCell extends LinearLayout {
set.playSequentially(hideAnim, showAnim);
set.start();
}
/**
* Returns true if this WidgetCell is displaying the same item as info.
*/
public boolean matchesItem(WidgetItem info) {
if (info == null || mItem == null) return false;
if (info.widgetInfo != null && mItem.widgetInfo != null) {
return info.widgetInfo.getUser().equals(mItem.widgetInfo.getUser())
&& info.widgetInfo.getComponent().equals(mItem.widgetInfo.getComponent());
} else if (info.activityInfo != null && mItem.activityInfo != null) {
return info.activityInfo.getUser().equals(mItem.activityInfo.getUser())
&& info.activityInfo.getComponent().equals(mItem.activityInfo.getComponent());
}
return false;
}
}
@@ -142,6 +142,9 @@ public class WidgetsBottomSheet extends BaseWidgetSheet {
row.forEach(widgetItem -> {
WidgetCell widget = addItemCell(tableRow);
widget.applyFromCellItem(widgetItem);
if (widget.matchesItem(getLastSelectedWidgetItem())) {
widget.callOnClick();
}
});
widgetsTable.addView(tableRow);
});
@@ -29,6 +29,7 @@ import android.util.AttributeSet;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewParent;
import android.widget.FrameLayout;
import android.widget.LinearLayout;
@@ -40,6 +41,7 @@ import androidx.annotation.Px;
import com.android.launcher3.DeviceProfile;
import com.android.launcher3.R;
import com.android.launcher3.Utilities;
import com.android.launcher3.model.WidgetItem;
import com.android.launcher3.model.data.PackageItemInfo;
import com.android.launcher3.recyclerview.ViewHolderBinder;
import com.android.launcher3.util.PackageUserKey;
@@ -194,15 +196,21 @@ public class WidgetsTwoPaneSheet extends WidgetsFullSheet {
layoutParams.width = 0;
}
layoutParams.weight = layoutParams.width == 0 ? 0.33F : 0;
leftPane.setLayoutParams(layoutParams);
requestApplyInsets();
if (mSelectedHeader != null) {
if (mSelectedHeader.equals(mSuggestedWidgetsPackageUserKey)) {
mSuggestedWidgetsHeader.callOnClick();
} else {
getHeaderChangeListener().onHeaderChanged(mSelectedHeader);
post(() -> {
// The following calls all trigger requestLayout, so we post them to avoid
// calling requestLayout during a layout pass. This also fixes the related warnings
// in logcat.
leftPane.setLayoutParams(layoutParams);
requestApplyInsets();
if (mSelectedHeader != null) {
if (mSelectedHeader.equals(mSuggestedWidgetsPackageUserKey)) {
mSuggestedWidgetsHeader.callOnClick();
} else {
getHeaderChangeListener().onHeaderChanged(mSelectedHeader);
}
}
}
});
}
}
@@ -222,6 +230,9 @@ public class WidgetsTwoPaneSheet extends WidgetsFullSheet {
if (mSuggestedWidgetsContainer == null && mRecommendedWidgetsCount > 0) {
setupSuggestedWidgets(LayoutInflater.from(getContext()));
mSuggestedWidgetsHeader.callOnClick();
} else if (mSelectedHeader.equals(mSuggestedWidgetsPackageUserKey)) {
// Reselect widget if we are reloading recommendations while it is currently showing.
selectWidgetCell(mWidgetRecommendationsContainer, getLastSelectedWidgetItem());
}
}
@@ -269,6 +280,16 @@ public class WidgetsTwoPaneSheet extends WidgetsFullSheet {
mRightPaneScrollView.setScrollY(0);
mRightPane.setAccessibilityPaneTitle(suggestionsRightPaneTitle);
mSuggestedWidgetsPackageUserKey = PackageUserKey.fromPackageItemInfo(packageItemInfo);
final boolean isChangingHeaders =
!mSelectedHeader.equals(mSuggestedWidgetsPackageUserKey);
if (isChangingHeaders) {
// If switching from another header, unselect any WidgetCells. This is necessary
// because we do not clear/recycle the WidgetCells in the recommendations container
// when the header is clicked, only when onRecommendationsBound is called. That
// means a WidgetCell in the recommendations container may still be selected from
// the last time the recommendations were shown.
unselectWidgetCell(mWidgetRecommendationsContainer, getLastSelectedWidgetItem());
}
mSelectedHeader = mSuggestedWidgetsPackageUserKey;
});
mSuggestedWidgetsContainer.addView(mSuggestedWidgetsHeader);
@@ -357,6 +378,8 @@ public class WidgetsTwoPaneSheet extends WidgetsFullSheet {
return new HeaderChangeListener() {
@Override
public void onHeaderChanged(@NonNull PackageUserKey selectedHeader) {
final boolean isSameHeader = mSelectedHeader != null
&& mSelectedHeader.equals(selectedHeader);
mSelectedHeader = selectedHeader;
WidgetsListContentEntry contentEntry = mActivityContext.getPopupDataProvider()
.getSelectedAppWidgets(selectedHeader);
@@ -384,11 +407,20 @@ public class WidgetsTwoPaneSheet extends WidgetsFullSheet {
contentEntryToBind,
ViewHolderBinder.POSITION_FIRST | ViewHolderBinder.POSITION_LAST,
Collections.EMPTY_LIST);
if (isSameHeader) {
// Reselect the last selected widget if we are reloading the same header.
selectWidgetCell(widgetsRowViewHolder.tableContainer,
getLastSelectedWidgetItem());
}
widgetsRowViewHolder.mDataCallback = data -> {
mWidgetsListTableViewHolderBinder.bindViewHolder(widgetsRowViewHolder,
contentEntryToBind,
ViewHolderBinder.POSITION_FIRST | ViewHolderBinder.POSITION_LAST,
Collections.singletonList(data));
if (isSameHeader) {
selectWidgetCell(widgetsRowViewHolder.tableContainer,
getLastSelectedWidgetItem());
}
};
mRightPane.removeAllViews();
mRightPane.addView(widgetsRowViewHolder.itemView);
@@ -401,6 +433,24 @@ public class WidgetsTwoPaneSheet extends WidgetsFullSheet {
};
}
private static void selectWidgetCell(ViewGroup parent, WidgetItem item) {
if (parent == null || item == null) return;
WidgetCell cell = Utilities.findViewByPredicate(parent, v -> v instanceof WidgetCell wc
&& wc.matchesItem(item));
if (cell != null && !cell.isShowingAddButton()) {
cell.callOnClick();
}
}
private static void unselectWidgetCell(ViewGroup parent, WidgetItem item) {
if (parent == null || item == null) return;
WidgetCell cell = Utilities.findViewByPredicate(parent, v -> v instanceof WidgetCell wc
&& wc.matchesItem(item));
if (cell != null && cell.isShowingAddButton()) {
cell.hideAddButton(/* animate= */ false);
}
}
@Override
public void setInsets(Rect insets) {
super.setInsets(insets);
+1
View File
@@ -105,6 +105,7 @@ android_library {
android_library {
name: "Launcher3TestResources",
resource_dirs: ["res"],
asset_dirs: ["assets"],
// TODO(b/319712088): re-enable use_resource_processor
use_resource_processor: false,
}
@@ -175,7 +175,6 @@ public final class TestProtocol {
public static final String OVERVIEW_OVER_HOME = "b/279059025";
public static final String UIOBJECT_STALE_ELEMENT = "b/319501259";
public static final String TEST_DRAG_APP_ICON_TO_MULTIPLE_WORKSPACES_FAILURE = "b/326908466";
public static final String TEST_TAPL_OVERVIEW_ACTIONS_MENU_FAILURE = "b/326073471";
public static final String WIDGET_CONFIG_NULL_EXTRA_INTENT = "b/324419890";
public static final String ACTIVITY_NOT_RESUMED_AFTER_BACK = "b/322823209";
public static final String OVERVIEW_SELECT_TOOLTIP_MISALIGNED = "b/332485341";
@@ -22,6 +22,8 @@ import android.util.Log
import android.view.View
import androidx.core.view.get
import androidx.test.core.app.ApplicationProvider
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.launcher3.CellLayout
import com.android.launcher3.celllayout.board.CellLayoutBoard
import com.android.launcher3.celllayout.board.IconPoint
@@ -34,6 +36,7 @@ import java.util.Random
import org.junit.Assert
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
private class HotseatReorderTestCase(
val startBoard: CellLayoutBoard,
@@ -44,6 +47,8 @@ private class HotseatReorderTestCase(
}
}
@SmallTest
@RunWith(AndroidJUnit4::class)
class HotseatReorderUnitTest {
private val applicationContext: Context =
@@ -141,11 +141,14 @@ class ReorderPreviewAnimationTest {
ReorderPreviewAnimation.MODE_PREVIEW,
AnimationValues(dx = 0, dy = 0, scale = 100)
)
testAnimationAtGivenProgress(
PREVIEW_DURATION * 99,
ReorderPreviewAnimation.MODE_PREVIEW,
AnimationValues(dx = 5, dy = -10, scale = 96)
)
// (b/339313407) Temporarily disable this test as the behavior is
// inconsistent between Soong & Gradle builds.
//
// testAnimationAtGivenProgress(
// PREVIEW_DURATION * 99,
// ReorderPreviewAnimation.MODE_PREVIEW,
// AnimationValues(dx = 5, dy = -10, scale = 96)
// )
testAnimationAtGivenProgress(
PREVIEW_DURATION * 98,
ReorderPreviewAnimation.MODE_PREVIEW,
@@ -358,6 +358,21 @@ public class BaseOverview extends LauncherInstrumentation.VisibleContainer {
}
}
/**
* Gets Overview Actions specific to grouped tasks.
*
* @return The Overview group actions bar
*/
@NonNull
public OverviewActions getOverviewGroupActions() {
try (LauncherInstrumentation.Closable c = mLauncher.addContextLayer(
"want to get overview group actions")) {
verifyActiveContainer();
UiObject2 groupActions = mLauncher.waitForOverviewObject("group_action_buttons");
return new OverviewActions(groupActions, mLauncher);
}
}
/**
* Returns if clear all button is visible.
*/
@@ -449,10 +464,18 @@ public class BaseOverview extends LauncherInstrumentation.VisibleContainer {
private void verifyActionsViewVisibility() {
try (LauncherInstrumentation.Closable c = mLauncher.addContextLayer(
"want to assert overview actions view visibility")) {
boolean isTablet = mLauncher.isTablet();
OverviewTask task = isTablet ? getFocusedTaskForTablet() : getCurrentTask();
if (isActionsViewVisible()) {
mLauncher.waitForOverviewObject("action_buttons");
if (task.isTaskSplit()) {
mLauncher.waitForOverviewObject("group_action_buttons");
} else {
mLauncher.waitForOverviewObject("action_buttons");
}
} else {
mLauncher.waitUntilOverviewObjectGone("action_buttons");
mLauncher.waitUntilOverviewObjectGone("group_action_buttons");
}
}
}
@@ -108,7 +108,7 @@ import java.util.stream.Collectors;
public final class LauncherInstrumentation {
private static final String TAG = "Tapl";
private static final int ZERO_BUTTON_STEPS_FROM_BACKGROUND_TO_HOME = 15;
private static final int ZERO_BUTTON_STEPS_FROM_BACKGROUND_TO_HOME = 5;
private static final int GESTURE_STEP_MS = 16;
static final Pattern EVENT_PILFER_POINTERS = Pattern.compile("pilferPointers");