Skip to content

Commit e07493f

Browse files
authored
Merge pull request #3338 from Ghabry/android
Android: Add button to open save directory and better "open folder" experience
2 parents 8296199 + 587e071 commit e07493f

File tree

11 files changed

+108
-69
lines changed

11 files changed

+108
-69
lines changed

builds/android/app/src/gamebrowser/org_easyrpg_player_game_browser.cpp

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -187,6 +187,10 @@ Java_org_easyrpg_player_game_1browser_GameScanner_findGames(JNIEnv *env, jclass,
187187
// jpath is the SAF path to the game, is converted to FilesystemView "root"
188188
std::string spath = jstring_to_string(env, jpath);
189189
auto root = FileFinder::Root().Create(spath);
190+
if (!root) {
191+
return nullptr;
192+
}
193+
190194
root.ClearCache();
191195

192196
auto ge_list = FileFinder::FindGames(root);
@@ -196,7 +200,7 @@ Java_org_easyrpg_player_game_1browser_GameScanner_findGames(JNIEnv *env, jclass,
196200

197201
if (ge_list.empty()) {
198202
// No games found
199-
return jgame_array;
203+
return nullptr;
200204
}
201205

202206
jmethodID jgame_constructor_unsupported = env->GetMethodID(jgame_class, "<init>", "(I)V");

builds/android/app/src/main/java/org/easyrpg/player/Helper.java

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
import android.app.Activity;
44
import android.content.ContentResolver;
55
import android.content.Context;
6+
import android.content.Intent;
67
import android.content.res.Resources;
78
import android.database.Cursor;
89
import android.graphics.Bitmap;
@@ -17,6 +18,8 @@
1718
import android.util.Log;
1819
import android.util.TypedValue;
1920
import android.view.View;
21+
import android.view.ViewGroup;
22+
import android.widget.Button;
2023
import android.widget.RelativeLayout;
2124
import android.widget.RelativeLayout.LayoutParams;
2225

@@ -356,4 +359,41 @@ public static Bitmap createBitmapFromRGBA(byte[] rgba, int width, int height) {
356359
return bitmap;
357360
}
358361

362+
public static void attachOpenFolderButton(Context context, Button button, Uri uri) {
363+
if (android.os.Build.VERSION.SDK_INT >= 26) {
364+
button.setOnClickListener(v -> {
365+
openFileBrowser(context, uri);
366+
});
367+
} else {
368+
// ACTION_OPEN_DOCUMENT does not support providing an URI
369+
// Useless, remove the button
370+
ViewGroup layout = (ViewGroup) button.getParent();
371+
if(layout != null) {
372+
layout.removeView(button);
373+
}
374+
}
375+
}
376+
377+
public static boolean openFileBrowser(Context context, Uri uri) {
378+
if (android.os.Build.VERSION.SDK_INT >= 29) {
379+
// Open the file explorer in the folder specified by URI
380+
// This opens a real file browser which allows file operations like view, copy, etc.
381+
Intent intent = new Intent(Intent.ACTION_VIEW);
382+
intent.setDataAndType(uri, DocumentsContract.Document.MIME_TYPE_DIR);
383+
context.startActivity(intent);
384+
} else if (android.os.Build.VERSION.SDK_INT >= 26) {
385+
// Open the file explorer in the folder specified by URI
386+
// This opens a (useless) file chooser which closes itself after selecting a file
387+
// Still better than nothing because the user can see where the folder is
388+
Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT);
389+
intent.setType("*/*");
390+
intent.putExtra(DocumentsContract.EXTRA_INITIAL_URI, uri);
391+
context.startActivity(intent);
392+
} else {
393+
// ACTION_OPEN_DOCUMENT does not support providing an URI
394+
return false;
395+
}
396+
397+
return true;
398+
}
359399
}

builds/android/app/src/main/java/org/easyrpg/player/game_browser/Game.java

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
import androidx.annotation.NonNull;
1111
import androidx.documentfile.provider.DocumentFile;
1212

13+
import org.easyrpg.player.Helper;
1314
import org.easyrpg.player.settings.SettingsManager;
1415

1516
import java.io.ByteArrayOutputStream;
@@ -188,6 +189,20 @@ public String toString() {
188189
return getDisplayTitle();
189190
}
190191

192+
public Uri createSaveUri(Context context) {
193+
if (!getSavePath().isEmpty()) {
194+
DocumentFile saveFolder = Helper.createFolderInSave(context, getSavePath());
195+
196+
if (saveFolder != null) {
197+
return saveFolder.getUri();
198+
}
199+
} else {
200+
return Uri.parse(getGameFolderPath());
201+
}
202+
203+
return null;
204+
}
205+
191206
public static Game fromCacheEntry(Context context, String cache) {
192207
String[] entries = cache.split(String.valueOf(escapeCode));
193208

builds/android/app/src/main/java/org/easyrpg/player/game_browser/GameBrowserActivity.java

Lines changed: 19 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,6 @@
2323

2424
import androidx.annotation.NonNull;
2525
import androidx.appcompat.app.ActionBarDrawerToggle;
26-
import androidx.appcompat.app.AppCompatActivity;
2726
import androidx.appcompat.widget.Toolbar;
2827
import androidx.core.view.GravityCompat;
2928
import androidx.drawerlayout.widget.DrawerLayout;
@@ -33,10 +32,13 @@
3332
import com.google.android.material.navigation.NavigationView;
3433

3534
import org.easyrpg.player.BaseActivity;
35+
import org.easyrpg.player.Helper;
3636
import org.easyrpg.player.R;
3737
import org.easyrpg.player.settings.SettingsManager;
3838
import org.libsdl.app.SDL;
3939

40+
import java.util.ArrayList;
41+
import java.util.Arrays;
4042
import java.util.Collections;
4143
import java.util.List;
4244

@@ -358,22 +360,33 @@ public void onBindViewHolder(final ViewHolder holder, final int position) {
358360

359361
// Settings Button
360362
holder.settingsButton.setOnClickListener(v -> {
361-
String[] choices_list = {
363+
ArrayList<String> choices_list = new ArrayList<String>(Arrays.asList(
362364
activity.getResources().getString(R.string.select_game_region),
363365
activity.getResources().getString(R.string.game_rename),
364366
activity.getResources().getString(R.string.launch_debug_mode)
365-
};
367+
));
368+
369+
if (android.os.Build.VERSION.SDK_INT >= 26) {
370+
choices_list.add(activity.getResources().getString(R.string.open_save_folder));
371+
}
372+
373+
// It's 2025 and converting an ArrayList to an Array is still hot-garbage in Java
374+
// because of type erasure and ugly APIs
375+
String[] choices_list_arr = new String[choices_list.size()];
376+
choices_list.toArray(choices_list_arr);
366377

367378
AlertDialog.Builder builder = new AlertDialog.Builder(activity);
368379
builder
369380
.setTitle(R.string.settings)
370-
.setItems(choices_list, (dialog, which) -> {
381+
.setItems(choices_list_arr, (dialog, which) -> {
371382
if (which == 0) {
372-
chooseRegion(activity, holder, gameList.get(position));
383+
chooseRegion(activity, holder, game);
373384
} else if (which == 1) {
374-
renameGame(activity, holder, gameList.get(position));
385+
renameGame(activity, holder, game);
375386
} else if (which == 2) {
376387
launchGame(position, true);
388+
} else if (which == 3) {
389+
Helper.openFileBrowser(activity, game.createSaveUri(activity));
377390
}
378391
});
379392
builder.show();

builds/android/app/src/main/java/org/easyrpg/player/game_browser/GameBrowserHelper.java

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -194,14 +194,22 @@ public static SafError dealAfterFolderSelected(Activity activity, int requestCod
194194

195195
List<String[]> items = Helper.listChildrenDocuments(activity, folder.getUri());
196196
int item_count = 0;
197+
197198
for (String[] item: items) {
198-
if (item[0] == null || Helper.isDirectoryFromMimeType(item[1]) || item[0].endsWith(".nomedia")) {
199+
if (item[2] == null ||
200+
item[2].contains(".") ||
201+
item[2].equals(SettingsManager.RTP_FOLDER_NAME) ||
202+
item[2].equals(SettingsManager.GAMES_FOLDER_NAME) ||
203+
item[2].equals(SettingsManager.SOUND_FONTS_FOLDER_NAME) ||
204+
item[2].equals(SettingsManager.SAVES_FOLDER_NAME) ||
205+
item[2].equals(SettingsManager.FONTS_FOLDER_NAME)
206+
) {
199207
continue;
200208
}
201209

202210
item_count += 1;
203211

204-
if (item_count >= 3) {
212+
if (item_count > 3) {
205213
return SafError.FOLDER_NOT_ALMOST_EMPTY;
206214
}
207215
}

builds/android/app/src/main/java/org/easyrpg/player/game_browser/GameScanner.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -171,6 +171,10 @@ private void scanRootFolder(Activity activity, Uri folderURI) {
171171

172172
Game[] candidates = findGames(fileURIs.get(i).toString(), names.get(i));
173173

174+
if (candidates == null) {
175+
continue;
176+
}
177+
174178
for (Game candidate: candidates) {
175179
if (candidate != null) {
176180
gameList.add(candidate);

builds/android/app/src/main/java/org/easyrpg/player/settings/SettingsAudioActivity.java

Lines changed: 1 addition & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -37,21 +37,7 @@ protected void onCreate(Bundle savedInstanceState) {
3737
// Setup UI components
3838
// The Soundfont Button
3939
Button button = this.findViewById(R.id.button_open_soundfont_folder);
40-
// We can open the file picker in a specific folder only with API >= 26
41-
if (android.os.Build.VERSION.SDK_INT >= 26) {
42-
button.setOnClickListener(v -> {
43-
// Open the file explorer in the "soundfont" folder
44-
Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT);
45-
intent.setType("*/*");
46-
intent.putExtra(DocumentsContract.EXTRA_INITIAL_URI, SettingsManager.getSoundFontsFolderURI(this));
47-
startActivity(intent);
48-
});
49-
} else {
50-
ViewGroup layout = (ViewGroup) button.getParent();
51-
if(layout != null) {
52-
layout.removeView(button);
53-
}
54-
}
40+
Helper.attachOpenFolderButton(this, button, SettingsManager.getSoundFontsFolderURI(this));
5541

5642
configureMusicVolume();
5743
configureSoundVolume();

builds/android/app/src/main/java/org/easyrpg/player/settings/SettingsFontActivity.java

Lines changed: 1 addition & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -49,21 +49,7 @@ protected void onCreate(Bundle savedInstanceState) {
4949
// Setup UI components
5050
// The Font Button
5151
Button button = this.findViewById(R.id.button_open_font_folder);
52-
// We can open the file picker in a specific folder only with API >= 26
53-
if (android.os.Build.VERSION.SDK_INT >= 26) {
54-
button.setOnClickListener(v -> {
55-
// Open the file explorer in the "fonts" folder
56-
Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT);
57-
intent.setType("*/*");
58-
intent.putExtra(DocumentsContract.EXTRA_INITIAL_URI, SettingsManager.getFontsFolderURI(this));
59-
startActivity(intent);
60-
});
61-
} else {
62-
ViewGroup layout = (ViewGroup) button.getParent();
63-
if(layout != null) {
64-
layout.removeView(button);
65-
}
66-
}
52+
Helper.attachOpenFolderButton(this, button, SettingsManager.getFontsFolderURI(this));
6753

6854
configureFont1Size();
6955
configureFont2Size();

builds/android/app/src/main/java/org/easyrpg/player/settings/SettingsGamesFolderActivity.java

Lines changed: 3 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
import androidx.appcompat.app.AppCompatActivity;
1414

1515
import org.easyrpg.player.BaseActivity;
16+
import org.easyrpg.player.Helper;
1617
import org.easyrpg.player.R;
1718
import org.easyrpg.player.game_browser.GameBrowserActivity;
1819
import org.easyrpg.player.game_browser.GameBrowserHelper;
@@ -41,39 +42,11 @@ public void onCreate(Bundle savedInstanceState) {
4142
// Setup UI components
4243
// The "Open Game Folder" Button
4344
Button openGameFolderButton = this.findViewById(R.id.open_game_folder);
44-
// We can open the file picker in a specific folder only with API >= 26
45-
if (android.os.Build.VERSION.SDK_INT >= 26) {
46-
openGameFolderButton.setOnClickListener(v -> {
47-
// Open the file explorer in the "soundfont" folder
48-
Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT);
49-
intent.setType("*/*");
50-
intent.putExtra(DocumentsContract.EXTRA_INITIAL_URI, SettingsManager.getGamesFolderURI(this));
51-
startActivity(intent);
52-
});
53-
} else {
54-
ViewGroup layout = (ViewGroup) openGameFolderButton.getParent();
55-
if(layout != null) {
56-
layout.removeView(openGameFolderButton);
57-
}
58-
}
45+
Helper.attachOpenFolderButton(this, openGameFolderButton, SettingsManager.getGamesFolderURI(this));
5946

6047
// The "Open RTP Folder" Button
6148
Button openRTPFolderButton = this.findViewById(R.id.open_rtp_folder);
62-
// We can open the file picker in a specific folder only with API >= 26
63-
if (android.os.Build.VERSION.SDK_INT >= 26) {
64-
openRTPFolderButton.setOnClickListener(v -> {
65-
// Open the file explorer in the "soundfont" folder
66-
Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT);
67-
intent.setType("*/*");
68-
intent.putExtra(DocumentsContract.EXTRA_INITIAL_URI, SettingsManager.getRTPFolderURI(this));
69-
startActivity(intent);
70-
});
71-
} else {
72-
ViewGroup layout = (ViewGroup) openRTPFolderButton.getParent();
73-
if(layout != null) {
74-
layout.removeView(openRTPFolderButton);
75-
}
76-
}
49+
Helper.attachOpenFolderButton(this, openRTPFolderButton, SettingsManager.getRTPFolderURI(this));
7750

7851
// Video button
7952
findViewById(R.id.watch_video).setOnClickListener(v -> {

builds/android/app/src/main/res/values/strings.xml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@
3434
<string name="change_the_layout">Change the layout</string>
3535
<string name="launch_debug_mode">Launch in debug mode</string>
3636
<string name="game_rename">Rename game</string>
37+
<string name="open_save_folder">Open savegame folder</string>
3738
<string name="choose_layout">Choose a layout</string>
3839
<string name="unknown_region">Unknown region</string>
3940
<string name="region_modification_failed">Changing region failed</string>

0 commit comments

Comments
 (0)