Merge changes Ie275f5c3,I1cab53a6 into sc-dev
* changes: Move RecyclerView scroll capture to the center Move scroll capture back to ScreenshotController.
This commit is contained in:
@@ -105,6 +105,14 @@ public class RecyclerViewCaptureHelper implements ScrollCaptureViewHelper<ViewGr
|
||||
int prevAnchorTop = anchor.getTop();
|
||||
// Note: requestChildRectangleOnScreen may modify rectangle, must pass pass in a copy here
|
||||
Rect input = new Rect(requestedContentBounds);
|
||||
// Expand input rect to get the requested rect to be in the center
|
||||
int remainingHeight = recyclerView.getHeight() - recyclerView.getPaddingTop()
|
||||
- recyclerView.getPaddingBottom() - input.height();
|
||||
if (remainingHeight > 0) {
|
||||
input.inset(0, -remainingHeight / 2);
|
||||
}
|
||||
Log.d(TAG, "input (post center adjustment) = " + input);
|
||||
|
||||
if (recyclerView.requestChildRectangleOnScreen(anchor, input, true)) {
|
||||
int scrolled = prevAnchorTop - anchor.getTop(); // inverse of movement
|
||||
Log.d(TAG, "RecyclerView scrolled by " + scrolled + " px");
|
||||
|
||||
@@ -145,7 +145,9 @@ public class RecyclerViewCaptureHelperTest {
|
||||
ScrollResult scrollResult = rvc.onScrollRequested(mTarget, scrollBounds, request);
|
||||
assertThat(request).isEqualTo(scrollResult.requestedArea);
|
||||
assertThat(request).isEqualTo(scrollResult.availableArea);
|
||||
assertThat(scrollResult.scrollDelta).isEqualTo(CAPTURE_HEIGHT);
|
||||
// Capture height centered in the window
|
||||
assertThat(scrollResult.scrollDelta).isEqualTo(
|
||||
CAPTURE_HEIGHT + (WINDOW_HEIGHT - CAPTURE_HEIGHT) / 2);
|
||||
assertAvailableAreaCompletelyVisible(scrollResult, mTarget);
|
||||
}
|
||||
|
||||
@@ -163,7 +165,8 @@ public class RecyclerViewCaptureHelperTest {
|
||||
ScrollResult scrollResult = helper.onScrollRequested(mTarget, scrollBounds, request);
|
||||
assertThat(request).isEqualTo(scrollResult.requestedArea);
|
||||
assertThat(request).isEqualTo(scrollResult.availableArea);
|
||||
assertThat(scrollResult.scrollDelta).isEqualTo(-CAPTURE_HEIGHT);
|
||||
assertThat(scrollResult.scrollDelta).isEqualTo(
|
||||
-CAPTURE_HEIGHT - (WINDOW_HEIGHT - CAPTURE_HEIGHT) / 2);
|
||||
assertAvailableAreaCompletelyVisible(scrollResult, mTarget);
|
||||
}
|
||||
|
||||
@@ -182,7 +185,8 @@ public class RecyclerViewCaptureHelperTest {
|
||||
ScrollResult scrollResult = helper.onScrollRequested(mTarget, scrollBounds, request);
|
||||
assertThat(request).isEqualTo(scrollResult.requestedArea);
|
||||
assertThat(request).isEqualTo(scrollResult.availableArea);
|
||||
assertThat(scrollResult.scrollDelta).isEqualTo(CAPTURE_HEIGHT);
|
||||
assertThat(scrollResult.scrollDelta).isEqualTo(
|
||||
CAPTURE_HEIGHT + (WINDOW_HEIGHT - CAPTURE_HEIGHT) / 2);
|
||||
assertAvailableAreaCompletelyVisible(scrollResult, mTarget);
|
||||
}
|
||||
|
||||
@@ -200,7 +204,8 @@ public class RecyclerViewCaptureHelperTest {
|
||||
ScrollResult scrollResult = helper.onScrollRequested(mTarget, scrollBounds, request);
|
||||
assertThat(request).isEqualTo(scrollResult.requestedArea);
|
||||
assertThat(request).isEqualTo(scrollResult.availableArea);
|
||||
assertThat(scrollResult.scrollDelta).isEqualTo(-CAPTURE_HEIGHT);
|
||||
assertThat(scrollResult.scrollDelta).isEqualTo(
|
||||
-CAPTURE_HEIGHT - (WINDOW_HEIGHT - CAPTURE_HEIGHT) / 2);
|
||||
assertAvailableAreaCompletelyVisible(scrollResult, mTarget);
|
||||
}
|
||||
|
||||
|
||||
@@ -18,7 +18,6 @@ package com.android.systemui.screenshot;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.ComponentName;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.HardwareRenderer;
|
||||
@@ -32,7 +31,6 @@ import android.os.Bundle;
|
||||
import android.os.UserHandle;
|
||||
import android.text.TextUtils;
|
||||
import android.util.Log;
|
||||
import android.view.IWindowManager;
|
||||
import android.view.ScrollCaptureResponse;
|
||||
import android.view.View;
|
||||
import android.widget.ImageView;
|
||||
@@ -65,14 +63,10 @@ public class LongScreenshotActivity extends Activity {
|
||||
private static final String KEY_SAVED_IMAGE_PATH = "saved-image-path";
|
||||
|
||||
private final UiEventLogger mUiEventLogger;
|
||||
private final ScrollCaptureController mScrollCaptureController;
|
||||
private final Executor mUiExecutor;
|
||||
private final Executor mBackgroundExecutor;
|
||||
private final ImageExporter mImageExporter;
|
||||
|
||||
// If true, the activity is re-loading an image from storage, which should either succeed and
|
||||
// populate the UI or fail and finish the activity.
|
||||
private boolean mRestoringInstance;
|
||||
private final LongScreenshotHolder mLongScreenshotHolder;
|
||||
|
||||
private ImageView mPreview;
|
||||
private View mSave;
|
||||
@@ -86,7 +80,6 @@ public class LongScreenshotActivity extends Activity {
|
||||
private ListenableFuture<File> mCacheSaveFuture;
|
||||
private ListenableFuture<ImageLoader.Result> mCacheLoadFuture;
|
||||
|
||||
private ListenableFuture<LongScreenshot> mLongScreenshotFuture;
|
||||
private LongScreenshot mLongScreenshot;
|
||||
|
||||
private enum PendingAction {
|
||||
@@ -97,13 +90,13 @@ public class LongScreenshotActivity extends Activity {
|
||||
|
||||
@Inject
|
||||
public LongScreenshotActivity(UiEventLogger uiEventLogger, ImageExporter imageExporter,
|
||||
@Main Executor mainExecutor, @Background Executor bgExecutor, IWindowManager wms,
|
||||
Context context, ScrollCaptureController scrollCaptureController) {
|
||||
@Main Executor mainExecutor, @Background Executor bgExecutor,
|
||||
LongScreenshotHolder longScreenshotHolder) {
|
||||
mUiEventLogger = uiEventLogger;
|
||||
mUiExecutor = mainExecutor;
|
||||
mBackgroundExecutor = bgExecutor;
|
||||
mImageExporter = imageExporter;
|
||||
mScrollCaptureController = scrollCaptureController;
|
||||
mLongScreenshotHolder = longScreenshotHolder;
|
||||
}
|
||||
|
||||
|
||||
@@ -152,7 +145,7 @@ public class LongScreenshotActivity extends Activity {
|
||||
super.onStart();
|
||||
|
||||
if (mCacheLoadFuture != null) {
|
||||
Log.d(TAG, "mRestoringInstance = true");
|
||||
Log.d(TAG, "mCacheLoadFuture != null");
|
||||
final ListenableFuture<ImageLoader.Result> future = mCacheLoadFuture;
|
||||
mCacheLoadFuture.addListener(() -> {
|
||||
Log.d(TAG, "cached bitmap load complete");
|
||||
@@ -170,40 +163,19 @@ public class LongScreenshotActivity extends Activity {
|
||||
}, mUiExecutor);
|
||||
mCacheLoadFuture = null;
|
||||
return;
|
||||
}
|
||||
|
||||
if (mLongScreenshotFuture == null) {
|
||||
Log.d(TAG, "mLongScreenshotFuture == null");
|
||||
// First run through, ensure we have a connection to use (see #onCreate)
|
||||
if (mScrollCaptureResponse == null || !mScrollCaptureResponse.isConnected()) {
|
||||
Log.e(TAG, "Did not receive a live scroll capture connection, bailing out!");
|
||||
finishAndRemoveTask();
|
||||
return;
|
||||
}
|
||||
mLongScreenshotFuture = mScrollCaptureController.run(mScrollCaptureResponse);
|
||||
mLongScreenshotFuture.addListener(() -> {
|
||||
LongScreenshot longScreenshot;
|
||||
try {
|
||||
longScreenshot = mLongScreenshotFuture.get();
|
||||
} catch (CancellationException | InterruptedException | ExecutionException e) {
|
||||
Log.e(TAG, "Error capturing long screenshot!", e);
|
||||
finishAndRemoveTask();
|
||||
return;
|
||||
}
|
||||
if (longScreenshot.getHeight() == 0) {
|
||||
Log.e(TAG, "Got a zero height result");
|
||||
finishAndRemoveTask();
|
||||
return;
|
||||
}
|
||||
onCaptureCompleted(longScreenshot);
|
||||
}, mUiExecutor);
|
||||
} else {
|
||||
Log.d(TAG, "mLongScreenshotFuture != null");
|
||||
LongScreenshot longScreenshot = mLongScreenshotHolder.takeLongScreenshot();
|
||||
if (longScreenshot != null) {
|
||||
onLongScreenshotReceived(longScreenshot);
|
||||
} else {
|
||||
Log.e(TAG, "No long screenshot available!");
|
||||
finishAndRemoveTask();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void onCaptureCompleted(LongScreenshot longScreenshot) {
|
||||
Log.d(TAG, "onCaptureCompleted(longScreenshot=" + longScreenshot + ")");
|
||||
private void onLongScreenshotReceived(LongScreenshot longScreenshot) {
|
||||
Log.d(TAG, "onLongScreenshotReceived(longScreenshot=" + longScreenshot + ")");
|
||||
mLongScreenshot = longScreenshot;
|
||||
mPreview.setImageDrawable(mLongScreenshot.getDrawable());
|
||||
updateImageDimensions();
|
||||
@@ -283,9 +255,6 @@ public class LongScreenshotActivity extends Activity {
|
||||
}
|
||||
cleanupCache();
|
||||
|
||||
if (mLongScreenshotFuture != null) {
|
||||
mLongScreenshotFuture.cancel(true);
|
||||
}
|
||||
if (mLongScreenshot != null) {
|
||||
mLongScreenshot.release();
|
||||
}
|
||||
|
||||
@@ -0,0 +1,59 @@
|
||||
/*
|
||||
* 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 com.android.systemui.dagger.SysUISingleton;
|
||||
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
/**
|
||||
* LongScreenshotHolder holds on to 1 LongScreenshot reference to facilitate indirect in-process
|
||||
* passing.
|
||||
*/
|
||||
@SysUISingleton
|
||||
public class LongScreenshotHolder {
|
||||
private final AtomicReference<ScrollCaptureController.LongScreenshot> mLongScreenshot;
|
||||
|
||||
@Inject
|
||||
public LongScreenshotHolder() {
|
||||
mLongScreenshot = new AtomicReference<>();
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the holder's stored LongScreenshot.
|
||||
*/
|
||||
public void setLongScreenshot(ScrollCaptureController.LongScreenshot longScreenshot) {
|
||||
mLongScreenshot.set(longScreenshot);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return true if the holder has a non-null LongScreenshot.
|
||||
*/
|
||||
public boolean hasLongScreenshot() {
|
||||
return mLongScreenshot.get() != null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the current stored LongScreenshot, clear the holder's storage.
|
||||
*/
|
||||
public ScrollCaptureController.LongScreenshot takeLongScreenshot() {
|
||||
return mLongScreenshot.getAndSet(null);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -191,6 +191,8 @@ public class ScreenshotController {
|
||||
private final ScrollCaptureClient mScrollCaptureClient;
|
||||
private final PhoneWindow mWindow;
|
||||
private final DisplayManager mDisplayManager;
|
||||
private final ScrollCaptureController mScrollCaptureController;
|
||||
private final LongScreenshotHolder mLongScreenshotHolder;
|
||||
|
||||
private ScreenshotView mScreenshotView;
|
||||
private Bitmap mScreenBitmap;
|
||||
@@ -233,13 +235,17 @@ public class ScreenshotController {
|
||||
ScrollCaptureClient scrollCaptureClient,
|
||||
UiEventLogger uiEventLogger,
|
||||
ImageExporter imageExporter,
|
||||
@Main Executor mainExecutor) {
|
||||
@Main Executor mainExecutor,
|
||||
ScrollCaptureController scrollCaptureController,
|
||||
LongScreenshotHolder longScreenshotHolder) {
|
||||
mScreenshotSmartActions = screenshotSmartActions;
|
||||
mNotificationsController = screenshotNotificationsController;
|
||||
mScrollCaptureClient = scrollCaptureClient;
|
||||
mUiEventLogger = uiEventLogger;
|
||||
mImageExporter = imageExporter;
|
||||
mMainExecutor = mainExecutor;
|
||||
mScrollCaptureController = scrollCaptureController;
|
||||
mLongScreenshotHolder = longScreenshotHolder;
|
||||
mBgExecutor = Executors.newSingleThreadExecutor();
|
||||
|
||||
mDisplayManager = requireNonNull(context.getSystemService(DisplayManager.class));
|
||||
@@ -569,15 +575,30 @@ public class ScreenshotController {
|
||||
}
|
||||
Log.d(TAG, "ScrollCapture: connected to window ["
|
||||
+ mLastScrollCaptureResponse.getWindowTitle() + "]");
|
||||
final Intent intent = new Intent(mContext, LongScreenshotActivity.class);
|
||||
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP);
|
||||
intent.putExtra(LongScreenshotActivity.EXTRA_CAPTURE_RESPONSE,
|
||||
mLastScrollCaptureResponse);
|
||||
|
||||
final ScrollCaptureResponse response = mLastScrollCaptureResponse;
|
||||
mScreenshotView.showScrollChip(/* onClick */ () -> {
|
||||
// Clear the reference to prevent close() in dismissScreenshot
|
||||
mLastScrollCaptureResponse = null;
|
||||
mContext.startActivity(intent);
|
||||
dismissScreenshot(false);
|
||||
final ListenableFuture<ScrollCaptureController.LongScreenshot> future =
|
||||
mScrollCaptureController.run(response);
|
||||
future.addListener(() -> {
|
||||
ScrollCaptureController.LongScreenshot longScreenshot;
|
||||
try {
|
||||
longScreenshot = future.get();
|
||||
} catch (CancellationException | InterruptedException | ExecutionException e) {
|
||||
Log.e(TAG, "Exception", e);
|
||||
return;
|
||||
}
|
||||
|
||||
mLongScreenshotHolder.setLongScreenshot(longScreenshot);
|
||||
|
||||
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);
|
||||
}, mMainExecutor);
|
||||
});
|
||||
} catch (CancellationException e) {
|
||||
// Ignore
|
||||
|
||||
Reference in New Issue
Block a user