web-dev-qa-db-ja.com

Android Cordovaカメラを使用せずにCordovaアプリケーションのファイルアップロードでカメラを選択する

だから私はcordovaアプリを作りました、Androidプラットフォームを追加し、入力フィールドを持つ単純なhtmlを作りました

<input type="file" capture="camera" accept="image/*" id="takePictureField">

私が追加しました

<uses-permission Android:name="Android.permission.CAMERA" />
<uses-feature Android:name="Android.hardware.camera" Android:required="false" />
<uses-feature Android:name="Android.hardware.camera.autofocus" />
<feature name="http://api.phonegap.com/1.0/camera" />

マニフェストファイルに。

ボタンを押すと、カメラで新しい写真を撮ることができません。私が逃した許可、または何か他のものはありますか?

コルドバの写真撮影機能は使用できません。純粋なhtmlで実行する必要があります。

27
pjensen68321

少しググリングした後、これは私が結論付けることができるものです:

モバイルブラウザーでのメディアキャプチャには、まだいくつかの問題があるようです。これをチェックしてください リンク 。抜粋は言う:

実際、現在の実装はcapture属性にまったく依存しておらず、typeおよびaccept属性にのみ依存しているようです。ブラウザにダイアログボックスが表示されます。ユーザーは、ファイルを取得する必要がある場所を選択できます。キャプチャ属性は考慮されません。たとえば、iOS Safariは、画像とビデオ(オーディオではない)のaccept属性(キャプチャではない)に依存しています。 accept属性を使用しない場合でも、ブラウザで「写真を撮るかビデオを撮る」か「既存のものを選択する」のどちらかを選択できます

したがって、キャプチャ属性は影響を与えないように見えます。

また、この作業の詳細については、 SO post を参照することをお勧めします。それが役に立てば幸い。乾杯。

更新:反対票を投じた後、問題についてさらに深く掘り下げます。この問題の最適な解決策はCordovaカメラプラグインを使用することであるため、ほとんどの検索は成功しませんでした。最後にこれにつまずいた SO post これはこの質問とまったく同じです。ユーザーは問題を解決できました(ただし、横断歩道のWebビューを使用)。その投稿の答えは、@ Fabioによってすでにここで言及されています。ただし、権限を含めるためだけにプラグインを追加する代わりに、 cordova-custom-plugin を使用して必要な権限を追加できます。

また、@ jcesarmobileのコメント(Cordovaエキスパート)によるクロスウォークWebビュープラグインなしの投稿によると、入力タイプはiOSではのみ機能し、Androidでは機能しません。したがって、カメラプラグインを使用することが、横断歩道プラグインを使用せずに機能させる唯一の方法です。

更新2:更新された質問の後、私はこの問題の解決のためにもう少し深く掘り下げました。しかし、今はこの問題がAndroid Webviewで解決されないことを確認できます。Cordovaの問題ではなく、Chromium Webビューの問題のようです。

詳細については、Apache Cordova Issue Trackerで以下の問題を確認するように要求してください。

これらの問題はどちらも現在まで解決されていません。だから今のところ、Androidで動作させることができないと思います。Cordovaカメラプラグインを使用しない限り。私に同意して解決策を受け入れてください。)乾杯

12
Gandhi

プラグインのソースコードを変更したくない場合の回避策を次に示します。写真を撮るための別のコントロールを作成します。コントロールのクリックイベントを次のハンドラーに設定します。

(event) => {
    event.stopPropagation();
    Camera.sourceType = Camera.PictureSourceType.CAMERA;

    const onCameraSuccess = (imgURL) => {
        window.resolveLocalFileSystemURL(imgURL, (entry) => {
            const onFileSuccess = (file) => this._onSelectMultipleFiles(event, file);
            const onFileFail = (error) => console.log(error);
            entry.file(onFileSuccess, onFileFail);
        });

        console.log("picture retrieved successfully");
    };

    const onCameraFail = () => {
        console.log("picture retrieval failed");
    };

    navigator.camera.getPicture(onCameraSuccess, onCameraFail, {
        quality: 100, 
        destinationType: Camera.DestinationType.FILE_URI,
    });
}

これは、コントロールがクリックされたときにcordova-plugin-cameraを使用してカメラアプリを起動し、cordova-plugin-fileからresolveLocalFileSystemURLを呼び出して、カメラから返された画像URLを、_onSelectMultipleFilesメソッドで処理されるFileオブジェクトに変換します。 cordova docsの例を参照

この実装は私のプロジェクトに固有であるため、onSuccessで渡されたイメージURLの使用方法によってはresolveLocalFileSystemURLを呼び出す必要がない場合がありますcamera.getPictureのコールバック。

明らかに、これの欠点は、2つのコントロールを使用する必要があることです。1つはファイルから画像を取得するため、もう1つはカメラから画像を取得するためです。

0
Yoshimitsu

免責事項:この回答に関する最近のコメントでは、機能しなくなったと報告されています。この質問と回答は古いコードバとプラグインのバージョンに関連しているため、現在の状況には適用されない場合があります。

古い質問ですが、私はこの問題に直面しました。あなたが望むものを達成する最も簡単な方法は、あなたがそれらを使用する必要がない場合でも、あなたのアプリにこれらのコルドバプラグインを含めることです

cordova-plugin-camera
cordova-plugin-media-capture
cordova-plugin-device
cordova-plugin-file
cordova-plugin-media

これらがロードされていると、私はこの動作を見つけました

クリック

<input type="file" />

カメラ、ビデオカメラ、マイク、ドキュメントから選択するように求めます

クリック

<input type="file" accept="image/*" />

カメラまたはドキュメントから選択するように求めます

クリック

<input type="file" accept="image/*" capture="capture" />

すぐにカメラを起動します。

0
Fabio C.

私のプロジェクトは、cordova-plugin-inappbrowserを使用していました。

プラグインソースのクラスInAppBrowserのメソッドonActivityResultとonShowFileChooserを使用して、マージソリューション filechooserのない入力フィールドからwebview open camera で解決します。

プラグインバージョン3.0.0からInAppBrowserクラスに加えられた変更を確認する

1-インポートを含める:

import Android.app.Activity;
import Android.os.Environment;
import Android.provider.MediaStore;
import Android.util.Log;
import Java.io.File;
import Java.io.IOException;
import Java.text.SimpleDateFormat;
import Java.util.Date;

2-バリアベルを宣言します。

    private String mCM;

3-onShowFileChooserコードを置き換える


                    // For Android 5.0+
                    public boolean onShowFileChooser (WebView webView, ValueCallback<Uri[]> filePathCallback, WebChromeClient.FileChooserParams fileChooserParams)
                    {
                        LOG.d(LOG_TAG, "File Chooser 5.0+");

                        // If callback exists, finish it.
                        if(mUploadCallbackLollipop != null) {
                            mUploadCallbackLollipop.onReceiveValue(null);
                        }
                        mUploadCallbackLollipop = filePathCallback;

                        Intent takePictureIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);

                        if(takePictureIntent.resolveActivity(cordova.getActivity().getPackageManager()) != null) {

                            File photoFile = null;
                            try{
                                photoFile = createImageFile();
                                takePictureIntent.putExtra("PhotoPath", mCM);
                            }catch(IOException ex){
                                Log.e(LOG_TAG, "Image file creation failed", ex);
                            }
                            if(photoFile != null){
                                mCM = "file:" + photoFile.getAbsolutePath();
                                takePictureIntent.putExtra(MediaStore.EXTRA_OUTPUT, Uri.fromFile(photoFile));
                            }else{
                                takePictureIntent = null;
                            }
                        }
                        // Create File Chooser Intent
                        Intent contentSelectionIntent = new Intent(Intent.ACTION_GET_CONTENT);
                        contentSelectionIntent.addCategory(Intent.CATEGORY_OPENABLE);
                        contentSelectionIntent.setType("*/*");
                        Intent[] intentArray;
                        if(takePictureIntent != null){
                            intentArray = new Intent[]{takePictureIntent};
                        }else{
                            intentArray = new Intent[0];
                        }

                        Intent chooserIntent = new Intent(Intent.ACTION_CHOOSER);
                        chooserIntent.putExtra(Intent.EXTRA_INTENT, contentSelectionIntent);
                        chooserIntent.putExtra(Intent.EXTRA_TITLE, "Selecione a imagem");
                        chooserIntent.putExtra(Intent.EXTRA_INITIAL_INTENTS, intentArray);

                        // Run cordova startActivityForResult
                        cordova.startActivityForResult(InAppBrowser.this, chooserIntent, FILECHOOSER_REQUESTCODE);

                        return true;
                    }

4-メソッドの作成


    private File createImageFile() throws IOException{
        @SuppressLint("SimpleDateFormat") String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss").format(new Date());
        String imageFileName = "img_"+timeStamp+"_";
        File storageDir = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES);
        return File.createTempFile(imageFileName,".jpg",storageDir);
    }

5-onActivityResultを置き換える


    public void onActivityResult(int requestCode, int resultCode, Intent intent) {
        // For Android >= 5.0
        if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.Lollipop) {

            LOG.d(LOG_TAG, "onActivityResult (For Android >= 5.0)");

            Uri[] results = null;
            //Check if response is positive
            if(resultCode== Activity.RESULT_OK){
                if(requestCode == FILECHOOSER_REQUESTCODE){
                    if(null == mUploadCallbackLollipop){
                        return;
                    }
                    if(intent == null || intent.getData() == null){
                        //Capture Photo if no image available
                        if(mCM != null){
                            results = new Uri[]{Uri.parse(mCM)};
                        }
                    }else{
                        String dataString = intent.getDataString();
                        if(dataString != null){
                            results = new Uri[]{Uri.parse(dataString)};
                        }
                    }
                }
            }
            mUploadCallbackLollipop .onReceiveValue(results);
            mUploadCallbackLollipop = null;
        }
        // For Android < 5.0
        else {
            LOG.d(LOG_TAG, "onActivityResult (For Android < 5.0)");
            // If RequestCode or Callback is Invalid
            if(requestCode != FILECHOOSER_REQUESTCODE || mUploadCallback == null) {
                super.onActivityResult(requestCode, resultCode, intent);
                return;
            }

            if (null == mUploadCallback) return;
            Uri result = intent == null || resultCode != cordova.getActivity().RESULT_OK ? null : intent.getData();

            mUploadCallback.onReceiveValue(result);
            mUploadCallback = null;
        }
    }

0

Gilberto answer からインスピレーションを得て、これが私の適応です:

  1. 常にカメラを開く
  2. 実際のカメラを開く前に権限を要求します。権限が付与されている場合はカメラを開き、それ以外の場合はトーストを表示します。
  3. 権限が拒否された場合は、入力フィルをクリックしてそれを要求します。画面を更新したり変更したりする必要はありません。

新しい方法は4つあります。1つは許可を取得する方法、もう1つは許可を確認する方法、写真を撮る前に画像ファイルを作成する方法、カメラを開く方法です。

ここにそれらの方法があります


    /**
     * Called by the system when the user grants permissions
     *
     * @param requestCode
     * @param permissions
     * @param grantResults
     */
    public void onRequestPermissionResult(int requestCode, String[] permissions, int[] grantResults)
            throws JSONException {

        for (int r : grantResults) {
            if (r == PackageManager.PERMISSION_DENIED) {
                Toast.makeText(cordova.getActivity(), "Autorisation pour ouvrir la caméra refusé", Toast.LENGTH_LONG)
                        .show();
                mUploadCallbackLollipop.onReceiveValue(null);
                mUploadCallbackLollipop = null;
                return;
            }
        }

        if (requestCode == PERM_REQUEST_CAMERA_FOR_FILE) {
            startCameraActivityForAndroidFivePlus();
        }
    }

    private File createImageFile() throws IOException {
        @SuppressLint("SimpleDateFormat")
        String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss").format(new Date());
        String imageFileName = "img_" + timeStamp + "_";
        File storageDir = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES);
        return File.createTempFile(imageFileName, ".jpg", storageDir);
    }

    private void startCameraActivityForAndroidFivePlus() {
        Intent takePictureIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
        File photoFile = null;
        try {
            photoFile = createImageFile();
            takePictureIntent.putExtra("PhotoPath", mCapturedPhoto);
        } catch (IOException ex) {
            LOG.e(LOG_TAG, "Image file creation failed", ex);
        }
        if (photoFile != null) {
            mCapturedPhoto = "file:" + photoFile.getAbsolutePath();
            takePictureIntent.putExtra(MediaStore.EXTRA_OUTPUT, Uri.fromFile(photoFile));
        } else {
            takePictureIntent = null;
        }

        // Fix FileUriExposedException exposed beyond app through ClipData.Item.getUri()
        StrictMode.VmPolicy.Builder builder = new StrictMode.VmPolicy.Builder();
        StrictMode.setVmPolicy(builder.build());

        // Run cordova startActivityForResult
        cordova.startActivityForResult(InAppBrowser.this, takePictureIntent, FILECHOOSER_REQUESTCODE_Lollipop);
    }

    private boolean checkPermissionForCamera() {
        if (Build.VERSION.SDK_INT >= 23) {
            List<String> permToAsk = new ArrayList<String>();
            if (cordova.getActivity().checkSelfPermission(
                    Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) {
                permToAsk.add(Manifest.permission.WRITE_EXTERNAL_STORAGE);
            }
            if (cordova.getActivity()
                    .checkSelfPermission(Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED) {
                permToAsk.add(Manifest.permission.CAMERA);
            }
            if (permToAsk.size() > 0) {
                cordova.requestPermissions(InAppBrowser.this, PERM_REQUEST_CAMERA_FOR_FILE,
                        permToAsk.toArray(new String[permToAsk.size()]));
                return true;
            }
        }
        return false;
    }

InAppChromeClient実装が次のように更新されました。

inAppWebView.setWebChromeClient(new InAppChromeClient(thatWebView) {
                    // For Android 5.0+
                    public boolean onShowFileChooser(WebView webView, ValueCallback<Uri[]> filePathCallback,
                            WebChromeClient.FileChooserParams fileChooserParams) {

                        LOG.d(LOG_TAG, "File Chooser 5.0+");
                        // If callback exists, finish it.
                        if (mUploadCallbackLollipop != null) {
                            mUploadCallbackLollipop.onReceiveValue(null);
                        }
                        mUploadCallbackLollipop = filePathCallback;

                        // #Update to always open camera app
                        if (checkPermissionForCamera()) {
                            return true;
                        }

                        startCameraActivityForAndroidFivePlus();
                        return true;
                    }
                });

そしてここにそれらの定数があります:

    private String mCapturedPhoto;
    private final static int PERM_REQUEST_CAMERA_FOR_FILE = 3;

完全な InAppBrowser.Javaはここにあります。

0
Hugo Gresse