Diğer uygulamalara içerik paylaşmak için destek FileProvider nasıl kullanılır?


142

Android Destek kitaplığının FileProvider'ı kullanarak dahili bir dosyayı harici uygulama ile doğru bir şekilde (AÇIK değil) paylaşmak için bir yol arıyorum .

Dokümanlardaki örneği takip ederek,

<provider
    android:name="android.support.v4.content.FileProvider"
    android:authorities="com.example.android.supportv4.my_files"
    android:grantUriPermissions="true"
    android:exported="false">
    <meta-data
        android:name="android.support.FILE_PROVIDER_PATHS"
        android:resource="@xml/my_paths" />
</provider>

ve bir dosyayı aşağıdaki gibi diğer uygulamalarla paylaşmak için ShareCompat'ı kullanma:

ShareCompat.IntentBuilder.from(activity)
.setStream(uri) // uri from FileProvider
.setType("text/html")
.getIntent()
.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)

çalışmıyor, çünkü FLAG_GRANT_READ_URI_PERMISSION ekstraın datadeğerini EXTRA_STREAM(tarafından ayarlandığı gibi setStream) değil, yalnızca niyette belirtilen Uri için izin verir .

Ben ayarlayarak uzlaşı güvenlik çalıştı android:exportedüzere truesağlayıcı, ama FileProviderkendisi ihraç eğer içten çekler, bu nedenle zaman, bir istisna atar.


7
+1 bu gerçekten orijinal bir soru gibi görünüyor - Google veya StackOverflow'da söyleyebildiğim kadarıyla hiçbir şey yok .
Phil

İşte asgari bir github örnek projesi kullanarak temel bir FileProvider kurulumunu almak için bir gönderi: stackoverflow.com/a/48103567/2162226 . Bu dosyalar yerel bir bağımsız projeye (değişiklik yapmadan) kopyalamak için hangi adımları sağlar
Gen Bo

Yanıtlar:


159

FileProviderDestek kitaplığından kullanarak , diğer uygulamaların belirli Uri'yi okuması için izinleri (çalışma zamanında) manuel olarak vermeniz ve iptal etmeniz gerekir. Context.grantUriPermission ve Context.revokeUriPermission yöntemlerini kullanın .

Örneğin:

//grant permision for app with package "packegeName", eg. before starting other app via intent
context.grantUriPermission(packageName, uri, Intent.FLAG_GRANT_WRITE_URI_PERMISSION | Intent.FLAG_GRANT_READ_URI_PERMISSION);

//revoke permisions
context.revokeUriPermission(uri, Intent.FLAG_GRANT_WRITE_URI_PERMISSION | Intent.FLAG_GRANT_READ_URI_PERMISSION);

Son çare olarak, paket adı sağlayamazsanız, belirli bir amacı ele alabilecek tüm uygulamalara izin verebilirsiniz:

//grant permisions for all apps that can handle given intent
Intent intent = new Intent();
intent.setAction(Intent.ACTION_SEND);
...
List<ResolveInfo> resInfoList = context.getPackageManager().queryIntentActivities(intent, PackageManager.MATCH_DEFAULT_ONLY);
for (ResolveInfo resolveInfo : resInfoList) {
    String packageName = resolveInfo.activityInfo.packageName;
    context.grantUriPermission(packageName, uri, Intent.FLAG_GRANT_WRITE_URI_PERMISSION | Intent.FLAG_GRANT_READ_URI_PERMISSION);
}

Belgelere göre alternatif yöntem :

  • SetData () öğesini çağırarak içerik URI'sini bir Niyet'e yerleştirin.
  • Ardından, FLAG_GRANT_READ_URI_PERMISSION veya FLAG_GRANT_WRITE_URI_PERMISSION veya her ikisiyle Intent.setFlags () yöntemini çağırın.
  • Son olarak, Niyeti başka bir uygulamaya gönderin. Çoğu zaman bunu setResult () öğesini çağırarak yaparsınız.

    Bir Niyette verilen izinler, alıcı Etkinliğin yığını etkinken yürürlükte kalır. Yığın tamamlandığında,
    izinler otomatik olarak kaldırılır.
    Bir istemci uygulamasında bir Etkinliğe verilen izinler otomatik
    olarak o uygulamanın diğer bileşenlerine genişletilir .

Btw. gerekiyorsa , FileProvider kaynağını kopyalayabilir ve attachInfosağlayıcının dışa aktarılıp aktarılmadığını kontrol etmesini önlemek için yöntemi değiştirebilirsiniz .


26
grantUriPermissionYöntemi kullanarak izin vermek istediğimiz uygulamanın paket adını vermeliyiz. Ancak, paylaşırken genellikle paylaşım hedefinin hangi uygulama olduğunu bilmiyoruz.
Randy Sugianto 'Yuku'

Paket adını bilmiyor ve sadece diğer uygulamaların açabilmesini istiyorsak
StuStirling

3
En son çözüm @Lecho için lütfen bir çalışma kodu sağlayabilir misiniz?
StErMi

2
FileProvider desteğinin setFlags ile neden çalışmadığını, ancak grantUriPermission ile çalıştığını izleyen bir hata açık izleme var mı? Yoksa bu bir hata değil, bu durumda bu nasıl bir hata değil?
nmr

1
Buna ek olarak, grantUriPermission çağrısının ShareCompat kullanılarak oluşturulan bir niyet için çalışmadığını, ancak Intent'i manuel olarak oluştururken çalıştığını gördüm.
StuStirling

33

Tamamen çalışan kod örneği nasıl iç uygulama klasöründen dosya paylaşmak için. Android 7 ve Android 5'te test edildi.

AndroidManifest.xml

</application>
   ....
    <provider
        android:name="androidx.core.content.FileProvider"
        android:authorities="android.getqardio.com.gmslocationtest"
        android:exported="false"
        android:grantUriPermissions="true">
        <meta-data
            android:name="android.support.FILE_PROVIDER_PATHS"
            android:resource="@xml/provider_paths"/>
    </provider>
</application>

xml / provider_paths

<?xml version="1.0" encoding="utf-8"?>
<paths>
    <files-path
        name="share"
        path="external_files"/>
</paths>

Kodlama

    File imagePath = new File(getFilesDir(), "external_files");
    imagePath.mkdir();
    File imageFile = new File(imagePath.getPath(), "test.jpg");

    // Write data in your file

    Uri uri = FileProvider.getUriForFile(this, getPackageName(), imageFile);

    Intent intent = ShareCompat.IntentBuilder.from(this)
                .setStream(uri) // uri from FileProvider
                .setType("text/html")
                .getIntent()
                .setAction(Intent.ACTION_VIEW) //Change if needed
                .setDataAndType(uri, "image/*")
                .addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);

   startActivity(intent);

1
Ve ShareCompat.IntentBuilderokuma URI izni kullanımı sonunda benim için yaptı.
Kordon Rehn

Android'in diğer sürümleri için
Oliver Dixon

@OliverDixon: hangileri? Android 6.0 ve 9.0'da test ettim.
Menekşe Zürafa

1
Bu FileProviderişe yarayan tek cevap budur. Diğer cevaplar niyetin yanlış kullanılmasını sağlar (kullanmazlar ShareCompat.IntentBuilder) ve bu niyet harici uygulamalar tarafından anlamlı bir şekilde açılamaz. Sonunda cevabınızı bulana kadar günün çoğunu bu saçmalıkta geçirdim.
Menekşe Zürafa

1
@VioletGiraffe Ben de ShareCompat kullanmıyorum ama benim çalışır. Benim sorunum, seçicimin amacına okuma izinlerini vermemdi, seçicinin amacına ve seçiciye aktarılmadan ÖNCE amacım için okuma izinleri vermem gerektiğinde. Umarım bu mantıklıdır. Doğru niyet için okuma izni verdiğinizden emin olun.
Ryan

23

Bu çözüm OS 4.4'ten beri benim için çalışıyor. Tüm cihazlarda çalışmasını sağlamak için eski cihazlar için bir geçici çözüm ekledim. Bu, daima en güvenli çözümün kullanılmasını sağlar.

Manifest.xml:

    <provider
        android:name="androidx.core.content.FileProvider"
        android:authorities="com.package.name.fileprovider"
        android:exported="false"
        android:grantUriPermissions="true">
        <meta-data
            android:name="android.support.FILE_PROVIDER_PATHS"
            android:resource="@xml/file_paths" />
    </provider>

file_paths.xml:

<paths>
    <files-path name="app_directory" path="directory/"/>
</paths>

Java:

public static void sendFile(Context context) {
    Intent intent = new Intent(Intent.ACTION_SEND);
    intent.setType("text/plain");
    String dirpath = context.getFilesDir() + File.separator + "directory";
    File file = new File(dirpath + File.separator + "file.txt");
    Uri uri = FileProvider.getUriForFile(context, "com.package.name.fileprovider", file);
    intent.putExtra(Intent.EXTRA_STREAM, uri);
    intent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
    // Workaround for Android bug.
    // grantUriPermission also needed for KITKAT,
    // see https://code.google.com/p/android/issues/detail?id=76683
    if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.KITKAT) {
        List<ResolveInfo> resInfoList = context.getPackageManager().queryIntentActivities(intent, PackageManager.MATCH_DEFAULT_ONLY);
        for (ResolveInfo resolveInfo : resInfoList) {
            String packageName = resolveInfo.activityInfo.packageName;
            context.grantUriPermission(packageName, attachmentUri, Intent.FLAG_GRANT_READ_URI_PERMISSION);
        }
    }
    if (intent.resolveActivity(context.getPackageManager()) != null) {
        context.startActivity(intent);
    }
}

public static void revokeFileReadPermission(Context context) {
    if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.KITKAT) {
        String dirpath = context.getFilesDir() + File.separator + "directory";
        File file = new File(dirpath + File.separator + "file.txt");
        Uri uri = FileProvider.getUriForFile(context, "com.package.name.fileprovider", file);
        context.revokeUriPermission(uri, Intent.FLAG_GRANT_READ_URI_PERMISSION);
    }
}

İzin, Parçanın veya Etkinliğin onResume ve onDestroy () yöntemlerinde revokeFileReadPermission () ile iptal edilir.


1
Bu çözüm benim için çalıştı ancak ben intent.setDataAndType (uri, "video / *") ekledikten sonra;
UI

Eğer bunu mu demek istiyorsunuz filedan değişmeyecek onResumeiçin onDestroy?
CoolMind

16

Phil'in orijinal soru hakkındaki yorumunda söylediği gibi, bu benzersizdir ve Google'da SO hakkında başka bir bilgi yoktur, ayrıca sonuçlarımı da paylaşmam gerektiğini düşündüm:

Uygulamamda FileProvider, paylaşım amacını kullanarak dosyaları paylaşmak için kutunun dışında çalıştı. FileProvider'ı kurmak için bunun dışında özel bir yapılandırma veya kod gerekli değildi. Benim manifest.xml dosyasında:

    <provider
        android:name="android.support.v4.content.FileProvider"
        android:authorities="com.my.apps.package.files"
        android:exported="false"
        android:grantUriPermissions="true" >
        <meta-data
            android:name="android.support.FILE_PROVIDER_PATHS"
            android:resource="@xml/my_paths" />
    </provider>

My_paths.xml dosyasında var:

<paths xmlns:android="http://schemas.android.com/apk/res/android">
    <files-path name="files" path="." />
</paths>

Kodumda var:

    Intent shareIntent = new Intent();
    shareIntent.setAction(Intent.ACTION_SEND);
    shareIntent.setType("application/xml");

    Uri uri = FileProvider.getUriForFile(this, "com.my.apps.package.files", fileToShare);
    shareIntent.putExtra(Intent.EXTRA_STREAM, uri);

    startActivity(Intent.createChooser(shareIntent, getResources().getText(R.string.share_file)));

Ayrıca, uygulamalar özel depolama alanındaki dosya depomu Gmail ve google drive gibi uygulamalarla sorunsuz bir şekilde paylaşabilirim.


9
Ne yazık ki bu işe yaramıyor. Bu değişken fileToShare yalnızca dosya bir SD kartta veya harici dosya sisteminde varsa çalışır. FileProvider'ı kullanmanın amacı, dahili sistemden içerik paylaşabilmenizdir.
Keith Connolly

Bu, yerel depolama dosyalarıyla (Android 5+) düzgün çalışıyor. Ama Android 4'te sorunlar
alıyorum

15

Bu sadece Android'in yeni sürümlerinde çalışacağını söyleyebildiğim kadarıyla, muhtemelen bunu yapmak için farklı bir yol bulmanız gerekecek. Bu çözüm benim için 4.4'te çalışıyor, ancak 4.0 veya 2.3.3'te çalışmıyor, bu nedenle bu, herhangi bir Android cihazında çalışması amaçlanan bir uygulama için içerik paylaşma konusunda kullanışlı bir yol olmayacak.

Manifest.xml dosyasında:

<provider
    android:name="android.support.v4.content.FileProvider"
    android:authorities="com.mydomain.myapp.SharingActivity"
    android:exported="false"
    android:grantUriPermissions="true">
    <meta-data
        android:name="android.support.FILE_PROVIDER_PATHS"
        android:resource="@xml/file_paths" />
</provider>

Yetkilileri nasıl belirlediğinize dikkat edin. URI'yi oluşturacağınız ve paylaşım amacını başlatacağınız etkinliği belirtmelisiniz, bu durumda etkinliğe SharingActivity denir. Bu gereksinim Google'ın dokümanlarında belli değil!

file_paths.xml:

<?xml version="1.0" encoding="utf-8"?>
<paths xmlns:android="http://schemas.android.com/apk/res/android">
    <files-path name="just_a_name" path=""/>
</paths>

Yolu nasıl belirlediğinize dikkat edin. Yukarıdaki varsayılan, özel dahili depolama alanınızın kök dizinidir.

SharingActivity.java içinde:

Uri contentUri = FileProvider.getUriForFile(getActivity(),
"com.mydomain.myapp.SharingActivity", myFile);
Intent shareIntent = new Intent();
shareIntent.setAction(Intent.ACTION_SEND);
shareIntent.setType("image/jpeg");
shareIntent.putExtra(Intent.EXTRA_STREAM, contentUri);
shareIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
startActivity(Intent.createChooser(shareIntent, "Share with"));

Bu örnekte bir JPEG resmi paylaşıyoruz.

Son olarak, dosyayı düzgün bir şekilde kaydettiğinizden ve buna benzer bir şeyle erişebildiğinizden emin olmak muhtemelen iyi bir fikirdir:

File myFile = getActivity().getFileStreamPath("mySavedImage.jpeg");
if(myFile != null){
    Log.d(TAG, "File found, file description: "+myFile.toString());
}else{
    Log.w(TAG, "File not found!");
}

8

Uygulamamda FileProvider gayet iyi çalışıyor ve Gmail, Yahoo vb. E-posta istemcilerine dosya dizininde depolanan dahili dosyaları ekleyebiliyorum.

Android dokümanlarında belirtildiği gibi manifestoma yerleştirdim:

<provider
        android:name="android.support.v4.content.FileProvider"
        android:authorities="com.package.name.fileprovider"
        android:grantUriPermissions="true"
        android:exported="false">
        <meta-data
            android:name="android.support.FILE_PROVIDER_PATHS"
            android:resource="@xml/filepaths" />
    </provider>

Dosyalarım kök dosyalar dizininde depolandıkça, filepaths.xml dosyası şöyle:

 <paths>
<files-path path="." name="name" />

Şimdi kodda:

 File file=new File(context.getFilesDir(),"test.txt");

 Intent shareIntent = new Intent(android.content.Intent.ACTION_SEND_MULTIPLE);

 shareIntent.putExtra(android.content.Intent.EXTRA_SUBJECT,
                                     "Test");

 shareIntent.setType("text/plain");

 shareIntent.putExtra(android.content.Intent.EXTRA_EMAIL,
                                 new String[] {"email-address you want to send the file to"});

   Uri uri = FileProvider.getUriForFile(context,"com.package.name.fileprovider",
                                                   file);

                ArrayList<Uri> uris = new ArrayList<Uri>();
                uris.add(uri);

                shareIntent .putParcelableArrayListExtra(Intent.EXTRA_STREAM,
                                                        uris);


                try {
                   context.startActivity(Intent.createChooser(shareIntent , "Email:").addFlags(Intent.FLAG_ACTIVITY_NEW_TASK));                                                      


                }
                catch(ActivityNotFoundException e) {
                    Toast.makeText(context,
                                   "Sorry No email Application was found",
                                   Toast.LENGTH_SHORT).show();
                }
            }

Bu benim için çalıştı.


1
Yollar göndermek için gerçek bir MVP = "." Benim için çalıştı
robsstackoverflow

Im "gibi içeren yapılandırılmış kök bulunamadı /" hata alıyorum Aynı kodu kullandım
Rakshith Kumar

5

Kameradan bir görüntü alırsanız, bu çözümlerin hiçbiri Android 4.4 için çalışmaz. Bu durumda sürümleri kontrol etmek daha iyidir.

Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
if (intent.resolveActivity(getContext().getPackageManager()) != null) {
    if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {
        uri = Uri.fromFile(file);
    } else {
        uri = FileProvider.getUriForFile(getContext(), getContext().getPackageName() + ".provider", file);
    }
    intent.putExtra(MediaStore.EXTRA_OUTPUT, uri);
    startActivityForResult(intent, CAMERA_REQUEST);
}

1

sadece yukarıda verilen cevabı geliştirmek için: NullPointerEx alıyorsanız:

bağlam olmadan getApplicationContext () yöntemini de kullanabilirsiniz

                List<ResolveInfo> resInfoList = getPackageManager().queryIntentActivities(takePictureIntent, PackageManager.MATCH_DEFAULT_ONLY);
                for (ResolveInfo resolveInfo : resInfoList) {
                    String packageName = resolveInfo.activityInfo.packageName;
                    grantUriPermission(packageName, photoURI, Intent.FLAG_GRANT_WRITE_URI_PERMISSION | Intent.FLAG_GRANT_READ_URI_PERMISSION);
                }

1

Bizi birkaç gün engelleyen bir şey paylaşmak istiyorum: dosya sağlayıcı kodu uygulama etiketleri arasına eklenmeli ZORUNLU değil. Önemsiz olabilir, ama asla belirtilmedi ve birisine yardım edebileceğimi düşündüm! (piolo94'e tekrar teşekkürler)

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.