Update: 2026-07-04 15:39:18
This commit is contained in:
@@ -18,6 +18,7 @@
|
||||
<uses-permission android:name="android.permission.WAKE_LOCK" />
|
||||
<uses-permission android:name="android.permission.SCHEDULE_EXACT_ALARM" />
|
||||
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
|
||||
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
|
||||
|
||||
<uses-feature android:name="android.hardware.camera" />
|
||||
<uses-feature android:name="android.hardware.camera.autofocus" />
|
||||
|
||||
+311
@@ -0,0 +1,311 @@
|
||||
package com.siro.siro_rider
|
||||
|
||||
import android.app.Notification
|
||||
import android.app.NotificationChannel
|
||||
import android.app.NotificationManager
|
||||
import android.app.PendingIntent
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.graphics.BitmapFactory
|
||||
import android.os.Build
|
||||
import androidx.core.app.NotificationCompat
|
||||
import io.flutter.plugin.common.MethodChannel
|
||||
|
||||
/**
|
||||
* Android 16 Progress Style Live Update Plugin
|
||||
*
|
||||
* يستخدم NotificationCompat مع setProgressStyle لتوفير
|
||||
* تجربة Live Update متقدمة على Android 16 (API 36) والأجهزة الأحدث.
|
||||
*
|
||||
* الميزات:
|
||||
* - شريط تقدم متحرك (Progress Style)
|
||||
* - تحديث مباشر للوقت المتبقي
|
||||
* - دعم الحالات المختلفة (بحث، في الطريق، وصل، رحلة جارية)
|
||||
* - إشعار ثابت (Ongoing) لا يمكن إلغاؤه أثناء الرحلة
|
||||
*/
|
||||
class AndroidLiveUpdatePlugin(private val context: Context) {
|
||||
|
||||
companion object {
|
||||
private const val CHANNEL_ID = "siro_live_ride_progress"
|
||||
private const val CHANNEL_NAME = "Siro Live Ride Progress"
|
||||
private const val CHANNEL_DESC = "Live progress updates for your current Siro ride"
|
||||
private const val NOTIFICATION_ID = 999
|
||||
private const val SUMMARY_NOTIFICATION_ID = 1000
|
||||
|
||||
// Ride status constants
|
||||
private const val STATUS_SEARCHING = "searching"
|
||||
private const val STATUS_DRIVER_ON_WAY = "driver_on_way"
|
||||
private const val STATUS_DRIVER_ARRIVED = "driver_arrived"
|
||||
private const val STATUS_IN_PROGRESS = "in_progress"
|
||||
private const val STATUS_FINISHED = "finished"
|
||||
}
|
||||
|
||||
private val notificationManager: NotificationManager =
|
||||
context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
|
||||
|
||||
private var currentProgress: Int = 0
|
||||
private var currentMaxProgress: Int = 100
|
||||
private var currentStatus: String = STATUS_SEARCHING
|
||||
private var currentTitle: String = ""
|
||||
private var currentBody: String = ""
|
||||
|
||||
init {
|
||||
createNotificationChannel()
|
||||
}
|
||||
|
||||
/**
|
||||
* إنشاء قناة الإشعارات المخصصة للرحلة
|
||||
* مع دعم Android 16 Progress Style
|
||||
*/
|
||||
private fun createNotificationChannel() {
|
||||
val channel = NotificationChannel(
|
||||
CHANNEL_ID,
|
||||
CHANNEL_NAME,
|
||||
NotificationManager.IMPORTANCE_LOW // Low importance for progress style
|
||||
).apply {
|
||||
description = CHANNEL_DESC
|
||||
setShowBadge(false)
|
||||
enableVibration(false)
|
||||
enableLights(false)
|
||||
// Android 16: دعم التقدم المستمر
|
||||
if (Build.VERSION.SDK_INT >= 36) {
|
||||
// يمكن إضافة خصائص Android 16 هنا
|
||||
}
|
||||
}
|
||||
notificationManager.createNotificationChannel(channel)
|
||||
}
|
||||
|
||||
/**
|
||||
* بدء التحديث المباشر للرحلة
|
||||
* يعرض إشعاراً ثابتاً مع شريط تقدم
|
||||
*/
|
||||
fun startLiveUpdate(
|
||||
rideId: String,
|
||||
driverName: String,
|
||||
etaText: String,
|
||||
progress: Double,
|
||||
carDetails: String
|
||||
) {
|
||||
currentStatus = STATUS_DRIVER_ON_WAY
|
||||
currentTitle = "🚗 السائق في الطريق إليك"
|
||||
currentBody = "$driverName • $carDetails • $etaText"
|
||||
currentProgress = (progress * 100).toInt().coerceIn(0, 100)
|
||||
currentMaxProgress = 100
|
||||
|
||||
showNotification(
|
||||
title = currentTitle,
|
||||
body = currentBody,
|
||||
progress = currentProgress,
|
||||
maxProgress = currentMaxProgress,
|
||||
indeterminate = false,
|
||||
ongoing = true
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* تحديث التقدم والوقت المتبقي
|
||||
* يستخدم Progress Style مع animate للتحديثات
|
||||
*/
|
||||
fun updateProgress(progress: Double, etaText: String) {
|
||||
currentProgress = (progress * 100).toInt().coerceIn(0, 100)
|
||||
currentBody = when (currentStatus) {
|
||||
STATUS_DRIVER_ON_WAY -> {
|
||||
val parts = currentBody.split(" • ")
|
||||
if (parts.size >= 3) {
|
||||
"${parts[0]} • ${parts[1]} • $etaText"
|
||||
} else {
|
||||
"$currentBody • $etaText"
|
||||
}
|
||||
}
|
||||
STATUS_IN_PROGRESS -> "المتبقي تقريبًا: $etaText"
|
||||
else -> currentBody
|
||||
}
|
||||
|
||||
showNotification(
|
||||
title = currentTitle,
|
||||
body = currentBody,
|
||||
progress = currentProgress,
|
||||
maxProgress = currentMaxProgress,
|
||||
indeterminate = false,
|
||||
ongoing = true
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* تحديث حالة الرحلة بالكامل
|
||||
* يستخدم لتغيير الحالة بين (في الطريق، وصل، رحلة جارية)
|
||||
*/
|
||||
fun updateStatus(
|
||||
status: String,
|
||||
driverName: String,
|
||||
etaText: String,
|
||||
progress: Double,
|
||||
carDetails: String
|
||||
) {
|
||||
currentStatus = status
|
||||
currentProgress = (progress * 100).toInt().coerceIn(0, 100)
|
||||
|
||||
when (status) {
|
||||
STATUS_SEARCHING -> {
|
||||
currentTitle = "🔍 جاري البحث عن سائق"
|
||||
currentBody = etaText
|
||||
showNotification(
|
||||
title = currentTitle,
|
||||
body = currentBody,
|
||||
progress = 0,
|
||||
maxProgress = 0,
|
||||
indeterminate = true,
|
||||
ongoing = true
|
||||
)
|
||||
}
|
||||
STATUS_DRIVER_ON_WAY -> {
|
||||
currentTitle = "🚗 السائق في الطريق إليك"
|
||||
currentBody = "$driverName • $carDetails • $etaText"
|
||||
showNotification(
|
||||
title = currentTitle,
|
||||
body = currentBody,
|
||||
progress = currentProgress,
|
||||
maxProgress = 100,
|
||||
indeterminate = false,
|
||||
ongoing = true
|
||||
)
|
||||
}
|
||||
STATUS_DRIVER_ARRIVED -> {
|
||||
currentTitle = "📍 السائق وصل"
|
||||
currentBody = "الرجاء التوجّه لمقابلة $driverName عند نقطة الالتقاء"
|
||||
showNotification(
|
||||
title = currentTitle,
|
||||
body = currentBody,
|
||||
progress = 100,
|
||||
maxProgress = 100,
|
||||
indeterminate = false,
|
||||
ongoing = true
|
||||
)
|
||||
}
|
||||
STATUS_IN_PROGRESS -> {
|
||||
currentTitle = "🚀 الرحلة جارية الآن"
|
||||
currentBody = "المتبقي تقريبًا: $etaText"
|
||||
showNotification(
|
||||
title = currentTitle,
|
||||
body = currentBody,
|
||||
progress = currentProgress,
|
||||
maxProgress = 100,
|
||||
indeterminate = false,
|
||||
ongoing = true
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* إنهاء التحديث المباشر وإزالة الإشعار
|
||||
*/
|
||||
fun endLiveUpdate() {
|
||||
notificationManager.cancel(NOTIFICATION_ID)
|
||||
currentStatus = STATUS_FINISHED
|
||||
currentProgress = 0
|
||||
}
|
||||
|
||||
/**
|
||||
* إظهار الإشعار مع الإعدادات المناسبة
|
||||
*/
|
||||
private fun showNotification(
|
||||
title: String,
|
||||
body: String,
|
||||
progress: Int,
|
||||
maxProgress: Int,
|
||||
indeterminate: Boolean,
|
||||
ongoing: Boolean
|
||||
) {
|
||||
// Intent لفتح التطبيق عند النقر على الإشعار
|
||||
val intent = context.packageManager.getLaunchIntentForPackage(context.packageName)?.apply {
|
||||
flags = Intent.FLAG_ACTIVITY_SINGLE_TOP or Intent.FLAG_ACTIVITY_CLEAR_TOP
|
||||
putExtra("notification_ride", true)
|
||||
}
|
||||
|
||||
val pendingIntent = PendingIntent.getActivity(
|
||||
context,
|
||||
0,
|
||||
intent,
|
||||
PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE
|
||||
)
|
||||
|
||||
val notificationBuilder = NotificationCompat.Builder(context, CHANNEL_ID)
|
||||
.setSmallIcon(android.R.drawable.ic_dialog_info)
|
||||
.setLargeIcon(
|
||||
BitmapFactory.decodeResource(
|
||||
context.resources,
|
||||
android.R.drawable.ic_menu_directions
|
||||
)
|
||||
)
|
||||
.setContentTitle(title)
|
||||
.setContentText(body)
|
||||
.setStyle(
|
||||
NotificationCompat.BigTextStyle()
|
||||
.bigText(body)
|
||||
)
|
||||
.setPriority(NotificationCompat.PRIORITY_LOW)
|
||||
.setOngoing(ongoing)
|
||||
.setAutoCancel(false)
|
||||
.setOnlyAlertOnce(true)
|
||||
.setContentIntent(pendingIntent)
|
||||
.setVisibility(NotificationCompat.VISIBILITY_PUBLIC)
|
||||
|
||||
// تطبيق Progress Style
|
||||
if (indeterminate) {
|
||||
notificationBuilder.setProgress(0, 0, true)
|
||||
} else {
|
||||
notificationBuilder.setProgress(maxProgress, progress, false)
|
||||
}
|
||||
|
||||
// Android 16: إضافة إجراءات سريعة للإشعار
|
||||
if (Build.VERSION.SDK_INT >= 36) {
|
||||
// يمكن إضافة إجراءات Android 16 هنا
|
||||
// مثل: إظهار الخريطة، مشاركة الرحلة، إلخ
|
||||
}
|
||||
|
||||
notificationManager.notify(NOTIFICATION_ID, notificationBuilder.build())
|
||||
}
|
||||
|
||||
/**
|
||||
* معالجة استدعاءات MethodChannel
|
||||
*/
|
||||
fun handleMethodCall(call: MethodChannel.MethodCall, result: MethodChannel.Result) {
|
||||
when (call.method) {
|
||||
"startLiveUpdate" -> {
|
||||
val rideId = call.argument<String>("rideId") ?: ""
|
||||
val driverName = call.argument<String>("driverName") ?: ""
|
||||
val etaText = call.argument<String>("etaText") ?: ""
|
||||
val progress = call.argument<Double>("progress") ?: 0.0
|
||||
val carDetails = call.argument<String>("carDetails") ?: ""
|
||||
startLiveUpdate(rideId, driverName, etaText, progress, carDetails)
|
||||
result.success(true)
|
||||
}
|
||||
"updateProgress" -> {
|
||||
val progress = call.argument<Double>("progress") ?: 0.0
|
||||
val etaText = call.argument<String>("etaText") ?: ""
|
||||
updateProgress(progress, etaText)
|
||||
result.success(true)
|
||||
}
|
||||
"updateStatus" -> {
|
||||
val status = call.argument<String>("status") ?: ""
|
||||
val driverName = call.argument<String>("driverName") ?: ""
|
||||
val etaText = call.argument<String>("etaText") ?: ""
|
||||
val progress = call.argument<Double>("progress") ?: 0.0
|
||||
val carDetails = call.argument<String>("carDetails") ?: ""
|
||||
updateStatus(status, driverName, etaText, progress, carDetails)
|
||||
result.success(true)
|
||||
}
|
||||
"endLiveUpdate" -> {
|
||||
endLiveUpdate()
|
||||
result.success(true)
|
||||
}
|
||||
"isSupported" -> {
|
||||
result.success(Build.VERSION.SDK_INT >= 30) // يدعم Android 11+
|
||||
}
|
||||
else -> {
|
||||
result.notImplemented()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -29,6 +29,10 @@ class MainActivity : FlutterFragmentActivity() {
|
||||
private val PIP_CHANNEL = "siro/pip"
|
||||
private var pipEnabled = false // هل الرحلة نشطة ويجب تفعيل PiP عند الخروج؟
|
||||
|
||||
// قناة Android Live Update (Android 16 Progress Style)
|
||||
private val LIVE_UPDATE_CHANNEL = "siro/android_live_update"
|
||||
private lateinit var liveUpdatePlugin: AndroidLiveUpdatePlugin
|
||||
|
||||
override fun configureFlutterEngine(flutterEngine: FlutterEngine) {
|
||||
super.configureFlutterEngine(flutterEngine)
|
||||
|
||||
@@ -72,6 +76,14 @@ class MainActivity : FlutterFragmentActivity() {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// -------- 3) قناة Android Live Update (Progress Style) --------
|
||||
liveUpdatePlugin = AndroidLiveUpdatePlugin(this)
|
||||
|
||||
MethodChannel(flutterEngine.dartExecutor.binaryMessenger, LIVE_UPDATE_CHANNEL)
|
||||
.setMethodCallHandler { call, result ->
|
||||
liveUpdatePlugin.handleMethodCall(call, result)
|
||||
}
|
||||
}
|
||||
|
||||
// -------- PiP Helper Methods --------
|
||||
|
||||
Reference in New Issue
Block a user