В этом уроке:
- получаем фото и видео, используя системное приложение
Открываем серию уроков по работе с камерой. И начнем с самого простого. Если вам в приложении необходимо сделать снимок или снять видео, вовсе необязательно для этого создавать отдельное Activity и работать в нем с объектом Camera. Можно использовать уже существующее в системе приложение.
Для этого ваше приложение должно отправить Intent с action = MediaStore.ACTION_IMAGE_CAPTURE (фото) или MediaStore.ACTION_VIDEO_CAPTURE (видео) и ждать ответ. Т.е. надо использовать методы startActivityForResult и onActivityResult.
Также, в этот Intent вы можете поместить желаемый путь к файлу и туда будет сохранен результат работы камеры. Для этого в Intent необходимо добавить Uri с ключом MediaStore.EXTRA_OUTPUT.
Напишем простое приложение, демонстрирующее эти возможности.
Создадим проект:
Project name: P1311_CameraIntent
Build Target: Android 2.3.3
Application name: CameraIntent
Package name: ru.startandroid.develop.p1311cameraintent
Create Activity: MainActivity
Добавим строки в strings.xml:
<string name="photo">Photo</string> <string name="video">Video</string>
Экран main.xml:
<?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent"> <Button android:id="@+id/btnPhoto" android:layout_width="wrap_content" android:layout_height="wrap_content" android:onClick="onClickPhoto" android:text="@string/photo"> </Button> <Button android:id="@+id/btnVideo" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_below="@+id/btnPhoto" android:onClick="onClickVideo" android:text="@string/video"> </Button> <ImageView android:id="@+id/ivPhoto" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_below="@+id/btnVideo"> </ImageView> </RelativeLayout>
На экране только кнопки для получения фото и видео, и ImageView, в котором будем отображать полученное фото.
MainActivity.java:
package ru.startandroid.develop.p1311cameraintent; import java.io.File; import android.app.Activity; import android.content.Intent; import android.graphics.Bitmap; import android.net.Uri; import android.os.Bundle; import android.os.Environment; import android.provider.MediaStore; import android.util.Log; import android.view.View; import android.widget.ImageView; public class MainActivity extends Activity { File directory; final int TYPE_PHOTO = 1; final int TYPE_VIDEO = 2; final int REQUEST_CODE_PHOTO = 1; final int REQUEST_CODE_VIDEO = 2; final String TAG = "myLogs"; ImageView ivPhoto; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); createDirectory(); ivPhoto = (ImageView) findViewById(R.id.ivPhoto); } public void onClickPhoto(View view) { Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE); intent.putExtra(MediaStore.EXTRA_OUTPUT, generateFileUri(TYPE_PHOTO)); startActivityForResult(intent, REQUEST_CODE_PHOTO); } public void onClickVideo(View view) { Intent intent = new Intent(MediaStore.ACTION_VIDEO_CAPTURE); intent.putExtra(MediaStore.EXTRA_OUTPUT, generateFileUri(TYPE_VIDEO)); startActivityForResult(intent, REQUEST_CODE_VIDEO); } @Override protected void onActivityResult(int requestCode, int resultCode, Intent intent) { if (requestCode == REQUEST_CODE_PHOTO) { if (resultCode == RESULT_OK) { if (intent == null) { Log.d(TAG, "Intent is null"); } else { Log.d(TAG, "Photo uri: " + intent.getData()); Bundle bndl = intent.getExtras(); if (bndl != null) { Object obj = intent.getExtras().get("data"); if (obj instanceof Bitmap) { Bitmap bitmap = (Bitmap) obj; Log.d(TAG, "bitmap " + bitmap.getWidth() + " x " + bitmap.getHeight()); ivPhoto.setImageBitmap(bitmap); } } } } else if (resultCode == RESULT_CANCELED) { Log.d(TAG, "Canceled"); } } if (requestCode == REQUEST_CODE_VIDEO) { if (resultCode == RESULT_OK) { if (intent == null) { Log.d(TAG, "Intent is null"); } else { Log.d(TAG, "Video uri: " + intent.getData()); } } else if (resultCode == RESULT_CANCELED) { Log.d(TAG, "Canceled"); } } } private Uri generateFileUri(int type) { File file = null; switch (type) { case TYPE_PHOTO: file = new File(directory.getPath() + "/" + "photo_" + System.currentTimeMillis() + ".jpg"); break; case TYPE_VIDEO: file = new File(directory.getPath() + "/" + "video_" + System.currentTimeMillis() + ".mp4"); break; } Log.d(TAG, "fileName = " + file); return Uri.fromFile(file); } private void createDirectory() { directory = new File( Environment .getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES), "MyFolder"); if (!directory.exists()) directory.mkdirs(); } }
В onCreate мы вызываем свой метод createDirectory, который в папке Pictures создаст папку для наших файлов и поместит соответствующий объект File в переменную directory.
В onClickPhoto и onClickVideo создаем Intent с соответствующим action, добавляем в этот Intent желаемый путь к файлу и отправляем методом startActivityForResult.
В onActivityResult мы ловим результат от приложения камеры. Код громоздкий, т.к. там куча проверок на null. Для фото и для видео я пытаюсь вытащить путь к получившемуся файлу, используя Intent.getData. Кроме этого, для фото я еще пытаюсь вытащить Bitmap с получившимся изображением.
Метод generateFileUri генерирует путь к файлу. Для этого он берет путь из directory, определяет префикс и расширение в зависимости от типа (фото или видео) и использует системное время, как основную часть имени файла. Далее все это конвертируется в Uri и возвращается как результат метода.
Осталось дописать в манифест пару настроек.
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/> - право на запись на SD карту
<uses-feature android:name="android.hardware.camera" /> - ваше приложение в маркете будет видно только устройствам с камерой
Сохраняем все и запускаем приложение.
Жмем Photo – открывается системное приложение камеры. Делаем фото и подтверждаем, что нас устраивает полученный снимок. Нас возвращают в наше приложение.
Смотрим логи:
fileName = /storage/emulated/0/Pictures/MyFolder/photo_1376465721626.jpg
Intent is null
В ответе мы не получили ничего, Intent = null. Но мы указывали путь, куда надо сохранить фото. Идем в папку Pictures/MyFolder и ищем свой файл, он должен там быть.
Попробуем получить видео. Жмем кнопку Video, снимаем ролик и подтверждаем, что он нас устраивает.
Смотрим логи:
fileName = /storage/emulated/0/Pictures/MyFolder/video_1376466182087.mp4
Video uri: file:///storage/emulated/0/Pictures/MyFolder/video_1376466182087.mp4
В случае с видео Intent не null. И его метод getData вернул нам инфу о пути к файлу. Идем в папку и ищем свой файл.
Теперь давайте не будем явно указывать путь для сохранения, и посмотрим, как поведет себя приложение камеры.
Закомментируем соответствующие строки кода:
public void onClickPhoto(View view) { Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE); //intent.putExtra(MediaStore.EXTRA_OUTPUT, generateFileUri(TYPE_PHOTO)); startActivityForResult(intent, REQUEST_CODE_PHOTO); } public void onClickVideo(View view) { Intent intent = new Intent(MediaStore.ACTION_VIDEO_CAPTURE); //intent.putExtra(MediaStore.EXTRA_OUTPUT, generateFileUri(TYPE_VIDEO)); startActivityForResult(intent, REQUEST_CODE_VIDEO); }
Сохраняем, запускаем приложение.
Жмем Photo, делаем снимок и подтверждаем его, открывается наше приложение и ImageView показывает только что сделанное фото.
Смотрим логи:
Photo uri: null
bitmap 120 x 160
getData вернул null, хотя хелп обещал, что там будет путь к файлу. Зато в Intent мы получили Bitmap. Но, как видим, размер его оставляет желать лучшего. Получается, что вообще нет доступа к полноценному сделанному фото. Оно у меня даже не создалось в папке, куда камера по дефолту фоты сохраняет.
Скорее всего, это зависит от версии Android и системного приложения камеры. И у вас вполне может быть другое поведение системы.
Попробуем с видео. Жмем Video, снимаем и подтверждаем ролик.
В логах:
Video uri: content://media/external/video/media/8018
С видео все ок. Камера сохранила видео в папку по умолчанию, и в Intent вернула нам Uri.
Кроме желаемого пути для видео вы можете указать еще некоторые параметры в intent:
MediaStore.EXTRA_VIDEO_QUALITY – качество видео. 0 (плохое) или 1 (хорошее).
MediaStore.EXTRA_DURATION_LIMIT – лимит продолжительности видео в секундах
MediaStore.EXTRA_SIZE_LIMIT – лимит размера получившегося видео в байтах
На следующем уроке:
- используем объект Camera для получения изображения с камеры
- подгоняем изображение под размеры экрана
- учитываем поворот устройства
Присоединяйтесь к нам в Telegram:
- в канале StartAndroid публикуются ссылки на новые статьи с сайта startandroid.ru и интересные материалы с хабра, medium.com и т.п.
- в чатах решаем возникающие вопросы и проблемы по различным темам: Android, Compose, Kotlin, RxJava, Dagger, Тестирование, Performance
- ну и если просто хочется поговорить с коллегами по разработке, то есть чат Флудильня