1

My question already in the title , and also after 5-7 min the didExit and didEnter called together because there after this time the didExit will be called then the didEnter called too, how can I check this, (i know my English is so bad) ,please find my code below

note : i removed the imports becoause there is limitation

------------------------UPDATED-----------------------------

MainActivity.class


public class MainActivity extends AppCompatActivity {

    private AppBarConfiguration mAppBarConfiguration;
    protected static final String TAG = "MonitoringActivity";
    private static final int PERMISSION_REQUEST_FINE_LOCATION = 1;
    private static final int PERMISSION_REQUEST_BACKGROUND_LOCATION = 2;
    Intent mServiceIntent;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        Toolbar toolbar = findViewById(R.id.toolbar);
        setSupportActionBar(toolbar);
        FloatingActionButton fab = findViewById(R.id.fab);
        fab.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                Snackbar.make(view, "Replace with your own action", Snackbar.LENGTH_LONG)
                        .setAction("Action", null).show();
            }
        });
        DrawerLayout drawer = findViewById(R.id.drawer_layout);
        NavigationView navigationView = findViewById(R.id.nav_view);
        // Passing each menu ID as a set of Ids because each
        // menu should be considered as top level destinations.
        mAppBarConfiguration = new AppBarConfiguration.Builder(
                R.id.nav_home, R.id.nav_profile, R.id.nav_slideshow)
                .setDrawerLayout(drawer)
                .build();
        NavController navController = Navigation.findNavController(this, R.id.nav_host_fragment);
        NavigationUI.setupActionBarWithNavController(this, navController, mAppBarConfiguration);
        NavigationUI.setupWithNavController(navigationView, navController);


        verifyBluetooth();


        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
            if (this.checkSelfPermission(Manifest.permission.ACCESS_FINE_LOCATION)
                    == PackageManager.PERMISSION_GRANTED) {
                if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
                    if (this.checkSelfPermission(Manifest.permission.ACCESS_BACKGROUND_LOCATION)
                            != PackageManager.PERMISSION_GRANTED) {
                        if (!this.shouldShowRequestPermissionRationale(Manifest.permission.ACCESS_BACKGROUND_LOCATION)) {
                            final AlertDialog.Builder builder = new AlertDialog.Builder(this);
                            builder.setTitle("This app needs background location access");
                            builder.setMessage("Please grant location access so this app can detect beacons in the background.");
                            builder.setPositiveButton(android.R.string.ok, null);
                            builder.setOnDismissListener(new DialogInterface.OnDismissListener() {

                                @TargetApi(23)
                                @Override
                                public void onDismiss(DialogInterface dialog) {
                                    requestPermissions(new String[]{Manifest.permission.ACCESS_BACKGROUND_LOCATION},
                                            PERMISSION_REQUEST_BACKGROUND_LOCATION);
                                }

                            });
                            builder.show();
                        }
                        else {
                            final AlertDialog.Builder builder = new AlertDialog.Builder(this);
                            builder.setTitle("Functionality limited");
                            builder.setMessage("Since background location access has not been granted, this app will not be able to discover beacons in the background.  Please go to Settings -> Applications -> Permissions and grant background location access to this app.");
                            builder.setPositiveButton(android.R.string.ok, null);
                            builder.setOnDismissListener(new DialogInterface.OnDismissListener() {

                                @Override
                                public void onDismiss(DialogInterface dialog) {
                                }

                            });
                            builder.show();
                        }
                    }
                }
            } else {
                if (!this.shouldShowRequestPermissionRationale(Manifest.permission.ACCESS_FINE_LOCATION)) {
                    requestPermissions(new String[]{Manifest.permission.ACCESS_FINE_LOCATION,
                                    Manifest.permission.ACCESS_BACKGROUND_LOCATION},
                            PERMISSION_REQUEST_FINE_LOCATION);
                }
                else {
                    final AlertDialog.Builder builder = new AlertDialog.Builder(this);
                    builder.setTitle("Functionality limited");
                    builder.setMessage("Since location access has not been granted, this app will not be able to discover beacons.  Please go to Settings -> Applications -> Permissions and grant location access to this app.");
                    builder.setPositiveButton(android.R.string.ok, null);
                    builder.setOnDismissListener(new DialogInterface.OnDismissListener() {

                        @Override
                        public void onDismiss(DialogInterface dialog) {
                        }

                    });
                    builder.show();
                }

            }
        }

        String URL = "";
        try {
            InputStream is = this.getAssets().open("config.properties");
            Properties props = new Properties();
            props.load(is);
            URL = props.getProperty("userModule");
            is.close();
        } catch (Exception e) {
        }
        Retrofit adapter = new Retrofit.Builder()
                .baseUrl(URL)
                .addConverterFactory(GsonConverterFactory.create())
                .build();
        APIService api = adapter.create(APIService.class);
        SharedPreferences userDetails = this.getSharedPreferences("userDetails", 0);
        Call<Map<String,Object>> call = api.loadUserByToken(userDetails.getString("accessToken", "").toString());
        View headerView = navigationView.getHeaderView(0);
        call.enqueue(new Callback<Map<String,Object>>() {
            @Override
            public void onResponse(Call<Map<String,Object>> call, Response<Map<String,Object>> response) {
                Map<String,Object> result = response.body();
                Map<String,Object> allData = (Map<String, Object>) result.get("result");
                Log.d("result",allData.toString());
                if(allData.get("ErrorResutl") != null){
                    SharedPreferences userDetails = getSharedPreferences("userDetails", 0);
                    SharedPreferences.Editor editor = userDetails.edit();
                    editor.putString("accessToken", null);
                    editor.commit();
                    Intent intent = new Intent(MainActivity.this, LoginActivity.class);
                    intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_CLEAR_TASK | Intent.FLAG_ACTIVITY_NEW_TASK);
                    startActivity(intent);
                    finish();
                }
                SharedPreferences.Editor editor = userDetails.edit();
                editor.putString("data", allData.toString() );
                editor.commit();
                TextView username = (TextView) headerView.findViewById(R.id.username);
                TextView email = (TextView) headerView.findViewById(R.id.email);
                username.setText(allData.get("firstName").toString() + " " + allData.get("lastName").toString());
                email.setText(allData.get("email").toString());
            }
            @Override
            public void onFailure(Call<Map<String,Object>> call, Throwable t) {
                Snackbar.make(findViewById(android.R.id.content), t.toString(), Snackbar.LENGTH_LONG)
                        .setAction("Action", null).show();
            }
        });

        Intent intent = new Intent(this, BeaconActivity.class);
        startActivity(intent);
        // Important:  make sure to add android:launchMode="singleInstance" in the manifest
        // to keep multiple copies of this activity from getting created if the user has
        // already manually launched the app.
    }


    private void verifyBluetooth() {

        try {
            if (!BeaconManager.getInstanceForApplication(this).checkAvailability()) {
                final AlertDialog.Builder builder = new AlertDialog.Builder(this);

                startActivityForResult(new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE),1);
            }
        } catch (RuntimeException e) {
            final AlertDialog.Builder builder = new AlertDialog.Builder(this);
            builder.setTitle("Bluetooth LE not available");
            builder.setMessage("Sorry, this device does not support Bluetooth LE.");
            builder.setPositiveButton(android.R.string.ok, null);
            builder.setOnDismissListener(new DialogInterface.OnDismissListener() {

                @Override
                public void onDismiss(DialogInterface dialog) {
                    finish();
                    System.exit(0);
                }

            });
            builder.show();

        }

    }
    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        super.onActivityResult(requestCode, resultCode, data);
        if (requestCode == 1) {
            if (resultCode != RESULT_OK) {
                verifyBluetooth();
            }
        }
    }
    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        // Inflate the menu; this adds items to the action bar if it is present.
        getMenuInflater().inflate(R.menu.main, menu);
        return true;
    }

    @Override
    public boolean onSupportNavigateUp() {
        NavController navController = Navigation.findNavController(this, R.id.nav_host_fragment);
        return NavigationUI.navigateUp(navController, mAppBarConfiguration)
                || super.onSupportNavigateUp();
    }


}


BeaconActivity.class


public class BeaconActivity extends Activity implements  BeaconConsumer  {
    protected static final String TAG = "RangingActivity";
    private BeaconManager beaconManager = BeaconManager.getInstanceForApplication(this);

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        Log.d("naji","beaconactivity");
            beaconManager.bind(this);
        super.onBackPressed();

    }

    public BeaconManager getBeaconManager() {
        return beaconManager;
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        beaconManager.unbind(this);
    }

    @Override
    protected void onPause() {
        super.onPause();
        beaconManager.unbind(this);
    }

    @Override
    protected void onResume() {
        super.onResume();
        beaconManager.bind(this);
    }


    @Override
    public void onBeaconServiceConnect() {

        RangeNotifier rangeNotifier = new RangeNotifier() {
            @Override
            public void didRangeBeaconsInRegion(Collection<Beacon> beacons, Region region) {
                if (beacons.size() > 0) {
                    Log.d(TAG, "didRangeBeaconsInRegion called with beacon count:  "+beacons.size());
                    for (Beacon beacon: beacons) {
                        Log.d("naji","The beacon " + beacon.toString() + " is about " + beacon.getDistance() + " meters away."+ beacon.getServiceUuid());
                        Collection<Region> monitoredRegions = beaconManager.getMonitoredRegions();
                        boolean duplicate = false;
                        for (Region monitoredRegion :monitoredRegions) {
                            if(monitoredRegion.getId1() != null && monitoredRegion.getId1().equals(beacon.getId1())){
                                duplicate = true;
                                break;
                            }
                        }
                        if(!duplicate && beacon.getId1() != null){
                            Log.d(TAG, "duplicate:  False ");
                            try {
                                beaconManager.startMonitoringBeaconsInRegion(new Region(beacon.getId1().toString(),beacon.getId1(),beacon.getId2(),beacon.getId3()));
                            } catch (RemoteException e) {
                                e.printStackTrace();
                            }
                        } else {
                            Log.d(TAG, "duplicate:  True ");
                        }

                    }
                }
            }
        };
        try {
            beaconManager.startRangingBeaconsInRegion(new Region("myRangingUniqueId", null, null, null));
            beaconManager.addRangeNotifier(rangeNotifier);
        } catch (RemoteException e) {
            e.printStackTrace();
        }
    }
}

BeaconApplication.calss



public class BeaconApplication extends Application implements BootstrapNotifier {
    private BeaconActivity beaconActivity;
    private static final String TAG = "BeaconReferenceApp";
    private RegionBootstrap regionBootstrap;
    private BackgroundPowerSaver backgroundPowerSaver;
    private boolean haveDetectedBeaconsSinceBoot = false;
    private String cumulativeLog = "";
    BeaconManager beaconManager ;
    public void onCreate() {
        super.onCreate();
        beaconManager = org.altbeacon.beacon.BeaconManager.getInstanceForApplication(this);
        beaconManager.setDebug(false);
        beaconManager.setEnableScheduledScanJobs(false);
        beaconManager.setBackgroundBetweenScanPeriod(0);
        beaconManager.setBackgroundScanPeriod(1100);
        beaconManager.setForegroundBetweenScanPeriod(0);
        beaconManager.setForegroundScanPeriod(1100);
        beaconManager.getBeaconParsers().add(new BeaconParser().
                setBeaconLayout("m:2-3=0215,i:4-19,i:20-21,i:22-23,p:24-24"));
        Notification.Builder builder = new Notification.Builder(this);
        builder.setSmallIcon(R.drawable.ic_launcher_background);
        builder.setContentTitle("Scanning for Beacons");
        beaconManager.enableForegroundServiceScanning(builder.getNotification(),0);
        Log.d(TAG, "setting up background monitoring for beacons and power saving");
        Region region = new Region("myRangingUniqueId",
            null, null, null);
        regionBootstrap = new RegionBootstrap(this, region);
        backgroundPowerSaver = new BackgroundPowerSaver(this);

        // OneSignal Initialization
        OneSignal.startInit(this)
                .inFocusDisplaying(OneSignal.OSInFocusDisplayOption.Notification)
                .unsubscribeWhenNotificationsAreDisabled(true)
                .setNotificationOpenedHandler(new ExampleNotificationOpenedHandler())
                .setNotificationReceivedHandler(new ExampleNotificationReceivedHandler())
                .autoPromptLocation(true)
                .init();

        OSPermissionSubscriptionState status = OneSignal.getPermissionSubscriptionState();
        status.getPermissionStatus().getEnabled();
        status.getSubscriptionStatus().getSubscribed();
        status.getSubscriptionStatus().getUserSubscriptionSetting();
        status.getSubscriptionStatus().getUserId();
        status.getSubscriptionStatus().getPushToken();
        Log.d(TAG, "status.getSubscriptionStatus().getUserId(); --->"+status.getSubscriptionStatus().getUserId());
        SharedPreferences userDetails = getSharedPreferences("userDetails", 0);
        SharedPreferences.Editor editor = userDetails.edit();
        editor.putString("player_id", status.getSubscriptionStatus().getUserId());
        editor.commit();


    }

    private class ExampleNotificationOpenedHandler implements OneSignal.NotificationOpenedHandler {
        private ExampleNotificationOpenedHandler() {
        }

        public void notificationOpened(OSNotificationOpenResult result) {
            Log.i("OneSignalExample", result.toString());
            OSNotificationAction.ActionType actionType = result.action.type;
            JSONObject data = result.notification.payload.additionalData;
            if (data != null) {
                String customKey = data.optString("customkey", null);
                if (customKey != null) {
                    Log.i("OneSignalExample", "customkey set with value: " + customKey);
                }
            }
            if (actionType == OSNotificationAction.ActionType.ActionTaken) {
                Log.i("OneSignalExample", "Button pressed with id: " + result.action.actionID);
            }
        }
    }

    private class ExampleNotificationReceivedHandler implements OneSignal.NotificationReceivedHandler {
        private ExampleNotificationReceivedHandler() {
        }

        @Override
        public void notificationReceived(OSNotification notification) {
            Log.i("OneSignalExample", notification.payload.additionalData.toString());
//             BeaconManager beaconManager = BeaconManager.getInstanceForApplication(beaconActivity);
//             beaconManager.bind(beaconActivity);

        }
    }

    @Override
    public void didEnterRegion(Region arg0) {
        if(arg0.getId1() != null) {
            pushNotification(arg0,"enter");
        }
//        Intent intent = new Intent(this, MainActivity.class);
//        intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
//        this.startActivity(intent);
    }

    @Override
    public void didExitRegion(Region region) {
        if(region.getId1() != null) {
            pushNotification(region, "exit");
        }
    }

    @Override
    public void didDetermineStateForRegion(int state, Region region) {
        Log.d(TAG,"Current region state is: " + (state == 1 ? "INSIDE" : "OUTSIDE ("+state+")"));



        RangeNotifier rangeNotifier = new RangeNotifier() {
            @Override
            public void didRangeBeaconsInRegion(Collection<Beacon> beacons, Region region) {
                if (beacons.size() > 0) {
                    Log.d(TAG, "didRangeBeaconsInRegion called with beacon count:  "+beacons.size());
                    for (Beacon beacon: beacons) {
                        Log.d("naji","The beacon " + beacon.toString() + " is about " + beacon.getDistance() + " meters away."+ beacon.getServiceUuid());
                        Collection<Region> monitoredRegions = beaconManager.getMonitoredRegions();
                        boolean duplicate = false;
                        for (Region monitoredRegion :monitoredRegions) {
                            if(monitoredRegion.getId1() != null && monitoredRegion.getId1().equals(beacon.getId1())){
                                duplicate = true;
                                break;
                            }
                        }
                        if(!duplicate && beacon.getId1() != null){
                            Log.d(TAG, "duplicate:  False ");
                            try {
                                beaconManager.startMonitoringBeaconsInRegion(new Region(beacon.getId1().toString(),beacon.getId1(),beacon.getId2(),beacon.getId3()));
                            } catch (RemoteException e) {
                                e.printStackTrace();
                            }
                        } else {
                            Log.d(TAG, "duplicate:  True ");
                        }

                    }
                }
            }
        };
        try {
            beaconManager.startRangingBeaconsInRegion(new Region("myRangingUniqueId", null, null, null));
            beaconManager.addRangeNotifier(rangeNotifier);
        } catch (RemoteException e) {
            e.printStackTrace();
        }
    }


    private void pushNotification(Region region,String action){
        String URL = "";
        try {
            InputStream is = BeaconApplication.this.getAssets().open("config.properties");
            Properties props = new Properties();
            props.load(is);
            URL = props.getProperty("hospitalModule");
            is.close();
        } catch (Exception e) {
        }
        Retrofit adapter = new Retrofit.Builder()
                .baseUrl(URL)
                .addConverterFactory(GsonConverterFactory.create())
                .build();
        //Creating an object of our api interface
        APIService api = adapter.create(APIService.class);
        //Defining the method
        SharedPreferences userDetails = getSharedPreferences("userDetails", 0);
        Map<String, Object> object = new LinkedHashMap<>();
        List<String> player_id = new ArrayList<>();
        player_id.add("\""+userDetails.getString("player_id", "")+"\"");
        object.put("type", action);
        object.put("player_id", player_id);
        if(region.getId1() == null){
            object.put("uuid", "no uuid");
        } else {
            object.put("uuid", region.getId1().toString());
        }
        Call<Map<String, Object>> call = api.pushNotification(userDetails.getString("accessToken", ""), object);
        call.enqueue(new Callback<Map<String, Object>>() {
            @Override
            public void onResponse(Call<Map<String, Object>> call, Response<Map<String, Object>> response) {
                Map<String, Object> result = response.body();
                Log.d("result", result.toString());
                Map<String, Object> allData = (Map<String, Object>) result.get("result");
                if (allData.get("ErrorResutl") != null) {
                    SharedPreferences userDetails = getSharedPreferences("userDetails", 0);
                    SharedPreferences.Editor editor = userDetails.edit();
                    editor.putString("accessToken", null);
                    editor.commit();
                    Intent intent = new Intent(BeaconApplication.this, LoginActivity.class);
                    intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_CLEAR_TASK | Intent.FLAG_ACTIVITY_NEW_TASK);
                    startActivity(intent);
                }
            }

            @Override
            public void onFailure(Call<Map<String, Object>> call, Throwable t) {
            }
        });
    }
}

AndroidManifiets.xml


<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    package="com.isleem.hospital">
    <uses-permission android:name="android.permission.INTERNET" />
    <uses-permission android:name="android.permission.BLUETOOTH"/>
    <uses-permission android:name="android.permission.BLUETOOTH_ADMIN"/>
    <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
    <uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION" />
    <uses-permission android:name="android.permission.FOREGROUND_SERVICE"/>
    <uses-permission android:name="android.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS" />
    <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
    <uses-permission android:name="android.permission.WAKE_LOCK" />
    <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
    <uses-permission android:name="android.permission.ALLOW_BACKGROUND_LOCATION" />

<!--android:allowBackup="true" in beacon activity android:launchMode="singleTop"-->
    <application
        android:name=".application.BeaconApplication"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/AppTheme.NoActionBar">
        <service android:name="org.altbeacon.beacon.service.BeaconService" tools:node="replace">
            <meta-data android:name="longScanForcingEnabled" android:value="true"/>
        </service>
        <meta-data android:name="com.onesignal.NotificationOpened.DEFAULT" android:value="DISABLE" />
        <activity android:name=".activities.MainActivity" />
        <activity android:name=".activities.BeaconActivity"   android:theme="@style/noAnimTheme"/>
        <activity
            android:name=".activities.LoginActivity"
            android:label="@string/app_name"
            android:launchMode="singleInstance"
            android:theme="@style/AppTheme.NoActionBar">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>

    </application>
</manifest>

2 Answers2

1

The code shown is probably not compatible with background restrictions on Android 8+. In particular, a custom service called BeaconService is created that tries to launch the BeaconActivity every 60 seconds. I do not believe that will prove reliable -- the operating system will kill the activity when it is not visible, and it is unclear if attempts to re-create it will work. Even if the work in the short-term, they probably won't work in the long-term.

I suggest you simplify the approach and leave all beacon code in the Application class. (You can move logic to POJO utility classes as needed to keep things clean, but I would not add any new services to deal with beacon detections. The built-in foreground service provided by the library should be good enough.) You can start beacon ranging in the didDetermineStateForRegion callback in the Application class, which will prevent you from having to bind/unbind to the BeaconManager and implement BeaconConsumer instances.

Using the above simplified approach, if you need to communicate beacon detections to the BeaconActivity, you can do so with a LocalBroadcastManager. See here for how you might do that.

davidgyoung
  • 63,876
  • 14
  • 121
  • 204
  • Ok i will do this and get back to you , Thanks – Naje isleem Apr 07 '20 at 22:06
  • I made the update on my code, but still, the same issue, need 5-8 min to detect then dead after 15-30 min. any idea to make detection never stop and detecting every second? – Naje isleem Apr 08 '20 at 10:54
  • You might want to try starting with the library's reference application, and modifying it to (a) use a foreground service by uncommenting the code in the Application class (b) comment out the user of BackgroundPowerSaver and (c) starting ranging inside `didDetermineStateForRegion`. I can confirm this approach works to range every second indefinitely. – davidgyoung Apr 08 '20 at 17:39
  • i do as you said but its same , because im saved the beacon details in SharedPreferences but i didnt see any recorde inside it when i kill the app , is there any other thing can i do to make sure i really detect the beacon ? – Naje isleem Apr 08 '20 at 20:46
  • Please note the timing restrictions and caveats mentioned in the documentation here: https://altbeacon.github.io/android-beacon-library/resume-after-terminate.html Make sure your testing accounts for these. You can use the `adb logcat` command to look for logs after your app is terminated, and you should see logs from your app when it resumes. – davidgyoung Apr 09 '20 at 18:58
  • if i remove the phone from USB i could see logs after connect it ? i dont think so and why i could not call API when see beacon when app killed ? this is main thing i must do in my app ( i now im asking you alot of questions but really i must do this app) – Naje isleem Apr 09 '20 at 21:00
  • Finally it's working fine, I didn't change anything in code but I change the settings in my mobile (battery --> App launch --> turn off manage automatic but keep all options on inside it) – Naje isleem Apr 10 '20 at 11:53
0

Finally, it's working fine, I didn't change anything in code but I change the settings in my mobile (battery --> App launch --> turn off manage automatic but keep all options on inside it) but if anyone can make it programmatically please let me know.