From e44d668e277275cb4a22bc8a516aec984b14028a Mon Sep 17 00:00:00 2001 From: Vincent Breitmoser Date: Tue, 30 May 2017 18:05:50 +0200 Subject: [PATCH] tls-psk: display list of incoming keys on receiving side --- .../network/KeyTransferInteractor.java | 13 +- .../keychain/pgp/UncachedKeyRing.java | 4 + .../transfer/presenter/TransferPresenter.java | 38 +++- .../transfer/view/ReceivedSecretKeyList.java | 186 ++++++++++++++++++ .../ui/transfer/view/TransferFragment.java | 37 +--- .../transfer/view/TransferSecretKeyList.java | 10 +- .../src/main/res/layout/key_transfer_item.xml | 11 +- .../src/main/res/layout/transfer_fragment.xml | 41 +++- 8 files changed, 295 insertions(+), 45 deletions(-) create mode 100644 OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/transfer/view/ReceivedSecretKeyList.java diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/network/KeyTransferInteractor.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/network/KeyTransferInteractor.java index 0c4860ced..a1b354d43 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/network/KeyTransferInteractor.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/network/KeyTransferInteractor.java @@ -135,8 +135,8 @@ public class KeyTransferInteractor { } handleOpenConnection(socket); - Log.d(Constants.TAG, "connection closed ok!"); } catch (IOException e) { + invokeListener(CONNECTION_LOST, null); Log.e(Constants.TAG, "error!", e); } finally { try { @@ -176,8 +176,13 @@ public class KeyTransferInteractor { socket.setSoTimeout(500); while (!isInterrupted() && socket.isConnected()) { sendDataIfAvailable(socket, outputStream); - receiveDataIfAvailable(socket, bufferedReader); + + boolean connectionClosed = receiveDataIfAvailable(socket, bufferedReader); + if (connectionClosed) { + break; + } } + Log.d(Constants.TAG, "disconnected"); invokeListener(CONNECTION_LOST, null); } @@ -191,7 +196,6 @@ public class KeyTransferInteractor { } if (firstLine == null) { - invokeListener(CONNECTION_LOST, null); return true; } @@ -200,7 +204,7 @@ public class KeyTransferInteractor { socket.setSoTimeout(500); invokeListener(CONNECTION_RECEIVE_OK, receivedData); - return true; + return false; } private boolean sendDataIfAvailable(Socket socket, OutputStream outputStream) throws IOException { @@ -212,6 +216,7 @@ public class KeyTransferInteractor { outputStream.write(data); outputStream.write('\n'); outputStream.write('\n'); + outputStream.flush(); socket.setSoTimeout(500); invokeListener(CONNECTION_SEND_OK, sendPassthrough); diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/UncachedKeyRing.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/UncachedKeyRing.java index acdbb93fc..850c24640 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/UncachedKeyRing.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/UncachedKeyRing.java @@ -103,6 +103,10 @@ public class UncachedKeyRing { return mRing.getPublicKey().getKeyID(); } + public long getCreationTime() { + return mRing.getPublicKey().getCreationTime().getTime(); + } + public UncachedPublicKey getPublicKey() { return new UncachedPublicKey(mRing.getPublicKey()); } 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 index b44cede18..01d0a398a 100644 --- 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 @@ -34,13 +34,19 @@ import android.support.v4.content.Loader; import android.support.v7.widget.RecyclerView.Adapter; import android.view.LayoutInflater; +import org.openintents.openpgp.util.OpenPgpUtils; +import org.openintents.openpgp.util.OpenPgpUtils.UserId; import org.sufficientlysecure.keychain.Constants; import org.sufficientlysecure.keychain.network.KeyTransferInteractor; import org.sufficientlysecure.keychain.network.KeyTransferInteractor.KeyTransferCallback; +import org.sufficientlysecure.keychain.pgp.UncachedKeyRing; import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralException; import org.sufficientlysecure.keychain.provider.KeyRepository; import org.sufficientlysecure.keychain.provider.KeyRepository.NotFoundException; import org.sufficientlysecure.keychain.ui.transfer.loader.SecretKeyLoader.SecretKeyItem; +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; @@ -49,7 +55,7 @@ import org.sufficientlysecure.keychain.util.Log; @RequiresApi(api = VERSION_CODES.LOLLIPOP) public class TransferPresenter implements KeyTransferCallback, LoaderCallbacks>, - OnClickTransferKeyListener { + OnClickTransferKeyListener, OnClickImportKeyListener { private final Context context; private final TransferMvpView view; private final LoaderManager loaderManager; @@ -58,6 +64,7 @@ public class TransferPresenter implements KeyTransferCallback, LoaderCallbacks { + 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 focusItem(Long masterKeyId) { + focusedMasterKeyId = masterKeyId; + notifyItemRangeChanged(0, getItemCount()); + } + + public void addItem(ReceivedKeyItem receivedKeyItem) { + data.add(receivedKeyItem); + notifyItemInserted(data.size() -1); + } + } + + 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 = (TextView) itemView.findViewById(R.id.key_list_item_name); + vEmail = (TextView) itemView.findViewById(R.id.key_list_item_email); + vCreation = (TextView) itemView.findViewById(R.id.key_list_item_creation); + + vImportButton = itemView.findViewById(R.id.button_import); + vState = (ViewAnimator) 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.keyData); + } + }); + } else { + vImportButton.setOnClickListener(null); + } + } + } + + public interface OnClickImportKeyListener { + void onUiClickImportKey(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 index 58c11646b..94c343142 100644 --- 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 @@ -55,6 +55,7 @@ public class TransferFragment extends Fragment implements TransferMvpView { public static final int VIEW_WAITING = 0; public static final int VIEW_CONNECTED = 1; public static final int VIEW_SEND_OK = 2; + public static final int VIEW_RECEIVING = 3; public static final int REQUEST_CODE_SCAN = 1; public static final int LOADER_ID = 1; @@ -65,6 +66,7 @@ public class TransferFragment extends Fragment implements TransferMvpView { private ViewAnimator vTransferAnimator; private TextView vConnectionStatusText; private RecyclerView vTransferKeyList; + private RecyclerView vReceivedKeyList; @Override @@ -75,6 +77,7 @@ public class TransferFragment extends Fragment implements TransferMvpView { vConnectionStatusText = (TextView) view.findViewById(R.id.connection_status); vTransferKeyList = (RecyclerView) view.findViewById(R.id.transfer_key_list); + vReceivedKeyList = (RecyclerView) view.findViewById(R.id.received_key_list); vQrCodeImage = (ImageView) view.findViewById(R.id.qr_code_image); @@ -124,8 +127,8 @@ public class TransferFragment extends Fragment implements TransferMvpView { } @Override - public void showKeySentOk() { - vTransferAnimator.setDisplayedChild(VIEW_SEND_OK); + public void showReceivingKeys() { + vTransferAnimator.setDisplayedChild(VIEW_RECEIVING); } @Override @@ -155,34 +158,8 @@ public class TransferFragment extends Fragment implements TransferMvpView { } @Override - public void showFakeSendProgressDialog() { - final ProgressDialogFragment progressDialogFragment = - ProgressDialogFragment.newInstance("Sending key…", ProgressDialog.STYLE_HORIZONTAL, false); - progressDialogFragment.show(getFragmentManager(), "progress"); - - final Handler handler = new Handler(); - - Timer timer = new Timer(); - timer.scheduleAtFixedRate(new TimerTask() { - int fakeProgress = 0; - - @Override - public void run() { - fakeProgress += 6; - if (fakeProgress > 100) { - cancel(); - progressDialogFragment.dismissAllowingStateLoss(); - handler.post(new Runnable() { - @Override - public void run() { - presenter.onUiFakeProgressFinished(); - } - }); - return; - } - progressDialogFragment.setProgress(fakeProgress, 100); - } - }, 0, 100); + public void setReceivedKeyAdapter(Adapter adapter) { + vReceivedKeyList.setAdapter(adapter); } @Override 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 index a5f74ae28..620385d67 100644 --- 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 @@ -28,6 +28,7 @@ public class TransferSecretKeyList extends RecyclerView { 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; public TransferSecretKeyList(Context context) { @@ -58,7 +59,7 @@ public class TransferSecretKeyList extends RecyclerView { private Long focusedMasterKeyId; private List data; - private ArrayList finishedItems; + private ArrayList finishedItems = new ArrayList<>(); public TransferKeyAdapter(Context context, LayoutInflater layoutInflater, @@ -92,12 +93,17 @@ public class TransferSecretKeyList extends RecyclerView { public void setData(List data) { this.data = data; - this.finishedItems = new ArrayList<>(); 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) { diff --git a/OpenKeychain/src/main/res/layout/key_transfer_item.xml b/OpenKeychain/src/main/res/layout/key_transfer_item.xml index 2faf2aef0..e5939f51b 100644 --- a/OpenKeychain/src/main/res/layout/key_transfer_item.xml +++ b/OpenKeychain/src/main/res/layout/key_transfer_item.xml @@ -54,7 +54,8 @@ android:outAnimation="@anim/fade_out" android:inAnimation="@anim/fade_in" android:id="@+id/transfer_state" - custom:initialView="2"> + android:measureAllChildren="false" + custom:initialView="04"> +