#Android application state

1 messages · Page 1 of 1 (latest)

naive crypt
#

Hello,

I'm trying to build a simple Android application using Dioxus. I've been able to have a simple example working in the Android emulator. What is the recommended way of storing application state locally on the phone? Are there any examples showing how to set this up? Thanks!

daring slate
#

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 rusqlite crate 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 serde crate to serialize/deserialize and the std::fs crate 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.
river shuttle
#

It use local storage in wasm

daring slate
#

Nice, thank you, Evan!

lost venture
lost venture
daring slate
# lost venture where exactly do you save your local files ?

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");
    }
}
lost venture
daring slate
#

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.

median glen
#

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();
}```
GitHub

I've got a basic app up and running in android with slint and rust. Is there any examples of setting up permissions in the same way android apps do? I'd like to add some Bluetooth support t...

rain flume
#

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