diff --git a/AndroidManifest.xml b/AndroidManifest.xml
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -176,14 +176,24 @@
+
+
+
+
+
diff --git a/res/drawable-hdpi/ic_presenter.png b/res/drawable-hdpi/ic_presenter.png
new file mode 100644
index 0000000000000000000000000000000000000000..0cb547e31419c0d742f33848568087561d100778
GIT binary patch
literal 343
zc$@)O0jU0oP)AI5JAM>V031}z3?iX;T)KG
z>bP1l48t(LPFa@gAPCN37+!tfzv(P6fCWsW;2p==$-t)!dYe>W0^6W~+?<;RU#q+q
zK1q^|n7&LE02>%P1>!i~HwL6qN6nSinZVd75Jl0UVSU%#P{2@U0wV<|V2c1cMsL?J
zSpeXk=RHtUB(w-r@YOt1Bo-HtKE0LRU9>!e3S2Wp-EhoE8yi
zuKA%-*%Px1sKTpCEWU{aEbD>JjPF7epa2CZpjW_k-IL67X)9dg2Mq-%Fr$F=2V2%3
pEn0uL`xnRS*8kvO7=~d~@&UZ-H_$5_P*VT^002ovPDHLkV1mAvjywPW
literal 0
Hc$@Ja)8mNDj3*
zcH`3{8K(;QmlqnYy?R+JFxk#>#q^Rlkq$NKjp7>Y0vDZsRc%b4$Z5nN$k69oAj-h8
zc+vFfll@p56vDrT?3tCx?ZmS{>^`?5(}c_a)}Q>-B(RssWLlKWeZSR=%mM5XQWsW#
rO8MVZ&AftN<(2ZxUjbQZ%Tnd9A1SWUJu{mf=nDo)^NzrzLs?cCBf
zOv3sBOa4d-ef8X>T(+z)#mibritXMs>yr~#2`S{+&MCg{Zxy;K=5^it^<_zSvswk5
zI22nh@SFO6j+pk;J2aHN^ZS`c3wEw>=+Abniq@yQiPEpL58W1wQLuR53xGcjs*T
zPDk6LYyOovK0AKn9GgPw=Ghuc_qxpYe53y9{#iDK*~f1mVfh|={q8e?S+6Vqy?Uv|
zkbC>y)1s=(bqo;jn&HWgsO%jq0%z)5B6c$=e72vvH=F0zTyBP4w~YQ5DjiH-MA
z!2I({#ThIMj-~BO;=k@>!2V$WRR37cWehK#i6#FNIkZ%Iu_S|F>ijS76PONEoKxR%
zxx3EKU?*o=cUN88!?@#m??f2jppLO4@5r6aXTJUle<2}zdZ`9P_<#UIIIqyV=#)F=
to;&j=bB8|I9#}Nxby#EzC|I@>)G{a4Z9T_wx2^)D+SAp~Wt~$(69B={tFQn7
literal 0
Hc$@VB++2aSW-r^>*%Aufq-^Zm%yj
zMQ;!7P7(hUEfw=q?ve5jAy!S5rL*HcEb%^Xkj}
ze-_jPEQ{P?z54P5iI{mmCa72Xn?227SKr6#oy_Pt$>y7^P1mB?E7$jKY2Mej#hFEc
zfsw<3fk~i&0mf`g+#7d4Q{chMKQ`yfrREE%X+DUkOFw*icFRf5h_zwg?|#}X^u?6L
zVu{Xa_PyK-(_e4D_+8-uuhe3HPDhHqKR*TzyhXQum2o7XlvEmUFsCTRJX1`)|l_*FU_g+q3?=<-UEzk*AntY-|rOB4X}9
ZlYYa)$=?(f{9gd#db;|#taD0e0syq=^OFDo
literal 0
Hc$@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/res/values/strings.xml b/res/values/strings.xml
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -11,6 +11,8 @@
Share the clipboard contentRemote inputUse your phone or tablet as a touchpad and keyboard
+ Presentation remote
+ Use your device to change slides in a presentationReceive remote keypressesReceive keypress events from remote devicesMultimedia controls
@@ -236,6 +238,9 @@
Device iconSettings icon
+ Fullscreen
+ You can lock your device to use the volume keys as previous/next buttons
+
Add a commandThere are no commands registeredYou can add new commands in the KDE Connect System Settings
diff --git a/src/org/kde/kdeconnect/Plugins/MousePadPlugin/KeyListenerView.java b/src/org/kde/kdeconnect/Plugins/MousePadPlugin/KeyListenerView.java
--- a/src/org/kde/kdeconnect/Plugins/MousePadPlugin/KeyListenerView.java
+++ b/src/org/kde/kdeconnect/Plugins/MousePadPlugin/KeyListenerView.java
@@ -37,7 +37,7 @@
private String deviceId;
- private static SparseIntArray SpecialKeysMap = new SparseIntArray();
+ public static SparseIntArray SpecialKeysMap = new SparseIntArray();
static {
int i = 0;
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
@@ -35,6 +35,7 @@
import org.kde.kdeconnect.Plugins.MprisReceiverPlugin.MprisReceiverPlugin;
import org.kde.kdeconnect.Plugins.NotificationsPlugin.NotificationsPlugin;
import org.kde.kdeconnect.Plugins.PingPlugin.PingPlugin;
+import org.kde.kdeconnect.Plugins.PresenterPlugin.PresenterPlugin;
import org.kde.kdeconnect.Plugins.ReceiveNotificationsPlugin.ReceiveNotificationsPlugin;
import org.kde.kdeconnect.Plugins.RemoteKeyboardPlugin.RemoteKeyboardPlugin;
import org.kde.kdeconnect.Plugins.RunCommandPlugin.RunCommandPlugin;
@@ -127,6 +128,7 @@
PluginFactory.registerPlugin(NotificationsPlugin.class);
PluginFactory.registerPlugin(ReceiveNotificationsPlugin.class);
PluginFactory.registerPlugin(MousePadPlugin.class);
+ PluginFactory.registerPlugin(PresenterPlugin.class);
PluginFactory.registerPlugin(SharePlugin.class);
PluginFactory.registerPlugin(TelepathyPlugin.class);
PluginFactory.registerPlugin(FindMyPhonePlugin.class);
diff --git a/src/org/kde/kdeconnect/Plugins/PresenterPlugin/PresenterActivity.java b/src/org/kde/kdeconnect/Plugins/PresenterPlugin/PresenterActivity.java
new file mode 100644
--- /dev/null
+++ b/src/org/kde/kdeconnect/Plugins/PresenterPlugin/PresenterActivity.java
@@ -0,0 +1,154 @@
+/*
+ * Copyright 2014 Ahmed I. Khalil
+ *
+ * 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.PresenterPlugin;
+
+import android.content.Context;
+import android.content.SharedPreferences;
+import android.media.AudioManager;
+import android.os.Build;
+import android.os.Bundle;
+import android.os.PowerManager;
+import android.preference.PreferenceManager;
+import android.support.v4.media.VolumeProviderCompat;
+import android.support.v4.media.session.MediaSessionCompat;
+import android.support.v4.media.session.PlaybackStateCompat;
+import android.support.v7.app.AppCompatActivity;
+import android.util.Log;
+import android.view.GestureDetector;
+import android.view.HapticFeedbackConstants;
+import android.view.Menu;
+import android.view.MenuInflater;
+import android.view.MenuItem;
+import android.view.MotionEvent;
+import android.view.View;
+import android.view.inputmethod.InputMethodManager;
+
+import org.kde.kdeconnect.BackgroundService;
+import org.kde.kdeconnect.Device;
+import org.kde.kdeconnect.Plugins.MousePadPlugin.KeyListenerView;
+import org.kde.kdeconnect.Plugins.MousePadPlugin.MousePadGestureDetector;
+import org.kde.kdeconnect.Plugins.PresenterPlugin.PresenterPlugin;
+import org.kde.kdeconnect.UserInterface.ThemeUtil;
+import org.kde.kdeconnect_tp.R;
+
+public class PresenterActivity extends AppCompatActivity {
+
+ private MediaSessionCompat mMediaSession;
+
+ PresenterPlugin plugin;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ ThemeUtil.setUserPreferredTheme(this);
+
+ setContentView(R.layout.activity_presenter);
+
+ final String deviceId = getIntent().getStringExtra("deviceId");
+
+ BackgroundService.RunCommand(this, service -> {
+ Device device = service.getDevice(deviceId);
+
+ plugin = device.getPlugin(PresenterPlugin.class);
+ if (plugin == null) {
+ Log.e("PresenterActivity", "device has no presenter plugin!");
+ return;
+ }
+
+ runOnUiThread(() -> {
+
+ findViewById(R.id.next_button).setOnClickListener(v -> {
+ plugin.sendNext();
+ });
+
+ findViewById(R.id.previous_button).setOnClickListener(v -> {
+ plugin.sendPrevious();
+ });
+
+ findViewById(R.id.fullscreen_button).setOnClickListener(v -> {
+ plugin.sendFullscreen();
+ });
+
+ });
+ });
+
+ }
+
+ @Override
+ protected void onStart() {
+ super.onStart();
+ BackgroundService.addGuiInUseCounter(this);
+ if (mMediaSession != null) {
+ mMediaSession.setActive(true);
+ return;
+ }
+ createMediaSession(); //Mediasession will keep
+ }
+
+ @Override
+ protected void onStop() {
+ super.onStop();
+ BackgroundService.removeGuiInUseCounter(this);
+
+ if (mMediaSession != null) {
+ PowerManager pm = (PowerManager) this.getSystemService(Context.POWER_SERVICE);
+ boolean screenOn;
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT_WATCH) {
+ screenOn = pm.isInteractive();
+ } else {
+ screenOn = pm.isScreenOn();
+ }
+ if (screenOn) {
+ mMediaSession.release();
+ } // else we are in the lockscreen, keep the mediasession
+ }
+ }
+
+ private void createMediaSession() {
+ mMediaSession = new MediaSessionCompat(this, "kdeconnect");
+
+ mMediaSession.setFlags(MediaSessionCompat.FLAG_HANDLES_MEDIA_BUTTONS |
+ MediaSessionCompat.FLAG_HANDLES_TRANSPORT_CONTROLS);
+ mMediaSession.setPlaybackState(new PlaybackStateCompat.Builder()
+ .setState(PlaybackStateCompat.STATE_PLAYING, 0, 0)
+ .build());
+ mMediaSession.setPlaybackToRemote(getVolumeProvider());
+ mMediaSession.setActive(true);
+ }
+
+ private VolumeProviderCompat getVolumeProvider() {
+ final int VOLUME_UP = 1;
+ final int VOLUME_DOWN = -1;
+ return new VolumeProviderCompat(VolumeProviderCompat.VOLUME_CONTROL_RELATIVE, 1, 0) {
+ @Override
+ public void onAdjustVolume(int direction) {
+ if (direction == VOLUME_UP) {
+ plugin.sendNext();
+ }
+ else if (direction == VOLUME_DOWN) {
+ plugin.sendPrevious();
+ }
+ }
+ };
+ }
+
+}
+
diff --git a/src/org/kde/kdeconnect/Plugins/PresenterPlugin/PresenterPlugin.java b/src/org/kde/kdeconnect/Plugins/PresenterPlugin/PresenterPlugin.java
new file mode 100644
--- /dev/null
+++ b/src/org/kde/kdeconnect/Plugins/PresenterPlugin/PresenterPlugin.java
@@ -0,0 +1,103 @@
+/*
+ * Copyright 2014 Ahmed I. Khalil
+ *
+ * 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.PresenterPlugin;
+
+
+import android.app.Activity;
+import android.content.Intent;
+import android.graphics.drawable.Drawable;
+import android.support.v4.content.ContextCompat;
+import android.view.KeyEvent;
+
+import org.kde.kdeconnect.NetworkPacket;
+import org.kde.kdeconnect.Plugins.Plugin;
+import org.kde.kdeconnect_tp.R;
+
+import static org.kde.kdeconnect.Plugins.MousePadPlugin.KeyListenerView.SpecialKeysMap;
+
+public class PresenterPlugin extends Plugin {
+
+ public final static String PACKET_TYPE_MOUSEPAD_REQUEST = "kdeconnect.mousepad.request";
+
+ @Override
+ public String getDisplayName() {
+ return context.getString(R.string.pref_plugin_presenter);
+ }
+
+ @Override
+ public String getDescription() {
+ return context.getString(R.string.pref_plugin_presenter_desc);
+ }
+
+ @Override
+ public Drawable getIcon() {
+ return ContextCompat.getDrawable(context, R.drawable.ic_presenter);
+ }
+
+ @Override
+ public boolean hasSettings() {
+ return false;
+ }
+
+ @Override
+ public boolean hasMainActivity() {
+ return true;
+ }
+
+ @Override
+ public void startMainActivity(Activity parentActivity) {
+ Intent intent = new Intent(parentActivity, PresenterActivity.class);
+ intent.putExtra("deviceId", device.getDeviceId());
+ parentActivity.startActivity(intent);
+ }
+
+ @Override
+ public String[] getSupportedPacketTypes() {
+ return new String[0];
+ }
+
+ @Override
+ public String[] getOutgoingPacketTypes() {
+ return new String[]{PACKET_TYPE_MOUSEPAD_REQUEST};
+ }
+
+ @Override
+ public String getActionName() {
+ return context.getString(R.string.pref_plugin_presenter);
+ }
+
+ public void sendNext() {
+ NetworkPacket np = new NetworkPacket(PACKET_TYPE_MOUSEPAD_REQUEST);
+ np.set("specialKey", SpecialKeysMap.get(KeyEvent.KEYCODE_PAGE_DOWN));
+ device.sendPacket(np);
+ }
+ public void sendPrevious() {
+ NetworkPacket np = new NetworkPacket(PACKET_TYPE_MOUSEPAD_REQUEST);
+ np.set("specialKey", SpecialKeysMap.get(KeyEvent.KEYCODE_PAGE_UP));
+ device.sendPacket(np);
+ }
+ public void sendFullscreen() {
+ NetworkPacket np = new NetworkPacket(PACKET_TYPE_MOUSEPAD_REQUEST);
+ np.set("specialKey", SpecialKeysMap.get(KeyEvent.KEYCODE_F5));
+ device.sendPacket(np);
+ }
+
+}