blob: b8c4a2147f4a8a393367c425a37ad6a153416e0a [file] [log] [blame]
Samuel Fufaaed008d2019-12-19 10:57:40 -08001/*
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 */
16package com.android.launcher3.hybridhotseat;
17
Samuel Fufab641ac22020-02-10 17:58:11 -080018import static com.android.launcher3.logging.LoggerUtils.newLauncherEvent;
Samuel Fufaaa2aff52020-03-24 18:34:12 -070019import static com.android.launcher3.userevent.nano.LauncherLogProto.ControlType
20 .HYBRID_HOTSEAT_CANCELED;
Samuel Fufab641ac22020-02-10 17:58:11 -080021
Samuel Fufaaed008d2019-12-19 10:57:40 -080022import android.animation.PropertyValuesHolder;
23import android.content.Context;
24import android.content.res.Configuration;
25import android.graphics.Rect;
26import android.util.AttributeSet;
27import android.view.LayoutInflater;
28import android.view.View;
29import android.widget.Button;
Samuel Fufaa4211432020-02-25 18:47:54 -080030import android.widget.TextView;
Samuel Fufaaed008d2019-12-19 10:57:40 -080031
32import com.android.launcher3.CellLayout;
33import com.android.launcher3.DeviceProfile;
34import com.android.launcher3.Insettable;
35import com.android.launcher3.Launcher;
36import com.android.launcher3.R;
37import com.android.launcher3.WorkspaceItemInfo;
38import com.android.launcher3.anim.Interpolators;
Samuel Fufaa4211432020-02-25 18:47:54 -080039import com.android.launcher3.config.FeatureFlags;
Samuel Fufab641ac22020-02-10 17:58:11 -080040import com.android.launcher3.logging.UserEventDispatcher;
Samuel Fufaaed008d2019-12-19 10:57:40 -080041import com.android.launcher3.uioverrides.PredictedAppIcon;
42import com.android.launcher3.userevent.nano.LauncherLogProto;
43import com.android.launcher3.views.AbstractSlideInView;
44
45import java.util.List;
46
47/**
48 * User education dialog for hybrid hotseat. Allows user to migrate hotseat items to a new page in
49 * the workspace and shows predictions on the whole hotseat
50 */
51public class HotseatEduDialog extends AbstractSlideInView implements Insettable {
52
53 private static final int DEFAULT_CLOSE_DURATION = 200;
Samuel Fufaa4211432020-02-25 18:47:54 -080054 protected static final int FINAL_SCRIM_BG_COLOR = 0x88000000;
Samuel Fufaaed008d2019-12-19 10:57:40 -080055
Samuel Fufaa4211432020-02-25 18:47:54 -080056
Samuel Fufa82bbdac2020-03-09 18:24:47 -070057 // we use this value to keep track of migration logs as we experiment with different migrations
58 private static final int MIGRATION_EXPERIMENT_IDENTIFIER = 1;
Samuel Fufaaed008d2019-12-19 10:57:40 -080059
60 private final Rect mInsets = new Rect();
61 private View mHotseatWrapper;
62 private CellLayout mSampleHotseat;
Samuel Fufaa4211432020-02-25 18:47:54 -080063 private Button mDismissBtn;
64
Samuel Fufaaed008d2019-12-19 10:57:40 -080065 public void setHotseatEduController(HotseatEduController hotseatEduController) {
66 mHotseatEduController = hotseatEduController;
67 }
68
69 private HotseatEduController mHotseatEduController;
70
71 public HotseatEduDialog(Context context, AttributeSet attr) {
72 this(context, attr, 0);
73 }
74
75 public HotseatEduDialog(Context context, AttributeSet attrs,
76 int defStyleAttr) {
77 super(context, attrs, defStyleAttr);
78 mContent = this;
79 }
80
81
82 @Override
83 protected void onFinishInflate() {
84 super.onFinishInflate();
85 mHotseatWrapper = findViewById(R.id.hotseat_wrapper);
86 mSampleHotseat = findViewById(R.id.sample_prediction);
87
88 DeviceProfile grid = mLauncher.getDeviceProfile();
89 Rect padding = grid.getHotseatLayoutPadding();
90
91 mSampleHotseat.getLayoutParams().height = grid.cellHeightPx;
92 mSampleHotseat.setGridSize(grid.inv.numHotseatIcons, 1);
93 mSampleHotseat.setPadding(padding.left, 0, padding.right, 0);
94
95 Button turnOnBtn = findViewById(R.id.turn_predictions_on);
Samuel Fufaa4211432020-02-25 18:47:54 -080096 turnOnBtn.setOnClickListener(this::onAccept);
Samuel Fufaaed008d2019-12-19 10:57:40 -080097
Samuel Fufaa4211432020-02-25 18:47:54 -080098 mDismissBtn = findViewById(R.id.no_thanks);
99 mDismissBtn.setOnClickListener(this::onDismiss);
Samuel Fufaaed008d2019-12-19 10:57:40 -0800100
Samuel Fufa82bbdac2020-03-09 18:24:47 -0700101 // update ui to reflect which migration method is going to be used
102 if (FeatureFlags.HOTSEAT_MIGRATE_TO_FOLDER.get()) {
103 ((TextView) findViewById(R.id.hotseat_edu_content)).setText(
104 R.string.hotseat_edu_message_migrate_alt);
105 }
Samuel Fufaaed008d2019-12-19 10:57:40 -0800106 }
107
Samuel Fufaa4211432020-02-25 18:47:54 -0800108 private void onAccept(View v) {
Samuel Fufa82bbdac2020-03-09 18:24:47 -0700109 mHotseatEduController.migrate();
Samuel Fufaaed008d2019-12-19 10:57:40 -0800110 handleClose(true);
Samuel Fufaaa2aff52020-03-24 18:34:12 -0700111
112 mHotseatEduController.moveHotseatItems();
Samuel Fufaaed008d2019-12-19 10:57:40 -0800113 mHotseatEduController.finishOnboarding();
Samuel Fufa82bbdac2020-03-09 18:24:47 -0700114 //TODO: pass actual page index here.
115 // Temporarily we're passing 1 for folder migration and 2 for page migration
116 logUserAction(true, FeatureFlags.HOTSEAT_MIGRATE_TO_FOLDER.get() ? 1 : 2);
Samuel Fufaaed008d2019-12-19 10:57:40 -0800117 }
118
Samuel Fufaa4211432020-02-25 18:47:54 -0800119 private void onDismiss(View v) {
Samuel Fufaaa2aff52020-03-24 18:34:12 -0700120 mHotseatEduController.showDimissTip();
Samuel Fufaaed008d2019-12-19 10:57:40 -0800121 mHotseatEduController.finishOnboarding();
Samuel Fufa82bbdac2020-03-09 18:24:47 -0700122 logUserAction(false, -1);
Samuel Fufaaed008d2019-12-19 10:57:40 -0800123 handleClose(true);
124 }
125
126 @Override
127 public void logActionCommand(int command) {
128 // Since this is on-boarding popup, it is not a user controlled action.
129 }
130
131 @Override
132 public int getLogContainerType() {
133 return LauncherLogProto.ContainerType.TIP;
134 }
135
136 @Override
137 protected boolean isOfType(int type) {
138 return (type & TYPE_ON_BOARD_POPUP) != 0;
139 }
140
141 @Override
142 public void setInsets(Rect insets) {
143 int leftInset = insets.left - mInsets.left;
144 int rightInset = insets.right - mInsets.right;
145 int bottomInset = insets.bottom - mInsets.bottom;
146 mInsets.set(insets);
147 setPadding(leftInset, getPaddingTop(), rightInset, 0);
148 mHotseatWrapper.setPadding(mHotseatWrapper.getPaddingLeft(), getPaddingTop(),
149 mHotseatWrapper.getPaddingRight(), bottomInset);
150 mHotseatWrapper.getLayoutParams().height =
151 mLauncher.getDeviceProfile().hotseatBarSizePx + insets.bottom;
152 }
153
Samuel Fufa82bbdac2020-03-09 18:24:47 -0700154 private void logUserAction(boolean migrated, int pageIndex) {
Samuel Fufab641ac22020-02-10 17:58:11 -0800155 LauncherLogProto.Action action = new LauncherLogProto.Action();
156 LauncherLogProto.Target target = new LauncherLogProto.Target();
157 action.type = LauncherLogProto.Action.Type.TOUCH;
158 action.touch = LauncherLogProto.Action.Touch.TAP;
159 target.containerType = LauncherLogProto.ContainerType.TIP;
160 target.tipType = LauncherLogProto.TipType.HYBRID_HOTSEAT;
161 target.controlType = migrated ? LauncherLogProto.ControlType.HYBRID_HOTSEAT_ACCEPTED
162 : HYBRID_HOTSEAT_CANCELED;
Samuel Fufa82bbdac2020-03-09 18:24:47 -0700163 target.rank = MIGRATION_EXPERIMENT_IDENTIFIER;
Samuel Fufaa4211432020-02-25 18:47:54 -0800164 // encoding migration type on pageIndex
Samuel Fufa82bbdac2020-03-09 18:24:47 -0700165 target.pageIndex = pageIndex;
Samuel Fufa6b13c842020-03-25 13:11:38 -0700166 target.cardinality = HotseatPredictionController.MAX_ITEMS_FOR_MIGRATION;
Samuel Fufab641ac22020-02-10 17:58:11 -0800167 LauncherLogProto.LauncherEvent event = newLauncherEvent(action, target);
168 UserEventDispatcher.newInstance(getContext()).dispatchUserEvent(event, null);
169 }
Samuel Fufaaed008d2019-12-19 10:57:40 -0800170
Samuel Fufab641ac22020-02-10 17:58:11 -0800171 private void logOnBoardingSeen() {
172 LauncherLogProto.Action action = new LauncherLogProto.Action();
173 LauncherLogProto.Target target = new LauncherLogProto.Target();
174 action.type = LauncherLogProto.Action.Type.TIP;
175 target.containerType = LauncherLogProto.ContainerType.TIP;
176 target.tipType = LauncherLogProto.TipType.HYBRID_HOTSEAT;
177 LauncherLogProto.LauncherEvent event = newLauncherEvent(action, target);
178 UserEventDispatcher.newInstance(getContext()).dispatchUserEvent(event, null);
179 }
Samuel Fufaa4211432020-02-25 18:47:54 -0800180
Samuel Fufaaed008d2019-12-19 10:57:40 -0800181 private void animateOpen() {
182 if (mIsOpen || mOpenCloseAnimator.isRunning()) {
183 return;
184 }
185 mIsOpen = true;
186 mOpenCloseAnimator.setValues(
187 PropertyValuesHolder.ofFloat(TRANSLATION_SHIFT, TRANSLATION_SHIFT_OPENED));
188 mOpenCloseAnimator.setInterpolator(Interpolators.FAST_OUT_SLOW_IN);
189 mOpenCloseAnimator.start();
190 }
191
192 @Override
193 protected void handleClose(boolean animate) {
194 handleClose(true, DEFAULT_CLOSE_DURATION);
195 }
196
197 @Override
198 protected void onConfigurationChanged(Configuration newConfig) {
199 super.onConfigurationChanged(newConfig);
200 handleClose(false);
201 }
202
Samuel Fufaa4211432020-02-25 18:47:54 -0800203 @Override
204 protected int getScrimColor(Context context) {
205 return FINAL_SCRIM_BG_COLOR;
206 }
207
208 private void populatePreview(List<WorkspaceItemInfo> predictions) {
Samuel Fufaaed008d2019-12-19 10:57:40 -0800209 for (int i = 0; i < mLauncher.getDeviceProfile().inv.numHotseatIcons; i++) {
210 WorkspaceItemInfo info = predictions.get(i);
211 PredictedAppIcon icon = PredictedAppIcon.createIcon(mSampleHotseat, info);
212 icon.setEnabled(false);
Samuel Fufa6eaf9892020-04-01 11:40:40 -0700213 icon.setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_NO);
Samuel Fufaaed008d2019-12-19 10:57:40 -0800214 icon.verifyHighRes();
215 CellLayout.LayoutParams lp = new CellLayout.LayoutParams(i, 0, 1, 1);
216 mSampleHotseat.addViewToCellLayout(icon, i, info.getViewId(), lp, true);
217 }
218 }
219
Samuel Fufaa4211432020-02-25 18:47:54 -0800220 /**
221 * Opens User education dialog with a list of suggested apps
222 */
223 public void show(List<WorkspaceItemInfo> predictions) {
224 if (getParent() != null
225 || predictions.size() < mLauncher.getDeviceProfile().inv.numHotseatIcons
226 || mHotseatEduController == null) {
227 return;
228 }
229 attachToContainer();
230 logOnBoardingSeen();
231 animateOpen();
232 populatePreview(predictions);
233 }
234
Samuel Fufaaed008d2019-12-19 10:57:40 -0800235 /**
236 * Factory method for HotseatPredictionUserEdu dialog
237 */
238 public static HotseatEduDialog getDialog(Launcher launcher) {
239 LayoutInflater layoutInflater = LayoutInflater.from(launcher);
240 return (HotseatEduDialog) layoutInflater.inflate(
241 R.layout.predicted_hotseat_edu, launcher.getDragLayer(),
242 false);
243
244 }
245}