Android4.0 源码-Screenshot 截屏流程分析

时间:2022-10-07 17:20:45


LOG: I/ActivityManager(  195): Start proc com.android.systemui:screenshot for service com.android.systemui/.screenshot.TakeScreenshotServ}

1)  按下组合键,进如PhoneWindowManager.java (/framework/base/policy/src/com/android/internal/policy/impl )

 public int interceptKeyBeforeQueueing(KeyEvent event, int policyFlags, boolean isScreenOn) {
        final boolean down = event.getAction() == KeyEvent.ACTION_DOWN;
        final boolean canceled = event.isCanceled();
        final int keyCode = event.getKeyCode();
....

       switch (keyCode) {
           ....
                if (keyCode == KeyEvent.KEYCODE_VOLUME_DOWN) {
                    if (down) {
                        if (isScreenOn && !mVolumeDownKeyTriggered
                                && (event.getFlags() & KeyEvent.FLAG_FALLBACK) == 0) {
                            mVolumeDownKeyTriggered = true;
                            mVolumeDownKeyTime = event.getDownTime();
                            mVolumeDownKeyConsumedByScreenshotChord = false;
                            cancelPendingPowerKeyAction();
                            interceptScreenshotChord();
                      Slog.d(TAG,"[Paul]interceptKeyBeforeQueueing.........Call  interceptScreenshotChord().... 1 "); //按组合键之后先到这
                        }
                    } else {
                        mVolumeDownKeyTriggered = false;
                        cancelPendingScreenshotChordAction();
                     Slog.d(TAG,"[Paul]interceptKeyBeforeQueueing.........Call cancelPendingScreenshotChordAction().... 2 "); //按键放手到这
            
                    }
                } else if (keyCode == KeyEvent.KEYCODE_VOLUME_UP) {
....

 }

2)Call interceptScreenshotChord() 方法;


 private void cancelPendingPowerKeyAction() {
if (!mPowerKeyHandled) {
mHandler.removeCallbacks(mPowerLongPress);
}
if (mPowerKeyTriggered) {
mPendingPowerKeyUpCanceled = true;
Slog.d(TAG,"[Paul]cancelPendingPowerKeyAction...........5 "); //paul
}
}

private void interceptScreenshotChord() {
if (mVolumeDownKeyTriggered && mPowerKeyTriggered && !mVolumeUpKeyTriggered) {
final long now = SystemClock.uptimeMillis();
if (now <= mVolumeDownKeyTime + SCREENSHOT_CHORD_DEBOUNCE_DELAY_MILLIS
&& now <= mPowerKeyTime + SCREENSHOT_CHORD_DEBOUNCE_DELAY_MILLIS) {
mVolumeDownKeyConsumedByScreenshotChord = true;
cancelPendingPowerKeyAction();
Slog.d(TAG,"[Paul]interceptScreenshotChord...........6 "); //paul
mHandler.postDelayed(mScreenshotChordLongPress,
ViewConfiguration.getGlobalActionKeyTimeout());
}
}
}

3)运行takeScreenshot() 方法:

 private final Runnable mScreenshotChordLongPress = new Runnable() {
public void run() {
Slog.d(TAG,"[Paul]mScreenshotChordLongPress...........7 "); //paul
takeScreenshot();
}
};

4)哈哈  跑到TakeScreenshotService里去了

  private void takeScreenshot() {
synchronized (mScreenshotLock) {
if (mScreenshotConnection != null) {
return;
}
ComponentName cn = new ComponentName("com.android.systemui",
"com.android.systemui.screenshot.TakeScreenshotService");
Slog.d(TAG,"[Paul]takeScreenshot()...................8 "); //paul
Intent intent = new Intent();
intent.setComponent(cn);
ServiceConnection conn = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
synchronized (mScreenshotLock) {
if (mScreenshotConnection != this) {
return;
}
Messenger messenger = new Messenger(service);
Message msg = Message.obtain(null, 1);
final ServiceConnection myConn = this;
Handler h = new Handler(mHandler.getLooper()) {
@Override
public void handleMessage(Message msg) {
synchronized (mScreenshotLock) {
if (mScreenshotConnection == myConn) {
mContext.unbindService(mScreenshotConnection);
mScreenshotConnection = null;
mHandler.removeCallbacks(mScreenshotTimeout);
}
}
}
};
msg.replyTo = new Messenger(h);
msg.arg1 = msg.arg2 = 0;
if (mStatusBar != null && mStatusBar.isVisibleLw())
msg.arg1 = 1;
if (mNavigationBar != null && mNavigationBar.isVisibleLw())
msg.arg2 = 1;
try {
messenger.send(msg);
} catch (RemoteException e) {
}
}
}
@Override
public void onServiceDisconnected(ComponentName name) {}
};
if (mContext.bindService(intent, conn, Context.BIND_AUTO_CREATE)) {
mScreenshotConnection = conn;
mHandler.postDelayed(mScreenshotTimeout, 10000);
}
}
}

5)TakeScreenshotService.java  (/frameworks/base/packages/SystemUI/src/com/android/systemui/screenshot )

     

public class TakeScreenshotService extends Service {
private static final String TAG = "TakeScreenshotService";

private static GlobalScreenshot mScreenshot;

private Handler mHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case 1:
final Messenger callback = msg.replyTo;
if (mScreenshot == null) {
mScreenshot = new GlobalScreenshot(TakeScreenshotService.this);
}
mScreenshot.takeScreenshot(new Runnable() {
@Override public void run() {
Message reply = Message.obtain(null, 1);
try {
callback.send(reply);
} catch (RemoteException e) {
}
}
}, msg.arg1 > 0, msg.arg2 > 0);
}
}
};

6) GlobalScreenshot.java (/frameworks/base/packages/SystemUI/src/com/android/systemui/screenshot )  终于找到真相了,隐藏在这里!这个方法会完成截图动作,以及显示动画。


 /**
* Takes a screenshot of the current display and shows an animation.
*/
void takeScreenshot(Runnable finisher, boolean statusBarVisible, boolean navBarVisible) {
// We need to orient the screenshot correctly (and the Surface api seems to take screenshots
// only in the natural orientation of the device :!)
mDisplay.getRealMetrics(mDisplayMetrics);
float[] dims = {mDisplayMetrics.widthPixels, mDisplayMetrics.heightPixels};
float degrees = getDegreesForRotation(mDisplay.getRotation());
boolean requiresRotation = (degrees > 0);
if (requiresRotation) {
// Get the dimensions of the device in its native orientation
mDisplayMatrix.reset();
mDisplayMatrix.preRotate(-degrees);
mDisplayMatrix.mapPoints(dims);
dims[0] = Math.abs(dims[0]);
dims[1] = Math.abs(dims[1]);
}

// Take the screenshot
mScreenBitmap = Surface.screenshot((int) dims[0], (int) dims[1]);
if (mScreenBitmap == null) {
notifyScreenshotError(mContext, mNotificationManager);
finisher.run();
return;
}

if (requiresRotation) {
// Rotate the screenshot to the current orientation
Bitmap ss = Bitmap.createBitmap(mDisplayMetrics.widthPixels,
mDisplayMetrics.heightPixels, Bitmap.Config.ARGB_8888);
Canvas c = new Canvas(ss);
c.translate(ss.getWidth() / 2, ss.getHeight() / 2);
c.rotate(degrees);
c.translate(-dims[0] / 2, -dims[1] / 2);
c.drawBitmap(mScreenBitmap, 0, 0, null);
c.setBitmap(null);
mScreenBitmap = ss;
}

// Optimizations
mScreenBitmap.setHasAlpha(false);
mScreenBitmap.prepareToDraw();

// Start the post-screenshot animation
startAnimation(finisher, mDisplayMetrics.widthPixels, mDisplayMetrics.heightPixels,
statusBarVisible, navBarVisible);
}


private void startAnimation(final Runnable finisher, int w, int h, boolean statusBarVisible,
            boolean navBarVisible) {
        // Add the view for the animation
        mScreenshotView.setImageBitmap(mScreenBitmap);
        mScreenshotLayout.requestFocus();

        // Setup the animation with the screenshot just taken
        if (mScreenshotAnimation != null) {
            mScreenshotAnimation.end();
        }

        mWindowManager.addView(mScreenshotLayout, mWindowLayoutParams);
        ValueAnimator screenshotDropInAnim = createScreenshotDropInAnimation();
        ValueAnimator screenshotFadeOutAnim = createScreenshotDropOutAnimation(w, h,
                statusBarVisible, navBarVisible);
        mScreenshotAnimation = new AnimatorSet();
        mScreenshotAnimation.playSequentially(screenshotDropInAnim, screenshotFadeOutAnim);
        mScreenshotAnimation.addListener(new AnimatorListenerAdapter() {
            @Override
            public void onAnimationEnd(Animator animation) {
                // Save the screenshot once we have a bit of time now
                saveScreenshotInWorkerThread(finisher);
                mWindowManager.removeView(mScreenshotLayout);
            }
        });
        mScreenshotLayout.post(new Runnable() {
            @Override
            public void run() {
                // Play the shutter sound to notify that we've taken a screenshot
                mCameraSound.playSound(CameraSound.SHUTTER_CLICK); //截图会播放快门音啊

                mScreenshotView.setLayerType(View.LAYER_TYPE_HARDWARE, null);
                mScreenshotView.buildLayer();
                mScreenshotAnimation.start();
            }
        });

接下来关键看把截图的图片存放在何处?!

7)还是刚刚上面的那个类里。

/**
* Creates a new worker thread and saves the screenshot to the media store.
*/
private void saveScreenshotInWorkerThread(Runnable finisher) {
SaveImageInBackgroundData data = new SaveImageInBackgroundData();
data.context = mContext;
data.image = mScreenBitmap;
data.iconSize = mNotificationIconSize;
data.finisher = finisher;
new SaveImageInBackgroundTask(mContext, data, mNotificationManager,
SCREENSHOT_NOTIFICATION_ID).execute(data);
}

8)这回是到类外个类里面,发现在这里有environment里面的成员。

SaveImageInBackgroundTask(Context context, SaveImageInBackgroundData data,
NotificationManager nManager, int nId) {
Resources r = context.getResources();

// Prepare all the output metadata
mImageTime = System.currentTimeMillis();
String imageDate = new SimpleDateFormat("yyyy-MM-dd-HH-mm-ss").format(new Date(mImageTime));
String imageDir = Environment.getExternalStoragePublicDirectory(
Environment.DIRECTORY_PICTURES).getAbsolutePath();

mImageFileName = String.format(SCREENSHOT_FILE_NAME_TEMPLATE, imageDate);
mImageFilePath = String.format(SCREENSHOT_FILE_PATH_TEMPLATE, imageDir,
SCREENSHOTS_DIR_NAME, mImageFileName);

// Create the large notification icon
int imageWidth = data.image.getWidth();
int imageHeight = data.image.getHeight();
int iconWidth = data.iconSize;
int iconHeight = data.iconSize;
if (imageWidth > imageHeight) {
iconWidth = (int) (((float) iconHeight / imageHeight) * imageWidth);
} else {
iconHeight = (int) (((float) iconWidth / imageWidth) * imageHeight);
}
Bitmap rawIcon = Bitmap.createScaledBitmap(data.image, iconWidth, iconHeight, true);
Bitmap croppedIcon = Bitmap.createBitmap(rawIcon, (iconWidth - data.iconSize) / 2,
(iconHeight - data.iconSize) / 2, data.iconSize, data.iconSize);

// Show the intermediate notification
mTickerAddSpace = !mTickerAddSpace;
mNotificationId = nId;
mNotificationManager = nManager;
mNotificationBuilder = new Notification.Builder(context)
.setTicker(r.getString(R.string.screenshot_saving_ticker)
+ (mTickerAddSpace ? " " : ""))
.setContentTitle(r.getString(R.string.screenshot_saving_title))
.setContentText(r.getString(R.string.screenshot_saving_text))
.setSmallIcon(R.drawable.stat_notify_image)
.setWhen(System.currentTimeMillis());
Notification n = mNotificationBuilder.getNotification();
n.flags |= Notification.FLAG_NO_CLEAR;
mNotificationManager.notify(nId, n);

// On the tablet, the large icon makes the notification appear as if it is clickable (and
// on small devices, the large icon is not shown) so defer showing the large icon until
// we compose the final post-save notification below.
mNotificationBuilder.setLargeIcon(croppedIcon);
}

9)Environment.java (framework/base/core/java/android/os )


public static File getExternalStoragePublicDirectory(String type) {
return new File(getExternalStorageDirectory(), type);
}

   public static File getExternalStorageDirectory() {
return EXTERNAL_STORAGE_DIRECTORY;
}

 private static final File EXTERNAL_STORAGE_DIRECTORY
= getDirectory("EXTERNAL_STORAGE", "/mnt/sdcard");


终于截图功能基本上分析完成,存放路径也找到,很简单,一路下来~~ENVIRONMENT.JAVA 里面好多资源啊:

参考如下:http://blog.csdn.net/djpraul/article/details/8727534