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.
|
||||
minSdk = 23
|
||||
targetSdk = flutter.targetSdkVersion
|
||||
versionCode = 64
|
||||
versionName = '1.5.64'
|
||||
versionCode = 68
|
||||
versionName = '1.5.68'
|
||||
multiDexEnabled =true
|
||||
}
|
||||
|
||||
// defaultConfig {
|
||||
@@ -95,5 +96,6 @@ dependencies {
|
||||
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
|
||||
// implementation platform('com.google.firebase:firebase-bom:32.1.1')
|
||||
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"
|
||||
},
|
||||
"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": {
|
||||
"mobilesdk_app_id": "1:594687661098:android:683982cbf71fa423595f53",
|
||||
@@ -12,6 +84,58 @@
|
||||
"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": [
|
||||
{
|
||||
"client_id": "594687661098-2u640akrb3k7sak5t0nqki6f4v6hq1bq.apps.googleusercontent.com",
|
||||
@@ -49,6 +173,22 @@
|
||||
}
|
||||
},
|
||||
"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_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.ACCESS_FINE_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.FOREGROUND_SERVICE" />
|
||||
<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.VIBRATE" />
|
||||
<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.WRITE_EXTERNAL_STORAGE" />
|
||||
<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.autofocus" />
|
||||
|
||||
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
|
||||
|
||||
<application
|
||||
android:allowBackup="true"
|
||||
android:icon="@mipmap/launcher_icon"
|
||||
android:label="Sefer Driver"
|
||||
android:name="${applicationName}"
|
||||
android:icon="@mipmap/launcher_icon">
|
||||
android:theme="@style/LaunchTheme">
|
||||
<service
|
||||
android:name=".MyFirebaseMessagingService"
|
||||
android:exported="false"></service>
|
||||
|
||||
<activity
|
||||
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:exported="true"
|
||||
android:hardwareAccelerated="true"
|
||||
android:launchMode="singleTask"
|
||||
android:theme="@style/LaunchTheme"
|
||||
android:windowSoftInputMode="adjustResize">
|
||||
<meta-data
|
||||
android:name="io.flutter.embedding.android.NormalTheme"
|
||||
android:resource="@style/NormalTheme" />
|
||||
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN" />
|
||||
<category android:name="android.intent.category.LAUNCHER" />
|
||||
<action android:name="com.example.action.APP_ACTION" />
|
||||
<category android:name="android.intent.category.DEFAULT" />
|
||||
</intent-filter>
|
||||
</activity>
|
||||
<activity
|
||||
android:name="com.yalantis.ucrop.UCropActivity"
|
||||
android:screenOrientation="portrait"
|
||||
android:theme="@style/Theme.AppCompat.Light.NoActionBar" />
|
||||
<meta-data
|
||||
android:name="com.google.android.geo.API_KEY"
|
||||
android:value="@string/api_key" />
|
||||
@@ -49,28 +69,77 @@
|
||||
<meta-data
|
||||
android:name="flutterEmbedding"
|
||||
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
|
||||
android:name=".LocationUpdatesService"
|
||||
android:foregroundServiceType="location"
|
||||
android:exported="false"> <!-- or false, depending on whether it should be accessible by other
|
||||
apps -->
|
||||
android:exported="false"
|
||||
android:foregroundServiceType="location" />
|
||||
|
||||
|
||||
<!-- <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
|
||||
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
|
||||
android:name=".YourBroadcastReceiver"
|
||||
android:exported="false"> <!-- or false, depending on whether it should be accessible by other
|
||||
apps -->
|
||||
<!-- <intent-filter>
|
||||
<action android:name="com.example.ACTION" />
|
||||
</intent-filter> -->
|
||||
android:name="com.google.firebase.iid.FirebaseInstanceIdReceiver"
|
||||
android:exported="true"
|
||||
android:permission="com.google.android.c2dm.permission.SEND">
|
||||
<intent-filter>
|
||||
<action android:name="com.google.android.c2dm.intent.RECEIVE" />
|
||||
|
||||
<category android:name="com.sefer_driver" />
|
||||
</intent-filter>
|
||||
</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>
|
||||
|
||||
</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
|
||||
|
||||
import io.flutter.embedding.android.FlutterActivity
|
||||
|
||||
import android.content.Intent
|
||||
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">default_channel</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>
|
||||
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)
|
||||
- nanopb (< 2.30911.0, >= 2.30908.0)
|
||||
- Flutter (1.0.0)
|
||||
- flutter_contacts (0.0.1):
|
||||
- Flutter
|
||||
- flutter_image_compress_common (1.0.0):
|
||||
- Flutter
|
||||
- Mantle
|
||||
@@ -283,6 +285,7 @@ DEPENDENCIES:
|
||||
- firebase_core (from `.symlinks/plugins/firebase_core/ios`)
|
||||
- firebase_messaging (from `.symlinks/plugins/firebase_messaging/ios`)
|
||||
- Flutter (from `Flutter`)
|
||||
- flutter_contacts (from `.symlinks/plugins/flutter_contacts/ios`)
|
||||
- flutter_image_compress_common (from `.symlinks/plugins/flutter_image_compress_common/ios`)
|
||||
- flutter_local_notifications (from `.symlinks/plugins/flutter_local_notifications/ios`)
|
||||
- flutter_secure_storage (from `.symlinks/plugins/flutter_secure_storage/ios`)
|
||||
@@ -369,6 +372,8 @@ EXTERNAL SOURCES:
|
||||
:path: ".symlinks/plugins/firebase_messaging/ios"
|
||||
Flutter:
|
||||
:path: Flutter
|
||||
flutter_contacts:
|
||||
:path: ".symlinks/plugins/flutter_contacts/ios"
|
||||
flutter_image_compress_common:
|
||||
:path: ".symlinks/plugins/flutter_image_compress_common/ios"
|
||||
flutter_local_notifications:
|
||||
@@ -441,6 +446,7 @@ SPEC CHECKSUMS:
|
||||
FirebaseInstallations: 913cf60d0400ebd5d6b63a28b290372ab44590dd
|
||||
FirebaseMessaging: 087a7c7cadef7b9239f005bc4db823894844f323
|
||||
Flutter: e0871f40cf51350855a761d2e70bf5af5b9b5de7
|
||||
flutter_contacts: edb1c5ce76aa433e20e6cb14c615f4c0b66e0983
|
||||
flutter_image_compress_common: ec1d45c362c9d30a3f6a0426c297f47c52007e3e
|
||||
flutter_local_notifications: 4cde75091f6327eb8517fa068a0a5950212d2086
|
||||
flutter_secure_storage: d33dac7ae2ea08509be337e775f6b59f1ff45f12
|
||||
|
||||
@@ -6,6 +6,8 @@
|
||||
<string>594687661098-9fnj82nef9oagl98prigdf8qne3ddbto.apps.googleusercontent.com</string>
|
||||
<key>REVERSED_CLIENT_ID</key>
|
||||
<string>com.googleusercontent.apps.594687661098-9fnj82nef9oagl98prigdf8qne3ddbto</string>
|
||||
<key>ANDROID_CLIENT_ID</key>
|
||||
<string>594687661098-2dhoogl7be9phobfbu8bbg1sj567iv88.apps.googleusercontent.com</string>
|
||||
<key>API_KEY</key>
|
||||
<string>AIzaSyCf2mW2h0HD8ZYjwh4VOa2ladw6MJkCDTM</string>
|
||||
<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">
|
||||
<plist version="1.0">
|
||||
<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>
|
||||
<true/>
|
||||
<key>CFBundleDevelopmentRegion</key>
|
||||
@@ -21,7 +33,7 @@
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>APPL</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>51</string>
|
||||
<string>52</string>
|
||||
<key>CFBundleSignature</key>
|
||||
<string>????</string>
|
||||
<key>CFBundleURLTypes</key>
|
||||
@@ -36,7 +48,7 @@
|
||||
</dict>
|
||||
</array>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>4.0.51</string>
|
||||
<string>4.0.52</string>
|
||||
<key>FirebaseAppDelegateProxyEnabled</key>
|
||||
<string>NO</string>
|
||||
<key>GMSApiKey</key>
|
||||
@@ -51,8 +63,8 @@
|
||||
<key>LSRequiresIPhoneOS</key>
|
||||
<true/>
|
||||
<key>NSCameraUsageDescription</key>
|
||||
<string>This app requires access to your camera in order to scan QR codes and capture
|
||||
images for uploading and access to connect to a call.</string>
|
||||
<string>This app requires access to your camera in order to scan QR codes and capture images
|
||||
for uploading and access to connect to a call.</string>
|
||||
<key>NSFaceIDUsageDescription</key>
|
||||
<string>Use Face ID to securely authenticate payment accounts.</string>
|
||||
<key>NSLocationAlwaysAndWhenInUseUsageDescription</key>
|
||||
@@ -72,12 +84,6 @@
|
||||
<string>Explanation of why your app needs access to the photo library.</string>
|
||||
<key>UIApplicationSupportsIndirectInputEvents</key>
|
||||
<true/>
|
||||
<key>UIBackgroundModes</key>
|
||||
<array>
|
||||
<string>fetch</string>
|
||||
<string>location</string>
|
||||
<string>remote-notification</string>
|
||||
</array>
|
||||
<key>UILaunchStoryboardName</key>
|
||||
<string>LaunchScreen</string>
|
||||
<key>UIMainStoryboardFile</key>
|
||||
|
||||
@@ -4,6 +4,9 @@ class BoxName {
|
||||
static const String googlaMapApp = "googlaMapApp";
|
||||
|
||||
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 carType = "carType";
|
||||
static const String isFirstTime = "isFirstTime";
|
||||
|
||||
@@ -21,8 +21,9 @@ class AppColor {
|
||||
|
||||
// For dynamic elements like gradients
|
||||
static List<Color> gradientStartEnd = [
|
||||
const Color(0xFF1DA1F2), // Start with primary color
|
||||
const Color(0xFF0C7ABF), // End with a slightly darker shade of Twitter blue
|
||||
Color.fromARGB(255, 40, 158, 232), // Start with primary color
|
||||
Color.fromARGB(
|
||||
255, 44, 63, 75), // End with a slightly darker shade of Twitter blue
|
||||
];
|
||||
|
||||
static List<Color> secondaryGradientStartEnd = [
|
||||
|
||||
@@ -257,6 +257,9 @@ class AppLink {
|
||||
static String sendmany = "https://sms.kazumi.me/api/sms/send-many";
|
||||
static String checkCredit = "https://sms.kazumi.me/api/sms/check-credit";
|
||||
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///////////
|
||||
|
||||
|
||||
@@ -10,29 +10,30 @@ class AppStyle {
|
||||
fontSize: 40,
|
||||
color: AppColor.accentColor,
|
||||
fontFamily: box.read(BoxName.lang) == 'ar'
|
||||
? 'mohanad'
|
||||
: GoogleFonts.josefinSans().fontFamily);
|
||||
// ?GoogleFonts.notoNaskhArabic().fontFamily
|
||||
? GoogleFonts.notoNaskhArabic().fontFamily
|
||||
: GoogleFonts.roboto().fontFamily);
|
||||
static TextStyle headTitle2 = TextStyle(
|
||||
fontWeight: FontWeight.bold,
|
||||
fontSize: 26,
|
||||
color: AppColor.writeColor,
|
||||
fontFamily: box.read(BoxName.lang) == 'ar'
|
||||
? 'mohanad'
|
||||
: GoogleFonts.josefinSans().fontFamily);
|
||||
? GoogleFonts.notoNaskhArabic().fontFamily
|
||||
: GoogleFonts.roboto().fontFamily);
|
||||
static TextStyle title = TextStyle(
|
||||
fontWeight: FontWeight.normal,
|
||||
fontSize: box.read(BoxName.lang) == 'ar' ? 14 : 16,
|
||||
color: AppColor.writeColor,
|
||||
fontFamily: box.read(BoxName.lang) == 'ar'
|
||||
? 'mohanad'
|
||||
: GoogleFonts.josefinSans().fontFamily);
|
||||
? GoogleFonts.notoNaskhArabic().fontFamily
|
||||
: GoogleFonts.roboto().fontFamily);
|
||||
static TextStyle subtitle = TextStyle(
|
||||
fontWeight: FontWeight.bold,
|
||||
fontSize: 13,
|
||||
color: AppColor.writeColor,
|
||||
fontFamily: box.read(BoxName.lang) == 'ar'
|
||||
? 'mohanad'
|
||||
: GoogleFonts.josefinSans().fontFamily);
|
||||
? GoogleFonts.notoNaskhArabic().fontFamily
|
||||
: GoogleFonts.roboto().fontFamily);
|
||||
static TextStyle number = const TextStyle(
|
||||
fontWeight: FontWeight.bold,
|
||||
fontSize: 14,
|
||||
@@ -42,9 +43,9 @@ class AppStyle {
|
||||
static BoxDecoration boxDecoration = const BoxDecoration(
|
||||
boxShadow: [
|
||||
BoxShadow(
|
||||
color: AppColor.accentColor, blurRadius: 2, offset: Offset(1, 2)),
|
||||
color: AppColor.accentColor, blurRadius: 5, offset: Offset(2, 4)),
|
||||
BoxShadow(
|
||||
color: AppColor.accentColor, blurRadius: 2, offset: Offset(-1, -1))
|
||||
color: AppColor.accentColor, blurRadius: 5, offset: Offset(-2, -2))
|
||||
],
|
||||
color: AppColor.secondaryColor,
|
||||
borderRadius: BorderRadius.all(
|
||||
@@ -53,9 +54,13 @@ class AppStyle {
|
||||
static BoxDecoration boxDecoration1 = const BoxDecoration(
|
||||
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(
|
||||
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,
|
||||
borderRadius: BorderRadius.all(
|
||||
|
||||
@@ -6,4 +6,5 @@ class TableName {
|
||||
static const String rideLocation = "rideLocation";
|
||||
static const String faceDetectTimes = "faceDetectTimes";
|
||||
static const String captainNotification = "captainNotification";
|
||||
static const String applyRideFromOverLay = "applyRideFromOverLay";
|
||||
}
|
||||
|
||||
@@ -1,14 +1,18 @@
|
||||
import 'dart:convert';
|
||||
|
||||
import 'package:SEFER/constant/box_name.dart';
|
||||
import 'package:SEFER/constant/colors.dart';
|
||||
import 'package:SEFER/constant/links.dart';
|
||||
import 'package:SEFER/controller/functions/crud.dart';
|
||||
import 'package:SEFER/controller/home/payment/captain_wallet_controller.dart';
|
||||
import 'package:SEFER/views/widgets/mydialoug.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 '../../../main.dart';
|
||||
import '../../../print.dart';
|
||||
import '../../functions/launch.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 {
|
||||
MyDialog().getDialog(
|
||||
driverInvitationData[index]['countOfInvitDriver'] < 100
|
||||
@@ -89,7 +127,7 @@ class InviteController extends GetxController {
|
||||
"driverId": box.read(BoxName.driverID),
|
||||
"inviterDriverPhone": '+2${invitePhoneController.text}'
|
||||
});
|
||||
|
||||
Log.print('response: ${response}');
|
||||
if (response != 'failure') {
|
||||
var d = jsonDecode(response);
|
||||
Get.snackbar('Success', 'Invite sent successfully'.tr);
|
||||
@@ -109,7 +147,9 @@ class InviteController extends GetxController {
|
||||
|
||||
invitePhoneController.clear();
|
||||
} 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) {
|
||||
// print('Error sending invite: $e');
|
||||
|
||||
@@ -55,7 +55,7 @@ class LoginDriverController extends GetxController {
|
||||
'id': driverID,
|
||||
});
|
||||
print(res);
|
||||
if (res == 'Failure') {
|
||||
if (res == 'failure') {
|
||||
//Failure
|
||||
if (box.read(BoxName.phoneVerified).toString() == '1') {
|
||||
Get.offAll(() => EgyptCardAI());
|
||||
|
||||
@@ -79,6 +79,21 @@ class RegisterCaptainController extends GetxController {
|
||||
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 {
|
||||
SmsEgyptController smsEgyptController = Get.put(SmsEgyptController());
|
||||
|
||||
@@ -87,21 +102,38 @@ class RegisterCaptainController extends GetxController {
|
||||
update();
|
||||
if (formKey3.currentState!.validate()) {
|
||||
if (box.read(BoxName.countryCode) == 'Egypt') {
|
||||
var responseCheker = await CRUD()
|
||||
.post(link: AppLink.checkPhoneNumberISVerfiedDriver, payload: {
|
||||
'phone_number': '+2${phoneController.text}',
|
||||
});
|
||||
if (responseCheker != 'failure') {
|
||||
var d = jsonDecode(responseCheker);
|
||||
if (d['message'][0]['is_verified'].toString() == '1') {
|
||||
Get.snackbar('Phone number is verified before'.tr, '',
|
||||
backgroundColor: AppColor.greenColor);
|
||||
box.write(BoxName.isVerified, '1');
|
||||
box.write(BoxName.phone, '+2${phoneController.text}');
|
||||
await Get.put(LoginDriverController()).loginUsingCredentials(
|
||||
box.read(BoxName.driverID).toString(),
|
||||
box.read(BoxName.emailDriver).toString(),
|
||||
);
|
||||
if (isValidEgyptianPhoneNumber(phoneController.text)) {
|
||||
var responseCheker = await CRUD()
|
||||
.post(link: AppLink.checkPhoneNumberISVerfiedDriver, payload: {
|
||||
'phone_number': '+2${phoneController.text}',
|
||||
});
|
||||
if (responseCheker != 'failure') {
|
||||
var d = jsonDecode(responseCheker);
|
||||
if (d['message'][0]['is_verified'].toString() == '1') {
|
||||
Get.snackbar('Phone number is verified before'.tr, '',
|
||||
backgroundColor: AppColor.greenColor);
|
||||
box.write(BoxName.phoneVerified, '1');
|
||||
box.write(BoxName.phone, '+2${phoneController.text}');
|
||||
await Get.put(LoginDriverController()).loginUsingCredentials(
|
||||
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 {
|
||||
await CRUD().post(link: AppLink.sendVerifyOtpMessage, payload: {
|
||||
'phone_number': '+2${phoneController.text}',
|
||||
@@ -112,25 +144,15 @@ class RegisterCaptainController extends GetxController {
|
||||
|
||||
await smsEgyptController.sendSmsEgypt(
|
||||
phoneController.text.toString(), randomNumber.toString());
|
||||
|
||||
isSent = true;
|
||||
|
||||
isLoading = false;
|
||||
update();
|
||||
}
|
||||
} 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();
|
||||
Get.snackbar('Phone Number wrong'.tr, '',
|
||||
backgroundColor: AppColor.redColor);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
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/main.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 {
|
||||
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) {
|
||||
Get.snackbar('Google Sign-In error', '$error',
|
||||
backgroundColor: AppColor.redColor);
|
||||
// Log error details
|
||||
print('Google Sign-In error: $error');
|
||||
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:io';
|
||||
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:firebase_messaging/firebase_messaging.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
@@ -79,9 +78,27 @@ class FirebaseMessagesController extends GetxController {
|
||||
}
|
||||
});
|
||||
FirebaseMessaging.onBackgroundMessage((RemoteMessage message) async {
|
||||
// Handle background message
|
||||
if (message.data.isNotEmpty && message.notification != null) {
|
||||
fireBaseTitles(message);
|
||||
if (message.notification!.title! == 'Order'.tr) {
|
||||
if (Platform.isAndroid) {
|
||||
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 {
|
||||
if (message.notification!.title! == 'Order'.tr) {
|
||||
if (Platform.isAndroid) {
|
||||
NotificationController().showNotification('Order'.tr, '', 'order');
|
||||
NotificationController().showNotification('Order'.tr, '', 'order', '');
|
||||
}
|
||||
// await FirebaseMessagesController().showOverlayNotification(message);
|
||||
var myListString = message.data['DriverList'];
|
||||
// var points = message.data['PolylineJson'];
|
||||
|
||||
@@ -113,8 +131,10 @@ class FirebaseMessagesController extends GetxController {
|
||||
'body': message.notification!.body
|
||||
});
|
||||
} else if (message.notification!.title == 'Cancel Trip') {
|
||||
NotificationController().showNotification(
|
||||
'Cancel Trip'.tr, 'Passenger Cancel Trip'.tr, 'cancel');
|
||||
// if (Platform.isAndroid) {
|
||||
// NotificationController().showNotification(
|
||||
// 'Cancel Trip'.tr, 'Passenger Cancel Trip'.tr, 'cancel', '');
|
||||
// }
|
||||
cancelTripDialog();
|
||||
} else if (message.notification!.title! == 'token change') {
|
||||
// NotificationController()
|
||||
@@ -122,14 +142,18 @@ class FirebaseMessagesController extends GetxController {
|
||||
// GoogleSignInHelper.signOut();
|
||||
GoogleSignInHelper.signOut();
|
||||
} else if (message.notification!.title! == 'message From passenger') {
|
||||
NotificationController()
|
||||
.showNotification('message From passenger', ''.tr, 'tone2');
|
||||
if (Platform.isAndroid) {
|
||||
NotificationController()
|
||||
.showNotification('message From passenger', ''.tr, 'tone2', '');
|
||||
}
|
||||
passengerDialog(message.notification!.body!);
|
||||
|
||||
update();
|
||||
} else if (message.notification!.title! == 'face detect') {
|
||||
NotificationController()
|
||||
.showNotification('face detect'.tr, ''.tr, 'tone2');
|
||||
if (Platform.isAndroid) {
|
||||
NotificationController()
|
||||
.showNotification('face detect'.tr, ''.tr, 'tone2', '');
|
||||
}
|
||||
String result0 = await faceDetector();
|
||||
// Handle the result here, e.g., show a dialog or update the UI
|
||||
var result = jsonDecode(result0);
|
||||
@@ -156,19 +180,20 @@ class FirebaseMessagesController extends GetxController {
|
||||
} else if (message.notification!.title! == 'Hi ,I will go now') {
|
||||
// Get.snackbar('Hi ,I will go now', '',
|
||||
// backgroundColor: AppColor.greenColor);
|
||||
NotificationController().showNotification(
|
||||
'Passenger come to you'.tr, 'Hi ,I will go now'.tr, 'tone2');
|
||||
if (Platform.isAndroid) {
|
||||
NotificationController().showNotification(
|
||||
'Passenger come to you'.tr, 'Hi ,I will go now'.tr, 'tone2', '');
|
||||
}
|
||||
update();
|
||||
} else if (message.notification!.title! == 'Call Income'.tr) {
|
||||
try {
|
||||
var myListString = message.data['passengerList'];
|
||||
var driverList = jsonDecode(myListString) as List<dynamic>;
|
||||
// if (Platform.isAndroid) {
|
||||
NotificationController().showNotification(
|
||||
'Call Income'.tr,
|
||||
message.notification!.body!,
|
||||
'iphone_ringtone',
|
||||
);
|
||||
if (Platform.isAndroid) {
|
||||
NotificationController().showNotification('Call Income'.tr,
|
||||
message.notification!.body!, 'iphone_ringtone', '');
|
||||
}
|
||||
// }
|
||||
// Assuming GetMaterialApp is initialized and context is valid for navigation
|
||||
// Get.to(() => PassengerCallPage(
|
||||
@@ -183,11 +208,10 @@ class FirebaseMessagesController extends GetxController {
|
||||
var myListString = message.data['passengerList'];
|
||||
var driverList = jsonDecode(myListString) as List<dynamic>;
|
||||
// if (Platform.isAndroid) {
|
||||
NotificationController().showNotification(
|
||||
'Call Income'.tr,
|
||||
message.notification!.body!,
|
||||
'iphone_ringtone',
|
||||
);
|
||||
if (Platform.isAndroid) {
|
||||
NotificationController().showNotification('Call Income'.tr,
|
||||
message.notification!.body!, 'iphone_ringtone', '');
|
||||
}
|
||||
// }
|
||||
// Assuming GetMaterialApp is initialized and context is valid for navigation
|
||||
// Get.to(() => CallPage(
|
||||
@@ -198,11 +222,13 @@ class FirebaseMessagesController extends GetxController {
|
||||
} catch (e) {}
|
||||
} else if (message.notification!.title! ==
|
||||
"Criminal Document Required".tr) {
|
||||
NotificationController().showNotification(
|
||||
"Criminal Document Required".tr,
|
||||
message.notification!.body!,
|
||||
'tone2',
|
||||
);
|
||||
if (Platform.isAndroid) {
|
||||
NotificationController().showNotification(
|
||||
"Criminal Document Required".tr,
|
||||
message.notification!.body!,
|
||||
'tone2',
|
||||
'');
|
||||
}
|
||||
MyDialog().getDialog(
|
||||
"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>;
|
||||
if (Platform.isAndroid) {
|
||||
NotificationController().showNotification(
|
||||
'Call End'.tr,
|
||||
message.notification!.body!,
|
||||
'tone2',
|
||||
);
|
||||
'Call End'.tr, message.notification!.body!, 'tone2', '');
|
||||
}
|
||||
// Assuming GetMaterialApp is initialized and context is valid for navigation
|
||||
// Get.off(const CallPage());
|
||||
@@ -247,11 +270,14 @@ class FirebaseMessagesController extends GetxController {
|
||||
'body': message.notification!.body
|
||||
});
|
||||
} else if (message.notification!.title! == 'Order Applied'.tr) {
|
||||
NotificationController().showNotification(
|
||||
'The order Accepted by another Driver'.tr,
|
||||
'We regret to inform you that another driver has accepted this order.'
|
||||
.tr,
|
||||
'order');
|
||||
if (Platform.isAndroid) {
|
||||
NotificationController().showNotification(
|
||||
'The order Accepted by another Driver'.tr,
|
||||
'We regret to inform you that another driver has accepted this 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:get/get.dart';
|
||||
|
||||
import '../../main.dart';
|
||||
import '../../print.dart';
|
||||
|
||||
class NotificationController extends GetxController {
|
||||
final FlutterLocalNotificationsPlugin _flutterLocalNotificationsPlugin =
|
||||
FlutterLocalNotificationsPlugin();
|
||||
@@ -8,22 +17,108 @@ class NotificationController extends GetxController {
|
||||
// Initializes the local notifications plugin
|
||||
Future<void> initNotifications() async {
|
||||
const AndroidInitializationSettings android =
|
||||
AndroidInitializationSettings('@mipmap/launcher_icon');
|
||||
AndroidInitializationSettings('app_icon');
|
||||
|
||||
const InitializationSettings initializationSettings =
|
||||
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
|
||||
void showNotification(String title, String message, String tone) async {
|
||||
AndroidNotificationDetails android = AndroidNotificationDetails(
|
||||
'your channel id', 'your channel name',
|
||||
importance: Importance.max,
|
||||
priority: Priority.high,
|
||||
showWhen: false,
|
||||
sound: RawResourceAndroidNotificationSound(tone));
|
||||
void showNotification(
|
||||
String title, String message, String tone, String payLoad) async {
|
||||
BigTextStyleInformation bigTextStyleInformation = BigTextStyleInformation(
|
||||
message,
|
||||
contentTitle: title.tr,
|
||||
htmlFormatContent: true,
|
||||
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);
|
||||
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) {
|
||||
// Log.print('response: ${response.request}');
|
||||
// Log.print('response: ${response.body}');
|
||||
// Log.print('response: ${payload}');
|
||||
var jsonData = jsonDecode(response.body);
|
||||
if (jsonData['status'] == 'success') {
|
||||
return response.body;
|
||||
@@ -230,7 +233,7 @@ class CRUD {
|
||||
if (jsonData['status'] == 'success') {
|
||||
return response.body;
|
||||
} else {
|
||||
String errorMessage = jsonData['message'];
|
||||
// String errorMessage = jsonData['message'];
|
||||
// Get.snackbar('Error'.tr, errorMessage.tr,
|
||||
// backgroundColor: AppColor.redColor);
|
||||
return (jsonData['status']);
|
||||
|
||||
@@ -28,7 +28,7 @@ class LocationBackgroundController extends GetxController {
|
||||
await BackgroundLocation.setAndroidNotification(
|
||||
title: "Background Location",
|
||||
message: "Tracking location...",
|
||||
icon: "@mipmap/ic_launcher",
|
||||
icon: "@mipmap/launcher_icon",
|
||||
);
|
||||
|
||||
// Set the location update interval to 5 seconds
|
||||
|
||||
@@ -38,7 +38,7 @@ class LocationController extends GetxController {
|
||||
getLocation();
|
||||
// startLocationUpdates();
|
||||
|
||||
totalPoints = Get.put(CaptainWalletController()).totalPoints;
|
||||
totalPoints = Get.put(CaptainWalletController()).totalPoints.toString();
|
||||
// isActive = Get.put(HomeCaptainController()).isActive;
|
||||
}
|
||||
|
||||
@@ -47,7 +47,8 @@ class LocationController extends GetxController {
|
||||
_locationTimer =
|
||||
Timer.periodic(const Duration(seconds: 5), (timer) async {
|
||||
try {
|
||||
totalPoints = Get.find<CaptainWalletController>().totalPoints;
|
||||
totalPoints =
|
||||
Get.find<CaptainWalletController>().totalPoints.toString();
|
||||
isActive = Get.find<HomeCaptainController>().isActive;
|
||||
if (isActive) {
|
||||
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/info.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/views/widgets/elevated_btn.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:http/http.dart' as http;
|
||||
|
||||
import '../../print.dart';
|
||||
import '../auth/captin/login_captin_controller.dart';
|
||||
|
||||
class SmsEgyptController extends GetxController {
|
||||
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 {
|
||||
String sender = await getSender();
|
||||
var body = jsonEncode({
|
||||
"username": AppInformation.appName,
|
||||
"password": AK.smsPasswordEgypt, //'E)Pu=an/@Z',
|
||||
"message": "${AppInformation.appName} app code is $otp\ncopy it to app",
|
||||
"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"
|
||||
});
|
||||
|
||||
@@ -28,7 +44,20 @@ class SmsEgyptController extends GetxController {
|
||||
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(
|
||||
title: 'You will receive code in sms message'.tr,
|
||||
middleText: '',
|
||||
|
||||
@@ -101,54 +101,57 @@ class ImageController extends GetxController {
|
||||
}
|
||||
|
||||
choosImage(String link, String imageType) async {
|
||||
final pickedImage = await picker.pickImage(
|
||||
source: ImageSource.camera,
|
||||
preferredCameraDevice: CameraDevice.rear,
|
||||
);
|
||||
try {
|
||||
final pickedImage = await picker.pickImage(
|
||||
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(
|
||||
sourcePath: image!.path,
|
||||
uiSettings: [
|
||||
AndroidUiSettings(
|
||||
croppedFile = await ImageCropper().cropImage(
|
||||
sourcePath: image!.path,
|
||||
uiSettings: [
|
||||
AndroidUiSettings(
|
||||
toolbarTitle: 'Cropper'.tr,
|
||||
toolbarColor: AppColor.blueColor,
|
||||
toolbarWidgetColor: AppColor.yellowColor,
|
||||
initAspectRatio: CropAspectRatioPreset.original,
|
||||
lockAspectRatio: false),
|
||||
IOSUiSettings(
|
||||
title: 'Cropper'.tr,
|
||||
),
|
||||
],
|
||||
);
|
||||
lockAspectRatio: false,
|
||||
),
|
||||
IOSUiSettings(
|
||||
title: 'Cropper'.tr,
|
||||
),
|
||||
],
|
||||
);
|
||||
|
||||
if (croppedFile == null) return;
|
||||
if (croppedFile == null) return;
|
||||
|
||||
myImage = File(croppedFile!.path);
|
||||
isloading = true;
|
||||
update();
|
||||
myImage = File(croppedFile!.path);
|
||||
isloading = true;
|
||||
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(
|
||||
compressedImage,
|
||||
{
|
||||
'driverID':
|
||||
box.read(BoxName.driverID) ?? box.read(BoxName.passengerID),
|
||||
'imageType': imageType
|
||||
'imageType': imageType,
|
||||
},
|
||||
link,
|
||||
);
|
||||
} catch (e) {
|
||||
print('Error in choosImage: $e');
|
||||
Get.snackbar('Image Upload Failed'.tr, e.toString(),
|
||||
backgroundColor: AppColor.redColor);
|
||||
backgroundColor: AppColor.primaryColor);
|
||||
} finally {
|
||||
isloading = false;
|
||||
update();
|
||||
|
||||
@@ -229,7 +229,7 @@ class HomeCaptainController extends GetxController {
|
||||
getAllPayment();
|
||||
startPeriodicExecution();
|
||||
onMapCreated(mapHomeCaptainController!);
|
||||
totalPoints = Get.find<CaptainWalletController>().totalPoints;
|
||||
totalPoints = Get.find<CaptainWalletController>().totalPoints.toString();
|
||||
getRefusedOrderByCaptain();
|
||||
// LocationController().getLocation();
|
||||
super.onInit();
|
||||
|
||||
@@ -26,8 +26,10 @@ class MapDriverController extends GetxController {
|
||||
bool isLoading = true;
|
||||
final formKey1 = GlobalKey<FormState>();
|
||||
final formKey2 = GlobalKey<FormState>();
|
||||
final formKeyCancel = GlobalKey<FormState>();
|
||||
final messageToPassenger = TextEditingController();
|
||||
final sosEmergincyNumberCotroller = TextEditingController();
|
||||
final cancelTripCotroller = TextEditingController();
|
||||
List data = [];
|
||||
List dataDestination = [];
|
||||
LatLngBounds? boundsData;
|
||||
@@ -165,6 +167,30 @@ class MapDriverController extends GetxController {
|
||||
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 {
|
||||
if (box.read(BoxName.rideStatus) == 'Begin') {
|
||||
isPassengerInfoWindow = false;
|
||||
|
||||
@@ -33,6 +33,12 @@ class OrderRequestController extends GetxController {
|
||||
super.onInit();
|
||||
}
|
||||
|
||||
getRideDEtailsForBackgroundOrder() async {
|
||||
await CRUD().get(link: AppLink.getRidesDetails, payload: {
|
||||
'id': box.read(BoxName.myList)[2].toString(),
|
||||
});
|
||||
}
|
||||
|
||||
void addCustomStartIcon() async {
|
||||
// Create the marker with the resized image
|
||||
|
||||
|
||||
@@ -106,18 +106,18 @@ class CaptainWalletController extends GetxController {
|
||||
update();
|
||||
var res = await CRUD().get(
|
||||
link: AppLink.getAllPaymentFromRide,
|
||||
payload: {'driverID': box.read(BoxName.driverID)},
|
||||
payload: {'driverID': box.read(BoxName.driverID).toString()},
|
||||
);
|
||||
// isLoading = false;
|
||||
if (res != 'failure') {
|
||||
walletDate = jsonDecode(res);
|
||||
totalAmount = walletDate['message'][0]['total_amount'] ?? '0';
|
||||
totalAmount = walletDate['message'][0]['total_amount'].toString();
|
||||
update();
|
||||
var res1 = await CRUD().get(
|
||||
link: AppLink.getAllPaymentVisa,
|
||||
payload: {'driverID': box.read(BoxName.driverID)});
|
||||
payload: {'driverID': box.read(BoxName.driverID).toString()});
|
||||
walletDateVisa = jsonDecode(res1);
|
||||
totalAmountVisa = walletDateVisa['message'][0]['diff'] ?? '0';
|
||||
totalAmountVisa = walletDateVisa['message'][0]['diff'].toString();
|
||||
|
||||
update();
|
||||
} else {
|
||||
@@ -132,12 +132,13 @@ class CaptainWalletController extends GetxController {
|
||||
|
||||
var res = await CRUD().get(
|
||||
link: AppLink.getDriverPaymentPoints,
|
||||
payload: {'driverID': box.read(BoxName.driverID)},
|
||||
payload: {'driverID': box.read(BoxName.driverID).toString()},
|
||||
);
|
||||
isLoading = false;
|
||||
// update();
|
||||
walletDriverPointsDate = jsonDecode(res);
|
||||
|
||||
if (res != 'failure') {
|
||||
walletDriverPointsDate = jsonDecode(res);
|
||||
double totalPointsDouble = double.parse(
|
||||
walletDriverPointsDate['message'][0]['total_amount'].toString());
|
||||
totalPoints = totalPointsDouble.toStringAsFixed(0);
|
||||
@@ -283,7 +284,7 @@ class CaptainWalletController extends GetxController {
|
||||
);
|
||||
if (res != 'failure') {
|
||||
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']);
|
||||
// heavyPrice = double.parse(json['message'][0]['heavyPrice']);
|
||||
// latePrice = double.parse(json['message'][0]['latePrice']);
|
||||
|
||||
@@ -10,7 +10,7 @@ class LocaleController extends GetxController {
|
||||
String countryCode = '';
|
||||
void restartApp() {
|
||||
// Get.offAll(MyApp);
|
||||
runApp(const MyApp());
|
||||
runApp(MyApp());
|
||||
}
|
||||
|
||||
ThemeData appTheme = themeEnglish;
|
||||
|
||||
@@ -150,7 +150,29 @@ class MyTranslation extends Translations {
|
||||
"This driver is not registered": "هذا السائق غير مسجل",
|
||||
'insert amount': "أدخل المبلغ",
|
||||
"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": "طلب خاص",
|
||||
"Speed Order": "طلب سريع",
|
||||
"No data yet!": "لا توجد بيانات حتى الآن!",
|
||||
@@ -200,6 +222,7 @@ class MyTranslation extends Translations {
|
||||
"History Page": "سجل الرحلات ",
|
||||
"Finished": "مكتملة",
|
||||
"Trip Detail": "تفاصيل الرحلة",
|
||||
"Invite code already used": "تم استخدام رمز الدعوة بالفعل",
|
||||
"Price is": "السعر",
|
||||
"Times of Trip": "أوقات الرحلة",
|
||||
"Time to Passenger is": "الوقت للوصول للراكب",
|
||||
@@ -258,8 +281,17 @@ class MyTranslation extends Translations {
|
||||
"التقط صورة لرخصة سيارتك من الأمام",
|
||||
"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": "الاسم الكامل",
|
||||
"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": "نتيجة الفحص",
|
||||
"Criminal Record": "صحيفة الحالة الجنائية",
|
||||
"The email or phone number is already registered.":
|
||||
@@ -331,10 +363,13 @@ class MyTranslation extends Translations {
|
||||
"Invite sent successfully": "تم إرسال الدعوة بنجاح",
|
||||
"Failed to send invite": "فشل إرسال الدعوة",
|
||||
"An error occurred": "حدث خطأ",
|
||||
"Air Condition Trip": "رحلة تكييف ",
|
||||
"Passenger name: ": "اسم الراكب: ",
|
||||
"Criminal Document Required": "الفيش الجنائي مطلوب",
|
||||
"Criminal Document": "الفيش الجنائي",
|
||||
"Marital Status": "الحالة الاجتماعية",
|
||||
"Full Name (Marital)": "الاسم الكامل (الزوجي)",
|
||||
"Payment Method": "طريقة الدفع",
|
||||
"Expiration Date": "تاريخ الانتهاء",
|
||||
"Capture an Image of Your ID Document Back":
|
||||
"التقاط صورة للجهة الخلفية من وثيقة الهوية الخاصة بك",
|
||||
|
||||
@@ -4,6 +4,8 @@ import 'package:dio/dio.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:webview_flutter/webview_flutter.dart';
|
||||
|
||||
import '../../../print.dart';
|
||||
|
||||
class PaymobResponseWallet {
|
||||
final bool success;
|
||||
final String? transactionID;
|
||||
@@ -142,7 +144,7 @@ class PaymobPaymentWallet {
|
||||
}) async {
|
||||
final Map<String, dynamic> data = {
|
||||
"source": {
|
||||
"identifier": "01010101010", // Replace with actual source identifier
|
||||
"identifier": box.read(BoxName.phoneDriver).toString(),
|
||||
"subtype": "WALLET",
|
||||
},
|
||||
"payment_token": paymentToken,
|
||||
@@ -217,6 +219,7 @@ class PaymobPaymentWallet {
|
||||
),
|
||||
);
|
||||
final urlWallet = await _getWalletUrl(paymentToken: purchaseToken);
|
||||
Log.print('urlWallet: ${urlWallet}');
|
||||
|
||||
if (context.mounted) {
|
||||
final response = await PaymobIFrameWallet.show(
|
||||
|
||||
@@ -17,10 +17,7 @@ import 'package:flutter/foundation.dart'
|
||||
class DefaultFirebaseOptions {
|
||||
static FirebaseOptions get currentPlatform {
|
||||
if (kIsWeb) {
|
||||
throw UnsupportedError(
|
||||
'DefaultFirebaseOptions have not been configured for web - '
|
||||
'you can reconfigure this by running the FlutterFire CLI again.',
|
||||
);
|
||||
return web;
|
||||
}
|
||||
switch (defaultTargetPlatform) {
|
||||
case TargetPlatform.android:
|
||||
@@ -28,15 +25,9 @@ class DefaultFirebaseOptions {
|
||||
case TargetPlatform.iOS:
|
||||
return ios;
|
||||
case TargetPlatform.macOS:
|
||||
throw UnsupportedError(
|
||||
'DefaultFirebaseOptions have not been configured for macos - '
|
||||
'you can reconfigure this by running the FlutterFire CLI again.',
|
||||
);
|
||||
return macos;
|
||||
case TargetPlatform.windows:
|
||||
throw UnsupportedError(
|
||||
'DefaultFirebaseOptions have not been configured for windows - '
|
||||
'you can reconfigure this by running the FlutterFire CLI again.',
|
||||
);
|
||||
return windows;
|
||||
case TargetPlatform.linux:
|
||||
throw UnsupportedError(
|
||||
'DefaultFirebaseOptions have not been configured for linux - '
|
||||
@@ -51,7 +42,7 @@ class DefaultFirebaseOptions {
|
||||
|
||||
static const FirebaseOptions android = FirebaseOptions(
|
||||
apiKey: 'AIzaSyCyfwRXTwSTLOFQSQgN5p7QZgGJVZnEKq0',
|
||||
appId: '1:594687661098:android:46557bd4f534b5bb595f53',
|
||||
appId: '1:594687661098:android:b7ce96c17eb928ca595f53',
|
||||
messagingSenderId: '594687661098',
|
||||
projectId: 'ride-b1bd8',
|
||||
storageBucket: 'ride-b1bd8.appspot.com',
|
||||
@@ -63,8 +54,40 @@ class DefaultFirebaseOptions {
|
||||
messagingSenderId: '594687661098',
|
||||
projectId: 'ride-b1bd8',
|
||||
storageBucket: 'ride-b1bd8.appspot.com',
|
||||
iosClientId:
|
||||
'594687661098-9fnj82nef9oagl98prigdf8qne3ddbto.apps.googleusercontent.com',
|
||||
androidClientId: '594687661098-2dhoogl7be9phobfbu8bbg1sj567iv88.apps.googleusercontent.com',
|
||||
iosClientId: '594687661098-9fnj82nef9oagl98prigdf8qne3ddbto.apps.googleusercontent.com',
|
||||
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:io';
|
||||
|
||||
import 'package:SEFER/constant/box_name.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_messaging/firebase_messaging.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_stripe/flutter_stripe.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:get_storage/get_storage.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:permission_handler/permission_handler.dart';
|
||||
import 'package:wakelock_plus/wakelock_plus.dart';
|
||||
import 'constant/api_key.dart';
|
||||
import 'constant/credential.dart';
|
||||
import 'constant/info.dart';
|
||||
import 'controller/firebase/firbase_messge.dart';
|
||||
import 'controller/firebase/local_notification.dart';
|
||||
import 'controller/functions/location_controller.dart';
|
||||
import 'controller/functions/overlay_permisssion.dart';
|
||||
import 'controller/local/local_controller.dart';
|
||||
import 'controller/local/translations.dart';
|
||||
import 'controller/payment/paymob/paymob_wallet.dart';
|
||||
import 'firebase_options.dart';
|
||||
import 'models/db_sql.dart';
|
||||
import 'print.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();
|
||||
const storage = FlutterSecureStorage();
|
||||
|
||||
final PaymobPayment paymobPayment = PaymobPayment();
|
||||
final PaymobPaymentWallet paymobPaymentWallet = PaymobPaymentWallet();
|
||||
|
||||
DbSql sql = DbSql.instance;
|
||||
|
||||
@pragma('vm:entry-point')
|
||||
Future<void> backgroundMessageHandler(RemoteMessage message) async {
|
||||
await Firebase.initializeApp();
|
||||
if (message.data.isNotEmpty && message.notification != null) {
|
||||
FirebaseMessagesController().fireBaseTitles(message);
|
||||
if (Platform.isAndroid) {
|
||||
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 {
|
||||
await Firebase.initializeApp();
|
||||
final GlobalKey<NavigatorState> navigatorKey = GlobalKey<NavigatorState>();
|
||||
|
||||
var myListString = message.data['DriverList'];
|
||||
var myList = jsonDecode(myListString) as List<dynamic>;
|
||||
@pragma('vm:entry-point')
|
||||
void overlayMain() async {
|
||||
WidgetsFlutterBinding.ensureInitialized();
|
||||
|
||||
Get.to(() => OrderRequestPage(), arguments: {
|
||||
'myListString': myListString,
|
||||
'DriverList': myList,
|
||||
'body': message.notification?.body,
|
||||
});
|
||||
runApp(const MaterialApp(
|
||||
debugShowCheckedModeBanner: false,
|
||||
home: OrderOverlay(),
|
||||
));
|
||||
}
|
||||
|
||||
void closeOverLay() {
|
||||
FlutterOverlayWindow.closeOverlay();
|
||||
}
|
||||
|
||||
void main() async {
|
||||
WidgetsFlutterBinding.ensureInitialized();
|
||||
WakelockPlus.enable();
|
||||
await LocationController().startLocationUpdates();
|
||||
if (Platform.isAndroid) {
|
||||
await NotificationController().initNotifications();
|
||||
// Request location permission first
|
||||
PermissionStatus status = await Permission.location.request();
|
||||
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();
|
||||
|
||||
Stripe.publishableKey = AK.publishableKeyStripe;
|
||||
|
||||
if (Platform.isAndroid || Platform.isIOS) {
|
||||
@@ -72,7 +129,7 @@ void main() async {
|
||||
await FirebaseMessagesController().requestFirebaseMessagingPermission();
|
||||
|
||||
FirebaseMessaging.onBackgroundMessage(backgroundMessageHandler);
|
||||
|
||||
NotificationController().initNotifications();
|
||||
List<Future> initializationTasks = [
|
||||
FirebaseMessagesController().getNotificationSettings(),
|
||||
FirebaseMessagesController().getToken(),
|
||||
@@ -98,24 +155,35 @@ void main() async {
|
||||
iFrameID: 837992,
|
||||
);
|
||||
|
||||
runApp(const MyApp());
|
||||
runApp(MyApp());
|
||||
}
|
||||
|
||||
class MyApp extends StatelessWidget {
|
||||
const MyApp({super.key});
|
||||
MyApp({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
LocaleController localController = Get.put(LocaleController());
|
||||
// checkForUpdate(context);
|
||||
return GetMaterialApp(
|
||||
title: AppInformation.appName,
|
||||
translations: MyTranslation(),
|
||||
debugShowCheckedModeBanner: false,
|
||||
locale: localController.language,
|
||||
theme: localController.appTheme,
|
||||
key: UniqueKey(),
|
||||
initialRoute: '/',
|
||||
home: SplashScreen());
|
||||
navigatorKey: navigatorKey,
|
||||
title: AppInformation.appName,
|
||||
translations: MyTranslation(),
|
||||
debugShowCheckedModeBanner: false,
|
||||
locale: localController.language,
|
||||
theme: localController.appTheme,
|
||||
key: UniqueKey(),
|
||||
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
|
||||
)
|
||||
''');
|
||||
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),
|
||||
),
|
||||
const SizedBox(height: 20),
|
||||
MyTextForm(
|
||||
controller: controller.invitePhoneController,
|
||||
label: 'Enter driver\'s phone'.tr,
|
||||
hint: 'Enter driver\'s phone'.tr,
|
||||
type: TextInputType.phone),
|
||||
Row(
|
||||
children: [
|
||||
Expanded(
|
||||
child: MyTextForm(
|
||||
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),
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceAround,
|
||||
@@ -49,7 +62,6 @@ class InviteDriverScreen extends StatelessWidget {
|
||||
const SizedBox(height: 20),
|
||||
GetBuilder<InviteController>(builder: (controller) {
|
||||
return SizedBox(
|
||||
// decoration: AppStyle.boxDecoration,
|
||||
height: Get.height * .4,
|
||||
child: controller.driverInvitationData.isEmpty
|
||||
? Center(
|
||||
@@ -61,7 +73,6 @@ class InviteDriverScreen extends StatelessWidget {
|
||||
: ListView.builder(
|
||||
itemCount: controller.driverInvitationData.length,
|
||||
itemBuilder: (context, index) {
|
||||
// Ensure the 'countOfInvitDriver' key exists and is parseable as an int
|
||||
int countOfInvitDriver = 0;
|
||||
if (controller.driverInvitationData[index]
|
||||
.containsKey('countOfInvitDriver')) {
|
||||
@@ -72,25 +83,24 @@ class InviteDriverScreen extends StatelessWidget {
|
||||
0;
|
||||
}
|
||||
|
||||
// Calculate the progress value, ensuring it is between 0 and 1
|
||||
double progressValue = countOfInvitDriver / 100.0;
|
||||
if (progressValue > 1.0) progressValue = 1.0;
|
||||
if (progressValue < 0.0) progressValue = 0.0;
|
||||
|
||||
return Container(
|
||||
margin: const EdgeInsets.symmetric(vertical: 8.0),
|
||||
child: Stack(
|
||||
alignment: AlignmentDirectional.center,
|
||||
children: [
|
||||
InkWell(
|
||||
onTap: () async {
|
||||
controller.onSelectDriverInvitation(index);
|
||||
},
|
||||
child: Container(
|
||||
return InkWell(
|
||||
onTap: () async {
|
||||
controller.onSelectDriverInvitation(index);
|
||||
},
|
||||
child: Container(
|
||||
margin: const EdgeInsets.symmetric(vertical: 8.0),
|
||||
child: Stack(
|
||||
alignment: AlignmentDirectional.center,
|
||||
children: [
|
||||
Container(
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
color: AppColor.accentColor.withOpacity(
|
||||
.5), // Background color of the container
|
||||
color:
|
||||
AppColor.accentColor.withOpacity(.5),
|
||||
),
|
||||
width: Get.width * .85,
|
||||
child: ClipRRect(
|
||||
@@ -104,10 +114,11 @@ class InviteDriverScreen extends StatelessWidget {
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
Text(
|
||||
'${controller.driverInvitationData[index]['invitorName']} ${controller.driverInvitationData[index]['countOfInvitDriver']} / 100 ${'Trip'.tr}'),
|
||||
],
|
||||
Text(
|
||||
'${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/views/widgets/my_scafold.dart';
|
||||
|
||||
import '../../../constant/colors.dart';
|
||||
import '../../../controller/functions/location_controller.dart';
|
||||
import '../../Rate/rate_passenger.dart';
|
||||
import '../../widgets/my_textField.dart';
|
||||
import 'mapDriverWidgets/driver_end_ride_bar.dart';
|
||||
import 'mapDriverWidgets/google_driver_map_page.dart';
|
||||
import 'mapDriverWidgets/google_map_app.dart';
|
||||
@@ -36,6 +38,7 @@ class PassengerLocationMapPage extends StatelessWidget {
|
||||
body: [
|
||||
GoogleDriverMap(locationController: locationController),
|
||||
const PassengerInfoWindow(),
|
||||
CancelWidget(mapDriverController: mapDriverController),
|
||||
driverEndRideBar(),
|
||||
const SosConnect(),
|
||||
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 {
|
||||
const PricesWindow({
|
||||
super.key,
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import 'package:SEFER/constant/box_name.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:get/get.dart';
|
||||
import 'package:google_maps_flutter/google_maps_flutter.dart';
|
||||
@@ -12,6 +13,7 @@ import '../../../../constant/info.dart';
|
||||
import '../../../../constant/style.dart';
|
||||
import '../../../../constant/table_names.dart';
|
||||
import '../../../../controller/functions/location_controller.dart';
|
||||
import '../../../../controller/functions/overlay_permisssion.dart';
|
||||
import '../../../../controller/functions/package_info.dart';
|
||||
import '../../../../controller/home/captin/home_captain_controller.dart';
|
||||
import '../../../../controller/home/captin/order_request_controller.dart';
|
||||
@@ -32,8 +34,10 @@ class HomeCaptain extends StatelessWidget {
|
||||
Widget build(BuildContext context) {
|
||||
Get.put(OrderRequestController());
|
||||
Get.put(HomeCaptainController());
|
||||
Get.put(CaptainWalletController());
|
||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||
checkForUpdate(context);
|
||||
// checkForUpdate(context);
|
||||
getPermissionOverlay();
|
||||
_showFirstTimeOfferNotification(context);
|
||||
});
|
||||
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(),
|
||||
box.read(BoxName.rideStatus) == 'Applied' ||
|
||||
box.read(BoxName.rideStatus) == 'Begin'
|
||||
@@ -401,8 +427,8 @@ void _showFirstTimeOfferNotification(BuildContext context) {
|
||||
),
|
||||
),
|
||||
onPressed: () {
|
||||
Navigator.of(context).pop();
|
||||
_markAsNotFirstTime();
|
||||
Navigator.of(context).pop();
|
||||
},
|
||||
),
|
||||
],
|
||||
@@ -416,7 +442,7 @@ void _showFirstTimeOfferNotification(BuildContext context) {
|
||||
}
|
||||
|
||||
bool _checkIfFirstTime() {
|
||||
if (box.read(BoxName.isFirstTime) == null) {
|
||||
if (box.read(BoxName.isFirstTime).toString() == '') {
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
@@ -424,5 +450,5 @@ bool _checkIfFirstTime() {
|
||||
}
|
||||
|
||||
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:flutter/material.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:SEFER/controller/home/captin/home_captain_controller.dart';
|
||||
import 'package:google_maps_flutter/google_maps_flutter.dart';
|
||||
|
||||
import '../../../../../constant/colors.dart';
|
||||
import '../../../../../print.dart';
|
||||
import '../../../../Rate/ride_calculate_driver.dart';
|
||||
import '../../../../../controller/functions/location_controller.dart';
|
||||
import '../../../../auth/captin/cards/egypt_card_a_i.dart';
|
||||
|
||||
GetBuilder<HomeCaptainController> leftMainMenuCaptainIcons() {
|
||||
return GetBuilder<HomeCaptainController>(
|
||||
@@ -168,16 +169,125 @@ GetBuilder<HomeCaptainController> leftMainMenuCaptainIcons() {
|
||||
// color: AppColor.secondaryColor,
|
||||
// border: Border.all(color: AppColor.blueColor),
|
||||
// borderRadius: BorderRadius.circular(15)),
|
||||
// child: IconButton(
|
||||
// onPressed: () async {
|
||||
// Get.to(() => EgyptCardAI());
|
||||
// },
|
||||
// icon: const Icon(
|
||||
// FontAwesome5.grin_tears,
|
||||
// size: 29,
|
||||
// color: AppColor.blueColor,
|
||||
// ),
|
||||
// ),
|
||||
// child: Builder(builder: (context) {
|
||||
// return IconButton(
|
||||
// onPressed: () async {
|
||||
// // Get.to(() => EgyptCardAI());
|
||||
// // print(box.read(BoxName.myList));
|
||||
//
|
||||
// List<String> d = [
|
||||
// "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/views/widgets/elevated_btn.dart';
|
||||
|
||||
import '../../../../constant/box_name.dart';
|
||||
import '../../../../constant/style.dart';
|
||||
import '../../../../controller/functions/launch.dart';
|
||||
import '../../../../main.dart';
|
||||
|
||||
class PassengerInfoWindow extends StatelessWidget {
|
||||
const PassengerInfoWindow({
|
||||
@@ -57,13 +55,6 @@ class PassengerInfoWindow extends StatelessWidget {
|
||||
mainAxisAlignment:
|
||||
MainAxisAlignment.spaceAround,
|
||||
children: [
|
||||
IconButton(
|
||||
onPressed: () {
|
||||
print(box
|
||||
.read(BoxName.rideStatus));
|
||||
},
|
||||
icon: const Icon(Icons.add),
|
||||
),
|
||||
GestureDetector(
|
||||
onTap: () async {
|
||||
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/views/widgets/mydialoug.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
@@ -23,23 +25,36 @@ class OrderRequestPage extends StatelessWidget {
|
||||
Get.put(OrderRequestController());
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
//TODO show order from start page from sql or api
|
||||
|
||||
final arguments = Get.arguments;
|
||||
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 body = arguments['body'];
|
||||
// final body = arguments['body'];
|
||||
Duration durationToAdd = Duration(seconds: int.parse(myList[4]));
|
||||
int hours = durationToAdd.inHours;
|
||||
int minutes = (durationToAdd.inMinutes % 60).round();
|
||||
orderRequestController.startTimer(myList[6].toString(), body.toString());
|
||||
var coords = myList[0].split(',');
|
||||
var coordDestination = myList[1].split(',');
|
||||
orderRequestController.startTimer(
|
||||
myList[6].toString(),
|
||||
myList[16].toString(),
|
||||
);
|
||||
var cords = myList[0].split(',');
|
||||
var cordDestination = myList[1].split(',');
|
||||
|
||||
// Parse to double
|
||||
double latPassengerLocation = double.parse(coords[0]);
|
||||
double lngPassengerLocation = double.parse(coords[1]);
|
||||
double latPassengerDestination = double.parse(coordDestination[0]);
|
||||
double lngPassengerDestination = double.parse(coordDestination[1]);
|
||||
double latPassengerLocation = double.parse(cords[0]);
|
||||
double lngPassengerLocation = double.parse(cords[1]);
|
||||
double latPassengerDestination = double.parse(cordDestination[0]);
|
||||
double lngPassengerDestination = double.parse(cordDestination[1]);
|
||||
|
||||
List<LatLng> pointsDirection = [
|
||||
LatLng(latPassengerLocation, lngPassengerLocation),
|
||||
@@ -71,378 +86,422 @@ class OrderRequestPage extends StatelessWidget {
|
||||
body: [
|
||||
Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 6),
|
||||
child: ListView(
|
||||
// crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
// SizedBox(height: 200, child: Text(pointsList.toString())),
|
||||
// Text(message.notification!.body.toString()),
|
||||
SizedBox(
|
||||
height: Get.height * .33,
|
||||
child: GoogleMap(
|
||||
initialCameraPosition: CameraPosition(
|
||||
zoom: 12,
|
||||
target: Get.find<HomeCaptainController>().myLocation),
|
||||
cameraTargetBounds: CameraTargetBounds(bounds),
|
||||
myLocationButtonEnabled: true,
|
||||
trafficEnabled: true,
|
||||
buildingsEnabled: true,
|
||||
mapToolbarEnabled: true,
|
||||
myLocationEnabled: true,
|
||||
markers: {
|
||||
Marker(
|
||||
markerId: MarkerId('MyLocation'.tr),
|
||||
position: LatLng(
|
||||
latPassengerLocation, lngPassengerLocation),
|
||||
draggable: true,
|
||||
icon: orderRequestController.startIcon),
|
||||
Marker(
|
||||
markerId: MarkerId('Destination'.tr),
|
||||
position: LatLng(
|
||||
latPassengerDestination, lngPassengerDestination),
|
||||
draggable: true,
|
||||
icon: orderRequestController.endIcon),
|
||||
},
|
||||
polylines: {
|
||||
Polyline(
|
||||
zIndex: 1,
|
||||
consumeTapEvents: true,
|
||||
geodesic: true,
|
||||
endCap: Cap.buttCap,
|
||||
startCap: Cap.buttCap,
|
||||
visible: true,
|
||||
polylineId: const PolylineId('routeOrder'),
|
||||
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),
|
||||
)
|
||||
],
|
||||
child: Container(
|
||||
color: const Color.fromARGB(255, 210, 201, 201),
|
||||
child: ListView(
|
||||
// crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
// SizedBox(height: 200, child: Text(pointsList.toString())),
|
||||
// Text(message.notification!.body.toString()),
|
||||
SizedBox(
|
||||
height: Get.height * .33,
|
||||
child: GoogleMap(
|
||||
initialCameraPosition: CameraPosition(
|
||||
zoom: 12,
|
||||
target: Get.find<HomeCaptainController>().myLocation),
|
||||
cameraTargetBounds: CameraTargetBounds(bounds),
|
||||
myLocationButtonEnabled: true,
|
||||
trafficEnabled: true,
|
||||
buildingsEnabled: true,
|
||||
mapToolbarEnabled: true,
|
||||
myLocationEnabled: true,
|
||||
markers: {
|
||||
Marker(
|
||||
markerId: MarkerId('MyLocation'.tr),
|
||||
position: LatLng(
|
||||
latPassengerLocation, lngPassengerLocation),
|
||||
draggable: true,
|
||||
icon: orderRequestController.startIcon),
|
||||
Marker(
|
||||
markerId: MarkerId('Destination'.tr),
|
||||
position: LatLng(latPassengerDestination,
|
||||
lngPassengerDestination),
|
||||
draggable: true,
|
||||
icon: orderRequestController.endIcon),
|
||||
},
|
||||
polylines: {
|
||||
Polyline(
|
||||
zIndex: 1,
|
||||
consumeTapEvents: true,
|
||||
geodesic: true,
|
||||
endCap: Cap.buttCap,
|
||||
startCap: Cap.buttCap,
|
||||
visible: true,
|
||||
polylineId: const PolylineId('routeOrder'),
|
||||
points: pointsDirection,
|
||||
color: AppColor.primaryColor,
|
||||
width: 2,
|
||||
),
|
||||
},
|
||||
),
|
||||
),
|
||||
),
|
||||
|
||||
Container(
|
||||
decoration: AppStyle.boxDecoration1,
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(8.0),
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(8.0),
|
||||
child: Card(
|
||||
elevation: 3,
|
||||
color: myList[20].toString() == 'haveSteps'
|
||||
? AppColor.greenColor
|
||||
: AppColor.secondaryColor,
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceAround,
|
||||
children: [
|
||||
Text(
|
||||
double.parse(myList[2]).toStringAsFixed(2),
|
||||
style: AppStyle.headTitle2,
|
||||
),
|
||||
AnimatedContainer(
|
||||
duration: const Duration(seconds: 5),
|
||||
curve: Curves.easeInOut,
|
||||
child: AnimatedSize(
|
||||
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('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),
|
||||
curve: Curves.easeInOut,
|
||||
child: myList[31].toString() == 'Comfort'
|
||||
? const Icon(
|
||||
Icons.ac_unit,
|
||||
color: AppColor.blueColor,
|
||||
size: 50,
|
||||
)
|
||||
: const SizedBox(),
|
||||
child: AnimatedSize(
|
||||
duration: const Duration(seconds: 5),
|
||||
curve: Curves.easeInOut,
|
||||
child: myList[31].toString() == 'Comfort'
|
||||
? Column(
|
||||
mainAxisAlignment:
|
||||
MainAxisAlignment.spaceAround,
|
||||
children: [
|
||||
const Icon(
|
||||
Icons.ac_unit,
|
||||
color: AppColor.blueColor,
|
||||
size: 50,
|
||||
),
|
||||
Text(
|
||||
'Air condition Trip'.tr,
|
||||
style: AppStyle.subtitle,
|
||||
),
|
||||
],
|
||||
)
|
||||
: const SizedBox(),
|
||||
),
|
||||
),
|
||||
),
|
||||
Text(
|
||||
myList[31].toString(),
|
||||
style: AppStyle.title
|
||||
.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,
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
Text(
|
||||
myList[31].toString().tr,
|
||||
style: AppStyle.title
|
||||
.copyWith(color: AppColor.greenColor),
|
||||
),
|
||||
],
|
||||
)),
|
||||
),
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(8.0),
|
||||
child: RichText(
|
||||
text: TextSpan(
|
||||
text: 'Passenger name : '
|
||||
.tr, // Changed text to be more generic
|
||||
style: AppStyle.subtitle,
|
||||
children: [
|
||||
TextSpan(
|
||||
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),
|
||||
],
|
||||
const SizedBox(
|
||||
height: 5,
|
||||
),
|
||||
Container(
|
||||
height: Get.height * .15,
|
||||
width: Get.width * .9,
|
||||
decoration: AppStyle.boxDecoration1,
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.symmetric(
|
||||
horizontal: 5, vertical: 1),
|
||||
child: ListView(
|
||||
// mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
// crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
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(
|
||||
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(4),
|
||||
child: Container(
|
||||
color: AppColor.greenColor.withOpacity(.5),
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(8.0),
|
||||
child: RichText(
|
||||
text: TextSpan(
|
||||
text: 'Cost Of Trip IS '.tr,
|
||||
style: AppStyle.title,
|
||||
text: "Passenger name: "
|
||||
.tr, // Changed text to be more generic
|
||||
style: AppStyle.subtitle,
|
||||
children: [
|
||||
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();
|
||||
orderRequestController.changeApplied();
|
||||
await CRUD().postFromDialogue(
|
||||
link: AppLink.addDriverOrder,
|
||||
payload: {
|
||||
'driver_id': myList[6].toString(),
|
||||
// box.read(BoxName.driverID).toString(),
|
||||
'order_id': body.toString(),
|
||||
'status': 'Apply'
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(4),
|
||||
child: Container(
|
||||
color: AppColor.greenColor.withOpacity(.5),
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
|
||||
children: [
|
||||
RichText(
|
||||
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(
|
||||
link: AppLink.updateRideAndCheckIfApplied,
|
||||
payload: {
|
||||
'id': myList[16],
|
||||
'rideTimeStart': DateTime.now().toString(),
|
||||
'status': 'Apply',
|
||||
'driver_id': box.read(BoxName.driverID),
|
||||
});
|
||||
if (res == 'failure') {
|
||||
MyDialog().getDialog(
|
||||
"This ride is already applied by another driver."
|
||||
.tr,
|
||||
'', () {
|
||||
} else {
|
||||
List<String> bodyToPassenger = [
|
||||
myList[6].toString(), //driver id
|
||||
myList[8].toString(), // driver name
|
||||
myList[9].toString(), //token driver
|
||||
];
|
||||
FirebaseMessagesController()
|
||||
.sendNotificationToPassengerToken(
|
||||
'Apply Ride'.tr,
|
||||
'your ride is applied'.tr,
|
||||
// arguments['DriverList'][9].toString(),
|
||||
myList[9].toString(),
|
||||
// box.read(BoxName.tokenDriver).toString(),
|
||||
bodyToPassenger,
|
||||
'start.wav');
|
||||
Get.back();
|
||||
});
|
||||
} else {
|
||||
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(
|
||||
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(
|
||||
'Apply Ride',
|
||||
'your ride is applied'.tr,
|
||||
// arguments['DriverList'][9].toString(),
|
||||
arguments['DriverList'][9].toString(),
|
||||
// box.read(BoxName.tokenDriver).toString(),
|
||||
'Order Under Review'.tr,
|
||||
'${box.read(BoxName.nameDriver)} ${'is reviewing your order. They may need more information or a higher price.'.tr}',
|
||||
myList[9].toString(),
|
||||
bodyToPassenger,
|
||||
'start.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"
|
||||
'notification.wav');
|
||||
|
||||
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 = [
|
||||
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(
|
||||
orderRequestController.refuseOrder(
|
||||
myList[16].toString(),
|
||||
myList[29].toString(),
|
||||
myList[30].toString(),
|
||||
'${DateTime.now().year}-${DateTime.now().month}-${DateTime.now().day}',
|
||||
'${DateTime.now().hour}:${DateTime.now().minute}',
|
||||
myList[2].toString(),
|
||||
myList[7].toString(),
|
||||
'wait',
|
||||
myList[31].toString(),
|
||||
myList[33].toString(),
|
||||
myList[2].toString(),
|
||||
myList[5].toString(),
|
||||
myList[4].toString()); //duration
|
||||
},
|
||||
kolor: AppColor.redColor,
|
||||
),
|
||||
],
|
||||
),
|
||||
)
|
||||
],
|
||||
);
|
||||
orderRequestController.addRideToNotificationDriverString(
|
||||
myList[16].toString(),
|
||||
myList[29].toString(),
|
||||
myList[30].toString(),
|
||||
'${DateTime.now().year}-${DateTime.now().month}-${DateTime.now().day}',
|
||||
'${DateTime.now().hour}:${DateTime.now().minute}',
|
||||
myList[2].toString(),
|
||||
myList[7].toString(),
|
||||
'wait',
|
||||
myList[31].toString(),
|
||||
myList[33].toString(),
|
||||
myList[2].toString(),
|
||||
myList[5].toString(),
|
||||
myList[4].toString()); //duration
|
||||
},
|
||||
kolor: AppColor.redColor,
|
||||
),
|
||||
],
|
||||
),
|
||||
)
|
||||
],
|
||||
),
|
||||
),
|
||||
)
|
||||
],
|
||||
|
||||
@@ -131,7 +131,7 @@ class OrderSpeedRequest extends StatelessWidget {
|
||||
? AppColor.greenColor
|
||||
: AppColor.secondaryColor,
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
mainAxisAlignment: MainAxisAlignment.spaceAround,
|
||||
children: [
|
||||
TextButton.icon(
|
||||
onPressed: () {
|
||||
@@ -145,12 +145,14 @@ class OrderSpeedRequest extends StatelessWidget {
|
||||
'Trip has Steps'.tr,
|
||||
style: AppStyle.title,
|
||||
)
|
||||
: Text('Routs of Trip'.tr,
|
||||
: Text('Payment Method'.tr,
|
||||
style: AppStyle.title)),
|
||||
Container(
|
||||
color: myList[13].toString() == 'true'
|
||||
? AppColor.deepPurpleAccent
|
||||
: AppColor.greenColor,
|
||||
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(
|
||||
@@ -182,90 +184,99 @@ class OrderSpeedRequest extends StatelessWidget {
|
||||
duration: const Duration(seconds: 5),
|
||||
curve: Curves.easeInOut,
|
||||
child: myList[31].toString() == 'Comfort'
|
||||
? const Icon(
|
||||
Icons.ac_unit,
|
||||
color: AppColor.blueColor,
|
||||
size: 50,
|
||||
? Column(
|
||||
mainAxisAlignment:
|
||||
MainAxisAlignment.spaceAround,
|
||||
children: [
|
||||
const Icon(
|
||||
Icons.ac_unit,
|
||||
color: AppColor.blueColor,
|
||||
size: 50,
|
||||
),
|
||||
Text(
|
||||
'Air condition Trip'.tr,
|
||||
style: AppStyle.subtitle,
|
||||
),
|
||||
],
|
||||
)
|
||||
: const SizedBox(),
|
||||
),
|
||||
),
|
||||
Text(
|
||||
myList[31].toString(),
|
||||
myList[31].toString().tr,
|
||||
style: AppStyle.title
|
||||
.copyWith(color: AppColor.greenColor),
|
||||
),
|
||||
],
|
||||
)),
|
||||
),
|
||||
const SizedBox(
|
||||
height: 5,
|
||||
),
|
||||
Container(
|
||||
height: Get.height * .15,
|
||||
width: Get.width * .9,
|
||||
decoration: AppStyle.boxDecoration1,
|
||||
child: ListView(
|
||||
// mainAxisAlignment: MainAxisAlignment.spaceAround,
|
||||
// crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
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,
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
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,
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
child: Padding(
|
||||
padding:
|
||||
const EdgeInsets.symmetric(horizontal: 5, vertical: 1),
|
||||
child: ListView(
|
||||
// mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
// crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
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(
|
||||
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),
|
||||
child: RichText(
|
||||
text: TextSpan(
|
||||
text: 'Passenger name : '
|
||||
text: "Passenger name: "
|
||||
.tr, // Changed text to be more generic
|
||||
style: AppStyle.subtitle,
|
||||
children: [
|
||||
@@ -283,21 +294,42 @@ class OrderSpeedRequest extends StatelessWidget {
|
||||
),
|
||||
),
|
||||
),
|
||||
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(4),
|
||||
child: Container(
|
||||
color: AppColor.deepPurpleAccent,
|
||||
child: RichText(
|
||||
text: TextSpan(
|
||||
text: 'Cost Of Trip IS '.tr,
|
||||
style: AppStyle.title,
|
||||
children: [
|
||||
TextSpan(text: myList[26], style: AppStyle.headTitle2),
|
||||
],
|
||||
),
|
||||
color: AppColor.greenColor.withOpacity(.5),
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
|
||||
children: [
|
||||
RichText(
|
||||
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(
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user