Sharing assets with fileprovider

Further https://developer.android.com/training/secure-file-sharing/index.html and the ability to share files in the internal directory (/ data / data / package / files / xxx /) of the application for the client application using fileprovider ...

How to share files in a resource folder (instead of an internal directory) with a client application.

thank

+3


source to share


2 answers


See CWAC-Provider from CommonsWare which is a library that does exactly what you want.



+3


source


This is how I finally used it, hope it helps someone. Added provider in manifest file

        <provider
        android:name=".AssetsProvider"
        android:authorities="yourpackage.provider"
        android:exported="true"
        android:grantUriPermissions="true"
        android:readPermission="yourpermission"></provider>

    <activity
        android:name=".MainActivity"
        android:label="@string/app_name"
        android:screenOrientation="landscape">
        <intent-filter>
            <action android:name="android.intent.action.PICK" />

            <category android:name="android.intent.category.DEFAULT" />
            <category android:name="android.intent.category.OPENABLE" />

            <data android:mimeType="application/octet-stream" />
        </intent-filter>
    </activity>

      

Following inProvider Activity's onCreate () to get a list of assets and return uriArray for the caller (Consumer App)

    String[] assetFilesList = null;
    // Get Asset Mangaer
    AssetManager assetManager = getAssets();
    try {
        assetFilesList = assetManager.list();
    } catch (IOException e) {
        Log.e(TAG, Log.getStackTraceString(e));
    }
    // Set up an Intent to send back to apps that request files
    mResultIntent = new Intent("yourpackage.ACTION_SEND_MULTIPLE");
    // new Uri list
    ArrayList<Uri> uriArrayList = new ArrayList<>();
    // Set the Activity result to null to begin with
    setResult(Activity.RESULT_CANCELED, null);

    Uri fileUri;
    if (assetFilesList != null) {
        for (String currFile : assetFilesList) {
            Log.i(TAG, "Adding File " + currFile);
            // parse and create uri
            fileUri = Uri.parse("content://" + this.getPackageName() + ".provider/" + currFile);
            // add current file uri to the list
            uriArrayList.add(fileUri);
        }
    }
    else {
        Log.e(TAG, "files array is pointing to null");
    }

    if (uriArrayList.size() != 0) {
        // Put the UriList Intent
        mResultIntent.putParcelableArrayListExtra(Intent.EXTRA_STREAM, uriArrayList);
        mResultIntent.setType("application/octet-stream");
        // Set the result
        this.setResult(Activity.RESULT_OK, mResultIntent);
    } else {
        // Set the result to failed
        mResultIntent.setDataAndType(null, "");
        this.setResult(RESULT_CANCELED, mResultIntent);
    }
    // Finish Activity and return Result to Caller
    finish();

      

My asset provider class, I have not implemented query, update, etc. as they are not needed for my case.

public class AssetsProvider extends ContentProvider {
static final String TAG = "AssetsProvider";

@Override
public AssetFileDescriptor openAssetFile(Uri uri, String mode) throws FileNotFoundException {
    Log.v(TAG, "AssetsGetter: Open asset file " + uri.toString());
    AssetManager am = getContext().getAssets();
    String file_name = uri.getLastPathSegment();
    if (file_name == null)
        throw new FileNotFoundException();
    AssetFileDescriptor afd = null;
    try {
        afd = am.openFd(file_name);
    } catch (IOException e) {
        Log.e(TAG, Log.getStackTraceString(e));
    }
    return afd;
}

@Override
public String getType(Uri p1) {
    // TODO: Implement this method
    return null;
}

@Override
public int delete(Uri p1, String p2, String[] p3) {
    // TODO: Implement this method
    return 0;
}

@Override
public Cursor query(Uri p1, String[] p2, String p3, String[] p4, String p5) {
    // TODO: Implement this method
    return null;
}

@Override
public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder, CancellationSignal cancellationSignal) {
    // TODO: Implement this method
    return super.query(uri, projection, selection, selectionArgs, sortOrder, cancellationSignal);
}

@Override
public Uri insert(Uri p1, ContentValues p2) {
    // TODO: Implement this method
    return null;
}

@Override
public boolean onCreate() {
    // TODO: Implement this method
    return false;
}

@Override
public int update(Uri p1, ContentValues p2, String p3, String[] p4) {
    // TODO: Implement this method
    return 0;
}
}

      

Gradle build options to avoid compressing asset files (these are the file types I had in assets)

aaptOptions {
    noCompress '.json' , '.xls'
}

      

Following in user activity



In onCreate () - setPackage () is required since we want to send ACTION_PICK to a specific application

        Intent mRequestFileIntent = new Intent(Intent.ACTION_PICK);
        mRequestFileIntent.setPackage("yourAssetsProviderpackage");
        mRequestFileIntent.setType("application/octet-stream");
        try {
            startActivityForResult(mRequestFileIntent, 0);
        } catch (Exception e) {
            e.printStackTrace();
            Toast.makeText(this, "Install Assets Provider app before start", Toast.LENGTH_LONG).show();
            finish();
        }

      

Added onActivityResult () override method

public void onActivityResult(int requestCode, int resultCode,
                             Intent returnIntent) {
    // If the selection didn't work
    if (resultCode != Activity.RESULT_OK) {
        // Exit without doing anything else
        Log.e(TAG, "Activity returned fail");
    } else {
        // get array list
        ArrayList<Uri> uriArrayList = returnIntent.getParcelableArrayListExtra(Intent.EXTRA_STREAM);
        // create directory in internal storage to store the assets from uri list
        String toPath = this.getFilesDir().getPath();
        if (uriArrayList != null) {
            AssetFileDescriptor mInputAFD;
            for (int i = 0; i < uriArrayList.size(); i++) {
                // Get the file content URI
                Uri returnUri = uriArrayList.get(i);
                try {
                    mInputAFD = getContentResolver().openAssetFileDescriptor(returnUri, "r");
                    // Get file name
                    String fileName = returnUri.getLastPathSegment();
                    Log.i(TAG, "URI " + returnUri.toString() + " fileName " + fileName);
                    // Create dest filename and copy
                    File dest = new File(toPath + "/" + fileName);
                    copyRaw(mInputAFD, dest);
                } catch (Exception e) {
                    Log.e(TAG, Log.getStackTraceString(e));
                    // Break loop at first exception
                    break;
                }
            }
        } 
    }
}

      

CopyRaw method to copy file using AssetFileDescriptor

public void copyRaw(AssetFileDescriptor fd, File destinationFile) throws IOException {

    FileChannel sourceChannel = new FileInputStream(fd.getFileDescriptor()).getChannel();
    FileChannel destinationChannel = new FileOutputStream(destinationFile).getChannel();

    sourceChannel.transferTo(fd.getStartOffset(), fd.getLength(), destinationChannel);
}

      

Add permission in client manifest file

<uses-permission android:name="yourpermission" />

      

0


source







All Articles