Android 10.0 截屏流程

手机软件开发 2024-9-30 09:21:09 86 0 来自 中国
通常未通过特殊定制的 Android 体系,截屏都是颠末同时按住音量下键和电源键来截屏。本篇文章就只讨论使用这些特殊按键来举行截屏。
这里我们就要明确变乱是在那里举行分发拦截的。通过源码的分析,我们发现是在PhoneWindowManager.java 中。
PhoneWindowManager#interceptKeyBeforeQueueing()
// frameworks\base\services\core\java\com\android\server\policy\PhoneWindowManager.java    @Override    public int interceptKeyBeforeQueueing(KeyEvent event, int policyFlags) {        if (!mSystemBooted) {            // If we have not yet booted, don't let key events do anything.            return 0;        }        // 省略部分代码......        // Handle special keys.        switch (keyCode) {             ......            case KeyEvent.KEYCODE_VOLUME_DOWN:            case KeyEvent.KEYCODE_VOLUME_UP:            case KeyEvent.KEYCODE_VOLUME_MUTE: {                // 按下音量键调用                handleVolumeKey(event, policyFlags);                ......                break;            }            ......            case KeyEvent.KEYCODE_POWER: {                ......                if (down) {                    // 按下电源键将调用                    interceptPowerKeyDown(event, interactive);                } else {                              interceptPowerKeyUp(event, interactive, canceled);                }                break;            }        }        return result;    }1、电源键处理惩罚

PhoneWindowManager#interceptPowerKeyDown()
// PhoneWindowManager.java    private void interceptPowerKeyDown(KeyEvent event, boolean interactive) {        // 省略部分代码......        // Latch power key state to detect screenshot chord.        if (interactive && !mScreenshotChordPowerKeyTriggered                && (event.getFlags() & KeyEvent.FLAG_FALLBACK) == 0) {            // power键按下的标志            mScreenshotChordPowerKeyTriggered = true;            // 获取 Power 键的触发时间            mScreenshotChordPowerKeyTime = event.getDownTime();            // 处理惩罚屏幕截图变乱            interceptScreenshotChord();            // 这个方法应该是斲丧、拦截变乱的,制止改变音量、铃声等。            interceptRingerToggleChord();        }        // 省略部分代码......    }interceptScreenshotChord()该方法下面再说,先先容电源按键、音量按键的处理惩罚。
2、音量键处理惩罚

PhoneWindowManager#handleVolumeKey()
// PhoneWindowManager.java    public void handleVolumeKey(KeyEvent event, int policyFlags) {        final boolean down = event.getAction() == KeyEvent.ACTION_DOWN;        final int keyCode = event.getKeyCode();        final boolean interactive = (policyFlags & FLAG_INTERACTIVE) != 0;        if (keyCode == KeyEvent.KEYCODE_VOLUME_DOWN) {            if (down) {                // Any activity on the vol down button stops the ringer toggle shortcut                cancelPendingRingerToggleChordAction();                if (interactive && !mScreenshotChordVolumeDownKeyTriggered                        && (event.getFlags() & KeyEvent.FLAG_FALLBACK) == 0) {                    // Volume键按下的标志                    mScreenshotChordVolumeDownKeyTriggered = true;                    // 获取 Volume 键的触发时间                    mScreenshotChordVolumeDownKeyTime = event.getDownTime();                    // 赋值  false 该属性为了防止截屏的时间音量下键见效出现调治音量的 dialog 状态值                    mScreenshotChordVolumeDownKeyConsumed = false;                    // 防止触发 Power  键长按功能                    cancelPendingPowerKeyAction();                    //处理惩罚屏幕截图变乱                    interceptScreenshotChord();                    // 拦截相关快捷键                    interceptAccessibilityShortcutChord();                }            } else {                 // 省略部分代码......            }        } else if (keyCode == KeyEvent.KEYCODE_VOLUME_UP) {             // 省略部分代码......        }        return;    }3、截屏变乱处理惩罚 interceptScreenshotChord()

PhoneWindowManager#interceptScreenshotChord()
// PhoneWindowManager.java    private void interceptScreenshotChord() {          /*           * if 判断参数先容           * mScreenshotChordEnabled 其值为mContext.getResources().getBoolean(com.android.internal.R.bool.config_enableScreenshotChord);           * mScreenshotChordVolumeDownKeyTriggered 音量下键按下时值为true           * mScreenshotChordPowerKeyTriggered  电源键按下时值为true           * mA11yShortcutChordVolumeUpKeyTriggered 音量上(加)键抬起时为false , 按下时为true           **/        if (mScreenshotChordEnabled                && mScreenshotChordVolumeDownKeyTriggered && mScreenshotChordPowerKeyTriggered                && !mA11yShortcutChordVolumeUpKeyTriggered) {            // 获取当前时间            final long now = SystemClock.uptimeMillis();            // 当前时间小于 音量下键按下时间 + 150ms            // 当前时间小于 power键按下时间 + 150ms            if (now <= mScreenshotChordVolumeDownKeyTime + SCREENSHOT_CHORD_DEBOUNCE_DELAY_MILLIS                    && now <= mScreenshotChordPowerKeyTime                    + SCREENSHOT_CHORD_DEBOUNCE_DELAY_MILLIS) {                boolean inLongScreenshot = Settings.System.getIntForUser(mContext.getContentResolver(),                        LONGSCREENSHOT_SETTING, 0, UserHandle.USER_CURRENT_OR_SELF) == 1;                if (hasInPowerUtrlSavingMode() || inLongScreenshot) {                    return;                }                      // 长按音量下键,到达截屏条件,将该变乱消耗掉。                mScreenshotChordVolumeDownKeyConsumed = true;                // 防止触发 Power  键长按功能                cancelPendingPowerKeyAction();                // 设置截图的范例,TAKE_SCREENSHOT_FULLSCREEN 为全屏                mScreenshotRunnable.setScreenshotType(TAKE_SCREENSHOT_FULLSCREEN);                // 截图的方式,(比方:按键、三指下滑 等等)                mScreenshotRunnable.setScreenshotSource(SCREENSHOT_KEY_CHORD);                //实行 mScreenshotRunnable                mHandler.postDelayed(mScreenshotRunnable, getScreenshotChordLongPressDelay());            }        }    }继承检察ScreenshotRunnable,此时会一步步向下调用,终极到SystemUI。
// PhoneWindowManager.java    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() {            // 回调到 DisplayPolicy.java            mDefaultDisplayPolicy.takeScreenshot(mScreenshotType, mScreenshotSource);        }    }DisplayPolicy#takeScreenshot()
// DisplayPolicy.java    // 哀求截取屏幕截图    public void takeScreenshot(int screenshotType, int source) {        if (mScreenshotHelper != null) {            mScreenshotHelper.takeScreenshot(screenshotType,                    mStatusBar != null && mStatusBar.isVisibleLw(),                    mNavigationBar != null && mNavigationBar.isVisibleLw(),                    source, mHandler, null /* completionConsumer */);        }    }继承往下看ScreenshotHelper#takeScreenshot()
// ScreenshotHelper.java    // 哀求截取屏幕截图    public void takeScreenshot(final int screenshotType, final boolean hasStatus,            final boolean hasNav, int source, @NonNull Handler handler,            @Nullable Consumer<Uri> completionConsumer) {        ScreenshotRequest screenshotRequest = new ScreenshotRequest(source, hasStatus, hasNav);        takeScreenshot(screenshotType, SCREENSHOT_TIMEOUT_MS, handler, screenshotRequest,                completionConsumer);    }    //到了 Binder调用环节, 此为客户端, 服务端为SystemUI中的 TakeScreenshotService    private void takeScreenshot(final int screenshotType, long timeoutMs, @NonNull Handler handler,                ScreenshotRequest screenshotRequest, @Nullable Consumer<Uri> completionConsumer) {            synchronized (mScreenshotLock) {                final Runnable mScreenshotTimeout = () -> {                    synchronized (mScreenshotLock) {                        if (mScreenshotConnection != null) {                            // 在获取屏幕截图捕捉相应之前超时                            Log.e(TAG, "Timed out before getting screenshot capture response");                            // 重置毗连                            resetConnection();                            // 关照截屏错误                            notifyScreenshotError();                        }                    }                    if (completionConsumer != null) {                        completionConsumer.accept(null);                    }                };                Message msg = Message.obtain(null, screenshotType, screenshotRequest);                Handler h = new Handler(handler.getLooper()) {                    @Override                    public void handleMessage(Message msg) {                        switch (msg.what) {                            case SCREENSHOT_MSG_URI:                                if (completionConsumer != null) {                                    completionConsumer.accept((Uri) msg.obj);                                }                                handler.removeCallbacks(mScreenshotTimeout);                                break;                            case SCREENSHOT_MSG_PROCESS_COMPLETE:                                synchronized (mScreenshotLock) {                                    resetConnection();                                }                                break;                        }                    }                };                msg.replyTo = new Messenger(h);                if (mScreenshotConnection == null || mScreenshotService == null) {                    // 一个尺度的Service毗连                    // config_screenshotServiceComponent == com.android.systemui/com.android.systemui.screenshot.TakeScreenshotService                    final ComponentName serviceComponent = ComponentName.unflattenFromString(                            mContext.getResources().getString(                                    com.android.internal.R.string.config_screenshotServiceComponent));                    final Intent serviceIntent = new Intent();                    serviceIntent.setComponent(serviceComponent);                    ServiceConnection conn = new ServiceConnection() {                        @Override                        // 当Service毗连乐成之后                        public void onServiceConnected(ComponentName name, IBinder service) {                            synchronized (mScreenshotLock) {                                if (mScreenshotConnection != this) {                                    return;                                }                                mScreenshotService = service;                                Messenger messenger = new Messenger(mScreenshotService);                                try {                                    messenger.send(msg);                                } catch (RemoteException e) {                                    Log.e(TAG, "Couldn't take screenshot: " + e);                                    if (completionConsumer != null) {                                        completionConsumer.accept(null);                                    }                                }                            }                        }                        @Override                        // 当Service断开毗连时                        public void onServiceDisconnected(ComponentName name) {                            synchronized (mScreenshotLock) {                                if (mScreenshotConnection != null) {                                    resetConnection();                                    // only log an error if we're still within the timeout period                                    if (handler.hasCallbacks(mScreenshotTimeout)) {                                        handler.removeCallbacks(mScreenshotTimeout);                                        notifyScreenshotError();                                    }                                }                            }                        }                    };                    // bindService                    if (mContext.bindServiceAsUser(serviceIntent, conn,                            Context.BIND_AUTO_CREATE | Context.BIND_FOREGROUND_SERVICE,                            UserHandle.CURRENT)) {                        mScreenshotConnection = conn;                        handler.postDelayed(mScreenshotTimeout, timeoutMs);                    }                } else {                    // 假如已经毗连则直接发送Message                    Messenger messenger = new Messenger(mScreenshotService);                    try {                        messenger.send(msg);                    } catch (RemoteException e) {                        Log.e(TAG, "Couldn't take screenshot: " + e);                        if (completionConsumer != null) {                            completionConsumer.accept(null);                        }                    }                    handler.postDelayed(mScreenshotTimeout, timeoutMs);                }            }    }客户端通过向服务端发送 message 来将截屏任务交给 service,由 service 处理惩罚反面的操纵。
// TakeScreenshotService.java    private Handler mHandler = new Handler(Looper.myLooper()) {        @Override        public void handleMessage(Message msg) {            // 获取客户端传的 Messenger 对象            final Messenger callback = msg.replyTo;            Consumer<Uri> uriConsumer = uri -> {                Message reply = Message.obtain(null, SCREENSHOT_MSG_URI, uri);                try {                    / /Messenger 双向通讯,在服务端用长途客户端的 Messenger 对象给客户端发送信息                    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 (!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:  // 全屏截图                    // 我们在PhoneWindowManager传入的type为全屏截图,以是必要实行全屏截图流程                    mScreenshot.takeScreenshot(uriConsumer, onComplete);                    break;                case WindowManager.TAKE_SCREENSHOT_SELECTED_REGION:  // 地区截图                    mScreenshot.takeScreenshot(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);            }        }    };TakeScreenshotService调用GlobalScreenshot.java的takeScreenshot();
GlobalScreenshot#takeScreenshot()
// 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) {            // 假如没有Bitmap则陈诉错误信息            mNotificationsController.notifyScreenshotError(                    R.string.screenshot_failed_to_capture_text);            finisher.accept(null);            mOnCompleteRunnable.run();            return;        }        if (!isUserSetupComplete()) {            // 用户设置尚未完成,不应该向用户展示 分享和编辑 , 只表现一个Toast并生存图片            saveScreenshotAndToast(finisher);            return;        }        // Optimizations        mScreenBitmap.setHasAlpha(false);        mScreenBitmap.prepareToDraw();        onConfigChanged(mContext.getResources().getConfiguration());        if (mDismissAnimation != null && mDismissAnimation.isRunning()) {            mDismissAnimation.cancel();        }        // 获取核心        setWindowFocusable(true);        // 开始截图后动画        startAnimation(finisher, screenRect, screenInsets, showFlash);    }    /**     * 截屏后开始动画     */    private void startAnimation(final Consumer<Uri> finisher, Rect screenRect, Insets screenInsets,            boolean showFlash) {        if (mScreenshotIng == false) {//unisoc: Modify for bug1360276            mScreenshotIng = true;                 // 假如开启了省电模式,表现 toast,以便有一些视觉提示已截取屏幕截图            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()) {                    // mScreenshotLayout是截屏的缩略图的父View                    // mScreenshotLayout 在 GlobalScreenshot.java 的构造方法中初始化。对应结构文件:global_screenshot.xml                    mWindowManager.addView(mScreenshotLayout, mWindowLayoutParams);                }                // 动画相关的View                mScreenshotAnimatedView.setImageDrawable(                        createScreenDrawable(mScreenBitmap, screenInsets));                setAnimatedViewSize(screenRect.width(), screenRect.height());                // 表现动画何时开始                mScreenshotAnimatedView.setVisibility(View.GONE);                //缩略图表现的View,将native层返回的Bitmap加载到此View上                mScreenshotPreview.setImageDrawable(createScreenDrawable(mScreenBitmap, screenInsets));                // 使静态预览不可见(消失),以便我们可以在屏幕上查询其位置                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);                        mScreenshotIng = false;                    }                });                // 播放快门声音以关照我们已截屏                mCameraSound.play(MediaActionSound.SHUTTER_CLICK);                if (mScreenshotPreview.isAttachedToWindow()) {                    mScreenshotPreview.setLayerType(View.LAYER_TYPE_HARDWARE, null);                    mScreenshotPreview.buildLayer();                }                // 开始实办法画                mScreenshotAnimation.start();            });            });        }    }    /**     * 创建一个新的工作线程并将屏幕截图生存到媒体存储     */    private void saveScreenshotInWorkerThread(            Consumer<Uri> finisher, @Nullable ActionsReadyListener actionsReadyListener) {        SaveImageInBackgroundData data = new SaveImageInBackgroundData();        data.image = mScreenBitmap;  // native 层返回的 Bitmap        data.finisher = finisher;        data.mActionsReadyListener = actionsReadyListener;        if (mSaveInBgTask != null) {            // just log success/failure for the pre-existing screenshot            // 只需记载预先存在的屏幕截图的乐成失败            mSaveInBgTask.setActionsReadyListener(new ActionsReadyListener() {                @Override                void onActionsReady(SavedImageData imageData) {                    logSuccessOnActionsReady(imageData);                }            });        }        // 截图的一些信息存储在 SaveImageInBackgroundTask 中构建        mSaveInBgTask = new SaveImageInBackgroundTask(mContext, data);        mSaveInBgTask.execute();    }到此截屏流程完毕,可以检察下截图的View的xml文件:global_screenshot.xml
<FrameLayout    xmlns:android="http://schemas.android.com/apk/res/android"    android:id="@+id/global_screenshot_frame"    android:layout_width="match_parent"    android:layout_height="match_parent">    <ImageView        android:id="@+id/global_screenshot_actions_background"        android:layout_height="@dimen/screenshot_bg_protection_height"        android:layout_width="match_parent"        android:layout_gravity="bottom"        android:alpha="0.0"        android:src="@drawable/screenshot_actions_background_protection"/>    <!--截屏动画相关的View -->    <ImageView        android:id="@+id/global_screenshot_animated_view"        android:layout_width="wrap_content"        android:layout_height="wrap_content"        android:layout_gravity="top|start"        android:visibility="gone"        android:elevation="@dimen/screenshot_preview_elevation"        android:background="@drawable/screenshot_rounded_corners" />    <ImageView        android:id="@+id/global_screenshot_flash"        android:layout_width="match_parent"        android:layout_height="match_parent"        android:visibility="gone"        android:elevation="@dimen/screenshot_preview_elevation"        android:src="@android:color/white"/>    <com.android.systemui.screenshot.ScreenshotSelectorView        android:id="@+id/global_screenshot_selector"        android:layout_width="match_parent"        android:layout_height="match_parent"        android:visibility="gone"        android:pointerIcon="crosshair"/>    <!-- 此处包罗了一个layout, 而缩略图的View就在此layout中,         截屏右上角的关闭缩略图按钮 也在此layout中 -->    <include layout="@layout/global_screenshot_static"/></FrameLayout>
您需要登录后才可以回帖 登录 | 立即注册

Powered by CangBaoKu v1.0 小黑屋藏宝库It社区( 冀ICP备14008649号 )

GMT+8, 2024-11-24 05:16, Processed in 0.188098 second(s), 32 queries.© 2003-2025 cbk Team.

快速回复 返回顶部 返回列表