mirror of
https://github.com/KDE/kdeconnect-android
synced 2025-08-22 09:58:08 +00:00
WIP Power plugin
This commit is contained in:
parent
c429bb81b0
commit
a331b1e3f1
@ -114,6 +114,14 @@
|
||||
</intent-filter>
|
||||
</service>
|
||||
|
||||
<!-- New signature permission to ensure only systemui can bind to these services -->
|
||||
<service android:name="org.kde.kdeconnect.Plugins.PowerPlugin.PowerControlsService" android:label="@string/kde_connect"
|
||||
android:permission="android.permission.BIND_CONTROLS">
|
||||
<intent-filter>
|
||||
<action android:name="android.service.controls.ControlsProviderService" />
|
||||
</intent-filter>
|
||||
</service>
|
||||
|
||||
<service
|
||||
android:name="com.android.mms.transaction.TransactionService"
|
||||
android:enabled="true"
|
||||
|
@ -18,7 +18,7 @@ buildscript {
|
||||
}
|
||||
|
||||
android {
|
||||
compileSdkVersion 29
|
||||
compileSdkVersion 30
|
||||
defaultConfig {
|
||||
minSdkVersion 14
|
||||
targetSdkVersion 29
|
||||
@ -167,6 +167,8 @@ dependencies {
|
||||
implementation 'com.google.android.material:material:1.2.1'
|
||||
implementation 'com.jakewharton:disklrucache:2.0.2' //For caching album art bitmaps
|
||||
implementation 'com.jaredrummler:android-device-names:1.1.9' //To get a human-friendly device name
|
||||
implementation 'org.reactivestreams:reactive-streams:1.0.3'
|
||||
implementation 'io.reactivex.rxjava2:rxjava:2.2.0'
|
||||
|
||||
implementation 'org.apache.sshd:sshd-core:0.14.0'
|
||||
implementation 'org.apache.mina:mina-core:2.0.19' //For some reason, makes sshd-core:0.14.0 work without NIO, which isn't available until Android 8 (api 26)
|
||||
|
43
res/layout/activity_power.xml
Normal file
43
res/layout/activity_power.xml
Normal file
@ -0,0 +1,43 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical">
|
||||
|
||||
|
||||
<Button
|
||||
android:id="@+id/shutdown"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="75dp"
|
||||
android:layout_gravity="center"
|
||||
android:adjustViewBounds="false"
|
||||
android:baselineAlignBottom="true"
|
||||
android:clickable="false"
|
||||
android:text="@string/power_shutdown"
|
||||
android:theme="@style/DisableableButton" />
|
||||
|
||||
<Button
|
||||
android:id="@+id/suspend"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="75dp"
|
||||
android:layout_gravity="center"
|
||||
android:adjustViewBounds="false"
|
||||
android:baselineAlignBottom="true"
|
||||
android:clickable="false"
|
||||
android:text="@string/power_suspend"
|
||||
android:theme="@style/DisableableButton" />
|
||||
|
||||
<Button
|
||||
android:id="@+id/lock"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="75dp"
|
||||
android:layout_gravity="center"
|
||||
android:adjustViewBounds="false"
|
||||
android:baselineAlignBottom="true"
|
||||
android:clickable="false"
|
||||
android:text="@string/power_lock"
|
||||
android:theme="@style/DisableableButton" />
|
||||
|
||||
|
||||
</LinearLayout>
|
@ -29,6 +29,8 @@
|
||||
<string name="pref_plugin_mpris_desc">Provides a remote control for your media player</string>
|
||||
<string name="pref_plugin_runcommand">Run Command</string>
|
||||
<string name="pref_plugin_runcommand_desc">Trigger remote commands from your phone or tablet</string>
|
||||
<string name="pref_plugin_power">Power</string>
|
||||
<string name="pref_plugin_power_desc">Power off, lock or suspend your device</string>
|
||||
<string name="pref_plugin_contacts">Contacts Synchronizer</string>
|
||||
<string name="pref_plugin_contacts_desc">Allow synchronizing the device\'s contacts book</string>
|
||||
<string name="pref_plugin_ping">Ping</string>
|
||||
@ -183,6 +185,9 @@
|
||||
<string name="mpris_time_settings_title">Forward/rewind buttons</string>
|
||||
<string name="mpris_time_settings_summary">Adjust the time to fast forward/rewind when pressed</string>
|
||||
<string name="mpris_time_key" translatable="false">mpris_interval_time</string>
|
||||
<string name="power_shutdown">Shutdown</string>
|
||||
<string name="power_suspend">Suspend</string>
|
||||
<string name="power_lock">Lock screen</string>
|
||||
<string-array name="mpris_time_entries">
|
||||
<item>10 seconds</item>
|
||||
<item>20 seconds</item>
|
||||
|
@ -0,0 +1,54 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2020 Albert Vaca Cintora <albertvaka@gmail.com>
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
|
||||
*/
|
||||
|
||||
package org.kde.kdeconnect.Plugins.PowerPlugin;
|
||||
|
||||
import android.os.Bundle;
|
||||
|
||||
import androidx.appcompat.app.AppCompatActivity;
|
||||
|
||||
import org.kde.kdeconnect.BackgroundService;
|
||||
import org.kde.kdeconnect.UserInterface.ThemeUtil;
|
||||
import org.kde.kdeconnect_tp.databinding.ActivityPowerBinding;
|
||||
|
||||
public class PowerActivity extends AppCompatActivity {
|
||||
private String deviceId;
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
ThemeUtil.setUserPreferredTheme(this);
|
||||
|
||||
ActivityPowerBinding binding = ActivityPowerBinding.inflate(getLayoutInflater());
|
||||
setContentView(binding.getRoot());
|
||||
|
||||
deviceId = getIntent().getStringExtra("deviceId");
|
||||
|
||||
binding.shutdown.setOnClickListener(
|
||||
v -> BackgroundService.RunWithPlugin(PowerActivity.this, deviceId, PowerPlugin.class, plugin ->
|
||||
{
|
||||
plugin.sendCommand("shutdown");
|
||||
}
|
||||
)
|
||||
);
|
||||
|
||||
binding.suspend.setOnClickListener(
|
||||
v -> BackgroundService.RunWithPlugin(PowerActivity.this, deviceId, PowerPlugin.class, plugin ->
|
||||
{
|
||||
plugin.sendCommand("suspend");
|
||||
}
|
||||
)
|
||||
);
|
||||
|
||||
binding.lock.setOnClickListener(
|
||||
v -> BackgroundService.RunWithPlugin(PowerActivity.this, deviceId, PowerPlugin.class, plugin ->
|
||||
{
|
||||
plugin.sendCommand("lock");
|
||||
}
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
@ -0,0 +1,106 @@
|
||||
package org.kde.kdeconnect.Plugins.PowerPlugin;
|
||||
|
||||
import android.app.PendingIntent;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.service.controls.Control;
|
||||
import android.service.controls.ControlsProviderService;
|
||||
import android.service.controls.DeviceTypes;
|
||||
import android.service.controls.actions.BooleanAction;
|
||||
import android.service.controls.actions.ControlAction;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.RequiresApi;
|
||||
|
||||
|
||||
import org.reactivestreams.FlowAdapters;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.Flow;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
import io.reactivex.Flowable;
|
||||
import io.reactivex.processors.ReplayProcessor;
|
||||
|
||||
@RequiresApi(30)
|
||||
public class PowerControlsService extends ControlsProviderService {
|
||||
|
||||
private ReplayProcessor updatePublisher;
|
||||
ArrayList<Control> controls =new ArrayList<>();
|
||||
|
||||
final static String MY_TEST_ID = "myAwesomeId";
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public Flow.Publisher createPublisherForAllAvailable() {
|
||||
Context context=getBaseContext();
|
||||
Intent i=new Intent();
|
||||
PendingIntent pi=PendingIntent.getActivity(context,1,i,PendingIntent.FLAG_UPDATE_CURRENT);
|
||||
|
||||
|
||||
Control control = new Control.StatelessBuilder(MY_TEST_ID, pi)
|
||||
.setTitle("My device")
|
||||
.setSubtitle("KDE Connect")
|
||||
.setDeviceType(DeviceTypes.TYPE_DISPLAY) //There's no type "computer"
|
||||
.build();
|
||||
|
||||
controls.add(control);
|
||||
|
||||
return FlowAdapters.toFlowPublisher(Flowable.fromIterable(controls));
|
||||
}
|
||||
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public Flow.Publisher<Control> createPublisherFor(@NonNull List<String> controlIds) {
|
||||
|
||||
Context context = getBaseContext();
|
||||
/* Fill in details for the activity related to this device. On long press,
|
||||
* this Intent will be launched in a bottomsheet. Please design the activity
|
||||
* accordingly to fit a more limited space (about 2/3 screen height).
|
||||
*/
|
||||
Intent i = new Intent();
|
||||
PendingIntent pi = PendingIntent.getActivity(context, 1, i, PendingIntent.FLAG_UPDATE_CURRENT);
|
||||
|
||||
updatePublisher = ReplayProcessor.create();
|
||||
|
||||
// For each controlId in controlIds
|
||||
if (controlIds.contains(MY_TEST_ID)) {
|
||||
|
||||
Control control = new Control.StatefulBuilder(MY_TEST_ID, pi)
|
||||
.setTitle("My device")
|
||||
.setSubtitle("KDE Connect")
|
||||
.setDeviceType(DeviceTypes.TYPE_DISPLAY) //There's no type "computer"
|
||||
.setStatus(Control.STATUS_OK) // For example, Control.STATUS_OK
|
||||
.build();
|
||||
|
||||
updatePublisher.onNext(control);
|
||||
}
|
||||
|
||||
return FlowAdapters.toFlowPublisher(updatePublisher);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void performControlAction(@NonNull String controlId, @NonNull ControlAction action, @NonNull Consumer consumer) {
|
||||
|
||||
/* First, locate the control identified by the controlId. Once it is located, you can
|
||||
* interpret the action appropriately for that specific device. For instance, the following
|
||||
* assumes that the controlId is associated with a light, and the light can be turned on
|
||||
* or off.
|
||||
*/
|
||||
if (action instanceof BooleanAction) {
|
||||
|
||||
// Inform SystemUI that the action has been received and is being processed
|
||||
consumer.accept(ControlAction.RESPONSE_OK);
|
||||
|
||||
|
||||
/* This is where application logic/network requests would be invoked to update the state of
|
||||
* the device.
|
||||
* After updating, the application should use the publisher to update SystemUI with the new
|
||||
* state.
|
||||
*/
|
||||
|
||||
}
|
||||
}
|
||||
}
|
81
src/org/kde/kdeconnect/Plugins/PowerPlugin/PowerPlugin.java
Normal file
81
src/org/kde/kdeconnect/Plugins/PowerPlugin/PowerPlugin.java
Normal file
@ -0,0 +1,81 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2015 Aleix Pol Gonzalez <aleixpol@kde.org>
|
||||
* SPDX-FileCopyrightText: 2015 Albert Vaca Cintora <albertvaka@gmail.com>
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
|
||||
*/
|
||||
|
||||
package org.kde.kdeconnect.Plugins.PowerPlugin;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.Intent;
|
||||
import android.graphics.drawable.Drawable;
|
||||
|
||||
import androidx.core.content.ContextCompat;
|
||||
|
||||
import org.kde.kdeconnect.NetworkPacket;
|
||||
import org.kde.kdeconnect.Plugins.Plugin;
|
||||
import org.kde.kdeconnect.Plugins.PluginFactory;
|
||||
import org.kde.kdeconnect_tp.R;
|
||||
|
||||
@PluginFactory.LoadablePlugin
|
||||
public class PowerPlugin extends Plugin {
|
||||
|
||||
private final static String PACKET_TYPE_POWER = "kdeconnect.power";
|
||||
|
||||
@Override
|
||||
public String getDisplayName() {
|
||||
return context.getResources().getString(R.string.pref_plugin_runcommand);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDescription() {
|
||||
return context.getResources().getString(R.string.pref_plugin_runcommand_desc);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Drawable getIcon() {
|
||||
return ContextCompat.getDrawable(context, R.drawable.run_command_plugin_icon_24dp);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onCreate() {
|
||||
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String[] getSupportedPacketTypes() {
|
||||
return new String[]{};
|
||||
}
|
||||
|
||||
@Override
|
||||
public String[] getOutgoingPacketTypes() {
|
||||
return new String[]{PACKET_TYPE_POWER};
|
||||
}
|
||||
|
||||
public void sendCommand(String command) {
|
||||
NetworkPacket np = new NetworkPacket(PACKET_TYPE_POWER);
|
||||
np.set("command", command);
|
||||
device.sendPacket(np);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasMainActivity() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void startMainActivity(Activity parentActivity) {
|
||||
Intent intent = new Intent(parentActivity, PowerActivity.class);
|
||||
intent.putExtra("deviceId", device.getDeviceId());
|
||||
parentActivity.startActivity(intent);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getActionName() {
|
||||
return context.getString(R.string.pref_plugin_power);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,22 @@
|
||||
package org.kde.kdeconnect.Plugins.PowerPlugin;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.concurrent.Flow;
|
||||
|
||||
public class PowerPluginsSingleton {
|
||||
|
||||
HashMap<String, PowerPlugin> availableDevices = new HashMap<>();
|
||||
|
||||
|
||||
static public PowerPluginsSingleton getInstance() {
|
||||
if (instance == null) {
|
||||
instance = new PowerPluginsSingleton();
|
||||
}
|
||||
return instance;
|
||||
}
|
||||
|
||||
private static PowerPluginsSingleton instance = null;
|
||||
private PowerPluginsSingleton() { }
|
||||
|
||||
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user