Warning, /network/kdeconnect-android/src/org/kde/kdeconnect/Plugins/RunCommandPlugin/RunCommandControlsProviderService.kt is written in an unsupported language. File is not indexed.
0001 /* 0002 * SPDX-FileCopyrightText: 2021 Maxim Leshchenko <cnmaks90@gmail.com> 0003 * 0004 * SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL 0005 */ 0006 0007 package org.kde.kdeconnect.Plugins.RunCommandPlugin 0008 0009 import android.app.PendingIntent 0010 import android.content.Intent 0011 import android.content.SharedPreferences 0012 import android.graphics.drawable.Icon 0013 import android.os.Build 0014 import android.service.controls.Control 0015 import android.service.controls.ControlsProviderService 0016 import android.service.controls.DeviceTypes 0017 import android.service.controls.actions.CommandAction 0018 import android.service.controls.actions.ControlAction 0019 import android.service.controls.templates.StatelessTemplate 0020 import android.util.Log 0021 import androidx.annotation.RequiresApi 0022 import androidx.preference.PreferenceManager 0023 import io.reactivex.Flowable 0024 import io.reactivex.processors.ReplayProcessor 0025 import org.json.JSONArray 0026 import org.json.JSONException 0027 import org.json.JSONObject 0028 import org.kde.kdeconnect.Device 0029 import org.kde.kdeconnect.KdeConnect 0030 import org.kde.kdeconnect.UserInterface.MainActivity 0031 import org.kde.kdeconnect_tp.R 0032 import org.reactivestreams.FlowAdapters 0033 import java.util.concurrent.Flow 0034 import java.util.function.Consumer 0035 0036 private class CommandEntryWithDevice(o: JSONObject, val device: Device) : CommandEntry(o) 0037 0038 @RequiresApi(Build.VERSION_CODES.R) 0039 class RunCommandControlsProviderService : ControlsProviderService() { 0040 private lateinit var updatePublisher: ReplayProcessor<Control> 0041 private lateinit var sharedPreferences: SharedPreferences 0042 0043 override fun createPublisherForAllAvailable(): Flow.Publisher<Control> { 0044 return FlowAdapters.toFlowPublisher(Flowable.fromIterable(getAllCommandsList().map { commandEntry -> 0045 Control.StatelessBuilder(commandEntry.device.deviceId + ":" + commandEntry.key, getIntent(commandEntry.device)) 0046 .setTitle(commandEntry.name) 0047 .setSubtitle(commandEntry.command) 0048 .setStructure(commandEntry.device.name) 0049 .setCustomIcon(Icon.createWithResource(this, R.drawable.run_command_plugin_icon_24dp)) 0050 .build() 0051 })) 0052 } 0053 0054 override fun createPublisherFor(controlIds: MutableList<String>): Flow.Publisher<Control> { 0055 updatePublisher = ReplayProcessor.create() 0056 0057 for (controlId in controlIds) { 0058 val commandEntry = getCommandByControlId(controlId) 0059 if (commandEntry != null && commandEntry.device.isReachable) { 0060 updatePublisher.onNext(createStatefulBuilder(commandEntry, controlId) 0061 .setStatus(Control.STATUS_OK) 0062 .setStatusText(getString(R.string.tap_to_execute)) 0063 .build()) 0064 } else if (commandEntry != null && commandEntry.device.isPaired && !commandEntry.device.isReachable) { 0065 updatePublisher.onNext(createStatefulBuilder(commandEntry, controlId) 0066 .setStatus(Control.STATUS_DISABLED) 0067 .build()) 0068 } else { 0069 updatePublisher.onNext(Control.StatefulBuilder(controlId, getIntent(commandEntry?.device)) 0070 .setStatus(Control.STATUS_NOT_FOUND) 0071 .build()) 0072 } 0073 } 0074 0075 return FlowAdapters.toFlowPublisher(updatePublisher) 0076 } 0077 0078 override fun performControlAction(controlId: String, action: ControlAction, consumer: Consumer<Int>) { 0079 if (!this::updatePublisher.isInitialized) { 0080 updatePublisher = ReplayProcessor.create() 0081 } 0082 0083 if (action is CommandAction) { 0084 val commandEntry = getCommandByControlId(controlId) 0085 if (commandEntry != null) { 0086 val deviceId = controlId.split(":")[0] 0087 val plugin = KdeConnect.getInstance().getDevicePlugin(deviceId ,RunCommandPlugin::class.java) 0088 if (plugin != null) { 0089 plugin.runCommand(commandEntry.key) 0090 consumer.accept(ControlAction.RESPONSE_OK) 0091 } else { 0092 consumer.accept(ControlAction.RESPONSE_FAIL) 0093 } 0094 0095 updatePublisher.onNext(createStatefulBuilder(commandEntry, controlId) 0096 .setStatus(Control.STATUS_OK) 0097 .setStatusText(getString(R.string.tap_to_execute)) 0098 .build()) 0099 } 0100 } 0101 } 0102 0103 private fun getSavedCommandsList(device: Device): List<CommandEntryWithDevice> { 0104 if (!this::sharedPreferences.isInitialized) { 0105 sharedPreferences = PreferenceManager.getDefaultSharedPreferences(this) 0106 } 0107 0108 val commandList = mutableListOf<CommandEntryWithDevice>() 0109 0110 return try { 0111 val jsonArray = JSONArray(sharedPreferences.getString(RunCommandPlugin.KEY_COMMANDS_PREFERENCE + device.deviceId, "[]")) 0112 0113 for (index in 0 until jsonArray.length()) { 0114 val jsonObject = jsonArray.getJSONObject(index) 0115 commandList.add(CommandEntryWithDevice(jsonObject, device)) 0116 } 0117 0118 commandList 0119 } catch (error: JSONException) { 0120 Log.e("RunCommand", "Error parsing JSON", error) 0121 listOf() 0122 } 0123 } 0124 0125 private fun getAllCommandsList(): List<CommandEntryWithDevice> { 0126 val commandList = mutableListOf<CommandEntryWithDevice>() 0127 0128 for (device in KdeConnect.getInstance().devices.values) { 0129 if (!device.isReachable) { 0130 commandList.addAll(getSavedCommandsList(device)) 0131 continue 0132 } else if (!device.isPaired) { 0133 continue 0134 } 0135 0136 val plugin = device.getPlugin(RunCommandPlugin::class.java) 0137 if (plugin != null) { 0138 for (jsonObject in plugin.commandList) { 0139 try { 0140 commandList.add(CommandEntryWithDevice(jsonObject, device)) 0141 } catch (error: JSONException) { 0142 Log.e("RunCommand", "Error parsing JSON", error) 0143 } 0144 } 0145 } 0146 } 0147 0148 return commandList 0149 } 0150 0151 private fun getCommandByControlId(controlId: String): CommandEntryWithDevice? { 0152 val controlIdParts = controlId.split(":") 0153 0154 val device = KdeConnect.getInstance().getDevice(controlIdParts[0]) 0155 0156 if (device == null || !device.isPaired) return null 0157 0158 val commandList = if (device.isReachable) { 0159 device.getPlugin(RunCommandPlugin::class.java)?.commandList?.map { jsonObject -> 0160 CommandEntryWithDevice(jsonObject, device) 0161 } 0162 } else { 0163 getSavedCommandsList(device) 0164 } 0165 0166 return commandList?.find { command -> 0167 try { 0168 command.key == controlIdParts[1] 0169 } catch (error: JSONException) { 0170 Log.e("RunCommand", "Error parsing JSON", error) 0171 false 0172 } 0173 } 0174 } 0175 0176 private fun createStatefulBuilder(commandEntry: CommandEntryWithDevice, controlId: String): Control.StatefulBuilder { 0177 if (!this::sharedPreferences.isInitialized) { 0178 sharedPreferences = PreferenceManager.getDefaultSharedPreferences(this) 0179 } 0180 val useNameForTitle = sharedPreferences.getBoolean(getString(R.string.set_runcommand_name_as_title), true) 0181 0182 return Control.StatefulBuilder(controlId, getIntent(commandEntry.device)) 0183 .setTitle(if (useNameForTitle) commandEntry.name else commandEntry.command) 0184 .setSubtitle(if (useNameForTitle) commandEntry.command else commandEntry.name) 0185 .setStructure(commandEntry.device.name) 0186 .setControlTemplate(StatelessTemplate(commandEntry.key)) 0187 .setDeviceType(DeviceTypes.TYPE_ROUTINE) 0188 .setCustomIcon(Icon.createWithResource(this, R.drawable.run_command_plugin_icon_24dp)) 0189 } 0190 0191 private fun getIntent(device: Device?): PendingIntent { 0192 val target = if (device?.isReachable == true) RunCommandActivity::class else MainActivity::class 0193 0194 val intent = Intent(Intent.ACTION_MAIN) 0195 .setClass(this, target.java) 0196 .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) 0197 .putExtra(MainActivity.EXTRA_DEVICE_ID, device?.deviceId) 0198 0199 return PendingIntent.getActivity(this, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE) 0200 } 0201 }