Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
28 changes: 28 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,34 @@

All notable changes to RootADB Pro will be documented in this file.

## [Unreleased]

### Changed
- Target and compile SDK raised from 34 to 35 (Android 15); Android 15's
enforced edge-to-edge is handled by the explicit inset listeners added in
this release

### Added
- Accent color selection now actually themes the app: each accent applies a
Material 3 tonal overlay (light + dark palettes) at activity creation
- "Dynamic" accent option using Material You wallpaper colors (Android 12+)
- Adaptive launcher icon with monochrome layer for Android 13+ themed icons
- Predictive back gesture opt-in (Android 13+)

### Fixed
- Android 12+ light mode no longer renders a near-black background: removed
the broken `values-v31` dynamic-color theme that hardcoded
`system_neutral1_900` into a DayNight theme
- Edge-to-edge insets are now handled explicitly (status bar padding on the
app bar, navigation-bar padding on scrolling content) instead of relying on
`fitsSystemWindows`, surviving Android 15's enforced edge-to-edge and
display cutouts
- Fragment content no longer rests hidden behind the gesture navigation bar
- Android 8.0 (API 26): navigation bar now uses a translucent scrim since the
platform cannot render dark nav-bar icons, keeping buttons visible on light
content
- Orange accent uses a dark on-primary color for WCAG-compliant contrast

## [1.3.0] - 2026-07-15

### Security
Expand Down
4 changes: 3 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,8 @@ A powerful Android utility that enables wireless ADB debugging with multiple rem
### Theme Customization
- Light / Dark / System modes
- 6 accent colors: Blue, Teal, Purple, Orange, Pink, Green
- Dynamic (Material You) wallpaper colors on Android 12+
- Adaptive + themed launcher icon (Android 13+ monochrome)
- Material 3 design language

## Requirements
Expand Down Expand Up @@ -214,7 +216,7 @@ app/src/main/java/com/phenix/wirelessadb/

### v1.2.0 (Current)
- **Dashboard Tab** - Unified control center
- **Theme System** - Light/Dark modes + 6 accent colors
- **Theme System** - Light/Dark modes + 6 accent colors + Material You dynamic color (Android 12+)
- **P2P Token** - Device-to-device connections
- **ADB Auto-Pairing** - Android 11+ wireless pairing
- **Shizuku Enhancement** - Proper UserService integration
Expand Down
4 changes: 2 additions & 2 deletions app/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -22,12 +22,12 @@ val ciKeyPassword: String? by project

android {
namespace = "com.phenix.wirelessadb"
compileSdk = 34
compileSdk = 35

defaultConfig {
applicationId = "com.phenix.wirelessadb"
minSdk = 26
targetSdk = 34
targetSdk = 35
versionCode = 6
versionName = "1.3.0"
}
Expand Down
7 changes: 4 additions & 3 deletions app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -33,12 +33,13 @@

<application
android:allowBackup="true"
android:icon="@drawable/ic_launcher"
android:enableOnBackInvokedCallback="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@drawable/ic_launcher"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/Theme.WirelessADB"
tools:targetApi="34">
tools:targetApi="35">

<activity
android:name=".MainActivity"
Expand Down
17 changes: 17 additions & 0 deletions app/src/main/java/com/phenix/wirelessadb/MainActivity.kt
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import android.content.Context
import android.content.Intent
import android.content.IntentFilter
import android.content.pm.PackageManager
import android.graphics.Color
import android.net.ConnectivityManager
import android.os.Build
import android.os.Bundle
Expand All @@ -21,6 +22,7 @@ import com.phenix.wirelessadb.databinding.ActivityMainBinding
import com.phenix.wirelessadb.theme.ThemeManager
import com.phenix.wirelessadb.ui.MainPagerAdapter
import com.phenix.wirelessadb.util.AccessibilityHelper
import com.phenix.wirelessadb.util.applyTopSystemBarInsets
import com.phenix.wirelessadb.util.isReducedMotionEnabled
import com.phenix.wirelessadb.viewmodel.AdbViewModel

Expand Down Expand Up @@ -50,11 +52,26 @@ class MainActivity : AppCompatActivity() {
ThemeManager.applyTheme(this)

super.onCreate(savedInstanceState)

// Apply accent color overlay (or Material You dynamic color) before inflating views
ThemeManager.applyAccent(this)

WindowCompat.setDecorFitsSystemWindows(window, false)

// API 26 can't render dark nav-bar icons (windowLightNavigationBar is API 27+),
// so a fully transparent nav bar would leave white buttons invisible on light
// content. Use a translucent scrim there instead.
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O_MR1) {
@Suppress("DEPRECATION")
window.navigationBarColor = Color.argb(0x66, 0, 0, 0)
}

binding = ActivityMainBinding.inflate(layoutInflater)
setContentView(binding.root)

// Edge-to-edge: app bar absorbs the status bar / cutout insets
binding.appBarLayout.applyTopSystemBarInsets()

setSupportActionBar(binding.toolbar)

requestNotificationPermission()
Expand Down
38 changes: 30 additions & 8 deletions app/src/main/java/com/phenix/wirelessadb/theme/AccentColor.kt
Original file line number Diff line number Diff line change
@@ -1,59 +1,81 @@
package com.phenix.wirelessadb.theme

import androidx.annotation.ColorRes
import androidx.annotation.StyleRes
import com.phenix.wirelessadb.R

/**
* Accent color presets for the app theme (v1.2.0).
*
* Each accent defines primary and secondary colors for consistent theming.
* Each accent defines a tonal palette (night-qualified color resources) and a
* theme overlay applied at Activity creation by [ThemeManager.applyAccent].
* [DYNAMIC] uses Material You wallpaper colors on Android 12+ instead of an overlay.
*/
enum class AccentColor(
val displayName: String,
@ColorRes val primaryColor: Int,
@ColorRes val primaryVariant: Int,
@ColorRes val onPrimary: Int
@ColorRes val onPrimary: Int,
@StyleRes val themeOverlay: Int
) {
BLUE(
displayName = "Blue",
primaryColor = R.color.accent_blue_primary,
primaryVariant = R.color.accent_blue_variant,
onPrimary = R.color.accent_on_primary
onPrimary = R.color.accent_blue_on_primary,
themeOverlay = R.style.ThemeOverlay_WirelessADB_Accent_Blue
),

TEAL(
displayName = "Teal",
primaryColor = R.color.accent_teal_primary,
primaryVariant = R.color.accent_teal_variant,
onPrimary = R.color.accent_on_primary
onPrimary = R.color.accent_teal_on_primary,
themeOverlay = R.style.ThemeOverlay_WirelessADB_Accent_Teal
),

PURPLE(
displayName = "Purple",
primaryColor = R.color.accent_purple_primary,
primaryVariant = R.color.accent_purple_variant,
onPrimary = R.color.accent_on_primary
onPrimary = R.color.accent_purple_on_primary,
themeOverlay = R.style.ThemeOverlay_WirelessADB_Accent_Purple
),

ORANGE(
displayName = "Orange",
primaryColor = R.color.accent_orange_primary,
primaryVariant = R.color.accent_orange_variant,
onPrimary = R.color.accent_on_primary
onPrimary = R.color.accent_orange_on_primary,
themeOverlay = R.style.ThemeOverlay_WirelessADB_Accent_Orange
),

PINK(
displayName = "Pink",
primaryColor = R.color.accent_pink_primary,
primaryVariant = R.color.accent_pink_variant,
onPrimary = R.color.accent_on_primary
onPrimary = R.color.accent_pink_on_primary,
themeOverlay = R.style.ThemeOverlay_WirelessADB_Accent_Pink
),

GREEN(
displayName = "Green",
primaryColor = R.color.accent_green_primary,
primaryVariant = R.color.accent_green_variant,
onPrimary = R.color.accent_on_primary
onPrimary = R.color.accent_green_on_primary,
themeOverlay = R.style.ThemeOverlay_WirelessADB_Accent_Green
),

/**
* Material You wallpaper-based colors (Android 12+ only). Falls back to
* [DEFAULT] on older devices. Keep last: prefs store the ordinal.
*/
DYNAMIC(
displayName = "Dynamic",
primaryColor = R.color.accent_teal_primary,
primaryVariant = R.color.accent_teal_variant,
onPrimary = R.color.accent_on_primary,
themeOverlay = 0
);

companion object {
Expand Down
26 changes: 26 additions & 0 deletions app/src/main/java/com/phenix/wirelessadb/theme/ThemeManager.kt
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
package com.phenix.wirelessadb.theme

import android.app.Activity
import android.content.Context
import android.content.res.Configuration
import android.os.Build
import androidx.appcompat.app.AppCompatDelegate
import com.google.android.material.color.DynamicColors
import com.phenix.wirelessadb.PrefsManager

/**
Expand Down Expand Up @@ -48,6 +51,29 @@ object ThemeManager {
return PrefsManager.getThemeMode(context)
}

/**
* Apply the saved accent color to an Activity's theme.
* Call after super.onCreate() and before setContentView().
*
* DYNAMIC uses Material You wallpaper colors (Android 12+); other accents
* apply a static tonal overlay that works on every supported version.
*/
fun applyAccent(activity: Activity) {
val accent = getAccentColor(activity)
if (accent == AccentColor.DYNAMIC && isDynamicColorAvailable()) {
DynamicColors.applyToActivityIfAvailable(activity)
} else {
val effective = if (accent == AccentColor.DYNAMIC) AccentColor.DEFAULT else accent
activity.theme.applyStyle(effective.themeOverlay, true)
}
}

/**
* Whether Material You dynamic color is available on this device.
*/
fun isDynamicColorAvailable(): Boolean =
Build.VERSION.SDK_INT >= Build.VERSION_CODES.S && DynamicColors.isDynamicColorAvailable()

/**
* Get current accent color.
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import com.phenix.wirelessadb.util.AccessibilityHelper.setStateDescription
import com.phenix.wirelessadb.relay.TailscaleHelper
import com.phenix.wirelessadb.viewmodel.AdbViewModel
import com.phenix.wirelessadb.warpgate.WarpgateConfig
import com.phenix.wirelessadb.util.applyBottomSystemBarInsets

/**
* TAB 1: CONTROL (v1.3.0 - Mode-Based UI)
Expand Down Expand Up @@ -74,6 +75,8 @@ class ControlFragment : Fragment() {

override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
// Edge-to-edge: keep scrollable content clear of the gesture/navigation bar
view.applyBottomSystemBarInsets()
setupViews()
observeViewModel()
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import com.phenix.wirelessadb.model.TrustedDevice
import com.phenix.wirelessadb.relay.DeviceAuthManager
import com.phenix.wirelessadb.util.AccessibilityHelper.announceForAccessibility
import com.phenix.wirelessadb.viewmodel.AdbViewModel
import com.phenix.wirelessadb.util.applyBottomSystemBarInsets

/**
* TAB 2: DEVICES (v1.3.0)
Expand Down Expand Up @@ -42,6 +43,8 @@ class DevicesFragment : Fragment() {

override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
// Edge-to-edge: keep scrollable content clear of the gesture/navigation bar
view.applyBottomSystemBarInsets()
authManager = DeviceAuthManager(requireContext())
setupViews()
observeViewModel()
Expand Down
26 changes: 21 additions & 5 deletions app/src/main/java/com/phenix/wirelessadb/ui/HelpFragment.kt
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import com.phenix.wirelessadb.databinding.FragmentHelpBinding
import com.phenix.wirelessadb.theme.AccentColor
import com.phenix.wirelessadb.theme.ThemeManager
import com.phenix.wirelessadb.theme.ThemeMode
import com.phenix.wirelessadb.util.applyBottomSystemBarInsets

class HelpFragment : Fragment() {

Expand All @@ -33,6 +34,8 @@ class HelpFragment : Fragment() {

override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
// Edge-to-edge: keep scrollable content clear of the gesture/navigation bar
view.applyBottomSystemBarInsets()
setupViews()
}

Expand Down Expand Up @@ -105,7 +108,7 @@ class HelpFragment : Fragment() {

private fun setupAccentColorButtons() {
binding.apply {
val accentButtons = listOf(
val accentButtons = mutableListOf(
accentBlue to AccentColor.BLUE,
accentTeal to AccentColor.TEAL,
accentPurple to AccentColor.PURPLE,
Expand All @@ -114,19 +117,28 @@ class HelpFragment : Fragment() {
accentGreen to AccentColor.GREEN
)

// Material You option only exists where the system provides dynamic color
if (ThemeManager.isDynamicColorAvailable()) {
accentDynamic.visibility = View.VISIBLE
accentDynamic.backgroundTintList = ContextCompat.getColorStateList(
requireContext(), android.R.color.system_accent1_500
)
accentButtons.add(accentDynamic to AccentColor.DYNAMIC)
}

// Set up click handlers and initial selection indicator
val currentAccent = ThemeManager.getAccentColor(requireContext())

accentButtons.forEach { (button, color) ->
// Add check icon to currently selected color
updateAccentButtonSelection(button, color == currentAccent)
updateAccentButtonSelection(button, color, color == currentAccent)

button.setOnClickListener {
ThemeManager.setAccentColor(requireContext(), color)

// Update selection indicators
accentButtons.forEach { (btn, clr) ->
updateAccentButtonSelection(btn, clr == color)
updateAccentButtonSelection(btn, clr, clr == color)
}

// Recreate activity to apply new accent color immediately
Expand All @@ -136,11 +148,15 @@ class HelpFragment : Fragment() {
}
}

private fun updateAccentButtonSelection(button: MaterialButton, isSelected: Boolean) {
private fun updateAccentButtonSelection(
button: MaterialButton,
accent: AccentColor,
isSelected: Boolean
) {
if (isSelected) {
button.icon = ContextCompat.getDrawable(requireContext(), R.drawable.ic_check)
button.iconSize = resources.getDimensionPixelSize(R.dimen.accent_check_size)
button.iconTint = ContextCompat.getColorStateList(requireContext(), R.color.accent_on_primary)
button.iconTint = ContextCompat.getColorStateList(requireContext(), accent.onPrimary)
} else {
button.icon = null
button.iconSize = 0
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import androidx.recyclerview.widget.LinearLayoutManager
import com.phenix.wirelessadb.R
import com.phenix.wirelessadb.databinding.FragmentStatisticsBinding
import com.phenix.wirelessadb.viewmodel.StatisticsViewModel
import com.phenix.wirelessadb.util.applyBottomSystemBarInsets

/**
* TAB 3: STATISTICS (v1.3.0)
Expand Down Expand Up @@ -39,6 +40,8 @@ class StatisticsFragment : Fragment() {

override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
// Edge-to-edge: keep scrollable content clear of the gesture/navigation bar
view.applyBottomSystemBarInsets()
setupViews()
observeViewModel()
}
Expand Down
Loading
Loading