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

Commit 06a09a93 authored by TreeHugger Robot's avatar TreeHugger Robot Committed by Android (Google) Code Review
Browse files

Merge "Remove bubble screenshot menu."

parents 94a9f216 c4381c47
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() {
@@ -737,13 +732,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)) {
                if (BubbleExperimentConfig.allowBubbleOverflow(mContext)
@@ -1168,7 +1150,6 @@ public class BubbleStackView extends FrameLayout {
            }
            return;
        }
        hideBubbleMenu();
        mStackAnimationController.cancelStackPositionAnimations();
        mBubbleContainer.setActiveController(mStackAnimationController);
        hideFlyoutImmediate();
@@ -1570,10 +1551,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);
@@ -1808,50 +1785,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
@@ -58,14 +58,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;
@@ -78,7 +76,6 @@ class BubbleTouchHandler implements View.OnTouchListener {
        mTouchSlopSquared = touchSlop * touchSlop;
        mBubbleData = bubbleData;
        mStack = stackView;
        mContext = context;
    }

    @Override
@@ -95,18 +92,10 @@ 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)) {
@@ -116,7 +105,6 @@ class BubbleTouchHandler implements View.OnTouchListener {
            }
            // Not touching anything touchable, but we shouldn't collapse (e.g. touching edge
            // of expanded view).
            mStack.hideBubbleMenu();
            resetForNextGesture();
            return false;
        }
@@ -139,12 +127,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 {
@@ -155,10 +137,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;
@@ -168,7 +146,6 @@ class BubbleTouchHandler implements View.OnTouchListener {
                }

                if (mMovedEnough) {
                    mStack.removeCallbacks(mShowBubbleMenuRunnable);
                    if (isStack) {
                        mStack.onDragged(viewX, viewY);
                    } else if (isFlyout) {
@@ -199,12 +176,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