Overview

Analysis SDK is part of the Poilabs` Proximity Marketing and Indoor Analytics Solution.

Analysis SDK integrates with an app to scan Poi beacons around. Send these beacons’ information to our signal processing structure. The signals are then forwarded to app owners’ real-time marketing systems. The data includes the mobile users id (unique_id), location (node_id), timestamp, and type of the data (passby, visit and windowshopping).  The app owner can use the location data to send location-based offers and information. Every day all the data is processed and sent to app owners data base.

To integrate and run the applications, the following variables are needed. For Appstore and Google Play submissions please read the related document.

Token Variable NameDescription
APPLICATION_IDApplication ID for Customer`s application, Provided by Poilabs.
APPLICATION_SECRET_KEYAccess Token for Customer`s application, Provided by Poilabs.
UNIQUE_IDUser ID to identify your app user. It must be unique for every app user.
JITPACK_TOKENJitpack token for adding jitpack dependencies. If you have your own token, you can remove it.

iOS Native

iOS Native

PoilabsAnalysis

Version Platform

INSTALLATION

CocoaPods

To integrate PoilabsNavigation into your Xcode project using CocoaPods, specify it in your Podfile:

pod 'PoilabsAnalysis'

Manually

You can add PoilabsAnalysis.xcframework file to your "Frameworks, Libaries, and Embedded Content" in your Project’s General Tab.

PRE-REQUIREMENTS

To Integrate this framework you should add some features to your project info.plist file.

Location Permission

This framework give support both Always and WhenInUse authorization.

  • Privacy - Location Usage Description

  • Privacy - Location When In Use Usage Description

  • Privacy - Location Always Usage Description

  • Privacy - Location Always and When In Use Usage Description

Required Background Modes

You should add "Location updates" and "Uses Bluetooth LE accessories" Background Modes from Project's Signing & Capabilities tab.

USAGE

You should import framework in your AppDelegate

import PoilabsAnalysis

In applicationDidBecomeActive: method you should activate the framework:

        PLAnalysisSettings.sharedInstance().applicationId = APPLICATION_ID
        PLAnalysisSettings.sharedInstance().applicationSecret = APPLICATION_SECRET_KEY
        PLAnalysisSettings.sharedInstance().analysisUniqueIdentifier = UNIQUE_ID
        
         PLConfigManager.sharedInstance().getReadyForTracking(completionHandler: { error in
            if error != nil {
                if let anError = error {
                    print("Error Desc \(anError)")
                }
            } else {
                print("Error Nil")
                PLSuspendedAnalysisManager.sharedInstance()?.stopBeaconMonitoring()
                PLStandardAnalysisManager.sharedInstance()?.startBeaconMonitoring()
                PLStandardAnalysisManager.sharedInstance().delegate = self as? PLAnalysisManagerDelegate
            }
        })

For background tracking:

In didFinishLaunchingWithOptions: method you should activate the framework:

    if launchOptions?[UIApplication.LaunchOptionsKey.location] != nil {
        if application.applicationState == UIApplication.State.background {
            PLSuspendedAnalysisManager.sharedInstance()?.startBeaconMonitoring()
        }
    }

Close All Actions

If you want to close all location services and regions for SDK you can call this method:

PLAnalysisSettings.sharedInstance()?.closeAllActions()

TESTING

You can only test PoilabsAnalysis sdk with real device. You can run on simulator but for testing you should run on a iPhone.

Some test cases are only for versions 3.8.2 or above. For better test cases, please update PoilabsAnalysis if you integrated a lower version.

Initialization

Error of below method should be nil.

PLConfigManager.sharedInstance().getReadyForTracking(completionHandler: { error in

})        

Error descriptions

  1. Request failed: forbidden (403)
    • Please check APPLICATION ID and APPLICATION SECRET KEY
  2. Your Application id is Unavailable
  • Set PLAnalysisSettings.sharedInstance().applicationId
  1. Your Application secret is Unavailable
  • Set PLAnalysisSettings.sharedInstance().applicationSecret
  1. Your Analysis uniqueId is Unavailable
  • Set PLAnalysisSettings.sharedInstance().analysisUniqueIdentifier

Foreground Monitoring

Foreground monitoring means scaning beacon and returning relevant node's id when application is active. If you initilize PoilabsAnalysis sdk with nil error and start beacon monitoring of PLStandartAnalysisManager, node ids will return to callback below.

extension AppDelegate: PLAnalysisManagerDelegate {
    func analysisManagerResponse(forBeaconMonitoring response: [AnyHashable : Any]!) {
        print(response)
    }
}

For getting response, you have to be nearby of a beacon with data which are shared by PoiLabs.

Trigger of this callback can take time, please wait for minumun 30 seconds after start monitoring.

You can see an example of response below.

{
"data": [
    ["nodeid1", "nodeid2", ...],
    ["nodeid1", ...],
    ["nodeid1", ...],
    ...
    ], 
"status": 1
}

If you can get a reponse like this, foreground monitoring is successfully integrated.

Background Monitoring

Background monitoring means scaning beacon when application is killed.

Before start to test please make sure always location permission is given.

To activate background mode, you should kill application and lock the screen. After you show the lock screen or unlock your iPhone, background monitoring will start if you entegrate it, like in the section USAGE/For background tracking.

For getting response, you have to be nearby of a beacon with data which are shared by PoiLabs.

You can test background monitoring on Console. Open Console application on your Mac. Type PLAnalysisSdk to search field. Select your iPhone from Devices section on the left. Press start button.

First you will see start log and then if sdk find any beacon and get its id, you will see response log. Examples of logs are below.

PLAnalysisSdk <PLSuspendedAnalysisManager: 0x...>->SuspendedAnalysisManager startBeaconMonitoring

PLAnalysisSdk <PLSuspendedAnalysisManager: 0x...>->Response {
    data =     ( (  "nodeid1", "nodeid2", ... ),
                ( "nodeid1", ... ),
                ( "nodeid1", ... )
    );
    status = 1;
}

If you get these log, background monitoring is successfully integrated.

Android Native

Android Native

PoilabsAnalysis

Platform Version

PoiLabs Analysis SDK is a data analysis library. It provides data for analysing POI Beacons data.

Installation

You can download our SDK via Gradle with following below steps

  1. Add jitpack dependency to your project level build.gradle file with their tokens.
    JITPACK_TOKEN is a token that PoiLabs will provide for you it will allow you to download our sdk.
    repositories {    
         jcenter()  
maven {            url "https://jitpack.io"   
           credentials { username = 'JITPACK_TOKEN' }    
       }    
} }  
  1. Add PoiLabs Analysis SDK dependency to your app level build.gradle file
 dependencies {    
    implementation 'com.github.poiteam:Android-Analysis-SDK:v3.11.4'    
}

Prerequirements

In order to our SDK can work properly we need location permission and bluetooth usage for scanning for beacons by following below steps you can implement these runtime permissions in your app

  1. Android Manifest file:
    <uses-permission android:name="android.permission.INTERNET" /> 
    <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" /> 
    <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />  
    <uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION" />
    <uses-permission android:name="android.permission.BLUETOOTH_CONNECT" />
    <uses-permission android:name="android.permission.BLUETOOTH_SCAN" />
    <uses-permission android:name="android.permission.FOREGROUND_SERVICE" android:foregroundServiceType="location"/>
  1. Asking for permissions on run time in kotlin language:
   companion object {
   private const val REQUEST_FOREGROUND_LOCATION_REQUEST_CODE = 56
   private const val REQUEST_BACKGROUND_LOCATION_REQUEST_CODE = 57
   private const val REQUEST_COARSE_LOCATION = 58
   private const val REQUEST_BLUETOOTH_PERMISSION = 59
   private const val TAG = "MainActivity"
}


override fun onCreate(savedInstanceState: Bundle?) {
   super.onCreate(savedInstanceState)
   setContentView(R.layout.main)
   askRuntimePermissionsIfNeeded()
}


private fun askRuntimePermissionsIfNeeded() {
   if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { // For Android 10 and above
      val hasFineLocation: Int =
         ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION)
      val hasBackgroundLocation: Int = ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_BACKGROUND_LOCATION)
      if (hasFineLocation != PackageManager.PERMISSION_GRANTED) {
         ActivityCompat.requestPermissions(
            this,
            arrayOf(Manifest.permission.ACCESS_FINE_LOCATION), REQUEST_FOREGROUND_LOCATION_REQUEST_CODE
         )
      }
      if (hasBackgroundLocation != PackageManager.PERMISSION_GRANTED) {
         ActivityCompat.requestPermissions(
            this,
            arrayOf(
               Manifest.permission.ACCESS_BACKGROUND_LOCATION
            ),
            REQUEST_BACKGROUND_LOCATION_REQUEST_CODE
         )
      }

   } else {  // For Android 9 and below
      val hasLocalPermission: Int =
         ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_COARSE_LOCATION)
      if (hasLocalPermission != PackageManager.PERMISSION_GRANTED) {
         ActivityCompat.requestPermissions(
            this,
            arrayOf(Manifest.permission.ACCESS_COARSE_LOCATION),
            REQUEST_COARSE_LOCATION
         )
      }
   }
   if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { // For Android 12 and above
      checkBluetoothPermission()
   }
   startScanIfPermissionsGranted()
}

@RequiresApi(Build.VERSION_CODES.S)
fun checkBluetoothPermission() {
   val hasBluetoothPermission = ActivityCompat.checkSelfPermission(this, Manifest.permission.BLUETOOTH_CONNECT) == PackageManager.PERMISSION_GRANTED
   if (!hasBluetoothPermission) {
      ActivityCompat.requestPermissions(
         this,
         arrayOf(Manifest.permission.BLUETOOTH_CONNECT, Manifest.permission.BLUETOOTH_SCAN),
         REQUEST_BLUETOOTH_PERMISSION
      )
   }
   startScanIfPermissionsGranted()
}

private fun startScanIfPermissionsGranted(): Boolean {
   val hasLocalPermission = ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_COARSE_LOCATION) == PackageManager.PERMISSION_GRANTED
   val hasFineLocation = ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED
   if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { // Android 12 and above
      val hasBackgroundLocation = ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_BACKGROUND_LOCATION) == PackageManager.PERMISSION_GRANTED
      val hasBluetoothPermission = ActivityCompat.checkSelfPermission(this, Manifest.permission.BLUETOOTH_CONNECT) == PackageManager.PERMISSION_GRANTED
      if (hasBluetoothPermission && hasFineLocation && hasBackgroundLocation) {
         startPoiSdk()
         return true
      }
   } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { // Android 10 and above
      val hasBackgroundLocation = ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_BACKGROUND_LOCATION) == PackageManager.PERMISSION_GRANTED
      if (hasFineLocation && hasBackgroundLocation) {
         startPoiSdk()
         return true
      }
   } else if (hasLocalPermission) { // Android 9 and below
      startPoiSdk()
      return true
   }
   return false
}

override fun onRequestPermissionsResult(
   requestCode: Int,
   permissions: Array<out String>,
   grantResults: IntArray
) {
   super.onRequestPermissionsResult(requestCode, permissions, grantResults)
   if (grantResults.isEmpty()) {
      return
   }
   if (grantResults[0] == PackageManager.PERMISSION_GRANTED && !startScanIfPermissionsGranted()) {
      askRuntimePermissionsIfNeeded()
   }
}


private fun startPoiSdk() {
   PoiAnalysis.getInstance().enable()
   PoiAnalysis.getInstance().startScan(applicationContext)
}
  1. Enable multi dex in your android project
    https://developer.android.com/studio/build/multidex

  2. Minimum requirements

Supported Minimum Android
Android 4.3 (API level 18)

Usage

After getting permissions you can now use PoiLabs Analysis SDK.
In this section we will introduce our SDK methods for you

PoiAnalysisConfig

In this class you provide your configuration settings for SDK to work. it's constructer take
"APPLICATION_ID", "APPLICATION_SECRET_KEY", "UNIQUE_ID" and they are mandatory

 val poiAnalysisConfig = PoiAnalysisConfig("APPLICATION_ID", "APPLICATION_SECRET_KEY", "UNIQUE_ID")  

setEnabled(true/false)
Takes a boolen as parameter and with this method you can enable or disable the SDK functionality in your app.

poiAnalysisConfig.setEnabled(ENABLE_SCAN)   

setOpenSystemBluetooth(true/false)
Takes a boolen as parameter and with this method you can enable or disable the auto openning bluetooth SDK funcionality.
Using this method need request BLUETOOTH_CONNECT permission and without this permission, your app will crash on Android 12, Use this method with caution

poiAnalysisConfig.setOpenSystemBluetooth(OPEN_BLUETOOTH_AUTOMATICALLY)   

setForegroundServiceIntent(Intent)
Takes a Intent object as parameter and with this method you should specify which activity should open when user clicks on the forground service notification.

poiAnalysisConfig.setForegroundServiceIntent(new Intent(this, YOUR_CLASS_TO_OPEN_ON_FOREGROUND_SERVICE_CLICK.class))   

enableForegroundService()
If you call this method on the config object you are enabling SDK forground service functionality which will show a foreground service notification when app is in the background to scan for beacons.

poiAnalysisConfig.enableForegroundService()   

setServiceNotificationTitle(String)
Takes a string as parameter and with this method you can specify the text on the foreground service notification.

poiAnalysisConfig.setServiceNotificationTitle(FORE_GROUND_SERVICE_NOTIFICATION_TITLE)   

setForegroundServiceNotificationChannelProperties(String,String)
Takes two strings as parameter and with this method you can specify the foreground service notification channel title and description.

poiAnalysisConfig.setForegroundServiceNotificationChannelProperties(FORE_GROUND_SERVICE_NOTIFICATION_CHANNEL_NAME,FORE_GROUND_SERVICE_NOTIFICATION_CHANNEL_DESCRIPTION)   

setForegroundServiceNotificationIconResourceId(Int)
Takes a drawable resource id as an integer and will show this drawable on foreground service notification small icon.

poiAnalysisConfig.setForegroundServiceNotificationIconResourceId(FORE_GROUND_SERVICE_NOTIFICATION_ICON)  

PoiAnalysis

With this singleton class you can access to SDK functions. On first access you should provide your app Context and an instance of PoiAnalysisConfig.
This First Access to this class should happen in your application class onCreate function otherwise SDK will not work properly.

PoiAnalysis.getInstance(this, config);   

Once you called getInstance with these parameters you can call it without any parameter in your further usages.
For get callbacks from SDK you should implement PoiResponseCallback interface.

PoiResponseCallback
it has two methods:

  1. onResponse(nodeId: String)
    on this call back you can see the node id of detected beacon

  2. onFail(exception: Exception)
    on this callback you can see the exception if any error has happened inside SDK.

After setting SDK up in your application class you can set this listener inside any class of your applicaiton

 PoiAnalysis.getInstance().setPoiResponseListener(object : PoiResponseCallback { override fun onResponse(nodeIds: List<String>?) { TODO("Not yet implemented") }  
 override fun onFail(cause: Exception?) { TODO("Not yet implemented") }  
 })   

Starting or Stopping SDK

As described in previous section after setting up in application class you can call the singleton class and start or stop SDK inside any class of your application.

Disclaimer: THE ANALYSIS SDK WILL BE WORK ONLY IF YOU START SCAN

 PoiAnalysis.getInstance().enable(); PoiAnalysis.getInstance().startScan(getApplicationContext()); PoiAnalysis.getInstance().stopScan();  

Update Unique Id

In case that your application has some user authentication after login, you should update the Unique Id in the SDK for getting a better classification, With the code snippet shown below you can achieve this.

PoiAnalysis.getInstance().updateUniqueId(NEW_UNIQUE_ID)  

This method takes a string as parameter and after calling this method all beacon data will be stored with new unique id that you provided.

Proguard Rules

-printmapping out.map  
-keepparameternames  
-renamesourcefileattribute SourceFile  
-keepattributes Exceptions,InnerClasses,Signature,Deprecated,SourceFile,LineNumberTable,EnclosingMethod  
  
# Preserve all annotations.  
  
-keepattributes *Annotation*  
  
# Preserve all public classes, and their public and protected fields and  
# methods.  
  
-keep public class * {  
 public protected *;}  
  
# Preserve all .class method names.  
  
-keepclassmembernames class * {  
 java.lang.Class class$(java.lang.String); java.lang.Class class$(java.lang.String, boolean);}  
  
# Preserve all native method names and the names of their classes.  
  
-keepclasseswithmembernames class * {  
 native <methods>;}  
  
# Preserve the special static methods that are required in all enumeration  
# classes.  
  
-keepclassmembers class * extends java.lang.Enum {  
 public static **[] values(); public static ** valueOf(java.lang.String);}  
  
# Explicitly preserve all serialization members. The Serializable interface  
# is only a marker interface, so it wouldn't save them.  
# You can comment this out if your library doesn't use serialization.  
# If your code contains serializable classes that have to be backward  
# compatible, please refer to the manual.  
  
-keepclassmembers class * implements java.io.Serializable {  
 static final long serialVersionUID; static final java.io.ObjectStreamField[] serialPersistentFields; private void writeObject(java.io.ObjectOutputStream); private void readObject(java.io.ObjectInputStream); java.lang.Object writeReplace(); java.lang.Object readResolve();}  
  
# Your library may contain more items that need to be preserved;  
# typically classes that are dynamically created using Class.forName:  
  
-keep public class getpoi.com.poibeaconsdk.PoiAnalysis  
-keep public interface getpoi.com.poibeaconsdk.models.BeaconScanCallback  
-keep public interface getpoi.com.poibeaconsdk.models.PoiResponseCallback  
  
  
-keep class getpoi.com.poibeaconsdk.PoiScanner* { *; }  
-keep class getpoi.com.poibeaconsdk.Models** { *; }  
-keep class getpoi.com.poibeaconsdk.PoiAnalysis { *; }  
-keep class getpoi.com.poibeaconsdk.models.** { *; }  
-keep class getpoi.com.poibeaconsdk.models.PoiAnalysisConfig { *; }  
  
  
  
-dontwarn  getpoi.com.poibeaconsdk.**  
-dontwarn   com.poilabs.poiutil.**  

Frequently Asked Questions (F.A.Q)

Why am I getting failed to resolve error in Gradle?

There could could be a number of reasons so we need to find the cause of this error. The first thing to check is if the jitpack token is correctly placed in your project.

To get more details run Gradle from command line with --info --refresh-dependencies flags. The output should contain a line “HTTP Could not get” with the full URL and HTTP Status Code.

Possible reasons:

  • 404 - File not found. Build failed, tag does not exist or there is no such file in build artifacts.
  • 401 - Unauthorized. No token was supplied.
  • 403 - Forbidden. The token doesn’t have access to the Git repository.

If the error is unclear, feel free to contact Support.

the most common error code is 401 and it is because probably the jitpack token is not provided correctly. to check how to insert jitpack token click here.

Will my app crash if user rejects permissions?

Actually NO. Basically if you enable Analytics SDK in your application class if necessary permissions is not granted it basically freezes itself and The SDK will start working again in the next session of the application once the user grants the required permissions.

Which permissions do app need for this SDK?

Basically to let the SDK work properly you need to get these permissions from user:

  • Location Permission (Precise location)
  • Background Location Permission
  • BLUETOOTH_SCAN Permission
  • BLUETOOTH_CONNECT Permission ( If you want to auto enable bluetooth)
  • Push notification Permission ( If you want to enable foreground scan)

Note that it does not effect SDK in anyway that how you get these permissions. The samples in documentation are just samples. Feel free to get permissions however you want

React Native

React Native Implementation

PoilabsAnalysis React Native Integration

iOS

INSTALLATION

To integrate PoilabsAnalysis into your Xcode project using CocoaPods, specify it in your Podfile.

pod 'PoilabsAnalysis'

PRE-REQUIREMENTS

To Integrate this framework you should add some features to your project info.plist file.

Privacy - Location Usage Description

Privacy - Location When In Use Usage Description

Privacy - Location Always Usage Description

Privacy - Location Always and When In Use Usage Description

USAGE

You should create a header file called PoilabsAnalysisModule.h and a Objective-C file called PoilabsAnalysisModule.m with content below.

PoilabsAnalysisModule.h

#ifndef PoilabsAnalysisModule_h
#define PoilabsAnalysisModule_h

#import "PoilabsAnalysis/PoilabsAnalysis.h"
#import <React/RCTBridgeModule.h>
@interface PoilabsAnalysisModule : NSObject <RCTBridgeModule, PLAnalysisManagerDelegate>
@end

#endif /* PoilabsAnalysisModule_h */

PoilabsAnalysisModule.m

#import <Foundation/Foundation.h>
#import "PoilabsAnalysisModule.h"

@implementation PoilabsAnalysisModule

RCT_EXPORT_MODULE(PoilabsAnalysisModule);

RCT_EXPORT_METHOD(stopPoilabsAnalysis) {
  [[PLAnalysisSettings sharedInstance] closeAllActions];
}

RCT_EXPORT_METHOD(startPoilabsAnalysis:(NSString *)applicationId applicationSecret:(NSString *) secret uniqueIdentifier:(NSString *) uniqueId) {
  [[PLAnalysisSettings sharedInstance] setApplicationId:applicationId];
  [[PLAnalysisSettings sharedInstance] setApplicationSecret:secret];
  [[PLAnalysisSettings sharedInstance] setAnalysisUniqueIdentifier:uniqueId];
  [[PLConfigManager sharedInstance] getReadyForTrackingWithCompletionHandler:^(PLError *error) {
      if (error) {
          NSLog(@"Error Desc %@",error.errorDescription);
      }
      else
      {
          [[PLSuspendedAnalysisManager sharedInstance] stopBeaconMonitoring];
          [[PLStandardAnalysisManager sharedInstance] startBeaconMonitoring];
          [[PLStandardAnalysisManager sharedInstance] setDelegate:self];
      }
  }];
}

-(void)analysisManagerDidFailWithPoiError:(PLError *)error
{
    NSLog(@"Error Desc %@",error);
}

-(void)analysisManagerResponseForBeaconMonitoring:(NSDictionary *)response
{
    NSLog(@"Response %@",response);
}

@end

AppDelegate.m

#import "PoilabsAnalysis/PoilabsAnalysis.h"

To start suspended mode that allows track location when application is killed, you should call method below in didFinishLaunchingWithOptions:

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
.....
  if ([launchOptions objectForKey:UIApplicationLaunchOptionsLocationKey] && [UIApplication sharedApplication].applicationState == UIApplicationStateBackground) {
      [[PLSuspendedAnalysisManager sharedInstance] startBeaconMonitoring];
  }
  .......
  }

Android

INSTALLATION

You can download our SDK via Gradle with following below steps

  1. Add jitpack dependency to your project level build.gradle file with their tokens.
    JITPACK_TOKEN is a token that PoiLabs will provide for you it will allow you to download our sdk.
allprojects {    
    repositories {    
        maven {            
            url "https://jitpack.io"   
            credentials { username = 'JITPACK_TOKEN' }    
        }    
    } 
 }  
  1. Add PoiLabs Analysis SDK dependency to your app level build.gradle file
 dependencies {    
    implementation 'com.github.poiteam:Android-Analysis-SDK:v3.11.4'    
}
  1. In order to our SDK can work properly we need location permission and bluetooth usage for scanning for beacons by following below steps you can implement these runtime permissions in your app

Android Manifest file

<uses-permission android:name="android.permission.BLUETOOTH" /> 
     <uses-permission android:name="android.permission.INTERNET" /> 
     <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" /> 
     <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />  
     <uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION" />
     <uses-permission android:name="android.permission.BLUETOOTH_CONNECT" />
     <uses-permission android:name="android.permission.BLUETOOTH_SCAN" />
     <uses-permission android:name="android.permission.FOREGROUND_SERVICE" android:foregroundServiceType="location"/>

USAGE

Add these methods to your MainActivity.kt file

MainActivity

private var requestPermissionLauncher: ActivityResultLauncher<String> =
   registerForActivityResult(ActivityResultContracts.RequestPermission()) { isGranted ->
      handleNextPermission()
   }

fun setPermissionLaunchers() {
   requestPermissionLauncher.launch(Manifest.permission.ACCESS_FINE_LOCATION)
}

fun handleNextPermission() {
    when {
        Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q && !isPermissionGranted(Manifest.permission.ACCESS_BACKGROUND_LOCATION) -> {
            requestPermissionLauncher.launch(Manifest.permission.ACCESS_BACKGROUND_LOCATION)
        }

        Build.VERSION.SDK_INT >= Build.VERSION_CODES.S && !isPermissionGranted(Manifest.permission.BLUETOOTH_SCAN) -> {
            requestPermissionLauncher.launch(Manifest.permission.BLUETOOTH_SCAN)
        }

        Build.VERSION.SDK_INT >= Build.VERSION_CODES.S && !isPermissionGranted(Manifest.permission.BLUETOOTH_CONNECT) -> {
            requestPermissionLauncher.launch(Manifest.permission.BLUETOOTH_CONNECT)
        }
        else -> {
            startPoiSdk()
        }
    }
}

fun isPermissionGranted(permission: String): Boolean {
    return ActivityCompat.checkSelfPermission(this, permission) == PackageManager.PERMISSION_GRANTED
}

fun startPoiSdk() {
    PoiAnalysis.getInstance().enable()
    PoiAnalysis.getInstance().startScan(applicationContext)
}

Imported packages into MainActivity:

import android.content.pm.PackageManager
import android.os.Build
import androidx.core.app.ActivityCompat
import com.facebook.react.ReactActivity
import com.facebook.react.ReactActivityDelegate
import com.facebook.react.defaults.DefaultNewArchitectureEntryPoint.fabricEnabled
import com.facebook.react.defaults.DefaultReactActivityDelegate
import getpoi.com.poibeaconsdk.PoiAnalysis
import android.Manifest
import androidx.activity.result.ActivityResultLauncher
import androidx.activity.result.contract.ActivityResultContracts

Create a Kotlin filed called PoilabsAnalysisModule with content below

import android.content.Intent
import android.util.Log
import com.facebook.react.bridge.ReactApplicationContext
import com.facebook.react.bridge.ReactContextBaseJavaModule
import com.facebook.react.bridge.ReactMethod
import getpoi.com.poibeaconsdk.PoiAnalysis
import getpoi.com.poibeaconsdk.models.PoiAnalysisConfig
import getpoi.com.poibeaconsdk.models.PoiResponseCallback

class PoilabsAnalysisModule  internal constructor(context: ReactApplicationContext?) :
ReactContextBaseJavaModule(context) {
   override fun getName(): String {
       return "PoilabsAnalysisModule"
   }
   @ReactMethod
   fun startPoilabsAnalysis(applicationId: String, applicationSecret: String, uniqueIdentifier: String) {
       currentActivity?.let { activity ->
           val poiAnalysisConfig = PoiAnalysisConfig(applicationId, applicationSecret, uniqueIdentifier)
           PoiAnalysis.getInstance(activity.applicationContext, poiAnalysisConfig)

           poiAnalysisConfig.setForegroundServiceIntent(Intent(activity.applicationContext, MainActivity::class.java))
           poiAnalysisConfig.setServiceNotificationTitle("Test1")
           poiAnalysisConfig.setForegroundServiceNotificationChannelProperties("name1" , "desc1")
           poiAnalysisConfig.enableForegroundService()

           PoiAnalysis.getInstance().setPoiResponseListener(object : PoiResponseCallback {
               override fun onResponse(nodeIds: List<String>?) {
                   
               }

               override fun onFail(cause: Exception?) {
                   
               }
           })
           (activity as MainActivity).setPermissionLaunchers()
       }
   }

   @ReactMethod
   fun stopPoilabsAnalysis() {
       PoiAnalysis.getInstance().stopScan()
   }
}

Create a Kotlin file called PoilabsPackage with content below

import com.facebook.react.ReactPackage
import com.facebook.react.bridge.NativeModule
import com.facebook.react.bridge.ReactApplicationContext
import com.facebook.react.uimanager.ViewManager

class PoilabsPackage : ReactPackage {
    override fun createNativeModules(reactApplicationContext: ReactApplicationContext): List<NativeModule> {
        val modules: MutableList<NativeModule> = ArrayList()
        modules.add(PoilabsAnalysisModule(reactApplicationContext))
        return modules
    }
    override fun createViewManagers(reactApplicationContext: ReactApplicationContext): List<ViewManager<*, *>> {
        return emptyList();
    }
}

Add PoilabsPackage to getPackages method of MainApplication

    override fun getPackages(): List<ReactPackage> =
        PackageList(this).packages.apply {
            add(PoilabsPackage())
        }

React Native

You should import NativeModules

import {
  NativeModules,
} from 'react-native';

You can start PoilabsAnalysis with calling bridge method

NativeModules.PoilabsAnalysisModule.startPoilabsAnalysis(APPLICATION_ID, APPLICATION_SECRET, UNIQUE_ID);

You can stop PoilabsAnalysis with calling bridge method

NativeModules.PoilabsAnalysisModule.stopPoilabsAnalysis;

Important Information on Store Submission

For AppStore (iOS)

You have to explain why you need the collect location. Sample info is provided below.

XXXXX is a retailer with more than 30 stores across Turkey. The mobile application is designed to inform users about promotions and also provide shopping list creation when they are not in the store. By using ibeacon technology we are planning to create a better customer experience in the store, show them related offers specific to that place, send surveys about their visits, and also gamification features to motivate users to visit our stores.

Sometime AppStore requires additional information and answers to questions. You can find the additional answers; 

How frequently is the users’ location collected?

The app uses a beacon region monitoring feature. The app searches for beacons that have our UUID and if there is no such beacon the location is not collected. We use beacons inside our stores across Turkey. Location collection is limited in those places. The location is generally collected every 30 seconds in the store. When the user leaves the store the location collection is stopped since the location collection depends on the beacons inside the stores.

Can the users see their location in the app?

Yes, they can see their location as a bluedot inside the store. They can get navigation to products or categories.

Can the users see the locations they have been in the app?

Since the app only collects locations in-store and in-mall the user does not see his/her locations.

Does this app track and show users’ distance traveled in the app?

No.

What are the specific persistent background location features that cannot be attained by significant-change location service or the region monitoring location service?

The user’s location is only collected when they are in the beacon range and the beacons are installed in our stores and in some malls. Since we use beacons we must use persistent background location otherwise users can not get in-store notifications about the stores, offers, welcome messages, and surveys. We will also introduce limited-time offers when a user enters the stores.

Additionally, what features in the app require persistent location updates while the app is in the background? Please provide us with detailed steps to locate this feature within your app. The app uses ibeacon signals to provide location-based offers inside the stores. Users can not locate the feature in the app since the user only sees the nearby offers and also get notification related to their location. But they can use the indoor navigation feature to see their location inside the store.

For Google Play Store (Android)

Firstly you need to prepare additional notifications while you are asking the location permission. Then you need the take a screen recording showing all the requirements.

 You can also check Google Play guide: https://support.google.com/googleplay/android-developer/answer/9799150?hl=en#zippy=

Sample Video:

XXXXX is a retailer with more than 30 stores across Turkey. The mobile application is designed to inform users about promotions and also provide shopping list creation when they are not in the store. By using ibeacon technology we are planning to create a better customer experience in the store, show them related offers specific to that place, send surveys about their visits and also gamification features to motivate users to visit our supermarkets

In the application, the following feature requires background location and it is crucial for the users.

  • – Real time location based offers. When a user pass by a store inside , they can learn the offers and get promotions in real time.
  • – Indoor navigation inside the store. The users can get navigation to products or categories.
  • – The user can create a shopping list and get a optimum route to collect the items.

Privacy Policy must include why you use the location and must be updated.

Sample information that should be added to the privacy policy.

“Genel veya size özel kişiselleştirilmiş kampanyalar, avantajlar, promosyonlar, reklamların oluşturulması ve sağlanması, segmentasyon ve pazarlama analiz çalışmalarının yapılması, lokasyon paylaşımına yapılacak tercihe göre (her zaman kullanım/uygulama açıkken kullanım/tek seferlik kullanım) izin verilmesi halinde tercihinize göre lokasyon bilgisi, bluetooth özelliğinin açık olması halinde beacon (yer belirleyici) cihazları aracılığıyla tespit edilen lokasyon bilgisi, lokasyon bilgisi kaydedilerek oluşturulacak teklifler için bilgileriniz kullanılmaktadır. “

Resources and Useful Links

  1. iOS Native Example App
  2. Android Native Example App
  3. React Native Example App

Skip to content