Skizo-ozᴉʞS Skizo-ozᴉʞS - 2 months ago 6
Android Question

Implementing a File Picker in Android and copying the selected file to another location

I'm trying to implement a File Picker in my Android project. What I've been able to do so far is :

Intent chooseFile;
Intent intent;
chooseFile = new Intent(Intent.ACTION_GET_CONTENT);
chooseFile.setType("*/*");
intent = Intent.createChooser(chooseFile, "Choose a file");
startActivityForResult(intent, PICKFILE_RESULT_CODE);


And then in my
onActivityResult()


switch(requestCode){
case PICKFILE_RESULT_CODE:
if(resultCode==-1){
Uri uri = data.getData();
String filePath = uri.getPath();
Toast.makeText(getActivity(), filePath,
Toast.LENGTH_LONG).show();
}
break;
}


This is opening a file picker, but its not what I want. For example, I want to select a file (.txt), and then get that
File
and then use it. With this code I thought I would get the full path but it doesn't happen; for example I get:
/document/5318/
. But with this path I can't get the file. I've created a method called
PathToFile()
that returns a
File
:

private File PathToFile(String path) {
File tempFileToUpload;
tempFileToUpload = new File(path);
return tempFileToUpload;
}


What I'm trying to do is let the user choose a
File
from anywhere means
DropBox
,
Drive
,
SDCard
,
Mega
, etc... And I don't find the way to do it correctly, I tried to get the
Path
then get a
File
by this
Path
... but it doesn't work, so I think it's better to get the
File
itself, and then with this
File
programmatically I
Copy
this or
Delete
.

EDIT (Current code)



My
Intent


Intent chooseFile = new Intent(Intent.ACTION_GET_CONTENT);
chooseFile.addCategory(Intent.CATEGORY_OPENABLE);
chooseFile.setType("text/plain");
startActivityForResult(
Intent.createChooser(chooseFile, "Choose a file"),
PICKFILE_RESULT_CODE
);


There I've got a question because I don't know what is supported as
text/plain
, but I'm gonna investigate about it, but it doesn't matter at the moment.

On my
onActivityResult()
I've used the same as @Lukas Knuth answer, but I don't know if with it I can
Copy
this
File
to another part from my
SDcard
I'm waitting for his answer.

@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (requestCode == PICKFILE_RESULT_CODE && resultCode == Activity.RESULT_OK){
Uri content_describer = data.getData();
//get the path
Log.d("Path???", content_describer.getPath());
BufferedReader reader = null;
try {
// open the user-picked file for reading:
InputStream in = getActivity().getContentResolver().openInputStream(content_describer);
// now read the content:
reader = new BufferedReader(new InputStreamReader(in));
String line;
StringBuilder builder = new StringBuilder();

while ((line = reader.readLine()) != null){
builder.append(line);
}
// Do something with the content in
text.setText(builder.toString());



} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
if (reader != null) {
try {
reader.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}


getPath()
from @Y.S.

I'm doing this :

String[] projection = { MediaStore.Files.FileColumns.DATA };
Cursor cursor = getActivity().getContentResolver().query(content_describer, projection, null, null, null);
int column_index = cursor.getColumnIndexOrThrow(projection[0]);
cursor.moveToFirst();
cursor.close();
Log.d( "PATH-->",cursor.getString(column_index));


Is getting a
NullPointerException
:


java.lang.RuntimeException: Failure delivering result ResultInfo{who=null, request=131073, result=-1, data=Intent { dat=file:///path typ=text/plain flg=0x3 }} to activity {info.androidhive.tabsswipe/info.androidhive.tabsswipe.MainActivity2}: java.lang.NullPointerException


EDIT with code working thanks to @Y.S., @Lukas Knuth, and @CommonsWare.



This is the
Intent
where I only accept files
text/plain
.

Intent chooseFile = new Intent(Intent.ACTION_GET_CONTENT);
chooseFile.addCategory(Intent.CATEGORY_OPENABLE);
chooseFile.setType("text/plain");
startActivityForResult(
Intent.createChooser(chooseFile, "Choose a file"),
PICKFILE_RESULT_CODE
);


On my
onActivityResult()
I create an
URI
where I get the data of the
Intent
, I create a
File
where I save the absolute path doing
content_describer.getPath();
, and then I keep the name of the path to use it in a
TextView
with
content_describer.getLastPathSegment();
(that was awesome @Y.S. didn't know about that function), and I create a second
File
which I called
destination
and I send the
AbsolutePath
to can create this
File
.

@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (requestCode == PICKFILE_RESULT_CODE && resultCode == Activity.RESULT_OK){
Uri content_describer = data.getData();
String src = content_describer.getPath();
source = new File(src);
Log.d("src is ", source.toString());
String filename = content_describer.getLastPathSegment();
text.setText(filename);
Log.d("FileName is ",filename);
destination = new File(Environment.getExternalStorageDirectory().getAbsolutePath() + "/Test/TestTest/" + filename);
Log.d("Destination is ", destination.toString());
SetToFolder.setEnabled(true);
}
}


Also I've created a function that you have to send the
source file
, and
destination file
that we have created previously to copy this to the new folder.

private void copy(File source, File destination) throws IOException {

FileChannel in = new FileInputStream(source).getChannel();
FileChannel out = new FileOutputStream(destination).getChannel();

try {
in.transferTo(0, in.size(), out);
} catch(Exception e){
Log.d("Exception", e.toString());
} finally {
if (in != null)
in.close();
if (out != null)
out.close();
}
}


Also I've created a function that says to me if this folder exist or not (I have to send the
destination file
, if it doesn't exist I create this folder and if it does not I do not do nothing.

private void DirectoryExist (File destination) {

if(!destination.isDirectory()) {
if(destination.mkdirs()){
Log.d("Carpeta creada","....");
}else{
Log.d("Carpeta no creada","....");
}
}


Thanks again for your help, hope you enjoy this code made with everyone of you guys :)

Answer

STEP 1 - Using an Implicit Intent:

To choose a file from the device, you should use an implicit Intent

Intent chooseFile = new Intent(Intent.ACTION_GET_CONTENT);
chooseFile.setType("*/*");
chooseFile = Intent.createChooser(chooseFile, "Choose a file");
startActivityForResult(chooseFile, PICKFILE_RESULT_CODE);

STEP 2 - Getting the absolute file path:

To get the file path from a Uri, first try using

Uri uri = data.getData();
String src = uri.getPath();

where data is the Intent returned in onActivityResult().

If that doesn't work, use the following method:

public String getPath(Uri uri) {

    String path = null;
    String[] projection = { MediaStore.Files.FileColumns.DATA };
    Cursor cursor = getContentResolver().query(uri, projection, null, null, null);

    if(cursor == null){
        path = uri.getPath()
    }
    else{
        cursor.moveToFirst();
        int column_index = cursor.getColumnIndexOrThrow(projection[0]);
        path = cursor.getString(column_index);
        cursor.close();
    }

    return ((path == null || path.isEmpty()) ? (uri.getPath()) : path);
}

At least one of these two methods should get you the correct, full path.

STEP 3 - Copying the file:

What you want, I believe, is to copy a file from one location to another.

To do this, it is absolutely necessary to have the absolute file path of both the source and destination locations.

First, get the absolute file path using either my getPath() method or uri.getPath():

String src = getPath(uri);    /* Method defined above. */

or

Uri uri = data.getData();
String src = uri.getPath();

Then, create two File objects as follows:

File source = new File(src);
String filename = uri.getLastPathSegment();
File destination = new File(Environment.getExternalStorageDirectory().getAbsolutePath() + "/CustomFolder/" + filename);

where CustomFolder is the directory on your external drive where you want to copy the file.

Then use the following method to copy a file from one place to another:

private void copy(File source, File destination) {

   FileChannel in = new FileInputStream(source).getChannel();
   FileChannel out = new FileOutputStream(destination).getChannel();

   try {
      in.transferTo(0, in.size(), out);
   } catch(Exception){
      // post to log
   } finally {
      if (in != null)
         in.close();
      if (out != null)
         out.close();
   }
}

Try this. This should work.

Note: Vis-a-vis Lukas' answer - what he has done is use a method called openInputStream() that returns the content of a Uri, whether that Uri represents a file or a URL.

Another promising approach - the FileProvider:

There is one more way by means of which it is possible to get a file from another app. If an app shares its files through the FileProvider, then it is possible to get hold of a FileDescriptor object which holds specific information about this file.

To do this, use the following Intent:

Intent mRequestFileIntent = new Intent(Intent.ACTION_GET_CONTENT);
mRequestFileIntent.setType("*/*");
startActivityForResult(mRequestFileIntent, 0);

and in your onActivityResult():

@Override
public void onActivityResult(int requestCode, int resultCode,
        Intent returnIntent) {
    // If the selection didn't work
    if (resultCode != RESULT_OK) {
        // Exit without doing anything else
        return;
    } else {
        // Get the file's content URI from the incoming Intent
        Uri returnUri = returnIntent.getData();
        /*
         * Try to open the file for "read" access using the
         * returned URI. If the file isn't found, write to the
         * error log and return.
         */
        try {
            /*
             * Get the content resolver instance for this context, and use it
             * to get a ParcelFileDescriptor for the file.
             */
            mInputPFD = getContentResolver().openFileDescriptor(returnUri, "r");
        } catch (FileNotFoundException e) {
            e.printStackTrace();
            Log.e("MainActivity", "File not found.");
            return;
        }
        // Get a regular file descriptor for the file
        FileDescriptor fd = mInputPFD.getFileDescriptor();
        ...
    }
}

where mInputPFD is a ParcelFileDescriptor.

References:

1. Common Intents - File Storage.

2. FileChannel.

3. FileProvider.

4. Requesting a Shared File.