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