blob: c60853923b699cfa757cd9e90fc06c29b9c23edc [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;
Sunny Goyale396abf2020-04-06 15:11:17 -070019import static com.android.launcher3.userevent.nano.LauncherLogProto.ControlType.HYBRID_HOTSEAT_CANCELED;
Samuel Fufab641ac22020-02-10 17:58:11 -080020
Samuel Fufaaed008d2019-12-19 10:57:40 -080021import android.animation.PropertyValuesHolder;
22import android.content.Context;
23import android.content.res.Configuration;
24import android.graphics.Rect;
25import android.util.AttributeSet;
26import android.view.LayoutInflater;
27import android.view.View;
28import android.widget.Button;
Samuel Fufaa4211432020-02-25 18:47:54 -080029import android.widget.TextView;
Samuel Fufaaed008d2019-12-19 10:57:40 -080030
31import com.android.launcher3.CellLayout;
32import com.android.launcher3.DeviceProfile;
33import com.android.launcher3.Insettable;
34import com.android.launcher3.Launcher;
35import com.android.launcher3.R;
Samuel Fufa40846c62020-04-12 16:26:16 -070036import com.android.launcher3.Workspace;
Samuel Fufaaed008d2019-12-19 10:57:40 -080037import com.android.launcher3.anim.Interpolators;
Samuel Fufaa4211432020-02-25 18:47:54 -080038import com.android.launcher3.config.FeatureFlags;
Samuel Fufab641ac22020-02-10 17:58:11 -080039import com.android.launcher3.logging.UserEventDispatcher;
Sunny Goyale396abf2020-04-06 15:11:17 -070040import com.android.launcher3.model.data.WorkspaceItemInfo;
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);
Samuel Fufabe40b292020-05-18 12:50:29 -0700147 if (mLauncher.getOrientation() == Configuration.ORIENTATION_PORTRAIT) {
148 setPadding(leftInset, getPaddingTop(), rightInset, 0);
149 mHotseatWrapper.setPadding(mHotseatWrapper.getPaddingLeft(), getPaddingTop(),
150 mHotseatWrapper.getPaddingRight(), bottomInset);
151 mHotseatWrapper.getLayoutParams().height =
152 mLauncher.getDeviceProfile().hotseatBarSizePx + insets.bottom;
153
154 } else {
155 setPadding(0, getPaddingTop(), 0, 0);
156 mHotseatWrapper.setPadding(mHotseatWrapper.getPaddingLeft(), getPaddingTop(),
157 mHotseatWrapper.getPaddingRight(),
158 (int) getResources().getDimension(R.dimen.bottom_sheet_edu_padding));
159 ((TextView) findViewById(R.id.hotseat_edu_heading)).setText(
160 R.string.hotseat_edu_title_migrate_landscape);
161 ((TextView) findViewById(R.id.hotseat_edu_content)).setText(
162 R.string.hotseat_edu_message_migrate_landscape);
163 }
Samuel Fufaaed008d2019-12-19 10:57:40 -0800164 }
165
Samuel Fufa82bbdac2020-03-09 18:24:47 -0700166 private void logUserAction(boolean migrated, int pageIndex) {
Samuel Fufab641ac22020-02-10 17:58:11 -0800167 LauncherLogProto.Action action = new LauncherLogProto.Action();
168 LauncherLogProto.Target target = new LauncherLogProto.Target();
Samuel Fufa40846c62020-04-12 16:26:16 -0700169
170 int hotseatItemsCount = mLauncher.getHotseat().getShortcutsAndWidgets().getChildCount();
171 // -1 to exclude smart space
172 int workspaceItemCount = mLauncher.getWorkspace().getScreenWithId(
173 Workspace.FIRST_SCREEN_ID).getShortcutsAndWidgets().getChildCount() - 1;
174
Samuel Fufab641ac22020-02-10 17:58:11 -0800175 action.type = LauncherLogProto.Action.Type.TOUCH;
176 action.touch = LauncherLogProto.Action.Touch.TAP;
177 target.containerType = LauncherLogProto.ContainerType.TIP;
178 target.tipType = LauncherLogProto.TipType.HYBRID_HOTSEAT;
179 target.controlType = migrated ? LauncherLogProto.ControlType.HYBRID_HOTSEAT_ACCEPTED
180 : HYBRID_HOTSEAT_CANCELED;
Samuel Fufa82bbdac2020-03-09 18:24:47 -0700181 target.rank = MIGRATION_EXPERIMENT_IDENTIFIER;
Samuel Fufaa4211432020-02-25 18:47:54 -0800182 // encoding migration type on pageIndex
Samuel Fufa82bbdac2020-03-09 18:24:47 -0700183 target.pageIndex = pageIndex;
Samuel Fufa40846c62020-04-12 16:26:16 -0700184 target.cardinality = (workspaceItemCount * 1000) + hotseatItemsCount;
Samuel Fufab641ac22020-02-10 17:58:11 -0800185 LauncherLogProto.LauncherEvent event = newLauncherEvent(action, target);
186 UserEventDispatcher.newInstance(getContext()).dispatchUserEvent(event, null);
187 }
Samuel Fufaaed008d2019-12-19 10:57:40 -0800188
Samuel Fufab641ac22020-02-10 17:58:11 -0800189 private void logOnBoardingSeen() {
190 LauncherLogProto.Action action = new LauncherLogProto.Action();
191 LauncherLogProto.Target target = new LauncherLogProto.Target();
192 action.type = LauncherLogProto.Action.Type.TIP;
193 target.containerType = LauncherLogProto.ContainerType.TIP;
194 target.tipType = LauncherLogProto.TipType.HYBRID_HOTSEAT;
195 LauncherLogProto.LauncherEvent event = newLauncherEvent(action, target);
196 UserEventDispatcher.newInstance(getContext()).dispatchUserEvent(event, null);
197 }
Samuel Fufaa4211432020-02-25 18:47:54 -0800198
Samuel Fufaaed008d2019-12-19 10:57:40 -0800199 private void animateOpen() {
200 if (mIsOpen || mOpenCloseAnimator.isRunning()) {
201 return;
202 }
203 mIsOpen = true;
204 mOpenCloseAnimator.setValues(
205 PropertyValuesHolder.ofFloat(TRANSLATION_SHIFT, TRANSLATION_SHIFT_OPENED));
206 mOpenCloseAnimator.setInterpolator(Interpolators.FAST_OUT_SLOW_IN);
207 mOpenCloseAnimator.start();
208 }
209
210 @Override
211 protected void handleClose(boolean animate) {
212 handleClose(true, DEFAULT_CLOSE_DURATION);
213 }
214
215 @Override
216 protected void onConfigurationChanged(Configuration newConfig) {
217 super.onConfigurationChanged(newConfig);
218 handleClose(false);
219 }
220
Samuel Fufaa4211432020-02-25 18:47:54 -0800221 @Override
222 protected int getScrimColor(Context context) {
223 return FINAL_SCRIM_BG_COLOR;
224 }
225
226 private void populatePreview(List<WorkspaceItemInfo> predictions) {
Samuel Fufaaed008d2019-12-19 10:57:40 -0800227 for (int i = 0; i < mLauncher.getDeviceProfile().inv.numHotseatIcons; i++) {
228 WorkspaceItemInfo info = predictions.get(i);
229 PredictedAppIcon icon = PredictedAppIcon.createIcon(mSampleHotseat, info);
230 icon.setEnabled(false);
Samuel Fufa6eaf9892020-04-01 11:40:40 -0700231 icon.setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_NO);
Samuel Fufaaed008d2019-12-19 10:57:40 -0800232 icon.verifyHighRes();
233 CellLayout.LayoutParams lp = new CellLayout.LayoutParams(i, 0, 1, 1);
234 mSampleHotseat.addViewToCellLayout(icon, i, info.getViewId(), lp, true);
235 }
236 }
237
Samuel Fufaa4211432020-02-25 18:47:54 -0800238 /**
239 * Opens User education dialog with a list of suggested apps
240 */
241 public void show(List<WorkspaceItemInfo> predictions) {
242 if (getParent() != null
243 || predictions.size() < mLauncher.getDeviceProfile().inv.numHotseatIcons
244 || mHotseatEduController == null) {
245 return;
246 }
247 attachToContainer();
248 logOnBoardingSeen();
249 animateOpen();
250 populatePreview(predictions);
251 }
252
Samuel Fufaaed008d2019-12-19 10:57:40 -0800253 /**
254 * Factory method for HotseatPredictionUserEdu dialog
255 */
256 public static HotseatEduDialog getDialog(Launcher launcher) {
257 LayoutInflater layoutInflater = LayoutInflater.from(launcher);
258 return (HotseatEduDialog) layoutInflater.inflate(
259 R.layout.predicted_hotseat_edu, launcher.getDragLayer(),
260 false);
261
262 }
263}