Snap for 12616459 from adfd002972 to 25Q1-release

Change-Id: Ib8c90391dd1fc473562f8a67ad1def1b614c9da1
This commit is contained in:
Android Build Coastguard Worker
2024-11-07 00:25:04 +00:00
49 changed files with 590 additions and 485 deletions
+14
View File
@@ -310,6 +310,13 @@ flag {
bug: "346408388"
}
flag {
name: "all_apps_sheet_for_handheld"
namespace: "launcher"
description: "All Apps will be presented on a bottom sheet in handheld mode"
bug: "374186088"
}
flag {
name: "multiline_search_bar"
namespace: "launcher"
@@ -506,3 +513,10 @@ flag {
description: "Enable launcher app contrast tiles."
bug: "341217082"
}
flag {
name: "msdl_feedback"
namespace: "launcher"
description: "Enable MSDL feedback for Launcher interactions"
bug: "377496684"
}
-2
View File
@@ -26,6 +26,4 @@
<!-- Turn on work apps button -->
<color name="work_turn_on_stroke">?attr/materialColorPrimary</color>
<color name="work_fab_bg_color">?attr/materialColorPrimaryFixedDim</color>
<color name="work_fab_icon_color">?attr/materialColorOnPrimaryFixed</color>
</resources>
-2
View File
@@ -94,6 +94,4 @@
<!-- Turn on work apps button -->
<color name="work_turn_on_stroke">?attr/materialColorPrimary</color>
<color name="work_fab_bg_color">?attr/materialColorPrimaryFixedDim</color>
<color name="work_fab_icon_color">?attr/materialColorOnPrimaryFixed</color>
</resources>
-1
View File
@@ -34,7 +34,6 @@
<string name="taskbar_view_callbacks_factory_class" translatable="false">com.android.launcher3.taskbar.TaskbarViewCallbacksFactory</string>
<string name="launcher_restore_event_logger_class" translatable="false">com.android.quickstep.LauncherRestoreEventLoggerImpl</string>
<string name="taskbar_edu_tooltip_controller_class" translatable="false">com.android.launcher3.taskbar.TaskbarEduTooltipController</string>
<string name="contextual_edu_manager_class" translatable="false">com.android.quickstep.contextualeducation.SystemContextualEduStatsManager</string>
<string name="nav_handle_long_press_handler_class" translatable="false"></string>
<string name="contextual_search_invoker_class" translatable="false"></string>
<string name="contextual_search_state_manager_class" translatable="false"></string>
@@ -560,25 +560,25 @@ public class TaskbarManager {
}
}
public void checkNavBarModes() {
public void checkNavBarModes(int displayId) {
if (mTaskbarActivityContext != null) {
mTaskbarActivityContext.checkNavBarModes();
}
}
public void finishBarAnimations() {
public void finishBarAnimations(int displayId) {
if (mTaskbarActivityContext != null) {
mTaskbarActivityContext.finishBarAnimations();
}
}
public void touchAutoDim(boolean reset) {
public void touchAutoDim(int displayId, boolean reset) {
if (mTaskbarActivityContext != null) {
mTaskbarActivityContext.touchAutoDim(reset);
}
}
public void transitionTo(@BarTransitions.TransitionMode int barMode,
public void transitionTo(int displayId, @BarTransitions.TransitionMode int barMode,
boolean animate) {
if (mTaskbarActivityContext != null) {
mTaskbarActivityContext.transitionTo(barMode, animate);
@@ -118,7 +118,7 @@ public class AllAppsState extends LauncherState {
@Override
public ScaleAndTranslation getHotseatScaleAndTranslation(Launcher launcher) {
if (launcher.getDeviceProfile().isTablet) {
if (launcher.getDeviceProfile().shouldShowAllAppsOnSheet()) {
return getWorkspaceScaleAndTranslation(launcher);
} else {
ScaleAndTranslation overviewScaleAndTranslation = LauncherState.OVERVIEW
@@ -133,7 +133,7 @@ public class AllAppsState extends LauncherState {
@Override
protected <DEVICE_PROFILE_CONTEXT extends Context & ActivityContext>
float getDepthUnchecked(DEVICE_PROFILE_CONTEXT context) {
if (context.getDeviceProfile().isTablet) {
if (context.getDeviceProfile().shouldShowAllAppsOnSheet()) {
return context.getDeviceProfile().bottomSheetDepth;
} else {
// The scrim fades in at approximately 50% of the swipe gesture.
@@ -154,7 +154,7 @@ public class AllAppsState extends LauncherState {
return new PageAlphaProvider(DECELERATE_2) {
@Override
public float getPageAlpha(int pageIndex) {
return launcher.getDeviceProfile().isTablet
return launcher.getDeviceProfile().shouldShowAllAppsOnSheet()
? superPageAlphaProvider.getPageAlpha(pageIndex)
: 0;
}
@@ -164,8 +164,8 @@ public class AllAppsState extends LauncherState {
@Override
public int getVisibleElements(Launcher launcher) {
int elements = ALL_APPS_CONTENT | FLOATING_SEARCH_BAR;
// Only add HOTSEAT_ICONS for tablets in ALL_APPS state.
if (launcher.getDeviceProfile().isTablet) {
// When All Apps is presented on a bottom sheet, HOTSEAT_ICONS are visible.
if (launcher.getDeviceProfile().shouldShowAllAppsOnSheet()) {
elements |= HOTSEAT_ICONS;
}
return elements;
@@ -202,7 +202,7 @@ public class AllAppsState extends LauncherState {
@Override
public int getWorkspaceScrimColor(Launcher launcher) {
return launcher.getDeviceProfile().isTablet
return launcher.getDeviceProfile().shouldShowAllAppsOnSheet()
? launcher.getResources().getColor(R.color.widgets_picker_scrim)
: Themes.getAttrColor(launcher, R.attr.allAppsScrimColor);
}
@@ -201,8 +201,7 @@ public class TaskAnimationManager implements RecentsAnimationCallbacks.RecentsAn
// Only finish if the end target is RECENTS. Otherwise, if the target is
// NEW_TASK, startActivityFromRecents will be skipped.
if (mLastGestureState.getEndTarget() == RECENTS) {
finishRunningRecentsAnimation(false /* toHome */,
true /* forceFinish */, null /* forceFinishCb */);
finishRunningRecentsAnimation(false /* toHome */);
}
});
}
@@ -342,36 +342,36 @@ public class TouchInteractionService extends Service {
@BinderThread
@Override
public void checkNavBarModes() {
MAIN_EXECUTOR.execute(() -> executeForTouchInteractionService(tis ->
executeForTaskbarManager(TaskbarManager::checkNavBarModes)
));
}
@BinderThread
@Override
public void finishBarAnimations() {
MAIN_EXECUTOR.execute(() -> executeForTouchInteractionService(tis ->
executeForTaskbarManager(TaskbarManager::finishBarAnimations)
));
}
@BinderThread
@Override
public void touchAutoDim(boolean reset) {
MAIN_EXECUTOR.execute(() -> executeForTouchInteractionService(tis ->
executeForTaskbarManager(taskbarManager -> taskbarManager.touchAutoDim(reset))
));
}
@BinderThread
@Override
public void transitionTo(@BarTransitions.TransitionMode int barMode,
boolean animate) {
public void checkNavBarModes(int displayId) {
MAIN_EXECUTOR.execute(() -> executeForTouchInteractionService(tis ->
executeForTaskbarManager(
taskbarManager -> taskbarManager.transitionTo(barMode, animate))
));
taskbarManager -> taskbarManager.checkNavBarModes(displayId))));
}
@BinderThread
@Override
public void finishBarAnimations(int displayId) {
MAIN_EXECUTOR.execute(() -> executeForTouchInteractionService(
tis -> executeForTaskbarManager(
taskbarManager -> taskbarManager.finishBarAnimations(displayId))));
}
@BinderThread
@Override
public void touchAutoDim(int displayId, boolean reset) {
MAIN_EXECUTOR.execute(() -> executeForTouchInteractionService(
tis -> executeForTaskbarManager(
taskbarManager -> taskbarManager.touchAutoDim(displayId, reset))));
}
@BinderThread
@Override
public void transitionTo(int displayId, @BarTransitions.TransitionMode int barMode,
boolean animate) {
MAIN_EXECUTOR.execute(() -> executeForTouchInteractionService(
tis -> executeForTaskbarManager(
taskbarManager -> taskbarManager.transitionTo(displayId, barMode,
animate))));
}
@BinderThread
@@ -16,29 +16,28 @@
package com.android.quickstep.contextualeducation;
import android.content.Context;
import com.android.launcher3.contextualeducation.ContextualEduStatsManager;
import com.android.launcher3.dagger.LauncherAppSingleton;
import com.android.quickstep.SystemUiProxy;
import com.android.systemui.contextualeducation.GestureType;
import javax.inject.Inject;
/**
* A class to update contextual education data via {@link SystemUiProxy}
*/
@LauncherAppSingleton
public class SystemContextualEduStatsManager extends ContextualEduStatsManager {
private Context mContext;
private final SystemUiProxy mSystemUiProxy;
public SystemContextualEduStatsManager(Context context) {
mContext = context;
@Inject
public SystemContextualEduStatsManager(SystemUiProxy systemUiProxy) {
mSystemUiProxy = systemUiProxy;
}
@Override
public void updateEduStats(boolean isTrackpadGesture, GestureType gestureType) {
SystemUiProxy.INSTANCE.get(mContext).updateContextualEduStats(isTrackpadGesture,
mSystemUiProxy.updateContextualEduStats(isTrackpadGesture,
gestureType.name());
}
@Override
public void close() {
}
}
@@ -15,10 +15,12 @@
*/
package com.android.quickstep.dagger;
import com.android.launcher3.contextualeducation.ContextualEduStatsManager;
import com.android.launcher3.uioverrides.SystemApiWrapper;
import com.android.launcher3.uioverrides.plugins.PluginManagerWrapperImpl;
import com.android.launcher3.util.ApiWrapper;
import com.android.launcher3.util.PluginManagerWrapper;
import com.android.quickstep.contextualeducation.SystemContextualEduStatsManager;
import dagger.Binds;
import dagger.Module;
@@ -28,4 +30,6 @@ public abstract class QuickStepModule {
@Binds abstract PluginManagerWrapper bindPluginManagerWrapper(PluginManagerWrapperImpl impl);
@Binds abstract ApiWrapper bindApiWrapper(SystemApiWrapper systemApiWrapper);
@Binds abstract ContextualEduStatsManager bindContextualEduStatsManager(
SystemContextualEduStatsManager manager);
}
@@ -218,6 +218,11 @@ class RecentsWindowManager(context: Context) :
)
}
private val onBackInvokedCallback: () -> Unit = {
// If we are in live tile mode, launch the live task, otherwise return home
recentsView?.runningTaskView?.launchWithAnimation() ?: startHome()
}
private fun cleanupRecentsWindow() {
RecentsWindowProtoLogProxy.logCleanup(isShowing())
if (isShowing()) {
@@ -241,6 +246,10 @@ class RecentsWindowManager(context: Context) :
}
windowManager.addView(windowView, windowLayoutParams)
windowView
?.findOnBackInvokedDispatcher()
?.registerSystemOnBackInvokedCallback(onBackInvokedCallback)
windowView?.systemUiVisibility =
(View.SYSTEM_UI_FLAG_LAYOUT_STABLE or
View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN or
@@ -23,6 +23,7 @@ import com.android.launcher3.R
import com.android.launcher3.dagger.LauncherAppComponent
import com.android.launcher3.dagger.LauncherAppSingleton
import com.android.launcher3.util.LauncherModelHelper
import com.android.quickstep.dagger.QuickStepModule
import com.android.systemui.contextualeducation.GestureType
import com.android.systemui.shared.system.InputConsumerController
import dagger.BindsInstance
@@ -105,7 +106,7 @@ class LauncherSwipeHandlerV2Test {
}
@LauncherAppSingleton
@Component
@Component(modules = [QuickStepModule::class])
interface TestComponent : LauncherAppComponent {
@Component.Builder
interface Builder : LauncherAppComponent.Builder {
@@ -104,28 +104,36 @@ class TaplTestsOverviewDesktop : AbstractLauncherUiTest<QuickstepLauncher?>() {
@Test
@PortraitLandscape
fun dismissFocusedTask_thenDesktopTask_thenFocusedTaskIsCentered() {
fun dismissTasks_whenDesktopTask_IsInTheCenter() {
// Create extra activity to be DesktopTaskView
startTestActivity(TEST_ACTIVITY_EXTRA)
mLauncher.goHome().switchToOverview()
val desktop = moveTaskToDesktop(TEST_ACTIVITY_EXTRA)
var overview = desktop.switchToOverview()
val overview = desktop.switchToOverview()
// Open focused task and go back to Overview to validate whether it has adjacent tasks in
// its both sides (grid task on left and desktop tasks at its right side)
val focusedTaskOpened = overview.getTestActivityTask(TEST_ACTIVITY_2).open()
// Dismiss focused task
val focusedTask1 = overview.getTestActivityTask(TEST_ACTIVITY_2)
assertTaskContentDescription(focusedTask1, TEST_ACTIVITY_2)
focusedTask1.dismiss()
// Dismiss DesktopTaskView
// Fling to desktop task and dismiss the focused task to check repositioning of
// grid tasks.
overview = focusedTaskOpened.switchToOverview().apply { flingBackward() }
val desktopTask = overview.currentTask
assertWithMessage("The current task is not a Desktop.").that(desktopTask.isDesktop).isTrue()
// Get focused task (previously opened task) then dismiss this task
val focusedTaskInOverview = overview.getTestActivityTask(TEST_ACTIVITY_2)
assertTaskContentDescription(focusedTaskInOverview, TEST_ACTIVITY_2)
focusedTaskInOverview.dismiss()
// Dismiss DesktopTask to validate whether the new focused task will take its position
desktopTask.dismiss()
// Dismiss focused task
val focusedTask2 = overview.currentTask
assertTaskContentDescription(focusedTask2, TEST_ACTIVITY_1)
focusedTask2.dismiss()
// Dismiss last focused task
val lastFocusedTask = overview.currentTask
assertTaskContentDescription(lastFocusedTask, TEST_ACTIVITY_1)
lastFocusedTask.dismiss()
assertWithMessage("Still have tasks after dismissing all the tasks")
.that(mLauncher.workspace.switchToOverview().hasTasks())
+5 -5
View File
@@ -16,9 +16,9 @@
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24"
android:tint="?android:attr/textColorHint">
android:viewportHeight="24">
<path
android:fillColor="@android:color/white"
android:pathData="M20,6h-4L16,4c0,-1.11 -0.89,-2 -2,-2h-4c-1.11,0 -2,0.89 -2,2v1.17L10.83,8L20,8v9.17l1.98,1.98c0,-0.05 0.02,-0.1 0.02,-0.16L22,8c0,-1.11 -0.89,-2 -2,-2zM14,6h-4L10,4h4v2zM19,19L8,8 6,6 2.81,2.81 1.39,4.22 3.3,6.13C2.54,6.41 2.01,7.14 2.01,8L2,19c0,1.11 0.89,2 2,2h14.17l1.61,1.61 1.41,-1.41 -0.37,-0.37L19,19zM4,19L4,8h1.17l11,11L4,19z" />
</vector>
android:pathData="M16,6H20C21.11,6 22,6.89 22,8V18.99C22,19.021 21.994,19.05 21.989,19.077C21.984,19.102 21.98,19.126 21.98,19.15L20,17.17V8H10.83L8,5.17V4C8,2.89 8.89,2 10,2H14C15.11,2 16,2.89 16,4V6ZM10,6H14V4H10V6ZM19,19L8,8L6,6L2.81,2.81L1.39,4.22L3.3,6.13C2.54,6.41 2.01,7.14 2.01,8L2,19C2,20.11 2.89,21 4,21H18.17L19.78,22.61L21.19,21.2L20.82,20.83L19,19ZM4,8V19H16.17L5.17,8H4Z"
android:fillColor="?attr/materialColorOnPrimary"
android:fillType="evenOdd"/>
</vector>
+25
View File
@@ -0,0 +1,25 @@
<?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.
-->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="960"
android:viewportHeight="960"
android:tint="?attr/colorControlNormal">
<path
android:fillColor="?attr/materialColorOnPrimary"
android:pathData="M612,668L668,612L520,464L520,280L440,280L440,496L612,668ZM480,880Q397,880 324,848.5Q251,817 197,763Q143,709 111.5,636Q80,563 80,480Q80,397 111.5,324Q143,251 197,197Q251,143 324,111.5Q397,80 480,80Q563,80 636,111.5Q709,143 763,197Q817,251 848.5,324Q880,397 880,480Q880,563 848.5,636Q817,709 763,763Q709,817 636,848.5Q563,880 480,880Z"/>
</vector>
+1 -1
View File
@@ -18,7 +18,7 @@
<item>
<shape android:shape="rectangle">
<corners android:radius="@dimen/work_fab_radius" />
<solid android:color="@color/work_fab_bg_color" />
<solid android:color="?attr/materialColorPrimary" />
<padding
android:left="@dimen/work_mode_fab_background_horizontal_padding"
android:right="@dimen/work_mode_fab_background_horizontal_padding"/>
@@ -0,0 +1,26 @@
<?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.
-->
<ripple xmlns:android="http://schemas.android.com/apk/res/android"
android:color="@color/accent_ripple_color">
<item>
<shape android:shape="rectangle">
<corners android:radius="@dimen/work_fab_radius" />
<solid android:color="?attr/materialColorPrimary" />
<padding
android:padding="@dimen/work_scheduler_background_padding" />
</shape>
</item>
</ripple>
+3 -6
View File
@@ -12,11 +12,9 @@
See the License for the specific language governing permissions and
limitations under the License.
-->
<com.android.launcher3.allapps.WorkModeSwitch
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/work_mode_toggle"
android:layout_alignParentBottom="true"
android:layout_alignParentEnd="true"
android:layout_height="@dimen/work_fab_height"
android:layout_width="wrap_content"
android:minHeight="@dimen/work_fab_height"
@@ -31,7 +29,6 @@
android:layout_marginVertical="@dimen/work_fab_icon_vertical_margin"
android:importantForAccessibility="no"
android:src="@drawable/ic_corp_off"
android:tint="@color/work_fab_icon_color"
android:layout_marginStart="@dimen/work_fab_icon_start_margin_expanded"
android:scaleType="center"/>
<TextView
@@ -39,7 +36,7 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:maxWidth="@dimen/work_fab_width"
android:textColor="@color/work_fab_icon_color"
android:textColor="?attr/materialColorOnPrimary"
android:textSize="14sp"
android:includeFontPadding="false"
android:textDirection="locale"
@@ -48,4 +45,4 @@
android:layout_marginEnd="@dimen/work_fab_text_end_margin"
android:maxLines="1"
style="@style/TextHeadline"/>
</com.android.launcher3.allapps.WorkModeSwitch>
</LinearLayout>
+32
View File
@@ -0,0 +1,32 @@
<!--
~ 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.launcher3.allapps.WorkUtilityView xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_height="wrap_content"
android:layout_width="wrap_content"
android:orientation="vertical"
android:layout_alignParentBottom="true"
android:layout_alignParentEnd="true">
<ImageButton
android:id="@+id/work_scheduler"
android:layout_width="@dimen/work_scheduler_size"
android:layout_height="@dimen/work_scheduler_size"
android:layout_marginBottom="@dimen/work_scheduler_bottom_margin"
android:contentDescription="@string/work_scheduler_button_content_description"
android:src="@drawable/ic_schedule"
android:layout_gravity="end"
android:background="@drawable/work_scheduler_background" />
<include layout="@layout/work_mode_fab" />
</com.android.launcher3.allapps.WorkUtilityView>
-5
View File
@@ -53,10 +53,5 @@
<color name="widget_picker_add_button_text_color_dark">
@android:color/system_accent1_800</color>
<color name="work_fab_bg_color">
@android:color/system_accent1_200</color>
<color name="work_fab_icon_color">
@android:color/system_accent1_900</color>
<color name="material_color_on_surface">@android:color/system_neutral1_100</color>
</resources>
-5
View File
@@ -104,11 +104,6 @@
<color name="widget_picker_add_button_text_color_light">
@android:color/system_accent1_0</color>
<color name="work_fab_bg_color">
@android:color/system_accent1_200</color>
<color name="work_fab_icon_color">
@android:color/system_accent1_900</color>
<color name="overview_foreground_scrim_color">@android:color/system_neutral1_1000</color>
<color name="material_color_on_surface">@android:color/system_neutral1_900</color>
+3
View File
@@ -268,6 +268,9 @@
defaults to @dimen/taskbar_button_margin_default -->
<attr name="inlineNavButtonsEndSpacing" format="reference" />
<!-- Grid flips row and column count when rotating the device -->
<attr name="isDualGrid" format="boolean" />
<attr name="dbFile" format="string" />
<attr name="defaultLayoutId" format="reference" />
<attr name="demoModeLayoutId" format="reference" />
-2
View File
@@ -98,8 +98,6 @@
<color name="preload_icon_background_color_dark">#40484D</color>
<color name="work_turn_on_stroke">?android:attr/colorAccent</color>
<color name="work_fab_bg_color">#A8C7FA</color>
<color name="work_fab_icon_color">#041E49</color>
<color name="widget_picker_primary_surface_color_light">#EFEDED</color>
<color name="widget_picker_secondary_surface_color_light">#FAF9F8</color>
-1
View File
@@ -76,7 +76,6 @@
<string name="taskbar_view_callbacks_factory_class" translatable="false"></string>
<string name="launcher_restore_event_logger_class" translatable="false"></string>
<string name="taskbar_edu_tooltip_controller_class" translatable="false"></string>
<string name="contextual_edu_manager_class" translatable="false"></string>
<!-- Used for determining category of a widget presented in widget recommendations. -->
<string name="widget_recommendation_category_provider_class" translatable="false"></string>
+3
View File
@@ -166,6 +166,9 @@
<dimen name="work_fab_margin">16dp</dimen>
<dimen name="work_fab_margin_bottom">20dp</dimen>
<dimen name="work_mode_fab_background_horizontal_padding">16dp</dimen>
<dimen name="work_scheduler_background_padding">16dp</dimen>
<dimen name="work_scheduler_bottom_margin">8dp</dimen>
<dimen name="work_scheduler_size">56dp</dimen>
<dimen name="work_profile_footer_padding">20dp</dimen>
<dimen name="work_edu_card_margin">16dp</dimen>
<dimen name="work_edu_card_radius">16dp</dimen>
+4
View File
@@ -466,6 +466,8 @@
<string name="work_profile_edu_accept">Got it</string>
<!-- Info icon unicode for alpha scroller when work edu card is present -->
<string name="work_profile_edu_section" translatable="false">\u24D8</string>
<!-- Intent for work profiler scheduler -->
<string name="work_profile_scheduler_intent" translatable="false"/>
<!--- heading shown when user opens work apps tab while work apps are paused -->
<string name="work_apps_paused_title">Work apps are paused</string>
@@ -484,6 +486,8 @@
<string name="work_apps_pause_btn_text">Pause work apps</string>
<!-- button string shown enable work profile -->
<string name="work_apps_enable_btn_text">Unpause</string>
<!-- Label for the work profile scheduler button in the work profile screen. [CHAR_LIMIT=40] -->
<string name="work_scheduler_button_content_description">Work apps schedule</string>
<!-- A hint shown in launcher settings develop options filter box -->
<string name="developer_options_filter_hint">Filter</string>
+1
View File
@@ -2,6 +2,7 @@
<full-backup-content xmlns:android="http://schemas.android.com/apk/res/android">
<include domain="database" path="launcher.db" />
<include domain="database" path="launcher_5_by_8.db" />
<include domain="database" path="launcher_6_by_5.db" />
<include domain="database" path="launcher_5_by_6.db" />
<include domain="database" path="launcher_4_by_6.db" />
+76
View File
@@ -0,0 +1,76 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright (C) 2021 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<favorites xmlns:launcher="http://schemas.android.com/apk/res-auto/com.android.launcher3">
<!-- Hotseat (We use the screen as the position of the item in the hotseat) -->
<!-- Mail Calendar Gallery Store Internet Camera -->
<resolve
launcher:container="-101"
launcher:screen="0"
launcher:x="0"
launcher:y="0" >
<favorite launcher:uri="#Intent;action=android.intent.action.MAIN;category=android.intent.category.APP_EMAIL;end" />
<favorite launcher:uri="mailto:" />
</resolve>
<resolve
launcher:container="-101"
launcher:screen="1"
launcher:x="1"
launcher:y="0" >
<favorite launcher:uri="#Intent;action=android.intent.action.MAIN;category=android.intent.category.APP_CALENDAR;end" />
</resolve>
<resolve
launcher:container="-101"
launcher:screen="2"
launcher:x="2"
launcher:y="0" >
<favorite launcher:uri="#Intent;action=android.intent.action.MAIN;category=android.intent.category.APP_GALLERY;end" />
<favorite launcher:uri="#Intent;type=images/*;end" />
</resolve>
<resolve
launcher:container="-101"
launcher:screen="3"
launcher:x="3"
launcher:y="0" >
<favorite launcher:uri="#Intent;action=android.intent.action.MAIN;category=android.intent.category.APP_MARKET;end" />
<favorite launcher:uri="market://details?id=com.android.launcher" />
</resolve>
<resolve
launcher:container="-101"
launcher:screen="4"
launcher:x="4"
launcher:y="0" >
<favorite
launcher:uri="#Intent;action=android.intent.action.MAIN;category=android.intent.category.APP_BROWSER;end" />
<favorite launcher:uri="http://www.example.com/" />
</resolve>
<!-- Resolve camera intent if GoogleCamera is not available e.g. on emulator -->
<resolve
launcher:container="-101"
launcher:screen="5"
launcher:x="5"
launcher:y="0" >
<favorite launcher:uri="#Intent;action=android.media.action.STILL_IMAGE_CAMERA;end" />
<favorite launcher:uri="#Intent;action=android.intent.action.CAMERA_BUTTON;end" />
</resolve>
</favorites>
+45
View File
@@ -0,0 +1,45 @@
<?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.
-->
<device-paddings xmlns:launcher="http://schemas.android.com/apk/res-auto" >
<device-padding
launcher:maxEmptySpace="100dp">
<workspaceTopPadding
launcher:a="0.31"
launcher:b="0"/>
<workspaceBottomPadding
launcher:a="0.69"
launcher:b="0"/>
<hotseatBottomPadding
launcher:a="0"
launcher:b="0"/>
</device-padding>
<device-padding
launcher:maxEmptySpace="9999dp">
<workspaceTopPadding
launcher:a="0.48"
launcher:b="0"/>
<workspaceBottomPadding
launcher:a="0.52"
launcher:b="0"/>
<hotseatBottomPadding
launcher:a="0"
launcher:b="0"/>
</device-padding>
</device-paddings>
+6 -1
View File
@@ -827,7 +827,7 @@ public class DeviceProfile {
hotseatBorderSpace = cellLayoutBorderSpacePx.y;
}
if (isTablet) {
if (shouldShowAllAppsOnSheet()) {
allAppsPadding.top = mInsets.top;
allAppsShiftRange = heightPx;
} else {
@@ -1516,6 +1516,11 @@ public class DeviceProfile {
}
}
/** Whether All Apps should be presented on a bottom sheet. */
public boolean shouldShowAllAppsOnSheet() {
return isTablet || Flags.allAppsSheetForHandheld();
}
private void setupAllAppsStyle(Context context) {
TypedArray allAppsStyle = context.obtainStyledAttributes(
inv.allAppsStyle != INVALID_RESOURCE_HANDLE ? inv.allAppsStyle
@@ -52,7 +52,6 @@ import androidx.core.content.res.ResourcesCompat;
import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.icons.DotRenderer;
import com.android.launcher3.logging.FileLog;
import com.android.launcher3.model.DeviceGridState;
import com.android.launcher3.provider.RestoreDbTask;
import com.android.launcher3.testing.shared.ResourceUtils;
@@ -303,35 +302,6 @@ public class InvariantDeviceProfile implements SafeCloseable {
DisplayController.INSTANCE.executeIfCreated(dc -> dc.setPriorityListener(null));
}
/**
* Reinitialize the current grid after a restore, where some grids might now be disabled.
*/
public void reinitializeAfterRestore(Context context) {
String currentGridName = getCurrentGridName(context);
String currentDbFile = dbFile;
String newGridName = initGrid(context, currentGridName);
String newDbFile = dbFile;
FileLog.d(TAG, "Reinitializing grid after restore."
+ " currentGridName=" + currentGridName
+ ", currentDbFile=" + currentDbFile
+ ", newGridName=" + newGridName
+ ", newDbFile=" + newDbFile);
if (!newDbFile.equals(currentDbFile)) {
FileLog.d(TAG, "Restored grid is disabled : " + currentGridName
+ ", migrating to: " + newGridName
+ ", removing all other grid db files");
for (String gridDbFile : LauncherFiles.GRID_DB_FILES) {
if (gridDbFile.equals(currentDbFile)) {
continue;
}
if (context.getDatabasePath(gridDbFile).delete()) {
FileLog.d(TAG, "Removed old grid db file: " + gridDbFile);
}
}
setCurrentGrid(context, newGridName);
}
}
public static String getCurrentGridName(Context context) {
return LauncherPrefs.get(context).get(GRID_NAME);
}
@@ -540,7 +510,7 @@ public class InvariantDeviceProfile implements SafeCloseable {
}
}
private List<DisplayOption> getPredefinedDeviceProfiles(Context context,
private static List<DisplayOption> getPredefinedDeviceProfiles(Context context,
String gridName, @DeviceType int deviceType, boolean allowDisabledGrid) {
ArrayList<DisplayOption> profiles = new ArrayList<>();
@@ -554,7 +524,7 @@ public class InvariantDeviceProfile implements SafeCloseable {
GridOption gridOption = new GridOption(context, Xml.asAttributeSet(parser));
if ((gridOption.isEnabled(deviceType) || allowDisabledGrid)
&& (Flags.oneGridSpecs() == gridOption.isNewGridOption())) {
&& gridOption.filterByFlag(deviceType)) {
final int displayDepth = parser.getDepth();
while (((type = parser.next()) != XmlPullParser.END_TAG
|| parser.getDepth() > displayDepth)
@@ -710,7 +680,7 @@ public class InvariantDeviceProfile implements SafeCloseable {
return parseAllDefinedGridOptions(context)
.stream()
.filter(go -> go.isEnabled(deviceType))
.filter(go -> (Flags.oneGridSpecs() == go.isNewGridOption()))
.filter(go -> go.filterByFlag(deviceType))
.collect(Collectors.toList());
}
@@ -967,6 +937,7 @@ public class InvariantDeviceProfile implements SafeCloseable {
private final int demoModeLayoutId;
private final boolean isScalable;
private final boolean mIsDualGrid;
private final int devicePaddingId;
private final int mWorkspaceSpecsId;
private final int mWorkspaceSpecsTwoPanelId;
@@ -991,6 +962,7 @@ public class InvariantDeviceProfile implements SafeCloseable {
DEVICE_CATEGORY_ALL);
mRowCountSpecsId = a.getResourceId(
R.styleable.GridDisplayOption_rowCountSpecsId, INVALID_RESOURCE_HANDLE);
mIsDualGrid = a.getBoolean(R.styleable.GridDisplayOption_isDualGrid, false);
if (mRowCountSpecsId != INVALID_RESOURCE_HANDLE) {
ResourceHelper resourceHelper = new ResourceHelper(context, mRowCountSpecsId);
NumRows numR = getRowCount(resourceHelper, context, deviceCategory);
@@ -1154,6 +1126,13 @@ public class InvariantDeviceProfile implements SafeCloseable {
public boolean isNewGridOption() {
return mRowCountSpecsId != INVALID_RESOURCE_HANDLE;
}
public boolean filterByFlag(int deviceType) {
if (deviceType == TYPE_TABLET) {
return Flags.oneGridRotationHandling() == mIsDualGrid;
}
return Flags.oneGridSpecs() == isNewGridOption();
}
}
public static final class NumRows {
@@ -16,6 +16,7 @@ public class LauncherFiles {
private static final String XML = ".xml";
public static final String LAUNCHER_DB = "launcher.db";
public static final String LAUNCHER_5_BY_8_DB = "launcher_5_by_8.db";
public static final String LAUNCHER_6_BY_5_DB = "launcher_6_by_5.db";
public static final String LAUNCHER_4_BY_5_DB = "launcher_4_by_5.db";
public static final String LAUNCHER_4_BY_6_DB = "launcher_4_by_6.db";
@@ -35,6 +36,7 @@ public class LauncherFiles {
public static final List<String> GRID_DB_FILES = Collections.unmodifiableList(Arrays.asList(
LAUNCHER_DB,
LAUNCHER_5_BY_8_DB,
LAUNCHER_6_BY_5_DB,
LAUNCHER_4_BY_5_DB,
LAUNCHER_4_BY_6_DB,
+8 -2
View File
@@ -375,8 +375,14 @@ public abstract class LauncherState implements BaseState<LauncherState> {
}
public PageAlphaProvider getWorkspacePageAlphaProvider(Launcher launcher) {
if ((this != NORMAL && this != HINT_STATE)
|| !launcher.getDeviceProfile().shouldFadeAdjacentWorkspaceScreens()) {
DeviceProfile dp = launcher.getDeviceProfile();
boolean shouldFadeAdjacentScreens = (this == NORMAL || this == HINT_STATE)
&& dp.shouldFadeAdjacentWorkspaceScreens();
// Avoid showing adjacent screens behind handheld All Apps sheet.
if (Flags.allAppsSheetForHandheld() && dp.isPhone && this == ALL_APPS) {
shouldFadeAdjacentScreens = true;
}
if (!shouldFadeAdjacentScreens) {
return DEFAULT_ALPHA_PROVIDER;
}
final int centerPage = launcher.getWorkspace().getNextPage();
@@ -47,7 +47,6 @@ import android.os.Parcelable;
import android.os.Process;
import android.os.UserManager;
import android.util.AttributeSet;
import android.util.FloatProperty;
import android.util.Log;
import android.util.SparseArray;
import android.view.KeyEvent;
@@ -115,19 +114,6 @@ public class ActivityAllAppsContainerView<T extends Context & ActivityContext>
ScrimView.ScrimDrawingController {
public static final FloatProperty<ActivityAllAppsContainerView<?>> BOTTOM_SHEET_ALPHA =
new FloatProperty<>("bottomSheetAlpha") {
@Override
public Float get(ActivityAllAppsContainerView<?> containerView) {
return containerView.mBottomSheetAlpha;
}
@Override
public void setValue(ActivityAllAppsContainerView<?> containerView, float v) {
containerView.setBottomSheetAlpha(v);
}
};
public static final float PULL_MULTIPLIER = .02f;
public static final float FLING_VELOCITY_MULTIPLIER = 1200f;
protected static final String BUNDLE_KEY_CURRENT_PAGE = "launcher.allapps.current_page";
@@ -191,8 +177,6 @@ public class ActivityAllAppsContainerView<T extends Context & ActivityContext>
private ScrimView mScrimView;
private int mHeaderColor;
private int mBottomSheetBackgroundColor;
private float mBottomSheetAlpha = 1f;
private boolean mForceBottomSheetVisible;
private int mTabsProtectionAlpha;
@Nullable private AllAppsTransitionController mAllAppsTransitionController;
@@ -351,20 +335,6 @@ public class ActivityAllAppsContainerView<T extends Context & ActivityContext>
return mSearchUiManager;
}
public View getBottomSheetBackground() {
return mBottomSheetBackground;
}
/**
* Temporarily force the bottom sheet to be visible on non-tablets.
*
* @param force {@code true} means bottom sheet will be visible on phones until {@code reset()}.
*/
public void forceBottomSheetVisible(boolean force) {
mForceBottomSheetVisible = force;
updateBackgroundVisibility(mActivityContext.getDeviceProfile());
}
public View getSearchView() {
return mSearchContainer;
}
@@ -496,7 +466,7 @@ public class ActivityAllAppsContainerView<T extends Context & ActivityContext>
if (mHeader != null && mHeader.getVisibility() == VISIBLE) {
mHeader.reset(animate);
}
forceBottomSheetVisible(false);
updateBackgroundVisibility(mActivityContext.getDeviceProfile());
// Reset the base recycler view after transitioning home.
updateHeaderScroll(0);
if (exitSearch) {
@@ -726,7 +696,7 @@ public class ActivityAllAppsContainerView<T extends Context & ActivityContext>
post(() -> mAH.get(AdapterHolder.WORK).applyPadding());
} else {
mWorkManager.detachWorkModeSwitch();
mWorkManager.detachWorkUtilityViews();
mViewPager = null;
}
@@ -1002,18 +972,13 @@ public class ActivityAllAppsContainerView<T extends Context & ActivityContext>
}
protected void updateBackgroundVisibility(DeviceProfile deviceProfile) {
boolean visible = deviceProfile.isTablet || mForceBottomSheetVisible;
mBottomSheetBackground.setVisibility(visible ? View.VISIBLE : View.GONE);
// Note: For tablets, the opaque background and header protection are added in drawOnScrim.
mBottomSheetBackground.setVisibility(
deviceProfile.shouldShowAllAppsOnSheet() ? View.VISIBLE : View.GONE);
// Note: The opaque sheet background and header protection are added in drawOnScrim.
// For the taskbar entrypoint, the scrim is drawn by its abstract slide in view container,
// so its header protection is derived from this scrim instead.
}
private void setBottomSheetAlpha(float alpha) {
// Bottom sheet alpha is always 1 for tablets.
mBottomSheetAlpha = mActivityContext.getDeviceProfile().isTablet ? 1f : alpha;
}
@VisibleForTesting
public void onAppsUpdated() {
mHasWorkApps = Stream.of(mAllAppsStore.getApps())
@@ -1151,8 +1116,8 @@ public class ActivityAllAppsContainerView<T extends Context & ActivityContext>
applyAdapterSideAndBottomPaddings(grid);
MarginLayoutParams mlp = (MarginLayoutParams) getLayoutParams();
// Ignore left/right insets on tablet because we are already centered in-screen.
if (grid.isTablet) {
// Ignore left/right insets on bottom sheet because we are already centered in-screen.
if (grid.shouldShowAllAppsOnSheet()) {
mlp.leftMargin = mlp.rightMargin = 0;
} else {
mlp.leftMargin = insets.left;
@@ -1257,8 +1222,8 @@ public class ActivityAllAppsContainerView<T extends Context & ActivityContext>
/** Called in Launcher#bindStringCache() to update the UI when cache is updated. */
public void updateWorkUI() {
setDeviceManagementResources();
if (mWorkManager.getWorkModeSwitch() != null) {
mWorkManager.getWorkModeSwitch().updateStringFromCache();
if (mWorkManager.getWorkUtilityView() != null) {
mWorkManager.getWorkUtilityView().updateStringFromCache();
}
inflateWorkCardsIfNeeded();
}
@@ -1398,7 +1363,7 @@ public class ActivityAllAppsContainerView<T extends Context & ActivityContext>
// Draw full background panel for tablets.
if (hasBottomSheet) {
mHeaderPaint.setColor(mBottomSheetBackgroundColor);
mHeaderPaint.setAlpha((int) (255 * mBottomSheetAlpha));
mHeaderPaint.setAlpha(255);
mTmpRectF.set(
leftWithScale,
@@ -1581,8 +1546,8 @@ public class ActivityAllAppsContainerView<T extends Context & ActivityContext>
void applyPadding() {
if (mRecyclerView != null) {
int bottomOffset = 0;
if (isWork() && mWorkManager.getWorkModeSwitch() != null) {
bottomOffset = mInsets.bottom + mWorkManager.getWorkModeSwitch().getHeight();
if (isWork() && mWorkManager.getWorkUtilityView() != null) {
bottomOffset = mInsets.bottom + mWorkManager.getWorkUtilityView().getHeight();
} else if (isMain() && mPrivateProfileManager != null) {
Optional<AdapterItem> privateSpaceHeaderItem = mAppsList.getAdapterItems()
.stream()
@@ -16,7 +16,6 @@
package com.android.launcher3.allapps;
import static com.android.app.animation.Interpolators.DECELERATE_1_7;
import static com.android.app.animation.Interpolators.INSTANT;
import static com.android.app.animation.Interpolators.LINEAR;
import static com.android.launcher3.LauncherAnimUtils.SCALE_PROPERTY;
import static com.android.launcher3.LauncherAnimUtils.VIEW_TRANSLATE_Y;
@@ -28,7 +27,6 @@ import static com.android.launcher3.UtilitiesKt.CLIP_CHILDREN_FALSE_MODIFIER;
import static com.android.launcher3.UtilitiesKt.modifyAttributesOnViewTree;
import static com.android.launcher3.UtilitiesKt.restoreAttributesOnViewTree;
import static com.android.launcher3.anim.PropertySetter.NO_ANIM_PROPERTY_SETTER;
import static com.android.launcher3.states.StateAnimationConfig.ANIM_ALL_APPS_BOTTOM_SHEET_FADE;
import static com.android.launcher3.states.StateAnimationConfig.ANIM_ALL_APPS_FADE;
import static com.android.launcher3.states.StateAnimationConfig.ANIM_VERTICAL_PROGRESS;
import static com.android.launcher3.util.SystemUiController.FLAG_DARK_NAV;
@@ -106,7 +104,7 @@ public class AllAppsTransitionController
@Override
public Float get(AllAppsTransitionController controller) {
if (controller.mIsTablet) {
if (controller.mShouldShowAllAppsOnSheet) {
return controller.mAppsView.getActiveRecyclerView().getTranslationY();
} else {
return controller.getAppsViewPullbackTranslationY().getValue();
@@ -115,7 +113,7 @@ public class AllAppsTransitionController
@Override
public void setValue(AllAppsTransitionController controller, float translation) {
if (controller.mIsTablet) {
if (controller.mShouldShowAllAppsOnSheet) {
controller.mAppsView.getActiveRecyclerView().setTranslationY(translation);
controller.getAppsViewPullbackTranslationY().setValue(
ALL_APPS_PULL_BACK_TRANSLATION_DEFAULT);
@@ -134,7 +132,7 @@ public class AllAppsTransitionController
@Override
public Float get(AllAppsTransitionController controller) {
if (controller.mIsTablet) {
if (controller.mShouldShowAllAppsOnSheet) {
return controller.mAppsView.getActiveRecyclerView().getAlpha();
} else {
return controller.getAppsViewPullbackAlpha().getValue();
@@ -143,7 +141,7 @@ public class AllAppsTransitionController
@Override
public void setValue(AllAppsTransitionController controller, float alpha) {
if (controller.mIsTablet) {
if (controller.mShouldShowAllAppsOnSheet) {
controller.mAppsView.getActiveRecyclerView().setAlpha(alpha);
controller.getAppsViewPullbackAlpha().setValue(
ALL_APPS_PULL_BACK_ALPHA_DEFAULT);
@@ -168,6 +166,7 @@ public class AllAppsTransitionController
@Nullable private Animator.AnimatorListener mAllAppsSearchBackAnimationListener;
private boolean mIsVerticalLayout;
private boolean mShouldShowAllAppsOnSheet;
// Animation in this class is controlled by a single variable {@link mProgress}.
// Visually, it represents top y coordinate of the all apps container if multiplied with
@@ -183,8 +182,6 @@ public class AllAppsTransitionController
private MultiValueAlpha mAppsViewAlpha;
private MultiPropertyFactory<View> mAppsViewTranslationY;
private boolean mIsTablet;
private boolean mHasScaleEffect;
private final VibratorWrapper mVibratorWrapper;
@@ -193,7 +190,7 @@ public class AllAppsTransitionController
DeviceProfile dp = mLauncher.getDeviceProfile();
mProgress = 1f;
mIsVerticalLayout = dp.isVerticalBarLayout();
mIsTablet = dp.isTablet;
mShouldShowAllAppsOnSheet = dp.shouldShowAllAppsOnSheet();
mNavScrimFlag = Themes.getAttrBoolean(l, R.attr.isMainColorDark)
? FLAG_DARK_NAV : FLAG_LIGHT_NAV;
@@ -217,7 +214,7 @@ public class AllAppsTransitionController
mLauncher.getWorkspace().getPageIndicator().setTranslationY(0);
}
mIsTablet = dp.isTablet;
mShouldShowAllAppsOnSheet = dp.shouldShowAllAppsOnSheet();
}
/**
@@ -395,10 +392,6 @@ public class AllAppsTransitionController
setter.setFloat(getAppsViewPullbackAlpha(), MultiPropertyFactory.MULTI_PROPERTY_VALUE,
hasAllAppsContent ? 1 : 0, allAppsFade);
setter.setFloat(mLauncher.getAppsView(),
ActivityAllAppsContainerView.BOTTOM_SHEET_ALPHA, hasAllAppsContent ? 1 : 0,
config.getInterpolator(ANIM_ALL_APPS_BOTTOM_SHEET_FADE, INSTANT));
boolean shouldProtectHeader = !config.hasAnimationFlag(StateAnimationConfig.SKIP_SCRIM)
&& (ALL_APPS == state || mLauncher.getStateManager().getState() == ALL_APPS);
mScrimView.setDrawingController(shouldProtectHeader ? mAppsView : null);
@@ -58,7 +58,7 @@ public class WorkProfileManager extends UserProfileManager
implements PersonalWorkSlidingTabStrip.OnActivePageChangedListener {
private static final String TAG = "WorkProfileManager";
private final ActivityAllAppsContainerView<?> mAllApps;
private WorkModeSwitch mWorkModeSwitch;
private WorkUtilityView mWorkUtilityView;
private final Predicate<UserHandle> mWorkProfileMatcher;
public WorkProfileManager(
@@ -79,15 +79,15 @@ public class WorkProfileManager extends UserProfileManager
@Override
public void onActivePageChanged(int page) {
updateWorkFAB(page);
updateWorkUtilityViews(page);
}
private void updateWorkFAB(int page) {
if (mWorkModeSwitch != null) {
private void updateWorkUtilityViews(int page) {
if (mWorkUtilityView != null) {
if (page == MAIN || page == SEARCH) {
mWorkModeSwitch.animateVisibility(false);
mWorkUtilityView.animateVisibility(false);
} else if (page == WORK && getCurrentState() == STATE_ENABLED) {
mWorkModeSwitch.animateVisibility(true);
mWorkUtilityView.animateVisibility(true);
}
}
}
@@ -104,10 +104,10 @@ public class WorkProfileManager extends UserProfileManager
}
boolean isEnabled = !mAllApps.getAppsStore().hasModelFlag(quietModeFlag);
updateCurrentState(isEnabled ? STATE_ENABLED : STATE_DISABLED);
if (mWorkModeSwitch != null) {
if (mWorkUtilityView != null) {
// reset the position of the button and clear IME insets.
mWorkModeSwitch.getImeInsets().setEmpty();
mWorkModeSwitch.updateTranslationY();
mWorkUtilityView.getImeInsets().setEmpty();
mWorkUtilityView.updateTranslationY();
}
}
@@ -116,54 +116,54 @@ public class WorkProfileManager extends UserProfileManager
if (getAH() != null) {
getAH().mAppsList.updateAdapterItems();
}
if (mWorkModeSwitch != null) {
updateWorkFAB(mAllApps.getCurrentPage());
if (mWorkUtilityView != null) {
updateWorkUtilityViews(mAllApps.getCurrentPage());
}
if (getCurrentState() == STATE_ENABLED) {
attachWorkModeSwitch();
attachWorkUtilityViews();
} else if (getCurrentState() == STATE_DISABLED) {
detachWorkModeSwitch();
detachWorkUtilityViews();
}
}
/**
* Creates and attaches for profile toggle button to {@link ActivityAllAppsContainerView}
*/
public boolean attachWorkModeSwitch() {
public boolean attachWorkUtilityViews() {
if (!mAllApps.getAppsStore().hasModelFlag(
FLAG_HAS_SHORTCUT_PERMISSION | FLAG_QUIET_MODE_CHANGE_PERMISSION)) {
Log.e(TAG, "unable to attach work mode switch; Missing required permissions");
return false;
}
if (mWorkModeSwitch == null) {
mWorkModeSwitch = (WorkModeSwitch) mAllApps.getLayoutInflater().inflate(
R.layout.work_mode_fab, mAllApps, false);
if (mWorkUtilityView == null) {
mWorkUtilityView = (WorkUtilityView) mAllApps.getLayoutInflater().inflate(
R.layout.work_mode_utility_view, mAllApps, false);
}
if (mWorkModeSwitch.getParent() == null) {
mAllApps.addView(mWorkModeSwitch);
if (mWorkUtilityView.getParent() == null) {
mAllApps.addView(mWorkUtilityView);
}
if (mAllApps.getCurrentPage() != WORK) {
mWorkModeSwitch.animateVisibility(false);
mWorkUtilityView.animateVisibility(false);
}
if (getAH() != null) {
getAH().applyPadding();
}
mWorkModeSwitch.setOnClickListener(this::onWorkFabClicked);
mWorkUtilityView.setOnClickListener(this::onWorkFabClicked);
return true;
}
/**
* Removes work profile toggle button from {@link ActivityAllAppsContainerView}
*/
public void detachWorkModeSwitch() {
if (mWorkModeSwitch != null && mWorkModeSwitch.getParent() == mAllApps) {
mAllApps.removeView(mWorkModeSwitch);
public void detachWorkUtilityViews() {
if (mWorkUtilityView != null && mWorkUtilityView.getParent() == mAllApps) {
mAllApps.removeView(mWorkUtilityView);
}
mWorkModeSwitch = null;
mWorkUtilityView = null;
}
@Nullable
public WorkModeSwitch getWorkModeSwitch() {
return mWorkModeSwitch;
public WorkUtilityView getWorkUtilityView() {
return mWorkUtilityView;
}
private ActivityAllAppsContainerView.AdapterHolder getAH() {
@@ -199,7 +199,7 @@ public class WorkProfileManager extends UserProfileManager
}
private void onWorkFabClicked(View view) {
if (getCurrentState() == STATE_ENABLED && mWorkModeSwitch.isEnabled()) {
if (getCurrentState() == STATE_ENABLED && mWorkUtilityView.isEnabled()) {
logEvents(LAUNCHER_TURN_OFF_WORK_APPS_TAP);
setWorkProfileEnabled(false);
}
@@ -216,7 +216,7 @@ public class WorkProfileManager extends UserProfileManager
}
@Override
public void onScrolled(@NonNull RecyclerView recyclerView, int dx, int dy) {
WorkModeSwitch fab = getWorkModeSwitch();
WorkUtilityView fab = getWorkUtilityView();
if (fab == null){
return;
}
@@ -21,11 +21,13 @@ import android.animation.AnimatorSet;
import android.animation.ObjectAnimator;
import android.animation.ValueAnimator;
import android.content.Context;
import android.content.Intent;
import android.graphics.Rect;
import android.text.TextUtils;
import android.util.AttributeSet;
import android.view.ViewGroup;
import android.view.WindowInsets;
import android.widget.ImageButton;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.TextView;
@@ -36,6 +38,7 @@ import androidx.core.view.WindowInsetsCompat;
import com.android.app.animation.Interpolators;
import com.android.launcher3.DeviceProfile;
import com.android.launcher3.Flags;
import com.android.launcher3.Insettable;
import com.android.launcher3.R;
import com.android.launcher3.Utilities;
@@ -43,10 +46,13 @@ import com.android.launcher3.anim.AnimatedPropertySetter;
import com.android.launcher3.anim.KeyboardInsetAnimationCallback;
import com.android.launcher3.model.StringCache;
import com.android.launcher3.views.ActivityContext;
import java.util.ArrayList;
/**
* Work profile toggle switch shown at the bottom of AllApps work tab
* Work profile utility ViewGroup that is shown at the bottom of AllApps work tab
*/
public class WorkModeSwitch extends LinearLayout implements Insettable,
public class WorkUtilityView extends LinearLayout implements Insettable,
KeyboardInsetAnimationCallback.KeyboardInsetListener {
private static final int TEXT_EXPAND_OPACITY_DURATION = 300;
@@ -54,10 +60,14 @@ public class WorkModeSwitch extends LinearLayout implements Insettable,
private static final int EXPAND_COLLAPSE_DURATION = 300;
private static final int TEXT_ALPHA_EXPAND_DELAY = 80;
private static final int TEXT_ALPHA_COLLAPSE_DELAY = 0;
private static final int WORK_SCHEDULER_OPACITY_DURATION =
(int) (EXPAND_COLLAPSE_DURATION * 0.75f);
private static final int FLAG_FADE_ONGOING = 1 << 1;
private static final int FLAG_TRANSLATION_ONGOING = 1 << 2;
private static final int FLAG_IS_EXPAND = 1 << 3;
private static final int SCROLL_THRESHOLD_DP = 10;
private static final float WORK_SCHEDULER_SCALE_MIN = 0.25f;
private static final float WORK_SCHEDULER_SCALE_MAX = 1f;
private final Rect mInsets = new Rect();
private final Rect mImeInsets = new Rect();
@@ -67,22 +77,25 @@ public class WorkModeSwitch extends LinearLayout implements Insettable,
private final int mTextMarginStart;
private final int mTextMarginEnd;
private final int mIconMarginStart;
private final String mWorkSchedulerIntentAction;
// Threshold when user scrolls up/down to determine when should button extend/collapse
private final int mScrollThreshold;
private TextView mTextView;
private ImageView mIcon;
private ValueAnimator mPauseFABAnim;
private TextView mPauseText;
private ImageView mWorkIcon;
private ImageButton mSchedulerButton;
public WorkModeSwitch(@NonNull Context context) {
public WorkUtilityView(@NonNull Context context) {
this(context, null, 0);
}
public WorkModeSwitch(@NonNull Context context, @NonNull AttributeSet attrs) {
public WorkUtilityView(@NonNull Context context, @NonNull AttributeSet attrs) {
this(context, attrs, 0);
}
public WorkModeSwitch(@NonNull Context context, @NonNull AttributeSet attrs, int defStyleAttr) {
public WorkUtilityView(@NonNull Context context, @NonNull AttributeSet attrs,
int defStyleAttr) {
super(context, attrs, defStyleAttr);
mContext = context;
mScrollThreshold = Utilities.dpToPx(SCROLL_THRESHOLD_DP);
@@ -93,14 +106,17 @@ public class WorkModeSwitch extends LinearLayout implements Insettable,
R.dimen.work_fab_text_end_margin);
mIconMarginStart = mContext.getResources().getDimensionPixelSize(
R.dimen.work_fab_icon_start_margin_expanded);
mWorkSchedulerIntentAction = mContext.getResources().getString(
R.string.work_profile_scheduler_intent);
}
@Override
protected void onFinishInflate() {
super.onFinishInflate();
mTextView = findViewById(R.id.pause_text);
mIcon = findViewById(R.id.work_icon);
mPauseText = findViewById(R.id.pause_text);
mWorkIcon = findViewById(R.id.work_icon);
mSchedulerButton = findViewById(R.id.work_scheduler);
setSelected(true);
KeyboardInsetAnimationCallback keyboardInsetAnimationCallback =
new KeyboardInsetAnimationCallback(this);
@@ -109,6 +125,12 @@ public class WorkModeSwitch extends LinearLayout implements Insettable,
addFlag(FLAG_IS_EXPAND);
setInsets(mActivityContext.getDeviceProfile().getInsets());
updateStringFromCache();
mSchedulerButton.setVisibility(GONE);
if (shouldUseScheduler()) {
mSchedulerButton.setVisibility(VISIBLE);
mSchedulerButton.setOnClickListener(view ->
mContext.startActivity(new Intent(mWorkSchedulerIntentAction)));
}
}
@Override
@@ -166,8 +188,10 @@ public class WorkModeSwitch extends LinearLayout implements Insettable,
WindowInsetsCompat.toWindowInsetsCompat(insets, this);
if (windowInsetsCompat.isVisible(WindowInsetsCompat.Type.ime())) {
setInsets(mImeInsets, windowInsetsCompat.getInsets(WindowInsetsCompat.Type.ime()));
shrink();
} else {
mImeInsets.setEmpty();
extend();
}
updateTranslationY();
return super.onApplyWindowInsets(insets);
@@ -183,15 +207,63 @@ public class WorkModeSwitch extends LinearLayout implements Insettable,
super.setTranslationY(Math.min(translationY, -mInsets.bottom));
}
private ValueAnimator animateSchedulerScale(boolean isExpanding) {
float scaleFrom = isExpanding ? WORK_SCHEDULER_SCALE_MIN : WORK_SCHEDULER_SCALE_MAX;
float scaleTo = isExpanding ? WORK_SCHEDULER_SCALE_MAX : WORK_SCHEDULER_SCALE_MIN;
ValueAnimator schedulerScaleAnim = ObjectAnimator.ofFloat(scaleFrom, scaleTo);
schedulerScaleAnim.setDuration(EXPAND_COLLAPSE_DURATION);
schedulerScaleAnim.setInterpolator(Interpolators.STANDARD);
schedulerScaleAnim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator valueAnimator) {
float scale = (float) valueAnimator.getAnimatedValue();
mSchedulerButton.setScaleX(scale);
mSchedulerButton.setScaleY(scale);
}
});
schedulerScaleAnim.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationStart(Animator animation) {
if (isExpanding) {
mSchedulerButton.setVisibility(VISIBLE);
}
}
private void animatePillTransition(boolean isExpanding) {
@Override
public void onAnimationEnd(Animator animation) {
if (!isExpanding) {
mSchedulerButton.setVisibility(GONE);
}
}
});
return schedulerScaleAnim;
}
private ValueAnimator animateSchedulerAlpha(boolean isExpanding) {
float alphaFrom = isExpanding ? 0 : 1;
float alphaTo = isExpanding ? 1 : 0;
ValueAnimator schedulerAlphaAnim = ObjectAnimator.ofFloat(alphaFrom, alphaTo);
schedulerAlphaAnim.setDuration(WORK_SCHEDULER_OPACITY_DURATION);
schedulerAlphaAnim.setStartDelay(isExpanding ? 0 :
EXPAND_COLLAPSE_DURATION - WORK_SCHEDULER_OPACITY_DURATION);
schedulerAlphaAnim.setInterpolator(Interpolators.STANDARD);
schedulerAlphaAnim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator valueAnimator) {
mSchedulerButton.setAlpha((float) valueAnimator.getAnimatedValue());
}
});
return schedulerAlphaAnim;
}
private void animateWorkUtilityViews(boolean isExpanding) {
if (!shouldAnimate(isExpanding)) {
return;
}
AnimatorSet animatorSet = new AnimatedPropertySetter().buildAnim();
mTextView.measure(0,0);
int currentWidth = mTextView.getWidth();
int fullWidth = mTextView.getMeasuredWidth();
mPauseText.measure(0,0);
int currentWidth = mPauseText.getWidth();
int fullWidth = mPauseText.getMeasuredWidth();
float from = isExpanding ? 0 : currentWidth;
float to = isExpanding ? fullWidth : 0;
mPauseFABAnim = ObjectAnimator.ofFloat(from, to);
@@ -203,15 +275,15 @@ public class WorkModeSwitch extends LinearLayout implements Insettable,
float translation = (float) valueAnimator.getAnimatedValue();
float translationFraction = translation / fullWidth;
ViewGroup.MarginLayoutParams textViewLayoutParams =
(ViewGroup.MarginLayoutParams) mTextView.getLayoutParams();
(ViewGroup.MarginLayoutParams) mPauseText.getLayoutParams();
textViewLayoutParams.width = (int) translation;
textViewLayoutParams.setMarginStart((int) (mTextMarginStart * translationFraction));
textViewLayoutParams.setMarginEnd((int) (mTextMarginEnd * translationFraction));
mTextView.setLayoutParams(textViewLayoutParams);
mPauseText.setLayoutParams(textViewLayoutParams);
ViewGroup.MarginLayoutParams iconLayoutParams =
(ViewGroup.MarginLayoutParams) mIcon.getLayoutParams();
(ViewGroup.MarginLayoutParams) mWorkIcon.getLayoutParams();
iconLayoutParams.setMarginStart((int) (mIconMarginStart * translationFraction));
mIcon.setLayoutParams(iconLayoutParams);
mWorkIcon.setLayoutParams(iconLayoutParams);
}
});
mPauseFABAnim.addListener(new AnimatorListenerAdapter() {
@@ -220,21 +292,28 @@ public class WorkModeSwitch extends LinearLayout implements Insettable,
if (isExpanding) {
addFlag(FLAG_IS_EXPAND);
} else {
mTextView.setVisibility(GONE);
mPauseText.setVisibility(GONE);
removeFlag(FLAG_IS_EXPAND);
}
mTextView.setHorizontallyScrolling(false);
mTextView.setEllipsize(TextUtils.TruncateAt.END);
mPauseText.setHorizontallyScrolling(false);
mPauseText.setEllipsize(TextUtils.TruncateAt.END);
}
@Override
public void onAnimationStart(Animator animator) {
mTextView.setHorizontallyScrolling(true);
mTextView.setVisibility(VISIBLE);
mTextView.setEllipsize(null);
mPauseText.setHorizontallyScrolling(true);
mPauseText.setVisibility(VISIBLE);
mPauseText.setEllipsize(null);
}
});
animatorSet.playTogether(mPauseFABAnim, updatePauseTextAlpha(isExpanding));
ArrayList<Animator> animatorList = new ArrayList<>();
animatorList.add(mPauseFABAnim);
animatorList.add(updatePauseTextAlpha(isExpanding));
if (shouldUseScheduler()) {
animatorList.add(animateSchedulerScale(isExpanding));
animatorList.add(animateSchedulerAlpha(isExpanding));
}
animatorSet.playTogether(animatorList);
animatorSet.start();
}
@@ -250,7 +329,7 @@ public class WorkModeSwitch extends LinearLayout implements Insettable,
alphaAnim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator valueAnimator) {
mTextView.setAlpha((float) valueAnimator.getAnimatedValue());
mPauseText.setAlpha((float) valueAnimator.getAnimatedValue());
}
});
return alphaAnim;
@@ -287,11 +366,11 @@ public class WorkModeSwitch extends LinearLayout implements Insettable,
}
public void extend() {
animatePillTransition(true);
animateWorkUtilityViews(true);
}
public void shrink(){
animatePillTransition(false);
public void shrink() {
animateWorkUtilityViews(false);
}
/**
@@ -310,7 +389,11 @@ public class WorkModeSwitch extends LinearLayout implements Insettable,
public void updateStringFromCache(){
StringCache cache = mActivityContext.getStringCache();
if (cache != null) {
mTextView.setText(cache.workProfilePauseButton);
mPauseText.setText(cache.workProfilePauseButton);
}
}
private boolean shouldUseScheduler() {
return Flags.workSchedulerInWorkProfile() && !mWorkSchedulerIntentAction.isEmpty();
}
}
@@ -16,22 +16,25 @@
package com.android.launcher3.contextualeducation;
import static com.android.launcher3.util.MainThreadInitializedObject.forOverride;
import com.android.launcher3.R;
import com.android.launcher3.util.MainThreadInitializedObject;
import com.android.launcher3.util.ResourceBasedOverride;
import com.android.launcher3.util.SafeCloseable;
import com.android.launcher3.dagger.LauncherAppSingleton;
import com.android.launcher3.dagger.LauncherBaseAppComponent;
import com.android.launcher3.util.DaggerSingletonObject;
import com.android.systemui.contextualeducation.GestureType;
import javax.inject.Inject;
/**
* A class to update contextual education data. It is a no-op implementation and could be
* overridden by changing the resource value [R.string.contextual_edu_manager_class] to provide
* a real implementation.
* overridden through dagger modules to provide a real implementation.
*/
public class ContextualEduStatsManager implements ResourceBasedOverride, SafeCloseable {
public static final MainThreadInitializedObject<ContextualEduStatsManager> INSTANCE =
forOverride(ContextualEduStatsManager.class, R.string.contextual_edu_manager_class);
@LauncherAppSingleton
public class ContextualEduStatsManager {
public static final DaggerSingletonObject<ContextualEduStatsManager> INSTANCE =
new DaggerSingletonObject<>(LauncherBaseAppComponent::getContextualEduStatsManager);
@Inject
public ContextualEduStatsManager() { }
/**
* Updates contextual education stats when a gesture is triggered
@@ -40,8 +43,4 @@ public class ContextualEduStatsManager implements ResourceBasedOverride, SafeClo
*/
public void updateEduStats(boolean isTrackpadGesture, GestureType gestureType) {
}
@Override
public void close() {
}
}
@@ -18,7 +18,9 @@ package com.android.launcher3.dagger;
import android.content.Context;
import com.android.launcher3.contextualeducation.ContextualEduStatsManager;
import com.android.launcher3.graphics.IconShape;
import com.android.launcher3.model.ItemInstallQueue;
import com.android.launcher3.pm.InstallSessionHelper;
import com.android.launcher3.util.ApiWrapper;
import com.android.launcher3.util.DaggerSingletonTracker;
@@ -43,9 +45,11 @@ import dagger.BindsInstance;
public interface LauncherBaseAppComponent {
DaggerSingletonTracker getDaggerSingletonTracker();
ApiWrapper getApiWrapper();
ContextualEduStatsManager getContextualEduStatsManager();
CustomWidgetManager getCustomWidgetManager();
IconShape getIconShape();
InstallSessionHelper getInstallSessionHelper();
ItemInstallQueue getItemInstallQueue();
RefreshRateTracker getRefreshRateTracker();
ScreenOnTracker getScreenOnTracker();
SettingsCache getSettingsCache();
@@ -1,172 +0,0 @@
/*
* Copyright (C) 2022 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.launcher3.icons;
import static android.graphics.Paint.FILTER_BITMAP_FLAG;
import android.annotation.TargetApi;
import android.graphics.Bitmap;
import android.graphics.Bitmap.Config;
import android.graphics.BlendMode;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.ColorFilter;
import android.graphics.ColorMatrix;
import android.graphics.ColorMatrixColorFilter;
import android.graphics.Paint;
import android.graphics.PixelFormat;
import android.graphics.Rect;
import android.graphics.drawable.AdaptiveIconDrawable;
import android.graphics.drawable.Drawable;
import android.os.Build;
import androidx.annotation.WorkerThread;
import com.android.launcher3.icons.BaseIconFactory.ClippedMonoDrawable;
import java.nio.ByteBuffer;
/**
* Utility class to generate monochrome icons version for a given drawable.
*/
@TargetApi(Build.VERSION_CODES.TIRAMISU)
public class MonochromeIconFactory extends Drawable {
private final Bitmap mFlatBitmap;
private final Canvas mFlatCanvas;
private final Paint mCopyPaint;
private final Bitmap mAlphaBitmap;
private final Canvas mAlphaCanvas;
private final byte[] mPixels;
private final int mBitmapSize;
private final int mEdgePixelLength;
private final Paint mDrawPaint;
private final Rect mSrcRect;
MonochromeIconFactory(int iconBitmapSize) {
float extraFactor = AdaptiveIconDrawable.getExtraInsetFraction();
float viewPortScale = 1 / (1 + 2 * extraFactor);
mBitmapSize = Math.round(iconBitmapSize * 2 * viewPortScale);
mPixels = new byte[mBitmapSize * mBitmapSize];
mEdgePixelLength = mBitmapSize * (mBitmapSize - iconBitmapSize) / 2;
mFlatBitmap = Bitmap.createBitmap(mBitmapSize, mBitmapSize, Config.ARGB_8888);
mFlatCanvas = new Canvas(mFlatBitmap);
mAlphaBitmap = Bitmap.createBitmap(mBitmapSize, mBitmapSize, Config.ALPHA_8);
mAlphaCanvas = new Canvas(mAlphaBitmap);
mDrawPaint = new Paint(FILTER_BITMAP_FLAG);
mDrawPaint.setColor(Color.WHITE);
mSrcRect = new Rect(0, 0, mBitmapSize, mBitmapSize);
mCopyPaint = new Paint(FILTER_BITMAP_FLAG);
mCopyPaint.setBlendMode(BlendMode.SRC);
// Crate a color matrix which converts the icon to grayscale and then uses the average
// of RGB components as the alpha component.
ColorMatrix satMatrix = new ColorMatrix();
satMatrix.setSaturation(0);
float[] vals = satMatrix.getArray();
vals[15] = vals[16] = vals[17] = .3333f;
vals[18] = vals[19] = 0;
mCopyPaint.setColorFilter(new ColorMatrixColorFilter(vals));
}
private void drawDrawable(Drawable drawable) {
if (drawable != null) {
drawable.setBounds(0, 0, mBitmapSize, mBitmapSize);
drawable.draw(mFlatCanvas);
}
}
/**
* Creates a monochrome version of the provided drawable
*/
@WorkerThread
public Drawable wrap(AdaptiveIconDrawable icon) {
mFlatCanvas.drawColor(Color.BLACK);
drawDrawable(icon.getBackground());
drawDrawable(icon.getForeground());
generateMono();
return new ClippedMonoDrawable(this);
}
@WorkerThread
private void generateMono() {
mAlphaCanvas.drawBitmap(mFlatBitmap, 0, 0, mCopyPaint);
// Scale the end points:
ByteBuffer buffer = ByteBuffer.wrap(mPixels);
buffer.rewind();
mAlphaBitmap.copyPixelsToBuffer(buffer);
int min = 0xFF;
int max = 0;
for (byte b : mPixels) {
min = Math.min(min, b & 0xFF);
max = Math.max(max, b & 0xFF);
}
if (min < max) {
// rescale pixels to increase contrast
float range = max - min;
// In order to check if the colors should be flipped, we just take the average color
// of top and bottom edge which should correspond to be background color. If the edge
// colors have more opacity, we flip the colors;
int sum = 0;
for (int i = 0; i < mEdgePixelLength; i++) {
sum += (mPixels[i] & 0xFF);
sum += (mPixels[mPixels.length - 1 - i] & 0xFF);
}
float edgeAverage = sum / (mEdgePixelLength * 2f);
float edgeMapped = (edgeAverage - min) / range;
boolean flipColor = edgeMapped > .5f;
for (int i = 0; i < mPixels.length; i++) {
int p = mPixels[i] & 0xFF;
int p2 = Math.round((p - min) * 0xFF / range);
mPixels[i] = flipColor ? (byte) (255 - p2) : (byte) (p2);
}
buffer.rewind();
mAlphaBitmap.copyPixelsFromBuffer(buffer);
}
}
@Override
public void draw(Canvas canvas) {
canvas.drawBitmap(mAlphaBitmap, mSrcRect, getBounds(), mDrawPaint);
}
@Override
public int getOpacity() {
return PixelFormat.TRANSLUCENT;
}
@Override
public void setAlpha(int i) {
mDrawPaint.setAlpha(i);
}
@Override
public void setColorFilter(ColorFilter colorFilter) {
mDrawPaint.setColorFilter(colorFilter);
}
}
@@ -38,7 +38,6 @@ import android.util.Log;
import androidx.annotation.NonNull;
import androidx.annotation.VisibleForTesting;
import com.android.launcher3.Flags;
import com.android.launcher3.InvariantDeviceProfile;
import com.android.launcher3.LauncherPrefs;
import com.android.launcher3.LauncherSettings;
@@ -86,6 +85,9 @@ public class GridSizeMigrationDBController {
if (needsToMigrate) {
Log.i(TAG, "Migration is needed. destDeviceState: " + destDeviceState
+ ", srcDeviceState: " + srcDeviceState);
} else {
Log.i(TAG, "Migration is not needed. destDeviceState: " + destDeviceState
+ ", srcDeviceState: " + srcDeviceState);
}
return needsToMigrate;
}
@@ -118,21 +120,13 @@ public class GridSizeMigrationDBController {
@NonNull DatabaseHelper target,
@NonNull SQLiteDatabase source) {
Log.i("b/360462379", "Going from " + srcDeviceState.getColumns() + "x"
+ srcDeviceState.getRows());
Log.i("b/360462379", "Going to " + destDeviceState.getColumns() + "x"
+ destDeviceState.getRows());
if (!needsToMigrate(srcDeviceState, destDeviceState)) {
Log.i("b/360462379", "Does not need to migrate.");
return true;
}
if (LauncherPrefs.get(context).get(IS_FIRST_LOAD_AFTER_RESTORE)
&& Flags.enableGridMigrationFix()
&& srcDeviceState.getColumns().equals(destDeviceState.getColumns())
&& srcDeviceState.getRows() < destDeviceState.getRows()) {
Log.i("b/360462379", "Grid migration fix entry point.");
// Only use this strategy when comparing the previous grid to the new grid and the
// columns are the same and the destination has more rows
copyTable(source, TABLE_NAME, target.getWritableDatabase(), TABLE_NAME, context);
@@ -339,7 +339,7 @@ class GridSizeMigrationLogic {
srcDeviceState: DeviceGridState,
destDeviceState: DeviceGridState,
): Boolean {
return (isFirstLoad && Flags.enableGridMigrationFix()) &&
return isFirstLoad &&
srcDeviceState.columns == destDeviceState.columns &&
srcDeviceState.rows < destDeviceState.rows
}
@@ -45,16 +45,18 @@ import com.android.launcher3.InvariantDeviceProfile;
import com.android.launcher3.Launcher;
import com.android.launcher3.LauncherAppState;
import com.android.launcher3.LauncherSettings.Favorites;
import com.android.launcher3.dagger.ApplicationContext;
import com.android.launcher3.dagger.LauncherAppSingleton;
import com.android.launcher3.dagger.LauncherBaseAppComponent;
import com.android.launcher3.logging.FileLog;
import com.android.launcher3.model.data.ItemInfo;
import com.android.launcher3.model.data.LauncherAppWidgetInfo;
import com.android.launcher3.model.data.WorkspaceItemInfo;
import com.android.launcher3.shortcuts.ShortcutKey;
import com.android.launcher3.shortcuts.ShortcutRequest;
import com.android.launcher3.util.MainThreadInitializedObject;
import com.android.launcher3.util.DaggerSingletonObject;
import com.android.launcher3.util.PersistedItemArray;
import com.android.launcher3.util.Preconditions;
import com.android.launcher3.util.SafeCloseable;
import com.android.launcher3.widget.LauncherAppWidgetProviderInfo;
import java.util.HashSet;
@@ -62,10 +64,13 @@ import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import javax.inject.Inject;
/**
* Class to maintain a queue of pending items to be added to the workspace.
*/
public class ItemInstallQueue implements SafeCloseable {
@LauncherAppSingleton
public class ItemInstallQueue {
private static final String LOG = "ItemInstallQueue";
@@ -81,9 +86,8 @@ public class ItemInstallQueue implements SafeCloseable {
public static final int NEW_SHORTCUT_BOUNCE_DURATION = 450;
public static final int NEW_SHORTCUT_STAGGER_DELAY = 85;
public static MainThreadInitializedObject<ItemInstallQueue> INSTANCE =
new MainThreadInitializedObject<>(ItemInstallQueue::new);
public static DaggerSingletonObject<ItemInstallQueue> INSTANCE =
new DaggerSingletonObject<>(LauncherBaseAppComponent::getItemInstallQueue);
private final PersistedItemArray<PendingInstallShortcutInfo> mStorage =
new PersistedItemArray<>(APPS_PENDING_INSTALL);
private final Context mContext;
@@ -95,13 +99,11 @@ public class ItemInstallQueue implements SafeCloseable {
// Only accessed on worker thread
private List<PendingInstallShortcutInfo> mItems;
private ItemInstallQueue(Context context) {
@Inject
public ItemInstallQueue(@ApplicationContext Context context) {
mContext = context;
}
@Override
public void close() {}
@WorkerThread
private void ensureQueueLoaded() {
Preconditions.assertWorkerThread();
@@ -51,7 +51,6 @@ import androidx.annotation.NonNull;
import androidx.annotation.VisibleForTesting;
import androidx.annotation.WorkerThread;
import com.android.launcher3.Flags;
import com.android.launcher3.InvariantDeviceProfile;
import com.android.launcher3.LauncherAppState;
import com.android.launcher3.LauncherFiles;
@@ -124,18 +123,14 @@ public class RestoreDbTask {
// executed again.
LauncherPrefs.get(context).removeSync(RESTORE_DEVICE);
if (Flags.enableNarrowGridRestore()) {
DeviceGridState deviceGridState = new DeviceGridState(context);
String oldPhoneFileName = deviceGridState.getDbFile();
List<String> previousDbs = existingDbs(context);
removeOldDBs(context, oldPhoneFileName);
// The idp before this contains data about the old phone, after this it becomes the idp
// of the current phone.
idp.reset(context);
trySettingPreviousGridAsCurrent(context, idp, oldPhoneFileName, previousDbs);
} else {
idp.reinitializeAfterRestore(context);
}
DeviceGridState deviceGridState = new DeviceGridState(context);
String oldPhoneFileName = deviceGridState.getDbFile();
List<String> previousDbs = existingDbs(context);
removeOldDBs(context, oldPhoneFileName);
// The idp before this contains data about the old phone, after this it becomes the idp
// of the current phone.
idp.reset(context);
trySettingPreviousGridAsCurrent(context, idp, oldPhoneFileName, previousDbs);
}
@@ -77,7 +77,6 @@ public class StateAnimationConfig {
ANIM_WORKSPACE_PAGE_TRANSLATE_X,
ANIM_OVERVIEW_SPLIT_SELECT_FLOATING_TASK_TRANSLATE_OFFSCREEN,
ANIM_OVERVIEW_SPLIT_SELECT_INSTRUCTIONS_FADE,
ANIM_ALL_APPS_BOTTOM_SHEET_FADE,
ANIM_ALL_APPS_KEYBOARD_FADE
})
@Retention(RetentionPolicy.SOURCE)
@@ -101,8 +100,7 @@ public class StateAnimationConfig {
public static final int ANIM_WORKSPACE_PAGE_TRANSLATE_X = 15;
public static final int ANIM_OVERVIEW_SPLIT_SELECT_FLOATING_TASK_TRANSLATE_OFFSCREEN = 17;
public static final int ANIM_OVERVIEW_SPLIT_SELECT_INSTRUCTIONS_FADE = 18;
public static final int ANIM_ALL_APPS_BOTTOM_SHEET_FADE = 19;
public static final int ANIM_ALL_APPS_KEYBOARD_FADE = 20;
public static final int ANIM_ALL_APPS_KEYBOARD_FADE = 19;
private static final int ANIM_TYPES_COUNT = 21;
@@ -198,7 +198,7 @@ public class AllAppsSwipeController extends AbstractStateChangeTouchController {
* Applies Animation config values for transition from all apps to home.
*/
public static void applyAllAppsToNormalConfig(Launcher launcher, StateAnimationConfig config) {
if (launcher.getDeviceProfile().isTablet) {
if (launcher.getDeviceProfile().shouldShowAllAppsOnSheet()) {
config.setInterpolator(ANIM_SCRIM_FADE,
Interpolators.reverse(ALL_APPS_SCRIM_RESPONDER));
config.setInterpolator(ANIM_ALL_APPS_FADE, FINAL_FRAME);
@@ -240,7 +240,7 @@ public class AllAppsSwipeController extends AbstractStateChangeTouchController {
*/
public static void applyNormalToAllAppsAnimConfig(
Launcher launcher, StateAnimationConfig config) {
if (launcher.getDeviceProfile().isTablet) {
if (launcher.getDeviceProfile().shouldShowAllAppsOnSheet()) {
config.setInterpolator(ANIM_ALL_APPS_FADE, INSTANT);
config.setInterpolator(ANIM_SCRIM_FADE, ALL_APPS_SCRIM_RESPONDER);
if (!config.isUserControlled()) {
@@ -158,16 +158,16 @@ public class TaplWorkProfileTest extends AbstractLauncherUiTest<Launcher> {
waitForLauncherCondition("work profile initial state check failed", launcher ->
manager.getWorkModeSwitch() != null
manager.getWorkUtilityView() != null
&& manager.getCurrentState() == WorkProfileManager.STATE_ENABLED
&& manager.getWorkModeSwitch().isEnabled(),
&& manager.getWorkUtilityView().isEnabled(),
LauncherInstrumentation.WAIT_TIME_MS);
//start work profile toggle OFF test
executeOnLauncher(l -> {
// Ensure updates are not deferred so notification happens when apps pause.
l.getAppsView().getAppsStore().disableDeferUpdates(DEFER_UPDATES_TEST);
l.getAppsView().getWorkManager().getWorkModeSwitch().performClick();
l.getAppsView().getWorkManager().getWorkUtilityView().performClick();
});
waitForLauncherCondition("Work profile toggle OFF failed", launcher -> {
@@ -29,6 +29,7 @@ import androidx.test.uiautomator.UiObject2;
import com.android.launcher3.tapl.LauncherInstrumentation.NavigationModel;
import com.android.launcher3.tapl.LauncherInstrumentation.TrackpadGestureType;
import com.android.launcher3.tapl.OverviewTask.TaskViewType;
import com.android.launcher3.testing.shared.TestProtocol;
import java.util.List;
@@ -121,12 +122,31 @@ public abstract class Background extends LauncherInstrumentation.VisibleContaine
if (mLauncher.isTablet()) {
List<UiObject2> tasks = mLauncher.getDevice().findObjects(
TASK_SELECTOR);
final int centerX = mLauncher.getDevice().getDisplayWidth() / 2;
mLauncher.assertTrue(
"Task(s) found to the right of the swiped task",
tasks.stream().allMatch(t ->
t.getVisibleBounds().right < centerX
|| t.getVisibleBounds().centerX() == centerX));
UiObject2 centerTask = tasks.stream()
.filter(t -> t.getVisibleCenter().x == centerX)
.findFirst()
.orElse(null);
if (centerTask != null) {
mLauncher.assertTrue(
"Task(s) found to the right of the swiped task",
tasks.stream()
.filter(t -> t != centerTask
&& OverviewTask.getType(t)
!= TaskViewType.DESKTOP)
.allMatch(t -> t.getVisibleBounds().right
< centerTask.getVisibleBounds().left));
mLauncher.assertTrue(
"DesktopTask(s) found to the left of the swiped task",
tasks.stream()
.filter(t -> t != centerTask
&& OverviewTask.getType(t)
== TaskViewType.DESKTOP)
.allMatch(t -> t.getVisibleBounds().left
> centerTask.getVisibleBounds().right));
}
}
}
@@ -57,7 +57,7 @@ public final class OverviewTask {
mLauncher.assertNotNull("task must not be null", task);
mTask = task;
mOverview = overview;
mType = getType();
mType = getType(task);
verifyActiveContainer();
}
@@ -304,8 +304,12 @@ public final class OverviewTask {
return containsContentDescription(expected, DEFAULT);
}
private TaskViewType getType() {
String resourceName = mTask.getResourceName();
/**
* Returns the TaskView type of the task. It will return whether the task is a single TaskView,
* a GroupedTaskView or a DesktopTaskView.
*/
static TaskViewType getType(UiObject2 task) {
String resourceName = task.getResourceName();
if (resourceName.endsWith("task_view_grouped")) {
return TaskViewType.GROUPED;
} else if (resourceName.endsWith("task_view_desktop")) {
@@ -345,7 +349,7 @@ public final class OverviewTask {
}
}
private enum TaskViewType {
enum TaskViewType {
SINGLE,
GROUPED,
DESKTOP