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.app.admin.DevicePolicyResources.Strings.Settings.PERSONAL_CATEGORY_HEADER; 20 import static android.app.admin.DevicePolicyResources.Strings.Settings.WORK_CATEGORY_HEADER; 21 import static android.content.Intent.EXTRA_USER_ID; 22 23 import android.annotation.IntDef; 24 import android.app.Activity; 25 import android.app.admin.DevicePolicyManager; 26 import android.os.Bundle; 27 import android.os.UserHandle; 28 import android.os.UserManager; 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.recyclerview.widget.RecyclerView; 38 import androidx.viewpager2.adapter.FragmentStateAdapter; 39 import androidx.viewpager2.widget.ViewPager2; 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 import com.google.android.material.tabs.TabLayoutMediator; 48 49 import java.lang.annotation.Retention; 50 import java.lang.annotation.RetentionPolicy; 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 100 private ViewGroup mContentView; 101 102 private ViewPager2 mViewPager; 103 104 @Override onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)105 public View onCreateView(LayoutInflater inflater, ViewGroup container, 106 Bundle savedInstanceState) { 107 mContentView = (ViewGroup) super.onCreateView(inflater, container, savedInstanceState); 108 final Activity activity = getActivity(); 109 final int titleResId = getTitleResId(); 110 if (titleResId > 0) { 111 activity.setTitle(titleResId); 112 } 113 final int selectedTab = getTabId(activity, getArguments()); 114 115 final View tabContainer = mContentView.findViewById(R.id.tab_container); 116 mViewPager = tabContainer.findViewById(R.id.view_pager); 117 mViewPager.setAdapter(new ProfileSelectFragment.ViewPagerAdapter(this)); 118 final TabLayout tabs = tabContainer.findViewById(R.id.tabs); 119 new TabLayoutMediator(tabs, mViewPager, 120 (tab, position) -> tab.setText(getPageTitle(position)) 121 ).attach(); 122 mViewPager.registerOnPageChangeCallback( 123 new ViewPager2.OnPageChangeCallback() { 124 @Override 125 public void onPageSelected(int position) { 126 super.onPageSelected(position); 127 updateHeight(position); 128 } 129 } 130 ); 131 tabContainer.setVisibility(View.VISIBLE); 132 final TabLayout.Tab tab = tabs.getTabAt(selectedTab); 133 tab.select(); 134 135 final FrameLayout listContainer = mContentView.findViewById(android.R.id.list_container); 136 listContainer.setLayoutParams(new LinearLayout.LayoutParams( 137 ViewGroup.LayoutParams.MATCH_PARENT, 138 ViewGroup.LayoutParams.WRAP_CONTENT)); 139 140 final RecyclerView recyclerView = getListView(); 141 recyclerView.setOverScrollMode(View.OVER_SCROLL_NEVER); 142 Utils.setActionBarShadowAnimation(activity, getSettingsLifecycle(), recyclerView); 143 144 return mContentView; 145 } 146 forceUpdateHeight()147 protected boolean forceUpdateHeight() { 148 return false; 149 } 150 updateHeight(int position)151 private void updateHeight(int position) { 152 if (!forceUpdateHeight()) { 153 return; 154 } 155 ViewPagerAdapter adapter = (ViewPagerAdapter) mViewPager.getAdapter(); 156 if (adapter == null || adapter.getItemCount() <= position) { 157 return; 158 } 159 160 Fragment fragment = adapter.createFragment(position); 161 View newPage = fragment.getView(); 162 if (newPage != null) { 163 int viewWidth = View.MeasureSpec.makeMeasureSpec(newPage.getWidth(), 164 View.MeasureSpec.EXACTLY); 165 int viewHeight = View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED); 166 newPage.measure(viewWidth, viewHeight); 167 int currentHeight = mViewPager.getLayoutParams().height; 168 int newHeight = newPage.getMeasuredHeight(); 169 if (newHeight != 0 && currentHeight != newHeight) { 170 ViewGroup.LayoutParams layoutParams = mViewPager.getLayoutParams(); 171 layoutParams.height = newHeight; 172 mViewPager.setLayoutParams(layoutParams); 173 } 174 } 175 } 176 177 @Override getMetricsCategory()178 public int getMetricsCategory() { 179 return METRICS_CATEGORY_UNKNOWN; 180 } 181 182 /** 183 * Returns an array of {@link Fragment} to display in the 184 * {@link com.google.android.material.tabs.TabLayout} 185 */ getFragments()186 public abstract Fragment[] getFragments(); 187 188 /** 189 * Returns a resource ID of the title 190 * Override this if the title needs to be updated dynamically. 191 */ getTitleResId()192 public int getTitleResId() { 193 return 0; 194 } 195 196 @Override getPreferenceScreenResId()197 protected int getPreferenceScreenResId() { 198 return R.xml.placeholder_preference_screen; 199 } 200 201 @Override getLogTag()202 protected String getLogTag() { 203 return TAG; 204 } 205 206 @VisibleForTesting getTabId(Activity activity, Bundle bundle)207 int getTabId(Activity activity, Bundle bundle) { 208 if (bundle != null) { 209 final int extraTab = bundle.getInt(SettingsActivity.EXTRA_SHOW_FRAGMENT_TAB, -1); 210 if (extraTab != -1) { 211 return extraTab; 212 } 213 final int userId = bundle.getInt(EXTRA_USER_ID, UserHandle.SYSTEM.getIdentifier()); 214 final boolean isWorkProfile = UserManager.get(activity).isManagedProfile(userId); 215 if (isWorkProfile) { 216 return WORK_TAB; 217 } 218 } 219 // Start intent from a specific user eg: adb shell --user 10 220 final int intentUser = activity.getIntent().getContentUserHint(); 221 if (UserManager.get(activity).isManagedProfile(intentUser)) { 222 return WORK_TAB; 223 } 224 225 return PERSONAL_TAB; 226 } 227 getPageTitle(int position)228 private CharSequence getPageTitle(int position) { 229 final DevicePolicyManager devicePolicyManager = 230 getContext().getSystemService(DevicePolicyManager.class); 231 232 if (position == WORK_TAB) { 233 return devicePolicyManager.getResources().getString(WORK_CATEGORY_HEADER, 234 () -> getContext().getString(R.string.category_work)); 235 } 236 237 return devicePolicyManager.getResources().getString(PERSONAL_CATEGORY_HEADER, 238 () -> getContext().getString(R.string.category_personal)); 239 } 240 241 static class ViewPagerAdapter extends FragmentStateAdapter { 242 243 private final Fragment[] mChildFragments; 244 ViewPagerAdapter(ProfileSelectFragment fragment)245 ViewPagerAdapter(ProfileSelectFragment fragment) { 246 super(fragment); 247 mChildFragments = fragment.getFragments(); 248 } 249 250 @Override createFragment(int position)251 public Fragment createFragment(int position) { 252 return mChildFragments[position]; 253 } 254 255 @Override getItemCount()256 public int getItemCount() { 257 return mChildFragments.length; 258 } 259 } 260 } 261