diff --git a/AndroidManifest.xml b/AndroidManifest.xml
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -87,22 +87,6 @@
android:name="android.support.PARENT_ACTIVITY"
android:value="org.kde.kdeconnect.UserInterface.MainActivity" />
-
-
-
-
-
-
@@ -268,12 +252,12 @@
+ android:parentActivityName="org.kde.kdeconnect.UserInterface.DeviceSettingsActivity">
+ android:value="org.kde.kdeconnect.UserInterface.DeviceSettingsActivity" />
\ No newline at end of file
diff --git a/res/layout/activity_device_settings.xml b/res/layout/activity_device_settings.xml
new file mode 100644
--- /dev/null
+++ b/res/layout/activity_device_settings.xml
@@ -0,0 +1,7 @@
+
+
+
\ No newline at end of file
diff --git a/res/layout/preference_with_button.xml b/res/layout/preference_with_button.xml
--- a/res/layout/preference_with_button.xml
+++ b/res/layout/preference_with_button.xml
@@ -34,17 +34,20 @@
android:id="@android:id/widget_frame"
android:layout_width="wrap_content"
android:layout_height="match_parent"
- android:gravity="center_vertical"
- android:orientation="vertical" />
+ android:gravity="left|center_vertical|start"
+ android:minWidth="56dp"
+ android:orientation="vertical">
+
+
diff --git a/res/values/strings.xml b/res/values/strings.xml
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -325,4 +325,5 @@
Block images in notifications
Notifications from other devices
+ findmyphone_ringtone
diff --git a/res/xml/findmyphoneplugin_preferences.xml b/res/xml/findmyphoneplugin_preferences.xml
--- a/res/xml/findmyphoneplugin_preferences.xml
+++ b/res/xml/findmyphoneplugin_preferences.xml
@@ -3,8 +3,7 @@
android:layout_width="match_parent"
android:layout_height="match_parent">
-
\ No newline at end of file
diff --git a/src/org/kde/kdeconnect/BackgroundService.java b/src/org/kde/kdeconnect/BackgroundService.java
--- a/src/org/kde/kdeconnect/BackgroundService.java
+++ b/src/org/kde/kdeconnect/BackgroundService.java
@@ -435,5 +435,4 @@
cb.run(plugin);
});
}
-
}
diff --git a/src/org/kde/kdeconnect/Plugins/FindMyPhonePlugin/FindMyPhoneActivity.java b/src/org/kde/kdeconnect/Plugins/FindMyPhonePlugin/FindMyPhoneActivity.java
--- a/src/org/kde/kdeconnect/Plugins/FindMyPhonePlugin/FindMyPhoneActivity.java
+++ b/src/org/kde/kdeconnect/Plugins/FindMyPhonePlugin/FindMyPhoneActivity.java
@@ -80,7 +80,7 @@
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this);
Uri ringtone;
- String ringtoneString = prefs.getString("select_ringtone", "");
+ String ringtoneString = prefs.getString(getString(R.string.findmyphone_preference_key_ringtone), "");
if (ringtoneString.isEmpty()) {
ringtone = Settings.System.DEFAULT_RINGTONE_URI;
} else {
diff --git a/src/org/kde/kdeconnect/Plugins/FindMyPhonePlugin/FindMyPhonePlugin.java b/src/org/kde/kdeconnect/Plugins/FindMyPhonePlugin/FindMyPhonePlugin.java
--- a/src/org/kde/kdeconnect/Plugins/FindMyPhonePlugin/FindMyPhonePlugin.java
+++ b/src/org/kde/kdeconnect/Plugins/FindMyPhonePlugin/FindMyPhonePlugin.java
@@ -25,6 +25,7 @@
import org.kde.kdeconnect.Helpers.DeviceHelper;
import org.kde.kdeconnect.NetworkPacket;
import org.kde.kdeconnect.Plugins.Plugin;
+import org.kde.kdeconnect.UserInterface.PluginSettingsFragment;
import org.kde.kdeconnect_tp.R;
public class FindMyPhonePlugin extends Plugin {
@@ -75,4 +76,8 @@
return true;
}
+ @Override
+ public PluginSettingsFragment getSettingsFragment() {
+ return FindMyPhoneSettingsFragment.newInstance(getPluginKey());
+ }
}
diff --git a/src/org/kde/kdeconnect/Plugins/FindMyPhonePlugin/FindMyPhoneSettingsFragment.java b/src/org/kde/kdeconnect/Plugins/FindMyPhonePlugin/FindMyPhoneSettingsFragment.java
new file mode 100644
--- /dev/null
+++ b/src/org/kde/kdeconnect/Plugins/FindMyPhonePlugin/FindMyPhoneSettingsFragment.java
@@ -0,0 +1,113 @@
+/*
+ * Copyright 2018 Erik Duisters
+ *
+ * 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.FindMyPhonePlugin;
+
+import android.app.Activity;
+import android.content.Intent;
+import android.content.SharedPreferences;
+import android.media.RingtoneManager;
+import android.net.Uri;
+import android.os.Bundle;
+import android.provider.Settings;
+
+import org.kde.kdeconnect.UserInterface.PluginSettingsFragment;
+import org.kde.kdeconnect_tp.R;
+
+import androidx.annotation.NonNull;
+import androidx.preference.Preference;
+import androidx.preference.PreferenceManager;
+
+public class FindMyPhoneSettingsFragment extends PluginSettingsFragment {
+ private static final int REQUEST_CODE_SELECT_RINGTONE = 1000;
+
+ private String preferenceKeyRingtone;
+ private SharedPreferences sharedPreferences;
+ private Preference ringtonePreference;
+
+ public static FindMyPhoneSettingsFragment newInstance(@NonNull String pluginKey) {
+ FindMyPhoneSettingsFragment fragment = new FindMyPhoneSettingsFragment();
+ fragment.setArguments(pluginKey);
+
+ return fragment;
+ }
+
+ @Override
+ public void onCreatePreferences(Bundle savedInstanceState, String rootKey) {
+ super.onCreatePreferences(savedInstanceState, rootKey);
+
+ preferenceKeyRingtone = getString(R.string.findmyphone_preference_key_ringtone);
+ sharedPreferences = PreferenceManager.getDefaultSharedPreferences(requireContext());
+
+ ringtonePreference = getPreferenceScreen().findPreference(preferenceKeyRingtone);
+
+ setRingtoneSummary();
+ }
+
+ private void setRingtoneSummary() {
+ String ringtone = sharedPreferences.getString(preferenceKeyRingtone, Settings.System.DEFAULT_RINGTONE_URI.toString());
+
+ Uri ringtoneUri = Uri.parse(ringtone);
+
+ ringtonePreference.setSummary(RingtoneManager.getRingtone(requireContext(), ringtoneUri).getTitle(requireContext()));
+ }
+
+ @Override
+ public boolean onPreferenceTreeClick(Preference preference) {
+ /*
+ * There is no RingtonePreference in support library nor androidx, this is the workaround proposed here:
+ * https://issuetracker.google.com/issues/37057453
+ */
+
+ if (preference.hasKey() && preference.getKey().equals(preferenceKeyRingtone)) {
+ Intent intent = new Intent(RingtoneManager.ACTION_RINGTONE_PICKER);
+ intent.putExtra(RingtoneManager.EXTRA_RINGTONE_TYPE, RingtoneManager.TYPE_NOTIFICATION);
+ intent.putExtra(RingtoneManager.EXTRA_RINGTONE_SHOW_DEFAULT, true);
+ intent.putExtra(RingtoneManager.EXTRA_RINGTONE_SHOW_SILENT, false);
+ intent.putExtra(RingtoneManager.EXTRA_RINGTONE_DEFAULT_URI, Settings.System.DEFAULT_NOTIFICATION_URI);
+
+ String existingValue = sharedPreferences.getString(preferenceKeyRingtone, null);
+ if (existingValue != null) {
+ intent.putExtra(RingtoneManager.EXTRA_RINGTONE_EXISTING_URI, Uri.parse(existingValue));
+ } else {
+ intent.putExtra(RingtoneManager.EXTRA_RINGTONE_EXISTING_URI, Settings.System.DEFAULT_RINGTONE_URI);
+ }
+
+ startActivityForResult(intent, REQUEST_CODE_SELECT_RINGTONE);
+ return true;
+ }
+ return super.onPreferenceTreeClick(preference);
+ }
+
+ @Override
+ public void onActivityResult(int requestCode, int resultCode, Intent data) {
+ if (requestCode == REQUEST_CODE_SELECT_RINGTONE && resultCode == Activity.RESULT_OK) {
+ Uri uri = data.getParcelableExtra(RingtoneManager.EXTRA_RINGTONE_PICKED_URI);
+
+ if (uri != null) {
+ sharedPreferences.edit()
+ .putString(preferenceKeyRingtone, uri.toString())
+ .apply();
+
+ setRingtoneSummary();
+ }
+ }
+ }
+}
diff --git a/src/org/kde/kdeconnect/Plugins/NotificationsPlugin/NotificationFilterActivity.java b/src/org/kde/kdeconnect/Plugins/NotificationsPlugin/NotificationFilterActivity.java
--- a/src/org/kde/kdeconnect/Plugins/NotificationsPlugin/NotificationFilterActivity.java
+++ b/src/org/kde/kdeconnect/Plugins/NotificationsPlugin/NotificationFilterActivity.java
@@ -48,6 +48,7 @@
import androidx.appcompat.app.AppCompatActivity;
+//TODO: Turn this into a PluginSettingsFragment
public class NotificationFilterActivity extends AppCompatActivity {
private AppDatabase appDatabase;
diff --git a/src/org/kde/kdeconnect/Plugins/NotificationsPlugin/NotificationsPlugin.java b/src/org/kde/kdeconnect/Plugins/NotificationsPlugin/NotificationsPlugin.java
--- a/src/org/kde/kdeconnect/Plugins/NotificationsPlugin/NotificationsPlugin.java
+++ b/src/org/kde/kdeconnect/Plugins/NotificationsPlugin/NotificationsPlugin.java
@@ -44,8 +44,8 @@
import org.kde.kdeconnect.Helpers.AppsHelper;
import org.kde.kdeconnect.NetworkPacket;
import org.kde.kdeconnect.Plugins.Plugin;
-import org.kde.kdeconnect.UserInterface.DeviceSettingsActivity;
import org.kde.kdeconnect.UserInterface.MainActivity;
+import org.kde.kdeconnect.UserInterface.PluginSettingsFragment;
import org.kde.kdeconnect_tp.R;
import java.io.ByteArrayOutputStream;
@@ -89,13 +89,14 @@
}
@Override
- public void startPreferencesActivity(final DeviceSettingsActivity parentActivity) {
+ public PluginSettingsFragment getSettingsFragment() {
if (hasPermission()) {
- Intent intent = new Intent(parentActivity, NotificationFilterActivity.class);
- parentActivity.startActivity(intent);
- } else {
- getErrorDialog(parentActivity).show();
+ Context context = device.getContext();
+ Intent intent = new Intent(context, NotificationFilterActivity.class);
+ context.startActivity(intent);
}
+
+ return null;
}
private boolean hasPermission() {
@@ -488,10 +489,8 @@
return true;
}
-
@Override
public AlertDialog getErrorDialog(final Activity deviceActivity) {
-
return new AlertDialog.Builder(deviceActivity)
.setTitle(R.string.pref_plugin_notifications)
.setMessage(R.string.no_permissions)
@@ -503,7 +502,6 @@
//Do nothing
})
.create();
-
}
@Override
diff --git a/src/org/kde/kdeconnect/Plugins/Plugin.java b/src/org/kde/kdeconnect/Plugins/Plugin.java
--- a/src/org/kde/kdeconnect/Plugins/Plugin.java
+++ b/src/org/kde/kdeconnect/Plugins/Plugin.java
@@ -23,24 +23,21 @@
import android.app.Activity;
import android.app.AlertDialog;
import android.content.Context;
-import android.content.Intent;
import android.content.pm.PackageManager;
import android.graphics.drawable.Drawable;
import android.os.Build;
import android.widget.Button;
import org.kde.kdeconnect.Device;
import org.kde.kdeconnect.NetworkPacket;
-import org.kde.kdeconnect.UserInterface.DeviceSettingsActivity;
-import org.kde.kdeconnect.UserInterface.PluginSettingsActivity;
+import org.kde.kdeconnect.UserInterface.PluginSettingsFragment;
import org.kde.kdeconnect_tp.R;
import androidx.annotation.StringRes;
import androidx.core.app.ActivityCompat;
import androidx.core.content.ContextCompat;
public abstract class Plugin {
-
protected Device device;
protected Context context;
protected int permissionExplanation = R.string.permission_explanation;
@@ -125,15 +122,13 @@
/**
* If hasSettings returns true, this will be called when the user
- * wants to access this plugin preferences and should launch some
- * kind of interface. The default implementation will launch a
- * PluginSettingsActivity with content from "yourplugin"_preferences.xml.
+ * wants to access this plugin's preferences. The default implementation
+ * will return a PluginSettingsFragment with content from "yourplugin"_preferences.xml
+ *
+ * @return The PluginSettingsFragment used to display this plugins settings
*/
- public void startPreferencesActivity(DeviceSettingsActivity parentActivity) {
- Intent intent = new Intent(parentActivity, PluginSettingsActivity.class);
- intent.putExtra("plugin_display_name", getDisplayName());
- intent.putExtra("plugin_key", getPluginKey());
- parentActivity.startActivity(intent);
+ public PluginSettingsFragment getSettingsFragment() {
+ return PluginSettingsFragment.newInstance(getPluginKey());
}
/**
diff --git a/src/org/kde/kdeconnect/Plugins/RemoteKeyboardPlugin/RemoteKeyboardPlugin.java b/src/org/kde/kdeconnect/Plugins/RemoteKeyboardPlugin/RemoteKeyboardPlugin.java
--- a/src/org/kde/kdeconnect/Plugins/RemoteKeyboardPlugin/RemoteKeyboardPlugin.java
+++ b/src/org/kde/kdeconnect/Plugins/RemoteKeyboardPlugin/RemoteKeyboardPlugin.java
@@ -387,4 +387,8 @@
np.set("state", state);
device.sendPacket(np);
}
+
+ String getDeviceId() {
+ return device.getDeviceId();
+ }
}
diff --git a/src/org/kde/kdeconnect/Plugins/RemoteKeyboardPlugin/RemoteKeyboardService.java b/src/org/kde/kdeconnect/Plugins/RemoteKeyboardPlugin/RemoteKeyboardService.java
--- a/src/org/kde/kdeconnect/Plugins/RemoteKeyboardPlugin/RemoteKeyboardService.java
+++ b/src/org/kde/kdeconnect/Plugins/RemoteKeyboardPlugin/RemoteKeyboardService.java
@@ -33,8 +33,8 @@
import android.view.inputmethod.InputMethodManager;
import android.widget.Toast;
+import org.kde.kdeconnect.UserInterface.DeviceSettingsActivity;
import org.kde.kdeconnect.UserInterface.MainActivity;
-import org.kde.kdeconnect.UserInterface.PluginSettingsActivity;
import org.kde.kdeconnect_tp.R;
import java.util.ArrayList;
@@ -162,10 +162,10 @@
if (instances.size() == 1) { // single instance of RemoteKeyboardPlugin -> access its settings
RemoteKeyboardPlugin plugin = instances.get(0);
if (plugin != null) {
- Intent intent = new Intent(this, PluginSettingsActivity.class);
+ Intent intent = new Intent(this, DeviceSettingsActivity.class);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
- intent.putExtra("plugin_display_name", plugin.getDisplayName());
- intent.putExtra("plugin_key", plugin.getPluginKey());
+ intent.putExtra(DeviceSettingsActivity.EXTRA_DEVICE_ID, plugin.getDeviceId());
+ intent.putExtra(DeviceSettingsActivity.EXTRA_PLUGIN_KEY, plugin.getPluginKey());
startActivity(intent);
}
} else { // != 1 instance of plugin -> show main activity view
diff --git a/src/org/kde/kdeconnect/Plugins/SharePlugin/SharePlugin.java b/src/org/kde/kdeconnect/Plugins/SharePlugin/SharePlugin.java
--- a/src/org/kde/kdeconnect/Plugins/SharePlugin/SharePlugin.java
+++ b/src/org/kde/kdeconnect/Plugins/SharePlugin/SharePlugin.java
@@ -47,7 +47,7 @@
import org.kde.kdeconnect.Helpers.NotificationHelper;
import org.kde.kdeconnect.NetworkPacket;
import org.kde.kdeconnect.Plugins.Plugin;
-import org.kde.kdeconnect.UserInterface.DeviceSettingsActivity;
+import org.kde.kdeconnect.UserInterface.PluginSettingsFragment;
import org.kde.kdeconnect_tp.R;
import java.io.BufferedOutputStream;
@@ -227,12 +227,12 @@
//We need to check for already existing files only when storing in the default path.
//User-defined paths use the new Storage Access Framework that already handles this.
//If the file should be opened immediately store it in the standard location to avoid the FileProvider trouble (See ShareNotification::setURI)
- if (np.getBoolean("open") || !ShareSettingsActivity.isCustomDestinationEnabled(context)) {
- final String defaultPath = ShareSettingsActivity.getDefaultDestinationDirectory().getAbsolutePath();
+ if (np.getBoolean("open") || !ShareSettingsFragment.isCustomDestinationEnabled(context)) {
+ final String defaultPath = ShareSettingsFragment.getDefaultDestinationDirectory().getAbsolutePath();
filename = FilesHelper.findNonExistingNameForNewFile(defaultPath, filename);
destinationFolderDocument = DocumentFile.fromFile(new File(defaultPath));
} else {
- destinationFolderDocument = ShareSettingsActivity.getDestinationDirectory(context);
+ destinationFolderDocument = ShareSettingsFragment.getDestinationDirectory(context);
}
String displayName = FilesHelper.getFileNameWithoutExt(filename);
String mimeType = FilesHelper.getMimeTypeFromFile(filename);
@@ -273,11 +273,8 @@
}
@Override
- public void startPreferencesActivity(DeviceSettingsActivity parentActivity) {
- Intent intent = new Intent(parentActivity, ShareSettingsActivity.class);
- intent.putExtra("plugin_display_name", getDisplayName());
- intent.putExtra("plugin_key", getPluginKey());
- parentActivity.startActivity(intent);
+ public PluginSettingsFragment getSettingsFragment() {
+ return ShareSettingsFragment.newInstance(getPluginKey());
}
void queuedSendUriList(final ArrayList uriList) {
@@ -489,7 +486,7 @@
context.startActivity(intent);
} else {
- if (!ShareSettingsActivity.isCustomDestinationEnabled(context)) {
+ if (!ShareSettingsFragment.isCustomDestinationEnabled(context)) {
Log.i("SharePlugin", "Adding to downloads");
DownloadManager manager = (DownloadManager) context.getSystemService(Context.DOWNLOAD_SERVICE);
manager.addCompletedDownload(info.fileDocument.getUri().getLastPathSegment(), device.getName(), true, info.fileDocument.getType(), info.fileDocument.getUri().getPath(), info.fileSize, false);
diff --git a/src/org/kde/kdeconnect/Plugins/SharePlugin/ShareSettingsActivity.java b/src/org/kde/kdeconnect/Plugins/SharePlugin/ShareSettingsFragment.java
rename from src/org/kde/kdeconnect/Plugins/SharePlugin/ShareSettingsActivity.java
rename to src/org/kde/kdeconnect/Plugins/SharePlugin/ShareSettingsFragment.java
--- a/src/org/kde/kdeconnect/Plugins/SharePlugin/ShareSettingsActivity.java
+++ b/src/org/kde/kdeconnect/Plugins/SharePlugin/ShareSettingsFragment.java
@@ -1,3 +1,23 @@
+/*
+ * Copyright 2016 Richard Wagler
+ *
+ * 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.SharePlugin;
import android.annotation.TargetApi;
@@ -9,32 +29,42 @@
import android.os.Build;
import android.os.Bundle;
import android.os.Environment;
-import android.preference.CheckBoxPreference;
-import android.preference.Preference;
-import android.preference.PreferenceManager;
import android.util.Log;
-import org.kde.kdeconnect.UserInterface.PluginSettingsActivity;
+import org.kde.kdeconnect.UserInterface.PluginSettingsFragment;
import java.io.File;
+import androidx.annotation.NonNull;
import androidx.documentfile.provider.DocumentFile;
+import androidx.preference.CheckBoxPreference;
+import androidx.preference.Preference;
+import androidx.preference.PreferenceManager;
+import androidx.preference.PreferenceScreen;
-public class ShareSettingsActivity extends PluginSettingsActivity {
+public class ShareSettingsFragment extends PluginSettingsFragment {
private final static String PREFERENCE_CUSTOMIZE_DESTINATION = "share_destination_custom";
private final static String PREFERENCE_DESTINATION = "share_destination_folder_uri";
private static final int RESULT_PICKER = Activity.RESULT_FIRST_USER;
private Preference filePicker;
+ public static ShareSettingsFragment newInstance(@NonNull String pluginKey) {
+ ShareSettingsFragment fragment = new ShareSettingsFragment();
+ fragment.setArguments(pluginKey);
+
+ return fragment;
+ }
+
@Override
- public void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
+ public void onCreatePreferences(Bundle savedInstanceState, String rootKey) {
+ super.onCreatePreferences(savedInstanceState, rootKey);
- final CheckBoxPreference customDownloads = (CheckBoxPreference) findPreference("share_destination_custom");
- filePicker = findPreference("share_destination_folder_preference");
+ PreferenceScreen preferenceScreen = getPreferenceScreen();
+ final CheckBoxPreference customDownloads = (CheckBoxPreference) preferenceScreen.findPreference("share_destination_custom");
+ filePicker = preferenceScreen.findPreference("share_destination_folder_preference");
if ((Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP)) {
customDownloads.setOnPreferenceChangeListener((preference, newValue) -> {
@@ -51,13 +81,19 @@
filePicker.setEnabled(false);
}
- boolean customized = PreferenceManager.getDefaultSharedPreferences(this).getBoolean(PREFERENCE_CUSTOMIZE_DESTINATION, false);
+ boolean customized = PreferenceManager
+ .getDefaultSharedPreferences(requireContext())
+ .getBoolean(PREFERENCE_CUSTOMIZE_DESTINATION, false);
+
updateFilePickerStatus(customized);
}
private void updateFilePickerStatus(boolean enabled) {
filePicker.setEnabled(enabled);
- String path = PreferenceManager.getDefaultSharedPreferences(this).getString(PREFERENCE_DESTINATION, null);
+ String path = PreferenceManager
+ .getDefaultSharedPreferences(requireContext())
+ .getString(PREFERENCE_DESTINATION, null);
+
if (enabled && path != null) {
filePicker.setSummary(Uri.parse(path).getPath());
} else {
@@ -99,22 +135,20 @@
@TargetApi(Build.VERSION_CODES.KITKAT)
@Override
public void onActivityResult(int requestCode, int resultCode, Intent resultData) {
-
if (requestCode == RESULT_PICKER
&& resultCode == Activity.RESULT_OK
&& resultData != null) {
Uri uri = resultData.getData();
- getContentResolver().takePersistableUriPermission(uri, Intent.FLAG_GRANT_READ_URI_PERMISSION |
+ requireContext().getContentResolver().takePersistableUriPermission(uri, Intent.FLAG_GRANT_READ_URI_PERMISSION |
Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
Preference filePicker = findPreference("share_destination_folder_preference");
filePicker.setSummary(uri.getPath());
- SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this);
+ SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(requireContext());
prefs.edit().putString(PREFERENCE_DESTINATION, uri.toString()).apply();
}
}
-
}
diff --git a/src/org/kde/kdeconnect/UserInterface/DeviceSettingsActivity.java b/src/org/kde/kdeconnect/UserInterface/DeviceSettingsActivity.java
--- a/src/org/kde/kdeconnect/UserInterface/DeviceSettingsActivity.java
+++ b/src/org/kde/kdeconnect/UserInterface/DeviceSettingsActivity.java
@@ -21,52 +21,79 @@
package org.kde.kdeconnect.UserInterface;
import android.os.Bundle;
-import android.preference.PreferenceScreen;
import android.view.MenuItem;
import org.kde.kdeconnect.BackgroundService;
import org.kde.kdeconnect.Device;
+import org.kde.kdeconnect.Plugins.Plugin;
+import org.kde.kdeconnect_tp.R;
-import java.util.List;
+import androidx.appcompat.app.AppCompatActivity;
+import androidx.fragment.app.Fragment;
+import androidx.fragment.app.FragmentManager;
-public class DeviceSettingsActivity extends AppCompatPreferenceActivity {
+public class DeviceSettingsActivity
+ extends AppCompatActivity
+ implements PluginPreference.PluginPreferenceCallback {
+ public static final String EXTRA_DEVICE_ID = "deviceId";
+ public static final String EXTRA_PLUGIN_KEY = "pluginKey";
+
+ //TODO: Save/restore state
static private String deviceId; //Static because if we get here by using the back button in the action bar, the extra deviceId will not be set.
@Override
public void onCreate(Bundle savedInstanceState) {
+ ThemeUtil.setUserPreferredTheme(this);
super.onCreate(savedInstanceState);
- final PreferenceScreen preferenceScreen = getPreferenceManager().createPreferenceScreen(this);
- setPreferenceScreen(preferenceScreen);
+ setContentView(R.layout.activity_device_settings);
- if (getIntent().hasExtra("deviceId")) {
- deviceId = getIntent().getStringExtra("deviceId");
+ if (getSupportActionBar() != null) {
+ getSupportActionBar().setDefaultDisplayHomeAsUpEnabled(true);
}
- BackgroundService.RunCommand(getApplicationContext(), service -> {
- final Device device = service.getDevice(deviceId);
- if (device == null) {
- DeviceSettingsActivity.this.runOnUiThread(DeviceSettingsActivity.this::finish);
- return;
+ String pluginKey = null;
+
+ if (getIntent().hasExtra(EXTRA_DEVICE_ID)) {
+ deviceId = getIntent().getStringExtra(EXTRA_DEVICE_ID);
+
+ if (getIntent().hasExtra(EXTRA_PLUGIN_KEY)) {
+ pluginKey = getIntent().getStringExtra(EXTRA_PLUGIN_KEY);
}
- List plugins = device.getSupportedPlugins();
- for (final String pluginKey : plugins) {
- PluginPreference pref = new PluginPreference(DeviceSettingsActivity.this, pluginKey, device);
- preferenceScreen.addPreference(pref);
+ } else if (deviceId == null) {
+ throw new RuntimeException("You must start DeviceSettingActivity using an intent that has a " + EXTRA_DEVICE_ID + " extra");
+ }
+
+ Fragment fragment = getSupportFragmentManager().findFragmentById(R.id.fragmentPlaceHolder);
+ if (fragment == null) {
+ if (pluginKey == null) {
+ fragment = DeviceSettingsFragment.newInstance(deviceId);
+ } else {
+ Device device = BackgroundService.getInstance().getDevice(deviceId);
+ Plugin plugin = device.getPlugin(pluginKey, true);
+ fragment = plugin.getSettingsFragment();
}
- });
+
+ getSupportFragmentManager()
+ .beginTransaction()
+ .add(R.id.fragmentPlaceHolder, fragment)
+ .commit();
+ }
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
- //ActionBar's back button
if (item.getItemId() == android.R.id.home) {
- finish();
- return true;
- } else {
- return super.onOptionsItemSelected(item);
+ FragmentManager fm = getSupportFragmentManager();
+
+ if (fm.getBackStackEntryCount() > 0) {
+ fm.popBackStack();
+ return true;
+ }
}
+
+ return super.onOptionsItemSelected(item);
}
@Override
@@ -81,4 +108,27 @@
BackgroundService.removeGuiInUseCounter(this);
}
+ @Override
+ public void onStartPluginSettingsFragment(Plugin plugin) {
+ setTitle(getString(R.string.plugin_settings_with_name, plugin.getDisplayName()));
+
+ PluginSettingsFragment fragment = plugin.getSettingsFragment();
+
+ //TODO: Remove when NotificationFilterActivity has been turned into a PluginSettingsFragment
+ if (fragment == null) {
+ return;
+ }
+
+ getSupportFragmentManager()
+ .beginTransaction()
+ .replace(R.id.fragmentPlaceHolder, fragment)
+ .addToBackStack(null)
+ .commit();
+
+ }
+
+ @Override
+ public void onFinish() {
+ finish();
+ }
}
diff --git a/src/org/kde/kdeconnect/UserInterface/DeviceSettingsFragment.java b/src/org/kde/kdeconnect/UserInterface/DeviceSettingsFragment.java
new file mode 100644
--- /dev/null
+++ b/src/org/kde/kdeconnect/UserInterface/DeviceSettingsFragment.java
@@ -0,0 +1,175 @@
+/*
+ * Copyright 2018 Erik Duisters
+ *
+ * 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.UserInterface;
+
+import android.os.Bundle;
+import android.os.Parcelable;
+
+import org.kde.kdeconnect.BackgroundService;
+import org.kde.kdeconnect.Device;
+import org.kde.kdeconnect_tp.R;
+
+import java.util.List;
+
+import androidx.annotation.NonNull;
+import androidx.fragment.app.FragmentActivity;
+import androidx.preference.PreferenceFragmentCompat;
+import androidx.preference.PreferenceScreen;
+import androidx.recyclerview.widget.RecyclerView;
+
+public class DeviceSettingsFragment extends PreferenceFragmentCompat {
+ private static final String ARG_DEVICE_ID = "deviceId";
+ private static final String KEY_RECYCLERVIEW_LAYOUTMANAGER_STATE = "RecyclerViewLayoutmanagerState";
+
+ private PluginPreference.PluginPreferenceCallback callback;
+ private Parcelable recyclerViewLayoutManagerState;
+
+ /*
+ https://bricolsoftconsulting.com/state-preservation-in-backstack-fragments/
+ When adding a fragment to the backstack the fragments onDestroyView is called (which releases
+ the RecyclerView) but the fragments onSaveInstanceState is not called. When the fragment is destroyed later
+ on, its onSaveInstanceState() is called but I don't have access to the RecyclerView or it's LayoutManager any more
+ */
+ private boolean stateSaved;
+
+ public static DeviceSettingsFragment newInstance(@NonNull String deviceId) {
+ DeviceSettingsFragment fragment = new DeviceSettingsFragment();
+
+ Bundle args = new Bundle();
+ args.putString(ARG_DEVICE_ID, deviceId);
+ fragment.setArguments(args);
+
+ return fragment;
+ }
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ if (requireActivity() instanceof PluginPreference.PluginPreferenceCallback) {
+ callback = (PluginPreference.PluginPreferenceCallback) getActivity();
+ } else {
+ throw new RuntimeException(requireActivity().getClass().getSimpleName()
+ + " must implement PluginPreference.PluginPreferenceCallback");
+ }
+
+ super.onCreate(savedInstanceState);
+
+ if (savedInstanceState != null && savedInstanceState.containsKey(KEY_RECYCLERVIEW_LAYOUTMANAGER_STATE)) {
+ recyclerViewLayoutManagerState = savedInstanceState.getParcelable(KEY_RECYCLERVIEW_LAYOUTMANAGER_STATE);
+ }
+ }
+
+ @Override
+ public void onDestroy() {
+ super.onDestroy();
+
+ callback = null;
+ }
+
+ @Override
+ public void onCreatePreferences(Bundle savedInstanceState, String rootKey) {
+ PreferenceScreen preferenceScreen = getPreferenceManager().createPreferenceScreen(requireContext());
+ setPreferenceScreen(preferenceScreen);
+
+ final String deviceId = getArguments().getString(ARG_DEVICE_ID);
+
+ BackgroundService.RunCommand(requireContext(), service -> {
+ final Device device = service.getDevice(deviceId);
+ if (device == null) {
+ final FragmentActivity activity = requireActivity();
+ activity.runOnUiThread(() -> activity.finish());
+ return;
+ }
+ List plugins = device.getSupportedPlugins();
+
+ for (final String pluginKey : plugins) {
+ PluginPreference pref = new PluginPreference(requireContext(), pluginKey, device, callback);
+ preferenceScreen.addPreference(pref);
+ }
+ });
+ }
+
+ @Override
+ protected RecyclerView.Adapter onCreateAdapter(PreferenceScreen preferenceScreen) {
+ RecyclerView.Adapter adapter = super.onCreateAdapter(preferenceScreen);
+
+ /*
+ The layoutmanager's state (e.g. scroll position) can only be restored when the recyclerView's
+ adapter has been re-populated with data.
+ */
+ if (recyclerViewLayoutManagerState != null) {
+ adapter.registerAdapterDataObserver(new RecyclerView.AdapterDataObserver() {
+ @Override
+ public void onChanged() {
+ RecyclerView.LayoutManager layoutManager = getListView().getLayoutManager();
+
+ if (layoutManager != null) {
+ layoutManager.onRestoreInstanceState(recyclerViewLayoutManagerState);
+ }
+
+ recyclerViewLayoutManagerState = null;
+ adapter.unregisterAdapterDataObserver(this);
+ }
+ });
+ }
+
+ return adapter;
+ }
+
+ @Override
+ public void onPause() {
+ super.onPause();
+
+ stateSaved = false;
+ }
+
+ @Override
+ public void onResume() {
+ super.onResume();
+
+ requireActivity().setTitle(getString(R.string.device_menu_plugins));
+ }
+
+ @Override
+ public void onDestroyView() {
+ if (!stateSaved && getListView() != null && getListView().getLayoutManager() != null) {
+ recyclerViewLayoutManagerState = getListView().getLayoutManager().onSaveInstanceState();
+ }
+
+ super.onDestroyView();
+ }
+
+ @Override
+ public void onSaveInstanceState(Bundle outState) {
+ super.onSaveInstanceState(outState);
+
+ Parcelable layoutManagerState = recyclerViewLayoutManagerState;
+
+ if (getListView() != null && getListView().getLayoutManager() != null) {
+ layoutManagerState = getListView().getLayoutManager().onSaveInstanceState();
+ }
+
+ if (layoutManagerState != null) {
+ outState.putParcelable(KEY_RECYCLERVIEW_LAYOUTMANAGER_STATE, layoutManagerState);
+ }
+
+ stateSaved = true;
+ }
+}
diff --git a/src/org/kde/kdeconnect/UserInterface/PluginPreference.java b/src/org/kde/kdeconnect/UserInterface/PluginPreference.java
--- a/src/org/kde/kdeconnect/UserInterface/PluginPreference.java
+++ b/src/org/kde/kdeconnect/UserInterface/PluginPreference.java
@@ -1,66 +1,76 @@
package org.kde.kdeconnect.UserInterface;
-import android.preference.CheckBoxPreference;
+import android.content.Context;
import android.view.View;
import org.kde.kdeconnect.Device;
import org.kde.kdeconnect.Plugins.Plugin;
import org.kde.kdeconnect.Plugins.PluginFactory;
import org.kde.kdeconnect_tp.R;
-class PluginPreference extends CheckBoxPreference {
+import androidx.annotation.NonNull;
+import androidx.preference.CheckBoxPreference;
+import androidx.preference.PreferenceViewHolder;
+class PluginPreference extends CheckBoxPreference {
private final Device device;
private final String pluginKey;
private final View.OnClickListener listener;
- public PluginPreference(final DeviceSettingsActivity activity, final String pluginKey, final Device device) {
- super(activity);
+ public PluginPreference(@NonNull final Context context, @NonNull final String pluginKey,
+ @NonNull final Device device, @NonNull PluginPreferenceCallback callback) {
+ super(context);
- setLayoutResource(R.layout.preference_with_button);
+ setLayoutResource(R.layout.preference_with_button/*R.layout.preference_with_button_androidx*/);
this.device = device;
this.pluginKey = pluginKey;
- PluginFactory.PluginInfo info = PluginFactory.getPluginInfo(activity, pluginKey);
+ PluginFactory.PluginInfo info = PluginFactory.getPluginInfo(context, pluginKey);
setTitle(info.getDisplayName());
setSummary(info.getDescription());
+setIcon(android.R.color.transparent);
setChecked(device.isPluginEnabled(pluginKey));
Plugin plugin = device.getPlugin(pluginKey, true);
if (info.hasSettings() && plugin != null) {
this.listener = v -> {
Plugin plugin1 = device.getPlugin(pluginKey, true);
if (plugin1 != null) {
- plugin1.startPreferencesActivity(activity);
+ callback.onStartPluginSettingsFragment(plugin1);
} else { //Could happen if the device is not connected anymore
- activity.finish(); //End this activity so we go to the "device not reachable" screen
+ callback.onFinish();
}
};
} else {
this.listener = null;
}
-
}
@Override
- protected void onBindView(View root) {
- super.onBindView(root);
- final View button = root.findViewById(R.id.settingsButton);
+ public void onBindViewHolder(PreferenceViewHolder holder) {
+ super.onBindViewHolder(holder);
+
+ final View button = holder.findViewById(R.id.settingsButton);
if (listener == null) {
button.setVisibility(View.GONE);
} else {
button.setEnabled(isChecked());
+ button.setVisibility(View.VISIBLE);
button.setOnClickListener(listener);
}
- root.setOnClickListener(v -> {
+ holder.itemView.setOnClickListener(v -> {
boolean newState = !device.isPluginEnabled(pluginKey);
setChecked(newState); //It actually works on API<14
button.setEnabled(newState);
device.setPluginEnabled(pluginKey, newState);
});
}
+ interface PluginPreferenceCallback {
+ void onStartPluginSettingsFragment(Plugin plugin);
+ void onFinish();
+ }
}
diff --git a/src/org/kde/kdeconnect/UserInterface/PluginSettingsActivity.java b/src/org/kde/kdeconnect/UserInterface/PluginSettingsActivity.java
deleted file mode 100644
--- a/src/org/kde/kdeconnect/UserInterface/PluginSettingsActivity.java
+++ /dev/null
@@ -1,68 +0,0 @@
-/*
- * Copyright 2014 Ronny Yabar Aizcorbe
- *
- * 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.UserInterface;
-
-import android.os.Bundle;
-import android.view.MenuItem;
-
-import org.kde.kdeconnect.BackgroundService;
-import org.kde.kdeconnect_tp.R;
-
-import java.util.Locale;
-
-public class PluginSettingsActivity extends AppCompatPreferenceActivity {
-
- @Override
- public void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
-
- String pluginDisplayName = getIntent().getStringExtra("plugin_display_name");
- setTitle(getString(R.string.plugin_settings_with_name, pluginDisplayName));
-
- String pluginKey = getIntent().getStringExtra("plugin_key");
- int resFile = getResources().getIdentifier(pluginKey.toLowerCase(Locale.ENGLISH) + "_preferences", "xml", getPackageName());
- addPreferencesFromResource(resFile);
- }
-
- @Override
- public boolean onOptionsItemSelected(MenuItem item) {
- //ActionBar's back button
- if (item.getItemId() == android.R.id.home) {
- finish();
- return true;
- } else {
- return super.onOptionsItemSelected(item);
- }
- }
-
- @Override
- protected void onStart() {
- super.onStart();
- BackgroundService.addGuiInUseCounter(this);
- }
-
- @Override
- protected void onStop() {
- super.onStop();
- BackgroundService.removeGuiInUseCounter(this);
- }
-
-}
diff --git a/src/org/kde/kdeconnect/UserInterface/PluginSettingsFragment.java b/src/org/kde/kdeconnect/UserInterface/PluginSettingsFragment.java
new file mode 100644
--- /dev/null
+++ b/src/org/kde/kdeconnect/UserInterface/PluginSettingsFragment.java
@@ -0,0 +1,81 @@
+/*
+ * Copyright 2018 Erik Duisters
+ *
+ * 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.UserInterface;
+
+import android.os.Bundle;
+
+import org.kde.kdeconnect.Plugins.PluginFactory;
+import org.kde.kdeconnect_tp.R;
+
+import java.util.Locale;
+
+import androidx.annotation.NonNull;
+import androidx.preference.PreferenceFragmentCompat;
+
+public class PluginSettingsFragment extends PreferenceFragmentCompat {
+ public static final String ARG_PLUGIN_KEY = "plugin_key";
+
+ private String pluginKey;
+
+ public static PluginSettingsFragment newInstance(@NonNull String pluginKey) {
+ PluginSettingsFragment fragment = new PluginSettingsFragment();
+ fragment.setArguments(pluginKey);
+
+ return fragment;
+ }
+
+ public PluginSettingsFragment() {}
+
+ protected Bundle setArguments(@NonNull String pluginKey) {
+ Bundle args = new Bundle();
+ args.putString(ARG_PLUGIN_KEY, pluginKey);
+
+ setArguments(args);
+
+ return args;
+ }
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ if (getArguments() == null || !getArguments().containsKey(ARG_PLUGIN_KEY)) {
+ throw new RuntimeException("You must provide a pluginKey by calling setArguments(@NonNull String pluginKey)");
+ }
+
+ pluginKey = getArguments().getString(ARG_PLUGIN_KEY);
+
+ super.onCreate(savedInstanceState);
+ }
+
+ @Override
+ public void onCreatePreferences(Bundle savedInstanceState, String rootKey) {
+ int resFile = getResources().getIdentifier(pluginKey.toLowerCase(Locale.ENGLISH) + "_preferences", "xml",
+ requireContext().getPackageName());
+ addPreferencesFromResource(resFile);
+ }
+
+ @Override
+ public void onResume() {
+ super.onResume();
+
+ PluginFactory.PluginInfo info = PluginFactory.getPluginInfo(requireContext(), pluginKey);
+ requireActivity().setTitle(getString(R.string.plugin_settings_with_name, info.getDisplayName()));
+ }
+}