Move scroll capture back to ScreenshotController.

Makes timing more flexible for acquisition, allowing for delayed content
loading.

Bug: 173700533
Test: Verify scroll content still loads with longer timeouts
Change-Id: I1cab53a6bda671854fb24203063c780923e34572
This commit is contained in:
Matt Casey
2021-04-16 10:45:39 -04:00
parent cb071176be
commit 0edac713f5
3 changed files with 101 additions and 52 deletions

View File

@@ -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();
}

View File

@@ -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);
}
}

View File

@@ -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