Implement "Open game/user data directory" menus on Android[ci skip]

This commit is contained in:
biroder 2023-10-29 17:17:01 +00:00
parent 12a305becb
commit 06ae269b7b
6 changed files with 73 additions and 7 deletions

View File

@ -10,16 +10,20 @@ import android.os.CancellationSignal;
import android.os.ParcelFileDescriptor;
import android.provider.DocumentsContract;
import android.provider.DocumentsContract.Document;
import android.provider.DocumentsContract.Path;
import android.provider.DocumentsContract.Root;
import android.provider.DocumentsProvider;
import android.util.Log;
import android.webkit.MimeTypeMap;
import androidx.annotation.Nullable;
import androidx.annotation.RequiresApi;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.nio.file.Files;
import java.util.LinkedList;
import static android.os.Build.VERSION.SDK_INT;
@ -175,6 +179,29 @@ public class DoukutsuDocumentsProvider extends DocumentsProvider {
}
}
@Override
@RequiresApi(Build.VERSION_CODES.O)
public Path findDocumentPath(@Nullable String parentDocumentId, String childDocumentId) throws FileNotFoundException {
if (parentDocumentId == null) {
parentDocumentId = getContext().getFilesDir().getAbsolutePath();
}
File childFile = new File(childDocumentId);
if (!childFile.exists()) {
throw new FileNotFoundException(childFile.getAbsolutePath()+" doesn't exist");
} else if (!isChildDocument(parentDocumentId, childDocumentId)) {
throw new FileNotFoundException(childDocumentId+" is not child of "+parentDocumentId);
}
LinkedList<String> path = new LinkedList<>();
while (childFile != null && isChildDocument(parentDocumentId, childFile.getAbsolutePath())) {
path.addFirst(childFile.getAbsolutePath());
childFile = childFile.getParentFile();
}
return new Path(parentDocumentId, path);
}
@Override
public String getDocumentType(String documentId) throws FileNotFoundException {
File file = new File(documentId);

View File

@ -1,14 +1,17 @@
package io.github.doukutsu_rs;
import android.app.AlertDialog;
import android.app.NativeActivity;
import android.content.ActivityNotFoundException;
import android.content.Intent;
import android.content.res.Configuration;
import android.hardware.SensorManager;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.provider.DocumentsContract;
import android.view.OrientationEventListener;
import android.view.WindowInsets;
import android.widget.Toast;
import java.io.File;
@ -87,7 +90,27 @@ public class GameActivity extends NativeActivity {
this.displayInsets[2] = Math.max(this.displayInsets[0], cutout.getSafeInsetRight());
this.displayInsets[3] = Math.max(this.displayInsets[0], cutout.getSafeInsetBottom());
}
}
}
public void openDir(String path) {
Uri uri = DocumentsContract.buildDocumentUri(BuildConfig.DOCUMENTS_AUTHORITY, path);
File file = new File(path);
if (!file.isDirectory()) {
Toast.makeText(getApplicationContext(), R.string.dir_not_found, Toast.LENGTH_LONG).show();
return;
}
Intent intent = new Intent(Intent.ACTION_VIEW);
intent.addCategory(Intent.CATEGORY_DEFAULT);
intent.setDataAndType(uri, DocumentsContract.Document.MIME_TYPE_DIR);
intent.setFlags(Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION | Intent.FLAG_GRANT_PREFIX_URI_PERMISSION | Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
try {
startActivity(intent);
} catch(ActivityNotFoundException e) {
Toast.makeText(getApplicationContext(), R.string.no_app_found_to_open_dir, Toast.LENGTH_LONG).show();
}
}
}

View File

@ -6,7 +6,7 @@
<string name="download_desc">No data files found, would you like to download them?</string>
<string name="download_status_error_http">Bad HTTP response code: %d</string>
<!-- Downloading {entry_name}... {percentage progress}% ({downloaded}/{from} KiB, {speed} KiB/s) -->
<!-- Downloading {entry_name}… {progress}% ({downloaded_size}/{total_size} KiB, {speed} KiB/s) -->
<string name="download_status_downloading">Downloading %1$s… %2$d%% (%3$d/%4$d KiB, %5$d KiB/s)</string>
<string name="download_status_downloading_null">Downloading %1$s… --%% (%2$d KiB, %3$d KiB/s)</string>
<string name="download_status_unpacking">Unpacking: %s</string>
@ -14,4 +14,7 @@
<!-- Look {entry_name} on 9th line -->
<string name="download_entries_base">base game files</string>
<string name="dir_not_found">Dir not found</string>
<string name="no_app_found_to_open_dir">No app found to open dir</string>
</resources>

View File

@ -147,7 +147,6 @@ fn get_insets() -> GameResult<(f32, f32, f32, f32)> {
let vm = JavaVM::from_raw(vm_ptr)?;
let vm_env = vm.attach_current_thread()?;
//let class = vm_env.find_class("io/github/doukutsu_rs/MainActivity")?;
let class = vm_env.new_global_ref(JObject::from_raw(ndk_glue::native_activity().activity()))?;
let field = vm_env.get_field(class.as_obj(), "displayInsets", "[I")?.to_jni().l as jni::sys::jintArray;
@ -155,11 +154,11 @@ fn get_insets() -> GameResult<(f32, f32, f32, f32)> {
vm_env.get_int_array_region(field, 0, &mut elements)?;
vm_env.delete_local_ref(JObject::from_raw(field));
//Game always runs with horizontal orientation so top and bottom cutouts not needed and only wastes piece of the screen
elements[1] = 0;
elements[3] = 0;
Ok((elements[0] as f32, elements[1] as f32, elements[2] as f32, elements[3] as f32))
}
}

View File

@ -196,7 +196,21 @@ impl FilesystemContainer {
return Ok(()); // can't open directories on switch
#[cfg(target_os = "android")]
return Ok(()); // TODO: figure out how to do this on android
unsafe {
use jni::objects::{JObject, JValue};
use jni::JavaVM;
let vm_ptr = ndk_glue::native_activity().vm();
let vm = JavaVM::from_raw(vm_ptr)?;
let vm_env = vm.attach_current_thread()?;
let class = vm_env.new_global_ref(JObject::from_raw(ndk_glue::native_activity().activity()))?;
let method = vm_env.call_method(class.as_obj(), "openDir", "(Ljava/lang/String;)V", &[
JValue::from(vm_env.new_string(path.to_str().unwrap()).unwrap())
])?;
return Ok(());
}
#[cfg(not(any(target_os = "android", target_os = "horizon")))]
open::that(path).map_err(|e| {

View File

@ -392,7 +392,7 @@ impl SettingsMenu {
);
self.links.push_entry(LinksMenuEntry::Link(GETPLUS_LINK), MenuEntry::Active("Get Cave Story+".to_owned()));
#[cfg(not(any(target_os = "android", target_os = "horizon")))]
#[cfg(not(any(target_os = "horizon")))]
self.main.push_entry(
MainMenuEntry::Advanced,
MenuEntry::Active(state.loc.t("menus.options_menu.advanced").to_owned()),