kuznikowski
kuznikowski

Reputation: 563

Android app can't read external storage files

I'm creating an app for a private use only... it's not so important then, respect your time :P

What I want to make is an app that reads from a database and plays sounds. I've already made the database and I am able to play sound from res/raw folder. However, there are thousands of files to play and some new ones will be a part of the app and other will be deleted in the future. So, adding a simple file makes me to download the whole app (~1 GB) each time (or maybe I'm wrong?). Instead, I thought of reading an Excel file and playing sounds from SD card then. But I can't... as far as i know:

Apps are not allowed to write (delete, modify ...) to external storage except to their package-specific directories.

I put the files into the /storage/emulated/0/Android/data/my_package_folder/files folder. I've read really many topics and nothing helped me.

Environment.MEDIA_MOUNTED.equals(extStorageState) //returns true
Environment.MEDIA_MOUNTED_READ_ONLY.equals(extStorageState) //returns false

I tried a few ways of creating new file object or playing the sound with the mediaplayer (as I've said before, there's no problem with playing res/raw files) but file.exists always returns false or sound doesn't play:

String filePath = getExternalFilesDir(null) + "/myfile.mp3"; //returns correct file path
File file = new File(filePath);

OR

File file = new File(getExternalFilesDir(null).getPath(), "myfile.mp3");

OR

MediaPlayer mp;
mp = MediaPlayer.create(this, Uri.parse(getExternalFilesDir(null).getPath() + "/myfile.mp3"));

The minSdkVersion is set to 19 so I don't need that line in Manifest:

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

I have included that line and nothing changed:

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

I was also trying to do that with other file extensions. Also, the solution from here didn't help. My phone is Samsung Galaxy Grand Prime (SM-G530FZ), Android v. 5.0.2. Help, please! :)

Upvotes: 5

Views: 19882

Answers (5)

Android 9 (API level 28) started to restrict which apps could make the files in their data directories on internal storage world-accessible to other apps. Apps that target Android 9 or higher cannot make the files in their data directories world-accessible. For further info you can check this link.

Incase you're targeting the API level 28 or higher, to solve your problem you can simply add some tags inside your AndroidManifest.xml. So it should have at least those ones:

<?xml version="1.0" encoding="utf-8"?>
<manifest
   xmlns:android="http://schemas.android.com/apk/res/android"
   xmlns:tools="http://schemas.android.com/tools"
   package="com.your.application">

   <!-- These three tags -->
   <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
   <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
   <uses-permission android:name="android.permission.WRITE_INTERNAL_STORAGE"/>

   <!-- Add the tag android:requestLegacyExternalStorage="true" in application -->
   <!-- Other tags has been omitted -->
   <application
      android:requestLegacyExternalStorage="true">
   </application>

</manifest>

Now you can check if you are able to read and write:

File root = new File(Environment.getExternalStorageDirectory() + "/My app folder");

Log.d("Storage: can write", String.valueOf(root.canWrite()));
Log.d("Storage: can read", String.valueOf(root.canRead()));

The result of both logs should be TRUE.

Upvotes: 0

Harry Sandal
Harry Sandal

Reputation: 51

 MyMediaPlayer=MediaPlayer.create(this,Uri.parse(String.valueOf(Uri.fromFile(f))));

in this MyMediaPlayer is object of MediaPlayer,||ly f is object of file in this we convert to uri first and then uri to string and finally string to uri.

I have done this because when we take the path of our choise it take /// 3times / but it is needed only 2 times thats why I have done this conversion which help me to take the desired file path.

try this my friend.

Upvotes: 0

kuznikowski
kuznikowski

Reputation: 563

Here is the code @Nisarg, had to clean it a bit:

import android.Manifest;
import android.content.pm.PackageManager;
import android.media.MediaPlayer;
import android.os.Build;
import android.os.Bundle;
import android.os.Environment;
import android.support.v4.app.ActivityCompat;
import android.support.v4.content.ContextCompat;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.Toast;

import java.io.File;

public class MainActivity extends AppCompatActivity implements OnClickListener {

Button readExcelButton;
static String TAG = "ExelLog";
MediaPlayer mpintro;

@Override
public void onCreate(Bundle savedInstanceState)
{
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

    if (Build.VERSION.SDK_INT == Build.VERSION_CODES.M) {
        Toast.makeText(this, "Permission checking", Toast.LENGTH_SHORT).show();
        checkPermission();
    }

    readExcelButton = (Button) findViewById(R.id.readExcel);
    readExcelButton.setOnClickListener(this);

}

public void onClick(View v)
{
    switch (v.getId())
    {
        case R.id.readExcel:
            String filePath = getExternalFilesDir(null) + "/p0000.mp3";
            File file = new File(filePath);
            file.setReadable(true);
            file.setWritable(true);

            if (!isExternalStorageAvailable() || isExternalStorageReadOnly()) {
                Toast.makeText(this, "Ext storage not available or read only", Toast.LENGTH_SHORT).show();
            }
            else {
                Toast.makeText(this, "Ext storage available", Toast.LENGTH_SHORT).show();
            }

            if (file.exists()){
                Toast.makeText(this, "File found", Toast.LENGTH_SHORT).show();
            }
            else {
                Toast.makeText(this, getExternalFilesDir(null) + "/p0000.mp3 - File not found", Toast.LENGTH_LONG).show();
            }

            break;
    }
}

private void checkPermission() {
    if (ContextCompat.checkSelfPermission(this,
            Manifest.permission.WRITE_EXTERNAL_STORAGE)
            != PackageManager.PERMISSION_GRANTED && ContextCompat.checkSelfPermission(this,
            Manifest.permission.READ_EXTERNAL_STORAGE)
            != PackageManager.PERMISSION_GRANTED) {//Can add more as per requirement

        ActivityCompat.requestPermissions(this,
                new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE,Manifest.permission.READ_EXTERNAL_STORAGE},
                123);

    } else {

    }
}

@Override
public void onRequestPermissionsResult(int requestCode, String permissions[], int[] grantResults) {
    switch (requestCode) {
        case 123: {

            if (grantResults.length > 0
                    && grantResults[0] == PackageManager.PERMISSION_GRANTED)     {
                //Peform your task here if any
            } else {

                checkPermission();
            }
            return;
        }
    }
}

public static boolean isExternalStorageReadOnly() {
    String extStorageState = Environment.getExternalStorageState();
    if (Environment.MEDIA_MOUNTED_READ_ONLY.equals(extStorageState)) {
        return true;
    }
    return false;
}

public static boolean isExternalStorageAvailable() {
    String extStorageState = Environment.getExternalStorageState();
    if (Environment.MEDIA_MOUNTED.equals(extStorageState)) {
        return true;
    }
    return false;
}

}

Upvotes: 5

Nisarg
Nisarg

Reputation: 1388

if your targetSdk is 23 than you have to check permissions like this for marshmallow and above devices

if (Build.VERSION.SDK_INT == Build.VERSION_CODES.M) {
    checkPermission();
}
else {
}


private void checkPermission() {
        if (ContextCompat.checkSelfPermission(this,
                Manifest.permission.WRITE_EXTERNAL_STORAGE)
                != PackageManager.PERMISSION_GRANTED && ContextCompat.checkSelfPermission(this,
            Manifest.permission.READ_EXTERNAL_STORAGE)
            != PackageManager.PERMISSION_GRANTED) {//Can add more as per requirement

            ActivityCompat.requestPermissions(this,
                    new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE,Manifest.permission.READ_EXTERNAL_STORAGE},
                    123);

        } else {

        }
}

@Override
public void onRequestPermissionsResult(int requestCode,
                                   String permissions[], int[] grantResults)   
   {
    switch (requestCode) {
        case 123: {


 if (grantResults.length > 0
                    && grantResults[0] == PackageManager.PERMISSION_GRANTED)     {
   //Peform your task here if any
    } else {

        checkPermission();
    }
        return;
    }
}
}

Upvotes: 2

Zoe - Save the data dump
Zoe - Save the data dump

Reputation: 28298

You also have to have the permission READ_EXTERNAL_STORAGE; Either write or read is not enough when it comes to external storage, as you have to use both

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

You can also read more about external data writing in the declaration

The reason you cannot read is because you didn't add that permission. A good rule is if you only need to read, only use read permission. But if you need to write you need the write and the read permission

EDIT:

When loading the directory, try this:

file.setReadable(true);
file.setWritable(true);

Note: the value 'file' here is for the directory, not each separate file(all though that works fine too)

From the declaration:

This permission is enforced starting in API level 19. Before API level 19, this permission is not enforced and all apps still have access to read from external storage.

If I am correct that means the permission has to be there from API 19 and up, but not before API 19

Upvotes: 1

Related Questions