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