2
0
mirror of https://github.com/KDE/kdeconnect-android synced 2025-09-05 16:45:08 +00:00
Files
kdeconnect-android/src/org/kde/kdeconnect/UserInterface/DeviceFragment.java

377 lines
14 KiB
Java
Raw Normal View History

2015-08-20 00:59:21 -07:00
/*
2020-08-17 16:17:20 +02:00
* SPDX-FileCopyrightText: 2014 Albert Vaca Cintora <albertvaka@gmail.com>
2015-08-20 00:59:21 -07:00
*
2020-08-17 16:17:20 +02:00
* SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
*/
2015-08-20 00:59:21 -07:00
2015-09-07 01:49:12 -07:00
package org.kde.kdeconnect.UserInterface;
2015-08-20 00:59:21 -07:00
import android.content.Context;
2015-08-20 00:59:21 -07:00
import android.content.Intent;
import android.os.Build;
2015-08-20 00:59:21 -07:00
import android.os.Bundle;
import android.util.Log;
import android.view.KeyEvent;
2015-08-20 00:59:21 -07:00
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.View;
import android.view.ViewGroup;
import androidx.annotation.NonNull;
2019-04-19 23:34:42 +02:00
import androidx.annotation.Nullable;
import androidx.appcompat.app.AlertDialog;
import androidx.fragment.app.DialogFragment;
2019-04-19 23:34:42 +02:00
import androidx.fragment.app.Fragment;
import com.klinker.android.send_message.Utils;
2015-08-20 00:59:21 -07:00
import org.kde.kdeconnect.BackgroundService;
import org.kde.kdeconnect.Device;
import org.kde.kdeconnect.Helpers.SecurityHelpers.SslHelper;
import org.kde.kdeconnect.Helpers.TelephonyHelper;
2015-08-20 00:59:21 -07:00
import org.kde.kdeconnect.Plugins.Plugin;
import org.kde.kdeconnect.Plugins.SMSPlugin.SMSPlugin;
import org.kde.kdeconnect.UserInterface.List.FailedPluginListItem;
2015-08-20 00:59:21 -07:00
import org.kde.kdeconnect.UserInterface.List.ListAdapter;
2016-06-09 13:42:15 +02:00
import org.kde.kdeconnect.UserInterface.List.PluginItem;
2020-07-08 05:15:07 +05:30
import org.kde.kdeconnect.UserInterface.List.PluginListHeaderItem;
import org.kde.kdeconnect.UserInterface.List.SetDefaultAppPluginListItem;
2015-08-20 00:59:21 -07:00
import org.kde.kdeconnect_tp.R;
2020-07-08 05:15:07 +05:30
import org.kde.kdeconnect_tp.databinding.ActivityDeviceBinding;
2015-08-20 00:59:21 -07:00
import java.util.ArrayList;
import java.util.Collection;
import java.util.ConcurrentModificationException;
import java.util.List;
2015-11-12 06:41:14 -08:00
import java.util.concurrent.ConcurrentHashMap;
2015-08-20 00:59:21 -07:00
/**
* Main view. Displays the current device and its plugins
*/
public class DeviceFragment extends Fragment {
2018-10-26 23:53:58 +02:00
private static final String ARG_DEVICE_ID = "deviceId";
private static final String ARG_FROM_DEVICE_LIST = "fromDeviceList";
2015-08-20 00:59:21 -07:00
2019-05-21 00:21:01 +02:00
private static final String TAG = "KDE/DeviceFragment";
private String mDeviceId;
2018-10-26 23:53:58 +02:00
private Device device;
2018-10-26 23:53:58 +02:00
private MainActivity mActivity;
2018-10-26 23:53:58 +02:00
private ArrayList<ListAdapter.Item> pluginListItems;
2020-07-08 05:15:07 +05:30
private ActivityDeviceBinding binding;
public DeviceFragment() {
}
2015-08-20 00:59:21 -07:00
public static DeviceFragment newInstance(String deviceId, boolean fromDeviceList) {
DeviceFragment frag = new DeviceFragment();
Bundle args = new Bundle();
args.putString(ARG_DEVICE_ID, deviceId);
2017-07-24 16:39:00 +02:00
args.putBoolean(ARG_FROM_DEVICE_LIST, fromDeviceList);
frag.setArguments(args);
return frag;
}
@Override
2020-07-08 05:15:07 +05:30
public void onAttach(@NonNull Context context) {
2019-04-19 23:34:14 +02:00
super.onAttach(context);
mActivity = ((MainActivity) getActivity());
}
@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (getArguments() == null || !getArguments().containsKey(ARG_DEVICE_ID)) {
throw new RuntimeException("You must instantiate a new DeviceFragment using DeviceFragment.newInstance()");
}
mDeviceId = getArguments().getString(ARG_DEVICE_ID);
}
2015-08-20 00:59:21 -07:00
@Override
public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container,
2015-08-20 00:59:21 -07:00
Bundle savedInstanceState) {
2020-07-08 05:15:07 +05:30
binding = ActivityDeviceBinding.inflate(inflater, container, false);
binding.pairButton.setOnClickListener(v -> {
binding.pairButton.setVisibility(View.GONE);
binding.pairMessage.setText("");
binding.pairVerification.setVisibility(View.VISIBLE);
binding.pairVerification.setText(SslHelper.getVerificationKey(SslHelper.certificate, device.certificate));
2020-07-08 05:15:07 +05:30
binding.pairProgress.setVisibility(View.VISIBLE);
BackgroundService.RunCommand(mActivity, service -> {
device = service.getDevice(mDeviceId);
if (device == null) return;
device.requestPairing();
});
});
binding.acceptButton.setOnClickListener(v -> {
if (device != null) {
device.acceptPairing();
binding.pairingButtons.setVisibility(View.GONE);
}
});
binding.rejectButton.setOnClickListener(v -> {
if (device != null) {
//Remove listener so buttons don't show for a while before changing the view
device.removePluginsChangedListener(pluginsChangedListener);
device.removePairingCallback(pairingCallback);
device.rejectPairing();
}
mActivity.onDeviceSelected(null);
});
2015-08-20 00:59:21 -07:00
setHasOptionsMenu(true);
BackgroundService.RunCommand(mActivity, service -> {
device = service.getDevice(mDeviceId);
if (device == null) {
2019-05-21 00:21:01 +02:00
Log.e(TAG, "Trying to display a device fragment but the device is not present");
mActivity.onDeviceSelected(null);
return;
}
mActivity.getSupportActionBar().setTitle(device.getName());
device.addPairingCallback(pairingCallback);
device.addPluginsChangedListener(pluginsChangedListener);
refreshUI();
2015-08-20 00:59:21 -07:00
});
2020-07-08 05:15:07 +05:30
return binding.getRoot();
}
String getDeviceId() { return mDeviceId; }
private final Device.PluginsChangedListener pluginsChangedListener = device -> refreshUI();
2015-08-20 00:59:21 -07:00
@Override
public void onDestroyView() {
BackgroundService.RunCommand(mActivity, service -> {
Device device = service.getDevice(mDeviceId);
if (device == null) return;
device.removePluginsChangedListener(pluginsChangedListener);
device.removePairingCallback(pairingCallback);
2015-08-20 00:59:21 -07:00
});
2015-08-20 00:59:21 -07:00
super.onDestroyView();
2020-07-08 05:15:07 +05:30
binding = null;
2015-08-20 00:59:21 -07:00
}
@Override
2020-07-08 05:15:07 +05:30
public void onPrepareOptionsMenu(@NonNull Menu menu) {
2015-08-20 00:59:21 -07:00
super.onPrepareOptionsMenu(menu);
menu.clear();
if (device == null) {
2015-08-20 00:59:21 -07:00
return;
}
//Plugins button list
final Collection<Plugin> plugins = device.getLoadedPlugins().values();
for (final Plugin p : plugins) {
if (!p.displayInContextMenu()) {
continue;
}
menu.add(p.getActionName()).setOnMenuItemClickListener(item -> {
p.startMainActivity(mActivity);
return true;
2015-08-20 00:59:21 -07:00
});
}
menu.add(R.string.device_menu_plugins).setOnMenuItemClickListener(menuItem -> {
Intent intent = new Intent(mActivity, PluginSettingsActivity.class);
intent.putExtra("deviceId", mDeviceId);
startActivity(intent);
return true;
2015-08-20 00:59:21 -07:00
});
if (device.isReachable()) {
menu.add(R.string.encryption_info_title).setOnMenuItemClickListener(menuItem -> {
Context context = mActivity;
AlertDialog.Builder builder = new AlertDialog.Builder(context);
builder.setTitle(context.getResources().getString(R.string.encryption_info_title));
builder.setPositiveButton(context.getResources().getString(R.string.ok), (dialog, id) -> dialog.dismiss());
if (device.certificate == null) {
builder.setMessage(R.string.encryption_info_msg_no_ssl);
} else {
builder.setMessage(context.getResources().getString(R.string.my_device_fingerprint) + "\n" + SslHelper.getCertificateHash(SslHelper.certificate) + "\n\n"
+ context.getResources().getString(R.string.remote_device_fingerprint) + "\n" + SslHelper.getCertificateHash(device.certificate));
}
2019-10-27 23:43:13 +01:00
builder.show();
return true;
});
}
if (device.isPaired()) {
menu.add(R.string.device_menu_unpair).setOnMenuItemClickListener(menuItem -> {
//Remove listener so buttons don't show for a while before changing the view
device.removePluginsChangedListener(pluginsChangedListener);
device.removePairingCallback(pairingCallback);
device.unpair();
mActivity.onDeviceSelected(null);
return true;
});
}
2015-08-20 00:59:21 -07:00
}
@Override
public void onResume() {
super.onResume();
getView().setFocusableInTouchMode(true);
getView().requestFocus();
getView().setOnKeyListener((v, keyCode, event) -> {
if (event.getAction() == KeyEvent.ACTION_UP && keyCode == KeyEvent.KEYCODE_BACK) {
boolean fromDeviceList = getArguments().getBoolean(ARG_FROM_DEVICE_LIST, false);
// Handle back button so we go to the list of devices in case we came from there
if (fromDeviceList) {
mActivity.onDeviceSelected(null);
return true;
}
}
return false;
});
}
2018-10-26 23:53:58 +02:00
private void refreshUI() {
2020-07-08 05:15:07 +05:30
if (device == null || binding.getRoot() == null) {
return;
}
//Once in-app, there is no point in keep displaying the notification if any
device.hidePairingNotification();
mActivity.runOnUiThread(new Runnable() {
@Override
public void run() {
2016-01-10 03:16:14 -08:00
if (device.isPairRequestedByPeer()) {
2020-07-08 05:15:07 +05:30
binding.pairMessage.setText(R.string.pair_requested);
binding.pairVerification.setVisibility(View.VISIBLE);
binding.pairVerification.setText(SslHelper.getVerificationKey(SslHelper.certificate, device.certificate));
2020-07-08 05:15:07 +05:30
binding.pairingButtons.setVisibility(View.VISIBLE);
binding.pairProgress.setVisibility(View.GONE);
binding.pairButton.setVisibility(View.GONE);
binding.pairRequestButtons.setVisibility(View.VISIBLE);
} else {
boolean paired = device.isPaired();
boolean reachable = device.isReachable();
2020-07-08 05:15:07 +05:30
binding.pairingButtons.setVisibility(paired ? View.GONE : View.VISIBLE);
binding.errorMessageContainer.setVisibility((paired && !reachable) ? View.VISIBLE : View.GONE);
binding.notReachableMessage.setVisibility((paired && !reachable) ? View.VISIBLE : View.GONE);
try {
pluginListItems = new ArrayList<>();
if (paired && reachable) {
//Plugins button list
final Collection<Plugin> plugins = device.getLoadedPlugins().values();
for (final Plugin p : plugins) {
if (!p.hasMainActivity()) continue;
if (p.displayInContextMenu()) continue;
pluginListItems.add(new PluginItem(p, v -> p.startMainActivity(mActivity)));
}
DeviceFragment.this.createPluginsList(device.getPluginsWithoutPermissions(), R.string.plugins_need_permission, (plugin) -> {
DialogFragment dialog = plugin.getPermissionExplanationDialog();
if (dialog != null) {
dialog.show(getChildFragmentManager(), null);
}
});
DeviceFragment.this.createPluginsList(device.getPluginsWithoutOptionalPermissions(), R.string.plugins_need_optional_permission, (plugin) -> {
DialogFragment dialog = plugin.getOptionalPermissionExplanationDialog();
if (dialog != null) {
dialog.show(getChildFragmentManager(), null);
}
});
}
ListAdapter adapter = new ListAdapter(mActivity, pluginListItems);
2020-07-08 05:15:07 +05:30
binding.buttonsList.setAdapter(adapter);
mActivity.invalidateOptionsMenu();
} catch (IllegalStateException e) {
//Ignore: The activity was closed while we were trying to update it
} catch (ConcurrentModificationException e) {
2019-05-21 00:21:01 +02:00
Log.e(TAG, "ConcurrentModificationException");
this.run(); //Try again
}
}
}
});
}
2018-10-26 23:53:58 +02:00
private final Device.PairingCallback pairingCallback = new Device.PairingCallback() {
@Override
public void incomingRequest() {
refreshUI();
}
@Override
public void pairingSuccessful() {
refreshUI();
}
@Override
public void pairingFailed(final String error) {
mActivity.runOnUiThread(() -> {
2020-07-08 05:15:07 +05:30
if (binding.getRoot() == null) return;
binding.pairMessage.setText(error);
binding.pairVerification.setText("");
binding.pairVerification.setVisibility(View.GONE);
2020-07-08 05:15:07 +05:30
binding.pairProgress.setVisibility(View.GONE);
binding.pairButton.setVisibility(View.VISIBLE);
binding.pairRequestButtons.setVisibility(View.GONE);
refreshUI();
});
}
@Override
public void unpaired() {
mActivity.runOnUiThread(() -> {
2020-07-08 05:15:07 +05:30
if (binding.getRoot() == null) return;
binding.pairMessage.setText(R.string.device_not_paired);
binding.pairVerification.setVisibility(View.GONE);
2020-07-08 05:15:07 +05:30
binding.pairProgress.setVisibility(View.GONE);
binding.pairButton.setVisibility(View.VISIBLE);
binding.pairRequestButtons.setVisibility(View.GONE);
refreshUI();
});
}
};
2018-10-26 23:53:58 +02:00
private void createPluginsList(ConcurrentHashMap<String, Plugin> plugins, int headerText, FailedPluginListItem.Action action) {
2019-05-13 21:31:03 +00:00
if (plugins.isEmpty())
return;
2019-05-13 22:34:10 +00:00
pluginListItems.add(new PluginListHeaderItem(headerText));
2019-05-13 21:31:03 +00:00
for (Plugin plugin : plugins.values()) {
if (!device.isPluginEnabled(plugin.getPluginKey())) {
continue;
}
2019-05-13 21:31:03 +00:00
pluginListItems.add(new FailedPluginListItem(plugin, action));
}
}
2015-08-20 00:59:21 -07:00
}