7/31/1
This commit is contained in:
@@ -54,8 +54,9 @@ android {
|
|||||||
// For more information, see: https://docs.flutter.dev/deployment/android#reviewing-the-gradle-build-configuration.
|
// For more information, see: https://docs.flutter.dev/deployment/android#reviewing-the-gradle-build-configuration.
|
||||||
minSdk = 23
|
minSdk = 23
|
||||||
targetSdk = flutter.targetSdkVersion
|
targetSdk = flutter.targetSdkVersion
|
||||||
versionCode = 64
|
versionCode = 68
|
||||||
versionName = '1.5.64'
|
versionName = '1.5.68'
|
||||||
|
multiDexEnabled =true
|
||||||
}
|
}
|
||||||
|
|
||||||
// defaultConfig {
|
// defaultConfig {
|
||||||
@@ -95,5 +96,6 @@ dependencies {
|
|||||||
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
|
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
|
||||||
// implementation platform('com.google.firebase:firebase-bom:32.1.1')
|
// implementation platform('com.google.firebase:firebase-bom:32.1.1')
|
||||||
implementation 'com.stripe:paymentsheet:20.47.0'
|
implementation 'com.stripe:paymentsheet:20.47.0'
|
||||||
|
coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:1.2.2'
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,6 +5,78 @@
|
|||||||
"storage_bucket": "ride-b1bd8.appspot.com"
|
"storage_bucket": "ride-b1bd8.appspot.com"
|
||||||
},
|
},
|
||||||
"client": [
|
"client": [
|
||||||
|
{
|
||||||
|
"client_info": {
|
||||||
|
"mobilesdk_app_id": "1:594687661098:android:8ec72f5f8b0b0ab8595f53",
|
||||||
|
"android_client_info": {
|
||||||
|
"package_name": "com.example.sefer_admin1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"oauth_client": [
|
||||||
|
{
|
||||||
|
"client_id": "594687661098-2u640akrb3k7sak5t0nqki6f4v6hq1bq.apps.googleusercontent.com",
|
||||||
|
"client_type": 3
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"api_key": [
|
||||||
|
{
|
||||||
|
"current_key": "AIzaSyCyfwRXTwSTLOFQSQgN5p7QZgGJVZnEKq0"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"services": {
|
||||||
|
"appinvite_service": {
|
||||||
|
"other_platform_oauth_client": [
|
||||||
|
{
|
||||||
|
"client_id": "594687661098-2hfb9gumub3j60vb7mqtq794k8spihuh.apps.googleusercontent.com",
|
||||||
|
"client_type": 3
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"client_id": "594687661098-8e26699cris2k3nj5msj1osi59it9kpf.apps.googleusercontent.com",
|
||||||
|
"client_type": 2,
|
||||||
|
"ios_info": {
|
||||||
|
"bundle_id": "com.mobileapp.store.ride"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"client_info": {
|
||||||
|
"mobilesdk_app_id": "1:594687661098:android:f81fcce13962121a595f53",
|
||||||
|
"android_client_info": {
|
||||||
|
"package_name": "com.example.service"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"oauth_client": [
|
||||||
|
{
|
||||||
|
"client_id": "594687661098-2u640akrb3k7sak5t0nqki6f4v6hq1bq.apps.googleusercontent.com",
|
||||||
|
"client_type": 3
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"api_key": [
|
||||||
|
{
|
||||||
|
"current_key": "AIzaSyCyfwRXTwSTLOFQSQgN5p7QZgGJVZnEKq0"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"services": {
|
||||||
|
"appinvite_service": {
|
||||||
|
"other_platform_oauth_client": [
|
||||||
|
{
|
||||||
|
"client_id": "594687661098-2hfb9gumub3j60vb7mqtq794k8spihuh.apps.googleusercontent.com",
|
||||||
|
"client_type": 3
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"client_id": "594687661098-8e26699cris2k3nj5msj1osi59it9kpf.apps.googleusercontent.com",
|
||||||
|
"client_type": 2,
|
||||||
|
"ios_info": {
|
||||||
|
"bundle_id": "com.mobileapp.store.ride"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"client_info": {
|
"client_info": {
|
||||||
"mobilesdk_app_id": "1:594687661098:android:683982cbf71fa423595f53",
|
"mobilesdk_app_id": "1:594687661098:android:683982cbf71fa423595f53",
|
||||||
@@ -12,6 +84,58 @@
|
|||||||
"package_name": "com.mobileapp.store.ride"
|
"package_name": "com.mobileapp.store.ride"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"oauth_client": [
|
||||||
|
{
|
||||||
|
"client_id": "594687661098-2dhoogl7be9phobfbu8bbg1sj567iv88.apps.googleusercontent.com",
|
||||||
|
"client_type": 1,
|
||||||
|
"android_info": {
|
||||||
|
"package_name": "com.mobileapp.store.ride",
|
||||||
|
"certificate_hash": "9bf3876c66e490f30cd7982fa972d8e52e0edbb6"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"client_id": "594687661098-4f8qbb4r223su1pphor33l3oe0ie2v46.apps.googleusercontent.com",
|
||||||
|
"client_type": 1,
|
||||||
|
"android_info": {
|
||||||
|
"package_name": "com.mobileapp.store.ride",
|
||||||
|
"certificate_hash": "765bbb7c5d30bc58a7ba44372db614d6bbe6e34d"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"client_id": "594687661098-2u640akrb3k7sak5t0nqki6f4v6hq1bq.apps.googleusercontent.com",
|
||||||
|
"client_type": 3
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"api_key": [
|
||||||
|
{
|
||||||
|
"current_key": "AIzaSyCyfwRXTwSTLOFQSQgN5p7QZgGJVZnEKq0"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"services": {
|
||||||
|
"appinvite_service": {
|
||||||
|
"other_platform_oauth_client": [
|
||||||
|
{
|
||||||
|
"client_id": "594687661098-2hfb9gumub3j60vb7mqtq794k8spihuh.apps.googleusercontent.com",
|
||||||
|
"client_type": 3
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"client_id": "594687661098-8e26699cris2k3nj5msj1osi59it9kpf.apps.googleusercontent.com",
|
||||||
|
"client_type": 2,
|
||||||
|
"ios_info": {
|
||||||
|
"bundle_id": "com.mobileapp.store.ride"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"client_info": {
|
||||||
|
"mobilesdk_app_id": "1:594687661098:android:b7ce96c17eb928ca595f53",
|
||||||
|
"android_client_info": {
|
||||||
|
"package_name": "com.sefer.driver"
|
||||||
|
}
|
||||||
|
},
|
||||||
"oauth_client": [
|
"oauth_client": [
|
||||||
{
|
{
|
||||||
"client_id": "594687661098-2u640akrb3k7sak5t0nqki6f4v6hq1bq.apps.googleusercontent.com",
|
"client_id": "594687661098-2u640akrb3k7sak5t0nqki6f4v6hq1bq.apps.googleusercontent.com",
|
||||||
@@ -49,6 +173,22 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"oauth_client": [
|
"oauth_client": [
|
||||||
|
{
|
||||||
|
"client_id": "594687661098-7mj1ngkp5aodosos3gsr4252qfemuvan.apps.googleusercontent.com",
|
||||||
|
"client_type": 1,
|
||||||
|
"android_info": {
|
||||||
|
"package_name": "com.sefer_driver",
|
||||||
|
"certificate_hash": "765bbb7c5d30bc58a7ba44372db614d6bbe6e34d"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"client_id": "594687661098-op7a9cpgm9dilgh8nl48bu6aor55f7qj.apps.googleusercontent.com",
|
||||||
|
"client_type": 1,
|
||||||
|
"android_info": {
|
||||||
|
"package_name": "com.sefer_driver",
|
||||||
|
"certificate_hash": "6f83a0b80b7e1b30b3dd42811cbc2c60ee931a3b"
|
||||||
|
}
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"client_id": "594687661098-2u640akrb3k7sak5t0nqki6f4v6hq1bq.apps.googleusercontent.com",
|
"client_id": "594687661098-2u640akrb3k7sak5t0nqki6f4v6hq1bq.apps.googleusercontent.com",
|
||||||
"client_type": 3
|
"client_type": 3
|
||||||
|
|||||||
@@ -1,10 +1,14 @@
|
|||||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:tools="http://schemas.android.com/tools">
|
||||||
|
|
||||||
<uses-permission android:name="android.permission.INTERNET" />
|
<uses-permission android:name="android.permission.INTERNET" />
|
||||||
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
|
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
|
||||||
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
|
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
|
||||||
<uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION" />
|
<uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION" />
|
||||||
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
|
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
|
||||||
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_LOCATION" />
|
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_LOCATION" />
|
||||||
|
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_TYPE_REMOTE_MESSAGING" />
|
||||||
<uses-permission android:name="android.permission.USE_BIOMETRIC" />
|
<uses-permission android:name="android.permission.USE_BIOMETRIC" />
|
||||||
<uses-permission android:name="android.permission.VIBRATE" />
|
<uses-permission android:name="android.permission.VIBRATE" />
|
||||||
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
|
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
|
||||||
@@ -14,32 +18,48 @@
|
|||||||
<uses-permission android:name="android.permission.WAKE_LOCK" />
|
<uses-permission android:name="android.permission.WAKE_LOCK" />
|
||||||
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
|
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
|
||||||
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
|
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
|
||||||
|
<uses-permission android:name="android.permission.READ_CONTACTS" />
|
||||||
|
<uses-permission android:name="android.permission.WRITE_CONTACTS" />
|
||||||
|
<!-- <uses-permission android:name="android.permission.USE_FULL_SCREEN_INTENT" /> -->
|
||||||
|
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />
|
||||||
|
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_SPECIAL_USE" />
|
||||||
|
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
|
||||||
|
|
||||||
<uses-feature android:name="android.hardware.camera" />
|
<uses-feature android:name="android.hardware.camera" />
|
||||||
<uses-feature android:name="android.hardware.camera.autofocus" />
|
<uses-feature android:name="android.hardware.camera.autofocus" />
|
||||||
|
|
||||||
|
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
|
||||||
|
|
||||||
<application
|
<application
|
||||||
|
android:allowBackup="true"
|
||||||
|
android:icon="@mipmap/launcher_icon"
|
||||||
android:label="Sefer Driver"
|
android:label="Sefer Driver"
|
||||||
android:name="${applicationName}"
|
android:theme="@style/LaunchTheme">
|
||||||
android:icon="@mipmap/launcher_icon">
|
<service
|
||||||
|
android:name=".MyFirebaseMessagingService"
|
||||||
|
android:exported="false"></service>
|
||||||
|
|
||||||
<activity
|
<activity
|
||||||
android:name=".MainActivity"
|
android:name=".MainActivity"
|
||||||
android:exported="true"
|
|
||||||
android:launchMode="singleTop"
|
|
||||||
android:theme="@style/LaunchTheme"
|
|
||||||
android:configChanges="orientation|keyboardHidden|keyboard|screenSize|smallestScreenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode"
|
android:configChanges="orientation|keyboardHidden|keyboard|screenSize|smallestScreenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode"
|
||||||
|
android:exported="true"
|
||||||
android:hardwareAccelerated="true"
|
android:hardwareAccelerated="true"
|
||||||
|
android:launchMode="singleTask"
|
||||||
|
android:theme="@style/LaunchTheme"
|
||||||
android:windowSoftInputMode="adjustResize">
|
android:windowSoftInputMode="adjustResize">
|
||||||
<meta-data
|
<meta-data
|
||||||
android:name="io.flutter.embedding.android.NormalTheme"
|
android:name="io.flutter.embedding.android.NormalTheme"
|
||||||
android:resource="@style/NormalTheme" />
|
android:resource="@style/NormalTheme" />
|
||||||
|
|
||||||
<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" />
|
||||||
<action android:name="com.example.action.APP_ACTION" />
|
|
||||||
<category android:name="android.intent.category.DEFAULT" />
|
|
||||||
</intent-filter>
|
</intent-filter>
|
||||||
</activity>
|
</activity>
|
||||||
|
<activity
|
||||||
|
android:name="com.yalantis.ucrop.UCropActivity"
|
||||||
|
android:screenOrientation="portrait"
|
||||||
|
android:theme="@style/Theme.AppCompat.Light.NoActionBar" />
|
||||||
<meta-data
|
<meta-data
|
||||||
android:name="com.google.android.geo.API_KEY"
|
android:name="com.google.android.geo.API_KEY"
|
||||||
android:value="@string/api_key" />
|
android:value="@string/api_key" />
|
||||||
@@ -49,28 +69,77 @@
|
|||||||
<meta-data
|
<meta-data
|
||||||
android:name="flutterEmbedding"
|
android:name="flutterEmbedding"
|
||||||
android:value="2" />
|
android:value="2" />
|
||||||
<activity
|
|
||||||
android:name="com.yalantis.ucrop.UCropActivity"
|
|
||||||
android:screenOrientation="portrait"
|
|
||||||
android:theme="@style/Theme.AppCompat.Light.NoActionBar" />
|
|
||||||
|
|
||||||
<!-- <service
|
|
||||||
android:name=".LocationUpdatesService"
|
|
||||||
android:exported="false" /> -->
|
|
||||||
<service
|
<service
|
||||||
android:name=".LocationUpdatesService"
|
android:name=".LocationUpdatesService"
|
||||||
android:foregroundServiceType="location"
|
android:exported="false"
|
||||||
android:exported="false"> <!-- or false, depending on whether it should be accessible by other
|
android:foregroundServiceType="location" />
|
||||||
apps -->
|
|
||||||
|
|
||||||
|
<!-- <service
|
||||||
|
android:name=".java.MyFirebaseMessagingService"
|
||||||
|
android:exported="false">
|
||||||
|
<intent-filter>
|
||||||
|
<action android:name="com.google.firebase.MESSAGING_EVENT" />
|
||||||
|
</intent-filter>
|
||||||
|
</service> -->
|
||||||
|
<service
|
||||||
|
android:name="com.google.firebase.messaging.FirebaseMessagingService"
|
||||||
|
android:exported="false"
|
||||||
|
tools:replace="android:exported">
|
||||||
|
<intent-filter>
|
||||||
|
<action android:name="com.google.firebase.MESSAGING_EVENT" />
|
||||||
|
</intent-filter>
|
||||||
</service>
|
</service>
|
||||||
|
<service
|
||||||
|
android:name="com.phan_tech.flutter_overlay_apps.OverlayService"
|
||||||
|
android:exported="false" />
|
||||||
|
<service
|
||||||
|
android:name="flutter.overlay.window.flutter_overlay_window.OverlayService"
|
||||||
|
android:foregroundServiceType="specialUse">
|
||||||
|
<property
|
||||||
|
android:name="android.app.PROPERTY_SPECIAL_USE_FGS_SUBTYPE"
|
||||||
|
android:value="explanation_for_special_use" />
|
||||||
|
</service>
|
||||||
|
|
||||||
<receiver
|
<receiver
|
||||||
android:name=".YourBroadcastReceiver"
|
android:name="com.google.firebase.iid.FirebaseInstanceIdReceiver"
|
||||||
android:exported="false"> <!-- or false, depending on whether it should be accessible by other
|
android:exported="true"
|
||||||
apps -->
|
android:permission="com.google.android.c2dm.permission.SEND">
|
||||||
<!-- <intent-filter>
|
<intent-filter>
|
||||||
<action android:name="com.example.ACTION" />
|
<action android:name="com.google.android.c2dm.intent.RECEIVE" />
|
||||||
</intent-filter> -->
|
|
||||||
|
<category android:name="com.sefer_driver" />
|
||||||
|
</intent-filter>
|
||||||
</receiver>
|
</receiver>
|
||||||
|
|
||||||
|
<service
|
||||||
|
android:name="com.dsaved.bubblehead.bubble.BubbleHeadService"
|
||||||
|
android:enabled="true"
|
||||||
|
android:exported="false">
|
||||||
|
<intent-filter>
|
||||||
|
<action android:name="intent.bring.app.to.foreground" />
|
||||||
|
|
||||||
|
<category android:name="android.intent.category.DEFAULT" />
|
||||||
|
</intent-filter>
|
||||||
|
</service>
|
||||||
|
|
||||||
|
<receiver
|
||||||
|
android:name="com.dexterous.flutterlocalnotifications.ScheduledNotificationReceiver"
|
||||||
|
android:exported="false" />
|
||||||
|
<receiver
|
||||||
|
android:name="com.dexterous.flutterlocalnotifications.ScheduledNotificationBootReceiver"
|
||||||
|
android:exported="false">
|
||||||
|
<intent-filter>
|
||||||
|
<action android:name="android.intent.action.BOOT_COMPLETED" />
|
||||||
|
<action android:name="android.intent.action.MY_PACKAGE_REPLACED" />
|
||||||
|
<action android:name="android.intent.action.QUICKBOOT_POWERON" />
|
||||||
|
<action android:name="com.htc.intent.action.QUICKBOOT_POWERON" />
|
||||||
|
</intent-filter>
|
||||||
|
</receiver>
|
||||||
|
<receiver
|
||||||
|
android:name=".YourBroadcastReceiver"
|
||||||
|
android:exported="false" />
|
||||||
</application>
|
</application>
|
||||||
|
|
||||||
</manifest>
|
</manifest>
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
package com.sefer_driver
|
||||||
|
|
||||||
|
import android.app.IntentService
|
||||||
|
import android.content.Intent
|
||||||
|
import android.content.Context
|
||||||
|
|
||||||
|
// TODO: Rename actions, choose action names that describe tasks that this
|
||||||
|
// IntentService can perform, e.g. ACTION_FETCH_NEW_ITEMS
|
||||||
|
private const val ACTION_FOO = "com.sefer_driver.action.FOO"
|
||||||
|
private const val ACTION_BAZ = "com.sefer_driver.action.BAZ"
|
||||||
|
|
||||||
|
// TODO: Rename parameters
|
||||||
|
private const val EXTRA_PARAM1 = "com.sefer_driver.extra.PARAM1"
|
||||||
|
private const val EXTRA_PARAM2 = "com.sefer_driver.extra.PARAM2"
|
||||||
|
|
||||||
@@ -1,8 +1,21 @@
|
|||||||
package com.sefer_driver
|
package com.sefer_driver
|
||||||
|
|
||||||
import io.flutter.embedding.android.FlutterActivity
|
import android.content.Intent
|
||||||
|
|
||||||
import io.flutter.embedding.android.FlutterFragmentActivity
|
import io.flutter.embedding.android.FlutterFragmentActivity
|
||||||
|
import io.flutter.embedding.engine.FlutterEngine
|
||||||
|
|
||||||
class MainActivity: FlutterFragmentActivity() {
|
class MainActivity : FlutterFragmentActivity() {
|
||||||
}
|
override fun configureFlutterEngine(flutterEngine: FlutterEngine) {
|
||||||
|
try {
|
||||||
|
super.configureFlutterEngine(MyApplication.flutterEngine)
|
||||||
|
} catch (e: UninitializedPropertyAccessException) {
|
||||||
|
super.configureFlutterEngine(flutterEngine)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun bringAppToForeground() {
|
||||||
|
val intent = Intent(this, MainActivity::class.java)
|
||||||
|
intent.addFlags(Intent.FLAG_ACTIVITY_REORDER_TO_FRONT)
|
||||||
|
startActivity(intent)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -0,0 +1,41 @@
|
|||||||
|
package com.sefer_driver
|
||||||
|
|
||||||
|
import android.app.Application
|
||||||
|
import android.content.Intent
|
||||||
|
import io.flutter.embedding.engine.FlutterEngine
|
||||||
|
import io.flutter.embedding.engine.dart.DartExecutor
|
||||||
|
import io.flutter.plugin.common.MethodChannel
|
||||||
|
|
||||||
|
class MyApplication : Application() {
|
||||||
|
companion object {
|
||||||
|
lateinit var instance: MyApplication
|
||||||
|
private set
|
||||||
|
|
||||||
|
val flutterEngine: FlutterEngine by lazy {
|
||||||
|
FlutterEngine(instance).apply {
|
||||||
|
dartExecutor.executeDartEntrypoint(DartExecutor.DartEntrypoint.createDefault())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onCreate() {
|
||||||
|
super.onCreate()
|
||||||
|
instance = this
|
||||||
|
|
||||||
|
MethodChannel(flutterEngine.dartExecutor.binaryMessenger, "com.sefer_driver/app_lifecycle")
|
||||||
|
.setMethodCallHandler { call, result ->
|
||||||
|
if (call.method == "bringAppToForeground") {
|
||||||
|
bringAppToForeground()
|
||||||
|
result.success(null)
|
||||||
|
} else {
|
||||||
|
result.notImplemented()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun bringAppToForeground() {
|
||||||
|
val intent = Intent(applicationContext, MainActivity::class.java)
|
||||||
|
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_REORDER_TO_FRONT)
|
||||||
|
startActivity(intent)
|
||||||
|
}
|
||||||
|
}
|
||||||
21
android/app/src/main/res/layout/custom_notification.xml
Normal file
21
android/app/src/main/res/layout/custom_notification.xml
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:padding="16dp">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/notification_title"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:textAppearance="@style/TextAppearance.AppCompat.Title" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/notification_message"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="8dp"
|
||||||
|
android:textAppearance="@style/TextAppearance.AppCompat.Body1" />
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
@@ -3,4 +3,6 @@
|
|||||||
<!-- <string name="default_notification_channel_id">ride_channel</string> -->
|
<!-- <string name="default_notification_channel_id">ride_channel</string> -->
|
||||||
<string name="default_notification_channel_id">default_channel</string>
|
<string name="default_notification_channel_id">default_channel</string>
|
||||||
<string name="api_key">AIzaSyCyfwRXTwSTLOFQSQgN5p7QZgGJVZnEKq0</string>
|
<string name="api_key">AIzaSyCyfwRXTwSTLOFQSQgN5p7QZgGJVZnEKq0</string>
|
||||||
|
<string name="channel_name">FCM Notifications</string>
|
||||||
|
<string name="channel_description">Notifications from Firebase Cloud Messaging</string>
|
||||||
</resources>
|
</resources>
|
||||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
Before Width: | Height: | Size: 4.6 MiB After Width: | Height: | Size: 1.7 MiB |
BIN
assets/images/logo1.png
Normal file
BIN
assets/images/logo1.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 493 KiB |
Binary file not shown.
BIN
assets/order.mp3
Normal file
BIN
assets/order.mp3
Normal file
Binary file not shown.
7
bubble-master/.gitignore
vendored
Executable file
7
bubble-master/.gitignore
vendored
Executable file
@@ -0,0 +1,7 @@
|
|||||||
|
.DS_Store
|
||||||
|
.dart_tool/
|
||||||
|
|
||||||
|
.packages
|
||||||
|
.pub/
|
||||||
|
|
||||||
|
build/
|
||||||
10
bubble-master/.metadata
Executable file
10
bubble-master/.metadata
Executable file
@@ -0,0 +1,10 @@
|
|||||||
|
# This file tracks properties of this Flutter project.
|
||||||
|
# Used by Flutter tool to assess capabilities and perform upgrades etc.
|
||||||
|
#
|
||||||
|
# This file should be version controlled and should not be manually edited.
|
||||||
|
|
||||||
|
version:
|
||||||
|
revision: f4abaa0735eba4dfd8f33f73363911d63931fe03
|
||||||
|
channel: stable
|
||||||
|
|
||||||
|
project_type: plugin
|
||||||
14
bubble-master/CHANGELOG.md
Executable file
14
bubble-master/CHANGELOG.md
Executable file
@@ -0,0 +1,14 @@
|
|||||||
|
## 0.0.1
|
||||||
|
|
||||||
|
* Created and implemented startBubbleHead and closeBubbleHead usecases for bubble head package
|
||||||
|
|
||||||
|
|
||||||
|
## 0.0.2
|
||||||
|
* Fix read-me documentation
|
||||||
|
|
||||||
|
## 0.0.3
|
||||||
|
* Fix read-me documentation (added `Buy me a coffee link`)
|
||||||
|
|
||||||
|
## 0.0.4
|
||||||
|
* Added optional parameter to enable or disable send-app-to-background
|
||||||
|
* Updated documentation
|
||||||
21
bubble-master/LICENSE
Executable file
21
bubble-master/LICENSE
Executable file
@@ -0,0 +1,21 @@
|
|||||||
|
MIT License
|
||||||
|
|
||||||
|
Copyright (c) 2021 chrisoftech
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
||||||
128
bubble-master/README.md
Executable file
128
bubble-master/README.md
Executable file
@@ -0,0 +1,128 @@
|
|||||||
|
# bubble_head
|
||||||
|
|
||||||
|
|
||||||
|
A flutter plugin to enable you launch a bubble while putting your application to background and upon clicking the bubble brings your application back to foreground
|
||||||
|
|
||||||
|
## Getting Started
|
||||||
|
### Add dependency
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
dependencies:
|
||||||
|
bubble_head: ^0.0.4
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
### Add in android-manifest file (**../main/AndroidManifest.xml**)
|
||||||
|
|
||||||
|
If you are unsure on where to do this, you can reference the example project AndroidManifest.xml file [here](example/android/app/src/main/AndroidManifest.xml)
|
||||||
|
|
||||||
|
|
||||||
|
Add `SYSTEM_ALERT_WINDOW` permission in manifest
|
||||||
|
```xml
|
||||||
|
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW"/>
|
||||||
|
```
|
||||||
|
|
||||||
|
NOTE: For best UX practices, you should request for `SYSTEM_ALERT_WINDOW` permission on your application launch (if permission `status` is not granted)
|
||||||
|
To request for permission, we advise the use of this [package](https://pub.dev/packages/permission_handler)
|
||||||
|
|
||||||
|
|
||||||
|
Add `intent-filter` in activity tag
|
||||||
|
|
||||||
|
```xml
|
||||||
|
<intent-filter>
|
||||||
|
<action android:name="intent.bring.app.to.foreground" />
|
||||||
|
<category android:name="android.intent.category.DEFAULT" />
|
||||||
|
</intent-filter>
|
||||||
|
```
|
||||||
|
|
||||||
|
Add `service` in application tag
|
||||||
|
```xml
|
||||||
|
<service
|
||||||
|
android:name="com.dsaved.bubblehead.bubble.BubbleHeadService"
|
||||||
|
android:enabled="true"
|
||||||
|
android:exported="false"/>
|
||||||
|
```
|
||||||
|
|
||||||
|
### Note: To set bubble icon, create `assets/images` folder path and add your png icon with name `icon.png` to the directory (ensure to import assets in your `pubspec.yaml` file)
|
||||||
|
|
||||||
|
**GIF illustration**
|
||||||
|
|
||||||
|
[](example/assets/images/bubble_head_example.gif "Bubble-head example")
|
||||||
|
|
||||||
|
### Examples
|
||||||
|
|
||||||
|
**To start bubble**
|
||||||
|
[This puts your app in background and can be re-launched (brought to foreground) on tap of the bubble]
|
||||||
|
|
||||||
|
```dart
|
||||||
|
Bubble _bubble = new Bubble();
|
||||||
|
|
||||||
|
Future<void> startBubbleHead() async {
|
||||||
|
|
||||||
|
try {
|
||||||
|
await _bubble.startBubbleHead();
|
||||||
|
} on PlatformException {
|
||||||
|
print('Failed to call startBubbleHead');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**To stop/close bubble**
|
||||||
|
|
||||||
|
```dart
|
||||||
|
Bubble _bubble = new Bubble();
|
||||||
|
|
||||||
|
Future<void> stopBubbleHead() async {
|
||||||
|
|
||||||
|
try {
|
||||||
|
await _bubble.stopBubbleHead();
|
||||||
|
} on PlatformException {
|
||||||
|
print('Failed to call stopBubbleHead');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
You can prevent the default action of putting your application in background when starting `bubble_head` by setting `sendAppToBackground` parameter when starting bubble head (if you choose to use another means of sending your application to background)
|
||||||
|
|
||||||
|
```dart
|
||||||
|
Bubble _bubble = new Bubble();
|
||||||
|
|
||||||
|
Future<void> startBubbleHead() async {
|
||||||
|
|
||||||
|
try {
|
||||||
|
// this will only display the bubble-head without sending the application to background
|
||||||
|
await _bubble.startBubbleHead(sendAppToBackground: false);
|
||||||
|
} on PlatformException {
|
||||||
|
print('Failed to call startBubbleHead');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
**Other parameters**
|
||||||
|
(You can choose to tweak **optional** parameters when initializing bubble)
|
||||||
|
|
||||||
|
|
||||||
|
```dart
|
||||||
|
Bubble({
|
||||||
|
this.shouldBounce = true,
|
||||||
|
this.allowDragToClose = true,
|
||||||
|
this.showCloseButton = false,
|
||||||
|
});
|
||||||
|
```
|
||||||
|
```dart
|
||||||
|
Bubble().startBubbleHead(sendAppToBackground: true);
|
||||||
|
```
|
||||||
|
|
||||||
|
**Parameter Definition**
|
||||||
|
- shouldBounce - Defaults to `True`
|
||||||
|
(Adds animation to bubble-head)
|
||||||
|
- allowDragToClose - Defaults to `True`
|
||||||
|
(Enables dragging bubble to bottom screen to exit)
|
||||||
|
- showCloseButton - Defaults to `False`
|
||||||
|
(Adds a close button icon to the bubble-head)
|
||||||
|
- sendAppToBackground - Defaults to `True`
|
||||||
|
(Sends application to background)
|
||||||
|
|
||||||
|
## [Buy me a Coffee](https://www.buymeacoffee.com/dsaved)
|
||||||
|
|
||||||
8
bubble-master/android/.gitignore
vendored
Executable file
8
bubble-master/android/.gitignore
vendored
Executable file
@@ -0,0 +1,8 @@
|
|||||||
|
*.iml
|
||||||
|
.gradle
|
||||||
|
/local.properties
|
||||||
|
/.idea/workspace.xml
|
||||||
|
/.idea/libraries
|
||||||
|
.DS_Store
|
||||||
|
/build
|
||||||
|
/captures
|
||||||
35
bubble-master/android/build.gradle
Executable file
35
bubble-master/android/build.gradle
Executable file
@@ -0,0 +1,35 @@
|
|||||||
|
group 'com.dsaved.bubblehead.bubble'
|
||||||
|
version '1.0'
|
||||||
|
|
||||||
|
buildscript {
|
||||||
|
repositories {
|
||||||
|
google()
|
||||||
|
mavenCentral()
|
||||||
|
}
|
||||||
|
|
||||||
|
dependencies {
|
||||||
|
classpath 'com.android.tools.build:gradle:4.1.3'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
rootProject.allprojects {
|
||||||
|
repositories {
|
||||||
|
google()
|
||||||
|
mavenCentral()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
apply plugin: 'com.android.library'
|
||||||
|
|
||||||
|
android {
|
||||||
|
namespace 'com.dsaved.bubblehead.bubble'
|
||||||
|
compileSdkVersion 30
|
||||||
|
|
||||||
|
defaultConfig {
|
||||||
|
minSdkVersion 16
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
dependencies {
|
||||||
|
implementation 'com.google.android.material:material:1.4.0'
|
||||||
|
}
|
||||||
3
bubble-master/android/gradle.properties
Executable file
3
bubble-master/android/gradle.properties
Executable file
@@ -0,0 +1,3 @@
|
|||||||
|
org.gradle.jvmargs=-Xmx1536M
|
||||||
|
android.useAndroidX=true
|
||||||
|
android.enableJetifier=true
|
||||||
5
bubble-master/android/gradle/wrapper/gradle-wrapper.properties
vendored
Executable file
5
bubble-master/android/gradle/wrapper/gradle-wrapper.properties
vendored
Executable file
@@ -0,0 +1,5 @@
|
|||||||
|
distributionBase=GRADLE_USER_HOME
|
||||||
|
distributionPath=wrapper/dists
|
||||||
|
zipStoreBase=GRADLE_USER_HOME
|
||||||
|
zipStorePath=wrapper/dists
|
||||||
|
distributionUrl=https\://services.gradle.org/distributions/gradle-6.7-all.zip
|
||||||
1
bubble-master/android/settings.gradle
Executable file
1
bubble-master/android/settings.gradle
Executable file
@@ -0,0 +1 @@
|
|||||||
|
rootProject.name = 'bubble'
|
||||||
4
bubble-master/android/src/main/AndroidManifest.xml
Executable file
4
bubble-master/android/src/main/AndroidManifest.xml
Executable file
@@ -0,0 +1,4 @@
|
|||||||
|
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
package="com.dsaved.bubblehead.bubble">
|
||||||
|
|
||||||
|
</manifest>
|
||||||
@@ -0,0 +1,456 @@
|
|||||||
|
package com.dsaved.bubblehead.bubble;
|
||||||
|
|
||||||
|
import android.annotation.SuppressLint;
|
||||||
|
import android.app.Service;
|
||||||
|
import android.content.ComponentName;
|
||||||
|
import android.content.Context;
|
||||||
|
import android.content.Intent;
|
||||||
|
import android.content.res.Configuration;
|
||||||
|
import android.graphics.Bitmap;
|
||||||
|
import android.graphics.BitmapFactory;
|
||||||
|
import android.graphics.PixelFormat;
|
||||||
|
import android.graphics.Point;
|
||||||
|
import android.os.Build;
|
||||||
|
import android.os.CountDownTimer;
|
||||||
|
import android.os.Handler;
|
||||||
|
import android.os.IBinder;
|
||||||
|
import android.util.Base64;
|
||||||
|
import android.view.Gravity;
|
||||||
|
import android.view.LayoutInflater;
|
||||||
|
import android.view.MotionEvent;
|
||||||
|
import android.view.View;
|
||||||
|
import android.view.WindowManager;
|
||||||
|
import android.widget.ImageView;
|
||||||
|
|
||||||
|
public class BubbleHeadService extends Service implements View.OnClickListener {
|
||||||
|
private WindowManager mWindowManager;
|
||||||
|
private View mFloatingWidgetView;
|
||||||
|
private ImageView remove_image_view;
|
||||||
|
private final Point szWindow = new Point();
|
||||||
|
private View removeFloatingWidgetView;
|
||||||
|
private static boolean showCloseButton = false, _bounce = true, _dragToClose = true, _sendAppToBackground = true;
|
||||||
|
private boolean _continueToSnap = false;
|
||||||
|
|
||||||
|
private int x_init_cord, y_init_cord, x_init_margin, y_init_margin;
|
||||||
|
static Bitmap _image;
|
||||||
|
|
||||||
|
// Set the value for showing close button to true or false
|
||||||
|
public static void shouldShowCloseButton(Boolean show) {
|
||||||
|
showCloseButton = show;
|
||||||
|
}
|
||||||
|
|
||||||
|
// set to true to enable bouncing
|
||||||
|
public static void bounce(Boolean bounce) {
|
||||||
|
_bounce = bounce;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set the value for drag to close to enable dragging bubble to close
|
||||||
|
public static void dragToClose(Boolean dragToClose) {
|
||||||
|
_dragToClose = dragToClose;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void sendAppToBackground(Boolean sendAppToBackground) {
|
||||||
|
_sendAppToBackground = sendAppToBackground;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void startService(Context activity, String image) {
|
||||||
|
byte[] decodedBytes = Base64.decode(image, Base64.DEFAULT);
|
||||||
|
_image = BitmapFactory.decodeByteArray(decodedBytes, 0, decodedBytes.length);
|
||||||
|
|
||||||
|
// send application to background if this is true
|
||||||
|
if (_sendAppToBackground) {
|
||||||
|
Intent i = new Intent();
|
||||||
|
i.setAction(Intent.ACTION_MAIN);
|
||||||
|
i.addCategory(Intent.CATEGORY_HOME);
|
||||||
|
activity.startActivity(i);
|
||||||
|
}
|
||||||
|
|
||||||
|
Intent intent = new Intent(activity, BubbleHeadService.class);
|
||||||
|
activity.startService(intent);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void stopService(Context activity) {
|
||||||
|
Intent intent = new Intent(activity, BubbleHeadService.class);
|
||||||
|
activity.stopService(intent);
|
||||||
|
}
|
||||||
|
|
||||||
|
// create an empty constructor
|
||||||
|
public BubbleHeadService() {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public IBinder onBind(Intent intent) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressLint({"ClickableViewAccessibility", "InflateParams"})
|
||||||
|
@Override
|
||||||
|
public void onCreate() {
|
||||||
|
super.onCreate();
|
||||||
|
// init WindowManager
|
||||||
|
mWindowManager = (WindowManager) getSystemService(WINDOW_SERVICE);
|
||||||
|
getWindowManagerDefaultDisplay();
|
||||||
|
|
||||||
|
// Init LayoutInflater
|
||||||
|
LayoutInflater inflater = (LayoutInflater) getSystemService(LAYOUT_INFLATER_SERVICE);
|
||||||
|
|
||||||
|
// Inflate the removing view layout we created
|
||||||
|
removeFloatingWidgetView = inflater.inflate(R.layout.bubble_head_remove_widget, null);
|
||||||
|
|
||||||
|
// Add the view to the window.
|
||||||
|
WindowManager.LayoutParams paramRemove;
|
||||||
|
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) {
|
||||||
|
paramRemove = new WindowManager.LayoutParams(
|
||||||
|
WindowManager.LayoutParams.WRAP_CONTENT,
|
||||||
|
WindowManager.LayoutParams.WRAP_CONTENT,
|
||||||
|
WindowManager.LayoutParams.TYPE_PHONE,
|
||||||
|
WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE,
|
||||||
|
PixelFormat.TRANSLUCENT);
|
||||||
|
} else {
|
||||||
|
paramRemove = new WindowManager.LayoutParams(
|
||||||
|
WindowManager.LayoutParams.WRAP_CONTENT,
|
||||||
|
WindowManager.LayoutParams.WRAP_CONTENT,
|
||||||
|
WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY,
|
||||||
|
WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE,
|
||||||
|
PixelFormat.TRANSLUCENT);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Specify the view position
|
||||||
|
paramRemove.gravity = Gravity.TOP | Gravity.LEFT;
|
||||||
|
|
||||||
|
// Initially the Removing widget view is not visible, so set visibility to GONE
|
||||||
|
removeFloatingWidgetView.setVisibility(View.GONE);
|
||||||
|
remove_image_view = (ImageView) removeFloatingWidgetView.findViewById(R.id.remove_img);
|
||||||
|
|
||||||
|
// Add the view to the window
|
||||||
|
mWindowManager.addView(removeFloatingWidgetView, paramRemove);
|
||||||
|
|
||||||
|
// Inflate the floating view layout we created
|
||||||
|
mFloatingWidgetView = inflater.inflate(R.layout.layout_bubble_head, null);
|
||||||
|
|
||||||
|
// Add the view to the window.
|
||||||
|
WindowManager.LayoutParams params;
|
||||||
|
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) {
|
||||||
|
params = new WindowManager.LayoutParams(
|
||||||
|
WindowManager.LayoutParams.WRAP_CONTENT,
|
||||||
|
WindowManager.LayoutParams.WRAP_CONTENT,
|
||||||
|
WindowManager.LayoutParams.TYPE_PHONE,
|
||||||
|
WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE,
|
||||||
|
PixelFormat.TRANSLUCENT);
|
||||||
|
} else {
|
||||||
|
params = new WindowManager.LayoutParams(
|
||||||
|
WindowManager.LayoutParams.WRAP_CONTENT,
|
||||||
|
WindowManager.LayoutParams.WRAP_CONTENT,
|
||||||
|
WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY,
|
||||||
|
WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE,
|
||||||
|
PixelFormat.TRANSLUCENT);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Specify the view position
|
||||||
|
params.gravity = Gravity.TOP | Gravity.LEFT;
|
||||||
|
|
||||||
|
// Initially view will be added to top-left corner, you change x-y coordinates according to your need
|
||||||
|
params.x = 0;
|
||||||
|
params.y = 100;
|
||||||
|
|
||||||
|
// Add the view to the window
|
||||||
|
mWindowManager.addView(mFloatingWidgetView, params);
|
||||||
|
|
||||||
|
//set image to chatHead
|
||||||
|
ImageView chatHeadImage = mFloatingWidgetView.findViewById(R.id.chat_head_profile_iv);
|
||||||
|
chatHeadImage.setImageBitmap(_image);
|
||||||
|
|
||||||
|
// find id of close image button
|
||||||
|
ImageView closeBubbleHead = mFloatingWidgetView.findViewById(R.id.close_bubble_head);
|
||||||
|
closeBubbleHead.setOnClickListener(this);
|
||||||
|
if (!showCloseButton) {
|
||||||
|
closeBubbleHead.setVisibility(View.GONE);
|
||||||
|
}
|
||||||
|
|
||||||
|
implementTouchListenerToFloatingWidgetView();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void getWindowManagerDefaultDisplay() {
|
||||||
|
mWindowManager.getDefaultDisplay().getSize(szWindow);
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressLint("ClickableViewAccessibility")
|
||||||
|
private void implementTouchListenerToFloatingWidgetView() {
|
||||||
|
_continueToSnap = true;
|
||||||
|
// Drag and move chat head using user's touch action.
|
||||||
|
mFloatingWidgetView.findViewById(R.id.root_container);
|
||||||
|
mFloatingWidgetView.setOnTouchListener(new View.OnTouchListener() {
|
||||||
|
long time_start = 0, time_end = 0;
|
||||||
|
|
||||||
|
boolean isLongClick = false;
|
||||||
|
boolean inBounded = false;
|
||||||
|
int remove_img_width = 0, remove_img_height = 0;
|
||||||
|
|
||||||
|
final Handler handler_longClick = new Handler();
|
||||||
|
final Runnable runnable_longClick = new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
isLongClick = true;
|
||||||
|
removeFloatingWidgetView.setVisibility(View.VISIBLE);
|
||||||
|
onFloatingWidgetLongClick();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean onTouch(View v, MotionEvent event) {
|
||||||
|
WindowManager.LayoutParams layoutParams = (WindowManager.LayoutParams) mFloatingWidgetView.getLayoutParams();
|
||||||
|
|
||||||
|
int x_cord = (int) event.getRawX();
|
||||||
|
int y_cord = (int) event.getRawY();
|
||||||
|
|
||||||
|
int x_cord_Destination, y_cord_Destination;
|
||||||
|
|
||||||
|
switch (event.getAction()) {
|
||||||
|
case MotionEvent.ACTION_DOWN:
|
||||||
|
time_start = System.currentTimeMillis();
|
||||||
|
|
||||||
|
if (_dragToClose) {
|
||||||
|
handler_longClick.postDelayed(runnable_longClick, 100);
|
||||||
|
}
|
||||||
|
remove_img_width = remove_image_view.getLayoutParams().width;
|
||||||
|
remove_img_height = remove_image_view.getLayoutParams().height;
|
||||||
|
|
||||||
|
x_init_cord = x_cord;
|
||||||
|
y_init_cord = y_cord;
|
||||||
|
|
||||||
|
// remember the initial position.
|
||||||
|
x_init_margin = layoutParams.x;
|
||||||
|
y_init_margin = layoutParams.y;
|
||||||
|
return true;
|
||||||
|
case MotionEvent.ACTION_UP:
|
||||||
|
isLongClick = false;
|
||||||
|
removeFloatingWidgetView.setVisibility(View.GONE);
|
||||||
|
remove_image_view.getLayoutParams().height = remove_img_height;
|
||||||
|
remove_image_view.getLayoutParams().width = remove_img_width;
|
||||||
|
|
||||||
|
if (_dragToClose) {
|
||||||
|
handler_longClick.removeCallbacks(runnable_longClick);
|
||||||
|
}
|
||||||
|
|
||||||
|
// If user drag and drop the floating widget view
|
||||||
|
// into remove view then stop the service
|
||||||
|
if (inBounded) {
|
||||||
|
stopSelf();
|
||||||
|
inBounded = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Difference between initial coordinate and current coordinate
|
||||||
|
int x_diff = x_cord - x_init_cord;
|
||||||
|
int y_diff = y_cord - y_init_cord;
|
||||||
|
|
||||||
|
// check if action move is little as move happen on view with just a tap
|
||||||
|
if (Math.abs(x_diff) < 5 && Math.abs(y_diff) < 5) {
|
||||||
|
time_end = System.currentTimeMillis();
|
||||||
|
// only perform click if time is less than 200ms
|
||||||
|
if ((time_end - time_start) < 200) {
|
||||||
|
onFloatingWidgetClick();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
y_cord_Destination = y_init_margin + y_diff;
|
||||||
|
|
||||||
|
int barHeight = getStatusBarHeight();
|
||||||
|
if (y_cord_Destination < 0) {
|
||||||
|
y_cord_Destination = 0;
|
||||||
|
} else if (y_cord_Destination + (mFloatingWidgetView.getHeight() + barHeight) > szWindow.y) {
|
||||||
|
y_cord_Destination = szWindow.y - (mFloatingWidgetView.getHeight() + barHeight);
|
||||||
|
}
|
||||||
|
|
||||||
|
layoutParams.y = y_cord_Destination;
|
||||||
|
|
||||||
|
inBounded = false;
|
||||||
|
|
||||||
|
// reset position
|
||||||
|
resetPosition(x_cord);
|
||||||
|
return true;
|
||||||
|
case MotionEvent.ACTION_MOVE:
|
||||||
|
int x_diff_move = x_cord - x_init_cord;
|
||||||
|
int y_diff_move = y_cord - y_init_cord;
|
||||||
|
|
||||||
|
x_cord_Destination = x_init_margin + x_diff_move;
|
||||||
|
y_cord_Destination = y_init_margin + y_diff_move;
|
||||||
|
|
||||||
|
// If user long click the floating view, update remove view
|
||||||
|
if (isLongClick) {
|
||||||
|
int x_bound_left = szWindow.x / 2 - (int) (remove_img_width * 1.5);
|
||||||
|
int x_bound_right = szWindow.x / 2 + (int) (remove_img_width * 1.5);
|
||||||
|
int y_bound_top = szWindow.y - (int) (remove_img_height * 1.5);
|
||||||
|
|
||||||
|
// If Floating view comes under Remove View update Window Manager
|
||||||
|
if ((x_cord >= x_bound_left && x_cord <= x_bound_right) && y_cord >= y_bound_top) {
|
||||||
|
inBounded = true;
|
||||||
|
|
||||||
|
int x_cord_remove = (int) ((szWindow.x - (remove_img_height * 1.5)) / 2);
|
||||||
|
int y_cord_remove = (int) (szWindow.y - ((remove_img_width * 1.5) + getStatusBarHeight()));
|
||||||
|
|
||||||
|
if (remove_image_view.getLayoutParams().height == remove_img_height) {
|
||||||
|
WindowManager.LayoutParams param_remove = (WindowManager.LayoutParams) removeFloatingWidgetView.getLayoutParams();
|
||||||
|
param_remove.x = x_cord_remove;
|
||||||
|
param_remove.y = y_cord_remove;
|
||||||
|
|
||||||
|
mWindowManager.updateViewLayout(removeFloatingWidgetView, param_remove);
|
||||||
|
}
|
||||||
|
|
||||||
|
layoutParams.x = x_cord_remove + (Math.abs(removeFloatingWidgetView.getWidth() - mFloatingWidgetView.getWidth())) / 2;
|
||||||
|
layoutParams.y = y_cord_remove + (Math.abs(removeFloatingWidgetView.getHeight() - mFloatingWidgetView.getHeight())) / 2;
|
||||||
|
|
||||||
|
// Update the layout with new X & Y coordinate
|
||||||
|
mWindowManager.updateViewLayout(mFloatingWidgetView, layoutParams);
|
||||||
|
break;
|
||||||
|
} else {
|
||||||
|
// If Floating window gets out of the Remove view update Remove view again
|
||||||
|
inBounded = false;
|
||||||
|
remove_image_view.getLayoutParams().height = remove_img_height;
|
||||||
|
remove_image_view.getLayoutParams().width = remove_img_width;
|
||||||
|
// onFloatingWidgetClick();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
layoutParams.x = x_cord_Destination;
|
||||||
|
layoutParams.y = y_cord_Destination;
|
||||||
|
|
||||||
|
// Update the layout with new X & Y coordinate
|
||||||
|
mWindowManager.updateViewLayout(mFloatingWidgetView, layoutParams);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onClick(View v) {
|
||||||
|
int id = v.getId();
|
||||||
|
if (id == R.id.close_bubble_head) {
|
||||||
|
stopSelf();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void onFloatingWidgetLongClick() {
|
||||||
|
// Get remove Floating view params
|
||||||
|
WindowManager.LayoutParams removeParams = (WindowManager.LayoutParams) removeFloatingWidgetView.getLayoutParams();
|
||||||
|
|
||||||
|
// get x and y coordinates of remove view
|
||||||
|
int x_cord = (szWindow.x - removeFloatingWidgetView.getWidth()) / 2;
|
||||||
|
int y_cord = szWindow.y - (removeFloatingWidgetView.getHeight() + getStatusBarHeight());
|
||||||
|
|
||||||
|
removeParams.x = x_cord;
|
||||||
|
removeParams.y = y_cord;
|
||||||
|
|
||||||
|
// Update Remove view params
|
||||||
|
mWindowManager.updateViewLayout(removeFloatingWidgetView, removeParams);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reset position of Floating Widget view on dragging
|
||||||
|
private void resetPosition(int x_cord_now) {
|
||||||
|
if (_continueToSnap) {
|
||||||
|
if (x_cord_now <= szWindow.x / 2) {
|
||||||
|
snapToLeft(x_cord_now);
|
||||||
|
} else {
|
||||||
|
snapToRight(x_cord_now);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void snapToLeft(final int current_x_cord) {
|
||||||
|
final int x = szWindow.x - current_x_cord;
|
||||||
|
new CountDownTimer(500, 5) {
|
||||||
|
final WindowManager.LayoutParams mParams = (WindowManager.LayoutParams) mFloatingWidgetView.getLayoutParams();
|
||||||
|
|
||||||
|
public void onTick(long t) {
|
||||||
|
long step = (500 - t) / 5;
|
||||||
|
mParams.x = -(int) (current_x_cord * current_x_cord * step);
|
||||||
|
if (_bounce) {
|
||||||
|
mParams.x = -(int) (double) bounceValue(step, x);
|
||||||
|
}
|
||||||
|
mWindowManager.updateViewLayout(mFloatingWidgetView, mParams);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void onFinish() {
|
||||||
|
mParams.x = 0;
|
||||||
|
mWindowManager.updateViewLayout(mFloatingWidgetView, mParams);
|
||||||
|
}
|
||||||
|
}.start();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void snapToRight(final int current_x_cord) {
|
||||||
|
new CountDownTimer(500, 5) {
|
||||||
|
final WindowManager.LayoutParams mParams = (WindowManager.LayoutParams) mFloatingWidgetView.getLayoutParams();
|
||||||
|
|
||||||
|
public void onTick(long t) {
|
||||||
|
long step = (500 - t) / 5;
|
||||||
|
mParams.x = (int) (szWindow.x + (current_x_cord * current_x_cord * step) - mFloatingWidgetView.getWidth());
|
||||||
|
if (_bounce) {
|
||||||
|
mParams.x = szWindow.x + (int) (double) bounceValue(step, current_x_cord) - mFloatingWidgetView.getWidth();
|
||||||
|
}
|
||||||
|
mWindowManager.updateViewLayout(mFloatingWidgetView, mParams);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void onFinish() {
|
||||||
|
mParams.x = szWindow.x - mFloatingWidgetView.getWidth();
|
||||||
|
mWindowManager.updateViewLayout(mFloatingWidgetView, mParams);
|
||||||
|
}
|
||||||
|
}.start();
|
||||||
|
}
|
||||||
|
|
||||||
|
private double bounceValue(long step, long scale) {
|
||||||
|
return scale * Math.exp(-0.15 * step) * Math.cos(0.08 * step);
|
||||||
|
}
|
||||||
|
|
||||||
|
private int getStatusBarHeight() {
|
||||||
|
return (int) Math.ceil(25 * getApplicationContext().getResources().getDisplayMetrics().density);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onConfigurationChanged(Configuration newConfig) {
|
||||||
|
super.onConfigurationChanged(newConfig);
|
||||||
|
|
||||||
|
getWindowManagerDefaultDisplay();
|
||||||
|
WindowManager.LayoutParams layoutParams = (WindowManager.LayoutParams) mFloatingWidgetView.getLayoutParams();
|
||||||
|
|
||||||
|
if (newConfig.orientation == Configuration.ORIENTATION_LANDSCAPE) {
|
||||||
|
if (layoutParams.y + (mFloatingWidgetView.getHeight() + getStatusBarHeight()) > szWindow.y) {
|
||||||
|
layoutParams.y = szWindow.y - (mFloatingWidgetView.getHeight() + getStatusBarHeight());
|
||||||
|
mWindowManager.updateViewLayout(mFloatingWidgetView, layoutParams);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (layoutParams.x != 0 && layoutParams.x < szWindow.x) {
|
||||||
|
resetPosition(szWindow.x);
|
||||||
|
}
|
||||||
|
} else if (newConfig.orientation == Configuration.ORIENTATION_PORTRAIT) {
|
||||||
|
if (layoutParams.x > szWindow.x) {
|
||||||
|
resetPosition(szWindow.x);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void onFloatingWidgetClick() {
|
||||||
|
_continueToSnap = false;
|
||||||
|
// bring the application to front
|
||||||
|
Intent it = new Intent("intent.bring.app.to.foreground");
|
||||||
|
it.setComponent(new ComponentName(getPackageName(), getApplicationContext().getPackageName() + ".MainActivity"));
|
||||||
|
it.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
||||||
|
getApplicationContext().startActivity(it);
|
||||||
|
|
||||||
|
// stop the service
|
||||||
|
stopSelf();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onDestroy() {
|
||||||
|
super.onDestroy();
|
||||||
|
if (mFloatingWidgetView != null) {
|
||||||
|
mWindowManager.removeView(mFloatingWidgetView);
|
||||||
|
}
|
||||||
|
if (removeFloatingWidgetView != null) {
|
||||||
|
mWindowManager.removeView(removeFloatingWidgetView);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,95 @@
|
|||||||
|
package com.dsaved.bubblehead.bubble;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.os.Build;
|
||||||
|
import android.provider.Settings;
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
import androidx.annotation.RequiresApi;
|
||||||
|
|
||||||
|
import io.flutter.embedding.engine.plugins.FlutterPlugin;
|
||||||
|
import io.flutter.embedding.engine.plugins.activity.ActivityAware;
|
||||||
|
import io.flutter.embedding.engine.plugins.activity.ActivityPluginBinding;
|
||||||
|
import io.flutter.plugin.common.MethodCall;
|
||||||
|
import io.flutter.plugin.common.MethodChannel;
|
||||||
|
import io.flutter.plugin.common.MethodChannel.MethodCallHandler;
|
||||||
|
import io.flutter.plugin.common.MethodChannel.Result;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* BubblePlugin
|
||||||
|
*/
|
||||||
|
public class BubblePlugin implements FlutterPlugin, MethodCallHandler, ActivityAware {
|
||||||
|
/// The MethodChannel that will the communication between Flutter and native Android
|
||||||
|
///
|
||||||
|
/// This local reference serves to register the plugin with the Flutter Engine and unregister it
|
||||||
|
/// when the Flutter Engine is detached from the Activity
|
||||||
|
private MethodChannel channel;
|
||||||
|
private Context activity;
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onAttachedToEngine(@NonNull FlutterPluginBinding flutterPluginBinding) {
|
||||||
|
channel = new MethodChannel(flutterPluginBinding.getBinaryMessenger(), "com.dsaved.bubble.head");
|
||||||
|
channel.setMethodCallHandler(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
@RequiresApi(api = Build.VERSION_CODES.M)
|
||||||
|
@Override
|
||||||
|
public void onMethodCall(@NonNull MethodCall call, @NonNull Result result) {
|
||||||
|
if (call.method.equals("startBubbleHead")) {
|
||||||
|
startBubbleHead(result, call);
|
||||||
|
} else if (call.method.equals("stopBubbleHead")) {
|
||||||
|
BubbleHeadService.stopService(activity);
|
||||||
|
} else {
|
||||||
|
result.notImplemented();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@RequiresApi(api = Build.VERSION_CODES.M)
|
||||||
|
public void startBubbleHead(@NonNull Result result, MethodCall call) {
|
||||||
|
if (Settings.canDrawOverlays(activity)) {
|
||||||
|
boolean bounce = call.argument("bounce");
|
||||||
|
BubbleHeadService.bounce(bounce);
|
||||||
|
|
||||||
|
boolean showClose = call.argument("showClose");
|
||||||
|
BubbleHeadService.shouldShowCloseButton(showClose);
|
||||||
|
|
||||||
|
boolean dragToClose = call.argument("dragToClose");
|
||||||
|
BubbleHeadService.dragToClose(dragToClose);
|
||||||
|
|
||||||
|
boolean sendAppToBackground = call.argument("sendAppToBackground");
|
||||||
|
BubbleHeadService.sendAppToBackground(sendAppToBackground);
|
||||||
|
|
||||||
|
String imageByte = call.argument("image");
|
||||||
|
BubbleHeadService.startService(activity, imageByte);
|
||||||
|
} else {
|
||||||
|
//Permission is not available
|
||||||
|
result.error("EPERMNOTGRANTED", "permission not available", "Please request permission for: android.permission.SYSTEM_ALERT_WINDOW. with out this permission you cannot launch the bubble head.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onDetachedFromEngine(@NonNull FlutterPluginBinding binding) {
|
||||||
|
channel.setMethodCallHandler(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onAttachedToActivity(@NonNull ActivityPluginBinding binding) {
|
||||||
|
this.activity = binding.getActivity();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onDetachedFromActivityForConfigChanges() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onReattachedToActivityForConfigChanges(@NonNull ActivityPluginBinding binding) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onDetachedFromActivity() {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
6
bubble-master/android/src/main/res/drawable/circle_shape.xml
Executable file
6
bubble-master/android/src/main/res/drawable/circle_shape.xml
Executable file
@@ -0,0 +1,6 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<shape xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:shape="oval">
|
||||||
|
<solid android:color="@android:color/black" />
|
||||||
|
|
||||||
|
</shape>
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="24dp"
|
||||||
|
android:height="24dp"
|
||||||
|
android:viewportWidth="24.0"
|
||||||
|
android:viewportHeight="24.0">
|
||||||
|
<path
|
||||||
|
android:fillColor="#FF000000"
|
||||||
|
android:pathData="M19,12h-2v3h-3v2h5v-5zM7,9h3L10,7L5,7v5h2L7,9zM21,3L3,3c-1.1,0 -2,0.9 -2,2v14c0,1.1 0.9,2 2,2h18c1.1,0 2,-0.9 2,-2L23,5c0,-1.1 -0.9,-2 -2,-2zM21,19.01L3,19.01L3,4.99h18v14.02z"/>
|
||||||
|
</vector>
|
||||||
9
bubble-master/android/src/main/res/drawable/ic_close_black_24dp.xml
Executable file
9
bubble-master/android/src/main/res/drawable/ic_close_black_24dp.xml
Executable file
@@ -0,0 +1,9 @@
|
|||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="24dp"
|
||||||
|
android:height="24dp"
|
||||||
|
android:viewportWidth="24.0"
|
||||||
|
android:viewportHeight="24.0">
|
||||||
|
<path
|
||||||
|
android:fillColor="#FF000000"
|
||||||
|
android:pathData="M19,6.41L17.59,5 12,10.59 6.41,5 5,6.41 10.59,12 5,17.59 6.41,19 12,13.41 17.59,19 19,17.59 13.41,12z"/>
|
||||||
|
</vector>
|
||||||
9
bubble-master/android/src/main/res/drawable/ic_close_white_24dp.xml
Executable file
9
bubble-master/android/src/main/res/drawable/ic_close_white_24dp.xml
Executable file
@@ -0,0 +1,9 @@
|
|||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="24dp"
|
||||||
|
android:height="24dp"
|
||||||
|
android:viewportHeight="24.0"
|
||||||
|
android:viewportWidth="24.0">
|
||||||
|
<path
|
||||||
|
android:fillColor="#FFFFFFFF"
|
||||||
|
android:pathData="M19,6.41L17.59,5 12,10.59 6.41,5 5,6.41 10.59,12 5,17.59 6.41,19 12,13.41 17.59,19 19,17.59 13.41,12z" />
|
||||||
|
</vector>
|
||||||
BIN
bubble-master/android/src/main/res/drawable/ic_launcher.png
Executable file
BIN
bubble-master/android/src/main/res/drawable/ic_launcher.png
Executable file
Binary file not shown.
|
After Width: | Height: | Size: 3.3 KiB |
9
bubble-master/android/src/main/res/drawable/white_circle_shape.xml
Executable file
9
bubble-master/android/src/main/res/drawable/white_circle_shape.xml
Executable file
@@ -0,0 +1,9 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<shape xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:shape="oval">
|
||||||
|
<stroke
|
||||||
|
android:width="1dp"
|
||||||
|
android:color="@android:color/white" />
|
||||||
|
|
||||||
|
|
||||||
|
</shape>
|
||||||
16
bubble-master/android/src/main/res/layout/bubble_head_remove_widget.xml
Executable file
16
bubble-master/android/src/main/res/layout/bubble_head_remove_widget.xml
Executable file
@@ -0,0 +1,16 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:id="@+id/remove_relativelayout"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:padding="10dp">
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
android:id="@+id/remove_img"
|
||||||
|
android:layout_width="60dp"
|
||||||
|
android:layout_height="60dp"
|
||||||
|
android:background="@drawable/white_circle_shape"
|
||||||
|
android:padding="10dp"
|
||||||
|
android:src="@drawable/ic_close_white_24dp" />
|
||||||
|
|
||||||
|
</RelativeLayout>
|
||||||
45
bubble-master/android/src/main/res/layout/layout_bubble_head.xml
Executable file
45
bubble-master/android/src/main/res/layout/layout_bubble_head.xml
Executable file
@@ -0,0 +1,45 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content">
|
||||||
|
|
||||||
|
<!-- Root container of Floating Widget View -->
|
||||||
|
<RelativeLayout
|
||||||
|
android:id="@+id/root_container"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content">
|
||||||
|
|
||||||
|
<!-- View while view is collapsed -->
|
||||||
|
|
||||||
|
<RelativeLayout
|
||||||
|
android:id="@+id/collapse_view"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:visibility="visible">
|
||||||
|
|
||||||
|
<!-- ImageView of floating widget -->
|
||||||
|
<ImageView
|
||||||
|
android:id="@+id/chat_head_profile_iv"
|
||||||
|
android:layout_width="70dp"
|
||||||
|
android:layout_height="70dp"
|
||||||
|
android:layout_marginTop="8dp"
|
||||||
|
android:src="@drawable/ic_launcher"
|
||||||
|
tools:ignore="ContentDescription" />
|
||||||
|
|
||||||
|
<!-- Close button to close Floating Widget View -->
|
||||||
|
<ImageView
|
||||||
|
android:id="@+id/close_bubble_head"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginLeft="50dp"
|
||||||
|
android:layout_marginTop="5dp"
|
||||||
|
android:padding="1dp"
|
||||||
|
android:background="@drawable/circle_shape"
|
||||||
|
android:src="@drawable/ic_close_white_24dp"
|
||||||
|
tools:ignore="ContentDescription" />
|
||||||
|
</RelativeLayout>
|
||||||
|
|
||||||
|
</RelativeLayout>
|
||||||
|
</FrameLayout>
|
||||||
16
bubble-master/android/src/main/res/values/styles.xml
Executable file
16
bubble-master/android/src/main/res/values/styles.xml
Executable file
@@ -0,0 +1,16 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<resources>
|
||||||
|
|
||||||
|
<style name="ShapeAppearance.Image.PILL" parent="">
|
||||||
|
<item name="cornerFamily">rounded</item>
|
||||||
|
<item name="cornerSize">50%</item>
|
||||||
|
</style>
|
||||||
|
|
||||||
|
<style name="ShapeAppearance.Image.Top.PILL" parent="">
|
||||||
|
<item name="cornerSizeTopLeft">6dp</item>
|
||||||
|
<item name="cornerFamilyTopLeft">rounded</item>
|
||||||
|
<item name="cornerSizeTopRight">6dp</item>
|
||||||
|
<item name="cornerFamilyTopRight">rounded</item>
|
||||||
|
</style>
|
||||||
|
|
||||||
|
</resources>
|
||||||
39
bubble-master/lib/bubble.dart
Executable file
39
bubble-master/lib/bubble.dart
Executable file
@@ -0,0 +1,39 @@
|
|||||||
|
import 'dart:async';
|
||||||
|
import 'dart:convert';
|
||||||
|
import 'dart:typed_data';
|
||||||
|
|
||||||
|
import 'package:flutter/services.dart';
|
||||||
|
|
||||||
|
class Bubble {
|
||||||
|
static const MethodChannel _channel =
|
||||||
|
const MethodChannel('com.dsaved.bubble.head');
|
||||||
|
|
||||||
|
bool shouldBounce;
|
||||||
|
bool showCloseButton;
|
||||||
|
bool allowDragToClose;
|
||||||
|
|
||||||
|
Bubble({
|
||||||
|
this.shouldBounce = true,
|
||||||
|
this.allowDragToClose = true,
|
||||||
|
this.showCloseButton = false,
|
||||||
|
});
|
||||||
|
|
||||||
|
/// puts app in background and shows floaty-bubble head
|
||||||
|
Future<void> startBubbleHead({bool sendAppToBackground = true}) async {
|
||||||
|
ByteData bytes = await rootBundle.load('assets/images/logo1.png');
|
||||||
|
var buffer = bytes.buffer;
|
||||||
|
var encodedImage = base64.encode(Uint8List.view(buffer));
|
||||||
|
await _channel.invokeMethod('startBubbleHead', {
|
||||||
|
"image": encodedImage,
|
||||||
|
"bounce": shouldBounce,
|
||||||
|
"showClose": showCloseButton,
|
||||||
|
"dragToClose": allowDragToClose,
|
||||||
|
"sendAppToBackground": sendAppToBackground,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/// closes floaty-bubble head
|
||||||
|
Future<void> stopBubbleHead() async {
|
||||||
|
await _channel.invokeMethod('stopBubbleHead');
|
||||||
|
}
|
||||||
|
}
|
||||||
189
bubble-master/pubspec.lock
Executable file
189
bubble-master/pubspec.lock
Executable file
@@ -0,0 +1,189 @@
|
|||||||
|
# Generated by pub
|
||||||
|
# See https://dart.dev/tools/pub/glossary#lockfile
|
||||||
|
packages:
|
||||||
|
async:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: async
|
||||||
|
sha256: "947bfcf187f74dbc5e146c9eb9c0f10c9f8b30743e341481c1e2ed3ecc18c20c"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "2.11.0"
|
||||||
|
boolean_selector:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: boolean_selector
|
||||||
|
sha256: "6cfb5af12253eaf2b368f07bacc5a80d1301a071c73360d746b7f2e32d762c66"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "2.1.1"
|
||||||
|
characters:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: characters
|
||||||
|
sha256: "04a925763edad70e8443c99234dc3328f442e811f1d8fd1a72f1c8ad0f69a605"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "1.3.0"
|
||||||
|
clock:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: clock
|
||||||
|
sha256: cb6d7f03e1de671e34607e909a7213e31d7752be4fb66a86d29fe1eb14bfb5cf
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "1.1.1"
|
||||||
|
collection:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: collection
|
||||||
|
sha256: ee67cb0715911d28db6bf4af1026078bd6f0128b07a5f66fb2ed94ec6783c09a
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "1.18.0"
|
||||||
|
fake_async:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: fake_async
|
||||||
|
sha256: "511392330127add0b769b75a987850d136345d9227c6b94c96a04cf4a391bf78"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "1.3.1"
|
||||||
|
flutter:
|
||||||
|
dependency: "direct main"
|
||||||
|
description: flutter
|
||||||
|
source: sdk
|
||||||
|
version: "0.0.0"
|
||||||
|
flutter_test:
|
||||||
|
dependency: "direct dev"
|
||||||
|
description: flutter
|
||||||
|
source: sdk
|
||||||
|
version: "0.0.0"
|
||||||
|
leak_tracker:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: leak_tracker
|
||||||
|
sha256: "7f0df31977cb2c0b88585095d168e689669a2cc9b97c309665e3386f3e9d341a"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "10.0.4"
|
||||||
|
leak_tracker_flutter_testing:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: leak_tracker_flutter_testing
|
||||||
|
sha256: "06e98f569d004c1315b991ded39924b21af84cf14cc94791b8aea337d25b57f8"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "3.0.3"
|
||||||
|
leak_tracker_testing:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: leak_tracker_testing
|
||||||
|
sha256: "6ba465d5d76e67ddf503e1161d1f4a6bc42306f9d66ca1e8f079a47290fb06d3"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "3.0.1"
|
||||||
|
matcher:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: matcher
|
||||||
|
sha256: d2323aa2060500f906aa31a895b4030b6da3ebdcc5619d14ce1aada65cd161cb
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "0.12.16+1"
|
||||||
|
material_color_utilities:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: material_color_utilities
|
||||||
|
sha256: "0e0a020085b65b6083975e499759762399b4475f766c21668c4ecca34ea74e5a"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "0.8.0"
|
||||||
|
meta:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: meta
|
||||||
|
sha256: "7687075e408b093f36e6bbf6c91878cc0d4cd10f409506f7bc996f68220b9136"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "1.12.0"
|
||||||
|
path:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: path
|
||||||
|
sha256: "087ce49c3f0dc39180befefc60fdb4acd8f8620e5682fe2476afd0b3688bb4af"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "1.9.0"
|
||||||
|
sky_engine:
|
||||||
|
dependency: transitive
|
||||||
|
description: flutter
|
||||||
|
source: sdk
|
||||||
|
version: "0.0.99"
|
||||||
|
source_span:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: source_span
|
||||||
|
sha256: "53e943d4206a5e30df338fd4c6e7a077e02254531b138a15aec3bd143c1a8b3c"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "1.10.0"
|
||||||
|
stack_trace:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: stack_trace
|
||||||
|
sha256: "73713990125a6d93122541237550ee3352a2d84baad52d375a4cad2eb9b7ce0b"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "1.11.1"
|
||||||
|
stream_channel:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: stream_channel
|
||||||
|
sha256: ba2aa5d8cc609d96bbb2899c28934f9e1af5cddbd60a827822ea467161eb54e7
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "2.1.2"
|
||||||
|
string_scanner:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: string_scanner
|
||||||
|
sha256: "556692adab6cfa87322a115640c11f13cb77b3f076ddcc5d6ae3c20242bedcde"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "1.2.0"
|
||||||
|
term_glyph:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: term_glyph
|
||||||
|
sha256: a29248a84fbb7c79282b40b8c72a1209db169a2e0542bce341da992fe1bc7e84
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "1.2.1"
|
||||||
|
test_api:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: test_api
|
||||||
|
sha256: "9955ae474176f7ac8ee4e989dadfb411a58c30415bcfb648fa04b2b8a03afa7f"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "0.7.0"
|
||||||
|
vector_math:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: vector_math
|
||||||
|
sha256: "80b3257d1492ce4d091729e3a67a60407d227c27241d6927be0130c98e741803"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "2.1.4"
|
||||||
|
vm_service:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: vm_service
|
||||||
|
sha256: "3923c89304b715fb1eb6423f017651664a03bf5f4b29983627c4da791f74a4ec"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "14.2.1"
|
||||||
|
sdks:
|
||||||
|
dart: ">=3.3.0 <4.0.0"
|
||||||
|
flutter: ">=3.18.0-18.0.pre.54"
|
||||||
63
bubble-master/pubspec.yaml
Executable file
63
bubble-master/pubspec.yaml
Executable file
@@ -0,0 +1,63 @@
|
|||||||
|
name: bubble_head
|
||||||
|
description: A flutter plugin to enable you launch a bubble while putting your application to background and upon clicking the bubble brings your application back to foreground
|
||||||
|
version: 0.0.4
|
||||||
|
homepage: https://github.com/chrisoftech/bubble
|
||||||
|
# publish_to:
|
||||||
|
|
||||||
|
environment:
|
||||||
|
sdk: ">=2.12.0 <3.0.0"
|
||||||
|
flutter: ">=1.20.0"
|
||||||
|
|
||||||
|
dependencies:
|
||||||
|
flutter:
|
||||||
|
sdk: flutter
|
||||||
|
|
||||||
|
dev_dependencies:
|
||||||
|
flutter_test:
|
||||||
|
sdk: flutter
|
||||||
|
|
||||||
|
# For information on the generic Dart part of this file, see the
|
||||||
|
# following page: https://dart.dev/tools/pub/pubspec
|
||||||
|
|
||||||
|
# The following section is specific to Flutter.
|
||||||
|
flutter:
|
||||||
|
# This section identifies this Flutter project as a plugin project.
|
||||||
|
# The 'pluginClass' and Android 'package' identifiers should not ordinarily
|
||||||
|
# be modified. They are used by the tooling to maintain consistency when
|
||||||
|
# adding or updating assets for this project.
|
||||||
|
plugin:
|
||||||
|
platforms:
|
||||||
|
android:
|
||||||
|
package: com.dsaved.bubblehead.bubble
|
||||||
|
pluginClass: BubblePlugin
|
||||||
|
|
||||||
|
# To add assets to your plugin package, add an assets section, like this:
|
||||||
|
# assets:
|
||||||
|
# - assets/images/
|
||||||
|
# - images/a_dot_ham.jpeg
|
||||||
|
#
|
||||||
|
# For details regarding assets in packages, see
|
||||||
|
# https://flutter.dev/assets-and-images/#from-packages
|
||||||
|
#
|
||||||
|
# An image asset can refer to one or more resolution-specific "variants", see
|
||||||
|
# https://flutter.dev/assets-and-images/#resolution-aware.
|
||||||
|
|
||||||
|
# To add custom fonts to your plugin package, add a fonts section here,
|
||||||
|
# in this "flutter" section. Each entry in this list should have a
|
||||||
|
# "family" key with the font family name, and a "fonts" key with a
|
||||||
|
# list giving the asset and other descriptors for the font. For
|
||||||
|
# example:
|
||||||
|
# fonts:
|
||||||
|
# - family: Schyler
|
||||||
|
# fonts:
|
||||||
|
# - asset: fonts/Schyler-Regular.ttf
|
||||||
|
# - asset: fonts/Schyler-Italic.ttf
|
||||||
|
# style: italic
|
||||||
|
# - family: Trajan Pro
|
||||||
|
# fonts:
|
||||||
|
# - asset: fonts/TrajanPro.ttf
|
||||||
|
# - asset: fonts/TrajanPro_Bold.ttf
|
||||||
|
# weight: 700
|
||||||
|
#
|
||||||
|
# For details regarding fonts in packages, see
|
||||||
|
# https://flutter.dev/custom-fonts/#from-packages
|
||||||
@@ -1 +1 @@
|
|||||||
{"functions":[{"source":"functions","codebase":"default","ignore":["node_modules",".git","firebase-debug.log","firebase-debug.*.log"],"predeploy":["npm --prefix \"$RESOURCE_DIR\" run lint"]}],"flutter":{"platforms":{"android":{"default":{"projectId":"ride-b1bd8","appId":"1:594687661098:android:46557bd4f534b5bb595f53","fileOutput":"android/app/google-services.json"}},"ios":{"default":{"projectId":"ride-b1bd8","appId":"1:594687661098:ios:4f236057ba0383b0595f53","uploadDebugSymbols":false,"fileOutput":"ios/Runner/GoogleService-Info.plist"}},"dart":{"lib/firebase_options.dart":{"projectId":"ride-b1bd8","configurations":{"android":"1:594687661098:android:46557bd4f534b5bb595f53","ios":"1:594687661098:ios:4f236057ba0383b0595f53"}}}}}}
|
{"functions":[{"source":"functions","codebase":"default","ignore":["node_modules",".git","firebase-debug.log","firebase-debug.*.log"],"predeploy":["npm --prefix \"$RESOURCE_DIR\" run lint"]}],"flutter":{"platforms":{"android":{"default":{"projectId":"ride-b1bd8","appId":"1:594687661098:android:b7ce96c17eb928ca595f53","fileOutput":"android/app/google-services.json"}},"ios":{"default":{"projectId":"ride-b1bd8","appId":"1:594687661098:ios:4f236057ba0383b0595f53","uploadDebugSymbols":false,"fileOutput":"ios/Runner/GoogleService-Info.plist"}},"dart":{"lib/firebase_options.dart":{"projectId":"ride-b1bd8","configurations":{"android":"1:594687661098:android:b7ce96c17eb928ca595f53","ios":"1:594687661098:ios:4f236057ba0383b0595f53","macos":"1:594687661098:ios:6f69eee1449be943595f53","web":"1:594687661098:web:62d8388476ec91ec595f53","windows":"1:594687661098:web:d9f43a2091395d87595f53"}}},"macos":{"default":{"projectId":"ride-b1bd8","appId":"1:594687661098:ios:6f69eee1449be943595f53","uploadDebugSymbols":false,"fileOutput":"macos/Runner/GoogleService-Info.plist"}}}}}
|
||||||
29
flutter_overlay_apps-main/.gitignore
vendored
Normal file
29
flutter_overlay_apps-main/.gitignore
vendored
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
# Miscellaneous
|
||||||
|
*.class
|
||||||
|
*.log
|
||||||
|
*.pyc
|
||||||
|
*.swp
|
||||||
|
.DS_Store
|
||||||
|
.atom/
|
||||||
|
.buildlog/
|
||||||
|
.history
|
||||||
|
.svn/
|
||||||
|
|
||||||
|
# IntelliJ related
|
||||||
|
*.iml
|
||||||
|
*.ipr
|
||||||
|
*.iws
|
||||||
|
.idea/
|
||||||
|
|
||||||
|
# The .vscode folder contains launch configuration and tasks you configure in
|
||||||
|
# VS Code which you may wish to be included in version control, so this line
|
||||||
|
# is commented out by default.
|
||||||
|
#.vscode/
|
||||||
|
|
||||||
|
# Flutter/Dart/Pub related
|
||||||
|
# Libraries should not include pubspec.lock, per https://dart.dev/guides/libraries/private-files#pubspeclock.
|
||||||
|
/pubspec.lock
|
||||||
|
**/doc/api/
|
||||||
|
.dart_tool/
|
||||||
|
.packages
|
||||||
|
build/
|
||||||
10
flutter_overlay_apps-main/.metadata
Normal file
10
flutter_overlay_apps-main/.metadata
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
# This file tracks properties of this Flutter project.
|
||||||
|
# Used by Flutter tool to assess capabilities and perform upgrades etc.
|
||||||
|
#
|
||||||
|
# This file should be version controlled and should not be manually edited.
|
||||||
|
|
||||||
|
version:
|
||||||
|
revision: b101bfe32f634566e7cb2791a9efe19cf8828b15
|
||||||
|
channel: beta
|
||||||
|
|
||||||
|
project_type: plugin
|
||||||
20
flutter_overlay_apps-main/CHANGELOG.md
Normal file
20
flutter_overlay_apps-main/CHANGELOG.md
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
## 1.2.0
|
||||||
|
|
||||||
|
* Upgraded gradle
|
||||||
|
* Added Back Button navigation action
|
||||||
|
|
||||||
|
## 1.1.2
|
||||||
|
|
||||||
|
* exposed overlay StreamController
|
||||||
|
|
||||||
|
## 1.0.2
|
||||||
|
|
||||||
|
* enabled keyboard input
|
||||||
|
|
||||||
|
## 1.0.1
|
||||||
|
|
||||||
|
* Update app from overlay fix
|
||||||
|
|
||||||
|
## 1.0.0
|
||||||
|
|
||||||
|
* Ininial release
|
||||||
21
flutter_overlay_apps-main/LICENSE
Normal file
21
flutter_overlay_apps-main/LICENSE
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
MIT License
|
||||||
|
|
||||||
|
Copyright (c) 2022, Eddie Genius
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
||||||
179
flutter_overlay_apps-main/README.md
Normal file
179
flutter_overlay_apps-main/README.md
Normal file
@@ -0,0 +1,179 @@
|
|||||||
|
# flutter_overlay_apps
|
||||||
|
|
||||||
|
Android plugin for displaying flutter app over other apps
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
Add dependency to pubspec.yaml file
|
||||||
|
|
||||||
|
|
||||||
|
### Android
|
||||||
|
You'll need to add the `SYSTEM_ALERT_WINDOW` permission and `OverlayService` to your Android Manifest.
|
||||||
|
```XML
|
||||||
|
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />
|
||||||
|
<application>
|
||||||
|
...
|
||||||
|
<service
|
||||||
|
android:name="com.phan_tech.flutter_overlay_apps.OverlayService"
|
||||||
|
android:exported="false" />
|
||||||
|
</application>
|
||||||
|
```
|
||||||
|
|
||||||
|
### Entry point
|
||||||
|
Inside `main.dart` create an entry point for your Overlay widget;
|
||||||
|
NOTE: `MaterialApp` is required
|
||||||
|
```dart
|
||||||
|
// overlay entry point
|
||||||
|
@pragma("vm:entry-point")
|
||||||
|
void showOverlay() {
|
||||||
|
runApp(const MaterialApp(
|
||||||
|
debugShowCheckedModeBanner: false,
|
||||||
|
home: Material(child: Text("My overlay"))
|
||||||
|
));
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
### Methods
|
||||||
|
To open an overlay, call `FlutterOverlayApps.showOverlay()`.
|
||||||
|
Default `height` & `width` is fill screen
|
||||||
|
|
||||||
|
```dart
|
||||||
|
FlutterOverlayApps.showOverlay(height: 300, width: 400, alignment: OverlayAlignment.center);
|
||||||
|
```
|
||||||
|
|
||||||
|
To close the overlay widget call
|
||||||
|
```dart
|
||||||
|
FlutterOverlayApps.closeOverlay();
|
||||||
|
```
|
||||||
|
To send data to and from Overlay widget, call
|
||||||
|
```dart
|
||||||
|
FlutterOverlayApps.sendDataToAndFromOverlay(<data>);
|
||||||
|
```
|
||||||
|
For listening to broadcasted data, stream the messages by calling
|
||||||
|
```dart
|
||||||
|
FlutterOverlayApps.overlayListener().listen((data) {
|
||||||
|
print(data);
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
### Code Example
|
||||||
|
```dart
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'dart:async';
|
||||||
|
|
||||||
|
import 'package:flutter_overlay_apps/flutter_overlay_apps.dart';
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
runApp(const MyApp());
|
||||||
|
}
|
||||||
|
|
||||||
|
// overlay entry point
|
||||||
|
@pragma("vm:entry-point")
|
||||||
|
void showOverlay() {
|
||||||
|
runApp(const MaterialApp(
|
||||||
|
debugShowCheckedModeBanner: false,
|
||||||
|
home: MyOverlaContent()
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class MyApp extends StatelessWidget {
|
||||||
|
const MyApp({Key? key}) : super(key: key);
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return MaterialApp(
|
||||||
|
home: Scaffold(
|
||||||
|
appBar: AppBar(
|
||||||
|
title: const Text('Plugin example app'),
|
||||||
|
),
|
||||||
|
body: Center(
|
||||||
|
child: ElevatedButton(
|
||||||
|
onPressed: () async {
|
||||||
|
|
||||||
|
// Open overlay
|
||||||
|
await FlutterOverlayApps.showOverlay(height: 300, width: 400, alignment: OverlayAlignment.center);
|
||||||
|
|
||||||
|
// send data to ovelay
|
||||||
|
await Future.delayed(const Duration(seconds: 2));
|
||||||
|
FlutterOverlayApps.sendDataToAndFromOverlay("Hello from main app");
|
||||||
|
},
|
||||||
|
child: const Text("showOverlay")
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
class MyOverlaContent extends StatefulWidget {
|
||||||
|
const MyOverlaContent({ Key? key }) : super(key: key);
|
||||||
|
|
||||||
|
@override
|
||||||
|
State<MyOverlaContent> createState() => _MyOverlaContentState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _MyOverlaContentState extends State<MyOverlaContent> {
|
||||||
|
String _dataFromApp = "Hey send data";
|
||||||
|
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
super.initState();
|
||||||
|
|
||||||
|
// lisent for any data from the main app
|
||||||
|
FlutterOverlayApps.overlayListener().listen((event) {
|
||||||
|
setState(() {
|
||||||
|
_dataFromApp = event.toString();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Material(
|
||||||
|
child: InkWell(
|
||||||
|
onTap: (){
|
||||||
|
// close overlay
|
||||||
|
FlutterOverlayApps.closeOverlay();
|
||||||
|
},
|
||||||
|
child: Card(
|
||||||
|
shape: RoundedRectangleBorder(
|
||||||
|
borderRadius: BorderRadius.circular(16),
|
||||||
|
),
|
||||||
|
child: Center(child: Text(_dataFromApp, style: const TextStyle(color: Colors.red),)),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Android back button
|
||||||
|
The back navigation button on android will close the overlay app. To prevent this, set `closeOnBackButton` to `false`. Example;
|
||||||
|
|
||||||
|
```dart
|
||||||
|
await FlutterOverlayApps.showOverlay(
|
||||||
|
height: 300,
|
||||||
|
closeOnBackButton: false,
|
||||||
|
alignment: OverlayAlignment.center);
|
||||||
|
```
|
||||||
|
|
||||||
|
When `closeOnBackButton` is set to `false`, an action will be sent via the stream controller. You can get the action like the example below;
|
||||||
|
|
||||||
|
```dart
|
||||||
|
var overlayStreamController = FlutterOverlayApps.overlayListener();
|
||||||
|
|
||||||
|
overlayStreamController.stream.listen((event) {
|
||||||
|
if(event['method'] == 'backButton'){
|
||||||
|
// handle back button
|
||||||
|
if(mounted && Navigator.of(context).canPop()){
|
||||||
|
Navigator.of(context).pop();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
```
|
||||||
|
|
||||||
|
Support the plugin <a href="https://www.buymeacoffee.com/EddieGenius" target="_blank"><img src="https://i.imgur.com/aV6DDA7.png" alt="Buy Me A Coffee" style="height: 41px !important;width: 174px !important; box-shadow: 0px 3px 2px 0px rgba(190, 190, 190, 0.5) !important;-webkit-box-shadow: 0px 3px 2px 0px rgba(190, 190, 190, 0.5) !important;" > </a>
|
||||||
4
flutter_overlay_apps-main/analysis_options.yaml
Normal file
4
flutter_overlay_apps-main/analysis_options.yaml
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
include: package:flutter_lints/flutter.yaml
|
||||||
|
|
||||||
|
# Additional information about this file can be found at
|
||||||
|
# https://dart.dev/guides/language/analysis-options
|
||||||
9
flutter_overlay_apps-main/android/.gitignore
vendored
Normal file
9
flutter_overlay_apps-main/android/.gitignore
vendored
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
*.iml
|
||||||
|
.gradle
|
||||||
|
/local.properties
|
||||||
|
/.idea/workspace.xml
|
||||||
|
/.idea/libraries
|
||||||
|
.DS_Store
|
||||||
|
/build
|
||||||
|
/captures
|
||||||
|
.cxx
|
||||||
52
flutter_overlay_apps-main/android/build.gradle
Normal file
52
flutter_overlay_apps-main/android/build.gradle
Normal file
@@ -0,0 +1,52 @@
|
|||||||
|
group 'com.phan_tech.flutter_overlay_apps'
|
||||||
|
version '1.0-SNAPSHOT'
|
||||||
|
|
||||||
|
buildscript {
|
||||||
|
ext.kotlin_version = '1.6.10'
|
||||||
|
|
||||||
|
repositories {
|
||||||
|
google()
|
||||||
|
mavenCentral()
|
||||||
|
}
|
||||||
|
|
||||||
|
dependencies {
|
||||||
|
classpath 'com.android.tools.build:gradle:4.1.0'
|
||||||
|
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
rootProject.allprojects {
|
||||||
|
repositories {
|
||||||
|
google()
|
||||||
|
mavenCentral()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
apply plugin: 'com.android.library'
|
||||||
|
apply plugin: 'kotlin-android'
|
||||||
|
|
||||||
|
android {
|
||||||
|
compileSdkVersion 31
|
||||||
|
namespace 'com.phan_tech.flutter_overlay_apps' // Specify the namespace here
|
||||||
|
|
||||||
|
compileOptions {
|
||||||
|
sourceCompatibility JavaVersion.VERSION_1_8
|
||||||
|
targetCompatibility JavaVersion.VERSION_1_8
|
||||||
|
}
|
||||||
|
|
||||||
|
kotlinOptions {
|
||||||
|
jvmTarget = '1.8'
|
||||||
|
}
|
||||||
|
|
||||||
|
sourceSets {
|
||||||
|
main.java.srcDirs += 'src/main/kotlin'
|
||||||
|
}
|
||||||
|
|
||||||
|
defaultConfig {
|
||||||
|
minSdkVersion 16
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
dependencies {
|
||||||
|
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
|
||||||
|
}
|
||||||
BIN
flutter_overlay_apps-main/android/gradle/wrapper/gradle-wrapper.jar
vendored
Normal file
BIN
flutter_overlay_apps-main/android/gradle/wrapper/gradle-wrapper.jar
vendored
Normal file
Binary file not shown.
5
flutter_overlay_apps-main/android/gradle/wrapper/gradle-wrapper.properties
vendored
Normal file
5
flutter_overlay_apps-main/android/gradle/wrapper/gradle-wrapper.properties
vendored
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
distributionBase=GRADLE_USER_HOME
|
||||||
|
distributionPath=wrapper/dists
|
||||||
|
distributionUrl=https\://services.gradle.org/distributions/gradle-7.2-bin.zip
|
||||||
|
zipStoreBase=GRADLE_USER_HOME
|
||||||
|
zipStorePath=wrapper/dists
|
||||||
234
flutter_overlay_apps-main/android/gradlew
vendored
Executable file
234
flutter_overlay_apps-main/android/gradlew
vendored
Executable file
@@ -0,0 +1,234 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
#
|
||||||
|
# Copyright © 2015-2021 the original authors.
|
||||||
|
#
|
||||||
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
# you may not use this file except in compliance with the License.
|
||||||
|
# You may obtain a copy of the License at
|
||||||
|
#
|
||||||
|
# https://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
#
|
||||||
|
# Unless required by applicable law or agreed to in writing, software
|
||||||
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
# See the License for the specific language governing permissions and
|
||||||
|
# limitations under the License.
|
||||||
|
#
|
||||||
|
|
||||||
|
##############################################################################
|
||||||
|
#
|
||||||
|
# Gradle start up script for POSIX generated by Gradle.
|
||||||
|
#
|
||||||
|
# Important for running:
|
||||||
|
#
|
||||||
|
# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is
|
||||||
|
# noncompliant, but you have some other compliant shell such as ksh or
|
||||||
|
# bash, then to run this script, type that shell name before the whole
|
||||||
|
# command line, like:
|
||||||
|
#
|
||||||
|
# ksh Gradle
|
||||||
|
#
|
||||||
|
# Busybox and similar reduced shells will NOT work, because this script
|
||||||
|
# requires all of these POSIX shell features:
|
||||||
|
# * functions;
|
||||||
|
# * expansions «$var», «${var}», «${var:-default}», «${var+SET}»,
|
||||||
|
# «${var#prefix}», «${var%suffix}», and «$( cmd )»;
|
||||||
|
# * compound commands having a testable exit status, especially «case»;
|
||||||
|
# * various built-in commands including «command», «set», and «ulimit».
|
||||||
|
#
|
||||||
|
# Important for patching:
|
||||||
|
#
|
||||||
|
# (2) This script targets any POSIX shell, so it avoids extensions provided
|
||||||
|
# by Bash, Ksh, etc; in particular arrays are avoided.
|
||||||
|
#
|
||||||
|
# The "traditional" practice of packing multiple parameters into a
|
||||||
|
# space-separated string is a well documented source of bugs and security
|
||||||
|
# problems, so this is (mostly) avoided, by progressively accumulating
|
||||||
|
# options in "$@", and eventually passing that to Java.
|
||||||
|
#
|
||||||
|
# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS,
|
||||||
|
# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly;
|
||||||
|
# see the in-line comments for details.
|
||||||
|
#
|
||||||
|
# There are tweaks for specific operating systems such as AIX, CygWin,
|
||||||
|
# Darwin, MinGW, and NonStop.
|
||||||
|
#
|
||||||
|
# (3) This script is generated from the Groovy template
|
||||||
|
# https://github.com/gradle/gradle/blob/master/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
|
||||||
|
# within the Gradle project.
|
||||||
|
#
|
||||||
|
# You can find Gradle at https://github.com/gradle/gradle/.
|
||||||
|
#
|
||||||
|
##############################################################################
|
||||||
|
|
||||||
|
# Attempt to set APP_HOME
|
||||||
|
|
||||||
|
# Resolve links: $0 may be a link
|
||||||
|
app_path=$0
|
||||||
|
|
||||||
|
# Need this for daisy-chained symlinks.
|
||||||
|
while
|
||||||
|
APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path
|
||||||
|
[ -h "$app_path" ]
|
||||||
|
do
|
||||||
|
ls=$( ls -ld "$app_path" )
|
||||||
|
link=${ls#*' -> '}
|
||||||
|
case $link in #(
|
||||||
|
/*) app_path=$link ;; #(
|
||||||
|
*) app_path=$APP_HOME$link ;;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
|
||||||
|
APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit
|
||||||
|
|
||||||
|
APP_NAME="Gradle"
|
||||||
|
APP_BASE_NAME=${0##*/}
|
||||||
|
|
||||||
|
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||||
|
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
|
||||||
|
|
||||||
|
# Use the maximum available, or set MAX_FD != -1 to use that value.
|
||||||
|
MAX_FD=maximum
|
||||||
|
|
||||||
|
warn () {
|
||||||
|
echo "$*"
|
||||||
|
} >&2
|
||||||
|
|
||||||
|
die () {
|
||||||
|
echo
|
||||||
|
echo "$*"
|
||||||
|
echo
|
||||||
|
exit 1
|
||||||
|
} >&2
|
||||||
|
|
||||||
|
# OS specific support (must be 'true' or 'false').
|
||||||
|
cygwin=false
|
||||||
|
msys=false
|
||||||
|
darwin=false
|
||||||
|
nonstop=false
|
||||||
|
case "$( uname )" in #(
|
||||||
|
CYGWIN* ) cygwin=true ;; #(
|
||||||
|
Darwin* ) darwin=true ;; #(
|
||||||
|
MSYS* | MINGW* ) msys=true ;; #(
|
||||||
|
NONSTOP* ) nonstop=true ;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
|
||||||
|
|
||||||
|
|
||||||
|
# Determine the Java command to use to start the JVM.
|
||||||
|
if [ -n "$JAVA_HOME" ] ; then
|
||||||
|
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
|
||||||
|
# IBM's JDK on AIX uses strange locations for the executables
|
||||||
|
JAVACMD=$JAVA_HOME/jre/sh/java
|
||||||
|
else
|
||||||
|
JAVACMD=$JAVA_HOME/bin/java
|
||||||
|
fi
|
||||||
|
if [ ! -x "$JAVACMD" ] ; then
|
||||||
|
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
|
||||||
|
|
||||||
|
Please set the JAVA_HOME variable in your environment to match the
|
||||||
|
location of your Java installation."
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
JAVACMD=java
|
||||||
|
which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
||||||
|
|
||||||
|
Please set the JAVA_HOME variable in your environment to match the
|
||||||
|
location of your Java installation."
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Increase the maximum file descriptors if we can.
|
||||||
|
if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
|
||||||
|
case $MAX_FD in #(
|
||||||
|
max*)
|
||||||
|
MAX_FD=$( ulimit -H -n ) ||
|
||||||
|
warn "Could not query maximum file descriptor limit"
|
||||||
|
esac
|
||||||
|
case $MAX_FD in #(
|
||||||
|
'' | soft) :;; #(
|
||||||
|
*)
|
||||||
|
ulimit -n "$MAX_FD" ||
|
||||||
|
warn "Could not set maximum file descriptor limit to $MAX_FD"
|
||||||
|
esac
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Collect all arguments for the java command, stacking in reverse order:
|
||||||
|
# * args from the command line
|
||||||
|
# * the main class name
|
||||||
|
# * -classpath
|
||||||
|
# * -D...appname settings
|
||||||
|
# * --module-path (only if needed)
|
||||||
|
# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables.
|
||||||
|
|
||||||
|
# For Cygwin or MSYS, switch paths to Windows format before running java
|
||||||
|
if "$cygwin" || "$msys" ; then
|
||||||
|
APP_HOME=$( cygpath --path --mixed "$APP_HOME" )
|
||||||
|
CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" )
|
||||||
|
|
||||||
|
JAVACMD=$( cygpath --unix "$JAVACMD" )
|
||||||
|
|
||||||
|
# Now convert the arguments - kludge to limit ourselves to /bin/sh
|
||||||
|
for arg do
|
||||||
|
if
|
||||||
|
case $arg in #(
|
||||||
|
-*) false ;; # don't mess with options #(
|
||||||
|
/?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath
|
||||||
|
[ -e "$t" ] ;; #(
|
||||||
|
*) false ;;
|
||||||
|
esac
|
||||||
|
then
|
||||||
|
arg=$( cygpath --path --ignore --mixed "$arg" )
|
||||||
|
fi
|
||||||
|
# Roll the args list around exactly as many times as the number of
|
||||||
|
# args, so each arg winds up back in the position where it started, but
|
||||||
|
# possibly modified.
|
||||||
|
#
|
||||||
|
# NB: a `for` loop captures its iteration list before it begins, so
|
||||||
|
# changing the positional parameters here affects neither the number of
|
||||||
|
# iterations, nor the values presented in `arg`.
|
||||||
|
shift # remove old arg
|
||||||
|
set -- "$@" "$arg" # push replacement arg
|
||||||
|
done
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Collect all arguments for the java command;
|
||||||
|
# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of
|
||||||
|
# shell script including quotes and variable substitutions, so put them in
|
||||||
|
# double quotes to make sure that they get re-expanded; and
|
||||||
|
# * put everything else in single quotes, so that it's not re-expanded.
|
||||||
|
|
||||||
|
set -- \
|
||||||
|
"-Dorg.gradle.appname=$APP_BASE_NAME" \
|
||||||
|
-classpath "$CLASSPATH" \
|
||||||
|
org.gradle.wrapper.GradleWrapperMain \
|
||||||
|
"$@"
|
||||||
|
|
||||||
|
# Use "xargs" to parse quoted args.
|
||||||
|
#
|
||||||
|
# With -n1 it outputs one arg per line, with the quotes and backslashes removed.
|
||||||
|
#
|
||||||
|
# In Bash we could simply go:
|
||||||
|
#
|
||||||
|
# readarray ARGS < <( xargs -n1 <<<"$var" ) &&
|
||||||
|
# set -- "${ARGS[@]}" "$@"
|
||||||
|
#
|
||||||
|
# but POSIX shell has neither arrays nor command substitution, so instead we
|
||||||
|
# post-process each arg (as a line of input to sed) to backslash-escape any
|
||||||
|
# character that might be a shell metacharacter, then use eval to reverse
|
||||||
|
# that process (while maintaining the separation between arguments), and wrap
|
||||||
|
# the whole thing up as a single "set" statement.
|
||||||
|
#
|
||||||
|
# This will of course break if any of these variables contains a newline or
|
||||||
|
# an unmatched quote.
|
||||||
|
#
|
||||||
|
|
||||||
|
eval "set -- $(
|
||||||
|
printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" |
|
||||||
|
xargs -n1 |
|
||||||
|
sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' |
|
||||||
|
tr '\n' ' '
|
||||||
|
)" '"$@"'
|
||||||
|
|
||||||
|
exec "$JAVACMD" "$@"
|
||||||
89
flutter_overlay_apps-main/android/gradlew.bat
vendored
Normal file
89
flutter_overlay_apps-main/android/gradlew.bat
vendored
Normal file
@@ -0,0 +1,89 @@
|
|||||||
|
@rem
|
||||||
|
@rem Copyright 2015 the original author or authors.
|
||||||
|
@rem
|
||||||
|
@rem Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
@rem you may not use this file except in compliance with the License.
|
||||||
|
@rem You may obtain a copy of the License at
|
||||||
|
@rem
|
||||||
|
@rem https://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
@rem
|
||||||
|
@rem Unless required by applicable law or agreed to in writing, software
|
||||||
|
@rem distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
@rem See the License for the specific language governing permissions and
|
||||||
|
@rem limitations under the License.
|
||||||
|
@rem
|
||||||
|
|
||||||
|
@if "%DEBUG%" == "" @echo off
|
||||||
|
@rem ##########################################################################
|
||||||
|
@rem
|
||||||
|
@rem Gradle startup script for Windows
|
||||||
|
@rem
|
||||||
|
@rem ##########################################################################
|
||||||
|
|
||||||
|
@rem Set local scope for the variables with windows NT shell
|
||||||
|
if "%OS%"=="Windows_NT" setlocal
|
||||||
|
|
||||||
|
set DIRNAME=%~dp0
|
||||||
|
if "%DIRNAME%" == "" set DIRNAME=.
|
||||||
|
set APP_BASE_NAME=%~n0
|
||||||
|
set APP_HOME=%DIRNAME%
|
||||||
|
|
||||||
|
@rem Resolve any "." and ".." in APP_HOME to make it shorter.
|
||||||
|
for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
|
||||||
|
|
||||||
|
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||||
|
set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
|
||||||
|
|
||||||
|
@rem Find java.exe
|
||||||
|
if defined JAVA_HOME goto findJavaFromJavaHome
|
||||||
|
|
||||||
|
set JAVA_EXE=java.exe
|
||||||
|
%JAVA_EXE% -version >NUL 2>&1
|
||||||
|
if "%ERRORLEVEL%" == "0" goto execute
|
||||||
|
|
||||||
|
echo.
|
||||||
|
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
||||||
|
echo.
|
||||||
|
echo Please set the JAVA_HOME variable in your environment to match the
|
||||||
|
echo location of your Java installation.
|
||||||
|
|
||||||
|
goto fail
|
||||||
|
|
||||||
|
:findJavaFromJavaHome
|
||||||
|
set JAVA_HOME=%JAVA_HOME:"=%
|
||||||
|
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
|
||||||
|
|
||||||
|
if exist "%JAVA_EXE%" goto execute
|
||||||
|
|
||||||
|
echo.
|
||||||
|
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
|
||||||
|
echo.
|
||||||
|
echo Please set the JAVA_HOME variable in your environment to match the
|
||||||
|
echo location of your Java installation.
|
||||||
|
|
||||||
|
goto fail
|
||||||
|
|
||||||
|
:execute
|
||||||
|
@rem Setup the command line
|
||||||
|
|
||||||
|
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
|
||||||
|
|
||||||
|
|
||||||
|
@rem Execute Gradle
|
||||||
|
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
|
||||||
|
|
||||||
|
:end
|
||||||
|
@rem End local scope for the variables with windows NT shell
|
||||||
|
if "%ERRORLEVEL%"=="0" goto mainEnd
|
||||||
|
|
||||||
|
:fail
|
||||||
|
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
|
||||||
|
rem the _cmd.exe /c_ return code!
|
||||||
|
if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
|
||||||
|
exit /b 1
|
||||||
|
|
||||||
|
:mainEnd
|
||||||
|
if "%OS%"=="Windows_NT" endlocal
|
||||||
|
|
||||||
|
:omega
|
||||||
1
flutter_overlay_apps-main/android/settings.gradle
Normal file
1
flutter_overlay_apps-main/android/settings.gradle
Normal file
@@ -0,0 +1 @@
|
|||||||
|
rootProject.name = 'flutter_overlay_apps'
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
package="com.phan_tech.flutter_overlay_apps"
|
||||||
|
>
|
||||||
|
</manifest>
|
||||||
@@ -0,0 +1,131 @@
|
|||||||
|
package com.phan_tech.flutter_overlay_apps
|
||||||
|
|
||||||
|
import android.app.Activity
|
||||||
|
import android.content.Context
|
||||||
|
import android.content.Intent
|
||||||
|
import android.net.Uri
|
||||||
|
import android.os.Build
|
||||||
|
import android.provider.Settings
|
||||||
|
import androidx.annotation.NonNull
|
||||||
|
import androidx.annotation.RequiresApi
|
||||||
|
import io.flutter.FlutterInjector
|
||||||
|
import io.flutter.embedding.engine.FlutterEngineCache
|
||||||
|
import io.flutter.embedding.engine.FlutterEngineGroup
|
||||||
|
import io.flutter.embedding.engine.dart.DartExecutor
|
||||||
|
import io.flutter.embedding.engine.plugins.FlutterPlugin
|
||||||
|
import io.flutter.embedding.engine.plugins.activity.ActivityAware
|
||||||
|
import io.flutter.embedding.engine.plugins.activity.ActivityPluginBinding
|
||||||
|
import io.flutter.plugin.common.*
|
||||||
|
import io.flutter.plugin.common.MethodChannel.MethodCallHandler
|
||||||
|
import io.flutter.plugin.common.MethodChannel.Result
|
||||||
|
|
||||||
|
const val mainAppMethodChannel: String = "com.phan_tech/flutter_overlay_apps"
|
||||||
|
const val overlayAppMethodChannel: String = "com.phan_tech/flutter_overlay_apps/overlay"
|
||||||
|
const val overlayAppMessageChannel: String = "com.phan_tech/flutter_overlay_apps/overlay/messenger"
|
||||||
|
|
||||||
|
/** FlutterOverlayAppsPlugin */
|
||||||
|
class FlutterOverlayAppsPlugin : FlutterPlugin, MethodCallHandler, ActivityAware, BasicMessageChannel.MessageHandler<Any?> {
|
||||||
|
private lateinit var channel: MethodChannel
|
||||||
|
private lateinit var messenger: BasicMessageChannel<Any?>
|
||||||
|
private lateinit var context: Context
|
||||||
|
private var activity: Activity? = null
|
||||||
|
|
||||||
|
override fun onDetachedFromEngine(@NonNull binding: FlutterPlugin.FlutterPluginBinding) {
|
||||||
|
channel.setMethodCallHandler(null)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onAttachedToActivity(binding: ActivityPluginBinding) {
|
||||||
|
activity = binding.activity
|
||||||
|
context = binding.activity.applicationContext
|
||||||
|
WindowSetup.messenger = messenger
|
||||||
|
WindowSetup.messenger!!.setMessageHandler(this)
|
||||||
|
|
||||||
|
val engineGroup = FlutterEngineGroup(context)
|
||||||
|
val dartEntrypoint = DartExecutor.DartEntrypoint(
|
||||||
|
FlutterInjector.instance().flutterLoader().findAppBundlePath(),
|
||||||
|
"showOverlay"
|
||||||
|
)
|
||||||
|
val engine = engineGroup.createAndRunEngine(context, dartEntrypoint)
|
||||||
|
FlutterEngineCache.getInstance().put("my_engine_id", engine)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onDetachedFromActivityForConfigChanges() {
|
||||||
|
activity = null
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onReattachedToActivityForConfigChanges(binding: ActivityPluginBinding) {
|
||||||
|
activity = binding.activity
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onDetachedFromActivity() {
|
||||||
|
activity = null
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onAttachedToEngine(@NonNull flutterPluginBinding: FlutterPlugin.FlutterPluginBinding) {
|
||||||
|
channel = MethodChannel(flutterPluginBinding.binaryMessenger, mainAppMethodChannel)
|
||||||
|
channel.setMethodCallHandler(this)
|
||||||
|
|
||||||
|
messenger = BasicMessageChannel(flutterPluginBinding.binaryMessenger, overlayAppMessageChannel, JSONMessageCodec.INSTANCE)
|
||||||
|
messenger.setMessageHandler(this)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onMethodCall(@NonNull call: MethodCall, @NonNull result: Result) {
|
||||||
|
val currentActivity = activity
|
||||||
|
if (currentActivity == null) {
|
||||||
|
result.error("activity_not_attached", "Activity is not attached", null)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
when (call.method) {
|
||||||
|
"showOverlay" -> {
|
||||||
|
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) {
|
||||||
|
result.error("1", "SDK version is lower than 23", "Requires Android SDK 23 and above")
|
||||||
|
} else if (!checkPermissions()) {
|
||||||
|
requestPermissions()
|
||||||
|
} else {
|
||||||
|
val height = call.argument<Int>("height")
|
||||||
|
val width = call.argument<Int>("width")
|
||||||
|
val alignment = call.argument<String>("alignment")
|
||||||
|
val closeOnBackButton = call.argument<Boolean>("closeOnBackButton")
|
||||||
|
|
||||||
|
WindowSetup.width = width ?: -1
|
||||||
|
WindowSetup.height = height ?: -1
|
||||||
|
WindowSetup.closeOnBackButton = closeOnBackButton ?: true
|
||||||
|
WindowSetup.setGravityFromAlignment(alignment ?: "center")
|
||||||
|
currentActivity.startService(Intent(context, OverlayService::class.java))
|
||||||
|
result.success(true)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else -> {
|
||||||
|
result.notImplemented()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onMessage(message: Any?, reply: BasicMessageChannel.Reply<Any?>) {
|
||||||
|
val engine = FlutterEngineCache.getInstance().get("my_engine_id")
|
||||||
|
if (engine != null) {
|
||||||
|
val overlayMessageChannel = BasicMessageChannel(engine.dartExecutor, overlayAppMessageChannel, JSONMessageCodec.INSTANCE)
|
||||||
|
overlayMessageChannel.send(message, reply)
|
||||||
|
} else {
|
||||||
|
reply.reply(null)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@RequiresApi(Build.VERSION_CODES.M)
|
||||||
|
private fun checkPermissions(): Boolean {
|
||||||
|
return Settings.canDrawOverlays(context)
|
||||||
|
}
|
||||||
|
|
||||||
|
@RequiresApi(Build.VERSION_CODES.M)
|
||||||
|
private fun requestPermissions() {
|
||||||
|
activity?.let {
|
||||||
|
it.startActivity(
|
||||||
|
Intent(
|
||||||
|
Settings.ACTION_MANAGE_OVERLAY_PERMISSION,
|
||||||
|
Uri.parse("package:${it.packageName}")
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,97 @@
|
|||||||
|
package com.phan_tech.flutter_overlay_apps
|
||||||
|
|
||||||
|
import android.app.Service
|
||||||
|
import android.content.Intent
|
||||||
|
import android.graphics.PixelFormat
|
||||||
|
import android.os.Build
|
||||||
|
import android.os.IBinder
|
||||||
|
import android.view.KeyEvent
|
||||||
|
import android.view.WindowManager
|
||||||
|
import android.widget.FrameLayout
|
||||||
|
import androidx.annotation.RequiresApi
|
||||||
|
import io.flutter.embedding.android.FlutterView
|
||||||
|
import io.flutter.embedding.engine.FlutterEngineCache
|
||||||
|
import io.flutter.plugin.common.BasicMessageChannel
|
||||||
|
import io.flutter.plugin.common.JSONMessageCodec
|
||||||
|
import io.flutter.plugin.common.MethodCall
|
||||||
|
import io.flutter.plugin.common.MethodChannel
|
||||||
|
import org.json.JSONObject
|
||||||
|
|
||||||
|
|
||||||
|
class OverlayService : Service() {
|
||||||
|
private var windowManager: WindowManager? = null
|
||||||
|
private lateinit var flutterView: FlutterView
|
||||||
|
private val flutterChannel = MethodChannel(FlutterEngineCache.getInstance().get("my_engine_id")!!.dartExecutor, overlayAppMethodChannel)
|
||||||
|
private val overlayMessageChannel = BasicMessageChannel(FlutterEngineCache.getInstance().get("my_engine_id")!!.dartExecutor, overlayAppMessageChannel, JSONMessageCodec.INSTANCE)
|
||||||
|
|
||||||
|
override fun onBind(intent: Intent?): IBinder? {
|
||||||
|
// Not used
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
@RequiresApi(Build.VERSION_CODES.O)
|
||||||
|
override fun onCreate() {
|
||||||
|
super.onCreate()
|
||||||
|
|
||||||
|
val engine = FlutterEngineCache.getInstance().get("my_engine_id")!!
|
||||||
|
engine.lifecycleChannel.appIsResumed()
|
||||||
|
|
||||||
|
flutterView = object: FlutterView(applicationContext){
|
||||||
|
override fun dispatchKeyEvent(event: KeyEvent): Boolean {
|
||||||
|
return if (event.keyCode == KeyEvent.KEYCODE_BACK) {
|
||||||
|
// handle the back button code;
|
||||||
|
if(WindowSetup.closeOnBackButton){
|
||||||
|
stopService(Intent(baseContext, OverlayService().javaClass))
|
||||||
|
windowManager?.removeView(flutterView)
|
||||||
|
}else{
|
||||||
|
// send message
|
||||||
|
overlayMessageChannel.send(JSONObject("{\"method\": \"backButton\"}"))// {"method" "backButton"}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
true
|
||||||
|
} else super.dispatchKeyEvent(event)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
flutterView.attachToFlutterEngine(FlutterEngineCache.getInstance().get("my_engine_id")!!)
|
||||||
|
flutterView.fitsSystemWindows = true
|
||||||
|
|
||||||
|
flutterChannel.setMethodCallHandler{ methodCall: MethodCall, result: MethodChannel.Result ->
|
||||||
|
if(methodCall.method == "close"){
|
||||||
|
val closed = stopService(Intent(baseContext, OverlayService().javaClass))
|
||||||
|
result.success(closed)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
overlayMessageChannel.setMessageHandler(MyHandler())
|
||||||
|
|
||||||
|
|
||||||
|
windowManager = getSystemService(WINDOW_SERVICE) as WindowManager?
|
||||||
|
|
||||||
|
val params = WindowManager.LayoutParams(
|
||||||
|
WindowSetup.width,
|
||||||
|
WindowSetup.height,
|
||||||
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY
|
||||||
|
else WindowManager.LayoutParams.TYPE_SYSTEM_OVERLAY,
|
||||||
|
WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE,
|
||||||
|
PixelFormat.TRANSLUCENT
|
||||||
|
)
|
||||||
|
params.flags = params.flags and WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE.inv()
|
||||||
|
params.gravity = WindowSetup.gravity
|
||||||
|
windowManager!!.addView(flutterView, params)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
override fun onDestroy() {
|
||||||
|
super.onDestroy()
|
||||||
|
windowManager!!.removeView(flutterView)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class MyHandler: BasicMessageChannel.MessageHandler<Any?>{
|
||||||
|
override fun onMessage(message: Any?, reply: BasicMessageChannel.Reply<Any?>) {
|
||||||
|
WindowSetup.messenger!!.send(message)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,53 @@
|
|||||||
|
package com.phan_tech.flutter_overlay_apps
|
||||||
|
|
||||||
|
import android.annotation.SuppressLint
|
||||||
|
import android.view.Gravity
|
||||||
|
import android.view.WindowManager
|
||||||
|
import io.flutter.plugin.common.BasicMessageChannel
|
||||||
|
|
||||||
|
object WindowSetup {
|
||||||
|
var height: Int = -1
|
||||||
|
var width: Int = WindowManager.LayoutParams.MATCH_PARENT
|
||||||
|
var gravity: Int = Gravity.CENTER
|
||||||
|
var messenger : BasicMessageChannel<Any?>? = null
|
||||||
|
var closeOnBackButton : Boolean = true;
|
||||||
|
|
||||||
|
@SuppressLint("RtlHardcoded")
|
||||||
|
fun setGravityFromAlignment(alignment: String){
|
||||||
|
when {
|
||||||
|
alignment.lowercase() == "topLeft".lowercase() -> {
|
||||||
|
gravity = Gravity.TOP or Gravity.LEFT
|
||||||
|
}
|
||||||
|
alignment.lowercase() == "topCenter".lowercase() -> {
|
||||||
|
gravity = Gravity.TOP
|
||||||
|
}
|
||||||
|
alignment.lowercase() == "topRight".lowercase() -> {
|
||||||
|
gravity = Gravity.TOP or Gravity.RIGHT
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
alignment.lowercase() == "centerLeft".lowercase() -> {
|
||||||
|
gravity = Gravity.CENTER or Gravity.LEFT
|
||||||
|
}
|
||||||
|
alignment.lowercase() == "center".lowercase() -> {
|
||||||
|
gravity = Gravity.CENTER
|
||||||
|
}
|
||||||
|
alignment.lowercase() == "centerRight".lowercase() -> {
|
||||||
|
gravity = Gravity.CENTER or Gravity.RIGHT
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
alignment.lowercase() == "bottomLeft".lowercase() -> {
|
||||||
|
gravity = Gravity.BOTTOM or Gravity.LEFT
|
||||||
|
}
|
||||||
|
alignment.lowercase() == "bottomCenter".lowercase() -> {
|
||||||
|
gravity = Gravity.BOTTOM
|
||||||
|
}
|
||||||
|
alignment.lowercase() == "bottomRight".lowercase() -> {
|
||||||
|
gravity = Gravity.BOTTOM or Gravity.RIGHT
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
84
flutter_overlay_apps-main/lib/flutter_overlay_apps.dart
Normal file
84
flutter_overlay_apps-main/lib/flutter_overlay_apps.dart
Normal file
@@ -0,0 +1,84 @@
|
|||||||
|
import 'dart:async';
|
||||||
|
|
||||||
|
import 'package:flutter/services.dart';
|
||||||
|
|
||||||
|
const int overlaySizeFill = -1;
|
||||||
|
const String _mainAppMethodChannel = "com.phan_tech./flutter_overlay_apps";
|
||||||
|
const String _overlayAppMethodChannel =
|
||||||
|
"com.phan_tech/flutter_overlay_apps/overlay";
|
||||||
|
const String _overlayAppMessageChannel =
|
||||||
|
"com.phan_tech/flutter_overlay_apps/overlay/messenger";
|
||||||
|
|
||||||
|
class FlutterOverlayApps {
|
||||||
|
static const MethodChannel _channel = MethodChannel(_mainAppMethodChannel);
|
||||||
|
|
||||||
|
// overlay methodChanel
|
||||||
|
static const MethodChannel _overlayChannel =
|
||||||
|
MethodChannel(_overlayAppMethodChannel);
|
||||||
|
//Overlay BasicMessageChannel
|
||||||
|
static const BasicMessageChannel _overlayMessageChannel =
|
||||||
|
BasicMessageChannel(_overlayAppMessageChannel, JSONMessageCodec());
|
||||||
|
|
||||||
|
/// Open overLay content
|
||||||
|
/// Takes optional;
|
||||||
|
/// - int [height] default is [overlaySizeFill]
|
||||||
|
/// - int [width] default is [overlaySizeFill]
|
||||||
|
/// - OverlayAlignment [width] default is [alignment] [OverlayAlignment.center]
|
||||||
|
/// - bool [closeOnBackButton] default is `true`
|
||||||
|
static Future<bool?> showOverlay(
|
||||||
|
{int height = overlaySizeFill,
|
||||||
|
int width = overlaySizeFill,
|
||||||
|
bool closeOnBackButton = true,
|
||||||
|
OverlayAlignment alignment = OverlayAlignment.center}) async {
|
||||||
|
final bool? _res = await _channel.invokeMethod('showOverlay', {
|
||||||
|
"height": height,
|
||||||
|
"width": width,
|
||||||
|
"alignment": alignment.name,
|
||||||
|
"closeOnBackButton": closeOnBackButton
|
||||||
|
});
|
||||||
|
return _res;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Closes overlau if open
|
||||||
|
static Future<bool?> closeOverlay() async {
|
||||||
|
final bool? _res = await _overlayChannel.invokeMethod('close');
|
||||||
|
return _res;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// broadcast data to and from overlay app
|
||||||
|
/// the supported data type are;
|
||||||
|
/// - [int], [double], [bool], [String], null
|
||||||
|
/// - [List] of supported types
|
||||||
|
/// - [Map] of supported types
|
||||||
|
static Future sendDataToAndFromOverlay(dynamic data) async {
|
||||||
|
return await _overlayMessageChannel.send(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Streams message shared between overlay and main app
|
||||||
|
static final StreamController _controller = StreamController();
|
||||||
|
static StreamController overlayListener() {
|
||||||
|
_overlayMessageChannel.setMessageHandler((message) async {
|
||||||
|
_controller.add(message);
|
||||||
|
return message;
|
||||||
|
});
|
||||||
|
return _controller;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// dispose overlay controller
|
||||||
|
static void disposeOverlayListener() {
|
||||||
|
_controller.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Overlay alignment on screen
|
||||||
|
enum OverlayAlignment {
|
||||||
|
topLeft,
|
||||||
|
topCenter,
|
||||||
|
topRight,
|
||||||
|
centerLeft,
|
||||||
|
center,
|
||||||
|
centerRight,
|
||||||
|
bottomLeft,
|
||||||
|
bottomCenter,
|
||||||
|
bottomRight
|
||||||
|
}
|
||||||
27
flutter_overlay_apps-main/pubspec.yaml
Normal file
27
flutter_overlay_apps-main/pubspec.yaml
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
name: flutter_overlay_apps
|
||||||
|
description: Android plugin for displaying flutter widgets over other apps
|
||||||
|
version: 1.2.0
|
||||||
|
homepage: https://github.com/EddieKamau/flutter_overlay_apps
|
||||||
|
|
||||||
|
environment:
|
||||||
|
sdk: ">=2.16.0 <4.0.0"
|
||||||
|
flutter: ">=2.5.0"
|
||||||
|
|
||||||
|
dependencies:
|
||||||
|
flutter:
|
||||||
|
sdk: flutter
|
||||||
|
|
||||||
|
dev_dependencies:
|
||||||
|
flutter_test:
|
||||||
|
sdk: flutter
|
||||||
|
flutter_lints: ^1.0.0
|
||||||
|
|
||||||
|
|
||||||
|
flutter:
|
||||||
|
|
||||||
|
plugin:
|
||||||
|
platforms:
|
||||||
|
android:
|
||||||
|
package: com.phan_tech.flutter_overlay_apps
|
||||||
|
pluginClass: FlutterOverlayAppsPlugin
|
||||||
|
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
import 'package:flutter/services.dart';
|
||||||
|
import 'package:flutter_test/flutter_test.dart';
|
||||||
|
import 'package:flutter_overlay_apps/flutter_overlay_apps.dart';
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
const MethodChannel channel =
|
||||||
|
MethodChannel('com.phan_tech./flutter_overlay_apps');
|
||||||
|
|
||||||
|
TestWidgetsFlutterBinding.ensureInitialized();
|
||||||
|
|
||||||
|
setUp(() {
|
||||||
|
channel.setMockMethodCallHandler((MethodCall methodCall) async {
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
tearDown(() {
|
||||||
|
channel.setMockMethodCallHandler(null);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('getPlatformVersion', () async {
|
||||||
|
expect(await FlutterOverlayApps.showOverlay(), true);
|
||||||
|
});
|
||||||
|
}
|
||||||
@@ -61,6 +61,8 @@ PODS:
|
|||||||
- GoogleUtilities/UserDefaults (~> 7.8)
|
- GoogleUtilities/UserDefaults (~> 7.8)
|
||||||
- nanopb (< 2.30911.0, >= 2.30908.0)
|
- nanopb (< 2.30911.0, >= 2.30908.0)
|
||||||
- Flutter (1.0.0)
|
- Flutter (1.0.0)
|
||||||
|
- flutter_contacts (0.0.1):
|
||||||
|
- Flutter
|
||||||
- flutter_image_compress_common (1.0.0):
|
- flutter_image_compress_common (1.0.0):
|
||||||
- Flutter
|
- Flutter
|
||||||
- Mantle
|
- Mantle
|
||||||
@@ -283,6 +285,7 @@ DEPENDENCIES:
|
|||||||
- firebase_core (from `.symlinks/plugins/firebase_core/ios`)
|
- firebase_core (from `.symlinks/plugins/firebase_core/ios`)
|
||||||
- firebase_messaging (from `.symlinks/plugins/firebase_messaging/ios`)
|
- firebase_messaging (from `.symlinks/plugins/firebase_messaging/ios`)
|
||||||
- Flutter (from `Flutter`)
|
- Flutter (from `Flutter`)
|
||||||
|
- flutter_contacts (from `.symlinks/plugins/flutter_contacts/ios`)
|
||||||
- flutter_image_compress_common (from `.symlinks/plugins/flutter_image_compress_common/ios`)
|
- flutter_image_compress_common (from `.symlinks/plugins/flutter_image_compress_common/ios`)
|
||||||
- flutter_local_notifications (from `.symlinks/plugins/flutter_local_notifications/ios`)
|
- flutter_local_notifications (from `.symlinks/plugins/flutter_local_notifications/ios`)
|
||||||
- flutter_secure_storage (from `.symlinks/plugins/flutter_secure_storage/ios`)
|
- flutter_secure_storage (from `.symlinks/plugins/flutter_secure_storage/ios`)
|
||||||
@@ -369,6 +372,8 @@ EXTERNAL SOURCES:
|
|||||||
:path: ".symlinks/plugins/firebase_messaging/ios"
|
:path: ".symlinks/plugins/firebase_messaging/ios"
|
||||||
Flutter:
|
Flutter:
|
||||||
:path: Flutter
|
:path: Flutter
|
||||||
|
flutter_contacts:
|
||||||
|
:path: ".symlinks/plugins/flutter_contacts/ios"
|
||||||
flutter_image_compress_common:
|
flutter_image_compress_common:
|
||||||
:path: ".symlinks/plugins/flutter_image_compress_common/ios"
|
:path: ".symlinks/plugins/flutter_image_compress_common/ios"
|
||||||
flutter_local_notifications:
|
flutter_local_notifications:
|
||||||
@@ -441,6 +446,7 @@ SPEC CHECKSUMS:
|
|||||||
FirebaseInstallations: 913cf60d0400ebd5d6b63a28b290372ab44590dd
|
FirebaseInstallations: 913cf60d0400ebd5d6b63a28b290372ab44590dd
|
||||||
FirebaseMessaging: 087a7c7cadef7b9239f005bc4db823894844f323
|
FirebaseMessaging: 087a7c7cadef7b9239f005bc4db823894844f323
|
||||||
Flutter: e0871f40cf51350855a761d2e70bf5af5b9b5de7
|
Flutter: e0871f40cf51350855a761d2e70bf5af5b9b5de7
|
||||||
|
flutter_contacts: edb1c5ce76aa433e20e6cb14c615f4c0b66e0983
|
||||||
flutter_image_compress_common: ec1d45c362c9d30a3f6a0426c297f47c52007e3e
|
flutter_image_compress_common: ec1d45c362c9d30a3f6a0426c297f47c52007e3e
|
||||||
flutter_local_notifications: 4cde75091f6327eb8517fa068a0a5950212d2086
|
flutter_local_notifications: 4cde75091f6327eb8517fa068a0a5950212d2086
|
||||||
flutter_secure_storage: d33dac7ae2ea08509be337e775f6b59f1ff45f12
|
flutter_secure_storage: d33dac7ae2ea08509be337e775f6b59f1ff45f12
|
||||||
|
|||||||
@@ -6,6 +6,8 @@
|
|||||||
<string>594687661098-9fnj82nef9oagl98prigdf8qne3ddbto.apps.googleusercontent.com</string>
|
<string>594687661098-9fnj82nef9oagl98prigdf8qne3ddbto.apps.googleusercontent.com</string>
|
||||||
<key>REVERSED_CLIENT_ID</key>
|
<key>REVERSED_CLIENT_ID</key>
|
||||||
<string>com.googleusercontent.apps.594687661098-9fnj82nef9oagl98prigdf8qne3ddbto</string>
|
<string>com.googleusercontent.apps.594687661098-9fnj82nef9oagl98prigdf8qne3ddbto</string>
|
||||||
|
<key>ANDROID_CLIENT_ID</key>
|
||||||
|
<string>594687661098-2dhoogl7be9phobfbu8bbg1sj567iv88.apps.googleusercontent.com</string>
|
||||||
<key>API_KEY</key>
|
<key>API_KEY</key>
|
||||||
<string>AIzaSyCf2mW2h0HD8ZYjwh4VOa2ladw6MJkCDTM</string>
|
<string>AIzaSyCf2mW2h0HD8ZYjwh4VOa2ladw6MJkCDTM</string>
|
||||||
<key>GCM_SENDER_ID</key>
|
<key>GCM_SENDER_ID</key>
|
||||||
|
|||||||
@@ -2,6 +2,18 @@
|
|||||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||||
<plist version="1.0">
|
<plist version="1.0">
|
||||||
<dict>
|
<dict>
|
||||||
|
<key>UIBackgroundModes</key>
|
||||||
|
<array>
|
||||||
|
<string>fetch</string>
|
||||||
|
<string>location</string>
|
||||||
|
<string>remote-notification</string>
|
||||||
|
</array>
|
||||||
|
<key>BGTaskSchedulerPermittedIdentifiers</key>
|
||||||
|
<array>
|
||||||
|
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
|
||||||
|
</array>
|
||||||
|
<key>NSContactsUsageDescription</key>
|
||||||
|
<string>This app requires contacts access to function properly.</string>
|
||||||
<key>CADisableMinimumFrameDurationOnPhone</key>
|
<key>CADisableMinimumFrameDurationOnPhone</key>
|
||||||
<true/>
|
<true/>
|
||||||
<key>CFBundleDevelopmentRegion</key>
|
<key>CFBundleDevelopmentRegion</key>
|
||||||
@@ -21,7 +33,7 @@
|
|||||||
<key>CFBundlePackageType</key>
|
<key>CFBundlePackageType</key>
|
||||||
<string>APPL</string>
|
<string>APPL</string>
|
||||||
<key>CFBundleShortVersionString</key>
|
<key>CFBundleShortVersionString</key>
|
||||||
<string>51</string>
|
<string>52</string>
|
||||||
<key>CFBundleSignature</key>
|
<key>CFBundleSignature</key>
|
||||||
<string>????</string>
|
<string>????</string>
|
||||||
<key>CFBundleURLTypes</key>
|
<key>CFBundleURLTypes</key>
|
||||||
@@ -36,7 +48,7 @@
|
|||||||
</dict>
|
</dict>
|
||||||
</array>
|
</array>
|
||||||
<key>CFBundleVersion</key>
|
<key>CFBundleVersion</key>
|
||||||
<string>4.0.51</string>
|
<string>4.0.52</string>
|
||||||
<key>FirebaseAppDelegateProxyEnabled</key>
|
<key>FirebaseAppDelegateProxyEnabled</key>
|
||||||
<string>NO</string>
|
<string>NO</string>
|
||||||
<key>GMSApiKey</key>
|
<key>GMSApiKey</key>
|
||||||
@@ -51,8 +63,8 @@
|
|||||||
<key>LSRequiresIPhoneOS</key>
|
<key>LSRequiresIPhoneOS</key>
|
||||||
<true/>
|
<true/>
|
||||||
<key>NSCameraUsageDescription</key>
|
<key>NSCameraUsageDescription</key>
|
||||||
<string>This app requires access to your camera in order to scan QR codes and capture
|
<string>This app requires access to your camera in order to scan QR codes and capture images
|
||||||
images for uploading and access to connect to a call.</string>
|
for uploading and access to connect to a call.</string>
|
||||||
<key>NSFaceIDUsageDescription</key>
|
<key>NSFaceIDUsageDescription</key>
|
||||||
<string>Use Face ID to securely authenticate payment accounts.</string>
|
<string>Use Face ID to securely authenticate payment accounts.</string>
|
||||||
<key>NSLocationAlwaysAndWhenInUseUsageDescription</key>
|
<key>NSLocationAlwaysAndWhenInUseUsageDescription</key>
|
||||||
@@ -72,12 +84,6 @@
|
|||||||
<string>Explanation of why your app needs access to the photo library.</string>
|
<string>Explanation of why your app needs access to the photo library.</string>
|
||||||
<key>UIApplicationSupportsIndirectInputEvents</key>
|
<key>UIApplicationSupportsIndirectInputEvents</key>
|
||||||
<true/>
|
<true/>
|
||||||
<key>UIBackgroundModes</key>
|
|
||||||
<array>
|
|
||||||
<string>fetch</string>
|
|
||||||
<string>location</string>
|
|
||||||
<string>remote-notification</string>
|
|
||||||
</array>
|
|
||||||
<key>UILaunchStoryboardName</key>
|
<key>UILaunchStoryboardName</key>
|
||||||
<string>LaunchScreen</string>
|
<string>LaunchScreen</string>
|
||||||
<key>UIMainStoryboardFile</key>
|
<key>UIMainStoryboardFile</key>
|
||||||
|
|||||||
@@ -4,6 +4,9 @@ class BoxName {
|
|||||||
static const String googlaMapApp = "googlaMapApp";
|
static const String googlaMapApp = "googlaMapApp";
|
||||||
|
|
||||||
static const String lang = "lang";
|
static const String lang = "lang";
|
||||||
|
static const String myListString = "myListString";
|
||||||
|
static const String myList = "myList";
|
||||||
|
static const String bodyOrder = "bodyOrder";
|
||||||
static const String gender = "gender";
|
static const String gender = "gender";
|
||||||
static const String carType = "carType";
|
static const String carType = "carType";
|
||||||
static const String isFirstTime = "isFirstTime";
|
static const String isFirstTime = "isFirstTime";
|
||||||
|
|||||||
@@ -21,8 +21,9 @@ class AppColor {
|
|||||||
|
|
||||||
// For dynamic elements like gradients
|
// For dynamic elements like gradients
|
||||||
static List<Color> gradientStartEnd = [
|
static List<Color> gradientStartEnd = [
|
||||||
const Color(0xFF1DA1F2), // Start with primary color
|
Color.fromARGB(255, 40, 158, 232), // Start with primary color
|
||||||
const Color(0xFF0C7ABF), // End with a slightly darker shade of Twitter blue
|
Color.fromARGB(
|
||||||
|
255, 44, 63, 75), // End with a slightly darker shade of Twitter blue
|
||||||
];
|
];
|
||||||
|
|
||||||
static List<Color> secondaryGradientStartEnd = [
|
static List<Color> secondaryGradientStartEnd = [
|
||||||
|
|||||||
@@ -257,6 +257,9 @@ class AppLink {
|
|||||||
static String sendmany = "https://sms.kazumi.me/api/sms/send-many";
|
static String sendmany = "https://sms.kazumi.me/api/sms/send-many";
|
||||||
static String checkCredit = "https://sms.kazumi.me/api/sms/check-credit";
|
static String checkCredit = "https://sms.kazumi.me/api/sms/check-credit";
|
||||||
static String checkStatus = "https://sms.kazumi.me/api/sms/check-status";
|
static String checkStatus = "https://sms.kazumi.me/api/sms/check-status";
|
||||||
|
static String getSender = "$server/auth/sms/getSender.php";
|
||||||
|
static String updatePhoneInvalidSMS =
|
||||||
|
"$server/auth/sms/updatePhoneInvalidSMS.php";
|
||||||
|
|
||||||
//////////////service///////////
|
//////////////service///////////
|
||||||
|
|
||||||
|
|||||||
@@ -10,29 +10,30 @@ class AppStyle {
|
|||||||
fontSize: 40,
|
fontSize: 40,
|
||||||
color: AppColor.accentColor,
|
color: AppColor.accentColor,
|
||||||
fontFamily: box.read(BoxName.lang) == 'ar'
|
fontFamily: box.read(BoxName.lang) == 'ar'
|
||||||
? 'mohanad'
|
// ?GoogleFonts.notoNaskhArabic().fontFamily
|
||||||
: GoogleFonts.josefinSans().fontFamily);
|
? GoogleFonts.notoNaskhArabic().fontFamily
|
||||||
|
: GoogleFonts.roboto().fontFamily);
|
||||||
static TextStyle headTitle2 = TextStyle(
|
static TextStyle headTitle2 = TextStyle(
|
||||||
fontWeight: FontWeight.bold,
|
fontWeight: FontWeight.bold,
|
||||||
fontSize: 26,
|
fontSize: 26,
|
||||||
color: AppColor.writeColor,
|
color: AppColor.writeColor,
|
||||||
fontFamily: box.read(BoxName.lang) == 'ar'
|
fontFamily: box.read(BoxName.lang) == 'ar'
|
||||||
? 'mohanad'
|
? GoogleFonts.notoNaskhArabic().fontFamily
|
||||||
: GoogleFonts.josefinSans().fontFamily);
|
: GoogleFonts.roboto().fontFamily);
|
||||||
static TextStyle title = TextStyle(
|
static TextStyle title = TextStyle(
|
||||||
fontWeight: FontWeight.normal,
|
fontWeight: FontWeight.normal,
|
||||||
fontSize: box.read(BoxName.lang) == 'ar' ? 14 : 16,
|
fontSize: box.read(BoxName.lang) == 'ar' ? 14 : 16,
|
||||||
color: AppColor.writeColor,
|
color: AppColor.writeColor,
|
||||||
fontFamily: box.read(BoxName.lang) == 'ar'
|
fontFamily: box.read(BoxName.lang) == 'ar'
|
||||||
? 'mohanad'
|
? GoogleFonts.notoNaskhArabic().fontFamily
|
||||||
: GoogleFonts.josefinSans().fontFamily);
|
: GoogleFonts.roboto().fontFamily);
|
||||||
static TextStyle subtitle = TextStyle(
|
static TextStyle subtitle = TextStyle(
|
||||||
fontWeight: FontWeight.bold,
|
fontWeight: FontWeight.bold,
|
||||||
fontSize: 13,
|
fontSize: 13,
|
||||||
color: AppColor.writeColor,
|
color: AppColor.writeColor,
|
||||||
fontFamily: box.read(BoxName.lang) == 'ar'
|
fontFamily: box.read(BoxName.lang) == 'ar'
|
||||||
? 'mohanad'
|
? GoogleFonts.notoNaskhArabic().fontFamily
|
||||||
: GoogleFonts.josefinSans().fontFamily);
|
: GoogleFonts.roboto().fontFamily);
|
||||||
static TextStyle number = const TextStyle(
|
static TextStyle number = const TextStyle(
|
||||||
fontWeight: FontWeight.bold,
|
fontWeight: FontWeight.bold,
|
||||||
fontSize: 14,
|
fontSize: 14,
|
||||||
@@ -42,9 +43,9 @@ class AppStyle {
|
|||||||
static BoxDecoration boxDecoration = const BoxDecoration(
|
static BoxDecoration boxDecoration = const BoxDecoration(
|
||||||
boxShadow: [
|
boxShadow: [
|
||||||
BoxShadow(
|
BoxShadow(
|
||||||
color: AppColor.accentColor, blurRadius: 2, offset: Offset(1, 2)),
|
color: AppColor.accentColor, blurRadius: 5, offset: Offset(2, 4)),
|
||||||
BoxShadow(
|
BoxShadow(
|
||||||
color: AppColor.accentColor, blurRadius: 2, offset: Offset(-1, -1))
|
color: AppColor.accentColor, blurRadius: 5, offset: Offset(-2, -2))
|
||||||
],
|
],
|
||||||
color: AppColor.secondaryColor,
|
color: AppColor.secondaryColor,
|
||||||
borderRadius: BorderRadius.all(
|
borderRadius: BorderRadius.all(
|
||||||
@@ -53,9 +54,13 @@ class AppStyle {
|
|||||||
static BoxDecoration boxDecoration1 = const BoxDecoration(
|
static BoxDecoration boxDecoration1 = const BoxDecoration(
|
||||||
boxShadow: [
|
boxShadow: [
|
||||||
BoxShadow(
|
BoxShadow(
|
||||||
color: AppColor.accentColor, blurRadius: 2, offset: Offset(1, 2)),
|
color: Color.fromARGB(255, 237, 230, 230),
|
||||||
|
blurRadius: 5,
|
||||||
|
offset: Offset(2, 4)),
|
||||||
BoxShadow(
|
BoxShadow(
|
||||||
color: AppColor.accentColor, blurRadius: 2, offset: Offset(-1, -1))
|
color: Color.fromARGB(255, 242, 237, 237),
|
||||||
|
blurRadius: 5,
|
||||||
|
offset: Offset(-2, -2))
|
||||||
],
|
],
|
||||||
color: AppColor.secondaryColor,
|
color: AppColor.secondaryColor,
|
||||||
borderRadius: BorderRadius.all(
|
borderRadius: BorderRadius.all(
|
||||||
|
|||||||
@@ -6,4 +6,5 @@ class TableName {
|
|||||||
static const String rideLocation = "rideLocation";
|
static const String rideLocation = "rideLocation";
|
||||||
static const String faceDetectTimes = "faceDetectTimes";
|
static const String faceDetectTimes = "faceDetectTimes";
|
||||||
static const String captainNotification = "captainNotification";
|
static const String captainNotification = "captainNotification";
|
||||||
|
static const String applyRideFromOverLay = "applyRideFromOverLay";
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,14 +1,18 @@
|
|||||||
import 'dart:convert';
|
import 'dart:convert';
|
||||||
|
|
||||||
import 'package:SEFER/constant/box_name.dart';
|
import 'package:SEFER/constant/box_name.dart';
|
||||||
|
import 'package:SEFER/constant/colors.dart';
|
||||||
import 'package:SEFER/constant/links.dart';
|
import 'package:SEFER/constant/links.dart';
|
||||||
import 'package:SEFER/controller/functions/crud.dart';
|
import 'package:SEFER/controller/functions/crud.dart';
|
||||||
import 'package:SEFER/controller/home/payment/captain_wallet_controller.dart';
|
import 'package:SEFER/controller/home/payment/captain_wallet_controller.dart';
|
||||||
import 'package:SEFER/views/widgets/mydialoug.dart';
|
import 'package:SEFER/views/widgets/mydialoug.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter_contacts/contact.dart';
|
||||||
|
import 'package:flutter_contacts/flutter_contacts.dart';
|
||||||
import 'package:get/get.dart';
|
import 'package:get/get.dart';
|
||||||
|
|
||||||
import '../../../main.dart';
|
import '../../../main.dart';
|
||||||
|
import '../../../print.dart';
|
||||||
import '../../functions/launch.dart';
|
import '../../functions/launch.dart';
|
||||||
import '../../notification/notification_captain_controller.dart';
|
import '../../notification/notification_captain_controller.dart';
|
||||||
|
|
||||||
@@ -38,6 +42,40 @@ class InviteController extends GetxController {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Future<void> pickContact() async {
|
||||||
|
try {
|
||||||
|
print('Requesting contact permission...');
|
||||||
|
if (await FlutterContacts.requestPermission(readonly: true)) {
|
||||||
|
print('Permission granted. Opening external contact picker...');
|
||||||
|
final Contact? contact = await FlutterContacts.openExternalPick();
|
||||||
|
if (contact != null) {
|
||||||
|
print('Contact picked: ${contact.displayName}');
|
||||||
|
if (contact.phones.isNotEmpty) {
|
||||||
|
print('Phone number found: ${contact.phones.first.number}');
|
||||||
|
invitePhoneController.text = contact.phones.first.number;
|
||||||
|
update();
|
||||||
|
} else {
|
||||||
|
print('Selected contact has no phone number.');
|
||||||
|
Get.snackbar('No phone number'.tr,
|
||||||
|
'The selected contact does not have a phone number.'.tr);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
print('No contact selected or picker was cancelled.');
|
||||||
|
Get.snackbar('No contact selected'.tr, 'Please select a contact'.tr);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
print('Permission denied by user or system.');
|
||||||
|
Get.snackbar('Permission denied'.tr,
|
||||||
|
'Contact permission is required to pick a contact'.tr);
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
print('Error picking contact: $e');
|
||||||
|
print('Stack trace: ${StackTrace.current}');
|
||||||
|
Get.snackbar(
|
||||||
|
'Error'.tr, 'An error occurred while picking a contact: $e'.tr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void onSelectDriverInvitation(int index) async {
|
void onSelectDriverInvitation(int index) async {
|
||||||
MyDialog().getDialog(
|
MyDialog().getDialog(
|
||||||
driverInvitationData[index]['countOfInvitDriver'] < 100
|
driverInvitationData[index]['countOfInvitDriver'] < 100
|
||||||
@@ -89,7 +127,7 @@ class InviteController extends GetxController {
|
|||||||
"driverId": box.read(BoxName.driverID),
|
"driverId": box.read(BoxName.driverID),
|
||||||
"inviterDriverPhone": '+2${invitePhoneController.text}'
|
"inviterDriverPhone": '+2${invitePhoneController.text}'
|
||||||
});
|
});
|
||||||
|
Log.print('response: ${response}');
|
||||||
if (response != 'failure') {
|
if (response != 'failure') {
|
||||||
var d = jsonDecode(response);
|
var d = jsonDecode(response);
|
||||||
Get.snackbar('Success', 'Invite sent successfully'.tr);
|
Get.snackbar('Success', 'Invite sent successfully'.tr);
|
||||||
@@ -109,7 +147,9 @@ class InviteController extends GetxController {
|
|||||||
|
|
||||||
invitePhoneController.clear();
|
invitePhoneController.clear();
|
||||||
} else {
|
} else {
|
||||||
Get.snackbar('Error', 'Failed to send invite'.tr);
|
Get.snackbar('Error'.tr, "Invite code already used".tr,
|
||||||
|
backgroundColor: AppColor.redColor,
|
||||||
|
duration: const Duration(seconds: 4));
|
||||||
}
|
}
|
||||||
// } catch (e) {
|
// } catch (e) {
|
||||||
// print('Error sending invite: $e');
|
// print('Error sending invite: $e');
|
||||||
|
|||||||
@@ -55,7 +55,7 @@ class LoginDriverController extends GetxController {
|
|||||||
'id': driverID,
|
'id': driverID,
|
||||||
});
|
});
|
||||||
print(res);
|
print(res);
|
||||||
if (res == 'Failure') {
|
if (res == 'failure') {
|
||||||
//Failure
|
//Failure
|
||||||
if (box.read(BoxName.phoneVerified).toString() == '1') {
|
if (box.read(BoxName.phoneVerified).toString() == '1') {
|
||||||
Get.offAll(() => EgyptCardAI());
|
Get.offAll(() => EgyptCardAI());
|
||||||
|
|||||||
@@ -79,6 +79,21 @@ class RegisterCaptainController extends GetxController {
|
|||||||
update();
|
update();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool isValidEgyptianPhoneNumber(String phoneNumber) {
|
||||||
|
// Remove any whitespace from the phone number
|
||||||
|
phoneNumber = phoneNumber.replaceAll(RegExp(r'\s+'), '');
|
||||||
|
|
||||||
|
// Check if the phone number has exactly 11 digits
|
||||||
|
if (phoneNumber.length != 11) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if the phone number starts with 010, 011, 012, or 015
|
||||||
|
RegExp validPrefixes = RegExp(r'^01[0125]');
|
||||||
|
|
||||||
|
return validPrefixes.hasMatch(phoneNumber);
|
||||||
|
}
|
||||||
|
|
||||||
sendOtpMessage() async {
|
sendOtpMessage() async {
|
||||||
SmsEgyptController smsEgyptController = Get.put(SmsEgyptController());
|
SmsEgyptController smsEgyptController = Get.put(SmsEgyptController());
|
||||||
|
|
||||||
@@ -87,21 +102,38 @@ class RegisterCaptainController extends GetxController {
|
|||||||
update();
|
update();
|
||||||
if (formKey3.currentState!.validate()) {
|
if (formKey3.currentState!.validate()) {
|
||||||
if (box.read(BoxName.countryCode) == 'Egypt') {
|
if (box.read(BoxName.countryCode) == 'Egypt') {
|
||||||
var responseCheker = await CRUD()
|
if (isValidEgyptianPhoneNumber(phoneController.text)) {
|
||||||
.post(link: AppLink.checkPhoneNumberISVerfiedDriver, payload: {
|
var responseCheker = await CRUD()
|
||||||
'phone_number': '+2${phoneController.text}',
|
.post(link: AppLink.checkPhoneNumberISVerfiedDriver, payload: {
|
||||||
});
|
'phone_number': '+2${phoneController.text}',
|
||||||
if (responseCheker != 'failure') {
|
});
|
||||||
var d = jsonDecode(responseCheker);
|
if (responseCheker != 'failure') {
|
||||||
if (d['message'][0]['is_verified'].toString() == '1') {
|
var d = jsonDecode(responseCheker);
|
||||||
Get.snackbar('Phone number is verified before'.tr, '',
|
if (d['message'][0]['is_verified'].toString() == '1') {
|
||||||
backgroundColor: AppColor.greenColor);
|
Get.snackbar('Phone number is verified before'.tr, '',
|
||||||
box.write(BoxName.isVerified, '1');
|
backgroundColor: AppColor.greenColor);
|
||||||
box.write(BoxName.phone, '+2${phoneController.text}');
|
box.write(BoxName.phoneVerified, '1');
|
||||||
await Get.put(LoginDriverController()).loginUsingCredentials(
|
box.write(BoxName.phone, '+2${phoneController.text}');
|
||||||
box.read(BoxName.driverID).toString(),
|
await Get.put(LoginDriverController()).loginUsingCredentials(
|
||||||
box.read(BoxName.emailDriver).toString(),
|
box.read(BoxName.driverID).toString(),
|
||||||
);
|
box.read(BoxName.emailDriver).toString(),
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
await CRUD().post(link: AppLink.sendVerifyOtpMessage, payload: {
|
||||||
|
'phone_number': '+2${phoneController.text}',
|
||||||
|
'token_code': randomNumber.toString(),
|
||||||
|
"driverId": box.read(BoxName.driverID),
|
||||||
|
"email": box.read(BoxName.emailDriver),
|
||||||
|
});
|
||||||
|
|
||||||
|
await smsEgyptController.sendSmsEgypt(
|
||||||
|
phoneController.text.toString(), randomNumber.toString());
|
||||||
|
|
||||||
|
isSent = true;
|
||||||
|
|
||||||
|
isLoading = false;
|
||||||
|
update();
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
await CRUD().post(link: AppLink.sendVerifyOtpMessage, payload: {
|
await CRUD().post(link: AppLink.sendVerifyOtpMessage, payload: {
|
||||||
'phone_number': '+2${phoneController.text}',
|
'phone_number': '+2${phoneController.text}',
|
||||||
@@ -112,25 +144,15 @@ class RegisterCaptainController extends GetxController {
|
|||||||
|
|
||||||
await smsEgyptController.sendSmsEgypt(
|
await smsEgyptController.sendSmsEgypt(
|
||||||
phoneController.text.toString(), randomNumber.toString());
|
phoneController.text.toString(), randomNumber.toString());
|
||||||
|
|
||||||
isSent = true;
|
isSent = true;
|
||||||
|
|
||||||
isLoading = false;
|
isLoading = false;
|
||||||
update();
|
update();
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
await CRUD().post(link: AppLink.sendVerifyOtpMessage, payload: {
|
Get.snackbar('Phone Number wrong'.tr, '',
|
||||||
'phone_number': '+2${phoneController.text}',
|
backgroundColor: AppColor.redColor);
|
||||||
'token_code': randomNumber.toString(),
|
|
||||||
"driverId": box.read(BoxName.driverID),
|
|
||||||
"email": box.read(BoxName.emailDriver),
|
|
||||||
});
|
|
||||||
|
|
||||||
await smsEgyptController.sendSmsEgypt(
|
|
||||||
phoneController.text.toString(), randomNumber.toString());
|
|
||||||
isSent = true;
|
|
||||||
|
|
||||||
isLoading = false;
|
|
||||||
update();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import 'package:SEFER/constant/box_name.dart';
|
import 'package:SEFER/constant/box_name.dart';
|
||||||
|
import 'package:SEFER/constant/colors.dart';
|
||||||
import 'package:SEFER/controller/auth/captin/login_captin_controller.dart';
|
import 'package:SEFER/controller/auth/captin/login_captin_controller.dart';
|
||||||
import 'package:SEFER/main.dart';
|
import 'package:SEFER/main.dart';
|
||||||
import 'package:SEFER/views/auth/captin/cards/sms_signup.dart';
|
import 'package:SEFER/views/auth/captin/cards/sms_signup.dart';
|
||||||
@@ -35,22 +36,69 @@ class GoogleSignInHelper {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// static Future<GoogleSignInAccount?> signInFromLogin() async {
|
||||||
|
// // try {
|
||||||
|
// // final GoogleSignInAccount? googleUser = await _googleSignIn.signIn();
|
||||||
|
// // if (googleUser != null) {
|
||||||
|
// // await _handleSignUp(googleUser);
|
||||||
|
// // // if (box.read(BoxName.countryCode) == 'Egypt') {
|
||||||
|
// // await Get.find<LoginDriverController>().loginUsingCredentials(
|
||||||
|
// // box.read(BoxName.driverID).toString(),
|
||||||
|
// // box.read(BoxName.emailDriver).toString(),
|
||||||
|
// // );
|
||||||
|
// // // } else if (box.read(BoxName.countryCode) == 'Jordan') {
|
||||||
|
// // // // Get.to(() => AiPage());
|
||||||
|
// // // }
|
||||||
|
// // }
|
||||||
|
// // return googleUser;
|
||||||
|
// // } catch (error) {
|
||||||
|
// // return null;
|
||||||
|
// // }
|
||||||
|
// try {
|
||||||
|
// final GoogleSignInAccount? googleUser = await _googleSignIn.signIn();
|
||||||
|
// if (googleUser != null) {
|
||||||
|
// // Handle sign-up logic
|
||||||
|
// await _handleSignUp(googleUser);
|
||||||
|
//
|
||||||
|
// // Get country code
|
||||||
|
// final String countryCode = box.read(BoxName.countryCode).toString();
|
||||||
|
// final String driverID = box.read(BoxName.driverID).toString();
|
||||||
|
// final String emailDriver = box.read(BoxName.emailDriver).toString();
|
||||||
|
//
|
||||||
|
// // Log-in using credentials based on country code
|
||||||
|
// if (countryCode == 'Egypt') {
|
||||||
|
// await Get.find<LoginDriverController>()
|
||||||
|
// .loginUsingCredentials(driverID, emailDriver);
|
||||||
|
// } else if (countryCode == 'Jordan') {
|
||||||
|
// // Add logic for Jordan if needed, e.g., navigate to AiPage
|
||||||
|
// // Get.to(() => AiPage());
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// return googleUser;
|
||||||
|
// } catch (error) {
|
||||||
|
// Get.snackbar('Google Sign-In error', '$error',
|
||||||
|
// backgroundColor: AppColor.redColor);
|
||||||
|
// // Log error details
|
||||||
|
// print('Google Sign-In error: $error');
|
||||||
|
// return null;
|
||||||
|
// }
|
||||||
|
// }
|
||||||
static Future<GoogleSignInAccount?> signInFromLogin() async {
|
static Future<GoogleSignInAccount?> signInFromLogin() async {
|
||||||
try {
|
try {
|
||||||
final GoogleSignInAccount? googleUser = await _googleSignIn.signIn();
|
final GoogleSignInAccount? googleUser = await _googleSignIn.signIn();
|
||||||
if (googleUser != null) {
|
if (googleUser != null) {
|
||||||
await _handleSignUp(googleUser);
|
await _handleSignUp(googleUser);
|
||||||
// if (box.read(BoxName.countryCode) == 'Egypt') {
|
|
||||||
await Get.find<LoginDriverController>().loginUsingCredentials(
|
await Get.find<LoginDriverController>().loginUsingCredentials(
|
||||||
box.read(BoxName.driverID).toString(),
|
box.read(BoxName.driverID).toString(),
|
||||||
box.read(BoxName.emailDriver).toString(),
|
box.read(BoxName.emailDriver).toString(),
|
||||||
);
|
);
|
||||||
// } else if (box.read(BoxName.countryCode) == 'Jordan') {
|
|
||||||
// // Get.to(() => AiPage());
|
|
||||||
// }
|
|
||||||
}
|
}
|
||||||
return googleUser;
|
return googleUser;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
Get.snackbar('Google Sign-In error', '$error',
|
||||||
|
backgroundColor: AppColor.redColor);
|
||||||
|
// Log error details
|
||||||
|
print('Google Sign-In error: $error');
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
18
lib/controller/firebase/bring_app_foreground.dart
Normal file
18
lib/controller/firebase/bring_app_foreground.dart
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter/services.dart';
|
||||||
|
|
||||||
|
class AppLifecycleManager {
|
||||||
|
static const platform = MethodChannel('com.sefer_driver/app_lifecycle');
|
||||||
|
|
||||||
|
static Future<void> bringAppToForeground() async {
|
||||||
|
try {
|
||||||
|
debugPrint('Attempting to bring app to foreground');
|
||||||
|
await platform.invokeMethod('bringAppToForeground');
|
||||||
|
debugPrint('Method invocation completed');
|
||||||
|
} on PlatformException catch (e) {
|
||||||
|
debugPrint("Failed to bring app to foreground: '${e.message}'.");
|
||||||
|
} catch (e) {
|
||||||
|
debugPrint("Unexpected error: $e");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,7 +1,6 @@
|
|||||||
import 'dart:convert';
|
import 'dart:convert';
|
||||||
import 'dart:io';
|
import 'dart:io';
|
||||||
import 'package:SEFER/controller/home/captin/home_captain_controller.dart';
|
import 'package:SEFER/controller/home/captin/home_captain_controller.dart';
|
||||||
import 'package:SEFER/views/home/Captin/home_captain/widget/call_page.dart';
|
|
||||||
import 'package:SEFER/views/widgets/mydialoug.dart';
|
import 'package:SEFER/views/widgets/mydialoug.dart';
|
||||||
import 'package:firebase_messaging/firebase_messaging.dart';
|
import 'package:firebase_messaging/firebase_messaging.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
@@ -79,9 +78,27 @@ class FirebaseMessagesController extends GetxController {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
FirebaseMessaging.onBackgroundMessage((RemoteMessage message) async {
|
FirebaseMessaging.onBackgroundMessage((RemoteMessage message) async {
|
||||||
// Handle background message
|
if (message.notification!.title! == 'Order'.tr) {
|
||||||
if (message.data.isNotEmpty && message.notification != null) {
|
if (Platform.isAndroid) {
|
||||||
fireBaseTitles(message);
|
NotificationController()
|
||||||
|
.showNotification('Order'.tr, '', 'order', 'order_page_payload');
|
||||||
|
}
|
||||||
|
// await FirebaseMessagesController().showOverlayNotification(message);
|
||||||
|
var myListString = message.data['DriverList'];
|
||||||
|
// var points = message.data['PolylineJson'];
|
||||||
|
|
||||||
|
var myList = jsonDecode(myListString) as List<dynamic>;
|
||||||
|
// var myPoints = jsonDecode(points) as List<dynamic>;
|
||||||
|
driverToken = myList[14].toString();
|
||||||
|
// This is for location using and uploading status
|
||||||
|
Get.put(HomeCaptainController()).changeRideId();
|
||||||
|
update();
|
||||||
|
Get.to(() => OrderRequestPage(), arguments: {
|
||||||
|
'myListString': myListString,
|
||||||
|
'DriverList': myList,
|
||||||
|
// 'PolylineJson': myPoints,
|
||||||
|
'body': message.notification!.body
|
||||||
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -95,8 +112,9 @@ class FirebaseMessagesController extends GetxController {
|
|||||||
Future<void> fireBaseTitles(RemoteMessage message) async {
|
Future<void> fireBaseTitles(RemoteMessage message) async {
|
||||||
if (message.notification!.title! == 'Order'.tr) {
|
if (message.notification!.title! == 'Order'.tr) {
|
||||||
if (Platform.isAndroid) {
|
if (Platform.isAndroid) {
|
||||||
NotificationController().showNotification('Order'.tr, '', 'order');
|
NotificationController().showNotification('Order'.tr, '', 'order', '');
|
||||||
}
|
}
|
||||||
|
// await FirebaseMessagesController().showOverlayNotification(message);
|
||||||
var myListString = message.data['DriverList'];
|
var myListString = message.data['DriverList'];
|
||||||
// var points = message.data['PolylineJson'];
|
// var points = message.data['PolylineJson'];
|
||||||
|
|
||||||
@@ -113,8 +131,10 @@ class FirebaseMessagesController extends GetxController {
|
|||||||
'body': message.notification!.body
|
'body': message.notification!.body
|
||||||
});
|
});
|
||||||
} else if (message.notification!.title == 'Cancel Trip') {
|
} else if (message.notification!.title == 'Cancel Trip') {
|
||||||
NotificationController().showNotification(
|
// if (Platform.isAndroid) {
|
||||||
'Cancel Trip'.tr, 'Passenger Cancel Trip'.tr, 'cancel');
|
// NotificationController().showNotification(
|
||||||
|
// 'Cancel Trip'.tr, 'Passenger Cancel Trip'.tr, 'cancel', '');
|
||||||
|
// }
|
||||||
cancelTripDialog();
|
cancelTripDialog();
|
||||||
} else if (message.notification!.title! == 'token change') {
|
} else if (message.notification!.title! == 'token change') {
|
||||||
// NotificationController()
|
// NotificationController()
|
||||||
@@ -122,14 +142,18 @@ class FirebaseMessagesController extends GetxController {
|
|||||||
// GoogleSignInHelper.signOut();
|
// GoogleSignInHelper.signOut();
|
||||||
GoogleSignInHelper.signOut();
|
GoogleSignInHelper.signOut();
|
||||||
} else if (message.notification!.title! == 'message From passenger') {
|
} else if (message.notification!.title! == 'message From passenger') {
|
||||||
NotificationController()
|
if (Platform.isAndroid) {
|
||||||
.showNotification('message From passenger', ''.tr, 'tone2');
|
NotificationController()
|
||||||
|
.showNotification('message From passenger', ''.tr, 'tone2', '');
|
||||||
|
}
|
||||||
passengerDialog(message.notification!.body!);
|
passengerDialog(message.notification!.body!);
|
||||||
|
|
||||||
update();
|
update();
|
||||||
} else if (message.notification!.title! == 'face detect') {
|
} else if (message.notification!.title! == 'face detect') {
|
||||||
NotificationController()
|
if (Platform.isAndroid) {
|
||||||
.showNotification('face detect'.tr, ''.tr, 'tone2');
|
NotificationController()
|
||||||
|
.showNotification('face detect'.tr, ''.tr, 'tone2', '');
|
||||||
|
}
|
||||||
String result0 = await faceDetector();
|
String result0 = await faceDetector();
|
||||||
// Handle the result here, e.g., show a dialog or update the UI
|
// Handle the result here, e.g., show a dialog or update the UI
|
||||||
var result = jsonDecode(result0);
|
var result = jsonDecode(result0);
|
||||||
@@ -156,19 +180,20 @@ class FirebaseMessagesController extends GetxController {
|
|||||||
} else if (message.notification!.title! == 'Hi ,I will go now') {
|
} else if (message.notification!.title! == 'Hi ,I will go now') {
|
||||||
// Get.snackbar('Hi ,I will go now', '',
|
// Get.snackbar('Hi ,I will go now', '',
|
||||||
// backgroundColor: AppColor.greenColor);
|
// backgroundColor: AppColor.greenColor);
|
||||||
NotificationController().showNotification(
|
if (Platform.isAndroid) {
|
||||||
'Passenger come to you'.tr, 'Hi ,I will go now'.tr, 'tone2');
|
NotificationController().showNotification(
|
||||||
|
'Passenger come to you'.tr, 'Hi ,I will go now'.tr, 'tone2', '');
|
||||||
|
}
|
||||||
update();
|
update();
|
||||||
} else if (message.notification!.title! == 'Call Income'.tr) {
|
} else if (message.notification!.title! == 'Call Income'.tr) {
|
||||||
try {
|
try {
|
||||||
var myListString = message.data['passengerList'];
|
var myListString = message.data['passengerList'];
|
||||||
var driverList = jsonDecode(myListString) as List<dynamic>;
|
var driverList = jsonDecode(myListString) as List<dynamic>;
|
||||||
// if (Platform.isAndroid) {
|
// if (Platform.isAndroid) {
|
||||||
NotificationController().showNotification(
|
if (Platform.isAndroid) {
|
||||||
'Call Income'.tr,
|
NotificationController().showNotification('Call Income'.tr,
|
||||||
message.notification!.body!,
|
message.notification!.body!, 'iphone_ringtone', '');
|
||||||
'iphone_ringtone',
|
}
|
||||||
);
|
|
||||||
// }
|
// }
|
||||||
// Assuming GetMaterialApp is initialized and context is valid for navigation
|
// Assuming GetMaterialApp is initialized and context is valid for navigation
|
||||||
// Get.to(() => PassengerCallPage(
|
// Get.to(() => PassengerCallPage(
|
||||||
@@ -183,11 +208,10 @@ class FirebaseMessagesController extends GetxController {
|
|||||||
var myListString = message.data['passengerList'];
|
var myListString = message.data['passengerList'];
|
||||||
var driverList = jsonDecode(myListString) as List<dynamic>;
|
var driverList = jsonDecode(myListString) as List<dynamic>;
|
||||||
// if (Platform.isAndroid) {
|
// if (Platform.isAndroid) {
|
||||||
NotificationController().showNotification(
|
if (Platform.isAndroid) {
|
||||||
'Call Income'.tr,
|
NotificationController().showNotification('Call Income'.tr,
|
||||||
message.notification!.body!,
|
message.notification!.body!, 'iphone_ringtone', '');
|
||||||
'iphone_ringtone',
|
}
|
||||||
);
|
|
||||||
// }
|
// }
|
||||||
// Assuming GetMaterialApp is initialized and context is valid for navigation
|
// Assuming GetMaterialApp is initialized and context is valid for navigation
|
||||||
// Get.to(() => CallPage(
|
// Get.to(() => CallPage(
|
||||||
@@ -198,11 +222,13 @@ class FirebaseMessagesController extends GetxController {
|
|||||||
} catch (e) {}
|
} catch (e) {}
|
||||||
} else if (message.notification!.title! ==
|
} else if (message.notification!.title! ==
|
||||||
"Criminal Document Required".tr) {
|
"Criminal Document Required".tr) {
|
||||||
NotificationController().showNotification(
|
if (Platform.isAndroid) {
|
||||||
"Criminal Document Required".tr,
|
NotificationController().showNotification(
|
||||||
message.notification!.body!,
|
"Criminal Document Required".tr,
|
||||||
'tone2',
|
message.notification!.body!,
|
||||||
);
|
'tone2',
|
||||||
|
'');
|
||||||
|
}
|
||||||
MyDialog().getDialog(
|
MyDialog().getDialog(
|
||||||
"Criminal Document Required".tr, 'You should have upload it .'.tr,
|
"Criminal Document Required".tr, 'You should have upload it .'.tr,
|
||||||
() {
|
() {
|
||||||
@@ -215,10 +241,7 @@ class FirebaseMessagesController extends GetxController {
|
|||||||
var driverList = jsonDecode(myListString) as List<dynamic>;
|
var driverList = jsonDecode(myListString) as List<dynamic>;
|
||||||
if (Platform.isAndroid) {
|
if (Platform.isAndroid) {
|
||||||
NotificationController().showNotification(
|
NotificationController().showNotification(
|
||||||
'Call End'.tr,
|
'Call End'.tr, message.notification!.body!, 'tone2', '');
|
||||||
message.notification!.body!,
|
|
||||||
'tone2',
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
// Assuming GetMaterialApp is initialized and context is valid for navigation
|
// Assuming GetMaterialApp is initialized and context is valid for navigation
|
||||||
// Get.off(const CallPage());
|
// Get.off(const CallPage());
|
||||||
@@ -247,11 +270,14 @@ class FirebaseMessagesController extends GetxController {
|
|||||||
'body': message.notification!.body
|
'body': message.notification!.body
|
||||||
});
|
});
|
||||||
} else if (message.notification!.title! == 'Order Applied'.tr) {
|
} else if (message.notification!.title! == 'Order Applied'.tr) {
|
||||||
NotificationController().showNotification(
|
if (Platform.isAndroid) {
|
||||||
'The order Accepted by another Driver'.tr,
|
NotificationController().showNotification(
|
||||||
'We regret to inform you that another driver has accepted this order.'
|
'The order Accepted by another Driver'.tr,
|
||||||
.tr,
|
'We regret to inform you that another driver has accepted this order.'
|
||||||
'order');
|
.tr,
|
||||||
|
'order',
|
||||||
|
'');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -623,3 +649,34 @@ class FirebaseMessagesController extends GetxController {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class OverlayContent extends StatelessWidget {
|
||||||
|
final String title;
|
||||||
|
final String body;
|
||||||
|
|
||||||
|
OverlayContent(this.title, this.body);
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Material(
|
||||||
|
child: Container(
|
||||||
|
padding: EdgeInsets.all(16.0),
|
||||||
|
color: Colors.white,
|
||||||
|
child: Column(
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
children: [
|
||||||
|
Text(
|
||||||
|
title,
|
||||||
|
style: TextStyle(fontSize: 24, fontWeight: FontWeight.bold),
|
||||||
|
),
|
||||||
|
SizedBox(height: 8.0),
|
||||||
|
Text(
|
||||||
|
body,
|
||||||
|
style: TextStyle(fontSize: 16),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,6 +1,15 @@
|
|||||||
|
import 'dart:convert';
|
||||||
|
|
||||||
|
import 'package:SEFER/constant/box_name.dart';
|
||||||
|
import 'package:SEFER/constant/colors.dart';
|
||||||
|
import 'package:SEFER/views/home/Captin/orderCaptin/order_request_page.dart';
|
||||||
|
import 'package:SEFER/views/home/my_wallet/walet_captain.dart';
|
||||||
import 'package:flutter_local_notifications/flutter_local_notifications.dart';
|
import 'package:flutter_local_notifications/flutter_local_notifications.dart';
|
||||||
import 'package:get/get.dart';
|
import 'package:get/get.dart';
|
||||||
|
|
||||||
|
import '../../main.dart';
|
||||||
|
import '../../print.dart';
|
||||||
|
|
||||||
class NotificationController extends GetxController {
|
class NotificationController extends GetxController {
|
||||||
final FlutterLocalNotificationsPlugin _flutterLocalNotificationsPlugin =
|
final FlutterLocalNotificationsPlugin _flutterLocalNotificationsPlugin =
|
||||||
FlutterLocalNotificationsPlugin();
|
FlutterLocalNotificationsPlugin();
|
||||||
@@ -8,22 +17,108 @@ class NotificationController extends GetxController {
|
|||||||
// Initializes the local notifications plugin
|
// Initializes the local notifications plugin
|
||||||
Future<void> initNotifications() async {
|
Future<void> initNotifications() async {
|
||||||
const AndroidInitializationSettings android =
|
const AndroidInitializationSettings android =
|
||||||
AndroidInitializationSettings('@mipmap/launcher_icon');
|
AndroidInitializationSettings('app_icon');
|
||||||
|
|
||||||
const InitializationSettings initializationSettings =
|
const InitializationSettings initializationSettings =
|
||||||
InitializationSettings(android: android);
|
InitializationSettings(android: android);
|
||||||
await _flutterLocalNotificationsPlugin.initialize(initializationSettings);
|
|
||||||
|
await _flutterLocalNotificationsPlugin.initialize(
|
||||||
|
initializationSettings,
|
||||||
|
onDidReceiveNotificationResponse: onDidReceiveNotificationResponse,
|
||||||
|
onDidReceiveBackgroundNotificationResponse:
|
||||||
|
onDidReceiveBackgroundNotificationResponse,
|
||||||
|
);
|
||||||
|
|
||||||
|
// Create a notification channel
|
||||||
|
const AndroidNotificationChannel channel = AndroidNotificationChannel(
|
||||||
|
'order_channel', // Channel ID
|
||||||
|
'Order Notifications', // Channel name
|
||||||
|
description:
|
||||||
|
'This channel is used for order notifications.', // Channel description
|
||||||
|
importance: Importance.max,
|
||||||
|
);
|
||||||
|
|
||||||
|
// Register the channel with the system
|
||||||
|
await _flutterLocalNotificationsPlugin
|
||||||
|
.resolvePlatformSpecificImplementation<
|
||||||
|
AndroidFlutterLocalNotificationsPlugin>()
|
||||||
|
?.createNotificationChannel(channel);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Displays a notification with the given title and message
|
// Displays a notification with the given title and message
|
||||||
void showNotification(String title, String message, String tone) async {
|
void showNotification(
|
||||||
AndroidNotificationDetails android = AndroidNotificationDetails(
|
String title, String message, String tone, String payLoad) async {
|
||||||
'your channel id', 'your channel name',
|
BigTextStyleInformation bigTextStyleInformation = BigTextStyleInformation(
|
||||||
importance: Importance.max,
|
message,
|
||||||
priority: Priority.high,
|
contentTitle: title.tr,
|
||||||
showWhen: false,
|
htmlFormatContent: true,
|
||||||
sound: RawResourceAndroidNotificationSound(tone));
|
htmlFormatContentTitle: true,
|
||||||
|
);
|
||||||
|
|
||||||
|
AndroidNotificationDetails android =
|
||||||
|
AndroidNotificationDetails('order_channel', 'Order Notifications',
|
||||||
|
importance: Importance.max,
|
||||||
|
priority: Priority.high,
|
||||||
|
styleInformation: bigTextStyleInformation,
|
||||||
|
playSound: true,
|
||||||
|
sound: RawResourceAndroidNotificationSound(tone),
|
||||||
|
audioAttributesUsage: AudioAttributesUsage.alarm,
|
||||||
|
visibility: NotificationVisibility.public,
|
||||||
|
autoCancel: false,
|
||||||
|
color: AppColor.primaryColor,
|
||||||
|
showProgress: true,
|
||||||
|
showWhen: true,
|
||||||
|
subText: message,
|
||||||
|
actions: [
|
||||||
|
AndroidNotificationAction(
|
||||||
|
allowGeneratedReplies: true,
|
||||||
|
'id',
|
||||||
|
title.tr,
|
||||||
|
titleColor: AppColor.bronze,
|
||||||
|
showsUserInterface: true,
|
||||||
|
)
|
||||||
|
],
|
||||||
|
category: AndroidNotificationCategory.call);
|
||||||
|
|
||||||
NotificationDetails details = NotificationDetails(android: android);
|
NotificationDetails details = NotificationDetails(android: android);
|
||||||
await _flutterLocalNotificationsPlugin.show(0, title, message, details);
|
|
||||||
|
await _flutterLocalNotificationsPlugin.show(0, title, message, details,
|
||||||
|
payload: payLoad);
|
||||||
|
|
||||||
|
// payload: 'order_page_payload');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Callback when the notification is tapped
|
||||||
|
void onDidReceiveNotificationResponse(NotificationResponse response) {
|
||||||
|
// jsonDecode(response.payload);
|
||||||
|
print('Notification tapped!');
|
||||||
|
if (response.payload != null) {
|
||||||
|
print('Notification payload: ${response.payload}');
|
||||||
|
// if (response.payload != 'order_page_payload') {
|
||||||
|
// Log.print('arguments: ${box.read(BoxName.rideArguments)}');
|
||||||
|
closeOverLay();
|
||||||
|
Get.to(() => OrderRequestPage(),
|
||||||
|
arguments: {'myListString': response.payload});
|
||||||
|
// }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void onDidReceiveLocalNotification(
|
||||||
|
int id, String? title, String? body, String? payload) async {
|
||||||
|
// display a dialog with the notification details, tap ok to go to another page
|
||||||
|
}
|
||||||
|
// Callback when the notification is tapped while the app is in the background
|
||||||
|
void onDidReceiveBackgroundNotificationResponse(
|
||||||
|
NotificationResponse response) {
|
||||||
|
print('Notification tapped while app is in background!');
|
||||||
|
if (response.payload != null) {
|
||||||
|
print('Notification payload: ${response.payload}');
|
||||||
|
if (response.payload == 'order') {
|
||||||
|
Get.to(() => OrderRequestPage(),
|
||||||
|
arguments: box.read(BoxName.rideArguments));
|
||||||
|
closeOverLay();
|
||||||
|
Log.print('arguments: ${box.read(BoxName.rideArguments)}');
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
37
lib/controller/firebase/order_lay.dart
Normal file
37
lib/controller/firebase/order_lay.dart
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
import 'package:SEFER/constant/links.dart';
|
||||||
|
import 'package:SEFER/controller/functions/crud.dart';
|
||||||
|
import 'package:SEFER/views/home/Captin/home_captain/home_captin.dart';
|
||||||
|
import 'package:SEFER/views/home/Captin/orderCaptin/order_request_page.dart';
|
||||||
|
import 'package:SEFER/views/widgets/elevated_btn.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:get/get.dart';
|
||||||
|
|
||||||
|
class OverlayContent1 extends StatelessWidget {
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Material(
|
||||||
|
child: Container(
|
||||||
|
padding: EdgeInsets.all(16.0),
|
||||||
|
color: Colors.white,
|
||||||
|
child: MyElevatedButton(
|
||||||
|
title: 'go to order',
|
||||||
|
onPressed: () async {
|
||||||
|
var res = await CRUD().post(
|
||||||
|
link: AppLink.addFeedBack,
|
||||||
|
payload: {
|
||||||
|
"passengerId": 'dddddd',
|
||||||
|
"feedBack": "eeeee",
|
||||||
|
},
|
||||||
|
);
|
||||||
|
print(res);
|
||||||
|
if (res != 'failure') {
|
||||||
|
Navigator.push(
|
||||||
|
context, MaterialPageRoute(builder: (cont) => HomeCaptain()));
|
||||||
|
// Get.to(OrderRequestPage());
|
||||||
|
}
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -29,6 +29,9 @@ class CRUD {
|
|||||||
},
|
},
|
||||||
);
|
);
|
||||||
// if (response.statusCode == 200) {
|
// if (response.statusCode == 200) {
|
||||||
|
// Log.print('response: ${response.request}');
|
||||||
|
// Log.print('response: ${response.body}');
|
||||||
|
// Log.print('response: ${payload}');
|
||||||
var jsonData = jsonDecode(response.body);
|
var jsonData = jsonDecode(response.body);
|
||||||
if (jsonData['status'] == 'success') {
|
if (jsonData['status'] == 'success') {
|
||||||
return response.body;
|
return response.body;
|
||||||
@@ -230,7 +233,7 @@ class CRUD {
|
|||||||
if (jsonData['status'] == 'success') {
|
if (jsonData['status'] == 'success') {
|
||||||
return response.body;
|
return response.body;
|
||||||
} else {
|
} else {
|
||||||
String errorMessage = jsonData['message'];
|
// String errorMessage = jsonData['message'];
|
||||||
// Get.snackbar('Error'.tr, errorMessage.tr,
|
// Get.snackbar('Error'.tr, errorMessage.tr,
|
||||||
// backgroundColor: AppColor.redColor);
|
// backgroundColor: AppColor.redColor);
|
||||||
return (jsonData['status']);
|
return (jsonData['status']);
|
||||||
|
|||||||
@@ -28,7 +28,7 @@ class LocationBackgroundController extends GetxController {
|
|||||||
await BackgroundLocation.setAndroidNotification(
|
await BackgroundLocation.setAndroidNotification(
|
||||||
title: "Background Location",
|
title: "Background Location",
|
||||||
message: "Tracking location...",
|
message: "Tracking location...",
|
||||||
icon: "@mipmap/ic_launcher",
|
icon: "@mipmap/launcher_icon",
|
||||||
);
|
);
|
||||||
|
|
||||||
// Set the location update interval to 5 seconds
|
// Set the location update interval to 5 seconds
|
||||||
|
|||||||
@@ -38,7 +38,7 @@ class LocationController extends GetxController {
|
|||||||
getLocation();
|
getLocation();
|
||||||
// startLocationUpdates();
|
// startLocationUpdates();
|
||||||
|
|
||||||
totalPoints = Get.put(CaptainWalletController()).totalPoints;
|
totalPoints = Get.put(CaptainWalletController()).totalPoints.toString();
|
||||||
// isActive = Get.put(HomeCaptainController()).isActive;
|
// isActive = Get.put(HomeCaptainController()).isActive;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -47,7 +47,8 @@ class LocationController extends GetxController {
|
|||||||
_locationTimer =
|
_locationTimer =
|
||||||
Timer.periodic(const Duration(seconds: 5), (timer) async {
|
Timer.periodic(const Duration(seconds: 5), (timer) async {
|
||||||
try {
|
try {
|
||||||
totalPoints = Get.find<CaptainWalletController>().totalPoints;
|
totalPoints =
|
||||||
|
Get.find<CaptainWalletController>().totalPoints.toString();
|
||||||
isActive = Get.find<HomeCaptainController>().isActive;
|
isActive = Get.find<HomeCaptainController>().isActive;
|
||||||
if (isActive) {
|
if (isActive) {
|
||||||
if (double.parse(totalPoints) > -3000) {
|
if (double.parse(totalPoints) > -3000) {
|
||||||
|
|||||||
56
lib/controller/functions/overlay_permisssion.dart
Normal file
56
lib/controller/functions/overlay_permisssion.dart
Normal file
@@ -0,0 +1,56 @@
|
|||||||
|
import 'dart:io';
|
||||||
|
|
||||||
|
import 'package:SEFER/views/widgets/mydialoug.dart';
|
||||||
|
import 'package:flutter_overlay_window/flutter_overlay_window.dart';
|
||||||
|
import 'package:get/get.dart';
|
||||||
|
import 'package:permission_handler/permission_handler.dart';
|
||||||
|
|
||||||
|
Future<void> getPermissionOverlay() async {
|
||||||
|
if (Platform.isAndroid) {
|
||||||
|
final bool status = await FlutterOverlayWindow.isPermissionGranted();
|
||||||
|
if (status == false) {
|
||||||
|
MyDialog().getDialog(
|
||||||
|
'Allow overlay permission'.tr,
|
||||||
|
'To display orders instantly, please grant permission to draw over other apps.'
|
||||||
|
.tr,
|
||||||
|
() async {
|
||||||
|
Get.back();
|
||||||
|
await FlutterOverlayWindow.requestPermission();
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> getPermissionLocation() async {
|
||||||
|
final PermissionStatus status = await Permission.location.request();
|
||||||
|
if (status.isDenied) {
|
||||||
|
MyDialog().getDialog(
|
||||||
|
'Enable Location Permission'.tr, // {en:ar}
|
||||||
|
'Allowing location access will help us display orders near you. Please enable it now.'
|
||||||
|
.tr, // {en:ar}
|
||||||
|
() async {
|
||||||
|
Get.back();
|
||||||
|
await FlutterOverlayWindow.requestPermission();
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> getOverLay(String myListString) async {
|
||||||
|
bool isOverlayActive = await FlutterOverlayWindow.isActive();
|
||||||
|
if (isOverlayActive) {
|
||||||
|
await FlutterOverlayWindow.closeOverlay();
|
||||||
|
}
|
||||||
|
await FlutterOverlayWindow.showOverlay(
|
||||||
|
enableDrag: true,
|
||||||
|
flag: OverlayFlag.focusPointer,
|
||||||
|
visibility: NotificationVisibility.visibilityPublic,
|
||||||
|
positionGravity: PositionGravity.auto,
|
||||||
|
height: 700,
|
||||||
|
width: WindowSize.matchParent,
|
||||||
|
startPosition: const OverlayPosition(0, -150),
|
||||||
|
);
|
||||||
|
|
||||||
|
await FlutterOverlayWindow.shareData(myListString);
|
||||||
|
}
|
||||||
@@ -4,21 +4,37 @@ import 'package:SEFER/constant/api_key.dart';
|
|||||||
import 'package:SEFER/constant/box_name.dart';
|
import 'package:SEFER/constant/box_name.dart';
|
||||||
import 'package:SEFER/constant/info.dart';
|
import 'package:SEFER/constant/info.dart';
|
||||||
import 'package:SEFER/constant/links.dart';
|
import 'package:SEFER/constant/links.dart';
|
||||||
|
import 'package:SEFER/controller/auth/captin/register_captin_controller.dart';
|
||||||
|
import 'package:SEFER/controller/functions/crud.dart';
|
||||||
import 'package:SEFER/main.dart';
|
import 'package:SEFER/main.dart';
|
||||||
import 'package:SEFER/views/widgets/elevated_btn.dart';
|
import 'package:SEFER/views/widgets/elevated_btn.dart';
|
||||||
import 'package:get/get.dart';
|
import 'package:get/get.dart';
|
||||||
import 'package:http/http.dart' as http;
|
import 'package:http/http.dart' as http;
|
||||||
|
|
||||||
|
import '../../print.dart';
|
||||||
|
import '../auth/captin/login_captin_controller.dart';
|
||||||
|
|
||||||
class SmsEgyptController extends GetxController {
|
class SmsEgyptController extends GetxController {
|
||||||
var headers = {'Content-Type': 'application/json'};
|
var headers = {'Content-Type': 'application/json'};
|
||||||
|
|
||||||
|
Future<String> getSender() async {
|
||||||
|
var res = await CRUD().get(link: AppLink.getSender, payload: {});
|
||||||
|
if (res != 'failure') {
|
||||||
|
var d = jsonDecode(res)['message'][0]['senderId'].toString();
|
||||||
|
return d;
|
||||||
|
} else {
|
||||||
|
return "Sefer Egy";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Future<dynamic> sendSmsEgypt(String phone, otp) async {
|
Future<dynamic> sendSmsEgypt(String phone, otp) async {
|
||||||
|
String sender = await getSender();
|
||||||
var body = jsonEncode({
|
var body = jsonEncode({
|
||||||
"username": AppInformation.appName,
|
"username": AppInformation.appName,
|
||||||
"password": AK.smsPasswordEgypt, //'E)Pu=an/@Z',
|
"password": AK.smsPasswordEgypt, //'E)Pu=an/@Z',
|
||||||
"message": "${AppInformation.appName} app code is $otp\ncopy it to app",
|
"message": "${AppInformation.appName} app code is $otp\ncopy it to app",
|
||||||
"language": box.read(BoxName.lang) == 'en' ? "e" : 'r',
|
"language": box.read(BoxName.lang) == 'en' ? "e" : 'r',
|
||||||
"sender": "Sefer Egy", // todo add sefer sender name
|
"sender": sender, //"Sefer Egy", // todo add sefer sender name
|
||||||
"receiver": "2$phone"
|
"receiver": "2$phone"
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -28,7 +44,20 @@ class SmsEgyptController extends GetxController {
|
|||||||
headers: headers,
|
headers: headers,
|
||||||
);
|
);
|
||||||
|
|
||||||
if (res.statusCode == 200) {
|
if (jsonDecode(res.body)['message'].toString() != "Success") {
|
||||||
|
await CRUD().post(link: AppLink.updatePhoneInvalidSMS, payload: {
|
||||||
|
"phone_number":
|
||||||
|
'+2${Get.find<RegisterCaptainController>().phoneController.text}'
|
||||||
|
});
|
||||||
|
box.write(BoxName.phoneDriver,
|
||||||
|
'+2${Get.find<RegisterCaptainController>().phoneController.text}');
|
||||||
|
box.write(BoxName.phoneVerified, '1');
|
||||||
|
|
||||||
|
await Get.put(LoginDriverController()).loginUsingCredentials(
|
||||||
|
box.read(BoxName.driverID).toString(),
|
||||||
|
box.read(BoxName.emailDriver).toString(),
|
||||||
|
);
|
||||||
|
} else {
|
||||||
Get.defaultDialog(
|
Get.defaultDialog(
|
||||||
title: 'You will receive code in sms message'.tr,
|
title: 'You will receive code in sms message'.tr,
|
||||||
middleText: '',
|
middleText: '',
|
||||||
|
|||||||
@@ -101,54 +101,57 @@ class ImageController extends GetxController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
choosImage(String link, String imageType) async {
|
choosImage(String link, String imageType) async {
|
||||||
final pickedImage = await picker.pickImage(
|
try {
|
||||||
source: ImageSource.camera,
|
final pickedImage = await picker.pickImage(
|
||||||
preferredCameraDevice: CameraDevice.rear,
|
source: ImageSource.camera,
|
||||||
);
|
preferredCameraDevice: CameraDevice.rear,
|
||||||
|
);
|
||||||
|
|
||||||
if (pickedImage == null) return;
|
if (pickedImage == null) return;
|
||||||
|
|
||||||
image = File(pickedImage.path);
|
image = File(pickedImage.path);
|
||||||
|
|
||||||
croppedFile = await ImageCropper().cropImage(
|
croppedFile = await ImageCropper().cropImage(
|
||||||
sourcePath: image!.path,
|
sourcePath: image!.path,
|
||||||
uiSettings: [
|
uiSettings: [
|
||||||
AndroidUiSettings(
|
AndroidUiSettings(
|
||||||
toolbarTitle: 'Cropper'.tr,
|
toolbarTitle: 'Cropper'.tr,
|
||||||
toolbarColor: AppColor.blueColor,
|
toolbarColor: AppColor.blueColor,
|
||||||
toolbarWidgetColor: AppColor.yellowColor,
|
toolbarWidgetColor: AppColor.yellowColor,
|
||||||
initAspectRatio: CropAspectRatioPreset.original,
|
initAspectRatio: CropAspectRatioPreset.original,
|
||||||
lockAspectRatio: false),
|
lockAspectRatio: false,
|
||||||
IOSUiSettings(
|
),
|
||||||
title: 'Cropper'.tr,
|
IOSUiSettings(
|
||||||
),
|
title: 'Cropper'.tr,
|
||||||
],
|
),
|
||||||
);
|
],
|
||||||
|
);
|
||||||
|
|
||||||
if (croppedFile == null) return;
|
if (croppedFile == null) return;
|
||||||
|
|
||||||
myImage = File(croppedFile!.path);
|
myImage = File(croppedFile!.path);
|
||||||
isloading = true;
|
isloading = true;
|
||||||
update();
|
update();
|
||||||
|
|
||||||
|
// Rotate the compressed image
|
||||||
|
File processedImage = await rotateImageIfNeeded(File(croppedFile!.path));
|
||||||
|
File compressedImage = await compressImage(processedImage);
|
||||||
|
|
||||||
|
print('link =$link');
|
||||||
|
|
||||||
// Rotate the compressed image
|
|
||||||
// File rotatedImage = await rotateImage(compressedImage);
|
|
||||||
File processedImage = await rotateImageIfNeeded(File(croppedFile!.path));
|
|
||||||
File compressedImage = await compressImage(processedImage);
|
|
||||||
print('link =$link');
|
|
||||||
try {
|
|
||||||
await uploadImage(
|
await uploadImage(
|
||||||
compressedImage,
|
compressedImage,
|
||||||
{
|
{
|
||||||
'driverID':
|
'driverID':
|
||||||
box.read(BoxName.driverID) ?? box.read(BoxName.passengerID),
|
box.read(BoxName.driverID) ?? box.read(BoxName.passengerID),
|
||||||
'imageType': imageType
|
'imageType': imageType,
|
||||||
},
|
},
|
||||||
link,
|
link,
|
||||||
);
|
);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
|
print('Error in choosImage: $e');
|
||||||
Get.snackbar('Image Upload Failed'.tr, e.toString(),
|
Get.snackbar('Image Upload Failed'.tr, e.toString(),
|
||||||
backgroundColor: AppColor.redColor);
|
backgroundColor: AppColor.primaryColor);
|
||||||
} finally {
|
} finally {
|
||||||
isloading = false;
|
isloading = false;
|
||||||
update();
|
update();
|
||||||
|
|||||||
@@ -229,7 +229,7 @@ class HomeCaptainController extends GetxController {
|
|||||||
getAllPayment();
|
getAllPayment();
|
||||||
startPeriodicExecution();
|
startPeriodicExecution();
|
||||||
onMapCreated(mapHomeCaptainController!);
|
onMapCreated(mapHomeCaptainController!);
|
||||||
totalPoints = Get.find<CaptainWalletController>().totalPoints;
|
totalPoints = Get.find<CaptainWalletController>().totalPoints.toString();
|
||||||
getRefusedOrderByCaptain();
|
getRefusedOrderByCaptain();
|
||||||
// LocationController().getLocation();
|
// LocationController().getLocation();
|
||||||
super.onInit();
|
super.onInit();
|
||||||
|
|||||||
@@ -26,8 +26,10 @@ class MapDriverController extends GetxController {
|
|||||||
bool isLoading = true;
|
bool isLoading = true;
|
||||||
final formKey1 = GlobalKey<FormState>();
|
final formKey1 = GlobalKey<FormState>();
|
||||||
final formKey2 = GlobalKey<FormState>();
|
final formKey2 = GlobalKey<FormState>();
|
||||||
|
final formKeyCancel = GlobalKey<FormState>();
|
||||||
final messageToPassenger = TextEditingController();
|
final messageToPassenger = TextEditingController();
|
||||||
final sosEmergincyNumberCotroller = TextEditingController();
|
final sosEmergincyNumberCotroller = TextEditingController();
|
||||||
|
final cancelTripCotroller = TextEditingController();
|
||||||
List data = [];
|
List data = [];
|
||||||
List dataDestination = [];
|
List dataDestination = [];
|
||||||
LatLngBounds? boundsData;
|
LatLngBounds? boundsData;
|
||||||
@@ -165,6 +167,30 @@ class MapDriverController extends GetxController {
|
|||||||
update();
|
update();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
cancelTripFromDriverAfterApplied() async {
|
||||||
|
if (formKeyCancel.currentState!.validate()) {
|
||||||
|
await CRUD().post(link: AppLink.updateRides, payload: {
|
||||||
|
"id": rideId.toString(), // Convert to String
|
||||||
|
"status": 'CancelFromDriverAfterApply'
|
||||||
|
});
|
||||||
|
await CRUD().post(link: AppLink.addCancelRideFromPassenger, payload: {
|
||||||
|
"rideID": rideId.toString(),
|
||||||
|
"driverID": box.read(BoxName.driverID).toString(),
|
||||||
|
"passengerID": passengerId.toString(),
|
||||||
|
"note": cancelTripCotroller.text.toString()
|
||||||
|
});
|
||||||
|
FirebaseMessagesController().sendNotificationToDriverMAP(
|
||||||
|
"Cancel Trip from driver".tr,
|
||||||
|
"Trip Cancelled from driver. We are looking for a new driver. Please wait."
|
||||||
|
.tr,
|
||||||
|
tokenPassenger,
|
||||||
|
[],
|
||||||
|
'cancel.wav',
|
||||||
|
);
|
||||||
|
Get.offAll(HomeCaptain());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void startTimerToShowPassengerInfoWindowFromDriver() async {
|
void startTimerToShowPassengerInfoWindowFromDriver() async {
|
||||||
if (box.read(BoxName.rideStatus) == 'Begin') {
|
if (box.read(BoxName.rideStatus) == 'Begin') {
|
||||||
isPassengerInfoWindow = false;
|
isPassengerInfoWindow = false;
|
||||||
|
|||||||
@@ -33,6 +33,12 @@ class OrderRequestController extends GetxController {
|
|||||||
super.onInit();
|
super.onInit();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getRideDEtailsForBackgroundOrder() async {
|
||||||
|
await CRUD().get(link: AppLink.getRidesDetails, payload: {
|
||||||
|
'id': box.read(BoxName.myList)[2].toString(),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
void addCustomStartIcon() async {
|
void addCustomStartIcon() async {
|
||||||
// Create the marker with the resized image
|
// Create the marker with the resized image
|
||||||
|
|
||||||
|
|||||||
@@ -106,18 +106,18 @@ class CaptainWalletController extends GetxController {
|
|||||||
update();
|
update();
|
||||||
var res = await CRUD().get(
|
var res = await CRUD().get(
|
||||||
link: AppLink.getAllPaymentFromRide,
|
link: AppLink.getAllPaymentFromRide,
|
||||||
payload: {'driverID': box.read(BoxName.driverID)},
|
payload: {'driverID': box.read(BoxName.driverID).toString()},
|
||||||
);
|
);
|
||||||
// isLoading = false;
|
// isLoading = false;
|
||||||
if (res != 'failure') {
|
if (res != 'failure') {
|
||||||
walletDate = jsonDecode(res);
|
walletDate = jsonDecode(res);
|
||||||
totalAmount = walletDate['message'][0]['total_amount'] ?? '0';
|
totalAmount = walletDate['message'][0]['total_amount'].toString();
|
||||||
update();
|
update();
|
||||||
var res1 = await CRUD().get(
|
var res1 = await CRUD().get(
|
||||||
link: AppLink.getAllPaymentVisa,
|
link: AppLink.getAllPaymentVisa,
|
||||||
payload: {'driverID': box.read(BoxName.driverID)});
|
payload: {'driverID': box.read(BoxName.driverID).toString()});
|
||||||
walletDateVisa = jsonDecode(res1);
|
walletDateVisa = jsonDecode(res1);
|
||||||
totalAmountVisa = walletDateVisa['message'][0]['diff'] ?? '0';
|
totalAmountVisa = walletDateVisa['message'][0]['diff'].toString();
|
||||||
|
|
||||||
update();
|
update();
|
||||||
} else {
|
} else {
|
||||||
@@ -132,12 +132,13 @@ class CaptainWalletController extends GetxController {
|
|||||||
|
|
||||||
var res = await CRUD().get(
|
var res = await CRUD().get(
|
||||||
link: AppLink.getDriverPaymentPoints,
|
link: AppLink.getDriverPaymentPoints,
|
||||||
payload: {'driverID': box.read(BoxName.driverID)},
|
payload: {'driverID': box.read(BoxName.driverID).toString()},
|
||||||
);
|
);
|
||||||
isLoading = false;
|
isLoading = false;
|
||||||
// update();
|
// update();
|
||||||
walletDriverPointsDate = jsonDecode(res);
|
|
||||||
if (res != 'failure') {
|
if (res != 'failure') {
|
||||||
|
walletDriverPointsDate = jsonDecode(res);
|
||||||
double totalPointsDouble = double.parse(
|
double totalPointsDouble = double.parse(
|
||||||
walletDriverPointsDate['message'][0]['total_amount'].toString());
|
walletDriverPointsDate['message'][0]['total_amount'].toString());
|
||||||
totalPoints = totalPointsDouble.toStringAsFixed(0);
|
totalPoints = totalPointsDouble.toStringAsFixed(0);
|
||||||
@@ -283,7 +284,7 @@ class CaptainWalletController extends GetxController {
|
|||||||
);
|
);
|
||||||
if (res != 'failure') {
|
if (res != 'failure') {
|
||||||
var json = jsonDecode(res);
|
var json = jsonDecode(res);
|
||||||
kazan = double.parse(json['message'][0]['kazan']);
|
kazan = double.parse(json['message'][0]['kazan'].toString());
|
||||||
// naturePrice = double.parse(json['message'][0]['naturePrice']);
|
// naturePrice = double.parse(json['message'][0]['naturePrice']);
|
||||||
// heavyPrice = double.parse(json['message'][0]['heavyPrice']);
|
// heavyPrice = double.parse(json['message'][0]['heavyPrice']);
|
||||||
// latePrice = double.parse(json['message'][0]['latePrice']);
|
// latePrice = double.parse(json['message'][0]['latePrice']);
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ class LocaleController extends GetxController {
|
|||||||
String countryCode = '';
|
String countryCode = '';
|
||||||
void restartApp() {
|
void restartApp() {
|
||||||
// Get.offAll(MyApp);
|
// Get.offAll(MyApp);
|
||||||
runApp(const MyApp());
|
runApp(MyApp());
|
||||||
}
|
}
|
||||||
|
|
||||||
ThemeData appTheme = themeEnglish;
|
ThemeData appTheme = themeEnglish;
|
||||||
|
|||||||
@@ -150,7 +150,29 @@ class MyTranslation extends Translations {
|
|||||||
"This driver is not registered": "هذا السائق غير مسجل",
|
"This driver is not registered": "هذا السائق غير مسجل",
|
||||||
'insert amount': "أدخل المبلغ",
|
'insert amount': "أدخل المبلغ",
|
||||||
"phone number of driver": "رقم هاتف السائق",
|
"phone number of driver": "رقم هاتف السائق",
|
||||||
"Transfer budget": "نقل الميزانية",
|
"Transfer budget": "نقل الميزانية", "Comfort": "كمفورت",
|
||||||
|
"Speed": "سبيد",
|
||||||
|
"Lady": "ليدي", "Permission denied": "تم رفض الإذن",
|
||||||
|
"Contact permission is required to pick a contact":
|
||||||
|
"مطلوب إذن الوصول إلى جهات الاتصال لاختيار جهة اتصال",
|
||||||
|
"No contact selected": "لم يتم تحديد جهة اتصال",
|
||||||
|
"Please select a contact": "يرجى تحديد جهة اتصال",
|
||||||
|
"No phone number": "لا يوجد رقم هاتف",
|
||||||
|
"The selected contact does not have a phone number":
|
||||||
|
"جهة الاتصال المحددة لا تحتوي على رقم هاتف",
|
||||||
|
"Error": "خطأ",
|
||||||
|
"An error occurred while picking a contact":
|
||||||
|
"حدث خطأ أثناء اختيار جهة الاتصال",
|
||||||
|
"Are you sure you want to cancel this trip?":
|
||||||
|
"هل أنت متأكد من أنك تريد إلغاء هذه الرحلة؟",
|
||||||
|
"Cancel Trip from driver": "إلغاء الرحلة من السائق",
|
||||||
|
"Why do you want to cancel this trip?":
|
||||||
|
"لماذا تريد إلغاء هذه الرحلة؟",
|
||||||
|
"Write the reason for canceling the trip": "اكتب سبب الإلغاء:",
|
||||||
|
"Trip Cancelled from driver. We are looking for a new driver. Please wait.":
|
||||||
|
"تم إلغاء الرحلة من قبل السائق. نحن نبحث عن سائق جديد. من فضلك انتظر.",
|
||||||
|
"Delivery": "توصيل",
|
||||||
|
"Mashwari": "مشواري", "Total Net": "صافي الإجمالي",
|
||||||
"Special Order": "طلب خاص",
|
"Special Order": "طلب خاص",
|
||||||
"Speed Order": "طلب سريع",
|
"Speed Order": "طلب سريع",
|
||||||
"No data yet!": "لا توجد بيانات حتى الآن!",
|
"No data yet!": "لا توجد بيانات حتى الآن!",
|
||||||
@@ -200,6 +222,7 @@ class MyTranslation extends Translations {
|
|||||||
"History Page": "سجل الرحلات ",
|
"History Page": "سجل الرحلات ",
|
||||||
"Finished": "مكتملة",
|
"Finished": "مكتملة",
|
||||||
"Trip Detail": "تفاصيل الرحلة",
|
"Trip Detail": "تفاصيل الرحلة",
|
||||||
|
"Invite code already used": "تم استخدام رمز الدعوة بالفعل",
|
||||||
"Price is": "السعر",
|
"Price is": "السعر",
|
||||||
"Times of Trip": "أوقات الرحلة",
|
"Times of Trip": "أوقات الرحلة",
|
||||||
"Time to Passenger is": "الوقت للوصول للراكب",
|
"Time to Passenger is": "الوقت للوصول للراكب",
|
||||||
@@ -258,8 +281,17 @@ class MyTranslation extends Translations {
|
|||||||
"التقط صورة لرخصة سيارتك من الأمام",
|
"التقط صورة لرخصة سيارتك من الأمام",
|
||||||
"Capture an Image of Your ID Document front":
|
"Capture an Image of Your ID Document front":
|
||||||
"التقاط صورة لوثيقة هويتك من الأمام",
|
"التقاط صورة لوثيقة هويتك من الأمام",
|
||||||
"NationalID": "الرقم الوطني",
|
"NationalID": "الرقم الوطني", "reject your order.": "رفض طلبك.",
|
||||||
|
"Order Under Review": "الطلب قيد المراجعة",
|
||||||
|
"is reviewing your order. They may need more information or a higher price.":
|
||||||
|
"يتم مراجعة طلبك. قد يحتاجون إلى مزيد من المعلومات أو سعر أعلى.",
|
||||||
"FullName": "الاسم الكامل",
|
"FullName": "الاسم الكامل",
|
||||||
|
"Enable Location Permission": "تمكين إذن الموقع",
|
||||||
|
'Allowing location access will help us display orders near you. Please enable it now.':
|
||||||
|
"سيساعدنا السماح بالوصول إلى الموقع في عرض الطلبات القريبة منك. يرجى تمكينه الآن.",
|
||||||
|
"Allow overlay permission": "السماح بإذنالظهور فوق التطبيقات",
|
||||||
|
"To display orders instantly, please grant permission to draw over other apps.":
|
||||||
|
"لعرض الطلبات على الفور، يرجى منح إذن لرسم فوق التطبيقات الأخرى.",
|
||||||
"InspectionResult": "نتيجة الفحص",
|
"InspectionResult": "نتيجة الفحص",
|
||||||
"Criminal Record": "صحيفة الحالة الجنائية",
|
"Criminal Record": "صحيفة الحالة الجنائية",
|
||||||
"The email or phone number is already registered.":
|
"The email or phone number is already registered.":
|
||||||
@@ -331,10 +363,13 @@ class MyTranslation extends Translations {
|
|||||||
"Invite sent successfully": "تم إرسال الدعوة بنجاح",
|
"Invite sent successfully": "تم إرسال الدعوة بنجاح",
|
||||||
"Failed to send invite": "فشل إرسال الدعوة",
|
"Failed to send invite": "فشل إرسال الدعوة",
|
||||||
"An error occurred": "حدث خطأ",
|
"An error occurred": "حدث خطأ",
|
||||||
|
"Air Condition Trip": "رحلة تكييف ",
|
||||||
|
"Passenger name: ": "اسم الراكب: ",
|
||||||
"Criminal Document Required": "الفيش الجنائي مطلوب",
|
"Criminal Document Required": "الفيش الجنائي مطلوب",
|
||||||
"Criminal Document": "الفيش الجنائي",
|
"Criminal Document": "الفيش الجنائي",
|
||||||
"Marital Status": "الحالة الاجتماعية",
|
"Marital Status": "الحالة الاجتماعية",
|
||||||
"Full Name (Marital)": "الاسم الكامل (الزوجي)",
|
"Full Name (Marital)": "الاسم الكامل (الزوجي)",
|
||||||
|
"Payment Method": "طريقة الدفع",
|
||||||
"Expiration Date": "تاريخ الانتهاء",
|
"Expiration Date": "تاريخ الانتهاء",
|
||||||
"Capture an Image of Your ID Document Back":
|
"Capture an Image of Your ID Document Back":
|
||||||
"التقاط صورة للجهة الخلفية من وثيقة الهوية الخاصة بك",
|
"التقاط صورة للجهة الخلفية من وثيقة الهوية الخاصة بك",
|
||||||
|
|||||||
@@ -4,6 +4,8 @@ import 'package:dio/dio.dart';
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:webview_flutter/webview_flutter.dart';
|
import 'package:webview_flutter/webview_flutter.dart';
|
||||||
|
|
||||||
|
import '../../../print.dart';
|
||||||
|
|
||||||
class PaymobResponseWallet {
|
class PaymobResponseWallet {
|
||||||
final bool success;
|
final bool success;
|
||||||
final String? transactionID;
|
final String? transactionID;
|
||||||
@@ -142,7 +144,7 @@ class PaymobPaymentWallet {
|
|||||||
}) async {
|
}) async {
|
||||||
final Map<String, dynamic> data = {
|
final Map<String, dynamic> data = {
|
||||||
"source": {
|
"source": {
|
||||||
"identifier": "01010101010", // Replace with actual source identifier
|
"identifier": box.read(BoxName.phoneDriver).toString(),
|
||||||
"subtype": "WALLET",
|
"subtype": "WALLET",
|
||||||
},
|
},
|
||||||
"payment_token": paymentToken,
|
"payment_token": paymentToken,
|
||||||
@@ -217,6 +219,7 @@ class PaymobPaymentWallet {
|
|||||||
),
|
),
|
||||||
);
|
);
|
||||||
final urlWallet = await _getWalletUrl(paymentToken: purchaseToken);
|
final urlWallet = await _getWalletUrl(paymentToken: purchaseToken);
|
||||||
|
Log.print('urlWallet: ${urlWallet}');
|
||||||
|
|
||||||
if (context.mounted) {
|
if (context.mounted) {
|
||||||
final response = await PaymobIFrameWallet.show(
|
final response = await PaymobIFrameWallet.show(
|
||||||
|
|||||||
@@ -17,10 +17,7 @@ import 'package:flutter/foundation.dart'
|
|||||||
class DefaultFirebaseOptions {
|
class DefaultFirebaseOptions {
|
||||||
static FirebaseOptions get currentPlatform {
|
static FirebaseOptions get currentPlatform {
|
||||||
if (kIsWeb) {
|
if (kIsWeb) {
|
||||||
throw UnsupportedError(
|
return web;
|
||||||
'DefaultFirebaseOptions have not been configured for web - '
|
|
||||||
'you can reconfigure this by running the FlutterFire CLI again.',
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
switch (defaultTargetPlatform) {
|
switch (defaultTargetPlatform) {
|
||||||
case TargetPlatform.android:
|
case TargetPlatform.android:
|
||||||
@@ -28,15 +25,9 @@ class DefaultFirebaseOptions {
|
|||||||
case TargetPlatform.iOS:
|
case TargetPlatform.iOS:
|
||||||
return ios;
|
return ios;
|
||||||
case TargetPlatform.macOS:
|
case TargetPlatform.macOS:
|
||||||
throw UnsupportedError(
|
return macos;
|
||||||
'DefaultFirebaseOptions have not been configured for macos - '
|
|
||||||
'you can reconfigure this by running the FlutterFire CLI again.',
|
|
||||||
);
|
|
||||||
case TargetPlatform.windows:
|
case TargetPlatform.windows:
|
||||||
throw UnsupportedError(
|
return windows;
|
||||||
'DefaultFirebaseOptions have not been configured for windows - '
|
|
||||||
'you can reconfigure this by running the FlutterFire CLI again.',
|
|
||||||
);
|
|
||||||
case TargetPlatform.linux:
|
case TargetPlatform.linux:
|
||||||
throw UnsupportedError(
|
throw UnsupportedError(
|
||||||
'DefaultFirebaseOptions have not been configured for linux - '
|
'DefaultFirebaseOptions have not been configured for linux - '
|
||||||
@@ -51,7 +42,7 @@ class DefaultFirebaseOptions {
|
|||||||
|
|
||||||
static const FirebaseOptions android = FirebaseOptions(
|
static const FirebaseOptions android = FirebaseOptions(
|
||||||
apiKey: 'AIzaSyCyfwRXTwSTLOFQSQgN5p7QZgGJVZnEKq0',
|
apiKey: 'AIzaSyCyfwRXTwSTLOFQSQgN5p7QZgGJVZnEKq0',
|
||||||
appId: '1:594687661098:android:46557bd4f534b5bb595f53',
|
appId: '1:594687661098:android:b7ce96c17eb928ca595f53',
|
||||||
messagingSenderId: '594687661098',
|
messagingSenderId: '594687661098',
|
||||||
projectId: 'ride-b1bd8',
|
projectId: 'ride-b1bd8',
|
||||||
storageBucket: 'ride-b1bd8.appspot.com',
|
storageBucket: 'ride-b1bd8.appspot.com',
|
||||||
@@ -63,8 +54,40 @@ class DefaultFirebaseOptions {
|
|||||||
messagingSenderId: '594687661098',
|
messagingSenderId: '594687661098',
|
||||||
projectId: 'ride-b1bd8',
|
projectId: 'ride-b1bd8',
|
||||||
storageBucket: 'ride-b1bd8.appspot.com',
|
storageBucket: 'ride-b1bd8.appspot.com',
|
||||||
iosClientId:
|
androidClientId: '594687661098-2dhoogl7be9phobfbu8bbg1sj567iv88.apps.googleusercontent.com',
|
||||||
'594687661098-9fnj82nef9oagl98prigdf8qne3ddbto.apps.googleusercontent.com',
|
iosClientId: '594687661098-9fnj82nef9oagl98prigdf8qne3ddbto.apps.googleusercontent.com',
|
||||||
iosBundleId: 'com.sefer.driver',
|
iosBundleId: 'com.sefer.driver',
|
||||||
);
|
);
|
||||||
}
|
|
||||||
|
static const FirebaseOptions web = FirebaseOptions(
|
||||||
|
apiKey: 'AIzaSyAVtyV7YVMeLbwA1UlNPxV9FhCzT0kjeAE',
|
||||||
|
appId: '1:594687661098:web:62d8388476ec91ec595f53',
|
||||||
|
messagingSenderId: '594687661098',
|
||||||
|
projectId: 'ride-b1bd8',
|
||||||
|
authDomain: 'ride-b1bd8.firebaseapp.com',
|
||||||
|
storageBucket: 'ride-b1bd8.appspot.com',
|
||||||
|
measurementId: 'G-Y3HFEC6F4N',
|
||||||
|
);
|
||||||
|
|
||||||
|
static const FirebaseOptions macos = FirebaseOptions(
|
||||||
|
apiKey: 'AIzaSyCf2mW2h0HD8ZYjwh4VOa2ladw6MJkCDTM',
|
||||||
|
appId: '1:594687661098:ios:6f69eee1449be943595f53',
|
||||||
|
messagingSenderId: '594687661098',
|
||||||
|
projectId: 'ride-b1bd8',
|
||||||
|
storageBucket: 'ride-b1bd8.appspot.com',
|
||||||
|
androidClientId: '594687661098-2dhoogl7be9phobfbu8bbg1sj567iv88.apps.googleusercontent.com',
|
||||||
|
iosClientId: '594687661098-8e26699cris2k3nj5msj1osi59it9kpf.apps.googleusercontent.com',
|
||||||
|
iosBundleId: 'com.mobileapp.store.ride',
|
||||||
|
);
|
||||||
|
|
||||||
|
static const FirebaseOptions windows = FirebaseOptions(
|
||||||
|
apiKey: 'AIzaSyAVtyV7YVMeLbwA1UlNPxV9FhCzT0kjeAE',
|
||||||
|
appId: '1:594687661098:web:d9f43a2091395d87595f53',
|
||||||
|
messagingSenderId: '594687661098',
|
||||||
|
projectId: 'ride-b1bd8',
|
||||||
|
authDomain: 'ride-b1bd8.firebaseapp.com',
|
||||||
|
storageBucket: 'ride-b1bd8.appspot.com',
|
||||||
|
measurementId: 'G-C3DWQ8Z062',
|
||||||
|
);
|
||||||
|
|
||||||
|
}
|
||||||
132
lib/main.dart
132
lib/main.dart
@@ -1,68 +1,125 @@
|
|||||||
|
import 'dart:async';
|
||||||
import 'dart:convert';
|
import 'dart:convert';
|
||||||
import 'dart:io';
|
import 'dart:io';
|
||||||
|
import 'package:SEFER/constant/box_name.dart';
|
||||||
import 'package:SEFER/controller/payment/paymob/paymob_response.dart';
|
import 'package:SEFER/controller/payment/paymob/paymob_response.dart';
|
||||||
|
import 'package:SEFER/views/home/Captin/orderCaptin/order_request_page.dart';
|
||||||
import 'package:firebase_core/firebase_core.dart';
|
import 'package:firebase_core/firebase_core.dart';
|
||||||
import 'package:firebase_messaging/firebase_messaging.dart';
|
import 'package:firebase_messaging/firebase_messaging.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter_overlay_window/flutter_overlay_window.dart';
|
||||||
import 'package:flutter_secure_storage/flutter_secure_storage.dart';
|
import 'package:flutter_secure_storage/flutter_secure_storage.dart';
|
||||||
import 'package:flutter_stripe/flutter_stripe.dart';
|
import 'package:flutter_stripe/flutter_stripe.dart';
|
||||||
import 'package:get/get.dart';
|
import 'package:get/get.dart';
|
||||||
import 'package:get_storage/get_storage.dart';
|
import 'package:get_storage/get_storage.dart';
|
||||||
import 'package:flutter/services.dart';
|
import 'package:flutter/services.dart';
|
||||||
|
import 'package:permission_handler/permission_handler.dart';
|
||||||
import 'package:wakelock_plus/wakelock_plus.dart';
|
import 'package:wakelock_plus/wakelock_plus.dart';
|
||||||
import 'constant/api_key.dart';
|
import 'constant/api_key.dart';
|
||||||
import 'constant/credential.dart';
|
|
||||||
import 'constant/info.dart';
|
import 'constant/info.dart';
|
||||||
import 'controller/firebase/firbase_messge.dart';
|
import 'controller/firebase/firbase_messge.dart';
|
||||||
import 'controller/firebase/local_notification.dart';
|
import 'controller/firebase/local_notification.dart';
|
||||||
import 'controller/functions/location_controller.dart';
|
import 'controller/functions/location_controller.dart';
|
||||||
|
import 'controller/functions/overlay_permisssion.dart';
|
||||||
import 'controller/local/local_controller.dart';
|
import 'controller/local/local_controller.dart';
|
||||||
import 'controller/local/translations.dart';
|
import 'controller/local/translations.dart';
|
||||||
import 'controller/payment/paymob/paymob_wallet.dart';
|
import 'controller/payment/paymob/paymob_wallet.dart';
|
||||||
import 'firebase_options.dart';
|
import 'firebase_options.dart';
|
||||||
import 'models/db_sql.dart';
|
import 'models/db_sql.dart';
|
||||||
|
import 'print.dart';
|
||||||
import 'splash_screen_page.dart';
|
import 'splash_screen_page.dart';
|
||||||
import 'views/home/Captin/orderCaptin/order_request_page.dart';
|
import 'views/home/Captin/orderCaptin/order_over_lay.dart';
|
||||||
|
|
||||||
final box = GetStorage();
|
final box = GetStorage();
|
||||||
const storage = FlutterSecureStorage();
|
const storage = FlutterSecureStorage();
|
||||||
|
|
||||||
final PaymobPayment paymobPayment = PaymobPayment();
|
final PaymobPayment paymobPayment = PaymobPayment();
|
||||||
final PaymobPaymentWallet paymobPaymentWallet = PaymobPaymentWallet();
|
final PaymobPaymentWallet paymobPaymentWallet = PaymobPaymentWallet();
|
||||||
|
|
||||||
DbSql sql = DbSql.instance;
|
DbSql sql = DbSql.instance;
|
||||||
|
|
||||||
@pragma('vm:entry-point')
|
@pragma('vm:entry-point')
|
||||||
Future<void> backgroundMessageHandler(RemoteMessage message) async {
|
Future<void> backgroundMessageHandler(RemoteMessage message) async {
|
||||||
await Firebase.initializeApp();
|
await Firebase.initializeApp();
|
||||||
if (message.data.isNotEmpty && message.notification != null) {
|
if (Platform.isAndroid) {
|
||||||
FirebaseMessagesController().fireBaseTitles(message);
|
if (message.notification != null && message.notification!.title != null) {
|
||||||
|
if (message.notification?.title == 'Order') {
|
||||||
|
var myListString = message.data['DriverList'] ?? '[]';
|
||||||
|
Log.print('myListString: $myListString');
|
||||||
|
|
||||||
|
// Decode the JSON string to a list
|
||||||
|
var myList;
|
||||||
|
try {
|
||||||
|
myList = jsonDecode(myListString) as List<dynamic>;
|
||||||
|
} catch (e) {
|
||||||
|
Log.print('Error decoding JSON: $e');
|
||||||
|
myList = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
Future.delayed(const Duration(seconds: 1));
|
||||||
|
NotificationController().showNotification(
|
||||||
|
message.notification!.title.toString(),
|
||||||
|
message.notification!.body.toString(),
|
||||||
|
'order',
|
||||||
|
myListString,
|
||||||
|
);
|
||||||
|
|
||||||
|
bool isOverlayActive = await FlutterOverlayWindow.isActive();
|
||||||
|
if (isOverlayActive) {
|
||||||
|
await FlutterOverlayWindow.closeOverlay();
|
||||||
|
}
|
||||||
|
await FlutterOverlayWindow.showOverlay(
|
||||||
|
enableDrag: true,
|
||||||
|
flag: OverlayFlag.focusPointer,
|
||||||
|
visibility: NotificationVisibility.visibilityPublic,
|
||||||
|
positionGravity: PositionGravity.auto,
|
||||||
|
height: 700,
|
||||||
|
width: WindowSize.matchParent,
|
||||||
|
startPosition: const OverlayPosition(0, -150),
|
||||||
|
);
|
||||||
|
|
||||||
|
await FlutterOverlayWindow.shareData(myList);
|
||||||
|
|
||||||
|
// Bubble().startBubbleHead(sendAppToBackground: true);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
FirebaseMessagesController().fireBaseTitles(message);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> handleBackgroundNotificationClick(RemoteMessage message) async {
|
final GlobalKey<NavigatorState> navigatorKey = GlobalKey<NavigatorState>();
|
||||||
await Firebase.initializeApp();
|
|
||||||
|
|
||||||
var myListString = message.data['DriverList'];
|
@pragma('vm:entry-point')
|
||||||
var myList = jsonDecode(myListString) as List<dynamic>;
|
void overlayMain() async {
|
||||||
|
WidgetsFlutterBinding.ensureInitialized();
|
||||||
|
|
||||||
Get.to(() => OrderRequestPage(), arguments: {
|
runApp(const MaterialApp(
|
||||||
'myListString': myListString,
|
debugShowCheckedModeBanner: false,
|
||||||
'DriverList': myList,
|
home: OrderOverlay(),
|
||||||
'body': message.notification?.body,
|
));
|
||||||
});
|
}
|
||||||
|
|
||||||
|
void closeOverLay() {
|
||||||
|
FlutterOverlayWindow.closeOverlay();
|
||||||
}
|
}
|
||||||
|
|
||||||
void main() async {
|
void main() async {
|
||||||
WidgetsFlutterBinding.ensureInitialized();
|
WidgetsFlutterBinding.ensureInitialized();
|
||||||
WakelockPlus.enable();
|
WakelockPlus.enable();
|
||||||
await LocationController().startLocationUpdates();
|
// Request location permission first
|
||||||
if (Platform.isAndroid) {
|
PermissionStatus status = await Permission.location.request();
|
||||||
await NotificationController().initNotifications();
|
if (status.isDenied) {
|
||||||
|
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||||
|
getPermissionLocation();
|
||||||
|
});
|
||||||
|
// Handle the case when permission is denied
|
||||||
|
// You might want to show a dialog explaining why the permission is needed
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
// Set up the overlay entry point
|
||||||
|
// FlutterOverlayWindow.overlayMain = overlayMain;
|
||||||
|
await LocationController().startLocationUpdates();
|
||||||
|
|
||||||
await GetStorage.init();
|
await GetStorage.init();
|
||||||
|
|
||||||
Stripe.publishableKey = AK.publishableKeyStripe;
|
Stripe.publishableKey = AK.publishableKeyStripe;
|
||||||
|
|
||||||
if (Platform.isAndroid || Platform.isIOS) {
|
if (Platform.isAndroid || Platform.isIOS) {
|
||||||
@@ -72,7 +129,7 @@ void main() async {
|
|||||||
await FirebaseMessagesController().requestFirebaseMessagingPermission();
|
await FirebaseMessagesController().requestFirebaseMessagingPermission();
|
||||||
|
|
||||||
FirebaseMessaging.onBackgroundMessage(backgroundMessageHandler);
|
FirebaseMessaging.onBackgroundMessage(backgroundMessageHandler);
|
||||||
|
NotificationController().initNotifications();
|
||||||
List<Future> initializationTasks = [
|
List<Future> initializationTasks = [
|
||||||
FirebaseMessagesController().getNotificationSettings(),
|
FirebaseMessagesController().getNotificationSettings(),
|
||||||
FirebaseMessagesController().getToken(),
|
FirebaseMessagesController().getToken(),
|
||||||
@@ -98,24 +155,35 @@ void main() async {
|
|||||||
iFrameID: 837992,
|
iFrameID: 837992,
|
||||||
);
|
);
|
||||||
|
|
||||||
runApp(const MyApp());
|
runApp(MyApp());
|
||||||
}
|
}
|
||||||
|
|
||||||
class MyApp extends StatelessWidget {
|
class MyApp extends StatelessWidget {
|
||||||
const MyApp({super.key});
|
MyApp({super.key});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
LocaleController localController = Get.put(LocaleController());
|
LocaleController localController = Get.put(LocaleController());
|
||||||
// checkForUpdate(context);
|
|
||||||
return GetMaterialApp(
|
return GetMaterialApp(
|
||||||
title: AppInformation.appName,
|
navigatorKey: navigatorKey,
|
||||||
translations: MyTranslation(),
|
title: AppInformation.appName,
|
||||||
debugShowCheckedModeBanner: false,
|
translations: MyTranslation(),
|
||||||
locale: localController.language,
|
debugShowCheckedModeBanner: false,
|
||||||
theme: localController.appTheme,
|
locale: localController.language,
|
||||||
key: UniqueKey(),
|
theme: localController.appTheme,
|
||||||
initialRoute: '/',
|
key: UniqueKey(),
|
||||||
home: SplashScreen());
|
initialRoute: '/',
|
||||||
|
// home: SplashScreen(),
|
||||||
|
routes: {
|
||||||
|
'/order': (context) => OrderRequestPage(),
|
||||||
|
},
|
||||||
|
getPages: [
|
||||||
|
GetPage(name: '/', page: () => SplashScreen()),
|
||||||
|
GetPage(
|
||||||
|
name: '/order-page',
|
||||||
|
page: () => OrderRequestPage(),
|
||||||
|
arguments: box.read(BoxName.rideArguments)),
|
||||||
|
],
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -78,6 +78,47 @@ class DbSql {
|
|||||||
faceDetectTimes INTEGER
|
faceDetectTimes INTEGER
|
||||||
)
|
)
|
||||||
''');
|
''');
|
||||||
|
await db.execute('''
|
||||||
|
CREATE TABLE IF NOT EXISTS ${TableName.applyRideFromOverLay}(
|
||||||
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||||
|
start_location_lat TEXT,
|
||||||
|
start_location_lng TEXT,
|
||||||
|
end_location_lat TEXT,
|
||||||
|
end_location_lng TEXT,
|
||||||
|
total_passenger TEXT,
|
||||||
|
total_driver TEXT,
|
||||||
|
duration_to_ride TEXT,
|
||||||
|
distance TEXT,
|
||||||
|
driver_id TEXT,
|
||||||
|
passenger_id TEXT,
|
||||||
|
passenger_name TEXT,
|
||||||
|
passenger_token_fcm TEXT,
|
||||||
|
passenger_phone TEXT,
|
||||||
|
duration_by_passenger TEXT,
|
||||||
|
distance_by_passenger TEXT,
|
||||||
|
is_wallet_checked TEXT,
|
||||||
|
driver_token TEXT,
|
||||||
|
duration_to_passenger TEXT,
|
||||||
|
ride_id TEXT,
|
||||||
|
ride_timer_begin TEXT,
|
||||||
|
driver_id_duplicate TEXT,
|
||||||
|
duration_to_ride_duplicate TEXT,
|
||||||
|
way_points TEXT,
|
||||||
|
place_coordinate_0 TEXT,
|
||||||
|
place_coordinate_1 TEXT,
|
||||||
|
place_coordinate_2 TEXT,
|
||||||
|
place_coordinate_3 TEXT,
|
||||||
|
place_coordinate_4 TEXT,
|
||||||
|
cost_for_driver TEXT,
|
||||||
|
passenger_wallet_total TEXT,
|
||||||
|
passenger_email TEXT,
|
||||||
|
start_name_address TEXT,
|
||||||
|
end_name_address TEXT,
|
||||||
|
car_type TEXT,
|
||||||
|
kazan TEXT,
|
||||||
|
passenger_rate TEXT
|
||||||
|
)
|
||||||
|
''');
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -25,11 +25,24 @@ class InviteDriverScreen extends StatelessWidget {
|
|||||||
style: const TextStyle(fontSize: 18, fontWeight: FontWeight.bold),
|
style: const TextStyle(fontSize: 18, fontWeight: FontWeight.bold),
|
||||||
),
|
),
|
||||||
const SizedBox(height: 20),
|
const SizedBox(height: 20),
|
||||||
MyTextForm(
|
Row(
|
||||||
controller: controller.invitePhoneController,
|
children: [
|
||||||
label: 'Enter driver\'s phone'.tr,
|
Expanded(
|
||||||
hint: 'Enter driver\'s phone'.tr,
|
child: MyTextForm(
|
||||||
type: TextInputType.phone),
|
controller: controller.invitePhoneController,
|
||||||
|
label: 'Enter driver\'s phone'.tr,
|
||||||
|
hint: 'Enter driver\'s phone'.tr,
|
||||||
|
type: TextInputType.phone,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
IconButton(
|
||||||
|
icon: Icon(Icons.contacts),
|
||||||
|
onPressed: () async {
|
||||||
|
await controller.pickContact();
|
||||||
|
},
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
const SizedBox(height: 20),
|
const SizedBox(height: 20),
|
||||||
Row(
|
Row(
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceAround,
|
mainAxisAlignment: MainAxisAlignment.spaceAround,
|
||||||
@@ -49,7 +62,6 @@ class InviteDriverScreen extends StatelessWidget {
|
|||||||
const SizedBox(height: 20),
|
const SizedBox(height: 20),
|
||||||
GetBuilder<InviteController>(builder: (controller) {
|
GetBuilder<InviteController>(builder: (controller) {
|
||||||
return SizedBox(
|
return SizedBox(
|
||||||
// decoration: AppStyle.boxDecoration,
|
|
||||||
height: Get.height * .4,
|
height: Get.height * .4,
|
||||||
child: controller.driverInvitationData.isEmpty
|
child: controller.driverInvitationData.isEmpty
|
||||||
? Center(
|
? Center(
|
||||||
@@ -61,7 +73,6 @@ class InviteDriverScreen extends StatelessWidget {
|
|||||||
: ListView.builder(
|
: ListView.builder(
|
||||||
itemCount: controller.driverInvitationData.length,
|
itemCount: controller.driverInvitationData.length,
|
||||||
itemBuilder: (context, index) {
|
itemBuilder: (context, index) {
|
||||||
// Ensure the 'countOfInvitDriver' key exists and is parseable as an int
|
|
||||||
int countOfInvitDriver = 0;
|
int countOfInvitDriver = 0;
|
||||||
if (controller.driverInvitationData[index]
|
if (controller.driverInvitationData[index]
|
||||||
.containsKey('countOfInvitDriver')) {
|
.containsKey('countOfInvitDriver')) {
|
||||||
@@ -72,25 +83,24 @@ class InviteDriverScreen extends StatelessWidget {
|
|||||||
0;
|
0;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Calculate the progress value, ensuring it is between 0 and 1
|
|
||||||
double progressValue = countOfInvitDriver / 100.0;
|
double progressValue = countOfInvitDriver / 100.0;
|
||||||
if (progressValue > 1.0) progressValue = 1.0;
|
if (progressValue > 1.0) progressValue = 1.0;
|
||||||
if (progressValue < 0.0) progressValue = 0.0;
|
if (progressValue < 0.0) progressValue = 0.0;
|
||||||
|
|
||||||
return Container(
|
return InkWell(
|
||||||
margin: const EdgeInsets.symmetric(vertical: 8.0),
|
onTap: () async {
|
||||||
child: Stack(
|
controller.onSelectDriverInvitation(index);
|
||||||
alignment: AlignmentDirectional.center,
|
},
|
||||||
children: [
|
child: Container(
|
||||||
InkWell(
|
margin: const EdgeInsets.symmetric(vertical: 8.0),
|
||||||
onTap: () async {
|
child: Stack(
|
||||||
controller.onSelectDriverInvitation(index);
|
alignment: AlignmentDirectional.center,
|
||||||
},
|
children: [
|
||||||
child: Container(
|
Container(
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
borderRadius: BorderRadius.circular(12),
|
borderRadius: BorderRadius.circular(12),
|
||||||
color: AppColor.accentColor.withOpacity(
|
color:
|
||||||
.5), // Background color of the container
|
AppColor.accentColor.withOpacity(.5),
|
||||||
),
|
),
|
||||||
width: Get.width * .85,
|
width: Get.width * .85,
|
||||||
child: ClipRRect(
|
child: ClipRRect(
|
||||||
@@ -104,10 +114,11 @@ class InviteDriverScreen extends StatelessWidget {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
Text(
|
||||||
Text(
|
'${controller.driverInvitationData[index]['invitorName']} ${controller.driverInvitationData[index]['countOfInvitDriver']} / 100 ${'Trip'.tr}',
|
||||||
'${controller.driverInvitationData[index]['invitorName']} ${controller.driverInvitationData[index]['countOfInvitDriver']} / 100 ${'Trip'.tr}'),
|
),
|
||||||
],
|
],
|
||||||
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -5,8 +5,10 @@ import 'package:get/get.dart';
|
|||||||
import 'package:SEFER/controller/home/captin/map_driver_controller.dart';
|
import 'package:SEFER/controller/home/captin/map_driver_controller.dart';
|
||||||
import 'package:SEFER/views/widgets/my_scafold.dart';
|
import 'package:SEFER/views/widgets/my_scafold.dart';
|
||||||
|
|
||||||
|
import '../../../constant/colors.dart';
|
||||||
import '../../../controller/functions/location_controller.dart';
|
import '../../../controller/functions/location_controller.dart';
|
||||||
import '../../Rate/rate_passenger.dart';
|
import '../../Rate/rate_passenger.dart';
|
||||||
|
import '../../widgets/my_textField.dart';
|
||||||
import 'mapDriverWidgets/driver_end_ride_bar.dart';
|
import 'mapDriverWidgets/driver_end_ride_bar.dart';
|
||||||
import 'mapDriverWidgets/google_driver_map_page.dart';
|
import 'mapDriverWidgets/google_driver_map_page.dart';
|
||||||
import 'mapDriverWidgets/google_map_app.dart';
|
import 'mapDriverWidgets/google_map_app.dart';
|
||||||
@@ -36,6 +38,7 @@ class PassengerLocationMapPage extends StatelessWidget {
|
|||||||
body: [
|
body: [
|
||||||
GoogleDriverMap(locationController: locationController),
|
GoogleDriverMap(locationController: locationController),
|
||||||
const PassengerInfoWindow(),
|
const PassengerInfoWindow(),
|
||||||
|
CancelWidget(mapDriverController: mapDriverController),
|
||||||
driverEndRideBar(),
|
driverEndRideBar(),
|
||||||
const SosConnect(),
|
const SosConnect(),
|
||||||
speedCircle(),
|
speedCircle(),
|
||||||
@@ -46,6 +49,70 @@ class PassengerLocationMapPage extends StatelessWidget {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class CancelWidget extends StatelessWidget {
|
||||||
|
const CancelWidget({
|
||||||
|
super.key,
|
||||||
|
required this.mapDriverController,
|
||||||
|
});
|
||||||
|
|
||||||
|
final MapDriverController mapDriverController;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Positioned(
|
||||||
|
top: 10,
|
||||||
|
left: 5,
|
||||||
|
child: GestureDetector(
|
||||||
|
onTap: () {
|
||||||
|
Get.defaultDialog(
|
||||||
|
title: "Are you sure you want to cancel this trip?".tr,
|
||||||
|
titleStyle: AppStyle.title,
|
||||||
|
content: Column(
|
||||||
|
children: [
|
||||||
|
Text("Why do you want to cancel this trip?".tr),
|
||||||
|
Form(
|
||||||
|
key: mapDriverController.formKeyCancel,
|
||||||
|
child: MyTextForm(
|
||||||
|
controller: mapDriverController.cancelTripCotroller,
|
||||||
|
label: "Write the reason for canceling the trip".tr,
|
||||||
|
hint: "Write the reason for canceling the trip".tr,
|
||||||
|
type: TextInputType.name,
|
||||||
|
))
|
||||||
|
],
|
||||||
|
),
|
||||||
|
confirm: MyElevatedButton(
|
||||||
|
title: 'OK'.tr,
|
||||||
|
onPressed: () async {
|
||||||
|
// todo add cancel and inform passenger to get new driver
|
||||||
|
await mapDriverController
|
||||||
|
.cancelTripFromDriverAfterApplied();
|
||||||
|
Get.back();
|
||||||
|
}),
|
||||||
|
cancel: MyElevatedButton(
|
||||||
|
title: 'NO'.tr,
|
||||||
|
kolor: AppColor.redColor,
|
||||||
|
onPressed: () {
|
||||||
|
Get.back();
|
||||||
|
}));
|
||||||
|
},
|
||||||
|
child: Container(
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: AppColor.redColor,
|
||||||
|
borderRadius: BorderRadius.circular(15)),
|
||||||
|
child: const Padding(
|
||||||
|
padding: EdgeInsets.all(3),
|
||||||
|
child: Icon(
|
||||||
|
Icons.clear,
|
||||||
|
size: 40,
|
||||||
|
color: AppColor.secondaryColor,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
class PricesWindow extends StatelessWidget {
|
class PricesWindow extends StatelessWidget {
|
||||||
const PricesWindow({
|
const PricesWindow({
|
||||||
super.key,
|
super.key,
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import 'package:SEFER/constant/box_name.dart';
|
import 'package:SEFER/constant/box_name.dart';
|
||||||
import 'package:SEFER/controller/home/captin/map_driver_controller.dart';
|
import 'package:SEFER/controller/home/captin/map_driver_controller.dart';
|
||||||
|
import 'package:SEFER/views/notification/available_rides_page.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:get/get.dart';
|
import 'package:get/get.dart';
|
||||||
import 'package:google_maps_flutter/google_maps_flutter.dart';
|
import 'package:google_maps_flutter/google_maps_flutter.dart';
|
||||||
@@ -12,6 +13,7 @@ import '../../../../constant/info.dart';
|
|||||||
import '../../../../constant/style.dart';
|
import '../../../../constant/style.dart';
|
||||||
import '../../../../constant/table_names.dart';
|
import '../../../../constant/table_names.dart';
|
||||||
import '../../../../controller/functions/location_controller.dart';
|
import '../../../../controller/functions/location_controller.dart';
|
||||||
|
import '../../../../controller/functions/overlay_permisssion.dart';
|
||||||
import '../../../../controller/functions/package_info.dart';
|
import '../../../../controller/functions/package_info.dart';
|
||||||
import '../../../../controller/home/captin/home_captain_controller.dart';
|
import '../../../../controller/home/captin/home_captain_controller.dart';
|
||||||
import '../../../../controller/home/captin/order_request_controller.dart';
|
import '../../../../controller/home/captin/order_request_controller.dart';
|
||||||
@@ -32,8 +34,10 @@ class HomeCaptain extends StatelessWidget {
|
|||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
Get.put(OrderRequestController());
|
Get.put(OrderRequestController());
|
||||||
Get.put(HomeCaptainController());
|
Get.put(HomeCaptainController());
|
||||||
|
Get.put(CaptainWalletController());
|
||||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||||
checkForUpdate(context);
|
// checkForUpdate(context);
|
||||||
|
getPermissionOverlay();
|
||||||
_showFirstTimeOfferNotification(context);
|
_showFirstTimeOfferNotification(context);
|
||||||
});
|
});
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
@@ -240,6 +244,28 @@ class HomeCaptain extends StatelessWidget {
|
|||||||
// ),
|
// ),
|
||||||
// ),
|
// ),
|
||||||
// ),
|
// ),
|
||||||
|
Positioned(
|
||||||
|
bottom: Get.height * .3,
|
||||||
|
right: 6,
|
||||||
|
child: AnimatedContainer(
|
||||||
|
duration: const Duration(microseconds: 200),
|
||||||
|
width: homeCaptainController.widthMapTypeAndTraffic,
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
border: Border.all(color: AppColor.blueColor),
|
||||||
|
color: AppColor.secondaryColor,
|
||||||
|
borderRadius: BorderRadius.circular(15)),
|
||||||
|
child: IconButton(
|
||||||
|
onPressed: () {
|
||||||
|
Get.to(() => AvailableRidesPage());
|
||||||
|
},
|
||||||
|
icon: const Icon(
|
||||||
|
Icons.train_sharp,
|
||||||
|
size: 29,
|
||||||
|
color: AppColor.blueColor,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
leftMainMenuCaptainIcons(),
|
leftMainMenuCaptainIcons(),
|
||||||
box.read(BoxName.rideStatus) == 'Applied' ||
|
box.read(BoxName.rideStatus) == 'Applied' ||
|
||||||
box.read(BoxName.rideStatus) == 'Begin'
|
box.read(BoxName.rideStatus) == 'Begin'
|
||||||
@@ -401,8 +427,8 @@ void _showFirstTimeOfferNotification(BuildContext context) {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
Navigator.of(context).pop();
|
|
||||||
_markAsNotFirstTime();
|
_markAsNotFirstTime();
|
||||||
|
Navigator.of(context).pop();
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
@@ -416,7 +442,7 @@ void _showFirstTimeOfferNotification(BuildContext context) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool _checkIfFirstTime() {
|
bool _checkIfFirstTime() {
|
||||||
if (box.read(BoxName.isFirstTime) == null) {
|
if (box.read(BoxName.isFirstTime).toString() == '') {
|
||||||
return true;
|
return true;
|
||||||
} else {
|
} else {
|
||||||
return false;
|
return false;
|
||||||
@@ -424,5 +450,5 @@ bool _checkIfFirstTime() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void _markAsNotFirstTime() {
|
void _markAsNotFirstTime() {
|
||||||
box.write(BoxName.isFirstTime, false);
|
box.write(BoxName.isFirstTime, 'false');
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,14 +2,15 @@ import 'package:SEFER/constant/box_name.dart';
|
|||||||
import 'package:SEFER/main.dart';
|
import 'package:SEFER/main.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_font_icons/flutter_font_icons.dart';
|
import 'package:flutter_font_icons/flutter_font_icons.dart';
|
||||||
|
import 'package:flutter_overlay_window/flutter_overlay_window.dart';
|
||||||
import 'package:get/get.dart';
|
import 'package:get/get.dart';
|
||||||
import 'package:SEFER/controller/home/captin/home_captain_controller.dart';
|
import 'package:SEFER/controller/home/captin/home_captain_controller.dart';
|
||||||
import 'package:google_maps_flutter/google_maps_flutter.dart';
|
import 'package:google_maps_flutter/google_maps_flutter.dart';
|
||||||
|
|
||||||
import '../../../../../constant/colors.dart';
|
import '../../../../../constant/colors.dart';
|
||||||
|
import '../../../../../print.dart';
|
||||||
import '../../../../Rate/ride_calculate_driver.dart';
|
import '../../../../Rate/ride_calculate_driver.dart';
|
||||||
import '../../../../../controller/functions/location_controller.dart';
|
import '../../../../../controller/functions/location_controller.dart';
|
||||||
import '../../../../auth/captin/cards/egypt_card_a_i.dart';
|
|
||||||
|
|
||||||
GetBuilder<HomeCaptainController> leftMainMenuCaptainIcons() {
|
GetBuilder<HomeCaptainController> leftMainMenuCaptainIcons() {
|
||||||
return GetBuilder<HomeCaptainController>(
|
return GetBuilder<HomeCaptainController>(
|
||||||
@@ -168,16 +169,125 @@ GetBuilder<HomeCaptainController> leftMainMenuCaptainIcons() {
|
|||||||
// color: AppColor.secondaryColor,
|
// color: AppColor.secondaryColor,
|
||||||
// border: Border.all(color: AppColor.blueColor),
|
// border: Border.all(color: AppColor.blueColor),
|
||||||
// borderRadius: BorderRadius.circular(15)),
|
// borderRadius: BorderRadius.circular(15)),
|
||||||
// child: IconButton(
|
// child: Builder(builder: (context) {
|
||||||
// onPressed: () async {
|
// return IconButton(
|
||||||
// Get.to(() => EgyptCardAI());
|
// onPressed: () async {
|
||||||
// },
|
// // Get.to(() => EgyptCardAI());
|
||||||
// icon: const Icon(
|
// // print(box.read(BoxName.myList));
|
||||||
// FontAwesome5.grin_tears,
|
//
|
||||||
// size: 29,
|
// List<String> d = [
|
||||||
// color: AppColor.blueColor,
|
// "30.003028,31.2419628",
|
||||||
// ),
|
// "30.0955661,31.2665336",
|
||||||
// ),
|
// "160.00",
|
||||||
|
// "25.92",
|
||||||
|
// "1488",
|
||||||
|
// "16.93",
|
||||||
|
// "114243034311436865474",
|
||||||
|
// "113172279072358305645",
|
||||||
|
// "hamza ayed",
|
||||||
|
// "c9kqjnLqu08yogitln6B1Y:APA91bHyFJ7E7zv6-HIikwr6FrlMbi4Hc8L1STMPE99iPKqK4Gddwv8r9qZOCadsz9qTEJZ6KLEE9ruTJI6N8dKfK4CXez5pme5WIs14-1QGo29s07fQOniZgIlJV5XFL3yqzPRSUmn3",
|
||||||
|
// "+201023248456",
|
||||||
|
// "1 min",
|
||||||
|
// "1 m",
|
||||||
|
// "false",
|
||||||
|
// "em3j-v3PQlecGsTKFNU1wc:APA91bFjHq8xHpzeQwUMoyUtZ0J3oR6yXKUavrB_gBl9npUZe-qZtax-Raq4QBbdKv0AmtLKm0BfBd6N_592HBv4CVa41ii4122W3hr-BCUKKzJhzZcK8m0YjbWbtpvgJRD8uD_nuMk9",
|
||||||
|
// "0",
|
||||||
|
// "238",
|
||||||
|
// "false",
|
||||||
|
// "114243034311436865474",
|
||||||
|
// "1488",
|
||||||
|
// "startEnd",
|
||||||
|
// "30.049307749732176,31.274291574954987",
|
||||||
|
// "",
|
||||||
|
// "",
|
||||||
|
// "",
|
||||||
|
// "",
|
||||||
|
// "17.73",
|
||||||
|
// "0",
|
||||||
|
// "hamzaayedflutter@gmail.com",
|
||||||
|
// "الفسطاط، حي مصر القديمة، مصر",
|
||||||
|
// " الزاوية الحمراء، محافظة القاهرة، مصر",
|
||||||
|
// "Speed",
|
||||||
|
// "8",
|
||||||
|
// "5.00"
|
||||||
|
// ];
|
||||||
|
//
|
||||||
|
// try {
|
||||||
|
// print('Before showing overlay: ${box.read('some_key')}');
|
||||||
|
//
|
||||||
|
// // Ensure any existing overlay is closed before showing a new one
|
||||||
|
//
|
||||||
|
// bool isOverlayActive =
|
||||||
|
// await FlutterOverlayWindow.isActive();
|
||||||
|
// if (isOverlayActive) {
|
||||||
|
// await FlutterOverlayWindow.closeOverlay();
|
||||||
|
// }
|
||||||
|
// await FlutterOverlayWindow.showOverlay(
|
||||||
|
// enableDrag: true,
|
||||||
|
// overlayTitle: d[0],
|
||||||
|
// overlayContent: d[1],
|
||||||
|
// flag: OverlayFlag.focusPointer,
|
||||||
|
// visibility: NotificationVisibility.visibilityPublic,
|
||||||
|
// positionGravity: PositionGravity.auto,
|
||||||
|
// height: 700,
|
||||||
|
// width: WindowSize.matchParent,
|
||||||
|
// startPosition: const OverlayPosition(0, -170),
|
||||||
|
// );
|
||||||
|
//
|
||||||
|
// await FlutterOverlayWindow.shareData(d);
|
||||||
|
//
|
||||||
|
// print('After showing overlay: ${box.read('some_key')}');
|
||||||
|
// } catch (e) {
|
||||||
|
// print('Error showing overlay: $e');
|
||||||
|
// }
|
||||||
|
// // final Bubble _bubble = Bubble(showCloseButton: true);
|
||||||
|
// // try {
|
||||||
|
// // await _bubble.startBubbleHead(sendAppToBackground: false);
|
||||||
|
// // } on PlatformException {
|
||||||
|
// // print('Failed to call startBubbleHead');
|
||||||
|
// // }
|
||||||
|
//
|
||||||
|
// // Bubble().startBubbleHead(sendAppToBackground: true);
|
||||||
|
// // }
|
||||||
|
//
|
||||||
|
// // Future<void> stopBubbleHead() async {
|
||||||
|
// // try {
|
||||||
|
// // await _bubble.stopBubbleHead();
|
||||||
|
// // } on PlatformException {
|
||||||
|
// // print('Failed to call stopBubbleHead');
|
||||||
|
// // }
|
||||||
|
// // }
|
||||||
|
//
|
||||||
|
// // // send data to ovelay
|
||||||
|
// },
|
||||||
|
// icon: const Icon(
|
||||||
|
// FontAwesome5.grin_tears,
|
||||||
|
// size: 29,
|
||||||
|
// color: AppColor.blueColor,
|
||||||
|
// ),
|
||||||
|
// );
|
||||||
|
// }),
|
||||||
|
// ),
|
||||||
|
// AnimatedContainer(
|
||||||
|
// duration: const Duration(microseconds: 200),
|
||||||
|
// width: controller.widthMapTypeAndTraffic,
|
||||||
|
// decoration: BoxDecoration(
|
||||||
|
// color: AppColor.secondaryColor,
|
||||||
|
// border: Border.all(color: AppColor.blueColor),
|
||||||
|
// borderRadius: BorderRadius.circular(15)),
|
||||||
|
// child: Builder(builder: (context) {
|
||||||
|
// return IconButton(
|
||||||
|
// onPressed: () async {
|
||||||
|
// // Log.print('box: ${box.read(BoxName.rideStatus)}');
|
||||||
|
// Log.print('box: ${box.read(BoxName.tokenDriver)}');
|
||||||
|
// },
|
||||||
|
// icon: const Icon(
|
||||||
|
// FontAwesome5.closed_captioning,
|
||||||
|
// size: 29,
|
||||||
|
// color: AppColor.blueColor,
|
||||||
|
// ),
|
||||||
|
// );
|
||||||
|
// }),
|
||||||
// ),
|
// ),
|
||||||
],
|
],
|
||||||
)),
|
)),
|
||||||
|
|||||||
@@ -9,10 +9,8 @@ import 'package:SEFER/controller/firebase/firbase_messge.dart';
|
|||||||
import 'package:SEFER/controller/home/captin/map_driver_controller.dart';
|
import 'package:SEFER/controller/home/captin/map_driver_controller.dart';
|
||||||
import 'package:SEFER/views/widgets/elevated_btn.dart';
|
import 'package:SEFER/views/widgets/elevated_btn.dart';
|
||||||
|
|
||||||
import '../../../../constant/box_name.dart';
|
|
||||||
import '../../../../constant/style.dart';
|
import '../../../../constant/style.dart';
|
||||||
import '../../../../controller/functions/launch.dart';
|
import '../../../../controller/functions/launch.dart';
|
||||||
import '../../../../main.dart';
|
|
||||||
|
|
||||||
class PassengerInfoWindow extends StatelessWidget {
|
class PassengerInfoWindow extends StatelessWidget {
|
||||||
const PassengerInfoWindow({
|
const PassengerInfoWindow({
|
||||||
@@ -57,13 +55,6 @@ class PassengerInfoWindow extends StatelessWidget {
|
|||||||
mainAxisAlignment:
|
mainAxisAlignment:
|
||||||
MainAxisAlignment.spaceAround,
|
MainAxisAlignment.spaceAround,
|
||||||
children: [
|
children: [
|
||||||
IconButton(
|
|
||||||
onPressed: () {
|
|
||||||
print(box
|
|
||||||
.read(BoxName.rideStatus));
|
|
||||||
},
|
|
||||||
icon: const Icon(Icons.add),
|
|
||||||
),
|
|
||||||
GestureDetector(
|
GestureDetector(
|
||||||
onTap: () async {
|
onTap: () async {
|
||||||
controller.isSocialPressed =
|
controller.isSocialPressed =
|
||||||
|
|||||||
314
lib/views/home/Captin/orderCaptin/order_over_lay.dart
Normal file
314
lib/views/home/Captin/orderCaptin/order_over_lay.dart
Normal file
@@ -0,0 +1,314 @@
|
|||||||
|
import 'dart:async';
|
||||||
|
import 'package:SEFER/constant/colors.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter_overlay_window/flutter_overlay_window.dart';
|
||||||
|
import 'package:get/get.dart';
|
||||||
|
import 'package:just_audio/just_audio.dart';
|
||||||
|
import '../../../../constant/box_name.dart';
|
||||||
|
import '../../../../constant/style.dart';
|
||||||
|
import '../../../../main.dart';
|
||||||
|
|
||||||
|
class OrderOverlay extends StatefulWidget {
|
||||||
|
const OrderOverlay({Key? key}) : super(key: key);
|
||||||
|
|
||||||
|
@override
|
||||||
|
State<OrderOverlay> createState() => _OrderOverlayState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _OrderOverlayState extends State<OrderOverlay>
|
||||||
|
with WidgetsBindingObserver {
|
||||||
|
List d = [];
|
||||||
|
Timer? _timer;
|
||||||
|
double _progress = 1.0;
|
||||||
|
bool _isOverlayActive = false;
|
||||||
|
final AudioPlayer _audioPlayer = AudioPlayer();
|
||||||
|
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
super.initState();
|
||||||
|
WidgetsBinding.instance.addObserver(this);
|
||||||
|
_setupOverlayListener();
|
||||||
|
}
|
||||||
|
|
||||||
|
void _setupOverlayListener() {
|
||||||
|
FlutterOverlayWindow.overlayListener.listen((event) {
|
||||||
|
if (mounted) {
|
||||||
|
setState(() {
|
||||||
|
d = event;
|
||||||
|
_resetAndStartTimer();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void didChangeAppLifecycleState(AppLifecycleState state) {
|
||||||
|
if (state == AppLifecycleState.resumed) {
|
||||||
|
_checkOverlayStatus();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void _checkOverlayStatus() async {
|
||||||
|
bool isActive = await FlutterOverlayWindow.isActive();
|
||||||
|
if (isActive && mounted) {
|
||||||
|
_resetAndStartTimer();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void _resetAndStartTimer() {
|
||||||
|
_timer?.cancel();
|
||||||
|
setState(() {
|
||||||
|
_progress = 1.0;
|
||||||
|
_isOverlayActive = true;
|
||||||
|
});
|
||||||
|
_playAudio();
|
||||||
|
_startTimer();
|
||||||
|
}
|
||||||
|
|
||||||
|
void _startTimer() {
|
||||||
|
_timer = Timer.periodic(const Duration(milliseconds: 100), (timer) {
|
||||||
|
if (!_isOverlayActive) {
|
||||||
|
timer.cancel();
|
||||||
|
_stopAudio();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (mounted) {
|
||||||
|
setState(() {
|
||||||
|
_progress -=
|
||||||
|
1 / 100; // Decrease progress over 15 seconds (150 * 100ms)
|
||||||
|
if (_progress <= 0) {
|
||||||
|
timer.cancel();
|
||||||
|
_rejectOrder();
|
||||||
|
_stopAudio();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void _playAudio() async {
|
||||||
|
try {
|
||||||
|
await _audioPlayer.setAsset(
|
||||||
|
'assets/order.mp3',
|
||||||
|
preload: true,
|
||||||
|
initialPosition: Duration.zero,
|
||||||
|
);
|
||||||
|
await _audioPlayer.play();
|
||||||
|
} catch (e) {
|
||||||
|
print('An error occurred while playing the audio: $e');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void _stopAudio() {
|
||||||
|
_audioPlayer.stop();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void dispose() {
|
||||||
|
_timer?.cancel();
|
||||||
|
_stopAudio();
|
||||||
|
WidgetsBinding.instance.removeObserver(this);
|
||||||
|
super.dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
String duration = (double.parse(d[4].toString()) / 60).toStringAsFixed(0);
|
||||||
|
String price = d[2].toString().split('.')[0];
|
||||||
|
return Material(
|
||||||
|
color: Colors.transparent,
|
||||||
|
child: Center(
|
||||||
|
child: Container(
|
||||||
|
padding: const EdgeInsets.all(12.0),
|
||||||
|
width: double.infinity,
|
||||||
|
height: 450, // Adjust height as needed
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
gradient: const LinearGradient(colors: [
|
||||||
|
AppColor.blueColor,
|
||||||
|
AppColor.blueColor,
|
||||||
|
]),
|
||||||
|
borderRadius: BorderRadius.circular(12.0),
|
||||||
|
),
|
||||||
|
child: GestureDetector(
|
||||||
|
onTap: () async {
|
||||||
|
bool isOverlayActive = await FlutterOverlayWindow.isActive();
|
||||||
|
if (isOverlayActive) {
|
||||||
|
await FlutterOverlayWindow.closeOverlay();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
child: ListView(
|
||||||
|
// mainAxisAlignment: MainAxisAlignment.spaceEvenly,
|
||||||
|
children: [
|
||||||
|
ListTile(
|
||||||
|
leading: _buildPriceAvatar(price),
|
||||||
|
title: Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
|
children: [
|
||||||
|
Text(
|
||||||
|
d.isNotEmpty ? d[8] : '', // Customer name
|
||||||
|
style: const TextStyle(
|
||||||
|
fontSize: 16,
|
||||||
|
fontWeight: FontWeight.bold,
|
||||||
|
color: AppColor.secondaryColor,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Container(
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
border: Border.all(
|
||||||
|
color: AppColor.redColor, width: 2)),
|
||||||
|
child: Padding(
|
||||||
|
padding: const EdgeInsets.all(3),
|
||||||
|
child: Text(
|
||||||
|
"${d[5]} KM",
|
||||||
|
style: AppStyle.number.copyWith(
|
||||||
|
color: AppColor.secondaryColor, fontSize: 18),
|
||||||
|
),
|
||||||
|
)),
|
||||||
|
const Text('🛣️')
|
||||||
|
],
|
||||||
|
),
|
||||||
|
// subtitle: Text(d.isNotEmpty ? d[10] : ''), // Phone number
|
||||||
|
),
|
||||||
|
Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
_buildDetailRow("🟢", d.isNotEmpty ? d[29] : ''),
|
||||||
|
_buildDetailRow("🔴".tr, d.isNotEmpty ? d[30] : ''),
|
||||||
|
_buildDetailRow(
|
||||||
|
"المسافة للراكب", d.isNotEmpty ? d[12] : ''),
|
||||||
|
Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
|
children: [
|
||||||
|
_buildDetailRow("مدة الرحلة".tr, '$duration '),
|
||||||
|
_buildDetailRow("نوع الطلب".tr, _getRideType(d[31]))
|
||||||
|
],
|
||||||
|
),
|
||||||
|
const SizedBox(
|
||||||
|
height: 30,
|
||||||
|
),
|
||||||
|
const SizedBox(height: 8),
|
||||||
|
LinearProgressIndicator(
|
||||||
|
value: _progress,
|
||||||
|
minHeight: 15,
|
||||||
|
backgroundColor: Colors.white.withOpacity(0.3),
|
||||||
|
valueColor:
|
||||||
|
const AlwaysStoppedAnimation<Color>(Colors.white),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _buildDetailRow(String label, String value) {
|
||||||
|
return Padding(
|
||||||
|
padding: const EdgeInsets.symmetric(vertical: 2.0),
|
||||||
|
child: Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.end,
|
||||||
|
children: [
|
||||||
|
Text(
|
||||||
|
value,
|
||||||
|
style: AppStyle.title.copyWith(color: AppColor.secondaryColor),
|
||||||
|
),
|
||||||
|
Text(
|
||||||
|
label,
|
||||||
|
style: const TextStyle(
|
||||||
|
fontWeight: FontWeight.bold, color: AppColor.secondaryColor),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _buildPriceAvatar(String price) {
|
||||||
|
return Container(
|
||||||
|
width: 80,
|
||||||
|
height: 80,
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
shape: BoxShape.circle,
|
||||||
|
gradient: const RadialGradient(
|
||||||
|
colors: [Color(0xFF4CAF50), Color(0xFF2E7D32)],
|
||||||
|
center: Alignment.center,
|
||||||
|
radius: 0.8,
|
||||||
|
),
|
||||||
|
boxShadow: [
|
||||||
|
BoxShadow(
|
||||||
|
color: Colors.black.withOpacity(0.2),
|
||||||
|
blurRadius: 8,
|
||||||
|
offset: const Offset(0, 4),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
child: Center(
|
||||||
|
child: Text(
|
||||||
|
'\$$price',
|
||||||
|
style: const TextStyle(
|
||||||
|
fontSize: 22,
|
||||||
|
fontWeight: FontWeight.bold,
|
||||||
|
color: Colors.white,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _buildInfoRow(IconData icon, String label, String value) {
|
||||||
|
return Padding(
|
||||||
|
padding: const EdgeInsets.symmetric(vertical: 8.0),
|
||||||
|
child: Row(
|
||||||
|
children: [
|
||||||
|
Icon(icon, color: Colors.white.withOpacity(0.8), size: 24),
|
||||||
|
const SizedBox(width: 12),
|
||||||
|
Expanded(
|
||||||
|
child: Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
Text(
|
||||||
|
label,
|
||||||
|
style: TextStyle(
|
||||||
|
color: Colors.white.withOpacity(0.8), fontSize: 14),
|
||||||
|
),
|
||||||
|
Text(
|
||||||
|
value,
|
||||||
|
style: const TextStyle(
|
||||||
|
color: Colors.white,
|
||||||
|
fontSize: 16,
|
||||||
|
fontWeight: FontWeight.bold),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
String _getRideType(String type) {
|
||||||
|
switch (type) {
|
||||||
|
case 'Comfort':
|
||||||
|
return 'كمفورت ❄️';
|
||||||
|
case 'Lady':
|
||||||
|
return 'ليدي 👩';
|
||||||
|
case 'Speed':
|
||||||
|
return 'سبيد 🔻';
|
||||||
|
case 'Mashwari':
|
||||||
|
return 'مشواري';
|
||||||
|
case 'Rayeh Gai':
|
||||||
|
return 'رايح جاي';
|
||||||
|
default:
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void _rejectOrder() async {
|
||||||
|
box.write(BoxName.rideStatus, 'reject');
|
||||||
|
bool isOverlayActive = await FlutterOverlayWindow.isActive();
|
||||||
|
if (isOverlayActive) {
|
||||||
|
await FlutterOverlayWindow.closeOverlay();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,3 +1,5 @@
|
|||||||
|
import 'dart:convert';
|
||||||
|
|
||||||
import 'package:SEFER/controller/home/captin/home_captain_controller.dart';
|
import 'package:SEFER/controller/home/captin/home_captain_controller.dart';
|
||||||
import 'package:SEFER/views/widgets/mydialoug.dart';
|
import 'package:SEFER/views/widgets/mydialoug.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
@@ -23,23 +25,36 @@ class OrderRequestPage extends StatelessWidget {
|
|||||||
Get.put(OrderRequestController());
|
Get.put(OrderRequestController());
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
|
//TODO show order from start page from sql or api
|
||||||
|
|
||||||
final arguments = Get.arguments;
|
final arguments = Get.arguments;
|
||||||
final myListString = arguments['myListString'];
|
final myListString = arguments['myListString'];
|
||||||
final myList = arguments['DriverList'];
|
var myList;
|
||||||
|
|
||||||
|
// Check if 'DriverList' is null or empty
|
||||||
|
if (arguments['DriverList'] == null || arguments['DriverList'].isEmpty) {
|
||||||
|
myList = jsonDecode(myListString);
|
||||||
|
} else {
|
||||||
|
myList = arguments['DriverList'];
|
||||||
|
}
|
||||||
|
|
||||||
// final pointsList = arguments['PolylineJson'];
|
// final pointsList = arguments['PolylineJson'];
|
||||||
final body = arguments['body'];
|
// final body = arguments['body'];
|
||||||
Duration durationToAdd = Duration(seconds: int.parse(myList[4]));
|
Duration durationToAdd = Duration(seconds: int.parse(myList[4]));
|
||||||
int hours = durationToAdd.inHours;
|
int hours = durationToAdd.inHours;
|
||||||
int minutes = (durationToAdd.inMinutes % 60).round();
|
int minutes = (durationToAdd.inMinutes % 60).round();
|
||||||
orderRequestController.startTimer(myList[6].toString(), body.toString());
|
orderRequestController.startTimer(
|
||||||
var coords = myList[0].split(',');
|
myList[6].toString(),
|
||||||
var coordDestination = myList[1].split(',');
|
myList[16].toString(),
|
||||||
|
);
|
||||||
|
var cords = myList[0].split(',');
|
||||||
|
var cordDestination = myList[1].split(',');
|
||||||
|
|
||||||
// Parse to double
|
// Parse to double
|
||||||
double latPassengerLocation = double.parse(coords[0]);
|
double latPassengerLocation = double.parse(cords[0]);
|
||||||
double lngPassengerLocation = double.parse(coords[1]);
|
double lngPassengerLocation = double.parse(cords[1]);
|
||||||
double latPassengerDestination = double.parse(coordDestination[0]);
|
double latPassengerDestination = double.parse(cordDestination[0]);
|
||||||
double lngPassengerDestination = double.parse(coordDestination[1]);
|
double lngPassengerDestination = double.parse(cordDestination[1]);
|
||||||
|
|
||||||
List<LatLng> pointsDirection = [
|
List<LatLng> pointsDirection = [
|
||||||
LatLng(latPassengerLocation, lngPassengerLocation),
|
LatLng(latPassengerLocation, lngPassengerLocation),
|
||||||
@@ -71,378 +86,422 @@ class OrderRequestPage extends StatelessWidget {
|
|||||||
body: [
|
body: [
|
||||||
Padding(
|
Padding(
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 6),
|
padding: const EdgeInsets.symmetric(horizontal: 6),
|
||||||
child: ListView(
|
child: Container(
|
||||||
// crossAxisAlignment: CrossAxisAlignment.start,
|
color: const Color.fromARGB(255, 210, 201, 201),
|
||||||
children: [
|
child: ListView(
|
||||||
// SizedBox(height: 200, child: Text(pointsList.toString())),
|
// crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
// Text(message.notification!.body.toString()),
|
children: [
|
||||||
SizedBox(
|
// SizedBox(height: 200, child: Text(pointsList.toString())),
|
||||||
height: Get.height * .33,
|
// Text(message.notification!.body.toString()),
|
||||||
child: GoogleMap(
|
SizedBox(
|
||||||
initialCameraPosition: CameraPosition(
|
height: Get.height * .33,
|
||||||
zoom: 12,
|
child: GoogleMap(
|
||||||
target: Get.find<HomeCaptainController>().myLocation),
|
initialCameraPosition: CameraPosition(
|
||||||
cameraTargetBounds: CameraTargetBounds(bounds),
|
zoom: 12,
|
||||||
myLocationButtonEnabled: true,
|
target: Get.find<HomeCaptainController>().myLocation),
|
||||||
trafficEnabled: true,
|
cameraTargetBounds: CameraTargetBounds(bounds),
|
||||||
buildingsEnabled: true,
|
myLocationButtonEnabled: true,
|
||||||
mapToolbarEnabled: true,
|
trafficEnabled: true,
|
||||||
myLocationEnabled: true,
|
buildingsEnabled: true,
|
||||||
markers: {
|
mapToolbarEnabled: true,
|
||||||
Marker(
|
myLocationEnabled: true,
|
||||||
markerId: MarkerId('MyLocation'.tr),
|
markers: {
|
||||||
position: LatLng(
|
Marker(
|
||||||
latPassengerLocation, lngPassengerLocation),
|
markerId: MarkerId('MyLocation'.tr),
|
||||||
draggable: true,
|
position: LatLng(
|
||||||
icon: orderRequestController.startIcon),
|
latPassengerLocation, lngPassengerLocation),
|
||||||
Marker(
|
draggable: true,
|
||||||
markerId: MarkerId('Destination'.tr),
|
icon: orderRequestController.startIcon),
|
||||||
position: LatLng(
|
Marker(
|
||||||
latPassengerDestination, lngPassengerDestination),
|
markerId: MarkerId('Destination'.tr),
|
||||||
draggable: true,
|
position: LatLng(latPassengerDestination,
|
||||||
icon: orderRequestController.endIcon),
|
lngPassengerDestination),
|
||||||
},
|
draggable: true,
|
||||||
polylines: {
|
icon: orderRequestController.endIcon),
|
||||||
Polyline(
|
},
|
||||||
zIndex: 1,
|
polylines: {
|
||||||
consumeTapEvents: true,
|
Polyline(
|
||||||
geodesic: true,
|
zIndex: 1,
|
||||||
endCap: Cap.buttCap,
|
consumeTapEvents: true,
|
||||||
startCap: Cap.buttCap,
|
geodesic: true,
|
||||||
visible: true,
|
endCap: Cap.buttCap,
|
||||||
polylineId: const PolylineId('routeOrder'),
|
startCap: Cap.buttCap,
|
||||||
points: pointsDirection,
|
visible: true,
|
||||||
color: AppColor.primaryColor,
|
polylineId: const PolylineId('routeOrder'),
|
||||||
width: 2,
|
points: pointsDirection,
|
||||||
),
|
color: AppColor.primaryColor,
|
||||||
},
|
width: 2,
|
||||||
),
|
),
|
||||||
),
|
},
|
||||||
Padding(
|
|
||||||
padding: const EdgeInsets.all(8.0),
|
|
||||||
child: Card(
|
|
||||||
elevation: 3,
|
|
||||||
color: myList[20].toString() == 'haveSteps'
|
|
||||||
? AppColor.greenColor
|
|
||||||
: AppColor.secondaryColor,
|
|
||||||
child: Row(
|
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
|
||||||
children: [
|
|
||||||
TextButton.icon(
|
|
||||||
onPressed: () {
|
|
||||||
String mapUrl =
|
|
||||||
'https://www.google.com/maps/dir/${myList[0]}/${myList[1]}/';
|
|
||||||
showInBrowser(mapUrl);
|
|
||||||
},
|
|
||||||
icon: const Icon(Icons.map),
|
|
||||||
label: myList[20].toString() == 'haveSteps'
|
|
||||||
? Text(
|
|
||||||
'Trip has Steps'.tr,
|
|
||||||
style: AppStyle.title,
|
|
||||||
)
|
|
||||||
: Text('Routs of Trip'.tr,
|
|
||||||
style: AppStyle.title)),
|
|
||||||
Container(
|
|
||||||
color: myList[13].toString() == 'true'
|
|
||||||
? AppColor.deepPurpleAccent
|
|
||||||
: AppColor.greenColor,
|
|
||||||
child: myList[13].toString() ==
|
|
||||||
'true' //Visa or Cash Method from notify to driver
|
|
||||||
? Text(
|
|
||||||
'Visa',
|
|
||||||
style: AppStyle.title,
|
|
||||||
)
|
|
||||||
: Text('Cash', style: AppStyle.title),
|
|
||||||
)
|
|
||||||
],
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
Padding(
|
||||||
|
padding: const EdgeInsets.all(8.0),
|
||||||
Container(
|
child: Card(
|
||||||
decoration: AppStyle.boxDecoration1,
|
elevation: 3,
|
||||||
child: Padding(
|
color: myList[20].toString() == 'haveSteps'
|
||||||
padding: const EdgeInsets.all(8.0),
|
? AppColor.greenColor
|
||||||
|
: AppColor.secondaryColor,
|
||||||
child: Row(
|
child: Row(
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceAround,
|
mainAxisAlignment: MainAxisAlignment.spaceAround,
|
||||||
children: [
|
children: [
|
||||||
Text(
|
TextButton.icon(
|
||||||
double.parse(myList[2]).toStringAsFixed(2),
|
onPressed: () {
|
||||||
style: AppStyle.headTitle2,
|
String mapUrl =
|
||||||
),
|
'https://www.google.com/maps/dir/${myList[0]}/${myList[1]}/';
|
||||||
AnimatedContainer(
|
showInBrowser(mapUrl);
|
||||||
duration: const Duration(seconds: 5),
|
},
|
||||||
curve: Curves.easeInOut,
|
icon: const Icon(Icons.map),
|
||||||
child: AnimatedSize(
|
label: myList[20].toString() == 'haveSteps'
|
||||||
|
? Text(
|
||||||
|
'Trip has Steps'.tr,
|
||||||
|
style: AppStyle.title,
|
||||||
|
)
|
||||||
|
: Text('Payment Method'.tr,
|
||||||
|
style: AppStyle.title)),
|
||||||
|
Container(
|
||||||
|
decoration: AppStyle.boxDecoration.copyWith(
|
||||||
|
color: myList[13].toString() == 'true'
|
||||||
|
? AppColor.deepPurpleAccent
|
||||||
|
: AppColor.greenColor,
|
||||||
|
),
|
||||||
|
child: myList[13].toString() ==
|
||||||
|
'true' //Visa or Cash Method from notify to driver
|
||||||
|
? Text(
|
||||||
|
'Visa',
|
||||||
|
style: AppStyle.title,
|
||||||
|
)
|
||||||
|
: Text('Cash', style: AppStyle.title),
|
||||||
|
)
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
|
||||||
|
Container(
|
||||||
|
decoration: AppStyle.boxDecoration1,
|
||||||
|
child: Padding(
|
||||||
|
padding: const EdgeInsets.all(8.0),
|
||||||
|
child: Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.spaceAround,
|
||||||
|
children: [
|
||||||
|
Text(
|
||||||
|
double.parse(myList[2]).toStringAsFixed(2),
|
||||||
|
style: AppStyle.headTitle2,
|
||||||
|
),
|
||||||
|
AnimatedContainer(
|
||||||
duration: const Duration(seconds: 5),
|
duration: const Duration(seconds: 5),
|
||||||
curve: Curves.easeInOut,
|
curve: Curves.easeInOut,
|
||||||
child: myList[31].toString() == 'Comfort'
|
child: AnimatedSize(
|
||||||
? const Icon(
|
duration: const Duration(seconds: 5),
|
||||||
Icons.ac_unit,
|
curve: Curves.easeInOut,
|
||||||
color: AppColor.blueColor,
|
child: myList[31].toString() == 'Comfort'
|
||||||
size: 50,
|
? Column(
|
||||||
)
|
mainAxisAlignment:
|
||||||
: const SizedBox(),
|
MainAxisAlignment.spaceAround,
|
||||||
|
children: [
|
||||||
|
const Icon(
|
||||||
|
Icons.ac_unit,
|
||||||
|
color: AppColor.blueColor,
|
||||||
|
size: 50,
|
||||||
|
),
|
||||||
|
Text(
|
||||||
|
'Air condition Trip'.tr,
|
||||||
|
style: AppStyle.subtitle,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
)
|
||||||
|
: const SizedBox(),
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
Text(
|
||||||
Text(
|
myList[31].toString().tr,
|
||||||
myList[31].toString(),
|
style: AppStyle.title
|
||||||
style: AppStyle.title
|
.copyWith(color: AppColor.greenColor),
|
||||||
.copyWith(color: AppColor.greenColor),
|
),
|
||||||
),
|
],
|
||||||
],
|
)),
|
||||||
)),
|
|
||||||
),
|
|
||||||
Container(
|
|
||||||
height: Get.height * .15,
|
|
||||||
width: Get.width * .9,
|
|
||||||
decoration: AppStyle.boxDecoration1,
|
|
||||||
child: ListView(
|
|
||||||
// mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
|
||||||
// crossAxisAlignment: CrossAxisAlignment.start,
|
|
||||||
children: [
|
|
||||||
Column(
|
|
||||||
children: [
|
|
||||||
Row(
|
|
||||||
mainAxisAlignment: MainAxisAlignment.start,
|
|
||||||
children: [
|
|
||||||
const Icon(
|
|
||||||
Icons.arrow_circle_up,
|
|
||||||
color: AppColor.greenColor,
|
|
||||||
),
|
|
||||||
Text(
|
|
||||||
myList[12] + ' ' + ' (${myList[11]}) ',
|
|
||||||
style: AppStyle.title,
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
Text(
|
|
||||||
myList[29],
|
|
||||||
style: AppStyle.title,
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
Column(
|
|
||||||
children: [
|
|
||||||
Row(
|
|
||||||
mainAxisAlignment: MainAxisAlignment.start,
|
|
||||||
children: [
|
|
||||||
const Icon(
|
|
||||||
Icons.arrow_circle_up,
|
|
||||||
color: AppColor.greenColor,
|
|
||||||
),
|
|
||||||
Text(
|
|
||||||
myList[5] + ' ' + ' (${myList[4]}) ',
|
|
||||||
style: AppStyle.title,
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
Text(
|
|
||||||
myList[30],
|
|
||||||
style: AppStyle.title,
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
),
|
||||||
),
|
const SizedBox(
|
||||||
Padding(
|
height: 5,
|
||||||
padding: const EdgeInsets.all(8.0),
|
),
|
||||||
child: RichText(
|
Container(
|
||||||
text: TextSpan(
|
height: Get.height * .15,
|
||||||
text: 'Passenger name : '
|
width: Get.width * .9,
|
||||||
.tr, // Changed text to be more generic
|
decoration: AppStyle.boxDecoration1,
|
||||||
style: AppStyle.subtitle,
|
child: Padding(
|
||||||
children: [
|
padding: const EdgeInsets.symmetric(
|
||||||
TextSpan(
|
horizontal: 5, vertical: 1),
|
||||||
text: myList[8],
|
child: ListView(
|
||||||
style: AppStyle
|
// mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
.title), // Assuming myList[8] holds passenger name
|
// crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
TextSpan(text: ' (', style: AppStyle.subtitle),
|
children: [
|
||||||
TextSpan(
|
Column(
|
||||||
text: myList[33].toString(),
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
style: AppStyle
|
children: [
|
||||||
.title), // Assuming 'rate' holds the passenger rate
|
Row(
|
||||||
TextSpan(text: ' ⭐)', style: AppStyle.subtitle),
|
mainAxisAlignment: MainAxisAlignment.start,
|
||||||
],
|
children: [
|
||||||
|
const Icon(
|
||||||
|
Icons.arrow_circle_up,
|
||||||
|
color: AppColor.greenColor,
|
||||||
|
),
|
||||||
|
Text(
|
||||||
|
myList[12] + ' ' + ' (${myList[11]}) ',
|
||||||
|
style: AppStyle.title,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
Text(
|
||||||
|
myList[29],
|
||||||
|
style: AppStyle.title,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
const Icon(
|
||||||
|
Icons.arrow_circle_down,
|
||||||
|
color: AppColor.redColor,
|
||||||
|
),
|
||||||
|
Text(
|
||||||
|
myList[5] + ' ' + ' (${myList[4]}) ',
|
||||||
|
style: AppStyle.title,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
Text(
|
||||||
|
myList[30],
|
||||||
|
style: AppStyle.title,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
Padding(
|
||||||
|
padding: const EdgeInsets.all(8.0),
|
||||||
Padding(
|
|
||||||
padding: const EdgeInsets.all(4),
|
|
||||||
child: Container(
|
|
||||||
color: AppColor.greenColor.withOpacity(.5),
|
|
||||||
child: RichText(
|
child: RichText(
|
||||||
text: TextSpan(
|
text: TextSpan(
|
||||||
text: 'Cost Of Trip IS '.tr,
|
text: "Passenger name: "
|
||||||
style: AppStyle.title,
|
.tr, // Changed text to be more generic
|
||||||
|
style: AppStyle.subtitle,
|
||||||
children: [
|
children: [
|
||||||
TextSpan(
|
TextSpan(
|
||||||
text: myList[26], style: AppStyle.headTitle2),
|
text: myList[8],
|
||||||
|
style: AppStyle
|
||||||
|
.title), // Assuming myList[8] holds passenger name
|
||||||
|
TextSpan(text: ' (', style: AppStyle.subtitle),
|
||||||
|
TextSpan(
|
||||||
|
text: myList[33].toString(),
|
||||||
|
style: AppStyle
|
||||||
|
.title), // Assuming 'rate' holds the passenger rate
|
||||||
|
TextSpan(text: ' ⭐)', style: AppStyle.subtitle),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
|
||||||
Padding(
|
|
||||||
padding: const EdgeInsets.all(8.0),
|
|
||||||
child: Row(
|
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
|
||||||
children: [
|
|
||||||
MyElevatedButton(
|
|
||||||
kolor: AppColor.greenColor,
|
|
||||||
title: 'Accept Order'.tr,
|
|
||||||
onPressed: () async {
|
|
||||||
box.write(BoxName.statusDriverLocation, 'on');
|
|
||||||
|
|
||||||
orderRequestController.endTimer();
|
Padding(
|
||||||
orderRequestController.changeApplied();
|
padding: const EdgeInsets.all(4),
|
||||||
await CRUD().postFromDialogue(
|
child: Container(
|
||||||
link: AppLink.addDriverOrder,
|
color: AppColor.greenColor.withOpacity(.5),
|
||||||
payload: {
|
child: Row(
|
||||||
'driver_id': myList[6].toString(),
|
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
|
||||||
// box.read(BoxName.driverID).toString(),
|
children: [
|
||||||
'order_id': body.toString(),
|
RichText(
|
||||||
'status': 'Apply'
|
text: TextSpan(
|
||||||
|
text: 'Cost Of Trip IS '.tr,
|
||||||
|
style: AppStyle.title,
|
||||||
|
children: [
|
||||||
|
TextSpan(
|
||||||
|
text: myList[26],
|
||||||
|
style: AppStyle.headTitle2),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
RichText(
|
||||||
|
text: TextSpan(
|
||||||
|
text: 'Total net'.tr,
|
||||||
|
style: AppStyle.title,
|
||||||
|
children: [
|
||||||
|
TextSpan(
|
||||||
|
text: (double.parse(myList[2]) -
|
||||||
|
double.parse(myList[32]))
|
||||||
|
.toStringAsFixed(2),
|
||||||
|
style: AppStyle.headTitle2),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
|
||||||
|
Padding(
|
||||||
|
padding: const EdgeInsets.all(8.0),
|
||||||
|
child: Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
|
children: [
|
||||||
|
MyElevatedButton(
|
||||||
|
kolor: AppColor.greenColor,
|
||||||
|
title: 'Accept Order'.tr,
|
||||||
|
onPressed: () async {
|
||||||
|
Get.put(HomeCaptainController()).changeRideId();
|
||||||
|
box.write(BoxName.statusDriverLocation, 'on');
|
||||||
|
orderRequestController.endTimer();
|
||||||
|
orderRequestController.changeApplied();
|
||||||
|
await CRUD().postFromDialogue(
|
||||||
|
link: AppLink.addDriverOrder,
|
||||||
|
payload: {
|
||||||
|
'driver_id': myList[6].toString(),
|
||||||
|
// box.read(BoxName.driverID).toString(),
|
||||||
|
'order_id': myList[16].toString(),
|
||||||
|
'status': 'Apply'
|
||||||
|
});
|
||||||
|
var res = await CRUD().post(
|
||||||
|
link: AppLink.updateRideAndCheckIfApplied,
|
||||||
|
payload: {
|
||||||
|
'id': myList[16],
|
||||||
|
'rideTimeStart': DateTime.now().toString(),
|
||||||
|
'status': 'Apply',
|
||||||
|
'driver_id': myList[6].toString(),
|
||||||
|
});
|
||||||
|
if (res == 'failure') {
|
||||||
|
MyDialog().getDialog(
|
||||||
|
"This ride is already applied by another driver."
|
||||||
|
.tr,
|
||||||
|
'', () {
|
||||||
|
Get.back();
|
||||||
});
|
});
|
||||||
var res = await CRUD().post(
|
} else {
|
||||||
link: AppLink.updateRideAndCheckIfApplied,
|
List<String> bodyToPassenger = [
|
||||||
payload: {
|
myList[6].toString(), //driver id
|
||||||
'id': myList[16],
|
myList[8].toString(), // driver name
|
||||||
'rideTimeStart': DateTime.now().toString(),
|
myList[9].toString(), //token driver
|
||||||
'status': 'Apply',
|
];
|
||||||
'driver_id': box.read(BoxName.driverID),
|
FirebaseMessagesController()
|
||||||
});
|
.sendNotificationToPassengerToken(
|
||||||
if (res == 'failure') {
|
'Apply Ride'.tr,
|
||||||
MyDialog().getDialog(
|
'your ride is applied'.tr,
|
||||||
"This ride is already applied by another driver."
|
// arguments['DriverList'][9].toString(),
|
||||||
.tr,
|
myList[9].toString(),
|
||||||
'', () {
|
// box.read(BoxName.tokenDriver).toString(),
|
||||||
|
bodyToPassenger,
|
||||||
|
'start.wav');
|
||||||
Get.back();
|
Get.back();
|
||||||
});
|
box.write(BoxName.rideArguments, {
|
||||||
} else {
|
'passengerLocation': myList[0].toString(),
|
||||||
|
'passengerDestination': myList[1].toString(),
|
||||||
|
'Duration': myList[4].toString(),
|
||||||
|
'totalCost': myList[26].toString(),
|
||||||
|
'Distance': myList[5].toString(),
|
||||||
|
'name': myList[8].toString(),
|
||||||
|
'phone': myList[10].toString(),
|
||||||
|
'email': myList[28].toString(),
|
||||||
|
'WalletChecked': myList[13].toString(),
|
||||||
|
'tokenPassenger': myList[9].toString(),
|
||||||
|
'direction':
|
||||||
|
'https://www.google.com/maps/dir/${myList[0]}/${myList[1]}/',
|
||||||
|
'DurationToPassenger': myList[15].toString(),
|
||||||
|
'rideId': myList[16].toString(),
|
||||||
|
'passengerId': myList[7].toString(),
|
||||||
|
'driverId': myList[18].toString(),
|
||||||
|
'durationOfRideValue': myList[19].toString(),
|
||||||
|
'paymentAmount': myList[2].toString(),
|
||||||
|
'paymentMethod': myList[13].toString() == 'true'
|
||||||
|
? 'visa'
|
||||||
|
: 'cash',
|
||||||
|
'isHaveSteps': myList[20].toString(),
|
||||||
|
'step0': myList[21].toString(),
|
||||||
|
'step1': myList[22].toString(),
|
||||||
|
'step2': myList[23].toString(),
|
||||||
|
'step3': myList[24].toString(),
|
||||||
|
'step4': myList[25].toString(),
|
||||||
|
'passengerWalletBurc': myList[26].toString(),
|
||||||
|
'timeOfOrder': DateTime.now().toString(),
|
||||||
|
'totalPassenger': myList[2].toString(),
|
||||||
|
'carType': myList[31].toString(),
|
||||||
|
'kazan': myList[32].toString(),
|
||||||
|
'startNameLocation': myList[29].toString(),
|
||||||
|
'endNameLocation': myList[30].toString(),
|
||||||
|
});
|
||||||
|
'passengerID =${box.read(BoxName.rideArguments)}';
|
||||||
|
Get.to(() => PassengerLocationMapPage(),
|
||||||
|
arguments: box.read(BoxName.rideArguments));
|
||||||
|
}
|
||||||
|
// Get.back();
|
||||||
|
},
|
||||||
|
),
|
||||||
|
GetBuilder<OrderRequestController>(
|
||||||
|
builder: (timerController) {
|
||||||
|
final isNearEnd = timerController.remainingTime <=
|
||||||
|
5; // Define a threshold for "near end"
|
||||||
|
|
||||||
|
return Stack(
|
||||||
|
alignment: Alignment.center,
|
||||||
|
children: [
|
||||||
|
CircularProgressIndicator(
|
||||||
|
value: timerController.progress,
|
||||||
|
// Set the color based on the "isNearEnd" condition
|
||||||
|
color: isNearEnd ? Colors.red : Colors.blue,
|
||||||
|
),
|
||||||
|
Text(
|
||||||
|
'${timerController.remainingTime}',
|
||||||
|
style: AppStyle.number,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
MyElevatedButton(
|
||||||
|
title: 'Refuse Order'.tr,
|
||||||
|
onPressed: () async {
|
||||||
|
orderRequestController.endTimer();
|
||||||
List<String> bodyToPassenger = [
|
List<String> bodyToPassenger = [
|
||||||
box.read(BoxName.driverID).toString(),
|
box.read(BoxName.driverID).toString(),
|
||||||
box.read(BoxName.nameDriver).toString(),
|
box.read(BoxName.nameDriver).toString(),
|
||||||
box.read(BoxName.tokenDriver).toString(),
|
box.read(BoxName.tokenDriver).toString(),
|
||||||
];
|
];
|
||||||
|
|
||||||
FirebaseMessagesController()
|
FirebaseMessagesController()
|
||||||
.sendNotificationToPassengerToken(
|
.sendNotificationToPassengerToken(
|
||||||
'Apply Ride',
|
'Order Under Review'.tr,
|
||||||
'your ride is applied'.tr,
|
'${box.read(BoxName.nameDriver)} ${'is reviewing your order. They may need more information or a higher price.'.tr}',
|
||||||
// arguments['DriverList'][9].toString(),
|
myList[9].toString(),
|
||||||
arguments['DriverList'][9].toString(),
|
|
||||||
// box.read(BoxName.tokenDriver).toString(),
|
|
||||||
bodyToPassenger,
|
bodyToPassenger,
|
||||||
'start.wav');
|
'notification.wav');
|
||||||
Get.back();
|
|
||||||
box.write(BoxName.rideArguments, {
|
|
||||||
'passengerLocation': myList[0].toString(),
|
|
||||||
'passengerDestination': myList[1].toString(),
|
|
||||||
'Duration': myList[4].toString(),
|
|
||||||
'totalCost': myList[26].toString(),
|
|
||||||
'Distance': myList[5].toString(),
|
|
||||||
'name': myList[8].toString(),
|
|
||||||
'phone': myList[10].toString(),
|
|
||||||
'email': myList[28].toString(),
|
|
||||||
'WalletChecked': myList[13].toString(),
|
|
||||||
'tokenPassenger': myList[9].toString(),
|
|
||||||
'direction':
|
|
||||||
'https://www.google.com/maps/dir/${myList[0]}/${myList[1]}/',
|
|
||||||
'DurationToPassenger': myList[15].toString(),
|
|
||||||
'rideId': myList[16].toString(),
|
|
||||||
'passengerId': myList[7].toString(),
|
|
||||||
'driverId': myList[18].toString(),
|
|
||||||
'durationOfRideValue': myList[19].toString(),
|
|
||||||
'paymentAmount': myList[2].toString(),
|
|
||||||
'paymentMethod': myList[13].toString() == 'true'
|
|
||||||
? 'visa'
|
|
||||||
: 'cash',
|
|
||||||
'isHaveSteps': myList[20].toString(),
|
|
||||||
'step0': myList[21].toString(),
|
|
||||||
'step1': myList[22].toString(),
|
|
||||||
'step2': myList[23].toString(),
|
|
||||||
'step3': myList[24].toString(),
|
|
||||||
'step4': myList[25].toString(),
|
|
||||||
'passengerWalletBurc': myList[26].toString(),
|
|
||||||
'timeOfOrder': DateTime.now().toString(),
|
|
||||||
'totalPassenger': myList[2].toString(),
|
|
||||||
'carType': myList[31].toString(),
|
|
||||||
'kazan': myList[32].toString(),
|
|
||||||
'startNameLocation': myList[29].toString(),
|
|
||||||
'endNameLocation': myList[30].toString(),
|
|
||||||
});
|
|
||||||
'passengerID =${box.read(BoxName.rideArguments)}';
|
|
||||||
Get.to(() => PassengerLocationMapPage(),
|
|
||||||
arguments: box.read(BoxName.rideArguments));
|
|
||||||
}
|
|
||||||
// Get.back();
|
|
||||||
},
|
|
||||||
),
|
|
||||||
GetBuilder<OrderRequestController>(
|
|
||||||
builder: (timerController) {
|
|
||||||
final isNearEnd = timerController.remainingTime <=
|
|
||||||
5; // Define a threshold for "near end"
|
|
||||||
|
|
||||||
return Stack(
|
orderRequestController.refuseOrder(
|
||||||
alignment: Alignment.center,
|
|
||||||
children: [
|
|
||||||
CircularProgressIndicator(
|
|
||||||
value: timerController.progress,
|
|
||||||
// Set the color based on the "isNearEnd" condition
|
|
||||||
color: isNearEnd ? Colors.red : Colors.blue,
|
|
||||||
),
|
|
||||||
Text(
|
|
||||||
'${timerController.remainingTime}',
|
|
||||||
style: AppStyle.number,
|
|
||||||
),
|
|
||||||
],
|
|
||||||
);
|
|
||||||
},
|
|
||||||
),
|
|
||||||
MyElevatedButton(
|
|
||||||
title: 'Refuse Order'.tr,
|
|
||||||
onPressed: () async {
|
|
||||||
orderRequestController.endTimer();
|
|
||||||
List<String> bodyToPassenger = [
|
|
||||||
box.read(BoxName.driverID).toString(),
|
|
||||||
box.read(BoxName.nameDriver).toString(),
|
|
||||||
box.read(BoxName.tokenDriver).toString(),
|
|
||||||
];
|
|
||||||
|
|
||||||
FirebaseMessagesController()
|
|
||||||
.sendNotificationToPassengerToken(
|
|
||||||
'Refused Ride'.tr,
|
|
||||||
'${box.read(BoxName.nameDriver)} ${'reject your order.'.tr}',
|
|
||||||
arguments['DriverList'][9].toString(),
|
|
||||||
// box.read(BoxName.tokenDriver).toString(),
|
|
||||||
bodyToPassenger,
|
|
||||||
'cancel.wav');
|
|
||||||
orderRequestController.refuseOrder(
|
|
||||||
myList[16].toString(),
|
|
||||||
);
|
|
||||||
orderRequestController.addRideToNotificationDriverString(
|
|
||||||
myList[16].toString(),
|
myList[16].toString(),
|
||||||
myList[29].toString(),
|
);
|
||||||
myList[30].toString(),
|
orderRequestController.addRideToNotificationDriverString(
|
||||||
'${DateTime.now().year}-${DateTime.now().month}-${DateTime.now().day}',
|
myList[16].toString(),
|
||||||
'${DateTime.now().hour}:${DateTime.now().minute}',
|
myList[29].toString(),
|
||||||
myList[2].toString(),
|
myList[30].toString(),
|
||||||
myList[7].toString(),
|
'${DateTime.now().year}-${DateTime.now().month}-${DateTime.now().day}',
|
||||||
'wait',
|
'${DateTime.now().hour}:${DateTime.now().minute}',
|
||||||
myList[31].toString(),
|
myList[2].toString(),
|
||||||
myList[33].toString(),
|
myList[7].toString(),
|
||||||
myList[2].toString(),
|
'wait',
|
||||||
myList[5].toString(),
|
myList[31].toString(),
|
||||||
myList[4].toString()); //duration
|
myList[33].toString(),
|
||||||
},
|
myList[2].toString(),
|
||||||
kolor: AppColor.redColor,
|
myList[5].toString(),
|
||||||
),
|
myList[4].toString()); //duration
|
||||||
],
|
},
|
||||||
),
|
kolor: AppColor.redColor,
|
||||||
)
|
),
|
||||||
],
|
],
|
||||||
|
),
|
||||||
|
)
|
||||||
|
],
|
||||||
|
),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
],
|
],
|
||||||
|
|||||||
@@ -131,7 +131,7 @@ class OrderSpeedRequest extends StatelessWidget {
|
|||||||
? AppColor.greenColor
|
? AppColor.greenColor
|
||||||
: AppColor.secondaryColor,
|
: AppColor.secondaryColor,
|
||||||
child: Row(
|
child: Row(
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
mainAxisAlignment: MainAxisAlignment.spaceAround,
|
||||||
children: [
|
children: [
|
||||||
TextButton.icon(
|
TextButton.icon(
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
@@ -145,12 +145,14 @@ class OrderSpeedRequest extends StatelessWidget {
|
|||||||
'Trip has Steps'.tr,
|
'Trip has Steps'.tr,
|
||||||
style: AppStyle.title,
|
style: AppStyle.title,
|
||||||
)
|
)
|
||||||
: Text('Routs of Trip'.tr,
|
: Text('Payment Method'.tr,
|
||||||
style: AppStyle.title)),
|
style: AppStyle.title)),
|
||||||
Container(
|
Container(
|
||||||
color: myList[13].toString() == 'true'
|
decoration: AppStyle.boxDecoration.copyWith(
|
||||||
? AppColor.deepPurpleAccent
|
color: myList[13].toString() == 'true'
|
||||||
: AppColor.greenColor,
|
? AppColor.deepPurpleAccent
|
||||||
|
: AppColor.greenColor,
|
||||||
|
),
|
||||||
child: myList[13].toString() ==
|
child: myList[13].toString() ==
|
||||||
'true' //Visa or Cash Method from notify to driver
|
'true' //Visa or Cash Method from notify to driver
|
||||||
? Text(
|
? Text(
|
||||||
@@ -182,90 +184,99 @@ class OrderSpeedRequest extends StatelessWidget {
|
|||||||
duration: const Duration(seconds: 5),
|
duration: const Duration(seconds: 5),
|
||||||
curve: Curves.easeInOut,
|
curve: Curves.easeInOut,
|
||||||
child: myList[31].toString() == 'Comfort'
|
child: myList[31].toString() == 'Comfort'
|
||||||
? const Icon(
|
? Column(
|
||||||
Icons.ac_unit,
|
mainAxisAlignment:
|
||||||
color: AppColor.blueColor,
|
MainAxisAlignment.spaceAround,
|
||||||
size: 50,
|
children: [
|
||||||
|
const Icon(
|
||||||
|
Icons.ac_unit,
|
||||||
|
color: AppColor.blueColor,
|
||||||
|
size: 50,
|
||||||
|
),
|
||||||
|
Text(
|
||||||
|
'Air condition Trip'.tr,
|
||||||
|
style: AppStyle.subtitle,
|
||||||
|
),
|
||||||
|
],
|
||||||
)
|
)
|
||||||
: const SizedBox(),
|
: const SizedBox(),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
Text(
|
Text(
|
||||||
myList[31].toString(),
|
myList[31].toString().tr,
|
||||||
style: AppStyle.title
|
style: AppStyle.title
|
||||||
.copyWith(color: AppColor.greenColor),
|
.copyWith(color: AppColor.greenColor),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
)),
|
)),
|
||||||
),
|
),
|
||||||
|
const SizedBox(
|
||||||
|
height: 5,
|
||||||
|
),
|
||||||
Container(
|
Container(
|
||||||
height: Get.height * .15,
|
height: Get.height * .15,
|
||||||
width: Get.width * .9,
|
width: Get.width * .9,
|
||||||
decoration: AppStyle.boxDecoration1,
|
decoration: AppStyle.boxDecoration1,
|
||||||
child: ListView(
|
child: Padding(
|
||||||
// mainAxisAlignment: MainAxisAlignment.spaceAround,
|
padding:
|
||||||
// crossAxisAlignment: CrossAxisAlignment.start,
|
const EdgeInsets.symmetric(horizontal: 5, vertical: 1),
|
||||||
children: [
|
child: ListView(
|
||||||
Row(
|
// mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
// crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
children: [
|
||||||
Column(
|
Column(
|
||||||
children: [
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
Row(
|
children: [
|
||||||
mainAxisAlignment: MainAxisAlignment.start,
|
Row(
|
||||||
children: [
|
mainAxisAlignment: MainAxisAlignment.start,
|
||||||
const Icon(
|
children: [
|
||||||
Icons.arrow_circle_up,
|
const Icon(
|
||||||
color: AppColor.greenColor,
|
Icons.arrow_circle_up,
|
||||||
),
|
color: AppColor.greenColor,
|
||||||
Text(
|
),
|
||||||
myList[12] + ' ' + ' (${myList[11]}) ',
|
Text(
|
||||||
style: AppStyle.title,
|
myList[12] + ' ' + ' (${myList[11]}) ',
|
||||||
),
|
style: AppStyle.title,
|
||||||
],
|
),
|
||||||
),
|
],
|
||||||
Text(
|
),
|
||||||
myList[29],
|
Text(
|
||||||
style: AppStyle.title,
|
myList[29],
|
||||||
),
|
style: AppStyle.title,
|
||||||
],
|
),
|
||||||
),
|
],
|
||||||
],
|
),
|
||||||
),
|
Column(
|
||||||
Row(
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
children: [
|
||||||
children: [
|
Row(
|
||||||
Column(
|
mainAxisAlignment: MainAxisAlignment.start,
|
||||||
children: [
|
children: [
|
||||||
Row(
|
const Icon(
|
||||||
mainAxisAlignment: MainAxisAlignment.start,
|
Icons.arrow_circle_down,
|
||||||
children: [
|
color: AppColor.redColor,
|
||||||
const Icon(
|
),
|
||||||
Icons.arrow_circle_up,
|
Text(
|
||||||
color: AppColor.greenColor,
|
myList[5] + ' ' + ' (${myList[4]}) ',
|
||||||
),
|
style: AppStyle.title,
|
||||||
Text(
|
),
|
||||||
myList[5] + ' ' + ' (${myList[4]}) ',
|
],
|
||||||
style: AppStyle.title,
|
),
|
||||||
),
|
Text(
|
||||||
],
|
myList[30],
|
||||||
),
|
style: AppStyle.title,
|
||||||
Text(
|
),
|
||||||
myList[30],
|
],
|
||||||
style: AppStyle.title,
|
),
|
||||||
),
|
],
|
||||||
],
|
),
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
Padding(
|
Padding(
|
||||||
padding: const EdgeInsets.all(8.0),
|
padding: const EdgeInsets.all(8.0),
|
||||||
child: RichText(
|
child: RichText(
|
||||||
text: TextSpan(
|
text: TextSpan(
|
||||||
text: 'Passenger name : '
|
text: "Passenger name: "
|
||||||
.tr, // Changed text to be more generic
|
.tr, // Changed text to be more generic
|
||||||
style: AppStyle.subtitle,
|
style: AppStyle.subtitle,
|
||||||
children: [
|
children: [
|
||||||
@@ -283,21 +294,42 @@ class OrderSpeedRequest extends StatelessWidget {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|
||||||
Padding(
|
Padding(
|
||||||
padding: const EdgeInsets.all(4),
|
padding: const EdgeInsets.all(4),
|
||||||
child: Container(
|
child: Container(
|
||||||
color: AppColor.deepPurpleAccent,
|
color: AppColor.greenColor.withOpacity(.5),
|
||||||
child: RichText(
|
child: Row(
|
||||||
text: TextSpan(
|
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
|
||||||
text: 'Cost Of Trip IS '.tr,
|
children: [
|
||||||
style: AppStyle.title,
|
RichText(
|
||||||
children: [
|
text: TextSpan(
|
||||||
TextSpan(text: myList[26], style: AppStyle.headTitle2),
|
text: 'Cost Of Trip IS '.tr,
|
||||||
],
|
style: AppStyle.title,
|
||||||
),
|
children: [
|
||||||
|
TextSpan(
|
||||||
|
text: myList[26], style: AppStyle.headTitle2),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
RichText(
|
||||||
|
text: TextSpan(
|
||||||
|
text: 'Total net'.tr,
|
||||||
|
style: AppStyle.title,
|
||||||
|
children: [
|
||||||
|
TextSpan(
|
||||||
|
text: (double.parse(myList[2]) -
|
||||||
|
double.parse(myList[32]))
|
||||||
|
.toStringAsFixed(2),
|
||||||
|
style: AppStyle.headTitle2),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|
||||||
Padding(
|
Padding(
|
||||||
padding: const EdgeInsets.all(8.0),
|
padding: const EdgeInsets.all(8.0),
|
||||||
child: Row(
|
child: Row(
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user