Repository: capacitor-community/background-geolocation Branch: master Commit: e886440355d4 Files: 72 Total size: 145.4 KB Directory structure: gitextract_r8udmm8u/ ├── .gitignore ├── CapacitorCommunityBackgroundGeolocation.podspec ├── LICENSE ├── Package.swift ├── README.md ├── android/ │ ├── build.gradle │ ├── gradle/ │ │ └── wrapper/ │ │ ├── gradle-wrapper.jar │ │ └── gradle-wrapper.properties │ ├── gradle.properties │ ├── gradlew │ ├── gradlew.bat │ ├── proguard-rules.pro │ ├── settings.gradle │ └── src/ │ └── main/ │ ├── AndroidManifest.xml │ └── java/ │ └── com/ │ └── equimaps/ │ └── capacitor_background_geolocation/ │ ├── BackgroundGeolocation.java │ └── BackgroundGeolocationService.java ├── definitions.d.ts ├── example/ │ ├── .gitignore │ ├── README.md │ ├── android/ │ │ ├── .gitignore │ │ ├── app/ │ │ │ ├── .gitignore │ │ │ ├── build.gradle │ │ │ ├── capacitor.build.gradle │ │ │ ├── proguard-rules.pro │ │ │ └── src/ │ │ │ └── main/ │ │ │ ├── AndroidManifest.xml │ │ │ ├── java/ │ │ │ │ └── com/ │ │ │ │ └── equimaps/ │ │ │ │ └── capacitor_background_geolocation_example/ │ │ │ │ └── MainActivity.java │ │ │ └── res/ │ │ │ ├── drawable/ │ │ │ │ └── ic_launcher_background.xml │ │ │ ├── drawable-v24/ │ │ │ │ └── ic_launcher_foreground.xml │ │ │ ├── layout/ │ │ │ │ └── activity_main.xml │ │ │ ├── mipmap-anydpi-v26/ │ │ │ │ ├── ic_launcher.xml │ │ │ │ └── ic_launcher_round.xml │ │ │ ├── values/ │ │ │ │ ├── ic_launcher_background.xml │ │ │ │ ├── strings.xml │ │ │ │ └── styles.xml │ │ │ └── xml/ │ │ │ └── config.xml │ │ ├── build.gradle │ │ ├── capacitor.settings.gradle │ │ ├── gradle/ │ │ │ └── wrapper/ │ │ │ ├── gradle-wrapper.jar │ │ │ └── gradle-wrapper.properties │ │ ├── gradle.properties │ │ ├── gradlew │ │ ├── gradlew.bat │ │ ├── settings.gradle │ │ └── variables.gradle │ ├── capacitor.config.json │ ├── ios/ │ │ ├── .gitignore │ │ ├── App/ │ │ │ ├── App/ │ │ │ │ ├── AppDelegate.swift │ │ │ │ ├── Assets.xcassets/ │ │ │ │ │ ├── AppIcon.appiconset/ │ │ │ │ │ │ └── Contents.json │ │ │ │ │ ├── Contents.json │ │ │ │ │ └── Splash.imageset/ │ │ │ │ │ └── Contents.json │ │ │ │ ├── Base.lproj/ │ │ │ │ │ ├── LaunchScreen.storyboard │ │ │ │ │ └── Main.storyboard │ │ │ │ ├── Info.plist │ │ │ │ └── config.xml │ │ │ ├── App.xcodeproj/ │ │ │ │ ├── project.pbxproj │ │ │ │ └── project.xcworkspace/ │ │ │ │ └── contents.xcworkspacedata │ │ │ └── CapApp-SPM/ │ │ │ ├── .gitignore │ │ │ ├── Package.swift │ │ │ ├── README.md │ │ │ └── Sources/ │ │ │ └── CapApp-SPM/ │ │ │ └── CapApp-SPM.swift │ │ └── debug.xcconfig │ ├── package.json │ └── www/ │ ├── index.html │ └── main.js ├── ios/ │ ├── Plugin/ │ │ ├── Info.plist │ │ ├── Plugin.h │ │ ├── Plugin.m │ │ └── Swift/ │ │ └── Plugin.swift │ ├── Plugin.xcodeproj/ │ │ └── project.pbxproj │ ├── Plugin.xcworkspace/ │ │ └── contents.xcworkspacedata │ └── Podfile └── package.json ================================================ FILE CONTENTS ================================================ ================================================ FILE: .gitignore ================================================ shell.nix .settings # node files dist/ node_modules/ # iOS files Pods Build xcuserdata xcshareddata DerivedData # macOS files .DS_Store # Based on Android gitignore template: https://github.com/github/gitignore/blob/master/Android.gitignore # Built application files *.apk *.ap_ # Files for the ART/Dalvik VM *.dex # Java class files *.class # Generated files bin/ gen/ out/ # Gradle files .gradle/ build/ # Local configuration file (sdk path, etc) local.properties # Proguard folder generated by Eclipse proguard/ # Log Files *.log # Android Studio Navigation editor temp files .navigation/ # Android Studio captures folder captures/ # IntelliJ *.iml .idea # Keystore files # Uncomment the following line if you do not want to check your keystore files in. #*.jks # External native build folder generated in Android Studio 2.2 and later .externalNativeBuild ================================================ FILE: CapacitorCommunityBackgroundGeolocation.podspec ================================================ require 'json' package = JSON.parse(File.read(File.join(__dir__, 'package.json'))) Pod::Spec.new do |s| s.name = 'CapacitorCommunityBackgroundGeolocation' s.version = package['version'] s.summary = package['description'] s.license = package['license'] s.homepage = package['repository']['url'] s.author = package['author'] s.source = { :git => package['repository']['url'], :tag => s.version.to_s } s.source_files = 'ios/Plugin/**/*.{swift,h,m,c,cc,mm,cpp}' s.ios.deployment_target = '12.0' s.dependency 'Capacitor' end ================================================ FILE: LICENSE ================================================ Copyright 2021 James Diacono Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ================================================ FILE: Package.swift ================================================ // swift-tools-version: 5.9 import PackageDescription let package = Package( name: "CapacitorCommunityBackgroundGeolocation", platforms: [.iOS(.v12)], products: [ .library( name: "CapacitorCommunityBackgroundGeolocation", targets: ["BackgroundGeolocationPlugin"] ) ], dependencies: [ .package( url: "https://github.com/ionic-team/capacitor-swift-pm.git", from: "7.0.0" ) ], targets: [ .target( name: "BackgroundGeolocationPlugin", dependencies: [ .product(name: "Capacitor", package: "capacitor-swift-pm"), .product(name: "Cordova", package: "capacitor-swift-pm") ], path: "ios/Plugin/Swift" ) ] ) ================================================ FILE: README.md ================================================ # Background Geolocation A Capacitor plugin that lets you receive geolocation updates even while the app is backgrounded. Only iOS and Android platforms are supported. ## Usage ```javascript import {registerPlugin} from "@capacitor/core"; const BackgroundGeolocation = registerPlugin("BackgroundGeolocation"); // To start listening for changes in the device's location, add a new watcher. // You do this by calling 'addWatcher' with an options object and a callback. A // Promise is returned, which resolves to the callback ID used to remove the // watcher in the future. The callback will be called every time a new location // is available. Watchers can not be paused, only removed. Multiple watchers may // exist simultaneously. BackgroundGeolocation.addWatcher( { // If the "backgroundMessage" option is defined, the watcher will // provide location updates whether the app is in the background or the // foreground. If it is not defined, location updates are only // guaranteed in the foreground. This is true on both platforms. // On Android, a notification must be shown to continue receiving // location updates in the background. This option specifies the text of // that notification. backgroundMessage: "Cancel to prevent battery drain.", // The title of the notification mentioned above. Defaults to "Using // your location". backgroundTitle: "Tracking You.", // Whether permissions should be requested from the user automatically, // if they are not already granted. Defaults to "true". requestPermissions: true, // If "true", stale locations may be delivered while the device // obtains a GPS fix. You are responsible for checking the "time" // property. If "false", locations are guaranteed to be up to date. // Defaults to "false". stale: false, // The minimum number of metres between subsequent locations. Defaults // to 0. distanceFilter: 50 }, function callback(location, error) { if (error) { if (error.code === "NOT_AUTHORIZED") { if (window.confirm( "This app needs your location, " + "but does not have permission.\n\n" + "Open settings now?" )) { // It can be useful to direct the user to their device's // settings when location permissions have been denied. The // plugin provides the 'openSettings' method to do exactly // this. BackgroundGeolocation.openSettings(); } } return console.error(error); } return console.log(location); } ).then(function after_the_watcher_has_been_added(watcher_id) { // When a watcher is no longer needed, it should be removed by calling // 'removeWatcher' with an object containing its ID. BackgroundGeolocation.removeWatcher({ id: watcher_id }); }); // The location object. { // Longitude in degrees. longitude: 131.723423719132, // Latitude in degrees. latitude: -22.40106297456, // Radius of horizontal uncertainty in metres, with 68% confidence. accuracy: 11, // Metres above sea level (or null). altitude: 65, // Vertical uncertainty in metres, with 68% confidence (or null). altitudeAccuracy: 4, // Deviation from true north in degrees (or null). bearing: 159.60000610351562, // True if the location was simulated by software, rather than GPS. simulated: false, // Speed in metres per second (or null). speed: 23.51068878173828, // Time the location was produced, in milliseconds since the unix epoch. time: 1562731602000 } // If you just want the current location, try something like this. The longer // the timeout, the more accurate the guess will be. I wouldn't go below about // 100ms. function guess_location(callback, timeout) { let last_location; BackgroundGeolocation.addWatcher( { requestPermissions: false, stale: true }, function (location) { last_location = location || undefined; } ).then(function (id) { setTimeout(function () { callback(last_location); Plugins.BackgroundGeolocation.removeWatcher({id}); }, timeout); }); } ``` ### Typescript support ```typescript import {BackgroundGeolocationPlugin} from "@capacitor-community/background-geolocation"; const BackgroundGeolocation = registerPlugin("BackgroundGeolocation"); ``` ## Installation Different versions of the plugin support different versions of Capacitor: | Capacitor | Plugin | |------------|--------| | v2 | v0.3 | | v3 | v1 | | v4 | v1 | | v5 | v1 | | v6 | v1 | | v7 | v1 | Read the documentation for v0.3 [here](https://github.com/capacitor-community/background-geolocation/tree/0.3.x). ```sh npm install @capacitor-community/background-geolocation npx cap update ``` ### iOS Add the following keys to `Info.plist.`: ```xml ... NSLocationWhenInUseUsageDescription We need to track your location NSLocationAlwaysAndWhenInUseUsageDescription We need to track your location while your device is locked. UIBackgroundModes location ... ``` ### Android Set the the `android.useLegacyBridge` option to `true` in your Capacitor configuration. This prevents location updates halting after 5 minutes in the background. See https://capacitorjs.com/docs/config and https://github.com/capacitor-community/background-geolocation/issues/89. On Android 13+, the app needs the `POST_NOTIFICATIONS` runtime permission to show the persistent notification informing the user that their location is being used in the background. You may need to [request this permission](https://developer.android.com/develop/ui/views/notifications/notification-permission) from the user, this can be accomplished [using the `@capacitor/local-notifications` plugin](https://capacitorjs.com/docs/apis/local-notifications#checkpermissions). If your app forwards location updates to a server in real time, be aware that after 5 minutes in the background Android will throttle HTTP requests initiated from the WebView. The solution is to use a native HTTP plugin such as [CapacitorHttp](https://capacitorjs.com/docs/apis/http). See https://github.com/capacitor-community/background-geolocation/issues/14. Configration specific to Android can be made in `strings.xml`: ```xml Background Tracking drawable/ic_tracking yellow ``` ## Changelog ### v1.2.26 - Add support for Swift Package Manager (SPM). ### v1.2.25 - Add support for Capacitor v7. ### v1.2.24 - Avoid crash introduced in v1.2.23. ### v1.2.23 - Perhaps make location updates more persistent on Android, see #137. ### v1.2.22 - Avoid interfering with safe area insets on Android ### v1.2.21 - Customize the notification color on Android. ### v1.2.19 - Fix a bug preventing the foreground service starting on Android. ### v1.2.18 - Always show the notification when a background watcher exists, improving the reliability of location updates on Android. ### v1.2.17 - Add support for Capacitor v6. ### v1.2.16 - Fix background location updates for Android 14. ### v1.2.14 - Add support for Capacitor v5. ### v1.2.3 - Add support for Capacitor v4. ### v1.2.2 - Prevent location updates from halting on iOS due to extended inactivity. ### v1.2.1 - Fix background location updates for some devices running Android 12. ### v1.2.0 - On iOS, the status bar now turns blue whilst the location is being watched in the background. This provides the user a straightforward way to return to the app. ### v1.0.4 - Add the `ACCESS_COARSE_LOCATION` permission. This is required for apps that target Android 12 (API level 31). A preceeding example shows how to add this permission to your app's manifest. ### v1.0.0 - BREAKING: `addWatcher` now returns a Promise that resolves to the callback ID, rather than the callback ID itself. - BREAKING: The plugin is imported via Capacitor's `registerPlugin` function, rather than from the `Plugins` object. - BREAKING: Drops support for iOS v11 and Capacitor v2. - Add support for Capacitor v3. ================================================ FILE: android/build.gradle ================================================ ext { androidxAppCompatVersion = project.hasProperty('androidxAppCompatVersion') ? rootProject.ext.androidxAppCompatVersion : '1.6.1' androidxLocalbroadcastmanagerVersion = project.hasProperty('androidxLocalbroadcastmanagerVersion') ? rootProject.ext.androidxLocalbroadcastmanagerVersion : '1.0.0' playServicesLocationVersion = project.hasProperty('playServicesLocationVersion') ? rootProject.ext.playServicesLocationVersion : '21.0.1' } buildscript { repositories { google() mavenCentral() } dependencies { classpath 'com.android.tools.build:gradle:8.0.0' } } apply plugin: 'com.android.library' android { namespace "com.equimaps.capacitor_background_geolocation" compileSdkVersion project.hasProperty('compileSdkVersion') ? rootProject.ext.compileSdkVersion : 33 defaultConfig { minSdkVersion project.hasProperty('minSdkVersion') ? rootProject.ext.minSdkVersion : 22 targetSdkVersion project.hasProperty('targetSdkVersion') ? rootProject.ext.targetSdkVersion : 33 versionCode 1 versionName "1.0" } buildTypes { release { minifyEnabled false proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' } } lintOptions { abortOnError false } } repositories { google() mavenCentral() } dependencies { implementation fileTree(dir: 'libs', include: ['*.jar']) implementation project(':capacitor-android') implementation "androidx.appcompat:appcompat:$androidxAppCompatVersion" implementation "androidx.localbroadcastmanager:localbroadcastmanager:$androidxLocalbroadcastmanagerVersion" implementation "com.google.android.gms:play-services-location:$playServicesLocationVersion" } ================================================ FILE: android/gradle/wrapper/gradle-wrapper.properties ================================================ #Fri Dec 01 12:41:00 CST 2017 distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists distributionUrl=https\://services.gradle.org/distributions/gradle-8.0.2-all.zip ================================================ FILE: android/gradle.properties ================================================ # Project-wide Gradle settings. # IDE (e.g. Android Studio) users: # Gradle settings configured through the IDE *will override* # any settings specified in this file. # For more details on how to configure your build environment visit # http://www.gradle.org/docs/current/userguide/build_environment.html # Specifies the JVM arguments used for the daemon process. # The setting is particularly useful for tweaking memory settings. org.gradle.jvmargs=-Xmx1536m # When configured, Gradle will run in incubating parallel mode. # This option should only be used with decoupled projects. More details, visit # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects # org.gradle.parallel=true # Supports AndroidX android.useAndroidX=true ================================================ FILE: android/gradlew ================================================ #!/usr/bin/env bash ############################################################################## ## ## Gradle start up script for UN*X ## ############################################################################## # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. DEFAULT_JVM_OPTS="" APP_NAME="Gradle" APP_BASE_NAME=`basename "$0"` # Use the maximum available, or set MAX_FD != -1 to use that value. MAX_FD="maximum" warn ( ) { echo "$*" } die ( ) { echo echo "$*" echo exit 1 } # OS specific support (must be 'true' or 'false'). cygwin=false msys=false darwin=false case "`uname`" in CYGWIN* ) cygwin=true ;; Darwin* ) darwin=true ;; MINGW* ) msys=true ;; esac # Attempt to set APP_HOME # Resolve links: $0 may be a link PRG="$0" # Need this for relative symlinks. while [ -h "$PRG" ] ; do ls=`ls -ld "$PRG"` link=`expr "$ls" : '.*-> \(.*\)$'` if expr "$link" : '/.*' > /dev/null; then PRG="$link" else PRG=`dirname "$PRG"`"/$link" fi done SAVED="`pwd`" cd "`dirname \"$PRG\"`/" >/dev/null APP_HOME="`pwd -P`" cd "$SAVED" >/dev/null CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar # Determine the Java command to use to start the JVM. if [ -n "$JAVA_HOME" ] ; then if [ -x "$JAVA_HOME/jre/sh/java" ] ; then # IBM's JDK on AIX uses strange locations for the executables JAVACMD="$JAVA_HOME/jre/sh/java" else JAVACMD="$JAVA_HOME/bin/java" fi if [ ! -x "$JAVACMD" ] ; then die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME Please set the JAVA_HOME variable in your environment to match the location of your Java installation." fi else JAVACMD="java" which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. Please set the JAVA_HOME variable in your environment to match the location of your Java installation." fi # Increase the maximum file descriptors if we can. if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then MAX_FD_LIMIT=`ulimit -H -n` if [ $? -eq 0 ] ; then if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then MAX_FD="$MAX_FD_LIMIT" fi ulimit -n $MAX_FD if [ $? -ne 0 ] ; then warn "Could not set maximum file descriptor limit: $MAX_FD" fi else warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" fi fi # For Darwin, add options to specify how the application appears in the dock if $darwin; then GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" fi # For Cygwin, switch paths to Windows format before running java if $cygwin ; then APP_HOME=`cygpath --path --mixed "$APP_HOME"` CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` JAVACMD=`cygpath --unix "$JAVACMD"` # We build the pattern for arguments to be converted via cygpath ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` SEP="" for dir in $ROOTDIRSRAW ; do ROOTDIRS="$ROOTDIRS$SEP$dir" SEP="|" done OURCYGPATTERN="(^($ROOTDIRS))" # Add a user-defined pattern to the cygpath arguments if [ "$GRADLE_CYGPATTERN" != "" ] ; then OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" fi # Now convert the arguments - kludge to limit ourselves to /bin/sh i=0 for arg in "$@" ; do CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` else eval `echo args$i`="\"$arg\"" fi i=$((i+1)) done case $i in (0) set -- ;; (1) set -- "$args0" ;; (2) set -- "$args0" "$args1" ;; (3) set -- "$args0" "$args1" "$args2" ;; (4) set -- "$args0" "$args1" "$args2" "$args3" ;; (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; esac fi # Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules function splitJvmOpts() { JVM_OPTS=("$@") } eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME" exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@" ================================================ FILE: android/gradlew.bat ================================================ @if "%DEBUG%" == "" @echo off @rem ########################################################################## @rem @rem Gradle startup script for Windows @rem @rem ########################################################################## @rem Set local scope for the variables with windows NT shell if "%OS%"=="Windows_NT" setlocal @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. set DEFAULT_JVM_OPTS= set DIRNAME=%~dp0 if "%DIRNAME%" == "" set DIRNAME=. set APP_BASE_NAME=%~n0 set APP_HOME=%DIRNAME% @rem Find java.exe if defined JAVA_HOME goto findJavaFromJavaHome set JAVA_EXE=java.exe %JAVA_EXE% -version >NUL 2>&1 if "%ERRORLEVEL%" == "0" goto init echo. echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. echo. echo Please set the JAVA_HOME variable in your environment to match the echo location of your Java installation. goto fail :findJavaFromJavaHome set JAVA_HOME=%JAVA_HOME:"=% set JAVA_EXE=%JAVA_HOME%/bin/java.exe if exist "%JAVA_EXE%" goto init echo. echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% echo. echo Please set the JAVA_HOME variable in your environment to match the echo location of your Java installation. goto fail :init @rem Get command-line arguments, handling Windowz variants if not "%OS%" == "Windows_NT" goto win9xME_args if "%@eval[2+2]" == "4" goto 4NT_args :win9xME_args @rem Slurp the command line arguments. set CMD_LINE_ARGS= set _SKIP=2 :win9xME_args_slurp if "x%~1" == "x" goto execute set CMD_LINE_ARGS=%* goto execute :4NT_args @rem Get arguments from the 4NT Shell from JP Software set CMD_LINE_ARGS=%$ :execute @rem Setup the command line set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar @rem Execute Gradle "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% :end @rem End local scope for the variables with windows NT shell if "%ERRORLEVEL%"=="0" goto mainEnd :fail rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of rem the _cmd.exe /c_ return code! if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 exit /b 1 :mainEnd if "%OS%"=="Windows_NT" endlocal :omega ================================================ FILE: android/proguard-rules.pro ================================================ # Add project specific ProGuard rules here. # You can control the set of applied configuration files using the # proguardFiles setting in build.gradle. # # For more details, see # http://developer.android.com/guide/developing/tools/proguard.html # If your project uses WebView with JS, uncomment the following # and specify the fully qualified class name to the JavaScript interface # class: #-keepclassmembers class fqcn.of.javascript.interface.for.webview { # public *; #} # Uncomment this to preserve the line number information for # debugging stack traces. #-keepattributes SourceFile,LineNumberTable # If you keep the line number information, uncomment this to # hide the original source file name. #-renamesourcefileattribute SourceFile ================================================ FILE: android/settings.gradle ================================================ include ':capacitor-android' project(':capacitor-android').projectDir = new File('../node_modules/@capacitor/android/capacitor') ================================================ FILE: android/src/main/AndroidManifest.xml ================================================ ================================================ FILE: android/src/main/java/com/equimaps/capacitor_background_geolocation/BackgroundGeolocation.java ================================================ package com.equimaps.capacitor_background_geolocation; import android.Manifest; import android.app.Notification; import android.app.NotificationChannel; import android.app.NotificationManager; import android.graphics.Color; import android.app.PendingIntent; import android.content.BroadcastReceiver; import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.content.ServiceConnection; import android.content.pm.PackageManager; import android.location.Location; import android.location.LocationManager; import android.net.Uri; import android.os.Build; import android.os.IBinder; import android.provider.Settings; import com.getcapacitor.JSObject; import com.getcapacitor.Logger; import com.getcapacitor.PermissionState; import com.getcapacitor.Plugin; import com.getcapacitor.PluginCall; import com.getcapacitor.PluginMethod; import com.getcapacitor.annotation.CapacitorPlugin; import com.getcapacitor.annotation.Permission; import com.getcapacitor.annotation.PermissionCallback; import com.google.android.gms.location.LocationServices; import com.google.android.gms.tasks.OnSuccessListener; import org.json.JSONObject; import androidx.localbroadcastmanager.content.LocalBroadcastManager; @CapacitorPlugin( name = "BackgroundGeolocation", permissions = { @Permission( strings = { Manifest.permission.ACCESS_COARSE_LOCATION, Manifest.permission.ACCESS_FINE_LOCATION }, alias = "location" ) } ) public class BackgroundGeolocation extends Plugin { private BackgroundGeolocationService.LocalBinder service = null; private Boolean stoppedWithoutPermissions = false; private void fetchLastLocation(PluginCall call) { try { LocationServices.getFusedLocationProviderClient( getContext() ).getLastLocation().addOnSuccessListener( getActivity(), new OnSuccessListener() { @Override public void onSuccess(Location location) { if (location != null) { call.resolve(formatLocation(location)); } } } ); } catch (SecurityException ignore) {} } @PluginMethod(returnType = PluginMethod.RETURN_CALLBACK) public void addWatcher(final PluginCall call) { if (service == null) { call.reject("Service not running."); return; } call.setKeepAlive(true); if (getPermissionState("location") != PermissionState.GRANTED) { if (call.getBoolean("requestPermissions", true)) { requestPermissionForAlias("location", call, "locationPermissionsCallback"); } else { call.reject("Permission denied.", "NOT_AUTHORIZED"); } } else if (!isLocationEnabled(getContext())) { call.reject("Location services disabled.", "NOT_AUTHORIZED"); } if (call.getBoolean("stale", false)) { fetchLastLocation(call); } Notification backgroundNotification = null; String backgroundMessage = call.getString("backgroundMessage"); if (backgroundMessage != null) { Notification.Builder builder = new Notification.Builder(getContext()) .setContentTitle( call.getString( "backgroundTitle", "Using your location" ) ) .setContentText(backgroundMessage) .setOngoing(true) .setPriority(Notification.PRIORITY_HIGH) .setWhen(System.currentTimeMillis()); try { String name = getAppString( "capacitor_background_geolocation_notification_icon", "mipmap/ic_launcher" ); String[] parts = name.split("/"); // It is actually necessary to set a valid icon for the notification to behave // correctly when tapped. If there is no icon specified, tapping it will open the // app's settings, rather than bringing the application to the foreground. builder.setSmallIcon( getAppResourceIdentifier(parts[1], parts[0]) ); } catch (Exception e) { Logger.error("Could not set notification icon", e); } try { String color = getAppString( "capacitor_background_geolocation_notification_color", null ); if (color != null) { builder.setColor(Color.parseColor(color)); } } catch (Exception e) { Logger.error("Could not set notification color", e); } Intent launchIntent = getContext().getPackageManager().getLaunchIntentForPackage( getContext().getPackageName() ); if (launchIntent != null) { launchIntent.addFlags(Intent.FLAG_ACTIVITY_REORDER_TO_FRONT); builder.setContentIntent( PendingIntent.getActivity( getContext(), 0, launchIntent, PendingIntent.FLAG_CANCEL_CURRENT | PendingIntent.FLAG_IMMUTABLE ) ); } // Set the Channel ID for Android O. if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { builder.setChannelId(BackgroundGeolocationService.class.getPackage().getName()); } backgroundNotification = builder.build(); } service.addWatcher( call.getCallbackId(), backgroundNotification, call.getFloat("distanceFilter", 0f) ); } @PermissionCallback private void locationPermissionsCallback(PluginCall call) { if (getPermissionState("location") != PermissionState.GRANTED) { call.reject("User denied location permission", "NOT_AUTHORIZED"); return; } if (call.getBoolean("stale", false)) { fetchLastLocation(call); } if (service != null) { service.onPermissionsGranted(); // The handleOnResume method will now be called, and we don't need it to call // service.onPermissionsGranted again so we reset this flag. stoppedWithoutPermissions = false; } } @PluginMethod() public void removeWatcher(PluginCall call) { String callbackId = call.getString("id"); if (callbackId == null) { call.reject("Missing id."); return; } service.removeWatcher(callbackId); PluginCall savedCall = getBridge().getSavedCall(callbackId); if (savedCall != null) { savedCall.release(getBridge()); } call.resolve(); } @PluginMethod() public void openSettings(PluginCall call) { Intent intent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS); Uri uri = Uri.fromParts("package", getContext().getPackageName(), null); intent.setData(uri); getContext().startActivity(intent); call.resolve(); } // Checks if device-wide location services are disabled private static Boolean isLocationEnabled(Context context) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) { LocationManager lm = (LocationManager) context.getSystemService(Context.LOCATION_SERVICE); return lm != null && lm.isLocationEnabled(); } else { return ( Settings.Secure.getInt( context.getContentResolver(), Settings.Secure.LOCATION_MODE, Settings.Secure.LOCATION_MODE_OFF ) != Settings.Secure.LOCATION_MODE_OFF ); } } private static JSObject formatLocation(Location location) { JSObject obj = new JSObject(); obj.put("latitude", location.getLatitude()); obj.put("longitude", location.getLongitude()); // The docs state that all Location objects have an accuracy, but then why is there a // hasAccuracy method? Better safe than sorry. obj.put("accuracy", location.hasAccuracy() ? location.getAccuracy() : JSONObject.NULL); obj.put("altitude", location.hasAltitude() ? location.getAltitude() : JSONObject.NULL); if (Build.VERSION.SDK_INT >= 26 && location.hasVerticalAccuracy()) { obj.put("altitudeAccuracy", location.getVerticalAccuracyMeters()); } else { obj.put("altitudeAccuracy", JSONObject.NULL); } // In addition to mocking locations in development, Android allows the // installation of apps which have the power to simulate location // readings in other apps. obj.put("simulated", location.isFromMockProvider()); obj.put("speed", location.hasSpeed() ? location.getSpeed() : JSONObject.NULL); obj.put("bearing", location.hasBearing() ? location.getBearing() : JSONObject.NULL); obj.put("time", location.getTime()); return obj; } // Receives messages from the service. private class ServiceReceiver extends BroadcastReceiver { @Override public void onReceive(Context context, Intent intent) { String id = intent.getStringExtra("id"); PluginCall call = getBridge().getSavedCall(id); if (call == null) { return; } Location location = intent.getParcelableExtra("location"); if (location != null) { call.resolve(formatLocation(location)); } else { Logger.debug("No locations received"); } } } // Gets the identifier of the app's resource by name, returning 0 if not found. private int getAppResourceIdentifier(String name, String defType) { return getContext().getResources().getIdentifier( name, defType, getContext().getPackageName() ); } // Gets a string from the app's strings.xml file, resorting to a fallback if it is not defined. private String getAppString(String name, String fallback) { int id = getAppResourceIdentifier(name, "string"); return id == 0 ? fallback : getContext().getString(id); } @Override public void load() { super.load(); // Android O requires a Notification Channel. if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { NotificationManager manager = (NotificationManager) getContext().getSystemService( Context.NOTIFICATION_SERVICE ); NotificationChannel channel = new NotificationChannel( BackgroundGeolocationService.class.getPackage().getName(), getAppString( "capacitor_background_geolocation_notification_channel_name", "Background Tracking" ), NotificationManager.IMPORTANCE_DEFAULT ); channel.enableLights(false); channel.enableVibration(false); channel.setSound(null, null); manager.createNotificationChannel(channel); } this.getContext().bindService( new Intent(this.getContext(), BackgroundGeolocationService.class), new ServiceConnection() { @Override public void onServiceConnected(ComponentName name, IBinder binder) { BackgroundGeolocation.this.service = (BackgroundGeolocationService.LocalBinder) binder; } @Override public void onServiceDisconnected(ComponentName name) { } }, Context.BIND_AUTO_CREATE ); LocalBroadcastManager.getInstance(this.getContext()).registerReceiver( new ServiceReceiver(), new IntentFilter(BackgroundGeolocationService.ACTION_BROADCAST) ); } @Override protected void handleOnResume() { if (service != null) { if (stoppedWithoutPermissions && getPermissionState("location") == PermissionState.GRANTED) { service.onPermissionsGranted(); } } super.handleOnResume(); } @Override protected void handleOnPause() { stoppedWithoutPermissions = getPermissionState("location") != PermissionState.GRANTED; super.handleOnPause(); } @Override protected void handleOnDestroy() { if (service != null) { service.stopService(); } super.handleOnDestroy(); } } ================================================ FILE: android/src/main/java/com/equimaps/capacitor_background_geolocation/BackgroundGeolocationService.java ================================================ package com.equimaps.capacitor_background_geolocation; import android.app.Notification; import android.app.Service; import android.content.Intent; import android.content.pm.ServiceInfo; import android.location.Location; import android.os.Binder; import android.os.Build; import android.os.IBinder; import com.getcapacitor.Logger; import com.google.android.gms.location.FusedLocationProviderClient; import com.google.android.gms.location.LocationAvailability; import com.google.android.gms.location.LocationCallback; import com.google.android.gms.location.LocationRequest; import com.google.android.gms.location.LocationResult; import com.google.android.gms.location.LocationServices; import java.util.HashSet; import androidx.localbroadcastmanager.content.LocalBroadcastManager; // A bound and started service that is promoted to a foreground service // (showing a persistent notification) when the first background watcher is // added, and demoted when the last background watcher is removed. public class BackgroundGeolocationService extends Service { static final String ACTION_BROADCAST = ( BackgroundGeolocationService.class.getPackage().getName() + ".broadcast" ); private final IBinder binder = new LocalBinder(); // Must be unique for this application. private static final int NOTIFICATION_ID = 28351; private class Watcher { public String id; public FusedLocationProviderClient client; public LocationRequest locationRequest; public LocationCallback locationCallback; public Notification backgroundNotification; } private HashSet watchers = new HashSet(); @Override public IBinder onBind(Intent intent) { return binder; } // Some devices allow a foreground service to outlive the application's main // activity, leading to nasty crashes as reported in issue #59. If we learn // that the application has been killed, all watchers are stopped and the // service is terminated immediately. @Override public boolean onUnbind(Intent intent) { for (Watcher watcher : watchers) { watcher.client.removeLocationUpdates(watcher.locationCallback); } watchers = new HashSet(); stopSelf(); return false; } Notification getNotification() { for (Watcher watcher : watchers) { if (watcher.backgroundNotification != null) { return watcher.backgroundNotification; } } return null; } // Handles requests from the activity. public class LocalBinder extends Binder { void addWatcher( final String id, Notification backgroundNotification, float distanceFilter ) { FusedLocationProviderClient client = LocationServices.getFusedLocationProviderClient( BackgroundGeolocationService.this ); LocationRequest locationRequest = new LocationRequest(); locationRequest.setMaxWaitTime(1000); locationRequest.setInterval(1000); locationRequest.setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY); locationRequest.setSmallestDisplacement(distanceFilter); LocationCallback callback = new LocationCallback(){ @Override public void onLocationResult(LocationResult locationResult) { Location location = locationResult.getLastLocation(); Intent intent = new Intent(ACTION_BROADCAST); intent.putExtra("location", location); intent.putExtra("id", id); LocalBroadcastManager.getInstance( getApplicationContext() ).sendBroadcast(intent); } @Override public void onLocationAvailability(LocationAvailability availability) { if (!availability.isLocationAvailable()) { Logger.debug("Location not available"); } } }; Watcher watcher = new Watcher(); watcher.id = id; watcher.client = client; watcher.locationRequest = locationRequest; watcher.locationCallback = callback; watcher.backgroundNotification = backgroundNotification; watchers.add(watcher); // According to Android Studio, this method can throw a Security Exception if // permissions are not yet granted. Rather than check the permissions, which is fiddly, // we simply ignore the exception. try { watcher.client.requestLocationUpdates( watcher.locationRequest, watcher.locationCallback, null ); } catch (SecurityException ignore) {} // Promote the service to the foreground if necessary. // Ideally we would only call 'startForeground' if the service is not already // foregrounded. Unfortunately, 'getForegroundServiceType' was only introduced // in API level 29 and seems to behave weirdly, as reported in #120. However, // it appears that 'startForeground' is idempotent, so we just call it repeatedly // each time a background watcher is added. if (backgroundNotification != null) { try { // This method has been known to fail due to weird // permission bugs, so we prevent any exceptions from // crashing the app. See issue #86. startForeground(NOTIFICATION_ID, backgroundNotification); } catch (Exception exception) { Logger.error("Failed to foreground service", exception); } } } void removeWatcher(String id) { for (Watcher watcher : watchers) { if (watcher.id.equals(id)) { watcher.client.removeLocationUpdates(watcher.locationCallback); watchers.remove(watcher); if (getNotification() == null) { stopForeground(true); } return; } } } void onPermissionsGranted() { // If permissions were granted while the app was in the background, for example in // the Settings app, the watchers need restarting. for (Watcher watcher : watchers) { watcher.client.removeLocationUpdates(watcher.locationCallback); watcher.client.requestLocationUpdates( watcher.locationRequest, watcher.locationCallback, null ); } } void stopService() { BackgroundGeolocationService.this.stopSelf(); } } } ================================================ FILE: definitions.d.ts ================================================ /** * The options for configuring a watcher that listens for location updates. */ export interface WatcherOptions { /** * If the "backgroundMessage" option is defined, the watcher will * provide location updates whether the app is in the background or the * foreground. If it is not defined, location updates are only * guaranteed in the foreground. This is true on both platforms. * * On Android, a notification must be shown to continue receiving * location updates in the background. This option specifies the text of * that notification. */ backgroundMessage?: string; /** * The title of the notification mentioned above. * @default "Using your location" */ backgroundTitle?: string; /** * Whether permissions should be requested from the user automatically, * if they are not already granted. * @default true */ requestPermissions?: boolean; /** * If "true", stale locations may be delivered while the device * obtains a GPS fix. You are responsible for checking the "time" * property. If "false", locations are guaranteed to be up to date. * @default false */ stale?: boolean; /** * The distance in meters that the device must move before a new location update is triggered. * This is used to filter out small movements and reduce the number of updates. * @default 0 */ distanceFilter?: number; } /** * Represents a geographical location with various attributes. */ export interface Location { /** * Longitude in degrees. */ latitude: number; /** * Latitude in degrees. */ longitude: number; /** * Radius of horizontal uncertainty in metres, with 68% confidence. */ accuracy: number; /** * Metres above sea level (or null). */ altitude: number | null; /** * Vertical uncertainty in metres, with 68% confidence (or null). */ altitudeAccuracy: number | null; /** * `true` if the location was simulated by software, rather than GPS. */ simulated: boolean; /** * Deviation from true north in degrees (or null). */ bearing: number | null; /** * Speed in metres per second (or null). */ speed: number | null; /** * Time the location was produced, in milliseconds since the unix epoch. */ time: number | null; } export interface CallbackError extends Error { code?: string; } export interface BackgroundGeolocationPlugin { /** * Adds a watcher for location updates. * The watcher will be invoked with the latest location whenever it is available. * If an error occurs, the callback will be invoked with the error. * * @param options the watcher options * @param callback the callback to be invoked when a new location is available or an error occurs * @returns a promise that resolves to a unique identifier for the watcher ID */ addWatcher( options: WatcherOptions, callback: ( position?: Location, error?: CallbackError ) => void ): Promise; /** * Removes a watcher by its unique identifier. * @param options the options for removing the watcher * @returns a promise that resolves when the watcher is successfully removed */ removeWatcher(options: { id: string }): Promise; /** * Opens the settings page of the app. */ openSettings(): Promise; } ================================================ FILE: example/.gitignore ================================================ www/capacitor.js* www/plugin.js* android/app/src/main/assets ios/App/App/capacitor.config.json ================================================ FILE: example/README.md ================================================ A Capacitor app demonstrating the background geolocation plugin. npm i && npx cap sync npx cap open android npx cap open ios ================================================ FILE: example/android/.gitignore ================================================ # Using Android gitignore template: https://github.com/github/gitignore/blob/HEAD/Android.gitignore # Built application files *.apk *.aar *.ap_ *.aab # Files for the ART/Dalvik VM *.dex # Java class files *.class # Generated files bin/ gen/ out/ release/ # Gradle files .gradle/ build/ # Local configuration file (sdk path, etc) local.properties # Proguard folder generated by Eclipse proguard/ # Log Files *.log # Android Studio Navigation editor temp files .navigation/ # Android Studio captures folder captures/ # IntelliJ *.iml .idea/workspace.xml .idea/tasks.xml .idea/gradle.xml .idea/assetWizardSettings.xml .idea/dictionaries .idea/libraries # Android Studio 3 in .gitignore file. .idea/caches .idea/modules.xml # Comment next line if keeping position of elements in Navigation Editor is relevant for you .idea/navEditor.xml # Keystore files # Uncomment the following lines if you do not want to check your keystore files in. #*.jks #*.keystore # External native build folder generated in Android Studio 2.2 and later .externalNativeBuild .cxx/ # Google Services (e.g. APIs or Firebase) # google-services.json # Freeline freeline.py freeline/ freeline_project_description.json # fastlane fastlane/report.xml fastlane/Preview.html fastlane/screenshots fastlane/test_output fastlane/readme.md # Version control vcs.xml # lint lint/intermediates/ lint/generated/ lint/outputs/ lint/tmp/ # lint/reports/ # Android Profiling *.hprof # Cordova plugins for Capacitor capacitor-cordova-android-plugins # Copied web assets app/src/main/assets/public # Generated Config files app/src/main/assets/capacitor.config.json app/src/main/assets/capacitor.plugins.json app/src/main/res/xml/config.xml ================================================ FILE: example/android/app/.gitignore ================================================ /build/* !/build/.npmkeep ================================================ FILE: example/android/app/build.gradle ================================================ apply plugin: 'com.android.application' android { namespace "com.equimaps.capacitor_background_geolocation_example" compileSdk rootProject.ext.compileSdkVersion defaultConfig { applicationId "com.equimaps.capacitor_background_geolocation_example" minSdkVersion rootProject.ext.minSdkVersion targetSdkVersion rootProject.ext.targetSdkVersion versionCode 1 versionName "1.0" aaptOptions { // Files and dirs to omit from the packaged assets dir, modified to accommodate modern web apps. // Default: https://android.googlesource.com/platform/frameworks/base/+/282e181b58cf72b6ca770dc7ca5f91f135444502/tools/aapt/AaptAssets.cpp#61 ignoreAssetsPattern '!.svn:!.git:!.ds_store:!*.scc:.*:!CVS:!thumbs.db:!picasa.ini:!*~' } } buildTypes { release { minifyEnabled false proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' } } } repositories { flatDir{ dirs '../capacitor-cordova-android-plugins/src/main/libs', 'libs' } } dependencies { implementation fileTree(include: ['*.jar'], dir: 'libs') implementation "androidx.appcompat:appcompat:$androidxAppCompatVersion" implementation "androidx.coordinatorlayout:coordinatorlayout:$androidxCoordinatorLayoutVersion" implementation "androidx.core:core-splashscreen:$coreSplashScreenVersion" implementation project(':capacitor-android') testImplementation "junit:junit:$junitVersion" androidTestImplementation "androidx.test.ext:junit:$androidxJunitVersion" androidTestImplementation "androidx.test.espresso:espresso-core:$androidxEspressoCoreVersion" implementation project(':capacitor-cordova-android-plugins') } apply from: 'capacitor.build.gradle' try { def servicesJSON = file('google-services.json') if (servicesJSON.text) { apply plugin: 'com.google.gms.google-services' } } catch(Exception e) { logger.info("google-services.json not found, google-services plugin not applied. Push Notifications won't work") } ================================================ FILE: example/android/app/capacitor.build.gradle ================================================ // DO NOT EDIT THIS FILE! IT IS GENERATED EACH TIME "capacitor update" IS RUN android { compileOptions { sourceCompatibility JavaVersion.VERSION_21 targetCompatibility JavaVersion.VERSION_21 } } apply from: "../capacitor-cordova-android-plugins/cordova.variables.gradle" dependencies { implementation project(':capacitor-community-background-geolocation') implementation project(':capacitor-local-notifications') } if (hasProperty('postBuildExtras')) { postBuildExtras() } ================================================ FILE: example/android/app/proguard-rules.pro ================================================ # Add project specific ProGuard rules here. # You can control the set of applied configuration files using the # proguardFiles setting in build.gradle. # # For more details, see # http://developer.android.com/guide/developing/tools/proguard.html # If your project uses WebView with JS, uncomment the following # and specify the fully qualified class name to the JavaScript interface # class: #-keepclassmembers class fqcn.of.javascript.interface.for.webview { # public *; #} # Uncomment this to preserve the line number information for # debugging stack traces. #-keepattributes SourceFile,LineNumberTable # If you keep the line number information, uncomment this to # hide the original source file name. #-renamesourcefileattribute SourceFile ================================================ FILE: example/android/app/src/main/AndroidManifest.xml ================================================ ================================================ FILE: example/android/app/src/main/java/com/equimaps/capacitor_background_geolocation_example/MainActivity.java ================================================ package com.equimaps.capacitor_background_geolocation_example; import com.getcapacitor.BridgeActivity; public class MainActivity extends BridgeActivity {} ================================================ FILE: example/android/app/src/main/res/drawable/ic_launcher_background.xml ================================================ ================================================ FILE: example/android/app/src/main/res/drawable-v24/ic_launcher_foreground.xml ================================================ ================================================ FILE: example/android/app/src/main/res/layout/activity_main.xml ================================================ ================================================ FILE: example/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml ================================================ ================================================ FILE: example/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml ================================================ ================================================ FILE: example/android/app/src/main/res/values/ic_launcher_background.xml ================================================ #FFFFFF ================================================ FILE: example/android/app/src/main/res/values/strings.xml ================================================ background-geolocation-demo background-geolocation-demo com.equimaps.capacitor_background_geolocation_example com.equimaps.capacitor_background_geolocation_example GPS drawable/ic_json fuchsia ================================================ FILE: example/android/app/src/main/res/values/styles.xml ================================================ ================================================ FILE: example/android/app/src/main/res/xml/config.xml ================================================ ================================================ FILE: example/android/build.gradle ================================================ // Top-level build file where you can add configuration options common to all sub-projects/modules. buildscript { repositories { google() mavenCentral() } dependencies { classpath 'com.android.tools.build:gradle:8.7.2' classpath 'com.google.gms:google-services:4.4.2' // NOTE: Do not place your application dependencies here; they belong // in the individual module build.gradle files } } apply from: "variables.gradle" allprojects { repositories { google() mavenCentral() } } task clean(type: Delete) { delete rootProject.buildDir } ================================================ FILE: example/android/capacitor.settings.gradle ================================================ // DO NOT EDIT THIS FILE! IT IS GENERATED EACH TIME "capacitor update" IS RUN include ':capacitor-android' project(':capacitor-android').projectDir = new File('../node_modules/@capacitor/android/capacitor') include ':capacitor-community-background-geolocation' project(':capacitor-community-background-geolocation').projectDir = new File('../../android') include ':capacitor-local-notifications' project(':capacitor-local-notifications').projectDir = new File('../node_modules/@capacitor/local-notifications/android') ================================================ FILE: example/android/gradle/wrapper/gradle-wrapper.properties ================================================ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists distributionUrl=https\://services.gradle.org/distributions/gradle-8.11.1-all.zip networkTimeout=10000 validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists ================================================ FILE: example/android/gradle.properties ================================================ # Project-wide Gradle settings. # IDE (e.g. Android Studio) users: # Gradle settings configured through the IDE *will override* # any settings specified in this file. # For more details on how to configure your build environment visit # http://www.gradle.org/docs/current/userguide/build_environment.html # Specifies the JVM arguments used for the daemon process. # The setting is particularly useful for tweaking memory settings. org.gradle.jvmargs=-Xmx1536m # When configured, Gradle will run in incubating parallel mode. # This option should only be used with decoupled projects. More details, visit # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects # org.gradle.parallel=true # AndroidX package structure to make it clearer which packages are bundled with the # Android operating system, and which are packaged with your app's APK # https://developer.android.com/topic/libraries/support-library/androidx-rn android.useAndroidX=true ================================================ FILE: example/android/gradlew ================================================ #!/bin/sh # # Copyright © 2015-2021 the original authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # https://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # # SPDX-License-Identifier: Apache-2.0 # ############################################################################## # # Gradle start up script for POSIX generated by Gradle. # # Important for running: # # (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is # noncompliant, but you have some other compliant shell such as ksh or # bash, then to run this script, type that shell name before the whole # command line, like: # # ksh Gradle # # Busybox and similar reduced shells will NOT work, because this script # requires all of these POSIX shell features: # * functions; # * expansions «$var», «${var}», «${var:-default}», «${var+SET}», # «${var#prefix}», «${var%suffix}», and «$( cmd )»; # * compound commands having a testable exit status, especially «case»; # * various built-in commands including «command», «set», and «ulimit». # # Important for patching: # # (2) This script targets any POSIX shell, so it avoids extensions provided # by Bash, Ksh, etc; in particular arrays are avoided. # # The "traditional" practice of packing multiple parameters into a # space-separated string is a well documented source of bugs and security # problems, so this is (mostly) avoided, by progressively accumulating # options in "$@", and eventually passing that to Java. # # Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS, # and GRADLE_OPTS) rely on word-splitting, this is performed explicitly; # see the in-line comments for details. # # There are tweaks for specific operating systems such as AIX, CygWin, # Darwin, MinGW, and NonStop. # # (3) This script is generated from the Groovy template # https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt # within the Gradle project. # # You can find Gradle at https://github.com/gradle/gradle/. # ############################################################################## # Attempt to set APP_HOME # Resolve links: $0 may be a link app_path=$0 # Need this for daisy-chained symlinks. while APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path [ -h "$app_path" ] do ls=$( ls -ld "$app_path" ) link=${ls#*' -> '} case $link in #( /*) app_path=$link ;; #( *) app_path=$APP_HOME$link ;; esac done # This is normally unused # shellcheck disable=SC2034 APP_BASE_NAME=${0##*/} # Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036) APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s ' "$PWD" ) || exit # Use the maximum available, or set MAX_FD != -1 to use that value. MAX_FD=maximum warn () { echo "$*" } >&2 die () { echo echo "$*" echo exit 1 } >&2 # OS specific support (must be 'true' or 'false'). cygwin=false msys=false darwin=false nonstop=false case "$( uname )" in #( CYGWIN* ) cygwin=true ;; #( Darwin* ) darwin=true ;; #( MSYS* | MINGW* ) msys=true ;; #( NONSTOP* ) nonstop=true ;; esac CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar # Determine the Java command to use to start the JVM. if [ -n "$JAVA_HOME" ] ; then if [ -x "$JAVA_HOME/jre/sh/java" ] ; then # IBM's JDK on AIX uses strange locations for the executables JAVACMD=$JAVA_HOME/jre/sh/java else JAVACMD=$JAVA_HOME/bin/java fi if [ ! -x "$JAVACMD" ] ; then die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME Please set the JAVA_HOME variable in your environment to match the location of your Java installation." fi else JAVACMD=java if ! command -v java >/dev/null 2>&1 then die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. Please set the JAVA_HOME variable in your environment to match the location of your Java installation." fi fi # Increase the maximum file descriptors if we can. if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then case $MAX_FD in #( max*) # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked. # shellcheck disable=SC2039,SC3045 MAX_FD=$( ulimit -H -n ) || warn "Could not query maximum file descriptor limit" esac case $MAX_FD in #( '' | soft) :;; #( *) # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked. # shellcheck disable=SC2039,SC3045 ulimit -n "$MAX_FD" || warn "Could not set maximum file descriptor limit to $MAX_FD" esac fi # Collect all arguments for the java command, stacking in reverse order: # * args from the command line # * the main class name # * -classpath # * -D...appname settings # * --module-path (only if needed) # * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables. # For Cygwin or MSYS, switch paths to Windows format before running java if "$cygwin" || "$msys" ; then APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) JAVACMD=$( cygpath --unix "$JAVACMD" ) # Now convert the arguments - kludge to limit ourselves to /bin/sh for arg do if case $arg in #( -*) false ;; # don't mess with options #( /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath [ -e "$t" ] ;; #( *) false ;; esac then arg=$( cygpath --path --ignore --mixed "$arg" ) fi # Roll the args list around exactly as many times as the number of # args, so each arg winds up back in the position where it started, but # possibly modified. # # NB: a `for` loop captures its iteration list before it begins, so # changing the positional parameters here affects neither the number of # iterations, nor the values presented in `arg`. shift # remove old arg set -- "$@" "$arg" # push replacement arg done fi # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' # Collect all arguments for the java command: # * DEFAULT_JVM_OPTS, JAVA_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments, # and any embedded shellness will be escaped. # * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be # treated as '${Hostname}' itself on the command line. set -- \ "-Dorg.gradle.appname=$APP_BASE_NAME" \ -classpath "$CLASSPATH" \ org.gradle.wrapper.GradleWrapperMain \ "$@" # Stop when "xargs" is not available. if ! command -v xargs >/dev/null 2>&1 then die "xargs is not available" fi # Use "xargs" to parse quoted args. # # With -n1 it outputs one arg per line, with the quotes and backslashes removed. # # In Bash we could simply go: # # readarray ARGS < <( xargs -n1 <<<"$var" ) && # set -- "${ARGS[@]}" "$@" # # but POSIX shell has neither arrays nor command substitution, so instead we # post-process each arg (as a line of input to sed) to backslash-escape any # character that might be a shell metacharacter, then use eval to reverse # that process (while maintaining the separation between arguments), and wrap # the whole thing up as a single "set" statement. # # This will of course break if any of these variables contains a newline or # an unmatched quote. # eval "set -- $( printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" | xargs -n1 | sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' | tr '\n' ' ' )" '"$@"' exec "$JAVACMD" "$@" ================================================ FILE: example/android/gradlew.bat ================================================ @rem @rem Copyright 2015 the original author or authors. @rem @rem Licensed under the Apache License, Version 2.0 (the "License"); @rem you may not use this file except in compliance with the License. @rem You may obtain a copy of the License at @rem @rem https://www.apache.org/licenses/LICENSE-2.0 @rem @rem Unless required by applicable law or agreed to in writing, software @rem distributed under the License is distributed on an "AS IS" BASIS, @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @rem See the License for the specific language governing permissions and @rem limitations under the License. @rem @rem SPDX-License-Identifier: Apache-2.0 @rem @if "%DEBUG%"=="" @echo off @rem ########################################################################## @rem @rem Gradle startup script for Windows @rem @rem ########################################################################## @rem Set local scope for the variables with windows NT shell if "%OS%"=="Windows_NT" setlocal set DIRNAME=%~dp0 if "%DIRNAME%"=="" set DIRNAME=. @rem This is normally unused set APP_BASE_NAME=%~n0 set APP_HOME=%DIRNAME% @rem Resolve any "." and ".." in APP_HOME to make it shorter. for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" @rem Find java.exe if defined JAVA_HOME goto findJavaFromJavaHome set JAVA_EXE=java.exe %JAVA_EXE% -version >NUL 2>&1 if %ERRORLEVEL% equ 0 goto execute echo. 1>&2 echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2 echo. 1>&2 echo Please set the JAVA_HOME variable in your environment to match the 1>&2 echo location of your Java installation. 1>&2 goto fail :findJavaFromJavaHome set JAVA_HOME=%JAVA_HOME:"=% set JAVA_EXE=%JAVA_HOME%/bin/java.exe if exist "%JAVA_EXE%" goto execute echo. 1>&2 echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2 echo. 1>&2 echo Please set the JAVA_HOME variable in your environment to match the 1>&2 echo location of your Java installation. 1>&2 goto fail :execute @rem Setup the command line set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar @rem Execute Gradle "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* :end @rem End local scope for the variables with windows NT shell if %ERRORLEVEL% equ 0 goto mainEnd :fail rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of rem the _cmd.exe /c_ return code! set EXIT_CODE=%ERRORLEVEL% if %EXIT_CODE% equ 0 set EXIT_CODE=1 if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% exit /b %EXIT_CODE% :mainEnd if "%OS%"=="Windows_NT" endlocal :omega ================================================ FILE: example/android/settings.gradle ================================================ include ':app' include ':capacitor-cordova-android-plugins' project(':capacitor-cordova-android-plugins').projectDir = new File('./capacitor-cordova-android-plugins/') apply from: 'capacitor.settings.gradle' ================================================ FILE: example/android/variables.gradle ================================================ ext { minSdkVersion = 23 compileSdkVersion = 35 targetSdkVersion = 35 androidxActivityVersion = '1.9.2' androidxAppCompatVersion = '1.7.0' androidxCoordinatorLayoutVersion = '1.2.0' androidxCoreVersion = '1.15.0' androidxFragmentVersion = '1.8.4' coreSplashScreenVersion = '1.0.1' androidxWebkitVersion = '1.12.1' junitVersion = '4.13.2' androidxJunitVersion = '1.2.1' androidxEspressoCoreVersion = '3.6.1' cordovaAndroidVersion = '10.1.1' } ================================================ FILE: example/capacitor.config.json ================================================ { "appId": "com.equimaps.capacitor_background_geolocation_example", "appName": "background-geolocation-example", "android": {"useLegacyBridge": true}, "npmClient": "npm", "webDir": "www" } ================================================ FILE: example/ios/.gitignore ================================================ App/build App/Pods App/output App/App/public DerivedData xcuserdata # Cordova plugins for Capacitor capacitor-cordova-ios-plugins # Generated Config files App/App/capacitor.config.json App/App/config.xml ================================================ FILE: example/ios/App/App/AppDelegate.swift ================================================ import UIKit import Capacitor @UIApplicationMain class AppDelegate: UIResponder, UIApplicationDelegate { var window: UIWindow? func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { // Override point for customization after application launch. return true } func applicationWillResignActive(_ application: UIApplication) { // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state. // Use this method to pause ongoing tasks, disable timers, and invalidate graphics rendering callbacks. Games should use this method to pause the game. } func applicationDidEnterBackground(_ application: UIApplication) { // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later. // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits. } func applicationWillEnterForeground(_ application: UIApplication) { // Called as part of the transition from the background to the active state; here you can undo many of the changes made on entering the background. } func applicationDidBecomeActive(_ application: UIApplication) { // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface. } func applicationWillTerminate(_ application: UIApplication) { // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:. } func application(_ app: UIApplication, open url: URL, options: [UIApplication.OpenURLOptionsKey: Any] = [:]) -> Bool { // Called when the app was launched with a url. Feel free to add additional processing here, // but if you want the App API to support tracking app url opens, make sure to keep this call return ApplicationDelegateProxy.shared.application(app, open: url, options: options) } func application(_ application: UIApplication, continue userActivity: NSUserActivity, restorationHandler: @escaping ([UIUserActivityRestoring]?) -> Void) -> Bool { // Called when the app was launched with an activity, including Universal Links. // Feel free to add additional processing here, but if you want the App API to support // tracking app url opens, make sure to keep this call return ApplicationDelegateProxy.shared.application(application, continue: userActivity, restorationHandler: restorationHandler) } } ================================================ FILE: example/ios/App/App/Assets.xcassets/AppIcon.appiconset/Contents.json ================================================ { "images" : [ { "filename" : "AppIcon-512@2x.png", "idiom" : "universal", "platform" : "ios", "size" : "1024x1024" } ], "info" : { "author" : "xcode", "version" : 1 } } ================================================ FILE: example/ios/App/App/Assets.xcassets/Contents.json ================================================ { "info" : { "version" : 1, "author" : "xcode" } } ================================================ FILE: example/ios/App/App/Assets.xcassets/Splash.imageset/Contents.json ================================================ { "images" : [ { "idiom" : "universal", "filename" : "splash-2732x2732-2.png", "scale" : "1x" }, { "idiom" : "universal", "filename" : "splash-2732x2732-1.png", "scale" : "2x" }, { "idiom" : "universal", "filename" : "splash-2732x2732.png", "scale" : "3x" } ], "info" : { "version" : 1, "author" : "xcode" } } ================================================ FILE: example/ios/App/App/Base.lproj/LaunchScreen.storyboard ================================================ ================================================ FILE: example/ios/App/App/Base.lproj/Main.storyboard ================================================ ================================================ FILE: example/ios/App/App/Info.plist ================================================ CAPACITOR_DEBUG $(CAPACITOR_DEBUG) CFBundleDevelopmentRegion en CFBundleDisplayName background-geolocation-demo CFBundleExecutable $(EXECUTABLE_NAME) CFBundleIdentifier $(PRODUCT_BUNDLE_IDENTIFIER) CFBundleInfoDictionaryVersion 6.0 CFBundleName $(PRODUCT_NAME) CFBundlePackageType APPL CFBundleShortVersionString $(MARKETING_VERSION) CFBundleVersion $(CURRENT_PROJECT_VERSION) LSRequiresIPhoneOS NSLocationAlwaysAndWhenInUseUsageDescription Allow geolocation? NSLocationWhenInUseUsageDescription Allow Geolocation? UIBackgroundModes location UILaunchStoryboardName LaunchScreen UIMainStoryboardFile Main UIRequiredDeviceCapabilities armv7 UISupportedInterfaceOrientations UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight UISupportedInterfaceOrientations~ipad UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight UIViewControllerBasedStatusBarAppearance ================================================ FILE: example/ios/App/App/config.xml ================================================ ================================================ FILE: example/ios/App/App.xcodeproj/project.pbxproj ================================================ // !$*UTF8*$! { archiveVersion = 1; classes = { }; objectVersion = 60; objects = { /* Begin PBXBuildFile section */ 2FAD9763203C412B000D30F8 /* config.xml in Resources */ = {isa = PBXBuildFile; fileRef = 2FAD9762203C412B000D30F8 /* config.xml */; }; 50379B232058CBB4000EE86E /* capacitor.config.json in Resources */ = {isa = PBXBuildFile; fileRef = 50379B222058CBB4000EE86E /* capacitor.config.json */; }; 504EC3081FED79650016851F /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 504EC3071FED79650016851F /* AppDelegate.swift */; }; 504EC30D1FED79650016851F /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 504EC30B1FED79650016851F /* Main.storyboard */; }; 504EC30F1FED79650016851F /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 504EC30E1FED79650016851F /* Assets.xcassets */; }; 504EC3121FED79650016851F /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 504EC3101FED79650016851F /* LaunchScreen.storyboard */; }; 50B271D11FEDC1A000F3C39B /* public in Resources */ = {isa = PBXBuildFile; fileRef = 50B271D01FEDC1A000F3C39B /* public */; }; 8CA225782E6071C7007DCEEE /* CapApp-SPM in Frameworks */ = {isa = PBXBuildFile; productRef = 8CA225772E6071C7007DCEEE /* CapApp-SPM */; }; 8CA2257B2E6072F0007DCEEE /* CapApp-SPM in Frameworks */ = {isa = PBXBuildFile; productRef = 8CA2257A2E6072F0007DCEEE /* CapApp-SPM */; }; /* End PBXBuildFile section */ /* Begin PBXFileReference section */ 2FAD9762203C412B000D30F8 /* config.xml */ = {isa = PBXFileReference; lastKnownFileType = text.xml; path = config.xml; sourceTree = ""; }; 50379B222058CBB4000EE86E /* capacitor.config.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = capacitor.config.json; sourceTree = ""; }; 504EC3041FED79650016851F /* App.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = App.app; sourceTree = BUILT_PRODUCTS_DIR; }; 504EC3071FED79650016851F /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; 504EC30C1FED79650016851F /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; 504EC30E1FED79650016851F /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 504EC3111FED79650016851F /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; 504EC3131FED79650016851F /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 50B271D01FEDC1A000F3C39B /* public */ = {isa = PBXFileReference; lastKnownFileType = folder; path = public; sourceTree = ""; }; 958DCC722DB07C7200EA8C5F /* debug.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = debug.xcconfig; path = ../debug.xcconfig; sourceTree = SOURCE_ROOT; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ 504EC3011FED79650016851F /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( 8CA2257B2E6072F0007DCEEE /* CapApp-SPM in Frameworks */, 8CA225782E6071C7007DCEEE /* CapApp-SPM in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ 504EC2FB1FED79650016851F = { isa = PBXGroup; children = ( 958DCC722DB07C7200EA8C5F /* debug.xcconfig */, 504EC3061FED79650016851F /* App */, 504EC3051FED79650016851F /* Products */, ); sourceTree = ""; }; 504EC3051FED79650016851F /* Products */ = { isa = PBXGroup; children = ( 504EC3041FED79650016851F /* App.app */, ); name = Products; sourceTree = ""; }; 504EC3061FED79650016851F /* App */ = { isa = PBXGroup; children = ( 50379B222058CBB4000EE86E /* capacitor.config.json */, 504EC3071FED79650016851F /* AppDelegate.swift */, 504EC30B1FED79650016851F /* Main.storyboard */, 504EC30E1FED79650016851F /* Assets.xcassets */, 504EC3101FED79650016851F /* LaunchScreen.storyboard */, 504EC3131FED79650016851F /* Info.plist */, 2FAD9762203C412B000D30F8 /* config.xml */, 50B271D01FEDC1A000F3C39B /* public */, ); path = App; sourceTree = ""; }; /* End PBXGroup section */ /* Begin PBXNativeTarget section */ 504EC3031FED79650016851F /* App */ = { isa = PBXNativeTarget; buildConfigurationList = 504EC3161FED79650016851F /* Build configuration list for PBXNativeTarget "App" */; buildPhases = ( 504EC3001FED79650016851F /* Sources */, 504EC3011FED79650016851F /* Frameworks */, 504EC3021FED79650016851F /* Resources */, ); buildRules = ( ); dependencies = ( ); name = App; packageProductDependencies = ( 8CA225772E6071C7007DCEEE /* CapApp-SPM */, 8CA2257A2E6072F0007DCEEE /* CapApp-SPM */, ); productName = App; productReference = 504EC3041FED79650016851F /* App.app */; productType = "com.apple.product-type.application"; }; /* End PBXNativeTarget section */ /* Begin PBXProject section */ 504EC2FC1FED79650016851F /* Project object */ = { isa = PBXProject; attributes = { LastSwiftUpdateCheck = 0920; LastUpgradeCheck = 0920; TargetAttributes = { 504EC3031FED79650016851F = { CreatedOnToolsVersion = 9.2; LastSwiftMigration = 1100; ProvisioningStyle = Automatic; }; }; }; buildConfigurationList = 504EC2FF1FED79650016851F /* Build configuration list for PBXProject "App" */; compatibilityVersion = "Xcode 8.0"; developmentRegion = en; hasScannedForEncodings = 0; knownRegions = ( en, Base, ); mainGroup = 504EC2FB1FED79650016851F; packageReferences = ( 8CA225792E6072F0007DCEEE /* XCLocalSwiftPackageReference "CapApp-SPM" */, ); productRefGroup = 504EC3051FED79650016851F /* Products */; projectDirPath = ""; projectRoot = ""; targets = ( 504EC3031FED79650016851F /* App */, ); }; /* End PBXProject section */ /* Begin PBXResourcesBuildPhase section */ 504EC3021FED79650016851F /* Resources */ = { isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( 504EC3121FED79650016851F /* LaunchScreen.storyboard in Resources */, 50B271D11FEDC1A000F3C39B /* public in Resources */, 504EC30F1FED79650016851F /* Assets.xcassets in Resources */, 50379B232058CBB4000EE86E /* capacitor.config.json in Resources */, 504EC30D1FED79650016851F /* Main.storyboard in Resources */, 2FAD9763203C412B000D30F8 /* config.xml in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXResourcesBuildPhase section */ /* Begin PBXSourcesBuildPhase section */ 504EC3001FED79650016851F /* Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( 504EC3081FED79650016851F /* AppDelegate.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXSourcesBuildPhase section */ /* Begin PBXVariantGroup section */ 504EC30B1FED79650016851F /* Main.storyboard */ = { isa = PBXVariantGroup; children = ( 504EC30C1FED79650016851F /* Base */, ); name = Main.storyboard; sourceTree = ""; }; 504EC3101FED79650016851F /* LaunchScreen.storyboard */ = { isa = PBXVariantGroup; children = ( 504EC3111FED79650016851F /* Base */, ); name = LaunchScreen.storyboard; sourceTree = ""; }; /* End PBXVariantGroup section */ /* Begin XCBuildConfiguration section */ 504EC3141FED79650016851F /* Debug */ = { isa = XCBuildConfiguration; baseConfigurationReference = 958DCC722DB07C7200EA8C5F /* debug.xcconfig */; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; CLANG_ANALYZER_NONNULL = YES; CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; CLANG_CXX_LIBRARY = "libc++"; CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_OBJC_ARC = YES; CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; CLANG_WARN_BOOL_CONVERSION = YES; CLANG_WARN_COMMA = YES; CLANG_WARN_CONSTANT_CONVERSION = YES; CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; CLANG_WARN_DOCUMENTATION_COMMENTS = YES; CLANG_WARN_EMPTY_BODY = YES; CLANG_WARN_ENUM_CONVERSION = YES; CLANG_WARN_INFINITE_RECURSION = YES; CLANG_WARN_INT_CONVERSION = YES; CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; CLANG_WARN_STRICT_PROTOTYPES = YES; CLANG_WARN_SUSPICIOUS_MOVE = YES; CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; CODE_SIGN_IDENTITY = "iPhone Developer"; COPY_PHASE_STRIP = NO; DEBUG_INFORMATION_FORMAT = dwarf; ENABLE_STRICT_OBJC_MSGSEND = YES; ENABLE_TESTABILITY = YES; GCC_C_LANGUAGE_STANDARD = gnu11; GCC_DYNAMIC_NO_PIC = NO; GCC_NO_COMMON_BLOCKS = YES; GCC_OPTIMIZATION_LEVEL = 0; GCC_PREPROCESSOR_DEFINITIONS = ( "DEBUG=1", "$(inherited)", ); GCC_WARN_64_TO_32_BIT_CONVERSION = YES; GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; GCC_WARN_UNDECLARED_SELECTOR = YES; GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; IPHONEOS_DEPLOYMENT_TARGET = 14.0; MTL_ENABLE_DEBUG_INFO = YES; ONLY_ACTIVE_ARCH = YES; SDKROOT = iphoneos; SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; }; name = Debug; }; 504EC3151FED79650016851F /* Release */ = { isa = XCBuildConfiguration; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; CLANG_ANALYZER_NONNULL = YES; CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; CLANG_CXX_LIBRARY = "libc++"; CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_OBJC_ARC = YES; CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; CLANG_WARN_BOOL_CONVERSION = YES; CLANG_WARN_COMMA = YES; CLANG_WARN_CONSTANT_CONVERSION = YES; CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; CLANG_WARN_DOCUMENTATION_COMMENTS = YES; CLANG_WARN_EMPTY_BODY = YES; CLANG_WARN_ENUM_CONVERSION = YES; CLANG_WARN_INFINITE_RECURSION = YES; CLANG_WARN_INT_CONVERSION = YES; CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; CLANG_WARN_STRICT_PROTOTYPES = YES; CLANG_WARN_SUSPICIOUS_MOVE = YES; CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; CODE_SIGN_IDENTITY = "iPhone Developer"; COPY_PHASE_STRIP = NO; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; ENABLE_NS_ASSERTIONS = NO; ENABLE_STRICT_OBJC_MSGSEND = YES; GCC_C_LANGUAGE_STANDARD = gnu11; GCC_NO_COMMON_BLOCKS = YES; GCC_WARN_64_TO_32_BIT_CONVERSION = YES; GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; GCC_WARN_UNDECLARED_SELECTOR = YES; GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; IPHONEOS_DEPLOYMENT_TARGET = 14.0; MTL_ENABLE_DEBUG_INFO = NO; SDKROOT = iphoneos; SWIFT_COMPILATION_MODE = wholemodule; SWIFT_OPTIMIZATION_LEVEL = "-O"; VALIDATE_PRODUCT = YES; }; name = Release; }; 504EC3171FED79650016851F /* Debug */ = { isa = XCBuildConfiguration; baseConfigurationReference = 958DCC722DB07C7200EA8C5F /* debug.xcconfig */; buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CODE_SIGN_STYLE = Automatic; CURRENT_PROJECT_VERSION = 1; DEVELOPMENT_TEAM = NF4B9ND56C; INFOPLIST_FILE = App/Info.plist; IPHONEOS_DEPLOYMENT_TARGET = 14.0; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", ); MARKETING_VERSION = 1.0; OTHER_SWIFT_FLAGS = "$(inherited) \"-D\" \"COCOAPODS\" \"-DDEBUG\""; PRODUCT_BUNDLE_IDENTIFIER = com.equimaps.capacitor_background_geolocation_example; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; SWIFT_VERSION = 5.0; TARGETED_DEVICE_FAMILY = "1,2"; }; name = Debug; }; 504EC3181FED79650016851F /* Release */ = { isa = XCBuildConfiguration; buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CODE_SIGN_STYLE = Automatic; CURRENT_PROJECT_VERSION = 1; DEVELOPMENT_TEAM = NF4B9ND56C; INFOPLIST_FILE = App/Info.plist; IPHONEOS_DEPLOYMENT_TARGET = 14.0; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", ); MARKETING_VERSION = 1.0; PRODUCT_BUNDLE_IDENTIFIER = com.equimaps.capacitor_background_geolocation_example; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_ACTIVE_COMPILATION_CONDITIONS = ""; SWIFT_VERSION = 5.0; TARGETED_DEVICE_FAMILY = "1,2"; }; name = Release; }; /* End XCBuildConfiguration section */ /* Begin XCConfigurationList section */ 504EC2FF1FED79650016851F /* Build configuration list for PBXProject "App" */ = { isa = XCConfigurationList; buildConfigurations = ( 504EC3141FED79650016851F /* Debug */, 504EC3151FED79650016851F /* Release */, ); defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; 504EC3161FED79650016851F /* Build configuration list for PBXNativeTarget "App" */ = { isa = XCConfigurationList; buildConfigurations = ( 504EC3171FED79650016851F /* Debug */, 504EC3181FED79650016851F /* Release */, ); defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; /* End XCConfigurationList section */ /* Begin XCLocalSwiftPackageReference section */ 8CA225792E6072F0007DCEEE /* XCLocalSwiftPackageReference "CapApp-SPM" */ = { isa = XCLocalSwiftPackageReference; relativePath = "CapApp-SPM"; }; /* End XCLocalSwiftPackageReference section */ /* Begin XCSwiftPackageProductDependency section */ 8CA225772E6071C7007DCEEE /* CapApp-SPM */ = { isa = XCSwiftPackageProductDependency; productName = "CapApp-SPM"; }; 8CA2257A2E6072F0007DCEEE /* CapApp-SPM */ = { isa = XCSwiftPackageProductDependency; productName = "CapApp-SPM"; }; /* End XCSwiftPackageProductDependency section */ }; rootObject = 504EC2FC1FED79650016851F /* Project object */; } ================================================ FILE: example/ios/App/App.xcodeproj/project.xcworkspace/contents.xcworkspacedata ================================================ ================================================ FILE: example/ios/App/CapApp-SPM/.gitignore ================================================ .DS_Store /.build /Packages /*.xcodeproj xcuserdata/ DerivedData/ .swiftpm/config/registries.json .swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata .netrc ================================================ FILE: example/ios/App/CapApp-SPM/Package.swift ================================================ // swift-tools-version: 5.9 import PackageDescription // DO NOT MODIFY THIS FILE - managed by Capacitor CLI commands let package = Package( name: "CapApp-SPM", platforms: [.iOS(.v14)], products: [ .library( name: "CapApp-SPM", targets: ["CapApp-SPM"]) ], dependencies: [ .package(url: "https://github.com/ionic-team/capacitor-swift-pm.git", exact: "7.4.3"), .package(name: "CapacitorCommunityBackgroundGeolocation", path: "../../../.."), .package(name: "CapacitorLocalNotifications", path: "../../../node_modules/@capacitor/local-notifications") ], targets: [ .target( name: "CapApp-SPM", dependencies: [ .product(name: "Capacitor", package: "capacitor-swift-pm"), .product(name: "Cordova", package: "capacitor-swift-pm"), .product(name: "CapacitorCommunityBackgroundGeolocation", package: "CapacitorCommunityBackgroundGeolocation"), .product(name: "CapacitorLocalNotifications", package: "CapacitorLocalNotifications") ] ) ] ) ================================================ FILE: example/ios/App/CapApp-SPM/README.md ================================================ # CapApp-SPM This SPM is used to host SPM dependencies for you Capacitor project Do not modify the contents of it or there may be unintended consequences. ================================================ FILE: example/ios/App/CapApp-SPM/Sources/CapApp-SPM/CapApp-SPM.swift ================================================ public let isCapacitorApp = true ================================================ FILE: example/ios/debug.xcconfig ================================================ CAPACITOR_DEBUG = true ================================================ FILE: example/package.json ================================================ { "private": true, "name": "background-geolocation-example", "version": "1.0.0", "scripts": { "prepare": "cp node_modules/@capacitor/core/dist/capacitor.js* node_modules/@capacitor/local-notifications/dist/plugin.js* www/" }, "dependencies": { "@capacitor-community/background-geolocation": "../", "@capacitor/android": "^7.0.0", "@capacitor/cli": "^7.0.0", "@capacitor/core": "^7.0.0", "@capacitor/ios": "^7.0.0", "@capacitor/local-notifications": "^7.0.0" } } ================================================ FILE: example/www/index.html ================================================ BackgroundGeolocation Example

    Notification permissions:
    Guess:
    • Init
    ================================================ FILE: example/www/main.js ================================================ /*jslint browser, null, devel */ /*global capacitorExports, capacitorLocalNotifications */ const {registerPlugin} = capacitorExports; const BackgroundGeolocation = registerPlugin("BackgroundGeolocation"); const started = Date.now(); const watcher_colours = {}; const colours = [ "red", "green", "blue", "yellow", "pink", "orange", "purple", "cyan" ]; function timestamp(time) { return String(Math.floor((time - started) / 1000)); } function log_for_watcher(text, time = Date.now(), colour = "gray") { const li = document.createElement("li"); li.style.color = colour; li.innerText = ( "L" + timestamp(time) + ":W" + timestamp(Date.now()) + ":" + text ); const container = document.getElementById("log"); return container.insertBefore(li, container.firstChild); } function log_error(error, colour = "gray") { console.error(error); return log_for_watcher( error.name + ": " + error.message, Date.now(), colour ); } function log_location(location, watcher_ID) { return log_for_watcher( location.latitude + ":" + location.longitude, location.time, watcher_colours[watcher_ID] ); } function add_watcher(background) { let id; BackgroundGeolocation.addWatcher( Object.assign({ stale: true }, ( background ? { backgroundTitle: "Tracking your location, senõr.", backgroundMessage: "Cancel to prevent battery drain." } : { // distanceFilter: 10 } )), function callback(location, error) { if (error) { if ( error.code === "NOT_AUTHORIZED" && window.confirm( "This app needs your location, " + "but does not have permission.\n\n" + "Open settings now?" ) ) { BackgroundGeolocation.openSettings(); } return log_error(error, watcher_colours[id]); } return log_location(location, id); } ).then(function retain_the_watcher_id(the_id) { id = the_id; const watcher_nr = Object.keys(watcher_colours).length; watcher_colours[id] = colours[watcher_nr]; const container = document.getElementById("watchers"); const li = document.createElement("li"); li.style.backgroundColor = watcher_colours[id]; li.innerText = ( background ? "BG" : "FG" ); const remove_btn = document.createElement("button"); remove_btn.innerText = "Remove"; remove_btn.onclick = function () { return BackgroundGeolocation.removeWatcher({id}).then( function () { container.removeChild( container.children.item( Object.keys(watcher_colours).indexOf(id) ) ); delete watcher_colours[id]; } ).catch( (error) => log_error(error, watcher_colours[id]) ); }; li.appendChild(remove_btn); return container.appendChild(li); }); } // Produces the most accurate location possible within the specified time limit. function make_guess(timeout) { return new Promise(function (resolve) { let last_location = null; let id; BackgroundGeolocation.addWatcher( { requestPermissions: false, stale: true }, function callback(location) { last_location = location; } ).then(function retain_callback_id(the_id) { id = the_id; }); setTimeout(function () { resolve(last_location); BackgroundGeolocation.removeWatcher({id}); }, timeout); }); } function guess(timeout) { return make_guess(timeout).then(function (location) { return ( location === null ? log_for_watcher("null", Date.now()) : log_for_watcher( [ location.latitude, location.longitude ].map(String).join(":"), location.time ) ); }); } function request_permissions() { capacitorLocalNotifications.LocalNotifications.requestPermissions().then( function (status) { log_for_watcher("Notification permissions " + status.display); } ); } ================================================ FILE: ios/Plugin/Info.plist ================================================ CFBundleDevelopmentRegion $(DEVELOPMENT_LANGUAGE) CFBundleExecutable $(EXECUTABLE_NAME) CFBundleIdentifier $(PRODUCT_BUNDLE_IDENTIFIER) CFBundleInfoDictionaryVersion 6.0 CFBundleName $(PRODUCT_NAME) CFBundlePackageType FMWK CFBundleShortVersionString 1.0 CFBundleVersion $(CURRENT_PROJECT_VERSION) NSPrincipalClass ================================================ FILE: ios/Plugin/Plugin.h ================================================ #import //! Project version number for Plugin. FOUNDATION_EXPORT double PluginVersionNumber; //! Project version string for Plugin. FOUNDATION_EXPORT const unsigned char PluginVersionString[]; // In this header, you should import all the public headers of your framework using statements like #import ================================================ FILE: ios/Plugin/Plugin.m ================================================ #import #import CAP_PLUGIN(BackgroundGeolocation, "BackgroundGeolocation", CAP_PLUGIN_METHOD(addWatcher, CAPPluginReturnCallback); CAP_PLUGIN_METHOD(removeWatcher, CAPPluginReturnPromise); CAP_PLUGIN_METHOD(openSettings, CAPPluginReturnPromise); ) ================================================ FILE: ios/Plugin/Swift/Plugin.swift ================================================ import Capacitor import Foundation import UIKit import CoreLocation // Avoids a bewildering type warning. let null = Optional.none as Any func formatLocation(_ location: CLLocation) -> PluginCallResultData { var simulated = false; if #available(iOS 15, *) { // Prior to iOS 15, it was not possible to detect simulated locations. // But in general, it is very difficult to simulate locations on iOS in // production. if location.sourceInformation != nil { simulated = location.sourceInformation!.isSimulatedBySoftware; } } return [ "latitude": location.coordinate.latitude, "longitude": location.coordinate.longitude, "accuracy": location.horizontalAccuracy, "altitude": location.altitude, "altitudeAccuracy": location.verticalAccuracy, "simulated": simulated, "speed": location.speed < 0 ? null : location.speed, "bearing": location.course < 0 ? null : location.course, "time": NSNumber( value: Int( location.timestamp.timeIntervalSince1970 * 1000 ) ), ] } class Watcher { let callbackId: String let locationManager: CLLocationManager = CLLocationManager() private let created = Date() private let allowStale: Bool private var isUpdatingLocation: Bool = false init(_ id: String, stale: Bool) { callbackId = id allowStale = stale } func start() { // Avoid unnecessary calls to startUpdatingLocation, which can // result in extraneous invocations of didFailWithError. if !isUpdatingLocation { locationManager.startUpdatingLocation() isUpdatingLocation = true } } func stop() { if isUpdatingLocation { locationManager.stopUpdatingLocation() isUpdatingLocation = false } } func isLocationValid(_ location: CLLocation) -> Bool { return ( allowStale || location.timestamp >= created ) } } @objc(BackgroundGeolocation) public class BackgroundGeolocation: CAPPlugin, CAPBridgedPlugin, CLLocationManagerDelegate { private var watchers = [Watcher]() public let identifier = "BackgroundGeolocation" public let jsName = "BackgroundGeolocation" public let pluginMethods: [CAPPluginMethod] = [ CAPPluginMethod(name: "addWatcher", returnType: CAPPluginReturnCallback), CAPPluginMethod(name: "removeWatcher", returnType: CAPPluginReturnPromise), CAPPluginMethod(name: "openSettings", returnType: CAPPluginReturnPromise) ] @objc public override func load() { UIDevice.current.isBatteryMonitoringEnabled = true } @objc func addWatcher(_ call: CAPPluginCall) { call.keepAlive = true // CLLocationManager requires main thread DispatchQueue.main.async { let background = call.getString("backgroundMessage") != nil let watcher = Watcher( call.callbackId, stale: call.getBool("stale") ?? false ) let manager = watcher.locationManager manager.delegate = self let externalPower = [ .full, .charging ].contains(UIDevice.current.batteryState) manager.desiredAccuracy = ( externalPower ? kCLLocationAccuracyBestForNavigation : kCLLocationAccuracyBest ) var distanceFilter = call.getDouble("distanceFilter") // It appears that setting manager.distanceFilter to 0 can prevent // subsequent location updates. See issue #88. if distanceFilter == nil || distanceFilter == 0 { distanceFilter = kCLDistanceFilterNone } manager.distanceFilter = distanceFilter! manager.allowsBackgroundLocationUpdates = background manager.showsBackgroundLocationIndicator = background manager.pausesLocationUpdatesAutomatically = false self.watchers.append(watcher) if call.getBool("requestPermissions") != false { let status = CLLocationManager.authorizationStatus() if [ .notDetermined, .denied, .restricted, ].contains(status) { return ( background ? manager.requestAlwaysAuthorization() : manager.requestWhenInUseAuthorization() ) } if ( background && status == .authorizedWhenInUse ) { // Attempt to escalate. manager.requestAlwaysAuthorization() } } return watcher.start() } } @objc func removeWatcher(_ call: CAPPluginCall) { // CLLocationManager requires main thread DispatchQueue.main.async { if let callbackId = call.getString("id") { if let index = self.watchers.firstIndex( where: { $0.callbackId == callbackId } ) { self.watchers[index].locationManager.stopUpdatingLocation() self.watchers.remove(at: index) } if let savedCall = self.bridge?.savedCall(withID: callbackId) { self.bridge?.releaseCall(savedCall) } return call.resolve() } return call.reject("No callback ID") } } @objc func openSettings(_ call: CAPPluginCall) { DispatchQueue.main.async { guard let settingsUrl = URL( string: UIApplication.openSettingsURLString ) else { return call.reject("No link to settings available") } if UIApplication.shared.canOpenURL(settingsUrl) { UIApplication.shared.open(settingsUrl, completionHandler: { (success) in if (success) { return call.resolve() } else { return call.reject("Failed to open settings") } }) } else { return call.reject("Cannot open settings") } } } public func locationManager( _ manager: CLLocationManager, didFailWithError error: Error ) { if let watcher = self.watchers.first( where: { $0.locationManager == manager } ) { if let call = self.bridge?.savedCall(withID: watcher.callbackId) { if let clErr = error as? CLError { if clErr.code == .locationUnknown { // This error is sometimes sent by the manager if // it cannot get a fix immediately. return } else if (clErr.code == .denied) { watcher.stop() return call.reject( "Permission denied.", "NOT_AUTHORIZED" ) } } return call.reject(error.localizedDescription, nil, error) } } } public func locationManager( _ manager: CLLocationManager, didUpdateLocations locations: [CLLocation] ) { if let location = locations.last { if let watcher = self.watchers.first( where: { $0.locationManager == manager } ) { if watcher.isLocationValid(location) { if let call = self.bridge?.savedCall(withID: watcher.callbackId) { return call.resolve(formatLocation(location)) } } } } } public func locationManager( _ manager: CLLocationManager, didChangeAuthorization status: CLAuthorizationStatus ) { // If this method is called before the user decides on a permission, as // it is on iOS 14 when the permissions dialog is presented, we ignore // it. if status != .notDetermined { if let watcher = self.watchers.first( where: { $0.locationManager == manager } ) { return watcher.start() } } } } ================================================ FILE: ios/Plugin.xcodeproj/project.pbxproj ================================================ // !$*UTF8*$! { archiveVersion = 1; classes = { }; objectVersion = 48; objects = { /* Begin PBXBuildFile section */ 03FC29A292ACC40490383A1F /* Pods_Plugin.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 3B2A61DA5A1F2DD4F959604D /* Pods_Plugin.framework */; }; 20C0B05DCFC8E3958A738AF2 /* Pods_PluginTests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F6753A823D3815DB436415E3 /* Pods_PluginTests.framework */; }; 50ADFF92201F53D600D50D53 /* Plugin.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 50ADFF88201F53D600D50D53 /* Plugin.framework */; }; 50ADFF97201F53D600D50D53 /* PluginTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50ADFF96201F53D600D50D53 /* PluginTests.swift */; }; 50ADFF99201F53D600D50D53 /* Plugin.h in Headers */ = {isa = PBXBuildFile; fileRef = 50ADFF8B201F53D600D50D53 /* Plugin.h */; settings = {ATTRIBUTES = (Public, ); }; }; 50ADFFA42020D75100D50D53 /* Capacitor.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 50ADFFA52020D75100D50D53 /* Capacitor.framework */; }; 50ADFFA82020EE4F00D50D53 /* Plugin.m in Sources */ = {isa = PBXBuildFile; fileRef = 50ADFFA72020EE4F00D50D53 /* Plugin.m */; }; 50E1A94820377CB70090CE1A /* Plugin.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50E1A94720377CB70090CE1A /* Plugin.swift */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ 50ADFF93201F53D600D50D53 /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; containerPortal = 50ADFF7F201F53D600D50D53 /* Project object */; proxyType = 1; remoteGlobalIDString = 50ADFF87201F53D600D50D53; remoteInfo = Plugin; }; /* End PBXContainerItemProxy section */ /* Begin PBXFileReference section */ 3B2A61DA5A1F2DD4F959604D /* Pods_Plugin.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Plugin.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 50ADFF88201F53D600D50D53 /* Plugin.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Plugin.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 50ADFF8B201F53D600D50D53 /* Plugin.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = Plugin.h; sourceTree = ""; }; 50ADFF8C201F53D600D50D53 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 50ADFF91201F53D600D50D53 /* PluginTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = PluginTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 50ADFF96201F53D600D50D53 /* PluginTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PluginTests.swift; sourceTree = ""; }; 50ADFF98201F53D600D50D53 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 50ADFFA52020D75100D50D53 /* Capacitor.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = Capacitor.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 50ADFFA72020EE4F00D50D53 /* Plugin.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = Plugin.m; sourceTree = ""; }; 50E1A94720377CB70090CE1A /* Plugin.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Plugin.swift; sourceTree = ""; }; 5E23F77F099397094342571A /* Pods-Plugin.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Plugin.debug.xcconfig"; path = "Pods/Target Support Files/Pods-Plugin/Pods-Plugin.debug.xcconfig"; sourceTree = ""; }; 91781294A431A2A7CC6EB714 /* Pods-Plugin.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Plugin.release.xcconfig"; path = "Pods/Target Support Files/Pods-Plugin/Pods-Plugin.release.xcconfig"; sourceTree = ""; }; 96ED1B6440D6672E406C8D19 /* Pods-PluginTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-PluginTests.debug.xcconfig"; path = "Pods/Target Support Files/Pods-PluginTests/Pods-PluginTests.debug.xcconfig"; sourceTree = ""; }; F65BB2953ECE002E1EF3E424 /* Pods-PluginTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-PluginTests.release.xcconfig"; path = "Pods/Target Support Files/Pods-PluginTests/Pods-PluginTests.release.xcconfig"; sourceTree = ""; }; F6753A823D3815DB436415E3 /* Pods_PluginTests.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_PluginTests.framework; sourceTree = BUILT_PRODUCTS_DIR; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ 50ADFF84201F53D600D50D53 /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( 50ADFFA42020D75100D50D53 /* Capacitor.framework in Frameworks */, 03FC29A292ACC40490383A1F /* Pods_Plugin.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; 50ADFF8E201F53D600D50D53 /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( 50ADFF92201F53D600D50D53 /* Plugin.framework in Frameworks */, 20C0B05DCFC8E3958A738AF2 /* Pods_PluginTests.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ 50ADFF7E201F53D600D50D53 = { isa = PBXGroup; children = ( 50ADFF8A201F53D600D50D53 /* Plugin */, 50ADFF95201F53D600D50D53 /* PluginTests */, 50ADFF89201F53D600D50D53 /* Products */, 8C8E7744173064A9F6D438E3 /* Pods */, A797B9EFA3DCEFEA1FBB66A9 /* Frameworks */, ); sourceTree = ""; }; 50ADFF89201F53D600D50D53 /* Products */ = { isa = PBXGroup; children = ( 50ADFF88201F53D600D50D53 /* Plugin.framework */, 50ADFF91201F53D600D50D53 /* PluginTests.xctest */, ); name = Products; sourceTree = ""; }; 50ADFF8A201F53D600D50D53 /* Plugin */ = { isa = PBXGroup; children = ( 50E1A94720377CB70090CE1A /* Plugin.swift */, 50ADFF8B201F53D600D50D53 /* Plugin.h */, 50ADFFA72020EE4F00D50D53 /* Plugin.m */, 50ADFF8C201F53D600D50D53 /* Info.plist */, ); path = Plugin; sourceTree = ""; }; 50ADFF95201F53D600D50D53 /* PluginTests */ = { isa = PBXGroup; children = ( 50ADFF96201F53D600D50D53 /* PluginTests.swift */, 50ADFF98201F53D600D50D53 /* Info.plist */, ); path = PluginTests; sourceTree = ""; }; 8C8E7744173064A9F6D438E3 /* Pods */ = { isa = PBXGroup; children = ( 5E23F77F099397094342571A /* Pods-Plugin.debug.xcconfig */, 91781294A431A2A7CC6EB714 /* Pods-Plugin.release.xcconfig */, 96ED1B6440D6672E406C8D19 /* Pods-PluginTests.debug.xcconfig */, F65BB2953ECE002E1EF3E424 /* Pods-PluginTests.release.xcconfig */, ); name = Pods; sourceTree = ""; }; A797B9EFA3DCEFEA1FBB66A9 /* Frameworks */ = { isa = PBXGroup; children = ( 50ADFFA52020D75100D50D53 /* Capacitor.framework */, 3B2A61DA5A1F2DD4F959604D /* Pods_Plugin.framework */, F6753A823D3815DB436415E3 /* Pods_PluginTests.framework */, ); name = Frameworks; sourceTree = ""; }; /* End PBXGroup section */ /* Begin PBXHeadersBuildPhase section */ 50ADFF85201F53D600D50D53 /* Headers */ = { isa = PBXHeadersBuildPhase; buildActionMask = 2147483647; files = ( 50ADFF99201F53D600D50D53 /* Plugin.h in Headers */, ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXHeadersBuildPhase section */ /* Begin PBXNativeTarget section */ 50ADFF87201F53D600D50D53 /* Plugin */ = { isa = PBXNativeTarget; buildConfigurationList = 50ADFF9C201F53D600D50D53 /* Build configuration list for PBXNativeTarget "Plugin" */; buildPhases = ( AB5B3E54B4E897F32C2279DA /* [CP] Check Pods Manifest.lock */, 50ADFF83201F53D600D50D53 /* Sources */, 50ADFF84201F53D600D50D53 /* Frameworks */, 50ADFF85201F53D600D50D53 /* Headers */, 50ADFF86201F53D600D50D53 /* Resources */, ); buildRules = ( ); dependencies = ( ); name = Plugin; productName = Plugin; productReference = 50ADFF88201F53D600D50D53 /* Plugin.framework */; productType = "com.apple.product-type.framework"; }; 50ADFF90201F53D600D50D53 /* PluginTests */ = { isa = PBXNativeTarget; buildConfigurationList = 50ADFF9F201F53D600D50D53 /* Build configuration list for PBXNativeTarget "PluginTests" */; buildPhases = ( 0596884F929ED6F1DE134961 /* [CP] Check Pods Manifest.lock */, 50ADFF8D201F53D600D50D53 /* Sources */, 50ADFF8E201F53D600D50D53 /* Frameworks */, 50ADFF8F201F53D600D50D53 /* Resources */, CCA81D3B7E26D0D727D24C84 /* [CP] Embed Pods Frameworks */, ); buildRules = ( ); dependencies = ( 50ADFF94201F53D600D50D53 /* PBXTargetDependency */, ); name = PluginTests; productName = PluginTests; productReference = 50ADFF91201F53D600D50D53 /* PluginTests.xctest */; productType = "com.apple.product-type.bundle.unit-test"; }; /* End PBXNativeTarget section */ /* Begin PBXProject section */ 50ADFF7F201F53D600D50D53 /* Project object */ = { isa = PBXProject; attributes = { LastSwiftUpdateCheck = 0920; LastUpgradeCheck = 0920; ORGANIZATIONNAME = "Max Lynch"; TargetAttributes = { 50ADFF87201F53D600D50D53 = { CreatedOnToolsVersion = 9.2; LastSwiftMigration = 1100; ProvisioningStyle = Automatic; }; 50ADFF90201F53D600D50D53 = { CreatedOnToolsVersion = 9.2; LastSwiftMigration = 1100; ProvisioningStyle = Automatic; }; }; }; buildConfigurationList = 50ADFF82201F53D600D50D53 /* Build configuration list for PBXProject "Plugin" */; compatibilityVersion = "Xcode 8.0"; developmentRegion = en; hasScannedForEncodings = 0; knownRegions = ( en, ); mainGroup = 50ADFF7E201F53D600D50D53; productRefGroup = 50ADFF89201F53D600D50D53 /* Products */; projectDirPath = ""; projectRoot = ""; targets = ( 50ADFF87201F53D600D50D53 /* Plugin */, 50ADFF90201F53D600D50D53 /* PluginTests */, ); }; /* End PBXProject section */ /* Begin PBXResourcesBuildPhase section */ 50ADFF86201F53D600D50D53 /* Resources */ = { isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( ); runOnlyForDeploymentPostprocessing = 0; }; 50ADFF8F201F53D600D50D53 /* Resources */ = { isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXResourcesBuildPhase section */ /* Begin PBXShellScriptBuildPhase section */ 0596884F929ED6F1DE134961 /* [CP] Check Pods Manifest.lock */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( ); inputPaths = ( "${PODS_PODFILE_DIR_PATH}/Podfile.lock", "${PODS_ROOT}/Manifest.lock", ); name = "[CP] Check Pods Manifest.lock"; outputPaths = ( "$(DERIVED_FILE_DIR)/Pods-PluginTests-checkManifestLockResult.txt", ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; showEnvVarsInLog = 0; }; AB5B3E54B4E897F32C2279DA /* [CP] Check Pods Manifest.lock */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( ); inputPaths = ( "${PODS_PODFILE_DIR_PATH}/Podfile.lock", "${PODS_ROOT}/Manifest.lock", ); name = "[CP] Check Pods Manifest.lock"; outputPaths = ( "$(DERIVED_FILE_DIR)/Pods-Plugin-checkManifestLockResult.txt", ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; showEnvVarsInLog = 0; }; CCA81D3B7E26D0D727D24C84 /* [CP] Embed Pods Frameworks */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( ); inputPaths = ( "${PODS_ROOT}/Target Support Files/Pods-PluginTests/Pods-PluginTests-frameworks.sh", "${BUILT_PRODUCTS_DIR}/Capacitor/Capacitor.framework", "${BUILT_PRODUCTS_DIR}/CapacitorCordova/Cordova.framework", "${BUILT_PRODUCTS_DIR}/GCDWebServer/GCDWebServer.framework", ); name = "[CP] Embed Pods Frameworks"; outputPaths = ( "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Capacitor.framework", "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Cordova.framework", "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/GCDWebServer.framework", ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-PluginTests/Pods-PluginTests-frameworks.sh\"\n"; showEnvVarsInLog = 0; }; /* End PBXShellScriptBuildPhase section */ /* Begin PBXSourcesBuildPhase section */ 50ADFF83201F53D600D50D53 /* Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( 50E1A94820377CB70090CE1A /* Plugin.swift in Sources */, 50ADFFA82020EE4F00D50D53 /* Plugin.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; 50ADFF8D201F53D600D50D53 /* Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( 50ADFF97201F53D600D50D53 /* PluginTests.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXSourcesBuildPhase section */ /* Begin PBXTargetDependency section */ 50ADFF94201F53D600D50D53 /* PBXTargetDependency */ = { isa = PBXTargetDependency; target = 50ADFF87201F53D600D50D53 /* Plugin */; targetProxy = 50ADFF93201F53D600D50D53 /* PBXContainerItemProxy */; }; /* End PBXTargetDependency section */ /* Begin XCBuildConfiguration section */ 50ADFF9A201F53D600D50D53 /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; CLANG_ANALYZER_NONNULL = YES; CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; CLANG_CXX_LIBRARY = "libc++"; CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_OBJC_ARC = YES; CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; CLANG_WARN_BOOL_CONVERSION = YES; CLANG_WARN_COMMA = YES; CLANG_WARN_CONSTANT_CONVERSION = YES; CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; CLANG_WARN_DOCUMENTATION_COMMENTS = YES; CLANG_WARN_EMPTY_BODY = YES; CLANG_WARN_ENUM_CONVERSION = YES; CLANG_WARN_INFINITE_RECURSION = YES; CLANG_WARN_INT_CONVERSION = YES; CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; CLANG_WARN_STRICT_PROTOTYPES = YES; CLANG_WARN_SUSPICIOUS_MOVE = YES; CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; CODE_SIGN_IDENTITY = "iPhone Developer"; COPY_PHASE_STRIP = NO; CURRENT_PROJECT_VERSION = 1; DEBUG_INFORMATION_FORMAT = dwarf; ENABLE_STRICT_OBJC_MSGSEND = YES; ENABLE_TESTABILITY = YES; GCC_C_LANGUAGE_STANDARD = gnu11; GCC_DYNAMIC_NO_PIC = NO; GCC_NO_COMMON_BLOCKS = YES; GCC_OPTIMIZATION_LEVEL = 0; GCC_PREPROCESSOR_DEFINITIONS = ( "DEBUG=1", "$(inherited)", ); GCC_WARN_64_TO_32_BIT_CONVERSION = YES; GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; GCC_WARN_UNDECLARED_SELECTOR = YES; GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; IPHONEOS_DEPLOYMENT_TARGET = 13.0; MTL_ENABLE_DEBUG_INFO = YES; ONLY_ACTIVE_ARCH = YES; SDKROOT = iphoneos; SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; VERSIONING_SYSTEM = "apple-generic"; VERSION_INFO_PREFIX = ""; }; name = Debug; }; 50ADFF9B201F53D600D50D53 /* Release */ = { isa = XCBuildConfiguration; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; CLANG_ANALYZER_NONNULL = YES; CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; CLANG_CXX_LIBRARY = "libc++"; CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_OBJC_ARC = YES; CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; CLANG_WARN_BOOL_CONVERSION = YES; CLANG_WARN_COMMA = YES; CLANG_WARN_CONSTANT_CONVERSION = YES; CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; CLANG_WARN_DOCUMENTATION_COMMENTS = YES; CLANG_WARN_EMPTY_BODY = YES; CLANG_WARN_ENUM_CONVERSION = YES; CLANG_WARN_INFINITE_RECURSION = YES; CLANG_WARN_INT_CONVERSION = YES; CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; CLANG_WARN_STRICT_PROTOTYPES = YES; CLANG_WARN_SUSPICIOUS_MOVE = YES; CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; CODE_SIGN_IDENTITY = "iPhone Developer"; COPY_PHASE_STRIP = NO; CURRENT_PROJECT_VERSION = 1; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; ENABLE_NS_ASSERTIONS = NO; ENABLE_STRICT_OBJC_MSGSEND = YES; GCC_C_LANGUAGE_STANDARD = gnu11; GCC_NO_COMMON_BLOCKS = YES; GCC_WARN_64_TO_32_BIT_CONVERSION = YES; GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; GCC_WARN_UNDECLARED_SELECTOR = YES; GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; IPHONEOS_DEPLOYMENT_TARGET = 13.0; MTL_ENABLE_DEBUG_INFO = NO; SDKROOT = iphoneos; SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; VALIDATE_PRODUCT = YES; VERSIONING_SYSTEM = "apple-generic"; VERSION_INFO_PREFIX = ""; }; name = Release; }; 50ADFF9D201F53D600D50D53 /* Debug */ = { isa = XCBuildConfiguration; baseConfigurationReference = 5E23F77F099397094342571A /* Pods-Plugin.debug.xcconfig */; buildSettings = { CLANG_ENABLE_MODULES = YES; CODE_SIGN_IDENTITY = ""; CODE_SIGN_STYLE = Automatic; DEFINES_MODULE = YES; DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_CURRENT_VERSION = 1; DYLIB_INSTALL_NAME_BASE = "@rpath"; INFOPLIST_FILE = Plugin/Info.plist; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; IPHONEOS_DEPLOYMENT_TARGET = 13.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks $(FRAMEWORK_SEARCH_PATHS)\n$(FRAMEWORK_SEARCH_PATHS)\n$(FRAMEWORK_SEARCH_PATHS)"; ONLY_ACTIVE_ARCH = YES; PRODUCT_BUNDLE_IDENTIFIER = com.getcapacitor.Plugin; PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; SKIP_INSTALL = YES; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; SWIFT_VERSION = 5.0; TARGETED_DEVICE_FAMILY = "1,2"; }; name = Debug; }; 50ADFF9E201F53D600D50D53 /* Release */ = { isa = XCBuildConfiguration; baseConfigurationReference = 91781294A431A2A7CC6EB714 /* Pods-Plugin.release.xcconfig */; buildSettings = { CLANG_ENABLE_MODULES = YES; CODE_SIGN_IDENTITY = ""; CODE_SIGN_STYLE = Automatic; DEFINES_MODULE = YES; DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_CURRENT_VERSION = 1; DYLIB_INSTALL_NAME_BASE = "@rpath"; INFOPLIST_FILE = Plugin/Info.plist; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; IPHONEOS_DEPLOYMENT_TARGET = 13.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks $(FRAMEWORK_SEARCH_PATHS)"; ONLY_ACTIVE_ARCH = NO; PRODUCT_BUNDLE_IDENTIFIER = com.getcapacitor.Plugin; PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; SKIP_INSTALL = YES; SWIFT_VERSION = 5.0; TARGETED_DEVICE_FAMILY = "1,2"; }; name = Release; }; 50ADFFA0201F53D600D50D53 /* Debug */ = { isa = XCBuildConfiguration; baseConfigurationReference = 96ED1B6440D6672E406C8D19 /* Pods-PluginTests.debug.xcconfig */; buildSettings = { ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; CODE_SIGN_STYLE = Automatic; INFOPLIST_FILE = PluginTests/Info.plist; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; PRODUCT_BUNDLE_IDENTIFIER = com.getcapacitor.PluginTests; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_VERSION = 5.0; TARGETED_DEVICE_FAMILY = "1,2"; }; name = Debug; }; 50ADFFA1201F53D600D50D53 /* Release */ = { isa = XCBuildConfiguration; baseConfigurationReference = F65BB2953ECE002E1EF3E424 /* Pods-PluginTests.release.xcconfig */; buildSettings = { ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; CODE_SIGN_STYLE = Automatic; INFOPLIST_FILE = PluginTests/Info.plist; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; PRODUCT_BUNDLE_IDENTIFIER = com.getcapacitor.PluginTests; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_VERSION = 5.0; TARGETED_DEVICE_FAMILY = "1,2"; }; name = Release; }; /* End XCBuildConfiguration section */ /* Begin XCConfigurationList section */ 50ADFF82201F53D600D50D53 /* Build configuration list for PBXProject "Plugin" */ = { isa = XCConfigurationList; buildConfigurations = ( 50ADFF9A201F53D600D50D53 /* Debug */, 50ADFF9B201F53D600D50D53 /* Release */, ); defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; 50ADFF9C201F53D600D50D53 /* Build configuration list for PBXNativeTarget "Plugin" */ = { isa = XCConfigurationList; buildConfigurations = ( 50ADFF9D201F53D600D50D53 /* Debug */, 50ADFF9E201F53D600D50D53 /* Release */, ); defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; 50ADFF9F201F53D600D50D53 /* Build configuration list for PBXNativeTarget "PluginTests" */ = { isa = XCConfigurationList; buildConfigurations = ( 50ADFFA0201F53D600D50D53 /* Debug */, 50ADFFA1201F53D600D50D53 /* Release */, ); defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; /* End XCConfigurationList section */ }; rootObject = 50ADFF7F201F53D600D50D53 /* Project object */; } ================================================ FILE: ios/Plugin.xcworkspace/contents.xcworkspacedata ================================================ ================================================ FILE: ios/Podfile ================================================ platform :ios, '12.0' def capacitor_pods # Comment the next line if you're not using Swift and don't want to use dynamic frameworks use_frameworks! pod 'Capacitor', :path => '../node_modules/@capacitor/ios' pod 'CapacitorCordova', :path => '../node_modules/@capacitor/ios' end target 'Plugin' do capacitor_pods end target 'PluginTests' do capacitor_pods end ================================================ FILE: package.json ================================================ { "name": "@capacitor-community/background-geolocation", "version": "1.2.26", "description": "Receive geolocation updates even while the app is in the background.", "repository": { "type": "git", "url": "https://github.com/capacitor-community/background-geolocation" }, "license": "MIT", "author": "James Diacono", "types": "definitions.d.ts", "files": [ "CapacitorCommunityBackgroundGeolocation.podspec", "Package.swift", "android/build.gradle", "android/gradle.properties", "android/gradle/wrapper/gradle-wrapper.properties", "android/proguard-rules.pro", "android/settings.gradle", "android/src/main/", "definitions.d.ts", "ios/Plugin/Info.plist", "ios/Plugin/Plugin.*", "ios/Plugin/Swift/Plugin.swift", "ios/Podfile*" ], "devDependencies": { "@capacitor/android": "^7.0.0", "@capacitor/core": "^7.0.0", "@capacitor/ios": "^7.0.0" }, "peerDependencies": { "@capacitor/core": ">=3.0.0" }, "capacitor": { "ios": { "src": "ios" }, "android": { "src": "android" } } }