diff options
6 files changed, 149 insertions, 19 deletions
diff --git a/AconfigFlags.bp b/AconfigFlags.bp index a80194cf53d2..8fa870b7af62 100644 --- a/AconfigFlags.bp +++ b/AconfigFlags.bp @@ -333,6 +333,11 @@ java_aconfig_library { aconfig_declarations: "android.os.flags-aconfig", defaults: ["framework-minus-apex-aconfig-java-defaults"], mode: "exported", + min_sdk_version: "30", + apex_available: [ + "//apex_available:platform", + "com.android.mediaprovider", + ], } cc_aconfig_library { diff --git a/packages/SettingsLib/ProfileSelector/Android.bp b/packages/SettingsLib/ProfileSelector/Android.bp index 6dc07b29a510..4aa67c17ad98 100644 --- a/packages/SettingsLib/ProfileSelector/Android.bp +++ b/packages/SettingsLib/ProfileSelector/Android.bp @@ -20,6 +20,7 @@ android_library { static_libs: [ "com.google.android.material_material", "SettingsLibSettingsTheme", + "android.os.flags-aconfig-java-export", ], sdk_version: "system_current", diff --git a/packages/SettingsLib/ProfileSelector/AndroidManifest.xml b/packages/SettingsLib/ProfileSelector/AndroidManifest.xml index 80f6b7683269..303e20c2497e 100644 --- a/packages/SettingsLib/ProfileSelector/AndroidManifest.xml +++ b/packages/SettingsLib/ProfileSelector/AndroidManifest.xml @@ -18,5 +18,5 @@ <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.android.settingslib.widget.profileselector"> - <uses-sdk android:minSdkVersion="23" /> + <uses-sdk android:minSdkVersion="29" /> </manifest> diff --git a/packages/SettingsLib/ProfileSelector/res/values/strings.xml b/packages/SettingsLib/ProfileSelector/res/values/strings.xml index 68d4047a497c..76ccb651969b 100644 --- a/packages/SettingsLib/ProfileSelector/res/values/strings.xml +++ b/packages/SettingsLib/ProfileSelector/res/values/strings.xml @@ -21,4 +21,6 @@ <string name="settingslib_category_personal">Personal</string> <!-- Header for items under the work user [CHAR LIMIT=30] --> <string name="settingslib_category_work">Work</string> + <!-- Header for items under the private profile user [CHAR LIMIT=30] --> + <string name="settingslib_category_private">Private</string> </resources>
\ No newline at end of file diff --git a/packages/SettingsLib/ProfileSelector/src/com/android/settingslib/widget/ProfileSelectFragment.java b/packages/SettingsLib/ProfileSelector/src/com/android/settingslib/widget/ProfileSelectFragment.java index be5753beea4e..c52386bef07b 100644 --- a/packages/SettingsLib/ProfileSelector/src/com/android/settingslib/widget/ProfileSelectFragment.java +++ b/packages/SettingsLib/ProfileSelector/src/com/android/settingslib/widget/ProfileSelectFragment.java @@ -16,31 +16,77 @@ package com.android.settingslib.widget; +import android.annotation.TargetApi; import android.app.Activity; +import android.content.Context; +import android.content.pm.UserProperties; +import android.os.Build; import android.os.Bundle; +import android.os.UserHandle; +import android.os.UserManager; +import android.util.ArrayMap; +import android.util.Log; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; +import androidx.core.os.BuildCompat; import androidx.fragment.app.Fragment; import androidx.viewpager2.widget.ViewPager2; +import com.android.settingslib.widget.profileselector.R; + import com.google.android.material.tabs.TabLayout; import com.google.android.material.tabs.TabLayoutMediator; -import com.android.settingslib.widget.profileselector.R; + +import java.util.ArrayList; +import java.util.List; /** * Base fragment class for profile settings. */ public abstract class ProfileSelectFragment extends Fragment { + private static final String TAG = "ProfileSelectFragment"; + // UserHandle#USER_NULL is a @TestApi so is not accessible. + private static final int USER_NULL = -10000; + private static final int DEFAULT_POSITION = 0; + + /** + * The type of profile tab of {@link ProfileSelectFragment} to show + * <ul> + * <li>0: Personal tab. + * <li>1: Work profile tab. + * </ul> + * + * <p> Please note that this is supported for legacy reasons. Please use + * {@link #EXTRA_SHOW_FRAGMENT_USER_ID} instead. + */ + public static final String EXTRA_SHOW_FRAGMENT_TAB = ":settings:show_fragment_tab"; + + /** + * An {@link ArrayList} of users to show. The supported users are: System user, the managed + * profile user, and the private profile user. A client should pass all the user ids that need + * to be shown in this list. Note that if this list is not provided then, for legacy reasons + * see {@link #EXTRA_SHOW_FRAGMENT_TAB}, an attempt will be made to show two tabs: one for the + * System user and one for the managed profile user. + * + * <p>Please note that this MUST be used in conjunction with + * {@link #EXTRA_SHOW_FRAGMENT_USER_ID} + */ + public static final String EXTRA_LIST_OF_USER_IDS = ":settings:list_user_ids"; /** - * Personal or Work profile tab of {@link ProfileSelectFragment} - * <p>0: Personal tab. - * <p>1: Work profile tab. + * The user id of the user to be show in {@link ProfileSelectFragment}. Only the below user + * types are supported: + * <ul> + * <li> System user. + * <li> Managed profile user. + * <li> Private profile user. + * </ul> + * + * <p>Please note that this MUST be used in conjunction with {@link #EXTRA_LIST_OF_USER_IDS}. */ - public static final String EXTRA_SHOW_FRAGMENT_TAB = - ":settings:show_fragment_tab"; + public static final String EXTRA_SHOW_FRAGMENT_USER_ID = ":settings:show_fragment_user_id"; /** * Used in fragment argument with Extra key EXTRA_SHOW_FRAGMENT_TAB @@ -48,13 +94,23 @@ public abstract class ProfileSelectFragment extends Fragment { public static final int PERSONAL_TAB = 0; /** - * Used in fragment argument with Extra key EXTRA_SHOW_FRAGMENT_TAB + * Used in fragment argument with Extra key EXTRA_SHOW_FRAGMENT_TAB for the managed profile */ public static final int WORK_TAB = 1; + /** + * Please note that private profile is available from API LEVEL + * {@link Build.VERSION_CODES.VANILLA_ICE_CREAM} only, therefore PRIVATE_TAB MUST be + * passed in {@link #EXTRA_SHOW_FRAGMENT_TAB} and {@link #EXTRA_LIST_OF_PROFILE_TABS} for + * {@link Build.VERSION_CODES.VANILLA_ICE_CREAM} or higher API Levels only. + */ + private static final int PRIVATE_TAB = 2; + private ViewGroup mContentView; private ViewPager2 mViewPager; + private final ArrayMap<UserHandle, Integer> mProfileTabsByUsers = new ArrayMap<>(); + private boolean mUsingUserIds = false; @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, @@ -67,7 +123,7 @@ public abstract class ProfileSelectFragment extends Fragment { if (titleResId > 0) { activity.setTitle(titleResId); } - final int selectedTab = getTabId(activity, getArguments()); + initProfileTabsToShow(); final View tabContainer = mContentView.findViewById(R.id.tab_container); mViewPager = tabContainer.findViewById(R.id.view_pager); @@ -78,16 +134,14 @@ public abstract class ProfileSelectFragment extends Fragment { ).attach(); tabContainer.setVisibility(View.VISIBLE); - final TabLayout.Tab tab = tabs.getTabAt(selectedTab); + final TabLayout.Tab tab = tabs.getTabAt(getSelectedTabPosition(activity, getArguments())); tab.select(); return mContentView; } /** - * create Personal or Work profile fragment - * <p>0: Personal profile. - * <p>1: Work profile. + * Create Personal or Work or Private profile fragment. See {@link #EXTRA_SHOW_FRAGMENT_USER_ID} */ public abstract Fragment createFragment(int position); @@ -99,21 +153,90 @@ public abstract class ProfileSelectFragment extends Fragment { return 0; } - int getTabId(Activity activity, Bundle bundle) { + int getSelectedTabPosition(Activity activity, Bundle bundle) { if (bundle != null) { + final int extraUserId = bundle.getInt(EXTRA_SHOW_FRAGMENT_USER_ID, USER_NULL); + if (extraUserId != USER_NULL) { + return mProfileTabsByUsers.indexOfKey(UserHandle.of(extraUserId)); + } final int extraTab = bundle.getInt(EXTRA_SHOW_FRAGMENT_TAB, -1); if (extraTab != -1) { return extraTab; } } - return PERSONAL_TAB; + return DEFAULT_POSITION; + } + + int getTabCount() { + return mUsingUserIds ? mProfileTabsByUsers.size() : 2; + } + + void initProfileTabsToShow() { + Bundle bundle = getArguments(); + if (bundle != null) { + ArrayList<Integer> userIdsToShow = + bundle.getIntegerArrayList(EXTRA_LIST_OF_USER_IDS); + if (userIdsToShow != null && !userIdsToShow.isEmpty()) { + mUsingUserIds = true; + UserManager userManager = getContext().getSystemService(UserManager.class); + List<UserHandle> userHandles = userManager.getUserProfiles(); + for (UserHandle userHandle : userHandles) { + if (!userIdsToShow.contains(userHandle.getIdentifier())) { + continue; + } + if (userHandle.isSystem()) { + mProfileTabsByUsers.put(userHandle, PERSONAL_TAB); + } else if (userManager.isManagedProfile(userHandle.getIdentifier())) { + mProfileTabsByUsers.put(userHandle, WORK_TAB); + } else if (shouldShowPrivateProfileIfItsOne(userHandle)) { + mProfileTabsByUsers.put(userHandle, PRIVATE_TAB); + } + } + } + } + } + + private int getProfileTabForPosition(int position) { + return mUsingUserIds ? mProfileTabsByUsers.valueAt(position) : position; + } + + int getUserIdForPosition(int position) { + return mUsingUserIds ? mProfileTabsByUsers.keyAt(position).getIdentifier() : position; } private CharSequence getPageTitle(int position) { - if (position == WORK_TAB) { + int tab = getProfileTabForPosition(position); + if (tab == WORK_TAB) { return getContext().getString(R.string.settingslib_category_work); + } else if (tab == PRIVATE_TAB) { + return getContext().getString(R.string.settingslib_category_private); } return getString(R.string.settingslib_category_personal); } + + @TargetApi(Build.VERSION_CODES.VANILLA_ICE_CREAM) + private boolean shouldShowUserInQuietMode(UserHandle userHandle, UserManager userManager) { + UserProperties userProperties = userManager.getUserProperties(userHandle); + return !userManager.isQuietModeEnabled(userHandle) + || userProperties.getShowInQuietMode() != UserProperties.SHOW_IN_QUIET_MODE_HIDDEN; + } + + // It's sufficient to have this method marked with the appropriate API level because we expect + // to be here only for this API level - when then private profile was introduced. + @TargetApi(Build.VERSION_CODES.VANILLA_ICE_CREAM) + private boolean shouldShowPrivateProfileIfItsOne(UserHandle userHandle) { + if (!BuildCompat.isAtLeastV() || !android.os.Flags.allowPrivateProfile()) { + return false; + } + try { + Context userContext = getContext().createContextAsUser(userHandle, /* flags= */ 0); + UserManager userManager = userContext.getSystemService(UserManager.class); + return userManager.isPrivateProfile() + && shouldShowUserInQuietMode(userHandle, userManager); + } catch (IllegalStateException exception) { + Log.i(TAG, "Ignoring this user as the calling package not available in this user."); + } + return false; + } } diff --git a/packages/SettingsLib/ProfileSelector/src/com/android/settingslib/widget/ProfileViewPagerAdapter.java b/packages/SettingsLib/ProfileSelector/src/com/android/settingslib/widget/ProfileViewPagerAdapter.java index f5ab64742992..37f4f275cfe7 100644 --- a/packages/SettingsLib/ProfileSelector/src/com/android/settingslib/widget/ProfileViewPagerAdapter.java +++ b/packages/SettingsLib/ProfileSelector/src/com/android/settingslib/widget/ProfileViewPagerAdapter.java @@ -18,7 +18,6 @@ package com.android.settingslib.widget; import androidx.fragment.app.Fragment; import androidx.viewpager2.adapter.FragmentStateAdapter; -import com.android.settingslib.widget.profileselector.R; /** * ViewPager Adapter to handle between TabLayout and ViewPager2 @@ -34,11 +33,11 @@ public class ProfileViewPagerAdapter extends FragmentStateAdapter { @Override public Fragment createFragment(int position) { - return mParentFragments.createFragment(position); + return mParentFragments.createFragment(mParentFragments.getUserIdForPosition(position)); } @Override public int getItemCount() { - return 2; + return mParentFragments.getTabCount(); } } |