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