1 /* 2 * Copyright (C) 2019 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.settings.dashboard.profileselector; 18 19 import static android.content.Intent.EXTRA_USER_ID; 20 21 import android.annotation.IntDef; 22 import android.app.Activity; 23 import android.content.Context; 24 import android.content.res.ColorStateList; 25 import android.os.Bundle; 26 import android.os.UserHandle; 27 import android.os.UserManager; 28 import android.text.TextUtils; 29 import android.view.LayoutInflater; 30 import android.view.View; 31 import android.view.ViewGroup; 32 import android.widget.FrameLayout; 33 import android.widget.LinearLayout; 34 35 import androidx.annotation.VisibleForTesting; 36 import androidx.fragment.app.Fragment; 37 import androidx.fragment.app.FragmentStatePagerAdapter; 38 import androidx.recyclerview.widget.RecyclerView; 39 import androidx.viewpager.widget.ViewPager; 40 41 import com.android.settings.R; 42 import com.android.settings.SettingsActivity; 43 import com.android.settings.Utils; 44 import com.android.settings.dashboard.DashboardFragment; 45 46 import com.google.android.material.tabs.TabLayout; 47 48 import java.lang.annotation.Retention; 49 import java.lang.annotation.RetentionPolicy; 50 import java.util.Locale; 51 52 /** 53 * Base fragment class for profile settings. 54 */ 55 public abstract class ProfileSelectFragment extends DashboardFragment { 56 57 private static final String TAG = "ProfileSelectFragment"; 58 59 /** 60 * Denotes the profile type. 61 */ 62 @Retention(RetentionPolicy.SOURCE) 63 @IntDef({ 64 ProfileType.PERSONAL, 65 ProfileType.WORK, 66 ProfileType.ALL 67 }) 68 public @interface ProfileType { 69 /** 70 * It is personal work profile. 71 */ 72 int PERSONAL = 1; 73 74 /** 75 * It is work profile 76 */ 77 int WORK = 1 << 1; 78 79 /** 80 * It is personal and work profile 81 */ 82 int ALL = PERSONAL | WORK; 83 } 84 85 /** 86 * Used in fragment argument and pass {@link ProfileType} to it 87 */ 88 public static final String EXTRA_PROFILE = "profile"; 89 90 /** 91 * Used in fragment argument with Extra key {@link SettingsActivity.EXTRA_SHOW_FRAGMENT_TAB} 92 */ 93 public static final int PERSONAL_TAB = 0; 94 95 /** 96 * Used in fragment argument with Extra key {@link SettingsActivity.EXTRA_SHOW_FRAGMENT_TAB} 97 */ 98 public static final int WORK_TAB = 1; 99 private static final int[] LABEL = { 100 R.string.category_personal, R.string.category_work 101 }; 102 103 private ViewGroup mContentView; 104 105 @Override onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)106 public View onCreateView(LayoutInflater inflater, ViewGroup container, 107 Bundle savedInstanceState) { 108 mContentView = (ViewGroup) super.onCreateView(inflater, container, savedInstanceState); 109 final Activity activity = getActivity(); 110 final int titleResId = getTitleResId(); 111 if (titleResId > 0) { 112 activity.setTitle(titleResId); 113 } 114 final int selectedTab = convertPosition(getTabId(activity, getArguments())); 115 116 final View tabContainer = mContentView.findViewById(R.id.tab_container); 117 final ViewPager viewPager = tabContainer.findViewById(R.id.view_pager); 118 viewPager.setAdapter(new ProfileSelectFragment.ViewPagerAdapter(this)); 119 final TabLayout tabs = tabContainer.findViewById(R.id.tabs); 120 tabs.setupWithViewPager(viewPager); 121 setupTabTextColor(tabs); 122 tabContainer.setVisibility(View.VISIBLE); 123 final TabLayout.Tab tab = tabs.getTabAt(selectedTab); 124 tab.select(); 125 126 final FrameLayout listContainer = mContentView.findViewById(android.R.id.list_container); 127 listContainer.setLayoutParams(new LinearLayout.LayoutParams( 128 ViewGroup.LayoutParams.MATCH_PARENT, 129 ViewGroup.LayoutParams.WRAP_CONTENT)); 130 131 final RecyclerView recyclerView = getListView(); 132 recyclerView.setOverScrollMode(View.OVER_SCROLL_NEVER); 133 Utils.setActionBarShadowAnimation(activity, getSettingsLifecycle(), recyclerView); 134 135 return mContentView; 136 } 137 138 /** 139 * TabLayout uses ColorStateList of 2 states, selected state and empty state. 140 * It's expected to use textColorSecondary default state color as empty state tabTextColor. 141 * 142 * However, TabLayout uses textColorSecondary by a not expected state. 143 * This method sets tabTextColor with the color of expected textColorSecondary state. 144 */ setupTabTextColor(TabLayout tabLayout)145 private void setupTabTextColor(TabLayout tabLayout) { 146 final ColorStateList defaultColorStateList = tabLayout.getTabTextColors(); 147 final ColorStateList resultColorStateList = new ColorStateList( 148 new int[][]{ 149 new int[]{android.R.attr.state_selected}, 150 new int[]{} 151 }, 152 new int[] { 153 defaultColorStateList.getColorForState(new int[]{android.R.attr.state_selected}, 154 Utils.getColorAttrDefaultColor(getContext(), 155 com.android.internal.R.attr.colorAccentPrimaryVariant)), 156 Utils.getColorAttrDefaultColor(getContext(), android.R.attr.textColorSecondary) 157 } 158 ); 159 tabLayout.setTabTextColors(resultColorStateList); 160 } 161 162 @Override getMetricsCategory()163 public int getMetricsCategory() { 164 return METRICS_CATEGORY_UNKNOWN; 165 } 166 167 /** 168 * Returns an array of {@link Fragment} to display in the 169 * {@link com.google.android.material.tabs.TabLayout} 170 */ getFragments()171 public abstract Fragment[] getFragments(); 172 173 /** 174 * Returns a resource ID of the title 175 * Override this if the title needs to be updated dynamically. 176 */ getTitleResId()177 int getTitleResId() { 178 return 0; 179 } 180 181 @Override getPreferenceScreenResId()182 protected int getPreferenceScreenResId() { 183 return R.xml.placeholder_preference_screen; 184 } 185 186 @Override getLogTag()187 protected String getLogTag() { 188 return TAG; 189 } 190 191 @VisibleForTesting getTabId(Activity activity, Bundle bundle)192 int getTabId(Activity activity, Bundle bundle) { 193 if (bundle != null) { 194 final int extraTab = bundle.getInt(SettingsActivity.EXTRA_SHOW_FRAGMENT_TAB, -1); 195 if (extraTab != -1) { 196 return extraTab; 197 } 198 final int userId = bundle.getInt(EXTRA_USER_ID, UserHandle.SYSTEM.getIdentifier()); 199 final boolean isWorkProfile = UserManager.get(activity).isManagedProfile(userId); 200 if (isWorkProfile) { 201 return WORK_TAB; 202 } 203 } 204 // Start intent from a specific user eg: adb shell --user 10 205 final int intentUser = activity.getIntent().getContentUserHint(); 206 if (UserManager.get(activity).isManagedProfile(intentUser)) { 207 return WORK_TAB; 208 } 209 210 return PERSONAL_TAB; 211 } 212 213 static class ViewPagerAdapter extends FragmentStatePagerAdapter { 214 215 private final Fragment[] mChildFragments; 216 private final Context mContext; 217 ViewPagerAdapter(ProfileSelectFragment fragment)218 ViewPagerAdapter(ProfileSelectFragment fragment) { 219 super(fragment.getChildFragmentManager()); 220 mContext = fragment.getContext(); 221 mChildFragments = fragment.getFragments(); 222 } 223 224 @Override getItem(int position)225 public Fragment getItem(int position) { 226 return mChildFragments[convertPosition(position)]; 227 } 228 229 @Override getCount()230 public int getCount() { 231 return mChildFragments.length; 232 } 233 234 @Override getPageTitle(int position)235 public CharSequence getPageTitle(int position) { 236 return mContext.getString(LABEL[convertPosition(position)]); 237 } 238 } 239 convertPosition(int index)240 private static int convertPosition(int index) { 241 if (TextUtils.getLayoutDirectionFromLocale(Locale.getDefault()) 242 == View.LAYOUT_DIRECTION_RTL) { 243 return LABEL.length - 1 - index; 244 } 245 return index; 246 } 247 } 248