summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author Matt Casey <mrcasey@google.com> 2021-02-02 11:30:06 -0500
committer Matt Casey <mrcasey@google.com> 2021-02-02 14:24:34 -0500
commit6950419d5ecacb5de886b4eccf5c0d36a39a38fb (patch)
tree35f443d5071109ba2320f3e9204d2e94b6e9eadc
parente8bd9217ed49cd6e98cd3985cba4b541218624cf (diff)
Add CropView for long screenshots
View is hidden for now since it doesn't do anything. Placeholder UI, made up colors, etc. Also make edit use the preferred screenshot editor (matching screenshot behavior). Bug: 148519342 Test: Make CropView visbible, interact with UI to verify it behaves properly. Change-Id: Ib93616b7355b6b69fe556ff37a570d73af8844d1
-rw-r--r--packages/SystemUI/res/layout/long_screenshot.xml19
-rw-r--r--packages/SystemUI/res/values/attrs.xml6
-rw-r--r--packages/SystemUI/src/com/android/systemui/screenshot/CropView.java151
-rw-r--r--packages/SystemUI/src/com/android/systemui/screenshot/ScrollCaptureController.java29
4 files changed, 194 insertions, 11 deletions
diff --git a/packages/SystemUI/res/layout/long_screenshot.xml b/packages/SystemUI/res/layout/long_screenshot.xml
index fc68a64ac9bd..2a055fc8252f 100644
--- a/packages/SystemUI/res/layout/long_screenshot.xml
+++ b/packages/SystemUI/res/layout/long_screenshot.xml
@@ -39,6 +39,25 @@
tools:minHeight="100dp"
tools:minWidth="100dp" />
+ <com.android.systemui.screenshot.CropView
+ android:id="@+id/crop_view"
+ android:visibility="gone"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginVertical="8dp"
+ app:layout_constrainedHeight="true"
+ app:layout_constrainedWidth="true"
+ app:layout_constraintBottom_toBottomOf="@id/guideline"
+ app:layout_constraintEnd_toEndOf="parent"
+ app:layout_constraintStart_toStartOf="parent"
+ app:layout_constraintTop_toTopOf="parent"
+ app:handleThickness="3dp"
+ app:handleColor="@*android:color/accent_device_default"
+ app:scrimColor="#9444"
+ tools:background="?android:colorBackground"
+ tools:minHeight="100dp"
+ tools:minWidth="100dp" />
+
<androidx.constraintlayout.widget.Guideline
android:id="@+id/guideline"
android:layout_width="wrap_content"
diff --git a/packages/SystemUI/res/values/attrs.xml b/packages/SystemUI/res/values/attrs.xml
index 4059b49ec486..e2fe223f591b 100644
--- a/packages/SystemUI/res/values/attrs.xml
+++ b/packages/SystemUI/res/values/attrs.xml
@@ -171,5 +171,11 @@
<declare-styleable name="PagedTileLayout">
<attr name="sideLabels" format="boolean"/>
</declare-styleable>
+
+ <declare-styleable name="CropView">
+ <attr name="handleThickness" format="dimension" />
+ <attr name="handleColor" format="color" />
+ <attr name="scrimColor" format="color" />
+ </declare-styleable>
</resources>
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/CropView.java b/packages/SystemUI/src/com/android/systemui/screenshot/CropView.java
new file mode 100644
index 000000000000..8e182b415488
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/CropView.java
@@ -0,0 +1,151 @@
+/*
+ * Copyright (C) 2021 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.Context;
+import android.content.res.TypedArray;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.Paint;
+import android.util.AttributeSet;
+import android.util.MathUtils;
+import android.view.MotionEvent;
+import android.view.View;
+
+import androidx.annotation.Nullable;
+
+import com.android.systemui.R;
+
+/**
+ * CropView has top and bottom draggable crop handles, with a scrim to darken the areas being
+ * cropped out.
+ */
+public class CropView extends View {
+ private enum CropBoundary {
+ NONE, TOP, BOTTOM
+ }
+
+ private final float mCropTouchMargin;
+ private final Paint mShadePaint;
+ private final Paint mHandlePaint;
+
+ // Top and bottom crops are stored as floats [0, 1], representing the top and bottom of the
+ // view, respectively.
+ private float mTopCrop = 0f;
+ private float mBottomCrop = 1f;
+
+ private CropBoundary mCurrentDraggingBoundary = CropBoundary.NONE;
+ private float mLastY;
+
+ public CropView(Context context, @Nullable AttributeSet attrs) {
+ this(context, attrs, 0);
+ }
+
+ public CropView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
+ super(context, attrs, defStyleAttr);
+ TypedArray t = context.getTheme().obtainStyledAttributes(
+ attrs, R.styleable.CropView, 0, 0);
+ mShadePaint = new Paint();
+ mShadePaint.setColor(t.getColor(R.styleable.CropView_scrimColor, Color.TRANSPARENT));
+ mHandlePaint = new Paint();
+ mHandlePaint.setColor(t.getColor(R.styleable.CropView_handleColor, Color.BLACK));
+ mHandlePaint.setStrokeWidth(
+ t.getDimensionPixelSize(R.styleable.CropView_handleThickness, 20));
+ t.recycle();
+ // 48 dp touchable region around each handle.
+ mCropTouchMargin = 24 * getResources().getDisplayMetrics().density;
+ }
+
+ @Override
+ public void onDraw(Canvas canvas) {
+ super.onDraw(canvas);
+ drawShade(canvas, 0, mTopCrop);
+ drawShade(canvas, mBottomCrop, 1f);
+ drawHandle(canvas, mTopCrop);
+ drawHandle(canvas, mBottomCrop);
+ }
+
+ @Override
+ public boolean onTouchEvent(MotionEvent event) {
+ int topPx = fractionToPixels(mTopCrop);
+ int bottomPx = fractionToPixels(mBottomCrop);
+ if (event.getAction() == MotionEvent.ACTION_DOWN) {
+ mCurrentDraggingBoundary = nearestBoundary(event, topPx, bottomPx);
+ if (mCurrentDraggingBoundary != CropBoundary.NONE) {
+ mLastY = event.getY();
+ }
+ return true;
+ }
+ if (event.getAction() == MotionEvent.ACTION_MOVE
+ && mCurrentDraggingBoundary != CropBoundary.NONE) {
+ float delta = event.getY() - mLastY;
+ if (mCurrentDraggingBoundary == CropBoundary.TOP) {
+ mTopCrop = pixelsToFraction((int) MathUtils.constrain(topPx + delta, 0,
+ bottomPx - 2 * mCropTouchMargin));
+ } else { // Bottom
+ mBottomCrop = pixelsToFraction((int) MathUtils.constrain(bottomPx + delta,
+ topPx + 2 * mCropTouchMargin, getMeasuredHeight()));
+ }
+ mLastY = event.getY();
+ invalidate();
+ return true;
+ }
+ return super.onTouchEvent(event);
+ }
+
+ /**
+ * @return value [0,1] representing the position of the top crop boundary.
+ */
+ public float getTopBoundary() {
+ return mTopCrop;
+ }
+
+ /**
+ * @return value [0,1] representing the position of the bottom crop boundary.
+ */
+ public float getBottomBoundary() {
+ return mBottomCrop;
+ }
+
+ private void drawShade(Canvas canvas, float fracStart, float fracEnd) {
+ canvas.drawRect(0, fractionToPixels(fracStart), getMeasuredWidth(),
+ fractionToPixels(fracEnd), mShadePaint);
+ }
+
+ private void drawHandle(Canvas canvas, float frac) {
+ int y = fractionToPixels(frac);
+ canvas.drawLine(0, y, getMeasuredWidth(), y, mHandlePaint);
+ }
+
+ private int fractionToPixels(float frac) {
+ return (int) (frac * getMeasuredHeight());
+ }
+
+ private float pixelsToFraction(int px) {
+ return px / (float) getMeasuredHeight();
+ }
+
+ private CropBoundary nearestBoundary(MotionEvent event, int topPx, int bottomPx) {
+ if (Math.abs(event.getY() - topPx) < mCropTouchMargin) {
+ return CropBoundary.TOP;
+ }
+ if (Math.abs(event.getY() - bottomPx) < mCropTouchMargin) {
+ return CropBoundary.BOTTOM;
+ }
+ return CropBoundary.NONE;
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScrollCaptureController.java b/packages/SystemUI/src/com/android/systemui/screenshot/ScrollCaptureController.java
index 176a2c78796b..fd7db4bfffb4 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/ScrollCaptureController.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScrollCaptureController.java
@@ -18,10 +18,12 @@ package com.android.systemui.screenshot;
import android.annotation.IdRes;
import android.annotation.UiThread;
+import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.net.Uri;
import android.os.UserHandle;
+import android.text.TextUtils;
import android.util.Log;
import android.view.View;
import android.view.ViewTreeObserver.InternalInsetsInfo;
@@ -168,19 +170,20 @@ public class ScrollCaptureController implements OnComputeInternalInsetsListener
}
private void edit() {
- sendIntentWhenReady(Intent.ACTION_EDIT);
+ String editorPackage = mContext.getString(R.string.config_screenshotEditor);
+ sendIntentWhenReady(Intent.ACTION_EDIT, editorPackage);
}
private void share() {
- sendIntentWhenReady(Intent.ACTION_SEND);
+ sendIntentWhenReady(Intent.ACTION_SEND, null);
}
- void sendIntentWhenReady(String action) {
+ void sendIntentWhenReady(String action, String component) {
if (mExportFuture != null) {
mExportFuture.addListener(() -> {
try {
ImageExporter.Result result = mExportFuture.get();
- sendIntent(action, result.uri);
+ sendIntent(action, component, result.uri);
mCallback.onFinish();
} catch (InterruptedException | ExecutionException e) {
Log.e(TAG, "failed to export", e);
@@ -254,12 +257,16 @@ public class ScrollCaptureController implements OnComputeInternalInsetsListener
}
}
- void sendIntent(String action, Uri uri) {
- Intent editIntent = new Intent(action);
- editIntent.setType("image/png");
- editIntent.setData(uri);
- editIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
- editIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
- mContext.startActivityAsUser(editIntent, UserHandle.CURRENT);
+ void sendIntent(String action, String component, Uri uri) {
+ Intent intent = new Intent(action);
+ if (!TextUtils.isEmpty(component)) {
+ intent.setComponent(ComponentName.unflattenFromString(component));
+ }
+ intent.setType("image/png");
+ intent.setData(uri);
+ intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
+ intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
+
+ mContext.startActivityAsUser(intent, UserHandle.CURRENT);
}
}