[JAVA] Easy implementation of Android file browsing

Introduction

Android didn't have a standard file browsing UI, so it had to be implemented individually. The new storage access framework made it easy to implement reading and writing files, so I'll remind you. You can access Google Drive, SD card, and internal shared storage. I created a sample application that loads, edits, and saves a text file.

Implementation

"Basic Activity" was selected as the Activity, and all the code was written in the Main Activity. The project name is "FileSample". The description related to FloatingButton was deleted.

Java code description

MainActivity.java


package xx.xx.xx.xx.filesample;

import android.content.ClipData;
import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;
import android.provider.DocumentsContract;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.Toolbar;
import android.view.Menu;
import android.view.MenuItem;
import android.view.MotionEvent;
import android.view.View;
import android.view.WindowManager;
import android.view.inputmethod.InputMethodManager;
import android.widget.EditText;
import android.widget.Toast;

import java.io.ByteArrayOutputStream;
import java.io.FileNotFoundException;
import java.io.InputStream;
import java.io.OutputStream;

public class MainActivity extends AppCompatActivity implements View.OnTouchListener {

    static final int REQUEST_OPEN_FILE = 1001;
    static final int REQUEST_CREATE_FILE = 1002;
    static final int REQUEST_DELETE_FILE = 1003;
    enum Mode {OPEN, CREATE, DELETE};
    EditText editText;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
        setSupportActionBar(toolbar);
        editText = findViewById(R.id.edit_text);
        editText.setOnTouchListener(this);
    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        // Inflate the menu; this adds items to the action bar if it is present.
        getMenuInflater().inflate(R.menu.menu_main, menu);
        return true;
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        // Handle action bar item clicks here. The action bar will
        // automatically handle clicks on the Home/Up button, so long
        // as you specify a parent activity in AndroidManifest.xml.
        int id = item.getItemId();

        switch (id) {
            case R.id.action_load:
                startFileBrowser(Mode.OPEN);
                return true;
            case R.id.action_save:
                startFileBrowser(Mode.CREATE);
                return true;
            case R.id.action_delete:
                startFileBrowser(Mode.DELETE);
                return true;
        }

        return super.onOptionsItemSelected(item);
    }

    private void startFileBrowser(Mode mode) {
        Intent intent = null;
        try {
            switch (mode) {
                case OPEN:
                    intent = new Intent(Intent.ACTION_OPEN_DOCUMENT);
                    intent.setType("text/plain");   //TEXT file only
                    intent.addCategory(Intent.CATEGORY_OPENABLE);
                    startActivityForResult(Intent.createChooser(intent, "Open a file"), REQUEST_OPEN_FILE);
                    break;
                case CREATE:
                    intent = new Intent(Intent.ACTION_CREATE_DOCUMENT);
                    intent.setType("text/plain");   //TEXT file only
                    intent.putExtra(Intent.EXTRA_TITLE, "newfile.txt");
                    intent.addCategory(Intent.CATEGORY_OPENABLE);
                    startActivityForResult(Intent.createChooser(intent, "Create a file"), REQUEST_CREATE_FILE);
                    break;
                case DELETE:
                    intent = new Intent(Intent.ACTION_OPEN_DOCUMENT);
                    intent.setType("text/plain");   //TEXT file only
                    intent.addCategory(Intent.CATEGORY_OPENABLE);
                    intent.putExtra(Intent.EXTRA_ALLOW_MULTIPLE, true);
                    startActivityForResult(Intent.createChooser(intent, "Delete a file"), REQUEST_DELETE_FILE);
                    break;
                default:
            }
        } catch (android.content.ActivityNotFoundException ex) {
            // Potentially direct the user to the Market with a Dialog
            Toast.makeText(this, "Please install a File Browser/Manager", Toast.LENGTH_LONG).show();
        }
    }

    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        super.onActivityResult(requestCode, resultCode, data);

        // File load
        if (requestCode == REQUEST_OPEN_FILE) {
            if (resultCode == RESULT_OK && data != null) {
                Uri uri = data.getData();
                if (uri != null) {
                    editText.setText(loadStrFromUri(uri));
                }
            }
        }
        // File save
        else if (requestCode == REQUEST_CREATE_FILE) {
            if (resultCode == RESULT_OK && data != null) {
                Uri uri = data.getData();
                if (uri != null) {
                    saveStrToUri(uri, editText.getText().toString());
                }
            }
        }
        // File delete
        else if (requestCode == REQUEST_DELETE_FILE) {
            if (resultCode == RESULT_OK && data != null) {
                ClipData clipData = data.getClipData();
                if(clipData==null){  // single selection
                    Uri uri = data.getData();
                    deleteUri(uri);
                }else {  // multiple selection
                    for (int i = 0; i < clipData.getItemCount(); i++) {
                        Uri uri = clipData.getItemAt(i).getUri();
                        deleteUri(uri);
                    }
                }
            }
        }
    }

    String loadStrFromUri(Uri uri) {
        String str = "";
        Boolean loop=true;
        try {
            if (uri.getScheme().equals("content")) {
                InputStream iStream = getContentResolver().openInputStream(uri);
                if(iStream==null) loop=false;
                ByteArrayOutputStream bout = new ByteArrayOutputStream();
                byte[] buffer = new byte[1024];
                while (loop) {
                    int len = iStream.read(buffer);
                    if (len < 0) break;
                    bout.write(buffer, 0, len);
                }
                str = bout.toString();
            } else {
                Toast.makeText(this, "Unknown scheme", Toast.LENGTH_LONG).show();
            }
        } catch (Exception e) {
            Toast.makeText(this, "Cannot read the file:" + e.toString(), Toast.LENGTH_LONG).show();
        }
        return str;
    }

    void saveStrToUri(Uri uri, String str) {
        try {
            if (uri.getScheme().equals("content")) {
                OutputStream oStream = getContentResolver().openOutputStream(uri);
                if(oStream!=null) oStream.write(str.getBytes());
            } else {
                Toast.makeText(this, "Unknown scheme", Toast.LENGTH_LONG).show();
            }
        } catch (Exception e) {
            Toast.makeText(this, "Cannot write the file:" + e.toString(), Toast.LENGTH_LONG).show();
        }
    }

    void deleteUri(Uri uri) {
        try {
            DocumentsContract.deleteDocument(getContentResolver(), uri);
        } catch(FileNotFoundException e){
            Toast.makeText(this, "Cannot find the file:" + e.toString(), Toast.LENGTH_LONG).show();
        }
    }

    //Below is the code to redisplay the virtual keyboard with touch.
    @Override
    public boolean onTouch(View view, MotionEvent motionEvent) {
        view.performClick();
        switch (motionEvent.getAction()) {
            case MotionEvent.ACTION_DOWN:
                if(view == editText) showKeyboard();
                break;
        }
        return false;
    }
    void showKeyboard() {
        InputMethodManager inputMethodManager = (InputMethodManager) getSystemService(INPUT_METHOD_SERVICE);
        if(inputMethodManager != null) inputMethodManager.showSoftInput(editText, InputMethodManager.SHOW_IMPLICIT);
        getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_VISIBLE);
    }
}

Calling Intent.ACTION_OPEN_DOCUMENT or Intent.ACTION_CREATE_DOCUMENT brings up the file browsing UI. When the user selects a text file in the UI, the corresponding URI is returned, which is used to read and write the file.

Layout file description

In "Basic Activity", two layout files "activity_main.xml" and "content_main.xml" are created corresponding to MainActivity.java. Among them, describe content_main.xml as follows and display the text display / edit UI in full screen.

content_main.xml


<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    app:layout_behavior="@string/appbar_scrolling_view_behavior"
    tools:context=".MainActivity"
    tools:showIn="@layout/activity_main">

    <ScrollView
        android:layout_width="match_parent"
        android:layout_height="match_parent">
        <EditText
            android:id="@+id/edit_text"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:inputType="textMultiLine"
            android:textIsSelectable="true" />
    </ScrollView>

</android.support.constraint.ConstraintLayout>

Menu description

You can select READ and WRITE for files in the main menu. Select READ to read the text from the file and display it on the screen. Select WRITE to write the text displayed on the screen to a file.

menu_main.xml


<menu xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    tools:context="me.osdn.users.watanaby.filesample.MainActivity">
    <item
        android:id="@+id/action_load"
        android:orderInCategory="100"
        android:title="Load"
        app:showAsAction="never" />
    <item
        android:id="@+id/action_save"
        android:orderInCategory="100"
        android:title="Save"
        app:showAsAction="never" />
    <item
        android:id="@+id/action_delete"
        android:orderInCategory="100"
        android:title="Delete"
        app:showAsAction="never" />
</menu>

screenshot

Method of operation

When you start the app, the edit screen is displayed. Call Load, Save, and Delete from the menu icon at the top right of the screen (Screenshot 1). On the file browse screen, you can select storage from the menu at the top left (screenshots 2 and 3). In Save, you can either key in the file name to create a new one, or touch the existing file name to rewrite it (Screenshot 4). With Load and Delete, the key input field does not appear (Screenshot 5). With Delete, you can select multiple files by long-pressing (Screenshot 6). If you touch "Open" at the top of the screen after selecting, the deletion will be executed.

Recommended Posts

Easy implementation of Android file browsing
[Android] Implementation of side-scrolling ListView using RecyclerView
Implementation of GKAccessPoint
Easy way to create an implementation of java.util.stream.Stream
Implementation of flash messages
Implementation of search function
Applied implementation of chat-space
Implementation of pagination function
Utilization of seed file
Definition of Android constants
Rails implementation of ajax removal
[Swift] Simple implementation of UIImageView
[Swift] Implementation of ultra-simple billing
Implementation of like function (Ajax)
[Rails 6] Implementation of search function
Implementation of image preview function
[Java] Implementation of Faistel Network
Implementation of XLPagerTabStrip with TabBarController
[Rails] Implementation of category function
Android library collection of "Mercariatte"
Implementation of category pull-down function
Implementation of unit test code
Implementation of gzip in java
[Java] [Android] Read ini file
[Rails] Implementation of tutorial function
[Rails] Implementation of like function
List of hosts file locations
Implementation of tri-tree in Java
Implementation of HashMap in kotlin