Merge "Add action chips to corner screenshot flow"

This commit is contained in:
Miranda Kephart
2019-11-26 20:36:17 +00:00
committed by Android (Google) Code Review
10 changed files with 191 additions and 32 deletions

View File

@@ -0,0 +1,27 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
~ 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.
-->
<ripple
xmlns:android="http://schemas.android.com/apk/res/android"
android:color="@color/global_screenshot_button_ripple">
<item android:id="@android:id/background">
<shape android:shape="rectangle">
<stroke android:width="1dp" android:color="@color/global_screenshot_button_text"/>
<solid android:color="@color/global_screenshot_button_background"/>
<corners android:radius="@dimen/screenshot_button_corner_radius"/>
</shape>
</item>
</ripple>

View File

@@ -0,0 +1,21 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
~ 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.
-->
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<solid android:color="@color/global_screenshot_button_background"/>
<corners android:radius="@dimen/screenshot_action_container_corner_radius"/>
</shape>

View File

@@ -39,4 +39,13 @@
android:layout_height="match_parent"
android:visibility="gone"
android:pointerIcon="crosshair"/>
<LinearLayout
android:id="@+id/global_screenshot_actions"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="bottom|center"
android:gravity="center"
android:paddingVertical="@dimen/screenshot_action_container_padding"
android:visibility="gone"
android:background="@drawable/action_chip_container_background"/>
</FrameLayout>

View File

@@ -0,0 +1,25 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
~ 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.
-->
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/global_screenshot_action_chip"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginHorizontal="@dimen/screenshot_action_chip_margin_horizontal"
android:paddingVertical="@dimen/screenshot_action_chip_padding_vertical"
android:paddingHorizontal="@dimen/screenshot_action_chip_padding_horizontal"
android:background="@drawable/action_chip_background"
android:textColor="@color/global_screenshot_button_text"/>

View File

@@ -78,6 +78,11 @@
<!-- The color of the text in the Global Actions menu -->
<color name="global_actions_alert_text">@color/GM2_red_300</color>
<!-- Global screenshot actions -->
<color name="global_screenshot_button_background">@color/GM2_grey_900</color>
<color name="global_screenshot_button_ripple">#42FFFFFF</color>
<color name="global_screenshot_button_text">@color/GM2_blue_300</color>
<!-- Biometric dialog colors -->
<color name="biometric_dialog_gray">#ff888888</color>
<color name="biometric_dialog_accent">#ff80cbc4</color> <!-- light teal -->

View File

@@ -180,6 +180,11 @@
<!-- Color for the Assistant invocation lights -->
<color name="default_invocation_lights_color">#ffffffff</color> <!-- white -->
<!-- Global screenshot actions -->
<color name="global_screenshot_button_background">#F5F5F5</color>
<color name="global_screenshot_button_ripple">#1f000000</color>
<color name="global_screenshot_button_text">@color/GM2_blue_500</color>
<!-- GM2 colors -->
<color name="GM2_grey_50">#F8F9FA</color>
<color name="GM2_grey_100">#F1F3F4</color>

View File

@@ -285,8 +285,18 @@
<!-- the padding between dots in the icon overflow -->
<dimen name="overflow_icon_dot_padding">3dp</dimen>
<!-- Dimensions related to screenshots -->
<!-- The padding on the global screenshot background image -->
<dimen name="global_screenshot_bg_padding">20dp</dimen>
<dimen name="screenshot_action_container_corner_radius">10dp</dimen>
<dimen name="screenshot_action_container_padding">20dp</dimen>
<!-- Radius of the chip background on global screenshot actions -->
<dimen name="screenshot_button_corner_radius">20dp</dimen>
<dimen name="screenshot_action_chip_margin_horizontal">10dp</dimen>
<dimen name="screenshot_action_chip_padding_vertical">10dp</dimen>
<dimen name="screenshot_action_chip_padding_horizontal">15dp</dimen>
<!-- The width of the view containing navigation buttons -->
<dimen name="navigation_key_width">70dp</dimen>

View File

@@ -19,6 +19,7 @@ package com.android.systemui.screenshot;
import static android.content.Context.NOTIFICATION_SERVICE;
import static android.os.AsyncTask.THREAD_POOL_EXECUTOR;
import static android.provider.DeviceConfig.NAMESPACE_SYSTEMUI;
import static android.view.View.VISIBLE;
import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
import static com.android.internal.config.sysui.SystemUiDeviceConfigFlags.SCREENSHOT_CORNER_FLOW;
@@ -69,6 +70,8 @@ import android.view.ViewGroup;
import android.view.WindowManager;
import android.view.animation.Interpolator;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.TextView;
import android.widget.Toast;
import com.android.internal.annotations.VisibleForTesting;
@@ -89,7 +92,6 @@ import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import java.util.function.Consumer;
import java.util.function.Function;
import javax.inject.Inject;
import javax.inject.Singleton;
@@ -110,7 +112,7 @@ public class GlobalScreenshot {
public Bitmap image;
public Uri imageUri;
public Consumer<Uri> finisher;
public Function<PendingIntent, Void> onEditReady;
public GlobalScreenshot.ActionsReadyListener mActionsReadyListener;
public int iconSize;
public int previewWidth;
public int previewheight;
@@ -127,6 +129,10 @@ public class GlobalScreenshot {
}
}
abstract static class ActionsReadyListener {
abstract void onActionsReady(PendingIntent shareAction, PendingIntent editAction);
}
// These strings are used for communicating the action invoked to
// ScreenshotNotificationSmartActionsProvider.
static final String EXTRA_ACTION_TYPE = "android:screenshot_action_type";
@@ -175,6 +181,10 @@ public class GlobalScreenshot {
private ImageView mBackgroundView;
private ImageView mScreenshotView;
private ImageView mScreenshotFlash;
private LinearLayout mActionsView;
private TextView mShareAction;
private TextView mEditAction;
private TextView mScrollAction;
private AnimatorSet mScreenshotAnimation;
@@ -211,17 +221,31 @@ public class GlobalScreenshot {
mScreenshotLayout = layoutInflater.inflate(R.layout.global_screenshot, null);
mBackgroundView = mScreenshotLayout.findViewById(R.id.global_screenshot_background);
mScreenshotView = mScreenshotLayout.findViewById(R.id.global_screenshot);
mActionsView = mScreenshotLayout.findViewById(R.id.global_screenshot_actions);
mShareAction = (TextView) layoutInflater.inflate(
R.layout.global_screenshot_action_chip, mActionsView, false);
mEditAction = (TextView) layoutInflater.inflate(
R.layout.global_screenshot_action_chip, mActionsView, false);
mScrollAction = (TextView) layoutInflater.inflate(
R.layout.global_screenshot_action_chip, mActionsView, false);
mShareAction.setText(com.android.internal.R.string.share);
mEditAction.setText(com.android.internal.R.string.screenshot_edit);
mScrollAction.setText("Scroll"); // TODO (mkephart): Add to resources and translate
mActionsView.addView(mShareAction);
mActionsView.addView(mEditAction);
mActionsView.addView(mScrollAction);
mScreenshotFlash = mScreenshotLayout.findViewById(R.id.global_screenshot_flash);
mScreenshotSelectorView = mScreenshotLayout.findViewById(R.id.global_screenshot_selector);
mScreenshotLayout.setFocusable(true);
mScreenshotSelectorView.setFocusable(true);
mScreenshotSelectorView.setFocusableInTouchMode(true);
mScreenshotLayout.setOnTouchListener(new View.OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {
// Intercept and ignore all touch events
return true;
}
mScreenshotLayout.setOnTouchListener((v, event) -> {
// Intercept and ignore all touch events
return true;
});
// Setup the window that we are going to use
@@ -271,13 +295,13 @@ public class GlobalScreenshot {
* Creates a new worker thread and saves the screenshot to the media store.
*/
private void saveScreenshotInWorkerThread(
Consumer<Uri> finisher, @Nullable Function<PendingIntent, Void> onEditReady) {
Consumer<Uri> finisher, @Nullable ActionsReadyListener actionsReadyListener) {
SaveImageInBackgroundData data = new SaveImageInBackgroundData();
data.context = mContext;
data.image = mScreenBitmap;
data.iconSize = mNotificationIconSize;
data.finisher = finisher;
data.onEditReady = onEditReady;
data.mActionsReadyListener = actionsReadyListener;
data.previewWidth = mPreviewWidth;
data.previewheight = mPreviewHeight;
if (mSaveInBgTask != null) {
@@ -395,6 +419,7 @@ public class GlobalScreenshot {
// Clear any references to the bitmap
mScreenBitmap = null;
mScreenshotView.setImageBitmap(null);
mActionsView.setVisibility(View.GONE);
mBackgroundView.setVisibility(View.GONE);
mScreenshotView.setVisibility(View.GONE);
mScreenshotView.setLayerType(View.LAYER_TYPE_NONE, null);
@@ -441,24 +466,13 @@ public class GlobalScreenshot {
saveScreenshotInWorkerThread(finisher);
clearScreenshot();
} else {
mScreenshotView.requestFocus();
mScreenshotView.setOnClickListener((v) -> {
// TODO: remove once we have a better UI to show that we aren't ready yet
Toast notReadyToast = Toast.makeText(
mContext, "Screenshot is not ready yet", Toast.LENGTH_SHORT);
notReadyToast.show();
});
saveScreenshotInWorkerThread(finisher, intent -> {
mScreenshotHandler.post(() -> mScreenshotView.setOnClickListener(v -> {
try {
intent.send();
clearScreenshot();
} catch (PendingIntent.CanceledException e) {
Log.e(TAG, "Edit intent cancelled", e);
}
mScreenshotHandler.removeMessages(MESSAGE_CORNER_TIMEOUT);
}));
return null;
saveScreenshotInWorkerThread(finisher, new ActionsReadyListener() {
@Override
void onActionsReady(PendingIntent shareAction, PendingIntent editAction) {
mScreenshotHandler.post(() ->
createScreenshotActionsShadeAnimation(shareAction, editAction)
.start());
}
});
mScreenshotHandler.sendMessageDelayed(
mScreenshotHandler.obtainMessage(MESSAGE_CORNER_TIMEOUT),
@@ -664,6 +678,49 @@ public class GlobalScreenshot {
return anim;
}
private ValueAnimator createScreenshotActionsShadeAnimation(
PendingIntent shareAction, PendingIntent editAction) {
ValueAnimator animator = ValueAnimator.ofFloat(0, 1);
mActionsView.setY(mDisplayMetrics.heightPixels);
mActionsView.setVisibility(VISIBLE);
mActionsView.measure(View.MeasureSpec.UNSPECIFIED, View.MeasureSpec.UNSPECIFIED);
float actionsViewHeight = mActionsView.getMeasuredHeight();
float screenshotStartHeight = mScreenshotView.getTranslationY();
animator.addUpdateListener(animation -> {
float t = animation.getAnimatedFraction();
mScreenshotView.setTranslationY(screenshotStartHeight - actionsViewHeight * t);
mActionsView.setY(mDisplayMetrics.heightPixels - actionsViewHeight * t);
});
animator.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
super.onAnimationEnd(animation);
mScreenshotView.requestFocus();
mShareAction.setOnClickListener(v -> {
try {
shareAction.send();
clearScreenshot();
} catch (PendingIntent.CanceledException e) {
Log.e(TAG, "Share intent cancelled", e);
}
});
mEditAction.setOnClickListener(v -> {
try {
editAction.send();
clearScreenshot();
} catch (PendingIntent.CanceledException e) {
Log.e(TAG, "Edit intent cancelled", e);
}
});
Toast scrollNotImplemented = Toast.makeText(
mContext, "Not implemented", Toast.LENGTH_SHORT);
mScrollAction.setOnClickListener(v -> scrollNotImplemented.show());
}
});
return animator;
}
static void notifyScreenshotError(Context context, NotificationManager nManager, int msgResId) {
Resources r = context.getResources();
String errorMsg = r.getString(msgResId);

View File

@@ -434,8 +434,8 @@ class SaveImageInBackgroundTask extends AsyncTask<Void, Void, Void> {
R.drawable.ic_screenshot_edit,
r.getString(com.android.internal.R.string.screenshot_edit), editAction);
notificationBuilder.addAction(editActionBuilder.build());
if (editAction != null && mParams.onEditReady != null) {
mParams.onEditReady.apply(editAction);
if (mParams.mActionsReadyListener != null) {
mParams.mActionsReadyListener.onActionsReady(shareAction, editAction);
}
// Create a delete action for the notification
@@ -472,7 +472,7 @@ class SaveImageInBackgroundTask extends AsyncTask<Void, Void, Void> {
GlobalScreenshot.notifyScreenshotError(mParams.context, mNotificationManager,
mParams.errorMsgResId);
} else {
if (mParams.onEditReady != null) {
if (mParams.mActionsReadyListener != null) {
// Cancel the "saving screenshot" notification
mNotificationManager.cancel(
SystemMessageProto.SystemMessage.NOTE_GLOBAL_SCREENSHOT);

View File

@@ -175,7 +175,7 @@ public class ScreenshotNotificationSmartActionsTest extends SysuiTestCase {
data.image = Bitmap.createBitmap(100, 100, Bitmap.Config.ARGB_8888);
data.iconSize = 10;
data.finisher = null;
data.onEditReady = null;
data.mActionsReadyListener = null;
data.previewWidth = 10;
data.previewheight = 10;
SaveImageInBackgroundTask task = new SaveImageInBackgroundTask(mContext, data,