diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 659017b..888bd00 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -16,8 +16,8 @@ android { applicationId = "dev.robin.flip_2_dnd" minSdk = 23 targetSdk = 34 - versionCode = 6 - versionName = "1.0.6" + versionCode = 7 + versionName = "1.0.7" vectorDrawables { useSupportLibrary = true } diff --git a/app/src/main/java/dev/robin/flip_2_dnd/data/repository/DndRepositoryImpl.kt b/app/src/main/java/dev/robin/flip_2_dnd/data/repository/DndRepositoryImpl.kt index 2ff9f48..c503d97 100644 --- a/app/src/main/java/dev/robin/flip_2_dnd/data/repository/DndRepositoryImpl.kt +++ b/app/src/main/java/dev/robin/flip_2_dnd/data/repository/DndRepositoryImpl.kt @@ -4,24 +4,42 @@ import android.app.NotificationManager import android.content.Context import android.util.Log import dev.robin.flip_2_dnd.domain.repository.DndRepository +import dev.robin.flip_2_dnd.domain.repository.SettingsRepository import dagger.hilt.android.qualifiers.ApplicationContext +import kotlinx.coroutines.* import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.first +import kotlinx.coroutines.runBlocking import javax.inject.Inject import javax.inject.Singleton @Singleton class DndRepositoryImpl @Inject constructor( - @ApplicationContext private val context: Context + @ApplicationContext private val context: Context, + private val settingsRepository: SettingsRepository ) : DndRepository { private val notificationManager = context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager private val _isDndEnabled = MutableStateFlow(false) + private val _dndMode = MutableStateFlow("All Notifications") + + private val dndStateUpdateJob: Job init { updateDndState() + // Start continuous monitoring of DND state + dndStateUpdateJob = startDndStateMonitoring() + } + + private fun startDndStateMonitoring(): Job = CoroutineScope(Dispatchers.Default).launch { + while (isActive) { + updateDndState() + delay(1000) // Check every second + } } override fun isDndEnabled(): Flow = _isDndEnabled + override fun getDndMode(): Flow = _dndMode override suspend fun setDndEnabled(enabled: Boolean) { if (!notificationManager.isNotificationPolicyAccessGranted) { @@ -31,12 +49,19 @@ class DndRepositoryImpl @Inject constructor( try { val newFilter = if (enabled) { - NotificationManager.INTERRUPTION_FILTER_NONE + val isPriorityDndEnabled = runBlocking { + settingsRepository.getPriorityDndEnabled().first() + } + if (isPriorityDndEnabled) { + NotificationManager.INTERRUPTION_FILTER_PRIORITY + } else { + NotificationManager.INTERRUPTION_FILTER_NONE + } } else { NotificationManager.INTERRUPTION_FILTER_ALL } notificationManager.setInterruptionFilter(newFilter) - updateDndState() + updateDndState() // Ensure state is updated immediately after changing Log.d("DndRepository", "DND state changed to: $enabled") } catch (e: Exception) { Log.e("DndRepository", "Error setting DND state", e) @@ -49,9 +74,38 @@ class DndRepositoryImpl @Inject constructor( } private fun updateDndState() { + // Use Android's native method to check if DND is active val currentFilter = notificationManager.currentInterruptionFilter - val isDndEnabled = currentFilter == NotificationManager.INTERRUPTION_FILTER_NONE - _isDndEnabled.value = isDndEnabled - Log.d("DndRepository", "Current DND state: $isDndEnabled (Filter: $currentFilter)") + + val isDndActive = currentFilter != NotificationManager.INTERRUPTION_FILTER_ALL + + val dndMode = when (currentFilter) { + NotificationManager.INTERRUPTION_FILTER_NONE -> "Total Silence" + NotificationManager.INTERRUPTION_FILTER_PRIORITY -> "Priority Mode" + NotificationManager.INTERRUPTION_FILTER_ALARMS -> "Alarms Only" + NotificationManager.INTERRUPTION_FILTER_ALL -> "All Notifications" + else -> "Unknown Mode" + } + + // Only update if the state has changed to avoid unnecessary updates + if (_isDndEnabled.value != isDndActive) { + _isDndEnabled.value = isDndActive + } + + if (_dndMode.value != dndMode) { + _dndMode.value = dndMode + } + + Log.d("DndRepository", """ + Current DND Details: + - Active: $isDndActive + - Mode: $dndMode + - Raw Filter: $currentFilter + """.trimIndent()) + } + + // Ensure the monitoring job is cancelled when the repository is no longer used + override fun onCleared() { + dndStateUpdateJob.cancel() } } diff --git a/app/src/main/java/dev/robin/flip_2_dnd/domain/repository/DndRepository.kt b/app/src/main/java/dev/robin/flip_2_dnd/domain/repository/DndRepository.kt index e380432..05f7770 100644 --- a/app/src/main/java/dev/robin/flip_2_dnd/domain/repository/DndRepository.kt +++ b/app/src/main/java/dev/robin/flip_2_dnd/domain/repository/DndRepository.kt @@ -4,6 +4,8 @@ import kotlinx.coroutines.flow.Flow interface DndRepository { fun isDndEnabled(): Flow + fun getDndMode(): Flow suspend fun setDndEnabled(enabled: Boolean) suspend fun toggleDnd() + fun onCleared() } diff --git a/app/src/main/java/dev/robin/flip_2_dnd/presentation/main/MainScreen.kt b/app/src/main/java/dev/robin/flip_2_dnd/presentation/main/MainScreen.kt index ab1c9f7..6eacf73 100644 --- a/app/src/main/java/dev/robin/flip_2_dnd/presentation/main/MainScreen.kt +++ b/app/src/main/java/dev/robin/flip_2_dnd/presentation/main/MainScreen.kt @@ -41,8 +41,10 @@ fun MainScreen( verticalArrangement = Arrangement.Center ) { Image( - painter = painterResource(id = R.drawable.app_icon), - contentDescription = "Do Not Enter Icon", + painter = painterResource( + id = if (state.isDndEnabled) R.drawable.ic_dnd_on else R.drawable.ic_dnd_off + ), + contentDescription = "Do Not Disturb Icon", modifier = Modifier .fillMaxWidth(0.7f) .aspectRatio(1f), @@ -52,7 +54,7 @@ fun MainScreen( Spacer(modifier = Modifier.height(32.dp)) Text( - text = if (state.isDndEnabled) "DND is ON" else "DND is OFF", + text = if (state.isDndEnabled) "DND: ${state.dndMode}" else "DND is OFF", style = MaterialTheme.typography.headlineMedium, color = MaterialTheme.colorScheme.primary ) diff --git a/app/src/main/java/dev/robin/flip_2_dnd/presentation/main/MainState.kt b/app/src/main/java/dev/robin/flip_2_dnd/presentation/main/MainState.kt index fc1c122..fd24a01 100644 --- a/app/src/main/java/dev/robin/flip_2_dnd/presentation/main/MainState.kt +++ b/app/src/main/java/dev/robin/flip_2_dnd/presentation/main/MainState.kt @@ -5,6 +5,7 @@ import dev.robin.flip_2_dnd.domain.model.PhoneOrientation data class MainState( val orientation: PhoneOrientation = PhoneOrientation.UNKNOWN, val isDndEnabled: Boolean = false, + val dndMode: String = "All Notifications", val isScreenOffOnly: Boolean = false, val isVibrationEnabled: Boolean = true, val isSoundEnabled: Boolean = false diff --git a/app/src/main/java/dev/robin/flip_2_dnd/presentation/main/MainViewModel.kt b/app/src/main/java/dev/robin/flip_2_dnd/presentation/main/MainViewModel.kt index 23c942e..e31b071 100644 --- a/app/src/main/java/dev/robin/flip_2_dnd/presentation/main/MainViewModel.kt +++ b/app/src/main/java/dev/robin/flip_2_dnd/presentation/main/MainViewModel.kt @@ -73,10 +73,19 @@ class MainViewModel @Inject constructor( private fun observeDndState() { viewModelScope.launch { - dndRepository.isDndEnabled().collect { enabled -> - Log.d("MainViewModel", "DND state changed: $enabled") - _state.update { it.copy(isDndEnabled = enabled) } - } + // Combine DND enabled state and mode + combine( + dndRepository.isDndEnabled(), + dndRepository.getDndMode() + ) { enabled, mode -> + Log.d("MainViewModel", "DND state changed: $enabled, Mode: $mode") + _state.update { + it.copy( + isDndEnabled = enabled, + dndMode = mode + ) + } + }.collect() } } diff --git a/app/src/main/java/dev/robin/flip_2_dnd/services/DndService.kt b/app/src/main/java/dev/robin/flip_2_dnd/services/DndService.kt index f3202f0..6e9a956 100644 --- a/app/src/main/java/dev/robin/flip_2_dnd/services/DndService.kt +++ b/app/src/main/java/dev/robin/flip_2_dnd/services/DndService.kt @@ -10,6 +10,7 @@ import android.util.Log import dev.robin.flip_2_dnd.R import dev.robin.flip_2_dnd.domain.repository.SettingsRepository import dev.robin.flip_2_dnd.data.repository.SettingsRepositoryImpl +import kotlinx.coroutines.delay import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.first @@ -42,16 +43,27 @@ class DndService(private val context: Context) { private fun vibrate(pattern: LongArray) { runBlocking { val isVibrationEnabled = settingsRepository.getVibrationEnabled().first() - Log.d(TAG, "Vibration check: enabled=$isVibrationEnabled") - if (isVibrationEnabled) { + Log.d(TAG, "Vibration check: enabled=$isVibrationEnabled, pattern=${pattern.contentToString()}") + + // Log the entire vibration process for debugging + if (!isVibrationEnabled) { + Log.w(TAG, "Vibration is disabled. Skipping vibration.") + return@runBlocking + } + + try { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { val timings = pattern val amplitudes = IntArray(pattern.size) { VibrationEffect.DEFAULT_AMPLITUDE } + Log.d(TAG, "Creating waveform vibration for Android O+") vibrator.vibrate(VibrationEffect.createWaveform(timings, amplitudes, -1)) } else { + Log.d(TAG, "Using deprecated vibration method for older Android versions") @Suppress("DEPRECATION") vibrator.vibrate(pattern, -1) } + } catch (e: Exception) { + Log.e(TAG, "Error during vibration: ${e.message}", e) } } } @@ -102,6 +114,7 @@ class DndService(private val context: Context) { // Any other value means DND is ON with different levels val isDndCurrentlyOn = currentFilter != NotificationManager.INTERRUPTION_FILTER_ALL + // Determine the new filter first val newFilter = if (!isDndCurrentlyOn) { // If DND is currently OFF, turn it ON with appropriate mode runBlocking { @@ -118,6 +131,26 @@ class DndService(private val context: Context) { NotificationManager.INTERRUPTION_FILTER_ALL } + // Play feedback BEFORE setting the filter + val willBeDndEnabled = newFilter != NotificationManager.INTERRUPTION_FILTER_ALL + playSound(willBeDndEnabled) + if (willBeDndEnabled) { + // Two vibrations for DND ON + vibrate(longArrayOf(0, 200, 200, 200)) + } else { + // One vibration for DND OFF + vibrate(longArrayOf(0, 200)) + } + + // Add 2-second delay only when turning on Total Silence DND + if (willBeDndEnabled && newFilter == NotificationManager.INTERRUPTION_FILTER_NONE) { + runBlocking { + Log.d(TAG, "Waiting 2 seconds before setting Total Silence DND") + delay(2000) + } + } + + // Now set the interruption filter Log.d(TAG, "Setting DND filter from $currentFilter to $newFilter") notificationManager.setInterruptionFilter(newFilter) @@ -133,16 +166,6 @@ class DndService(private val context: Context) { } Log.d(TAG, "DND ${if (_isDndEnabled.value) "enabled" else "disabled"} by ${if (_isAppEnabledDnd.value) "app" else "user"}") - // Play feedback - playSound(_isDndEnabled.value) - if (_isDndEnabled.value) { - // Two vibrations for DND ON - vibrate(longArrayOf(0, 200, 200, 200)) - } else { - // One vibration for DND OFF - vibrate(longArrayOf(0, 200)) - } - } catch (e: SecurityException) { Log.e(TAG, "SecurityException toggling DND: ${e.message}", e) openDndSettings() diff --git a/app/src/main/res/drawable/ic_dnd_off.xml b/app/src/main/res/drawable/ic_dnd_off.xml new file mode 100644 index 0000000..2f857bc --- /dev/null +++ b/app/src/main/res/drawable/ic_dnd_off.xml @@ -0,0 +1,15 @@ + + + + + diff --git a/app/src/main/res/drawable/ic_dnd_on.xml b/app/src/main/res/drawable/ic_dnd_on.xml new file mode 100644 index 0000000..5aa3dd6 --- /dev/null +++ b/app/src/main/res/drawable/ic_dnd_on.xml @@ -0,0 +1,10 @@ + + + + diff --git a/metadata/dev.robin.flip_2_dnd.yml b/metadata/dev.robin.flip_2_dnd.yml index ff4b846..f42df5a 100644 --- a/metadata/dev.robin.flip_2_dnd.yml +++ b/metadata/dev.robin.flip_2_dnd.yml @@ -57,8 +57,20 @@ Builds: subdir: app gradle: - yes + - versionName: '1.0.6' + versionCode: 6 + commit: v1.0.6 + subdir: app + gradle: + - yes + - versionName: '1.0.7' + versionCode: 7 + commit: v1.0.7 + subdir: app + gradle: + - yes AutoUpdateMode: Version UpdateCheckMode: Tags -CurrentVersion: '1.0.6' -CurrentVersionCode: 6 +CurrentVersion: '1.0.7' +CurrentVersionCode: 7 diff --git a/metadata/en-US/changelogs/7.txt b/metadata/en-US/changelogs/7.txt new file mode 100644 index 0000000..8f9e545 --- /dev/null +++ b/metadata/en-US/changelogs/7.txt @@ -0,0 +1,3 @@ +- Improved DND mode detection and toggle behavior +- Enhanced Total Silence mode activation +- Minor performance and stability improvements