From a963f7e6d3a0e8dabfbb2a147511ae8ef722e180 Mon Sep 17 00:00:00 2001 From: darken Date: Mon, 3 Jan 2022 18:09:51 +0100 Subject: [PATCH] Improved UI --- .../capod/main/ui/overview/OverviewAdapter.kt | 4 +- .../main/ui/overview/OverviewFragment.kt | 2 +- .../main/ui/overview/OverviewFragmentVM.kt | 4 +- .../overview/cards/UnknownPodDeviceCardVH.kt | 32 --------------- .../cards/{ => pods}/DualApplePodsCardVH.kt | 32 +++++++++++---- .../ui/overview/cards/pods/PodDeviceVH.kt | 29 +++++++++++++ .../cards/pods/UnknownPodDeviceCardVH.kt | 41 +++++++++++++++++++ .../capod/monitor/ui/MonitorNotifications.kt | 6 ++- .../pods/core/airpods/models/AirPodsGen1.kt | 8 ++++ .../pods/core/airpods/models/AirPodsGen2.kt | 8 ++++ .../pods/core/airpods/models/AirPodsMax.kt | 4 ++ .../pods/core/airpods/models/AirPodsPro.kt | 4 ++ .../core/airpods/models/UnknownAppleDevice.kt | 4 ++ .../res/drawable/ic_device_airpods_gen1.xml | 10 +++++ .../res/drawable/ic_device_airpods_gen2.xml | 10 +++++ .../layout/overview_pods_apple_dual_item.xml | 27 ++++++++++-- .../res/layout/overview_pods_unknown_item.xml | 15 ++++++- 17 files changed, 189 insertions(+), 51 deletions(-) delete mode 100644 app/src/main/java/eu/darken/capod/main/ui/overview/cards/UnknownPodDeviceCardVH.kt rename app/src/main/java/eu/darken/capod/main/ui/overview/cards/{ => pods}/DualApplePodsCardVH.kt (75%) create mode 100644 app/src/main/java/eu/darken/capod/main/ui/overview/cards/pods/PodDeviceVH.kt create mode 100644 app/src/main/java/eu/darken/capod/main/ui/overview/cards/pods/UnknownPodDeviceCardVH.kt create mode 100644 app/src/main/res/drawable/ic_device_airpods_gen1.xml create mode 100644 app/src/main/res/drawable/ic_device_airpods_gen2.xml diff --git a/app/src/main/java/eu/darken/capod/main/ui/overview/OverviewAdapter.kt b/app/src/main/java/eu/darken/capod/main/ui/overview/OverviewAdapter.kt index fee8dd28..f9af53b0 100644 --- a/app/src/main/java/eu/darken/capod/main/ui/overview/OverviewAdapter.kt +++ b/app/src/main/java/eu/darken/capod/main/ui/overview/OverviewAdapter.kt @@ -11,9 +11,9 @@ 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 eu.darken.capod.main.ui.overview.cards.DualApplePodsCardVH import eu.darken.capod.main.ui.overview.cards.PermissionCardVH -import eu.darken.capod.main.ui.overview.cards.UnknownPodDeviceCardVH +import eu.darken.capod.main.ui.overview.cards.pods.DualApplePodsCardVH +import eu.darken.capod.main.ui.overview.cards.pods.UnknownPodDeviceCardVH import javax.inject.Inject class OverviewAdapter @Inject constructor() : diff --git a/app/src/main/java/eu/darken/capod/main/ui/overview/OverviewFragment.kt b/app/src/main/java/eu/darken/capod/main/ui/overview/OverviewFragment.kt index 60ff673e..be0a2cd9 100644 --- a/app/src/main/java/eu/darken/capod/main/ui/overview/OverviewFragment.kt +++ b/app/src/main/java/eu/darken/capod/main/ui/overview/OverviewFragment.kt @@ -38,7 +38,7 @@ class OverviewFragment : Fragment3(R.layout.main_fragment) { override fun onViewCreated(view: View, savedInstanceState: Bundle?) { ui.apply { - list.setupDefaults(adapter) + list.setupDefaults(adapter, dividers = false) } ui.toolbar.apply { diff --git a/app/src/main/java/eu/darken/capod/main/ui/overview/OverviewFragmentVM.kt b/app/src/main/java/eu/darken/capod/main/ui/overview/OverviewFragmentVM.kt index 33329ec3..70be9f2c 100644 --- a/app/src/main/java/eu/darken/capod/main/ui/overview/OverviewFragmentVM.kt +++ b/app/src/main/java/eu/darken/capod/main/ui/overview/OverviewFragmentVM.kt @@ -12,9 +12,9 @@ import eu.darken.capod.common.navigation.navVia import eu.darken.capod.common.permissions.Permission import eu.darken.capod.common.permissions.isGrantedOrNotRequired import eu.darken.capod.common.uix.ViewModel3 -import eu.darken.capod.main.ui.overview.cards.DualApplePodsCardVH import eu.darken.capod.main.ui.overview.cards.PermissionCardVH -import eu.darken.capod.main.ui.overview.cards.UnknownPodDeviceCardVH +import eu.darken.capod.main.ui.overview.cards.pods.DualApplePodsCardVH +import eu.darken.capod.main.ui.overview.cards.pods.UnknownPodDeviceCardVH import eu.darken.capod.monitor.core.PodMonitor import eu.darken.capod.monitor.core.worker.MonitorControl import eu.darken.capod.pods.core.PodDevice diff --git a/app/src/main/java/eu/darken/capod/main/ui/overview/cards/UnknownPodDeviceCardVH.kt b/app/src/main/java/eu/darken/capod/main/ui/overview/cards/UnknownPodDeviceCardVH.kt deleted file mode 100644 index 48582685..00000000 --- a/app/src/main/java/eu/darken/capod/main/ui/overview/cards/UnknownPodDeviceCardVH.kt +++ /dev/null @@ -1,32 +0,0 @@ -package eu.darken.capod.main.ui.overview.cards - -import android.view.ViewGroup -import eu.darken.capod.R -import eu.darken.capod.databinding.OverviewPodsUnknownItemBinding -import eu.darken.capod.main.ui.overview.OverviewAdapter -import eu.darken.capod.pods.core.PodDevice - -class UnknownPodDeviceCardVH(parent: ViewGroup) : - OverviewAdapter.BaseVH( - R.layout.overview_pods_unknown_item, - parent - ) { - - override val viewBinding = lazy { - OverviewPodsUnknownItemBinding.bind(itemView) - } - - override val onBindData: OverviewPodsUnknownItemBinding.( - item: Item, - payloads: List - ) -> Unit = { item, _ -> - - name.text = item.device.getLabel(context) - } - - data class Item( - val device: PodDevice - ) : OverviewAdapter.Item { - override val stableId: Long = this.javaClass.hashCode().toLong() - } -} \ No newline at end of file diff --git a/app/src/main/java/eu/darken/capod/main/ui/overview/cards/DualApplePodsCardVH.kt b/app/src/main/java/eu/darken/capod/main/ui/overview/cards/pods/DualApplePodsCardVH.kt similarity index 75% rename from app/src/main/java/eu/darken/capod/main/ui/overview/cards/DualApplePodsCardVH.kt rename to app/src/main/java/eu/darken/capod/main/ui/overview/cards/pods/DualApplePodsCardVH.kt index ef2d7c05..42e5becb 100644 --- a/app/src/main/java/eu/darken/capod/main/ui/overview/cards/DualApplePodsCardVH.kt +++ b/app/src/main/java/eu/darken/capod/main/ui/overview/cards/pods/DualApplePodsCardVH.kt @@ -1,9 +1,10 @@ -package eu.darken.capod.main.ui.overview.cards +package eu.darken.capod.main.ui.overview.cards.pods +import android.icu.text.RelativeDateTimeFormatter import android.view.ViewGroup import eu.darken.capod.R +import eu.darken.capod.common.BuildConfigWrap import eu.darken.capod.databinding.OverviewPodsAppleDualItemBinding -import eu.darken.capod.main.ui.overview.OverviewAdapter import eu.darken.capod.pods.core.DualPods import eu.darken.capod.pods.core.airpods.DualApplePods import eu.darken.capod.pods.core.airpods.DualApplePods.DeviceColor @@ -11,9 +12,11 @@ import eu.darken.capod.pods.core.airpods.DualApplePods.LidState import eu.darken.capod.pods.core.getBatteryCase import eu.darken.capod.pods.core.getBatteryLeftPod import eu.darken.capod.pods.core.getBatteryRightPod +import java.time.Duration +import java.time.Instant class DualApplePodsCardVH(parent: ViewGroup) : - OverviewAdapter.BaseVH( + PodDeviceVH( R.layout.overview_pods_apple_dual_item, parent ) { @@ -22,12 +25,15 @@ class DualApplePodsCardVH(parent: ViewGroup) : OverviewPodsAppleDualItemBinding.bind(itemView) } + private val lastSeenFormatter = RelativeDateTimeFormatter.getInstance() + override val onBindData: OverviewPodsAppleDualItemBinding.( item: Item, payloads: List ) -> Unit = { item, _ -> val device = item.device + name.apply { val sb = StringBuilder(device.getLabel(context)) if (device.deviceColor != DeviceColor.UNKNOWN) { @@ -35,8 +41,16 @@ class DualApplePodsCardVH(parent: ViewGroup) : } text = sb } + deviceIcon.setImageResource(device.iconRes) - reception.text = "${device.rssi} (RSSI)" + val duration = Duration.between(device.lastSeenAt, Instant.now()) + lastSeen.text = lastSeenFormatter.format( + duration.seconds.toDouble(), + RelativeDateTimeFormatter.Direction.LAST, + RelativeDateTimeFormatter.RelativeUnit.SECONDS + ) + + reception.text = device.getSignalQuality(context) podLeft.apply { val sb = StringBuilder(context.getString(R.string.pods_dual_left_label)) @@ -80,11 +94,13 @@ class DualApplePodsCardVH(parent: ViewGroup) : } status.text = getString(R.string.pods_status_x_label, device.getConnectionStateLabel(context)) + if (BuildConfigWrap.DEBUG) { + status.append("\n") + status.append(device.identifier.toString()) + } } data class Item( - val device: DualApplePods - ) : OverviewAdapter.Item { - override val stableId: Long = this.javaClass.hashCode().toLong() - } + override val device: DualApplePods + ) : PodDeviceVH.Item } \ No newline at end of file diff --git a/app/src/main/java/eu/darken/capod/main/ui/overview/cards/pods/PodDeviceVH.kt b/app/src/main/java/eu/darken/capod/main/ui/overview/cards/pods/PodDeviceVH.kt new file mode 100644 index 00000000..7be9fdc1 --- /dev/null +++ b/app/src/main/java/eu/darken/capod/main/ui/overview/cards/pods/PodDeviceVH.kt @@ -0,0 +1,29 @@ +package eu.darken.capod.main.ui.overview.cards.pods + +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.DifferItem +import eu.darken.capod.common.lists.modular.ModularAdapter +import eu.darken.capod.main.ui.overview.OverviewAdapter +import eu.darken.capod.pods.core.PodDevice + +abstract class PodDeviceVH( + @LayoutRes layoutId: Int, + parent: ViewGroup +) : ModularAdapter.VH(layoutId, parent), BindableVH { + + interface Item : OverviewAdapter.Item { + + val device: PodDevice + + override val stableId: Long get() = device.identifier.hashCode().toLong() + + override val payloadProvider: ((DifferItem, DifferItem) -> DifferItem?)? + get() = { old, new -> + if (new::class.isInstance(old)) new else null + } + + } +} \ No newline at end of file diff --git a/app/src/main/java/eu/darken/capod/main/ui/overview/cards/pods/UnknownPodDeviceCardVH.kt b/app/src/main/java/eu/darken/capod/main/ui/overview/cards/pods/UnknownPodDeviceCardVH.kt new file mode 100644 index 00000000..32edb697 --- /dev/null +++ b/app/src/main/java/eu/darken/capod/main/ui/overview/cards/pods/UnknownPodDeviceCardVH.kt @@ -0,0 +1,41 @@ +package eu.darken.capod.main.ui.overview.cards.pods + +import android.icu.text.RelativeDateTimeFormatter +import android.view.ViewGroup +import eu.darken.capod.R +import eu.darken.capod.databinding.OverviewPodsUnknownItemBinding +import eu.darken.capod.pods.core.PodDevice +import java.time.Duration +import java.time.Instant + +class UnknownPodDeviceCardVH(parent: ViewGroup) : + PodDeviceVH( + R.layout.overview_pods_unknown_item, + parent + ) { + + override val viewBinding = lazy { + OverviewPodsUnknownItemBinding.bind(itemView) + } + + private val lastSeenFormatter = RelativeDateTimeFormatter.getInstance() + + override val onBindData: OverviewPodsUnknownItemBinding.( + item: Item, + payloads: List + ) -> Unit = { item, _ -> + val device = item.device + name.text = device.getLabel(context) + + val duration = Duration.between(device.lastSeenAt, Instant.now()) + lastSeen.text = lastSeenFormatter.format( + duration.seconds.toDouble(), + RelativeDateTimeFormatter.Direction.LAST, + RelativeDateTimeFormatter.RelativeUnit.SECONDS + ) + } + + data class Item( + override val device: PodDevice + ) : PodDeviceVH.Item +} \ No newline at end of file diff --git a/app/src/main/java/eu/darken/capod/monitor/ui/MonitorNotifications.kt b/app/src/main/java/eu/darken/capod/monitor/ui/MonitorNotifications.kt index 76dddd23..7a0398de 100644 --- a/app/src/main/java/eu/darken/capod/monitor/ui/MonitorNotifications.kt +++ b/app/src/main/java/eu/darken/capod/monitor/ui/MonitorNotifications.kt @@ -53,7 +53,7 @@ class MonitorNotifications @Inject constructor( .setChannelId(NOTIFICATION_CHANNEL_ID) .setContentIntent(openPi) .setPriority(NotificationCompat.PRIORITY_LOW) - .setSmallIcon(R.drawable.ic_notification_device_status_icon) + .setSmallIcon(R.drawable.ic_baseline_earbuds_24) .setOngoing(true) .setContentTitle(context.getString(R.string.app_name)) } @@ -61,8 +61,12 @@ class MonitorNotifications @Inject constructor( fun getBuilder(device: PodDevice?): NotificationCompat.Builder { if (device == null) { builder.setContentTitle(context.getString(R.string.device_status_loading_message)) + builder.setSmallIcon(R.drawable.ic_baseline_earbuds_24) return builder } + + builder.setSmallIcon(device.iconRes) + val infoText = when (device) { is DualApplePods -> { val sb = StringBuilder() diff --git a/app/src/main/java/eu/darken/capod/pods/core/airpods/models/AirPodsGen1.kt b/app/src/main/java/eu/darken/capod/pods/core/airpods/models/AirPodsGen1.kt index 698fe8c2..da55d1af 100644 --- a/app/src/main/java/eu/darken/capod/pods/core/airpods/models/AirPodsGen1.kt +++ b/app/src/main/java/eu/darken/capod/pods/core/airpods/models/AirPodsGen1.kt @@ -2,11 +2,16 @@ package eu.darken.capod.pods.core.airpods.models import android.bluetooth.le.ScanResult import android.content.Context +import eu.darken.capod.R import eu.darken.capod.common.debug.logging.logTag import eu.darken.capod.pods.core.airpods.DualApplePods import eu.darken.capod.pods.core.airpods.protocol.ProximityPairing +import java.time.Instant +import java.util.* data class AirPodsGen1 constructor( + override val identifier: UUID = UUID.randomUUID(), + override val lastSeenAt: Instant = Instant.now(), override val scanResult: ScanResult, override val proximityMessage: ProximityPairing.Message ) : DualApplePods { @@ -15,5 +20,8 @@ data class AirPodsGen1 constructor( return "AirPods (Gen 1)" } + override val iconRes: Int + get() = R.drawable.ic_device_airpods_gen1 + override val tag: String = logTag("Pod", "AirPodsGen1") } \ No newline at end of file diff --git a/app/src/main/java/eu/darken/capod/pods/core/airpods/models/AirPodsGen2.kt b/app/src/main/java/eu/darken/capod/pods/core/airpods/models/AirPodsGen2.kt index d1024a17..6ea38d8e 100644 --- a/app/src/main/java/eu/darken/capod/pods/core/airpods/models/AirPodsGen2.kt +++ b/app/src/main/java/eu/darken/capod/pods/core/airpods/models/AirPodsGen2.kt @@ -2,11 +2,16 @@ package eu.darken.capod.pods.core.airpods.models import android.bluetooth.le.ScanResult import android.content.Context +import eu.darken.capod.R import eu.darken.capod.common.debug.logging.logTag import eu.darken.capod.pods.core.airpods.DualApplePods import eu.darken.capod.pods.core.airpods.protocol.ProximityPairing +import java.time.Instant +import java.util.* data class AirPodsGen2 constructor( + override val identifier: UUID = UUID.randomUUID(), + override val lastSeenAt: Instant = Instant.now(), override val scanResult: ScanResult, override val proximityMessage: ProximityPairing.Message ) : DualApplePods { @@ -15,5 +20,8 @@ data class AirPodsGen2 constructor( return "AirPods (Gen 2)" } + override val iconRes: Int + get() = R.drawable.ic_device_airpods_gen2 + override val tag: String = logTag("Pod", "AirPodsGen2") } \ No newline at end of file diff --git a/app/src/main/java/eu/darken/capod/pods/core/airpods/models/AirPodsMax.kt b/app/src/main/java/eu/darken/capod/pods/core/airpods/models/AirPodsMax.kt index 61a48647..af08444f 100644 --- a/app/src/main/java/eu/darken/capod/pods/core/airpods/models/AirPodsMax.kt +++ b/app/src/main/java/eu/darken/capod/pods/core/airpods/models/AirPodsMax.kt @@ -4,8 +4,12 @@ import android.bluetooth.le.ScanResult import android.content.Context import eu.darken.capod.pods.core.airpods.ApplePods import eu.darken.capod.pods.core.airpods.protocol.ProximityPairing +import java.time.Instant +import java.util.* data class AirPodsMax constructor( + override val identifier: UUID = UUID.randomUUID(), + override val lastSeenAt: Instant = Instant.now(), override val scanResult: ScanResult, override val proximityMessage: ProximityPairing.Message ) : ApplePods { diff --git a/app/src/main/java/eu/darken/capod/pods/core/airpods/models/AirPodsPro.kt b/app/src/main/java/eu/darken/capod/pods/core/airpods/models/AirPodsPro.kt index 2b9c1ec7..63f10163 100644 --- a/app/src/main/java/eu/darken/capod/pods/core/airpods/models/AirPodsPro.kt +++ b/app/src/main/java/eu/darken/capod/pods/core/airpods/models/AirPodsPro.kt @@ -5,8 +5,12 @@ import android.content.Context import eu.darken.capod.common.debug.logging.logTag import eu.darken.capod.pods.core.airpods.DualApplePods import eu.darken.capod.pods.core.airpods.protocol.ProximityPairing +import java.time.Instant +import java.util.* data class AirPodsPro constructor( + override val identifier: UUID = UUID.randomUUID(), + override val lastSeenAt: Instant = Instant.now(), override val scanResult: ScanResult, override val proximityMessage: ProximityPairing.Message ) : DualApplePods { diff --git a/app/src/main/java/eu/darken/capod/pods/core/airpods/models/UnknownAppleDevice.kt b/app/src/main/java/eu/darken/capod/pods/core/airpods/models/UnknownAppleDevice.kt index cd3e6724..f51599e9 100644 --- a/app/src/main/java/eu/darken/capod/pods/core/airpods/models/UnknownAppleDevice.kt +++ b/app/src/main/java/eu/darken/capod/pods/core/airpods/models/UnknownAppleDevice.kt @@ -4,8 +4,12 @@ import android.bluetooth.le.ScanResult import android.content.Context import eu.darken.capod.pods.core.airpods.ApplePods import eu.darken.capod.pods.core.airpods.protocol.ProximityPairing +import java.time.Instant +import java.util.* data class UnknownAppleDevice constructor( + override val identifier: UUID = UUID.randomUUID(), + override val lastSeenAt: Instant = Instant.now(), override val scanResult: ScanResult, override val proximityMessage: ProximityPairing.Message ) : ApplePods { diff --git a/app/src/main/res/drawable/ic_device_airpods_gen1.xml b/app/src/main/res/drawable/ic_device_airpods_gen1.xml new file mode 100644 index 00000000..2f765df4 --- /dev/null +++ b/app/src/main/res/drawable/ic_device_airpods_gen1.xml @@ -0,0 +1,10 @@ + + + diff --git a/app/src/main/res/drawable/ic_device_airpods_gen2.xml b/app/src/main/res/drawable/ic_device_airpods_gen2.xml new file mode 100644 index 00000000..eea68070 --- /dev/null +++ b/app/src/main/res/drawable/ic_device_airpods_gen2.xml @@ -0,0 +1,10 @@ + + + diff --git a/app/src/main/res/layout/overview_pods_apple_dual_item.xml b/app/src/main/res/layout/overview_pods_apple_dual_item.xml index 34ea789e..6ac0b1a2 100644 --- a/app/src/main/res/layout/overview_pods_apple_dual_item.xml +++ b/app/src/main/res/layout/overview_pods_apple_dual_item.xml @@ -14,21 +14,30 @@ android:layout_width="match_parent" android:layout_height="wrap_content"> + + @@ -49,6 +58,18 @@ app:layout_constraintTop_toTopOf="parent" tools:text="Yours (RSSI -61)" /> + + + + + app:layout_constraintTop_toBottomOf="@id/last_seen" />