From c76b2035aac8c8aae0fba8f8c16e82c7da8acc7a Mon Sep 17 00:00:00 2001 From: kholoud mohamed Date: Mon, 20 Dec 2021 16:47:38 +0000 Subject: [PATCH] Allow overriding enterprise related strings in Launcher Test: manual Bug: 188414133 Bug: 211422509 Bug: 188410712 Change-Id: I75858cdcf2057e7c270da5893cd9a90c6753f182 --- .../model/QuickstepModelDelegate.java | 51 +++++++ src/com/android/launcher3/Launcher.java | 13 ++ .../allapps/BaseAllAppsContainerView.java | 39 +++-- .../allapps/WorkAdapterProvider.java | 44 +++++- .../launcher3/allapps/WorkModeSwitch.java | 10 +- .../launcher3/allapps/WorkProfileManager.java | 2 +- .../launcher3/folder/FolderNameProvider.java | 9 +- .../launcher3/model/BaseLoaderResults.java | 2 + .../android/launcher3/model/BgDataModel.java | 10 ++ .../android/launcher3/model/LoaderTask.java | 3 + .../launcher3/model/ModelDelegate.java | 11 ++ .../android/launcher3/model/StringCache.java | 139 ++++++++++++++++++ .../SecondaryDisplayLauncher.java | 13 ++ .../launcher3/views/ActivityContext.java | 6 + .../launcher3/views/OptionsPopupView.java | 5 +- .../widget/picker/WidgetsFullSheet.java | 12 ++ .../folder/FolderNameProviderTest.java | 3 +- 17 files changed, 353 insertions(+), 19 deletions(-) create mode 100644 src/com/android/launcher3/model/StringCache.java diff --git a/quickstep/src/com/android/launcher3/model/QuickstepModelDelegate.java b/quickstep/src/com/android/launcher3/model/QuickstepModelDelegate.java index 0e534f4ea9..8424564459 100644 --- a/quickstep/src/com/android/launcher3/model/QuickstepModelDelegate.java +++ b/quickstep/src/com/android/launcher3/model/QuickstepModelDelegate.java @@ -15,6 +15,20 @@ */ package com.android.launcher3.model; +import static android.app.admin.DevicePolicyResources.Strings.Launcher.ALL_APPS_PERSONAL_TAB; +import static android.app.admin.DevicePolicyResources.Strings.Launcher.ALL_APPS_PERSONAL_TAB_ACCESSIBILITY; +import static android.app.admin.DevicePolicyResources.Strings.Launcher.ALL_APPS_WORK_TAB; +import static android.app.admin.DevicePolicyResources.Strings.Launcher.ALL_APPS_WORK_TAB_ACCESSIBILITY; +import static android.app.admin.DevicePolicyResources.Strings.Launcher.DISABLED_BY_ADMIN_MESSAGE; +import static android.app.admin.DevicePolicyResources.Strings.Launcher.WIDGETS_PERSONAL_TAB; +import static android.app.admin.DevicePolicyResources.Strings.Launcher.WIDGETS_WORK_TAB; +import static android.app.admin.DevicePolicyResources.Strings.Launcher.WORK_FOLDER_NAME; +import static android.app.admin.DevicePolicyResources.Strings.Launcher.WORK_PROFILE_EDU; +import static android.app.admin.DevicePolicyResources.Strings.Launcher.WORK_PROFILE_EDU_ACCEPT; +import static android.app.admin.DevicePolicyResources.Strings.Launcher.WORK_PROFILE_ENABLE_BUTTON; +import static android.app.admin.DevicePolicyResources.Strings.Launcher.WORK_PROFILE_PAUSED_DESCRIPTION; +import static android.app.admin.DevicePolicyResources.Strings.Launcher.WORK_PROFILE_PAUSED_TITLE; +import static android.app.admin.DevicePolicyResources.Strings.Launcher.WORK_PROFILE_PAUSE_BUTTON; import static android.text.format.DateUtils.DAY_IN_MILLIS; import static android.text.format.DateUtils.formatElapsedTime; @@ -32,6 +46,7 @@ import static com.android.launcher3.util.Executors.MODEL_EXECUTOR; import static java.util.stream.Collectors.toCollection; import android.app.StatsManager; +import android.app.admin.DevicePolicyManager; import android.app.prediction.AppPredictionContext; import android.app.prediction.AppPredictionManager; import android.app.prediction.AppPredictor; @@ -135,6 +150,42 @@ public class QuickstepModelDelegate extends ModelDelegate { mActive = true; } + @Override + @WorkerThread + public void loadStringCache(StringCache cache) { + cache.loadDefaultStrings(mContext); + + cache.workProfileEdu = getEnterpriseString(WORK_PROFILE_EDU, cache.workProfileEdu); + cache.workProfileEduAccept = getEnterpriseString( + WORK_PROFILE_EDU_ACCEPT, cache.workProfileEduAccept); + cache.workProfilePausedTitle = getEnterpriseString( + WORK_PROFILE_PAUSED_TITLE, cache.workProfilePausedTitle); + cache.workProfilePausedDescription = getEnterpriseString( + WORK_PROFILE_PAUSED_DESCRIPTION, cache.workProfilePausedDescription); + cache.workProfilePauseButton = getEnterpriseString( + WORK_PROFILE_PAUSE_BUTTON, cache.workProfilePauseButton); + cache.workProfileEnableButton = getEnterpriseString( + WORK_PROFILE_ENABLE_BUTTON, cache.workProfileEnableButton); + cache.allAppsWorkTab = getEnterpriseString(ALL_APPS_WORK_TAB, cache.allAppsWorkTab); + cache.allAppsPersonalTab = getEnterpriseString( + ALL_APPS_PERSONAL_TAB, cache.allAppsPersonalTab); + cache.allAppsWorkTabAccessibility = getEnterpriseString( + ALL_APPS_WORK_TAB_ACCESSIBILITY, cache.allAppsWorkTabAccessibility); + cache.allAppsPersonalTabAccessibility = getEnterpriseString( + ALL_APPS_PERSONAL_TAB_ACCESSIBILITY, cache.allAppsPersonalTabAccessibility); + cache.workFolderName = getEnterpriseString(WORK_FOLDER_NAME, cache.workFolderName); + cache.widgetsWorkTab = getEnterpriseString(WIDGETS_WORK_TAB, cache.widgetsWorkTab); + cache.widgetsPersonalTab = getEnterpriseString( + WIDGETS_PERSONAL_TAB, cache.widgetsPersonalTab); + cache.disabledByAdminMessage = getEnterpriseString( + DISABLED_BY_ADMIN_MESSAGE, cache.disabledByAdminMessage); + } + + private String getEnterpriseString(String updatableStringId, String defaultString) { + DevicePolicyManager dpm = mContext.getSystemService(DevicePolicyManager.class); + return dpm.getString(updatableStringId, () -> defaultString); + } + @Override public void workspaceLoadComplete() { super.workspaceLoadComplete(); diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java index 84c4783060..59c40ccbe3 100644 --- a/src/com/android/launcher3/Launcher.java +++ b/src/com/android/launcher3/Launcher.java @@ -149,6 +149,7 @@ import com.android.launcher3.model.BgDataModel.Callbacks; import com.android.launcher3.model.ItemInstallQueue; import com.android.launcher3.model.ModelUtils; import com.android.launcher3.model.ModelWriter; +import com.android.launcher3.model.StringCache; import com.android.launcher3.model.WidgetsModel; import com.android.launcher3.model.data.AppInfo; import com.android.launcher3.model.data.FolderInfo; @@ -379,6 +380,8 @@ public class Launcher extends StatefulActivity implements Launche protected InstanceId mAllAppsSessionLogId; private LauncherState mPrevLauncherState; + private StringCache mStringCache; + @Override @TargetApi(Build.VERSION_CODES.S) protected void onCreate(Bundle savedInstanceState) { @@ -2872,6 +2875,16 @@ public class Launcher extends StatefulActivity implements Launche mPopupDataProvider.setAllWidgets(allWidgets); } + @Override + public void bindStringCache(StringCache cache) { + mStringCache = cache; + } + + @Override + public StringCache getStringCache() { + return mStringCache; + } + /** * @param packageUser if null, refreshes all widgets and shortcuts, otherwise only * refreshes the widgets and shortcuts associated with the given package/user diff --git a/src/com/android/launcher3/allapps/BaseAllAppsContainerView.java b/src/com/android/launcher3/allapps/BaseAllAppsContainerView.java index c1f0e068eb..1253863893 100644 --- a/src/com/android/launcher3/allapps/BaseAllAppsContainerView.java +++ b/src/com/android/launcher3/allapps/BaseAllAppsContainerView.java @@ -38,10 +38,10 @@ import android.view.LayoutInflater; import android.view.MotionEvent; import android.view.View; import android.view.WindowInsets; +import android.widget.Button; import androidx.annotation.NonNull; import androidx.annotation.Nullable; -import androidx.annotation.StringRes; import androidx.annotation.VisibleForTesting; import androidx.core.graphics.ColorUtils; import androidx.recyclerview.widget.LinearLayoutManager; @@ -57,6 +57,7 @@ import com.android.launcher3.R; import com.android.launcher3.Utilities; import com.android.launcher3.allapps.search.SearchAdapterProvider; import com.android.launcher3.keyboard.FocusedItemDecorator; +import com.android.launcher3.model.StringCache; import com.android.launcher3.model.data.AppInfo; import com.android.launcher3.util.ItemInfoMatcher; import com.android.launcher3.util.Themes; @@ -298,27 +299,34 @@ public abstract class BaseAllAppsContainerView users = workspaceItemInfos.stream().map(w -> w.user) .collect(Collectors.toSet()); if (users.size() == 1 && !users.contains(Process.myUserHandle())) { - setAsLastSuggestion(nameInfos, - context.getResources().getString(R.string.work_folder_name)); + StringCache cache = ActivityContext.lookupContext(context).getStringCache(); + String workFolderName = cache != null + ? cache.workFolderName : context.getString(R.string.work_folder_name); + setAsLastSuggestion(nameInfos, workFolderName); } // If all the icons are from same package (e.g., main icon, shortcut, shortcut) diff --git a/src/com/android/launcher3/model/BaseLoaderResults.java b/src/com/android/launcher3/model/BaseLoaderResults.java index d270cc56a1..5b278ab4cf 100644 --- a/src/com/android/launcher3/model/BaseLoaderResults.java +++ b/src/com/android/launcher3/model/BaseLoaderResults.java @@ -226,6 +226,8 @@ public abstract class BaseLoaderResults { MODEL_EXECUTOR.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND); c.onInitialBindComplete(currentScreenIds, pendingTasks); }, mUiExecutor); + + mCallbacks.bindStringCache(mBgDataModel.stringCache.clone()); } private void bindWorkspaceItems( diff --git a/src/com/android/launcher3/model/BgDataModel.java b/src/com/android/launcher3/model/BgDataModel.java index 84612def6c..866d18a86b 100644 --- a/src/com/android/launcher3/model/BgDataModel.java +++ b/src/com/android/launcher3/model/BgDataModel.java @@ -113,6 +113,11 @@ public class BgDataModel { */ public final WidgetsModel widgetsModel = new WidgetsModel(); + /** + * Cache for strings used in launcher + */ + public final StringCache stringCache = new StringCache(); + /** * Id when the model was last bound */ @@ -505,5 +510,10 @@ public class BgDataModel { default void bindExtraContainerItems(FixedContainerItems item) { } default void bindAllApplications(AppInfo[] apps, int flags) { } + + /** + * Binds the cache of string resources + */ + default void bindStringCache(StringCache cache) { } } } diff --git a/src/com/android/launcher3/model/LoaderTask.java b/src/com/android/launcher3/model/LoaderTask.java index 2a0f9a6f67..b9fa21d10d 100644 --- a/src/com/android/launcher3/model/LoaderTask.java +++ b/src/com/android/launcher3/model/LoaderTask.java @@ -857,6 +857,9 @@ public class LoaderTask implements Runnable { // Load delegate items mModelDelegate.loadItems(mUserManagerState, shortcutKeyToPinnedShortcuts); + // Load string cache + mModelDelegate.loadStringCache(mBgDataModel.stringCache); + // Break early if we've stopped loading if (mStopped) { mBgDataModel.clear(); diff --git a/src/com/android/launcher3/model/ModelDelegate.java b/src/com/android/launcher3/model/ModelDelegate.java index 765141a0e2..60ca63bba7 100644 --- a/src/com/android/launcher3/model/ModelDelegate.java +++ b/src/com/android/launcher3/model/ModelDelegate.java @@ -48,9 +48,11 @@ public class ModelDelegate implements ResourceBasedOverride { delegate.mAppsList = appsList; delegate.mDataModel = dataModel; delegate.mIsPrimaryInstance = isPrimaryInstance; + delegate.mContext = context; return delegate; } + protected Context mContext; protected LauncherAppState mApp; protected AllAppsList mAppsList; protected BgDataModel mDataModel; @@ -75,6 +77,15 @@ public class ModelDelegate implements ResourceBasedOverride { @WorkerThread public void loadItems(UserManagerState ums, Map pinnedShortcuts) { } + /** + * Load String cache + */ + @WorkerThread + public void loadStringCache(StringCache cache) { + cache.loadDefaultStrings(mContext); + } + + /** * Called during loader after workspace loading is complete */ diff --git a/src/com/android/launcher3/model/StringCache.java b/src/com/android/launcher3/model/StringCache.java new file mode 100644 index 0000000000..11d3e70f5f --- /dev/null +++ b/src/com/android/launcher3/model/StringCache.java @@ -0,0 +1,139 @@ +/* + * 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.model; + +import android.content.Context; + +import com.android.launcher3.R; + +/** + * + * Cache for some of the string used in Launcher. + */ +public class StringCache { + + /** + * User on-boarding title for work profile apps. + */ + public String workProfileEdu; + + /** + * Action label to finish work profile edu. + */ + public String workProfileEduAccept; + + /** + * Title shown when user opens work apps tab while work profile is paused. + */ + public String workProfilePausedTitle; + + /** + * Description shown when user opens work apps tab while work profile is paused. + */ + public String workProfilePausedDescription; + + /** + * Shown on the button to pause work profile. + */ + public String workProfilePauseButton; + + /** + * Shown on the button to enable work profile. + */ + public String workProfileEnableButton; + + /** + * Label on launcher tab to indicate work apps. + */ + public String allAppsWorkTab; + + /** + * Label on launcher tab to indicate personal apps. + */ + public String allAppsPersonalTab; + + /** + * Accessibility description for launcher tab to indicate work apps. + */ + public String allAppsWorkTabAccessibility; + + /** + * Accessibility description for launcher tab to indicate personal apps. + */ + public String allAppsPersonalTabAccessibility; + + /** + * Work folder name. + */ + public String workFolderName; + + /** + * Label on widget tab to indicate work app widgets. + */ + public String widgetsWorkTab; + + /** + * Label on widget tab to indicate personal app widgets. + */ + public String widgetsPersonalTab; + + /** + * Message shown when a feature is disabled by the admin (e.g. changing wallpaper). + */ + public String disabledByAdminMessage; + + /** + * Sets the default values for the strings. + */ + public void loadDefaultStrings(Context context) { + workProfileEdu = context.getString(R.string.work_profile_edu_work_apps); + workProfileEduAccept = context.getString(R.string.work_profile_edu_accept); + workProfilePausedTitle = context.getString(R.string.work_apps_paused_title); + workProfilePausedDescription = context.getString(R.string.work_apps_paused_body); + workProfilePauseButton = context.getString(R.string.work_apps_pause_btn_text); + workProfileEnableButton = context.getString(R.string.work_apps_enable_btn_text); + allAppsWorkTab = context.getString(R.string.all_apps_work_tab); + allAppsPersonalTab = context.getString(R.string.all_apps_personal_tab); + allAppsWorkTabAccessibility = context.getString(R.string.all_apps_button_work_label); + allAppsPersonalTabAccessibility = context.getString( + R.string.all_apps_button_personal_label); + workFolderName = context.getString(R.string.work_folder_name); + widgetsWorkTab = context.getString(R.string.widgets_full_sheet_work_tab); + widgetsPersonalTab = context.getString(R.string.widgets_full_sheet_personal_tab); + disabledByAdminMessage = context.getString(R.string.msg_disabled_by_admin); + } + + @Override + public StringCache clone() { + StringCache clone = new StringCache(); + clone.workProfileEdu = this.workProfileEdu; + clone.workProfileEduAccept = this.workProfileEduAccept; + clone.workProfilePausedTitle = this.workProfilePausedTitle; + clone.workProfilePausedDescription = this.workProfilePausedDescription; + clone.workProfilePauseButton = this.workProfilePauseButton; + clone.workProfileEnableButton = this.workProfileEnableButton; + clone.allAppsWorkTab = this.allAppsWorkTab; + clone.allAppsPersonalTab = this.allAppsPersonalTab; + clone.allAppsWorkTabAccessibility = this.allAppsWorkTabAccessibility; + clone.allAppsPersonalTabAccessibility = this.allAppsPersonalTabAccessibility; + clone.workFolderName = this.workFolderName; + clone.widgetsWorkTab = this.widgetsWorkTab; + clone.widgetsPersonalTab = this.widgetsPersonalTab; + clone.disabledByAdminMessage = this.disabledByAdminMessage; + return clone; + } +} diff --git a/src/com/android/launcher3/secondarydisplay/SecondaryDisplayLauncher.java b/src/com/android/launcher3/secondarydisplay/SecondaryDisplayLauncher.java index cba0655b24..73aa296001 100644 --- a/src/com/android/launcher3/secondarydisplay/SecondaryDisplayLauncher.java +++ b/src/com/android/launcher3/secondarydisplay/SecondaryDisplayLauncher.java @@ -32,6 +32,7 @@ import com.android.launcher3.LauncherModel; import com.android.launcher3.R; import com.android.launcher3.allapps.ActivityAllAppsContainerView; 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.ItemInfo; import com.android.launcher3.model.data.ItemInfoWithIcon; @@ -60,6 +61,8 @@ public class SecondaryDisplayLauncher extends BaseDraggingActivity private boolean mAppDrawerShown = false; + private StringCache mStringCache; + @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); @@ -227,6 +230,16 @@ public class SecondaryDisplayLauncher extends BaseDraggingActivity PopupContainerWithArrow.dismissInvalidPopup(this); } + @Override + public StringCache getStringCache() { + return mStringCache; + } + + @Override + public void bindStringCache(StringCache cache) { + mStringCache = cache; + } + public PopupDataProvider getPopupDataProvider() { return mPopupDataProvider; } diff --git a/src/com/android/launcher3/views/ActivityContext.java b/src/com/android/launcher3/views/ActivityContext.java index 8b48baeab7..a318363af3 100644 --- a/src/com/android/launcher3/views/ActivityContext.java +++ b/src/com/android/launcher3/views/ActivityContext.java @@ -31,6 +31,7 @@ import com.android.launcher3.dragndrop.DragController; import com.android.launcher3.folder.FolderIcon; import com.android.launcher3.logger.LauncherAtom; import com.android.launcher3.logging.StatsLogManager; +import com.android.launcher3.model.StringCache; import com.android.launcher3.model.data.ItemInfo; import com.android.launcher3.popup.PopupDataProvider; import com.android.launcher3.util.ViewCache; @@ -173,4 +174,9 @@ public interface ActivityContext { default PopupDataProvider getPopupDataProvider() { return null; } + + @Nullable + default StringCache getStringCache() { + return null; + } } diff --git a/src/com/android/launcher3/views/OptionsPopupView.java b/src/com/android/launcher3/views/OptionsPopupView.java index fc8b4b7edb..95bce31796 100644 --- a/src/com/android/launcher3/views/OptionsPopupView.java +++ b/src/com/android/launcher3/views/OptionsPopupView.java @@ -240,7 +240,10 @@ public class OptionsPopupView extends ArrowPopup private static boolean startWallpaperPicker(View v) { Launcher launcher = Launcher.getLauncher(v.getContext()); if (!Utilities.isWallpaperAllowed(launcher)) { - Toast.makeText(launcher, R.string.msg_disabled_by_admin, Toast.LENGTH_SHORT).show(); + String message = launcher.getStringCache() != null + ? launcher.getStringCache().disabledByAdminMessage + : launcher.getString(R.string.msg_disabled_by_admin); + Toast.makeText(launcher, message, Toast.LENGTH_SHORT).show(); return false; } Intent intent = new Intent(Intent.ACTION_SET_WALLPAPER) diff --git a/src/com/android/launcher3/widget/picker/WidgetsFullSheet.java b/src/com/android/launcher3/widget/picker/WidgetsFullSheet.java index 894c4c9cd3..daa8fb7889 100644 --- a/src/com/android/launcher3/widget/picker/WidgetsFullSheet.java +++ b/src/com/android/launcher3/widget/picker/WidgetsFullSheet.java @@ -41,6 +41,7 @@ import android.view.ViewGroup; import android.view.WindowInsets; import android.view.animation.AnimationUtils; import android.view.animation.Interpolator; +import android.widget.Button; import android.widget.TextView; import androidx.annotation.Nullable; @@ -199,6 +200,7 @@ public class WidgetsFullSheet extends BaseWidgetSheet findViewById(R.id.tab_work) .setOnClickListener((View view) -> mViewPager.snapToPage(1)); mAdapters.get(AdapterHolder.WORK).setup(findViewById(R.id.work_widgets_list_view)); + setDeviceManagementResources(); } else { mViewPager = null; } @@ -220,6 +222,16 @@ public class WidgetsFullSheet extends BaseWidgetSheet setUpEducationViewsIfNeeded(); } + private void setDeviceManagementResources() { + if (mActivityContext.getStringCache() != null) { + Button personalTab = findViewById(R.id.tab_personal); + personalTab.setText(mActivityContext.getStringCache().widgetsPersonalTab); + + Button workTab = findViewById(R.id.tab_work); + workTab.setText(mActivityContext.getStringCache().widgetsWorkTab); + } + } + @Override public void onActivePageChanged(int currentActivePage) { AdapterHolder currentAdapterHolder = mAdapters.get(currentActivePage); diff --git a/tests/src/com/android/launcher3/folder/FolderNameProviderTest.java b/tests/src/com/android/launcher3/folder/FolderNameProviderTest.java index 23e62356c4..9c15309f82 100644 --- a/tests/src/com/android/launcher3/folder/FolderNameProviderTest.java +++ b/tests/src/com/android/launcher3/folder/FolderNameProviderTest.java @@ -30,6 +30,7 @@ import androidx.test.filters.SmallTest; import com.android.launcher3.model.data.AppInfo; import com.android.launcher3.model.data.WorkspaceItemInfo; +import com.android.launcher3.util.ActivityContextWrapper; import com.android.launcher3.util.Executors; import org.junit.Before; @@ -47,7 +48,7 @@ public final class FolderNameProviderTest { @Before public void setUp() { - mContext = getApplicationContext(); + mContext = new ActivityContextWrapper(getApplicationContext()); mItem1 = new WorkspaceItemInfo(new AppInfo( new ComponentName("a.b.c", "a.b.c/a.b.c.d"), "title1",