Merge "Import translations. DO NOT MERGE ANYWHERE" into tm-d1-dev
diff --git a/quickstep/src/com/android/launcher3/taskbar/NavbarButtonsViewController.java b/quickstep/src/com/android/launcher3/taskbar/NavbarButtonsViewController.java
index 349dd0a..cbdbdb9 100644
--- a/quickstep/src/com/android/launcher3/taskbar/NavbarButtonsViewController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/NavbarButtonsViewController.java
@@ -16,6 +16,7 @@
 package com.android.launcher3.taskbar;
 
 import static com.android.launcher3.LauncherAnimUtils.VIEW_TRANSLATE_X;
+import static com.android.launcher3.Utilities.getDescendantCoordRelativeToAncestor;
 import static com.android.launcher3.taskbar.LauncherTaskbarUIController.SYSUI_SURFACE_PROGRESS_INDEX;
 import static com.android.launcher3.taskbar.TaskbarNavButtonController.BUTTON_A11Y;
 import static com.android.launcher3.taskbar.TaskbarNavButtonController.BUTTON_BACK;
@@ -51,6 +52,7 @@
 import android.graphics.drawable.AnimatedVectorDrawable;
 import android.graphics.drawable.PaintDrawable;
 import android.inputmethodservice.InputMethodService;
+import android.os.Handler;
 import android.util.Property;
 import android.view.Gravity;
 import android.view.MotionEvent;
@@ -158,6 +160,7 @@
     private BaseDragLayer<TaskbarActivityContext> mSeparateWindowParent; // Initialized in init.
     private final ViewTreeObserverWrapper.OnComputeInsetsListener mSeparateWindowInsetsComputer =
             this::onComputeInsetsForSeparateWindow;
+    private final RecentsHitboxExtender mHitboxExtender = new RecentsHitboxExtender();
 
     public NavbarButtonsViewController(TaskbarActivityContext context, FrameLayout navButtonsView) {
         mContext = context;
@@ -297,6 +300,8 @@
                 navButtonsLayoutParams.setMarginEnd(navButtonsLayoutParams.getMarginStart());
                 navButtonsLayoutParams.gravity = Gravity.CENTER;
                 mNavButtonContainer.requestLayout();
+
+                mHomeButton.setOnLongClickListener(null);
             }
 
             // Animate taskbar background when either..
@@ -388,8 +393,7 @@
                         || (flags & FLAG_KEYGUARD_VISIBLE) != 0,
                 VIEW_TRANSLATE_X, navButtonSize * (isRtl ? -2 : 2), 0));
 
-
-        // home and recents buttons
+        // home button
         mHomeButton = addButton(R.drawable.ic_sysbar_home, BUTTON_HOME, navContainer,
                 navButtonController, R.id.home);
         mHomeButtonAlpha = new MultiValueAlpha(mHomeButton, NUM_ALPHA_CHANNELS);
@@ -399,8 +403,21 @@
                         ALPHA_INDEX_KEYGUARD_OR_DISABLE),
                 flags -> (flags & FLAG_KEYGUARD_VISIBLE) == 0 &&
                         (flags & FLAG_DISABLE_HOME) == 0));
+
+        // Recents button
         View recentsButton = addButton(R.drawable.ic_sysbar_recent, BUTTON_RECENTS,
                 navContainer, navButtonController, R.id.recent_apps);
+        mHitboxExtender.init(recentsButton, mNavButtonsView, mContext.getDeviceProfile(),
+                () -> {
+                    float[] recentsCoords = new float[2];
+                    getDescendantCoordRelativeToAncestor(recentsButton, mNavButtonsView,
+                            recentsCoords, false);
+                    return recentsCoords;
+                }, new Handler());
+        recentsButton.setOnClickListener(v -> {
+            navButtonController.onButtonClick(BUTTON_RECENTS);
+            mHitboxExtender.onRecentsButtonClicked();
+        });
         mPropertyHolders.add(new StatePropertyHolder(recentsButton,
                 flags -> (flags & FLAG_KEYGUARD_VISIBLE) == 0 && (flags & FLAG_DISABLE_RECENTS) == 0
                         && !mContext.isNavBarKidsModeActive()));
@@ -504,6 +521,9 @@
             View button = mAllButtons.get(i);
             if (button.getVisibility() == View.VISIBLE) {
                 parent.getDescendantRectRelativeToSelf(button, mTempRect);
+                if (mHitboxExtender.extendedHitboxEnabled()) {
+                    mTempRect.bottom += mContext.mDeviceProfile.getTaskbarOffsetY();
+                }
                 outRegion.op(mTempRect, Op.UNION);
             }
         }
@@ -733,6 +753,17 @@
         return str.toString();
     }
 
+    public TouchController getTouchController() {
+        return mHitboxExtender;
+    }
+
+    /**
+     * @param alignment 0 -> Taskbar, 1 -> Workspace
+     */
+    public void updateTaskbarAlignment(float alignment) {
+        mHitboxExtender.onAnimationProgressToOverview(alignment);
+    }
+
     private class RotationButtonListener implements RotationButton.RotationButtonUpdatesCallback {
         @Override
         public void onVisibilityChanged(boolean isVisible) {
diff --git a/quickstep/src/com/android/launcher3/taskbar/RecentsHitboxExtender.java b/quickstep/src/com/android/launcher3/taskbar/RecentsHitboxExtender.java
new file mode 100644
index 0000000..4651570
--- /dev/null
+++ b/quickstep/src/com/android/launcher3/taskbar/RecentsHitboxExtender.java
@@ -0,0 +1,134 @@
+/*
+ * 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.taskbar;
+
+import android.graphics.Rect;
+import android.os.Handler;
+import android.view.MotionEvent;
+import android.view.TouchDelegate;
+import android.view.View;
+
+import com.android.launcher3.DeviceProfile;
+import com.android.launcher3.util.TouchController;
+
+import java.util.function.Supplier;
+
+/**
+ * Extends the Recents touch area during the taskbar to overview animation
+ * to give user some error room when trying to quickly double tap recents button since it moves.
+ *
+ * Listens for icon alignment as our indication for the animation.
+ */
+public class RecentsHitboxExtender implements TouchController {
+
+    private static final int RECENTS_HITBOX_TIMEOUT_MS = 500;
+
+    private View mRecentsButton;
+    private View mRecentsParent;
+    private DeviceProfile mDeviceProfile;
+    private Supplier<float[]> mParentCoordSupplier;
+    private TouchDelegate mRecentsTouchDelegate;
+    /**
+     * Will be true while the animation from taskbar to overview is occurring.
+     * Lifecycle of this variable slightly extends past the animation by
+     * {@link #RECENTS_HITBOX_TIMEOUT_MS}, so can use this variable as a proxy for if
+     * the current hitbox is extended or not.
+     */
+    private boolean mAnimatingFromTaskbarToOverview;
+    private float mLastIconAlignment;
+    private final Rect mRecentsHitBox = new Rect();
+    private boolean mRecentsButtonClicked;
+    private Handler mHandler;
+    private final Runnable mRecentsHitboxResetRunnable = this::reset;
+
+    public void init(View recentsButton, View recentsParent, DeviceProfile deviceProfile,
+            Supplier<float[]> parentCoordSupplier, Handler handler) {
+        mRecentsButton = recentsButton;
+        mRecentsParent = recentsParent;
+        mDeviceProfile = deviceProfile;
+        mParentCoordSupplier = parentCoordSupplier;
+        mHandler = handler;
+    }
+
+    public void onRecentsButtonClicked() {
+        mRecentsButtonClicked = true;
+    }
+
+    /**
+     * @param progress 0 -> Taskbar, 1 -> Overview
+     */
+    public void onAnimationProgressToOverview(float progress) {
+        if (progress == 1 || progress == 0) {
+            // Done w/ animation
+            mLastIconAlignment = progress;
+            if (mAnimatingFromTaskbarToOverview) {
+                if (progress == 1) {
+                    // Finished animation to workspace, remove the touch delegate shortly
+                    mHandler.postDelayed(mRecentsHitboxResetRunnable, RECENTS_HITBOX_TIMEOUT_MS);
+                    return;
+                } else {
+                    // Went back to taskbar, reset immediately
+                    mHandler.removeCallbacks(mRecentsHitboxResetRunnable);
+                    reset();
+                }
+            }
+        }
+
+        if (mAnimatingFromTaskbarToOverview) {
+            return;
+        }
+
+        if (progress > 0 && mLastIconAlignment == 0 && mRecentsButtonClicked) {
+            // Starting animation, previously we were showing taskbar
+            mAnimatingFromTaskbarToOverview = true;
+            float[] recentsCoords = mParentCoordSupplier.get();
+            int x = (int) recentsCoords[0];
+            int y = (int) (recentsCoords[1]);
+            // Extend hitbox vertically by the offset amount from mDeviceProfile.getTaskbarOffsetY()
+            mRecentsHitBox.set(x, y,
+                    x + mRecentsButton.getWidth(),
+                    y + mRecentsButton.getHeight() + mDeviceProfile.getTaskbarOffsetY()
+            );
+            mRecentsTouchDelegate = new TouchDelegate(mRecentsHitBox, mRecentsButton);
+            mRecentsParent.setTouchDelegate(mRecentsTouchDelegate);
+        }
+    }
+
+    private void reset() {
+        mAnimatingFromTaskbarToOverview = false;
+        mRecentsButton.setTouchDelegate(null);
+        mRecentsHitBox.setEmpty();
+        mRecentsButtonClicked = false;
+    }
+
+    /**
+     * @return {@code true} if the bounds for recents touches are currently extended
+     */
+    public boolean extendedHitboxEnabled() {
+        return mAnimatingFromTaskbarToOverview;
+    }
+
+    @Override
+    public boolean onControllerTouchEvent(MotionEvent ev) {
+        return mRecentsTouchDelegate.onTouchEvent(ev);
+    }
+
+    @Override
+    public boolean onControllerInterceptTouchEvent(MotionEvent ev) {
+        return mRecentsHitBox.contains((int)ev.getX(), (int)ev.getY());
+    }
+}
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarDragLayerController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarDragLayerController.java
index 3e2695c..99c59a8 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarDragLayerController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarDragLayerController.java
@@ -182,7 +182,8 @@
          */
         public TouchController[] getTouchControllers() {
             return new TouchController[]{mActivity.getDragController(),
-                    mControllers.taskbarForceVisibleImmersiveController};
+                    mControllers.taskbarForceVisibleImmersiveController,
+                    mControllers.navbarButtonsViewController.getTouchController()};
         }
     }
 }
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarLauncherStateController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarLauncherStateController.java
index 2be1179..052c695 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarLauncherStateController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarLauncherStateController.java
@@ -428,6 +428,7 @@
 
         // Switch taskbar and hotseat in last frame
         setTaskbarViewVisible(alignment < 1);
+        mControllers.navbarButtonsViewController.updateTaskbarAlignment(alignment);
     }
 
     private float getCurrentIconAlignmentRatioBetweenAppAndHome() {
diff --git a/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java b/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java
index 13389c0..f60b225 100644
--- a/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java
+++ b/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java
@@ -1177,6 +1177,11 @@
     }
 
     private void doLogGesture(GestureEndTarget endTarget, @Nullable TaskView targetTask) {
+        if (mDp == null || !mDp.isGestureMode || mDownPos == null) {
+            // We probably never received an animation controller, skip logging.
+            return;
+        }
+
         StatsLogManager.EventEnum event;
         switch (endTarget) {
             case HOME:
@@ -1200,11 +1205,6 @@
             logger.withItemInfo(targetTask.getItemInfo());
         }
 
-        DeviceProfile dp = mDp;
-        if (dp == null || mDownPos == null) {
-            // We probably never received an animation controller, skip logging.
-            return;
-        }
         int pageIndex = endTarget == LAST_TASK || mRecentsView == null
                 ? LOG_NO_OP_PAGE_INDEX
                 : mRecentsView.getNextPage();
diff --git a/quickstep/src/com/android/quickstep/TaskAnimationManager.java b/quickstep/src/com/android/quickstep/TaskAnimationManager.java
index 2565674..54f457d 100644
--- a/quickstep/src/com/android/quickstep/TaskAnimationManager.java
+++ b/quickstep/src/com/android/quickstep/TaskAnimationManager.java
@@ -192,6 +192,10 @@
                                 RemoteAnimationTargetCompat.wrap(nonAppTargets) /* nonApps */);
                         return;
                     }
+                } else if (nonAppTargets != null && nonAppTargets.length > 0) {
+                    TaskViewUtils.createSplitAuxiliarySurfacesAnimator(
+                            RemoteAnimationTargetCompat.wrap(nonAppTargets) /* nonApps */,
+                            true /*shown*/, dividerAnimator -> dividerAnimator.start());
                 }
                 if (mController != null) {
                     if (mLastAppearedTaskTarget == null
diff --git a/quickstep/tests/src/com/android/launcher3/taskbar/RecentsHitboxExtenderTest.java b/quickstep/tests/src/com/android/launcher3/taskbar/RecentsHitboxExtenderTest.java
new file mode 100644
index 0000000..929bff3
--- /dev/null
+++ b/quickstep/tests/src/com/android/launcher3/taskbar/RecentsHitboxExtenderTest.java
@@ -0,0 +1,125 @@
+package com.android.launcher3.taskbar;
+
+import static android.view.MotionEvent.ACTION_DOWN;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyLong;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.app.Instrumentation;
+import android.content.Context;
+import android.os.Handler;
+import android.view.MotionEvent;
+import android.view.View;
+
+import androidx.test.platform.app.InstrumentationRegistry;
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.launcher3.DeviceProfile;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import java.util.function.Supplier;
+
+@RunWith(AndroidJUnit4.class)
+public class RecentsHitboxExtenderTest {
+
+    private static final int TASKBAR_OFFSET_Y = 35;
+    private static final int BUTTON_WIDTH = 10;
+    private static final int BUTTON_HEIGHT = 10;
+
+    private final RecentsHitboxExtender mHitboxExtender = new RecentsHitboxExtender();
+    @Mock
+    View mMockRecentsButton;
+    @Mock
+    View mMockRecentsParent;
+    @Mock
+    DeviceProfile mMockDeviceProfile;
+    @Mock
+    Handler mMockHandler;
+    Context mContext;
+
+    float[] mRecentsCoords = new float[]{0,0};
+    private final Supplier<float[]> mSupplier = () -> mRecentsCoords;
+
+    @Before
+    public void setup() {
+        MockitoAnnotations.initMocks(this);
+        Instrumentation instrumentation = InstrumentationRegistry.getInstrumentation();
+        mContext = instrumentation.getContext();
+        mHitboxExtender.init(mMockRecentsButton, mMockRecentsParent, mMockDeviceProfile, mSupplier,
+                mMockHandler);
+        when(mMockDeviceProfile.getTaskbarOffsetY()).thenReturn(TASKBAR_OFFSET_Y);
+        when(mMockRecentsButton.getContext()).thenReturn(mContext);
+        when(mMockRecentsButton.getWidth()).thenReturn(BUTTON_WIDTH);
+        when(mMockRecentsButton.getHeight()).thenReturn(BUTTON_HEIGHT);
+    }
+
+    @Test
+    public void noRecentsButtonClick_notActive() {
+        mHitboxExtender.onAnimationProgressToOverview(0);
+        mHitboxExtender.onAnimationProgressToOverview(0.5f);
+        assertFalse(mHitboxExtender.extendedHitboxEnabled());
+    }
+
+    @Test
+    public void recentsButtonClick_active() {
+        mHitboxExtender.onRecentsButtonClicked();
+        mHitboxExtender.onAnimationProgressToOverview(0);
+        mHitboxExtender.onAnimationProgressToOverview(0.5f);
+        assertTrue(mHitboxExtender.extendedHitboxEnabled());
+    }
+
+    @Test
+    public void homeToTaskbar_notActive() {
+        mHitboxExtender.onAnimationProgressToOverview(1);
+        mHitboxExtender.onAnimationProgressToOverview(0.5f);
+        assertFalse(mHitboxExtender.extendedHitboxEnabled());
+    }
+
+    @Test
+    public void animationEndReset() {
+        mHitboxExtender.onRecentsButtonClicked();
+        mHitboxExtender.onAnimationProgressToOverview(0);
+        mHitboxExtender.onAnimationProgressToOverview(0.5f);
+        assertTrue(mHitboxExtender.extendedHitboxEnabled());
+        mHitboxExtender.onAnimationProgressToOverview(1);
+        verify(mMockHandler, times(1)).postDelayed(any(), anyLong());
+    }
+
+    @Test
+    public void motionWithinHitbox() {
+        mHitboxExtender.onRecentsButtonClicked();
+        mHitboxExtender.onAnimationProgressToOverview(0);
+        mHitboxExtender.onAnimationProgressToOverview(0.5f);
+        assertTrue(mHitboxExtender.extendedHitboxEnabled());
+        // Center width, past height but w/in offset bounds
+        MotionEvent motionEvent = getMotionEvent(ACTION_DOWN,
+                BUTTON_WIDTH / 2, BUTTON_HEIGHT + TASKBAR_OFFSET_Y / 2);
+        assertTrue(mHitboxExtender.onControllerInterceptTouchEvent(motionEvent));
+    }
+
+    @Test
+    public void motionOutsideHitbox() {
+        mHitboxExtender.onRecentsButtonClicked();
+        mHitboxExtender.onAnimationProgressToOverview(0);
+        mHitboxExtender.onAnimationProgressToOverview(0.5f);
+        assertTrue(mHitboxExtender.extendedHitboxEnabled());
+        // Center width, past height and offset
+        MotionEvent motionEvent = getMotionEvent(ACTION_DOWN,
+                BUTTON_WIDTH / 2, BUTTON_HEIGHT + TASKBAR_OFFSET_Y * 2);
+        assertFalse(mHitboxExtender.onControllerInterceptTouchEvent(motionEvent));
+    }
+
+    private MotionEvent getMotionEvent(int action, int x, int y) {
+        return MotionEvent.obtain(0, 0, action, x, y, 0);
+    }
+}
diff --git a/src/com/android/launcher3/LauncherAppState.java b/src/com/android/launcher3/LauncherAppState.java
index 4501159..597bc8d 100644
--- a/src/com/android/launcher3/LauncherAppState.java
+++ b/src/com/android/launcher3/LauncherAppState.java
@@ -16,6 +16,8 @@
 
 package com.android.launcher3;
 
+import static android.app.admin.DevicePolicyManager.ACTION_DEVICE_POLICY_RESOURCE_UPDATED;
+
 import static com.android.launcher3.Utilities.getDevicePrefs;
 import static com.android.launcher3.config.FeatureFlags.ENABLE_THEMED_ICONS;
 import static com.android.launcher3.util.Executors.MODEL_EXECUTOR;
@@ -97,7 +99,8 @@
         modelChangeReceiver.register(mContext, Intent.ACTION_LOCALE_CHANGED,
                 Intent.ACTION_MANAGED_PROFILE_AVAILABLE,
                 Intent.ACTION_MANAGED_PROFILE_UNAVAILABLE,
-                Intent.ACTION_MANAGED_PROFILE_UNLOCKED);
+                Intent.ACTION_MANAGED_PROFILE_UNLOCKED,
+                ACTION_DEVICE_POLICY_RESOURCE_UPDATED);
         if (FeatureFlags.IS_STUDIO_BUILD) {
             modelChangeReceiver.register(mContext, Context.RECEIVER_EXPORTED, ACTION_FORCE_ROLOAD);
         }
diff --git a/src/com/android/launcher3/LauncherModel.java b/src/com/android/launcher3/LauncherModel.java
index ee6f51e..5c5c101 100644
--- a/src/com/android/launcher3/LauncherModel.java
+++ b/src/com/android/launcher3/LauncherModel.java
@@ -16,6 +16,8 @@
 
 package com.android.launcher3;
 
+import static android.app.admin.DevicePolicyManager.ACTION_DEVICE_POLICY_RESOURCE_UPDATED;
+
 import static com.android.launcher3.LauncherAppState.ACTION_FORCE_ROLOAD;
 import static com.android.launcher3.config.FeatureFlags.IS_STUDIO_BUILD;
 import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
@@ -51,6 +53,7 @@
 import com.android.launcher3.model.PackageIncrementalDownloadUpdatedTask;
 import com.android.launcher3.model.PackageInstallStateChangedTask;
 import com.android.launcher3.model.PackageUpdatedTask;
+import com.android.launcher3.model.ReloadStringCacheTask;
 import com.android.launcher3.model.ShortcutsChangedTask;
 import com.android.launcher3.model.UserLockStateChangedTask;
 import com.android.launcher3.model.data.AppInfo;
@@ -278,6 +281,8 @@
                             user, Intent.ACTION_MANAGED_PROFILE_UNLOCKED.equals(action)));
                 }
             }
+        } else if (ACTION_DEVICE_POLICY_RESOURCE_UPDATED.equals(action)) {
+            enqueueModelUpdateTask(new ReloadStringCacheTask(mModelDelegate));
         } else if (IS_STUDIO_BUILD && ACTION_FORCE_ROLOAD.equals(action)) {
             for (Callbacks cb : getCallbacks()) {
                 if (cb instanceof Launcher) {
diff --git a/src/com/android/launcher3/allapps/BaseAllAppsContainerView.java b/src/com/android/launcher3/allapps/BaseAllAppsContainerView.java
index 5bbc67e..72ca5a7 100644
--- a/src/com/android/launcher3/allapps/BaseAllAppsContainerView.java
+++ b/src/com/android/launcher3/allapps/BaseAllAppsContainerView.java
@@ -437,8 +437,12 @@
         if (showTabs == mUsingTabs && !force) {
             return;
         }
+
+        // replaceRVcontainer() needs to use both mUsingTabs value to remove the old view AND
+        // showTabs value to create new view. Hence the mUsingTabs new value assignment MUST happen
+        // after this call.
+        replaceRVContainer(showTabs);
         mUsingTabs = showTabs;
-        replaceRVContainer(mUsingTabs);
 
         mAllAppsStore.unregisterIconContainer(mAH.get(AdapterHolder.MAIN).mRecyclerView);
         mAllAppsStore.unregisterIconContainer(mAH.get(AdapterHolder.WORK).mRecyclerView);
diff --git a/src/com/android/launcher3/folder/FolderNameProvider.java b/src/com/android/launcher3/folder/FolderNameProvider.java
index 2b621bd..5021644 100644
--- a/src/com/android/launcher3/folder/FolderNameProvider.java
+++ b/src/com/android/launcher3/folder/FolderNameProvider.java
@@ -15,6 +15,8 @@
  */
 package com.android.launcher3.folder;
 
+import android.annotation.SuppressLint;
+import android.app.admin.DevicePolicyManager;
 import android.content.ComponentName;
 import android.content.Context;
 import android.os.Process;
@@ -22,11 +24,15 @@
 import android.text.TextUtils;
 import android.util.Log;
 
+import androidx.annotation.WorkerThread;
+
 import com.android.launcher3.LauncherAppState;
 import com.android.launcher3.R;
+import com.android.launcher3.Utilities;
 import com.android.launcher3.model.AllAppsList;
 import com.android.launcher3.model.BaseModelUpdateTask;
 import com.android.launcher3.model.BgDataModel;
+import com.android.launcher3.model.StringCache;
 import com.android.launcher3.model.data.AppInfo;
 import com.android.launcher3.model.data.FolderInfo;
 import com.android.launcher3.model.data.WorkspaceItemInfo;
@@ -94,6 +100,7 @@
     /**
      * Generate and rank the suggested Folder names.
      */
+    @WorkerThread
     public void getSuggestedFolderName(Context context,
             ArrayList<WorkspaceItemInfo> workspaceItemInfos,
             FolderNameInfos nameInfos) {
@@ -107,8 +114,7 @@
         Set<UserHandle> users = workspaceItemInfos.stream().map(w -> w.user)
                 .collect(Collectors.toSet());
         if (users.size() == 1 && !users.contains(Process.myUserHandle())) {
-            String workFolderName = context.getString(R.string.work_folder_name);
-            setAsLastSuggestion(nameInfos, workFolderName);
+            setAsLastSuggestion(nameInfos, getWorkFolderName(context));
         }
 
         // If all the icons are from same package (e.g., main icon, shortcut, shortcut)
@@ -130,6 +136,17 @@
         }
     }
 
+    @WorkerThread
+    @SuppressLint("NewApi")
+    private String getWorkFolderName(Context context) {
+        if (!Utilities.ATLEAST_T) {
+            return context.getString(R.string.work_folder_name);
+        }
+        return context.getSystemService(DevicePolicyManager.class).getResources()
+                .getString(StringCache.WORK_FOLDER_NAME, () ->
+                        context.getString(R.string.work_folder_name));
+    }
+
     private Optional<AppInfo> getAppInfoByPackageName(String packageName) {
         if (mAppInfos == null || mAppInfos.isEmpty()) {
             return Optional.empty();
diff --git a/src/com/android/launcher3/model/GridSizeMigrationTaskV2.java b/src/com/android/launcher3/model/GridSizeMigrationTaskV2.java
index e36d4cf..ef9250c 100644
--- a/src/com/android/launcher3/model/GridSizeMigrationTaskV2.java
+++ b/src/com/android/launcher3/model/GridSizeMigrationTaskV2.java
@@ -216,14 +216,15 @@
             return false;
         }
 
+        // Sort the items by the reading order.
+        Collections.sort(mHotseatDiff);
+        Collections.sort(mWorkspaceDiff);
+
         // Migrate hotseat
         HotseatPlacementSolution hotseatSolution = new HotseatPlacementSolution(mDb, mSrcReader,
                 mDestReader, mContext, mDestHotseatSize, mHotseatItems, mHotseatDiff);
         hotseatSolution.find();
 
-        // Sort the items by the reading order.
-        Collections.sort(mWorkspaceDiff);
-
         // Migrate workspace.
         // First we create a collection of the screens
         List<Integer> screens = new ArrayList<>();
diff --git a/src/com/android/launcher3/model/ReloadStringCacheTask.java b/src/com/android/launcher3/model/ReloadStringCacheTask.java
new file mode 100644
index 0000000..f4d4298
--- /dev/null
+++ b/src/com/android/launcher3/model/ReloadStringCacheTask.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2016 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.model;
+
+import com.android.launcher3.LauncherAppState;
+
+/**
+ * Handles updates due to changes in Device Policy Management resources triggered by
+ * {@link android.app.admin.DevicePolicyManager#ACTION_DEVICE_POLICY_RESOURCE_UPDATED}.
+ */
+public class ReloadStringCacheTask extends BaseModelUpdateTask {
+    private ModelDelegate mModelDelegate;
+
+    public ReloadStringCacheTask(ModelDelegate modelDelegate) {
+        mModelDelegate = modelDelegate;
+    }
+
+    @Override
+    public void execute(LauncherAppState app, BgDataModel dataModel, AllAppsList appsList) {
+        synchronized (dataModel) {
+            mModelDelegate.loadStringCache(dataModel.stringCache);
+            StringCache cloneSC = dataModel.stringCache.clone();
+            scheduleCallbackTask(c -> c.bindStringCache(cloneSC));
+        }
+    }
+}
diff --git a/src/com/android/launcher3/model/StringCache.java b/src/com/android/launcher3/model/StringCache.java
index 663a463..9859ddc 100644
--- a/src/com/android/launcher3/model/StringCache.java
+++ b/src/com/android/launcher3/model/StringCache.java
@@ -35,6 +35,11 @@
     private static final String PREFIX = "Launcher.";
 
     /**
+     * Work folder name.
+     */
+    public static final String WORK_FOLDER_NAME = PREFIX + "WORK_FOLDER_NAME";
+
+    /**
      * User on-boarding title for work profile apps.
      */
     private static final String WORK_PROFILE_EDU = PREFIX + "WORK_PROFILE_EDU";
@@ -91,11 +96,6 @@
             PREFIX + "ALL_APPS_PERSONAL_TAB_ACCESSIBILITY";
 
     /**
-     * Work folder name.
-     */
-    private static final String WORK_FOLDER_NAME = PREFIX + "WORK_FOLDER_NAME";
-
-    /**
      * Label on widget tab to indicate work app widgets.
      */
     private static final String WIDGETS_WORK_TAB = PREFIX + "WIDGETS_WORK_TAB";