rokkonet

PC・Androidソフトウェア・アプリの開発・使い方に関するメモ

カメラ、SDカードの利用(許可/パーミッション取得) android開発

2020 Apr. 30.
2020 Apr. 29.
2020 Apr. 28.


AndroidManifest.xml

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="MY.PACKAGE.PROJECT">
    <uses-feature android:name="android.hardware.camera" />
    <uses-feature android:name="android.hardware.camera.autofocus"/>
    <uses-permission android:name="android.permission.CAMERA" />
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />

    <application
      ...
    </application>
</manifest>


MainActivity.java

public class MainActivity extends AppCompatActivity {

    Button button_take_photo;
    ImageView image_view;
    TextView text_view_uri_path, text_view_file_path, text_view_image_title;
    Uri pictureUri;
    private final int REQUEST_PERMISSION_CAMERA = 1000;
    private final int REQUEST_PERMISSION_EX_STORAGE = 1010;
    private final int REQUEST_CAPTURE_IMAGE = 100;

    @RequiresApi(api = Build.VERSION_CODES.M)
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        findViews();
        setListeners();

        // check & get permissions
        getPermissionSdCard();
        getPermissionCamera();
        if ( checkSelfPermission( Manifest.permission.WRITE_EXTERNAL_STORAGE ) !=
                PackageManager.PERMISSION_GRANTED) ||
                 ( checkSelfPermission(Manifest.permission.CAMERA ) !=
                PackageManager.PERMISSION_GRANTED)  {
            // display "Unable camera", in case of no-permission
            button_take_photo.setText(R.string.button_unable_photo );
            button_take_photo.setEnabled(false);
            return;
        }


        /*
         * カメラ起動ボタンが押された時のオンクリックリスナ
         */
        button_take_photo.setOnClickListener(new OnClickListener() {
            @Override
            public void onClick(View v) {
                ContentResolver contentResolver = getContentResolver();
                ContentValues values = new ContentValues(5);
                values.put(MediaStore.Images.Media.MIME_TYPE, "image/jpeg");
                values.put(MediaStore.Images.Media.DATE_MODIFIED, System.currentTimeMillis() / 1000);
                values.put(MediaStore.Images.Media.TITLE, "MYFILENAME");
                values.put(MediaStore.Images.Media.DISPLAY_NAME, "MYFILENAME");
                values.put(MediaStore.Images.Media.DATE_TAKEN, System.currentTimeMillis());

                pictureUri = contentResolver.insert(
                        MediaStore.Images.Media.EXTERNAL_CONTENT_URI, values);


                // カメラを起動
                Intent intent = new Intent();
                intent.setAction("android.media.action.IMAGE_CAPTURE");
                intent.putExtra(MediaStore.EXTRA_OUTPUT, pictureUri);
                // MediaStore.EXTRA_OUTPUTで指定したpictureUriに、撮影後に画像データが書き込まれる
                startActivityForResult(intent, REQUEST_CAPTURE_IMAGE);
            }
        });
    }


    /*
     * SD-card permissionの確認・取得
     */
    @TargetApi(Build.VERSION_CODES.M)
    private void getPermissionSdCard() {
        if (checkSelfPermission(Manifest.permission.WRITE_EXTERNAL_STORAGE) !=
                PackageManager.PERMISSION_GRANTED) {

            // 許可されていない
            if (shouldShowRequestPermissionRationale(Manifest.permission.WRITE_EXTERNAL_STORAGE)) {
                
                /* shouldShowRequestPermissionRationale()は
                 * 許可が必要な理由をユーザーに示すべき場合にtrueとなる

                 * 一度も拒否されていない(まだパーミッションダイアログを出していない)ならfalse、
                 * 1度拒否されて「今後表示しない」が選択されてないならtrue、
                 * 1度拒否されて「今後表示しない」が選択された場合はfalse
                 * https://peitechblog.com/?p=949 より
                 */

                new AlertDialog.Builder(this)
                    .setTitle("パーミッションの追加説明")
                    // 1) Messageを確認するだけのダイアログを表示する
                    .setMessage("写真を保存するには許可が必要です")
                    .setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
                       // 2) OKがタップされると許可諾否ダイアログが表示される
                        @Override
                        public void onClick(DialogInterface dialog, int which) {
                            ActivityCompat.requestPermissions(MainActivity.this,
                                    new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE},
                                    REQUEST_PERMISSION_EX_STORAGE);
                        }
                    })
                    .create()
                    .show();
                // 許可されたかどうかはonRequestPermissionsResult()で確認できる


                /* Kotlin
                AlertDialog.Builder(this)
                    .setTitle("パーミッションの追加説明")
                    //  1) Messageを確認するだけのダイアログを表示する
                    .setMessage("写真を保存するには許可が必要です")
                    .setPositiveButton(
                        android.R.string.ok, DialogInterface.OnClickListener { _, _ ->
                            // 2) OKがタップされると許可諾否ダイアログが表示される
                            ActivityCompat.requestPermissions(
                                this,arrayOf(Manifest.permission.WRITE_EXTERNAL_STORAGE),
                                REQUEST_PERMISSION_EX_STORAGE)
                        })
                    .create()
                    .show()
                 */

            } else {
                /* 初めてパーミッション要求ダイアログを出す場合
                 * あるいは
                 * 1度拒否されて「今後表示しない」が選択された場合
                 */

                /* 前に、許可要求ダイアログで「今後は確認しない」にチェックして許可しなかった場合、
                 * checkSelfPermission() はダイアログ表示せず、権限を許可しようとしない。
                 * https://qiita.com/caad1229/items/35bab757217b204711df より
                 */

                ActivityCompat.requestPermissions(MainActivity.this,
                    new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE},
                    REQUEST_PERMISSION_EX_STORAGE);
                // 許可されたかどうかはonRequestPermissionsResult()で確認できる

            }
        }
    }


    /*
     * camera permissionの確認・取得
     */
    @TargetApi(Build.VERSION_CODES.M)
    private void getPermissionCamera() {
        if (checkSelfPermission(Manifest.permission.CAMERA) !=
                PackageManager.PERMISSION_GRANTED) {

            // 許可されていない
            if (shouldShowRequestPermissionRationale(Manifest.permission.CAMERA)) {
                
                /* shouldShowRequestPermissionRationale()は
                 * 許可が必要な理由をユーザーに示すべき場合にtrueとなる

                 * 一度も拒否されていない(まだパーミッションダイアログを出していない)ならfalse、
                 * 1度拒否されて「今後表示しない」が選択されてないならtrue、
                 * 1度拒否されて「今後表示しない」が選択された場合はfalse
                 * https://peitechblog.com/?p=949 より
                 */

                new AlertDialog.Builder(this)
                    .setTitle("パーミッションの追加説明")
                    // 1) Messageを確認するだけのダイアログを表示する
                    .setMessage("写真を撮るにはパーミッションが必要です")
                    .setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
                       // 2) OKがタップされると許可諾否ダイアログが表示される
                        @Override
                        public void onClick(DialogInterface dialog, int which) {
                            ActivityCompat.requestPermissions(MainActivity.this,
                                    new String[]{Manifest.permission.CAMERA},
                                    REQUEST_PERMISSION_CAMERA);
                        }
                    })
                    .create()
                    .show();
                // 許可されたかどうかはonRequestPermissionsResult()で確認できる

                /* Kotlin
                AlertDialog.Builder(this)
                    .setTitle("パーミッションの追加説明")
                    //  1) Messageを確認するだけのダイアログを表示する
                    .setMessage("Rationale 写真を撮るには許可が必要です")
                    .setPositiveButton(
                        android.R.string.ok, DialogInterface.OnClickListener { _, _ ->
                            // 2) OKがタップされると許可諾否ダイアログが表示される
                            ActivityCompat.requestPermissions(
                                this,arrayOf(Manifest.permission.CAMERA),
                                REQUEST_PERMISSION_CAMERA)
                        })
                    .create()
                    .show()
                 */


            } else {
                /* 初めてパーミッション要求ダイアログを出す場合
                 * あるいは
                 * 1度拒否されて「今後表示しない」が選択された場合
                 */

                /* 前に、許可要求ダイアログで「今後は確認しない」にチェックして許可しなかった場合、
                 * checkSelfPermission() はダイアログ表示せず、権限を許可しようとしない。
                 * https://qiita.com/caad1229/items/35bab757217b204711df より
                 */

                ActivityCompat.requestPermissions(MainActivity.this,
                    new String[]{Manifest.permission.CAMERA},
                    REQUEST_PERMISSION_CAMERA);
                // 許可されたかどうかはonRequestPermissionsResult()で確認できる

            }
        }
    }


    /*
     * requestPermission結果に対する処理
     */
    @Override
    public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions,
            @NonNull int[] grantResults) {
        if (requestCode == REQUEST_PERMISSION_CAMERA) {
            if (grantResults[0] != PackageManager.PERMISSION_GRANTED) {
                // 拒否された時の対応
                Toast toast = Toast.makeText(this, "カメラ利用できません",
                        Toast.LENGTH_LONG);
                toast.show();
            }
        } else if (requestCode == REQUEST_PERMISSION_EX_STORAGE) {
            if (grantResults[0] != PackageManager.PERMISSION_GRANTED) {
                // 拒否された時の対応
                Toast toast = Toast.makeText(this,
                        "外部ストレージを利用できません",
                        Toast.LENGTH_LONG);
                toast.show();
            }
        } else {
            Toast toast = Toast.makeText(this, "Bad requestCode", Toast.LENGTH_LONG);
            toast.show();
        }
    }


    /*
     * Intentによってこのアクティビティから起動された他の機能から戻ってきた時の処理
     */
    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        // 写真撮影
        if (requestCode == REQUEST_CAPTURE_IMAGE) {
            buttonPhoto.setEnabled(true);
            if (resultCode != RESULT_OK) {
                /*
                 * 正しい結果が得られなかった場合の処理
                 * 撮影キャンセルなど
                 */
                return;
            }

            Uri resultUri = null;

            // 写真のUriを取得し、撮影した写真を取り出す
            if (data != null && data.getData() != null) {
                resultUri = data.getData();
            } else {
                resultUri = pictureUri;
            }
        }
    }


    /*
     * 撮影した写真の取り出し
     *     returns Map<String, Strng> : {path_of_picture-file, title_of_picture} 
     */
    Map<String, String> getPhotoFromUri( Uri pictureUri ) {
        // get filepath & title from uri
        ContentResolver contentResolver = this.getContentResolver();
        String[] columns =
                {
                    MediaStore.Images.Media.DATA,
                    MediaStore.Images.Media.TITLE
                };
        Cursor cursor = contentResolver.query(pictureUri, columns, null,
                null, null);
        if(cursor == null) return null;
        int pathIndex = cursor.getColumnIndex( MediaStore.Images.Media.DATA);
        int titleIndex = cursor.getColumnIndex( MediaStore.Images.Media.TITLE);
        cursor.moveToFirst();
        Map<String, String> map = new HashMap<>();
        return map.put( cursor.getString(pathIndex), cursor.getString(titleIndex) );
    }


    /*
     * リスナーセット
     */
    protected void setListeners( ) {
        // カメラ起動ボタンが押された時
         button_take_photo.setOnClickListener(new View.OnClickListener() {

             @Override
             public void onClick(View v) {
                /*
                写真用情報を収集し、ContentResolverを使ってandroidに備わっている
                MediaStore.Images.Mediaデータベースに写真用情報を追加し、そのURI
                を取得する
                */
                long dateTaken = System.currentTimeMillis();
                String filename = DateFormat.format("yyyy-MM-dd_kk:mm:ss", dateTaken).toString() + ".jpg";
                ContentResolver contentResolver = getContentResolver();
                ContentValues values = new ContentValues(5);
                values.put(MediaStore.Images.Media.MIME_TYPE, "image/jpeg");
                values.put(MediaStore.Images.Media.DATE_MODIFIED, System.currentTimeMillis()/1000);
                values.put(MediaStore.Images.Media.TITLE, filename);
                values.put(MediaStore.Images.Media.DISPLAY_NAME, filename);
                values.put(MediaStore.Images.Media.DATE_TAKEN,System.currentTimeMillis());
                pictureUri = contentResolver.insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, values);

                // カメラを起動する
                Intent intent = new Intent();
                intent.setAction("android.media.action.IMAGE_CAPTURE");
                intent.putExtra(MediaStore.EXTRA_OUTPUT, pictureUri);
                  // MediaStore.EXTRA_OUTPUTで指定したpictureUriに、撮影後に画像データが書き込まれる
                startActivityForResult(intent, REQUEST_CAPTURE_IMAGE);
             }
         });
    }


    protected void findViews( ) {
        button_take_photo = findViewById(R.id.button_take_photo);
        image_view = findViewById(R.id.imageView);
        text_view_uri_path = findViewById(R.id.textViewUriPath);
        text_view_file_path = findViewById(R.id.textViewFilePath);
        text_view_image_title = findViewById(R.id.textViewImageTitle);
    }
}