diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index 30cd194d..af3ab818 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -202,7 +202,7 @@
android:value="org.kde.kdeconnect.UserInterface.MainActivity" />
diff --git a/src/org/kde/kdeconnect/Plugins/RunCommandPlugin/RunCommandPlugin.java b/src/org/kde/kdeconnect/Plugins/RunCommandPlugin/RunCommandPlugin.java
index 86d25f33..e10ed648 100644
--- a/src/org/kde/kdeconnect/Plugins/RunCommandPlugin/RunCommandPlugin.java
+++ b/src/org/kde/kdeconnect/Plugins/RunCommandPlugin/RunCommandPlugin.java
@@ -7,6 +7,8 @@
package org.kde.kdeconnect.Plugins.RunCommandPlugin;
+import static org.kde.kdeconnect.Plugins.RunCommandPlugin.RunCommandWidgetProviderKt.forceRefreshWidgets;
+
import android.app.Activity;
import android.content.Context;
import android.content.Intent;
@@ -129,8 +131,7 @@ public class RunCommandPlugin extends Plugin {
sharedPreferences.edit().putString(KEY_COMMANDS_PREFERENCE + device.getDeviceId(), array.toString()).apply();
}
- Intent updateWidget = new Intent(context, RunCommandWidget.class);
- context.sendBroadcast(updateWidget);
+ forceRefreshWidgets(context);
} catch (JSONException e) {
Log.e("RunCommand", "Error parsing JSON", e);
diff --git a/src/org/kde/kdeconnect/Plugins/RunCommandPlugin/RunCommandWidget.java b/src/org/kde/kdeconnect/Plugins/RunCommandPlugin/RunCommandWidget.java
deleted file mode 100644
index 0536e1f5..00000000
--- a/src/org/kde/kdeconnect/Plugins/RunCommandPlugin/RunCommandWidget.java
+++ /dev/null
@@ -1,149 +0,0 @@
-package org.kde.kdeconnect.Plugins.RunCommandPlugin;
-
-import android.app.PendingIntent;
-import android.appwidget.AppWidgetManager;
-import android.appwidget.AppWidgetProvider;
-import android.content.ComponentName;
-import android.content.Context;
-import android.content.Intent;
-import android.net.Uri;
-import android.text.TextUtils;
-import android.util.Log;
-import android.view.View;
-import android.widget.RemoteViews;
-
-import org.kde.kdeconnect.Device;
-import org.kde.kdeconnect.KdeConnect;
-import org.kde.kdeconnect_tp.R;
-
-import java.util.concurrent.ConcurrentHashMap;
-
-public class RunCommandWidget extends AppWidgetProvider {
-
- public static final String RUN_COMMAND_ACTION = "RUN_COMMAND_ACTION";
- public static final String TARGET_COMMAND = "TARGET_COMMAND";
- public static final String TARGET_DEVICE = "TARGET_DEVICE";
- private static final String SET_CURRENT_DEVICE = "SET_CURRENT_DEVICE";
-
- private static String currentDeviceId;
-
- @Override
- public void onReceive(Context context, Intent intent) {
-
- super.onReceive(context, intent);
-
- if (intent != null && intent.getAction() != null && intent.getAction().equals(RUN_COMMAND_ACTION)) {
-
- final String targetCommand = intent.getStringExtra(TARGET_COMMAND);
- final String targetDevice = intent.getStringExtra(TARGET_DEVICE);
-
- RunCommandPlugin plugin = KdeConnect.getInstance().getDevicePlugin(targetDevice, RunCommandPlugin.class);
-
- if (plugin != null) {
- try {
- plugin.runCommand(targetCommand);
- } catch (Exception ex) {
- Log.e("RunCommandWidget", "Error running command", ex);
- }
- }
- } else if (intent != null && TextUtils.equals(intent.getAction(), SET_CURRENT_DEVICE)) {
- setCurrentDevice(context);
- }
-
- try {
- // FIXME: Remove the need for a background service, we are not allowed to start them in API 31+
- final Intent newIntent = new Intent(context, RunCommandWidgetDataProviderService.class);
- context.startService(newIntent);
- updateWidget(context);
- } catch(IllegalStateException exception) { // Meant to catch BackgroundServiceStartNotAllowedException on API 31+
- exception.printStackTrace();
- }
- }
-
- private void setCurrentDevice(final Context context) {
- Intent popup = new Intent(context, RunCommandWidgetDeviceSelector.class);
- popup.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
-
- context.startActivity(popup);
- }
-
- public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) {
- updateWidget(context);
- }
-
- private void updateWidget(final Context context) {
-
- Device device = getCurrentDevice();
-
- if (device == null || !device.isReachable()) {
- ConcurrentHashMap devices = KdeConnect.getInstance().getDevices();
- if (devices.size() > 0) {
- currentDeviceId = devices.elements().nextElement().getDeviceId();
- }
- }
-
- updateWidgetImpl(context);
- }
-
- private void updateWidgetImpl(Context context) {
-
- try {
-
- AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(context);
- int[] appWidgetIds = appWidgetManager.getAppWidgetIds(new ComponentName(context, RunCommandWidget.class));
- RemoteViews views = new RemoteViews(context.getPackageName(), R.layout.widget_remotecommandplugin);
-
- PendingIntent pendingIntent;
- Intent intent;
-
- intent = new Intent(context, RunCommandWidget.class);
- intent.setAction(SET_CURRENT_DEVICE);
- pendingIntent = PendingIntent.getBroadcast(context, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_MUTABLE);
- views.setOnClickPendingIntent(R.id.runcommandWidgetTitleHeader, pendingIntent);
-
- Device device = getCurrentDevice();
- if (device == null || !device.isReachable()) {
- views.setTextViewText(R.id.runcommandWidgetTitle, context.getString(R.string.kde_connect));
- views.setViewVisibility(R.id.run_commands_list, View.GONE);
- views.setViewVisibility(R.id.not_reachable_message, View.VISIBLE);
- } else {
- views.setTextViewText(R.id.runcommandWidgetTitle, device.getName());
- views.setViewVisibility(R.id.run_commands_list, View.VISIBLE);
- views.setViewVisibility(R.id.not_reachable_message, View.GONE);
- }
-
- for (int appWidgetId : appWidgetIds) {
-
- intent = new Intent(context, RunCommandWidgetDataProviderService.class);
- intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetId);
- intent.setData(Uri.parse(intent.toUri(Intent.URI_INTENT_SCHEME)));
- views.setRemoteAdapter(R.id.run_commands_list, intent);
-
- intent = new Intent(context, RunCommandWidget.class);
- pendingIntent = PendingIntent.getBroadcast(context, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_MUTABLE);
- views.setPendingIntentTemplate(R.id.run_commands_list, pendingIntent);
-
- AppWidgetManager.getInstance(context).updateAppWidget(appWidgetId, views);
- AppWidgetManager.getInstance(context).notifyAppWidgetViewDataChanged(appWidgetId, R.id.run_commands_list);
-
- }
-
- } catch (Exception ex) {
- Log.e("RunCommandWidget", "Error updating widget", ex);
- }
-
-
- KdeConnect.getInstance().addDeviceListChangedCallback("RunCommandWidget", () -> {
- Intent updateWidget = new Intent(context, RunCommandWidget.class);
- context.sendBroadcast(updateWidget);
- });
- }
-
- public static Device getCurrentDevice() {
- return KdeConnect.getInstance().getDevice(currentDeviceId);
- }
-
- public static void setCurrentDevice(final String DeviceId) {
- currentDeviceId = DeviceId;
- }
-}
\ No newline at end of file
diff --git a/src/org/kde/kdeconnect/Plugins/RunCommandPlugin/RunCommandWidgetConfig.kt b/src/org/kde/kdeconnect/Plugins/RunCommandPlugin/RunCommandWidgetConfig.kt
new file mode 100644
index 00000000..1a670203
--- /dev/null
+++ b/src/org/kde/kdeconnect/Plugins/RunCommandPlugin/RunCommandWidgetConfig.kt
@@ -0,0 +1,85 @@
+package org.kde.kdeconnect.Plugins.RunCommandPlugin
+
+import android.appwidget.AppWidgetManager
+import android.appwidget.AppWidgetManager.EXTRA_APPWIDGET_ID
+import android.content.Context
+import android.content.Intent
+import android.os.Bundle
+import android.view.View
+import android.view.ViewGroup
+import android.view.Window
+import android.widget.ArrayAdapter
+import android.widget.ImageView
+import android.widget.TextView
+import androidx.appcompat.app.AppCompatActivity
+import org.kde.kdeconnect.Device
+import org.kde.kdeconnect.KdeConnect
+import org.kde.kdeconnect_tp.R
+import org.kde.kdeconnect_tp.databinding.WidgetRemoteCommandPluginDialogBinding
+import kotlin.streams.toList
+
+class RunCommandWidgetConfig : AppCompatActivity() {
+
+ private var appWidgetId = AppWidgetManager.INVALID_APPWIDGET_ID
+
+ override fun onCreate(bundle: Bundle?) {
+ super.onCreate(bundle)
+
+ setResult(RESULT_CANCELED) // Default result
+
+ appWidgetId = intent.extras?.getInt(EXTRA_APPWIDGET_ID, AppWidgetManager.INVALID_APPWIDGET_ID) ?: AppWidgetManager.INVALID_APPWIDGET_ID
+ if (appWidgetId == AppWidgetManager.INVALID_APPWIDGET_ID) {
+ finish()
+ return
+ }
+
+ supportRequestWindowFeature(Window.FEATURE_NO_TITLE)
+
+ val binding = WidgetRemoteCommandPluginDialogBinding.inflate(layoutInflater)
+ setContentView(binding.root)
+
+ val pairedDevices = KdeConnect.getInstance().devices.values.stream().filter(Device::isPaired).toList()
+
+ binding.runCommandsDeviceList.adapter = object : ArrayAdapter(this, 0, pairedDevices) {
+ override fun getView(position: Int, convertView: View?, parent: ViewGroup): View {
+ val view : View = convertView ?: layoutInflater.inflate(R.layout.list_item_with_icon_entry, parent, false)
+ val device = pairedDevices[position]
+ view.findViewById(R.id.list_item_entry_title).text = device.name
+ view.findViewById(R.id.list_item_entry_icon).setImageDrawable(device.icon)
+ return view
+ }
+ }
+ binding.runCommandsDeviceList.setOnItemClickListener { _, _, position, _ ->
+ val deviceId = pairedDevices[position].deviceId
+ saveWidgetDeviceIdPref(this, appWidgetId, deviceId)
+
+ val appWidgetManager = AppWidgetManager.getInstance(this)
+ updateAppWidget(this, appWidgetManager, appWidgetId)
+
+ val resultValue = Intent()
+ resultValue.putExtra(EXTRA_APPWIDGET_ID, appWidgetId)
+ setResult(RESULT_OK, resultValue)
+ finish()
+ }
+ }
+}
+
+private const val PREFS_NAME = "org.kde.kdeconnect_tp.WidgetProvider"
+private const val PREF_PREFIX_KEY = "appwidget_"
+
+internal fun saveWidgetDeviceIdPref(context: Context, appWidgetId: Int, deviceName: String) {
+ val prefs = context.getSharedPreferences(PREFS_NAME, 0).edit()
+ prefs.putString(PREF_PREFIX_KEY + appWidgetId, deviceName)
+ prefs.apply()
+}
+
+internal fun loadWidgetDeviceIdPref(context: Context, appWidgetId: Int): String? {
+ val prefs = context.getSharedPreferences(PREFS_NAME, 0)
+ return prefs.getString(PREF_PREFIX_KEY + appWidgetId, null)
+}
+
+internal fun deleteWidgetDeviceIdPref(context: Context, appWidgetId: Int) {
+ val prefs = context.getSharedPreferences(PREFS_NAME, 0).edit()
+ prefs.remove(PREF_PREFIX_KEY + appWidgetId)
+ prefs.apply()
+}
diff --git a/src/org/kde/kdeconnect/Plugins/RunCommandPlugin/RunCommandWidgetDataProvider.java b/src/org/kde/kdeconnect/Plugins/RunCommandPlugin/RunCommandWidgetDataProvider.java
deleted file mode 100644
index 5ea099e4..00000000
--- a/src/org/kde/kdeconnect/Plugins/RunCommandPlugin/RunCommandWidgetDataProvider.java
+++ /dev/null
@@ -1,94 +0,0 @@
-package org.kde.kdeconnect.Plugins.RunCommandPlugin;
-
-import android.content.Context;
-import android.content.Intent;
-import android.view.View;
-import android.widget.RemoteViews;
-import android.widget.RemoteViewsService;
-
-import org.kde.kdeconnect_tp.R;
-
-import java.util.ArrayList;
-
-
-class RunCommandWidgetDataProvider implements RemoteViewsService.RemoteViewsFactory {
-
- private final Context mContext;
-
- public RunCommandWidgetDataProvider(Context context, Intent intent) {
- mContext = context;
- }
-
- private boolean checkPlugin() {
- return RunCommandWidget.getCurrentDevice() != null && RunCommandWidget.getCurrentDevice().isReachable() && RunCommandWidget.getCurrentDevice().getPlugin(RunCommandPlugin.class) != null;
- }
-
- @Override
- public void onCreate() {
- }
-
- @Override
- public void onDataSetChanged() {
-
- }
-
- @Override
- public void onDestroy() {
- }
-
- @Override
- public int getCount() {
- return checkPlugin() ? RunCommandWidget.getCurrentDevice().getPlugin(RunCommandPlugin.class).getCommandItems().size() : 0;
- }
-
- @Override
- public RemoteViews getViewAt(int i) {
-
- RemoteViews remoteView = new RemoteViews(mContext.getPackageName(), R.layout.list_item_entry);
-
- if (checkPlugin() && RunCommandWidget.getCurrentDevice().getPlugin(RunCommandPlugin.class).getCommandItems().size() > i) {
- CommandEntry listItem = RunCommandWidget.getCurrentDevice().getPlugin(RunCommandPlugin.class).getCommandItems().get(i);
-
- final Intent configIntent = new Intent(mContext, RunCommandWidget.class);
- configIntent.setAction(RunCommandWidget.RUN_COMMAND_ACTION);
- configIntent.putExtra(RunCommandWidget.TARGET_COMMAND, listItem.getKey());
- configIntent.putExtra(RunCommandWidget.TARGET_DEVICE, RunCommandWidget.getCurrentDevice().getDeviceId());
-
- remoteView.setTextViewText(R.id.list_item_entry_title, listItem.getName());
- remoteView.setTextViewText(R.id.list_item_entry_summary, listItem.getCommand());
- remoteView.setViewVisibility(R.id.list_item_entry_summary, View.VISIBLE);
-
- remoteView.setOnClickFillInIntent(R.id.list_item_entry, configIntent);
- }
-
- return remoteView;
- }
-
- @Override
- public RemoteViews getLoadingView() {
- return null;
- }
-
- @Override
- public int getViewTypeCount() {
- return 1;
- }
-
- @Override
- public long getItemId(int i) {
- int id = 0;
- if (RunCommandWidget.getCurrentDevice() != null) {
- RunCommandPlugin plugin = RunCommandWidget.getCurrentDevice().getPlugin(RunCommandPlugin.class);
- if (plugin != null) {
- ArrayList items = plugin.getCommandItems();
- id = items.get(i).getKey().hashCode();
- }
- }
- return id;
- }
-
- @Override
- public boolean hasStableIds() {
- return false;
- }
-}
\ No newline at end of file
diff --git a/src/org/kde/kdeconnect/Plugins/RunCommandPlugin/RunCommandWidgetDataProvider.kt b/src/org/kde/kdeconnect/Plugins/RunCommandPlugin/RunCommandWidgetDataProvider.kt
new file mode 100644
index 00000000..1fb1fbf4
--- /dev/null
+++ b/src/org/kde/kdeconnect/Plugins/RunCommandPlugin/RunCommandWidgetDataProvider.kt
@@ -0,0 +1,83 @@
+package org.kde.kdeconnect.Plugins.RunCommandPlugin
+
+import android.appwidget.AppWidgetManager
+import android.appwidget.AppWidgetManager.EXTRA_APPWIDGET_ID
+import android.content.Context
+import android.content.Intent
+import android.util.Log
+import android.view.View
+import android.widget.RemoteViews
+import android.widget.RemoteViewsService
+import android.widget.RemoteViewsService.RemoteViewsFactory
+import org.kde.kdeconnect.KdeConnect
+import org.kde.kdeconnect_tp.R
+
+internal class RunCommandWidgetDataProvider(private val context: Context, val intent: Intent?) : RemoteViewsFactory {
+
+ private lateinit var deviceId : String
+ private var widgetId : Int = AppWidgetManager.INVALID_APPWIDGET_ID
+
+ override fun onCreate() {
+ widgetId = intent?.getIntExtra(EXTRA_APPWIDGET_ID, AppWidgetManager.INVALID_APPWIDGET_ID) ?: AppWidgetManager.INVALID_APPWIDGET_ID
+ if (widgetId == AppWidgetManager.INVALID_APPWIDGET_ID) {
+ Log.e("KDEConnect/Widget", "RunCommandWidgetDataProvider: No widget id extra set")
+ return
+ }
+ deviceId = loadWidgetDeviceIdPref(context, widgetId)!!
+ }
+
+ override fun onDataSetChanged() {}
+ override fun onDestroy() {}
+
+ override fun getCount(): Int {
+ return KdeConnect.getInstance().getDevicePlugin(deviceId, RunCommandPlugin::class.java)?.commandItems?.size ?: 0
+ }
+
+ override fun getViewAt(i: Int): RemoteViews {
+ val remoteView = RemoteViews(context.packageName, R.layout.list_item_entry)
+
+ val plugin : RunCommandPlugin? = KdeConnect.getInstance().getDevicePlugin(deviceId, RunCommandPlugin::class.java)
+ if (plugin == null) {
+ Log.e("getViewAt", "RunCommandWidgetDataProvider: Plugin not found");
+ return remoteView
+ }
+
+ val listItem = plugin.commandItems[i]
+
+ remoteView.setTextViewText(R.id.list_item_entry_title, listItem.name)
+ remoteView.setTextViewText(R.id.list_item_entry_summary, listItem.command)
+ remoteView.setViewVisibility(R.id.list_item_entry_summary, View.VISIBLE)
+
+ val runCommandIntent = Intent(context, RunCommandWidgetProvider::class.java)
+ runCommandIntent.action = RUN_COMMAND_ACTION
+ runCommandIntent.putExtra(EXTRA_APPWIDGET_ID, widgetId)
+ runCommandIntent.putExtra(TARGET_COMMAND, listItem.key)
+ runCommandIntent.putExtra(TARGET_DEVICE, deviceId)
+ remoteView.setOnClickFillInIntent(R.id.list_item_entry, runCommandIntent)
+
+ return remoteView
+ }
+
+ override fun getLoadingView(): RemoteViews? {
+ return null
+ }
+
+ override fun getViewTypeCount(): Int {
+ return 1
+ }
+
+ override fun getItemId(i: Int): Long {
+ return KdeConnect.getInstance().getDevicePlugin(deviceId, RunCommandPlugin::class.java)?.commandItems?.get(i)?.key?.hashCode()?.toLong() ?: 0
+ }
+
+ override fun hasStableIds(): Boolean {
+ return false
+ }
+}
+
+class CommandsRemoteViewsService : RemoteViewsService() {
+ override fun onGetViewFactory(intent: Intent): RemoteViewsFactory {
+ return RunCommandWidgetDataProvider(this.applicationContext, intent)
+ }
+}
+
diff --git a/src/org/kde/kdeconnect/Plugins/RunCommandPlugin/RunCommandWidgetDataProviderService.java b/src/org/kde/kdeconnect/Plugins/RunCommandPlugin/RunCommandWidgetDataProviderService.java
deleted file mode 100644
index 16c4cc89..00000000
--- a/src/org/kde/kdeconnect/Plugins/RunCommandPlugin/RunCommandWidgetDataProviderService.java
+++ /dev/null
@@ -1,12 +0,0 @@
-package org.kde.kdeconnect.Plugins.RunCommandPlugin;
-
-import android.content.Intent;
-import android.widget.RemoteViewsService;
-
-public class RunCommandWidgetDataProviderService extends RemoteViewsService {
-
- @Override
- public RemoteViewsFactory onGetViewFactory(Intent intent) {
- return new RunCommandWidgetDataProvider(this, intent);
- }
-}
diff --git a/src/org/kde/kdeconnect/Plugins/RunCommandPlugin/RunCommandWidgetDeviceSelector.java b/src/org/kde/kdeconnect/Plugins/RunCommandPlugin/RunCommandWidgetDeviceSelector.java
deleted file mode 100644
index 0047f86a..00000000
--- a/src/org/kde/kdeconnect/Plugins/RunCommandPlugin/RunCommandWidgetDeviceSelector.java
+++ /dev/null
@@ -1,48 +0,0 @@
-package org.kde.kdeconnect.Plugins.RunCommandPlugin;
-
-import android.content.Intent;
-import android.os.Bundle;
-import android.view.Window;
-
-import androidx.appcompat.app.AppCompatActivity;
-
-import org.kde.kdeconnect.Device;
-import org.kde.kdeconnect.KdeConnect;
-import org.kde.kdeconnect.UserInterface.List.ListAdapter;
-import org.kde.kdeconnect_tp.databinding.WidgetRemoteCommandPluginDialogBinding;
-
-import java.util.Comparator;
-import java.util.List;
-import java.util.stream.Collectors;
-
-public class RunCommandWidgetDeviceSelector extends AppCompatActivity {
- @Override
- protected void onCreate(Bundle bundle) {
- super.onCreate(bundle);
-
- requestWindowFeature(Window.FEATURE_NO_TITLE);
-
- final WidgetRemoteCommandPluginDialogBinding binding =
- WidgetRemoteCommandPluginDialogBinding.inflate(getLayoutInflater());
- setContentView(binding.getRoot());
-
- final List deviceItems = KdeConnect.getInstance().getDevices().values().stream()
- .filter(Device::isPaired).filter(Device::isReachable)
- .map(device -> new CommandEntry(device.getName(), null, device.getDeviceId()))
- .sorted(Comparator.comparing(CommandEntry::getName))
- .collect(Collectors.toList());
-
- ListAdapter adapter = new ListAdapter(RunCommandWidgetDeviceSelector.this, deviceItems);
-
- binding.runCommandsDeviceList.setAdapter(adapter);
- binding.runCommandsDeviceList.setOnItemClickListener((adapterView, viewContent, i, l) -> {
- CommandEntry entry = deviceItems.get(i);
- RunCommandWidget.setCurrentDevice(entry.getKey());
-
- Intent updateWidget = new Intent(RunCommandWidgetDeviceSelector.this, RunCommandWidget.class);
- RunCommandWidgetDeviceSelector.this.sendBroadcast(updateWidget);
-
- finish();
- });
- }
-}
\ No newline at end of file
diff --git a/src/org/kde/kdeconnect/Plugins/RunCommandPlugin/RunCommandWidgetProvider.kt b/src/org/kde/kdeconnect/Plugins/RunCommandPlugin/RunCommandWidgetProvider.kt
new file mode 100644
index 00000000..91ce92f7
--- /dev/null
+++ b/src/org/kde/kdeconnect/Plugins/RunCommandPlugin/RunCommandWidgetProvider.kt
@@ -0,0 +1,138 @@
+package org.kde.kdeconnect.Plugins.RunCommandPlugin
+
+import android.app.PendingIntent
+import android.appwidget.AppWidgetManager
+import android.appwidget.AppWidgetManager.EXTRA_APPWIDGET_ID
+import android.appwidget.AppWidgetProvider
+import android.content.ComponentName
+import android.content.Context
+import android.content.Intent
+import android.net.Uri
+import android.util.Log
+import android.view.View
+import android.widget.RemoteViews
+import org.kde.kdeconnect.Device
+import org.kde.kdeconnect.KdeConnect
+import org.kde.kdeconnect_tp.BuildConfig
+import org.kde.kdeconnect_tp.R
+
+const val RUN_COMMAND_ACTION = "RUN_COMMAND_ACTION"
+const val TARGET_COMMAND = "TARGET_COMMAND"
+const val TARGET_DEVICE = "TARGET_DEVICE"
+private const val SET_CURRENT_DEVICE = "SET_CURRENT_DEVICE"
+
+class RunCommandWidgetProvider : AppWidgetProvider() {
+
+ override fun onUpdate(context: Context, appWidgetManager: AppWidgetManager, appWidgetIds: IntArray) {
+ for (appWidgetId in appWidgetIds) {
+ updateAppWidget(context, appWidgetManager, appWidgetId)
+ }
+ }
+
+ override fun onDeleted(context: Context, appWidgetIds: IntArray) {
+ for (appWidgetId in appWidgetIds) {
+ deleteWidgetDeviceIdPref(context, appWidgetId)
+ }
+ }
+
+ override fun onEnabled(context: Context) {
+ super.onEnabled(context)
+ KdeConnect.getInstance().addDeviceListChangedCallback("RunCommandWidget") {
+ forceRefreshWidgets(context)
+ }
+ }
+
+ override fun onDisabled(context: Context) {
+ KdeConnect.getInstance().removeDeviceListChangedCallback("RunCommandWidget")
+ super.onDisabled(context)
+ }
+
+ override fun onReceive(context: Context, intent: Intent) {
+ super.onReceive(context, intent)
+ Log.d("WidgetProvider", "onReceive " + intent.action)
+
+ val appWidgetId = intent.getIntExtra(EXTRA_APPWIDGET_ID, AppWidgetManager.INVALID_APPWIDGET_ID)
+ if (appWidgetId == AppWidgetManager.INVALID_APPWIDGET_ID) {
+ Log.e("KDEConnect/Widget", "No widget id extra set")
+ return
+ }
+
+ if (intent.action == RUN_COMMAND_ACTION) {
+ val targetCommand = intent.getStringExtra(TARGET_COMMAND)
+ val targetDevice = intent.getStringExtra(TARGET_DEVICE)
+ val plugin = KdeConnect.getInstance().getDevicePlugin(targetDevice, RunCommandPlugin::class.java)
+ if (plugin != null) {
+ try {
+ plugin.runCommand(targetCommand)
+ } catch (ex: Exception) {
+ Log.e("RunCommandWidget", "Error running command", ex)
+ }
+ }
+ } else if (intent.action == SET_CURRENT_DEVICE) {
+ val popup = Intent(context, RunCommandWidgetConfig::class.java)
+ popup.flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS
+ popup.putExtra(EXTRA_APPWIDGET_ID, appWidgetId)
+ context.startActivity(popup)
+ }
+ }
+}
+
+fun getAllWidgetIds(context : Context) : IntArray {
+ return AppWidgetManager.getInstance(context).getAppWidgetIds(
+ ComponentName(context, RunCommandWidgetProvider::class.java)
+ )
+}
+
+fun forceRefreshWidgets(context : Context) {
+ val intent = Intent(context, RunCommandWidgetProvider::class.java)
+ intent.action = AppWidgetManager.ACTION_APPWIDGET_UPDATE
+ intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS, getAllWidgetIds(context))
+ context.sendBroadcast(intent)
+}
+
+internal fun updateAppWidget(
+ context: Context,
+ appWidgetManager: AppWidgetManager,
+ appWidgetId: Int
+) {
+ Log.d("WidgetProvider", "updateAppWidget")
+
+ val deviceId = loadWidgetDeviceIdPref(context, appWidgetId)
+ val device: Device? = if (deviceId != null) KdeConnect.getInstance().getDevice(deviceId) else null
+
+ val views = RemoteViews(BuildConfig.APPLICATION_ID, R.layout.widget_remotecommandplugin)
+ val setDeviceIntent = Intent(context, RunCommandWidgetProvider::class.java)
+ setDeviceIntent.putExtra(EXTRA_APPWIDGET_ID, appWidgetId)
+ setDeviceIntent.action = SET_CURRENT_DEVICE
+ val setDevicePendingIntent = PendingIntent.getBroadcast(context,0,setDeviceIntent, PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_MUTABLE)
+ views.setOnClickPendingIntent(R.id.runcommandWidgetTitleHeader, setDevicePendingIntent)
+
+ if (device == null) {
+ views.setTextViewText(R.id.runcommandWidgetTitle, context.getString(R.string.kde_connect))
+ views.setViewVisibility(R.id.run_commands_list, View.VISIBLE)
+ views.setViewVisibility(R.id.not_reachable_message, View.GONE)
+ } else {
+ views.setTextViewText(R.id.runcommandWidgetTitle, device.name)
+ if (device.isReachable) {
+ views.setViewVisibility(R.id.run_commands_list, View.VISIBLE)
+ views.setViewVisibility(R.id.not_reachable_message, View.GONE)
+ // Configure remote adapter
+ val dataProviderIntent = Intent(context, CommandsRemoteViewsService::class.java)
+ dataProviderIntent.putExtra(EXTRA_APPWIDGET_ID, appWidgetId)
+ dataProviderIntent.data = Uri.parse(dataProviderIntent.toUri(Intent.URI_INTENT_SCHEME))
+ views.setRemoteAdapter(R.id.run_commands_list, dataProviderIntent)
+ // This pending intent allows the remote adapter to call fillInIntent so list items can do things
+ val runCommandTemplateIntent = Intent(context, RunCommandWidgetProvider::class.java)
+ runCommandTemplateIntent.action = RUN_COMMAND_ACTION
+ runCommandTemplateIntent.putExtra(EXTRA_APPWIDGET_ID, appWidgetId)
+ val runCommandTemplatePendingIntent = PendingIntent.getBroadcast(context, 0, runCommandTemplateIntent, PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_MUTABLE)
+ views.setPendingIntentTemplate(R.id.run_commands_list, runCommandTemplatePendingIntent)
+ } else {
+ views.setViewVisibility(R.id.run_commands_list, View.GONE)
+ views.setViewVisibility(R.id.not_reachable_message, View.VISIBLE)
+ }
+ }
+
+ appWidgetManager.notifyAppWidgetViewDataChanged(appWidgetId, R.id.run_commands_list)
+ appWidgetManager.updateAppWidget(appWidgetId, views)
+}