Share Assets Using fileprovider
Asked Answered
L

2

6

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

Leopoldine answered 22/7, 2015 at 6:44 Comment(0)
L
3

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

Lowery answered 22/7, 2015 at 8:48 Comment(1)
Looks cool but unfortunately it is about to disappear from the Internet :(Oversexed
L
2

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" />
Leopoldine answered 23/7, 2015 at 11:26 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.