Ah, the full screen Android experience sure is nice isn't it? But what about that status bar sometimes?
The Android status bar may reappear when you minimize the app or pull up the menu, disrupting the full-screen experience. Sometimes onWindowFocusChanged
will not be triggered if the user is slightly dragging the top of screen.
Did you know you can add your own custom Android Activity for your NativeScript app?
You can create your own with TypeScript as mentioned in the docs here.
To effectively manage the visibility of the status bar, you should implement the onWindowFocusChanged
method, which allows you to monitor when the app is minimized. Additionally, use the onCreate
method to set the initial state of the status bar to be transparent and hidden. To address the issue of onWindowFocusChanged
not being triggered during slight drags, override the dispatchTouchEvent
method to monitor touch events. This will help you determine if the user is dragging from the top of the screen; if so, you can use a boolean flag to hide the status bar accordingly.
./src/activity.android.ts
import {
Application,
setActivityCallbacks,
AndroidActivityCallbacks,
Utils,
} from "@nativescript/core";
@NativeClass()
@JavaProxy("org.nativescript.mycoolapp.MyCustomActivity")
class MyCustomActivity extends androidx.appcompat.app.AppCompatActivity {
isNativeScriptActivity: boolean;
isDragFromTop = false;
check = false;
private _callbacks: AndroidActivityCallbacks;
onCreate(savedInstanceState: android.os.Bundle): void {
Application.android.init(this.getApplication());
this.isNativeScriptActivity = true;
if (!this._callbacks) {
setActivityCallbacks(this);
}
this._callbacks.onCreate(
this,
savedInstanceState,
this.getIntent(),
super.onCreate
);
this.setFullScreen();
}
setFullScreen() {
const activity = Utils.android.getCurrentActivity();
const window = activity.getWindow();
const decorView = window.getDecorView();
let uiOptions = decorView.getSystemUiVisibility();
uiOptions =
uiOptions |
android.view.View.SYSTEM_UI_FLAG_LAYOUT_STABLE |
android.view.View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN;
decorView.setSystemUiVisibility(uiOptions);
window.setStatusBarColor(android.graphics.Color.TRANSPARENT);
const windowInsetsControllerCompat =
androidx.core.view.WindowCompat.getInsetsController(window, decorView);
windowInsetsControllerCompat.hide(
androidx.core.view.WindowInsetsCompat.Type.statusBars()
);
}
dispatchTouchEvent(event: android.view.MotionEvent) {
const action = event.getAction();
switch (action) {
case android.view.MotionEvent.ACTION_DOWN:
if (event.getY() < 100) {
this.isDragFromTop = true;
}
break;
case android.view.MotionEvent.ACTION_MOVE:
if (this.isDragFromTop && this.check) {
this.hideit();
}
break;
case android.view.MotionEvent.ACTION_UP:
case android.view.MotionEvent.ACTION_CANCEL:
this.isDragFromTop = false;
break;
}
return super.dispatchTouchEvent(event);
}
hideit() {
const activity = Utils.android.getCurrentActivity();
const window = activity.getWindow();
const decorView = window.getDecorView();
const windowInsetsControllerCompat =
androidx.core.view.WindowCompat.getInsetsController(window, decorView);
windowInsetsControllerCompat.hide(
androidx.core.view.WindowInsetsCompat.Type.statusBars()
);
}
onWindowFocusChanged(hasFocus: boolean) {
super.onWindowFocusChanged(hasFocus);
this.check = hasFocus;
if (hasFocus) {
this.hideit();
}
}
onNewIntent(intent: android.content.Intent): void {
this._callbacks.onNewIntent(
this,
intent,
super.setIntent,
super.onNewIntent
);
}
onSaveInstanceState(outState: android.os.Bundle): void {
this._callbacks.onSaveInstanceState(
this,
outState,
super.onSaveInstanceState
);
}
onStart(): void {
this._callbacks.onStart(this, super.onStart);
}
onStop(): void {
this._callbacks.onStop(this, super.onStop);
}
onDestroy(): void {
this._callbacks.onDestroy(this, super.onDestroy);
}
onPostResume(): void {
this._callbacks.onPostResume(this, super.onPostResume);
}
onBackPressed(): void {
this._callbacks.onBackPressed(this, super.onBackPressed);
}
onRequestPermissionsResult(
requestCode: number,
permissions: Array<string>,
grantResults: Array<number>
): void {
this._callbacks.onRequestPermissionsResult(
this,
requestCode,
permissions,
grantResults,
undefined
);
}
onActivityResult(
requestCode: number,
resultCode: number,
data: android.content.Intent
): void {
this._callbacks.onActivityResult(
this,
requestCode,
resultCode,
data,
super.onActivityResult
);
}
}
App_Resources/Android/src/main/AndroidManifest.xml
to use it:<activity
android:name="org.nativescript.mycoolapp.MyCustomActivity">
<!-- additional ommitted for brevity -->
</activity>
webpack.config.js
to include your custom activity:const webpack = require("@nativescript/webpack");
module.exports = (env) => {
webpack.init(env);
env.appComponents = (env.appComponents || []).concat(['./src/activity.android'])
return webpack.resolveConfig();
};
You will now have a full screen immersize app with no status bar 🎉
onWindowFocusChanged
method to detect when the app gains or loses focus.onCreate
method, configure the status bar to be transparent and hidden with setFullScreen
.onWindowFocusChanged
not being triggered during slight drags, override the dispatchTouchEvent
method. This allows us to monitor touch events and determine if the user is dragging from the top of the screen.A heartfelt thank you to those who have contributed valuable ideas and insights:
For any questions or discussions, feel free to join us on Discord! Connect with fellow developers and enthusiasts here: NativeScript Discord
Your participation is greatly appreciated!