add proper colored popups like ios

This commit is contained in:
Kavish Devar
2024-12-06 19:57:21 +05:30
parent db8458be59
commit ebdb1609f3
27 changed files with 273 additions and 62 deletions

View File

@@ -37,6 +37,7 @@ android {
} }
buildFeatures { buildFeatures {
compose = true compose = true
viewBinding = true
} }
} }

View File

@@ -8,7 +8,6 @@
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" /> <uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" /> <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" /> <uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />
<uses-permission <uses-permission
android:name="android.permission.BLUETOOTH_PRIVILEGED" android:name="android.permission.BLUETOOTH_PRIVILEGED"
tools:ignore="ProtectedPermissions" /> tools:ignore="ProtectedPermissions" />
@@ -29,6 +28,18 @@
android:theme="@style/Theme.ALN" android:theme="@style/Theme.ALN"
tools:ignore="UnusedAttribute" tools:ignore="UnusedAttribute"
tools:targetApi="31"> tools:targetApi="31">
<receiver
android:name=".BatteryWidget"
android:exported="false">
<intent-filter>
<action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
</intent-filter>
<meta-data
android:name="android.appwidget.provider"
android:resource="@xml/battery_widget_info" />
</receiver>
<activity <activity
android:name=".CustomDevice" android:name=".CustomDevice"
android:exported="true" android:exported="true"
@@ -44,6 +55,7 @@
android:theme="@style/Theme.ALN"> android:theme="@style/Theme.ALN">
<intent-filter> <intent-filter>
<action android:name="android.intent.action.MAIN" /> <action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" /> <category android:name="android.intent.category.LAUNCHER" />
</intent-filter> </intent-filter>
</activity> </activity>
@@ -64,6 +76,7 @@
<action android:name="android.service.quicksettings.action.QS_TILE" /> <action android:name="android.service.quicksettings.action.QS_TILE" />
</intent-filter> </intent-filter>
</service> </service>
<receiver <receiver
android:name=".BootReceiver" android:name=".BootReceiver"
android:enabled="true" android:enabled="true"

View File

@@ -0,0 +1,44 @@
package me.kavishdevar.aln
import android.appwidget.AppWidgetManager
import android.appwidget.AppWidgetProvider
import android.content.Context
import android.widget.RemoteViews
/**
* Implementation of App Widget functionality.
*/
class BatteryWidget : AppWidgetProvider() {
override fun onUpdate(
context: Context,
appWidgetManager: AppWidgetManager,
appWidgetIds: IntArray
) {
// There may be multiple widgets active, so update all of them
for (appWidgetId in appWidgetIds) {
updateAppWidget(context, appWidgetManager, appWidgetId)
}
}
override fun onEnabled(context: Context) {
// Enter relevant functionality for when the first widget is created
}
override fun onDisabled(context: Context) {
// Enter relevant functionality for when the last widget is disabled
}
}
internal fun updateAppWidget(
context: Context,
appWidgetManager: AppWidgetManager,
appWidgetId: Int
) {
val widgetText = context.getString(R.string.appwidget_text)
// Construct the RemoteViews object
val views = RemoteViews(context.packageName, R.layout.battery_widget)
views.setTextViewText(R.id.appwidget_text, widgetText)
// Instruct the widget manager to update the widget
appWidgetManager.updateAppWidget(appWidgetId, views)
}

View File

@@ -9,6 +9,7 @@ import android.graphics.PixelFormat
import android.util.Log import android.util.Log
import android.view.Gravity import android.view.Gravity
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.MotionEvent
import android.view.View import android.view.View
import android.view.WindowManager import android.view.WindowManager
import android.view.animation.AccelerateInterpolator import android.view.animation.AccelerateInterpolator
@@ -17,25 +18,35 @@ import android.widget.ImageButton
import android.widget.LinearLayout import android.widget.LinearLayout
import android.widget.TextView import android.widget.TextView
import android.widget.VideoView import android.widget.VideoView
import androidx.lifecycle.findViewTreeLifecycleOwner
import androidx.lifecycle.setViewTreeLifecycleOwner
import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.MainScope import kotlinx.coroutines.MainScope
import kotlinx.coroutines.delay import kotlinx.coroutines.delay
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import java.lang.Exception import java.lang.Exception
@SuppressLint("InflateParams") @SuppressLint("InflateParams", "ClickableViewAccessibility")
class Window (context: Context) { class Window (context: Context) {
private val mView: View private val mView: View
private val mParams: WindowManager.LayoutParams = WindowManager.LayoutParams(
(context.resources.displayMetrics.widthPixels * 0.95).toInt(), @Suppress("DEPRECATION")
WindowManager.LayoutParams.WRAP_CONTENT, // Display it on top of other application windows @SuppressLint("NewApi")
WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY, // Don't let it grab the input focus private val mParams: WindowManager.LayoutParams = WindowManager.LayoutParams().apply {
WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE, // Make the underlying application window visible height = WindowManager.LayoutParams.WRAP_CONTENT
PixelFormat.TRANSLUCENT width = WindowManager.LayoutParams.MATCH_PARENT
) type = WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY
format = PixelFormat.TRANSLUCENT
gravity = Gravity.BOTTOM
dimAmount = 0.3f
flags = WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS or
WindowManager.LayoutParams.FLAG_FULLSCREEN or
WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE or
WindowManager.LayoutParams.FLAG_DIM_BEHIND or
WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH
}
private val mWindowManager: WindowManager private val mWindowManager: WindowManager
init { init {
val layoutInflater = context.getSystemService(Context.LAYOUT_INFLATER_SERVICE) as LayoutInflater val layoutInflater = context.getSystemService(Context.LAYOUT_INFLATER_SERVICE) as LayoutInflater
mView = layoutInflater.inflate(R.layout.popup_window, null) mView = layoutInflater.inflate(R.layout.popup_window, null)
@@ -56,7 +67,29 @@ class Window (context: Context) {
ll.setOnClickListener { ll.setOnClickListener {
close() close()
} }
ll.setViewTreeLifecycleOwner(mView.findViewTreeLifecycleOwner())
@Suppress("DEPRECATION")
mView.systemUiVisibility = View.SYSTEM_UI_FLAG_LAYOUT_STABLE or
View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION or
View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN or
View.SYSTEM_UI_FLAG_HIDE_NAVIGATION or
View.SYSTEM_UI_FLAG_FULLSCREEN or
View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY
mView.setOnTouchListener { _, event ->
if (event.action == MotionEvent.ACTION_DOWN) {
val touchY = event.rawY
val popupTop = mView.top
if (touchY < popupTop) {
close()
true
} else {
false
}
} else {
false
}
}
mWindowManager = context.getSystemService(Context.WINDOW_SERVICE) as WindowManager mWindowManager = context.getSystemService(Context.WINDOW_SERVICE) as WindowManager
} }
@@ -91,49 +124,6 @@ class Window (context: Context) {
batteryCaseText.text = batteryStatus.find { it.component == BatteryComponent.CASE }?.let { batteryCaseText.text = batteryStatus.find { it.component == BatteryComponent.CASE }?.let {
"\uDBC3\uDE6C ${it.level}%" "\uDBC3\uDE6C ${it.level}%"
} ?: "" } ?: ""
// composeView.setContent {
// Row (
// modifier = Modifier
// .fillMaxWidth(),
// horizontalArrangement = Arrangement.Center,
// verticalAlignment = Alignment.CenterVertically
// ) {
// Row (
// modifier = Modifier
// .fillMaxWidth(0.5f),
// horizontalArrangement = Arrangement.SpaceBetween
// ){
// val left = batteryStatus.find { it.component == BatteryComponent.LEFT }
// val right = batteryStatus.find { it.component == BatteryComponent.RIGHT }
// if ((right?.status == BatteryStatus.CHARGING && left?.status == BatteryStatus.CHARGING) || (left?.status == BatteryStatus.NOT_CHARGING && right?.status == BatteryStatus.NOT_CHARGING))
// {
// BatteryIndicator(right.level.let { left.level.coerceAtMost(it) }, left.status == BatteryStatus.CHARGING)
// }
// else {
// Row {
// if (left?.status != BatteryStatus.DISCONNECTED) {
// Text(text = "\uDBC6\uDCE5", fontFamily = FontFamily(Font(R.font.sf_pro)))
// BatteryIndicator(left?.level ?: 0, left?.status == BatteryStatus.CHARGING)
// Spacer(modifier = Modifier.width(16.dp))
// }
// if (right?.status != BatteryStatus.DISCONNECTED) {
// Text(text = "\uDBC6\uDCE8", fontFamily = FontFamily(Font(R.font.sf_pro)))
// BatteryIndicator(right?.level ?: 0, right?.status == BatteryStatus.CHARGING)
// }
// }
// }
// }
// Row (
// modifier = Modifier
// .fillMaxWidth(),
// horizontalArrangement = Arrangement.Center
// ) {
// val case =
// batteryStatus.find { it.component == BatteryComponent.CASE }
// BatteryIndicator(case?.level ?: 0)
// }
// }
// }
// Slide-up animation // Slide-up animation
val displayMetrics = mView.context.resources.displayMetrics val displayMetrics = mView.context.resources.displayMetrics

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.4 KiB

View File

@@ -0,0 +1,10 @@
<?xml version="1.0" encoding="utf-8"?><!--
Background for widgets to make the rounded corners based on the
appWidgetRadius attribute value
-->
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<corners android:radius="?attr/appWidgetRadius" />
<solid android:color="?android:attr/colorBackground" />
</shape>

View File

@@ -0,0 +1,10 @@
<?xml version="1.0" encoding="utf-8"?><!--
Background for views inside widgets to make the rounded corners based on the
appWidgetInnerRadius attribute value
-->
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<corners android:radius="?attr/appWidgetInnerRadius" />
<solid android:color="?android:attr/colorAccent" />
</shape>

View File

@@ -4,16 +4,16 @@
android:shape="rectangle" > android:shape="rectangle" >
<solid <solid
android:color="#000000" > android:color="@color/popup_background" >
</solid> </solid>
<padding <padding
android:bottom="64dp" android:bottom="56dp"
android:top="16dp"> android:top="16dp">
</padding> </padding>
<corners <corners
android:radius="48dp"> android:radius="56dp">
</corners> </corners>
</shape> </shape>

View File

@@ -0,0 +1,19 @@
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
style="@style/Widget.ALN.AppWidget.Container"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:theme="@style/Theme.ALN.AppWidgetContainer">
<TextView
android:id="@+id/appwidget_text"
style="@style/Widget.ALN.AppWidget.InnerView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerHorizontal="true"
android:layout_centerVertical="true"
android:layout_margin="8dp"
android:contentDescription="@string/appwidget_text"
android:text="@string/appwidget_text"
android:textSize="24sp"
android:textStyle="bold|italic" />
</RelativeLayout>

View File

@@ -25,7 +25,7 @@
android:fontFamily="@font/sf_pro" android:fontFamily="@font/sf_pro"
android:gravity="center" android:gravity="center"
android:text="Kavish's AirPods Pro" android:text="Kavish's AirPods Pro"
android:textColor="@color/white" android:textColor="@color/popup_text"
android:textSize="28sp" android:textSize="28sp"
app:layout_constraintTop_toTopOf="parent" app:layout_constraintTop_toTopOf="parent"
@@ -72,7 +72,7 @@
android:layout_marginTop="16dp" android:layout_marginTop="16dp"
android:fontFamily="@font/sf_pro" android:fontFamily="@font/sf_pro"
android:text="" android:text=""
android:textColor="@color/white" android:textColor="@color/popup_text"
android:textSize="20sp" android:textSize="20sp"
android:id="@+id/left_battery" android:id="@+id/left_battery"
android:gravity="center" android:gravity="center"
@@ -88,7 +88,7 @@
android:gravity="center" android:gravity="center"
android:text="" android:text=""
android:id="@+id/right_battery" android:id="@+id/right_battery"
android:textColor="@color/white" android:textColor="@color/popup_text"
android:textSize="20sp" /> android:textSize="20sp" />
</LinearLayout> </LinearLayout>
@@ -103,7 +103,7 @@
android:fontFamily="@font/sf_pro" android:fontFamily="@font/sf_pro"
android:gravity="center" android:gravity="center"
android:text="" android:text=""
android:textColor="@color/white" android:textColor="@color/popup_text"
android:textSize="20sp" /> android:textSize="20sp" />
</LinearLayout> </LinearLayout>
</LinearLayout> </LinearLayout>

Binary file not shown.

View File

@@ -0,0 +1,10 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<!--
Having themes.xml for night-v31 because of the priority order of the resource qualifiers.
-->
<style name="Theme.ALN.AppWidgetContainerParent" parent="@android:style/Theme.DeviceDefault.DayNight">
<item name="appWidgetRadius">@android:dimen/system_app_widget_background_radius</item>
<item name="appWidgetInnerRadius">@android:dimen/system_app_widget_inner_radius</item>
</style>
</resources>

View File

@@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<color name="popup_background">#1C1B1E</color>
<color name="popup_text">@color/white</color>
</resources>

View File

@@ -0,0 +1,14 @@
<resources>
<style name="Widget.ALN.AppWidget.Container" parent="android:Widget">
<item name="android:id">@android:id/background</item>
<item name="android:padding">?attr/appWidgetPadding</item>
<item name="android:background">@drawable/app_widget_background</item>
</style>
<style name="Widget.ALN.AppWidget.InnerView" parent="android:Widget">
<item name="android:padding">?attr/appWidgetPadding</item>
<item name="android:background">@drawable/app_widget_inner_view_background</item>
<item name="android:textColor">?android:attr/textColorPrimary</item>
</style>
</resources>

View File

@@ -0,0 +1,16 @@
<resources>
<style name="Widget.ALN.AppWidget.Container" parent="android:Widget">
<item name="android:id">@android:id/background</item>
<item name="android:padding">?attr/appWidgetPadding</item>
<item name="android:background">@drawable/app_widget_background</item>
<item name="android:clipToOutline">true</item>
</style>
<style name="Widget.ALN.AppWidget.InnerView" parent="android:Widget">
<item name="android:padding">?attr/appWidgetPadding</item>
<item name="android:background">@drawable/app_widget_inner_view_background</item>
<item name="android:textColor">?android:attr/textColorPrimary</item>
<item name="android:clipToOutline">true</item>
</style>
</resources>

View File

@@ -0,0 +1,11 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<!--
Having themes.xml for v31 variant because @android:dimen/system_app_widget_background_radius
and @android:dimen/system_app_widget_internal_padding requires API level 31
-->
<style name="Theme.ALN.AppWidgetContainerParent" parent="@android:style/Theme.DeviceDefault.DayNight">
<item name="appWidgetRadius">@android:dimen/system_app_widget_background_radius</item>
<item name="appWidgetInnerRadius">@android:dimen/system_app_widget_inner_radius</item>
</style>
</resources>

View File

@@ -0,0 +1,7 @@
<resources>
<declare-styleable name="AppWidgetAttrs">
<attr name="appWidgetPadding" format="dimension" />
<attr name="appWidgetInnerRadius" format="dimension" />
<attr name="appWidgetRadius" format="dimension" />
</declare-styleable>
</resources>

View File

@@ -2,4 +2,10 @@
<resources> <resources>
<color name="black">#FF000000</color> <color name="black">#FF000000</color>
<color name="white">#FFFFFFFF</color> <color name="white">#FFFFFFFF</color>
<color name="popup_background">#FFFFFF</color>
<color name="popup_text">@color/black</color>
<color name="light_blue_50">#FFE1F5FE</color>
<color name="light_blue_200">#FF81D4FA</color>
<color name="light_blue_600">#FF039BE5</color>
<color name="light_blue_900">#FF01579B</color>
</resources> </resources>

View File

@@ -0,0 +1,10 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<!--
Refer to App Widget Documentation for margin information
http://developer.android.com/guide/topics/appwidgets/index.html#CreatingLayout
-->
<dimen name="widget_margin">0dp</dimen>
</resources>

View File

@@ -1,4 +1,7 @@
<resources> <resources>
<string name="app_name">ALN</string> <string name="app_name">ALN</string>
<string name="title_activity_custom_device">GATT Testing</string> <string name="title_activity_custom_device">GATT Testing</string>
<string name="appwidget_text">EXAMPLE</string>
<string name="add_widget">Add widget</string>
<string name="app_widget_description">This is an app widget description</string>
</resources> </resources>

View File

@@ -0,0 +1,12 @@
<resources>
<style name="Widget.ALN.AppWidget.Container" parent="android:Widget">
<item name="android:id">@android:id/background</item>
<item name="android:background">?android:attr/colorBackground</item>
</style>
<style name="Widget.ALN.AppWidget.InnerView" parent="android:Widget">
<item name="android:background">?android:attr/colorBackground</item>
<item name="android:textColor">?android:attr/textColorPrimary</item>
</style>
</resources>

View File

@@ -1,4 +1,20 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<resources> <resources>
<style name="Theme.ALN" parent="android:Theme.Material.Light.NoActionBar" /> <style name="Theme.ALN" parent="android:Theme.Material.Light.NoActionBar" />
<style name="Theme.ALN.AppWidgetContainerParent" parent="@android:style/Theme.DeviceDefault">
<!-- Radius of the outer bound of widgets to make the rounded corners -->
<item name="appWidgetRadius">16dp</item>
<!--
Radius of the inner view's bound of widgets to make the rounded corners.
It needs to be 8dp or less than the value of appWidgetRadius
-->
<item name="appWidgetInnerRadius">8dp</item>
</style>
<style name="Theme.ALN.AppWidgetContainer" parent="Theme.ALN.AppWidgetContainerParent">
<!-- Apply padding to avoid the content of the widget colliding with the rounded corners -->
<item name="appWidgetPadding">16dp</item>
</style>
</resources> </resources>

View File

@@ -0,0 +1,14 @@
<?xml version="1.0" encoding="utf-8"?>
<appwidget-provider xmlns:android="http://schemas.android.com/apk/res/android"
android:description="@string/app_widget_description"
android:initialKeyguardLayout="@layout/battery_widget"
android:initialLayout="@layout/battery_widget"
android:minWidth="40dp"
android:minHeight="40dp"
android:previewImage="@drawable/example_appwidget_preview"
android:previewLayout="@layout/battery_widget"
android:resizeMode="horizontal|vertical"
android:targetCellWidth="1"
android:targetCellHeight="1"
android:updatePeriodMillis="86400000"
android:widgetCategory="home_screen|keyguard" />