Skip to content
Open
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
4 changes: 4 additions & 0 deletions android/app/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,10 @@ android {

implementation(libs.androidx.lifecycle.viewmodel.compose)

// Glance (App Widgets)
implementation(libs.androidx.glance.appwidget)
implementation(libs.androidx.glance.material3)

// Shuttle Core
implementation(project(":android:core"))

Expand Down
12 changes: 12 additions & 0 deletions android/app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,18 @@
android:resource="@xml/appwidget_info_42" />
</receiver>

<receiver
android:name=".ui.widgets.WidgetProvider43"
android:exported="true"
android:label="Shuttle 4x3">
<intent-filter>
<action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
</intent-filter>
<meta-data
android:name="android.appwidget.provider"
android:resource="@xml/appwidget_info_43" />
</receiver>

<!-- Crashlytics -->
<meta-data
android:name="firebase_crashlytics_collection_enabled"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ import android.os.Bundle
import android.view.View
import androidx.appcompat.widget.Toolbar
import androidx.navigation.fragment.findNavController
import androidx.preference.Preference
import androidx.preference.PreferenceFragmentCompat
import androidx.preference.SeekBarPreference
import com.simplecityapps.shuttle.R
Expand Down Expand Up @@ -34,11 +33,6 @@ class WidgetPreferenceFragment : PreferenceFragmentCompat() {
toolbar.setNavigationOnClickListener { findNavController().popBackStack() }
toolbar.setTitle(R.string.pref_category_title_widgets)

preferenceScreen.findPreference<Preference>("widget_dark_mode")?.setOnPreferenceClickListener {
widgetManager.updateAppWidgets(WidgetManager.UpdateReason.Unknown)
true
}

preferenceScreen.findPreference<SeekBarPreference>("widget_background_opacity")?.setOnPreferenceChangeListener { _, _ ->
widgetManager.updateAppWidgets(WidgetManager.UpdateReason.Unknown)
true
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
package com.simplecityapps.shuttle.ui.widgets

import android.content.Context
import android.content.Intent
import android.os.Build
import androidx.glance.GlanceId
import androidx.glance.action.ActionParameters
import androidx.glance.appwidget.action.ActionCallback
import com.simplecityapps.playback.PlaybackService

class PlayPauseAction : ActionCallback {
override suspend fun onAction(
context: Context,
glanceId: GlanceId,
parameters: ActionParameters
) {
val intent = Intent(context, PlaybackService::class.java).apply {
action = PlaybackService.ACTION_TOGGLE_PLAYBACK
}
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
context.startForegroundService(intent)
} else {
context.startService(intent)
}
}
}

class NextTrackAction : ActionCallback {
override suspend fun onAction(
context: Context,
glanceId: GlanceId,
parameters: ActionParameters
) {
val intent = Intent(context, PlaybackService::class.java).apply {
action = PlaybackService.ACTION_SKIP_NEXT
}
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
context.startForegroundService(intent)
} else {
context.startService(intent)
}
}
}

class PreviousTrackAction : ActionCallback {
override suspend fun onAction(
context: Context,
glanceId: GlanceId,
parameters: ActionParameters
) {
val intent = Intent(context, PlaybackService::class.java).apply {
action = PlaybackService.ACTION_SKIP_PREV
}
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
context.startForegroundService(intent)
} else {
context.startService(intent)
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package com.simplecityapps.shuttle.ui.widgets

import com.simplecityapps.playback.PlaybackManager
import com.simplecityapps.playback.queue.QueueManager
import com.simplecityapps.shuttle.persistence.GeneralPreferenceManager
import dagger.hilt.EntryPoint
import dagger.hilt.InstallIn
import dagger.hilt.components.SingletonComponent

@EntryPoint
@InstallIn(SingletonComponent::class)
interface WidgetEntryPoint {
fun playbackManager(): PlaybackManager
fun queueManager(): QueueManager
fun preferenceManager(): GeneralPreferenceManager
}
Original file line number Diff line number Diff line change
@@ -1,15 +1,18 @@
package com.simplecityapps.shuttle.ui.widgets

import android.appwidget.AppWidgetManager
import android.content.Context
import android.content.Intent
import androidx.glance.appwidget.updateAll
import com.simplecityapps.playback.PlaybackState
import com.simplecityapps.playback.PlaybackWatcher
import com.simplecityapps.playback.PlaybackWatcherCallback
import com.simplecityapps.playback.queue.QueueChangeCallback
import com.simplecityapps.playback.queue.QueueWatcher
import dagger.hilt.android.qualifiers.ApplicationContext
import javax.inject.Inject
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.SupervisorJob
import kotlinx.coroutines.launch

class WidgetManager
@Inject
Expand All @@ -19,6 +22,9 @@ constructor(
private val queueWatcher: QueueWatcher
) : PlaybackWatcherCallback,
QueueChangeCallback {

private val scope = CoroutineScope(SupervisorJob() + Dispatchers.Main)

enum class UpdateReason {
PlaystateChanged,
QueueChanged,
Expand All @@ -36,27 +42,19 @@ constructor(
queueWatcher.removeCallback(this)
}

fun updateAppWidgets(updateReason: UpdateReason) {
listOf(
Intent(context, WidgetProvider41::class.java),
Intent(context, WidgetProvider42::class.java)
).forEach { intent ->
context.sendBroadcast(
intent.apply {
action = AppWidgetManager.ACTION_APPWIDGET_UPDATE
val ids = AppWidgetManager.getInstance(context).getAppWidgetIds(component)
putExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS, ids)
putExtra(ARG_UPDATE_REASON, updateReason.ordinal)
}
)
fun updateAppWidgets(updateReason: UpdateReason = UpdateReason.Unknown) {
scope.launch {
// Update all widgets using Glance API
Widget41().updateAll(context)
Widget42().updateAll(context)
Widget43().updateAll(context)
}
}

// PlaybackWatcherCallback Implementation

override fun onPlaybackStateChanged(playbackState: PlaybackState) {
super.onPlaybackStateChanged(playbackState)

updateAppWidgets(UpdateReason.PlaystateChanged)
}

Expand All @@ -71,7 +69,6 @@ constructor(
newPosition: Int?
) {
super.onQueuePositionChanged(oldPosition, newPosition)

updateAppWidgets(UpdateReason.QueuePositionChanged)
}

Expand Down
Loading
Loading