Arnauld Alex
Arnauld Alex

Reputation: 349

Internal or External storage for editable app-specific file

I am creating an app that requier an app-specific file that I called "conf.cfg" for example. this file need to be read by my application to create some object etc... the body of the file look like that :

#activation, level, type, regex or array 0, "critic", 0,"\\d{4}\\w\\d{3}" 1, "critic", 1, [word1,word2] 1,"minor", 0,"\\d{2}-\\w{3}-\\d{4}\\s?\\/?\\s?\\d{2}:\\d{2}"

Doing my research I found that there is two type of Storage in android :

  1. Internal storage :

Internal storage is best when you want to be sure that neither the user nor other apps can access your files.

  1. External storae :

External storage is the best place for files that don't require access restrictions and for files that you want to share with other apps or allow the user to access with a computer.

As I want the user to be able to edit/download/upload/USE this file, External Storage seems to be a good choice. However on Developper Android they said :

Caution: The external storage might become unavailable if the user removes the SD card or connects the device to a computer. And the files are still visible to the user and other apps that have the READ_EXTERNAL_STORAGE permission. So if your app's functionality depends on these files or you need to completely restrict access, you should instead write your files to the internal storage.

Caution: Files on external storage are not always accessible, because users can mount the external storage to a computer for use as a storage device. So if you need to store files that are critical to your app's functionality, you should instead store them on internal storage.

As this file need to be always available and is critical to my app's functionality So... Internal Storage seems to be better. But I need the user to see and be able to use the file. And here I'm stuck.

Anyone has an idea of where and how to put/create this file ?

EDIT : following @greenapps answer

heer is a piece of code I've wrote. I use the getExternalFilesDir(null) command to write and store my file

    String folderName = "Innovation";
    String confFileName = "conf.txt";
    String commentSymbol = "#";
    String commentLine = commentSymbol + " activation, level, type , regex or array";

    File storage = getExternalFilesDir(null);
    File folder = new File(storage, folderName);
    File conf = new File(folder, confFileName);

    Log.d(TAG, "Folder action!");
    if (!folder.exists()) {
        if (folder.mkdirs()) {
            Log.d(TAG, "Created : " + folder.getAbsolutePath());
        } else {
            Log.e(TAG, "folder not created!");
        }
    }

    Log.d(TAG, "File action!");
    if (!conf.exists()) {
        try {
            Log.d(TAG, "opening...");
            FileOutputStream fos = new FileOutputStream(conf);
            fos.write(commentLine.getBytes());
            fos.close();
            Log.d(TAG, "Created : " + conf.getAbsolutePath());
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    if (conf.exists()) {
        Log.d(TAG, "File exist at : " + conf.getAbsolutePath());
    }

the file is created, as shown by the last log

Created : /storage/emulated/0/Android/data/com.aralex.innovation/files/Innovation/conf.txt

But when I search the file with the native file explorer application of the phone, I can't find it. I can go to the file folder but the folder "Innovation/" is hidden.

This is a problem because I want the file to be visible.

Phone : Samsung s7, s7edge, s9+

Default File Explorer Icon

Default File Explorer Oppened

Upvotes: 2

Views: 2726

Answers (2)

Arnauld Alex
Arnauld Alex

Reputation: 349

Well I finally found an answer myself.

On this post Android create folders in Internal Memory @prodev specify that Environment.getExternalStorageDirectory() is a good place, because the file will be accessible and :

note that ExternalStorage in Environment.getExternalStorageDirectory() does not necessarily refers to sdcard, it returns phone primary storage memory

it requires permissions (only for build version >= M) :

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

So here is a code to answer my problem (it ask permission on runtime) :

private ArrayList<Rule> ruleList; 

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);

    [...]

    // Check for the storage permission before accessing the camera.  If the
    // permission is not granted yet, request permission.
    if (hasPermissions(this, Manifest.permission.WRITE_EXTERNAL_STORAGE, Manifest.permission.READ_EXTERNAL_STORAGE)
            || Build.VERSION.SDK_INT < Build.VERSION_CODES.M) {
        ruleList = createRules();
    } else {
        requestStoragePermission();
    }
}

private boolean hasPermissions(Context context, String... permissions) {
    if (context != null && permissions != null) {
        for (String permission : permissions) {
            Log.d(TAG, "Checking permission : " + permission);
            if (ActivityCompat.checkSelfPermission(context, permission) != PackageManager.PERMISSION_GRANTED) {
                Log.w(TAG, "not granted : " + permission);
                return false;
            } else {
                Log.d(TAG, "granted : " + permission);
            }
        }
    }
    return true;
}

/**
 * Handles the requesting of the storage permission.  This includes
 * showing a "Snackbar" errorMessage of why the permission is needed then
 * sending the request.
 */
private void requestStoragePermission() {
    Log.w(TAG, "Storage permission is not granted. Requesting permission");

    final String[] permissions = new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE, Manifest.permission.READ_EXTERNAL_STORAGE};

    if (!ActivityCompat.shouldShowRequestPermissionRationale(this,
            Manifest.permission.WRITE_EXTERNAL_STORAGE)) {
        ActivityCompat.requestPermissions(this, permissions, RC_HANDLE_EXTERNAL_PERM);
        return;
    }

    final Activity thisActivity = this;

    View.OnClickListener listener = view -> ActivityCompat.requestPermissions(thisActivity, permissions,
            RC_HANDLE_EXTERNAL_PERM);

    Snackbar.make(findViewById(android.R.id.content), R.string.permission_storage_rationale,
            Snackbar.LENGTH_INDEFINITE)
            .setAction(R.string.ok, listener)
            .show();
}

@Override
public void onRequestPermissionsResult(int requestCode,
                                       @NonNull String[] permissions,
                                       @NonNull int[] grantResults) {
    if (requestCode != RC_HANDLE_EXTERNAL_PERM) {
        Log.d(TAG, "Got unexpected permission result: " + requestCode);
        super.onRequestPermissionsResult(requestCode, permissions, grantResults);
        return;
    }

    if (grantResults.length != 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED
            && grantResults[1] == PackageManager.PERMISSION_GRANTED) {
        Log.d(TAG, "Storage permission granted");
        // We have permission
        ruleList = createRules();
        return;
    }

    Log.e(TAG, "Permission not granted: results len = " + grantResults.length +
            " Result code = " + (grantResults.length > 1 ? grantResults[0] + " " + grantResults[1] : grantResults.length > 0 ? grantResults[0] : "(empty)"));

    DialogInterface.OnClickListener listener = (dialog, id) -> finish();

    AlertDialog.Builder builder = new AlertDialog.Builder(this);
    builder.setTitle("Assisting Tool")
            .setMessage(R.string.no_storage_permission)
            .setPositiveButton(R.string.ok, listener)
            .show();
}

private ArrayList<Rule> createRules() {
    Log.d(TAG, "=========================READING FILE======================");

    ArrayList<Rule> ruleList = new ArrayList<>();

    String folderName = "Innovation";
    String confFileName = "conf.txt";
    String commentSymbol = "#";
    String commentLine = commentSymbol + " activation, level, type , regex or array";

    File storage = Environment.getExternalStorageDirectory();
    File folder = new File(storage, folderName);
    File conf = new File(folder, confFileName);

    Log.d(TAG, "Folder action!");
    if (!folder.exists()) {
        if (folder.mkdirs()) {
            Log.d(TAG, "Created : " + folder.getAbsolutePath());
        } else {
            Log.e(TAG, "folder not created!");
        }
    }

    Log.d(TAG, "File action!");
    if (!conf.exists()) {
        try {
            Log.d(TAG, "opening...");
            FileOutputStream fos = new FileOutputStream(conf);
            fos.write(commentLine.getBytes());
            fos.close();
            Log.d(TAG, "Created : " + conf.getAbsolutePath());
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    if (conf.exists()) {
        Log.d(TAG, "File exist at : " + conf.getAbsolutePath());
    } else {
        Log.e(TAG, "The file doesn't exist...");
    }
}

Now it create a app-specific file

/storage/emulated/0/Innovation/conf.txt

that is accessible by user !

Upvotes: 1

greenapps
greenapps

Reputation: 11224

So external storage.

No internal as file explorers have no access to your apps private internal memory.

You could use getExternalFilesDir(null) as then you dont need read and write permissions

Upvotes: 0

Related Questions