feat(android): update notification handling and UI responsiveness with new audio settings permission and improved back navigation

Credit: thanks to Sergey B (Samara Telegram - Reticulum Самара) for testing and providing solutions.
This commit is contained in:
Ivan
2026-04-30 11:52:10 -05:00
parent 24da5de7fd
commit b7e1593306
4 changed files with 77 additions and 39 deletions
+2 -1
View File
@@ -11,6 +11,7 @@
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_DATA_SYNC" />
<uses-permission android:name="android.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS" />
<uses-permission android:name="android.permission.RECORD_AUDIO" />
<uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS" />
<uses-feature android:name="android.hardware.microphone" android:required="false" />
<uses-permission android:name="android.permission.CAMERA" />
<uses-feature android:name="android.hardware.camera" android:required="false" />
@@ -43,7 +44,7 @@
android:name=".MainActivity"
android:exported="true"
android:launchMode="singleTask"
android:configChanges="orientation|screenSize|smallestScreenSize|screenLayout|density|uiMode|keyboard|keyboardHidden|navigation">
android:configChanges="orientation|screenSize|smallestScreenSize|screenLayout|density|fontScale|keyboard|keyboardHidden|navigation|uiMode|colorMode|layoutDirection">
<meta-data
android:name="android.app.shortcuts"
android:resource="@xml/shortcuts" />
@@ -229,6 +229,22 @@ public final class AndroidNotificationBridge {
PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE
);
int id = NOTIFY_BASE_ID;
if (dedupeHex != null && dedupeHex.length() >= 8) {
try {
id = NOTIFY_BASE_ID
+ (int) (Long.parseLong(
dedupeHex.substring(0, Math.min(8, dedupeHex.length())), 16) & 0x7fff_ffff);
} catch (NumberFormatException ignored) {
id = NOTIFY_BASE_ID + (dedupeHex.hashCode() & 0x7fff_ffff);
}
}
try {
nm.cancel(id);
} catch (Exception ignored) {
}
NotificationCompat.Builder b = new NotificationCompat.Builder(ctx, MeshChatApplication.CHANNEL_ID_MESSAGES)
.setSmallIcon(R.drawable.ic_stat_meshchatx)
.setContentTitle(title)
@@ -237,16 +253,9 @@ public final class AndroidNotificationBridge {
.setContentIntent(pi)
.setAutoCancel(true)
.setCategory(NotificationCompat.CATEGORY_MESSAGE)
.setVisibility(NotificationCompat.VISIBILITY_PRIVATE);
int id = NOTIFY_BASE_ID;
if (dedupeHex != null && dedupeHex.length() >= 8) {
try {
id = NOTIFY_BASE_ID + (int) (Long.parseLong(dedupeHex.substring(0, Math.min(8, dedupeHex.length())), 16) & 0x7fff_ffff);
} catch (NumberFormatException ignored) {
id = NOTIFY_BASE_ID + (dedupeHex.hashCode() & 0x7fff_ffff);
}
}
.setVisibility(NotificationCompat.VISIBILITY_PRIVATE)
.setDefaults(NotificationCompat.DEFAULT_ALL)
.setOnlyAlertOnce(false);
try {
nm.notify(id, b.build());
@@ -32,6 +32,7 @@ import android.widget.ImageView;
import android.widget.ProgressBar;
import android.widget.TextView;
import android.widget.Toast;
import androidx.activity.OnBackPressedCallback;
import androidx.activity.result.ActivityResultLauncher;
import androidx.activity.result.contract.ActivityResultContracts;
import androidx.core.app.ActivityCompat;
@@ -196,33 +197,35 @@ public class MainActivity extends AppCompatActivity {
@Override
public void onPageFinished(WebView view, String url) {
super.onPageFinished(view, url);
if (!isStartupRequest(url)) {
if (!startupPageLoaded) {
if (!isStartupRequest(url)) {
return;
}
if (startupRequestHadLoadError) {
return;
}
startupPageLoaded = true;
mainHandler.removeCallbacksAndMessages(null);
hideStartupLoadingOverlay();
dispatchPendingIntentUri();
dispatchCallNotificationAction();
return;
}
if (startupRequestHadLoadError) {
return;
}
startupPageLoaded = true;
mainHandler.removeCallbacksAndMessages(null);
webView.setVisibility(android.view.View.VISIBLE);
loadingLogo.setVisibility(android.view.View.GONE);
progressBar.setVisibility(android.view.View.GONE);
loadingText.setVisibility(android.view.View.GONE);
errorText.setVisibility(android.view.View.GONE);
dispatchPendingIntentUri();
dispatchCallNotificationAction();
hideStartupLoadingOverlay();
}
@Override
public void onPageStarted(WebView view, String url, android.graphics.Bitmap favicon) {
super.onPageStarted(view, url, favicon);
if (isStartupRequest(url)) {
if (!startupPageLoaded && isStartupRequest(url)) {
startupRequestHadLoadError = false;
if (!startupPageLoaded) {
webView.setVisibility(android.view.View.INVISIBLE);
}
webView.setVisibility(android.view.View.INVISIBLE);
progressBar.setVisibility(android.view.View.VISIBLE);
return;
}
if (!startupPageLoaded) {
progressBar.setVisibility(android.view.View.VISIBLE);
}
progressBar.setVisibility(android.view.View.VISIBLE);
}
@Override
@@ -371,6 +374,22 @@ public class MainActivity extends AppCompatActivity {
handleIncomingIntent(getIntent());
consumeCallIntentForPending(getIntent());
getOnBackPressedDispatcher().addCallback(
this,
new OnBackPressedCallback(true) {
@Override
public void handleOnBackPressed() {
if (webView != null && webView.canGoBack()) {
webView.goBack();
} else {
setEnabled(false);
MainActivity.this.getOnBackPressedDispatcher().onBackPressed();
setEnabled(true);
}
}
}
);
startMeshChatServer();
scheduleConnectionRetry("Connecting to local server...");
}
@@ -674,6 +693,24 @@ public class MainActivity extends AppCompatActivity {
return sw.toString();
}
private void hideStartupLoadingOverlay() {
if (webView != null) {
webView.setVisibility(android.view.View.VISIBLE);
}
if (loadingLogo != null) {
loadingLogo.setVisibility(android.view.View.GONE);
}
if (progressBar != null) {
progressBar.setVisibility(android.view.View.GONE);
}
if (loadingText != null) {
loadingText.setVisibility(android.view.View.GONE);
}
if (errorText != null && !backendFailed) {
errorText.setVisibility(android.view.View.GONE);
}
}
private void showStartupError(String message) {
runOnUiThread(() -> {
mainHandler.removeCallbacksAndMessages(null);
@@ -718,15 +755,6 @@ public class MainActivity extends AppCompatActivity {
return phase + " (" + connectionAttempts + "/" + MAX_CONNECTION_ATTEMPTS + ")";
}
@Override
public void onBackPressed() {
if (webView.canGoBack()) {
webView.goBack();
} else {
super.onBackPressed();
}
}
@Override
public void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
@@ -51,7 +51,7 @@ public class MeshChatApplication extends PyApplication {
NotificationChannel messages = new NotificationChannel(
CHANNEL_ID_MESSAGES,
getString(R.string.notification_channel_messages_name),
NotificationManager.IMPORTANCE_DEFAULT
NotificationManager.IMPORTANCE_HIGH
);
messages.setDescription(getString(R.string.notification_channel_messages_desc));
nm.createNotificationChannel(messages);