1 /* 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 */ 16 package com.android.launcher3.hybridhotseat; 17 18 import static com.android.launcher3.logging.StatsLogManager.LauncherEvent 19 .LAUNCHER_HOTSEAT_EDU_ACCEPT; 20 import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_HOTSEAT_EDU_DENY; 21 import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_HOTSEAT_EDU_SEEN; 22 23 import android.animation.PropertyValuesHolder; 24 import android.content.Context; 25 import android.content.res.Configuration; 26 import android.graphics.Rect; 27 import android.util.AttributeSet; 28 import android.view.LayoutInflater; 29 import android.view.View; 30 import android.widget.Button; 31 import android.widget.LinearLayout; 32 import android.widget.TextView; 33 34 import com.android.launcher3.AbstractFloatingView; 35 import com.android.launcher3.CellLayout; 36 import com.android.launcher3.DeviceProfile; 37 import com.android.launcher3.Insettable; 38 import com.android.launcher3.InvariantDeviceProfile; 39 import com.android.launcher3.Launcher; 40 import com.android.launcher3.R; 41 import com.android.launcher3.anim.Interpolators; 42 import com.android.launcher3.config.FeatureFlags; 43 import com.android.launcher3.model.data.WorkspaceItemInfo; 44 import com.android.launcher3.uioverrides.ApiWrapper; 45 import com.android.launcher3.uioverrides.PredictedAppIcon; 46 import com.android.launcher3.views.AbstractSlideInView; 47 48 import java.util.List; 49 50 /** 51 * User education dialog for hybrid hotseat. Allows user to migrate hotseat items to a new page in 52 * the workspace and shows predictions on the whole hotseat 53 */ 54 public class HotseatEduDialog extends AbstractSlideInView<Launcher> implements Insettable { 55 56 private static final int DEFAULT_CLOSE_DURATION = 200; 57 protected static final int FINAL_SCRIM_BG_COLOR = 0x88000000; 58 59 // we use this value to keep track of migration logs as we experiment with different migrations 60 private static final int MIGRATION_EXPERIMENT_IDENTIFIER = 1; 61 62 private final Rect mInsets = new Rect(); 63 private View mHotseatWrapper; 64 private CellLayout mSampleHotseat; 65 private Button mDismissBtn; 66 setHotseatEduController(HotseatEduController hotseatEduController)67 public void setHotseatEduController(HotseatEduController hotseatEduController) { 68 mHotseatEduController = hotseatEduController; 69 } 70 71 private HotseatEduController mHotseatEduController; 72 HotseatEduDialog(Context context, AttributeSet attr)73 public HotseatEduDialog(Context context, AttributeSet attr) { 74 this(context, attr, 0); 75 } 76 HotseatEduDialog(Context context, AttributeSet attrs, int defStyleAttr)77 public HotseatEduDialog(Context context, AttributeSet attrs, 78 int defStyleAttr) { 79 super(context, attrs, defStyleAttr); 80 mContent = this; 81 } 82 83 @Override onLayout(boolean changed, int l, int t, int r, int b)84 protected void onLayout(boolean changed, int l, int t, int r, int b) { 85 super.onLayout(changed, l, t, r, b); 86 setTranslationShift(TRANSLATION_SHIFT_CLOSED); 87 } 88 89 @Override onFinishInflate()90 protected void onFinishInflate() { 91 super.onFinishInflate(); 92 mHotseatWrapper = findViewById(R.id.hotseat_wrapper); 93 mSampleHotseat = findViewById(R.id.sample_prediction); 94 95 Context context = getContext(); 96 DeviceProfile grid = mActivityContext.getDeviceProfile(); 97 Rect padding = grid.getHotseatLayoutPadding(context); 98 99 mSampleHotseat.getLayoutParams().height = grid.cellHeightPx; 100 mSampleHotseat.setGridSize(grid.numShownHotseatIcons, 1); 101 mSampleHotseat.setPadding(padding.left, 0, padding.right, 0); 102 103 Button turnOnBtn = findViewById(R.id.turn_predictions_on); 104 turnOnBtn.setOnClickListener(this::onAccept); 105 106 mDismissBtn = findViewById(R.id.no_thanks); 107 mDismissBtn.setOnClickListener(this::onDismiss); 108 109 LinearLayout buttonContainer = findViewById(R.id.button_container); 110 int adjustedMarginEnd = ApiWrapper.getHotseatEndOffset(context) 111 - buttonContainer.getPaddingEnd(); 112 if (InvariantDeviceProfile.INSTANCE.get(context) 113 .getDeviceProfile(context).isTaskbarPresent && adjustedMarginEnd > 0) { 114 ((LinearLayout.LayoutParams) buttonContainer.getLayoutParams()).setMarginEnd( 115 adjustedMarginEnd); 116 } 117 118 // update ui to reflect which migration method is going to be used 119 if (FeatureFlags.HOTSEAT_MIGRATE_TO_FOLDER.get()) { 120 ((TextView) findViewById(R.id.hotseat_edu_content)).setText( 121 R.string.hotseat_edu_message_migrate_alt); 122 } 123 } 124 onAccept(View v)125 private void onAccept(View v) { 126 mHotseatEduController.migrate(); 127 handleClose(true); 128 129 mHotseatEduController.moveHotseatItems(); 130 mHotseatEduController.finishOnboarding(); 131 mActivityContext.getStatsLogManager().logger().log(LAUNCHER_HOTSEAT_EDU_ACCEPT); 132 } 133 onDismiss(View v)134 private void onDismiss(View v) { 135 mHotseatEduController.showDimissTip(); 136 mHotseatEduController.finishOnboarding(); 137 mActivityContext.getStatsLogManager().logger().log(LAUNCHER_HOTSEAT_EDU_DENY); 138 handleClose(true); 139 } 140 141 @Override isOfType(int type)142 protected boolean isOfType(int type) { 143 return (type & TYPE_ON_BOARD_POPUP) != 0; 144 } 145 146 @Override setInsets(Rect insets)147 public void setInsets(Rect insets) { 148 int leftInset = insets.left - mInsets.left; 149 int rightInset = insets.right - mInsets.right; 150 int bottomInset = insets.bottom - mInsets.bottom; 151 mInsets.set(insets); 152 if (mActivityContext.getOrientation() == Configuration.ORIENTATION_PORTRAIT) { 153 setPadding(leftInset, getPaddingTop(), rightInset, 0); 154 mHotseatWrapper.setPadding(mHotseatWrapper.getPaddingLeft(), getPaddingTop(), 155 mHotseatWrapper.getPaddingRight(), bottomInset); 156 mHotseatWrapper.getLayoutParams().height = 157 mActivityContext.getDeviceProfile().hotseatBarSizePx + insets.bottom; 158 159 } else { 160 setPadding(0, getPaddingTop(), 0, 0); 161 mHotseatWrapper.setPadding(mHotseatWrapper.getPaddingLeft(), getPaddingTop(), 162 mHotseatWrapper.getPaddingRight(), 163 (int) getResources().getDimension(R.dimen.bottom_sheet_edu_padding)); 164 ((TextView) findViewById(R.id.hotseat_edu_heading)).setText( 165 R.string.hotseat_edu_title_migrate_landscape); 166 ((TextView) findViewById(R.id.hotseat_edu_content)).setText( 167 R.string.hotseat_edu_message_migrate_landscape); 168 } 169 } 170 animateOpen()171 private void animateOpen() { 172 if (mIsOpen || mOpenCloseAnimator.isRunning()) { 173 return; 174 } 175 mIsOpen = true; 176 mOpenCloseAnimator.setValues( 177 PropertyValuesHolder.ofFloat(TRANSLATION_SHIFT, TRANSLATION_SHIFT_OPENED)); 178 mOpenCloseAnimator.setInterpolator(Interpolators.FAST_OUT_SLOW_IN); 179 mOpenCloseAnimator.start(); 180 } 181 182 @Override handleClose(boolean animate)183 protected void handleClose(boolean animate) { 184 handleClose(true, DEFAULT_CLOSE_DURATION); 185 } 186 187 @Override onConfigurationChanged(Configuration newConfig)188 protected void onConfigurationChanged(Configuration newConfig) { 189 super.onConfigurationChanged(newConfig); 190 handleClose(false); 191 } 192 193 @Override getScrimColor(Context context)194 protected int getScrimColor(Context context) { 195 return FINAL_SCRIM_BG_COLOR; 196 } 197 populatePreview(List<WorkspaceItemInfo> predictions)198 private void populatePreview(List<WorkspaceItemInfo> predictions) { 199 for (int i = 0; i < mActivityContext.getDeviceProfile().numShownHotseatIcons; i++) { 200 WorkspaceItemInfo info = predictions.get(i); 201 PredictedAppIcon icon = PredictedAppIcon.createIcon(mSampleHotseat, info); 202 icon.setEnabled(false); 203 icon.setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_NO); 204 icon.verifyHighRes(); 205 CellLayout.LayoutParams lp = new CellLayout.LayoutParams(i, 0, 1, 1); 206 mSampleHotseat.addViewToCellLayout(icon, i, info.getViewId(), lp, true); 207 } 208 } 209 210 /** 211 * Opens User education dialog with a list of suggested apps 212 */ show(List<WorkspaceItemInfo> predictions)213 public void show(List<WorkspaceItemInfo> predictions) { 214 if (getParent() != null 215 || predictions.size() < mActivityContext.getDeviceProfile().numShownHotseatIcons 216 || mHotseatEduController == null) { 217 return; 218 } 219 AbstractFloatingView.closeAllOpenViews(mActivityContext); 220 attachToContainer(); 221 animateOpen(); 222 populatePreview(predictions); 223 mActivityContext.getStatsLogManager().logger().log(LAUNCHER_HOTSEAT_EDU_SEEN); 224 } 225 226 /** 227 * Factory method for HotseatPredictionUserEdu dialog 228 */ getDialog(Launcher launcher)229 public static HotseatEduDialog getDialog(Launcher launcher) { 230 LayoutInflater layoutInflater = LayoutInflater.from(launcher); 231 return (HotseatEduDialog) layoutInflater.inflate( 232 R.layout.predicted_hotseat_edu, launcher.getDragLayer(), 233 false); 234 235 } 236 } 237