blob: 538b7f3952ea4e568cebb21c072672c94cbd50f5 [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
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;
Samuel Fufaa4211432020-02-25 18:47:54 -080038import com.android.launcher3.WorkspaceLayoutManager;
Samuel Fufaaed008d2019-12-19 10:57:40 -080039import 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 // We don't migrate if user has more than SAME_PAGE_MAX_ROWS rows of item in their screen
58 private static final int SAME_PAGE_MAX_ROWS = 2;
59
60 private static final int MIGRATE_SAME_PAGE = 0;
61 private static final int MIGRATE_NEW_PAGE = 1;
62 private static final int MIGRATE_NO_MIGRATE = 2;
63
Samuel Fufaaed008d2019-12-19 10:57:40 -080064
65 private final Rect mInsets = new Rect();
66 private View mHotseatWrapper;
67 private CellLayout mSampleHotseat;
Samuel Fufaa4211432020-02-25 18:47:54 -080068 private TextView mEduHeading;
69 private TextView mEduContent;
70 private Button mDismissBtn;
71
72 private int mMigrationMode = MIGRATE_SAME_PAGE;
Samuel Fufaaed008d2019-12-19 10:57:40 -080073
74 public void setHotseatEduController(HotseatEduController hotseatEduController) {
75 mHotseatEduController = hotseatEduController;
76 }
77
78 private HotseatEduController mHotseatEduController;
79
80 public HotseatEduDialog(Context context, AttributeSet attr) {
81 this(context, attr, 0);
82 }
83
84 public HotseatEduDialog(Context context, AttributeSet attrs,
85 int defStyleAttr) {
86 super(context, attrs, defStyleAttr);
87 mContent = this;
88 }
89
90
91 @Override
92 protected void onFinishInflate() {
93 super.onFinishInflate();
94 mHotseatWrapper = findViewById(R.id.hotseat_wrapper);
95 mSampleHotseat = findViewById(R.id.sample_prediction);
Samuel Fufaa4211432020-02-25 18:47:54 -080096 mEduHeading = findViewById(R.id.hotseat_edu_heading);
97 mEduContent = findViewById(R.id.hotseat_edu_content);
Samuel Fufaaed008d2019-12-19 10:57:40 -080098
99 DeviceProfile grid = mLauncher.getDeviceProfile();
100 Rect padding = grid.getHotseatLayoutPadding();
101
102 mSampleHotseat.getLayoutParams().height = grid.cellHeightPx;
103 mSampleHotseat.setGridSize(grid.inv.numHotseatIcons, 1);
104 mSampleHotseat.setPadding(padding.left, 0, padding.right, 0);
105
106 Button turnOnBtn = findViewById(R.id.turn_predictions_on);
Samuel Fufaa4211432020-02-25 18:47:54 -0800107 turnOnBtn.setOnClickListener(this::onAccept);
Samuel Fufaaed008d2019-12-19 10:57:40 -0800108
Samuel Fufaa4211432020-02-25 18:47:54 -0800109 mDismissBtn = findViewById(R.id.no_thanks);
110 mDismissBtn.setOnClickListener(this::onDismiss);
Samuel Fufaaed008d2019-12-19 10:57:40 -0800111
112 }
113
Samuel Fufaa4211432020-02-25 18:47:54 -0800114 private void onAccept(View v) {
115 if (mMigrationMode == MIGRATE_NO_MIGRATE || !mHotseatEduController.migrate()) {
116 onDismiss(v);
117 return;
118 }
Samuel Fufaaed008d2019-12-19 10:57:40 -0800119 handleClose(true);
Samuel Fufaaed008d2019-12-19 10:57:40 -0800120 mHotseatEduController.finishOnboarding();
Samuel Fufab641ac22020-02-10 17:58:11 -0800121 logUserAction(true);
Samuel Fufaa4211432020-02-25 18:47:54 -0800122 int toastStringRes = mMigrationMode == MIGRATE_SAME_PAGE
123 ? R.string.hotseat_items_migrated : R.string.hotseat_items_migrated_alt;
124 Toast.makeText(mLauncher, toastStringRes, Toast.LENGTH_LONG).show();
Samuel Fufaaed008d2019-12-19 10:57:40 -0800125 }
126
Samuel Fufaa4211432020-02-25 18:47:54 -0800127 private void onDismiss(View v) {
Samuel Fufaaed008d2019-12-19 10:57:40 -0800128 Toast.makeText(getContext(), R.string.hotseat_no_migration, Toast.LENGTH_LONG).show();
129 mHotseatEduController.finishOnboarding();
Samuel Fufab641ac22020-02-10 17:58:11 -0800130 logUserAction(false);
Samuel Fufaaed008d2019-12-19 10:57:40 -0800131 handleClose(true);
132 }
133
134 @Override
135 public void logActionCommand(int command) {
136 // Since this is on-boarding popup, it is not a user controlled action.
137 }
138
139 @Override
140 public int getLogContainerType() {
141 return LauncherLogProto.ContainerType.TIP;
142 }
143
144 @Override
145 protected boolean isOfType(int type) {
146 return (type & TYPE_ON_BOARD_POPUP) != 0;
147 }
148
149 @Override
150 public void setInsets(Rect insets) {
151 int leftInset = insets.left - mInsets.left;
152 int rightInset = insets.right - mInsets.right;
153 int bottomInset = insets.bottom - mInsets.bottom;
154 mInsets.set(insets);
155 setPadding(leftInset, getPaddingTop(), rightInset, 0);
156 mHotseatWrapper.setPadding(mHotseatWrapper.getPaddingLeft(), getPaddingTop(),
157 mHotseatWrapper.getPaddingRight(), bottomInset);
158 mHotseatWrapper.getLayoutParams().height =
159 mLauncher.getDeviceProfile().hotseatBarSizePx + insets.bottom;
160 }
161
Samuel Fufab641ac22020-02-10 17:58:11 -0800162 private void logUserAction(boolean migrated) {
163 LauncherLogProto.Action action = new LauncherLogProto.Action();
164 LauncherLogProto.Target target = new LauncherLogProto.Target();
165 action.type = LauncherLogProto.Action.Type.TOUCH;
166 action.touch = LauncherLogProto.Action.Touch.TAP;
167 target.containerType = LauncherLogProto.ContainerType.TIP;
168 target.tipType = LauncherLogProto.TipType.HYBRID_HOTSEAT;
169 target.controlType = migrated ? LauncherLogProto.ControlType.HYBRID_HOTSEAT_ACCEPTED
170 : HYBRID_HOTSEAT_CANCELED;
Samuel Fufaa4211432020-02-25 18:47:54 -0800171 // encoding migration type on pageIndex
172 target.pageIndex = mMigrationMode;
Samuel Fufab641ac22020-02-10 17:58:11 -0800173 LauncherLogProto.LauncherEvent event = newLauncherEvent(action, target);
174 UserEventDispatcher.newInstance(getContext()).dispatchUserEvent(event, null);
175 }
Samuel Fufaaed008d2019-12-19 10:57:40 -0800176
Samuel Fufab641ac22020-02-10 17:58:11 -0800177 private void logOnBoardingSeen() {
178 LauncherLogProto.Action action = new LauncherLogProto.Action();
179 LauncherLogProto.Target target = new LauncherLogProto.Target();
180 action.type = LauncherLogProto.Action.Type.TIP;
181 target.containerType = LauncherLogProto.ContainerType.TIP;
182 target.tipType = LauncherLogProto.TipType.HYBRID_HOTSEAT;
183 LauncherLogProto.LauncherEvent event = newLauncherEvent(action, target);
184 UserEventDispatcher.newInstance(getContext()).dispatchUserEvent(event, null);
185 }
Samuel Fufaa4211432020-02-25 18:47:54 -0800186
Samuel Fufaaed008d2019-12-19 10:57:40 -0800187 private void animateOpen() {
188 if (mIsOpen || mOpenCloseAnimator.isRunning()) {
189 return;
190 }
191 mIsOpen = true;
192 mOpenCloseAnimator.setValues(
193 PropertyValuesHolder.ofFloat(TRANSLATION_SHIFT, TRANSLATION_SHIFT_OPENED));
194 mOpenCloseAnimator.setInterpolator(Interpolators.FAST_OUT_SLOW_IN);
195 mOpenCloseAnimator.start();
196 }
197
198 @Override
199 protected void handleClose(boolean animate) {
200 handleClose(true, DEFAULT_CLOSE_DURATION);
201 }
202
203 @Override
204 protected void onConfigurationChanged(Configuration newConfig) {
205 super.onConfigurationChanged(newConfig);
206 handleClose(false);
207 }
208
Samuel Fufaa4211432020-02-25 18:47:54 -0800209 @Override
210 protected int getScrimColor(Context context) {
211 return FINAL_SCRIM_BG_COLOR;
212 }
213
214 private void populatePreview(List<WorkspaceItemInfo> predictions) {
Samuel Fufaaed008d2019-12-19 10:57:40 -0800215 for (int i = 0; i < mLauncher.getDeviceProfile().inv.numHotseatIcons; i++) {
216 WorkspaceItemInfo info = predictions.get(i);
217 PredictedAppIcon icon = PredictedAppIcon.createIcon(mSampleHotseat, info);
218 icon.setEnabled(false);
219 icon.verifyHighRes();
220 CellLayout.LayoutParams lp = new CellLayout.LayoutParams(i, 0, 1, 1);
221 mSampleHotseat.addViewToCellLayout(icon, i, info.getViewId(), lp, true);
222 }
223 }
224
Samuel Fufaa4211432020-02-25 18:47:54 -0800225 @Override
226 protected void attachToContainer() {
227 super.attachToContainer();
228 if (FeatureFlags.HOTSEAT_MIGRATE_NEW_PAGE.get()) {
229 mEduContent.setText(R.string.hotseat_edu_message_migrate_alt);
230 mMigrationMode = MIGRATE_NEW_PAGE;
231 return;
232 }
233 CellLayout page = mLauncher.getWorkspace().getScreenWithId(
234 WorkspaceLayoutManager.FIRST_SCREEN_ID);
235
236 int maxItemsOnPage = SAME_PAGE_MAX_ROWS * mLauncher.getDeviceProfile().inv.numColumns
237 + (FeatureFlags.QSB_ON_FIRST_SCREEN ? 1 : 0);
238 if (page.getShortcutsAndWidgets().getChildCount() > maxItemsOnPage
239 || !page.makeSpaceForHotseatMigration(false)) {
240 mMigrationMode = MIGRATE_NO_MIGRATE;
241 mEduContent.setText(R.string.hotseat_edu_message_no_migrate);
242 mEduHeading.setText(R.string.hotseat_edu_title_no_migrate);
243 mDismissBtn.setVisibility(GONE);
244 }
245 }
246
247 /**
248 * Opens User education dialog with a list of suggested apps
249 */
250 public void show(List<WorkspaceItemInfo> predictions) {
251 if (getParent() != null
252 || predictions.size() < mLauncher.getDeviceProfile().inv.numHotseatIcons
253 || mHotseatEduController == null) {
254 return;
255 }
256 attachToContainer();
257 logOnBoardingSeen();
258 animateOpen();
259 populatePreview(predictions);
260 }
261
Samuel Fufaaed008d2019-12-19 10:57:40 -0800262 /**
263 * Factory method for HotseatPredictionUserEdu dialog
264 */
265 public static HotseatEduDialog getDialog(Launcher launcher) {
266 LayoutInflater layoutInflater = LayoutInflater.from(launcher);
267 return (HotseatEduDialog) layoutInflater.inflate(
268 R.layout.predicted_hotseat_edu, launcher.getDragLayer(),
269 false);
270
271 }
272}