Since Android 10 (API 29) I need to use the Storage Access Framework's File Picker to select GPX (GPS) files to copy from the Downloads folder to my local app folder. I have implemented the file picker and am able to select the GPX file, however the result data URI appears different to the filename (but unique) and I cannot seem to use it to copy the files. The rest of the code is the same "copy" code I used in previous versions of Android. What am I doing wrong and how should I best use the SAF File Picker to copy files? I haven't been able to find a recent (API 29) "file copy" example on the net...
private static final int READ_REQUEST_CODE = 42;
...
public void performFileSearch() {
Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT);
intent.addCategory(Intent.CATEGORY_OPENABLE);
// intent.setType("application/gpx"); // Filters GPX file but wont let me select them.
intent.setType("*/*");
startActivityForResult(intent, READ_REQUEST_CODE);
}
...
if (requestCode == READ_REQUEST_CODE && resultCode == Activity.RESULT_OK) {
Uri uri = null;
if (data != null) {
uri = data.getData();
handleDownloadedGPXFiles2(uri);
}
}
...
private void handleDownloadedGPXFiles2(Uri selectedFileUri) {
File sourceFile = new File(selectedFileUri.getPath()); // Returns a unique number or string but NOT filename string???
File destDirectory = new File(this.getExternalFilesDir(null), "Imported");
File destFile = new File(destDirectory, "test.gpx"); // Needs to be same name as original filename.
try {
if (!destFile.exists()) {
destFile.createNewFile();
}
FileInputStream inStream = new FileInputStream(sourceFile);
FileOutputStream outStream = new FileOutputStream(destFile);
FileChannel inChannel = inStream.getChannel();
FileChannel outChannel = outStream.getChannel();
inChannel.transferTo(0, inChannel.size(), outChannel);
inStream.close();
outStream.close();
} catch (IOException e) {
e.printStackTrace();
}
Toast.makeText(getApplicationContext(), "File Import Complete", Toast.LENGTH_LONG).show();
}
File sourceFile = new File(selectedFileUri.getPath());
Remove above line.
FileInputStream inStream = new FileInputStream(sourceFile);
Replace that line by:
InputStream inStream = getContentResolver().openInputStream(selectedFileUri);
Further you can remove
if (!destFile.exists()) {
destFile.createNewFile();
}
as the file will be created by the new FileOutputStream();
Finally: Your last Toast() is on the wrong place. It should be in the try block.
Place a different Toast() in the catch block to inform yourself or the user.
Thanks blackapps. Final code works well...
private void handleDownloadedGPXFiles2(Uri selectedFileUri) {
String displayName = "imported.gpx";
String fileExtension;
ContentResolver contentResolver = getContentResolver();
Cursor cursor = contentResolver.query(selectedFileUri, null, null, null, null);
try {
if (cursor != null && cursor.moveToFirst()) {
displayName = cursor.getString(cursor.getColumnIndex(OpenableColumns.DISPLAY_NAME));
if (displayName != null && displayName.length() >=4) {
fileExtension = displayName.substring(displayName.length() - 4);
if (!fileExtension.equals(".gpx")){
myCustomToast("Must be a .GPX file!");
return;
}
} else {
myCustomToast("Must be a .GPX file!");
return;
}
}
File destDirectory = new File(this.getExternalFilesDir(null), "Imported");
File destFile = new File(destDirectory, displayName);
FileOutputStream outStream = new FileOutputStream(destFile);
InputStream in = getContentResolver().openInputStream(selectedFileUri);
OutputStream out = outStream;
byte[] buffer = new byte[1024];
int read;
while ((read = in.read(buffer)) != -1) {
out.write(buffer, 0, read);
}
in.close();
out.flush();
out.close();
Toast.makeText(getApplicationContext(), "File Import Complete", Toast.LENGTH_LONG).show();
} catch (IOException e) {
Toast.makeText(getApplicationContext(), "File Import FAILED", Toast.LENGTH_LONG).show();
e.printStackTrace();
}
finally
{
if (cursor != null)
cursor.close();
}
}
How can I add a executable into assets and run it in Android and show the output?
I've a executable that will work. I assume there will need to be some chmod in the code.
Thank you.
here is my answer
put copyAssets() to your mainactivity.
someone's code:
private void copyAssets() {
AssetManager assetManager = getAssets();
String[] files = null;
try {
files = assetManager.list("");
} catch (IOException e) {
Log.e("tag", "Failed to get asset file list.", e);
}
for(String filename : files) {
InputStream in = null;
OutputStream out = null;
try {
in = assetManager.open(filename);
File outFile = new File(getFilesDir(), filename);
out = new FileOutputStream(outFile);
copyFile(in, out);
} catch(IOException e) {
Log.e("tag", "Failed to copy asset file: " + filename, e);
}
finally {
if (in != null) {
try {
in.close();
} catch (IOException e) {
// NOOP
}
}
if (out != null) {
try {
out.close();
} catch (IOException e) {
// NOOP
}
}
}
}
}
private void copyFile(InputStream in, OutputStream out) throws IOException {
byte[] buffer = new byte[1024];
int read;
while ((read = in.read(buffer)) != -1) {
out.write(buffer, 0, read);
}
}
also here is code to run command
public String runcmd(String cmd){
try {
Process p = Runtime.getRuntime().exec(cmd);
BufferedReader in = new BufferedReader(new InputStreamReader(p.getInputStream()));
int read;
char[] buffer = new char[4096];
StringBuffer out = new StringBuffer();
while ((read = in.read(buffer)) > 0) {
out.append(buffer, 0, read);
}
in.close();
p.waitFor();
return out.substring(0);
} catch (IOException e) {
throw new RuntimeException(e);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
you may need to change it to
String prog= "programname";
String[] env= { "parameter 1","p2"};
File dir= new File(getFilesDir().getAbsolutePath());
Process p = Runtime.getRuntime().exec(prog,env,dir);
to ensure proper parameter handling
also add this to your main code
to check proper copying of files
String s;
File file4 = new File(getFilesDir().getAbsolutePath()+"/executable");
file4.setExecutable(true);
s+=file4.getName();
s+=file4.exists();
s+=file4.canExecute();
s+=file4.length();
//output s however you want it
should write: filename, true, true, correct filelength.
Place your executable in raw folder, then run it by using ProcessBuilder or Runtime.exec like they do here http://gimite.net/en/index.php?Run%20native%20executable%20in%20Android%20App
Good day!
I have just started developing for android. In my app, I need to copy the items in my assets folder to the internal storage.
I have searched a lot on SO including this which copies it to the external storage.
How to copy files from 'assets' folder to sdcard?
This is what I want to achieve:
I have a directory already present in the internal storage as X>Y>Z. I need a file to be copied to Y and another to Z.
Can anyone help me out with a code snippet? I really don't have any idea how to go on about this.
Sorry for my bad English.
Thanks a lot.
Use
String out= Environment.getExternalStorageDirectory().getAbsolutePath() + "/X/Y/Z/" ;
File outFile = new File(out, Filename);
After Editing in your ref. Link Answer.
private void copyAssets() {
AssetManager assetManager = getAssets();
String[] files = null;
try {
files = assetManager.list("");
} catch (IOException e) {
Log.e("tag", "Failed to get asset file list.", e);
}
for(String filename : files) {
InputStream in = null;
OutputStream out = null;
try {
in = assetManager.open(filename);
String outDir = Environment.getExternalStorageDirectory().getAbsolutePath() + "/X/Y/Z/" ;
File outFile = new File(outDir, filename);
out = new FileOutputStream(outFile);
copyFile(in, out);
in.close();
in = null;
out.flush();
out.close();
out = null;
} catch(IOException e) {
Log.e("tag", "Failed to copy asset file: " + filename, e);
}
}
}
private void copyFile(InputStream in, OutputStream out) throws IOException {
byte[] buffer = new byte[1024];
int read;
while((read = in.read(buffer)) != -1){
out.write(buffer, 0, read);
}
}
I did something like this. This allows you to copy all the directory structure to copy from Android AssetManager.
public String copyDirorfileFromAssetManager(String arg_assetDir, String arg_destinationDir) throws IOException
{
File sd_path = Environment.getExternalStorageDirectory();
String dest_dir_path = sd_path + addLeadingSlash(arg_destinationDir);
File dest_dir = new File(dest_dir_path);
createDir(dest_dir);
AssetManager asset_manager = getApplicationContext().getAssets();
String[] files = asset_manager.list(arg_assetDir);
for (int i = 0; i < files.length; i++)
{
String abs_asset_file_path = addTrailingSlash(arg_assetDir) + files[i];
String sub_files[] = asset_manager.list(abs_asset_file_path);
if (sub_files.length == 0)
{
// It is a file
String dest_file_path = addTrailingSlash(dest_dir_path) + files[i];
copyAssetFile(abs_asset_file_path, dest_file_path);
} else
{
// It is a sub directory
copyDirorfileFromAssetManager(abs_asset_file_path, addTrailingSlash(arg_destinationDir) + files[i]);
}
}
return dest_dir_path;
}
public void copyAssetFile(String assetFilePath, String destinationFilePath) throws IOException
{
InputStream in = getApplicationContext().getAssets().open(assetFilePath);
OutputStream out = new FileOutputStream(destinationFilePath);
byte[] buf = new byte[1024];
int len;
while ((len = in.read(buf)) > 0)
out.write(buf, 0, len);
in.close();
out.close();
}
public String addTrailingSlash(String path)
{
if (path.charAt(path.length() - 1) != '/')
{
path += "/";
}
return path;
}
public String addLeadingSlash(String path)
{
if (path.charAt(0) != '/')
{
path = "/" + path;
}
return path;
}
public void createDir(File dir) throws IOException
{
if (dir.exists())
{
if (!dir.isDirectory())
{
throw new IOException("Can't create directory, a file is in the way");
}
} else
{
dir.mkdirs();
if (!dir.isDirectory())
{
throw new IOException("Unable to create directory");
}
}
}
try this below code
private void copyAssets() {
AssetManager assetManager = getAssets();
String[] files = null;
try {
files = assetManager.list("");
} catch (IOException e) {
Log.e("tag", "Failed to get asset file list.", e);
}
for(String filename : files) {
InputStream in = null;
OutputStream out = null;
try {
in = assetManager.open(filename);
File outFile = new File(getExternalFilesDir(null), filename);
out = new FileOutputStream(outFile);
copyFile(in, out);
in.close();
in = null;
out.flush();
out.close();
out = null;
} catch(IOException e) {
Log.e("tag", "Failed to copy asset file: " + filename, e);
}
}
}
private void copyFile(InputStream in, OutputStream out) throws IOException {
byte[] buffer = new byte[1024];
int read;
while((read = in.read(buffer)) != -1){
out.write(buffer, 0, read);
}
}
This is my Kotlin solution with auto-closable streams to copy in internal app storage:
val copiedFile = File(context.filesDir, "copied_file.txt")
context.assets.open("original_file.txt").use { input ->
copiedFile.outputStream().use { output ->
input.copyTo(output, 1024)
}
}
My small solution on Kotlin, for copy data from assets to INTERNAL STORAGE
fun copy() {
val bufferSize = 1024
val assetManager = context.assets
val assetFiles = assetManager.list("")
assetFiles.forEach {
val inputStream = assetManager.open(it)
val outputStream = FileOutputStream(File(context.filesDir, it))
try {
inputStream.copyTo(outputStream, bufferSize)
} finally {
inputStream.close()
outputStream.flush()
outputStream.close()
}
}
}
public void addFilesToSystem(String sysName, String intFil, Context c){
//sysName is the name of the file we have in the android os
//intFil is the name of the internal file
file = new File(path, sysName + ".txt");
if(!file.exists()){
path.mkdirs();
try {
AssetManager am = c.getAssets();
InputStream is = am.open(intFil);
OutputStream os = new FileOutputStream(file);
byte[] data = new byte[is.available()];
is.read(data);
os.write(data);
is.close();
os.close();
Toast t = Toast.makeText(c, "Making file: " + file.getName() + ". One time action", Toast.LENGTH_LONG);
t.show();
//Update files for the user to use
MediaScannerConnection.scanFile(c,
new String[] {file.toString()},
null,
new MediaScannerConnection.OnScanCompletedListener() {
public void onScanCompleted(String path, Uri uri) {
// TODO Auto-generated method stub
}
});
} catch (IOException e) {
Toast t = Toast.makeText(c, "Error: " + e.toString() + ". One time action", Toast.LENGTH_LONG);
t.show();
e.printStackTrace();
}
}
}
To add a file, call the addFilesToSystem("this_file_is_in_the_public_system", "this_file_is_in_the_assets_folder", context/this context is if you do not have the method in the Activity/
Hope it helps
You can use the Envrionment#getDataDirectory method for that. It'll give the path of the data directory of the internal storage memory. This is generally where all the app related data is stored.
Alternately, if you want to store in the root directory, you can use the Environment#getRootDirectory method for that.
If you need to copy any file from assets to the internal storage and do it only once:
public void writeFileToStorage() {
Logger.d(TAG, ">> writeFileToStorage");
AssetManager assetManager = mContext.getAssets();
if (new File(getFilePath()).exists()) {
Logger.d(TAG, "File exists, do nothing");
Logger.d(TAG, "<< writeFileToStorage");
return;
}
try (InputStream input = assetManager.open(FILE_NAME);
OutputStream output = new FileOutputStream(getFilePath())) {
Logger.d(TAG, "File does not exist, write it");
byte[] buffer = new byte[input.available()];
int length;
while ((length = input.read(buffer)) != -1) {
output.write(buffer, 0, length);
}
} catch (FileNotFoundException e) {
e.printStackTrace();
Logger.e(TAG, "File is not found");
} catch (IOException e) {
e.printStackTrace();
Logger.d(TAG, "Error while writing the file");
}
Logger.d(TAG, "<< writeFileToStorage");
}
public String getFilePath() {
String filePath = mContext.getFilesDir() + "/" + FILE_NAME;
Logger.d(TAG, "File path: " + filePath);
return filePath;
}
I'm using the code below to try and move my database file to my sdcard. I have no problems except that I get a redline under sd. Any ideas?
File data = Environment.getDataDirectory();
if (sd.canWrite()) {
String currentDBPath = "\\data\\application.package\\databases\\name";
String backupDBPath = "name";
File currentDB = new File(data, currentDBPath);
File backupDB = new File(sd, backupDBPath);
if (currentDB.exists()) {
FileChannel src;
try {
src = new FileInputStream(currentDB).getChannel();
FileChannel dst = new FileOutputStream(backupDB).getChannel();
try {
dst.transferFrom(src, 0, src.size());
src.close();
dst.close();
} catch (IOException e) {
e.printStackTrace();
}
} catch (FileNotFoundException e) {
e.printStackTrace();
}
You can only use a variable if you create an instance of it:
Put this before your code:
File sd = Environment.getExternalStorageDirectory();
if you are using SQLite database try this:
public class _DBHelper extends SQLiteOpenHelper {
public boolean backUp() throws Exception
{
InputStream input = null;
OutputStream output = null;
try {
SQLiteDatabase db = this.getReadableDatabase();
String strSource = db.getPath();
String strDest = Utilities.getAppDocumentsFolder(_context) + "/"
+ DATABASE_NAME;
File fileDest = new File(strDest);
if (fileDest.exists())
{
fileDest.delete();
}
input = new FileInputStream(strSource);
output = new FileOutputStream(strDest);
byte[] buffer = new byte[1024];
int length;
while ((length = input.read(buffer)) > 0) {
output.write(buffer, 0, length);
}
} catch (Exception e) {
throw e;
} finally
{
if (output != null)
{
output.flush();
output.close();
}
if (input != null)
{
input.close();
}
}
return true;
}
}
I'm using the code below to try and move my database file to my sdcard. I have no problems except that I get a redline under sd. Any ideas?
File data = Environment.getDataDirectory();
if (sd.canWrite()) {
String currentDBPath = "\\data\\application.package\\databases\\name";
String backupDBPath = "name";
File currentDB = new File(data, currentDBPath);
File backupDB = new File(sd, backupDBPath);
if (currentDB.exists()) {
FileChannel src;
try {
src = new FileInputStream(currentDB).getChannel();
FileChannel dst = new FileOutputStream(backupDB).getChannel();
try {
dst.transferFrom(src, 0, src.size());
src.close();
dst.close();
} catch (IOException e) {
e.printStackTrace();
}
} catch (FileNotFoundException e) {
e.printStackTrace();
}
You can only use a variable if you create an instance of it:
Put this before your code:
File sd = Environment.getExternalStorageDirectory();
if you are using SQLite database try this:
public class _DBHelper extends SQLiteOpenHelper {
public boolean backUp() throws Exception
{
InputStream input = null;
OutputStream output = null;
try {
SQLiteDatabase db = this.getReadableDatabase();
String strSource = db.getPath();
String strDest = Utilities.getAppDocumentsFolder(_context) + "/"
+ DATABASE_NAME;
File fileDest = new File(strDest);
if (fileDest.exists())
{
fileDest.delete();
}
input = new FileInputStream(strSource);
output = new FileOutputStream(strDest);
byte[] buffer = new byte[1024];
int length;
while ((length = input.read(buffer)) > 0) {
output.write(buffer, 0, length);
}
} catch (Exception e) {
throw e;
} finally
{
if (output != null)
{
output.flush();
output.close();
}
if (input != null)
{
input.close();
}
}
return true;
}
}