traccar / traccar-client-android

Traccar Client for Android
https://www.traccar.org/client
Apache License 2.0
659 stars 727 forks source link

Status Widget #429

Open Anton-V-K opened 2 years ago

Anton-V-K commented 2 years ago

It would be handy to have a widget which could show the application status - whether its service running or not. When the widget is clicked, the main window may be opened.

Is your feature request related to a problem? Please describe. On some smartphones with too restrictive built-in task managers (Oukitel Y4800 with Android 9 is a typical example), the application can be closed (killed) by the system despite all optimization settings (ignoring battery optimization, white-listing, etc.).

Describe the solution you'd like A possible solution (a kind of workaround) to keep the app alive is to add an updatable widget (to the home screen), which will let system know that the app shouldn't be killed (for power optimization reasons).

Additional context Refer to the forum thread for more details.

Anton-V-K commented 2 years ago

@tananaev , I had some free time to look into this feature implementation, and it doesn't seem to be sophisticated. I've managed to generate new widget in Android Studio, and with minor code changes it works quite good. The main concern is the place where I can update widget's state when the service is started/stopped. I've added one line to onSharedPreferenceChanged (in MainFragment):

    override fun onSharedPreferenceChanged(sharedPreferences: SharedPreferences, key: String) {
        if (key == KEY_STATUS) {
            if (sharedPreferences.getBoolean(KEY_STATUS, false)) {
                startTrackingService(checkPermission = true, initialPermission = false)
            } else {
                stopTrackingService()
            }
            (requireActivity().application as MainApplication).handleRatingFlow(requireActivity())
            StatusWidget.updateWidgets(requireContext()) // <<<
        } else if (key == KEY_DEVICE) {
            findPreference<Preference>(KEY_DEVICE)?.summary = sharedPreferences.getString(KEY_DEVICE, null)
        }
    }

Is this the right place?

I'll test my build on the device where the system kills the application to check whether the new status widget solves the problem.

tananaev commented 2 years ago

Probably makes sense to update it somewhere inside startTrackingService.

Anton-V-K commented 2 years ago

OK. Same update should also be performed from stopTrackingService. I'm ready to commit my changes. I'll create a new branch statius_widget, so my changes will be isolated until they are ready for merging into master. The widget really solves the problem with keeping app alive, so the feature may be useful for others as well.

Anton-V-K commented 2 years ago

while testing my build I've noticed that the widget isn't updated if the service started/stoped via the shortcut. Probably the update should also be performed from ShortcutActivity.executeAction ? I was also thinking about adding service-related broadcasts to the widget's intents filter, so the widget could be decoupled from the rest:

        <receiver
            android:name=".StatusWidget"
            android:label="@string/status_widget_label"
            android:exported="true">
            <intent-filter>
                <action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
                <action android:name="org.traccar.action.SERVICE_STARTED" />
                <action android:name="org.traccar.action.SERVICE_STOPPED" />
            </intent-filter>
            <meta-data
                android:name="android.appwidget.provider"
                android:resource="@xml/status_widget_info" />
        </receiver>

Unfortunately this approach doesn't seem to work on modern Androids (8.0+), probably because of Broadcast Limitations.

Anton-V-K commented 2 years ago

I've managed to register for service-related intents in the code of StatusWidget:

    override fun onEnabled(context: Context) {
        // Enter relevant functionality for when the first widget is created
        val filter = IntentFilter()
        filter.addAction(TrackingService.ACTION_STARTED)
        filter.addAction(TrackingService.ACTION_STOPPED)
        // Note: we must register through the app context as a workaround
        // for 'BroadcastReceiver components are not allowed to register to receive intents'
        context.applicationContext.registerReceiver(Companion, filter)
    }
Anton-V-K commented 2 years ago

If anyone is willing to test the functionality until it is officially available, here is the test build TraccarClient-app-regular-debug-81.apk.zip It has different package name and some icons are recolored, so the app can be installed and used side-by-side with the original one (the settings aren't shared between them as well). This build had been under test on Android 9 (Oukitel Y4800) for a month, and I can confirm the app keeps running while you have the status widget on a home screen.

buhao75 commented 2 years ago

I have tested the widget and it seems to work. It would be cool if this feature would be available in the main release. I would suggest two optimizations:

  1. The status widget icon is quite similar to the Widget-Buttons for starting / stopping the service.
  2. When clicking on the status widget the app should open