1

I can't share audio file from assets. Each app says that it can't send the file.

Method for converting inputstream to temporary file

    public File getFile(String Prefix, String Suffix) throws IOException {

    File tempFile = File.createTempFile(Prefix, Suffix);
    AssetFileDescriptor tempafd = FXActivity.getInstance().getAssets().openFd(filepath);
    tempFile.deleteOnExit();
    FileOutputStream out = new FileOutputStream(tempFile);
    IOUtils.copy(tempafd.createInputStream(), out);


    return tempFile;
}

Sharing file

        item2.setOnAction(n ->{
            try {
                Uri uri = Uri.fromFile(tekst.getFile(tekst.getFilename(), ".mp3"));
                Intent share = new Intent();
                share.setType("audio/*");
                share.setAction(Intent.ACTION_SEND);
                share.putExtra(Intent.EXTRA_STREAM, uri);
                FXActivity.getInstance().startActivity(share);
            } catch (IOException ex) {
                Logger.getLogger(MainCategoryCreator.class.getName()).log(Level.SEVERE, null, ex);
            }

        });
siemaeniu500
  • 115
  • 1
  • 9
  • Can u check tekst.getFilename().mp3 file exists or not. I mean your temp file exists or not and check the size of the file to confirm its not corrupted? – Raghavendra Nov 18 '16 at 07:33
  • Yes it exists because it have the same size that the original one from assets. – siemaeniu500 Nov 18 '16 at 07:38
  • @Raghavendra so what can cause that ? – siemaeniu500 Nov 18 '16 at 08:07
  • Can u tell me where u r creating the temp file? Can u post the sample path? – Raghavendra Nov 18 '16 at 08:43
  • Have you checked this [answer](http://stackoverflow.com/a/21389696/3956070)? I'm not sure if creating the temporal file in an external public folder could work. – José Pereda Nov 18 '16 at 09:14
  • I dont place temp file in any directory. I have to rather first import assets to external storage. – siemaeniu500 Nov 18 '16 at 10:35
  • 1
    I've tested with an audio file placed in the app assets folder, and the temp file is created in the local private path of the app. Print the Uri and you will see. That can't be accessed externally. – José Pereda Nov 18 '16 at 10:45
  • So i have to place temp file in external directory – siemaeniu500 Nov 18 '16 at 11:05
  • Yes and no: Not necessarily in the external storage, but at least into your internal files dir (via `Context.getFilesDir()`). From there, there are sophisticated means in the Android world to share your content. See my answer below. – dzim Dec 02 '16 at 12:51

1 Answers1

0

Just as it happens, I struggled with almost an identical problem: I needed to share a video file. The problem is: There is now way to share an internal file. Never. You either need a ContentProvider, or since it's a bit simpler with it, it's extension FileProvider.

First: you need to update you AndroidManifest.xml:

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

This needs to be added into the <application>tag.

Then you need the XML file file_paths.xml in the Android sub-directory res/xml/

It should something like this:

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

And to finally trigger it, I needed to call it like this:

Uri uri = Uri.parse("content://my.package.fileprovider/" + fn); 
Intent intent = new Intent(Intent.ACTION_VIEW, uri); // or parse uri each time
intent.setDataAndType(uri, "video/*"); // all video type == * - alternative: mp4, ...
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_SINGLE_TOP | Intent.FLAG_GRANT_READ_URI_PERMISSION);
List<ResolveInfo> resInfoList = FXActivity.getInstance().getPackageManager().queryIntentActivities(intent, PackageManager.MATCH_DEFAULT_ONLY);
for (ResolveInfo resolveInfo : resInfoList) {
    String packageName = resolveInfo.activityInfo.packageName;
    FXActivity.getInstance().grantUriPermission(packageName, uri, Intent.FLAG_GRANT_READ_URI_PERMISSION);
}
FXActivity.getInstance().startActivity(intent);

But what I needed to do prior to be able to use it like this: I needed to copy all assets to the private files dir, because the FileProvider itself has no option to access your assets (I guess you could achieve this with a custom ContentProvider, but I found way to complicated and didn't have that much time).

See this Android Developer reference on FileProvider for mor information.

My simple solution for this looks like this:

public boolean copyAssetsToStorage() throws NativeServiceException {
    try {
        String[] assets = getContext().getAssets().list(DIR_NAME);
        if (assets == null || assets.length == 0) {
            LOG.warning("No assets found in '" + DIR_NAME + "'!");
            return false;
        }
        File filesDir = getContext().getFilesDir();
        File targetDir = new File(filesDir, DIR_NAME);
        if (!targetDir.isDirectory()) {
            boolean b = targetDir.mkdir();
            if (!b) {
                LOG.warning("could not create private directory with the name '" + DIR_NAME + "'!");
                return false;
            }
        }
        for (String asset : assets) {
            File targetFile = new File(targetDir, asset);
            if (targetFile.isFile()) {
                LOG.info("Asset " + asset + " already present. Nothing to do.");
                continue;
            } else {
                LOG.info("Copying asset " + asset + " to private files.");
            }
            InputStream is = null;
            OutputStream os = null;
            try {
                is = getContext().getAssets().open(DIR_NAME + "/" + asset);
                os = new FileOutputStream(targetFile.getAbsolutePath());
                byte[] buff = new byte[1024];
                int len;
                while ((len = is.read(buff)) > 0)
                    os.write(buff, 0, len);
            } catch (IOException e) {
                LOG.log(Level.SEVERE, e.getMessage(), e);
                continue;
            }
            if (os != null) {
                os.flush();
                os.close();
            }
            if (is != null)
                is.close();
        }
        return true;
    } catch (IOException e) {
        LOG.log(Level.SEVERE, e.getMessage(), e);
        return false;
    }
}

As you can see, I only support a flat hierarchy for now...

This at least did the trick for me.

Regards, Daniel


ADDIDIONAL QUESTION: Why are you sending an Intent and not implement a simple JavaFX audio player control? This is what I did prior to the Video stuff.

dzim
  • 1,131
  • 2
  • 12
  • 28