commit 18333f0d749753456c63c2321b016a559be24e68 Author: mohamedkaif356 Date: Mon Jun 19 16:30:43 2023 +0530 Init diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..aa724b7 --- /dev/null +++ b/.gitignore @@ -0,0 +1,15 @@ +*.iml +.gradle +/local.properties +/.idea/caches +/.idea/libraries +/.idea/modules.xml +/.idea/workspace.xml +/.idea/navEditor.xml +/.idea/assetWizardSettings.xml +.DS_Store +/build +/captures +.externalNativeBuild +.cxx +local.properties diff --git a/.idea/.gitignore b/.idea/.gitignore new file mode 100644 index 0000000..26d3352 --- /dev/null +++ b/.idea/.gitignore @@ -0,0 +1,3 @@ +# Default ignored files +/shelf/ +/workspace.xml diff --git a/.idea/.name b/.idea/.name new file mode 100644 index 0000000..bd8ef55 --- /dev/null +++ b/.idea/.name @@ -0,0 +1 @@ +HPOS Registration \ No newline at end of file diff --git a/.idea/compiler.xml b/.idea/compiler.xml new file mode 100644 index 0000000..b589d56 --- /dev/null +++ b/.idea/compiler.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/.idea/deploymentTargetDropDown.xml b/.idea/deploymentTargetDropDown.xml new file mode 100644 index 0000000..db248ac --- /dev/null +++ b/.idea/deploymentTargetDropDown.xml @@ -0,0 +1,17 @@ + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/gradle.xml b/.idea/gradle.xml new file mode 100644 index 0000000..662fec0 --- /dev/null +++ b/.idea/gradle.xml @@ -0,0 +1,21 @@ + + + + + + + \ No newline at end of file diff --git a/.idea/kotlinc.xml b/.idea/kotlinc.xml new file mode 100644 index 0000000..217e5c5 --- /dev/null +++ b/.idea/kotlinc.xml @@ -0,0 +1,6 @@ + + + + + \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml new file mode 100644 index 0000000..9c90002 --- /dev/null +++ b/.idea/misc.xml @@ -0,0 +1,16 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/app/.gitignore b/app/.gitignore new file mode 100644 index 0000000..42afabf --- /dev/null +++ b/app/.gitignore @@ -0,0 +1 @@ +/build \ No newline at end of file diff --git a/app/build.gradle b/app/build.gradle new file mode 100644 index 0000000..9d8b040 --- /dev/null +++ b/app/build.gradle @@ -0,0 +1,100 @@ +plugins { + id 'com.android.application' + id 'org.jetbrains.kotlin.android' + id 'dagger.hilt.android.plugin' + id 'com.google.gms.google-services' + id 'androidx.navigation.safeargs.kotlin' + id 'kotlin-kapt' +} + +android { + namespace 'com.example.hposregistration' + compileSdk 33 + + defaultConfig { + applicationId "com.example.hposregistration" + minSdk 26 + targetSdk 33 + versionCode 1 + versionName "1.0" + + testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" + } + + buildTypes { + release { + minifyEnabled false + proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' + } + } + compileOptions { + sourceCompatibility JavaVersion.VERSION_17 + targetCompatibility JavaVersion.VERSION_17 + } + buildFeatures { + viewBinding true + } + kotlinOptions { + jvmTarget = '17' + } +} + +dependencies { + + implementation 'androidx.core:core-ktx:1.10.1' + implementation 'androidx.appcompat:appcompat:1.6.1' + implementation 'com.google.android.material:material:1.9.0' + implementation 'androidx.constraintlayout:constraintlayout:2.1.4' + testImplementation 'junit:junit:4.13.2' + androidTestImplementation 'androidx.test.ext:junit:1.1.5' + androidTestImplementation 'androidx.test.espresso:espresso-core:3.5.1' + + //firebase + implementation platform('com.google.firebase:firebase-bom:32.1.0') + implementation 'com.google.firebase:firebase-firestore-ktx' + implementation 'com.google.firebase:firebase-auth-ktx' + implementation 'com.google.firebase:firebase-storage-ktx' + implementation 'com.firebaseui:firebase-ui-firestore:8.0.2' + implementation 'com.google.android.gms:play-services-auth:20.5.0' + + // Coroutines + implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.6.4' + implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.6.4' + + // Coroutine Lifecycle Scopes + implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:2.6.1" + implementation "androidx.lifecycle:lifecycle-runtime-ktx:2.6.1" + + //Dagger - Hilt + implementation "com.google.dagger:hilt-android:2.46" + kapt "com.google.dagger:hilt-android-compiler:2.46" + kapt "androidx.hilt:hilt-compiler:1.0.0" + + // Navigation Component + implementation "androidx.navigation:navigation-fragment-ktx:2.7.0-beta01" + implementation "androidx.navigation:navigation-ui-ktx:2.7.0-beta01" + + // Retrofit + GSON + implementation "com.squareup.retrofit2:retrofit:2.9.0" + implementation "com.squareup.retrofit2:converter-gson:2.9.0" + + // Timber + implementation 'com.jakewharton.timber:timber:4.7.1' + + // Activity KTX for viewModels() + implementation "androidx.activity:activity-ktx:1.7.2" + + //image + implementation 'com.github.bumptech.glide:glide:4.13.2' + annotationProcessor 'com.github.bumptech.glide:compiler:4.12.0' + + //Room + implementation "androidx.room:room-ktx:2.6.0-alpha01" + implementation "androidx.room:room-runtime:2.6.0-alpha01" + kapt ("androidx.room:room-compiler:2.6.0-alpha01") + + // Barcode scanner + implementation 'com.journeyapps:zxing-android-embedded:4.3.0' + + +} \ No newline at end of file diff --git a/app/google-services.json b/app/google-services.json new file mode 100644 index 0000000..b6ee2b1 --- /dev/null +++ b/app/google-services.json @@ -0,0 +1,113 @@ +{ + "project_info": { + "project_number": "650071678820", + "project_id": "hpos-af3cc", + "storage_bucket": "hpos-af3cc.appspot.com" + }, + "client": [ + { + "client_info": { + "mobilesdk_app_id": "1:650071678820:android:9a445bbab80173b96c6471", + "android_client_info": { + "package_name": "com.example.hpos" + } + }, + "oauth_client": [ + { + "client_id": "650071678820-rvst80rkqd2raoc9pvnp0k9huf3gnp4p.apps.googleusercontent.com", + "client_type": 1, + "android_info": { + "package_name": "com.example.hpos", + "certificate_hash": "7714b9268a81d0cf0fb178b0af8dbb630f8fc70a" + } + }, + { + "client_id": "650071678820-to41afi9f0gi5r3k6v7pe1e5p96nupcs.apps.googleusercontent.com", + "client_type": 3 + } + ], + "api_key": [ + { + "current_key": "AIzaSyAVdNGCev_AFX0qmuZJF6kxzRzXUTeAW5I" + } + ], + "services": { + "appinvite_service": { + "other_platform_oauth_client": [ + { + "client_id": "650071678820-to41afi9f0gi5r3k6v7pe1e5p96nupcs.apps.googleusercontent.com", + "client_type": 3 + } + ] + } + } + }, + { + "client_info": { + "mobilesdk_app_id": "1:650071678820:android:74c39c1cfacdc3ae6c6471", + "android_client_info": { + "package_name": "com.example.hposregistration" + } + }, + "oauth_client": [ + { + "client_id": "650071678820-f0c5dbjpss3av4vrvojpr6qo2h1a0fh1.apps.googleusercontent.com", + "client_type": 1, + "android_info": { + "package_name": "com.example.hposregistration", + "certificate_hash": "7714b9268a81d0cf0fb178b0af8dbb630f8fc70a" + } + }, + { + "client_id": "650071678820-to41afi9f0gi5r3k6v7pe1e5p96nupcs.apps.googleusercontent.com", + "client_type": 3 + } + ], + "api_key": [ + { + "current_key": "AIzaSyAVdNGCev_AFX0qmuZJF6kxzRzXUTeAW5I" + } + ], + "services": { + "appinvite_service": { + "other_platform_oauth_client": [ + { + "client_id": "650071678820-to41afi9f0gi5r3k6v7pe1e5p96nupcs.apps.googleusercontent.com", + "client_type": 3 + } + ] + } + } + }, + { + "client_info": { + "mobilesdk_app_id": "1:650071678820:android:ca5abf286e5e8a726c6471", + "android_client_info": { + "package_name": "com.example.refactoredapp" + } + }, + "oauth_client": [ + { + "client_id": "650071678820-to41afi9f0gi5r3k6v7pe1e5p96nupcs.apps.googleusercontent.com", + "client_type": 3 + } + ], + "api_key": [ + { + "current_key": "AIzaSyAVdNGCev_AFX0qmuZJF6kxzRzXUTeAW5I" + } + ], + "services": { + "appinvite_service": { + "other_platform_oauth_client": [ + { + "client_id": "650071678820-to41afi9f0gi5r3k6v7pe1e5p96nupcs.apps.googleusercontent.com", + "client_type": 3 + } + ] + } + } + } + ], + "configuration_version": "1" +} \ No newline at end of file diff --git a/app/proguard-rules.pro b/app/proguard-rules.pro new file mode 100644 index 0000000..481bb43 --- /dev/null +++ b/app/proguard-rules.pro @@ -0,0 +1,21 @@ +# Add project specific ProGuard rules here. +# You can control the set of applied configuration files using the +# proguardFiles setting in build.gradle. +# +# For more details, see +# http://developer.android.com/guide/developing/tools/proguard.html + +# If your project uses WebView with JS, uncomment the following +# and specify the fully qualified class name to the JavaScript interface +# class: +#-keepclassmembers class fqcn.of.javascript.interface.for.webview { +# public *; +#} + +# Uncomment this to preserve the line number information for +# debugging stack traces. +#-keepattributes SourceFile,LineNumberTable + +# If you keep the line number information, uncomment this to +# hide the original source file name. +#-renamesourcefileattribute SourceFile \ No newline at end of file diff --git a/app/src/androidTest/java/com/example/hposregistration/ExampleInstrumentedTest.kt b/app/src/androidTest/java/com/example/hposregistration/ExampleInstrumentedTest.kt new file mode 100644 index 0000000..719b5ce --- /dev/null +++ b/app/src/androidTest/java/com/example/hposregistration/ExampleInstrumentedTest.kt @@ -0,0 +1,24 @@ +package com.example.hposregistration + +import androidx.test.platform.app.InstrumentationRegistry +import androidx.test.ext.junit.runners.AndroidJUnit4 + +import org.junit.Test +import org.junit.runner.RunWith + +import org.junit.Assert.* + +/** + * Instrumented test, which will execute on an Android device. + * + * See [testing documentation](http://d.android.com/tools/testing). + */ +@RunWith(AndroidJUnit4::class) +class ExampleInstrumentedTest { + @Test + fun useAppContext() { + // Context of the app under test. + val appContext = InstrumentationRegistry.getInstrumentation().targetContext + assertEquals("com.example.hposregistration", appContext.packageName) + } +} \ No newline at end of file diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml new file mode 100644 index 0000000..2211d89 --- /dev/null +++ b/app/src/main/AndroidManifest.xml @@ -0,0 +1,47 @@ + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/java/com/example/hposregistration/HPOSRegistrationApplication.kt b/app/src/main/java/com/example/hposregistration/HPOSRegistrationApplication.kt new file mode 100644 index 0000000..e652dc2 --- /dev/null +++ b/app/src/main/java/com/example/hposregistration/HPOSRegistrationApplication.kt @@ -0,0 +1,7 @@ +package com.example.hposregistration + +import android.app.Application +import dagger.hilt.android.HiltAndroidApp + +@HiltAndroidApp +class HPOSRegistrationApplication: Application() \ No newline at end of file diff --git a/app/src/main/java/com/example/hposregistration/adapters/UserListOfflineAdapter.kt b/app/src/main/java/com/example/hposregistration/adapters/UserListOfflineAdapter.kt new file mode 100644 index 0000000..830aa0a --- /dev/null +++ b/app/src/main/java/com/example/hposregistration/adapters/UserListOfflineAdapter.kt @@ -0,0 +1,49 @@ +package com.example.hposregistration.adapters + +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import androidx.recyclerview.widget.AsyncListDiffer +import androidx.recyclerview.widget.DiffUtil +import androidx.recyclerview.widget.RecyclerView +import com.bumptech.glide.Glide +import com.example.hposregistration.data.user.UserData +import com.example.hposregistration.databinding.UserItemViewBinding + +class UserListOfflineAdapter(private val view: View) : RecyclerView.Adapter() { + + inner class CompanyViewHolder(val binding: UserItemViewBinding) : + RecyclerView.ViewHolder(binding.root) + + private val differCallback = object : DiffUtil.ItemCallback() { + override fun areItemsTheSame(oldItem: UserData, newItem: UserData): Boolean { + return oldItem.name == newItem.name + } + + override fun areContentsTheSame(oldItem: UserData, newItem: UserData): Boolean { + return oldItem == newItem + } + } + + val differ = AsyncListDiffer(this, differCallback) + + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): CompanyViewHolder { + return CompanyViewHolder( + UserItemViewBinding + .inflate(LayoutInflater.from(parent.context), parent, false) + ) + } + + override fun getItemCount(): Int { + return differ.currentList.size + } + + override fun onBindViewHolder(holder: CompanyViewHolder, position: Int) { + val userList = differ.currentList[position] + holder.binding.apply { + userName.text = userList.name + userId.text = userList._id + Glide.with(view).load(userList.userImage).into(userImage) + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/example/hposregistration/adapters/UserListOnlineAdapter.kt b/app/src/main/java/com/example/hposregistration/adapters/UserListOnlineAdapter.kt new file mode 100644 index 0000000..25b2faf --- /dev/null +++ b/app/src/main/java/com/example/hposregistration/adapters/UserListOnlineAdapter.kt @@ -0,0 +1,35 @@ +package com.example.hposregistration.adapters + +import android.annotation.SuppressLint +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import androidx.recyclerview.widget.RecyclerView +import com.bumptech.glide.Glide +import com.example.hposregistration.data.user.UserData +import com.example.hposregistration.databinding.UserItemViewBinding +import com.firebase.ui.firestore.FirestoreRecyclerAdapter +import com.firebase.ui.firestore.FirestoreRecyclerOptions + +class UserListOnlineAdapter(options: FirestoreRecyclerOptions, private val view: View) : + FirestoreRecyclerAdapter(options) { + + class OrderItemViewHolder(val binding: UserItemViewBinding) : + RecyclerView.ViewHolder(binding.root) + + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): OrderItemViewHolder { + return OrderItemViewHolder( + UserItemViewBinding + .inflate(LayoutInflater.from(parent.context), parent, false) + ) + } + + @SuppressLint("SetTextI18n") + override fun onBindViewHolder(holder: OrderItemViewHolder, position: Int, model: UserData) { + holder.binding.apply { + userName.text = model.name + userId.text = model._id + Glide.with(view).load(model.userImage).into(userImage) + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/example/hposregistration/dao/Converters.kt b/app/src/main/java/com/example/hposregistration/dao/Converters.kt new file mode 100644 index 0000000..2f3b5d7 --- /dev/null +++ b/app/src/main/java/com/example/hposregistration/dao/Converters.kt @@ -0,0 +1,27 @@ +package com.example.hposregistration.dao + +import androidx.room.TypeConverter +import com.example.hposregistration.data.user.UserData +import com.google.gson.Gson + +class Converters { + private val gson = Gson() + + @TypeConverter + fun fromString(value: String?): UserData.Location? { + return if (value != null) { + gson.fromJson(value, UserData.Location::class.java) + } else { + null + } + } + + @TypeConverter + fun toString(location: UserData.Location?): String? { + return if (location != null) { + gson.toJson(location) + } else { + null + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/example/hposregistration/dao/MyDataBase.kt b/app/src/main/java/com/example/hposregistration/dao/MyDataBase.kt new file mode 100644 index 0000000..10264d1 --- /dev/null +++ b/app/src/main/java/com/example/hposregistration/dao/MyDataBase.kt @@ -0,0 +1,12 @@ +package com.example.hposregistration.dao + +import androidx.room.Database +import androidx.room.RoomDatabase +import androidx.room.TypeConverters +import com.example.hposregistration.data.user.UserData + +@Database(entities = [UserData::class], version = 1, exportSchema = false) +@TypeConverters(Converters::class) +abstract class MyDatabase : RoomDatabase() { + abstract fun userDao(): UserDao +} \ No newline at end of file diff --git a/app/src/main/java/com/example/hposregistration/dao/UserDao.kt b/app/src/main/java/com/example/hposregistration/dao/UserDao.kt new file mode 100644 index 0000000..bdff697 --- /dev/null +++ b/app/src/main/java/com/example/hposregistration/dao/UserDao.kt @@ -0,0 +1,23 @@ +package com.example.hposregistration.dao + +import androidx.lifecycle.LiveData +import androidx.room.Dao +import androidx.room.Insert +import androidx.room.OnConflictStrategy +import androidx.room.Query +import com.example.hposregistration.data.user.UserData + +@Dao +interface UserDao { + @Query("SELECT * from user_table") + fun getAll(): LiveData> + + @Insert(onConflict = OnConflictStrategy.REPLACE) + suspend fun insertAll(userData: UserData) + + @Query("SELECT * FROM user_table WHERE _id = :id") + suspend fun getUserByID(id: String): UserData + + @Query("DELETE FROM user_table WHERE _id = :id") + suspend fun deleteById(id: String) +} \ No newline at end of file diff --git a/app/src/main/java/com/example/hposregistration/data/Constant.kt b/app/src/main/java/com/example/hposregistration/data/Constant.kt new file mode 100644 index 0000000..7270bf6 --- /dev/null +++ b/app/src/main/java/com/example/hposregistration/data/Constant.kt @@ -0,0 +1,21 @@ +package com.example.hposregistration.data + +import com.example.hposregistration.data.user.UserData + +object Constant { + + fun String.fetchYOBFromDOB(): Int { + val startIndex = if (this.length >= 4) this.length - 4 else 0 + return this.substring(startIndex).toInt() + } + + fun String.mapToGender(): UserData.Gender { + return if (this.lowercase() == "m"|| this.lowercase() == "male") { + UserData.Gender.MALE + } else if (this.lowercase() == "f"|| this.lowercase() == "female") { + UserData.Gender.FEMALE + } else { + UserData.Gender.OTHER + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/example/hposregistration/data/InternetConnectionLiveData.kt b/app/src/main/java/com/example/hposregistration/data/InternetConnectionLiveData.kt new file mode 100644 index 0000000..4f1eb2e --- /dev/null +++ b/app/src/main/java/com/example/hposregistration/data/InternetConnectionLiveData.kt @@ -0,0 +1,58 @@ +package com.example.hposregistration.data + + +import android.content.Context +import android.net.ConnectivityManager +import android.net.Network +import android.net.NetworkCapabilities +import android.net.NetworkRequest +import androidx.lifecycle.LiveData + +class NetworkStatusLiveData(context: Context) : LiveData() { + + private val connectivityManager: ConnectivityManager = + context.applicationContext.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager + + private val networkCallback = object : ConnectivityManager.NetworkCallback() { + override fun onAvailable(network: Network) { + postValue(true) + } + + override fun onLost(network: Network) { + postValue(false) + } + } + + override fun onActive() { + super.onActive() + postValue(isNetworkAvailable()) + registerNetworkCallback() + } + + override fun onInactive() { + super.onInactive() + unregisterNetworkCallback() + } + + private fun registerNetworkCallback() { + val networkRequest = NetworkRequest.Builder() + .addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR) + .addTransportType(NetworkCapabilities.TRANSPORT_WIFI) + .addTransportType(NetworkCapabilities.TRANSPORT_ETHERNET) + .build() + + connectivityManager.registerNetworkCallback(networkRequest, networkCallback) + } + + private fun unregisterNetworkCallback() { + connectivityManager.unregisterNetworkCallback(networkCallback) + } + + private fun isNetworkAvailable(): Boolean { + val network = connectivityManager.activeNetwork + val networkCapabilities = connectivityManager.getNetworkCapabilities(network) + return networkCapabilities != null && + networkCapabilities.hasCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET) && + networkCapabilities.hasCapability(NetworkCapabilities.NET_CAPABILITY_VALIDATED) + } +} \ No newline at end of file diff --git a/app/src/main/java/com/example/hposregistration/data/MainRepository.kt b/app/src/main/java/com/example/hposregistration/data/MainRepository.kt new file mode 100644 index 0000000..715827d --- /dev/null +++ b/app/src/main/java/com/example/hposregistration/data/MainRepository.kt @@ -0,0 +1,33 @@ +package com.example.hposregistration.data + +import androidx.lifecycle.LiveData +import com.example.hposregistration.dao.UserDao +import com.example.hposregistration.data.user.UserData +import javax.inject.Inject +import javax.inject.Singleton + +@Singleton +class MainRepository @Inject constructor( + private val userDao: UserDao, + private val userFireBaseDao: com.example.hposregistration.network.UserDao +) { + + val allUsers: LiveData> = userDao.getAll() + + suspend fun insert(userData: UserData){ + userDao.insertAll(userData) + } + + suspend fun getUserByID(id: String): UserData { + return userDao.getUserByID(id) + } + + suspend fun deleteById(id: String) { + return userDao.deleteById(id) + } + + suspend fun addDataToFirebase(userData: UserData): Response { + return userFireBaseDao.addUserToDatabase(userData) + } + +} \ No newline at end of file diff --git a/app/src/main/java/com/example/hposregistration/data/Response.kt b/app/src/main/java/com/example/hposregistration/data/Response.kt new file mode 100644 index 0000000..ae4d1e1 --- /dev/null +++ b/app/src/main/java/com/example/hposregistration/data/Response.kt @@ -0,0 +1,6 @@ +package com.example.hposregistration.data + +sealed class Response { + data class Success(val data: T) : Response() + data class Error(val exception: Exception) : Response() +} \ No newline at end of file diff --git a/app/src/main/java/com/example/hposregistration/data/SecureQrCode.kt b/app/src/main/java/com/example/hposregistration/data/SecureQrCode.kt new file mode 100644 index 0000000..be15ad6 --- /dev/null +++ b/app/src/main/java/com/example/hposregistration/data/SecureQrCode.kt @@ -0,0 +1,253 @@ +package com.example.hposregistration.data + +import com.example.hposregistration.data.aadhar.AadharCard +import com.example.hposregistration.data.aadhar.QrCodeException +import java.io.ByteArrayInputStream +import java.io.ByteArrayOutputStream +import java.io.IOException +import java.math.BigInteger +import java.nio.charset.StandardCharsets +import java.util.LinkedList +import java.util.zip.GZIPInputStream + +/** + * Class to decode scanned QRcode + */ +open class SecureQrCode(scanData: String?) { + private var emailMobilePresent = 0 + private var imageStartIndex = 0 + private var imageEndIndex = 0 + private var decodedData: ArrayList? = null + private var signature: String? = null + private var email: String? = null + private var mobile: String? = null + var scannedAadharCard: AadharCard = AadharCard() + + init { + + // 1. Convert Base10 to BigInt + val bigIntScanData = scanData?.let { BigInteger(it, 10) } + + // 2. Convert BigInt to Byte Array + val byteScanData = bigIntScanData?.toByteArray() + + // 3. Decompress Byte Array + val decompByteScanData = byteScanData?.let { decompressData(it) } + + // 4. Split the byte array using delimiter + val parts = decompByteScanData?.let { separateData(it) } + // Throw error if there are no parts + if (parts != null) { + if (parts.isEmpty()) { + throw QrCodeException("Invalid QR Code Data, no parts found after splitting by delimiter") + } + } + + // 5. decode extracted data to string + if (parts != null) { + decodeData(parts) + } + + // 6. Extract Signature + if (decompByteScanData != null) { + decodeSignature(decompByteScanData) + } + + // 7. Email and Mobile number + if (decompByteScanData != null) { + decodeMobileEmail(decompByteScanData) + } + } + + /** + * Decompress the byte array, compression used is GZIP + * @param byteScanData compressed byte array + * @return uncompressed byte array + */ + @Throws(QrCodeException::class) + protected fun decompressData(byteScanData: ByteArray): ByteArray { + val bos = ByteArrayOutputStream(byteScanData.size) + val bin = ByteArrayInputStream(byteScanData) + val gis: GZIPInputStream = try { + GZIPInputStream(bin) + } catch (e: IOException) { + throw QrCodeException("Error in opening Gzip byte stream while decompressing QRcode", e) + } + var size = 0 + val buf = ByteArray(1024) + while (size >= 0) { + try { + size = gis.read(buf, 0, buf.size) + if (size > 0) { + bos.write(buf, 0, size) + } + } catch (e: IOException) { + throw QrCodeException("Error in writing byte stream while decompressing QRcode", e) + } + } + try { + gis.close() + bin.close() + } catch (e: IOException) { + throw QrCodeException("Error in closing byte stream while decompressing QRcode", e) + } + return bos.toByteArray() + } + + /** + * Function to split byte array with delimiter + * @param source source byte array + * @return list of separated byte arrays + */ + private fun separateData(source: ByteArray): List { + val separatedParts: MutableList = LinkedList() + var begin = 0 + for (i in source.indices) { + if (source[i] == SEPARATOR_BYTE) { + // skip if first or last byte is separator + if (i != 0 && i != source.size - 1) { + separatedParts.add(source.copyOfRange(begin, i)) + } + begin = i + 1 + // check if we have got all the parts of text data + if (separatedParts.size == VTC_INDEX + 1) { + // this is required to extract image data + imageStartIndex = begin + break + } + } + } + return separatedParts + } + + private fun decodeData(encodedData: List) { + val i = encodedData.iterator() + decodedData = ArrayList() + while (i.hasNext()) { + decodedData!!.add(String(i.next(), StandardCharsets.ISO_8859_1)) + } + // set the value of email/mobile present flag + emailMobilePresent = decodedData!![0].toInt() + + // populate decoded data + scannedAadharCard.name = decodedData!![2] + scannedAadharCard.dateOfBirth = decodedData!![3] + scannedAadharCard.gender = decodedData!![4] + scannedAadharCard.careOf = decodedData!![5] + scannedAadharCard.district = decodedData!![6] + scannedAadharCard.landmark = decodedData!![7] + scannedAadharCard.house = decodedData!![8] + scannedAadharCard.location = decodedData!![9] + scannedAadharCard.pinCode = decodedData!![10] + scannedAadharCard.postOffice = decodedData!![11] + scannedAadharCard.state = decodedData!![12] + scannedAadharCard.street = decodedData!![13] + scannedAadharCard.subDistrict = decodedData!![14] + scannedAadharCard.vtc = decodedData!![15] + } + + /** + * ref : https://uidai.gov.in/2-uncategorised/11320-aadhaar-paperless-offline-e-kyc-3.html + * Hashing logic for Email ID : + * Sha256(Sha256(Email+SharePhrase))*number of times last digit of Aadhaar number + * (Ref ID field contains last 4 digits). + * Example : + * Email: abc@gm.com + * Aadhaar Number:XXXX XXXX 3632 + * Passcode : Lock@487 + * Hash : Sha256(Sha256(abc@gm.comLock@487))*2 + * In case of Aadhaar number ends with Zero we will hashed one time. + * ********************************************************************** + * ********************************************************************** + * Hashing logic for Mobile Number : + * Sha256(Sha256(Mobile+SharePhrase))*number of times last digit of Aadhaar number + * (Ref ID field contains last 4 digits). + * Example : + * Mobile: 1234567890 + * Aadhaar Number:XXXX XXXX 3632 + * Passcode : Lock@487 + * Hash: Sha256(Sha256(1234567890Lock@487))*2 + * In case of Aadhaar number ends with Zero we will hashed one time. + */ + private fun decodeMobileEmail(decompressedData: ByteArray) { + val mobileStartIndex: Int + val mobileEndIndex: Int + val emailStartIndex: Int + val emailEndIndex: Int + when (emailMobilePresent) { + 3 -> { + // both email mobile present + mobileStartIndex = decompressedData.size - 289 // length -1 -256 -32 + mobileEndIndex = decompressedData.size - 257 // length -1 -256 + emailStartIndex = decompressedData.size - 322 // length -1 -256 -32 -1 -32 + emailEndIndex = decompressedData.size - 290 // length -1 -256 -32 -1 + mobile = bytesToHex( + decompressedData.copyOfRange(mobileStartIndex, mobileEndIndex + 1) + ) + email = bytesToHex( + decompressedData.copyOfRange(emailStartIndex, emailEndIndex + 1) + ) + // set image end index, it will be used to extract image data + imageEndIndex = decompressedData.size - 323 + } + + 2 -> { + // only mobile + email = "" + mobileStartIndex = decompressedData.size - 289 // length -1 -256 -32 + mobileEndIndex = decompressedData.size - 257 // length -1 -256 + mobile = bytesToHex( + decompressedData.copyOfRange(mobileStartIndex, mobileEndIndex + 1) + ) + // set image end index, it will be used to extract image data + imageEndIndex = decompressedData.size - 290 + } + + 1 -> { + // only email + mobile = "" + emailStartIndex = decompressedData.size - 289 // length -1 -256 -32 + emailEndIndex = decompressedData.size - 257 // length -1 -256 + email = bytesToHex( + decompressedData.copyOfRange(emailStartIndex, emailEndIndex + 1) + ) + // set image end index, it will be used to extract image data + imageEndIndex = decompressedData.size - 290 + } + + else -> { + // no mobile or email + mobile = "" + email = "" + // set image end index, it will be used to extract image data + imageEndIndex = decompressedData.size - 257 + } + } + } + + private fun decodeSignature(decompressedData: ByteArray) { + // extract 256 bytes from the end of the byte array + val startIndex = decompressedData.size - 257 + val noOfBytes = 256 + signature = String(decompressedData, startIndex, noOfBytes, StandardCharsets.ISO_8859_1) + } + + companion object { + protected const val SEPARATOR_BYTE = 255.toByte() + protected const val VTC_INDEX = 15 + + /** + * Convert byte array to hex string + */ + fun bytesToHex(bytes: ByteArray): String { + val hexArray = "0123456789ABCDEF".toCharArray() + val hexChars = CharArray(bytes.size * 2) + for (j in bytes.indices) { + val v = bytes[j].toInt() and 0xFF + hexChars[j * 2] = hexArray[v ushr 4] + hexChars[j * 2 + 1] = hexArray[v and 0x0F] + } + return String(hexChars) + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/example/hposregistration/data/aadhar/AadharCard.kt b/app/src/main/java/com/example/hposregistration/data/aadhar/AadharCard.kt new file mode 100644 index 0000000..9d56c8f --- /dev/null +++ b/app/src/main/java/com/example/hposregistration/data/aadhar/AadharCard.kt @@ -0,0 +1,20 @@ +package com.example.hposregistration.data.aadhar + + +class AadharCard { + var name = "" + var dateOfBirth = "" + var gender = "" + var careOf = "" + var district = "" + var landmark = "" + var house = "" + var location = "" + var pinCode = "" + var postOffice = "" + var state = "" + var street = "" + var subDistrict = "" + var vtc = "" + +} \ No newline at end of file diff --git a/app/src/main/java/com/example/hposregistration/data/aadhar/QrCodeException.kt b/app/src/main/java/com/example/hposregistration/data/aadhar/QrCodeException.kt new file mode 100644 index 0000000..8a60ada --- /dev/null +++ b/app/src/main/java/com/example/hposregistration/data/aadhar/QrCodeException.kt @@ -0,0 +1,6 @@ +package com.example.hposregistration.data.aadhar + +class QrCodeException : Exception { + constructor(message: String?) : super(message) {} + constructor(message: String?, cause: Throwable?) : super(message, cause) {} +} \ No newline at end of file diff --git a/app/src/main/java/com/example/hposregistration/data/user/UserData.kt b/app/src/main/java/com/example/hposregistration/data/user/UserData.kt new file mode 100644 index 0000000..00c3a06 --- /dev/null +++ b/app/src/main/java/com/example/hposregistration/data/user/UserData.kt @@ -0,0 +1,49 @@ +package com.example.hposregistration.data.user + +import androidx.room.Entity +import androidx.room.PrimaryKey + +@Entity(tableName = "user_table") +data class UserData( + @PrimaryKey + var _id: String = "", + var AbhaId: String = "", + var AadharId: String = "", + var name: String = "", + var birthYear: String = "", + var gender: String = "", + var phoneNumber: String = "", + var careOf: String = "", + var maritalStatus: String = "", + var caste: String = "", + var subCaste: String = "", + var house: String = "", + var city: String = "", + var district: String = "", + var state: String = "", + var pinCode: String = "", + var bloodGroup: String = "", + var isUnderMedication: Boolean? = false, + var isUnderTransfusion: Boolean? = false, + var sickleCellHistory: String? = "", + var userImage: String = "", + var location: Location? = null, + var uploadTime: String? = "", + var testStatus: Boolean? = false, + var mobileId: String = "", + var deviceId: String = "", + var kitSerial: String = "", + var csvPath: String = "", + var reportPath: String = "" +){ + enum class Gender { + MALE, + FEMALE, + OTHER + } + + data class Location( + var latitude: Double? = null, + var longitude: Double? = null + ) +} diff --git a/app/src/main/java/com/example/hposregistration/di/AppModule.kt b/app/src/main/java/com/example/hposregistration/di/AppModule.kt new file mode 100644 index 0000000..9f0bdf2 --- /dev/null +++ b/app/src/main/java/com/example/hposregistration/di/AppModule.kt @@ -0,0 +1,44 @@ +package com.example.hposregistration.di + +import android.app.Application +import android.content.Context +import androidx.room.Room +import com.example.hposregistration.dao.MyDatabase +import com.example.hposregistration.dao.UserDao +import dagger.Module +import dagger.Provides +import dagger.hilt.InstallIn +import dagger.hilt.android.qualifiers.ApplicationContext +import dagger.hilt.components.SingletonComponent +import javax.inject.Singleton + +@Module +@InstallIn(SingletonComponent::class) +object AppModule { + + @Provides + @Singleton + fun provideMyDatabase(@ApplicationContext context: Context): MyDatabase { + return Room.databaseBuilder( + context, MyDatabase::class.java, "my_database" + ).build() + } + + @Provides + @Singleton + fun provideMyDao(myDatabase: MyDatabase): UserDao { + return myDatabase.userDao() + } + + @Provides + @Singleton + fun provideMyNetworkDao(): com.example.hposregistration.network.UserDao { + return com.example.hposregistration.network.UserDao() + } + + @Provides + @Singleton + fun provideContext(application: Application): Context { + return application.applicationContext + } +} \ No newline at end of file diff --git a/app/src/main/java/com/example/hposregistration/network/UserDao.kt b/app/src/main/java/com/example/hposregistration/network/UserDao.kt new file mode 100644 index 0000000..a05f0bf --- /dev/null +++ b/app/src/main/java/com/example/hposregistration/network/UserDao.kt @@ -0,0 +1,30 @@ +package com.example.hposregistration.network + +import com.example.hposregistration.data.Response +import com.example.hposregistration.data.user.UserData +import com.google.firebase.firestore.FirebaseFirestore +import com.google.firebase.firestore.ktx.firestore +import com.google.firebase.ktx.Firebase +import kotlinx.coroutines.tasks.await + +class UserDao { + + private val db: FirebaseFirestore = Firebase.firestore + private val patientCollection = db.collection("patientData") + + suspend fun addUserToDatabase(data: UserData): Response { + return try { + patientCollection.document() + .set(data) + .await() + + Response.Success(data._id) + } catch (e: Exception) { + Response.Error(e) + } + } + +// suspend fun getPatientList(): List { +// return patientCollection.get().await().toObjects(UserData::class.java) +// } +} \ No newline at end of file diff --git a/app/src/main/java/com/example/hposregistration/ui/activities/MainActivity.kt b/app/src/main/java/com/example/hposregistration/ui/activities/MainActivity.kt new file mode 100644 index 0000000..9998986 --- /dev/null +++ b/app/src/main/java/com/example/hposregistration/ui/activities/MainActivity.kt @@ -0,0 +1,18 @@ +package com.example.hposregistration.ui.activities + +import androidx.appcompat.app.AppCompatActivity +import android.os.Bundle +import com.example.hposregistration.databinding.ActivityMainBinding +import dagger.hilt.android.AndroidEntryPoint + +@AndroidEntryPoint +class MainActivity : AppCompatActivity() { + + private lateinit var binding: ActivityMainBinding + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + binding = ActivityMainBinding.inflate(layoutInflater) + setContentView(binding.root) + } +} \ No newline at end of file diff --git a/app/src/main/java/com/example/hposregistration/ui/fragments/BaseFragment.kt b/app/src/main/java/com/example/hposregistration/ui/fragments/BaseFragment.kt new file mode 100644 index 0000000..8b467de --- /dev/null +++ b/app/src/main/java/com/example/hposregistration/ui/fragments/BaseFragment.kt @@ -0,0 +1,50 @@ +package com.example.hposregistration.ui.fragments + +import android.view.View +import android.widget.Button +import android.widget.TextView +import androidx.fragment.app.Fragment +import com.journeyapps.barcodescanner.ScanOptions + +open class BaseFragment: Fragment() { + + fun hideProgressBar(view: View) { + view.visibility = View.GONE + } + + fun showProgressBar(view: View) { + view.visibility = View.VISIBLE + } + +// fun hideErrorMessage(view: View) { +// view.visibility = View.GONE +// } +// +// fun showErrorMessage(message: String, errorView: View, errorMessageView: TextView) { +// errorView.visibility = View.VISIBLE +// errorMessageView.text = message +// } +// +// fun markButtonDisable(button: Button) { +// button.isEnabled = false +// } +// +// fun markButtonEnable(button: Button) { +// button.isEnabled = true +// } + + fun scanQR(scannerMessage: String): ScanOptions { + val options = ScanOptions() + options.setDesiredBarcodeFormats(ScanOptions.QR_CODE) + options.setPrompt("Scan a barcode") + options.setCameraId(0) + + options.setBeepEnabled(true) + options.setBarcodeImageEnabled(true) + + options.setPrompt(scannerMessage) + options.setOrientationLocked(false) + + return options + } +} \ No newline at end of file diff --git a/app/src/main/java/com/example/hposregistration/ui/fragments/ProfileFragment.kt b/app/src/main/java/com/example/hposregistration/ui/fragments/ProfileFragment.kt new file mode 100644 index 0000000..4b6c012 --- /dev/null +++ b/app/src/main/java/com/example/hposregistration/ui/fragments/ProfileFragment.kt @@ -0,0 +1,26 @@ +package com.example.hposregistration.ui.fragments + +import android.os.Bundle +import androidx.fragment.app.Fragment +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import com.example.hposregistration.databinding.FragmentProfileBinding +import dagger.hilt.android.AndroidEntryPoint + +@AndroidEntryPoint +class ProfileFragment : Fragment() { + + private lateinit var _binding: FragmentProfileBinding + + private val binding get() = _binding + + override fun onCreateView( + inflater: LayoutInflater, container: ViewGroup?, + savedInstanceState: Bundle? + ): View { + _binding = FragmentProfileBinding.inflate(layoutInflater, container, false) + + return binding.root + } +} \ No newline at end of file diff --git a/app/src/main/java/com/example/hposregistration/ui/fragments/UserListFragment.kt b/app/src/main/java/com/example/hposregistration/ui/fragments/UserListFragment.kt new file mode 100644 index 0000000..a0b1a44 --- /dev/null +++ b/app/src/main/java/com/example/hposregistration/ui/fragments/UserListFragment.kt @@ -0,0 +1,161 @@ +package com.example.hposregistration.ui.fragments + +import android.app.AlertDialog +import android.content.Context +import android.content.DialogInterface +import android.os.Bundle +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import android.widget.Toast +import androidx.fragment.app.viewModels +import androidx.navigation.fragment.findNavController +import com.example.hposregistration.R +import com.example.hposregistration.adapters.UserListOfflineAdapter +import com.example.hposregistration.adapters.UserListOnlineAdapter +import com.example.hposregistration.data.user.UserData +import com.example.hposregistration.databinding.FragmentUserListBinding +import com.example.hposregistration.ui.viewmodels.RegistrationViewModel +import com.firebase.ui.firestore.FirestoreRecyclerOptions +import com.google.firebase.firestore.ktx.firestore +import com.google.firebase.ktx.Firebase +import dagger.hilt.android.AndroidEntryPoint + +@AndroidEntryPoint +class UserListFragment : BaseFragment() { + + private var _binding: FragmentUserListBinding? = null + private val binding get() = _binding!! + + private val viewModel by viewModels() + + private var isOnline = false + + override fun onCreateView( + inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle? + ): View { + _binding = FragmentUserListBinding.inflate(inflater, container, false) + setupViews() + return binding.root + } + + override fun onResume() { + super.onResume() + init() + } + + override fun onDestroyView() { + super.onDestroyView() + _binding = null + } + + private fun setupViews() { + binding.btnRegisterUser.setOnClickListener { + findNavController().navigate(R.id.action_userListFragment_to_registrationFragment) + } + } + + private fun init() { + viewModel.networkStatusLiveData.observe(viewLifecycleOwner) { isNetworkAvailable -> + if (isNetworkAvailable) { + isOnline = isNetworkAvailable + fetchFromServer() + checkForLocalDBData() + } else { + isOnline = isNetworkAvailable + fetchFromLocalDB() + } + } + + binding.uploadData.setOnClickListener { + showUploadDialog(requireContext()) + } + } + + private fun showUploadDialog(context: Context) { + val builder = AlertDialog.Builder(context) + builder.setTitle("Upload DB Registration") + builder.setMessage("Do you want to upload the local DB Registration to the cloud?") + + // Positive button + builder.setPositiveButton("Upload") { dialog, _ -> + uploadLoadDBData(dialog) + } + + // Negative button + builder.setNegativeButton("Cancel") { dialog, _ -> + dialog.dismiss() + } + + val dialog = builder.create() + dialog.show() + } + + private fun uploadLoadDBData(dialog: DialogInterface) { + viewModel.allUserData.observe(viewLifecycleOwner) { + if (it.isEmpty()) { + dialog.dismiss() + } else { + it.forEach { userData -> + viewModel.addUserToDatabase(userData) + viewModel.deleteById(userData._id) + } + dialog.dismiss() + Toast.makeText(requireContext(), "Upload done successfully", Toast.LENGTH_SHORT) + .show() + } + } + } + + private fun checkForLocalDBData() { + viewModel.allUserData.observe(viewLifecycleOwner) { userDataList -> + if (userDataList.isEmpty()) { + binding.uploadData.visibility = View.GONE + } else { + binding.uploadData.visibility = View.VISIBLE + } + } + } + + private fun fetchFromServer() { + binding.noList.visibility = View.GONE + binding.rvOrder.visibility = View.VISIBLE + val query = Firebase.firestore.collection("patientData") + val recyclerViewOptions = + FirestoreRecyclerOptions.Builder().setQuery(query, UserData::class.java) + .build() + val adapter = UserListOnlineAdapter(recyclerViewOptions, binding.root) + binding.rvOrder.adapter = adapter + adapter.startListening() + } + + private fun fetchFromLocalDB() { + binding.uploadData.visibility = View.GONE + viewModel.allUserData.observe(viewLifecycleOwner) { userDataList -> + if (isOnline) { + binding.noList.visibility = View.GONE + binding.rvOrder.visibility = View.VISIBLE + } else { + if (userDataList.isEmpty()) { + binding.noList.visibility = View.VISIBLE + binding.rvOrder.visibility = View.GONE + } else { + binding.noList.visibility = View.GONE + binding.rvOrder.visibility = View.VISIBLE + deteleIncompleteRegistration(userDataList) + val adapter = UserListOfflineAdapter(binding.root) + adapter.differ.submitList(userDataList) + binding.rvOrder.adapter = adapter + } + } + } + } + + private fun deteleIncompleteRegistration(userDataList: List) { + userDataList.forEach { + if (it.userImage.isEmpty()) { + viewModel.deleteById(it._id) + } + } + } +} diff --git a/app/src/main/java/com/example/hposregistration/ui/fragments/registration/AbhaRegistrationFragment.kt b/app/src/main/java/com/example/hposregistration/ui/fragments/registration/AbhaRegistrationFragment.kt new file mode 100644 index 0000000..cc260f3 --- /dev/null +++ b/app/src/main/java/com/example/hposregistration/ui/fragments/registration/AbhaRegistrationFragment.kt @@ -0,0 +1,204 @@ +package com.example.hposregistration.ui.fragments.registration + +import android.os.Bundle +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import android.widget.Toast +import androidx.fragment.app.viewModels +import androidx.navigation.fragment.findNavController +import androidx.navigation.fragment.navArgs +import com.example.hposregistration.data.Constant.fetchYOBFromDOB +import com.example.hposregistration.data.Constant.mapToGender +import com.example.hposregistration.data.SecureQrCode +import com.example.hposregistration.data.user.UserData +import com.example.hposregistration.databinding.FragmentAbhaRegistrationBinding +import com.example.hposregistration.ui.fragments.BaseFragment +import com.example.hposregistration.ui.viewmodels.RegistrationViewModel +import com.journeyapps.barcodescanner.ScanContract +import com.journeyapps.barcodescanner.ScanIntentResult +import dagger.hilt.android.AndroidEntryPoint +import org.json.JSONObject + +@AndroidEntryPoint +class AbhaRegistrationFragment : BaseFragment() { + + private var _binding: FragmentAbhaRegistrationBinding? = null + private val binding get() = _binding!! + + private val viewModel by viewModels() + private val args: AbhaRegistrationFragmentArgs by navArgs() + private var userData = UserData() + + private val barcodeLauncher = + registerForActivityResult(ScanContract()) { result: ScanIntentResult -> + if (result.contents == null) { + Toast.makeText( + requireContext(), "Cancelled: Unable to scan, Try Again!!", Toast.LENGTH_LONG + ).show() + } else { + processScannedData(result.contents) + } + } + + override fun onCreateView( + inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle? + ): View { + _binding = FragmentAbhaRegistrationBinding.inflate(inflater, container, false) + return binding.root + } + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + init() + } + + private fun init() { + binding.btnScanNow.setOnClickListener { + barcodeLauncher.launch(scanQR("Please Scan the Abha ID/Aadhar ID")) + } + + if (args.userId != "null") { + viewModel.getUserByID(args.userId) + } + + viewModel.userData.observe(viewLifecycleOwner) { user -> + userData = user + with(binding) { + aadharID.setText(user.AadharId) + etAbhaId.setText(user.AbhaId) + etName.setText(user.name) + } + } + + binding.btnProceed.setOnClickListener { + val abhaId = binding.etAbhaId.text.toString() + val userName = binding.etName.text.toString() + val aadharId = binding.aadharID.text.toString() + + if (validateInputs(abhaId, aadharId, userName)) { + userData.apply { + name = userName + AadharId = aadharId + AbhaId = abhaId + _id = aadharId + userName.substring(0, 3).uppercase() + } + + viewModel.insert(userData) + + navigateToUserDetailsFragment(userData._id) + } + } + } + + private fun processScannedData(contents: String) { + try { + val obj = JSONObject(contents) + + userData.apply { + AbhaId = obj.getString("hidn").replace("-", "") + name = obj.getString("name") + birthYear = obj.getString("dob").fetchYOBFromDOB().toString() + gender = obj.getString("gender").mapToGender().toString() + phoneNumber = obj.getString("mobile") + + state = obj.getString("state name") + district = obj.getString("district_name") + house = obj.getString("address") + } + + if (userData.AbhaId.isNotEmpty()) { + saveUserDataAsPerAbhaId() + } else { + saveUserDataAsPerAadharId() + } + } catch (e: Exception) { + try { + val decodedData = SecureQrCode(contents) + val aadharData = decodedData.scannedAadharCard + userData.apply { + name = aadharData.name + birthYear = aadharData.dateOfBirth.fetchYOBFromDOB().toString() + gender = aadharData.gender.mapToGender().toString() + state = aadharData.state + district = aadharData.district + house = aadharData.house + } + + saveUserDataAsPerAadharId() + } catch (e: Exception) { + Toast.makeText( + requireContext(), + "The QR Code you have scanned is not valid, enter the data manually", + Toast.LENGTH_SHORT + ).show() + } + } + } + + private fun saveUserDataAsPerAbhaId() { + with(binding) { + tvScanMessage.visibility = View.VISIBLE + tvScanMessage.text = "You have scanned the Abha ID, need to enter Aadhar ID manually" + etAbhaId.setText(userData.AbhaId) + etName.setText(userData.name) + } + Toast.makeText( + requireContext(), "You have scanned the Abha ID", Toast.LENGTH_SHORT + ).show() + } + + private fun saveUserDataAsPerAadharId() { + with(binding) { + tvScanMessage.text = + "You have scanned the Aadhar ID, need to enter Aadhar ID number you have scan and Abha ID manually" + etName.setText(userData.name) + } + Toast.makeText( + requireContext(), "You have scanned the Aadhar ID", Toast.LENGTH_SHORT + ).show() + } + + private fun validateInputs(abhaId: String, aadharId: String, userName: String): Boolean { + with(binding) { + if (!checkAbhaIDNumber(abhaId)) { + etAbhaId.error = "Invalid Abha ID" + return false + } + if (!checkAadharIDNumber(aadharId)) { + aadharID.error = "Invalid Aadhar ID" + return false + } + if (!checkUserName(userName)) { + etName.error = "Name field can't be empty or less than 3 characters" + return false + } + } + return true + } + + private fun checkAbhaIDNumber(abhaId: String): Boolean { + return abhaId.isNotEmpty() || abhaId.length == 14 + } + + private fun checkAadharIDNumber(aadharId: String): Boolean { + return aadharId.isNotEmpty() || aadharId.length == 12 + } + + private fun checkUserName(userName: String): Boolean { + return userName.isNotEmpty() && userName.length >= 3 + } + + private fun navigateToUserDetailsFragment(userId: String) { + val action = + AbhaRegistrationFragmentDirections.actionRegistrationFragmentToUserDetailsRegistrationFragment( + userId + ) + findNavController().navigate(action) + } + + override fun onDestroyView() { + super.onDestroyView() + _binding = null + } +} diff --git a/app/src/main/java/com/example/hposregistration/ui/fragments/registration/CaptureUserImageFragment.kt b/app/src/main/java/com/example/hposregistration/ui/fragments/registration/CaptureUserImageFragment.kt new file mode 100644 index 0000000..c96ce79 --- /dev/null +++ b/app/src/main/java/com/example/hposregistration/ui/fragments/registration/CaptureUserImageFragment.kt @@ -0,0 +1,181 @@ +package com.example.hposregistration.ui.fragments.registration + +import android.Manifest +import android.app.Activity +import android.content.Intent +import android.content.pm.PackageManager +import android.graphics.Bitmap +import android.net.Uri +import android.os.Bundle +import android.provider.MediaStore +import androidx.fragment.app.Fragment +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import android.widget.ImageView +import androidx.activity.result.ActivityResultLauncher +import androidx.activity.result.contract.ActivityResultContracts +import androidx.core.content.ContextCompat +import androidx.fragment.app.viewModels +import androidx.navigation.fragment.findNavController +import androidx.navigation.fragment.navArgs +import com.bumptech.glide.Glide +import com.example.hposregistration.data.user.UserData +import com.example.hposregistration.databinding.FragmentCaptureUserImageBinding +import com.example.hposregistration.ui.viewmodels.RegistrationViewModel +import dagger.hilt.android.AndroidEntryPoint +import java.io.File +import java.io.FileOutputStream +import java.io.IOException +import java.io.OutputStream +import java.util.UUID + +@AndroidEntryPoint +class CaptureUserImageFragment : Fragment() { + + private var _binding: FragmentCaptureUserImageBinding? = null + private val binding get() = _binding!! + + private val viewModel by viewModels() + + private var userData = UserData() + + private lateinit var cameraLauncher: ActivityResultLauncher + + private val args: CaptureUserImageFragmentArgs by navArgs() + + private lateinit var requestPermissionLauncher: ActivityResultLauncher> + + override fun onCreateView( + inflater: LayoutInflater, container: ViewGroup?, + savedInstanceState: Bundle? + ): View { + _binding = FragmentCaptureUserImageBinding.inflate(inflater, container, false) + return binding.root + } + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + init() + } + + private fun init() { + setupViewModel() + setupCameraLauncher() + setupRequestPermissionLauncher() + checkPermissionsAndPerformCameraOperation() + } + + private fun setupViewModel() { + viewModel.getUserByID(args.userId) + viewModel.userData.observe(viewLifecycleOwner) { user -> + userData = user + if (userData.userImage.isNotEmpty()) { + binding.userImage.loadImageFromUri(Uri.parse(user.userImage)) + } + } + } + + private fun setupCameraLauncher() { + cameraLauncher = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { result -> + if (result.resultCode == Activity.RESULT_OK && result.data != null) { + val thumbnail: Bitmap = result.data!!.extras!!.get("data") as Bitmap + val uri: Uri = saveImageToInternalStorage(thumbnail) + uri.let { + binding.btnSubmit.isEnabled = true + Glide.with(requireActivity()).load(it).into(binding.userImage) + binding.btnSubmit.setOnClickListener { + submitImage(uri) + } + } + } + } + } + + private fun setupRequestPermissionLauncher() { + requestPermissionLauncher = + registerForActivityResult(ActivityResultContracts.RequestMultiplePermissions()) { permissions -> + val grantedPermissions = permissions.filterValues { it } + val deniedPermissions = permissions.filterValues { !it } + + if (grantedPermissions.isNotEmpty()) { + binding.userImage.setOnClickListener { + takePicture() + } + } + + if (deniedPermissions.isNotEmpty()) { + requestPermissions() + } + } + } + + private fun takePicture() { + Intent(MediaStore.ACTION_IMAGE_CAPTURE).also { + cameraLauncher.launch(it) + } + } + + private fun submitImage(uri: Uri) { + userData.userImage = uri.toString() + viewModel.insert(userData) + val action = CaptureUserImageFragmentDirections + .actionCaptureUserImageFragment2ToReviewDetailsRegistrationFragment(args.userId) + findNavController().navigate(action) + } + + private fun saveImageToInternalStorage(bitmap: Bitmap): Uri { + + val file = File(context?.filesDir, "${UUID.randomUUID()}.jpg") + + try { + val stream: OutputStream = FileOutputStream(file) + bitmap.compress(Bitmap.CompressFormat.JPEG, 80, stream) + stream.flush() + stream.close() + } catch (e: IOException) { + e.printStackTrace() + } + return Uri.fromFile(file) + } + + private fun checkPermissionsAndPerformCameraOperation() { + val cameraPermission = ContextCompat.checkSelfPermission( + requireContext(), + Manifest.permission.CAMERA + ) + val storagePermission = ContextCompat.checkSelfPermission( + requireContext(), + Manifest.permission.WRITE_EXTERNAL_STORAGE + ) + + if (cameraPermission == PackageManager.PERMISSION_GRANTED && + storagePermission == PackageManager.PERMISSION_GRANTED + ) { + binding.userImage.setOnClickListener { + takePicture() + } + } else { + requestPermissions() + } + } + + private fun requestPermissions() { + val permissions = arrayOf( + Manifest.permission.CAMERA, + Manifest.permission.WRITE_EXTERNAL_STORAGE + ) + requestPermissionLauncher.launch(permissions) + } + + override fun onDestroyView() { + super.onDestroyView() + _binding = null + } +} + +private fun ImageView.loadImageFromUri(uri: Uri) { + Glide.with(this) + .load(uri) + .into(this) +} diff --git a/app/src/main/java/com/example/hposregistration/ui/fragments/registration/ReviewDetailsRegistrationFragment.kt b/app/src/main/java/com/example/hposregistration/ui/fragments/registration/ReviewDetailsRegistrationFragment.kt new file mode 100644 index 0000000..a0012b5 --- /dev/null +++ b/app/src/main/java/com/example/hposregistration/ui/fragments/registration/ReviewDetailsRegistrationFragment.kt @@ -0,0 +1,154 @@ +package com.example.hposregistration.ui.fragments.registration + +import android.net.Uri +import android.os.Bundle +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import android.widget.Toast +import androidx.fragment.app.viewModels +import androidx.navigation.fragment.findNavController +import androidx.navigation.fragment.navArgs +import com.bumptech.glide.Glide +import com.example.hposregistration.R +import com.example.hposregistration.data.user.UserData +import com.example.hposregistration.databinding.FragmentReviewDetailsRegistrationBinding +import com.example.hposregistration.ui.fragments.BaseFragment +import com.example.hposregistration.ui.viewmodels.RegistrationViewModel +import dagger.hilt.android.AndroidEntryPoint + +@AndroidEntryPoint +class ReviewDetailsRegistrationFragment : BaseFragment() { + + private var _binding: FragmentReviewDetailsRegistrationBinding? = null + private val binding get() = _binding!! + + private val viewModel by viewModels() + private val args: ReviewDetailsRegistrationFragmentArgs by navArgs() + private var userData = UserData() + private var isOnline = false + + override fun onCreateView( + inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle? + ): View { + _binding = FragmentReviewDetailsRegistrationBinding.inflate(inflater, container, false) + return binding.root + } + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + init() + } + + private fun init() { + viewModel.getUserByID(args.userId) + viewModel.userData.observe(viewLifecycleOwner) { user -> + userData = user + setUserData(user) + } + + viewModel.networkStatusLiveData.observe(viewLifecycleOwner) { isNetworkAvailable -> + isOnline = isNetworkAvailable + } + + binding.btnSubmit.setOnClickListener { + if (isOnline) { + showProgressBar(binding.progressBarCL) + addDataToFirebase() + } else { + Toast.makeText( + requireContext(), + "Internet not available, Data stored in local DB", + Toast.LENGTH_SHORT + ).show() + navigateToUserListFragment() + } + } + + binding.btnEdit.setOnClickListener { + val action = + ReviewDetailsRegistrationFragmentDirections.actionReviewDetailsRegistrationFragmentToRegistrationFragment( + args.userId + ) + findNavController().navigate(action) + } + + viewModel.fireBaseUpload.observe(viewLifecycleOwner) { + if (it == "Success") { + Toast.makeText(requireContext(), "Registration Done Successfully", Toast.LENGTH_SHORT) + .show() + navigateToUserListFragment() + } + } + } + + private fun addDataToFirebase() { + viewModel.addUserToDatabase(userData) + viewModel.deleteById(args.userId) + } + + private fun setUserData(userData: UserData) { + with(binding) { + Glide.with(requireActivity()).load(Uri.parse(userData.userImage)).into(userImage) + binding.tvUserID.text = requireContext().getString(R.string.user_id, userData._id) + binding.tvAbhaId.text = + requireContext().getString(R.string.abha_number, userData.AbhaId) + binding.tvAadharId.text = + requireContext().getString(R.string.aadhar_number_details, userData.AadharId) + binding.tvUserName.text = requireContext().getString(R.string.user_name, userData.name) + binding.tvBirthYear.text = + requireContext().getString(R.string.birth_year_details, userData.birthYear) + binding.tvGender.text = + requireContext().getString(R.string.gender_details, userData.gender) + binding.tvPhoneNumber.text = + requireContext().getString(R.string.phone_number_details, userData.phoneNumber) + binding.tvCareOf.text = + requireContext().getString(R.string.care_of_details, userData.careOf) + binding.tvMaritalStatus.text = + requireContext().getString(R.string.marital_status_details, userData.maritalStatus) + binding.tvCaste.text = + requireContext().getString(R.string.caste_details, userData.caste) + binding.tvSubCaste.text = + requireContext().getString(R.string.sub_caste_details, userData.subCaste) + binding.tvHouse.text = + requireContext().getString(R.string.house_details, userData.house) + binding.tvCity.text = + requireContext().getString(R.string.city_town_details, userData.city) + binding.tvDistrict.text = + requireContext().getString(R.string.district_details, userData.district) + binding.tvState.text = + requireContext().getString(R.string.state_details, userData.state) + binding.tvPincode.text = + requireContext().getString(R.string.pincode_details, userData.pinCode) + if (userData.isUnderMedication!!) { + binding.tvIsUnderMedication.text = requireContext().getString( + R.string.is_patient_under_any_medication_details, "Yes" + ) + } + if (userData.isUnderTransfusion!!) { + binding.tvIsUnderTransfusion.text = requireContext().getString( + R.string.is_patient_undergoing_any_blood_transfusion_details, "Yes" + ) + } + + if (userData.sickleCellHistory != "") { + binding.tvSickleCellFamilyHistory.text = requireContext().getString( + R.string.sickle_cell_family_history, userData.sickleCellHistory + ) + } + } + } + + private fun navigateToUserListFragment() { + hideProgressBar(binding.progressBarCL) + + val action = + ReviewDetailsRegistrationFragmentDirections.actionReviewDetailsRegistrationFragmentToUserListFragment() + findNavController().navigate(action) + } + + override fun onDestroyView() { + super.onDestroyView() + _binding = null + } +} \ No newline at end of file diff --git a/app/src/main/java/com/example/hposregistration/ui/fragments/registration/UserDetailsRegistrationFragment.kt b/app/src/main/java/com/example/hposregistration/ui/fragments/registration/UserDetailsRegistrationFragment.kt new file mode 100644 index 0000000..259f9bd --- /dev/null +++ b/app/src/main/java/com/example/hposregistration/ui/fragments/registration/UserDetailsRegistrationFragment.kt @@ -0,0 +1,221 @@ +package com.example.hposregistration.ui.fragments.registration + +import android.os.Bundle +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import android.widget.Toast +import androidx.fragment.app.Fragment +import androidx.fragment.app.viewModels +import androidx.navigation.fragment.findNavController +import androidx.navigation.fragment.navArgs +import com.example.hposregistration.data.user.UserData +import com.example.hposregistration.databinding.FragmentUserDetailsRegistrationBinding +import com.example.hposregistration.ui.viewmodels.RegistrationViewModel +import dagger.hilt.android.AndroidEntryPoint + +@AndroidEntryPoint +class UserDetailsRegistrationFragment : Fragment() { + + private var _binding: FragmentUserDetailsRegistrationBinding? = null + private val binding get() = _binding!! + + private val viewModel by viewModels() + + private lateinit var userData: UserData + + private val args: UserDetailsRegistrationFragmentArgs by navArgs() + + override fun onCreateView( + inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle? + ): View { + _binding = FragmentUserDetailsRegistrationBinding.inflate(inflater, container, false) + return binding.root + } + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + init() + } + + private fun init() { + setupViewModel() + setupSubmitButton() + } + + private fun setupViewModel() { + viewModel.getUserByID(args.userId) + viewModel.userData.observe(viewLifecycleOwner) { + userData = it + autoFillUserData(userData) + } + } + + private fun autoFillUserData(user: UserData) { + with(binding) { + user.birthYear.let { etYob.setText(it) } + user.gender.let { etGenderBlock.setText(it, false) } + user.phoneNumber.let { etMobile.setText(it) } + user.careOf.let { etCareof.setText(it) } + user.maritalStatus.let { etIsMarried.setText(it, false) } + user.caste.let { etCategory.setText(it, false) } + user.subCaste.let { etSubCaste.setText(it) } + user.house.let { etHouse.setText(it) } + user.city.let { etCity.setText(it) } + user.district.let { etDistrict.setText(it) } + user.state.let { etState.setText(it) } + user.pinCode.let { etPincode.setText(it) } + user.bloodGroup.let { etBloodGroup.setText(it, false) } + cbItem1.isChecked = user.isUnderMedication ?: false + cbItem2.isChecked = user.isUnderTransfusion ?: false + } + } + + private fun setupSubmitButton() { + binding.btnSubmit.setOnClickListener { + if (verifyUserDetails()) { + userData._id = args.userId + viewModel.insert(userData) + navigateToCaptureUserImageFragment() + } else { + showToast("Please enter all the details properly") + } + } + } + + private fun navigateToCaptureUserImageFragment() { + val action = UserDetailsRegistrationFragmentDirections + .actionUserDetailsRegistrationFragmentToCaptureUserImageFragment2(args.userId) + findNavController().navigate(action) + } + + private fun verifyUserDetails(): Boolean { + with(binding) { + val yob = etYob.text.toString() + if (yob.isEmpty() || yob.toInt() <= 1900 || yob.toInt() > 2023) { + etYob.error = "Please enter a valid birth year between 1900 and 2023" + return false + } else { + userData.birthYear = yob + } + + val gender = etGenderBlock.text.toString() + if (gender.isEmpty()) { + etGenderBlock.error = "Please enter your gender" + return false + } else { + userData.gender = gender + } + + val mobile = etMobile.text.toString() + if (mobile.isEmpty() || mobile.length != 10) { + etMobile.error = "Please enter a valid 10-digit mobile number" + return false + } else { + userData.phoneNumber = mobile + } + + val careOf = etCareof.text.toString() + if (careOf.isEmpty()) { + etCareof.error = "Please enter your name" + return false + } else { + userData.careOf = careOf + } + + val maritalStatus = etIsMarried.text.toString() + if (maritalStatus.isEmpty()) { + etIsMarried.error = "Please enter your marital status" + return false + } else { + userData.maritalStatus = maritalStatus + } + + val category = etCategory.text.toString() + if (category.isEmpty()) { + etCategory.error = "Please enter your caste" + return false + } else { + userData.caste = category + } + + val subCaste = etSubCaste.text.toString() + if (subCaste.isEmpty()) { + etSubCaste.error = "Please enter your sub caste" + return false + } else { + userData.subCaste = subCaste + } + + val house = etHouse.text.toString() + if (house.isEmpty()) { + etHouse.error = "Please enter your house address" + return false + } else { + userData.house = house + } + + val city = etCity.text.toString() + if (city.isEmpty()) { + etCity.error = "Please enter your city" + return false + } else { + userData.city = city + } + + val district = etDistrict.text.toString() + if (district.isEmpty()) { + etDistrict.error = "Please enter your district" + return false + } else { + userData.district = district + } + + val state = etState.text.toString() + if (state.isEmpty()) { + etState.error = "Please enter your state" + return false + } else { + userData.state = state + } + + val pincode = etPincode.text.toString() + if (pincode.isEmpty() || pincode.length != 6) { + etPincode.error = "Please enter a valid 6-digit pin code" + return false + } else { + userData.pinCode = pincode + } + + val bloodGroup = etBloodGroup.text.toString() + if (bloodGroup.isEmpty()) { + etBloodGroup.error = "Please enter your blood group" + return false + } else { + userData.bloodGroup = bloodGroup + } + + userData.isUnderMedication = cbItem1.isChecked + userData.isUnderTransfusion = cbItem2.isChecked + + + val familyHistory = etFamilyHistory.text.toString() + if (familyHistory.isEmpty()) { + etFamilyHistory.error = "Please enter your family history" + return false + } else { + userData.sickleCellHistory = familyHistory + } + return true + } + } + + private fun showToast(message: String) { + Toast.makeText(requireContext(), message, Toast.LENGTH_SHORT).show() + } + + override fun onDestroyView() { + super.onDestroyView() + _binding = null + } +} diff --git a/app/src/main/java/com/example/hposregistration/ui/viewmodels/RegistrationViewModel.kt b/app/src/main/java/com/example/hposregistration/ui/viewmodels/RegistrationViewModel.kt new file mode 100644 index 0000000..19b5f0a --- /dev/null +++ b/app/src/main/java/com/example/hposregistration/ui/viewmodels/RegistrationViewModel.kt @@ -0,0 +1,76 @@ +package com.example.hposregistration.ui.viewmodels + +import android.content.Context +import android.net.Uri +import androidx.lifecycle.LiveData +import androidx.lifecycle.MutableLiveData +import androidx.lifecycle.ViewModel +import androidx.lifecycle.viewModelScope +import com.example.hposregistration.data.MainRepository +import com.example.hposregistration.data.NetworkStatusLiveData +import com.example.hposregistration.data.Response +import com.example.hposregistration.data.user.UserData +import com.google.firebase.storage.FirebaseStorage +import dagger.hilt.android.lifecycle.HiltViewModel +import kotlinx.coroutines.launch +import javax.inject.Inject + +@HiltViewModel +class RegistrationViewModel @Inject constructor( + private val mainRepository: MainRepository, + context: Context +) : ViewModel() { + + val userData = MutableLiveData() + + val allUserData = mainRepository.allUsers + + val fireBaseUpload = MutableLiveData() + + fun addUserToDatabase(userData: UserData) = viewModelScope.launch { + val file = Uri.parse(userData.userImage) + val storageRef = FirebaseStorage.getInstance().reference + val imageRef = storageRef.child("${userData._id}/${file.lastPathSegment}") + + val uploadTask = file.let { imageRef.putFile(it) } + uploadTask.continueWithTask { task -> + if (!task.isSuccessful) { + throw task.exception!! + } + imageRef.downloadUrl + }.addOnSuccessListener { uri -> + userData.userImage = uri.toString() + addDataToFirebase(userData) + }.addOnFailureListener { exception -> + + } + } + + private fun addDataToFirebase(userData: UserData) = viewModelScope.launch { + when (mainRepository.addDataToFirebase(userData)) { + is Response.Success -> { + fireBaseUpload.postValue("Success") + } + + else -> { + + } + } + } + + private val _networkStatusLiveData = NetworkStatusLiveData(context) + val networkStatusLiveData: LiveData + get() = _networkStatusLiveData + + fun insert(userData: UserData) = viewModelScope.launch { + mainRepository.insert(userData) + } + + fun getUserByID(userId: String) = viewModelScope.launch { + userData.postValue(mainRepository.getUserByID(id = userId)) + } + + fun deleteById(userId: String) = viewModelScope.launch { + mainRepository.deleteById(id = userId) + } +} \ No newline at end of file diff --git a/app/src/main/res/drawable-v24/ic_launcher_foreground.xml b/app/src/main/res/drawable-v24/ic_launcher_foreground.xml new file mode 100644 index 0000000..2b068d1 --- /dev/null +++ b/app/src/main/res/drawable-v24/ic_launcher_foreground.xml @@ -0,0 +1,30 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_add.xml b/app/src/main/res/drawable/ic_add.xml new file mode 100644 index 0000000..c22704d --- /dev/null +++ b/app/src/main/res/drawable/ic_add.xml @@ -0,0 +1,5 @@ + + + diff --git a/app/src/main/res/drawable/ic_launcher_background.xml b/app/src/main/res/drawable/ic_launcher_background.xml new file mode 100644 index 0000000..07d5da9 --- /dev/null +++ b/app/src/main/res/drawable/ic_launcher_background.xml @@ -0,0 +1,170 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/drawable/ic_qr_code_scanner.xml b/app/src/main/res/drawable/ic_qr_code_scanner.xml new file mode 100644 index 0000000..206747a --- /dev/null +++ b/app/src/main/res/drawable/ic_qr_code_scanner.xml @@ -0,0 +1,5 @@ + + + diff --git a/app/src/main/res/drawable/progressbar_costume_view.xml b/app/src/main/res/drawable/progressbar_costume_view.xml new file mode 100644 index 0000000..613b88a --- /dev/null +++ b/app/src/main/res/drawable/progressbar_costume_view.xml @@ -0,0 +1,27 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/upload_data.xml b/app/src/main/res/drawable/upload_data.xml new file mode 100644 index 0000000..c990419 --- /dev/null +++ b/app/src/main/res/drawable/upload_data.xml @@ -0,0 +1,5 @@ + + + diff --git a/app/src/main/res/font/poppins_bold.ttf b/app/src/main/res/font/poppins_bold.ttf new file mode 100644 index 0000000..b94d47f Binary files /dev/null and b/app/src/main/res/font/poppins_bold.ttf differ diff --git a/app/src/main/res/font/poppins_extra_bold.ttf b/app/src/main/res/font/poppins_extra_bold.ttf new file mode 100644 index 0000000..8f008c3 Binary files /dev/null and b/app/src/main/res/font/poppins_extra_bold.ttf differ diff --git a/app/src/main/res/font/poppins_light.ttf b/app/src/main/res/font/poppins_light.ttf new file mode 100644 index 0000000..2ab0221 Binary files /dev/null and b/app/src/main/res/font/poppins_light.ttf differ diff --git a/app/src/main/res/font/poppins_medium.ttf b/app/src/main/res/font/poppins_medium.ttf new file mode 100644 index 0000000..e90e87e Binary files /dev/null and b/app/src/main/res/font/poppins_medium.ttf differ diff --git a/app/src/main/res/font/poppins_regular.ttf b/app/src/main/res/font/poppins_regular.ttf new file mode 100644 index 0000000..be06e7f Binary files /dev/null and b/app/src/main/res/font/poppins_regular.ttf differ diff --git a/app/src/main/res/font/poppins_semi_bold.ttf b/app/src/main/res/font/poppins_semi_bold.ttf new file mode 100644 index 0000000..dabf7c2 Binary files /dev/null and b/app/src/main/res/font/poppins_semi_bold.ttf differ diff --git a/app/src/main/res/layout/activity_main.xml b/app/src/main/res/layout/activity_main.xml new file mode 100644 index 0000000..0e525ec --- /dev/null +++ b/app/src/main/res/layout/activity_main.xml @@ -0,0 +1,22 @@ + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_abha_registration.xml b/app/src/main/res/layout/fragment_abha_registration.xml new file mode 100644 index 0000000..c0d1d53 --- /dev/null +++ b/app/src/main/res/layout/fragment_abha_registration.xml @@ -0,0 +1,295 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_capture_user_image.xml b/app/src/main/res/layout/fragment_capture_user_image.xml new file mode 100644 index 0000000..8d9ca7e --- /dev/null +++ b/app/src/main/res/layout/fragment_capture_user_image.xml @@ -0,0 +1,47 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_profile.xml b/app/src/main/res/layout/fragment_profile.xml new file mode 100644 index 0000000..ad75626 --- /dev/null +++ b/app/src/main/res/layout/fragment_profile.xml @@ -0,0 +1,10 @@ + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_review_details_registration.xml b/app/src/main/res/layout/fragment_review_details_registration.xml new file mode 100644 index 0000000..f796a51 --- /dev/null +++ b/app/src/main/res/layout/fragment_review_details_registration.xml @@ -0,0 +1,301 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_user_details_registration.xml b/app/src/main/res/layout/fragment_user_details_registration.xml new file mode 100644 index 0000000..7d74bb6 --- /dev/null +++ b/app/src/main/res/layout/fragment_user_details_registration.xml @@ -0,0 +1,374 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_user_list.xml b/app/src/main/res/layout/fragment_user_list.xml new file mode 100644 index 0000000..41fc25c --- /dev/null +++ b/app/src/main/res/layout/fragment_user_list.xml @@ -0,0 +1,67 @@ + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/item_error_message.xml b/app/src/main/res/layout/item_error_message.xml new file mode 100644 index 0000000..d5f0e9b --- /dev/null +++ b/app/src/main/res/layout/item_error_message.xml @@ -0,0 +1,44 @@ + + + + + + + +