• After 15+ years, we've made a big change: Android Forums is now Early Bird Club. Learn more here.

Problem with sharing a file via Intent with another app as a FileProvider

Hi everybody. I'm having a problem that I can't get to the bottom of, while trying to share a file by becoming a FileProvider in my app. I think that my issue is coming from an incorrect provider authority, but I'm not sure. This is definitely my first time with working with these authorities, or any sort of Intents other than opening a new activity, so I'm in virgin territory here. Googling isn't turning up much for me, and the error message that I'm receiving isn't giving me much to go on here. I'll just go ahead and paste the stack trace first, as I'm not even sure at this point what parts of it are relevant and not...

E/AndroidRuntime: FATAL EXCEPTION: main
Process: com.example.sprite.half_lifetimer, PID: 18548
java.lang.IllegalStateException: Could not execute method for android:onClick
at android.support.v7.app.AppCompatViewInflater$DeclaredOnClickListener.onClick(AppCompatViewInflater.java:390)
at android.view.View.performClick(View.java:6274)
at android.view.View$PerformClick.run(View.java:24859)
at android.os.Handler.handleCallback(Handler.java:789)
at android.os.Handler.dispatchMessage(Handler.java:98)
at android.os.Looper.loop(Looper.java:164)
at android.app.ActivityThread.main(ActivityThread.java:6710)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.Zygote$MethodAndArgsCaller.run(Zygote.java:240)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:770)
Caused by: java.lang.reflect.InvocationTargetException
at java.lang.reflect.Method.invoke(Native Method)
at android.support.v7.app.AppCompatViewInflater$DeclaredOnClickListener.onClick(AppCompatViewInflater.java:385)
at android.view.View.performClick(View.java:6274)
at android.view.View$PerformClick.run(View.java:24859)
at android.os.Handler.handleCallback(Handler.java:789)
at android.os.Handler.dispatchMessage(Handler.java:98)
at android.os.Looper.loop(Looper.java:164)
at android.app.ActivityThread.main(ActivityThread.java:6710)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.Zygote$MethodAndArgsCaller.run(Zygote.java:240)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:770)
Caused by: java.lang.NullPointerException: Attempt to invoke virtual method 'android.content.res.XmlResourceParser android.content.pm.ProviderInfo.loadXmlMetaData(android.content.pm.PackageManager, java.lang.String)' on a null object reference
at android.support.v4.content.FileProvider.parsePathStrategy(FileProvider.java:605)
at android.support.v4.content.FileProvider.getPathStrategy(FileProvider.java:579)
at android.support.v4.content.FileProvider.getUriForFile(FileProvider.java:417)
at com.example.sprite.half_lifetimer.ExportDatabase.shareJSON(ExportDatabase.java:109)
at java.lang.reflect.Method.invoke(Native Method)
at android.support.v7.app.AppCompatViewInflater$DeclaredOnClickListener.onClick(AppCompatViewInflater.java:385)
at android.view.View.performClick(View.java:6274)
at android.view.View$PerformClick.run(View.java:24859)
at android.os.Handler.handleCallback(Handler.java:789)
at android.os.Handler.dispatchMessage(Handler.java:98)
at android.os.Looper.loop(Looper.java:164)
at android.app.ActivityThread.main(ActivityThread.java:6710)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.Zygote$MethodAndArgsCaller.run(Zygote.java:240)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:770)


Code for the activity, which I am attempting to share from (ExportDatabase.java), with this error, follows:

Code:
package com.example.sprite.half_lifetimer;

import android.content.Intent;
import android.net.Uri;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.LinearLayout;
import android.widget.TextView;

import com.google.gson.Gson;
import com.google.gson.GsonBuilder;

import java.io.BufferedWriter;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.io.Writer;
import java.lang.reflect.Array;
import java.net.URLConnection;
import java.util.ArrayList;
import java.util.List;

import static android.support.v4.content.FileProvider.getUriForFile;

public class ExportDatabase extends AppCompatActivity {

    boolean prettyPrinting = false;
    String usagesFN = "usages.json";
    String substancesFN = "substances.json";

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_export_database);

        updateDisplay();
    }

    private void updateDisplay() {
        LinearLayout lloJSONtxt = findViewById(R.id.lloDBDump);
        LinearLayout lloDumpStats = findViewById(R.id.lloDumpStats);
        TextView tvwJSON = new TextView(this);
        TextView tvwDumpStats = new TextView(this);

        tvwJSON.setText(generateJSON());
        lloJSONtxt.removeAllViewsInLayout();
        lloJSONtxt.addView(tvwJSON);

        tvwDumpStats.setText(
                getResources().getString(R.string.json_resides_in, this.getFilesDir()));
        lloDumpStats.removeAllViewsInLayout();
        lloDumpStats.addView(tvwDumpStats);
    }

    private String generateJSON() {
        Gson gson;
        List<Usage> allUsages = Permanence.loadUsages();
        List<Substance> allSubstances = Permanence.loadSubstances(this);
        String substancesJSON;
        String usagesJSON;

        if (prettyPrinting) {
            gson = new GsonBuilder().setPrettyPrinting().create();
        } else {
            gson = new Gson();
        }
        substancesJSON = gson.toJson(allSubstances);
        usagesJSON = gson.toJson(allUsages);

        try {
            File usagesFile = new File(this.getFilesDir(), usagesFN);
            File substancesFile = new File(this.getFilesDir(), substancesFN);
            FileOutputStream usagesIS = new FileOutputStream(usagesFile);
            FileOutputStream substancesIS = new FileOutputStream(substancesFile);
            OutputStreamWriter usagesOSW = new OutputStreamWriter(usagesIS);
            OutputStreamWriter substancesOSW = new OutputStreamWriter(substancesIS);
            Writer usagesW = new BufferedWriter(usagesOSW);
            Writer substancesW = new BufferedWriter(substancesOSW);

            usagesW.write(usagesJSON);
            substancesW.write(substancesJSON);
            usagesW.close();
            substancesW.close();
        } catch (FileNotFoundException ex) {
            GlobalMisc.showSimpleDialog(this, "Exception", "File not found " +
                    "exception!\n" + ex.toString());
        } catch (IOException ex) {
            GlobalMisc.showSimpleDialog(this, "Exception", "Input/Output " +
                    "exception!\n" + ex.toString());
        }

        return "Substances JSON\n\n" + substancesJSON + "\n\nUsages JSON\n\n" + usagesJSON;
    }

    public void togglePretty(View v) {
        prettyPrinting = !prettyPrinting;

        updateDisplay();
    }

    public void shareJSON(View v) {
        File filesDirPath = new File(getFilesDir(), "files");
        filesDirPath.mkdirs();
        File usagesFile = new File(filesDirPath, usagesFN);
        File substancesFile = new File(this.getFilesDir(), substancesFN);
        Uri usagesUri = getUriForFile(this, "com.example.sprite.fileprovider", usagesFile);
        /*Uri substancesUri = getUriForFile(this, "com.example.sprite.half_lifetimer.fileprovider",
                substancesFile);*/

        Intent intentShareJSON = new Intent(Intent.ACTION_SEND);

        //intentShareJSON.setType(URLConnection.guessContentTypeFromName(usagesFN));
        intentShareJSON.setType("text/plain");
        /*intentShareJSON.putExtra(Intent.EXTRA_STREAM, Uri.parse("content://" + this.getFilesDir() +
                "/" + usagesFN));*/
        //intentShareJSON.setData(usagesUri);
        intentShareJSON.putExtra(Intent.EXTRA_STREAM, usagesUri);

        startActivity(Intent.createChooser(intentShareJSON, "Share JSON"));
    }
}

AndroidManifest.xml follows:
Code:
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.sprite.half_lifetimer">

    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/AppTheme">
        <activity android:name=".ExportDatabase"></activity>
        <activity android:name=".ConsolidateDatabase" />
        <activity android:name=".DailyStats" />
        <activity android:name=".DatabaseMaint" />
        <activity android:name=".SubClassTabbedData" />
        <activity
            android:name=".SubClass"
            android:label="@string/title_activity_sub_class"
            android:theme="@style/AppTheme.NoActionBar" />
        <activity android:name=".DosageGraphing" />
        <activity android:name=".UsualSuspects" />
        <activity android:name=".AddPastUsage" />
        <activity android:name=".SubsRankedByLastUsage" />
        <activity android:name=".FullAdminsDumpByID" />
        <activity android:name=".FullAdminsDump" />
        <activity android:name=".TabbedData" />
        <activity android:name=".AdminData" />
        <activity
            android:name=".SubData"
            android:label="@string/app_name"
            android:theme="@style/AppTheme.NoActionBar">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>

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

    </application>

</manifest>

And finally, file_paths.xml follows:
Code:
<?xml version="1.0" encoding="utf-8"?>
<paths xmlns:android="http://schemas.android.com/apk/res/android">
    <files-path name="files" path="files/" />
</paths>

I've looked through multiple tutorials on sharing a file via Intent, and haven't been able to get any of them to work properly in my case. I'm sure that what I'm missing is something fairly trivial, as the basics seem, well, basic enough; I think I just haven't hodgepodged the pieces together correctly.

Oh, for what it's worth, I also know that the file that I'm trying to share should be "text/json", not "text/plain". I'm currently testing it as "text/plain" just to make sure that there are other apps set up that will be able to receive it when I'm debugging. After that bit, I intend to either relabel it as "text/json", or perhaps compress it for transmission to ES File Explorer or something that will allow me to sftp or otherwise send it to a desktop easily. Also, my apologies for the large amounts of commented out code inline; as you can see, I've made several attempts along different routes from the different examples I've been examining.

I really appreciate whatever kinds of issues you may be able to help me locate in this, as well as any time spent assisting on this matter. Thanks in advance!
 
Your clue to the cause of the crash is this section from the stack trace

Code:
Caused by: java.lang.NullPointerException: Attempt to invoke virtual method 'android.content.res.XmlResourceParser android.content.pm.ProviderInfo.loadXmlMetaData(android.content.pm.PackageManager, java.lang.String)' on a null object reference
at android.support.v4.content.FileProvider.parsePathStrategy(FileProvider.java:605)
at android.support.v4.content.FileProvider.getPathStrategy(FileProvider.java:579)
at android.support.v4.content.FileProvider.getUriForFile(FileProvider.java:417)
at com.example.sprite.half_lifetimer.ExportDatabase.shareJSON(ExportDatabase.java:109)

This tells you the exception that crashed your app, and the line in your code which caused it.

So Googling around that error "Attempt to invoke virtual method 'android.content.res.XmlResourceParser ", turns up this link on StackOverflow, amongst others

https://stackoverflow.com/questions...-to-invoke-xmlresourceparser-on-a-null-string
 
Back
Top Bottom