Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Share Assets Using fileprovider

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

How to share the files in assets folder(instead of internal directory) to the client app.

Thanks

like image 973
user207064 Avatar asked Jul 22 '15 06:07

user207064


People also ask

What is the use of FileProvider?

FileProvider is a special subclass of ContentProvider that facilitates secure sharing of files associated with an app by creating a content:// Uri for a file instead of a file:/// Uri . A content URI allows you to grant read and write access using temporary access permissions.

How do I use FileProvider on Android?

To make FileProvider work follow these three steps: Define the FileProvider in your AndroidManifest file. Create an XML file that contains all paths that the FileProvider will share with other applications. Bundle a valid URI in the Intent and activate it.


2 Answers

See CWAC-Provider from CommonsWare which is a library to do precisely what you want.

like image 158
zmarties Avatar answered Sep 20 '22 23:09

zmarties


This is the way i used finally, hope this will help 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 onCreate() to get assets list and return uriArray to 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's 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 Assets Provider Class, I have not implemented query, update etc... as these are not necessary 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 compression for assets files (these are the types of files i had in assets)

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

Following in the Consumer activity

In onCreate() -- setPackage() is required since we want send ACTION_PICK to 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 Override method onActivityResult()

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's 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 the 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 Consumer manifest file

<uses-permission android:name="yourpermission" />
like image 42
user207064 Avatar answered Sep 19 '22 23:09

user207064