-
-
Notifications
You must be signed in to change notification settings - Fork 5
Corrected volume max setting not loading properly #8
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Conversation
Tested this PR: Stopping the service and starting the app again with "Allow lower than limit" enabled now stays functional as expected. I did happen to notice if you set the volume lower than the limit while stopping the service and restarting the app, the upper limit gets changed to that lower volume, meaning you'd have to go into the app and turn it back up if wanted. This could be considered a feature rather than a bug though depending who you ask since it could help prevent getting hit with overly-loud volume in some cases. |
WalkthroughMultiple build, manifest, resource, and Kotlin source updates: build tooling and SDK versions bumped; Material components and theming applied; notification permission and foreground media playback permission added; Slider/Material components replace legacy widgets; VolumeService preference loading refactored and permission/request flow added in MainActivity. Changes
Sequence Diagram(s)sequenceDiagram
participant User
participant MainActivity
participant System as AndroidSystem
participant VolumeService
Note over MainActivity: App start / UI init
User->>MainActivity: Launch app
MainActivity->>AndroidSystem: check POST_NOTIFICATIONS permission
alt permission denied
MainActivity->>User: request permission (runtime dialog)
User-->>MainActivity: grant/deny
end
MainActivity->>AndroidSystem: check DND access (existing flow)
Note over VolumeService: Service start path
AndroidSystem->>VolumeService: startService / onStartCommand
VolumeService->>VolumeService: loadGeneralPreferences()
VolumeService->>AndroidSystem: register content observers (ringer/volume)
AndroidSystem-->>VolumeService: content changes (async)
VolumeService->>AndroidSystem: show/hide notification based on mVolumeLock (tryShowNotification/tryHideNotification)
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~20–30 minutes
Pre-merge checks and finishing touches❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✨ Finishing touches
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Caution
Inline review comments failed to post. This is likely due to GitHub's limits when posting large numbers of comments.
Actionable comments posted: 2
🧹 Outside diff range and nitpick comments (9)
app/src/main/java/com/klee/volumelockr/ui/VolumeLockrApplication.kt (1)
7-10
: Consider adding version check and error handling.While the implementation is functional, it could be more robust:
- DynamicColors might not be available on all Android versions
- The application should gracefully handle cases where dynamic colors aren't supported
Consider this more defensive implementation:
override fun onCreate() { super.onCreate() - DynamicColors.applyToActivitiesIfAvailable(this) + try { + if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.S) { + DynamicColors.applyToActivitiesIfAvailable(this) + } + } catch (e: Exception) { + android.util.Log.w("VolumeLockr", "Failed to apply dynamic colors", e) + } }app/src/main/res/values/themes.xml (1)
3-4
: LGTM! Good choice using Material3 with dynamic colors.The migration to Material3 with dynamic colors support is a good modernization step. This will provide better theming capabilities and ensure proper dark mode support.
Consider documenting the theme capabilities in the README.md to help future contributors understand the theming system, especially regarding dynamic colors support.
build.gradle (2)
Line range hint
23-23
: Remove deprecated jcenter() repository.The jcenter() repository is deprecated and will be shut down. Consider removing it and ensuring all dependencies are available through mavenCentral() or google().
Apply this diff:
allprojects { repositories { google() mavenCentral() - jcenter() // Warning: this repository is going to shut down soon } }
Line range hint
1-31
: Consider implementing version catalogs for dependency management.The current build file uses hardcoded versions. Consider implementing Gradle version catalogs to centralize dependency management and make version updates more maintainable.
Create a new file
gradle/libs.versions.toml
:[versions] kotlin = "1.8.0" agp = "8.5.0" aboutlibraries = "8.9.0" [libraries] kotlin-gradle = { module = "org.jetbrains.kotlin:kotlin-gradle-plugin", version.ref = "kotlin" } android-gradle = { module = "com.android.tools.build:gradle", version.ref = "agp" } aboutlibraries-gradle = { module = "com.mikepenz.aboutlibraries.plugin:aboutlibraries-plugin", version.ref = "aboutlibraries" }Then update the build.gradle:
buildscript { - ext.kotlin_version = '1.8.0' repositories { google() mavenCentral() maven { url = uri("https://plugins.gradle.org/m2/") } } dependencies { - classpath 'com.android.tools.build:gradle:8.5.0' - classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" - classpath "com.mikepenz.aboutlibraries.plugin:aboutlibraries-plugin:8.9.0" + classpath(libs.android.gradle) + classpath(libs.kotlin.gradle) + classpath(libs.aboutlibraries.gradle) } }app/src/main/res/layout/volume_card.xml (1)
39-44
: Add contentDescription for accessibility support.The MaterialSwitch should have a content description to improve accessibility for users with screen readers.
Apply this diff:
<com.google.android.material.materialswitch.MaterialSwitch android:id="@+id/switchButton" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center" - android:text="@string/lock" /> + android:text="@string/lock" + android:contentDescription="@string/lock" />app/src/main/java/com/klee/volumelockr/ui/VolumeAdapter.kt (1)
122-124
: Fix indentation for better readability.The conditional expression could be formatted more clearly.
- holder.binding.slider.isEnabled = - mService?.getMode() == 2 && - mService?.getLocks()?.containsKey(AudioManager.STREAM_NOTIFICATION) == false + holder.binding.slider.isEnabled = mService?.getMode() == 2 && + !mService?.getLocks()?.containsKey(AudioManager.STREAM_NOTIFICATION)!!app/src/main/java/com/klee/volumelockr/service/VolumeService.kt (3)
179-182
: LGTM! Consider adding documentation.The new method effectively centralizes the loading of general preferences. Consider adding KDoc documentation to explain its purpose and the impact of the
ALLOW_LOWER
setting.+ /** + * Loads general preferences including the "Allow lower than limit" setting + * which determines if volumes can be set below their locked values. + */ private fun loadGeneralPreferences() {
Line range hint
186-195
: Consider additional synchronization for preference access.While
checkVolumes()
is synchronized, it readsmAllowLower
which can be modified byloadGeneralPreferences()
without synchronization. This could potentially lead to a race condition.Consider these options:
- Add synchronization to preference access:
@WorkerThread @Synchronized private fun checkVolumes() { + val allowLower = synchronized(this) { mAllowLower } for ((stream, volume) in mVolumeLock) { val current = mAudioManager.getStreamVolume(stream) - if ((current > volume) || (!mAllowLower && current != volume)) { + if ((current > volume) || (!allowLower && current != volume)) { mAudioManager.setStreamVolume(stream, volume, 0) invokeVolumeListenerCallback() } } }
- Or make mAllowLower volatile:
- private var mAllowLower = false + @Volatile + private var mAllowLower = false
Line range hint
186-195
: Document the volume adjustment behavior.The current implementation matches BurnyBoi's observation: when the service restarts with a volume lower than the limit, it maintains that lower volume. While this behavior is reasonable from a user experience perspective (preventing unexpected volume increases), it should be documented.
Consider adding a comment explaining this behavior:
@WorkerThread @Synchronized private fun checkVolumes() { + // Note: When "Allow lower than limit" is enabled and the service restarts, + // if the current volume is below the limit, it will be maintained at that level. + // This prevents unexpected volume increases on service restart. for ((stream, volume) in mVolumeLock) {
🛑 Comments failed to post (2)
app/src/main/res/layout/volume_card.xml (1)
33-37:
⚠️ Potential issueAdd essential Slider attributes for volume control.
The Slider component is missing crucial attributes needed for proper volume control functionality:
Apply this diff to add the necessary attributes:
<com.google.android.material.slider.Slider android:id="@+id/slider" android:layout_width="match_parent" android:layout_height="wrap_content" - android:layout_gravity="center"/> + android:layout_gravity="center" + android:valueFrom="0" + android:valueTo="100" + android:stepSize="1" + android:value="0"/>📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.<com.google.android.material.slider.Slider android:id="@+id/slider" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_gravity="center" android:valueFrom="0" android:valueTo="100" android:stepSize="1" android:value="0"/>
app/src/main/java/com/klee/volumelockr/ui/VolumeAdapter.kt (1)
65-73:
⚠️ Potential issueAdd validation for volume limit changes.
The current implementation allows immediate value changes without checking against previous maximum limits. This explains the reported behavior where the maximum volume limit adjusts to lower settings after restart.
Consider adding validation to:
- Check if the new value is below the previous maximum when "Allow lower than limit" is enabled
- Store the original maximum value separately to allow restoration
private fun registerSeekBarCallback(holder: ViewHolder, volume: Volume) { val listener = Slider.OnChangeListener { _, value, _ -> + val previousMax = mService?.getStoredMaxVolume(volume.stream) + val allowLower = mService?.isAllowLowerThanLimitEnabled() ?: false + + val newValue = if (!allowLower && previousMax != null) { + kotlin.math.min(value, previousMax.toFloat()) + } else { + value + } + if (volume.stream != AudioManager.STREAM_NOTIFICATION || mService?.getMode() == 2) { - mAudioManager.setStreamVolume(volume.stream, value.toInt(), 0) + mAudioManager.setStreamVolume(volume.stream, newValue.toInt(), 0) } - volume.value = value.toInt() + volume.value = newValue.toInt() } holder.binding.slider.addOnChangeListener(listener) }Committable suggestion skipped: line range outside the PR's diff.
Service could easily be killed by the system
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 4
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
⛔ Files ignored due to path filters (4)
fastlane/metadata/android/en-US/images/phoneScreenshots/1.png
is excluded by!**/*.png
fastlane/metadata/android/en-US/images/phoneScreenshots/2.png
is excluded by!**/*.png
screenshot.png
is excluded by!**/*.png
screenshot_night.png
is excluded by!**/*.png
📒 Files selected for processing (14)
README.md
(1 hunks)app/build.gradle
(2 hunks)app/src/main/AndroidManifest.xml
(2 hunks)app/src/main/java/com/klee/volumelockr/service/VolumeService.kt
(11 hunks)app/src/main/java/com/klee/volumelockr/ui/MainActivity.kt
(1 hunks)app/src/main/java/com/klee/volumelockr/ui/VolumeAdapter.kt
(7 hunks)app/src/main/java/com/klee/volumelockr/ui/VolumeLockrApplication.kt
(1 hunks)app/src/main/res/layout/preference_switch.xml
(1 hunks)app/src/main/res/layout/volume_card.xml
(3 hunks)app/src/main/res/values-night/themes.xml
(0 hunks)app/src/main/res/values/themes.xml
(1 hunks)build.gradle
(2 hunks)gradle.properties
(1 hunks)gradle/wrapper/gradle-wrapper.properties
(1 hunks)
💤 Files with no reviewable changes (1)
- app/src/main/res/values-night/themes.xml
🚧 Files skipped from review as they are similar to previous changes (3)
- app/src/main/res/layout/preference_switch.xml
- app/src/main/AndroidManifest.xml
- build.gradle
🧰 Additional context used
🪛 markdownlint-cli2 (0.18.1)
README.md
4-4: Images should have alternate text (alt text)
(MD045, no-alt-text)
4-4: Images should have alternate text (alt text)
(MD045, no-alt-text)
🔇 Additional comments (14)
app/src/main/java/com/klee/volumelockr/ui/VolumeLockrApplication.kt (1)
6-11
: LGTM!The application class correctly integrates Material's dynamic color theming. The implementation follows best practices by calling
super.onCreate()
first and using the recommendedapplyToActivitiesIfAvailable
method.app/src/main/java/com/klee/volumelockr/ui/MainActivity.kt (1)
43-43
: LGTM!Good improvement using the
NOTIFICATION_SERVICE
constant instead of the string literal.app/src/main/java/com/klee/volumelockr/service/VolumeService.kt (6)
182-185
: LGTM! This fixes the volume max setting not loading properly.The new
loadGeneralPreferences()
method correctly centralizes the loading of theALLOW_LOWER
setting from default shared preferences. This ensures the setting is reloaded when the service restarts, addressing the PR objective.
101-101
: LGTM! Ensures preferences are reloaded on service restart.Calling
loadGeneralPreferences()
inonStartCommand
ensures that theALLOW_LOWER
preference is reloaded each time the service is started, which is essential for the fix.
166-168
: LGTM! Idiomatic Kotlin preference editing.Using the
edit
extension function is the recommended approach for Kotlin. This makes the code more concise and ensuresapply()
is called automatically.
241-243
: Verify mode observer registration without version check.The mode observer registration now occurs unconditionally, whereas it may have previously been guarded by a version check (based on the pattern in the removed code). Verify that registering a content observer for
Settings.Global.getUriFor(MODE_RINGER_SETTING)
is safe across all supported API levels (19+).
254-254
: LGTM! Idiomatic collection checks.Using
isEmpty()
andisNotEmpty()
is more idiomatic Kotlin compared to size-based checks.Also applies to: 273-273
91-91
: No API level guard needed for Settings.Global.getInt – Settings.Global was introduced in API 17 and your minSdkVersion is 19 (app/build.gradle:14), so the calls on lines 91 and 217 are safe.gradle.properties (1)
20-22
: LGTM! Appropriate configuration for AGP 8.5.0.The added build feature flags are correct for the Android Gradle Plugin 8.5.0 upgrade:
buildconfig=true
ensures BuildConfig class generationnonTransitiveRClass=false
maintains R class transitivity for compatibilitynonFinalResIds=false
keeps resource IDs non-final for reflection-based librariesBased on learnings.
app/src/main/res/values/themes.xml (1)
3-14
: LGTM: confirmedpreference_switch.xml
exists.gradle/wrapper/gradle-wrapper.properties (1)
3-4
: Resolve build failure with Gradle 8.7 wrapperAfter updating from 7.2 to 8.7 the build (
./gradlew clean build --no-daemon
) fails with “Could not connect to the Gradle daemon”. Investigate and fix the daemon connectivity or wrapper configuration so that the build succeeds under Gradle 8.7, then re-verify.app/src/main/res/layout/volume_card.xml (1)
8-47
: LGTM – VolumeAdapter fully migrated to Material Slider;slider
ID and API in use, noseekBar
remnants.app/src/main/java/com/klee/volumelockr/ui/VolumeAdapter.kt (2)
47-49
: Slider initialization looks correct.The migration from SeekBar to Material Slider properly initializes the slider's value from the service's locked volume (if available) or the current volume value, and sets the bounds from the volume's min/max values.
59-62
: LGTM! Slider enable/disable logic is correct.All instances of enabling/disabling the slider are properly handled for:
- Password protection
- Volume lock status
- Notification stream ringer mode conditions
The migration from
seekBar.isEnabled
toslider.isEnabled
is consistent throughout.Also applies to: 92-96, 122-126, 133-134, 142-143
targetSdkVersion 34 | ||
versionCode 11 | ||
versionName "1.6.1" | ||
multiDexEnabled true |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion | 🟠 Major
Add the multidex runtime dependency for pre‑21 devices.
With multiDexEnabled true
and minSdkVersion 19
, ART/DEX on API 19–20 will crash at launch unless androidx.multidex:multidex
is packaged and initialized. Please add the dependency and ensure VolumeLockrApplication
calls MultiDex.install()
(or extends MultiDexApplication
) so the app boots on legacy devices.
dependencies {
@@
- implementation 'androidx.preference:preference-ktx:1.1.1'
+ implementation 'androidx.preference:preference-ktx:1.1.1'
+ implementation 'androidx.multidex:multidex:2.0.1'
}
If VolumeLockrApplication
doesn’t already handle it, add:
override fun attachBaseContext(base: Context) {
super.attachBaseContext(base)
MultiDex.install(this)
}
🤖 Prompt for AI Agents
In app/build.gradle around line 18, multiDexEnabled is set true but the multidex
runtime dependency is missing; add the AndroidX multidex dependency
(implementation "androidx.multidex:multidex:2.0.1") to the dependencies block
and keep multiDexEnabled true; then ensure the app's VolumeLockrApplication
initializes multidex on pre‑21 devices by either extending
androidx.multidex.MultiDexApplication or adding an override of attachBaseContext
that calls MultiDex.install(this) so the app won't crash on API 19–20.
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { | ||
if (ContextCompat.checkSelfPermission(applicationContext, NOTIFICATION_PERMISSION) | ||
== PackageManager.PERMISSION_DENIED) { | ||
requestPermissions(arrayOf(NOTIFICATION_PERMISSION), PERMISSION_REQUEST_CODE) | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Use correct API level constant for POST_NOTIFICATIONS.
The notification permission check uses Build.VERSION_CODES.M
(API 23), but POST_NOTIFICATIONS
was introduced in API 33 (TIRAMISU). This will cause the permission check to run on API levels 23-32 where the permission doesn't exist.
Apply this diff to use the correct API level:
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
if (ContextCompat.checkSelfPermission(applicationContext, NOTIFICATION_PERMISSION)
== PackageManager.PERMISSION_DENIED) {
requestPermissions(arrayOf(NOTIFICATION_PERMISSION), PERMISSION_REQUEST_CODE)
}
}
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { | |
if (ContextCompat.checkSelfPermission(applicationContext, NOTIFICATION_PERMISSION) | |
== PackageManager.PERMISSION_DENIED) { | |
requestPermissions(arrayOf(NOTIFICATION_PERMISSION), PERMISSION_REQUEST_CODE) | |
} | |
} | |
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { | |
if (ContextCompat.checkSelfPermission(applicationContext, NOTIFICATION_PERMISSION) | |
== PackageManager.PERMISSION_DENIED) { | |
requestPermissions(arrayOf(NOTIFICATION_PERMISSION), PERMISSION_REQUEST_CODE) | |
} | |
} |
🤖 Prompt for AI Agents
In app/src/main/java/com/klee/volumelockr/ui/MainActivity.kt around lines 32 to
37, the code checks for POST_NOTIFICATIONS using Build.VERSION_CODES.M (API 23)
which is incorrect because POST_NOTIFICATIONS was introduced in API 33; update
the API-level guard to check Build.VERSION_CODES.TIRAMISU (or SDK_INT >= 33)
before requesting or checking NOTIFICATION_PERMISSION so the permission logic
only runs on Android 13+ where the permission exists.
private fun registerSeekBarCallback(holder: ViewHolder, volume: Volume) { | ||
val listener = object : SeekBar.OnSeekBarChangeListener { | ||
override fun onProgressChanged(view: SeekBar?, progress: Int, fromUser: Boolean) { | ||
val listener = | ||
Slider.OnChangeListener { _, value, _ -> | ||
if (volume.stream != AudioManager.STREAM_NOTIFICATION || mService?.getMode() == 2) { | ||
mAudioManager.setStreamVolume(volume.stream, progress, 0) | ||
mAudioManager.setStreamVolume(volume.stream, value.toInt(), 0) | ||
} | ||
|
||
volume.value = progress | ||
} | ||
|
||
override fun onStartTrackingTouch(view: SeekBar?) { | ||
volume.value = value.toInt() | ||
} | ||
|
||
override fun onStopTrackingTouch(view: SeekBar?) { | ||
} | ||
} | ||
holder.binding.seekBar.setOnSeekBarChangeListener(listener) | ||
holder.binding.slider.addOnChangeListener(listener) | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Memory leak: Multiple listeners accumulate on slider.
Each time onBindViewHolder
is called for a ViewHolder (which happens frequently in RecyclerView during scrolling or data updates), a new OnChangeListener
is added to the slider without clearing previous listeners. This causes:
- The callback to be invoked multiple times for a single slider change
- Memory leaks as old listeners accumulate
- Potential performance degradation
Recommended solution: Clear existing listeners before adding a new one:
private fun registerSeekBarCallback(holder: ViewHolder, volume: Volume) {
+ holder.binding.slider.clearOnChangeListeners()
val listener =
Slider.OnChangeListener { _, value, _ ->
if (volume.stream != AudioManager.STREAM_NOTIFICATION || mService?.getMode() == 2) {
mAudioManager.setStreamVolume(volume.stream, value.toInt(), 0)
}
volume.value = value.toInt()
}
holder.binding.slider.addOnChangeListener(listener)
}
Alternatively, consider registering the listener once during ViewHolder creation in onCreateViewHolder
if the callback logic doesn't need per-bind customization (though this would require restructuring to pass volume data differently).
🤖 Prompt for AI Agents
In app/src/main/java/com/klee/volumelockr/ui/VolumeAdapter.kt around lines 64 to
74, the code adds a new Slider.OnChangeListener on every bind causing multiple
callbacks and memory leaks; fix it by removing existing listeners before adding
a new one (e.g., call slider.clearOnChangeListeners() or remove the previous
listener) or move listener registration to onCreateViewHolder and update the
ViewHolder’s bound Volume via a stored reference so the listener is only
registered once.
VolumeLockr allows you to control your Android device volume levels and set locks for each one of them. | ||
|
||
<p><img src="https://github.com/jonathanklee/VolumeLockr/blob/main/screenshot.png" width="200"/>  <img src="https://github.com/jonathanklee/VolumeLockr/blob/main/screenshot_night.png" width="200"/></p> | ||
<p><img src="https://github.com/jonathanklee/VolumeLockr/blob/main/fastlane/metadata/android/en-US/images/phoneScreenshots/1.png?raw=true" width="200"/>  <img src="https://github.com/jonathanklee/VolumeLockr/blob/main/fastlane/metadata/android/en-US/images/phoneScreenshots/2.png?raw=true" width="200"/></p> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Add alt text to images for accessibility.
The screenshot images are missing alternative text, which is important for accessibility and SEO.
Apply this diff to add descriptive alt text:
-<p><img src="https://github.com/jonathanklee/VolumeLockr/blob/main/fastlane/metadata/android/en-US/images/phoneScreenshots/1.png?raw=true" width="200"/>  <img src="https://github.com/jonathanklee/VolumeLockr/blob/main/fastlane/metadata/android/en-US/images/phoneScreenshots/2.png?raw=true" width="200"/></p>
+<p><img src="https://github.com/jonathanklee/VolumeLockr/blob/main/fastlane/metadata/android/en-US/images/phoneScreenshots/1.png?raw=true" alt="VolumeLockr main screen showing volume controls" width="200"/>  <img src="https://github.com/jonathanklee/VolumeLockr/blob/main/fastlane/metadata/android/en-US/images/phoneScreenshots/2.png?raw=true" alt="VolumeLockr settings screen" width="200"/></p>
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
<p><img src="https://github.com/jonathanklee/VolumeLockr/blob/main/fastlane/metadata/android/en-US/images/phoneScreenshots/1.png?raw=true" width="200"/>  <img src="https://github.com/jonathanklee/VolumeLockr/blob/main/fastlane/metadata/android/en-US/images/phoneScreenshots/2.png?raw=true" width="200"/></p> | |
<p><img src="https://github.com/jonathanklee/VolumeLockr/blob/main/fastlane/metadata/android/en-US/images/phoneScreenshots/1.png?raw=true" alt="VolumeLockr main screen showing volume controls" width="200"/>  <img src="https://github.com/jonathanklee/VolumeLockr/blob/main/fastlane/metadata/android/en-US/images/phoneScreenshots/2.png?raw=true" alt="VolumeLockr settings screen" width="200"/></p> |
🧰 Tools
🪛 markdownlint-cli2 (0.18.1)
4-4: Images should have alternate text (alt text)
(MD045, no-alt-text)
4-4: Images should have alternate text (alt text)
(MD045, no-alt-text)
🤖 Prompt for AI Agents
In README.md around line 4, the two <img> tags lack alt attributes; update each
tag to include descriptive alt text for accessibility and SEO (e.g.,
alt="VolumeLockr screenshot — main screen" and alt="VolumeLockr screenshot —
settings screen"), preserving the existing src and width attributes so the
images render the same while providing meaningful alternative text.
Per discussion: #3 (comment)
Summary by CodeRabbit
New Features
Improvements
Bug Fixes
Chores