Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement authentication on sending and receiving device #28

Merged
Merged
Show file tree
Hide file tree
Changes from 31 commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
1997693
Add QR Code scanner view and dialog
ekigamba Mar 14, 2019
3910b7d
Add permissions required by QR code scanner
ekigamba Mar 14, 2019
9721189
Add CameraSourcePreview for QR code scanner
ekigamba Mar 14, 2019
12bb9f5
Change usernickname in sample application
ekigamba Mar 14, 2019
3a19ce9
Implement authentication on sending device
ekigamba Mar 14, 2019
6563170
Refactor and clean code adding a SyncLifecycleCallback
ekigamba Mar 15, 2019
5eb16e8
Move strings to strings.xml
ekigamba Mar 15, 2019
2dba573
Reset discovering state if the user cancells the connection
ekigamba Mar 15, 2019
12b4822
Fix tests & alert user authentication failed
ekigamba Mar 15, 2019
a5e6e28
Code cleanup
ekigamba Mar 15, 2019
609e9e5
Implement authentication on receiving device
ekigamba Mar 18, 2019
f37fc1b
Fix authentication and auth dialogs
ekigamba Mar 19, 2019
e580b2f
Code cleanup
ekigamba Mar 19, 2019
c47b9b0
Fix tests
ekigamba Mar 19, 2019
f6cce28
Add ReceiverConnectionAuthenticator tests
ekigamba Mar 19, 2019
85a623e
Add SenderConnectionAuthenticator tests
ekigamba Mar 19, 2019
f6781c6
Add SyncConnectionLifecycleCallback tests
ekigamba Mar 19, 2019
488c4d3
Code cleanup
ekigamba Mar 19, 2019
72c7831
Add tests for ReceiverSyncLifecycleCallbackTest
ekigamba Mar 19, 2019
46bd323
Add tests for SenderSyncLifecycleCallback
ekigamba Mar 19, 2019
72ec49b
Fix SenderSyncLifecycleCallback tests
ekigamba Mar 20, 2019
fd36df9
Add Permissions tests
ekigamba Mar 20, 2019
77c2776
Add more tests for P2pModeSelectInteractor
ekigamba Mar 20, 2019
156a642
Code cleanup
ekigamba Mar 20, 2019
224e5b5
Add more tests for P2pModeSelectInteractor
ekigamba Mar 20, 2019
fd596a8
Refactor presenter and synclifecyclecallback classes to 2 presenters
ekigamba Mar 21, 2019
4e1f088
Fix ConnectionAuthenticator dependency on view and interactor
ekigamba Mar 21, 2019
e403194
Move UI strings and log strings to strings.xml
ekigamba Mar 21, 2019
55b9f60
Remove duplicate strings in strings.xml
ekigamba Mar 22, 2019
1e70309
Fix failing tests
ekigamba Mar 22, 2019
d74fd52
Fix QR code size in small devices
ekigamba Mar 22, 2019
d504e30
Add BaseP2pModeSelectPresenter tests
ekigamba Mar 22, 2019
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 10 additions & 2 deletions p2p-sync/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ android {
}

testOptions.unitTests.includeAndroidResources = true

lintOptions.abortOnError = false
}

dependencies {
Expand All @@ -50,13 +50,21 @@ dependencies {
exclude group: 'com.android.support', module: 'support-v4'
}

implementation 'com.google.android.gms:play-services-vision:17.0.2'

implementation 'com.jakewharton.timber:timber:4.7.1'

testImplementation 'junit:junit:4.12'
androidTestImplementation 'com.android.support.test:runner:1.0.2'
androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2'
testImplementation 'org.mockito:mockito-core:2.25.0'
testImplementation 'org.mockito:mockito-inline:2.25.0'
testImplementation "org.robolectric:robolectric:4.2"

// ZXING DEPENDENCIES
implementation('com.journeyapps:zxing-android-embedded:3.6.0') {
exclude group: 'com.android.support', module: 'support-v4'
}
implementation 'com.google.zxing:core:3.3.0'
}

task jacocoTestReport(type: JacocoReport, dependsOn: ['testDebugUnitTest', 'createDebugCoverageReport']) {
Expand Down
3 changes: 3 additions & 0 deletions p2p-sync/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,9 @@
<uses-permission android:name="android.permission.CHANGE_WIFI_STATE" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />

<uses-permission android:name="android.permission.CAMERA"/>
<uses-permission android:name="android.permission.VIBRATE"/>

<application>
<activity android:name=".activity.P2pModeSelectActivity"></activity>
</application>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,15 @@
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.v4.app.DialogFragment;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentManager;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;
import android.widget.Toast;

import com.google.android.gms.common.api.ApiException;
import com.google.android.gms.common.api.ResolvableApiException;
Expand All @@ -27,13 +32,15 @@

import org.smartregister.p2p.R;
import org.smartregister.p2p.contract.P2pModeSelectContract;
import org.smartregister.p2p.dialog.QRCodeGeneratorDialog;
import org.smartregister.p2p.dialog.QRCodeScanningDialog;
import org.smartregister.p2p.dialog.StartDiscoveringModeProgressDialog;
import org.smartregister.p2p.dialog.StartReceiveModeProgressDialog;
import org.smartregister.p2p.handler.OnActivityRequestPermissionHandler;
import org.smartregister.p2p.handler.OnActivityResultHandler;
import org.smartregister.p2p.handler.OnResumeHandler;
import org.smartregister.p2p.interactor.P2pModeSelectInteractor;
import org.smartregister.p2p.presenter.P2pModeSelectPresenter;
import org.smartregister.p2p.presenter.P2PReceiverPresenter;
import org.smartregister.p2p.presenter.P2PSenderPresenter;
import org.smartregister.p2p.util.Constants;
import org.smartregister.p2p.util.DialogUtils;
import org.smartregister.p2p.util.Permissions;
Expand All @@ -47,8 +54,14 @@ public class P2pModeSelectActivity extends AppCompatActivity implements P2pModeS

private Button sendButton;
private Button receiveButton;
private Button sendMsgBtn;

private TextView messagesTv;
private EditText messageToSendEt;

private P2pModeSelectContract.SenderPresenter senderBasePresenter;
private P2pModeSelectContract.ReceiverPresenter receiverBasePresenter;

private P2pModeSelectContract.Presenter presenter;
private ArrayList<OnActivityResultHandler> onActivityResultHandlers = new ArrayList<>();
private ArrayList<OnResumeHandler> onResumeHandlers = new ArrayList<>();
private ArrayList<OnActivityRequestPermissionHandler> onActivityRequestPermissionHandlers = new ArrayList<>();
Expand All @@ -60,23 +73,46 @@ protected void onCreate(Bundle savedInstanceState) {

sendButton = findViewById(R.id.btn_p2pModeSelectActivity_send);
receiveButton = findViewById(R.id.btn_p2pModeSelectActivity_receive);
sendMsgBtn = findViewById(R.id.btn_p2pModeSelectActivity_sendMsgBtn);
messagesTv = findViewById(R.id.tv_p2pModeSelectActivity_conversationDetails);
messageToSendEt = findViewById(R.id.et_p2pModeSelectActivity_messageToSend);

sendMsgBtn = findViewById(R.id.btn_p2pModeSelectActivity_sendMsgBtn);
messageToSendEt = findViewById(R.id.et_p2pModeSelectActivity_messageToSend);

sendMsgBtn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if (messageToSendEt.getText() != null) {
String messageToSend = messageToSendEt.getText().toString();

// This is not ideal but for testing purposes for now
// It will not be in the any final release
receiverBasePresenter.sendTextMessage(messageToSend);
senderBasePresenter.sendTextMessage(messageToSend);
displayMessage("YOU: " + messageToSend);

messageToSendEt.setText("");
}
}
});
}

@Override
protected void onStart() {
super.onStart();
initializePresenter();
initializePresenters();

sendButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
presenter.onSendButtonClicked();
senderBasePresenter.onSendButtonClicked();
}
});
receiveButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
presenter.onReceiveButtonClicked();
receiverBasePresenter.onReceiveButtonClicked();
}
});
}
Expand All @@ -93,7 +129,22 @@ public void showReceiveProgressDialog(@NonNull DialogCancelCallback dialogCancel
StartReceiveModeProgressDialog newFragment = new StartReceiveModeProgressDialog();
newFragment.setDialogCancelCallback(dialogCancelCallback);

newFragment.show(fragmentManager, "dialog_start_receive_mode_progress");
newFragment.show(fragmentManager, Constants.DIALOG.START_RECEIVE_MODE_PROGRESS);
}

@Override
public boolean removeReceiveProgressDialog() {
Fragment fragment = getSupportFragmentManager()
.findFragmentByTag(Constants.DIALOG.START_RECEIVE_MODE_PROGRESS);

if (fragment != null && fragment instanceof DialogFragment) {
((DialogFragment) fragment)
.dismiss();

return true;
}

return false;
}

@Override
Expand All @@ -102,7 +153,65 @@ public void showDiscoveringProgressDialog(@NonNull DialogCancelCallback dialogCa
StartDiscoveringModeProgressDialog newFragment = new StartDiscoveringModeProgressDialog();
newFragment.setDialogCancelCallback(dialogCancelCallback);

newFragment.show(fragmentManager, "dialog_start_send_mode_progress");
newFragment.show(fragmentManager, Constants.DIALOG.START_SEND_MODE_PROGRESS);
}

@Override
public boolean removeDiscoveringProgressDialog() {
Fragment fragment = getSupportFragmentManager()
.findFragmentByTag(Constants.DIALOG.START_SEND_MODE_PROGRESS);

if (fragment != null && fragment instanceof DialogFragment) {
((DialogFragment) fragment)
.dismiss();

return true;
}

return false;
}

@Override
public void showQRCodeScanningDialog(@NonNull QRCodeScanningDialog.QRCodeScanDialogCallback qrCodeScanDialogCallback) {
FragmentManager fragmentManager = getSupportFragmentManager();
QRCodeScanningDialog newFragment = new QRCodeScanningDialog();
newFragment.setOnQRRecognisedListener(qrCodeScanDialogCallback);

newFragment.show(fragmentManager, Constants.DIALOG.QR_CODE_SCANNING);
}

@Override
public void showQRCodeGeneratorDialog(@NonNull String authenticationCode, @NonNull String deviceName
, @NonNull QRCodeGeneratorDialog.QRCodeAuthenticationCallback qrCodeAuthenticationCallback) {
FragmentManager fragmentManager = getSupportFragmentManager();
QRCodeGeneratorDialog newFragment = new QRCodeGeneratorDialog();
newFragment.setAuthenticationCode(authenticationCode);
newFragment.setDeviceName(deviceName);
newFragment.setQrCodeAuthenticationCallback(qrCodeAuthenticationCallback);

newFragment.show(fragmentManager, Constants.DIALOG.AUTHENTICATION_QR_CODE_GENERATOR);
}

@Override
public void showConnectionAcceptDialog(@NonNull String receiverDeviceName, @NonNull String authenticationCode
, @NonNull final DialogInterface.OnClickListener onClickListener) {
new android.support.v7.app.AlertDialog.Builder(this)
.setTitle(String.format(getString(R.string.accept_connection_dialog_title), receiverDeviceName))
.setMessage(String.format(getString(R.string.accept_connection_dialog_content), authenticationCode))
.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
onClickListener.onClick(dialog, which);
}
})
.setNegativeButton(R.string.no, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
onClickListener.onClick(dialog, which);
}
})
.setCancelable(false)
.show();
}

@Override
Expand Down Expand Up @@ -233,6 +342,25 @@ public void handleActivityResult(int resultCode, Intent data) {
});
}

@Override
public void showToast(@NonNull String text, int length) {
Toast.makeText(this, text, length)
.show();
}

@Override
public void displayMessage(@NonNull String text) {
String beforeText = messagesTv.getText() != null ? messagesTv.getText().toString() : "";

// Todo: this should be moved to another method or only called once
if (!messageToSendEt.isEnabled()) {
messageToSendEt.setEnabled(true);
sendMsgBtn.setEnabled(true);
}

messagesTv.setText(String.format("%s\n%s", beforeText, text));
}

private void showLocationEnableRejectionDialog() {
new AlertDialog.Builder(P2pModeSelectActivity.this)
.setTitle(R.string.location_service_disabled)
Expand Down Expand Up @@ -272,7 +400,7 @@ protected void onActivityResult(int requestCode, int resultCode, @Nullable Inten

for (OnActivityResultHandler onActivityResultHandler: onActivityResultHandlers) {
if (requestCode == onActivityResultHandler.getRequestCode()) {
onActivityResultHandler.handleActivityResult(resultCode, data);
onActivityResultHandler.handleActivityResult(resultCode, data); getString(R.string.connected);
}
}
}
Expand All @@ -284,8 +412,9 @@ public Context getContext() {
}

@Override
public void initializePresenter() {
presenter = new P2pModeSelectPresenter(this, new P2pModeSelectInteractor(this));
public void initializePresenters() {
receiverBasePresenter = new P2PReceiverPresenter(this);
senderBasePresenter = new P2PSenderPresenter(this);
}

@Override
Expand All @@ -303,10 +432,14 @@ public boolean removeOnActivityRequestPermissionHandler(@NonNull OnActivityReque
protected void onStop() {
super.onStop();

messageToSendEt.setEnabled(false);
sendMsgBtn.setEnabled(false);

sendButton.setOnClickListener(null);
receiveButton.setOnClickListener(null);

presenter.onStop();
receiverBasePresenter.onStop();
senderBasePresenter.onStop();
}


Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
package org.smartregister.p2p.authenticator;

import android.support.annotation.NonNull;

import org.smartregister.p2p.contract.P2pModeSelectContract;
import org.smartregister.p2p.sync.DiscoveredDevice;

/**
* Created by Ephraim Kigamba - ekigamba@ona.io on 14/03/2019
*/

public abstract class BaseSyncConnectionAuthenticator {

protected P2pModeSelectContract.BasePresenter presenter;

public BaseSyncConnectionAuthenticator(@NonNull P2pModeSelectContract.BasePresenter presenter) {
this.presenter = presenter;
}

public P2pModeSelectContract.BasePresenter getPresenter() {
return presenter;
}

public abstract void authenticate(@NonNull DiscoveredDevice discoveredDevice, @NonNull final AuthenticationCallback authenticationCallback);

public interface AuthenticationCallback {

void onAuthenticationSuccessful();

void onAuthenticationFailed(@NonNull Exception exception);

void onAuthenticationCancelled(@NonNull String reason);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
package org.smartregister.p2p.authenticator;

import android.content.DialogInterface;
import android.support.annotation.NonNull;

import org.smartregister.p2p.contract.P2pModeSelectContract;
import org.smartregister.p2p.dialog.QRCodeGeneratorDialog;
import org.smartregister.p2p.sync.DiscoveredDevice;

/**
* Created by Ephraim Kigamba - ekigamba@ona.io on 14/03/2019
*/

public class ReceiverConnectionAuthenticator extends BaseSyncConnectionAuthenticator {

public ReceiverConnectionAuthenticator(@NonNull P2pModeSelectContract.BasePresenter basePresenter) {
super(basePresenter);
}

@Override
public void authenticate(@NonNull final DiscoveredDevice discoveredDevice, @NonNull final AuthenticationCallback authenticationCallback) {
if (discoveredDevice.getConnectionInfo() != null
&& discoveredDevice.getConnectionInfo().isIncomingConnection()) {

getPresenter().getView().showQRCodeGeneratorDialog(discoveredDevice.getConnectionInfo().getAuthenticationToken()
, discoveredDevice.getEndpointName()
, new QRCodeGeneratorDialog.QRCodeAuthenticationCallback() {
@Override
public void onAccepted(@NonNull DialogInterface dialogInterface) {
authenticationCallback.onAuthenticationSuccessful();
}

@Override
public void onRejected(@NonNull DialogInterface dialogInterface) {
authenticationCallback.onAuthenticationFailed(new Exception("User rejected the connection"));
}
});

} else {
authenticationCallback.onAuthenticationFailed(new Exception("DiscoveredDevice information passed is invalid"));
}
}
}
Loading