Dubhe

add: first commit

Showing 100 changed files with 3959 additions and 0 deletions

Too many changes to show.

To preserve performance only 100 of 100+ files are displayed.

  1 +# Miscellaneous
  2 +*.class
  3 +*.log
  4 +*.pyc
  5 +*.swp
  6 +.DS_Store
  7 +.atom/
  8 +.buildlog/
  9 +.history
  10 +.svn/
  11 +migrate_working_dir/
  12 +
  13 +# IntelliJ related
  14 +*.iml
  15 +*.ipr
  16 +*.iws
  17 +.idea/
  18 +
  19 +# The .vscode folder contains launch configuration and tasks you configure in
  20 +# VS Code which you may wish to be included in version control, so this line
  21 +# is commented out by default.
  22 +#.vscode/
  23 +
  24 +# Flutter/Dart/Pub related
  25 +# Libraries should not include pubspec.lock, per https://dart.dev/guides/libraries/private-files#pubspeclock.
  26 +/pubspec.lock
  27 +**/doc/api/
  28 +.dart_tool/
  29 +build/
  1 +# This file tracks properties of this Flutter project.
  2 +# Used by Flutter tool to assess capabilities and perform upgrades etc.
  3 +#
  4 +# This file should be version controlled and should not be manually edited.
  5 +
  6 +version:
  7 + revision: "78666c8dc57e9f7548ca9f8dd0740fbf0c658dc9"
  8 + channel: "stable"
  9 +
  10 +project_type: plugin
  11 +
  12 +# Tracks metadata for the flutter migrate command
  13 +migration:
  14 + platforms:
  15 + - platform: root
  16 + create_revision: 78666c8dc57e9f7548ca9f8dd0740fbf0c658dc9
  17 + base_revision: 78666c8dc57e9f7548ca9f8dd0740fbf0c658dc9
  18 + - platform: android
  19 + create_revision: 78666c8dc57e9f7548ca9f8dd0740fbf0c658dc9
  20 + base_revision: 78666c8dc57e9f7548ca9f8dd0740fbf0c658dc9
  21 + - platform: ios
  22 + create_revision: 78666c8dc57e9f7548ca9f8dd0740fbf0c658dc9
  23 + base_revision: 78666c8dc57e9f7548ca9f8dd0740fbf0c658dc9
  24 +
  25 + # User provided section
  26 +
  27 + # List of Local paths (relative to this file) that should be
  28 + # ignored by the migrate tool.
  29 + #
  30 + # Files that are not part of the templates will be ignored by default.
  31 + unmanaged_files:
  32 + - 'lib/main.dart'
  33 + - 'ios/Runner.xcodeproj/project.pbxproj'
  1 +## 0.0.1
  2 +
  3 +* TODO: Describe initial release.
  1 +TODO: Add your license here.
  1 +# auto_track
  2 +
  3 +Auto Track Plugin
  4 +
  5 +## Getting Started
  6 +
  7 +This project is a starting point for a Flutter
  8 +[plug-in package](https://flutter.dev/developing-packages/),
  9 +a specialized package that includes platform-specific implementation code for
  10 +Android and/or iOS.
  11 +
  12 +For help getting started with Flutter development, view the
  13 +[online documentation](https://flutter.dev/docs), which offers tutorials,
  14 +samples, guidance on mobile development, and a full API reference.
  15 +
  1 +include: package:flutter_lints/flutter.yaml
  2 +
  3 +# Additional information about this file can be found at
  4 +# https://dart.dev/guides/language/analysis-options
  1 +*.iml
  2 +.gradle
  3 +/local.properties
  4 +/.idea/workspace.xml
  5 +/.idea/libraries
  6 +.DS_Store
  7 +/build
  8 +/captures
  9 +.cxx
  1 +group 'mobi.iflow.flutter.auto_track.auto_track'
  2 +version '1.0-SNAPSHOT'
  3 +
  4 +buildscript {
  5 + ext.kotlin_version = '1.7.10'
  6 + repositories {
  7 + google()
  8 + mavenCentral()
  9 + }
  10 +
  11 + dependencies {
  12 + classpath 'com.android.tools.build:gradle:7.3.0'
  13 + classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
  14 + }
  15 +}
  16 +
  17 +allprojects {
  18 + repositories {
  19 + google()
  20 + mavenCentral()
  21 + }
  22 +}
  23 +
  24 +apply plugin: 'com.android.library'
  25 +apply plugin: 'kotlin-android'
  26 +
  27 +android {
  28 + if (project.android.hasProperty("namespace")) {
  29 + namespace 'mobi.iflow.flutter.auto_track.auto_track'
  30 + }
  31 +
  32 + compileSdkVersion 33
  33 +
  34 + compileOptions {
  35 + sourceCompatibility JavaVersion.VERSION_1_8
  36 + targetCompatibility JavaVersion.VERSION_1_8
  37 + }
  38 +
  39 + kotlinOptions {
  40 + jvmTarget = '1.8'
  41 + }
  42 +
  43 + sourceSets {
  44 + main.java.srcDirs += 'src/main/kotlin'
  45 + test.java.srcDirs += 'src/test/kotlin'
  46 + }
  47 +
  48 + defaultConfig {
  49 + minSdkVersion 19
  50 + }
  51 +
  52 + dependencies {
  53 + testImplementation 'org.jetbrains.kotlin:kotlin-test'
  54 + testImplementation 'org.mockito:mockito-core:5.0.0'
  55 + }
  56 +
  57 + testOptions {
  58 + unitTests.all {
  59 + useJUnitPlatform()
  60 +
  61 + testLogging {
  62 + events "passed", "skipped", "failed", "standardOut", "standardError"
  63 + outputs.upToDateWhen {false}
  64 + showStandardStreams = true
  65 + }
  66 + }
  67 + }
  68 +}
  1 +rootProject.name = 'auto_track'
  1 +<manifest xmlns:android="http://schemas.android.com/apk/res/android"
  2 + package="mobi.iflow.flutter.auto_track.auto_track">
  3 +</manifest>
  1 +package mobi.iflow.flutter.auto_track.auto_track
  2 +
  3 +import androidx.annotation.NonNull
  4 +
  5 +import io.flutter.embedding.engine.plugins.FlutterPlugin
  6 +import io.flutter.plugin.common.MethodCall
  7 +import io.flutter.plugin.common.MethodChannel
  8 +import io.flutter.plugin.common.MethodChannel.MethodCallHandler
  9 +import io.flutter.plugin.common.MethodChannel.Result
  10 +
  11 +/** AutoTrackPlugin */
  12 +class AutoTrackPlugin: FlutterPlugin, MethodCallHandler {
  13 + /// The MethodChannel that will the communication between Flutter and native Android
  14 + ///
  15 + /// This local reference serves to register the plugin with the Flutter Engine and unregister it
  16 + /// when the Flutter Engine is detached from the Activity
  17 + private lateinit var channel : MethodChannel
  18 +
  19 + override fun onAttachedToEngine(flutterPluginBinding: FlutterPlugin.FlutterPluginBinding) {
  20 + channel = MethodChannel(flutterPluginBinding.binaryMessenger, "auto_track")
  21 + channel.setMethodCallHandler(this)
  22 + }
  23 +
  24 + override fun onMethodCall(call: MethodCall, result: Result) {
  25 + if (call.method == "getPlatformVersion") {
  26 + result.success("Android ${android.os.Build.VERSION.RELEASE}")
  27 + } else {
  28 + result.notImplemented()
  29 + }
  30 + }
  31 +
  32 + override fun onDetachedFromEngine(binding: FlutterPlugin.FlutterPluginBinding) {
  33 + channel.setMethodCallHandler(null)
  34 + }
  35 +}
  1 +package mobi.iflow.flutter.auto_track.auto_track
  2 +
  3 +import io.flutter.plugin.common.MethodCall
  4 +import io.flutter.plugin.common.MethodChannel
  5 +import kotlin.test.Test
  6 +import org.mockito.Mockito
  7 +
  8 +/*
  9 + * This demonstrates a simple unit test of the Kotlin portion of this plugin's implementation.
  10 + *
  11 + * Once you have built the plugin's example app, you can run these tests from the command
  12 + * line by running `./gradlew testDebugUnitTest` in the `example/android/` directory, or
  13 + * you can run them directly from IDEs that support JUnit such as Android Studio.
  14 + */
  15 +
  16 +internal class AutoTrackPluginTest {
  17 + @Test
  18 + fun onMethodCall_getPlatformVersion_returnsExpectedValue() {
  19 + val plugin = AutoTrackPlugin()
  20 +
  21 + val call = MethodCall("getPlatformVersion", null)
  22 + val mockResult: MethodChannel.Result = Mockito.mock(MethodChannel.Result::class.java)
  23 + plugin.onMethodCall(call, mockResult)
  24 +
  25 + Mockito.verify(mockResult).success("Android " + android.os.Build.VERSION.RELEASE)
  26 + }
  27 +}
  1 +# Miscellaneous
  2 +*.class
  3 +*.log
  4 +*.pyc
  5 +*.swp
  6 +.DS_Store
  7 +.atom/
  8 +.buildlog/
  9 +.history
  10 +.svn/
  11 +migrate_working_dir/
  12 +
  13 +# IntelliJ related
  14 +*.iml
  15 +*.ipr
  16 +*.iws
  17 +.idea/
  18 +
  19 +# The .vscode folder contains launch configuration and tasks you configure in
  20 +# VS Code which you may wish to be included in version control, so this line
  21 +# is commented out by default.
  22 +#.vscode/
  23 +
  24 +# Flutter/Dart/Pub related
  25 +**/doc/api/
  26 +**/ios/Flutter/.last_build_id
  27 +.dart_tool/
  28 +.flutter-plugins
  29 +.flutter-plugins-dependencies
  30 +.pub-cache/
  31 +.pub/
  32 +/build/
  33 +
  34 +# Symbolication related
  35 +app.*.symbols
  36 +
  37 +# Obfuscation related
  38 +app.*.map.json
  39 +
  40 +# Android Studio will place build artifacts here
  41 +/android/app/debug
  42 +/android/app/profile
  43 +/android/app/release
  1 +# auto_track_example
  2 +
  3 +Demonstrates how to use the auto_track plugin.
  4 +
  5 +## Getting Started
  6 +
  7 +This project is a starting point for a Flutter application.
  8 +
  9 +A few resources to get you started if this is your first Flutter project:
  10 +
  11 +- [Lab: Write your first Flutter app](https://docs.flutter.dev/get-started/codelab)
  12 +- [Cookbook: Useful Flutter samples](https://docs.flutter.dev/cookbook)
  13 +
  14 +For help getting started with Flutter development, view the
  15 +[online documentation](https://docs.flutter.dev/), which offers tutorials,
  16 +samples, guidance on mobile development, and a full API reference.
  1 +# This file configures the analyzer, which statically analyzes Dart code to
  2 +# check for errors, warnings, and lints.
  3 +#
  4 +# The issues identified by the analyzer are surfaced in the UI of Dart-enabled
  5 +# IDEs (https://dart.dev/tools#ides-and-editors). The analyzer can also be
  6 +# invoked from the command line by running `flutter analyze`.
  7 +
  8 +# The following line activates a set of recommended lints for Flutter apps,
  9 +# packages, and plugins designed to encourage good coding practices.
  10 +include: package:flutter_lints/flutter.yaml
  11 +
  12 +linter:
  13 + # The lint rules applied to this project can be customized in the
  14 + # section below to disable rules from the `package:flutter_lints/flutter.yaml`
  15 + # included above or to enable additional rules. A list of all available lints
  16 + # and their documentation is published at https://dart.dev/lints.
  17 + #
  18 + # Instead of disabling a lint rule for the entire project in the
  19 + # section below, it can also be suppressed for a single line of code
  20 + # or a specific dart file by using the `// ignore: name_of_lint` and
  21 + # `// ignore_for_file: name_of_lint` syntax on the line or in the file
  22 + # producing the lint.
  23 + rules:
  24 + # avoid_print: false # Uncomment to disable the `avoid_print` rule
  25 + # prefer_single_quotes: true # Uncomment to enable the `prefer_single_quotes` rule
  26 +
  27 +# Additional information about this file can be found at
  28 +# https://dart.dev/guides/language/analysis-options
  1 +gradle-wrapper.jar
  2 +/.gradle
  3 +/captures/
  4 +/gradlew
  5 +/gradlew.bat
  6 +/local.properties
  7 +GeneratedPluginRegistrant.java
  8 +
  9 +# Remember to never publicly share your keystore.
  10 +# See https://flutter.dev/docs/deployment/android#reference-the-keystore-from-the-app
  11 +key.properties
  12 +**/*.keystore
  13 +**/*.jks
  1 +plugins {
  2 + id "com.android.application"
  3 + id "kotlin-android"
  4 + id "dev.flutter.flutter-gradle-plugin"
  5 +}
  6 +
  7 +def localProperties = new Properties()
  8 +def localPropertiesFile = rootProject.file('local.properties')
  9 +if (localPropertiesFile.exists()) {
  10 + localPropertiesFile.withReader('UTF-8') { reader ->
  11 + localProperties.load(reader)
  12 + }
  13 +}
  14 +
  15 +def flutterVersionCode = localProperties.getProperty('flutter.versionCode')
  16 +if (flutterVersionCode == null) {
  17 + flutterVersionCode = '1'
  18 +}
  19 +
  20 +def flutterVersionName = localProperties.getProperty('flutter.versionName')
  21 +if (flutterVersionName == null) {
  22 + flutterVersionName = '1.0'
  23 +}
  24 +
  25 +android {
  26 + namespace "mobi.iflow.flutter.auto_track.auto_track_example"
  27 + compileSdkVersion flutter.compileSdkVersion
  28 + ndkVersion flutter.ndkVersion
  29 +
  30 + compileOptions {
  31 + sourceCompatibility JavaVersion.VERSION_1_8
  32 + targetCompatibility JavaVersion.VERSION_1_8
  33 + }
  34 +
  35 + kotlinOptions {
  36 + jvmTarget = '1.8'
  37 + }
  38 +
  39 + sourceSets {
  40 + main.java.srcDirs += 'src/main/kotlin'
  41 + }
  42 +
  43 + defaultConfig {
  44 + // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html).
  45 + applicationId "mobi.iflow.flutter.auto_track.auto_track_example"
  46 + // You can update the following values to match your application needs.
  47 + // For more information, see: https://docs.flutter.dev/deployment/android#reviewing-the-gradle-build-configuration.
  48 + minSdkVersion flutter.minSdkVersion
  49 + targetSdkVersion flutter.targetSdkVersion
  50 + versionCode flutterVersionCode.toInteger()
  51 + versionName flutterVersionName
  52 + }
  53 +
  54 + buildTypes {
  55 + release {
  56 + // TODO: Add your own signing config for the release build.
  57 + // Signing with the debug keys for now, so `flutter run --release` works.
  58 + signingConfig signingConfigs.debug
  59 + }
  60 + }
  61 +}
  62 +
  63 +flutter {
  64 + source '../..'
  65 +}
  66 +
  67 +dependencies {}
  1 +<manifest xmlns:android="http://schemas.android.com/apk/res/android">
  2 + <!-- The INTERNET permission is required for development. Specifically,
  3 + the Flutter tool needs it to communicate with the running application
  4 + to allow setting breakpoints, to provide hot reload, etc.
  5 + -->
  6 + <uses-permission android:name="android.permission.INTERNET"/>
  7 +</manifest>
  1 +<manifest xmlns:android="http://schemas.android.com/apk/res/android">
  2 + <application
  3 + android:label="auto_track_example"
  4 + android:name="${applicationName}"
  5 + android:icon="@mipmap/ic_launcher">
  6 + <activity
  7 + android:name=".MainActivity"
  8 + android:exported="true"
  9 + android:launchMode="singleTop"
  10 + android:theme="@style/LaunchTheme"
  11 + android:configChanges="orientation|keyboardHidden|keyboard|screenSize|smallestScreenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode"
  12 + android:hardwareAccelerated="true"
  13 + android:windowSoftInputMode="adjustResize">
  14 + <!-- Specifies an Android theme to apply to this Activity as soon as
  15 + the Android process has started. This theme is visible to the user
  16 + while the Flutter UI initializes. After that, this theme continues
  17 + to determine the Window background behind the Flutter UI. -->
  18 + <meta-data
  19 + android:name="io.flutter.embedding.android.NormalTheme"
  20 + android:resource="@style/NormalTheme"
  21 + />
  22 + <intent-filter>
  23 + <action android:name="android.intent.action.MAIN"/>
  24 + <category android:name="android.intent.category.LAUNCHER"/>
  25 + </intent-filter>
  26 + </activity>
  27 + <!-- Don't delete the meta-data below.
  28 + This is used by the Flutter tool to generate GeneratedPluginRegistrant.java -->
  29 + <meta-data
  30 + android:name="flutterEmbedding"
  31 + android:value="2" />
  32 + </application>
  33 +</manifest>
  1 +package mobi.iflow.flutter.auto_track.auto_track_example
  2 +
  3 +import io.flutter.embedding.android.FlutterActivity
  4 +
  5 +class MainActivity: FlutterActivity() {
  6 +}
  1 +<?xml version="1.0" encoding="utf-8"?>
  2 +<!-- Modify this file to customize your launch splash screen -->
  3 +<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
  4 + <item android:drawable="?android:colorBackground" />
  5 +
  6 + <!-- You can insert your own image assets here -->
  7 + <!-- <item>
  8 + <bitmap
  9 + android:gravity="center"
  10 + android:src="@mipmap/launch_image" />
  11 + </item> -->
  12 +</layer-list>
  1 +<?xml version="1.0" encoding="utf-8"?>
  2 +<!-- Modify this file to customize your launch splash screen -->
  3 +<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
  4 + <item android:drawable="@android:color/white" />
  5 +
  6 + <!-- You can insert your own image assets here -->
  7 + <!-- <item>
  8 + <bitmap
  9 + android:gravity="center"
  10 + android:src="@mipmap/launch_image" />
  11 + </item> -->
  12 +</layer-list>
  1 +<?xml version="1.0" encoding="utf-8"?>
  2 +<resources>
  3 + <!-- Theme applied to the Android Window while the process is starting when the OS's Dark Mode setting is on -->
  4 + <style name="LaunchTheme" parent="@android:style/Theme.Black.NoTitleBar">
  5 + <!-- Show a splash screen on the activity. Automatically removed when
  6 + the Flutter engine draws its first frame -->
  7 + <item name="android:windowBackground">@drawable/launch_background</item>
  8 + </style>
  9 + <!-- Theme applied to the Android Window as soon as the process has started.
  10 + This theme determines the color of the Android Window while your
  11 + Flutter UI initializes, as well as behind your Flutter UI while its
  12 + running.
  13 +
  14 + This Theme is only used starting with V2 of Flutter's Android embedding. -->
  15 + <style name="NormalTheme" parent="@android:style/Theme.Black.NoTitleBar">
  16 + <item name="android:windowBackground">?android:colorBackground</item>
  17 + </style>
  18 +</resources>
  1 +<?xml version="1.0" encoding="utf-8"?>
  2 +<resources>
  3 + <!-- Theme applied to the Android Window while the process is starting when the OS's Dark Mode setting is off -->
  4 + <style name="LaunchTheme" parent="@android:style/Theme.Light.NoTitleBar">
  5 + <!-- Show a splash screen on the activity. Automatically removed when
  6 + the Flutter engine draws its first frame -->
  7 + <item name="android:windowBackground">@drawable/launch_background</item>
  8 + </style>
  9 + <!-- Theme applied to the Android Window as soon as the process has started.
  10 + This theme determines the color of the Android Window while your
  11 + Flutter UI initializes, as well as behind your Flutter UI while its
  12 + running.
  13 +
  14 + This Theme is only used starting with V2 of Flutter's Android embedding. -->
  15 + <style name="NormalTheme" parent="@android:style/Theme.Light.NoTitleBar">
  16 + <item name="android:windowBackground">?android:colorBackground</item>
  17 + </style>
  18 +</resources>
  1 +<manifest xmlns:android="http://schemas.android.com/apk/res/android">
  2 + <!-- The INTERNET permission is required for development. Specifically,
  3 + the Flutter tool needs it to communicate with the running application
  4 + to allow setting breakpoints, to provide hot reload, etc.
  5 + -->
  6 + <uses-permission android:name="android.permission.INTERNET"/>
  7 +</manifest>
  1 +buildscript {
  2 + ext.kotlin_version = '1.7.10'
  3 + repositories {
  4 + google()
  5 + mavenCentral()
  6 + }
  7 +
  8 + dependencies {
  9 + classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
  10 + }
  11 +}
  12 +
  13 +allprojects {
  14 + repositories {
  15 + google()
  16 + mavenCentral()
  17 + }
  18 +}
  19 +
  20 +rootProject.buildDir = '../build'
  21 +subprojects {
  22 + project.buildDir = "${rootProject.buildDir}/${project.name}"
  23 +}
  24 +subprojects {
  25 + project.evaluationDependsOn(':app')
  26 +}
  27 +
  28 +tasks.register("clean", Delete) {
  29 + delete rootProject.buildDir
  30 +}
  1 +org.gradle.jvmargs=-Xmx4G
  2 +android.useAndroidX=true
  3 +android.enableJetifier=true
  1 +distributionBase=GRADLE_USER_HOME
  2 +distributionPath=wrapper/dists
  3 +zipStoreBase=GRADLE_USER_HOME
  4 +zipStorePath=wrapper/dists
  5 +distributionUrl=https\://services.gradle.org/distributions/gradle-7.5-all.zip
  1 +pluginManagement {
  2 + def flutterSdkPath = {
  3 + def properties = new Properties()
  4 + file("local.properties").withInputStream { properties.load(it) }
  5 + def flutterSdkPath = properties.getProperty("flutter.sdk")
  6 + assert flutterSdkPath != null, "flutter.sdk not set in local.properties"
  7 + return flutterSdkPath
  8 + }
  9 + settings.ext.flutterSdkPath = flutterSdkPath()
  10 +
  11 + includeBuild("${settings.ext.flutterSdkPath}/packages/flutter_tools/gradle")
  12 +
  13 + repositories {
  14 + google()
  15 + mavenCentral()
  16 + gradlePluginPortal()
  17 + }
  18 +
  19 + plugins {
  20 + id "dev.flutter.flutter-gradle-plugin" version "1.0.0" apply false
  21 + }
  22 +}
  23 +
  24 +plugins {
  25 + id "dev.flutter.flutter-plugin-loader" version "1.0.0"
  26 + id "com.android.application" version "7.3.0" apply false
  27 +}
  28 +
  29 +include ":app"
  1 +// This is a basic Flutter integration test.
  2 +//
  3 +// Since integration tests run in a full Flutter application, they can interact
  4 +// with the host side of a plugin implementation, unlike Dart unit tests.
  5 +//
  6 +// For more information about Flutter integration tests, please see
  7 +// https://docs.flutter.dev/cookbook/testing/integration/introduction
  8 +
  9 +
  10 +import 'package:flutter_test/flutter_test.dart';
  11 +import 'package:integration_test/integration_test.dart';
  12 +
  13 +import 'package:auto_track/auto_track.dart';
  14 +
  15 +void main() {
  16 + IntegrationTestWidgetsFlutterBinding.ensureInitialized();
  17 +
  18 + testWidgets('getPlatformVersion test', (WidgetTester tester) async {
  19 + final AutoTrack plugin = AutoTrack();
  20 + // final String? version = await plugin.getPlatformVersion();
  21 + // The version string depends on the host platform running the test, so
  22 + // just assert that some non-empty string is returned.
  23 + // expect(version?.isNotEmpty, true);
  24 + });
  25 +}
  1 +**/dgph
  2 +*.mode1v3
  3 +*.mode2v3
  4 +*.moved-aside
  5 +*.pbxuser
  6 +*.perspectivev3
  7 +**/*sync/
  8 +.sconsign.dblite
  9 +.tags*
  10 +**/.vagrant/
  11 +**/DerivedData/
  12 +Icon?
  13 +**/Pods/
  14 +**/.symlinks/
  15 +profile
  16 +xcuserdata
  17 +**/.generated/
  18 +Flutter/App.framework
  19 +Flutter/Flutter.framework
  20 +Flutter/Flutter.podspec
  21 +Flutter/Generated.xcconfig
  22 +Flutter/ephemeral/
  23 +Flutter/app.flx
  24 +Flutter/app.zip
  25 +Flutter/flutter_assets/
  26 +Flutter/flutter_export_environment.sh
  27 +ServiceDefinitions.json
  28 +Runner/GeneratedPluginRegistrant.*
  29 +
  30 +# Exceptions to above rules.
  31 +!default.mode1v3
  32 +!default.mode2v3
  33 +!default.pbxuser
  34 +!default.perspectivev3
  1 +<?xml version="1.0" encoding="UTF-8"?>
  2 +<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
  3 +<plist version="1.0">
  4 +<dict>
  5 + <key>CFBundleDevelopmentRegion</key>
  6 + <string>en</string>
  7 + <key>CFBundleExecutable</key>
  8 + <string>App</string>
  9 + <key>CFBundleIdentifier</key>
  10 + <string>io.flutter.flutter.app</string>
  11 + <key>CFBundleInfoDictionaryVersion</key>
  12 + <string>6.0</string>
  13 + <key>CFBundleName</key>
  14 + <string>App</string>
  15 + <key>CFBundlePackageType</key>
  16 + <string>FMWK</string>
  17 + <key>CFBundleShortVersionString</key>
  18 + <string>1.0</string>
  19 + <key>CFBundleSignature</key>
  20 + <string>????</string>
  21 + <key>CFBundleVersion</key>
  22 + <string>1.0</string>
  23 + <key>MinimumOSVersion</key>
  24 + <string>11.0</string>
  25 +</dict>
  26 +</plist>
  1 +#include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"
  2 +#include "Generated.xcconfig"
  1 +#include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"
  2 +#include "Generated.xcconfig"
  1 +# Uncomment this line to define a global platform for your project
  2 +# platform :ios, '11.0'
  3 +
  4 +# CocoaPods analytics sends network stats synchronously affecting flutter build latency.
  5 +ENV['COCOAPODS_DISABLE_STATS'] = 'true'
  6 +
  7 +project 'Runner', {
  8 + 'Debug' => :debug,
  9 + 'Profile' => :release,
  10 + 'Release' => :release,
  11 +}
  12 +
  13 +def flutter_root
  14 + generated_xcode_build_settings_path = File.expand_path(File.join('..', 'Flutter', 'Generated.xcconfig'), __FILE__)
  15 + unless File.exist?(generated_xcode_build_settings_path)
  16 + raise "#{generated_xcode_build_settings_path} must exist. If you're running pod install manually, make sure flutter pub get is executed first"
  17 + end
  18 +
  19 + File.foreach(generated_xcode_build_settings_path) do |line|
  20 + matches = line.match(/FLUTTER_ROOT\=(.*)/)
  21 + return matches[1].strip if matches
  22 + end
  23 + raise "FLUTTER_ROOT not found in #{generated_xcode_build_settings_path}. Try deleting Generated.xcconfig, then run flutter pub get"
  24 +end
  25 +
  26 +require File.expand_path(File.join('packages', 'flutter_tools', 'bin', 'podhelper'), flutter_root)
  27 +
  28 +flutter_ios_podfile_setup
  29 +
  30 +target 'Runner' do
  31 + use_frameworks!
  32 + use_modular_headers!
  33 +
  34 + flutter_install_all_ios_pods File.dirname(File.realpath(__FILE__))
  35 + target 'RunnerTests' do
  36 + inherit! :search_paths
  37 + end
  38 +end
  39 +
  40 +post_install do |installer|
  41 + installer.pods_project.targets.each do |target|
  42 + flutter_additional_ios_build_settings(target)
  43 + end
  44 +end
  1 +PODS:
  2 + - auto_track (0.0.1):
  3 + - Flutter
  4 + - Flutter (1.0.0)
  5 + - integration_test (0.0.1):
  6 + - Flutter
  7 +
  8 +DEPENDENCIES:
  9 + - auto_track (from `.symlinks/plugins/auto_track/ios`)
  10 + - Flutter (from `Flutter`)
  11 + - integration_test (from `.symlinks/plugins/integration_test/ios`)
  12 +
  13 +EXTERNAL SOURCES:
  14 + auto_track:
  15 + :path: ".symlinks/plugins/auto_track/ios"
  16 + Flutter:
  17 + :path: Flutter
  18 + integration_test:
  19 + :path: ".symlinks/plugins/integration_test/ios"
  20 +
  21 +SPEC CHECKSUMS:
  22 + auto_track: bca6ccfba824abcc8de9d51ac0eeed3dfb141d5d
  23 + Flutter: f04841e97a9d0b0a8025694d0796dd46242b2854
  24 + integration_test: 13825b8a9334a850581300559b8839134b124670
  25 +
  26 +PODFILE CHECKSUM: 70d9d25280d0dd177a5f637cdb0f0b0b12c6a189
  27 +
  28 +COCOAPODS: 1.12.1
  1 +// !$*UTF8*$!
  2 +{
  3 + archiveVersion = 1;
  4 + classes = {
  5 + };
  6 + objectVersion = 54;
  7 + objects = {
  8 +
  9 +/* Begin PBXBuildFile section */
  10 + 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; };
  11 + 331C808B294A63AB00263BE5 /* RunnerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 331C807B294A618700263BE5 /* RunnerTests.swift */; };
  12 + 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; };
  13 + 74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74858FAE1ED2DC5600515810 /* AppDelegate.swift */; };
  14 + 868313707E054002C9EBBC23 /* Pods_RunnerTests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 830356C44B37234273CFF03C /* Pods_RunnerTests.framework */; };
  15 + 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; };
  16 + 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; };
  17 + 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; };
  18 + 9A4BC01B8C3E977CE74C747D /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DD602501C62F389F5FFD3029 /* Pods_Runner.framework */; };
  19 +/* End PBXBuildFile section */
  20 +
  21 +/* Begin PBXContainerItemProxy section */
  22 + 331C8085294A63A400263BE5 /* PBXContainerItemProxy */ = {
  23 + isa = PBXContainerItemProxy;
  24 + containerPortal = 97C146E61CF9000F007C117D /* Project object */;
  25 + proxyType = 1;
  26 + remoteGlobalIDString = 97C146ED1CF9000F007C117D;
  27 + remoteInfo = Runner;
  28 + };
  29 +/* End PBXContainerItemProxy section */
  30 +
  31 +/* Begin PBXCopyFilesBuildPhase section */
  32 + 9705A1C41CF9048500538489 /* Embed Frameworks */ = {
  33 + isa = PBXCopyFilesBuildPhase;
  34 + buildActionMask = 2147483647;
  35 + dstPath = "";
  36 + dstSubfolderSpec = 10;
  37 + files = (
  38 + );
  39 + name = "Embed Frameworks";
  40 + runOnlyForDeploymentPostprocessing = 0;
  41 + };
  42 +/* End PBXCopyFilesBuildPhase section */
  43 +
  44 +/* Begin PBXFileReference section */
  45 + 12AD15FE86700BFEC11CC92D /* Pods-RunnerTests.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.profile.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.profile.xcconfig"; sourceTree = "<group>"; };
  46 + 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GeneratedPluginRegistrant.h; sourceTree = "<group>"; };
  47 + 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = "<group>"; };
  48 + 229DB93165071F31B9EA4231 /* 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 = "<group>"; };
  49 + 331C807B294A618700263BE5 /* RunnerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RunnerTests.swift; sourceTree = "<group>"; };
  50 + 331C8081294A63A400263BE5 /* RunnerTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = RunnerTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
  51 + 361BA2F76E56261380B96FE7 /* 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 = "<group>"; };
  52 + 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = "<group>"; };
  53 + 74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Runner-Bridging-Header.h"; sourceTree = "<group>"; };
  54 + 74858FAE1ED2DC5600515810 /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
  55 + 7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = "<group>"; };
  56 + 830356C44B37234273CFF03C /* Pods_RunnerTests.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_RunnerTests.framework; sourceTree = BUILT_PRODUCTS_DIR; };
  57 + 9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Debug.xcconfig; path = Flutter/Debug.xcconfig; sourceTree = "<group>"; };
  58 + 9740EEB31CF90195004384FC /* Generated.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Generated.xcconfig; path = Flutter/Generated.xcconfig; sourceTree = "<group>"; };
  59 + 97C146EE1CF9000F007C117D /* Runner.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Runner.app; sourceTree = BUILT_PRODUCTS_DIR; };
  60 + 97C146FB1CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = "<group>"; };
  61 + 97C146FD1CF9000F007C117D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
  62 + 97C147001CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = "<group>"; };
  63 + 97C147021CF9000F007C117D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
  64 + D1653FC9B1E82CA1821FD739 /* 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 = "<group>"; };
  65 + DD602501C62F389F5FFD3029 /* Pods_Runner.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Runner.framework; sourceTree = BUILT_PRODUCTS_DIR; };
  66 + EB9A8197B759B617DFCCB6F1 /* Pods-RunnerTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.debug.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.debug.xcconfig"; sourceTree = "<group>"; };
  67 + F782189BA737B0E3C9A3C330 /* Pods-RunnerTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.release.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.release.xcconfig"; sourceTree = "<group>"; };
  68 +/* End PBXFileReference section */
  69 +
  70 +/* Begin PBXFrameworksBuildPhase section */
  71 + 93DAF74BCB585A23752E0704 /* Frameworks */ = {
  72 + isa = PBXFrameworksBuildPhase;
  73 + buildActionMask = 2147483647;
  74 + files = (
  75 + 868313707E054002C9EBBC23 /* Pods_RunnerTests.framework in Frameworks */,
  76 + );
  77 + runOnlyForDeploymentPostprocessing = 0;
  78 + };
  79 + 97C146EB1CF9000F007C117D /* Frameworks */ = {
  80 + isa = PBXFrameworksBuildPhase;
  81 + buildActionMask = 2147483647;
  82 + files = (
  83 + 9A4BC01B8C3E977CE74C747D /* Pods_Runner.framework in Frameworks */,
  84 + );
  85 + runOnlyForDeploymentPostprocessing = 0;
  86 + };
  87 +/* End PBXFrameworksBuildPhase section */
  88 +
  89 +/* Begin PBXGroup section */
  90 + 331C8082294A63A400263BE5 /* RunnerTests */ = {
  91 + isa = PBXGroup;
  92 + children = (
  93 + 331C807B294A618700263BE5 /* RunnerTests.swift */,
  94 + );
  95 + path = RunnerTests;
  96 + sourceTree = "<group>";
  97 + };
  98 + 5F8A6DB13075A4D449F6A685 /* Frameworks */ = {
  99 + isa = PBXGroup;
  100 + children = (
  101 + DD602501C62F389F5FFD3029 /* Pods_Runner.framework */,
  102 + 830356C44B37234273CFF03C /* Pods_RunnerTests.framework */,
  103 + );
  104 + name = Frameworks;
  105 + sourceTree = "<group>";
  106 + };
  107 + 936DC10DBAFF5951E82260FA /* Pods */ = {
  108 + isa = PBXGroup;
  109 + children = (
  110 + D1653FC9B1E82CA1821FD739 /* Pods-Runner.debug.xcconfig */,
  111 + 229DB93165071F31B9EA4231 /* Pods-Runner.release.xcconfig */,
  112 + 361BA2F76E56261380B96FE7 /* Pods-Runner.profile.xcconfig */,
  113 + EB9A8197B759B617DFCCB6F1 /* Pods-RunnerTests.debug.xcconfig */,
  114 + F782189BA737B0E3C9A3C330 /* Pods-RunnerTests.release.xcconfig */,
  115 + 12AD15FE86700BFEC11CC92D /* Pods-RunnerTests.profile.xcconfig */,
  116 + );
  117 + name = Pods;
  118 + path = Pods;
  119 + sourceTree = "<group>";
  120 + };
  121 + 9740EEB11CF90186004384FC /* Flutter */ = {
  122 + isa = PBXGroup;
  123 + children = (
  124 + 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */,
  125 + 9740EEB21CF90195004384FC /* Debug.xcconfig */,
  126 + 7AFA3C8E1D35360C0083082E /* Release.xcconfig */,
  127 + 9740EEB31CF90195004384FC /* Generated.xcconfig */,
  128 + );
  129 + name = Flutter;
  130 + sourceTree = "<group>";
  131 + };
  132 + 97C146E51CF9000F007C117D = {
  133 + isa = PBXGroup;
  134 + children = (
  135 + 9740EEB11CF90186004384FC /* Flutter */,
  136 + 97C146F01CF9000F007C117D /* Runner */,
  137 + 97C146EF1CF9000F007C117D /* Products */,
  138 + 331C8082294A63A400263BE5 /* RunnerTests */,
  139 + 936DC10DBAFF5951E82260FA /* Pods */,
  140 + 5F8A6DB13075A4D449F6A685 /* Frameworks */,
  141 + );
  142 + sourceTree = "<group>";
  143 + };
  144 + 97C146EF1CF9000F007C117D /* Products */ = {
  145 + isa = PBXGroup;
  146 + children = (
  147 + 97C146EE1CF9000F007C117D /* Runner.app */,
  148 + 331C8081294A63A400263BE5 /* RunnerTests.xctest */,
  149 + );
  150 + name = Products;
  151 + sourceTree = "<group>";
  152 + };
  153 + 97C146F01CF9000F007C117D /* Runner */ = {
  154 + isa = PBXGroup;
  155 + children = (
  156 + 97C146FA1CF9000F007C117D /* Main.storyboard */,
  157 + 97C146FD1CF9000F007C117D /* Assets.xcassets */,
  158 + 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */,
  159 + 97C147021CF9000F007C117D /* Info.plist */,
  160 + 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */,
  161 + 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */,
  162 + 74858FAE1ED2DC5600515810 /* AppDelegate.swift */,
  163 + 74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */,
  164 + );
  165 + path = Runner;
  166 + sourceTree = "<group>";
  167 + };
  168 +/* End PBXGroup section */
  169 +
  170 +/* Begin PBXNativeTarget section */
  171 + 331C8080294A63A400263BE5 /* RunnerTests */ = {
  172 + isa = PBXNativeTarget;
  173 + buildConfigurationList = 331C8087294A63A400263BE5 /* Build configuration list for PBXNativeTarget "RunnerTests" */;
  174 + buildPhases = (
  175 + E48DE49AC08A847B9639DEEA /* [CP] Check Pods Manifest.lock */,
  176 + 331C807D294A63A400263BE5 /* Sources */,
  177 + 331C807F294A63A400263BE5 /* Resources */,
  178 + 93DAF74BCB585A23752E0704 /* Frameworks */,
  179 + );
  180 + buildRules = (
  181 + );
  182 + dependencies = (
  183 + 331C8086294A63A400263BE5 /* PBXTargetDependency */,
  184 + );
  185 + name = RunnerTests;
  186 + productName = RunnerTests;
  187 + productReference = 331C8081294A63A400263BE5 /* RunnerTests.xctest */;
  188 + productType = "com.apple.product-type.bundle.unit-test";
  189 + };
  190 + 97C146ED1CF9000F007C117D /* Runner */ = {
  191 + isa = PBXNativeTarget;
  192 + buildConfigurationList = 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */;
  193 + buildPhases = (
  194 + 34BC5E1D1DEA8AEF16370400 /* [CP] Check Pods Manifest.lock */,
  195 + 9740EEB61CF901F6004384FC /* Run Script */,
  196 + 97C146EA1CF9000F007C117D /* Sources */,
  197 + 97C146EB1CF9000F007C117D /* Frameworks */,
  198 + 97C146EC1CF9000F007C117D /* Resources */,
  199 + 9705A1C41CF9048500538489 /* Embed Frameworks */,
  200 + 3B06AD1E1E4923F5004D2608 /* Thin Binary */,
  201 + DE97054EB3448BA88001379F /* [CP] Embed Pods Frameworks */,
  202 + );
  203 + buildRules = (
  204 + );
  205 + dependencies = (
  206 + );
  207 + name = Runner;
  208 + productName = Runner;
  209 + productReference = 97C146EE1CF9000F007C117D /* Runner.app */;
  210 + productType = "com.apple.product-type.application";
  211 + };
  212 +/* End PBXNativeTarget section */
  213 +
  214 +/* Begin PBXProject section */
  215 + 97C146E61CF9000F007C117D /* Project object */ = {
  216 + isa = PBXProject;
  217 + attributes = {
  218 + BuildIndependentTargetsInParallel = YES;
  219 + LastUpgradeCheck = 1430;
  220 + ORGANIZATIONNAME = "";
  221 + TargetAttributes = {
  222 + 331C8080294A63A400263BE5 = {
  223 + CreatedOnToolsVersion = 14.0;
  224 + TestTargetID = 97C146ED1CF9000F007C117D;
  225 + };
  226 + 97C146ED1CF9000F007C117D = {
  227 + CreatedOnToolsVersion = 7.3.1;
  228 + LastSwiftMigration = 1100;
  229 + };
  230 + };
  231 + };
  232 + buildConfigurationList = 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */;
  233 + compatibilityVersion = "Xcode 9.3";
  234 + developmentRegion = en;
  235 + hasScannedForEncodings = 0;
  236 + knownRegions = (
  237 + en,
  238 + Base,
  239 + );
  240 + mainGroup = 97C146E51CF9000F007C117D;
  241 + productRefGroup = 97C146EF1CF9000F007C117D /* Products */;
  242 + projectDirPath = "";
  243 + projectRoot = "";
  244 + targets = (
  245 + 97C146ED1CF9000F007C117D /* Runner */,
  246 + 331C8080294A63A400263BE5 /* RunnerTests */,
  247 + );
  248 + };
  249 +/* End PBXProject section */
  250 +
  251 +/* Begin PBXResourcesBuildPhase section */
  252 + 331C807F294A63A400263BE5 /* Resources */ = {
  253 + isa = PBXResourcesBuildPhase;
  254 + buildActionMask = 2147483647;
  255 + files = (
  256 + );
  257 + runOnlyForDeploymentPostprocessing = 0;
  258 + };
  259 + 97C146EC1CF9000F007C117D /* Resources */ = {
  260 + isa = PBXResourcesBuildPhase;
  261 + buildActionMask = 2147483647;
  262 + files = (
  263 + 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */,
  264 + 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */,
  265 + 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */,
  266 + 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */,
  267 + );
  268 + runOnlyForDeploymentPostprocessing = 0;
  269 + };
  270 +/* End PBXResourcesBuildPhase section */
  271 +
  272 +/* Begin PBXShellScriptBuildPhase section */
  273 + 34BC5E1D1DEA8AEF16370400 /* [CP] Check Pods Manifest.lock */ = {
  274 + isa = PBXShellScriptBuildPhase;
  275 + buildActionMask = 2147483647;
  276 + files = (
  277 + );
  278 + inputFileListPaths = (
  279 + );
  280 + inputPaths = (
  281 + "${PODS_PODFILE_DIR_PATH}/Podfile.lock",
  282 + "${PODS_ROOT}/Manifest.lock",
  283 + );
  284 + name = "[CP] Check Pods Manifest.lock";
  285 + outputFileListPaths = (
  286 + );
  287 + outputPaths = (
  288 + "$(DERIVED_FILE_DIR)/Pods-Runner-checkManifestLockResult.txt",
  289 + );
  290 + runOnlyForDeploymentPostprocessing = 0;
  291 + shellPath = /bin/sh;
  292 + 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";
  293 + showEnvVarsInLog = 0;
  294 + };
  295 + 3B06AD1E1E4923F5004D2608 /* Thin Binary */ = {
  296 + isa = PBXShellScriptBuildPhase;
  297 + alwaysOutOfDate = 1;
  298 + buildActionMask = 2147483647;
  299 + files = (
  300 + );
  301 + inputPaths = (
  302 + "${TARGET_BUILD_DIR}/${INFOPLIST_PATH}",
  303 + );
  304 + name = "Thin Binary";
  305 + outputPaths = (
  306 + );
  307 + runOnlyForDeploymentPostprocessing = 0;
  308 + shellPath = /bin/sh;
  309 + shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" embed_and_thin";
  310 + };
  311 + 9740EEB61CF901F6004384FC /* Run Script */ = {
  312 + isa = PBXShellScriptBuildPhase;
  313 + alwaysOutOfDate = 1;
  314 + buildActionMask = 2147483647;
  315 + files = (
  316 + );
  317 + inputPaths = (
  318 + );
  319 + name = "Run Script";
  320 + outputPaths = (
  321 + );
  322 + runOnlyForDeploymentPostprocessing = 0;
  323 + shellPath = /bin/sh;
  324 + shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" build";
  325 + };
  326 + DE97054EB3448BA88001379F /* [CP] Embed Pods Frameworks */ = {
  327 + isa = PBXShellScriptBuildPhase;
  328 + buildActionMask = 2147483647;
  329 + files = (
  330 + );
  331 + inputFileListPaths = (
  332 + "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-input-files.xcfilelist",
  333 + );
  334 + name = "[CP] Embed Pods Frameworks";
  335 + outputFileListPaths = (
  336 + "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-output-files.xcfilelist",
  337 + );
  338 + runOnlyForDeploymentPostprocessing = 0;
  339 + shellPath = /bin/sh;
  340 + shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh\"\n";
  341 + showEnvVarsInLog = 0;
  342 + };
  343 + E48DE49AC08A847B9639DEEA /* [CP] Check Pods Manifest.lock */ = {
  344 + isa = PBXShellScriptBuildPhase;
  345 + buildActionMask = 2147483647;
  346 + files = (
  347 + );
  348 + inputFileListPaths = (
  349 + );
  350 + inputPaths = (
  351 + "${PODS_PODFILE_DIR_PATH}/Podfile.lock",
  352 + "${PODS_ROOT}/Manifest.lock",
  353 + );
  354 + name = "[CP] Check Pods Manifest.lock";
  355 + outputFileListPaths = (
  356 + );
  357 + outputPaths = (
  358 + "$(DERIVED_FILE_DIR)/Pods-RunnerTests-checkManifestLockResult.txt",
  359 + );
  360 + runOnlyForDeploymentPostprocessing = 0;
  361 + shellPath = /bin/sh;
  362 + 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";
  363 + showEnvVarsInLog = 0;
  364 + };
  365 +/* End PBXShellScriptBuildPhase section */
  366 +
  367 +/* Begin PBXSourcesBuildPhase section */
  368 + 331C807D294A63A400263BE5 /* Sources */ = {
  369 + isa = PBXSourcesBuildPhase;
  370 + buildActionMask = 2147483647;
  371 + files = (
  372 + 331C808B294A63AB00263BE5 /* RunnerTests.swift in Sources */,
  373 + );
  374 + runOnlyForDeploymentPostprocessing = 0;
  375 + };
  376 + 97C146EA1CF9000F007C117D /* Sources */ = {
  377 + isa = PBXSourcesBuildPhase;
  378 + buildActionMask = 2147483647;
  379 + files = (
  380 + 74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */,
  381 + 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */,
  382 + );
  383 + runOnlyForDeploymentPostprocessing = 0;
  384 + };
  385 +/* End PBXSourcesBuildPhase section */
  386 +
  387 +/* Begin PBXTargetDependency section */
  388 + 331C8086294A63A400263BE5 /* PBXTargetDependency */ = {
  389 + isa = PBXTargetDependency;
  390 + target = 97C146ED1CF9000F007C117D /* Runner */;
  391 + targetProxy = 331C8085294A63A400263BE5 /* PBXContainerItemProxy */;
  392 + };
  393 +/* End PBXTargetDependency section */
  394 +
  395 +/* Begin PBXVariantGroup section */
  396 + 97C146FA1CF9000F007C117D /* Main.storyboard */ = {
  397 + isa = PBXVariantGroup;
  398 + children = (
  399 + 97C146FB1CF9000F007C117D /* Base */,
  400 + );
  401 + name = Main.storyboard;
  402 + sourceTree = "<group>";
  403 + };
  404 + 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */ = {
  405 + isa = PBXVariantGroup;
  406 + children = (
  407 + 97C147001CF9000F007C117D /* Base */,
  408 + );
  409 + name = LaunchScreen.storyboard;
  410 + sourceTree = "<group>";
  411 + };
  412 +/* End PBXVariantGroup section */
  413 +
  414 +/* Begin XCBuildConfiguration section */
  415 + 249021D3217E4FDB00AE95B9 /* Profile */ = {
  416 + isa = XCBuildConfiguration;
  417 + buildSettings = {
  418 + ALWAYS_SEARCH_USER_PATHS = NO;
  419 + CLANG_ANALYZER_NONNULL = YES;
  420 + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
  421 + CLANG_CXX_LIBRARY = "libc++";
  422 + CLANG_ENABLE_MODULES = YES;
  423 + CLANG_ENABLE_OBJC_ARC = YES;
  424 + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
  425 + CLANG_WARN_BOOL_CONVERSION = YES;
  426 + CLANG_WARN_COMMA = YES;
  427 + CLANG_WARN_CONSTANT_CONVERSION = YES;
  428 + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
  429 + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
  430 + CLANG_WARN_EMPTY_BODY = YES;
  431 + CLANG_WARN_ENUM_CONVERSION = YES;
  432 + CLANG_WARN_INFINITE_RECURSION = YES;
  433 + CLANG_WARN_INT_CONVERSION = YES;
  434 + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
  435 + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
  436 + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
  437 + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
  438 + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
  439 + CLANG_WARN_STRICT_PROTOTYPES = YES;
  440 + CLANG_WARN_SUSPICIOUS_MOVE = YES;
  441 + CLANG_WARN_UNREACHABLE_CODE = YES;
  442 + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
  443 + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
  444 + COPY_PHASE_STRIP = NO;
  445 + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
  446 + ENABLE_NS_ASSERTIONS = NO;
  447 + ENABLE_STRICT_OBJC_MSGSEND = YES;
  448 + GCC_C_LANGUAGE_STANDARD = gnu99;
  449 + GCC_NO_COMMON_BLOCKS = YES;
  450 + GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
  451 + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
  452 + GCC_WARN_UNDECLARED_SELECTOR = YES;
  453 + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
  454 + GCC_WARN_UNUSED_FUNCTION = YES;
  455 + GCC_WARN_UNUSED_VARIABLE = YES;
  456 + IPHONEOS_DEPLOYMENT_TARGET = 11.0;
  457 + MTL_ENABLE_DEBUG_INFO = NO;
  458 + SDKROOT = iphoneos;
  459 + SUPPORTED_PLATFORMS = iphoneos;
  460 + TARGETED_DEVICE_FAMILY = "1,2";
  461 + VALIDATE_PRODUCT = YES;
  462 + };
  463 + name = Profile;
  464 + };
  465 + 249021D4217E4FDB00AE95B9 /* Profile */ = {
  466 + isa = XCBuildConfiguration;
  467 + baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */;
  468 + buildSettings = {
  469 + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
  470 + CLANG_ENABLE_MODULES = YES;
  471 + CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
  472 + DEVELOPMENT_TEAM = FVLFR9QYGC;
  473 + ENABLE_BITCODE = NO;
  474 + INFOPLIST_FILE = Runner/Info.plist;
  475 + LD_RUNPATH_SEARCH_PATHS = (
  476 + "$(inherited)",
  477 + "@executable_path/Frameworks",
  478 + );
  479 + PRODUCT_BUNDLE_IDENTIFIER = mobi.iflow.flutter.autotrack.autoTrackExample;
  480 + PRODUCT_NAME = "$(TARGET_NAME)";
  481 + SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
  482 + SWIFT_VERSION = 5.0;
  483 + VERSIONING_SYSTEM = "apple-generic";
  484 + };
  485 + name = Profile;
  486 + };
  487 + 331C8088294A63A400263BE5 /* Debug */ = {
  488 + isa = XCBuildConfiguration;
  489 + baseConfigurationReference = EB9A8197B759B617DFCCB6F1 /* Pods-RunnerTests.debug.xcconfig */;
  490 + buildSettings = {
  491 + BUNDLE_LOADER = "$(TEST_HOST)";
  492 + CODE_SIGN_STYLE = Automatic;
  493 + CURRENT_PROJECT_VERSION = 1;
  494 + GENERATE_INFOPLIST_FILE = YES;
  495 + MARKETING_VERSION = 1.0;
  496 + PRODUCT_BUNDLE_IDENTIFIER = mobi.iflow.flutter.autotrack.autoTrackExample.RunnerTests;
  497 + PRODUCT_NAME = "$(TARGET_NAME)";
  498 + SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;
  499 + SWIFT_OPTIMIZATION_LEVEL = "-Onone";
  500 + SWIFT_VERSION = 5.0;
  501 + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Runner.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/Runner";
  502 + };
  503 + name = Debug;
  504 + };
  505 + 331C8089294A63A400263BE5 /* Release */ = {
  506 + isa = XCBuildConfiguration;
  507 + baseConfigurationReference = F782189BA737B0E3C9A3C330 /* Pods-RunnerTests.release.xcconfig */;
  508 + buildSettings = {
  509 + BUNDLE_LOADER = "$(TEST_HOST)";
  510 + CODE_SIGN_STYLE = Automatic;
  511 + CURRENT_PROJECT_VERSION = 1;
  512 + GENERATE_INFOPLIST_FILE = YES;
  513 + MARKETING_VERSION = 1.0;
  514 + PRODUCT_BUNDLE_IDENTIFIER = mobi.iflow.flutter.autotrack.autoTrackExample.RunnerTests;
  515 + PRODUCT_NAME = "$(TARGET_NAME)";
  516 + SWIFT_VERSION = 5.0;
  517 + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Runner.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/Runner";
  518 + };
  519 + name = Release;
  520 + };
  521 + 331C808A294A63A400263BE5 /* Profile */ = {
  522 + isa = XCBuildConfiguration;
  523 + baseConfigurationReference = 12AD15FE86700BFEC11CC92D /* Pods-RunnerTests.profile.xcconfig */;
  524 + buildSettings = {
  525 + BUNDLE_LOADER = "$(TEST_HOST)";
  526 + CODE_SIGN_STYLE = Automatic;
  527 + CURRENT_PROJECT_VERSION = 1;
  528 + GENERATE_INFOPLIST_FILE = YES;
  529 + MARKETING_VERSION = 1.0;
  530 + PRODUCT_BUNDLE_IDENTIFIER = mobi.iflow.flutter.autotrack.autoTrackExample.RunnerTests;
  531 + PRODUCT_NAME = "$(TARGET_NAME)";
  532 + SWIFT_VERSION = 5.0;
  533 + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Runner.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/Runner";
  534 + };
  535 + name = Profile;
  536 + };
  537 + 97C147031CF9000F007C117D /* Debug */ = {
  538 + isa = XCBuildConfiguration;
  539 + buildSettings = {
  540 + ALWAYS_SEARCH_USER_PATHS = NO;
  541 + CLANG_ANALYZER_NONNULL = YES;
  542 + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
  543 + CLANG_CXX_LIBRARY = "libc++";
  544 + CLANG_ENABLE_MODULES = YES;
  545 + CLANG_ENABLE_OBJC_ARC = YES;
  546 + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
  547 + CLANG_WARN_BOOL_CONVERSION = YES;
  548 + CLANG_WARN_COMMA = YES;
  549 + CLANG_WARN_CONSTANT_CONVERSION = YES;
  550 + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
  551 + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
  552 + CLANG_WARN_EMPTY_BODY = YES;
  553 + CLANG_WARN_ENUM_CONVERSION = YES;
  554 + CLANG_WARN_INFINITE_RECURSION = YES;
  555 + CLANG_WARN_INT_CONVERSION = YES;
  556 + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
  557 + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
  558 + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
  559 + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
  560 + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
  561 + CLANG_WARN_STRICT_PROTOTYPES = YES;
  562 + CLANG_WARN_SUSPICIOUS_MOVE = YES;
  563 + CLANG_WARN_UNREACHABLE_CODE = YES;
  564 + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
  565 + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
  566 + COPY_PHASE_STRIP = NO;
  567 + DEBUG_INFORMATION_FORMAT = dwarf;
  568 + ENABLE_STRICT_OBJC_MSGSEND = YES;
  569 + ENABLE_TESTABILITY = YES;
  570 + GCC_C_LANGUAGE_STANDARD = gnu99;
  571 + GCC_DYNAMIC_NO_PIC = NO;
  572 + GCC_NO_COMMON_BLOCKS = YES;
  573 + GCC_OPTIMIZATION_LEVEL = 0;
  574 + GCC_PREPROCESSOR_DEFINITIONS = (
  575 + "DEBUG=1",
  576 + "$(inherited)",
  577 + );
  578 + GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
  579 + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
  580 + GCC_WARN_UNDECLARED_SELECTOR = YES;
  581 + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
  582 + GCC_WARN_UNUSED_FUNCTION = YES;
  583 + GCC_WARN_UNUSED_VARIABLE = YES;
  584 + IPHONEOS_DEPLOYMENT_TARGET = 11.0;
  585 + MTL_ENABLE_DEBUG_INFO = YES;
  586 + ONLY_ACTIVE_ARCH = YES;
  587 + SDKROOT = iphoneos;
  588 + TARGETED_DEVICE_FAMILY = "1,2";
  589 + };
  590 + name = Debug;
  591 + };
  592 + 97C147041CF9000F007C117D /* Release */ = {
  593 + isa = XCBuildConfiguration;
  594 + buildSettings = {
  595 + ALWAYS_SEARCH_USER_PATHS = NO;
  596 + CLANG_ANALYZER_NONNULL = YES;
  597 + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
  598 + CLANG_CXX_LIBRARY = "libc++";
  599 + CLANG_ENABLE_MODULES = YES;
  600 + CLANG_ENABLE_OBJC_ARC = YES;
  601 + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
  602 + CLANG_WARN_BOOL_CONVERSION = YES;
  603 + CLANG_WARN_COMMA = YES;
  604 + CLANG_WARN_CONSTANT_CONVERSION = YES;
  605 + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
  606 + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
  607 + CLANG_WARN_EMPTY_BODY = YES;
  608 + CLANG_WARN_ENUM_CONVERSION = YES;
  609 + CLANG_WARN_INFINITE_RECURSION = YES;
  610 + CLANG_WARN_INT_CONVERSION = YES;
  611 + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
  612 + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
  613 + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
  614 + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
  615 + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
  616 + CLANG_WARN_STRICT_PROTOTYPES = YES;
  617 + CLANG_WARN_SUSPICIOUS_MOVE = YES;
  618 + CLANG_WARN_UNREACHABLE_CODE = YES;
  619 + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
  620 + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
  621 + COPY_PHASE_STRIP = NO;
  622 + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
  623 + ENABLE_NS_ASSERTIONS = NO;
  624 + ENABLE_STRICT_OBJC_MSGSEND = YES;
  625 + GCC_C_LANGUAGE_STANDARD = gnu99;
  626 + GCC_NO_COMMON_BLOCKS = YES;
  627 + GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
  628 + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
  629 + GCC_WARN_UNDECLARED_SELECTOR = YES;
  630 + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
  631 + GCC_WARN_UNUSED_FUNCTION = YES;
  632 + GCC_WARN_UNUSED_VARIABLE = YES;
  633 + IPHONEOS_DEPLOYMENT_TARGET = 11.0;
  634 + MTL_ENABLE_DEBUG_INFO = NO;
  635 + SDKROOT = iphoneos;
  636 + SUPPORTED_PLATFORMS = iphoneos;
  637 + SWIFT_COMPILATION_MODE = wholemodule;
  638 + SWIFT_OPTIMIZATION_LEVEL = "-O";
  639 + TARGETED_DEVICE_FAMILY = "1,2";
  640 + VALIDATE_PRODUCT = YES;
  641 + };
  642 + name = Release;
  643 + };
  644 + 97C147061CF9000F007C117D /* Debug */ = {
  645 + isa = XCBuildConfiguration;
  646 + baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */;
  647 + buildSettings = {
  648 + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
  649 + CLANG_ENABLE_MODULES = YES;
  650 + CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
  651 + DEVELOPMENT_TEAM = FVLFR9QYGC;
  652 + ENABLE_BITCODE = NO;
  653 + INFOPLIST_FILE = Runner/Info.plist;
  654 + LD_RUNPATH_SEARCH_PATHS = (
  655 + "$(inherited)",
  656 + "@executable_path/Frameworks",
  657 + );
  658 + PRODUCT_BUNDLE_IDENTIFIER = mobi.iflow.flutter.autotrack.autoTrackExample;
  659 + PRODUCT_NAME = "$(TARGET_NAME)";
  660 + SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
  661 + SWIFT_OPTIMIZATION_LEVEL = "-Onone";
  662 + SWIFT_VERSION = 5.0;
  663 + VERSIONING_SYSTEM = "apple-generic";
  664 + };
  665 + name = Debug;
  666 + };
  667 + 97C147071CF9000F007C117D /* Release */ = {
  668 + isa = XCBuildConfiguration;
  669 + baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */;
  670 + buildSettings = {
  671 + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
  672 + CLANG_ENABLE_MODULES = YES;
  673 + CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
  674 + DEVELOPMENT_TEAM = FVLFR9QYGC;
  675 + ENABLE_BITCODE = NO;
  676 + INFOPLIST_FILE = Runner/Info.plist;
  677 + LD_RUNPATH_SEARCH_PATHS = (
  678 + "$(inherited)",
  679 + "@executable_path/Frameworks",
  680 + );
  681 + PRODUCT_BUNDLE_IDENTIFIER = mobi.iflow.flutter.autotrack.autoTrackExample;
  682 + PRODUCT_NAME = "$(TARGET_NAME)";
  683 + SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
  684 + SWIFT_VERSION = 5.0;
  685 + VERSIONING_SYSTEM = "apple-generic";
  686 + };
  687 + name = Release;
  688 + };
  689 +/* End XCBuildConfiguration section */
  690 +
  691 +/* Begin XCConfigurationList section */
  692 + 331C8087294A63A400263BE5 /* Build configuration list for PBXNativeTarget "RunnerTests" */ = {
  693 + isa = XCConfigurationList;
  694 + buildConfigurations = (
  695 + 331C8088294A63A400263BE5 /* Debug */,
  696 + 331C8089294A63A400263BE5 /* Release */,
  697 + 331C808A294A63A400263BE5 /* Profile */,
  698 + );
  699 + defaultConfigurationIsVisible = 0;
  700 + defaultConfigurationName = Release;
  701 + };
  702 + 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */ = {
  703 + isa = XCConfigurationList;
  704 + buildConfigurations = (
  705 + 97C147031CF9000F007C117D /* Debug */,
  706 + 97C147041CF9000F007C117D /* Release */,
  707 + 249021D3217E4FDB00AE95B9 /* Profile */,
  708 + );
  709 + defaultConfigurationIsVisible = 0;
  710 + defaultConfigurationName = Release;
  711 + };
  712 + 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */ = {
  713 + isa = XCConfigurationList;
  714 + buildConfigurations = (
  715 + 97C147061CF9000F007C117D /* Debug */,
  716 + 97C147071CF9000F007C117D /* Release */,
  717 + 249021D4217E4FDB00AE95B9 /* Profile */,
  718 + );
  719 + defaultConfigurationIsVisible = 0;
  720 + defaultConfigurationName = Release;
  721 + };
  722 +/* End XCConfigurationList section */
  723 + };
  724 + rootObject = 97C146E61CF9000F007C117D /* Project object */;
  725 +}
  1 +<?xml version="1.0" encoding="UTF-8"?>
  2 +<Workspace
  3 + version = "1.0">
  4 + <FileRef
  5 + location = "self:">
  6 + </FileRef>
  7 +</Workspace>
  1 +<?xml version="1.0" encoding="UTF-8"?>
  2 +<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
  3 +<plist version="1.0">
  4 +<dict>
  5 + <key>IDEDidComputeMac32BitWarning</key>
  6 + <true/>
  7 +</dict>
  8 +</plist>
  1 +<?xml version="1.0" encoding="UTF-8"?>
  2 +<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
  3 +<plist version="1.0">
  4 +<dict>
  5 + <key>PreviewsEnabled</key>
  6 + <false/>
  7 +</dict>
  8 +</plist>
  1 +<?xml version="1.0" encoding="UTF-8"?>
  2 +<Scheme
  3 + LastUpgradeVersion = "1430"
  4 + version = "1.3">
  5 + <BuildAction
  6 + parallelizeBuildables = "YES"
  7 + buildImplicitDependencies = "YES">
  8 + <BuildActionEntries>
  9 + <BuildActionEntry
  10 + buildForTesting = "YES"
  11 + buildForRunning = "YES"
  12 + buildForProfiling = "YES"
  13 + buildForArchiving = "YES"
  14 + buildForAnalyzing = "YES">
  15 + <BuildableReference
  16 + BuildableIdentifier = "primary"
  17 + BlueprintIdentifier = "97C146ED1CF9000F007C117D"
  18 + BuildableName = "Runner.app"
  19 + BlueprintName = "Runner"
  20 + ReferencedContainer = "container:Runner.xcodeproj">
  21 + </BuildableReference>
  22 + </BuildActionEntry>
  23 + </BuildActionEntries>
  24 + </BuildAction>
  25 + <TestAction
  26 + buildConfiguration = "Debug"
  27 + selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
  28 + selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
  29 + shouldUseLaunchSchemeArgsEnv = "YES">
  30 + <MacroExpansion>
  31 + <BuildableReference
  32 + BuildableIdentifier = "primary"
  33 + BlueprintIdentifier = "97C146ED1CF9000F007C117D"
  34 + BuildableName = "Runner.app"
  35 + BlueprintName = "Runner"
  36 + ReferencedContainer = "container:Runner.xcodeproj">
  37 + </BuildableReference>
  38 + </MacroExpansion>
  39 + <Testables>
  40 + <TestableReference
  41 + skipped = "NO"
  42 + parallelizable = "YES">
  43 + <BuildableReference
  44 + BuildableIdentifier = "primary"
  45 + BlueprintIdentifier = "331C8080294A63A400263BE5"
  46 + BuildableName = "RunnerTests.xctest"
  47 + BlueprintName = "RunnerTests"
  48 + ReferencedContainer = "container:Runner.xcodeproj">
  49 + </BuildableReference>
  50 + </TestableReference>
  51 + </Testables>
  52 + </TestAction>
  53 + <LaunchAction
  54 + buildConfiguration = "Debug"
  55 + selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
  56 + selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
  57 + launchStyle = "0"
  58 + useCustomWorkingDirectory = "NO"
  59 + ignoresPersistentStateOnLaunch = "NO"
  60 + debugDocumentVersioning = "YES"
  61 + debugServiceExtension = "internal"
  62 + allowLocationSimulation = "YES">
  63 + <BuildableProductRunnable
  64 + runnableDebuggingMode = "0">
  65 + <BuildableReference
  66 + BuildableIdentifier = "primary"
  67 + BlueprintIdentifier = "97C146ED1CF9000F007C117D"
  68 + BuildableName = "Runner.app"
  69 + BlueprintName = "Runner"
  70 + ReferencedContainer = "container:Runner.xcodeproj">
  71 + </BuildableReference>
  72 + </BuildableProductRunnable>
  73 + </LaunchAction>
  74 + <ProfileAction
  75 + buildConfiguration = "Profile"
  76 + shouldUseLaunchSchemeArgsEnv = "YES"
  77 + savedToolIdentifier = ""
  78 + useCustomWorkingDirectory = "NO"
  79 + debugDocumentVersioning = "YES">
  80 + <BuildableProductRunnable
  81 + runnableDebuggingMode = "0">
  82 + <BuildableReference
  83 + BuildableIdentifier = "primary"
  84 + BlueprintIdentifier = "97C146ED1CF9000F007C117D"
  85 + BuildableName = "Runner.app"
  86 + BlueprintName = "Runner"
  87 + ReferencedContainer = "container:Runner.xcodeproj">
  88 + </BuildableReference>
  89 + </BuildableProductRunnable>
  90 + </ProfileAction>
  91 + <AnalyzeAction
  92 + buildConfiguration = "Debug">
  93 + </AnalyzeAction>
  94 + <ArchiveAction
  95 + buildConfiguration = "Release"
  96 + revealArchiveInOrganizer = "YES">
  97 + </ArchiveAction>
  98 +</Scheme>
  1 +<?xml version="1.0" encoding="UTF-8"?>
  2 +<Workspace
  3 + version = "1.0">
  4 + <FileRef
  5 + location = "group:Runner.xcodeproj">
  6 + </FileRef>
  7 + <FileRef
  8 + location = "group:Pods/Pods.xcodeproj">
  9 + </FileRef>
  10 +</Workspace>
  1 +<?xml version="1.0" encoding="UTF-8"?>
  2 +<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
  3 +<plist version="1.0">
  4 +<dict>
  5 + <key>IDEDidComputeMac32BitWarning</key>
  6 + <true/>
  7 +</dict>
  8 +</plist>
  1 +<?xml version="1.0" encoding="UTF-8"?>
  2 +<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
  3 +<plist version="1.0">
  4 +<dict>
  5 + <key>PreviewsEnabled</key>
  6 + <false/>
  7 +</dict>
  8 +</plist>
  1 +import UIKit
  2 +import Flutter
  3 +
  4 +@UIApplicationMain
  5 +@objc class AppDelegate: FlutterAppDelegate {
  6 + override func application(
  7 + _ application: UIApplication,
  8 + didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
  9 + ) -> Bool {
  10 + GeneratedPluginRegistrant.register(with: self)
  11 + return super.application(application, didFinishLaunchingWithOptions: launchOptions)
  12 + }
  13 +}
  1 +{
  2 + "images" : [
  3 + {
  4 + "size" : "20x20",
  5 + "idiom" : "iphone",
  6 + "filename" : "Icon-App-20x20@2x.png",
  7 + "scale" : "2x"
  8 + },
  9 + {
  10 + "size" : "20x20",
  11 + "idiom" : "iphone",
  12 + "filename" : "Icon-App-20x20@3x.png",
  13 + "scale" : "3x"
  14 + },
  15 + {
  16 + "size" : "29x29",
  17 + "idiom" : "iphone",
  18 + "filename" : "Icon-App-29x29@1x.png",
  19 + "scale" : "1x"
  20 + },
  21 + {
  22 + "size" : "29x29",
  23 + "idiom" : "iphone",
  24 + "filename" : "Icon-App-29x29@2x.png",
  25 + "scale" : "2x"
  26 + },
  27 + {
  28 + "size" : "29x29",
  29 + "idiom" : "iphone",
  30 + "filename" : "Icon-App-29x29@3x.png",
  31 + "scale" : "3x"
  32 + },
  33 + {
  34 + "size" : "40x40",
  35 + "idiom" : "iphone",
  36 + "filename" : "Icon-App-40x40@2x.png",
  37 + "scale" : "2x"
  38 + },
  39 + {
  40 + "size" : "40x40",
  41 + "idiom" : "iphone",
  42 + "filename" : "Icon-App-40x40@3x.png",
  43 + "scale" : "3x"
  44 + },
  45 + {
  46 + "size" : "60x60",
  47 + "idiom" : "iphone",
  48 + "filename" : "Icon-App-60x60@2x.png",
  49 + "scale" : "2x"
  50 + },
  51 + {
  52 + "size" : "60x60",
  53 + "idiom" : "iphone",
  54 + "filename" : "Icon-App-60x60@3x.png",
  55 + "scale" : "3x"
  56 + },
  57 + {
  58 + "size" : "20x20",
  59 + "idiom" : "ipad",
  60 + "filename" : "Icon-App-20x20@1x.png",
  61 + "scale" : "1x"
  62 + },
  63 + {
  64 + "size" : "20x20",
  65 + "idiom" : "ipad",
  66 + "filename" : "Icon-App-20x20@2x.png",
  67 + "scale" : "2x"
  68 + },
  69 + {
  70 + "size" : "29x29",
  71 + "idiom" : "ipad",
  72 + "filename" : "Icon-App-29x29@1x.png",
  73 + "scale" : "1x"
  74 + },
  75 + {
  76 + "size" : "29x29",
  77 + "idiom" : "ipad",
  78 + "filename" : "Icon-App-29x29@2x.png",
  79 + "scale" : "2x"
  80 + },
  81 + {
  82 + "size" : "40x40",
  83 + "idiom" : "ipad",
  84 + "filename" : "Icon-App-40x40@1x.png",
  85 + "scale" : "1x"
  86 + },
  87 + {
  88 + "size" : "40x40",
  89 + "idiom" : "ipad",
  90 + "filename" : "Icon-App-40x40@2x.png",
  91 + "scale" : "2x"
  92 + },
  93 + {
  94 + "size" : "76x76",
  95 + "idiom" : "ipad",
  96 + "filename" : "Icon-App-76x76@1x.png",
  97 + "scale" : "1x"
  98 + },
  99 + {
  100 + "size" : "76x76",
  101 + "idiom" : "ipad",
  102 + "filename" : "Icon-App-76x76@2x.png",
  103 + "scale" : "2x"
  104 + },
  105 + {
  106 + "size" : "83.5x83.5",
  107 + "idiom" : "ipad",
  108 + "filename" : "Icon-App-83.5x83.5@2x.png",
  109 + "scale" : "2x"
  110 + },
  111 + {
  112 + "size" : "1024x1024",
  113 + "idiom" : "ios-marketing",
  114 + "filename" : "Icon-App-1024x1024@1x.png",
  115 + "scale" : "1x"
  116 + }
  117 + ],
  118 + "info" : {
  119 + "version" : 1,
  120 + "author" : "xcode"
  121 + }
  122 +}
  1 +{
  2 + "images" : [
  3 + {
  4 + "idiom" : "universal",
  5 + "filename" : "LaunchImage.png",
  6 + "scale" : "1x"
  7 + },
  8 + {
  9 + "idiom" : "universal",
  10 + "filename" : "LaunchImage@2x.png",
  11 + "scale" : "2x"
  12 + },
  13 + {
  14 + "idiom" : "universal",
  15 + "filename" : "LaunchImage@3x.png",
  16 + "scale" : "3x"
  17 + }
  18 + ],
  19 + "info" : {
  20 + "version" : 1,
  21 + "author" : "xcode"
  22 + }
  23 +}
  1 +# Launch Screen Assets
  2 +
  3 +You can customize the launch screen with your own desired assets by replacing the image files in this directory.
  4 +
  5 +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.
  1 +<?xml version="1.0" encoding="UTF-8" standalone="no"?>
  2 +<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="12121" systemVersion="16G29" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" launchScreen="YES" colorMatched="YES" initialViewController="01J-lp-oVM">
  3 + <dependencies>
  4 + <deployment identifier="iOS"/>
  5 + <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="12089"/>
  6 + </dependencies>
  7 + <scenes>
  8 + <!--View Controller-->
  9 + <scene sceneID="EHf-IW-A2E">
  10 + <objects>
  11 + <viewController id="01J-lp-oVM" sceneMemberID="viewController">
  12 + <layoutGuides>
  13 + <viewControllerLayoutGuide type="top" id="Ydg-fD-yQy"/>
  14 + <viewControllerLayoutGuide type="bottom" id="xbc-2k-c8Z"/>
  15 + </layoutGuides>
  16 + <view key="view" contentMode="scaleToFill" id="Ze5-6b-2t3">
  17 + <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
  18 + <subviews>
  19 + <imageView opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" image="LaunchImage" translatesAutoresizingMaskIntoConstraints="NO" id="YRO-k0-Ey4">
  20 + </imageView>
  21 + </subviews>
  22 + <color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
  23 + <constraints>
  24 + <constraint firstItem="YRO-k0-Ey4" firstAttribute="centerX" secondItem="Ze5-6b-2t3" secondAttribute="centerX" id="1a2-6s-vTC"/>
  25 + <constraint firstItem="YRO-k0-Ey4" firstAttribute="centerY" secondItem="Ze5-6b-2t3" secondAttribute="centerY" id="4X2-HB-R7a"/>
  26 + </constraints>
  27 + </view>
  28 + </viewController>
  29 + <placeholder placeholderIdentifier="IBFirstResponder" id="iYj-Kq-Ea1" userLabel="First Responder" sceneMemberID="firstResponder"/>
  30 + </objects>
  31 + <point key="canvasLocation" x="53" y="375"/>
  32 + </scene>
  33 + </scenes>
  34 + <resources>
  35 + <image name="LaunchImage" width="168" height="185"/>
  36 + </resources>
  37 +</document>
  1 +<?xml version="1.0" encoding="UTF-8" standalone="no"?>
  2 +<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="10117" systemVersion="15F34" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" initialViewController="BYZ-38-t0r">
  3 + <dependencies>
  4 + <deployment identifier="iOS"/>
  5 + <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="10085"/>
  6 + </dependencies>
  7 + <scenes>
  8 + <!--Flutter View Controller-->
  9 + <scene sceneID="tne-QT-ifu">
  10 + <objects>
  11 + <viewController id="BYZ-38-t0r" customClass="FlutterViewController" sceneMemberID="viewController">
  12 + <layoutGuides>
  13 + <viewControllerLayoutGuide type="top" id="y3c-jy-aDJ"/>
  14 + <viewControllerLayoutGuide type="bottom" id="wfy-db-euE"/>
  15 + </layoutGuides>
  16 + <view key="view" contentMode="scaleToFill" id="8bC-Xf-vdC">
  17 + <rect key="frame" x="0.0" y="0.0" width="600" height="600"/>
  18 + <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
  19 + <color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="calibratedWhite"/>
  20 + </view>
  21 + </viewController>
  22 + <placeholder placeholderIdentifier="IBFirstResponder" id="dkx-z0-nzr" sceneMemberID="firstResponder"/>
  23 + </objects>
  24 + </scene>
  25 + </scenes>
  26 +</document>
  1 +<?xml version="1.0" encoding="UTF-8"?>
  2 +<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
  3 +<plist version="1.0">
  4 +<dict>
  5 + <key>CFBundleDevelopmentRegion</key>
  6 + <string>$(DEVELOPMENT_LANGUAGE)</string>
  7 + <key>CFBundleDisplayName</key>
  8 + <string>Auto Track</string>
  9 + <key>CFBundleExecutable</key>
  10 + <string>$(EXECUTABLE_NAME)</string>
  11 + <key>CFBundleIdentifier</key>
  12 + <string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
  13 + <key>CFBundleInfoDictionaryVersion</key>
  14 + <string>6.0</string>
  15 + <key>CFBundleName</key>
  16 + <string>auto_track_example</string>
  17 + <key>CFBundlePackageType</key>
  18 + <string>APPL</string>
  19 + <key>CFBundleShortVersionString</key>
  20 + <string>$(FLUTTER_BUILD_NAME)</string>
  21 + <key>CFBundleSignature</key>
  22 + <string>????</string>
  23 + <key>CFBundleVersion</key>
  24 + <string>$(FLUTTER_BUILD_NUMBER)</string>
  25 + <key>LSRequiresIPhoneOS</key>
  26 + <true/>
  27 + <key>UILaunchStoryboardName</key>
  28 + <string>LaunchScreen</string>
  29 + <key>UIMainStoryboardFile</key>
  30 + <string>Main</string>
  31 + <key>UISupportedInterfaceOrientations</key>
  32 + <array>
  33 + <string>UIInterfaceOrientationPortrait</string>
  34 + <string>UIInterfaceOrientationLandscapeLeft</string>
  35 + <string>UIInterfaceOrientationLandscapeRight</string>
  36 + </array>
  37 + <key>UISupportedInterfaceOrientations~ipad</key>
  38 + <array>
  39 + <string>UIInterfaceOrientationPortrait</string>
  40 + <string>UIInterfaceOrientationPortraitUpsideDown</string>
  41 + <string>UIInterfaceOrientationLandscapeLeft</string>
  42 + <string>UIInterfaceOrientationLandscapeRight</string>
  43 + </array>
  44 + <key>CADisableMinimumFrameDurationOnPhone</key>
  45 + <true/>
  46 + <key>UIApplicationSupportsIndirectInputEvents</key>
  47 + <true/>
  48 +</dict>
  49 +</plist>
  1 +#import "GeneratedPluginRegistrant.h"
  1 +import Flutter
  2 +import UIKit
  3 +import XCTest
  4 +
  5 +@testable import auto_track
  6 +
  7 +// This demonstrates a simple unit test of the Swift portion of this plugin's implementation.
  8 +//
  9 +// See https://developer.apple.com/documentation/xctest for more information about using XCTest.
  10 +
  11 +class RunnerTests: XCTestCase {
  12 +
  13 + func testGetPlatformVersion() {
  14 + let plugin = AutoTrackPlugin()
  15 +
  16 + let call = FlutterMethodCall(methodName: "getPlatformVersion", arguments: [])
  17 +
  18 + let resultExpectation = expectation(description: "result block must be called.")
  19 + plugin.handle(call) { result in
  20 + XCTAssertEqual(result as! String, "iOS " + UIDevice.current.systemVersion)
  21 + resultExpectation.fulfill()
  22 + }
  23 + waitForExpectations(timeout: 1)
  24 + }
  25 +
  26 +}
  1 +import 'package:auto_track_example/page_a.dart';
  2 +import 'package:flutter/material.dart';
  3 +
  4 +class Home extends StatelessWidget {
  5 + const Home({super.key});
  6 +
  7 + @override
  8 + Widget build(BuildContext context) {
  9 + return ListView(
  10 + children: [
  11 + ListTile(
  12 + title: const Text('page a'),
  13 + onTap: () {
  14 + Navigator.push(context, MaterialPageRoute(builder: (context) {
  15 + return const PageA();
  16 + }));
  17 + },
  18 + )
  19 + ],
  20 + );
  21 + }
  22 +}
  1 +import 'package:auto_track/auto_track.dart';
  2 +import 'package:auto_track/auto_track/config/config.dart';
  3 +import 'package:auto_track/auto_track/index.dart';
  4 +import 'package:auto_track_example/home.dart';
  5 +import 'package:auto_track_example/page_a.dart';
  6 +import 'package:flutter/material.dart';
  7 +
  8 +
  9 +void main() {
  10 + runApp(const MyApp());
  11 +}
  12 +
  13 +class MyApp extends StatefulWidget {
  14 + const MyApp({super.key});
  15 +
  16 + @override
  17 + State<MyApp> createState() => _MyAppState();
  18 +}
  19 +
  20 +class _MyAppState extends State<MyApp> {
  21 + @override
  22 + void initState() {
  23 + AutoTrack()
  24 + .config(AutoTrackConfig(
  25 + pageConfigs: [
  26 + AutoTrackPageConfig<PageA>(
  27 + pageID: 'page_a',
  28 + ),
  29 +
  30 + ]))
  31 + .enable()
  32 + .enablePageLeave()
  33 + .enablePageView()
  34 + .enableClick()
  35 + .enableLog();
  36 +
  37 + super.initState();
  38 + }
  39 +
  40 + @override
  41 + Widget build(BuildContext context) {
  42 + return MaterialApp(
  43 + home: Scaffold(
  44 + appBar: AppBar(
  45 + title: const Text('auto track example app'),
  46 + ),
  47 + body: const Center(
  48 + child: Home(),
  49 + ),
  50 + ),
  51 + navigatorObservers: AutoTrackNavigationObserver.wrap([]),
  52 + );
  53 + }
  54 +}
  1 +import 'package:flutter/cupertino.dart';
  2 +
  3 +class PageA extends StatelessWidget {
  4 + const PageA({super.key});
  5 +
  6 + @override
  7 + Widget build(BuildContext context) {
  8 + return GestureDetector(
  9 + onTap: () {
  10 + print("tap page a");
  11 + },
  12 + child: const Text('page a'),
  13 + );
  14 + }
  15 +}
  1 +# Generated by pub
  2 +# See https://dart.dev/tools/pub/glossary#lockfile
  3 +packages:
  4 + async:
  5 + dependency: transitive
  6 + description:
  7 + name: async
  8 + sha256: "947bfcf187f74dbc5e146c9eb9c0f10c9f8b30743e341481c1e2ed3ecc18c20c"
  9 + url: "https://pub.dev"
  10 + source: hosted
  11 + version: "2.11.0"
  12 + auto_track:
  13 + dependency: "direct main"
  14 + description:
  15 + path: ".."
  16 + relative: true
  17 + source: path
  18 + version: "0.0.1"
  19 + boolean_selector:
  20 + dependency: transitive
  21 + description:
  22 + name: boolean_selector
  23 + sha256: "6cfb5af12253eaf2b368f07bacc5a80d1301a071c73360d746b7f2e32d762c66"
  24 + url: "https://pub.dev"
  25 + source: hosted
  26 + version: "2.1.1"
  27 + characters:
  28 + dependency: transitive
  29 + description:
  30 + name: characters
  31 + sha256: "04a925763edad70e8443c99234dc3328f442e811f1d8fd1a72f1c8ad0f69a605"
  32 + url: "https://pub.dev"
  33 + source: hosted
  34 + version: "1.3.0"
  35 + clock:
  36 + dependency: transitive
  37 + description:
  38 + name: clock
  39 + sha256: cb6d7f03e1de671e34607e909a7213e31d7752be4fb66a86d29fe1eb14bfb5cf
  40 + url: "https://pub.dev"
  41 + source: hosted
  42 + version: "1.1.1"
  43 + collection:
  44 + dependency: transitive
  45 + description:
  46 + name: collection
  47 + sha256: ee67cb0715911d28db6bf4af1026078bd6f0128b07a5f66fb2ed94ec6783c09a
  48 + url: "https://pub.dev"
  49 + source: hosted
  50 + version: "1.18.0"
  51 + crypto:
  52 + dependency: transitive
  53 + description:
  54 + name: crypto
  55 + sha256: ff625774173754681d66daaf4a448684fb04b78f902da9cb3d308c19cc5e8bab
  56 + url: "https://pub.dev"
  57 + source: hosted
  58 + version: "3.0.3"
  59 + cupertino_icons:
  60 + dependency: "direct main"
  61 + description:
  62 + name: cupertino_icons
  63 + sha256: d57953e10f9f8327ce64a508a355f0b1ec902193f66288e8cb5070e7c47eeb2d
  64 + url: "https://pub.dev"
  65 + source: hosted
  66 + version: "1.0.6"
  67 + dio:
  68 + dependency: transitive
  69 + description:
  70 + name: dio
  71 + sha256: "49af28382aefc53562459104f64d16b9dfd1e8ef68c862d5af436cc8356ce5a8"
  72 + url: "https://pub.dev"
  73 + source: hosted
  74 + version: "5.4.1"
  75 + fake_async:
  76 + dependency: transitive
  77 + description:
  78 + name: fake_async
  79 + sha256: "511392330127add0b769b75a987850d136345d9227c6b94c96a04cf4a391bf78"
  80 + url: "https://pub.dev"
  81 + source: hosted
  82 + version: "1.3.1"
  83 + file:
  84 + dependency: transitive
  85 + description:
  86 + name: file
  87 + sha256: "1b92bec4fc2a72f59a8e15af5f52cd441e4a7860b49499d69dfa817af20e925d"
  88 + url: "https://pub.dev"
  89 + source: hosted
  90 + version: "6.1.4"
  91 + fixnum:
  92 + dependency: transitive
  93 + description:
  94 + name: fixnum
  95 + sha256: "25517a4deb0c03aa0f32fd12db525856438902d9c16536311e76cdc57b31d7d1"
  96 + url: "https://pub.dev"
  97 + source: hosted
  98 + version: "1.1.0"
  99 + flutter:
  100 + dependency: "direct main"
  101 + description: flutter
  102 + source: sdk
  103 + version: "0.0.0"
  104 + flutter_driver:
  105 + dependency: transitive
  106 + description: flutter
  107 + source: sdk
  108 + version: "0.0.0"
  109 + flutter_lints:
  110 + dependency: "direct dev"
  111 + description:
  112 + name: flutter_lints
  113 + sha256: a25a15ebbdfc33ab1cd26c63a6ee519df92338a9c10f122adda92938253bef04
  114 + url: "https://pub.dev"
  115 + source: hosted
  116 + version: "2.0.3"
  117 + flutter_test:
  118 + dependency: "direct dev"
  119 + description: flutter
  120 + source: sdk
  121 + version: "0.0.0"
  122 + fuchsia_remote_debug_protocol:
  123 + dependency: transitive
  124 + description: flutter
  125 + source: sdk
  126 + version: "0.0.0"
  127 + http_parser:
  128 + dependency: transitive
  129 + description:
  130 + name: http_parser
  131 + sha256: "2aa08ce0341cc9b354a498388e30986515406668dbcc4f7c950c3e715496693b"
  132 + url: "https://pub.dev"
  133 + source: hosted
  134 + version: "4.0.2"
  135 + integration_test:
  136 + dependency: "direct dev"
  137 + description: flutter
  138 + source: sdk
  139 + version: "0.0.0"
  140 + lints:
  141 + dependency: transitive
  142 + description:
  143 + name: lints
  144 + sha256: "0a217c6c989d21039f1498c3ed9f3ed71b354e69873f13a8dfc3c9fe76f1b452"
  145 + url: "https://pub.dev"
  146 + source: hosted
  147 + version: "2.1.1"
  148 + matcher:
  149 + dependency: transitive
  150 + description:
  151 + name: matcher
  152 + sha256: "1803e76e6653768d64ed8ff2e1e67bea3ad4b923eb5c56a295c3e634bad5960e"
  153 + url: "https://pub.dev"
  154 + source: hosted
  155 + version: "0.12.16"
  156 + material_color_utilities:
  157 + dependency: transitive
  158 + description:
  159 + name: material_color_utilities
  160 + sha256: "9528f2f296073ff54cb9fee677df673ace1218163c3bc7628093e7eed5203d41"
  161 + url: "https://pub.dev"
  162 + source: hosted
  163 + version: "0.5.0"
  164 + meta:
  165 + dependency: transitive
  166 + description:
  167 + name: meta
  168 + sha256: a6e590c838b18133bb482a2745ad77c5bb7715fb0451209e1a7567d416678b8e
  169 + url: "https://pub.dev"
  170 + source: hosted
  171 + version: "1.10.0"
  172 + path:
  173 + dependency: transitive
  174 + description:
  175 + name: path
  176 + sha256: "8829d8a55c13fc0e37127c29fedf290c102f4e40ae94ada574091fe0ff96c917"
  177 + url: "https://pub.dev"
  178 + source: hosted
  179 + version: "1.8.3"
  180 + platform:
  181 + dependency: transitive
  182 + description:
  183 + name: platform
  184 + sha256: ae68c7bfcd7383af3629daafb32fb4e8681c7154428da4febcff06200585f102
  185 + url: "https://pub.dev"
  186 + source: hosted
  187 + version: "3.1.2"
  188 + plugin_platform_interface:
  189 + dependency: transitive
  190 + description:
  191 + name: plugin_platform_interface
  192 + sha256: "4820fbfdb9478b1ebae27888254d445073732dae3d6ea81f0b7e06d5dedc3f02"
  193 + url: "https://pub.dev"
  194 + source: hosted
  195 + version: "2.1.8"
  196 + process:
  197 + dependency: transitive
  198 + description:
  199 + name: process
  200 + sha256: "53fd8db9cec1d37b0574e12f07520d582019cb6c44abf5479a01505099a34a09"
  201 + url: "https://pub.dev"
  202 + source: hosted
  203 + version: "4.2.4"
  204 + sky_engine:
  205 + dependency: transitive
  206 + description: flutter
  207 + source: sdk
  208 + version: "0.0.99"
  209 + source_span:
  210 + dependency: transitive
  211 + description:
  212 + name: source_span
  213 + sha256: "53e943d4206a5e30df338fd4c6e7a077e02254531b138a15aec3bd143c1a8b3c"
  214 + url: "https://pub.dev"
  215 + source: hosted
  216 + version: "1.10.0"
  217 + sprintf:
  218 + dependency: transitive
  219 + description:
  220 + name: sprintf
  221 + sha256: "1fc9ffe69d4df602376b52949af107d8f5703b77cda567c4d7d86a0693120f23"
  222 + url: "https://pub.dev"
  223 + source: hosted
  224 + version: "7.0.0"
  225 + stack_trace:
  226 + dependency: transitive
  227 + description:
  228 + name: stack_trace
  229 + sha256: "73713990125a6d93122541237550ee3352a2d84baad52d375a4cad2eb9b7ce0b"
  230 + url: "https://pub.dev"
  231 + source: hosted
  232 + version: "1.11.1"
  233 + stream_channel:
  234 + dependency: transitive
  235 + description:
  236 + name: stream_channel
  237 + sha256: ba2aa5d8cc609d96bbb2899c28934f9e1af5cddbd60a827822ea467161eb54e7
  238 + url: "https://pub.dev"
  239 + source: hosted
  240 + version: "2.1.2"
  241 + string_scanner:
  242 + dependency: transitive
  243 + description:
  244 + name: string_scanner
  245 + sha256: "556692adab6cfa87322a115640c11f13cb77b3f076ddcc5d6ae3c20242bedcde"
  246 + url: "https://pub.dev"
  247 + source: hosted
  248 + version: "1.2.0"
  249 + sync_http:
  250 + dependency: transitive
  251 + description:
  252 + name: sync_http
  253 + sha256: "7f0cd72eca000d2e026bcd6f990b81d0ca06022ef4e32fb257b30d3d1014a961"
  254 + url: "https://pub.dev"
  255 + source: hosted
  256 + version: "0.3.1"
  257 + term_glyph:
  258 + dependency: transitive
  259 + description:
  260 + name: term_glyph
  261 + sha256: a29248a84fbb7c79282b40b8c72a1209db169a2e0542bce341da992fe1bc7e84
  262 + url: "https://pub.dev"
  263 + source: hosted
  264 + version: "1.2.1"
  265 + test_api:
  266 + dependency: transitive
  267 + description:
  268 + name: test_api
  269 + sha256: "5c2f730018264d276c20e4f1503fd1308dfbbae39ec8ee63c5236311ac06954b"
  270 + url: "https://pub.dev"
  271 + source: hosted
  272 + version: "0.6.1"
  273 + typed_data:
  274 + dependency: transitive
  275 + description:
  276 + name: typed_data
  277 + sha256: facc8d6582f16042dd49f2463ff1bd6e2c9ef9f3d5da3d9b087e244a7b564b3c
  278 + url: "https://pub.dev"
  279 + source: hosted
  280 + version: "1.3.2"
  281 + uuid:
  282 + dependency: transitive
  283 + description:
  284 + name: uuid
  285 + sha256: cd210a09f7c18cbe5a02511718e0334de6559871052c90a90c0cca46a4aa81c8
  286 + url: "https://pub.dev"
  287 + source: hosted
  288 + version: "4.3.3"
  289 + vector_math:
  290 + dependency: transitive
  291 + description:
  292 + name: vector_math
  293 + sha256: "80b3257d1492ce4d091729e3a67a60407d227c27241d6927be0130c98e741803"
  294 + url: "https://pub.dev"
  295 + source: hosted
  296 + version: "2.1.4"
  297 + vm_service:
  298 + dependency: transitive
  299 + description:
  300 + name: vm_service
  301 + sha256: c538be99af830f478718b51630ec1b6bee5e74e52c8a802d328d9e71d35d2583
  302 + url: "https://pub.dev"
  303 + source: hosted
  304 + version: "11.10.0"
  305 + web:
  306 + dependency: transitive
  307 + description:
  308 + name: web
  309 + sha256: afe077240a270dcfd2aafe77602b4113645af95d0ad31128cc02bce5ac5d5152
  310 + url: "https://pub.dev"
  311 + source: hosted
  312 + version: "0.3.0"
  313 + webdriver:
  314 + dependency: transitive
  315 + description:
  316 + name: webdriver
  317 + sha256: "3c923e918918feeb90c4c9fdf1fe39220fa4c0e8e2c0fffaded174498ef86c49"
  318 + url: "https://pub.dev"
  319 + source: hosted
  320 + version: "3.0.2"
  321 +sdks:
  322 + dart: ">=3.2.3 <4.0.0"
  323 + flutter: ">=3.3.0"
  1 +name: auto_track_example
  2 +description: "Demonstrates how to use the auto_track plugin."
  3 +# The following line prevents the package from being accidentally published to
  4 +# pub.dev using `flutter pub publish`. This is preferred for private packages.
  5 +publish_to: 'none' # Remove this line if you wish to publish to pub.dev
  6 +
  7 +environment:
  8 + sdk: '>=3.2.3 <4.0.0'
  9 +
  10 +# Dependencies specify other packages that your package needs in order to work.
  11 +# To automatically upgrade your package dependencies to the latest versions
  12 +# consider running `flutter pub upgrade --major-versions`. Alternatively,
  13 +# dependencies can be manually updated by changing the version numbers below to
  14 +# the latest version available on pub.dev. To see which dependencies have newer
  15 +# versions available, run `flutter pub outdated`.
  16 +dependencies:
  17 + flutter:
  18 + sdk: flutter
  19 +
  20 + auto_track:
  21 + # When depending on this package from a real application you should use:
  22 + # auto_track: ^x.y.z
  23 + # See https://dart.dev/tools/pub/dependencies#version-constraints
  24 + # The example app is bundled with the plugin so we use a path dependency on
  25 + # the parent directory to use the current plugin's version.
  26 + path: ../
  27 +
  28 + # The following adds the Cupertino Icons font to your application.
  29 + # Use with the CupertinoIcons class for iOS style icons.
  30 + cupertino_icons: ^1.0.2
  31 +
  32 +dev_dependencies:
  33 + integration_test:
  34 + sdk: flutter
  35 + flutter_test:
  36 + sdk: flutter
  37 +
  38 + # The "flutter_lints" package below contains a set of recommended lints to
  39 + # encourage good coding practices. The lint set provided by the package is
  40 + # activated in the `analysis_options.yaml` file located at the root of your
  41 + # package. See that file for information about deactivating specific lint
  42 + # rules and activating additional ones.
  43 + flutter_lints: ^2.0.0
  44 +
  45 +# For information on the generic Dart part of this file, see the
  46 +# following page: https://dart.dev/tools/pub/pubspec
  47 +
  48 +# The following section is specific to Flutter packages.
  49 +flutter:
  50 +
  51 + # The following line ensures that the Material Icons font is
  52 + # included with your application, so that you can use the icons in
  53 + # the material Icons class.
  54 + uses-material-design: true
  55 +
  56 + # To add assets to your application, add an assets section, like this:
  57 + # assets:
  58 + # - images/a_dot_burr.jpeg
  59 + # - images/a_dot_ham.jpeg
  60 +
  61 + # An image asset can refer to one or more resolution-specific "variants", see
  62 + # https://flutter.dev/assets-and-images/#resolution-aware
  63 +
  64 + # For details regarding adding assets from package dependencies, see
  65 + # https://flutter.dev/assets-and-images/#from-packages
  66 +
  67 + # To add custom fonts to your application, add a fonts section here,
  68 + # in this "flutter" section. Each entry in this list should have a
  69 + # "family" key with the font family name, and a "fonts" key with a
  70 + # list giving the asset and other descriptors for the font. For
  71 + # example:
  72 + # fonts:
  73 + # - family: Schyler
  74 + # fonts:
  75 + # - asset: fonts/Schyler-Regular.ttf
  76 + # - asset: fonts/Schyler-Italic.ttf
  77 + # style: italic
  78 + # - family: Trajan Pro
  79 + # fonts:
  80 + # - asset: fonts/TrajanPro.ttf
  81 + # - asset: fonts/TrajanPro_Bold.ttf
  82 + # weight: 700
  83 + #
  84 + # For details regarding fonts from package dependencies,
  85 + # see https://flutter.dev/custom-fonts/#from-packages
  1 +// This is a basic Flutter widget test.
  2 +//
  3 +// To perform an interaction with a widget in your test, use the WidgetTester
  4 +// utility in the flutter_test package. For example, you can send tap and scroll
  5 +// gestures. You can also use WidgetTester to find child widgets in the widget
  6 +// tree, read text, and verify that the values of widget properties are correct.
  7 +
  8 +import 'package:flutter/material.dart';
  9 +import 'package:flutter_test/flutter_test.dart';
  10 +
  11 +import 'package:auto_track_example/main.dart';
  12 +
  13 +void main() {
  14 + testWidgets('Verify Platform version', (WidgetTester tester) async {
  15 + // Build our app and trigger a frame.
  16 + await tester.pumpWidget(const MyApp());
  17 +
  18 + // Verify that platform version is retrieved.
  19 + expect(
  20 + find.byWidgetPredicate(
  21 + (Widget widget) => widget is Text &&
  22 + widget.data!.startsWith('Running on:'),
  23 + ),
  24 + findsOneWidget,
  25 + );
  26 + });
  27 +}
  1 +.idea/
  2 +.vagrant/
  3 +.sconsign.dblite
  4 +.svn/
  5 +
  6 +.DS_Store
  7 +*.swp
  8 +profile
  9 +
  10 +DerivedData/
  11 +build/
  12 +GeneratedPluginRegistrant.h
  13 +GeneratedPluginRegistrant.m
  14 +
  15 +.generated/
  16 +
  17 +*.pbxuser
  18 +*.mode1v3
  19 +*.mode2v3
  20 +*.perspectivev3
  21 +
  22 +!default.pbxuser
  23 +!default.mode1v3
  24 +!default.mode2v3
  25 +!default.perspectivev3
  26 +
  27 +xcuserdata
  28 +
  29 +*.moved-aside
  30 +
  31 +*.pyc
  32 +*sync/
  33 +Icon?
  34 +.tags*
  35 +
  36 +/Flutter/Generated.xcconfig
  37 +/Flutter/ephemeral/
  38 +/Flutter/flutter_export_environment.sh
  1 +import Flutter
  2 +import UIKit
  3 +
  4 +public class AutoTrackPlugin: NSObject, FlutterPlugin {
  5 + public static func register(with registrar: FlutterPluginRegistrar) {
  6 + let channel = FlutterMethodChannel(name: "auto_track", binaryMessenger: registrar.messenger())
  7 + let instance = AutoTrackPlugin()
  8 + registrar.addMethodCallDelegate(instance, channel: channel)
  9 + }
  10 +
  11 + public func handle(_ call: FlutterMethodCall, result: @escaping FlutterResult) {
  12 + switch call.method {
  13 + case "getPlatformVersion":
  14 + result("iOS " + UIDevice.current.systemVersion)
  15 + default:
  16 + result(FlutterMethodNotImplemented)
  17 + }
  18 + }
  19 +}
  1 +#
  2 +# To learn more about a Podspec see http://guides.cocoapods.org/syntax/podspec.html.
  3 +# Run `pod lib lint auto_track.podspec` to validate before publishing.
  4 +#
  5 +Pod::Spec.new do |s|
  6 + s.name = 'auto_track'
  7 + s.version = '0.0.1'
  8 + s.summary = 'Auto Track Plugin'
  9 + s.description = <<-DESC
  10 +Auto Track Plugin
  11 + DESC
  12 + s.homepage = 'http://example.com'
  13 + s.license = { :file => '../LICENSE' }
  14 + s.author = { 'Your Company' => 'email@example.com' }
  15 + s.source = { :path => '.' }
  16 + s.source_files = 'Classes/**/*'
  17 + s.dependency 'Flutter'
  18 + s.platform = :ios, '11.0'
  19 +
  20 + # Flutter.framework does not contain a i386 slice.
  21 + s.pod_target_xcconfig = { 'DEFINES_MODULE' => 'YES', 'EXCLUDED_ARCHS[sdk=iphonesimulator*]' => 'i386' }
  22 + s.swift_version = '5.0'
  23 +end
  1 +library autotrack;
  2 +
  3 +export './auto_track/index.dart';
  4 +export './auto_track/config/config.dart';
  5 +export './auto_track/page_view/navigation_observer.dart';
  6 +export './auto_track/click/navigator_key.dart';
  7 +export './auto_track/click/element_key.dart';
  8 +export './auto_track/log/logger.dart';
  1 +import 'package:flutter/widgets.dart';
  2 +
  3 +import '../config/manager.dart';
  4 +import '../page_view/page_info.dart';
  5 +import '../utils/element_util.dart';
  6 +import 'element_key.dart';
  7 +import 'xpath.dart';
  8 +
  9 +class ClickInfo {
  10 + ClickInfo._(this.pageInfo);
  11 +
  12 + factory ClickInfo.from({
  13 + required Element gestureElement,
  14 + required PointerDownEvent event,
  15 + required Element pageElement,
  16 + required PageInfo pageInfo,
  17 + }) {
  18 + ClickInfo clickInfo = ClickInfo._(pageInfo);
  19 + clickInfo._touchX = event.position.dx.round();
  20 + clickInfo._touchY = event.position.dy.round();
  21 +
  22 + XPath xpath = XPath.createBy(element: gestureElement, pageElement: pageElement);
  23 + Element element = xpath.targetElement;
  24 + clickInfo._elementType = element.widget.runtimeType.toString();
  25 + clickInfo._elementWidth = element.size?.width.round() ?? 0;
  26 + clickInfo._elementHeight = element.size?.height.round() ?? 0;
  27 + clickInfo._elementPath = xpath.toString();
  28 + clickInfo._texts = ElementUtil.findTexts(element);
  29 + Key? key = element.widget.key;
  30 + if (key != null && key is ValueKey) {
  31 + clickInfo._elementManualKey = (key).value;
  32 + } else {
  33 + clickInfo._elementManualKey = key?.toString() ?? '';
  34 + }
  35 + clickInfo._ignore = AutoTrackConfigManager.instance.isIgnoreElement(key);
  36 + if (key is AutoTrackElementKey && !clickInfo._ignore) {
  37 + clickInfo._ignore = key.ignore;
  38 + }
  39 +
  40 + return clickInfo;
  41 + }
  42 +
  43 + int _touchX = 0;
  44 + int get touchX => _touchX;
  45 +
  46 + int _touchY = 0;
  47 + int get touchY => _touchY;
  48 +
  49 + int _elementWidth = 0;
  50 + int get elementWidth => _elementWidth;
  51 +
  52 + int _elementHeight = 0;
  53 + int get elementHeight => _elementHeight;
  54 +
  55 + String _elementType = '';
  56 + String get elementType => _elementType;
  57 +
  58 + String _elementManualKey = '';
  59 + String get elementManualKey => _elementManualKey;
  60 +
  61 + String _elementPath = '';
  62 + String get elementPath => _elementPath;
  63 +
  64 + bool _ignore = false;
  65 + bool get ignore => _ignore;
  66 +
  67 + List<String> _texts = [];
  68 + List<String> get texts => _texts;
  69 +
  70 + final PageInfo pageInfo;
  71 +
  72 + @override
  73 + String toString() {
  74 + return [
  75 + 'elementType: $elementType',
  76 + 'elementManualKey: $elementManualKey',
  77 + 'elementPath: $elementPath',
  78 + 'touchX: $touchX',
  79 + 'touchY: $touchY',
  80 + 'elementWidth: $elementWidth',
  81 + 'elementHeight: $elementHeight',
  82 + 'texts: ${texts.join(";")}',
  83 + 'pageInfo: $pageInfo',
  84 + ].join(', ');
  85 + }
  86 +}
  1 +import 'package:flutter/widgets.dart';
  2 +
  3 +class AutoTrackElementKey extends Key {
  4 + const AutoTrackElementKey(this.name, {
  5 + this.ignore = false
  6 + }) : super.empty();
  7 +
  8 + final String name;
  9 + final bool ignore;
  10 +
  11 + @override
  12 + String toString() {
  13 + return name;
  14 + }
  15 +}
  1 +import 'package:flutter/widgets.dart';
  2 +
  3 +class AutoTrackNavigatorKey {
  4 + static AutoTrackNavigatorKey? _instance;
  5 + AutoTrackNavigatorKey._();
  6 +
  7 + static AutoTrackNavigatorKey _getInstance() {
  8 + _instance ??= AutoTrackNavigatorKey._();
  9 + return _instance!;
  10 + }
  11 +
  12 + GlobalKey<NavigatorState> _navigatorKey = GlobalKey<NavigatorState>();
  13 +
  14 + static GlobalKey<NavigatorState> navigatorKeyWrap(GlobalKey<NavigatorState>? navigatorKey) {
  15 + if (navigatorKey != null) {
  16 + _getInstance()._navigatorKey = navigatorKey;
  17 + }
  18 + return _getInstance()._navigatorKey;
  19 + }
  20 +
  21 + static GlobalKey<NavigatorState> get navigatorKey => _getInstance()._navigatorKey;
  22 +}
  1 +import 'dart:collection';
  2 +
  3 +import 'package:flutter/gestures.dart';
  4 +import 'package:flutter/rendering.dart';
  5 +import 'package:flutter/widgets.dart';
  6 +
  7 +
  8 +import '../log/logger.dart';
  9 +import '../page_view/page_info.dart';
  10 +import '../page_view/page_stack.dart';
  11 +import '../track/track.dart';
  12 +import '../utils/element_util.dart';
  13 +import 'click_info.dart';
  14 +
  15 +class PointerEventListener {
  16 + static PointerEventListener instance = PointerEventListener._();
  17 + PointerEventListener._();
  18 +
  19 + bool _started = false;
  20 + _AutoTrackTapGestureRecognizer _gestureRecognizer = _AutoTrackTapGestureRecognizer();
  21 +
  22 + void start() {
  23 + if (!_started) {
  24 + GestureBinding.instance?.pointerRouter.addGlobalRoute(_pointerRoute);
  25 + _gestureRecognizer = _AutoTrackTapGestureRecognizer();
  26 + _gestureRecognizer.onTap = (){};
  27 + _started = true;
  28 + }
  29 + }
  30 +
  31 + void stop() {
  32 + if (_started) {
  33 + GestureBinding.instance?.pointerRouter.removeGlobalRoute(_pointerRoute);
  34 + _gestureRecognizer.dispose();
  35 + _started = false;
  36 + }
  37 + }
  38 +
  39 + void _pointerRoute(PointerEvent event) {
  40 + try {
  41 + if (event is PointerDownEvent) {
  42 + _gestureRecognizer.addPointer(event);
  43 + } else if (event is PointerUpEvent) {
  44 + _gestureRecognizer.checkPointerUp(event);
  45 + PointerDownEvent? pointerDownEvent = _gestureRecognizer.lastPointerDownEvent;
  46 + if (event.pointer != _gestureRecognizer.rejectPointer && pointerDownEvent != null) {
  47 + _checkTapElementAndTrack(pointerDownEvent, event);
  48 + }
  49 + }
  50 + } catch (e) {
  51 + AutoTrackLogger.getInstance().error(e);
  52 + }
  53 + }
  54 +
  55 + void _checkTapElementAndTrack(PointerDownEvent event, PointerUpEvent upEvent) {
  56 + final page = PageStack.instance.getCurrentPage();
  57 + if (page == null) {
  58 + return;
  59 + }
  60 +
  61 + LinkedList<_HitEntry> hits = LinkedList();
  62 + ElementUtil.walkElement(page.element, (child, _) {
  63 + if (child is RenderObjectElement && child.renderObject is RenderBox) {
  64 + RenderBox renderBox = child.renderObject as RenderBox;
  65 + if (!renderBox.hasSize) {
  66 + return false;
  67 + }
  68 +
  69 + Offset localPosition = renderBox.globalToLocal(upEvent.position);
  70 + if (!renderBox.size.contains(localPosition)) {
  71 + return false;
  72 + }
  73 +
  74 + if (renderBox is RenderPointerListener) {
  75 + hits.add(_HitEntry(child));
  76 + }
  77 + }
  78 + return true;
  79 + });
  80 +
  81 + if (hits.isEmpty) {
  82 + return;
  83 + }
  84 +
  85 + _HitEntry? entry = hits.last;
  86 + Element? gestureElement;
  87 + while (entry != null) {
  88 + gestureElement = ElementUtil.findAncestorElementOfWidgetType<GestureDetector>(entry.element);
  89 + if (gestureElement != null) {
  90 + break;
  91 + }
  92 + entry = entry.previous;
  93 + }
  94 +
  95 + if (gestureElement != null) {
  96 + _trackClick(gestureElement, event, page.element, page.pageInfo);
  97 + }
  98 + }
  99 +
  100 + void _trackClick(Element gestureElement, PointerDownEvent event, Element pageElement, PageInfo pageInfo) {
  101 + ClickInfo clickInfo = ClickInfo.from(
  102 + gestureElement: gestureElement,
  103 + event: event,
  104 + pageElement: pageElement,
  105 + pageInfo: pageInfo,
  106 + );
  107 + if (!clickInfo.ignore) {
  108 + Track.instance.click(clickInfo);
  109 + }
  110 + }
  111 +}
  112 +
  113 +class _AutoTrackTapGestureRecognizer extends TapGestureRecognizer {
  114 + _AutoTrackTapGestureRecognizer({ Object? debugOwner }) : super(debugOwner: debugOwner);
  115 +
  116 + PointerDownEvent? lastPointerDownEvent;
  117 + int rejectPointer = 0;
  118 +
  119 + void checkPointerUp(PointerUpEvent upEvent) {
  120 + if (lastPointerDownEvent == null) {
  121 + return;
  122 + }
  123 +
  124 + Offset downPosition = lastPointerDownEvent!.position;
  125 + Offset upPosition = upEvent.position;
  126 + double offset = (downPosition.dx - upPosition.dx).abs() + (downPosition.dy - upPosition.dy).abs();
  127 + if (offset > 30) {
  128 + rejectGesture(upEvent.pointer);
  129 + }
  130 + }
  131 +
  132 + @override
  133 + void addPointer(PointerDownEvent event) {
  134 + lastPointerDownEvent = event;
  135 + super.addPointer(event);
  136 + }
  137 +
  138 + @override
  139 + void rejectGesture(int pointer) {
  140 + if (lastPointerDownEvent?.pointer == pointer) {
  141 + lastPointerDownEvent = null;
  142 + }
  143 + rejectPointer = pointer;
  144 + super.rejectGesture(pointer);
  145 + }
  146 +}
  147 +
  148 +final class _HitEntry extends LinkedListEntry<_HitEntry> {
  149 + _HitEntry(this.element);
  150 + final Element element;
  151 +}
  1 +import 'dart:collection';
  2 +
  3 +import 'package:flutter/material.dart';
  4 +
  5 +class XPath {
  6 + XPath._(this._targetElement);
  7 +
  8 + factory XPath.createBy({
  9 + required Element element,
  10 + required Element pageElement,
  11 + }) {
  12 + XPath xpath = XPath._(element);
  13 + xpath._targetElement = element;
  14 +
  15 + final highLevelSet = _PathConst.highLevelSet;
  16 + LinkedList<_ElementEntry> originalPath = LinkedList();
  17 + originalPath.add(_ElementEntry(element));
  18 +
  19 + bool lookForTarget = true;
  20 + element.visitAncestorElements((parent) {
  21 + if (parent.widget is GestureDetector) {
  22 + lookForTarget = false;
  23 + }
  24 + if (lookForTarget && highLevelSet.contains(parent.widget.runtimeType)) {
  25 + xpath._targetElement = parent;
  26 + }
  27 + originalPath.add(_ElementEntry(parent));
  28 + if (pageElement == parent) {
  29 + return false;
  30 + }
  31 + return true;
  32 + });
  33 +
  34 + LinkedList<PathNode> path = xpath._buildFromOriginal(xpath._targetElement, originalPath);
  35 + xpath._shortPath(path);
  36 +
  37 + if (path.isNotEmpty) {
  38 + path.first.isPage = true;
  39 + }
  40 + for (var node in path) {
  41 + node.computeIndex();
  42 + }
  43 + xpath._path = path;
  44 +
  45 + return xpath;
  46 + }
  47 +
  48 + Element _targetElement;
  49 + Element get targetElement => _targetElement;
  50 +
  51 + LinkedList<PathNode> _path = LinkedList();
  52 + LinkedList<PathNode> get path => _path;
  53 +
  54 + LinkedList<PathNode> _buildFromOriginal(Element targetElement, LinkedList<_ElementEntry> originalPath) {
  55 + LinkedList<PathNode> path = LinkedList();
  56 + if (originalPath.isEmpty) {
  57 + return path;
  58 + }
  59 +
  60 + _ElementEntry? entry = originalPath.last;
  61 + while (entry != null) {
  62 + PathNode node = PathNode(entry.element);
  63 + if (!node.ignore) {
  64 + node.formatName();
  65 + path.add(node);
  66 + }
  67 + if (entry.element == targetElement) {
  68 + break;
  69 + }
  70 + entry = entry.previous;
  71 + }
  72 + return path;
  73 + }
  74 +
  75 + void _shortPath(LinkedList<PathNode> path) {
  76 + if (path.isEmpty) {
  77 + return;
  78 + }
  79 +
  80 + final shortWidgetMap = _PathConst.shortWidgetMap;
  81 + PathNode? node = path.first;
  82 + while (node != null) {
  83 + if (shortWidgetMap.containsKey(node.name)) {
  84 + _ShortWidgetConfig short = shortWidgetMap[node.name]!;
  85 + node = _removeInternal(path, node, short);
  86 + } else {
  87 + node = node.next;
  88 + }
  89 + }
  90 + }
  91 +
  92 + PathNode? _removeInternal(LinkedList<PathNode> path, PathNode node, _ShortWidgetConfig short) {
  93 + PathNode? internalNode = node.next;
  94 + Element? indexElement;
  95 + for (String internalWidgetName in short.internalWidgets) {
  96 + if (internalNode == null) {
  97 + return null;
  98 + }
  99 +
  100 + if (internalNode.name != internalWidgetName) {
  101 + return internalNode;
  102 + }
  103 + if (internalWidgetName == short.indexWidget) {
  104 + indexElement = internalNode.indexElement;
  105 + }
  106 + PathNode tmpNode = internalNode;
  107 + internalNode = internalNode.next;
  108 + path.remove(tmpNode);
  109 + }
  110 + if (indexElement != null) {
  111 + internalNode?.indexElement = indexElement;
  112 + }
  113 + return internalNode;
  114 + }
  115 +
  116 + @override
  117 + String toString() {
  118 + return path.join('/');
  119 + }
  120 +}
  121 +
  122 +final class _ElementEntry extends LinkedListEntry<_ElementEntry> {
  123 + _ElementEntry(this.element);
  124 + final Element element;
  125 +}
  126 +
  127 +final class PathNode extends LinkedListEntry<PathNode> {
  128 + PathNode(this.indexElement) {
  129 + _name = indexElement.widget.runtimeType.toString();
  130 + _checkIgnore(indexElement);
  131 + }
  132 +
  133 + String _name = '';
  134 + String get name => _name;
  135 +
  136 + int _index = 0;
  137 + int get index => _index;
  138 +
  139 + bool _ignore = false;
  140 + bool get ignore => _ignore;
  141 +
  142 + bool isPage = false;
  143 + Element indexElement;
  144 +
  145 + void formatName() {
  146 + String widgetName = _name;
  147 + int index = widgetName.indexOf('\<');
  148 + if (index > -1) {
  149 + _name = widgetName.substring(0, index);
  150 + }
  151 + }
  152 +
  153 + void _checkIgnore(Element element) {
  154 + Widget widget = element.widget;
  155 + if (widget is! StatelessWidget && widget is! StatefulWidget) {
  156 + _ignore = true;
  157 + return;
  158 + }
  159 +
  160 + if (_name[0] == '_') {
  161 + _ignore = true;
  162 + return;
  163 + }
  164 + }
  165 +
  166 + void computeIndex() {
  167 + Element? parent;
  168 + indexElement.visitAncestorElements((element) {
  169 + parent = element;
  170 + return false;
  171 + });
  172 + if (parent == null) {
  173 + isPage = true;
  174 + return;
  175 + }
  176 +
  177 + bool found = false;
  178 + _index = 0;
  179 + parent!.visitChildElements((element) {
  180 + if (element == indexElement) {
  181 + found = true;
  182 + }
  183 + if (!found) {
  184 + _index++;
  185 + }
  186 + });
  187 + }
  188 +
  189 + @override
  190 + String toString() {
  191 + if (isPage) {
  192 + return _name;
  193 + }
  194 + return '$_name[$_index]';
  195 + }
  196 +}
  197 +
  198 +class _PathConst {
  199 + static final Set<Type> highLevelSet = {
  200 + InkWell,
  201 + ElevatedButton,
  202 + IconButton,
  203 + TextButton,
  204 + ListTile,
  205 + };
  206 + /// key: Widget Name, value: Widget Name who handle child/children
  207 + static final Map<String, _ShortWidgetConfig> shortWidgetMap = {
  208 + 'Scaffold': _ShortWidgetConfig([
  209 + 'ScrollNotificationObserver',
  210 + 'Material',
  211 + 'AnimatedPhysicalModel',
  212 + 'AnimatedDefaultTextStyle',
  213 + 'AnimatedBuilder',
  214 + 'Actions'
  215 + ]),
  216 + 'AppBar': _ShortWidgetConfig([
  217 + 'Material',
  218 + 'AnimatedPhysicalModel',
  219 + 'AnimatedDefaultTextStyle',
  220 + 'SafeArea',
  221 + 'Builder',
  222 + 'NavigationToolbar',
  223 + ]),
  224 + 'BottomNavigationBar': _ShortWidgetConfig([
  225 + 'Material',
  226 + 'AnimatedPhysicalModel',
  227 + 'AnimatedDefaultTextStyle',
  228 + 'Material',
  229 + 'AnimatedDefaultTextStyle',
  230 + 'Builder',
  231 + ]),
  232 + 'ListView': _ShortWidgetConfig([
  233 + 'Scrollable',
  234 + 'RawGestureDetector',
  235 + 'KeyedSubtree',
  236 + 'AutomaticKeepAlive',
  237 + ], indexWidget: 'KeyedSubtree'),
  238 + 'PageView': _ShortWidgetConfig([
  239 + 'Scrollable',
  240 + 'RawGestureDetector',
  241 + 'SliverFillViewport',
  242 + 'KeyedSubtree',
  243 + 'AutomaticKeepAlive',
  244 + ], indexWidget: 'KeyedSubtree'),
  245 + 'Card': _ShortWidgetConfig([
  246 + 'Container',
  247 + 'Material',
  248 + 'AnimatedDefaultTextStyle',
  249 + ]),
  250 + 'IconButton': _ShortWidgetConfig([
  251 + 'InkResponse',
  252 + 'Actions',
  253 + 'Focus',
  254 + 'GestureDetector',
  255 + 'RawGestureDetector',
  256 + ]),
  257 + 'InkResponse': _ShortWidgetConfig([
  258 + 'Actions',
  259 + 'Focus',
  260 + 'GestureDetector',
  261 + ]),
  262 + };
  263 +}
  264 +
  265 +class _ShortWidgetConfig {
  266 + _ShortWidgetConfig(this.internalWidgets, { this.indexWidget });
  267 + final List<String> internalWidgets;
  268 + final String? indexWidget;
  269 +}
  1 +import 'dart:convert';
  2 +
  3 +import 'package:crypto/crypto.dart';
  4 +import 'package:flutter/widgets.dart';
  5 +import 'package:uuid/uuid.dart';
  6 +
  7 +class AutoTrackConfig {
  8 + AutoTrackConfig({
  9 + this.host,
  10 + this.appKey = '',
  11 + this.appSecret = '',
  12 + this.trackId,
  13 + this.userId,
  14 + this.signature,
  15 + this.pageConfigs = const [],
  16 + this.useCustomRoute = false,
  17 + this.ignoreElementKeys = const [],
  18 + this.ignoreElementStringKeys = const [],
  19 + this.enablePageView = true,
  20 + this.enablePageLeave = false,
  21 + this.enableClick = true,
  22 + this.enableUpload = false
  23 + }) {
  24 + trackId ??= const Uuid().v4().replaceAll('-', '');
  25 + signature ??= (t) => sha256.convert(utf8.encode('$appKey$t$appSecret')).toString();
  26 + }
  27 +
  28 + String? host;
  29 + String? appKey;
  30 + String? appSecret;
  31 + String? trackId;
  32 + String? userId;
  33 + Function? signature;
  34 +
  35 + List<AutoTrackPageConfig> pageConfigs;
  36 +
  37 + /// 如果使用 MaterialPageRoute/PageRoute/ModalRoute 之外的 Route,
  38 + /// 请打开该开关,并保证所有页面都配置在 pageConfigs 中
  39 + bool useCustomRoute;
  40 +
  41 + /// 推荐使用 [ElementKey]
  42 + List<Key> ignoreElementKeys;
  43 +
  44 + List<String> ignoreElementStringKeys;
  45 +
  46 + Set<Key> getIgnoreElementKeySet() => Set.from(ignoreElementKeys);
  47 +
  48 + Set<String> getIgnoreElementStringKeySet() =>
  49 + Set.from(ignoreElementStringKeys);
  50 +
  51 + bool enablePageView;
  52 +
  53 + bool enablePageLeave;
  54 +
  55 + bool enableClick;
  56 +
  57 + bool enableUpload;
  58 +}
  59 +
  60 +typedef PageWidgetFunc = bool Function(Widget);
  61 +
  62 +class AutoTrackPageConfig<T extends Widget> {
  63 + AutoTrackPageConfig({
  64 + this.pageID,
  65 + this.pagePath,
  66 + this.ignore = false,
  67 + this.pageTitle,
  68 + this.isPageWidget
  69 + }) {
  70 + isPageWidget ??= (pageWidget) => pageWidget is T;
  71 + }
  72 +
  73 + String? pageID;
  74 + String? pagePath;
  75 + bool ignore;
  76 + String? pageTitle;
  77 + PageWidgetFunc? isPageWidget;
  78 +
  79 +// bool isPageWidget(Widget pageWidget) => pageWidget is T;
  80 +}
  1 +import 'package:auto_track/auto_track/config/queue.dart';
  2 +import 'package:flutter/widgets.dart';
  3 +
  4 +import 'config.dart';
  5 +
  6 +class AutoTrackConfigManager {
  7 + static final AutoTrackConfigManager instance = AutoTrackConfigManager._();
  8 + AutoTrackConfigManager._();
  9 +
  10 + AutoTrackConfig _config = AutoTrackConfig();
  11 + AutoTrackConfig get config => _config;
  12 +
  13 + bool _autoTrackEnable = false;
  14 + bool get autoTrackEnable => _autoTrackEnable;
  15 +
  16 + void updateConfig(AutoTrackConfig config) {
  17 + _config = config;
  18 + if (config.enableUpload) {
  19 + AutoTrackQueue.instance.start();
  20 + } else {
  21 + AutoTrackQueue.instance.stop();
  22 + }
  23 + }
  24 +
  25 + void updateUserId(String userId) {
  26 + _config.userId = userId;
  27 + }
  28 +
  29 + void updatePageConfigs(List<AutoTrackPageConfig> pageConfigs) {
  30 + _config.pageConfigs = pageConfigs;
  31 + }
  32 +
  33 + void updateIgnoreElementKeys(List<Key> ignoreElementKeys) {
  34 + _config.ignoreElementKeys = ignoreElementKeys;
  35 + }
  36 +
  37 + void updateIgnoreElementStringKeys(List<String> ignoreElementStringKeys) {
  38 + _config.ignoreElementStringKeys = ignoreElementStringKeys;
  39 + }
  40 +
  41 + void enablePageView(bool enable) {
  42 + _config.enablePageView = enable;
  43 + }
  44 +
  45 + void enablePageLeave(bool enable) {
  46 + _config.enablePageLeave = enable;
  47 + }
  48 +
  49 + void enableClick(bool enable) {
  50 + _config.enableClick = enable;
  51 + }
  52 +
  53 + void enableAutoTrack(bool enable) {
  54 + _autoTrackEnable = enable;
  55 + }
  56 +
  57 + void enableUpload(bool enable) {
  58 + _config.enableUpload = enable;
  59 + if (enable) {
  60 + AutoTrackQueue.instance.start();
  61 + } else {
  62 + AutoTrackQueue.instance.stop();
  63 + }
  64 + }
  65 +
  66 + List<AutoTrackPageConfig> get pageConfigs => _config.pageConfigs;
  67 +
  68 + bool get useCustomRoute => _config.useCustomRoute;
  69 +
  70 + AutoTrackPageConfig getPageConfig(Widget pageWidget) {
  71 + return _config.pageConfigs.firstWhere(
  72 + (pageConfig) => pageConfig.isPageWidget!(pageWidget),
  73 + orElse: () => AutoTrackPageConfig()
  74 + );
  75 + }
  76 +
  77 + Set<Key> getIgnoreElementKeySet() => _config.getIgnoreElementKeySet();
  78 +
  79 + Set<String> getIgnoreElementStringKeySet() => _config.getIgnoreElementStringKeySet();
  80 +
  81 + bool isIgnoreElement(Key? key) {
  82 + if (key == null) {
  83 + return false;
  84 + }
  85 + if (getIgnoreElementKeySet().contains(key)) {
  86 + return true;
  87 + }
  88 + if (getIgnoreElementStringKeySet().contains(key.toString())) {
  89 + return true;
  90 + }
  91 + return false;
  92 + }
  93 +
  94 + bool get pageViewEnabled => _config.enablePageView;
  95 +
  96 + bool get pageLeaveEnable => _config.enablePageLeave;
  97 +
  98 + bool get clickEnable => _config.enableClick;
  99 +}
  1 +import 'dart:async';
  2 +
  3 +import 'package:auto_track/auto_track/config/manager.dart';
  4 +import 'package:auto_track/auto_track/utils/track_model.dart';
  5 +import 'package:dio/dio.dart';
  6 +
  7 +import '../log/logger.dart';
  8 +
  9 +
  10 +
  11 +class AutoTrackQueue {
  12 + static final AutoTrackQueue instance = AutoTrackQueue._();
  13 + AutoTrackQueue._();
  14 +
  15 + Timer? _timer;
  16 + final List<TrackModel> _queue = [];
  17 + final dio = Dio();
  18 +
  19 + void appendQueue(TrackModel model) {
  20 + if (_timer == null) return;
  21 + _queue.add(model);
  22 + }
  23 +
  24 + void start() {
  25 + if (_timer != null) return;
  26 + _timer = Timer.periodic(const Duration(seconds: 10), (timer) {
  27 + flush();
  28 + });
  29 + }
  30 +
  31 + void stop() {
  32 + _timer?.cancel();
  33 + _timer = null;
  34 + }
  35 +
  36 + void flush() {
  37 + if (_queue.isEmpty) return;
  38 + final uploadList = List.from(_queue);
  39 + _queue.clear();
  40 + final config = AutoTrackConfigManager.instance.config;
  41 + final host = config.host;
  42 + if (host != null) {
  43 + final t = DateTime.now().millisecond;
  44 + dio.post(host, data: {
  45 + 'app_key': config.appKey ?? '',
  46 + 'signature': config.signature!(t),
  47 + 't': t,
  48 + 'user_id': config.userId ?? '',
  49 + 'track_id': config.trackId ?? '',
  50 + 'data_list': uploadList.map((e) => e.toMap()).toList(),
  51 + }).onError((error, stackTrace) {
  52 + AutoTrackLogger.getInstance().error(error!);
  53 + return Future.value(Response(statusCode: 500, requestOptions: RequestOptions(path: host)));
  54 + });
  55 + }
  56 + }
  57 +}
  1 +import 'package:flutter/foundation.dart';
  2 +
  3 +import 'click/pointer_event_listener.dart';
  4 +import 'config/config.dart';
  5 +import 'config/manager.dart';
  6 +import 'log/logger.dart';
  7 +
  8 +class AutoTrack {
  9 + static final AutoTrack _instance = AutoTrack._();
  10 + AutoTrack._();
  11 +
  12 + factory AutoTrack({ AutoTrackConfig? config }) {
  13 + _instance.config(config);
  14 + return _instance;
  15 + }
  16 +
  17 + void updateUserId(String id) {
  18 + AutoTrackConfigManager.instance.updateUserId(id);
  19 + }
  20 +
  21 + AutoTrack config(AutoTrackConfig? config) {
  22 + if (config != null) {
  23 + AutoTrackConfigManager.instance.updateConfig(config);
  24 + }
  25 + return _instance;
  26 + }
  27 +
  28 + AutoTrack pageConfigs(List<AutoTrackPageConfig>? pageConfigs) {
  29 + if (pageConfigs != null) {
  30 + AutoTrackConfigManager.instance.updatePageConfigs(pageConfigs);
  31 + }
  32 + return _instance;
  33 + }
  34 +
  35 + AutoTrack ignoreElementKeys(List<Key>? ignoreElementKeys) {
  36 + if (ignoreElementKeys != null) {
  37 + AutoTrackConfigManager.instance.updateIgnoreElementKeys(ignoreElementKeys);
  38 + }
  39 + return _instance;
  40 + }
  41 +
  42 + AutoTrack ignoreElementStringKeys(List<String>? ignoreElementStringKeys) {
  43 + if (ignoreElementStringKeys != null) {
  44 + AutoTrackConfigManager.instance.updateIgnoreElementStringKeys(ignoreElementStringKeys);
  45 + }
  46 + return _instance;
  47 + }
  48 +
  49 + AutoTrack enablePageView() {
  50 + AutoTrackConfigManager.instance.enablePageView(true);
  51 + return _instance;
  52 + }
  53 +
  54 + AutoTrack disablePageView() {
  55 + AutoTrackConfigManager.instance.enablePageView(false);
  56 + return _instance;
  57 + }
  58 +
  59 + AutoTrack enablePageLeave() {
  60 + AutoTrackConfigManager.instance.enablePageLeave(true);
  61 + return _instance;
  62 + }
  63 +
  64 + AutoTrack disablePageLeave() {
  65 + AutoTrackConfigManager.instance.enablePageLeave(false);
  66 + return _instance;
  67 + }
  68 +
  69 + AutoTrack enableUpload() {
  70 + AutoTrackConfigManager.instance.enableUpload(true);
  71 + return _instance;
  72 + }
  73 +
  74 + AutoTrack disableUpload() {
  75 + AutoTrackConfigManager.instance.enableUpload(false);
  76 + return _instance;
  77 + }
  78 +
  79 + AutoTrack enableClick() {
  80 + AutoTrackConfigManager.instance.enableClick(true);
  81 + return _instance;
  82 + }
  83 +
  84 + AutoTrack disableClick() {
  85 + AutoTrackConfigManager.instance.enableClick(false);
  86 + return _instance;
  87 + }
  88 +
  89 + AutoTrack enable() {
  90 + AutoTrackConfigManager.instance.enableAutoTrack(true);
  91 + PointerEventListener.instance.start();
  92 + return _instance;
  93 + }
  94 +
  95 + AutoTrack disable() {
  96 + AutoTrackConfigManager.instance.enableAutoTrack(false);
  97 + PointerEventListener.instance.stop();
  98 + return _instance;
  99 + }
  100 +
  101 + AutoTrack enableLog() {
  102 + final logger = AutoTrackLogger.getInstance();
  103 + if (!logger.hasHandler) {
  104 + logger.setHandler((level, message) {
  105 + if (kDebugMode) {
  106 + print('AutoTrack [$level] - $message');
  107 + }
  108 + });
  109 + }
  110 + return _instance;
  111 + }
  112 +}
  1 +import 'package:dio/dio.dart';
  2 +import 'package:flutter/widgets.dart';
  3 +
  4 +typedef AutoTrackLoggerHandler = void Function(AutoTrackLoggerLevel level, String message);
  5 +
  6 +class AutoTrackLogger {
  7 + static final AutoTrackLogger _instance = AutoTrackLogger();
  8 + static AutoTrackLogger getInstance() => _instance;
  9 +
  10 + List<_LoggerData> _data = [];
  11 + AutoTrackLoggerHandler? _handler;
  12 + bool _isPrinting = false;
  13 + bool get hasHandler => _handler != null;
  14 +
  15 + void info(String message) {
  16 + _print(AutoTrackLoggerLevel.Info, message);
  17 + }
  18 +
  19 + void debug(String message) {
  20 + _print(AutoTrackLoggerLevel.Debug, message);
  21 + }
  22 +
  23 + void error(Object e) {
  24 + String message = Error.safeToString(e);
  25 + if (e is FlutterError) {
  26 + message = e.message;
  27 + } else if (e is Error) {
  28 + message = e.stackTrace.toString();
  29 + } else if (e is DioException) {
  30 + message = (e).message ?? 'dio exception with unknown message';
  31 + }
  32 + _print(AutoTrackLoggerLevel.Error, message);
  33 + }
  34 +
  35 + void setHandler(AutoTrackLoggerHandler handler) {
  36 + _handler = handler;
  37 + }
  38 +
  39 + void _print(AutoTrackLoggerLevel level, String message) {
  40 + if (_handler == null) {
  41 + return;
  42 + }
  43 +
  44 + _data.add(_LoggerData(level, message));
  45 + if (_isPrinting) {
  46 + return;
  47 + }
  48 +
  49 + _isPrinting = true;
  50 + Future.delayed(const Duration(milliseconds: 300)).then((_) {
  51 + List<_LoggerData> data = _data;
  52 + _data = [];
  53 + if (_handler != null) {
  54 + for (var log in data) {
  55 + _handler!(log.level, log.message);
  56 + }
  57 + }
  58 + _isPrinting = false;
  59 + });
  60 + }
  61 +}
  62 +
  63 +enum AutoTrackLoggerLevel {
  64 + Info,
  65 + Debug,
  66 + Error,
  67 +}
  68 +
  69 +class _LoggerData {
  70 + const _LoggerData(this.level, this.message);
  71 + final AutoTrackLoggerLevel level;
  72 + final String message;
  73 +}
  1 +import 'package:flutter/material.dart';
  2 +import 'package:flutter/scheduler.dart';
  3 +
  4 +import '../config/config.dart';
  5 +import '../config/manager.dart';
  6 +import '../log/logger.dart';
  7 +import '../utils/element_util.dart';
  8 +import 'page_stack.dart';
  9 +
  10 +class AutoTrackNavigationObserver extends NavigatorObserver {
  11 + static List<NavigatorObserver> wrap(List<NavigatorObserver>? navigatorObservers) {
  12 + if (navigatorObservers == null) {
  13 + return [AutoTrackNavigationObserver()];
  14 + }
  15 +
  16 + bool found = false;
  17 + List<NavigatorObserver> removeList = [];
  18 + for (NavigatorObserver observer in navigatorObservers) {
  19 + if (observer is AutoTrackNavigationObserver) {
  20 + if (found) {
  21 + removeList.add(observer);
  22 + }
  23 + found = true;
  24 + }
  25 + }
  26 + for (NavigatorObserver observer in removeList) {
  27 + navigatorObservers.remove(observer);
  28 + }
  29 + if (!found) {
  30 + navigatorObservers.insert(0, AutoTrackNavigationObserver());
  31 + }
  32 + return navigatorObservers;
  33 + }
  34 +
  35 + static List<NavigatorObserver> defaultNavigatorObservers() => [AutoTrackNavigationObserver()];
  36 +
  37 + @override
  38 + void didPop(Route route, Route? previousRoute) {
  39 + super.didPop(route, previousRoute);
  40 + // print('didPop -> $route, previousRoute -> $previousRoute');
  41 + try {
  42 + PageStack.instance.pop(route, previousRoute);
  43 + } catch (e) {
  44 + AutoTrackLogger.getInstance().error(e);
  45 + }
  46 + }
  47 +
  48 + @override
  49 + void didPush(Route route, Route? previousRoute) {
  50 + super.didPush(route, previousRoute);
  51 + // print('didPush -> $route, previousRoute -> $previousRoute');
  52 + try {
  53 + _findElement(route, (element) {
  54 + PageStack.instance.push(route, element, previousRoute);
  55 + });
  56 + } catch (e) {
  57 + AutoTrackLogger.getInstance().error(e);
  58 + }
  59 + }
  60 +
  61 + @override
  62 + void didRemove(Route route, Route? previousRoute) {
  63 + super.didRemove(route, previousRoute);
  64 + // print('didRemove -> $route, previousRoute -> $previousRoute');
  65 + try {
  66 + PageStack.instance.remove(route, previousRoute);
  67 + } catch (e) {
  68 + AutoTrackLogger.getInstance().error(e);
  69 + }
  70 + }
  71 +
  72 + @override
  73 + void didReplace({Route? newRoute, Route? oldRoute}) {
  74 + super.didReplace(newRoute: newRoute, oldRoute: oldRoute);
  75 + // print('didReplace -> $newRoute, oldRoute -> $oldRoute');
  76 + try {
  77 + if (newRoute != null) {
  78 + _findElement(newRoute, (element) {
  79 + PageStack.instance.replace(newRoute, element, oldRoute);
  80 + });
  81 + }
  82 + } catch (e) {
  83 + AutoTrackLogger.getInstance().error(e);
  84 + }
  85 + }
  86 +
  87 + void _findElement(Route route, Function(Element) callback) {
  88 + SchedulerBinding.instance?.addPostFrameCallback((_) {
  89 + if (route is ModalRoute) {
  90 + ModalRoute pageRoute = route;
  91 + ElementUtil.walk(pageRoute.subtreeContext, (element, parent) {
  92 + if (parent != null && parent.widget is Semantics) {
  93 + callback(element);
  94 + return false;
  95 + }
  96 + return true;
  97 + });
  98 + } else if (AutoTrackConfigManager.instance.useCustomRoute) {
  99 + List<AutoTrackPageConfig> pageConfigs = AutoTrackConfigManager.instance.pageConfigs;
  100 + if (pageConfigs.isEmpty) {
  101 + return;
  102 + }
  103 +
  104 + Element? lastPageElement;
  105 + ElementUtil.walk(route.navigator?.context, (element, parent) {
  106 + if (pageConfigs.last.isPageWidget!(element.widget)) {
  107 + lastPageElement = element;
  108 + return false;
  109 + }
  110 + return true;
  111 + });
  112 + if (lastPageElement != null) {
  113 + callback(lastPageElement!);
  114 + }
  115 + }
  116 + });
  117 + }
  118 +}
  1 +import 'package:flutter/material.dart';
  2 +
  3 +import '../config/config.dart';
  4 +import '../config/manager.dart';
  5 +import '../utils/element_util.dart';
  6 +
  7 +class PageInfo {
  8 + PageInfo._(this.timer);
  9 +
  10 + factory PageInfo.fromElement(Element element, Route route) {
  11 + AutoTrackPageConfig pageConfig = AutoTrackConfigManager.instance.getPageConfig(element.widget);
  12 + PageInfo pageInfo = PageInfo._(PageTimer());
  13 + pageInfo._pageKey = element.widget.runtimeType.toString();
  14 + pageInfo._pagePath = pageConfig.pagePath ?? route.settings.name ?? '';
  15 + pageInfo._pageManualKey = pageConfig.pageID ?? '';
  16 + pageInfo._pageTitle = pageConfig.pageTitle ?? pageInfo._findTitle(element) ?? '';
  17 + pageInfo.ignore = pageConfig.ignore;
  18 + return pageInfo;
  19 + }
  20 +
  21 + final PageTimer timer;
  22 + bool isBack = false;
  23 + bool ignore = false;
  24 +
  25 + String _pageKey = '';
  26 + String get pageKey => _pageKey;
  27 +
  28 + String _pageTitle = '';
  29 + String get pageTitle => _pageTitle;
  30 +
  31 + String _pageManualKey = '';
  32 + String get pageManualKey => _pageManualKey;
  33 +
  34 + String _pagePath = '';
  35 + String get pagePath => _pagePath;
  36 +
  37 + String? _findTitle(Element element) {
  38 + String? title;
  39 + ElementUtil.walkElement(element, (child, _) {
  40 + if (child.widget is NavigationToolbar) {
  41 + NavigationToolbar toolBar = child.widget as NavigationToolbar;
  42 + if (toolBar.middle == null) {
  43 + return false;
  44 + }
  45 +
  46 + if (toolBar.middle is Text) {
  47 + title = (toolBar.middle as Text).data;
  48 + return false;
  49 + }
  50 +
  51 + int layoutIndex = 0;
  52 + if (toolBar.leading != null) {
  53 + layoutIndex += 1;
  54 + }
  55 + title = _findTextsInMiddle(child, layoutIndex);
  56 + return false;
  57 + }
  58 + return true;
  59 + });
  60 + return title;
  61 + }
  62 + String? _findTextsInMiddle(Element element, int layoutIndex) {
  63 + String? text;
  64 + int index = 0;
  65 + ElementUtil.walkElement(element, ((child, _) {
  66 + if (child.widget is LayoutId) {
  67 + if (index == layoutIndex) {
  68 + text = ElementUtil.findTexts(child).join('');
  69 + return false;
  70 + }
  71 + index += 1;
  72 + }
  73 + return true;
  74 + }));
  75 + return text;
  76 + }
  77 +
  78 + @override
  79 + String toString() => '{ pageKey: $pageKey, pageTitle: $pageTitle, pageManualKey: $pageManualKey, pagePath: $pagePath, isBack: $isBack }';
  80 +}
  81 +
  82 +enum PageTimerState {
  83 + Init,
  84 + Start,
  85 + Pause,
  86 + Resume,
  87 + End,
  88 +}
  89 +
  90 +class PageTimer {
  91 + PageTimer();
  92 +
  93 + PageTimerState _state = PageTimerState.Init;
  94 + PageTimerState get state => _state;
  95 +
  96 + int _lastTimeStamp = 0;
  97 +
  98 + Duration _duration = const Duration();
  99 + Duration get duration => _duration;
  100 +
  101 + int _computeMilliseconds() {
  102 + return DateTime.now().millisecondsSinceEpoch - _lastTimeStamp;
  103 + }
  104 +
  105 + start() {
  106 + if (_state != PageTimerState.Init && _state != PageTimerState.End) {
  107 + return;
  108 + }
  109 +
  110 + _state = PageTimerState.Start;
  111 + _lastTimeStamp = DateTime.now().millisecondsSinceEpoch;
  112 + _duration = const Duration();
  113 + }
  114 +
  115 + pause() {
  116 + if (_state != PageTimerState.Start && _state != PageTimerState.Resume) {
  117 + return;
  118 + }
  119 +
  120 + _state = PageTimerState.Pause;
  121 + _duration = Duration(milliseconds: _duration.inMilliseconds + _computeMilliseconds());
  122 + }
  123 +
  124 + resume() {
  125 + if (_state != PageTimerState.Pause) {
  126 + return;
  127 + }
  128 +
  129 + _state = PageTimerState.Resume;
  130 + _lastTimeStamp = DateTime.now().millisecondsSinceEpoch;
  131 + }
  132 +
  133 + end() {
  134 + if (_state == PageTimerState.Pause) {
  135 + _state = PageTimerState.End;
  136 + return;
  137 + }
  138 +
  139 + if (_state == PageTimerState.Start || _state == PageTimerState.Resume) {
  140 + _state = PageTimerState.End;
  141 + _duration = Duration(milliseconds: _duration.inMilliseconds + _computeMilliseconds());
  142 + }
  143 + }
  144 +}
  1 +import 'dart:collection';
  2 +import 'dart:core';
  3 +
  4 +import 'package:flutter/widgets.dart';
  5 +
  6 +import '../track/track.dart';
  7 +import 'page_info.dart';
  8 +
  9 +class PageStack with WidgetsBindingObserver {
  10 + static final PageStack instance = PageStack._();
  11 + PageStack._() {
  12 + WidgetsBinding.instance?.addObserver(this);
  13 + }
  14 +
  15 + final LinkedList<Page> _stack = LinkedList<Page>();
  16 + final _PageTask _task = _PageTask();
  17 +
  18 + @override
  19 + void didChangeAppLifecycleState(AppLifecycleState state) {
  20 + if (state == AppLifecycleState.resumed) {
  21 + for (var page in _stack) {
  22 + page.pageInfo.timer.resume();
  23 + }
  24 + } else if (state == AppLifecycleState.paused) {
  25 + for (var page in _stack) {
  26 + page.pageInfo.timer.pause();
  27 + }
  28 + }
  29 + }
  30 +
  31 + push(Route route, Element element, Route? previousRoute) {
  32 + Page page = Page(route, element);
  33 + _stack.add(page);
  34 + _task.addPush(page, page.previous);
  35 + }
  36 +
  37 + pop(Route route, Route? previousRoute) {
  38 + if (_stack.isEmpty) {
  39 + return;
  40 + }
  41 +
  42 + Page? page = _findPage(route);
  43 + if (page != null) {
  44 + _task.addPop(page, page.previous);
  45 + }
  46 + _removeAllAfter(page);
  47 + }
  48 +
  49 + remove(Route route, Route? previousRoute) {
  50 + if (_stack.isEmpty) {
  51 + return;
  52 + }
  53 +
  54 + Page? page = _findPage(route);
  55 + if (page != null) {
  56 + _stack.remove(page);
  57 + }
  58 + }
  59 +
  60 + replace(Route newRoute, Element newElement, Route? oldRoute) {
  61 + Page newPage = Page(newRoute, newElement);
  62 + Page? oldPage;
  63 + if (oldRoute != null) {
  64 + oldPage = _findPage(oldRoute);
  65 + _removeAllAfter(oldPage);
  66 + }
  67 + _stack.add(newPage);
  68 + _task.addReplace(newPage, oldPage);
  69 + }
  70 +
  71 + Page? _findPage(Route route) {
  72 + if (_stack.isEmpty) {
  73 + return null;
  74 + }
  75 +
  76 + Page? page = _stack.last;
  77 + while (page != null) {
  78 + if (page.route == route) {
  79 + return page;
  80 + }
  81 + page = page.previous;
  82 + }
  83 + return null;
  84 + }
  85 +
  86 + _removeAllAfter(Page? page) {
  87 + while (page != null) {
  88 + _stack.remove(page);
  89 + page = page.next;
  90 + }
  91 + }
  92 +
  93 + Page? getCurrentPage() {
  94 + if (_stack.isEmpty) {
  95 + return null;
  96 + }
  97 + return _stack.last;
  98 + }
  99 +}
  100 +
  101 +final class Page extends LinkedListEntry<Page> {
  102 + Page._({
  103 + required this.pageInfo,
  104 + required this.route,
  105 + required this.element,
  106 + });
  107 + factory Page(Route route, Element element) {
  108 + return Page._(
  109 + pageInfo: PageInfo.fromElement(element, route),
  110 + route: route,
  111 + element: element,
  112 + );
  113 + }
  114 +
  115 + final PageInfo pageInfo;
  116 + final Route route;
  117 + final Element element;
  118 +
  119 + @override
  120 + String toString() => 'pageInfo: $pageInfo, route: $route';
  121 +}
  122 +
  123 +class _PageTask {
  124 + final List<_PageTaskData> _list = [];
  125 + bool _taskRunning = false;
  126 +
  127 + addPush(Page page, Page? prevPage) {
  128 + _PageTaskData taskData = _PageTaskData(_PageTaskType.Push, page);
  129 + taskData.prevPage = prevPage;
  130 + _list.add(taskData);
  131 + _triggerTask();
  132 + }
  133 +
  134 + addPop(Page page, Page? prevPage) {
  135 + _PageTaskData taskData = _PageTaskData(_PageTaskType.Pop, page);
  136 + taskData.prevPage = prevPage;
  137 + _list.add(taskData);
  138 + _triggerTask();
  139 + }
  140 +
  141 + addReplace(Page page, Page? prevPage) {
  142 + _PageTaskData taskData = _PageTaskData(_PageTaskType.Replace, page);
  143 + taskData.prevPage = prevPage;
  144 + _list.add(taskData);
  145 + _triggerTask();
  146 + }
  147 +
  148 + _triggerTask() {
  149 + if (_taskRunning) {
  150 + return;
  151 + }
  152 +
  153 + _taskRunning = true;
  154 +
  155 + Future.delayed(const Duration(milliseconds: 30)).then((_) => _executeTask());
  156 + }
  157 +
  158 + _executeTask() {
  159 + if (_list.isEmpty) {
  160 + _taskRunning = false;
  161 + return;
  162 + }
  163 +
  164 + List list = _list.sublist(0);
  165 + Page? enterPage, leavePage;
  166 + _list.clear();
  167 + for (_PageTaskData taskData in list as List<_PageTaskData>) {
  168 + if (taskData.type == _PageTaskType.Push) {
  169 + leavePage ??= taskData.prevPage;
  170 + enterPage = taskData.page;
  171 + } else if (taskData.type == _PageTaskType.Pop) {
  172 + leavePage ??= taskData.page;
  173 + if (enterPage == null || enterPage == taskData.page) {
  174 + enterPage = taskData.prevPage;
  175 + enterPage?.pageInfo.isBack = true;
  176 + }
  177 + } else if (taskData.type == _PageTaskType.Replace) {
  178 + leavePage ??= taskData.prevPage;
  179 + if (enterPage == null || enterPage == taskData.prevPage) {
  180 + enterPage = taskData.page;
  181 + }
  182 + }
  183 + }
  184 + if (enterPage != leavePage) {
  185 + if (enterPage != null && !enterPage.pageInfo.ignore) {
  186 + enterPage.pageInfo.timer.start();
  187 + Track.instance.pageView(enterPage.pageInfo);
  188 + }
  189 + if (leavePage != null && !leavePage.pageInfo.ignore) {
  190 + leavePage.pageInfo.timer.end();
  191 + Track.instance.pageLeave(leavePage.pageInfo);
  192 + }
  193 + }
  194 + _taskRunning = false;
  195 + }
  196 +}
  197 +
  198 +class _PageTaskData {
  199 + _PageTaskData(this.type, this.page);
  200 + final _PageTaskType type;
  201 + final Page page;
  202 + Page? prevPage;
  203 +}
  204 +
  205 +enum _PageTaskType {
  206 + Push,
  207 + Pop,
  208 + Replace,
  209 +}