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

Commit 06a02127 authored by Miranda Kephart's avatar Miranda Kephart Committed by Automerger Merge Worker
Browse files

Merge "Move screenshot receivers and add tests" into rvc-qpr-dev am: 08a2a77e

Original change: https://googleplex-android-review.googlesource.com/c/platform/frameworks/base/+/12186963

Change-Id: I4ad21b7c1e4e3d82f03b9e006fe1453cee3710a3
parents 85b5b118 08a2a77e
Loading
Loading
Loading
Loading
+3 −7
Original line number Diff line number Diff line
@@ -395,19 +395,15 @@

        <!-- Springboard for launching the share and edit activity. This needs to be in the main
             system ui process since we need to notify the status bar to dismiss the keyguard -->
        <receiver android:name=".screenshot.GlobalScreenshot$ActionProxyReceiver"
            android:exported="false" />

        <!-- Callback for dismissing screenshot notification after a share target is picked -->
        <receiver android:name=".screenshot.GlobalScreenshot$TargetChosenReceiver"
        <receiver android:name=".screenshot.ActionProxyReceiver"
            android:exported="false" />

        <!-- Callback for deleting screenshot notification -->
        <receiver android:name=".screenshot.GlobalScreenshot$DeleteScreenshotReceiver"
        <receiver android:name=".screenshot.DeleteScreenshotReceiver"
            android:exported="false" />

        <!-- Callback for invoking a smart action from the screenshot notification. -->
        <receiver android:name=".screenshot.GlobalScreenshot$SmartActionsReceiver"
        <receiver android:name=".screenshot.SmartActionsReceiver"
                  android:exported="false"/>

        <!-- started from UsbDeviceSettingsManager -->
+25 −2
Original line number Diff line number Diff line
@@ -18,7 +18,9 @@ package com.android.systemui.dagger;

import android.content.BroadcastReceiver;

import com.android.systemui.screenshot.GlobalScreenshot.ActionProxyReceiver;
import com.android.systemui.screenshot.ActionProxyReceiver;
import com.android.systemui.screenshot.DeleteScreenshotReceiver;
import com.android.systemui.screenshot.SmartActionsReceiver;

import dagger.Binds;
import dagger.Module;
@@ -30,10 +32,31 @@ import dagger.multibindings.IntoMap;
 */
@Module
public abstract class DefaultBroadcastReceiverBinder {
    /** */
    /**
     *
     */
    @Binds
    @IntoMap
    @ClassKey(ActionProxyReceiver.class)
    public abstract BroadcastReceiver bindActionProxyReceiver(
            ActionProxyReceiver broadcastReceiver);

    /**
     *
     */
    @Binds
    @IntoMap
    @ClassKey(DeleteScreenshotReceiver.class)
    public abstract BroadcastReceiver bindDeleteScreenshotReceiver(
            DeleteScreenshotReceiver broadcastReceiver);

    /**
     *
     */
    @Binds
    @IntoMap
    @ClassKey(SmartActionsReceiver.class)
    public abstract BroadcastReceiver bindSmartActionsReceiver(
            SmartActionsReceiver broadcastReceiver);

}
+105 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2020 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.screenshot;

import static com.android.systemui.screenshot.GlobalScreenshot.ACTION_TYPE_EDIT;
import static com.android.systemui.screenshot.GlobalScreenshot.ACTION_TYPE_SHARE;
import static com.android.systemui.screenshot.GlobalScreenshot.EXTRA_ACTION_INTENT;
import static com.android.systemui.screenshot.GlobalScreenshot.EXTRA_DISALLOW_ENTER_PIP;
import static com.android.systemui.screenshot.GlobalScreenshot.EXTRA_ID;
import static com.android.systemui.screenshot.GlobalScreenshot.EXTRA_SMART_ACTIONS_ENABLED;
import static com.android.systemui.statusbar.phone.StatusBar.SYSTEM_DIALOG_REASON_SCREENSHOT;

import android.app.ActivityOptions;
import android.app.PendingIntent;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.util.Log;

import com.android.systemui.shared.system.ActivityManagerWrapper;
import com.android.systemui.statusbar.phone.StatusBar;

import java.util.Optional;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;

import javax.inject.Inject;

/**
 * Receiver to proxy the share or edit intent, used to clean up the notification and send
 * appropriate signals to the system (ie. to dismiss the keyguard if necessary).
 */
public class ActionProxyReceiver extends BroadcastReceiver {
    private static final String TAG = "ActionProxyReceiver";

    private static final int CLOSE_WINDOWS_TIMEOUT_MILLIS = 3000;
    private final StatusBar mStatusBar;
    private final ActivityManagerWrapper mActivityManagerWrapper;
    private final ScreenshotSmartActions mScreenshotSmartActions;

    @Inject
    public ActionProxyReceiver(Optional<StatusBar> statusBar,
            ActivityManagerWrapper activityManagerWrapper,
            ScreenshotSmartActions screenshotSmartActions) {
        mStatusBar = statusBar.orElse(null);
        mActivityManagerWrapper = activityManagerWrapper;
        mScreenshotSmartActions = screenshotSmartActions;
    }

    @Override
    public void onReceive(Context context, final Intent intent) {
        Runnable startActivityRunnable = () -> {
            try {
                mActivityManagerWrapper.closeSystemWindows(
                        SYSTEM_DIALOG_REASON_SCREENSHOT).get(
                        CLOSE_WINDOWS_TIMEOUT_MILLIS, TimeUnit.MILLISECONDS);
            } catch (TimeoutException | InterruptedException | ExecutionException e) {
                Log.e(TAG, "Unable to share screenshot", e);
                return;
            }

            PendingIntent actionIntent = intent.getParcelableExtra(EXTRA_ACTION_INTENT);
            ActivityOptions opts = ActivityOptions.makeBasic();
            opts.setDisallowEnterPictureInPictureWhileLaunching(
                    intent.getBooleanExtra(EXTRA_DISALLOW_ENTER_PIP, false));
            try {
                actionIntent.send(context, 0, null, null, null, null, opts.toBundle());
            } catch (PendingIntent.CanceledException e) {
                Log.e(TAG, "Pending intent canceled", e);
            }

        };

        if (mStatusBar != null) {
            mStatusBar.executeRunnableDismissingKeyguard(startActivityRunnable, null,
                    true /* dismissShade */, true /* afterKeyguardGone */,
                    true /* deferred */);
        } else {
            startActivityRunnable.run();
        }

        if (intent.getBooleanExtra(EXTRA_SMART_ACTIONS_ENABLED, false)) {
            String actionType = Intent.ACTION_EDIT.equals(intent.getAction())
                    ? ACTION_TYPE_EDIT
                    : ACTION_TYPE_SHARE;
            mScreenshotSmartActions.notifyScreenshotAction(
                    context, intent.getStringExtra(EXTRA_ID), actionType, false);
        }
    }
}
+68 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2019 The Android Open Source Project
 * Copyright (C) 2020 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.
@@ -16,28 +16,53 @@

package com.android.systemui.screenshot;

import static com.android.systemui.screenshot.GlobalScreenshot.ACTION_TYPE_DELETE;
import static com.android.systemui.screenshot.GlobalScreenshot.EXTRA_ID;
import static com.android.systemui.screenshot.GlobalScreenshot.EXTRA_SMART_ACTIONS_ENABLED;
import static com.android.systemui.screenshot.GlobalScreenshot.SCREENSHOT_URI_ID;

import android.content.BroadcastReceiver;
import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
import android.net.Uri;
import android.os.AsyncTask;

import com.android.systemui.dagger.qualifiers.Background;

import java.util.concurrent.Executor;

import javax.inject.Inject;

/**
 * An AsyncTask that deletes an image from the media store in the background.
 * Removes the file at a provided URI.
 */
class DeleteImageInBackgroundTask extends AsyncTask<Uri, Void, Void> {
    private Context mContext;
public class DeleteScreenshotReceiver extends BroadcastReceiver {

    DeleteImageInBackgroundTask(Context context) {
        mContext = context;
    private final ScreenshotSmartActions mScreenshotSmartActions;
    private final Executor mBackgroundExecutor;

    @Inject
    public DeleteScreenshotReceiver(ScreenshotSmartActions screenshotSmartActions,
            @Background Executor backgroundExecutor) {
        mScreenshotSmartActions = screenshotSmartActions;
        mBackgroundExecutor = backgroundExecutor;
    }

    @Override
    protected Void doInBackground(Uri... params) {
        if (params.length != 1) return null;
    public void onReceive(Context context, Intent intent) {
        if (!intent.hasExtra(SCREENSHOT_URI_ID)) {
            return;
        }

        Uri screenshotUri = params[0];
        ContentResolver resolver = mContext.getContentResolver();
        resolver.delete(screenshotUri, null, null);
        return null;
        // And delete the image from the media store
        final Uri uri = Uri.parse(intent.getStringExtra(SCREENSHOT_URI_ID));
        mBackgroundExecutor.execute(() -> {
            ContentResolver resolver = context.getContentResolver();
            resolver.delete(uri, null, null);
        });
        if (intent.getBooleanExtra(EXTRA_SMART_ACTIONS_ENABLED, false)) {
            mScreenshotSmartActions.notifyScreenshotAction(
                    context, intent.getStringExtra(EXTRA_ID), ACTION_TYPE_DELETE, false);
        }
    }
}
+4 −130
Original line number Diff line number Diff line
@@ -21,8 +21,6 @@ import static android.content.res.Configuration.ORIENTATION_PORTRAIT;
import static android.view.WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;

import static com.android.systemui.statusbar.phone.StatusBar.SYSTEM_DIALOG_REASON_SCREENSHOT;

import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.AnimatorSet;
@@ -30,13 +28,10 @@ import android.animation.ValueAnimator;
import android.annotation.Nullable;
import android.annotation.SuppressLint;
import android.app.ActivityManager;
import android.app.ActivityOptions;
import android.app.Notification;
import android.app.PendingIntent;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.res.Configuration;
import android.content.res.Resources;
import android.graphics.Bitmap;
@@ -62,7 +57,6 @@ import android.provider.Settings;
import android.util.DisplayMetrics;
import android.util.Log;
import android.util.MathUtils;
import android.util.Slog;
import android.view.Display;
import android.view.KeyEvent;
import android.view.LayoutInflater;
@@ -87,23 +81,15 @@ import android.widget.Toast;
import com.android.internal.logging.UiEventLogger;
import com.android.systemui.R;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.shared.system.ActivityManagerWrapper;
import com.android.systemui.shared.system.QuickStepContract;
import com.android.systemui.statusbar.phone.StatusBar;

import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import java.util.function.Consumer;

import javax.inject.Inject;
import javax.inject.Singleton;

import dagger.Lazy;

/**
 * Class for handling device screen shots
 */
@@ -192,6 +178,7 @@ public class GlobalScreenshot implements ViewTreeObserver.OnComputeInternalInset
    private final UiEventLogger mUiEventLogger;

    private final Context mContext;
    private final ScreenshotSmartActions mScreenshotSmartActions;
    private final WindowManager mWindowManager;
    private final WindowManager.LayoutParams mWindowLayoutParams;
    private final Display mDisplay;
@@ -247,9 +234,11 @@ public class GlobalScreenshot implements ViewTreeObserver.OnComputeInternalInset
    @Inject
    public GlobalScreenshot(
            Context context, @Main Resources resources,
            ScreenshotSmartActions screenshotSmartActions,
            ScreenshotNotificationsController screenshotNotificationsController,
            UiEventLogger uiEventLogger) {
        mContext = context;
        mScreenshotSmartActions = screenshotSmartActions;
        mNotificationsController = screenshotNotificationsController;
        mUiEventLogger = uiEventLogger;

@@ -704,7 +693,7 @@ public class GlobalScreenshot implements ViewTreeObserver.OnComputeInternalInset
            });
        }

        mSaveInBgTask = new SaveImageInBackgroundTask(mContext, data);
        mSaveInBgTask = new SaveImageInBackgroundTask(mContext, mScreenshotSmartActions, data);
        mSaveInBgTask.execute();
    }

@@ -1116,119 +1105,4 @@ public class GlobalScreenshot implements ViewTreeObserver.OnComputeInternalInset
            return insetDrawable;
        }
    }

    /**
     * Receiver to proxy the share or edit intent, used to clean up the notification and send
     * appropriate signals to the system (ie. to dismiss the keyguard if necessary).
     */
    public static class ActionProxyReceiver extends BroadcastReceiver {
        static final int CLOSE_WINDOWS_TIMEOUT_MILLIS = 3000;
        private final StatusBar mStatusBar;

        @Inject
        public ActionProxyReceiver(Optional<Lazy<StatusBar>> statusBarLazy) {
            Lazy<StatusBar> statusBar = statusBarLazy.orElse(null);
            mStatusBar = statusBar != null ? statusBar.get() : null;
        }

        @Override
        public void onReceive(Context context, final Intent intent) {
            Runnable startActivityRunnable = () -> {
                try {
                    ActivityManagerWrapper.getInstance().closeSystemWindows(
                            SYSTEM_DIALOG_REASON_SCREENSHOT).get(
                            CLOSE_WINDOWS_TIMEOUT_MILLIS, TimeUnit.MILLISECONDS);
                } catch (TimeoutException | InterruptedException | ExecutionException e) {
                    Slog.e(TAG, "Unable to share screenshot", e);
                    return;
                }

                PendingIntent actionIntent = intent.getParcelableExtra(EXTRA_ACTION_INTENT);
                if (intent.getBooleanExtra(EXTRA_CANCEL_NOTIFICATION, false)) {
                    ScreenshotNotificationsController.cancelScreenshotNotification(context);
                }
                ActivityOptions opts = ActivityOptions.makeBasic();
                opts.setDisallowEnterPictureInPictureWhileLaunching(
                        intent.getBooleanExtra(EXTRA_DISALLOW_ENTER_PIP, false));
                try {
                    actionIntent.send(context, 0, null, null, null, null, opts.toBundle());
                } catch (PendingIntent.CanceledException e) {
                    Log.e(TAG, "Pending intent canceled", e);
                }

            };

            if (mStatusBar != null) {
                mStatusBar.executeRunnableDismissingKeyguard(startActivityRunnable, null,
                        true /* dismissShade */, true /* afterKeyguardGone */,
                        true /* deferred */);
            } else {
                startActivityRunnable.run();
            }

            if (intent.getBooleanExtra(EXTRA_SMART_ACTIONS_ENABLED, false)) {
                String actionType = Intent.ACTION_EDIT.equals(intent.getAction())
                        ? ACTION_TYPE_EDIT
                        : ACTION_TYPE_SHARE;
                ScreenshotSmartActions.notifyScreenshotAction(
                        context, intent.getStringExtra(EXTRA_ID), actionType, false);
            }
        }
    }

    /**
     * Removes the notification for a screenshot after a share target is chosen.
     */
    public static class TargetChosenReceiver extends BroadcastReceiver {
        @Override
        public void onReceive(Context context, Intent intent) {
            // Clear the notification only after the user has chosen a share action
            ScreenshotNotificationsController.cancelScreenshotNotification(context);
        }
    }

    /**
     * Removes the last screenshot.
     */
    public static class DeleteScreenshotReceiver extends BroadcastReceiver {
        @Override
        public void onReceive(Context context, Intent intent) {
            if (!intent.hasExtra(SCREENSHOT_URI_ID)) {
                return;
            }

            // Clear the notification when the image is deleted
            ScreenshotNotificationsController.cancelScreenshotNotification(context);

            // And delete the image from the media store
            final Uri uri = Uri.parse(intent.getStringExtra(SCREENSHOT_URI_ID));
            new DeleteImageInBackgroundTask(context).execute(uri);
            if (intent.getBooleanExtra(EXTRA_SMART_ACTIONS_ENABLED, false)) {
                ScreenshotSmartActions.notifyScreenshotAction(
                        context, intent.getStringExtra(EXTRA_ID), ACTION_TYPE_DELETE, false);
            }
        }
    }

    /**
     * Executes the smart action tapped by the user in the notification.
     */
    public static class SmartActionsReceiver extends BroadcastReceiver {
        @Override
        public void onReceive(Context context, Intent intent) {
            PendingIntent pendingIntent = intent.getParcelableExtra(EXTRA_ACTION_INTENT);
            String actionType = intent.getStringExtra(EXTRA_ACTION_TYPE);
            Slog.d(TAG, "Executing smart action [" + actionType + "]:" + pendingIntent.getIntent());
            ActivityOptions opts = ActivityOptions.makeBasic();

            try {
                pendingIntent.send(context, 0, null, null, null, null, opts.toBundle());
            } catch (PendingIntent.CanceledException e) {
                Log.e(TAG, "Pending intent canceled", e);
            }

            ScreenshotSmartActions.notifyScreenshotAction(
                    context, intent.getStringExtra(EXTRA_ID), actionType, true);
        }
    }
}
Loading