2

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

Arnauld Alex
  • 339
  • 1
  • 3
  • 13
  • Internal or external storage are both possible. All devices have it. They have nothing to do with a removable micro SD card. You readed an old doc. – greenapps Jun 01 '18 at 08:42
  • but it says : **The external storage might become unavailable if the user removes the SD card or connects the device to a computer** – Arnauld Alex Jun 01 '18 at 08:44
  • What is unclear in your story is how this file would land on my device if i installed your app on it. – greenapps Jun 01 '18 at 08:45
  • Yes you readed an old doc. External storage is always available. Please reread my comment. – greenapps Jun 01 '18 at 08:46
  • If it's the first installation then the file is created with only the first line (the one that begins by "#"). Then user have to be able to edit this file (with an other app on his phone, like a simple notepad) or be able to go with his phone file explorer to the file, replace it etc... like a file simple file on a computer – Arnauld Alex Jun 01 '18 at 08:48
  • What is your source ? hwo the external is always available ? "Yes you readed an old doc. External storage is always available." – Arnauld Alex Jun 01 '18 at 08:49
  • Well do you have an Android device? Then you can check for yourself. If it has a micro SD card then remove it to be sure. Reread my first comment. It tells you how it is. – greenapps Jun 01 '18 at 08:52
  • with other app, my app just read it. The file is basically like a file.txt in your computer. – Arnauld Alex Jun 01 '18 at 09:03
  • `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.`. Do you mean the 'files' folder? What you say makes no sense. Never seen such. Most Android devices have no default file explorer. So it is unclear where you are talking about. You are not naming device, model and Android version. You should yourself install some file explorer apps. – greenapps Jun 04 '18 at 08:42
  • `folder.mkdirs(); Log.d(TAG, "Created : " + folder.getAbsolutePath());`. Wrong! You are not checking the return value. mkdirs() can fail. You are just crying victory then. `if(!mkdirs()) return;`. – greenapps Jun 04 '18 at 08:47
  • Oh... The reason that you cannot find it can be that it is not created. You should add a `fos.close();`. If you add another if(conf.exists()) at the end of all code you could directly check existence. – greenapps Jun 04 '18 at 08:49
  • At the moment if you rerun your code you will again see that toast that cries created. This should have made you think. – greenapps Jun 04 '18 at 08:52
  • I have changed my code like you told me, see the code. the last log is : `File exist at : /storage/emulated/0/Android/data/com.aralex.innovation/files/Innovation/conf.txt` but it is not visible in the explorer (see main post for screenshot of explorer) – Arnauld Alex Jun 04 '18 at 09:05
  • Use a decent file exolorer app. I told you that before. And why dont you tell about how you use your explorer. What are you doing if you try to give the user access? – greenapps Jun 04 '18 at 10:11

2 Answers2

1

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 !

Arnauld Alex
  • 339
  • 1
  • 3
  • 13
  • 1
    All you say makes no sense. getExternalFilesDir() should work too. And without the need for permissions. – greenapps Jun 04 '18 at 10:08
0

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

greenapps
  • 11,154
  • 2
  • 16
  • 19