After modifying build.gradle with :
compileSdkVersion 28 to 32
targetSdkVersion 28 to 32
I can't write a local file from retrofit response.
I get error :
java.io.FileNotFoundException: /storage/emulated/0/DontEat/rangt_00001.jpg: open failed: EPERM (Operation not permitted)
AndroidManifest.xml contains :
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
and my code is :
public static void downloadFile(SyncCalls.CallbacksDownload callbacks, final String type, final File file, final Boolean last) {
final WeakReference<SyncCalls.CallbacksDownload> callbacksWeakReference = new WeakReference<>(callbacks);
SyncService service = SyncService.retrofit.create(SyncService.class);
String filename = file.toString().split("/")[5];
Call<ResponseBody> call = service.downloadFile(filename);
call.enqueue(new Callback<ResponseBody>() {
#Override
public void onResponse(#NonNull Call<ResponseBody> call, #NonNull Response<ResponseBody> response) {
try {
OutputStream out = null;
InputStream in = null;
ResponseBody body = response.body();
try {
if (body != null) {
in = body.byteStream();
out = new FileOutputStream(file);
byte[] fileReader = new byte[4096];
StrictMode.ThreadPolicy policy = new StrictMode.ThreadPolicy.Builder().permitAll().build();
StrictMode.setThreadPolicy(policy);
while (true) {
int read = in.read(fileReader);
if (read == -1) {
break;
}
out.write(fileReader, 0, read);
}
out.flush();
}
} catch (IOException e) {
if (BuildConfig.DEBUG) Log.v(Consts.TAG, e.toString());
} finally {
if (in != null) {
in.close();
}
if (out != null) {
out.close();
}
}
} catch (IOException e) {
if (BuildConfig.DEBUG) Log.v(Consts.TAG, e.toString());
}
if (BuildConfig.DEBUG) Log.v(Consts.TAG, "download " + file.toString());
if (callbacksWeakReference.get() != null)
callbacksWeakReference.get().onResponseSync(type, "down", last);
}
Permissions are verified with :
// Storage Permissions variables
private static final int REQUEST_EXTERNAL_STORAGE = 1;
private static final String[] PERMISSIONS_STORAGE = {
Manifest.permission.READ_EXTERNAL_STORAGE,
Manifest.permission.WRITE_EXTERNAL_STORAGE
};
//permission method.
public static void verifyStoragePermissions(Activity activity) {
// Check if we have read or write permission
int writePermission = ActivityCompat.checkSelfPermission(activity, Manifest.permission.WRITE_EXTERNAL_STORAGE);
int readPermission = ActivityCompat.checkSelfPermission(activity, Manifest.permission.READ_EXTERNAL_STORAGE);
if (writePermission != PackageManager.PERMISSION_GRANTED || readPermission != PackageManager.PERMISSION_GRANTED) {
// We don't have permission so prompt the user
ActivityCompat.requestPermissions(
activity,
PERMISSIONS_STORAGE,
REQUEST_EXTERNAL_STORAGE
);
}
}
Is my mistake in the code or the permissions please?
I don't develop more because of my very limited English.
(It's an app I made many years ago, for personal use only, family, but I can't scale it because of this bug that appeared with new android versions while it was working perfectly so far)
Edit:
Actually, I think I'm taking the problem upside down and I'd better use the application's internal data directory with getFilesDir() and I won't have permission problems anymore, if I understood correctly.
Add the line in AndroidManifest.xml, inside application tag..
Hope it'll help you.
android:requestLegacyExternalStorage=”true”
I am trying to get the file from picking image from photos and get intent data, save the file in internal memory and use the file to load on Image views.
But I am getting the error as follows:
java.io.FileNotFoundException: /storage/emulated/0/Android/data/com.dailyfaithapp.dailyfaith/Files/MI_10052020_1711.png: open failed: ENOENT (No such file or directory)
I checked on the lower version of api i.e. on 24 it worked once or twice but again it failed.
And on api 29 its not working at all. For that I followed this url :
https://medium.com/#sriramaripirala/android-10-open-failed-eacces-permission-denied-da8b630a89df
I checked the code in java and tried the same but still its giving the error.
I am also checking for runtime permissions and have specified permissions in manifest file.
Following is my code:
<uses-permission android:name = "android.permission.INTERNET" />
<uses-permission android:name = "android.permission.SYSTEM_ALERT_WINDOW" />
<uses-permission android:name = "android.permission.WAKE_LOCK" />
<uses-permission android:name = "android.permission.RECEIVE_BOOT_COMPLETED" />
<uses-permission android:name = "android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name = "android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name = "com.google.android.apps.photos.permission.GOOGLE_PHOTOS" />
Checking runtime permissions :
private boolean checkPermission() {
return ContextCompat.checkSelfPermission(this, WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED
&& ContextCompat.checkSelfPermission(this, READ_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED
;
}
private void requestPermissionAndContinue() {
if (ContextCompat.checkSelfPermission(this, WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED
&& ContextCompat.checkSelfPermission(this, READ_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) {
if (ActivityCompat.shouldShowRequestPermissionRationale(this, WRITE_EXTERNAL_STORAGE)
&& ActivityCompat.shouldShowRequestPermissionRationale(this, READ_EXTERNAL_STORAGE)) {
AlertDialog.Builder alertBuilder = new AlertDialog.Builder(this);
alertBuilder.setCancelable(true);
alertBuilder.setTitle("Allow Daily Faith to access photos," +
"media, and files on your device?");
alertBuilder.setPositiveButton(android.R.string.yes, new DialogInterface.OnClickListener() {
#TargetApi(Build.VERSION_CODES.JELLY_BEAN)
public void onClick(DialogInterface dialog, int which) {
ActivityCompat.requestPermissions(ThemesActivity.this,
new String[]{WRITE_EXTERNAL_STORAGE
, READ_EXTERNAL_STORAGE}, 200);
}
});
AlertDialog alert = alertBuilder.create();
alert.show();
Log.e("", "permission denied, show dialog");
} else {
ActivityCompat.requestPermissions(ThemesActivity.this,
new String[]{WRITE_EXTERNAL_STORAGE,
READ_EXTERNAL_STORAGE}, 200);
}
} else {
selectImageFromGallery();
}
}
Checking if permission is given:
if (!checkPermission()) {
selectImageFromGallery();
} else {
if (checkPermission()) {
requestPermissionAndContinue();
} else {
selectImageFromGallery();
}
}
Opening intent :
public void selectImageFromGallery()
{
Intent pickPhoto = new Intent(Intent.ACTION_PICK,
MediaStore.Images.Media.EXTERNAL_CONTENT_URI);
startActivityForResult(pickPhoto , 2);
}
Get intent data on result :
#RequiresApi(api = Build.VERSION_CODES.KITKAT) #Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
// Check which request we're responding to
super.onActivityResult(requestCode, resultCode, data);
if (requestCode == 2) {
// Make sure the request was successful
if (resultCode == RESULT_OK) {
// The user picked a image.
// The Intent's data Uri identifies which item was selected.
if (data != null) {
customTheme = true;
// This is the key line item, URI specifies the name of the data
mImageUri = data.getData();
// Saves image URI as string to Default Shared Preferences
SharedPreferencesData sharedPreferencesData =
new SharedPreferencesData(this);
sharedPreferencesData.setStr("customThemeSet","true");
try {
Bitmap bitmap =
MediaStore.Images.Media.getBitmap(getContentResolver(), mImageUri);
/* Utils.storeImage(bitmap,
ThemesActivity.this);
File file = Utils.getOutputMediaFile(ThemesActivity.this);
*/
try {
final ParcelFileDescriptor parcelFileDescriptor = getContentResolver().openFileDescriptor(
mImageUri, "r");
final FileDescriptor fileDescriptor = parcelFileDescriptor.getFileDescriptor();
bitmap = BitmapFactory.decodeFileDescriptor(fileDescriptor);
parcelFileDescriptor.close();
Utils.storeImage(bitmap,
ThemesActivity.this);
File file = Utils.getOutputMediaFile(ThemesActivity.this);
int color = Utils.getDominantColor(bitmap);
Log.d("Bitmap", bitmap.toString());
Boolean isDark = Utils.isColorDark(color);
if(customTheme) {
for (Themes themes : themesArrayList) {
themes.setCustomTheme(file.getPath());
themes.setDark(isDark);
}
if(isDark)
sharedPreferencesData.setStr("ThemeColor","dark");
else
sharedPreferencesData.setStr("ThemeColor","light");
themesAdapter = new ThemesAdapter(themesArrayList, this,customTheme);
recyclerView.setAdapter(themesAdapter);
}
} catch (Exception e) {
e.printStackTrace();
}
} catch (Exception e) {
Log.e("Failed", "Failed to Parse Image Uri", e);
try {
throw new Exception("failed to parse image uri");
} catch (Exception ex) {
ex.printStackTrace();
}
}
}
}
}
}
Saving and getting file
public static File getOutputMediaFile(Context context){
// To be safe, you should check that the SDCard is mounted
// using Environment.getExternalStorageState() before doing this.
File mediaStorageDir = new File(Environment.getExternalStorageDirectory()
+ "/Android/data/"
+ context.getApplicationContext().getPackageName()
+ "/Files");
// This location works best if you want the created images to be shared
// between applications and persist after your app has been uninstalled.
// Create the storage directory if it does not exist
if (! mediaStorageDir.exists()){
mediaStorageDir.mkdirs();
}
else {
return null;
}
// Create a media file name
String timeStamp = new SimpleDateFormat("ddMMyyyy_HHmm").format(new Date());
File mediaFile;
String mImageName="MI_"+ timeStamp +".png";
mediaFile = new File(mediaStorageDir.getPath() + File.separator + mImageName);
return mediaFile;
}
public static void storeImage(Bitmap image,Context context) {
File pictureFile = getOutputMediaFile(context);
if (pictureFile == null) {
Log.d(TAG,
"Error creating media file, check storage permissions: ");// e.getMessage());
return;
}
try {
FileOutputStream fos = new FileOutputStream(pictureFile);
image.compress(Bitmap.CompressFormat.PNG, 90, fos);
fos.close();
} catch (FileNotFoundException e) {
Log.d(TAG, "File not found: " + e.getMessage());
} catch (IOException e) {
Log.d(TAG, "Error accessing file: " + e.getMessage());
}
}
Before I was getting EACCESS error when I only used above two funtions to save the file.
Later I tried Parcel file descriptor but not working
Why am I getting this error? Is it only on api level 29 or below too?
What can be the solution for this to run on all devices?
Here is what worked for me Using the downloads folder which all phones old and new have public access to..
try{
File csvfile = new File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS) + "/brcOrdersaved.csv");
final String filename = csvfile.toString();
if (!csvfile.exists()) {
// displayMsg(context, "No Saved Order: ");
return (false);
}
FileReader fr = new FileReader(filename);
BufferedReader reader = new BufferedReader(fr);
String csvLine = "";
final char Separator = ',';
final char Delimiter = '"';
final char LF = '\n';
final char CR = '\r';
boolean quote_open = false;
if (reader.equals(null)) {
displayMsg(context, "NULL");
return (false);//rww 11/13/2021
}
int i = 0;
while (!myendofcsvfile) {
csvLine = reader.readLine();
if (csvLine == null) {
myendofcsvfile = true;
}
// do stuff here
}
fr.close();
fileexists = true;
} catch (Exception e) {
String msg = "Can not Load Saved Order ";
fileexists = false;
return (fileexists);
}
I add this permissions, and it works.
<uses-permission
enter code hereandroid:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS"
tools:ignore="ProtectedPermissions" />
I am using an app to create a csv file which I would then like to export and read on the phone. However, the location that I am saving it to is not viewable and is making it hard to transfer.
Is there a way of saving this to a more accesible location such as /documents on the phone?
(I am very new to Java so sorry if this is an obvious question)
Thanks!
public void submit(View v)
{
String nline = System.getProperty("line.separator");
String fname = firstName.getText().toString() + ",";
String sname = surname.getText().toString() + ",";
String gender = genderSpin.getSelectedItem().toString() + ",";
String eaddress = email.getText().toString() + ",";
String mnum = mobile.getText().toString() + ",";
String fos = course.getText().toString() + ",";
String prole = proleSpin.getSelectedItem().toString();
FileOutputStream file = null;
if(fname.length() <= 1 || sname.length() <= 1 || eaddress.length() <= 1){
Toast.makeText(this, "Please enter all mandatory fields", Toast.LENGTH_SHORT).show();
}
else {
try {
file = openFileOutput(fileName, MODE_APPEND);
file.write(fname.getBytes());
file.write(sname.getBytes());
file.write(gender.getBytes());
file.write(eaddress.getBytes());
if (mnum.length() < 11) {
mnum = "null,";
file.write(mnum.getBytes());
}
if (fos.length() <= 1) {
fos = "null,";
file.write(fos.getBytes());
}
file.write(prole.getBytes());
file.write(nline.getBytes());
firstName.getText().clear();
surname.getText().clear();
genderSpin.setSelection(0);
email.getText().clear();
mobile.getText().clear();
course.getText().clear();
proleSpin.setSelection(0);
Toast.makeText(this, "Successfully Submitted", Toast.LENGTH_SHORT).show();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
if (file != null) {
try {
file.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
First add WRITE_EXTERNAL_STORAGE to your manifest file.
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
Now you need request permission you can do it like this.
if(!checkPermission()){
if (ActivityCompat.shouldShowRequestPermissionRationale(this,
Manifest.permission.WRITE_EXTERNAL_STORAGE)) {
} else {
// No explanation needed; request the permission
ActivityCompat.requestPermissions(this,
new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE},
WRITE_PERMISSION);
}
}
else {
// Permission already granted
}
Once the permission has been granted you can save file.
Following is the working MainActivity.class
package co.introtuce.nex2me.writefle;
import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;
import androidx.core.app.ActivityCompat;
import androidx.core.content.ContextCompat;
import android.Manifest;
import android.content.Context;
import android.content.pm.PackageManager;
import android.os.Bundle;
import android.os.Environment;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.Toast;
import java.io.File;
import java.io.FileOutputStream;
import java.io.OutputStream;
public class MainActivity extends AppCompatActivity {
public static final int WRITE_PERMISSION=0xff;
Button button;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
button=findViewById(R.id.mid);
button.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
requestPermission();
}
});
}
public void requestPermission(){
if(!checkPermission()){
if (ActivityCompat.shouldShowRequestPermissionRationale(this,
Manifest.permission.WRITE_EXTERNAL_STORAGE)) {
// Show an explanation to the user *asynchronously* -- don't block
// this thread waiting for the user's response! After the user
// sees the explanation, try again to request the permission.
} else {
// No explanation needed; request the permission
ActivityCompat.requestPermissions(this,
new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE},
WRITE_PERMISSION);
// MY_PERMISSIONS_REQUEST_READ_CONTACTS is an
// app-defined int constant. The callback method gets the
// result of the request.
}
}
else {
afterPermisiion();
}
}
public boolean checkPermission(){
if(ContextCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE)
!= PackageManager.PERMISSION_GRANTED){
return false;
}
return true;
}
#Override
public void onRequestPermissionsResult(int requestCode, #NonNull String[] permissions, #NonNull int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
if(requestCode == WRITE_PERMISSION){
if(grantResults.length>0 && grantResults[0] == PackageManager.PERMISSION_GRANTED){
//Permisiion Granted
afterPermisiion();
return;
}
}
requestPermission();
}
public void afterPermisiion(){
submit();
}
public boolean saveFile(String csv_contents, Context context){
OutputStream outputStream = null;
try{
File root = Environment.getExternalStorageDirectory();
if(root == null){
Log.d("SAVE_PHONE", "Failed to get root");
return false;
}
// create a directory
File saveDirectory = new File(root,"appName/files/csv" );
// create direcotory if it doesn't exists
// create direcotory if it doesn't exists
if(!saveDirectory.exists()) if ( !saveDirectory.mkdirs()){
Toast.makeText(context,"sorry could not create directory"+saveDirectory.getAbsolutePath(), Toast.LENGTH_LONG).show();
return false;
}
outputStream = new FileOutputStream( saveDirectory + "myfile.csv"); // filename.png, .mp3, .mp4 ...
if(outputStream != null){
Log.e( "SAVE_PHONE", "Output Stream Opened successfully");
}
byte[] bytes = csv_contents.getBytes();
outputStream.write( bytes, 0, bytes.length );
outputStream.close();
return true;
}catch (Exception e){
Log.d("EXCEPTION_IN",e.toString());
return false;
}
}
public void submit()
{
String nline = System.getProperty("line.separator");
String fname = "Name" + ",";
String sname = "Surname" + ",";
String gender = "gn" + ",";
String eaddress = "email" + ",";
String mnum = "num" + ",";
String fos = "fos" + ",";
String prole = "prole";
FileOutputStream file = null;
if(fname.length() <= 1 || sname.length() <= 1 || eaddress.length() <= 1){
Toast.makeText(this, "Please enter all mandatory fields", Toast.LENGTH_SHORT).show();
}
String csv_contents = nline+""+fname+sname+gender+eaddress+mnum+fos+prole;
if(saveFile(csv_contents,this)){
//File has saved
// DO something
Toast.makeText(this,"File has been saved",Toast.LENGTH_LONG).show();
}
else{
//Could not save file
// DO something
}
}
}
This will save cvs file in FileManager like /storage/emulated/0/appName/files/csv location. You can modify this location.
public void DOCS(View btnDocs)
{
File fileBrochure = new File("android.resource://com.project.datastructure/assets/abc.pdf");
if (!fileBrochure.exists())
{
CopyAssetsbrochure();
}
/** PDF reader code */
File file = new File("android.resource://com.project.datastructure/assets/abc.pdf");
Intent intent = new Intent(Intent.ACTION_VIEW);
intent.setDataAndType(Uri.fromFile(file),"application/pdf");
intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
try
{
getApplicationContext().startActivity(intent);
}
catch (ActivityNotFoundException e)
{
Toast.makeText(Stack_dr.this, "NO Pdf Viewer", Toast.LENGTH_SHORT).show();
}
}
private void CopyAssetsbrochure() {
AssetManager assetManager = getAssets();
String[] files = null;
try
{
files = assetManager.list("");
}
catch (IOException e){}
for(int i=0; i<files.length; i++)
{
String fStr = files[i];
if(fStr.equalsIgnoreCase("abc.pdf"))
{
InputStream in = null;
OutputStream out = null;
try
{
in = assetManager.open(files[i]);
out = new FileOutputStream("/sdcard/" + files[i]);
copyFile(in, out);
in.close();
in = null;
out.flush();
out.close();
out = null;
break;
}
catch(Exception e){}
}
}
}
private void copyFile(InputStream in, OutputStream out) throws IOException
{
byte[] buffer = new byte[1024];
int read;
while((read = in.read(buffer)) != -1){
out.write(buffer, 0, read);
}
}
I am trying to read a pdf file from assets folder which is present in my application folder . Everything is working perfectly when i click on my DOCS button a pop up comes to let me choose an application for opening the pdf i.e "abc.pdf" but after selecting an option i get an error message "The file path is not valid". I think their is some problem with the path which i have specified in the code.
please help
Try this
public class SampleActivity extends Activity
{
#Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
CopyReadAssets();
}
private void CopyReadAssets()
{
AssetManager assetManager = getAssets();
InputStream in = null;
OutputStream out = null;
File file = new File(getFilesDir(), "abc.pdf");
try
{
in = assetManager.open("abc.pdf");
out = openFileOutput(file.getName(), Context.MODE_WORLD_READABLE);
copyFile(in, out);
in.close();
in = null;
out.flush();
out.close();
out = null;
} catch (Exception e)
{
Log.e("tag", e.getMessage());
}
Intent intent = new Intent(Intent.ACTION_VIEW);
intent.setDataAndType(
Uri.parse("file://" + getFilesDir() + "/abc.pdf"),
"application/pdf");
startActivity(intent);
}
private void copyFile(InputStream in, OutputStream out) throws IOException
{
byte[] buffer = new byte[1024];
int read;
while ((read = in.read(buffer)) != -1)
{
out.write(buffer, 0, read);
}
}
}
Make sure to include
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
in manifest
You can do it like this (tested and working on API 27)
Step 1
Add following dependency in your app gradle:
implementation 'com.github.barteksc:android-pdf-viewer:2.8.2'
Step 2
Add following XML code:
<com.github.barteksc.pdfviewer.PDFView
android:id="#+id/pdfv"
android:layout_width="match_parent"
android:layout_height="match_parent">
</com.github.barteksc.pdfviewer.PDFView>
Step 3
Add following code in your java file:
public class MainActivity extends AppCompatActivity {
PDFView pdfView;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
pdfView=findViewById(R.id.pdfv);
pdfView.fromAsset("filename.pdf").load();
}
}
These changes will load a PDF file on creation of your activity.
Though this has been answered, I wanted to share my solution as I think it is a bit easier to include.
Usage:
new OpenLocalPDF(context, 'nameOfPDFStoredInAssets').execute()
And here is the OpenLocalPDF class:
public class OpenLocalPDF {
private static String TAG = OpenLocalPDF.class.getSimpleName();
private WeakReference<Context> contextWeakReference;
private String fileName;
public OpenLocalPDF(Context context, String fileName) {
this.contextWeakReference = new WeakReference<>(context);
this.fileName = fileName.endsWith("pdf") ? fileName : fileName + ".pdf";
}
public void execute() {
Context context = contextWeakReference.get();
if (context != null) {
new CopyFileAsyncTask().execute();
}
}
private class CopyFileAsyncTask extends AsyncTask<Void, Void, File> {
final String appDirectoryName = BuildConfig.APPLICATION_ID;
final File fileRoot = new File(Environment.getExternalStoragePublicDirectory(
Environment.DIRECTORY_DOCUMENTS), appDirectoryName);
#Override
protected File doInBackground(Void... params) {
Context context = contextWeakReference.get();
AssetManager assetManager = context.getAssets();
File file = new File(fileRoot, fileName);
InputStream in = null;
OutputStream out = null;
try {
file.mkdirs();
if (file.exists()) {
file.delete();
}
file.createNewFile();
in = assetManager.open(fileName);
Log.d(TAG, "In");
out = new FileOutputStream(file);
Log.d(TAG, "Out");
Log.d(TAG, "Copy file");
copyFile(in, out);
Log.d(TAG, "Close");
in.close();
out.flush();
out.close();
return file;
} catch (Exception e)
{
Log.e(TAG, e.getMessage());
}
return null;
}
private void copyFile(InputStream in, OutputStream out) throws IOException
{
byte[] buffer = new byte[1024];
int read;
while ((read = in.read(buffer)) != -1)
{
out.write(buffer, 0, read);
}
}
#Override
protected void onPostExecute(File file) {
super.onPostExecute(file);
Context context = contextWeakReference.get();
Intent intent = new Intent(Intent.ACTION_VIEW);
intent.setDataAndType(
Uri.fromFile(file),
"application/pdf");
context.startActivity(intent);
}
}
}
add this permission in Manifest.xml
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
like say sunsil, but in the case for external directory.
import android.app.Activity;
import android.content.Intent;
import android.content.res.AssetManager;
import android.net.Uri;
import android.os.Environment;
import android.os.Bundle;
import android.util.Log;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
public class MainActivity extends Activity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
copyReadAssets();
}
private void copyReadAssets()
{
AssetManager assetManager = getAssets();
InputStream in = null;
OutputStream out = null;
String strDir = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS)+ File.separator + "Pdfs";
File fileDir = new File(strDir);
fileDir.mkdirs(); // crear la ruta si no existe
File file = new File(fileDir, "example2.pdf");
try
{
in = assetManager.open("example.pdf"); //leer el archivo de assets
out = new BufferedOutputStream(new FileOutputStream(file)); //crear el archivo
copyFile(in, out);
in.close();
in = null;
out.flush();
out.close();
out = null;
} catch (Exception e)
{
Log.e("tag", e.getMessage());
}
Intent intent = new Intent(Intent.ACTION_VIEW);
intent.setDataAndType(Uri.parse("file://" + Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS) + File.separator + "Pdfs" + "/example2.pdf"), "application/pdf");
startActivity(intent);
}
private void copyFile(InputStream in, OutputStream out) throws IOException
{
byte[] buffer = new byte[1024];
int read;
while ((read = in.read(buffer)) != -1)
{
out.write(buffer, 0, read);
}
}
}
change parts of code like these:
out = new BufferedOutputStream(new FileOutputStream(file));
the before example is for Pdfs, in case of to example .txt
FileOutputStream fos = new FileOutputStream(file);
This works for me.
Step 1: In MainActivity.java
public class SampleActivity extends Activity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//call this method by passing file name in params.
readFromAssets("User guide.pdf");
}
public static void readFromAssets(String fileName) {
AssetManager assetManager = getAssets();
InputStream in = null;
OutputStream out = null;
File file = new File(mContext.getFilesDir(), fileName);
try {
in = assetManager.open(fileName);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
out = mContext.openFileOutput(file.getName(), Context.MODE_PRIVATE);
} else {
out = mContext.openFileOutput(file.getName(), Context.MODE_WORLD_READABLE);
}
copyFile(in, out);
in.close();
in = null;
out.flush();
out.close();
out = null;
} catch (Exception e) {
Log.e("tag", e.getMessage());
}
Uri pdfFileURI;
Intent intent = new Intent(Intent.ACTION_VIEW);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
pdfFileURI = FileProvider.getUriForFile(mContext,
BuildConfig.APPLICATION_ID + ".provider", file);
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
} else {
pdfFileURI = Uri.parse("file://" + mContext.getFilesDir() + "/" + fileName);
}
intent.setDataAndType(pdfFileURI, "application/pdf");
mContext.startActivity(intent);
}
private static void copyFile(InputStream in, OutputStream out) throws IOException {
byte[] buffer = new byte[1024];
int read;
while ((read = in.read(buffer)) != -1) {
out.write(buffer, 0, read);
}
}
}
Step 2: Create provider_paths.xml inside res->xml->provider_paths.xml. See here
<?xml version="1.0" encoding="utf-8"?>
<paths>
<external-path
name="external"
path="." />
<root-path
name="root"
path="." />
</paths>
Step 3: In AndroidManifest.xml
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<application
android:icon="#mipmap/ic_launcher"
android:label="#string/app_name"
android:theme="#style/AppThemeMaterial">
<!-- INSIDE APPLICATION TAG -->
<provider
android:name="androidx.core.content.FileProvider"
android:authorities="${applicationId}.provider"
android:exported="false"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="#xml/provider_paths"/>
</provider>
</application>
No answer worked for me out-of-the-box when attempting to open a file via an intent (in my case PDF) from the assets folder. So here is my solution combining 2 brilliant answers I'm sure it will help someone. Must be for api >= 24.
Once you have followed this link and added the your own GenericFileProvider and declared it in your manifest within the Application tag and created the provider_paths.xml simply use the below class and call it like:
OpenLocalPDF(this, "YOUR_PDF_NAME_IN_ASSETS_FOLDER.pdf").execute()
OpenLocalPdf.kt: (kudos too the OP https://stackoverflow.com/a/41212708/1133011)
import android.content.Context
import android.content.Intent
import android.content.res.AssetManager
import android.os.AsyncTask
import android.util.Log
import androidx.core.content.FileProvider
import java.io.File
import java.io.FileOutputStream
import java.io.InputStream
import java.io.OutputStream
import java.lang.ref.WeakReference
class OpenLocalPDF(context: Context?, fileName: String?) {
private val contextWeakReference: WeakReference<Context?>?
private val fileName: String?
fun execute() {
val context: Context = contextWeakReference!!.get()!!
if (context != null) {
CopyFileAsyncTask().execute()
}
}
private inner class CopyFileAsyncTask :
AsyncTask<Void?, Void?, File?>() {
val appDirectoryName: String? = co.za.umbiflow.BuildConfig.APPLICATION_ID
val fileRoot: File? = File(
android.os.Environment.getExternalStoragePublicDirectory(
android.os.Environment.DIRECTORY_DOCUMENTS
), appDirectoryName
)
override fun doInBackground(vararg params: Void?): File? {
val context: Context = contextWeakReference!!.get()!!
val assetManager: AssetManager = context.getAssets()
val file = File(fileRoot, fileName)
var `in`: InputStream? = null
var out: OutputStream? = null
try {
file.mkdirs()
if (file.exists()) {
file.delete()
}
file.createNewFile()
`in` = assetManager.open(fileName)
out = FileOutputStream(file)
copyFile(`in`, out)
`in`.close()
out.flush()
out.close()
return file
} catch (e: Exception) {
Log.e(TAG, e.message)
}
return null
}
private fun copyFile(`in`: InputStream, out: OutputStream) {
val buffer = ByteArray(1024)
var read: Int
while (`in`.read(buffer).also { read = it } != -1) {
out.write(buffer, 0, read)
}
}
override fun onPostExecute(file: File?) {
super.onPostExecute(file)
val context: Context = contextWeakReference!!.get()!!
var pdfUri =
FileProvider.getUriForFile(
context,
context.packageName + ".provider",
file!!
)
val intent = Intent()
intent.action = Intent.ACTION_VIEW
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
intent.setDataAndType(
pdfUri,
"application/pdf"
)
context.startActivity(intent)
}
}
companion object {
private val TAG = OpenLocalPDF::class.java.simpleName
}
init {
contextWeakReference = WeakReference(context)
this.fileName = if (fileName!!.endsWith("pdf")) fileName else "$fileName.pdf"
}
}
This is a way to open multiple PDF files from assets using a zip file.
Step1: Copy a zip file(pdf.zip) into assets folder that contains the list of pdfs
Step2: Write PDF's into external cache inside the app in a Utils class
public static void writePDFs(Context context) {
try {
InputStream inputStream = context.getApplicationContext().getAssets().open("pdf.zip");
ZipInputStream zipInputStream = new ZipInputStream(inputStream);
ZipEntry zipEntry;
File file = new File(context.getExternalCacheDir().getAbsolutePath(), "PDFs");
if (!file.getAbsoluteFile().exists()) {
if (file.mkdir()) {
BufferedOutputStream outputStream;
byte[] buffer = new byte[1024];
int count;
while ((zipEntry = zipInputStream.getNextEntry()) != null) {
String s1 = file + "/" + zipEntry.getName();
File file1 = new File(s1);
if (!file1.exists()) {
file1.createNewFile();
}
outputStream = new BufferedOutputStream(new FileOutputStream(file1));
while ((count = zipInputStream.read(buffer)) != -1) {
outputStream.write(buffer, 0, count);
}
outputStream.flush();
outputStream.close();
zipInputStream.closeEntry();
}
}
zipInputStream.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
Step3: Call the above function in Activities onCreate
#Override
protected void onCreate(#Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
FileUtils.writePDFs(this);
}
Step4: Create a common function in Utils class to open the PDF
public static void openPDF(Activity activity, String fileName) {
if (activity == null) return;
File file = new File(activity.getExternalCacheDir().getAbsolutePath() + "/PDFs" + "/" + fileName);
Uri docUri = GenericFileProvider.getUriForFile(activity, activity.getApplicationContext().getPackageName(), file);
try {
Intent intent = new Intent();
intent.setAction(Intent.ACTION_VIEW);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
intent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
intent.setDataAndType(docUri, "application/pdf");
} else {
intent.setDataAndType(Uri.fromFile(file), "application/pdf");
}
activity.startActivityForResult(intent, ACTIVITY_VIEW_ATTACHMENT);
} catch (ActivityNotFoundException e) {
e.printStackTrace();
}
}
Step5: Call the openPDF function on click on the view in Activity
myView.setOnClickListener(v -> ApplicationUtils.openPDF(this, "MyPdfFile.pdf"));
I had success, using the answer from cYrixmorten, with the OpenLocalPDF class.
But, Environment.DIRECTORY_DOCUMENTS is not supported for Android 18. To support Android 18 - 28, I had to make the following changes.
In "CopyFileAsyncTask", change the fileRoot declaration:
final File fileRoot = new File(Environment.getExternalStoragePublicDirectory(
Environment.DIRECTORY_DOCUMENTS), appDirectoryName);
To this:
final File fileRoot = new File(Environment.getExternalStorageDirectory() + "/YourAppName");
In "onPostExecute", change this:
Intent intent = new Intent(Intent.ACTION_VIEW);
intent.setDataAndType(
Uri.fromFile(file),
"application/pdf");
context.startActivity(intent);
To this:
Intent intent = new Intent(Intent.ACTION_VIEW);
intent.setDataAndType(FileProvider.getUriForFile(context, "com.example.yourappname.provider", file), "application/pdf");
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
context.startActivity(intent);
Finally, setup your manifest to support the FileProvider as described in this post.
try this:
public String getAssetsPdfPath(Context context) {
String filePath = context.getFilesDir() + File.separator + "myFile.pdf";
File destinationFile = new File(filePath);
try {
FileOutputStream outputStream = new FileOutputStream(destinationFile);
InputStream inputStream = context.getAssets().open("myFile.pdf");
byte[] buffer = new byte[1024];
int length = 0;
while ((length = inputStream.read(buffer)) != -1) {
outputStream.write(buffer, 0, length);
}
outputStream.close();
inputStream.close();
} catch (IOException e) {
Log.e(context.getClass().getSimpleName(), "Error.");
}
return destinationFile.getPath();
}
If you want to open a .pdf file that is stored locally in the assets folder without using an Intent to launch an external app, I suggest using the Android class PdfRenderer. Documentation found here.
This is a good example that worked for me.
However, this example wouldn't run when I downloaded it. I had to change it a bit to use the copyReadAssets() function as mentioned in the other answers here, then to reference the file (after it has been copied) I use:
File file = new File("/data/data/" + getContext().getPackageName() + "/files/mypdf.pdf");
I also ended up modifying onAttach() because it was using the deprecated form of onAttach() and closeRenderer() because it was throwing errors for closing objects that were null.
So my complete PdfRendererBasicFragment.java file looks like this:
package com.example.android.pdfrendererbasic;
import android.app.Activity;
import android.app.Fragment;
import android.content.Context;
import android.content.res.AssetManager;
import android.graphics.Bitmap;
import android.graphics.pdf.PdfRenderer;
import android.os.Bundle;
import android.os.ParcelFileDescriptor;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.Toast;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
/**
* This fragment has a big {#ImageView} that shows PDF pages, and 2 {#link android.widget.Button}s to move between
* pages. We use a {#link android.graphics.pdf.PdfRenderer} to render PDF pages as {#link android.graphics.Bitmap}s.
*/
public class PdfRendererBasicFragment extends Fragment implements View.OnClickListener {
/**
* Key string for saving the state of current page index.
*/
private static final String STATE_CURRENT_PAGE_INDEX = "current_page_index";
/**
* File descriptor of the PDF.
*/
private ParcelFileDescriptor mFileDescriptor;
/**
* {#link android.graphics.pdf.PdfRenderer} to render the PDF.
*/
private PdfRenderer mPdfRenderer;
/**
* Page that is currently shown on the screen.
*/
private PdfRenderer.Page mCurrentPage;
/**
* {#link android.widget.ImageView} that shows a PDF page as a {#link android.graphics.Bitmap}
*/
private ImageView mImageView;
/**
* {#link android.widget.Button} to move to the previous page.
*/
private Button mButtonPrevious;
/**
* {#link android.widget.Button} to move to the next page.
*/
private Button mButtonNext;
public PdfRendererBasicFragment() {
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
return inflater.inflate(R.layout.fragment_pdf_renderer_basic, container, false);
}
#Override
public void onViewCreated(View view, Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
// Retain view references.
mImageView = (ImageView) view.findViewById(R.id.image);
mButtonPrevious = (Button) view.findViewById(R.id.previous);
mButtonNext = (Button) view.findViewById(R.id.next);
// Bind events.
mButtonPrevious.setOnClickListener(this);
mButtonNext.setOnClickListener(this);
// Show the first page by default.
int index = 0;
// If there is a savedInstanceState (screen orientations, etc.), we restore the page index.
if (null != savedInstanceState) {
index = savedInstanceState.getInt(STATE_CURRENT_PAGE_INDEX, 0);
}
showPage(index);
}
#Override
public void onAttach(Context context) {
super.onAttach(context);
try {
openRenderer(context);
} catch (IOException e) {
e.printStackTrace();
Toast.makeText(context, "Error! " + e.getMessage(), Toast.LENGTH_SHORT).show();
getActivity().finish();
}
}
#Override
public void onDetach() {
try {
closeRenderer();
} catch (IOException e) {
e.printStackTrace();
}
super.onDetach();
}
#Override
public void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
if (null != mCurrentPage) {
outState.putInt(STATE_CURRENT_PAGE_INDEX, mCurrentPage.getIndex());
}
}
/**
* Sets up a {#link android.graphics.pdf.PdfRenderer} and related resources.
*/
private void openRenderer(Context context) throws IOException {
// Copy the pdf to a usable location
CopyReadAssets();
File file = new File("/data/data/" + context.getPackageName() + "/files/sample.pdf");
mPdfRenderer = new PdfRenderer(ParcelFileDescriptor.open(file, ParcelFileDescriptor.MODE_READ_ONLY));
}
/**
* Closes the {#link android.graphics.pdf.PdfRenderer} and related resources.
*
* #throws java.io.IOException When the PDF file cannot be closed.
*/
private void closeRenderer() throws IOException {
if (mCurrentPage != null) {
mCurrentPage.close();
}
if (mPdfRenderer != null) {
mPdfRenderer.close();
}
if (mFileDescriptor != null) {
mFileDescriptor.close();
}
}
/**
* Shows the specified page of PDF to the screen.
*
* #param index The page index.
*/
private void showPage(int index) {
if (mPdfRenderer.getPageCount() <= index) {
return;
}
// Make sure to close the current page before opening another one.
if (null != mCurrentPage) {
mCurrentPage.close();
}
// Use `openPage` to open a specific page in PDF.
mCurrentPage = mPdfRenderer.openPage(index);
// Important: the destination bitmap must be ARGB (not RGB).
Bitmap bitmap = Bitmap.createBitmap(mCurrentPage.getWidth(), mCurrentPage.getHeight(),
Bitmap.Config.ARGB_8888);
// Here, we render the page onto the Bitmap.
// To render a portion of the page, use the second and third parameter. Pass nulls to get
// the default result.
// Pass either RENDER_MODE_FOR_DISPLAY or RENDER_MODE_FOR_PRINT for the last parameter.
mCurrentPage.render(bitmap, null, null, PdfRenderer.Page.RENDER_MODE_FOR_DISPLAY);
// We are ready to show the Bitmap to user.
mImageView.setImageBitmap(bitmap);
updateUi();
}
/**
* Updates the state of 2 control buttons in response to the current page index.
*/
private void updateUi() {
int index = mCurrentPage.getIndex();
int pageCount = mPdfRenderer.getPageCount();
mButtonPrevious.setEnabled(0 != index);
mButtonNext.setEnabled(index + 1 < pageCount);
getActivity().setTitle(getString(R.string.app_name_with_index, index + 1, pageCount));
}
/**
* Gets the number of pages in the PDF. This method is marked as public for testing.
*
* #return The number of pages.
*/
public int getPageCount() {
return mPdfRenderer.getPageCount();
}
#Override
public void onClick(View view) {
switch (view.getId()) {
case R.id.previous: {
// Move to the previous page
showPage(mCurrentPage.getIndex() - 1);
break;
}
case R.id.next: {
// Move to the next page
showPage(mCurrentPage.getIndex() + 1);
break;
}
}
}
private void CopyReadAssets()
{
AssetManager assetManager = getActivity().getAssets();
InputStream in = null;
OutputStream out = null;
File file = new File(getActivity().getFilesDir(), "sample.pdf");
if(!file.exists()) {
try {
in = assetManager.open("sample.pdf");
out = getActivity().openFileOutput(file.getName(), Context.MODE_WORLD_READABLE);
copyFile(in, out);
in.close();
in = null;
out.flush();
out.close();
out = null;
} catch (Exception e) {
Log.e("tag", e.getMessage());
}
}
else {
Log.d("test", "file already exists");
}
}
private void copyFile(InputStream in, OutputStream out) throws IOException
{
byte[] buffer = new byte[1024];
int read;
while ((read = in.read(buffer)) != -1)
{
out.write(buffer, 0, read);
}
}
}
This code work on every android version:
Activity:
import android.app.Activity;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.content.res.AssetManager;
import android.net.Uri;
import android.os.Environment;
import android.os.Bundle;
import android.support.v4.app.ActivityCompat;
import android.support.v4.content.ContextCompat;
import android.support.v4.content.FileProvider;
import android.util.Log;
import android.widget.Toast;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import unimedbh.UnimedBH.R;
public class MainActivity extends Activity {
private static final int MY_PERMISSION_REQUEST_STORAGE = 1;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
if (ContextCompat.checkSelfPermission(MainActivity.this, android.Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) {
ActivityCompat.requestPermissions(MainActivity.this, new String[]{android.Manifest.permission.WRITE_EXTERNAL_STORAGE}, MY_PERMISSION_REQUEST_STORAGE);
} else {
copyReadAssets("YOUR_PDF_NAME.pdf");
}
}
private void copyReadAssets(String fileName) {
String dirPath = Environment.getExternalStorageDirectory().getAbsolutePath() + "/MyFiles";
File dir = new File(dirPath);
if (!dir.exists()) {
dir.mkdirs();
}
AssetManager assetManager = getAssets();
InputStream in = null;
OutputStream out = null;
try {
in = assetManager.open(fileName);
File outFile = new File(dirPath, fileName);
out = new FileOutputStream(outFile);
copyFile(in, out);
Intent intent = new Intent(Intent.ACTION_VIEW, FileProvider.getUriForFile(this, "com.package.name.fileprovider", outFile));
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_ACTIVITY_NO_HISTORY);
startActivity(intent);
} catch (IOException e) {
e.printStackTrace();
Toast.makeText(this, "Error!", Toast.LENGTH_SHORT).show();
} finally {
if (in != null) {
try {
in.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (out != null) {
try {
out.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
private void copyFile(InputStream in, OutputStream out) throws IOException {
byte[] buffer = new byte[1024];
int read;
while ((read = in.read(buffer)) != -1) {
out.write(buffer, 0, read);
}
}
}
Manifest.xml:
<provider
android:name="android.support.v4.content.FileProvider"
android:authorities="com.package.name.fileprovider"
android:exported="false"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="#xml/file_paths" />
</provider>
file_paths.xml:
<paths>
<files-path name="app_directory" path="directory/"/>
</paths>
Tested in API 16-28, worked on all api´s!