Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Geolocation, service and local notifications

I'm trying to make my first system application for Android related to geolocation and local notifications. I imagine it like this... There is basic activity MainActivity. After start it launches a service TestService which in case of change of coordinates sends them on the server, and in reply receives some message which will displayed as the local notification. And I have some problems.

  1. If I close the application (using task manager) then the service will stop, so after change of coordinates nothing happens. What I need do that service work all the time? Or it's impossible?

  2. After activation of the local notification it launches NotifyActivity which shows detailed information. There I click buttonDelete - it will close NotifyActivity and start MainActivity. But if after that I switch to the OS screen (using Back button) and back (using task manager) then instead of 'MainActivity' will be again displayed 'NotifyActivity'. Why it occurs and how to avoid it?

MainActivity

[Activity(Label = "LocationTest", MainLauncher = true, Icon = "@drawable/icon")]
public class MainActivity : Activity
{
    protected override void OnCreate(Bundle bundle)
    {
        base.OnCreate(bundle);
        SetContentView(Resource.Layout.Main);

        var button = FindViewById<Button>(Resource.Id.myButton);
        button.Click += delegate {
            StartService(new Intent(this, typeof(TestService)));
            button.Text = "Started";
        };
    }
}

Geolocation service

[Service]
public class TestService : Service, ILocationListener
{
    // Start service
    public override StartCommandResult OnStartCommand(Intent intent, StartCommandFlags flags, int startId)
    {
        locManager = (LocationManager)GetSystemService(LocationService);
        locationCriteria = new Criteria();

        locationCriteria.Accuracy = Accuracy.Coarse;
        locationCriteria.PowerRequirement = Power.Low;

        string locationProvider = locManager.GetBestProvider(locationCriteria, true);

        // Preferences.MinTime, for example, 60 (seconds)
        // Preferences.MinDist, for example, 100 (meters)
        locManager.RequestLocationUpdates(locationProvider, Preferences.MinTime * 1000, Preferences.MinDist, this);

        return StartCommandResult.Sticky;
    }

    public void OnLocationChanged(Location loc)
    {
        // Send coordinates to the server, receive a response, and show local notification
        var msg = new ReceivedMessage(counter++, "Test Title", loc.ToString());
        ShowNotification(msg);
    }

    // show local notification
    void ShowNotification(ReceivedMessage msg)
    {
        var myContainer = new Bundle();
        myContainer.PutLong("msg_id", Convert.ToInt64(msg.Id));
        myContainer.PutStringArray("msg_data", new [] { msg.Title, msg.Text });
        var resultIntent = new Intent(this, typeof(NotifyActivity));
        resultIntent.PutExtras(myContainer);

        TaskStackBuilder stackBuilder = TaskStackBuilder.Create(this);
        stackBuilder.AddParentStack(Java.Lang.Class.FromType(typeof(NotifyActivity)));
        stackBuilder.AddNextIntent(resultIntent);

        PendingIntent resultPendingIntent = stackBuilder.GetPendingIntent(Convert.ToInt32(msg.Id), PendingIntentFlags.UpdateCurrent);

        Notification.Builder builder = new Notification.Builder(this)
            .SetDefaults(NotificationDefaults.Sound | NotificationDefaults.Vibrate)
            .SetAutoCancel(true)
            .SetContentIntent(resultPendingIntent)
            .SetContentTitle(msg.Title)
            .SetContentText(msg.Text)
            .SetSmallIcon(Resource.Drawable.Icon);

        var nm = (NotificationManager)GetSystemService(NotificationService);
        nm.Notify(Convert.ToInt32(msg.Id), builder.Build());
    }

}

local notifications

[Activity(Label = "NotifyActivity")]            
public class NotifyActivity : Activity
{
    protected override void OnCreate(Bundle savedInstanceState)
    {
        base.OnCreate(savedInstanceState);

        SetContentView(Resource.Layout.NotifyActivity);

        var msg_id = Intent.Extras.GetLong("msg_id");
        var msg_data = Intent.Extras.GetStringArray("msg_data");

        FindViewById<TextView>(Resource.Id.textTitle).Text = msg_data[0];
        FindViewById<TextView>(Resource.Id.textDescription).Text = msg_data[1];

        FindViewById<Button>(Resource.Id.buttonDelete).Click += delegate {
            StartActivity(typeof(MainActivity));
            Finish();
        };
    }
}

Example project here

like image 990
Dmi7ry Avatar asked May 19 '15 14:05

Dmi7ry


People also ask

What is difference between push notification and local notification?

The essential difference between local notifications and push notifications is simple: Local notifications are scheduled by an app locally and are delivered by the same device. Push notifications are sent by a remote server (its provider) which sends these notifications to devices on which the app is installed.

What is local notification in Android?

Local notifications ... to inform users when new data becomes available for your app, even when your app is not running in the foreground. For example, a messaging app might let the user know when a new message has arrived, and a calendar app might inform the user of an upcoming appointment.

What are geo notifications?

When you enable geo-notifications the app will send you a notification when you leave home without arming the alarm or when you arrive home and the alarm is armed.

What is local notification in IOS?

Local notifications reach users whether your app is running in the foreground or the background and require no external infrastructure to send, which is why they are aptly termed “local.” These notifications simply require that a user's device is on in order to be received.


2 Answers

In MainActivity add this code. This method will solve your second problem (bus still I don't know how to solve issue with application title in the list of recent apps).

public override bool OnKeyDown (Keycode keyCode, KeyEvent e)
{
    if (keyCode == Keycode.Back) {
        MoveTaskToBack(true);
        return true;
    }
    return base.OnKeyDown (keyCode, e);
}

Beeing hones activities in Android have a lot of pitfalls. Hence my suggestion is to launch MainActivity from service and then launch NotifyAcitivity. Then you can launch MainActivity again (in this case you'll need to set LaunchMode = LaunchMode.SingleTop for MainActivity).

For the first problem - have you tried defining [Service (Process = ":remote")] instead of [Service]?

like image 83
Artur Shamsutdinov Avatar answered Oct 04 '22 23:10

Artur Shamsutdinov


I don't have experience with xamarin, but I can try to answer the questions as they relate generally to Android.

Question 1

When you close your app through the task manager, you are doing more than simply closing the app - you are killing the entire process. Although you can't control your users' behavior, generally this is not the way that an Android user is supposed to stop an app. The "correct" way to close an app is to simply back out of it, which does not destroy the process, and keeps your Service running in the background.

So to answer your question, I don't think there's anything you can do to get around this situation where a user forcefully kills your app's process. However, I don't think you need to worry about this edge case because under normal circumstances, your app's process won't be killed until the system deems it's ok or absolutely needs it. So the service should continue running if the app is closed in a normal way. The docs say:

the system will keep the service running as long as either it is started or there are one or more connections to it with the Context.BIND_AUTO_CREATE flag. Once neither of these situations hold, the service's onDestroy() method is called and the service is effectively terminated.

So your service will be OK. Generally, your app is not expected to handle cases where its process is forcefully killed.

More information about Processes

More information about the Service lifecycle

Question 2

I am not exactly sure what's going on here. However, looking at your linked project, I notice that your AndroidManifest.xml file is barely filled out. My suspicion is that without this file, the android system doesn't know how to initialize your backstack, and it doesn't know which of your activities is the MAIN and LAUNCHER activity.

You should declare your activities in AndroidManifest.xml, and in it you should specify that MainActivity is your MAIN and LAUNCHER activity, and that it is a parent of NotifyActivity. This way the system will know to open MainActivity when the app is started, and it'll know to return to it when you hit "back" from NotifyActivity. Something like this:

<application android:label="LocationTest" android:icon="@drawable/icon">
    <activity android:name="MainActivity">
      <intent-filter>
          <action android:name="android.intent.action.MAIN" />
          <category android:name="android.intent.category.LAUNCHER" />
      </intent-filter> 
    </activity>

    <activity android:name="NotifyActivity" 
        android:parentActivityName="MainActivity"/>
</application>

More information about how to use your Android Manifest

Hopefully that helps you solve your problems!

like image 29
yuval Avatar answered Oct 05 '22 01:10

yuval