Default File Path (ASD) Code?

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 :slight_smile:

1 Like

Hi @TIMAI2 ,
Try this one

Sadly, this won't work in Kodular companion.

2 Likes

Many thanks :slight_smile:

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 :upside_down_face:

Will mark as solved - for now :slight_smile:

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

This topic was automatically closed 7 days after the last reply. New replies are no longer allowed.