Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit c4381c47 authored by Aran Ink's avatar Aran Ink
Browse files

Remove bubble screenshot menu.

Test: Manual. Bubble menu no longer appears when long-pressing bubbles with "allow_bubble_screenshot_menu" enabled.

Bug: 144113115

Change-Id: I64b79e0b44e326e8a037ea20cd2cb9be46e1cd8d
parent 70ee4c46
Loading
Loading
Loading
Loading
+3 −98
Original line number Diff line number Diff line
@@ -44,19 +44,13 @@ 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;
@@ -75,7 +69,6 @@ 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;
@@ -93,7 +86,6 @@ import com.android.systemui.statusbar.phone.NotificationShadeWindowController;
import com.android.systemui.statusbar.phone.ShadeController;
import com.android.systemui.statusbar.phone.StatusBar;
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;
@@ -101,10 +93,8 @@ 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;
@@ -146,8 +136,6 @@ public class BubbleController implements ConfigurationController.ConfigurationLi
    @Nullable private BubbleStackView.SurfaceSynchronizer mSurfaceSynchronizer;
    private final NotificationGroupManager mNotificationGroupManager;
    private final ShadeController mShadeController;
    private final RemoteInputUriController mRemoteInputUriController;
    private Handler mHandler = new Handler() {};

    private BubbleData mBubbleData;
    @Nullable private BubbleStackView mStackView;
@@ -171,7 +159,6 @@ public class BubbleController implements ConfigurationController.ConfigurationLi
    private final NotificationShadeWindowController mNotificationShadeWindowController;
    private final ZenModeController mZenModeController;
    private StatusBarStateListener mStatusBarStateListener;
    private final ScreenshotHelper mScreenshotHelper;

    // Callback that updates BubbleOverflowActivity on data change.
    @Nullable private Runnable mOverflowCallback = null;
@@ -216,16 +203,6 @@ 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);
    }

    /**
     * Listener to be notified when a bubbles' notification suppression state changes.
     */
@@ -300,12 +277,10 @@ public class BubbleController implements ConfigurationController.ConfigurationLi
            ZenModeController zenModeController,
            NotificationLockscreenUserManager notifUserManager,
            NotificationGroupManager groupManager,
            NotificationEntryManager entryManager,
            RemoteInputUriController remoteInputUriController) {
            NotificationEntryManager entryManager) {
        this(context, notificationShadeWindowController, statusBarStateController, shadeController,
                data, null /* synchronizer */, configurationController, interruptionStateProvider,
                zenModeController, notifUserManager, groupManager, entryManager,
                remoteInputUriController);
                zenModeController, notifUserManager, groupManager, entryManager);
    }

    public BubbleController(Context context,
@@ -319,14 +294,12 @@ public class BubbleController implements ConfigurationController.ConfigurationLi
            ZenModeController zenModeController,
            NotificationLockscreenUserManager notifUserManager,
            NotificationGroupManager groupManager,
            NotificationEntryManager entryManager,
            RemoteInputUriController remoteInputUriController) {
            NotificationEntryManager entryManager) {
        mContext = context;
        mShadeController = shadeController;
        mNotificationInterruptionStateProvider = interruptionStateProvider;
        mNotifUserManager = notifUserManager;
        mZenModeController = zenModeController;
        mRemoteInputUriController = remoteInputUriController;
        mZenModeController.addCallback(new ZenModeController.Callback() {
            @Override
            public void onZenChanged(int zen) {
@@ -399,7 +372,6 @@ public class BubbleController implements ConfigurationController.ConfigurationLi
        mUserCreatedBubbles = new HashSet<>();
        mUserBlockedBubbles = new HashSet<>();

        mScreenshotHelper = new ScreenshotHelper(context);
        mBubbleIconFactory = new BubbleIconFactory(context);
    }

@@ -536,9 +508,6 @@ public class BubbleController implements ConfigurationController.ConfigurationLi
            if (mExpandListener != null) {
                mStackView.setExpandListener(mExpandListener);
            }
            if (mBubbleScreenshotListener != null) {
                mStackView.setBubbleScreenshotListener(mBubbleScreenshotListener);
            }
        }
    }

@@ -1267,68 +1236,4 @@ 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) {
        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);
                            if (pair != null) {
                                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);
                            } else {
                                Log.w(TAG, "No RemoteInput found for notification: "
                                        + entry.getSbn().getKey());
                            }
                        }
                    }
                });
    }

    private final BubbleScreenshotListener mBubbleScreenshotListener =
            bubble -> sendScreenshotToBubble(bubble);
}
+0 −13
Original line number Diff line number Diff line
@@ -73,9 +73,6 @@ 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;

    private static final String ALLOW_BUBBLE_OVERFLOW = "allow_bubble_overflow";
    private static final boolean ALLOW_BUBBLE_OVERFLOW_DEFAULT = false;

@@ -133,16 +130,6 @@ 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;
    }

    /**
     * When true, show a menu when a bubble is long-pressed, which will allow the user to take
     * actions on that bubble.
+0 −84
Original line number Diff line number Diff line
/*
 * 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;

    /** Delay before taking a screenshot once the button is tapped to allow the menu time to hide.*/
    public static final long SCREENSHOT_DELAY = 200;

    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);
    }
}
+0 −69
Original line number Diff line number Diff line
@@ -115,7 +115,6 @@ 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.
@@ -169,7 +168,6 @@ 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. */
@@ -516,9 +514,6 @@ 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 setUpOverflow() {
@@ -741,13 +736,6 @@ 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.
     */
@@ -942,12 +930,6 @@ 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
@@ -1164,7 +1146,6 @@ public class BubbleStackView extends FrameLayout {
            }
            return;
        }
        hideBubbleMenu();
        mStackAnimationController.cancelStackPositionAnimations();
        mBubbleContainer.setActiveController(mStackAnimationController);
        hideFlyoutImmediate();
@@ -1566,10 +1547,6 @@ 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 (getBubbleCount() > 0) {
                mBubbleContainer.getChildAt(0).getBoundsOnScreen(outRect);
@@ -1804,50 +1781,4 @@ public class BubbleStackView extends FrameLayout {
        }
        return bubbles;
    }

    /**
     * Show the bubble menu, positioned relative to the stack.
     */
    public void showBubbleMenu() {
        PointF currentPos = mStackAnimationController.getStackPosition();
        mBubbleMenuView.setVisibility(View.INVISIBLE);
        post(() -> {
            float yPos = currentPos.y;
            float xPos = currentPos.x;
            if (mStackAnimationController.isStackOnLeftSide()) {
                xPos += mBubbleSize;
            } else {
                xPos -= mBubbleMenuView.getMenuView().getWidth();
            }

            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) {
        hideBubbleMenu();
        // 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.
        postDelayed(() -> {
            mBubbleScreenshotListener.onBubbleScreenshot(bubble);
        }, BubbleMenuView.SCREENSHOT_DELAY);
    }
}
+0 −29
Original line number Diff line number Diff line
@@ -57,14 +57,12 @@ 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);

    private boolean mMovedEnough;
    private int mTouchSlopSquared;
    private VelocityTracker mVelocityTracker;
    private Runnable mShowBubbleMenuRunnable;

    /** View that was initially touched, when we received the first ACTION_DOWN event. */
    private View mTouchedView;
@@ -77,7 +75,6 @@ class BubbleTouchHandler implements View.OnTouchListener {
        mTouchSlopSquared = touchSlop * touchSlop;
        mBubbleData = bubbleData;
        mStack = stackView;
        mContext = context;
    }

    @Override
@@ -94,24 +91,15 @@ 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;
        }
@@ -134,12 +122,6 @@ class BubbleTouchHandler implements View.OnTouchListener {
                if (isStack) {
                    mViewPositionOnTouchDown.set(mStack.getStackPosition());
                    mStack.onDragStart();
                    if (!mStack.isShowingBubbleMenu() && !mStack.isExpanded()
                            && BubbleExperimentConfig.allowBubbleScreenshotMenu(mContext)) {
                        mShowBubbleMenuRunnable = mStack::showBubbleMenu;
                        mStack.postDelayed(mShowBubbleMenuRunnable,
                                ViewConfiguration.getLongPressTimeout());
                    }
                } else if (isFlyout) {
                    mStack.onFlyoutDragStart();
                } else {
@@ -150,10 +132,6 @@ 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;
@@ -163,7 +141,6 @@ class BubbleTouchHandler implements View.OnTouchListener {
                }

                if (mMovedEnough) {
                    mStack.removeCallbacks(mShowBubbleMenuRunnable);
                    if (isStack) {
                        mStack.onDragged(viewX, viewY);
                    } else if (isFlyout) {
@@ -194,12 +171,6 @@ class BubbleTouchHandler implements View.OnTouchListener {
                break;

            case MotionEvent.ACTION_UP:
                if (mStack.isShowingBubbleMenu()) {
                    resetForNextGesture();
                    return true;
                } else {
                    mStack.removeCallbacks(mShowBubbleMenuRunnable);
                }
                trackMovement(event);
                mVelocityTracker.computeCurrentVelocity(/* maxVelocity */ 1000);
                final float velX = mVelocityTracker.getXVelocity();
Loading