I am in the process of reworking an open source extension from 2018, thus created before Android 10/11 and the ASD came along. There exists a file path resolver function which sets the default path to /storage/emulated/0
and a now incorrect path to assets in companion, but this latter one doesn't seem to work.
private String resolveFileName(final String fileName) {
if (fileName.startsWith("/"))
return Environment.getExternalStorageDirectory().getPath() + fileName;
File dirPath = context.getFilesDir();
if (isRepl) {
String path = Environment.getExternalStorageDirectory().getPath() + "/AppInventor/data/";
dirPath = new File(path);
if (! dirPath.exists()) {
dirPath.mkdirs(); // Make sure it exists
}
}
return dirPath.getPath() + "/" + fileName;
}
I would like to update this code so that the default path is to /files/
in the ASD, and for it to be able to read from the assets in companion and compiled mode. Some pseudo code:
(let us assume ASD
= storage/emulated/0/Android/data/<pkg_name>/files
private String resolveFileName(final String fileName) {
if (fileName.startsWith("//"))
if (in Companion) set file path: (~/ASD + /assets/ + filename (without the `//`)
if (compiled) set file path: ~ + assetsPath + filename (without the `//`)
if (fileName.startsWith("/"))
set file path to ASD + filename
if (fileName)
set file path to private directory
}
Any help, greatly appreciated
1 Like
Hi @TIMAI2 ,
Try this one
"begins with a slash (/) the file is written to the sdcard. For example writing to " +
"/myFile.txt will write the file to /sdcard/myFile.txt. If the filename does not start " +
"with a slash, it will be written in the programs private data directory where it will " +
"not be accessible to other programs on the phone. There is a special exception for the " +
"AI/Kodular Companion where these files are written to /sdcard/AppInventor/data (and /sdcard/Makeroid/data for Kodular) to facilitate " +
"debugging. Note that this block will overwrite a file if it already exists." +
"\n\nIf you want to add content to a file use the append block.")
public void SaveFile(String text, String fileName){
Write(fileName,text,false);
}
public String getAbsoluteFilePath(String fileName) {
if(fileName.isEmpty()){
return fileName;
}else{
String sd = getExternalStoragePath();
String completeFileName = fileName;
if (fileName.startsWith("file:///")) {
completeFileName = fileName.substring(7);
} else if (fileName.startsWith("//")) {
fileName = fileName.substring(2);
if (isRepl) {
Sadly, this won't work in Kodular companion.
2 Likes
Many thanks
Found quite a few things I needed to add:
Code
public Activity activity;
private boolean legacy = true;
activity = container.$context();
context = container.$context();
@SuppressWarnings("deprecation")
public String getExternalStoragePath(){
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q){
return context.getExternalFilesDir(null).getAbsolutePath();
}else{
return Environment.getExternalStorageDirectory().getAbsolutePath();
}
}
private String getReplFilePath(){
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
return getExternalStoragePath() + "/data/";
} else {
return getExternalStoragePath() + "/AppInventor/data/";
}
}
@SimpleFunction(description="Returns external storage path")
public String GetExternalStoragePath(){
return getExternalStoragePath();
}
@SimpleFunction(description="Returns app's private storage directory path")
public String GetPrivateDirPath(){
return activity.getFilesDir().getAbsolutePath();
}
@SimpleFunction(description = "Returns application specific directory path")
public String GetApplicationSpecificDirPath(){
return context.getExternalFilesDir(null).getAbsolutePath();
}
@DesignerProperty(editorType = PropertyTypeConstants.PROPERTY_TYPE_BOOLEAN,
defaultValue = "True")
@SimpleProperty(description = "Allows app to write to Application Specific Directory instead of Private Directory")
public void LegacyMode(boolean legacy) {
this.legacy = legacy;
}
public String getAbsoluteFilePath(String fileName) {
if(fileName.isEmpty()){
return fileName;
}else{
String sd = getExternalStoragePath();
String completeFileName = fileName;
if (fileName.startsWith("file:///")) {
completeFileName = fileName.substring(7);
} else if (fileName.startsWith("//")) {
fileName = fileName.substring(2);
if (isRepl) {
completeFileName = getReplFilePath() + fileName;
}
} else if (fileName.startsWith("/")) {
if (!fileName.startsWith(sd)){
completeFileName = sd + fileName;
}
} else {
if (legacy) {
completeFileName = GetApplicationSpecificDirPath() + File.separator + fileName;
}else{
completeFileName = GetPrivateDirPath() + File.separator + fileName;
}
}
return completeFileName;
}
}
I presume I can make all those simple functions private except for legacy
?
Should I set legacy
to default=false, or leave it as true ?
Had some success, can change the path to the ASD OK, but unable to load file from assets. At the moment only testing in companion....
Code
private String resolveFileName(String fileName) {
if (fileName.startsWith("/")) {
return getExternalStoragePath() + fileName;
}
File dirPath = context.getFilesDir();
if (fileName.startsWith("//")) {
fileName = fileName.substring(2);
if (this.isRepl) {
String path = getReplFilePath() + fileName;
dirPath = new File(path);
}
}
return dirPath.getPath() + "/" + fileName;
}
@SuppressWarnings("deprecation")
public String getExternalStoragePath(){
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q){
return context.getExternalFilesDir(null).getAbsolutePath();
}else{
return Environment.getExternalStorageDirectory().getAbsolutePath();
}
}
private String getReplFilePath(){
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
return getExternalStoragePath() + "/assets/";
} else {
return getExternalStoragePath() + "/AppInventor/data/";
}
}
Might have solved it, was missing this in my file loading code:
if(fileName.startsWith("//")){
inputStream = form.openAsset(fileName.substring(2));
}
and if I had just looked 10 lines up in the code, i would have found the function that sets the InputStream
Will mark as solved - for now
1 Like
Thought I had better show what I needed to add and change to get things working (note: now tested compiled on Android 10 and Android 12)
With thanks @vknow360 I first added a couple of private functions and a simple function which return ASD, Private and Asset paths:
@SuppressWarnings("deprecation")
public String getExternalStoragePath(){
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q){
return context.getExternalFilesDir(null).getAbsolutePath();
}else{
return Environment.getExternalStorageDirectory().getAbsolutePath();
}
}
private String getReplFilePath(){
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
return getExternalStoragePath() + "/assets/";
} else {
return getExternalStoragePath() + "/AppInventor/data/";
}
}
@SimpleFunction(description="Returns app's private storage directory path")
public String GetPrivateDirPath(){
return context.getFilesDir().getAbsolutePath();
}
(Note: in @vknow360 's getReplFilePath()
function he had data
instead of assets
? )
then quite a few changes to the resolveFileName
function:
@SuppressWarnings("deprecation")
private String resolveFileName(String fileName) {
String completeFileName = fileName;
if (fileName.startsWith("/")) {
completeFileName = getExternalStoragePath() + fileName;
} else if (fileName.startsWith("//")) {
if (isRepl) {
fileName = fileName.substring(2);
completeFileName = getReplFilePath() + fileName;
}
} else {
completeFileName = GetPrivateDirPath() + "/" + fileName;
}
return completeFileName;
}
and finally, a change to the openInputStream
function:
private InputStream openInputStream(final String fileName) throws IOException {
if (fileName.startsWith("//")) {
if (isRepl)
return new FileInputStream(getReplFilePath() + fileName.substring(2));
else
return context.getAssets().open(fileName.substring(2));
} else
return new FileInputStream(resolveFileName(fileName));
}
1 Like
TIMAI2
Closed
May 17, 2022, 9:40am
6
This topic was automatically closed 7 days after the last reply. New replies are no longer allowed.