mirror of
https://github.com/LawnchairLauncher/lawnchair.git
synced 2026-02-19 10:48:19 +00:00
Bug: 189726613 Test: Manually tested Change-Id: I048740f949571423ab53bbb01e7128d9981b1ce7
382 lines
15 KiB
Java
382 lines
15 KiB
Java
/*
|
|
* Copyright (C) 2017 The Android Open Source Project
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
|
|
package com.android.launcher3.dragndrop;
|
|
|
|
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_ADD_EXTERNAL_ITEM_BACK;
|
|
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_ADD_EXTERNAL_ITEM_CANCELLED;
|
|
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_ADD_EXTERNAL_ITEM_DRAGGED;
|
|
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_ADD_EXTERNAL_ITEM_PLACED_AUTOMATICALLY;
|
|
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_ADD_EXTERNAL_ITEM_START;
|
|
import static com.android.launcher3.util.Executors.MODEL_EXECUTOR;
|
|
|
|
import android.annotation.TargetApi;
|
|
import android.app.ActivityOptions;
|
|
import android.appwidget.AppWidgetManager;
|
|
import android.content.ClipData;
|
|
import android.content.ClipDescription;
|
|
import android.content.Intent;
|
|
import android.content.pm.LauncherApps.PinItemRequest;
|
|
import android.content.res.Configuration;
|
|
import android.graphics.Canvas;
|
|
import android.graphics.Point;
|
|
import android.graphics.PointF;
|
|
import android.graphics.Rect;
|
|
import android.os.AsyncTask;
|
|
import android.os.Build;
|
|
import android.os.Bundle;
|
|
import android.view.MotionEvent;
|
|
import android.view.View;
|
|
import android.view.View.DragShadowBuilder;
|
|
import android.view.View.OnLongClickListener;
|
|
import android.view.View.OnTouchListener;
|
|
import android.view.WindowManager;
|
|
import android.widget.TextView;
|
|
|
|
import com.android.launcher3.BaseActivity;
|
|
import com.android.launcher3.InvariantDeviceProfile;
|
|
import com.android.launcher3.Launcher;
|
|
import com.android.launcher3.LauncherAppState;
|
|
import com.android.launcher3.R;
|
|
import com.android.launcher3.logging.StatsLogManager;
|
|
import com.android.launcher3.model.ItemInstallQueue;
|
|
import com.android.launcher3.model.WidgetItem;
|
|
import com.android.launcher3.model.data.ItemInfo;
|
|
import com.android.launcher3.pm.PinRequestHelper;
|
|
import com.android.launcher3.util.SystemUiController;
|
|
import com.android.launcher3.views.AbstractSlideInView;
|
|
import com.android.launcher3.views.BaseDragLayer;
|
|
import com.android.launcher3.widget.AddItemWidgetsBottomSheet;
|
|
import com.android.launcher3.widget.LauncherAppWidgetHost;
|
|
import com.android.launcher3.widget.LauncherAppWidgetProviderInfo;
|
|
import com.android.launcher3.widget.NavigableAppWidgetHostView;
|
|
import com.android.launcher3.widget.PendingAddShortcutInfo;
|
|
import com.android.launcher3.widget.PendingAddWidgetInfo;
|
|
import com.android.launcher3.widget.WidgetCell;
|
|
import com.android.launcher3.widget.WidgetCellPreview;
|
|
import com.android.launcher3.widget.WidgetImageView;
|
|
import com.android.launcher3.widget.WidgetManagerHelper;
|
|
|
|
import java.util.function.Supplier;
|
|
|
|
/**
|
|
* Activity to show pin widget dialog.
|
|
*/
|
|
@TargetApi(Build.VERSION_CODES.O)
|
|
public class AddItemActivity extends BaseActivity
|
|
implements OnLongClickListener, OnTouchListener, AbstractSlideInView.OnCloseListener {
|
|
|
|
private static final int SHADOW_SIZE = 10;
|
|
|
|
private static final int REQUEST_BIND_APPWIDGET = 1;
|
|
private static final String STATE_EXTRA_WIDGET_ID = "state.widget.id";
|
|
|
|
private final PointF mLastTouchPos = new PointF();
|
|
|
|
private PinItemRequest mRequest;
|
|
private LauncherAppState mApp;
|
|
private InvariantDeviceProfile mIdp;
|
|
private BaseDragLayer<AddItemActivity> mDragLayer;
|
|
private AddItemWidgetsBottomSheet mSlideInView;
|
|
|
|
private WidgetCell mWidgetCell;
|
|
|
|
// Widget request specific options.
|
|
private LauncherAppWidgetHost mAppWidgetHost;
|
|
private WidgetManagerHelper mAppWidgetManager;
|
|
private int mPendingBindWidgetId;
|
|
private Bundle mWidgetOptions;
|
|
|
|
private boolean mFinishOnPause = false;
|
|
|
|
@Override
|
|
protected void onCreate(Bundle savedInstanceState) {
|
|
super.onCreate(savedInstanceState);
|
|
|
|
mRequest = PinRequestHelper.getPinItemRequest(getIntent());
|
|
if (mRequest == null) {
|
|
finish();
|
|
return;
|
|
}
|
|
|
|
mApp = LauncherAppState.getInstance(this);
|
|
mIdp = mApp.getInvariantDeviceProfile();
|
|
|
|
// Use the application context to get the device profile, as in multiwindow-mode, the
|
|
// confirmation activity might be rotated.
|
|
mDeviceProfile = mIdp.getDeviceProfile(getApplicationContext());
|
|
|
|
setContentView(R.layout.add_item_confirmation_activity);
|
|
// Set flag to allow activity to draw over navigation and status bar.
|
|
getWindow().setFlags(WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS,
|
|
WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS);
|
|
mDragLayer = findViewById(R.id.add_item_drag_layer);
|
|
mDragLayer.recreateControllers();
|
|
mWidgetCell = findViewById(R.id.widget_cell);
|
|
|
|
if (mRequest.getRequestType() == PinItemRequest.REQUEST_TYPE_SHORTCUT) {
|
|
setupShortcut();
|
|
} else {
|
|
if (!setupWidget()) {
|
|
// TODO: show error toast?
|
|
finish();
|
|
}
|
|
}
|
|
|
|
WidgetCellPreview previewContainer = mWidgetCell.findViewById(
|
|
R.id.widget_preview_container);
|
|
previewContainer.setOnTouchListener(this);
|
|
previewContainer.setOnLongClickListener(this);
|
|
|
|
// savedInstanceState is null when the activity is created the first time (i.e., avoids
|
|
// duplicate logging during rotation)
|
|
if (savedInstanceState == null) {
|
|
logCommand(LAUNCHER_ADD_EXTERNAL_ITEM_START);
|
|
}
|
|
|
|
TextView widgetAppName = findViewById(R.id.widget_appName);
|
|
widgetAppName.setText(getApplicationInfo().labelRes);
|
|
|
|
mSlideInView = findViewById(R.id.add_item_bottom_sheet);
|
|
mSlideInView.addOnCloseListener(this);
|
|
mSlideInView.show();
|
|
setupNavBarColor();
|
|
}
|
|
|
|
@Override
|
|
public boolean onTouch(View view, MotionEvent motionEvent) {
|
|
mLastTouchPos.set(motionEvent.getX(), motionEvent.getY());
|
|
return false;
|
|
}
|
|
|
|
@Override
|
|
public boolean onLongClick(View view) {
|
|
// Find the position of the preview relative to the touch location.
|
|
WidgetImageView img = mWidgetCell.getWidgetView();
|
|
NavigableAppWidgetHostView appWidgetHostView = mWidgetCell.getAppWidgetHostViewPreview();
|
|
|
|
// If the ImageView doesn't have a drawable yet, the widget preview hasn't been loaded and
|
|
// we abort the drag.
|
|
if (img.getDrawable() == null && appWidgetHostView == null) {
|
|
return false;
|
|
}
|
|
|
|
final Rect bounds;
|
|
// Start home and pass the draw request params
|
|
final PinItemDragListener listener;
|
|
if (appWidgetHostView != null) {
|
|
bounds = new Rect();
|
|
appWidgetHostView.getSourceVisualDragBounds(bounds);
|
|
bounds.offset(appWidgetHostView.getLeft() - (int) mLastTouchPos.x,
|
|
appWidgetHostView.getTop() - (int) mLastTouchPos.y);
|
|
listener = new PinItemDragListener(mRequest, bounds,
|
|
appWidgetHostView.getMeasuredWidth(), appWidgetHostView.getMeasuredWidth());
|
|
} else {
|
|
bounds = img.getBitmapBounds();
|
|
bounds.offset(img.getLeft() - (int) mLastTouchPos.x,
|
|
img.getTop() - (int) mLastTouchPos.y);
|
|
listener = new PinItemDragListener(mRequest, bounds,
|
|
img.getDrawable().getIntrinsicWidth(), img.getWidth());
|
|
}
|
|
|
|
// Start a system drag and drop. We use a transparent bitmap as preview for system drag
|
|
// as the preview is handled internally by launcher.
|
|
ClipDescription description = new ClipDescription("", new String[]{listener.getMimeType()});
|
|
ClipData data = new ClipData(description, new ClipData.Item(""));
|
|
view.startDragAndDrop(data, new DragShadowBuilder(view) {
|
|
|
|
@Override
|
|
public void onDrawShadow(Canvas canvas) { }
|
|
|
|
@Override
|
|
public void onProvideShadowMetrics(Point outShadowSize, Point outShadowTouchPoint) {
|
|
outShadowSize.set(SHADOW_SIZE, SHADOW_SIZE);
|
|
outShadowTouchPoint.set(SHADOW_SIZE / 2, SHADOW_SIZE / 2);
|
|
}
|
|
}, null, View.DRAG_FLAG_GLOBAL);
|
|
|
|
Intent homeIntent = new Intent(Intent.ACTION_MAIN)
|
|
.addCategory(Intent.CATEGORY_HOME)
|
|
.setPackage(getPackageName())
|
|
.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
|
Launcher.ACTIVITY_TRACKER.runCallbackWhenActivityExists(listener, homeIntent);
|
|
startActivity(homeIntent,
|
|
ActivityOptions.makeCustomAnimation(this, 0, android.R.anim.fade_out)
|
|
.toBundle());
|
|
logCommand(LAUNCHER_ADD_EXTERNAL_ITEM_DRAGGED);
|
|
mFinishOnPause = true;
|
|
return false;
|
|
}
|
|
|
|
@Override
|
|
protected void onPause() {
|
|
super.onPause();
|
|
if (mFinishOnPause) {
|
|
finish();
|
|
}
|
|
}
|
|
|
|
private void setupShortcut() {
|
|
PinShortcutRequestActivityInfo shortcutInfo =
|
|
new PinShortcutRequestActivityInfo(mRequest, this);
|
|
mWidgetCell.getWidgetView().setTag(new PendingAddShortcutInfo(shortcutInfo));
|
|
applyWidgetItemAsync(
|
|
() -> new WidgetItem(shortcutInfo, mApp.getIconCache(), getPackageManager()));
|
|
}
|
|
|
|
private boolean setupWidget() {
|
|
LauncherAppWidgetProviderInfo widgetInfo = LauncherAppWidgetProviderInfo
|
|
.fromProviderInfo(this, mRequest.getAppWidgetProviderInfo(this));
|
|
if (widgetInfo.minSpanX > mIdp.numColumns || widgetInfo.minSpanY > mIdp.numRows) {
|
|
// Cannot add widget
|
|
return false;
|
|
}
|
|
mWidgetCell.setRemoteViewsPreview(PinItemDragListener.getPreview(mRequest));
|
|
|
|
mAppWidgetManager = new WidgetManagerHelper(this);
|
|
mAppWidgetHost = new LauncherAppWidgetHost(this);
|
|
|
|
PendingAddWidgetInfo pendingInfo = new PendingAddWidgetInfo(widgetInfo);
|
|
pendingInfo.spanX = Math.min(mIdp.numColumns, widgetInfo.spanX);
|
|
pendingInfo.spanY = Math.min(mIdp.numRows, widgetInfo.spanY);
|
|
mWidgetOptions = pendingInfo.getDefaultSizeOptions(this);
|
|
mWidgetCell.getWidgetView().setTag(pendingInfo);
|
|
|
|
applyWidgetItemAsync(() -> new WidgetItem(widgetInfo, mIdp, mApp.getIconCache()));
|
|
return true;
|
|
}
|
|
|
|
private void applyWidgetItemAsync(final Supplier<WidgetItem> itemProvider) {
|
|
new AsyncTask<Void, Void, WidgetItem>() {
|
|
@Override
|
|
protected WidgetItem doInBackground(Void... voids) {
|
|
return itemProvider.get();
|
|
}
|
|
|
|
@Override
|
|
protected void onPostExecute(WidgetItem item) {
|
|
mWidgetCell.setPreviewSize(item.spanX, item.spanY);
|
|
mWidgetCell.applyFromCellItem(item, mApp.getWidgetCache());
|
|
mWidgetCell.ensurePreview();
|
|
}
|
|
}.executeOnExecutor(MODEL_EXECUTOR);
|
|
// TODO: Create a worker looper executor and reuse that everywhere.
|
|
}
|
|
|
|
/**
|
|
* Called when the cancel button is clicked.
|
|
*/
|
|
public void onCancelClick(View v) {
|
|
logCommand(LAUNCHER_ADD_EXTERNAL_ITEM_CANCELLED);
|
|
mSlideInView.close(/* animate= */ true);
|
|
}
|
|
|
|
/**
|
|
* Called when place-automatically button is clicked.
|
|
*/
|
|
public void onPlaceAutomaticallyClick(View v) {
|
|
if (mRequest.getRequestType() == PinItemRequest.REQUEST_TYPE_SHORTCUT) {
|
|
ItemInstallQueue.INSTANCE.get(this).queueItem(mRequest.getShortcutInfo());
|
|
logCommand(LAUNCHER_ADD_EXTERNAL_ITEM_PLACED_AUTOMATICALLY);
|
|
mRequest.accept();
|
|
mSlideInView.close(/* animate= */ true);
|
|
return;
|
|
}
|
|
|
|
mPendingBindWidgetId = mAppWidgetHost.allocateAppWidgetId();
|
|
boolean success = mAppWidgetManager.bindAppWidgetIdIfAllowed(
|
|
mPendingBindWidgetId, mRequest.getAppWidgetProviderInfo(this), mWidgetOptions);
|
|
if (success) {
|
|
acceptWidget(mPendingBindWidgetId);
|
|
return;
|
|
}
|
|
|
|
// request bind widget
|
|
mAppWidgetHost.startBindFlow(this, mPendingBindWidgetId,
|
|
mRequest.getAppWidgetProviderInfo(this), REQUEST_BIND_APPWIDGET);
|
|
}
|
|
|
|
private void acceptWidget(int widgetId) {
|
|
ItemInstallQueue.INSTANCE.get(this)
|
|
.queueItem(mRequest.getAppWidgetProviderInfo(this), widgetId);
|
|
mWidgetOptions.putInt(AppWidgetManager.EXTRA_APPWIDGET_ID, widgetId);
|
|
mRequest.accept(mWidgetOptions);
|
|
logCommand(LAUNCHER_ADD_EXTERNAL_ITEM_PLACED_AUTOMATICALLY);
|
|
mSlideInView.close(/* animate= */ true);
|
|
}
|
|
|
|
@Override
|
|
public void onBackPressed() {
|
|
logCommand(LAUNCHER_ADD_EXTERNAL_ITEM_BACK);
|
|
mSlideInView.close(/* animate= */ true);
|
|
}
|
|
|
|
@Override
|
|
public void onActivityResult(int requestCode, int resultCode, Intent data) {
|
|
if (requestCode == REQUEST_BIND_APPWIDGET) {
|
|
int widgetId = data != null
|
|
? data.getIntExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, mPendingBindWidgetId)
|
|
: mPendingBindWidgetId;
|
|
if (resultCode == RESULT_OK) {
|
|
acceptWidget(widgetId);
|
|
} else {
|
|
// Simply wait it out.
|
|
mAppWidgetHost.deleteAppWidgetId(widgetId);
|
|
mPendingBindWidgetId = -1;
|
|
}
|
|
return;
|
|
}
|
|
super.onActivityResult(requestCode, resultCode, data);
|
|
}
|
|
|
|
@Override
|
|
protected void onSaveInstanceState(Bundle outState) {
|
|
super.onSaveInstanceState(outState);
|
|
outState.putInt(STATE_EXTRA_WIDGET_ID, mPendingBindWidgetId);
|
|
}
|
|
|
|
@Override
|
|
protected void onRestoreInstanceState(Bundle savedInstanceState) {
|
|
super.onRestoreInstanceState(savedInstanceState);
|
|
mPendingBindWidgetId = savedInstanceState
|
|
.getInt(STATE_EXTRA_WIDGET_ID, mPendingBindWidgetId);
|
|
}
|
|
|
|
@Override
|
|
public BaseDragLayer getDragLayer() {
|
|
return mDragLayer;
|
|
}
|
|
|
|
@Override
|
|
public void onSlideInViewClosed() {
|
|
finish();
|
|
}
|
|
|
|
protected void setupNavBarColor() {
|
|
boolean isSheetDark = (getApplicationContext().getResources().getConfiguration().uiMode
|
|
& Configuration.UI_MODE_NIGHT_MASK) == Configuration.UI_MODE_NIGHT_YES;
|
|
getSystemUiController().updateUiState(
|
|
SystemUiController.UI_STATE_BASE_WINDOW,
|
|
isSheetDark ? SystemUiController.FLAG_DARK_NAV : SystemUiController.FLAG_LIGHT_NAV);
|
|
}
|
|
|
|
private void logCommand(StatsLogManager.EventEnum command) {
|
|
getStatsLogManager().logger()
|
|
.withItemInfo((ItemInfo) mWidgetCell.getWidgetView().getTag())
|
|
.log(command);
|
|
}
|
|
}
|