Merge "Create a long-press menu for sending screenshots to bubbles."
This commit is contained in:
43
packages/SystemUI/res/layout/bubble_menu_view.xml
Normal file
43
packages/SystemUI/res/layout/bubble_menu_view.xml
Normal file
@@ -0,0 +1,43 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
~ Copyright (C) 2019 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.
|
||||
-->
|
||||
<com.android.systemui.bubbles.BubbleMenuView
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_width="match_parent"
|
||||
android:background="#66000000"
|
||||
android:visibility="gone"
|
||||
android:id="@+id/bubble_menu_container">
|
||||
|
||||
<FrameLayout
|
||||
android:layout_height="@dimen/individual_bubble_size"
|
||||
android:layout_width="wrap_content"
|
||||
android:background="#FFFFFF"
|
||||
android:id="@+id/bubble_menu_view">
|
||||
|
||||
<ImageView
|
||||
android:id="@*android:id/icon"
|
||||
android:layout_width="@dimen/global_actions_grid_item_icon_width"
|
||||
android:layout_height="@dimen/global_actions_grid_item_icon_height"
|
||||
android:layout_marginTop="@dimen/global_actions_grid_item_icon_top_margin"
|
||||
android:layout_marginBottom="@dimen/global_actions_grid_item_icon_bottom_margin"
|
||||
android:layout_marginLeft="@dimen/global_actions_grid_item_icon_side_margin"
|
||||
android:layout_marginRight="@dimen/global_actions_grid_item_icon_side_margin"
|
||||
android:scaleType="centerInside"
|
||||
android:tint="@color/global_actions_text"
|
||||
/>
|
||||
</FrameLayout>
|
||||
</com.android.systemui.bubbles.BubbleMenuView>
|
||||
@@ -44,13 +44,19 @@ import static java.lang.annotation.RetentionPolicy.SOURCE;
|
||||
|
||||
import android.annotation.UserIdInt;
|
||||
import android.app.ActivityManager.RunningTaskInfo;
|
||||
import android.app.Notification;
|
||||
import android.app.NotificationManager;
|
||||
import android.app.PendingIntent;
|
||||
import android.app.RemoteInput;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.pm.ActivityInfo;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.content.pm.ShortcutManager;
|
||||
import android.content.res.Configuration;
|
||||
import android.graphics.Rect;
|
||||
import android.net.Uri;
|
||||
import android.os.Handler;
|
||||
import android.os.RemoteException;
|
||||
import android.os.ServiceManager;
|
||||
import android.service.notification.NotificationListenerService.RankingMap;
|
||||
@@ -69,6 +75,7 @@ import androidx.annotation.Nullable;
|
||||
|
||||
import com.android.internal.annotations.VisibleForTesting;
|
||||
import com.android.internal.statusbar.IStatusBarService;
|
||||
import com.android.internal.util.ScreenshotHelper;
|
||||
import com.android.systemui.R;
|
||||
import com.android.systemui.plugins.statusbar.StatusBarStateController;
|
||||
import com.android.systemui.shared.system.ActivityManagerWrapper;
|
||||
@@ -86,6 +93,7 @@ import com.android.systemui.statusbar.phone.ShadeController;
|
||||
import com.android.systemui.statusbar.phone.StatusBar;
|
||||
import com.android.systemui.statusbar.phone.StatusBarWindowController;
|
||||
import com.android.systemui.statusbar.policy.ConfigurationController;
|
||||
import com.android.systemui.statusbar.policy.RemoteInputUriController;
|
||||
import com.android.systemui.statusbar.policy.ZenModeController;
|
||||
|
||||
import java.io.FileDescriptor;
|
||||
@@ -93,8 +101,10 @@ import java.io.PrintWriter;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.Target;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Singleton;
|
||||
@@ -138,6 +148,8 @@ public class BubbleController implements ConfigurationController.ConfigurationLi
|
||||
@Nullable private BubbleStackView.SurfaceSynchronizer mSurfaceSynchronizer;
|
||||
private final NotificationGroupManager mNotificationGroupManager;
|
||||
private final Lazy<ShadeController> mShadeController;
|
||||
private final RemoteInputUriController mRemoteInputUriController;
|
||||
private Handler mHandler = new Handler() {};
|
||||
|
||||
private BubbleData mBubbleData;
|
||||
@Nullable private BubbleStackView mStackView;
|
||||
@@ -155,6 +167,8 @@ public class BubbleController implements ConfigurationController.ConfigurationLi
|
||||
private final StatusBarWindowController mStatusBarWindowController;
|
||||
private final ZenModeController mZenModeController;
|
||||
private StatusBarStateListener mStatusBarStateListener;
|
||||
private final ScreenshotHelper mScreenshotHelper;
|
||||
|
||||
|
||||
private final NotificationInterruptionStateProvider mNotificationInterruptionStateProvider;
|
||||
private IStatusBarService mBarService;
|
||||
@@ -191,6 +205,16 @@ public class BubbleController implements ConfigurationController.ConfigurationLi
|
||||
void onBubbleExpandChanged(boolean isExpanding, String key);
|
||||
}
|
||||
|
||||
/**
|
||||
* Listener for handling bubble screenshot events.
|
||||
*/
|
||||
public interface BubbleScreenshotListener {
|
||||
/**
|
||||
* Called to trigger taking a screenshot and sending the result to a bubble.
|
||||
*/
|
||||
void onBubbleScreenshot(Bubble bubble);
|
||||
}
|
||||
|
||||
/**
|
||||
* Listens for the current state of the status bar and updates the visibility state
|
||||
* of bubbles as needed.
|
||||
@@ -226,10 +250,12 @@ public class BubbleController implements ConfigurationController.ConfigurationLi
|
||||
ZenModeController zenModeController,
|
||||
NotificationLockscreenUserManager notifUserManager,
|
||||
NotificationGroupManager groupManager,
|
||||
NotificationEntryManager entryManager) {
|
||||
NotificationEntryManager entryManager,
|
||||
RemoteInputUriController remoteInputUriController) {
|
||||
this(context, statusBarWindowController, statusBarStateController, shadeController,
|
||||
data, null /* synchronizer */, configurationController, interruptionStateProvider,
|
||||
zenModeController, notifUserManager, groupManager, entryManager);
|
||||
zenModeController, notifUserManager, groupManager, entryManager,
|
||||
remoteInputUriController);
|
||||
}
|
||||
|
||||
public BubbleController(Context context,
|
||||
@@ -243,11 +269,13 @@ public class BubbleController implements ConfigurationController.ConfigurationLi
|
||||
ZenModeController zenModeController,
|
||||
NotificationLockscreenUserManager notifUserManager,
|
||||
NotificationGroupManager groupManager,
|
||||
NotificationEntryManager entryManager) {
|
||||
NotificationEntryManager entryManager,
|
||||
RemoteInputUriController remoteInputUriController) {
|
||||
mContext = context;
|
||||
mNotificationInterruptionStateProvider = interruptionStateProvider;
|
||||
mNotifUserManager = notifUserManager;
|
||||
mZenModeController = zenModeController;
|
||||
mRemoteInputUriController = remoteInputUriController;
|
||||
mZenModeController.addCallback(new ZenModeController.Callback() {
|
||||
@Override
|
||||
public void onZenChanged(int zen) {
|
||||
@@ -320,6 +348,8 @@ public class BubbleController implements ConfigurationController.ConfigurationLi
|
||||
});
|
||||
|
||||
mUserCreatedBubbles = new HashSet<>();
|
||||
|
||||
mScreenshotHelper = new ScreenshotHelper(context);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -337,6 +367,9 @@ public class BubbleController implements ConfigurationController.ConfigurationLi
|
||||
if (mExpandListener != null) {
|
||||
mStackView.setExpandListener(mExpandListener);
|
||||
}
|
||||
if (mBubbleScreenshotListener != null) {
|
||||
mStackView.setBubbleScreenshotListener(mBubbleScreenshotListener);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1058,4 +1091,71 @@ public class BubbleController implements ConfigurationController.ConfigurationLi
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Copied from RemoteInputView. Consolidate RemoteInput intent logic.
|
||||
private Intent prepareRemoteInputFromData(String contentType, Uri data,
|
||||
RemoteInput remoteInput, NotificationEntry entry) {
|
||||
HashMap<String, Uri> results = new HashMap<>();
|
||||
results.put(contentType, data);
|
||||
mRemoteInputUriController.grantInlineReplyUriPermission(entry.getSbn(), data);
|
||||
Intent fillInIntent = new Intent().addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
|
||||
RemoteInput.addDataResultToIntent(remoteInput, fillInIntent, results);
|
||||
|
||||
return fillInIntent;
|
||||
}
|
||||
|
||||
// TODO: Copied from RemoteInputView. Consolidate RemoteInput intent logic.
|
||||
private void sendRemoteInput(Intent intent, NotificationEntry entry,
|
||||
PendingIntent pendingIntent) {
|
||||
// Tell ShortcutManager that this package has been "activated". ShortcutManager
|
||||
// will reset the throttling for this package.
|
||||
// Strictly speaking, the intent receiver may be different from the notification publisher,
|
||||
// but that's an edge case, and also because we can't always know which package will receive
|
||||
// an intent, so we just reset for the publisher.
|
||||
mContext.getSystemService(ShortcutManager.class).onApplicationActive(
|
||||
entry.getSbn().getPackageName(),
|
||||
entry.getSbn().getUser().getIdentifier());
|
||||
|
||||
try {
|
||||
pendingIntent.send(mContext, 0, intent);
|
||||
} catch (PendingIntent.CanceledException e) {
|
||||
Log.i(TAG, "Unable to send remote input result", e);
|
||||
}
|
||||
}
|
||||
|
||||
private void sendScreenshotToBubble(Bubble bubble) {
|
||||
// delay allows the bubble menu to disappear before the screenshot
|
||||
// done here because we already have a Handler to delay with.
|
||||
// TODO: Hide bubble + menu UI from screenshots entirely instead of just delaying.
|
||||
mHandler.postDelayed(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
mScreenshotHelper.takeScreenshot(
|
||||
android.view.WindowManager.TAKE_SCREENSHOT_FULLSCREEN,
|
||||
true /* hasStatus */,
|
||||
true /* hasNav */,
|
||||
mHandler,
|
||||
new Consumer<Uri>() {
|
||||
@Override
|
||||
public void accept(Uri uri) {
|
||||
if (uri != null) {
|
||||
NotificationEntry entry = bubble.getEntry();
|
||||
Pair<RemoteInput, Notification.Action> pair = entry.getSbn()
|
||||
.getNotification().findRemoteInputActionPair(false);
|
||||
RemoteInput remoteInput = pair.first;
|
||||
Notification.Action action = pair.second;
|
||||
Intent dataIntent = prepareRemoteInputFromData("image/png", uri,
|
||||
remoteInput, entry);
|
||||
sendRemoteInput(dataIntent, entry, action.actionIntent);
|
||||
mBubbleData.setSelectedBubble(bubble);
|
||||
mBubbleData.setExpanded(true);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}, 200);
|
||||
}
|
||||
|
||||
private final BubbleScreenshotListener mBubbleScreenshotListener =
|
||||
bubble -> sendScreenshotToBubble(bubble);
|
||||
}
|
||||
|
||||
@@ -68,6 +68,9 @@ public class BubbleExperimentConfig {
|
||||
|
||||
private static final String WHITELISTED_AUTO_BUBBLE_APPS = "whitelisted_auto_bubble_apps";
|
||||
|
||||
private static final String ALLOW_BUBBLE_MENU = "allow_bubble_screenshot_menu";
|
||||
private static final boolean ALLOW_BUBBLE_MENU_DEFAULT = false;
|
||||
|
||||
/**
|
||||
* When true, if a notification has the information necessary to bubble (i.e. valid
|
||||
* contentIntent and an icon or image), then a {@link android.app.Notification.BubbleMetadata}
|
||||
@@ -122,6 +125,16 @@ public class BubbleExperimentConfig {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* When true, show a menu when a bubble is long-pressed, which will allow the user to take
|
||||
* actions on that bubble.
|
||||
*/
|
||||
static boolean allowBubbleScreenshotMenu(Context context) {
|
||||
return Settings.Secure.getInt(context.getContentResolver(),
|
||||
ALLOW_BUBBLE_MENU,
|
||||
ALLOW_BUBBLE_MENU_DEFAULT ? 1 : 0) != 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* If {@link #allowAnyNotifToBubble(Context)} is true, this method creates and adds
|
||||
* {@link android.app.Notification.BubbleMetadata} to the notification entry as long as
|
||||
|
||||
@@ -0,0 +1,81 @@
|
||||
/*
|
||||
* Copyright (C) 2019 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.systemui.bubbles;
|
||||
|
||||
import android.content.Context;
|
||||
import android.util.AttributeSet;
|
||||
import android.view.View;
|
||||
import android.widget.FrameLayout;
|
||||
import android.widget.ImageView;
|
||||
|
||||
import com.android.systemui.R;
|
||||
|
||||
/**
|
||||
* Menu which allows users to take actions on bubbles, ex. screenshots.
|
||||
*/
|
||||
public class BubbleMenuView extends FrameLayout {
|
||||
private FrameLayout mMenu;
|
||||
private boolean mShowing = false;
|
||||
|
||||
public BubbleMenuView(Context context, AttributeSet attrs) {
|
||||
super(context, attrs);
|
||||
}
|
||||
|
||||
public BubbleMenuView(Context context) {
|
||||
super(context);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onFinishInflate() {
|
||||
super.onFinishInflate();
|
||||
mMenu = findViewById(R.id.bubble_menu_view);
|
||||
ImageView icon = findViewById(com.android.internal.R.id.icon);
|
||||
icon.setImageDrawable(mContext.getDrawable(com.android.internal.R.drawable.ic_screenshot));
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the bubble menu view.
|
||||
*/
|
||||
public View getMenuView() {
|
||||
return mMenu;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether the bubble menu is currently displayed.
|
||||
*/
|
||||
public boolean isShowing() {
|
||||
return mShowing;
|
||||
}
|
||||
|
||||
/**
|
||||
* Show the bubble menu at the specified position on the screen.
|
||||
*/
|
||||
public void show(float x, float y) {
|
||||
mShowing = true;
|
||||
this.setVisibility(VISIBLE);
|
||||
mMenu.setTranslationX(x);
|
||||
mMenu.setTranslationY(y);
|
||||
}
|
||||
|
||||
/**
|
||||
* Hide the bubble menu.
|
||||
*/
|
||||
public void hide() {
|
||||
mShowing = false;
|
||||
this.setVisibility(GONE);
|
||||
}
|
||||
}
|
||||
@@ -110,6 +110,7 @@ public class BubbleStackView extends FrameLayout {
|
||||
/** How long to wait, in milliseconds, before hiding the flyout. */
|
||||
@VisibleForTesting
|
||||
static final int FLYOUT_HIDE_AFTER = 5000;
|
||||
private BubbleController.BubbleScreenshotListener mBubbleScreenshotListener;
|
||||
|
||||
/**
|
||||
* Interface to synchronize {@link View} state and the screen.
|
||||
@@ -163,6 +164,7 @@ public class BubbleStackView extends FrameLayout {
|
||||
private ExpandedAnimationController mExpandedAnimationController;
|
||||
|
||||
private FrameLayout mExpandedViewContainer;
|
||||
@Nullable private BubbleMenuView mBubbleMenuView;
|
||||
|
||||
private BubbleFlyoutView mFlyout;
|
||||
/** Runnable that fades out the flyout and then sets it to GONE. */
|
||||
@@ -194,6 +196,7 @@ public class BubbleStackView extends FrameLayout {
|
||||
private int mPointerHeight;
|
||||
private int mStatusBarHeight;
|
||||
private int mImeOffset;
|
||||
private int mBubbleMenuOffset = 252;
|
||||
private BubbleIconFactory mBubbleIconFactory;
|
||||
private Bubble mExpandedBubble;
|
||||
private boolean mIsExpanded;
|
||||
@@ -492,6 +495,9 @@ public class BubbleStackView extends FrameLayout {
|
||||
mDesaturateAndDarkenPaint.setColorFilter(new ColorMatrixColorFilter(animatedMatrix));
|
||||
mDesaturateAndDarkenTargetView.setLayerPaint(mDesaturateAndDarkenPaint);
|
||||
});
|
||||
|
||||
mInflater.inflate(R.layout.bubble_menu_view, this);
|
||||
mBubbleMenuView = findViewById(R.id.bubble_menu_container);
|
||||
}
|
||||
|
||||
private void setUpFlyout() {
|
||||
@@ -682,6 +688,13 @@ public class BubbleStackView extends FrameLayout {
|
||||
mExpandListener = listener;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the screenshot listener.
|
||||
*/
|
||||
public void setBubbleScreenshotListener(BubbleController.BubbleScreenshotListener listener) {
|
||||
mBubbleScreenshotListener = listener;
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether the stack of bubbles is expanded or not.
|
||||
*/
|
||||
@@ -870,6 +883,12 @@ public class BubbleStackView extends FrameLayout {
|
||||
public View getTargetView(MotionEvent event) {
|
||||
float x = event.getRawX();
|
||||
float y = event.getRawY();
|
||||
if (mBubbleMenuView.isShowing()) {
|
||||
if (isIntersecting(mBubbleMenuView.getMenuView(), x, y)) {
|
||||
return mBubbleMenuView;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
if (mIsExpanded) {
|
||||
if (isIntersecting(mBubbleContainer, x, y)) {
|
||||
// Could be tapping or dragging a bubble while expanded
|
||||
@@ -1074,6 +1093,7 @@ public class BubbleStackView extends FrameLayout {
|
||||
return;
|
||||
}
|
||||
|
||||
hideBubbleMenu();
|
||||
mStackAnimationController.cancelStackPositionAnimations();
|
||||
mBubbleContainer.setActiveController(mStackAnimationController);
|
||||
hideFlyoutImmediate();
|
||||
@@ -1473,6 +1493,11 @@ public class BubbleStackView extends FrameLayout {
|
||||
|
||||
@Override
|
||||
public void getBoundsOnScreen(Rect outRect) {
|
||||
// If the bubble menu is open, the entire screen should capture touch events.
|
||||
if (mBubbleMenuView.isShowing()) {
|
||||
outRect.set(0, 0, getWidth(), getHeight());
|
||||
return;
|
||||
}
|
||||
if (!mIsExpanded) {
|
||||
if (mBubbleContainer.getChildCount() > 0) {
|
||||
mBubbleContainer.getChildAt(0).getBoundsOnScreen(outRect);
|
||||
@@ -1700,4 +1725,43 @@ public class BubbleStackView extends FrameLayout {
|
||||
}
|
||||
return bubbles;
|
||||
}
|
||||
|
||||
/**
|
||||
* Show the bubble menu, positioned relative to the stack.
|
||||
*/
|
||||
public void showBubbleMenu() {
|
||||
PointF currentPos = mStackAnimationController.getStackPosition();
|
||||
float yPos = currentPos.y;
|
||||
float xPos = currentPos.x;
|
||||
if (mStackAnimationController.isStackOnLeftSide()) {
|
||||
xPos += mBubbleSize;
|
||||
} else {
|
||||
//TODO: Use the width of the menu instead of this fixed offset. Offset used for now
|
||||
// because menu width isn't correct the first time the menu is shown.
|
||||
xPos -= mBubbleMenuOffset;
|
||||
}
|
||||
|
||||
mBubbleMenuView.show(xPos, yPos);
|
||||
}
|
||||
|
||||
/**
|
||||
* Hide the bubble menu.
|
||||
*/
|
||||
public void hideBubbleMenu() {
|
||||
mBubbleMenuView.hide();
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines whether the bubble menu is currently showing.
|
||||
*/
|
||||
public boolean isShowingBubbleMenu() {
|
||||
return mBubbleMenuView.isShowing();
|
||||
}
|
||||
|
||||
/**
|
||||
* Take a screenshot and send it to the specified bubble.
|
||||
*/
|
||||
public void sendScreenshotToBubble(Bubble bubble) {
|
||||
mBubbleScreenshotListener.onBubbleScreenshot(bubble);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -57,6 +57,7 @@ class BubbleTouchHandler implements View.OnTouchListener {
|
||||
private final PointF mViewPositionOnTouchDown = new PointF();
|
||||
private final BubbleStackView mStack;
|
||||
private final BubbleData mBubbleData;
|
||||
private final Context mContext;
|
||||
|
||||
private BubbleController mController = Dependency.get(BubbleController.class);
|
||||
|
||||
@@ -75,6 +76,7 @@ class BubbleTouchHandler implements View.OnTouchListener {
|
||||
mTouchSlopSquared = touchSlop * touchSlop;
|
||||
mBubbleData = bubbleData;
|
||||
mStack = stackView;
|
||||
mContext = context;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -91,15 +93,24 @@ class BubbleTouchHandler implements View.OnTouchListener {
|
||||
// anything, collapse the stack.
|
||||
if (action == MotionEvent.ACTION_OUTSIDE || mTouchedView == null) {
|
||||
mBubbleData.setExpanded(false);
|
||||
mStack.hideBubbleMenu();
|
||||
resetForNextGesture();
|
||||
return false;
|
||||
}
|
||||
|
||||
if (mTouchedView instanceof BubbleMenuView) {
|
||||
mStack.hideBubbleMenu();
|
||||
resetForNextGesture();
|
||||
mStack.sendScreenshotToBubble(mBubbleData.getSelectedBubble());
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!(mTouchedView instanceof BadgedImageView)
|
||||
&& !(mTouchedView instanceof BubbleStackView)
|
||||
&& !(mTouchedView instanceof BubbleFlyoutView)) {
|
||||
// Not touching anything touchable, but we shouldn't collapse (e.g. touching edge
|
||||
// of expanded view).
|
||||
mStack.hideBubbleMenu();
|
||||
resetForNextGesture();
|
||||
return false;
|
||||
}
|
||||
@@ -132,6 +143,10 @@ class BubbleTouchHandler implements View.OnTouchListener {
|
||||
|
||||
break;
|
||||
case MotionEvent.ACTION_MOVE:
|
||||
// block all further touch inputs once the menu is open
|
||||
if (mStack.isShowingBubbleMenu()) {
|
||||
return true;
|
||||
}
|
||||
trackMovement(event);
|
||||
final float deltaX = rawX - mTouchDown.x;
|
||||
final float deltaY = rawY - mTouchDown.y;
|
||||
@@ -148,6 +163,13 @@ class BubbleTouchHandler implements View.OnTouchListener {
|
||||
} else {
|
||||
mStack.onBubbleDragged(mTouchedView, viewX, viewY);
|
||||
}
|
||||
} else {
|
||||
float touchTime = event.getEventTime() - event.getDownTime();
|
||||
if (touchTime > ViewConfiguration.getLongPressTimeout() && !mStack.isExpanded()
|
||||
&& BubbleExperimentConfig.allowBubbleScreenshotMenu(mContext)) {
|
||||
mStack.showBubbleMenu();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
final boolean currentlyInDismissTarget = mStack.isInDismissTarget(event);
|
||||
@@ -171,6 +193,10 @@ class BubbleTouchHandler implements View.OnTouchListener {
|
||||
break;
|
||||
|
||||
case MotionEvent.ACTION_UP:
|
||||
if (mStack.isShowingBubbleMenu()) {
|
||||
resetForNextGesture();
|
||||
return true;
|
||||
}
|
||||
trackMovement(event);
|
||||
mVelocityTracker.computeCurrentVelocity(/* maxVelocity */ 1000);
|
||||
final float velX = mVelocityTracker.getXVelocity();
|
||||
@@ -261,7 +287,6 @@ class BubbleTouchHandler implements View.OnTouchListener {
|
||||
mVelocityTracker.recycle();
|
||||
mVelocityTracker = null;
|
||||
}
|
||||
|
||||
mTouchedView = null;
|
||||
mMovedEnough = false;
|
||||
mInDismissTarget = false;
|
||||
|
||||
@@ -82,6 +82,7 @@ import com.android.systemui.statusbar.phone.StatusBarWindowController;
|
||||
import com.android.systemui.statusbar.policy.BatteryController;
|
||||
import com.android.systemui.statusbar.policy.ConfigurationController;
|
||||
import com.android.systemui.statusbar.policy.HeadsUpManager;
|
||||
import com.android.systemui.statusbar.policy.RemoteInputUriController;
|
||||
import com.android.systemui.statusbar.policy.ZenModeController;
|
||||
import com.android.systemui.util.InjectionInflationController;
|
||||
|
||||
@@ -153,6 +154,8 @@ public class BubbleControllerTest extends SysuiTestCase {
|
||||
private Resources mResources;
|
||||
@Mock
|
||||
private Lazy<ShadeController> mShadeController;
|
||||
@Mock
|
||||
private RemoteInputUriController mRemoteInputUriController;
|
||||
|
||||
private SuperStatusBarViewFactory mSuperStatusBarViewFactory;
|
||||
private BubbleData mBubbleData;
|
||||
@@ -212,7 +215,8 @@ public class BubbleControllerTest extends SysuiTestCase {
|
||||
mZenModeController,
|
||||
mLockscreenUserManager,
|
||||
mNotificationGroupManager,
|
||||
mNotificationEntryManager);
|
||||
mNotificationEntryManager,
|
||||
mRemoteInputUriController);
|
||||
mBubbleController.setBubbleStateChangeListener(mBubbleStateChangeListener);
|
||||
mBubbleController.setExpandListener(mBubbleExpandListener);
|
||||
|
||||
@@ -708,11 +712,13 @@ public class BubbleControllerTest extends SysuiTestCase {
|
||||
ZenModeController zenModeController,
|
||||
NotificationLockscreenUserManager lockscreenUserManager,
|
||||
NotificationGroupManager groupManager,
|
||||
NotificationEntryManager entryManager) {
|
||||
NotificationEntryManager entryManager,
|
||||
RemoteInputUriController remoteInputUriController) {
|
||||
super(context,
|
||||
statusBarWindowController, statusBarStateController, shadeController,
|
||||
data, Runnable::run, configurationController, interruptionStateProvider,
|
||||
zenModeController, lockscreenUserManager, groupManager, entryManager);
|
||||
zenModeController, lockscreenUserManager, groupManager, entryManager,
|
||||
remoteInputUriController);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user