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

Add the backend connection to register new users #66

Merged
merged 14 commits into from
Jan 25, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
6 changes: 2 additions & 4 deletions android/app/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -194,9 +194,6 @@ dependencies {
implementation 'com.fasterxml.jackson.module:jackson-module-kotlin:2.13.4'
implementation 'com.google.mlkit:barcode-scanning:17.2.0'
implementation 'com.google.mlkit:text-recognition:16.0.0'
// ReactiveX
implementation 'io.reactivex.rxjava3:rxjava:3.1.8'
implementation 'io.reactivex.rxjava3:rxkotlin:3.0.1'

implementation "androidx.camera:camera-core:${camerax_version}"
implementation "androidx.camera:camera-camera2:${camerax_version}"
Expand All @@ -212,7 +209,8 @@ dependencies {
implementation 'com.madgag.spongycastle:prov:1.58.0.0'

// HTTP Library
implementation 'com.google.android.gms:play-services-cronet:18.0.1'
implementation 'com.squareup.retrofit2:retrofit:2.9.0'
implementation 'com.squareup.retrofit2:converter-jackson:2.9.0'

debugImplementation 'junit:junit:4.13.2'
debugImplementation 'androidx.test.ext:junit-ktx:1.1.5'
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@ package com.epfl.dedis.hbt.test.ui.page.register
import androidx.annotation.IdRes
import com.epfl.dedis.hbt.R

object ScanPassportFragmentPage {
object PassportScanFragmentPage {

@IdRes
fun scanPassportFragmentId() = R.id.scanPassportFragment
fun scanPassportFragmentId() = R.id.passportScanFragment
}
3 changes: 3 additions & 0 deletions android/app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@
android:required="true" />
<uses-permission android:name="android.permission.CAMERA" />
<uses-feature android:name="android.hardware.camera.any" />
<uses-permission android:name="android.permission.INTERNET" />

<application
android:name=".HBTApplication"
android:allowBackup="true"
Expand All @@ -18,6 +20,7 @@
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/Theme.Hbt"
android:networkSecurityConfig="@xml/network_security_config"
tools:targetApi="31">
<activity
android:name=".ui.MainActivity"
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package com.epfl.dedis.hbt.data.document

import com.fasterxml.jackson.annotation.JsonProperty

data class Document(
@JsonProperty("doc_id") val id: String
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package com.epfl.dedis.hbt.data.document

import java.io.Serializable

data class Portrait(
val type: String,
val data: ByteArray
) : Serializable {
override fun equals(other: Any?): Boolean {
if (this === other) return true
if (javaClass != other?.javaClass) return false

other as Portrait

if (type != other.type) return false
return data.contentEquals(other.data)
}

override fun hashCode(): Int {
var result = type.hashCode()
result = 31 * result + data.contentHashCode()
return result
}

override fun toString(): String {
return "Portrait(type='$type', data=${data.contentToString()})"
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ package com.epfl.dedis.hbt.data.user

import android.content.SharedPreferences
import com.epfl.dedis.hbt.data.Result
import com.epfl.dedis.hbt.data.document.Portrait
import com.epfl.dedis.hbt.service.document.DocumentService
import com.epfl.dedis.hbt.service.json.JsonService
import com.epfl.dedis.hbt.service.json.JsonType.USER_DATA
import javax.inject.Inject
Expand All @@ -13,7 +15,8 @@ import javax.inject.Singleton
@Singleton
class UserDataSource @Inject constructor(
private val sharedPref: SharedPreferences,
private val jsonService: JsonService
private val jsonService: JsonService,
private val documentService: DocumentService
) {

private val usernamesKey: String = "users"
Expand Down Expand Up @@ -47,11 +50,22 @@ class UserDataSource @Inject constructor(
return username == usernamesKey || users.containsKey(username)
}

fun register(username: String, pincode: Int, passport: String, role: Role): Result<User> {
if (isRegistered(username)) return Result.Error(Exception("Already registered"))
suspend fun register(
username: String,
pincode: Int,
passport: String,
role: Role,
portrait: Portrait
): Result<User> {
//if (isRegistered(username)) return Result.Error(Exception("Already registered"))

// create user
val user = User(username, pincode, passport, role)
val result = documentService.create(user, portrait, false)
if (result is Result.Error) {
return result
}

users[username] = user

//create wallet
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package com.epfl.dedis.hbt.data.user

import com.epfl.dedis.hbt.data.Result
import com.epfl.dedis.hbt.data.document.Portrait
import javax.inject.Inject
import javax.inject.Singleton

Expand Down Expand Up @@ -36,9 +37,15 @@ class UserRepository @Inject constructor(private val dataSource: UserDataSource)
loggedInUser = null
}

fun register(username: String, pincode: String, passport: String, role: Role): Result<User> {
suspend fun register(
username: String,
pincode: String,
passport: String,
role: Role,
portrait: Portrait
): Result<User> {
val pin = pincode.toIntOrNull() ?: return Result.Error(NumberFormatException())
val result = dataSource.register(username, pin, passport, role)
val result = dataSource.register(username, pin, passport, role, portrait)

if (result is Result.Success) {
setLoggedInUser(result.data)
Expand Down
41 changes: 41 additions & 0 deletions android/app/src/main/java/com/epfl/dedis/hbt/di/HttpModule.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
package com.epfl.dedis.hbt.di

import com.epfl.dedis.hbt.service.document.DocumentEndpoint
import com.epfl.dedis.hbt.service.http.ResultCallAdapterFactory
import com.fasterxml.jackson.databind.ObjectMapper
import dagger.Module
import dagger.Provides
import dagger.hilt.InstallIn
import dagger.hilt.components.SingletonComponent
import retrofit2.Retrofit
import retrofit2.converter.jackson.JacksonConverterFactory
import javax.inject.Qualifier
import javax.inject.Singleton

@Module
@InstallIn(SingletonComponent::class)
object HttpModule {

@BaseURL
@Provides
@Singleton
fun provideBaseURL() = "http://10.0.2.2:3000"

@Provides
@Singleton
fun provideRetrofit(@BaseURL baseUrl: String, mapper: ObjectMapper): Retrofit =
Retrofit.Builder()
.baseUrl(baseUrl)
.addConverterFactory(JacksonConverterFactory.create(mapper))
.addCallAdapterFactory(ResultCallAdapterFactory())
.build()

@Provides
@Singleton
fun provideDocumentService(retrofit: Retrofit): DocumentEndpoint =
retrofit.create(DocumentEndpoint::class.java)

@Qualifier
@Retention(AnnotationRetention.BINARY)
annotation class BaseURL
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,5 +14,5 @@ object JsonModule {

@Provides
@Singleton
fun provideObjectMapper() = ObjectMapper().registerKotlinModule()
fun provideObjectMapper(): ObjectMapper = ObjectMapper().registerKotlinModule()
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package com.epfl.dedis.hbt.service.document

import com.epfl.dedis.hbt.data.Result
import com.epfl.dedis.hbt.data.document.Document
import okhttp3.MultipartBody
import retrofit2.http.Multipart
import retrofit2.http.POST
import retrofit2.http.Part

interface DocumentEndpoint {

@Multipart
@POST("document")
suspend fun create(
@Part("name") name: String,
@Part("passport") passport: String,
@Part("role") role: Int,
@Part portrait: MultipartBody.Part,
@Part("registered") registered: Boolean,
): Result<Document>
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package com.epfl.dedis.hbt.service.document

import com.epfl.dedis.hbt.data.Result
import com.epfl.dedis.hbt.data.document.Document
import com.epfl.dedis.hbt.data.document.Portrait
import com.epfl.dedis.hbt.data.user.User
import okhttp3.MediaType
import okhttp3.MultipartBody
import okhttp3.RequestBody
import javax.inject.Inject
import javax.inject.Singleton

@Singleton
class DocumentService @Inject constructor(private val endpoint: DocumentEndpoint) {

suspend fun create(user: User, portrait: Portrait, registered: Boolean): Result<Document> =
endpoint.create(
user.name,
user.passport,
user.role.ordinal,
MultipartBody.Part.createFormData("portrait",
"portrait.png",
RequestBody.create(MediaType.parse(portrait.type), portrait.data)),
registered
)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
package com.epfl.dedis.hbt.service.http

import com.epfl.dedis.hbt.data.Result
import okhttp3.Request
import okio.Timeout
import retrofit2.Call
import retrofit2.Callback
import retrofit2.HttpException
import retrofit2.Response
import java.io.IOException

class ResultCall<T : Any>(private val delegate: Call<T>) : Call<Result<T>> {
override fun enqueue(callback: Callback<Result<T>>) {
delegate.enqueue(
object : Callback<T> {
override fun onResponse(call: Call<T>, response: Response<T>) {
if (response.isSuccessful) {
callback.onResponse(
this@ResultCall,
Response.success(
response.code(),
Result.Success(response.body()!!)
)
)
} else {
callback.onResponse(
this@ResultCall,
Response.success(
Result.Error(
HttpException(response)
)
)
)
}
}

override fun onFailure(call: Call<T>, t: Throwable) {
val errorMessage = when (t) {
is IOException -> "No internet connection"
is HttpException -> "Something went wrong!"
else -> t.localizedMessage
}
callback.onResponse(
this@ResultCall,
Response.success(Result.Error(RuntimeException(errorMessage, t)))
)
}
}
)
}

override fun isExecuted(): Boolean {
return delegate.isExecuted
}

override fun execute(): Response<Result<T>> {
return Response.success(Result.Success(delegate.execute().body()!!))
}

override fun cancel() {
delegate.cancel()
}

override fun isCanceled(): Boolean {
return delegate.isCanceled
}

override fun clone(): Call<Result<T>> {
return ResultCall(delegate.clone())
}

override fun request(): Request {
return delegate.request()
}

override fun timeout(): Timeout {
return delegate.timeout()
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package com.epfl.dedis.hbt.service.http

import com.epfl.dedis.hbt.data.Result
import retrofit2.Call
import retrofit2.CallAdapter
import retrofit2.Retrofit
import java.lang.reflect.ParameterizedType
import java.lang.reflect.Type

class ResultCallAdapterFactory : CallAdapter.Factory() {
override fun get(
returnType: Type,
annotations: Array<out Annotation>,
retrofit: Retrofit
): CallAdapter<*, *>? {
if (getRawType(returnType) != Call::class.java || returnType !is ParameterizedType) {
return null
}
val upperBound = getParameterUpperBound(0, returnType)

return if (upperBound is ParameterizedType && upperBound.rawType == Result::class.java) {
object : CallAdapter<Any, Call<Result<*>>> {
override fun responseType(): Type = getParameterUpperBound(0, upperBound)

override fun adapt(call: Call<Any>): Call<Result<*>> = ResultCall(call)
}
} else {
null
}
}
}
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
package com.epfl.dedis.hbt.service.passport

import com.epfl.dedis.hbt.data.document.Portrait
import com.epfl.dedis.hbt.service.passport.mrz.MRZInfo
import org.jmrtd.lds.SODFile
import org.jmrtd.lds.icao.DG11File

data class Passport(
val mrzInfo: MRZInfo,
val sodFile: SODFile,
val portrait: Portrait,
val dg11File: DG11File?
)
Loading
Loading