Programing

Android M 카메라 의도 + 권한 버그?

lottogame 2020. 11. 24. 07:33
반응형

Android M 카메라 의도 + 권한 버그?


새로운 Android M 권한 변경에 대해 앱을 준비하려고하는데 이상한 동작을 발견했습니다. 내 앱은 카메라 인 텐트 메커니즘을 사용하여 사용자가 카메라에서 사진을 얻을 수 있도록합니다. 그러나 다른 활동에서는 카메라 권한으로 카메라 자체를 사용해야합니다 (이를 필요로하는 라이브러리 종속성 card.io 때문에).

그러나 카메라 인 텐트를 시작하려고 할 때 카메라 인 텐트 만 필요한 활동에서 M을 사용하면 다음 충돌이 표시됩니다 (매니페스트에서 카메라 권한을 제거하면 발생하지 않음).

> 09-25 21:57:55.260 774-8053/? I/ActivityManager: START u0
> {act=android.media.action.IMAGE_CAPTURE flg=0x3000003
> pkg=com.google.android.GoogleCamera
> cmp=com.google.android.GoogleCamera/com.android.camera.CaptureActivity
> (has clip) (has extras)} from uid 10098 on display 0 09-25
> 21:57:55.261 774-8053/? W/ActivityManager: Permission Denial: starting
> Intent { act=android.media.action.IMAGE_CAPTURE flg=0x3000003
> pkg=com.google.android.GoogleCamera
> cmp=com.google.android.GoogleCamera/com.android.camera.CaptureActivity
> (has clip) (has extras) } from null (pid=-1, uid=10098) with revoked
> permission android.permission.CAMERA 09-25 21:57:55.263 32657-32657/?
> E/ResolverActivity: Unable to launch as uid 10098 package
> com.example.me.mycamerselectapp, while running in android:ui 09-25
> 21:57:55.263 32657-32657/? E/ResolverActivity:
> java.lang.SecurityException: Permission Denial: starting Intent {
> act=android.media.action.IMAGE_CAPTURE flg=0x3000003
> pkg=com.google.android.GoogleCamera
> cmp=com.google.android.GoogleCamera/com.android.camera.CaptureActivity
> (has clip) (has extras) } from null (pid=-1, uid=10098) with revoked
> permission android.permission.CAMERA 09-25 21:57:55.263 32657-32657/?
> E/ResolverActivity:     at
> android.os.Parcel.readException(Parcel.java:1599) 09-25 21:57:55.263
> 32657-32657/? E/ResolverActivity:     at
> android.os.Parcel.readException(Parcel.java:1552) 09-25 21:57:55.263
> 32657-32657/? E/ResolverActivity:     at
> android.app.ActivityManagerProxy.startActivityAsCaller(ActivityManagerNative.java:2730)
> 09-25 21:57:55.263 32657-32657/? E/ResolverActivity:     at
> android.app.Instrumentation.execStartActivityAsCaller(Instrumentation.java:1725)
> 09-25 21:57:55.263 32657-32657/? E/ResolverActivity:     at
> android.app.Activity.startActivityAsCaller(Activity.java:4047) 09-25
> 21:57:55.263 32657-32657/? E/ResolverActivity:     at
> com.android.internal.app.ResolverActivity$DisplayResolveInfo.startAsCaller(ResolverActivity.java:983)
> 09-25 21:57:55.263 32657-32657/? E/ResolverActivity:     at
> com.android.internal.app.ResolverActivity.safelyStartActivity(ResolverActivity.java:772)
> 09-25 21:57:55.263 32657-32657/? E/ResolverActivity:     at
> com.android.internal.app.ResolverActivity.onTargetSelected(ResolverActivity.java:754)
> 09-25 21:57:55.263 32657-32657/? E/ResolverActivity:     at
> com.android.internal.app.ChooserActivity.onTargetSelected(ChooserActivity.java:305)
> 09-25 21:57:55.263 32657-32657/? E/ResolverActivity:     at
> com.android.internal.app.ResolverActivity.startSelected(ResolverActivity.java:603)
> 09-25 21:57:55.263 32657-32657/? E/ResolverActivity:     at
> com.android.internal.app.ChooserActivity.startSelected(ChooserActivity.java:310)
> 09-25 21:57:55.263 32657-32657/? E/ResolverActivity:     at
> com.android.internal.app.ChooserActivity$ChooserRowAdapter$2.onClick(ChooserActivity.java:990)
> 09-25 21:57:55.263 32657-32657/? E/ResolverActivity:     at
> android.view.View.performClick(View.java:5198) 09-25 21:57:55.263
> 32657-32657/? E/ResolverActivity:     at
> android.view.View$PerformClick.run(View.java:21147) 09-25 21:57:55.263
> 32657-32657/? E/ResolverActivity:     at
> android.os.Handler.handleCallback(Handler.java:739) 09-25 21:57:55.263
> 32657-32657/? E/ResolverActivity:     at
> android.os.Handler.dispatchMessage(Handler.java:95) 09-25 21:57:55.263
> 32657-32657/? E/ResolverActivity:     at
> android.os.Looper.loop(Looper.java:148) 09-25 21:57:55.263
> 32657-32657/? E/ResolverActivity:     at
> android.app.ActivityThread.main(ActivityThread.java:5417) 09-25
> 21:57:55.263 32657-32657/? E/ResolverActivity:     at
> java.lang.reflect.Method.invoke(Native Method) 09-25 21:57:55.263
> 32657-32657/? E/ResolverActivity:     at
> com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:726)
> 09-25 21:57:55.263 32657-32657/? E/ResolverActivity:     at
> com.android.internal.os.ZygoteInit.main(ZygoteInit.java:616) 09-25
> 21:57:55.286 1159-1159/? I/Keyboard.Facilitator: onFinishInput() 09-25
> 21:57:55.297 32657-32676/? E/Surface: getSlotFromBufferLocked: unknown
> buffer: 0xaec352e0 09-25 21:57:55.344 325-349/? V/RenderScript:
> 0xb3693000 Launching thread(s), CPUs 4 09-25 21:57:57.290 325-349/?
> E/Surface: getSlotFromBufferLocked: unknown buffer: 0xb3f88240

Android M의 알려진 문제입니까? 그리고 더 중요한 것은이 문제를 어떻게 해결해야합니까?

매니페스트에는 다음이 있습니다.

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

이것은 사용자가 카메라로 사진을 클릭하거나 이미지를 선택할 수 있도록하는 데 사용하는 코드입니다.

public static Intent openImageIntent(Context context, Uri cameraOutputFile) {

    // Camera.
    final List<Intent> cameraIntents = new ArrayList<Intent>();
    final Intent captureIntent = new Intent(android.provider.MediaStore.ACTION_IMAGE_CAPTURE);
    final PackageManager packageManager = context.getPackageManager();
    final List<ResolveInfo> listCam = packageManager.queryIntentActivities(captureIntent, 0);
    for(ResolveInfo res : listCam) {
        final String packageName = res.activityInfo.packageName;
        final Intent intent = new Intent(captureIntent);
        intent.setComponent(new ComponentName(res.activityInfo.packageName, res.activityInfo.name));
        intent.setPackage(packageName);
        intent.putExtra(MediaStore.EXTRA_OUTPUT, cameraOutputFile);
        cameraIntents.add(intent);
    }

    // Filesystem.
    final Intent galleryIntent = new Intent();
    galleryIntent.setType("image/*");
    galleryIntent.setAction(Intent.ACTION_GET_CONTENT);

    // Chooser of filesystem options.
    final Intent chooserIntent = Intent.createChooser(galleryIntent, "Take or select pic");

    // Add the camera options.
    chooserIntent.putExtra(Intent.EXTRA_INITIAL_INTENTS, cameraIntents.toArray(new Parcelable[]{}));
    return chooserIntent;
}

openImageIntent()내 활동에서 버튼 클릭을 호출합니다 . 내 앱에 CAMERA 권한이 없으면 제대로 작동하지만 위에 게시 된 예외가 추가됩니다.

    @Override
    public void onClick(View v) {
        Intent picCaptureIntenet = openImageIntent(MainActivity.this, getTempImageFileUri(MainActivity.this));
        try {
            startActivityForResult(picCaptureIntenet, 100);
        } catch(Exception e) {
            Toast.makeText(MainActivity.this, e.getMessage(), Toast.LENGTH_SHORT).show();
        }
    }


동일한 문제가 발생하여 Google에서이 문서를 찾습니다. https://developer.android.com/reference/android/provider/MediaStore.html#ACTION_IMAGE_CAPTURE

"참고 : 앱이 M 이상을 대상으로하고 부여되지 않은 CAMERA 권한을 사용하는 것으로 선언 한 경우이 작업을 사용하려고하면 SecurityException이 발생합니다."

정말 이상 해요. 전혀 이해하지 마십시오. 앱은 IMAGE_CAPTURE 작업이있는 인 텐트를 사용하여 카메라 권한을 선언합니다. 그러나 앱이 IMAGE_CAPTURE 작업이있는 인 텐트를 사용하여 카메라 권한을 선언하지 않으면 문제없이 카메라 앱을 시작할 수 있습니다.

해결 방법은 앱에 매니페스트에 포함 된 카메라 권한이 있는지 확인하는 것입니다. 해당되는 경우 인 텐트를 시작하기 전에 카메라 권한을 요청합니다.

다음은 권한이 부여되었는지 여부와 상관없이 매니페스트에 권한이 포함되어 있는지 확인하는 방법입니다.

public boolean hasPermissionInManifest(Context context, String permissionName) {
    final String packageName = context.getPackageName();
    try {
        final PackageInfo packageInfo = context.getPackageManager()
                .getPackageInfo(packageName, PackageManager.GET_PERMISSIONS);
        final String[] declaredPermisisons = packageInfo.requestedPermissions;
        if (declaredPermisisons != null && declaredPermisisons.length > 0) {
            for (String p : declaredPermisisons) {
                if (p.equals(permissionName)) {
                    return true;
                }
            }
        }
    } catch (NameNotFoundException e) {

    }
    return false;
}

Android M 권한 모델을 사용하는 경우 먼저 앱에 런타임 중에이 권한이 있는지 확인하고 런타임 중에 사용자에게이 권한을 요청해야합니다. 매니페스트에 정의한 권한은 설치시 자동으로 부여되지 않습니다.

if (checkSelfPermission(Manifest.permission.CAMERA)
        != PackageManager.PERMISSION_GRANTED) {

    requestPermissions(new String[]{Manifest.permission.CAMERA},
            MY_REQUEST_CODE);
}

MY_REQUEST_CODE는 정의 할 수있는 정적 상수이며 requestPermission 대화 상자 콜백에 다시 사용됩니다.

대화 결과에 대한 콜백이 필요합니다.

@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
    super.onRequestPermissionsResult(requestCode, permissions, grantResults);
    if (requestCode == MY_REQUEST_CODE) {
        if (grantResults[0] == PackageManager.PERMISSION_GRANTED) {
            // Now user should be able to use camera
        }
        else {
            // Your app will not have this permission. Turn off all functions 
            // that require this permission or it will force close like your 
            // original question
        }
    }
}

편집하다

스택 추적에서 읽어 보니 Google 카메라에 CAMERA 권한이 활성화되지 않은 것 같습니다. 이것은 결국 이전 버전과의 호환성처럼 보일 수 있습니다.

Google 카메라 (또는 ACTION 의도를 처리하는 다른 애플리케이션)에 특정 권한이 필요하다고 가정 해 보겠습니다.

앱에 CAMERA 권한이 없으면 Google 카메라가 이전 권한 모델을 사용하도록하는 것입니다.

그러나 매니페스트에 선언 된 CAMERA 권한과 함께 Android M 권한 모델을 사용하기 위해 Google 카메라 (Android M 권한 모델이 없음) 내에서 CAMERA 권한도 적용됩니다 (제 생각에).

따라서 위의 방법을 사용하면 런타임 중에 앱 권한을 제공해야합니다. 즉, 이제 하위 작업 (이 경우 Google 카메라)도 해당 권한을 갖게됩니다.


'이게 M에서 알려진 문제입니까?'라는 질문까지 Google 개발자가이 문제를 버그로보고하는 사람에게 응답했습니다.

여기를 참조하십시오 : https://code.google.com/p/android/issues/detail?id=188073&q=label%3APriority-Medium&colspec=ID%20Type%20Status%20Owner%20Summary%20Stars&start=100

다음은 Google 직원의 말입니다. "이것은 사용자가 앱에서 카메라 권한을 취소하고 앱이 인 텐트를 통해 사진을 계속 찍을 수있는 사용자의 불만을 피하기위한 것입니다. 사용자는 권한 취소 후 찍은 사진이 다른 메커니즘을 통해 발생한다는 사실을 모르고 권한 모델의 정확성에 의문을 제기합니다. 이는 MediaStore.ACTION_IMAGE_CAPTURE, MediaStore.ACTION_VIDEO_CAPTURE 및 Intent.ACTION_CALL을 대상으로하는 앱의 동작 변경을 문서화하는 문서에 적용됩니다. "

Google은 사용자로부터 카메라를 사용하는 메커니즘을 추상화하는 것을 신경 쓰지 않으므로 카메라 권한에 대한 첫 번째 요청을 전략적으로 트리거하고 요청의 이유로 카메라를 사용하는 활동의 기능을 참조하는 것이 좋습니다. 사용자가 단순히 사진을 찍으려고 할 때 앱에서 먼저이 권한을 요청하도록 허용하면 일반적으로 사진을 찍을 때 권한을 부여 할 필요가 없기 때문에 사용자는 앱이 이상하게 작동한다고 생각할 수 있습니다.


Google M을 사용하는 경우 설정-> 앱-> ->으로 이동하여 적절한 권한을 부여하세요.


나는이 문제에 붙어 있었고 이미 JTY의 대답을 사용하고있었습니다. 문제는 어떤 시점에서 요청 권한 대화 상자가 "다시 묻지 않음"에서 확인되었다는 것입니다. SDK 24에서 개발 중입니다.

권한 (제 경우에는 카메라)을 처리하는 전체 코드는 다음과 같습니다.

public void checksCameraPermission(View view) {
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
        Log.d("MyApp", "SDK >= 23");
        if (this.checkSelfPermission(Manifest.permission.CAMERA)
                != PackageManager.PERMISSION_GRANTED) {
                Log.d("MyApp", "Request permission");
                ActivityCompat.requestPermissions(this,
                        new String[]{Manifest.permission.CAMERA},
                        MY_REQUEST_CODE);

                if (! shouldShowRequestPermissionRationale(Manifest.permission.CAMERA)) {
                    showMessageOKCancel("You need to allow camera usage",
                            new DialogInterface.OnClickListener() {
                                @Override
                                public void onClick(DialogInterface dialog, int which) {
                                    ActivityCompat.requestPermissions(FotoPerfil.this, new String[] {Manifest.permission.CAMERA},
                                            MY_REQUEST_CODE);
                                }
                            });
                }
        }
        else {
            Log.d("MyApp", "Permission granted: taking pic");
            takePicture();
        }
    }
    else {
        Log.d("MyApp", "Android < 6.0");
    }
}

그때

private void showMessageOKCancel(String message, DialogInterface.OnClickListener okListener) {
    new AlertDialog.Builder(this)
            .setMessage(message)
            .setPositiveButton("OK", okListener)
            .setNegativeButton("Cancel", null)
            .create()
            .show();
}

그리고

@Override
public void onRequestPermissionsResult(int requestCode,
                                       String permissions[], int[] grantResults) {
    switch (requestCode) {
        case MY_REQUEST_CODE: {
            if (grantResults.length > 0
                    && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
                criarFoto();
            } else {
                Toast.makeText(this, "You did not allow camera usage :(", Toast.LENGTH_SHORT).show();
                noFotoTaken();
            }
            return;
        }
    }
}

의도 된 동작은 사용자가 실수로 "다시 묻지 않음"을 선택한 경우 앱이 중단되고 (요청 대화 상자가 표시되지 않음) 사용자가 좌절감을 느낄 수 있습니다. 이렇게하면이 권한이 필요하다는 메시지가 나타납니다.


나는 지웠다:

uses-permission android:name="android.permission.CAMERA"

다음에 만 의존했습니다.

uses-feature android:name="android.hardware.camera" android:required="true"

매니페스트 파일에서.


이 방법은 Camera 만 확인하지 않고 시작하는 동안 내 앱에 필요한 모든 권한을 확인합니다 ...이 파일은 Helper.java 파일에 있습니다. 또한 대화 상자에는 https : // github를 사용하고 있습니다. com / afollestad / material-dialogs

  ///check camera permission
    public static boolean hasPermissions(final Activity activity){

        //add your permissions here
        String[] AppPermissions = {
                Manifest.permission.CAMERA,
                Manifest.permission.READ_EXTERNAL_STORAGE,
                Manifest.permission.WRITE_EXTERNAL_STORAGE
        };

        //ungranted permissions
        ArrayList<String> ungrantedPerms = new ArrayList<String>();
        //loop

        //lets set a boolean of hasUngrantedPerm to false
        Boolean needsPermRequest = false;

        //permissionGranted
        int permGranted = PackageManager.PERMISSION_GRANTED;

        //permission required content
        String permRequestStr = activity.getString(R.string.the_following_perm_required);

        //loop
        for(String permission : AppPermissions){

            //check if perm is granted
            int checkPerm = ContextCompat.checkSelfPermission(activity,permission);

            //if the permission is not granted
            if(ContextCompat.checkSelfPermission(activity,permission) != permGranted){

                needsPermRequest = true;

                //add the permission to the ungranted permission list
                ungrantedPerms.add(permission);

                //permssion name
               String[] splitPerm = permission.split(Pattern.quote("."));

                String permName = splitPerm[splitPerm.length-1].concat("\n");

                permRequestStr = permRequestStr.concat(permName);
            }//end if

        }//end loop


        //if all permission is granted end exec
        //then continue code exec
        if(!needsPermRequest) {

            return true;
        }//end if

        //convert array list to array string
       final String[]  ungrantedPermsArray = ungrantedPerms.toArray(new String[ungrantedPerms.size()]);

            //show alert Dialog requesting permission
            new MaterialDialog.Builder(activity)
                    .title(R.string.permission_required)
                    .content(permRequestStr)
                    .positiveText(R.string.enable)
                    .negativeText(R.string.cancel)
                    .onPositive(new MaterialDialog.SingleButtonCallback(){
                        @Override
                        public void onClick(@NonNull MaterialDialog dialog,@NonNull DialogAction which){
                            //request the permission now
                            ActivityCompat.requestPermissions(activity,ungrantedPermsArray,0);
                        }
                    })
                    .show();

        //return false so that code exec in that script will not be allowed
        //to continue
        return false;

    }//end checkPermissions

따라서 여기에서 권한 목록을 추가하거나 제거합니다.

//add your permissions here
            String[] AppPermissions = {
                    Manifest.permission.CAMERA,
                    Manifest.permission.READ_EXTERNAL_STORAGE,
                    Manifest.permission.WRITE_EXTERNAL_STORAGE
            };

내 활동 파일에서 이와 같은 권한을 확인하면 Helper 클래스가 hasPermissions 메서드를 유지하는 곳입니다.

 if(Helper.hasPermissions(this) == false){
            return;
  }//end if

권한이 부여되지 않은 경우 실행을 계속할 필요가 없음을 의미합니다. 다시 권한 요청이 완료된 후 수신해야합니다.이를 수행하려면 아래 코드를 활동 파일에 추가합니다 (선택 사항).

//Listen to Permission request completion
//put in your activity file
@Override
public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults)
{
    int permGranted = PackageManager.PERMISSION_GRANTED;

    Boolean permissionRequired = false;

    for(int perm : grantResults){

        if(perm != permGranted){
            permissionRequired = true;
        }
    }

    //if permission is still required
    if(permissionRequired){

        //recheck and enforce permission again
        Helper.hasPermissions(this);
    }//end if

}//end method

it's a little late. but i want to add one more thing. whenever you call methods which contains camera functionality use it in try catch block. if not app will crash on some devices like Moto G4 plus or one plus.

private static final int CAMERA_REQUEST_CODE = 10;
//TODO add camera opening functionality here.
                try {
                    captureImage();
                    Intent intent = new Intent("android.media.action.IMAGE_CAPTURE");
                    startActivityForResult(intent,CAMERA_REQUEST_CODE);
                } catch (Exception e){
                    e.printStackTrace();
                }

private void captureImage(){
    if( ContextCompat.checkSelfPermission(getContext(), android.Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED) {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
            requestPermissions(new String[]{android.Manifest.permission.CAMERA},
                    CAMERA_REQUEST_CODE);
        }
        else {
            // Open your camera here.
        }
    }
}

@Override
public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
    super.onRequestPermissionsResult(requestCode, permissions, grantResults);
    if (requestCode == CAMERA_REQUEST_CODE) {
        if (grantResults[0] == PackageManager.PERMISSION_GRANTED) {
            // Now user should be able to use camera
        }
        else {
            // Your app will not have this permission. Turn off all functions
            // that require this permission or it will force close like your
            // original question
        }
    }
}

P.S: make sure not to copy paste the overridden method.


You have to enable App permission for Camera usage. I prefer to add this method add command that activate camera :

    public static async Task<bool> HasPermission()
    {
        var status = await CrossPermissions.Current.CheckPermissionStatusAsync(Permission.Camera);
        if (status == PermissionStatus.Granted) return true;

        if (await CrossPermissions.Current.ShouldShowRequestPermissionRationaleAsync(Permission.Camera))
        {
            ShowDialogOk("Error", "Please allow access to the camera.");//that is my custom method for allert
        }

        var results = await CrossPermissions.Current.RequestPermissionsAsync(Permission.Camera);
        status = results[Permission.Camera];

        return status == PermissionStatus.Granted;
    }

참고URL : https://stackoverflow.com/questions/32789027/android-m-camera-intent-permission-bug

반응형