Skip to content

Comments

feat: track connection volume and add restore on disconnect option#32

Merged
Steve-Mr merged 5 commits intomainfrom
dev
Dec 5, 2025
Merged

feat: track connection volume and add restore on disconnect option#32
Steve-Mr merged 5 commits intomainfrom
dev

Conversation

@Steve-Mr
Copy link
Owner

@Steve-Mr Steve-Mr commented Dec 4, 2025

Summary

This PR introduces a new feature to record the system volume level at the start and end of a device connection session. It also adds an experimental setting to automatically restore the system volume to its pre-connection level when a device disconnects. Additionally, this update includes a database migration, UI updates to display volume logs, and a comprehensive upgrade of project dependencies (Kotlin, Compose, Room, Hilt).

Changes

New Features & Logic

  • Volume Tracking: The Connection entity now includes startVolume and endVolume fields to store volume percentages.
  • Database Migration: Bumped Room database version to 2 and implemented MIGRATION_1_2 to add the new volume columns to the existing table.
  • Volume Restoration:
    • Implemented logic in ForegroundService to capture volume before connection and after disconnection.
    • Added a delay (600ms) during disconnection handling to ensure the system switches audio output before recording the final volume.
    • Added functionality to force-restore the pre-connection volume if the "Restore Volume on Disconnect" preference is enabled and no other devices are connected.
  • Preferences: Added DataStore keys and repository methods for PREF_RESTORE_VOLUME and PREF_LAST_SYSTEM_VOLUME.

UI Updates

  • Settings: Added a new "Restore Volume on Disconnect" switch in the Settings screen.
  • Connection List: Updated ConnectionListAdapter and item_connection.xml to display volume logs (e.g., "Vol: 20% -> 50%") for past connections.
  • Strings: Added localized strings for the volume log format and the new settings description.

Dependency & Build Updates

  • Versioning: Bumped versionCode to 6 and versionName to "2025.12.04".
  • Kotlin & Tools: Updated Kotlin to 2.2.21 and KSP to 2.2.21-2.0.4.
  • AndroidX & Compose:
    • Updated Compose BOM to 2025.12.00.
    • Updated Activity, Lifecycle, and Room (2.8.4) libraries.
  • DI: Updated Hilt to 2.57.2.

This commit introduces the ability to record and display the media volume before a device connects and after it disconnects.

- **Database:**
    - The `Connection` entity is updated with `startVolume` and `endVolume` fields to store volume percentages.
    - The database version is bumped to `2`, and a migration (`MIGRATION_1_2`) is added to alter the `connection_table` with the new `start_volume` and `end_volume` columns.

- **UI:**
    - A new `TextView` (`text_volume_log`) is added to the `item_connection.xml` layout to display the volume change.
    - The `ConnectionListAdapter` is updated to bind the `startVolume` and `endVolume` data to the new `TextView`, showing it only when the data is available.
This commit introduces a new feature that saves the system volume when a wired headset is connected and restores it after disconnection.

This includes:
- A new toggle in the settings screen to enable or disable this feature.
- `DataStore` preferences to store the feature's enabled state and the last known system volume.
- Updates to the `SettingsViewModel` and `PreferenceRepository` to handle the logic for storing and retrieving these new settings.
This commit refactors the volume restoration logic to be more reliable and introduces several key improvements:

- **Delayed Volume Restoration:** When a device disconnects, the service now waits 600ms before restoring the volume. This delay allows the system to switch audio output back to the internal speakers, ensuring the correct volume is restored.
- **Improved Volume Capture:**
    - The `startVolume` (volume when a device connects) is now recorded.
    - The `endVolume` (system volume after a device disconnects) is also recorded.
- **System Volume Tracking:**
    - The service now caches the system's speaker volume when no devices are connected.
    - This cached volume is used as the `startVolume` for a new connection if it's the first one, providing a more accurate restoration point.
- **Conditional Restoration:** The volume restoration now only occurs if all Bluetooth audio devices are disconnected, preventing interference when switching between different headphones.
- **Code Cleanup:** Added a `percentageToVolumeIndex` helper function for cleaner volume calculations and removed commented-out legacy code.
This commit adds new string resources for the "Restore Volume on Disconnect" feature.

- `volume_log_format`: A format string to log volume changes.
- `restore_system_volume`: The title for the feature toggle.
- `restore_volume_description`: A description explaining what the feature does, noting it's experimental.

Translations for Simplified Chinese are also included.
This commit updates various dependencies to their latest versions and increments the app version.

**Version Changes:**
- `versionCode` increased from `5` to `6`.
- `versionName` updated from `2025.09.21` to `2025.12.04`.

**Dependency Updates:**
- **Kotlin:**
    - `org.jetbrains.kotlin.android` plugin to `2.2.21`.
    - `org.jetbrains.kotlin.plugin.compose` plugin to `2.2.21`.
- **KSP:**
    - `com.google.devtools.ksp` plugin to `2.2.21-2.0.4`.
- **Dagger Hilt:**
    - `com.google.dagger.hilt.android` plugin and dependencies to `2.57.2`.
- **AndroidX Activity:**
    - `androidx.activity:activity-ktx` and `androidx.activity:activity-compose` to `1.12.1`.
- **AndroidX Lifecycle:**
    - All `androidx.lifecycle` artifacts to `2.10.0`.
- **AndroidX Compose:**
    - `androidx.compose:compose-bom` to `2025.12.00`.
    - `androidx.compose.material3:material3` to `1.4.0`.
- **AndroidX Room:**
    - All `androidx.room` artifacts to `2.8.4`.
- **AndroidX WorkManager:**
    - `androidx.work:work-runtime-ktx` to `2.11.0`.
- **AndroidX Databinding:**
    - `androidx.databinding:databinding-runtime` to `8.13.1`.
- **AndroidX DataStore:**
    - `androidx.datastore:datastore-preferences` to `1.2.0`.
@gemini-code-assist
Copy link

Summary of Changes

Hello @Steve-Mr, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request introduces a significant feature set focused on audio volume management during device connections. It enables the application to monitor and log system volume changes when devices connect and disconnect, and provides users with an experimental option to automatically revert the volume to its initial state after disconnection. These changes are supported by necessary database schema updates, user interface modifications to display volume history, and a comprehensive update of various core Android and third-party libraries to their latest versions.

Highlights

  • Volume Tracking: The application now tracks and records the system volume level at the beginning and end of each device connection session.
  • Volume Restoration Option: An experimental setting has been added that allows the system volume to be automatically restored to its pre-connection level when a device disconnects.
  • Database Migration: The Room database schema has been updated to version 2, including a migration to add new columns for storing start and end volume data in the 'Connection' entity.
  • UI Enhancements: The connection list now displays the recorded volume logs (e.g., 'Vol: 20% -> 50%') for past connections, and a new switch for the volume restoration feature is available in the Settings screen.
  • Dependency Updates: Numerous project dependencies have been upgraded, including Kotlin (to 2.2.21), Compose BOM (to 2025.12.00), Room (to 2.8.4), and Hilt (to 2.57.2).
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

Copy link

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces a valuable feature for tracking and restoring volume, complete with a database migration and significant dependency upgrades. The implementation is solid, especially the clever use of a delay in ForegroundService to handle audio output switching. My review focuses on enhancing the clarity and robustness of the new logic, with suggestions to improve maintainability by removing magic numbers and clarifying control flow.

Comment on lines +66 to +72
if (startVol == null || endVol == null) connectionVolumeLogView.visibility = View.GONE
else {
val startVolString = startVol.toString()
val endVolString = endVol.toString()
connectionVolumeLogView.visibility = View.VISIBLE
connectionVolumeLogView.text = itemView.context.getString(R.string.volume_log_format, startVolString, endVolString)
}

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

This block can be simplified. You can remove the intermediate startVolString and endVolString variables, as getString can directly accept the Int? values and will call toString() on them. This makes the code more concise and idiomatic.

            if (startVol == null || endVol == null) {
                connectionVolumeLogView.visibility = View.GONE
            } else {
                connectionVolumeLogView.visibility = View.VISIBLE
                connectionVolumeLogView.text = itemView.context.getString(R.string.volume_log_format, startVol, endVol)
            }

Comment on lines +144 to +145
fun getLastSystemVolume(): Flow<Int> = datastore.data
.map { preferences -> preferences[PREF_LAST_SYSTEM_VOLUME] ?: -1 }

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

This function uses -1 as a default value to indicate that no volume has been saved. While functional, this "magic number" can make the code harder to understand for others who might not know its special meaning. It's a best practice to define such special values as named constants.

I recommend adding a constant like const val VOLUME_NOT_SET = -1 to your Constants.kt file and using it here and in ForegroundService where you check if (savedVolume != -1). This will make the code more self-documenting and maintainable.

Suggested change
fun getLastSystemVolume(): Flow<Int> = datastore.data
.map { preferences -> preferences[PREF_LAST_SYSTEM_VOLUME] ?: -1 }
fun getLastSystemVolume(): Flow<Int> = datastore.data
.map { preferences -> preferences[PREF_LAST_SYSTEM_VOLUME] ?: Constants.VOLUME_NOT_SET }

if (connectionToSave != null) {
// [核心修改] 延迟 600ms,等待系统将音频输出切换回扬声器并更新音量状态
// 具体的毫秒数可能因机型而异,500-800ms 通常是安全的
delay(600)

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

Using a fixed delay(600) is a good pragmatic approach for this experimental feature, but it can be fragile. On some devices or under heavy load, this delay might not be sufficient, leading to an incorrect endVolume being recorded. On faster devices, it adds an unnecessary wait.

For future improvement, you might consider a more robust mechanism. For example, you could listen for the AudioManager.ACTION_AUDIO_BECOMING_NOISY broadcast intent, which signals that the audio output is about to change. This would provide a more reliable trigger than a fixed delay. For now, the current implementation is a reasonable starting point.

return finalConnection
}

return connectionToSave

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The statement return connectionToSave on this line is slightly confusing. If the if (connectionToSave != null) block above is entered, finalConnection is returned and this line is unreachable. If the if block is skipped, connectionToSave is null at this point.

To improve code clarity and explicitly show that null is returned in that case, it's better to return null directly.

Suggested change
return connectionToSave
return null

@Steve-Mr Steve-Mr merged commit 0470a98 into main Dec 5, 2025
3 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant