How can I prevent my .NET Wearable app from going to sleep

I am developing a Tizen .NET app for the Samsung Gear S3 (Tizen v4.0). I want my Wearable app (NOT a watchface) to always stay alive and never be put to sleep until the user exits the application.

I have made a small PowerManager that wraps the native dll:

    [DllImport("libcapi-system-device.so.0", EntryPoint = "device_power_request_lock", CallingConvention = CallingConvention.Cdecl)]
    internal static extern int DevicePowerRequestLock(int type, int timeout_ms);

    [DllImport("libcapi-system-device.so.0", EntryPoint = "device_power_release_lock", CallingConvention = CallingConvention.Cdecl)]
    internal static extern int DevicePowerReleaseLock(int type);

    enum Power_Type { CPU = 0, DISPLAY = 1, DISPLAY_DIM = 2 };

Here is what works:

If I call:

    DevicePowerRequestLock((int)Power_Type.DISPLAY, 0);

Then my app does indeed stay focused but the display is always on. For now, I am managing myself what to show and I am manually coding the equivalent of the Ambient mode with timers. This is not ideal in terms of battery consumption.

Here is what does NOT work:

If I call:

     DevicePowerRequestLock((int)Power_Type.CPU, 0);

or even if I call the Tizen .NET 4.0 call:

    Tizen.System.Power.RequestCpuLock(0);

This does absolutely nothing. The screen goes out after the screen timeout (which is what I want) but if I wait long enough before I wake the screen up, the app is sleeping and I am back to my watchface.

So it seems to me the CPU lock is not working.

Furthermore, is there a way to know in a wearable app when Ambient Mode has been turned on to selectively display certain UI elements much like in a watchface?

Sorry if these are newbie questions but I cannot find the answer for this anywhere and I have a feeling I am not the only one with this problem.

Thanks!

Hi,

try to find solution in this topic on Tizen forum:

I hope it will be helpful.

Frankie

Yes, I have the same problem. I want to prevent the app to “go on background” after some time. Currently after 20 seconds or 5 minutes (can be set by user in the device) the app is hidden and the watch face is shown.

I want to keep the app always in front, even after the display was turned off. So user can just do a wrist gesture and the app will be shown (not the watch face).

I also tried the Power.RequestCpuLock(0); function, and the Power_Type.CPU enum, but it does not work (on Galaxy Watch Active). The Power_Type.DISPLAY enum works always (when I call it every time in App.OnResume), but this keeps the display always turned on, it consumes the battery.

I found this F# code that is using the Display class, not the Power. Maybe this could be used? But I was not able to make it work for this use case: https://github.com/swift-kim/FSharpSamples/blob/master/XStopWatch.FSharp/XStopWatch/WindowExtension.fs

Note that I have these items inserted in the manifest:

<privilege>http://tizen.org/privilege/power</privilege>
<privilege>http://tizen.org/privilege/display</privilege>

And also the background category is set, so the app is not stopped when on background. But still it does not stay displayed like the documentation says.

<background-category value="location" />

Did you solve the problem? Try to add:

<privilege>http://tizen.org/privilege/appmanager.launch</privilege>

to your manifest file.

I’ve tried to keep vibrating watch when app go on background and theselines fixxed me the problem:

<background-category value="media" />

<privilege>http://tizen.org/privilege/appmanager.launch</privilege> <privilege>http://tizen.org/privilege/haptic</privilege> <privilege>http://tizen.org/privilege/display</privilege>

Thanks Frankie!

Yes, I think I have solved it. I use these permissions in the manifest:
<feature name="http://tizen.org/feature/location.gps">true</feature>
<privilege>http://tizen.org/privilege/power</privilege>
<privilege>http://tizen.org/privilege/display</privilege>
<privilege>http://tizen.org/privilege/appmanager.launch</privilege>

The app stays running on background. Just on Gear S3 I encountered that the app still can be killed, if the battery is less than 30%? But on Galaxy Active it seems it’s running on background even with a low battery.

For the second part - keeping the app always in front, I am monitoring the Display.StateChanged event. If the display was turned on, I call the AppControl.SendLaunchRequest with the App ID of this app. Seems that it’s working (so when user looks on his watch and the display lights up, the app is sent to front). Then I can request the DevicePowerRequestLock for example for 30 seconds and the display stays active, even if it was set differently in the device settings.

Hi everyone,

Thanks all for the discussion and solution. I have tried your recommendations and it does do what I want it to do.

I have to say that I am slightly disappointed with the behavior. Since the solution is to re-launch the app when the display is turned on, there is a slight delay where you actually see the Watchface and then you see your app being woken up. Also, when doing many times, it will happen that the app will simply not get launched and stay on the Watchface (although I have yet to confirm that it is not a problem in my code). Like I said, this works, but it is not the most responsive solution.

I am probably asking for too much here, but it would have been ideal to be able to have a solution that mimics the exact behavior of the Samsung Health app. When I am using it and I am running for example, as soon as I wake up the display, the app is visible and running in record time. That, in my view, is the best user experience for this type of activity.

I wish Samsung would provide clear guidelines to make “Always On” applications for real-time tracking of sports and activities.

Thanks again!

In my experience, the app is still running on background, it does not get closed. You can see if you display a second screen in the app, then go to home screen and select the app icon again, the second screen is still displayed. Also if you write some info into the log file, you can see that the app was running.

But you are right, sometimes the Display.StateChanged does not get called (the turned on display is not detected), or maybe the SendLaunchRequest is not called? Sometimes (in 1 of 10 cases) the app is not sent to front. I still need to research it. But I hope there will be some trick (for example I already saw the LaunchRejectedException in the analytics, this may be it).

Yes, it’s hard to make a reliable app and users are angry if it does not work 100% :slight_smile: But I also understand that this is a watch, very limited platform with a very small battery. We need to be very careful what we do regarding multitasking.

Hi mai1588157995,

I agree with all of your points and finding the reason why the SendLaunchRequest is not always called may be a step forward.

Still, I maintain that it is possible to have a rock solid solution for this since the Samsung Health app doe sit perfectly.

You have the .NET API, native API, web API, sometimes even an undocummented API :slight_smile: The native Samsung Health app can use different approaches. If you have a partner certificate from Samsung, you can also probably get access to more features. But I hope this should be possible even from the normal .NET API. I will let you know if I find something.

Just to summarize, it works now. When user looks on the watch, the app is moved to foreground. It’s always activated.

Previously I had some logic in App.OnResume, and it seems this method was sometimes called before the Display.StateChanged and sometimes after. This is all what we need, in App.xaml.cs constructor:

Display.StateChanged += Display_StateChanged;

Then add this method:

private void Display_StateChanged(object sender, DisplayStateChangedEventArgs e)
{
    if (e.State == DisplayState.Normal && _lastDisplayState == DisplayState.Off)
        SendLaunchRequest();

    _lastDisplayState = e.State;
}

And implement the SendLaunchRequest method:

private void SendLaunchRequest()
{
    try
    {
        AppControl.SendLaunchRequest(
            new AppControl
            {
                ApplicationId = Constants.AppID,
                LaunchMode = AppControlLaunchMode.Single,
            }, (launchRequest, replyRequest, result) => { });
    }
    catch (LaunchRejectedException) { Logger.Log("Launch rejected"); }
    catch (Exception ex) { Logger.Log(ex); }
}