Merge "Query and surface Quick Share chip before screenshot image compress/export. Update chip intent, once image URL is available after compress/export." into sc-dev
This commit is contained in:
@@ -81,6 +81,12 @@ public final class SystemUiDeviceConfigFlags {
|
||||
public static final String SCREENSHOT_NOTIFICATION_SMART_ACTIONS_TIMEOUT_MS =
|
||||
"screenshot_notification_smart_actions_timeout_ms";
|
||||
|
||||
/**
|
||||
* (int) Timeout value in ms to get Quick Share actions for screenshot notification.
|
||||
*/
|
||||
public static final String SCREENSHOT_NOTIFICATION_QUICK_SHARE_ACTIONS_TIMEOUT_MS =
|
||||
"screenshot_notification_quick_share_actions_timeout_ms";
|
||||
|
||||
// Flags related to Smart Suggestions - these are read in SmartReplyConstants.
|
||||
|
||||
/** (boolean) Whether to enable smart suggestions in notifications. */
|
||||
|
||||
@@ -20,6 +20,8 @@ import static com.android.systemui.screenshot.LogConfig.DEBUG_ACTIONS;
|
||||
import static com.android.systemui.screenshot.LogConfig.DEBUG_CALLBACK;
|
||||
import static com.android.systemui.screenshot.LogConfig.DEBUG_STORAGE;
|
||||
import static com.android.systemui.screenshot.LogConfig.logTag;
|
||||
import static com.android.systemui.screenshot.ScreenshotNotificationSmartActionsProvider.ScreenshotSmartActionType.QUICK_SHARE_ACTION;
|
||||
import static com.android.systemui.screenshot.ScreenshotNotificationSmartActionsProvider.ScreenshotSmartActionType.REGULAR_SMART_ACTIONS;
|
||||
|
||||
import android.app.ActivityTaskManager;
|
||||
import android.app.Notification;
|
||||
@@ -74,6 +76,7 @@ class SaveImageInBackgroundTask extends AsyncTask<Void, Void, Void> {
|
||||
private final ScreenshotSmartActions mScreenshotSmartActions;
|
||||
private final ScreenshotController.SaveImageInBackgroundData mParams;
|
||||
private final ScreenshotController.SavedImageData mImageData;
|
||||
private final ScreenshotController.QuickShareData mQuickShareData;
|
||||
|
||||
private final ScreenshotNotificationSmartActionsProvider mSmartActionsProvider;
|
||||
private String mScreenshotId;
|
||||
@@ -90,6 +93,7 @@ class SaveImageInBackgroundTask extends AsyncTask<Void, Void, Void> {
|
||||
mContext = context;
|
||||
mScreenshotSmartActions = screenshotSmartActions;
|
||||
mImageData = new ScreenshotController.SavedImageData();
|
||||
mQuickShareData = new ScreenshotController.QuickShareData();
|
||||
mSharedElementTransition = sharedElementTransition;
|
||||
mImageExporter = exporter;
|
||||
|
||||
@@ -127,6 +131,13 @@ class SaveImageInBackgroundTask extends AsyncTask<Void, Void, Void> {
|
||||
Bitmap image = mParams.image;
|
||||
mScreenshotId = String.format(SCREENSHOT_ID_TEMPLATE, requestId);
|
||||
try {
|
||||
if (mSmartActionsEnabled && mParams.mQuickShareActionsReadyListener != null) {
|
||||
// Since Quick Share target recommendation does not rely on image URL, it is
|
||||
// queried and surfaced before image compress/export. Action intent would not be
|
||||
// used, because it does not contain image URL.
|
||||
queryQuickShareAction(image, user);
|
||||
}
|
||||
|
||||
// Call synchronously here since already on a background thread.
|
||||
ListenableFuture<ImageExporter.Result> future =
|
||||
mImageExporter.export(Runnable::run, requestId, image);
|
||||
@@ -136,7 +147,7 @@ class SaveImageInBackgroundTask extends AsyncTask<Void, Void, Void> {
|
||||
|
||||
CompletableFuture<List<Notification.Action>> smartActionsFuture =
|
||||
mScreenshotSmartActions.getSmartActionsFuture(
|
||||
mScreenshotId, uri, image, mSmartActionsProvider,
|
||||
mScreenshotId, uri, image, mSmartActionsProvider, REGULAR_SMART_ACTIONS,
|
||||
mSmartActionsEnabled, user);
|
||||
|
||||
List<Notification.Action> smartActions = new ArrayList<>();
|
||||
@@ -148,7 +159,7 @@ class SaveImageInBackgroundTask extends AsyncTask<Void, Void, Void> {
|
||||
smartActions.addAll(buildSmartActions(
|
||||
mScreenshotSmartActions.getSmartActions(
|
||||
mScreenshotId, smartActionsFuture, timeoutMs,
|
||||
mSmartActionsProvider),
|
||||
mSmartActionsProvider, REGULAR_SMART_ACTIONS),
|
||||
mContext));
|
||||
}
|
||||
|
||||
@@ -157,6 +168,8 @@ class SaveImageInBackgroundTask extends AsyncTask<Void, Void, Void> {
|
||||
mImageData.shareTransition = createShareAction(mContext, mContext.getResources(), uri);
|
||||
mImageData.editTransition = createEditAction(mContext, mContext.getResources(), uri);
|
||||
mImageData.deleteAction = createDeleteAction(mContext, mContext.getResources(), uri);
|
||||
mImageData.quickShareAction = createQuickShareAction(mContext,
|
||||
mQuickShareData.quickShareAction, uri);
|
||||
|
||||
mParams.mActionsReadyListener.onActionsReady(mImageData);
|
||||
if (DEBUG_CALLBACK) {
|
||||
@@ -173,6 +186,7 @@ class SaveImageInBackgroundTask extends AsyncTask<Void, Void, Void> {
|
||||
}
|
||||
mParams.clearImage();
|
||||
mImageData.reset();
|
||||
mQuickShareData.reset();
|
||||
mParams.mActionsReadyListener.onActionsReady(mImageData);
|
||||
if (DEBUG_CALLBACK) {
|
||||
Log.d(TAG, "Calling (Consumer<Uri>) finisher.accept(null)");
|
||||
@@ -197,6 +211,7 @@ class SaveImageInBackgroundTask extends AsyncTask<Void, Void, Void> {
|
||||
// params. The finisher is expected to always be called back, so just use the baked-in
|
||||
// params from the ctor in any case.
|
||||
mImageData.reset();
|
||||
mQuickShareData.reset();
|
||||
mParams.mActionsReadyListener.onActionsReady(mImageData);
|
||||
if (DEBUG_CALLBACK) {
|
||||
Log.d(TAG, "onCancelled, calling (Consumer<Uri>) finisher.accept(null)");
|
||||
@@ -389,4 +404,74 @@ class SaveImageInBackgroundTask extends AsyncTask<Void, Void, Void> {
|
||||
.putExtra(ScreenshotController.EXTRA_ID, screenshotId)
|
||||
.putExtra(ScreenshotController.EXTRA_SMART_ACTIONS_ENABLED, smartActionsEnabled);
|
||||
}
|
||||
|
||||
/**
|
||||
* Populate image uri into intent of Quick Share action.
|
||||
*/
|
||||
@VisibleForTesting
|
||||
private Notification.Action createQuickShareAction(Context context, Notification.Action action,
|
||||
Uri uri) {
|
||||
if (action == null) {
|
||||
return null;
|
||||
}
|
||||
// Populate image URI into Quick Share chip intent
|
||||
Intent sharingIntent = action.actionIntent.getIntent();
|
||||
sharingIntent.setType("image/png");
|
||||
sharingIntent.putExtra(Intent.EXTRA_STREAM, uri);
|
||||
String subjectDate = DateFormat.getDateTimeInstance().format(new Date(mImageTime));
|
||||
String subject = String.format(SCREENSHOT_SHARE_SUBJECT_TEMPLATE, subjectDate);
|
||||
sharingIntent.putExtra(Intent.EXTRA_SUBJECT, subject);
|
||||
// Include URI in ClipData also, so that grantPermission picks it up.
|
||||
// We don't use setData here because some apps interpret this as "to:".
|
||||
ClipData clipdata = new ClipData(new ClipDescription("content",
|
||||
new String[]{"image/png"}),
|
||||
new ClipData.Item(uri));
|
||||
sharingIntent.setClipData(clipdata);
|
||||
sharingIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
|
||||
PendingIntent updatedPendingIntent = PendingIntent.getActivity(
|
||||
context, 0, sharingIntent,
|
||||
PendingIntent.FLAG_CANCEL_CURRENT | PendingIntent.FLAG_IMMUTABLE);
|
||||
|
||||
// Proxy smart actions through {@link GlobalScreenshot.SmartActionsReceiver}
|
||||
// for logging smart actions.
|
||||
Bundle extras = action.getExtras();
|
||||
String actionType = extras.getString(
|
||||
ScreenshotNotificationSmartActionsProvider.ACTION_TYPE,
|
||||
ScreenshotNotificationSmartActionsProvider.DEFAULT_ACTION_TYPE);
|
||||
Intent intent = new Intent(context, SmartActionsReceiver.class)
|
||||
.putExtra(ScreenshotController.EXTRA_ACTION_INTENT, updatedPendingIntent)
|
||||
.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
|
||||
addIntentExtras(mScreenshotId, intent, actionType, mSmartActionsEnabled);
|
||||
PendingIntent broadcastIntent = PendingIntent.getBroadcast(context,
|
||||
mRandom.nextInt(),
|
||||
intent,
|
||||
PendingIntent.FLAG_CANCEL_CURRENT | PendingIntent.FLAG_IMMUTABLE);
|
||||
return new Notification.Action.Builder(action.getIcon(), action.title,
|
||||
broadcastIntent).setContextual(true).addExtras(extras).build();
|
||||
}
|
||||
|
||||
/**
|
||||
* Query and surface Quick Share chip if it is available. Action intent would not be used,
|
||||
* because it does not contain image URL which would be populated in {@link
|
||||
* #createQuickShareAction(Context, Notification.Action, Uri)}
|
||||
*/
|
||||
private void queryQuickShareAction(Bitmap image, UserHandle user) {
|
||||
CompletableFuture<List<Notification.Action>> quickShareActionsFuture =
|
||||
mScreenshotSmartActions.getSmartActionsFuture(
|
||||
mScreenshotId, null, image, mSmartActionsProvider,
|
||||
QUICK_SHARE_ACTION,
|
||||
mSmartActionsEnabled, user);
|
||||
int timeoutMs = DeviceConfig.getInt(
|
||||
DeviceConfig.NAMESPACE_SYSTEMUI,
|
||||
SystemUiDeviceConfigFlags.SCREENSHOT_NOTIFICATION_QUICK_SHARE_ACTIONS_TIMEOUT_MS,
|
||||
500);
|
||||
List<Notification.Action> quickShareActions =
|
||||
mScreenshotSmartActions.getSmartActions(
|
||||
mScreenshotId, quickShareActionsFuture, timeoutMs,
|
||||
mSmartActionsProvider, QUICK_SHARE_ACTION);
|
||||
if (!quickShareActions.isEmpty()) {
|
||||
mQuickShareData.quickShareAction = quickShareActions.get(0);
|
||||
mParams.mQuickShareActionsReadyListener.onActionsReady(mQuickShareData);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -114,6 +114,7 @@ public class ScreenshotController {
|
||||
public Bitmap image;
|
||||
public Consumer<Uri> finisher;
|
||||
public ScreenshotController.ActionsReadyListener mActionsReadyListener;
|
||||
public ScreenshotController.QuickShareActionReadyListener mQuickShareActionsReadyListener;
|
||||
|
||||
void clearImage() {
|
||||
image = null;
|
||||
@@ -129,6 +130,7 @@ public class ScreenshotController {
|
||||
public Supplier<ActionTransition> editTransition;
|
||||
public Notification.Action deleteAction;
|
||||
public List<Notification.Action> smartActions;
|
||||
public Notification.Action quickShareAction;
|
||||
|
||||
/**
|
||||
* POD for shared element transition.
|
||||
@@ -148,6 +150,21 @@ public class ScreenshotController {
|
||||
editTransition = null;
|
||||
deleteAction = null;
|
||||
smartActions = null;
|
||||
quickShareAction = null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Structure returned by the QueryQuickShareInBackgroundTask
|
||||
*/
|
||||
static class QuickShareData {
|
||||
public Notification.Action quickShareAction;
|
||||
|
||||
/**
|
||||
* Used to reset the return data on error
|
||||
*/
|
||||
public void reset() {
|
||||
quickShareAction = null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -155,6 +172,10 @@ public class ScreenshotController {
|
||||
void onActionsReady(ScreenshotController.SavedImageData imageData);
|
||||
}
|
||||
|
||||
interface QuickShareActionReadyListener {
|
||||
void onActionsReady(ScreenshotController.QuickShareData quickShareData);
|
||||
}
|
||||
|
||||
// These strings are used for communicating the action invoked to
|
||||
// ScreenshotNotificationSmartActionsProvider.
|
||||
static final String EXTRA_ACTION_TYPE = "android:screenshot_action_type";
|
||||
@@ -519,7 +540,8 @@ public class ScreenshotController {
|
||||
mScreenBitmap.setHasAlpha(false);
|
||||
mScreenBitmap.prepareToDraw();
|
||||
|
||||
saveScreenshotInWorkerThread(finisher, this::showUiOnActionsReady);
|
||||
saveScreenshotInWorkerThread(finisher, this::showUiOnActionsReady,
|
||||
this::showUiOnQuickShareActionReady);
|
||||
|
||||
// The window is focusable by default
|
||||
setWindowFocusable(true);
|
||||
@@ -664,20 +686,21 @@ public class ScreenshotController {
|
||||
saveScreenshotInWorkerThread(
|
||||
/* onComplete */ finisher,
|
||||
/* actionsReadyListener */ imageData -> {
|
||||
if (DEBUG_CALLBACK) {
|
||||
Log.d(TAG, "returning URI to finisher (Consumer<URI>): " + imageData.uri);
|
||||
}
|
||||
finisher.accept(imageData.uri);
|
||||
if (imageData.uri == null) {
|
||||
mUiEventLogger.log(ScreenshotEvent.SCREENSHOT_NOT_SAVED);
|
||||
mNotificationsController.notifyScreenshotError(
|
||||
R.string.screenshot_failed_to_save_text);
|
||||
} else {
|
||||
mUiEventLogger.log(ScreenshotEvent.SCREENSHOT_SAVED);
|
||||
mScreenshotHandler.post(() -> Toast.makeText(mContext,
|
||||
R.string.screenshot_saved_title, Toast.LENGTH_SHORT).show());
|
||||
}
|
||||
});
|
||||
if (DEBUG_CALLBACK) {
|
||||
Log.d(TAG, "returning URI to finisher (Consumer<URI>): " + imageData.uri);
|
||||
}
|
||||
finisher.accept(imageData.uri);
|
||||
if (imageData.uri == null) {
|
||||
mUiEventLogger.log(ScreenshotEvent.SCREENSHOT_NOT_SAVED);
|
||||
mNotificationsController.notifyScreenshotError(
|
||||
R.string.screenshot_failed_to_save_text);
|
||||
} else {
|
||||
mUiEventLogger.log(ScreenshotEvent.SCREENSHOT_SAVED);
|
||||
mScreenshotHandler.post(() -> Toast.makeText(mContext,
|
||||
R.string.screenshot_saved_title, Toast.LENGTH_SHORT).show());
|
||||
}
|
||||
},
|
||||
null);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -718,12 +741,15 @@ public class ScreenshotController {
|
||||
* Creates a new worker thread and saves the screenshot to the media store.
|
||||
*/
|
||||
private void saveScreenshotInWorkerThread(Consumer<Uri> finisher,
|
||||
@Nullable ScreenshotController.ActionsReadyListener actionsReadyListener) {
|
||||
@Nullable ScreenshotController.ActionsReadyListener actionsReadyListener,
|
||||
@Nullable ScreenshotController.QuickShareActionReadyListener
|
||||
quickShareActionsReadyListener) {
|
||||
ScreenshotController.SaveImageInBackgroundData
|
||||
data = new ScreenshotController.SaveImageInBackgroundData();
|
||||
data.image = mScreenBitmap;
|
||||
data.finisher = finisher;
|
||||
data.mActionsReadyListener = actionsReadyListener;
|
||||
data.mQuickShareActionsReadyListener = quickShareActionsReadyListener;
|
||||
|
||||
if (mSaveInBgTask != null) {
|
||||
// just log success/failure for the pre-existing screenshot
|
||||
@@ -785,6 +811,30 @@ public class ScreenshotController {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets up the action shade and its entrance animation, once we get the Quick Share action data.
|
||||
*/
|
||||
private void showUiOnQuickShareActionReady(ScreenshotController.QuickShareData quickShareData) {
|
||||
if (DEBUG_UI) {
|
||||
Log.d(TAG, "Showing UI for Quick Share action");
|
||||
}
|
||||
if (quickShareData.quickShareAction != null) {
|
||||
mScreenshotHandler.post(() -> {
|
||||
if (mScreenshotAnimation != null && mScreenshotAnimation.isRunning()) {
|
||||
mScreenshotAnimation.addListener(new AnimatorListenerAdapter() {
|
||||
@Override
|
||||
public void onAnimationEnd(Animator animation) {
|
||||
super.onAnimationEnd(animation);
|
||||
mScreenshotView.addQuickShareChip(quickShareData.quickShareAction);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
mScreenshotView.addQuickShareChip(quickShareData.quickShareAction);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Supplies the necessary bits for the shared element transition to share sheet.
|
||||
* Note that once supplied, the action intent to share must be sent immediately after.
|
||||
|
||||
@@ -60,11 +60,14 @@ public class ScreenshotSmartActions {
|
||||
CompletableFuture<List<Notification.Action>> getSmartActionsFuture(
|
||||
String screenshotId, Uri screenshotUri, Bitmap image,
|
||||
ScreenshotNotificationSmartActionsProvider smartActionsProvider,
|
||||
ScreenshotSmartActionType actionType,
|
||||
boolean smartActionsEnabled, UserHandle userHandle) {
|
||||
if (DEBUG_ACTIONS) {
|
||||
Log.d(TAG, String.format("getSmartActionsFuture id=%s, uri=%s, provider=%s, "
|
||||
+ "smartActionsEnabled=%b, userHandle=%s", screenshotId, screenshotUri,
|
||||
smartActionsProvider.getClass(), smartActionsEnabled, userHandle));
|
||||
Log.d(TAG, String.format(
|
||||
"getSmartActionsFuture id=%s, uri=%s, provider=%s, actionType=%s, "
|
||||
+ "smartActionsEnabled=%b, userHandle=%s",
|
||||
screenshotId, screenshotUri, smartActionsProvider.getClass(), actionType,
|
||||
smartActionsEnabled, userHandle));
|
||||
}
|
||||
if (!smartActionsEnabled) {
|
||||
if (DEBUG_ACTIONS) {
|
||||
@@ -89,7 +92,7 @@ public class ScreenshotSmartActions {
|
||||
? runningTask.topActivity
|
||||
: new ComponentName("", "");
|
||||
smartActionsFuture = smartActionsProvider.getActions(screenshotId, screenshotUri, image,
|
||||
componentName, ScreenshotSmartActionType.REGULAR_SMART_ACTIONS, userHandle);
|
||||
componentName, actionType, userHandle);
|
||||
} catch (Throwable e) {
|
||||
long waitTimeMs = SystemClock.uptimeMillis() - startTimeMs;
|
||||
smartActionsFuture = CompletableFuture.completedFuture(Collections.emptyList());
|
||||
@@ -107,19 +110,21 @@ public class ScreenshotSmartActions {
|
||||
@VisibleForTesting
|
||||
List<Notification.Action> getSmartActions(String screenshotId,
|
||||
CompletableFuture<List<Notification.Action>> smartActionsFuture, int timeoutMs,
|
||||
ScreenshotNotificationSmartActionsProvider smartActionsProvider) {
|
||||
ScreenshotNotificationSmartActionsProvider smartActionsProvider,
|
||||
ScreenshotSmartActionType actionType) {
|
||||
long startTimeMs = SystemClock.uptimeMillis();
|
||||
if (DEBUG_ACTIONS) {
|
||||
Log.d(TAG, String.format("getSmartActions id=%s, timeoutMs=%d, provider=%s",
|
||||
screenshotId, timeoutMs, smartActionsProvider.getClass()));
|
||||
Log.d(TAG,
|
||||
String.format("getSmartActions id=%s, timeoutMs=%d, actionType=%s, provider=%s",
|
||||
screenshotId, timeoutMs, actionType, smartActionsProvider.getClass()));
|
||||
}
|
||||
try {
|
||||
List<Notification.Action> actions = smartActionsFuture.get(timeoutMs,
|
||||
TimeUnit.MILLISECONDS);
|
||||
long waitTimeMs = SystemClock.uptimeMillis() - startTimeMs;
|
||||
if (DEBUG_ACTIONS) {
|
||||
Log.d(TAG, String.format("Got %d smart actions. Wait time: %d ms",
|
||||
actions.size(), waitTimeMs));
|
||||
Log.d(TAG, String.format("Got %d smart actions. Wait time: %d ms, actionType=%s",
|
||||
actions.size(), waitTimeMs, actionType));
|
||||
}
|
||||
notifyScreenshotOp(screenshotId, smartActionsProvider,
|
||||
ScreenshotNotificationSmartActionsProvider.ScreenshotOp.WAIT_FOR_SMART_ACTIONS,
|
||||
@@ -129,8 +134,9 @@ public class ScreenshotSmartActions {
|
||||
} catch (Throwable e) {
|
||||
long waitTimeMs = SystemClock.uptimeMillis() - startTimeMs;
|
||||
if (DEBUG_ACTIONS) {
|
||||
Log.e(TAG, String.format("Error getting smart actions. Wait time: %d ms",
|
||||
waitTimeMs), e);
|
||||
Log.e(TAG, String.format(
|
||||
"Error getting smart actions. Wait time: %d ms, actionType=%s",
|
||||
waitTimeMs, actionType), e);
|
||||
}
|
||||
ScreenshotNotificationSmartActionsProvider.ScreenshotOpStatus status =
|
||||
(e instanceof TimeoutException)
|
||||
@@ -165,7 +171,7 @@ public class ScreenshotSmartActions {
|
||||
SystemUIFactory.getInstance().createScreenshotNotificationSmartActionsProvider(
|
||||
context, THREAD_POOL_EXECUTOR, new Handler());
|
||||
if (DEBUG_ACTIONS) {
|
||||
Log.e(TAG, String.format("%s notifyAction: %s id=%s, isSmartAction=%b",
|
||||
Log.d(TAG, String.format("%s notifyAction: %s id=%s, isSmartAction=%b",
|
||||
provider.getClass(), action, screenshotId, isSmartAction));
|
||||
}
|
||||
provider.notifyAction(screenshotId, action, isSmartAction);
|
||||
|
||||
@@ -137,6 +137,7 @@ public class ScreenshotView extends FrameLayout implements
|
||||
private ScreenshotActionChip mShareChip;
|
||||
private ScreenshotActionChip mEditChip;
|
||||
private ScreenshotActionChip mScrollChip;
|
||||
private ScreenshotActionChip mQuickShareChip;
|
||||
|
||||
private UiEventLogger mUiEventLogger;
|
||||
private ScreenshotViewCallback mCallbacks;
|
||||
@@ -151,7 +152,8 @@ public class ScreenshotView extends FrameLayout implements
|
||||
private enum PendingInteraction {
|
||||
PREVIEW,
|
||||
EDIT,
|
||||
SHARE
|
||||
SHARE,
|
||||
QUICK_SHARE
|
||||
}
|
||||
|
||||
public ScreenshotView(Context context) {
|
||||
@@ -505,6 +507,9 @@ public class ScreenshotView extends FrameLayout implements
|
||||
mShareChip.setOnClickListener(v -> {
|
||||
mShareChip.setIsPending(true);
|
||||
mEditChip.setIsPending(false);
|
||||
if (mQuickShareChip != null) {
|
||||
mQuickShareChip.setIsPending(false);
|
||||
}
|
||||
mPendingInteraction = PendingInteraction.SHARE;
|
||||
});
|
||||
chips.add(mShareChip);
|
||||
@@ -514,6 +519,9 @@ public class ScreenshotView extends FrameLayout implements
|
||||
mEditChip.setOnClickListener(v -> {
|
||||
mEditChip.setIsPending(true);
|
||||
mShareChip.setIsPending(false);
|
||||
if (mQuickShareChip != null) {
|
||||
mQuickShareChip.setIsPending(false);
|
||||
}
|
||||
mPendingInteraction = PendingInteraction.EDIT;
|
||||
});
|
||||
chips.add(mEditChip);
|
||||
@@ -521,6 +529,9 @@ public class ScreenshotView extends FrameLayout implements
|
||||
mScreenshotPreview.setOnClickListener(v -> {
|
||||
mShareChip.setIsPending(false);
|
||||
mEditChip.setIsPending(false);
|
||||
if (mQuickShareChip != null) {
|
||||
mQuickShareChip.setIsPending(false);
|
||||
}
|
||||
mPendingInteraction = PendingInteraction.PREVIEW;
|
||||
});
|
||||
|
||||
@@ -582,6 +593,13 @@ public class ScreenshotView extends FrameLayout implements
|
||||
startSharedTransition(
|
||||
imageData.editTransition.get());
|
||||
});
|
||||
if (mQuickShareChip != null) {
|
||||
mQuickShareChip.setPendingIntent(imageData.quickShareAction.actionIntent,
|
||||
() -> {
|
||||
mUiEventLogger.log(ScreenshotEvent.SCREENSHOT_SMART_ACTION_TAPPED);
|
||||
animateDismissal();
|
||||
});
|
||||
}
|
||||
|
||||
if (mPendingInteraction != null) {
|
||||
switch (mPendingInteraction) {
|
||||
@@ -594,6 +612,9 @@ public class ScreenshotView extends FrameLayout implements
|
||||
case EDIT:
|
||||
mEditChip.callOnClick();
|
||||
break;
|
||||
case QUICK_SHARE:
|
||||
mQuickShareChip.callOnClick();
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
LayoutInflater inflater = LayoutInflater.from(mContext);
|
||||
@@ -615,6 +636,25 @@ public class ScreenshotView extends FrameLayout implements
|
||||
}
|
||||
}
|
||||
|
||||
void addQuickShareChip(Notification.Action quickShareAction) {
|
||||
if (mPendingInteraction == null) {
|
||||
LayoutInflater inflater = LayoutInflater.from(mContext);
|
||||
mQuickShareChip = (ScreenshotActionChip) inflater.inflate(
|
||||
R.layout.global_screenshot_action_chip, mActionsView, false);
|
||||
mQuickShareChip.setText(quickShareAction.title);
|
||||
mQuickShareChip.setIcon(quickShareAction.getIcon(), false);
|
||||
mQuickShareChip.setOnClickListener(v -> {
|
||||
mShareChip.setIsPending(false);
|
||||
mEditChip.setIsPending(false);
|
||||
mQuickShareChip.setIsPending(true);
|
||||
mPendingInteraction = PendingInteraction.QUICK_SHARE;
|
||||
});
|
||||
mQuickShareChip.setAlpha(1);
|
||||
mActionsView.addView(mQuickShareChip);
|
||||
mSmartChips.add(mQuickShareChip);
|
||||
}
|
||||
}
|
||||
|
||||
boolean isDismissing() {
|
||||
return (mDismissAnimation != null && mDismissAnimation.isRunning());
|
||||
}
|
||||
@@ -700,6 +740,7 @@ public class ScreenshotView extends FrameLayout implements
|
||||
mActionsView.removeView(chip);
|
||||
}
|
||||
mSmartChips.clear();
|
||||
mQuickShareChip = null;
|
||||
setAlpha(1);
|
||||
mDismissButton.setTranslationY(0);
|
||||
mActionsContainer.setTranslationY(0);
|
||||
|
||||
@@ -16,6 +16,8 @@
|
||||
|
||||
package com.android.systemui.screenshot;
|
||||
|
||||
import static com.android.systemui.screenshot.ScreenshotNotificationSmartActionsProvider.ScreenshotSmartActionType.REGULAR_SMART_ACTIONS;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.junit.Assert.assertNull;
|
||||
@@ -87,6 +89,7 @@ public class ScreenshotNotificationSmartActionsTest extends SysuiTestCase {
|
||||
CompletableFuture<List<Notification.Action>> smartActionsFuture =
|
||||
mScreenshotSmartActions.getSmartActionsFuture(
|
||||
"", Uri.parse("content://authority/data"), bitmap, smartActionsProvider,
|
||||
REGULAR_SMART_ACTIONS,
|
||||
true, UserHandle.of(UserHandle.myUserId()));
|
||||
assertNotNull(smartActionsFuture);
|
||||
List<Notification.Action> smartActions = smartActionsFuture.get(5, TimeUnit.MILLISECONDS);
|
||||
@@ -104,7 +107,7 @@ public class ScreenshotNotificationSmartActionsTest extends SysuiTestCase {
|
||||
when(smartActionsFuture.get(timeoutMs, TimeUnit.MILLISECONDS)).thenThrow(
|
||||
RuntimeException.class);
|
||||
List<Notification.Action> actions = mScreenshotSmartActions.getSmartActions(
|
||||
"", smartActionsFuture, timeoutMs, mSmartActionsProvider);
|
||||
"", smartActionsFuture, timeoutMs, mSmartActionsProvider, REGULAR_SMART_ACTIONS);
|
||||
assertEquals(Collections.emptyList(), actions);
|
||||
}
|
||||
|
||||
@@ -127,6 +130,7 @@ public class ScreenshotNotificationSmartActionsTest extends SysuiTestCase {
|
||||
CompletableFuture<List<Notification.Action>> smartActionsFuture =
|
||||
mScreenshotSmartActions.getSmartActionsFuture(
|
||||
"", Uri.parse("content://autority/data"), bitmap, mSmartActionsProvider,
|
||||
REGULAR_SMART_ACTIONS,
|
||||
true, UserHandle.of(UserHandle.myUserId()));
|
||||
verify(mSmartActionsProvider, never()).getActions(any(), any(), any(), any(), any(), any());
|
||||
assertNotNull(smartActionsFuture);
|
||||
@@ -140,7 +144,8 @@ public class ScreenshotNotificationSmartActionsTest extends SysuiTestCase {
|
||||
Bitmap bitmap = mock(Bitmap.class);
|
||||
when(bitmap.getConfig()).thenReturn(Bitmap.Config.HARDWARE);
|
||||
mScreenshotSmartActions.getSmartActionsFuture(
|
||||
"", Uri.parse("content://autority/data"), bitmap, mSmartActionsProvider, true,
|
||||
"", Uri.parse("content://autority/data"), bitmap, mSmartActionsProvider,
|
||||
REGULAR_SMART_ACTIONS, true,
|
||||
UserHandle.of(UserHandle.myUserId()));
|
||||
verify(mSmartActionsProvider, times(1)).getActions(
|
||||
any(), any(), any(), any(), any(), any());
|
||||
@@ -157,7 +162,7 @@ public class ScreenshotNotificationSmartActionsTest extends SysuiTestCase {
|
||||
mContext, null, mHandler);
|
||||
CompletableFuture<List<Notification.Action>> smartActionsFuture =
|
||||
mScreenshotSmartActions.getSmartActionsFuture("", null, bitmap,
|
||||
actionsProvider,
|
||||
actionsProvider, REGULAR_SMART_ACTIONS,
|
||||
true, UserHandle.of(UserHandle.myUserId()));
|
||||
assertNotNull(smartActionsFuture);
|
||||
List<Notification.Action> smartActions = smartActionsFuture.get(5, TimeUnit.MILLISECONDS);
|
||||
|
||||
Reference in New Issue
Block a user