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