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.hybridhotseat;
17 
18 import static com.android.launcher3.logging.StatsLogManager.LauncherEvent
19         .LAUNCHER_HOTSEAT_EDU_ACCEPT;
20 import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_HOTSEAT_EDU_DENY;
21 import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_HOTSEAT_EDU_SEEN;
22 
23 import android.animation.PropertyValuesHolder;
24 import android.content.Context;
25 import android.content.res.Configuration;
26 import android.graphics.Rect;
27 import android.util.AttributeSet;
28 import android.view.LayoutInflater;
29 import android.view.View;
30 import android.widget.Button;
31 import android.widget.LinearLayout;
32 import android.widget.TextView;
33 
34 import com.android.launcher3.AbstractFloatingView;
35 import com.android.launcher3.CellLayout;
36 import com.android.launcher3.DeviceProfile;
37 import com.android.launcher3.Insettable;
38 import com.android.launcher3.InvariantDeviceProfile;
39 import com.android.launcher3.Launcher;
40 import com.android.launcher3.R;
41 import com.android.launcher3.anim.Interpolators;
42 import com.android.launcher3.config.FeatureFlags;
43 import com.android.launcher3.model.data.WorkspaceItemInfo;
44 import com.android.launcher3.uioverrides.ApiWrapper;
45 import com.android.launcher3.uioverrides.PredictedAppIcon;
46 import com.android.launcher3.views.AbstractSlideInView;
47 
48 import java.util.List;
49 
50 /**
51  * User education dialog for hybrid hotseat. Allows user to migrate hotseat items to a new page in
52  * the workspace and shows predictions on the whole hotseat
53  */
54 public class HotseatEduDialog extends AbstractSlideInView<Launcher> implements Insettable {
55 
56     private static final int DEFAULT_CLOSE_DURATION = 200;
57     protected static final int FINAL_SCRIM_BG_COLOR = 0x88000000;
58 
59     // we use this value to keep track of migration logs as we experiment with different migrations
60     private static final int MIGRATION_EXPERIMENT_IDENTIFIER = 1;
61 
62     private final Rect mInsets = new Rect();
63     private View mHotseatWrapper;
64     private CellLayout mSampleHotseat;
65     private Button mDismissBtn;
66 
setHotseatEduController(HotseatEduController hotseatEduController)67     public void setHotseatEduController(HotseatEduController hotseatEduController) {
68         mHotseatEduController = hotseatEduController;
69     }
70 
71     private HotseatEduController mHotseatEduController;
72 
HotseatEduDialog(Context context, AttributeSet attr)73     public HotseatEduDialog(Context context, AttributeSet attr) {
74         this(context, attr, 0);
75     }
76 
HotseatEduDialog(Context context, AttributeSet attrs, int defStyleAttr)77     public HotseatEduDialog(Context context, AttributeSet attrs,
78             int defStyleAttr) {
79         super(context, attrs, defStyleAttr);
80         mContent = this;
81     }
82 
83     @Override
onLayout(boolean changed, int l, int t, int r, int b)84     protected void onLayout(boolean changed, int l, int t, int r, int b) {
85         super.onLayout(changed, l, t, r, b);
86         setTranslationShift(TRANSLATION_SHIFT_CLOSED);
87     }
88 
89     @Override
onFinishInflate()90     protected void onFinishInflate() {
91         super.onFinishInflate();
92         mHotseatWrapper = findViewById(R.id.hotseat_wrapper);
93         mSampleHotseat = findViewById(R.id.sample_prediction);
94 
95         Context context = getContext();
96         DeviceProfile grid = mActivityContext.getDeviceProfile();
97         Rect padding = grid.getHotseatLayoutPadding(context);
98 
99         mSampleHotseat.getLayoutParams().height = grid.cellHeightPx;
100         mSampleHotseat.setGridSize(grid.numShownHotseatIcons, 1);
101         mSampleHotseat.setPadding(padding.left, 0, padding.right, 0);
102 
103         Button turnOnBtn = findViewById(R.id.turn_predictions_on);
104         turnOnBtn.setOnClickListener(this::onAccept);
105 
106         mDismissBtn = findViewById(R.id.no_thanks);
107         mDismissBtn.setOnClickListener(this::onDismiss);
108 
109         LinearLayout buttonContainer = findViewById(R.id.button_container);
110         int adjustedMarginEnd = ApiWrapper.getHotseatEndOffset(context)
111                 - buttonContainer.getPaddingEnd();
112         if (InvariantDeviceProfile.INSTANCE.get(context)
113                 .getDeviceProfile(context).isTaskbarPresent && adjustedMarginEnd > 0) {
114             ((LinearLayout.LayoutParams) buttonContainer.getLayoutParams()).setMarginEnd(
115                     adjustedMarginEnd);
116         }
117 
118         // update ui to reflect which migration method is going to be used
119         if (FeatureFlags.HOTSEAT_MIGRATE_TO_FOLDER.get()) {
120             ((TextView) findViewById(R.id.hotseat_edu_content)).setText(
121                     R.string.hotseat_edu_message_migrate_alt);
122         }
123     }
124 
onAccept(View v)125     private void onAccept(View v) {
126         mHotseatEduController.migrate();
127         handleClose(true);
128 
129         mHotseatEduController.moveHotseatItems();
130         mHotseatEduController.finishOnboarding();
131         mActivityContext.getStatsLogManager().logger().log(LAUNCHER_HOTSEAT_EDU_ACCEPT);
132     }
133 
onDismiss(View v)134     private void onDismiss(View v) {
135         mHotseatEduController.showDimissTip();
136         mHotseatEduController.finishOnboarding();
137         mActivityContext.getStatsLogManager().logger().log(LAUNCHER_HOTSEAT_EDU_DENY);
138         handleClose(true);
139     }
140 
141     @Override
isOfType(int type)142     protected boolean isOfType(int type) {
143         return (type & TYPE_ON_BOARD_POPUP) != 0;
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         if (mActivityContext.getOrientation() == Configuration.ORIENTATION_PORTRAIT) {
153             setPadding(leftInset, getPaddingTop(), rightInset, 0);
154             mHotseatWrapper.setPadding(mHotseatWrapper.getPaddingLeft(), getPaddingTop(),
155                     mHotseatWrapper.getPaddingRight(), bottomInset);
156             mHotseatWrapper.getLayoutParams().height =
157                     mActivityContext.getDeviceProfile().hotseatBarSizePx + insets.bottom;
158 
159         } else {
160             setPadding(0, getPaddingTop(), 0, 0);
161             mHotseatWrapper.setPadding(mHotseatWrapper.getPaddingLeft(), getPaddingTop(),
162                     mHotseatWrapper.getPaddingRight(),
163                     (int) getResources().getDimension(R.dimen.bottom_sheet_edu_padding));
164             ((TextView) findViewById(R.id.hotseat_edu_heading)).setText(
165                     R.string.hotseat_edu_title_migrate_landscape);
166             ((TextView) findViewById(R.id.hotseat_edu_content)).setText(
167                     R.string.hotseat_edu_message_migrate_landscape);
168         }
169     }
170 
animateOpen()171     private void animateOpen() {
172         if (mIsOpen || mOpenCloseAnimator.isRunning()) {
173             return;
174         }
175         mIsOpen = true;
176         mOpenCloseAnimator.setValues(
177                 PropertyValuesHolder.ofFloat(TRANSLATION_SHIFT, TRANSLATION_SHIFT_OPENED));
178         mOpenCloseAnimator.setInterpolator(Interpolators.FAST_OUT_SLOW_IN);
179         mOpenCloseAnimator.start();
180     }
181 
182     @Override
handleClose(boolean animate)183     protected void handleClose(boolean animate) {
184         handleClose(true, DEFAULT_CLOSE_DURATION);
185     }
186 
187     @Override
onConfigurationChanged(Configuration newConfig)188     protected void onConfigurationChanged(Configuration newConfig) {
189         super.onConfigurationChanged(newConfig);
190         handleClose(false);
191     }
192 
193     @Override
getScrimColor(Context context)194     protected int getScrimColor(Context context) {
195         return FINAL_SCRIM_BG_COLOR;
196     }
197 
populatePreview(List<WorkspaceItemInfo> predictions)198     private void populatePreview(List<WorkspaceItemInfo> predictions) {
199         for (int i = 0; i < mActivityContext.getDeviceProfile().numShownHotseatIcons; i++) {
200             WorkspaceItemInfo info = predictions.get(i);
201             PredictedAppIcon icon = PredictedAppIcon.createIcon(mSampleHotseat, info);
202             icon.setEnabled(false);
203             icon.setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_NO);
204             icon.verifyHighRes();
205             CellLayout.LayoutParams lp = new CellLayout.LayoutParams(i, 0, 1, 1);
206             mSampleHotseat.addViewToCellLayout(icon, i, info.getViewId(), lp, true);
207         }
208     }
209 
210     /**
211      * Opens User education dialog with a list of suggested apps
212      */
show(List<WorkspaceItemInfo> predictions)213     public void show(List<WorkspaceItemInfo> predictions) {
214         if (getParent() != null
215                 || predictions.size() < mActivityContext.getDeviceProfile().numShownHotseatIcons
216                 || mHotseatEduController == null) {
217             return;
218         }
219         AbstractFloatingView.closeAllOpenViews(mActivityContext);
220         attachToContainer();
221         animateOpen();
222         populatePreview(predictions);
223         mActivityContext.getStatsLogManager().logger().log(LAUNCHER_HOTSEAT_EDU_SEEN);
224     }
225 
226     /**
227      * Factory method for HotseatPredictionUserEdu dialog
228      */
getDialog(Launcher launcher)229     public static HotseatEduDialog getDialog(Launcher launcher) {
230         LayoutInflater layoutInflater = LayoutInflater.from(launcher);
231         return (HotseatEduDialog) layoutInflater.inflate(
232                 R.layout.predicted_hotseat_edu, launcher.getDragLayer(),
233                 false);
234 
235     }
236 }
237