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