Note:
Moving from startActivityForResult to ActivityResultLauncher to pickup file in Shared Folder - Download /storage/emulated/0/Download
Minimum Android 7 API 24 Targeted SDK Android 24 API 34
build.gradle.kts (Module :app)
plugins {
alias(libs.plugins.android.application)
}
android {
namespace = "com.dedetok.tutorialsharedfolder"
compileSdk = 34
defaultConfig {
applicationId = "com.dedetok.tutorialsharedfolder"
minSdk = 24
targetSdk = 34
versionCode = 1
versionName = "1.0"
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
}
buildTypes {
release {
isMinifyEnabled = false
proguardFiles(
getDefaultProguardFile("proguard-android-optimize.txt"),
"proguard-rules.pro"
)
}
}
compileOptions {
sourceCompatibility = JavaVersion.VERSION_1_8
targetCompatibility = JavaVersion.VERSION_1_8
}
}
dependencies {
implementation(libs.appcompat)
implementation(libs.material)
implementation(libs.activity)
implementation(libs.constraintlayout)
testImplementation(libs.junit)
androidTestImplementation(libs.ext.junit)
androidTestImplementation(libs.espresso.core)
}
AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"
android:maxSdkVersion="29"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"
android:maxSdkVersion="29"/>
<uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE"/>
<application
android:requestLegacyExternalStorage="true"
android:allowBackup="true"
android:dataExtractionRules="@xml/data_extraction_rules"
android:fullBackupContent="@xml/backup_rules"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/Theme.TutorialSharedFolder"
tools:targetApi="31">
<activity
android:name=".MainActivity"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>
activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.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:id="@+id/main"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
</androidx.constraintlayout.widget.ConstraintLayout>
MainActivity.java
package com.dedetok.tutorialsharedfolder;
import android.Manifest;
import android.app.Activity;
import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.database.Cursor;
import android.net.Uri;
import android.os.Bundle;
import android.os.Environment;
import android.util.Log;
import androidx.activity.EdgeToEdge;
import androidx.activity.result.ActivityResult;
import androidx.activity.result.ActivityResultCallback;
import androidx.activity.result.ActivityResultLauncher;
import androidx.activity.result.contract.ActivityResultContracts;
import androidx.appcompat.app.AppCompatActivity;
import androidx.core.content.ContextCompat;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import android.provider.DocumentsContract;
public class MainActivity extends AppCompatActivity {
/*
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"
android:maxSdkVersion="29"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"
android:maxSdkVersion="29"/>
<uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE"/>
<application
android:requestLegacyExternalStorage="true"
/storage/emulated/0/Download
MediaStore.Downloads (/storage/emulated/0/Download) Android 10 (API level 29) and higher,
*/
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
EdgeToEdge.enable(this);
setContentView(R.layout.activity_main);
/*
* check perrmission
*/
boolean isPermitted;
String myPermissionRequired = android.Manifest.permission.READ_EXTERNAL_STORAGE;
isPermitted = myCheckPermission(this, myPermissionRequired);
Log.e("dedetok", "READ_EXTERNAL_STORAGE: "+isPermitted);
myPermissionRequired = android.Manifest.permission.WRITE_EXTERNAL_STORAGE;
isPermitted = myCheckPermission(this, myPermissionRequired);
Log.e("dedetok", "WRITE_EXTERNAL_STORAGE: "+isPermitted);
myPermissionRequired = Manifest.permission.MANAGE_EXTERNAL_STORAGE;
isPermitted = myCheckPermission(this, myPermissionRequired);
Log.e("dedetok", "MANAGE_EXTERNAL_STORAGE: "+isPermitted);
/*
* root Environment.getExternalStorageDirectory() -> /storage/emulated/0
*/
// get root directory path NOT USED
File fExternalStorage = Environment.getExternalStorageDirectory();
String sFExternalStorage = fExternalStorage.getPath();
//String fsFExternalStorage = sFExternalStorage+File.separator+sFilename;
Log.e("dedetok", "Environment.getExternalStorageDirectory() "+sFExternalStorage); //debug
/*
* Access file in /storage/emulated/0/Download
* Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS) ->
* /storage/emulated/0/Download
*/
// get Download directory path NOT USED IN PICK FILE BUT USED IN PRINT FILE
File extDir = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS);
String dirPath = extDir.getPath();
Log.e("dedetok", "getExternalStoragePublicDirectory "+dirPath); //debug
/*
* Select file in Download Folder
*/
//getFileURI(); // OLD WAT Deprecated
getFileURINew(); // New Way using system dialog to select file
/*
* writing to Download Folder
*/
String sFileOutput = extDir+File.separator+"helloworld.txt";
File fOutput = new File(sFileOutput);
try {
PrintWriter myPW = new PrintWriter(fOutput);
myPW.println("Hello World");
myPW.println("this is a test");
myPW.flush();
myPW.close();
} catch (FileNotFoundException e) {
e.printStackTrace(); // debug
}
}
/*
* self check permission function
*/
private boolean myCheckPermission(Context appContext, String sPermission) {
return (ContextCompat.checkSelfPermission(appContext, sPermission)
== PackageManager.PERMISSION_GRANTED);
// or
// return (PermissionChecker.checkSelfPermission(appContext, sPermission)
// == PermissionChecker.PERMISSION_GRANTED);
}
///////////// THIS IS PART OF DEPRECATED SECTION START /////////////
/*
* get result
*/
@Override
protected void onActivityResult(int requestCode, int resultCode,
Intent resultData) {
super.onActivityResult(requestCode, resultCode, resultData);
if (requestCode == MY_ID_PICK_FILE
&& resultCode == Activity.RESULT_OK) {
// The result data contains a URI for the document or directory that
// the user selected.
Uri uri;
if (resultData != null) {
uri = resultData.getData();
// Perform operations on the document using its URI.
Log.e("dedetok", "Result"); // debug
if (uri!=null) {
Log.e("dedetok", "Result uri: "+uri.getPath()); // debug
openMyFile(uri);
}
}
}
}
private static final int MY_ID_PICK_FILE = 1; // any number
private static final String mimeTypeFilter = "text/csv";
/*
* https://developer.android.com/training/data-storage/shared/documents-files
* https://stackoverflow.com/questions/71667342/how-to-allow-selection-of-csv-in-a-file-chooser-intent-in-android
* Deprecated startActivityForResult
*/
private void getFileURI() {
String[] myMime = {"*/*",
"text/plain",
"application/csv",
"application/vnd.ms-excel",
"application/excel",
"application/x-excel",
"application/x-msexcel"
};
Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT); // MUST
intent.addCategory(Intent.CATEGORY_OPENABLE); // MUST
intent.setType(mimeTypeFilter);
// Optionally, specify a URI for the file that should appear in the
// system file picker when it loads.
intent.putExtra(Intent.EXTRA_MIME_TYPES, myMime);
startActivityForResult(intent, MY_ID_PICK_FILE);
}
///////////// THIS IS PART OF DEPRECATED SECTION END /////////////
///////////// THIS PART OF NEW WAY START /////////////
/*
* https://developer.android.com/training/basics/intents/result
*/
private void getFileURINew() {
String[] myMime = {"*/*",
"text/plain",
"application/csv",
"application/vnd.ms-excel",
"application/excel",
"application/x-excel",
"application/x-msexcel"
};
Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT); // MUST
intent.addCategory(Intent.CATEGORY_OPENABLE); // MUST
intent.setType(mimeTypeFilter);
// Optionally, specify a URI for the file that should appear in the
// system file picker when it loads.
intent.putExtra(Intent.EXTRA_MIME_TYPES, myMime);
mGetContent.launch(intent);
}
// ActivityResultCallback
ActivityResultCallback<ActivityResult> myActivityResult = new ActivityResultCallback<ActivityResult>() {
@Override
public void onActivityResult(ActivityResult aResult) {
if (aResult.getResultCode()==RESULT_OK) {
Intent iData = aResult.getData();
Uri uriData = iData.getData();
Log.e("dedetok", "new "+uriData.getPath());
}
}
};
// ActivityResultContracts
ActivityResultContracts.StartActivityForResult myARC = new ActivityResultContracts.StartActivityForResult();
// ActivityResultLauncher
ActivityResultLauncher<Intent> mGetContent = registerForActivityResult(
myARC,
myActivityResult);
///////////// THIS PART OF NEW WAY END /////////////
///////////// SUPPORTING FUNCTION /////////////
/*
* Open File after receive from system pick up
*/
private void openMyFile(Uri uriMyFile) {
//getFileURI(); // crash
StringBuilder stringBuilder = new StringBuilder();
if (isVirtualFile(uriMyFile)) {
Log.e("dedetok","It is Virual"); // debug
ContentResolver resolver = getContentResolver();
String[] openableMimeTypes = resolver.getStreamTypes(uriMyFile, mimeTypeFilter);
if (openableMimeTypes != null ||
openableMimeTypes.length > 0) {
try {
InputStream inputStream = resolver
.openTypedAssetFileDescriptor(uriMyFile, openableMimeTypes[0], null)
.createInputStream();
BufferedReader reader = new BufferedReader(
new InputStreamReader(inputStream));
String line;
while ((line = reader.readLine()) != null) {
stringBuilder.append(line);
}
} catch (IOException e) {
e.printStackTrace(); // debug
}
}
} else {
try {
InputStream inputStream =
getContentResolver().openInputStream(uriMyFile);
BufferedReader reader = new BufferedReader(
new InputStreamReader(inputStream));
String line;
while ((line = reader.readLine()) != null) {
stringBuilder.append(line);
}
} catch (IOException e) {
e.printStackTrace(); // debug
}
}
Log.e("dedetok",stringBuilder.toString());
}
/*
* check if file receive is virtual
* Android 7.0 (API level 24) and higher
*https://developer.android.com/training/data-storage/shared/documents-files
*/
private boolean isVirtualFile(Uri uri) {
if (!DocumentsContract.isDocumentUri(this, uri)) {
return false;
}
Cursor cursor = getContentResolver().query(
uri,
new String[] { DocumentsContract.Document.COLUMN_FLAGS },
null, null, null);
int flags = 0;
if (cursor.moveToFirst()) {
flags = cursor.getInt(0);
}
cursor.close();
return (flags & DocumentsContract.Document.FLAG_VIRTUAL_DOCUMENT) != 0;
}
}
References:
- https://developer.android.com/training/basics/intents/result
- https://devofandroid.blogspot.com/2022/09/get-result-from-another-activity-using.html
- https://medium.com/@steves2001/moving-from-android-startactivityforresult-to-registerforactivityresult-76ca04044ff1
- https://www.geeksforgeeks.org/how-to-use-activityforresultluncher-as-startactivityforresult-is-deprecated-in-android/