diff --git a/.gitignore b/.gitignore
index 0de4d0a16..a259a6db2 100644
--- a/.gitignore
+++ b/.gitignore
@@ -56,5 +56,7 @@ app.*.map.json
/firebase.json
/.fvm
/.tmp
+/.worktrees/
/caches
-/.fvmrc
\ No newline at end of file
+/.fvmrc
+/artifacts/
diff --git a/android/app/build.gradle b/android/app/build.gradle
index 3da777f22..2ccc998f8 100644
--- a/android/app/build.gradle
+++ b/android/app/build.gradle
@@ -1,3 +1,10 @@
+plugins {
+ id "com.android.application"
+ id "org.jetbrains.kotlin.android"
+ id "dev.flutter.flutter-gradle-plugin"
+ id "com.google.gms.google-services"
+}
+
def localProperties = new Properties()
def localPropertiesFile = rootProject.file('local.properties')
if (localPropertiesFile.exists()) {
@@ -6,11 +13,6 @@ if (localPropertiesFile.exists()) {
}
}
-def flutterRoot = localProperties.getProperty('flutter.sdk')
-if (flutterRoot == null) {
- throw new Exception("Flutter SDK not found. Define location with flutter.sdk in the local.properties file.")
-}
-
def flutterVersionCode = localProperties.getProperty('flutter.versionCode')
if (flutterVersionCode == null) {
flutterVersionCode = '4'
@@ -21,11 +23,6 @@ if (flutterVersionName == null) {
flutterVersionName = '0.2.1'
}
-apply plugin: 'com.android.application'
-apply plugin: 'com.google.gms.google-services'
-apply plugin: 'kotlin-android'
-apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle"
-
def keystoreProperties = new Properties()
def keystorePropertiesFile = rootProject.file('key.properties')
if (keystorePropertiesFile.exists()) {
@@ -33,7 +30,8 @@ if (keystorePropertiesFile.exists()) {
}
android {
- compileSdkVersion 34
+ namespace "com.ccextractor.ultimate_alarm_clock"
+ compileSdk 36
ndkVersion flutter.ndkVersion
defaultConfig {
@@ -41,8 +39,8 @@ android {
// For more information, see: https://docs.flutter.dev/deployment/android#reviewing-the-gradle-build-configuration.
applicationId "com.ccextractor.ultimate_alarm_clock"
- minSdkVersion 24
- targetSdkVersion 34 // Updated to latest targetSdkVersion
+ minSdk 24
+ targetSdk 34
versionCode flutterVersionCode.toInteger()
versionName flutterVersionName
multiDexEnabled true
@@ -51,12 +49,12 @@ android {
compileOptions {
- sourceCompatibility JavaVersion.VERSION_17 // Updated to Java 17
+ sourceCompatibility JavaVersion.VERSION_17
targetCompatibility JavaVersion.VERSION_17
}
kotlinOptions {
- jvmTarget = '17' // Updated to Java 17
+ jvmTarget = '17'
}
sourceSets {
@@ -93,12 +91,10 @@ flutter {
}
dependencies {
- implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
- implementation "org.jetbrains.kotlin:kotlin-stdlib:1.7.10"
- implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.7.10"
- implementation platform('com.google.firebase:firebase-bom:32.0.0') // Updated Firebase BOM to latest
+ implementation platform('com.google.firebase:firebase-bom:32.0.0')
implementation 'com.google.firebase:protolite-well-known-types:18.0.0'
implementation("com.android.volley:volley:1.2.1")
implementation("com.google.code.gson:gson:2.10.1")
implementation("androidx.multidex:multidex:2.0.1")
-}
\ No newline at end of file
+ testImplementation "junit:junit:4.13.2"
+}
diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml
index 64507121e..a1a765a1d 100644
--- a/android/app/src/main/AndroidManifest.xml
+++ b/android/app/src/main/AndroidManifest.xml
@@ -1,7 +1,6 @@
+ xmlns:tools="http://schemas.android.com/tools">
@@ -45,8 +44,7 @@
android:name="${applicationName}"
android:allowBackup="true"
android:icon="@mipmap/launcher_icon"
- android:label="Ultimate Alarm Clock"
- tools:replace="android:label">
+ android:label="Ultimate Alarm Clock">
+
+
-
\ No newline at end of file
+
diff --git a/android/app/src/main/kotlin/com/ccextractor/ultimate_alarm_clock/ultimate_alarm_clock/AlarmScheduleContract.kt b/android/app/src/main/kotlin/com/ccextractor/ultimate_alarm_clock/ultimate_alarm_clock/AlarmScheduleContract.kt
new file mode 100644
index 000000000..614f7eedb
--- /dev/null
+++ b/android/app/src/main/kotlin/com/ccextractor/ultimate_alarm_clock/ultimate_alarm_clock/AlarmScheduleContract.kt
@@ -0,0 +1,371 @@
+package com.ccextractor.ultimate_alarm_clock
+
+import android.annotation.SuppressLint
+import android.app.AlarmManager
+import android.app.PendingIntent
+import android.content.Context
+import android.content.Intent
+import android.os.Build
+import android.os.UserManager
+import android.util.Log
+import java.time.Instant
+import java.time.LocalDate
+import java.time.LocalDateTime
+import java.time.LocalTime
+import java.time.ZoneId
+import java.time.format.DateTimeFormatter
+import java.time.format.DateTimeParseException
+
+data class ScheduledAlarmPayload(
+ val triggerAtMs: Long,
+ val activityMonitor: Int,
+ val locationMonitor: Int,
+ val location: String,
+ val isWeather: Int,
+ val weatherTypes: String
+)
+
+object AlarmScheduleStore {
+ private const val PREFS_NAME = "ultimate_alarm_clock_schedule"
+ private const val KEY_TRIGGER_AT_MS = "trigger_at_ms"
+ private const val KEY_ACTIVITY_MONITOR = "activity_monitor"
+ private const val KEY_LOCATION_MONITOR = "location_monitor"
+ private const val KEY_LOCATION = "location"
+ private const val KEY_IS_WEATHER = "is_weather"
+ private const val KEY_WEATHER_TYPES = "weather_types"
+
+ private fun deviceProtectedContext(context: Context): Context {
+ return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
+ context.createDeviceProtectedStorageContext()
+ } else {
+ context
+ }
+ }
+
+ fun canAccessCredentialEncryptedStorage(context: Context): Boolean {
+ if (Build.VERSION.SDK_INT < Build.VERSION_CODES.N) {
+ return true
+ }
+
+ val userManager = context.getSystemService(UserManager::class.java)
+ return userManager?.isUserUnlocked != false
+ }
+
+ private fun writePreferences(context: Context, payload: ScheduledAlarmPayload) {
+ context.getSharedPreferences(PREFS_NAME, Context.MODE_PRIVATE)
+ .edit()
+ .putLong(KEY_TRIGGER_AT_MS, payload.triggerAtMs)
+ .putInt(KEY_ACTIVITY_MONITOR, payload.activityMonitor)
+ .putInt(KEY_LOCATION_MONITOR, payload.locationMonitor)
+ .putString(KEY_LOCATION, payload.location)
+ .putInt(KEY_IS_WEATHER, payload.isWeather)
+ .putString(KEY_WEATHER_TYPES, payload.weatherTypes)
+ .apply()
+ }
+
+ private fun readPreferences(context: Context): ScheduledAlarmPayload? {
+ val preferences = context.getSharedPreferences(PREFS_NAME, Context.MODE_PRIVATE)
+ if (!preferences.contains(KEY_TRIGGER_AT_MS)) {
+ return null
+ }
+
+ return ScheduledAlarmPayload(
+ triggerAtMs = preferences.getLong(KEY_TRIGGER_AT_MS, -1L),
+ activityMonitor = preferences.getInt(KEY_ACTIVITY_MONITOR, 0),
+ locationMonitor = preferences.getInt(KEY_LOCATION_MONITOR, 0),
+ location = preferences.getString(KEY_LOCATION, "") ?: "",
+ isWeather = preferences.getInt(KEY_IS_WEATHER, 0),
+ weatherTypes = preferences.getString(KEY_WEATHER_TYPES, "[]") ?: "[]"
+ )
+ }
+
+ fun save(context: Context, payload: ScheduledAlarmPayload) {
+ val protectedContext = deviceProtectedContext(context)
+ writePreferences(protectedContext, payload)
+ if (protectedContext !== context && canAccessCredentialEncryptedStorage(context)) {
+ writePreferences(context, payload)
+ }
+ }
+
+ fun load(context: Context): ScheduledAlarmPayload? {
+ val protectedContext = deviceProtectedContext(context)
+ val protectedPayload = readPreferences(protectedContext)
+ if (protectedPayload != null) {
+ return protectedPayload
+ }
+
+ val legacyPayload = if (
+ protectedContext !== context &&
+ canAccessCredentialEncryptedStorage(context)
+ ) {
+ readPreferences(context)
+ } else {
+ null
+ }
+
+ if (legacyPayload != null) {
+ writePreferences(protectedContext, legacyPayload)
+ }
+
+ return legacyPayload
+ }
+
+ fun clear(context: Context) {
+ val protectedContext = deviceProtectedContext(context)
+ protectedContext.getSharedPreferences(PREFS_NAME, Context.MODE_PRIVATE)
+ .edit()
+ .clear()
+ .apply()
+ if (protectedContext !== context && canAccessCredentialEncryptedStorage(context)) {
+ context.getSharedPreferences(PREFS_NAME, Context.MODE_PRIVATE)
+ .edit()
+ .clear()
+ .apply()
+ }
+ }
+}
+
+object BootRestorePlanner {
+ fun chooseAlarmToRestore(
+ persistedAlarm: ScheduledAlarmPayload?,
+ localAlarm: ScheduledAlarmPayload?,
+ nowMs: Long,
+ allowLocalFallback: Boolean,
+ ): ScheduledAlarmPayload? {
+ val validLocal = if (allowLocalFallback) {
+ localAlarm?.takeIf { it.triggerAtMs > nowMs }
+ } else {
+ null
+ }
+ if (validLocal != null) {
+ return validLocal
+ }
+
+ val validPersisted = persistedAlarm?.takeIf { it.triggerAtMs > nowMs }
+ if (validPersisted != null) {
+ return validPersisted
+ }
+ return null
+ }
+}
+
+object LocalAlarmScheduleResolver {
+ private val timeFormatter: DateTimeFormatter = DateTimeFormatter.ofPattern("H:mm")
+
+ fun nextTriggerAtMs(
+ alarm: AlarmModel,
+ nowMs: Long,
+ zoneId: ZoneId = ZoneId.systemDefault(),
+ ): Long? {
+ val referenceTime = Instant.ofEpochMilli(nowMs).atZone(zoneId).toLocalDateTime()
+ val alarmTime = parseTime(alarm.alarmTime) ?: return null
+
+ if (alarm.ringOn == 1) {
+ val alarmDate = parseDate(alarm.alarmDate) ?: return null
+ return toEpochMillis(LocalDateTime.of(alarmDate, alarmTime), zoneId)
+ ?.takeIf { it > nowMs }
+ }
+
+ if (alarm.days.any { it == '1' }) {
+ for (offset in 0..7) {
+ val candidateDate = referenceTime.toLocalDate().plusDays(offset.toLong())
+ val candidate = LocalDateTime.of(candidateDate, alarmTime)
+ val dayIndex = candidate.dayOfWeek.value % 7
+ if (alarm.days.getOrNull(dayIndex) == '1' && candidate.isAfter(referenceTime)) {
+ return toEpochMillis(candidate, zoneId)
+ }
+ }
+
+ return null
+ }
+
+ val today = LocalDateTime.of(referenceTime.toLocalDate(), alarmTime)
+ val nextCandidate = if (today.isAfter(referenceTime)) {
+ today
+ } else {
+ today.plusDays(1)
+ }
+ return toEpochMillis(nextCandidate, zoneId)
+ }
+
+ private fun parseTime(rawTime: String): LocalTime? {
+ return try {
+ LocalTime.parse(rawTime, timeFormatter)
+ } catch (_: DateTimeParseException) {
+ null
+ }
+ }
+
+ private fun parseDate(rawDate: String): LocalDate? {
+ return try {
+ LocalDate.parse(rawDate.trim())
+ } catch (_: DateTimeParseException) {
+ null
+ }
+ }
+
+ private fun toEpochMillis(dateTime: LocalDateTime, zoneId: ZoneId): Long? {
+ return try {
+ dateTime.atZone(zoneId).toInstant().toEpochMilli()
+ } catch (_: DateTimeParseException) {
+ null
+ }
+ }
+}
+
+object AlarmScheduler {
+ private const val ALARM_REQUEST_CODE = 0
+ private const val LEGACY_BOOT_ALARM_REQUEST_CODE = 1
+ private const val ACTIVITY_REQUEST_CODE = 4
+ private const val LOCATION_REQUEST_CODE = 5
+ private const val WEATHER_REQUEST_CODE = 6
+
+ @SuppressLint("ScheduleExactAlarm")
+ fun schedule(
+ context: Context,
+ payload: ScheduledAlarmPayload,
+ allowCredentialEncryptedAccess: Boolean = AlarmScheduleStore.canAccessCredentialEncryptedStorage(context),
+ ) {
+ val alarmManager = context.getSystemService(Context.ALARM_SERVICE) as AlarmManager
+ val now = System.currentTimeMillis()
+ val triggerAtMs = payload.triggerAtMs
+
+ if (triggerAtMs <= now) {
+ Log.d("AlarmScheduler", "Skipping stale alarm payload at $triggerAtMs")
+ clear(context)
+ return
+ }
+
+ val intent = Intent(context, AlarmReceiver::class.java)
+ val pendingIntent = PendingIntent.getBroadcast(
+ context,
+ ALARM_REQUEST_CODE,
+ intent,
+ PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_MUTABLE
+ )
+ val activityCheckIntent = Intent(context, ScreenMonitorService::class.java)
+ val pendingActivityCheckIntent = PendingIntent.getService(
+ context,
+ ACTIVITY_REQUEST_CODE,
+ activityCheckIntent,
+ PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_MUTABLE
+ )
+
+ val tenMinutesInMilliseconds = 600000L
+ val preTriggerTime = maxOf(now, triggerAtMs - tenMinutesInMilliseconds)
+
+ if (!allowCredentialEncryptedAccess) {
+ val alarmClockInfo = AlarmManager.AlarmClockInfo(triggerAtMs, pendingIntent)
+ alarmManager.setAlarmClock(alarmClockInfo, pendingIntent)
+ return
+ }
+
+ if (payload.activityMonitor == 1) {
+ val alarmClockInfo = AlarmManager.AlarmClockInfo(preTriggerTime, pendingIntent)
+ alarmManager.setAlarmClock(alarmClockInfo, pendingActivityCheckIntent)
+ } else {
+ val sharedPreferences =
+ context.getSharedPreferences("FlutterSharedPreferences", Context.MODE_PRIVATE)
+ sharedPreferences.edit()
+ .putLong("flutter.is_screen_off", 0L)
+ .putLong("flutter.is_screen_on", 0L)
+ .apply()
+ }
+
+ if (payload.locationMonitor == 1) {
+ val sharedPreferences =
+ context.getSharedPreferences("FlutterSharedPreferences", Context.MODE_PRIVATE)
+ sharedPreferences.edit()
+ .putString("flutter.set_location", payload.location)
+ .putInt("flutter.is_location_on", 1)
+ .apply()
+ Log.d("location", payload.location)
+
+ val locationAlarmIntent = Intent(context, LocationFetcherService::class.java)
+ val pendingLocationAlarmIntent = PendingIntent.getService(
+ context,
+ LOCATION_REQUEST_CODE,
+ locationAlarmIntent,
+ PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_MUTABLE
+ )
+ val alarmClockInfo = AlarmManager.AlarmClockInfo(maxOf(now, triggerAtMs - 10000), pendingIntent)
+ alarmManager.setAlarmClock(alarmClockInfo, pendingLocationAlarmIntent)
+ } else if (payload.isWeather == 1) {
+ val sharedPreferences =
+ context.getSharedPreferences("FlutterSharedPreferences", Context.MODE_PRIVATE)
+ val weatherConditions = getWeatherConditions(payload.weatherTypes)
+ sharedPreferences.edit()
+ .putString("flutter.weatherTypes", weatherConditions)
+ .apply()
+ Log.d("we", weatherConditions)
+
+ val weatherAlarmIntent = Intent(context, WeatherFetcherService::class.java)
+ val pendingWeatherAlarmIntent = PendingIntent.getService(
+ context,
+ WEATHER_REQUEST_CODE,
+ weatherAlarmIntent,
+ PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_MUTABLE
+ )
+ val alarmClockInfo = AlarmManager.AlarmClockInfo(maxOf(now, triggerAtMs - 10000), pendingIntent)
+ alarmManager.setAlarmClock(alarmClockInfo, pendingWeatherAlarmIntent)
+ } else {
+ val alarmClockInfo = AlarmManager.AlarmClockInfo(triggerAtMs, pendingIntent)
+ alarmManager.setAlarmClock(alarmClockInfo, pendingIntent)
+ }
+ }
+
+ fun clear(context: Context) {
+ val alarmManager = context.getSystemService(Context.ALARM_SERVICE) as AlarmManager
+
+ val alarmIntent = Intent(context, AlarmReceiver::class.java)
+ val pendingIntent = PendingIntent.getBroadcast(
+ context,
+ ALARM_REQUEST_CODE,
+ alarmIntent,
+ PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_MUTABLE
+ )
+ alarmManager.cancel(pendingIntent)
+ pendingIntent.cancel()
+
+ val legacyBootPendingIntent = PendingIntent.getBroadcast(
+ context,
+ LEGACY_BOOT_ALARM_REQUEST_CODE,
+ alarmIntent,
+ PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_MUTABLE
+ )
+ alarmManager.cancel(legacyBootPendingIntent)
+ legacyBootPendingIntent.cancel()
+
+ val activityIntent = Intent(context, ScreenMonitorService::class.java)
+ val pendingActivityIntent = PendingIntent.getService(
+ context,
+ ACTIVITY_REQUEST_CODE,
+ activityIntent,
+ PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_MUTABLE
+ )
+ alarmManager.cancel(pendingActivityIntent)
+ pendingActivityIntent.cancel()
+
+ val locationIntent = Intent(context, LocationFetcherService::class.java)
+ val pendingLocationIntent = PendingIntent.getService(
+ context,
+ LOCATION_REQUEST_CODE,
+ locationIntent,
+ PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_MUTABLE
+ )
+ alarmManager.cancel(pendingLocationIntent)
+ pendingLocationIntent.cancel()
+
+ val weatherIntent = Intent(context, WeatherFetcherService::class.java)
+ val pendingWeatherIntent = PendingIntent.getService(
+ context,
+ WEATHER_REQUEST_CODE,
+ weatherIntent,
+ PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_MUTABLE
+ )
+ alarmManager.cancel(pendingWeatherIntent)
+ pendingWeatherIntent.cancel()
+
+ AlarmScheduleStore.clear(context)
+ }
+}
diff --git a/android/app/src/main/kotlin/com/ccextractor/ultimate_alarm_clock/ultimate_alarm_clock/BootReceiver.kt b/android/app/src/main/kotlin/com/ccextractor/ultimate_alarm_clock/ultimate_alarm_clock/BootReceiver.kt
index e7d1ffcba..f2c310744 100644
--- a/android/app/src/main/kotlin/com/ccextractor/ultimate_alarm_clock/ultimate_alarm_clock/BootReceiver.kt
+++ b/android/app/src/main/kotlin/com/ccextractor/ultimate_alarm_clock/ultimate_alarm_clock/BootReceiver.kt
@@ -1,116 +1,138 @@
package com.ccextractor.ultimate_alarm_clock
-import android.annotation.SuppressLint
-import android.app.AlarmManager
-import android.app.PendingIntent
import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
-import android.os.SystemClock
+import android.app.PendingIntent
import android.app.NotificationChannel
import android.app.NotificationManager
import android.os.Build
-import android.os.CountDownTimer
+import android.util.Log
import androidx.core.app.NotificationCompat
import com.ccextractor.ultimate_alarm_clock.getLatestTimer
class BootReceiver : BroadcastReceiver() {
+ companion object {
+ private const val TAG = "BootReceiver"
+ }
override fun onReceive(context: Context, intent: Intent) {
+ val action = intent.action ?: return
+ if (
+ action != Intent.ACTION_BOOT_COMPLETED &&
+ action != Intent.ACTION_USER_UNLOCKED &&
+ action != Intent.ACTION_LOCKED_BOOT_COMPLETED
+ ) {
+ return
+ }
- if (intent.action == Intent.ACTION_BOOT_COMPLETED) {
- val sharedPreferences = context.getSharedPreferences("FlutterSharedPreferences", Context.MODE_PRIVATE)
- val profile = sharedPreferences.getString("flutter.profile", "Default")
-
- val dbHelper = DatabaseHelper(context)
- val logdbHelper = LogDatabaseHelper(context)
- val db = dbHelper.readableDatabase
- val ringTime = getLatestAlarm(db, true, profile?:"Default", context)
- db.close()
- if (ringTime != null) {
- scheduleAlarm(ringTime["interval"]!! as Long, context, ringTime["isActivity"]!!)
- }
-
- val timerdbhelper = TimerDatabaseHelper(context)
- val timerdb = timerdbhelper.readableDatabase
- val time = getLatestTimer(timerdb)
- timerdb.close()
- var notificationManager =
- context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
- val commonTimer = CommonTimerManager.getCommonTimer(object : TimerListener {
- override fun onTick(millisUntilFinished: Long) {
- println(millisUntilFinished)
- showTimerNotification(millisUntilFinished, "Timer", context)
-
- }
-
- override fun onFinish() {
- notificationManager.cancel(1)
- }
- })
- createNotificationChannel(context)
-
+ Log.i(TAG, "Handling action=$action")
+ val allowLocalFallback = action != Intent.ACTION_LOCKED_BOOT_COMPLETED
+ restoreAlarms(context, allowLocalFallback)
+ if (action != Intent.ACTION_LOCKED_BOOT_COMPLETED) {
+ restoreTimers(context)
+ }
+ }
+ private fun restoreAlarms(context: Context, allowLocalFallback: Boolean) {
+ val now = System.currentTimeMillis()
+ val persistedAlarm = AlarmScheduleStore.load(context)
+ val localAlarm = if (allowLocalFallback) restoreAlarmFromLocalState(context) else null
- if (time != null) {
+ val reconciledAlarm = BootRestorePlanner.chooseAlarmToRestore(
+ persistedAlarm = persistedAlarm,
+ localAlarm = localAlarm,
+ nowMs = now,
+ allowLocalFallback = allowLocalFallback
+ )
+ Log.i(
+ TAG,
+ "restoreAlarms allowLocalFallback=$allowLocalFallback persisted=${persistedAlarm?.triggerAtMs} local=${localAlarm?.triggerAtMs} reconciled=${reconciledAlarm?.triggerAtMs}"
+ )
+ if (reconciledAlarm != null) {
+ AlarmScheduleStore.save(context, reconciledAlarm)
+ AlarmScheduler.schedule(
+ context,
+ reconciledAlarm,
+ allowCredentialEncryptedAccess = AlarmScheduleStore.canAccessCredentialEncryptedStorage(context)
+ )
+ Log.i(TAG, "Scheduled restored alarm at ${reconciledAlarm.triggerAtMs}")
+ } else {
+ AlarmScheduleStore.clear(context)
+ Log.i(TAG, "No valid alarm found to restore")
+ }
+ }
- // Start or stop the timer based on your requirements
- commonTimer.startTimer(time.second)
+ private fun restoreTimers(context: Context) {
+ val timerdbhelper = TimerDatabaseHelper(context)
+ val timerdb = timerdbhelper.readableDatabase
+ val time = getLatestTimer(timerdb)
+ timerdb.close()
+ val notificationManager =
+ context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
+ val commonTimer = CommonTimerManager.getCommonTimer(object : TimerListener {
+ override fun onTick(millisUntilFinished: Long) {
+ println(millisUntilFinished)
+ showTimerNotification(millisUntilFinished, "Timer", context)
}
+ override fun onFinish() {
+ notificationManager.cancel(1)
+ }
+ })
+ createNotificationChannel(context)
+ if (time != null) {
+ commonTimer.startTimer(time.second)
+ Log.i(TAG, "Restored timer for ${time.second} seconds")
+ }
+ else {
+ Log.i(TAG, "No timer found to restore")
}
}
- @SuppressLint("ScheduleExactAlarm")
- fun scheduleAlarm(milliSeconds: Long, context: Context, activityMonitor: Any) {
- val alarmManager = context.getSystemService(Context.ALARM_SERVICE) as AlarmManager
- val intent = Intent(context, AlarmReceiver::class.java)
- val pendingIntent = PendingIntent.getBroadcast(
- context,
- 1,
- intent,
- PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_MUTABLE
+ private fun restoreAlarmFromLocalState(context: Context): ScheduledAlarmPayload? {
+ val sharedPreferences =
+ context.getSharedPreferences("FlutterSharedPreferences", Context.MODE_PRIVATE)
+ val profile = sharedPreferences.getString("flutter.profile", "Default")
+ val dbHelper = DatabaseHelper(context)
+ val db = dbHelper.readableDatabase
+ val cursor = db.rawQuery(
+ """
+ SELECT * FROM alarms
+ WHERE isEnabled = 1
+ AND (profile = ? OR ringOn = 1)
+ """.trimIndent(),
+ arrayOf(profile ?: "Default")
)
- val activityCheckIntent = Intent(context, ScreenMonitorService::class.java)
- val pendingActivityCheckIntent = PendingIntent.getService(
- context,
- 4,
- activityCheckIntent,
- PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_MUTABLE
- )
- // Schedule the alarm
- val tenMinutesInMilliseconds = 600000L
- val preTriggerTime =
- System.currentTimeMillis() + (milliSeconds - tenMinutesInMilliseconds)
-
- // Schedule the alarm
- val triggerTime = System.currentTimeMillis() + milliSeconds
-
- if (activityMonitor == 1) {
- val alarmClockInfo = AlarmManager.AlarmClockInfo(preTriggerTime, pendingIntent)
- alarmManager.setAlarmClock(
- alarmClockInfo,
- pendingActivityCheckIntent
- )
- } else {
- val sharedPreferences =
- context.getSharedPreferences("FlutterSharedPreferences", Context.MODE_PRIVATE)
- val editor = sharedPreferences.edit()
- editor.putLong("flutter.is_screen_off", 0L)
- editor.apply()
- editor.putLong("flutter.is_screen_on", 0L)
- editor.apply()
- }
- val clockInfo = AlarmManager.AlarmClockInfo(triggerTime, pendingIntent)
- alarmManager.setAlarmClock(clockInfo, pendingIntent)
+ return try {
+ var bestPayload: ScheduledAlarmPayload? = null
+ val now = System.currentTimeMillis()
+ while (cursor.moveToNext()) {
+ val alarm = AlarmModel.fromCursor(cursor)
+ val triggerAtMs = LocalAlarmScheduleResolver.nextTriggerAtMs(alarm, now) ?: continue
+ val candidate = ScheduledAlarmPayload(
+ triggerAtMs = triggerAtMs,
+ activityMonitor = alarm.activityMonitor,
+ locationMonitor = alarm.isLocationEnabled,
+ location = alarm.location,
+ isWeather = alarm.isWeatherEnabled,
+ weatherTypes = alarm.weatherTypes
+ )
+ if (bestPayload == null || candidate.triggerAtMs < bestPayload.triggerAtMs) {
+ bestPayload = candidate
+ }
+ }
+ bestPayload
+ } finally {
+ cursor.close()
+ db.close()
+ }
}
-
-
private fun createNotificationChannel(context: Context) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
@@ -156,4 +178,4 @@ class BootReceiver : BroadcastReceiver() {
String.format("%02d:%02d", minutes, seconds)
}
}
-}
\ No newline at end of file
+}
diff --git a/android/app/src/main/kotlin/com/ccextractor/ultimate_alarm_clock/ultimate_alarm_clock/MainActivity.kt b/android/app/src/main/kotlin/com/ccextractor/ultimate_alarm_clock/ultimate_alarm_clock/MainActivity.kt
index f97f97f76..589682adb 100644
--- a/android/app/src/main/kotlin/com/ccextractor/ultimate_alarm_clock/ultimate_alarm_clock/MainActivity.kt
+++ b/android/app/src/main/kotlin/com/ccextractor/ultimate_alarm_clock/ultimate_alarm_clock/MainActivity.kt
@@ -1,10 +1,7 @@
package com.ccextractor.ultimate_alarm_clock
-import android.annotation.SuppressLint
import android.app.ActivityManager
-import android.app.AlarmManager
import android.app.NotificationManager
-import android.app.PendingIntent
import android.content.Context
import android.content.Intent
import android.content.IntentFilter
@@ -17,13 +14,13 @@ import android.media.RingtoneManager
import android.net.Uri
import android.os.Build
import android.os.Bundle
-import android.os.SystemClock
import android.provider.Settings
import android.util.Log
import android.view.WindowManager
import androidx.annotation.NonNull
import io.flutter.embedding.android.FlutterActivity
import io.flutter.embedding.engine.FlutterEngine
+import io.flutter.plugin.common.MethodCall
import io.flutter.plugin.common.MethodChannel
@@ -135,28 +132,13 @@ class MainActivity : FlutterActivity() {
}
methodChannel1.setMethodCallHandler { call, result ->
if (call.method == "scheduleAlarm") {
- println("FLUTTER CALLED SCHEDULE")
- val dbHelper = DatabaseHelper(context)
- val db = dbHelper.readableDatabase
- val sharedPreferences =
- context.getSharedPreferences("FlutterSharedPreferences", Context.MODE_PRIVATE)
- val profile = sharedPreferences.getString("flutter.profile", "Default")
- val ringTime = getLatestAlarm(db, true, profile ?: "Default", context)
- Log.d("yay yay", "yay ${ringTime ?: "null"}")
- if (ringTime != null) {
- android.util.Log.d("yay", "yay ${ringTime["interval"]}")
- Log.d("yay", "yay ${ringTime["isLocation"]}")
- scheduleAlarm(
- ringTime["interval"]!! as Long,
- ringTime["isActivity"]!! as Int,
- ringTime["isLocation"]!! as Int,
- ringTime["location"]!! as String,
- ringTime["isWeather"]!! as Int,
- ringTime["weatherTypes"]!! as String
+ if (!scheduleAlarmFromPayload(call)) {
+ result.error(
+ "INVALID_ARGUMENT",
+ "scheduleAlarm requires a payload generated by Flutter",
+ null
)
- } else {
- println("FLUTTER CALLED CANCEL ALARMS")
- cancelAllScheduledAlarms()
+ return@setMethodCallHandler
}
result.success(null)
} else if (call.method == "cancelAllScheduledAlarms") {
@@ -181,6 +163,29 @@ class MainActivity : FlutterActivity() {
}
}
+ private fun scheduleAlarmFromPayload(call: MethodCall): Boolean {
+ val triggerAtMs =
+ call.argument("triggerAtMs")?.toLong()
+ ?: return false
+ val activityMonitor = call.argument("activityMonitor") ?: return false
+ val locationMonitor = call.argument("locationMonitor") ?: 0
+ val location = call.argument("location") ?: ""
+ val isWeather = call.argument("isWeather") ?: 0
+ val weatherTypes = call.argument("weatherTypes") ?: "[]"
+
+ val payload = ScheduledAlarmPayload(
+ triggerAtMs,
+ activityMonitor,
+ locationMonitor,
+ location,
+ isWeather,
+ weatherTypes
+ )
+ AlarmScheduleStore.save(this, payload)
+ AlarmScheduler.schedule(this, payload)
+ return true
+ }
+
fun bringAppToForeground(context: Context) {
val activityManager = context.getSystemService(Context.ACTIVITY_SERVICE) as ActivityManager?
@@ -199,123 +204,8 @@ class MainActivity : FlutterActivity() {
}
- @SuppressLint("ScheduleExactAlarm")
- private fun scheduleAlarm(
- milliSeconds: Long,
- activityMonitor: Int,
- locationMonitor: Int,
- setLocation: String,
- isWeather: Int,
- weatherTypes: String
- ) {
-
- val alarmManager = getSystemService(Context.ALARM_SERVICE) as AlarmManager
- val intent = Intent(this, AlarmReceiver::class.java)
- val pendingIntent = PendingIntent.getBroadcast(
- this,
- 0,
- intent,
- PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_MUTABLE
- )
- val activityCheckIntent = Intent(this, ScreenMonitorService::class.java)
- val pendingActivityCheckIntent = PendingIntent.getService(
- this,
- 4,
- activityCheckIntent,
- PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_MUTABLE
- )
- // Schedule the alarm
- val tenMinutesInMilliseconds = 600000L
- val preTriggerTime =
- System.currentTimeMillis() + (milliSeconds - tenMinutesInMilliseconds)
- val triggerTime = System.currentTimeMillis() + milliSeconds
- if (activityMonitor == 1) {
- val alarmClockInfo = AlarmManager.AlarmClockInfo(preTriggerTime, pendingIntent)
- alarmManager.setAlarmClock(
- alarmClockInfo,
- pendingActivityCheckIntent
- )
- } else {
- val sharedPreferences =
- getSharedPreferences("FlutterSharedPreferences", Context.MODE_PRIVATE)
- val editor = sharedPreferences.edit()
- editor.putLong("flutter.is_screen_off", 0L)
- editor.apply()
- editor.putLong("flutter.is_screen_on", 0L)
- editor.apply()
- }
- if (locationMonitor == 1) {
- val sharedPreferences =
- getSharedPreferences("FlutterSharedPreferences", Context.MODE_PRIVATE)
- val editor = sharedPreferences.edit()
- editor.putString("flutter.set_location", setLocation)
- Log.d("location", setLocation)
- editor.apply()
- editor.putInt("flutter.is_location_on", 1)
- editor.apply()
- val locationAlarmIntent = Intent(this, LocationFetcherService::class.java)
- val pendingLocationAlarmIntent = PendingIntent.getService(
- this,
- 5,
- locationAlarmIntent,
- PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_MUTABLE
- )
- val alarmClockInfo = AlarmManager.AlarmClockInfo(triggerTime - 10000, pendingIntent)
- alarmManager.setAlarmClock(
- alarmClockInfo,
- pendingLocationAlarmIntent
- )
- } else if (isWeather == 1) {
- val sharedPreferences =
- getSharedPreferences("FlutterSharedPreferences", Context.MODE_PRIVATE)
- val editor = sharedPreferences.edit()
- editor.putString("flutter.weatherTypes", getWeatherConditions(weatherTypes))
- Log.d("we", getWeatherConditions(weatherTypes))
- editor.apply()
- val weatherAlarmIntent = Intent(this, WeatherFetcherService::class.java)
- val pendingWeatherAlarmIntent = PendingIntent.getService(
- this,
- 6,
- weatherAlarmIntent,
- PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_MUTABLE
- )
- val alarmClockInfo = AlarmManager.AlarmClockInfo(triggerTime - 10000, pendingIntent)
- alarmManager.setAlarmClock(
- alarmClockInfo,
- pendingWeatherAlarmIntent
- )
- } else {
- val alarmClockInfo = AlarmManager.AlarmClockInfo(triggerTime, pendingIntent)
- alarmManager.setAlarmClock(alarmClockInfo, pendingIntent)
- }
-
- }
-
-
private fun cancelAllScheduledAlarms() {
- val alarmManager = getSystemService(Context.ALARM_SERVICE) as AlarmManager
- val intent = Intent(this, AlarmReceiver::class.java)
- val pendingIntent = PendingIntent.getBroadcast(
- this,
- 0,
- intent,
- PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_MUTABLE
- )
-
- val activityCheckIntent = Intent(this, ScreenMonitorService::class.java)
- val pendingActivityCheckIntent = PendingIntent.getService(
- this,
- 4,
- activityCheckIntent,
- PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_MUTABLE
- )
-
- // Cancel any existing alarms by providing the same pending intent
- alarmManager.cancel(pendingIntent)
- pendingIntent.cancel()
- alarmManager.cancel(pendingActivityCheckIntent)
- pendingActivityCheckIntent.cancel()
-
+ AlarmScheduler.clear(this)
}
private fun playDefaultAlarm(context: Context) {
@@ -556,4 +446,4 @@ class MainActivity : FlutterActivity() {
Log.i("SystemRingtone", "=== END DIAGNOSTICS ===")
}
-}
\ No newline at end of file
+}
diff --git a/android/app/src/test/kotlin/com/ccextractor/ultimate_alarm_clock/ultimate_alarm_clock/BootRestorePlannerTest.kt b/android/app/src/test/kotlin/com/ccextractor/ultimate_alarm_clock/ultimate_alarm_clock/BootRestorePlannerTest.kt
new file mode 100644
index 000000000..b94246856
--- /dev/null
+++ b/android/app/src/test/kotlin/com/ccextractor/ultimate_alarm_clock/ultimate_alarm_clock/BootRestorePlannerTest.kt
@@ -0,0 +1,146 @@
+package com.ccextractor.ultimate_alarm_clock
+
+import org.junit.Assert.assertEquals
+import org.junit.Assert.assertNull
+import org.junit.Test
+import java.time.LocalDateTime
+import java.time.ZoneId
+
+class BootRestorePlannerTest {
+ private fun payload(triggerAtMs: Long) = ScheduledAlarmPayload(
+ triggerAtMs = triggerAtMs,
+ activityMonitor = 0,
+ locationMonitor = 0,
+ location = "",
+ isWeather = 0,
+ weatherTypes = "[]"
+ )
+
+ @Test
+ fun `prefers local fallback over persisted payload when both are valid`() {
+ val now = 1_000L
+ val persisted = payload(5_000L)
+ val local = payload(10_000L)
+
+ val reconciled = BootRestorePlanner.chooseAlarmToRestore(
+ persistedAlarm = persisted,
+ localAlarm = local,
+ nowMs = now,
+ allowLocalFallback = true
+ )
+
+ assertEquals(10_000L, reconciled?.triggerAtMs)
+ }
+
+ @Test
+ fun `prefers recomputed local alarm when persisted trigger drifted`() {
+ val now = 1_000L
+ val persisted = payload(5_000L)
+ val local = payload(7_200_000L)
+
+ val reconciled = BootRestorePlanner.chooseAlarmToRestore(
+ persistedAlarm = persisted,
+ localAlarm = local,
+ nowMs = now,
+ allowLocalFallback = true
+ )
+
+ assertEquals(7_200_000L, reconciled?.triggerAtMs)
+ }
+
+ @Test
+ fun `uses local fallback when persisted payload is missing`() {
+ val reconciled = BootRestorePlanner.chooseAlarmToRestore(
+ persistedAlarm = null,
+ localAlarm = payload(5_000L),
+ nowMs = 1_000L,
+ allowLocalFallback = true
+ )
+
+ assertEquals(5_000L, reconciled?.triggerAtMs)
+ }
+
+ @Test
+ fun `ignores stale persisted payload and falls back to local when allowed`() {
+ val reconciled = BootRestorePlanner.chooseAlarmToRestore(
+ persistedAlarm = payload(500L),
+ localAlarm = payload(5_000L),
+ nowMs = 1_000L,
+ allowLocalFallback = true
+ )
+
+ assertEquals(5_000L, reconciled?.triggerAtMs)
+ }
+
+ @Test
+ fun `does not use local fallback during locked boot recovery`() {
+ val reconciled = BootRestorePlanner.chooseAlarmToRestore(
+ persistedAlarm = null,
+ localAlarm = payload(5_000L),
+ nowMs = 1_000L,
+ allowLocalFallback = false
+ )
+
+ assertNull(reconciled)
+ }
+
+ @Test
+ fun `local resolver computes next repeating trigger using sunday first day string`() {
+ val zone = ZoneId.systemDefault()
+ val now = LocalDateTime.of(2026, 3, 12, 13, 38)
+ .atZone(zone)
+ .toInstant()
+ .toEpochMilli()
+ val alarm = AlarmModel(
+ id = 1,
+ minutesSinceMidnight = 13 * 60 + 37,
+ alarmTime = "13:37",
+ days = "1111111",
+ isOneTime = 0,
+ activityMonitor = 0,
+ isWeatherEnabled = 0,
+ weatherTypes = "[]",
+ isLocationEnabled = 0,
+ location = "",
+ alarmDate = "",
+ alarmId = "alarm-id",
+ ringOn = 0
+ )
+
+ val triggerAtMs = LocalAlarmScheduleResolver.nextTriggerAtMs(alarm, now, zone)
+
+ val expected = LocalDateTime.of(2026, 3, 13, 13, 37)
+ .atZone(zone)
+ .toInstant()
+ .toEpochMilli()
+ assertEquals(expected, triggerAtMs)
+ }
+
+ @Test
+ fun `local resolver ignores stale one time alarms`() {
+ val zone = ZoneId.systemDefault()
+ val now = LocalDateTime.of(2026, 3, 12, 13, 38)
+ .atZone(zone)
+ .toInstant()
+ .toEpochMilli()
+ val alarm = AlarmModel(
+ id = 2,
+ minutesSinceMidnight = 13 * 60 + 37,
+ alarmTime = "13:37",
+ days = "0000000",
+ isOneTime = 1,
+ activityMonitor = 0,
+ isWeatherEnabled = 0,
+ weatherTypes = "[]",
+ isLocationEnabled = 0,
+ location = "",
+ alarmDate = "2026-03-11",
+ alarmId = "alarm-id",
+ ringOn = 1
+ )
+
+ val triggerAtMs = LocalAlarmScheduleResolver.nextTriggerAtMs(alarm, now, zone)
+
+ assertNull(triggerAtMs)
+ }
+}
diff --git a/android/build.gradle b/android/build.gradle
index 5dc5d980d..e1bb5c50a 100644
--- a/android/build.gradle
+++ b/android/build.gradle
@@ -1,21 +1,3 @@
-buildscript {
- ext.kotlin_version = '1.7.10'
- ext {
- compileSdkVersion = 34
- targetSdkVersion = 34
- appCompatVersion = "1.6.1"
- }
- repositories {
- google()
- mavenCentral()
- }
- dependencies {
- classpath 'com.android.tools.build:gradle:7.3.0'
- classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
- classpath 'com.google.gms:google-services:4.3.15'
- }
-}
-
allprojects {
repositories {
google()
@@ -23,14 +5,37 @@ allprojects {
}
}
-rootProject.buildDir = '../build'
+rootProject.buildDir = "../build"
subprojects {
project.buildDir = "${rootProject.buildDir}/${project.name}"
}
+
subprojects {
- project.evaluationDependsOn(':app')
+ project.evaluationDependsOn(":app")
+}
+
+subprojects { subproject ->
+ ["com.android.application", "com.android.library"].each { pluginId ->
+ subproject.plugins.withId(pluginId) {
+ def androidExtension = subproject.extensions.findByName("android")
+ if (androidExtension == null || androidExtension.namespace != null) {
+ return
+ }
+
+ def manifestFile = androidExtension.sourceSets.main.manifest.srcFile
+ if (!manifestFile.exists()) {
+ return
+ }
+
+ def manifestPackage = new XmlSlurper().parse(manifestFile).@package.text()
+ if (manifestPackage) {
+ androidExtension.namespace = manifestPackage
+ }
+ }
+ }
+
}
tasks.register("clean", Delete) {
delete rootProject.buildDir
-}
\ No newline at end of file
+}
diff --git a/android/gradle.properties b/android/gradle.properties
index ef661b9b5..71e7b0884 100644
--- a/android/gradle.properties
+++ b/android/gradle.properties
@@ -1,5 +1,4 @@
org.gradle.jvmargs=-Xmx4608m
android.useAndroidX=true
android.enableJetifier=true
-android.enableR8=true
org.gradle.wrapper.verification=true
diff --git a/android/gradle/wrapper/gradle-wrapper.properties b/android/gradle/wrapper/gradle-wrapper.properties
index 532677ec6..e2847c820 100644
--- a/android/gradle/wrapper/gradle-wrapper.properties
+++ b/android/gradle/wrapper/gradle-wrapper.properties
@@ -1,7 +1,7 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
-distributionUrl=https\://services.gradle.org/distributions/gradle-8.3-bin.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-8.11.1-bin.zip
networkTimeout=10000
validateDistributionUrl=true
zipStoreBase=GRADLE_USER_HOME
-zipStorePath=wrapper/dists
\ No newline at end of file
+zipStorePath=wrapper/dists
diff --git a/android/settings.gradle b/android/settings.gradle
index 44e62bcf0..264b316e2 100644
--- a/android/settings.gradle
+++ b/android/settings.gradle
@@ -1,11 +1,26 @@
-include ':app'
+pluginManagement {
+ def flutterSdkPath = {
+ def properties = new Properties()
+ file("local.properties").withInputStream { properties.load(it) }
+ def flutterSdkPath = properties.getProperty("flutter.sdk")
+ assert flutterSdkPath != null, "flutter.sdk not set in local.properties"
+ return flutterSdkPath
+ }()
-def localPropertiesFile = new File(rootProject.projectDir, "local.properties")
-def properties = new Properties()
+ includeBuild("$flutterSdkPath/packages/flutter_tools/gradle")
-assert localPropertiesFile.exists()
-localPropertiesFile.withReader("UTF-8") { reader -> properties.load(reader) }
+ repositories {
+ google()
+ mavenCentral()
+ gradlePluginPortal()
+ }
+}
-def flutterSdkPath = properties.getProperty("flutter.sdk")
-assert flutterSdkPath != null, "flutter.sdk not set in local.properties"
-apply from: "$flutterSdkPath/packages/flutter_tools/gradle/app_plugin_loader.gradle"
+plugins {
+ id "dev.flutter.flutter-plugin-loader" version "1.0.0"
+ id "com.android.application" version "8.9.1" apply false
+ id "org.jetbrains.kotlin.android" version "2.1.0" apply false
+ id "com.google.gms.google-services" version "4.4.2" apply false
+}
+
+include ":app"
diff --git a/lib/app/data/providers/firestore_provider.dart b/lib/app/data/providers/firestore_provider.dart
index e27bd7288..25eef1bc7 100644
--- a/lib/app/data/providers/firestore_provider.dart
+++ b/lib/app/data/providers/firestore_provider.dart
@@ -5,7 +5,7 @@ import 'package:get/get.dart';
import 'package:ultimate_alarm_clock/app/data/models/alarm_model.dart';
import 'package:ultimate_alarm_clock/app/data/models/user_model.dart';
import 'package:ultimate_alarm_clock/app/data/providers/isar_provider.dart';
-import 'package:ultimate_alarm_clock/app/utils/utils.dart';
+import 'package:ultimate_alarm_clock/app/utils/alarm_schedule_payload.dart';
import 'package:sqflite/sqflite.dart';
import '../../modules/home/controllers/home_controller.dart';
@@ -182,22 +182,9 @@ class FirestoreDb {
return alarmRecord;
}
- int nowInMinutes = 0;
- if (wantNextAlarm == true) {
- nowInMinutes = Utils.timeOfDayToInt(
- TimeOfDay(
- hour: TimeOfDay.now().hour,
- minute: TimeOfDay.now().minute + 1,
- ),
- );
- } else {
- nowInMinutes = Utils.timeOfDayToInt(
- TimeOfDay(
- hour: TimeOfDay.now().hour,
- minute: TimeOfDay.now().minute + 1,
- ),
- );
- }
+ final referenceTime = AlarmSchedulePayload.selectionReferenceTime(
+ wantNextAlarm: wantNextAlarm,
+ );
late List alarms;
@@ -235,50 +222,37 @@ class FirestoreDb {
alarmRecord.minutesSinceMidnight = -1;
return alarmRecord;
} else {
- // Get the closest alarm to the current time
- AlarmModel closestAlarm = alarms.reduce((a, b) {
- int aTimeUntilNextAlarm = a.minutesSinceMidnight - nowInMinutes;
- int bTimeUntilNextAlarm = b.minutesSinceMidnight - nowInMinutes;
-
- // Check if alarm repeats on any day
- bool aRepeats = a.days.any((day) => day);
- bool bRepeats = b.days.any((day) => day);
-
- // If alarm is one-time and has already passed, set time until
- // next alarm to next day
- if (!aRepeats && aTimeUntilNextAlarm < 0) {
- aTimeUntilNextAlarm += Duration.minutesPerDay;
- }
- if (!bRepeats && bTimeUntilNextAlarm < 0) {
- bTimeUntilNextAlarm += Duration.minutesPerDay;
- }
-
- // If alarm repeats on any day, find the next upcoming day
- if (aRepeats) {
- int currentDay = DateTime.now().weekday - 1;
- for (int i = 0; i < a.days.length; i++) {
- int dayIndex = (currentDay + i) % a.days.length;
- if (a.days[dayIndex]) {
- aTimeUntilNextAlarm += i * Duration.minutesPerDay;
- break;
- }
- }
- }
-
- if (bRepeats) {
- int currentDay = DateTime.now().weekday - 1;
- for (int i = 0; i < b.days.length; i++) {
- int dayIndex = (currentDay + i) % b.days.length;
- if (b.days[dayIndex]) {
- bTimeUntilNextAlarm += i * Duration.minutesPerDay;
- break;
- }
- }
- }
-
- return aTimeUntilNextAlarm < bTimeUntilNextAlarm ? a : b;
+ final eligibleAlarms = alarms
+ .where(
+ (alarm) =>
+ AlarmSchedulePayload.nextTriggerAt(
+ alarm,
+ referenceTime: referenceTime,
+ inclusive: true,
+ ) !=
+ null,
+ )
+ .toList();
+
+ if (eligibleAlarms.isEmpty) {
+ alarmRecord.minutesSinceMidnight = -1;
+ return alarmRecord;
+ }
+
+ return eligibleAlarms.reduce((a, b) {
+ final aTrigger = AlarmSchedulePayload.nextTriggerAt(
+ a,
+ referenceTime: referenceTime,
+ inclusive: true,
+ )!;
+ final bTrigger = AlarmSchedulePayload.nextTriggerAt(
+ b,
+ referenceTime: referenceTime,
+ inclusive: true,
+ )!;
+
+ return aTrigger.isBefore(bTrigger) ? a : b;
});
- return closestAlarm;
}
}
diff --git a/lib/app/data/providers/isar_provider.dart b/lib/app/data/providers/isar_provider.dart
index 3b15b118a..d64a739ae 100644
--- a/lib/app/data/providers/isar_provider.dart
+++ b/lib/app/data/providers/isar_provider.dart
@@ -11,7 +11,7 @@ import 'package:ultimate_alarm_clock/app/data/models/saved_emails.dart';
import 'package:ultimate_alarm_clock/app/data/models/timer_model.dart';
import 'package:ultimate_alarm_clock/app/data/providers/firestore_provider.dart';
import 'package:ultimate_alarm_clock/app/data/providers/get_storage_provider.dart';
-import 'package:ultimate_alarm_clock/app/utils/utils.dart';
+import 'package:ultimate_alarm_clock/app/utils/alarm_schedule_payload.dart';
import 'package:sqflite/sqflite.dart';
enum Status {
@@ -37,7 +37,6 @@ enum LogType {
String toString() => value;
}
-
class IsarDb {
static final IsarDb _instance = IsarDb._internal();
late Future db;
@@ -171,7 +170,6 @@ class IsarDb {
''');
}
-
Future openDB() async {
final dir = await getApplicationDocumentsDirectory();
if (Isar.instanceNames.isEmpty) {
@@ -189,7 +187,11 @@ class IsarDb {
}
return Future.value(Isar.getInstance());
}
- Future insertLog(String msg, {Status status = Status.warning, LogType type = LogType.dev, int hasRung = 0}) async {
+
+ Future insertLog(String msg,
+ {Status status = Status.warning,
+ LogType type = LogType.dev,
+ int hasRung = 0}) async {
try {
final db = await setAlarmLogs();
if (db == null) {
@@ -252,7 +254,7 @@ class IsarDb {
final isarProvider = IsarDb();
final sql = await IsarDb().getAlarmSQLiteDatabase();
final db = await isarProvider.db;
-
+
await db.writeTxn(() async {
await db.alarmModels.put(alarmRecord);
});
@@ -287,7 +289,8 @@ class IsarDb {
static Future getProfile(String name) async {
final isarProvider = IsarDb();
final db = await isarProvider.db;
- final a = await db.profileModels.filter().profileNameEqualTo(name).findFirst();
+ final a =
+ await db.profileModels.filter().profileNameEqualTo(name).findFirst();
print('$a appkle');
return a;
}
@@ -306,7 +309,7 @@ class IsarDb {
static Future profileExists(String name) async {
final isarProvider = IsarDb();
final db = await isarProvider.db;
- final a =
+ final a =
await db.profileModels.filter().profileNameEqualTo(name).findFirst();
return a != null;
@@ -337,39 +340,25 @@ class IsarDb {
static Future doesAlarmExist(String alarmID) async {
final isarProvider = IsarDb();
final db = await isarProvider.db;
- final alarms =
- await db.alarmModels.where().filter().alarmIDEqualTo(alarmID).findAll();
- print('checkEmpty ${alarms[0].alarmID} ${alarms.isNotEmpty}');
+ final alarm = await db.alarmModels
+ .where()
+ .filter()
+ .alarmIDEqualTo(alarmID)
+ .findFirst();
- return alarms.isNotEmpty;
+ return alarm != null;
}
static Future getLatestAlarm(
AlarmModel alarmRecord,
bool wantNextAlarm,
) async {
- int nowInMinutes = 0;
final isarProvider = IsarDb();
final db = await isarProvider.db;
final currentProfile = await storage.readProfile();
-
-// Increasing a day since we need alarms AFTER the current time
-// Logically, alarms at current time will ring in the future ;-;
- if (wantNextAlarm == true) {
- nowInMinutes = Utils.timeOfDayToInt(
- TimeOfDay(
- hour: TimeOfDay.now().hour,
- minute: TimeOfDay.now().minute + 1,
- ),
- );
- } else {
- nowInMinutes = Utils.timeOfDayToInt(
- TimeOfDay(
- hour: TimeOfDay.now().hour,
- minute: TimeOfDay.now().minute,
- ),
- );
- }
+ final referenceTime = AlarmSchedulePayload.selectionReferenceTime(
+ wantNextAlarm: wantNextAlarm,
+ );
// Get all enabled alarms
List alarms = await db.alarmModels
@@ -383,51 +372,37 @@ class IsarDb {
alarmRecord.minutesSinceMidnight = -1;
return alarmRecord;
} else {
- // Get the closest alarm to the current time
- AlarmModel closestAlarm = alarms.reduce((a, b) {
- int aTimeUntilNextAlarm = a.minutesSinceMidnight - nowInMinutes;
- int bTimeUntilNextAlarm = b.minutesSinceMidnight - nowInMinutes;
-
- // Check if alarm repeats on any day
- bool aRepeats = a.days.any((day) => day);
- bool bRepeats = b.days.any((day) => day);
-
- // If alarm is one-time and has already passed or is happening now,
- // set time until next alarm to next day
- if (!aRepeats && aTimeUntilNextAlarm < 0) {
- aTimeUntilNextAlarm += Duration.minutesPerDay;
- }
- if (!bRepeats && bTimeUntilNextAlarm < 0) {
- bTimeUntilNextAlarm += Duration.minutesPerDay;
- }
-
- // If alarm repeats on any day, find the next upcoming day
- if (aRepeats) {
- int currentDay = DateTime.now().weekday - 1;
- for (int i = 0; i < a.days.length; i++) {
- int dayIndex = (currentDay + i) % a.days.length;
- if (a.days[dayIndex]) {
- aTimeUntilNextAlarm += i * Duration.minutesPerDay;
- break;
- }
- }
- }
-
- if (bRepeats) {
- int currentDay = DateTime.now().weekday - 1;
- for (int i = 0; i < b.days.length; i++) {
- int dayIndex = (currentDay + i) % b.days.length;
- if (b.days[dayIndex]) {
- bTimeUntilNextAlarm += i * Duration.minutesPerDay;
- break;
- }
- }
- }
-
- return aTimeUntilNextAlarm < bTimeUntilNextAlarm ? a : b;
+ final eligibleAlarms = alarms
+ .where(
+ (alarm) =>
+ AlarmSchedulePayload.nextTriggerAt(
+ alarm,
+ referenceTime: referenceTime,
+ inclusive: true,
+ ) !=
+ null,
+ )
+ .toList();
+
+ if (eligibleAlarms.isEmpty) {
+ alarmRecord.minutesSinceMidnight = -1;
+ return alarmRecord;
+ }
+
+ return eligibleAlarms.reduce((a, b) {
+ final aTrigger = AlarmSchedulePayload.nextTriggerAt(
+ a,
+ referenceTime: referenceTime,
+ inclusive: true,
+ )!;
+ final bTrigger = AlarmSchedulePayload.nextTriggerAt(
+ b,
+ referenceTime: referenceTime,
+ inclusive: true,
+ )!;
+
+ return aTrigger.isBefore(bTrigger) ? a : b;
});
-
- return closestAlarm;
}
}
@@ -438,7 +413,8 @@ class IsarDb {
await db.writeTxn(() async {
await db.alarmModels.put(alarmRecord);
});
- await IsarDb().insertLog('Alarm updated ${alarmRecord.alarmTime}', status: Status.success, type: LogType.normal);
+ await IsarDb().insertLog('Alarm updated ${alarmRecord.alarmTime}',
+ status: Status.success, type: LogType.normal);
await sql!.update(
'alarms',
alarmRecord.toSQFliteMap(),
@@ -447,18 +423,14 @@ class IsarDb {
);
}
-
static Future fixMaxSnoozeCountInAlarms() async {
final isarProvider = IsarDb();
final db = await isarProvider.db;
final sql = await IsarDb().getAlarmSQLiteDatabase();
-
-
+
final alarms = await db.alarmModels.where().findAll();
-
-
+
for (final alarm in alarms) {
-
await sql!.update(
'alarms',
{'maxSnoozeCount': alarm.maxSnoozeCount},
@@ -768,27 +740,32 @@ class IsarDb {
if (ringtoneCount.isEmpty) {
await db.writeTxn(() async {
await db.ringtoneModels.importJson([
- {'isarId' : fastHash('Digital Alarm 1'),
+ {
+ 'isarId': fastHash('Digital Alarm 1'),
'ringtoneName': 'Digital Alarm 1',
'ringtonePath': 'ringtones/digialarm.mp3',
'currentCounterOfUsage': 0
},
- {'isarId' : fastHash('Digital Alarm 2'),
+ {
+ 'isarId': fastHash('Digital Alarm 2'),
'ringtoneName': 'Digital Alarm 2',
'ringtonePath': 'ringtones/digialarm2.mp3',
'currentCounterOfUsage': 0
},
- {'isarId' : fastHash('Digital Alarm 3'),
+ {
+ 'isarId': fastHash('Digital Alarm 3'),
'ringtoneName': 'Digital Alarm 3',
'ringtonePath': 'ringtones/digialarm3.mp3',
'currentCounterOfUsage': 0
},
- {'isarId' : fastHash('Mystery'),
+ {
+ 'isarId': fastHash('Mystery'),
'ringtoneName': 'Mystery',
'ringtonePath': 'ringtones/mystery.mp3',
'currentCounterOfUsage': 0
},
- {'isarId' : fastHash('New Day'),
+ {
+ 'isarId': fastHash('New Day'),
'ringtoneName': 'New Day',
'ringtonePath': 'ringtones/newday.mp3',
'currentCounterOfUsage': 0
diff --git a/lib/app/modules/alarmRing/controllers/alarm_ring_controller.dart b/lib/app/modules/alarmRing/controllers/alarm_ring_controller.dart
index 01b319d97..10bcbd997 100644
--- a/lib/app/modules/alarmRing/controllers/alarm_ring_controller.dart
+++ b/lib/app/modules/alarmRing/controllers/alarm_ring_controller.dart
@@ -18,6 +18,7 @@ import 'package:ultimate_alarm_clock/app/data/providers/isar_provider.dart';
import 'package:ultimate_alarm_clock/app/data/providers/secure_storage_provider.dart';
import 'package:ultimate_alarm_clock/app/modules/settings/controllers/settings_controller.dart';
import 'package:ultimate_alarm_clock/app/modules/settings/controllers/theme_controller.dart';
+import 'package:ultimate_alarm_clock/app/utils/alarm_schedule_payload.dart';
import 'package:ultimate_alarm_clock/app/utils/audio_utils.dart';
import 'package:ultimate_alarm_clock/app/utils/constants.dart';
@@ -59,11 +60,11 @@ class AlarmControlController extends GetxController {
UserModel? _userModel = await SecureStorageProvider().retrieveUserModel();
AlarmModel _alarmRecord = homeController.genFakeAlarmModel();
AlarmModel isarLatestAlarm =
- await IsarDb.getLatestAlarm(_alarmRecord, true);
+ await IsarDb.getLatestAlarm(_alarmRecord, true);
AlarmModel firestoreLatestAlarm =
- await FirestoreDb.getLatestAlarm(_userModel, _alarmRecord, true);
+ await FirestoreDb.getLatestAlarm(_userModel, _alarmRecord, true);
AlarmModel latestAlarm =
- Utils.getFirstScheduledAlarm(isarLatestAlarm, firestoreLatestAlarm);
+ Utils.getFirstScheduledAlarm(isarLatestAlarm, firestoreLatestAlarm);
debugPrint('LATEST : ${latestAlarm.alarmTime}');
return latestAlarm;
@@ -75,14 +76,14 @@ class AlarmControlController extends GetxController {
void startSnooze() async {
int actualMaxSnoozeCount = currentlyRingingAlarm.value.maxSnoozeCount;
-
+
if (currentlyRingingAlarm.value.isarId > 0) {
final dbAlarm = await IsarDb.getAlarm(currentlyRingingAlarm.value.isarId);
if (dbAlarm != null) {
actualMaxSnoozeCount = dbAlarm.maxSnoozeCount;
}
}
-
+
if (snoozeCount.value >= actualMaxSnoozeCount) {
Get.snackbar(
"Max Snooze Limit",
@@ -95,7 +96,7 @@ class AlarmControlController extends GetxController {
return;
}
snoozeCount.value++;
-
+
Vibration.cancel();
vibrationTimer!.cancel();
isSnoozing.value = true;
@@ -111,8 +112,8 @@ class AlarmControlController extends GetxController {
timer.cancel();
vibrationTimer =
Timer.periodic(const Duration(milliseconds: 3500), (Timer timer) {
- Vibration.vibrate(pattern: [500, 3000]);
- });
+ Vibration.vibrate(pattern: [500, 3000]);
+ });
AudioUtils.playAlarm(alarmRecord: currentlyRingingAlarm.value);
@@ -151,7 +152,7 @@ class AlarmControlController extends GetxController {
double vol = currentlyRingingAlarm.value.volMin / 10.0;
double diff = (currentlyRingingAlarm.value.volMax -
- currentlyRingingAlarm.value.volMin) /
+ currentlyRingingAlarm.value.volMin) /
10.0;
int len = currentlyRingingAlarm.value.gradient * 1000;
double steps = (diff / 0.01).abs();
@@ -183,10 +184,13 @@ class AlarmControlController extends GetxController {
}
});
}
+
void startListeningToFlip() {
_sensorSubscription = accelerometerEvents.listen((event) {
- if (event.z < -8) { // Device is flipped (screen down)
- if (!isSnoozing.value && settingsController.isFlipToSnooze.value == true) {
+ if (event.z < -8) {
+ // Device is flipped (screen down)
+ if (!isSnoozing.value &&
+ settingsController.isFlipToSnooze.value == true) {
startSnooze();
}
}
@@ -259,7 +263,7 @@ class AlarmControlController extends GetxController {
void onInit() async {
super.onInit();
startListeningToFlip();
-
+
// Extract alarm and preview flag from arguments
final args = Get.arguments;
if (args is Map) {
@@ -272,18 +276,20 @@ class AlarmControlController extends GetxController {
if (currentlyRingingAlarm.value.isarId > 0) {
final dbAlarm = await IsarDb.getAlarm(currentlyRingingAlarm.value.isarId);
- if (dbAlarm != null && dbAlarm.maxSnoozeCount != currentlyRingingAlarm.value.maxSnoozeCount) {
+ if (dbAlarm != null &&
+ dbAlarm.maxSnoozeCount !=
+ currentlyRingingAlarm.value.maxSnoozeCount) {
currentlyRingingAlarm.value.maxSnoozeCount = dbAlarm.maxSnoozeCount;
}
}
-
+
if (currentlyRingingAlarm.value.isGuardian) {
guardianTimer = Timer.periodic(const Duration(seconds: 1), (timer) {
if (guardianCoundown.value == 0) {
currentlyRingingAlarm.value.isCall
? Utils.dialNumber(currentlyRingingAlarm.value.guardian)
: Utils.sendSMS(currentlyRingingAlarm.value.guardian,
- "Your Friend is not waking up \n - Ultimate Alarm Clock");
+ "Your Friend is not waking up \n - Ultimate Alarm Clock");
timer.cancel();
} else {
guardianCoundown.value = guardianCoundown.value - 1;
@@ -302,8 +308,8 @@ class AlarmControlController extends GetxController {
vibrationTimer =
Timer.periodic(const Duration(milliseconds: 3500), (Timer timer) {
- Vibration.vibrate(pattern: [500, 3000]);
- });
+ Vibration.vibrate(pattern: [500, 3000]);
+ });
// Preventing app from being minimized!
_subscription = FGBGEvents.stream.listen((event) {
@@ -358,8 +364,7 @@ class AlarmControlController extends GetxController {
AudioUtils.playAlarm(alarmRecord: currentlyRingingAlarm.value);
-
- if(currentlyRingingAlarm.value.showMotivationalQuote) {
+ if (currentlyRingingAlarm.value.showMotivationalQuote) {
Quote quote = Utils.getRandomQuote();
showQuotePopup(quote);
}
@@ -372,7 +377,7 @@ class AlarmControlController extends GetxController {
// Finding the next alarm to ring
AlarmModel latestAlarm = await getNextAlarm();
TimeOfDay latestAlarmTimeOfDay =
- Utils.stringToTimeOfDay(latestAlarm.alarmTime);
+ Utils.stringToTimeOfDay(latestAlarm.alarmTime);
// }
// This condition will never satisfy because this will only
@@ -380,22 +385,17 @@ class AlarmControlController extends GetxController {
if (latestAlarm.isEnabled == false) {
debugPrint(
'STOPPED IF CONDITION with latest = '
- '${latestAlarmTimeOfDay.toString()} and '
- 'current = ${currentTime.toString()}',
+ '${latestAlarmTimeOfDay.toString()} and '
+ 'current = ${currentTime.toString()}',
);
await alarmChannel.invokeMethod('cancelAllScheduledAlarms');
} else {
- int intervaltoAlarm = Utils.getMillisecondsToAlarm(
- DateTime.now(),
- Utils.timeOfDayToDateTime(latestAlarmTimeOfDay),
- );
-
try {
- await alarmChannel.invokeMethod('scheduleAlarm', {
- 'milliSeconds': intervaltoAlarm,
- 'activityMonitor': latestAlarm.activityMonitor
- });
+ await alarmChannel.invokeMethod(
+ 'scheduleAlarm',
+ AlarmSchedulePayload.fromAlarm(latestAlarm),
+ );
print("Scheduled...");
} on PlatformException catch (e) {
print("Failed to schedule alarm: ${e.message}");
@@ -416,24 +416,23 @@ class AlarmControlController extends GetxController {
initialVolume,
stream: AudioStream.alarm,
);
-
+
if (!isPreviewMode.value) {
if (currentlyRingingAlarm.value.deleteAfterGoesOff == true) {
- if (currentlyRingingAlarm.value.isSharedAlarmEnabled &&
- currentlyRingingAlarm.value.ownerId != null &&
- currentlyRingingAlarm.value.firestoreId != null) {
+ if (currentlyRingingAlarm.value.isSharedAlarmEnabled &&
+ currentlyRingingAlarm.value.ownerId != null &&
+ currentlyRingingAlarm.value.firestoreId != null) {
await FirestoreDb.deleteOneTimeAlarm(
currentlyRingingAlarm.value.ownerId,
currentlyRingingAlarm.value.firestoreId,
);
} else if (currentlyRingingAlarm.value.isarId > 0) {
-
await IsarDb.deleteAlarm(currentlyRingingAlarm.value.isarId);
}
- }
- else if (currentlyRingingAlarm.value.days.every((element) => element == false)) {
+ } else if (currentlyRingingAlarm.value.days
+ .every((element) => element == false)) {
currentlyRingingAlarm.value.isEnabled = false;
- if (!currentlyRingingAlarm.value.isSharedAlarmEnabled &&
+ if (!currentlyRingingAlarm.value.isSharedAlarmEnabled &&
currentlyRingingAlarm.value.isarId > 0) {
await IsarDb.updateAlarm(currentlyRingingAlarm.value);
} else if (currentlyRingingAlarm.value.ownerId != null) {
diff --git a/lib/app/modules/home/controllers/home_controller.dart b/lib/app/modules/home/controllers/home_controller.dart
index 449ca94fa..673f8f603 100644
--- a/lib/app/modules/home/controllers/home_controller.dart
+++ b/lib/app/modules/home/controllers/home_controller.dart
@@ -17,6 +17,7 @@ import 'package:ultimate_alarm_clock/app/data/providers/get_storage_provider.dar
import 'package:ultimate_alarm_clock/app/data/providers/isar_provider.dart';
import 'package:ultimate_alarm_clock/app/data/providers/secure_storage_provider.dart';
import 'package:ultimate_alarm_clock/app/modules/settings/controllers/theme_controller.dart';
+import 'package:ultimate_alarm_clock/app/utils/alarm_schedule_payload.dart';
import 'package:ultimate_alarm_clock/app/utils/constants.dart';
import 'package:ultimate_alarm_clock/app/utils/utils.dart';
@@ -232,19 +233,16 @@ class HomeController extends GetxController {
String profileName = await storage.readProfile();
selectedProfile.value = profileName;
ProfileModel? p = await IsarDb.getProfile(profileName);
- if (p != null)
- {
+ if (p != null) {
profileModel.value = p!;
}
-
}
void writeProfileName(String name) async {
await storage.writeProfile(name);
selectedProfile.value = name;
ProfileModel? p = await IsarDb.getProfile(name);
- if (p != null)
- {
+ if (p != null) {
profileModel.value = p!;
}
}
@@ -261,20 +259,18 @@ class HomeController extends GetxController {
readProfileName();
userModel.value = await SecureStorageProvider().retrieveUserModel();
- if (userModel.value == null){
- FirebaseAuth.instance.authStateChanges().listen((user) {
- if (user == null) {
- isUserSignedIn.value = false;
- } else {
- isUserSignedIn.value = true;
- }
- });
- }
- else {
- isUserSignedIn.value = true;
+ if (userModel.value == null) {
+ FirebaseAuth.instance.authStateChanges().listen((user) {
+ if (user == null) {
+ isUserSignedIn.value = false;
+ } else {
+ isUserSignedIn.value = true;
+ }
+ });
+ } else {
+ isUserSignedIn.value = true;
}
-
isSortedAlarmListEnabled.value = await SecureStorageProvider()
.readSortedAlarmListValue(key: 'sorted_alarm_list');
@@ -287,7 +283,6 @@ class HomeController extends GetxController {
final newFactor = 1.0 - (offset / maxOffset).clamp(0.0, 1.0);
scalingFactor.value = (minFactor + (maxFactor - minFactor) * newFactor);
});
-
}
refreshUpcomingAlarms() async {
@@ -401,15 +396,11 @@ class HomeController extends GetxController {
);
await alarmChannel.invokeMethod('cancelAllScheduledAlarms');
} else {
- int intervaltoAlarm = Utils.getMillisecondsToAlarm(
- DateTime.now(),
- Utils.timeOfDayToDateTime(latestAlarmTimeOfDay),
- );
try {
- await alarmChannel.invokeMethod('scheduleAlarm', {
- 'milliSeconds': intervaltoAlarm,
- 'activityMonitor': latestAlarm.activityMonitor,
- });
+ await alarmChannel.invokeMethod(
+ 'scheduleAlarm',
+ AlarmSchedulePayload.fromAlarm(latestAlarm),
+ );
print('Scheduled...');
} on PlatformException catch (e) {
print('Failed to schedule alarm: ${e.message}');
@@ -517,15 +508,14 @@ class HomeController extends GetxController {
try {
if (isSharedAlarmEnabled) {
-
- AlarmModel? alarmToDelete = await FirestoreDb.getAlarm(userModel.value, alarmId);
+ AlarmModel? alarmToDelete =
+ await FirestoreDb.getAlarm(userModel.value, alarmId);
if (alarmToDelete != null) {
deletedAlarms.add(alarmToDelete);
await FirestoreDb.deleteAlarm(userModel.value, alarmId);
successCount++;
}
} else {
-
AlarmModel? alarmToDelete = await IsarDb.getAlarm(alarmId);
if (alarmToDelete != null) {
deletedAlarms.add(alarmToDelete);
@@ -557,7 +547,6 @@ class HomeController extends GetxController {
),
mainButton: TextButton(
onPressed: () async {
-
for (var alarm in deletedAlarms) {
if (alarm.isSharedAlarmEnabled) {
await FirestoreDb.addAlarm(userModel.value, alarm);
@@ -565,7 +554,7 @@ class HomeController extends GetxController {
await IsarDb.addAlarm(alarm);
}
}
-
+
refreshTimer = true;
refreshUpcomingAlarms();
},
@@ -576,7 +565,6 @@ class HomeController extends GetxController {
),
);
-
selectedAlarmSet.clear();
numberOfAlarmsSelected.value = 0;
} else {
diff --git a/lib/app/modules/splashScreen/controllers/splash_screen_controller.dart b/lib/app/modules/splashScreen/controllers/splash_screen_controller.dart
index 243c482d1..874773a27 100644
--- a/lib/app/modules/splashScreen/controllers/splash_screen_controller.dart
+++ b/lib/app/modules/splashScreen/controllers/splash_screen_controller.dart
@@ -6,6 +6,7 @@ import 'package:ultimate_alarm_clock/app/data/models/user_model.dart';
import 'package:ultimate_alarm_clock/app/data/providers/firestore_provider.dart';
import 'package:ultimate_alarm_clock/app/data/providers/isar_provider.dart';
import 'package:ultimate_alarm_clock/app/data/providers/secure_storage_provider.dart';
+import 'package:ultimate_alarm_clock/app/utils/alarm_schedule_payload.dart';
import 'package:ultimate_alarm_clock/app/utils/utils.dart';
import '../../home/controllers/home_controller.dart';
@@ -22,8 +23,7 @@ class SplashScreenController extends GetxController {
getCurrentlyRingingAlarm() async {
AlarmModel _alarmRecord = homeController.genFakeAlarmModel();
- AlarmModel latestAlarm =
- await IsarDb.getLatestAlarm(_alarmRecord, false);
+ AlarmModel latestAlarm = await IsarDb.getLatestAlarm(_alarmRecord, false);
debugPrint('CURRENT RINGING : ${latestAlarm.alarmTime}');
return latestAlarm;
}
@@ -99,9 +99,9 @@ class SplashScreenController extends GetxController {
@override
void onInit() async {
super.onInit();
-
+
await IsarDb.fixMaxSnoozeCountInAlarms();
-
+
currentlyRingingAlarm.value = homeController.genFakeAlarmModel();
alarmChannel.setMethodCallHandler((call) async {
if (call.method == 'appStartup') {
@@ -118,15 +118,20 @@ class SplashScreenController extends GetxController {
} else {
if (shouldAlarmRing) {
currentlyRingingAlarm.value = await getCurrentlyRingingAlarm();
-
+
if (currentlyRingingAlarm.value.alarmID != null) {
- final dbAlarm = await IsarDb.getAlarm(currentlyRingingAlarm.value.isarId);
- if (dbAlarm != null && dbAlarm.maxSnoozeCount != currentlyRingingAlarm.value.maxSnoozeCount) {
- currentlyRingingAlarm.value.maxSnoozeCount = dbAlarm.maxSnoozeCount;
+ final dbAlarm =
+ await IsarDb.getAlarm(currentlyRingingAlarm.value.isarId);
+ if (dbAlarm != null &&
+ dbAlarm.maxSnoozeCount !=
+ currentlyRingingAlarm.value.maxSnoozeCount) {
+ currentlyRingingAlarm.value.maxSnoozeCount =
+ dbAlarm.maxSnoozeCount;
}
}
-
- Get.offNamed('/alarm-ring',arguments: currentlyRingingAlarm.value);
+
+ Get.offNamed('/alarm-ring',
+ arguments: currentlyRingingAlarm.value);
} else {
currentlyRingingAlarm.value = await getCurrentlyRingingAlarm();
// If the alarm is set to NEVER repeat, then it will be chosen as
@@ -156,16 +161,11 @@ class SplashScreenController extends GetxController {
'${latestAlarmTimeOfDay.toString()} and ');
await alarmChannel.invokeMethod('cancelAllScheduledAlarms');
} else {
- int intervaltoAlarm = Utils.getMillisecondsToAlarm(
- DateTime.now(),
- Utils.timeOfDayToDateTime(latestAlarmTimeOfDay),
- );
-
try {
- await alarmChannel.invokeMethod('scheduleAlarm', {
- 'milliSeconds': intervaltoAlarm,
- 'activityMonitor': latestAlarm.activityMonitor
- });
+ await alarmChannel.invokeMethod(
+ 'scheduleAlarm',
+ AlarmSchedulePayload.fromAlarm(latestAlarm),
+ );
print("Scheduled...");
} on PlatformException catch (e) {
print("Failed to schedule alarm: ${e.message}");
diff --git a/lib/app/utils/alarm_schedule_payload.dart b/lib/app/utils/alarm_schedule_payload.dart
new file mode 100644
index 000000000..69163adf1
--- /dev/null
+++ b/lib/app/utils/alarm_schedule_payload.dart
@@ -0,0 +1,101 @@
+import 'dart:convert';
+
+import 'package:ultimate_alarm_clock/app/data/models/alarm_model.dart';
+import 'package:ultimate_alarm_clock/app/utils/utils.dart';
+
+class AlarmSchedulePayload {
+ static DateTime selectionReferenceTime({
+ DateTime? now,
+ required bool wantNextAlarm,
+ }) {
+ final currentTime = now ?? DateTime.now();
+ final truncated = DateTime(
+ currentTime.year,
+ currentTime.month,
+ currentTime.day,
+ currentTime.hour,
+ currentTime.minute,
+ );
+
+ return wantNextAlarm
+ ? truncated.add(const Duration(minutes: 1))
+ : truncated;
+ }
+
+ static DateTime? nextTriggerAt(
+ AlarmModel alarm, {
+ DateTime? referenceTime,
+ bool inclusive = false,
+ }) {
+ final currentTime = referenceTime ?? DateTime.now();
+ final alarmTime = Utils.stringToTimeOfDay(alarm.alarmTime);
+
+ bool isValidCandidate(DateTime candidate) {
+ return inclusive
+ ? !candidate.isBefore(currentTime)
+ : candidate.isAfter(currentTime);
+ }
+
+ DateTime buildCandidate(DateTime date) {
+ return DateTime(
+ date.year,
+ date.month,
+ date.day,
+ alarmTime.hour,
+ alarmTime.minute,
+ );
+ }
+
+ if (alarm.ringOn) {
+ final scheduledDate = DateTime.tryParse(alarm.alarmDate.trim());
+ if (scheduledDate == null) {
+ return null;
+ }
+
+ final candidate = buildCandidate(scheduledDate);
+ return isValidCandidate(candidate) ? candidate : null;
+ }
+
+ if (alarm.days.any((day) => day)) {
+ for (var offset = 0; offset <= 7; offset++) {
+ final candidateDate = currentTime.add(Duration(days: offset));
+ final candidate = buildCandidate(candidateDate);
+ final weekdayIndex = candidate.weekday - 1;
+
+ if (alarm.days[weekdayIndex] && isValidCandidate(candidate)) {
+ return candidate;
+ }
+ }
+
+ return null;
+ }
+
+ final candidate = buildCandidate(currentTime);
+ if (isValidCandidate(candidate)) {
+ return candidate;
+ }
+
+ return candidate.add(const Duration(days: 1));
+ }
+
+ static Map fromAlarm(
+ AlarmModel alarm, {
+ DateTime? now,
+ }) {
+ final currentTime = now ?? DateTime.now();
+ final scheduledTime = nextTriggerAt(alarm, referenceTime: currentTime);
+ if (scheduledTime == null) {
+ throw StateError('Alarm does not have a future trigger time');
+ }
+
+ return {
+ 'triggerAtMs': scheduledTime.millisecondsSinceEpoch,
+ 'milliSeconds': Utils.getMillisecondsToAlarm(currentTime, scheduledTime),
+ 'activityMonitor': alarm.activityMonitor,
+ 'locationMonitor': alarm.isLocationEnabled ? 1 : 0,
+ 'location': alarm.location,
+ 'isWeather': alarm.isWeatherEnabled ? 1 : 0,
+ 'weatherTypes': jsonEncode(alarm.weatherTypes),
+ };
+ }
+}
diff --git a/pubspec.yaml b/pubspec.yaml
index ea1939f07..9129cefab 100644
--- a/pubspec.yaml
+++ b/pubspec.yaml
@@ -35,7 +35,7 @@ dependencies:
url_launcher: ^6.3.0
flutter_expandable_fab: ^1.8.1
vibration: ^1.8.1
- file_picker: ^6.1.1
+ file_picker: 8.0.7
audioplayers: ^5.2.1
sensors_plus: ^1.4.1
rxdart: ^0.27.7
@@ -66,6 +66,20 @@ dev_dependencies:
flutter_test:
sdk: flutter
+dependency_overrides:
+ fluttertoast:
+ path: third_party/pub_patches/fluttertoast
+ fl_location:
+ path: third_party/pub_patches/fl_location
+ flutter_fgbg:
+ path: third_party/pub_patches/flutter_fgbg
+ audioplayers_android:
+ path: third_party/pub_patches/audioplayers_android
+ sensors_plus:
+ path: third_party/pub_patches/sensors_plus
+ vibration:
+ path: third_party/pub_patches/vibration
+
flutter_native_splash:
android: true
ios: true
diff --git a/test/app/data/providers/isar_provider_test.dart b/test/app/data/providers/isar_provider_test.dart
new file mode 100644
index 000000000..fd128f145
--- /dev/null
+++ b/test/app/data/providers/isar_provider_test.dart
@@ -0,0 +1,51 @@
+import 'dart:ffi';
+import 'dart:io';
+
+import 'package:flutter/services.dart';
+import 'package:flutter_test/flutter_test.dart';
+import 'package:isar/isar.dart';
+import 'package:ultimate_alarm_clock/app/data/providers/isar_provider.dart';
+
+void main() {
+ TestWidgetsFlutterBinding.ensureInitialized();
+
+ const pathProviderChannel = MethodChannel('plugins.flutter.io/path_provider');
+ late Directory tempDirectory;
+
+ setUpAll(() async {
+ await Isar.initializeIsarCore(
+ libraries: {
+ Abi.current():
+ '${Platform.environment['HOME']}/.pub-cache/hosted/pub.dev/isar_flutter_libs-3.1.0+1/linux/libisar.so',
+ },
+ );
+
+ tempDirectory = await Directory.systemTemp.createTemp(
+ 'ultimate_alarm_clock_isar_test',
+ );
+
+ TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger
+ .setMockMethodCallHandler(pathProviderChannel, (methodCall) async {
+ if (methodCall.method == 'getApplicationDocumentsDirectory') {
+ return tempDirectory.path;
+ }
+ return null;
+ });
+ });
+
+ tearDownAll(() async {
+ TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger
+ .setMockMethodCallHandler(pathProviderChannel, null);
+
+ final db = await IsarDb().db;
+ await db.close(deleteFromDisk: true);
+
+ if (tempDirectory.existsSync()) {
+ await tempDirectory.delete(recursive: true);
+ }
+ });
+
+ test('doesAlarmExist returns false when the alarm is missing', () async {
+ expect(await IsarDb.doesAlarmExist('missing-alarm-id'), isFalse);
+ });
+}
diff --git a/test/app/utils/alarm_schedule_payload_test.dart b/test/app/utils/alarm_schedule_payload_test.dart
new file mode 100644
index 000000000..98e3d0ed4
--- /dev/null
+++ b/test/app/utils/alarm_schedule_payload_test.dart
@@ -0,0 +1,141 @@
+import 'dart:convert';
+
+import 'package:flutter_test/flutter_test.dart';
+import 'package:ultimate_alarm_clock/app/data/models/alarm_model.dart';
+import 'package:ultimate_alarm_clock/app/utils/alarm_schedule_payload.dart';
+
+AlarmModel buildAlarm() {
+ return AlarmModel(
+ alarmTime: '09:00',
+ alarmID: 'alarm-1',
+ ownerId: '',
+ ownerName: '',
+ lastEditedUserId: '',
+ mutexLock: false,
+ days: [false, false, false, false, false, false, false],
+ intervalToAlarm: 0,
+ isActivityEnabled: false,
+ minutesSinceMidnight: 0,
+ isLocationEnabled: false,
+ isSharedAlarmEnabled: false,
+ isWeatherEnabled: false,
+ location: '',
+ weatherTypes: const [],
+ isMathsEnabled: false,
+ mathsDifficulty: 0,
+ numMathsQuestions: 0,
+ isShakeEnabled: false,
+ shakeTimes: 0,
+ isQrEnabled: false,
+ qrValue: '',
+ isPedometerEnabled: false,
+ numberOfSteps: 0,
+ activityInterval: 0,
+ mainAlarmTime: '09:00',
+ label: '',
+ isOneTime: true,
+ snoozeDuration: 5,
+ gradient: 0,
+ ringtoneName: 'Default',
+ note: '',
+ deleteAfterGoesOff: false,
+ showMotivationalQuote: false,
+ volMax: 1,
+ volMin: 0,
+ activityMonitor: 0,
+ ringOn: false,
+ alarmDate: '2026-03-12',
+ profile: 'Default',
+ isGuardian: false,
+ guardianTimer: 0,
+ guardian: '',
+ isCall: false,
+ );
+}
+
+void main() {
+ test('finds the next enabled weekday for repeating alarms', () {
+ final alarm = buildAlarm()
+ ..alarmTime = '08:45'
+ ..days = [false, false, true, false, true, false, false];
+
+ final triggerAt = AlarmSchedulePayload.nextTriggerAt(
+ alarm,
+ referenceTime: DateTime(2026, 3, 11, 9, 0),
+ );
+
+ expect(triggerAt, DateTime(2026, 3, 13, 8, 45));
+ });
+
+ test('respects ringOn date based alarms instead of wrapping to tomorrow', () {
+ final alarm = buildAlarm()
+ ..alarmTime = '07:30'
+ ..ringOn = true
+ ..alarmDate = '2026-03-15 ';
+
+ final triggerAt = AlarmSchedulePayload.nextTriggerAt(
+ alarm,
+ referenceTime: DateTime(2026, 3, 12, 9, 0),
+ );
+
+ final payload = AlarmSchedulePayload.fromAlarm(
+ alarm,
+ now: DateTime(2026, 3, 12, 9, 0),
+ );
+
+ expect(triggerAt, DateTime(2026, 3, 15, 7, 30));
+ expect(
+ payload['triggerAtMs'],
+ DateTime(2026, 3, 15, 7, 30).millisecondsSinceEpoch,
+ );
+ expect(
+ payload['milliSeconds'],
+ const Duration(days: 2, hours: 22, minutes: 30).inMilliseconds,
+ );
+ });
+
+ test('builds the native scheduling payload from alarm state', () {
+ final alarm = buildAlarm()
+ ..alarmTime = '09:30'
+ ..activityMonitor = 1
+ ..isLocationEnabled = true
+ ..location = '12.9716,77.5946'
+ ..isWeatherEnabled = true
+ ..weatherTypes = [1, 3];
+
+ final payload = AlarmSchedulePayload.fromAlarm(
+ alarm,
+ now: DateTime(2026, 3, 12, 9, 0),
+ );
+
+ expect(
+ payload['triggerAtMs'],
+ DateTime(2026, 3, 12, 9, 30).millisecondsSinceEpoch,
+ );
+ expect(payload['milliSeconds'], 30 * 60 * 1000);
+ expect(payload['activityMonitor'], 1);
+ expect(payload['locationMonitor'], 1);
+ expect(payload['location'], '12.9716,77.5946');
+ expect(payload['isWeather'], 1);
+ expect(payload['weatherTypes'], jsonEncode([1, 3]));
+ });
+
+ test('wraps scheduling to the next day when the alarm time already passed',
+ () {
+ final alarm = buildAlarm()..alarmTime = '08:45';
+
+ final payload = AlarmSchedulePayload.fromAlarm(
+ alarm,
+ now: DateTime(2026, 3, 12, 9, 0),
+ );
+
+ expect(
+ payload['triggerAtMs'],
+ DateTime(2026, 3, 13, 8, 45).millisecondsSinceEpoch,
+ );
+ expect(payload['milliSeconds'], ((23 * 60) + 45) * 60 * 1000);
+ expect(payload['locationMonitor'], 0);
+ expect(payload['isWeather'], 0);
+ expect(payload['weatherTypes'], jsonEncode([]));
+ });
+}
diff --git a/third_party/pub_patches/README.md b/third_party/pub_patches/README.md
new file mode 100644
index 000000000..01bb4bff9
--- /dev/null
+++ b/third_party/pub_patches/README.md
@@ -0,0 +1,6 @@
+Project-local patched pub packages used to keep the Android build reproducible.
+
+These directories were copied from the working patched pub cache used to validate
+the Android migration on March 12, 2026. `pubspec.yaml` uses path-based
+`dependency_overrides` so CI and other contributors resolve the same plugin
+sources without relying on local `.pub-cache` edits.
diff --git a/third_party/pub_patches/audioplayers_android/CHANGELOG.md b/third_party/pub_patches/audioplayers_android/CHANGELOG.md
new file mode 100644
index 000000000..f941c5b97
--- /dev/null
+++ b/third_party/pub_patches/audioplayers_android/CHANGELOG.md
@@ -0,0 +1,388 @@
+## 4.0.3
+
+ - **FIX**(android): Released wrong source in LOW_LATENCY mode ([#1672](https://github.com/bluefireteam/audioplayers/issues/1672)). ([d9c5f693](https://github.com/bluefireteam/audioplayers/commit/d9c5f693cafab21b67b785de6244c3c371344a53))
+
+## 4.0.2
+
+ - **REFACTOR**: Lint Kotlin, C and C++ code ([#1610](https://github.com/bluefireteam/audioplayers/issues/1610)). ([05394668](https://github.com/bluefireteam/audioplayers/commit/0539466850aaa49a0bde9448939c6c3d536dd6e2))
+ - **FIX**: Set playback rate only when playing ([#1658](https://github.com/bluefireteam/audioplayers/issues/1658)). ([d73c7d5c](https://github.com/bluefireteam/audioplayers/commit/d73c7d5c2ef13e8eff2c438b96ade6e2483a2014))
+ - **FIX**: Improve Error handling for Unsupported Sources ([#1625](https://github.com/bluefireteam/audioplayers/issues/1625)). ([a4d84422](https://github.com/bluefireteam/audioplayers/commit/a4d84422f1421755b05aa7eff38b4d2ed0cf7482))
+ - **FIX**: Return null for duration and position, if not available ([#1606](https://github.com/bluefireteam/audioplayers/issues/1606)). ([2a79644a](https://github.com/bluefireteam/audioplayers/commit/2a79644a2064ccc5d8e9a31aaf888b0b60ee321d))
+
+## 4.0.1
+
+ - **REVERT**(android): Upgrade androidx.core:core-ktx, restore support for AGP7 ([#1590](https://github.com/bluefireteam/audioplayers/issues/1590)). ([f6bf1260](https://github.com/bluefireteam/audioplayers/commit/f6bf12609ec9e457451f1c786522bff28a1555f4))
+
+## 4.0.0
+
+> Note: This release has breaking changes.
+
+ - **FIX**(android): Allow AudioFocus.none ([#1534](https://github.com/bluefireteam/audioplayers/issues/1534)). ([858d3f44](https://github.com/bluefireteam/audioplayers/commit/858d3f4410b1bc7b203090c20cf60b5136dad4fe))
+ - **FEAT**(android): Add support for AGP 8 in example, add compileOptions to build.gradle ([#1503](https://github.com/bluefireteam/audioplayers/issues/1503)). ([7c08e4e1](https://github.com/bluefireteam/audioplayers/commit/7c08e4e1a524f53294f6967996fd31837e62cb81))
+ - **BREAKING** **FIX**: Default audio output to system preferences ([#1563](https://github.com/bluefireteam/audioplayers/issues/1563)). ([381c43e3](https://github.com/bluefireteam/audioplayers/commit/381c43e3725fbb0cb4fd35982893a3c92b188886))
+ - **BREAKING** **CHORE**: Bump Flutter to version 3.10.x ([#1529](https://github.com/bluefireteam/audioplayers/issues/1529)). ([c1296c9b](https://github.com/bluefireteam/audioplayers/commit/c1296c9ba0cc43284b31d78f2f484454fbf6b773))
+
+## 3.0.2
+
+ - **FIX**(android): `onComplete` is not called when audio has completed playing ([#1523](https://github.com/bluefireteam/audioplayers/issues/1523)). ([293d6c0e](https://github.com/bluefireteam/audioplayers/commit/293d6c0eec1d89ad200b2914cae0adf644b25013))
+ - **FIX**: Timeout on setting same source twice ([#1520](https://github.com/bluefireteam/audioplayers/issues/1520)). ([5d164d1f](https://github.com/bluefireteam/audioplayers/commit/5d164d1f20463a8a31a228cd1d85252d47ae256e))
+ - **FIX**: test and fix compatibility with min flutter version ([#1510](https://github.com/bluefireteam/audioplayers/issues/1510)). ([9f39e95f](https://github.com/bluefireteam/audioplayers/commit/9f39e95ff7913d8fc30fff27fef7aefc32de26fb))
+ - **FIX**(android): Add AGP 8 support with namespace property ([#1514](https://github.com/bluefireteam/audioplayers/issues/1514)). ([8d7b322e](https://github.com/bluefireteam/audioplayers/commit/8d7b322e79fd802fb75ca72f5c8ac388754cd406))
+ - **FIX**: onPrepared event to wait until player is ready / finished loading the source ([#1469](https://github.com/bluefireteam/audioplayers/issues/1469)). ([50f56365](https://github.com/bluefireteam/audioplayers/commit/50f56365f8e512df0fc5bdb7222614389cbd4ea0))
+ - **FIX**: rework dispose ([#1480](https://github.com/bluefireteam/audioplayers/issues/1480)). ([c64ef6d9](https://github.com/bluefireteam/audioplayers/commit/c64ef6d914a52743128c717b90c4da0abbd7538d))
+
+## 3.0.1
+
+ - **FIX**: dispose player implementation ([#1470](https://github.com/bluefireteam/audioplayers/issues/1470)). ([d9026c15](https://github.com/bluefireteam/audioplayers/commit/d9026c1538cc83dfba5745771ad71c307b6da852))
+
+## 3.0.0
+
+> Note: This release has breaking changes.
+
+ - **FIX**(android): Avoid calling onDuration on position event (closes [#136](https://github.com/bluefireteam/audioplayers/issues/136)) ([#1460](https://github.com/bluefireteam/audioplayers/issues/1460)). ([6cfb3753](https://github.com/bluefireteam/audioplayers/commit/6cfb3753cd8003f341d97e0b2417d4512f452267))
+ - **FIX**(android): reset prepared state on player error ([#1425](https://github.com/bluefireteam/audioplayers/issues/1425)). ([6f24c8f5](https://github.com/bluefireteam/audioplayers/commit/6f24c8f57e4549edbf7d68a021d1d94371c23f3f))
+ - **FEAT**(android): add `setBalance` ([#58](https://github.com/bluefireteam/audioplayers/issues/58)) ([#1444](https://github.com/bluefireteam/audioplayers/issues/1444)). ([3b5de50e](https://github.com/bluefireteam/audioplayers/commit/3b5de50ea7fa5248165616fc1ffd80da6c66583a))
+ - **DOCS**: update AudioCache explanation, migration guide, replace package READMEs ([#1457](https://github.com/bluefireteam/audioplayers/issues/1457)). ([b8eb1974](https://github.com/bluefireteam/audioplayers/commit/b8eb197435631fafeaa9a26eb76aca8e43e86420))
+ - **BREAKING** **FEAT**: event channel ([#1352](https://github.com/bluefireteam/audioplayers/issues/1352)). ([c9fd6a76](https://github.com/bluefireteam/audioplayers/commit/c9fd6a762c8c346d8d5598e3550c5571a5e460f0))
+
+## 2.0.0
+
+> Note: This release has breaking changes.
+
+ - **FIX**: playing at playback rate `1.0` in android API level < 23 (fixes [#1344](https://github.com/bluefireteam/audioplayers/issues/1344)) ([#1390](https://github.com/bluefireteam/audioplayers/issues/1390)). ([b248e71d](https://github.com/bluefireteam/audioplayers/commit/b248e71dabf923072f1fd14355b4e0230c9a6593))
+ - **BREAKING** **FEAT**: configurable SoundPool and `AudioManager.mode` ([#1388](https://github.com/bluefireteam/audioplayers/issues/1388)). ([5697f187](https://github.com/bluefireteam/audioplayers/commit/5697f187bcca64de2e519f8f49aaf4817fcf6398))
+
+## 1.1.4
+
+ - Update a dependency to the latest release.
+
+## 1.1.3
+
+ - **FIX**: Avoid ConcurrentModificationException ([#1297](https://github.com/bluefireteam/audioplayers/issues/1297)). ([d15ef5ab](https://github.com/bluefireteam/audioplayers/commit/d15ef5ab93f11e2f19089af08f1533fcdc1397e6))
+ - **DOCS**: Fix repos and homepages on pubspecs ([#1349](https://github.com/bluefireteam/audioplayers/issues/1349)). ([0bdde4d9](https://github.com/bluefireteam/audioplayers/commit/0bdde4d9f8f62487cdcfe96221216eba03b31060))
+
+## 1.1.1
+
+ - **FIX**: Avoid ConcurrentModificationException ([#1297](https://github.com/bluefireteam/audioplayers/issues/1297)). ([d15ef5ab](https://github.com/bluefireteam/audioplayers/commit/d15ef5ab93f11e2f19089af08f1533fcdc1397e6))
+
+## 1.1.0
+
+ - **FIX**: lowLatency bugs (closes #1176, closes #1193, closes #1165) (#1272). ([541578cc](https://github.com/bluefireteam/audioplayers/commit/541578cc50f3856c23c393faa1a71380b3b49222))
+ - **FIX**: revert compileSdkVersion to be compatible with flutter.compileSdkVersion (#1273). ([0b9fed43](https://github.com/bluefireteam/audioplayers/commit/0b9fed43d9dfa90870826dc9a34d1a0d730bd78d))
+ - **FIX**: emit onPositionChanged when seek is completed (closes #1259) (#1265). ([be7ac6a9](https://github.com/bluefireteam/audioplayers/commit/be7ac6a957fccadf5bcecf0f1fbea197d32bda21))
+ - **FIX**: bugs from integration tests (#1247). ([6fad1cc4](https://github.com/bluefireteam/audioplayers/commit/6fad1cc4443e623e5c94519f130b4004b2dc3857))
+ - **FIX**: Fix lowLatency mode for Android (#1193) (#1224). ([a25ca284](https://github.com/bluefireteam/audioplayers/commit/a25ca284835252147c85944575c7e71a3ef6abc4))
+ - **FEAT**: wait for source to be prepared (#1191). ([5eeca894](https://github.com/bluefireteam/audioplayers/commit/5eeca8940e764546023567fa2f6b1bc3802f97d3))
+
+## 1.0.1
+
+ - **FIX**: getDuration, getPosition causes MEDIA_ERROR_UNKNOWN (#1172). ([51b4c73e](https://github.com/bluefireteam/audioplayers/commit/51b4c73eaff5c60d1c3c3e42ae783df07d34be09))
+
+## 1.0.0
+
+ - **FEAT**: Upgrade flame lint dependency (#1132). ([0d6dae3e](https://github.com/bluefireteam/audioplayers/commit/0d6dae3efc4a73abeb554fd0862d64fda0269066))
+
+## 1.0.0-rc.2
+
+## 1.0.0-rc.1
+
+ - First release after federation
+
+# Changelog
+
+## 0.20.2
+- Fix bug with inversed log levels
+
+## 0.20.1
+- Fix enum parsing on release mode on android
+
+## 0.20.0
+- Fix android/kotlin build for old projects
+- Add method to clearNotification
+- Add currentPosition stream on web
+- Add seek on web
+- Add a proper Logger
+- Make setPlaybackRate signature consistent
+- Fix fatal exception on Android API < 21 in WrappedMediaPlayer.kt setAttributes
+- Add clearNotification method
+
+## 0.19.1
+- Add missing awaits for AudioCache
+- Fix Kotlin Core version to v1.6.0
+- Fix iOS warning
+- Fix README link to audio_cache.md to work on pub
+- Fix documentation referencing old class
+- Add web support for audioPlayer.getCurrentPosition
+- Add web support for audioPlayer.getDuration
+- Add web support for audioPlayer.setPlaybackRate
+- Fix local file playback in LOW_LATENCY mode on Android
+
+## 0.19.0
+- Refactor Notifications code (small breaking changes)
+- AudioCache for web
+- Fixing basic features for Android lower than API 23
+- Fixing error after playing music several times with AudioCache
+- Re-organize folder and file structure on the Dart side (project layout)
+- Re-organize folders into a mono-repo
+- Fix several bugs
+
+## 0.18.3
+- Fix Float vs Double mixup on Swift that prevent non-integer values for volume/playback
+- Fix open sink issue / resource leak
+
+## 0.18.2
+- Changing Android minSdk verison to 16
+- Improve build processes and other small bug fixes
+
+## 0.18.1
+- Fix kotlin config issue for some apps
+- Fix warning from pub
+- Fix iOS lock screen
+- Fix setUrl method
+
+## 0.18.0
+- Stable null-safety release
+- Removed all the `@deprecated` code blocks
+
+## 0.17.4
+- Fix java.lang.UnsupportedOperationException on read-only kotlin map
+
+## 0.17.3
+- Backport some code to old kt (for now)
+
+## 0.17.2
+- Fix macos compilation issue
+- Fix android for non-kotlin projects
+
+## 0.17.1
+- Use better algorithm for speed modulation on iOS
+- Extracted and refactored all the notifications code onto the new file
+- Add more checks and make sure notifcations code is not ran when it shouldn't
+- Add more useful info to the troubleshoot guide
+
+## 0.17.0
+- Swift conversion of the darwin code
+
+## 0.16.2
+- Overhauled our contributing guidelines
+- Improve docs around player state
+- Update dependencies versions
+
+## 0.16.1
+- Fix Exception thrown when calling audioPlayer.dispose
+- Fix bug with AudioCache crash on iOS
+
+## 0.16.0
+- Implemented stream routing for iOS
+- Call release on dispose
+- Fix iOS build
+- Breaking change audio cache prefix in order to allow override 'assets'
+
+## audioplayers 0.15.1
+- Fix web for release mode
+
+## audioplayers 0.15.0
+- Improve loop/readme for web support
+- Audio cache support for web
+- Re-adding partial web support
+
+## audioplayers 0.14.3
+- Add next and previous command for ios
+
+## audioplayers 0.14.2
+- Fix pubspec problem because of web file
+
+## audioplayers 0.14.1
+- Adding linter, tests and flutter_driver integration tests to a CI (github actions)
+- Minor fixes to the APIs and documentation
+- Fix restarting the playback of a failed AVPlayerItem
+- Prevent exceptions when null values are passed to notifications center
+- Prevent crash by checking if headlessServiceInitialized before invoking onNotificationBackgroundPlayerStateChanged
+
+## audioplayers 0.14.0
+- Adding macOs support
+- ios:fix lack of seek completion handle
+- ios Delay start fixed
+
+## audioplayers 0.13.7
+- Bump dependencies, improve gitignore
+- Upgrade pubspec pattern
+
+## audioplayers 0.13.6
+- added `setPlaybackRate` feature for Android
+- Automatic detect address is local or remote (thanks, @saeed-golshan)
+
+## audioplayers 0.13.5
+- fixed crash on iOS when `startHeadlessService()` wasn't called on `AudioPlayer` (by @JesseScott)
+
+## audioplayers 0.13.4
+- fixing missing cleanup on hot restart on Android
+- Background notification updates on iOS
+
+## audioplayers 0.13.3
+- audio notification area fixes
+- fix when other apps are playing sounds
+- fix android race condition
+- Support for registering plugin in background enviroment
+- fix typos and docs
+
+## audioplayers 0.13.2
+- Handling plugin dealloc and onTimeInterval crashs (thanks @chedechao111)
+- Audio position update when the audio is paused (thanks @bjornjacobs)
+
+## audioplayers 0.13.1
+- Added stayAwake feature (thanks, @danielR2001)
+- Improved dispose method (thanks, @hugocbpassos)
+- Added getCurrentPosition (thanks, @hariom08)
+- Some bug fixes and small changes
+
+## audioplayers 0.13.0
+- Call onDurationChanged after setUrl() to be consistent with ios version (thanks @subhash279)
+- Adding getDuration feature iOS/Android (thanks @alecorsino)
+
+## audioplayers 0.12.1
+- Fixes bug where the stream handlers were not called due to exception on the handler
+- Proper error message when errors in the dart handler occurs
+
+## audioplayers 0.12.0
+- Update to path_provider 1.1.0
+- Upgrade to Swift 5 in example project setting (thanks @jerryzhoujw)
+
+## audioplayers 0.11.0
+- **Breaking change**. Migrate from the deprecated original Android Support
+ Library to AndroidX. This shouldn't result in any functional changes, but it
+ requires any Android apps using this plugin to [also
+ migrate](https://developer.android.com/jetpack/androidx/migrate) if they're
+ using the original support library.
+
+## audioplayers 0.10.1
+- Seek and play now works with milliseconds instead of second (thanks, @catoldcui and @erickzanardo)
+
+## audioplayers 0.10.0
+- Added a low latency api for android (thanks, @feroult)
+
+## audioplayers 0.9.0
+- Improved callbacks using Streams to allow for multiple subscibers (thanks, @LucasCLuk)
+- Update uuid version to 2.0.0 (thanks, @BeMacized)
+
+## audioplayers 0.8.2
+- Update path_provider version (thanks, @apiraino)
+
+## audioplayers 0.8.1
+- Fix for duration when playing a stream
+- Added respectSilence flag in audioplayers, or isNotification for play methos in audio_cache
+ False by default, to use player for local notification. Silent when device is in silent mode.
+
+## audioplayers 0.8.0
+- Allow setting seek position in play function (thanks @rob-patchett)
+- Get duration from the underlaying asset instead of from AVPlayerItem (thanks @andressade)
+- Adding player state (thanks @renancaraujo)
+- Set the audio session to active (thanks @benwicks)
+- Delay seek operations on Android until player is ready (thanks @jeffmikels)
+
+## audioplayers 0.7.8
+- Fix bug regarding name clash with other plugins (thanks @imtaehyun)
+
+## audioplayers 0.7.7
+- Fix bug when using nested files with audio cache (thanks @hotstu for reporting and @eclewlow for fixing)
+
+## audioplayers 0.7.6
+- Fix the nefarious bug of 'sound only playing through headphones' (thanks so much, @tsun424)
+
+## audioplayers 0.7.5
+- Fix SDK constraint for Dart 2.1 (thanks @snoofer and @sroddy)
+
+## audioplayers 0.7.4
+- Some more fixes to work without errors with Dart 2 stronger types
+
+## audioplayers 0.7.3
+- Support Android SDK 16-20 (thanks, @sroddy)
+- Avoid restarting a looping player if is stopped (thanks, @sroddy)
+
+## audioplayers 0.7.2
+- Bug fixes for iOS
+
+## audioplayers 0.7.1
+- Formatting
+
+## audioplayers 0.7.0
+
+- Improved lifecycle handling for android
+- Big performance boots
+- Allows for finer control of releasing (with setReleaseMode, setUrl, resume, release)
+- Allows for setting the volume at any time (with setVolume)
+- Added LOOP as a ReleaseMode options, making it significantly faster
+- Some other refactorings
+
+## audioplayers 0.6.0
+
+- Major Refactoring!
+- Renaming everything to audioplayers (mind the s)
+- Better logging
+- Added AudioCache (imported from Flame)
+- Adding tests!
+- Adding better example
+- Greatly improving README
+- Lots of other minor tweaks
+
+## audioplayers 0.5.2
+
+- don't call the onClomplete hook when you manually stop the audio
+
+## audioplayers 0.5.1
+
+- fix for dart 2 (thanks to @efortuna)
+
+## audioplayers 0.5.0
+
+- improves Android performance by not calling `prepare` on the main thread
+
+## audioplayers 0.4.1
+
+- fix `seek` for iOS
+
+## audioplayers 0.4.0
+
+- volume controls
+
+## audioplayers 0.3.0
+
+- working on iOS (thanks @feroult <3)
+
+## audioplayers 0.2.0
+
+- adding disable log option
+
+## audioplayers 0.1.0
+
+- support for multiple audios simultaneously
+
+## 0.2.0
+
+- support for local files
+
+## 0.1.0
+
+- update to the current Plugin API
+- move to https://github.com/rxlabz/audioplayer
+
+## 0.0.2
+
+Separated handlers for position, duration, completion and errors
+
+- setDurationHandler(TimeChangeHandler handler)
+- setPositionHandler(TimeChangeHandler handler)
+- setCompletionHandler(VoidCallback callback)
+- setErrorHandler(ErrorHandler handler)
+
+- new typedef
+```dart
+typedef void TimeChangeHandler(Duration duration);
+typedef void ErrorHandler(String message);
+```
+
+## 0.0.1
+
+- first POC :
+ - methods : play, pause, stop
+ - a globalHandler for position, duration, completion and errors
diff --git a/third_party/pub_patches/audioplayers_android/LICENSE b/third_party/pub_patches/audioplayers_android/LICENSE
new file mode 100644
index 000000000..382de332d
--- /dev/null
+++ b/third_party/pub_patches/audioplayers_android/LICENSE
@@ -0,0 +1,21 @@
+MIT License
+
+Copyright (c) 2017 Luan Nico
+
+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.
\ No newline at end of file
diff --git a/third_party/pub_patches/audioplayers_android/README.md b/third_party/pub_patches/audioplayers_android/README.md
new file mode 100644
index 000000000..b16680c0a
--- /dev/null
+++ b/third_party/pub_patches/audioplayers_android/README.md
@@ -0,0 +1,23 @@
+
+
+
+
+
+
+---
+
+# audioplayers_android
+
+
+
+
+
+
+
+The Android implementation of [`audioplayers`](https://pub.dev/packages/audioplayers).
+
+## Usage
+
+This package is [endorsed](https://flutter.dev/docs/development/packages-and-plugins/developing-packages#endorsed-federated-plugin),
+which means you can simply use `audioplayers` normally.
+This package will be automatically included in your app when you do, so you do not need to add it to your `pubspec.yaml`.
diff --git a/third_party/pub_patches/audioplayers_android/analysis_options.yaml b/third_party/pub_patches/audioplayers_android/analysis_options.yaml
new file mode 100644
index 000000000..85732fa02
--- /dev/null
+++ b/third_party/pub_patches/audioplayers_android/analysis_options.yaml
@@ -0,0 +1 @@
+include: package:flame_lint/analysis_options.yaml
diff --git a/third_party/pub_patches/audioplayers_android/android/build.gradle b/third_party/pub_patches/audioplayers_android/android/build.gradle
new file mode 100644
index 000000000..41a562d7e
--- /dev/null
+++ b/third_party/pub_patches/audioplayers_android/android/build.gradle
@@ -0,0 +1,80 @@
+group 'xyz.luan.audioplayers'
+version '1.0-SNAPSHOT'
+
+buildscript {
+ ext.kotlin_version = '1.7.10'
+ ext.coroutines_version = '1.6.4'
+
+ repositories {
+ google()
+ mavenCentral()
+ }
+
+ dependencies {
+ classpath 'com.android.tools.build:gradle:7.3.1'
+ classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
+ classpath "de.mannodermaus.gradle.plugins:android-junit5:1.7.1.1"
+ }
+}
+
+rootProject.allprojects {
+ repositories {
+ google()
+ mavenCentral()
+ }
+}
+
+apply plugin: 'com.android.library'
+apply plugin: 'kotlin-android'
+apply plugin: 'de.mannodermaus.android-junit5'
+
+android {
+ compileSdk 33
+
+ // Conditional for compatibility with AGP <4.2.
+ if (project.android.hasProperty('namespace')) {
+ namespace 'xyz.luan.audioplayers'
+ }
+
+ compileOptions {
+ sourceCompatibility JavaVersion.VERSION_17
+ targetCompatibility JavaVersion.VERSION_17
+ }
+
+ kotlinOptions {
+ jvmTarget = '17'
+ }
+
+ sourceSets {
+ main.java.srcDirs += 'src/main/kotlin'
+ }
+
+ defaultConfig {
+ minSdkVersion 16
+ testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
+ }
+
+ lint {
+ disable 'InvalidPackage'
+ }
+}
+
+allprojects {
+ gradle.projectsEvaluated {
+ tasks.withType(JavaCompile) {
+ options.compilerArgs << "-Xlint:unchecked" << "-Xlint:deprecation"
+ }
+ }
+}
+
+dependencies {
+ implementation "androidx.core:core-ktx:1.9.0" // Do not pump unless dropping support for AGP7
+ implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
+ implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:$coroutines_version"
+ implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:$coroutines_version"
+ testImplementation 'org.junit.jupiter:junit-jupiter:5.9.0'
+ testImplementation 'org.assertj:assertj-core:3.23.1'
+}
+repositories {
+ mavenCentral()
+}
diff --git a/third_party/pub_patches/audioplayers_android/android/gradle.properties b/third_party/pub_patches/audioplayers_android/android/gradle.properties
new file mode 100644
index 000000000..d9cf55df7
--- /dev/null
+++ b/third_party/pub_patches/audioplayers_android/android/gradle.properties
@@ -0,0 +1,2 @@
+org.gradle.jvmargs=-Xmx1536M
+android.useAndroidX=true
diff --git a/third_party/pub_patches/audioplayers_android/android/settings.gradle b/third_party/pub_patches/audioplayers_android/android/settings.gradle
new file mode 100644
index 000000000..096aeb19c
--- /dev/null
+++ b/third_party/pub_patches/audioplayers_android/android/settings.gradle
@@ -0,0 +1 @@
+rootProject.name = 'audioplayers_android'
diff --git a/third_party/pub_patches/audioplayers_android/android/src/main/AndroidManifest.xml b/third_party/pub_patches/audioplayers_android/android/src/main/AndroidManifest.xml
new file mode 100644
index 000000000..0f77d9bf9
--- /dev/null
+++ b/third_party/pub_patches/audioplayers_android/android/src/main/AndroidManifest.xml
@@ -0,0 +1 @@
+
diff --git a/third_party/pub_patches/audioplayers_android/android/src/main/kotlin/xyz/luan/audioplayers/AudioContextAndroid.kt b/third_party/pub_patches/audioplayers_android/android/src/main/kotlin/xyz/luan/audioplayers/AudioContextAndroid.kt
new file mode 100644
index 000000000..eabbb50b9
--- /dev/null
+++ b/third_party/pub_patches/audioplayers_android/android/src/main/kotlin/xyz/luan/audioplayers/AudioContextAndroid.kt
@@ -0,0 +1,69 @@
+package xyz.luan.audioplayers
+
+import android.annotation.SuppressLint
+import android.media.AudioAttributes
+import android.media.AudioAttributes.Builder
+import android.media.AudioAttributes.CONTENT_TYPE_MUSIC
+import android.media.AudioAttributes.USAGE_MEDIA
+import android.media.AudioAttributes.USAGE_NOTIFICATION_RINGTONE
+import android.media.AudioAttributes.USAGE_VOICE_COMMUNICATION
+import android.media.AudioManager
+import android.media.MediaPlayer
+import android.os.Build
+import androidx.annotation.RequiresApi
+import java.util.*
+
+data class AudioContextAndroid(
+ val isSpeakerphoneOn: Boolean,
+ val stayAwake: Boolean,
+ val contentType: Int,
+ val usageType: Int,
+ val audioFocus: Int,
+ val audioMode: Int,
+) {
+ @SuppressLint("InlinedApi") // we are just using numerical constants
+ constructor() : this(
+ isSpeakerphoneOn = false,
+ stayAwake = false,
+ contentType = CONTENT_TYPE_MUSIC,
+ usageType = USAGE_MEDIA,
+ audioFocus = AudioManager.AUDIOFOCUS_GAIN,
+ audioMode = AudioManager.MODE_NORMAL,
+ )
+
+ fun setAttributesOnPlayer(player: MediaPlayer) {
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
+ player.setAudioAttributes(buildAttributes())
+ } else {
+ @Suppress("DEPRECATION")
+ player.setAudioStreamType(getStreamType())
+ }
+ }
+
+ @RequiresApi(Build.VERSION_CODES.LOLLIPOP)
+ fun buildAttributes(): AudioAttributes {
+ return Builder()
+ .setUsage(usageType)
+ .setContentType(contentType)
+ .build()
+ }
+
+ @Deprecated("This is used for Android older than LOLLIPOP", replaceWith = ReplaceWith("buildAttributes"))
+ private fun getStreamType(): Int {
+ return when (usageType) {
+ USAGE_VOICE_COMMUNICATION -> AudioManager.STREAM_VOICE_CALL
+ USAGE_NOTIFICATION_RINGTONE -> AudioManager.STREAM_RING
+ else -> AudioManager.STREAM_MUSIC
+ }
+ }
+
+ override fun hashCode() = Objects.hash(isSpeakerphoneOn, stayAwake, contentType, usageType, audioFocus, audioMode)
+
+ override fun equals(other: Any?) = (other is AudioContextAndroid) &&
+ isSpeakerphoneOn == other.isSpeakerphoneOn &&
+ stayAwake == other.stayAwake &&
+ contentType == other.contentType &&
+ usageType == other.usageType &&
+ audioFocus == other.audioFocus &&
+ audioMode == other.audioMode
+}
diff --git a/third_party/pub_patches/audioplayers_android/android/src/main/kotlin/xyz/luan/audioplayers/AudioplayersPlugin.kt b/third_party/pub_patches/audioplayers_android/android/src/main/kotlin/xyz/luan/audioplayers/AudioplayersPlugin.kt
new file mode 100644
index 000000000..b220aed15
--- /dev/null
+++ b/third_party/pub_patches/audioplayers_android/android/src/main/kotlin/xyz/luan/audioplayers/AudioplayersPlugin.kt
@@ -0,0 +1,385 @@
+package xyz.luan.audioplayers
+
+import android.content.Context
+import android.media.AudioManager
+import android.os.Build
+import android.os.Handler
+import android.os.Looper
+import io.flutter.embedding.engine.plugins.FlutterPlugin
+import io.flutter.embedding.engine.plugins.FlutterPlugin.FlutterPluginBinding
+import io.flutter.plugin.common.BinaryMessenger
+import io.flutter.plugin.common.EventChannel
+import io.flutter.plugin.common.MethodCall
+import io.flutter.plugin.common.MethodChannel
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.cancel
+import kotlinx.coroutines.launch
+import xyz.luan.audioplayers.player.SoundPoolManager
+import xyz.luan.audioplayers.player.WrappedPlayer
+import xyz.luan.audioplayers.source.BytesSource
+import xyz.luan.audioplayers.source.UrlSource
+import java.io.FileNotFoundException
+import java.lang.ref.WeakReference
+import java.util.concurrent.ConcurrentHashMap
+import java.util.concurrent.ConcurrentMap
+
+typealias FlutterHandler = (call: MethodCall, response: MethodChannel.Result) -> Unit
+
+class AudioplayersPlugin : FlutterPlugin, IUpdateCallback {
+ private val mainScope = CoroutineScope(Dispatchers.Main)
+
+ private lateinit var methods: MethodChannel
+ private lateinit var globalMethods: MethodChannel
+ private lateinit var globalEvents: EventHandler
+ private lateinit var context: Context
+ private lateinit var binaryMessenger: BinaryMessenger
+ private lateinit var soundPoolManager: SoundPoolManager
+
+ private val players = ConcurrentHashMap()
+ private val handler = Handler(Looper.getMainLooper())
+ private var updateRunnable: Runnable? = null
+
+ private var defaultAudioContext = AudioContextAndroid()
+
+ override fun onAttachedToEngine(binding: FlutterPluginBinding) {
+ context = binding.applicationContext
+ binaryMessenger = binding.binaryMessenger
+ soundPoolManager = SoundPoolManager(this)
+ methods = MethodChannel(binding.binaryMessenger, "xyz.luan/audioplayers")
+ methods.setMethodCallHandler { call, response -> safeCall(call, response, ::methodHandler) }
+ globalMethods = MethodChannel(binding.binaryMessenger, "xyz.luan/audioplayers.global")
+ globalMethods.setMethodCallHandler { call, response -> safeCall(call, response, ::globalMethodHandler) }
+ updateRunnable = UpdateRunnable(players, methods, handler, this)
+ globalEvents = EventHandler(EventChannel(binding.binaryMessenger, "xyz.luan/audioplayers.global/events"))
+ }
+
+ override fun onDetachedFromEngine(binding: FlutterPluginBinding) {
+ stopUpdates()
+ handler.removeCallbacksAndMessages(null)
+ updateRunnable = null
+ players.values.forEach { it.dispose() }
+ players.clear()
+ mainScope.cancel()
+ soundPoolManager.dispose()
+ globalEvents.dispose()
+ }
+
+ private fun safeCall(
+ call: MethodCall,
+ response: MethodChannel.Result,
+ handler: FlutterHandler,
+ ) {
+ mainScope.launch(Dispatchers.IO) {
+ try {
+ handler(call, response)
+ } catch (e: Exception) {
+ response.error("Unexpected AndroidAudioError", e.message, e)
+ }
+ }
+ }
+
+ private fun globalMethodHandler(call: MethodCall, response: MethodChannel.Result) {
+ when (call.method) {
+ "setAudioContext" -> {
+ val audioManager = getAudioManager()
+ audioManager.mode = defaultAudioContext.audioMode
+ audioManager.isSpeakerphoneOn = defaultAudioContext.isSpeakerphoneOn
+
+ defaultAudioContext = call.audioContext()
+ }
+
+ "emitLog" -> {
+ val message = call.argument("message") ?: error("message is required")
+ handleGlobalLog(message)
+ }
+
+ "emitError" -> {
+ val code = call.argument("code") ?: error("code is required")
+ val message = call.argument("message") ?: error("message is required")
+ handleGlobalError(code, message, null)
+ }
+
+ else -> {
+ response.notImplemented()
+ return
+ }
+ }
+
+ response.success(1)
+ }
+
+ private fun methodHandler(call: MethodCall, response: MethodChannel.Result) {
+ val playerId = call.argument("playerId") ?: return
+ if (call.method == "create") {
+ val eventHandler = EventHandler(EventChannel(binaryMessenger, "xyz.luan/audioplayers/events/$playerId"))
+ players[playerId] = WrappedPlayer(this, eventHandler, defaultAudioContext.copy(), soundPoolManager)
+ response.success(1)
+ return
+ }
+ val player = getPlayer(playerId)
+ try {
+ when (call.method) {
+ "setSourceUrl" -> {
+ val url = call.argument("url") ?: error("url is required")
+ val isLocal = call.argument("isLocal") ?: false
+ try {
+ player.source = UrlSource(url, isLocal)
+ } catch (e: FileNotFoundException) {
+ response.error(
+ "AndroidAudioError",
+ "Failed to set source. For troubleshooting, see: " +
+ "https://github.com/bluefireteam/audioplayers/blob/main/troubleshooting.md",
+ e,
+ )
+ return
+ }
+ }
+
+ "setSourceBytes" -> {
+ val bytes = call.argument("bytes") ?: error("bytes are required")
+ if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) {
+ error("Operation not supported on Android <= M")
+ }
+ player.source = BytesSource(bytes)
+ }
+
+ "resume" -> player.play()
+ "pause" -> player.pause()
+ "stop" -> player.stop()
+ "release" -> player.release()
+ "seek" -> {
+ val position = call.argument("position") ?: error("position is required")
+ player.seek(position)
+ }
+
+ "setVolume" -> {
+ val volume = call.argument("volume") ?: error("volume is required")
+ player.volume = volume.toFloat()
+ }
+
+ "setBalance" -> {
+ val balance = call.argument("balance") ?: error("balance is required")
+ player.balance = balance.toFloat()
+ }
+
+ "setPlaybackRate" -> {
+ val rate = call.argument("playbackRate") ?: error("playbackRate is required")
+ player.rate = rate.toFloat()
+ }
+
+ "getDuration" -> {
+ response.success(player.getDuration())
+ return
+ }
+
+ "getCurrentPosition" -> {
+ response.success(player.getCurrentPosition())
+ return
+ }
+
+ "setReleaseMode" -> {
+ val releaseMode = call.enumArgument("releaseMode") ?: error("releaseMode is required")
+ player.releaseMode = releaseMode
+ }
+
+ "setPlayerMode" -> {
+ val playerMode = call.enumArgument("playerMode") ?: error("playerMode is required")
+ player.playerMode = playerMode
+ }
+
+ "setAudioContext" -> {
+ val audioContext = call.audioContext()
+ player.updateAudioContext(audioContext)
+ }
+
+ "emitLog" -> {
+ val message = call.argument("message") ?: error("message is required")
+ player.handleLog(message)
+ }
+
+ "emitError" -> {
+ val code = call.argument("code") ?: error("code is required")
+ val message = call.argument("message") ?: error("message is required")
+ player.handleError(code, message, null)
+ }
+
+ "dispose" -> {
+ handler.post {
+ player.dispose()
+ players.remove(playerId)
+ }
+ }
+
+ else -> {
+ response.notImplemented()
+ return
+ }
+ }
+ response.success(1)
+ } catch (e: Exception) {
+ response.error("AndroidAudioError", e.message, e)
+ }
+ }
+
+ private fun getPlayer(playerId: String): WrappedPlayer {
+ return players[playerId] ?: error("Player has not yet been created or has already been disposed.")
+ }
+
+ fun getApplicationContext(): Context {
+ return context.applicationContext
+ }
+
+ fun getAudioManager(): AudioManager {
+ return context.applicationContext.getSystemService(Context.AUDIO_SERVICE) as AudioManager
+ }
+
+ fun handleIsPlaying() {
+ startUpdates()
+ }
+
+ fun handleDuration(player: WrappedPlayer) {
+ handler.post {
+ player.eventHandler.success(
+ "audio.onDuration",
+ hashMapOf("value" to (player.getDuration() ?: 0)),
+ )
+ }
+ }
+
+ fun handleComplete(player: WrappedPlayer) {
+ handler.post { player.eventHandler.success("audio.onComplete") }
+ }
+
+ fun handlePrepared(player: WrappedPlayer, isPrepared: Boolean) {
+ handler.post { player.eventHandler.success("audio.onPrepared", hashMapOf("value" to isPrepared)) }
+ }
+
+ fun handleLog(player: WrappedPlayer, message: String) {
+ handler.post { player.eventHandler.success("audio.onLog", hashMapOf("value" to message)) }
+ }
+
+ fun handleGlobalLog(message: String) {
+ handler.post { globalEvents.success("audio.onLog", hashMapOf("value" to message)) }
+ }
+
+ fun handleError(player: WrappedPlayer, errorCode: String?, errorMessage: String?, errorDetails: Any?) {
+ handler.post { player.eventHandler.error(errorCode, errorMessage, errorDetails) }
+ }
+
+ fun handleGlobalError(errorCode: String?, errorMessage: String?, errorDetails: Any?) {
+ handler.post { globalEvents.error(errorCode, errorMessage, errorDetails) }
+ }
+
+ fun handleSeekComplete(player: WrappedPlayer) {
+ handler.post {
+ player.eventHandler.success("audio.onSeekComplete")
+ player.eventHandler.success(
+ "audio.onCurrentPosition",
+ hashMapOf("value" to (player.getCurrentPosition() ?: 0)),
+ )
+ }
+ }
+
+ override fun startUpdates() {
+ updateRunnable?.let { handler.post(it) }
+ }
+
+ override fun stopUpdates() {
+ updateRunnable?.let { handler.removeCallbacks(it) }
+ }
+
+ private class UpdateRunnable(
+ mediaPlayers: ConcurrentMap,
+ methodChannel: MethodChannel,
+ handler: Handler,
+ updateCallback: IUpdateCallback,
+ ) : Runnable {
+ private val mediaPlayers = WeakReference(mediaPlayers)
+ private val methodChannel = WeakReference(methodChannel)
+ private val handler = WeakReference(handler)
+ private val updateCallback = WeakReference(updateCallback)
+
+ override fun run() {
+ val mediaPlayers = mediaPlayers.get()
+ val channel = methodChannel.get()
+ val handler = handler.get()
+ val updateCallback = updateCallback.get()
+ if (mediaPlayers == null || channel == null || handler == null || updateCallback == null) {
+ updateCallback?.stopUpdates()
+ return
+ }
+ var isAnyPlaying = false
+ for (player in mediaPlayers.values) {
+ if (!player.isActuallyPlaying()) {
+ continue
+ }
+ isAnyPlaying = true
+ val time = player.getCurrentPosition()
+ player.eventHandler.success("audio.onCurrentPosition", hashMapOf("value" to (time ?: 0)))
+ }
+ if (isAnyPlaying) {
+ handler.postDelayed(this, 200)
+ } else {
+ updateCallback.stopUpdates()
+ }
+ }
+ }
+}
+
+private interface IUpdateCallback {
+ fun stopUpdates()
+ fun startUpdates()
+}
+
+private inline fun > MethodCall.enumArgument(name: String): T? {
+ val enumName = argument(name) ?: return null
+ return enumValueOf(enumName.split('.').last().toConstantCase())
+}
+
+fun String.toConstantCase(): String {
+ return replace(Regex("(.)(\\p{Upper})"), "$1_$2").replace(Regex("(.) (.)"), "$1_$2").uppercase()
+}
+
+private fun MethodCall.audioContext(): AudioContextAndroid {
+ return AudioContextAndroid(
+ isSpeakerphoneOn = argument("isSpeakerphoneOn") ?: error("isSpeakerphoneOn is required"),
+ stayAwake = argument("stayAwake") ?: error("stayAwake is required"),
+ contentType = argument("contentType") ?: error("contentType is required"),
+ usageType = argument("usageType") ?: error("usageType is required"),
+ audioFocus = argument("audioFocus") ?: error("audioFocus is required"),
+ audioMode = argument("audioMode") ?: error("audioMode is required"),
+ )
+}
+
+class EventHandler(private val eventChannel: EventChannel) : EventChannel.StreamHandler {
+ private var eventSink: EventChannel.EventSink? = null
+
+ init {
+ eventChannel.setStreamHandler(this)
+ }
+
+ override fun onListen(arguments: Any?, events: EventChannel.EventSink?) {
+ eventSink = events
+ }
+
+ override fun onCancel(arguments: Any?) {
+ eventSink = null
+ }
+
+ fun success(method: String, arguments: Map = HashMap()) {
+ eventSink?.success(arguments + Pair("event", method))
+ }
+
+ fun error(errorCode: String?, errorMessage: String?, errorDetails: Any?) {
+ eventSink?.error(errorCode, errorMessage, errorDetails)
+ }
+
+ fun dispose() {
+ eventSink?.let {
+ it.endOfStream()
+ onCancel(null)
+ }
+ eventChannel.setStreamHandler(null)
+ }
+}
diff --git a/third_party/pub_patches/audioplayers_android/android/src/main/kotlin/xyz/luan/audioplayers/ByteDataSource.kt b/third_party/pub_patches/audioplayers_android/android/src/main/kotlin/xyz/luan/audioplayers/ByteDataSource.kt
new file mode 100644
index 000000000..1a59f4e53
--- /dev/null
+++ b/third_party/pub_patches/audioplayers_android/android/src/main/kotlin/xyz/luan/audioplayers/ByteDataSource.kt
@@ -0,0 +1,35 @@
+package xyz.luan.audioplayers
+
+import android.media.MediaDataSource
+import android.os.Build
+import androidx.annotation.RequiresApi
+
+@RequiresApi(Build.VERSION_CODES.M)
+class ByteDataSource(
+ private val data: ByteArray,
+) : MediaDataSource() {
+ @Synchronized
+ override fun getSize(): Long = data.size.toLong()
+
+ @Synchronized
+ override fun close() = Unit
+
+ @Synchronized
+ override fun readAt(position: Long, buffer: ByteArray, offset: Int, size: Int): Int {
+ if (position >= data.size) {
+ return -1
+ }
+
+ val remainingSize = computeRemainingSize(size, position)
+ System.arraycopy(data, position.toInt(), buffer, offset, remainingSize)
+ return remainingSize
+ }
+
+ private fun computeRemainingSize(size: Int, position: Long): Int {
+ var remainingSize = size.toLong()
+ if (position + remainingSize > data.size) {
+ remainingSize -= position + remainingSize - data.size
+ }
+ return remainingSize.toInt()
+ }
+}
diff --git a/third_party/pub_patches/audioplayers_android/android/src/main/kotlin/xyz/luan/audioplayers/PlayerMode.kt b/third_party/pub_patches/audioplayers_android/android/src/main/kotlin/xyz/luan/audioplayers/PlayerMode.kt
new file mode 100644
index 000000000..d2a249e29
--- /dev/null
+++ b/third_party/pub_patches/audioplayers_android/android/src/main/kotlin/xyz/luan/audioplayers/PlayerMode.kt
@@ -0,0 +1,5 @@
+package xyz.luan.audioplayers
+
+enum class PlayerMode {
+ MEDIA_PLAYER, LOW_LATENCY
+}
diff --git a/third_party/pub_patches/audioplayers_android/android/src/main/kotlin/xyz/luan/audioplayers/ReleaseMode.kt b/third_party/pub_patches/audioplayers_android/android/src/main/kotlin/xyz/luan/audioplayers/ReleaseMode.kt
new file mode 100644
index 000000000..cadb54b37
--- /dev/null
+++ b/third_party/pub_patches/audioplayers_android/android/src/main/kotlin/xyz/luan/audioplayers/ReleaseMode.kt
@@ -0,0 +1,5 @@
+package xyz.luan.audioplayers
+
+enum class ReleaseMode {
+ RELEASE, LOOP, STOP
+}
diff --git a/third_party/pub_patches/audioplayers_android/android/src/main/kotlin/xyz/luan/audioplayers/player/FocusManager.kt b/third_party/pub_patches/audioplayers_android/android/src/main/kotlin/xyz/luan/audioplayers/player/FocusManager.kt
new file mode 100644
index 000000000..0b8e5115e
--- /dev/null
+++ b/third_party/pub_patches/audioplayers_android/android/src/main/kotlin/xyz/luan/audioplayers/player/FocusManager.kt
@@ -0,0 +1,75 @@
+package xyz.luan.audioplayers.player
+
+import android.media.AudioFocusRequest
+import android.media.AudioManager
+import android.os.Build
+import androidx.annotation.RequiresApi
+import xyz.luan.audioplayers.AudioContextAndroid
+
+class FocusManager(
+ private val player: WrappedPlayer,
+) {
+ private var audioFocusChangeListener: AudioManager.OnAudioFocusChangeListener? = null
+ private var audioFocusRequest: AudioFocusRequest? = null
+
+ private val context: AudioContextAndroid
+ get() = player.context
+
+ private val audioManager: AudioManager
+ get() = player.audioManager
+
+ fun maybeRequestAudioFocus(andThen: () -> Unit) {
+ if (context.audioFocus == AudioManager.AUDIOFOCUS_NONE) {
+ andThen()
+ } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
+ newRequestAudioFocus(andThen)
+ } else {
+ @Suppress("DEPRECATION")
+ oldRequestAudioFocus(andThen)
+ }
+ }
+
+ fun handleStop() {
+ if (context.audioFocus != AudioManager.AUDIOFOCUS_NONE) {
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
+ audioFocusRequest?.let { audioManager.abandonAudioFocusRequest(it) }
+ } else {
+ @Suppress("DEPRECATION")
+ audioManager.abandonAudioFocus(audioFocusChangeListener)
+ }
+ }
+ }
+
+ @RequiresApi(Build.VERSION_CODES.O)
+ private fun newRequestAudioFocus(andThen: () -> Unit) {
+ val audioFocus = context.audioFocus
+
+ val audioFocusRequest = AudioFocusRequest.Builder(audioFocus)
+ .setAudioAttributes(context.buildAttributes())
+ .setOnAudioFocusChangeListener { handleFocusResult(it, andThen) }
+ .build()
+ this.audioFocusRequest = audioFocusRequest
+
+ val result = audioManager.requestAudioFocus(audioFocusRequest)
+ handleFocusResult(result, andThen)
+ }
+
+ @Deprecated("Use requestAudioFocus instead")
+ private fun oldRequestAudioFocus(andThen: () -> Unit) {
+ val audioFocus = context.audioFocus
+ audioFocusChangeListener = AudioManager.OnAudioFocusChangeListener { handleFocusResult(it, andThen) }
+ @Suppress("DEPRECATION")
+ val result = audioManager.requestAudioFocus(
+ audioFocusChangeListener,
+ AudioManager.STREAM_MUSIC,
+ audioFocus,
+ )
+ handleFocusResult(result, andThen)
+ }
+
+ private fun handleFocusResult(result: Int, andThen: () -> Unit) {
+ if (result == AudioManager.AUDIOFOCUS_REQUEST_GRANTED) {
+ andThen()
+ }
+ }
+}
diff --git a/third_party/pub_patches/audioplayers_android/android/src/main/kotlin/xyz/luan/audioplayers/player/MediaPlayerPlayer.kt b/third_party/pub_patches/audioplayers_android/android/src/main/kotlin/xyz/luan/audioplayers/player/MediaPlayerPlayer.kt
new file mode 100644
index 000000000..9cff7e389
--- /dev/null
+++ b/third_party/pub_patches/audioplayers_android/android/src/main/kotlin/xyz/luan/audioplayers/player/MediaPlayerPlayer.kt
@@ -0,0 +1,103 @@
+package xyz.luan.audioplayers.player
+
+import android.media.MediaPlayer
+import android.os.Build
+import android.os.PowerManager
+import xyz.luan.audioplayers.AudioContextAndroid
+import xyz.luan.audioplayers.source.Source
+
+class MediaPlayerPlayer(
+ private val wrappedPlayer: WrappedPlayer,
+) : Player {
+ private val mediaPlayer = createMediaPlayer(wrappedPlayer)
+
+ private fun createMediaPlayer(wrappedPlayer: WrappedPlayer): MediaPlayer {
+ val mediaPlayer = MediaPlayer().apply {
+ setOnPreparedListener { wrappedPlayer.onPrepared() }
+ setOnCompletionListener { wrappedPlayer.onCompletion() }
+ setOnSeekCompleteListener { wrappedPlayer.onSeekComplete() }
+ setOnErrorListener { _, what, extra -> wrappedPlayer.onError(what, extra) }
+ setOnBufferingUpdateListener { _, percent -> wrappedPlayer.onBuffering(percent) }
+ }
+ wrappedPlayer.context.setAttributesOnPlayer(mediaPlayer)
+ return mediaPlayer
+ }
+
+ override fun getDuration(): Int? {
+ // media player returns -1 if the duration is unknown
+ return mediaPlayer.duration.takeUnless { it == -1 }
+ }
+
+ override fun getCurrentPosition(): Int {
+ return mediaPlayer.currentPosition
+ }
+
+ override fun isActuallyPlaying(): Boolean {
+ return mediaPlayer.isPlaying
+ }
+
+ override fun setVolume(leftVolume: Float, rightVolume: Float) {
+ mediaPlayer.setVolume(leftVolume, rightVolume)
+ }
+
+ override fun setRate(rate: Float) {
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
+ mediaPlayer.playbackParams = mediaPlayer.playbackParams.setSpeed(rate)
+ } else if (rate == 1.0f) {
+ mediaPlayer.start()
+ } else {
+ error("Changing the playback rate is only available for Android M/23+ or using LOW_LATENCY mode.")
+ }
+ }
+
+ override fun setSource(source: Source) {
+ reset()
+ source.setForMediaPlayer(mediaPlayer)
+ }
+
+ override fun setLooping(looping: Boolean) {
+ mediaPlayer.isLooping = looping
+ }
+
+ override fun start() {
+ // Setting playback rate instead of mediaPlayer.start().
+ setRate(wrappedPlayer.rate)
+ }
+
+ override fun pause() {
+ mediaPlayer.pause()
+ }
+
+ override fun stop() {
+ mediaPlayer.stop()
+ }
+
+ override fun release() {
+ mediaPlayer.reset()
+ mediaPlayer.release()
+ }
+
+ override fun seekTo(position: Int) {
+ mediaPlayer.seekTo(position)
+ }
+
+ override fun updateContext(context: AudioContextAndroid) {
+ context.setAttributesOnPlayer(mediaPlayer)
+ if (context.stayAwake) {
+ mediaPlayer.setWakeMode(wrappedPlayer.applicationContext, PowerManager.PARTIAL_WAKE_LOCK)
+ }
+ }
+
+ override fun prepare() {
+ mediaPlayer.prepareAsync()
+ }
+
+ override fun reset() {
+ mediaPlayer.reset()
+ }
+
+ override fun isLiveStream(): Boolean {
+ val duration = getDuration()
+ return duration == null || duration == 0
+ }
+}
diff --git a/third_party/pub_patches/audioplayers_android/android/src/main/kotlin/xyz/luan/audioplayers/player/Player.kt b/third_party/pub_patches/audioplayers_android/android/src/main/kotlin/xyz/luan/audioplayers/player/Player.kt
new file mode 100644
index 000000000..db7df08b0
--- /dev/null
+++ b/third_party/pub_patches/audioplayers_android/android/src/main/kotlin/xyz/luan/audioplayers/player/Player.kt
@@ -0,0 +1,26 @@
+package xyz.luan.audioplayers.player
+
+import xyz.luan.audioplayers.AudioContextAndroid
+import xyz.luan.audioplayers.source.Source
+
+interface Player {
+ fun getDuration(): Int?
+ fun getCurrentPosition(): Int?
+ fun isActuallyPlaying(): Boolean
+ fun isLiveStream(): Boolean
+
+ fun start()
+ fun pause()
+ fun stop()
+ fun seekTo(position: Int)
+ fun release()
+
+ fun setVolume(leftVolume: Float, rightVolume: Float)
+ fun setRate(rate: Float)
+ fun setLooping(looping: Boolean)
+ fun updateContext(context: AudioContextAndroid)
+ fun setSource(source: Source)
+
+ fun prepare()
+ fun reset()
+}
diff --git a/third_party/pub_patches/audioplayers_android/android/src/main/kotlin/xyz/luan/audioplayers/player/SoundPoolPlayer.kt b/third_party/pub_patches/audioplayers_android/android/src/main/kotlin/xyz/luan/audioplayers/player/SoundPoolPlayer.kt
new file mode 100644
index 000000000..4205bef6b
--- /dev/null
+++ b/third_party/pub_patches/audioplayers_android/android/src/main/kotlin/xyz/luan/audioplayers/player/SoundPoolPlayer.kt
@@ -0,0 +1,296 @@
+package xyz.luan.audioplayers.player
+
+import android.media.AudioAttributes
+import android.media.AudioManager
+import android.media.SoundPool
+import android.os.Build
+import xyz.luan.audioplayers.AudioContextAndroid
+import xyz.luan.audioplayers.AudioplayersPlugin
+import xyz.luan.audioplayers.source.Source
+import xyz.luan.audioplayers.source.UrlSource
+import java.util.Collections.synchronizedMap
+
+/** Value should not exceed 32 */
+// TODO(luan): make this configurable
+private const val MAX_STREAMS = 32
+
+class SoundPoolPlayer(
+ val wrappedPlayer: WrappedPlayer,
+ private val soundPoolManager: SoundPoolManager,
+) : Player {
+
+ /** The id of the sound of source which will be played */
+ var soundId: Int? = null
+
+ /** The id of the stream / player */
+ private var streamId: Int? = null
+
+ private var audioContext = wrappedPlayer.context
+ set(value) {
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
+ // AudioAttributes are compared by its property values.
+ if (field.buildAttributes() != value.buildAttributes()) {
+ release()
+ soundPoolManager.createSoundPoolWrapper(MAX_STREAMS, value)
+ soundPoolWrapper = soundPoolManager.getSoundPoolWrapper(value)
+ ?: error("Could not create SoundPool $value")
+ }
+ }
+ field = value
+ }
+
+ private var soundPoolWrapper: SoundPoolWrapper
+
+ private val soundPool: SoundPool
+ get() = soundPoolWrapper.soundPool
+
+ init {
+ soundPoolManager.createSoundPoolWrapper(MAX_STREAMS, audioContext)
+ soundPoolWrapper = soundPoolManager.getSoundPoolWrapper(audioContext)
+ ?: error("Could not create SoundPool $audioContext")
+ }
+
+ override fun stop() {
+ streamId?.let {
+ soundPool.stop(it)
+ streamId = null
+ }
+ }
+
+ override fun release() {
+ stop()
+ val soundId = this.soundId ?: return
+ val urlSource = this.urlSource ?: return
+
+ synchronized(soundPoolWrapper.urlToPlayers) {
+ val playersForSoundId = soundPoolWrapper.urlToPlayers[urlSource] ?: return
+ if (playersForSoundId.singleOrNull() === this) {
+ soundPoolWrapper.urlToPlayers.remove(urlSource)
+ soundPool.unload(soundId)
+ soundPoolWrapper.soundIdToPlayer.remove(soundId)
+ wrappedPlayer.handleLog("unloaded soundId $soundId")
+ } else {
+ // This is not the last player using the soundId, just remove it from the list.
+ playersForSoundId.remove(this)
+ }
+ this.soundId = null
+ this.urlSource = null
+ }
+ }
+
+ override fun pause() {
+ streamId?.let { soundPool.pause(it) }
+ }
+
+ override fun updateContext(context: AudioContextAndroid) {
+ audioContext = context
+ }
+
+ override fun setSource(source: Source) {
+ source.setForSoundPool(this)
+ }
+
+ var urlSource: UrlSource? = null
+ set(value) {
+ if (value != null) {
+ synchronized(soundPoolWrapper.urlToPlayers) {
+ val urlPlayers = soundPoolWrapper.urlToPlayers.getOrPut(value) { mutableListOf() }
+ val originalPlayer = urlPlayers.firstOrNull()
+
+ if (originalPlayer != null) {
+ // Sound has already been loaded - reuse the soundId.
+ val prepared = originalPlayer.wrappedPlayer.prepared
+ wrappedPlayer.prepared = prepared
+ soundId = originalPlayer.soundId
+ wrappedPlayer.handleLog("Reusing soundId $soundId for $value is prepared=$prepared $this")
+ } else {
+ // First one for this URL - load it.
+ val start = System.currentTimeMillis()
+
+ wrappedPlayer.prepared = false
+ wrappedPlayer.handleLog("Fetching actual URL for $value")
+ val actualUrl = value.getAudioPathForSoundPool()
+ wrappedPlayer.handleLog("Now loading $actualUrl")
+ val intSoundId = soundPool.load(actualUrl, 1)
+ soundPoolWrapper.soundIdToPlayer[intSoundId] = this
+ soundId = intSoundId
+
+ wrappedPlayer.handleLog(
+ "time to call load() for $value: ${System.currentTimeMillis() - start} player=$this",
+ )
+ }
+ urlPlayers.add(this)
+ }
+ }
+ field = value
+ }
+
+ override fun setVolume(leftVolume: Float, rightVolume: Float) {
+ streamId?.let { soundPool.setVolume(it, leftVolume, rightVolume) }
+ }
+
+ override fun setRate(rate: Float) {
+ streamId?.let { soundPool.setRate(it, rate) }
+ }
+
+ override fun setLooping(looping: Boolean) {
+ streamId?.let { soundPool.setLoop(it, looping.loopModeInteger()) }
+ }
+
+ // Cannot get duration for Sound Pool
+ override fun getDuration() = null
+
+ // Cannot get current position for Sound Pool
+ override fun getCurrentPosition() = null
+
+ override fun isActuallyPlaying() = false
+
+ override fun seekTo(position: Int) {
+ if (position == 0) {
+ streamId?.let {
+ stop()
+ if (wrappedPlayer.playing) {
+ soundPool.resume(it)
+ }
+ }
+ } else {
+ unsupportedOperation("seek")
+ }
+ }
+
+ override fun start() {
+ val streamId = streamId
+ val soundId = soundId
+
+ if (streamId != null) {
+ soundPool.resume(streamId)
+ } else if (soundId != null) {
+ this.streamId = soundPool.play(
+ soundId,
+ wrappedPlayer.volume,
+ wrappedPlayer.volume,
+ 0,
+ wrappedPlayer.isLooping.loopModeInteger(),
+ wrappedPlayer.rate,
+ )
+ }
+ }
+
+ override fun prepare() {
+ // sound pool automatically prepares when source URL is set
+ }
+
+ override fun reset() {
+ // TODO(luan): what do I do here?
+ }
+
+ override fun isLiveStream() = false
+
+ /** Integer representation of the loop mode used by Android */
+ private fun Boolean.loopModeInteger(): Int = if (this) -1 else 0
+
+ private fun unsupportedOperation(message: String): Nothing {
+ throw UnsupportedOperationException("LOW_LATENCY mode does not support: $message")
+ }
+}
+
+class SoundPoolManager(
+ private val ref: AudioplayersPlugin,
+) {
+
+ // Only needed for legacy apps with SDK < 21
+ private var legacySoundPoolWrapper: SoundPoolWrapper? = null
+
+ /**
+ * Lazy store one [SoundPoolWrapper] for each [AudioAttributes] configuration.
+ * [AudioAttributes] are compared by its property values, so it can be used as key.
+ */
+ private val soundPoolWrappers = HashMap()
+
+ /**
+ * Create a SoundPoolWrapper with the given [maxStreams] and the according [audioContext] and save it to be
+ * globally accessible for every player.
+ *
+ * @param maxStreams the maximum number of simultaneous streams for this
+ * SoundPool object, see [SoundPool.Builder.setMaxStreams]
+ */
+ fun createSoundPoolWrapper(maxStreams: Int, audioContext: AudioContextAndroid) {
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
+ val attrs = audioContext.buildAttributes()
+ if (!soundPoolWrappers.containsKey(attrs)) {
+ val soundPool = SoundPool.Builder()
+ .setAudioAttributes(attrs)
+ .setMaxStreams(maxStreams)
+ .build()
+ ref.handleGlobalLog("Create SoundPool with $attrs")
+ val soundPoolWrapper = SoundPoolWrapper(soundPool)
+ soundPoolWrapper.soundPool.setOnLoadCompleteListener { _, sampleId, _ ->
+ ref.handleGlobalLog("Loaded $sampleId")
+ val loadingPlayer = soundPoolWrapper.soundIdToPlayer[sampleId]
+ val urlSource = loadingPlayer?.urlSource
+ if (urlSource != null) {
+ soundPoolWrapper.soundIdToPlayer.remove(loadingPlayer.soundId)
+ // Now mark all players using this sound as not loading and start them if necessary
+ synchronized(soundPoolWrapper.urlToPlayers) {
+ val urlPlayers = soundPoolWrapper.urlToPlayers[urlSource] ?: listOf()
+ for (player in urlPlayers) {
+ player.wrappedPlayer.handleLog("Marking $player as loaded")
+ player.wrappedPlayer.prepared = true
+ if (player.wrappedPlayer.playing) {
+ player.wrappedPlayer.handleLog("Delayed start of $player")
+ player.start()
+ }
+ }
+ }
+ }
+ }
+ soundPoolWrappers[attrs] = soundPoolWrapper
+ }
+ } else if (legacySoundPoolWrapper == null) {
+ @Suppress("DEPRECATION")
+ val soundPool = SoundPool(maxStreams, AudioManager.STREAM_MUSIC, 0)
+ ref.handleGlobalLog("Create legacy SoundPool")
+ legacySoundPoolWrapper = SoundPoolWrapper(soundPool)
+ }
+ }
+
+ /**
+ * Get the [SoundPoolWrapper] with the given [audioContext].
+ */
+ fun getSoundPoolWrapper(audioContext: AudioContextAndroid): SoundPoolWrapper? {
+ return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
+ val attrs = audioContext.buildAttributes()
+ soundPoolWrappers[attrs]
+ } else {
+ legacySoundPoolWrapper
+ }
+ }
+
+ fun dispose() {
+ for (soundPoolEntry in soundPoolWrappers) {
+ soundPoolEntry.value.dispose()
+ }
+ soundPoolWrappers.clear()
+ }
+}
+
+class SoundPoolWrapper(val soundPool: SoundPool) {
+
+ /** For the onLoadComplete listener, track which sound id is associated with which player. An entry only exists until
+ * it has been loaded.
+ */
+ val soundIdToPlayer: MutableMap = synchronizedMap(mutableMapOf())
+
+ /** This is to keep track of the players which share the same sound id, referenced by url. When a player release()s, it
+ * is removed from the associated player list. The last player to be removed actually unloads() the sound id and then
+ * the url is removed from this map.
+ */
+ val urlToPlayers: MutableMap> =
+ synchronizedMap(mutableMapOf>())
+
+ fun dispose() {
+ soundPool.release()
+ soundIdToPlayer.clear()
+ urlToPlayers.clear()
+ }
+}
diff --git a/third_party/pub_patches/audioplayers_android/android/src/main/kotlin/xyz/luan/audioplayers/player/WrappedPlayer.kt b/third_party/pub_patches/audioplayers_android/android/src/main/kotlin/xyz/luan/audioplayers/player/WrappedPlayer.kt
new file mode 100644
index 000000000..183ea2923
--- /dev/null
+++ b/third_party/pub_patches/audioplayers_android/android/src/main/kotlin/xyz/luan/audioplayers/player/WrappedPlayer.kt
@@ -0,0 +1,384 @@
+package xyz.luan.audioplayers.player
+
+import android.content.Context
+import android.media.AudioManager
+import android.media.MediaPlayer
+import xyz.luan.audioplayers.AudioContextAndroid
+import xyz.luan.audioplayers.AudioplayersPlugin
+import xyz.luan.audioplayers.EventHandler
+import xyz.luan.audioplayers.PlayerMode
+import xyz.luan.audioplayers.PlayerMode.LOW_LATENCY
+import xyz.luan.audioplayers.PlayerMode.MEDIA_PLAYER
+import xyz.luan.audioplayers.ReleaseMode
+import xyz.luan.audioplayers.source.Source
+import kotlin.math.min
+
+// For some reason this cannot be accessed from MediaPlayer.MEDIA_ERROR_SYSTEM
+private const val MEDIA_ERROR_SYSTEM = -2147483648
+
+class WrappedPlayer internal constructor(
+ private val ref: AudioplayersPlugin,
+ val eventHandler: EventHandler,
+ var context: AudioContextAndroid,
+ private val soundPoolManager: SoundPoolManager,
+) {
+ private var player: Player? = null
+
+ var source: Source? = null
+ set(value) {
+ if (field != value) {
+ if (value != null) {
+ val player = getOrCreatePlayer()
+ player.setSource(value)
+ player.configAndPrepare()
+ } else {
+ released = true
+ prepared = false
+ playing = false
+ player?.release()
+ }
+ field = value
+ } else {
+ ref.handlePrepared(this, true)
+ }
+ }
+
+ var volume = 1.0f
+ set(value) {
+ if (field != value) {
+ field = value
+ if (!released) {
+ player?.setVolumeAndBalance(value, balance)
+ }
+ }
+ }
+
+ var balance = 0.0f
+ set(value) {
+ if (field != value) {
+ field = value
+ if (!released) {
+ player?.setVolumeAndBalance(volume, value)
+ }
+ }
+ }
+
+ var rate = 1.0f
+ set(value) {
+ if (field != value) {
+ field = value
+ if (playing) {
+ player?.setRate(value)
+ }
+ }
+ }
+
+ var releaseMode = ReleaseMode.RELEASE
+ set(value) {
+ if (field != value) {
+ field = value
+ if (!released) {
+ player?.setLooping(isLooping)
+ }
+ }
+ }
+
+ val isLooping: Boolean
+ get() = releaseMode == ReleaseMode.LOOP
+
+ var playerMode: PlayerMode = MEDIA_PLAYER
+ set(value) {
+ if (field != value) {
+ field = value
+
+ // if the player exists, we need to re-create it from scratch;
+ // this will probably cause music to pause for a second
+ player?.let {
+ shouldSeekTo = maybeGetCurrentPosition()
+ prepared = false
+ it.release()
+ }
+ initPlayer()
+ }
+ }
+
+ var released = true
+
+ var prepared: Boolean = false
+ set(value) {
+ if (field != value) {
+ field = value
+ ref.handlePrepared(this, value)
+ }
+ }
+
+ var playing = false
+ var shouldSeekTo = -1
+
+ private val focusManager = FocusManager(this)
+
+ private fun maybeGetCurrentPosition(): Int {
+ // for Sound Pool, we can't get current position, so we just start over
+ return runCatching { player?.getCurrentPosition().takeUnless { it == 0 } }.getOrNull() ?: -1
+ }
+
+ private fun getOrCreatePlayer(): Player {
+ val currentPlayer = player
+ return if (released || currentPlayer == null) {
+ createPlayer().also {
+ player = it
+ released = false
+ }
+ } else if (prepared) {
+ currentPlayer.also {
+ it.reset()
+ prepared = false
+ }
+ } else {
+ currentPlayer
+ }
+ }
+
+ fun updateAudioContext(audioContext: AudioContextAndroid) {
+ if (context == audioContext) {
+ return
+ }
+ if (context.audioFocus != AudioManager.AUDIOFOCUS_NONE &&
+ audioContext.audioFocus == AudioManager.AUDIOFOCUS_NONE
+ ) {
+ focusManager.handleStop()
+ }
+ this.context = audioContext.copy()
+
+ // AudioManager values are set globally
+ audioManager.mode = context.audioMode
+ audioManager.isSpeakerphoneOn = context.isSpeakerphoneOn
+
+ player?.let { p ->
+ p.stop()
+ prepared = false
+ // Context is only applied, once the player.reset() was called
+ p.updateContext(context)
+ source?.let {
+ p.setSource(it)
+ p.configAndPrepare()
+ }
+ }
+ }
+
+ // Getters
+
+ /**
+ * Returns the duration of the media in milliseconds, if available.
+ */
+ fun getDuration(): Int? {
+ return if (prepared) player?.getDuration() else null
+ }
+
+ /**
+ * Returns the current position of the playback in milliseconds, if available.
+ */
+ fun getCurrentPosition(): Int? {
+ return if (prepared) player?.getCurrentPosition() else null
+ }
+
+ fun isActuallyPlaying(): Boolean {
+ return playing && prepared && player?.isActuallyPlaying() == true
+ }
+
+ val applicationContext: Context
+ get() = ref.getApplicationContext()
+
+ val audioManager: AudioManager
+ get() = ref.getAudioManager()
+
+ /**
+ * Playback handling methods
+ */
+ fun play() {
+ focusManager.maybeRequestAudioFocus(andThen = ::actuallyPlay)
+ }
+
+ private fun actuallyPlay() {
+ if (!playing && !released) {
+ val currentPlayer = player
+ playing = true
+ if (currentPlayer == null) {
+ initPlayer()
+ } else if (prepared) {
+ currentPlayer.start()
+ ref.handleIsPlaying()
+ }
+ }
+ }
+
+ fun stop() {
+ focusManager.handleStop()
+ if (released) {
+ return
+ }
+ if (releaseMode != ReleaseMode.RELEASE) {
+ pause()
+ if (prepared) {
+ if (player?.isLiveStream() == true) {
+ player?.stop()
+ prepared = false
+ player?.prepare()
+ } else {
+ // MediaPlayer does not allow to call player.seekTo after calling player.stop
+ seek(0)
+ }
+ }
+ } else {
+ release()
+ }
+ }
+
+ fun release() {
+ focusManager.handleStop()
+ if (released) {
+ return
+ }
+ if (playing) {
+ player?.stop()
+ }
+
+ // Setting source to null will reset released, prepared and playing
+ // and also calls player.release()
+ source = null
+ player = null
+ }
+
+ fun pause() {
+ if (playing) {
+ playing = false
+ if (prepared) {
+ player?.pause()
+ }
+ }
+ }
+
+ // seek operations cannot be called until after
+ // the player is ready.
+ fun seek(position: Int) {
+ shouldSeekTo = if (prepared && player?.isLiveStream() != true) {
+ player?.seekTo(position)
+ -1
+ } else {
+ position
+ }
+ }
+
+ /**
+ * Player callbacks
+ */
+ fun onPrepared() {
+ prepared = true
+ ref.handleDuration(this)
+ if (playing) {
+ player?.start()
+ ref.handleIsPlaying()
+ }
+ if (shouldSeekTo >= 0 && player?.isLiveStream() != true) {
+ player?.seekTo(shouldSeekTo)
+ }
+ }
+
+ fun onCompletion() {
+ if (releaseMode != ReleaseMode.LOOP) {
+ stop()
+ }
+ ref.handleComplete(this)
+ }
+
+ @Suppress("UNUSED_PARAMETER")
+ fun onBuffering(percent: Int) {
+ // TODO(luan): expose this as a stream
+ }
+
+ fun onSeekComplete() {
+ ref.handleSeekComplete(this)
+ }
+
+ fun handleLog(message: String) {
+ ref.handleLog(this, message)
+ }
+
+ fun handleError(errorCode: String?, errorMessage: String?, errorDetails: Any?) {
+ ref.handleError(this, errorCode, errorMessage, errorDetails)
+ }
+
+ fun onError(what: Int, extra: Int): Boolean {
+ val whatMsg = if (what == MediaPlayer.MEDIA_ERROR_SERVER_DIED) {
+ "MEDIA_ERROR_SERVER_DIED"
+ } else {
+ "MEDIA_ERROR_UNKNOWN {what:$what}"
+ }
+ val extraMsg = when (extra) {
+ MEDIA_ERROR_SYSTEM -> "MEDIA_ERROR_SYSTEM"
+ MediaPlayer.MEDIA_ERROR_IO -> "MEDIA_ERROR_IO"
+ MediaPlayer.MEDIA_ERROR_MALFORMED -> "MEDIA_ERROR_MALFORMED"
+ MediaPlayer.MEDIA_ERROR_UNSUPPORTED -> "MEDIA_ERROR_UNSUPPORTED"
+ MediaPlayer.MEDIA_ERROR_TIMED_OUT -> "MEDIA_ERROR_TIMED_OUT"
+ else -> "MEDIA_ERROR_UNKNOWN {extra:$extra}"
+ }
+ if (!prepared && extraMsg == "MEDIA_ERROR_SYSTEM") {
+ handleError(
+ "AndroidAudioError",
+ "Failed to set source. For troubleshooting, see: " +
+ "https://github.com/bluefireteam/audioplayers/blob/main/troubleshooting.md",
+ "$whatMsg, $extraMsg",
+ )
+ } else {
+ // When an error occurs, reset player to not [prepared].
+ // Then no functions will be called, which end up in an illegal player state.
+ prepared = false
+ handleError("AndroidAudioError", whatMsg, extraMsg)
+ }
+ return false
+ }
+
+ /**
+ * Internal logic. Private methods
+ */
+
+ /**
+ * Create new player
+ */
+ private fun createPlayer(): Player {
+ return when (playerMode) {
+ MEDIA_PLAYER -> MediaPlayerPlayer(this)
+ LOW_LATENCY -> SoundPoolPlayer(this, soundPoolManager)
+ }
+ }
+
+ /**
+ * Create new player, assign and configure source
+ */
+ private fun initPlayer() {
+ val player = createPlayer()
+ // Need to set player before calling prepare, as onPrepared may is called before player is assigned
+ this.player = player
+ source?.let {
+ player.setSource(it)
+ player.configAndPrepare()
+ }
+ }
+
+ private fun Player.configAndPrepare() {
+ setVolumeAndBalance(volume, balance)
+ setLooping(isLooping)
+ prepare()
+ }
+
+ private fun Player.setVolumeAndBalance(volume: Float, balance: Float) {
+ val leftVolume = min(1f, 1f - balance) * volume
+ val rightVolume = min(1f, 1f + balance) * volume
+ setVolume(leftVolume, rightVolume)
+ }
+
+ fun dispose() {
+ release()
+ eventHandler.dispose()
+ }
+}
diff --git a/third_party/pub_patches/audioplayers_android/android/src/main/kotlin/xyz/luan/audioplayers/source/BytesSource.kt b/third_party/pub_patches/audioplayers_android/android/src/main/kotlin/xyz/luan/audioplayers/source/BytesSource.kt
new file mode 100644
index 000000000..eec5bd648
--- /dev/null
+++ b/third_party/pub_patches/audioplayers_android/android/src/main/kotlin/xyz/luan/audioplayers/source/BytesSource.kt
@@ -0,0 +1,22 @@
+package xyz.luan.audioplayers.source
+
+import android.media.MediaPlayer
+import android.os.Build
+import androidx.annotation.RequiresApi
+import xyz.luan.audioplayers.ByteDataSource
+import xyz.luan.audioplayers.player.SoundPoolPlayer
+
+@RequiresApi(Build.VERSION_CODES.M)
+data class BytesSource(
+ val dataSource: ByteDataSource,
+) : Source {
+ constructor(bytes: ByteArray) : this(ByteDataSource(bytes))
+
+ override fun setForMediaPlayer(mediaPlayer: MediaPlayer) {
+ mediaPlayer.setDataSource(dataSource)
+ }
+
+ override fun setForSoundPool(soundPoolPlayer: SoundPoolPlayer) {
+ error("Bytes sources are not supported on LOW_LATENCY mode yet.")
+ }
+}
diff --git a/third_party/pub_patches/audioplayers_android/android/src/main/kotlin/xyz/luan/audioplayers/source/Source.kt b/third_party/pub_patches/audioplayers_android/android/src/main/kotlin/xyz/luan/audioplayers/source/Source.kt
new file mode 100644
index 000000000..8d410bed7
--- /dev/null
+++ b/third_party/pub_patches/audioplayers_android/android/src/main/kotlin/xyz/luan/audioplayers/source/Source.kt
@@ -0,0 +1,10 @@
+package xyz.luan.audioplayers.source
+
+import android.media.MediaPlayer
+import xyz.luan.audioplayers.player.SoundPoolPlayer
+
+// TODO(luan) replace this indirection with a sealed interface once we have that option!
+interface Source {
+ fun setForMediaPlayer(mediaPlayer: MediaPlayer)
+ fun setForSoundPool(soundPoolPlayer: SoundPoolPlayer)
+}
diff --git a/third_party/pub_patches/audioplayers_android/android/src/main/kotlin/xyz/luan/audioplayers/source/UrlSource.kt b/third_party/pub_patches/audioplayers_android/android/src/main/kotlin/xyz/luan/audioplayers/source/UrlSource.kt
new file mode 100644
index 000000000..ad371e872
--- /dev/null
+++ b/third_party/pub_patches/audioplayers_android/android/src/main/kotlin/xyz/luan/audioplayers/source/UrlSource.kt
@@ -0,0 +1,53 @@
+package xyz.luan.audioplayers.source
+
+import android.media.MediaPlayer
+import xyz.luan.audioplayers.player.SoundPoolPlayer
+import java.io.ByteArrayOutputStream
+import java.io.File
+import java.io.FileOutputStream
+import java.net.URI
+import java.net.URL
+
+data class UrlSource(
+ val url: String,
+ val isLocal: Boolean,
+) : Source {
+ override fun setForMediaPlayer(mediaPlayer: MediaPlayer) {
+ mediaPlayer.setDataSource(url)
+ }
+
+ override fun setForSoundPool(soundPoolPlayer: SoundPoolPlayer) {
+ soundPoolPlayer.release()
+ soundPoolPlayer.urlSource = this
+ }
+
+ fun getAudioPathForSoundPool(): String {
+ if (isLocal) {
+ return url.removePrefix("file://")
+ }
+
+ return loadTempFileFromNetwork().absolutePath
+ }
+
+ private fun loadTempFileFromNetwork(): File {
+ val bytes = downloadUrl(URI.create(url).toURL())
+ val tempFile = File.createTempFile("sound", "")
+ FileOutputStream(tempFile).use {
+ it.write(bytes)
+ tempFile.deleteOnExit()
+ }
+ return tempFile
+ }
+
+ private fun downloadUrl(url: URL): ByteArray {
+ val outputStream = ByteArrayOutputStream()
+ url.openStream().use { stream ->
+ val chunk = ByteArray(4096)
+ while (true) {
+ val bytesRead = stream.read(chunk).takeIf { it > 0 } ?: break
+ outputStream.write(chunk, 0, bytesRead)
+ }
+ }
+ return outputStream.toByteArray()
+ }
+}
diff --git a/third_party/pub_patches/audioplayers_android/android/src/test/kotlin/xyz/luan/audioplayers/ToConstantCaseTest.kt b/third_party/pub_patches/audioplayers_android/android/src/test/kotlin/xyz/luan/audioplayers/ToConstantCaseTest.kt
new file mode 100644
index 000000000..9c6cb85f5
--- /dev/null
+++ b/third_party/pub_patches/audioplayers_android/android/src/test/kotlin/xyz/luan/audioplayers/ToConstantCaseTest.kt
@@ -0,0 +1,18 @@
+package xyz.luan.audioplayers
+
+import org.assertj.core.api.Assertions.assertThat
+import org.junit.jupiter.api.Test
+
+internal class ToConstantCaseTest {
+ @Test
+ fun `convert from sentence case`() {
+ assertThat("foo".toConstantCase()).isEqualTo("FOO")
+ assertThat("foo bar".toConstantCase()).isEqualTo("FOO_BAR")
+ }
+
+ @Test
+ fun `convert from camelCase`() {
+ assertThat("foo".toConstantCase()).isEqualTo("FOO")
+ assertThat("fooBar".toConstantCase()).isEqualTo("FOO_BAR")
+ }
+}
diff --git a/third_party/pub_patches/audioplayers_android/pubspec.yaml b/third_party/pub_patches/audioplayers_android/pubspec.yaml
new file mode 100644
index 000000000..b4666b6ca
--- /dev/null
+++ b/third_party/pub_patches/audioplayers_android/pubspec.yaml
@@ -0,0 +1,29 @@
+name: audioplayers_android
+description: Android implementation of audioplayers, a Flutter plugin to play multiple audio files simultaneously
+version: 4.0.3
+homepage: https://github.com/bluefireteam/audioplayers
+repository: https://github.com/bluefireteam/audioplayers/tree/master/packages/audioplayers_android
+
+flutter:
+ plugin:
+ implements: audioplayers
+ platforms:
+ android:
+ package: xyz.luan.audioplayers
+ pluginClass: AudioplayersPlugin
+
+dependencies:
+ audioplayers_platform_interface: ^6.1.0
+ flutter:
+ sdk: flutter
+
+dev_dependencies:
+ dartdoc: ^6.1.5
+ flame_lint: ^1.0.0
+ flutter_test:
+ sdk: flutter
+
+environment:
+ sdk: '>=3.0.0 <4.0.0'
+ flutter: '>=3.10.0'
+
diff --git a/third_party/pub_patches/fl_location/CHANGELOG.md b/third_party/pub_patches/fl_location/CHANGELOG.md
new file mode 100644
index 000000000..cfe836746
--- /dev/null
+++ b/third_party/pub_patches/fl_location/CHANGELOG.md
@@ -0,0 +1,44 @@
+## 2.1.1
+
+* [**FIX**] Fix IllegalStateException [#12](https://github.com/Dev-hwang/flutter_location/issues/12)
+
+## 2.1.0
+
+* [**FEAT**] Add ability to get location in the background [#10](https://github.com/Dev-hwang/flutter_location/issues/10)
+* [**FIX**] Fix ConcurrentModificationException [#13](https://github.com/Dev-hwang/flutter_location/issues/13)
+* [**FIX**] Fix location update channel close issue [#14](https://github.com/Dev-hwang/flutter_location/issues/14)
+
+## 2.0.0
+
+* [**CHORE**] Upgrade minimum Flutter version to 3.0.0
+* [**CHORE**] Upgrade dependencies
+
+## 1.2.2
+
+* [[#4](https://github.com/Dev-hwang/flutter_location/issues/4)] Fix onRequestPermissionsResult implement issue.
+
+## 1.2.1
+
+* Upgrade dependencies.
+* Downgrade Android minSdkVersion to 21.
+
+## 1.2.0
+
+* Upgrade dependencies.
+* Added LocationUtils class which implements `distanceBetween` and `bearingBetween` functions.
+* Bump Android minSdkVersion to 23.
+* Bump Android compileSdkVersion to 31.
+
+## 1.1.0
+
+* Now this plugin is also available on the web.
+
+## 1.0.1
+
+* Upgrade `fl_location_platform_interface: ^1.0.1`
+* Fixed invalid homepage URL.
+* Fixed an issue where `getLocation` and `getLocationStream` functions could not be called at the same time.
+
+## 1.0.0
+
+* Initial release.
diff --git a/third_party/pub_patches/fl_location/LICENSE b/third_party/pub_patches/fl_location/LICENSE
new file mode 100644
index 000000000..0bc6bc8b8
--- /dev/null
+++ b/third_party/pub_patches/fl_location/LICENSE
@@ -0,0 +1,21 @@
+MIT License
+
+Copyright (c) 2021 Dev-hwang
+
+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.
diff --git a/third_party/pub_patches/fl_location/README.md b/third_party/pub_patches/fl_location/README.md
new file mode 100644
index 000000000..cabe3672f
--- /dev/null
+++ b/third_party/pub_patches/fl_location/README.md
@@ -0,0 +1,217 @@
+# fl_location
+
+A plugin that can access the location services of each platform and collect device location data.
+
+## Platform
+
+- [x] Android
+- [x] iOS
+- [x] Web
+
+## Features
+
+* Can request location permission.
+* Can get the current location of the device.
+* Can check whether location services are enabled.
+* Can subscribe to `LocationStream` to collect location data in real time.
+* Can subscribe to `LocationServicesStatusStream` to listen location services status changes in real time.
+
+## Getting started
+
+To use this plugin, add `fl_location` as a [dependency in your pubspec.yaml file](https://flutter.io/platform-plugins/). For example:
+
+```yaml
+dependencies:
+ fl_location: ^2.1.1
+```
+
+After adding the `fl_location` plugin to the flutter project, we need to specify the platform-specific permissions for this plugin to work properly.
+
+### :baby_chick: Android
+
+Since this plugin works based on location, we need to add the following permission to the `AndroidManifest.xml` file. Open the `AndroidManifest.xml` file and specify it between the `` and `` tags.
+
+```
+
+
+```
+
+If you want to get the location in the background, add the following permission. If your project supports Android 10, be sure to add the `ACCESS_BACKGROUND_LOCATION` permission.
+
+```
+
+```
+
+### :baby_chick: iOS
+
+Like the Android platform, this plugin works based on location, we need to add the following description. Open the `ios/Runner/Info.plist` file and specify it inside the `` tag.
+
+```
+NSLocationWhenInUseUsageDescription
+Used to collect location data.
+```
+
+If you want to get the location in the background, add the following description.
+
+```
+NSLocationAlwaysAndWhenInUseUsageDescription
+Used to collect location data in the background.
+NSLocationAlwaysUsageDescription
+Used to collect location data in the background.
+UIBackgroundModes
+
+ fetch
+ location
+
+```
+
+## How to use
+
+1. Check whether the location permission is allowed or not, and if not allowed, request the location permission.
+
+```dart
+Future _checkAndRequestPermission({bool? background}) async {
+ if (!await FlLocation.isLocationServicesEnabled) {
+ // Location services are disabled.
+ return false;
+ }
+
+ var locationPermission = await FlLocation.checkLocationPermission();
+ if (locationPermission == LocationPermission.deniedForever) {
+ // Cannot request runtime permission because location permission is denied forever.
+ return false;
+ } else if (locationPermission == LocationPermission.denied) {
+ // Ask the user for location permission.
+ locationPermission = await FlLocation.requestLocationPermission();
+ if (locationPermission == LocationPermission.denied ||
+ locationPermission == LocationPermission.deniedForever) return false;
+ }
+
+ // Location permission must always be allowed (LocationPermission.always)
+ // to collect location data in the background.
+ if (background == true &&
+ locationPermission == LocationPermission.whileInUse) return false;
+
+ // Location services has been enabled and permission have been granted.
+ return true;
+}
+```
+
+2. To get the current location, use the `getLocation` function.
+
+```dart
+Future _getLocation() async {
+ if (await _checkAndRequestPermission()) {
+ final timeLimit = const Duration(seconds: 10);
+ await FlLocation.getLocation(timeLimit: timeLimit).then((location) {
+ print('location: ${location.toJson().toString()}');
+ }).onError((error, stackTrace) {
+ print('error: ${error.toString()}');
+ });
+ }
+}
+```
+
+3. To collect location data in real time, use the `getLocationStream` function.
+
+```dart
+StreamSubscription? _locationSubscription;
+
+Future _listenLocationStream() async {
+ if (await _checkAndRequestPermission()) {
+ if (_locationSubscription != null) await _cancelLocationSubscription();
+
+ _locationSubscription = FlLocation.getLocationStream()
+ .handleError(_handleError)
+ .listen((event) {
+ print('location: ${event.toJson().toString()}');
+ });
+ }
+}
+
+Future _cancelLocationSubscription() async {
+ await _locationSubscription?.cancel();
+ _locationSubscription = null;
+}
+
+void _handleError(dynamic error, StackTrace stackTrace) {
+ print('error: ${error.toString()}');
+}
+```
+
+4. To listen location services status changes in real time, use the `getLocationServicesStatusStream` function.
+
+```dart
+StreamSubscription? _locationServicesStatusSubscription;
+
+Future _listenLocationServicesStatusStream() async {
+ if (_locationServicesStatusSubscription != null)
+ await _cancelLocationServicesStatusSubscription();
+
+ _locationServicesStatusSubscription =
+ FlLocation.getLocationServicesStatusStream().listen((event) {
+ print('location services status: $event');
+ });
+}
+
+Future _cancelLocationServicesStatusSubscription() async {
+ await _locationServicesStatusSubscription?.cancel();
+ _locationServicesStatusSubscription = null;
+}
+```
+
+## Models
+
+### :chicken: Location
+
+A data class that represents a location model.
+
+| Field | Description |
+|---|---|
+| `latitude` | The latitude of the location. |
+| `longitude` | The longitude of the location. |
+| `accuracy` | The accuracy of the location. |
+| `altitude` | The altitude of the location. |
+| `heading` | The angle in the direction the device is moving. |
+| `speed` | The movement speed of the device. |
+| `speedAccuracy` | The accuracy of `speed`. |
+| `millisecondsSinceEpoch` | The millisecondsSinceEpoch at which the location update occurred. |
+| `timestamp` | The device time at which the location update occurred. |
+| `isMock` | Whether the mock location. |
+
+### :chicken: LocationAccuracy
+
+An enumeration of location accuracy.
+
+| Value | Description |
+|---|---|
+| `powerSave` | The location has an accuracy of 10km on Android and 3km on iOS. |
+| `low` | The location has an accuracy of 10km on Android and 1km on iOS. |
+| `balanced` | The location has an accuracy of 100m for both Android and iOS. |
+| `high` | The location has an accuracy of ~100m on Android and 10m on iOS. |
+| `best` | The location has an accuracy of ~100m on Android and ~10m on iOS. |
+| `navigation` | The location has an accuracy of ~100m on Android and optimized for navigation on iOS. |
+
+### :chicken: LocationPermission
+
+An enumeration of location permission.
+
+| Value | Description |
+|---|---|
+| `always` | The app can read location at any time. The app need this permission to read location in the background. |
+| `whileInUse` | The location can only be read while using the app. |
+| `denied` | The location cannot be read because permission is denied. The app can request runtime permissions again. |
+| `deniedForever` | The location cannot be read because permission is denied. The app can no longer request runtime permissions and must grant permissions manually. |
+
+### :chicken: LocationServicesStatus
+
+An enumeration of location services status.
+
+| Value | Description |
+|---|---|
+| `enabled` | Location services are enabled. |
+| `disabled` | Location services are disabled. |
+
+## Support
+
+If you find any bugs or issues while using the plugin, please register an issues on [GitHub](https://github.com/Dev-hwang/flutter_location/issues). You can also contact us at .
diff --git a/third_party/pub_patches/fl_location/android/build.gradle b/third_party/pub_patches/fl_location/android/build.gradle
new file mode 100644
index 000000000..4a1ebc3f3
--- /dev/null
+++ b/third_party/pub_patches/fl_location/android/build.gradle
@@ -0,0 +1,49 @@
+group 'com.pravera.fl_location'
+version '1.0-SNAPSHOT'
+
+buildscript {
+ ext.kotlin_version = '1.6.10'
+ repositories {
+ google()
+ mavenCentral()
+ }
+
+ dependencies {
+ classpath 'com.android.tools.build:gradle:7.1.2'
+ classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
+ }
+}
+
+rootProject.allprojects {
+ repositories {
+ google()
+ mavenCentral()
+ }
+}
+
+apply plugin: 'com.android.library'
+apply plugin: 'kotlin-android'
+
+android {
+ namespace "com.pravera.fl_location"
+ compileSdkVersion 31
+ compileOptions {
+ sourceCompatibility JavaVersion.VERSION_1_8
+ targetCompatibility JavaVersion.VERSION_1_8
+ }
+ kotlinOptions {
+ jvmTarget = '1.8'
+ }
+ sourceSets {
+ main.java.srcDirs += 'src/main/kotlin'
+ }
+ defaultConfig {
+ minSdkVersion 21
+ }
+}
+
+dependencies {
+ implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
+ implementation 'com.google.android.gms:play-services-location:21.0.1'
+ implementation 'com.google.code.gson:gson:2.10.1'
+}
diff --git a/third_party/pub_patches/fl_location/android/gradle.properties b/third_party/pub_patches/fl_location/android/gradle.properties
new file mode 100644
index 000000000..94adc3a3f
--- /dev/null
+++ b/third_party/pub_patches/fl_location/android/gradle.properties
@@ -0,0 +1,3 @@
+org.gradle.jvmargs=-Xmx1536M
+android.useAndroidX=true
+android.enableJetifier=true
diff --git a/third_party/pub_patches/fl_location/android/gradle/wrapper/gradle-wrapper.properties b/third_party/pub_patches/fl_location/android/gradle/wrapper/gradle-wrapper.properties
new file mode 100644
index 000000000..cb24abda1
--- /dev/null
+++ b/third_party/pub_patches/fl_location/android/gradle/wrapper/gradle-wrapper.properties
@@ -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-7.4-all.zip
diff --git a/third_party/pub_patches/fl_location/android/settings.gradle b/third_party/pub_patches/fl_location/android/settings.gradle
new file mode 100644
index 000000000..87ef5147e
--- /dev/null
+++ b/third_party/pub_patches/fl_location/android/settings.gradle
@@ -0,0 +1 @@
+rootProject.name = 'fl_location'
diff --git a/third_party/pub_patches/fl_location/android/src/main/AndroidManifest.xml b/third_party/pub_patches/fl_location/android/src/main/AndroidManifest.xml
new file mode 100644
index 000000000..62a4ab70c
--- /dev/null
+++ b/third_party/pub_patches/fl_location/android/src/main/AndroidManifest.xml
@@ -0,0 +1,13 @@
+
+
+
+
+
+
+
+
diff --git a/third_party/pub_patches/fl_location/android/src/main/kotlin/com/pravera/fl_location/FlLocationPlugin.kt b/third_party/pub_patches/fl_location/android/src/main/kotlin/com/pravera/fl_location/FlLocationPlugin.kt
new file mode 100644
index 000000000..9f4730480
--- /dev/null
+++ b/third_party/pub_patches/fl_location/android/src/main/kotlin/com/pravera/fl_location/FlLocationPlugin.kt
@@ -0,0 +1,82 @@
+package com.pravera.fl_location
+
+import com.pravera.fl_location.service.*
+import io.flutter.embedding.engine.plugins.FlutterPlugin
+import io.flutter.embedding.engine.plugins.activity.ActivityAware
+import io.flutter.embedding.engine.plugins.activity.ActivityPluginBinding
+
+/** FlLocationPlugin */
+class FlLocationPlugin: FlutterPlugin, ActivityAware, ServiceProvider {
+ private lateinit var locationPermissionManager: LocationPermissionManager
+ private lateinit var locationDataProviderManager: LocationDataProviderManager
+ private lateinit var locationServicesStatusWatcher: LocationServicesStatusWatcher
+
+ private var activityBinding: ActivityPluginBinding? = null
+ private lateinit var methodCallHandler: MethodCallHandlerImpl
+ private lateinit var locationStreamHandler: LocationStreamHandlerImpl
+ private lateinit var locationServicesStatusStreamHandler: LocationServicesStatusStreamHandlerImpl
+
+ override fun onAttachedToEngine(binding: FlutterPlugin.FlutterPluginBinding) {
+ val applicationContext = binding.applicationContext
+ val binaryMessenger = binding.binaryMessenger
+
+ locationPermissionManager = LocationPermissionManager()
+ locationDataProviderManager = LocationDataProviderManager(applicationContext)
+ locationServicesStatusWatcher = LocationServicesStatusWatcher()
+
+ methodCallHandler = MethodCallHandlerImpl(applicationContext, this)
+ methodCallHandler.initChannel(binaryMessenger)
+ locationStreamHandler = LocationStreamHandlerImpl(applicationContext, this)
+ locationStreamHandler.initChannel(binaryMessenger)
+ locationServicesStatusStreamHandler =
+ LocationServicesStatusStreamHandlerImpl(applicationContext, this)
+ locationServicesStatusStreamHandler.initChannel(binaryMessenger)
+ }
+
+ override fun onDetachedFromEngine(binding: FlutterPlugin.FlutterPluginBinding) {
+ if (::methodCallHandler.isInitialized) {
+ methodCallHandler.disposeChannel()
+ }
+ if (::locationStreamHandler.isInitialized) {
+ locationStreamHandler.disposeChannel()
+ }
+ if (::locationServicesStatusStreamHandler.isInitialized) {
+ locationServicesStatusStreamHandler.disposeChannel()
+ }
+ locationDataProviderManager.stopAllLocationUpdates()
+ }
+
+ override fun onAttachedToActivity(binding: ActivityPluginBinding) {
+ methodCallHandler.setActivity(binding.activity)
+ locationStreamHandler.setActivity(binding.activity)
+ locationServicesStatusStreamHandler.setActivity(binding.activity)
+ locationDataProviderManager.setActivity(binding.activity)
+ binding.addRequestPermissionsResultListener(locationPermissionManager)
+ binding.addActivityResultListener(locationDataProviderManager)
+ activityBinding = binding
+ }
+
+ override fun onDetachedFromActivityForConfigChanges() {
+ onDetachedFromActivity()
+ }
+
+ override fun onReattachedToActivityForConfigChanges(binding: ActivityPluginBinding) {
+ onAttachedToActivity(binding)
+ }
+
+ override fun onDetachedFromActivity() {
+ activityBinding?.removeRequestPermissionsResultListener(locationPermissionManager)
+ activityBinding?.removeActivityResultListener(locationDataProviderManager)
+ activityBinding = null
+ methodCallHandler.setActivity(null)
+ locationStreamHandler.setActivity(null)
+ locationServicesStatusStreamHandler.setActivity(null)
+ locationDataProviderManager.setActivity(null)
+ }
+
+ override fun getLocationPermissionManager() = locationPermissionManager
+
+ override fun getLocationDataProviderManager() = locationDataProviderManager
+
+ override fun getLocationServicesStatusWatcher() = locationServicesStatusWatcher
+}
diff --git a/third_party/pub_patches/fl_location/android/src/main/kotlin/com/pravera/fl_location/FlLocationPluginChannel.kt b/third_party/pub_patches/fl_location/android/src/main/kotlin/com/pravera/fl_location/FlLocationPluginChannel.kt
new file mode 100644
index 000000000..d00a3af23
--- /dev/null
+++ b/third_party/pub_patches/fl_location/android/src/main/kotlin/com/pravera/fl_location/FlLocationPluginChannel.kt
@@ -0,0 +1,11 @@
+package com.pravera.fl_location
+
+import android.app.Activity
+import io.flutter.plugin.common.BinaryMessenger
+
+/** FlLocationPluginChannel */
+interface FlLocationPluginChannel {
+ fun initChannel(messenger: BinaryMessenger)
+ fun setActivity(activity: Activity?)
+ fun disposeChannel()
+}
diff --git a/third_party/pub_patches/fl_location/android/src/main/kotlin/com/pravera/fl_location/LocationServicesStatusStreamHandlerImpl.kt b/third_party/pub_patches/fl_location/android/src/main/kotlin/com/pravera/fl_location/LocationServicesStatusStreamHandlerImpl.kt
new file mode 100644
index 000000000..5c10de0d6
--- /dev/null
+++ b/third_party/pub_patches/fl_location/android/src/main/kotlin/com/pravera/fl_location/LocationServicesStatusStreamHandlerImpl.kt
@@ -0,0 +1,41 @@
+package com.pravera.fl_location
+
+import android.app.Activity
+import android.content.Context
+import com.pravera.fl_location.service.ServiceProvider
+import io.flutter.plugin.common.BinaryMessenger
+import io.flutter.plugin.common.EventChannel
+
+/** LocationServicesStatusStreamHandlerImpl */
+class LocationServicesStatusStreamHandlerImpl(
+ private val context: Context,
+ private val serviceProvider: ServiceProvider):
+ EventChannel.StreamHandler, FlLocationPluginChannel {
+
+ private lateinit var channel: EventChannel
+ private var activity: Activity? = null
+
+ override fun onListen(arguments: Any?, events: EventChannel.EventSink?) {
+ serviceProvider.getLocationServicesStatusWatcher()
+ .start(context, onChanged = { events?.success(it.ordinal) })
+ }
+
+ override fun onCancel(arguments: Any?) {
+ serviceProvider.getLocationServicesStatusWatcher().stop(context)
+ }
+
+ override fun initChannel(messenger: BinaryMessenger) {
+ channel = EventChannel(messenger,
+ "plugins.pravera.com/fl_location/location_services_status")
+ channel.setStreamHandler(this)
+ }
+
+ override fun setActivity(activity: Activity?) {
+ this.activity = activity
+ }
+
+ override fun disposeChannel() {
+ if (::channel.isInitialized)
+ channel.setStreamHandler(null)
+ }
+}
diff --git a/third_party/pub_patches/fl_location/android/src/main/kotlin/com/pravera/fl_location/LocationStreamHandlerImpl.kt b/third_party/pub_patches/fl_location/android/src/main/kotlin/com/pravera/fl_location/LocationStreamHandlerImpl.kt
new file mode 100644
index 000000000..3bf1fc126
--- /dev/null
+++ b/third_party/pub_patches/fl_location/android/src/main/kotlin/com/pravera/fl_location/LocationStreamHandlerImpl.kt
@@ -0,0 +1,64 @@
+package com.pravera.fl_location
+
+import android.app.Activity
+import android.content.Context
+import com.pravera.fl_location.errors.ErrorCodes
+import com.pravera.fl_location.models.LocationSettings
+import com.pravera.fl_location.service.LocationDataCallback
+import com.pravera.fl_location.service.ServiceProvider
+import com.pravera.fl_location.utils.ErrorHandleUtils
+import io.flutter.plugin.common.BinaryMessenger
+import io.flutter.plugin.common.EventChannel
+
+/** LocationStreamHandlerImpl */
+class LocationStreamHandlerImpl(
+ private val context: Context,
+ private val serviceProvider: ServiceProvider):
+ EventChannel.StreamHandler, FlLocationPluginChannel {
+
+ private lateinit var channel: EventChannel
+ private var activity: Activity? = null
+ private var locationDataProviderHashCode: Int? = null
+
+ override fun onListen(arguments: Any?, events: EventChannel.EventSink) {
+ val callback = object : LocationDataCallback {
+ override fun onUpdate(locationJson: String) {
+ events.success(locationJson)
+ }
+
+ override fun onError(errorCode: ErrorCodes) {
+ ErrorHandleUtils.handleStreamError(events, errorCode)
+ }
+ }
+
+ val argsMap = arguments as? Map<*, *>
+ val settings = LocationSettings.fromMap(argsMap)
+
+ locationDataProviderHashCode = serviceProvider
+ .getLocationDataProviderManager()
+ .requestLocationUpdates(callback, settings)
+ }
+
+ override fun onCancel(arguments: Any?) {
+ if (locationDataProviderHashCode == null) return
+
+ serviceProvider
+ .getLocationDataProviderManager()
+ .stopLocationUpdates(locationDataProviderHashCode!!)
+ }
+
+ override fun initChannel(messenger: BinaryMessenger) {
+ channel = EventChannel(messenger, "plugins.pravera.com/fl_location/updates")
+ channel.setStreamHandler(this)
+ }
+
+ override fun setActivity(activity: Activity?) {
+ this.activity = activity
+ }
+
+ override fun disposeChannel() {
+ if (::channel.isInitialized) {
+ channel.setStreamHandler(null)
+ }
+ }
+}
diff --git a/third_party/pub_patches/fl_location/android/src/main/kotlin/com/pravera/fl_location/MethodCallHandlerImpl.kt b/third_party/pub_patches/fl_location/android/src/main/kotlin/com/pravera/fl_location/MethodCallHandlerImpl.kt
new file mode 100644
index 000000000..ef47f565a
--- /dev/null
+++ b/third_party/pub_patches/fl_location/android/src/main/kotlin/com/pravera/fl_location/MethodCallHandlerImpl.kt
@@ -0,0 +1,98 @@
+package com.pravera.fl_location
+
+import android.app.Activity
+import android.content.Context
+import com.pravera.fl_location.errors.ErrorCodes
+import com.pravera.fl_location.models.LocationPermission
+import com.pravera.fl_location.models.LocationSettings
+import com.pravera.fl_location.service.LocationDataCallback
+import com.pravera.fl_location.service.LocationPermissionCallback
+import com.pravera.fl_location.service.ServiceProvider
+import com.pravera.fl_location.utils.ErrorHandleUtils
+import com.pravera.fl_location.utils.LocationServicesUtils
+import io.flutter.plugin.common.BinaryMessenger
+import io.flutter.plugin.common.MethodCall
+import io.flutter.plugin.common.MethodChannel
+
+/** MethodCallHandlerImpl */
+class MethodCallHandlerImpl(
+ private val context: Context,
+ private val serviceProvider: ServiceProvider):
+ MethodChannel.MethodCallHandler, FlLocationPluginChannel {
+
+ private lateinit var channel: MethodChannel
+ private var activity: Activity? = null
+
+ override fun onMethodCall(call: MethodCall, result: MethodChannel.Result) {
+ val reqMethod = call.method
+ if (reqMethod.contains("checkLocationPermission") ||
+ reqMethod.contains("requestLocationPermission")) {
+ if (activity == null) {
+ ErrorHandleUtils.handleMethodCallError(result, ErrorCodes.ACTIVITY_NOT_ATTACHED)
+ return
+ }
+ }
+
+ when (reqMethod) {
+ "checkLocationServicesStatus" -> {
+ val checkResult = LocationServicesUtils.checkLocationServicesStatus(context)
+ result.success(checkResult.ordinal)
+ }
+ "checkLocationPermission" -> {
+ val checkResult = serviceProvider.getLocationPermissionManager()
+ .checkLocationPermission(activity!!)
+ result.success(checkResult.ordinal)
+ }
+ "requestLocationPermission" -> {
+ val callback = object : LocationPermissionCallback {
+ override fun onResult(locationPermission: LocationPermission) {
+ result.success(locationPermission.ordinal)
+ }
+
+ override fun onError(errorCode: ErrorCodes) {
+ ErrorHandleUtils.handleMethodCallError(result, errorCode)
+ }
+ }
+
+ serviceProvider.getLocationPermissionManager()
+ .requestLocationPermission(activity!!, callback)
+ }
+ "getLocation" -> {
+ val callback = object : LocationDataCallback {
+ override fun onUpdate(locationJson: String) {
+ // 매니저에서 stopLocationUpdates 처리
+ result.success(locationJson)
+ }
+
+ override fun onError(errorCode: ErrorCodes) {
+ // 매니저에서 stopLocationUpdates 처리
+ ErrorHandleUtils.handleMethodCallError(result, errorCode)
+ }
+ }
+
+ val argsMap = call.arguments as? Map<*, *>
+ val settings = LocationSettings.fromMap(argsMap)
+
+ serviceProvider
+ .getLocationDataProviderManager()
+ .getLocation(callback, settings)
+ }
+ else -> result.notImplemented()
+ }
+ }
+
+ override fun initChannel(messenger: BinaryMessenger) {
+ channel = MethodChannel(messenger, "plugins.pravera.com/fl_location")
+ channel.setMethodCallHandler(this)
+ }
+
+ override fun setActivity(activity: Activity?) {
+ this.activity = activity
+ }
+
+ override fun disposeChannel() {
+ if (::channel.isInitialized) {
+ channel.setMethodCallHandler(null)
+ }
+ }
+}
diff --git a/third_party/pub_patches/fl_location/android/src/main/kotlin/com/pravera/fl_location/errors/ErrorCodes.kt b/third_party/pub_patches/fl_location/android/src/main/kotlin/com/pravera/fl_location/errors/ErrorCodes.kt
new file mode 100644
index 000000000..722c3689a
--- /dev/null
+++ b/third_party/pub_patches/fl_location/android/src/main/kotlin/com/pravera/fl_location/errors/ErrorCodes.kt
@@ -0,0 +1,24 @@
+package com.pravera.fl_location.errors
+
+enum class ErrorCodes {
+ ACTIVITY_NOT_ATTACHED,
+ LOCATION_PERMISSION_REQUEST_CANCELLED,
+ LOCATION_SETTINGS_CHANGE_FAILED,
+ LOCATION_SERVICES_NOT_AVAILABLE,
+ LOCATION_DATA_ENCODING_FAILED;
+
+ fun message(): String {
+ return when (this) {
+ ACTIVITY_NOT_ATTACHED ->
+ "The function to use Activity is not available because Activity is not attached to FlutterEngine."
+ LOCATION_PERMISSION_REQUEST_CANCELLED ->
+ "The dialog was closed or the request was canceled during a runtime location permission request."
+ LOCATION_SETTINGS_CHANGE_FAILED ->
+ "The request to change location settings failed."
+ LOCATION_SERVICES_NOT_AVAILABLE ->
+ "Location services are not available."
+ LOCATION_DATA_ENCODING_FAILED ->
+ "Failed to encode location data in JSON format."
+ }
+ }
+}
diff --git a/third_party/pub_patches/fl_location/android/src/main/kotlin/com/pravera/fl_location/models/LocationAccuracy.kt b/third_party/pub_patches/fl_location/android/src/main/kotlin/com/pravera/fl_location/models/LocationAccuracy.kt
new file mode 100644
index 000000000..35cc4aec9
--- /dev/null
+++ b/third_party/pub_patches/fl_location/android/src/main/kotlin/com/pravera/fl_location/models/LocationAccuracy.kt
@@ -0,0 +1,36 @@
+package com.pravera.fl_location.models
+
+import com.google.android.gms.location.Priority
+
+enum class LocationAccuracy {
+ POWER_SAVE,
+ LOW,
+ BALANCED,
+ HIGH,
+ BEST,
+ NAVIGATION;
+
+ companion object {
+ fun fromIndex(index: Int): LocationAccuracy {
+ return when (index) {
+ 1 -> LOW
+ 2 -> BALANCED
+ 3 -> HIGH
+ 4 -> BEST
+ 5 -> NAVIGATION
+ else -> POWER_SAVE
+ }
+ }
+ }
+
+ fun toPriority(): Int {
+ return when (this) {
+ POWER_SAVE -> Priority.PRIORITY_LOW_POWER
+ LOW -> Priority.PRIORITY_LOW_POWER
+ BALANCED -> Priority.PRIORITY_BALANCED_POWER_ACCURACY
+ HIGH -> Priority.PRIORITY_HIGH_ACCURACY
+ BEST -> Priority.PRIORITY_HIGH_ACCURACY
+ NAVIGATION -> Priority.PRIORITY_HIGH_ACCURACY
+ }
+ }
+}
diff --git a/third_party/pub_patches/fl_location/android/src/main/kotlin/com/pravera/fl_location/models/LocationData.kt b/third_party/pub_patches/fl_location/android/src/main/kotlin/com/pravera/fl_location/models/LocationData.kt
new file mode 100644
index 000000000..8361fe1c3
--- /dev/null
+++ b/third_party/pub_patches/fl_location/android/src/main/kotlin/com/pravera/fl_location/models/LocationData.kt
@@ -0,0 +1,15 @@
+package com.pravera.fl_location.models
+
+import com.google.gson.annotations.SerializedName
+
+data class LocationData(
+ @SerializedName("latitude") val latitude: Double,
+ @SerializedName("longitude") val longitude: Double,
+ @SerializedName("accuracy") val accuracy: Double,
+ @SerializedName("altitude") val altitude: Double,
+ @SerializedName("heading") val heading: Double,
+ @SerializedName("speed") val speed: Double,
+ @SerializedName("speedAccuracy") val speedAccuracy: Double?,
+ @SerializedName("millisecondsSinceEpoch") val millisecondsSinceEpoch: Double,
+ @SerializedName("isMock") val isMock: Boolean?
+)
diff --git a/third_party/pub_patches/fl_location/android/src/main/kotlin/com/pravera/fl_location/models/LocationPermission.kt b/third_party/pub_patches/fl_location/android/src/main/kotlin/com/pravera/fl_location/models/LocationPermission.kt
new file mode 100644
index 000000000..de65e2d1f
--- /dev/null
+++ b/third_party/pub_patches/fl_location/android/src/main/kotlin/com/pravera/fl_location/models/LocationPermission.kt
@@ -0,0 +1,8 @@
+package com.pravera.fl_location.models
+
+enum class LocationPermission {
+ ALWAYS,
+ WHILE_IN_USE,
+ DENIED,
+ DENIED_FOREVER,
+}
diff --git a/third_party/pub_patches/fl_location/android/src/main/kotlin/com/pravera/fl_location/models/LocationServicesStatus.kt b/third_party/pub_patches/fl_location/android/src/main/kotlin/com/pravera/fl_location/models/LocationServicesStatus.kt
new file mode 100644
index 000000000..d96f0a314
--- /dev/null
+++ b/third_party/pub_patches/fl_location/android/src/main/kotlin/com/pravera/fl_location/models/LocationServicesStatus.kt
@@ -0,0 +1,6 @@
+package com.pravera.fl_location.models
+
+enum class LocationServicesStatus {
+ ENABLED,
+ DISABLED,
+}
diff --git a/third_party/pub_patches/fl_location/android/src/main/kotlin/com/pravera/fl_location/models/LocationSettings.kt b/third_party/pub_patches/fl_location/android/src/main/kotlin/com/pravera/fl_location/models/LocationSettings.kt
new file mode 100644
index 000000000..8f07c7fad
--- /dev/null
+++ b/third_party/pub_patches/fl_location/android/src/main/kotlin/com/pravera/fl_location/models/LocationSettings.kt
@@ -0,0 +1,21 @@
+package com.pravera.fl_location.models
+
+data class LocationSettings(
+ val accuracy: LocationAccuracy,
+ val interval: Long? = null,
+ val distanceFilter: Float? = null
+) {
+ companion object {
+ fun fromMap(map: Map<*, *>?): LocationSettings {
+ val accuracy = map?.get("accuracy").toString().toIntOrNull() ?: LocationAccuracy.BEST.ordinal
+ val interval = map?.get("interval").toString().toLongOrNull()
+ val distanceFilter = map?.get("distanceFilter").toString().toFloatOrNull()
+
+ return LocationSettings(
+ accuracy = LocationAccuracy.fromIndex(accuracy),
+ interval = interval,
+ distanceFilter = distanceFilter
+ )
+ }
+ }
+}
diff --git a/third_party/pub_patches/fl_location/android/src/main/kotlin/com/pravera/fl_location/service/LocationDataCallback.kt b/third_party/pub_patches/fl_location/android/src/main/kotlin/com/pravera/fl_location/service/LocationDataCallback.kt
new file mode 100644
index 000000000..9543ab594
--- /dev/null
+++ b/third_party/pub_patches/fl_location/android/src/main/kotlin/com/pravera/fl_location/service/LocationDataCallback.kt
@@ -0,0 +1,8 @@
+package com.pravera.fl_location.service
+
+import com.pravera.fl_location.errors.ErrorCodes
+
+interface LocationDataCallback {
+ fun onUpdate(locationJson: String)
+ fun onError(errorCode: ErrorCodes)
+}
diff --git a/third_party/pub_patches/fl_location/android/src/main/kotlin/com/pravera/fl_location/service/LocationDataProvider.kt b/third_party/pub_patches/fl_location/android/src/main/kotlin/com/pravera/fl_location/service/LocationDataProvider.kt
new file mode 100644
index 000000000..3e142c4c0
--- /dev/null
+++ b/third_party/pub_patches/fl_location/android/src/main/kotlin/com/pravera/fl_location/service/LocationDataProvider.kt
@@ -0,0 +1,177 @@
+package com.pravera.fl_location.service
+
+import android.app.Activity
+import android.content.Context
+import android.content.IntentSender
+import android.os.Build
+import android.os.Looper
+import com.google.android.gms.common.api.ApiException
+import com.google.android.gms.common.api.ResolvableApiException
+import com.google.android.gms.location.*
+import com.google.gson.Gson
+import com.pravera.fl_location.errors.ErrorCodes
+import com.pravera.fl_location.models.LocationData
+import com.pravera.fl_location.models.LocationSettings
+
+class LocationDataProvider(private val context: Context) {
+ companion object {
+ private const val DEFAULT_LOCATION_INTERVAL = 5000L
+ private const val REQUEST_CHECK_SETTINGS = 0x1
+ }
+
+ private val jsonEncoder = Gson()
+ private val locationProvider = LocationServices.getFusedLocationProviderClient(context)
+
+ private var activity: Activity? = null
+ private var callback: LocationDataCallback? = null
+ private var settings: LocationSettings? = null
+
+ private var locationCallback: LocationCallback? = null
+ private var locationRequest: LocationRequest? = null
+
+ var isRunningLocationUpdates = false
+ private set
+
+ fun onActivityResult(requestCode: Int, resultCode: Int): Boolean {
+ if (requestCode == REQUEST_CHECK_SETTINGS) {
+ if (resultCode == Activity.RESULT_OK) {
+ // The stopLocationUpdates function has already been called.
+ if (callback == null) {
+ return false
+ }
+ startLocationUpdates()
+ return true
+ } else {
+ callback?.onError(ErrorCodes.LOCATION_SERVICES_NOT_AVAILABLE)
+ }
+ }
+
+ return false
+ }
+
+ fun setActivity(activity: Activity?) {
+ this.activity = activity
+ }
+
+ fun requestLocationUpdates(callback: LocationDataCallback, settings: LocationSettings) {
+ stopLocationUpdates()
+ this.callback = callback
+ this.settings = settings
+ this.locationCallback = createLocationCallback(callback)
+ this.locationRequest = createLocationRequest(settings)
+ checkLocationSettingsAndStartLocationUpdates()
+ }
+
+ private fun checkLocationSettingsAndStartLocationUpdates() {
+ val settingsRequest = LocationSettingsRequest.Builder()
+ .addLocationRequest(locationRequest!!)
+ .build()
+
+ val settingsClient = LocationServices.getSettingsClient(context)
+ settingsClient.checkLocationSettings(settingsRequest)
+ .addOnSuccessListener {
+ startLocationUpdates()
+ }
+ .addOnFailureListener {
+ val statusCode: Int
+ if (it is ResolvableApiException) {
+ statusCode = it.statusCode
+
+ // Location settings are not satisfied.
+ // But could be fixed by showing the user a dialog.
+ if (statusCode == LocationSettingsStatusCodes.RESOLUTION_REQUIRED) {
+ try {
+ if (activity == null) {
+ callback?.onError(ErrorCodes.LOCATION_SETTINGS_CHANGE_FAILED)
+ return@addOnFailureListener
+ }
+
+ it.startResolutionForResult(activity!!, REQUEST_CHECK_SETTINGS)
+ } catch (sendEx: IntentSender.SendIntentException) {
+ callback?.onError(ErrorCodes.LOCATION_SETTINGS_CHANGE_FAILED)
+ }
+ } else {
+ callback?.onError(ErrorCodes.LOCATION_SERVICES_NOT_AVAILABLE)
+ }
+ } else if (it is ApiException) {
+ statusCode = it.statusCode
+
+ // Location settings are not satisfied.
+ // However, we have no way to fix the settings so we won't show the dialog.
+ if (statusCode == LocationSettingsStatusCodes.SETTINGS_CHANGE_UNAVAILABLE) {
+ startLocationUpdates()
+ } else {
+ callback?.onError(ErrorCodes.LOCATION_SERVICES_NOT_AVAILABLE)
+ }
+ } else {
+ callback?.onError(ErrorCodes.LOCATION_SERVICES_NOT_AVAILABLE)
+ }
+ }
+ }
+
+ private fun startLocationUpdates() {
+ if (locationRequest == null || locationCallback == null) return
+
+ isRunningLocationUpdates = true
+ locationProvider.requestLocationUpdates(
+ locationRequest!!, locationCallback!!, Looper.getMainLooper())
+ }
+
+ fun stopLocationUpdates() {
+ if (locationCallback != null) {
+ isRunningLocationUpdates = false
+ locationProvider.removeLocationUpdates(locationCallback!!)
+ }
+ this.callback = null
+ this.settings = null
+ this.locationCallback = null
+ this.locationRequest = null
+ }
+
+ private fun createLocationCallback(callback: LocationDataCallback): LocationCallback {
+ return object : LocationCallback() {
+ override fun onLocationResult(locationResult: LocationResult) {
+ val location = locationResult.lastLocation ?: return
+
+ var speedAccuracy: Double? = null
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
+ speedAccuracy = location.speedAccuracyMetersPerSecond.toDouble()
+ }
+
+ var isMock: Boolean? = null
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
+ isMock = location.isMock
+ }
+
+ val locationData = LocationData(
+ latitude = location.latitude,
+ longitude = location.longitude,
+ accuracy = location.accuracy.toDouble(),
+ altitude = location.altitude,
+ heading = location.bearing.toDouble(),
+ speed = location.speed.toDouble(),
+ speedAccuracy = speedAccuracy,
+ millisecondsSinceEpoch = location.time.toDouble(),
+ isMock = isMock
+ )
+
+ try {
+ callback.onUpdate(jsonEncoder.toJson(locationData))
+ } catch (ex: Exception) {
+ callback.onError(ErrorCodes.LOCATION_DATA_ENCODING_FAILED)
+ }
+ }
+ }
+ }
+
+ private fun createLocationRequest(settings: LocationSettings): LocationRequest {
+ val accuracy = settings.accuracy.toPriority()
+ val interval = settings.interval ?: DEFAULT_LOCATION_INTERVAL
+ val distanceFilter = settings.distanceFilter ?: 0F
+
+ return LocationRequest.Builder(accuracy, interval).apply {
+ setMinUpdateDistanceMeters(distanceFilter)
+ setMinUpdateIntervalMillis(interval)
+ }.build()
+ }
+}
diff --git a/third_party/pub_patches/fl_location/android/src/main/kotlin/com/pravera/fl_location/service/LocationDataProviderManager.kt b/third_party/pub_patches/fl_location/android/src/main/kotlin/com/pravera/fl_location/service/LocationDataProviderManager.kt
new file mode 100644
index 000000000..ccfe74f4e
--- /dev/null
+++ b/third_party/pub_patches/fl_location/android/src/main/kotlin/com/pravera/fl_location/service/LocationDataProviderManager.kt
@@ -0,0 +1,80 @@
+package com.pravera.fl_location.service
+
+import android.app.Activity
+import android.content.Context
+import android.content.Intent
+import com.pravera.fl_location.errors.ErrorCodes
+import com.pravera.fl_location.models.LocationSettings
+import io.flutter.plugin.common.PluginRegistry
+
+class LocationDataProviderManager(private val context: Context): PluginRegistry.ActivityResultListener {
+ private val providers = mutableMapOf()
+
+ private fun buildLocationDataProvider() = LocationDataProvider(context)
+
+ fun setActivity(activity: Activity?) {
+ val iterator = providers.values.iterator()
+ for (provider in iterator) {
+ provider.setActivity(activity)
+ }
+ }
+
+ fun getLocation(callback: LocationDataCallback, settings: LocationSettings): Int {
+ val newLocationDataProvider = buildLocationDataProvider()
+ val hashCode = newLocationDataProvider.hashCode()
+ providers[hashCode] = newLocationDataProvider
+
+ val newCallback = object : LocationDataCallback {
+ override fun onUpdate(locationJson: String) {
+ if (newLocationDataProvider.isRunningLocationUpdates) {
+ stopLocationUpdates(hashCode)
+ callback.onUpdate(locationJson)
+ }
+ }
+
+ override fun onError(errorCode: ErrorCodes) {
+ if (newLocationDataProvider.isRunningLocationUpdates) {
+ stopLocationUpdates(hashCode)
+ callback.onError(errorCode)
+ }
+ }
+ }
+ newLocationDataProvider.requestLocationUpdates(newCallback, settings)
+ return hashCode
+ }
+
+ fun requestLocationUpdates(callback: LocationDataCallback, settings: LocationSettings): Int {
+ val newLocationDataProvider = buildLocationDataProvider()
+ val hashCode = newLocationDataProvider.hashCode()
+ providers[hashCode] = newLocationDataProvider
+
+ newLocationDataProvider.requestLocationUpdates(callback, settings)
+ return hashCode
+ }
+
+ fun stopLocationUpdates(hashCode: Int) {
+ providers[hashCode]?.let {
+ it.stopLocationUpdates()
+ providers.remove(hashCode)
+ }
+ }
+
+ fun stopAllLocationUpdates() {
+ val iterator = providers.values.iterator()
+ for (provider in iterator) {
+ provider.stopLocationUpdates()
+ }
+ providers.clear()
+ }
+
+ override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?): Boolean {
+ val iterator = providers.values.iterator()
+ for (provider in iterator) {
+ if (provider.onActivityResult(requestCode, resultCode)) {
+ return true
+ }
+ }
+
+ return false
+ }
+}
diff --git a/third_party/pub_patches/fl_location/android/src/main/kotlin/com/pravera/fl_location/service/LocationPermissionCallback.kt b/third_party/pub_patches/fl_location/android/src/main/kotlin/com/pravera/fl_location/service/LocationPermissionCallback.kt
new file mode 100644
index 000000000..dbc7068c5
--- /dev/null
+++ b/third_party/pub_patches/fl_location/android/src/main/kotlin/com/pravera/fl_location/service/LocationPermissionCallback.kt
@@ -0,0 +1,9 @@
+package com.pravera.fl_location.service
+
+import com.pravera.fl_location.errors.ErrorCodes
+import com.pravera.fl_location.models.LocationPermission
+
+interface LocationPermissionCallback {
+ fun onResult(locationPermission: LocationPermission)
+ fun onError(errorCode: ErrorCodes)
+}
diff --git a/third_party/pub_patches/fl_location/android/src/main/kotlin/com/pravera/fl_location/service/LocationPermissionManager.kt b/third_party/pub_patches/fl_location/android/src/main/kotlin/com/pravera/fl_location/service/LocationPermissionManager.kt
new file mode 100644
index 000000000..2fdf7c1ad
--- /dev/null
+++ b/third_party/pub_patches/fl_location/android/src/main/kotlin/com/pravera/fl_location/service/LocationPermissionManager.kt
@@ -0,0 +1,169 @@
+package com.pravera.fl_location.service
+
+import android.Manifest
+import android.annotation.SuppressLint
+import android.app.Activity
+import android.content.Context
+import android.content.pm.PackageManager
+import android.os.Build
+import androidx.core.app.ActivityCompat
+import androidx.core.content.ContextCompat
+import com.pravera.fl_location.errors.ErrorCodes
+import com.pravera.fl_location.models.LocationPermission
+import io.flutter.plugin.common.PluginRegistry
+
+class LocationPermissionManager: PluginRegistry.RequestPermissionsResultListener {
+ companion object {
+ private const val LOCATION_PERMISSION_REQ_CODE = 109
+ private const val BACKGROUND_LOCATION_PERMISSION_REQ_CODE = 110
+ private const val PREV_PERMISSION_STATUS_PREFS_NAME = "PREV_PERMISSION_STATUS_PREFS"
+ }
+
+ private var activity: Activity? = null
+ private var callback: LocationPermissionCallback? = null
+
+ fun checkLocationPermission(activity: Activity): LocationPermission {
+ if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M)
+ return LocationPermission.ALWAYS
+
+ val locationPermission = Manifest.permission.ACCESS_FINE_LOCATION
+ if (!activity.isPermissionGranted(locationPermission)) {
+ val prevPermissionStatus = activity.getPrevPermissionStatus(locationPermission)
+ if (prevPermissionStatus != null &&
+ prevPermissionStatus == LocationPermission.DENIED_FOREVER &&
+ !activity.shouldShowRequestPermissionRationale(locationPermission))
+ return LocationPermission.DENIED_FOREVER
+
+ return LocationPermission.DENIED
+ }
+
+ if (Build.VERSION.SDK_INT < Build.VERSION_CODES.Q)
+ return LocationPermission.ALWAYS
+
+ val backgroundLocationPermission = Manifest.permission.ACCESS_BACKGROUND_LOCATION
+ if (activity.hasPermissionInManifest(backgroundLocationPermission) &&
+ activity.isPermissionGranted(backgroundLocationPermission))
+ return LocationPermission.ALWAYS
+
+ return LocationPermission.WHILE_IN_USE
+ }
+
+ fun requestLocationPermission(activity: Activity, callback: LocationPermissionCallback) {
+ if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) {
+ callback.onResult(LocationPermission.ALWAYS)
+ return
+ }
+
+ // The app has already requested location permission and is awaiting results.
+ if (this.callback != null) return
+
+ this.activity = activity
+ this.callback = callback
+
+ ActivityCompat.requestPermissions(
+ activity,
+ arrayOf(Manifest.permission.ACCESS_FINE_LOCATION),
+ LOCATION_PERMISSION_REQ_CODE)
+ }
+
+ private fun requestBackgroundLocationPermission(activity: Activity) {
+ if (Build.VERSION.SDK_INT < Build.VERSION_CODES.Q) return
+
+ ActivityCompat.requestPermissions(
+ activity,
+ arrayOf(Manifest.permission.ACCESS_BACKGROUND_LOCATION),
+ BACKGROUND_LOCATION_PERMISSION_REQ_CODE)
+ }
+
+ private fun Context.hasPermissionInManifest(permission: String): Boolean {
+ val packageInfo = packageManager.getPackageInfo(packageName, PackageManager.GET_PERMISSIONS)
+ val permissions = packageInfo.requestedPermissions
+
+ return permissions?.any { perm -> perm == permission } ?: false
+ }
+
+ private fun Context.isPermissionGranted(permission: String): Boolean {
+ return ContextCompat.checkSelfPermission(this, permission) ==
+ PackageManager.PERMISSION_GRANTED
+ }
+
+ private fun Context.setPrevPermissionStatus(permission: String, status: LocationPermission) {
+ val prefs = getSharedPreferences(
+ PREV_PERMISSION_STATUS_PREFS_NAME, Context.MODE_PRIVATE) ?: return
+
+ with (prefs.edit()) {
+ putString(permission, status.toString())
+ commit()
+ }
+ }
+
+ private fun Context.getPrevPermissionStatus(permission: String): LocationPermission? {
+ val prefs = getSharedPreferences(
+ PREV_PERMISSION_STATUS_PREFS_NAME, Context.MODE_PRIVATE) ?: return null
+
+ val value = prefs.getString(permission, null) ?: return null
+ return LocationPermission.valueOf(value)
+ }
+
+ private fun disposeResources() {
+ this.activity = null
+ this.callback = null
+ }
+
+ @SuppressLint("InlinedApi")
+ override fun onRequestPermissionsResult(requestCode: Int, permissions: Array, grantResults: IntArray): Boolean {
+ if (grantResults.isEmpty()) {
+ callback?.onError(ErrorCodes.LOCATION_PERMISSION_REQUEST_CANCELLED)
+ disposeResources()
+ return false
+ }
+
+ val permission: String
+ val permissionIndex: Int
+ var permissionStatus = LocationPermission.DENIED
+
+ when (requestCode) {
+ LOCATION_PERMISSION_REQ_CODE -> {
+ permission = Manifest.permission.ACCESS_FINE_LOCATION
+ permissionIndex = permissions.indexOf(permission)
+
+ if (permissionIndex >= 0 &&
+ grantResults[permissionIndex] == PackageManager.PERMISSION_GRANTED) {
+ permissionStatus = if (Build.VERSION.SDK_INT < Build.VERSION_CODES.Q)
+ LocationPermission.ALWAYS
+ else
+ LocationPermission.WHILE_IN_USE
+ } else {
+ if (activity?.shouldShowRequestPermissionRationale(permission) == false)
+ permissionStatus = LocationPermission.DENIED_FOREVER
+ }
+ }
+ BACKGROUND_LOCATION_PERMISSION_REQ_CODE -> {
+ permission = Manifest.permission.ACCESS_BACKGROUND_LOCATION
+ permissionIndex = permissions.indexOf(permission)
+
+ permissionStatus = if (permissionIndex >= 0 &&
+ grantResults[permissionIndex] == PackageManager.PERMISSION_GRANTED)
+ LocationPermission.ALWAYS
+ else
+ LocationPermission.WHILE_IN_USE
+ }
+ else -> return false
+ }
+
+ activity?.setPrevPermissionStatus(permission, permissionStatus)
+
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q &&
+ activity != null &&
+ activity!!.hasPermissionInManifest(Manifest.permission.ACCESS_BACKGROUND_LOCATION) &&
+ permission == Manifest.permission.ACCESS_FINE_LOCATION &&
+ permissionStatus == LocationPermission.WHILE_IN_USE) {
+ requestBackgroundLocationPermission(activity!!)
+ } else {
+ callback?.onResult(permissionStatus)
+ disposeResources()
+ }
+
+ return true
+ }
+}
diff --git a/third_party/pub_patches/fl_location/android/src/main/kotlin/com/pravera/fl_location/service/LocationServicesStatusIntentService.kt b/third_party/pub_patches/fl_location/android/src/main/kotlin/com/pravera/fl_location/service/LocationServicesStatusIntentService.kt
new file mode 100644
index 000000000..52d00e4ca
--- /dev/null
+++ b/third_party/pub_patches/fl_location/android/src/main/kotlin/com/pravera/fl_location/service/LocationServicesStatusIntentService.kt
@@ -0,0 +1,33 @@
+package com.pravera.fl_location.service
+
+import android.content.Context
+import android.content.Intent
+import androidx.core.app.JobIntentService
+import com.pravera.fl_location.utils.LocationServicesUtils
+
+class LocationServicesStatusIntentService: JobIntentService() {
+ companion object {
+ private const val JOB_ID = 1000
+
+ fun enqueueWork(context: Context, intent: Intent) {
+ enqueueWork(context, LocationServicesStatusIntentService::class.java, JOB_ID, intent)
+ }
+ }
+
+ override fun onHandleWork(intent: Intent) {
+ val currValue = LocationServicesUtils.checkLocationServicesStatus(applicationContext).toString()
+
+ val prefs = getSharedPreferences(
+ LocationServicesStatusWatcher.LOCATION_SERVICES_STATUS_PREFS_NAME,
+ Context.MODE_PRIVATE) ?: return
+
+ val prevValue = prefs.getString(
+ LocationServicesStatusWatcher.LOCATION_SERVICES_STATUS_PREFS_KEY, null)
+ if (currValue == prevValue) return
+
+ with(prefs.edit()) {
+ putString(LocationServicesStatusWatcher.LOCATION_SERVICES_STATUS_PREFS_KEY, currValue)
+ commit()
+ }
+ }
+}
diff --git a/third_party/pub_patches/fl_location/android/src/main/kotlin/com/pravera/fl_location/service/LocationServicesStatusReceiver.kt b/third_party/pub_patches/fl_location/android/src/main/kotlin/com/pravera/fl_location/service/LocationServicesStatusReceiver.kt
new file mode 100644
index 000000000..c24708d3f
--- /dev/null
+++ b/third_party/pub_patches/fl_location/android/src/main/kotlin/com/pravera/fl_location/service/LocationServicesStatusReceiver.kt
@@ -0,0 +1,15 @@
+package com.pravera.fl_location.service
+
+import android.content.BroadcastReceiver
+import android.content.Context
+import android.content.Intent
+import android.location.LocationManager
+
+class LocationServicesStatusReceiver: BroadcastReceiver() {
+ override fun onReceive(context: Context, intent: Intent) {
+ if (intent.action != LocationManager.PROVIDERS_CHANGED_ACTION) return
+
+ intent.setClass(context, LocationServicesStatusIntentService::class.java)
+ LocationServicesStatusIntentService.enqueueWork(context, intent)
+ }
+}
diff --git a/third_party/pub_patches/fl_location/android/src/main/kotlin/com/pravera/fl_location/service/LocationServicesStatusWatcher.kt b/third_party/pub_patches/fl_location/android/src/main/kotlin/com/pravera/fl_location/service/LocationServicesStatusWatcher.kt
new file mode 100644
index 000000000..ff460068a
--- /dev/null
+++ b/third_party/pub_patches/fl_location/android/src/main/kotlin/com/pravera/fl_location/service/LocationServicesStatusWatcher.kt
@@ -0,0 +1,69 @@
+package com.pravera.fl_location.service
+
+import android.content.Context
+import android.content.IntentFilter
+import android.content.SharedPreferences
+import android.location.LocationManager
+import com.pravera.fl_location.models.LocationServicesStatus
+
+class LocationServicesStatusWatcher: SharedPreferences.OnSharedPreferenceChangeListener {
+ companion object {
+ const val LOCATION_SERVICES_STATUS_PREFS_NAME = "LOCATION_SERVICES_STATUS_PREFS"
+ const val LOCATION_SERVICES_STATUS_PREFS_KEY = "LOCATION_SERVICES_STATUS"
+ }
+
+ private var onChangedCallback: ((LocationServicesStatus) -> Unit)? = null
+ private var broadcastReceiver: LocationServicesStatusReceiver? = null
+
+ fun start(context: Context, onChanged: ((LocationServicesStatus) -> Unit)) {
+ if (this.onChangedCallback != null) stop(context)
+
+ this.onChangedCallback = onChanged
+ context.registerSharedPreferenceChangeListener()
+ context.registerLocationServicesStatusIntentReceiver()
+ }
+
+ fun stop(context: Context) {
+ this.onChangedCallback = null
+ context.unregisterSharedPreferenceChangeListener()
+ context.unregisterLocationServicesStatusIntentReceiver()
+ }
+
+ private fun Context.registerSharedPreferenceChangeListener() {
+ val prefs = getSharedPreferences(
+ LOCATION_SERVICES_STATUS_PREFS_NAME, Context.MODE_PRIVATE) ?: return
+
+ with (prefs.edit()) {
+ remove(LOCATION_SERVICES_STATUS_PREFS_KEY)
+ commit()
+ }
+
+ prefs.registerOnSharedPreferenceChangeListener(this@LocationServicesStatusWatcher)
+ }
+
+ private fun Context.unregisterSharedPreferenceChangeListener() {
+ val prefs = getSharedPreferences(
+ LOCATION_SERVICES_STATUS_PREFS_NAME, Context.MODE_PRIVATE) ?: return
+ prefs.unregisterOnSharedPreferenceChangeListener(this@LocationServicesStatusWatcher)
+ }
+
+ private fun Context.registerLocationServicesStatusIntentReceiver() {
+ broadcastReceiver = LocationServicesStatusReceiver()
+ registerReceiver(broadcastReceiver, IntentFilter(LocationManager.PROVIDERS_CHANGED_ACTION))
+ }
+
+ private fun Context.unregisterLocationServicesStatusIntentReceiver() {
+ unregisterReceiver(broadcastReceiver)
+ broadcastReceiver = null
+ }
+
+ override fun onSharedPreferenceChanged(sharedPreferences: SharedPreferences, key: String) {
+ when (key) {
+ LOCATION_SERVICES_STATUS_PREFS_KEY -> {
+ val value = sharedPreferences.getString(key, null) ?: return
+ val locationServicesStatus = LocationServicesStatus.valueOf(value)
+ onChangedCallback?.invoke(locationServicesStatus)
+ }
+ }
+ }
+}
diff --git a/third_party/pub_patches/fl_location/android/src/main/kotlin/com/pravera/fl_location/service/ServiceProvider.kt b/third_party/pub_patches/fl_location/android/src/main/kotlin/com/pravera/fl_location/service/ServiceProvider.kt
new file mode 100644
index 000000000..baf2973d3
--- /dev/null
+++ b/third_party/pub_patches/fl_location/android/src/main/kotlin/com/pravera/fl_location/service/ServiceProvider.kt
@@ -0,0 +1,7 @@
+package com.pravera.fl_location.service
+
+interface ServiceProvider {
+ fun getLocationPermissionManager(): LocationPermissionManager
+ fun getLocationDataProviderManager(): LocationDataProviderManager
+ fun getLocationServicesStatusWatcher(): LocationServicesStatusWatcher
+}
diff --git a/third_party/pub_patches/fl_location/android/src/main/kotlin/com/pravera/fl_location/utils/ErrorHandleUtils.kt b/third_party/pub_patches/fl_location/android/src/main/kotlin/com/pravera/fl_location/utils/ErrorHandleUtils.kt
new file mode 100644
index 000000000..8f5f86ddc
--- /dev/null
+++ b/third_party/pub_patches/fl_location/android/src/main/kotlin/com/pravera/fl_location/utils/ErrorHandleUtils.kt
@@ -0,0 +1,17 @@
+package com.pravera.fl_location.utils
+
+import com.pravera.fl_location.errors.ErrorCodes
+import io.flutter.plugin.common.EventChannel
+import io.flutter.plugin.common.MethodChannel
+
+class ErrorHandleUtils {
+ companion object {
+ fun handleMethodCallError(result: MethodChannel.Result, errorCode: ErrorCodes) {
+ result.error(errorCode.toString(), errorCode.message(), null)
+ }
+
+ fun handleStreamError(events: EventChannel.EventSink, errorCode: ErrorCodes) {
+ events.error(errorCode.toString(), errorCode.message(), null)
+ }
+ }
+}
diff --git a/third_party/pub_patches/fl_location/android/src/main/kotlin/com/pravera/fl_location/utils/LocationServicesUtils.kt b/third_party/pub_patches/fl_location/android/src/main/kotlin/com/pravera/fl_location/utils/LocationServicesUtils.kt
new file mode 100644
index 000000000..6d6be1dc4
--- /dev/null
+++ b/third_party/pub_patches/fl_location/android/src/main/kotlin/com/pravera/fl_location/utils/LocationServicesUtils.kt
@@ -0,0 +1,20 @@
+package com.pravera.fl_location.utils
+
+import android.content.Context
+import android.location.LocationManager
+import com.pravera.fl_location.models.LocationServicesStatus
+
+class LocationServicesUtils {
+ companion object {
+ fun checkLocationServicesStatus(context: Context): LocationServicesStatus {
+ val locationManager = context.getSystemService(Context.LOCATION_SERVICE) as LocationManager
+ val isGpsProviderEnabled = locationManager.isProviderEnabled(LocationManager.GPS_PROVIDER)
+ val isNetProviderEnabled = locationManager.isProviderEnabled(LocationManager.NETWORK_PROVIDER)
+
+ return if (isGpsProviderEnabled || isNetProviderEnabled)
+ LocationServicesStatus.ENABLED
+ else
+ LocationServicesStatus.DISABLED
+ }
+ }
+}
diff --git a/third_party/pub_patches/fl_location/example/README.md b/third_party/pub_patches/fl_location/example/README.md
new file mode 100644
index 000000000..8adc71c75
--- /dev/null
+++ b/third_party/pub_patches/fl_location/example/README.md
@@ -0,0 +1,16 @@
+# fl_location_example
+
+Demonstrates how to use the fl_location plugin.
+
+## Getting Started
+
+This project is a starting point for a Flutter application.
+
+A few resources to get you started if this is your first Flutter project:
+
+- [Lab: Write your first Flutter app](https://flutter.dev/docs/get-started/codelab)
+- [Cookbook: Useful Flutter samples](https://flutter.dev/docs/cookbook)
+
+For help getting started with Flutter, view our
+[online documentation](https://flutter.dev/docs), which offers tutorials,
+samples, guidance on mobile development, and a full API reference.
diff --git a/third_party/pub_patches/fl_location/example/android/app/build.gradle b/third_party/pub_patches/fl_location/example/android/app/build.gradle
new file mode 100644
index 000000000..9d9c65c95
--- /dev/null
+++ b/third_party/pub_patches/fl_location/example/android/app/build.gradle
@@ -0,0 +1,57 @@
+def localProperties = new Properties()
+def localPropertiesFile = rootProject.file('local.properties')
+if (localPropertiesFile.exists()) {
+ localPropertiesFile.withReader('UTF-8') { reader ->
+ localProperties.load(reader)
+ }
+}
+
+def flutterRoot = localProperties.getProperty('flutter.sdk')
+if (flutterRoot == null) {
+ throw new GradleException("Flutter SDK not found. Define location with flutter.sdk in the local.properties file.")
+}
+
+def flutterVersionCode = localProperties.getProperty('flutter.versionCode')
+if (flutterVersionCode == null) {
+ flutterVersionCode = '1'
+}
+
+def flutterVersionName = localProperties.getProperty('flutter.versionName')
+if (flutterVersionName == null) {
+ flutterVersionName = '1.0'
+}
+
+apply plugin: 'com.android.application'
+apply plugin: 'kotlin-android'
+apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle"
+
+android {
+ compileSdkVersion 31
+
+ sourceSets {
+ main.java.srcDirs += 'src/main/kotlin'
+ }
+
+ defaultConfig {
+ applicationId "com.pravera.fl_location_example"
+ minSdkVersion 21
+ targetSdkVersion 31
+ versionCode flutterVersionCode.toInteger()
+ versionName flutterVersionName
+ }
+
+ buildTypes {
+ release {
+ // Signing with the debug keys for now, so `flutter run --release` works.
+ signingConfig signingConfigs.debug
+ }
+ }
+}
+
+flutter {
+ source '../..'
+}
+
+dependencies {
+ implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
+}
diff --git a/third_party/pub_patches/fl_location/example/android/app/src/debug/AndroidManifest.xml b/third_party/pub_patches/fl_location/example/android/app/src/debug/AndroidManifest.xml
new file mode 100644
index 000000000..5a204f049
--- /dev/null
+++ b/third_party/pub_patches/fl_location/example/android/app/src/debug/AndroidManifest.xml
@@ -0,0 +1,7 @@
+
+
+
+
diff --git a/third_party/pub_patches/fl_location/example/android/app/src/main/AndroidManifest.xml b/third_party/pub_patches/fl_location/example/android/app/src/main/AndroidManifest.xml
new file mode 100644
index 000000000..a23ff6253
--- /dev/null
+++ b/third_party/pub_patches/fl_location/example/android/app/src/main/AndroidManifest.xml
@@ -0,0 +1,38 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/third_party/pub_patches/fl_location/example/android/app/src/main/kotlin/com/pravera/fl_location_example/MainActivity.kt b/third_party/pub_patches/fl_location/example/android/app/src/main/kotlin/com/pravera/fl_location_example/MainActivity.kt
new file mode 100644
index 000000000..5c17448cb
--- /dev/null
+++ b/third_party/pub_patches/fl_location/example/android/app/src/main/kotlin/com/pravera/fl_location_example/MainActivity.kt
@@ -0,0 +1,6 @@
+package com.pravera.fl_location_example
+
+import io.flutter.embedding.android.FlutterActivity
+
+class MainActivity: FlutterActivity() {
+}
diff --git a/third_party/pub_patches/fl_location/example/android/app/src/main/res/drawable-v21/launch_background.xml b/third_party/pub_patches/fl_location/example/android/app/src/main/res/drawable-v21/launch_background.xml
new file mode 100644
index 000000000..f74085f3f
--- /dev/null
+++ b/third_party/pub_patches/fl_location/example/android/app/src/main/res/drawable-v21/launch_background.xml
@@ -0,0 +1,12 @@
+
+
+
+
+
+
+
+
diff --git a/third_party/pub_patches/fl_location/example/android/app/src/main/res/drawable/launch_background.xml b/third_party/pub_patches/fl_location/example/android/app/src/main/res/drawable/launch_background.xml
new file mode 100644
index 000000000..304732f88
--- /dev/null
+++ b/third_party/pub_patches/fl_location/example/android/app/src/main/res/drawable/launch_background.xml
@@ -0,0 +1,12 @@
+
+
+
+
+
+
+
+
diff --git a/third_party/pub_patches/fl_location/example/android/app/src/main/res/mipmap-hdpi/ic_launcher.png b/third_party/pub_patches/fl_location/example/android/app/src/main/res/mipmap-hdpi/ic_launcher.png
new file mode 100644
index 000000000..db77bb4b7
Binary files /dev/null and b/third_party/pub_patches/fl_location/example/android/app/src/main/res/mipmap-hdpi/ic_launcher.png differ
diff --git a/third_party/pub_patches/fl_location/example/android/app/src/main/res/mipmap-mdpi/ic_launcher.png b/third_party/pub_patches/fl_location/example/android/app/src/main/res/mipmap-mdpi/ic_launcher.png
new file mode 100644
index 000000000..17987b79b
Binary files /dev/null and b/third_party/pub_patches/fl_location/example/android/app/src/main/res/mipmap-mdpi/ic_launcher.png differ
diff --git a/third_party/pub_patches/fl_location/example/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png b/third_party/pub_patches/fl_location/example/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png
new file mode 100644
index 000000000..09d439148
Binary files /dev/null and b/third_party/pub_patches/fl_location/example/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png differ
diff --git a/third_party/pub_patches/fl_location/example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png b/third_party/pub_patches/fl_location/example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
new file mode 100644
index 000000000..d5f1c8d34
Binary files /dev/null and b/third_party/pub_patches/fl_location/example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png differ
diff --git a/third_party/pub_patches/fl_location/example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/third_party/pub_patches/fl_location/example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
new file mode 100644
index 000000000..4d6372eeb
Binary files /dev/null and b/third_party/pub_patches/fl_location/example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png differ
diff --git a/third_party/pub_patches/fl_location/example/android/app/src/main/res/values-night/styles.xml b/third_party/pub_patches/fl_location/example/android/app/src/main/res/values-night/styles.xml
new file mode 100644
index 000000000..449a9f930
--- /dev/null
+++ b/third_party/pub_patches/fl_location/example/android/app/src/main/res/values-night/styles.xml
@@ -0,0 +1,18 @@
+
+
+
+
+
+
+
diff --git a/third_party/pub_patches/fl_location/example/android/app/src/main/res/values/styles.xml b/third_party/pub_patches/fl_location/example/android/app/src/main/res/values/styles.xml
new file mode 100644
index 000000000..d74aa35c2
--- /dev/null
+++ b/third_party/pub_patches/fl_location/example/android/app/src/main/res/values/styles.xml
@@ -0,0 +1,18 @@
+
+
+
+
+
+
+
diff --git a/third_party/pub_patches/fl_location/example/android/app/src/profile/AndroidManifest.xml b/third_party/pub_patches/fl_location/example/android/app/src/profile/AndroidManifest.xml
new file mode 100644
index 000000000..5a204f049
--- /dev/null
+++ b/third_party/pub_patches/fl_location/example/android/app/src/profile/AndroidManifest.xml
@@ -0,0 +1,7 @@
+
+
+
+
diff --git a/third_party/pub_patches/fl_location/example/android/build.gradle b/third_party/pub_patches/fl_location/example/android/build.gradle
new file mode 100644
index 000000000..ba6fe4a43
--- /dev/null
+++ b/third_party/pub_patches/fl_location/example/android/build.gradle
@@ -0,0 +1,29 @@
+buildscript {
+ ext.kotlin_version = '1.6.10'
+ repositories {
+ google()
+ mavenCentral()
+ }
+
+ dependencies {
+ classpath 'com.android.tools.build:gradle:7.1.2'
+ classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
+ }
+}
+
+allprojects {
+ repositories {
+ google()
+ mavenCentral()
+ }
+}
+
+rootProject.buildDir = '../build'
+subprojects {
+ project.buildDir = "${rootProject.buildDir}/${project.name}"
+ project.evaluationDependsOn(':app')
+}
+
+task clean(type: Delete) {
+ delete rootProject.buildDir
+}
diff --git a/third_party/pub_patches/fl_location/example/android/gradle.properties b/third_party/pub_patches/fl_location/example/android/gradle.properties
new file mode 100644
index 000000000..94adc3a3f
--- /dev/null
+++ b/third_party/pub_patches/fl_location/example/android/gradle.properties
@@ -0,0 +1,3 @@
+org.gradle.jvmargs=-Xmx1536M
+android.useAndroidX=true
+android.enableJetifier=true
diff --git a/third_party/pub_patches/fl_location/example/android/gradle/wrapper/gradle-wrapper.properties b/third_party/pub_patches/fl_location/example/android/gradle/wrapper/gradle-wrapper.properties
new file mode 100644
index 000000000..cc5527d78
--- /dev/null
+++ b/third_party/pub_patches/fl_location/example/android/gradle/wrapper/gradle-wrapper.properties
@@ -0,0 +1,6 @@
+#Fri Jun 23 08:50:38 CEST 2017
+distributionBase=GRADLE_USER_HOME
+distributionPath=wrapper/dists
+zipStoreBase=GRADLE_USER_HOME
+zipStorePath=wrapper/dists
+distributionUrl=https\://services.gradle.org/distributions/gradle-7.4-all.zip
diff --git a/third_party/pub_patches/fl_location/example/android/settings.gradle b/third_party/pub_patches/fl_location/example/android/settings.gradle
new file mode 100644
index 000000000..44e62bcf0
--- /dev/null
+++ b/third_party/pub_patches/fl_location/example/android/settings.gradle
@@ -0,0 +1,11 @@
+include ':app'
+
+def localPropertiesFile = new File(rootProject.projectDir, "local.properties")
+def properties = new Properties()
+
+assert localPropertiesFile.exists()
+localPropertiesFile.withReader("UTF-8") { reader -> properties.load(reader) }
+
+def flutterSdkPath = properties.getProperty("flutter.sdk")
+assert flutterSdkPath != null, "flutter.sdk not set in local.properties"
+apply from: "$flutterSdkPath/packages/flutter_tools/gradle/app_plugin_loader.gradle"
diff --git a/third_party/pub_patches/fl_location/example/ios/Flutter/AppFrameworkInfo.plist b/third_party/pub_patches/fl_location/example/ios/Flutter/AppFrameworkInfo.plist
new file mode 100644
index 000000000..9367d483e
--- /dev/null
+++ b/third_party/pub_patches/fl_location/example/ios/Flutter/AppFrameworkInfo.plist
@@ -0,0 +1,26 @@
+
+
+
+
+ CFBundleDevelopmentRegion
+ en
+ CFBundleExecutable
+ App
+ CFBundleIdentifier
+ io.flutter.flutter.app
+ CFBundleInfoDictionaryVersion
+ 6.0
+ CFBundleName
+ App
+ CFBundlePackageType
+ FMWK
+ CFBundleShortVersionString
+ 1.0
+ CFBundleSignature
+ ????
+ CFBundleVersion
+ 1.0
+ MinimumOSVersion
+ 8.0
+
+
diff --git a/third_party/pub_patches/fl_location/example/ios/Flutter/Debug.xcconfig b/third_party/pub_patches/fl_location/example/ios/Flutter/Debug.xcconfig
new file mode 100644
index 000000000..ec97fc6f3
--- /dev/null
+++ b/third_party/pub_patches/fl_location/example/ios/Flutter/Debug.xcconfig
@@ -0,0 +1,2 @@
+#include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"
+#include "Generated.xcconfig"
diff --git a/third_party/pub_patches/fl_location/example/ios/Flutter/Release.xcconfig b/third_party/pub_patches/fl_location/example/ios/Flutter/Release.xcconfig
new file mode 100644
index 000000000..c4855bfe2
--- /dev/null
+++ b/third_party/pub_patches/fl_location/example/ios/Flutter/Release.xcconfig
@@ -0,0 +1,2 @@
+#include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"
+#include "Generated.xcconfig"
diff --git a/third_party/pub_patches/fl_location/example/ios/Podfile b/third_party/pub_patches/fl_location/example/ios/Podfile
new file mode 100644
index 000000000..1e8c3c90a
--- /dev/null
+++ b/third_party/pub_patches/fl_location/example/ios/Podfile
@@ -0,0 +1,41 @@
+# Uncomment this line to define a global platform for your project
+# platform :ios, '9.0'
+
+# CocoaPods analytics sends network stats synchronously affecting flutter build latency.
+ENV['COCOAPODS_DISABLE_STATS'] = 'true'
+
+project 'Runner', {
+ 'Debug' => :debug,
+ 'Profile' => :release,
+ 'Release' => :release,
+}
+
+def flutter_root
+ generated_xcode_build_settings_path = File.expand_path(File.join('..', 'Flutter', 'Generated.xcconfig'), __FILE__)
+ unless File.exist?(generated_xcode_build_settings_path)
+ raise "#{generated_xcode_build_settings_path} must exist. If you're running pod install manually, make sure flutter pub get is executed first"
+ end
+
+ File.foreach(generated_xcode_build_settings_path) do |line|
+ matches = line.match(/FLUTTER_ROOT\=(.*)/)
+ return matches[1].strip if matches
+ end
+ raise "FLUTTER_ROOT not found in #{generated_xcode_build_settings_path}. Try deleting Generated.xcconfig, then run flutter pub get"
+end
+
+require File.expand_path(File.join('packages', 'flutter_tools', 'bin', 'podhelper'), flutter_root)
+
+flutter_ios_podfile_setup
+
+target 'Runner' do
+ use_frameworks!
+ use_modular_headers!
+
+ flutter_install_all_ios_pods File.dirname(File.realpath(__FILE__))
+end
+
+post_install do |installer|
+ installer.pods_project.targets.each do |target|
+ flutter_additional_ios_build_settings(target)
+ end
+end
diff --git a/third_party/pub_patches/fl_location/example/ios/Podfile.lock b/third_party/pub_patches/fl_location/example/ios/Podfile.lock
new file mode 100644
index 000000000..c15b65c87
--- /dev/null
+++ b/third_party/pub_patches/fl_location/example/ios/Podfile.lock
@@ -0,0 +1,22 @@
+PODS:
+ - fl_location (0.0.1):
+ - Flutter
+ - Flutter (1.0.0)
+
+DEPENDENCIES:
+ - fl_location (from `.symlinks/plugins/fl_location/ios`)
+ - Flutter (from `Flutter`)
+
+EXTERNAL SOURCES:
+ fl_location:
+ :path: ".symlinks/plugins/fl_location/ios"
+ Flutter:
+ :path: Flutter
+
+SPEC CHECKSUMS:
+ fl_location: 68b4a6c4aad2a453493ff66f196e0748280cf43e
+ Flutter: 434fef37c0980e73bb6479ef766c45957d4b510c
+
+PODFILE CHECKSUM: aafe91acc616949ddb318b77800a7f51bffa2a4c
+
+COCOAPODS: 1.10.1
diff --git a/third_party/pub_patches/fl_location/example/ios/Runner.xcodeproj/project.pbxproj b/third_party/pub_patches/fl_location/example/ios/Runner.xcodeproj/project.pbxproj
new file mode 100644
index 000000000..d2f38e0f6
--- /dev/null
+++ b/third_party/pub_patches/fl_location/example/ios/Runner.xcodeproj/project.pbxproj
@@ -0,0 +1,539 @@
+// !$*UTF8*$!
+{
+ archiveVersion = 1;
+ classes = {
+ };
+ objectVersion = 46;
+ objects = {
+
+/* Begin PBXBuildFile section */
+ 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; };
+ 3A2FF4D1939657022BD61D6F /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 09A1AA4F0F6FCAE48C75D407 /* Pods_Runner.framework */; };
+ 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; };
+ 74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74858FAE1ED2DC5600515810 /* AppDelegate.swift */; };
+ 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; };
+ 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; };
+ 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; };
+/* End PBXBuildFile section */
+
+/* Begin PBXCopyFilesBuildPhase section */
+ 9705A1C41CF9048500538489 /* Embed Frameworks */ = {
+ isa = PBXCopyFilesBuildPhase;
+ buildActionMask = 2147483647;
+ dstPath = "";
+ dstSubfolderSpec = 10;
+ files = (
+ );
+ name = "Embed Frameworks";
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+/* End PBXCopyFilesBuildPhase section */
+
+/* Begin PBXFileReference section */
+ 09A1AA4F0F6FCAE48C75D407 /* Pods_Runner.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Runner.framework; sourceTree = BUILT_PRODUCTS_DIR; };
+ 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GeneratedPluginRegistrant.h; sourceTree = ""; };
+ 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = ""; };
+ 240244A9685D38C12E59345C /* Pods-Runner.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.profile.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.profile.xcconfig"; sourceTree = ""; };
+ 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = ""; };
+ 74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Runner-Bridging-Header.h"; sourceTree = ""; };
+ 74858FAE1ED2DC5600515810 /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; };
+ 7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = ""; };
+ 9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Debug.xcconfig; path = Flutter/Debug.xcconfig; sourceTree = ""; };
+ 9740EEB31CF90195004384FC /* Generated.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Generated.xcconfig; path = Flutter/Generated.xcconfig; sourceTree = ""; };
+ 97C146EE1CF9000F007C117D /* Runner.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Runner.app; sourceTree = BUILT_PRODUCTS_DIR; };
+ 97C146FB1CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; };
+ 97C146FD1CF9000F007C117D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; };
+ 97C147001CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; };
+ 97C147021CF9000F007C117D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; };
+ B8C213FA797AF9D6B1E063D6 /* Pods-Runner.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.debug.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"; sourceTree = ""; };
+ EF9A0BD320B79569C142D4ED /* Pods-Runner.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.release.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"; sourceTree = ""; };
+/* End PBXFileReference section */
+
+/* Begin PBXFrameworksBuildPhase section */
+ 97C146EB1CF9000F007C117D /* Frameworks */ = {
+ isa = PBXFrameworksBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ 3A2FF4D1939657022BD61D6F /* Pods_Runner.framework in Frameworks */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+/* End PBXFrameworksBuildPhase section */
+
+/* Begin PBXGroup section */
+ 1F63284331C6C4585861AF52 /* Pods */ = {
+ isa = PBXGroup;
+ children = (
+ B8C213FA797AF9D6B1E063D6 /* Pods-Runner.debug.xcconfig */,
+ EF9A0BD320B79569C142D4ED /* Pods-Runner.release.xcconfig */,
+ 240244A9685D38C12E59345C /* Pods-Runner.profile.xcconfig */,
+ );
+ name = Pods;
+ path = Pods;
+ sourceTree = "";
+ };
+ 9740EEB11CF90186004384FC /* Flutter */ = {
+ isa = PBXGroup;
+ children = (
+ 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */,
+ 9740EEB21CF90195004384FC /* Debug.xcconfig */,
+ 7AFA3C8E1D35360C0083082E /* Release.xcconfig */,
+ 9740EEB31CF90195004384FC /* Generated.xcconfig */,
+ );
+ name = Flutter;
+ sourceTree = "";
+ };
+ 97C146E51CF9000F007C117D = {
+ isa = PBXGroup;
+ children = (
+ 9740EEB11CF90186004384FC /* Flutter */,
+ 97C146F01CF9000F007C117D /* Runner */,
+ 97C146EF1CF9000F007C117D /* Products */,
+ 1F63284331C6C4585861AF52 /* Pods */,
+ C838E5273D25FD5F48FFA498 /* Frameworks */,
+ );
+ sourceTree = "";
+ };
+ 97C146EF1CF9000F007C117D /* Products */ = {
+ isa = PBXGroup;
+ children = (
+ 97C146EE1CF9000F007C117D /* Runner.app */,
+ );
+ name = Products;
+ sourceTree = "";
+ };
+ 97C146F01CF9000F007C117D /* Runner */ = {
+ isa = PBXGroup;
+ children = (
+ 97C146FA1CF9000F007C117D /* Main.storyboard */,
+ 97C146FD1CF9000F007C117D /* Assets.xcassets */,
+ 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */,
+ 97C147021CF9000F007C117D /* Info.plist */,
+ 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */,
+ 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */,
+ 74858FAE1ED2DC5600515810 /* AppDelegate.swift */,
+ 74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */,
+ );
+ path = Runner;
+ sourceTree = "";
+ };
+ C838E5273D25FD5F48FFA498 /* Frameworks */ = {
+ isa = PBXGroup;
+ children = (
+ 09A1AA4F0F6FCAE48C75D407 /* Pods_Runner.framework */,
+ );
+ name = Frameworks;
+ sourceTree = "";
+ };
+/* End PBXGroup section */
+
+/* Begin PBXNativeTarget section */
+ 97C146ED1CF9000F007C117D /* Runner */ = {
+ isa = PBXNativeTarget;
+ buildConfigurationList = 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */;
+ buildPhases = (
+ C8B6F6305BF18244B3C47F42 /* [CP] Check Pods Manifest.lock */,
+ 9740EEB61CF901F6004384FC /* Run Script */,
+ 97C146EA1CF9000F007C117D /* Sources */,
+ 97C146EB1CF9000F007C117D /* Frameworks */,
+ 97C146EC1CF9000F007C117D /* Resources */,
+ 9705A1C41CF9048500538489 /* Embed Frameworks */,
+ 3B06AD1E1E4923F5004D2608 /* Thin Binary */,
+ 1F8149480FDA5DE173925972 /* [CP] Embed Pods Frameworks */,
+ );
+ buildRules = (
+ );
+ dependencies = (
+ );
+ name = Runner;
+ productName = Runner;
+ productReference = 97C146EE1CF9000F007C117D /* Runner.app */;
+ productType = "com.apple.product-type.application";
+ };
+/* End PBXNativeTarget section */
+
+/* Begin PBXProject section */
+ 97C146E61CF9000F007C117D /* Project object */ = {
+ isa = PBXProject;
+ attributes = {
+ LastUpgradeCheck = 1020;
+ ORGANIZATIONNAME = "";
+ TargetAttributes = {
+ 97C146ED1CF9000F007C117D = {
+ CreatedOnToolsVersion = 7.3.1;
+ LastSwiftMigration = 1100;
+ };
+ };
+ };
+ buildConfigurationList = 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */;
+ compatibilityVersion = "Xcode 9.3";
+ developmentRegion = en;
+ hasScannedForEncodings = 0;
+ knownRegions = (
+ en,
+ Base,
+ );
+ mainGroup = 97C146E51CF9000F007C117D;
+ productRefGroup = 97C146EF1CF9000F007C117D /* Products */;
+ projectDirPath = "";
+ projectRoot = "";
+ targets = (
+ 97C146ED1CF9000F007C117D /* Runner */,
+ );
+ };
+/* End PBXProject section */
+
+/* Begin PBXResourcesBuildPhase section */
+ 97C146EC1CF9000F007C117D /* Resources */ = {
+ isa = PBXResourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */,
+ 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */,
+ 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */,
+ 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+/* End PBXResourcesBuildPhase section */
+
+/* Begin PBXShellScriptBuildPhase section */
+ 1F8149480FDA5DE173925972 /* [CP] Embed Pods Frameworks */ = {
+ isa = PBXShellScriptBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ );
+ inputFileListPaths = (
+ "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-input-files.xcfilelist",
+ );
+ name = "[CP] Embed Pods Frameworks";
+ outputFileListPaths = (
+ "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-output-files.xcfilelist",
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ shellPath = /bin/sh;
+ shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh\"\n";
+ showEnvVarsInLog = 0;
+ };
+ 3B06AD1E1E4923F5004D2608 /* Thin Binary */ = {
+ isa = PBXShellScriptBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ );
+ inputPaths = (
+ );
+ name = "Thin Binary";
+ outputPaths = (
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ shellPath = /bin/sh;
+ shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" embed_and_thin";
+ };
+ 9740EEB61CF901F6004384FC /* Run Script */ = {
+ isa = PBXShellScriptBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ );
+ inputPaths = (
+ );
+ name = "Run Script";
+ outputPaths = (
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ shellPath = /bin/sh;
+ shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" build";
+ };
+ C8B6F6305BF18244B3C47F42 /* [CP] Check Pods Manifest.lock */ = {
+ isa = PBXShellScriptBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ );
+ inputFileListPaths = (
+ );
+ inputPaths = (
+ "${PODS_PODFILE_DIR_PATH}/Podfile.lock",
+ "${PODS_ROOT}/Manifest.lock",
+ );
+ name = "[CP] Check Pods Manifest.lock";
+ outputFileListPaths = (
+ );
+ outputPaths = (
+ "$(DERIVED_FILE_DIR)/Pods-Runner-checkManifestLockResult.txt",
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ shellPath = /bin/sh;
+ shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n";
+ showEnvVarsInLog = 0;
+ };
+/* End PBXShellScriptBuildPhase section */
+
+/* Begin PBXSourcesBuildPhase section */
+ 97C146EA1CF9000F007C117D /* Sources */ = {
+ isa = PBXSourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ 74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */,
+ 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+/* End PBXSourcesBuildPhase section */
+
+/* Begin PBXVariantGroup section */
+ 97C146FA1CF9000F007C117D /* Main.storyboard */ = {
+ isa = PBXVariantGroup;
+ children = (
+ 97C146FB1CF9000F007C117D /* Base */,
+ );
+ name = Main.storyboard;
+ sourceTree = "";
+ };
+ 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */ = {
+ isa = PBXVariantGroup;
+ children = (
+ 97C147001CF9000F007C117D /* Base */,
+ );
+ name = LaunchScreen.storyboard;
+ sourceTree = "";
+ };
+/* End PBXVariantGroup section */
+
+/* Begin XCBuildConfiguration section */
+ 249021D3217E4FDB00AE95B9 /* Profile */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ ALWAYS_SEARCH_USER_PATHS = NO;
+ CLANG_ANALYZER_NONNULL = YES;
+ CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
+ CLANG_CXX_LIBRARY = "libc++";
+ CLANG_ENABLE_MODULES = YES;
+ CLANG_ENABLE_OBJC_ARC = YES;
+ CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
+ CLANG_WARN_BOOL_CONVERSION = YES;
+ CLANG_WARN_COMMA = YES;
+ CLANG_WARN_CONSTANT_CONVERSION = YES;
+ CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
+ CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
+ CLANG_WARN_EMPTY_BODY = YES;
+ CLANG_WARN_ENUM_CONVERSION = YES;
+ CLANG_WARN_INFINITE_RECURSION = YES;
+ CLANG_WARN_INT_CONVERSION = YES;
+ CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
+ CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
+ CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
+ CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
+ CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
+ CLANG_WARN_STRICT_PROTOTYPES = YES;
+ CLANG_WARN_SUSPICIOUS_MOVE = YES;
+ CLANG_WARN_UNREACHABLE_CODE = YES;
+ CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
+ "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
+ COPY_PHASE_STRIP = NO;
+ DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
+ ENABLE_NS_ASSERTIONS = NO;
+ ENABLE_STRICT_OBJC_MSGSEND = YES;
+ GCC_C_LANGUAGE_STANDARD = gnu99;
+ GCC_NO_COMMON_BLOCKS = YES;
+ GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
+ GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
+ GCC_WARN_UNDECLARED_SELECTOR = YES;
+ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
+ GCC_WARN_UNUSED_FUNCTION = YES;
+ GCC_WARN_UNUSED_VARIABLE = YES;
+ IPHONEOS_DEPLOYMENT_TARGET = 9.0;
+ MTL_ENABLE_DEBUG_INFO = NO;
+ SDKROOT = iphoneos;
+ SUPPORTED_PLATFORMS = iphoneos;
+ TARGETED_DEVICE_FAMILY = "1,2";
+ VALIDATE_PRODUCT = YES;
+ };
+ name = Profile;
+ };
+ 249021D4217E4FDB00AE95B9 /* Profile */ = {
+ isa = XCBuildConfiguration;
+ baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */;
+ buildSettings = {
+ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
+ CLANG_ENABLE_MODULES = YES;
+ CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
+ ENABLE_BITCODE = NO;
+ INFOPLIST_FILE = Runner/Info.plist;
+ LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
+ PRODUCT_BUNDLE_IDENTIFIER = com.pravera.flLocationExample;
+ PRODUCT_NAME = "$(TARGET_NAME)";
+ SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
+ SWIFT_VERSION = 5.0;
+ VERSIONING_SYSTEM = "apple-generic";
+ };
+ name = Profile;
+ };
+ 97C147031CF9000F007C117D /* Debug */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ ALWAYS_SEARCH_USER_PATHS = NO;
+ CLANG_ANALYZER_NONNULL = YES;
+ CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
+ CLANG_CXX_LIBRARY = "libc++";
+ CLANG_ENABLE_MODULES = YES;
+ CLANG_ENABLE_OBJC_ARC = YES;
+ CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
+ CLANG_WARN_BOOL_CONVERSION = YES;
+ CLANG_WARN_COMMA = YES;
+ CLANG_WARN_CONSTANT_CONVERSION = YES;
+ CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
+ CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
+ CLANG_WARN_EMPTY_BODY = YES;
+ CLANG_WARN_ENUM_CONVERSION = YES;
+ CLANG_WARN_INFINITE_RECURSION = YES;
+ CLANG_WARN_INT_CONVERSION = YES;
+ CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
+ CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
+ CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
+ CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
+ CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
+ CLANG_WARN_STRICT_PROTOTYPES = YES;
+ CLANG_WARN_SUSPICIOUS_MOVE = YES;
+ CLANG_WARN_UNREACHABLE_CODE = YES;
+ CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
+ "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
+ COPY_PHASE_STRIP = NO;
+ DEBUG_INFORMATION_FORMAT = dwarf;
+ ENABLE_STRICT_OBJC_MSGSEND = YES;
+ ENABLE_TESTABILITY = YES;
+ GCC_C_LANGUAGE_STANDARD = gnu99;
+ GCC_DYNAMIC_NO_PIC = NO;
+ GCC_NO_COMMON_BLOCKS = YES;
+ GCC_OPTIMIZATION_LEVEL = 0;
+ GCC_PREPROCESSOR_DEFINITIONS = (
+ "DEBUG=1",
+ "$(inherited)",
+ );
+ GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
+ GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
+ GCC_WARN_UNDECLARED_SELECTOR = YES;
+ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
+ GCC_WARN_UNUSED_FUNCTION = YES;
+ GCC_WARN_UNUSED_VARIABLE = YES;
+ IPHONEOS_DEPLOYMENT_TARGET = 9.0;
+ MTL_ENABLE_DEBUG_INFO = YES;
+ ONLY_ACTIVE_ARCH = YES;
+ SDKROOT = iphoneos;
+ TARGETED_DEVICE_FAMILY = "1,2";
+ };
+ name = Debug;
+ };
+ 97C147041CF9000F007C117D /* Release */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ ALWAYS_SEARCH_USER_PATHS = NO;
+ CLANG_ANALYZER_NONNULL = YES;
+ CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
+ CLANG_CXX_LIBRARY = "libc++";
+ CLANG_ENABLE_MODULES = YES;
+ CLANG_ENABLE_OBJC_ARC = YES;
+ CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
+ CLANG_WARN_BOOL_CONVERSION = YES;
+ CLANG_WARN_COMMA = YES;
+ CLANG_WARN_CONSTANT_CONVERSION = YES;
+ CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
+ CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
+ CLANG_WARN_EMPTY_BODY = YES;
+ CLANG_WARN_ENUM_CONVERSION = YES;
+ CLANG_WARN_INFINITE_RECURSION = YES;
+ CLANG_WARN_INT_CONVERSION = YES;
+ CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
+ CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
+ CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
+ CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
+ CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
+ CLANG_WARN_STRICT_PROTOTYPES = YES;
+ CLANG_WARN_SUSPICIOUS_MOVE = YES;
+ CLANG_WARN_UNREACHABLE_CODE = YES;
+ CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
+ "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
+ COPY_PHASE_STRIP = NO;
+ DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
+ ENABLE_NS_ASSERTIONS = NO;
+ ENABLE_STRICT_OBJC_MSGSEND = YES;
+ GCC_C_LANGUAGE_STANDARD = gnu99;
+ GCC_NO_COMMON_BLOCKS = YES;
+ GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
+ GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
+ GCC_WARN_UNDECLARED_SELECTOR = YES;
+ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
+ GCC_WARN_UNUSED_FUNCTION = YES;
+ GCC_WARN_UNUSED_VARIABLE = YES;
+ IPHONEOS_DEPLOYMENT_TARGET = 9.0;
+ MTL_ENABLE_DEBUG_INFO = NO;
+ SDKROOT = iphoneos;
+ SUPPORTED_PLATFORMS = iphoneos;
+ SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule";
+ TARGETED_DEVICE_FAMILY = "1,2";
+ VALIDATE_PRODUCT = YES;
+ };
+ name = Release;
+ };
+ 97C147061CF9000F007C117D /* Debug */ = {
+ isa = XCBuildConfiguration;
+ baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */;
+ buildSettings = {
+ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
+ CLANG_ENABLE_MODULES = YES;
+ CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
+ ENABLE_BITCODE = NO;
+ INFOPLIST_FILE = Runner/Info.plist;
+ LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
+ PRODUCT_BUNDLE_IDENTIFIER = com.pravera.flLocationExample;
+ PRODUCT_NAME = "$(TARGET_NAME)";
+ SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
+ SWIFT_OPTIMIZATION_LEVEL = "-Onone";
+ SWIFT_VERSION = 5.0;
+ VERSIONING_SYSTEM = "apple-generic";
+ };
+ name = Debug;
+ };
+ 97C147071CF9000F007C117D /* Release */ = {
+ isa = XCBuildConfiguration;
+ baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */;
+ buildSettings = {
+ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
+ CLANG_ENABLE_MODULES = YES;
+ CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
+ ENABLE_BITCODE = NO;
+ INFOPLIST_FILE = Runner/Info.plist;
+ LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
+ PRODUCT_BUNDLE_IDENTIFIER = com.pravera.flLocationExample;
+ PRODUCT_NAME = "$(TARGET_NAME)";
+ SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
+ SWIFT_VERSION = 5.0;
+ VERSIONING_SYSTEM = "apple-generic";
+ };
+ name = Release;
+ };
+/* End XCBuildConfiguration section */
+
+/* Begin XCConfigurationList section */
+ 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ 97C147031CF9000F007C117D /* Debug */,
+ 97C147041CF9000F007C117D /* Release */,
+ 249021D3217E4FDB00AE95B9 /* Profile */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
+ };
+ 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ 97C147061CF9000F007C117D /* Debug */,
+ 97C147071CF9000F007C117D /* Release */,
+ 249021D4217E4FDB00AE95B9 /* Profile */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
+ };
+/* End XCConfigurationList section */
+ };
+ rootObject = 97C146E61CF9000F007C117D /* Project object */;
+}
diff --git a/third_party/pub_patches/fl_location/example/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/third_party/pub_patches/fl_location/example/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata
new file mode 100644
index 000000000..919434a62
--- /dev/null
+++ b/third_party/pub_patches/fl_location/example/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata
@@ -0,0 +1,7 @@
+
+
+
+
+
diff --git a/third_party/pub_patches/fl_location/example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/third_party/pub_patches/fl_location/example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist
new file mode 100644
index 000000000..18d981003
--- /dev/null
+++ b/third_party/pub_patches/fl_location/example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist
@@ -0,0 +1,8 @@
+
+
+
+
+ IDEDidComputeMac32BitWarning
+
+
+
diff --git a/third_party/pub_patches/fl_location/example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings b/third_party/pub_patches/fl_location/example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings
new file mode 100644
index 000000000..f9b0d7c5e
--- /dev/null
+++ b/third_party/pub_patches/fl_location/example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings
@@ -0,0 +1,8 @@
+
+
+
+
+ PreviewsEnabled
+
+
+
diff --git a/third_party/pub_patches/fl_location/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme b/third_party/pub_patches/fl_location/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme
new file mode 100644
index 000000000..a28140cfd
--- /dev/null
+++ b/third_party/pub_patches/fl_location/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme
@@ -0,0 +1,91 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/third_party/pub_patches/fl_location/example/ios/Runner.xcworkspace/contents.xcworkspacedata b/third_party/pub_patches/fl_location/example/ios/Runner.xcworkspace/contents.xcworkspacedata
new file mode 100644
index 000000000..21a3cc14c
--- /dev/null
+++ b/third_party/pub_patches/fl_location/example/ios/Runner.xcworkspace/contents.xcworkspacedata
@@ -0,0 +1,10 @@
+
+
+
+
+
+
+
diff --git a/third_party/pub_patches/fl_location/example/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/third_party/pub_patches/fl_location/example/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist
new file mode 100644
index 000000000..18d981003
--- /dev/null
+++ b/third_party/pub_patches/fl_location/example/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist
@@ -0,0 +1,8 @@
+
+
+
+
+ IDEDidComputeMac32BitWarning
+
+
+
diff --git a/third_party/pub_patches/fl_location/example/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings b/third_party/pub_patches/fl_location/example/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings
new file mode 100644
index 000000000..f9b0d7c5e
--- /dev/null
+++ b/third_party/pub_patches/fl_location/example/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings
@@ -0,0 +1,8 @@
+
+
+
+
+ PreviewsEnabled
+
+
+
diff --git a/third_party/pub_patches/fl_location/example/ios/Runner/AppDelegate.swift b/third_party/pub_patches/fl_location/example/ios/Runner/AppDelegate.swift
new file mode 100644
index 000000000..70693e4a8
--- /dev/null
+++ b/third_party/pub_patches/fl_location/example/ios/Runner/AppDelegate.swift
@@ -0,0 +1,13 @@
+import UIKit
+import Flutter
+
+@UIApplicationMain
+@objc class AppDelegate: FlutterAppDelegate {
+ override func application(
+ _ application: UIApplication,
+ didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
+ ) -> Bool {
+ GeneratedPluginRegistrant.register(with: self)
+ return super.application(application, didFinishLaunchingWithOptions: launchOptions)
+ }
+}
diff --git a/third_party/pub_patches/fl_location/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json b/third_party/pub_patches/fl_location/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json
new file mode 100644
index 000000000..d36b1fab2
--- /dev/null
+++ b/third_party/pub_patches/fl_location/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json
@@ -0,0 +1,122 @@
+{
+ "images" : [
+ {
+ "size" : "20x20",
+ "idiom" : "iphone",
+ "filename" : "Icon-App-20x20@2x.png",
+ "scale" : "2x"
+ },
+ {
+ "size" : "20x20",
+ "idiom" : "iphone",
+ "filename" : "Icon-App-20x20@3x.png",
+ "scale" : "3x"
+ },
+ {
+ "size" : "29x29",
+ "idiom" : "iphone",
+ "filename" : "Icon-App-29x29@1x.png",
+ "scale" : "1x"
+ },
+ {
+ "size" : "29x29",
+ "idiom" : "iphone",
+ "filename" : "Icon-App-29x29@2x.png",
+ "scale" : "2x"
+ },
+ {
+ "size" : "29x29",
+ "idiom" : "iphone",
+ "filename" : "Icon-App-29x29@3x.png",
+ "scale" : "3x"
+ },
+ {
+ "size" : "40x40",
+ "idiom" : "iphone",
+ "filename" : "Icon-App-40x40@2x.png",
+ "scale" : "2x"
+ },
+ {
+ "size" : "40x40",
+ "idiom" : "iphone",
+ "filename" : "Icon-App-40x40@3x.png",
+ "scale" : "3x"
+ },
+ {
+ "size" : "60x60",
+ "idiom" : "iphone",
+ "filename" : "Icon-App-60x60@2x.png",
+ "scale" : "2x"
+ },
+ {
+ "size" : "60x60",
+ "idiom" : "iphone",
+ "filename" : "Icon-App-60x60@3x.png",
+ "scale" : "3x"
+ },
+ {
+ "size" : "20x20",
+ "idiom" : "ipad",
+ "filename" : "Icon-App-20x20@1x.png",
+ "scale" : "1x"
+ },
+ {
+ "size" : "20x20",
+ "idiom" : "ipad",
+ "filename" : "Icon-App-20x20@2x.png",
+ "scale" : "2x"
+ },
+ {
+ "size" : "29x29",
+ "idiom" : "ipad",
+ "filename" : "Icon-App-29x29@1x.png",
+ "scale" : "1x"
+ },
+ {
+ "size" : "29x29",
+ "idiom" : "ipad",
+ "filename" : "Icon-App-29x29@2x.png",
+ "scale" : "2x"
+ },
+ {
+ "size" : "40x40",
+ "idiom" : "ipad",
+ "filename" : "Icon-App-40x40@1x.png",
+ "scale" : "1x"
+ },
+ {
+ "size" : "40x40",
+ "idiom" : "ipad",
+ "filename" : "Icon-App-40x40@2x.png",
+ "scale" : "2x"
+ },
+ {
+ "size" : "76x76",
+ "idiom" : "ipad",
+ "filename" : "Icon-App-76x76@1x.png",
+ "scale" : "1x"
+ },
+ {
+ "size" : "76x76",
+ "idiom" : "ipad",
+ "filename" : "Icon-App-76x76@2x.png",
+ "scale" : "2x"
+ },
+ {
+ "size" : "83.5x83.5",
+ "idiom" : "ipad",
+ "filename" : "Icon-App-83.5x83.5@2x.png",
+ "scale" : "2x"
+ },
+ {
+ "size" : "1024x1024",
+ "idiom" : "ios-marketing",
+ "filename" : "Icon-App-1024x1024@1x.png",
+ "scale" : "1x"
+ }
+ ],
+ "info" : {
+ "version" : 1,
+ "author" : "xcode"
+ }
+}
diff --git a/third_party/pub_patches/fl_location/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png b/third_party/pub_patches/fl_location/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png
new file mode 100644
index 000000000..dc9ada472
Binary files /dev/null and b/third_party/pub_patches/fl_location/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png differ
diff --git a/third_party/pub_patches/fl_location/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png b/third_party/pub_patches/fl_location/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png
new file mode 100644
index 000000000..28c6bf030
Binary files /dev/null and b/third_party/pub_patches/fl_location/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png differ
diff --git a/third_party/pub_patches/fl_location/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png b/third_party/pub_patches/fl_location/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png
new file mode 100644
index 000000000..2ccbfd967
Binary files /dev/null and b/third_party/pub_patches/fl_location/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png differ
diff --git a/third_party/pub_patches/fl_location/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png b/third_party/pub_patches/fl_location/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png
new file mode 100644
index 000000000..f091b6b0b
Binary files /dev/null and b/third_party/pub_patches/fl_location/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png differ
diff --git a/third_party/pub_patches/fl_location/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png b/third_party/pub_patches/fl_location/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png
new file mode 100644
index 000000000..4cde12118
Binary files /dev/null and b/third_party/pub_patches/fl_location/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png differ
diff --git a/third_party/pub_patches/fl_location/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png b/third_party/pub_patches/fl_location/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png
new file mode 100644
index 000000000..d0ef06e7e
Binary files /dev/null and b/third_party/pub_patches/fl_location/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png differ
diff --git a/third_party/pub_patches/fl_location/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png b/third_party/pub_patches/fl_location/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png
new file mode 100644
index 000000000..dcdc2306c
Binary files /dev/null and b/third_party/pub_patches/fl_location/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png differ
diff --git a/third_party/pub_patches/fl_location/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png b/third_party/pub_patches/fl_location/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png
new file mode 100644
index 000000000..2ccbfd967
Binary files /dev/null and b/third_party/pub_patches/fl_location/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png differ
diff --git a/third_party/pub_patches/fl_location/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png b/third_party/pub_patches/fl_location/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png
new file mode 100644
index 000000000..c8f9ed8f5
Binary files /dev/null and b/third_party/pub_patches/fl_location/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png differ
diff --git a/third_party/pub_patches/fl_location/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png b/third_party/pub_patches/fl_location/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png
new file mode 100644
index 000000000..a6d6b8609
Binary files /dev/null and b/third_party/pub_patches/fl_location/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png differ
diff --git a/third_party/pub_patches/fl_location/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png b/third_party/pub_patches/fl_location/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png
new file mode 100644
index 000000000..a6d6b8609
Binary files /dev/null and b/third_party/pub_patches/fl_location/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png differ
diff --git a/third_party/pub_patches/fl_location/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png b/third_party/pub_patches/fl_location/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png
new file mode 100644
index 000000000..75b2d164a
Binary files /dev/null and b/third_party/pub_patches/fl_location/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png differ
diff --git a/third_party/pub_patches/fl_location/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png b/third_party/pub_patches/fl_location/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png
new file mode 100644
index 000000000..c4df70d39
Binary files /dev/null and b/third_party/pub_patches/fl_location/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png differ
diff --git a/third_party/pub_patches/fl_location/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png b/third_party/pub_patches/fl_location/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png
new file mode 100644
index 000000000..6a84f41e1
Binary files /dev/null and b/third_party/pub_patches/fl_location/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png differ
diff --git a/third_party/pub_patches/fl_location/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png b/third_party/pub_patches/fl_location/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png
new file mode 100644
index 000000000..d0e1f5853
Binary files /dev/null and b/third_party/pub_patches/fl_location/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png differ
diff --git a/third_party/pub_patches/fl_location/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json b/third_party/pub_patches/fl_location/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json
new file mode 100644
index 000000000..0bedcf2fd
--- /dev/null
+++ b/third_party/pub_patches/fl_location/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json
@@ -0,0 +1,23 @@
+{
+ "images" : [
+ {
+ "idiom" : "universal",
+ "filename" : "LaunchImage.png",
+ "scale" : "1x"
+ },
+ {
+ "idiom" : "universal",
+ "filename" : "LaunchImage@2x.png",
+ "scale" : "2x"
+ },
+ {
+ "idiom" : "universal",
+ "filename" : "LaunchImage@3x.png",
+ "scale" : "3x"
+ }
+ ],
+ "info" : {
+ "version" : 1,
+ "author" : "xcode"
+ }
+}
diff --git a/third_party/pub_patches/fl_location/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png b/third_party/pub_patches/fl_location/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png
new file mode 100644
index 000000000..9da19eaca
Binary files /dev/null and b/third_party/pub_patches/fl_location/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png differ
diff --git a/third_party/pub_patches/fl_location/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png b/third_party/pub_patches/fl_location/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png
new file mode 100644
index 000000000..9da19eaca
Binary files /dev/null and b/third_party/pub_patches/fl_location/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png differ
diff --git a/third_party/pub_patches/fl_location/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png b/third_party/pub_patches/fl_location/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png
new file mode 100644
index 000000000..9da19eaca
Binary files /dev/null and b/third_party/pub_patches/fl_location/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png differ
diff --git a/third_party/pub_patches/fl_location/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md b/third_party/pub_patches/fl_location/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md
new file mode 100644
index 000000000..89c2725b7
--- /dev/null
+++ b/third_party/pub_patches/fl_location/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md
@@ -0,0 +1,5 @@
+# Launch Screen Assets
+
+You can customize the launch screen with your own desired assets by replacing the image files in this directory.
+
+You can also do it by opening your Flutter project's Xcode project with `open ios/Runner.xcworkspace`, selecting `Runner/Assets.xcassets` in the Project Navigator and dropping in the desired images.
\ No newline at end of file
diff --git a/third_party/pub_patches/fl_location/example/ios/Runner/Base.lproj/LaunchScreen.storyboard b/third_party/pub_patches/fl_location/example/ios/Runner/Base.lproj/LaunchScreen.storyboard
new file mode 100644
index 000000000..f2e259c7c
--- /dev/null
+++ b/third_party/pub_patches/fl_location/example/ios/Runner/Base.lproj/LaunchScreen.storyboard
@@ -0,0 +1,37 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/third_party/pub_patches/fl_location/example/ios/Runner/Base.lproj/Main.storyboard b/third_party/pub_patches/fl_location/example/ios/Runner/Base.lproj/Main.storyboard
new file mode 100644
index 000000000..f3c28516f
--- /dev/null
+++ b/third_party/pub_patches/fl_location/example/ios/Runner/Base.lproj/Main.storyboard
@@ -0,0 +1,26 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/third_party/pub_patches/fl_location/example/ios/Runner/Info.plist b/third_party/pub_patches/fl_location/example/ios/Runner/Info.plist
new file mode 100644
index 000000000..1a4abef93
--- /dev/null
+++ b/third_party/pub_patches/fl_location/example/ios/Runner/Info.plist
@@ -0,0 +1,56 @@
+
+
+
+
+ CFBundleDevelopmentRegion
+ $(DEVELOPMENT_LANGUAGE)
+ CFBundleExecutable
+ $(EXECUTABLE_NAME)
+ CFBundleIdentifier
+ $(PRODUCT_BUNDLE_IDENTIFIER)
+ CFBundleInfoDictionaryVersion
+ 6.0
+ CFBundleName
+ fl_location_example
+ CFBundlePackageType
+ APPL
+ CFBundleShortVersionString
+ $(FLUTTER_BUILD_NAME)
+ CFBundleSignature
+ ????
+ CFBundleVersion
+ $(FLUTTER_BUILD_NUMBER)
+ LSRequiresIPhoneOS
+
+ UILaunchStoryboardName
+ LaunchScreen
+ UIMainStoryboardFile
+ Main
+ UISupportedInterfaceOrientations
+
+ UIInterfaceOrientationPortrait
+ UIInterfaceOrientationLandscapeLeft
+ UIInterfaceOrientationLandscapeRight
+
+ UISupportedInterfaceOrientations~ipad
+
+ UIInterfaceOrientationPortrait
+ UIInterfaceOrientationPortraitUpsideDown
+ UIInterfaceOrientationLandscapeLeft
+ UIInterfaceOrientationLandscapeRight
+
+ UIViewControllerBasedStatusBarAppearance
+
+ NSLocationWhenInUseUsageDescription
+ Used to collect location data.
+ NSLocationAlwaysAndWhenInUseUsageDescription
+ Used to collect location data in the background.
+ NSLocationAlwaysUsageDescription
+ Used to collect location data in the background.
+ UIBackgroundModes
+
+ fetch
+ location
+
+
+
diff --git a/third_party/pub_patches/fl_location/example/ios/Runner/Runner-Bridging-Header.h b/third_party/pub_patches/fl_location/example/ios/Runner/Runner-Bridging-Header.h
new file mode 100644
index 000000000..308a2a560
--- /dev/null
+++ b/third_party/pub_patches/fl_location/example/ios/Runner/Runner-Bridging-Header.h
@@ -0,0 +1 @@
+#import "GeneratedPluginRegistrant.h"
diff --git a/third_party/pub_patches/fl_location/example/lib/main.dart b/third_party/pub_patches/fl_location/example/lib/main.dart
new file mode 100644
index 000000000..7aaf12956
--- /dev/null
+++ b/third_party/pub_patches/fl_location/example/lib/main.dart
@@ -0,0 +1,181 @@
+import 'dart:async';
+// import 'dart:io';
+
+import 'package:fl_location/fl_location.dart';
+import 'package:flutter/foundation.dart';
+import 'package:flutter/material.dart';
+
+void main() => runApp(ExampleApp());
+
+enum ButtonState { LOADING, DONE, DISABLED }
+
+class ExampleApp extends StatefulWidget {
+ @override
+ _ExampleAppState createState() => _ExampleAppState();
+}
+
+class _ExampleAppState extends State {
+ StreamSubscription? _locationSubscription;
+
+ String _result = '';
+ ButtonState _getLocationButtonState = ButtonState.DONE;
+ ButtonState _listenLocationStreamButtonState = ButtonState.DONE;
+
+ Future _checkAndRequestPermission({bool? background}) async {
+ if (!await FlLocation.isLocationServicesEnabled) {
+ // Location services are disabled.
+ return false;
+ }
+
+ var locationPermission = await FlLocation.checkLocationPermission();
+ if (locationPermission == LocationPermission.deniedForever) {
+ // Cannot request runtime permission because location permission is denied forever.
+ return false;
+ } else if (locationPermission == LocationPermission.denied) {
+ // Ask the user for location permission.
+ locationPermission = await FlLocation.requestLocationPermission();
+ if (locationPermission == LocationPermission.denied ||
+ locationPermission == LocationPermission.deniedForever) return false;
+ }
+
+ // Location permission must always be allowed (LocationPermission.always)
+ // to collect location data in the background.
+ if (background == true &&
+ locationPermission == LocationPermission.whileInUse) return false;
+
+ // Location services has been enabled and permission have been granted.
+ return true;
+ }
+
+ void _refreshPage() {
+ setState(() {});
+ }
+
+ Future _getLocation() async {
+ if (await _checkAndRequestPermission()) {
+ _getLocationButtonState = ButtonState.LOADING;
+ _listenLocationStreamButtonState = ButtonState.DISABLED;
+ _refreshPage();
+
+ final timeLimit = const Duration(seconds: 10);
+ await FlLocation.getLocation(timeLimit: timeLimit).then((location) {
+ _result = location.toJson().toString();
+ }).onError((error, stackTrace) {
+ _result = error.toString();
+ }).whenComplete(() {
+ _getLocationButtonState = ButtonState.DONE;
+ _listenLocationStreamButtonState = ButtonState.DONE;
+ _refreshPage();
+ });
+ }
+ }
+
+ Future _listenLocationStream() async {
+ if (await _checkAndRequestPermission()) {
+ if (_locationSubscription != null) await _cancelLocationSubscription();
+
+ _locationSubscription = FlLocation.getLocationStream()
+ .handleError(_handleError)
+ .listen((event) {
+ _result = event.toJson().toString();
+ _refreshPage();
+ });
+
+ _getLocationButtonState = ButtonState.DISABLED;
+ _refreshPage();
+ }
+ }
+
+ Future _cancelLocationSubscription() async {
+ await _locationSubscription?.cancel();
+ _locationSubscription = null;
+
+ _getLocationButtonState = ButtonState.DONE;
+ _refreshPage();
+ }
+
+ void _handleError(dynamic error, StackTrace stackTrace) {
+ _result = error.toString();
+ _refreshPage();
+ }
+
+ @override
+ void initState() {
+ super.initState();
+ // The getLocationServicesStatusStream function is not available in Web.
+ if (!kIsWeb) {
+ FlLocation.getLocationServicesStatusStream().listen((event) {
+ print('location services status: $event');
+ });
+ }
+ }
+
+ @override
+ void dispose() {
+ _locationSubscription?.cancel();
+ super.dispose();
+ }
+
+ @override
+ Widget build(BuildContext context) {
+ return MaterialApp(
+ home: Scaffold(
+ appBar: AppBar(
+ title: const Text('Plugin example app'),
+ ),
+ body: _buildContentView(),
+ ),
+ );
+ }
+
+ Widget _buildContentView() {
+ final buttonList = Column(
+ children: [
+ const SizedBox(height: 8.0),
+ _buildTestButton(
+ text: 'GET Location',
+ state: _getLocationButtonState,
+ onPressed: _getLocation,
+ ),
+ const SizedBox(height: 8.0),
+ _buildTestButton(
+ text: _locationSubscription == null
+ ? 'Listen LocationStream'
+ : 'Cancel LocationSubscription',
+ state: _listenLocationStreamButtonState,
+ onPressed: _locationSubscription == null
+ ? _listenLocationStream
+ : _cancelLocationSubscription,
+ ),
+ ],
+ );
+
+ final resultText =
+ Text(_result, style: Theme.of(context).textTheme.bodyText1);
+
+ return ListView(
+ padding: const EdgeInsets.symmetric(horizontal: 10.0),
+ children: [
+ buttonList,
+ const SizedBox(height: 10.0),
+ resultText,
+ ],
+ );
+ }
+
+ Widget _buildTestButton({
+ required String text,
+ required ButtonState state,
+ required VoidCallback? onPressed,
+ }) {
+ return ElevatedButton(
+ child: state == ButtonState.LOADING
+ ? SizedBox.fromSize(
+ size: const Size(20.0, 20.0),
+ child: const CircularProgressIndicator(strokeWidth: 2.0),
+ )
+ : Text(text),
+ onPressed: state == ButtonState.DONE ? onPressed : null,
+ );
+ }
+}
diff --git a/third_party/pub_patches/fl_location/example/pubspec.yaml b/third_party/pub_patches/fl_location/example/pubspec.yaml
new file mode 100644
index 000000000..e2c162b77
--- /dev/null
+++ b/third_party/pub_patches/fl_location/example/pubspec.yaml
@@ -0,0 +1,26 @@
+name: fl_location_example
+description: Demonstrates how to use the fl_location plugin.
+publish_to: 'none'
+
+environment:
+ sdk: ">=2.17.0 <3.0.0"
+ flutter: ">=3.0.0"
+
+dependencies:
+ flutter:
+ sdk: flutter
+
+ fl_location:
+ # When depending on this package from a real application you should use:
+ # fl_location: ^x.y.z
+ # See https://dart.dev/tools/pub/dependencies#version-constraints
+ # The example app is bundled with the plugin so we use a path dependency on
+ # the parent directory to use the current plugin's version.
+ path: ../
+
+dev_dependencies:
+ flutter_test:
+ sdk: flutter
+
+flutter:
+ uses-material-design: true
diff --git a/third_party/pub_patches/fl_location/example/test/widget_test.dart b/third_party/pub_patches/fl_location/example/test/widget_test.dart
new file mode 100644
index 000000000..153f05566
--- /dev/null
+++ b/third_party/pub_patches/fl_location/example/test/widget_test.dart
@@ -0,0 +1,3 @@
+void main() {
+
+}
diff --git a/third_party/pub_patches/fl_location/example/web/favicon.png b/third_party/pub_patches/fl_location/example/web/favicon.png
new file mode 100644
index 000000000..8aaa46ac1
Binary files /dev/null and b/third_party/pub_patches/fl_location/example/web/favicon.png differ
diff --git a/third_party/pub_patches/fl_location/example/web/icons/Icon-192.png b/third_party/pub_patches/fl_location/example/web/icons/Icon-192.png
new file mode 100644
index 000000000..b749bfef0
Binary files /dev/null and b/third_party/pub_patches/fl_location/example/web/icons/Icon-192.png differ
diff --git a/third_party/pub_patches/fl_location/example/web/icons/Icon-512.png b/third_party/pub_patches/fl_location/example/web/icons/Icon-512.png
new file mode 100644
index 000000000..88cfd48df
Binary files /dev/null and b/third_party/pub_patches/fl_location/example/web/icons/Icon-512.png differ
diff --git a/third_party/pub_patches/fl_location/example/web/index.html b/third_party/pub_patches/fl_location/example/web/index.html
new file mode 100644
index 000000000..e9869bbec
--- /dev/null
+++ b/third_party/pub_patches/fl_location/example/web/index.html
@@ -0,0 +1,98 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ fl_location_example
+
+
+
+
+
+
+
diff --git a/third_party/pub_patches/fl_location/example/web/manifest.json b/third_party/pub_patches/fl_location/example/web/manifest.json
new file mode 100644
index 000000000..166f121e9
--- /dev/null
+++ b/third_party/pub_patches/fl_location/example/web/manifest.json
@@ -0,0 +1,23 @@
+{
+ "name": "fl_location_example",
+ "short_name": "fl_location_example",
+ "start_url": ".",
+ "display": "standalone",
+ "background_color": "#0175C2",
+ "theme_color": "#0175C2",
+ "description": "Demonstrates how to use the fl_location plugin.",
+ "orientation": "portrait-primary",
+ "prefer_related_applications": false,
+ "icons": [
+ {
+ "src": "icons/Icon-192.png",
+ "sizes": "192x192",
+ "type": "image/png"
+ },
+ {
+ "src": "icons/Icon-512.png",
+ "sizes": "512x512",
+ "type": "image/png"
+ }
+ ]
+}
diff --git a/third_party/pub_patches/fl_location/ios/Classes/FlLocationPlugin.h b/third_party/pub_patches/fl_location/ios/Classes/FlLocationPlugin.h
new file mode 100644
index 000000000..78cec9b43
--- /dev/null
+++ b/third_party/pub_patches/fl_location/ios/Classes/FlLocationPlugin.h
@@ -0,0 +1,4 @@
+#import
+
+@interface FlLocationPlugin : NSObject
+@end
diff --git a/third_party/pub_patches/fl_location/ios/Classes/FlLocationPlugin.m b/third_party/pub_patches/fl_location/ios/Classes/FlLocationPlugin.m
new file mode 100644
index 000000000..e750ad8f4
--- /dev/null
+++ b/third_party/pub_patches/fl_location/ios/Classes/FlLocationPlugin.m
@@ -0,0 +1,15 @@
+#import "FlLocationPlugin.h"
+#if __has_include()
+#import
+#else
+// Support project import fallback if the generated compatibility header
+// is not copied when this plugin is created as a library.
+// https://forums.swift.org/t/swift-static-libraries-dont-copy-generated-objective-c-header/19816
+#import "fl_location-Swift.h"
+#endif
+
+@implementation FlLocationPlugin
++ (void)registerWithRegistrar:(NSObject*)registrar {
+ [SwiftFlLocationPlugin registerWithRegistrar:registrar];
+}
+@end
diff --git a/third_party/pub_patches/fl_location/ios/Classes/LocationServicesStatusStreamHandlerImpl.swift b/third_party/pub_patches/fl_location/ios/Classes/LocationServicesStatusStreamHandlerImpl.swift
new file mode 100644
index 000000000..3b56d4efd
--- /dev/null
+++ b/third_party/pub_patches/fl_location/ios/Classes/LocationServicesStatusStreamHandlerImpl.swift
@@ -0,0 +1,33 @@
+//
+// LocationServicesStatusStreamHandlerImpl.swift
+// fl_location
+//
+// Created by WOO JIN HWANG on 2021/07/27.
+//
+
+import Flutter
+import Foundation
+
+class LocationServicesStatusStreamHandlerImpl: NSObject, FlutterStreamHandler {
+ private let channel: FlutterEventChannel
+ private let serviceProvider: ServiceProvider
+
+ init(messenger: FlutterBinaryMessenger, serviceProvider: ServiceProvider) {
+ self.serviceProvider = serviceProvider
+ self.channel = FlutterEventChannel(name: "plugins.pravera.com/fl_location/location_services_status", binaryMessenger: messenger)
+ super.init()
+ self.channel.setStreamHandler(self)
+ }
+
+ func onListen(withArguments arguments: Any?, eventSink events: @escaping FlutterEventSink) -> FlutterError? {
+ serviceProvider.getLocationServicesStatusWatcher().start { (locationServicesStatus) in
+ events(locationServicesStatus.rawValue)
+ }
+ return nil
+ }
+
+ func onCancel(withArguments arguments: Any?) -> FlutterError? {
+ serviceProvider.getLocationServicesStatusWatcher().stop()
+ return nil
+ }
+}
diff --git a/third_party/pub_patches/fl_location/ios/Classes/LocationStreamHandlerImpl.swift b/third_party/pub_patches/fl_location/ios/Classes/LocationStreamHandlerImpl.swift
new file mode 100644
index 000000000..3dd7dbf0c
--- /dev/null
+++ b/third_party/pub_patches/fl_location/ios/Classes/LocationStreamHandlerImpl.swift
@@ -0,0 +1,55 @@
+//
+// LocationStreamHandlerImpl.swift
+// fl_location
+//
+// Created by WOO JIN HWANG on 2021/07/29.
+//
+
+import Flutter
+import Foundation
+
+class LocationStreamHandlerImpl: NSObject, FlutterStreamHandler, LocationDataHandler {
+ private let channel: FlutterEventChannel
+ private let serviceProvider: ServiceProvider
+
+ private var locationEventSink: FlutterEventSink? = nil
+ private var locationDataProviderHashCode: Int? = nil
+
+ init(messenger: FlutterBinaryMessenger, serviceProvider: ServiceProvider) {
+ self.serviceProvider = serviceProvider
+ self.channel = FlutterEventChannel(name: "plugins.pravera.com/fl_location/updates", binaryMessenger: messenger)
+ super.init()
+ self.channel.setStreamHandler(self)
+ }
+
+ func onListen(withArguments arguments: Any?, eventSink events: @escaping FlutterEventSink) -> FlutterError? {
+ let argsDict = arguments as? Dictionary
+ let settings = LocationSettings(from: argsDict)
+
+ locationEventSink = events
+ locationDataProviderHashCode = serviceProvider
+ .getLocationDataProviderManager()
+ .startUpdatingLocation(handler: self, settings: settings)
+ return nil
+ }
+
+ func onCancel(withArguments arguments: Any?) -> FlutterError? {
+ locationEventSink = nil
+ if locationDataProviderHashCode != nil {
+ serviceProvider
+ .getLocationDataProviderManager()
+ .stopUpdatingLocation(hashCode: locationDataProviderHashCode!)
+ locationDataProviderHashCode = nil
+ }
+ return nil
+ }
+
+ func onLocationUpdate(locationJson: String) {
+ locationEventSink?(locationJson)
+ }
+
+ func onLocationError(errorCode: ErrorCodes) {
+ if locationEventSink == nil { return }
+ ErrorHandleUtils.handleStreamError(events: locationEventSink!, errorCode: errorCode)
+ }
+}
diff --git a/third_party/pub_patches/fl_location/ios/Classes/MethodCallHandlerImpl.swift b/third_party/pub_patches/fl_location/ios/Classes/MethodCallHandlerImpl.swift
new file mode 100644
index 000000000..7f35b5272
--- /dev/null
+++ b/third_party/pub_patches/fl_location/ios/Classes/MethodCallHandlerImpl.swift
@@ -0,0 +1,44 @@
+//
+// MethodCallHandlerImpl.swift
+// fl_location
+//
+// Created by WOO JIN HWANG on 2021/07/26.
+//
+
+import Flutter
+import Foundation
+
+class MethodCallHandlerImpl: NSObject {
+ private let channel: FlutterMethodChannel
+ private let serviceProvider: ServiceProvider
+
+ init(messenger: FlutterBinaryMessenger, serviceProvider: ServiceProvider) {
+ self.channel = FlutterMethodChannel(name: "plugins.pravera.com/fl_location", binaryMessenger: messenger)
+ self.serviceProvider = serviceProvider
+ super.init()
+ self.channel.setMethodCallHandler(onMethodCall)
+ }
+
+ func onMethodCall(call: FlutterMethodCall, result: @escaping FlutterResult) {
+ switch call.method {
+ case "checkLocationServicesStatus":
+ result(LocationServicesUtils.checkLocationServicesStatus().rawValue)
+ case "checkLocationPermission":
+ let handler = LocationPermissionHandlerImpl(result)
+ serviceProvider.getLocationPermissionManager().checkLocationPermission(handler: handler)
+ case "requestLocationPermission":
+ let handler = LocationPermissionHandlerImpl(result)
+ serviceProvider.getLocationPermissionManager().requestLocationPermission(handler: handler)
+ case "getLocation":
+ let argsDict = call.arguments as? Dictionary
+ let settings = LocationSettings(from: argsDict)
+
+ let handler = LocationDataHandlerImpl(result)
+ serviceProvider
+ .getLocationDataProviderManager()
+ .getLocation(handler: handler, settings: settings)
+ default:
+ result(FlutterMethodNotImplemented)
+ }
+ }
+}
diff --git a/third_party/pub_patches/fl_location/ios/Classes/SwiftFlLocationPlugin.swift b/third_party/pub_patches/fl_location/ios/Classes/SwiftFlLocationPlugin.swift
new file mode 100644
index 000000000..0f1f14006
--- /dev/null
+++ b/third_party/pub_patches/fl_location/ios/Classes/SwiftFlLocationPlugin.swift
@@ -0,0 +1,36 @@
+import Flutter
+import UIKit
+
+public class SwiftFlLocationPlugin: NSObject, FlutterPlugin, ServiceProvider {
+ private var locationPermissionManager: LocationPermissionManager? = nil
+ private var locationDataProviderManager: LocationDataProviderManager? = nil
+ private var locationServicesStatusWatcher: LocationServicesStatusWatcher? = nil
+
+ private var methodCallHandler: MethodCallHandlerImpl? = nil
+ private var locationStreamHandler: LocationStreamHandlerImpl? = nil
+ private var locationServicesStatusStreamHandler: LocationServicesStatusStreamHandlerImpl? = nil
+
+ public static func register(with registrar: FlutterPluginRegistrar) {
+ let instance = SwiftFlLocationPlugin()
+ instance.initServices()
+ instance.initChannels(registrar.messenger())
+ }
+
+ private func initServices() {
+ locationPermissionManager = LocationPermissionManager()
+ locationDataProviderManager = LocationDataProviderManager()
+ locationServicesStatusWatcher = LocationServicesStatusWatcher()
+ }
+
+ private func initChannels(_ messenger: FlutterBinaryMessenger) {
+ methodCallHandler = MethodCallHandlerImpl(messenger: messenger, serviceProvider: self)
+ locationStreamHandler = LocationStreamHandlerImpl(messenger: messenger, serviceProvider: self)
+ locationServicesStatusStreamHandler = LocationServicesStatusStreamHandlerImpl(messenger: messenger, serviceProvider: self)
+ }
+
+ func getLocationPermissionManager() -> LocationPermissionManager { return locationPermissionManager! }
+
+ func getLocationDataProviderManager() -> LocationDataProviderManager { return locationDataProviderManager! }
+
+ func getLocationServicesStatusWatcher() -> LocationServicesStatusWatcher { return locationServicesStatusWatcher! }
+}
diff --git a/third_party/pub_patches/fl_location/ios/Classes/errors/ErrorCodes.swift b/third_party/pub_patches/fl_location/ios/Classes/errors/ErrorCodes.swift
new file mode 100644
index 000000000..2e681c3fe
--- /dev/null
+++ b/third_party/pub_patches/fl_location/ios/Classes/errors/ErrorCodes.swift
@@ -0,0 +1,25 @@
+//
+// ErrorCodes.swift
+// fl_location
+//
+// Created by WOO JIN HWANG on 2021/07/27.
+//
+
+import Foundation
+
+enum ErrorCodes: String {
+ case LOCATION_USAGE_DESCRIPTION_NOT_FOUND
+ case LOCATION_UPDATE_FAILED
+ case LOCATION_DATA_ENCODING_FAILED
+
+ func message() -> String {
+ switch self {
+ case .LOCATION_USAGE_DESCRIPTION_NOT_FOUND:
+ return "The location usage description key could not be found in the Info.plist file. You are required to include the NSLocationWhenInUseUsageDescription and NSLocationAlwaysAndWhenInUsageDescription keys in your app's Info.plist file. (If your app supports iOS 10 and earlier, the NSLocationAlwaysUsageDescription key is also required.) If those keys are not present, authorization requests fail immediately."
+ case .LOCATION_UPDATE_FAILED:
+ return "Location data could not be obtained because location update failed."
+ case .LOCATION_DATA_ENCODING_FAILED:
+ return "Failed to encode location data in JSON format."
+ }
+ }
+}
diff --git a/third_party/pub_patches/fl_location/ios/Classes/models/LocationAccuracy.swift b/third_party/pub_patches/fl_location/ios/Classes/models/LocationAccuracy.swift
new file mode 100644
index 000000000..56eb8fd53
--- /dev/null
+++ b/third_party/pub_patches/fl_location/ios/Classes/models/LocationAccuracy.swift
@@ -0,0 +1,52 @@
+//
+// LocationAccuracy.swift
+// fl_location
+//
+// Created by WOO JIN HWANG on 2021/07/28.
+//
+
+import CoreLocation
+import Foundation
+
+enum LocationAccuracy: Int {
+ case POWER_SAVE = 0
+ case LOW = 1
+ case BALANCED = 2
+ case HIGH = 3
+ case BEST = 4
+ case NAVIGATION = 5
+
+ static func fromIndex(index: Int) -> LocationAccuracy {
+ switch index {
+ case 1:
+ return .LOW
+ case 2:
+ return .BALANCED
+ case 3:
+ return .HIGH
+ case 4:
+ return .BEST
+ case 5:
+ return .NAVIGATION
+ default:
+ return .POWER_SAVE
+ }
+ }
+
+ func toCLLocationAccuracy() -> CLLocationAccuracy {
+ switch self {
+ case .POWER_SAVE:
+ return kCLLocationAccuracyThreeKilometers
+ case .LOW:
+ return kCLLocationAccuracyKilometer
+ case .BALANCED:
+ return kCLLocationAccuracyHundredMeters
+ case .HIGH:
+ return kCLLocationAccuracyNearestTenMeters
+ case .BEST:
+ return kCLLocationAccuracyBest
+ case .NAVIGATION:
+ return kCLLocationAccuracyBestForNavigation
+ }
+ }
+}
diff --git a/third_party/pub_patches/fl_location/ios/Classes/models/LocationData.swift b/third_party/pub_patches/fl_location/ios/Classes/models/LocationData.swift
new file mode 100644
index 000000000..69ecc8b83
--- /dev/null
+++ b/third_party/pub_patches/fl_location/ios/Classes/models/LocationData.swift
@@ -0,0 +1,39 @@
+//
+// LocationData.swift
+// fl_location
+//
+// Created by WOO JIN HWANG on 2021/07/29.
+//
+
+import CoreLocation
+import Foundation
+
+struct LocationData: Codable {
+ let latitude: Double
+ let longitude: Double
+ let accuracy: Double
+ let altitude: Double
+ let heading: Double
+ let speed: Double
+ let speedAccuracy: Double
+ let millisecondsSinceEpoch: Double
+ let isMock: Bool
+
+ init(from location: CLLocation) {
+ self.latitude = location.coordinate.latitude
+ self.longitude = location.coordinate.longitude
+ self.accuracy = location.horizontalAccuracy
+ self.altitude = location.altitude
+ self.heading = location.course
+ self.speed = location.speed
+
+ if #available(iOS 10.0, *) {
+ self.speedAccuracy = location.speedAccuracy
+ } else {
+ self.speedAccuracy = 0.0
+ }
+
+ self.millisecondsSinceEpoch = location.timestamp.timeIntervalSince1970 * 1000.0
+ self.isMock = false
+ }
+}
diff --git a/third_party/pub_patches/fl_location/ios/Classes/models/LocationPermission.swift b/third_party/pub_patches/fl_location/ios/Classes/models/LocationPermission.swift
new file mode 100644
index 000000000..8482824d0
--- /dev/null
+++ b/third_party/pub_patches/fl_location/ios/Classes/models/LocationPermission.swift
@@ -0,0 +1,31 @@
+//
+// LocationPermission.swift
+// fl_location
+//
+// Created by WOO JIN HWANG on 2021/07/26.
+//
+
+import CoreLocation
+import Foundation
+
+enum LocationPermission: Int {
+ case ALWAYS = 0
+ case WHILE_IN_USE = 1
+ case DENIED = 2
+ case DENIED_FOREVER = 3
+
+ static func fromAuthorizationStatus(status: CLAuthorizationStatus) -> LocationPermission {
+ switch status {
+ case .authorizedAlways:
+ return LocationPermission.ALWAYS
+ case .authorizedWhenInUse:
+ return LocationPermission.WHILE_IN_USE
+ case .notDetermined, .restricted:
+ return LocationPermission.DENIED
+ case .denied:
+ return LocationPermission.DENIED_FOREVER
+ default:
+ return LocationPermission.DENIED
+ }
+ }
+}
diff --git a/third_party/pub_patches/fl_location/ios/Classes/models/LocationServicesStatus.swift b/third_party/pub_patches/fl_location/ios/Classes/models/LocationServicesStatus.swift
new file mode 100644
index 000000000..4bb2f305a
--- /dev/null
+++ b/third_party/pub_patches/fl_location/ios/Classes/models/LocationServicesStatus.swift
@@ -0,0 +1,13 @@
+//
+// LocationServicesStatus.swift
+// fl_location
+//
+// Created by WOO JIN HWANG on 2021/07/26.
+//
+
+import Foundation
+
+enum LocationServicesStatus: Int {
+ case ENABLED = 0
+ case DISABLED = 1
+}
diff --git a/third_party/pub_patches/fl_location/ios/Classes/models/LocationSettings.swift b/third_party/pub_patches/fl_location/ios/Classes/models/LocationSettings.swift
new file mode 100644
index 000000000..c817f9f41
--- /dev/null
+++ b/third_party/pub_patches/fl_location/ios/Classes/models/LocationSettings.swift
@@ -0,0 +1,24 @@
+//
+// LocationSettings.swift
+// fl_location
+//
+// Created by WOO JIN HWANG on 2021/07/29.
+//
+
+import Foundation
+
+class LocationSettings {
+ let accuracy: LocationAccuracy
+ let interval: Int?
+ let distanceFilter: Double?
+
+ init(from dictionary: Dictionary?) {
+ let rAccuracy = dictionary?["accuracy"] as? Int ?? LocationAccuracy.BEST.rawValue
+ let rInterval = dictionary?["interval"] as? Int
+ let rDistanceFilter = dictionary?["distanceFilter"] as? Double
+
+ self.accuracy = LocationAccuracy.fromIndex(index: rAccuracy)
+ self.interval = rInterval
+ self.distanceFilter = rDistanceFilter
+ }
+}
diff --git a/third_party/pub_patches/fl_location/ios/Classes/service/LocationDataHandler.swift b/third_party/pub_patches/fl_location/ios/Classes/service/LocationDataHandler.swift
new file mode 100644
index 000000000..fa71b3d92
--- /dev/null
+++ b/third_party/pub_patches/fl_location/ios/Classes/service/LocationDataHandler.swift
@@ -0,0 +1,13 @@
+//
+// LocationDataHandler.swift
+// fl_location
+//
+// Created by WOO JIN HWANG on 2021/07/29.
+//
+
+import Foundation
+
+protocol LocationDataHandler {
+ func onLocationUpdate(locationJson: String)
+ func onLocationError(errorCode: ErrorCodes)
+}
diff --git a/third_party/pub_patches/fl_location/ios/Classes/service/LocationDataHandlerImpl.swift b/third_party/pub_patches/fl_location/ios/Classes/service/LocationDataHandlerImpl.swift
new file mode 100644
index 000000000..9504d8575
--- /dev/null
+++ b/third_party/pub_patches/fl_location/ios/Classes/service/LocationDataHandlerImpl.swift
@@ -0,0 +1,24 @@
+//
+// LocationDataHandlerImpl.swift
+// fl_location
+//
+// Created by WOO JIN HWANG on 2021/08/09.
+//
+
+import Foundation
+
+class LocationDataHandlerImpl: NSObject, LocationDataHandler {
+ private let result: FlutterResult
+
+ init(_ result: @escaping FlutterResult) {
+ self.result = result
+ }
+
+ func onLocationUpdate(locationJson: String) {
+ result(locationJson)
+ }
+
+ func onLocationError(errorCode: ErrorCodes) {
+ ErrorHandleUtils.handleMethodCallError(result: result, errorCode: errorCode)
+ }
+}
diff --git a/third_party/pub_patches/fl_location/ios/Classes/service/LocationDataHandlerImplForManager.swift b/third_party/pub_patches/fl_location/ios/Classes/service/LocationDataHandlerImplForManager.swift
new file mode 100644
index 000000000..4b83cb040
--- /dev/null
+++ b/third_party/pub_patches/fl_location/ios/Classes/service/LocationDataHandlerImplForManager.swift
@@ -0,0 +1,28 @@
+//
+// LocationDataHandlerImplForManager.swift
+// fl_location
+//
+// Created by WOO JIN HWANG on 2021/08/09.
+//
+
+import Foundation
+
+class LocationDataHandlerImplForManager: NSObject, LocationDataHandler {
+ private let handler: LocationDataHandler
+ private let onComplete: () -> Void
+
+ init(_ handler: LocationDataHandler, onComplete: @escaping () -> Void) {
+ self.handler = handler
+ self.onComplete = onComplete
+ }
+
+ func onLocationUpdate(locationJson: String) {
+ onComplete()
+ handler.onLocationUpdate(locationJson: locationJson)
+ }
+
+ func onLocationError(errorCode: ErrorCodes) {
+ onComplete()
+ handler.onLocationError(errorCode: errorCode)
+ }
+}
diff --git a/third_party/pub_patches/fl_location/ios/Classes/service/LocationDataProvider.swift b/third_party/pub_patches/fl_location/ios/Classes/service/LocationDataProvider.swift
new file mode 100644
index 000000000..375e99e1d
--- /dev/null
+++ b/third_party/pub_patches/fl_location/ios/Classes/service/LocationDataProvider.swift
@@ -0,0 +1,68 @@
+//
+// LocationDataProvider.swift
+// fl_location
+//
+// Created by WOO JIN HWANG on 2021/07/28.
+//
+
+import CoreLocation
+import Foundation
+
+class LocationDataProvider: NSObject, CLLocationManagerDelegate {
+ private let jsonEncoder: JSONEncoder
+ private let locationManager: CLLocationManager
+
+ private var handler: LocationDataHandler? = nil
+ private var settings: LocationSettings? = nil
+
+ override init() {
+ self.jsonEncoder = JSONEncoder()
+ self.locationManager = CLLocationManager()
+ super.init()
+ self.locationManager.delegate = self
+ }
+
+ func startUpdatingLocation(handler: LocationDataHandler, settings: LocationSettings) {
+ if self.handler != nil { stopUpdatingLocation() }
+
+ self.handler = handler
+ self.settings = settings
+
+ self.locationManager.desiredAccuracy = settings.accuracy.toCLLocationAccuracy()
+ self.locationManager.distanceFilter = settings.distanceFilter ?? kCLDistanceFilterNone
+ self.locationManager.allowsBackgroundLocationUpdates = containsBackgroundLocationMode()
+
+ self.locationManager.startUpdatingLocation()
+ }
+
+ func stopUpdatingLocation() {
+ self.locationManager.stopUpdatingLocation()
+
+ self.handler = nil
+ self.settings = nil
+ }
+
+ func containsBackgroundLocationMode() -> Bool {
+ let backgroundModes = Bundle.main.object(forInfoDictionaryKey: "UIBackgroundModes") as? NSArray
+ return backgroundModes?.contains("location") ?? false
+ }
+
+ func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
+ if handler == nil { return }
+
+ do {
+ let locationData = LocationData(from: locations.last!)
+ guard let locationJson = String(data: try jsonEncoder.encode(locationData), encoding: .utf8) else { return }
+ handler?.onLocationUpdate(locationJson: locationJson)
+ } catch {
+ handler?.onLocationError(errorCode: ErrorCodes.LOCATION_DATA_ENCODING_FAILED)
+ }
+ }
+
+ func locationManager(_ manager: CLLocationManager, didFailWithError error: Error) {
+ if handler == nil { return }
+
+ NSLog("LOCATION_UPDATE_FAILED: \(error)")
+ handler?.onLocationError(errorCode: ErrorCodes.LOCATION_UPDATE_FAILED)
+ }
+}
diff --git a/third_party/pub_patches/fl_location/ios/Classes/service/LocationDataProviderManager.swift b/third_party/pub_patches/fl_location/ios/Classes/service/LocationDataProviderManager.swift
new file mode 100644
index 000000000..42d20c098
--- /dev/null
+++ b/third_party/pub_patches/fl_location/ios/Classes/service/LocationDataProviderManager.swift
@@ -0,0 +1,45 @@
+//
+// LocationDataProviderManager.swift
+// fl_location
+//
+// Created by WOO JIN HWANG on 2021/08/06.
+//
+
+import Foundation
+
+class LocationDataProviderManager: NSObject {
+ private var providers = Dictionary()
+
+ private func buildLocationDataProvider() -> LocationDataProvider {
+ return LocationDataProvider()
+ }
+
+ func getLocation(handler: LocationDataHandler, settings: LocationSettings) -> Int {
+ let newLocationDataProvider = buildLocationDataProvider()
+ let hashCode = newLocationDataProvider.hash
+ providers[hashCode] = newLocationDataProvider
+
+ let newHandler = LocationDataHandlerImplForManager(handler) {
+ self.stopUpdatingLocation(hashCode: hashCode)
+ }
+
+ newLocationDataProvider.startUpdatingLocation(handler: newHandler, settings: settings)
+ return hashCode
+ }
+
+ func startUpdatingLocation(handler: LocationDataHandler, settings: LocationSettings) -> Int {
+ let newLocationDataProvider = buildLocationDataProvider()
+ let hashCode = newLocationDataProvider.hash
+ providers[hashCode] = newLocationDataProvider
+
+ newLocationDataProvider.startUpdatingLocation(handler: handler, settings: settings)
+ return hashCode
+ }
+
+ func stopUpdatingLocation(hashCode: Int) {
+ guard let locationDataProvider = providers[hashCode] else { return }
+
+ locationDataProvider.stopUpdatingLocation()
+ providers.removeValue(forKey: hashCode)
+ }
+}
diff --git a/third_party/pub_patches/fl_location/ios/Classes/service/LocationPermissionHandler.swift b/third_party/pub_patches/fl_location/ios/Classes/service/LocationPermissionHandler.swift
new file mode 100644
index 000000000..5d730b8b3
--- /dev/null
+++ b/third_party/pub_patches/fl_location/ios/Classes/service/LocationPermissionHandler.swift
@@ -0,0 +1,13 @@
+//
+// LocationPermissionHandler.swift
+// fl_location
+//
+// Created by WOO JIN HWANG on 2021/07/29.
+//
+
+import Foundation
+
+protocol LocationPermissionHandler {
+ func onPermissionResult(locationPermission: LocationPermission)
+ func onPermissionError(errorCode: ErrorCodes)
+}
diff --git a/third_party/pub_patches/fl_location/ios/Classes/service/LocationPermissionHandlerImpl.swift b/third_party/pub_patches/fl_location/ios/Classes/service/LocationPermissionHandlerImpl.swift
new file mode 100644
index 000000000..4357b7e74
--- /dev/null
+++ b/third_party/pub_patches/fl_location/ios/Classes/service/LocationPermissionHandlerImpl.swift
@@ -0,0 +1,24 @@
+//
+// LocationPermissionHandlerImpl.swift
+// fl_location
+//
+// Created by WOO JIN HWANG on 2021/08/09.
+//
+
+import Foundation
+
+class LocationPermissionHandlerImpl: NSObject, LocationPermissionHandler {
+ private let result: FlutterResult
+
+ init(_ result: @escaping FlutterResult) {
+ self.result = result
+ }
+
+ func onPermissionResult(locationPermission: LocationPermission) {
+ result(locationPermission.rawValue)
+ }
+
+ func onPermissionError(errorCode: ErrorCodes) {
+ ErrorHandleUtils.handleMethodCallError(result: result, errorCode: errorCode)
+ }
+}
diff --git a/third_party/pub_patches/fl_location/ios/Classes/service/LocationPermissionManager.swift b/third_party/pub_patches/fl_location/ios/Classes/service/LocationPermissionManager.swift
new file mode 100644
index 000000000..429d19eb2
--- /dev/null
+++ b/third_party/pub_patches/fl_location/ios/Classes/service/LocationPermissionManager.swift
@@ -0,0 +1,75 @@
+//
+// LocationPermissionManager.swift
+// fl_location
+//
+// Created by WOO JIN HWANG on 2021/07/26.
+//
+
+import CoreLocation
+import Foundation
+
+class LocationPermissionManager: NSObject, CLLocationManagerDelegate {
+ private let locationManager: CLLocationManager
+
+ private var handler: LocationPermissionHandler? = nil
+
+ override init() {
+ self.locationManager = CLLocationManager()
+ super.init()
+ self.locationManager.delegate = self
+ }
+
+ func checkLocationPermission(handler: LocationPermissionHandler) {
+ let status = CLLocationManager.authorizationStatus()
+ let result = LocationPermission.fromAuthorizationStatus(status: status)
+ handler.onPermissionResult(locationPermission: result)
+ }
+
+ func requestLocationPermission(handler: LocationPermissionHandler) {
+ // The app has already requested location permission and is awaiting results.
+ if self.handler != nil { return }
+
+ self.handler = handler
+
+ if containsLocationAlwaysUsageDescription() {
+ locationManager.requestAlwaysAuthorization()
+ } else if containsLocationWhenInUseUsageDescription() {
+ locationManager.requestWhenInUseAuthorization()
+ } else {
+ handler.onPermissionError(errorCode: ErrorCodes.LOCATION_USAGE_DESCRIPTION_NOT_FOUND)
+ disposeResources()
+ }
+ }
+
+ func containsLocationAlwaysUsageDescription() -> Bool {
+ if #available(iOS 11.0, *) {
+ return Bundle.main.object(forInfoDictionaryKey: "NSLocationAlwaysAndWhenInUseUsageDescription") != nil
+ } else {
+ return Bundle.main.object(forInfoDictionaryKey: "NSLocationAlwaysUsageDescription") != nil
+ }
+ }
+
+ func containsLocationWhenInUseUsageDescription() -> Bool {
+ return Bundle.main.object(forInfoDictionaryKey: "NSLocationWhenInUseUsageDescription") != nil
+ }
+
+ private func disposeResources() {
+ self.handler = nil
+ }
+
+ @available(iOS, introduced: 4.2, deprecated: 14.0)
+ func locationManager(_ manager: CLLocationManager, didChangeAuthorization status: CLAuthorizationStatus) {
+ if handler == nil { return }
+
+ checkLocationPermission(handler: handler!)
+ disposeResources()
+ }
+
+ @available(iOS 14.0, *)
+ func locationManagerDidChangeAuthorization(_ manager: CLLocationManager) {
+ if handler == nil { return }
+
+ checkLocationPermission(handler: handler!)
+ disposeResources()
+ }
+}
diff --git a/third_party/pub_patches/fl_location/ios/Classes/service/LocationServicesStatusWatcher.swift b/third_party/pub_patches/fl_location/ios/Classes/service/LocationServicesStatusWatcher.swift
new file mode 100644
index 000000000..0c88e69df
--- /dev/null
+++ b/third_party/pub_patches/fl_location/ios/Classes/service/LocationServicesStatusWatcher.swift
@@ -0,0 +1,58 @@
+//
+// LocationServicesStatusWatcher.swift
+// fl_location
+//
+// Created by WOO JIN HWANG on 2021/07/27.
+//
+
+import CoreLocation
+import Foundation
+
+typealias LocationServicesStatusChangeHandler = (LocationServicesStatus) -> Void
+
+class LocationServicesStatusWatcher: NSObject, CLLocationManagerDelegate {
+ private let locationManager: CLLocationManager
+
+ private var handler: LocationServicesStatusChangeHandler? = nil
+ private var prevLocationServicesStatus: LocationServicesStatus? = nil
+
+ override init() {
+ self.locationManager = CLLocationManager()
+ super.init()
+ self.locationManager.delegate = self
+ }
+
+ func start(handler: @escaping LocationServicesStatusChangeHandler) {
+ if self.handler != nil { stop() }
+
+ self.handler = handler
+ self.prevLocationServicesStatus = LocationServicesUtils.checkLocationServicesStatus()
+ }
+
+ func stop() {
+ self.handler = nil
+ self.prevLocationServicesStatus = nil
+ }
+
+ func checkLocationServicesStatusChange(handler: LocationServicesStatusChangeHandler) {
+ let currLocationServicesStatus = LocationServicesUtils.checkLocationServicesStatus()
+ if currLocationServicesStatus == prevLocationServicesStatus { return }
+
+ handler(currLocationServicesStatus)
+ prevLocationServicesStatus = currLocationServicesStatus
+ }
+
+ @available(iOS, introduced: 4.2, deprecated: 14.0)
+ func locationManager(_ manager: CLLocationManager, didChangeAuthorization status: CLAuthorizationStatus) {
+ if handler == nil { return }
+
+ checkLocationServicesStatusChange(handler: handler!)
+ }
+
+ @available(iOS 14.0, *)
+ func locationManagerDidChangeAuthorization(_ manager: CLLocationManager) {
+ if handler == nil { return }
+
+ checkLocationServicesStatusChange(handler: handler!)
+ }
+}
diff --git a/third_party/pub_patches/fl_location/ios/Classes/service/ServiceProvider.swift b/third_party/pub_patches/fl_location/ios/Classes/service/ServiceProvider.swift
new file mode 100644
index 000000000..3bd7cae5b
--- /dev/null
+++ b/third_party/pub_patches/fl_location/ios/Classes/service/ServiceProvider.swift
@@ -0,0 +1,14 @@
+//
+// ServiceProvider.swift
+// fl_location
+//
+// Created by WOO JIN HWANG on 2021/07/27.
+//
+
+import Foundation
+
+protocol ServiceProvider {
+ func getLocationPermissionManager() -> LocationPermissionManager
+ func getLocationDataProviderManager() -> LocationDataProviderManager
+ func getLocationServicesStatusWatcher() -> LocationServicesStatusWatcher
+}
diff --git a/third_party/pub_patches/fl_location/ios/Classes/utils/ErrorHandleUtils.swift b/third_party/pub_patches/fl_location/ios/Classes/utils/ErrorHandleUtils.swift
new file mode 100644
index 000000000..d59f891e8
--- /dev/null
+++ b/third_party/pub_patches/fl_location/ios/Classes/utils/ErrorHandleUtils.swift
@@ -0,0 +1,21 @@
+//
+// ErrorHandleUtils.swift
+// fl_location
+//
+// Created by WOO JIN HWANG on 2021/07/27.
+//
+
+import Flutter
+import Foundation
+
+class ErrorHandleUtils {
+ static func handleMethodCallError(result: FlutterResult, errorCode: ErrorCodes) {
+ let error = FlutterError(code: errorCode.rawValue, message: errorCode.message(), details: nil)
+ result(error)
+ }
+
+ static func handleStreamError(events: FlutterEventSink, errorCode: ErrorCodes) {
+ let error = FlutterError(code: errorCode.rawValue, message: errorCode.message(), details: nil)
+ events(error)
+ }
+}
diff --git a/third_party/pub_patches/fl_location/ios/Classes/utils/LocationServicesUtils.swift b/third_party/pub_patches/fl_location/ios/Classes/utils/LocationServicesUtils.swift
new file mode 100644
index 000000000..e3f05712e
--- /dev/null
+++ b/third_party/pub_patches/fl_location/ios/Classes/utils/LocationServicesUtils.swift
@@ -0,0 +1,19 @@
+//
+// LocationServicesUtils.swift
+// fl_location
+//
+// Created by WOO JIN HWANG on 2021/07/26.
+//
+
+import CoreLocation
+import Foundation
+
+class LocationServicesUtils {
+ static func checkLocationServicesStatus() -> LocationServicesStatus {
+ if CLLocationManager.locationServicesEnabled() {
+ return LocationServicesStatus.ENABLED
+ } else {
+ return LocationServicesStatus.DISABLED
+ }
+ }
+}
diff --git a/third_party/pub_patches/fl_location/ios/fl_location.podspec b/third_party/pub_patches/fl_location/ios/fl_location.podspec
new file mode 100644
index 000000000..b898e0f7d
--- /dev/null
+++ b/third_party/pub_patches/fl_location/ios/fl_location.podspec
@@ -0,0 +1,23 @@
+#
+# To learn more about a Podspec see http://guides.cocoapods.org/syntax/podspec.html.
+# Run `pod lib lint fl_location.podspec` to validate before publishing.
+#
+Pod::Spec.new do |s|
+ s.name = 'fl_location'
+ s.version = '0.0.1'
+ s.summary = 'A new Flutter project.'
+ s.description = <<-DESC
+A new Flutter project.
+ DESC
+ s.homepage = 'http://example.com'
+ s.license = { :file => '../LICENSE' }
+ s.author = { 'Your Company' => 'email@example.com' }
+ s.source = { :path => '.' }
+ s.source_files = 'Classes/**/*'
+ s.dependency 'Flutter'
+ s.platform = :ios, '8.0'
+
+ # Flutter.framework does not contain a i386 slice.
+ s.pod_target_xcconfig = { 'DEFINES_MODULE' => 'YES', 'EXCLUDED_ARCHS[sdk=iphonesimulator*]' => 'i386' }
+ s.swift_version = '5.0'
+end
diff --git a/third_party/pub_patches/fl_location/lib/fl_location.dart b/third_party/pub_patches/fl_location/lib/fl_location.dart
new file mode 100644
index 000000000..642e7d991
--- /dev/null
+++ b/third_party/pub_patches/fl_location/lib/fl_location.dart
@@ -0,0 +1,39 @@
+import 'package:fl_location_platform_interface/fl_location_platform_interface.dart';
+export 'package:fl_location_platform_interface/fl_location_platform_interface.dart';
+
+class FlLocation {
+ /// Whether location services are enabled.
+ static Future get isLocationServicesEnabled =>
+ FlLocationPlatform.instance.isLocationServicesEnabled();
+
+ /// Check location permission.
+ static Future checkLocationPermission() =>
+ FlLocationPlatform.instance.checkLocationPermission();
+
+ /// Request location permission.
+ static Future requestLocationPermission() =>
+ FlLocationPlatform.instance.requestLocationPermission();
+
+ /// Get the current location.
+ static Future getLocation({
+ LocationAccuracy accuracy = LocationAccuracy.best,
+ Duration? timeLimit,
+ }) =>
+ FlLocationPlatform.instance
+ .getLocation(accuracy: accuracy, timeLimit: timeLimit);
+
+ /// Get the location stream.
+ static Stream getLocationStream({
+ LocationAccuracy accuracy = LocationAccuracy.best,
+ int interval = 5000,
+ double distanceFilter = 0.0,
+ }) =>
+ FlLocationPlatform.instance.getLocationStream(
+ accuracy: accuracy,
+ interval: interval,
+ distanceFilter: distanceFilter);
+
+ /// Get the location services status stream.
+ static Stream getLocationServicesStatusStream() =>
+ FlLocationPlatform.instance.getLocationServicesStatusStream();
+}
diff --git a/third_party/pub_patches/fl_location/pubspec.yaml b/third_party/pub_patches/fl_location/pubspec.yaml
new file mode 100644
index 000000000..b56f59a18
--- /dev/null
+++ b/third_party/pub_patches/fl_location/pubspec.yaml
@@ -0,0 +1,29 @@
+name: fl_location
+description: A plugin that can access the location services of each platform and collect device location data.
+homepage: https://github.com/Dev-hwang/flutter_location/tree/master/fl_location
+version: 2.1.1
+
+environment:
+ sdk: ">=2.17.0 <3.0.0"
+ flutter: ">=3.0.0"
+
+dependencies:
+ flutter:
+ sdk: flutter
+ fl_location_platform_interface: ^2.0.0
+ fl_location_web: ^2.0.0
+
+dev_dependencies:
+ flutter_test:
+ sdk: flutter
+
+flutter:
+ plugin:
+ platforms:
+ android:
+ package: com.pravera.fl_location
+ pluginClass: FlLocationPlugin
+ ios:
+ pluginClass: FlLocationPlugin
+ web:
+ default_package: fl_location_web
diff --git a/third_party/pub_patches/fl_location/test/fl_location_test.dart b/third_party/pub_patches/fl_location/test/fl_location_test.dart
new file mode 100644
index 000000000..153f05566
--- /dev/null
+++ b/third_party/pub_patches/fl_location/test/fl_location_test.dart
@@ -0,0 +1,3 @@
+void main() {
+
+}
diff --git a/third_party/pub_patches/flutter_fgbg/CHANGELOG.md b/third_party/pub_patches/flutter_fgbg/CHANGELOG.md
new file mode 100644
index 000000000..1d5b6d7ab
--- /dev/null
+++ b/third_party/pub_patches/flutter_fgbg/CHANGELOG.md
@@ -0,0 +1,24 @@
+## 0.3.0
+
+* Gradle 8 compatibility. Thanks @Rexios80
+* New `FGBGEvents.ignoreWhile` API
+
+## 0.2.2
+
+* Move to mavenCentral
+
+## 0.2.1
+
+* Fix use of FGBGNotifier and event stream in multiple places
+
+## 0.2.0
+
+* Removed deprecated `registerWith` function to cleanup warnings
+
+## 0.1.0
+
+* Null safety migration
+
+## 0.0.1
+
+* Initial release.
diff --git a/third_party/pub_patches/flutter_fgbg/LICENSE b/third_party/pub_patches/flutter_fgbg/LICENSE
new file mode 100644
index 000000000..6fc5e040b
--- /dev/null
+++ b/third_party/pub_patches/flutter_fgbg/LICENSE
@@ -0,0 +1,21 @@
+MIT License
+
+Copyright (c) 2020 Ajin Asokan
+
+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.
\ No newline at end of file
diff --git a/third_party/pub_patches/flutter_fgbg/README.md b/third_party/pub_patches/flutter_fgbg/README.md
new file mode 100644
index 000000000..ffbc2573e
--- /dev/null
+++ b/third_party/pub_patches/flutter_fgbg/README.md
@@ -0,0 +1,60 @@
+# Flutter Foreground/Background Event Notifier
+
+Flutter plugin to detect when app(not Flutter container) goes to background or foreground.
+
+## Why
+
+Flutter has [WidgetsBindingObserver](https://api.flutter.dev/flutter/widgets/WidgetsBindingObserver-class.html) to get notified when app changes its state from active to inactive states and back. But it actually includes the state changes of the embedding Activity/ViewController as well. So if you have a plugin that opens a new activity/view controller or in iOS if you start a FaceID prompt then WidgetsBindingObserver will report as the app is inactive/resumed.
+
+This plugin on the other hand reports the events only at app level. Since most apps need only background/foreground events this plugin is implemented with just those events. In iOS, plugin reports `didEnterBackgroundNotification` and `willEnterForegroundNotification` notifications and in Android, plugin reports these using `androidx.lifecycle:lifecycle-process` package.
+
+Checkout `example/` project to see the differences in action.
+
+## Getting Started
+
+Add to your pubpsec:
+
+```shell
+flutter pub add flutter_fgbg
+```
+
+Use the built in Widget:
+
+```dart
+FGBGNotifier(
+ onEvent: (event) {
+ print(event); // FGBGType.foreground or FGBGType.background
+ },
+ child: ...,
+)
+```
+
+Or consume the event stream directly:
+
+```dart
+import 'package:flutter_fgbg/flutter_fgbg.dart';
+
+StreamSubscription subscription;
+
+...
+// in initState
+subscription = FGBGEvents.stream.listen((event) {
+ print(event); // FGBGType.foreground or FGBGType.background
+});
+
+// in dispose
+subscription.cancel();
+```
+
+## Caveats
+
+In newer Android versions and `image_picker` library, picking an image will open a different app this will make Flutter app switch to background/foreground and `flutter_fgbg` will report this. At the moment there is no solution to this since the API is working as intented. Tracking the issue [here](https://github.com/ajinasokan/flutter_fgbg/issues/5).
+
+As a work around you can use `FGBGEvents.ignoreWhile` API like this:
+
+```dart
+FGBGEvents.ignoreWhile(() async {
+ await picker.pickImage(source: ImageSource.gallery);
+ // or do something else that can put app to background but don't want to be handled by flutter_fgbg
+});
+```
diff --git a/third_party/pub_patches/flutter_fgbg/android/build.gradle b/third_party/pub_patches/flutter_fgbg/android/build.gradle
new file mode 100644
index 000000000..c6f275157
--- /dev/null
+++ b/third_party/pub_patches/flutter_fgbg/android/build.gradle
@@ -0,0 +1,38 @@
+group 'com.ajinasokan.flutter_fgbg'
+version '1.0'
+
+buildscript {
+ repositories {
+ google()
+ mavenCentral()
+ }
+
+ dependencies {
+ classpath 'com.android.tools.build:gradle:3.5.0'
+ }
+}
+
+rootProject.allprojects {
+ repositories {
+ google()
+ mavenCentral()
+ }
+}
+
+apply plugin: 'com.android.library'
+
+android {
+ compileSdkVersion 36
+
+ defaultConfig {
+ minSdkVersion 16
+ }
+ lintOptions {
+ disable 'InvalidPackage'
+ }
+ namespace "com.ajinasokan.flutter_fgbg"
+}
+
+dependencies {
+ implementation 'androidx.lifecycle:lifecycle-process:2.2.0'
+}
diff --git a/third_party/pub_patches/flutter_fgbg/android/gradle.properties b/third_party/pub_patches/flutter_fgbg/android/gradle.properties
new file mode 100644
index 000000000..38c8d4544
--- /dev/null
+++ b/third_party/pub_patches/flutter_fgbg/android/gradle.properties
@@ -0,0 +1,4 @@
+org.gradle.jvmargs=-Xmx1536M
+android.enableR8=true
+android.useAndroidX=true
+android.enableJetifier=true
diff --git a/third_party/pub_patches/flutter_fgbg/android/gradle/wrapper/gradle-wrapper.properties b/third_party/pub_patches/flutter_fgbg/android/gradle/wrapper/gradle-wrapper.properties
new file mode 100644
index 000000000..01a286e96
--- /dev/null
+++ b/third_party/pub_patches/flutter_fgbg/android/gradle/wrapper/gradle-wrapper.properties
@@ -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-5.6.2-all.zip
diff --git a/third_party/pub_patches/flutter_fgbg/android/settings.gradle b/third_party/pub_patches/flutter_fgbg/android/settings.gradle
new file mode 100644
index 000000000..c8bd3d371
--- /dev/null
+++ b/third_party/pub_patches/flutter_fgbg/android/settings.gradle
@@ -0,0 +1 @@
+rootProject.name = 'flutter_fgbg'
diff --git a/third_party/pub_patches/flutter_fgbg/android/src/main/AndroidManifest.xml b/third_party/pub_patches/flutter_fgbg/android/src/main/AndroidManifest.xml
new file mode 100644
index 000000000..63537af68
--- /dev/null
+++ b/third_party/pub_patches/flutter_fgbg/android/src/main/AndroidManifest.xml
@@ -0,0 +1,3 @@
+
+
diff --git a/third_party/pub_patches/flutter_fgbg/android/src/main/java/com/ajinasokan/flutter_fgbg/FlutterFGBGPlugin.java b/third_party/pub_patches/flutter_fgbg/android/src/main/java/com/ajinasokan/flutter_fgbg/FlutterFGBGPlugin.java
new file mode 100644
index 000000000..a949d7b4f
--- /dev/null
+++ b/third_party/pub_patches/flutter_fgbg/android/src/main/java/com/ajinasokan/flutter_fgbg/FlutterFGBGPlugin.java
@@ -0,0 +1,67 @@
+package com.ajinasokan.flutter_fgbg;
+
+
+import androidx.annotation.NonNull;
+import androidx.lifecycle.Lifecycle;
+import androidx.lifecycle.LifecycleObserver;
+import androidx.lifecycle.OnLifecycleEvent;
+import androidx.lifecycle.ProcessLifecycleOwner;
+
+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.EventChannel;
+
+/** FlutterFGBGPlugin */
+public class FlutterFGBGPlugin implements FlutterPlugin, ActivityAware, LifecycleObserver, EventChannel.StreamHandler {
+ EventChannel.EventSink lifecycleSink;
+
+ @Override
+ public void onListen(Object o, EventChannel.EventSink eventSink) {
+ lifecycleSink = eventSink;
+ }
+
+ @Override
+ public void onCancel(Object o) {
+ lifecycleSink = null;
+ }
+
+ @Override
+ public void onAttachedToEngine(@NonNull FlutterPluginBinding flutterPluginBinding) {
+ new EventChannel(flutterPluginBinding.getBinaryMessenger(), "com.ajinasokan.flutter_fgbg/events")
+ .setStreamHandler(this);
+ }
+
+ @Override
+ public void onDetachedFromEngine(@NonNull FlutterPluginBinding binding) {}
+
+ @Override
+ public void onAttachedToActivity(@NonNull ActivityPluginBinding binding) {
+ ProcessLifecycleOwner.get().getLifecycle().addObserver(this);
+ }
+
+ @OnLifecycleEvent(Lifecycle.Event.ON_STOP)
+ void onAppBackgrounded() {
+ if (lifecycleSink != null) {
+ lifecycleSink.success("background");
+ }
+ }
+
+ @OnLifecycleEvent(Lifecycle.Event.ON_START)
+ void onAppForegrounded() {
+ if (lifecycleSink != null) {
+ lifecycleSink.success("foreground");
+ }
+ }
+
+ @Override
+ public void onDetachedFromActivityForConfigChanges() {}
+
+ @Override
+ public void onReattachedToActivityForConfigChanges(@NonNull ActivityPluginBinding binding) {}
+
+ @Override
+ public void onDetachedFromActivity() {
+ ProcessLifecycleOwner.get().getLifecycle().removeObserver(this);
+ }
+}
diff --git a/third_party/pub_patches/flutter_fgbg/example/README.md b/third_party/pub_patches/flutter_fgbg/example/README.md
new file mode 100644
index 000000000..1e5963b8c
--- /dev/null
+++ b/third_party/pub_patches/flutter_fgbg/example/README.md
@@ -0,0 +1,16 @@
+# flutter_fgbg_example
+
+Demonstrates how to use the flutter_fgbg plugin.
+
+## Getting Started
+
+This project is a starting point for a Flutter application.
+
+A few resources to get you started if this is your first Flutter project:
+
+- [Lab: Write your first Flutter app](https://flutter.dev/docs/get-started/codelab)
+- [Cookbook: Useful Flutter samples](https://flutter.dev/docs/cookbook)
+
+For help getting started with Flutter, view our
+[online documentation](https://flutter.dev/docs), which offers tutorials,
+samples, guidance on mobile development, and a full API reference.
diff --git a/third_party/pub_patches/flutter_fgbg/example/analysis_options.yaml b/third_party/pub_patches/flutter_fgbg/example/analysis_options.yaml
new file mode 100644
index 000000000..61b6c4de1
--- /dev/null
+++ b/third_party/pub_patches/flutter_fgbg/example/analysis_options.yaml
@@ -0,0 +1,29 @@
+# This file configures the analyzer, which statically analyzes Dart code to
+# check for errors, warnings, and lints.
+#
+# The issues identified by the analyzer are surfaced in the UI of Dart-enabled
+# IDEs (https://dart.dev/tools#ides-and-editors). The analyzer can also be
+# invoked from the command line by running `flutter analyze`.
+
+# The following line activates a set of recommended lints for Flutter apps,
+# packages, and plugins designed to encourage good coding practices.
+include: package:flutter_lints/flutter.yaml
+
+linter:
+ # The lint rules applied to this project can be customized in the
+ # section below to disable rules from the `package:flutter_lints/flutter.yaml`
+ # included above or to enable additional rules. A list of all available lints
+ # and their documentation is published at
+ # https://dart-lang.github.io/linter/lints/index.html.
+ #
+ # Instead of disabling a lint rule for the entire project in the
+ # section below, it can also be suppressed for a single line of code
+ # or a specific dart file by using the `// ignore: name_of_lint` and
+ # `// ignore_for_file: name_of_lint` syntax on the line or in the file
+ # producing the lint.
+ rules:
+ # avoid_print: false # Uncomment to disable the `avoid_print` rule
+ # prefer_single_quotes: true # Uncomment to enable the `prefer_single_quotes` rule
+
+# Additional information about this file can be found at
+# https://dart.dev/guides/language/analysis-options
diff --git a/third_party/pub_patches/flutter_fgbg/example/android/app/build.gradle b/third_party/pub_patches/flutter_fgbg/example/android/app/build.gradle
new file mode 100644
index 000000000..d6cf152d1
--- /dev/null
+++ b/third_party/pub_patches/flutter_fgbg/example/android/app/build.gradle
@@ -0,0 +1,56 @@
+def localProperties = new Properties()
+def localPropertiesFile = rootProject.file('local.properties')
+if (localPropertiesFile.exists()) {
+ localPropertiesFile.withReader('UTF-8') { reader ->
+ localProperties.load(reader)
+ }
+}
+
+def flutterRoot = localProperties.getProperty('flutter.sdk')
+if (flutterRoot == null) {
+ throw new GradleException("Flutter SDK not found. Define location with flutter.sdk in the local.properties file.")
+}
+
+def flutterVersionCode = localProperties.getProperty('flutter.versionCode')
+if (flutterVersionCode == null) {
+ flutterVersionCode = '1'
+}
+
+def flutterVersionName = localProperties.getProperty('flutter.versionName')
+if (flutterVersionName == null) {
+ flutterVersionName = '1.0'
+}
+
+apply plugin: 'com.android.application'
+apply plugin: 'kotlin-android'
+apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle"
+
+android {
+ compileSdkVersion flutter.compileSdkVersion
+
+ compileOptions {
+ sourceCompatibility JavaVersion.VERSION_1_8
+ targetCompatibility JavaVersion.VERSION_1_8
+ }
+
+ defaultConfig {
+ // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html).
+ applicationId "com.ajinasokan.example"
+ minSdkVersion flutter.minSdkVersion
+ targetSdkVersion flutter.targetSdkVersion
+ versionCode flutterVersionCode.toInteger()
+ versionName flutterVersionName
+ }
+
+ buildTypes {
+ release {
+ // TODO: Add your own signing config for the release build.
+ // Signing with the debug keys for now, so `flutter run --release` works.
+ signingConfig signingConfigs.debug
+ }
+ }
+}
+
+flutter {
+ source '../..'
+}
diff --git a/third_party/pub_patches/flutter_fgbg/example/android/app/src/debug/AndroidManifest.xml b/third_party/pub_patches/flutter_fgbg/example/android/app/src/debug/AndroidManifest.xml
new file mode 100644
index 000000000..bce4beeb8
--- /dev/null
+++ b/third_party/pub_patches/flutter_fgbg/example/android/app/src/debug/AndroidManifest.xml
@@ -0,0 +1,7 @@
+
+
+
+
diff --git a/third_party/pub_patches/flutter_fgbg/example/android/app/src/main/AndroidManifest.xml b/third_party/pub_patches/flutter_fgbg/example/android/app/src/main/AndroidManifest.xml
new file mode 100644
index 000000000..d8b053a2c
--- /dev/null
+++ b/third_party/pub_patches/flutter_fgbg/example/android/app/src/main/AndroidManifest.xml
@@ -0,0 +1,44 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/third_party/pub_patches/flutter_fgbg/example/android/app/src/main/java/com/ajinasokan/example/MainActivity.java b/third_party/pub_patches/flutter_fgbg/example/android/app/src/main/java/com/ajinasokan/example/MainActivity.java
new file mode 100644
index 000000000..eee26c7d7
--- /dev/null
+++ b/third_party/pub_patches/flutter_fgbg/example/android/app/src/main/java/com/ajinasokan/example/MainActivity.java
@@ -0,0 +1,7 @@
+package com.ajinasokan.example;
+
+import io.flutter.embedding.android.FlutterActivity;
+import io.flutter.embedding.android.FlutterFragmentActivity;
+
+public class MainActivity extends FlutterFragmentActivity {
+}
diff --git a/third_party/pub_patches/flutter_fgbg/example/android/app/src/main/res/drawable-v21/launch_background.xml b/third_party/pub_patches/flutter_fgbg/example/android/app/src/main/res/drawable-v21/launch_background.xml
new file mode 100644
index 000000000..f74085f3f
--- /dev/null
+++ b/third_party/pub_patches/flutter_fgbg/example/android/app/src/main/res/drawable-v21/launch_background.xml
@@ -0,0 +1,12 @@
+
+
+
+
+
+
+
+
diff --git a/third_party/pub_patches/flutter_fgbg/example/android/app/src/main/res/drawable/launch_background.xml b/third_party/pub_patches/flutter_fgbg/example/android/app/src/main/res/drawable/launch_background.xml
new file mode 100644
index 000000000..304732f88
--- /dev/null
+++ b/third_party/pub_patches/flutter_fgbg/example/android/app/src/main/res/drawable/launch_background.xml
@@ -0,0 +1,12 @@
+
+
+
+
+
+
+
+
diff --git a/third_party/pub_patches/flutter_fgbg/example/android/app/src/main/res/mipmap-hdpi/ic_launcher.png b/third_party/pub_patches/flutter_fgbg/example/android/app/src/main/res/mipmap-hdpi/ic_launcher.png
new file mode 100644
index 000000000..db77bb4b7
Binary files /dev/null and b/third_party/pub_patches/flutter_fgbg/example/android/app/src/main/res/mipmap-hdpi/ic_launcher.png differ
diff --git a/third_party/pub_patches/flutter_fgbg/example/android/app/src/main/res/mipmap-mdpi/ic_launcher.png b/third_party/pub_patches/flutter_fgbg/example/android/app/src/main/res/mipmap-mdpi/ic_launcher.png
new file mode 100644
index 000000000..17987b79b
Binary files /dev/null and b/third_party/pub_patches/flutter_fgbg/example/android/app/src/main/res/mipmap-mdpi/ic_launcher.png differ
diff --git a/third_party/pub_patches/flutter_fgbg/example/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png b/third_party/pub_patches/flutter_fgbg/example/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png
new file mode 100644
index 000000000..09d439148
Binary files /dev/null and b/third_party/pub_patches/flutter_fgbg/example/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png differ
diff --git a/third_party/pub_patches/flutter_fgbg/example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png b/third_party/pub_patches/flutter_fgbg/example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
new file mode 100644
index 000000000..d5f1c8d34
Binary files /dev/null and b/third_party/pub_patches/flutter_fgbg/example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png differ
diff --git a/third_party/pub_patches/flutter_fgbg/example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/third_party/pub_patches/flutter_fgbg/example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
new file mode 100644
index 000000000..4d6372eeb
Binary files /dev/null and b/third_party/pub_patches/flutter_fgbg/example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png differ
diff --git a/third_party/pub_patches/flutter_fgbg/example/android/app/src/main/res/values-night/styles.xml b/third_party/pub_patches/flutter_fgbg/example/android/app/src/main/res/values-night/styles.xml
new file mode 100644
index 000000000..3db14bb53
--- /dev/null
+++ b/third_party/pub_patches/flutter_fgbg/example/android/app/src/main/res/values-night/styles.xml
@@ -0,0 +1,18 @@
+
+
+
+
+
+
+
diff --git a/third_party/pub_patches/flutter_fgbg/example/android/app/src/main/res/values/styles.xml b/third_party/pub_patches/flutter_fgbg/example/android/app/src/main/res/values/styles.xml
new file mode 100644
index 000000000..d460d1e92
--- /dev/null
+++ b/third_party/pub_patches/flutter_fgbg/example/android/app/src/main/res/values/styles.xml
@@ -0,0 +1,18 @@
+
+
+
+
+
+
+
diff --git a/third_party/pub_patches/flutter_fgbg/example/android/app/src/profile/AndroidManifest.xml b/third_party/pub_patches/flutter_fgbg/example/android/app/src/profile/AndroidManifest.xml
new file mode 100644
index 000000000..bce4beeb8
--- /dev/null
+++ b/third_party/pub_patches/flutter_fgbg/example/android/app/src/profile/AndroidManifest.xml
@@ -0,0 +1,7 @@
+
+
+
+
diff --git a/third_party/pub_patches/flutter_fgbg/example/android/build.gradle b/third_party/pub_patches/flutter_fgbg/example/android/build.gradle
new file mode 100644
index 000000000..4256f9173
--- /dev/null
+++ b/third_party/pub_patches/flutter_fgbg/example/android/build.gradle
@@ -0,0 +1,31 @@
+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"
+ }
+}
+
+allprojects {
+ repositories {
+ google()
+ mavenCentral()
+ }
+}
+
+rootProject.buildDir = '../build'
+subprojects {
+ project.buildDir = "${rootProject.buildDir}/${project.name}"
+}
+subprojects {
+ project.evaluationDependsOn(':app')
+}
+
+task clean(type: Delete) {
+ delete rootProject.buildDir
+}
diff --git a/third_party/pub_patches/flutter_fgbg/example/android/gradle.properties b/third_party/pub_patches/flutter_fgbg/example/android/gradle.properties
new file mode 100644
index 000000000..94adc3a3f
--- /dev/null
+++ b/third_party/pub_patches/flutter_fgbg/example/android/gradle.properties
@@ -0,0 +1,3 @@
+org.gradle.jvmargs=-Xmx1536M
+android.useAndroidX=true
+android.enableJetifier=true
diff --git a/third_party/pub_patches/flutter_fgbg/example/android/gradle/wrapper/gradle-wrapper.properties b/third_party/pub_patches/flutter_fgbg/example/android/gradle/wrapper/gradle-wrapper.properties
new file mode 100644
index 000000000..bc6a58afd
--- /dev/null
+++ b/third_party/pub_patches/flutter_fgbg/example/android/gradle/wrapper/gradle-wrapper.properties
@@ -0,0 +1,6 @@
+#Fri Jun 23 08:50:38 CEST 2017
+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
diff --git a/third_party/pub_patches/flutter_fgbg/example/android/settings.gradle b/third_party/pub_patches/flutter_fgbg/example/android/settings.gradle
new file mode 100644
index 000000000..44e62bcf0
--- /dev/null
+++ b/third_party/pub_patches/flutter_fgbg/example/android/settings.gradle
@@ -0,0 +1,11 @@
+include ':app'
+
+def localPropertiesFile = new File(rootProject.projectDir, "local.properties")
+def properties = new Properties()
+
+assert localPropertiesFile.exists()
+localPropertiesFile.withReader("UTF-8") { reader -> properties.load(reader) }
+
+def flutterSdkPath = properties.getProperty("flutter.sdk")
+assert flutterSdkPath != null, "flutter.sdk not set in local.properties"
+apply from: "$flutterSdkPath/packages/flutter_tools/gradle/app_plugin_loader.gradle"
diff --git a/third_party/pub_patches/flutter_fgbg/example/ios/Flutter/AppFrameworkInfo.plist b/third_party/pub_patches/flutter_fgbg/example/ios/Flutter/AppFrameworkInfo.plist
new file mode 100644
index 000000000..f2872cf47
--- /dev/null
+++ b/third_party/pub_patches/flutter_fgbg/example/ios/Flutter/AppFrameworkInfo.plist
@@ -0,0 +1,26 @@
+
+
+
+
+ CFBundleDevelopmentRegion
+ $(DEVELOPMENT_LANGUAGE)
+ CFBundleExecutable
+ App
+ CFBundleIdentifier
+ io.flutter.flutter.app
+ CFBundleInfoDictionaryVersion
+ 6.0
+ CFBundleName
+ App
+ CFBundlePackageType
+ FMWK
+ CFBundleShortVersionString
+ 1.0
+ CFBundleSignature
+ ????
+ CFBundleVersion
+ 1.0
+ MinimumOSVersion
+ 9.0
+
+
diff --git a/third_party/pub_patches/flutter_fgbg/example/ios/Flutter/Debug.xcconfig b/third_party/pub_patches/flutter_fgbg/example/ios/Flutter/Debug.xcconfig
new file mode 100644
index 000000000..e8efba114
--- /dev/null
+++ b/third_party/pub_patches/flutter_fgbg/example/ios/Flutter/Debug.xcconfig
@@ -0,0 +1,2 @@
+#include "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"
+#include "Generated.xcconfig"
diff --git a/third_party/pub_patches/flutter_fgbg/example/ios/Flutter/Release.xcconfig b/third_party/pub_patches/flutter_fgbg/example/ios/Flutter/Release.xcconfig
new file mode 100644
index 000000000..399e9340e
--- /dev/null
+++ b/third_party/pub_patches/flutter_fgbg/example/ios/Flutter/Release.xcconfig
@@ -0,0 +1,2 @@
+#include "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"
+#include "Generated.xcconfig"
diff --git a/third_party/pub_patches/flutter_fgbg/example/ios/Podfile b/third_party/pub_patches/flutter_fgbg/example/ios/Podfile
new file mode 100644
index 000000000..1e8c3c90a
--- /dev/null
+++ b/third_party/pub_patches/flutter_fgbg/example/ios/Podfile
@@ -0,0 +1,41 @@
+# Uncomment this line to define a global platform for your project
+# platform :ios, '9.0'
+
+# CocoaPods analytics sends network stats synchronously affecting flutter build latency.
+ENV['COCOAPODS_DISABLE_STATS'] = 'true'
+
+project 'Runner', {
+ 'Debug' => :debug,
+ 'Profile' => :release,
+ 'Release' => :release,
+}
+
+def flutter_root
+ generated_xcode_build_settings_path = File.expand_path(File.join('..', 'Flutter', 'Generated.xcconfig'), __FILE__)
+ unless File.exist?(generated_xcode_build_settings_path)
+ raise "#{generated_xcode_build_settings_path} must exist. If you're running pod install manually, make sure flutter pub get is executed first"
+ end
+
+ File.foreach(generated_xcode_build_settings_path) do |line|
+ matches = line.match(/FLUTTER_ROOT\=(.*)/)
+ return matches[1].strip if matches
+ end
+ raise "FLUTTER_ROOT not found in #{generated_xcode_build_settings_path}. Try deleting Generated.xcconfig, then run flutter pub get"
+end
+
+require File.expand_path(File.join('packages', 'flutter_tools', 'bin', 'podhelper'), flutter_root)
+
+flutter_ios_podfile_setup
+
+target 'Runner' do
+ use_frameworks!
+ use_modular_headers!
+
+ flutter_install_all_ios_pods File.dirname(File.realpath(__FILE__))
+end
+
+post_install do |installer|
+ installer.pods_project.targets.each do |target|
+ flutter_additional_ios_build_settings(target)
+ end
+end
diff --git a/third_party/pub_patches/flutter_fgbg/example/ios/Podfile.lock b/third_party/pub_patches/flutter_fgbg/example/ios/Podfile.lock
new file mode 100644
index 000000000..635bf7d3e
--- /dev/null
+++ b/third_party/pub_patches/flutter_fgbg/example/ios/Podfile.lock
@@ -0,0 +1,34 @@
+PODS:
+ - Flutter (1.0.0)
+ - flutter_fgbg (0.0.1):
+ - Flutter
+ - image_picker (0.0.1):
+ - Flutter
+ - local_auth (0.0.1):
+ - Flutter
+
+DEPENDENCIES:
+ - Flutter (from `Flutter`)
+ - flutter_fgbg (from `.symlinks/plugins/flutter_fgbg/ios`)
+ - image_picker (from `.symlinks/plugins/image_picker/ios`)
+ - local_auth (from `.symlinks/plugins/local_auth/ios`)
+
+EXTERNAL SOURCES:
+ Flutter:
+ :path: Flutter
+ flutter_fgbg:
+ :path: ".symlinks/plugins/flutter_fgbg/ios"
+ image_picker:
+ :path: ".symlinks/plugins/image_picker/ios"
+ local_auth:
+ :path: ".symlinks/plugins/local_auth/ios"
+
+SPEC CHECKSUMS:
+ Flutter: 50d75fe2f02b26cc09d224853bb45737f8b3214a
+ flutter_fgbg: 31c0d1140a131daea2d342121808f6aa0dcd879d
+ image_picker: 50e7c7ff960e5f58faa4d1f4af84a771c671bc4a
+ local_auth: ef62030a2731330b95df7ef1331bd15f6a64b8a6
+
+PODFILE CHECKSUM: aafe91acc616949ddb318b77800a7f51bffa2a4c
+
+COCOAPODS: 1.10.1
diff --git a/third_party/pub_patches/flutter_fgbg/example/ios/Runner.xcodeproj/project.pbxproj b/third_party/pub_patches/flutter_fgbg/example/ios/Runner.xcodeproj/project.pbxproj
new file mode 100644
index 000000000..a9141eff7
--- /dev/null
+++ b/third_party/pub_patches/flutter_fgbg/example/ios/Runner.xcodeproj/project.pbxproj
@@ -0,0 +1,583 @@
+// !$*UTF8*$!
+{
+ archiveVersion = 1;
+ classes = {
+ };
+ objectVersion = 51;
+ objects = {
+
+/* Begin PBXBuildFile section */
+ 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; };
+ 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; };
+ 74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74858FAE1ED2DC5600515810 /* AppDelegate.swift */; };
+ 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; };
+ 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; };
+ 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; };
+ 9A00D670E6F367FE1519AC62 /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = B941493834C965DD20A6A502 /* Pods_Runner.framework */; };
+/* End PBXBuildFile section */
+
+/* Begin PBXCopyFilesBuildPhase section */
+ 9705A1C41CF9048500538489 /* Embed Frameworks */ = {
+ isa = PBXCopyFilesBuildPhase;
+ buildActionMask = 2147483647;
+ dstPath = "";
+ dstSubfolderSpec = 10;
+ files = (
+ );
+ name = "Embed Frameworks";
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+/* End PBXCopyFilesBuildPhase section */
+
+/* Begin PBXFileReference section */
+ 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GeneratedPluginRegistrant.h; sourceTree = ""; };
+ 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = ""; };
+ 25D318A4458E7A20BDDED965 /* Pods-Runner.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.profile.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.profile.xcconfig"; sourceTree = ""; };
+ 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = ""; };
+ 74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Runner-Bridging-Header.h"; sourceTree = ""; };
+ 74858FAE1ED2DC5600515810 /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; };
+ 7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = ""; };
+ 9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Debug.xcconfig; path = Flutter/Debug.xcconfig; sourceTree = ""; };
+ 9740EEB31CF90195004384FC /* Generated.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Generated.xcconfig; path = Flutter/Generated.xcconfig; sourceTree = ""; };
+ 97C146EE1CF9000F007C117D /* Runner.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Runner.app; sourceTree = BUILT_PRODUCTS_DIR; };
+ 97C146FB1CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; };
+ 97C146FD1CF9000F007C117D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; };
+ 97C147001CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; };
+ 97C147021CF9000F007C117D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; };
+ A961349DD7066D1AE7183DEB /* Pods-Runner.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.release.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"; sourceTree = ""; };
+ B941493834C965DD20A6A502 /* Pods_Runner.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Runner.framework; sourceTree = BUILT_PRODUCTS_DIR; };
+ F809CCBF8D4010BAD89EDC37 /* Pods-Runner.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.debug.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"; sourceTree = ""; };
+/* End PBXFileReference section */
+
+/* Begin PBXFrameworksBuildPhase section */
+ 97C146EB1CF9000F007C117D /* Frameworks */ = {
+ isa = PBXFrameworksBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ 9A00D670E6F367FE1519AC62 /* Pods_Runner.framework in Frameworks */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+/* End PBXFrameworksBuildPhase section */
+
+/* Begin PBXGroup section */
+ 1D990EAF64B7A96239E9C78E /* Frameworks */ = {
+ isa = PBXGroup;
+ children = (
+ B941493834C965DD20A6A502 /* Pods_Runner.framework */,
+ );
+ name = Frameworks;
+ sourceTree = "";
+ };
+ 9740EEB11CF90186004384FC /* Flutter */ = {
+ isa = PBXGroup;
+ children = (
+ 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */,
+ 9740EEB21CF90195004384FC /* Debug.xcconfig */,
+ 7AFA3C8E1D35360C0083082E /* Release.xcconfig */,
+ 9740EEB31CF90195004384FC /* Generated.xcconfig */,
+ );
+ name = Flutter;
+ sourceTree = "";
+ };
+ 97C146E51CF9000F007C117D = {
+ isa = PBXGroup;
+ children = (
+ 9740EEB11CF90186004384FC /* Flutter */,
+ 97C146F01CF9000F007C117D /* Runner */,
+ 97C146EF1CF9000F007C117D /* Products */,
+ B96F73C090901BB9DB50CC54 /* Pods */,
+ 1D990EAF64B7A96239E9C78E /* Frameworks */,
+ );
+ sourceTree = "";
+ };
+ 97C146EF1CF9000F007C117D /* Products */ = {
+ isa = PBXGroup;
+ children = (
+ 97C146EE1CF9000F007C117D /* Runner.app */,
+ );
+ name = Products;
+ sourceTree = "";
+ };
+ 97C146F01CF9000F007C117D /* Runner */ = {
+ isa = PBXGroup;
+ children = (
+ 97C146FA1CF9000F007C117D /* Main.storyboard */,
+ 97C146FD1CF9000F007C117D /* Assets.xcassets */,
+ 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */,
+ 97C147021CF9000F007C117D /* Info.plist */,
+ 97C146F11CF9000F007C117D /* Supporting Files */,
+ 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */,
+ 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */,
+ 74858FAE1ED2DC5600515810 /* AppDelegate.swift */,
+ 74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */,
+ );
+ path = Runner;
+ sourceTree = "";
+ };
+ 97C146F11CF9000F007C117D /* Supporting Files */ = {
+ isa = PBXGroup;
+ children = (
+ );
+ name = "Supporting Files";
+ sourceTree = "";
+ };
+ B96F73C090901BB9DB50CC54 /* Pods */ = {
+ isa = PBXGroup;
+ children = (
+ F809CCBF8D4010BAD89EDC37 /* Pods-Runner.debug.xcconfig */,
+ A961349DD7066D1AE7183DEB /* Pods-Runner.release.xcconfig */,
+ 25D318A4458E7A20BDDED965 /* Pods-Runner.profile.xcconfig */,
+ );
+ path = Pods;
+ sourceTree = "";
+ };
+/* End PBXGroup section */
+
+/* Begin PBXNativeTarget section */
+ 97C146ED1CF9000F007C117D /* Runner */ = {
+ isa = PBXNativeTarget;
+ buildConfigurationList = 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */;
+ buildPhases = (
+ 0DC4B0A3A8974CC2F1BCEDBF /* [CP] Check Pods Manifest.lock */,
+ 9740EEB61CF901F6004384FC /* Run Script */,
+ 97C146EA1CF9000F007C117D /* Sources */,
+ 97C146EB1CF9000F007C117D /* Frameworks */,
+ 97C146EC1CF9000F007C117D /* Resources */,
+ 9705A1C41CF9048500538489 /* Embed Frameworks */,
+ 3B06AD1E1E4923F5004D2608 /* Thin Binary */,
+ D819938DB2643FC53019EC56 /* [CP] Embed Pods Frameworks */,
+ );
+ buildRules = (
+ );
+ dependencies = (
+ );
+ name = Runner;
+ productName = Runner;
+ productReference = 97C146EE1CF9000F007C117D /* Runner.app */;
+ productType = "com.apple.product-type.application";
+ };
+/* End PBXNativeTarget section */
+
+/* Begin PBXProject section */
+ 97C146E61CF9000F007C117D /* Project object */ = {
+ isa = PBXProject;
+ attributes = {
+ LastUpgradeCheck = 1020;
+ ORGANIZATIONNAME = "";
+ TargetAttributes = {
+ 97C146ED1CF9000F007C117D = {
+ CreatedOnToolsVersion = 7.3.1;
+ LastSwiftMigration = 1100;
+ };
+ };
+ };
+ buildConfigurationList = 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */;
+ compatibilityVersion = "Xcode 9.3";
+ developmentRegion = en;
+ hasScannedForEncodings = 0;
+ knownRegions = (
+ en,
+ Base,
+ );
+ mainGroup = 97C146E51CF9000F007C117D;
+ productRefGroup = 97C146EF1CF9000F007C117D /* Products */;
+ projectDirPath = "";
+ projectRoot = "";
+ targets = (
+ 97C146ED1CF9000F007C117D /* Runner */,
+ );
+ };
+/* End PBXProject section */
+
+/* Begin PBXResourcesBuildPhase section */
+ 97C146EC1CF9000F007C117D /* Resources */ = {
+ isa = PBXResourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */,
+ 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */,
+ 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */,
+ 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+/* End PBXResourcesBuildPhase section */
+
+/* Begin PBXShellScriptBuildPhase section */
+ 0DC4B0A3A8974CC2F1BCEDBF /* [CP] Check Pods Manifest.lock */ = {
+ isa = PBXShellScriptBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ );
+ inputFileListPaths = (
+ );
+ inputPaths = (
+ "${PODS_PODFILE_DIR_PATH}/Podfile.lock",
+ "${PODS_ROOT}/Manifest.lock",
+ );
+ name = "[CP] Check Pods Manifest.lock";
+ outputFileListPaths = (
+ );
+ outputPaths = (
+ "$(DERIVED_FILE_DIR)/Pods-Runner-checkManifestLockResult.txt",
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ shellPath = /bin/sh;
+ shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n";
+ showEnvVarsInLog = 0;
+ };
+ 3B06AD1E1E4923F5004D2608 /* Thin Binary */ = {
+ isa = PBXShellScriptBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ );
+ inputPaths = (
+ );
+ name = "Thin Binary";
+ outputPaths = (
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ shellPath = /bin/sh;
+ shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" embed_and_thin";
+ };
+ 9740EEB61CF901F6004384FC /* Run Script */ = {
+ isa = PBXShellScriptBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ );
+ inputPaths = (
+ );
+ name = "Run Script";
+ outputPaths = (
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ shellPath = /bin/sh;
+ shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" build";
+ };
+ D819938DB2643FC53019EC56 /* [CP] Embed Pods Frameworks */ = {
+ isa = PBXShellScriptBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ );
+ inputFileListPaths = (
+ "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-input-files.xcfilelist",
+ );
+ name = "[CP] Embed Pods Frameworks";
+ outputFileListPaths = (
+ "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-output-files.xcfilelist",
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ shellPath = /bin/sh;
+ shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh\"\n";
+ showEnvVarsInLog = 0;
+ };
+/* End PBXShellScriptBuildPhase section */
+
+/* Begin PBXSourcesBuildPhase section */
+ 97C146EA1CF9000F007C117D /* Sources */ = {
+ isa = PBXSourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ 74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */,
+ 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+/* End PBXSourcesBuildPhase section */
+
+/* Begin PBXVariantGroup section */
+ 97C146FA1CF9000F007C117D /* Main.storyboard */ = {
+ isa = PBXVariantGroup;
+ children = (
+ 97C146FB1CF9000F007C117D /* Base */,
+ );
+ name = Main.storyboard;
+ sourceTree = "";
+ };
+ 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */ = {
+ isa = PBXVariantGroup;
+ children = (
+ 97C147001CF9000F007C117D /* Base */,
+ );
+ name = LaunchScreen.storyboard;
+ sourceTree = "";
+ };
+/* End PBXVariantGroup section */
+
+/* Begin XCBuildConfiguration section */
+ 249021D3217E4FDB00AE95B9 /* Profile */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ ALWAYS_SEARCH_USER_PATHS = NO;
+ CLANG_ANALYZER_NONNULL = YES;
+ CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
+ CLANG_CXX_LIBRARY = "libc++";
+ CLANG_ENABLE_MODULES = YES;
+ CLANG_ENABLE_OBJC_ARC = YES;
+ CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
+ CLANG_WARN_BOOL_CONVERSION = YES;
+ CLANG_WARN_COMMA = YES;
+ CLANG_WARN_CONSTANT_CONVERSION = YES;
+ CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
+ CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
+ CLANG_WARN_EMPTY_BODY = YES;
+ CLANG_WARN_ENUM_CONVERSION = YES;
+ CLANG_WARN_INFINITE_RECURSION = YES;
+ CLANG_WARN_INT_CONVERSION = YES;
+ CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
+ CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
+ CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
+ CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
+ CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
+ CLANG_WARN_STRICT_PROTOTYPES = YES;
+ CLANG_WARN_SUSPICIOUS_MOVE = YES;
+ CLANG_WARN_UNREACHABLE_CODE = YES;
+ CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
+ "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
+ COPY_PHASE_STRIP = NO;
+ DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
+ ENABLE_NS_ASSERTIONS = NO;
+ ENABLE_STRICT_OBJC_MSGSEND = YES;
+ GCC_C_LANGUAGE_STANDARD = gnu99;
+ GCC_NO_COMMON_BLOCKS = YES;
+ GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
+ GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
+ GCC_WARN_UNDECLARED_SELECTOR = YES;
+ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
+ GCC_WARN_UNUSED_FUNCTION = YES;
+ GCC_WARN_UNUSED_VARIABLE = YES;
+ IPHONEOS_DEPLOYMENT_TARGET = 9.0;
+ MTL_ENABLE_DEBUG_INFO = NO;
+ SDKROOT = iphoneos;
+ SUPPORTED_PLATFORMS = iphoneos;
+ TARGETED_DEVICE_FAMILY = "1,2";
+ VALIDATE_PRODUCT = YES;
+ };
+ name = Profile;
+ };
+ 249021D4217E4FDB00AE95B9 /* Profile */ = {
+ isa = XCBuildConfiguration;
+ baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */;
+ buildSettings = {
+ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
+ CLANG_ENABLE_MODULES = YES;
+ CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
+ DEVELOPMENT_TEAM = 3634FUSGED;
+ ENABLE_BITCODE = NO;
+ FRAMEWORK_SEARCH_PATHS = (
+ "$(inherited)",
+ "$(PROJECT_DIR)/Flutter",
+ );
+ INFOPLIST_FILE = Runner/Info.plist;
+ LD_RUNPATH_SEARCH_PATHS = (
+ "$(inherited)",
+ "@executable_path/Frameworks",
+ );
+ LIBRARY_SEARCH_PATHS = (
+ "$(inherited)",
+ "$(PROJECT_DIR)/Flutter",
+ );
+ PRODUCT_BUNDLE_IDENTIFIER = com.ajinasokan.flutterFgbgExample;
+ PRODUCT_NAME = "$(TARGET_NAME)";
+ SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
+ SWIFT_VERSION = 5.0;
+ VERSIONING_SYSTEM = "apple-generic";
+ };
+ name = Profile;
+ };
+ 97C147031CF9000F007C117D /* Debug */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ ALWAYS_SEARCH_USER_PATHS = NO;
+ CLANG_ANALYZER_NONNULL = YES;
+ CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
+ CLANG_CXX_LIBRARY = "libc++";
+ CLANG_ENABLE_MODULES = YES;
+ CLANG_ENABLE_OBJC_ARC = YES;
+ CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
+ CLANG_WARN_BOOL_CONVERSION = YES;
+ CLANG_WARN_COMMA = YES;
+ CLANG_WARN_CONSTANT_CONVERSION = YES;
+ CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
+ CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
+ CLANG_WARN_EMPTY_BODY = YES;
+ CLANG_WARN_ENUM_CONVERSION = YES;
+ CLANG_WARN_INFINITE_RECURSION = YES;
+ CLANG_WARN_INT_CONVERSION = YES;
+ CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
+ CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
+ CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
+ CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
+ CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
+ CLANG_WARN_STRICT_PROTOTYPES = YES;
+ CLANG_WARN_SUSPICIOUS_MOVE = YES;
+ CLANG_WARN_UNREACHABLE_CODE = YES;
+ CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
+ "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
+ COPY_PHASE_STRIP = NO;
+ DEBUG_INFORMATION_FORMAT = dwarf;
+ ENABLE_STRICT_OBJC_MSGSEND = YES;
+ ENABLE_TESTABILITY = YES;
+ GCC_C_LANGUAGE_STANDARD = gnu99;
+ GCC_DYNAMIC_NO_PIC = NO;
+ GCC_NO_COMMON_BLOCKS = YES;
+ GCC_OPTIMIZATION_LEVEL = 0;
+ GCC_PREPROCESSOR_DEFINITIONS = (
+ "DEBUG=1",
+ "$(inherited)",
+ );
+ GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
+ GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
+ GCC_WARN_UNDECLARED_SELECTOR = YES;
+ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
+ GCC_WARN_UNUSED_FUNCTION = YES;
+ GCC_WARN_UNUSED_VARIABLE = YES;
+ IPHONEOS_DEPLOYMENT_TARGET = 9.0;
+ MTL_ENABLE_DEBUG_INFO = YES;
+ ONLY_ACTIVE_ARCH = YES;
+ SDKROOT = iphoneos;
+ TARGETED_DEVICE_FAMILY = "1,2";
+ };
+ name = Debug;
+ };
+ 97C147041CF9000F007C117D /* Release */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ ALWAYS_SEARCH_USER_PATHS = NO;
+ CLANG_ANALYZER_NONNULL = YES;
+ CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
+ CLANG_CXX_LIBRARY = "libc++";
+ CLANG_ENABLE_MODULES = YES;
+ CLANG_ENABLE_OBJC_ARC = YES;
+ CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
+ CLANG_WARN_BOOL_CONVERSION = YES;
+ CLANG_WARN_COMMA = YES;
+ CLANG_WARN_CONSTANT_CONVERSION = YES;
+ CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
+ CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
+ CLANG_WARN_EMPTY_BODY = YES;
+ CLANG_WARN_ENUM_CONVERSION = YES;
+ CLANG_WARN_INFINITE_RECURSION = YES;
+ CLANG_WARN_INT_CONVERSION = YES;
+ CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
+ CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
+ CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
+ CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
+ CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
+ CLANG_WARN_STRICT_PROTOTYPES = YES;
+ CLANG_WARN_SUSPICIOUS_MOVE = YES;
+ CLANG_WARN_UNREACHABLE_CODE = YES;
+ CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
+ "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
+ COPY_PHASE_STRIP = NO;
+ DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
+ ENABLE_NS_ASSERTIONS = NO;
+ ENABLE_STRICT_OBJC_MSGSEND = YES;
+ GCC_C_LANGUAGE_STANDARD = gnu99;
+ GCC_NO_COMMON_BLOCKS = YES;
+ GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
+ GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
+ GCC_WARN_UNDECLARED_SELECTOR = YES;
+ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
+ GCC_WARN_UNUSED_FUNCTION = YES;
+ GCC_WARN_UNUSED_VARIABLE = YES;
+ IPHONEOS_DEPLOYMENT_TARGET = 9.0;
+ MTL_ENABLE_DEBUG_INFO = NO;
+ SDKROOT = iphoneos;
+ SUPPORTED_PLATFORMS = iphoneos;
+ SWIFT_COMPILATION_MODE = wholemodule;
+ SWIFT_OPTIMIZATION_LEVEL = "-O";
+ TARGETED_DEVICE_FAMILY = "1,2";
+ VALIDATE_PRODUCT = YES;
+ };
+ name = Release;
+ };
+ 97C147061CF9000F007C117D /* Debug */ = {
+ isa = XCBuildConfiguration;
+ baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */;
+ buildSettings = {
+ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
+ CLANG_ENABLE_MODULES = YES;
+ CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
+ DEVELOPMENT_TEAM = 3634FUSGED;
+ ENABLE_BITCODE = NO;
+ FRAMEWORK_SEARCH_PATHS = (
+ "$(inherited)",
+ "$(PROJECT_DIR)/Flutter",
+ );
+ INFOPLIST_FILE = Runner/Info.plist;
+ LD_RUNPATH_SEARCH_PATHS = (
+ "$(inherited)",
+ "@executable_path/Frameworks",
+ );
+ LIBRARY_SEARCH_PATHS = (
+ "$(inherited)",
+ "$(PROJECT_DIR)/Flutter",
+ );
+ PRODUCT_BUNDLE_IDENTIFIER = com.ajinasokan.flutterFgbgExample;
+ PRODUCT_NAME = "$(TARGET_NAME)";
+ SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
+ SWIFT_OPTIMIZATION_LEVEL = "-Onone";
+ SWIFT_VERSION = 5.0;
+ VERSIONING_SYSTEM = "apple-generic";
+ };
+ name = Debug;
+ };
+ 97C147071CF9000F007C117D /* Release */ = {
+ isa = XCBuildConfiguration;
+ baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */;
+ buildSettings = {
+ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
+ CLANG_ENABLE_MODULES = YES;
+ CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
+ DEVELOPMENT_TEAM = 3634FUSGED;
+ ENABLE_BITCODE = NO;
+ FRAMEWORK_SEARCH_PATHS = (
+ "$(inherited)",
+ "$(PROJECT_DIR)/Flutter",
+ );
+ INFOPLIST_FILE = Runner/Info.plist;
+ LD_RUNPATH_SEARCH_PATHS = (
+ "$(inherited)",
+ "@executable_path/Frameworks",
+ );
+ LIBRARY_SEARCH_PATHS = (
+ "$(inherited)",
+ "$(PROJECT_DIR)/Flutter",
+ );
+ PRODUCT_BUNDLE_IDENTIFIER = com.ajinasokan.flutterFgbgExample;
+ PRODUCT_NAME = "$(TARGET_NAME)";
+ SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
+ SWIFT_VERSION = 5.0;
+ VERSIONING_SYSTEM = "apple-generic";
+ };
+ name = Release;
+ };
+/* End XCBuildConfiguration section */
+
+/* Begin XCConfigurationList section */
+ 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ 97C147031CF9000F007C117D /* Debug */,
+ 97C147041CF9000F007C117D /* Release */,
+ 249021D3217E4FDB00AE95B9 /* Profile */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
+ };
+ 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ 97C147061CF9000F007C117D /* Debug */,
+ 97C147071CF9000F007C117D /* Release */,
+ 249021D4217E4FDB00AE95B9 /* Profile */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
+ };
+/* End XCConfigurationList section */
+ };
+ rootObject = 97C146E61CF9000F007C117D /* Project object */;
+}
diff --git a/third_party/pub_patches/flutter_fgbg/example/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/third_party/pub_patches/flutter_fgbg/example/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata
new file mode 100644
index 000000000..919434a62
--- /dev/null
+++ b/third_party/pub_patches/flutter_fgbg/example/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata
@@ -0,0 +1,7 @@
+
+
+
+
+
diff --git a/third_party/pub_patches/flutter_fgbg/example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/third_party/pub_patches/flutter_fgbg/example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist
new file mode 100644
index 000000000..18d981003
--- /dev/null
+++ b/third_party/pub_patches/flutter_fgbg/example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist
@@ -0,0 +1,8 @@
+
+
+
+
+ IDEDidComputeMac32BitWarning
+
+
+
diff --git a/third_party/pub_patches/flutter_fgbg/example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings b/third_party/pub_patches/flutter_fgbg/example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings
new file mode 100644
index 000000000..f9b0d7c5e
--- /dev/null
+++ b/third_party/pub_patches/flutter_fgbg/example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings
@@ -0,0 +1,8 @@
+
+
+
+
+ PreviewsEnabled
+
+
+
diff --git a/third_party/pub_patches/flutter_fgbg/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme b/third_party/pub_patches/flutter_fgbg/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme
new file mode 100644
index 000000000..a28140cfd
--- /dev/null
+++ b/third_party/pub_patches/flutter_fgbg/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme
@@ -0,0 +1,91 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/third_party/pub_patches/flutter_fgbg/example/ios/Runner.xcworkspace/contents.xcworkspacedata b/third_party/pub_patches/flutter_fgbg/example/ios/Runner.xcworkspace/contents.xcworkspacedata
new file mode 100644
index 000000000..21a3cc14c
--- /dev/null
+++ b/third_party/pub_patches/flutter_fgbg/example/ios/Runner.xcworkspace/contents.xcworkspacedata
@@ -0,0 +1,10 @@
+
+
+
+
+
+
+
diff --git a/third_party/pub_patches/flutter_fgbg/example/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/third_party/pub_patches/flutter_fgbg/example/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist
new file mode 100644
index 000000000..18d981003
--- /dev/null
+++ b/third_party/pub_patches/flutter_fgbg/example/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist
@@ -0,0 +1,8 @@
+
+
+
+
+ IDEDidComputeMac32BitWarning
+
+
+
diff --git a/third_party/pub_patches/flutter_fgbg/example/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings b/third_party/pub_patches/flutter_fgbg/example/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings
new file mode 100644
index 000000000..f9b0d7c5e
--- /dev/null
+++ b/third_party/pub_patches/flutter_fgbg/example/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings
@@ -0,0 +1,8 @@
+
+
+
+
+ PreviewsEnabled
+
+
+
diff --git a/third_party/pub_patches/flutter_fgbg/example/ios/Runner/AppDelegate.swift b/third_party/pub_patches/flutter_fgbg/example/ios/Runner/AppDelegate.swift
new file mode 100644
index 000000000..70693e4a8
--- /dev/null
+++ b/third_party/pub_patches/flutter_fgbg/example/ios/Runner/AppDelegate.swift
@@ -0,0 +1,13 @@
+import UIKit
+import Flutter
+
+@UIApplicationMain
+@objc class AppDelegate: FlutterAppDelegate {
+ override func application(
+ _ application: UIApplication,
+ didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
+ ) -> Bool {
+ GeneratedPluginRegistrant.register(with: self)
+ return super.application(application, didFinishLaunchingWithOptions: launchOptions)
+ }
+}
diff --git a/third_party/pub_patches/flutter_fgbg/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json b/third_party/pub_patches/flutter_fgbg/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json
new file mode 100644
index 000000000..d36b1fab2
--- /dev/null
+++ b/third_party/pub_patches/flutter_fgbg/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json
@@ -0,0 +1,122 @@
+{
+ "images" : [
+ {
+ "size" : "20x20",
+ "idiom" : "iphone",
+ "filename" : "Icon-App-20x20@2x.png",
+ "scale" : "2x"
+ },
+ {
+ "size" : "20x20",
+ "idiom" : "iphone",
+ "filename" : "Icon-App-20x20@3x.png",
+ "scale" : "3x"
+ },
+ {
+ "size" : "29x29",
+ "idiom" : "iphone",
+ "filename" : "Icon-App-29x29@1x.png",
+ "scale" : "1x"
+ },
+ {
+ "size" : "29x29",
+ "idiom" : "iphone",
+ "filename" : "Icon-App-29x29@2x.png",
+ "scale" : "2x"
+ },
+ {
+ "size" : "29x29",
+ "idiom" : "iphone",
+ "filename" : "Icon-App-29x29@3x.png",
+ "scale" : "3x"
+ },
+ {
+ "size" : "40x40",
+ "idiom" : "iphone",
+ "filename" : "Icon-App-40x40@2x.png",
+ "scale" : "2x"
+ },
+ {
+ "size" : "40x40",
+ "idiom" : "iphone",
+ "filename" : "Icon-App-40x40@3x.png",
+ "scale" : "3x"
+ },
+ {
+ "size" : "60x60",
+ "idiom" : "iphone",
+ "filename" : "Icon-App-60x60@2x.png",
+ "scale" : "2x"
+ },
+ {
+ "size" : "60x60",
+ "idiom" : "iphone",
+ "filename" : "Icon-App-60x60@3x.png",
+ "scale" : "3x"
+ },
+ {
+ "size" : "20x20",
+ "idiom" : "ipad",
+ "filename" : "Icon-App-20x20@1x.png",
+ "scale" : "1x"
+ },
+ {
+ "size" : "20x20",
+ "idiom" : "ipad",
+ "filename" : "Icon-App-20x20@2x.png",
+ "scale" : "2x"
+ },
+ {
+ "size" : "29x29",
+ "idiom" : "ipad",
+ "filename" : "Icon-App-29x29@1x.png",
+ "scale" : "1x"
+ },
+ {
+ "size" : "29x29",
+ "idiom" : "ipad",
+ "filename" : "Icon-App-29x29@2x.png",
+ "scale" : "2x"
+ },
+ {
+ "size" : "40x40",
+ "idiom" : "ipad",
+ "filename" : "Icon-App-40x40@1x.png",
+ "scale" : "1x"
+ },
+ {
+ "size" : "40x40",
+ "idiom" : "ipad",
+ "filename" : "Icon-App-40x40@2x.png",
+ "scale" : "2x"
+ },
+ {
+ "size" : "76x76",
+ "idiom" : "ipad",
+ "filename" : "Icon-App-76x76@1x.png",
+ "scale" : "1x"
+ },
+ {
+ "size" : "76x76",
+ "idiom" : "ipad",
+ "filename" : "Icon-App-76x76@2x.png",
+ "scale" : "2x"
+ },
+ {
+ "size" : "83.5x83.5",
+ "idiom" : "ipad",
+ "filename" : "Icon-App-83.5x83.5@2x.png",
+ "scale" : "2x"
+ },
+ {
+ "size" : "1024x1024",
+ "idiom" : "ios-marketing",
+ "filename" : "Icon-App-1024x1024@1x.png",
+ "scale" : "1x"
+ }
+ ],
+ "info" : {
+ "version" : 1,
+ "author" : "xcode"
+ }
+}
diff --git a/third_party/pub_patches/flutter_fgbg/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png b/third_party/pub_patches/flutter_fgbg/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png
new file mode 100644
index 000000000..dc9ada472
Binary files /dev/null and b/third_party/pub_patches/flutter_fgbg/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png differ
diff --git a/third_party/pub_patches/flutter_fgbg/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png b/third_party/pub_patches/flutter_fgbg/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png
new file mode 100644
index 000000000..28c6bf030
Binary files /dev/null and b/third_party/pub_patches/flutter_fgbg/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png differ
diff --git a/third_party/pub_patches/flutter_fgbg/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png b/third_party/pub_patches/flutter_fgbg/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png
new file mode 100644
index 000000000..2ccbfd967
Binary files /dev/null and b/third_party/pub_patches/flutter_fgbg/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png differ
diff --git a/third_party/pub_patches/flutter_fgbg/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png b/third_party/pub_patches/flutter_fgbg/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png
new file mode 100644
index 000000000..f091b6b0b
Binary files /dev/null and b/third_party/pub_patches/flutter_fgbg/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png differ
diff --git a/third_party/pub_patches/flutter_fgbg/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png b/third_party/pub_patches/flutter_fgbg/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png
new file mode 100644
index 000000000..4cde12118
Binary files /dev/null and b/third_party/pub_patches/flutter_fgbg/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png differ
diff --git a/third_party/pub_patches/flutter_fgbg/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png b/third_party/pub_patches/flutter_fgbg/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png
new file mode 100644
index 000000000..d0ef06e7e
Binary files /dev/null and b/third_party/pub_patches/flutter_fgbg/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png differ
diff --git a/third_party/pub_patches/flutter_fgbg/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png b/third_party/pub_patches/flutter_fgbg/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png
new file mode 100644
index 000000000..dcdc2306c
Binary files /dev/null and b/third_party/pub_patches/flutter_fgbg/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png differ
diff --git a/third_party/pub_patches/flutter_fgbg/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png b/third_party/pub_patches/flutter_fgbg/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png
new file mode 100644
index 000000000..2ccbfd967
Binary files /dev/null and b/third_party/pub_patches/flutter_fgbg/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png differ
diff --git a/third_party/pub_patches/flutter_fgbg/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png b/third_party/pub_patches/flutter_fgbg/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png
new file mode 100644
index 000000000..c8f9ed8f5
Binary files /dev/null and b/third_party/pub_patches/flutter_fgbg/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png differ
diff --git a/third_party/pub_patches/flutter_fgbg/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png b/third_party/pub_patches/flutter_fgbg/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png
new file mode 100644
index 000000000..a6d6b8609
Binary files /dev/null and b/third_party/pub_patches/flutter_fgbg/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png differ
diff --git a/third_party/pub_patches/flutter_fgbg/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png b/third_party/pub_patches/flutter_fgbg/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png
new file mode 100644
index 000000000..a6d6b8609
Binary files /dev/null and b/third_party/pub_patches/flutter_fgbg/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png differ
diff --git a/third_party/pub_patches/flutter_fgbg/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png b/third_party/pub_patches/flutter_fgbg/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png
new file mode 100644
index 000000000..75b2d164a
Binary files /dev/null and b/third_party/pub_patches/flutter_fgbg/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png differ
diff --git a/third_party/pub_patches/flutter_fgbg/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png b/third_party/pub_patches/flutter_fgbg/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png
new file mode 100644
index 000000000..c4df70d39
Binary files /dev/null and b/third_party/pub_patches/flutter_fgbg/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png differ
diff --git a/third_party/pub_patches/flutter_fgbg/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png b/third_party/pub_patches/flutter_fgbg/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png
new file mode 100644
index 000000000..6a84f41e1
Binary files /dev/null and b/third_party/pub_patches/flutter_fgbg/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png differ
diff --git a/third_party/pub_patches/flutter_fgbg/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png b/third_party/pub_patches/flutter_fgbg/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png
new file mode 100644
index 000000000..d0e1f5853
Binary files /dev/null and b/third_party/pub_patches/flutter_fgbg/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png differ
diff --git a/third_party/pub_patches/flutter_fgbg/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json b/third_party/pub_patches/flutter_fgbg/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json
new file mode 100644
index 000000000..0bedcf2fd
--- /dev/null
+++ b/third_party/pub_patches/flutter_fgbg/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json
@@ -0,0 +1,23 @@
+{
+ "images" : [
+ {
+ "idiom" : "universal",
+ "filename" : "LaunchImage.png",
+ "scale" : "1x"
+ },
+ {
+ "idiom" : "universal",
+ "filename" : "LaunchImage@2x.png",
+ "scale" : "2x"
+ },
+ {
+ "idiom" : "universal",
+ "filename" : "LaunchImage@3x.png",
+ "scale" : "3x"
+ }
+ ],
+ "info" : {
+ "version" : 1,
+ "author" : "xcode"
+ }
+}
diff --git a/third_party/pub_patches/flutter_fgbg/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png b/third_party/pub_patches/flutter_fgbg/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png
new file mode 100644
index 000000000..9da19eaca
Binary files /dev/null and b/third_party/pub_patches/flutter_fgbg/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png differ
diff --git a/third_party/pub_patches/flutter_fgbg/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png b/third_party/pub_patches/flutter_fgbg/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png
new file mode 100644
index 000000000..9da19eaca
Binary files /dev/null and b/third_party/pub_patches/flutter_fgbg/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png differ
diff --git a/third_party/pub_patches/flutter_fgbg/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png b/third_party/pub_patches/flutter_fgbg/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png
new file mode 100644
index 000000000..9da19eaca
Binary files /dev/null and b/third_party/pub_patches/flutter_fgbg/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png differ
diff --git a/third_party/pub_patches/flutter_fgbg/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md b/third_party/pub_patches/flutter_fgbg/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md
new file mode 100644
index 000000000..89c2725b7
--- /dev/null
+++ b/third_party/pub_patches/flutter_fgbg/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md
@@ -0,0 +1,5 @@
+# Launch Screen Assets
+
+You can customize the launch screen with your own desired assets by replacing the image files in this directory.
+
+You can also do it by opening your Flutter project's Xcode project with `open ios/Runner.xcworkspace`, selecting `Runner/Assets.xcassets` in the Project Navigator and dropping in the desired images.
\ No newline at end of file
diff --git a/third_party/pub_patches/flutter_fgbg/example/ios/Runner/Base.lproj/LaunchScreen.storyboard b/third_party/pub_patches/flutter_fgbg/example/ios/Runner/Base.lproj/LaunchScreen.storyboard
new file mode 100644
index 000000000..f2e259c7c
--- /dev/null
+++ b/third_party/pub_patches/flutter_fgbg/example/ios/Runner/Base.lproj/LaunchScreen.storyboard
@@ -0,0 +1,37 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/third_party/pub_patches/flutter_fgbg/example/ios/Runner/Base.lproj/Main.storyboard b/third_party/pub_patches/flutter_fgbg/example/ios/Runner/Base.lproj/Main.storyboard
new file mode 100644
index 000000000..f3c28516f
--- /dev/null
+++ b/third_party/pub_patches/flutter_fgbg/example/ios/Runner/Base.lproj/Main.storyboard
@@ -0,0 +1,26 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/third_party/pub_patches/flutter_fgbg/example/ios/Runner/Info.plist b/third_party/pub_patches/flutter_fgbg/example/ios/Runner/Info.plist
new file mode 100644
index 000000000..0fdce28e8
--- /dev/null
+++ b/third_party/pub_patches/flutter_fgbg/example/ios/Runner/Info.plist
@@ -0,0 +1,53 @@
+
+
+
+
+ CFBundleDevelopmentRegion
+ $(DEVELOPMENT_LANGUAGE)
+ CFBundleExecutable
+ $(EXECUTABLE_NAME)
+ CFBundleIdentifier
+ $(PRODUCT_BUNDLE_IDENTIFIER)
+ CFBundleInfoDictionaryVersion
+ 6.0
+ CFBundleName
+ flutter_fgbg_example
+ CFBundlePackageType
+ APPL
+ CFBundleShortVersionString
+ $(FLUTTER_BUILD_NAME)
+ CFBundleSignature
+ ????
+ CFBundleVersion
+ $(FLUTTER_BUILD_NUMBER)
+ LSRequiresIPhoneOS
+
+ UILaunchStoryboardName
+ LaunchScreen
+ UIMainStoryboardFile
+ Main
+ UISupportedInterfaceOrientations
+
+ UIInterfaceOrientationPortrait
+ UIInterfaceOrientationLandscapeLeft
+ UIInterfaceOrientationLandscapeRight
+
+ UISupportedInterfaceOrientations~ipad
+
+ UIInterfaceOrientationPortrait
+ UIInterfaceOrientationPortraitUpsideDown
+ UIInterfaceOrientationLandscapeLeft
+ UIInterfaceOrientationLandscapeRight
+
+ UIViewControllerBasedStatusBarAppearance
+
+ NSFaceIDUsageDescription
+ Just testing
+ NSPhotoLibraryUsageDescription
+ Just testing
+ NSCameraUsageDescription
+ Just testing
+ NSMicrophoneUsageDescription
+ Just testing
+
+
diff --git a/third_party/pub_patches/flutter_fgbg/example/ios/Runner/Runner-Bridging-Header.h b/third_party/pub_patches/flutter_fgbg/example/ios/Runner/Runner-Bridging-Header.h
new file mode 100644
index 000000000..308a2a560
--- /dev/null
+++ b/third_party/pub_patches/flutter_fgbg/example/ios/Runner/Runner-Bridging-Header.h
@@ -0,0 +1 @@
+#import "GeneratedPluginRegistrant.h"
diff --git a/third_party/pub_patches/flutter_fgbg/example/lib/main.dart b/third_party/pub_patches/flutter_fgbg/example/lib/main.dart
new file mode 100644
index 000000000..e3aa45dba
--- /dev/null
+++ b/third_party/pub_patches/flutter_fgbg/example/lib/main.dart
@@ -0,0 +1,122 @@
+import 'package:flutter/material.dart';
+import 'package:flutter_fgbg/flutter_fgbg.dart';
+import 'package:local_auth/local_auth.dart';
+import 'package:image_picker/image_picker.dart';
+
+void main() {
+ runApp(MyApp());
+}
+
+class MyApp extends StatefulWidget {
+ @override
+ _MyAppState createState() => _MyAppState();
+}
+
+class _MyAppState extends State with WidgetsBindingObserver {
+ final picker = ImagePicker();
+
+ List events = [];
+
+ void didChangeAppLifecycleState(AppLifecycleState state) {
+ events.add(state.toString());
+ setState(() {});
+ }
+
+ @override
+ void initState() {
+ super.initState();
+ WidgetsBinding.instance.addObserver(this);
+ }
+
+ @override
+ Widget build(BuildContext context) {
+ return FGBGNotifier(
+ onEvent: (event) {
+ events.add(event.toString());
+ setState(() {});
+ },
+ child: MaterialApp(
+ home: Scaffold(
+ body: SafeArea(
+ child: Padding(
+ padding: const EdgeInsets.all(16.0),
+ child: Column(
+ crossAxisAlignment: CrossAxisAlignment.start,
+ children: [
+ ElevatedButton(
+ onPressed: () {
+ events.clear();
+ setState(() {});
+ },
+ child: Text("Clear logs"),
+ ),
+ ElevatedButton(
+ onPressed: () async {
+ events.add("// Opening camera");
+ setState(() {});
+ await picker.pickImage(source: ImageSource.camera);
+ },
+ child: Text("Take Image"),
+ ),
+ ElevatedButton(
+ onPressed: () async {
+ events.add("// Opening gallery");
+ setState(() {});
+ await picker.pickImage(source: ImageSource.gallery);
+ },
+ child: Text("Pick Image"),
+ ),
+ ElevatedButton(
+ onPressed: () async {
+ events.add(
+ "// Opening camera but ignoring events during this");
+ setState(() {});
+ FGBGEvents.ignoreWhile(() async {
+ await picker.pickImage(source: ImageSource.camera);
+ });
+ },
+ child: Text("Take Image ignoreWhile"),
+ ),
+ ElevatedButton(
+ onPressed: () async {
+ events.add(
+ "// Opening gallery but ignoring events during this");
+ setState(() {});
+
+ FGBGEvents.ignoreWhile(() async {
+ await picker.pickImage(source: ImageSource.gallery);
+ });
+ },
+ child: Text("Pick Image ignoreWhile"),
+ ),
+ ElevatedButton(
+ onPressed: () async {
+ events.add("// Prompting biometric");
+ setState(() {});
+ var auth = LocalAuthentication();
+
+ await auth.authenticate(
+ // biometricOnly: true,
+ options: AuthenticationOptions(
+ biometricOnly: true,
+ ),
+ localizedReason: 'Test',
+ );
+ },
+ child: Text("FaceID"),
+ ),
+ SizedBox(height: 16),
+ Expanded(
+ child: ListView(
+ children: [for (var e in events) Text(e)],
+ ),
+ ),
+ ],
+ ),
+ ),
+ ),
+ ),
+ ),
+ );
+ }
+}
diff --git a/third_party/pub_patches/flutter_fgbg/example/pubspec.yaml b/third_party/pub_patches/flutter_fgbg/example/pubspec.yaml
new file mode 100644
index 000000000..8a33cf649
--- /dev/null
+++ b/third_party/pub_patches/flutter_fgbg/example/pubspec.yaml
@@ -0,0 +1,71 @@
+name: flutter_fgbg_example
+description: Demonstrates how to use the flutter_fgbg plugin.
+
+# The following line prevents the package from being accidentally published to
+# pub.dev using `pub publish`. This is preferred for private packages.
+publish_to: "none" # Remove this line if you wish to publish to pub.dev
+
+environment:
+ sdk: ">=2.12.0 <3.0.0"
+
+dependencies:
+ flutter:
+ sdk: flutter
+ local_auth: 2.1.6
+ image_picker: 0.8.7+1
+ flutter_fgbg:
+ # When depending on this package from a real application you should use:
+ # flutter_fgbg: ^x.y.z
+ # See https://dart.dev/tools/pub/dependencies#version-constraints
+ # The example app is bundled with the plugin so we use a path dependency on
+ # the parent directory to use the current plugin's version.
+ path: ../
+
+ # The following adds the Cupertino Icons font to your application.
+ # Use with the CupertinoIcons class for iOS style icons.
+ cupertino_icons: 1.0.2
+
+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:
+ # The following line ensures that the Material Icons font is
+ # included with your application, so that you can use the icons in
+ # the material Icons class.
+ uses-material-design: true
+
+ # To add assets to your application, add an assets section, like this:
+ # assets:
+ # - images/a_dot_burr.jpeg
+ # - images/a_dot_ham.jpeg
+
+ # An image asset can refer to one or more resolution-specific "variants", see
+ # https://flutter.dev/assets-and-images/#resolution-aware.
+
+ # For details regarding adding assets from package dependencies, see
+ # https://flutter.dev/assets-and-images/#from-packages
+
+ # To add custom fonts to your application, 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 from package dependencies,
+ # see https://flutter.dev/custom-fonts/#from-packages
diff --git a/third_party/pub_patches/flutter_fgbg/example/test/widget_test.dart b/third_party/pub_patches/flutter_fgbg/example/test/widget_test.dart
new file mode 100644
index 000000000..2f0f9724f
--- /dev/null
+++ b/third_party/pub_patches/flutter_fgbg/example/test/widget_test.dart
@@ -0,0 +1,27 @@
+// This is a basic Flutter widget test.
+//
+// To perform an interaction with a widget in your test, use the WidgetTester
+// utility that Flutter provides. For example, you can send tap and scroll
+// gestures. You can also use WidgetTester to find child widgets in the widget
+// tree, read text, and verify that the values of widget properties are correct.
+
+import 'package:flutter/material.dart';
+import 'package:flutter_test/flutter_test.dart';
+
+import 'package:flutter_fgbg_example/main.dart';
+
+void main() {
+ testWidgets('Verify Platform version', (WidgetTester tester) async {
+ // Build our app and trigger a frame.
+ await tester.pumpWidget(MyApp());
+
+ // Verify that platform version is retrieved.
+ expect(
+ find.byWidgetPredicate(
+ (Widget widget) =>
+ widget is Text && widget.data!.startsWith('Running on:'),
+ ),
+ findsOneWidget,
+ );
+ });
+}
diff --git a/third_party/pub_patches/flutter_fgbg/ios/Classes/FlutterFGBGPlugin.h b/third_party/pub_patches/flutter_fgbg/ios/Classes/FlutterFGBGPlugin.h
new file mode 100644
index 000000000..771700bea
--- /dev/null
+++ b/third_party/pub_patches/flutter_fgbg/ios/Classes/FlutterFGBGPlugin.h
@@ -0,0 +1,4 @@
+#import
+
+@interface FlutterFGBGPlugin : NSObject
+@end
diff --git a/third_party/pub_patches/flutter_fgbg/ios/Classes/FlutterFGBGPlugin.m b/third_party/pub_patches/flutter_fgbg/ios/Classes/FlutterFGBGPlugin.m
new file mode 100644
index 000000000..5f34edc0d
--- /dev/null
+++ b/third_party/pub_patches/flutter_fgbg/ios/Classes/FlutterFGBGPlugin.m
@@ -0,0 +1,15 @@
+#import "FlutterFGBGPlugin.h"
+#if __has_include()
+#import
+#else
+// Support project import fallback if the generated compatibility header
+// is not copied when this plugin is created as a library.
+// https://forums.swift.org/t/swift-static-libraries-dont-copy-generated-objective-c-header/19816
+#import "flutter_fgbg-Swift.h"
+#endif
+
+@implementation FlutterFGBGPlugin
++ (void)registerWithRegistrar:(NSObject*)registrar {
+ [SwiftFlutterFGBGPlugin registerWithRegistrar:registrar];
+}
+@end
diff --git a/third_party/pub_patches/flutter_fgbg/ios/Classes/SwiftFlutterFgbgPlugin.swift b/third_party/pub_patches/flutter_fgbg/ios/Classes/SwiftFlutterFgbgPlugin.swift
new file mode 100644
index 000000000..3a5103b27
--- /dev/null
+++ b/third_party/pub_patches/flutter_fgbg/ios/Classes/SwiftFlutterFgbgPlugin.swift
@@ -0,0 +1,45 @@
+import UIKit
+import Flutter
+import UserNotifications
+
+public class SwiftFlutterFGBGPlugin: NSObject, FlutterPlugin, FlutterStreamHandler {
+ private var eventSink: FlutterEventSink?
+
+ public static func register(with registrar: FlutterPluginRegistrar) {
+ let instance = SwiftFlutterFGBGPlugin()
+
+ let lifeCycleChannel = "com.ajinasokan.flutter_fgbg/events"
+ let lifecycleEventChannel = FlutterEventChannel(name: lifeCycleChannel, binaryMessenger: registrar.messenger())
+ lifecycleEventChannel.setStreamHandler(instance as FlutterStreamHandler & NSObjectProtocol)
+
+ let notificationCenter = NotificationCenter.default
+ notificationCenter.addObserver(instance,
+ selector: #selector(didEnterBackground),
+ name: UIApplication.didEnterBackgroundNotification,
+ object: nil)
+
+ notificationCenter.addObserver(instance,
+ selector: #selector(willEnterForeground),
+ name: UIApplication.willEnterForegroundNotification,
+ object: nil)
+ }
+
+ public func onListen(withArguments arguments: Any?,
+ eventSink: @escaping FlutterEventSink) -> FlutterError? {
+ self.eventSink = eventSink
+ return nil
+ }
+
+ public func onCancel(withArguments arguments: Any?) -> FlutterError? {
+ eventSink = nil
+ return nil
+ }
+
+ @objc func didEnterBackground() {
+ self.eventSink?("background")
+ }
+
+ @objc func willEnterForeground() {
+ self.eventSink?("foreground")
+ }
+}
diff --git a/third_party/pub_patches/flutter_fgbg/ios/flutter_fgbg.podspec b/third_party/pub_patches/flutter_fgbg/ios/flutter_fgbg.podspec
new file mode 100644
index 000000000..890967981
--- /dev/null
+++ b/third_party/pub_patches/flutter_fgbg/ios/flutter_fgbg.podspec
@@ -0,0 +1,23 @@
+#
+# To learn more about a Podspec see http://guides.cocoapods.org/syntax/podspec.html.
+# Run `pod lib lint flutter_fgbg.podspec' to validate before publishing.
+#
+Pod::Spec.new do |s|
+ s.name = 'flutter_fgbg'
+ s.version = '0.0.1'
+ s.summary = 'Flutter plugin to detect when app(not Flutter container) goes to background or foreground'
+ s.description = <<-DESC
+Flutter plugin to detect when app(not Flutter container) goes to background or foreground
+ DESC
+ s.homepage = 'http://example.com'
+ s.license = { :file => '../LICENSE' }
+ s.author = { 'Your Company' => 'email@example.com' }
+ s.source = { :path => '.' }
+ s.source_files = 'Classes/**/*'
+ s.dependency 'Flutter'
+ s.platform = :ios, '8.0'
+
+ # Flutter.framework does not contain a i386 slice. Only x86_64 simulators are supported.
+ s.pod_target_xcconfig = { 'DEFINES_MODULE' => 'YES', 'VALID_ARCHS[sdk=iphonesimulator*]' => 'x86_64' }
+ s.swift_version = '5.0'
+end
diff --git a/third_party/pub_patches/flutter_fgbg/lib/flutter_fgbg.dart b/third_party/pub_patches/flutter_fgbg/lib/flutter_fgbg.dart
new file mode 100644
index 000000000..393b449b3
--- /dev/null
+++ b/third_party/pub_patches/flutter_fgbg/lib/flutter_fgbg.dart
@@ -0,0 +1,69 @@
+import 'dart:async';
+
+import 'package:flutter/services.dart';
+import 'package:flutter/widgets.dart';
+
+enum FGBGType {
+ foreground,
+ background,
+}
+
+class FGBGEvents {
+ static const _channel = EventChannel("com.ajinasokan.flutter_fgbg/events");
+ static Stream? _stream;
+
+ static Stream get stream {
+ return _stream ??= _channel
+ .receiveBroadcastStream()
+ .where((_) => !_ignoreEvent)
+ .map((e) =>
+ e == "foreground" ? FGBGType.foreground : FGBGType.background);
+ }
+
+ static bool _ignoreEvent = false;
+ static void ignoreWhile(dynamic Function() closure) async {
+ _ignoreEvent = true;
+ try {
+ final result = closure();
+ if (result is Future) await result;
+ } catch (e) {
+ _ignoreEvent = false;
+ rethrow;
+ }
+ _ignoreEvent = false;
+ }
+}
+
+class FGBGNotifier extends StatefulWidget {
+ final Widget child;
+ final ValueChanged onEvent;
+
+ FGBGNotifier({
+ required this.child,
+ required this.onEvent,
+ });
+
+ @override
+ _FGBGNotifierState createState() => _FGBGNotifierState();
+}
+
+class _FGBGNotifierState extends State {
+ StreamSubscription? subscription;
+
+ @override
+ void initState() {
+ super.initState();
+ subscription = FGBGEvents.stream.listen((event) {
+ widget.onEvent.call(event);
+ });
+ }
+
+ @override
+ void dispose() {
+ subscription?.cancel();
+ super.dispose();
+ }
+
+ @override
+ Widget build(BuildContext context) => widget.child;
+}
diff --git a/third_party/pub_patches/flutter_fgbg/pubspec.yaml b/third_party/pub_patches/flutter_fgbg/pubspec.yaml
new file mode 100644
index 000000000..6bffacbb2
--- /dev/null
+++ b/third_party/pub_patches/flutter_fgbg/pubspec.yaml
@@ -0,0 +1,64 @@
+name: flutter_fgbg
+description: Flutter plugin to detect when app(not Flutter container) goes to background or foreground
+version: 0.3.0
+homepage: https://github.com/ajinasokan/flutter_fgbg
+
+environment:
+ sdk: ">=2.12.0 <3.0.0"
+ flutter: ">=1.12.13+hotfix.6"
+
+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.ajinasokan.flutter_fgbg
+ pluginClass: FlutterFGBGPlugin
+ ios:
+ pluginClass: FlutterFGBGPlugin
+
+ # To add assets to your plugin package, add an assets section, like this:
+ # assets:
+ # - images/a_dot_burr.jpeg
+ # - 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
diff --git a/third_party/pub_patches/fluttertoast/CHANGELOG.md b/third_party/pub_patches/fluttertoast/CHANGELOG.md
new file mode 100644
index 000000000..c7ffdf4f4
--- /dev/null
+++ b/third_party/pub_patches/fluttertoast/CHANGELOG.md
@@ -0,0 +1,338 @@
+## [8.2.8]
+
+- Merged multiple PRs thanks for your contributions
+
+## [8.2.6]
+
+- Merged PR #507 - Migrating dart:html to package:web thanks to @a-h-mzd for PR
+
+## [8.2.5]
+
+- Merged multiple PRs #492 #490 #489 #480 - Thanks to all contributors
+
+## [8.2.4]
+
+- Reverted the minSDK 22 which broke many apps - Sorry everyone
+
+## [8.2.3]
+
+- Merged PR #466 (fixes issue in android)
+
+## [8.2.2]
+
+- Adjustments for flutter >=3.10
+
+## [8.2.1]
+
+- Removed `context.mounted`
+
+## [8.2.0]
+
+- Updated the flow for Toast with Context
+
+## [8.1.4]
+
+- Merged #419 (added environment restriction in pubspec)
+
+## [8.1.3]
+
+- Merged #415 (improvements to provide safer usage) @AlexSeednov
+- Updated Readme.md
+
+## [8.1.2]
+
+- Merged #405 #408
+
+## [8.1.1] [8.1.0]
+
+- Many issues fixed
+- iOS M1 Chip Fix
+
+## [8.0.9]
+
+- Merged PRS #342 #353 #363 #346 and #370
+
+## [8.0.8]
+
+- Many issues fixed
+
+## [8.0.7]
+
+- Added fadeDuration in FToast to set fade animation Duration
+- Fixed Toast behind the screen #287 , #281
+- Fixed #303
+## [8.0.6]
+
+- Only safe (?.) or non-null asserted (!!.) (#300)
+
+## [8.0.5]
+
+- Fixed Unresolved reference: R (Issue with Android API 30)
+## [8.0.4]
+
+- Fixed Unresolved reference: R (Issue with Android API 30)
+
+## [8.0.3]
+
+- flutter analyze fixes
+
+## [8.0.2]
+
+- Null Safety
+- Code Docs Added
+
+## [7.1.8]
+
+- Web sourceMap Warning
+
+## [7.1.7]
+
+- '\n' line crash on Web PR Merged
+- Android 11 Crash fixed
+- Many bug fixes
+
+## [7.1.6]
+
+- minor fixes
+
+## [7.1.4]
+
+- minor fixes
+
+## [7.1.3]
+
+- Android Default bg when fontSize or textColor set fixed
+
+## [7.1.2]
+
+- Android Rounded Corners fix (#238)
+- Android Crash if cancel called before init (#231)
+- Web now load js & css from assets
+- Web SyntaxError if Toast msg has `'` fixed
+
+## [7.1.1]
+
+- iOS Unused variables fix
+
+## [7.1.0]
+
+- Breaking change for FToast, Need to call `FToast.init(context)` before `showToast`
+- AnimationController fix
+- Android `NonNull` build Fix
+- FToast Added new `PositionedToastBuilder` you can define Custom Postition now for toast
+- Merged #228, Fix UIView+Toast.o duplicate symbols - Thanks @jackkang0401 and @yongshuai.kang
+- Now `textcolor` will work for web toast
+
+## [7.0.4]
+
+- iOS Build Failed Fixed #218
+- Fixed Cancel Toasts in iOS
+
+## [7.0.3]
+
+- FToast now Fade when showing and hiding the toast
+- Toast backgroud now supports transparency
+- Bug fixes
+
+## [7.0.2]
+
+- iOS Toast behind keyboard fixed. #203
+
+## [7.0.1+1]
+
+- Readme Updated
+
+## [7.0.1]
+
+- Android Build failed fix
+- iOS Crash Fix
+
+## [7.0.0]
+
+- Reverted to Old code `Fluttertoast`
+- Also contains new code `FToast`
+
+## [6.0.1]
+
+- Support for old `Fluttertoast.showToast`
+
+## [6.0.0]
+
+- Complete new package
+- Now plugin dont use any native code
+
+## [5.0.2]
+
+- Web Fix after name change
+
+
+## [5.0.1]
+
+- Many things changes on android side (this will break your current implementation)
+- `Fluttertoast.` to `FlutterToast.`
+- many fixes
+
+## [4.0.2]
+
+- Delete print on fluttertoast_web
+
+## [4.0.1]
+
+- ReadMe Fixes
+
+## [4.0.0]
+
+- Added Web Support
+
+## [3.1.3]
+
+- Toast optimized for Android
+
+## [3.1.2]
+
+- Flutter analysis failed fixed
+
+## [3.1.1]
+
+- Not Compiling in android (issue with AndroidX)
+
+## [3.1.0]
+
+- Migrated to AndroidX
+
+##[3.0.6]
+
+- iOS build failed fixed
+
+## [3.0.5]
+
+- deprecation fixed
+- hope ios notch fixed
+
+## [3.0.4]
+
+- Android Color fix
+
+## [3.0.3]
+
+- fixed Android Toast.LENGTH\_\*
+
+## [3.0.2]
+
+- fixed #70 #71
+
+## [3.0.1]
+
+- Release build failed fix
+- Multiline text android fix
+
+## [3.0.0]
+
+- Migrated to AndroidX
+
+## [2.2.12]
+
+- Incomplete Text Fix
+
+## [2.2.11]
+
+- Incomplete Text Fix
+
+## [2.2.10]
+
+- iOS build Failed fix
+
+## [2.2.9]
+
+- iOS build Failed fix
+
+## [2.2.8]
+
+- `Fluttertoast.cancel()` added
+- FlutterToast Implementation revert back to previous
+
+## [2.2.7]
+
+- FontSize Can be changed
+- FlutterToast Implementation Changed to `FlutterToast.instance`
+
+## [2.2.6]
+
+- removed androidx
+
+## [2.2.5]
+
+- Cannot build because of dependency w/ v28 #47
+
+## [2.2.4]
+
+- androidX crash fix #45
+
+## [2.2.3]
+
+- iOS Crash fix #41 & #39
+
+## [2.2.1]
+
+- default toast style fix #38
+
+## [2.2.0]
+
+- Background color fixed #29
+
+## [2.1.5]
+
+- Merged PR #36 - Fix Number Cast Error for issue #35
+
+## [2.1.4]
+
+- Merged PR #32
+
+## [2.1.2]
+
+- iOS Color Fix
+- Background color fix in PIE
+
+## [2.1.1]
+
+- Background color does not fill the whole Toast fixed
+
+## [2.1.0]
+
+- build error fixed
+
+## [2.0.9]
+
+- fix error in flutter 0.9.7
+
+## [2.0.8]
+
+- Build failed with an exception fixed
+- The plugin calls the build of the previous widget fixed
+- Screenshots added
+
+## [2.0.7]
+
+- Text background fix for android
+
+## [2.0.6]
+
+- iOS Release build error fixed
+
+## [2.0.3]
+
+- iOs run time error fixed
+
+## [2.0.2]
+
+- iOs build error fixed
+
+## [2.0.1]
+
+- Ios Support added
+- option for setting toast gravity (top, center, bottom)
+
+## [1.0.1]
+
+- Initial Open Sources
+- show Toast in Android
diff --git a/third_party/pub_patches/fluttertoast/CONTRIBUTING.md b/third_party/pub_patches/fluttertoast/CONTRIBUTING.md
new file mode 100644
index 000000000..a1d8c668d
--- /dev/null
+++ b/third_party/pub_patches/fluttertoast/CONTRIBUTING.md
@@ -0,0 +1,22 @@
+# Contributing
+
+Awesome! Contributions of all kinds are greatly appreciated. To help smoothen the process we have a few non-exhaustive guidelines to follow which should get you going in no time.
+
+## Using GitHub Issues
+
+- Feel free to use GitHub issues for questions, bug reports, and feature requests
+- Use the search feature to check for an existing issue
+- Include as much information as possible and provide any relevant resources (Eg. screenshots)
+- For bug reports ensure you have a reproducible test case
+ - A pull request with a breaking test would be super preferable here but isn't required
+
+## Getting Started
+- Fork the repository
+- Send your commits
+- Submit a Pull request
+
+## Submitting a Pull Request
+- Write appropriate title
+- Wrtie a proper description including the issue name and solution
+
+
diff --git a/third_party/pub_patches/fluttertoast/LICENSE b/third_party/pub_patches/fluttertoast/LICENSE
new file mode 100644
index 000000000..26d032e2f
--- /dev/null
+++ b/third_party/pub_patches/fluttertoast/LICENSE
@@ -0,0 +1,21 @@
+MIT License
+
+Copyright (c) 2020 Karthik Ponnam
+
+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.
diff --git a/third_party/pub_patches/fluttertoast/README.md b/third_party/pub_patches/fluttertoast/README.md
new file mode 100644
index 000000000..1395f76a2
--- /dev/null
+++ b/third_party/pub_patches/fluttertoast/README.md
@@ -0,0 +1,234 @@
+
+# [fluttertoast](https://pub.dev/packages/fluttertoast)
+
+Toast Library for Flutter
+
+
+
+Now this toast library supports two kinds of toast messages one which requires `BuildContext` other with No `BuildContext`
+
+## Toast with no context
+
+> Supported Platforms
+>
+> - Android
+> - IOS
+> - Web (Uses [Toastify-JS](https://github.com/apvarun/toastify-js))
+
+This one has limited features and no control over UI
+
+
+## Toast Which requires BuildContext
+
+> Supported Platforms
+>
+> - ALL
+
+1. Full Control of the Toast
+2. Toasts will be queued
+3. Remove a toast
+4. Clear the queue
+
+
+## How to Use
+
+```yaml
+# add this line to your dependencies
+fluttertoast: ^8.2.8
+```
+
+```dart
+import 'package:fluttertoast/fluttertoast.dart';
+```
+
+## Toast with No Build Context (Android & iOS)
+
+```dart
+Fluttertoast.showToast(
+ msg: "This is Center Short Toast",
+ toastLength: Toast.LENGTH_SHORT,
+ gravity: ToastGravity.CENTER,
+ timeInSecForIosWeb: 1,
+ backgroundColor: Colors.red,
+ textColor: Colors.white,
+ fontSize: 16.0
+ );
+```
+
+| property | description | default |
+| --------------- | ------------------------------------------------------------------ |------------|
+| msg | String (Not Null)(required) |required |
+| toastLength | Toast.LENGTH_SHORT or Toast.LENGTH_LONG (optional) |Toast.LENGTH_SHORT |
+| gravity | ToastGravity.TOP (or) ToastGravity.CENTER (or) ToastGravity.BOTTOM (Web Only supports top, bottom) | ToastGravity.BOTTOM |
+| timeInSecForIosWeb | int (for ios & web) | 1 (sec) |
+| backgroundColor | Colors.red |null |
+| textcolor | Colors.white |null |
+| fontSize | 16.0 (float) | null |
+| fontAsset | Path to a font file in the Flutter app assets folder, e.g. 'assets/path/to/some-font.ttf' (String) | null |
+| webShowClose | false (bool) | false |
+| webBgColor | String (hex Color) | linear-gradient(to right, #00b09b, #96c93d) |
+| webPosition | String (`left`, `center` or `right`) | right |
+
+### To cancel all the toasts call
+
+```dart
+Fluttertoast.cancel()
+```
+
+### Note Android
+
+
+
+
+> Custom Toast will not work on android 11 and above, it will only use *msg* and *toastLength* remaining all properties are ignored
+
+
+### Custom Toast For Android
+
+Create a file named `toast_custom.xml` in your project `app/res/layout` folder and do custom styling
+
+```xml
+
+
+
+
+
+```
+
+## Toast with BuildContext (All Platforms)
+
+Update your `MaterialApp` with `builder` like below for the use of Context globally check doc section Use NavigatorKey for Context(to access context globally)
+
+```dart
+MaterialApp(
+ builder: FToastBuilder(),
+ home: MyApp(),
+ navigatorKey: navigatorKey,
+),
+```
+
+```dart
+FToast fToast;
+
+@override
+void initState() {
+ super.initState();
+ fToast = FToast();
+ // if you want to use context from globally instead of content we need to pass navigatorKey.currentContext!
+ fToast.init(context);
+}
+
+_showToast() {
+ Widget toast = Container(
+ padding: const EdgeInsets.symmetric(horizontal: 24.0, vertical: 12.0),
+ decoration: BoxDecoration(
+ borderRadius: BorderRadius.circular(25.0),
+ color: Colors.greenAccent,
+ ),
+ child: Row(
+ mainAxisSize: MainAxisSize.min,
+ children: [
+ Icon(Icons.check),
+ SizedBox(
+ width: 12.0,
+ ),
+ Text("This is a Custom Toast"),
+ ],
+ ),
+ );
+
+
+ fToast.showToast(
+ child: toast,
+ gravity: ToastGravity.BOTTOM,
+ toastDuration: Duration(seconds: 2),
+ );
+
+ // Custom Toast Position
+ fToast.showToast(
+ child: toast,
+ toastDuration: Duration(seconds: 2),
+ positionedToastBuilder: (context, child) {
+ return Positioned(
+ child: child,
+ top: 16.0,
+ left: 16.0,
+ );
+ });
+}
+
+```
+
+Now Call `_showToast()`
+
+For more details check `example` project
+
+| property | description | default |
+| --------------- | ------------------------------------------------------------------ |------------|
+| child | Widget (Not Null)(required) |required |
+| toastDuration | Duration (optional) | |
+| gravity | ToastGravity.* | |
+
+### Use NavigatorKey for Context(to access context globally)
+
+To use NavigatorKey for Context first define the `GlobalKey` at top level in your `main.dart` file
+
+```dart
+GlobalKey navigatorKey = GlobalKey();
+```
+
+At the time of initializing the `FToast` we need to use context from globally defined `GlobalKey`
+
+```dart
+FToast fToast = FToast();
+fToast.init(yourNavKey.currentContext!);
+```
+
+### To cancel all the toasts call
+
+```dart
+// To remove present shwoing toast
+fToast.removeCustomToast()
+
+// To clear the queue
+fToast.removeQueuedCustomToasts();
+```
+
+## Preview Images (No BuildContext)
+
+
+
+
+
+
+## Preview Images (BuildContext)
+
+
+
+
+## If you need any features suggest
+
+...
+
+
+## Buy Me a Coffee
+
+
diff --git a/third_party/pub_patches/fluttertoast/android/build.gradle b/third_party/pub_patches/fluttertoast/android/build.gradle
new file mode 100644
index 000000000..0be8fd5d0
--- /dev/null
+++ b/third_party/pub_patches/fluttertoast/android/build.gradle
@@ -0,0 +1,59 @@
+group 'com.example.FlutterToast'
+version '1.0-SNAPSHOT'
+
+buildscript {
+ ext.kotlin_version = '1.7.0'
+ repositories {
+ google()
+ mavenCentral()
+ }
+
+ dependencies {
+ classpath 'com.android.tools.build:gradle:7.1.3'
+ //noinspection GradleDependency
+ 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 33
+
+ compileOptions {
+ sourceCompatibility JavaVersion.VERSION_11
+ targetCompatibility JavaVersion.VERSION_11
+ }
+
+ kotlinOptions {
+ jvmTarget = '11'
+ }
+
+ sourceSets {
+ main.java.srcDirs += 'src/main/kotlin'
+ }
+
+ defaultConfig {
+ minSdkVersion 19
+ targetSdkVersion 33
+ if (project.android.hasProperty('namespace')) {
+ namespace 'io.github.ponnamkarthik.toast.fluttertoast'
+ }
+ }
+
+ lintOptions {
+ disable 'InvalidPackage'
+ }
+}
+
+dependencies {
+ implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
+}
diff --git a/third_party/pub_patches/fluttertoast/android/gradle.properties b/third_party/pub_patches/fluttertoast/android/gradle.properties
new file mode 100644
index 000000000..94adc3a3f
--- /dev/null
+++ b/third_party/pub_patches/fluttertoast/android/gradle.properties
@@ -0,0 +1,3 @@
+org.gradle.jvmargs=-Xmx1536M
+android.useAndroidX=true
+android.enableJetifier=true
diff --git a/third_party/pub_patches/fluttertoast/android/gradle/wrapper/gradle-wrapper.properties b/third_party/pub_patches/fluttertoast/android/gradle/wrapper/gradle-wrapper.properties
new file mode 100644
index 000000000..595fb867a
--- /dev/null
+++ b/third_party/pub_patches/fluttertoast/android/gradle/wrapper/gradle-wrapper.properties
@@ -0,0 +1,6 @@
+#Fri Jun 23 08:50:38 CEST 2017
+distributionBase=GRADLE_USER_HOME
+distributionPath=wrapper/dists
+zipStoreBase=GRADLE_USER_HOME
+zipStorePath=wrapper/dists
+distributionUrl=https\://services.gradle.org/distributions/gradle-7.2-all.zip
diff --git a/third_party/pub_patches/fluttertoast/android/settings.gradle b/third_party/pub_patches/fluttertoast/android/settings.gradle
new file mode 100644
index 000000000..c68c12ec0
--- /dev/null
+++ b/third_party/pub_patches/fluttertoast/android/settings.gradle
@@ -0,0 +1 @@
+rootProject.name = 'FlutterToast'
diff --git a/third_party/pub_patches/fluttertoast/android/src/main/AndroidManifest.xml b/third_party/pub_patches/fluttertoast/android/src/main/AndroidManifest.xml
new file mode 100644
index 000000000..e68b9d7b0
--- /dev/null
+++ b/third_party/pub_patches/fluttertoast/android/src/main/AndroidManifest.xml
@@ -0,0 +1,3 @@
+
+
diff --git a/third_party/pub_patches/fluttertoast/android/src/main/kotlin/io/github/ponnamkarthik/toast/fluttertoast/FlutterToastPlugin.kt b/third_party/pub_patches/fluttertoast/android/src/main/kotlin/io/github/ponnamkarthik/toast/fluttertoast/FlutterToastPlugin.kt
new file mode 100644
index 000000000..e660fe415
--- /dev/null
+++ b/third_party/pub_patches/fluttertoast/android/src/main/kotlin/io/github/ponnamkarthik/toast/fluttertoast/FlutterToastPlugin.kt
@@ -0,0 +1,33 @@
+package io.github.ponnamkarthik.toast.fluttertoast
+
+import android.content.Context
+import androidx.annotation.NonNull
+import io.flutter.embedding.engine.plugins.FlutterPlugin
+import io.flutter.plugin.common.BinaryMessenger
+import io.flutter.plugin.common.MethodCall
+import io.flutter.plugin.common.MethodChannel
+/** FlutterToastPlugin */
+public class FlutterToastPlugin: FlutterPlugin {
+
+ private var channel: MethodChannel? = null
+
+ override fun onAttachedToEngine(binding: FlutterPlugin.FlutterPluginBinding,) {
+ setupChannel(binding.binaryMessenger, binding.applicationContext,)
+ }
+
+ override fun onDetachedFromEngine(p0: FlutterPlugin.FlutterPluginBinding,) {
+ teardownChannel();
+ }
+
+ private fun setupChannel(messenger: BinaryMessenger, context: Context,) {
+ channel = MethodChannel(messenger, "PonnamKarthik/fluttertoast",)
+ val handler = MethodCallHandlerImpl(context,)
+ channel?.setMethodCallHandler(handler,)
+ }
+
+ private fun teardownChannel() {
+ channel?.setMethodCallHandler(null,)
+ channel = null
+ }
+
+}
diff --git a/third_party/pub_patches/fluttertoast/android/src/main/kotlin/io/github/ponnamkarthik/toast/fluttertoast/MethodCallHandlerImpl.kt b/third_party/pub_patches/fluttertoast/android/src/main/kotlin/io/github/ponnamkarthik/toast/fluttertoast/MethodCallHandlerImpl.kt
new file mode 100644
index 000000000..c15d35f56
--- /dev/null
+++ b/third_party/pub_patches/fluttertoast/android/src/main/kotlin/io/github/ponnamkarthik/toast/fluttertoast/MethodCallHandlerImpl.kt
@@ -0,0 +1,138 @@
+package io.github.ponnamkarthik.toast.fluttertoast
+
+import android.app.Activity
+import android.content.Context
+import android.content.res.AssetManager
+import android.graphics.PorterDuff
+import android.graphics.Typeface
+import android.graphics.drawable.Drawable
+import android.os.Build
+import android.view.Gravity
+import android.view.LayoutInflater
+import android.widget.TextView
+import android.widget.Toast
+import androidx.core.content.ContextCompat
+import io.flutter.FlutterInjector
+import io.flutter.plugin.common.MethodCall
+import io.flutter.plugin.common.MethodChannel
+import io.flutter.plugin.common.MethodChannel.MethodCallHandler
+import java.io.File
+
+internal class MethodCallHandlerImpl(private var context: Context) : MethodCallHandler {
+
+ private var mToast: Toast? = null
+
+ override fun onMethodCall(call: MethodCall, result: MethodChannel.Result,) {
+ when (call.method) {
+ "showToast" -> {
+ val mMessage = call.argument("msg").toString()
+ val length = call.argument("length").toString()
+ val gravity = call.argument("gravity").toString()
+ val bgcolor = call.argument("bgcolor")
+ val textcolor = call.argument("textcolor")
+ val fontSize = call.argument("fontSize")
+ val fontAsset = call.argument("fontAsset")
+
+ val mGravity: Int = when (gravity) {
+ "top" -> Gravity.TOP
+ "center" -> Gravity.CENTER
+ else -> Gravity.BOTTOM
+ }
+
+ val mDuration: Int = if (length == "long") {
+ Toast.LENGTH_LONG
+ } else {
+ Toast.LENGTH_SHORT
+ }
+
+ if (bgcolor != null) {
+ val layout = (context.getSystemService(Context.LAYOUT_INFLATER_SERVICE,) as LayoutInflater).inflate(R.layout.toast_custom, null,)
+ val text = layout.findViewById(R.id.text,)
+ text.text = mMessage
+
+ val gradientDrawable: Drawable? = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
+ context.getDrawable(R.drawable.corner)!!
+ } else {
+ // context.resources.getDrawable(R.drawable.corner)
+ ContextCompat.getDrawable(context, R.drawable.corner)
+ }
+ gradientDrawable!!.setColorFilter(bgcolor.toInt(), PorterDuff.Mode.SRC_IN)
+ text.background = gradientDrawable
+
+ if (fontSize != null) {
+ text.textSize = fontSize.toFloat()
+ }
+ if (textcolor != null) {
+ text.setTextColor(textcolor.toInt())
+ }
+
+ mToast = Toast(context,)
+ mToast?.duration = mDuration
+
+ if (fontAsset != null) {
+ val assetManager: AssetManager = context.assets
+ val key = FlutterInjector.instance().flutterLoader().getLookupKeyForAsset(fontAsset)
+ text.typeface = Typeface.createFromAsset(assetManager, key);
+ }
+ mToast?.view = layout
+ } else {
+ mToast = Toast.makeText(context, mMessage, mDuration)
+ if (Build.VERSION.SDK_INT < Build.VERSION_CODES.R) {
+ val textView: TextView = mToast?.view!!.findViewById(android.R.id.message)
+ if (fontSize != null) {
+ textView.textSize = fontSize.toFloat()
+ }
+ if (textcolor != null) {
+ textView.setTextColor(textcolor.toInt())
+ }
+ if (fontAsset != null) {
+ val assetManager: AssetManager = context.assets
+ val key = FlutterInjector.instance().flutterLoader().getLookupKeyForAsset(fontAsset)
+ textView.typeface = Typeface.createFromAsset(assetManager, key);
+ }
+ }
+ }
+
+ try {
+ when (mGravity) {
+ Gravity.CENTER -> {
+ mToast?.setGravity(mGravity, 0, 0,)
+ }
+ Gravity.TOP -> {
+ mToast?.setGravity(mGravity, 0, 100,)
+ }
+ else -> {
+ mToast?.setGravity(mGravity, 0, 100,)
+ }
+ }
+ } catch (e: Exception,) { }
+
+ if (context is Activity) {
+ (context as Activity).runOnUiThread { mToast?.show() }
+ } else {
+ mToast?.show()
+ }
+
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
+ mToast?.addCallback(
+ object : Toast.Callback() {
+ override fun onToastHidden() {
+ super.onToastHidden()
+ mToast = null
+ }
+ },
+ )
+ }
+ result.success(true,)
+ }
+ "cancel" -> {
+ if (mToast != null) {
+ mToast?.cancel()
+ mToast = null
+ }
+ result.success(true,)
+ }
+ else -> result.notImplemented()
+ }
+ }
+}
diff --git a/third_party/pub_patches/fluttertoast/android/src/main/res/drawable/corner.xml b/third_party/pub_patches/fluttertoast/android/src/main/res/drawable/corner.xml
new file mode 100644
index 000000000..341a7119a
--- /dev/null
+++ b/third_party/pub_patches/fluttertoast/android/src/main/res/drawable/corner.xml
@@ -0,0 +1,7 @@
+
+
+
+
\ No newline at end of file
diff --git a/third_party/pub_patches/fluttertoast/android/src/main/res/drawable/toast_bg.xml b/third_party/pub_patches/fluttertoast/android/src/main/res/drawable/toast_bg.xml
new file mode 100644
index 000000000..6c99b3100
--- /dev/null
+++ b/third_party/pub_patches/fluttertoast/android/src/main/res/drawable/toast_bg.xml
@@ -0,0 +1,14 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/third_party/pub_patches/fluttertoast/android/src/main/res/layout/toast_custom.xml b/third_party/pub_patches/fluttertoast/android/src/main/res/layout/toast_custom.xml
new file mode 100644
index 000000000..e24c63daf
--- /dev/null
+++ b/third_party/pub_patches/fluttertoast/android/src/main/res/layout/toast_custom.xml
@@ -0,0 +1,24 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/third_party/pub_patches/fluttertoast/assets/toastify.css b/third_party/pub_patches/fluttertoast/assets/toastify.css
new file mode 100644
index 000000000..2d0471ef9
--- /dev/null
+++ b/third_party/pub_patches/fluttertoast/assets/toastify.css
@@ -0,0 +1,14 @@
+/**
+ * Minified by jsDelivr using clean-css v4.2.3.
+ * Original file: /npm/toastify-js@1.9.3/src/toastify.css
+ *
+ * Do NOT use SRI with dynamically generated files! More information: https://www.jsdelivr.com/using-sri-with-dynamic-files
+ */
+/*!
+ * Toastify js 1.9.3
+ * https://github.com/apvarun/toastify-js
+ * @license MIT licensed
+ *
+ * Copyright (C) 2018 Varun A P
+ */
+.toastify{padding:12px 20px;color:#fff;display:inline-block;box-shadow:0 3px 6px -1px rgba(0,0,0,.12),0 10px 36px -4px rgba(77,96,232,.3);background:-webkit-linear-gradient(315deg,#73a5ff,#5477f5);background:linear-gradient(135deg,#73a5ff,#5477f5);position:fixed;opacity:0;transition:all .4s cubic-bezier(.215,.61,.355,1);border-radius:2px;cursor:pointer;text-decoration:none;max-width:calc(50% - 20px);z-index:2147483647}.toastify.on{opacity:1}.toast-close{opacity:.4;padding:0 5px}.toastify-right{right:15px}.toastify-left{left:15px}.toastify-top{top:-150px}.toastify-bottom{bottom:-150px}.toastify-rounded{border-radius:25px}.toastify-avatar{width:1.5em;height:1.5em;margin:-7px 5px;border-radius:2px}.toastify-center{margin-left:auto;margin-right:auto;left:0;right:0;max-width:fit-content;max-width:-moz-fit-content}@media only screen and (max-width:360px){.toastify-left,.toastify-right{margin-left:auto;margin-right:auto;left:0;right:0;max-width:fit-content}}
\ No newline at end of file
diff --git a/third_party/pub_patches/fluttertoast/assets/toastify.js b/third_party/pub_patches/fluttertoast/assets/toastify.js
new file mode 100644
index 000000000..3bb0860a0
--- /dev/null
+++ b/third_party/pub_patches/fluttertoast/assets/toastify.js
@@ -0,0 +1,14 @@
+/**
+ * Minified by jsDelivr using Terser v5.3.0.
+ * Original file: /npm/toastify-js@1.9.3/src/toastify.js
+ *
+ * Do NOT use SRI with dynamically generated files! More information: https://www.jsdelivr.com/using-sri-with-dynamic-files
+ */
+/*!
+ * Toastify js 1.9.3
+ * https://github.com/apvarun/toastify-js
+ * @license MIT licensed
+ *
+ * Copyright (C) 2018 Varun A P
+ */
+!function(t,o){"object"==typeof module && module && module.exports?module.exports=o():t.Toastify=o()}(this,(function(t){var o=function(t){return new o.lib.init(t)};function i(t,o){return o.offset[t]?isNaN(o.offset[t])?o.offset[t]:o.offset[t]+"px":"0px"}function s(t,o){return!(!t||"string"!=typeof o)&&!!(t.className&&t.className.trim().split(/\s+/gi).indexOf(o)>-1)}return o.lib=o.prototype={toastify:"1.9.3",constructor:o,init:function(t){return t||(t={}),this.options={},this.toastElement=null,this.options.text=t.text||"Hi there!",this.options.node=t.node,this.options.duration=0===t.duration?0:t.duration||3e3,this.options.selector=t.selector,this.options.callback=t.callback||function(){},this.options.destination=t.destination,this.options.newWindow=t.newWindow||!1,this.options.close=t.close||!1,this.options.gravity="bottom"===t.gravity?"toastify-bottom":"toastify-top",this.options.positionLeft=t.positionLeft||!1,this.options.position=t.position||"",this.options.backgroundColor=t.backgroundColor,this.options.avatar=t.avatar||"",this.options.className=t.className||"",this.options.stopOnFocus=void 0===t.stopOnFocus||t.stopOnFocus,this.options.onClick=t.onClick,this.options.offset=t.offset||{x:0,y:0},this},buildToast:function(){if(!this.options)throw"Toastify is not initialized";var t=document.createElement("div");if(t.className="toastify on "+this.options.className,this.options.position?t.className+=" toastify-"+this.options.position:!0===this.options.positionLeft?(t.className+=" toastify-left",console.warn("Property `positionLeft` will be depreciated in further versions. Please use `position` instead.")):t.className+=" toastify-right",t.className+=" "+this.options.gravity,this.options.backgroundColor&&(t.style.background=this.options.backgroundColor),this.options.node&&this.options.node.nodeType===Node.ELEMENT_NODE)t.appendChild(this.options.node);else if(t.innerHTML=this.options.text,""!==this.options.avatar){var o=document.createElement("img");o.src=this.options.avatar,o.className="toastify-avatar","left"==this.options.position||!0===this.options.positionLeft?t.appendChild(o):t.insertAdjacentElement("afterbegin",o)}if(!0===this.options.close){var s=document.createElement("span");s.innerHTML="✖",s.className="toast-close",s.addEventListener("click",function(t){t.stopPropagation(),this.removeElement(this.toastElement),window.clearTimeout(this.toastElement.timeOutValue)}.bind(this));var n=window.innerWidth>0?window.innerWidth:screen.width;("left"==this.options.position||!0===this.options.positionLeft)&&n>360?t.insertAdjacentElement("afterbegin",s):t.appendChild(s)}if(this.options.stopOnFocus&&this.options.duration>0){var e=this;t.addEventListener("mouseover",(function(o){window.clearTimeout(t.timeOutValue)})),t.addEventListener("mouseleave",(function(){t.timeOutValue=window.setTimeout((function(){e.removeElement(t)}),e.options.duration)}))}if(void 0!==this.options.destination&&t.addEventListener("click",function(t){t.stopPropagation(),!0===this.options.newWindow?window.open(this.options.destination,"_blank"):window.location=this.options.destination}.bind(this)),"function"==typeof this.options.onClick&&void 0===this.options.destination&&t.addEventListener("click",function(t){t.stopPropagation(),this.options.onClick()}.bind(this)),"object"==typeof this.options.offset){var a=i("x",this.options),p=i("y",this.options),r="left"==this.options.position?a:"-"+a,l="toastify-top"==this.options.gravity?p:"-"+p;t.style.transform="translate("+r+","+l+")"}return t},showToast:function(){var t;if(this.toastElement=this.buildToast(),!(t=void 0===this.options.selector?document.body:document.getElementById(this.options.selector)))throw"Root element is not defined";return t.insertBefore(this.toastElement,t.firstChild),o.reposition(),this.options.duration>0&&(this.toastElement.timeOutValue=window.setTimeout(function(){this.removeElement(this.toastElement)}.bind(this),this.options.duration)),this},hideToast:function(){this.toastElement.timeOutValue&&clearTimeout(this.toastElement.timeOutValue),this.removeElement(this.toastElement)},removeElement:function(t){t.className=t.className.replace(" on",""),window.setTimeout(function(){this.options.node&&this.options.node.parentNode&&this.options.node.parentNode.removeChild(this.options.node),t.parentNode&&t.parentNode.removeChild(t),this.options.callback.call(t),o.reposition()}.bind(this),400)}},o.reposition=function(){for(var t,o={top:15,bottom:15},i={top:15,bottom:15},n={top:15,bottom:15},e=document.getElementsByClassName("toastify"),a=0;a0?window.innerWidth:screen.width)<=360?(e[a].style[t]=n[t]+"px",n[t]+=p+15):!0===s(e[a],"toastify-left")?(e[a].style[t]=o[t]+"px",o[t]+=p+15):(e[a].style[t]=i[t]+"px",i[t]+=p+15)}return this},o.lib.init.prototype=o.lib,o}));
diff --git a/third_party/pub_patches/fluttertoast/example/README.md b/third_party/pub_patches/fluttertoast/example/README.md
new file mode 100644
index 000000000..cd375a691
--- /dev/null
+++ b/third_party/pub_patches/fluttertoast/example/README.md
@@ -0,0 +1,16 @@
+# FlutterToast_example
+
+Demonstrates how to use the FlutterToast plugin.
+
+## Getting Started
+
+This project is a starting point for a Flutter application.
+
+A few resources to get you started if this is your first Flutter project:
+
+- [Lab: Write your first Flutter app](https://flutter.dev/docs/get-started/codelab)
+- [Cookbook: Useful Flutter samples](https://flutter.dev/docs/cookbook)
+
+For help getting started with Flutter, view our
+[online documentation](https://flutter.dev/docs), which offers tutorials,
+samples, guidance on mobile development, and a full API reference.
diff --git a/third_party/pub_patches/fluttertoast/example/analysis_options.yaml b/third_party/pub_patches/fluttertoast/example/analysis_options.yaml
new file mode 100644
index 000000000..61b6c4de1
--- /dev/null
+++ b/third_party/pub_patches/fluttertoast/example/analysis_options.yaml
@@ -0,0 +1,29 @@
+# This file configures the analyzer, which statically analyzes Dart code to
+# check for errors, warnings, and lints.
+#
+# The issues identified by the analyzer are surfaced in the UI of Dart-enabled
+# IDEs (https://dart.dev/tools#ides-and-editors). The analyzer can also be
+# invoked from the command line by running `flutter analyze`.
+
+# The following line activates a set of recommended lints for Flutter apps,
+# packages, and plugins designed to encourage good coding practices.
+include: package:flutter_lints/flutter.yaml
+
+linter:
+ # The lint rules applied to this project can be customized in the
+ # section below to disable rules from the `package:flutter_lints/flutter.yaml`
+ # included above or to enable additional rules. A list of all available lints
+ # and their documentation is published at
+ # https://dart-lang.github.io/linter/lints/index.html.
+ #
+ # Instead of disabling a lint rule for the entire project in the
+ # section below, it can also be suppressed for a single line of code
+ # or a specific dart file by using the `// ignore: name_of_lint` and
+ # `// ignore_for_file: name_of_lint` syntax on the line or in the file
+ # producing the lint.
+ rules:
+ # avoid_print: false # Uncomment to disable the `avoid_print` rule
+ # prefer_single_quotes: true # Uncomment to enable the `prefer_single_quotes` rule
+
+# Additional information about this file can be found at
+# https://dart.dev/guides/language/analysis-options
diff --git a/third_party/pub_patches/fluttertoast/example/android/app/build.gradle b/third_party/pub_patches/fluttertoast/example/android/app/build.gradle
new file mode 100644
index 000000000..ef7cb5a49
--- /dev/null
+++ b/third_party/pub_patches/fluttertoast/example/android/app/build.gradle
@@ -0,0 +1,71 @@
+def localProperties = new Properties()
+def localPropertiesFile = rootProject.file('local.properties')
+if (localPropertiesFile.exists()) {
+ localPropertiesFile.withReader('UTF-8') { reader ->
+ localProperties.load(reader)
+ }
+}
+
+def flutterRoot = localProperties.getProperty('flutter.sdk')
+if (flutterRoot == null) {
+ throw new GradleException("Flutter SDK not found. Define location with flutter.sdk in the local.properties file.")
+}
+
+def flutterVersionCode = localProperties.getProperty('flutter.versionCode')
+if (flutterVersionCode == null) {
+ flutterVersionCode = '1'
+}
+
+def flutterVersionName = localProperties.getProperty('flutter.versionName')
+if (flutterVersionName == null) {
+ flutterVersionName = '1.0'
+}
+
+apply plugin: 'com.android.application'
+apply plugin: 'kotlin-android'
+apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle"
+
+android {
+ compileSdkVersion 33
+ ndkVersion flutter.ndkVersion
+
+ compileOptions {
+ sourceCompatibility JavaVersion.VERSION_1_8
+ targetCompatibility JavaVersion.VERSION_1_8
+ }
+
+ kotlinOptions {
+ jvmTarget = '1.8'
+ }
+
+ sourceSets {
+ main.java.srcDirs += 'src/main/kotlin'
+ }
+
+ defaultConfig {
+ // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html).
+ applicationId "com.example.example"
+ // You can update the following values to match your application needs.
+ // For more information, see: https://docs.flutter.dev/deployment/android#reviewing-the-build-configuration.
+ minSdkVersion flutter.minSdkVersion
+ targetSdkVersion flutter.targetSdkVersion
+ versionCode flutterVersionCode.toInteger()
+ versionName flutterVersionName
+ }
+
+ buildTypes {
+ release {
+ // TODO: Add your own signing config for the release build.
+ // Signing with the debug keys for now, so `flutter run --release` works.
+ signingConfig signingConfigs.debug
+ }
+ }
+}
+
+flutter {
+ source '../..'
+}
+
+dependencies {
+ implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
+}
diff --git a/third_party/pub_patches/fluttertoast/example/android/app/src/debug/AndroidManifest.xml b/third_party/pub_patches/fluttertoast/example/android/app/src/debug/AndroidManifest.xml
new file mode 100644
index 000000000..45d523a2a
--- /dev/null
+++ b/third_party/pub_patches/fluttertoast/example/android/app/src/debug/AndroidManifest.xml
@@ -0,0 +1,8 @@
+
+
+
+
diff --git a/third_party/pub_patches/fluttertoast/example/android/app/src/main/AndroidManifest.xml b/third_party/pub_patches/fluttertoast/example/android/app/src/main/AndroidManifest.xml
new file mode 100644
index 000000000..3f41384db
--- /dev/null
+++ b/third_party/pub_patches/fluttertoast/example/android/app/src/main/AndroidManifest.xml
@@ -0,0 +1,34 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/third_party/pub_patches/fluttertoast/example/android/app/src/main/kotlin/com/example/example/MainActivity.kt b/third_party/pub_patches/fluttertoast/example/android/app/src/main/kotlin/com/example/example/MainActivity.kt
new file mode 100644
index 000000000..e793a000d
--- /dev/null
+++ b/third_party/pub_patches/fluttertoast/example/android/app/src/main/kotlin/com/example/example/MainActivity.kt
@@ -0,0 +1,6 @@
+package com.example.example
+
+import io.flutter.embedding.android.FlutterActivity
+
+class MainActivity: FlutterActivity() {
+}
diff --git a/third_party/pub_patches/fluttertoast/example/android/app/src/main/res/drawable-v21/launch_background.xml b/third_party/pub_patches/fluttertoast/example/android/app/src/main/res/drawable-v21/launch_background.xml
new file mode 100644
index 000000000..f74085f3f
--- /dev/null
+++ b/third_party/pub_patches/fluttertoast/example/android/app/src/main/res/drawable-v21/launch_background.xml
@@ -0,0 +1,12 @@
+
+
+
+
+
+
+
+
diff --git a/third_party/pub_patches/fluttertoast/example/android/app/src/main/res/drawable/launch_background.xml b/third_party/pub_patches/fluttertoast/example/android/app/src/main/res/drawable/launch_background.xml
new file mode 100644
index 000000000..304732f88
--- /dev/null
+++ b/third_party/pub_patches/fluttertoast/example/android/app/src/main/res/drawable/launch_background.xml
@@ -0,0 +1,12 @@
+
+
+
+
+
+
+
+
diff --git a/third_party/pub_patches/fluttertoast/example/android/app/src/main/res/mipmap-hdpi/ic_launcher.png b/third_party/pub_patches/fluttertoast/example/android/app/src/main/res/mipmap-hdpi/ic_launcher.png
new file mode 100644
index 000000000..db77bb4b7
Binary files /dev/null and b/third_party/pub_patches/fluttertoast/example/android/app/src/main/res/mipmap-hdpi/ic_launcher.png differ
diff --git a/third_party/pub_patches/fluttertoast/example/android/app/src/main/res/mipmap-mdpi/ic_launcher.png b/third_party/pub_patches/fluttertoast/example/android/app/src/main/res/mipmap-mdpi/ic_launcher.png
new file mode 100644
index 000000000..17987b79b
Binary files /dev/null and b/third_party/pub_patches/fluttertoast/example/android/app/src/main/res/mipmap-mdpi/ic_launcher.png differ
diff --git a/third_party/pub_patches/fluttertoast/example/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png b/third_party/pub_patches/fluttertoast/example/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png
new file mode 100644
index 000000000..09d439148
Binary files /dev/null and b/third_party/pub_patches/fluttertoast/example/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png differ
diff --git a/third_party/pub_patches/fluttertoast/example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png b/third_party/pub_patches/fluttertoast/example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
new file mode 100644
index 000000000..d5f1c8d34
Binary files /dev/null and b/third_party/pub_patches/fluttertoast/example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png differ
diff --git a/third_party/pub_patches/fluttertoast/example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/third_party/pub_patches/fluttertoast/example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
new file mode 100644
index 000000000..4d6372eeb
Binary files /dev/null and b/third_party/pub_patches/fluttertoast/example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png differ
diff --git a/third_party/pub_patches/fluttertoast/example/android/app/src/main/res/values-night/styles.xml b/third_party/pub_patches/fluttertoast/example/android/app/src/main/res/values-night/styles.xml
new file mode 100644
index 000000000..06952be74
--- /dev/null
+++ b/third_party/pub_patches/fluttertoast/example/android/app/src/main/res/values-night/styles.xml
@@ -0,0 +1,18 @@
+
+
+
+
+
+
+
diff --git a/third_party/pub_patches/fluttertoast/example/android/app/src/main/res/values/styles.xml b/third_party/pub_patches/fluttertoast/example/android/app/src/main/res/values/styles.xml
new file mode 100644
index 000000000..cb1ef8805
--- /dev/null
+++ b/third_party/pub_patches/fluttertoast/example/android/app/src/main/res/values/styles.xml
@@ -0,0 +1,18 @@
+
+
+
+
+
+
+
diff --git a/third_party/pub_patches/fluttertoast/example/android/app/src/profile/AndroidManifest.xml b/third_party/pub_patches/fluttertoast/example/android/app/src/profile/AndroidManifest.xml
new file mode 100644
index 000000000..45d523a2a
--- /dev/null
+++ b/third_party/pub_patches/fluttertoast/example/android/app/src/profile/AndroidManifest.xml
@@ -0,0 +1,8 @@
+
+
+
+
diff --git a/third_party/pub_patches/fluttertoast/example/android/build.gradle b/third_party/pub_patches/fluttertoast/example/android/build.gradle
new file mode 100644
index 000000000..ffb6e0ec7
--- /dev/null
+++ b/third_party/pub_patches/fluttertoast/example/android/build.gradle
@@ -0,0 +1,31 @@
+buildscript {
+ ext.kotlin_version = '1.7.0'
+ repositories {
+ google()
+ mavenCentral()
+ }
+
+ dependencies {
+ classpath 'com.android.tools.build:gradle:7.4.0'
+ classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
+ }
+}
+
+allprojects {
+ repositories {
+ google()
+ mavenCentral()
+ }
+}
+
+rootProject.buildDir = '../build'
+subprojects {
+ project.buildDir = "${rootProject.buildDir}/${project.name}"
+}
+subprojects {
+ project.evaluationDependsOn(':app')
+}
+
+tasks.register("clean", Delete) {
+ delete rootProject.buildDir
+}
diff --git a/third_party/pub_patches/fluttertoast/example/android/gradle.properties b/third_party/pub_patches/fluttertoast/example/android/gradle.properties
new file mode 100644
index 000000000..94adc3a3f
--- /dev/null
+++ b/third_party/pub_patches/fluttertoast/example/android/gradle.properties
@@ -0,0 +1,3 @@
+org.gradle.jvmargs=-Xmx1536M
+android.useAndroidX=true
+android.enableJetifier=true
diff --git a/third_party/pub_patches/fluttertoast/example/android/gradle/wrapper/gradle-wrapper.properties b/third_party/pub_patches/fluttertoast/example/android/gradle/wrapper/gradle-wrapper.properties
new file mode 100644
index 000000000..c08cdc9e9
--- /dev/null
+++ b/third_party/pub_patches/fluttertoast/example/android/gradle/wrapper/gradle-wrapper.properties
@@ -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-7.6-bin.zip
diff --git a/third_party/pub_patches/fluttertoast/example/android/gradlew b/third_party/pub_patches/fluttertoast/example/android/gradlew
new file mode 100755
index 000000000..9d82f7891
--- /dev/null
+++ b/third_party/pub_patches/fluttertoast/example/android/gradlew
@@ -0,0 +1,160 @@
+#!/usr/bin/env bash
+
+##############################################################################
+##
+## Gradle start up script for UN*X
+##
+##############################################################################
+
+# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+DEFAULT_JVM_OPTS=""
+
+APP_NAME="Gradle"
+APP_BASE_NAME=`basename "$0"`
+
+# Use the maximum available, or set MAX_FD != -1 to use that value.
+MAX_FD="maximum"
+
+warn ( ) {
+ echo "$*"
+}
+
+die ( ) {
+ echo
+ echo "$*"
+ echo
+ exit 1
+}
+
+# OS specific support (must be 'true' or 'false').
+cygwin=false
+msys=false
+darwin=false
+case "`uname`" in
+ CYGWIN* )
+ cygwin=true
+ ;;
+ Darwin* )
+ darwin=true
+ ;;
+ MINGW* )
+ msys=true
+ ;;
+esac
+
+# Attempt to set APP_HOME
+# Resolve links: $0 may be a link
+PRG="$0"
+# Need this for relative symlinks.
+while [ -h "$PRG" ] ; do
+ ls=`ls -ld "$PRG"`
+ link=`expr "$ls" : '.*-> \(.*\)$'`
+ if expr "$link" : '/.*' > /dev/null; then
+ PRG="$link"
+ else
+ PRG=`dirname "$PRG"`"/$link"
+ fi
+done
+SAVED="`pwd`"
+cd "`dirname \"$PRG\"`/" >/dev/null
+APP_HOME="`pwd -P`"
+cd "$SAVED" >/dev/null
+
+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" = "false" -a "$darwin" = "false" ] ; then
+ MAX_FD_LIMIT=`ulimit -H -n`
+ if [ $? -eq 0 ] ; then
+ if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
+ MAX_FD="$MAX_FD_LIMIT"
+ fi
+ ulimit -n $MAX_FD
+ if [ $? -ne 0 ] ; then
+ warn "Could not set maximum file descriptor limit: $MAX_FD"
+ fi
+ else
+ warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
+ fi
+fi
+
+# For Darwin, add options to specify how the application appears in the dock
+if $darwin; then
+ GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
+fi
+
+# For Cygwin, switch paths to Windows format before running java
+if $cygwin ; then
+ APP_HOME=`cygpath --path --mixed "$APP_HOME"`
+ CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
+ JAVACMD=`cygpath --unix "$JAVACMD"`
+
+ # We build the pattern for arguments to be converted via cygpath
+ ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
+ SEP=""
+ for dir in $ROOTDIRSRAW ; do
+ ROOTDIRS="$ROOTDIRS$SEP$dir"
+ SEP="|"
+ done
+ OURCYGPATTERN="(^($ROOTDIRS))"
+ # Add a user-defined pattern to the cygpath arguments
+ if [ "$GRADLE_CYGPATTERN" != "" ] ; then
+ OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
+ fi
+ # Now convert the arguments - kludge to limit ourselves to /bin/sh
+ i=0
+ for arg in "$@" ; do
+ CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
+ CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
+
+ if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
+ eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
+ else
+ eval `echo args$i`="\"$arg\""
+ fi
+ i=$((i+1))
+ done
+ case $i in
+ (0) set -- ;;
+ (1) set -- "$args0" ;;
+ (2) set -- "$args0" "$args1" ;;
+ (3) set -- "$args0" "$args1" "$args2" ;;
+ (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
+ (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
+ (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
+ (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
+ (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
+ (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
+ esac
+fi
+
+# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules
+function splitJvmOpts() {
+ JVM_OPTS=("$@")
+}
+eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS
+JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME"
+
+exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@"
diff --git a/third_party/pub_patches/fluttertoast/example/android/gradlew.bat b/third_party/pub_patches/fluttertoast/example/android/gradlew.bat
new file mode 100644
index 000000000..8a0b282aa
--- /dev/null
+++ b/third_party/pub_patches/fluttertoast/example/android/gradlew.bat
@@ -0,0 +1,90 @@
+@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
+
+@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=
+
+set DIRNAME=%~dp0
+if "%DIRNAME%" == "" set DIRNAME=.
+set APP_BASE_NAME=%~n0
+set APP_HOME=%DIRNAME%
+
+@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 init
+
+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 init
+
+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
+
+:init
+@rem Get command-line arguments, handling Windowz variants
+
+if not "%OS%" == "Windows_NT" goto win9xME_args
+if "%@eval[2+2]" == "4" goto 4NT_args
+
+:win9xME_args
+@rem Slurp the command line arguments.
+set CMD_LINE_ARGS=
+set _SKIP=2
+
+:win9xME_args_slurp
+if "x%~1" == "x" goto execute
+
+set CMD_LINE_ARGS=%*
+goto execute
+
+:4NT_args
+@rem Get arguments from the 4NT Shell from JP Software
+set CMD_LINE_ARGS=%$
+
+: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 %CMD_LINE_ARGS%
+
+: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
diff --git a/third_party/pub_patches/fluttertoast/example/android/settings.gradle b/third_party/pub_patches/fluttertoast/example/android/settings.gradle
new file mode 100644
index 000000000..44e62bcf0
--- /dev/null
+++ b/third_party/pub_patches/fluttertoast/example/android/settings.gradle
@@ -0,0 +1,11 @@
+include ':app'
+
+def localPropertiesFile = new File(rootProject.projectDir, "local.properties")
+def properties = new Properties()
+
+assert localPropertiesFile.exists()
+localPropertiesFile.withReader("UTF-8") { reader -> properties.load(reader) }
+
+def flutterSdkPath = properties.getProperty("flutter.sdk")
+assert flutterSdkPath != null, "flutter.sdk not set in local.properties"
+apply from: "$flutterSdkPath/packages/flutter_tools/gradle/app_plugin_loader.gradle"
diff --git a/third_party/pub_patches/fluttertoast/example/ios/Flutter/AppFrameworkInfo.plist b/third_party/pub_patches/fluttertoast/example/ios/Flutter/AppFrameworkInfo.plist
new file mode 100644
index 000000000..7c5696400
--- /dev/null
+++ b/third_party/pub_patches/fluttertoast/example/ios/Flutter/AppFrameworkInfo.plist
@@ -0,0 +1,26 @@
+
+
+
+
+ CFBundleDevelopmentRegion
+ en
+ CFBundleExecutable
+ App
+ CFBundleIdentifier
+ io.flutter.flutter.app
+ CFBundleInfoDictionaryVersion
+ 6.0
+ CFBundleName
+ App
+ CFBundlePackageType
+ FMWK
+ CFBundleShortVersionString
+ 1.0
+ CFBundleSignature
+ ????
+ CFBundleVersion
+ 1.0
+ MinimumOSVersion
+ 12.0
+
+
diff --git a/third_party/pub_patches/fluttertoast/example/ios/Flutter/Debug.xcconfig b/third_party/pub_patches/fluttertoast/example/ios/Flutter/Debug.xcconfig
new file mode 100644
index 000000000..ec97fc6f3
--- /dev/null
+++ b/third_party/pub_patches/fluttertoast/example/ios/Flutter/Debug.xcconfig
@@ -0,0 +1,2 @@
+#include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"
+#include "Generated.xcconfig"
diff --git a/third_party/pub_patches/fluttertoast/example/ios/Flutter/Release.xcconfig b/third_party/pub_patches/fluttertoast/example/ios/Flutter/Release.xcconfig
new file mode 100644
index 000000000..c4855bfe2
--- /dev/null
+++ b/third_party/pub_patches/fluttertoast/example/ios/Flutter/Release.xcconfig
@@ -0,0 +1,2 @@
+#include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"
+#include "Generated.xcconfig"
diff --git a/third_party/pub_patches/fluttertoast/example/ios/Podfile b/third_party/pub_patches/fluttertoast/example/ios/Podfile
new file mode 100644
index 000000000..f09c53e06
--- /dev/null
+++ b/third_party/pub_patches/fluttertoast/example/ios/Podfile
@@ -0,0 +1,44 @@
+# Uncomment this line to define a global platform for your project
+# platform :ios, '12.0'
+
+# CocoaPods analytics sends network stats synchronously affecting flutter build latency.
+ENV['COCOAPODS_DISABLE_STATS'] = 'true'
+
+project 'Runner', {
+ 'Debug' => :debug,
+ 'Profile' => :release,
+ 'Release' => :release,
+}
+
+def flutter_root
+ generated_xcode_build_settings_path = File.expand_path(File.join('..', 'Flutter', 'Generated.xcconfig'), __FILE__)
+ unless File.exist?(generated_xcode_build_settings_path)
+ raise "#{generated_xcode_build_settings_path} must exist. If you're running pod install manually, make sure flutter pub get is executed first"
+ end
+
+ File.foreach(generated_xcode_build_settings_path) do |line|
+ matches = line.match(/FLUTTER_ROOT\=(.*)/)
+ return matches[1].strip if matches
+ end
+ raise "FLUTTER_ROOT not found in #{generated_xcode_build_settings_path}. Try deleting Generated.xcconfig, then run flutter pub get"
+end
+
+require File.expand_path(File.join('packages', 'flutter_tools', 'bin', 'podhelper'), flutter_root)
+
+flutter_ios_podfile_setup
+
+target 'Runner' do
+ use_frameworks!
+ use_modular_headers!
+
+ flutter_install_all_ios_pods File.dirname(File.realpath(__FILE__))
+end
+
+post_install do |installer|
+ installer.pods_project.build_configurations.each do |config|
+ config.build_settings["EXCLUDED_ARCHS[sdk=iphonesimulator*]"] = "arm64"
+ end
+ installer.pods_project.targets.each do |target|
+ flutter_additional_ios_build_settings(target)
+ end
+end
diff --git a/third_party/pub_patches/fluttertoast/example/ios/Podfile.lock b/third_party/pub_patches/fluttertoast/example/ios/Podfile.lock
new file mode 100644
index 000000000..1be0743f5
--- /dev/null
+++ b/third_party/pub_patches/fluttertoast/example/ios/Podfile.lock
@@ -0,0 +1,29 @@
+PODS:
+ - Flutter (1.0.0)
+ - fluttertoast (0.0.2):
+ - Flutter
+ - Toast
+ - Toast (4.0.0)
+
+DEPENDENCIES:
+ - Flutter (from `Flutter`)
+ - fluttertoast (from `.symlinks/plugins/fluttertoast/ios`)
+
+SPEC REPOS:
+ trunk:
+ - Toast
+
+EXTERNAL SOURCES:
+ Flutter:
+ :path: Flutter
+ fluttertoast:
+ :path: ".symlinks/plugins/fluttertoast/ios"
+
+SPEC CHECKSUMS:
+ Flutter: e0871f40cf51350855a761d2e70bf5af5b9b5de7
+ fluttertoast: e9a18c7be5413da53898f660530c56f35edfba9c
+ Toast: 91b396c56ee72a5790816f40d3a94dd357abc196
+
+PODFILE CHECKSUM: 196b22cfac078643b05547cc40c0d80279b4874b
+
+COCOAPODS: 1.13.0
diff --git a/third_party/pub_patches/fluttertoast/example/ios/Runner.xcodeproj/project.pbxproj b/third_party/pub_patches/fluttertoast/example/ios/Runner.xcodeproj/project.pbxproj
new file mode 100644
index 000000000..14fb60f91
--- /dev/null
+++ b/third_party/pub_patches/fluttertoast/example/ios/Runner.xcodeproj/project.pbxproj
@@ -0,0 +1,555 @@
+// !$*UTF8*$!
+{
+ archiveVersion = 1;
+ classes = {
+ };
+ objectVersion = 54;
+ objects = {
+
+/* Begin PBXBuildFile section */
+ 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; };
+ 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; };
+ 74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74858FAE1ED2DC5600515810 /* AppDelegate.swift */; };
+ 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; };
+ 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; };
+ 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; };
+ B258CC96A73DFA5D644773DE /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 9C3E157E544CED11802E00C7 /* Pods_Runner.framework */; };
+/* End PBXBuildFile section */
+
+/* Begin PBXCopyFilesBuildPhase section */
+ 9705A1C41CF9048500538489 /* Embed Frameworks */ = {
+ isa = PBXCopyFilesBuildPhase;
+ buildActionMask = 2147483647;
+ dstPath = "";
+ dstSubfolderSpec = 10;
+ files = (
+ );
+ name = "Embed Frameworks";
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+/* End PBXCopyFilesBuildPhase section */
+
+/* Begin PBXFileReference section */
+ 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GeneratedPluginRegistrant.h; sourceTree = ""; };
+ 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = ""; };
+ 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = ""; };
+ 74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Runner-Bridging-Header.h"; sourceTree = ""; };
+ 74858FAE1ED2DC5600515810 /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; };
+ 7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = ""; };
+ 7E6448B676433AEABB5439B3 /* Pods-Runner.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.debug.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"; sourceTree = ""; };
+ 8831E6A950CD469DC16E09C6 /* Pods-Runner.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.release.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"; sourceTree = ""; };
+ 89F6F8D60DC6CEC891FB4473 /* Pods-Runner.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.profile.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.profile.xcconfig"; sourceTree = ""; };
+ 9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Debug.xcconfig; path = Flutter/Debug.xcconfig; sourceTree = ""; };
+ 9740EEB31CF90195004384FC /* Generated.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Generated.xcconfig; path = Flutter/Generated.xcconfig; sourceTree = ""; };
+ 97C146EE1CF9000F007C117D /* Runner.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Runner.app; sourceTree = BUILT_PRODUCTS_DIR; };
+ 97C146FB1CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; };
+ 97C146FD1CF9000F007C117D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; };
+ 97C147001CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; };
+ 97C147021CF9000F007C117D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; };
+ 9C3E157E544CED11802E00C7 /* Pods_Runner.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Runner.framework; sourceTree = BUILT_PRODUCTS_DIR; };
+/* End PBXFileReference section */
+
+/* Begin PBXFrameworksBuildPhase section */
+ 97C146EB1CF9000F007C117D /* Frameworks */ = {
+ isa = PBXFrameworksBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ B258CC96A73DFA5D644773DE /* Pods_Runner.framework in Frameworks */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+/* End PBXFrameworksBuildPhase section */
+
+/* Begin PBXGroup section */
+ 373601E36306F84B036EB455 /* Frameworks */ = {
+ isa = PBXGroup;
+ children = (
+ 9C3E157E544CED11802E00C7 /* Pods_Runner.framework */,
+ );
+ name = Frameworks;
+ sourceTree = "";
+ };
+ 9740EEB11CF90186004384FC /* Flutter */ = {
+ isa = PBXGroup;
+ children = (
+ 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */,
+ 9740EEB21CF90195004384FC /* Debug.xcconfig */,
+ 7AFA3C8E1D35360C0083082E /* Release.xcconfig */,
+ 9740EEB31CF90195004384FC /* Generated.xcconfig */,
+ );
+ name = Flutter;
+ sourceTree = "";
+ };
+ 97C146E51CF9000F007C117D = {
+ isa = PBXGroup;
+ children = (
+ 9740EEB11CF90186004384FC /* Flutter */,
+ 97C146F01CF9000F007C117D /* Runner */,
+ 97C146EF1CF9000F007C117D /* Products */,
+ C68FA61D2A053F5122846726 /* Pods */,
+ 373601E36306F84B036EB455 /* Frameworks */,
+ );
+ sourceTree = "";
+ };
+ 97C146EF1CF9000F007C117D /* Products */ = {
+ isa = PBXGroup;
+ children = (
+ 97C146EE1CF9000F007C117D /* Runner.app */,
+ );
+ name = Products;
+ sourceTree = "";
+ };
+ 97C146F01CF9000F007C117D /* Runner */ = {
+ isa = PBXGroup;
+ children = (
+ 97C146FA1CF9000F007C117D /* Main.storyboard */,
+ 97C146FD1CF9000F007C117D /* Assets.xcassets */,
+ 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */,
+ 97C147021CF9000F007C117D /* Info.plist */,
+ 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */,
+ 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */,
+ 74858FAE1ED2DC5600515810 /* AppDelegate.swift */,
+ 74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */,
+ );
+ path = Runner;
+ sourceTree = "";
+ };
+ C68FA61D2A053F5122846726 /* Pods */ = {
+ isa = PBXGroup;
+ children = (
+ 7E6448B676433AEABB5439B3 /* Pods-Runner.debug.xcconfig */,
+ 8831E6A950CD469DC16E09C6 /* Pods-Runner.release.xcconfig */,
+ 89F6F8D60DC6CEC891FB4473 /* Pods-Runner.profile.xcconfig */,
+ );
+ name = Pods;
+ path = Pods;
+ sourceTree = "";
+ };
+/* End PBXGroup section */
+
+/* Begin PBXNativeTarget section */
+ 97C146ED1CF9000F007C117D /* Runner */ = {
+ isa = PBXNativeTarget;
+ buildConfigurationList = 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */;
+ buildPhases = (
+ 069DA48D6E9C750356F2FF02 /* [CP] Check Pods Manifest.lock */,
+ 9740EEB61CF901F6004384FC /* Run Script */,
+ 97C146EA1CF9000F007C117D /* Sources */,
+ 97C146EB1CF9000F007C117D /* Frameworks */,
+ 97C146EC1CF9000F007C117D /* Resources */,
+ 9705A1C41CF9048500538489 /* Embed Frameworks */,
+ 3B06AD1E1E4923F5004D2608 /* Thin Binary */,
+ 49226DDD737324CB669C67D4 /* [CP] Embed Pods Frameworks */,
+ );
+ buildRules = (
+ );
+ dependencies = (
+ );
+ name = Runner;
+ productName = Runner;
+ productReference = 97C146EE1CF9000F007C117D /* Runner.app */;
+ productType = "com.apple.product-type.application";
+ };
+/* End PBXNativeTarget section */
+
+/* Begin PBXProject section */
+ 97C146E61CF9000F007C117D /* Project object */ = {
+ isa = PBXProject;
+ attributes = {
+ LastUpgradeCheck = 1510;
+ ORGANIZATIONNAME = "";
+ TargetAttributes = {
+ 97C146ED1CF9000F007C117D = {
+ CreatedOnToolsVersion = 7.3.1;
+ LastSwiftMigration = 1100;
+ };
+ };
+ };
+ buildConfigurationList = 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */;
+ compatibilityVersion = "Xcode 9.3";
+ developmentRegion = en;
+ hasScannedForEncodings = 0;
+ knownRegions = (
+ en,
+ Base,
+ );
+ mainGroup = 97C146E51CF9000F007C117D;
+ productRefGroup = 97C146EF1CF9000F007C117D /* Products */;
+ projectDirPath = "";
+ projectRoot = "";
+ targets = (
+ 97C146ED1CF9000F007C117D /* Runner */,
+ );
+ };
+/* End PBXProject section */
+
+/* Begin PBXResourcesBuildPhase section */
+ 97C146EC1CF9000F007C117D /* Resources */ = {
+ isa = PBXResourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */,
+ 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */,
+ 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */,
+ 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+/* End PBXResourcesBuildPhase section */
+
+/* Begin PBXShellScriptBuildPhase section */
+ 069DA48D6E9C750356F2FF02 /* [CP] Check Pods Manifest.lock */ = {
+ isa = PBXShellScriptBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ );
+ inputFileListPaths = (
+ );
+ inputPaths = (
+ "${PODS_PODFILE_DIR_PATH}/Podfile.lock",
+ "${PODS_ROOT}/Manifest.lock",
+ );
+ name = "[CP] Check Pods Manifest.lock";
+ outputFileListPaths = (
+ );
+ outputPaths = (
+ "$(DERIVED_FILE_DIR)/Pods-Runner-checkManifestLockResult.txt",
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ shellPath = /bin/sh;
+ shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n";
+ showEnvVarsInLog = 0;
+ };
+ 3B06AD1E1E4923F5004D2608 /* Thin Binary */ = {
+ isa = PBXShellScriptBuildPhase;
+ alwaysOutOfDate = 1;
+ buildActionMask = 2147483647;
+ files = (
+ );
+ inputPaths = (
+ "${TARGET_BUILD_DIR}/${INFOPLIST_PATH}",
+ );
+ name = "Thin Binary";
+ outputPaths = (
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ shellPath = /bin/sh;
+ shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" embed_and_thin";
+ };
+ 49226DDD737324CB669C67D4 /* [CP] Embed Pods Frameworks */ = {
+ isa = PBXShellScriptBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ );
+ inputFileListPaths = (
+ "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-input-files.xcfilelist",
+ );
+ name = "[CP] Embed Pods Frameworks";
+ outputFileListPaths = (
+ "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-output-files.xcfilelist",
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ shellPath = /bin/sh;
+ shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh\"\n";
+ showEnvVarsInLog = 0;
+ };
+ 9740EEB61CF901F6004384FC /* Run Script */ = {
+ isa = PBXShellScriptBuildPhase;
+ alwaysOutOfDate = 1;
+ buildActionMask = 2147483647;
+ files = (
+ );
+ inputPaths = (
+ );
+ name = "Run Script";
+ outputPaths = (
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ shellPath = /bin/sh;
+ shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" build";
+ };
+/* End PBXShellScriptBuildPhase section */
+
+/* Begin PBXSourcesBuildPhase section */
+ 97C146EA1CF9000F007C117D /* Sources */ = {
+ isa = PBXSourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ 74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */,
+ 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+/* End PBXSourcesBuildPhase section */
+
+/* Begin PBXVariantGroup section */
+ 97C146FA1CF9000F007C117D /* Main.storyboard */ = {
+ isa = PBXVariantGroup;
+ children = (
+ 97C146FB1CF9000F007C117D /* Base */,
+ );
+ name = Main.storyboard;
+ sourceTree = "";
+ };
+ 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */ = {
+ isa = PBXVariantGroup;
+ children = (
+ 97C147001CF9000F007C117D /* Base */,
+ );
+ name = LaunchScreen.storyboard;
+ sourceTree = "";
+ };
+/* End PBXVariantGroup section */
+
+/* Begin XCBuildConfiguration section */
+ 249021D3217E4FDB00AE95B9 /* Profile */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ ALWAYS_SEARCH_USER_PATHS = NO;
+ CLANG_ANALYZER_NONNULL = YES;
+ CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
+ CLANG_CXX_LIBRARY = "libc++";
+ CLANG_ENABLE_MODULES = YES;
+ CLANG_ENABLE_OBJC_ARC = YES;
+ CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
+ CLANG_WARN_BOOL_CONVERSION = YES;
+ CLANG_WARN_COMMA = YES;
+ CLANG_WARN_CONSTANT_CONVERSION = YES;
+ CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
+ CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
+ CLANG_WARN_EMPTY_BODY = YES;
+ CLANG_WARN_ENUM_CONVERSION = YES;
+ CLANG_WARN_INFINITE_RECURSION = YES;
+ CLANG_WARN_INT_CONVERSION = YES;
+ CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
+ CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
+ CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
+ CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
+ CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
+ CLANG_WARN_STRICT_PROTOTYPES = YES;
+ CLANG_WARN_SUSPICIOUS_MOVE = YES;
+ CLANG_WARN_UNREACHABLE_CODE = YES;
+ CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
+ "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
+ COPY_PHASE_STRIP = NO;
+ DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
+ ENABLE_NS_ASSERTIONS = NO;
+ ENABLE_STRICT_OBJC_MSGSEND = YES;
+ GCC_C_LANGUAGE_STANDARD = gnu99;
+ GCC_NO_COMMON_BLOCKS = YES;
+ GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
+ GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
+ GCC_WARN_UNDECLARED_SELECTOR = YES;
+ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
+ GCC_WARN_UNUSED_FUNCTION = YES;
+ GCC_WARN_UNUSED_VARIABLE = YES;
+ IPHONEOS_DEPLOYMENT_TARGET = 12.0;
+ MTL_ENABLE_DEBUG_INFO = NO;
+ SDKROOT = iphoneos;
+ SUPPORTED_PLATFORMS = iphoneos;
+ TARGETED_DEVICE_FAMILY = "1,2";
+ VALIDATE_PRODUCT = YES;
+ };
+ name = Profile;
+ };
+ 249021D4217E4FDB00AE95B9 /* Profile */ = {
+ isa = XCBuildConfiguration;
+ baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */;
+ buildSettings = {
+ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
+ CLANG_ENABLE_MODULES = YES;
+ CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
+ DEVELOPMENT_TEAM = 82W3J47LPH;
+ ENABLE_BITCODE = NO;
+ INFOPLIST_FILE = Runner/Info.plist;
+ LD_RUNPATH_SEARCH_PATHS = (
+ "$(inherited)",
+ "@executable_path/Frameworks",
+ );
+ PRODUCT_BUNDLE_IDENTIFIER = com.example.example;
+ PRODUCT_NAME = "$(TARGET_NAME)";
+ SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
+ SWIFT_VERSION = 5.0;
+ VERSIONING_SYSTEM = "apple-generic";
+ };
+ name = Profile;
+ };
+ 97C147031CF9000F007C117D /* Debug */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ ALWAYS_SEARCH_USER_PATHS = NO;
+ CLANG_ANALYZER_NONNULL = YES;
+ CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
+ CLANG_CXX_LIBRARY = "libc++";
+ CLANG_ENABLE_MODULES = YES;
+ CLANG_ENABLE_OBJC_ARC = YES;
+ CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
+ CLANG_WARN_BOOL_CONVERSION = YES;
+ CLANG_WARN_COMMA = YES;
+ CLANG_WARN_CONSTANT_CONVERSION = YES;
+ CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
+ CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
+ CLANG_WARN_EMPTY_BODY = YES;
+ CLANG_WARN_ENUM_CONVERSION = YES;
+ CLANG_WARN_INFINITE_RECURSION = YES;
+ CLANG_WARN_INT_CONVERSION = YES;
+ CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
+ CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
+ CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
+ CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
+ CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
+ CLANG_WARN_STRICT_PROTOTYPES = YES;
+ CLANG_WARN_SUSPICIOUS_MOVE = YES;
+ CLANG_WARN_UNREACHABLE_CODE = YES;
+ CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
+ "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
+ COPY_PHASE_STRIP = NO;
+ DEBUG_INFORMATION_FORMAT = dwarf;
+ ENABLE_STRICT_OBJC_MSGSEND = YES;
+ ENABLE_TESTABILITY = YES;
+ GCC_C_LANGUAGE_STANDARD = gnu99;
+ GCC_DYNAMIC_NO_PIC = NO;
+ GCC_NO_COMMON_BLOCKS = YES;
+ GCC_OPTIMIZATION_LEVEL = 0;
+ GCC_PREPROCESSOR_DEFINITIONS = (
+ "DEBUG=1",
+ "$(inherited)",
+ );
+ GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
+ GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
+ GCC_WARN_UNDECLARED_SELECTOR = YES;
+ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
+ GCC_WARN_UNUSED_FUNCTION = YES;
+ GCC_WARN_UNUSED_VARIABLE = YES;
+ IPHONEOS_DEPLOYMENT_TARGET = 12.0;
+ MTL_ENABLE_DEBUG_INFO = YES;
+ ONLY_ACTIVE_ARCH = YES;
+ SDKROOT = iphoneos;
+ TARGETED_DEVICE_FAMILY = "1,2";
+ };
+ name = Debug;
+ };
+ 97C147041CF9000F007C117D /* Release */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ ALWAYS_SEARCH_USER_PATHS = NO;
+ CLANG_ANALYZER_NONNULL = YES;
+ CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
+ CLANG_CXX_LIBRARY = "libc++";
+ CLANG_ENABLE_MODULES = YES;
+ CLANG_ENABLE_OBJC_ARC = YES;
+ CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
+ CLANG_WARN_BOOL_CONVERSION = YES;
+ CLANG_WARN_COMMA = YES;
+ CLANG_WARN_CONSTANT_CONVERSION = YES;
+ CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
+ CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
+ CLANG_WARN_EMPTY_BODY = YES;
+ CLANG_WARN_ENUM_CONVERSION = YES;
+ CLANG_WARN_INFINITE_RECURSION = YES;
+ CLANG_WARN_INT_CONVERSION = YES;
+ CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
+ CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
+ CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
+ CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
+ CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
+ CLANG_WARN_STRICT_PROTOTYPES = YES;
+ CLANG_WARN_SUSPICIOUS_MOVE = YES;
+ CLANG_WARN_UNREACHABLE_CODE = YES;
+ CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
+ "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
+ COPY_PHASE_STRIP = NO;
+ DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
+ ENABLE_NS_ASSERTIONS = NO;
+ ENABLE_STRICT_OBJC_MSGSEND = YES;
+ GCC_C_LANGUAGE_STANDARD = gnu99;
+ GCC_NO_COMMON_BLOCKS = YES;
+ GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
+ GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
+ GCC_WARN_UNDECLARED_SELECTOR = YES;
+ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
+ GCC_WARN_UNUSED_FUNCTION = YES;
+ GCC_WARN_UNUSED_VARIABLE = YES;
+ IPHONEOS_DEPLOYMENT_TARGET = 12.0;
+ MTL_ENABLE_DEBUG_INFO = NO;
+ SDKROOT = iphoneos;
+ SUPPORTED_PLATFORMS = iphoneos;
+ SWIFT_COMPILATION_MODE = wholemodule;
+ SWIFT_OPTIMIZATION_LEVEL = "-O";
+ TARGETED_DEVICE_FAMILY = "1,2";
+ VALIDATE_PRODUCT = YES;
+ };
+ name = Release;
+ };
+ 97C147061CF9000F007C117D /* Debug */ = {
+ isa = XCBuildConfiguration;
+ baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */;
+ buildSettings = {
+ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
+ CLANG_ENABLE_MODULES = YES;
+ CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
+ DEVELOPMENT_TEAM = 82W3J47LPH;
+ ENABLE_BITCODE = NO;
+ INFOPLIST_FILE = Runner/Info.plist;
+ LD_RUNPATH_SEARCH_PATHS = (
+ "$(inherited)",
+ "@executable_path/Frameworks",
+ );
+ PRODUCT_BUNDLE_IDENTIFIER = com.example.example;
+ PRODUCT_NAME = "$(TARGET_NAME)";
+ SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
+ SWIFT_OPTIMIZATION_LEVEL = "-Onone";
+ SWIFT_VERSION = 5.0;
+ VERSIONING_SYSTEM = "apple-generic";
+ };
+ name = Debug;
+ };
+ 97C147071CF9000F007C117D /* Release */ = {
+ isa = XCBuildConfiguration;
+ baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */;
+ buildSettings = {
+ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
+ CLANG_ENABLE_MODULES = YES;
+ CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
+ DEVELOPMENT_TEAM = 82W3J47LPH;
+ ENABLE_BITCODE = NO;
+ INFOPLIST_FILE = Runner/Info.plist;
+ LD_RUNPATH_SEARCH_PATHS = (
+ "$(inherited)",
+ "@executable_path/Frameworks",
+ );
+ PRODUCT_BUNDLE_IDENTIFIER = com.example.example;
+ PRODUCT_NAME = "$(TARGET_NAME)";
+ SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
+ SWIFT_VERSION = 5.0;
+ VERSIONING_SYSTEM = "apple-generic";
+ };
+ name = Release;
+ };
+/* End XCBuildConfiguration section */
+
+/* Begin XCConfigurationList section */
+ 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ 97C147031CF9000F007C117D /* Debug */,
+ 97C147041CF9000F007C117D /* Release */,
+ 249021D3217E4FDB00AE95B9 /* Profile */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
+ };
+ 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ 97C147061CF9000F007C117D /* Debug */,
+ 97C147071CF9000F007C117D /* Release */,
+ 249021D4217E4FDB00AE95B9 /* Profile */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
+ };
+/* End XCConfigurationList section */
+ };
+ rootObject = 97C146E61CF9000F007C117D /* Project object */;
+}
diff --git a/third_party/pub_patches/fluttertoast/example/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/third_party/pub_patches/fluttertoast/example/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata
new file mode 100644
index 000000000..919434a62
--- /dev/null
+++ b/third_party/pub_patches/fluttertoast/example/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata
@@ -0,0 +1,7 @@
+
+
+
+
+
diff --git a/third_party/pub_patches/fluttertoast/example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/third_party/pub_patches/fluttertoast/example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist
new file mode 100644
index 000000000..18d981003
--- /dev/null
+++ b/third_party/pub_patches/fluttertoast/example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist
@@ -0,0 +1,8 @@
+
+
+
+
+ IDEDidComputeMac32BitWarning
+
+
+
diff --git a/third_party/pub_patches/fluttertoast/example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings b/third_party/pub_patches/fluttertoast/example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings
new file mode 100644
index 000000000..f9b0d7c5e
--- /dev/null
+++ b/third_party/pub_patches/fluttertoast/example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings
@@ -0,0 +1,8 @@
+
+
+
+
+ PreviewsEnabled
+
+
+
diff --git a/third_party/pub_patches/fluttertoast/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme b/third_party/pub_patches/fluttertoast/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme
new file mode 100644
index 000000000..5e31d3d34
--- /dev/null
+++ b/third_party/pub_patches/fluttertoast/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme
@@ -0,0 +1,87 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/third_party/pub_patches/fluttertoast/example/ios/Runner.xcworkspace/contents.xcworkspacedata b/third_party/pub_patches/fluttertoast/example/ios/Runner.xcworkspace/contents.xcworkspacedata
new file mode 100644
index 000000000..21a3cc14c
--- /dev/null
+++ b/third_party/pub_patches/fluttertoast/example/ios/Runner.xcworkspace/contents.xcworkspacedata
@@ -0,0 +1,10 @@
+
+
+
+
+
+
+
diff --git a/third_party/pub_patches/fluttertoast/example/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/third_party/pub_patches/fluttertoast/example/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist
new file mode 100644
index 000000000..18d981003
--- /dev/null
+++ b/third_party/pub_patches/fluttertoast/example/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist
@@ -0,0 +1,8 @@
+
+
+
+
+ IDEDidComputeMac32BitWarning
+
+
+
diff --git a/third_party/pub_patches/fluttertoast/example/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings b/third_party/pub_patches/fluttertoast/example/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings
new file mode 100644
index 000000000..f9b0d7c5e
--- /dev/null
+++ b/third_party/pub_patches/fluttertoast/example/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings
@@ -0,0 +1,8 @@
+
+
+
+
+ PreviewsEnabled
+
+
+
diff --git a/third_party/pub_patches/fluttertoast/example/ios/Runner/AppDelegate.swift b/third_party/pub_patches/fluttertoast/example/ios/Runner/AppDelegate.swift
new file mode 100644
index 000000000..70693e4a8
--- /dev/null
+++ b/third_party/pub_patches/fluttertoast/example/ios/Runner/AppDelegate.swift
@@ -0,0 +1,13 @@
+import UIKit
+import Flutter
+
+@UIApplicationMain
+@objc class AppDelegate: FlutterAppDelegate {
+ override func application(
+ _ application: UIApplication,
+ didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
+ ) -> Bool {
+ GeneratedPluginRegistrant.register(with: self)
+ return super.application(application, didFinishLaunchingWithOptions: launchOptions)
+ }
+}
diff --git a/third_party/pub_patches/fluttertoast/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json b/third_party/pub_patches/fluttertoast/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json
new file mode 100644
index 000000000..d36b1fab2
--- /dev/null
+++ b/third_party/pub_patches/fluttertoast/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json
@@ -0,0 +1,122 @@
+{
+ "images" : [
+ {
+ "size" : "20x20",
+ "idiom" : "iphone",
+ "filename" : "Icon-App-20x20@2x.png",
+ "scale" : "2x"
+ },
+ {
+ "size" : "20x20",
+ "idiom" : "iphone",
+ "filename" : "Icon-App-20x20@3x.png",
+ "scale" : "3x"
+ },
+ {
+ "size" : "29x29",
+ "idiom" : "iphone",
+ "filename" : "Icon-App-29x29@1x.png",
+ "scale" : "1x"
+ },
+ {
+ "size" : "29x29",
+ "idiom" : "iphone",
+ "filename" : "Icon-App-29x29@2x.png",
+ "scale" : "2x"
+ },
+ {
+ "size" : "29x29",
+ "idiom" : "iphone",
+ "filename" : "Icon-App-29x29@3x.png",
+ "scale" : "3x"
+ },
+ {
+ "size" : "40x40",
+ "idiom" : "iphone",
+ "filename" : "Icon-App-40x40@2x.png",
+ "scale" : "2x"
+ },
+ {
+ "size" : "40x40",
+ "idiom" : "iphone",
+ "filename" : "Icon-App-40x40@3x.png",
+ "scale" : "3x"
+ },
+ {
+ "size" : "60x60",
+ "idiom" : "iphone",
+ "filename" : "Icon-App-60x60@2x.png",
+ "scale" : "2x"
+ },
+ {
+ "size" : "60x60",
+ "idiom" : "iphone",
+ "filename" : "Icon-App-60x60@3x.png",
+ "scale" : "3x"
+ },
+ {
+ "size" : "20x20",
+ "idiom" : "ipad",
+ "filename" : "Icon-App-20x20@1x.png",
+ "scale" : "1x"
+ },
+ {
+ "size" : "20x20",
+ "idiom" : "ipad",
+ "filename" : "Icon-App-20x20@2x.png",
+ "scale" : "2x"
+ },
+ {
+ "size" : "29x29",
+ "idiom" : "ipad",
+ "filename" : "Icon-App-29x29@1x.png",
+ "scale" : "1x"
+ },
+ {
+ "size" : "29x29",
+ "idiom" : "ipad",
+ "filename" : "Icon-App-29x29@2x.png",
+ "scale" : "2x"
+ },
+ {
+ "size" : "40x40",
+ "idiom" : "ipad",
+ "filename" : "Icon-App-40x40@1x.png",
+ "scale" : "1x"
+ },
+ {
+ "size" : "40x40",
+ "idiom" : "ipad",
+ "filename" : "Icon-App-40x40@2x.png",
+ "scale" : "2x"
+ },
+ {
+ "size" : "76x76",
+ "idiom" : "ipad",
+ "filename" : "Icon-App-76x76@1x.png",
+ "scale" : "1x"
+ },
+ {
+ "size" : "76x76",
+ "idiom" : "ipad",
+ "filename" : "Icon-App-76x76@2x.png",
+ "scale" : "2x"
+ },
+ {
+ "size" : "83.5x83.5",
+ "idiom" : "ipad",
+ "filename" : "Icon-App-83.5x83.5@2x.png",
+ "scale" : "2x"
+ },
+ {
+ "size" : "1024x1024",
+ "idiom" : "ios-marketing",
+ "filename" : "Icon-App-1024x1024@1x.png",
+ "scale" : "1x"
+ }
+ ],
+ "info" : {
+ "version" : 1,
+ "author" : "xcode"
+ }
+}
diff --git a/third_party/pub_patches/fluttertoast/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png b/third_party/pub_patches/fluttertoast/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png
new file mode 100644
index 000000000..dc9ada472
Binary files /dev/null and b/third_party/pub_patches/fluttertoast/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png differ
diff --git a/third_party/pub_patches/fluttertoast/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png b/third_party/pub_patches/fluttertoast/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png
new file mode 100644
index 000000000..28c6bf030
Binary files /dev/null and b/third_party/pub_patches/fluttertoast/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png differ
diff --git a/third_party/pub_patches/fluttertoast/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png b/third_party/pub_patches/fluttertoast/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png
new file mode 100644
index 000000000..2ccbfd967
Binary files /dev/null and b/third_party/pub_patches/fluttertoast/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png differ
diff --git a/third_party/pub_patches/fluttertoast/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png b/third_party/pub_patches/fluttertoast/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png
new file mode 100644
index 000000000..f091b6b0b
Binary files /dev/null and b/third_party/pub_patches/fluttertoast/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png differ
diff --git a/third_party/pub_patches/fluttertoast/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png b/third_party/pub_patches/fluttertoast/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png
new file mode 100644
index 000000000..4cde12118
Binary files /dev/null and b/third_party/pub_patches/fluttertoast/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png differ
diff --git a/third_party/pub_patches/fluttertoast/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png b/third_party/pub_patches/fluttertoast/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png
new file mode 100644
index 000000000..d0ef06e7e
Binary files /dev/null and b/third_party/pub_patches/fluttertoast/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png differ
diff --git a/third_party/pub_patches/fluttertoast/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png b/third_party/pub_patches/fluttertoast/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png
new file mode 100644
index 000000000..dcdc2306c
Binary files /dev/null and b/third_party/pub_patches/fluttertoast/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png differ
diff --git a/third_party/pub_patches/fluttertoast/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png b/third_party/pub_patches/fluttertoast/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png
new file mode 100644
index 000000000..2ccbfd967
Binary files /dev/null and b/third_party/pub_patches/fluttertoast/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png differ
diff --git a/third_party/pub_patches/fluttertoast/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png b/third_party/pub_patches/fluttertoast/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png
new file mode 100644
index 000000000..c8f9ed8f5
Binary files /dev/null and b/third_party/pub_patches/fluttertoast/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png differ
diff --git a/third_party/pub_patches/fluttertoast/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png b/third_party/pub_patches/fluttertoast/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png
new file mode 100644
index 000000000..a6d6b8609
Binary files /dev/null and b/third_party/pub_patches/fluttertoast/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png differ
diff --git a/third_party/pub_patches/fluttertoast/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png b/third_party/pub_patches/fluttertoast/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png
new file mode 100644
index 000000000..a6d6b8609
Binary files /dev/null and b/third_party/pub_patches/fluttertoast/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png differ
diff --git a/third_party/pub_patches/fluttertoast/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png b/third_party/pub_patches/fluttertoast/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png
new file mode 100644
index 000000000..75b2d164a
Binary files /dev/null and b/third_party/pub_patches/fluttertoast/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png differ
diff --git a/third_party/pub_patches/fluttertoast/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png b/third_party/pub_patches/fluttertoast/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png
new file mode 100644
index 000000000..c4df70d39
Binary files /dev/null and b/third_party/pub_patches/fluttertoast/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png differ
diff --git a/third_party/pub_patches/fluttertoast/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png b/third_party/pub_patches/fluttertoast/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png
new file mode 100644
index 000000000..6a84f41e1
Binary files /dev/null and b/third_party/pub_patches/fluttertoast/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png differ
diff --git a/third_party/pub_patches/fluttertoast/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png b/third_party/pub_patches/fluttertoast/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png
new file mode 100644
index 000000000..d0e1f5853
Binary files /dev/null and b/third_party/pub_patches/fluttertoast/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png differ
diff --git a/third_party/pub_patches/fluttertoast/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json b/third_party/pub_patches/fluttertoast/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json
new file mode 100644
index 000000000..0bedcf2fd
--- /dev/null
+++ b/third_party/pub_patches/fluttertoast/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json
@@ -0,0 +1,23 @@
+{
+ "images" : [
+ {
+ "idiom" : "universal",
+ "filename" : "LaunchImage.png",
+ "scale" : "1x"
+ },
+ {
+ "idiom" : "universal",
+ "filename" : "LaunchImage@2x.png",
+ "scale" : "2x"
+ },
+ {
+ "idiom" : "universal",
+ "filename" : "LaunchImage@3x.png",
+ "scale" : "3x"
+ }
+ ],
+ "info" : {
+ "version" : 1,
+ "author" : "xcode"
+ }
+}
diff --git a/third_party/pub_patches/fluttertoast/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png b/third_party/pub_patches/fluttertoast/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png
new file mode 100644
index 000000000..9da19eaca
Binary files /dev/null and b/third_party/pub_patches/fluttertoast/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png differ
diff --git a/third_party/pub_patches/fluttertoast/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png b/third_party/pub_patches/fluttertoast/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png
new file mode 100644
index 000000000..9da19eaca
Binary files /dev/null and b/third_party/pub_patches/fluttertoast/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png differ
diff --git a/third_party/pub_patches/fluttertoast/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png b/third_party/pub_patches/fluttertoast/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png
new file mode 100644
index 000000000..9da19eaca
Binary files /dev/null and b/third_party/pub_patches/fluttertoast/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png differ
diff --git a/third_party/pub_patches/fluttertoast/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md b/third_party/pub_patches/fluttertoast/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md
new file mode 100644
index 000000000..89c2725b7
--- /dev/null
+++ b/third_party/pub_patches/fluttertoast/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md
@@ -0,0 +1,5 @@
+# Launch Screen Assets
+
+You can customize the launch screen with your own desired assets by replacing the image files in this directory.
+
+You can also do it by opening your Flutter project's Xcode project with `open ios/Runner.xcworkspace`, selecting `Runner/Assets.xcassets` in the Project Navigator and dropping in the desired images.
\ No newline at end of file
diff --git a/third_party/pub_patches/fluttertoast/example/ios/Runner/Base.lproj/LaunchScreen.storyboard b/third_party/pub_patches/fluttertoast/example/ios/Runner/Base.lproj/LaunchScreen.storyboard
new file mode 100644
index 000000000..f2e259c7c
--- /dev/null
+++ b/third_party/pub_patches/fluttertoast/example/ios/Runner/Base.lproj/LaunchScreen.storyboard
@@ -0,0 +1,37 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/third_party/pub_patches/fluttertoast/example/ios/Runner/Base.lproj/Main.storyboard b/third_party/pub_patches/fluttertoast/example/ios/Runner/Base.lproj/Main.storyboard
new file mode 100644
index 000000000..f3c28516f
--- /dev/null
+++ b/third_party/pub_patches/fluttertoast/example/ios/Runner/Base.lproj/Main.storyboard
@@ -0,0 +1,26 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/third_party/pub_patches/fluttertoast/example/ios/Runner/Info.plist b/third_party/pub_patches/fluttertoast/example/ios/Runner/Info.plist
new file mode 100644
index 000000000..7f553465b
--- /dev/null
+++ b/third_party/pub_patches/fluttertoast/example/ios/Runner/Info.plist
@@ -0,0 +1,51 @@
+
+
+
+
+ CFBundleDevelopmentRegion
+ $(DEVELOPMENT_LANGUAGE)
+ CFBundleDisplayName
+ Example
+ CFBundleExecutable
+ $(EXECUTABLE_NAME)
+ CFBundleIdentifier
+ $(PRODUCT_BUNDLE_IDENTIFIER)
+ CFBundleInfoDictionaryVersion
+ 6.0
+ CFBundleName
+ example
+ CFBundlePackageType
+ APPL
+ CFBundleShortVersionString
+ $(FLUTTER_BUILD_NAME)
+ CFBundleSignature
+ ????
+ CFBundleVersion
+ $(FLUTTER_BUILD_NUMBER)
+ LSRequiresIPhoneOS
+
+ UILaunchStoryboardName
+ LaunchScreen
+ UIMainStoryboardFile
+ Main
+ UISupportedInterfaceOrientations
+
+ UIInterfaceOrientationPortrait
+ UIInterfaceOrientationLandscapeLeft
+ UIInterfaceOrientationLandscapeRight
+
+ UISupportedInterfaceOrientations~ipad
+
+ UIInterfaceOrientationPortrait
+ UIInterfaceOrientationPortraitUpsideDown
+ UIInterfaceOrientationLandscapeLeft
+ UIInterfaceOrientationLandscapeRight
+
+ UIViewControllerBasedStatusBarAppearance
+
+ CADisableMinimumFrameDurationOnPhone
+
+ UIApplicationSupportsIndirectInputEvents
+
+
+
diff --git a/third_party/pub_patches/fluttertoast/example/ios/Runner/Runner-Bridging-Header.h b/third_party/pub_patches/fluttertoast/example/ios/Runner/Runner-Bridging-Header.h
new file mode 100644
index 000000000..308a2a560
--- /dev/null
+++ b/third_party/pub_patches/fluttertoast/example/ios/Runner/Runner-Bridging-Header.h
@@ -0,0 +1 @@
+#import "GeneratedPluginRegistrant.h"
diff --git a/third_party/pub_patches/fluttertoast/example/lib/main.dart b/third_party/pub_patches/fluttertoast/example/lib/main.dart
new file mode 100644
index 000000000..f4f99cb12
--- /dev/null
+++ b/third_party/pub_patches/fluttertoast/example/lib/main.dart
@@ -0,0 +1,56 @@
+import 'package:fluttertoast_example/toast_context.dart';
+import 'package:fluttertoast_example/toast_no_context.dart';
+import 'package:fluttertoast/fluttertoast.dart';
+import 'package:flutter/material.dart';
+
+GlobalKey navigatorKey = GlobalKey();
+
+void main() => runApp(
+ MaterialApp(
+ builder: FToastBuilder(),
+ home: MyApp(),
+ navigatorKey: navigatorKey,
+ ),
+ );
+
+class MyApp extends StatefulWidget {
+ @override
+ _MyAppState createState() => _MyAppState();
+}
+
+class _MyAppState extends State {
+ @override
+ Widget build(BuildContext context) {
+ return Scaffold(
+ appBar: AppBar(
+ title: Text("Toast"),
+ ),
+ body: Center(
+ child: Column(
+ mainAxisAlignment: MainAxisAlignment.center,
+ children: [
+ ElevatedButton(
+ onPressed: () {
+ Navigator.of(context).push(MaterialPageRoute(
+ builder: (context) => ToastNoContext(),
+ ));
+ },
+ child: Text("Flutter Toast No Context"),
+ ),
+ SizedBox(
+ height: 24.0,
+ ),
+ ElevatedButton(
+ onPressed: () {
+ Navigator.of(context).push(MaterialPageRoute(
+ builder: (context) => ToastContext(),
+ ));
+ },
+ child: Text("Flutter Toast Context"),
+ ),
+ ],
+ ),
+ ),
+ );
+ }
+}
diff --git a/third_party/pub_patches/fluttertoast/example/lib/toast_context.dart b/third_party/pub_patches/fluttertoast/example/lib/toast_context.dart
new file mode 100644
index 000000000..7b78623ce
--- /dev/null
+++ b/third_party/pub_patches/fluttertoast/example/lib/toast_context.dart
@@ -0,0 +1,199 @@
+import 'package:fluttertoast_example/main.dart';
+import 'package:flutter/material.dart';
+import 'package:fluttertoast/fluttertoast.dart';
+
+class ToastContext extends StatefulWidget {
+ @override
+ _ToastContextState createState() => _ToastContextState();
+}
+
+class _ToastContextState extends State {
+ late FToast fToast;
+
+ Widget toast = Container(
+ padding: const EdgeInsets.symmetric(horizontal: 24.0, vertical: 12.0),
+ decoration: BoxDecoration(
+ borderRadius: BorderRadius.circular(25.0),
+ color: Colors.greenAccent,
+ ),
+ child: Row(
+ mainAxisSize: MainAxisSize.min,
+ children: [
+ Icon(Icons.check),
+ SizedBox(
+ width: 12.0,
+ ),
+ Text("This is a Custom Toast"),
+ ],
+ ),
+ );
+
+ _showToast() {
+ fToast.showToast(
+ child: toast,
+ gravity: ToastGravity.BOTTOM,
+ toastDuration: Duration(seconds: 2),
+ );
+ }
+
+ _showBuilderToast() {
+ fToast.showToast(
+ child: toast,
+ gravity: ToastGravity.BOTTOM,
+ toastDuration: Duration(seconds: 2),
+ positionedToastBuilder: (context, child) {
+ return Positioned(
+ child: child,
+ top: 16.0,
+ left: 16.0,
+ );
+ });
+ }
+
+ _showToastCancel() {
+ Widget toastWithButton = Container(
+ padding: const EdgeInsets.symmetric(horizontal: 24.0, vertical: 12.0),
+ decoration: BoxDecoration(
+ borderRadius: BorderRadius.circular(25.0),
+ color: Colors.redAccent,
+ ),
+ child: Row(
+ mainAxisSize: MainAxisSize.min,
+ children: [
+ Expanded(
+ child: Text(
+ "This is a Custom Toast This is a Custom Toast This is a Custom Toast This is a Custom Toast This is a Custom Toast This is a Custom Toast",
+ softWrap: true,
+ style: TextStyle(
+ color: Colors.white,
+ ),
+ ),
+ ),
+ IconButton(
+ icon: Icon(
+ Icons.close,
+ ),
+ color: Colors.white,
+ onPressed: () {
+ fToast.removeCustomToast();
+ },
+ )
+ ],
+ ),
+ );
+ fToast.showToast(
+ child: toastWithButton,
+ gravity: ToastGravity.CENTER,
+ toastDuration: Duration(seconds: 50),
+ );
+ }
+
+ _queueToasts() {
+ fToast.showToast(
+ child: toast,
+ gravity: ToastGravity.CENTER,
+ toastDuration: Duration(seconds: 2),
+ );
+ fToast.showToast(
+ child: toast,
+ gravity: ToastGravity.BOTTOM,
+ toastDuration: Duration(seconds: 2),
+ );
+ fToast.showToast(
+ child: toast,
+ gravity: ToastGravity.TOP,
+ toastDuration: Duration(seconds: 2),
+ );
+ fToast.showToast(
+ child: toast,
+ gravity: ToastGravity.CENTER,
+ toastDuration: Duration(seconds: 2),
+ );
+ fToast.showToast(
+ child: toast,
+ gravity: ToastGravity.TOP,
+ toastDuration: Duration(seconds: 2),
+ );
+ }
+
+ _removeToast() {
+ fToast.removeCustomToast();
+ }
+
+ _removeAllQueuedToasts() {
+ fToast.removeQueuedCustomToasts();
+ }
+
+ @override
+ void initState() {
+ super.initState();
+ fToast = FToast();
+ fToast.init(navigatorKey.currentContext!);
+ }
+
+ @override
+ Widget build(BuildContext context) {
+ return Scaffold(
+ appBar: AppBar(
+ title: Text("Custom Toasts"),
+ ),
+ body: Center(
+ child: Column(
+ children: [
+ SizedBox(
+ height: 24.0,
+ ),
+ ElevatedButton(
+ child: Text("Show Custom Toast"),
+ onPressed: () {
+ _showToast();
+ },
+ ),
+ ElevatedButton(
+ child: Text("Show Custom Toast via PositionedToastBuilder"),
+ onPressed: () {
+ _showBuilderToast();
+ },
+ ),
+ SizedBox(
+ height: 24.0,
+ ),
+ ElevatedButton(
+ child: Text("Custom Toast With Close Button"),
+ onPressed: () {
+ _showToastCancel();
+ },
+ ),
+ SizedBox(
+ height: 24.0,
+ ),
+ ElevatedButton(
+ child: Text("Queue Toasts"),
+ onPressed: () {
+ _queueToasts();
+ },
+ ),
+ SizedBox(
+ height: 24.0,
+ ),
+ ElevatedButton(
+ child: Text("Cancel Toast"),
+ onPressed: () {
+ _removeToast();
+ },
+ ),
+ SizedBox(
+ height: 24.0,
+ ),
+ ElevatedButton(
+ child: Text("Remove Queued Toasts"),
+ onPressed: () {
+ _removeAllQueuedToasts();
+ },
+ ),
+ ],
+ ),
+ ),
+ );
+ }
+}
diff --git a/third_party/pub_patches/fluttertoast/example/lib/toast_no_context.dart b/third_party/pub_patches/fluttertoast/example/lib/toast_no_context.dart
new file mode 100644
index 000000000..1c368fd97
--- /dev/null
+++ b/third_party/pub_patches/fluttertoast/example/lib/toast_no_context.dart
@@ -0,0 +1,127 @@
+import 'package:flutter/material.dart';
+import 'package:fluttertoast/fluttertoast.dart';
+
+class ToastNoContext extends StatefulWidget {
+ @override
+ _ToastNoContextState createState() => _ToastNoContextState();
+}
+
+class _ToastNoContextState extends State {
+ void showLongToast() {
+ Fluttertoast.showToast(
+ msg: "This is Long Toast",
+ toastLength: Toast.LENGTH_LONG,
+ fontSize: 18.0,
+ );
+ }
+
+ void showWebColoredToast() {
+ Fluttertoast.showToast(
+ msg: "This is Colored Toast with android duration of 5 Sec",
+ toastLength: Toast.LENGTH_SHORT,
+ webBgColor: "#e74c3c",
+ textColor: Colors.black,
+ timeInSecForIosWeb: 5,
+ );
+ }
+
+ void showColoredToast() {
+ Fluttertoast.showToast(
+ msg: "This is Colored Toast with android of short",
+ toastLength: Toast.LENGTH_SHORT,
+ backgroundColor: Colors.red,
+ textColor: Colors.white);
+ }
+
+ void showShortToast() {
+ Fluttertoast.showToast(
+ msg: "This is Short Toast",
+ toastLength: Toast.LENGTH_SHORT,
+ timeInSecForIosWeb: 1);
+ }
+
+ void showTopShortToast() {
+ Fluttertoast.showToast(
+ msg: "This is Top Short Toast",
+ toastLength: Toast.LENGTH_SHORT,
+ gravity: ToastGravity.TOP,
+ timeInSecForIosWeb: 1);
+ }
+
+ void showCenterShortToast() {
+ Fluttertoast.showToast(
+ msg: "This is Center Short Toast",
+ toastLength: Toast.LENGTH_SHORT,
+ gravity: ToastGravity.CENTER,
+ timeInSecForIosWeb: 1);
+ }
+
+ void cancelToast() {
+ Fluttertoast.cancel();
+ }
+
+ @override
+ void initState() {
+ super.initState();
+ }
+
+ @override
+ Widget build(BuildContext context) {
+ return new MaterialApp(
+ home: new Scaffold(
+ appBar: new AppBar(
+ title: new Text('Flutter Toast'),
+ ),
+ body: new Center(
+ child: new Column(
+ children: [
+ new Padding(
+ padding: const EdgeInsets.all(10.0),
+ child: new ElevatedButton(
+ child: new Text('Show Long Toast'),
+ onPressed: showLongToast),
+ ),
+ new Padding(
+ padding: const EdgeInsets.all(10.0),
+ child: new ElevatedButton(
+ child: new Text('Show Short Toast'),
+ onPressed: showShortToast),
+ ),
+ new Padding(
+ padding: const EdgeInsets.all(10.0),
+ child: new ElevatedButton(
+ child: new Text('Show Center Short Toast'),
+ onPressed: showCenterShortToast),
+ ),
+ new Padding(
+ padding: const EdgeInsets.all(10.0),
+ child: new ElevatedButton(
+ child: new Text('Show Top Short Toast'),
+ onPressed: showTopShortToast),
+ ),
+ new Padding(
+ padding: const EdgeInsets.all(10.0),
+ child: new ElevatedButton(
+ child: new Text('Show Colored Toast'),
+ onPressed: showColoredToast),
+ ),
+ new Padding(
+ padding: const EdgeInsets.all(10.0),
+ child: new ElevatedButton(
+ child: new Text('Show Web Colored Toast'),
+ onPressed: showWebColoredToast),
+ ),
+ new Padding(
+ padding: const EdgeInsets.all(10.0),
+ child: new ElevatedButton(
+ child: new Text('Cancel Toasts'),
+ onPressed: cancelToast,
+ ),
+ ),
+ ],
+ ),
+ ),
+ ),
+ );
+ }
+}
diff --git a/third_party/pub_patches/fluttertoast/example/linux/CMakeLists.txt b/third_party/pub_patches/fluttertoast/example/linux/CMakeLists.txt
new file mode 100644
index 000000000..74c66dd44
--- /dev/null
+++ b/third_party/pub_patches/fluttertoast/example/linux/CMakeLists.txt
@@ -0,0 +1,138 @@
+# Project-level configuration.
+cmake_minimum_required(VERSION 3.10)
+project(runner LANGUAGES CXX)
+
+# The name of the executable created for the application. Change this to change
+# the on-disk name of your application.
+set(BINARY_NAME "example")
+# The unique GTK application identifier for this application. See:
+# https://wiki.gnome.org/HowDoI/ChooseApplicationID
+set(APPLICATION_ID "com.example.example")
+
+# Explicitly opt in to modern CMake behaviors to avoid warnings with recent
+# versions of CMake.
+cmake_policy(SET CMP0063 NEW)
+
+# Load bundled libraries from the lib/ directory relative to the binary.
+set(CMAKE_INSTALL_RPATH "$ORIGIN/lib")
+
+# Root filesystem for cross-building.
+if(FLUTTER_TARGET_PLATFORM_SYSROOT)
+ set(CMAKE_SYSROOT ${FLUTTER_TARGET_PLATFORM_SYSROOT})
+ set(CMAKE_FIND_ROOT_PATH ${CMAKE_SYSROOT})
+ set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER)
+ set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE ONLY)
+ set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY)
+ set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY)
+endif()
+
+# Define build configuration options.
+if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES)
+ set(CMAKE_BUILD_TYPE "Debug" CACHE
+ STRING "Flutter build mode" FORCE)
+ set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS
+ "Debug" "Profile" "Release")
+endif()
+
+# Compilation settings that should be applied to most targets.
+#
+# Be cautious about adding new options here, as plugins use this function by
+# default. In most cases, you should add new options to specific targets instead
+# of modifying this function.
+function(APPLY_STANDARD_SETTINGS TARGET)
+ target_compile_features(${TARGET} PUBLIC cxx_std_14)
+ target_compile_options(${TARGET} PRIVATE -Wall -Werror)
+ target_compile_options(${TARGET} PRIVATE "$<$>:-O3>")
+ target_compile_definitions(${TARGET} PRIVATE "$<$>:NDEBUG>")
+endfunction()
+
+# Flutter library and tool build rules.
+set(FLUTTER_MANAGED_DIR "${CMAKE_CURRENT_SOURCE_DIR}/flutter")
+add_subdirectory(${FLUTTER_MANAGED_DIR})
+
+# System-level dependencies.
+find_package(PkgConfig REQUIRED)
+pkg_check_modules(GTK REQUIRED IMPORTED_TARGET gtk+-3.0)
+
+add_definitions(-DAPPLICATION_ID="${APPLICATION_ID}")
+
+# Define the application target. To change its name, change BINARY_NAME above,
+# not the value here, or `flutter run` will no longer work.
+#
+# Any new source files that you add to the application should be added here.
+add_executable(${BINARY_NAME}
+ "main.cc"
+ "my_application.cc"
+ "${FLUTTER_MANAGED_DIR}/generated_plugin_registrant.cc"
+)
+
+# Apply the standard set of build settings. This can be removed for applications
+# that need different build settings.
+apply_standard_settings(${BINARY_NAME})
+
+# Add dependency libraries. Add any application-specific dependencies here.
+target_link_libraries(${BINARY_NAME} PRIVATE flutter)
+target_link_libraries(${BINARY_NAME} PRIVATE PkgConfig::GTK)
+
+# Run the Flutter tool portions of the build. This must not be removed.
+add_dependencies(${BINARY_NAME} flutter_assemble)
+
+# Only the install-generated bundle's copy of the executable will launch
+# correctly, since the resources must in the right relative locations. To avoid
+# people trying to run the unbundled copy, put it in a subdirectory instead of
+# the default top-level location.
+set_target_properties(${BINARY_NAME}
+ PROPERTIES
+ RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/intermediates_do_not_run"
+)
+
+# Generated plugin build rules, which manage building the plugins and adding
+# them to the application.
+include(flutter/generated_plugins.cmake)
+
+
+# === Installation ===
+# By default, "installing" just makes a relocatable bundle in the build
+# directory.
+set(BUILD_BUNDLE_DIR "${PROJECT_BINARY_DIR}/bundle")
+if(CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT)
+ set(CMAKE_INSTALL_PREFIX "${BUILD_BUNDLE_DIR}" CACHE PATH "..." FORCE)
+endif()
+
+# Start with a clean build bundle directory every time.
+install(CODE "
+ file(REMOVE_RECURSE \"${BUILD_BUNDLE_DIR}/\")
+ " COMPONENT Runtime)
+
+set(INSTALL_BUNDLE_DATA_DIR "${CMAKE_INSTALL_PREFIX}/data")
+set(INSTALL_BUNDLE_LIB_DIR "${CMAKE_INSTALL_PREFIX}/lib")
+
+install(TARGETS ${BINARY_NAME} RUNTIME DESTINATION "${CMAKE_INSTALL_PREFIX}"
+ COMPONENT Runtime)
+
+install(FILES "${FLUTTER_ICU_DATA_FILE}" DESTINATION "${INSTALL_BUNDLE_DATA_DIR}"
+ COMPONENT Runtime)
+
+install(FILES "${FLUTTER_LIBRARY}" DESTINATION "${INSTALL_BUNDLE_LIB_DIR}"
+ COMPONENT Runtime)
+
+foreach(bundled_library ${PLUGIN_BUNDLED_LIBRARIES})
+ install(FILES "${bundled_library}"
+ DESTINATION "${INSTALL_BUNDLE_LIB_DIR}"
+ COMPONENT Runtime)
+endforeach(bundled_library)
+
+# Fully re-copy the assets directory on each build to avoid having stale files
+# from a previous install.
+set(FLUTTER_ASSET_DIR_NAME "flutter_assets")
+install(CODE "
+ file(REMOVE_RECURSE \"${INSTALL_BUNDLE_DATA_DIR}/${FLUTTER_ASSET_DIR_NAME}\")
+ " COMPONENT Runtime)
+install(DIRECTORY "${PROJECT_BUILD_DIR}/${FLUTTER_ASSET_DIR_NAME}"
+ DESTINATION "${INSTALL_BUNDLE_DATA_DIR}" COMPONENT Runtime)
+
+# Install the AOT library on non-Debug builds only.
+if(NOT CMAKE_BUILD_TYPE MATCHES "Debug")
+ install(FILES "${AOT_LIBRARY}" DESTINATION "${INSTALL_BUNDLE_LIB_DIR}"
+ COMPONENT Runtime)
+endif()
diff --git a/third_party/pub_patches/fluttertoast/example/linux/flutter/CMakeLists.txt b/third_party/pub_patches/fluttertoast/example/linux/flutter/CMakeLists.txt
new file mode 100644
index 000000000..d5bd01648
--- /dev/null
+++ b/third_party/pub_patches/fluttertoast/example/linux/flutter/CMakeLists.txt
@@ -0,0 +1,88 @@
+# This file controls Flutter-level build steps. It should not be edited.
+cmake_minimum_required(VERSION 3.10)
+
+set(EPHEMERAL_DIR "${CMAKE_CURRENT_SOURCE_DIR}/ephemeral")
+
+# Configuration provided via flutter tool.
+include(${EPHEMERAL_DIR}/generated_config.cmake)
+
+# TODO: Move the rest of this into files in ephemeral. See
+# https://github.com/flutter/flutter/issues/57146.
+
+# Serves the same purpose as list(TRANSFORM ... PREPEND ...),
+# which isn't available in 3.10.
+function(list_prepend LIST_NAME PREFIX)
+ set(NEW_LIST "")
+ foreach(element ${${LIST_NAME}})
+ list(APPEND NEW_LIST "${PREFIX}${element}")
+ endforeach(element)
+ set(${LIST_NAME} "${NEW_LIST}" PARENT_SCOPE)
+endfunction()
+
+# === Flutter Library ===
+# System-level dependencies.
+find_package(PkgConfig REQUIRED)
+pkg_check_modules(GTK REQUIRED IMPORTED_TARGET gtk+-3.0)
+pkg_check_modules(GLIB REQUIRED IMPORTED_TARGET glib-2.0)
+pkg_check_modules(GIO REQUIRED IMPORTED_TARGET gio-2.0)
+
+set(FLUTTER_LIBRARY "${EPHEMERAL_DIR}/libflutter_linux_gtk.so")
+
+# Published to parent scope for install step.
+set(FLUTTER_LIBRARY ${FLUTTER_LIBRARY} PARENT_SCOPE)
+set(FLUTTER_ICU_DATA_FILE "${EPHEMERAL_DIR}/icudtl.dat" PARENT_SCOPE)
+set(PROJECT_BUILD_DIR "${PROJECT_DIR}/build/" PARENT_SCOPE)
+set(AOT_LIBRARY "${PROJECT_DIR}/build/lib/libapp.so" PARENT_SCOPE)
+
+list(APPEND FLUTTER_LIBRARY_HEADERS
+ "fl_basic_message_channel.h"
+ "fl_binary_codec.h"
+ "fl_binary_messenger.h"
+ "fl_dart_project.h"
+ "fl_engine.h"
+ "fl_json_message_codec.h"
+ "fl_json_method_codec.h"
+ "fl_message_codec.h"
+ "fl_method_call.h"
+ "fl_method_channel.h"
+ "fl_method_codec.h"
+ "fl_method_response.h"
+ "fl_plugin_registrar.h"
+ "fl_plugin_registry.h"
+ "fl_standard_message_codec.h"
+ "fl_standard_method_codec.h"
+ "fl_string_codec.h"
+ "fl_value.h"
+ "fl_view.h"
+ "flutter_linux.h"
+)
+list_prepend(FLUTTER_LIBRARY_HEADERS "${EPHEMERAL_DIR}/flutter_linux/")
+add_library(flutter INTERFACE)
+target_include_directories(flutter INTERFACE
+ "${EPHEMERAL_DIR}"
+)
+target_link_libraries(flutter INTERFACE "${FLUTTER_LIBRARY}")
+target_link_libraries(flutter INTERFACE
+ PkgConfig::GTK
+ PkgConfig::GLIB
+ PkgConfig::GIO
+)
+add_dependencies(flutter flutter_assemble)
+
+# === Flutter tool backend ===
+# _phony_ is a non-existent file to force this command to run every time,
+# since currently there's no way to get a full input/output list from the
+# flutter tool.
+add_custom_command(
+ OUTPUT ${FLUTTER_LIBRARY} ${FLUTTER_LIBRARY_HEADERS}
+ ${CMAKE_CURRENT_BINARY_DIR}/_phony_
+ COMMAND ${CMAKE_COMMAND} -E env
+ ${FLUTTER_TOOL_ENVIRONMENT}
+ "${FLUTTER_ROOT}/packages/flutter_tools/bin/tool_backend.sh"
+ ${FLUTTER_TARGET_PLATFORM} ${CMAKE_BUILD_TYPE}
+ VERBATIM
+)
+add_custom_target(flutter_assemble DEPENDS
+ "${FLUTTER_LIBRARY}"
+ ${FLUTTER_LIBRARY_HEADERS}
+)
diff --git a/third_party/pub_patches/fluttertoast/example/linux/flutter/generated_plugin_registrant.cc b/third_party/pub_patches/fluttertoast/example/linux/flutter/generated_plugin_registrant.cc
new file mode 100644
index 000000000..e71a16d23
--- /dev/null
+++ b/third_party/pub_patches/fluttertoast/example/linux/flutter/generated_plugin_registrant.cc
@@ -0,0 +1,11 @@
+//
+// Generated file. Do not edit.
+//
+
+// clang-format off
+
+#include "generated_plugin_registrant.h"
+
+
+void fl_register_plugins(FlPluginRegistry* registry) {
+}
diff --git a/third_party/pub_patches/fluttertoast/example/linux/flutter/generated_plugin_registrant.h b/third_party/pub_patches/fluttertoast/example/linux/flutter/generated_plugin_registrant.h
new file mode 100644
index 000000000..e0f0a47bc
--- /dev/null
+++ b/third_party/pub_patches/fluttertoast/example/linux/flutter/generated_plugin_registrant.h
@@ -0,0 +1,15 @@
+//
+// Generated file. Do not edit.
+//
+
+// clang-format off
+
+#ifndef GENERATED_PLUGIN_REGISTRANT_
+#define GENERATED_PLUGIN_REGISTRANT_
+
+#include
+
+// Registers Flutter plugins.
+void fl_register_plugins(FlPluginRegistry* registry);
+
+#endif // GENERATED_PLUGIN_REGISTRANT_
diff --git a/third_party/pub_patches/fluttertoast/example/linux/flutter/generated_plugins.cmake b/third_party/pub_patches/fluttertoast/example/linux/flutter/generated_plugins.cmake
new file mode 100644
index 000000000..2e1de87a7
--- /dev/null
+++ b/third_party/pub_patches/fluttertoast/example/linux/flutter/generated_plugins.cmake
@@ -0,0 +1,23 @@
+#
+# Generated file, do not edit.
+#
+
+list(APPEND FLUTTER_PLUGIN_LIST
+)
+
+list(APPEND FLUTTER_FFI_PLUGIN_LIST
+)
+
+set(PLUGIN_BUNDLED_LIBRARIES)
+
+foreach(plugin ${FLUTTER_PLUGIN_LIST})
+ add_subdirectory(flutter/ephemeral/.plugin_symlinks/${plugin}/linux plugins/${plugin})
+ target_link_libraries(${BINARY_NAME} PRIVATE ${plugin}_plugin)
+ list(APPEND PLUGIN_BUNDLED_LIBRARIES $)
+ list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${plugin}_bundled_libraries})
+endforeach(plugin)
+
+foreach(ffi_plugin ${FLUTTER_FFI_PLUGIN_LIST})
+ add_subdirectory(flutter/ephemeral/.plugin_symlinks/${ffi_plugin}/linux plugins/${ffi_plugin})
+ list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${ffi_plugin}_bundled_libraries})
+endforeach(ffi_plugin)
diff --git a/third_party/pub_patches/fluttertoast/example/linux/main.cc b/third_party/pub_patches/fluttertoast/example/linux/main.cc
new file mode 100644
index 000000000..e7c5c5437
--- /dev/null
+++ b/third_party/pub_patches/fluttertoast/example/linux/main.cc
@@ -0,0 +1,6 @@
+#include "my_application.h"
+
+int main(int argc, char** argv) {
+ g_autoptr(MyApplication) app = my_application_new();
+ return g_application_run(G_APPLICATION(app), argc, argv);
+}
diff --git a/third_party/pub_patches/fluttertoast/example/linux/my_application.cc b/third_party/pub_patches/fluttertoast/example/linux/my_application.cc
new file mode 100644
index 000000000..0ba8f4309
--- /dev/null
+++ b/third_party/pub_patches/fluttertoast/example/linux/my_application.cc
@@ -0,0 +1,104 @@
+#include "my_application.h"
+
+#include
+#ifdef GDK_WINDOWING_X11
+#include
+#endif
+
+#include "flutter/generated_plugin_registrant.h"
+
+struct _MyApplication {
+ GtkApplication parent_instance;
+ char** dart_entrypoint_arguments;
+};
+
+G_DEFINE_TYPE(MyApplication, my_application, GTK_TYPE_APPLICATION)
+
+// Implements GApplication::activate.
+static void my_application_activate(GApplication* application) {
+ MyApplication* self = MY_APPLICATION(application);
+ GtkWindow* window =
+ GTK_WINDOW(gtk_application_window_new(GTK_APPLICATION(application)));
+
+ // Use a header bar when running in GNOME as this is the common style used
+ // by applications and is the setup most users will be using (e.g. Ubuntu
+ // desktop).
+ // If running on X and not using GNOME then just use a traditional title bar
+ // in case the window manager does more exotic layout, e.g. tiling.
+ // If running on Wayland assume the header bar will work (may need changing
+ // if future cases occur).
+ gboolean use_header_bar = TRUE;
+#ifdef GDK_WINDOWING_X11
+ GdkScreen* screen = gtk_window_get_screen(window);
+ if (GDK_IS_X11_SCREEN(screen)) {
+ const gchar* wm_name = gdk_x11_screen_get_window_manager_name(screen);
+ if (g_strcmp0(wm_name, "GNOME Shell") != 0) {
+ use_header_bar = FALSE;
+ }
+ }
+#endif
+ if (use_header_bar) {
+ GtkHeaderBar* header_bar = GTK_HEADER_BAR(gtk_header_bar_new());
+ gtk_widget_show(GTK_WIDGET(header_bar));
+ gtk_header_bar_set_title(header_bar, "example");
+ gtk_header_bar_set_show_close_button(header_bar, TRUE);
+ gtk_window_set_titlebar(window, GTK_WIDGET(header_bar));
+ } else {
+ gtk_window_set_title(window, "example");
+ }
+
+ gtk_window_set_default_size(window, 1280, 720);
+ gtk_widget_show(GTK_WIDGET(window));
+
+ g_autoptr(FlDartProject) project = fl_dart_project_new();
+ fl_dart_project_set_dart_entrypoint_arguments(project, self->dart_entrypoint_arguments);
+
+ FlView* view = fl_view_new(project);
+ gtk_widget_show(GTK_WIDGET(view));
+ gtk_container_add(GTK_CONTAINER(window), GTK_WIDGET(view));
+
+ fl_register_plugins(FL_PLUGIN_REGISTRY(view));
+
+ gtk_widget_grab_focus(GTK_WIDGET(view));
+}
+
+// Implements GApplication::local_command_line.
+static gboolean my_application_local_command_line(GApplication* application, gchar*** arguments, int* exit_status) {
+ MyApplication* self = MY_APPLICATION(application);
+ // Strip out the first argument as it is the binary name.
+ self->dart_entrypoint_arguments = g_strdupv(*arguments + 1);
+
+ g_autoptr(GError) error = nullptr;
+ if (!g_application_register(application, nullptr, &error)) {
+ g_warning("Failed to register: %s", error->message);
+ *exit_status = 1;
+ return TRUE;
+ }
+
+ g_application_activate(application);
+ *exit_status = 0;
+
+ return TRUE;
+}
+
+// Implements GObject::dispose.
+static void my_application_dispose(GObject* object) {
+ MyApplication* self = MY_APPLICATION(object);
+ g_clear_pointer(&self->dart_entrypoint_arguments, g_strfreev);
+ G_OBJECT_CLASS(my_application_parent_class)->dispose(object);
+}
+
+static void my_application_class_init(MyApplicationClass* klass) {
+ G_APPLICATION_CLASS(klass)->activate = my_application_activate;
+ G_APPLICATION_CLASS(klass)->local_command_line = my_application_local_command_line;
+ G_OBJECT_CLASS(klass)->dispose = my_application_dispose;
+}
+
+static void my_application_init(MyApplication* self) {}
+
+MyApplication* my_application_new() {
+ return MY_APPLICATION(g_object_new(my_application_get_type(),
+ "application-id", APPLICATION_ID,
+ "flags", G_APPLICATION_NON_UNIQUE,
+ nullptr));
+}
diff --git a/third_party/pub_patches/fluttertoast/example/linux/my_application.h b/third_party/pub_patches/fluttertoast/example/linux/my_application.h
new file mode 100644
index 000000000..72271d5e4
--- /dev/null
+++ b/third_party/pub_patches/fluttertoast/example/linux/my_application.h
@@ -0,0 +1,18 @@
+#ifndef FLUTTER_MY_APPLICATION_H_
+#define FLUTTER_MY_APPLICATION_H_
+
+#include
+
+G_DECLARE_FINAL_TYPE(MyApplication, my_application, MY, APPLICATION,
+ GtkApplication)
+
+/**
+ * my_application_new:
+ *
+ * Creates a new Flutter-based application.
+ *
+ * Returns: a new #MyApplication.
+ */
+MyApplication* my_application_new();
+
+#endif // FLUTTER_MY_APPLICATION_H_
diff --git a/third_party/pub_patches/fluttertoast/example/macos/Flutter/Flutter-Debug.xcconfig b/third_party/pub_patches/fluttertoast/example/macos/Flutter/Flutter-Debug.xcconfig
new file mode 100644
index 000000000..4b81f9b2d
--- /dev/null
+++ b/third_party/pub_patches/fluttertoast/example/macos/Flutter/Flutter-Debug.xcconfig
@@ -0,0 +1,2 @@
+#include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"
+#include "ephemeral/Flutter-Generated.xcconfig"
diff --git a/third_party/pub_patches/fluttertoast/example/macos/Flutter/Flutter-Release.xcconfig b/third_party/pub_patches/fluttertoast/example/macos/Flutter/Flutter-Release.xcconfig
new file mode 100644
index 000000000..5caa9d157
--- /dev/null
+++ b/third_party/pub_patches/fluttertoast/example/macos/Flutter/Flutter-Release.xcconfig
@@ -0,0 +1,2 @@
+#include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"
+#include "ephemeral/Flutter-Generated.xcconfig"
diff --git a/third_party/pub_patches/fluttertoast/example/macos/Flutter/GeneratedPluginRegistrant.swift b/third_party/pub_patches/fluttertoast/example/macos/Flutter/GeneratedPluginRegistrant.swift
new file mode 100644
index 000000000..cccf817a5
--- /dev/null
+++ b/third_party/pub_patches/fluttertoast/example/macos/Flutter/GeneratedPluginRegistrant.swift
@@ -0,0 +1,10 @@
+//
+// Generated file. Do not edit.
+//
+
+import FlutterMacOS
+import Foundation
+
+
+func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) {
+}
diff --git a/third_party/pub_patches/fluttertoast/example/macos/Podfile b/third_party/pub_patches/fluttertoast/example/macos/Podfile
new file mode 100644
index 000000000..049abe295
--- /dev/null
+++ b/third_party/pub_patches/fluttertoast/example/macos/Podfile
@@ -0,0 +1,40 @@
+platform :osx, '10.14'
+
+# CocoaPods analytics sends network stats synchronously affecting flutter build latency.
+ENV['COCOAPODS_DISABLE_STATS'] = 'true'
+
+project 'Runner', {
+ 'Debug' => :debug,
+ 'Profile' => :release,
+ 'Release' => :release,
+}
+
+def flutter_root
+ generated_xcode_build_settings_path = File.expand_path(File.join('..', 'Flutter', 'ephemeral', 'Flutter-Generated.xcconfig'), __FILE__)
+ unless File.exist?(generated_xcode_build_settings_path)
+ raise "#{generated_xcode_build_settings_path} must exist. If you're running pod install manually, make sure \"flutter pub get\" is executed first"
+ end
+
+ File.foreach(generated_xcode_build_settings_path) do |line|
+ matches = line.match(/FLUTTER_ROOT\=(.*)/)
+ return matches[1].strip if matches
+ end
+ raise "FLUTTER_ROOT not found in #{generated_xcode_build_settings_path}. Try deleting Flutter-Generated.xcconfig, then run \"flutter pub get\""
+end
+
+require File.expand_path(File.join('packages', 'flutter_tools', 'bin', 'podhelper'), flutter_root)
+
+flutter_macos_podfile_setup
+
+target 'Runner' do
+ use_frameworks!
+ use_modular_headers!
+
+ flutter_install_all_macos_pods File.dirname(File.realpath(__FILE__))
+end
+
+post_install do |installer|
+ installer.pods_project.targets.each do |target|
+ flutter_additional_macos_build_settings(target)
+ end
+end
diff --git a/third_party/pub_patches/fluttertoast/example/macos/Runner.xcodeproj/project.pbxproj b/third_party/pub_patches/fluttertoast/example/macos/Runner.xcodeproj/project.pbxproj
new file mode 100644
index 000000000..d9333e470
--- /dev/null
+++ b/third_party/pub_patches/fluttertoast/example/macos/Runner.xcodeproj/project.pbxproj
@@ -0,0 +1,573 @@
+// !$*UTF8*$!
+{
+ archiveVersion = 1;
+ classes = {
+ };
+ objectVersion = 54;
+ objects = {
+
+/* Begin PBXAggregateTarget section */
+ 33CC111A2044C6BA0003C045 /* Flutter Assemble */ = {
+ isa = PBXAggregateTarget;
+ buildConfigurationList = 33CC111B2044C6BA0003C045 /* Build configuration list for PBXAggregateTarget "Flutter Assemble" */;
+ buildPhases = (
+ 33CC111E2044C6BF0003C045 /* ShellScript */,
+ );
+ dependencies = (
+ );
+ name = "Flutter Assemble";
+ productName = FLX;
+ };
+/* End PBXAggregateTarget section */
+
+/* Begin PBXBuildFile section */
+ 335BBD1B22A9A15E00E9071D /* GeneratedPluginRegistrant.swift in Sources */ = {isa = PBXBuildFile; fileRef = 335BBD1A22A9A15E00E9071D /* GeneratedPluginRegistrant.swift */; };
+ 33CC10F12044A3C60003C045 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 33CC10F02044A3C60003C045 /* AppDelegate.swift */; };
+ 33CC10F32044A3C60003C045 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 33CC10F22044A3C60003C045 /* Assets.xcassets */; };
+ 33CC10F62044A3C60003C045 /* MainMenu.xib in Resources */ = {isa = PBXBuildFile; fileRef = 33CC10F42044A3C60003C045 /* MainMenu.xib */; };
+ 33CC11132044BFA00003C045 /* MainFlutterWindow.swift in Sources */ = {isa = PBXBuildFile; fileRef = 33CC11122044BFA00003C045 /* MainFlutterWindow.swift */; };
+/* End PBXBuildFile section */
+
+/* Begin PBXContainerItemProxy section */
+ 33CC111F2044C79F0003C045 /* PBXContainerItemProxy */ = {
+ isa = PBXContainerItemProxy;
+ containerPortal = 33CC10E52044A3C60003C045 /* Project object */;
+ proxyType = 1;
+ remoteGlobalIDString = 33CC111A2044C6BA0003C045;
+ remoteInfo = FLX;
+ };
+/* End PBXContainerItemProxy section */
+
+/* Begin PBXCopyFilesBuildPhase section */
+ 33CC110E2044A8840003C045 /* Bundle Framework */ = {
+ isa = PBXCopyFilesBuildPhase;
+ buildActionMask = 2147483647;
+ dstPath = "";
+ dstSubfolderSpec = 10;
+ files = (
+ );
+ name = "Bundle Framework";
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+/* End PBXCopyFilesBuildPhase section */
+
+/* Begin PBXFileReference section */
+ 333000ED22D3DE5D00554162 /* Warnings.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Warnings.xcconfig; sourceTree = ""; };
+ 335BBD1A22A9A15E00E9071D /* GeneratedPluginRegistrant.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GeneratedPluginRegistrant.swift; sourceTree = ""; };
+ 33CC10ED2044A3C60003C045 /* example.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "example.app"; sourceTree = BUILT_PRODUCTS_DIR; };
+ 33CC10F02044A3C60003C045 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; };
+ 33CC10F22044A3C60003C045 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; name = Assets.xcassets; path = Runner/Assets.xcassets; sourceTree = ""; };
+ 33CC10F52044A3C60003C045 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/MainMenu.xib; sourceTree = ""; };
+ 33CC10F72044A3C60003C045 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; name = Info.plist; path = Runner/Info.plist; sourceTree = ""; };
+ 33CC11122044BFA00003C045 /* MainFlutterWindow.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MainFlutterWindow.swift; sourceTree = ""; };
+ 33CEB47222A05771004F2AC0 /* Flutter-Debug.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = "Flutter-Debug.xcconfig"; sourceTree = ""; };
+ 33CEB47422A05771004F2AC0 /* Flutter-Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = "Flutter-Release.xcconfig"; sourceTree = ""; };
+ 33CEB47722A0578A004F2AC0 /* Flutter-Generated.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = "Flutter-Generated.xcconfig"; path = "ephemeral/Flutter-Generated.xcconfig"; sourceTree = ""; };
+ 33E51913231747F40026EE4D /* DebugProfile.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = DebugProfile.entitlements; sourceTree = ""; };
+ 33E51914231749380026EE4D /* Release.entitlements */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.entitlements; path = Release.entitlements; sourceTree = ""; };
+ 33E5194F232828860026EE4D /* AppInfo.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = AppInfo.xcconfig; sourceTree = ""; };
+ 7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Release.xcconfig; sourceTree = ""; };
+ 9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = Debug.xcconfig; sourceTree = ""; };
+/* End PBXFileReference section */
+
+/* Begin PBXFrameworksBuildPhase section */
+ 33CC10EA2044A3C60003C045 /* Frameworks */ = {
+ isa = PBXFrameworksBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+/* End PBXFrameworksBuildPhase section */
+
+/* Begin PBXGroup section */
+ 33BA886A226E78AF003329D5 /* Configs */ = {
+ isa = PBXGroup;
+ children = (
+ 33E5194F232828860026EE4D /* AppInfo.xcconfig */,
+ 9740EEB21CF90195004384FC /* Debug.xcconfig */,
+ 7AFA3C8E1D35360C0083082E /* Release.xcconfig */,
+ 333000ED22D3DE5D00554162 /* Warnings.xcconfig */,
+ );
+ path = Configs;
+ sourceTree = "";
+ };
+ 33CC10E42044A3C60003C045 = {
+ isa = PBXGroup;
+ children = (
+ 33FAB671232836740065AC1E /* Runner */,
+ 33CEB47122A05771004F2AC0 /* Flutter */,
+ 33CC10EE2044A3C60003C045 /* Products */,
+ D73912EC22F37F3D000D13A0 /* Frameworks */,
+ );
+ sourceTree = "";
+ };
+ 33CC10EE2044A3C60003C045 /* Products */ = {
+ isa = PBXGroup;
+ children = (
+ 33CC10ED2044A3C60003C045 /* example.app */,
+ );
+ name = Products;
+ sourceTree = "";
+ };
+ 33CC11242044D66E0003C045 /* Resources */ = {
+ isa = PBXGroup;
+ children = (
+ 33CC10F22044A3C60003C045 /* Assets.xcassets */,
+ 33CC10F42044A3C60003C045 /* MainMenu.xib */,
+ 33CC10F72044A3C60003C045 /* Info.plist */,
+ );
+ name = Resources;
+ path = ..;
+ sourceTree = "";
+ };
+ 33CEB47122A05771004F2AC0 /* Flutter */ = {
+ isa = PBXGroup;
+ children = (
+ 335BBD1A22A9A15E00E9071D /* GeneratedPluginRegistrant.swift */,
+ 33CEB47222A05771004F2AC0 /* Flutter-Debug.xcconfig */,
+ 33CEB47422A05771004F2AC0 /* Flutter-Release.xcconfig */,
+ 33CEB47722A0578A004F2AC0 /* Flutter-Generated.xcconfig */,
+ );
+ path = Flutter;
+ sourceTree = "";
+ };
+ 33FAB671232836740065AC1E /* Runner */ = {
+ isa = PBXGroup;
+ children = (
+ 33CC10F02044A3C60003C045 /* AppDelegate.swift */,
+ 33CC11122044BFA00003C045 /* MainFlutterWindow.swift */,
+ 33E51913231747F40026EE4D /* DebugProfile.entitlements */,
+ 33E51914231749380026EE4D /* Release.entitlements */,
+ 33CC11242044D66E0003C045 /* Resources */,
+ 33BA886A226E78AF003329D5 /* Configs */,
+ );
+ path = Runner;
+ sourceTree = "";
+ };
+ D73912EC22F37F3D000D13A0 /* Frameworks */ = {
+ isa = PBXGroup;
+ children = (
+ );
+ name = Frameworks;
+ sourceTree = "";
+ };
+/* End PBXGroup section */
+
+/* Begin PBXNativeTarget section */
+ 33CC10EC2044A3C60003C045 /* Runner */ = {
+ isa = PBXNativeTarget;
+ buildConfigurationList = 33CC10FB2044A3C60003C045 /* Build configuration list for PBXNativeTarget "Runner" */;
+ buildPhases = (
+ 33CC10E92044A3C60003C045 /* Sources */,
+ 33CC10EA2044A3C60003C045 /* Frameworks */,
+ 33CC10EB2044A3C60003C045 /* Resources */,
+ 33CC110E2044A8840003C045 /* Bundle Framework */,
+ 3399D490228B24CF009A79C7 /* ShellScript */,
+ );
+ buildRules = (
+ );
+ dependencies = (
+ 33CC11202044C79F0003C045 /* PBXTargetDependency */,
+ );
+ name = Runner;
+ productName = Runner;
+ productReference = 33CC10ED2044A3C60003C045 /* example.app */;
+ productType = "com.apple.product-type.application";
+ };
+/* End PBXNativeTarget section */
+
+/* Begin PBXProject section */
+ 33CC10E52044A3C60003C045 /* Project object */ = {
+ isa = PBXProject;
+ attributes = {
+ LastSwiftUpdateCheck = 0920;
+ LastUpgradeCheck = 1300;
+ ORGANIZATIONNAME = "";
+ TargetAttributes = {
+ 33CC10EC2044A3C60003C045 = {
+ CreatedOnToolsVersion = 9.2;
+ LastSwiftMigration = 1100;
+ ProvisioningStyle = Automatic;
+ SystemCapabilities = {
+ com.apple.Sandbox = {
+ enabled = 1;
+ };
+ };
+ };
+ 33CC111A2044C6BA0003C045 = {
+ CreatedOnToolsVersion = 9.2;
+ ProvisioningStyle = Manual;
+ };
+ };
+ };
+ buildConfigurationList = 33CC10E82044A3C60003C045 /* Build configuration list for PBXProject "Runner" */;
+ compatibilityVersion = "Xcode 9.3";
+ developmentRegion = en;
+ hasScannedForEncodings = 0;
+ knownRegions = (
+ en,
+ Base,
+ );
+ mainGroup = 33CC10E42044A3C60003C045;
+ productRefGroup = 33CC10EE2044A3C60003C045 /* Products */;
+ projectDirPath = "";
+ projectRoot = "";
+ targets = (
+ 33CC10EC2044A3C60003C045 /* Runner */,
+ 33CC111A2044C6BA0003C045 /* Flutter Assemble */,
+ );
+ };
+/* End PBXProject section */
+
+/* Begin PBXResourcesBuildPhase section */
+ 33CC10EB2044A3C60003C045 /* Resources */ = {
+ isa = PBXResourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ 33CC10F32044A3C60003C045 /* Assets.xcassets in Resources */,
+ 33CC10F62044A3C60003C045 /* MainMenu.xib in Resources */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+/* End PBXResourcesBuildPhase section */
+
+/* Begin PBXShellScriptBuildPhase section */
+ 3399D490228B24CF009A79C7 /* ShellScript */ = {
+ isa = PBXShellScriptBuildPhase;
+ alwaysOutOfDate = 1;
+ buildActionMask = 2147483647;
+ files = (
+ );
+ inputFileListPaths = (
+ );
+ inputPaths = (
+ );
+ outputFileListPaths = (
+ );
+ outputPaths = (
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ shellPath = /bin/sh;
+ shellScript = "echo \"$PRODUCT_NAME.app\" > \"$PROJECT_DIR\"/Flutter/ephemeral/.app_filename && \"$FLUTTER_ROOT\"/packages/flutter_tools/bin/macos_assemble.sh embed\n";
+ };
+ 33CC111E2044C6BF0003C045 /* ShellScript */ = {
+ isa = PBXShellScriptBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ );
+ inputFileListPaths = (
+ Flutter/ephemeral/FlutterInputs.xcfilelist,
+ );
+ inputPaths = (
+ Flutter/ephemeral/tripwire,
+ );
+ outputFileListPaths = (
+ Flutter/ephemeral/FlutterOutputs.xcfilelist,
+ );
+ outputPaths = (
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ shellPath = /bin/sh;
+ shellScript = "\"$FLUTTER_ROOT\"/packages/flutter_tools/bin/macos_assemble.sh && touch Flutter/ephemeral/tripwire";
+ };
+/* End PBXShellScriptBuildPhase section */
+
+/* Begin PBXSourcesBuildPhase section */
+ 33CC10E92044A3C60003C045 /* Sources */ = {
+ isa = PBXSourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ 33CC11132044BFA00003C045 /* MainFlutterWindow.swift in Sources */,
+ 33CC10F12044A3C60003C045 /* AppDelegate.swift in Sources */,
+ 335BBD1B22A9A15E00E9071D /* GeneratedPluginRegistrant.swift in Sources */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+/* End PBXSourcesBuildPhase section */
+
+/* Begin PBXTargetDependency section */
+ 33CC11202044C79F0003C045 /* PBXTargetDependency */ = {
+ isa = PBXTargetDependency;
+ target = 33CC111A2044C6BA0003C045 /* Flutter Assemble */;
+ targetProxy = 33CC111F2044C79F0003C045 /* PBXContainerItemProxy */;
+ };
+/* End PBXTargetDependency section */
+
+/* Begin PBXVariantGroup section */
+ 33CC10F42044A3C60003C045 /* MainMenu.xib */ = {
+ isa = PBXVariantGroup;
+ children = (
+ 33CC10F52044A3C60003C045 /* Base */,
+ );
+ name = MainMenu.xib;
+ path = Runner;
+ sourceTree = "";
+ };
+/* End PBXVariantGroup section */
+
+/* Begin XCBuildConfiguration section */
+ 338D0CE9231458BD00FA5F75 /* Profile */ = {
+ isa = XCBuildConfiguration;
+ baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */;
+ buildSettings = {
+ ALWAYS_SEARCH_USER_PATHS = NO;
+ CLANG_ANALYZER_NONNULL = YES;
+ CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
+ CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
+ CLANG_CXX_LIBRARY = "libc++";
+ CLANG_ENABLE_MODULES = YES;
+ CLANG_ENABLE_OBJC_ARC = YES;
+ CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
+ CLANG_WARN_BOOL_CONVERSION = YES;
+ CLANG_WARN_CONSTANT_CONVERSION = YES;
+ CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
+ CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
+ CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
+ CLANG_WARN_EMPTY_BODY = YES;
+ CLANG_WARN_ENUM_CONVERSION = YES;
+ CLANG_WARN_INFINITE_RECURSION = YES;
+ CLANG_WARN_INT_CONVERSION = YES;
+ CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
+ CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
+ CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
+ CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
+ CLANG_WARN_SUSPICIOUS_MOVE = YES;
+ CODE_SIGN_IDENTITY = "-";
+ COPY_PHASE_STRIP = NO;
+ DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
+ ENABLE_NS_ASSERTIONS = NO;
+ ENABLE_STRICT_OBJC_MSGSEND = YES;
+ GCC_C_LANGUAGE_STANDARD = gnu11;
+ GCC_NO_COMMON_BLOCKS = YES;
+ GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
+ GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
+ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
+ GCC_WARN_UNUSED_FUNCTION = YES;
+ GCC_WARN_UNUSED_VARIABLE = YES;
+ MACOSX_DEPLOYMENT_TARGET = 10.14;
+ MTL_ENABLE_DEBUG_INFO = NO;
+ SDKROOT = macosx;
+ SWIFT_COMPILATION_MODE = wholemodule;
+ SWIFT_OPTIMIZATION_LEVEL = "-O";
+ };
+ name = Profile;
+ };
+ 338D0CEA231458BD00FA5F75 /* Profile */ = {
+ isa = XCBuildConfiguration;
+ baseConfigurationReference = 33E5194F232828860026EE4D /* AppInfo.xcconfig */;
+ buildSettings = {
+ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
+ CLANG_ENABLE_MODULES = YES;
+ CODE_SIGN_ENTITLEMENTS = Runner/DebugProfile.entitlements;
+ CODE_SIGN_STYLE = Automatic;
+ COMBINE_HIDPI_IMAGES = YES;
+ INFOPLIST_FILE = Runner/Info.plist;
+ LD_RUNPATH_SEARCH_PATHS = (
+ "$(inherited)",
+ "@executable_path/../Frameworks",
+ );
+ PROVISIONING_PROFILE_SPECIFIER = "";
+ SWIFT_VERSION = 5.0;
+ };
+ name = Profile;
+ };
+ 338D0CEB231458BD00FA5F75 /* Profile */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ CODE_SIGN_STYLE = Manual;
+ PRODUCT_NAME = "$(TARGET_NAME)";
+ };
+ name = Profile;
+ };
+ 33CC10F92044A3C60003C045 /* Debug */ = {
+ isa = XCBuildConfiguration;
+ baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */;
+ buildSettings = {
+ ALWAYS_SEARCH_USER_PATHS = NO;
+ CLANG_ANALYZER_NONNULL = YES;
+ CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
+ CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
+ CLANG_CXX_LIBRARY = "libc++";
+ CLANG_ENABLE_MODULES = YES;
+ CLANG_ENABLE_OBJC_ARC = YES;
+ CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
+ CLANG_WARN_BOOL_CONVERSION = YES;
+ CLANG_WARN_CONSTANT_CONVERSION = YES;
+ CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
+ CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
+ CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
+ CLANG_WARN_EMPTY_BODY = YES;
+ CLANG_WARN_ENUM_CONVERSION = YES;
+ CLANG_WARN_INFINITE_RECURSION = YES;
+ CLANG_WARN_INT_CONVERSION = YES;
+ CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
+ CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
+ CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
+ CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
+ CLANG_WARN_SUSPICIOUS_MOVE = YES;
+ CODE_SIGN_IDENTITY = "-";
+ COPY_PHASE_STRIP = NO;
+ DEBUG_INFORMATION_FORMAT = dwarf;
+ ENABLE_STRICT_OBJC_MSGSEND = YES;
+ ENABLE_TESTABILITY = YES;
+ GCC_C_LANGUAGE_STANDARD = gnu11;
+ GCC_DYNAMIC_NO_PIC = NO;
+ GCC_NO_COMMON_BLOCKS = YES;
+ GCC_OPTIMIZATION_LEVEL = 0;
+ GCC_PREPROCESSOR_DEFINITIONS = (
+ "DEBUG=1",
+ "$(inherited)",
+ );
+ GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
+ GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
+ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
+ GCC_WARN_UNUSED_FUNCTION = YES;
+ GCC_WARN_UNUSED_VARIABLE = YES;
+ MACOSX_DEPLOYMENT_TARGET = 10.14;
+ MTL_ENABLE_DEBUG_INFO = YES;
+ ONLY_ACTIVE_ARCH = YES;
+ SDKROOT = macosx;
+ SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;
+ SWIFT_OPTIMIZATION_LEVEL = "-Onone";
+ };
+ name = Debug;
+ };
+ 33CC10FA2044A3C60003C045 /* Release */ = {
+ isa = XCBuildConfiguration;
+ baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */;
+ buildSettings = {
+ ALWAYS_SEARCH_USER_PATHS = NO;
+ CLANG_ANALYZER_NONNULL = YES;
+ CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
+ CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
+ CLANG_CXX_LIBRARY = "libc++";
+ CLANG_ENABLE_MODULES = YES;
+ CLANG_ENABLE_OBJC_ARC = YES;
+ CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
+ CLANG_WARN_BOOL_CONVERSION = YES;
+ CLANG_WARN_CONSTANT_CONVERSION = YES;
+ CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
+ CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
+ CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
+ CLANG_WARN_EMPTY_BODY = YES;
+ CLANG_WARN_ENUM_CONVERSION = YES;
+ CLANG_WARN_INFINITE_RECURSION = YES;
+ CLANG_WARN_INT_CONVERSION = YES;
+ CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
+ CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
+ CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
+ CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
+ CLANG_WARN_SUSPICIOUS_MOVE = YES;
+ CODE_SIGN_IDENTITY = "-";
+ COPY_PHASE_STRIP = NO;
+ DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
+ ENABLE_NS_ASSERTIONS = NO;
+ ENABLE_STRICT_OBJC_MSGSEND = YES;
+ GCC_C_LANGUAGE_STANDARD = gnu11;
+ GCC_NO_COMMON_BLOCKS = YES;
+ GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
+ GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
+ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
+ GCC_WARN_UNUSED_FUNCTION = YES;
+ GCC_WARN_UNUSED_VARIABLE = YES;
+ MACOSX_DEPLOYMENT_TARGET = 10.14;
+ MTL_ENABLE_DEBUG_INFO = NO;
+ SDKROOT = macosx;
+ SWIFT_COMPILATION_MODE = wholemodule;
+ SWIFT_OPTIMIZATION_LEVEL = "-O";
+ };
+ name = Release;
+ };
+ 33CC10FC2044A3C60003C045 /* Debug */ = {
+ isa = XCBuildConfiguration;
+ baseConfigurationReference = 33E5194F232828860026EE4D /* AppInfo.xcconfig */;
+ buildSettings = {
+ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
+ CLANG_ENABLE_MODULES = YES;
+ CODE_SIGN_ENTITLEMENTS = Runner/DebugProfile.entitlements;
+ CODE_SIGN_STYLE = Automatic;
+ COMBINE_HIDPI_IMAGES = YES;
+ INFOPLIST_FILE = Runner/Info.plist;
+ LD_RUNPATH_SEARCH_PATHS = (
+ "$(inherited)",
+ "@executable_path/../Frameworks",
+ );
+ PROVISIONING_PROFILE_SPECIFIER = "";
+ SWIFT_OPTIMIZATION_LEVEL = "-Onone";
+ SWIFT_VERSION = 5.0;
+ };
+ name = Debug;
+ };
+ 33CC10FD2044A3C60003C045 /* Release */ = {
+ isa = XCBuildConfiguration;
+ baseConfigurationReference = 33E5194F232828860026EE4D /* AppInfo.xcconfig */;
+ buildSettings = {
+ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
+ CLANG_ENABLE_MODULES = YES;
+ CODE_SIGN_ENTITLEMENTS = Runner/Release.entitlements;
+ CODE_SIGN_STYLE = Automatic;
+ COMBINE_HIDPI_IMAGES = YES;
+ INFOPLIST_FILE = Runner/Info.plist;
+ LD_RUNPATH_SEARCH_PATHS = (
+ "$(inherited)",
+ "@executable_path/../Frameworks",
+ );
+ PROVISIONING_PROFILE_SPECIFIER = "";
+ SWIFT_VERSION = 5.0;
+ };
+ name = Release;
+ };
+ 33CC111C2044C6BA0003C045 /* Debug */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ CODE_SIGN_STYLE = Manual;
+ PRODUCT_NAME = "$(TARGET_NAME)";
+ };
+ name = Debug;
+ };
+ 33CC111D2044C6BA0003C045 /* Release */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ CODE_SIGN_STYLE = Automatic;
+ PRODUCT_NAME = "$(TARGET_NAME)";
+ };
+ name = Release;
+ };
+/* End XCBuildConfiguration section */
+
+/* Begin XCConfigurationList section */
+ 33CC10E82044A3C60003C045 /* Build configuration list for PBXProject "Runner" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ 33CC10F92044A3C60003C045 /* Debug */,
+ 33CC10FA2044A3C60003C045 /* Release */,
+ 338D0CE9231458BD00FA5F75 /* Profile */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
+ };
+ 33CC10FB2044A3C60003C045 /* Build configuration list for PBXNativeTarget "Runner" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ 33CC10FC2044A3C60003C045 /* Debug */,
+ 33CC10FD2044A3C60003C045 /* Release */,
+ 338D0CEA231458BD00FA5F75 /* Profile */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
+ };
+ 33CC111B2044C6BA0003C045 /* Build configuration list for PBXAggregateTarget "Flutter Assemble" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ 33CC111C2044C6BA0003C045 /* Debug */,
+ 33CC111D2044C6BA0003C045 /* Release */,
+ 338D0CEB231458BD00FA5F75 /* Profile */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
+ };
+/* End XCConfigurationList section */
+ };
+ rootObject = 33CC10E52044A3C60003C045 /* Project object */;
+}
diff --git a/third_party/pub_patches/fluttertoast/example/macos/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/third_party/pub_patches/fluttertoast/example/macos/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist
new file mode 100644
index 000000000..18d981003
--- /dev/null
+++ b/third_party/pub_patches/fluttertoast/example/macos/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist
@@ -0,0 +1,8 @@
+
+
+
+
+ IDEDidComputeMac32BitWarning
+
+
+
diff --git a/third_party/pub_patches/fluttertoast/example/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme b/third_party/pub_patches/fluttertoast/example/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme
new file mode 100644
index 000000000..fb7259e17
--- /dev/null
+++ b/third_party/pub_patches/fluttertoast/example/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme
@@ -0,0 +1,87 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/third_party/pub_patches/fluttertoast/example/macos/Runner.xcworkspace/contents.xcworkspacedata b/third_party/pub_patches/fluttertoast/example/macos/Runner.xcworkspace/contents.xcworkspacedata
new file mode 100644
index 000000000..1d526a16e
--- /dev/null
+++ b/third_party/pub_patches/fluttertoast/example/macos/Runner.xcworkspace/contents.xcworkspacedata
@@ -0,0 +1,7 @@
+
+
+
+
+
diff --git a/third_party/pub_patches/fluttertoast/example/macos/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/third_party/pub_patches/fluttertoast/example/macos/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist
new file mode 100644
index 000000000..18d981003
--- /dev/null
+++ b/third_party/pub_patches/fluttertoast/example/macos/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist
@@ -0,0 +1,8 @@
+
+
+
+
+ IDEDidComputeMac32BitWarning
+
+
+
diff --git a/third_party/pub_patches/fluttertoast/example/macos/Runner/AppDelegate.swift b/third_party/pub_patches/fluttertoast/example/macos/Runner/AppDelegate.swift
new file mode 100644
index 000000000..d53ef6437
--- /dev/null
+++ b/third_party/pub_patches/fluttertoast/example/macos/Runner/AppDelegate.swift
@@ -0,0 +1,9 @@
+import Cocoa
+import FlutterMacOS
+
+@NSApplicationMain
+class AppDelegate: FlutterAppDelegate {
+ override func applicationShouldTerminateAfterLastWindowClosed(_ sender: NSApplication) -> Bool {
+ return true
+ }
+}
diff --git a/third_party/pub_patches/fluttertoast/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json b/third_party/pub_patches/fluttertoast/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json
new file mode 100644
index 000000000..a2ec33f19
--- /dev/null
+++ b/third_party/pub_patches/fluttertoast/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json
@@ -0,0 +1,68 @@
+{
+ "images" : [
+ {
+ "size" : "16x16",
+ "idiom" : "mac",
+ "filename" : "app_icon_16.png",
+ "scale" : "1x"
+ },
+ {
+ "size" : "16x16",
+ "idiom" : "mac",
+ "filename" : "app_icon_32.png",
+ "scale" : "2x"
+ },
+ {
+ "size" : "32x32",
+ "idiom" : "mac",
+ "filename" : "app_icon_32.png",
+ "scale" : "1x"
+ },
+ {
+ "size" : "32x32",
+ "idiom" : "mac",
+ "filename" : "app_icon_64.png",
+ "scale" : "2x"
+ },
+ {
+ "size" : "128x128",
+ "idiom" : "mac",
+ "filename" : "app_icon_128.png",
+ "scale" : "1x"
+ },
+ {
+ "size" : "128x128",
+ "idiom" : "mac",
+ "filename" : "app_icon_256.png",
+ "scale" : "2x"
+ },
+ {
+ "size" : "256x256",
+ "idiom" : "mac",
+ "filename" : "app_icon_256.png",
+ "scale" : "1x"
+ },
+ {
+ "size" : "256x256",
+ "idiom" : "mac",
+ "filename" : "app_icon_512.png",
+ "scale" : "2x"
+ },
+ {
+ "size" : "512x512",
+ "idiom" : "mac",
+ "filename" : "app_icon_512.png",
+ "scale" : "1x"
+ },
+ {
+ "size" : "512x512",
+ "idiom" : "mac",
+ "filename" : "app_icon_1024.png",
+ "scale" : "2x"
+ }
+ ],
+ "info" : {
+ "version" : 1,
+ "author" : "xcode"
+ }
+}
diff --git a/third_party/pub_patches/fluttertoast/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_1024.png b/third_party/pub_patches/fluttertoast/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_1024.png
new file mode 100644
index 000000000..82b6f9d9a
Binary files /dev/null and b/third_party/pub_patches/fluttertoast/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_1024.png differ
diff --git a/third_party/pub_patches/fluttertoast/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_128.png b/third_party/pub_patches/fluttertoast/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_128.png
new file mode 100644
index 000000000..13b35eba5
Binary files /dev/null and b/third_party/pub_patches/fluttertoast/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_128.png differ
diff --git a/third_party/pub_patches/fluttertoast/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_16.png b/third_party/pub_patches/fluttertoast/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_16.png
new file mode 100644
index 000000000..0a3f5fa40
Binary files /dev/null and b/third_party/pub_patches/fluttertoast/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_16.png differ
diff --git a/third_party/pub_patches/fluttertoast/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_256.png b/third_party/pub_patches/fluttertoast/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_256.png
new file mode 100644
index 000000000..bdb57226d
Binary files /dev/null and b/third_party/pub_patches/fluttertoast/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_256.png differ
diff --git a/third_party/pub_patches/fluttertoast/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_32.png b/third_party/pub_patches/fluttertoast/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_32.png
new file mode 100644
index 000000000..f083318e0
Binary files /dev/null and b/third_party/pub_patches/fluttertoast/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_32.png differ
diff --git a/third_party/pub_patches/fluttertoast/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_512.png b/third_party/pub_patches/fluttertoast/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_512.png
new file mode 100644
index 000000000..326c0e72c
Binary files /dev/null and b/third_party/pub_patches/fluttertoast/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_512.png differ
diff --git a/third_party/pub_patches/fluttertoast/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_64.png b/third_party/pub_patches/fluttertoast/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_64.png
new file mode 100644
index 000000000..2f1632cfd
Binary files /dev/null and b/third_party/pub_patches/fluttertoast/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_64.png differ
diff --git a/third_party/pub_patches/fluttertoast/example/macos/Runner/Base.lproj/MainMenu.xib b/third_party/pub_patches/fluttertoast/example/macos/Runner/Base.lproj/MainMenu.xib
new file mode 100644
index 000000000..80e867a4e
--- /dev/null
+++ b/third_party/pub_patches/fluttertoast/example/macos/Runner/Base.lproj/MainMenu.xib
@@ -0,0 +1,343 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/third_party/pub_patches/fluttertoast/example/macos/Runner/Configs/AppInfo.xcconfig b/third_party/pub_patches/fluttertoast/example/macos/Runner/Configs/AppInfo.xcconfig
new file mode 100644
index 000000000..8b42559e8
--- /dev/null
+++ b/third_party/pub_patches/fluttertoast/example/macos/Runner/Configs/AppInfo.xcconfig
@@ -0,0 +1,14 @@
+// Application-level settings for the Runner target.
+//
+// This may be replaced with something auto-generated from metadata (e.g., pubspec.yaml) in the
+// future. If not, the values below would default to using the project name when this becomes a
+// 'flutter create' template.
+
+// The application's name. By default this is also the title of the Flutter window.
+PRODUCT_NAME = example
+
+// The application's bundle identifier
+PRODUCT_BUNDLE_IDENTIFIER = com.example.example
+
+// The copyright displayed in application information
+PRODUCT_COPYRIGHT = Copyright © 2022 com.example. All rights reserved.
diff --git a/third_party/pub_patches/fluttertoast/example/macos/Runner/Configs/Debug.xcconfig b/third_party/pub_patches/fluttertoast/example/macos/Runner/Configs/Debug.xcconfig
new file mode 100644
index 000000000..36b0fd946
--- /dev/null
+++ b/third_party/pub_patches/fluttertoast/example/macos/Runner/Configs/Debug.xcconfig
@@ -0,0 +1,2 @@
+#include "../../Flutter/Flutter-Debug.xcconfig"
+#include "Warnings.xcconfig"
diff --git a/third_party/pub_patches/fluttertoast/example/macos/Runner/Configs/Release.xcconfig b/third_party/pub_patches/fluttertoast/example/macos/Runner/Configs/Release.xcconfig
new file mode 100644
index 000000000..dff4f4956
--- /dev/null
+++ b/third_party/pub_patches/fluttertoast/example/macos/Runner/Configs/Release.xcconfig
@@ -0,0 +1,2 @@
+#include "../../Flutter/Flutter-Release.xcconfig"
+#include "Warnings.xcconfig"
diff --git a/third_party/pub_patches/fluttertoast/example/macos/Runner/Configs/Warnings.xcconfig b/third_party/pub_patches/fluttertoast/example/macos/Runner/Configs/Warnings.xcconfig
new file mode 100644
index 000000000..42bcbf478
--- /dev/null
+++ b/third_party/pub_patches/fluttertoast/example/macos/Runner/Configs/Warnings.xcconfig
@@ -0,0 +1,13 @@
+WARNING_CFLAGS = -Wall -Wconditional-uninitialized -Wnullable-to-nonnull-conversion -Wmissing-method-return-type -Woverlength-strings
+GCC_WARN_UNDECLARED_SELECTOR = YES
+CLANG_UNDEFINED_BEHAVIOR_SANITIZER_NULLABILITY = YES
+CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE
+CLANG_WARN__DUPLICATE_METHOD_MATCH = YES
+CLANG_WARN_PRAGMA_PACK = YES
+CLANG_WARN_STRICT_PROTOTYPES = YES
+CLANG_WARN_COMMA = YES
+GCC_WARN_STRICT_SELECTOR_MATCH = YES
+CLANG_WARN_OBJC_REPEATED_USE_OF_WEAK = YES
+CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES
+GCC_WARN_SHADOW = YES
+CLANG_WARN_UNREACHABLE_CODE = YES
diff --git a/third_party/pub_patches/fluttertoast/example/macos/Runner/DebugProfile.entitlements b/third_party/pub_patches/fluttertoast/example/macos/Runner/DebugProfile.entitlements
new file mode 100644
index 000000000..dddb8a30c
--- /dev/null
+++ b/third_party/pub_patches/fluttertoast/example/macos/Runner/DebugProfile.entitlements
@@ -0,0 +1,12 @@
+
+
+
+
+ com.apple.security.app-sandbox
+
+ com.apple.security.cs.allow-jit
+
+ com.apple.security.network.server
+
+
+
diff --git a/third_party/pub_patches/fluttertoast/example/macos/Runner/Info.plist b/third_party/pub_patches/fluttertoast/example/macos/Runner/Info.plist
new file mode 100644
index 000000000..4789daa6a
--- /dev/null
+++ b/third_party/pub_patches/fluttertoast/example/macos/Runner/Info.plist
@@ -0,0 +1,32 @@
+
+
+
+
+ CFBundleDevelopmentRegion
+ $(DEVELOPMENT_LANGUAGE)
+ CFBundleExecutable
+ $(EXECUTABLE_NAME)
+ CFBundleIconFile
+
+ CFBundleIdentifier
+ $(PRODUCT_BUNDLE_IDENTIFIER)
+ CFBundleInfoDictionaryVersion
+ 6.0
+ CFBundleName
+ $(PRODUCT_NAME)
+ CFBundlePackageType
+ APPL
+ CFBundleShortVersionString
+ $(FLUTTER_BUILD_NAME)
+ CFBundleVersion
+ $(FLUTTER_BUILD_NUMBER)
+ LSMinimumSystemVersion
+ $(MACOSX_DEPLOYMENT_TARGET)
+ NSHumanReadableCopyright
+ $(PRODUCT_COPYRIGHT)
+ NSMainNibFile
+ MainMenu
+ NSPrincipalClass
+ NSApplication
+
+
diff --git a/third_party/pub_patches/fluttertoast/example/macos/Runner/MainFlutterWindow.swift b/third_party/pub_patches/fluttertoast/example/macos/Runner/MainFlutterWindow.swift
new file mode 100644
index 000000000..2722837ec
--- /dev/null
+++ b/third_party/pub_patches/fluttertoast/example/macos/Runner/MainFlutterWindow.swift
@@ -0,0 +1,15 @@
+import Cocoa
+import FlutterMacOS
+
+class MainFlutterWindow: NSWindow {
+ override func awakeFromNib() {
+ let flutterViewController = FlutterViewController.init()
+ let windowFrame = self.frame
+ self.contentViewController = flutterViewController
+ self.setFrame(windowFrame, display: true)
+
+ RegisterGeneratedPlugins(registry: flutterViewController)
+
+ super.awakeFromNib()
+ }
+}
diff --git a/third_party/pub_patches/fluttertoast/example/macos/Runner/Release.entitlements b/third_party/pub_patches/fluttertoast/example/macos/Runner/Release.entitlements
new file mode 100644
index 000000000..852fa1a47
--- /dev/null
+++ b/third_party/pub_patches/fluttertoast/example/macos/Runner/Release.entitlements
@@ -0,0 +1,8 @@
+
+
+
+
+ com.apple.security.app-sandbox
+
+
+
diff --git a/third_party/pub_patches/fluttertoast/example/pubspec.yaml b/third_party/pub_patches/fluttertoast/example/pubspec.yaml
new file mode 100644
index 000000000..14ac8e562
--- /dev/null
+++ b/third_party/pub_patches/fluttertoast/example/pubspec.yaml
@@ -0,0 +1,71 @@
+name: fluttertoast_example
+description: Demonstrates how to use the FlutterToast plugin.
+
+# The following line prevents the package from being accidentally published to
+# pub.dev using `pub publish`. This is preferred for private packages.
+publish_to: 'none' # Remove this line if you wish to publish to pub.dev
+
+environment:
+ sdk: '>=2.12.0 <4.0.0'
+
+dependencies:
+ flutter:
+ sdk: flutter
+
+ fluttertoast:
+ # When depending on this package from a real application you should use:
+ # FlutterToast: ^x.y.z
+ # See https://dart.dev/tools/pub/dependencies#version-constraints
+ # The example app is bundled with the plugin so we use a path dependency on
+ # the parent directory to use the current plugin's version.
+ path: ../
+
+ # The following adds the Cupertino Icons font to your application.
+ # Use with the CupertinoIcons class for iOS style icons.
+ cupertino_icons: ^1.0.2
+
+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:
+
+ # The following line ensures that the Material Icons font is
+ # included with your application, so that you can use the icons in
+ # the material Icons class.
+ uses-material-design: true
+
+ # To add assets to your application, add an assets section, like this:
+ # assets:
+ # - images/a_dot_burr.jpeg
+ # - images/a_dot_ham.jpeg
+
+ # An image asset can refer to one or more resolution-specific "variants", see
+ # https://flutter.dev/assets-and-images/#resolution-aware.
+
+ # For details regarding adding assets from package dependencies, see
+ # https://flutter.dev/assets-and-images/#from-packages
+
+ # To add custom fonts to your application, 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 from package dependencies,
+ # see https://flutter.dev/custom-fonts/#from-packages
diff --git a/third_party/pub_patches/fluttertoast/example/test/widget_test.dart b/third_party/pub_patches/fluttertoast/example/test/widget_test.dart
new file mode 100644
index 000000000..e3b992916
--- /dev/null
+++ b/third_party/pub_patches/fluttertoast/example/test/widget_test.dart
@@ -0,0 +1,27 @@
+// This is a basic Flutter widget test.
+//
+// To perform an interaction with a widget in your test, use the WidgetTester
+// utility that Flutter provides. For example, you can send tap and scroll
+// gestures. You can also use WidgetTester to find child widgets in the widget
+// tree, read text, and verify that the values of widget properties are correct.
+
+import 'package:flutter/material.dart';
+import 'package:flutter_test/flutter_test.dart';
+
+import 'package:fluttertoast_example/main.dart';
+
+void main() {
+ testWidgets('Verify Platform version', (WidgetTester tester) async {
+ // Build our app and trigger a frame.
+ await tester.pumpWidget(MyApp());
+
+ // Verify that platform version is retrieved.
+ expect(
+ find.byWidgetPredicate(
+ (Widget widget) => widget is Text &&
+ widget.data!.startsWith('Running on:'),
+ ),
+ findsOneWidget,
+ );
+ });
+}
diff --git a/third_party/pub_patches/fluttertoast/example/web/favicon.png b/third_party/pub_patches/fluttertoast/example/web/favicon.png
new file mode 100644
index 000000000..8aaa46ac1
Binary files /dev/null and b/third_party/pub_patches/fluttertoast/example/web/favicon.png differ
diff --git a/third_party/pub_patches/fluttertoast/example/web/icons/Icon-192.png b/third_party/pub_patches/fluttertoast/example/web/icons/Icon-192.png
new file mode 100644
index 000000000..b749bfef0
Binary files /dev/null and b/third_party/pub_patches/fluttertoast/example/web/icons/Icon-192.png differ
diff --git a/third_party/pub_patches/fluttertoast/example/web/icons/Icon-512.png b/third_party/pub_patches/fluttertoast/example/web/icons/Icon-512.png
new file mode 100644
index 000000000..88cfd48df
Binary files /dev/null and b/third_party/pub_patches/fluttertoast/example/web/icons/Icon-512.png differ
diff --git a/third_party/pub_patches/fluttertoast/example/web/icons/Icon-maskable-192.png b/third_party/pub_patches/fluttertoast/example/web/icons/Icon-maskable-192.png
new file mode 100644
index 000000000..eb9b4d76e
Binary files /dev/null and b/third_party/pub_patches/fluttertoast/example/web/icons/Icon-maskable-192.png differ
diff --git a/third_party/pub_patches/fluttertoast/example/web/icons/Icon-maskable-512.png b/third_party/pub_patches/fluttertoast/example/web/icons/Icon-maskable-512.png
new file mode 100644
index 000000000..d69c56691
Binary files /dev/null and b/third_party/pub_patches/fluttertoast/example/web/icons/Icon-maskable-512.png differ
diff --git a/third_party/pub_patches/fluttertoast/example/web/index.html b/third_party/pub_patches/fluttertoast/example/web/index.html
new file mode 100644
index 000000000..41b3bc336
--- /dev/null
+++ b/third_party/pub_patches/fluttertoast/example/web/index.html
@@ -0,0 +1,58 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ example
+
+
+
+
+
+
+
+
+
+
diff --git a/third_party/pub_patches/fluttertoast/example/web/manifest.json b/third_party/pub_patches/fluttertoast/example/web/manifest.json
new file mode 100644
index 000000000..096edf8fe
--- /dev/null
+++ b/third_party/pub_patches/fluttertoast/example/web/manifest.json
@@ -0,0 +1,35 @@
+{
+ "name": "example",
+ "short_name": "example",
+ "start_url": ".",
+ "display": "standalone",
+ "background_color": "#0175C2",
+ "theme_color": "#0175C2",
+ "description": "A new Flutter project.",
+ "orientation": "portrait-primary",
+ "prefer_related_applications": false,
+ "icons": [
+ {
+ "src": "icons/Icon-192.png",
+ "sizes": "192x192",
+ "type": "image/png"
+ },
+ {
+ "src": "icons/Icon-512.png",
+ "sizes": "512x512",
+ "type": "image/png"
+ },
+ {
+ "src": "icons/Icon-maskable-192.png",
+ "sizes": "192x192",
+ "type": "image/png",
+ "purpose": "maskable"
+ },
+ {
+ "src": "icons/Icon-maskable-512.png",
+ "sizes": "512x512",
+ "type": "image/png",
+ "purpose": "maskable"
+ }
+ ]
+}
diff --git a/third_party/pub_patches/fluttertoast/example/windows/CMakeLists.txt b/third_party/pub_patches/fluttertoast/example/windows/CMakeLists.txt
new file mode 100644
index 000000000..c0270746b
--- /dev/null
+++ b/third_party/pub_patches/fluttertoast/example/windows/CMakeLists.txt
@@ -0,0 +1,101 @@
+# Project-level configuration.
+cmake_minimum_required(VERSION 3.14)
+project(example LANGUAGES CXX)
+
+# The name of the executable created for the application. Change this to change
+# the on-disk name of your application.
+set(BINARY_NAME "example")
+
+# Explicitly opt in to modern CMake behaviors to avoid warnings with recent
+# versions of CMake.
+cmake_policy(SET CMP0063 NEW)
+
+# Define build configuration option.
+get_property(IS_MULTICONFIG GLOBAL PROPERTY GENERATOR_IS_MULTI_CONFIG)
+if(IS_MULTICONFIG)
+ set(CMAKE_CONFIGURATION_TYPES "Debug;Profile;Release"
+ CACHE STRING "" FORCE)
+else()
+ if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES)
+ set(CMAKE_BUILD_TYPE "Debug" CACHE
+ STRING "Flutter build mode" FORCE)
+ set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS
+ "Debug" "Profile" "Release")
+ endif()
+endif()
+# Define settings for the Profile build mode.
+set(CMAKE_EXE_LINKER_FLAGS_PROFILE "${CMAKE_EXE_LINKER_FLAGS_RELEASE}")
+set(CMAKE_SHARED_LINKER_FLAGS_PROFILE "${CMAKE_SHARED_LINKER_FLAGS_RELEASE}")
+set(CMAKE_C_FLAGS_PROFILE "${CMAKE_C_FLAGS_RELEASE}")
+set(CMAKE_CXX_FLAGS_PROFILE "${CMAKE_CXX_FLAGS_RELEASE}")
+
+# Use Unicode for all projects.
+add_definitions(-DUNICODE -D_UNICODE)
+
+# Compilation settings that should be applied to most targets.
+#
+# Be cautious about adding new options here, as plugins use this function by
+# default. In most cases, you should add new options to specific targets instead
+# of modifying this function.
+function(APPLY_STANDARD_SETTINGS TARGET)
+ target_compile_features(${TARGET} PUBLIC cxx_std_17)
+ target_compile_options(${TARGET} PRIVATE /W4 /WX /wd"4100")
+ target_compile_options(${TARGET} PRIVATE /EHsc)
+ target_compile_definitions(${TARGET} PRIVATE "_HAS_EXCEPTIONS=0")
+ target_compile_definitions(${TARGET} PRIVATE "$<$:_DEBUG>")
+endfunction()
+
+# Flutter library and tool build rules.
+set(FLUTTER_MANAGED_DIR "${CMAKE_CURRENT_SOURCE_DIR}/flutter")
+add_subdirectory(${FLUTTER_MANAGED_DIR})
+
+# Application build; see runner/CMakeLists.txt.
+add_subdirectory("runner")
+
+# Generated plugin build rules, which manage building the plugins and adding
+# them to the application.
+include(flutter/generated_plugins.cmake)
+
+
+# === Installation ===
+# Support files are copied into place next to the executable, so that it can
+# run in place. This is done instead of making a separate bundle (as on Linux)
+# so that building and running from within Visual Studio will work.
+set(BUILD_BUNDLE_DIR "$")
+# Make the "install" step default, as it's required to run.
+set(CMAKE_VS_INCLUDE_INSTALL_TO_DEFAULT_BUILD 1)
+if(CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT)
+ set(CMAKE_INSTALL_PREFIX "${BUILD_BUNDLE_DIR}" CACHE PATH "..." FORCE)
+endif()
+
+set(INSTALL_BUNDLE_DATA_DIR "${CMAKE_INSTALL_PREFIX}/data")
+set(INSTALL_BUNDLE_LIB_DIR "${CMAKE_INSTALL_PREFIX}")
+
+install(TARGETS ${BINARY_NAME} RUNTIME DESTINATION "${CMAKE_INSTALL_PREFIX}"
+ COMPONENT Runtime)
+
+install(FILES "${FLUTTER_ICU_DATA_FILE}" DESTINATION "${INSTALL_BUNDLE_DATA_DIR}"
+ COMPONENT Runtime)
+
+install(FILES "${FLUTTER_LIBRARY}" DESTINATION "${INSTALL_BUNDLE_LIB_DIR}"
+ COMPONENT Runtime)
+
+if(PLUGIN_BUNDLED_LIBRARIES)
+ install(FILES "${PLUGIN_BUNDLED_LIBRARIES}"
+ DESTINATION "${INSTALL_BUNDLE_LIB_DIR}"
+ COMPONENT Runtime)
+endif()
+
+# Fully re-copy the assets directory on each build to avoid having stale files
+# from a previous install.
+set(FLUTTER_ASSET_DIR_NAME "flutter_assets")
+install(CODE "
+ file(REMOVE_RECURSE \"${INSTALL_BUNDLE_DATA_DIR}/${FLUTTER_ASSET_DIR_NAME}\")
+ " COMPONENT Runtime)
+install(DIRECTORY "${PROJECT_BUILD_DIR}/${FLUTTER_ASSET_DIR_NAME}"
+ DESTINATION "${INSTALL_BUNDLE_DATA_DIR}" COMPONENT Runtime)
+
+# Install the AOT library on non-Debug builds only.
+install(FILES "${AOT_LIBRARY}" DESTINATION "${INSTALL_BUNDLE_DATA_DIR}"
+ CONFIGURATIONS Profile;Release
+ COMPONENT Runtime)
diff --git a/third_party/pub_patches/fluttertoast/example/windows/flutter/CMakeLists.txt b/third_party/pub_patches/fluttertoast/example/windows/flutter/CMakeLists.txt
new file mode 100644
index 000000000..930d2071a
--- /dev/null
+++ b/third_party/pub_patches/fluttertoast/example/windows/flutter/CMakeLists.txt
@@ -0,0 +1,104 @@
+# This file controls Flutter-level build steps. It should not be edited.
+cmake_minimum_required(VERSION 3.14)
+
+set(EPHEMERAL_DIR "${CMAKE_CURRENT_SOURCE_DIR}/ephemeral")
+
+# Configuration provided via flutter tool.
+include(${EPHEMERAL_DIR}/generated_config.cmake)
+
+# TODO: Move the rest of this into files in ephemeral. See
+# https://github.com/flutter/flutter/issues/57146.
+set(WRAPPER_ROOT "${EPHEMERAL_DIR}/cpp_client_wrapper")
+
+# === Flutter Library ===
+set(FLUTTER_LIBRARY "${EPHEMERAL_DIR}/flutter_windows.dll")
+
+# Published to parent scope for install step.
+set(FLUTTER_LIBRARY ${FLUTTER_LIBRARY} PARENT_SCOPE)
+set(FLUTTER_ICU_DATA_FILE "${EPHEMERAL_DIR}/icudtl.dat" PARENT_SCOPE)
+set(PROJECT_BUILD_DIR "${PROJECT_DIR}/build/" PARENT_SCOPE)
+set(AOT_LIBRARY "${PROJECT_DIR}/build/windows/app.so" PARENT_SCOPE)
+
+list(APPEND FLUTTER_LIBRARY_HEADERS
+ "flutter_export.h"
+ "flutter_windows.h"
+ "flutter_messenger.h"
+ "flutter_plugin_registrar.h"
+ "flutter_texture_registrar.h"
+)
+list(TRANSFORM FLUTTER_LIBRARY_HEADERS PREPEND "${EPHEMERAL_DIR}/")
+add_library(flutter INTERFACE)
+target_include_directories(flutter INTERFACE
+ "${EPHEMERAL_DIR}"
+)
+target_link_libraries(flutter INTERFACE "${FLUTTER_LIBRARY}.lib")
+add_dependencies(flutter flutter_assemble)
+
+# === Wrapper ===
+list(APPEND CPP_WRAPPER_SOURCES_CORE
+ "core_implementations.cc"
+ "standard_codec.cc"
+)
+list(TRANSFORM CPP_WRAPPER_SOURCES_CORE PREPEND "${WRAPPER_ROOT}/")
+list(APPEND CPP_WRAPPER_SOURCES_PLUGIN
+ "plugin_registrar.cc"
+)
+list(TRANSFORM CPP_WRAPPER_SOURCES_PLUGIN PREPEND "${WRAPPER_ROOT}/")
+list(APPEND CPP_WRAPPER_SOURCES_APP
+ "flutter_engine.cc"
+ "flutter_view_controller.cc"
+)
+list(TRANSFORM CPP_WRAPPER_SOURCES_APP PREPEND "${WRAPPER_ROOT}/")
+
+# Wrapper sources needed for a plugin.
+add_library(flutter_wrapper_plugin STATIC
+ ${CPP_WRAPPER_SOURCES_CORE}
+ ${CPP_WRAPPER_SOURCES_PLUGIN}
+)
+apply_standard_settings(flutter_wrapper_plugin)
+set_target_properties(flutter_wrapper_plugin PROPERTIES
+ POSITION_INDEPENDENT_CODE ON)
+set_target_properties(flutter_wrapper_plugin PROPERTIES
+ CXX_VISIBILITY_PRESET hidden)
+target_link_libraries(flutter_wrapper_plugin PUBLIC flutter)
+target_include_directories(flutter_wrapper_plugin PUBLIC
+ "${WRAPPER_ROOT}/include"
+)
+add_dependencies(flutter_wrapper_plugin flutter_assemble)
+
+# Wrapper sources needed for the runner.
+add_library(flutter_wrapper_app STATIC
+ ${CPP_WRAPPER_SOURCES_CORE}
+ ${CPP_WRAPPER_SOURCES_APP}
+)
+apply_standard_settings(flutter_wrapper_app)
+target_link_libraries(flutter_wrapper_app PUBLIC flutter)
+target_include_directories(flutter_wrapper_app PUBLIC
+ "${WRAPPER_ROOT}/include"
+)
+add_dependencies(flutter_wrapper_app flutter_assemble)
+
+# === Flutter tool backend ===
+# _phony_ is a non-existent file to force this command to run every time,
+# since currently there's no way to get a full input/output list from the
+# flutter tool.
+set(PHONY_OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/_phony_")
+set_source_files_properties("${PHONY_OUTPUT}" PROPERTIES SYMBOLIC TRUE)
+add_custom_command(
+ OUTPUT ${FLUTTER_LIBRARY} ${FLUTTER_LIBRARY_HEADERS}
+ ${CPP_WRAPPER_SOURCES_CORE} ${CPP_WRAPPER_SOURCES_PLUGIN}
+ ${CPP_WRAPPER_SOURCES_APP}
+ ${PHONY_OUTPUT}
+ COMMAND ${CMAKE_COMMAND} -E env
+ ${FLUTTER_TOOL_ENVIRONMENT}
+ "${FLUTTER_ROOT}/packages/flutter_tools/bin/tool_backend.bat"
+ windows-x64 $
+ VERBATIM
+)
+add_custom_target(flutter_assemble DEPENDS
+ "${FLUTTER_LIBRARY}"
+ ${FLUTTER_LIBRARY_HEADERS}
+ ${CPP_WRAPPER_SOURCES_CORE}
+ ${CPP_WRAPPER_SOURCES_PLUGIN}
+ ${CPP_WRAPPER_SOURCES_APP}
+)
diff --git a/third_party/pub_patches/fluttertoast/example/windows/flutter/generated_plugin_registrant.cc b/third_party/pub_patches/fluttertoast/example/windows/flutter/generated_plugin_registrant.cc
new file mode 100644
index 000000000..8b6d4680a
--- /dev/null
+++ b/third_party/pub_patches/fluttertoast/example/windows/flutter/generated_plugin_registrant.cc
@@ -0,0 +1,11 @@
+//
+// Generated file. Do not edit.
+//
+
+// clang-format off
+
+#include "generated_plugin_registrant.h"
+
+
+void RegisterPlugins(flutter::PluginRegistry* registry) {
+}
diff --git a/third_party/pub_patches/fluttertoast/example/windows/flutter/generated_plugin_registrant.h b/third_party/pub_patches/fluttertoast/example/windows/flutter/generated_plugin_registrant.h
new file mode 100644
index 000000000..dc139d85a
--- /dev/null
+++ b/third_party/pub_patches/fluttertoast/example/windows/flutter/generated_plugin_registrant.h
@@ -0,0 +1,15 @@
+//
+// Generated file. Do not edit.
+//
+
+// clang-format off
+
+#ifndef GENERATED_PLUGIN_REGISTRANT_
+#define GENERATED_PLUGIN_REGISTRANT_
+
+#include
+
+// Registers Flutter plugins.
+void RegisterPlugins(flutter::PluginRegistry* registry);
+
+#endif // GENERATED_PLUGIN_REGISTRANT_
diff --git a/third_party/pub_patches/fluttertoast/example/windows/flutter/generated_plugins.cmake b/third_party/pub_patches/fluttertoast/example/windows/flutter/generated_plugins.cmake
new file mode 100644
index 000000000..b93c4c30c
--- /dev/null
+++ b/third_party/pub_patches/fluttertoast/example/windows/flutter/generated_plugins.cmake
@@ -0,0 +1,23 @@
+#
+# Generated file, do not edit.
+#
+
+list(APPEND FLUTTER_PLUGIN_LIST
+)
+
+list(APPEND FLUTTER_FFI_PLUGIN_LIST
+)
+
+set(PLUGIN_BUNDLED_LIBRARIES)
+
+foreach(plugin ${FLUTTER_PLUGIN_LIST})
+ add_subdirectory(flutter/ephemeral/.plugin_symlinks/${plugin}/windows plugins/${plugin})
+ target_link_libraries(${BINARY_NAME} PRIVATE ${plugin}_plugin)
+ list(APPEND PLUGIN_BUNDLED_LIBRARIES $)
+ list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${plugin}_bundled_libraries})
+endforeach(plugin)
+
+foreach(ffi_plugin ${FLUTTER_FFI_PLUGIN_LIST})
+ add_subdirectory(flutter/ephemeral/.plugin_symlinks/${ffi_plugin}/windows plugins/${ffi_plugin})
+ list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${ffi_plugin}_bundled_libraries})
+endforeach(ffi_plugin)
diff --git a/third_party/pub_patches/fluttertoast/example/windows/runner/CMakeLists.txt b/third_party/pub_patches/fluttertoast/example/windows/runner/CMakeLists.txt
new file mode 100644
index 000000000..17411a8ab
--- /dev/null
+++ b/third_party/pub_patches/fluttertoast/example/windows/runner/CMakeLists.txt
@@ -0,0 +1,39 @@
+cmake_minimum_required(VERSION 3.14)
+project(runner LANGUAGES CXX)
+
+# Define the application target. To change its name, change BINARY_NAME in the
+# top-level CMakeLists.txt, not the value here, or `flutter run` will no longer
+# work.
+#
+# Any new source files that you add to the application should be added here.
+add_executable(${BINARY_NAME} WIN32
+ "flutter_window.cpp"
+ "main.cpp"
+ "utils.cpp"
+ "win32_window.cpp"
+ "${FLUTTER_MANAGED_DIR}/generated_plugin_registrant.cc"
+ "Runner.rc"
+ "runner.exe.manifest"
+)
+
+# Apply the standard set of build settings. This can be removed for applications
+# that need different build settings.
+apply_standard_settings(${BINARY_NAME})
+
+# Add preprocessor definitions for the build version.
+target_compile_definitions(${BINARY_NAME} PRIVATE "FLUTTER_VERSION=\"${FLUTTER_VERSION}\"")
+target_compile_definitions(${BINARY_NAME} PRIVATE "FLUTTER_VERSION_MAJOR=${FLUTTER_VERSION_MAJOR}")
+target_compile_definitions(${BINARY_NAME} PRIVATE "FLUTTER_VERSION_MINOR=${FLUTTER_VERSION_MINOR}")
+target_compile_definitions(${BINARY_NAME} PRIVATE "FLUTTER_VERSION_PATCH=${FLUTTER_VERSION_PATCH}")
+target_compile_definitions(${BINARY_NAME} PRIVATE "FLUTTER_VERSION_BUILD=${FLUTTER_VERSION_BUILD}")
+
+# Disable Windows macros that collide with C++ standard library functions.
+target_compile_definitions(${BINARY_NAME} PRIVATE "NOMINMAX")
+
+# Add dependency libraries and include directories. Add any application-specific
+# dependencies here.
+target_link_libraries(${BINARY_NAME} PRIVATE flutter flutter_wrapper_app)
+target_include_directories(${BINARY_NAME} PRIVATE "${CMAKE_SOURCE_DIR}")
+
+# Run the Flutter tool portions of the build. This must not be removed.
+add_dependencies(${BINARY_NAME} flutter_assemble)
diff --git a/third_party/pub_patches/fluttertoast/example/windows/runner/Runner.rc b/third_party/pub_patches/fluttertoast/example/windows/runner/Runner.rc
new file mode 100644
index 000000000..0f5c08571
--- /dev/null
+++ b/third_party/pub_patches/fluttertoast/example/windows/runner/Runner.rc
@@ -0,0 +1,121 @@
+// Microsoft Visual C++ generated resource script.
+//
+#pragma code_page(65001)
+#include "resource.h"
+
+#define APSTUDIO_READONLY_SYMBOLS
+/////////////////////////////////////////////////////////////////////////////
+//
+// Generated from the TEXTINCLUDE 2 resource.
+//
+#include "winres.h"
+
+/////////////////////////////////////////////////////////////////////////////
+#undef APSTUDIO_READONLY_SYMBOLS
+
+/////////////////////////////////////////////////////////////////////////////
+// English (United States) resources
+
+#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU)
+LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
+
+#ifdef APSTUDIO_INVOKED
+/////////////////////////////////////////////////////////////////////////////
+//
+// TEXTINCLUDE
+//
+
+1 TEXTINCLUDE
+BEGIN
+ "resource.h\0"
+END
+
+2 TEXTINCLUDE
+BEGIN
+ "#include ""winres.h""\r\n"
+ "\0"
+END
+
+3 TEXTINCLUDE
+BEGIN
+ "\r\n"
+ "\0"
+END
+
+#endif // APSTUDIO_INVOKED
+
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// Icon
+//
+
+// Icon with lowest ID value placed first to ensure application icon
+// remains consistent on all systems.
+IDI_APP_ICON ICON "resources\\app_icon.ico"
+
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// Version
+//
+
+#if defined(FLUTTER_VERSION_MAJOR) && defined(FLUTTER_VERSION_MINOR) && defined(FLUTTER_VERSION_PATCH) && defined(FLUTTER_VERSION_BUILD)
+#define VERSION_AS_NUMBER FLUTTER_VERSION_MAJOR,FLUTTER_VERSION_MINOR,FLUTTER_VERSION_PATCH,FLUTTER_VERSION_BUILD
+#else
+#define VERSION_AS_NUMBER 1,0,0,0
+#endif
+
+#if defined(FLUTTER_VERSION)
+#define VERSION_AS_STRING FLUTTER_VERSION
+#else
+#define VERSION_AS_STRING "1.0.0"
+#endif
+
+VS_VERSION_INFO VERSIONINFO
+ FILEVERSION VERSION_AS_NUMBER
+ PRODUCTVERSION VERSION_AS_NUMBER
+ FILEFLAGSMASK VS_FFI_FILEFLAGSMASK
+#ifdef _DEBUG
+ FILEFLAGS VS_FF_DEBUG
+#else
+ FILEFLAGS 0x0L
+#endif
+ FILEOS VOS__WINDOWS32
+ FILETYPE VFT_APP
+ FILESUBTYPE 0x0L
+BEGIN
+ BLOCK "StringFileInfo"
+ BEGIN
+ BLOCK "040904e4"
+ BEGIN
+ VALUE "CompanyName", "com.example" "\0"
+ VALUE "FileDescription", "example" "\0"
+ VALUE "FileVersion", VERSION_AS_STRING "\0"
+ VALUE "InternalName", "example" "\0"
+ VALUE "LegalCopyright", "Copyright (C) 2022 com.example. All rights reserved." "\0"
+ VALUE "OriginalFilename", "example.exe" "\0"
+ VALUE "ProductName", "example" "\0"
+ VALUE "ProductVersion", VERSION_AS_STRING "\0"
+ END
+ END
+ BLOCK "VarFileInfo"
+ BEGIN
+ VALUE "Translation", 0x409, 1252
+ END
+END
+
+#endif // English (United States) resources
+/////////////////////////////////////////////////////////////////////////////
+
+
+
+#ifndef APSTUDIO_INVOKED
+/////////////////////////////////////////////////////////////////////////////
+//
+// Generated from the TEXTINCLUDE 3 resource.
+//
+
+
+/////////////////////////////////////////////////////////////////////////////
+#endif // not APSTUDIO_INVOKED
diff --git a/third_party/pub_patches/fluttertoast/example/windows/runner/flutter_window.cpp b/third_party/pub_patches/fluttertoast/example/windows/runner/flutter_window.cpp
new file mode 100644
index 000000000..b43b9095e
--- /dev/null
+++ b/third_party/pub_patches/fluttertoast/example/windows/runner/flutter_window.cpp
@@ -0,0 +1,61 @@
+#include "flutter_window.h"
+
+#include
+
+#include "flutter/generated_plugin_registrant.h"
+
+FlutterWindow::FlutterWindow(const flutter::DartProject& project)
+ : project_(project) {}
+
+FlutterWindow::~FlutterWindow() {}
+
+bool FlutterWindow::OnCreate() {
+ if (!Win32Window::OnCreate()) {
+ return false;
+ }
+
+ RECT frame = GetClientArea();
+
+ // The size here must match the window dimensions to avoid unnecessary surface
+ // creation / destruction in the startup path.
+ flutter_controller_ = std::make_unique(
+ frame.right - frame.left, frame.bottom - frame.top, project_);
+ // Ensure that basic setup of the controller was successful.
+ if (!flutter_controller_->engine() || !flutter_controller_->view()) {
+ return false;
+ }
+ RegisterPlugins(flutter_controller_->engine());
+ SetChildContent(flutter_controller_->view()->GetNativeWindow());
+ return true;
+}
+
+void FlutterWindow::OnDestroy() {
+ if (flutter_controller_) {
+ flutter_controller_ = nullptr;
+ }
+
+ Win32Window::OnDestroy();
+}
+
+LRESULT
+FlutterWindow::MessageHandler(HWND hwnd, UINT const message,
+ WPARAM const wparam,
+ LPARAM const lparam) noexcept {
+ // Give Flutter, including plugins, an opportunity to handle window messages.
+ if (flutter_controller_) {
+ std::optional result =
+ flutter_controller_->HandleTopLevelWindowProc(hwnd, message, wparam,
+ lparam);
+ if (result) {
+ return *result;
+ }
+ }
+
+ switch (message) {
+ case WM_FONTCHANGE:
+ flutter_controller_->engine()->ReloadSystemFonts();
+ break;
+ }
+
+ return Win32Window::MessageHandler(hwnd, message, wparam, lparam);
+}
diff --git a/third_party/pub_patches/fluttertoast/example/windows/runner/flutter_window.h b/third_party/pub_patches/fluttertoast/example/windows/runner/flutter_window.h
new file mode 100644
index 000000000..6da0652f0
--- /dev/null
+++ b/third_party/pub_patches/fluttertoast/example/windows/runner/flutter_window.h
@@ -0,0 +1,33 @@
+#ifndef RUNNER_FLUTTER_WINDOW_H_
+#define RUNNER_FLUTTER_WINDOW_H_
+
+#include
+#include
+
+#include
+
+#include "win32_window.h"
+
+// A window that does nothing but host a Flutter view.
+class FlutterWindow : public Win32Window {
+ public:
+ // Creates a new FlutterWindow hosting a Flutter view running |project|.
+ explicit FlutterWindow(const flutter::DartProject& project);
+ virtual ~FlutterWindow();
+
+ protected:
+ // Win32Window:
+ bool OnCreate() override;
+ void OnDestroy() override;
+ LRESULT MessageHandler(HWND window, UINT const message, WPARAM const wparam,
+ LPARAM const lparam) noexcept override;
+
+ private:
+ // The project to run.
+ flutter::DartProject project_;
+
+ // The Flutter instance hosted by this window.
+ std::unique_ptr flutter_controller_;
+};
+
+#endif // RUNNER_FLUTTER_WINDOW_H_
diff --git a/third_party/pub_patches/fluttertoast/example/windows/runner/main.cpp b/third_party/pub_patches/fluttertoast/example/windows/runner/main.cpp
new file mode 100644
index 000000000..bcb57b0e2
--- /dev/null
+++ b/third_party/pub_patches/fluttertoast/example/windows/runner/main.cpp
@@ -0,0 +1,43 @@
+#include
+#include
+#include
+
+#include "flutter_window.h"
+#include "utils.h"
+
+int APIENTRY wWinMain(_In_ HINSTANCE instance, _In_opt_ HINSTANCE prev,
+ _In_ wchar_t *command_line, _In_ int show_command) {
+ // Attach to console when present (e.g., 'flutter run') or create a
+ // new console when running with a debugger.
+ if (!::AttachConsole(ATTACH_PARENT_PROCESS) && ::IsDebuggerPresent()) {
+ CreateAndAttachConsole();
+ }
+
+ // Initialize COM, so that it is available for use in the library and/or
+ // plugins.
+ ::CoInitializeEx(nullptr, COINIT_APARTMENTTHREADED);
+
+ flutter::DartProject project(L"data");
+
+ std::vector command_line_arguments =
+ GetCommandLineArguments();
+
+ project.set_dart_entrypoint_arguments(std::move(command_line_arguments));
+
+ FlutterWindow window(project);
+ Win32Window::Point origin(10, 10);
+ Win32Window::Size size(1280, 720);
+ if (!window.CreateAndShow(L"example", origin, size)) {
+ return EXIT_FAILURE;
+ }
+ window.SetQuitOnClose(true);
+
+ ::MSG msg;
+ while (::GetMessage(&msg, nullptr, 0, 0)) {
+ ::TranslateMessage(&msg);
+ ::DispatchMessage(&msg);
+ }
+
+ ::CoUninitialize();
+ return EXIT_SUCCESS;
+}
diff --git a/third_party/pub_patches/fluttertoast/example/windows/runner/resource.h b/third_party/pub_patches/fluttertoast/example/windows/runner/resource.h
new file mode 100644
index 000000000..66a65d1e4
--- /dev/null
+++ b/third_party/pub_patches/fluttertoast/example/windows/runner/resource.h
@@ -0,0 +1,16 @@
+//{{NO_DEPENDENCIES}}
+// Microsoft Visual C++ generated include file.
+// Used by Runner.rc
+//
+#define IDI_APP_ICON 101
+
+// Next default values for new objects
+//
+#ifdef APSTUDIO_INVOKED
+#ifndef APSTUDIO_READONLY_SYMBOLS
+#define _APS_NEXT_RESOURCE_VALUE 102
+#define _APS_NEXT_COMMAND_VALUE 40001
+#define _APS_NEXT_CONTROL_VALUE 1001
+#define _APS_NEXT_SYMED_VALUE 101
+#endif
+#endif
diff --git a/third_party/pub_patches/fluttertoast/example/windows/runner/resources/app_icon.ico b/third_party/pub_patches/fluttertoast/example/windows/runner/resources/app_icon.ico
new file mode 100644
index 000000000..c04e20caf
Binary files /dev/null and b/third_party/pub_patches/fluttertoast/example/windows/runner/resources/app_icon.ico differ
diff --git a/third_party/pub_patches/fluttertoast/example/windows/runner/runner.exe.manifest b/third_party/pub_patches/fluttertoast/example/windows/runner/runner.exe.manifest
new file mode 100644
index 000000000..a42ea7687
--- /dev/null
+++ b/third_party/pub_patches/fluttertoast/example/windows/runner/runner.exe.manifest
@@ -0,0 +1,20 @@
+
+
+
+
+ PerMonitorV2
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/third_party/pub_patches/fluttertoast/example/windows/runner/utils.cpp b/third_party/pub_patches/fluttertoast/example/windows/runner/utils.cpp
new file mode 100644
index 000000000..f5bf9fa0f
--- /dev/null
+++ b/third_party/pub_patches/fluttertoast/example/windows/runner/utils.cpp
@@ -0,0 +1,64 @@
+#include "utils.h"
+
+#include
+#include
+#include
+#include
+
+#include
+
+void CreateAndAttachConsole() {
+ if (::AllocConsole()) {
+ FILE *unused;
+ if (freopen_s(&unused, "CONOUT$", "w", stdout)) {
+ _dup2(_fileno(stdout), 1);
+ }
+ if (freopen_s(&unused, "CONOUT$", "w", stderr)) {
+ _dup2(_fileno(stdout), 2);
+ }
+ std::ios::sync_with_stdio();
+ FlutterDesktopResyncOutputStreams();
+ }
+}
+
+std::vector GetCommandLineArguments() {
+ // Convert the UTF-16 command line arguments to UTF-8 for the Engine to use.
+ int argc;
+ wchar_t** argv = ::CommandLineToArgvW(::GetCommandLineW(), &argc);
+ if (argv == nullptr) {
+ return std::vector();
+ }
+
+ std::vector command_line_arguments;
+
+ // Skip the first argument as it's the binary name.
+ for (int i = 1; i < argc; i++) {
+ command_line_arguments.push_back(Utf8FromUtf16(argv[i]));
+ }
+
+ ::LocalFree(argv);
+
+ return command_line_arguments;
+}
+
+std::string Utf8FromUtf16(const wchar_t* utf16_string) {
+ if (utf16_string == nullptr) {
+ return std::string();
+ }
+ int target_length = ::WideCharToMultiByte(
+ CP_UTF8, WC_ERR_INVALID_CHARS, utf16_string,
+ -1, nullptr, 0, nullptr, nullptr);
+ std::string utf8_string;
+ if (target_length == 0 || target_length > utf8_string.max_size()) {
+ return utf8_string;
+ }
+ utf8_string.resize(target_length);
+ int converted_length = ::WideCharToMultiByte(
+ CP_UTF8, WC_ERR_INVALID_CHARS, utf16_string,
+ -1, utf8_string.data(),
+ target_length, nullptr, nullptr);
+ if (converted_length == 0) {
+ return std::string();
+ }
+ return utf8_string;
+}
diff --git a/third_party/pub_patches/fluttertoast/example/windows/runner/utils.h b/third_party/pub_patches/fluttertoast/example/windows/runner/utils.h
new file mode 100644
index 000000000..3879d5475
--- /dev/null
+++ b/third_party/pub_patches/fluttertoast/example/windows/runner/utils.h
@@ -0,0 +1,19 @@
+#ifndef RUNNER_UTILS_H_
+#define RUNNER_UTILS_H_
+
+#include
+#include
+
+// Creates a console for the process, and redirects stdout and stderr to
+// it for both the runner and the Flutter library.
+void CreateAndAttachConsole();
+
+// Takes a null-terminated wchar_t* encoded in UTF-16 and returns a std::string
+// encoded in UTF-8. Returns an empty std::string on failure.
+std::string Utf8FromUtf16(const wchar_t* utf16_string);
+
+// Gets the command line arguments passed in as a std::vector,
+// encoded in UTF-8. Returns an empty std::vector on failure.
+std::vector GetCommandLineArguments();
+
+#endif // RUNNER_UTILS_H_
diff --git a/third_party/pub_patches/fluttertoast/example/windows/runner/win32_window.cpp b/third_party/pub_patches/fluttertoast/example/windows/runner/win32_window.cpp
new file mode 100644
index 000000000..c10f08dc7
--- /dev/null
+++ b/third_party/pub_patches/fluttertoast/example/windows/runner/win32_window.cpp
@@ -0,0 +1,245 @@
+#include "win32_window.h"
+
+#include
+
+#include "resource.h"
+
+namespace {
+
+constexpr const wchar_t kWindowClassName[] = L"FLUTTER_RUNNER_WIN32_WINDOW";
+
+// The number of Win32Window objects that currently exist.
+static int g_active_window_count = 0;
+
+using EnableNonClientDpiScaling = BOOL __stdcall(HWND hwnd);
+
+// Scale helper to convert logical scaler values to physical using passed in
+// scale factor
+int Scale(int source, double scale_factor) {
+ return static_cast(source * scale_factor);
+}
+
+// Dynamically loads the |EnableNonClientDpiScaling| from the User32 module.
+// This API is only needed for PerMonitor V1 awareness mode.
+void EnableFullDpiSupportIfAvailable(HWND hwnd) {
+ HMODULE user32_module = LoadLibraryA("User32.dll");
+ if (!user32_module) {
+ return;
+ }
+ auto enable_non_client_dpi_scaling =
+ reinterpret_cast(
+ GetProcAddress(user32_module, "EnableNonClientDpiScaling"));
+ if (enable_non_client_dpi_scaling != nullptr) {
+ enable_non_client_dpi_scaling(hwnd);
+ FreeLibrary(user32_module);
+ }
+}
+
+} // namespace
+
+// Manages the Win32Window's window class registration.
+class WindowClassRegistrar {
+ public:
+ ~WindowClassRegistrar() = default;
+
+ // Returns the singleton registar instance.
+ static WindowClassRegistrar* GetInstance() {
+ if (!instance_) {
+ instance_ = new WindowClassRegistrar();
+ }
+ return instance_;
+ }
+
+ // Returns the name of the window class, registering the class if it hasn't
+ // previously been registered.
+ const wchar_t* GetWindowClass();
+
+ // Unregisters the window class. Should only be called if there are no
+ // instances of the window.
+ void UnregisterWindowClass();
+
+ private:
+ WindowClassRegistrar() = default;
+
+ static WindowClassRegistrar* instance_;
+
+ bool class_registered_ = false;
+};
+
+WindowClassRegistrar* WindowClassRegistrar::instance_ = nullptr;
+
+const wchar_t* WindowClassRegistrar::GetWindowClass() {
+ if (!class_registered_) {
+ WNDCLASS window_class{};
+ window_class.hCursor = LoadCursor(nullptr, IDC_ARROW);
+ window_class.lpszClassName = kWindowClassName;
+ window_class.style = CS_HREDRAW | CS_VREDRAW;
+ window_class.cbClsExtra = 0;
+ window_class.cbWndExtra = 0;
+ window_class.hInstance = GetModuleHandle(nullptr);
+ window_class.hIcon =
+ LoadIcon(window_class.hInstance, MAKEINTRESOURCE(IDI_APP_ICON));
+ window_class.hbrBackground = 0;
+ window_class.lpszMenuName = nullptr;
+ window_class.lpfnWndProc = Win32Window::WndProc;
+ RegisterClass(&window_class);
+ class_registered_ = true;
+ }
+ return kWindowClassName;
+}
+
+void WindowClassRegistrar::UnregisterWindowClass() {
+ UnregisterClass(kWindowClassName, nullptr);
+ class_registered_ = false;
+}
+
+Win32Window::Win32Window() {
+ ++g_active_window_count;
+}
+
+Win32Window::~Win32Window() {
+ --g_active_window_count;
+ Destroy();
+}
+
+bool Win32Window::CreateAndShow(const std::wstring& title,
+ const Point& origin,
+ const Size& size) {
+ Destroy();
+
+ const wchar_t* window_class =
+ WindowClassRegistrar::GetInstance()->GetWindowClass();
+
+ const POINT target_point = {static_cast(origin.x),
+ static_cast(origin.y)};
+ HMONITOR monitor = MonitorFromPoint(target_point, MONITOR_DEFAULTTONEAREST);
+ UINT dpi = FlutterDesktopGetDpiForMonitor(monitor);
+ double scale_factor = dpi / 96.0;
+
+ HWND window = CreateWindow(
+ window_class, title.c_str(), WS_OVERLAPPEDWINDOW | WS_VISIBLE,
+ Scale(origin.x, scale_factor), Scale(origin.y, scale_factor),
+ Scale(size.width, scale_factor), Scale(size.height, scale_factor),
+ nullptr, nullptr, GetModuleHandle(nullptr), this);
+
+ if (!window) {
+ return false;
+ }
+
+ return OnCreate();
+}
+
+// static
+LRESULT CALLBACK Win32Window::WndProc(HWND const window,
+ UINT const message,
+ WPARAM const wparam,
+ LPARAM const lparam) noexcept {
+ if (message == WM_NCCREATE) {
+ auto window_struct = reinterpret_cast(lparam);
+ SetWindowLongPtr(window, GWLP_USERDATA,
+ reinterpret_cast(window_struct->lpCreateParams));
+
+ auto that = static_cast