系统截屏分析(AndroidO)
1 概述
848方案系统自带的截屏功能主要两种,一种是截取全屏,一种是区域截屏。两种触发方式都是在 PhoneWindowManager 监听按键使用 Handler 来分发消息进行处理。
截屏功能的具体实现主要在 SystemUI,通过接收消息调用截屏服务 TakeScreenshotService 来对两种方式进行,具体的功能全部逻辑在 GlobalScreenshot 这个类中,主要做了几件事:
- 加载截屏布局页面
- 初始化相关功能的handler、listener
- 截取全屏、区域截屏(动画、保存、删除)
2 时序图
3 流程分析
3.1 按键监听
监听按键 KeyEvent.KEYCODE_SYSRQ,handler post 一个截屏请求任务, TAKE_SCREENSHOT_SELECTED_REGION 为区域截屏, TAKE_SCREENSHOT_FULLSCREEN 为全屏截屏。
// frameworks\base\services\core\java\com\android\server\policy\PhoneWindowManager.java
@Override
public long interceptKeyBeforeDispatching(WindowState win, KeyEvent event, int policyFlags) {
final int keyCode = event.getKeyCode();
final int repeatCount = event.getRepeatCount();
final boolean down = event.getAction() == KeyEvent.ACTION_DOWN;
...
} else if (keyCode == KeyEvent.KEYCODE_SYSRQ) {
if (down && repeatCount == 0) {
mScreenshotRunnable.setScreenshotType(TAKE_SCREENSHOT_FULLSCREEN); // 截屏类型
mHandler.post(mScreenshotRunnable);
}
return -1;
}
}
截屏任务调用 takeScreenshot 方法
private final ScreenshotRunnable mScreenshotRunnable = new ScreenshotRunnable();
private class ScreenshotRunnable implements Runnable {
private int mScreenshotType = TAKE_SCREENSHOT_FULLSCREEN;
public void setScreenshotType(int screenshotType) {
mScreenshotType = screenshotType;
}
@Override
public void run() {
takeScreenshot(mScreenshotType); //
}
}
takeScreenshot 方法主要用于截屏服务的绑定及超时的错误通知。
private static final String SYSUI_PACKAGE = "com.android.systemui";
private static final String SYSUI_SCREENSHOT_SERVICE =
"com.android.systemui.screenshot.TakeScreenshotService";
private static final String SYSUI_SCREENSHOT_ERROR_RECEIVER =
"com.android.systemui.screenshot.ScreenshotServiceErrorReceiver";
private void takeScreenshot(final int screenshotType) {
synchronized (mScreenshotLock) {
if (mScreenshotConnection != null) {
return;
}
// com.android.systemui.screenshot.TakeScreenshotService
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) {
...
}
};
// 绑定截屏服务 TakeScreenshotService
if (mContext.bindServiceAsUser(serviceIntent, conn,
Context.BIND_AUTO_CREATE | Context.BIND_FOREGROUND_SERVICE_WHILE_AWAKE, UserHandle.CURRENT)) {
mScreenshotConnection = conn;
// 10s超时通知
mHandler.postDelayed(mScreenshotTimeout, 10000);
}
}
}
3.2 截屏服务
截屏服务内调用 GlobalScreenshot 类处理截屏逻辑
// frameworks\base\packages\SystemUI\src\com\android\systemui\screenshot\TakeScreenshotService.java
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是为了当截屏不可用/完成时,回调给PhoneWindowManager处理相关逻辑
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!");
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;
}
}
};
...
}
3.3 截取全屏/区域
3.3.1 takeScreenshot()
区域截屏的逻辑比全屏的多一点,其实都是差不多的。
// frameworks\base\packages\SystemUI\src\com\android\systemui\screenshot\GlobalScreenshot.java
// 全屏截图
void takeScreenshot(Runnable finisher, boolean statusBarVisible, boolean navBarVisible) {
mDisplay.getRealMetrics(mDisplayMetrics);
takeScreenshot(finisher, statusBarVisible, navBarVisible, 0, 0, mDisplayMetrics.widthPixels, mDisplayMetrics.heightPixels);
}
// 区域截图
void takeScreenshotPartial(final Runnable finisher, final boolean statusBarVisible,
final boolean navBarVisible) {
// addView添加区域截屏布局
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:
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) {
mScreenshotLayout.post(new Runnable() {
public void run() {
// 最终都是调用这个方法, 只不过这里的参数时区域选择后得到的参数rect
takeScreenshot(finisher, statusBarVisible, navBarVisible,
rect.left, rect.top, rect.width(), rect.height());
}
});
}
}
view.stopSelection();
return true;
}
return false;
}
});
mScreenshotLayout.post(new Runnable() {
@Override
public void run() {
//显示选择视图
mScreenshotSelectorView.setVisibility(View.VISIBLE);
mScreenshotSelectorView.requestFocus();
}
});
}
void takeScreenshot(Runnable finisher, boolean statusBarVisible, boolean navBarVisible,
int x, int y, int width, int height) {
mDisplay.getRealMetrics(mDisplayMetrics);
float[] dims = {mDisplayMetrics.widthPixels, mDisplayMetrics.heightPixels};
// 获取当前屏幕旋转度数
float degrees = getDegreesForRotation(mDisplay.getRotation());
boolean requiresRotation = (degrees > 0);
if (requiresRotation) { // 如果屏幕处于已旋转状态, 则获取当前设备的原始尺寸并重新设置
mDisplayMatrix.reset();
mDisplayMatrix.preRotate(-degrees);
mDisplayMatrix.mapPoints(dims);
dims[0] = Math.abs(dims[0]);
dims[1] = Math.abs(dims[1]);
}
// 截屏,这里直接调用了 SurfaceControl 类的静态方法 screenshot, 再往下就是 native 方法
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) { // 如果屏幕处于已旋转状态
Bitmap ss = Bitmap.createBitmap(mDisplayMetrics.widthPixels,
mDisplayMetrics.heightPixels, Bitmap.Config.ARGB_8888);
Canvas c = new Canvas(ss);
c.translate(ss.getWidth() / 2, ss.getHeight() / 2);//截图宽高分别折为屏幕高宽的一半
c.rotate(degrees); // 截图旋转指定度数
c.translate(-dims[0] / 2, -dims[1] / 2);
c.drawBitmap(mScreenBitmap, 0, 0, null);
c.setBitmap(null);
// 回收以前的位图
mScreenBitmap.recycle();
mScreenBitmap = ss;
}
if (width != mDisplayMetrics.widthPixels || height != mDisplayMetrics.heightPixels) {
//区域截屏判断, 截取指定位置宽高的Bitmap
Bitmap cropped = Bitmap.createBitmap(mScreenBitmap, x, y, width, height);
mScreenBitmap.recycle();
mScreenBitmap = cropped;
}
// Optimizations
mScreenBitmap.setHasAlpha(false);
mScreenBitmap.prepareToDraw();
// 开始截图动画
startAnimation(finisher, mDisplayMetrics.widthPixels, mDisplayMetrics.heightPixels,
statusBarVisible, navBarVisible);
}
3.3.2 SurfaceControl.screenshot()
如何获取当前屏幕的Bitmap
// frameworks\base\core\java\android\view\SurfaceControl.java
public static Bitmap screenshot(int width, int height) {
IBinder displayToken = SurfaceControl.getBuiltInDisplay(
SurfaceControl.BUILT_IN_DISPLAY_ID_MAIN);
return nativeScreenshot(displayToken, new Rect(), width, height, 0, 0, true,
false, Surface.ROTATION_0);
}
// 调用 native 方法
private static native Bitmap nativeScreenshot(IBinder displayToken,
Rect sourceCrop, int width, int height, int minLayer, int maxLayer,
boolean allLayers, boolean useIdentityTransform, int rotation);
3.3.3 startAnimation()
再来看一下截图后的动画,这里做了一些比较重要的事:
- 设置动画
- 播放截屏提示声音
- 播放动画
- 动画完成后保存截图
- 截图成功通知
private void startAnimation(final Runnable finisher, int w, int h, boolean statusBarVisible,
boolean navBarVisible) {
// 设置动画视图
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();
}
});
}
3.4 保存截图
使用 SaveImageInBackgroundTask保存截图
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);
}
mSaveInBgTask = new SaveImageInBackgroundTask(mContext, data, mNotificationManager).execute();
}
SaveImageInBackgroundTask 这个类是 GlobalScreenshot 的内部类,用于在后台保存屏幕截图
构造方法 SaveImageInBackgroundTask():
- 初始化保存截图需要的数据
- 构造截图相关的通知
SaveImageInBackgroundTask(Context context, SaveImageInBackgroundData data,
NotificationManager nManager) {
Resources r = context.getResources();
mParams = data;
mImageTime = System.currentTimeMillis();
String imageDate = new SimpleDateFormat("yyyyMMdd-HHmmss").format(new Date(mImageTime));
// 截图文件名
mImageFileName = String.format(SCREENSHOT_FILE_NAME_TEMPLATE, imageDate);
File dirFile = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES);
// 是否有外部磁盘可用
MStorageManager storageManager = MStorageManager.getInstance(context);
String[] volumes = storageManager.getVolumePaths();
if(volumes != null && volumes.length > 1 ){
dirFile = new File(volumes[1]+"/Pictures");
}
mScreenshotDir = new File(dirFile, SCREENSHOTS_DIR_NAME);
// 截图全路径
mImageFilePath = new File(mScreenshotDir, mImageFileName).getAbsolutePath();
mImageWidth = data.image.getWidth();
mImageHeight = data.image.getHeight();
int iconSize = data.iconSize;
int previewWidth = data.previewWidth;
int previewHeight = data.previewheight;
...
// 通知截图正在保存...
mNotificationManager.notify(SystemMessage.NOTE_GLOBAL_SCREENSHOT,mNotificationBuilder.build());
}
doInBackground():
- 创建屏幕截图目录,保存截图
- 将屏幕截图保存到 MediaStore
- 为通知创建共享、删除操作
protected Void doInBackground(Void... params) {
if (isCancelled()) {
return null;
}
// AsyncTask 将工作线程设置为具有后台线程优先级
Process.setThreadPriority(Process.THREAD_PRIORITY_FOREGROUND);
Context context = mParams.context;
Bitmap image = mParams.image;
Resources r = context.getResources();
try {
// 创建屏幕截图目录
mScreenshotDir.mkdirs();
long dateSeconds = mImageTime / 1000;
// 保存截图
OutputStream out = new FileOutputStream(mImageFilePath);
image.compress(Bitmap.CompressFormat.PNG, 100, out);
out.flush();
out.close();
// 将屏幕截图保存到 MediaStore.Images.Media.EXTERNAL_CONTENT_URI
...
Uri uri = resolver.insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, values);
// 为通知创建共享操作, TargetChosenReceiver 删除屏幕快照的通知
...
mNotificationBuilder.addAction(shareActionBuilder.build());
// 为通知创建删除操作,详见 DeleteScreenshotReceiver
...
mNotificationBuilder.addAction(deleteActionBuilder.build());
mParams.imageUri = uri;
mParams.image = null;
mParams.errorMsgResId = 0;
} catch (Exception e) {
//如果未安装外部存储,则可能引发IOExceptionUnsupportedOperationException
mParams.clearImage();
mParams.errorMsgResId = R.string.screenshot_failed_to_save_text;
}
// 回收位图数据
if (image != null) {
image.recycle();
}
return null;
}
onPostExecute():
通知屏幕截图已保存
protected void onPostExecute(Void params) {
if (mParams.errorMsgResId != 0) { // 在 doInBackground 保存截图失败时设置的标志
// 提示无法将图像保存到磁盘
GlobalScreenshot.notifyScreenshotError(mParams.context, mNotificationManager,mParams.errorMsgResId);
} else {
...
mNotificationManager.notify(SystemMessage.NOTE_GLOBAL_SCREENSHOT,
mNotificationBuilder.build());
}
mParams.finisher.run();
mParams.clearContext();
}
以上就是系统截图的全部流程,调用链比较简单。
4 客制化
截图后将当前界面作为浮窗缩放到屏幕右下角停留几秒,停留期间可响应触摸事件(自行定义:滑动、点击、长按),无操作后在指定时间浮窗进行动画消失。
- 截图动画
在 startAnimation() 方法中替换之前截图后动画
private void startAnimation(final Runnable finisher, int w, int h, boolean statusBarVisible,boolean navBarVisible) {
// 直接在这里修改获取动画的方法
ValueAnimator screenshotDropInAnim = createScreenshotDropInAnimation(w, h);
//ValueAnimator screenshotFadeOutAnim = createScreenshotDropOutAnimation(w, h, statusBarVisible, navBarVisible);
mScreenshotAnimation = new AnimatorSet();
mScreenshotAnimation.playSequentially(screenshotDropInAnim);
}
修改后的动画:
// frameworks\base\packages\SystemUI\src\com\android\systemui\screenshot\GlobalScreenshot.java
private ValueAnimator createScreenshotDropInAnimation(int w, int h) {
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) {
if (x <= flashDurationPct) {
return (float) Math.sin(Math.PI * (x / flashDurationPct));
}
return 0;
}
};
final Interpolator scaleInterpolator = new Interpolator() {
@Override
public float getInterpolation(float x) {
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) {
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);
mHandler.removeMessages(MSG_CLOSE_SCREENSHOT);
// 截图缩小、停留至右下角,无操作5s后截图向右淡出
mHandler.sendEmptyMessageDelayed(MSG_CLOSE_SCREENSHOT, TIME_CLOSE_SCREENSHOT);
}
});
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);
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);
mScreenshotView.setAlpha(t);
mScreenshotView.setScaleX(scaleT * 0.5f);
mScreenshotView.setScaleY(scaleT * 0.5f);
mScreenshotFlash.setAlpha(flashAlphaInterpolator.getInterpolation(t));
mScreenshotView.setTranslationX(t * finalPos.x);
mScreenshotView.setTranslationY(- t * finalPos.y);
}
});
return anim;
}
- 自定义触摸事件
在初始化监听器时为 mScreenshotView 添加相关的触摸监听器
mScreenshotView.setOnTouchListener(new View.OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {
boolean actionUp = event.getAction() == MotionEvent.ACTION_UP;
boolean touchEvent = mGestureDetector.onTouchEvent(event);
if (!touchEvent && actionUp) {
int[] locationOnScreen = mScreenshotView.getLocationOnScreen();
int left = locationOnScreen[0];
int dx = (int) (event.getRawX() - sx);
if (dx < 0 && left < 0) {
closeScreenshotView(true);
} else {
mHandler.sendEmptyMessageDelayed(MSG_CLOSE_SCREENSHOT, TIME_CLOSE_SCREENSHOT);
}
}
return true;
}
});
mGestureDetector = new GestureDetector(mContext, new GestureDetector.SimpleOnGestureListener() {
@Override
public boolean onDown(MotionEvent e) { // 按下
sx = (int) e.getRawX();
mHandler.removeMessages(MSG_CLOSE_SCREENSHOT);
return true;
}
@Override
public boolean onSingleTapUp(MotionEvent e) { // 单击
cropScreenshotImage();
return true;
}
@Override
public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) { // 拖动
int dx = (int) (e2.getX() - e1.getX());
int left = mScreenshotView.getLeft();
int top = mScreenshotView.getTop();
int right = mScreenshotView.getRight();
int bottom = mScreenshotView.getBottom();
Log.d("wugm", "onScroll " + "dx:" + dx);
if (dx < 0) {
mScreenshotView.layout(left + dx, top, right + dx, bottom);
// if (mLayoutImageButton.getVisibility() == View.VISIBLE) {
// mLayoutBtnVisible = false;
// mLayoutImageButton.setVisibility(View.INVISIBLE);
// }
}
return false;
}
@Override
public void onLongPress(MotionEvent e) { // 长按
mHandler.sendEmptyMessageDelayed(MSG_CLOSE_SCREENSHOT, TIME_CLOSE_SCREENSHOT);
// showImageButton(); todo
}
});
单击截图窗口打开图片浏览、裁剪界面(自行修改)
private void cropScreenshotImage() {
closeScreenshotView(true);
Intent intent = new Intent();
intent.putExtra(REGION_WIDTH, mRegionWidth);
intent.putExtra(REGION_HEIGHT, mRegionHeight);
intent.putExtra(IMAGE_FILE_PATH, mImageFilePath);
String packageName = "com.xxx.cropimage";
String className = "com.xxx.cropimage.CropViewActivity";
intent.setClassName(packageName, className);
Uri sourceUri = null;
try {
sourceUri = Uri.parse(MediaStore.Images.Media.insertImage(mContext.getContentResolver(), mScreenBitmap, null,null));
} catch (Exception e) {
Log.e(TAG, e.getMessage());
sourceUri = Uri.parse("file://" + mImageFilePath);
}
intent.setData(sourceUri);
mContext.startActivity(intent);
}