@maximilien0405/capacitor-android-launcher 7.0.1 → 7.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -4,11 +4,8 @@ This Capacitor plugin allows you to open the launcher settings to set your app a
4
4
 
5
5
  It can also, if needed, start (and stop) and immersive mode that hides the all UI elements (status bar and navigation bar). This is usefull for an Kiosk App, or for special apps made for childrens or seniors.
6
6
 
7
- ## Support
8
-
9
- ANDROID Only.
10
-
11
- - v7.X.X supports Capacitor V7 (Android minSdkVersion 23+).
7
+ > When set as the default launcher, your user will still be able to exit it, but when it happens it will re-open the app.
8
+ If you really want to prevent your user from leaving, i suggest "pinning" the app, and then setting it as the default launcher.
12
9
 
13
10
  ## Installation
14
11
 
@@ -19,7 +16,6 @@ npx cap sync
19
16
 
20
17
  ## Usage
21
18
 
22
-
23
19
  #### Open Launcher Settings
24
20
 
25
21
  This method opens the settings page where the user can set your app as the default launcher.
@@ -30,9 +26,7 @@ await AndroidLauncher.openLauncherSettings();
30
26
 
31
27
  #### Start Immersive Mode
32
28
 
33
- This method hides all system controls.
34
-
35
- > ⚠️ The app must be set as the launcher for it to work.
29
+ This method hides all system controls.
36
30
 
37
31
  ```ts
38
32
  await AndroidLauncher.startImmersiveMode();
@@ -4,10 +4,14 @@ import android.content.Intent;
4
4
  import android.content.pm.PackageManager;
5
5
  import android.content.pm.ResolveInfo;
6
6
  import android.os.Handler;
7
+ import android.app.Activity;
7
8
  import android.os.Build;
8
9
  import android.os.Looper;
9
10
  import android.view.View;
11
+ import android.view.ViewTreeObserver;
12
+ import android.view.WindowInsets;
10
13
  import android.view.WindowInsetsController;
14
+
11
15
  import androidx.appcompat.app.AppCompatActivity;
12
16
  import androidx.annotation.RequiresApi;
13
17
  import com.getcapacitor.Plugin;
@@ -20,11 +24,12 @@ public class AndroidLauncher {
20
24
  private AppCompatActivity activity;
21
25
  private Bridge bridge;
22
26
  private boolean immersiveModeEnabled = false;
27
+ private ViewTreeObserver.OnWindowFocusChangeListener focusChangeListener;
28
+ private View.OnSystemUiVisibilityChangeListener systemUiVisibilityChangeListener;
23
29
 
24
30
  public AndroidLauncher(AppCompatActivity activity, Bridge bridge) {
25
31
  this.activity = activity;
26
32
  this.bridge = bridge;
27
- setupSystemUiVisibilityListener();
28
33
  }
29
34
 
30
35
  // Opens the settings to allow the user to set the app as launcher
@@ -38,39 +43,29 @@ public class AndroidLauncher {
38
43
  }
39
44
  }
40
45
 
41
- // Starts immersive mode and prevents the user from leaving the app (if it's the launcher)
46
+ // Starts immersive mode
42
47
  public void startImmersiveMode(PluginCall call) {
43
- try {
44
- if (Looper.myLooper() == Looper.getMainLooper()) {
48
+ runOnMainThread(() -> {
49
+ try {
45
50
  enableImmersiveMode();
46
51
  call.resolve();
47
- } else {
48
- new Handler(Looper.getMainLooper()).post(new Runnable() {
49
- @Override
50
- public void run() {
51
- enableImmersiveMode();
52
- call.resolve();
53
- }
54
- });
52
+ } catch (Exception e) {
53
+ call.reject("Error starting immersive mode", e);
55
54
  }
56
- } catch (Exception e) {
57
- call.reject("Error starting immersive mode", e);
58
- }
55
+ });
59
56
  }
60
57
 
61
58
  private void enableImmersiveMode() {
62
- try {
63
59
  immersiveModeEnabled = true;
60
+ setupSystemUiVisibilityListener();
64
61
  View decorView = this.bridge.getActivity().getWindow().getDecorView();
65
- if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.R) {
66
- WindowInsetsController insetsController = decorView.getWindowInsetsController();
67
- if (insetsController != null) {
68
- insetsController.hide(android.view.WindowInsets.Type.statusBars()
69
- | android.view.WindowInsets.Type.navigationBars());
70
-
71
- insetsController.setSystemBarsBehavior(
72
- WindowInsetsController.BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE);
73
- }
62
+
63
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
64
+ WindowInsetsController insetsController = decorView.getWindowInsetsController();
65
+ if (insetsController != null) {
66
+ insetsController.hide(WindowInsets.Type.statusBars() | WindowInsets.Type.navigationBars());
67
+ insetsController.setSystemBarsBehavior(WindowInsetsController.BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE);
68
+ }
74
69
  } else {
75
70
  int flags = View.SYSTEM_UI_FLAG_LAYOUT_STABLE
76
71
  | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
@@ -80,48 +75,43 @@ public class AndroidLauncher {
80
75
  | View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY;
81
76
  decorView.setSystemUiVisibility(flags);
82
77
  }
83
- } catch (Exception e) {
84
- e.printStackTrace();
85
- }
86
78
  }
87
79
 
80
+ // Stops immersive mode
88
81
  public void stopImmersiveMode(PluginCall call) {
89
- try {
90
- if (Looper.myLooper() == Looper.getMainLooper()) {
91
- clearImmersiveMode();
92
- call.resolve();
93
- } else {
94
- new Handler(Looper.getMainLooper()).post(new Runnable() {
95
- @Override
96
- public void run() {
97
- clearImmersiveMode();
98
- call.resolve();
99
- }
100
- });
101
- }
102
- } catch (Exception e) {
103
- call.reject("Error stopping immersive mode", e);
104
- }
82
+ runOnMainThread(() -> {
83
+ try {
84
+ clearImmersiveMode();
85
+ call.resolve();
86
+ } catch (Exception e) {
87
+ call.reject("Error stopping immersive mode", e);
88
+ }
89
+ });
105
90
  }
106
91
 
107
92
  private void clearImmersiveMode() {
108
- try {
109
- immersiveModeEnabled = false;
110
- View decorView = this.bridge.getActivity().getWindow().getDecorView();
111
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
112
- WindowInsetsController insetsController = decorView.getWindowInsetsController();
113
- if (insetsController != null) {
114
- insetsController.hide(WindowInsetsController.BEHAVIOR_SHOW_BARS_BY_SWIPE);
115
- insetsController.setSystemBarsBehavior(WindowInsetsController.BEHAVIOR_SHOW_BARS_BY_TOUCH);
116
- }
117
- } else {
118
- int uiOptions = View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY |
119
- View.SYSTEM_UI_FLAG_HIDE_NAVIGATION |
120
- View.SYSTEM_UI_FLAG_FULLSCREEN;
121
- decorView.setSystemUiVisibility(uiOptions);
122
- }
123
- } catch (Exception e) {
124
- e.printStackTrace();
93
+ immersiveModeEnabled = false;
94
+ removeSystemUiVisibilityListener();
95
+ View decorView = this.bridge.getActivity().getWindow().getDecorView();
96
+
97
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
98
+ WindowInsetsController insetsController = decorView.getWindowInsetsController();
99
+ if (insetsController != null) {
100
+ insetsController.show(WindowInsets.Type.statusBars() | WindowInsets.Type.navigationBars());
101
+ insetsController.setSystemBarsBehavior(WindowInsetsController.BEHAVIOR_SHOW_BARS_BY_TOUCH);
102
+ }
103
+ } else {
104
+ int flags = View.SYSTEM_UI_FLAG_LAYOUT_STABLE;
105
+ decorView.setSystemUiVisibility(flags);
106
+ }
107
+ }
108
+
109
+ // Helper to run things on the UI thread
110
+ private void runOnMainThread(Runnable runnable) {
111
+ if (Looper.myLooper() == Looper.getMainLooper()) {
112
+ runnable.run();
113
+ } else {
114
+ new Handler(Looper.getMainLooper()).post(runnable);
125
115
  }
126
116
  }
127
117
 
@@ -136,29 +126,41 @@ public class AndroidLauncher {
136
126
 
137
127
  // Listen to system UI changes and hide UI if swiped
138
128
  private void setupSystemUiVisibilityListener() {
139
- if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.R) {
140
- // For Android 11+, use a window focus change listener instead
141
- activity.getWindow().getDecorView().setOnApplyWindowInsetsListener(
142
- (view, windowInsets) -> {
143
- if (immersiveModeEnabled &&
144
- windowInsets.isVisible(android.view.WindowInsets.Type.statusBars() | android.view.WindowInsets.Type.navigationBars())) {
145
- if (immersiveModeEnabled) {
146
- enableImmersiveMode();
147
- }
129
+ Activity activity = this.bridge.getActivity();
130
+ View decorView = activity.getWindow().getDecorView();
131
+
132
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
133
+ focusChangeListener = hasFocus -> {
134
+ if (immersiveModeEnabled && hasFocus) {
135
+ enableImmersiveMode();
148
136
  }
149
- return view.onApplyWindowInsets(windowInsets);
150
- });
151
- } else {
152
- // For older Android versions
153
- activity.getWindow().getDecorView().setOnSystemUiVisibilityChangeListener(
154
- visibility -> {
137
+ };
138
+ decorView.getViewTreeObserver().addOnWindowFocusChangeListener(focusChangeListener);
139
+ } else {
140
+ systemUiVisibilityChangeListener = visibility -> {
155
141
  if (immersiveModeEnabled && (visibility & View.SYSTEM_UI_FLAG_FULLSCREEN) == 0) {
156
- if (immersiveModeEnabled) {
157
- enableImmersiveMode();
158
- }
142
+ enableImmersiveMode();
159
143
  }
144
+ };
145
+ decorView.setOnSystemUiVisibilityChangeListener(systemUiVisibilityChangeListener);
146
+ }
147
+ }
148
+
149
+ // Remove the listener
150
+ private void removeSystemUiVisibilityListener() {
151
+ Activity activity = this.bridge.getActivity();
152
+ View decorView = activity.getWindow().getDecorView();
153
+
154
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
155
+ if (focusChangeListener != null) {
156
+ decorView.getViewTreeObserver().removeOnWindowFocusChangeListener(focusChangeListener);
157
+ focusChangeListener = null;
160
158
  }
161
- );
162
- }
159
+ } else {
160
+ if (systemUiVisibilityChangeListener != null) {
161
+ decorView.setOnSystemUiVisibilityChangeListener(null);
162
+ systemUiVisibilityChangeListener = null;
163
+ }
164
+ }
163
165
  }
164
166
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@maximilien0405/capacitor-android-launcher",
3
- "version": "7.0.1",
3
+ "version": "7.0.2",
4
4
  "description": "Capacitor plugin to request your app to be the main launcher.",
5
5
  "main": "dist/plugin.cjs.js",
6
6
  "module": "dist/esm/index.js",