Merge "Screenshot to long screenshot shared transition" into sc-dev
This commit is contained in:
committed by
Android (Google) Code Review
commit
367720d17f
@@ -20,6 +20,11 @@
|
||||
android:theme="@style/Screenshot"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
<ImageView
|
||||
android:id="@+id/screenshot_scrolling_scrim"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:visibility="gone"/>
|
||||
<ImageView
|
||||
android:id="@+id/global_screenshot_actions_background"
|
||||
android:layout_height="@dimen/screenshot_bg_protection_height"
|
||||
|
||||
@@ -122,4 +122,12 @@
|
||||
android:layout_margin="@dimen/screenshot_dismiss_button_margin"
|
||||
android:src="@drawable/screenshot_cancel"/>
|
||||
</FrameLayout>
|
||||
<ImageView
|
||||
android:id="@+id/screenshot_scrollable_preview"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:scaleType="matrix"
|
||||
app:layout_constraintStart_toStartOf="@id/global_screenshot_preview"
|
||||
app:layout_constraintTop_toTopOf="@id/global_screenshot_preview"
|
||||
android:elevation="@dimen/screenshot_preview_elevation"/>
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
||||
@@ -62,6 +62,7 @@
|
||||
android:paddingHorizontal="48dp"
|
||||
android:paddingTop="8dp"
|
||||
android:paddingBottom="42dp"
|
||||
android:alpha="0"
|
||||
app:layout_constrainedHeight="true"
|
||||
app:layout_constrainedWidth="true"
|
||||
app:layout_constraintTop_toBottomOf="@id/save"
|
||||
@@ -72,6 +73,16 @@
|
||||
tools:minHeight="100dp"
|
||||
tools:minWidth="100dp" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/enter_transition"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:scaleType="matrix"
|
||||
app:layout_constraintTop_toTopOf="@id/preview"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
android:transitionName="screenshot_preview_image"/>
|
||||
|
||||
<com.android.systemui.screenshot.CropView
|
||||
android:id="@+id/crop_view"
|
||||
android:layout_width="0px"
|
||||
|
||||
@@ -22,6 +22,7 @@ import android.content.ComponentName;
|
||||
import android.content.Intent;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.HardwareRenderer;
|
||||
import android.graphics.Matrix;
|
||||
import android.graphics.RecordingCanvas;
|
||||
import android.graphics.Rect;
|
||||
import android.graphics.RenderNode;
|
||||
@@ -31,9 +32,12 @@ import android.net.Uri;
|
||||
import android.os.Bundle;
|
||||
import android.os.UserHandle;
|
||||
import android.text.TextUtils;
|
||||
import android.transition.Transition;
|
||||
import android.transition.TransitionListenerAdapter;
|
||||
import android.util.Log;
|
||||
import android.view.ScrollCaptureResponse;
|
||||
import android.view.View;
|
||||
import android.view.ViewTreeObserver;
|
||||
import android.widget.ImageView;
|
||||
|
||||
import androidx.constraintlayout.widget.ConstraintLayout;
|
||||
@@ -74,6 +78,7 @@ public class LongScreenshotActivity extends Activity {
|
||||
|
||||
private ImageView mPreview;
|
||||
private ImageView mTransitionView;
|
||||
private ImageView mEnterTransitionView;
|
||||
private View mSave;
|
||||
private View mEdit;
|
||||
private View mShare;
|
||||
@@ -111,7 +116,7 @@ public class LongScreenshotActivity extends Activity {
|
||||
public void onCreate(Bundle savedInstanceState) {
|
||||
Log.d(TAG, "onCreate(savedInstanceState = " + savedInstanceState + ")");
|
||||
super.onCreate(savedInstanceState);
|
||||
|
||||
postponeEnterTransition();
|
||||
setContentView(R.layout.long_screenshot);
|
||||
|
||||
mPreview = requireViewById(R.id.preview);
|
||||
@@ -122,6 +127,7 @@ public class LongScreenshotActivity extends Activity {
|
||||
mMagnifierView = requireViewById(R.id.magnifier);
|
||||
mCropView.setCropInteractionListener(mMagnifierView);
|
||||
mTransitionView = requireViewById(R.id.transition);
|
||||
mEnterTransitionView = requireViewById(R.id.enter_transition);
|
||||
|
||||
mSave.setOnClickListener(this::onClicked);
|
||||
mEdit.setOnClickListener(this::onClicked);
|
||||
@@ -184,8 +190,8 @@ public class LongScreenshotActivity extends Activity {
|
||||
private void onLongScreenshotReceived(LongScreenshot longScreenshot) {
|
||||
Log.d(TAG, "onLongScreenshotReceived(longScreenshot=" + longScreenshot + ")");
|
||||
mLongScreenshot = longScreenshot;
|
||||
mPreview.setImageDrawable(mLongScreenshot.getDrawable());
|
||||
updateImageDimensions();
|
||||
Drawable drawable = mLongScreenshot.getDrawable();
|
||||
mPreview.setImageDrawable(drawable);
|
||||
mCropView.setVisibility(View.VISIBLE);
|
||||
mMagnifierView.setDrawable(mLongScreenshot.getDrawable(),
|
||||
mLongScreenshot.getWidth(), mLongScreenshot.getHeight());
|
||||
@@ -196,9 +202,35 @@ public class LongScreenshotActivity extends Activity {
|
||||
float bottomFraction = Math.min(1f,
|
||||
1 - (mLongScreenshot.getBottom() - mLongScreenshot.getPageHeight())
|
||||
/ (float) mLongScreenshot.getHeight());
|
||||
mCropView.animateBoundaryTo(CropView.CropBoundary.TOP, topFraction);
|
||||
mCropView.animateBoundaryTo(CropView.CropBoundary.BOTTOM, bottomFraction);
|
||||
setButtonsEnabled(true);
|
||||
|
||||
mEnterTransitionView.setImageDrawable(drawable);
|
||||
|
||||
mEnterTransitionView.getViewTreeObserver().addOnPreDrawListener(
|
||||
new ViewTreeObserver.OnPreDrawListener() {
|
||||
@Override
|
||||
public boolean onPreDraw() {
|
||||
mEnterTransitionView.getViewTreeObserver().removeOnPreDrawListener(this);
|
||||
updateImageDimensions();
|
||||
startPostponedEnterTransition();
|
||||
if (isActivityTransitionRunning()) {
|
||||
getWindow().getSharedElementEnterTransition().addListener(
|
||||
new TransitionListenerAdapter() {
|
||||
@Override
|
||||
public void onTransitionEnd(Transition transition) {
|
||||
super.onTransitionEnd(transition);
|
||||
mPreview.animate().alpha(1f);
|
||||
mCropView.animateBoundaryTo(
|
||||
CropView.CropBoundary.TOP, topFraction);
|
||||
mCropView.animateBoundaryTo(
|
||||
CropView.CropBoundary.BOTTOM, bottomFraction);
|
||||
setButtonsEnabled(true);
|
||||
mEnterTransitionView.setVisibility(View.GONE);
|
||||
}
|
||||
});
|
||||
}
|
||||
return true;
|
||||
}
|
||||
});
|
||||
|
||||
// Immediately export to temp image file for saved state
|
||||
mCacheSaveFuture = mImageExporter.exportAsTempFile(mBackgroundExecutor,
|
||||
@@ -412,22 +444,26 @@ public class LongScreenshotActivity extends Activity {
|
||||
// The image width and height on screen
|
||||
int imageHeight = previewHeight;
|
||||
int imageWidth = previewWidth;
|
||||
float scale;
|
||||
int extraPadding = 0;
|
||||
if (imageRatio > viewRatio) {
|
||||
// Image is full width and height is constrained, compute extra padding to inform
|
||||
// CropView
|
||||
imageHeight = (int) (previewHeight * viewRatio / imageRatio);
|
||||
int extraPadding = (previewHeight - imageHeight) / 2;
|
||||
extraPadding = (previewHeight - imageHeight) / 2;
|
||||
mCropView.setExtraPadding(extraPadding + mPreview.getPaddingTop(),
|
||||
extraPadding + mPreview.getPaddingBottom());
|
||||
imageTop += (previewHeight - imageHeight) / 2;
|
||||
mCropView.setExtraPadding(extraPadding, extraPadding);
|
||||
mCropView.setImageWidth(previewWidth);
|
||||
scale = previewWidth / (float) mPreview.getDrawable().getIntrinsicWidth();
|
||||
} else {
|
||||
imageWidth = (int) (previewWidth * imageRatio / viewRatio);
|
||||
imageLeft += (previewWidth - imageWidth) / 2;
|
||||
// Image is full height
|
||||
mCropView.setExtraPadding(mPreview.getPaddingTop(), mPreview.getPaddingBottom());
|
||||
mCropView.setImageWidth((int) (previewHeight * imageRatio));
|
||||
scale = previewHeight / (float) mPreview.getDrawable().getIntrinsicHeight();
|
||||
}
|
||||
|
||||
// Update transition view's position and scale.
|
||||
@@ -439,5 +475,20 @@ public class LongScreenshotActivity extends Activity {
|
||||
params.width = boundaries.width();
|
||||
params.height = boundaries.height();
|
||||
mTransitionView.setLayoutParams(params);
|
||||
|
||||
ConstraintLayout.LayoutParams enterTransitionParams =
|
||||
(ConstraintLayout.LayoutParams) mEnterTransitionView.getLayoutParams();
|
||||
float topFraction = Math.max(0,
|
||||
-mLongScreenshot.getTop() / (float) mLongScreenshot.getHeight());
|
||||
enterTransitionParams.width = (int) (scale * drawable.getIntrinsicWidth());
|
||||
enterTransitionParams.height = (int) (scale * mLongScreenshot.getPageHeight());
|
||||
mEnterTransitionView.setLayoutParams(enterTransitionParams);
|
||||
|
||||
Matrix matrix = new Matrix();
|
||||
matrix.setScale(scale, scale);
|
||||
matrix.postTranslate(0, -scale * drawable.getIntrinsicHeight() * topFraction);
|
||||
mEnterTransitionView.setImageMatrix(matrix);
|
||||
mEnterTransitionView.setTranslationY(
|
||||
topFraction * previewHeight + mPreview.getPaddingTop() + extraPadding);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -598,7 +598,7 @@ public class ScreenshotController {
|
||||
// No connection means that the target window wasn't found
|
||||
// or that it cannot support scroll capture.
|
||||
Log.d(TAG, "ScrollCapture: " + mLastScrollCaptureResponse.getDescription() + " ["
|
||||
+ mLastScrollCaptureResponse.getWindowTitle() + "]");
|
||||
+ mLastScrollCaptureResponse.getWindowTitle() + "]");
|
||||
return;
|
||||
}
|
||||
Log.d(TAG, "ScrollCapture: connected to window ["
|
||||
@@ -606,6 +606,7 @@ public class ScreenshotController {
|
||||
|
||||
final ScrollCaptureResponse response = mLastScrollCaptureResponse;
|
||||
mScreenshotView.showScrollChip(/* onClick */ () -> {
|
||||
mScreenshotView.prepareScrollingTransition(response, mScreenBitmap);
|
||||
// Clear the reference to prevent close() in dismissScreenshot
|
||||
mLastScrollCaptureResponse = null;
|
||||
final ListenableFuture<ScrollCaptureController.LongScreenshot> future =
|
||||
@@ -623,9 +624,14 @@ public class ScreenshotController {
|
||||
|
||||
final Intent intent = new Intent(mContext, LongScreenshotActivity.class);
|
||||
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP);
|
||||
mContext.startActivity(intent);
|
||||
|
||||
dismissScreenshot(false);
|
||||
Pair<ActivityOptions, ExitTransitionCoordinator> transition =
|
||||
ActivityOptions.startSharedElementAnimation(
|
||||
mWindow, new ScreenshotExitTransitionCallbacks(), null,
|
||||
Pair.create(mScreenshotView.getScrollablePreview(),
|
||||
ChooserActivity.FIRST_IMAGE_PREVIEW_TRANSITION_NAME));
|
||||
transition.second.startExit();
|
||||
mContext.startActivity(intent, transition.first.toBundle());
|
||||
}, mMainExecutor);
|
||||
});
|
||||
} catch (CancellationException e) {
|
||||
@@ -649,7 +655,8 @@ public class ScreenshotController {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onWindowDetached() { }
|
||||
public void onWindowDetached() {
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
@@ -847,24 +854,9 @@ public class ScreenshotController {
|
||||
*/
|
||||
private Supplier<ActionTransition> getActionTransitionSupplier() {
|
||||
return () -> {
|
||||
ExitTransitionCallbacks cb = new ExitTransitionCallbacks() {
|
||||
@Override
|
||||
public boolean isReturnTransitionAllowed() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void hideSharedElements() {
|
||||
finishDismiss();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFinish() {
|
||||
}
|
||||
};
|
||||
|
||||
Pair<ActivityOptions, ExitTransitionCoordinator> transition =
|
||||
ActivityOptions.startSharedElementAnimation(mWindow, cb, null,
|
||||
ActivityOptions.startSharedElementAnimation(
|
||||
mWindow, new ScreenshotExitTransitionCallbacks(), null,
|
||||
Pair.create(mScreenshotView.getScreenshotPreview(),
|
||||
ChooserActivity.FIRST_IMAGE_PREVIEW_TRANSITION_NAME));
|
||||
transition.second.startExit();
|
||||
@@ -950,4 +942,20 @@ public class ScreenshotController {
|
||||
}
|
||||
return matchWithinTolerance;
|
||||
}
|
||||
|
||||
private class ScreenshotExitTransitionCallbacks implements ExitTransitionCallbacks {
|
||||
@Override
|
||||
public boolean isReturnTransitionAllowed() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void hideSharedElements() {
|
||||
finishDismiss();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFinish() {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -40,6 +40,7 @@ import android.content.res.Resources;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.Color;
|
||||
import android.graphics.Insets;
|
||||
import android.graphics.Matrix;
|
||||
import android.graphics.PointF;
|
||||
import android.graphics.Rect;
|
||||
import android.graphics.Region;
|
||||
@@ -58,6 +59,7 @@ import android.view.DisplayCutout;
|
||||
import android.view.GestureDetector;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.MotionEvent;
|
||||
import android.view.ScrollCaptureResponse;
|
||||
import android.view.TouchDelegate;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
@@ -72,6 +74,8 @@ import android.widget.HorizontalScrollView;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.LinearLayout;
|
||||
|
||||
import androidx.constraintlayout.widget.ConstraintLayout;
|
||||
|
||||
import com.android.internal.logging.UiEventLogger;
|
||||
import com.android.systemui.R;
|
||||
import com.android.systemui.screenshot.ScreenshotController.SavedImageData.ActionTransition;
|
||||
@@ -126,9 +130,11 @@ public class ScreenshotView extends FrameLayout implements
|
||||
private boolean mDirectionLTR;
|
||||
|
||||
private ScreenshotSelectorView mScreenshotSelectorView;
|
||||
private ImageView mScrollingScrim;
|
||||
private View mScreenshotStatic;
|
||||
private ImageView mScreenshotPreview;
|
||||
private View mScreenshotPreviewBorder;
|
||||
private ImageView mScrollablePreview;
|
||||
private ImageView mScreenshotFlash;
|
||||
private ImageView mActionsContainerBackground;
|
||||
private HorizontalScrollView mActionsContainer;
|
||||
@@ -267,6 +273,7 @@ public class ScreenshotView extends FrameLayout implements
|
||||
|
||||
@Override // View
|
||||
protected void onFinishInflate() {
|
||||
mScrollingScrim = requireNonNull(findViewById(R.id.screenshot_scrolling_scrim));
|
||||
mScreenshotStatic = requireNonNull(findViewById(R.id.global_screenshot_static));
|
||||
mScreenshotPreview = requireNonNull(findViewById(R.id.global_screenshot_preview));
|
||||
mScreenshotPreviewBorder = requireNonNull(
|
||||
@@ -280,6 +287,7 @@ public class ScreenshotView extends FrameLayout implements
|
||||
mBackgroundProtection = requireNonNull(
|
||||
findViewById(R.id.global_screenshot_actions_background));
|
||||
mDismissButton = requireNonNull(findViewById(R.id.global_screenshot_dismiss_button));
|
||||
mScrollablePreview = requireNonNull(findViewById(R.id.screenshot_scrollable_preview));
|
||||
mScreenshotFlash = requireNonNull(findViewById(R.id.global_screenshot_flash));
|
||||
mScreenshotSelectorView = requireNonNull(findViewById(R.id.global_screenshot_selector));
|
||||
mShareChip = requireNonNull(mActionsContainer.findViewById(R.id.screenshot_share_chip));
|
||||
@@ -328,6 +336,10 @@ public class ScreenshotView extends FrameLayout implements
|
||||
return mScreenshotPreview;
|
||||
}
|
||||
|
||||
View getScrollablePreview() {
|
||||
return mScrollablePreview;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set up the logger and callback on dismissal.
|
||||
*
|
||||
@@ -701,6 +713,38 @@ public class ScreenshotView extends FrameLayout implements
|
||||
}
|
||||
}
|
||||
|
||||
private Rect scrollableAreaOnScreen(ScrollCaptureResponse response) {
|
||||
Rect r = new Rect(response.getBoundsInWindow());
|
||||
Rect windowInScreen = response.getWindowBounds();
|
||||
r.offset(windowInScreen.left, windowInScreen.top);
|
||||
r.intersect(new Rect(0, 0, mDisplayMetrics.widthPixels, mDisplayMetrics.heightPixels));
|
||||
return r;
|
||||
}
|
||||
|
||||
void prepareScrollingTransition(ScrollCaptureResponse response, Bitmap screenBitmap) {
|
||||
Rect scrollableArea = scrollableAreaOnScreen(response);
|
||||
float scale = mCornerSizeX
|
||||
/ (mOrientationPortrait ? screenBitmap.getWidth() : screenBitmap.getHeight());
|
||||
ConstraintLayout.LayoutParams params =
|
||||
(ConstraintLayout.LayoutParams) mScrollablePreview.getLayoutParams();
|
||||
|
||||
params.width = (int) (scale * scrollableArea.width());
|
||||
params.height = (int) (scale * scrollableArea.height());
|
||||
Matrix matrix = new Matrix();
|
||||
matrix.setScale(scale, scale);
|
||||
matrix.postTranslate(0, -scrollableArea.top * scale);
|
||||
|
||||
mScrollablePreview.setTranslationX(scale * scrollableArea.left);
|
||||
mScrollablePreview.setTranslationY(scale * scrollableArea.top);
|
||||
mScrollablePreview.setImageMatrix(matrix);
|
||||
|
||||
mScrollingScrim.setImageBitmap(screenBitmap);
|
||||
mScrollingScrim.setVisibility(View.VISIBLE);
|
||||
mScrollablePreview.setImageBitmap(screenBitmap);
|
||||
mScrollablePreview.setVisibility(View.VISIBLE);
|
||||
createScreenshotFadeDismissAnimation(true).start();
|
||||
}
|
||||
|
||||
boolean isDismissing() {
|
||||
return (mDismissAnimation != null && mDismissAnimation.isRunning());
|
||||
}
|
||||
@@ -801,7 +845,7 @@ public class ScreenshotView extends FrameLayout implements
|
||||
transition.action.actionIntent.send();
|
||||
|
||||
// fade out non-preview UI
|
||||
createScreenshotFadeDismissAnimation().start();
|
||||
createScreenshotFadeDismissAnimation(false).start();
|
||||
} catch (PendingIntent.CanceledException e) {
|
||||
mPendingSharedTransition = false;
|
||||
if (transition.onCancelRunnable != null) {
|
||||
@@ -839,7 +883,7 @@ public class ScreenshotView extends FrameLayout implements
|
||||
return animSet;
|
||||
}
|
||||
|
||||
private ValueAnimator createScreenshotFadeDismissAnimation() {
|
||||
ValueAnimator createScreenshotFadeDismissAnimation(boolean fadePreview) {
|
||||
ValueAnimator alphaAnim = ValueAnimator.ofFloat(0, 1);
|
||||
alphaAnim.addUpdateListener(animation -> {
|
||||
float alpha = 1 - animation.getAnimatedFraction();
|
||||
@@ -847,6 +891,10 @@ public class ScreenshotView extends FrameLayout implements
|
||||
mActionsContainerBackground.setAlpha(alpha);
|
||||
mActionsContainer.setAlpha(alpha);
|
||||
mBackgroundProtection.setAlpha(alpha);
|
||||
mScreenshotPreviewBorder.setAlpha(alpha);
|
||||
if (fadePreview) {
|
||||
mScreenshotPreview.setAlpha(alpha);
|
||||
}
|
||||
});
|
||||
alphaAnim.setDuration(600);
|
||||
return alphaAnim;
|
||||
|
||||
@@ -405,6 +405,10 @@ public class ScrollCaptureClient {
|
||||
return new Rect(mWindowBounds);
|
||||
}
|
||||
|
||||
public Rect getBoundsInWindow() {
|
||||
return new Rect(mBoundsInWindow);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getMaxTiles() {
|
||||
return mMaxTiles;
|
||||
|
||||
Reference in New Issue
Block a user