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.views;
17 
18 
19 import android.animation.Animator;
20 import android.animation.ObjectAnimator;
21 import android.animation.PropertyValuesHolder;
22 import android.content.Context;
23 import android.graphics.Rect;
24 import android.util.AttributeSet;
25 import android.view.LayoutInflater;
26 import android.view.View;
27 import android.widget.Button;
28 import android.widget.TextView;
29 
30 import androidx.annotation.Nullable;
31 
32 import com.android.launcher3.Insettable;
33 import com.android.launcher3.Launcher;
34 import com.android.launcher3.LauncherState;
35 import com.android.launcher3.R;
36 import com.android.launcher3.allapps.AllAppsContainerView;
37 import com.android.launcher3.allapps.AllAppsPagedView;
38 import com.android.launcher3.anim.AnimationSuccessListener;
39 import com.android.launcher3.anim.Interpolators;
40 import com.android.launcher3.statemanager.StateManager.StateListener;
41 import com.android.launcher3.userevent.nano.LauncherLogProto;
42 
43 /**
44  * On boarding flow for users right after setting up work profile
45  */
46 public class WorkEduView extends AbstractSlideInView
47         implements Insettable, StateListener<LauncherState> {
48 
49     private static final int DEFAULT_CLOSE_DURATION = 200;
50     public static final String KEY_WORK_EDU_STEP = "showed_work_profile_edu";
51     public static final String KEY_LEGACY_WORK_EDU_SEEN = "showed_bottom_user_education";
52 
53     private static final int WORK_EDU_NOT_STARTED = 0;
54     private static final int WORK_EDU_PERSONAL_APPS = 1;
55     private static final int WORK_EDU_WORK_APPS = 2;
56 
57     protected static final int FINAL_SCRIM_BG_COLOR = 0x88000000;
58 
59 
60     private Rect mInsets = new Rect();
61     private View mViewWrapper;
62     private Button mProceedButton;
63     private TextView mContentText;
64     private AllAppsPagedView mAllAppsPagedView;
65 
66     private int mNextWorkEduStep = WORK_EDU_PERSONAL_APPS;
67 
68 
WorkEduView(Context context, AttributeSet attr)69     public WorkEduView(Context context, AttributeSet attr) {
70         this(context, attr, 0);
71     }
72 
WorkEduView(Context context, AttributeSet attrs, int defStyleAttr)73     public WorkEduView(Context context, AttributeSet attrs,
74             int defStyleAttr) {
75         super(context, attrs, defStyleAttr);
76         mContent = this;
77     }
78 
79     @Override
handleClose(boolean animate)80     protected void handleClose(boolean animate) {
81         mLauncher.getSharedPrefs().edit().putInt(KEY_WORK_EDU_STEP, mNextWorkEduStep).apply();
82         handleClose(true, DEFAULT_CLOSE_DURATION);
83     }
84 
85     @Override
onCloseComplete()86     protected void onCloseComplete() {
87         super.onCloseComplete();
88         mLauncher.getStateManager().removeStateListener(this);
89     }
90 
91     @Override
logActionCommand(int command)92     public void logActionCommand(int command) {
93         // Since this is on-boarding popup, it is not a user controlled action.
94     }
95 
96     @Override
getLogContainerType()97     public int getLogContainerType() {
98         return LauncherLogProto.ContainerType.TIP;
99     }
100 
101     @Override
isOfType(int type)102     protected boolean isOfType(int type) {
103         return (type & TYPE_ON_BOARD_POPUP) != 0;
104     }
105 
106     @Override
onFinishInflate()107     protected void onFinishInflate() {
108         super.onFinishInflate();
109         mViewWrapper = findViewById(R.id.view_wrapper);
110         mProceedButton = findViewById(R.id.proceed);
111         mContentText = findViewById(R.id.content_text);
112 
113         // make sure layout does not shrink when we change the text
114         mContentText.post(() -> mContentText.setMinLines(mContentText.getLineCount()));
115         if (mLauncher.getAppsView().getContentView() instanceof AllAppsPagedView) {
116             mAllAppsPagedView = (AllAppsPagedView) mLauncher.getAppsView().getContentView();
117         }
118 
119         mProceedButton.setOnClickListener(view -> {
120             if (mAllAppsPagedView != null) {
121                 mAllAppsPagedView.snapToPage(AllAppsContainerView.AdapterHolder.WORK);
122             }
123             goToWorkTab(true);
124         });
125     }
126 
goToWorkTab(boolean animate)127     private void goToWorkTab(boolean animate) {
128         mProceedButton.setText(R.string.work_profile_edu_accept);
129         if (animate) {
130             ObjectAnimator animator = ObjectAnimator.ofFloat(mContentText, ALPHA, 0);
131             animator.addListener(new AnimationSuccessListener() {
132                 @Override
133                 public void onAnimationSuccess(Animator animator) {
134                     mContentText.setText(mLauncher.getString(R.string.work_profile_edu_work_apps));
135                     ObjectAnimator.ofFloat(mContentText, ALPHA, 1).start();
136                 }
137             });
138             animator.start();
139         } else {
140             mContentText.setText(mLauncher.getString(R.string.work_profile_edu_work_apps));
141         }
142         mNextWorkEduStep = WORK_EDU_WORK_APPS;
143         mProceedButton.setOnClickListener(v -> handleClose(true));
144     }
145 
146     @Override
setInsets(Rect insets)147     public void setInsets(Rect insets) {
148         int leftInset = insets.left - mInsets.left;
149         int rightInset = insets.right - mInsets.right;
150         int bottomInset = insets.bottom - mInsets.bottom;
151         mInsets.set(insets);
152         setPadding(leftInset, getPaddingTop(), rightInset, 0);
153         mViewWrapper.setPaddingRelative(mViewWrapper.getPaddingStart(),
154                 mViewWrapper.getPaddingTop(), mViewWrapper.getPaddingEnd(), bottomInset);
155     }
156 
show()157     private void show() {
158         attachToContainer();
159         animateOpen();
160         mLauncher.getStateManager().addStateListener(this);
161     }
162 
163     @Override
getScrimColor(Context context)164     protected int getScrimColor(Context context) {
165         return FINAL_SCRIM_BG_COLOR;
166     }
167 
goToFirstPage()168     private void goToFirstPage() {
169         if (mAllAppsPagedView != null) {
170             mAllAppsPagedView.snapToPageImmediately(AllAppsContainerView.AdapterHolder.MAIN);
171         }
172     }
173 
animateOpen()174     private void animateOpen() {
175         if (mIsOpen || mOpenCloseAnimator.isRunning()) {
176             return;
177         }
178         mIsOpen = true;
179         mOpenCloseAnimator.setValues(
180                 PropertyValuesHolder.ofFloat(TRANSLATION_SHIFT, TRANSLATION_SHIFT_OPENED));
181         mOpenCloseAnimator.setInterpolator(Interpolators.FAST_OUT_SLOW_IN);
182         mOpenCloseAnimator.start();
183     }
184 
185     /**
186      * Checks if user has not seen onboarding UI yet and shows it when user navigates to all apps
187      */
showEduFlowIfNeeded(Launcher launcher, @Nullable StateListener<LauncherState> oldListener)188     public static StateListener<LauncherState> showEduFlowIfNeeded(Launcher launcher,
189             @Nullable StateListener<LauncherState> oldListener) {
190         if (oldListener != null) {
191             launcher.getStateManager().removeStateListener(oldListener);
192         }
193         if (hasSeenLegacyEdu(launcher) || launcher.getSharedPrefs().getInt(KEY_WORK_EDU_STEP,
194                 WORK_EDU_NOT_STARTED) != WORK_EDU_NOT_STARTED) {
195             return null;
196         }
197 
198         StateListener<LauncherState> listener = new StateListener<LauncherState>() {
199             @Override
200             public void onStateTransitionComplete(LauncherState finalState) {
201                 if (finalState != LauncherState.ALL_APPS) return;
202                 LayoutInflater layoutInflater = LayoutInflater.from(launcher);
203                 WorkEduView v = (WorkEduView) layoutInflater.inflate(
204                         R.layout.work_profile_edu, launcher.getDragLayer(),
205                         false);
206                 v.show();
207                 v.goToFirstPage();
208                 launcher.getStateManager().removeStateListener(this);
209             }
210         };
211         launcher.getStateManager().addStateListener(listener);
212         return listener;
213     }
214 
215     /**
216      * Shows work apps edu if user had dismissed full edu flow
217      */
showWorkEduIfNeeded(Launcher launcher)218     public static void showWorkEduIfNeeded(Launcher launcher) {
219         if (hasSeenLegacyEdu(launcher) || launcher.getSharedPrefs().getInt(KEY_WORK_EDU_STEP,
220                 WORK_EDU_NOT_STARTED) != WORK_EDU_PERSONAL_APPS) {
221             return;
222         }
223         LayoutInflater layoutInflater = LayoutInflater.from(launcher);
224         WorkEduView v = (WorkEduView) layoutInflater.inflate(
225                 R.layout.work_profile_edu, launcher.getDragLayer(), false);
226         v.show();
227         v.goToWorkTab(false);
228     }
229 
hasSeenLegacyEdu(Launcher launcher)230     private static boolean hasSeenLegacyEdu(Launcher launcher) {
231         return launcher.getSharedPrefs().getBoolean(KEY_LEGACY_WORK_EDU_SEEN, false);
232     }
233 
234     @Override
onStateTransitionComplete(LauncherState finalState)235     public void onStateTransitionComplete(LauncherState finalState) {
236         close(false);
237     }
238 }
239