Android源码系列(21) -- GlobalScreenshot

栏目: IOS · Android · 发布时间: 5年前

内容简介:这篇文章介绍系统如何实现屏幕截取操作,并为拦截截屏事件提供思路。下篇文章TakeScreenshotService是此类负责获取截图,下面出现的定义类都是

这篇文章介绍系统如何实现屏幕截取操作,并为拦截截屏事件提供思路。下篇文章 Android源码系列(22) – TakeScreenshotService 将介绍截图如何写入系统磁盘。源码版本 Android 28

一、TakeScreenshotService

TakeScreenshotService是 Service 的子类,通过IPC的方式接受截屏请求,并通过 GlobalScreenshot 实现屏幕截取和图片保存逻辑。

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;

            // 构建finisher响应Messenger
            Runnable finisher = new Runnable() {
                @Override
                public void run() {
                    Message reply = Message.obtain(null, 1);
                    try {
                        callback.send(reply);
                    } catch (RemoteException e) {
                    }
                }
            };

            // 如果此用户的存储被锁定无法保存屏幕截图,跳过执行而不是显示误导性的动画和错误通知
            if (!getSystemService(UserManager.class).isUserUnlocked()) {
                Log.w(TAG, "Skipping screenshot because storage is locked!");
                // 截图没有保存,发送finisher
                post(finisher);
                return;
            }

            // 初始化GlobalScreenshot
            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;
                    
                // 不支持类型,输出日志后不执行操作
                default:
                    Log.d(TAG, "Invalid screenshot option: " + msg.what);
            }
        }
    };

    @Override
    public IBinder onBind(Intent intent) {
        // 返回IBinder支持IPC
        return new Messenger(mHandler).getBinder();
    }

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

二、GlobalScreenshot

此类负责获取截图,下面出现的定义类都是 GlobalScreenshot 的内部类。

class GlobalScreenshot

2.1 常量

static final String SCREENSHOT_URI_ID = "android:screenshot_uri_id";
static final String SHARING_INTENT = "android:screenshot_sharing_intent";

// 动画展示时间的配置
private static final int SCREENSHOT_FLASH_TO_PEAK_DURATION = 130;
private static final int SCREENSHOT_DROP_IN_DURATION = 430;
private static final int SCREENSHOT_DROP_OUT_DELAY = 500;
private static final int SCREENSHOT_DROP_OUT_DURATION = 430;
private static final int SCREENSHOT_DROP_OUT_SCALE_DURATION = 370;
private static final int SCREENSHOT_FAST_DROP_OUT_DURATION = 320;
private static final float BACKGROUND_ALPHA = 0.5f;
private static final float SCREENSHOT_SCALE = 1f;
private static final float SCREENSHOT_DROP_IN_MIN_SCALE = SCREENSHOT_SCALE * 0.725f;
private static final float SCREENSHOT_DROP_OUT_MIN_SCALE = SCREENSHOT_SCALE * 0.45f;
private static final float SCREENSHOT_FAST_DROP_OUT_MIN_SCALE = SCREENSHOT_SCALE * 0.6f;
private static final float SCREENSHOT_DROP_OUT_MIN_SCALE_OFFSET = 0f;

2.2 数据成员

// 预览图宽高
private final int mPreviewWidth;
private final int mPreviewHeight;

private Context mContext;
private WindowManager mWindowManager;
private WindowManager.LayoutParams mWindowLayoutParams;
private NotificationManager mNotificationManager;

// 用于测量屏幕宽高
private Display mDisplay;
private DisplayMetrics mDisplayMetrics;
private Matrix mDisplayMatrix;

// 截图的Bitmap
private Bitmap mScreenBitmap;
private View mScreenshotLayout;

// 截图选择器
private ScreenshotSelectorView mScreenshotSelectorView;
private ImageView mBackgroundView;
private ImageView mScreenshotView;
private ImageView mScreenshotFlash;

// 截屏的屏幕动画
private AnimatorSet mScreenshotAnimation;

private int mNotificationIconSize;
private float mBgPadding;
private float mBgPaddingScale;

// 异步保存截图的AsyncTask
private AsyncTask<Void, Void, Void> mSaveInBgTask;

// 截屏时发出模拟快门的声音
private MediaActionSound mCameraSound;

2.3 构造方法

public GlobalScreenshot(Context context) {
    Resources r = context.getResources();
    mContext = context;
    LayoutInflater layoutInflater = (LayoutInflater)
            context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);

    mDisplayMatrix = new Matrix();
    
    // 填充截屏布局
    mScreenshotLayout = layoutInflater.inflate(R.layout.global_screenshot, null);
    // 绑定View
    mBackgroundView = (ImageView) mScreenshotLayout.findViewById(R.id.global_screenshot_background);
    mScreenshotView = (ImageView) mScreenshotLayout.findViewById(R.id.global_screenshot);
    mScreenshotFlash = (ImageView) mScreenshotLayout.findViewById(R.id.global_screenshot_flash);
    mScreenshotSelectorView = (ScreenshotSelectorView) mScreenshotLayout.findViewById(
            R.id.global_screenshot_selector);
    mScreenshotLayout.setFocusable(true); // 令此布局获取焦点
    mScreenshotSelectorView.setFocusable(true);
    mScreenshotSelectorView.setFocusableInTouchMode(true);
    mScreenshotLayout.setOnTouchListener(new View.OnTouchListener() {
        @Override
        public boolean onTouch(View v, MotionEvent event) {
            // 拦截并抛弃所有触摸事件
            return true;
        }
    });

    // 设置将要使用的window
    mWindowLayoutParams = new WindowManager.LayoutParams(
            ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT, 0, 0,
            WindowManager.LayoutParams.TYPE_SCREENSHOT,
            WindowManager.LayoutParams.FLAG_FULLSCREEN
                | WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN
                | WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED,
            PixelFormat.TRANSLUCENT);
    mWindowLayoutParams.setTitle("ScreenshotAnimation");
    mWindowLayoutParams.layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
    mWindowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
    mNotificationManager =
        (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
    // 从WindowManager获取Display
    mDisplay = mWindowManager.getDefaultDisplay();
    mDisplayMetrics = new DisplayMetrics();
    // 测量Display参数
    mDisplay.getRealMetrics(mDisplayMetrics);

    // 获取通知图标的尺寸
    mNotificationIconSize =
        r.getDimensionPixelSize(android.R.dimen.notification_large_icon_height);

    // 背景的边距
    mBgPadding = (float) r.getDimensionPixelSize(R.dimen.global_screenshot_bg_padding);
    mBgPaddingScale = mBgPadding /  mDisplayMetrics.widthPixels;

    // 确定最优化的预览尺寸
    int panelWidth = 0;
    try {
        panelWidth = r.getDimensionPixelSize(R.dimen.notification_panel_width);
    } catch (Resources.NotFoundException e) {
    }

    // panelWidth在上述异常出现时为0
    if (panelWidth <= 0) {
        // includes notification_panel_width==match_parent (-1)
        panelWidth = mDisplayMetrics.widthPixels;
    }
    mPreviewWidth = panelWidth;
    mPreviewHeight = r.getDimensionPixelSize(R.dimen.notification_max_height);

    // 加载快门声音
    mCameraSound = new MediaActionSound();
    mCameraSound.load(MediaActionSound.SHUTTER_CLICK);
}

2.4 saveScreenshotInWorkerThread

调用方法时截图已经保存在内存中。此方法会创建新工作任务,并在 AsyncTask 子线程把截图保存到媒体存储。

private void saveScreenshotInWorkerThread(Runnable finisher) {
    // 创建空任务,把参数填到对象中
    SaveImageInBackgroundData data = new SaveImageInBackgroundData();
    data.context = mContext;
    data.image = mScreenBitmap; // 截图
    data.iconSize = mNotificationIconSize;
    data.finisher = finisher;
    data.previewWidth = mPreviewWidth;
    data.previewheight = mPreviewHeight;
    if (mSaveInBgTask != null) {
        mSaveInBgTask.cancel(false);
    }
    // 由此.execute()可知任务在AsyncTask是串行执行的
    mSaveInBgTask = new SaveImageInBackgroundTask(mContext, data, mNotificationManager)
            .execute();
}

系统很多任务通过 AsyncTask 而不是子线程的方式执行后台任务,类似上面的截图写入到存储的场景。所以一定不能把长耗时任务放入 AsyncTask 导致任务阻塞,或依赖 AsyncTask 完成实时性要求高的工作。

2.5 getDegreesForRotation

获取屏幕旋转角度

private float getDegreesForRotation(int value) {
    switch (value) {
    case Surface.ROTATION_90:
        return 360f - 90f;
    case Surface.ROTATION_180:
        return 360f - 180f;
    case Surface.ROTATION_270:
        return 360f - 270f;
    }
    return 0f;
}

2.6 takeScreenshot

GlobalScreenshot初始化完成后,即可截取当前屏幕并展示动画。方法使用 private 修饰,由同类的其他方法调用。

private void takeScreenshot(Runnable finisher, boolean statusBarVisible, boolean navBarVisible,
        Rect crop) {
    // 获取屏幕的旋转角度、宽、高
    int rot = mDisplay.getRotation();
    int width = crop.width();
    int height = crop.height();

    // 从SurfaceControl获得截取的Bitmap
    mScreenBitmap = SurfaceControl.screenshot(crop, width, height, rot);

    // 检查获取的屏幕截图是否为空
    if (mScreenBitmap == null) {
        notifyScreenshotError(mContext, mNotificationManager,
                R.string.screenshot_failed_to_capture_text);
        finisher.run();
        return;
    }

    // 截图设置为非透明图片,能提升绘制速度
    mScreenBitmap.setHasAlpha(false);
    mScreenBitmap.prepareToDraw();

    // 开始截屏后的动画
    startAnimation(finisher, mDisplayMetrics.widthPixels, mDisplayMetrics.heightPixels,
            statusBarVisible, navBarVisible);
}

以下方法截取全屏图片,调用了上面的方法。根据屏幕宽高创建一个 Rect

void takeScreenshot(Runnable finisher, boolean statusBarVisible, boolean navBarVisible) {
    // 计算全屏的DisplayMetrics
    mDisplay.getRealMetrics(mDisplayMetrics);
    // 并把全屏宽高的参数传到方法内
    takeScreenshot(finisher, statusBarVisible, navBarVisible,
            new Rect(0, 0, mDisplayMetrics.widthPixels, mDisplayMetrics.heightPixels));
}

2.7 takeScreenshotPartial

takeScreenshot()截取全屏,此方法能截取屏幕的部分区域。

void takeScreenshotPartial(final Runnable finisher, final boolean statusBarVisible,
        final boolean navBarVisible) {
    // 向WindowManager添加选择器布局
    mWindowManager.addView(mScreenshotLayout, mWindowLayoutParams);

    // 处理选择器的点击事件
    mScreenshotSelectorView.setOnTouchListener(new View.OnTouchListener() {
        @Override
        public boolean onTouch(View v, MotionEvent event) {
            ScreenshotSelectorView view = (ScreenshotSelectorView) v;
            switch (event.getAction()) {
                case MotionEvent.ACTION_DOWN: // 获取ACTION_DOWN操作,开始截屏
                    view.startSelection((int) event.getX(), (int) event.getY());
                    return true;

                case MotionEvent.ACTION_MOVE: // 选择截屏范围
                    view.updateSelection((int) event.getX(), (int) event.getY());
                    return true;

                case MotionEvent.ACTION_UP: // 手指离开屏幕,结束选择
                    view.setVisibility(View.GONE);
                    mWindowManager.removeView(mScreenshotLayout);
                    // 获取选择的矩形区域
                    final Rect rect = view.getSelectionRect();
                    if (rect != null) {
                        if (rect.width() != 0 && rect.height() != 0) {
                            // 在view消失之后需要mScreenshotLayout处理截图保存的任务
                            mScreenshotLayout.post(new Runnable() {
                                public void run() {
                                    // 把选中的矩形区域作为依据从全屏截取部分图像
                                    takeScreenshot(finisher, statusBarVisible, navBarVisible,
                                            rect);
                                }
                            });
                        }
                    }

                    // 结束选择操作
                    view.stopSelection();
                    return true;
            }

            return false;
        }
    });

    // 先mScreenshotLayout发出requestFocus()
    mScreenshotLayout.post(new Runnable() {
        @Override
        public void run() {
            mScreenshotSelectorView.setVisibility(View.VISIBLE);
            mScreenshotSelectorView.requestFocus();
        }
    });
}

2.8 stopScreenshot

停止截屏

void stopScreenshot() {
    // 如果选择器图层依然呈现在屏幕上,则将其移除并重置其状态
    if (mScreenshotSelectorView.getSelectionRect() != null) {
        mWindowManager.removeView(mScreenshotLayout);
        mScreenshotSelectorView.stopSelection();
    }
}

2.9 startAnimation

截图动画

private void startAnimation(final Runnable finisher, int w, int h, boolean statusBarVisible,
        boolean navBarVisible) {
    // 手机处于省电模式的话显示一个toast提示用于已截屏
    PowerManager powerManager = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
    if (powerManager.isPowerSaveMode()) {
        Toast.makeText(mContext, R.string.screenshot_saved_title, Toast.LENGTH_SHORT).show();
    }

    // 添加动画视图
    mScreenshotView.setImageBitmap(mScreenBitmap);
    mScreenshotLayout.requestFocus();

    // 使用刚拍摄的屏幕截图设置动画,如动画已启动则需要结束
    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) {
            // 动画播放结束时启动保存截图的任务
            saveScreenshotInWorkerThread(finisher);
            // 截屏的布局也可以从屏幕移除了
            mWindowManager.removeView(mScreenshotLayout);

            // 清除位图的引用,避免内存泄漏
            mScreenBitmap = null;
            mScreenshotView.setImageBitmap(null);
        }
    });
    mScreenshotLayout.post(new Runnable() {
        @Override
        public void run() {
            // 播放快门声通知用户已截屏
            mCameraSound.play(MediaActionSound.SHUTTER_CLICK);
            // 通过硬件加速播放动画
            mScreenshotView.setLayerType(View.LAYER_TYPE_HARDWARE, null);
            mScreenshotView.buildLayer();
            mScreenshotAnimation.start();
        }
    });
}

通过代码构造动画

private ValueAnimator createScreenshotDropInAnimation() {
    final float flashPeakDurationPct = ((float) (SCREENSHOT_FLASH_TO_PEAK_DURATION)
            / SCREENSHOT_DROP_IN_DURATION);
    final float flashDurationPct = 2f * flashPeakDurationPct;
    final Interpolator flashAlphaInterpolator = new Interpolator() {
        @Override
        public float getInterpolation(float x) {
            // Flash the flash view in and out quickly
            if (x <= flashDurationPct) {
                return (float) Math.sin(Math.PI * (x / flashDurationPct));
            }
            return 0;
        }
    };
    final Interpolator scaleInterpolator = new Interpolator() {
        @Override
        public float getInterpolation(float x) {
            // We start scaling when the flash is at it's peak
            if (x < flashPeakDurationPct) {
                return 0;
            }
            return (x - flashDurationPct) / (1f - flashDurationPct);
        }
    };
    ValueAnimator anim = ValueAnimator.ofFloat(0f, 1f);
    anim.setDuration(SCREENSHOT_DROP_IN_DURATION);
    anim.addListener(new AnimatorListenerAdapter() {
        @Override
        public void onAnimationStart(Animator animation) {
            mBackgroundView.setAlpha(0f);
            mBackgroundView.setVisibility(View.VISIBLE);
            mScreenshotView.setAlpha(0f);
            mScreenshotView.setTranslationX(0f);
            mScreenshotView.setTranslationY(0f);
            mScreenshotView.setScaleX(SCREENSHOT_SCALE + mBgPaddingScale);
            mScreenshotView.setScaleY(SCREENSHOT_SCALE + mBgPaddingScale);
            mScreenshotView.setVisibility(View.VISIBLE);
            mScreenshotFlash.setAlpha(0f);
            mScreenshotFlash.setVisibility(View.VISIBLE);
        }
        @Override
        public void onAnimationEnd(android.animation.Animator animation) {
            mScreenshotFlash.setVisibility(View.GONE);
        }
    });
    anim.addUpdateListener(new AnimatorUpdateListener() {
        @Override
        public void onAnimationUpdate(ValueAnimator animation) {
            float t = (Float) animation.getAnimatedValue();
            float scaleT = (SCREENSHOT_SCALE + mBgPaddingScale)
                - scaleInterpolator.getInterpolation(t)
                    * (SCREENSHOT_SCALE - SCREENSHOT_DROP_IN_MIN_SCALE);
            mBackgroundView.setAlpha(scaleInterpolator.getInterpolation(t) * BACKGROUND_ALPHA);
            mScreenshotView.setAlpha(t);
            mScreenshotView.setScaleX(scaleT);
            mScreenshotView.setScaleY(scaleT);
            mScreenshotFlash.setAlpha(flashAlphaInterpolator.getInterpolation(t));
        }
    });
    return anim;
}

通过代码构造动画

private ValueAnimator createScreenshotDropOutAnimation(int w, int h, boolean statusBarVisible,
        boolean navBarVisible) {
    ValueAnimator anim = ValueAnimator.ofFloat(0f, 1f);
    anim.setStartDelay(SCREENSHOT_DROP_OUT_DELAY);
    anim.addListener(new AnimatorListenerAdapter() {
        @Override
        public void onAnimationEnd(Animator animation) {
            mBackgroundView.setVisibility(View.GONE);
            mScreenshotView.setVisibility(View.GONE);
            mScreenshotView.setLayerType(View.LAYER_TYPE_NONE, null);
        }
    });

    if (!statusBarVisible || !navBarVisible) {
        // 没有状态栏或导航栏,截屏提示直接淡出屏幕
        anim.setDuration(SCREENSHOT_FAST_DROP_OUT_DURATION);
        anim.addUpdateListener(new AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                float t = (Float) animation.getAnimatedValue();
                float scaleT = (SCREENSHOT_DROP_IN_MIN_SCALE + mBgPaddingScale)
                        - t * (SCREENSHOT_DROP_IN_MIN_SCALE - SCREENSHOT_FAST_DROP_OUT_MIN_SCALE);
                mBackgroundView.setAlpha((1f - t) * BACKGROUND_ALPHA);
                mScreenshotView.setAlpha(1f - t);
                mScreenshotView.setScaleX(scaleT);
                mScreenshotView.setScaleY(scaleT);
            }
        });
    } else {
        // 屏幕上有状态栏,动画到状态栏的左上方
        final float scaleDurationPct = (float) SCREENSHOT_DROP_OUT_SCALE_DURATION
                / SCREENSHOT_DROP_OUT_DURATION;
        final Interpolator scaleInterpolator = new Interpolator() {
            @Override
            public float getInterpolation(float x) {
                if (x < scaleDurationPct) {
                    // Decelerate, and scale the input accordingly
                    return (float) (1f - Math.pow(1f - (x / scaleDurationPct), 2f));
                }
                return 1f;
            }
        };

        // Determine the bounds of how to scale
        float halfScreenWidth = (w - 2f * mBgPadding) / 2f;
        float halfScreenHeight = (h - 2f * mBgPadding) / 2f;
        final float offsetPct = SCREENSHOT_DROP_OUT_MIN_SCALE_OFFSET;
        final PointF finalPos = new PointF(
            -halfScreenWidth + (SCREENSHOT_DROP_OUT_MIN_SCALE + offsetPct) * halfScreenWidth,
            -halfScreenHeight + (SCREENSHOT_DROP_OUT_MIN_SCALE + offsetPct) * halfScreenHeight);

        // 截图通过动画移动到status bar
        anim.setDuration(SCREENSHOT_DROP_OUT_DURATION);
        anim.addUpdateListener(new AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                float t = (Float) animation.getAnimatedValue();
                float scaleT = (SCREENSHOT_DROP_IN_MIN_SCALE + mBgPaddingScale)
                    - scaleInterpolator.getInterpolation(t)
                        * (SCREENSHOT_DROP_IN_MIN_SCALE - SCREENSHOT_DROP_OUT_MIN_SCALE);
                mBackgroundView.setAlpha((1f - t) * BACKGROUND_ALPHA);
                mScreenshotView.setAlpha(1f - scaleInterpolator.getInterpolation(t));
                mScreenshotView.setScaleX(scaleT);
                mScreenshotView.setScaleY(scaleT);
                mScreenshotView.setTranslationX(t * finalPos.x);
                mScreenshotView.setTranslationY(t * finalPos.y);
            }
        });
    }
    return anim;
}

2.10 notifyScreenshotError

截屏出现错误通知用户

static void notifyScreenshotError(Context context, NotificationManager nManager, int msgResId) {
    Resources r = context.getResources();
    String errorMsg = r.getString(msgResId);

    // 重新利用现有通知以通知用户错误信息
    Notification.Builder b = new Notification.Builder(context, NotificationChannels.ALERTS)
        .setTicker(r.getString(R.string.screenshot_failed_title))
        .setContentTitle(r.getString(R.string.screenshot_failed_title))
        .setContentText(errorMsg)
        .setSmallIcon(R.drawable.stat_notify_image_error)
        .setWhen(System.currentTimeMillis())
        .setVisibility(Notification.VISIBILITY_PUBLIC) // ok to show outside lockscreen
        .setCategory(Notification.CATEGORY_ERROR)
        .setAutoCancel(true)
        .setColor(context.getColor(
                    com.android.internal.R.color.system_notification_accent_color));

    final DevicePolicyManager dpm = (DevicePolicyManager) context.getSystemService(
            Context.DEVICE_POLICY_SERVICE);
    final Intent intent = dpm.createAdminSupportIntent(
            DevicePolicyManager.POLICY_DISABLE_SCREEN_CAPTURE);
    if (intent != null) {
        final PendingIntent pendingIntent = PendingIntent.getActivityAsUser(
                context, 0, intent, 0, null, UserHandle.CURRENT);
        b.setContentIntent(pendingIntent);
    }

    SystemUI.overrideNotificationAppName(context, b, true);

    Notification n = new Notification.BigTextStyle(b)
            .bigText(errorMsg)
            .build();
    nManager.notify(SystemMessage.NOTE_GLOBAL_SCREENSHOT, n);
}

2.11 ScreenshotActionReceiver

代理分享或编辑intent的 Receiver

public static class ScreenshotActionReceiver extends BroadcastReceiver {
    @Override
    public void onReceive(Context context, Intent intent) {
        try {
            ActivityManager.getService().closeSystemDialogs(SYSTEM_DIALOG_REASON_SCREENSHOT);
        } catch (RemoteException e) {
        }

        Intent actionIntent = intent.getParcelableExtra(SHARING_INTENT);

        // If this is an edit & default editor exists, route straight there.
        String editorPackage = context.getResources().getString(R.string.config_screenshotEditor);
        if (actionIntent.getAction() == Intent.ACTION_EDIT &&
                editorPackage != null && editorPackage.length() > 0) {
            actionIntent.setComponent(ComponentName.unflattenFromString(editorPackage));
            final NotificationManager nm =
                    (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
            nm.cancel(SystemMessage.NOTE_GLOBAL_SCREENSHOT);
        } else {
            PendingIntent chooseAction = PendingIntent.getBroadcast(context, 0,
                    new Intent(context, GlobalScreenshot.TargetChosenReceiver.class),
                    PendingIntent.FLAG_CANCEL_CURRENT | PendingIntent.FLAG_ONE_SHOT);
            actionIntent = Intent.createChooser(actionIntent, null,
                    chooseAction.getIntentSender())
                    .addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK | Intent.FLAG_ACTIVITY_NEW_TASK);
        }

        ActivityOptions opts = ActivityOptions.makeBasic();
        opts.setDisallowEnterPictureInPictureWhileLaunching(true);

        context.startActivityAsUser(actionIntent, opts.toBundle(), UserHandle.CURRENT);
    }
}

2.12 TargetChosenReceiver

选择分享或编辑目标后移除截图的通知

public static class TargetChosenReceiver extends BroadcastReceiver {
    @Override
    public void onReceive(Context context, Intent intent) {
        // 移除通知
        final NotificationManager nm =
                (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
        nm.cancel(SystemMessage.NOTE_GLOBAL_SCREENSHOT);
    }
}

2.13 DeleteScreenshotReceiver

从存储里移除截图

public static class DeleteScreenshotReceiver extends BroadcastReceiver {
    @Override
    public void onReceive(Context context, Intent intent) {
        if (!intent.hasExtra(SCREENSHOT_URI_ID)) {
            return;
        }

        // 移除通知,先获取NotificationManager
        final NotificationManager nm =
                (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
        // 获取SCREENSHOT_URI_ID,构建Uri
        final Uri uri = Uri.parse(intent.getStringExtra(SCREENSHOT_URI_ID));
        // 移除截屏通知
        nm.cancel(SystemMessage.NOTE_GLOBAL_SCREENSHOT);

        // 从媒体存储中删除图片,后台任务串行执行
        new DeleteImageInBackgroundTask(context).execute(uri);
    }
}

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

网易一千零一夜

网易一千零一夜

网易杭研项目管理部 / 电子工业出版社 / 2016-9-1 / 46

本书是网易杭州研究院项目管理部多年来丰富的项目管理实践总结与干货分享。字字句句凝结了网易项目经理的甘与苦、汗与泪。 全书围绕项目管理体系,从敏捷实践、项目立项、需求管理、沟通管理,到计划进度管理、风险管理,真实反映了网易面向互联网产品项目管理实战经验与心路历程。 不论你是项目管理新手,还是资深项目经理,都可以从本书中获得启发与借鉴。一起来看看 《网易一千零一夜》 这本书的介绍吧!

URL 编码/解码
URL 编码/解码

URL 编码/解码

Markdown 在线编辑器
Markdown 在线编辑器

Markdown 在线编辑器

HEX HSV 转换工具
HEX HSV 转换工具

HEX HSV 互换工具