0

In my xamarin android app, AltBeacon DidEnterRegion not triggered when MainLauncher attribute set to false for MainActivity. As I have SplashActivity for showing splash screen, I have to make the SplashActivity as my MainLauncher. I am using foreground service to detect iBeacon.

Code of my Application class:

 namespace AltBeaconLibrarySample.Droid
 {
 #if DEBUG
     [Application(Debuggable = true)]
 #else
     [Application(Debuggable = false)]
 #endif
     public class MainApplication : Application, IBootstrapNotifier
     {
         private RegionBootstrap regionBootstrap;
         private BackgroundPowerSaver backgroundPowerSaver;
         Region _generalRegion;
         public bool IsStartedRanging { get; set; }
         string foregroundServiceChannelId = "foregroundService";
         string channelName = "ForegroundService";
         int pendingIntentId = 1;

         public MainApplication(IntPtr handle, JniHandleOwnership transer)
           : base(handle, transer)
         {
         }

         public void DidDetermineStateForRegion(int p0, Region p1)
         {
         }

         public void DidEnterRegion(Region p0)
         {
             var beaconService = Xamarin.Forms.DependencyService.Get<IAltBeaconService>();
             if (!IsStartedRanging)
             {
                 beaconService.StartRanging();
                 IsStartedRanging = true;
             }
         }

         public void DidExitRegion(Region p0)
         {
             var beaconService = Xamarin.Forms.DependencyService.Get<IAltBeaconService>();
             if (IsStartedRanging)
             {
                 beaconService.StopRanging();
                 IsStartedRanging = false;
             }
             beaconService.DidExitRegion();
         }

         public override void OnCreate()
         {
             base.OnCreate();
             CrossCurrentActivity.Current.Init(this);

             BeaconManager bm = BeaconManager.GetInstanceForApplication(this);
             CreateNotificationChannel();
             bm.EnableForegroundServiceScanning(GetForegroundServiceNotification(), 456);
             bm.SetEnableScheduledScanJobs(false);

             _generalRegion = new Org.Altbeacon.Beacon.Region/* AltBeaconOrg.BoundBeacon.Region*/("myEmptyBeaconId", Identifier.Parse("23A01AF0-232A-4518-9C0E-323FB773F5EF"), null, null);
             regionBootstrap = new RegionBootstrap(this, _generalRegion);

             backgroundPowerSaver = new BackgroundPowerSaver(this);
         }

         void CreateNotificationChannel()
         {
             if (Build.VERSION.SdkInt < BuildVersionCodes.O)
             {
                 // Notification channels are new in API 26 (and not a part of the
                 // support library). There is no need to create a notification
                 // channel on older versions of Android.
                 return;
             }
             var channelDescription = "Foreground Sertrvice";
             var channel = new NotificationChannel(foregroundServiceChannelId, channelName, NotificationImportance.High)
             {
                 Description = channelDescription
             };

             var notificationManager = (NotificationManager)GetSystemService(NotificationService);
             notificationManager.CreateNotificationChannel(channel);
         }

         public Notification GetForegroundServiceNotification()
         {
             NotificationCompat.Builder builder = new NotificationCompat.Builder(this, foregroundServiceChannelId);
             builder.SetSmallIcon(Resource.Drawable.xamagonBlue);
             builder.SetContentTitle("Scanning for Beacons");
             Intent intent = new Intent(this, typeof(MainActivity));
             PendingIntent pendingIntent = PendingIntent.GetActivity(this, pendingIntentId, intent, PendingIntentFlags.UpdateCurrent);
             builder.SetContentIntent(pendingIntent);

             return builder.Build();
         }
     }
 }

Code of SplashActivity class:

namespace AltBeaconLibrarySample.Droid
{
    [Activity(Label = "AltBeaconLibrarySample.Droid",
        Icon = "@mipmap/icon",
        Theme = "@style/MainTheme",
        MainLauncher = true,
        NoHistory = true,
        LaunchMode = Android.Content.PM.LaunchMode.SingleInstance,
        ConfigurationChanges = ConfigChanges.ScreenSize | ConfigChanges.Orientation)]

    public class SplashActivity : Activity
    {
        static readonly string TAG = "X:" + typeof(SplashActivity).Name;

        public override void OnCreate(Bundle savedInstanceState, PersistableBundle persistentState)
        {
            base.OnCreate(savedInstanceState, persistentState);
        }

        // Launches the startup task
        protected override void OnResume()
        {
            base.OnResume();
            Task startupWork = new Task(() => { SimulateStartup(); });
            startupWork.Start();
        }

        // Simulates background work that happens behind the splash screen
        async void SimulateStartup()
        {
            Log.Debug(TAG, "Performing some startup work that takes a bit of time.");
            await Task.Delay(50); // Simulate a bit of startup work.
            Log.Debug(TAG, "Startup work is finished - starting MainActivity.");
            StartActivity(new Intent(Application.Context, typeof(MainActivity)));
        }
    }
}

Android manifest:

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android" android:versionCode="1" android:versionName="1.0" package="com.companyname.AltBeaconLibrarySample" android:installLocation="auto">
    <uses-sdk android:minSdkVersion="21" android:targetSdkVersion="28" />
    <uses-permission android:name="android.permission.VIBRATE" />
    <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
    <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
    <uses-permission android:name="android.permission.BLUETOOTH" />
    <uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
    <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
    <uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
    <application android:label="AltBeaconLibrarySample.Android">
        <service android:enabled="true" android:exported="false" android:isolatedProcess="false" android:label="Beacon" android:name="org.altbeacon.beacon.service.BeaconService"></service>
        <service android:enabled="true" android:exported="false" android:name="org.altbeacon.beacon.BeaconIntentProcessor"></service>
        <receiver android:name="org.altbeacon.beacon.startup.StartupBroadcastReceiver">
            <intent-filter>
                <action android:name="android.intent.action.BOOT_COMPLETED" />
                <action android:name="android.intent.action.ACTION_POWER_CONNECTED" />
                <action android:name="android.intent.action.ACTION_POWER_DISCONNECTED" />
            </intent-filter>
        </receiver>
    </application>
</manifest>

Code of MainActivity class:

namespace AltBeaconLibrarySample.Droid
{
    [Activity(Label = "AltBeaconLibrarySample.Droid", 
              Icon = "@mipmap/icon", 
              Theme = "@style/MainTheme",
              MainLauncher = false, 
              LaunchMode = Android.Content.PM.LaunchMode.SingleInstance,
              ConfigurationChanges = ConfigChanges.ScreenSize | ConfigChanges.Orientation)]
    public class MainActivity : global::Xamarin.Forms.Platform.Android.FormsAppCompatActivity, IBeaconConsumer
    {
        protected override void OnCreate(Bundle savedInstanceState)
        {
            TabLayoutResource = Resource.Layout.Tabbar;
            ToolbarResource = Resource.Layout.Toolbar;

            base.OnCreate(savedInstanceState);

            Xamarin.Essentials.Platform.Init(this, savedInstanceState); // add this line to your code, it may also be called: bundle
            CrossCurrentActivity.Current.Init(this, savedInstanceState);

            global::Xamarin.Forms.Forms.Init(this, savedInstanceState);

            LoadApplication(new App());
        }

        public override void OnRequestPermissionsResult(int requestCode, string[] permissions, [GeneratedEnum] Android.Content.PM.Permission[] grantResults)
        {
            Xamarin.Essentials.Platform.OnRequestPermissionsResult(requestCode, permissions, grantResults);
            PermissionsImplementation.Current.OnRequestPermissionsResult(requestCode, permissions, grantResults);

            base.OnRequestPermissionsResult(requestCode, permissions, grantResults);
        }

        #region IBeaconConsumer Implementation
        public void OnBeaconServiceConnect()
        {
        }
        #endregion
        protected override void OnDestroy()
        {
            base.OnDestroy();
            DependencyService.Get<IAltBeaconService>().OnDestroy();
        }

        protected override void OnResume()
        {
            base.OnResume();
        }

        protected override void OnPause()
        {
            base.OnPause();
        }
    }
}

BeaconManager code snippet:

BeaconManager bm = BeaconManager.GetInstanceForApplication(Plugin.CurrentActivity.CrossCurrentActivity.Current.Activity);

var iBeaconParser = new BeaconParser();
//  Estimote > 2013
iBeaconParser.SetBeaconLayout("m:2-3=0215,i:4-19,i:20-21,i:22-23,p:24-24");
bm.BeaconParsers.Add(iBeaconParser);

bm.BackgroundMode = false;
bm.Bind((IBeaconConsumer)Plugin.CurrentActivity.CrossCurrentActivity.Current.Activity);
Aminur Rashid
  • 741
  • 6
  • 13
  • Please show your code – davidgyoung Mar 18 '20 at 11:03
  • I have modified the question and added code of my Application class. – Aminur Rashid Mar 18 '20 at 12:00
  • When the SplashActivity is the launcher activity, does the foreground service start? Can you please show your manifest with the SplashActivity enabled? – davidgyoung Mar 19 '20 at 11:53
  • Yes, the foreground service starts when the SplashActivity is the launcher activity. Codes for manifest and splashactivity is now added to the question. – Aminur Rashid Mar 22 '20 at 11:38
  • @davidgyoung, is this behavior normal when SplashActivity is the main launcher? – Aminur Rashid Apr 06 '20 at 10:33
  • If you have a foreground service, its notification should come up when the app first launches, regardless of what is the launcher activity. – davidgyoung Apr 06 '20 at 16:07
  • Thanks for your reply. But it's not working when the IBeaconConsumer activity (in my case MainActivity) is not launcher activity. At least not in xamarin android. I can't find any example online on which an activity implements IBeaconConsumer and not a launcher activity. – Aminur Rashid Apr 06 '20 at 17:32
  • Can you please show the code of your MainActivity? if that is the code that isn't working, then that is the most important code to show. – davidgyoung Apr 06 '20 at 19:20
  • Code of MainActivity added. – Aminur Rashid Apr 07 '20 at 02:59
  • Why do you declare MainActivity as implementing IBeaconConsumer? (The code doesn't do anything with the interface's implemented methods.) If you remove this, does it start working? – davidgyoung Apr 07 '20 at 16:00
  • As beacon manager needs IBeaconConsumer type to bind: BeaconManager.Bind((IBeaconConsumer)Plugin.CurrentActivity.CrossCurrentActivity.Current.Activity), I need an activity which implements IBeaconConsumer. I've added the code snippet of creation and binding of beacon manager. – Aminur Rashid Apr 07 '20 at 16:24
  • Since you are using the Xamarin wrapper for the Android Beacon Library, I recommend you leave all your beacon code in your custom application class. Do not use an iBeaconConsumer in an Activity. If you need to communicate beacon detections from the Application class to your activity, consider doing so with local broadcasts as described here: https://stackoverflow.com/questions/8802157/how-to-use-localbroadcastmanager – davidgyoung Apr 07 '20 at 18:17

0 Answers0