42

I'm developing an Android application and I have to open some files.

This is my code using intent:

public class FacturaActivity extends Activity {

    (...)

    public void downloadInvoice(View view) {
        File file = new File(Environment.getExternalStorageDirectory().getAbsolutePath() +"/"+ filename);
        Intent intent = new Intent(Intent.ACTION_VIEW);
        intent.setDataAndType(Uri.fromFile(file),"application/pdf");
        intent.setFlags(Intent.FLAG_ACTIVITY_NO_HISTORY);
        startActivity(intent);
    }
}

File is in the root directory of the SD card and I can manually open it.

Problem

Application is closed when it arrives at startActivity(intent). I think the problem is in AndroidManifest.xml file, but I don't know how to put it correctly.

AndroidManifest.xml

<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />

<uses-sdk
    android:minSdkVersion="8"
    android:targetSdkVersion="8" />

<application
    android:allowBackup="true"
    android:icon="@drawable/ic_launcher"
    android:label="@string/app_name"
    android:theme="@style/AppTheme"
    android:name="###.MyApplication" > <!--cant show complete name-->
    <activity
        android:name="###.MainActivity"
        android:label="@string/app_name" >
        <intent-filter>
            <action android:name="android.intent.action.MAIN" />

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

    <activity 
        android:name=".FacturaActivity" >
    </activity>

</application>

LogCat

07-03 15:49:13.094: E/AndroidRuntime(1032): FATAL EXCEPTION: main
07-03 15:49:13.094: E/AndroidRuntime(1032): java.lang.IllegalStateException: Could not execute method of the activity
(...)
07-03 15:49:13.094: E/AndroidRuntime(1032): Caused by: android.content.ActivityNotFoundException: No Activity found to handle Intent { act=android.intent.action.VIEW dat=file:///mnt/sdcard/201209_F2012212782.PDF typ=application/pdf flg=0x40000000 }
07-03 15:49:13.094: E/AndroidRuntime(1032):     at android.app.Instrumentation.checkStartActivityResult(Instrumentation.java:1408)
07-03 15:49:13.094: E/AndroidRuntime(1032):     at android.app.Instrumentation.execStartActivity(Instrumentation.java:1378)
07-03 15:49:13.094: E/AndroidRuntime(1032):     at android.app.Activity.startActivityForResult(Activity.java:2817)
07-03 15:49:13.094: E/AndroidRuntime(1032):     at android.app.Activity.startActivity(Activity.java:2923)

Can you help me to complete AndroidManifest? Or how can I open that pdf?

tshepang
  • 12,111
  • 21
  • 91
  • 136
Lyd
  • 2,106
  • 3
  • 26
  • 33
  • it seems your Android has no any pdf reader app installed, add one –  Jul 03 '13 at 16:20
  • I can open other pdf's files with ThinkFree PDF Viewer. – Lyd Jul 03 '13 at 16:32
  • This blog post helps if you're trying to use `ContentProvider` (which is now recommended): http://www.blogc.at/2014/03/23/share-private-files-with-other-apps-fileprovider/ – Sakiboy Dec 30 '16 at 01:39

6 Answers6

109

The problem is that there is no app installed to handle opening the PDF. You should use the Intent Chooser, like so:

File file = new File(Environment.getExternalStorageDirectory().getAbsolutePath() +"/"+ filename);
Intent target = new Intent(Intent.ACTION_VIEW);
target.setDataAndType(Uri.fromFile(file),"application/pdf");
target.setFlags(Intent.FLAG_ACTIVITY_NO_HISTORY);

Intent intent = Intent.createChooser(target, "Open File");
try {
    startActivity(intent);
} catch (ActivityNotFoundException e) {
    // Instruct the user to install a PDF reader here, or something
}   
Sassa
  • 3,294
  • 2
  • 28
  • 42
Paul Burke
  • 25,496
  • 9
  • 66
  • 62
  • I have tried it in my mobile phone. When I open some pdf of another app, it says to register ThinkFree, I say "Later" and "ThinkFree PDF Viewer Mobile for Android" is openned, showing me pdf file. With your code, it only arrives to ask for ThinkFree registering. If I say "Later", it closes. – Lyd Jul 03 '13 at 16:32
  • 1
    That has nothing to do with this code. You can't control the way ThinkFree handles this intent. Try installing an alternative PDF viewer. – Paul Burke Jul 03 '13 at 16:34
  • Thanks, it is ok. One more thing...When I close pdf file (with back button of the phone) my application closes with an error. How can I solve that? – Lyd Jul 03 '13 at 16:38
  • Are you using `startActivity(Intent)` or `startActivityForResult(Intent, int)`? I'd have to see what you're doing in `onResume()` or `onActivityResult(int, int, Intent)`. What is the error? – Paul Burke Jul 03 '13 at 16:40
  • I use Intent intent = new Intent(this, FacturaActivity.class); startActivity(intent); I haven't written anything in onResume(). The error is: "Sorry! The application ### (process com.###) has stopped unespectedly. Please try again. [Force close]" and it returns to another previous activity (not the one that handled pdf viewer) without any data that should have. I want to return to same Activity that startActivity of PDF viewer. – Lyd Jul 03 '13 at 16:46
  • I meant the actual error (exception stack), from the logs. That "Force Close" message, on the UI, is the same for all exceptions. – Paul Burke Jul 03 '13 at 16:50
  • Sorry, I can't see it now because I've had to try it in my mobile phone, not connected to pc, so I can't read log. I'll try it tomorrow and answer you. – Lyd Jul 03 '13 at 17:00
  • Error given is ArrayIndexOutOfBoundsException because data has been deleted. But this happens with ThinkFree PDF Viewer, with Adobe Reader data is loaded correctly. I don't understand it... – Lyd Jul 04 '13 at 08:30
  • In my app gives me file does not exist message.A have the file in AndroidStudioProjects/Myapp/app/src/main/res/myfilefolder/test.pdf.Can you give the syntax of the filepath?I am using android studio and I try it in the emulator. – Apollon Jul 06 '15 at 21:58
  • Is intent will be handled , if my file is in internal storage ? – John Aug 04 '16 at 09:55
  • ActivityNotFoundException is never be thrown, because it always resolve com.android.internal.app.ChooserActivity. – bitvale Jan 17 '19 at 11:09
13

As of API 24, sending a file:// URI to another app will throw a FileUriExposedException. Instead, use FileProvider to send a content:// URI:

public File getFile(Context context, String fileName) {
    if (!Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {
        return null;
    }

    File storageDir = context.getExternalFilesDir(null);
    return new File(storageDir, fileName);
}

public Uri getFileUri(Context context, String fileName) {
    File file = getFile(context, fileName);
    return FileProvider.getUriForFile(context, BuildConfig.APPLICATION_ID + ".provider", file);
}

You must also define the FileProvider in your manifest:

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

Example file_paths.xml:

<?xml version="1.0" encoding="utf-8"?>
<paths xmlns:android="http://schemas.android.com/apk/res/android">
    <external-files-path name="name" path="path" />
</paths>

Replace "name" and "path" as appropriate.

To give the PDF viewer access to the file, you also have to add the FLAG_GRANT_READ_URI_PERMISSION flag to the intent:

private void displayPdf(String fileName) {
    Uri uri = getFileUri(this, fileName);

    Intent intent = new Intent(Intent.ACTION_VIEW);
    intent.setDataAndType(uri, "application/pdf");

    // FLAG_GRANT_READ_URI_PERMISSION is needed on API 24+ so the activity opening the file can read it
    intent.setFlags(Intent.FLAG_ACTIVITY_NO_HISTORY | Intent.FLAG_GRANT_READ_URI_PERMISSION);

    if (intent.resolveActivity(getPackageManager()) == null) {
        // Show an error
    } else {
        startActivity(intent);
    }
}

See the FileProvider documentation for more details.

Big McLargeHuge
  • 14,841
  • 10
  • 80
  • 108
8
String dir="/Attendancesystem";

 public void displaypdf() {

        File file = null;
            file = new File(Environment.getExternalStorageDirectory()+dir+ "/sample.pdf");
        Toast.makeText(getApplicationContext(), file.toString() , Toast.LENGTH_LONG).show();
        if(file.exists()) {
            Intent target = new Intent(Intent.ACTION_VIEW);
            target.setDataAndType(Uri.fromFile(file), "application/pdf");
            target.setFlags(Intent.FLAG_ACTIVITY_NO_HISTORY);

            Intent intent = Intent.createChooser(target, "Open File");
            try {
                startActivity(intent);
            } catch (ActivityNotFoundException e) {
                // Instruct the user to install a PDF reader here, or something
            }
        }
        else
            Toast.makeText(getApplicationContext(), "File path is incorrect." , Toast.LENGTH_LONG).show();
    }
CubeJockey
  • 2,209
  • 8
  • 24
  • 31
Sanjit Majee
  • 129
  • 1
  • 7
2

Kotlin version below (Updated version of @paul-burke response:

fun openPDFDocument(context: Context, filename: String) {
    //Create PDF Intent
    val pdfFile = File(Environment.getExternalStorageDirectory().absolutePath + "/" + filename)
    val pdfIntent = Intent(Intent.ACTION_VIEW)
    pdfIntent.setDataAndType(Uri.fromFile(pdfFile), "application/pdf")
    pdfIntent.setFlags(Intent.FLAG_ACTIVITY_NO_HISTORY)

    //Create Viewer Intent
    val viewerIntent = Intent.createChooser(pdfIntent, "Open PDF")
    context.startActivity(viewerIntent)
}
Brandon Stillitano
  • 1,304
  • 8
  • 27
1

The reason you don't have permissions to open file is because you didn't grant other apps to open or view the file on your intent. To grant other apps to open the downloaded file, include the flag(as shown below): FLAG_GRANT_READ_URI_PERMISSION

Intent browserIntent = new Intent(Intent.ACTION_VIEW);
browserIntent.setDataAndType(getUriFromFile(localFile), "application/pdf");
browserIntent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION|
Intent.FLAG_ACTIVITY_NO_HISTORY);
startActivity(browserIntent);

And for function:

getUriFromFile(localFile)

private Uri getUriFromFile(File file){
    if (Build.VERSION.SDK_INT < Build.VERSION_CODES.N) {
        return Uri.fromFile(file);
    }else {
        return FileProvider.getUriForFile(itemView.getContext(), itemView.getContext().getApplicationContext().getPackageName() + ".provider", file);
    }
}
1

Want to chime in with the answers above. The code is nearly identical, except it's in an Android Jetpack Compose composable (and therefore in Kotlin). That, and I did two videos talking through it. Here's the happy path version, (clocking in at 10 minutes).

For the whole hog, this behemoth 30 minute screenshow has me provide a significant amount of context and a/b options of the code.

If you want to see the code, you can find it in this repo branch.

POBrien
  • 101
  • 1
  • 4