Errors with saveTable() on Android

Hello, I’m making an android application with processing. It started as a Java game but then i ported it to Android. However, this game utilizes save data, and I used .csv files to save the game’s data. This worked fine in Java mode, but on an Android phone, it strangely doesn’t save the data, and it closes out, and Android gives me an error message. I have looked into other threads, but I don’t know how to fix it. Maybe it can be because i set the game to save data when closed on Java mode, but since the sketch utilizes “fullScreen()”, it only saves data when the esc key is hit.

Search for android permissions in the forum search you should find your answer there.

To save game results I would use SharedPreferences; Then you don’t have to worry, about permissions. But results, however, will be lost when you uninstall the App. You can also use the save() function, or saveStrings(), but along with giving permissions, you have to add some extra code for versions above Lollipop. See examples for these on my repo here.

Hello, I have tried looking for android permissions but it still unexpectedly closes the app. I am able to load the csv or tsv files, but i am not able to write them.

I just verified, and indeed my android code for saving a table like csv was not updated for versions above Lollipop. Try this code instead.

//  Be sure to give permission WRITE_EXTERNAL_STORAGE

import android.os.Environment;
import android.Manifest; 
import android.content.pm.PackageManager; 
import android.os.Build; 
import android.os.Build.VERSION_CODES; 
import processing.core.PConstants;
import android.app.Activity; 
import android.os.Environment;
Table table; 

Activity activity; 
boolean permissions_granted;
boolean file_saved;
String str_1, str_2;
String tableFile;
String tableFolder;

void setup() { 
  size(displayWidth, displayHeight);
  activity = this.getActivity();
  if (Build.VERSION.SDK_INT > Build.VERSION_CODES.LOLLIPOP_MR1) { 
    requestParticularPermission();
  }
  textAlign(CENTER);
  str_1 = "Click to save a table.";
  str_2 = "Table saved on SD card.";
  textSize(12);
  float twf = width/textWidth(str_1);
  textSize(8*twf);
  table = new Table(); 
  table.addColumn("id"); 
  table.addColumn("species"); 
  table.addColumn("name"); 
  TableRow newRow = table.addRow(); 
  newRow.setInt("id", table.getRowCount() - 1); 
  newRow.setString("species", "Panthera leo"); 
  newRow.setString("name", "Lion"); 
  // Folowing file name is the folder you save your table file in.
  // If it does not exist it will be created automatically
  tableFolder = "MyTables";
  // Folowing string is the name of your table file
  // Be sure to include extension.
  tableFile = "MyTable.csv";
}

void draw() {
  background(0, 0, 200);
  if (file_saved) text(str_2, width/2, height/2);
  else text(str_1, width/2, height/2);
}

void mousePressed () {
  saveMyTable(tableFolder, tableFile);
}

void saveMyTable(String sf, String tf) { 
  try { 
    String absolute_path = new String(Environment.getExternalStorageDirectory().getAbsolutePath()); 
    File file = new File(absolute_path+"/"+sf); 
    println(file);
    if (!file.exists()) { 
      boolean success = true; 
      success = file.mkdirs();
    } 
    saveTable(table, file+"/"+tf);
    println("File saved successfully.");
    file_saved = true;
  } 
  catch (Exception e) { 
    println("Error while saving file: " + e);
  }
} 


private static String[] PERMISSIONS_STORAGE = { Manifest.permission.READ_EXTERNAL_STORAGE, Manifest.permission.WRITE_EXTERNAL_STORAGE }; 
private void requestParticularPermission() { 
  activity.requestPermissions(PERMISSIONS_STORAGE, 2020);
}

public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) { 
  switch (requestCode) { 
  case 2020: 
    if (grantResults[0] == PackageManager.PERMISSION_GRANTED) { 
      println("permissions granted");
      permissions_granted = true;
    } else { 
      println("permissions not granted");
    } 
    break; 
  default: 
    activity.onRequestPermissionsResult(requestCode, permissions, grantResults);
  }
}

void onStop() {
  file_saved = false;
}

void onPause() {
  file_saved = false;
}

Thanks! I’ll try implementing this code.

Hmm, I tried implementing this, but it didn’t seem to work. It may be because I used the loadTable() function wrong, but I will have to keep trying.

I’ve edited the code above and tested it on a KitKat, a Lollipop, and an Android 10 device. I would like to ask you to retry the code again. Previous installations of the app should be uninstalled, to have a clean start. On devices with an Android version above Lollipop, immediately after installing, a dialog will appear asking permission to access photos, media, and other files. (Unless a previous app was not uninstalled) After running the app a folder named “myTables” containing a file “MyTable.csv” should exist on your internal storage SDcard. (Not in Sketch data folder!) The permission WRITE_EXTERNAL_STORAGE is required when compiling. If you use the APDE app, please use the “app mode”, instead of the “preview mode”.

Sorry, but the code is not able to write a save file. I will try using a tsv file because on a website it says that you cannot write a csv file, only read it. However, it did say that you can write a tsv file.

Did you give the permission WRITE_EXTERNAL_STORAGE?
Could you please tell me what device and which Android version you are using?

I’m using a samsung s10e, and I always had the write external storage permission enabled. I don’t know for sure what android version it is, but I think it is that latest API.

But does it give any error? Is there something else written in the console?
Are you using PC or APDE?

@akenaton could you revise the code above?

@SNICKRS Until what API are you targeting?
Please add this to your Manifest and try once more.

<manifest ... >
  <application android:requestLegacyExternalStorage="true" ... >
  </application>
</manifest>

@ noel=== your code seems to be good (yet i ll test tomorrow) and it is difficult to answer without any error code

1 Like

Maybe I should send my code? Then we can figure out the error.

Oh sure. If you don’t want it public you can send it as a message…

int[] scores = new int[10];
for(int i = 0; i < scores.length; i++) {
scores[i] = 0;
}
table = null;
table = loadTable(“highscore.csv”, “header”);
if(table != null){
for(TableRow row : table.rows()){
int id = row.getInt(“id”);
scores[id] = row.getInt(“savedata”);
}
} else {
table = new Table();
table.addColumn(“id”);
table.addColumn(“savedata”);
for(int i = 0; i < scores.length; i++){
TableRow newRow = table.addRow();
newRow.setInt(“id”, table.lastRowIndex());
newRow.setInt(“savedata”, scores[i]);
}
}

It doesn’t give an error message, because I don’t test on the emulator, I test on a phone. Also, the tsv file method didn’t work.

Sorry I was too tired and fell asleep.
Seems that maybe you did not load the table correctly for Android.
Try this code. If everything goes well, a folder “GameTables” will be created in your sdcard with the table file in it.

//  Be sure to give permission WRITE_EXTERNAL_STORAGE

import android.os.Environment;
import android.Manifest; 
import android.content.pm.PackageManager; 
import android.os.Build; 
import android.os.Build.VERSION_CODES; 
import processing.core.PConstants;
import android.app.Activity; 
import android.os.Environment;

Table table; 
Activity activity; 
boolean permissions_granted;
String tableFile = "highscore.csv";
String tableFolder = "GameTables";
int[] scores = new int[10];

void setup() { 
  size(displayWidth, displayHeight);
  activity = this.getActivity();
  if (Build.VERSION.SDK_INT > Build.VERSION_CODES.LOLLIPOP_MR1) { 
    requestParticularPermission();
  }
  
  for (int i = 0; i < scores.length; i++) {
    scores[i] = 0;
  }
  loadMyTable(tableFile);
  if (table != null) {
    for (TableRow row : table.rows()) {
      int id = row.getInt("id");
      scores[id] = row.getInt("savedata");
    }
  }
  printArray(scores);
 
  // gain points in game
  scores[0] = 123;
  scores[1] = 456;
  scores[9] = 789;
  setTableValues();
  printArray(scores);
}

void draw() {
}

void setTableValues() {
  table = new Table();
  table.addColumn("id");
  table.addColumn("savedata");
  for (int i = 0; i < scores.length; i++) {
    TableRow newRow = table.addRow();
    newRow.setInt("id", table.lastRowIndex());
    newRow.setInt("savedata", scores[i]);
  }
  saveMyTable(tableFile, tableFolder);
}

void saveMyTable(String file_s, String folder_s) { 
  try { 
    String absolute_path = new String(Environment.getExternalStorageDirectory().getAbsolutePath()); 
    File file = new File(absolute_path+"/"+folder_s); 
    println(file);
    if (!file.exists()) { 
      boolean success = true; 
      success = file.mkdirs();
    } 
    saveTable(table, file+"/"+file_s);
    println("File saved successfully.");
  } 
  catch (Exception e) { 
    println("Error while saving file: " + e);
  }
} 

void loadMyTable(String s) {
  try {
    String directory = new String(Environment.getExternalStorageDirectory().getAbsolutePath()+"/"+tableFolder+"/");
    table = loadTable(directory+s, "header");
    println(directory+s+tableFile);
  } 
  catch (Exception e) {
   println("first table loading");
  }
}

private static String[] PERMISSIONS_STORAGE = { Manifest.permission.READ_EXTERNAL_STORAGE, Manifest.permission.WRITE_EXTERNAL_STORAGE }; 
private void requestParticularPermission() { 
  activity.requestPermissions(PERMISSIONS_STORAGE, 2020);
}

public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) { 
  switch (requestCode) { 
  case 2020: 
    if (grantResults[0] == PackageManager.PERMISSION_GRANTED) { 
      println("permissions granted");
      permissions_granted = true;
    } else { 
      println("permissions not granted");
    } 
    break; 
  default: 
    activity.onRequestPermissionsResult(requestCode, permissions, grantResults);
  }
}

This code works for me, and so does the other one.

1 Like