Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
9118191
feat(island): add OTP notification handler filtering
isaacsa51 Oct 14, 2026
a14907b
feat(island): missing shizuku permissions
isaacsa51 Oct 15, 2026
480f896
Merge remote-tracking branch 'upstream/develop' into feat/island/otp_…
isaacsa51 Oct 15, 2026
00900cb
Merge branch 'develop' into feat/island/otp_plugin_handler
isaacsa51 Oct 18, 2026
4761488
Merge branch 'develop' into feat/island/otp_plugin_handler
sameerasw Oct 18, 2026
956da5c
Merge branch 'develop' into feat/island/otp_plugin_handler
isaacsa51 Oct 18, 2026
e224da6
feat: add customizable low and critical battery level thresholds for …
sameerasw Oct 18, 2026
17e5096
feat: add camera slot handling and safe width constraints to island U…
sameerasw Oct 18, 2026
3d183b4
fix: battery alerts not respecting settings
sameerasw Oct 18, 2026
a815beb
feat(island): migrate island OTP settings to ViewModel and refine OTP…
isaacsa51 Oct 18, 2026
e963804
Merge branch 'develop' into feat/island/otp_plugin_handler
isaacsa51 Oct 18, 2026
b872259
feat: add balance compact island setting
sameerasw Oct 18, 2026
1698fa2
refactor: move island balance compact setting
sameerasw Oct 18, 2026
4e01c5e
feat: merge side camera peek in island text
sameerasw Oct 18, 2026
5358da6
Merge branch 'develop' into feat/island/otp_plugin_handler
sameerasw Oct 18, 2026
b1c50b3
OTP: skip non-message categories, register remaining settings, refres…
sameerasw Oct 18, 2026
4684ae3
OTP: off by default, register sensitive-notifications permission and …
sameerasw Oct 18, 2026
f026363
feat(island): add OTP notification handler filtering (#1386)
sameerasw Oct 18, 2026
90df947
fix: pause haptics and background loops when Conscious Gate is inacti…
sameerasw Oct 18, 2026
5a47add
feat: prevent notification reposting after reply and reset input state
sameerasw Oct 18, 2026
600dd50
fix: exclude progress-based notifications from chronometer tracking
sameerasw Oct 18, 2026
77cee9d
feat: keep island notificaiotns optionally
sameerasw Oct 18, 2026
29615f0
refactor: restructure call plugin button generation and 3rd party app…
sameerasw Oct 18, 2026
ccd5a12
fix: add connection settle delays and debouncing to Bluetooth and Net…
sameerasw Oct 18, 2026
1ab1d87
feat: add secondary bubble setting to island features
sameerasw Oct 18, 2026
8c34632
feat: bump version to 20.0 and update watchface promotion to public r…
sameerasw Oct 18, 2026
622f0b4
feat: add local network permission support for watch wireless debugging
sameerasw Oct 18, 2026
74daf55
feat: reorder what's new sections and add previous updates divider
sameerasw Oct 18, 2026
ab2de0a
feat: add Watch Wireless Debugging feature settings and simplify loca…
sameerasw Oct 18, 2026
a02341e
refactor: add bottom content padding to SoundModeTileSettingsUI lazy …
sameerasw Oct 18, 2026
43541a4
fix: persist search query and tab state across config changes
sameerasw Oct 18, 2026
0f6985c
Merge branch 'main' into develop
sameerasw Oct 18, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions app/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -115,10 +115,10 @@ android {
applicationId = "com.sameerasw.essentials"
minSdk = 26
targetSdk = 37
versionCode = 68
versionName = "19.1-beta.3"
versionCode = 69
versionName = "20.0"

val whatsNewCounter = 6
val whatsNewCounter = 7
buildConfigField("int", "WHATS_NEW_COUNTER", whatsNewCounter.toString())
buildConfigField("int", "REQUIRED_WEAR_VERSION_CODE", "7")

Expand Down
1 change: 1 addition & 0 deletions app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
xmlns:tools="http://schemas.android.com/tools">

<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_LOCAL_NETWORK" />
<uses-permission android:name="android.permission.READ_CALENDAR" />
<uses-permission android:name="android.permission.WRITE_CALENDAR" />
<uses-permission android:name="android.permission.DETECT_SCREEN_RECORDING" />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ import com.sameerasw.essentials.domain.model.AppSelection
import com.sameerasw.essentials.domain.model.BackGestureAppConfig
import com.sameerasw.essentials.domain.model.AppTag
import com.sameerasw.essentials.domain.model.DnsPreset
import com.sameerasw.essentials.utils.DuoBatteryLevels
import com.sameerasw.essentials.domain.model.HilightEffect
import com.sameerasw.essentials.domain.model.HilightProgressColorMode
import com.sameerasw.essentials.domain.model.NotificationLightingColorMode
Expand Down Expand Up @@ -493,6 +494,8 @@ class SettingsRepository(
const val KEY_ISLAND_BATTERY_IDLE_COLOR = "island_battery_idle_color"
const val KEY_DUO_BATTERY_CRITICAL_COLOR_ENABLED = "duo_battery_critical_color_enabled"
const val KEY_DUO_BATTERY_CRITICAL_COLOR = "duo_battery_critical_color"
const val KEY_DUO_BATTERY_LOW_LEVEL = "duo_battery_low_level"
const val KEY_DUO_BATTERY_CRITICAL_LEVEL = "duo_battery_critical_level"
const val KEY_DUO_SHOW_NETWORKS = "duo_show_networks"
const val KEY_DUO_DIFFERENTIATE_WIFI = "duo_differentiate_wifi"
const val KEY_DUO_SHOW_TIME = "duo_show_time"
Expand Down Expand Up @@ -587,6 +590,8 @@ class SettingsRepository(
const val KEY_ISLAND_DISABLE_INTERACTIONS_IN_FULLSCREEN = "island_disable_interactions_in_fullscreen"
const val KEY_ISLAND_LANDSCAPE_TOP_SPACING = "island_landscape_top_spacing"
const val KEY_ISLAND_BOND_EDGE = "island_bond_edge"
const val KEY_ISLAND_BALANCE_COMPACT = "island_balance_compact"
const val KEY_ISLAND_SECONDARY_BUBBLE = "island_secondary_bubble"
const val KEY_ISLAND_MAX_ITEMS = "island_max_items"
const val KEY_ISLAND_ALWAYS_GESTURES = "island_always_gestures"
const val KEY_ISLAND_DISMISS_ON_OUTSIDE = "island_dismiss_on_outside"
Expand Down Expand Up @@ -676,6 +681,11 @@ class SettingsRepository(
const val KEY_ISLAND_NOTIF_FILTER_APPS_LIST = "island_notif_filter_apps_list"
const val KEY_ISLAND_SHOW_NOTIFICATIONS = "island_show_notifications"
const val KEY_ISLAND_NOTIF_TAP_TO_OPEN = "island_notif_tap_to_open"
const val KEY_ISLAND_OTP_ENABLED = "island_otp_enabled"
const val KEY_ISLAND_OTP_TAP_TO_COPY = "island_otp_tap_to_copy"
const val KEY_ISLAND_OTP_DISMISS_AFTER_COPY = "island_otp_dismiss_after_copy"
const val KEY_ISLAND_OTP_BYPASS_FILTER = "island_otp_bypass_filter"
const val KEY_ISLAND_OTP_TIMEOUT_MS = "island_otp_timeout_ms"
const val KEY_ISLAND_BORDER_OUTLINE_ENABLED = "island_border_outline_enabled"
const val KEY_ISLAND_BORDER_OUTLINE_COLOR = "island_border_outline_color"
const val KEY_ISLAND_BORDER_OUTLINE_DYNAMIC = "island_border_outline_dynamic"
Expand Down Expand Up @@ -3903,6 +3913,14 @@ class SettingsRepository(
fun getDuoBatteryCriticalColor(): String = getString(KEY_DUO_BATTERY_CRITICAL_COLOR, "#F44336") ?: "#F44336"
fun setDuoBatteryCriticalColor(colorHex: String) = putString(KEY_DUO_BATTERY_CRITICAL_COLOR, colorHex)

fun getDuoBatteryLowLevel(): Int = getInt(KEY_DUO_BATTERY_LOW_LEVEL, DuoBatteryLevels.DEFAULT_LOW)
fun getDuoBatteryCriticalLevel(): Int = getInt(KEY_DUO_BATTERY_CRITICAL_LEVEL, DuoBatteryLevels.DEFAULT_CRITICAL)

fun setDuoBatteryLevels(low: Int, critical: Int) {
putInt(KEY_DUO_BATTERY_LOW_LEVEL, low)
putInt(KEY_DUO_BATTERY_CRITICAL_LEVEL, critical)
}

fun isDuoShowNetworksEnabled(): Boolean = getBoolean(KEY_DUO_SHOW_NETWORKS, true)
fun setDuoShowNetworksEnabled(enabled: Boolean) = putBoolean(KEY_DUO_SHOW_NETWORKS, enabled)

Expand Down Expand Up @@ -4279,6 +4297,14 @@ class SettingsRepository(

fun setIslandBondEdgeEnabled(enabled: Boolean) = putBoolean(KEY_ISLAND_BOND_EDGE, enabled)

fun isIslandBalanceCompactEnabled(): Boolean = getBoolean(KEY_ISLAND_BALANCE_COMPACT, true)

fun setIslandBalanceCompactEnabled(enabled: Boolean) = putBoolean(KEY_ISLAND_BALANCE_COMPACT, enabled)

fun isIslandSecondaryBubbleEnabled(): Boolean = getBoolean(KEY_ISLAND_SECONDARY_BUBBLE, true)

fun setIslandSecondaryBubbleEnabled(enabled: Boolean) = putBoolean(KEY_ISLAND_SECONDARY_BUBBLE, enabled)

fun getIslandLandscapeTopSpacing(): Float = getFloat(KEY_ISLAND_LANDSCAPE_TOP_SPACING, 0f)

fun setIslandLandscapeTopSpacing(value: Float) = putFloat(KEY_ISLAND_LANDSCAPE_TOP_SPACING, value)
Expand Down Expand Up @@ -4380,6 +4406,14 @@ class SettingsRepository(
fun isIslandNotifTapToOpenEnabled(): Boolean = getBoolean(KEY_ISLAND_NOTIF_TAP_TO_OPEN, false)
fun setIslandNotifTapToOpenEnabled(enabled: Boolean) = putBoolean(KEY_ISLAND_NOTIF_TAP_TO_OPEN, enabled)

fun isIslandOtpEnabled(): Boolean = getBoolean(KEY_ISLAND_OTP_ENABLED, false)
fun setIslandOtpEnabled(enabled: Boolean) = putBoolean(KEY_ISLAND_OTP_ENABLED, enabled)
fun isIslandOtpTapToCopyEnabled(): Boolean = getBoolean(KEY_ISLAND_OTP_TAP_TO_COPY, true)
fun isIslandOtpDismissAfterCopyEnabled(): Boolean = getBoolean(KEY_ISLAND_OTP_DISMISS_AFTER_COPY, false)
fun isIslandOtpBypassFilterEnabled(): Boolean = getBoolean(KEY_ISLAND_OTP_BYPASS_FILTER, true)
fun getIslandOtpTimeoutMs(): Long = getLong(KEY_ISLAND_OTP_TIMEOUT_MS, 60_000L)
fun setIslandOtpTimeoutMs(ms: Long) = putLong(KEY_ISLAND_OTP_TIMEOUT_MS, ms)

fun isIslandNotifCompactHeadsUpEnabled(): Boolean = getBoolean(KEY_ISLAND_NOTIF_COMPACT_HEADS_UP, true)
fun setIslandNotifCompactHeadsUpEnabled(enabled: Boolean) = putBoolean(KEY_ISLAND_NOTIF_COMPACT_HEADS_UP, enabled)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,5 +28,6 @@ data class ActiveNotificationAlert(
val chatIcon: Bitmap? = null,
val isSilent: Boolean = false,
val blockedByDnd: Boolean = false,
val otpCode: String? = null,
)

Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import android.content.Context
import androidx.annotation.DrawableRes
import androidx.annotation.StringRes
import com.sameerasw.essentials.R
import com.sameerasw.essentials.utils.PermissionGrantUtil
import com.sameerasw.essentials.utils.PermissionUtils
import com.sameerasw.essentials.utils.ShellUtils

Expand Down Expand Up @@ -131,6 +132,17 @@ enum class AppPermission(
iconRes = R.drawable.rounded_image_24,
aliases = listOf("READ_MEDIA_IMAGES", "READ_MEDIA_VIDEO", "READ_MEDIA_AUDIO", "READ_EXTERNAL_STORAGE", "MANAGE_EXTERNAL_STORAGE"),
),
SENSITIVE_NOTIFICATIONS(
key = "SENSITIVE_NOTIFICATIONS",
titleRes = R.string.perm_sensitive_notifications_title,
iconRes = R.drawable.rounded_vpn_key_24,
),
LOCAL_NETWORK(
key = "LOCAL_NETWORK",
titleRes = R.string.perm_local_network_title,
iconRes = R.drawable.rounded_lan_24,
aliases = listOf("ACCESS_LOCAL_NETWORK"),
),
NOTIFICATION_BUBBLES(
key = "NOTIFICATION_BUBBLES",
titleRes = R.string.perm_bubbles_title,
Expand All @@ -145,6 +157,8 @@ enum class AppPermission(
DRAW_OVERLAYS -> PermissionUtils.canDrawOverlays(context)
WRITE_SETTINGS -> PermissionUtils.canWriteSystemSettings(context)
NOTIFICATION_POLICY -> PermissionUtils.hasNotificationPolicyAccess(context)
SENSITIVE_NOTIFICATIONS -> PermissionGrantUtil.hasSensitiveNotifications(context)
LOCAL_NETWORK -> PermissionUtils.hasLocalNetworkPermission(context)
ROOT, SHIZUKU -> ShellUtils.isAvailable(context) && ShellUtils.hasPermission(context)
else -> true
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1008,6 +1008,16 @@ object FeatureRegistry {
R.string.island_line_peek_desc,
"island_line_stage_enabled",
),
SearchSetting(
R.string.island_secondary_bubble_title,
R.string.island_section_behavior,
"island_secondary_bubble",
),
SearchSetting(
R.string.island_balance_compact_title,
R.string.island_section_behavior,
"island_balance_compact",
),
),
) {
override fun isEnabled(viewModel: MainViewModel) = true
Expand Down Expand Up @@ -1084,6 +1094,31 @@ object FeatureRegistry {
R.string.island_catch_up_desc,
"island_catch_up",
),
SearchSetting(
R.string.island_otp_enabled_title,
R.string.island_otp_enabled_desc,
"island_otp_enabled",
),
SearchSetting(
R.string.island_otp_tap_to_copy_title,
R.string.island_otp_tap_to_copy_desc,
"island_otp_tap_to_copy",
),
SearchSetting(
R.string.island_otp_dismiss_after_copy_title,
R.string.island_otp_section,
"island_otp_dismiss_after_copy",
),
SearchSetting(
R.string.island_otp_timeout_title,
R.string.island_otp_section,
"island_otp_timeout_ms",
),
SearchSetting(
R.string.island_otp_bypass_filter_title,
R.string.island_otp_bypass_filter_desc,
"island_otp_bypass_filter",
),
),
) {
override fun isEnabled(viewModel: MainViewModel) = viewModel.isIslandEnabled.value
Expand Down Expand Up @@ -2605,6 +2640,7 @@ object FeatureRegistry {
iconRes = R.drawable.rounded_adb_24,
category = R.string.cat_tools,
description = R.string.feat_watch_wireless_debugging_desc,
permissionKeys = listOf("LOCAL_NETWORK"),
parentFeatureId = "Watch",
hasMoreSettings = true,
showToggle = true,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,8 @@ fun initPermissionRegistry() {

// Post notifications permission
PermissionRegistry.register("POST_NOTIFICATIONS", R.string.feat_caffeinate_title)
PermissionRegistry.register("LOCAL_NETWORK", R.string.feat_watch_wireless_debugging_title)
PermissionRegistry.register("SENSITIVE_NOTIFICATIONS", R.string.island_otp_section)
PermissionRegistry.register("POST_NOTIFICATIONS", R.string.feat_battery_notification_title)

// Read phone state permission
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,7 @@ class InteractionOverrides(
val onTap: (() -> Boolean)? = null,
val onLongPress: (() -> Unit)? = null,
val onExpandedTap: (() -> Boolean)? = null,
val onLineTap: (() -> Boolean)? = null,
) {
companion object {
val Default = InteractionOverrides()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ interface IslandPlugin {
fun stop()
fun refresh() {}
fun onScreenStateChanged() = refresh()
fun onSuppressionChanged(suppressed: Boolean) {}
fun onUserInteraction(focusedKey: String?) {}
fun onFocusChanged(stage: IslandStage, focusedKey: String?) {}
}
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ class BatteryAlertsPlugin : BaseIslandPlugin() {
override val id = "battery_alerts_plugin"

override val settingKeys = setOf(
SettingsRepository.KEY_ISLAND_SHOW_BATTERY,
SettingsRepository.KEY_ISLAND_SHOW_BATTERY_ALERTS,
SettingsRepository.KEY_ISLAND_BATTERY_ALERT_CHARGING,
SettingsRepository.KEY_ISLAND_BATTERY_ALERT_LOW,
Expand Down Expand Up @@ -55,7 +56,10 @@ class BatteryAlertsPlugin : BaseIslandPlugin() {
readBattery(intent)
val newAlertState = currentAlertState()

if ((isCharging && !wasCharging && newAlertState == "charging") || (newAlertState != prevAlertState && newAlertState.isNotEmpty())) {
if (!enabled()) {
if (peeking) stopPeek()
render()
} else if ((isCharging && !wasCharging && newAlertState == "charging") || (newAlertState != prevAlertState && newAlertState.isNotEmpty())) {
peekAlert()
} else if (!peeking) {
render()
Expand All @@ -80,9 +84,18 @@ class BatteryAlertsPlugin : BaseIslandPlugin() {

override fun refresh() {
readBattery(null)
if (!enabled() && peeking) stopPeek()
render()
}

private fun enabled() =
settings.isIslandShowBatteryEnabled() && settings.isIslandShowBatteryAlertsEnabled()

private fun stopPeek() {
ctx?.mainHandler?.removeCallbacks(endPeek)
peeking = false
}

private fun readBattery(intent: Intent?) {
val batteryIntent = intent ?: context.registerReceiver(
null, IntentFilter(Intent.ACTION_BATTERY_CHANGED)
Expand Down Expand Up @@ -140,6 +153,7 @@ class BatteryAlertsPlugin : BaseIslandPlugin() {

private fun peekAlert() {
val c = ctx ?: return
if (!enabled()) return
val duration = settings.getIslandPeekDurationMs()
peeking = true
render()
Expand All @@ -154,8 +168,7 @@ class BatteryAlertsPlugin : BaseIslandPlugin() {
return
}

val masterEnabled = settings.getBoolean(SettingsRepository.KEY_ISLAND_SHOW_BATTERY_ALERTS, true)
if (!masterEnabled || !peeking || level < 0) {
if (!enabled() || !peeking || level < 0) {
publish(emptyList())
return
}
Expand Down
Loading
Loading