• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2020 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.documentsui;
18 
19 import static androidx.core.util.Preconditions.checkNotNull;
20 
21 import static com.android.documentsui.DevicePolicyResources.Strings.PERSONAL_TAB;
22 import static com.android.documentsui.DevicePolicyResources.Strings.WORK_TAB;
23 
24 import android.app.admin.DevicePolicyManager;
25 import android.os.Build;
26 import android.view.View;
27 import android.view.ViewGroup;
28 
29 import androidx.annotation.Nullable;
30 import androidx.annotation.RequiresApi;
31 
32 import com.android.documentsui.base.RootInfo;
33 import com.android.documentsui.base.State;
34 import com.android.documentsui.base.UserId;
35 import com.android.modules.utils.build.SdkLevel;
36 
37 import com.google.android.material.tabs.TabLayout;
38 import com.google.common.base.Objects;
39 
40 import java.util.Collections;
41 import java.util.List;
42 
43 /**
44  * A manager class to control UI on a {@link TabLayout} for cross-profile purpose.
45  */
46 public class ProfileTabs implements ProfileTabsAddons {
47     private static final float DISABLED_TAB_OPACITY = 0.38f;
48 
49     private final View mTabsContainer;
50     private final TabLayout mTabs;
51     private final State mState;
52     private final NavigationViewManager.Environment mEnv;
53     private final AbstractActionHandler.CommonAddons mCommonAddons;
54     private final UserIdManager mUserIdManager;
55     private List<UserId> mUserIds;
56     @Nullable
57     private Listener mListener;
58     private TabLayout.OnTabSelectedListener mOnTabSelectedListener;
59     private View mTabSeparator;
60 
ProfileTabs(View tabLayoutContainer, State state, UserIdManager userIdManager, NavigationViewManager.Environment env, AbstractActionHandler.CommonAddons commonAddons)61     public ProfileTabs(View tabLayoutContainer, State state, UserIdManager userIdManager,
62             NavigationViewManager.Environment env,
63             AbstractActionHandler.CommonAddons commonAddons) {
64         mTabsContainer = checkNotNull(tabLayoutContainer);
65         mTabs = tabLayoutContainer.findViewById(R.id.tabs);
66         mState = checkNotNull(state);
67         mEnv = checkNotNull(env);
68         mCommonAddons = checkNotNull(commonAddons);
69         mUserIdManager = checkNotNull(userIdManager);
70         mTabs.removeAllTabs();
71         mUserIds = Collections.singletonList(UserId.CURRENT_USER);
72         mTabSeparator = tabLayoutContainer.findViewById(R.id.tab_separator);
73 
74         mOnTabSelectedListener = new TabLayout.OnTabSelectedListener() {
75             @Override
76             public void onTabSelected(TabLayout.Tab tab) {
77                 if (mListener != null) {
78                     // find a way to identify user iteraction
79                     mListener.onUserSelected((UserId) tab.getTag());
80                 }
81             }
82 
83             @Override
84             public void onTabUnselected(TabLayout.Tab tab) {
85             }
86 
87             @Override
88             public void onTabReselected(TabLayout.Tab tab) {
89             }
90         };
91         mTabs.addOnTabSelectedListener(mOnTabSelectedListener);
92     }
93 
94     /**
95      * Update the tab layout based on conditions.
96      */
updateView()97     public void updateView() {
98         updateTabsIfNeeded();
99         RootInfo currentRoot = mCommonAddons.getCurrentRoot();
100         if (mTabs.getSelectedTabPosition() == -1
101                 || !Objects.equal(currentRoot.userId, getSelectedUser())) {
102             // Update the layout according to the current root if necessary.
103             // Make sure we do not invoke callback. Otherwise, it is likely to cause infinite loop.
104             mTabs.removeOnTabSelectedListener(mOnTabSelectedListener);
105             mTabs.selectTab(mTabs.getTabAt(mUserIds.indexOf(currentRoot.userId)));
106             mTabs.addOnTabSelectedListener(mOnTabSelectedListener);
107         }
108         mTabsContainer.setVisibility(shouldShow() ? View.VISIBLE : View.GONE);
109 
110         // Material next changes apply only for version S or greater
111         if (SdkLevel.isAtLeastS()) {
112             mTabSeparator.setVisibility(View.GONE);
113             int tabContainerHeightInDp = (int)mTabsContainer.getContext().getResources().
114                 getDimension(R.dimen.tab_container_height);
115             mTabsContainer.getLayoutParams().height = tabContainerHeightInDp;
116             ViewGroup.MarginLayoutParams tabContainerMarginLayoutParams =
117                 (ViewGroup.MarginLayoutParams) mTabsContainer.getLayoutParams();
118             int tabContainerMarginTop = (int)mTabsContainer.getContext().getResources().
119                 getDimension(R.dimen.profile_tab_margin_top);
120             tabContainerMarginLayoutParams.setMargins(0, tabContainerMarginTop, 0, 0);
121             mTabsContainer.requestLayout();
122             for (int i = 0; i < mTabs.getTabCount(); i++) {
123 
124                 // Tablayout holds a view that contains the individual tab
125                 View tab = ((ViewGroup) mTabs.getChildAt(0)).getChildAt(i);
126 
127                 // Get individual tab to set the style
128                 ViewGroup.MarginLayoutParams marginLayoutParams =
129                         (ViewGroup.MarginLayoutParams) tab.getLayoutParams();
130                 int tabMarginSide = (int)mTabsContainer.getContext().getResources().
131                     getDimension(R.dimen.profile_tab_margin_side);
132                 marginLayoutParams.setMargins(tabMarginSide, 0, tabMarginSide, 0);
133                 int tabHeightInDp = (int)mTabsContainer.getContext().getResources().
134                     getDimension(R.dimen.tab_height);
135                 tab.getLayoutParams().height = tabHeightInDp;
136                 tab.requestLayout();
137                 tab.setBackgroundResource(R.drawable.tab_border_rounded);
138             }
139 
140         }
141     }
142 
setListener(@ullable Listener listener)143     public void setListener(@Nullable Listener listener) {
144         mListener = listener;
145     }
146 
updateTabsIfNeeded()147     private void updateTabsIfNeeded() {
148         List<UserId> userIds = mUserIdManager.getUserIds();
149         // Add tabs if the userIds is not equals to cached mUserIds.
150         // Given that mUserIds was initialized with only the current user, if getUserIds()
151         // returns just the current user, we don't need to do anything on the tab layout.
152         if (!userIds.equals(mUserIds)) {
153             mUserIds = userIds;
154             mTabs.removeAllTabs();
155             if (mUserIds.size() > 1) {
156                 // set setSelected to false otherwise it will trigger callback.
157                 mTabs.addTab(createTab(
158                         getEnterpriseString(PERSONAL_TAB, R.string.personal_tab),
159                         mUserIdManager.getSystemUser()), /* setSelected= */false);
160                 mTabs.addTab(createTab(
161                         getEnterpriseString(WORK_TAB, R.string.work_tab),
162                         mUserIdManager.getManagedUser()), /* setSelected= */false);
163             }
164         }
165     }
166 
getEnterpriseString(String updatableStringId, int defaultStringId)167     private String getEnterpriseString(String updatableStringId, int defaultStringId) {
168         if (SdkLevel.isAtLeastT()) {
169             return getUpdatableEnterpriseString(updatableStringId, defaultStringId);
170         } else {
171             return mTabsContainer.getContext().getString(defaultStringId);
172         }
173     }
174 
175     @RequiresApi(Build.VERSION_CODES.TIRAMISU)
getUpdatableEnterpriseString(String updatableStringId, int defaultStringId)176     private String getUpdatableEnterpriseString(String updatableStringId, int defaultStringId) {
177         DevicePolicyManager dpm = mTabsContainer.getContext().getSystemService(
178                 DevicePolicyManager.class);
179         return dpm.getResources().getString(
180                 updatableStringId,
181                 () -> mTabsContainer.getContext().getString(defaultStringId));
182     }
183 
184     /**
185      * Returns the user represented by the selected tab. If there is no tab, return the
186      * current user.
187      */
getSelectedUser()188     public UserId getSelectedUser() {
189         if (mTabs.getTabCount() > 1 && mTabs.getSelectedTabPosition() >= 0) {
190             return (UserId) mTabs.getTabAt(mTabs.getSelectedTabPosition()).getTag();
191         }
192         return UserId.CURRENT_USER;
193     }
194 
shouldShow()195     private boolean shouldShow() {
196         // Only show tabs when:
197         // 1. state supports cross profile, and
198         // 2. more than one tab, and
199         // 3. not in search mode, and
200         // 4. not in sub-folder, and
201         // 5. the root supports cross profile.
202         return mState.supportsCrossProfile()
203                 && mTabs.getTabCount() > 1
204                 && !mEnv.isSearchExpanded()
205                 && mState.stack.size() <= 1
206                 && mState.stack.getRoot() != null && mState.stack.getRoot().supportsCrossProfile();
207     }
208 
createTab(String text, UserId userId)209     private TabLayout.Tab createTab(String text, UserId userId) {
210         return mTabs.newTab().setText(text).setTag(userId);
211     }
212 
213     @Override
setEnabled(boolean enabled)214     public void setEnabled(boolean enabled) {
215         if (mTabs.getChildCount() > 0) {
216             View view = mTabs.getChildAt(0);
217             if (view instanceof ViewGroup) {
218                 ViewGroup tabs = (ViewGroup) view;
219                 for (int i = 0; i < tabs.getChildCount(); i++) {
220                     View tabView = tabs.getChildAt(i);
221                     tabView.setEnabled(enabled);
222                     tabView.setAlpha((enabled || mTabs.getSelectedTabPosition() == i) ? 1f
223                             : DISABLED_TAB_OPACITY);
224                 }
225             }
226         }
227     }
228 
229     /**
230      * Interface definition for a callback to be invoked.
231      */
232     interface Listener {
233         /**
234          * Called when a user tab has been selected.
235          */
onUserSelected(UserId userId)236         void onUserSelected(UserId userId);
237     }
238 }
239