diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index d79e1632..981a2252 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -28,6 +28,8 @@
android:usesPermissionFlags="neverForLocation" />
+
+
diff --git a/app/src/main/java/eu/darken/capod/common/permissions/Permission.kt b/app/src/main/java/eu/darken/capod/common/permissions/Permission.kt
index 8756dd37..12acdbdf 100644
--- a/app/src/main/java/eu/darken/capod/common/permissions/Permission.kt
+++ b/app/src/main/java/eu/darken/capod/common/permissions/Permission.kt
@@ -62,6 +62,15 @@ enum class Permission(
val pwm = it.getSystemService(Context.POWER_SERVICE) as PowerManager
pwm.isIgnoringBatteryOptimizations(BuildConfigWrap.APPLICATION_ID)
},
+ ),
+ SYSTEM_ALERT_WINDOW(
+ minApiLevel = Build.VERSION_CODES.BASE,
+ labelRes = R.string.permission_system_alert_window_label,
+ descriptionRes = R.string.permission_system_alert_window_description,
+ permissionId = "android.permission.SYSTEM_ALERT_WINDOW",
+ isGranted = {
+ android.provider.Settings.canDrawOverlays(it)
+ },
)
}
diff --git a/app/src/main/java/eu/darken/capod/main/core/PermissionTool.kt b/app/src/main/java/eu/darken/capod/main/core/PermissionTool.kt
index 845b3d75..49895bfc 100644
--- a/app/src/main/java/eu/darken/capod/main/core/PermissionTool.kt
+++ b/app/src/main/java/eu/darken/capod/main/core/PermissionTool.kt
@@ -6,6 +6,7 @@ import eu.darken.capod.common.debug.logging.log
import eu.darken.capod.common.debug.logging.logTag
import eu.darken.capod.common.permissions.Permission
import eu.darken.capod.common.permissions.isRequired
+import eu.darken.capod.reaction.settings.ReactionSettings
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.combine
@@ -18,6 +19,7 @@ import javax.inject.Singleton
class PermissionTool @Inject constructor(
@ApplicationContext private val context: Context,
private val generalSettings: GeneralSettings,
+ private val reactionSettings: ReactionSettings,
) {
private val permissionCheckTrigger = MutableStateFlow(UUID.randomUUID())
@@ -29,10 +31,12 @@ class PermissionTool @Inject constructor(
val missingPermissions: Flow> = combine(
permissionCheckTrigger,
generalSettings.monitorMode.flow,
- ) { _, monitorMode ->
+ reactionSettings.showPopUpOnCaseOpen.flow
+ ) { _, monitorMode, showPopUp ->
Permission.values()
.filter { it != Permission.IGNORE_BATTERY_OPTIMIZATION || monitorMode == MonitorMode.ALWAYS }
.filter { it != Permission.ACCESS_BACKGROUND_LOCATION || monitorMode == MonitorMode.ALWAYS }
+ .filter { it != Permission.SYSTEM_ALERT_WINDOW || showPopUp }
.filter { it.isRequired(context) }
.toSet()
}
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 909ffaf0..43e7595f 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
@@ -33,11 +33,11 @@ class OverviewFragment : Fragment3(R.layout.main_fragment) {
lateinit var adapter: OverviewAdapter
lateinit var permissionLauncher: ActivityResultLauncher
- var awaitingIgnoreBatteryOptimization = false
+ var awaitingPermission = false
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
- awaitingIgnoreBatteryOptimization = savedInstanceState?.getBoolean("awaitingIgnoreBatteryOptimization") ?: false
+ awaitingPermission = savedInstanceState?.getBoolean("awaitingPermission") ?: false
permissionLauncher = registerForActivityResult(ActivityResultContracts.RequestPermission()) { granted ->
log { "Request for $id was granted=$granted" }
@@ -77,16 +77,28 @@ class OverviewFragment : Fragment3(R.layout.main_fragment) {
}
vm.requestPermissionEvent.observe2(ui) {
- if (it == Permission.IGNORE_BATTERY_OPTIMIZATION) {
- awaitingIgnoreBatteryOptimization = true
- startActivity(
- Intent(
- Settings.ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONS,
- Uri.parse("package:${requireContext().packageName}")
+ when (it) {
+ Permission.IGNORE_BATTERY_OPTIMIZATION -> {
+ awaitingPermission = true
+ startActivity(
+ Intent(
+ Settings.ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONS,
+ Uri.parse("package:${requireContext().packageName}")
+ )
)
- )
- } else {
- permissionLauncher.launch(it.permissionId)
+ }
+ Permission.SYSTEM_ALERT_WINDOW -> {
+ awaitingPermission = true
+ startActivity(
+ Intent(
+ Settings.ACTION_MANAGE_OVERLAY_PERMISSION,
+ Uri.parse("package:${requireContext().packageName}")
+ )
+ )
+ }
+ else -> {
+ permissionLauncher.launch(it.permissionId)
+ }
}
}
@@ -135,15 +147,15 @@ class OverviewFragment : Fragment3(R.layout.main_fragment) {
}
override fun onSaveInstanceState(outState: Bundle) {
- outState.putBoolean("awaitingIgnoreBatteryOptimization", awaitingIgnoreBatteryOptimization)
+ outState.putBoolean("awaitingPermission", awaitingPermission)
super.onSaveInstanceState(outState)
}
override fun onResume() {
super.onResume()
- if (awaitingIgnoreBatteryOptimization) {
- awaitingIgnoreBatteryOptimization = false
- log { "awaitingIgnoreBatteryOptimization=true" }
+ if (awaitingPermission) {
+ awaitingPermission = false
+ log { "awaitingPermission=true" }
vm.onPermissionResult(true)
}
}
diff --git a/app/src/main/java/eu/darken/capod/pods/core/apple/DualApplePods.kt b/app/src/main/java/eu/darken/capod/pods/core/apple/DualApplePods.kt
index 8c5be778..8f93d8a7 100644
--- a/app/src/main/java/eu/darken/capod/pods/core/apple/DualApplePods.kt
+++ b/app/src/main/java/eu/darken/capod/pods/core/apple/DualApplePods.kt
@@ -93,7 +93,8 @@ interface DualApplePods : ApplePods, HasDualPods, HasDualEarDetection, HasCase {
val caseLidState: LidState
get() {
- return LidState.values().firstOrNull { it.rawRange.contains(rawCaseLidState.toInt()) } ?: LidState.UNKNOWN
+ val rawstate = rawCaseLidState
+ return LidState.values().firstOrNull { it.rawRange.contains(rawstate.toInt()) } ?: LidState.UNKNOWN
}
/**
diff --git a/app/src/main/java/eu/darken/capod/reaction/popup/PopUpReaction.kt b/app/src/main/java/eu/darken/capod/reaction/popup/PopUpReaction.kt
index acb5b471..7c2688ae 100644
--- a/app/src/main/java/eu/darken/capod/reaction/popup/PopUpReaction.kt
+++ b/app/src/main/java/eu/darken/capod/reaction/popup/PopUpReaction.kt
@@ -1,5 +1,6 @@
package eu.darken.capod.reaction.popup
+import eu.darken.capod.common.coroutine.DispatcherProvider
import eu.darken.capod.common.debug.logging.Logging.Priority.INFO
import eu.darken.capod.common.debug.logging.Logging.Priority.VERBOSE
import eu.darken.capod.common.debug.logging.log
@@ -9,9 +10,10 @@ import eu.darken.capod.common.flow.withPrevious
import eu.darken.capod.monitor.core.PodMonitor
import eu.darken.capod.pods.core.PodDevice
import eu.darken.capod.pods.core.apple.DualApplePods
-import eu.darken.capod.reaction.popup.ui.PopUpNotification
+import eu.darken.capod.reaction.popup.ui.PopUpWindow
import eu.darken.capod.reaction.settings.ReactionSettings
import kotlinx.coroutines.flow.*
+import kotlinx.coroutines.withContext
import javax.inject.Inject
import javax.inject.Singleton
@@ -19,7 +21,8 @@ import javax.inject.Singleton
class PopUpReaction @Inject constructor(
private val podMonitor: PodMonitor,
private val reactionSettings: ReactionSettings,
- private val popUpNotification: PopUpNotification,
+ private val popupWindow: PopUpWindow,
+ private val dispatcherProvider: DispatcherProvider,
) {
fun monitor(): Flow = reactionSettings.showPopUpOnCaseOpen.flow
@@ -51,10 +54,14 @@ class PopUpReaction @Inject constructor(
if (current.caseLidState == DualApplePods.LidState.OPEN && current.model != PodDevice.Model.UNKNOWN) {
log(TAG, INFO) { "Show popup" }
- popUpNotification.showNotification(current)
+ withContext(dispatcherProvider.Main) {
+ popupWindow.show(current)
+ }
} else if (current.caseLidState != DualApplePods.LidState.OPEN) {
log(TAG, INFO) { "Hide popup" }
- popUpNotification.hideNotification()
+ withContext(dispatcherProvider.Main) {
+ popupWindow.close()
+ }
}
}
}
diff --git a/app/src/main/java/eu/darken/capod/reaction/popup/ui/PopUpNotification.kt b/app/src/main/java/eu/darken/capod/reaction/popup/ui/PopUpNotification.kt
deleted file mode 100644
index 30b6d386..00000000
--- a/app/src/main/java/eu/darken/capod/reaction/popup/ui/PopUpNotification.kt
+++ /dev/null
@@ -1,82 +0,0 @@
-package eu.darken.capod.reaction.popup.ui
-
-import android.app.NotificationChannel
-import android.app.NotificationManager
-import android.app.PendingIntent
-import android.content.Context
-import android.content.Intent
-import androidx.core.app.NotificationCompat
-import dagger.hilt.android.qualifiers.ApplicationContext
-import eu.darken.capod.R
-import eu.darken.capod.common.BuildConfigWrap
-import eu.darken.capod.common.debug.logging.log
-import eu.darken.capod.common.debug.logging.logTag
-import eu.darken.capod.common.notifications.PendingIntentCompat
-import eu.darken.capod.main.ui.MainActivity
-import eu.darken.capod.pods.core.PodDevice
-import javax.inject.Inject
-
-
-class PopUpNotification @Inject constructor(
- @ApplicationContext private val context: Context,
- private val notificationManager: NotificationManager,
- private val notificationViewFactory: PopUpNotificationViewFactory
-) {
-
- private val builder: NotificationCompat.Builder
-
- init {
- NotificationChannel(
- NOTIFICATION_CHANNEL_ID,
- context.getString(R.string.notification_channel_reaction_popup_label),
- NotificationManager.IMPORTANCE_HIGH
- ).run {
- vibrationPattern = LongArray(1) { 0 }
- enableVibration(true)
- enableLights(false)
- setSound(null, null)
- notificationManager.createNotificationChannel(this)
- }
-
- val openIntent = Intent(context, MainActivity::class.java)
- val openPi = PendingIntent.getActivity(
- context,
- 0,
- openIntent,
- PendingIntentCompat.FLAG_IMMUTABLE
- )
-
- builder = NotificationCompat.Builder(context, NOTIFICATION_CHANNEL_ID).apply {
- setChannelId(NOTIFICATION_CHANNEL_ID)
- setContentIntent(openPi)
- priority = NotificationCompat.PRIORITY_LOW
- setSmallIcon(R.drawable.ic_device_generic_earbuds)
- setContentTitle(context.getString(R.string.app_name))
- }
- }
-
- private fun getBuilder(device: PodDevice): NotificationCompat.Builder = builder.apply {
- setStyle(NotificationCompat.DecoratedCustomViewStyle())
- setCustomContentView(notificationViewFactory.createContentView(device))
- setSmallIcon(device.iconRes)
- setSubText(null)
- }
-
- fun showNotification(device: PodDevice) {
- val notification = getBuilder(device).build()
- log(TAG) { "showNotification(): $device" }
- notificationManager.notify(NOTIFICATION_ID, notification)
- }
-
- fun hideNotification() {
- log(TAG) { "hideNotification()" }
- notificationManager.cancel(NOTIFICATION_ID)
- }
-
- companion object {
- val TAG = logTag("Reaction", "PopUp", "Notifications")
- private val NOTIFICATION_CHANNEL_ID =
- "${BuildConfigWrap.APPLICATION_ID}.notification.channel.reaction.popup"
- internal const val NOTIFICATION_ID = 9000
- }
-}
diff --git a/app/src/main/java/eu/darken/capod/reaction/popup/ui/PopUpNotificationViewFactory.kt b/app/src/main/java/eu/darken/capod/reaction/popup/ui/PopUpNotificationViewFactory.kt
deleted file mode 100644
index 1dc8697d..00000000
--- a/app/src/main/java/eu/darken/capod/reaction/popup/ui/PopUpNotificationViewFactory.kt
+++ /dev/null
@@ -1,65 +0,0 @@
-package eu.darken.capod.reaction.popup.ui
-
-import android.content.Context
-import android.widget.RemoteViews
-import dagger.hilt.android.qualifiers.ApplicationContext
-import eu.darken.capod.R
-import eu.darken.capod.pods.core.*
-import eu.darken.capod.pods.core.apple.BasicSingleApplePods
-import eu.darken.capod.pods.core.apple.DualApplePods
-import eu.darken.capod.pods.core.apple.SingleApplePods
-import javax.inject.Inject
-
-
-class PopUpNotificationViewFactory @Inject constructor(
- @ApplicationContext private val context: Context
-) {
-
- fun createContentView(device: PodDevice): RemoteViews = when (device) {
- is DualApplePods -> createDualApplePods(device)
- is SingleApplePods -> createSingleApplePods(device)
- is BasicSingleApplePods -> createSingleBasicApplePods(device)
- else -> throw IllegalArgumentException("Unexpected device: $device")
- }
-
- private fun createDualApplePods(device: DualApplePods): RemoteViews = RemoteViews(
- context.packageName,
- R.layout.popup_notification_dual_pods
- ).apply {
- setTextViewText(R.id.pod_label, device.getLabel(context))
-
- device.getBatteryLevelLeftPod(context)
- .let { if (device.isLeftPodCharging) "$it⚡" else it }
- .run { setTextViewText(R.id.pod_left, this) }
-
- device.getBatteryLevelCase(context)
- .let { if (device.isCaseCharging) "$it⚡" else it }
- .run { setTextViewText(R.id.pod_case, this) }
-
- device.getBatteryLevelRightPod(context)
- .let { if (device.isRightPodCharging) "$it⚡" else it }
- .run { setTextViewText(R.id.pod_right, this) }
- }
-
- private fun createSingleApplePods(device: SingleApplePods): RemoteViews = RemoteViews(
- context.packageName,
- R.layout.popup_notification_single_pods
- ).apply {
- setTextViewText(R.id.headphones_label, device.getLabel(context))
-
- device.getBatteryLevelHeadset(context)
- .let { if (!device.isHeadsetBeingCharged) "$it⚡" else it }
- .run { setTextViewText(R.id.headphones, this) }
- }
-
- private fun createSingleBasicApplePods(device: BasicSingleApplePods): RemoteViews = RemoteViews(
- context.packageName,
- R.layout.popup_notification_single_pods_basic
- ).apply {
- setTextViewText(R.id.headphones_label, device.getLabel(context))
-
- device.getBatteryLevelHeadset(context)
- .run { setTextViewText(R.id.headphones, this) }
- }
-
-}
\ No newline at end of file
diff --git a/app/src/main/java/eu/darken/capod/reaction/popup/ui/PopUpPodViewFactory.kt b/app/src/main/java/eu/darken/capod/reaction/popup/ui/PopUpPodViewFactory.kt
new file mode 100644
index 00000000..d59718b7
--- /dev/null
+++ b/app/src/main/java/eu/darken/capod/reaction/popup/ui/PopUpPodViewFactory.kt
@@ -0,0 +1,88 @@
+package eu.darken.capod.reaction.popup.ui
+
+import android.content.Context
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import androidx.appcompat.view.ContextThemeWrapper
+import androidx.core.view.isInvisible
+import dagger.hilt.android.qualifiers.ApplicationContext
+import eu.darken.capod.R
+import eu.darken.capod.common.debug.autoreport.DebugSettings
+import eu.darken.capod.databinding.PopupNotificationDualPodsBinding
+import eu.darken.capod.databinding.PopupNotificationSinglePodsBasicBinding
+import eu.darken.capod.databinding.PopupNotificationSinglePodsBinding
+import eu.darken.capod.pods.core.*
+import eu.darken.capod.pods.core.apple.BasicSingleApplePods
+import eu.darken.capod.pods.core.apple.DualApplePods
+import eu.darken.capod.pods.core.apple.SingleApplePods
+import javax.inject.Inject
+
+
+class PopUpPodViewFactory @Inject constructor(
+ @ApplicationContext private val appContext: Context,
+ private val debugSettings: DebugSettings,
+) {
+
+ private val context = ContextThemeWrapper(appContext, R.style.AppTheme)
+ private val layoutInflater = context.getSystemService(Context.LAYOUT_INFLATER_SERVICE) as LayoutInflater
+
+ fun createContentView(parent: ViewGroup, device: PodDevice): View = when (device) {
+ is DualApplePods -> createDualApplePods(parent, device)
+ is SingleApplePods -> createSingleApplePods(parent, device) // Unused, has no case to trigger reaction?
+ is BasicSingleApplePods -> createSingleBasicApplePods(
+ parent,
+ device
+ ) // Unused, has no case to trigger reaction?
+ else -> throw IllegalArgumentException("Unexpected device: $device")
+ }
+
+ private fun createDualApplePods(parent: ViewGroup, device: DualApplePods): View =
+ PopupNotificationDualPodsBinding.inflate(layoutInflater, parent, false).apply {
+ device.apply {
+ podIcon.setImageResource(iconRes)
+ podLabel.text = getLabel(context)
+ signal.text = getSignalQuality(context)
+ signal.isInvisible = debugSettings.isDebugModeEnabled.value
+
+ // Left
+ podLeftBatteryIcon.setImageResource(getBatteryDrawable(batteryLeftPodPercent))
+ podLeftBatteryLabel.text = getBatteryLevelLeftPod(context)
+
+ // Case
+ podCaseBatteryIcon.setImageResource(getBatteryDrawable(batteryCasePercent))
+ podCaseBatteryLabel.text = getBatteryLevelCase(context)
+
+ // Right
+ podRightBatteryIcon.setImageResource(getBatteryDrawable(batteryRightPodPercent))
+ podRightBatteryLabel.text = getBatteryLevelRightPod(context)
+ }
+ }.root
+
+ private fun createSingleApplePods(parent: ViewGroup, device: SingleApplePods): View =
+ PopupNotificationSinglePodsBinding.inflate(layoutInflater, parent, false).apply {
+ device.apply {
+ headphonesIcon.setImageResource(iconRes)
+ headphonesLabel.text = getLabel(context)
+ signal.text = getSignalQuality(context)
+ signal.isInvisible = debugSettings.isDebugModeEnabled.value
+
+ headphonesBatteryIcon.setImageResource(getBatteryDrawable(batteryHeadsetPercent))
+ headphonesBatteryLabel.text = getBatteryLevelHeadset(context)
+ }
+ }.root
+
+ private fun createSingleBasicApplePods(parent: ViewGroup, device: BasicSingleApplePods): View =
+ PopupNotificationSinglePodsBasicBinding.inflate(layoutInflater, parent, false).apply {
+ device.apply {
+ headphonesIcon.setImageResource(iconRes)
+ headphonesLabel.text = getLabel(context)
+ signal.text = getSignalQuality(context)
+ signal.isInvisible = debugSettings.isDebugModeEnabled.value
+
+ headphonesBatteryIcon.setImageResource(getBatteryDrawable(batteryHeadsetPercent))
+ headphonesBatteryLabel.text = getBatteryLevelHeadset(context)
+ }
+ }.root
+
+}
\ No newline at end of file
diff --git a/app/src/main/java/eu/darken/capod/reaction/popup/ui/PopUpWindow.kt b/app/src/main/java/eu/darken/capod/reaction/popup/ui/PopUpWindow.kt
new file mode 100644
index 00000000..c4441a85
--- /dev/null
+++ b/app/src/main/java/eu/darken/capod/reaction/popup/ui/PopUpWindow.kt
@@ -0,0 +1,75 @@
+package eu.darken.capod.reaction.popup.ui
+
+import android.content.Context
+import android.content.Context.WINDOW_SERVICE
+import android.graphics.PixelFormat
+import android.view.Gravity
+import android.view.LayoutInflater
+import android.view.View
+import android.view.WindowManager
+import android.widget.FrameLayout
+import androidx.appcompat.view.ContextThemeWrapper
+import dagger.hilt.android.qualifiers.ApplicationContext
+import eu.darken.capod.R
+import eu.darken.capod.common.debug.logging.Logging.Priority.ERROR
+import eu.darken.capod.common.debug.logging.asLog
+import eu.darken.capod.common.debug.logging.log
+import eu.darken.capod.common.debug.logging.logTag
+import eu.darken.capod.pods.core.PodDevice
+import javax.inject.Inject
+import javax.inject.Singleton
+
+@Singleton
+class PopUpWindow @Inject constructor(
+ @ApplicationContext private val appContext: Context,
+ private val podViewFactory: PopUpPodViewFactory
+) {
+
+ private val context = ContextThemeWrapper(appContext, R.style.AppTheme)
+ private val layoutInflater = context.getSystemService(Context.LAYOUT_INFLATER_SERVICE) as LayoutInflater
+ private val windowManager: WindowManager = context.getSystemService(WINDOW_SERVICE) as WindowManager
+ private val layoutParams = WindowManager.LayoutParams(
+ WindowManager.LayoutParams.WRAP_CONTENT,
+ WindowManager.LayoutParams.WRAP_CONTENT, // Display it on top of other application windows
+ WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY, // Don't let it grab the input focus
+ WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE, // Make the underlying application window visible
+
+ PixelFormat.TRANSLUCENT
+ ).apply {
+ gravity = Gravity.BOTTOM
+ }
+ private val popUpView: View = layoutInflater.inflate(R.layout.popup_window_container_layout, null).apply {
+ findViewById(R.id.close_action).setOnClickListener { close() }
+ }
+ private val deviceContainer = popUpView.findViewById(R.id.popup_content)
+
+ fun show(device: PodDevice) = try {
+ log(TAG) { "open()" }
+ if (popUpView.windowToken == null && popUpView.parent == null) {
+ val podView = podViewFactory.createContentView(deviceContainer, device)
+ deviceContainer.addView(podView)
+ windowManager.addView(popUpView, layoutParams)
+ } else {
+ log(TAG) { "View already added." }
+ }
+ } catch (e: Exception) {
+ log(TAG, ERROR) { "open() failed: ${e.asLog()}" }
+ }
+
+ fun close() = try {
+ log(TAG) { "close()" }
+ if (popUpView.parent != null) {
+// (popUpView.parent as ViewGroup).removeAllViews()
+// popUpView.invalidate()
+ windowManager.removeView(popUpView)
+ } else {
+ log(TAG) { "View was not added" }
+ }
+ } catch (e: Exception) {
+ log(TAG, ERROR) { "close() failed: ${e.asLog()}" }
+ }
+
+ companion object {
+ private val TAG = logTag("Reaction", "PopUp", "Window")
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/res/drawable/ic_baseline_close_24.xml b/app/src/main/res/drawable/ic_baseline_close_24.xml
new file mode 100644
index 00000000..95cc170f
--- /dev/null
+++ b/app/src/main/res/drawable/ic_baseline_close_24.xml
@@ -0,0 +1,10 @@
+
+
+
diff --git a/app/src/main/res/layout/popup_notification_dual_pods.xml b/app/src/main/res/layout/popup_notification_dual_pods.xml
index 6dffd6f1..a301f8be 100644
--- a/app/src/main/res/layout/popup_notification_dual_pods.xml
+++ b/app/src/main/res/layout/popup_notification_dual_pods.xml
@@ -1,72 +1,182 @@
-
+ android:layout_height="wrap_content">
-
+
+
-
+ android:drawableEnd="@drawable/ic_baseline_signal_cellular_alt_24"
+ android:gravity="center"
+ android:minWidth="44dp"
+ app:layout_constraintBottom_toBottomOf="@+id/pod_label"
+ app:layout_constraintEnd_toEndOf="parent"
+ app:layout_constraintTop_toTopOf="@+id/pod_label"
+ tools:text="-90%" />
-
+
-
+
+
+
+
-
+
-
+
+
+
+
+
+
-
+
-
+
+
+
+
+
+
-
-
-
\ No newline at end of file
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/popup_notification_single_pods.xml b/app/src/main/res/layout/popup_notification_single_pods.xml
index e160d10f..fa54becb 100644
--- a/app/src/main/res/layout/popup_notification_single_pods.xml
+++ b/app/src/main/res/layout/popup_notification_single_pods.xml
@@ -1,29 +1,81 @@
-
+ android:layout_height="wrap_content">
-
+
+
-
+ android:drawableEnd="@drawable/ic_baseline_signal_cellular_alt_24"
+ android:gravity="center"
+ android:minWidth="44dp"
+ app:layout_constraintBottom_toBottomOf="@+id/headphones_label"
+ app:layout_constraintEnd_toEndOf="parent"
+ app:layout_constraintTop_toTopOf="@+id/headphones_label"
+ tools:text="-90%" />
-
\ No newline at end of file
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/popup_notification_single_pods_basic.xml b/app/src/main/res/layout/popup_notification_single_pods_basic.xml
index e92ba0fe..7bf391e5 100644
--- a/app/src/main/res/layout/popup_notification_single_pods_basic.xml
+++ b/app/src/main/res/layout/popup_notification_single_pods_basic.xml
@@ -1,38 +1,81 @@
-
+ android:layout_height="wrap_content">
-
+
+
+ app:layout_constraintEnd_toStartOf="@id/signal"
+ app:layout_constraintStart_toEndOf="@id/headphones_icon"
+ app:layout_constraintTop_toTopOf="parent"
+ tools:text="Beats Solo 3" />
-
+ android:drawableEnd="@drawable/ic_baseline_signal_cellular_alt_24"
+ android:gravity="center"
+ android:minWidth="44dp"
+ app:layout_constraintBottom_toBottomOf="@+id/headphones_label"
+ app:layout_constraintEnd_toEndOf="parent"
+ app:layout_constraintTop_toTopOf="@+id/headphones_label"
+ tools:text="-90%" />
-
+
+
+
+
-
+
-
\ No newline at end of file
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/popup_window_container_layout.xml b/app/src/main/res/layout/popup_window_container_layout.xml
new file mode 100644
index 00000000..5571d4c2
--- /dev/null
+++ b/app/src/main/res/layout/popup_window_container_layout.xml
@@ -0,0 +1,37 @@
+
+
+
+
+
+
+
+
+
+
\ 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 04e306d8..8f758ed2 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -15,6 +15,7 @@
Grant permission
Upgrade
Check
+ Close
Size
Compressed size
@@ -134,4 +135,6 @@
Help translate this into your favorite language.
One pod mode
Wearing both pods is not required, wearing a single pod is sufficient to trigger reactions.
+ System Alert Window
+ Allow CAPod to draw over other apps to make the feature \"Show PopUp\" possible.
\ No newline at end of file
diff --git a/app/src/main/res/values/styles.xml b/app/src/main/res/values/styles.xml
index 5f460505..6173be45 100644
--- a/app/src/main/res/values/styles.xml
+++ b/app/src/main/res/values/styles.xml
@@ -14,5 +14,6 @@
\ No newline at end of file