diff --git a/res/drawable/ic_volume_mute_black.xml b/res/drawable/ic_volume_mute_black.xml
new file mode 100644
--- /dev/null
+++ b/res/drawable/ic_volume_mute_black.xml
@@ -0,0 +1,13 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/res/layout/fragment_systemvolume.xml b/res/layout/fragment_systemvolume.xml
new file mode 100644
--- /dev/null
+++ b/res/layout/fragment_systemvolume.xml
@@ -0,0 +1,44 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/res/layout/mpris_control.xml b/res/layout/mpris_control.xml
--- a/res/layout/mpris_control.xml
+++ b/res/layout/mpris_control.xml
@@ -166,19 +166,10 @@
-
diff --git a/res/values/strings.xml b/res/values/strings.xml
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -217,4 +217,9 @@
To see phone calls and SMS from the desktop you need to give permission to phone calls and SMS
To see a contact name instead of a phone number you need to give access to the phone\'s contacts
+ System volume:
+ System volume
+ Control the system volume of the remote device
+ Mute
+
diff --git a/src/org/kde/kdeconnect/Plugins/MprisPlugin/MprisActivity.java b/src/org/kde/kdeconnect/Plugins/MprisPlugin/MprisActivity.java
--- a/src/org/kde/kdeconnect/Plugins/MprisPlugin/MprisActivity.java
+++ b/src/org/kde/kdeconnect/Plugins/MprisPlugin/MprisActivity.java
@@ -26,6 +26,8 @@
import android.os.Message;
import android.preference.PreferenceManager;
import android.support.annotation.NonNull;
+import android.support.v4.app.FragmentManager;
+import android.support.v4.app.FragmentTransaction;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;
import android.view.KeyEvent;
@@ -42,6 +44,7 @@
import org.kde.kdeconnect.BackgroundService;
import org.kde.kdeconnect.Device;
import org.kde.kdeconnect.NetworkPackage;
+import org.kde.kdeconnect.Plugins.SystemvolumePlugin.SystemvolumeFragment;
import org.kde.kdeconnect_tp.R;
import java.util.List;
@@ -81,8 +84,11 @@
Log.e("MprisActivity", "device has no mpris plugin!");
return;
}
+
targetPlayer = mpris.getPlayerStatus(targetPlayerName);
+ addSytemvolumeFragment();
+
mpris.setPlayerStatusUpdatedHandler("activity", new Handler() {
@Override
public void handleMessage(Message msg) {
@@ -160,6 +166,17 @@
}
+ private void addSytemvolumeFragment() {
+
+ FragmentManager fragmentManager = getSupportFragmentManager();
+// FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
+// SystemvolumeFragment fragment = new SystemvolumeFragment();
+// fragmentTransaction.add(R.id.mpris_control_view, fragment);
+// fragmentTransaction.commitNow();
+ ((SystemvolumeFragment) fragmentManager.findFragmentById(R.id.systemvolume_fragment)).connectToPlugin(deviceId);
+
+ }
+
private final BaseLinkProvider.ConnectionReceiver connectionReceiver = new BaseLinkProvider.ConnectionReceiver() {
@Override
public void onConnectionReceived(NetworkPackage identityPackage, BaseLink link) {
@@ -442,4 +459,5 @@
BackgroundService.removeGuiInUseCounter(this);
}
+
}
diff --git a/src/org/kde/kdeconnect/Plugins/PluginFactory.java b/src/org/kde/kdeconnect/Plugins/PluginFactory.java
--- a/src/org/kde/kdeconnect/Plugins/PluginFactory.java
+++ b/src/org/kde/kdeconnect/Plugins/PluginFactory.java
@@ -37,6 +37,7 @@
import org.kde.kdeconnect.Plugins.RunCommandPlugin.RunCommandPlugin;
import org.kde.kdeconnect.Plugins.SftpPlugin.SftpPlugin;
import org.kde.kdeconnect.Plugins.SharePlugin.SharePlugin;
+import org.kde.kdeconnect.Plugins.SystemvolumePlugin.SystemvolumePlugin;
import org.kde.kdeconnect.Plugins.TelepathyPlugin.TelepathyPlugin;
import org.kde.kdeconnect.Plugins.TelephonyPlugin.TelephonyPlugin;
@@ -126,6 +127,7 @@
PluginFactory.registerPlugin(FindMyPhonePlugin.class);
PluginFactory.registerPlugin(RunCommandPlugin.class);
PluginFactory.registerPlugin(RemoteKeyboardPlugin.class);
+ PluginFactory.registerPlugin(SystemvolumePlugin.class);
}
public static PluginInfo getPluginInfo(Context context, String pluginKey) {
diff --git a/src/org/kde/kdeconnect/Plugins/SystemvolumePlugin/Sink.java b/src/org/kde/kdeconnect/Plugins/SystemvolumePlugin/Sink.java
new file mode 100644
--- /dev/null
+++ b/src/org/kde/kdeconnect/Plugins/SystemvolumePlugin/Sink.java
@@ -0,0 +1,83 @@
+/*
+ * Copyright 2017 Nicolas Fella
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of
+ * the License or (at your option) version 3 or any later version
+ * accepted by the membership of KDE e.V. (or its successor approved
+ * by the membership of KDE e.V.), which shall act as a proxy
+ * defined in Section 14 of version 3 of the license.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+*/
+
+package org.kde.kdeconnect.Plugins.SystemvolumePlugin;
+
+import org.kde.kdeconnect.NetworkPackage;
+
+import java.util.ArrayList;
+import java.util.List;
+
+class Sink {
+
+ public interface UpdateListener {
+ void updateSink(Sink sink);
+ }
+
+ private int volume;
+ private String description;
+ private String name;
+ private boolean mute;
+
+ private List listeners;
+
+ public Sink(String name) {
+ listeners = new ArrayList<>();
+ this.name = name;
+ }
+
+ public void update(NetworkPackage np) {
+ volume = np.getInt("volume");
+ description = np.getString("description");
+ mute = np.getBoolean("mute");
+
+ for (UpdateListener l: listeners) {
+ l.updateSink(this);
+ }
+ }
+
+ public int getVolume() {
+ return volume;
+ }
+
+ public String getDescription() {
+ return description;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public boolean isMute() {
+ return mute;
+ }
+
+ public void addListener(UpdateListener l) {
+
+ if (!listeners.contains(l)) {
+ listeners.add(l);
+ }
+ }
+
+ public void removeListener(UpdateListener l) {
+ listeners.remove(l);
+ }
+
+}
diff --git a/src/org/kde/kdeconnect/Plugins/SystemvolumePlugin/SystemvolumeFragment.java b/src/org/kde/kdeconnect/Plugins/SystemvolumePlugin/SystemvolumeFragment.java
new file mode 100644
--- /dev/null
+++ b/src/org/kde/kdeconnect/Plugins/SystemvolumePlugin/SystemvolumeFragment.java
@@ -0,0 +1,169 @@
+/*
+ * Copyright 2017 Nicolas Fella
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of
+ * the License or (at your option) version 3 or any later version
+ * accepted by the membership of KDE e.V. (or its successor approved
+ * by the membership of KDE e.V.), which shall act as a proxy
+ * defined in Section 14 of version 3 of the license.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+*/
+
+package org.kde.kdeconnect.Plugins.SystemvolumePlugin;
+
+import android.app.Activity;
+import android.content.Context;
+import android.os.Bundle;
+import android.os.Handler;
+import android.support.annotation.Nullable;
+import android.support.v4.app.Fragment;
+import android.util.Log;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.ImageButton;
+import android.widget.SeekBar;
+import android.widget.TextView;
+
+import org.kde.kdeconnect.BackgroundService;
+import org.kde.kdeconnect.Device;
+import org.kde.kdeconnect_tp.R;
+
+public class SystemvolumeFragment extends Fragment implements Sink.UpdateListener, View.OnClickListener, Runnable, SeekBar.OnSeekBarChangeListener {
+
+ private final Handler updateHandler = new Handler();
+
+ private SeekBar seekBar;
+ private ImageButton button;
+ private TextView description;
+
+ private boolean mute = false;
+ private SystemvolumePlugin plugin;
+
+ private Activity activity;
+
+ @Nullable
+ @Override
+ public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
+
+ View view = inflater.inflate(R.layout.fragment_systemvolume, container, false);
+
+ seekBar = (SeekBar) view.findViewById(R.id.systemvolume_seek);
+ seekBar.setMax(65535);
+ seekBar.setOnSeekBarChangeListener(this);
+
+ button = (ImageButton) view.findViewById(R.id.systemvolume_mute);
+ button.setOnClickListener(this);
+
+ description = (TextView) view.findViewById(R.id.systemvolume_label);
+
+ return view;
+ }
+
+ @Override
+ public void updateSink(final Sink sink) {
+
+ activity.runOnUiThread(new Runnable() {
+ @Override
+ public void run() {
+ seekBar.setProgress(sink.getVolume());
+ description.setText(sink.getDescription());
+
+ int iconRes = sink.isMute() ? R.drawable.ic_volume_mute_black : R.drawable.ic_volume_black;
+ button.setImageResource(iconRes);
+ }
+ });
+ }
+
+ @Override
+ public void onStop() {
+ super.onStop();
+ updateHandler.removeCallbacks(this);
+ }
+
+ @Override
+ public void onClick(View view) {
+
+ mute = !mute;
+ plugin.setMute(mute);
+
+ }
+
+ @Override
+ public void run() {
+ BackgroundService.RunCommand(activity, new BackgroundService.InstanceCallback() {
+ @Override
+ public void onServiceStart(BackgroundService service) {
+
+ plugin.askForSystemVolume();
+
+ for (Sink sink : plugin.getSinks()) {
+ sink.addListener(SystemvolumeFragment.this);
+ }
+
+ updateHandler.removeCallbacks(SystemvolumeFragment.this);
+ updateHandler.postDelayed(SystemvolumeFragment.this, 500);
+ }
+ });
+ }
+
+ @Override
+ public void onProgressChanged(final SeekBar seekBar, int i, boolean b) {
+ BackgroundService.RunCommand(activity, new BackgroundService.InstanceCallback() {
+ @Override
+ public void onServiceStart(BackgroundService service) {
+ plugin.sendSystemVolumeSet(seekBar.getProgress());
+ }
+ });
+ }
+
+ @Override
+ public void onStartTrackingTouch(SeekBar seekBar) {
+ updateHandler.removeCallbacks(this);
+ }
+
+ @Override
+ public void onStopTrackingTouch(final SeekBar seekBar) {
+ BackgroundService.RunCommand(activity, new BackgroundService.InstanceCallback() {
+ @Override
+ public void onServiceStart(BackgroundService service) {
+ plugin.sendSystemVolumeSet(seekBar.getProgress());
+ updateHandler.postDelayed(SystemvolumeFragment.this, 1);
+ }
+ });
+ }
+
+ public void connectToPlugin(final String deviceId) {
+
+ BackgroundService.RunCommand(activity, new BackgroundService.InstanceCallback() {
+ @Override
+ public void onServiceStart(BackgroundService service) {
+ Device device = service.getDevice(deviceId);
+ plugin = device.getPlugin(SystemvolumePlugin.class);
+
+ if (plugin == null) {
+ Log.e("SystemvolumeFragment", "device has no systemvolume plugin!");
+ return;
+ }
+
+ updateHandler.postDelayed(SystemvolumeFragment.this, 0);
+ }
+ });
+
+ }
+
+ @Override
+ public void onAttach(Context context) {
+ super.onAttach(context);
+ activity = getActivity();
+ }
+}
diff --git a/src/org/kde/kdeconnect/Plugins/SystemvolumePlugin/SystemvolumePlugin.java b/src/org/kde/kdeconnect/Plugins/SystemvolumePlugin/SystemvolumePlugin.java
new file mode 100644
--- /dev/null
+++ b/src/org/kde/kdeconnect/Plugins/SystemvolumePlugin/SystemvolumePlugin.java
@@ -0,0 +1,113 @@
+/*
+ * Copyright 2017 Nicolas Fella
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of
+ * the License or (at your option) version 3 or any later version
+ * accepted by the membership of KDE e.V. (or its successor approved
+ * by the membership of KDE e.V.), which shall act as a proxy
+ * defined in Section 14 of version 3 of the license.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+*/
+
+package org.kde.kdeconnect.Plugins.SystemvolumePlugin;
+
+import android.util.Log;
+
+import org.kde.kdeconnect.NetworkPackage;
+import org.kde.kdeconnect.Plugins.Plugin;
+import org.kde.kdeconnect_tp.R;
+
+import java.util.Collection;
+import java.util.HashMap;
+
+
+public class SystemvolumePlugin extends Plugin {
+
+ private final static String PACKAGE_TYPE_SYSTEMVOLUME = "kdeconnect.systemvolume";
+
+ private HashMap sinks;
+
+ public SystemvolumePlugin() {
+ sinks = new HashMap<>();
+ }
+
+ @Override
+ public String getDisplayName() {
+ return context.getResources().getString(R.string.pref_plugin_systemvolume);
+ }
+
+ @Override
+ public String getDescription() {
+ return context.getResources().getString(R.string.pref_plugin_systemvolume_desc);
+ }
+
+ @Override
+ public boolean onPackageReceived(NetworkPackage np) {
+
+ String name = np.getString("name");
+
+ if (!sinks.containsKey(name)) {
+ sinks.put(name, new Sink(name));
+ }
+
+ sinks.get(np.getString("name")).update(np);
+
+ return true;
+ }
+
+ public void sendSystemVolumeSet(int volume) {
+
+ NetworkPackage np = new NetworkPackage(PACKAGE_TYPE_SYSTEMVOLUME);
+ np.set("volume", volume);
+ np.set("request", false);
+ device.sendPackage(np);
+ }
+
+ public void askForSystemVolume() {
+ NetworkPackage np = new NetworkPackage(PACKAGE_TYPE_SYSTEMVOLUME);
+ np.set("request", true);
+ device.sendPackage(np);
+ Log.d("Systemvolume", "sent ask");
+ }
+
+ public void setMute(boolean mute) {
+ NetworkPackage np = new NetworkPackage(PACKAGE_TYPE_SYSTEMVOLUME);
+ np.set("mute", mute);
+ np.set("request", false);
+ device.sendPackage(np);
+ }
+
+ @Override
+ public boolean hasMainActivity() {
+ return false;
+ }
+
+ @Override
+ public boolean displayInContextMenu() {
+ return false;
+ }
+
+ @Override
+ public String[] getSupportedPackageTypes() {
+ return new String[]{PACKAGE_TYPE_SYSTEMVOLUME};
+ }
+
+ @Override
+ public String[] getOutgoingPackageTypes() {
+ return new String[]{PACKAGE_TYPE_SYSTEMVOLUME};
+ }
+
+ public Collection getSinks() {
+ return sinks.values();
+ }
+
+}