Android8.1 MTKプラットフォームの画面キャプチャ機能解析

序文

ソースコードを持っている関連

フレームワーク\ベース\サービス\コア\のJava \ COMアンドロイド\サーバー\ポリシー\のPhoneWindowManager.java \

ベンダー\テック\独自\パッケージ\ \ SystemUI \ SRC \ COM \ androidの\ systemui \スクリーンショット\ TakeScreenshotService.javaアプリ
ベンダー\テック\独自\パッケージ\ \ SystemUIが\ SRC \ COM \アプリアンドロイド\ systemui \スクリーンショット\ GlobalScreenshot.java

主な処理はPhoneWindowManagerで、実際のスクリーンショット機能は、バインドTakeScreenshotServiceを経てスクリーンショット機能を実現するためにGlobalScreenshot、PhoneWindowManagerとsystemuiに実装されています

プロセス

一般的には、特別なカスタムAndroidシステムされていないスクリーンショットが同時にスクリーンショットへのキーと電源キー音量ダウンを押したままにしてあり、以降華為、OPPOと私たちはあなたはそれが三本指のスクリーンショットをスライド見つかります使用する他のベンダーのシステムでは、次のいくつかの我々は、この特集記事をカスタマイズし、スクリーンショットは、iPhoneのスクリーンショットのサムネイル表示に似たスタイルが表示され、拡大図をジャンプする]をクリックし、何も操作は3Sが左下にスライドさせ、自動的に消えた後、残っていません。

まあ、今のは、プロセス管理システムのスクリーンショットを見てみましょう

    system_process D/WindowManager: interceptKeyTi keyCode=25 down=true repeatCount=0 keyguardOn=false mHomePressed=false canceled=false metaState:0
    system_process D/WindowManager: interceptKeyTq keycode=25 interactive=true keyguardActive=false policyFlags=22000000 down =false canceled = false isWakeKey=false mVolumeDownKeyTriggered =true result = 1 useHapticFeedback = false isInjected = false
    system_process D/WindowManager: interceptKeyTi keyCode=25 down=false repeatCount=0 keyguardOn=false mHomePressed=false canceled=false metaState:0
    system_process D/WindowManager: interceptKeyTq keycode=26 interactive=true keyguardActive=false policyFlags=22000000 down =false canceled = false isWakeKey=false mVolumeDownKeyTriggered =false result = 1 useHapticFeedback = false isInjected = false

上記のログキー及び電源キー、電源キーのkeyCode = 26、interceptKeyBeforeQueueingにPhoneWindowManagerを(参照する)方法、キー操作方法に対応する、ボリュームキーのkeyCode = 25に対応するボリュームで押圧されます

    /** {@inheritDoc} */
    @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;
        }

        .....

        if (DEBUG_INPUT) {
            Log.d(TAG, "interceptKeyTq keycode=" + keyCode
                    + " interactive=" + interactive + " keyguardActive=" + keyguardActive
                    + " policyFlags=" + Integer.toHexString(policyFlags));
        }

      .....

        // Handle special keys.
        switch (keyCode) {
            .......

            case KeyEvent.KEYCODE_VOLUME_DOWN:
            case KeyEvent.KEYCODE_VOLUME_UP:
            case KeyEvent.KEYCODE_VOLUME_MUTE: {
                if (keyCode == KeyEvent.KEYCODE_VOLUME_DOWN) {
                    if (down) {
                        if (interactive && !mScreenshotChordVolumeDownKeyTriggered
                                && (event.getFlags() & KeyEvent.FLAG_FALLBACK) == 0) {
                            mScreenshotChordVolumeDownKeyTriggered = true;
                            mScreenshotChordVolumeDownKeyTime = event.getDownTime();
                            mScreenshotChordVolumeDownKeyConsumed = false;
                            cancelPendingPowerKeyAction();
                            interceptScreenshotChord();
                            interceptAccessibilityShortcutChord();
                        }
                    } else {
                        mScreenshotChordVolumeDownKeyTriggered = false;
                        cancelPendingScreenshotChordAction();
                        cancelPendingAccessibilityShortcutAction();
                    }
                } 
        ....
    }

コアメソッドinterceptScreenshotChord(見て、電源ボタンを押してメッセージMSG_POWER_LONG_PRESSを除去するKEYCODE_VOLUME_DOWN、現在のキー押下時間mScreenshotChordVolumeDownKeyTime、cancelPendingPowerKeyAction(記録容量)を参照)

// Time to volume and power must be pressed within this interval of each other.
private static final long SCREENSHOT_CHORD_DEBOUNCE_DELAY_MILLIS = 150;

private void interceptScreenshotChord() {
        if (mScreenshotChordEnabled
                && mScreenshotChordVolumeDownKeyTriggered && mScreenshotChordPowerKeyTriggered
                && !mA11yShortcutChordVolumeUpKeyTriggered) {
            final long now = SystemClock.uptimeMillis();
            if (now <= mScreenshotChordVolumeDownKeyTime + SCREENSHOT_CHORD_DEBOUNCE_DELAY_MILLIS
                    && now <= mScreenshotChordPowerKeyTime
                            + SCREENSHOT_CHORD_DEBOUNCE_DELAY_MILLIS) {
                mScreenshotChordVolumeDownKeyConsumed = true;
                cancelPendingPowerKeyAction();
                mScreenshotRunnable.setScreenshotType(TAKE_SCREENSHOT_FULLSCREEN);
                mHandler.postDelayed(mScreenshotRunnable, getScreenshotChordLongPressDelay());
            }
        }
    }

電源キーが押されているだけmScreenshotChordPowerKeyTriggered 2つのボタンは、実行タスクmScreenshotRunnableのスクリーンショットを遅らせるために、150以上を押したときに、時間が本当でした

private long getScreenshotChordLongPressDelay() {
        if (mKeyguardDelegate.isShowing()) {
            // Double the time it takes to take a screenshot from the keyguard
            return (long) (KEYGUARD_SCREENSHOT_CHORD_DELAY_MULTIPLIER *
                    ViewConfiguration.get(mContext).getDeviceGlobalActionKeyTimeout());
        }
        return ViewConfiguration.get(mContext).getDeviceGlobalActionKeyTimeout();
    }

現在の入力ボックスが開いている場合、入力フレームタイムアウト時間プラスキーシステム構成の閉鎖遅延時間、現在の入力ブロックが直接開いていない場合、キーシステム構成タイムアウト処理時間

その後mScreenshotRunnableが行っている何を見てアクション

private class ScreenshotRunnable implements Runnable {
        private int mScreenshotType = TAKE_SCREENSHOT_FULLSCREEN;

        public void setScreenshotType(int screenshotType) {
            mScreenshotType = screenshotType;
        }

        @Override
        public void run() {
            takeScreenshot(mScreenshotType);
        }
    }

private final ScreenshotRunnable mScreenshotRunnable = new ScreenshotRunnable();

TakeScreenshot)は(スレッドでコールを見ることができ、デフォルトのタイプは、フルスクリーンのスクリーンショット、スクリーンショットの種類TAKE_SCREENSHOT_SELECTED_REGION選択領域と全画面TAKE_SCREENSHOT_FULLSCREENの二種類設定されていません

// Assume this is called from the Handler thread.
    private void takeScreenshot(final int screenshotType) {
        synchronized (mScreenshotLock) {
            if (mScreenshotConnection != null) {
                return;
            }
            final ComponentName serviceComponent = new ComponentName(SYSUI_PACKAGE,
                    SYSUI_SCREENSHOT_SERVICE);
            final Intent serviceIntent = new Intent();
            serviceIntent.setComponent(serviceComponent);
            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, screenshotType);
                        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) {
                    synchronized (mScreenshotLock) {
                        if (mScreenshotConnection != null) {
                            mContext.unbindService(mScreenshotConnection);
                            mScreenshotConnection = null;
                            mHandler.removeCallbacks(mScreenshotTimeout);
                            notifyScreenshotError();
                        }
                    }
                }
            };
            if (mContext.bindServiceAsUser(serviceIntent, conn,
                    Context.BIND_AUTO_CREATE | Context.BIND_FOREGROUND_SERVICE_WHILE_AWAKE,
                    UserHandle.CURRENT)) {
                mScreenshotConnection = conn;
                mHandler.postDelayed(mScreenshotTimeout, 10000);
            }
        }
    }

takeScreenshotバインドSystemUIメッセンジャーについてやや似AIDLは、を参照してもよい、TakeScreenshotService接続、メッセンジャーのトラフィックを介した接続が成功した後、二つにメッセージを渡すプロセスによって確立されたAndroidのメッセンジャーのプロセス間通信 Messengerのメイン転写電流mStatusBarとmNavigationBarか目に見える、見えるTakeScreenshotServiceどのように受信処理を

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) {
            final Messenger callback = msg.replyTo;
            Runnable finisher = new Runnable() {
                @Override
                public void run() {
                    Message reply = Message.obtain(null, 1);
                    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 (!getSystemService(UserManager.class).isUserUnlocked()) {
                Log.w(TAG, "Skipping screenshot because storage is locked!");
                post(finisher);
                return;
            }

            if (mScreenshot == null) {
                mScreenshot = new GlobalScreenshot(TakeScreenshotService.this);
            }

            switch (msg.what) {
                case WindowManager.TAKE_SCREENSHOT_FULLSCREEN:
                    mScreenshot.takeScreenshot(finisher, msg.arg1 > 0, msg.arg2 > 0);
                    break;
                case WindowManager.TAKE_SCREENSHOT_SELECTED_REGION:
                    mScreenshot.takeScreenshotPartial(finisher, msg.arg1 > 0, msg.arg2 > 0);
                    break;
            }
        }
    };

    @Override
    public IBinder onBind(Intent intent) {
        return new Messenger(mHandler).getBinder();
    }

    @Override
    public boolean onUnbind(Intent intent) {
        if (mScreenshot != null) mScreenshot.stopScreenshot();
        return true;
    }
}

あなたはフォローアップ、(実際の作業が来る)GlobalScreenshotオブジェクトを作成することにより、takeScreenshotはスクリーンショットを実行呼び出し、スクリーンショットを入力し、ステータスバー、ナビゲーションバーを含めるかどうかを取得し、mHandlerが受信したメッセージの転送を見ることができます

    void takeScreenshot(Runnable finisher, boolean statusBarVisible, boolean navBarVisible) {
        mDisplay.getRealMetrics(mDisplayMetrics);
        takeScreenshot(finisher, statusBarVisible, navBarVisible, 0, 0, mDisplayMetrics.widthPixels,
                mDisplayMetrics.heightPixels);
    }

    /**
     * Takes a screenshot of the current display and shows an animation.
     */
    void takeScreenshot(Runnable finisher, boolean statusBarVisible, boolean navBarVisible,
            int x, int y, int width, int height) {
        // 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 = SurfaceControl.screenshot((int) dims[0], (int) dims[1]);
        if (mScreenBitmap == null) {
            notifyScreenshotError(mContext, mNotificationManager,
                    R.string.screenshot_failed_to_capture_text);
            finisher.run();
            return;
        }

        if (requiresRotation) {
            // Rotate the screenshot to the current orientation
            Bitmap ss = Bitmap.createBitmap(mDisplayMetrics.widthPixels,
                    mDisplayMetrics.heightPixels, Bitmap.Config.ARGB_8888,
                    mScreenBitmap.hasAlpha(), mScreenBitmap.getColorSpace());
            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);
            // Recycle the previous bitmap
            mScreenBitmap.recycle();
            mScreenBitmap = ss;
        }

        if (width != mDisplayMetrics.widthPixels || height != mDisplayMetrics.heightPixels) {
            // Crop the screenshot to selected region
            Bitmap cropped = Bitmap.createBitmap(mScreenBitmap, x, y, width, height);
            mScreenBitmap.recycle();
            mScreenBitmap = cropped;
        }

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

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

画像を回転させるかどうかを決定するために、画面の現在の画面の幅と高さと方向を取得し、その後、ウェル、次に参照するダウン続け、スクリーンショットをSurfaceControl.screenshot

public static Bitmap screenshot(int width, int height) {
    // TODO: should take the display as a parameter
    IBinder displayToken = SurfaceControl.getBuiltInDisplay(
            SurfaceControl.BUILT_IN_DISPLAY_ID_MAIN);
    return nativeScreenshot(displayToken, new Rect(), width, height, 0, 0, true,
            false, Surface.ROTATION_0);
}

ここでは、コールがネイティブメソッドであるnativeScreenshot方法は、ある、ここでのJNI層内の特定の実装には、あまりにも多くの導入を行うにはありません。スクリーンショットの成功かどうかを判断するために、画面キャプチャ方式のscreentshotを呼び出した後、戻って私たちのtakeScreenshot方法に進みます:
スクリーンショットは、notifyScreenshotError通知を呼び出すために失敗した送信されます。スクリーンショットが成功し、コールはアニメーションを再生startAnimation、アニメーション分析する必要があり、我々は後に、このアニメーション効果を変更します

    /**
     * Starts the animation after taking the screenshot
     */
    private void startAnimation(final Runnable finisher, int w, int h, boolean statusBarVisible,
            boolean navBarVisible) {
        // 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();
        }

        // Add the view for the animation
        mScreenshotView.setImageBitmap(mScreenBitmap);
        mScreenshotLayout.requestFocus();

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

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

                // Clear any references to the bitmap
                mScreenBitmap = null;
                mScreenshotView.setImageBitmap(null);
            }
        });
        mScreenshotLayout.post(new Runnable() {
            @Override
            public void run() {
                // Play the shutter sound to notify that we've taken a screenshot
                mCameraSound.play(MediaActionSound.SHUTTER_CLICK);

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

まず、トーストのキャプチャスクリーンショットを送信した場合、低バッテリーモードは、その後、mCameraSoundプレーヤーで、2つのアニメーションのポートフォリオを作成しながら、サムネイルビューのスクリーンショットで画面の中央でウィンドウマネージャを追加クリックスクリーンショットを、アニメーションを実行するかどうかを判断します、追加したばかりのアニメーションビューを除去した後、saveScreenshotInWorkerThreadは、メディアライブラリに画像を保存呼び出し中に、私たちに直接SaveImageInBackgroundTaskを見て

class SaveImageInBackgroundTask extends AsyncTask<Void, Void, Void> {
    .....

    SaveImageInBackgroundTask(Context context, SaveImageInBackgroundData data,
            NotificationManager nManager) {
       ......

        mNotificationBuilder = new Notification.Builder(context, NotificationChannels.SCREENSHOTS)
            .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(now)
            .setShowWhen(true)
            .setColor(r.getColor(com.android.internal.R.color.system_notification_accent_color))
            .setStyle(mNotificationStyle)
            .setPublicVersion(mPublicNotificationBuilder.build());
        mNotificationBuilder.setFlag(Notification.FLAG_NO_CLEAR, true);
        SystemUI.overrideNotificationAppName(context, mNotificationBuilder);

        mNotificationManager.notify(SystemMessage.NOTE_GLOBAL_SCREENSHOT,
                mNotificationBuilder.build());
    }

    @Override
    protected Void doInBackground(Void... params) {
        if (isCancelled()) {
            return null;
        }

        // By default, AsyncTask sets the worker thread to have background thread priority, so bump
        // it back up so that we save a little quicker.
        Process.setThreadPriority(Process.THREAD_PRIORITY_FOREGROUND);

        Context context = mParams.context;
        Bitmap image = mParams.image;
        Resources r = context.getResources();

        try {
            // Create screenshot directory if it doesn't exist
            mScreenshotDir.mkdirs();

            // media provider uses seconds for DATE_MODIFIED and DATE_ADDED, but milliseconds
            // for DATE_TAKEN
            long dateSeconds = mImageTime / 1000;

            // Save
            OutputStream out = new FileOutputStream(mImageFilePath);
            image.compress(Bitmap.CompressFormat.PNG, 100, out);
            out.flush();
            out.close();

            // Save the screenshot to the MediaStore
            ContentValues values = new ContentValues();
            ContentResolver resolver = context.getContentResolver();
            values.put(MediaStore.Images.ImageColumns.DATA, mImageFilePath);
            values.put(MediaStore.Images.ImageColumns.TITLE, mImageFileName);
            values.put(MediaStore.Images.ImageColumns.DISPLAY_NAME, mImageFileName);
            values.put(MediaStore.Images.ImageColumns.DATE_TAKEN, mImageTime);
            values.put(MediaStore.Images.ImageColumns.DATE_ADDED, dateSeconds);
            values.put(MediaStore.Images.ImageColumns.DATE_MODIFIED, dateSeconds);
            values.put(MediaStore.Images.ImageColumns.MIME_TYPE, "image/png");
            values.put(MediaStore.Images.ImageColumns.WIDTH, mImageWidth);
            values.put(MediaStore.Images.ImageColumns.HEIGHT, mImageHeight);
            values.put(MediaStore.Images.ImageColumns.SIZE, new File(mImageFilePath).length());
            Uri uri = resolver.insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, values);

            // Create a share intent
            String subjectDate = DateFormat.getDateTimeInstance().format(new Date(mImageTime));
            String subject = String.format(SCREENSHOT_SHARE_SUBJECT_TEMPLATE, subjectDate);
            Intent sharingIntent = new Intent(Intent.ACTION_SEND);
            sharingIntent.setType("image/png");
            sharingIntent.putExtra(Intent.EXTRA_STREAM, uri);
            sharingIntent.putExtra(Intent.EXTRA_SUBJECT, subject);

            // Create a share action for the notification. Note, we proxy the call to ShareReceiver
            // because RemoteViews currently forces an activity options on the PendingIntent being
            // launched, and since we don't want to trigger the share sheet in this case, we will
            // start the chooser activitiy directly in ShareReceiver.
            PendingIntent shareAction = PendingIntent.getBroadcast(context, 0,
                    new Intent(context, GlobalScreenshot.ShareReceiver.class)
                            .putExtra(SHARING_INTENT, sharingIntent),
                    PendingIntent.FLAG_CANCEL_CURRENT);
            Notification.Action.Builder shareActionBuilder = new Notification.Action.Builder(
                    R.drawable.ic_screenshot_share,
                    r.getString(com.android.internal.R.string.share), shareAction);
            mNotificationBuilder.addAction(shareActionBuilder.build());

            // Create a delete action for the notification
            PendingIntent deleteAction = PendingIntent.getBroadcast(context, 0,
                    new Intent(context, GlobalScreenshot.DeleteScreenshotReceiver.class)
                            .putExtra(GlobalScreenshot.SCREENSHOT_URI_ID, uri.toString()),
                    PendingIntent.FLAG_CANCEL_CURRENT | PendingIntent.FLAG_ONE_SHOT);
            Notification.Action.Builder deleteActionBuilder = new Notification.Action.Builder(
                    R.drawable.ic_screenshot_delete,
                    r.getString(com.android.internal.R.string.delete), deleteAction);
            mNotificationBuilder.addAction(deleteActionBuilder.build());

            mParams.imageUri = uri;
            mParams.image = null;
            mParams.errorMsgResId = 0;
        } catch (Exception e) {
            // IOException/UnsupportedOperationException may be thrown if external storage is not
            // mounted
            Slog.e(TAG, "unable to save screenshot", e);
            mParams.clearImage();
            mParams.errorMsgResId = R.string.screenshot_failed_to_save_text;
        }

        // Recycle the bitmap data
        if (image != null) {
            image.recycle();
        }

        return null;
    }

    @Override
    protected void onPostExecute(Void params) {
        if (mParams.errorMsgResId != 0) {
            // Show a message that we've failed to save the image to disk
            GlobalScreenshot.notifyScreenshotError(mParams.context, mNotificationManager,
                    mParams.errorMsgResId);
        } else {
            // Show the final notification to indicate screenshot saved
            Context context = mParams.context;
            Resources r = context.getResources();

            // Create the intent to show the screenshot in gallery
            Intent launchIntent = new Intent(Intent.ACTION_VIEW);
            launchIntent.setDataAndType(mParams.imageUri, "image/png");
            launchIntent.setFlags(
                    Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_GRANT_READ_URI_PERMISSION);

            final long now = System.currentTimeMillis();

            // Update the text and the icon for the existing notification
            mPublicNotificationBuilder
                    .setContentTitle(r.getString(R.string.screenshot_saved_title))
                    .setContentText(r.getString(R.string.screenshot_saved_text))
                    .setContentIntent(PendingIntent.getActivity(mParams.context, 0, launchIntent, 0))
                    .setWhen(now)
                    .setAutoCancel(true)
                    .setColor(context.getColor(
                            com.android.internal.R.color.system_notification_accent_color));
            mNotificationBuilder
                .setContentTitle(r.getString(R.string.screenshot_saved_title))
                .setContentText(r.getString(R.string.screenshot_saved_text))
                .setContentIntent(PendingIntent.getActivity(mParams.context, 0, launchIntent, 0))
                .setWhen(now)
                .setAutoCancel(true)
                .setColor(context.getColor(
                        com.android.internal.R.color.system_notification_accent_color))
                .setPublicVersion(mPublicNotificationBuilder.build())
                .setFlag(Notification.FLAG_NO_CLEAR, false);

            mNotificationManager.notify(SystemMessage.NOTE_GLOBAL_SCREENSHOT,
                    mNotificationBuilder.build());
        }
        mParams.finisher.run();
        mParams.clearContext();
    }

    @Override
    protected void onCancelled(Void params) {
        // If we are cancelled while the task is running in the background, we may get null params.
        // The finisher is expected to always be called back, so just use the baked-in params from
        // the ctor in any case.
        mParams.finisher.run();
        mParams.clearImage();
        mParams.clearContext();

        // Cancel the posted notification
        mNotificationManager.cancel(SystemMessage.NOTE_GLOBAL_SCREENSHOT);
    }
}

単純に、準備作業のSaveImageInBackgroundTaskコンストラクタの多くを置く形式に名前を付ける画像スクリーンの時間をカット、スクリーンショットの通知オブジェクトが作成され、スクリーンショットがメディアストアにContentResolverのストレージでdoInBackgroundます下では、共有のために、2 PendingIntentを作成、および削除スクリーンショットは、ちょうどonPostExecuteに表示されて作成され、このプロセスのスクリーンショットが終わっstatuBarに通知を送信します。

他の

我々は戻って行くPhoneWindowManager我々は単に2つのコード行を次によって上記の画面を切断することを知って、見に

mScreenshotRunnable.setScreenshotType(TAKE_SCREENSHOT_FULLSCREEN);
mHandler.post(mScreenshotRunnable);

上記のキーコードを検索することにより、私たちは、一緒に見て、他の二つはまた、コードのスクリーンショットを呼び出すことがあることを見つけます

@Override
public long interceptKeyBeforeDispatching(WindowState win, KeyEvent event, int policyFlags) {
    final boolean keyguardOn = keyguardOn();
    final int keyCode = event.getKeyCode();
    .....
    
    else if (keyCode == KeyEvent.KEYCODE_S && event.isMetaPressed()
                && event.isCtrlPressed()) {
            if (down && repeatCount == 0) {
                int type = event.isShiftPressed() ? TAKE_SCREENSHOT_SELECTED_REGION
                        : TAKE_SCREENSHOT_FULLSCREEN;
                mScreenshotRunnable.setScreenshotType(type);
                mHandler.post(mScreenshotRunnable);
                return -1;
            }
    }
    ....

    else if (keyCode == KeyEvent.KEYCODE_SYSRQ) {
        if (down && repeatCount == 0) {
            mScreenshotRunnable.setScreenshotType(TAKE_SCREENSHOT_FULLSCREEN);
            mHandler.post(mScreenshotRunnable);
        }
        return -1;
    }
    ......

}

また、キーメッセージの配布を傍受にKeyEventのソースを確認し、オンライン検索の下で、おそらく最初のケースに従来の方法では、それはあなたが周辺機器を接続した場合も、プレスS +のMetaキー+スクリーンショットにCtrlキー必要があり、メタ記述について参照Metaキー始まりと終わりは第二ケースは、スクリーンショットボタンが押された場合には、対応のkeyCode 120、120は、KeyEventのスクリーンショットを見つけることができるアナログADBシェル入力されてもよいです

 /** Key code constant: 'S' key. */
    public static final int KEYCODE_S               = 47;

/** Key code constant: System Request / Print Screen key. */
    public static final int KEYCODE_SYSRQ           = 120;

共通のキーに対応する値

ebFZ5R.png

3本の指のスクリーンショットに2行のコード、我々はジェスチャーリスナーが3本の指を取得する際に、単純に言及した資料の冒頭に、こののスクリーンショットを呼び出し、PhoneWindowManagerに追加することができます

概要

  • アプリは、電源ボタンとキーの組み合わせキー音量ダウンもちろん含めて、処理方法のキーイベントのPhoneWindowManager dispatchUnhandledKeyに処理されていません

  • 一連のコールを通じてTakeScreenshotServiceサービスを開始し、そのスクリーンショットを通じて操作を実行します。

  • 具体的なスクリーンショットネイティブコード層を実装しています。

  • スクリーンショットの失敗を通知直接通知を送信するために失敗した場合のスクリーンショットは、時間を操作します。

  • スクリーンショットを以下の後、スクリーンショットが成功した場合、アニメーションのスクリーンショットを実行すると、アニメーションが終了し、成功した通知の通知のスクリーンショットを送信します。

参考記事

Androidのスクリーンショット方法
Androidのキーコードリスト

おすすめ

転載: www.cnblogs.com/cczheng-666/p/11365869.html