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
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
.idea
.gradle
local.properties
build/
.kotlin/
1 change: 1 addition & 0 deletions app/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ dependencies {
implementation libs.aboutlibraries.core
implementation libs.aboutlibraries
implementation libs.preference.ktx
implementation libs.security.crypto
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🌐 Web query:

androidx.security security-crypto latest stable version 1.1.0-alpha06 production ready

💡 Result:

No — 1.1.0-alpha06 is an alpha (pre‑release) and not intended for production. The stable 1.1.0 release was published on July 30, 2025; prefer that (or a later stable) for production. Note: the security-crypto artifacts have been marked deprecated — consider following the deprecation guidance in the release notes. [1][2]

Sources:
[1] AndroidX Security release notes (shows 1.1.0 release on July 30, 2025 and alpha/rc history).
[2] AndroidX versions/all-channel & deprecation note for security-crypto.


🏁 Script executed:

find . -name "libs.versions.toml" -o -name "build.gradle" | head -20

Repository: jonathanklee/VolumeLockr

Length of output: 129


🏁 Script executed:

cat gradle/libs.versions.toml | grep -A 2 -B 2 "security-crypto\|security\.crypto"

Repository: jonathanklee/VolumeLockr

Length of output: 387


🏁 Script executed:

cat gradle/libs.versions.toml | grep "securityCrypto"

Repository: jonathanklee/VolumeLockr

Length of output: 199


Upgrade security-crypto version and address library deprecation.

The dependency uses version 1.1.0-alpha06, an alpha pre-release. A stable version 1.1.0 has been available since July 30, 2025 and should be used for production.

More importantly, androidx.security:security-crypto has been deprecated. Follow the deprecation guidance in the AndroidX Security release notes to migrate to the recommended alternative.

Update gradle/libs.versions.toml line with securityCrypto = "1.1.0" at minimum, and plan migration away from the deprecated library.


testImplementation libs.junit
androidTestImplementation libs.ext.junit
Expand Down
7 changes: 6 additions & 1 deletion app/src/main/java/com/klee/volumelockr/BootReceiver.kt
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,12 @@ class BootReceiver : BroadcastReceiver() {

override fun onReceive(context: Context, intent: Intent) {
if (intent.action == Intent.ACTION_BOOT_COMPLETED) {
VolumeService.start(context)
val prefs = context.getSharedPreferences(VolumeService.APP_SHARED_PREFERENCES, Context.MODE_PRIVATE)
val locks = prefs.getString(VolumeService.LOCKS_KEY, "")

if (!locks.isNullOrEmpty() && locks != "{}") {
VolumeService.start(context)
}
}
}
}
16 changes: 9 additions & 7 deletions app/src/main/java/com/klee/volumelockr/service/VolumeService.kt
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
package com.klee.volumelockr.service

import android.annotation.SuppressLint
import android.app.Notification
import android.app.NotificationChannel
import android.app.NotificationManager
Expand Down Expand Up @@ -91,6 +90,13 @@ class VolumeService : Service() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
tryShowNotification()
}

if (mVolumeLock.isEmpty()) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
stopForeground(Service.STOP_FOREGROUND_REMOVE)
}
stopSelf()
}
}

override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
Expand Down Expand Up @@ -262,10 +268,6 @@ class VolumeService : Service() {
@RequiresApi(Build.VERSION_CODES.O)
@Synchronized
fun tryShowNotification() {
if (mVolumeLock.isEmpty()) {
return
}

createNotificationChannel()
val notification = Notification.Builder(this, NOTIFICATION_CHANNEL_ID)
.setContentTitle(NOTIFICATION_TITLE)
Expand All @@ -277,15 +279,15 @@ class VolumeService : Service() {
startForeground(NOTIFICATION_ID, notification)
}

@SuppressLint("WrongConstant")
@RequiresApi(Build.VERSION_CODES.N)
@Synchronized
fun tryHideNotification() {
if (mVolumeLock.isNotEmpty()) {
return
}

stopForeground(NOTIFICATION_ID)
stopForeground(Service.STOP_FOREGROUND_REMOVE)
stopSelf()
}

private fun createNotificationContentIntent(): PendingIntent {
Expand Down
57 changes: 57 additions & 0 deletions app/src/main/java/com/klee/volumelockr/ui/AboutFragment.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
package com.klee.volumelockr.ui

import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.fragment.app.Fragment
import androidx.recyclerview.widget.GridLayoutManager
import androidx.recyclerview.widget.RecyclerView
import com.klee.volumelockr.R
import com.mikepenz.aboutlibraries.LibsBuilder

class AboutFragment : Fragment() {

override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View {
return inflater.inflate(R.layout.fragment_about, container, false)
}

override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)

if (savedInstanceState == null) {
val libsFragment = LibsBuilder()
.supportFragment()

childFragmentManager.beginTransaction()
.add(R.id.about_libs_container, libsFragment)
.commit()
}

view.post {
val recyclerView = findRecyclerView(view)
recyclerView?.let { rv ->
val spanCount = if (resources.getBoolean(R.bool.use_two_columns)) 2 else 1
if (spanCount > 1) {
rv.layoutManager = GridLayoutManager(requireContext(), spanCount)
}
}
}
}

private fun findRecyclerView(view: View): RecyclerView? {
if (view is RecyclerView) return view
if (view is ViewGroup) {
for (i in 0 until view.childCount) {
val child = view.getChildAt(i)
val rv = findRecyclerView(child)
if (rv != null) return rv
}
}
return null
}
}
73 changes: 47 additions & 26 deletions app/src/main/java/com/klee/volumelockr/ui/MainActivity.kt
Original file line number Diff line number Diff line change
@@ -1,17 +1,17 @@
package com.klee.volumelockr.ui

import android.app.AlertDialog
import android.app.Dialog
import android.app.NotificationManager
import android.content.Intent
import android.os.Build
import android.os.Bundle
import android.provider.Settings
import androidx.activity.enableEdgeToEdge
import androidx.annotation.RequiresApi
import androidx.appcompat.app.AppCompatActivity
import androidx.core.view.ViewCompat
import androidx.core.view.WindowInsetsCompat
import androidx.fragment.app.DialogFragment
import androidx.navigation.fragment.NavHostFragment
import androidx.navigation.ui.AppBarConfiguration
import androidx.navigation.ui.setupWithNavController
import com.google.android.material.navigation.NavigationBarView
import com.klee.volumelockr.R
import com.klee.volumelockr.databinding.ActivityMainBinding

Expand All @@ -20,10 +20,12 @@ class MainActivity : AppCompatActivity() {
private lateinit var binding: ActivityMainBinding

override fun onCreate(savedInstanceState: Bundle?) {
enableEdgeToEdge()
super.onCreate(savedInstanceState)
binding = ActivityMainBinding.inflate(layoutInflater)
setContentView(binding.root)
handleEdgeToEdgeInsets()
setupNavigation()
setupWindowInsets()
}

override fun onResume() {
Expand All @@ -34,6 +36,20 @@ class MainActivity : AppCompatActivity() {
}
}

private fun setupNavigation() {
setSupportActionBar(binding.toolbar)
val navHostFragment = supportFragmentManager
.findFragmentById(R.id.fragment_container_view) as NavHostFragment
val navController = navHostFragment.navController
val appBarConfiguration = AppBarConfiguration(
setOf(R.id.volumeSliderFragment, R.id.settingsFragment, R.id.about_libraries)
)
binding.toolbar.setupWithNavController(navController, appBarConfiguration)

val navView: NavigationBarView? = binding.bottomNavigation ?: binding.navigationRail
navView?.setupWithNavController(navController)
}

@RequiresApi(Build.VERSION_CODES.M)
private fun checkDoNotDisturbPermission() {
val notificationManager =
Expand All @@ -44,32 +60,37 @@ class MainActivity : AppCompatActivity() {
}
}

class PolicyAccessDialog : DialogFragment() {
companion object {
const val TAG = "PolicyAccessDialog"
}

@RequiresApi(Build.VERSION_CODES.M)
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog =
AlertDialog.Builder(requireContext())
.setMessage(getString(R.string.dialog_policy_access_title))
.setCancelable(false)
.setPositiveButton(getString(R.string.dialog_allow)) { _, _ ->
startActivity(Intent(Settings.ACTION_NOTIFICATION_POLICY_ACCESS_SETTINGS))
}
.create()
}

private fun handleEdgeToEdgeInsets() {
ViewCompat.setOnApplyWindowInsetsListener(binding.root) { v, windowInsets ->
private fun setupWindowInsets() {
ViewCompat.setOnApplyWindowInsetsListener(binding.appBarLayout) { v, windowInsets ->
val bars = windowInsets.getInsets(
WindowInsetsCompat.Type.systemBars()
or WindowInsetsCompat.Type.displayCutout()
)
v.setPadding(bars.left, bars.top, bars.right, 0)
WindowInsetsCompat.CONSUMED
}

v.setPadding(bars.left, bars.top, bars.right, bars.bottom)
binding.bottomNavigation?.let { view ->
ViewCompat.setOnApplyWindowInsetsListener(view) { v, windowInsets ->
val bars = windowInsets.getInsets(
WindowInsetsCompat.Type.systemBars()
or WindowInsetsCompat.Type.displayCutout()
)
v.setPadding(bars.left, 0, bars.right, bars.bottom)
WindowInsetsCompat.CONSUMED
}
}

WindowInsetsCompat.CONSUMED
binding.navigationRail?.let { view ->
ViewCompat.setOnApplyWindowInsetsListener(view) { v, windowInsets ->
val bars = windowInsets.getInsets(
WindowInsetsCompat.Type.systemBars()
or WindowInsetsCompat.Type.displayCutout()
)
// Rail needs top and bottom padding usually, and left padding
v.setPadding(bars.left, bars.top, 0, bars.bottom)
WindowInsetsCompat.CONSUMED
}
}
}
}
27 changes: 27 additions & 0 deletions app/src/main/java/com/klee/volumelockr/ui/PolicyAccessDialog.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package com.klee.volumelockr.ui

import android.app.AlertDialog
import android.app.Dialog
import android.content.Intent
import android.os.Build
import android.os.Bundle
import android.provider.Settings
import androidx.annotation.RequiresApi
import androidx.fragment.app.DialogFragment
import com.klee.volumelockr.R

class PolicyAccessDialog : DialogFragment() {
companion object {
const val TAG = "PolicyAccessDialog"
}

@RequiresApi(Build.VERSION_CODES.M)
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog =
AlertDialog.Builder(requireContext())
.setMessage(getString(R.string.dialog_policy_access_title))
.setCancelable(false)
.setPositiveButton(getString(R.string.dialog_allow)) { _, _ ->
startActivity(Intent(Settings.ACTION_NOTIFICATION_POLICY_ACCESS_SETTINGS))
}
.create()
}
Loading