react-native-background-geolocation icon indicating copy to clipboard operation
react-native-background-geolocation copied to clipboard

check if the service will always works

Open ahmedabartaieb opened this issue 1 year ago • 0 comments

``

Your Environment

  • Plugin version: 4.14.5
  • Platform: Android
  • Device manufacturer / model: samsung
  • React Native version (react-native -v): 0.72.5
  • Plugin config
`android/build.gradle`
buildscript {
    ext {
        buildToolsVersion = "33.0.0"
        minSdkVersion = 21
        compileSdkVersion = 33
        targetSdkVersion = 33
        kotlinVersion = "1.7.0"
        // We use NDK 23 which has both M1 support and is the side-by-side NDK version from AGP.
        ndkVersion = "23.1.7779620"
        googlePlayServicesLocationVersion = "21.0.1"
        playServicesLocationVersion = "21.0.1"

    }
    repositories {
        google()
        mavenCentral()
    }
    dependencies {
        classpath("com.android.tools.build:gradle")
        classpath("com.facebook.react:react-native-gradle-plugin")
        classpath 'com.google.gms:google-services:4.3.15'    
        
    }
}
allprojects {   // <-- NOTE:  allprojects container -- If you don't see this, create it.
    repositories {
       // Required for react-native-background-geolocation
       maven { url("${project(':react-native-background-geolocation').projectDir}/libs") }
       //maven { url 'https://developer.huawei.com/repo/' }
       // Required for react-native-background-fetch
       maven { url("${project(':react-native-background-fetch').projectDir}/libs")}
}
}

Context

apply plugin: "com.android.application"
apply plugin: "com.facebook.react"
apply plugin: 'com.google.gms.google-services'
apply from: "../../node_modules/react-native-vector-image/strip_svgs.gradle"

/**
 * This is the configuration block to customize your React Native Android app.
 * By default you don't need to apply any configuration, just uncomment the lines you need.
 */
react {
    /* Folders */
    //   The root of your project, i.e. where "package.json" lives. Default is '..'
    // root = file("../")
    //   The folder where the react-native NPM package is. Default is ../node_modules/react-native
    // reactNativeDir = file("../node_modules/react-native")
    //   The folder where the react-native Codegen package is. Default is ../node_modules/@react-native/codegen
    // codegenDir = file("../node_modules/@react-native/codegen")
    //   The cli.js file which is the React Native CLI entrypoint. Default is ../node_modules/react-native/cli.js
    // cliFile = file("../node_modules/react-native/cli.js")

    /* Variants */
    //   The list of variants to that are debuggable. For those we're going to
    //   skip the bundling of the JS bundle and the assets. By default is just 'debug'.
    //   If you add flavors like lite, prod, etc. you'll have to list your debuggableVariants.
    // debuggableVariants = ["liteDebug", "prodDebug"]

    /* Bundling */
    //   A list containing the node command and its flags. Default is just 'node'.
    // nodeExecutableAndArgs = ["node"]
    //
    //   The command to run when bundling. By default is 'bundle'
    // bundleCommand = "ram-bundle"
    //
    //   The path to the CLI configuration file. Default is empty.
    // bundleConfig = file(../rn-cli.config.js)
    //
    //   The name of the generated asset file containing your JS bundle
    // bundleAssetName = "MyApplication.android.bundle"
    //
    //   The entry file for bundle generation. Default is 'index.android.js' or 'index.js'
    // entryFile = file("../js/MyApplication.android.js")
    //
    //   A list of extra flags to pass to the 'bundle' commands.
    //   See https://github.com/react-native-community/cli/blob/main/docs/commands.md#bundle
    // extraPackagerArgs = []

    /* Hermes Commands */
    //   The hermes compiler command to run. By default it is 'hermesc'
    // hermesCommand = "$rootDir/my-custom-hermesc/bin/hermesc"
    //
    //   The list of flags to pass to the Hermes compiler. By default is "-O", "-output-source-map"
    // hermesFlags = ["-O", "-output-source-map"]
}

/**
 * Set this to true to Run Proguard on Release builds to minify the Java bytecode.
 */
def enableProguardInReleaseBuilds = false

/**
 * The preferred build flavor of JavaScriptCore (JSC)
 *
 * For example, to use the international variant, you can use:
 * def jscFlavor = 'org.webkit:android-jsc-intl:+'
 *
 * The international variant includes ICU i18n library and necessary data
 * allowing to use e.g. Date.toLocaleString and String.localeCompare that
 * give correct results when using with locales other than en-US. Note that
 * this variant is about 6MiB larger per architecture than default.
 */
def jscFlavor = 'org.webkit:android-jsc:+'

android {
    ndkVersion rootProject.ext.ndkVersion

    compileSdkVersion rootProject.ext.compileSdkVersion

    namespace "com.marketme"
    defaultConfig {
        applicationId "com.marketme"
        minSdkVersion rootProject.ext.minSdkVersion
        targetSdkVersion rootProject.ext.targetSdkVersion
        versionCode 28
        versionName "0.0.28"
    }
    signingConfigs {
        debug {
            storeFile file('debug.keystore')
            storePassword 'android'
            keyAlias 'androiddebugkey'
            keyPassword 'android'
        }
        release {
            if (project.hasProperty('MYAPP_UPLOAD_STORE_FILE')) {
                storeFile file(MYAPP_UPLOAD_STORE_FILE)
                storePassword MYAPP_UPLOAD_STORE_PASSWORD
                keyAlias MYAPP_UPLOAD_KEY_ALIAS
                keyPassword MYAPP_UPLOAD_KEY_PASSWORD
            }
        }
        
    }
    buildTypes {
        debug {
            signingConfig signingConfigs.debug
        }
        release {
            // Caution! In production, you need to generate your own keystore file.
            // see https://reactnative.dev/docs/signed-apk-android.
            signingConfig signingConfigs.release
            minifyEnabled enableProguardInReleaseBuilds
            proguardFiles getDefaultProguardFile("proguard-android.txt"), "proguard-rules.pro"
            proguardFiles "${project(':react-native-background-geolocation').projectDir}/proguard-rules.pro"

        }
    }
}

dependencies {
    // The version of react-native is set by the React Native Gradle Plugin
    implementation("com.facebook.react:react-android")

    implementation "androidx.swiperefreshlayout:swiperefreshlayout:1.0.0"

    implementation project(':react-native-splash-screen')
    
    debugImplementation("com.facebook.flipper:flipper:${FLIPPER_VERSION}")
    debugImplementation("com.facebook.flipper:flipper-network-plugin:${FLIPPER_VERSION}") {
        exclude group:'com.squareup.okhttp3', module:'okhttp'
    }

    debugImplementation("com.facebook.flipper:flipper-fresco-plugin:${FLIPPER_VERSION}")
    if (hermesEnabled.toBoolean()) {
        implementation("com.facebook.react:hermes-android")
    } else {
        implementation jscFlavor
    }

   implementation 'com.facebook.react:react-native:+'
   implementation project(':react-native-maps')
   implementation 'com.google.android.gms:play-services-location:21.0.1'
  
  implementation 'com.google.android.gms:play-services-maps:18.0.2'
}

apply from: file("../../node_modules/@react-native-community/cli-platform-android/native_modules.gradle"); applyNativeModulesAppBuildGradle(project)
// background-geolocation
Project background_geolocation = project(':react-native-background-geolocation')
apply from: "${background_geolocation.projectDir}/app.gradle"
**AndroidManifesqt.xml** `
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION" />
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
<uses-permission android:name="android.permission.com.google.android.gms.permission.AD_ID" />


<meta-data
    android:name="com.google.firebase.messaging.default_notification_icon"
   />

<application
  android:name=".MainApplication"
  android:label="@string/app_name"
  android:icon="@mipmap/ic_launcher"
  android:allowBackup="false"
  android:usesCleartextTraffic="true"
  android:theme="@style/AppTheme">

  <meta-data
 android:name="com.google.android.geo.API_KEY"
 android:value="xxxxxxxxxxxxxxxxxxxxxxxx"/>
 
  <activity
    android:name=".MainActivity"
    android:label="@string/app_name"
    android:configChanges="keyboard|keyboardHidden|orientation|screenLayout|screenSize|smallestScreenSize|uiMode"
    android:launchMode="singleTask"
    android:windowSoftInputMode="adjustResize"
    android:screenOrientation="portrait"
    android:exported="true"  
    android:theme="@style/AppTheme"
   >
    <intent-filter android:autoVerify="true">
        <action android:name="android.intent.action.MAIN" />
        <category android:name="android.intent.category.LAUNCHER" />
    </intent-filter>
      <intent-filter android:autoVerify="true">
    <action android:name="android.intent.action.VIEW" />
    <category android:name="android.intent.category.DEFAULT" />
    <category android:name="android.intent.category.BROWSABLE" />
      <data android:scheme="https" />
      <data android:scheme="http" />
      <data android:host="api.marketme-app.com"/>
</intent-filter>
<intent-filter >
    <action android:name="android.intent.action.VIEW" />
    <category android:name="android.intent.category.DEFAULT" />
    <category android:name="android.intent.category.BROWSABLE" />
  <data android:scheme="marketMe" android:host="*" />
</intent-filter>
  </activity>
        <service android:name="com.asterinet.react.bgactions.RNBackgroundActionsTask" />
        <meta-data android:name="com.transistorsoft.locationmanager.license" android:value="xxxxxxxxxxxxxxxxxxxx"  />

</application>

**in the code i make a reducer : position reducer . this is the necessary part of code :**import {createSlice} from '@reduxjs/toolkit'; import { check_near_sellers, add_search_product, add_search_stores, allSearchs, deleteSearch, } from '~/Actions/nearBySellersActions'; const initialState = { isAllPermissionsFine: false, enabled: false, locationListened: null, region: { latitude: 0, longitude: 0, latitudeDelta: 0.01, longitudeDelta: 0.01, }, tracking: 1, searchSaved: [], myNearSellersByMyFavorites: [], checkingAfterSearch: 0, myAllSearchs: [], myAllSearchsLoading: false, currentSearch: null, };

const positionReducer = createSlice({ name: 'position', initialState, reducers: { setIsAllPermissionsFine: (state, action) => { const {value} = action.payload; state.isAllPermissionsFine = value; }, setEnabled: (state, action) => { const {value} = action.payload; state.enabled = value; }, setRegion: (state, action) => { const {region} = action.payload; state.region = region; }, putLocationListened: (state, action) => { const {location} = action.payload; if (state.locationListened === null) { state.region = { latitude: location.coords.latitude, longitude: location.coords.longitude, latitudeDelta: 0.01, longitudeDelta: 0.01, }; } state.locationListened = location; state.tracking = state.tracking + 1; console.log('[Tracking]', state.tracking); }, pushSearchStored: (state, action) => { const {data} = action.payload; state.searchSaved.push(data); }, setCurrentSearch: (state, action) => { const {value} = action.payload; state.currentSearch = value; }, }, extraReducers: builder => { .....} }); export const { setIsAllPermissionsFine, setEnabled, setRegion, putLocationListened, pushSearchStored, setCurrentSearch, } = positionReducer.actions;

export default positionReducer.reducer;**now the most important file index.tsx this is the important code :**import { Alert, AppState, Linking, PermissionsAndroid, StatusBar, } from 'react-native'; import { setIsAllPermissionsFine, setEnabled, putLocationListened, } from '~/Reducers/PositionReducer'; import { check_near_sellers, update_location, } from '~/Actions/nearBySellersActions'; import BackgroundGeolocation, { Location, Subscription, } from 'react-native-background-geolocation'; import {ModalAlert} from '~/Components/ModalAlert'; export default () => { const [appState, setAppState] = useState(AppState.currentState); const [modalVisible, setModalVisible] = useState({ isVisible: false, iconName: '', iconSizeWidth: 50, iconSizeHeight: 50, message: '', showLeftButton: false, showRightButton: false, buttonLeftName: '', buttonRightName: '', buttonLeftFunction: () => {}, buttonRightFunction: () => {}, closeModel: () => {}, }); const requestLocationForBackgroundGeolocation = async () => { try { let result = { activity: false, locationPermission: false, }; const fineLocationPermissionsStatus = await Permission.check( 'android.permission.ACCESS_FINE_LOCATION', ); console.log({ fineLocationPermissionsStatus: fineLocationPermissionsStatus, }); const backGroundLocationPermissionsStatus = await Permission.check( 'android.permission.ACCESS_BACKGROUND_LOCATION', ); console.log({ backGroundLocationPermissionsStatus: backGroundLocationPermissionsStatus, }); if ( fineLocationPermissionsStatus === 'granted' && backGroundLocationPermissionsStatus === 'granted' ) { result.locationPermission = true; } if ( fineLocationPermissionsStatus !== 'granted' && backGroundLocationPermissionsStatus !== 'granted' ) { setModalVisible(prev => ({ ...prev, isVisible: true, iconName: 'info', message: 'MarketMe collects location data in the background to send you personalized notifications about the products or store you search for, even when the app is closed or not in use. This feature is designed to enhance your experience by providing relevant information at the right time. You can manage or disable this option in the app settings at any time', showLeftButton: true, showRightButton: true, buttonLeftName: 'Cancel', buttonRightName: 'Open Settings', buttonLeftFunction: () => { closeModalFn(); }, buttonRightFunction: () => { setModalVisible(prev => ({...prev, isVisible: false})); openSettings(); }, closeModel: () => { closeModalFn(); }, })); } const activityPhysique = await Permission.check( 'android.permission.ACTIVITY_RECOGNITION', ); console.log({activityPhysique: activityPhysique}); if (activityPhysique === 'granted') { result.activity = true; } return result; } catch (error) { console.error(error); return false; } }; const closeModalFn = () => { setModalVisible(prev => ({...prev, isVisible: false})); }; const { isAllPermissionsFine, enabled, locationListened, tracking, myNearSellersByMyFavorites, checkingAfterSearch, } = useSelector(state => state.position);

React.useEffect(() => { // first useEffect activated (config of BackgroundGeolocation) (async () => { /// 1. Subscribe to events. const user_role = await AsyncStorage.getItem(USER_ROLE); if (user_role == USER_ROLE_BUYER) { dispatch(get_product_names_buyer()); BackgroundGeolocation.ready({ desiredAccuracy: BackgroundGeolocation.DESIRED_ACCURACY_HIGH, distanceFilter: 15, stopTimeout: 30, // Permissions locationAuthorizationRequest: 'Always', debug: false, logLevel: BackgroundGeolocation.LOG_LEVEL_VERBOSE, enableHeadless: true, stopOnStationary: false, stopOnTerminate: false, startOnBoot: true, //disableLocationAuthorizationAlert: true, }).then(state => { if (state.enabled === false) { dispatch(setEnabled({value: true})); } }); const onLocation: Subscription = BackgroundGeolocation.onLocation( location => { console.log('[LOCATION]'); dispatch(putLocationListened({location: location})); }, );

    return () => {
      onLocation.remove();
    };
  }
})();

}, [userLoginSuccess]); React.useEffect(() => { // second useEffect activated check permiisions by requestLocationForBackgroundGeolocation if all things fine starting the service //else showing modal to go to app setting and then activte the useEffect bellow if (enabled) { BackgroundGeolocation.getProviderState().then(async providerState => { if (providerState.gps) { if (providerState.status < 3 || providerState.status == 4) { const check = await requestLocationForBackgroundGeolocation(); console.log(check); if (check.activity && check.locationPermission) { dispatch(setIsAllPermissionsFine({value: true})); await BackgroundGeolocation.start().then(state => { console.log('[start] success - '); }); } } } }); } if (enabled === false) { BackgroundGeolocation.stop(); } }, [enabled]); useEffect(() => { // third useEffect activated (after user allow permissions) const subscription = AppState.addEventListener('change', nextAppState => { if (nextAppState === 'active') { console.log('[App has come to the foreground!]', { isAllPermissionsFine, }); // Check a specific permission status if (isAllPermissionsFine === false) { BackgroundGeolocation.getProviderState().then(async providerState => { if (providerState.gps) { const user_role = await AsyncStorage.getItem(USER_ROLE); if ( providerState.status == 4 || (providerState.status == 2 && user_role == USER_ROLE_BUYER) ) { setModalVisible(prev => ({ ...prev, isVisible: true, iconName: 'info', message: 'you should select Allow all the time', showLeftButton: true, showRightButton: true, buttonLeftName: 'Cancel', buttonRightName: 'Open Settings', buttonLeftFunction: () => { closeModalFn(); }, buttonRightFunction: () => { setModalVisible(prev => ({...prev, isVisible: false})); openSettings(); }, closeModel: () => { closeModalFn(); }, })); } if (providerState.status == 3) { let checkActivityPhysique = false; let activityPhysique = await Permission.check( 'android.permission.ACTIVITY_RECOGNITION', ); checkActivityPhysique = activityPhysique === 'granted'; if (activityPhysique !== 'granted') { const granted = await PermissionsAndroid.request( PermissionsAndroid.PERMISSIONS.ACTIVITY_RECOGNITION, { title: 'Location Permission', message: 'Market Me needs access to your physical activity', buttonNeutral: 'Ask Me Later', buttonNegative: 'Cancel', buttonPositive: 'OK', }, ); if (granted === PermissionsAndroid.RESULTS.GRANTED) { checkActivityPhysique = true; } } if (checkActivityPhysique === true) { await BackgroundGeolocation.start().then(state => { console.log('[start] success - '); }); dispatch(setIsAllPermissionsFine({value: true})); } } } }); } else { BackgroundGeolocation.getProviderState().then(async providerState => { const activityPhysique = await Permission.check( 'android.permission.ACTIVITY_RECOGNITION', ); if (providerState.status !== 3 || activityPhysique !== 'granted') { dispatch(setIsAllPermissionsFine({value: false})); //await BackgroundGeolocation.stop(); } }); } } setAppState(nextAppState); });

return () => {
  subscription.remove();
};

}, [appState]); React.useEffect(() => { // this useEffect get activated when the service woks fine and distance of tracking reach 20m or 30m or n * 13* 20 m (n> 0) (async () => { const user_role = await AsyncStorage.getItem(USER_ROLE); if (user_role == USER_ROLE_BUYER) { if ( tracking % 13 === 0 || tracking === 3 || tracking === 2 || checkingAfterSearch > 0 ) { const data = { latitude: locationListened.coords?.latitude, longitude: locationListened.coords?.longitude, }; dispatch(update_location(data)).then(res => { dispatch(check_near_sellers()); }); } } })(); }, [tracking, checkingAfterSearch]); } **when the app terminated : index.js file :**import {AppRegistry} from 'react-native'; import App from './App'; import {name as appName} from './app.json'; import messaging from '@react-native-firebase/messaging'; import BackgroundGeolocation from 'react-native-background-geolocation'; import store from '~/Store/index'; import { putLocationListened } from '~/Reducers/PositionReducer'; import { check_near_sellers,update_location } from '~/Actions/nearBySellersActions'; messaging().setBackgroundMessageHandler(async remoteMessage => { console.log('first', remoteMessage); }); AppRegistry.registerComponent(appName, () => App); const headlessTask = async (event) => { if (event.name === 'location') { const location = event.params; console.log('[Headless Task] - Location: '); store.dispatch(putLocationListened({ location: location })); const currentState = store.getState(); const { tracking, locationListened } = currentState.position; console.log("[APP TERMINATED] tracking :" + tracking + " locationListened " + locationListened?.coords.latitude); if (tracking % 10 === 0 || tracking === 3 || tracking === 2) { if (locationListened !== null) { const data = { latitude: locationListened.coords?.latitude, longitude: locationListened.coords?.longitude, }; store.dispatch(update_location(data)).then(() => { store.dispatch(check_near_sellers()); }); } } } }; BackgroundGeolocation.registerHeadlessTask(headlessTask);` i will send to you a file contains the logs whe, app terminated captureLocation

ahmedabartaieb avatar Apr 29 '24 11:04 ahmedabartaieb