blob: bcce168b951c8c633547a0904dc5874770608964 [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;
19import static com.android.launcher3.userevent.nano.LauncherLogProto.ControlType.HYBRID_HOTSEAT_CANCELED;
20
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 -080030import android.widget.Toast;
31
Samuel Fufa82bbdac2020-03-09 18:24:47 -070032import com.android.launcher3.ArrowTipView;
Samuel Fufaaed008d2019-12-19 10:57:40 -080033import com.android.launcher3.CellLayout;
34import com.android.launcher3.DeviceProfile;
35import com.android.launcher3.Insettable;
36import com.android.launcher3.Launcher;
37import com.android.launcher3.R;
38import com.android.launcher3.WorkspaceItemInfo;
39import com.android.launcher3.anim.Interpolators;
Samuel Fufaa4211432020-02-25 18:47:54 -080040import com.android.launcher3.config.FeatureFlags;
Samuel Fufab641ac22020-02-10 17:58:11 -080041import com.android.launcher3.logging.UserEventDispatcher;
Samuel Fufaaed008d2019-12-19 10:57:40 -080042import com.android.launcher3.uioverrides.PredictedAppIcon;
43import com.android.launcher3.userevent.nano.LauncherLogProto;
44import com.android.launcher3.views.AbstractSlideInView;
45
46import java.util.List;
47
48/**
49 * User education dialog for hybrid hotseat. Allows user to migrate hotseat items to a new page in
50 * the workspace and shows predictions on the whole hotseat
51 */
52public class HotseatEduDialog extends AbstractSlideInView implements Insettable {
53
54 private static final int DEFAULT_CLOSE_DURATION = 200;
Samuel Fufaa4211432020-02-25 18:47:54 -080055 protected static final int FINAL_SCRIM_BG_COLOR = 0x88000000;
Samuel Fufaaed008d2019-12-19 10:57:40 -080056
Samuel Fufaa4211432020-02-25 18:47:54 -080057
Samuel Fufa82bbdac2020-03-09 18:24:47 -070058 // we use this value to keep track of migration logs as we experiment with different migrations
59 private static final int MIGRATION_EXPERIMENT_IDENTIFIER = 1;
Samuel Fufaaed008d2019-12-19 10:57:40 -080060
61 private final Rect mInsets = new Rect();
62 private View mHotseatWrapper;
63 private CellLayout mSampleHotseat;
Samuel Fufaa4211432020-02-25 18:47:54 -080064 private Button mDismissBtn;
65
Samuel Fufaaed008d2019-12-19 10:57:40 -080066 public void setHotseatEduController(HotseatEduController hotseatEduController) {
67 mHotseatEduController = hotseatEduController;
68 }
69
70 private HotseatEduController mHotseatEduController;
71
72 public HotseatEduDialog(Context context, AttributeSet attr) {
73 this(context, attr, 0);
74 }
75
76 public HotseatEduDialog(Context context, AttributeSet attrs,
77 int defStyleAttr) {
78 super(context, attrs, defStyleAttr);
79 mContent = this;
80 }
81
82
83 @Override
84 protected void onFinishInflate() {
85 super.onFinishInflate();
86 mHotseatWrapper = findViewById(R.id.hotseat_wrapper);
87 mSampleHotseat = findViewById(R.id.sample_prediction);
88
89 DeviceProfile grid = mLauncher.getDeviceProfile();
90 Rect padding = grid.getHotseatLayoutPadding();
91
92 mSampleHotseat.getLayoutParams().height = grid.cellHeightPx;
93 mSampleHotseat.setGridSize(grid.inv.numHotseatIcons, 1);
94 mSampleHotseat.setPadding(padding.left, 0, padding.right, 0);
95
96 Button turnOnBtn = findViewById(R.id.turn_predictions_on);
Samuel Fufaa4211432020-02-25 18:47:54 -080097 turnOnBtn.setOnClickListener(this::onAccept);
Samuel Fufaaed008d2019-12-19 10:57:40 -080098
Samuel Fufaa4211432020-02-25 18:47:54 -080099 mDismissBtn = findViewById(R.id.no_thanks);
100 mDismissBtn.setOnClickListener(this::onDismiss);
Samuel Fufaaed008d2019-12-19 10:57:40 -0800101
Samuel Fufa82bbdac2020-03-09 18:24:47 -0700102 // update ui to reflect which migration method is going to be used
103 if (FeatureFlags.HOTSEAT_MIGRATE_TO_FOLDER.get()) {
104 ((TextView) findViewById(R.id.hotseat_edu_content)).setText(
105 R.string.hotseat_edu_message_migrate_alt);
106 }
Samuel Fufaaed008d2019-12-19 10:57:40 -0800107 }
108
Samuel Fufaa4211432020-02-25 18:47:54 -0800109 private void onAccept(View v) {
Samuel Fufa82bbdac2020-03-09 18:24:47 -0700110 mHotseatEduController.migrate();
Samuel Fufaaed008d2019-12-19 10:57:40 -0800111 handleClose(true);
Samuel Fufaaed008d2019-12-19 10:57:40 -0800112 mHotseatEduController.finishOnboarding();
Samuel Fufa82bbdac2020-03-09 18:24:47 -0700113 //TODO: pass actual page index here.
114 // Temporarily we're passing 1 for folder migration and 2 for page migration
115 logUserAction(true, FeatureFlags.HOTSEAT_MIGRATE_TO_FOLDER.get() ? 1 : 2);
116 int toastStringRes = !FeatureFlags.HOTSEAT_MIGRATE_TO_FOLDER.get()
Samuel Fufaa4211432020-02-25 18:47:54 -0800117 ? R.string.hotseat_items_migrated : R.string.hotseat_items_migrated_alt;
118 Toast.makeText(mLauncher, toastStringRes, Toast.LENGTH_LONG).show();
Samuel Fufaaed008d2019-12-19 10:57:40 -0800119 }
120
Samuel Fufaa4211432020-02-25 18:47:54 -0800121 private void onDismiss(View v) {
Samuel Fufa82bbdac2020-03-09 18:24:47 -0700122 int top = mLauncher.getHotseat().getTop();
123 new ArrowTipView(mLauncher).show(mLauncher.getString(R.string.hotseat_no_migration), top);
Samuel Fufaaed008d2019-12-19 10:57:40 -0800124 mHotseatEduController.finishOnboarding();
Samuel Fufa82bbdac2020-03-09 18:24:47 -0700125 logUserAction(false, -1);
Samuel Fufaaed008d2019-12-19 10:57:40 -0800126 handleClose(true);
127 }
128
129 @Override
130 public void logActionCommand(int command) {
131 // Since this is on-boarding popup, it is not a user controlled action.
132 }
133
134 @Override
135 public int getLogContainerType() {
136 return LauncherLogProto.ContainerType.TIP;
137 }
138
139 @Override
140 protected boolean isOfType(int type) {
141 return (type & TYPE_ON_BOARD_POPUP) != 0;
142 }
143
144 @Override
145 public void setInsets(Rect insets) {
146 int leftInset = insets.left - mInsets.left;
147 int rightInset = insets.right - mInsets.right;
148 int bottomInset = insets.bottom - mInsets.bottom;
149 mInsets.set(insets);
150 setPadding(leftInset, getPaddingTop(), rightInset, 0);
151 mHotseatWrapper.setPadding(mHotseatWrapper.getPaddingLeft(), getPaddingTop(),
152 mHotseatWrapper.getPaddingRight(), bottomInset);
153 mHotseatWrapper.getLayoutParams().height =
154 mLauncher.getDeviceProfile().hotseatBarSizePx + insets.bottom;
155 }
156
Samuel Fufa82bbdac2020-03-09 18:24:47 -0700157 private void logUserAction(boolean migrated, int pageIndex) {
Samuel Fufab641ac22020-02-10 17:58:11 -0800158 LauncherLogProto.Action action = new LauncherLogProto.Action();
159 LauncherLogProto.Target target = new LauncherLogProto.Target();
160 action.type = LauncherLogProto.Action.Type.TOUCH;
161 action.touch = LauncherLogProto.Action.Touch.TAP;
162 target.containerType = LauncherLogProto.ContainerType.TIP;
163 target.tipType = LauncherLogProto.TipType.HYBRID_HOTSEAT;
164 target.controlType = migrated ? LauncherLogProto.ControlType.HYBRID_HOTSEAT_ACCEPTED
165 : HYBRID_HOTSEAT_CANCELED;
Samuel Fufa82bbdac2020-03-09 18:24:47 -0700166 target.rank = MIGRATION_EXPERIMENT_IDENTIFIER;
Samuel Fufaa4211432020-02-25 18:47:54 -0800167 // encoding migration type on pageIndex
Samuel Fufa82bbdac2020-03-09 18:24:47 -0700168 target.pageIndex = pageIndex;
Samuel Fufab641ac22020-02-10 17:58:11 -0800169 LauncherLogProto.LauncherEvent event = newLauncherEvent(action, target);
170 UserEventDispatcher.newInstance(getContext()).dispatchUserEvent(event, null);
171 }
Samuel Fufaaed008d2019-12-19 10:57:40 -0800172
Samuel Fufab641ac22020-02-10 17:58:11 -0800173 private void logOnBoardingSeen() {
174 LauncherLogProto.Action action = new LauncherLogProto.Action();
175 LauncherLogProto.Target target = new LauncherLogProto.Target();
176 action.type = LauncherLogProto.Action.Type.TIP;
177 target.containerType = LauncherLogProto.ContainerType.TIP;
178 target.tipType = LauncherLogProto.TipType.HYBRID_HOTSEAT;
179 LauncherLogProto.LauncherEvent event = newLauncherEvent(action, target);
180 UserEventDispatcher.newInstance(getContext()).dispatchUserEvent(event, null);
181 }
Samuel Fufaa4211432020-02-25 18:47:54 -0800182
Samuel Fufaaed008d2019-12-19 10:57:40 -0800183 private void animateOpen() {
184 if (mIsOpen || mOpenCloseAnimator.isRunning()) {
185 return;
186 }
187 mIsOpen = true;
188 mOpenCloseAnimator.setValues(
189 PropertyValuesHolder.ofFloat(TRANSLATION_SHIFT, TRANSLATION_SHIFT_OPENED));
190 mOpenCloseAnimator.setInterpolator(Interpolators.FAST_OUT_SLOW_IN);
191 mOpenCloseAnimator.start();
192 }
193
194 @Override
195 protected void handleClose(boolean animate) {
196 handleClose(true, DEFAULT_CLOSE_DURATION);
197 }
198
199 @Override
200 protected void onConfigurationChanged(Configuration newConfig) {
201 super.onConfigurationChanged(newConfig);
202 handleClose(false);
203 }
204
Samuel Fufaa4211432020-02-25 18:47:54 -0800205 @Override
206 protected int getScrimColor(Context context) {
207 return FINAL_SCRIM_BG_COLOR;
208 }
209
210 private void populatePreview(List<WorkspaceItemInfo> predictions) {
Samuel Fufaaed008d2019-12-19 10:57:40 -0800211 for (int i = 0; i < mLauncher.getDeviceProfile().inv.numHotseatIcons; i++) {
212 WorkspaceItemInfo info = predictions.get(i);
213 PredictedAppIcon icon = PredictedAppIcon.createIcon(mSampleHotseat, info);
214 icon.setEnabled(false);
215 icon.verifyHighRes();
216 CellLayout.LayoutParams lp = new CellLayout.LayoutParams(i, 0, 1, 1);
217 mSampleHotseat.addViewToCellLayout(icon, i, info.getViewId(), lp, true);
218 }
219 }
220
Samuel Fufaa4211432020-02-25 18:47:54 -0800221 /**
222 * Opens User education dialog with a list of suggested apps
223 */
224 public void show(List<WorkspaceItemInfo> predictions) {
225 if (getParent() != null
226 || predictions.size() < mLauncher.getDeviceProfile().inv.numHotseatIcons
227 || mHotseatEduController == null) {
228 return;
229 }
230 attachToContainer();
231 logOnBoardingSeen();
232 animateOpen();
233 populatePreview(predictions);
234 }
235
Samuel Fufaaed008d2019-12-19 10:57:40 -0800236 /**
237 * Factory method for HotseatPredictionUserEdu dialog
238 */
239 public static HotseatEduDialog getDialog(Launcher launcher) {
240 LayoutInflater layoutInflater = LayoutInflater.from(launcher);
241 return (HotseatEduDialog) layoutInflater.inflate(
242 R.layout.predicted_hotseat_edu, launcher.getDragLayer(),
243 false);
244
245 }
246}