Skip to content

Commit e2ef4a6

Browse files
author
hotmule
committed
Apps notifications permission request added, now playing service tuned
1 parent 2a89e17 commit e2ef4a6

File tree

28 files changed

+271
-209
lines changed

28 files changed

+271
-209
lines changed

app-android/src/main/AndroidManifest.xml

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -45,18 +45,18 @@
4545

4646
</activity>
4747

48-
<!--service
48+
<service
49+
android:name="ru.hotmule.lastik.feature.now.playing.NowPlayingService"
50+
android:label="Now playing"
4951
android:exported="false"
50-
android:name=".feature.now_playing.NowPlayingService"
51-
android:label="@string/now_playing"
5252
android:permission="android.permission.BIND_NOTIFICATION_LISTENER_SERVICE">
5353

5454
<intent-filter>
5555
<action android:name="android.service.notification.NotificationListenerService" />
5656
</intent-filter>
5757

58-
</service-->
58+
</service>
5959

6060
</application>
6161

62-
</manifest>
62+
</manifest>
Lines changed: 4 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,10 @@
11
package ru.hotmule.lastik
22

3-
import Lastik.R
43
import android.app.Application
5-
import android.app.NotificationChannel
6-
import android.app.NotificationManager
74
import android.content.Context
8-
import android.content.Intent
9-
import android.os.Build
10-
import org.kodein.di.*
11-
import ru.hotmule.lastik.feature.now_playing.NowPlayingService
5+
import org.kodein.di.DI
6+
import org.kodein.di.DIAware
7+
import org.kodein.di.bindInstance
128
import ru.hotmule.lastik.feature.root.rootComponentModule
139

1410
class App : Application(), DIAware {
@@ -19,22 +15,4 @@ class App : Application(), DIAware {
1915

2016
bindInstance<Context> { this@App }
2117
}
22-
23-
override fun onCreate() {
24-
super.onCreate()
25-
26-
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
27-
(getSystemService(NOTIFICATION_SERVICE) as NotificationManager).createNotificationChannel(
28-
NotificationChannel(
29-
getString(R.string.scrobbler_notification_channel_id),
30-
getString(R.string.scrobbler_notification_channel_name),
31-
NotificationManager.IMPORTANCE_DEFAULT
32-
)
33-
)
34-
}
35-
36-
startService(
37-
Intent(this, NowPlayingService::class.java)
38-
)
39-
}
40-
}
18+
}

data-sdk/build.gradle.kts

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,12 @@ plugins {
44

55
kotlin {
66
sourceSets {
7-
named("commonMain") {
7+
androidMain {
8+
dependencies {
9+
implementation(libs.androidx.core)
10+
}
11+
}
12+
commonMain {
813
dependencies {
914
implementation(projects.utils)
1015
implementation(libs.coil.core)
@@ -16,4 +21,4 @@ kotlin {
1621
}
1722
}
1823
}
19-
}
24+
}
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
package ru.hotmule.lastik.data.sdk.permission
2+
3+
import android.content.Context
4+
import android.content.Intent
5+
import android.provider.Settings
6+
import androidx.core.app.NotificationManagerCompat
7+
import org.kodein.di.DI
8+
import org.kodein.di.DIAware
9+
import org.kodein.di.instance
10+
11+
actual class PermissionManager actual constructor(override val di: DI) : DIAware {
12+
13+
private val context: Context by instance()
14+
15+
actual fun isNotificationsAccessGranted(): Boolean {
16+
return NotificationManagerCompat
17+
.getEnabledListenerPackages(context)
18+
.contains(context.packageName)
19+
}
20+
21+
actual fun requestNotificationsAccess() {
22+
context.startActivity(
23+
Intent(Settings.ACTION_NOTIFICATION_LISTENER_SETTINGS).apply {
24+
flags = Intent.FLAG_ACTIVITY_NEW_TASK
25+
}
26+
)
27+
}
28+
}
Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,16 @@
11
package ru.hotmule.lastik.data.sdk
22

3-
import org.kodein.di.*
3+
import org.kodein.di.DI
4+
import org.kodein.di.bindSingleton
5+
import org.kodein.di.instance
6+
import ru.hotmule.lastik.data.sdk.permission.PermissionManager
47
import ru.hotmule.lastik.data.sdk.packages.PackageManager
58
import ru.hotmule.lastik.data.sdk.prefs.PrefsStore
69
import ru.hotmule.lastik.data.sdk.prefs.SettingsFactory
710

811
val sdkDataModule = DI.Module("sdkData") {
12+
bindSingleton { PermissionManager(di) }
913
bindSingleton { PackageManager(di) }
1014
bindSingleton { SettingsFactory(di) }
1115
bindSingleton { PrefsStore(instance()) }
12-
}
16+
}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
package ru.hotmule.lastik.data.sdk.permission
2+
3+
import org.kodein.di.DI
4+
import org.kodein.di.DIAware
5+
6+
expect class PermissionManager(di: DI): DIAware {
7+
8+
fun isNotificationsAccessGranted(): Boolean
9+
10+
fun requestNotificationsAccess()
11+
}
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
package ru.hotmule.lastik.data.sdk.permission
2+
3+
import org.kodein.di.DI
4+
import org.kodein.di.DIAware
5+
6+
actual class PermissionManager actual constructor(override val di: DI) : DIAware {
7+
8+
actual fun isNotificationsAccessGranted(): Boolean = false
9+
10+
actual fun requestNotificationsAccess() {
11+
println("Notifications access requested")
12+
}
13+
}

feature-library/src/commonMain/kotlin/ru/hotmule/lastik/feature/library/LibraryComponent.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ package ru.hotmule.lastik.feature.library
22

33
import com.arkivanov.decompose.router.stack.ChildStack
44
import com.arkivanov.decompose.value.Value
5-
import ru.hotmule.lastik.feature.app.NowPlayingComponent
5+
import ru.hotmule.lastik.feature.now.playing.NowPlayingComponent
66
import ru.hotmule.lastik.feature.main.MainComponent
77
import ru.hotmule.lastik.feature.top.TopComponent
88
import ru.hotmule.lastik.feature.profile.ProfileComponent

feature-library/src/commonMain/kotlin/ru/hotmule/lastik/feature/library/LibraryComponentImpl.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ import org.kodein.di.DI
1212
import org.kodein.di.DIAware
1313
import org.kodein.di.factory
1414
import org.kodein.di.instance
15-
import ru.hotmule.lastik.feature.app.NowPlayingComponent
15+
import ru.hotmule.lastik.feature.now.playing.NowPlayingComponent
1616
import ru.hotmule.lastik.feature.library.LibraryComponent.Child
1717
import ru.hotmule.lastik.feature.main.MainComponent
1818
import ru.hotmule.lastik.feature.top.TopComponent

feature-library/src/commonMain/kotlin/ru/hotmule/lastik/feature/library/module.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ package ru.hotmule.lastik.feature.library
33
import com.arkivanov.decompose.ComponentContext
44
import org.kodein.di.DI
55
import org.kodein.di.bindFactory
6-
import ru.hotmule.lastik.feature.app.nowPlayingComponentModule
6+
import ru.hotmule.lastik.feature.now.playing.nowPlayingComponentModule
77
import ru.hotmule.lastik.feature.main.mainComponentModule
88
import ru.hotmule.lastik.feature.top.topComponentModule
99
import ru.hotmule.lastik.feature.profile.profileComponentModule
Lines changed: 3 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -1,35 +1,24 @@
1-
package ru.hotmule.lastik.feature.now_playing
1+
package ru.hotmule.lastik.feature.now.playing
22

3-
import Lastik.R
43
import android.content.ComponentName
54
import android.content.Intent
6-
import android.graphics.Bitmap
75
import android.media.MediaMetadata
86
import android.media.session.MediaController
97
import android.media.session.MediaSessionManager
108
import android.media.session.PlaybackState
119
import android.service.notification.NotificationListenerService
12-
import androidx.core.app.NotificationCompat
1310
import androidx.core.content.getSystemService
1411
import coil3.asImage
15-
import coil3.toBitmap
1612
import kotlinx.coroutines.CoroutineScope
1713
import kotlinx.coroutines.cancel
18-
import kotlinx.coroutines.launch
1914
import org.kodein.di.DI
2015
import org.kodein.di.DIAware
2116
import org.kodein.di.android.closestDI
2217
import org.kodein.di.instance
23-
import ru.hotmule.lastik.feature.app.NowPlayingComponent
24-
import ru.hotmule.lastik.feature.app.NowPlayingComponent.*
2518
import ru.hotmule.lastik.utils.AppCoroutineDispatcher
2619

2720
class NowPlayingService : NotificationListenerService(), DIAware {
2821

29-
companion object {
30-
private const val SCROBBLE_NOTIFICATION_ID = 1
31-
}
32-
3322
override val di: DI by closestDI()
3423

3524
private val nowPlayingComponent by instance<NowPlayingComponent>()
@@ -39,9 +28,7 @@ class NowPlayingService : NotificationListenerService(), DIAware {
3928

4029
override fun onCreate() {
4130
super.onCreate()
42-
4331
catchPlayers()
44-
provideNowPlaying()
4532
}
4633

4734
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int = START_STICKY
@@ -100,7 +87,7 @@ class NowPlayingService : NotificationListenerService(), DIAware {
10087
) {
10188
nowPlayingComponent.onTrackDetected(
10289
packageName = packageName,
103-
track = Track(
90+
track = NowPlayingComponent.Track(
10491
metadata?.getString(MediaMetadata.METADATA_KEY_ARTIST),
10592
metadata?.getString(MediaMetadata.METADATA_KEY_ALBUM),
10693
metadata?.getString(MediaMetadata.METADATA_KEY_TITLE),
@@ -110,33 +97,4 @@ class NowPlayingService : NotificationListenerService(), DIAware {
11097
)
11198
)
11299
}
113-
114-
private fun provideNowPlaying() {
115-
116-
serviceScope.launch {
117-
nowPlayingComponent.model.collect {
118-
119-
if (it.isPlaying) {
120-
startForeground(
121-
SCROBBLE_NOTIFICATION_ID,
122-
nowPlayingNotification(it.track, it.artist, it.art?.toBitmap())
123-
)
124-
} else {
125-
stopForeground(true)
126-
}
127-
}
128-
}
129-
}
130-
131-
private fun nowPlayingNotification(
132-
track: String,
133-
artist: String,
134-
art: Bitmap?,
135-
) = NotificationCompat.Builder(this, getString(R.string.scrobbler_notification_channel_id))
136-
.setContentTitle(track)
137-
.setContentText(artist)
138-
.setSmallIcon(R.drawable.ic_now_playing)
139-
.setLargeIcon(art)
140-
.setOngoing(true)
141-
.build()
142-
}
100+
}

feature-now-playing/src/androidMain/res/drawable/ic_now_playing.xml

Lines changed: 0 additions & 9 deletions
This file was deleted.

feature-now-playing/src/androidMain/res/values/strings.xml

Lines changed: 0 additions & 6 deletions
This file was deleted.

feature-now-playing/src/commonMain/kotlin/ru/hotmule/lastik/feature/app/store/NowPlayingStore.kt

Lines changed: 0 additions & 24 deletions
This file was deleted.

feature-now-playing/src/commonMain/kotlin/ru/hotmule/lastik/feature/app/NowPlayingComponent.kt renamed to feature-now-playing/src/commonMain/kotlin/ru/hotmule/lastik/feature/now/playing/NowPlayingComponent.kt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
package ru.hotmule.lastik.feature.app
1+
package ru.hotmule.lastik.feature.now.playing
22

33
import coil3.Image
44
import kotlinx.coroutines.flow.Flow
@@ -32,4 +32,4 @@ interface NowPlayingComponent {
3232
packageName: String,
3333
track: Track
3434
)
35-
}
35+
}
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,12 @@
1-
package ru.hotmule.lastik.feature.app
1+
package ru.hotmule.lastik.feature.now.playing
22

33
import com.arkivanov.mvikotlin.extensions.coroutines.states
44
import kotlinx.coroutines.flow.Flow
55
import kotlinx.coroutines.flow.map
66
import org.kodein.di.DirectDI
77
import org.kodein.di.instance
8-
import ru.hotmule.lastik.feature.app.NowPlayingComponent.*
9-
import ru.hotmule.lastik.feature.app.store.NowPlayingStore.*
10-
import ru.hotmule.lastik.feature.app.store.NowPlayingStoreFactory
8+
import ru.hotmule.lastik.feature.now.playing.store.NowPlayingStore
9+
import ru.hotmule.lastik.feature.now.playing.store.NowPlayingStoreFactory
1110

1211
internal class NowPlayingComponentImpl(directDi: DirectDI) : NowPlayingComponent {
1312

@@ -18,8 +17,8 @@ internal class NowPlayingComponentImpl(directDi: DirectDI) : NowPlayingComponent
1817
db = directDi.instance()
1918
).create()
2019

21-
override val model: Flow<Model> = store.states.map {
22-
Model(
20+
override val model: Flow<NowPlayingComponent.Model> = store.states.map {
21+
NowPlayingComponent.Model(
2322
isPlaying = it.isPlaying,
2423
track = it.track?.name ?: "UNKNOWN",
2524
artist = it.track?.albumArtist ?: it.track?.artist ?: "UNKNOWN",
@@ -28,10 +27,10 @@ internal class NowPlayingComponentImpl(directDi: DirectDI) : NowPlayingComponent
2827
}
2928

3029
override fun onPlayStateChanged(packageName: String, isPlaying: Boolean) {
31-
store.accept(Intent.CheckPlayState(packageName, isPlaying))
30+
store.accept(NowPlayingStore.Intent.CheckPlayState(packageName, isPlaying))
3231
}
3332

34-
override fun onTrackDetected(packageName: String, track: Track) {
35-
store.accept(Intent.CheckDetectedTrack(packageName, track))
33+
override fun onTrackDetected(packageName: String, track: NowPlayingComponent.Track) {
34+
store.accept(NowPlayingStore.Intent.CheckDetectedTrack(packageName, track))
3635
}
37-
}
36+
}
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
1-
package ru.hotmule.lastik.feature.app
1+
package ru.hotmule.lastik.feature.now.playing
22

33
import org.kodein.di.DI
44
import org.kodein.di.bindSingleton
55

66
val nowPlayingComponentModule = DI.Module("nowPlayingComponent") {
77

88
bindSingleton { NowPlayingComponentImpl(directDI) }
9-
}
9+
}

0 commit comments

Comments
 (0)