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