ScreenShot流程
base/services/core/java/com/android/server/policy/PhoneWindowManager.java
物理截屏:按下power键和音量下键
private void initKeyCombinationRules() {
mKeyCombinationManager = new KeyCombinationManager();
final boolean screenshotChordEnabled = mContext.getResources().getBoolean(
com.android.internal.R.bool.config_enableScreenshotChord);
if (screenshotChordEnabled) {
mKeyCombinationManager.addRule(
//按下电源键和音量下键
new TwoKeysCombinationRule(KEYCODE_VOLUME_DOWN, KEYCODE_POWER) {
@Override
void execute() {
mPowerKeyHandled = true;
interceptScreenshotChord();
}
@Override
void cancel() {
cancelPendingScreenshotChordAction();
}
});
}
}
收到截屏指令后,执行interceptScreenshotChord方法,
设置截屏类型
全屏:TAKE_SCREENSHOT_FULLSCREEN
用户选择区域:TAKE_SCREENSHOT_SELECTED_REGION
调用者提供的图像:TAKE_SCREENSHOT_PROVIDED_IMAGE
设置截屏来源
SCREENSHOT_KEY_CHORD
SCREENSHOT_KEY_OTHER
SCREENSHOT_OVERVIEW
SCREENSHOT_ACCESSIBILITY_ACTIONS
SCREENSHOT_OTHER
SCREENSHOT_VENDOR_GESTURE
private void interceptScreenshotChord() {
mHandler.removeCallbacks(mScreenshotRunnable);
mScreenshotRunnable.setScreenshotType(TAKE_SCREENSHOT_FULLSCREEN);
mScreenshotRunnable.setScreenshotSource(SCREENSHOT_KEY_CHORD);
mHandler.postDelayed(mScreenshotRunnable, getScreenshotChordLongPressDelay());
}
执行ScreenRunnale.run()方法
private class ScreenshotRunnable implements Runnable {
private int mScreenshotType = TAKE_SCREENSHOT_FULLSCREEN;
private int mScreenshotSource = SCREENSHOT_KEY_OTHER;
public void setScreenshotType(int screenshotType) {
mScreenshotType = screenshotType;
}
public void setScreenshotSource(int screenshotSource) {
mScreenshotSource = screenshotSource;
}
@Override
public void run() {
mDefaultDisplayPolicy.takeScreenshot(mScreenshotType, mScreenshotSource);
}
}
截屏takeScreenshot()
base/services/core/java/com/android/server/wm/DisplayPolicy.java
public void takeScreenshot(int screenshotType, int source) {
if (mScreenshotHelper != null) {
mScreenshotHelper.takeScreenshot(screenshotType,
getStatusBar() != null && getStatusBar().isVisible(),
getNavigationBar() != null && getNavigationBar().isVisible(),
source, mHandler, null /* completionConsumer */);
}
}
base/packages/SystemUI/src/com/android/systemui/screenshot/TakeScreenshotService.java
创建一个 GlobalScreenshot 对象,通过GlobalScreenshot对象实现截屏 private final GlobalScreenshot mScreenshot;
在Handler中对三种截屏方式做处理
private Handler mHandler = new Handler(Looper.myLooper()) {
@Override
public void handleMessage(Message msg) {
final Messenger callback = msg.replyTo;
Consumer<Uri> uriConsumer = uri -> {
Message reply = Message.obtain(null, SCREENSHOT_MSG_URI, uri);
try {
callback.send(reply);
} catch (RemoteException e) {
}
};
Runnable onComplete = () -> {
Message reply = Message.obtain(null, SCREENSHOT_MSG_PROCESS_COMPLETE);
try {
callback.send(reply);
} catch (RemoteException e) {
}
};
// If the storage for this user is locked, we have no place to store
// the screenshot, so skip taking it instead of showing a misleading
// animation and error notification.
if (!mUserManager.isUserUnlocked()) {
Log.w(TAG, "Skipping screenshot because storage is locked!");
post(() -> uriConsumer.accept(null));
post(onComplete);
return;
}
ScreenshotHelper.ScreenshotRequest screenshotRequest =
(ScreenshotHelper.ScreenshotRequest) msg.obj;
mUiEventLogger.log(ScreenshotEvent.getScreenshotSource(screenshotRequest.getSource()));
switch (msg.what) {
case WindowManager.TAKE_SCREENSHOT_FULLSCREEN:
mScreenshot.takeScreenshot(uriConsumer, onComplete);
break;
case WindowManager.TAKE_SCREENSHOT_SELECTED_REGION:
mScreenshot.takeScreenshotPartial(uriConsumer, onComplete);
break;
case WindowManager.TAKE_SCREENSHOT_PROVIDED_IMAGE:
Bitmap screenshot = BitmapUtil.bundleToHardwareBitmap(
screenshotRequest.getBitmapBundle());
Rect screenBounds = screenshotRequest.getBoundsInScreen();
Insets insets = screenshotRequest.getInsets();
int taskId = screenshotRequest.getTaskId();
int userId = screenshotRequest.getUserId();
ComponentName topComponent = screenshotRequest.getTopComponent();
mScreenshot.handleImageAsScreenshot(screenshot, screenBounds, insets,
taskId, userId, topComponent, uriConsumer, onComplete);
break;
default:
Log.d(TAG, "Invalid screenshot option: " + msg.what);
}
}
};
当截取全屏时
base/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java
private void takeScreenshot(Consumer<Uri> finisher, Rect crop) {
// copy the input Rect, since SurfaceControl.screenshot can mutate it
Rect screenRect = new Rect(crop);
int rot = mDisplay.getRotation();
int width = crop.width();
int height = crop.height();
takeScreenshot(SurfaceControl.screenshot(crop, width, height, rot), finisher, screenRect,
Insets.NONE, true);
}
private void takeScreenshot(Bitmap screenshot, Consumer<Uri> finisher, Rect screenRect,
Insets screenInsets, boolean showFlash) {
dismissScreenshot("new screenshot requested", true);
mScreenBitmap = screenshot;
if (mScreenBitmap == null) {
mNotificationsController.notifyScreenshotError(
R.string.screenshot_failed_to_capture_text);
finisher.accept(null);
mOnCompleteRunnable.run();
return;
}
if (!isUserSetupComplete()) {
// User setup isn't complete, so we don't want to show any UI beyond a toast, as editing
// and sharing shouldn't be exposed to the user.
saveScreenshotAndToast(finisher);
return;
}
// Optimizations
mScreenBitmap.setHasAlpha(false);
mScreenBitmap.prepareToDraw();
onConfigChanged(mContext.getResources().getConfiguration());
if (mDismissAnimation != null && mDismissAnimation.isRunning()) {
mDismissAnimation.cancel();
}
// The window is focusable by default
setWindowFocusable(true);
// Start the post-screenshot animation
startAnimation(finisher, screenRect, screenInsets, showFlash);
}
截屏之后开始截屏动画
private void startAnimation(final Consumer<Uri> finisher, Rect screenRect, Insets screenInsets,
boolean showFlash) {
// If power save is on, show a toast so there is some visual indication that a
// screenshot has been taken.
PowerManager powerManager = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
if (powerManager.isPowerSaveMode()) {
Toast.makeText(mContext, R.string.screenshot_saved_title, Toast.LENGTH_SHORT).show();
}
mScreenshotHandler.post(() -> {
if (!mScreenshotLayout.isAttachedToWindow()) {
mWindowManager.addView(mScreenshotLayout, mWindowLayoutParams);
}
mScreenshotAnimatedView.setImageDrawable(
createScreenDrawable(mScreenBitmap, screenInsets));
setAnimatedViewSize(screenRect.width(), screenRect.height());
// Show when the animation starts
mScreenshotAnimatedView.setVisibility(View.GONE);
mScreenshotPreview.setImageDrawable(createScreenDrawable(mScreenBitmap, screenInsets));
// make static preview invisible (from gone) so we can query its location on screen
mScreenshotPreview.setVisibility(View.INVISIBLE);
mScreenshotHandler.post(() -> {
mScreenshotLayout.getViewTreeObserver().addOnComputeInternalInsetsListener(this);
mScreenshotAnimation =
createScreenshotDropInAnimation(screenRect, showFlash);
saveScreenshotInWorkerThread(finisher, new ActionsReadyListener() {
@Override
void onActionsReady(SavedImageData imageData) {
showUiOnActionsReady(imageData);
}
});
// Play the shutter sound to notify that we've taken a screenshot
mCameraSound.play(MediaActionSound.SHUTTER_CLICK);
mScreenshotPreview.setLayerType(View.LAYER_TYPE_HARDWARE, null);
mScreenshotPreview.buildLayer();
mScreenshotAnimation.start();
});
});
}