#Android application state
1 messages · Page 1 of 1 (latest)
Hey, I came across your question as I had/have the exact same one. For anyone else who may be asking:
- I tried using SQLite database but I never got it to work. Mainly the
rusqlitecrate doesn't appear to be wasm compatible. - Instead, I ended up creating a JSON file and adding it to the local files directory in the Dioxus Android app. I then use
serdecrate to serialize/deserialize and thestd::fscrate to read/write to the JSON. This works great, but has its obvious limitations. - I think there is a sqlite-wasm crate but my use case was simple so I never dove into this.
Dioxus SDK has a cross platform hook that works on web + desktop with a similar approach: https://github.com/DioxusLabs/sdk/tree/main/examples/storage
It use local storage in wasm
Nice, thank you, Evan!
where exactly do you save your local files ?
Doesn't seem to work on mobile (or I'm missing something, which is quite likely)
I ended up hard coding a path to the data directory within the android phone and creating a JSON file there, kind of like this:
pub fn get_storage_path() -> PathBuf {
let path = PathBuf::from("/data/user/0/com.example.yourAppName/files");
fs::create_dir_all(&path)
.expect("Failed to create app storage directory");
path.join("yourAppName.json")
}
This creates a JSON file that you can read/write to from your app. Although hard coding is not ideal, it worked for the time being. I had my phone plugged up to the computer thru a USB cable; not the Android emulator. And I used commands like this:
adb shell run-as com.example.yourAppName
to get in the shell of the phone so that I could find the files path with the pwd command. The file folder is what you need to write to. I would write to it like this:
pub fn save_data(data: &SomeStruct) {
let path = get_storage_path();
if let Ok(json) = serde_json::to_string_pretty(data) {
fs::write(path, json).expect("Failed to save data");
}
}
Thank you for the detailed answer! Does the app name / path matter or do I just make up one ? (Does it have to match with something I set up in dioxus ?)
Yea, it definitely matters. You can set up custom names in Dioxus.toml but the easist thing to do is find the default name. It would be something like.: com.example.whateverYourAppNameIs . If you are not sure, you can plug your phone up, install the .apk file with something like:
adb install /your/file/path/target/dx/your-app-name/debug/android/app/app/build/outputs/apk/debug/app-debug.apk
Then you should be able to find the app name somewhere in the app list with:
adb shell pm list packages | grep <INSERT SOME PATTERN HERE>
When I run that command, I found my app under the name com.example.myAppName. This is all assuming you are using Linux. Idk anything about mac or windows.
That saved me!
I was trying the same thing but getting a PermissionDenied error until I found your implementation. Turns out I had to create the directories (that fs::create_dir_all).
Regarding the hard coded path, you can actually call the Andriod java API with JNI. I'm using this right now:
use jni::JavaVM;
let ctx = ndk_context::android_context();
let vm = unsafe { JavaVM::from_raw(ctx.vm().cast()) }?;
let mut env = vm.attach_current_thread()?;
let ctx = unsafe { jni::objects::JObject::from_raw(ctx.context().cast()) };
let files_dir = env
.call_method(ctx, "getFilesDir", "()Ljava/io/File;", &[])?
.l()?;
let files_dir_jstring: jni::objects::JString = env
.call_method(&files_dir, "toString", "()Ljava/lang/String;", &[])?
.l()?
.try_into().unwrap();
let files_dir = env.get_string(&files_dir_jstring)?;
let files_dir = PathBuf::from(files_dir.to_str().unwrap());
fs::create_dir_all(&files_dir).expect("Failed to create app files directory");
Ok(files_dir)
}```
As for the permissions, I found that you can tweak the app's Manifest.xml if you are brave enough (though it will be reset at every rebuild), but i haven't managed to get the runtime permissions request to pop yet. Not sure if that's possible at the moment.
Quick update:
Actually this works for permissions!!
https://github.com/slint-ui/slint/discussions/5692
I implemented it like this:
let ctx = ndk_context::android_context();
let vm = unsafe { JavaVM::from_raw(ctx.vm().cast()).unwrap() };
let mut env = vm.attach_current_thread().unwrap();
let activity = unsafe { JObject::from_raw(ctx.context().cast()) };
let record_string = env.new_string("android.permission.CAMERA").unwrap();
let string_class = env.find_class("java/lang/String").unwrap();
let permissions = env.new_object_array(1, string_class, record_string).unwrap();
env.call_method(
activity,
"requestPermissions",
"([Ljava/lang/String;I)V",
&[(&permissions).into(), jni::objects::JValueGen::Int(1)]
).unwrap();
}```
To get that snippet to work, did you need to make any changes to main or any init calls? Sticking just that into a basic dioxus build for android seems to hang at ndk_context::android_context() for me, though I see the logs showing a panic from dioxus-mobile with "Android to have set the app trampoline"
typical, of course as soon as I ask others I get the revelation - I was calling it before hitting dioxus::launch, so some state within dioxus-mobile wasn't initialised yet, which it needed