diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index 958c9b09..ddaad4b2 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -97,6 +97,16 @@
android:resource="@xml/file_provider_paths" />
+
+
+
+
+
+
+
+ val adapterItems = state.profiles.map { profile ->
+ WidgetProfileSelectionVH.Item(
+ profile = profile,
+ isSelected = profile.id == state.selectedProfile,
+ onProfileClick = { vm.selectProfile(it.id) }
+ )
+ }
+ profileAdapter.asyncDiffer.submitUpdate(adapterItems)
+
+ ui.proRequiredCaption.isVisible = !state.isPro
+
+ if (state.isPro) {
+ ui.confirmButton.text = getString(android.R.string.ok)
+ ui.confirmButton.isEnabled = state.canConfirm
+ } else {
+ ui.confirmButton.text = getString(R.string.general_upgrade_action)
+ ui.confirmButton.isEnabled = true
+ }
+ }
+ }
+
+ private fun confirmSelection() {
+ vm.confirmSelection()
+
+ val resultValue = Intent().putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, widgetId)
+ setResult(RESULT_OK, resultValue)
+
+ val appWidgetManager = AppWidgetManager.getInstance(this@WidgetConfigurationActivity)
+
+ WidgetProvider.updateWidget(
+ context = this@WidgetConfigurationActivity,
+ appWidgetManager = appWidgetManager,
+ widgetId = widgetId
+ )
+
+ finish()
+
+ }
+
+ companion object {
+ private val TAG = logTag("Widget", "ConfigurationActivity")
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/eu/darken/capod/main/ui/widget/WidgetConfigurationViewModel.kt b/app/src/main/java/eu/darken/capod/main/ui/widget/WidgetConfigurationViewModel.kt
new file mode 100644
index 00000000..e251a23f
--- /dev/null
+++ b/app/src/main/java/eu/darken/capod/main/ui/widget/WidgetConfigurationViewModel.kt
@@ -0,0 +1,79 @@
+package eu.darken.capod.main.ui.widget
+
+import android.appwidget.AppWidgetManager
+import androidx.lifecycle.SavedStateHandle
+import dagger.hilt.android.lifecycle.HiltViewModel
+import eu.darken.capod.common.coroutine.DispatcherProvider
+import eu.darken.capod.common.debug.logging.log
+import eu.darken.capod.common.debug.logging.logTag
+import eu.darken.capod.common.flow.combine
+import eu.darken.capod.common.uix.ViewModel3
+import eu.darken.capod.common.upgrade.UpgradeRepo
+import eu.darken.capod.profiles.core.DeviceProfile
+import eu.darken.capod.profiles.core.DeviceProfilesRepo
+import eu.darken.capod.profiles.core.ProfileId
+import kotlinx.coroutines.flow.MutableStateFlow
+import javax.inject.Inject
+
+@HiltViewModel
+class WidgetConfigurationViewModel @Inject constructor(
+ private val savedStateHandle: SavedStateHandle,
+ dispatcherProvider: DispatcherProvider,
+ private val deviceProfilesRepo: DeviceProfilesRepo,
+ private val widgetSettings: WidgetSettings,
+ private val upgradeRepo: UpgradeRepo,
+) : ViewModel3(dispatcherProvider) {
+
+ val widgetId: Int
+ get() = savedStateHandle.get(AppWidgetManager.EXTRA_APPWIDGET_ID) ?: AppWidgetManager.INVALID_APPWIDGET_ID
+
+ init {
+ log(TAG) { "ViewModel init(widgetId=$widgetId)" }
+
+ if (widgetId == AppWidgetManager.INVALID_APPWIDGET_ID) {
+ log(TAG) { "Invalid widget ID" }
+ }
+ }
+
+ private val selectedProfile = MutableStateFlow(widgetSettings.getWidgetProfile(widgetId))
+
+ val state = combine(
+ selectedProfile,
+ deviceProfilesRepo.profiles,
+ upgradeRepo.upgradeInfo,
+ ) { selected, profiles, upgradeInfo ->
+ log(TAG) { "loadProfiles()" }
+
+
+ State(
+ profiles = profiles,
+ isPro = upgradeInfo.isPro,
+ selectedProfile = selected,
+ )
+ }.asLiveData2()
+
+ data class State(
+ val profiles: List = emptyList(),
+ val selectedProfile: ProfileId? = null,
+ val isPro: Boolean = false,
+ ) {
+ val canConfirm: Boolean = selectedProfile != null
+ }
+
+ fun selectProfile(profileId: ProfileId) {
+ log(TAG) { "selectProfile(profileId=$profileId)" }
+ selectedProfile.value = profileId
+ }
+
+ fun confirmSelection() {
+ val selectedProfile = selectedProfile.value
+ if (selectedProfile != null) {
+ log(TAG) { "confirmSelection(widgetId=$widgetId, selectedProfile=$selectedProfile)" }
+ widgetSettings.saveWidgetProfile(widgetId, selectedProfile)
+ }
+ }
+
+ companion object {
+ private val TAG = logTag("Widget", "ConfigurationVM")
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/eu/darken/capod/main/ui/widget/WidgetProfileSelectionAdapter.kt b/app/src/main/java/eu/darken/capod/main/ui/widget/WidgetProfileSelectionAdapter.kt
new file mode 100644
index 00000000..61f04056
--- /dev/null
+++ b/app/src/main/java/eu/darken/capod/main/ui/widget/WidgetProfileSelectionAdapter.kt
@@ -0,0 +1,35 @@
+package eu.darken.capod.main.ui.widget
+
+import android.view.ViewGroup
+import androidx.annotation.LayoutRes
+import androidx.viewbinding.ViewBinding
+import eu.darken.capod.common.lists.BindableVH
+import eu.darken.capod.common.lists.differ.AsyncDiffer
+import eu.darken.capod.common.lists.differ.DifferItem
+import eu.darken.capod.common.lists.differ.HasAsyncDiffer
+import eu.darken.capod.common.lists.differ.setupDiffer
+import eu.darken.capod.common.lists.modular.ModularAdapter
+import eu.darken.capod.common.lists.modular.mods.DataBinderMod
+import eu.darken.capod.common.lists.modular.mods.TypedVHCreatorMod
+import javax.inject.Inject
+
+class WidgetProfileSelectionAdapter @Inject constructor() :
+ ModularAdapter>(),
+ HasAsyncDiffer {
+
+ override val asyncDiffer: AsyncDiffer<*, Item> = setupDiffer()
+
+ init {
+ modules.add(DataBinderMod(data))
+ modules.add(TypedVHCreatorMod({ data[it] is WidgetProfileSelectionVH.Item }) { WidgetProfileSelectionVH(it) })
+ }
+
+ override fun getItemCount(): Int = data.size
+
+ abstract class BaseVH(
+ @LayoutRes layoutId: Int,
+ parent: ViewGroup
+ ) : VH(layoutId, parent), BindableVH
+
+ interface Item : DifferItem
+}
\ No newline at end of file
diff --git a/app/src/main/java/eu/darken/capod/main/ui/widget/WidgetProfileSelectionVH.kt b/app/src/main/java/eu/darken/capod/main/ui/widget/WidgetProfileSelectionVH.kt
new file mode 100644
index 00000000..07b1b1c2
--- /dev/null
+++ b/app/src/main/java/eu/darken/capod/main/ui/widget/WidgetProfileSelectionVH.kt
@@ -0,0 +1,46 @@
+package eu.darken.capod.main.ui.widget
+
+import android.view.ViewGroup
+import eu.darken.capod.R
+import eu.darken.capod.common.lists.binding
+import eu.darken.capod.databinding.WidgetConfigurationProfileItemBinding
+import eu.darken.capod.pods.core.PodDevice
+import eu.darken.capod.profiles.core.DeviceProfile
+
+class WidgetProfileSelectionVH(parent: ViewGroup) :
+ WidgetProfileSelectionAdapter.BaseVH(
+ R.layout.widget_configuration_profile_item,
+ parent
+ ) {
+
+ override val viewBinding = lazy {
+ WidgetConfigurationProfileItemBinding.bind(itemView)
+ }
+
+ override val onBindData: WidgetConfigurationProfileItemBinding.(
+ item: Item,
+ payloads: List
+ ) -> Unit = binding(payload = true) { item ->
+ val profile = item.profile
+
+ profileName.text = profile.label
+ val modelText = when (profile.model) {
+ PodDevice.Model.UNKNOWN -> context.getString(R.string.pods_unknown_label)
+ else -> profile.model.label
+ }
+ profileModel.text = modelText
+ profileIcon.setImageResource(profile.model.iconRes)
+ profileRadio.isChecked = item.isSelected
+ root.isChecked = item.isSelected
+
+ root.setOnClickListener { item.onProfileClick(profile) }
+ }
+
+ data class Item(
+ val profile: DeviceProfile,
+ val isSelected: Boolean,
+ val onProfileClick: (DeviceProfile) -> Unit,
+ ) : WidgetProfileSelectionAdapter.Item {
+ override val stableId: Long = profile.id.hashCode().toLong()
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/eu/darken/capod/main/ui/widget/WidgetProvider.kt b/app/src/main/java/eu/darken/capod/main/ui/widget/WidgetProvider.kt
index 6a017e4b..15c9f07b 100644
--- a/app/src/main/java/eu/darken/capod/main/ui/widget/WidgetProvider.kt
+++ b/app/src/main/java/eu/darken/capod/main/ui/widget/WidgetProvider.kt
@@ -35,6 +35,7 @@ import eu.darken.capod.pods.core.getBatteryLevelCase
import eu.darken.capod.pods.core.getBatteryLevelHeadset
import eu.darken.capod.pods.core.getBatteryLevelLeftPod
import eu.darken.capod.pods.core.getBatteryLevelRightPod
+import eu.darken.capod.profiles.core.ProfileId
import finish2
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.launch
@@ -50,6 +51,7 @@ class WidgetProvider : AppWidgetProvider() {
@Inject lateinit var podDeviceCache: PodDeviceCache
@Inject lateinit var podFactory: PodFactory
@Inject lateinit var upgradeRepo: UpgradeRepo
+ @Inject lateinit var widgetSettings: WidgetSettings
@AppScope @Inject lateinit var appScope: CoroutineScope
private fun executeAsync(
@@ -97,6 +99,15 @@ class WidgetProvider : AppWidgetProvider() {
}
}
+ override fun onDeleted(context: Context, appWidgetIds: IntArray) {
+ log(TAG) { "onDeleted(appWidgetIds=${appWidgetIds.toList()})" }
+ executeAsync("onDeleted") {
+ appWidgetIds.forEach { widgetId ->
+ widgetSettings.removeWidget(widgetId)
+ }
+ }
+ }
+
/**
* Returns number of cells needed for given size of the widget.
*
@@ -124,9 +135,10 @@ class WidgetProvider : AppWidgetProvider() {
widgetId: Int,
options: Bundle?
) {
- log(TAG) { "updateWidget(widgetId=$widgetId, options=$options)" }
+ val profileId: ProfileId? = widgetSettings.getWidgetProfile(widgetId)
+ log(TAG) { "updateWidget(widgetId=$widgetId, profileId=$profileId options=$options)" }
- val device: PodDevice? = podMonitor.latestMainDevice()
+ val device: PodDevice? = profileId?.let { podMonitor.getDeviceForProfile(it) }
val layout = when {
!upgradeRepo.isPro() -> createUpgradeRequiredLayout(context)
@@ -141,15 +153,16 @@ class WidgetProvider : AppWidgetProvider() {
* added if these restrictions will ever be loosened in the future.
*/
val layout = when (columns) {
- in 1 .. 4 -> R.layout.widget_pod_dual_compact_layout
+ in 1..4 -> R.layout.widget_pod_dual_compact_layout
else -> R.layout.widget_pod_dual_wide_layout
}
createDualPodLayout(context, device, layout)
}
+
device is SinglePodDevice -> createSinglePodLayout(context, device)
device is PodDevice -> createUnknownPodLayout(context, device)
- else -> createNoDeviceLayout(context)
+ else -> createNoDeviceLayout(context, profileId != null)
}
widgetManager.updateAppWidget(widgetId, layout)
}
@@ -191,8 +204,9 @@ class WidgetProvider : AppWidgetProvider() {
private fun createNoDeviceLayout(
context: Context,
+ hasConfiguredProfile: Boolean = false
): RemoteViews = RemoteViews(context.packageName, R.layout.widget_message_layout).apply {
- log(TAG, VERBOSE) { "createNoDeviceLayout(context=$context)" }
+ log(TAG, VERBOSE) { "createNoDeviceLayout(context=$context, hasConfiguredProfile=$hasConfiguredProfile)" }
val pendingIntent: PendingIntent = PendingIntent.getActivity(
context,
0,
@@ -202,7 +216,12 @@ class WidgetProvider : AppWidgetProvider() {
setOnClickPendingIntent(R.id.widget_root, pendingIntent)
- setTextViewText(R.id.primary, context.getString(R.string.overview_nomaindevice_label))
+ val messageRes = if (hasConfiguredProfile) {
+ R.string.widget_no_data_label
+ } else {
+ R.string.overview_nomaindevice_label
+ }
+ setTextViewText(R.id.primary, context.getString(messageRes))
}
private fun createDualPodLayout(
@@ -286,5 +305,13 @@ class WidgetProvider : AppWidgetProvider() {
companion object {
val TAG = logTag("Widget", "Provider")
+
+ fun updateWidget(context: Context, appWidgetManager: AppWidgetManager, widgetId: Int) {
+ val intent = Intent(context, WidgetProvider::class.java).apply {
+ action = AppWidgetManager.ACTION_APPWIDGET_UPDATE
+ putExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS, intArrayOf(widgetId))
+ }
+ context.sendBroadcast(intent)
+ }
}
}
\ No newline at end of file
diff --git a/app/src/main/java/eu/darken/capod/main/ui/widget/WidgetSettings.kt b/app/src/main/java/eu/darken/capod/main/ui/widget/WidgetSettings.kt
new file mode 100644
index 00000000..e0bdae3d
--- /dev/null
+++ b/app/src/main/java/eu/darken/capod/main/ui/widget/WidgetSettings.kt
@@ -0,0 +1,50 @@
+package eu.darken.capod.main.ui.widget
+
+import android.content.Context
+import android.content.SharedPreferences
+import androidx.core.content.edit
+import dagger.hilt.android.qualifiers.ApplicationContext
+import eu.darken.capod.common.debug.logging.Logging.Priority.VERBOSE
+import eu.darken.capod.common.debug.logging.log
+import eu.darken.capod.common.debug.logging.logTag
+import eu.darken.capod.profiles.core.ProfileId
+import javax.inject.Inject
+import javax.inject.Singleton
+
+@Singleton
+class WidgetSettings @Inject constructor(
+ @ApplicationContext private val context: Context
+) {
+
+ private val preferences: SharedPreferences = context.getSharedPreferences(
+ "widget_preferences",
+ Context.MODE_PRIVATE
+ )
+
+ fun saveWidgetProfile(widgetId: Int, profileId: ProfileId) {
+ log(TAG, VERBOSE) { "saveWidgetProfile(widgetId=$widgetId, profileId=$profileId)" }
+ preferences.edit {
+ putString(getWidgetProfileKey(widgetId), profileId)
+ }
+ }
+
+ fun getWidgetProfile(widgetId: Int): ProfileId? {
+ val profileId = preferences.getString(getWidgetProfileKey(widgetId), null)
+ log(TAG, VERBOSE) { "getWidgetProfile(widgetId=$widgetId) = $profileId" }
+ return profileId
+ }
+
+ fun removeWidget(widgetId: Int) {
+ log(TAG, VERBOSE) { "removeWidget(widgetId=$widgetId)" }
+ preferences.edit {
+ remove(getWidgetProfileKey(widgetId))
+ }
+ }
+
+ private fun getWidgetProfileKey(widgetId: Int): String = "$WIDGET_PROFILE_PREFIX$widgetId"
+
+ companion object {
+ private const val WIDGET_PROFILE_PREFIX = "widget_profile_"
+ private val TAG = logTag("Widget", "Settings")
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/eu/darken/capod/monitor/core/PodDeviceCache.kt b/app/src/main/java/eu/darken/capod/monitor/core/PodDeviceCache.kt
index a0763e7b..dcea022a 100644
--- a/app/src/main/java/eu/darken/capod/monitor/core/PodDeviceCache.kt
+++ b/app/src/main/java/eu/darken/capod/monitor/core/PodDeviceCache.kt
@@ -28,47 +28,54 @@ class PodDeviceCache @Inject constructor(
private val cacheDir by lazy {
File(context.cacheDir, "device_cache").apply { mkdirs() }
}
- private val mainDeviceCacheFile = File(cacheDir, "main_device.raw")
private val jsonAdapter = moshi.adapter()
private val lock = Mutex()
- suspend fun saveMainDevice(device: BleScanResult?) = withContext(dispatcherProvider.IO) {
- log(TAG, VERBOSE) { "saveMainDevice(device=$device)" }
- lock.withLock {
- try {
- if (device == null) {
- mainDeviceCacheFile.delete()
- } else {
- val json = jsonAdapter.toJson(device)
- mainDeviceCacheFile.writeText(json)
- }
- } catch (e: Exception) {
- log(TAG, ERROR) { "Failed to save $device:${e.asLog()}" }
- }
- }
- }
+ private fun ProfileId.toCacheFile(): File = File(cacheDir, "profile_${this}.json")
- suspend fun loadMainDevice(): BleScanResult? = withContext(dispatcherProvider.IO) {
- log(TAG, VERBOSE) { "loadMainDevice()" }
+ suspend fun load(id: ProfileId): BleScanResult? = withContext(dispatcherProvider.IO) {
+ log(TAG, VERBOSE) { "load(id=$id)" }
+ val cacheFile = id.toCacheFile()
lock.withLock {
- if (!mainDeviceCacheFile.exists()) return@withLock null
+ if (!cacheFile.exists()) return@withLock null
try {
- val raw = mainDeviceCacheFile.readText()
+ val raw = cacheFile.readText()
jsonAdapter.fromJson(raw)
} catch (e: Exception) {
- log(TAG, ERROR) { "Failed to read main-device:${e.asLog()}" }
+ log(TAG, ERROR) { "Failed to read profile $id device: ${e.asLog()}, deleting corrupted cache file" }
+ cacheFile.delete()
null
}
}
}
- fun load(id: ProfileId): BleScanResult? {
- log(TAG, VERBOSE) { "load(): $id" }
- return null
+ suspend fun saveAll(data: Map) {
+ log(TAG, VERBOSE) { "saveAll(): ${data.size} entries" }
+ lock.withLock {
+ data.forEach { (id, device) ->
+ log(TAG, VERBOSE) { "save(id=$id, device=$device)" }
+ val cacheFile = id.toCacheFile()
+ try {
+ val json = jsonAdapter.toJson(device)
+ cacheFile.writeText(json)
+ } catch (e: Exception) {
+ log(TAG, ERROR) { "Failed to save profile $id device $device: ${e.asLog()}" }
+ cacheFile.delete()
+ }
+ }
+ }
}
- fun save(id: ProfileId) {
- log(TAG, VERBOSE) { "save(): $id" }
+ suspend fun delete(id: ProfileId) {
+ log(TAG, VERBOSE) { "delete(): profileId=$id" }
+ lock.withLock {
+ val cacheFile = id.toCacheFile()
+ try {
+ cacheFile.delete()
+ } catch (e: Exception) {
+ log(TAG, ERROR) { "Failed to delete profile for $id: ${e.asLog()}" }
+ }
+ }
}
companion object {
diff --git a/app/src/main/java/eu/darken/capod/monitor/core/PodMonitor.kt b/app/src/main/java/eu/darken/capod/monitor/core/PodMonitor.kt
index b661583e..92e664a5 100644
--- a/app/src/main/java/eu/darken/capod/monitor/core/PodMonitor.kt
+++ b/app/src/main/java/eu/darken/capod/monitor/core/PodMonitor.kt
@@ -72,7 +72,7 @@ class PodMonitor @Inject constructor(
}
.map { results -> results?.mapNotNull { podFactory.createPod(it) } }
.map { processWithCache(it).values }
- .flatMapLatest { devices ->
+ .flatMapLatest { devices ->
flowOf(sortPodsToInterest(devices))
}
.retryWhen { cause, attempt ->
@@ -86,7 +86,7 @@ class PodMonitor @Inject constructor(
private suspend fun sortPodsToInterest(devices: Collection): List {
val now = Instant.now()
val profiles = profilesRepo.currentProfiles() ?: emptyList()
-
+
return devices.sortedWith(
compareBy { device ->
// Use profile position in list as priority (0 = highest priority)
@@ -178,17 +178,33 @@ class PodMonitor @Inject constructor(
deviceCache[it.identifier] = it
pods[it.identifier] = it
}
+
+ newPods
+ .mapNotNull {
+ val profileId = it.device.meta.profile?.id ?: return@mapNotNull null
+ profileId to it.device.scanResult
+ }
+ .toMap()
+ .run { podDeviceCache.saveAll(this) }
return pods
}
- suspend fun latestMainDevice(): PodDevice? {
- val currentMain = devices.firstOrNull()?.firstOrNull()
- log(TAG) { "Live mainDevice is $currentMain" }
+ suspend fun getDeviceForProfile(profileId: String): PodDevice? {
+ log(TAG) { "getDeviceForProfile(profileId=$profileId)" }
- return currentMain ?: profilesRepo.currentProfiles().firstOrNull()
- ?.let { podDeviceCache.load(it.id) }
- ?.let { podFactory.createPod(it)?.device }
- .also { log(TAG) { "Cached mainDevice is $it" } }
+ val liveDevice = devices.firstOrNull()?.firstOrNull { device ->
+ device.meta.profile?.id == profileId
+ }
+ if (liveDevice != null) {
+ log(TAG) { "Found live device for profile $profileId: $liveDevice" }
+ return liveDevice
+ }
+
+ val cachedDevice = podDeviceCache.load(profileId)?.let {
+ podFactory.createPod(it)?.device
+ }
+ log(TAG) { "Cached device for profile $profileId: $cachedDevice" }
+ return cachedDevice
}
companion object {
diff --git a/app/src/main/java/eu/darken/capod/profiles/core/DeviceProfilesRepo.kt b/app/src/main/java/eu/darken/capod/profiles/core/DeviceProfilesRepo.kt
index 181c228b..66d0bb2f 100644
--- a/app/src/main/java/eu/darken/capod/profiles/core/DeviceProfilesRepo.kt
+++ b/app/src/main/java/eu/darken/capod/profiles/core/DeviceProfilesRepo.kt
@@ -8,6 +8,7 @@ import eu.darken.capod.common.debug.logging.Logging.Priority.VERBOSE
import eu.darken.capod.common.debug.logging.log
import eu.darken.capod.common.debug.logging.logTag
import eu.darken.capod.main.core.GeneralSettings
+import eu.darken.capod.monitor.core.PodDeviceCache
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.map
@@ -23,6 +24,7 @@ class DeviceProfilesRepo @Inject constructor(
@ApplicationContext private val context: Context,
private val generalSettings: GeneralSettings,
private val settings: DeviceProfilesSettings,
+ private val podDeviceCache: PodDeviceCache,
) {
private val mutex = Mutex()
@@ -74,11 +76,12 @@ class DeviceProfilesRepo @Inject constructor(
log(VERBOSE) { "Updated device profile: ${profile.label}" }
}
- suspend fun removeProfile(profileId: String) = mutex.withLock {
+ suspend fun removeProfile(profileId: ProfileId) = mutex.withLock {
val currentContainer = settings.profiles.value
val updatedProfiles = currentContainer.profiles.filter { it.id != profileId }
settings.profiles.value = DeviceProfilesContainer(updatedProfiles)
log(VERBOSE) { "Removed device profile with ID: $profileId" }
+ podDeviceCache.delete(profileId)
}
suspend fun reorderProfiles(profiles: List) = mutex.withLock {
diff --git a/app/src/main/res/layout/widget_configuration_activity.xml b/app/src/main/res/layout/widget_configuration_activity.xml
new file mode 100644
index 00000000..63fd8e51
--- /dev/null
+++ b/app/src/main/res/layout/widget_configuration_activity.xml
@@ -0,0 +1,82 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/widget_configuration_profile_item.xml b/app/src/main/res/layout/widget_configuration_profile_item.xml
new file mode 100644
index 00000000..700b4290
--- /dev/null
+++ b/app/src/main/res/layout/widget_configuration_profile_item.xml
@@ -0,0 +1,60 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index 4a53b6f3..b5c38bba 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -114,6 +114,11 @@
darken
A widget showing the last known device status.
+ Select Device
+ Choose which device profile this widget should display.
+ This feature requires CAPod Pro.
+
+ No data
Indirect data delivery
Use an alternative method to receive BLE data from the system (broadcast instead of callback).
diff --git a/app/src/main/res/xml/battery_widget_info.xml b/app/src/main/res/xml/battery_widget_info.xml
index ace71875..fa101795 100644
--- a/app/src/main/res/xml/battery_widget_info.xml
+++ b/app/src/main/res/xml/battery_widget_info.xml
@@ -11,5 +11,6 @@
android:initialLayout="@layout/widget_loading_layout"
android:resizeMode="horizontal|vertical"
android:widgetCategory="home_screen"
- android:widgetFeatures="reconfigurable|configuration_optional"
+ android:widgetFeatures="reconfigurable"
+ android:configure="eu.darken.capod.main.ui.widget.WidgetConfigurationActivity"
tools:targetApi="s" />
\ No newline at end of file