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