5

Reading the documentation at https://developer.android.com/training/system-ui/immersive.html I can't seem to find any information on how to set full screen immersive mode in a Native Activity NDK app (without using JNI) as it seems full screen immersive mode can only be toggled from Java.

As it cannot be set from the manifest (Set Android immersive full screen mode in manifest), is there any way to request it via EGL?

Seems the only way to enable full screen immersive mode is to call setSystemUiVisibility via JNI ahead of requesting a surface via EGL?

Viktor Sehr
  • 12,825
  • 5
  • 58
  • 90
  • native-activity is an app with NO java(seems to fit what you want) [here](https://developer.android.com/ndk/samples/sample_na.html).The native-activity sample resides under the NDK installation root, in samples/native-activity. It is a very simple example of a purely native application, with no Java source code. In the absence of any Java source, the Java compiler still creates an executable stub for the virtual machine to run. The stub serves as a wrapper for the actual, native program, which is located in the .so file. – Jon Goodwin Nov 27 '17 at 09:51
  • Thank you, but my question is how I enable immersive full screen, setSystemUiVisibility is, as I understand, invokable via some JNI wrapper https://stackoverflow.com/questions/10184527/android-calling-java-class-from-c-native-activity. – Viktor Sehr Nov 27 '17 at 10:10
  • https://stackoverflow.com/questions/41059909/hiding-the-navigation-bar-in-pure-android-native/50831255#50831255 – kaiser Sep 29 '19 at 14:48

1 Answers1

2

Answering my own question, you can set immersive mode via C++/JNI without add java to your project. Here is my snippet, large parts are copied from somewhere on the internet.

auto portis::android_util::SetImmersiveMode(JNIEnv* env, android_app* iandroid_app) -> bool {
    PORTIS_ASSERT(iandroid_app && env);

    jclass activityClass = env->FindClass("android/app/NativeActivity");
    jclass windowClass = env->FindClass("android/view/Window");
    jclass viewClass = env->FindClass("android/view/View");
    jmethodID getWindow = env->GetMethodID(activityClass, "getWindow", "()Landroid/view/Window;");
    jmethodID getDecorView = env->GetMethodID(windowClass, "getDecorView", "()Landroid/view/View;");
    jmethodID setSystemUiVisibility = env->GetMethodID(viewClass, "setSystemUiVisibility", "(I)V");
    jmethodID getSystemUiVisibility = env->GetMethodID(viewClass, "getSystemUiVisibility", "()I");

    jobject windowObj = env->CallObjectMethod(iandroid_app->activity->clazz, getWindow);
    jobject decorViewObj = env->CallObjectMethod(windowObj, getDecorView);

    // Get flag ids
    jfieldID id_SYSTEM_UI_FLAG_LAYOUT_STABLE = env->GetStaticFieldID(viewClass, "SYSTEM_UI_FLAG_LAYOUT_STABLE", "I");
    jfieldID id_SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION = env->GetStaticFieldID(viewClass, "SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION", "I");
    jfieldID id_SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN = env->GetStaticFieldID(viewClass, "SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN", "I");
    jfieldID id_SYSTEM_UI_FLAG_HIDE_NAVIGATION = env->GetStaticFieldID(viewClass, "SYSTEM_UI_FLAG_HIDE_NAVIGATION", "I");
    jfieldID id_SYSTEM_UI_FLAG_FULLSCREEN = env->GetStaticFieldID(viewClass, "SYSTEM_UI_FLAG_FULLSCREEN", "I");
    jfieldID id_SYSTEM_UI_FLAG_IMMERSIVE_STICKY = env->GetStaticFieldID(viewClass, "SYSTEM_UI_FLAG_IMMERSIVE_STICKY", "I");

    // Get flags
    const int flag_SYSTEM_UI_FLAG_LAYOUT_STABLE = env->GetStaticIntField(viewClass, id_SYSTEM_UI_FLAG_LAYOUT_STABLE);
    const int flag_SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION = env->GetStaticIntField(viewClass, id_SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION);
    const int flag_SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN = env->GetStaticIntField(viewClass, id_SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN);
    const int flag_SYSTEM_UI_FLAG_HIDE_NAVIGATION = env->GetStaticIntField(viewClass, id_SYSTEM_UI_FLAG_HIDE_NAVIGATION);
    const int flag_SYSTEM_UI_FLAG_FULLSCREEN = env->GetStaticIntField(viewClass, id_SYSTEM_UI_FLAG_FULLSCREEN);
    const int flag_SYSTEM_UI_FLAG_IMMERSIVE_STICKY = env->GetStaticIntField(viewClass, id_SYSTEM_UI_FLAG_IMMERSIVE_STICKY);

    // Get current immersiveness
    const int currentVisibility = env->CallIntMethod(decorViewObj, getSystemUiVisibility);
    const bool is_SYSTEM_UI_FLAG_LAYOUT_STABLE = (currentVisibility & flag_SYSTEM_UI_FLAG_LAYOUT_STABLE) != 0;
    const bool is_SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION = (currentVisibility & flag_SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION) != 0;
    const bool is_SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN = (currentVisibility & flag_SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN) != 0;
    const bool is_SYSTEM_UI_FLAG_HIDE_NAVIGATION = (currentVisibility & flag_SYSTEM_UI_FLAG_HIDE_NAVIGATION) != 0;
    const bool is_SYSTEM_UI_FLAG_FULLSCREEN = (currentVisibility & flag_SYSTEM_UI_FLAG_FULLSCREEN) != 0;
    const bool is_SYSTEM_UI_FLAG_IMMERSIVE_STICKY = (currentVisibility & flag_SYSTEM_UI_FLAG_IMMERSIVE_STICKY) != 0;

    const auto isAlreadyImmersive =
        is_SYSTEM_UI_FLAG_LAYOUT_STABLE &&
        is_SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION &&
        is_SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN &&
        is_SYSTEM_UI_FLAG_HIDE_NAVIGATION &&
        is_SYSTEM_UI_FLAG_FULLSCREEN &&
        is_SYSTEM_UI_FLAG_IMMERSIVE_STICKY;
    PORTIS_LOGD()
        << "set_immersive data"
        << is_SYSTEM_UI_FLAG_LAYOUT_STABLE
        << is_SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
        << is_SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
        << is_SYSTEM_UI_FLAG_HIDE_NAVIGATION
        << is_SYSTEM_UI_FLAG_FULLSCREEN
        << is_SYSTEM_UI_FLAG_IMMERSIVE_STICKY;
    auto success = true;
    if (true) {
        const int flag =
            flag_SYSTEM_UI_FLAG_LAYOUT_STABLE |
            flag_SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION |
            flag_SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN |
            flag_SYSTEM_UI_FLAG_HIDE_NAVIGATION |
            flag_SYSTEM_UI_FLAG_FULLSCREEN |
            flag_SYSTEM_UI_FLAG_IMMERSIVE_STICKY;
        env->CallVoidMethod(decorViewObj, setSystemUiVisibility, flag);
        if(env->ExceptionCheck()) {
            // Read exception msg
            jthrowable e = env->ExceptionOccurred();
            env->ExceptionClear(); // clears the exception; e seems to remain valid
            jclass clazz = env->GetObjectClass(e);
            jmethodID getMessage = env->GetMethodID(clazz, "getMessage", "()Ljava/lang/String;");
            jstring message = (jstring)env->CallObjectMethod(e, getMessage);
            const char *mstr = env->GetStringUTFChars(message, NULL);
            const auto exception_msg = std::string{mstr};
            env->ReleaseStringUTFChars(message, mstr);
            env->DeleteLocalRef(message);
            env->DeleteLocalRef(clazz);
            env->DeleteLocalRef(e);
            PORTIS_LOGW() << "set_immersive exception [" << exception_msg << "]";
            success = false;
        }
        else {
            PORTIS_LOGI() << "set_immersive success";
        }
    }
    env->DeleteLocalRef(windowObj);
    env->DeleteLocalRef(decorViewObj);
    return success;
}
Viktor Sehr
  • 12,825
  • 5
  • 58
  • 90
  • 1
    this works and all but if you minimize then resume an app it requires that you reapply it because of how immersive_sticky works. and i can't get this code to reapply due to an error about how only the ui thread can touch the view. – Colby Ray Sep 02 '19 at 12:43