diff options
| author | 2021-02-02 11:30:06 -0500 | |
|---|---|---|
| committer | 2021-02-02 14:24:34 -0500 | |
| commit | 6950419d5ecacb5de886b4eccf5c0d36a39a38fb (patch) | |
| tree | 35f443d5071109ba2320f3e9204d2e94b6e9eadc | |
| parent | e8bd9217ed49cd6e98cd3985cba4b541218624cf (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
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); } } |