blob: 4213740c1340fb524befbccf10037d1ff9241bf1 [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 Fufaaed008d2019-12-19 10:57:40 -080036import com.android.launcher3.anim.Interpolators;
Samuel Fufaa4211432020-02-25 18:47:54 -080037import com.android.launcher3.config.FeatureFlags;
Samuel Fufab641ac22020-02-10 17:58:11 -080038import com.android.launcher3.logging.UserEventDispatcher;
Sunny Goyale396abf2020-04-06 15:11:17 -070039import com.android.launcher3.model.data.WorkspaceItemInfo;
Samuel Fufaaed008d2019-12-19 10:57:40 -080040import com.android.launcher3.uioverrides.PredictedAppIcon;
41import com.android.launcher3.userevent.nano.LauncherLogProto;
42import com.android.launcher3.views.AbstractSlideInView;
43
44import java.util.List;
45
46/**
47 * User education dialog for hybrid hotseat. Allows user to migrate hotseat items to a new page in
48 * the workspace and shows predictions on the whole hotseat
49 */
50public class HotseatEduDialog extends AbstractSlideInView implements Insettable {
51
52 private static final int DEFAULT_CLOSE_DURATION = 200;
Samuel Fufaa4211432020-02-25 18:47:54 -080053 protected static final int FINAL_SCRIM_BG_COLOR = 0x88000000;
Samuel Fufaaed008d2019-12-19 10:57:40 -080054
Samuel Fufaa4211432020-02-25 18:47:54 -080055
Samuel Fufa82bbdac2020-03-09 18:24:47 -070056 // we use this value to keep track of migration logs as we experiment with different migrations
57 private static final int MIGRATION_EXPERIMENT_IDENTIFIER = 1;
Samuel Fufaaed008d2019-12-19 10:57:40 -080058
59 private final Rect mInsets = new Rect();
60 private View mHotseatWrapper;
61 private CellLayout mSampleHotseat;
Samuel Fufaa4211432020-02-25 18:47:54 -080062 private Button mDismissBtn;
63
Samuel Fufaaed008d2019-12-19 10:57:40 -080064 public void setHotseatEduController(HotseatEduController hotseatEduController) {
65 mHotseatEduController = hotseatEduController;
66 }
67
68 private HotseatEduController mHotseatEduController;
69
70 public HotseatEduDialog(Context context, AttributeSet attr) {
71 this(context, attr, 0);
72 }
73
74 public HotseatEduDialog(Context context, AttributeSet attrs,
75 int defStyleAttr) {
76 super(context, attrs, defStyleAttr);
77 mContent = this;
78 }
79
80
81 @Override
82 protected void onFinishInflate() {
83 super.onFinishInflate();
84 mHotseatWrapper = findViewById(R.id.hotseat_wrapper);
85 mSampleHotseat = findViewById(R.id.sample_prediction);
86
87 DeviceProfile grid = mLauncher.getDeviceProfile();
88 Rect padding = grid.getHotseatLayoutPadding();
89
90 mSampleHotseat.getLayoutParams().height = grid.cellHeightPx;
91 mSampleHotseat.setGridSize(grid.inv.numHotseatIcons, 1);
92 mSampleHotseat.setPadding(padding.left, 0, padding.right, 0);
93
94 Button turnOnBtn = findViewById(R.id.turn_predictions_on);
Samuel Fufaa4211432020-02-25 18:47:54 -080095 turnOnBtn.setOnClickListener(this::onAccept);
Samuel Fufaaed008d2019-12-19 10:57:40 -080096
Samuel Fufaa4211432020-02-25 18:47:54 -080097 mDismissBtn = findViewById(R.id.no_thanks);
98 mDismissBtn.setOnClickListener(this::onDismiss);
Samuel Fufaaed008d2019-12-19 10:57:40 -080099
Samuel Fufa82bbdac2020-03-09 18:24:47 -0700100 // update ui to reflect which migration method is going to be used
101 if (FeatureFlags.HOTSEAT_MIGRATE_TO_FOLDER.get()) {
102 ((TextView) findViewById(R.id.hotseat_edu_content)).setText(
103 R.string.hotseat_edu_message_migrate_alt);
104 }
Samuel Fufaaed008d2019-12-19 10:57:40 -0800105 }
106
Samuel Fufaa4211432020-02-25 18:47:54 -0800107 private void onAccept(View v) {
Samuel Fufa82bbdac2020-03-09 18:24:47 -0700108 mHotseatEduController.migrate();
Samuel Fufaaed008d2019-12-19 10:57:40 -0800109 handleClose(true);
Samuel Fufaaa2aff52020-03-24 18:34:12 -0700110
111 mHotseatEduController.moveHotseatItems();
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);
Samuel Fufaaed008d2019-12-19 10:57:40 -0800116 }
117
Samuel Fufaa4211432020-02-25 18:47:54 -0800118 private void onDismiss(View v) {
Samuel Fufaaa2aff52020-03-24 18:34:12 -0700119 mHotseatEduController.showDimissTip();
Samuel Fufaaed008d2019-12-19 10:57:40 -0800120 mHotseatEduController.finishOnboarding();
Samuel Fufa82bbdac2020-03-09 18:24:47 -0700121 logUserAction(false, -1);
Samuel Fufaaed008d2019-12-19 10:57:40 -0800122 handleClose(true);
123 }
124
125 @Override
126 public void logActionCommand(int command) {
127 // Since this is on-boarding popup, it is not a user controlled action.
128 }
129
130 @Override
131 public int getLogContainerType() {
132 return LauncherLogProto.ContainerType.TIP;
133 }
134
135 @Override
136 protected boolean isOfType(int type) {
137 return (type & TYPE_ON_BOARD_POPUP) != 0;
138 }
139
140 @Override
141 public void setInsets(Rect insets) {
142 int leftInset = insets.left - mInsets.left;
143 int rightInset = insets.right - mInsets.right;
144 int bottomInset = insets.bottom - mInsets.bottom;
145 mInsets.set(insets);
146 setPadding(leftInset, getPaddingTop(), rightInset, 0);
147 mHotseatWrapper.setPadding(mHotseatWrapper.getPaddingLeft(), getPaddingTop(),
148 mHotseatWrapper.getPaddingRight(), bottomInset);
149 mHotseatWrapper.getLayoutParams().height =
150 mLauncher.getDeviceProfile().hotseatBarSizePx + insets.bottom;
151 }
152
Samuel Fufa82bbdac2020-03-09 18:24:47 -0700153 private void logUserAction(boolean migrated, int pageIndex) {
Samuel Fufab641ac22020-02-10 17:58:11 -0800154 LauncherLogProto.Action action = new LauncherLogProto.Action();
155 LauncherLogProto.Target target = new LauncherLogProto.Target();
156 action.type = LauncherLogProto.Action.Type.TOUCH;
157 action.touch = LauncherLogProto.Action.Touch.TAP;
158 target.containerType = LauncherLogProto.ContainerType.TIP;
159 target.tipType = LauncherLogProto.TipType.HYBRID_HOTSEAT;
160 target.controlType = migrated ? LauncherLogProto.ControlType.HYBRID_HOTSEAT_ACCEPTED
161 : HYBRID_HOTSEAT_CANCELED;
Samuel Fufa82bbdac2020-03-09 18:24:47 -0700162 target.rank = MIGRATION_EXPERIMENT_IDENTIFIER;
Samuel Fufaa4211432020-02-25 18:47:54 -0800163 // encoding migration type on pageIndex
Samuel Fufa82bbdac2020-03-09 18:24:47 -0700164 target.pageIndex = pageIndex;
Samuel Fufa6b13c842020-03-25 13:11:38 -0700165 target.cardinality = HotseatPredictionController.MAX_ITEMS_FOR_MIGRATION;
Samuel Fufab641ac22020-02-10 17:58:11 -0800166 LauncherLogProto.LauncherEvent event = newLauncherEvent(action, target);
167 UserEventDispatcher.newInstance(getContext()).dispatchUserEvent(event, null);
168 }
Samuel Fufaaed008d2019-12-19 10:57:40 -0800169
Samuel Fufab641ac22020-02-10 17:58:11 -0800170 private void logOnBoardingSeen() {
171 LauncherLogProto.Action action = new LauncherLogProto.Action();
172 LauncherLogProto.Target target = new LauncherLogProto.Target();
173 action.type = LauncherLogProto.Action.Type.TIP;
174 target.containerType = LauncherLogProto.ContainerType.TIP;
175 target.tipType = LauncherLogProto.TipType.HYBRID_HOTSEAT;
176 LauncherLogProto.LauncherEvent event = newLauncherEvent(action, target);
177 UserEventDispatcher.newInstance(getContext()).dispatchUserEvent(event, null);
178 }
Samuel Fufaa4211432020-02-25 18:47:54 -0800179
Samuel Fufaaed008d2019-12-19 10:57:40 -0800180 private void animateOpen() {
181 if (mIsOpen || mOpenCloseAnimator.isRunning()) {
182 return;
183 }
184 mIsOpen = true;
185 mOpenCloseAnimator.setValues(
186 PropertyValuesHolder.ofFloat(TRANSLATION_SHIFT, TRANSLATION_SHIFT_OPENED));
187 mOpenCloseAnimator.setInterpolator(Interpolators.FAST_OUT_SLOW_IN);
188 mOpenCloseAnimator.start();
189 }
190
191 @Override
192 protected void handleClose(boolean animate) {
193 handleClose(true, DEFAULT_CLOSE_DURATION);
194 }
195
196 @Override
197 protected void onConfigurationChanged(Configuration newConfig) {
198 super.onConfigurationChanged(newConfig);
199 handleClose(false);
200 }
201
Samuel Fufaa4211432020-02-25 18:47:54 -0800202 @Override
203 protected int getScrimColor(Context context) {
204 return FINAL_SCRIM_BG_COLOR;
205 }
206
207 private void populatePreview(List<WorkspaceItemInfo> predictions) {
Samuel Fufaaed008d2019-12-19 10:57:40 -0800208 for (int i = 0; i < mLauncher.getDeviceProfile().inv.numHotseatIcons; i++) {
209 WorkspaceItemInfo info = predictions.get(i);
210 PredictedAppIcon icon = PredictedAppIcon.createIcon(mSampleHotseat, info);
211 icon.setEnabled(false);
Samuel Fufa6eaf9892020-04-01 11:40:40 -0700212 icon.setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_NO);
Samuel Fufaaed008d2019-12-19 10:57:40 -0800213 icon.verifyHighRes();
214 CellLayout.LayoutParams lp = new CellLayout.LayoutParams(i, 0, 1, 1);
215 mSampleHotseat.addViewToCellLayout(icon, i, info.getViewId(), lp, true);
216 }
217 }
218
Samuel Fufaa4211432020-02-25 18:47:54 -0800219 /**
220 * Opens User education dialog with a list of suggested apps
221 */
222 public void show(List<WorkspaceItemInfo> predictions) {
223 if (getParent() != null
224 || predictions.size() < mLauncher.getDeviceProfile().inv.numHotseatIcons
225 || mHotseatEduController == null) {
226 return;
227 }
228 attachToContainer();
229 logOnBoardingSeen();
230 animateOpen();
231 populatePreview(predictions);
232 }
233
Samuel Fufaaed008d2019-12-19 10:57:40 -0800234 /**
235 * Factory method for HotseatPredictionUserEdu dialog
236 */
237 public static HotseatEduDialog getDialog(Launcher launcher) {
238 LayoutInflater layoutInflater = LayoutInflater.from(launcher);
239 return (HotseatEduDialog) layoutInflater.inflate(
240 R.layout.predicted_hotseat_edu, launcher.getDragLayer(),
241 false);
242
243 }
244}