*
* 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.ClibpoardPlugin;
+import android.os.Build;
+import androidx.fragment.app.DialogFragment;
+import androidx.preference.PreferenceManager;
import org.kde.kdeconnect.NetworkPacket;
import org.kde.kdeconnect.Plugins.Plugin;
import org.kde.kdeconnect.Plugins.PluginFactory;
+import org.kde.kdeconnect.UserInterface.NoticeAlertDialogFragment;
import org.kde.kdeconnect_tp.R;
@PluginFactory.LoadablePlugin
public class ClipboardPlugin extends Plugin {
/**
* Packet containing just clipboard contents, sent when a device updates its clipboard.
*
* The body should look like so:
* {
* "content": "password"
* }
*/
private final static String PACKET_TYPE_CLIPBOARD = "kdeconnect.clipboard";
/**
* Packet containing clipboard contents and a timestamp that the contents were last updated, sent
* on first connection
*
* The timestamp is milliseconds since epoch. It can be 0, which indicates that the clipboard
* update time is currently unknown.
*
* The body should look like so:
* {
* "timestamp": 542904563213,
* "content": "password"
* }
*/
private final static String PACKET_TYPE_CLIPBOARD_CONNECT = "kdeconnect.clipboard.connect";
+ private final static String ANDROID_10_INCOMPAT_DIALOG_SHOWN_PREFERENCE = "android10IncompatDialogShown";
+
@Override
public String getDisplayName() {
return context.getResources().getString(R.string.pref_plugin_clipboard);
}
@Override
public String getDescription() {
return context.getResources().getString(R.string.pref_plugin_clipboard_desc);
}
@Override
public boolean onPacketReceived(NetworkPacket np) {
String content = np.getString("content");
switch (np.getType()) {
case (PACKET_TYPE_CLIPBOARD):
ClipboardListener.instance(context).setText(content);
return true;
case(PACKET_TYPE_CLIPBOARD_CONNECT):
long packetTime = np.getLong("timestamp");
// If the packetTime is 0, it means the timestamp is unknown (so do nothing).
if (packetTime == 0 || packetTime < ClipboardListener.instance(context).getUpdateTimestamp()) {
return false;
}
ClipboardListener.instance(context).setText(content);
return true;
}
throw new UnsupportedOperationException("Unknown packet type: " + np.getType());
}
private final ClipboardListener.ClipboardObserver observer = this::propagateClipboard;
- void propagateClipboard(String content) {
+ private void propagateClipboard(String content) {
NetworkPacket np = new NetworkPacket(ClipboardPlugin.PACKET_TYPE_CLIPBOARD);
np.set("content", content);
device.sendPacket(np);
}
private void sendConnectPacket() {
String content = ClipboardListener.instance(context).getCurrentContent();
NetworkPacket np = new NetworkPacket(ClipboardPlugin.PACKET_TYPE_CLIPBOARD_CONNECT);
long timestamp = ClipboardListener.instance(context).getUpdateTimestamp();
np.set("timestamp", timestamp);
np.set("content", content);
device.sendPacket(np);
}
+ @Override
+ public boolean checkRequiredPermissions() {
+ return Build.VERSION.SDK_INT <= Build.VERSION_CODES.P || PreferenceManager.getDefaultSharedPreferences(context).getBoolean(ANDROID_10_INCOMPAT_DIALOG_SHOWN_PREFERENCE, false);
+ }
+
+ @Override
+ public DialogFragment getPermissionExplanationDialog() {
+ PreferenceManager.getDefaultSharedPreferences(context).edit().putBoolean(ANDROID_10_INCOMPAT_DIALOG_SHOWN_PREFERENCE, true).apply();
+ return new NoticeAlertDialogFragment.Builder()
+ .setTitle(R.string.pref_plugin_clipboard)
+ .setMessage(R.string.clipboard_android_x_incompat)
+ .setPositiveButton(R.string.sad_ok)
+ .create();
+ }
@Override
public boolean onCreate() {
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
+ return false;
+ }
ClipboardListener.instance(context).registerObserver(observer);
sendConnectPacket();
return true;
}
@Override
public void onDestroy() {
ClipboardListener.instance(context).removeObserver(observer);
}
@Override
public String[] getSupportedPacketTypes() {
return new String[]{PACKET_TYPE_CLIPBOARD, PACKET_TYPE_CLIPBOARD_CONNECT};
}
@Override
public String[] getOutgoingPacketTypes() {
return new String[]{PACKET_TYPE_CLIPBOARD, PACKET_TYPE_CLIPBOARD_CONNECT};
}
}
diff --git a/src/org/kde/kdeconnect/UserInterface/MainActivity.java b/src/org/kde/kdeconnect/UserInterface/MainActivity.java
index 55d3566f..ee862e08 100644
--- a/src/org/kde/kdeconnect/UserInterface/MainActivity.java
+++ b/src/org/kde/kdeconnect/UserInterface/MainActivity.java
@@ -1,389 +1,395 @@
package org.kde.kdeconnect.UserInterface;
import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.content.pm.PackageManager;
import android.os.Bundle;
import android.preference.PreferenceManager;
import android.text.TextUtils;
import android.util.Log;
import android.view.Menu;
import android.view.MenuItem;
import android.view.SubMenu;
import android.view.View;
import android.widget.TextView;
import com.google.android.material.navigation.NavigationView;
import org.kde.kdeconnect.BackgroundService;
import org.kde.kdeconnect.Device;
import org.kde.kdeconnect.Helpers.DeviceHelper;
import org.kde.kdeconnect_tp.R;
import java.util.Collection;
import java.util.HashMap;
import androidx.annotation.NonNull;
import androidx.appcompat.app.ActionBar;
import androidx.appcompat.app.ActionBarDrawerToggle;
import androidx.appcompat.app.AppCompatActivity;
import androidx.appcompat.widget.Toolbar;
import androidx.core.view.GravityCompat;
import androidx.drawerlayout.widget.DrawerLayout;
import androidx.fragment.app.Fragment;
import butterknife.BindView;
import butterknife.ButterKnife;
public class MainActivity extends AppCompatActivity implements SharedPreferences.OnSharedPreferenceChangeListener {
private static final int MENU_ENTRY_ADD_DEVICE = 1; //0 means no-selection
private static final int MENU_ENTRY_SETTINGS = 2;
private static final int MENU_ENTRY_DEVICE_FIRST_ID = 1000; //All subsequent ids are devices in the menu
private static final int MENU_ENTRY_DEVICE_UNKNOWN = 9999; //It's still a device, but we don't know which one yet
private static final String STATE_SELECTED_MENU_ENTRY = "selected_entry"; //Saved only in onSaveInstanceState
private static final String STATE_SELECTED_DEVICE = "selected_device"; //Saved persistently in preferences
public static final int RESULT_NEEDS_RELOAD = Activity.RESULT_FIRST_USER;
public static final String PAIR_REQUEST_STATUS = "pair_req_status";
public static final String PAIRING_ACCEPTED = "accepted";
public static final String PAIRING_REJECTED = "rejected";
public static final String PAIRING_PENDING = "pending";
public static final String EXTRA_DEVICE_ID = "deviceId";
@BindView(R.id.navigation_drawer) NavigationView mNavigationView;
@BindView(R.id.drawer_layout) DrawerLayout mDrawerLayout;
@BindView(R.id.toolbar) Toolbar mToolbar;
private TextView mNavViewDeviceName;
private String mCurrentDevice;
private int mCurrentMenuEntry;
private SharedPreferences preferences;
private final HashMap