内容简介:这篇文章介绍系统如何实现屏幕截取操作,并为拦截截屏事件提供思路。下篇文章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);
}
}
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网
猜你喜欢:- 【源码阅读】AndPermission源码阅读
- ReactNative源码解析-初识源码
- 【源码阅读】Gson源码阅读
- Spring源码系列:BeanDefinition源码解析
- istio 源码 – Citadel 源码分析 (原创)
- istio 源码 – pilot 源码分析(原创)
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
从零开始学创业大全集
阳飞扬 / 中国华侨出版社 / 2011-10-1 / 29.80元
为了让每一个怀揣梦想走上创业之路的有志者能在最短的时间内叩开创业的大门,了解创业的流程和方法,从而找到适合自己的创业之路,我们精心编写了这本《从零开始学创业大全集》。阳飞扬编著的《从零开始学创业大全集(超值白金版)》从创业准备、创业团队的组建、创业项目和商业模式的选择、创业计划书的制作、创业资金的筹集、企业的经营策略、资本运作以及产品营销方法、危机应对策略等方面,全面系统地阐述了创业的基本理论与实......一起来看看 《从零开始学创业大全集》 这本书的介绍吧!