一,基本情况
不说废话,先直接上图,大致情况就是,在一则New Message中添加一些附件(图片,视频,声音等)和信息一起发送,如下图;
二,基本思路
其实,这就类似于我们的拍照上传,是采用一样的处理方法,这里选择临时拍一张照片作为一起发送的附件,即上图中的Capture Picture。主要步骤:
1)点击Capture Picture时,会启动系统Camera应用程序来拍照;主要使用startActivityForResult(Intent intent, int requestCode)方法来启动Camera程序;
2)拍摄照片;这里就是用Camera进行拍照,这里不做介绍;
3)保存照片,并将照片数据返回给Message应用;主要用到一个setResult(int resultCode, Intent intent)方法,返回到原调用程序,关闭Camera;
4)在Message应用中处理返回的数据;重写onActivityResult(int requestCode, int resultCode, Intent data)方法来处理返回的数据;
三,具体流程
首先,我们已经启动Message-->New Message-->Attachment,在Message主活动ComposeMessageActivity中有一个addAttachment()方法来实现为新信息添加附件;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
|
private
void
addAttachment(
int
type,
boolean
replace) {
switch
(type) {
case
AttachmentTypeSelectorAdapter.ADD_IMAGE:
/* TODO */
break
;
case
AttachmentTypeSelectorAdapter.TAKE_PICTURE: {
Intent intent =
new
Intent(MediaStore.ACTION_IMAGE_CAPTURE);
intent.putExtra(MediaStore.EXTRA_OUTPUT, TempFileProvider.SCRAP_CONTENT_URI);
startActivityForResult(intent, REQUEST_CODE_TAKE_PICTURE);
break
;
}
case
AttachmentTypeSelectorAdapter.ADD_VIDEO:<span></span><span></span>
break
;
case
AttachmentTypeSelectorAdapter.RECORD_VIDEO: {
break
;
case
AttachmentTypeSelectorAdapter.ADD_SOUND:
break
;
case
AttachmentTypeSelectorAdapter.RECORD_SOUND:
break
;
case
AttachmentTypeSelectorAdapter.ADD_SLIDESHOW:
break
;
default
:
break
;
}
|
新建一个 MediaStore.ACTION_IMAGE_CAPTURE 意图的Intent,ACTION_IMAGE_CAPTURE 的定义为"android.media.action.IMAGE_CAPTURE",这就时告诉系统,要进行抓取图片,调用startActivityForResult()启动Camera。
在Camera中会进行正常的onCreate()-->Open Camera-->Preview,在此处会有一个不同之处,
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
public
void
onCreate(Bundle icicle) {
super
.onCreate(icicle);
……
mIsImageCaptureIntent = isImageCaptureIntent();
setContentView(R.layout.camera);
if
(mIsImageCaptureIntent) {
mReviewDoneButton = (Rotatable) findViewById(R.id.btn_done);
mReviewCancelButton = (Rotatable) findViewById(R.id.btn_cancel);
findViewById(R.id.btn_cancel).setVisibility(View.VISIBLE);
}
else
{
mThumbnailView = (RotateImageView) findViewById(R.id.thumbnail);
mThumbnailView.enableFilter(
false
);
mThumbnailView.setVisibility(View.VISIBLE);
}
……
}
|
1
2
3
4
|
private
boolean
isImageCaptureIntent() {
String action = getIntent().getAction();
return
(MediaStore.ACTION_IMAGE_CAPTURE.equals(action));
}
|
从上面代码可以看出,Camera会判断当前的action,若为ACTION_IMAGE_CAPTURE,isImageCaptureIntent()会返回true,这时显示的界面也会有所不同。
接下来就是Shutter键进行拍照,都知道正常拍照后会回到PictureCallback中的onPictureTaken()方法,再次Preview,保存图片等一些事务处理,而在这里也会回到onPictureTaken(),但有一些不同,不再Preview,而是停留在拍照的画面;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
|
public
void
onPictureTaken
……
if
(!mIsImageCaptureIntent) {
startPreview();
startFaceDetection();
}
if
(!mIsImageCaptureIntent) {
Size s = mParameters.getPictureSize();
mImageSaver.addImage(jpegData, mLocation, s.width, s.height);
}
else
{
mJpegImageData = jpegData;
if
(!mQuickCapture) {
showPostCaptureAlert();
}
else
{
doAttach();
}
}
……
}
|
1
|
mQuickCapture = getIntent().getBooleanExtra(EXTRA_QUICK_CAPTURE,
false
);
|
从前面我们已经知道mIsImageCaptureIntent()返回true,取反则代码会直接运行else语句,这时我们来到showPostCaptureAlert()方法,onCreate方法中mQuickCapture默认会是false。
1
2
3
4
5
6
7
8
9
10
11
|
private
void
showPostCaptureAlert() {
if
(mIsImageCaptureIntent) {
Util.fadeOut(mIndicatorControlContainer);
Util.fadeOut(mShutterButton);
int
[] pickIds = {R.id.btn_retake, R.id.btn_done};
for
(
int
id : pickIds) {
Util.fadeIn(findViewById(id));
}
}
}
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
@OnClickAttr
public
void
onReviewRetakeClicked(View v) {
hidePostCaptureAlert();
startPreview();
startFaceDetection();
}
@OnClickAttr
public
void
onReviewDoneClicked(View v) {
doAttach();
}
@OnClickAttr
public
void
onReviewCancelClicked(View v) {
doCancel();
}
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
|
private
void
doAttach() {
if
(mPausing) {
return
;
} <span></span>
byte
[] data = mJpegImageData;
if
(mCropValue ==
null
) {
// First handle the no crop case -- just return the value. If the
// caller specifies a "save uri" then write the data to it's
// stream. Otherwise, pass back a scaled down version of the bitmap
// directly in the extras.
if
(mSaveUri !=
null
) {
OutputStream outputStream =
null
;
try
{
outputStream = mContentResolver.openOutputStream(mSaveUri);
outputStream.write(data);
outputStream.close();
setResultEx(RESULT_OK);
finish();
}
catch
(IOException ex) {
// ignore exception
}
finally
{
Util.closeSilently(outputStream);
}
}
else
{
int
orientation = Exif.getOrientation(data);
Bitmap bitmap = Util.makeBitmap(data,
50
*
1024
);
bitmap = Util.rotate(bitmap, orientation);
setResultEx(RESULT_OK,
new
Intent(
"inline-data"
).putExtra(
"data"
, bitmap));
finish();
}
}
else
{
// Save the image to a temp file and invoke the cropper
Uri tempUri =
null
;
FileOutputStream tempStream =
null
;
try
{
File path = getFileStreamPath(sTempCropFilename);
path.delete();
tempStream = openFileOutput(sTempCropFilename,
0
);
tempStream.write(data);
tempStream.close();
tempUri = Uri.fromFile(path);
}
catch
(FileNotFoundException ex) {
setResultEx(Activity.RESULT_CANCELED);
finish();
return
;
}
catch
(IOException ex) {
setResultEx(Activity.RESULT_CANCELED);
finish();
return
;
}
finally
{
Util.closeSilently(tempStream);
}
Bundle newExtras =
new
Bundle();
if
(mCropValue.equals(
"circle"
)) {
newExtras.putString(
"circleCrop"
,
"true"
);
}
if
(mSaveUri !=
null
) {
newExtras.putParcelable(MediaStore.EXTRA_OUTPUT, mSaveUri);
}
else
{
newExtras.putBoolean(
"return-data"
,
true
);
}
Intent cropIntent =
new
Intent(
"com.android.camera.action.CROP"
);
cropIntent.setData(tempUri);
cropIntent.putExtras(newExtras);
startActivityForResult(cropIntent, CROP_MSG);
}
}
|
1
2
3
4
5
6
7
|
private
void
setupCaptureParams() {
Bundle myExtras = getIntent().getExtras();
if
(myExtras !=
null
) {
mSaveUri = (Uri) myExtras.getParcelable(MediaStore.EXTRA_OUTPUT);
mCropValue = myExtras.getString(
"crop"
);
}
}
|
mCropValue为null,至于mSaveUri在onCreate()方法中会调用一个setupCaptureParams()方法,取得Uri;这时会执行setResultEx(RESULT_OK),并调用 finish()关闭Camera。
1
2
3
4
|
protected
void
setResultEx(
int
resultCode) {
mResultCodeForTesting = resultCode;
setResult(resultCode);
}
|
setResult()方法终于出现了,至此,Camera的功能已经完成。
然后,我们继续回到ComposeMessageActivity,来到onActivityResult();
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
|
@Override
protected
void
onActivityResult(
int
requestCode,
int
resultCode, Intent data) {
……
switch
(requestCode) {
case
REQUEST_CODE_CREATE_SLIDESHOW:
if
(data !=
null
) {
WorkingMessage newMessage = WorkingMessage.load(
this
, data.getData());
if
(newMessage !=
null
) {
mWorkingMessage = newMessage;
mWorkingMessage.setConversation(mConversation);
drawTopPanel(
false
);
updateSendButtonState();
invalidateOptionsMenu();
}
}
break
;
case
REQUEST_CODE_TAKE_PICTURE: {
// create a file based uri and pass to addImage(). We want to read the JPEG
// data directly from file (using UriImage) instead of decoding it into a Bitmap,
// which takes up too much memory and could easily lead to OOM.
File file =
new
File(TempFileProvider.getScrapPath(
this
));
Uri uri = Uri.fromFile(file);
addImageAsync(uri,
false
);
break
;
}
……
}
}
|
1) TempFileProvider.getScrapPath(this)获取媒体文件的路径,我打印了绝对路径,一看结果我想大家都懂了,这是一个系统隐藏文件.temp.jpg:
/mnt/sdcard/Android/data/com.android.mms/cache/.temp.jpg;
2)调用addImageAsync(uri, false)方法异步加载图片到Message。