Skip to content

Commit 2a69adb

Browse files
author
Jonathan Klee
authored
Merge pull request #13 from KoukeNeko/main
feat: Foldable support, Edge-to-Edge, and Fixes
2 parents 675912a + b391937 commit 2a69adb

26 files changed

+470
-158
lines changed

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
11
.idea
22
.gradle
33
local.properties
4+
build/
5+
.kotlin/

app/build.gradle

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@ dependencies {
5353
implementation libs.aboutlibraries.core
5454
implementation libs.aboutlibraries
5555
implementation libs.preference.ktx
56+
implementation libs.security.crypto
5657

5758
testImplementation libs.junit
5859
androidTestImplementation libs.ext.junit

app/src/main/java/com/klee/volumelockr/BootReceiver.kt

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,12 @@ class BootReceiver : BroadcastReceiver() {
99

1010
override fun onReceive(context: Context, intent: Intent) {
1111
if (intent.action == Intent.ACTION_BOOT_COMPLETED) {
12-
VolumeService.start(context)
12+
val prefs = context.getSharedPreferences(VolumeService.APP_SHARED_PREFERENCES, Context.MODE_PRIVATE)
13+
val locks = prefs.getString(VolumeService.LOCKS_KEY, "")
14+
15+
if (!locks.isNullOrEmpty() && locks != "{}") {
16+
VolumeService.start(context)
17+
}
1318
}
1419
}
1520
}

app/src/main/java/com/klee/volumelockr/service/VolumeService.kt

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
package com.klee.volumelockr.service
22

3-
import android.annotation.SuppressLint
43
import android.app.Notification
54
import android.app.NotificationChannel
65
import android.app.NotificationManager
@@ -91,6 +90,13 @@ class VolumeService : Service() {
9190
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
9291
tryShowNotification()
9392
}
93+
94+
if (mVolumeLock.isEmpty()) {
95+
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
96+
stopForeground(Service.STOP_FOREGROUND_REMOVE)
97+
}
98+
stopSelf()
99+
}
94100
}
95101

96102
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
@@ -262,10 +268,6 @@ class VolumeService : Service() {
262268
@RequiresApi(Build.VERSION_CODES.O)
263269
@Synchronized
264270
fun tryShowNotification() {
265-
if (mVolumeLock.isEmpty()) {
266-
return
267-
}
268-
269271
createNotificationChannel()
270272
val notification = Notification.Builder(this, NOTIFICATION_CHANNEL_ID)
271273
.setContentTitle(NOTIFICATION_TITLE)
@@ -277,15 +279,15 @@ class VolumeService : Service() {
277279
startForeground(NOTIFICATION_ID, notification)
278280
}
279281

280-
@SuppressLint("WrongConstant")
281282
@RequiresApi(Build.VERSION_CODES.N)
282283
@Synchronized
283284
fun tryHideNotification() {
284285
if (mVolumeLock.isNotEmpty()) {
285286
return
286287
}
287288

288-
stopForeground(NOTIFICATION_ID)
289+
stopForeground(Service.STOP_FOREGROUND_REMOVE)
290+
stopSelf()
289291
}
290292

291293
private fun createNotificationContentIntent(): PendingIntent {
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
package com.klee.volumelockr.ui
2+
3+
import android.os.Bundle
4+
import android.view.LayoutInflater
5+
import android.view.View
6+
import android.view.ViewGroup
7+
import androidx.fragment.app.Fragment
8+
import androidx.recyclerview.widget.GridLayoutManager
9+
import androidx.recyclerview.widget.RecyclerView
10+
import com.klee.volumelockr.R
11+
import com.mikepenz.aboutlibraries.LibsBuilder
12+
13+
class AboutFragment : Fragment() {
14+
15+
override fun onCreateView(
16+
inflater: LayoutInflater,
17+
container: ViewGroup?,
18+
savedInstanceState: Bundle?
19+
): View {
20+
return inflater.inflate(R.layout.fragment_about, container, false)
21+
}
22+
23+
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
24+
super.onViewCreated(view, savedInstanceState)
25+
26+
if (savedInstanceState == null) {
27+
val libsFragment = LibsBuilder()
28+
.supportFragment()
29+
30+
childFragmentManager.beginTransaction()
31+
.add(R.id.about_libs_container, libsFragment)
32+
.commit()
33+
}
34+
35+
view.post {
36+
val recyclerView = findRecyclerView(view)
37+
recyclerView?.let { rv ->
38+
val spanCount = if (resources.getBoolean(R.bool.use_two_columns)) 2 else 1
39+
if (spanCount > 1) {
40+
rv.layoutManager = GridLayoutManager(requireContext(), spanCount)
41+
}
42+
}
43+
}
44+
}
45+
46+
private fun findRecyclerView(view: View): RecyclerView? {
47+
if (view is RecyclerView) return view
48+
if (view is ViewGroup) {
49+
for (i in 0 until view.childCount) {
50+
val child = view.getChildAt(i)
51+
val rv = findRecyclerView(child)
52+
if (rv != null) return rv
53+
}
54+
}
55+
return null
56+
}
57+
}
Lines changed: 47 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,17 @@
11
package com.klee.volumelockr.ui
22

3-
import android.app.AlertDialog
4-
import android.app.Dialog
53
import android.app.NotificationManager
6-
import android.content.Intent
74
import android.os.Build
85
import android.os.Bundle
9-
import android.provider.Settings
6+
import androidx.activity.enableEdgeToEdge
107
import androidx.annotation.RequiresApi
118
import androidx.appcompat.app.AppCompatActivity
129
import androidx.core.view.ViewCompat
1310
import androidx.core.view.WindowInsetsCompat
14-
import androidx.fragment.app.DialogFragment
11+
import androidx.navigation.fragment.NavHostFragment
12+
import androidx.navigation.ui.AppBarConfiguration
13+
import androidx.navigation.ui.setupWithNavController
14+
import com.google.android.material.navigation.NavigationBarView
1515
import com.klee.volumelockr.R
1616
import com.klee.volumelockr.databinding.ActivityMainBinding
1717

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

2222
override fun onCreate(savedInstanceState: Bundle?) {
23+
enableEdgeToEdge()
2324
super.onCreate(savedInstanceState)
2425
binding = ActivityMainBinding.inflate(layoutInflater)
2526
setContentView(binding.root)
26-
handleEdgeToEdgeInsets()
27+
setupNavigation()
28+
setupWindowInsets()
2729
}
2830

2931
override fun onResume() {
@@ -34,6 +36,20 @@ class MainActivity : AppCompatActivity() {
3436
}
3537
}
3638

39+
private fun setupNavigation() {
40+
setSupportActionBar(binding.toolbar)
41+
val navHostFragment = supportFragmentManager
42+
.findFragmentById(R.id.fragment_container_view) as NavHostFragment
43+
val navController = navHostFragment.navController
44+
val appBarConfiguration = AppBarConfiguration(
45+
setOf(R.id.volumeSliderFragment, R.id.settingsFragment, R.id.about_libraries)
46+
)
47+
binding.toolbar.setupWithNavController(navController, appBarConfiguration)
48+
49+
val navView: NavigationBarView? = binding.bottomNavigation ?: binding.navigationRail
50+
navView?.setupWithNavController(navController)
51+
}
52+
3753
@RequiresApi(Build.VERSION_CODES.M)
3854
private fun checkDoNotDisturbPermission() {
3955
val notificationManager =
@@ -44,32 +60,37 @@ class MainActivity : AppCompatActivity() {
4460
}
4561
}
4662

47-
class PolicyAccessDialog : DialogFragment() {
48-
companion object {
49-
const val TAG = "PolicyAccessDialog"
50-
}
51-
52-
@RequiresApi(Build.VERSION_CODES.M)
53-
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog =
54-
AlertDialog.Builder(requireContext())
55-
.setMessage(getString(R.string.dialog_policy_access_title))
56-
.setCancelable(false)
57-
.setPositiveButton(getString(R.string.dialog_allow)) { _, _ ->
58-
startActivity(Intent(Settings.ACTION_NOTIFICATION_POLICY_ACCESS_SETTINGS))
59-
}
60-
.create()
61-
}
62-
63-
private fun handleEdgeToEdgeInsets() {
64-
ViewCompat.setOnApplyWindowInsetsListener(binding.root) { v, windowInsets ->
63+
private fun setupWindowInsets() {
64+
ViewCompat.setOnApplyWindowInsetsListener(binding.appBarLayout) { v, windowInsets ->
6565
val bars = windowInsets.getInsets(
6666
WindowInsetsCompat.Type.systemBars()
6767
or WindowInsetsCompat.Type.displayCutout()
6868
)
69+
v.setPadding(bars.left, bars.top, bars.right, 0)
70+
WindowInsetsCompat.CONSUMED
71+
}
6972

70-
v.setPadding(bars.left, bars.top, bars.right, bars.bottom)
73+
binding.bottomNavigation?.let { view ->
74+
ViewCompat.setOnApplyWindowInsetsListener(view) { v, windowInsets ->
75+
val bars = windowInsets.getInsets(
76+
WindowInsetsCompat.Type.systemBars()
77+
or WindowInsetsCompat.Type.displayCutout()
78+
)
79+
v.setPadding(bars.left, 0, bars.right, bars.bottom)
80+
WindowInsetsCompat.CONSUMED
81+
}
82+
}
7183

72-
WindowInsetsCompat.CONSUMED
84+
binding.navigationRail?.let { view ->
85+
ViewCompat.setOnApplyWindowInsetsListener(view) { v, windowInsets ->
86+
val bars = windowInsets.getInsets(
87+
WindowInsetsCompat.Type.systemBars()
88+
or WindowInsetsCompat.Type.displayCutout()
89+
)
90+
// Rail needs top and bottom padding usually, and left padding
91+
v.setPadding(bars.left, bars.top, 0, bars.bottom)
92+
WindowInsetsCompat.CONSUMED
93+
}
7394
}
7495
}
7596
}
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
package com.klee.volumelockr.ui
2+
3+
import android.app.AlertDialog
4+
import android.app.Dialog
5+
import android.content.Intent
6+
import android.os.Build
7+
import android.os.Bundle
8+
import android.provider.Settings
9+
import androidx.annotation.RequiresApi
10+
import androidx.fragment.app.DialogFragment
11+
import com.klee.volumelockr.R
12+
13+
class PolicyAccessDialog : DialogFragment() {
14+
companion object {
15+
const val TAG = "PolicyAccessDialog"
16+
}
17+
18+
@RequiresApi(Build.VERSION_CODES.M)
19+
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog =
20+
AlertDialog.Builder(requireContext())
21+
.setMessage(getString(R.string.dialog_policy_access_title))
22+
.setCancelable(false)
23+
.setPositiveButton(getString(R.string.dialog_allow)) { _, _ ->
24+
startActivity(Intent(Settings.ACTION_NOTIFICATION_POLICY_ACCESS_SETTINGS))
25+
}
26+
.create()
27+
}

0 commit comments

Comments
 (0)