Merge "Move screenshot receivers and add tests" into rvc-qpr-dev
This commit is contained in:
committed by
Android (Google) Code Review
commit
08a2a77e65
@@ -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 -->
|
||||
|
||||
@@ -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);
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,105 @@
|
||||
/*
|
||||
* 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,43 +0,0 @@
|
||||
/*
|
||||
* 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.screenshot;
|
||||
|
||||
import android.content.ContentResolver;
|
||||
import android.content.Context;
|
||||
import android.net.Uri;
|
||||
import android.os.AsyncTask;
|
||||
|
||||
/**
|
||||
* An AsyncTask that deletes an image from the media store in the background.
|
||||
*/
|
||||
class DeleteImageInBackgroundTask extends AsyncTask<Uri, Void, Void> {
|
||||
private Context mContext;
|
||||
|
||||
DeleteImageInBackgroundTask(Context context) {
|
||||
mContext = context;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Void doInBackground(Uri... params) {
|
||||
if (params.length != 1) return null;
|
||||
|
||||
Uri screenshotUri = params[0];
|
||||
ContentResolver resolver = mContext.getContentResolver();
|
||||
resolver.delete(screenshotUri, null, null);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
/*
|
||||
* 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_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 com.android.systemui.dagger.qualifiers.Background;
|
||||
|
||||
import java.util.concurrent.Executor;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
/**
|
||||
* Removes the file at a provided URI.
|
||||
*/
|
||||
public class DeleteScreenshotReceiver extends BroadcastReceiver {
|
||||
|
||||
private final ScreenshotSmartActions mScreenshotSmartActions;
|
||||
private final Executor mBackgroundExecutor;
|
||||
|
||||
@Inject
|
||||
public DeleteScreenshotReceiver(ScreenshotSmartActions screenshotSmartActions,
|
||||
@Background Executor backgroundExecutor) {
|
||||
mScreenshotSmartActions = screenshotSmartActions;
|
||||
mBackgroundExecutor = backgroundExecutor;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onReceive(Context context, Intent intent) {
|
||||
if (!intent.hasExtra(SCREENSHOT_URI_ID)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -81,6 +81,7 @@ class SaveImageInBackgroundTask extends AsyncTask<Void, Void, Void> {
|
||||
private static final String SCREENSHOT_SHARE_SUBJECT_TEMPLATE = "Screenshot (%s)";
|
||||
|
||||
private final Context mContext;
|
||||
private final ScreenshotSmartActions mScreenshotSmartActions;
|
||||
private final GlobalScreenshot.SaveImageInBackgroundData mParams;
|
||||
private final GlobalScreenshot.SavedImageData mImageData;
|
||||
private final String mImageFileName;
|
||||
@@ -90,8 +91,10 @@ class SaveImageInBackgroundTask extends AsyncTask<Void, Void, Void> {
|
||||
private final boolean mSmartActionsEnabled;
|
||||
private final Random mRandom = new Random();
|
||||
|
||||
SaveImageInBackgroundTask(Context context, GlobalScreenshot.SaveImageInBackgroundData data) {
|
||||
SaveImageInBackgroundTask(Context context, ScreenshotSmartActions screenshotSmartActions,
|
||||
GlobalScreenshot.SaveImageInBackgroundData data) {
|
||||
mContext = context;
|
||||
mScreenshotSmartActions = screenshotSmartActions;
|
||||
mImageData = new GlobalScreenshot.SavedImageData();
|
||||
|
||||
// Prepare all the output metadata
|
||||
@@ -141,7 +144,7 @@ class SaveImageInBackgroundTask extends AsyncTask<Void, Void, Void> {
|
||||
final Uri uri = resolver.insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, values);
|
||||
|
||||
CompletableFuture<List<Notification.Action>> smartActionsFuture =
|
||||
ScreenshotSmartActions.getSmartActionsFuture(
|
||||
mScreenshotSmartActions.getSmartActionsFuture(
|
||||
mScreenshotId, uri, image, mSmartActionsProvider,
|
||||
mSmartActionsEnabled, getUserHandle(mContext));
|
||||
|
||||
@@ -199,7 +202,7 @@ class SaveImageInBackgroundTask extends AsyncTask<Void, Void, Void> {
|
||||
SystemUiDeviceConfigFlags.SCREENSHOT_NOTIFICATION_SMART_ACTIONS_TIMEOUT_MS,
|
||||
1000);
|
||||
smartActions.addAll(buildSmartActions(
|
||||
ScreenshotSmartActions.getSmartActions(
|
||||
mScreenshotSmartActions.getSmartActions(
|
||||
mScreenshotId, smartActionsFuture, timeoutMs,
|
||||
mSmartActionsProvider),
|
||||
mContext));
|
||||
@@ -274,11 +277,8 @@ class SaveImageInBackgroundTask extends AsyncTask<Void, Void, Void> {
|
||||
// by setting the (otherwise unused) request code to the current user id.
|
||||
int requestCode = context.getUserId();
|
||||
|
||||
PendingIntent chooserAction = PendingIntent.getBroadcast(context, requestCode,
|
||||
new Intent(context, GlobalScreenshot.TargetChosenReceiver.class),
|
||||
PendingIntent.FLAG_CANCEL_CURRENT | PendingIntent.FLAG_ONE_SHOT);
|
||||
Intent sharingChooserIntent =
|
||||
Intent.createChooser(sharingIntent, null, chooserAction.getIntentSender())
|
||||
Intent.createChooser(sharingIntent, null)
|
||||
.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK | Intent.FLAG_ACTIVITY_NEW_TASK)
|
||||
.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
|
||||
|
||||
@@ -288,7 +288,7 @@ class SaveImageInBackgroundTask extends AsyncTask<Void, Void, Void> {
|
||||
|
||||
// Create a share action for the notification
|
||||
PendingIntent shareAction = PendingIntent.getBroadcastAsUser(context, requestCode,
|
||||
new Intent(context, GlobalScreenshot.ActionProxyReceiver.class)
|
||||
new Intent(context, ActionProxyReceiver.class)
|
||||
.putExtra(GlobalScreenshot.EXTRA_ACTION_INTENT, pendingIntent)
|
||||
.putExtra(GlobalScreenshot.EXTRA_DISALLOW_ENTER_PIP, true)
|
||||
.putExtra(GlobalScreenshot.EXTRA_ID, mScreenshotId)
|
||||
@@ -333,10 +333,8 @@ class SaveImageInBackgroundTask extends AsyncTask<Void, Void, Void> {
|
||||
|
||||
// Create a edit action
|
||||
PendingIntent editAction = PendingIntent.getBroadcastAsUser(context, requestCode,
|
||||
new Intent(context, GlobalScreenshot.ActionProxyReceiver.class)
|
||||
new Intent(context, ActionProxyReceiver.class)
|
||||
.putExtra(GlobalScreenshot.EXTRA_ACTION_INTENT, pendingIntent)
|
||||
.putExtra(GlobalScreenshot.EXTRA_CANCEL_NOTIFICATION,
|
||||
editIntent.getComponent() != null)
|
||||
.putExtra(GlobalScreenshot.EXTRA_ID, mScreenshotId)
|
||||
.putExtra(GlobalScreenshot.EXTRA_SMART_ACTIONS_ENABLED,
|
||||
mSmartActionsEnabled)
|
||||
@@ -358,7 +356,7 @@ class SaveImageInBackgroundTask extends AsyncTask<Void, Void, Void> {
|
||||
|
||||
// Create a delete action for the notification
|
||||
PendingIntent deleteAction = PendingIntent.getBroadcast(context, requestCode,
|
||||
new Intent(context, GlobalScreenshot.DeleteScreenshotReceiver.class)
|
||||
new Intent(context, DeleteScreenshotReceiver.class)
|
||||
.putExtra(GlobalScreenshot.SCREENSHOT_URI_ID, uri.toString())
|
||||
.putExtra(GlobalScreenshot.EXTRA_ID, mScreenshotId)
|
||||
.putExtra(GlobalScreenshot.EXTRA_SMART_ACTIONS_ENABLED,
|
||||
@@ -398,7 +396,7 @@ class SaveImageInBackgroundTask extends AsyncTask<Void, Void, Void> {
|
||||
String actionType = extras.getString(
|
||||
ScreenshotNotificationSmartActionsProvider.ACTION_TYPE,
|
||||
ScreenshotNotificationSmartActionsProvider.DEFAULT_ACTION_TYPE);
|
||||
Intent intent = new Intent(context, GlobalScreenshot.SmartActionsReceiver.class)
|
||||
Intent intent = new Intent(context, SmartActionsReceiver.class)
|
||||
.putExtra(GlobalScreenshot.EXTRA_ACTION_INTENT, action.actionIntent)
|
||||
.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
|
||||
addIntentExtras(mScreenshotId, intent, actionType, mSmartActionsEnabled);
|
||||
|
||||
@@ -39,14 +39,21 @@ import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.TimeoutException;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Singleton;
|
||||
|
||||
/**
|
||||
* Collects the static functions for retrieving and acting on smart actions.
|
||||
*/
|
||||
@Singleton
|
||||
public class ScreenshotSmartActions {
|
||||
private static final String TAG = "ScreenshotSmartActions";
|
||||
|
||||
@Inject
|
||||
public ScreenshotSmartActions() {}
|
||||
|
||||
@VisibleForTesting
|
||||
static CompletableFuture<List<Notification.Action>> getSmartActionsFuture(
|
||||
CompletableFuture<List<Notification.Action>> getSmartActionsFuture(
|
||||
String screenshotId, Uri screenshotUri, Bitmap image,
|
||||
ScreenshotNotificationSmartActionsProvider smartActionsProvider,
|
||||
boolean smartActionsEnabled, UserHandle userHandle) {
|
||||
@@ -86,7 +93,7 @@ public class ScreenshotSmartActions {
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
static List<Notification.Action> getSmartActions(String screenshotId,
|
||||
List<Notification.Action> getSmartActions(String screenshotId,
|
||||
CompletableFuture<List<Notification.Action>> smartActionsFuture, int timeoutMs,
|
||||
ScreenshotNotificationSmartActionsProvider smartActionsProvider) {
|
||||
long startTimeMs = SystemClock.uptimeMillis();
|
||||
@@ -116,7 +123,7 @@ public class ScreenshotSmartActions {
|
||||
}
|
||||
}
|
||||
|
||||
static void notifyScreenshotOp(String screenshotId,
|
||||
void notifyScreenshotOp(String screenshotId,
|
||||
ScreenshotNotificationSmartActionsProvider smartActionsProvider,
|
||||
ScreenshotNotificationSmartActionsProvider.ScreenshotOp op,
|
||||
ScreenshotNotificationSmartActionsProvider.ScreenshotOpStatus status, long durationMs) {
|
||||
@@ -127,7 +134,7 @@ public class ScreenshotSmartActions {
|
||||
}
|
||||
}
|
||||
|
||||
static void notifyScreenshotAction(Context context, String screenshotId, String action,
|
||||
void notifyScreenshotAction(Context context, String screenshotId, String action,
|
||||
boolean isSmartAction) {
|
||||
try {
|
||||
ScreenshotNotificationSmartActionsProvider provider =
|
||||
|
||||
@@ -0,0 +1,63 @@
|
||||
/*
|
||||
* 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.EXTRA_ACTION_INTENT;
|
||||
import static com.android.systemui.screenshot.GlobalScreenshot.EXTRA_ACTION_TYPE;
|
||||
import static com.android.systemui.screenshot.GlobalScreenshot.EXTRA_ID;
|
||||
|
||||
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 android.util.Slog;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
|
||||
/**
|
||||
* Executes the smart action tapped by the user in the notification.
|
||||
*/
|
||||
public class SmartActionsReceiver extends BroadcastReceiver {
|
||||
private static final String TAG = "SmartActionsReceiver";
|
||||
|
||||
private final ScreenshotSmartActions mScreenshotSmartActions;
|
||||
|
||||
@Inject
|
||||
SmartActionsReceiver(ScreenshotSmartActions screenshotSmartActions) {
|
||||
mScreenshotSmartActions = screenshotSmartActions;
|
||||
}
|
||||
|
||||
@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);
|
||||
}
|
||||
|
||||
mScreenshotSmartActions.notifyScreenshotAction(
|
||||
context, intent.getStringExtra(EXTRA_ID), actionType, true);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,153 @@
|
||||
/*
|
||||
* 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_SHARE;
|
||||
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 static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.ArgumentMatchers.anyBoolean;
|
||||
import static org.mockito.ArgumentMatchers.anyInt;
|
||||
import static org.mockito.ArgumentMatchers.anyLong;
|
||||
import static org.mockito.ArgumentMatchers.anyString;
|
||||
import static org.mockito.ArgumentMatchers.eq;
|
||||
import static org.mockito.ArgumentMatchers.isNull;
|
||||
import static org.mockito.Mockito.doAnswer;
|
||||
import static org.mockito.Mockito.never;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import android.app.PendingIntent;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.os.Bundle;
|
||||
import android.testing.AndroidTestingRunner;
|
||||
|
||||
import androidx.test.filters.SmallTest;
|
||||
|
||||
import com.android.systemui.SysuiTestCase;
|
||||
import com.android.systemui.shared.system.ActivityManagerWrapper;
|
||||
import com.android.systemui.statusbar.phone.StatusBar;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.MockitoAnnotations;
|
||||
import org.mockito.stubbing.Answer;
|
||||
|
||||
import java.util.Optional;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.concurrent.Future;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.TimeoutException;
|
||||
|
||||
@RunWith(AndroidTestingRunner.class)
|
||||
@SmallTest
|
||||
public class ActionProxyReceiverTest extends SysuiTestCase {
|
||||
|
||||
@Mock
|
||||
private StatusBar mMockStatusBar;
|
||||
@Mock
|
||||
private ActivityManagerWrapper mMockActivityManagerWrapper;
|
||||
@Mock
|
||||
private Future mMockFuture;
|
||||
@Mock
|
||||
private ScreenshotSmartActions mMockScreenshotSmartActions;
|
||||
@Mock
|
||||
private PendingIntent mMockPendingIntent;
|
||||
|
||||
private Intent mIntent;
|
||||
|
||||
@Before
|
||||
public void setup() throws InterruptedException, ExecutionException, TimeoutException {
|
||||
MockitoAnnotations.initMocks(this);
|
||||
mIntent = new Intent(mContext, ActionProxyReceiver.class)
|
||||
.putExtra(GlobalScreenshot.EXTRA_ACTION_INTENT, mMockPendingIntent);
|
||||
|
||||
when(mMockActivityManagerWrapper.closeSystemWindows(anyString())).thenReturn(mMockFuture);
|
||||
when(mMockFuture.get(anyLong(), any(TimeUnit.class))).thenReturn(null);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testPendingIntentSentWithoutStatusBar() throws PendingIntent.CanceledException {
|
||||
ActionProxyReceiver actionProxyReceiver = constructActionProxyReceiver(false);
|
||||
|
||||
actionProxyReceiver.onReceive(mContext, mIntent);
|
||||
|
||||
verify(mMockActivityManagerWrapper).closeSystemWindows(SYSTEM_DIALOG_REASON_SCREENSHOT);
|
||||
verify(mMockStatusBar, never()).executeRunnableDismissingKeyguard(
|
||||
any(Runnable.class), any(Runnable.class), anyBoolean(), anyBoolean(), anyBoolean());
|
||||
verify(mMockPendingIntent).send(
|
||||
eq(mContext), anyInt(), isNull(), isNull(), isNull(), isNull(), any(Bundle.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testPendingIntentSentWithStatusBar() throws PendingIntent.CanceledException {
|
||||
ActionProxyReceiver actionProxyReceiver = constructActionProxyReceiver(true);
|
||||
// ensure that the pending intent call is passed through
|
||||
doAnswer((Answer<Object>) invocation -> {
|
||||
((Runnable) invocation.getArgument(0)).run();
|
||||
return null;
|
||||
}).when(mMockStatusBar).executeRunnableDismissingKeyguard(
|
||||
any(Runnable.class), isNull(), anyBoolean(), anyBoolean(), anyBoolean());
|
||||
|
||||
actionProxyReceiver.onReceive(mContext, mIntent);
|
||||
|
||||
verify(mMockActivityManagerWrapper).closeSystemWindows(SYSTEM_DIALOG_REASON_SCREENSHOT);
|
||||
verify(mMockStatusBar).executeRunnableDismissingKeyguard(
|
||||
any(Runnable.class), isNull(), eq(true), eq(true), eq(true));
|
||||
verify(mMockPendingIntent).send(
|
||||
eq(mContext), anyInt(), isNull(), isNull(), isNull(), isNull(), any(Bundle.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSmartActionsNotNotifiedByDefault() {
|
||||
ActionProxyReceiver actionProxyReceiver = constructActionProxyReceiver(true);
|
||||
|
||||
actionProxyReceiver.onReceive(mContext, mIntent);
|
||||
|
||||
verify(mMockScreenshotSmartActions, never())
|
||||
.notifyScreenshotAction(any(Context.class), anyString(), anyString(), anyBoolean());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSmartActionsNotifiedIfEnabled() {
|
||||
ActionProxyReceiver actionProxyReceiver = constructActionProxyReceiver(true);
|
||||
mIntent.putExtra(EXTRA_SMART_ACTIONS_ENABLED, true);
|
||||
String testId = "testID";
|
||||
mIntent.putExtra(EXTRA_ID, testId);
|
||||
|
||||
actionProxyReceiver.onReceive(mContext, mIntent);
|
||||
|
||||
verify(mMockScreenshotSmartActions).notifyScreenshotAction(
|
||||
mContext, testId, ACTION_TYPE_SHARE, false);
|
||||
}
|
||||
|
||||
private ActionProxyReceiver constructActionProxyReceiver(boolean withStatusBar) {
|
||||
if (withStatusBar) {
|
||||
return new ActionProxyReceiver(
|
||||
Optional.of(mMockStatusBar), mMockActivityManagerWrapper,
|
||||
mMockScreenshotSmartActions);
|
||||
} else {
|
||||
return new ActionProxyReceiver(
|
||||
Optional.empty(), mMockActivityManagerWrapper, mMockScreenshotSmartActions);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,145 @@
|
||||
/*
|
||||
* 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_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 static junit.framework.Assert.assertEquals;
|
||||
import static junit.framework.Assert.assertNotNull;
|
||||
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.ArgumentMatchers.anyBoolean;
|
||||
import static org.mockito.Mockito.never;
|
||||
import static org.mockito.Mockito.verify;
|
||||
|
||||
import android.content.ContentResolver;
|
||||
import android.content.ContentValues;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.database.Cursor;
|
||||
import android.net.Uri;
|
||||
import android.os.Environment;
|
||||
import android.provider.MediaStore;
|
||||
import android.testing.AndroidTestingRunner;
|
||||
|
||||
import androidx.test.filters.SmallTest;
|
||||
|
||||
import com.android.systemui.SysuiTestCase;
|
||||
import com.android.systemui.util.concurrency.FakeExecutor;
|
||||
import com.android.systemui.util.time.FakeSystemClock;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.MockitoAnnotations;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.concurrent.Executor;
|
||||
|
||||
@RunWith(AndroidTestingRunner.class)
|
||||
@SmallTest
|
||||
public class DeleteScreenshotReceiverTest extends SysuiTestCase {
|
||||
|
||||
@Mock
|
||||
private ScreenshotSmartActions mMockScreenshotSmartActions;
|
||||
@Mock
|
||||
private Executor mMockExecutor;
|
||||
|
||||
private DeleteScreenshotReceiver mDeleteScreenshotReceiver;
|
||||
private FakeExecutor mFakeExecutor = new FakeExecutor(new FakeSystemClock());
|
||||
|
||||
@Before
|
||||
public void setup() {
|
||||
MockitoAnnotations.initMocks(this);
|
||||
mDeleteScreenshotReceiver =
|
||||
new DeleteScreenshotReceiver(mMockScreenshotSmartActions, mMockExecutor);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testNoUriProvided() {
|
||||
Intent intent = new Intent(mContext, DeleteScreenshotReceiver.class);
|
||||
|
||||
mDeleteScreenshotReceiver.onReceive(mContext, intent);
|
||||
|
||||
verify(mMockExecutor, never()).execute(any(Runnable.class));
|
||||
verify(mMockScreenshotSmartActions, never()).notifyScreenshotAction(
|
||||
any(Context.class), any(String.class), any(String.class), anyBoolean());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFileDeleted() {
|
||||
DeleteScreenshotReceiver deleteScreenshotReceiver =
|
||||
new DeleteScreenshotReceiver(mMockScreenshotSmartActions, mFakeExecutor);
|
||||
ContentResolver contentResolver = mContext.getContentResolver();
|
||||
final Uri testUri = contentResolver.insert(
|
||||
MediaStore.Images.Media.EXTERNAL_CONTENT_URI, getFakeContentValues());
|
||||
assertNotNull(testUri);
|
||||
|
||||
try {
|
||||
Cursor cursor =
|
||||
contentResolver.query(testUri, null, null, null, null);
|
||||
assertEquals(1, cursor.getCount());
|
||||
Intent intent = new Intent(mContext, DeleteScreenshotReceiver.class)
|
||||
.putExtra(SCREENSHOT_URI_ID, testUri.toString());
|
||||
|
||||
deleteScreenshotReceiver.onReceive(mContext, intent);
|
||||
int runCount = mFakeExecutor.runAllReady();
|
||||
|
||||
assertEquals(1, runCount);
|
||||
cursor =
|
||||
contentResolver.query(testUri, null, null, null, null);
|
||||
assertEquals(0, cursor.getCount());
|
||||
} finally {
|
||||
contentResolver.delete(testUri, null, null);
|
||||
}
|
||||
|
||||
// ensure smart actions not called by default
|
||||
verify(mMockScreenshotSmartActions, never()).notifyScreenshotAction(
|
||||
any(Context.class), any(String.class), any(String.class), anyBoolean());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testNotifyScreenshotAction() {
|
||||
Intent intent = new Intent(mContext, DeleteScreenshotReceiver.class);
|
||||
String uriString = "testUri";
|
||||
String testId = "testID";
|
||||
intent.putExtra(SCREENSHOT_URI_ID, uriString);
|
||||
intent.putExtra(EXTRA_ID, testId);
|
||||
intent.putExtra(EXTRA_SMART_ACTIONS_ENABLED, true);
|
||||
|
||||
mDeleteScreenshotReceiver.onReceive(mContext, intent);
|
||||
|
||||
verify(mMockExecutor).execute(any(Runnable.class));
|
||||
verify(mMockScreenshotSmartActions).notifyScreenshotAction(
|
||||
mContext, testId, ACTION_TYPE_DELETE, false);
|
||||
}
|
||||
|
||||
private static ContentValues getFakeContentValues() {
|
||||
final ContentValues values = new ContentValues();
|
||||
values.put(MediaStore.MediaColumns.RELATIVE_PATH, Environment.DIRECTORY_PICTURES
|
||||
+ File.separator + Environment.DIRECTORY_SCREENSHOTS);
|
||||
values.put(MediaStore.MediaColumns.DISPLAY_NAME, "test_screenshot");
|
||||
values.put(MediaStore.MediaColumns.MIME_TYPE, "image/png");
|
||||
values.put(MediaStore.MediaColumns.DATE_ADDED, 0);
|
||||
values.put(MediaStore.MediaColumns.DATE_MODIFIED, 0);
|
||||
return values;
|
||||
}
|
||||
}
|
||||
@@ -61,12 +61,14 @@ import java.util.concurrent.TimeUnit;
|
||||
*/
|
||||
public class ScreenshotNotificationSmartActionsTest extends SysuiTestCase {
|
||||
private ScreenshotNotificationSmartActionsProvider mSmartActionsProvider;
|
||||
private ScreenshotSmartActions mScreenshotSmartActions;
|
||||
private Handler mHandler;
|
||||
|
||||
@Before
|
||||
public void setup() {
|
||||
mSmartActionsProvider = mock(
|
||||
ScreenshotNotificationSmartActionsProvider.class);
|
||||
mScreenshotSmartActions = new ScreenshotSmartActions();
|
||||
mHandler = mock(Handler.class);
|
||||
}
|
||||
|
||||
@@ -82,7 +84,7 @@ public class ScreenshotNotificationSmartActionsTest extends SysuiTestCase {
|
||||
when(smartActionsProvider.getActions(any(), any(), any(), any(), any()))
|
||||
.thenThrow(RuntimeException.class);
|
||||
CompletableFuture<List<Notification.Action>> smartActionsFuture =
|
||||
ScreenshotSmartActions.getSmartActionsFuture(
|
||||
mScreenshotSmartActions.getSmartActionsFuture(
|
||||
"", Uri.parse("content://authority/data"), bitmap, smartActionsProvider,
|
||||
true, UserHandle.getUserHandleForUid(UserHandle.myUserId()));
|
||||
assertNotNull(smartActionsFuture);
|
||||
@@ -100,7 +102,7 @@ public class ScreenshotNotificationSmartActionsTest extends SysuiTestCase {
|
||||
int timeoutMs = 1000;
|
||||
when(smartActionsFuture.get(timeoutMs, TimeUnit.MILLISECONDS)).thenThrow(
|
||||
RuntimeException.class);
|
||||
List<Notification.Action> actions = ScreenshotSmartActions.getSmartActions(
|
||||
List<Notification.Action> actions = mScreenshotSmartActions.getSmartActions(
|
||||
"", smartActionsFuture, timeoutMs, mSmartActionsProvider);
|
||||
assertEquals(Collections.emptyList(), actions);
|
||||
}
|
||||
@@ -111,7 +113,7 @@ public class ScreenshotNotificationSmartActionsTest extends SysuiTestCase {
|
||||
throws Exception {
|
||||
doThrow(RuntimeException.class).when(mSmartActionsProvider).notifyOp(any(), any(), any(),
|
||||
anyLong());
|
||||
ScreenshotSmartActions.notifyScreenshotOp(null, mSmartActionsProvider, null, null, -1);
|
||||
mScreenshotSmartActions.notifyScreenshotOp(null, mSmartActionsProvider, null, null, -1);
|
||||
}
|
||||
|
||||
// Tests for a non-hardware bitmap, ScreenshotNotificationSmartActionsProvider is never invoked
|
||||
@@ -122,7 +124,7 @@ public class ScreenshotNotificationSmartActionsTest extends SysuiTestCase {
|
||||
Bitmap bitmap = mock(Bitmap.class);
|
||||
when(bitmap.getConfig()).thenReturn(Bitmap.Config.RGB_565);
|
||||
CompletableFuture<List<Notification.Action>> smartActionsFuture =
|
||||
ScreenshotSmartActions.getSmartActionsFuture(
|
||||
mScreenshotSmartActions.getSmartActionsFuture(
|
||||
"", Uri.parse("content://autority/data"), bitmap, mSmartActionsProvider,
|
||||
true, UserHandle.getUserHandleForUid(UserHandle.myUserId()));
|
||||
verify(mSmartActionsProvider, never()).getActions(any(), any(), any(), any(), any());
|
||||
@@ -136,7 +138,7 @@ public class ScreenshotNotificationSmartActionsTest extends SysuiTestCase {
|
||||
public void testScreenshotNotificationSmartActionsProviderInvokedOnce() {
|
||||
Bitmap bitmap = mock(Bitmap.class);
|
||||
when(bitmap.getConfig()).thenReturn(Bitmap.Config.HARDWARE);
|
||||
ScreenshotSmartActions.getSmartActionsFuture(
|
||||
mScreenshotSmartActions.getSmartActionsFuture(
|
||||
"", Uri.parse("content://autority/data"), bitmap, mSmartActionsProvider, true,
|
||||
UserHandle.getUserHandleForUid(UserHandle.myUserId()));
|
||||
verify(mSmartActionsProvider, times(1)).getActions(any(), any(), any(), any(), any());
|
||||
@@ -152,7 +154,7 @@ public class ScreenshotNotificationSmartActionsTest extends SysuiTestCase {
|
||||
SystemUIFactory.getInstance().createScreenshotNotificationSmartActionsProvider(
|
||||
mContext, null, mHandler);
|
||||
CompletableFuture<List<Notification.Action>> smartActionsFuture =
|
||||
ScreenshotSmartActions.getSmartActionsFuture("", null, bitmap,
|
||||
mScreenshotSmartActions.getSmartActionsFuture("", null, bitmap,
|
||||
actionsProvider,
|
||||
true, UserHandle.getUserHandleForUid(UserHandle.myUserId()));
|
||||
assertNotNull(smartActionsFuture);
|
||||
@@ -172,7 +174,8 @@ public class ScreenshotNotificationSmartActionsTest extends SysuiTestCase {
|
||||
data.image = Bitmap.createBitmap(100, 100, Bitmap.Config.ARGB_8888);
|
||||
data.finisher = null;
|
||||
data.mActionsReadyListener = null;
|
||||
SaveImageInBackgroundTask task = new SaveImageInBackgroundTask(mContext, data);
|
||||
SaveImageInBackgroundTask task =
|
||||
new SaveImageInBackgroundTask(mContext, mScreenshotSmartActions, data);
|
||||
|
||||
Notification.Action shareAction = task.createShareAction(mContext, mContext.getResources(),
|
||||
Uri.parse("Screenshot_123.png"));
|
||||
@@ -198,7 +201,8 @@ public class ScreenshotNotificationSmartActionsTest extends SysuiTestCase {
|
||||
data.image = Bitmap.createBitmap(100, 100, Bitmap.Config.ARGB_8888);
|
||||
data.finisher = null;
|
||||
data.mActionsReadyListener = null;
|
||||
SaveImageInBackgroundTask task = new SaveImageInBackgroundTask(mContext, data);
|
||||
SaveImageInBackgroundTask task =
|
||||
new SaveImageInBackgroundTask(mContext, mScreenshotSmartActions, data);
|
||||
|
||||
Notification.Action editAction = task.createEditAction(mContext, mContext.getResources(),
|
||||
Uri.parse("Screenshot_123.png"));
|
||||
@@ -224,7 +228,8 @@ public class ScreenshotNotificationSmartActionsTest extends SysuiTestCase {
|
||||
data.image = Bitmap.createBitmap(100, 100, Bitmap.Config.ARGB_8888);
|
||||
data.finisher = null;
|
||||
data.mActionsReadyListener = null;
|
||||
SaveImageInBackgroundTask task = new SaveImageInBackgroundTask(mContext, data);
|
||||
SaveImageInBackgroundTask task =
|
||||
new SaveImageInBackgroundTask(mContext, mScreenshotSmartActions, data);
|
||||
|
||||
Notification.Action deleteAction = task.createDeleteAction(mContext,
|
||||
mContext.getResources(),
|
||||
|
||||
@@ -0,0 +1,76 @@
|
||||
/*
|
||||
* 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.EXTRA_ACTION_TYPE;
|
||||
import static com.android.systemui.screenshot.GlobalScreenshot.EXTRA_ID;
|
||||
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.ArgumentMatchers.eq;
|
||||
import static org.mockito.ArgumentMatchers.isNull;
|
||||
import static org.mockito.Mockito.verify;
|
||||
|
||||
import android.app.PendingIntent;
|
||||
import android.content.Intent;
|
||||
import android.os.Bundle;
|
||||
import android.testing.AndroidTestingRunner;
|
||||
|
||||
import androidx.test.filters.SmallTest;
|
||||
|
||||
import com.android.systemui.SysuiTestCase;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.MockitoAnnotations;
|
||||
|
||||
@RunWith(AndroidTestingRunner.class)
|
||||
@SmallTest
|
||||
public class SmartActionsReceiverTest extends SysuiTestCase {
|
||||
|
||||
@Mock
|
||||
private ScreenshotSmartActions mMockScreenshotSmartActions;
|
||||
@Mock
|
||||
private PendingIntent mMockPendingIntent;
|
||||
|
||||
private SmartActionsReceiver mSmartActionsReceiver;
|
||||
private Intent mIntent;
|
||||
|
||||
@Before
|
||||
public void setup() {
|
||||
MockitoAnnotations.initMocks(this);
|
||||
mSmartActionsReceiver = new SmartActionsReceiver(mMockScreenshotSmartActions);
|
||||
mIntent = new Intent(mContext, SmartActionsReceiver.class)
|
||||
.putExtra(GlobalScreenshot.EXTRA_ACTION_INTENT, mMockPendingIntent);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSmartActionIntent() throws PendingIntent.CanceledException {
|
||||
String testId = "testID";
|
||||
String testActionType = "testActionType";
|
||||
mIntent.putExtra(EXTRA_ID, testId);
|
||||
mIntent.putExtra(EXTRA_ACTION_TYPE, testActionType);
|
||||
|
||||
mSmartActionsReceiver.onReceive(mContext, mIntent);
|
||||
|
||||
verify(mMockPendingIntent).send(
|
||||
eq(mContext), eq(0), isNull(), isNull(), isNull(), isNull(), any(Bundle.class));
|
||||
verify(mMockScreenshotSmartActions).notifyScreenshotAction(
|
||||
mContext, testId, testActionType, true);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user