【问题标题】:Can not resolve com.android.camera.action.CROP in android 11 for first time app is installed首次安装应用时无法在 android 11 中解析 com.android.camera.action.CROP
【发布时间】:2021-02-10 18:06:34
【问题描述】:

我开始在 android 11 上使用字符串“com.android.camera.action.CROP”进行裁剪的隐式意图。 首次安装应用程序时无法通过此代码解析其活动。

   Intent intent = new Intent("com.android.camera.action.CROP");


    intent.setType("image/*");

    //to check whether there is an cropping app present or not
    List<ResolveInfo> resInfoList = getPackageManager().queryIntentActivities(
            intent, MATCH_DEFAULT_ONLY);

第一次没有解决,第二次运行它得到可以处理意图的活动。

【问题讨论】:

    标签: java android android-intent crop android-implicit-intent


    【解决方案1】:

    此代码是从相机应用或图库应用捕获或导入图像并继续进行裁剪的代码。

    MainActivity.java

    public class MainActivity extends AppCompatActivity {
    
        public final String APP_TAG = "crop";
    
        public String intermediateName = "1.jpg";
        public String resultName = "2.jpg";
    
        Uri intermediateProvider;
        Uri resultProvider;
    
        ActivityResultLauncher<Intent> cameraActivityResultLauncher;
        ActivityResultLauncher<Intent> galleryActivityResultLauncher;
        ActivityResultLauncher<Intent> cropActivityResultLauncher;
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
    
            Button buttonCamera = findViewById(R.id.buttonCamera);
            buttonCamera.setOnClickListener(new View.OnClickListener() {
                public void onClick(View v) {
                    onLaunchCamera();
                }
            });
    
            Button buttonGallery = findViewById(R.id.buttonGallery);
            buttonGallery.setOnClickListener(new View.OnClickListener() {
                public void onClick(View v) {
                    onPickPhoto();
                }
            });
    
            cameraActivityResultLauncher = registerForActivityResult(
                    new ActivityResultContracts.StartActivityForResult(),
                    result -> {
                        if (result.getResultCode() == Activity.RESULT_OK) {
                            Bitmap takenImage = loadFromUri(intermediateProvider);
                            ImageView ivPreview = findViewById(R.id.originView);
                            ivPreview.setImageBitmap(getResizedBitmap(takenImage, 400));
                            onCropImage();
                        }
                    });
    
            galleryActivityResultLauncher = registerForActivityResult(
                    new ActivityResultContracts.StartActivityForResult(),
                    result -> {
                        if (result.getResultCode() == Activity.RESULT_OK && result.getData() != null) {
                            saveBitmapFileToIntermediate(result.getData().getData());
                            Bitmap selectedImage = loadFromUri(intermediateProvider);
                            ImageView ivPreview = findViewById(R.id.originView);
                            ivPreview.setImageBitmap(getResizedBitmap(selectedImage, 400));
                            onCropImage();
                        }
                    });
    
            cropActivityResultLauncher = registerForActivityResult(
                    new ActivityResultContracts.StartActivityForResult(),
                    result -> {
                        if (result.getResultCode() == Activity.RESULT_OK) {
                            Bitmap cropImage = loadFromUri(resultProvider);
                            ImageView ivPreview = findViewById(R.id.resultView);
                            ivPreview.setImageBitmap(getResizedBitmap(cropImage, 400));
                        }
                    });
    
        }
    
        public void onLaunchCamera() {
            Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
            File photoFile = getPhotoFileUri(intermediateName);
            if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.N)
                intermediateProvider = FileProvider.getUriForFile(MainActivity.this, "com.photostream.crop.fileprovider", photoFile);
            else
                intermediateProvider = Uri.fromFile(photoFile);
            intent.putExtra(MediaStore.EXTRA_OUTPUT, intermediateProvider);
            if (intent.resolveActivity(getPackageManager()) != null) {
                cameraActivityResultLauncher.launch(intent);
            }
        }
    
        // Trigger gallery selection for a photo
        public void onPickPhoto() {
            Intent intent = new Intent(Intent.ACTION_PICK, MediaStore.Images.Media.EXTERNAL_CONTENT_URI);
            if (intent.resolveActivity(getPackageManager()) != null) {
                galleryActivityResultLauncher.launch(intent);
            }
        }
    
        private void onCropImage() {
            if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
                grantUriPermission("com.android.camera", intermediateProvider, Intent.FLAG_GRANT_WRITE_URI_PERMISSION | Intent.FLAG_GRANT_READ_URI_PERMISSION);
                Intent intent = new Intent("com.android.camera.action.CROP");
                intent.setDataAndType(intermediateProvider, "image/*");
    
                List<ResolveInfo> list = getPackageManager().queryIntentActivities(intent, 0);
    
                int size = 0;
    
                if(list != null) {
                    grantUriPermission(list.get(0).activityInfo.packageName, intermediateProvider, Intent.FLAG_GRANT_WRITE_URI_PERMISSION | Intent.FLAG_GRANT_READ_URI_PERMISSION);
                    size = list.size();
                }
    
                if (size == 0) {
                    Toast.makeText(this, "Error, wasn't taken image!", Toast.LENGTH_SHORT).show();
                } else {
                    intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
                    intent.addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
                    intent.putExtra("crop", "true");
    
                    intent.putExtra("scale", true);
    
                    File photoFile = getPhotoFileUri(resultName);
                    // wrap File object into a content provider
                    // required for API >= 24
                    // See https://guides.codepath.com/android/Sharing-Content-with-Intents#sharing-files-with-api-24-or-higher
                    resultProvider = FileProvider.getUriForFile(MainActivity.this, "com.photostream.crop.fileprovider", photoFile);
    
                    intent.putExtra("return-data", false);
                    intent.putExtra(MediaStore.EXTRA_OUTPUT, resultProvider);
                    intent.putExtra("outputFormat", Bitmap.CompressFormat.JPEG.toString());
    
                    Intent cropIntent = new Intent(intent);
                    ResolveInfo res = list.get(0);
                    cropIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
                    cropIntent.addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
                    grantUriPermission(res.activityInfo.packageName, resultProvider, Intent.FLAG_GRANT_WRITE_URI_PERMISSION | Intent.FLAG_GRANT_READ_URI_PERMISSION);
    
                    cropIntent.setComponent(new ComponentName(res.activityInfo.packageName, res.activityInfo.name));
                    cropActivityResultLauncher.launch(cropIntent);
                }
            } else {
                File photoFile = getPhotoFileUri(resultName);
                resultProvider = Uri.fromFile(photoFile);
    
                Intent intentCrop = new Intent("com.android.camera.action.CROP");
                intentCrop.setFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
                intentCrop.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
                intentCrop.setDataAndType(intermediateProvider, "image/*");
                intentCrop.putExtra("crop", "true");
                intentCrop.putExtra("scale", true);
                intentCrop.putExtra("outputFormat", Bitmap.CompressFormat.JPEG.toString());
                intentCrop.putExtra("noFaceDetection", true);
                intentCrop.putExtra("return-data", false);
                intentCrop.putExtra(MediaStore.EXTRA_OUTPUT, resultProvider);
                cropActivityResultLauncher.launch(intentCrop);
            }
        }
    
        // Returns the File for a photo stored on disk given the fileName
        public File getPhotoFileUri(String fileName) {
            File mediaStorageDir = new File(getExternalFilesDir(""), APP_TAG);
            if (!mediaStorageDir.exists() && !mediaStorageDir.mkdirs()){
                Log.d(APP_TAG, "failed to create directory");
            }
            File file = new File(mediaStorageDir.getPath() + File.separator + fileName);
            return file;
        }
    
        public Bitmap loadFromUri(Uri photoUri) {
            Bitmap image = null;
            try {
                if(Build.VERSION.SDK_INT > Build.VERSION_CODES.O_MR1){
                    // on newer versions of Android, use the new decodeBitmap method
                    ImageDecoder.Source source = ImageDecoder.createSource(this.getContentResolver(), photoUri);
                    image = ImageDecoder.decodeBitmap(source);
                } else {
                    // support older versions of Android by using getBitmap
                    image = MediaStore.Images.Media.getBitmap(this.getContentResolver(), photoUri);
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
            return image;
        }
    
        private void saveBitmapFileToIntermediate(Uri sourceUri) {
            try {
                Bitmap bitmap =  loadFromUri(sourceUri);
    
                File imageFile = getPhotoFileUri(intermediateName);
                if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.N)
                    intermediateProvider = FileProvider.getUriForFile(MainActivity.this, "com.photostream.crop.fileprovider", imageFile);
                else
                    intermediateProvider = Uri.fromFile(imageFile);
    
                OutputStream out = new FileOutputStream(imageFile);
                bitmap.compress(Bitmap.CompressFormat.JPEG, 100, out);
                out.close();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    
        public Bitmap getResizedBitmap(Bitmap image, int maxSize) {
            int width = image.getWidth();
            int height = image.getHeight();
    
            float bitmapRatio = (float)width / (float) height;
            if (bitmapRatio > 1) {
                width = maxSize;
                height = (int) (width / bitmapRatio);
            } else {
                height = maxSize;
                width = (int) (height * bitmapRatio);
            }
            return Bitmap.createScaledBitmap(image, width, height, true);
        }
    
    }
    

    AndroidManifest.xml

    <?xml version="1.0" encoding="utf-8"?>
    <manifest xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:tools="http://schemas.android.com/tools"
        package="com.photostream.crop">
        <queries>
            <intent>
                <action android:name="android.media.action.IMAGE_CAPTURE" />
            </intent>
            <intent>
                <action android:name="com.android.camera.action.CROP" />
            </intent>
            <intent>
                <action android:name="android.intent.action.PICK" />
                <data android:mimeType="vnd.android.cursor.dir/image" />
            </intent>
        </queries>
    
        <application
            android:allowBackup="true"
            android:largeHeap="true"
            android:icon="@mipmap/ic_launcher"
            android:label="@string/app_name"
            android:roundIcon="@mipmap/ic_launcher_round"
            android:supportsRtl="true"
            android:theme="@style/Theme.Test">
            <activity
                android:name=".MainActivity"
                android:exported="true">
                <intent-filter>
                    <action android:name="android.intent.action.MAIN" />
    
                    <category android:name="android.intent.category.LAUNCHER" />
                </intent-filter>
            </activity>
    
            <provider
                android:name="androidx.core.content.FileProvider"
                android:authorities="com.photostream.crop.fileprovider"
                android:exported="false"
                android:grantUriPermissions="true">
                <meta-data
                    android:name="android.support.FILE_PROVIDER_PATHS"
                    android:resource="@xml/fileprovider" />
            </provider>
        </application>
    
    </manifest>
    

    它在 Android 11 上运行良好。 您也可以参考 Github 上的关注项目。 https://github.com/bigant02/image-crop

    【讨论】:

      【解决方案2】:

      在我的例子中,我在 AndroidManifest.xml 中使用了以下 &lt;queries&gt; 配置,它可以工作。

      <manifest>
        // ...
        <queries>
            <intent>
                <action android:name="com.android.camera.action.CROP" />
                <data android:scheme="content"
                  android:mimeType="image/*"/>
            </intent>
        </queries>
      </manifest>
      

      resolveActivity

          val intent = Intent("com.android.camera.action.CROP").apply {
              type = "image/*"
              data = photoUri
          }
          if (intent.resolveActivity(context.packageManager) != null) {
              // grant uri permission here
          }
      

      经过一番测试,我发现关键是queries中的android:mimeType必须与Intent中设置的type对齐。

      【讨论】:

      • 结合 grantUriPermission() 就可以了。
      • 是否需要设置 scheme="content" ?那是什么意思?因为在这种情况下:动作作物,不需要那个。
      • 我找到了我的问题的答案。在这种情况下,设置 scheme = content 是正确的。如果没有它,则假定 content: 和 file: 方案
      【解决方案3】:

      我假设您以 Android 11 (API 30) 为目标,这要求您在 AndroidManifest.xml 内的 queries 节点中指定外部应用程序的所有意图,如下所示:

      <queries>
          ...
          <intent>
              <action android:name="com.android.camera.action.CROP" />
          </intent>
          ...
      </queries>
      

      阅读更多 herehere。还有一个关于此事的中等帖子here

      注意com.android.camera.action.CROP 的意图是基于 AOSP 的相机应用程序,该应用程序可能在某些设备上丢失,请参阅 Commonsware 的旧博文:https://commonsware.com/blog/2013/01/23/no-android-does-not-have-crop-intent.html

      【讨论】:

      • 这确实很奇怪。您能否确认您的 AndroidManifest 中有这些条目?
      • targetSdkVersion 为 30
      • 在给定行上方插入,但问题仍然存在。
      • 行为发生了变化,在应用程序打开谷歌照片以查看图库之前,裁剪活动将无法解决。
      猜你喜欢
      • 2020-05-21
      • 2020-08-06
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-11-26
      • 1970-01-01
      • 1970-01-01
      • 2011-12-15
      相关资源
      最近更新 更多