Add Smartspacer integration
This commit is contained in:
@@ -118,6 +118,10 @@ dependencies {
|
||||
implementation(libs.coil.core)
|
||||
implementation(libs.coil.compose)
|
||||
|
||||
implementation(libs.smartspacer) {
|
||||
exclude(group = "com.github.skydoves", module = "balloon")
|
||||
}
|
||||
|
||||
implementation(project(":libs:material-color-utilities"))
|
||||
|
||||
implementation(project(":core:base"))
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
-keep class com.kieronquinn.app.smartspacer.sdk.** { *; }
|
||||
@@ -1,9 +1,14 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools">
|
||||
|
||||
<uses-permission android:name="android.permission.INTERNET" />
|
||||
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
|
||||
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
|
||||
|
||||
<uses-permission android:name="com.kieronquinn.app.smartspacer.permission.ACCESS_SMARTSPACER"/>
|
||||
<uses-sdk tools:overrideLibrary="com.kieronquinn.app.smartspacer.sdk,com.kieronquinn.app.smartspacer.sdk.client" />
|
||||
|
||||
<application>
|
||||
|
||||
<activity
|
||||
|
||||
@@ -8,6 +8,7 @@ import androidx.compose.foundation.layout.FlowRow
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.material3.CardDefaults
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
@@ -46,7 +47,7 @@ fun Banner(
|
||||
verticalAlignment = Alignment.CenterVertically
|
||||
) {
|
||||
Icon(
|
||||
modifier = Modifier.padding(end = 16.dp),
|
||||
modifier = Modifier.padding(end = 16.dp).size(24.dp),
|
||||
painter = painterResource(icon),
|
||||
contentDescription = null
|
||||
)
|
||||
|
||||
@@ -16,6 +16,7 @@ import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.foundation.layout.width
|
||||
import androidx.compose.foundation.rememberScrollState
|
||||
import androidx.compose.foundation.verticalScroll
|
||||
import androidx.compose.material3.Button
|
||||
import androidx.compose.material3.ButtonGroupDefaults
|
||||
import androidx.compose.material3.DropdownMenuGroup
|
||||
import androidx.compose.material3.DropdownMenuItem
|
||||
@@ -55,6 +56,7 @@ import de.mm20.launcher2.preferences.ClockWidgetStyle
|
||||
import de.mm20.launcher2.preferences.ui.ClockWidgetSettings
|
||||
import de.mm20.launcher2.ui.R
|
||||
import de.mm20.launcher2.ui.base.LocalTime
|
||||
import de.mm20.launcher2.ui.component.Banner
|
||||
import de.mm20.launcher2.ui.component.BottomSheetDialog
|
||||
import de.mm20.launcher2.ui.component.preferences.Preference
|
||||
import de.mm20.launcher2.ui.component.preferences.SwitchPreference
|
||||
@@ -356,6 +358,7 @@ fun ConfigureClockWidgetSheet(
|
||||
val monospaced by viewModel.monospaced.collectAsState()
|
||||
val useAccentColor by viewModel.useThemeColor.collectAsState()
|
||||
val parts by viewModel.parts.collectAsState()
|
||||
val smartspacer by viewModel.useSmartspacer.collectAsState()
|
||||
|
||||
BottomSheetDialog(onDismissRequest = onDismiss) {
|
||||
Column(
|
||||
@@ -618,42 +621,60 @@ fun ConfigureClockWidgetSheet(
|
||||
Column(
|
||||
modifier = Modifier.fillMaxWidth()
|
||||
) {
|
||||
SwitchPreference(
|
||||
title = stringResource(R.string.preference_clockwidget_date_part),
|
||||
summary = stringResource(R.string.preference_clockwidget_date_part_summary),
|
||||
icon = R.drawable.today_24px,
|
||||
value = parts?.date == true,
|
||||
onValueChanged = {
|
||||
viewModel.setDatePart(it)
|
||||
}
|
||||
)
|
||||
SwitchPreference(
|
||||
title = stringResource(R.string.preference_clockwidget_music_part),
|
||||
summary = stringResource(R.string.preference_clockwidget_music_part_summary),
|
||||
icon = R.drawable.music_note_24px,
|
||||
value = parts?.music == true,
|
||||
onValueChanged = {
|
||||
viewModel.setMusicPart(it)
|
||||
}
|
||||
)
|
||||
SwitchPreference(
|
||||
title = stringResource(R.string.preference_clockwidget_alarm_part),
|
||||
summary = stringResource(R.string.preference_clockwidget_alarm_part_summary),
|
||||
icon = R.drawable.alarm_24px,
|
||||
value = parts?.alarm == true,
|
||||
onValueChanged = {
|
||||
viewModel.setAlarmPart(it)
|
||||
}
|
||||
)
|
||||
SwitchPreference(
|
||||
title = stringResource(R.string.preference_clockwidget_battery_part),
|
||||
summary = stringResource(R.string.preference_clockwidget_battery_part_summary),
|
||||
icon = R.drawable.battery_full_24px,
|
||||
value = parts?.battery == true,
|
||||
onValueChanged = {
|
||||
viewModel.setBatteryPart(it)
|
||||
}
|
||||
)
|
||||
if (smartspacer == true) {
|
||||
Banner(
|
||||
modifier = Modifier.padding(16.dp),
|
||||
text = stringResource(R.string.preference_clockwidget_smartspacer),
|
||||
icon = R.drawable.info_24px,
|
||||
primaryAction = {
|
||||
Button(
|
||||
onClick = {
|
||||
viewModel.disableSmartspacer()
|
||||
}
|
||||
) {
|
||||
Text(stringResource(R.string.turn_off))
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
if (smartspacer == false) {
|
||||
SwitchPreference(
|
||||
title = stringResource(R.string.preference_clockwidget_date_part),
|
||||
summary = stringResource(R.string.preference_clockwidget_date_part_summary),
|
||||
icon = R.drawable.today_24px,
|
||||
value = parts?.date == true,
|
||||
onValueChanged = {
|
||||
viewModel.setDatePart(it)
|
||||
}
|
||||
)
|
||||
SwitchPreference(
|
||||
title = stringResource(R.string.preference_clockwidget_music_part),
|
||||
summary = stringResource(R.string.preference_clockwidget_music_part_summary),
|
||||
icon = R.drawable.music_note_24px,
|
||||
value = parts?.music == true,
|
||||
onValueChanged = {
|
||||
viewModel.setMusicPart(it)
|
||||
}
|
||||
)
|
||||
SwitchPreference(
|
||||
title = stringResource(R.string.preference_clockwidget_alarm_part),
|
||||
summary = stringResource(R.string.preference_clockwidget_alarm_part_summary),
|
||||
icon = R.drawable.alarm_24px,
|
||||
value = parts?.alarm == true,
|
||||
onValueChanged = {
|
||||
viewModel.setAlarmPart(it)
|
||||
}
|
||||
)
|
||||
SwitchPreference(
|
||||
title = stringResource(R.string.preference_clockwidget_battery_part),
|
||||
summary = stringResource(R.string.preference_clockwidget_battery_part_summary),
|
||||
icon = R.drawable.battery_full_24px,
|
||||
value = parts?.battery == true,
|
||||
onValueChanged = {
|
||||
viewModel.setBatteryPart(it)
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@ import android.content.Intent
|
||||
import android.provider.AlarmClock
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import de.mm20.launcher2.ktx.isAtLeastApiLevel
|
||||
import de.mm20.launcher2.ktx.tryStartActivity
|
||||
import de.mm20.launcher2.preferences.ui.ClockWidgetSettings
|
||||
import de.mm20.launcher2.ui.launcher.widgets.clock.parts.AlarmPartProvider
|
||||
@@ -13,6 +14,7 @@ import de.mm20.launcher2.ui.launcher.widgets.clock.parts.DatePartProvider
|
||||
import de.mm20.launcher2.ui.launcher.widgets.clock.parts.FavoritesPartProvider
|
||||
import de.mm20.launcher2.ui.launcher.widgets.clock.parts.MusicPartProvider
|
||||
import de.mm20.launcher2.ui.launcher.widgets.clock.parts.PartProvider
|
||||
import de.mm20.launcher2.ui.launcher.widgets.clock.parts.SmartspacerPartProvider
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.SharingStarted
|
||||
import kotlinx.coroutines.flow.channelFlow
|
||||
@@ -26,12 +28,18 @@ import org.koin.core.component.inject
|
||||
class ClockWidgetVM : ViewModel(), KoinComponent {
|
||||
private val settings: ClockWidgetSettings by inject()
|
||||
|
||||
private val partProviders = settings.parts.map {
|
||||
private val partProviders = settings.parts.combine(settings.useSmartspacer) { p, s ->
|
||||
p to s
|
||||
}.map { (parts, smartspacer) ->
|
||||
if (smartspacer && isAtLeastApiLevel(29)) {
|
||||
return@map listOf(SmartspacerPartProvider())
|
||||
}
|
||||
|
||||
val providers = mutableListOf<PartProvider>()
|
||||
if (it.date) providers += DatePartProvider()
|
||||
if (it.music) providers += MusicPartProvider()
|
||||
if (it.battery) providers += BatteryPartProvider()
|
||||
if (it.alarm) providers += AlarmPartProvider()
|
||||
if (parts.date) providers += DatePartProvider()
|
||||
if (parts.music) providers += MusicPartProvider()
|
||||
if (parts.battery) providers += BatteryPartProvider()
|
||||
if (parts.alarm) providers += AlarmPartProvider()
|
||||
providers
|
||||
}.stateIn(viewModelScope, SharingStarted.WhileSubscribed(), emptyList())
|
||||
|
||||
|
||||
@@ -0,0 +1,255 @@
|
||||
package de.mm20.launcher2.ui.launcher.widgets.clock.parts
|
||||
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import androidx.annotation.RequiresApi
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.material3.DropdownMenuGroup
|
||||
import androidx.compose.material3.DropdownMenuItem
|
||||
import androidx.compose.material3.DropdownMenuPopup
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.LocalContentColor
|
||||
import androidx.compose.material3.MenuDefaults
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.toArgb
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.viewinterop.AndroidView
|
||||
import com.kieronquinn.app.smartspacer.sdk.client.SmartspacerClient
|
||||
import com.kieronquinn.app.smartspacer.sdk.client.views.BcSmartspaceView
|
||||
import com.kieronquinn.app.smartspacer.sdk.client.views.popup.Popup
|
||||
import com.kieronquinn.app.smartspacer.sdk.client.views.popup.PopupFactory
|
||||
import com.kieronquinn.app.smartspacer.sdk.model.SmartspaceTarget
|
||||
import de.mm20.launcher2.ui.R
|
||||
import kotlinx.coroutines.flow.flow
|
||||
|
||||
@RequiresApi(29)
|
||||
class SmartspacerPartProvider : PartProvider {
|
||||
override fun getRanking(context: Context) = flow {
|
||||
emit(Int.MAX_VALUE)
|
||||
}
|
||||
|
||||
@Composable
|
||||
override fun Component(compactLayout: Boolean) {
|
||||
|
||||
val contentColor = LocalContentColor.current
|
||||
|
||||
val popupFactory = remember {
|
||||
ComposePopupFactory()
|
||||
}
|
||||
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth(),
|
||||
contentAlignment = Alignment.Center,
|
||||
) {
|
||||
Box(modifier = Modifier.align(Alignment.TopCenter)) {
|
||||
popupFactory.Popup()
|
||||
}
|
||||
AndroidView(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(start = 12.dp, end = if (compactLayout) 0.dp else 12.dp)
|
||||
.height(104.dp),
|
||||
factory = {
|
||||
val view = LayoutInflater
|
||||
.from(it)
|
||||
.inflate(R.layout.smartspacer, null, false) as BcSmartspaceView
|
||||
|
||||
view.setApplyShadowIfRequired(false)
|
||||
view.setTintColour(contentColor.toArgb())
|
||||
view.popupFactory = popupFactory
|
||||
|
||||
view
|
||||
},
|
||||
update = { view ->
|
||||
view.setTintColour(contentColor.toArgb())
|
||||
},
|
||||
onRelease = {
|
||||
SmartspacerClient.close()
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private data class PopupState(
|
||||
val target: SmartspaceTarget,
|
||||
val launchIntent: (Intent?) -> Unit,
|
||||
val dismissAction: ((SmartspaceTarget) -> Unit)?,
|
||||
val aboutIntent: Intent?,
|
||||
val feedbackIntent: Intent?,
|
||||
val settingsIntent: Intent?
|
||||
)
|
||||
|
||||
private class ComposePopupFactory : PopupFactory {
|
||||
var state by mutableStateOf<PopupState?>(null)
|
||||
|
||||
override fun createPopup(
|
||||
context: Context,
|
||||
anchorView: View,
|
||||
target: SmartspaceTarget,
|
||||
backgroundColor: Int,
|
||||
textColour: Int,
|
||||
launchIntent: (Intent?) -> Unit,
|
||||
dismissAction: ((SmartspaceTarget) -> Unit)?,
|
||||
aboutIntent: Intent?,
|
||||
feedbackIntent: Intent?,
|
||||
settingsIntent: Intent?
|
||||
): Popup {
|
||||
state = PopupState(
|
||||
target = target,
|
||||
launchIntent = launchIntent,
|
||||
dismissAction = dismissAction,
|
||||
aboutIntent = aboutIntent,
|
||||
feedbackIntent = feedbackIntent,
|
||||
settingsIntent = settingsIntent
|
||||
)
|
||||
return object : Popup {
|
||||
override fun dismiss() {
|
||||
state = null
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun Popup() {
|
||||
val state = state
|
||||
if (state != null) {
|
||||
DropdownMenuPopup(
|
||||
expanded = true,
|
||||
onDismissRequest = {
|
||||
this.state = null
|
||||
}
|
||||
) {
|
||||
|
||||
val itemCount = (if (state.settingsIntent != null) 1 else 0) +
|
||||
(if (state.feedbackIntent != null) 1 else 0) +
|
||||
(if (state.aboutIntent != null) 1 else 0) +
|
||||
(if (state.dismissAction != null) 1 else 0)
|
||||
|
||||
var item = 0
|
||||
|
||||
|
||||
DropdownMenuGroup(
|
||||
shapes = MenuDefaults.groupShapes(),
|
||||
) {
|
||||
if (state.settingsIntent != null) {
|
||||
DropdownMenuItem(
|
||||
shape = when {
|
||||
item == 0 && itemCount - 1 == item -> MenuDefaults.standaloneItemShape
|
||||
item == 0 -> MenuDefaults.leadingItemShape
|
||||
item == itemCount - 1 -> MenuDefaults.trailingItemShape
|
||||
else -> MenuDefaults.middleItemShape
|
||||
},
|
||||
text = {
|
||||
Text(stringResource(R.string.smartspace_long_press_popup_settings))
|
||||
},
|
||||
leadingIcon = {
|
||||
Icon(
|
||||
painterResource(R.drawable.settings_24px),
|
||||
null,
|
||||
)
|
||||
},
|
||||
onClick = {
|
||||
state.launchIntent(state.settingsIntent)
|
||||
}
|
||||
)
|
||||
|
||||
item++
|
||||
}
|
||||
|
||||
if (state.feedbackIntent != null) {
|
||||
DropdownMenuItem(
|
||||
shape = when {
|
||||
item == 0 && itemCount - 1 == item -> MenuDefaults.standaloneItemShape
|
||||
item == 0 -> MenuDefaults.leadingItemShape
|
||||
item == itemCount - 1 -> MenuDefaults.trailingItemShape
|
||||
else -> MenuDefaults.middleItemShape
|
||||
},
|
||||
text = {
|
||||
Text(stringResource(R.string.smartspace_long_press_popup_feedback))
|
||||
},
|
||||
leadingIcon = {
|
||||
Icon(
|
||||
painterResource(R.drawable.feedback_24px),
|
||||
null,
|
||||
)
|
||||
},
|
||||
onClick = {
|
||||
state.launchIntent(state.feedbackIntent)
|
||||
}
|
||||
)
|
||||
|
||||
item++
|
||||
}
|
||||
|
||||
if (state.aboutIntent != null) {
|
||||
DropdownMenuItem(
|
||||
shape = when {
|
||||
item == 0 && itemCount - 1 == item -> MenuDefaults.standaloneItemShape
|
||||
item == 0 -> MenuDefaults.leadingItemShape
|
||||
item == itemCount - 1 -> MenuDefaults.trailingItemShape
|
||||
else -> MenuDefaults.middleItemShape
|
||||
},
|
||||
text = {
|
||||
Text(stringResource(R.string.smartspace_long_press_popup_about))
|
||||
},
|
||||
leadingIcon = {
|
||||
Icon(
|
||||
painterResource(R.drawable.info_24px),
|
||||
null,
|
||||
)
|
||||
},
|
||||
onClick = {
|
||||
state.launchIntent(state.aboutIntent)
|
||||
}
|
||||
)
|
||||
|
||||
item++
|
||||
}
|
||||
|
||||
if (state.dismissAction != null) {
|
||||
DropdownMenuItem(
|
||||
shape = when {
|
||||
item == 0 && itemCount - 1 == item -> MenuDefaults.standaloneItemShape
|
||||
item == 0 -> MenuDefaults.leadingItemShape
|
||||
item == itemCount - 1 -> MenuDefaults.trailingItemShape
|
||||
else -> MenuDefaults.middleItemShape
|
||||
},
|
||||
text = {
|
||||
Text(stringResource(R.string.smartspace_long_press_popup_dismiss))
|
||||
},
|
||||
leadingIcon = {
|
||||
Icon(
|
||||
painterResource(R.drawable.close_24px),
|
||||
null,
|
||||
)
|
||||
},
|
||||
onClick = {
|
||||
state.dismissAction(state.target)
|
||||
}
|
||||
)
|
||||
|
||||
item++
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -114,6 +114,8 @@ import de.mm20.launcher2.ui.settings.shapes.ShapeSchemeSettingsRoute
|
||||
import de.mm20.launcher2.ui.settings.shapes.ShapeSchemeSettingsScreen
|
||||
import de.mm20.launcher2.ui.settings.shapes.ShapeSchemesSettingsRoute
|
||||
import de.mm20.launcher2.ui.settings.shapes.ShapeSchemesSettingsScreen
|
||||
import de.mm20.launcher2.ui.settings.smartspacer.SmartspacerSettingsRoute
|
||||
import de.mm20.launcher2.ui.settings.smartspacer.SmartspacerSettingsScreen
|
||||
import de.mm20.launcher2.ui.settings.tags.TagsSettingsRoute
|
||||
import de.mm20.launcher2.ui.settings.tags.TagsSettingsScreen
|
||||
import de.mm20.launcher2.ui.settings.tasks.TasksIntegrationSettingsRoute
|
||||
@@ -300,6 +302,9 @@ class SettingsActivity : BaseActivity() {
|
||||
entry<AppSearchSettingsRoute> {
|
||||
AppSearchSettingsScreen()
|
||||
}
|
||||
entry<SmartspacerSettingsRoute> {
|
||||
SmartspacerSettingsScreen()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -106,4 +106,12 @@ class ClockWidgetSettingsScreenVM : ViewModel(), KoinComponent {
|
||||
fun setAlignment(alignment: ClockWidgetAlignment) {
|
||||
settings.setAlignment(alignment)
|
||||
}
|
||||
|
||||
val useSmartspacer = settings.useSmartspacer
|
||||
.stateIn(viewModelScope, SharingStarted.WhileSubscribed(), null)
|
||||
|
||||
fun disableSmartspacer() {
|
||||
settings.setUseSmartspacer(false)
|
||||
}
|
||||
|
||||
}
|
||||
@@ -4,6 +4,7 @@ import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.lifecycle.viewmodel.compose.viewModel
|
||||
import androidx.navigation3.runtime.NavKey
|
||||
import de.mm20.launcher2.ktx.isAtLeastApiLevel
|
||||
import de.mm20.launcher2.ui.R
|
||||
import de.mm20.launcher2.ui.component.preferences.Preference
|
||||
import de.mm20.launcher2.ui.component.preferences.PreferenceCategory
|
||||
@@ -13,13 +14,14 @@ import de.mm20.launcher2.ui.settings.breezyweather.BreezyWeatherSettingsRoute
|
||||
import de.mm20.launcher2.ui.settings.media.MediaIntegrationSettingsRoute
|
||||
import de.mm20.launcher2.ui.settings.nextcloud.NextcloudSettingsRoute
|
||||
import de.mm20.launcher2.ui.settings.owncloud.OwncloudSettingsRoute
|
||||
import de.mm20.launcher2.ui.settings.smartspacer.SmartspacerSettingsRoute
|
||||
import de.mm20.launcher2.ui.settings.tasks.TasksIntegrationSettingsRoute
|
||||
import de.mm20.launcher2.ui.settings.weather.WeatherIntegrationSettingsRoute
|
||||
import de.mm20.launcher2.ui.settings.wikipedia.WikipediaSettingsRoute
|
||||
import kotlinx.serialization.Serializable
|
||||
|
||||
@Serializable
|
||||
data object IntegrationsSettingsRoute: NavKey
|
||||
data object IntegrationsSettingsRoute : NavKey
|
||||
|
||||
@Composable
|
||||
fun IntegrationsSettingsScreen() {
|
||||
@@ -68,6 +70,10 @@ fun IntegrationsSettingsScreen() {
|
||||
backStack.add(WikipediaSettingsRoute)
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
item {
|
||||
PreferenceCategory {
|
||||
Preference(
|
||||
title = stringResource(R.string.preference_tasks_integration),
|
||||
icon = R.drawable.check_24px_sharp,
|
||||
@@ -82,6 +88,15 @@ fun IntegrationsSettingsScreen() {
|
||||
backStack.add(BreezyWeatherSettingsRoute)
|
||||
}
|
||||
)
|
||||
if (isAtLeastApiLevel(29)) {
|
||||
Preference(
|
||||
title = stringResource(R.string.preference_smartspacer_integration),
|
||||
icon = R.drawable.smartspacer,
|
||||
onClick = {
|
||||
backStack.add(SmartspacerSettingsRoute)
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,88 @@
|
||||
package de.mm20.launcher2.ui.settings.smartspacer
|
||||
|
||||
import androidx.activity.compose.LocalActivity
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.material3.Button
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
import androidx.lifecycle.viewmodel.compose.viewModel
|
||||
import androidx.navigation3.runtime.NavKey
|
||||
import de.mm20.launcher2.ui.R
|
||||
import de.mm20.launcher2.ui.component.Banner
|
||||
import de.mm20.launcher2.ui.component.preferences.Preference
|
||||
import de.mm20.launcher2.ui.component.preferences.PreferenceCategory
|
||||
import de.mm20.launcher2.ui.component.preferences.PreferenceScreen
|
||||
import de.mm20.launcher2.ui.component.preferences.SwitchPreference
|
||||
import kotlinx.serialization.Serializable
|
||||
|
||||
@Serializable
|
||||
data object SmartspacerSettingsRoute : NavKey
|
||||
|
||||
@Composable
|
||||
fun SmartspacerSettingsScreen() {
|
||||
val viewModel = viewModel<SmartspacerSettingsScreenVM>()
|
||||
val activity = LocalActivity.current
|
||||
|
||||
val isSmartspacerAppInstalled by viewModel.isSmartspacerAppInstalled.collectAsStateWithLifecycle(
|
||||
null
|
||||
)
|
||||
val isSmartspacerEnabled by viewModel.isSmartspacerEnabled.collectAsStateWithLifecycle(null)
|
||||
|
||||
PreferenceScreen(
|
||||
title = stringResource(R.string.preference_smartspacer_integration)
|
||||
) {
|
||||
if (isSmartspacerAppInstalled == false) {
|
||||
item {
|
||||
PreferenceCategory {
|
||||
Banner(
|
||||
text = stringResource(
|
||||
R.string.preference_smartspacer_integration_description,
|
||||
stringResource(R.string.app_name)
|
||||
),
|
||||
icon = R.drawable.info_24px,
|
||||
modifier = Modifier
|
||||
.background(MaterialTheme.colorScheme.surface)
|
||||
.padding(16.dp),
|
||||
primaryAction = {
|
||||
Button(onClick = {
|
||||
viewModel.downloadSmartspacerApp(activity as AppCompatActivity)
|
||||
}) {
|
||||
Text(stringResource(R.string.action_install))
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
if (isSmartspacerAppInstalled == true) {
|
||||
item {
|
||||
PreferenceCategory {
|
||||
SwitchPreference(
|
||||
icon = R.drawable.smartspacer,
|
||||
title = stringResource(R.string.preference_smartspacer_enable),
|
||||
value = isSmartspacerEnabled == true,
|
||||
onValueChanged = {
|
||||
viewModel.setSmartspacerEnabled(it)
|
||||
}
|
||||
)
|
||||
|
||||
Preference(
|
||||
title = stringResource(R.string.preference_launch_smartspacer_app),
|
||||
icon = R.drawable.open_in_new_24px,
|
||||
onClick = {
|
||||
viewModel.launchSmartspacerApp(activity as AppCompatActivity)
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
package de.mm20.launcher2.ui.settings.smartspacer
|
||||
|
||||
import android.content.Intent
|
||||
import android.os.Process
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.core.net.toUri
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import de.mm20.launcher2.applications.AppRepository
|
||||
import de.mm20.launcher2.preferences.ui.ClockWidgetSettings
|
||||
import kotlinx.coroutines.flow.SharingStarted
|
||||
import kotlinx.coroutines.flow.first
|
||||
import kotlinx.coroutines.flow.map
|
||||
import kotlinx.coroutines.flow.shareIn
|
||||
import kotlinx.coroutines.launch
|
||||
import org.koin.core.component.KoinComponent
|
||||
import org.koin.core.component.inject
|
||||
import kotlin.getValue
|
||||
|
||||
class SmartspacerSettingsScreenVM: ViewModel(), KoinComponent {
|
||||
private val appRepository: AppRepository by inject()
|
||||
private val clockWidgetSettings: ClockWidgetSettings by inject()
|
||||
|
||||
val smartspacerApp = appRepository.findOne("com.kieronquinn.app.smartspacer", Process.myUserHandle())
|
||||
.shareIn(viewModelScope, SharingStarted.WhileSubscribed(), 1)
|
||||
|
||||
val isSmartspacerAppInstalled = smartspacerApp
|
||||
.map { it != null }
|
||||
.shareIn(viewModelScope, SharingStarted.WhileSubscribed(), 1)
|
||||
|
||||
fun downloadSmartspacerApp(activity: AppCompatActivity) {
|
||||
activity.startActivity(
|
||||
Intent(Intent.ACTION_VIEW).apply {
|
||||
data = "https://github.com/KieronQuinn/Smartspacer".toUri()
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
fun launchSmartspacerApp(activity: AppCompatActivity) {
|
||||
viewModelScope.launch {
|
||||
smartspacerApp.first()?.launch(activity, null)
|
||||
}
|
||||
}
|
||||
|
||||
val isSmartspacerEnabled = clockWidgetSettings.useSmartspacer
|
||||
.shareIn(viewModelScope, SharingStarted.WhileSubscribed(), 1)
|
||||
|
||||
fun setSmartspacerEnabled(enabled: Boolean) {
|
||||
clockWidgetSettings.setUseSmartspacer(enabled)
|
||||
}
|
||||
|
||||
}
|
||||
23
app/ui/src/main/res/layout-v29/smartspacer.xml
Normal file
23
app/ui/src/main/res/layout-v29/smartspacer.xml
Normal file
@@ -0,0 +1,23 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<com.kieronquinn.app.smartspacer.sdk.client.views.BcSmartspaceView xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:id="@+id/bc_smartspace_view"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center_vertical"
|
||||
android:layout_marginLeft="12px">
|
||||
|
||||
<androidx.viewpager.widget.ViewPager
|
||||
android:id="@+id/smartspace_card_pager"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center" />
|
||||
|
||||
<com.kieronquinn.app.smartspacer.sdk.client.views.PageIndicator
|
||||
android:id="@+id/smartspace_page_indicator"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="bottom|left|center_vertical|center_horizontal|center|start"
|
||||
android:importantForAccessibility="no"
|
||||
android:visibility="visible" />
|
||||
|
||||
</com.kieronquinn.app.smartspacer.sdk.client.views.BcSmartspaceView>
|
||||
Reference in New Issue
Block a user