• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2021 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 package com.android.launcher3.allapps;
17 
18 import static com.android.launcher3.LauncherPrefs.WORK_EDU_STEP;
19 import static com.android.launcher3.allapps.ActivityAllAppsContainerView.AdapterHolder.MAIN;
20 import static com.android.launcher3.allapps.ActivityAllAppsContainerView.AdapterHolder.SEARCH;
21 import static com.android.launcher3.allapps.ActivityAllAppsContainerView.AdapterHolder.WORK;
22 import static com.android.launcher3.allapps.BaseAllAppsAdapter.VIEW_TYPE_WORK_DISABLED_CARD;
23 import static com.android.launcher3.allapps.BaseAllAppsAdapter.VIEW_TYPE_WORK_EDU_CARD;
24 import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_TURN_OFF_WORK_APPS_TAP;
25 import static com.android.launcher3.model.BgDataModel.Callbacks.FLAG_HAS_SHORTCUT_PERMISSION;
26 import static com.android.launcher3.model.BgDataModel.Callbacks.FLAG_QUIET_MODE_CHANGE_PERMISSION;
27 import static com.android.launcher3.model.BgDataModel.Callbacks.FLAG_QUIET_MODE_ENABLED;
28 import static com.android.launcher3.model.BgDataModel.Callbacks.FLAG_WORK_PROFILE_QUIET_MODE_ENABLED;
29 
30 import android.os.UserHandle;
31 import android.os.UserManager;
32 import android.util.Log;
33 import android.view.View;
34 
35 import androidx.annotation.NonNull;
36 import androidx.annotation.Nullable;
37 import androidx.recyclerview.widget.RecyclerView;
38 
39 import com.android.launcher3.Flags;
40 import com.android.launcher3.LauncherPrefs;
41 import com.android.launcher3.R;
42 import com.android.launcher3.Utilities;
43 import com.android.launcher3.allapps.BaseAllAppsAdapter.AdapterItem;
44 import com.android.launcher3.logging.StatsLogManager;
45 import com.android.launcher3.pm.UserCache;
46 import com.android.launcher3.workprofile.PersonalWorkSlidingTabStrip;
47 
48 import java.util.ArrayList;
49 import java.util.function.Predicate;
50 import java.util.stream.Stream;
51 
52 /**
53  * Companion class for {@link ActivityAllAppsContainerView} to manage work tab and personal tab
54  * related
55  * logic based on {@link UserProfileState}?
56  */
57 public class WorkProfileManager extends UserProfileManager
58         implements PersonalWorkSlidingTabStrip.OnActivePageChangedListener {
59     private static final String TAG = "WorkProfileManager";
60     private final ActivityAllAppsContainerView<?> mAllApps;
61     private WorkUtilityView mWorkUtilityView;
62     private final Predicate<UserHandle> mWorkProfileMatcher;
63 
WorkProfileManager( UserManager userManager, ActivityAllAppsContainerView allApps, StatsLogManager statsLogManager, UserCache userCache)64     public WorkProfileManager(
65             UserManager userManager, ActivityAllAppsContainerView allApps,
66             StatsLogManager statsLogManager, UserCache userCache) {
67         super(userManager, statsLogManager, userCache);
68         mAllApps = allApps;
69         mWorkProfileMatcher = (user) -> userCache.getUserInfo(user).isWork();
70     }
71 
72     /**
73      * Posts quite mode enable/disable call for work profile user
74      */
setWorkProfileEnabled(boolean enabled)75     public void setWorkProfileEnabled(boolean enabled) {
76         updateCurrentState(STATE_TRANSITION);
77         setQuietMode(!enabled, mAllApps.mActivityContext);
78     }
79 
80     @Override
onActivePageChanged(int page)81     public void onActivePageChanged(int page) {
82         updateWorkUtilityViews(page);
83     }
84 
updateWorkUtilityViews(int page)85     private void updateWorkUtilityViews(int page) {
86         if (mWorkUtilityView != null) {
87             if (page == MAIN || page == SEARCH) {
88                 mWorkUtilityView.animateVisibility(false);
89             } else if (page == WORK && getCurrentState() == STATE_ENABLED) {
90                 mWorkUtilityView.animateVisibility(true);
91             }
92         }
93     }
94 
95     /**
96      * Requests work profile state from {@link AllAppsStore} and updates work profile related views
97      */
reset()98     public void reset() {
99         int quietModeFlag;
100         if (Flags.enablePrivateSpace()) {
101             quietModeFlag = FLAG_WORK_PROFILE_QUIET_MODE_ENABLED;
102         } else {
103             quietModeFlag = FLAG_QUIET_MODE_ENABLED;
104         }
105         boolean isEnabled = !mAllApps.getAppsStore().hasModelFlag(quietModeFlag);
106         updateCurrentState(isEnabled ? STATE_ENABLED : STATE_DISABLED);
107         if (mWorkUtilityView != null) {
108             // reset the position of the button and clear IME insets.
109             mWorkUtilityView.getImeInsets().setEmpty();
110             mWorkUtilityView.updateTranslationY();
111         }
112     }
113 
updateCurrentState(@serProfileState int currentState)114     private void updateCurrentState(@UserProfileState int currentState) {
115         setCurrentState(currentState);
116         if (getAH() != null) {
117             getAH().mAppsList.updateAdapterItems();
118         }
119         if (mWorkUtilityView != null) {
120             updateWorkUtilityViews(mAllApps.getCurrentPage());
121         }
122         if (getCurrentState() == STATE_ENABLED) {
123             attachWorkUtilityViews();
124         } else if (getCurrentState() == STATE_DISABLED) {
125             detachWorkUtilityViews();
126         }
127     }
128 
129     /**
130      * Creates and attaches for profile toggle button to {@link ActivityAllAppsContainerView}
131      */
attachWorkUtilityViews()132     public boolean attachWorkUtilityViews() {
133         if (!mAllApps.getAppsStore().hasModelFlag(
134                 FLAG_HAS_SHORTCUT_PERMISSION | FLAG_QUIET_MODE_CHANGE_PERMISSION)) {
135             Log.e(TAG, "unable to attach work mode switch; Missing required permissions");
136             return false;
137         }
138         if (mWorkUtilityView == null) {
139             mWorkUtilityView = (WorkUtilityView) mAllApps.getLayoutInflater().inflate(
140                     R.layout.work_mode_utility_view, mAllApps, false);
141         }
142         if (mWorkUtilityView.getParent() == null) {
143             mAllApps.addView(mWorkUtilityView);
144         }
145         if (mAllApps.getCurrentPage() != WORK) {
146             mWorkUtilityView.animateVisibility(false);
147         }
148         if (getAH() != null) {
149             getAH().applyPadding();
150         }
151         mWorkUtilityView.getWorkFAB().setOnClickListener(this::onWorkFabClicked);
152         return true;
153     }
154     /**
155      * Removes work profile toggle button from {@link ActivityAllAppsContainerView}
156      */
detachWorkUtilityViews()157     public void detachWorkUtilityViews() {
158         if (mWorkUtilityView != null && mWorkUtilityView.getParent() == mAllApps) {
159             mAllApps.removeView(mWorkUtilityView);
160         }
161         mWorkUtilityView = null;
162     }
163 
164     @Nullable
getWorkUtilityView()165     public WorkUtilityView getWorkUtilityView() {
166         return mWorkUtilityView;
167     }
168 
getAH()169     private ActivityAllAppsContainerView.AdapterHolder getAH() {
170         return mAllApps.mAH.get(WORK);
171     }
172 
173     /**
174      * returns whether or not work apps should be visible in work tab.
175      */
shouldShowWorkApps()176     public boolean shouldShowWorkApps() {
177         return getCurrentState() != WorkProfileManager.STATE_DISABLED;
178     }
179 
hasWorkApps()180     public boolean hasWorkApps() {
181         return Stream.of(mAllApps.getAppsStore().getApps()).anyMatch(getItemInfoMatcher());
182     }
183 
184     /**
185      * Adds work profile specific adapter items to adapterItems and returns number of items added
186      */
addWorkItems(ArrayList<AdapterItem> adapterItems)187     public int addWorkItems(ArrayList<AdapterItem> adapterItems) {
188         if (getCurrentState() == WorkProfileManager.STATE_DISABLED) {
189             //add disabled card here.
190             adapterItems.add(new AdapterItem(VIEW_TYPE_WORK_DISABLED_CARD));
191         } else if (getCurrentState() == WorkProfileManager.STATE_ENABLED && !isEduSeen()) {
192             adapterItems.add(new AdapterItem(VIEW_TYPE_WORK_EDU_CARD));
193         }
194         return adapterItems.size();
195     }
196 
isEduSeen()197     private boolean isEduSeen() {
198         return LauncherPrefs.get(mAllApps.getContext()).get(WORK_EDU_STEP) != 0;
199     }
200 
onWorkFabClicked(View view)201     private void onWorkFabClicked(View view) {
202         if (getCurrentState() == STATE_ENABLED && mWorkUtilityView.isEnabled()) {
203             Log.d(TAG, "Work FAB clicked.");
204             logEvents(LAUNCHER_TURN_OFF_WORK_APPS_TAP);
205             setWorkProfileEnabled(false);
206         }
207     }
208 
newScrollListener()209     public RecyclerView.OnScrollListener newScrollListener() {
210         return new RecyclerView.OnScrollListener() {
211             int totalDelta = 0;
212             @Override
213             public void onScrollStateChanged(@NonNull RecyclerView recyclerView, int newState){
214                 if (newState == RecyclerView.SCROLL_STATE_IDLE) {
215                     totalDelta = 0;
216                 }
217             }
218             @Override
219             public void onScrolled(@NonNull RecyclerView recyclerView, int dx, int dy) {
220                 WorkUtilityView fab = getWorkUtilityView();
221                 if (fab == null){
222                     return;
223                 }
224                 totalDelta = Utilities.boundToRange(totalDelta,
225                         -fab.getScrollThreshold(), fab.getScrollThreshold()) + dy;
226                 boolean isScrollAtTop = recyclerView.computeVerticalScrollOffset() == 0;
227                 if ((isScrollAtTop || totalDelta < -fab.getScrollThreshold())) {
228                     fab.extend();
229                 } else if (totalDelta > fab.getScrollThreshold()) {
230                     fab.shrink();
231                 }
232             }
233         };
234     }
235 
236     @Override
getUserMatcher()237     public Predicate<UserHandle> getUserMatcher() {
238         return mWorkProfileMatcher;
239     }
240 }
241