AddItemActivity.java revision 778634c81870ae2013907950a98bb7793802e6c7
1/* 2 * Copyright (C) 2017 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 17package com.android.launcher3.dragndrop; 18 19import static com.android.launcher3.logging.LoggerUtils.newCommandAction; 20import static com.android.launcher3.logging.LoggerUtils.newContainerTarget; 21import static com.android.launcher3.logging.LoggerUtils.newItemTarget; 22import static com.android.launcher3.logging.LoggerUtils.newLauncherEvent; 23 24import android.annotation.TargetApi; 25import android.app.ActivityOptions; 26import android.appwidget.AppWidgetHost; 27import android.appwidget.AppWidgetManager; 28import android.content.ClipData; 29import android.content.ClipDescription; 30import android.content.Intent; 31import android.graphics.Canvas; 32import android.graphics.Point; 33import android.graphics.PointF; 34import android.graphics.Rect; 35import android.os.Build; 36import android.os.Bundle; 37import android.view.MotionEvent; 38import android.view.View; 39import android.view.View.DragShadowBuilder; 40import android.view.View.OnLongClickListener; 41import android.view.View.OnTouchListener; 42 43import com.android.launcher3.BaseActivity; 44import com.android.launcher3.InstallShortcutReceiver; 45import com.android.launcher3.InvariantDeviceProfile; 46import com.android.launcher3.Launcher; 47import com.android.launcher3.LauncherAppState; 48import com.android.launcher3.LauncherAppWidgetProviderInfo; 49import com.android.launcher3.R; 50import com.android.launcher3.compat.AppWidgetManagerCompat; 51import com.android.launcher3.compat.PinItemRequestCompat; 52import com.android.launcher3.model.WidgetItem; 53import com.android.launcher3.shortcuts.ShortcutInfoCompat; 54import com.android.launcher3.userevent.nano.LauncherLogProto.Action; 55import com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType; 56import com.android.launcher3.widget.PendingAddShortcutInfo; 57import com.android.launcher3.widget.PendingAddWidgetInfo; 58import com.android.launcher3.widget.WidgetHostViewLoader; 59import com.android.launcher3.widget.WidgetImageView; 60 61@TargetApi(Build.VERSION_CODES.N_MR1) 62public class AddItemActivity extends BaseActivity implements OnLongClickListener, OnTouchListener { 63 64 private static final int SHADOW_SIZE = 10; 65 66 private static final int REQUEST_BIND_APPWIDGET = 1; 67 private static final String STATE_EXTRA_WIDGET_ID = "state.widget.id"; 68 69 private final PointF mLastTouchPos = new PointF(); 70 71 private PinItemRequestCompat mRequest; 72 private LauncherAppState mApp; 73 private InvariantDeviceProfile mIdp; 74 75 private LivePreviewWidgetCell mWidgetCell; 76 77 // Widget request specific options. 78 private AppWidgetHost mAppWidgetHost; 79 private AppWidgetManagerCompat mAppWidgetManager; 80 private PendingAddWidgetInfo mPendingWidgetInfo; 81 private int mPendingBindWidgetId; 82 private Bundle mWidgetOptions; 83 84 @Override 85 protected void onCreate(Bundle savedInstanceState) { 86 super.onCreate(savedInstanceState); 87 88 mRequest = PinItemRequestCompat.getPinItemRequest(getIntent()); 89 if (mRequest == null) { 90 finish(); 91 return; 92 } 93 94 mApp = LauncherAppState.getInstance(this); 95 mIdp = mApp.getInvariantDeviceProfile(); 96 97 // Use the application context to get the device profile, as in multiwindow-mode, the 98 // confirmation activity might be rotated. 99 mDeviceProfile = mIdp.getDeviceProfile(getApplicationContext()); 100 101 setContentView(R.layout.add_item_confirmation_activity); 102 mWidgetCell = (LivePreviewWidgetCell) findViewById(R.id.widget_cell); 103 104 if (mRequest.getRequestType() == PinItemRequestCompat.REQUEST_TYPE_SHORTCUT) { 105 setupShortcut(); 106 } else { 107 if (!setupWidget()) { 108 // TODO: show error toast? 109 finish(); 110 } 111 } 112 113 mWidgetCell.setOnTouchListener(this); 114 mWidgetCell.setOnLongClickListener(this); 115 116 // savedInstanceState is null when the activity is created the first time (i.e., avoids 117 // duplicate logging during rotation) 118 if (savedInstanceState == null) { 119 logCommand(Action.Command.ENTRY); 120 } 121 } 122 123 @Override 124 public boolean onTouch(View view, MotionEvent motionEvent) { 125 mLastTouchPos.set(motionEvent.getX(), motionEvent.getY()); 126 return false; 127 } 128 129 @Override 130 public boolean onLongClick(View view) { 131 // Find the position of the preview relative to the touch location. 132 WidgetImageView img = mWidgetCell.getWidgetView(); 133 134 // If the ImageView doesn't have a drawable yet, the widget preview hasn't been loaded and 135 // we abort the drag. 136 if (img.getBitmap() == null) { 137 return false; 138 } 139 140 Rect bounds = img.getBitmapBounds(); 141 bounds.offset(img.getLeft() - (int) mLastTouchPos.x, img.getTop() - (int) mLastTouchPos.y); 142 143 // Start home and pass the draw request params 144 PinItemDragListener listener = new PinItemDragListener(mRequest, bounds, 145 img.getBitmap().getWidth(), img.getWidth()); 146 Intent homeIntent = new Intent(Intent.ACTION_MAIN) 147 .addCategory(Intent.CATEGORY_HOME) 148 .setPackage(getPackageName()) 149 .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK) 150 .putExtra(PinItemDragListener.EXTRA_PIN_ITEM_DRAG_LISTENER, listener); 151 startActivity(homeIntent, 152 ActivityOptions.makeCustomAnimation(this, 0, android.R.anim.fade_out).toBundle()); 153 154 // Start a system drag and drop. We use a transparent bitmap as preview for system drag 155 // as the preview is handled internally by launcher. 156 ClipDescription description = new ClipDescription("", new String[]{listener.getMimeType()}); 157 ClipData data = new ClipData(description, new ClipData.Item("")); 158 view.startDragAndDrop(data, new DragShadowBuilder(view) { 159 160 @Override 161 public void onDrawShadow(Canvas canvas) { } 162 163 @Override 164 public void onProvideShadowMetrics(Point outShadowSize, Point outShadowTouchPoint) { 165 outShadowSize.set(SHADOW_SIZE, SHADOW_SIZE); 166 outShadowTouchPoint.set(SHADOW_SIZE / 2, SHADOW_SIZE / 2); 167 } 168 }, null, View.DRAG_FLAG_GLOBAL); 169 return false; 170 } 171 172 private void setupShortcut() { 173 PinShortcutRequestActivityInfo shortcutInfo = 174 new PinShortcutRequestActivityInfo(mRequest, this); 175 WidgetItem item = new WidgetItem(shortcutInfo); 176 mWidgetCell.getWidgetView().setTag(new PendingAddShortcutInfo(shortcutInfo)); 177 mWidgetCell.applyFromCellItem(item, mApp.getWidgetCache()); 178 mWidgetCell.ensurePreview(); 179 } 180 181 private boolean setupWidget() { 182 LauncherAppWidgetProviderInfo widgetInfo = LauncherAppWidgetProviderInfo 183 .fromProviderInfo(this, mRequest.getAppWidgetProviderInfo(this)); 184 if (widgetInfo.minSpanX > mIdp.numColumns || widgetInfo.minSpanY > mIdp.numRows) { 185 // Cannot add widget 186 return false; 187 } 188 mWidgetCell.setPreview(PinItemDragListener.getPreview(mRequest)); 189 190 mAppWidgetManager = AppWidgetManagerCompat.getInstance(this); 191 mAppWidgetHost = new AppWidgetHost(this, Launcher.APPWIDGET_HOST_ID); 192 193 mPendingWidgetInfo = new PendingAddWidgetInfo(widgetInfo); 194 mPendingWidgetInfo.spanX = Math.min(mIdp.numColumns, widgetInfo.spanX); 195 mPendingWidgetInfo.spanY = Math.min(mIdp.numRows, widgetInfo.spanY); 196 mWidgetOptions = WidgetHostViewLoader.getDefaultOptionsForWidget(this, mPendingWidgetInfo); 197 198 WidgetItem item = new WidgetItem(widgetInfo, getPackageManager(), mIdp); 199 mWidgetCell.getWidgetView().setTag(mPendingWidgetInfo); 200 mWidgetCell.applyFromCellItem(item, mApp.getWidgetCache()); 201 mWidgetCell.ensurePreview(); 202 return true; 203 } 204 205 /** 206 * Called when the cancel button is clicked. 207 */ 208 public void onCancelClick(View v) { 209 logCommand(Action.Command.CANCEL); 210 finish(); 211 } 212 213 /** 214 * Called when place-automatically button is clicked. 215 */ 216 public void onPlaceAutomaticallyClick(View v) { 217 if (mRequest.getRequestType() == PinItemRequestCompat.REQUEST_TYPE_SHORTCUT) { 218 InstallShortcutReceiver.queueShortcut( 219 new ShortcutInfoCompat(mRequest.getShortcutInfo()), this); 220 logCommand(Action.Command.CONFIRM); 221 mRequest.accept(); 222 finish(); 223 return; 224 } 225 226 mPendingBindWidgetId = mAppWidgetHost.allocateAppWidgetId(); 227 boolean success = mAppWidgetManager.bindAppWidgetIdIfAllowed( 228 mPendingBindWidgetId, mRequest.getAppWidgetProviderInfo(this), mWidgetOptions); 229 if (success) { 230 acceptWidget(mPendingBindWidgetId); 231 return; 232 } 233 234 // request bind widget 235 Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_BIND); 236 intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, mPendingBindWidgetId); 237 intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_PROVIDER, 238 mPendingWidgetInfo.componentName); 239 intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_PROVIDER_PROFILE, 240 mRequest.getAppWidgetProviderInfo(this).getProfile()); 241 startActivityForResult(intent, REQUEST_BIND_APPWIDGET); 242 } 243 244 private void acceptWidget(int widgetId) { 245 InstallShortcutReceiver.queueWidget(mRequest.getAppWidgetProviderInfo(this), widgetId, this); 246 mWidgetOptions.putInt(AppWidgetManager.EXTRA_APPWIDGET_ID, widgetId); 247 mRequest.accept(mWidgetOptions); 248 logCommand(Action.Command.CONFIRM); 249 finish(); 250 } 251 252 @Override 253 public void onBackPressed() { 254 logCommand(Action.Command.BACK); 255 super.onBackPressed(); 256 } 257 258 @Override 259 protected void onActivityResult(int requestCode, int resultCode, Intent data) { 260 if (requestCode == REQUEST_BIND_APPWIDGET) { 261 int widgetId = data != null 262 ? data.getIntExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, mPendingBindWidgetId) 263 : mPendingBindWidgetId; 264 if (resultCode == RESULT_OK) { 265 acceptWidget(widgetId); 266 } else { 267 // Simply wait it out. 268 mAppWidgetHost.deleteAppWidgetId(widgetId); 269 mPendingBindWidgetId = -1; 270 } 271 return; 272 } 273 super.onActivityResult(requestCode, resultCode, data); 274 } 275 276 @Override 277 protected void onSaveInstanceState(Bundle outState) { 278 super.onSaveInstanceState(outState); 279 outState.putInt(STATE_EXTRA_WIDGET_ID, mPendingBindWidgetId); 280 } 281 282 @Override 283 protected void onRestoreInstanceState(Bundle savedInstanceState) { 284 super.onRestoreInstanceState(savedInstanceState); 285 mPendingBindWidgetId = savedInstanceState 286 .getInt(STATE_EXTRA_WIDGET_ID, mPendingBindWidgetId); 287 } 288 289 private void logCommand(int command) { 290 getUserEventDispatcher().dispatchUserEvent(newLauncherEvent( 291 newCommandAction(command), 292 newItemTarget(mWidgetCell.getWidgetView()), 293 newContainerTarget(ContainerType.PINITEM)), null); 294 } 295} 296