diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/network/KeyTransferInteractor.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/network/KeyTransferInteractor.java deleted file mode 100644 index 0ba4c9d96..000000000 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/network/KeyTransferInteractor.java +++ /dev/null @@ -1,443 +0,0 @@ -/* - * Copyright (C) 2017 Schürmann & Breitmoser GbR - * - * 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 3 of the License, or - * (at your option) any later version. - * - * 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.sufficientlysecure.keychain.network; - - -import java.io.BufferedOutputStream; -import java.io.BufferedReader; -import java.io.Closeable; -import java.io.IOException; -import java.io.InputStreamReader; -import java.io.OutputStream; -import java.net.InetAddress; -import java.net.InetSocketAddress; -import java.net.NetworkInterface; -import java.net.NoRouteToHostException; -import java.net.Socket; -import java.net.SocketTimeoutException; -import java.net.URISyntaxException; -import java.security.SecureRandom; -import java.util.Arrays; -import java.util.Collections; -import java.util.HashSet; -import java.util.List; -import java.util.Set; - -import android.os.Build.VERSION_CODES; -import android.os.Handler; -import android.os.Looper; -import androidx.annotation.Nullable; -import androidx.annotation.RequiresApi; - -import javax.net.ssl.SSLContext; -import javax.net.ssl.SSLHandshakeException; -import javax.net.ssl.SSLServerSocket; -import javax.net.ssl.SSLSocket; -import timber.log.Timber; - - -@RequiresApi(api = VERSION_CODES.LOLLIPOP) -public class KeyTransferInteractor { - private static final String[] ALLOWED_CIPHERSUITES = new String[] { - // only allow ephemeral diffie-hellman based PSK ciphers! - "TLS_DHE_PSK_WITH_AES_128_CBC_SHA", - "TLS_DHE_PSK_WITH_AES_256_CBC_SHA", - "TLS_DHE_PSK_WITH_CHACHA20_POLY1305_SHA256", - "TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA", - "TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA", - "TLS_ECDHE_PSK_WITH_CHACHA20_POLY1305_SHA256" - }; - - private static final int CONNECTION_LISTENING = 1; - private static final int CONNECTION_ESTABLISHED = 2; - private static final int CONNECTION_SEND_OK = 3; - private static final int CONNECTION_RECEIVE_OK = 4; - private static final int CONNECTION_LOST = 5; - private static final int CONNECTION_ERROR_NO_ROUTE_TO_HOST = 6; - private static final int CONNECTION_ERROR_CONNECT = 7; - private static final int CONNECTION_ERROR_WHILE_CONNECTED = 8; - private static final int CONNECTION_ERROR_LISTEN = 0; - - private static final int TIMEOUT_CONNECTING = 1500; - private static final int TIMEOUT_RECEIVING = 2000; - private static final int TIMEOUT_WAITING = 500; - private static final int PSK_BYTE_LENGTH = 16; - - - private final String delimiterStart; - private final String delimiterEnd; - - private TransferThread transferThread; - - - public KeyTransferInteractor(String delimiterStart, String delimiterEnd) { - this.delimiterStart = delimiterStart; - this.delimiterEnd = delimiterEnd; - } - - public void connectToServer(String qrCodeContent, KeyTransferCallback callback) throws URISyntaxException { - SktUri sktUri = SktUri.parse(qrCodeContent); - - transferThread = TransferThread.createClientTransferThread(delimiterStart, delimiterEnd, callback, - sktUri.getPresharedKey(), sktUri.getHost(), sktUri.getPort(), sktUri.getWifiSsid()); - transferThread.start(); - } - - public void startServer(KeyTransferCallback callback, String wifiSsid) { - byte[] presharedKey = generatePresharedKey(); - - transferThread = TransferThread.createServerTransferThread(delimiterStart, delimiterEnd, callback, presharedKey, wifiSsid); - transferThread.start(); - } - - private static class TransferThread extends Thread { - private final String delimiterStart; - private final String delimiterEnd; - - private final Handler handler; - private final byte[] presharedKey; - private final boolean isServer; - private final String clientHost; - private final Integer clientPort; - private final String wifiSsid; - - private KeyTransferCallback callback; - private SSLServerSocket serverSocket; - private byte[] dataToSend; - private String sendPassthrough; - - static TransferThread createClientTransferThread(String delimiterStart, String delimiterEnd, - KeyTransferCallback callback, byte[] presharedKey, String host, int port, String wifiSsid) { - return new TransferThread(delimiterStart, delimiterEnd, callback, presharedKey, false, host, port, wifiSsid); - } - - static TransferThread createServerTransferThread(String delimiterStart, String delimiterEnd, - KeyTransferCallback callback, byte[] presharedKey, String wifiSsid) { - return new TransferThread(delimiterStart, delimiterEnd, callback, presharedKey, true, null, null, wifiSsid); - } - - private TransferThread(String delimiterStart, String delimiterEnd, - KeyTransferCallback callback, byte[] presharedKey, boolean isServer, - String clientHost, Integer clientPort, String wifiSsid) { - super("TLS-PSK Key Transfer Thread"); - - this.delimiterStart = delimiterStart; - this.delimiterEnd = delimiterEnd; - - this.callback = callback; - this.presharedKey = presharedKey; - this.clientHost = clientHost; - this.clientPort = clientPort; - this.wifiSsid = wifiSsid; - this.isServer = isServer; - - handler = new Handler(Looper.getMainLooper()); - } - - @Override - public void run() { - SSLContext sslContext = TlsPskCompat.createTlsPskSslContext(presharedKey); - - Socket socket = null; - try { - socket = getSocketListenOrConnect(sslContext); - if (socket == null) { - return; - } - - try { - handleOpenConnection(socket); - Timber.d("connection closed ok!"); - } catch (SSLHandshakeException e) { - Timber.d(e, "ssl handshake error!"); - invokeListener(CONNECTION_ERROR_CONNECT, null); - } catch (IOException e) { - Timber.e(e, "communication error!"); - invokeListener(CONNECTION_ERROR_WHILE_CONNECTED, e.getLocalizedMessage()); - } - } finally { - closeQuietly(socket); - closeQuietly(serverSocket); - } - } - - @Nullable - private Socket getSocketListenOrConnect(SSLContext sslContext) { - Socket socket; - if (isServer) { - try { - serverSocket = (SSLServerSocket) sslContext.getServerSocketFactory().createServerSocket(0); - String[] supportedCipherSuites = serverSocket.getSupportedCipherSuites(); - String[] enabledCipherSuites = intersectArrays(supportedCipherSuites, ALLOWED_CIPHERSUITES); - serverSocket.setEnabledCipherSuites(enabledCipherSuites); - - SktUri sktUri = SktUri.create(getIPAddress(true), serverSocket.getLocalPort(), presharedKey, wifiSsid); - invokeListener(CONNECTION_LISTENING, sktUri.toUriString()); - - socket = serverSocket.accept(); - } catch (IOException e) { - Timber.e(e, "error while listening!"); - invokeListener(CONNECTION_ERROR_LISTEN, null); - return null; - } - } else { - try { - SSLSocket sslSocket = (SSLSocket) sslContext.getSocketFactory().createSocket(); - String[] supportedCipherSuites = sslSocket.getSupportedCipherSuites(); - String[] enabledCipherSuites = intersectArrays(supportedCipherSuites, ALLOWED_CIPHERSUITES); - sslSocket.setEnabledCipherSuites(enabledCipherSuites); - - socket = sslSocket; - socket.connect(new InetSocketAddress(InetAddress.getByName(clientHost), clientPort), TIMEOUT_CONNECTING); - } catch (IOException e) { - Timber.e(e, "error while connecting!"); - if (e instanceof NoRouteToHostException) { - invokeListener(CONNECTION_ERROR_NO_ROUTE_TO_HOST, wifiSsid); - } else { - invokeListener(CONNECTION_ERROR_CONNECT, null); - } - return null; - } - } - return socket; - } - - private void handleOpenConnection(Socket socket) throws IOException { - BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(socket.getInputStream())); - OutputStream outputStream = new BufferedOutputStream(socket.getOutputStream()); - - invokeListener(CONNECTION_ESTABLISHED, socket.getInetAddress().toString()); - - socket.setSoTimeout(TIMEOUT_WAITING); - while (!isInterrupted() && socket.isConnected() && !socket.isClosed()) { - sendDataIfAvailable(socket, outputStream); - boolean connectionTerminated = receiveDataIfAvailable(socket, bufferedReader); - if (connectionTerminated) { - break; - } - } - Timber.d("disconnected"); - invokeListener(CONNECTION_LOST, null); - } - - private boolean receiveDataIfAvailable(Socket socket, BufferedReader bufferedReader) throws IOException { - String firstLine; - try { - firstLine = bufferedReader.readLine(); - } catch (SocketTimeoutException e) { - return false; - } - - if (firstLine == null) { - return true; - } - - boolean lineIsDelimiter = delimiterStart.equals(firstLine); - if (!lineIsDelimiter) { - Timber.d("bad beginning of key block?"); - return false; - } - - socket.setSoTimeout(TIMEOUT_RECEIVING); - String receivedData = receiveLinesUntilEndDelimiter(bufferedReader, firstLine); - socket.setSoTimeout(TIMEOUT_WAITING); - - invokeListener(CONNECTION_RECEIVE_OK, receivedData); - return false; - } - - private boolean sendDataIfAvailable(Socket socket, OutputStream outputStream) throws IOException { - if (dataToSend != null) { - byte[] data = dataToSend; - dataToSend = null; - - socket.setSoTimeout(TIMEOUT_RECEIVING); - outputStream.write(data); - outputStream.flush(); - socket.setSoTimeout(TIMEOUT_WAITING); - - invokeListener(CONNECTION_SEND_OK, sendPassthrough); - sendPassthrough = null; - return true; - } - return false; - } - - private String receiveLinesUntilEndDelimiter(BufferedReader bufferedReader, String line) throws IOException { - StringBuilder builder = new StringBuilder(); - do { - boolean lineIsDelimiter = delimiterEnd.equals(line); - if (lineIsDelimiter) { - break; - } - - builder.append(line).append('\n'); - - line = bufferedReader.readLine(); - } while (line != null); - - return builder.toString(); - } - - private void invokeListener(final int method, final String arg) { - if (handler == null) { - return; - } - - Runnable runnable = new Runnable() { - @Override - public void run() { - if (callback == null) { - return; - } - switch (method) { - case CONNECTION_LISTENING: - callback.onServerStarted(arg); - break; - case CONNECTION_ESTABLISHED: - callback.onConnectionEstablished(arg); - break; - case CONNECTION_RECEIVE_OK: - callback.onDataReceivedOk(arg); - break; - case CONNECTION_SEND_OK: - callback.onDataSentOk(arg); - break; - case CONNECTION_LOST: - callback.onConnectionLost(); - break; - case CONNECTION_ERROR_WHILE_CONNECTED: - callback.onConnectionError(arg); - break; - case CONNECTION_ERROR_NO_ROUTE_TO_HOST: - callback.onConnectionErrorNoRouteToHost(wifiSsid); - break; - case CONNECTION_ERROR_CONNECT: - callback.onConnectionErrorConnect(); - break; - case CONNECTION_ERROR_LISTEN: - callback.onConnectionErrorListen(); - break; - } - } - }; - - handler.post(runnable); - } - - synchronized void sendData(byte[] dataToSend, String passthrough) { - this.dataToSend = dataToSend; - this.sendPassthrough = passthrough; - } - - @Override - public void interrupt() { - callback = null; - super.interrupt(); - closeQuietly(serverSocket); - } - } - - private static byte[] generatePresharedKey() { - byte[] presharedKey = new byte[PSK_BYTE_LENGTH]; - new SecureRandom().nextBytes(presharedKey); - return presharedKey; - } - - public void closeConnection() { - if (transferThread != null) { - transferThread.interrupt(); - } - - transferThread = null; - } - - public void sendData(byte[] dataToSend, String passthrough) { - transferThread.sendData(dataToSend, passthrough); - } - - public interface KeyTransferCallback { - void onServerStarted(String qrCodeData); - void onConnectionEstablished(String otherName); - void onConnectionLost(); - - void onDataReceivedOk(String receivedData); - void onDataSentOk(String passthrough); - - void onConnectionErrorConnect(); - void onConnectionErrorNoRouteToHost(String wifiSsid); - void onConnectionErrorListen(); - void onConnectionError(String arg); - } - - /** - * from: http://stackoverflow.com/a/13007325 - *

- * Get IP address from first non-localhost interface - * - * @param useIPv4 true=return ipv4, false=return ipv6 - * @return address or empty string - */ - private static String getIPAddress(boolean useIPv4) { - try { - List interfaces = Collections.list(NetworkInterface.getNetworkInterfaces()); - for (NetworkInterface intf : interfaces) { - List addrs = Collections.list(intf.getInetAddresses()); - for (InetAddress addr : addrs) { - if (addr.isLoopbackAddress()) { - continue; - } - String sAddr = addr.getHostAddress(); - boolean isIPv4 = sAddr.indexOf(':') < 0; - if (useIPv4) { - if (isIPv4) { - return sAddr; - } - } else { - int delimIndex = sAddr.indexOf('%'); // drop ip6 zone suffix - if (delimIndex >= 0) { - sAddr = sAddr.substring(0, delimIndex); - } - return sAddr.toUpperCase(); - } - } - } - } catch (Exception ex) { - // ignore - } - return ""; - } - - private static void closeQuietly(Closeable closeable) { - try { - if (closeable != null) { - closeable.close(); - } - } catch (IOException e) { - // ignore - } - } - - private static String[] intersectArrays(String[] array1, String[] array2) { - Set s1 = new HashSet<>(Arrays.asList(array1)); - Set s2 = new HashSet<>(Arrays.asList(array2)); - s1.retainAll(s2); - - return s1.toArray(new String[0]); - } -} diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/network/SktUri.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/network/SktUri.java deleted file mode 100644 index 7186c8237..000000000 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/network/SktUri.java +++ /dev/null @@ -1,107 +0,0 @@ -/* - * Copyright (C) 2017 Schürmann & Breitmoser GbR - * - * 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 3 of the License, or - * (at your option) any later version. - * - * 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.sufficientlysecure.keychain.network; - -import java.net.URISyntaxException; -import java.nio.charset.Charset; - -import android.annotation.SuppressLint; -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; - -import com.google.auto.value.AutoValue; -import org.bouncycastle.util.encoders.DecoderException; -import org.bouncycastle.util.encoders.Hex; -import org.sufficientlysecure.keychain.Constants; -import timber.log.Timber; - - -@AutoValue -abstract class SktUri { - private static final String QRCODE_URI_FORMAT = Constants.SKT_SCHEME + ":%s/%d/%s"; - private static final String QRCODE_URI_FORMAT_SSID = Constants.SKT_SCHEME + ":%s/%d/%s/SSID:%s"; - - - public abstract String getHost(); - public abstract int getPort(); - @SuppressWarnings("mutable") - public abstract byte[] getPresharedKey(); - - @Nullable - public abstract String getWifiSsid(); - - @NonNull - public static SktUri parse(String input) throws URISyntaxException { - if (!input.startsWith(Constants.SKT_SCHEME + ":")) { - throw new URISyntaxException(input, "invalid scheme"); - } - - String[] pieces = input.substring(input.indexOf(":") +1).split("/"); - if (pieces.length < 3) { - throw new URISyntaxException(input, "invalid syntax"); - } - - String address = pieces[0]; - int port; - try { - port = Integer.parseInt(pieces[1]); - } catch (NumberFormatException e) { - throw new URISyntaxException(input, "error parsing port"); - } - byte[] psk; - try { - psk = Hex.decode(pieces[2]); - } catch (DecoderException e) { - throw new URISyntaxException(input, "error parsing hex psk"); - } - - String wifiSsid = null; - for (int i = 3; i < pieces.length; i++) { - String[] optarg = pieces[i].split(":", 2); - if (optarg.length == 2 && "SSID".equals(optarg[0])) { - try { - wifiSsid = new String(Hex.decode(optarg[1])); - } catch (DecoderException e) { - Timber.d("error parsing ssid in skt uri, ignoring: " + input); - } - } - } - - return new AutoValue_SktUri(address, port, psk, wifiSsid); - } - - @SuppressLint("DefaultLocale") - String toUriString() { - String sktHex = Hex.toHexString(getPresharedKey()); - String wifiSsid = getWifiSsid(); - - String result; - if (wifiSsid != null) { - String encodedWifiSsid = Hex.toHexString(getWifiSsid().getBytes(Charset.defaultCharset())); - result = String.format(QRCODE_URI_FORMAT_SSID, getHost(), getPort(), sktHex, encodedWifiSsid); - } else { - result = String.format(QRCODE_URI_FORMAT, getHost(), getPort(), sktHex); - } - - return result.toUpperCase(); - } - - static SktUri create(String host, int port, byte[] presharedKey, @Nullable String wifiSsid) { - return new AutoValue_SktUri(host, port, presharedKey, wifiSsid); - } -} diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/network/TlsPskCompat.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/network/TlsPskCompat.java deleted file mode 100644 index 24886557a..000000000 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/network/TlsPskCompat.java +++ /dev/null @@ -1,77 +0,0 @@ -package org.sufficientlysecure.keychain.network; - - -import java.net.Socket; -import java.security.KeyManagementException; -import java.security.NoSuchAlgorithmException; - -import android.os.Build.VERSION_CODES; -import androidx.annotation.RequiresApi; - -import javax.crypto.SecretKey; -import javax.crypto.spec.SecretKeySpec; -import javax.net.ssl.KeyManager; -import javax.net.ssl.SSLContext; -import javax.net.ssl.SSLEngine; -import javax.net.ssl.TrustManager; - - -@RequiresApi(api = VERSION_CODES.LOLLIPOP) -class TlsPskCompat { - - static SSLContext createTlsPskSslContext(byte[] presharedKey) { - try { - PresharedKeyManager pskKeyManager = new PresharedKeyManager(presharedKey); - SSLContext sslContext = SSLContext.getInstance("TLS"); - sslContext.init(new KeyManager[] { pskKeyManager }, new TrustManager[0], null); - - return sslContext; - } catch (KeyManagementException | NoSuchAlgorithmException e) { - throw new IllegalStateException(e); - } - } - - @SuppressWarnings("unused") - /* This class is a KeyManager that is compatible to TlsPskManager. - * - * Due to the way conscrypt works internally, this class will be internally duck typed to - * PSKKeyManager. This is quite a hack, and relies on conscrypt internals to work - but it - * works. - * - * see also: - * https://github.com/google/conscrypt/blob/b23e9353ed4e3256379d660cb09491a69b21affb/common/src/main/java/org/conscrypt/SSLParametersImpl.java#L494 - * https://github.com/google/conscrypt/blob/29916ef38dc9cb4e4c6e3fdb87d4e921546d3ef4/common/src/main/java/org/conscrypt/DuckTypedPSKKeyManager.java#L51 - * - */ - private static class PresharedKeyManager implements KeyManager { - byte[] presharedKey; - - private PresharedKeyManager(byte[] presharedKey) { - this.presharedKey = presharedKey; - } - - public String chooseServerKeyIdentityHint(Socket socket) { - return null; - } - - public String chooseServerKeyIdentityHint(SSLEngine engine) { - return null; - } - - public String chooseClientKeyIdentity(String identityHint, Socket socket) { - return identityHint; - } - - public String chooseClientKeyIdentity(String identityHint, SSLEngine engine) { - return identityHint; - } - - public SecretKey getKey(String identityHint, String identity, Socket socket) { - return new SecretKeySpec(presharedKey, "AES"); - } - - public SecretKey getKey(String identityHint, String identity, SSLEngine engine) { - return new SecretKeySpec(presharedKey, "AES"); - } - } -} diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CreateKeyStartFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CreateKeyStartFragment.java index e24dce606..be9049a9b 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CreateKeyStartFragment.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CreateKeyStartFragment.java @@ -21,19 +21,16 @@ package org.sufficientlysecure.keychain.ui; import android.app.Activity; import android.content.Context; import android.content.Intent; -import android.os.Build; -import android.os.Build.VERSION_CODES; import android.os.Bundle; -import androidx.annotation.NonNull; -import androidx.fragment.app.Fragment; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.TextView; +import androidx.annotation.NonNull; +import androidx.fragment.app.Fragment; import org.sufficientlysecure.keychain.R; import org.sufficientlysecure.keychain.ui.CreateKeyActivity.FragAction; -import org.sufficientlysecure.keychain.ui.transfer.view.TransferFragment; import org.sufficientlysecure.keychain.util.Preferences; import timber.log.Timber; @@ -48,7 +45,6 @@ public class CreateKeyStartFragment extends Fragment { View mImportKey; View mSecurityToken; TextView mSkipOrCancel; - View mSecureDeviceSetup; /** @@ -72,7 +68,6 @@ public class CreateKeyStartFragment extends Fragment { mImportKey = view.findViewById(R.id.create_key_import_button); mSecurityToken = view.findViewById(R.id.create_key_security_token_button); mSkipOrCancel = view.findViewById(R.id.create_key_cancel); - mSecureDeviceSetup = view.findViewById(R.id.create_key_secure_device_setup); if (mCreateKeyActivity.mFirstTime) { mSkipOrCancel.setText(R.string.first_time_skip); @@ -96,15 +91,6 @@ public class CreateKeyStartFragment extends Fragment { startActivityForResult(intent, REQUEST_CODE_IMPORT_KEY); }); - if (Build.VERSION.SDK_INT >= VERSION_CODES.LOLLIPOP) { - mSecureDeviceSetup.setOnClickListener(v -> { - TransferFragment frag = new TransferFragment(); - mCreateKeyActivity.loadFragment(frag, FragAction.TO_RIGHT); - }); - } else { - mSecureDeviceSetup.setVisibility(View.GONE); - } - mSkipOrCancel.setOnClickListener(v -> { if (!mCreateKeyActivity.mFirstTime) { mCreateKeyActivity.setResult(Activity.RESULT_CANCELED); diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysProxyActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysProxyActivity.java index f69a465af..565ebb5cc 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysProxyActivity.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysProxyActivity.java @@ -40,7 +40,6 @@ import org.sufficientlysecure.keychain.operations.results.OperationResult.LogTyp import org.sufficientlysecure.keychain.operations.results.SingletonResult; import org.sufficientlysecure.keychain.service.ImportKeyringParcel; import org.sufficientlysecure.keychain.ui.base.CryptoOperationHelper; -import org.sufficientlysecure.keychain.ui.transfer.view.TransferFragment; import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils; import org.sufficientlysecure.keychain.util.IntentIntegratorSupportV4; import org.sufficientlysecure.keychain.keyimport.HkpKeyserverAddress; @@ -155,17 +154,6 @@ public class ImportKeysProxyActivity extends FragmentActivity Timber.d("scanned: " + uri); - // example: pgp+transfer: - if (uri != null && uri.getScheme() != null && uri.getScheme().equalsIgnoreCase(Constants.SKT_SCHEME)) { - Intent intent = new Intent(this, MainActivity.class); - intent.putExtra(MainActivity.EXTRA_INIT_FRAG, MainActivity.ID_TRANSFER); - intent.putExtra(TransferFragment.EXTRA_OPENPGP_SKT_INFO, uri); - intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); - startActivity(intent); - finish(); - return; - } - // example: openpgp4fpr:73EE2314F65FA92EC2390D3A718C070100012282 if (uri == null || uri.getScheme() == null || !uri.getScheme().toLowerCase(Locale.ENGLISH).equals(Constants.FINGERPRINT_SCHEME)) { diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/MainActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/MainActivity.java index 35dfd4670..ce26e88e0 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/MainActivity.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/MainActivity.java @@ -19,9 +19,6 @@ package org.sufficientlysecure.keychain.ui; import android.content.Intent; -import android.graphics.Typeface; -import android.os.Build; -import android.os.Build.VERSION_CODES; import android.os.Bundle; import android.view.View; @@ -41,8 +38,6 @@ import org.sufficientlysecure.keychain.R; import org.sufficientlysecure.keychain.operations.results.OperationResult; import org.sufficientlysecure.keychain.remote.ui.AppsListFragment; import org.sufficientlysecure.keychain.ui.base.BaseSecurityTokenActivity; -import org.sufficientlysecure.keychain.ui.transfer.view.TransferFragment; -import org.sufficientlysecure.keychain.ui.transfer.view.TransferNotAvailableFragment; import org.sufficientlysecure.keychain.util.FabContainer; import org.sufficientlysecure.keychain.util.Preferences; @@ -52,7 +47,6 @@ public class MainActivity extends BaseSecurityTokenActivity implements FabContai static final int ID_ENCRYPT_DECRYPT = 2; static final int ID_APPS = 3; static final int ID_BACKUP = 4; - public static final int ID_TRANSFER = 5; static final int ID_SETTINGS = 6; static final int ID_HELP = 7; @@ -85,11 +79,6 @@ public class MainActivity extends BaseSecurityTokenActivity implements FabContai .withIdentifier(ID_APPS).withSelectable(false), new PrimaryDrawerItem().withName(R.string.nav_backup).withIcon(CommunityMaterial.Icon.cmd_backup_restore) .withIdentifier(ID_BACKUP).withSelectable(false), - new PrimaryDrawerItem().withName(R.string.nav_transfer) - .withIcon(R.drawable.ic_wifi_lock_24dp) - .withIconColorRes(R.color.md_grey_600) - .withIconTintingEnabled(true) - .withIdentifier(ID_TRANSFER).withSelectable(false), new DividerDrawerItem(), new PrimaryDrawerItem().withName(R.string.menu_preferences).withIcon(GoogleMaterial.Icon.gmd_settings).withIdentifier(ID_SETTINGS).withSelectable(false), new PrimaryDrawerItem().withName(R.string.menu_help).withIcon(CommunityMaterial.Icon.cmd_help_circle).withIdentifier(ID_HELP).withSelectable(false) @@ -113,9 +102,6 @@ public class MainActivity extends BaseSecurityTokenActivity implements FabContai case ID_BACKUP: onBackupSelected(); break; - case ID_TRANSFER: - onTransferSelected(); - break; case ID_SETTINGS: intent = new Intent(MainActivity.this, SettingsActivity.class); break; @@ -168,9 +154,6 @@ public class MainActivity extends BaseSecurityTokenActivity implements FabContai case ID_APPS: onAppsSelected(); break; - case ID_TRANSFER: - onTransferSelected(); - break; } } @@ -190,9 +173,6 @@ public class MainActivity extends BaseSecurityTokenActivity implements FabContai case ID_APPS: onAppsSelected(); break; - case ID_TRANSFER: - onTransferSelected(); - break; } } } @@ -234,18 +214,6 @@ public class MainActivity extends BaseSecurityTokenActivity implements FabContai setFragment(frag); } - private void onTransferSelected() { - mToolbar.setTitle(R.string.nav_transfer); - mDrawer.setSelection(ID_TRANSFER, false); - if (Build.VERSION.SDK_INT < VERSION_CODES.LOLLIPOP) { - Fragment frag = new TransferNotAvailableFragment(); - setFragment(frag); - } else { - Fragment frag = new TransferFragment(); - setFragment(frag); - } - } - @Override protected void onSaveInstanceState(Bundle outState) { // add the values which need to be saved from the drawer to the bundle diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/keyview/ViewKeyActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/keyview/ViewKeyActivity.java index b3193de09..2a6e507d4 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/keyview/ViewKeyActivity.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/keyview/ViewKeyActivity.java @@ -299,13 +299,6 @@ public class ViewKeyActivity extends BaseSecurityTokenActivity { startPassphraseActivity(REQUEST_BACKUP); return true; } - case R.id.menu_key_view_skt: { - Intent intent = new Intent(this, MainActivity.class); - intent.putExtra(MainActivity.EXTRA_INIT_FRAG, MainActivity.ID_TRANSFER); - intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); - startActivity(intent); - return true; - } case R.id.menu_key_view_delete: { deleteKey(); return true; @@ -335,7 +328,6 @@ public class ViewKeyActivity extends BaseSecurityTokenActivity { } MenuItem backupKey = menu.findItem(R.id.menu_key_view_backup); backupKey.setVisible(unifiedKeyInfo.has_any_secret()); - menu.findItem(R.id.menu_key_view_skt).setVisible(unifiedKeyInfo.has_any_secret()); MenuItem changePassword = menu.findItem(R.id.menu_key_change_password); changePassword.setVisible(unifiedKeyInfo.has_any_secret()); diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/transfer/presenter/TransferPresenter.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/transfer/presenter/TransferPresenter.java deleted file mode 100644 index ff76d29db..000000000 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/transfer/presenter/TransferPresenter.java +++ /dev/null @@ -1,477 +0,0 @@ -/* - * Copyright (C) 2017 Schürmann & Breitmoser GbR - * - * 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 3 of the License, or - * (at your option) any later version. - * - * 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.sufficientlysecure.keychain.ui.transfer.presenter; - - -import java.io.IOException; -import java.net.URISyntaxException; -import java.util.List; - -import androidx.lifecycle.LifecycleOwner; -import androidx.lifecycle.LiveData; -import android.content.Context; -import android.graphics.Bitmap; -import android.net.ConnectivityManager; -import android.net.NetworkInfo; -import android.net.Uri; -import android.net.wifi.WifiInfo; -import android.net.wifi.WifiManager; -import android.os.Build.VERSION_CODES; -import android.os.Handler; -import android.os.Parcelable; -import androidx.annotation.RequiresApi; -import androidx.recyclerview.widget.RecyclerView.Adapter; -import android.view.LayoutInflater; - -import org.openintents.openpgp.util.OpenPgpUtils; -import org.openintents.openpgp.util.OpenPgpUtils.UserId; -import org.sufficientlysecure.keychain.keyimport.ParcelableKeyRing; -import org.sufficientlysecure.keychain.model.SubKey.UnifiedKeyInfo; -import org.sufficientlysecure.keychain.network.KeyTransferInteractor; -import org.sufficientlysecure.keychain.network.KeyTransferInteractor.KeyTransferCallback; -import org.sufficientlysecure.keychain.operations.results.ImportKeyResult; -import org.sufficientlysecure.keychain.operations.results.OperationResult; -import org.sufficientlysecure.keychain.pgp.UncachedKeyRing; -import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralException; -import org.sufficientlysecure.keychain.daos.KeyRepository; -import org.sufficientlysecure.keychain.daos.KeyRepository.NotFoundException; -import org.sufficientlysecure.keychain.service.ImportKeyringParcel; -import org.sufficientlysecure.keychain.ui.base.CryptoOperationHelper; -import org.sufficientlysecure.keychain.ui.base.CryptoOperationHelper.Callback; -import org.sufficientlysecure.keychain.ui.keyview.GenericViewModel; -import org.sufficientlysecure.keychain.ui.transfer.view.ReceivedSecretKeyList.OnClickImportKeyListener; -import org.sufficientlysecure.keychain.ui.transfer.view.ReceivedSecretKeyList.ReceivedKeyAdapter; -import org.sufficientlysecure.keychain.ui.transfer.view.ReceivedSecretKeyList.ReceivedKeyItem; -import org.sufficientlysecure.keychain.ui.transfer.view.TransferSecretKeyList.OnClickTransferKeyListener; -import org.sufficientlysecure.keychain.ui.transfer.view.TransferSecretKeyList.TransferKeyAdapter; -import org.sufficientlysecure.keychain.ui.util.QrCodeUtils; -import timber.log.Timber; - - -@RequiresApi(api = VERSION_CODES.LOLLIPOP) -public class TransferPresenter implements KeyTransferCallback, OnClickTransferKeyListener, OnClickImportKeyListener { - private static final String DELIMITER_START = "-----BEGIN PGP PRIVATE KEY BLOCK-----"; - private static final String DELIMITER_END = "-----END PGP PRIVATE KEY BLOCK-----"; - private static final String BACKSTACK_TAG_TRANSFER = "transfer"; - - private final Context context; - private final TransferMvpView view; - private final KeyRepository keyRepository; - - private final TransferKeyAdapter secretKeyAdapter; - private final ReceivedKeyAdapter receivedKeyAdapter; - private final LifecycleOwner lifecycleOwner; - private final GenericViewModel viewModel; - - - private KeyTransferInteractor keyTransferClientInteractor; - private KeyTransferInteractor keyTransferServerInteractor; - - private boolean wasConnected = false; - private boolean sentData = false; - private boolean waitingForWifi = false; - private Long confirmingMasterKeyId; - - - public TransferPresenter(Context context, LifecycleOwner lifecycleOwner, - GenericViewModel viewModel, TransferMvpView view) { - this.context = context; - this.view = view; - this.lifecycleOwner = lifecycleOwner; - this.viewModel = viewModel; - this.keyRepository = KeyRepository.create(context); - - secretKeyAdapter = new TransferKeyAdapter(context, LayoutInflater.from(context), this); - view.setSecretKeyAdapter(secretKeyAdapter); - - receivedKeyAdapter = new ReceivedKeyAdapter(context, LayoutInflater.from(context), this); - view.setReceivedKeyAdapter(receivedKeyAdapter); - } - - - public void onUiInitFromIntentUri(final Uri initUri) { - connectionStartConnect(initUri.toString()); - } - - public void onUiStart() { - LiveData> liveData = - viewModel.getGenericLiveData(context, keyRepository::getAllUnifiedKeyInfoWithSecret); - liveData.observe(lifecycleOwner, this::onLoadSecretUnifiedKeyInfo); - - if (keyTransferServerInteractor == null && keyTransferClientInteractor == null && !wasConnected) { - checkWifiResetAndStartListen(); - } - } - - private void onLoadSecretUnifiedKeyInfo(List data) { - secretKeyAdapter.setData(data); - view.setShowSecretKeyEmptyView(data.isEmpty()); - } - - public void onUiStop() { - connectionClear(); - - if (wasConnected) { - view.showViewDisconnected(); - view.dismissConfirmationIfExists(); - secretKeyAdapter.setAllDisabled(true); - } - } - - public void onUiClickScan() { - connectionClear(); - - view.scanQrCode(); - } - - public void onUiClickScanAgain() { - onUiClickScan(); - } - - public void onUiClickDone() { - view.finishFragmentOrActivity(); - } - - public void onUiQrCodeScanned(String qrCodeContent) { - connectionStartConnect(qrCodeContent); - } - - public void onUiBackStackPop() { - if (wasConnected) { - checkWifiResetAndStartListen(); - } - } - - @Override - public void onUiClickTransferKey(long masterKeyId) { - if (sentData) { - prepareAndSendKey(masterKeyId); - } else { - confirmingMasterKeyId = masterKeyId; - view.showConfirmSendDialog(); - } - } - - public void onUiClickConfirmSend() { - if (confirmingMasterKeyId == null) { - return; - } - long masterKeyId = confirmingMasterKeyId; - confirmingMasterKeyId = null; - - prepareAndSendKey(masterKeyId); - } - - @Override - public void onUiClickImportKey(final long masterKeyId, String keyData) { - receivedKeyAdapter.focusItem(masterKeyId); - - final ImportKeyringParcel importKeyringParcel = ImportKeyringParcel.createImportKeyringParcel( - ParcelableKeyRing.createFromEncodedBytes(keyData.getBytes())); - - CryptoOperationHelper op = - view.createCryptoOperationHelper(new Callback() { - @Override - public ImportKeyringParcel createOperationInput() { - return importKeyringParcel; - } - - @Override - public void onCryptoOperationSuccess(ImportKeyResult result) { - receivedKeyAdapter.focusItem(null); - receivedKeyAdapter.addToFinishedItems(masterKeyId); - view.releaseCryptoOperationHelper(); - view.showResultNotification(result); - } - - @Override - public void onCryptoOperationCancelled() { - view.releaseCryptoOperationHelper(); - receivedKeyAdapter.focusItem(null); - } - - @Override - public void onCryptoOperationError(ImportKeyResult result) { - receivedKeyAdapter.focusItem(null); - view.releaseCryptoOperationHelper(); - view.showResultNotification(result); - } - - @Override - public boolean onCryptoSetProgress(String msg, int progress, int max) { - return false; - } - }); - - op.cryptoOperation(); - } - - public void onWifiConnected() { - if (waitingForWifi) { - resetAndStartListen(); - } - } - - @Override - public void onServerStarted(String qrCodeData) { - Bitmap qrCodeBitmap = QrCodeUtils.getQRCodeBitmap(Uri.parse(qrCodeData)); - view.setQrImage(qrCodeBitmap); - } - - @Override - public void onConnectionEstablished(String otherName) { - wasConnected = true; - - secretKeyAdapter.clearFinishedItems(); - secretKeyAdapter.focusItem(null); - secretKeyAdapter.setAllDisabled(false); - receivedKeyAdapter.clear(); - - view.showConnectionEstablished(otherName); - view.setShowDoneIcon(true); - view.addFakeBackStackItem(BACKSTACK_TAG_TRANSFER); - } - - @Override - public void onConnectionLost() { - if (!wasConnected) { - checkWifiResetAndStartListen(); - - view.showErrorConnectionFailed(); - } else { - connectionClear(); - - view.dismissConfirmationIfExists(); - view.showViewDisconnected(); - secretKeyAdapter.setAllDisabled(true); - } - } - - @Override - public void onDataReceivedOk(String receivedData) { - if (sentData) { - Timber.d("received data, but we already sent a key! race condition, or other side misbehaving?"); - return; - } - - Timber.d("received data"); - UncachedKeyRing uncachedKeyRing; - try { - uncachedKeyRing = UncachedKeyRing.decodeFromData(receivedData.getBytes()); - } catch (PgpGeneralException | IOException | RuntimeException e) { - Timber.e(e, "error parsing incoming key"); - view.showErrorBadKey(); - return; - } - - String primaryUserId = uncachedKeyRing.getPublicKey().getPrimaryUserIdWithFallback(); - UserId userId = OpenPgpUtils.splitUserId(primaryUserId); - - ReceivedKeyItem receivedKeyItem = new ReceivedKeyItem(receivedData, uncachedKeyRing.getMasterKeyId(), - uncachedKeyRing.getCreationTime(), userId.name, userId.email); - receivedKeyAdapter.addItem(receivedKeyItem); - - view.showReceivingKeys(); - } - - @Override - public void onDataSentOk(String passthrough) { - Timber.d("data sent ok!"); - final long masterKeyId = Long.parseLong(passthrough); - - new Handler().postDelayed(() -> { - secretKeyAdapter.focusItem(null); - secretKeyAdapter.addToFinishedItems(masterKeyId); - }, 750); - } - - @Override - public void onConnectionErrorConnect() { - view.showWaitingForConnection(); - view.showErrorConnectionFailed(); - - resetAndStartListen(); - } - - @Override - public void onConnectionErrorNoRouteToHost(String wifiSsid) { - connectionClear(); - - String ownWifiSsid = getConnectedWifiSsid(); - if (!wifiSsid.equalsIgnoreCase(ownWifiSsid)) { - view.showWifiError(wifiSsid); - } else { - view.showWaitingForConnection(); - view.showErrorConnectionFailed(); - - resetAndStartListen(); - } - } - - @Override - public void onConnectionErrorListen() { - view.showErrorListenFailed(); - } - - @Override - public void onConnectionError(String errorMessage) { - view.showErrorConnectionError(errorMessage); - - connectionClear(); - if (wasConnected) { - view.showViewDisconnected(); - secretKeyAdapter.setAllDisabled(true); - } - } - - private void connectionStartConnect(String qrCodeContent) { - connectionClear(); - - view.showEstablishingConnection(); - - keyTransferClientInteractor = new KeyTransferInteractor(DELIMITER_START, DELIMITER_END); - try { - keyTransferClientInteractor.connectToServer(qrCodeContent, TransferPresenter.this); - } catch (URISyntaxException e) { - view.showErrorConnectionFailed(); - } - } - - private void checkWifiResetAndStartListen() { - if (!isWifiConnected()) { - waitingForWifi = true; - view.showNotOnWifi(); - return; - } - - resetAndStartListen(); - } - - private void resetAndStartListen() { - waitingForWifi = false; - wasConnected = false; - sentData = false; - connectionClear(); - - String wifiSsid = getConnectedWifiSsid(); - - keyTransferServerInteractor = new KeyTransferInteractor(DELIMITER_START, DELIMITER_END); - keyTransferServerInteractor.startServer(this, wifiSsid); - - view.showWaitingForConnection(); - view.setShowDoneIcon(false); - } - - private boolean isWifiConnected() { - ConnectivityManager connManager = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE); - if (connManager == null) { - return false; - } - - NetworkInfo wifiNetwork = connManager.getNetworkInfo(ConnectivityManager.TYPE_WIFI); - - return wifiNetwork.isConnected(); - } - - private String getConnectedWifiSsid() { - WifiManager wifiManager = (WifiManager) context.getApplicationContext().getSystemService(Context.WIFI_SERVICE); - if (wifiManager == null) { - return null; - } - WifiInfo info = wifiManager.getConnectionInfo(); - if (info == null) { - return null; - } - // getSSID will return the ssid in quotes if it is valid utf-8. we only return it in that case. - String ssid = info.getSSID(); - if (ssid.charAt(0) != '"') { - return null; - } - return ssid.substring(1, ssid.length() -1); - } - - private void connectionClear() { - if (keyTransferServerInteractor != null) { - keyTransferServerInteractor.closeConnection(); - keyTransferServerInteractor = null; - } - if (keyTransferClientInteractor != null) { - keyTransferClientInteractor.closeConnection(); - keyTransferClientInteractor = null; - } - } - - private void prepareAndSendKey(long masterKeyId) { - try { - byte[] armoredSecretKey = keyRepository.getSecretKeyRingAsArmoredData(masterKeyId); - secretKeyAdapter.focusItem(masterKeyId); - connectionSend(armoredSecretKey, Long.toString(masterKeyId)); - } catch (IOException | NotFoundException e) { - // TODO - e.printStackTrace(); - } - } - - private void connectionSend(byte[] armoredSecretKey, String passthrough) { - sentData = true; - if (keyTransferClientInteractor != null) { - keyTransferClientInteractor.sendData(armoredSecretKey, passthrough); - } else if (keyTransferServerInteractor != null) { - keyTransferServerInteractor.sendData(armoredSecretKey, passthrough); - } - } - - public interface TransferMvpView { - void showNotOnWifi(); - void showWaitingForConnection(); - void showEstablishingConnection(); - void showConnectionEstablished(String hostname); - - void showWifiError(String wifiSsid); - - void showReceivingKeys(); - - void showViewDisconnected(); - - void scanQrCode(); - void setQrImage(Bitmap qrCode); - - void releaseCryptoOperationHelper(); - - void showErrorBadKey(); - void showErrorConnectionFailed(); - void showErrorListenFailed(); - void showErrorConnectionError(String errorMessage); - void showResultNotification(ImportKeyResult result); - - void setShowDoneIcon(boolean showDoneIcon); - - void setSecretKeyAdapter(Adapter adapter); - void setShowSecretKeyEmptyView(boolean isEmpty); - void setReceivedKeyAdapter(Adapter secretKeyAdapter); - - CryptoOperationHelper createCryptoOperationHelper(Callback callback); - - void addFakeBackStackItem(String tag); - - void finishFragmentOrActivity(); - - void showConfirmSendDialog(); - void dismissConfirmationIfExists(); - } -} diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/transfer/view/ReceivedSecretKeyList.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/transfer/view/ReceivedSecretKeyList.java deleted file mode 100644 index b61c9e0c4..000000000 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/transfer/view/ReceivedSecretKeyList.java +++ /dev/null @@ -1,215 +0,0 @@ -/* - * Copyright (C) 2017 Schürmann & Breitmoser GbR - * - * 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 3 of the License, or - * (at your option) any later version. - * - * 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.sufficientlysecure.keychain.ui.transfer.view; - - -import java.util.ArrayList; -import java.util.List; - -import android.content.Context; -import androidx.annotation.Nullable; -import androidx.recyclerview.widget.LinearLayoutManager; -import androidx.recyclerview.widget.RecyclerView; -import android.text.format.DateUtils; -import android.util.AttributeSet; -import android.view.LayoutInflater; -import android.view.View; -import android.view.ViewGroup; -import android.widget.TextView; -import android.widget.ViewAnimator; - -import org.sufficientlysecure.keychain.R; -import org.sufficientlysecure.keychain.ui.util.recyclerview.DividerItemDecoration; - - -public class ReceivedSecretKeyList extends RecyclerView { - private static final int STATE_INVISIBLE = 0; - // private static final int STATE_BUTTON = 1; // used in TransferSecretKeyList - private static final int STATE_PROGRESS = 2; - private static final int STATE_TRANSFERRED = 3; - private static final int STATE_IMPORT_BUTTON = 4; - - - public ReceivedSecretKeyList(Context context) { - super(context); - init(context); - } - - public ReceivedSecretKeyList(Context context, @Nullable AttributeSet attrs) { - super(context, attrs); - init(context); - } - - public ReceivedSecretKeyList(Context context, @Nullable AttributeSet attrs, int defStyle) { - super(context, attrs, defStyle); - init(context); - } - - private void init(Context context) { - setLayoutManager(new LinearLayoutManager(context)); - addItemDecoration(new DividerItemDecoration(context, DividerItemDecoration.VERTICAL_LIST, true)); - } - - public static class ReceivedKeyAdapter extends Adapter { - private final Context context; - private final LayoutInflater layoutInflater; - private final OnClickImportKeyListener onClickImportKeyListener; - - private Long focusedMasterKeyId; - private List data = new ArrayList<>(); - private ArrayList finishedItems = new ArrayList<>(); - - - public ReceivedKeyAdapter(Context context, LayoutInflater layoutInflater, - OnClickImportKeyListener onClickImportKeyListener) { - this.context = context; - this.layoutInflater = layoutInflater; - this.onClickImportKeyListener = onClickImportKeyListener; - } - - @Override - public ReceivedKeyViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { - return new ReceivedKeyViewHolder(layoutInflater.inflate(R.layout.key_transfer_item, parent, false)); - } - - @Override - public void onBindViewHolder(ReceivedKeyViewHolder holder, int position) { - ReceivedKeyItem item = data.get(position); - boolean isFinished = finishedItems.contains(item.masterKeyId); - holder.bind(context, item, onClickImportKeyListener, focusedMasterKeyId, isFinished); - } - - @Override - public int getItemCount() { - return data != null ? data.size() : 0; - } - - @Override - public long getItemId(int position) { - return data.get(position).masterKeyId; - } - - public void addToFinishedItems(long masterKeyId) { - finishedItems.add(masterKeyId); - // doeesn't notify, because it's non-trivial and this is called in conjunction with other refreshing things! - } - - public void focusItem(Long masterKeyId) { - focusedMasterKeyId = masterKeyId; - notifyItemRangeChanged(0, getItemCount()); - } - - public void addItem(ReceivedKeyItem receivedKeyItem) { - data.add(receivedKeyItem); - notifyItemInserted(data.size() -1); - } - - public void clear() { - data.clear(); - finishedItems.clear(); - focusedMasterKeyId = null; - notifyDataSetChanged(); - } - } - - static class ReceivedKeyViewHolder extends ViewHolder { - private final TextView vName; - private final TextView vEmail; - private final TextView vCreation; - private final View vImportButton; - private final ViewAnimator vState; - - ReceivedKeyViewHolder(View itemView) { - super(itemView); - - vName = itemView.findViewById(R.id.key_list_item_name); - vEmail = itemView.findViewById(R.id.key_list_item_email); - vCreation = itemView.findViewById(R.id.key_list_item_creation); - - vImportButton = itemView.findViewById(R.id.button_import); - vState = itemView.findViewById(R.id.transfer_state); - } - - private void bind(Context context, final ReceivedKeyItem item, - final OnClickImportKeyListener onClickReceiveKeyListener, Long focusedMasterKeyId, - boolean isFinished) { - if (item.name != null) { - vName.setText(item.name); - vName.setVisibility(View.VISIBLE); - } else { - vName.setVisibility(View.GONE); - } - if (item.email != null) { - vEmail.setText(item.email); - vEmail.setVisibility(View.VISIBLE); - } else { - vEmail.setVisibility(View.GONE); - } - - String dateTime = DateUtils.formatDateTime(context, item.creationMillis, - DateUtils.FORMAT_SHOW_DATE | DateUtils.FORMAT_SHOW_TIME | - DateUtils.FORMAT_SHOW_YEAR | DateUtils.FORMAT_ABBREV_MONTH); - vCreation.setText(context.getString(R.string.label_key_created, dateTime)); - - if (focusedMasterKeyId != null) { - if (focusedMasterKeyId != item.masterKeyId) { - itemView.animate().alpha(0.2f).start(); - vState.setDisplayedChild(isFinished ? STATE_TRANSFERRED : STATE_INVISIBLE); - } else { - itemView.setAlpha(1.0f); - vState.setDisplayedChild(STATE_PROGRESS); - } - } else { - itemView.animate().alpha(1.0f).start(); - vState.setDisplayedChild(isFinished ? STATE_TRANSFERRED : STATE_IMPORT_BUTTON); - } - - if (focusedMasterKeyId == null && onClickReceiveKeyListener != null) { - vImportButton.setOnClickListener(new OnClickListener() { - @Override - public void onClick(View v) { - onClickReceiveKeyListener.onUiClickImportKey(item.masterKeyId, item.keyData); - } - }); - } else { - vImportButton.setOnClickListener(null); - } - } - } - - public interface OnClickImportKeyListener { - void onUiClickImportKey(long masterKeyId, String keyData); - } - - public static class ReceivedKeyItem { - private final String keyData; - - private final long masterKeyId; - private final long creationMillis; - private final String name; - private final String email; - - public ReceivedKeyItem(String keyData, long masterKeyId, long creationMillis, String name, String email) { - this.keyData = keyData; - this.masterKeyId = masterKeyId; - this.creationMillis = creationMillis; - this.name = name; - this.email = email; - } - } -} diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/transfer/view/TransferFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/transfer/view/TransferFragment.java deleted file mode 100644 index fe55fc432..000000000 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/transfer/view/TransferFragment.java +++ /dev/null @@ -1,445 +0,0 @@ -/* - * Copyright (C) 2017 Schürmann & Breitmoser GbR - * - * 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 3 of the License, or - * (at your option) any later version. - * - * 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.sufficientlysecure.keychain.ui.transfer.view; - - -import android.app.Activity; -import androidx.lifecycle.ViewModelProviders; -import android.content.BroadcastReceiver; -import android.content.Context; -import android.content.DialogInterface; -import android.content.DialogInterface.OnDismissListener; -import android.content.Intent; -import android.content.IntentFilter; -import android.graphics.Bitmap; -import android.net.NetworkInfo; -import android.net.wifi.WifiManager; -import android.os.Build.VERSION_CODES; -import android.os.Bundle; -import android.os.Parcelable; -import androidx.annotation.Nullable; -import androidx.annotation.RequiresApi; -import androidx.fragment.app.Fragment; -import androidx.fragment.app.FragmentActivity; -import androidx.fragment.app.FragmentManager; -import androidx.fragment.app.FragmentManager.OnBackStackChangedListener; -import androidx.appcompat.app.AlertDialog; -import androidx.appcompat.app.AlertDialog.Builder; -import androidx.recyclerview.widget.RecyclerView; -import androidx.recyclerview.widget.RecyclerView.Adapter; -import android.text.TextUtils; -import android.view.LayoutInflater; -import android.view.Menu; -import android.view.MenuInflater; -import android.view.MenuItem; -import android.view.View; -import android.view.View.OnClickListener; -import android.view.ViewGroup; -import android.view.ViewTreeObserver.OnGlobalLayoutListener; -import android.widget.ImageView; -import android.widget.TextView; - -import com.google.zxing.client.android.Intents; -import org.sufficientlysecure.keychain.R; -import org.sufficientlysecure.keychain.operations.results.ImportKeyResult; -import org.sufficientlysecure.keychain.operations.results.OperationResult; -import org.sufficientlysecure.keychain.ui.MainActivity; -import org.sufficientlysecure.keychain.ui.QrCodeCaptureActivity; -import org.sufficientlysecure.keychain.ui.base.CryptoOperationHelper; -import org.sufficientlysecure.keychain.ui.base.CryptoOperationHelper.Callback; -import org.sufficientlysecure.keychain.ui.keyview.GenericViewModel; -import org.sufficientlysecure.keychain.ui.transfer.presenter.TransferPresenter; -import org.sufficientlysecure.keychain.ui.transfer.presenter.TransferPresenter.TransferMvpView; -import org.sufficientlysecure.keychain.ui.util.Notify; -import org.sufficientlysecure.keychain.ui.util.Notify.Style; -import org.sufficientlysecure.keychain.ui.widget.ConnectionStatusView; -import org.sufficientlysecure.keychain.ui.widget.ToolableViewAnimator; - - -@RequiresApi(api = VERSION_CODES.LOLLIPOP) -public class TransferFragment extends Fragment implements TransferMvpView { - public static final int REQUEST_CODE_SCAN = 1; - public static final int LOADER_ID = 1; - - public static final String EXTRA_OPENPGP_SKT_INFO = "openpgp_skt_info"; - - - private ImageView vQrCodeImage; - private TransferPresenter presenter; - private ToolableViewAnimator vTransferAnimator; - private TextView vConnectionStatusText1; - private TextView vConnectionStatusText2; - private ConnectionStatusView vConnectionStatusView1; - private ConnectionStatusView vConnectionStatusView2; - private RecyclerView vTransferKeyList; - private View vTransferKeyListEmptyView; - private RecyclerView vReceivedKeyList; - - private CryptoOperationHelper currentCryptoOperationHelper; - private BroadcastReceiver broadcastReceiver = new BroadcastReceiver() { - @Override - public void onReceive(Context context, Intent intent) { - if (WifiManager.NETWORK_STATE_CHANGED_ACTION.equals(intent.getAction())) { - NetworkInfo networkInfo = intent.getParcelableExtra(WifiManager.EXTRA_NETWORK_INFO); - if (networkInfo != null && networkInfo.isConnected()) { - presenter.onWifiConnected(); - } - } - } - }; - private boolean showDoneIcon; - private AlertDialog confirmationDialog; - private TextView vWifiErrorInstructions; - - @Override - public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { - View view = inflater.inflate(R.layout.transfer_fragment, container, false); - - vTransferAnimator = view.findViewById(R.id.transfer_animator); - - vConnectionStatusText1 = view.findViewById(R.id.connection_status_1); - vConnectionStatusText2 = view.findViewById(R.id.connection_status_2); - vConnectionStatusView1 = view.findViewById(R.id.connection_status_icon_1); - vConnectionStatusView2 = view.findViewById(R.id.connection_status_icon_2); - vTransferKeyList = view.findViewById(R.id.transfer_key_list); - vTransferKeyListEmptyView = view.findViewById(R.id.transfer_key_list_empty); - vReceivedKeyList = view.findViewById(R.id.received_key_list); - vWifiErrorInstructions = view.findViewById(R.id.transfer_wifi_error_instructions); - - vQrCodeImage = view.findViewById(R.id.qr_code_image); - - view.findViewById(R.id.button_scan).setOnClickListener(new OnClickListener() { - @Override - public void onClick(View v) { - if (presenter != null) { - presenter.onUiClickScan(); - } - } - }); - - view.findViewById(R.id.button_scan_again).setOnClickListener(new OnClickListener() { - @Override - public void onClick(View v) { - if (presenter != null) { - presenter.onUiClickScanAgain(); - } - } - }); - - GenericViewModel genericViewModel = ViewModelProviders.of(this).get(GenericViewModel.class); - presenter = new TransferPresenter(getContext(), this, genericViewModel, this); - - setHasOptionsMenu(true); - - return view; - } - - @Override - public void onActivityCreated(@Nullable Bundle savedInstanceState) { - super.onActivityCreated(savedInstanceState); - - if (savedInstanceState != null) { - return; - } - - Intent activityIntent = getActivity().getIntent(); - if (activityIntent != null && activityIntent.hasExtra(EXTRA_OPENPGP_SKT_INFO)) { - presenter.onUiInitFromIntentUri(activityIntent.getParcelableExtra(EXTRA_OPENPGP_SKT_INFO)); - } - } - - @Override - public void onStart() { - super.onStart(); - - presenter.onUiStart(); - } - - @Override - public void onResume() { - super.onResume(); - - IntentFilter intentFilter = new IntentFilter(); - intentFilter.addAction(WifiManager.NETWORK_STATE_CHANGED_ACTION); - getContext().registerReceiver(broadcastReceiver, intentFilter); - } - - @Override - public void onPause() { - super.onPause(); - - getContext().unregisterReceiver(broadcastReceiver); - } - - @Override - public void onStop() { - super.onStop(); - - presenter.onUiStop(); - } - - @Override - public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) { - if (showDoneIcon) { - inflater.inflate(R.menu.transfer_menu, menu); - } - } - - @Override - public boolean onOptionsItemSelected(MenuItem item) { - if (item.getItemId() == R.id.menu_done) { - presenter.onUiClickDone(); - return true; - } - return false; - } - - @Override - public void showNotOnWifi() { - vTransferAnimator.setDisplayedChildId(R.id.transfer_layout_no_wifi); - } - - @Override - public void showWaitingForConnection() { - vTransferAnimator.setDisplayedChildId(R.id.transfer_layout_waiting); - } - - @Override - public void showEstablishingConnection() { - vTransferAnimator.setDisplayedChildId(R.id.transfer_layout_connecting); - } - - @Override - public void showConnectionEstablished(String hostname) { - // String statusText = getString(R.string.transfer_status_connected, hostname); - - vConnectionStatusText1.setText(R.string.transfer_status_connected); - vConnectionStatusText2.setText(R.string.transfer_status_connected); - - vConnectionStatusView1.setConnected(true); - vConnectionStatusView2.setConnected(true); - - vTransferAnimator.setDisplayedChildId(R.id.transfer_layout_connected); - } - - @Override - public void showWifiError(String wifiSsid) { - vTransferAnimator.setDisplayedChildId(R.id.transfer_layout_wifi_error); - - if (!TextUtils.isEmpty(wifiSsid)) { - vWifiErrorInstructions - .setText(getResources().getString(R.string.transfer_error_wifi_text_instructions_ssid, wifiSsid)); - } else { - vWifiErrorInstructions.setText(R.string.transfer_error_wifi_text_instructions); - } - } - - @Override - public void showReceivingKeys() { - vTransferAnimator.setDisplayedChildId(R.id.transfer_layout_passive); - } - - @Override - public void showViewDisconnected() { - vConnectionStatusText1.setText(R.string.transfer_status_disconnected); - vConnectionStatusText2.setText(R.string.transfer_status_disconnected); - - vConnectionStatusView1.setConnected(false); - vConnectionStatusView2.setConnected(false); - } - - @Override - public void setQrImage(final Bitmap qrCode) { - vQrCodeImage.getViewTreeObserver().addOnGlobalLayoutListener( - new OnGlobalLayoutListener() { - @Override - public void onGlobalLayout() { - int viewSize = vQrCodeImage.getWidth(); - if (viewSize == 0) { - return; - } - // create actual bitmap in display dimensions - Bitmap scaled = Bitmap.createScaledBitmap(qrCode, viewSize, viewSize, false); - vQrCodeImage.setImageBitmap(scaled); - } - }); - vQrCodeImage.requestLayout(); - } - - @Override - public void scanQrCode() { - Intent intent = new Intent(getActivity(), QrCodeCaptureActivity.class); - startActivityForResult(intent, REQUEST_CODE_SCAN); - } - - @Override - public void setShowDoneIcon(boolean showDoneIcon) { - this.showDoneIcon = showDoneIcon; - FragmentActivity activity = getActivity(); - if (activity != null) { - activity.invalidateOptionsMenu(); - } - } - - @Override - public void setSecretKeyAdapter(Adapter adapter) { - vTransferKeyList.setAdapter(adapter); - } - - @Override - public void setShowSecretKeyEmptyView(boolean isEmpty) { - vTransferKeyListEmptyView.setVisibility(isEmpty ? View.VISIBLE : View.GONE); - } - - @Override - public void setReceivedKeyAdapter(Adapter adapter) { - vReceivedKeyList.setAdapter(adapter); - } - - @Override - public CryptoOperationHelper createCryptoOperationHelper(Callback callback) { - CryptoOperationHelper cryptoOperationHelper = new CryptoOperationHelper<>(1, this, callback, null); - currentCryptoOperationHelper = cryptoOperationHelper; - return cryptoOperationHelper; - } - - @Override - public void releaseCryptoOperationHelper() { - currentCryptoOperationHelper = null; - } - - @Override - public void showErrorBadKey() { - Notify.create(getActivity(), R.string.transfer_error_read_incoming, Style.ERROR).show(); - } - - @Override - public void showErrorConnectionFailed() { - Notify.create(getActivity(), R.string.transfer_error_connect, Style.ERROR).show(); - } - - @Override - public void showErrorListenFailed() { - Notify.create(getActivity(), R.string.transfer_error_listen, Style.ERROR).show(); - } - - @Override - public void showErrorConnectionError(String errorMessage) { - if (errorMessage != null) { - String text = getString(R.string.transfer_error_generic_msg, errorMessage); - Notify.create(getActivity(), text, Style.ERROR).show(); - } else { - Notify.create(getActivity(), R.string.transfer_error_generic, Style.ERROR).show(); - } - } - - @Override - public void showResultNotification(ImportKeyResult result) { - result.createNotify(getActivity()).show(); - } - - @Override - public void addFakeBackStackItem(final String tag) { - FragmentManager fragmentManager = getFragmentManager(); - - fragmentManager.beginTransaction() - .addToBackStack(tag) - .commitAllowingStateLoss(); - fragmentManager.executePendingTransactions(); - - fragmentManager.addOnBackStackChangedListener(new OnBackStackChangedListener() { - @Override - public void onBackStackChanged() { - FragmentManager fragMan = getFragmentManager(); - if (fragMan == null) { - return; - } - fragMan.popBackStack(tag, FragmentManager.POP_BACK_STACK_INCLUSIVE); - fragMan.removeOnBackStackChangedListener(this); - - presenter.onUiBackStackPop(); - } - }); - } - - @Override - public void finishFragmentOrActivity() { - FragmentActivity activity = getActivity(); - if (activity != null) { - if (activity instanceof MainActivity) { - ((MainActivity) activity).onKeysSelected(); - } else { - activity.finish(); - } - } - } - - @Override - public void showConfirmSendDialog() { - if (confirmationDialog != null) { - return; - } - confirmationDialog = new Builder(getContext()) - .setTitle(R.string.transfer_confirm_title) - .setMessage(R.string.transfer_confirm_text) - .setPositiveButton(R.string.transfer_confirm_ok, new DialogInterface.OnClickListener() { - public void onClick(DialogInterface dialog, int whichButton) { - if (whichButton == DialogInterface.BUTTON_POSITIVE) { - presenter.onUiClickConfirmSend(); - } else { - dialog.dismiss(); - } - } - }) - .setNegativeButton(R.string.transfer_confirm_cancel, null) - .setOnDismissListener(new OnDismissListener() { - @Override - public void onDismiss(DialogInterface dialog) { - confirmationDialog = null; - } - }) - .create(); - confirmationDialog.show(); - } - - @Override - public void dismissConfirmationIfExists() { - if (confirmationDialog != null && confirmationDialog.isShowing()) { - confirmationDialog.dismiss(); - } - } - - @Override - public void onActivityResult(int requestCode, int resultCode, Intent data) { - if (currentCryptoOperationHelper != null && - currentCryptoOperationHelper.handleActivityResult(requestCode, resultCode, data)) { - return; - } - - switch (requestCode) { - case REQUEST_CODE_SCAN: - if (resultCode == Activity.RESULT_OK) { - String qrCodeData = data.getStringExtra(Intents.Scan.RESULT); - presenter.onUiQrCodeScanned(qrCodeData); - } - break; - default: - super.onActivityResult(requestCode, resultCode, data); - } - } -} diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/transfer/view/TransferNotAvailableFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/transfer/view/TransferNotAvailableFragment.java deleted file mode 100644 index a45de3191..000000000 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/transfer/view/TransferNotAvailableFragment.java +++ /dev/null @@ -1,36 +0,0 @@ -/* - * Copyright (C) 2017 Schürmann & Breitmoser GbR - * - * 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 3 of the License, or - * (at your option) any later version. - * - * 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.sufficientlysecure.keychain.ui.transfer.view; - - -import android.os.Bundle; -import androidx.annotation.Nullable; -import androidx.fragment.app.Fragment; -import android.view.LayoutInflater; -import android.view.View; -import android.view.ViewGroup; - -import org.sufficientlysecure.keychain.R; - - -public class TransferNotAvailableFragment extends Fragment { - @Override - public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { - return inflater.inflate(R.layout.transfer_not_available_fragment, container, false); - } -} diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/transfer/view/TransferSecretKeyList.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/transfer/view/TransferSecretKeyList.java deleted file mode 100644 index 2f9e5b8f2..000000000 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/transfer/view/TransferSecretKeyList.java +++ /dev/null @@ -1,209 +0,0 @@ -/* - * Copyright (C) 2017 Schürmann & Breitmoser GbR - * - * 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 3 of the License, or - * (at your option) any later version. - * - * 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.sufficientlysecure.keychain.ui.transfer.view; - - -import java.util.ArrayList; -import java.util.List; - -import android.content.Context; -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; -import androidx.recyclerview.widget.LinearLayoutManager; -import androidx.recyclerview.widget.RecyclerView; -import android.text.format.DateUtils; -import android.util.AttributeSet; -import android.view.LayoutInflater; -import android.view.View; -import android.view.ViewGroup; -import android.widget.TextView; -import android.widget.ViewAnimator; - -import org.sufficientlysecure.keychain.R; -import org.sufficientlysecure.keychain.model.SubKey.UnifiedKeyInfo; -import org.sufficientlysecure.keychain.ui.util.recyclerview.DividerItemDecoration; - - -public class TransferSecretKeyList extends RecyclerView { - private static final int STATE_INVISIBLE = 0; - private static final int STATE_BUTTON = 1; - private static final int STATE_PROGRESS = 2; - private static final int STATE_TRANSFERRED = 3; - // private static final int STATE_IMPORT_BUTTON = 4; // used in ReceivedSecretKeyList - - - public TransferSecretKeyList(Context context) { - super(context); - init(context); - } - - public TransferSecretKeyList(Context context, @Nullable AttributeSet attrs) { - super(context, attrs); - init(context); - } - - public TransferSecretKeyList(Context context, @Nullable AttributeSet attrs, int defStyle) { - super(context, attrs, defStyle); - init(context); - } - - private void init(Context context) { - setLayoutManager(new LinearLayoutManager(context)); - addItemDecoration(new DividerItemDecoration(context, DividerItemDecoration.VERTICAL_LIST, true)); - setItemAnimator(null); - } - - public static class TransferKeyAdapter extends RecyclerView.Adapter { - private final Context context; - private final LayoutInflater layoutInflater; - private final OnClickTransferKeyListener onClickTransferKeyListener; - - private Long focusedMasterKeyId; - private List data; - private ArrayList finishedItems = new ArrayList<>(); - private boolean allItemsDisabled; - - - public TransferKeyAdapter(Context context, LayoutInflater layoutInflater, - OnClickTransferKeyListener onClickTransferKeyListener) { - this.context = context; - this.layoutInflater = layoutInflater; - this.onClickTransferKeyListener = onClickTransferKeyListener; - } - - @NonNull - @Override - public TransferKeyViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { - return new TransferKeyViewHolder(layoutInflater.inflate(R.layout.key_transfer_item, parent, false)); - } - - @Override - public void onBindViewHolder(@NonNull TransferKeyViewHolder holder, int position) { - UnifiedKeyInfo item = data.get(position); - boolean isFinished = finishedItems.contains(item.master_key_id()); - holder.bind(context, item, onClickTransferKeyListener, focusedMasterKeyId, isFinished, allItemsDisabled); - } - - @Override - public int getItemCount() { - return data != null ? data.size() : 0; - } - - @Override - public long getItemId(int position) { - return data.get(position).master_key_id(); - } - - public void setData(List data) { - this.data = data; - notifyDataSetChanged(); - } - - public void clearFinishedItems() { - finishedItems.clear(); - notifyItemRangeChanged(0, getItemCount()); - } - - public void addToFinishedItems(long masterKeyId) { - finishedItems.add(masterKeyId); - // doeesn't notify, because it's non-trivial and this is called in conjunction with other refreshing things! - } - - public void focusItem(Long masterKeyId) { - focusedMasterKeyId = masterKeyId; - notifyItemRangeChanged(0, getItemCount()); - } - - public void setAllDisabled(boolean allItemsdisablde) { - allItemsDisabled = allItemsdisablde; - notifyItemRangeChanged(0, getItemCount()); - } - } - - static class TransferKeyViewHolder extends RecyclerView.ViewHolder { - private final TextView vName; - private final TextView vEmail; - private final TextView vCreation; - private final View vSendButton; - private final ViewAnimator vState; - - TransferKeyViewHolder(View itemView) { - super(itemView); - - vName = itemView.findViewById(R.id.key_list_item_name); - vEmail = itemView.findViewById(R.id.key_list_item_email); - vCreation = itemView.findViewById(R.id.key_list_item_creation); - - vSendButton = itemView.findViewById(R.id.button_transfer); - vState = itemView.findViewById(R.id.transfer_state); - } - - private void bind(Context context, UnifiedKeyInfo item, - final OnClickTransferKeyListener onClickTransferKeyListener, Long focusedMasterKeyId, - boolean isFinished, boolean disableAll) { - if (item.name() != null) { - vName.setText(item.name()); - vName.setVisibility(View.VISIBLE); - } else { - vName.setVisibility(View.GONE); - } - if (item.email() != null) { - vEmail.setText(item.email()); - vEmail.setVisibility(View.VISIBLE); - } else { - vEmail.setVisibility(View.GONE); - } - - String dateTime = DateUtils.formatDateTime(context, item.creation() * 1000, - DateUtils.FORMAT_SHOW_DATE | DateUtils.FORMAT_SHOW_TIME | - DateUtils.FORMAT_SHOW_YEAR | DateUtils.FORMAT_ABBREV_MONTH); - vCreation.setText(context.getString(R.string.label_key_created, dateTime)); - - if (disableAll) { - itemView.setAlpha(0.2f); - vState.setDisplayedChild(STATE_INVISIBLE); - vSendButton.setOnClickListener(null); - return; - } - - if (focusedMasterKeyId != null) { - if (focusedMasterKeyId != item.master_key_id()) { - itemView.animate().alpha(0.2f).start(); - vState.setDisplayedChild(isFinished ? STATE_TRANSFERRED : STATE_INVISIBLE); - } else { - itemView.setAlpha(1.0f); - vState.setDisplayedChild(STATE_PROGRESS); - } - } else { - itemView.animate().alpha(1.0f).start(); - vState.setDisplayedChild(isFinished ? STATE_TRANSFERRED : STATE_BUTTON); - } - - if (focusedMasterKeyId == null && onClickTransferKeyListener != null) { - vSendButton.setOnClickListener( - v -> onClickTransferKeyListener.onUiClickTransferKey(item.master_key_id())); - } else { - vSendButton.setOnClickListener(null); - } - } - } - - public interface OnClickTransferKeyListener { - void onUiClickTransferKey(long masterKeyId); - } -} diff --git a/OpenKeychain/src/main/res/layout/create_key_start_fragment.xml b/OpenKeychain/src/main/res/layout/create_key_start_fragment.xml index 64be0789f..15420c92b 100644 --- a/OpenKeychain/src/main/res/layout/create_key_start_fragment.xml +++ b/OpenKeychain/src/main/res/layout/create_key_start_fragment.xml @@ -116,24 +116,6 @@ android:clickable="true" style="?android:attr/borderlessButtonStyle" /> - - - - "Close navigation drawer" "My Keys" "Backup/Restore" - "Secure Wifi Transfer" "Type text" diff --git a/OpenKeychain/src/test/java/org/sufficientlysecure/keychain/network/KeyTransferInteractorTest.java b/OpenKeychain/src/test/java/org/sufficientlysecure/keychain/network/KeyTransferInteractorTest.java deleted file mode 100644 index 1e8f33b37..000000000 --- a/OpenKeychain/src/test/java/org/sufficientlysecure/keychain/network/KeyTransferInteractorTest.java +++ /dev/null @@ -1,122 +0,0 @@ -package org.sufficientlysecure.keychain.network; - - -import java.net.URISyntaxException; - -import android.os.Build.VERSION_CODES; -import androidx.annotation.RequiresApi; - -import junit.framework.Assert; -import org.robolectric.shadows.ShadowLog; -import org.robolectric.shadows.ShadowLooper; -import org.sufficientlysecure.keychain.network.KeyTransferInteractor.KeyTransferCallback; - -import static junit.framework.Assert.assertTrue; -import static junit.framework.Assert.fail; - - -@SuppressWarnings("WeakerAccess") -// disabled, because we can't easily mock the tls-psk ciphersuite (it's removed in bouncycastle) :( -//@RunWith(KeychainTestRunner.class) -@RequiresApi(api = VERSION_CODES.LOLLIPOP) -public class KeyTransferInteractorTest { - private static final String DELIM_START = "--"; - private static final String DELIM_END = "--"; - - private String receivedQrCodeData; - private boolean clientConnectionEstablished; - private boolean serverConnectionEstablished; - -// @Before - public void setUp() throws Exception { - ShadowLog.stream = System.out; - } - -// @Test - public void testServerShouldGiveSuccessCallback() throws URISyntaxException { - KeyTransferInteractor serverKeyTransferInteractor = new KeyTransferInteractor(DELIM_START, DELIM_END); - - serverKeyTransferInteractor.startServer(new SimpleKeyTransferCallback() { - @Override - public void onServerStarted(String qrCodeData) { - receivedQrCodeData = qrCodeData; - } - - @Override - public void onConnectionEstablished(String otherName) { - serverConnectionEstablished = true; - } - }, null); - waitForLooperCallback(); - Assert.assertNotNull(receivedQrCodeData); - - final KeyTransferInteractor clientKeyTransferInteractor = new KeyTransferInteractor(DELIM_START, DELIM_END); - clientKeyTransferInteractor.connectToServer(receivedQrCodeData, new SimpleKeyTransferCallback() { - @Override - public void onConnectionEstablished(String otherName) { - clientConnectionEstablished = true; - } - }); - waitForLooperCallback(); - waitForLooperCallback(); - - assertTrue(clientConnectionEstablished); - assertTrue(serverConnectionEstablished); - - serverKeyTransferInteractor.sendData(new byte[] { (byte) 1, (byte) 2 }, "passthrough"); - waitForLooperCallback(); - } - - private void waitForLooperCallback() { - while (!ShadowLooper.getShadowMainLooper().getScheduler().runOneTask()); - } - - - static class SimpleKeyTransferCallback implements KeyTransferCallback { - @Override - public void onServerStarted(String qrCodeData) { - fail("unexpected callback: onServerStarted"); - } - - @Override - public void onConnectionEstablished(String otherName) { - fail("unexpected callback: onConnectionEstablished"); - } - - @Override - public void onConnectionLost() { - fail("unexpected callback: onConnectionLost"); - } - - @Override - public void onDataReceivedOk(String receivedData) { - fail("unexpected callback: onDataReceivedOk"); - } - - @Override - public void onDataSentOk(String passthrough) { - fail("unexpected callback: onDataSentOk"); - } - - @Override - public void onConnectionErrorNoRouteToHost(String wifiSsid) { - fail("unexpected callback: onConnectionErrorNoRouteToHost"); - } - - @Override - public void onConnectionErrorConnect() { - fail("unexpected callback: onConnectionErrorConnect"); - } - - @Override - public void onConnectionErrorListen() { - fail("unexpected callback: onConnectionErrorListen"); - } - - @Override - public void onConnectionError(String arg) { - fail("unexpected callback: onConnectionError"); - } - } - -} diff --git a/OpenKeychain/src/test/java/org/sufficientlysecure/keychain/network/SktUriTest.java b/OpenKeychain/src/test/java/org/sufficientlysecure/keychain/network/SktUriTest.java deleted file mode 100644 index 8e15691c5..000000000 --- a/OpenKeychain/src/test/java/org/sufficientlysecure/keychain/network/SktUriTest.java +++ /dev/null @@ -1,101 +0,0 @@ -package org.sufficientlysecure.keychain.network; - - -import java.net.URISyntaxException; - -import android.annotation.SuppressLint; - -import org.bouncycastle.util.encoders.Hex; -import org.junit.Test; - -import static org.junit.Assert.assertArrayEquals; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotNull; - - -@SuppressWarnings("WeakerAccess") -@SuppressLint("DefaultLocale") -public class SktUriTest { - static final String HOST = "127.0.0.1"; - static final int PORT = 1234; - static final byte[] PRESHARED_KEY = { 1, 2 }; - static final String SSID = "ssid"; - - static final String ENCODED_SKT = String.format("OPGPSKT:%s/%d/%s/SSID:%s", - HOST, PORT, Hex.toHexString(PRESHARED_KEY), Hex.toHexString(SSID.getBytes())); - - @Test - public void testCreate() { - SktUri sktUri = SktUri.create(HOST, PORT, PRESHARED_KEY, null); - - assertEquals(HOST, sktUri.getHost()); - assertEquals(PORT, sktUri.getPort()); - assertArrayEquals(PRESHARED_KEY, sktUri.getPresharedKey()); - assertEquals(null, sktUri.getWifiSsid()); - } - - @Test - public void testCreateWithSsid() { - SktUri sktUri = SktUri.create(HOST, PORT, PRESHARED_KEY, SSID); - - assertEquals(HOST, sktUri.getHost()); - assertEquals(PORT, sktUri.getPort()); - assertArrayEquals(PRESHARED_KEY, sktUri.getPresharedKey()); - assertEquals(SSID, sktUri.getWifiSsid()); - } - - @Test - public void testCreate_isAllUppercase() { - SktUri sktUri = SktUri.create(HOST, PORT, PRESHARED_KEY, SSID); - - String encodedSktUri = sktUri.toUriString(); - assertEquals(encodedSktUri.toUpperCase(), encodedSktUri); - } - - @Test - public void testParse() throws URISyntaxException { - SktUri sktUri = SktUri.parse(ENCODED_SKT); - - assertNotNull(sktUri); - assertEquals(HOST, sktUri.getHost()); - assertEquals(PORT, sktUri.getPort()); - assertArrayEquals(PRESHARED_KEY, sktUri.getPresharedKey()); - assertEquals(SSID, sktUri.getWifiSsid()); - } - - @Test - public void testBackAndForth() throws URISyntaxException { - SktUri sktUri = SktUri.create(HOST, PORT, PRESHARED_KEY, null); - String encodedSktUri = sktUri.toUriString(); - SktUri decodedSktUri = SktUri.parse(encodedSktUri); - - assertEquals(sktUri, decodedSktUri); - } - - @Test - public void testBackAndForthWithSsid() throws URISyntaxException { - SktUri sktUri = SktUri.create(HOST, PORT, PRESHARED_KEY, SSID); - String encodedSktUri = sktUri.toUriString(); - SktUri decodedSktUri = SktUri.parse(encodedSktUri); - - assertEquals(sktUri, decodedSktUri); - } - - @Test(expected = URISyntaxException.class) - public void testParse_withBadScheme_shouldFail() throws URISyntaxException { - SktUri.parse(String.format("XXXGPSKT:%s/%d/%s/SSID:%s", - HOST, PORT, Hex.toHexString(PRESHARED_KEY), Hex.toHexString(SSID.getBytes()))); - } - - @Test(expected = URISyntaxException.class) - public void testParse_withBadPsk_shouldFail() throws URISyntaxException { - SktUri.parse(String.format("OPGPSKT:%s/%d/xx%s/SSID:%s", - HOST, PORT, Hex.toHexString(PRESHARED_KEY), Hex.toHexString(SSID.getBytes()))); - } - - @Test(expected = URISyntaxException.class) - public void testParse_withBadPort_shouldFail() throws URISyntaxException { - SktUri.parse(String.format("OPGPSKT:%s/x%d/%s/SSID:%s", - HOST, PORT, Hex.toHexString(PRESHARED_KEY), Hex.toHexString(SSID.getBytes()))); - } -} \ No newline at end of file