Руководство Android Camera

1- Android Camera

Camera (Камера) это устройство позволяющее вам делать фотографии или снимать видео. в Android есть 2 cпособа работы с Camera.

Способ 1:

В системе  Android имеется приложения для работы с Камерой, ваше приложение можетт вызвать это приложение через неявное намерение (Implicit Intent), чтобы дать запрос на действие для Камеры, например запрос открыть  Камеру и сделать фото, или открыть  Камеру чтобы снять видео, и получить возвращенный результат.

Способ 2:

Android предоставляет вам API чтобы работать напрямую с Камерой.

С Android Level < 21 вы можете работать напрямую с Камерой через  android.hardware.Camera, но этот класс уже устарел (Deprected) и больше не используется в   Android Level >= 21, рекомендуем вам использовать Camera2 API.
В этом документе я покажу вам ка использовать Н еявное намерение для вызова приложения  Камера имеющийся в системе для открытия  Камера, чтобы сфотографировать или снять видео.
Можете посмотреть инструкцию использования  Camera2 API по ссылке:
  • TODO Link!

2- Обзор

В системе Android имеется приложение для работы с Камерой, в приложении вы можете создать Intent намерение чтобы вызвать данное приложение, запросить открытие Камеры для снятия видео или сделать фото.
// Create an implicit intent, for image capture.
Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);

int REQUEST_ID_IMAGE_CAPTURE = 100;

// Start camera and wait for the results.
this.startActivityForResult(intent, REQUEST_ID_IMAGE_CAPTURE);
Вид намерений для Камеры:
Вид Intent Описание
ACTION_IMAGE_CAPTURE_SECURE

Возвращает Image сфотографированный с Camera, когда устройство защищено

ACTION_VIDEO_CAPTURE

Вызывает video приложение в Android чтобы снять video из Camera.

EXTRA_SCREEN_ORIENTATION

Используется для настройки направления экрана "vertical" или "landscape"

EXTRA_FULL_SCREEN

Используется для управления интерфейсом пользователя в ViewImage

INTENT_ACTION_VIDEO_CAMERA

Используется для старта Camera в моде video.

EXTRA_SIZE_LIMIT

Используется для определения самого большого значения для размера файла изображения и видео

В том случае, если вы хотите сохранить фото или видео снятые устройством, вам нужно настроить разрешение на чтение и записи данных в устройство. Конфигурация на  AndroidManifest.xml.
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
С  Android Level >= 23, вам нужно использовать код, чтобы попросить пользователя разрешения прочитать и записать данные в устройство.
// With Android Level >= 23, you have to ask the user
// for permission to read/write data on the device.
if (android.os.Build.VERSION.SDK_INT >= 23) {

    // Check if we have read/write permission
    // Kiểm tra quyền đọc/ghi dữ liệu vào thiết bị lưu trữ ngoài.
    int readPermission = ActivityCompat.checkSelfPermission(this,
                                   Manifest.permission.READ_EXTERNAL_STORAGE);
    int writePermission = ActivityCompat.checkSelfPermission(this,
                                   Manifest.permission.WRITE_EXTERNAL_STORAGE);

   if (writePermission != PackageManager.PERMISSION_GRANTED ||
           readPermission != PackageManager.PERMISSION_GRANTED) {
       // If don't have permission so prompt the user.
       this.requestPermissions(
               new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE,
                       Manifest.permission.READ_EXTERNAL_STORAGE},
               REQUEST_ID_READ_WRITE_PERMISSION
       );
   }
}
Обработка при ответе пользователя.
// When you have the request results
@Override
public void onRequestPermissionsResult(int requestCode,
                                   String permissions[], int[] grantResults) {

    super.onRequestPermissionsResult(requestCode, permissions, grantResults);
    //
    switch (requestCode) {
        case REQUEST_ID_READ_WRITE_PERMISSION: {

            // Note: If request is cancelled, the result arrays are empty.
            // Permissions granted (read/write).
            if (grantResults.length > 1
                    && grantResults[0] == PackageManager.PERMISSION_GRANTED
                    && grantResults[1] == PackageManager.PERMISSION_GRANTED) {

                Toast.makeText(this, "Permission granted!", Toast.LENGTH_LONG).show();

                this.captureVideo();

            }
            // Cancelled or denied.
            else {
                Toast.makeText(this, "Permission denied!", Toast.LENGTH_LONG).show();
            }
            break;
        }
    }
}

3- Camera для эмулятора

Если вы работаете с Эмулятором, вам нужно настроить Камеру, есть два варианта:
  1. Использовать Камеру эмулятор.
  2. Или использовать Webcam компьютера как Камеру телефона для эмулятора.
Если вы не настроили  Камера, вы получите ошибку:
java.lang.RuntimeException: Fail to connect to camera service
Здесь я настраиваю Камеру эмулятор.
Используем Камеру эмулятор.

4- Пример

Создать новый project с названием  AndroidCameraDemo:
Добавить настройки, позволяющие читать и записывать данные в устройстве.
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="org.o7planning.androidcamerademo">


    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />

    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:supportsRtl="true"
        android:theme="@style/AppTheme">
        <activity android:name=".MainActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>

</manifest>
Дизайн интерфейса:
activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:paddingBottom="@dimen/activity_vertical_margin"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    tools:context="org.o7planning.androidcamerademo.MainActivity">

    <ImageView
        android:layout_width="fill_parent"
        android:layout_height="180dp"
        android:id="@+id/imageView"
        android:layout_alignParentTop="true" />

    <VideoView
        android:layout_width="wrap_content"
        android:layout_height="180dp"
        android:id="@+id/videoView"
        android:layout_below="@+id/imageView"
        android:layout_alignParentLeft="true"
        android:layout_alignParentStart="true" />

    <Button
        style="?android:attr/buttonStyleSmall"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Capture Image"
        android:id="@+id/button_image"
        android:layout_alignParentBottom="true"
        android:layout_alignParentLeft="true"
        android:layout_alignParentStart="true" />

    <Button
        style="?android:attr/buttonStyleSmall"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Capture Video"
        android:id="@+id/button_video"
        android:layout_alignTop="@+id/button_image"
        android:layout_toRightOf="@+id/button_image"
        android:layout_toEndOf="@+id/button_image" />

</RelativeLayout>
MainActivity.java
package org.o7planning.androidcamerademo;

import android.Manifest;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.graphics.Bitmap;
import android.net.Uri;
import android.os.Bundle;
import android.os.Environment;
import android.provider.MediaStore;
import android.support.v4.app.ActivityCompat;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.Toast;
import android.widget.VideoView;

import java.io.File;

public class MainActivity extends AppCompatActivity {

   private Button buttonImage;
   private Button buttonVideo;

   private VideoView videoView;
   private ImageView imageView;

   private static final int REQUEST_ID_READ_WRITE_PERMISSION = 99;
   private static final int REQUEST_ID_IMAGE_CAPTURE = 100;
   private static final int REQUEST_ID_VIDEO_CAPTURE = 101;

   @Override
   protected void onCreate(Bundle savedInstanceState) {
       super.onCreate(savedInstanceState);
       setContentView(R.layout.activity_main);

       this.buttonImage = (Button) this.findViewById(R.id.button_image);
       this.buttonVideo = (Button) this.findViewById(R.id.button_video);
       this.videoView = (VideoView) this.findViewById(R.id.videoView);
       this.imageView = (ImageView) this.findViewById(R.id.imageView);

       this.buttonImage.setOnClickListener(new Button.OnClickListener() {
           @Override
           public void onClick(View v) {
               captureImage();
           }
       });

       this.buttonVideo.setOnClickListener(new Button.OnClickListener() {
           @Override
           public void onClick(View v) {
               askPermissionAndCaptureVideo();
           }
       });
   }

   private void captureImage() {
       // Create an implicit intent, for image capture.
       Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);

       // Start camera and wait for the results.
       this.startActivityForResult(intent, REQUEST_ID_IMAGE_CAPTURE);
   }

   private void askPermissionAndCaptureVideo() {

       // With Android Level >= 23, you have to ask the user
       // for permission to read/write data on the device.
       if (android.os.Build.VERSION.SDK_INT >= 23) {

           // Check if we have read/write permission
           int readPermission = ActivityCompat.checkSelfPermission(this,
                                          Manifest.permission.READ_EXTERNAL_STORAGE);
           int writePermission = ActivityCompat.checkSelfPermission(this,
                                          Manifest.permission.WRITE_EXTERNAL_STORAGE);

           if (writePermission != PackageManager.PERMISSION_GRANTED ||
                   readPermission != PackageManager.PERMISSION_GRANTED) {
               // If don't have permission so prompt the user.
               this.requestPermissions(
                       new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE,
                               Manifest.permission.READ_EXTERNAL_STORAGE},
                       REQUEST_ID_READ_WRITE_PERMISSION
               );
               return;
           }
       }
       this.captureVideo();
   }

   private void captureVideo() {

       // Create an implicit intent, for video capture.
       Intent intent = new Intent(MediaStore.ACTION_VIDEO_CAPTURE);

       // The external storage directory.
       File dir = Environment.getExternalStorageDirectory();
       if (!dir.exists()) {
           dir.mkdirs();
       }
       // file:///storage/emulated/0/myvideo.mp4
       String savePath = dir.getAbsolutePath() + "/myvideo.mp4";
       File videoFile = new File(savePath);
       Uri videoUri = Uri.fromFile(videoFile);

       // Specify where to save video files.
       intent.putExtra(MediaStore.EXTRA_OUTPUT, videoUri);

       // Start camera and wait for the results.
       this.startActivityForResult(intent, REQUEST_ID_VIDEO_CAPTURE);
   }


   // When you have the request results
   @Override
   public void onRequestPermissionsResult(int requestCode,
                                          String permissions[], int[] grantResults) {

       super.onRequestPermissionsResult(requestCode, permissions, grantResults);
       //
       switch (requestCode) {
           case REQUEST_ID_READ_WRITE_PERMISSION: {

               // Note: If request is cancelled, the result arrays are empty.
               // Permissions granted (read/write).
               if (grantResults.length > 1
                       && grantResults[0] == PackageManager.PERMISSION_GRANTED
                       && grantResults[1] == PackageManager.PERMISSION_GRANTED) {

                   Toast.makeText(this, "Permission granted!", Toast.LENGTH_LONG).show();

                   this.captureVideo();

               }
               // Cancelled or denied.
               else {
                   Toast.makeText(this, "Permission denied!", Toast.LENGTH_LONG).show();
               }
               break;
           }
       }
   }

   // When results returned
   @Override
   protected void onActivityResult(int requestCode, int resultCode, Intent data) {
       super.onActivityResult(requestCode, resultCode, data);

       if (requestCode == REQUEST_ID_IMAGE_CAPTURE) {
           if (resultCode == RESULT_OK) {
               Bitmap bp = (Bitmap) data.getExtras().get("data");
               this.imageView.setImageBitmap(bp);
           } else if (resultCode == RESULT_CANCELED) {
               Toast.makeText(this, "Action canceled", Toast.LENGTH_LONG).show();
           } else {
               Toast.makeText(this, "Action Failed", Toast.LENGTH_LONG).show();
           }
       } else if (requestCode == REQUEST_ID_VIDEO_CAPTURE) {
           if (resultCode == RESULT_OK) {
               Uri videoUri = data.getData();
               Log.i("MyLog", "Video saved to: " + videoUri);
               Toast.makeText(this, "Video saved to:\n" +
                       videoUri, Toast.LENGTH_LONG).show();
               this.videoView.setVideoURI(videoUri);
               this.videoView.start();
           } else if (resultCode == RESULT_CANCELED) {
               Toast.makeText(this, "Action Cancelled.",
                       Toast.LENGTH_LONG).show();
           } else {
               Toast.makeText(this, "Action Failed",
                       Toast.LENGTH_LONG).show();
           }
       }
   }

}
OK, теперь вы можете запустить приложение. Здесь я запускаю приложении эмулятора с Камерой эмулятор.

5- Проблема с эмулятором

Примечание: При первом запуске приложения, нажмите на "Capture Video", приложение спросит авторизацию для чтения и заиси на устройстве хранения.

После того как пользователь разрешил читать/записывать данные на внешнем устройстве хранения, но все равно не работает (Это проблема эмулятора).

Вы можете перезапустить приложение с  Android Studio. Приложение разрешено с предыдущего запуска и не переспросит. Вы так же не встретите больше ошибку иллюстрированную выше.