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 package com.android.launcher3.allapps;
17 
18 import android.content.Context;
19 import android.content.SharedPreferences;
20 import android.graphics.Rect;
21 import android.os.AsyncTask;
22 import android.os.Process;
23 import android.os.UserHandle;
24 import android.os.UserManager;
25 import android.util.AttributeSet;
26 import android.view.MotionEvent;
27 import android.view.ViewConfiguration;
28 import android.widget.Switch;
29 
30 import com.android.launcher3.Insettable;
31 import com.android.launcher3.R;
32 import com.android.launcher3.Utilities;
33 import com.android.launcher3.pm.UserCache;
34 import com.android.launcher3.views.ArrowTipView;
35 
36 import java.lang.ref.WeakReference;
37 
38 /**
39  * Work profile toggle switch shown at the bottom of AllApps work tab
40  */
41 public class WorkModeSwitch extends Switch implements Insettable {
42 
43     private static final int WORK_TIP_THRESHOLD = 2;
44     public static final String KEY_WORK_TIP_COUNTER = "worked_tip_counter";
45 
46     private Rect mInsets = new Rect();
47 
48     private final float[] mTouch = new float[2];
49     private int mTouchSlop;
50 
WorkModeSwitch(Context context)51     public WorkModeSwitch(Context context) {
52         super(context);
53         init();
54     }
55 
WorkModeSwitch(Context context, AttributeSet attrs)56     public WorkModeSwitch(Context context, AttributeSet attrs) {
57         super(context, attrs);
58         init();
59     }
60 
WorkModeSwitch(Context context, AttributeSet attrs, int defStyleAttr)61     public WorkModeSwitch(Context context, AttributeSet attrs, int defStyleAttr) {
62         super(context, attrs, defStyleAttr);
63         init();
64     }
65 
init()66     private void init() {
67         ViewConfiguration viewConfiguration = ViewConfiguration.get(getContext());
68         mTouchSlop = viewConfiguration.getScaledTouchSlop();
69     }
70 
71     @Override
setChecked(boolean checked)72     public void setChecked(boolean checked) { }
73 
74     @Override
toggle()75     public void toggle() {
76         // don't show tip if user uses toggle
77         Utilities.getPrefs(getContext()).edit().putInt(KEY_WORK_TIP_COUNTER, -1).apply();
78         trySetQuietModeEnabledToAllProfilesAsync(isChecked());
79     }
80 
81     /**
82      * Sets the enabled or disabled state of the button
83      * @param isChecked
84      */
update(boolean isChecked)85     public void update(boolean isChecked) {
86         super.setChecked(isChecked);
87         setCompoundDrawablesRelativeWithIntrinsicBounds(
88                 isChecked ? R.drawable.ic_corp : R.drawable.ic_corp_off, 0, 0, 0);
89         setEnabled(true);
90     }
91 
92     @Override
onTouchEvent(MotionEvent ev)93     public boolean onTouchEvent(MotionEvent ev) {
94         if (ev.getActionMasked() == MotionEvent.ACTION_DOWN) {
95             mTouch[0] = ev.getX();
96             mTouch[1] = ev.getY();
97         } else if (ev.getActionMasked() == MotionEvent.ACTION_MOVE) {
98             if (Math.abs(mTouch[0] - ev.getX()) > mTouchSlop
99                     || Math.abs(mTouch[1] - ev.getY()) > mTouchSlop) {
100                 int action = ev.getAction();
101                 ev.setAction(MotionEvent.ACTION_CANCEL);
102                 super.onTouchEvent(ev);
103                 ev.setAction(action);
104                 return false;
105             }
106         }
107         return super.onTouchEvent(ev);
108     }
109 
trySetQuietModeEnabledToAllProfilesAsync(boolean enabled)110     private void trySetQuietModeEnabledToAllProfilesAsync(boolean enabled) {
111         new SetQuietModeEnabledAsyncTask(enabled, new WeakReference<>(this)).execute();
112     }
113 
114     @Override
setInsets(Rect insets)115     public void setInsets(Rect insets) {
116         int bottomInset = insets.bottom - mInsets.bottom;
117         mInsets.set(insets);
118         setPadding(getPaddingLeft(), getPaddingTop(), getPaddingRight(),
119                 getPaddingBottom() + bottomInset);
120     }
121 
122     /**
123      * Animates in/out work profile toggle panel based on the tab user is on
124      */
setWorkTabVisible(boolean workTabVisible)125     public void setWorkTabVisible(boolean workTabVisible) {
126         clearAnimation();
127         if (workTabVisible) {
128             setVisibility(VISIBLE);
129             setAlpha(0);
130             animate().alpha(1).start();
131             showTipIfNeeded();
132         } else {
133             animate().alpha(0).withEndAction(() -> this.setVisibility(GONE)).start();
134         }
135     }
136 
137     private static final class SetQuietModeEnabledAsyncTask
138             extends AsyncTask<Void, Void, Boolean> {
139 
140         private final boolean enabled;
141         private final WeakReference<WorkModeSwitch> switchWeakReference;
142 
SetQuietModeEnabledAsyncTask(boolean enabled, WeakReference<WorkModeSwitch> switchWeakReference)143         SetQuietModeEnabledAsyncTask(boolean enabled,
144                                      WeakReference<WorkModeSwitch> switchWeakReference) {
145             this.enabled = enabled;
146             this.switchWeakReference = switchWeakReference;
147         }
148 
149         @Override
onPreExecute()150         protected void onPreExecute() {
151             super.onPreExecute();
152             WorkModeSwitch workModeSwitch = switchWeakReference.get();
153             if (workModeSwitch != null) {
154                 workModeSwitch.setEnabled(false);
155             }
156         }
157 
158         @Override
doInBackground(Void... voids)159         protected Boolean doInBackground(Void... voids) {
160             WorkModeSwitch workModeSwitch = switchWeakReference.get();
161             if (workModeSwitch == null || !Utilities.ATLEAST_P) {
162                 return false;
163             }
164 
165             Context context = workModeSwitch.getContext();
166             UserManager userManager = context.getSystemService(UserManager.class);
167             boolean showConfirm = false;
168             for (UserHandle userProfile : UserCache.INSTANCE.get(context).getUserProfiles()) {
169                 if (Process.myUserHandle().equals(userProfile)) {
170                     continue;
171                 }
172                 showConfirm |= !userManager.requestQuietModeEnabled(enabled, userProfile);
173             }
174             return showConfirm;
175         }
176 
177         @Override
onPostExecute(Boolean showConfirm)178         protected void onPostExecute(Boolean showConfirm) {
179             if (showConfirm) {
180                 WorkModeSwitch workModeSwitch = switchWeakReference.get();
181                 if (workModeSwitch != null) {
182                     workModeSwitch.setEnabled(true);
183                 }
184             }
185         }
186     }
187 
188     /**
189      * Shows a work tip on the Nth work tab open
190      */
showTipIfNeeded()191     public void showTipIfNeeded() {
192         Context context = getContext();
193         SharedPreferences prefs = Utilities.getPrefs(context);
194         int tipCounter = prefs.getInt(KEY_WORK_TIP_COUNTER, WORK_TIP_THRESHOLD);
195         if (tipCounter < 0) return;
196         if (tipCounter == 0) {
197             new ArrowTipView(context)
198                     .show(context.getString(R.string.work_switch_tip), getTop());
199         }
200         prefs.edit().putInt(KEY_WORK_TIP_COUNTER, tipCounter - 1).apply();
201     }
202 }
203