diff --git a/.gitignore b/.gitignore
new file mode 100644
index 000000000..1f6132240
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,187 @@
+# Created by https://www.toptal.com/developers/gitignore/api/android,androidstudio,kotlin
+# Edit at https://www.toptal.com/developers/gitignore?templates=android,androidstudio,kotlin
+
+### Android ###
+# Gradle files
+.gradle/
+build/
+
+# Local configuration file (sdk path, etc)
+local.properties
+
+# Log/OS Files
+*.log
+
+# Android Studio generated files and folders
+captures/
+.externalNativeBuild/
+.cxx/
+*.apk
+output.json
+
+# IntelliJ
+*.iml
+.idea/
+misc.xml
+deploymentTargetDropDown.xml
+render.experimental.xml
+
+# Keystore files
+*.jks
+*.keystore
+
+# Google Services (e.g. APIs or Firebase)
+google-services.json
+
+# Android Profiling
+*.hprof
+
+### Android Patch ###
+gen-external-apklibs
+
+# Replacement of .externalNativeBuild directories introduced
+# with Android Studio 3.5.
+
+### Kotlin ###
+# Compiled class file
+*.class
+
+# Log file
+
+# BlueJ files
+*.ctxt
+
+# Mobile Tools for Java (J2ME)
+.mtj.tmp/
+
+# Package Files #
+*.jar
+*.war
+*.nar
+*.ear
+*.zip
+*.tar.gz
+*.rar
+
+# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml
+hs_err_pid*
+replay_pid*
+
+### AndroidStudio ###
+# Covers files to be ignored for android development using Android Studio.
+
+# Built application files
+*.ap_
+*.aab
+
+# Files for the ART/Dalvik VM
+*.dex
+
+# Java class files
+
+# Generated files
+bin/
+gen/
+out/
+
+# Gradle files
+.gradle
+
+# Signing files
+.signing/
+
+# Local configuration file (sdk path, etc)
+
+# Proguard folder generated by Eclipse
+proguard/
+
+# Log Files
+
+# Android Studio
+/*/build/
+/*/local.properties
+/*/out
+/*/*/build
+/*/*/production
+.navigation/
+*.ipr
+*~
+*.swp
+
+# Keystore files
+
+# Google Services (e.g. APIs or Firebase)
+# google-services.json
+
+# Android Patch
+
+# External native build folder generated in Android Studio 2.2 and later
+.externalNativeBuild
+
+# NDK
+obj/
+
+# IntelliJ IDEA
+*.iws
+/out/
+
+# User-specific configurations
+.idea/caches/
+.idea/libraries/
+.idea/shelf/
+.idea/workspace.xml
+.idea/tasks.xml
+.idea/.name
+.idea/compiler.xml
+.idea/copyright/profiles_settings.xml
+.idea/encodings.xml
+.idea/misc.xml
+.idea/modules.xml
+.idea/scopes/scope_settings.xml
+.idea/dictionaries
+.idea/vcs.xml
+.idea/jsLibraryMappings.xml
+.idea/datasources.xml
+.idea/dataSources.ids
+.idea/sqlDataSources.xml
+.idea/dynamic.xml
+.idea/uiDesigner.xml
+.idea/assetWizardSettings.xml
+.idea/gradle.xml
+.idea/jarRepositories.xml
+.idea/navEditor.xml
+
+# Legacy Eclipse project files
+.classpath
+.project
+.cproject
+.settings/
+
+# Mobile Tools for Java (J2ME)
+
+# Package Files #
+
+# virtual machine crash logs (Reference: http://www.java.com/en/download/help/error_hotspot.xml)
+
+## Plugin-specific files:
+
+# mpeltonen/sbt-idea plugin
+.idea_modules/
+
+# JIRA plugin
+atlassian-ide-plugin.xml
+
+# Mongo Explorer plugin
+.idea/mongoSettings.xml
+
+# Crashlytics plugin (for Android Studio and IntelliJ)
+com_crashlytics_export_strings.xml
+crashlytics.properties
+crashlytics-build.properties
+fabric.properties
+
+### AndroidStudio Patch ###
+
+!/gradle/wrapper/gradle-wrapper.jar
+
+# End of https://www.toptal.com/developers/gitignore/api/android,androidstudio,kotlin
\ No newline at end of file
diff --git a/app/.gitignore b/app/.gitignore
new file mode 100644
index 000000000..42afabfd2
--- /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 000000000..f39e13760
--- /dev/null
+++ b/app/build.gradle
@@ -0,0 +1,176 @@
+plugins {
+ id 'com.android.application'
+ id 'org.jetbrains.kotlin.android'
+ id 'dagger.hilt.android.plugin'
+ id 'org.jlleitschuh.gradle.ktlint'
+}
+
+apply plugin: 'kotlin-kapt'
+
+android {
+ namespace 'com.javg.cryptocurrencies'
+ compileSdk 33
+
+ defaultConfig {
+ applicationId "com.javg.cryptocurrencies"
+ minSdk 23
+ targetSdk 33
+ versionCode 1
+ versionName "1.0"
+
+ testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
+ vectorDrawables {
+ useSupportLibrary true
+ }
+ }
+
+ buildTypes {
+ release {
+ minifyEnabled false
+ proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
+ }
+ }
+ compileOptions {
+ sourceCompatibility JavaVersion.VERSION_1_8
+ targetCompatibility JavaVersion.VERSION_1_8
+ }
+ kotlinOptions {
+ jvmTarget = '1.8'
+ }
+ buildFeatures {
+ compose true
+ }
+ composeOptions {
+ kotlinCompilerExtensionVersion '1.2.0'
+ }
+ packagingOptions {
+ resources {
+ excludes += '/META-INF/{AL2.0,LGPL2.1}'
+ }
+ }
+ viewBinding {
+ enabled = true
+ }
+}
+
+dependencies {
+
+ implementation 'androidx.core:core-ktx:1.7.0'
+ implementation 'androidx.lifecycle:lifecycle-runtime-ktx:2.3.1'
+ implementation 'androidx.activity:activity-compose:1.3.1'
+ implementation "androidx.compose.ui:ui:$compose_ui_version"
+ implementation "androidx.compose.ui:ui-tooling-preview:$compose_ui_version"
+ implementation 'androidx.compose.material:material:1.2.0'
+ implementation 'androidx.databinding:databinding-runtime:7.1.2'
+ testImplementation 'junit:junit:4.12'
+ debugImplementation "androidx.compose.ui:ui-tooling:$compose_ui_version"
+ debugImplementation "androidx.compose.ui:ui-test-manifest:$compose_ui_version"
+
+ //testing
+ testImplementation 'junit:junit:4.13.2'
+ testImplementation 'io.mockk:mockk:1.11.0'
+ testImplementation 'androidx.test:core-ktx:1.4.0'
+ testImplementation 'androidx.test.ext:junit:1.1.3'
+ testImplementation 'com.google.truth:truth:1.1.3'
+ testImplementation 'androidx.test.espresso:espresso-core:3.5.1'
+ testImplementation 'org.robolectric:robolectric:4.8.1'
+ androidTestImplementation 'junit:junit:4.12'
+ androidTestImplementation "androidx.compose.ui:ui-test-junit4:$compose_ui_version"
+
+ //Retrofit
+ implementation "com.squareup.retrofit2:retrofit:$retrofit_version"
+ implementation "com.squareup.retrofit2:converter-gson:$retrofit_version"
+ implementation "com.squareup.okhttp3:logging-interceptor:$interceptor_version"
+ implementation "com.squareup.retrofit2:adapter-rxjava:$retrofit_version"
+
+ //Dagger HILT
+ implementation "com.google.dagger:hilt-android:$hilt_version"
+ kapt "com.google.dagger:hilt-compiler:$hilt_version"
+ kapt "androidx.hilt:hilt-compiler:$hilt_compiler_version"
+
+ // Lifecycle components
+ implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:$lifecycle_version"
+ implementation "androidx.lifecycle:lifecycle-viewmodel-compose:$lifecycle_version"
+ implementation "androidx.lifecycle:lifecycle-livedata-ktx:$lifecycle_version"
+ implementation "androidx.lifecycle:lifecycle-common-java8:$lifecycle_version"
+
+ //livedata jetpack compose
+ implementation "androidx.compose.runtime:runtime-livedata:$runtime_livedata_version"
+
+ //constraintlayout
+ implementation "androidx.constraintlayout:constraintlayout:$constraintlayout_version"
+ implementation "androidx.constraintlayout:constraintlayout-compose:$constraintlayout_compose_version"
+
+ //recycler view
+ implementation "androidx.recyclerview:recyclerview:$recyclerview_version"
+ implementation "androidx.recyclerview:recyclerview-selection:$recyclerview_selection_version"
+
+ //activityviewModels
+ implementation "androidx.fragment:fragment-ktx:$fragment_ktx_version"
+
+ // Kotlin
+ implementation "androidx.fragment:fragment:$fragment_version"
+ implementation "androidx.fragment:fragment-ktx:$fragment_version"
+ implementation "androidx.appcompat:appcompat:$appcompat_version"
+ implementation "androidx.legacy:legacy-support-v4:$legacy_support_version"
+ implementation "androidx.activity:activity-ktx:$activity_ktx_version"
+
+ //card
+ implementation "androidx.cardview:cardview:$cardview_version"
+
+ //
+ implementation "com.facebook.shimmer:shimmer:0.5.0"
+
+ //glide
+ implementation "com.github.bumptech.glide:glide:$glide_version"
+
+ //lottie
+ implementation "com.airbnb.android:lottie:$lottie_version"
+
+ //room
+ implementation "androidx.room:room-runtime:$room_version"
+ annotationProcessor "androidx.room:room-compiler:$room_version"
+
+ // To use Kotlin annotation processing tool (kapt)
+ kapt "androidx.room:room-compiler:$room_version"
+ // To use Kotlin Symbol Processing (KSP)
+ //ksp("androidx.room:room-compiler:$room_version")
+
+ // optional - Kotlin Extensions and Coroutines support for Room
+ implementation "androidx.room:room-ktx:$room_version"
+
+ // optional - RxJava2 support for Room
+ implementation "androidx.room:room-rxjava2:$room_version"
+
+ // optional - RxJava3 support for Room
+ implementation "androidx.room:room-rxjava3:$room_version"
+
+ // optional - Guava support for Room, including Optional and ListenableFuture
+ implementation "androidx.room:room-guava:$room_version"
+
+ // optional - Test helpers
+ testImplementation "androidx.room:room-testing:$room_version"
+
+ // optional - Paging 3 Integration
+ implementation "androidx.room:room-paging:$room_version"
+
+ implementation "com.google.android.material:material:$googleMaterialVersion"
+
+ //viewPager2
+ implementation "androidx.viewpager2:viewpager2:$viewpager2_version"
+
+ //navigation
+ implementation "androidx.navigation:navigation-fragment-ktx:$navigation_fragment_version"
+ implementation "androidx.navigation:navigation-ui-ktx:$navigation_fragment_version"
+
+ //rxjava
+ implementation "io.reactivex.rxjava2:rxjava:$rxjava2_java_version"
+ implementation "io.reactivex.rxjava2:rxandroid:$rxjava2_android_version"
+ implementation "io.reactivex:rxandroid:$rx_android_version"
+ implementation "io.reactivex:rxjava:$rx_java_version"
+}
+
+ktlint {
+ android = true
+ disabledRules = ["no-wildcard-imports"]
+}
\ No newline at end of file
diff --git a/app/proguard-rules.pro b/app/proguard-rules.pro
new file mode 100644
index 000000000..481bb4348
--- /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/javg/cryptocurrencies/ExampleInstrumentedTest.kt b/app/src/androidTest/java/com/javg/cryptocurrencies/ExampleInstrumentedTest.kt
new file mode 100644
index 000000000..289404abd
--- /dev/null
+++ b/app/src/androidTest/java/com/javg/cryptocurrencies/ExampleInstrumentedTest.kt
@@ -0,0 +1,24 @@
+package com.javg.cryptocurrencies
+
+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.javg.cryptocurrencies", 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 000000000..05a599edd
--- /dev/null
+++ b/app/src/main/AndroidManifest.xml
@@ -0,0 +1,33 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/assets/empty_data.json b/app/src/main/assets/empty_data.json
new file mode 100644
index 000000000..13a75943f
--- /dev/null
+++ b/app/src/main/assets/empty_data.json
@@ -0,0 +1 @@
+{"v":"5.5.9","fr":60,"ip":0,"op":295,"w":426,"h":290,"nm":"search_empty","ddd":0,"assets":[{"id":"comp_0","layers":[{"ddd":0,"ind":1,"ty":4,"nm":"mouth","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":44,"s":[211,145,0],"to":[0,0.667,0],"ti":[0,-0.667,0]},{"t":69,"s":[211,149,0]}],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":1,"k":[{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,0.833]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0.167]},"t":44,"s":[100,100,100]},{"t":68,"s":[105,105,100]}],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0.5,0.25],[-1.715,0.372]],"o":[[-13.025,-6.512],[7.5,-1.625]],"v":[[-10,12.25],[-31.75,12.625]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.454901960784,0.513725490196,0.580392156863,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Shape 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":3600,"st":0,"bm":0},{"ddd":0,"ind":2,"ty":4,"nm":"汗","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":51,"s":[0]},{"t":95,"s":[100]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":51,"s":[248,95.252,0],"to":[0,1.5,0],"ti":[0,-1.5,0]},{"t":94,"s":[248,104.252,0]}],"ix":2},"a":{"a":0,"k":[0,-19.333,0],"ix":1},"s":{"a":1,"k":[{"i":{"x":[0.667,0.667,0.667],"y":[1,1,1]},"o":{"x":[0.333,0.333,0.333],"y":[0,0,0]},"t":51,"s":[40,40,100]},{"t":95,"s":[100,100,100]}],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-9.94,0],[0,7.27],[0,0],[0,-7.27]],"o":[[9.94,0],[0,-7.27],[0,0],[0,7.27]],"v":[[0,19],[18,5.84],[0,-19],[-18,5.84]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"gf","o":{"a":0,"k":100,"ix":10},"r":1,"bm":0,"g":{"p":3,"k":{"a":0,"k":[0,0.69,0.976,1,0.5,0.714,0.954,0.997,1,0.738,0.932,0.994],"ix":9}},"s":{"a":0,"k":[0.019,-9.236],"ix":5},"e":{"a":0,"k":[10.208,19],"ix":6},"t":1,"nm":"Gradient Fill 1","mn":"ADBE Vector Graphic - G-Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"汗","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":569,"st":0,"bm":0},{"ddd":0,"ind":3,"ty":4,"nm":"eye","parent":1,"sr":1,"ks":{"o":{"a":0,"k":92,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[-7.064,-11.347,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":1,"k":[{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,0.833]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0.167]},"t":0,"s":[100,100,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,0.833]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0.167]},"t":3,"s":[100,20,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,0.833]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0.167]},"t":6,"s":[100,100,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,0.833]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0.167]},"t":9,"s":[100,20,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,0.833]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0.167]},"t":12,"s":[100,100,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,0.833]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0.167]},"t":50,"s":[100,100,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,0.833]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0.167]},"t":53,"s":[100,20,100]},{"t":56,"s":[100,100,100]}],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[8.466,16.135],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"fl","c":{"a":0,"k":[0,0,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"eye","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":569,"st":0,"bm":0},{"ddd":0,"ind":4,"ty":4,"nm":"eye","parent":1,"sr":1,"ks":{"o":{"a":0,"k":92,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[-33.767,-11.347,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":1,"k":[{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,0.833]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0.167]},"t":0,"s":[100,100,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,0.833]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0.167]},"t":3,"s":[100,20,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,0.833]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0.167]},"t":6,"s":[100,100,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,0.833]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0.167]},"t":9,"s":[100,20,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,0.833]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0.167]},"t":12,"s":[100,100,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,0.833]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0.167]},"t":50,"s":[100,100,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,0.833]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0.167]},"t":53,"s":[100,20,100]},{"t":56,"s":[100,100,100]}],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[8.466,16.135],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"fl","c":{"a":0,"k":[0,0,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"eye","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":569,"st":0,"bm":0},{"ddd":0,"ind":5,"ty":0,"nm":"magnifier","refId":"comp_1","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[213,145,0],"ix":2},"a":{"a":0,"k":[213,145,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"w":426,"h":290,"ip":0,"op":569,"st":0,"bm":0}]},{"id":"comp_1","layers":[{"ddd":0,"ind":1,"ty":4,"nm":"Shape Layer 1","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[213,145,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"st","c":{"a":0,"k":[0.435294117647,0.498039215686,0.564705882353,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Shape 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":3600,"st":0,"bm":0},{"ddd":0,"ind":3,"ty":4,"nm":"橢圓形","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":-5,"ix":10},"p":{"a":0,"k":[190.106,142.222,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[99.994,100.091],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"fl","c":{"a":0,"k":[1,1,1,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":-40,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"橢圓形","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":3600,"st":0,"bm":0},{"ddd":0,"ind":4,"ty":4,"nm":"橢圓形","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":-5,"ix":10},"p":{"a":0,"k":[192.013,142.223,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-26.81,22.62],[22.49,26.96],[26.8,-22.62],[-22.5,-26.95]],"o":[[26.81,-22.62],[-22.49,-26.96],[-26.81,22.62],[22.49,26.96]],"v":[[40.727,48.812],[48.537,-40.958],[-40.723,-48.808],[-48.533,40.952]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.6392156862745098,0.7490196078431373,0.7764705882352941,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"橢圓形","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":3600,"st":0,"bm":0},{"ddd":0,"ind":5,"ty":4,"nm":"矩形","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":-5,"ix":10},"p":{"a":0,"k":[268.024,226.941,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[42.094,74.507],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":10,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.6392156862745098,0.7490196078431373,0.7764705882352941,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":-40,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"矩形","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":3600,"st":0,"bm":0},{"ddd":0,"ind":6,"ty":4,"nm":"矩形","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":-5,"ix":10},"p":{"a":0,"k":[238.933,197.25,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[24.446,44.702],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":0,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.8745098039215686,0.8980392156862745,0.9333333333333333,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":-40,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"矩形","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":3600,"st":0,"bm":0}]},{"id":"comp_2","layers":[{"ddd":0,"ind":1,"ty":4,"nm":"合併形狀","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":60,"s":[0]},{"t":265,"s":[360]}],"ix":10},"p":{"a":0,"k":[208,27,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[16,2],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":0,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"rc","d":1,"s":{"a":0,"k":[2,18],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":0,"ix":4},"nm":"Rectangle Path 2","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"mm","mm":2,"nm":"Merge Paths 1","mn":"ADBE Vector Filter - Merge","hd":false},{"ty":"fl","c":{"a":0,"k":[0.792156862745098,0.8313725490196079,0.8392156862745098,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"合併形狀","np":4,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":3600,"st":0,"bm":0},{"ddd":0,"ind":2,"ty":4,"nm":"合併形狀","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":5,"s":[0]},{"t":358,"s":[360]}],"ix":10},"p":{"a":0,"k":[418,107,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[16,2],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":0,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"rc","d":1,"s":{"a":0,"k":[2,18],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":0,"ix":4},"nm":"Rectangle Path 2","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"mm","mm":2,"nm":"Merge Paths 1","mn":"ADBE Vector Filter - Merge","hd":false},{"ty":"fl","c":{"a":0,"k":[0.796078026295,0.835294008255,0.843137025833,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"合併形狀","np":4,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":3600,"st":0,"bm":0},{"ddd":0,"ind":3,"ty":4,"nm":"合併形狀","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":5,"s":[0]},{"t":281,"s":[360]}],"ix":10},"p":{"a":0,"k":[31,196,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[16,2],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":0,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"rc","d":1,"s":{"a":0,"k":[2,18],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":0,"ix":4},"nm":"Rectangle Path 2","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"mm","mm":2,"nm":"Merge Paths 1","mn":"ADBE Vector Filter - Merge","hd":false},{"ty":"fl","c":{"a":0,"k":[0.796078026295,0.835294008255,0.843137025833,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"合併形狀","np":4,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":3600,"st":0,"bm":0},{"ddd":0,"ind":4,"ty":4,"nm":"橢圓形","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":0,"s":[0]},{"t":281,"s":[360]}],"ix":10},"p":{"a":0,"k":[70,33,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[24,24],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"st","c":{"a":0,"k":[0.701960980892,0.756862998009,0.776471018791,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"橢圓形","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":3600,"st":0,"bm":0}]},{"id":"comp_3","layers":[{"ddd":0,"ind":1,"ty":4,"nm":"橢圓形","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[306,9,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[16,16],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"st","c":{"a":0,"k":[0.701960980892,0.756862998009,0.776471018791,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"橢圓形","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":3600,"st":0,"bm":0},{"ddd":0,"ind":2,"ty":4,"nm":"橢圓形","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[376,179,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[16,16],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"fl","c":{"a":0,"k":[0.701960980892,0.768626987934,0.776471018791,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"橢圓形","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":3600,"st":0,"bm":0},{"ddd":0,"ind":3,"ty":4,"nm":"橢圓形","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[8,115,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[16,16],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"fl","c":{"a":0,"k":[0.701960980892,0.768626987934,0.776471018791,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"橢圓形","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":3600,"st":0,"bm":0}]}],"layers":[{"ddd":0,"ind":1,"ty":4,"nm":"shadow","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[191,145,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":1,"k":[{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,0.833]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0.167]},"t":0,"s":[70,100,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,0.833]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0.167]},"t":30,"s":[50,100,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,0.833]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0.167]},"t":60,"s":[70,100,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,0.833]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0.167]},"t":90,"s":[50,100,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,0.833]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0.167]},"t":120,"s":[70,100,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,0.833]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0.167]},"t":150,"s":[50,100,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,0.833]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0.167]},"t":180,"s":[70,100,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,0.833]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0.167]},"t":210,"s":[50,100,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,0.833]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0.167]},"t":240,"s":[70,100,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,0.833]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0.167]},"t":270,"s":[50,100,100]},{"t":295,"s":[70,100,100]}],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[56.697,6.631],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"fl","c":{"a":0,"k":[0,0.2901960784313726,0.5098039215686274,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[-9.651,124.815],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Ellipse 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":300,"st":0,"bm":0},{"ddd":0,"ind":2,"ty":0,"nm":"main","refId":"comp_0","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":0,"s":[213,145,0],"to":[0,0.833,0],"ti":[0,0,0]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":30,"s":[213,150,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":60,"s":[213,145,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":90,"s":[213,150,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":120,"s":[213,145,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":150,"s":[213,150,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":180,"s":[213,145,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":210,"s":[213,150,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":240,"s":[213,145,0],"to":[0,0,0],"ti":[0,-0.833,0]},{"t":270,"s":[213,150,0]}],"ix":2},"a":{"a":0,"k":[213,145,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"w":426,"h":290,"ip":0,"op":3600,"st":0,"bm":0},{"ddd":0,"ind":3,"ty":0,"nm":"decoration1","refId":"comp_2","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.981],"y":[1.128]},"o":{"x":[0.39],"y":[0.019]},"t":8,"s":[0]},{"i":{"x":[0.833],"y":[1]},"o":{"x":[0.167],"y":[0]},"t":40,"s":[100]},{"i":{"x":[0.833],"y":[1]},"o":{"x":[0.167],"y":[0]},"t":72,"s":[80]},{"t":73,"s":[100]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[213,289,0],"ix":2},"a":{"a":0,"k":[213,289,0],"ix":1},"s":{"a":1,"k":[{"i":{"x":[0.833,0.833,0.667],"y":[0.866,0.866,1]},"o":{"x":[0.37,0.37,0.333],"y":[-0.029,-0.029,0]},"t":0,"s":[0,0,100]},{"i":{"x":[0.833,0.833,0.833],"y":[1,1,1]},"o":{"x":[0.333,0.333,0.333],"y":[0,0,0]},"t":40,"s":[100,100,100]},{"i":{"x":[0.833,0.833,0.833],"y":[1,1,1]},"o":{"x":[0.167,0.167,0.167],"y":[0,0,0]},"t":72,"s":[80,80,100]},{"t":133,"s":[90,90,100]}],"ix":6}},"ao":0,"w":426,"h":290,"ip":0,"op":569,"st":0,"bm":0},{"ddd":0,"ind":4,"ty":0,"nm":"decoratio2","refId":"comp_3","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":0,"s":[0]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":41,"s":[100]},{"t":72,"s":[100]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[211,291,0],"ix":2},"a":{"a":0,"k":[211,291,0],"ix":1},"s":{"a":1,"k":[{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,0.833]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0.167]},"t":0,"s":[0,0,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,0.833]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0.167]},"t":43,"s":[99.442,99.442,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,0.833]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0.167]},"t":72,"s":[84.442,84.442,100]},{"t":133,"s":[90,90,100]}],"ix":6}},"ao":0,"w":426,"h":290,"ip":0,"op":569,"st":0,"bm":0}],"markers":[{"tm":180,"cm":"1","dr":0},{"tm":3600,"cm":"2","dr":0}]}
\ No newline at end of file
diff --git a/app/src/main/ic_launcher-playstore.png b/app/src/main/ic_launcher-playstore.png
new file mode 100644
index 000000000..f2e45dcaf
Binary files /dev/null and b/app/src/main/ic_launcher-playstore.png differ
diff --git a/app/src/main/java/com/javg/cryptocurrencies/CRYSplashActivity.kt b/app/src/main/java/com/javg/cryptocurrencies/CRYSplashActivity.kt
new file mode 100644
index 000000000..ce1f912ec
--- /dev/null
+++ b/app/src/main/java/com/javg/cryptocurrencies/CRYSplashActivity.kt
@@ -0,0 +1,16 @@
+package com.javg.cryptocurrencies
+
+import android.annotation.SuppressLint
+import android.content.Intent
+import android.os.Bundle
+import androidx.appcompat.app.AppCompatActivity
+
+@SuppressLint("CustomSplashScreen")
+class CRYSplashActivity : AppCompatActivity() {
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ startActivity(Intent(this, MainActivity::class.java))
+ finish()
+ }
+}
diff --git a/app/src/main/java/com/javg/cryptocurrencies/MainActivity.kt b/app/src/main/java/com/javg/cryptocurrencies/MainActivity.kt
new file mode 100644
index 000000000..1b5cb0fb9
--- /dev/null
+++ b/app/src/main/java/com/javg/cryptocurrencies/MainActivity.kt
@@ -0,0 +1,39 @@
+package com.javg.cryptocurrencies
+
+import android.os.Bundle
+import androidx.navigation.NavController
+import androidx.navigation.NavGraph
+import androidx.navigation.fragment.NavHostFragment
+import com.javg.cryptocurrencies.view.base.CRYBaseActivity
+import dagger.hilt.android.AndroidEntryPoint
+
+/**
+ * @author Juan Antonio Vera
+ * Date modified 22/02/2023
+ *
+ * Contains the functionality and configuration to start
+ * the application and its navigation
+ *
+ * @since 2.0
+ */
+@AndroidEntryPoint
+class MainActivity : CRYBaseActivity() {
+
+ private lateinit var navController: NavController
+ private lateinit var navHostFragment: NavHostFragment
+ private lateinit var navGraph: NavGraph
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ setTheme(R.style.Theme_Cryptocurrencies)
+ super.onCreate(savedInstanceState)
+ setContentView(R.layout.cry_nav_host_main)
+ setUpNavHost()
+ }
+
+ private fun setUpNavHost() {
+ navHostFragment = supportFragmentManager.findFragmentById(R.id.nav_host_main) as NavHostFragment
+ navGraph = navHostFragment.navController.navInflater.inflate(R.navigation.cry_nav_graph_main)
+ navController = navHostFragment.navController
+ navController.graph = navGraph
+ }
+}
diff --git a/app/src/main/java/com/javg/cryptocurrencies/config/CRYApplication.kt b/app/src/main/java/com/javg/cryptocurrencies/config/CRYApplication.kt
new file mode 100644
index 000000000..7b38c9112
--- /dev/null
+++ b/app/src/main/java/com/javg/cryptocurrencies/config/CRYApplication.kt
@@ -0,0 +1,7 @@
+package com.javg.cryptocurrencies.config
+
+import android.app.Application
+import dagger.hilt.android.HiltAndroidApp
+
+@HiltAndroidApp
+class CRYApplication : Application()
diff --git a/app/src/main/java/com/javg/cryptocurrencies/config/CRYDispatcherModule.kt b/app/src/main/java/com/javg/cryptocurrencies/config/CRYDispatcherModule.kt
new file mode 100644
index 000000000..535bcfcd2
--- /dev/null
+++ b/app/src/main/java/com/javg/cryptocurrencies/config/CRYDispatcherModule.kt
@@ -0,0 +1,20 @@
+package com.javg.cryptocurrencies.config
+
+import dagger.Module
+import dagger.Provides
+import dagger.hilt.InstallIn
+import dagger.hilt.components.SingletonComponent
+import kotlinx.coroutines.Dispatchers
+import javax.inject.Singleton
+
+@Module
+@InstallIn(SingletonComponent::class)
+object CRYDispatcherModule {
+
+ /**
+ * Provides the trigger where the task will be executed
+ */
+ @Provides
+ @Singleton
+ fun providerDispatcher() = Dispatchers.IO
+}
diff --git a/app/src/main/java/com/javg/cryptocurrencies/config/CRYRetrofitHelper.kt b/app/src/main/java/com/javg/cryptocurrencies/config/CRYRetrofitHelper.kt
new file mode 100644
index 000000000..4aff5af00
--- /dev/null
+++ b/app/src/main/java/com/javg/cryptocurrencies/config/CRYRetrofitHelper.kt
@@ -0,0 +1,70 @@
+package com.javg.cryptocurrencies.config
+
+import com.javg.cryptocurrencies.data.network.CRYApi
+import dagger.Module
+import dagger.Provides
+import dagger.hilt.InstallIn
+import dagger.hilt.components.SingletonComponent
+import okhttp3.OkHttpClient
+import okhttp3.logging.HttpLoggingInterceptor
+import retrofit2.Retrofit
+import retrofit2.adapter.rxjava.RxJavaCallAdapterFactory
+import retrofit2.converter.gson.GsonConverterFactory
+import javax.inject.Singleton
+
+@Module
+@InstallIn(SingletonComponent::class)
+object CRYRetrofitHelper {
+ /**
+ * Takes care of providing an instance of the api interface using a retrofit construct
+ */
+ @Provides
+ @Singleton
+ fun providerApi(builder: Retrofit.Builder): CRYApi {
+ return builder.build().create(CRYApi::class.java)
+ }
+
+ /**
+ * Provide a retrofit instance already with your build
+ *
+ * @param httpClient is a builder type model
+ */
+ @Provides
+ @Singleton
+ fun providerRetrofit(httpClient: OkHttpClient.Builder): Retrofit.Builder {
+ return Retrofit.Builder()
+ .baseUrl(CRYApi.BASE_URL)
+ .addConverterFactory(GsonConverterFactory.create())
+ .addCallAdapterFactory(RxJavaCallAdapterFactory.create())
+ .client(httpClient.build())
+ }
+
+ /**
+ * It is responsible for providing an instance of an httpClient with
+ * its configuration to add a header and enable the body of the requests
+ *
+ * @param logging It is a model that contains the configuration to see
+ * the body of the requests
+ */
+ @Provides
+ @Singleton
+ fun providerHttpClient(logging: HttpLoggingInterceptor): OkHttpClient.Builder {
+ return OkHttpClient.Builder()
+ .addInterceptor { chain ->
+ val request = chain.request()
+ .newBuilder()
+ .addHeader("User-Agent", "CryptocurrenciesApp")
+ .build()
+ chain.proceed(request)
+ }.addInterceptor(logging)
+ }
+
+ /**
+ * Provides the instance of an interceptor
+ */
+ @Provides
+ @Singleton
+ fun providerHttpLoggingInterceptor(): HttpLoggingInterceptor = HttpLoggingInterceptor().apply {
+ setLevel(HttpLoggingInterceptor.Level.BODY)
+ }
+}
diff --git a/app/src/main/java/com/javg/cryptocurrencies/config/CRYRoomHelper.kt b/app/src/main/java/com/javg/cryptocurrencies/config/CRYRoomHelper.kt
new file mode 100644
index 000000000..8dce16ac7
--- /dev/null
+++ b/app/src/main/java/com/javg/cryptocurrencies/config/CRYRoomHelper.kt
@@ -0,0 +1,50 @@
+package com.javg.cryptocurrencies.config
+
+import android.content.Context
+import androidx.room.Room
+import com.javg.cryptocurrencies.data.db.remote.CRYAppDatabase
+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 CRYRoomHelper {
+ /**
+ * It is responsible for providing an instance of the room
+ * database with the necessary configuration
+ *
+ * @param appContext is the application context
+ */
+ @Singleton
+ @Provides
+ fun provideAppDatabase(@ApplicationContext appContext: Context): CRYAppDatabase {
+ return Room
+ .databaseBuilder(
+ appContext,
+ CRYAppDatabase::class.java,
+ CRYAppDatabase.DB_NAME,
+ )
+ .fallbackToDestructiveMigration()
+ .build()
+ }
+
+ /**
+ * Provides an instance of the interface that controls the books
+ * table with their respective queries
+ */
+ @Singleton
+ @Provides
+ fun provideBookDao(db: CRYAppDatabase) = db.bookDao()
+
+ /**
+ * Provides an instance of the interface that controls the detail
+ * table of a specific book with its respective queries.
+ */
+ @Singleton
+ @Provides
+ fun provideTickerDao(db: CRYAppDatabase) = db.tickerDao()
+}
diff --git a/app/src/main/java/com/javg/cryptocurrencies/data/db/dao/CRYBookDao.kt b/app/src/main/java/com/javg/cryptocurrencies/data/db/dao/CRYBookDao.kt
new file mode 100644
index 000000000..0627fd7b7
--- /dev/null
+++ b/app/src/main/java/com/javg/cryptocurrencies/data/db/dao/CRYBookDao.kt
@@ -0,0 +1,37 @@
+package com.javg.cryptocurrencies.data.db.dao
+
+import androidx.room.Dao
+import androidx.room.Insert
+import androidx.room.OnConflictStrategy
+import androidx.room.Query
+import com.javg.cryptocurrencies.data.db.entity.CRYBookEntity
+import com.javg.cryptocurrencies.data.db.remote.CRYAppDatabase
+import kotlinx.coroutines.flow.Flow
+
+/**
+ * @author Juan Vera Gomez
+ * Date modified 13/02/2023
+ *
+ * Contains the functions for the crud database of books
+ *
+ * @since 2.0
+ */
+@Dao
+interface CRYBookDao {
+
+ /**
+ * Returns a list of books from the books table saved in the database
+ */
+ @Query("SELECT * FROM ${CRYAppDatabase.BOOK_TABLE}")
+ fun getAllBook(): List
+
+ @Query("SELECT * FROM ${CRYAppDatabase.BOOK_TABLE}")
+ fun getAllBookV2(): Flow>
+
+ /**
+ * It is in charge of saving the entire list of books obtained remotely from the api
+ */
+ @Insert(onConflict = OnConflictStrategy.REPLACE)
+ @JvmSuppressWildcards
+ fun insertAll(books: List)
+}
diff --git a/app/src/main/java/com/javg/cryptocurrencies/data/db/dao/CRYTickerDao.kt b/app/src/main/java/com/javg/cryptocurrencies/data/db/dao/CRYTickerDao.kt
new file mode 100644
index 000000000..51216b9a0
--- /dev/null
+++ b/app/src/main/java/com/javg/cryptocurrencies/data/db/dao/CRYTickerDao.kt
@@ -0,0 +1,20 @@
+package com.javg.cryptocurrencies.data.db.dao
+
+import androidx.room.Dao
+import androidx.room.Insert
+import androidx.room.OnConflictStrategy
+import androidx.room.Query
+import com.javg.cryptocurrencies.data.db.entity.CRYDetailBookEntity
+import com.javg.cryptocurrencies.data.db.remote.CRYAppDatabase
+
+@Dao
+interface CRYTickerDao {
+ @Query("SELECT * FROM ${CRYAppDatabase.DETAIL_BOOK_TABLE} WHERE book = :book")
+ fun findById(book: String): CRYDetailBookEntity?
+
+ @Insert(onConflict = OnConflictStrategy.IGNORE)
+ fun insert(ticker: CRYDetailBookEntity)
+
+ @Query("UPDATE ${CRYAppDatabase.DETAIL_BOOK_TABLE} SET askList = :ask, bidsList = :bids WHERE book = :book")
+ fun update(ask: String, bids: String, book: String)
+}
diff --git a/app/src/main/java/com/javg/cryptocurrencies/data/db/entity/CRYEntity.kt b/app/src/main/java/com/javg/cryptocurrencies/data/db/entity/CRYEntity.kt
new file mode 100644
index 000000000..1ab4c30ae
--- /dev/null
+++ b/app/src/main/java/com/javg/cryptocurrencies/data/db/entity/CRYEntity.kt
@@ -0,0 +1,27 @@
+package com.javg.cryptocurrencies.data.db.entity
+
+import androidx.room.ColumnInfo
+import androidx.room.Entity
+import androidx.room.PrimaryKey
+import com.javg.cryptocurrencies.data.db.remote.CRYAppDatabase
+
+@Entity(tableName = CRYAppDatabase.BOOK_TABLE)
+data class CRYBookEntity(
+ @PrimaryKey
+ @ColumnInfo(name = "book")
+ val book: String,
+ @ColumnInfo(name = "minimumPrice") val minimumPrice: String,
+ @ColumnInfo(name = "maximumPrice") val maximumPrice: String,
+)
+
+@Entity(tableName = CRYAppDatabase.DETAIL_BOOK_TABLE)
+data class CRYDetailBookEntity(
+ @PrimaryKey
+ @ColumnInfo("book")
+ val book: String,
+ @ColumnInfo("high") val high: String,
+ @ColumnInfo("last") val last: String,
+ @ColumnInfo("low") val low: String,
+ @ColumnInfo("askList") val askList: String,
+ @ColumnInfo("bidsList") val bidsList: String,
+)
diff --git a/app/src/main/java/com/javg/cryptocurrencies/data/db/remote/CRYAppDatabase.kt b/app/src/main/java/com/javg/cryptocurrencies/data/db/remote/CRYAppDatabase.kt
new file mode 100644
index 000000000..0e41dc12a
--- /dev/null
+++ b/app/src/main/java/com/javg/cryptocurrencies/data/db/remote/CRYAppDatabase.kt
@@ -0,0 +1,20 @@
+package com.javg.cryptocurrencies.data.db.remote
+
+import androidx.room.Database
+import androidx.room.RoomDatabase
+import com.javg.cryptocurrencies.data.db.dao.CRYBookDao
+import com.javg.cryptocurrencies.data.db.dao.CRYTickerDao
+import com.javg.cryptocurrencies.data.db.entity.CRYBookEntity
+import com.javg.cryptocurrencies.data.db.entity.CRYDetailBookEntity
+
+@Database(entities = [CRYBookEntity::class, CRYDetailBookEntity::class], version = 4)
+abstract class CRYAppDatabase : RoomDatabase() {
+ companion object {
+ const val DB_NAME = "database_crypto_book"
+ const val BOOK_TABLE = "book_table"
+ const val DETAIL_BOOK_TABLE = "detail_book_table"
+ }
+ abstract fun bookDao(): CRYBookDao
+
+ abstract fun tickerDao(): CRYTickerDao
+}
diff --git a/app/src/main/java/com/javg/cryptocurrencies/data/domain/CRYBookUseCase.kt b/app/src/main/java/com/javg/cryptocurrencies/data/domain/CRYBookUseCase.kt
new file mode 100644
index 000000000..621c0b76d
--- /dev/null
+++ b/app/src/main/java/com/javg/cryptocurrencies/data/domain/CRYBookUseCase.kt
@@ -0,0 +1,85 @@
+package com.javg.cryptocurrencies.data.domain
+
+import com.javg.cryptocurrencies.data.model.CRYBook
+import com.javg.cryptocurrencies.data.repository.CRYBookRepository
+import com.javg.cryptocurrencies.utils.getSecondCoinsText
+import com.javg.cryptocurrencies.utils.separateStringCoins
+import kotlinx.coroutines.flow.Flow
+import javax.inject.Inject
+
+/**
+ * @author Juan Vera Gomez
+ * Date modified 22/02/2023
+ *
+ * Contains the necessary function to get the information
+ * from books to the repository layer
+ *
+ * @param repository It is the repository that is responsible for obtaining the books
+ *
+ * @since 2.1
+ */
+class CRYBookUseCase @Inject constructor(
+ private val repository: CRYBookRepository,
+) {
+ /**
+ * Transform the list to assign the name of the simple book and add
+ * the corresponding images to the main moment and the one to be converted
+ *
+ * @param books is the list a list of books
+ * @return a transformed list
+ */
+ fun transformBooks(books: List): List {
+ books.map {
+ it.singleBook = it.book.separateStringCoins()
+ it.imageUrl = "https://cryptoicons.org/api/icon/${it.book.separateStringCoins()}/200"
+ it.bookDestination = "https://cryptoicons.org/api/icon/${it.book.getSecondCoinsText()}/200"
+ }
+ return books
+ }
+
+ /**
+ * You are going to create a hashmap taking a list as a reference,
+ * which will have the name of the coin as a key and a list
+ * of all the coins that the main list has as a value to classify it.
+ *
+ * @param books is the list a list of books
+ * @return a hashmap with the name of the coin as the key and the
+ * list of all matches as the value
+ */
+ fun createUniqueMap(books: List): HashMap> {
+ val mapBooks = HashMap>()
+ val uniqueBooks: List = books
+ .distinctBy { it.singleBook }
+ .sortedBy { it.singleBook }
+
+ uniqueBooks.forEach { uniqueBook ->
+ val booksGroup = books.filter { it.singleBook == uniqueBook.singleBook }
+ mapBooks[uniqueBook.singleBook] = booksGroup
+ }
+
+ return mapBooks
+ }
+
+ /**
+ * Create a list distinguishing the first name of the book
+ * and ordering it alphabetically
+ *
+ * @param books is the list a list of books
+ * @return a list without repeated and ordered books
+ */
+ fun createListBookTitles(books: List): List {
+ return books
+ .distinctBy { it.singleBook }
+ .sortedBy { it.singleBook }
+ }
+
+ /**
+ * Gets the list of books that are stored in the database
+ */
+ fun queryBooks(): Flow> = repository.queryBooks()
+
+ /**
+ * Query the api to get the books remotely
+ */
+ fun getAvailableBooksRx(): Boolean = repository.getAvailableBooksRx()
+}
diff --git a/app/src/main/java/com/javg/cryptocurrencies/data/domain/CRYGetListBookWithTickerUseCase.kt b/app/src/main/java/com/javg/cryptocurrencies/data/domain/CRYGetListBookWithTickerUseCase.kt
new file mode 100644
index 000000000..3c433dea1
--- /dev/null
+++ b/app/src/main/java/com/javg/cryptocurrencies/data/domain/CRYGetListBookWithTickerUseCase.kt
@@ -0,0 +1,46 @@
+package com.javg.cryptocurrencies.data.domain
+
+import com.javg.cryptocurrencies.data.model.CRYDetailBook
+import com.javg.cryptocurrencies.data.repository.CRYOrderBookRepository
+import com.javg.cryptocurrencies.data.repository.CRYTickerRepository
+import kotlinx.coroutines.CoroutineDispatcher
+import kotlinx.coroutines.withContext
+import javax.inject.Inject
+
+/**
+ * @author Juan Vera Gomez
+ * Date modified 13/02/2023
+ *
+ * Contains the functionality to query the information and make
+ * a deal before returning the information to the view model
+ *
+ * @param tickerRepository is the repository that returns only ticker information
+ * @param orderBookRepository is the repository that returns only order book information
+ *
+ * @since 2.0
+ */
+class CRYGetListBookWithTickerUseCase @Inject constructor(
+ private val tickerRepository: CRYTickerRepository,
+ private val orderBookRepository: CRYOrderBookRepository,
+ private val dispatcher: CoroutineDispatcher,
+) {
+
+ /**
+ * Returns a detail type model of a specific book,
+ * obtaining the information from two different repositories
+ *
+ * @param book is the name of the book to consult its information
+ *
+ * @return CRYDetailBook is the detail model of the consulted book
+ */
+ suspend operator fun invoke(book: String): CRYDetailBook? = withContext(dispatcher) {
+ val ticker = tickerRepository.getTicker(book)
+ var detailBook: CRYDetailBook? = null
+
+ if (ticker != null) {
+ detailBook = orderBookRepository.getOrderBook(book)
+ }
+
+ detailBook
+ }
+}
diff --git a/app/src/main/java/com/javg/cryptocurrencies/data/mapper/CRYMapper.kt b/app/src/main/java/com/javg/cryptocurrencies/data/mapper/CRYMapper.kt
new file mode 100644
index 000000000..71be03cbb
--- /dev/null
+++ b/app/src/main/java/com/javg/cryptocurrencies/data/mapper/CRYMapper.kt
@@ -0,0 +1,74 @@
+package com.javg.cryptocurrencies.data.mapper
+
+import com.javg.cryptocurrencies.data.db.entity.CRYBookEntity
+import com.javg.cryptocurrencies.data.db.entity.CRYDetailBookEntity
+import com.javg.cryptocurrencies.data.model.CRYBook
+import com.javg.cryptocurrencies.data.model.CRYBookResponse
+import com.javg.cryptocurrencies.data.model.CRYDetailBook
+import com.javg.cryptocurrencies.data.model.CRYTicker
+import com.javg.cryptocurrencies.utils.CRYUtils
+
+/**
+ * A function is extended to be able to treat the model
+ * of the response to an entity model
+ */
+fun CRYBookResponse.toEntity(): CRYBookEntity {
+ return CRYBookEntity(
+ book = this.book,
+ minimumPrice = this.minimumPrice,
+ maximumPrice = this.maximumPrice,
+ )
+}
+
+/**
+ * A function is extended from the entity model to
+ * handle it and pass it to a general model
+ */
+fun CRYBookEntity.toDomain(): CRYBook {
+ return CRYBook(
+ book = this.book,
+ imageUrl = "",
+ bookDestination = "",
+ )
+}
+
+/**
+ * A function is extended to be able to treat the model
+ * of the response to an entity model
+ */
+fun CRYTicker.toEntity(): CRYDetailBookEntity {
+ return CRYDetailBookEntity(
+ book = this.book,
+ high = this.high,
+ last = this.last,
+ low = this.low,
+ askList = "",
+ bidsList = "",
+ )
+}
+
+/**
+ * A function is extended from the entity model to
+ * handle it and pass it to a general model
+ */
+fun CRYDetailBookEntity.toDomain(): CRYDetailBook {
+ return CRYDetailBook(
+ high = this.high,
+ last = this.last,
+ low = this.low,
+ )
+}
+
+/**
+ * An entity model function is extended to handle it and pass
+ * it to a general model by updating missing data
+ */
+fun CRYDetailBookEntity.toDomainAll(): CRYDetailBook {
+ return CRYDetailBook(
+ high = this.high,
+ last = this.last,
+ low = this.low,
+ askList = CRYUtils.convertersJsonToList(this.askList),
+ bidsList = CRYUtils.convertersJsonToList(this.bidsList),
+ )
+}
diff --git a/app/src/main/java/com/javg/cryptocurrencies/data/model/CRYModel.kt b/app/src/main/java/com/javg/cryptocurrencies/data/model/CRYModel.kt
new file mode 100644
index 000000000..abac831b2
--- /dev/null
+++ b/app/src/main/java/com/javg/cryptocurrencies/data/model/CRYModel.kt
@@ -0,0 +1,16 @@
+package com.javg.cryptocurrencies.data.model
+
+data class CRYBook(
+ var book: String,
+ var imageUrl: String,
+ var bookDestination: String,
+ var singleBook: String = "",
+)
+
+data class CRYDetailBook(
+ var high: String = "",
+ var last: String = "",
+ var low: String = "",
+ var askList: List? = null,
+ var bidsList: List? = null,
+)
diff --git a/app/src/main/java/com/javg/cryptocurrencies/data/model/CRYResponseModel.kt b/app/src/main/java/com/javg/cryptocurrencies/data/model/CRYResponseModel.kt
new file mode 100644
index 000000000..934c13270
--- /dev/null
+++ b/app/src/main/java/com/javg/cryptocurrencies/data/model/CRYResponseModel.kt
@@ -0,0 +1,70 @@
+package com.javg.cryptocurrencies.data.model
+
+import com.google.gson.annotations.Expose
+import com.google.gson.annotations.SerializedName
+
+open class CRYBaseResponse {
+ @SerializedName("success")
+ @Expose
+ var success: Boolean = false
+
+ @SerializedName("payload")
+ @Expose
+ var payload: T? = null
+}
+
+data class CRYBookResponse(
+ @SerializedName("book")
+ var book: String,
+ @SerializedName("minimum_price")
+ var minimumPrice: String,
+ @SerializedName("maximum_price")
+ var maximumPrice: String,
+ @SerializedName("minimum_amount")
+ var minimumAmount: String,
+ @SerializedName("maximum_amount")
+ var maximumAmount: String,
+ @SerializedName("minimum_value")
+ var minimumValue: String,
+ @SerializedName("maximum_value")
+ var maximumValue: String,
+ @SerializedName("tick_size")
+ var tickSize: String,
+ @SerializedName("default_chart")
+ var defaultChart: String,
+)
+
+data class CRYTicker(
+ @SerializedName("book")
+ var book: String,
+ @SerializedName("volume")
+ var volume: String,
+ @SerializedName("high")
+ var high: String,
+ @SerializedName("last")
+ var last: String,
+ @SerializedName("low")
+ var low: String,
+ @SerializedName("vwap")
+ var vwap: String,
+ @SerializedName("ask")
+ var ask: String,
+ @SerializedName("bid")
+ var bid: String,
+)
+
+data class CRYOrderBook(
+ @SerializedName("bids")
+ var bidsList: List,
+ @SerializedName("asks")
+ var asksList: List,
+)
+
+data class CRYAskOrBids(
+ @SerializedName("book")
+ var book: String,
+ @SerializedName("price")
+ var price: String,
+ @SerializedName("amount")
+ var amount: String,
+)
diff --git a/app/src/main/java/com/javg/cryptocurrencies/data/network/CRYApi.kt b/app/src/main/java/com/javg/cryptocurrencies/data/network/CRYApi.kt
new file mode 100644
index 000000000..22f0cf599
--- /dev/null
+++ b/app/src/main/java/com/javg/cryptocurrencies/data/network/CRYApi.kt
@@ -0,0 +1,31 @@
+package com.javg.cryptocurrencies.data.network
+
+import com.javg.cryptocurrencies.data.model.CRYBaseResponse
+import com.javg.cryptocurrencies.data.model.CRYBookResponse
+import com.javg.cryptocurrencies.data.model.CRYOrderBook
+import com.javg.cryptocurrencies.data.model.CRYTicker
+import retrofit2.http.GET
+import retrofit2.http.Query
+import rx.Observable
+
+interface CRYApi {
+
+ companion object {
+ const val BASE_URL = "https://api.bitso.com/"
+ const val END_POINT_AVAILABLE_BOOKS = "v3/available_books"
+ const val END_POINT_ORDER_BOOK = "v3/order_book"
+ const val END_POINT_TICKER = "v3/ticker"
+ }
+
+ @GET(END_POINT_AVAILABLE_BOOKS)
+ suspend fun getListAvailableBooks(): CRYBaseResponse>
+
+ @GET(END_POINT_AVAILABLE_BOOKS)
+ fun getListAvailableBooksRX(): Observable>>
+
+ @GET(END_POINT_TICKER)
+ suspend fun getTicker(@Query("book") book: String): CRYBaseResponse
+
+ @GET(END_POINT_ORDER_BOOK)
+ suspend fun getOrderBook(@Query("book") book: String): CRYBaseResponse
+}
diff --git a/app/src/main/java/com/javg/cryptocurrencies/data/repository/CRYBookRepository.kt b/app/src/main/java/com/javg/cryptocurrencies/data/repository/CRYBookRepository.kt
new file mode 100644
index 000000000..f35fadd10
--- /dev/null
+++ b/app/src/main/java/com/javg/cryptocurrencies/data/repository/CRYBookRepository.kt
@@ -0,0 +1,75 @@
+package com.javg.cryptocurrencies.data.repository
+
+import android.content.Context
+import android.util.Log
+import com.javg.cryptocurrencies.data.db.dao.CRYBookDao
+import com.javg.cryptocurrencies.data.mapper.toDomain
+import com.javg.cryptocurrencies.data.mapper.toEntity
+import com.javg.cryptocurrencies.data.model.CRYBook
+import com.javg.cryptocurrencies.data.network.CRYApi
+import com.javg.cryptocurrencies.utils.CRYUtils
+import dagger.hilt.android.qualifiers.ApplicationContext
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.map
+import rx.Observable
+import rx.android.schedulers.AndroidSchedulers
+import rx.schedulers.Schedulers
+import javax.inject.Inject
+
+/**
+ * @author Juan Vera Gomez
+ * Date modified 10/02/2023
+ *
+ * It is in charge of the functionality to be able to obtain the information
+ * from the database and in case of not having information,
+ * obtain it remotely through a service
+ *
+ * @since 2.0
+ */
+class CRYBookRepository @Inject constructor(
+ @ApplicationContext val context: Context,
+ private val cryApi: CRYApi,
+ private val bookDao: CRYBookDao,
+) : CRYGenericRepository() {
+
+ /**
+ * Observe through a flow the list of books stored in the database every time it changes
+ *
+ * @return a book list type flow
+ */
+ fun queryBooks(): Flow> = bookDao.getAllBookV2().map { books ->
+ books.map { it.toDomain() }
+ }
+
+ /**
+ * It is in charge of consuming the remote api to be able
+ * to obtain the list of books and later store them in the database
+ */
+ fun getAvailableBooksRx(): Boolean {
+ var result = false
+ cryApi.getListAvailableBooksRX()
+ .subscribeOn(Schedulers.io())
+ .observeOn(AndroidSchedulers.mainThread())
+ .subscribe({ response ->
+ result = if (response.payload.isNullOrEmpty()) {
+ false
+ } else {
+ CRYUtils.saveTime(context)
+ response?.payload?.let { payload ->
+ val booksEntity = payload.map { it.toEntity() }
+
+ Observable.just(bookDao)
+ .subscribeOn(Schedulers.io())
+ .subscribe {
+ it.insertAll(booksEntity)
+ }
+ }
+ true
+ }
+ }) { throwable ->
+ Log.e("CRYBookRepository", "Error $throwable")
+ result = false
+ }
+ return result
+ }
+}
diff --git a/app/src/main/java/com/javg/cryptocurrencies/data/repository/CRYGenericRepository.kt b/app/src/main/java/com/javg/cryptocurrencies/data/repository/CRYGenericRepository.kt
new file mode 100644
index 000000000..f96812cd4
--- /dev/null
+++ b/app/src/main/java/com/javg/cryptocurrencies/data/repository/CRYGenericRepository.kt
@@ -0,0 +1,13 @@
+package com.javg.cryptocurrencies.data.repository
+
+open class CRYGenericRepository {
+ suspend fun getResponse(
+ callFunction: suspend () -> T,
+ ) {
+ try {
+ callFunction.invoke()
+ } catch (e: Exception) {
+ println("exception getResponse -> ${e.message}")
+ }
+ }
+}
diff --git a/app/src/main/java/com/javg/cryptocurrencies/data/repository/CRYOrderBookRepository.kt b/app/src/main/java/com/javg/cryptocurrencies/data/repository/CRYOrderBookRepository.kt
new file mode 100644
index 000000000..1d984a341
--- /dev/null
+++ b/app/src/main/java/com/javg/cryptocurrencies/data/repository/CRYOrderBookRepository.kt
@@ -0,0 +1,58 @@
+package com.javg.cryptocurrencies.data.repository
+
+import com.javg.cryptocurrencies.data.db.dao.CRYTickerDao
+import com.javg.cryptocurrencies.data.mapper.toDomainAll
+import com.javg.cryptocurrencies.data.model.CRYDetailBook
+import com.javg.cryptocurrencies.data.network.CRYApi
+import com.javg.cryptocurrencies.utils.CRYUtils
+import javax.inject.Inject
+
+/**
+ * @author Juan Vera Gomez
+ * Date modified 13/02/2023
+ *
+ * allows you to retrieve a list of all open orders in
+ * the specified book.
+ *
+ * @param cryApi is an interface that contains the remote query endpoints
+ *
+ * @since 2.0
+ */
+class CRYOrderBookRepository @Inject constructor(
+ private val cryApi: CRYApi,
+ private val tickerDao: CRYTickerDao,
+) {
+
+ /**
+ * Returns a book detail model obtaining the information from the database or,
+ * if it is empty, consult it with the remote api
+ *
+ * @param book is the name of the book to consult its specific information
+ */
+ suspend fun getOrderBook(book: String): CRYDetailBook {
+ var ticker = tickerDao.findById(book)
+
+ ticker?.let { detailBookEntity ->
+ if (detailBookEntity.askList.isEmpty() || detailBookEntity.bidsList.isEmpty()) {
+ val response = getOrderBookFromApi(book)
+ response.payload?.let {
+ tickerDao.update(
+ ask = CRYUtils.convertersListToJson(it.asksList),
+ bids = CRYUtils.convertersListToJson(it.bidsList),
+ book = book,
+ )
+ }
+ ticker = tickerDao.findById(book)
+ }
+ }
+
+ return ticker?.toDomainAll() ?: CRYDetailBook()
+ }
+
+ /**
+ * Returns a book order type model consulting the information remotely to an api
+ *
+ * @param book is the name of the book to consult its specific information
+ */
+ private suspend fun getOrderBookFromApi(book: String) = cryApi.getOrderBook(book)
+}
diff --git a/app/src/main/java/com/javg/cryptocurrencies/data/repository/CRYTickerRepository.kt b/app/src/main/java/com/javg/cryptocurrencies/data/repository/CRYTickerRepository.kt
new file mode 100644
index 000000000..a0caf8f7e
--- /dev/null
+++ b/app/src/main/java/com/javg/cryptocurrencies/data/repository/CRYTickerRepository.kt
@@ -0,0 +1,54 @@
+package com.javg.cryptocurrencies.data.repository
+
+import android.content.Context
+import com.javg.cryptocurrencies.data.db.dao.CRYTickerDao
+import com.javg.cryptocurrencies.data.db.entity.CRYDetailBookEntity
+import com.javg.cryptocurrencies.data.mapper.toDomain
+import com.javg.cryptocurrencies.data.mapper.toEntity
+import com.javg.cryptocurrencies.data.model.CRYBaseResponse
+import com.javg.cryptocurrencies.data.model.CRYDetailBook
+import com.javg.cryptocurrencies.data.model.CRYTicker
+import com.javg.cryptocurrencies.data.network.CRYApi
+import com.javg.cryptocurrencies.utils.CRYUtils
+import dagger.hilt.android.qualifiers.ApplicationContext
+import javax.inject.Inject
+
+/**
+ * @author Juan Vera Gomez
+ * Date modified 16/02/2023
+ *
+ * Allows you to retrieve business information from the specified book.
+ *
+ * @param cryApi is an interface that contains the remote query endpoints
+ *
+ * @since 2.0
+ */
+class CRYTickerRepository @Inject constructor(
+ @ApplicationContext val context: Context,
+ private val cryApi: CRYApi,
+ private val tickerDao: CRYTickerDao,
+) : CRYGenericRepository() {
+
+ /**
+ *
+ */
+ suspend fun getTicker(book: String): CRYDetailBook? {
+ var ticker = tickerDao.findById(book)
+
+ if (ticker == null) {
+ val tickerAux = getTickerFromApi(book)
+ tickerAux?.let {
+ CRYUtils.saveTime(context)
+ tickerDao.insert(it)
+ }
+ ticker = tickerDao.findById(book)
+ }
+ return ticker?.toDomain()
+ }
+
+ private suspend fun getTickerFromApi(book: String): CRYDetailBookEntity? {
+ var response = CRYBaseResponse()
+ getResponse { response = cryApi.getTicker(book) }
+ return response.payload?.toEntity()
+ }
+}
diff --git a/app/src/main/java/com/javg/cryptocurrencies/utils/CRYUtils.kt b/app/src/main/java/com/javg/cryptocurrencies/utils/CRYUtils.kt
new file mode 100644
index 000000000..e12012c04
--- /dev/null
+++ b/app/src/main/java/com/javg/cryptocurrencies/utils/CRYUtils.kt
@@ -0,0 +1,117 @@
+package com.javg.cryptocurrencies.utils
+
+import android.annotation.SuppressLint
+import android.content.Context
+import android.net.ConnectivityManager
+import android.net.LinkProperties
+import android.net.Network
+import android.net.NetworkCapabilities
+import android.os.Build
+import android.util.Log
+import com.google.gson.Gson
+import com.javg.cryptocurrencies.data.model.CRYAskOrBids
+import java.text.SimpleDateFormat
+import java.util.*
+
+object CRYUtils {
+
+ /**
+ * Checks if the device is connected to a network source,
+ * be it mobile or wifi and returns a true or false
+ * depending on whether or not it has a connection
+ */
+ fun isInternetAvailable(context: Context, onChangeState: (isConnected: Boolean) -> Unit): Boolean {
+ val TAG = "isInternetAvailable"
+ var result = false
+ val connectivityManager = context.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
+ val networkCapabilities = connectivityManager.activeNetwork ?: return false
+ val actNw =
+ connectivityManager.getNetworkCapabilities(networkCapabilities) ?: return false
+ result = when {
+ actNw.hasTransport(NetworkCapabilities.TRANSPORT_WIFI) -> true
+ actNw.hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR) -> true
+ actNw.hasTransport(NetworkCapabilities.TRANSPORT_ETHERNET) -> true
+ else -> false
+ }
+
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
+ connectivityManager.registerDefaultNetworkCallback(object : ConnectivityManager.NetworkCallback() {
+ override fun onAvailable(network: Network) {
+ onChangeState.invoke(true)
+ Log.e(TAG, "====================== The default network is now: " + network)
+ }
+
+ override fun onLost(network: Network) {
+ onChangeState.invoke(false)
+ Log.e(TAG, "====================== The application no longer has a default network. The last default network was " + network)
+ }
+
+ override fun onCapabilitiesChanged(network: Network, networkCapabilities: NetworkCapabilities) {
+ Log.e(TAG, "====================== The default network changed capabilities: " + networkCapabilities)
+ }
+
+ override fun onLinkPropertiesChanged(network: Network, linkProperties: LinkProperties) {
+ Log.e(TAG, "====================== The default network changed link properties: " + linkProperties)
+ }
+ })
+ }
+ } else {
+ connectivityManager.run {
+ connectivityManager.activeNetworkInfo?.run {
+ result = when (type) {
+ ConnectivityManager.TYPE_WIFI -> true
+ ConnectivityManager.TYPE_MOBILE -> true
+ ConnectivityManager.TYPE_ETHERNET -> true
+ else -> false
+ }
+ }
+ }
+ }
+
+ return result
+ }
+
+ /**
+ * Gets device time with mexico time zone setting and returns it
+ */
+ @SuppressLint("SimpleDateFormat")
+ fun getCurrentDay(): String {
+ val myTimeZone = TimeZone.getTimeZone("America/Mexico_City")
+ val simpleDateFormat = SimpleDateFormat("HH:mm:ss")
+ simpleDateFormat.timeZone = myTimeZone
+ return simpleDateFormat.format(Date())
+ }
+
+ /**
+ * Save a time in device preferences
+ *
+ * @param context is the context of the activity
+ */
+ fun saveTime(context: Context) {
+ val sharedPreference = context.getSharedPreferences("PREFERENCE_NAME", Context.MODE_PRIVATE)
+ val editor = sharedPreference.edit()
+ editor.putString("time", getCurrentDay())
+ editor.apply()
+ }
+
+ /**
+ * Returns the time saved in device preferences
+ *
+ * @param context is the context of the activity
+ */
+ fun getSaveTime(context: Context): String {
+ val sharedPreference = context.getSharedPreferences("PREFERENCE_NAME", Context.MODE_PRIVATE)
+ return sharedPreference.getString("time", "") ?: ""
+ }
+
+ /**
+ * Convert a list of type CRYAskOrBids to a string
+ */
+ fun convertersListToJson(askOrBids: List): String = Gson().toJson(askOrBids)
+
+ /**
+ * Converts a string to a list of type CRYAskOrBids
+ */
+ fun convertersJsonToList(askOrBids: String) = Gson().fromJson(askOrBids, Array::class.java).toList()
+}
diff --git a/app/src/main/java/com/javg/cryptocurrencies/utils/CryStringExt.kt b/app/src/main/java/com/javg/cryptocurrencies/utils/CryStringExt.kt
new file mode 100644
index 000000000..404a4cd7d
--- /dev/null
+++ b/app/src/main/java/com/javg/cryptocurrencies/utils/CryStringExt.kt
@@ -0,0 +1,16 @@
+package com.javg.cryptocurrencies.utils
+
+/**
+ * Function that extends a string to be able to separate the text by a '_'
+ * symbol and return the first element separated in it
+ */
+fun String.separateStringCoins() = this.split("_")[0]
+
+/**
+ * function that extends a string to get the
+ * second element separated by the '_' symbol
+ */
+fun String.getSecondCoinsText(): String {
+ val list = this.split("_")
+ return if (list.size >= 2) list[1] else this
+}
diff --git a/app/src/main/java/com/javg/cryptocurrencies/view/base/CRYBaseActivity.kt b/app/src/main/java/com/javg/cryptocurrencies/view/base/CRYBaseActivity.kt
new file mode 100644
index 000000000..eab17f853
--- /dev/null
+++ b/app/src/main/java/com/javg/cryptocurrencies/view/base/CRYBaseActivity.kt
@@ -0,0 +1,56 @@
+package com.javg.cryptocurrencies.view.base
+
+import android.os.Bundle
+import androidx.activity.viewModels
+import androidx.appcompat.app.AppCompatActivity
+import androidx.core.content.ContextCompat
+import androidx.lifecycle.Lifecycle
+import androidx.lifecycle.lifecycleScope
+import androidx.lifecycle.repeatOnLifecycle
+import com.google.android.material.snackbar.Snackbar
+import com.javg.cryptocurrencies.R
+import com.javg.cryptocurrencies.view.viewmodel.CRYAppViewModel
+import kotlinx.coroutines.launch
+
+open class CRYBaseActivity : AppCompatActivity() {
+ private val appVM by viewModels()
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ testStateFlow()
+ }
+
+ private fun testStateFlow() {
+ lifecycleScope.launch {
+ repeatOnLifecycle(Lifecycle.State.STARTED) {
+ appVM.uiStateNetwork.collect {
+ if (it) {
+ showSuccessNetwork()
+ } else {
+ showErrorNetwork()
+ }
+ }
+ }
+ }
+ }
+
+ private fun showSuccessNetwork() {
+ val snack = Snackbar.make(
+ this@CRYBaseActivity.findViewById(R.id.nav_host_main),
+ resources.getString(R.string.cry_success_network),
+ Snackbar.LENGTH_LONG,
+ )
+ snack.setBackgroundTint(ContextCompat.getColor(this, R.color.green_success))
+ snack.show()
+ }
+
+ private fun showErrorNetwork() {
+ val snack = Snackbar.make(
+ this@CRYBaseActivity.findViewById(R.id.nav_host_main),
+ resources.getString(R.string.cry_error_network),
+ Snackbar.LENGTH_LONG,
+ )
+ snack.setBackgroundTint(ContextCompat.getColor(this, R.color.red_error))
+ snack.show()
+ }
+}
diff --git a/app/src/main/java/com/javg/cryptocurrencies/view/book/CRYBookFragment.kt b/app/src/main/java/com/javg/cryptocurrencies/view/book/CRYBookFragment.kt
new file mode 100644
index 000000000..ff5df8c25
--- /dev/null
+++ b/app/src/main/java/com/javg/cryptocurrencies/view/book/CRYBookFragment.kt
@@ -0,0 +1,210 @@
+package com.javg.cryptocurrencies.view.book
+
+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.activityViewModels
+import androidx.lifecycle.Observer
+import androidx.navigation.fragment.findNavController
+import androidx.recyclerview.widget.LinearLayoutManager
+import androidx.recyclerview.widget.RecyclerView
+import androidx.swiperefreshlayout.widget.SwipeRefreshLayout
+import androidx.viewpager2.widget.CompositePageTransformer
+import androidx.viewpager2.widget.MarginPageTransformer
+import androidx.viewpager2.widget.ViewPager2
+import com.facebook.shimmer.ShimmerFrameLayout
+import com.javg.cryptocurrencies.R
+import com.javg.cryptocurrencies.data.model.CRYBook
+import com.javg.cryptocurrencies.databinding.CryBookFragmentBinding
+import com.javg.cryptocurrencies.view.book.recyclerview.CRYBookRecyclerView
+import com.javg.cryptocurrencies.view.book.recyclerview.CRYChipsHeaderRecyclerView
+import com.javg.cryptocurrencies.view.viewmodel.CRYHomeVM
+import kotlin.math.abs
+
+/**
+ * @author Juan Vera Gomez
+ * Date modified 10/02/2023
+ *
+ * Fragment in charge of manipulating and displaying the
+ * information corresponding to the cryptocurrency cards
+ *
+ * @since 2.2
+ */
+class CRYBookFragment : Fragment() {
+
+ private lateinit var binding: CryBookFragmentBinding
+ private lateinit var adapterBook: CRYBookRecyclerView
+ private lateinit var adapterChips: CRYChipsHeaderRecyclerView
+ private val bookHomeVM by activityViewModels()
+ private lateinit var shimmerFrameLayout: ShimmerFrameLayout
+ private lateinit var swipeRefreshLayout: SwipeRefreshLayout
+
+ // Saves the Lambda event that is executed in the adapter item
+ private val onClickItem: (String, String) -> Unit = { book, image ->
+ val args = Bundle().apply {
+ putString("BOOK", book)
+ putString("IMAGE_NAME", image)
+ }
+ findNavController().navigate(R.id.action_cry_book_fragment_to_cry_detail_book_fragment, args)
+ }
+
+ override fun onCreateView(
+ inflater: LayoutInflater,
+ container: ViewGroup?,
+ savedInstanceState: Bundle?,
+ ): View {
+ binding = CryBookFragmentBinding.inflate(inflater, container, false)
+ return binding.root
+ }
+
+ override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
+ super.onViewCreated(view, savedInstanceState)
+ loadShimmer()
+ loadAdapter()
+ loadViewPagerChips()
+ swipeRefresh()
+ bookHomeVM.getBooks()
+ bookHomeVM.updateTime.observe(viewLifecycleOwner, observerUpdateTime)
+ bookHomeVM.chipsTitles.observe(viewLifecycleOwner, observerBooksChips)
+ bookHomeVM.equalBooks.observe(viewLifecycleOwner, observerEqualsBooks)
+ bookHomeVM.result.observe(viewLifecycleOwner, observerResult)
+ }
+
+ /**
+ * Performs an update when the user interacts with
+ * the view in order to update the books in the database
+ */
+ private fun swipeRefresh() {
+ swipeRefreshLayout = binding.swipeRefreshLayout
+ swipeRefreshLayout.setOnRefreshListener {
+ binding.rvBooks.visibility = View.GONE
+ loadShimmer()
+ bookHomeVM.getBooks()
+ }
+ }
+
+ /**
+ * It is responsible for linking the visual component
+ * and starting the facebook loading effect
+ */
+ private fun loadShimmer() {
+ binding.idShimmer.visibility = View.VISIBLE
+ shimmerFrameLayout = binding.idShimmer
+ shimmerFrameLayout.startShimmer()
+ }
+
+ /**
+ * Takes care of the adapter instance and the visual
+ * configuration of the recycle view
+ */
+ private fun loadAdapter() {
+ adapterBook = CRYBookRecyclerView(requireContext(), onClickItem)
+ binding.rvBooks.apply {
+ setHasFixedSize(false)
+ layoutManager = LinearLayoutManager(context)
+ adapter = adapterBook
+ }
+ }
+
+ /**
+ * Start the general configuration of the viewPager of the view
+ */
+ private fun loadViewPagerChips() {
+ adapterChips = CRYChipsHeaderRecyclerView(requireContext())
+ binding.includeChips.vpChips.adapter = adapterChips
+ setupViewPager()
+ onPagerListenerChips()
+ }
+
+ /**
+ * Configure the viewPager to transform the size of the card that is selected
+ */
+ private fun setupViewPager() {
+ with(binding) {
+ includeChips.vpChips.clipToPadding = false
+ includeChips.vpChips.clipChildren = false
+ includeChips.vpChips.offscreenPageLimit = 3
+ includeChips.vpChips.getChildAt(0).overScrollMode = RecyclerView.OVER_SCROLL_NEVER
+
+ val compositePageTransformer = CompositePageTransformer()
+ compositePageTransformer.addTransformer(MarginPageTransformer(40))
+ compositePageTransformer.addTransformer { page, position ->
+ page.apply {
+ val r = 1 - abs(position)
+ scaleY = (0.20f + r + 0.15f)
+ }
+ }
+ includeChips.vpChips.setPageTransformer(compositePageTransformer)
+ // redireccionar a posicion especifica
+ /*includeChips.vpChips.post {
+ includeChips.vpChips.setCurrentItem(5,false)
+ }*/
+ }
+ }
+
+ /**
+ * Contains the functionality to be able to detect when the user
+ * has changed the page in the view
+ */
+ private fun onPagerListenerChips() {
+ binding.includeChips.vpChips.registerOnPageChangeCallback(object : ViewPager2.OnPageChangeCallback() {
+ override fun onPageSelected(position: Int) {
+ super.onPageSelected(position)
+ println("onPageSelected -> $position")
+ bookHomeVM.updateListDifferentBook(position)
+ }
+ })
+ }
+
+ /**
+ * It is in charge of observing the list that contains the same
+ * books of the page of selection of the view
+ */
+ private val observerEqualsBooks = Observer> { equalsBooks ->
+ equalsBooks?.let {
+ swipeRefreshLayout.isRefreshing = false
+
+ if (equalsBooks.isEmpty()) {
+ Toast.makeText(requireContext(), "Lista vacia", Toast.LENGTH_SHORT).show()
+ } else {
+ shimmerFrameLayout.stopShimmer()
+ binding.idShimmer.visibility = View.GONE
+ binding.rvBooks.visibility = View.VISIBLE
+ adapterBook.submitList(equalsBooks)
+ }
+ }
+ }
+
+ /**
+ * Look at the list that contains the unique books to
+ * be able to show the selection options
+ *
+ */
+ private val observerBooksChips = Observer> { listBooks ->
+ listBooks?.let {
+ adapterChips.submitList(listBooks)
+ }
+ }
+
+ /**
+ * Observes the time change and reflects it in the view
+ */
+ private val observerUpdateTime = Observer {
+ it?.let { updateTime ->
+ binding.updateDay.text = updateTime
+ }
+ }
+
+ private val observerResult = Observer { result ->
+ result?.let {
+ if (it) {
+ println("success")
+ } else {
+ println("error")
+ }
+ }
+ }
+}
diff --git a/app/src/main/java/com/javg/cryptocurrencies/view/book/recyclerview/CRYBookRecyclerView.kt b/app/src/main/java/com/javg/cryptocurrencies/view/book/recyclerview/CRYBookRecyclerView.kt
new file mode 100644
index 000000000..b9c195a2a
--- /dev/null
+++ b/app/src/main/java/com/javg/cryptocurrencies/view/book/recyclerview/CRYBookRecyclerView.kt
@@ -0,0 +1,68 @@
+package com.javg.cryptocurrencies.view.book.recyclerview
+
+import android.content.Context
+import android.view.LayoutInflater
+import android.view.ViewGroup
+import androidx.recyclerview.widget.DiffUtil
+import androidx.recyclerview.widget.ListAdapter
+import androidx.recyclerview.widget.RecyclerView
+import com.bumptech.glide.Glide
+import com.javg.cryptocurrencies.R
+import com.javg.cryptocurrencies.data.model.CRYBook
+import com.javg.cryptocurrencies.databinding.CryBookItemBinding
+import com.javg.cryptocurrencies.utils.getSecondCoinsText
+import com.javg.cryptocurrencies.utils.separateStringCoins
+
+/**
+ * @author Juan Vera Gomez
+ * Date modified 08/02/2023
+ *
+ * Contains the necessary functions for the adapter to
+ * display cryptocurrency books to work
+ *
+ * @since 2.0
+ */
+
+class CRYBookRecyclerView(
+ private val context: Context,
+ private val onItemClick: (String, String) -> Unit,
+) :
+ ListAdapter(BookOrderDiffCallback()) {
+
+ inner class CRYBookViewHolder(private val binding: CryBookItemBinding) : RecyclerView.ViewHolder(binding.root) {
+
+ /**
+ * Function in charge of setting the information of the book model that it receives
+ */
+ fun bind(book: CRYBook) {
+ binding.txtBook.text = book.book.separateStringCoins().uppercase()
+ binding.txtBookEnd.text = book.book.getSecondCoinsText().uppercase()
+ Glide.with(context)
+ .load(book.imageUrl)
+ .placeholder(R.drawable.ic_default_book)
+ .into(binding.imageId)
+ Glide.with(context)
+ .load(book.bookDestination)
+ .placeholder(R.drawable.ic_default_book)
+ .into(binding.imageIdEnd)
+ binding.imageMore.setOnClickListener {
+ onItemClick(book.book, book.imageUrl)
+ }
+ }
+ }
+
+ override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): CRYBookViewHolder {
+ val binding: CryBookItemBinding = CryBookItemBinding.inflate(LayoutInflater.from(parent.context), parent, false)
+ return CRYBookViewHolder(binding)
+ }
+
+ override fun onBindViewHolder(holder: CRYBookViewHolder, position: Int) {
+ holder.bind(getItem(position))
+ }
+}
+
+class BookOrderDiffCallback : DiffUtil.ItemCallback() {
+ override fun areItemsTheSame(oldItem: CRYBook, newItem: CRYBook) = oldItem.book == newItem.book
+
+ override fun areContentsTheSame(oldItem: CRYBook, newItem: CRYBook) = oldItem == newItem
+}
diff --git a/app/src/main/java/com/javg/cryptocurrencies/view/book/recyclerview/CRYChipsHeaderRecyclerView.kt b/app/src/main/java/com/javg/cryptocurrencies/view/book/recyclerview/CRYChipsHeaderRecyclerView.kt
new file mode 100644
index 000000000..9c1871b80
--- /dev/null
+++ b/app/src/main/java/com/javg/cryptocurrencies/view/book/recyclerview/CRYChipsHeaderRecyclerView.kt
@@ -0,0 +1,55 @@
+package com.javg.cryptocurrencies.view.book.recyclerview
+
+import android.content.Context
+import android.view.LayoutInflater
+import android.view.ViewGroup
+import androidx.core.content.ContextCompat
+import androidx.recyclerview.widget.DiffUtil
+import androidx.recyclerview.widget.ListAdapter
+import androidx.recyclerview.widget.RecyclerView
+import com.bumptech.glide.Glide
+import com.javg.cryptocurrencies.R
+import com.javg.cryptocurrencies.data.model.CRYBook
+import com.javg.cryptocurrencies.databinding.CryChipsItemBinding
+import com.javg.cryptocurrencies.utils.separateStringCoins
+
+class CRYChipsHeaderRecyclerView(private val context: Context) :
+ ListAdapter(ChipsDiffCallback()) {
+
+ inner class CRyChipsViewHolder(private val binding: CryChipsItemBinding) : RecyclerView.ViewHolder(binding.root) {
+ fun bind(book: CRYBook, position: Int) {
+ if (position != 0) {
+ binding.clContainer.setBackgroundColor(ContextCompat.getColor(context, R.color.transparent))
+ }
+
+ binding.txtTitleBook.text = book.book.separateStringCoins().uppercase()
+ Glide.with(context)
+ .load(book.imageUrl)
+ .placeholder(R.drawable.ic_default_book)
+ .into(binding.ivBook)
+
+ binding.clContainer.setOnClickListener {
+ if (position == absoluteAdapterPosition) {
+ binding.clContainer.background = ContextCompat.getDrawable(context, R.drawable.background_button_oval)
+ } else {
+ binding.clContainer.setBackgroundColor(ContextCompat.getColor(context, R.color.transparent))
+ }
+ }
+ }
+ }
+
+ override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): CRyChipsViewHolder {
+ val binding: CryChipsItemBinding = CryChipsItemBinding.inflate(LayoutInflater.from(parent.context), parent, false)
+ return CRyChipsViewHolder(binding)
+ }
+
+ override fun onBindViewHolder(holder: CRyChipsViewHolder, position: Int) {
+ holder.bind(getItem(position), position)
+ }
+}
+
+class ChipsDiffCallback : DiffUtil.ItemCallback() {
+ override fun areItemsTheSame(oldItem: CRYBook, newItem: CRYBook) = oldItem.book == newItem.book
+
+ override fun areContentsTheSame(oldItem: CRYBook, newItem: CRYBook) = oldItem == newItem
+}
diff --git a/app/src/main/java/com/javg/cryptocurrencies/view/detail/CRYDetailBookFragment.kt b/app/src/main/java/com/javg/cryptocurrencies/view/detail/CRYDetailBookFragment.kt
new file mode 100644
index 000000000..4d3609b42
--- /dev/null
+++ b/app/src/main/java/com/javg/cryptocurrencies/view/detail/CRYDetailBookFragment.kt
@@ -0,0 +1,197 @@
+package com.javg.cryptocurrencies.view.detail
+
+import android.os.Bundle
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import androidx.appcompat.content.res.AppCompatResources
+import androidx.core.content.ContextCompat
+import androidx.fragment.app.Fragment
+import androidx.fragment.app.activityViewModels
+import androidx.lifecycle.Observer
+import androidx.navigation.fragment.findNavController
+import androidx.recyclerview.widget.LinearLayoutManager
+import com.airbnb.lottie.LottieDrawable
+import com.bumptech.glide.Glide
+import com.facebook.shimmer.ShimmerFrameLayout
+import com.javg.cryptocurrencies.R
+import com.javg.cryptocurrencies.data.model.CRYAskOrBids
+import com.javg.cryptocurrencies.data.model.CRYDetailBook
+import com.javg.cryptocurrencies.databinding.CryDetailBookFragmentBinding
+import com.javg.cryptocurrencies.utils.separateStringCoins
+import com.javg.cryptocurrencies.view.detail.recyclerview.CRYAskRecyclerView
+import com.javg.cryptocurrencies.view.viewmodel.CRYDetailBookVM
+
+/**
+ * @author Juan Antonio Vera
+ * Date modified 22/02/2023
+ *
+ * They contain the necessary functionalities to show the detailed
+ * information of a specific book visually.
+ *
+ * @since 2.1
+ */
+class CRYDetailBookFragment : Fragment() {
+ private lateinit var binding: CryDetailBookFragmentBinding
+ private val detailBookVM by activityViewModels()
+ private lateinit var adapterAskBids: CRYAskRecyclerView
+ private lateinit var shimmerFrameLayout: ShimmerFrameLayout
+ private val lottieAsset = "empty_data.json"
+
+ override fun onCreateView(
+ inflater: LayoutInflater,
+ container: ViewGroup?,
+ savedInstanceState: Bundle?,
+ ): View {
+ binding = CryDetailBookFragmentBinding.inflate(inflater, container, false)
+ return binding.root
+ }
+
+ /**
+ * the functionality is overwritten, in which you have
+ * the functions for the view and the data that is
+ * requested by instance is obtained
+ */
+ override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
+ super.onViewCreated(view, savedInstanceState)
+ val book = arguments?.getString("BOOK").orEmpty()
+ val imageId = arguments?.getString("IMAGE_NAME").orEmpty()
+ loadData(book, imageId)
+ onClickListener()
+ }
+
+ /**
+ * It is in charge of displaying the information of the book in addition
+ * to displaying the image that is received, configuring the adapter
+ * and having the observers for when the information changes.
+ *
+ * @param book is the name of the book to show its details
+ * @param imageName is the name of the image that will be consulted online to display
+ */
+ private fun loadData(book: String, imageName: String) {
+ if (book.isNotEmpty()) {
+ detailBookVM.getTicker(book)
+ }
+
+ Glide.with(requireContext())
+ .load(imageName)
+ .placeholder(R.drawable.ic_default_book)
+ .into(binding.bookImage)
+ binding.headerTopBar.title.text = book.separateStringCoins().uppercase()
+ configureAdapter()
+
+ detailBookVM.tickerBook.observe(viewLifecycleOwner, tickerObserver)
+ detailBookVM.listAskOrBids.observe(viewLifecycleOwner, listAskOrBids)
+ detailBookVM.emptyData.observe(viewLifecycleOwner, emptyDataObserver)
+ detailBookVM.updateTime.observe(viewLifecycleOwner, observerUpdateTime)
+ }
+
+ /**
+ * Configure and instantiate the adapter to display ask and bids
+ */
+ private fun configureAdapter() {
+ adapterAskBids = CRYAskRecyclerView(requireContext())
+ binding.rvAskBids.apply {
+ setHasFixedSize(false)
+ layoutManager = LinearLayoutManager(context)
+ adapter = adapterAskBids
+ }
+ }
+
+ /**
+ * listens for the click events of the view when the user interacts
+ */
+ private fun onClickListener() = with(binding) {
+ headerTopBar.imageBack.setOnClickListener {
+ findNavController().popBackStack()
+ }
+ txtAsk.setOnClickListener { changeAsk() }
+ txtBids.setOnClickListener { changeBids() }
+ }
+
+ /**
+ * Control the visual configuration when selecting ask,
+ * changing the design and color of the text and background
+ */
+ private fun changeAsk() {
+ binding.txtAsk.background = AppCompatResources.getDrawable(requireContext(), R.drawable.background_button_line)
+ binding.txtBids.setBackgroundColor(ContextCompat.getColor(requireContext(), R.color.transparent))
+ detailBookVM.tickerBook.value?.askList?.let { detailBookVM.sendListUpdate(it) }
+ }
+
+ /**
+ * Control the visual configuration when selecting bids,
+ * changing the design and color of the text and background
+ */
+ private fun changeBids() {
+ binding.txtAsk.setBackgroundColor(ContextCompat.getColor(requireContext(), R.color.transparent))
+ binding.txtBids.background = AppCompatResources.getDrawable(requireContext(), R.drawable.background_button_line)
+ detailBookVM.tickerBook.value?.bidsList?.let { detailBookVM.sendListUpdate(it) }
+ }
+
+ private fun startAnimationNoData() {
+ binding.clNotInformation.visibility = View.VISIBLE
+ binding.lavNotInformation.imageAssetsFolder = "assets"
+ binding.lavNotInformation.setAnimation(lottieAsset)
+ binding.lavNotInformation.repeatCount = LottieDrawable.INFINITE
+ binding.lavNotInformation.playAnimation()
+ }
+
+ private fun hideContentAll() {
+ binding.bookImage.visibility = View.GONE
+ binding.txtLastPrice.visibility = View.GONE
+ binding.edLastPrice.visibility = View.GONE
+ binding.clContainerAll.visibility = View.GONE
+ }
+
+ private fun showContentAll() {
+ binding.bookImage.visibility = View.VISIBLE
+ binding.txtLastPrice.visibility = View.VISIBLE
+ binding.edLastPrice.visibility = View.VISIBLE
+ binding.clContainerAll.visibility = View.VISIBLE
+ binding.clNotInformation.visibility = View.GONE
+ }
+
+ /**
+ * Observe when the data of the prices of the specific book changes
+ */
+ private val tickerObserver = Observer {
+ it?.let {
+ with(binding) {
+ edLastPrice.text = String.format(requireContext().resources.getString(R.string.cry_format_amount), it.last)
+ inCardPrice.edHighestPrice.text = String.format(requireContext().resources.getString(R.string.cry_format_amount), it.high)
+ inCardPrice.edLowMorePrice.text = String.format(requireContext().resources.getString(R.string.cry_format_amount), it.low)
+ showContentAll()
+ }
+ }
+ }
+
+ /**
+ * watches when the ask or bid list changes informing
+ * the adapter of the change, as well as the
+ * corresponding visual changes
+ */
+ private val listAskOrBids = Observer> {
+ it?.let {
+ adapterAskBids.submitList(it)
+ }
+ }
+
+ private val emptyDataObserver = Observer {
+ it?.let {
+ if (it) {
+ startAnimationNoData()
+ hideContentAll()
+ }
+ }
+ }
+
+ /**
+ * Observes the time change and reflects it in the view
+ */
+ private val observerUpdateTime = Observer {
+ it?.let { updateTime ->
+ binding.headerTopBar.txtLastUpdate.text = updateTime
+ }
+ }
+}
diff --git a/app/src/main/java/com/javg/cryptocurrencies/view/detail/recyclerview/CRYAskRecyclerView.kt b/app/src/main/java/com/javg/cryptocurrencies/view/detail/recyclerview/CRYAskRecyclerView.kt
new file mode 100644
index 000000000..8e91f07fb
--- /dev/null
+++ b/app/src/main/java/com/javg/cryptocurrencies/view/detail/recyclerview/CRYAskRecyclerView.kt
@@ -0,0 +1,52 @@
+package com.javg.cryptocurrencies.view.detail.recyclerview
+
+import android.content.Context
+import android.view.LayoutInflater
+import android.view.ViewGroup
+import androidx.recyclerview.widget.DiffUtil
+import androidx.recyclerview.widget.ListAdapter
+import androidx.recyclerview.widget.RecyclerView
+import com.javg.cryptocurrencies.R
+import com.javg.cryptocurrencies.data.model.CRYAskOrBids
+import com.javg.cryptocurrencies.databinding.CryAskItemBinding
+
+/**
+ * @author Juan Vera Gomez
+ * Date modified 08/02/2023
+ *
+ * Contains the necessary functions to display
+ * the ask and bids information
+ *
+ * @since 2.0
+ */
+class CRYAskRecyclerView(
+ private val context: Context,
+) :
+ ListAdapter(AskAndBindDiffCallback()) {
+
+ inner class CRYAskViewHolder(private val binding: CryAskItemBinding) : RecyclerView.ViewHolder(binding.root) {
+
+ /**
+ * Function in charge of setting the information of the ask or bids model that it receives
+ */
+ fun bind(askOrBids: CRYAskOrBids) {
+ binding.edPrice.text = String.format(context.getString(R.string.cry_price_order), askOrBids.price)
+ binding.edAmount.text = askOrBids.amount
+ }
+ }
+
+ override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): CRYAskViewHolder {
+ val binding = CryAskItemBinding.inflate(LayoutInflater.from(parent.context), parent, false)
+ return CRYAskViewHolder(binding)
+ }
+
+ override fun onBindViewHolder(holder: CRYAskViewHolder, position: Int) {
+ holder.bind(getItem(position))
+ }
+}
+
+class AskAndBindDiffCallback : DiffUtil.ItemCallback() {
+ override fun areItemsTheSame(oldItem: CRYAskOrBids, newItem: CRYAskOrBids) = oldItem.book == newItem.book
+
+ override fun areContentsTheSame(oldItem: CRYAskOrBids, newItem: CRYAskOrBids) = oldItem == newItem
+}
diff --git a/app/src/main/java/com/javg/cryptocurrencies/view/theme/Color.kt b/app/src/main/java/com/javg/cryptocurrencies/view/theme/Color.kt
new file mode 100644
index 000000000..c7590f978
--- /dev/null
+++ b/app/src/main/java/com/javg/cryptocurrencies/view/theme/Color.kt
@@ -0,0 +1,10 @@
+package com.javg.cryptocurrencies.view.theme
+
+import androidx.compose.ui.graphics.Color
+
+val Purple200 = Color(0xFFBB86FC)
+val Purple500 = Color(0xFF6200EE)
+val Purple700 = Color(0xFF3700B3)
+val Teal200 = Color(0xFF03DAC5)
+
+val Blue300 = Color(0xFF0C7A96)
\ No newline at end of file
diff --git a/app/src/main/java/com/javg/cryptocurrencies/view/theme/Shape.kt b/app/src/main/java/com/javg/cryptocurrencies/view/theme/Shape.kt
new file mode 100644
index 000000000..52c3744ae
--- /dev/null
+++ b/app/src/main/java/com/javg/cryptocurrencies/view/theme/Shape.kt
@@ -0,0 +1,11 @@
+package com.javg.cryptocurrencies.view.theme
+
+import androidx.compose.foundation.shape.RoundedCornerShape
+import androidx.compose.material.Shapes
+import androidx.compose.ui.unit.dp
+
+val Shapes = Shapes(
+ small = RoundedCornerShape(4.dp),
+ medium = RoundedCornerShape(4.dp),
+ large = RoundedCornerShape(0.dp)
+)
\ No newline at end of file
diff --git a/app/src/main/java/com/javg/cryptocurrencies/view/theme/Theme.kt b/app/src/main/java/com/javg/cryptocurrencies/view/theme/Theme.kt
new file mode 100644
index 000000000..6e0f6b0a1
--- /dev/null
+++ b/app/src/main/java/com/javg/cryptocurrencies/view/theme/Theme.kt
@@ -0,0 +1,47 @@
+package com.javg.cryptocurrencies.view.theme
+
+import androidx.compose.foundation.isSystemInDarkTheme
+import androidx.compose.material.MaterialTheme
+import androidx.compose.material.darkColors
+import androidx.compose.material.lightColors
+import androidx.compose.runtime.Composable
+
+private val DarkColorPalette = darkColors(
+ primary = Purple200,
+ primaryVariant = Purple700,
+ secondary = Teal200
+)
+
+private val LightColorPalette = lightColors(
+ primary = Purple500,
+ primaryVariant = Purple700,
+ secondary = Teal200
+
+ /* Other default colors to override
+ background = Color.White,
+ surface = Color.White,
+ onPrimary = Color.White,
+ onSecondary = Color.Black,
+ onBackground = Color.Black,
+ onSurface = Color.Black,
+ */
+)
+
+@Composable
+fun CryptocurrenciesTheme(
+ darkTheme: Boolean = isSystemInDarkTheme(),
+ content: @Composable () -> Unit
+) {
+ val colors = if (darkTheme) {
+ DarkColorPalette
+ } else {
+ LightColorPalette
+ }
+
+ MaterialTheme(
+ colors = colors,
+ typography = Typography,
+ shapes = Shapes,
+ content = content
+ )
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/javg/cryptocurrencies/view/theme/Type.kt b/app/src/main/java/com/javg/cryptocurrencies/view/theme/Type.kt
new file mode 100644
index 000000000..e214bd29e
--- /dev/null
+++ b/app/src/main/java/com/javg/cryptocurrencies/view/theme/Type.kt
@@ -0,0 +1,28 @@
+package com.javg.cryptocurrencies.view.theme
+
+import androidx.compose.material.Typography
+import androidx.compose.ui.text.TextStyle
+import androidx.compose.ui.text.font.FontFamily
+import androidx.compose.ui.text.font.FontWeight
+import androidx.compose.ui.unit.sp
+
+// Set of Material typography styles to start with
+val Typography = Typography(
+ body1 = TextStyle(
+ fontFamily = FontFamily.Default,
+ fontWeight = FontWeight.Normal,
+ fontSize = 16.sp
+ )
+ /* Other default text styles to override
+ button = TextStyle(
+ fontFamily = FontFamily.Default,
+ fontWeight = FontWeight.W500,
+ fontSize = 14.sp
+ ),
+ caption = TextStyle(
+ fontFamily = FontFamily.Default,
+ fontWeight = FontWeight.Normal,
+ fontSize = 12.sp
+ )
+ */
+)
\ No newline at end of file
diff --git a/app/src/main/java/com/javg/cryptocurrencies/view/viewmodel/CRYAppViewModel.kt b/app/src/main/java/com/javg/cryptocurrencies/view/viewmodel/CRYAppViewModel.kt
new file mode 100644
index 000000000..9ba84d3fc
--- /dev/null
+++ b/app/src/main/java/com/javg/cryptocurrencies/view/viewmodel/CRYAppViewModel.kt
@@ -0,0 +1,19 @@
+package com.javg.cryptocurrencies.view.viewmodel
+
+import android.app.Application
+import androidx.lifecycle.AndroidViewModel
+import com.javg.cryptocurrencies.utils.CRYUtils
+import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.StateFlow
+
+class CRYAppViewModel(application: Application) : AndroidViewModel(application) {
+ private val _uiStateNetwork = MutableStateFlow(true)
+
+ val uiStateNetwork: StateFlow = _uiStateNetwork
+
+ init {
+ CRYUtils.isInternetAvailable(application.applicationContext) {
+ _uiStateNetwork.value = it
+ }
+ }
+}
diff --git a/app/src/main/java/com/javg/cryptocurrencies/view/viewmodel/CRYDetailBookVM.kt b/app/src/main/java/com/javg/cryptocurrencies/view/viewmodel/CRYDetailBookVM.kt
new file mode 100644
index 000000000..519a8ad06
--- /dev/null
+++ b/app/src/main/java/com/javg/cryptocurrencies/view/viewmodel/CRYDetailBookVM.kt
@@ -0,0 +1,92 @@
+package com.javg.cryptocurrencies.view.viewmodel
+
+import android.app.Application
+import androidx.lifecycle.AndroidViewModel
+import androidx.lifecycle.LiveData
+import androidx.lifecycle.MutableLiveData
+import androidx.lifecycle.viewModelScope
+import com.javg.cryptocurrencies.R
+import com.javg.cryptocurrencies.data.domain.CRYGetListBookWithTickerUseCase
+import com.javg.cryptocurrencies.data.model.CRYAskOrBids
+import com.javg.cryptocurrencies.data.model.CRYDetailBook
+import com.javg.cryptocurrencies.utils.CRYUtils
+import dagger.hilt.android.lifecycle.HiltViewModel
+import kotlinx.coroutines.launch
+import javax.inject.Inject
+
+/**
+ * @author Juan Vera Gomez
+ *
+ * Contains the functionality to consult the specific information of each book
+ *
+ * @param tickerUseCase It is the case of use who manages
+ * the obtaining of information in the data layer
+ *
+ * @since 2.0
+ */
+@HiltViewModel
+class CRYDetailBookVM @Inject constructor(
+ application: Application,
+ private val tickerUseCase: CRYGetListBookWithTickerUseCase,
+) : AndroidViewModel(application) {
+
+ private var _tickerBook = MutableLiveData()
+ private var _listAskOrBids = MutableLiveData>()
+ private var _emptyData = MutableLiveData()
+ private var _updateTime = MutableLiveData()
+
+ val tickerBook: LiveData
+ get() = _tickerBook
+
+ val listAskOrBids: LiveData>
+ get() = _listAskOrBids
+
+ val emptyData: LiveData
+ get() = _emptyData
+
+ val updateTime: LiveData
+ get() = _updateTime
+
+ init {
+ _updateTime.value = getTimeUpdate()
+ }
+
+ /**
+ * Consult the price information and ask list and bids of a specific book
+ *
+ * @param book is the name of the book to consult its specific information
+ */
+ fun getTicker(book: String) {
+ viewModelScope.launch {
+ val ticker = tickerUseCase.invoke(book)
+ ticker?.let {
+ _tickerBook.value = it
+ it.askList?.let { list ->
+ _listAskOrBids.value = list as MutableList
+ _updateTime.value = getTimeUpdate()
+ }
+ } ?: run {
+ _emptyData.value = true
+ }
+ }
+ }
+
+ /**
+ * Updates a list depending on the selection at view level
+ *
+ * @param listUpdate is the list that will update the view
+ */
+ fun sendListUpdate(listUpdate: List) {
+ _listAskOrBids.value = listUpdate
+ }
+
+ /**
+ * Returns a composite legend with the updated time of the remote service query
+ */
+ private fun getTimeUpdate(): String = String.format(
+ getApplication().applicationContext.getString(
+ R.string.cry_update_day,
+ ),
+ CRYUtils.getSaveTime(getApplication().applicationContext),
+ )
+}
diff --git a/app/src/main/java/com/javg/cryptocurrencies/view/viewmodel/CRYHomeVM.kt b/app/src/main/java/com/javg/cryptocurrencies/view/viewmodel/CRYHomeVM.kt
new file mode 100644
index 000000000..af5386dfb
--- /dev/null
+++ b/app/src/main/java/com/javg/cryptocurrencies/view/viewmodel/CRYHomeVM.kt
@@ -0,0 +1,98 @@
+package com.javg.cryptocurrencies.view.viewmodel
+
+import android.app.Application
+import androidx.lifecycle.AndroidViewModel
+import androidx.lifecycle.LiveData
+import androidx.lifecycle.MutableLiveData
+import androidx.lifecycle.viewModelScope
+import com.javg.cryptocurrencies.R
+import com.javg.cryptocurrencies.data.domain.CRYBookUseCase
+import com.javg.cryptocurrencies.data.model.CRYBook
+import com.javg.cryptocurrencies.utils.CRYUtils
+import dagger.hilt.android.lifecycle.HiltViewModel
+import kotlinx.coroutines.launch
+import javax.inject.Inject
+
+/**
+ * @author Juan Vera Gomez
+ * Date modified 22/02/2023
+ *
+ * Contains the necessary functions to obtain the information
+ * from the cryptocurrency books
+ *
+ * @since 1.3
+ */
+@HiltViewModel
+class CRYHomeVM @Inject constructor(
+ application: Application,
+ private val bookUseCase: CRYBookUseCase,
+) : AndroidViewModel(application) {
+
+ private var _booksMap = MutableLiveData>>()
+ private var _books = MutableLiveData>()
+ private var _updateTime = MutableLiveData()
+ private var _chipsTitles = MutableLiveData>()
+ private var _equalBooks = MutableLiveData>()
+ private var _result = MutableLiveData()
+
+ val updateTime: LiveData
+ get() = _updateTime
+
+ val chipsTitles: LiveData>
+ get() = _chipsTitles
+
+ val equalBooks: LiveData>
+ get() = _equalBooks
+
+ val result: LiveData
+ get() = _result
+
+ init {
+ _updateTime.value = getTimeUpdate()
+ queryBookFlow()
+ }
+
+ /**
+ * It is responsible for requesting the list of books from the data
+ * layer which can be called with user interaction.
+ *
+ */
+ fun getBooks() {
+ val result = bookUseCase.getAvailableBooksRx()
+ _result.postValue(result)
+ }
+
+ /**
+ * Updates the view list with another list according to the
+ * position selected by the user
+ *
+ * @param position is the position of the user selection
+ */
+ fun updateListDifferentBook(position: Int) {
+ val book = _chipsTitles.value?.get(position)
+ _equalBooks.value = _booksMap.value?.get(book?.singleBook)
+ }
+
+ /**
+ * Returns a composite legend with the updated time of the remote service query
+ */
+ private fun getTimeUpdate(): String = String.format(getApplication().applicationContext.getString(R.string.cry_update_day), CRYUtils.getSaveTime(getApplication().applicationContext))
+
+ /**
+ * It is in charge of observing the changes of the list of books
+ * in the database and in case it has changed, it refreshes the view
+ * with the new information stored in it.
+ */
+ private fun queryBookFlow() {
+ viewModelScope.launch {
+ bookUseCase.queryBooks().collect {
+ if (it.isNotEmpty()) {
+ _books.value = bookUseCase.transformBooks(it)
+ _chipsTitles.value = bookUseCase.createListBookTitles(_books.value!!)
+ _booksMap.value = bookUseCase.createUniqueMap(_books.value!!)
+ _equalBooks.value = _booksMap.value?.get(_chipsTitles.value?.firstOrNull()?.singleBook)
+ }
+ }
+ }
+ }
+}
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 000000000..2b068d114
--- /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/background_button_line.xml b/app/src/main/res/drawable/background_button_line.xml
new file mode 100644
index 000000000..e23a78f47
--- /dev/null
+++ b/app/src/main/res/drawable/background_button_line.xml
@@ -0,0 +1,13 @@
+
+
+
+ -
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/drawable/background_button_oval.xml b/app/src/main/res/drawable/background_button_oval.xml
new file mode 100644
index 000000000..973225add
--- /dev/null
+++ b/app/src/main/res/drawable/background_button_oval.xml
@@ -0,0 +1,9 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/drawable/background_circle.xml b/app/src/main/res/drawable/background_circle.xml
new file mode 100644
index 000000000..e5e80db9f
--- /dev/null
+++ b/app/src/main/res/drawable/background_circle.xml
@@ -0,0 +1,8 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/drawable/background_header_rounded.xml b/app/src/main/res/drawable/background_header_rounded.xml
new file mode 100644
index 000000000..ebe9ff465
--- /dev/null
+++ b/app/src/main/res/drawable/background_header_rounded.xml
@@ -0,0 +1,9 @@
+
+
+
diff --git a/app/src/main/res/drawable/background_rounded_corner.xml b/app/src/main/res/drawable/background_rounded_corner.xml
new file mode 100644
index 000000000..686d35c31
--- /dev/null
+++ b/app/src/main/res/drawable/background_rounded_corner.xml
@@ -0,0 +1,9 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/drawable/ic_aave.xml b/app/src/main/res/drawable/ic_aave.xml
new file mode 100644
index 000000000..a74b65588
--- /dev/null
+++ b/app/src/main/res/drawable/ic_aave.xml
@@ -0,0 +1,24 @@
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/drawable/ic_arrow_down.xml b/app/src/main/res/drawable/ic_arrow_down.xml
new file mode 100644
index 000000000..dcfb7840c
--- /dev/null
+++ b/app/src/main/res/drawable/ic_arrow_down.xml
@@ -0,0 +1,5 @@
+
+
+
diff --git a/app/src/main/res/drawable/ic_arrow_left.xml b/app/src/main/res/drawable/ic_arrow_left.xml
new file mode 100644
index 000000000..3514225eb
--- /dev/null
+++ b/app/src/main/res/drawable/ic_arrow_left.xml
@@ -0,0 +1,5 @@
+
+
+
diff --git a/app/src/main/res/drawable/ic_arrow_right.xml b/app/src/main/res/drawable/ic_arrow_right.xml
new file mode 100644
index 000000000..5222707e6
--- /dev/null
+++ b/app/src/main/res/drawable/ic_arrow_right.xml
@@ -0,0 +1,21 @@
+
+
+
+
+
diff --git a/app/src/main/res/drawable/ic_arrow_up.xml b/app/src/main/res/drawable/ic_arrow_up.xml
new file mode 100644
index 000000000..46d5d587d
--- /dev/null
+++ b/app/src/main/res/drawable/ic_arrow_up.xml
@@ -0,0 +1,5 @@
+
+
+
diff --git a/app/src/main/res/drawable/ic_avalanche.xml b/app/src/main/res/drawable/ic_avalanche.xml
new file mode 100644
index 000000000..92080991e
--- /dev/null
+++ b/app/src/main/res/drawable/ic_avalanche.xml
@@ -0,0 +1,12 @@
+
+
+
+
diff --git a/app/src/main/res/drawable/ic_baseline_arrow_back_24.xml b/app/src/main/res/drawable/ic_baseline_arrow_back_24.xml
new file mode 100644
index 000000000..f4d35e884
--- /dev/null
+++ b/app/src/main/res/drawable/ic_baseline_arrow_back_24.xml
@@ -0,0 +1,5 @@
+
+
+
diff --git a/app/src/main/res/drawable/ic_binance.xml b/app/src/main/res/drawable/ic_binance.xml
new file mode 100644
index 000000000..6f409da39
--- /dev/null
+++ b/app/src/main/res/drawable/ic_binance.xml
@@ -0,0 +1,24 @@
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/drawable/ic_bitcoin.xml b/app/src/main/res/drawable/ic_bitcoin.xml
new file mode 100644
index 000000000..bc5a4ae08
--- /dev/null
+++ b/app/src/main/res/drawable/ic_bitcoin.xml
@@ -0,0 +1,12 @@
+
+
+
+
diff --git a/app/src/main/res/drawable/ic_bitcoin_cash.xml b/app/src/main/res/drawable/ic_bitcoin_cash.xml
new file mode 100644
index 000000000..1c4306433
--- /dev/null
+++ b/app/src/main/res/drawable/ic_bitcoin_cash.xml
@@ -0,0 +1,13 @@
+
+
+
+
diff --git a/app/src/main/res/drawable/ic_cardano.xml b/app/src/main/res/drawable/ic_cardano.xml
new file mode 100644
index 000000000..e014cdb55
--- /dev/null
+++ b/app/src/main/res/drawable/ic_cardano.xml
@@ -0,0 +1,13 @@
+
+
+
+
diff --git a/app/src/main/res/drawable/ic_chainlink.xml b/app/src/main/res/drawable/ic_chainlink.xml
new file mode 100644
index 000000000..1682c9783
--- /dev/null
+++ b/app/src/main/res/drawable/ic_chainlink.xml
@@ -0,0 +1,13 @@
+
+
+
+
diff --git a/app/src/main/res/drawable/ic_circle.xml b/app/src/main/res/drawable/ic_circle.xml
new file mode 100644
index 000000000..3dc5f0338
--- /dev/null
+++ b/app/src/main/res/drawable/ic_circle.xml
@@ -0,0 +1,5 @@
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/drawable/ic_close.xml b/app/src/main/res/drawable/ic_close.xml
new file mode 100644
index 000000000..98d97ad7b
--- /dev/null
+++ b/app/src/main/res/drawable/ic_close.xml
@@ -0,0 +1,5 @@
+
+
+
diff --git a/app/src/main/res/drawable/ic_compound.xml b/app/src/main/res/drawable/ic_compound.xml
new file mode 100644
index 000000000..26af67c82
--- /dev/null
+++ b/app/src/main/res/drawable/ic_compound.xml
@@ -0,0 +1,13 @@
+
+
+
+
diff --git a/app/src/main/res/drawable/ic_dai.xml b/app/src/main/res/drawable/ic_dai.xml
new file mode 100644
index 000000000..01180ab57
--- /dev/null
+++ b/app/src/main/res/drawable/ic_dai.xml
@@ -0,0 +1,12 @@
+
+
+
+
diff --git a/app/src/main/res/drawable/ic_dash.xml b/app/src/main/res/drawable/ic_dash.xml
new file mode 100644
index 000000000..b7aba599a
--- /dev/null
+++ b/app/src/main/res/drawable/ic_dash.xml
@@ -0,0 +1,13 @@
+
+
+
+
diff --git a/app/src/main/res/drawable/ic_default_book.xml b/app/src/main/res/drawable/ic_default_book.xml
new file mode 100644
index 000000000..815159ffa
--- /dev/null
+++ b/app/src/main/res/drawable/ic_default_book.xml
@@ -0,0 +1,730 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/drawable/ic_dfinity.xml b/app/src/main/res/drawable/ic_dfinity.xml
new file mode 100644
index 000000000..5a16557fb
--- /dev/null
+++ b/app/src/main/res/drawable/ic_dfinity.xml
@@ -0,0 +1,42 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/drawable/ic_dogecoin.xml b/app/src/main/res/drawable/ic_dogecoin.xml
new file mode 100644
index 000000000..c78168ce7
--- /dev/null
+++ b/app/src/main/res/drawable/ic_dogecoin.xml
@@ -0,0 +1,12 @@
+
+
+
+
diff --git a/app/src/main/res/drawable/ic_eos.xml b/app/src/main/res/drawable/ic_eos.xml
new file mode 100644
index 000000000..88c5d568d
--- /dev/null
+++ b/app/src/main/res/drawable/ic_eos.xml
@@ -0,0 +1,12 @@
+
+
+
+
diff --git a/app/src/main/res/drawable/ic_ethereum.xml b/app/src/main/res/drawable/ic_ethereum.xml
new file mode 100644
index 000000000..a75d0f266
--- /dev/null
+++ b/app/src/main/res/drawable/ic_ethereum.xml
@@ -0,0 +1,23 @@
+
+
+
+
+
+
diff --git a/app/src/main/res/drawable/ic_filecoin.xml b/app/src/main/res/drawable/ic_filecoin.xml
new file mode 100644
index 000000000..d84d776e7
--- /dev/null
+++ b/app/src/main/res/drawable/ic_filecoin.xml
@@ -0,0 +1,14 @@
+
+
+
+
diff --git a/app/src/main/res/drawable/ic_iota.xml b/app/src/main/res/drawable/ic_iota.xml
new file mode 100644
index 000000000..2fdd48dcf
--- /dev/null
+++ b/app/src/main/res/drawable/ic_iota.xml
@@ -0,0 +1,13 @@
+
+
+
+
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 000000000..c2cfce6f3
--- /dev/null
+++ b/app/src/main/res/drawable/ic_launcher_background.xml
@@ -0,0 +1,10 @@
+
+
+
+
diff --git a/app/src/main/res/drawable/ic_litecoin.xml b/app/src/main/res/drawable/ic_litecoin.xml
new file mode 100644
index 000000000..938c758bd
--- /dev/null
+++ b/app/src/main/res/drawable/ic_litecoin.xml
@@ -0,0 +1,13 @@
+
+
+
+
diff --git a/app/src/main/res/drawable/ic_monero.xml b/app/src/main/res/drawable/ic_monero.xml
new file mode 100644
index 000000000..f77d7adf8
--- /dev/null
+++ b/app/src/main/res/drawable/ic_monero.xml
@@ -0,0 +1,23 @@
+
+
+
+
+
+
+
diff --git a/app/src/main/res/drawable/ic_more_circle.xml b/app/src/main/res/drawable/ic_more_circle.xml
new file mode 100644
index 000000000..c5390aa94
--- /dev/null
+++ b/app/src/main/res/drawable/ic_more_circle.xml
@@ -0,0 +1,17 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/drawable/ic_nem.xml b/app/src/main/res/drawable/ic_nem.xml
new file mode 100644
index 000000000..e4b6a610a
--- /dev/null
+++ b/app/src/main/res/drawable/ic_nem.xml
@@ -0,0 +1,24 @@
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/drawable/ic_neocoin.xml b/app/src/main/res/drawable/ic_neocoin.xml
new file mode 100644
index 000000000..84b73f106
--- /dev/null
+++ b/app/src/main/res/drawable/ic_neocoin.xml
@@ -0,0 +1,13 @@
+
+
+
+
diff --git a/app/src/main/res/drawable/ic_pancakeswap.xml b/app/src/main/res/drawable/ic_pancakeswap.xml
new file mode 100644
index 000000000..ba7f77c70
--- /dev/null
+++ b/app/src/main/res/drawable/ic_pancakeswap.xml
@@ -0,0 +1,26 @@
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/drawable/ic_polkadot.xml b/app/src/main/res/drawable/ic_polkadot.xml
new file mode 100644
index 000000000..5499ca0ae
--- /dev/null
+++ b/app/src/main/res/drawable/ic_polkadot.xml
@@ -0,0 +1,18 @@
+
+
+
+
+
+
diff --git a/app/src/main/res/drawable/ic_polygon.xml b/app/src/main/res/drawable/ic_polygon.xml
new file mode 100644
index 000000000..341096bae
--- /dev/null
+++ b/app/src/main/res/drawable/ic_polygon.xml
@@ -0,0 +1,12 @@
+
+
+
+
diff --git a/app/src/main/res/drawable/ic_right_double_arrow.xml b/app/src/main/res/drawable/ic_right_double_arrow.xml
new file mode 100644
index 000000000..e733f34c6
--- /dev/null
+++ b/app/src/main/res/drawable/ic_right_double_arrow.xml
@@ -0,0 +1,5 @@
+
+
+
+
diff --git a/app/src/main/res/drawable/ic_shiba.xml b/app/src/main/res/drawable/ic_shiba.xml
new file mode 100644
index 000000000..94c5d0f52
--- /dev/null
+++ b/app/src/main/res/drawable/ic_shiba.xml
@@ -0,0 +1,48 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/drawable/ic_solana.xml b/app/src/main/res/drawable/ic_solana.xml
new file mode 100644
index 000000000..e4f1848c4
--- /dev/null
+++ b/app/src/main/res/drawable/ic_solana.xml
@@ -0,0 +1,30 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/drawable/ic_stellar.xml b/app/src/main/res/drawable/ic_stellar.xml
new file mode 100644
index 000000000..73a449b09
--- /dev/null
+++ b/app/src/main/res/drawable/ic_stellar.xml
@@ -0,0 +1,15 @@
+
+
+
+
+
diff --git a/app/src/main/res/drawable/ic_terra.xml b/app/src/main/res/drawable/ic_terra.xml
new file mode 100644
index 000000000..86dd50160
--- /dev/null
+++ b/app/src/main/res/drawable/ic_terra.xml
@@ -0,0 +1,18 @@
+
+
+
+
+
+
diff --git a/app/src/main/res/drawable/ic_tether.xml b/app/src/main/res/drawable/ic_tether.xml
new file mode 100644
index 000000000..ee6effe0f
--- /dev/null
+++ b/app/src/main/res/drawable/ic_tether.xml
@@ -0,0 +1,12 @@
+
+
+
+
diff --git a/app/src/main/res/drawable/ic_theta.xml b/app/src/main/res/drawable/ic_theta.xml
new file mode 100644
index 000000000..1c6a9abbf
--- /dev/null
+++ b/app/src/main/res/drawable/ic_theta.xml
@@ -0,0 +1,28 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/drawable/ic_tron.xml b/app/src/main/res/drawable/ic_tron.xml
new file mode 100644
index 000000000..f2126aa73
--- /dev/null
+++ b/app/src/main/res/drawable/ic_tron.xml
@@ -0,0 +1,13 @@
+
+
+
+
diff --git a/app/src/main/res/drawable/ic_uniswap.xml b/app/src/main/res/drawable/ic_uniswap.xml
new file mode 100644
index 000000000..bc0ad65fd
--- /dev/null
+++ b/app/src/main/res/drawable/ic_uniswap.xml
@@ -0,0 +1,38 @@
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/drawable/ic_vechain.xml b/app/src/main/res/drawable/ic_vechain.xml
new file mode 100644
index 000000000..592ade125
--- /dev/null
+++ b/app/src/main/res/drawable/ic_vechain.xml
@@ -0,0 +1,30 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/drawable/ic_wrapped_bitcoin.xml b/app/src/main/res/drawable/ic_wrapped_bitcoin.xml
new file mode 100644
index 000000000..5bec297b9
--- /dev/null
+++ b/app/src/main/res/drawable/ic_wrapped_bitcoin.xml
@@ -0,0 +1,27 @@
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/drawable/ic_xrp.xml b/app/src/main/res/drawable/ic_xrp.xml
new file mode 100644
index 000000000..34096e5d0
--- /dev/null
+++ b/app/src/main/res/drawable/ic_xrp.xml
@@ -0,0 +1,15 @@
+
+
+
+
+
diff --git a/app/src/main/res/drawable/ic_zcash.xml b/app/src/main/res/drawable/ic_zcash.xml
new file mode 100644
index 000000000..ae3962bde
--- /dev/null
+++ b/app/src/main/res/drawable/ic_zcash.xml
@@ -0,0 +1,13 @@
+
+
+
+
diff --git a/app/src/main/res/drawable/logoapp2.xml b/app/src/main/res/drawable/logoapp2.xml
new file mode 100644
index 000000000..44e55cf12
--- /dev/null
+++ b/app/src/main/res/drawable/logoapp2.xml
@@ -0,0 +1,43 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/drawable/splash_screen.xml b/app/src/main/res/drawable/splash_screen.xml
new file mode 100644
index 000000000..884a54955
--- /dev/null
+++ b/app/src/main/res/drawable/splash_screen.xml
@@ -0,0 +1,10 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/cry_activity.xml b/app/src/main/res/layout/cry_activity.xml
new file mode 100644
index 000000000..2c0e215ea
--- /dev/null
+++ b/app/src/main/res/layout/cry_activity.xml
@@ -0,0 +1,8 @@
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/cry_ask_item.xml b/app/src/main/res/layout/cry_ask_item.xml
new file mode 100644
index 000000000..0171176f8
--- /dev/null
+++ b/app/src/main/res/layout/cry_ask_item.xml
@@ -0,0 +1,55 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/cry_book_fragment.xml b/app/src/main/res/layout/cry_book_fragment.xml
new file mode 100644
index 000000000..74241375a
--- /dev/null
+++ b/app/src/main/res/layout/cry_book_fragment.xml
@@ -0,0 +1,93 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/cry_book_item.xml b/app/src/main/res/layout/cry_book_item.xml
new file mode 100644
index 000000000..b95c84770
--- /dev/null
+++ b/app/src/main/res/layout/cry_book_item.xml
@@ -0,0 +1,92 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/cry_card_price_include.xml b/app/src/main/res/layout/cry_card_price_include.xml
new file mode 100644
index 000000000..8b68d27f7
--- /dev/null
+++ b/app/src/main/res/layout/cry_card_price_include.xml
@@ -0,0 +1,127 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/cry_chips_item.xml b/app/src/main/res/layout/cry_chips_item.xml
new file mode 100644
index 000000000..416ae16df
--- /dev/null
+++ b/app/src/main/res/layout/cry_chips_item.xml
@@ -0,0 +1,51 @@
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/cry_detail_book_fragment.xml b/app/src/main/res/layout/cry_detail_book_fragment.xml
new file mode 100644
index 000000000..767cbc342
--- /dev/null
+++ b/app/src/main/res/layout/cry_detail_book_fragment.xml
@@ -0,0 +1,190 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/cry_header_book_include.xml b/app/src/main/res/layout/cry_header_book_include.xml
new file mode 100644
index 000000000..f8e517362
--- /dev/null
+++ b/app/src/main/res/layout/cry_header_book_include.xml
@@ -0,0 +1,35 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/cry_header_top_bar_include.xml b/app/src/main/res/layout/cry_header_top_bar_include.xml
new file mode 100644
index 000000000..e7f081cb8
--- /dev/null
+++ b/app/src/main/res/layout/cry_header_top_bar_include.xml
@@ -0,0 +1,52 @@
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/cry_nav_host_main.xml b/app/src/main/res/layout/cry_nav_host_main.xml
new file mode 100644
index 000000000..cd9d1142e
--- /dev/null
+++ b/app/src/main/res/layout/cry_nav_host_main.xml
@@ -0,0 +1,20 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/cry_shimmer_detail_book.xml b/app/src/main/res/layout/cry_shimmer_detail_book.xml
new file mode 100644
index 000000000..cf53ea09e
--- /dev/null
+++ b/app/src/main/res/layout/cry_shimmer_detail_book.xml
@@ -0,0 +1,349 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/cry_shimmer_layout.xml b/app/src/main/res/layout/cry_shimmer_layout.xml
new file mode 100644
index 000000000..03eae1cb2
--- /dev/null
+++ b/app/src/main/res/layout/cry_shimmer_layout.xml
@@ -0,0 +1,48 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml b/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
new file mode 100644
index 000000000..036d09bc5
--- /dev/null
+++ b/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
@@ -0,0 +1,5 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml b/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml
new file mode 100644
index 000000000..036d09bc5
--- /dev/null
+++ b/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml
@@ -0,0 +1,5 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/mipmap-hdpi/ic_launcher.png b/app/src/main/res/mipmap-hdpi/ic_launcher.png
new file mode 100644
index 000000000..e4810067f
Binary files /dev/null and b/app/src/main/res/mipmap-hdpi/ic_launcher.png differ
diff --git a/app/src/main/res/mipmap-hdpi/ic_launcher_foreground.png b/app/src/main/res/mipmap-hdpi/ic_launcher_foreground.png
new file mode 100644
index 000000000..82def3db1
Binary files /dev/null and b/app/src/main/res/mipmap-hdpi/ic_launcher_foreground.png differ
diff --git a/app/src/main/res/mipmap-hdpi/ic_launcher_round.png b/app/src/main/res/mipmap-hdpi/ic_launcher_round.png
new file mode 100644
index 000000000..80ff445d5
Binary files /dev/null and b/app/src/main/res/mipmap-hdpi/ic_launcher_round.png differ
diff --git a/app/src/main/res/mipmap-mdpi/ic_launcher.png b/app/src/main/res/mipmap-mdpi/ic_launcher.png
new file mode 100644
index 000000000..a09cffdcc
Binary files /dev/null and b/app/src/main/res/mipmap-mdpi/ic_launcher.png differ
diff --git a/app/src/main/res/mipmap-mdpi/ic_launcher_foreground.png b/app/src/main/res/mipmap-mdpi/ic_launcher_foreground.png
new file mode 100644
index 000000000..3171cae61
Binary files /dev/null and b/app/src/main/res/mipmap-mdpi/ic_launcher_foreground.png differ
diff --git a/app/src/main/res/mipmap-mdpi/ic_launcher_round.png b/app/src/main/res/mipmap-mdpi/ic_launcher_round.png
new file mode 100644
index 000000000..16a281cea
Binary files /dev/null and b/app/src/main/res/mipmap-mdpi/ic_launcher_round.png differ
diff --git a/app/src/main/res/mipmap-xhdpi/ic_launcher.png b/app/src/main/res/mipmap-xhdpi/ic_launcher.png
new file mode 100644
index 000000000..e6e0ed3e0
Binary files /dev/null and b/app/src/main/res/mipmap-xhdpi/ic_launcher.png differ
diff --git a/app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.png b/app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.png
new file mode 100644
index 000000000..2043a252d
Binary files /dev/null and b/app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.png differ
diff --git a/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png b/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png
new file mode 100644
index 000000000..9a9f53d49
Binary files /dev/null and b/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png differ
diff --git a/app/src/main/res/mipmap-xxhdpi/ic_launcher.png b/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
new file mode 100644
index 000000000..04c639f72
Binary files /dev/null and b/app/src/main/res/mipmap-xxhdpi/ic_launcher.png differ
diff --git a/app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.png b/app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.png
new file mode 100644
index 000000000..81068d787
Binary files /dev/null and b/app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.png differ
diff --git a/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png b/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png
new file mode 100644
index 000000000..a55493399
Binary files /dev/null and b/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png differ
diff --git a/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
new file mode 100644
index 000000000..696565876
Binary files /dev/null and b/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png differ
diff --git a/app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.png b/app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.png
new file mode 100644
index 000000000..b0b9312c8
Binary files /dev/null and b/app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.png differ
diff --git a/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png b/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png
new file mode 100644
index 000000000..0613ac42b
Binary files /dev/null and b/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png differ
diff --git a/app/src/main/res/navigation/cry_nav_graph_main.xml b/app/src/main/res/navigation/cry_nav_graph_main.xml
new file mode 100644
index 000000000..044dc0385
--- /dev/null
+++ b/app/src/main/res/navigation/cry_nav_graph_main.xml
@@ -0,0 +1,28 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/values/colors.xml b/app/src/main/res/values/colors.xml
new file mode 100644
index 000000000..a46c0ce15
--- /dev/null
+++ b/app/src/main/res/values/colors.xml
@@ -0,0 +1,31 @@
+
+
+ #FFBB86FC
+ #FF6200EE
+ #FF3700B3
+ #FF03DAC5
+ #FF018786
+ #FF000000
+ #FFFFFFFF
+ #BFBDBD
+ #918F8F
+ #726B6B
+ #0189ff
+ #286396
+ #F6A700
+ #e28000
+ #e2504c
+ #ffd792
+
+ #0AB10A
+ #B00A0A
+
+ #075366
+ #00303c
+ #f3f3f3
+ #bac7d8
+ #3db5e6
+ #00FFFFFF
+ #0C7A96
+ #BFEB87
+
\ No newline at end of file
diff --git a/app/src/main/res/values/dimens.xml b/app/src/main/res/values/dimens.xml
new file mode 100644
index 000000000..c5702b42e
--- /dev/null
+++ b/app/src/main/res/values/dimens.xml
@@ -0,0 +1,40 @@
+
+
+ 10sp
+ 12sp
+ 16sp
+ 18sp
+ 20sp
+ 22sp
+ 24sp
+ 32sp
+ 34sp
+ 36sp
+ 38sp
+ 1dp
+ 2dp
+ 4dp
+ 6dp
+ 8dp
+ 10dp
+ 12dp
+ 14dp
+ 16dp
+ 20dp
+ 22dp
+ 24dp
+ 28dp
+ 32dp
+ 34dp
+ 36dp
+ 38dp
+ 40dp
+ 50dp
+ 64dp
+ 84dp
+ 100dp
+ 150dp
+ 260dp
+ 300dp
+ 400dp
+
\ No newline at end of file
diff --git a/app/src/main/res/values/ic_launcher_background.xml b/app/src/main/res/values/ic_launcher_background.xml
new file mode 100644
index 000000000..9f804ca0a
--- /dev/null
+++ b/app/src/main/res/values/ic_launcher_background.xml
@@ -0,0 +1,4 @@
+
+
+ #F6A700
+
\ No newline at end of file
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
new file mode 100644
index 000000000..f70ad1d86
--- /dev/null
+++ b/app/src/main/res/values/strings.xml
@@ -0,0 +1,23 @@
+
+ CryptoBook
+ BTC
+ MXN
+ Monto minimo: $.003
+ Monto maximo: $1000.00
+ Precio maximo: $1000000.00
+ Ultimo precio
+ Más bajo
+ Más alto
+ Precio
+ Monto:
+ Demanda
+ Ofertas
+ $%1$s
+ Ultima actualización\n %1$s
+ Hay conexión a internet
+ Revisa tu conexión a internet
+ $%1$s
+ Detalle de la criptomoneda
+ Por el momento no hay información
+ $410060.00
+
\ No newline at end of file
diff --git a/app/src/main/res/values/themes.xml b/app/src/main/res/values/themes.xml
new file mode 100644
index 000000000..0ba489db2
--- /dev/null
+++ b/app/src/main/res/values/themes.xml
@@ -0,0 +1,64 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/xml/backup_rules.xml b/app/src/main/res/xml/backup_rules.xml
new file mode 100644
index 000000000..fa0f996d2
--- /dev/null
+++ b/app/src/main/res/xml/backup_rules.xml
@@ -0,0 +1,13 @@
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/xml/data_extraction_rules.xml b/app/src/main/res/xml/data_extraction_rules.xml
new file mode 100644
index 000000000..9ee9997b0
--- /dev/null
+++ b/app/src/main/res/xml/data_extraction_rules.xml
@@ -0,0 +1,19 @@
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/test/java/com/javg/cryptocurrencies/ExampleUnitTest.kt b/app/src/test/java/com/javg/cryptocurrencies/ExampleUnitTest.kt
new file mode 100644
index 000000000..45e08c206
--- /dev/null
+++ b/app/src/test/java/com/javg/cryptocurrencies/ExampleUnitTest.kt
@@ -0,0 +1,17 @@
+package com.javg.cryptocurrencies
+
+import org.junit.Test
+
+import org.junit.Assert.*
+
+/**
+ * Example local unit test, which will execute on the development machine (host).
+ *
+ * See [testing documentation](http://d.android.com/tools/testing).
+ */
+class ExampleUnitTest {
+ @Test
+ fun addition_isCorrect() {
+ assertEquals(4, 2 + 2)
+ }
+}
\ No newline at end of file
diff --git a/app/src/test/java/com/javg/cryptocurrencies/data/mapper/CRYMapperKtTest.kt b/app/src/test/java/com/javg/cryptocurrencies/data/mapper/CRYMapperKtTest.kt
new file mode 100644
index 000000000..18b3a4e8d
--- /dev/null
+++ b/app/src/test/java/com/javg/cryptocurrencies/data/mapper/CRYMapperKtTest.kt
@@ -0,0 +1,35 @@
+package com.javg.cryptocurrencies.data.mapper
+
+import com.javg.cryptocurrencies.data.model.CRYBookResponse
+import com.google.common.truth.Truth.assertThat
+import com.javg.cryptocurrencies.data.db.entity.CRYBookEntity
+import com.javg.cryptocurrencies.data.model.CRYBook
+import org.junit.Test
+
+class CRYMapperKtTest {
+
+ @Test
+ fun toEntity() {
+ val target = CRYBookResponse(
+ book = "btc_mxn",
+ minimumPrice = ".003",
+ maximumPrice = "1000.00",
+ minimumAmount = "100.00",
+ maximumAmount = "1000000.00",
+ minimumValue = "25.00",
+ maximumValue = "1000000.00",
+ tickSize = "10",
+ defaultChart = "tradingview")
+ assertThat(target.toEntity()).isInstanceOf(CRYBookEntity::class.java)
+ }
+
+ @Test
+ fun toDomain() {
+ val target = CRYBookEntity(
+ book = "btc_mxn",
+ minimumPrice = ".003",
+ maximumPrice = "1000.00")
+
+ assertThat(target.toDomain()).isInstanceOf(CRYBook::class.java)
+ }
+}
\ No newline at end of file
diff --git a/app/src/test/java/com/javg/cryptocurrencies/data/model/CRYBookTest.kt b/app/src/test/java/com/javg/cryptocurrencies/data/model/CRYBookTest.kt
new file mode 100644
index 000000000..615d1db06
--- /dev/null
+++ b/app/src/test/java/com/javg/cryptocurrencies/data/model/CRYBookTest.kt
@@ -0,0 +1,234 @@
+package com.javg.cryptocurrencies.data.model
+
+import com.google.common.truth.Truth.assertThat
+import org.junit.Test
+
+class CRYBookTest{
+ private val target = CRYBook(
+ book = "btc_mxn",
+ imageUrl = "https://cryptoicons.org/api/icon/dai/200",
+ bookDestination = "https://cryptoicons.org/api/icon/dai/200")
+ private val target2 = CRYDetailBook()
+ private val target3 = CRYAskOrBids(
+ book = "btc_mxn",
+ price = "123",
+ amount = "123456")
+
+ @Test
+ fun `test cryBook with custom properties`(){
+ assertThat(target.book).isNotEmpty()
+ assertThat(target.imageUrl).isNotEmpty()
+ assertThat(target.bookDestination).isNotEmpty()
+ }
+
+ @Test
+ fun `test cryBook with empty properties`(){
+ target.apply {
+ book = ""
+ imageUrl = ""
+ bookDestination = ""
+ }
+ assertThat(target.book).isEmpty()
+ assertThat(target.imageUrl).isEmpty()
+ assertThat(target.bookDestination).isEmpty()
+ }
+
+ @Test
+ fun `test cryDetailBook default properties`(){
+ assertThat(target2.high).isEmpty()
+ assertThat(target2.low).isEmpty()
+ assertThat(target2.last).isEmpty()
+ assertThat(target2.askList).isNull()
+ assertThat(target2.bidsList).isNull()
+ }
+
+ @Test
+ fun `test cryDetailBook with a custom property`(){
+ target2.apply {
+ high = "1234"
+ }
+ assertThat(target2.high).isNotEmpty()
+ assertThat(target2.low).isEmpty()
+ assertThat(target2.last).isEmpty()
+ assertThat(target2.askList).isNull()
+ assertThat(target2.bidsList).isNull()
+ }
+
+ @Test
+ fun `test cryDetailBook with two custom property`(){
+ target2.apply {
+ high = "1234"
+ low = "123455667"
+ }
+ assertThat(target2.high).isNotEmpty()
+ assertThat(target2.low).isNotEmpty()
+ assertThat(target2.last).isEmpty()
+ assertThat(target2.askList).isNull()
+ assertThat(target2.bidsList).isNull()
+ }
+
+ @Test
+ fun `test cryDetailBook with three custom property`(){
+ target2.apply {
+ high = "1234"
+ low = "123455667"
+ last = "123456789"
+ }
+ assertThat(target2.high).isNotEmpty()
+ assertThat(target2.low).isNotEmpty()
+ assertThat(target2.last).isNotEmpty()
+ assertThat(target2.askList).isNull()
+ assertThat(target2.bidsList).isNull()
+ }
+
+ @Test
+ fun `test cryDetailBook with empty list ask`(){
+ target2.apply {
+ high = "1234"
+ low = "123455667"
+ last = "123456789"
+ askList = mutableListOf()
+ }
+ assertThat(target2.high).isNotEmpty()
+ assertThat(target2.low).isNotEmpty()
+ assertThat(target2.last).isNotEmpty()
+ assertThat(target2.askList).isEmpty()
+ assertThat(target2.bidsList).isNull()
+ }
+
+ @Test
+ fun `test cryDetailBook with empty list bids`(){
+ target2.apply {
+ high = "1234"
+ low = "123455667"
+ last = "123456789"
+ bidsList = mutableListOf()
+ }
+ assertThat(target2.high).isNotEmpty()
+ assertThat(target2.low).isNotEmpty()
+ assertThat(target2.last).isNotEmpty()
+ assertThat(target2.askList).isNull()
+ assertThat(target2.bidsList).isEmpty()
+ }
+
+ @Test
+ fun `test cryDetailBook with list ask`(){
+ target2.apply {
+ high = "1234"
+ low = "123455667"
+ last = "123456789"
+ askList = mutableListOf().apply {
+ add(CRYAskOrBids(
+ book = "12345",
+ price = "123",
+ amount = "1234567"))
+ }
+ }
+ assertThat(target2.high).isNotEmpty()
+ assertThat(target2.low).isNotEmpty()
+ assertThat(target2.last).isNotEmpty()
+ assertThat(target2.askList).isNotEmpty()
+ assertThat(target2.bidsList).isNull()
+ }
+
+ @Test
+ fun `test cryDetailBook with list bids`(){
+ target2.apply {
+ high = "1234"
+ low = "123455667"
+ last = "123456789"
+ bidsList = mutableListOf().apply {
+ add(CRYAskOrBids(
+ book = "12345",
+ price = "123",
+ amount = "1234567"))
+ }
+ }
+ assertThat(target2.high).isNotEmpty()
+ assertThat(target2.low).isNotEmpty()
+ assertThat(target2.last).isNotEmpty()
+ assertThat(target2.askList).isNull()
+ assertThat(target2.bidsList).isNotEmpty()
+ }
+
+ @Test
+ fun `test cryDetailBook custom all properties`(){
+ target2.apply {
+ high = "1234"
+ low = "123455667"
+ last = "123456789"
+ askList = mutableListOf().apply {
+ add(CRYAskOrBids(
+ book = "12345",
+ price = "123",
+ amount = "1234567"))
+ }
+ bidsList = mutableListOf().apply {
+ add(CRYAskOrBids(
+ book = "12345",
+ price = "123",
+ amount = "1234567"))
+ }
+ }
+ assertThat(target2.high).isNotEmpty()
+ assertThat(target2.low).isNotEmpty()
+ assertThat(target2.last).isNotEmpty()
+ assertThat(target2.askList).isNotEmpty()
+ assertThat(target2.bidsList).isNotEmpty()
+ }
+
+ @Test
+ fun `test cryAskOrBids with custom properties`(){
+ assertThat(target3.book).isNotEmpty()
+ assertThat(target3.price).isNotEmpty()
+ assertThat(target3.amount).isNotEmpty()
+ }
+
+ @Test
+ fun `test cryAskOrBids with empty properties`(){
+ target3.apply {
+ book = ""
+ price = ""
+ amount = ""
+ }
+ assertThat(target3.book).isEmpty()
+ assertThat(target3.price).isEmpty()
+ assertThat(target3.amount).isEmpty()
+ }
+
+ @Test
+ fun `test cryAskOrBids with empty book property`(){
+ target3.apply {
+ book = ""
+ price = "123"
+ amount = "123456789"
+ }
+ assertThat(target3.book).isEmpty()
+ assertThat(target3.price).isNotEmpty()
+ assertThat(target3.amount).isNotEmpty()
+ }
+
+ @Test
+ fun `test cryAskOrBids with empty price property`(){
+ target3.apply {
+ book = "btc_mxn"
+ price = ""
+ amount = "123456789"
+ }
+ assertThat(target3.book).isNotEmpty()
+ assertThat(target3.price).isEmpty()
+ assertThat(target3.amount).isNotEmpty()
+ }
+
+ @Test
+ fun `test cryAskOrBids with empty amount property`(){
+ target3.apply {
+ book = "btc_mxn"
+ price = "1234"
+ amount = ""
+ }
+ assertThat(target3.book).isNotEmpty()
+ assertThat(target3.price).isNotEmpty()
+ assertThat(target3.amount).isEmpty()
+ }
+}
\ No newline at end of file
diff --git a/app/src/test/java/com/javg/cryptocurrencies/ext/CryStringExtKtTest.kt b/app/src/test/java/com/javg/cryptocurrencies/ext/CryStringExtKtTest.kt
new file mode 100644
index 000000000..37c212cf5
--- /dev/null
+++ b/app/src/test/java/com/javg/cryptocurrencies/ext/CryStringExtKtTest.kt
@@ -0,0 +1,49 @@
+package com.javg.cryptocurrencies.ext
+
+import com.google.common.truth.Truth.assertThat
+import org.junit.Test
+
+class CryStringExtKtTest{
+
+ @Test
+ fun `test to separate string with symbol _ and get the first text`(){
+ val text = "btc_mxn".separateStringCoins()
+ assertThat(text).isEqualTo("btc")
+ }
+
+ @Test
+ fun `test to separate string without symbol _ and get the first text`(){
+ val text = "btc mxn".separateStringCoins()
+ assertThat(text).isEqualTo("btc mxn")
+ }
+
+ @Test
+ fun `test to separate empty string and get the first text`(){
+ val text = "".separateStringCoins()
+ assertThat(text).isEqualTo("")
+ }
+
+ @Test
+ fun `test to separate string with a symbol other than _`(){
+ val text = "btc%mxn".separateStringCoins()
+ assertThat(text).isEqualTo("btc%mxn")
+ }
+
+ @Test
+ fun `test to get the second text separated by the _ symbol`(){
+ val text = "btc_mxn".getSecondCoinsText()
+ assertThat(text).isEqualTo("mxn")
+ }
+
+ @Test
+ fun `test to get the second separated text without the _ symbol`(){
+ val text = "btc mxn".getSecondCoinsText()
+ assertThat(text).isEqualTo("btc mxn")
+ }
+
+ @Test
+ fun `test to get the second separated text`(){
+ val text = "btc".getSecondCoinsText()
+ assertThat(text).isEqualTo("btc")
+ }
+}
\ No newline at end of file
diff --git a/build.gradle b/build.gradle
new file mode 100644
index 000000000..6a5610e35
--- /dev/null
+++ b/build.gradle
@@ -0,0 +1,43 @@
+buildscript {
+ dependencies {
+ classpath "org.jlleitschuh.gradle:ktlint-gradle:10.2.0"
+ // NOTE: Do not place your application dependencies here; they belong
+ // in the individual module build.gradle files
+ }
+ ext {
+ compose_ui_version = '1.2.0'
+ room_version = '2.5.0'
+ googleMaterialVersion = '1.4.0'
+ retrofit_version = '2.9.0'
+ interceptor_version = '4.9.3'
+ hilt_version = '2.42'
+ hilt_compiler_version = '1.0.0'
+ lifecycle_version = '2.5.0'
+ runtime_livedata_version = '1.1.1'
+ constraintlayout_version = '2.2.0-alpha05'
+ constraintlayout_compose_version = '1.1.0-alpha05'
+ recyclerview_version = '1.2.1'
+ recyclerview_selection_version = '1.1.0'
+ fragment_ktx_version = '1.3.2'
+ fragment_version = '1.3.5'
+ appcompat_version = '1.2.0'
+ legacy_support_version = '1.0.0'
+ activity_ktx_version = '1.2.3'
+ cardview_version = '1.0.0'
+ glide_version = '4.13.0'
+ lottie_version = '3.4.0'
+ viewpager2_version = '1.0.0'
+ navigation_fragment_version = '2.5.3'
+ rx_android_version = '1.2.1'
+ rx_java_version = '1.2.7'
+ rxjava2_java_version = '2.2.8'
+ rxjava2_android_version = '2.1.1'
+ }
+}// Top-level build file where you can add configuration options common to all sub-projects/modules.
+plugins {
+ id 'com.android.application' version '7.4.0' apply false
+ id 'com.android.library' version '7.4.0' apply false
+ id 'org.jetbrains.kotlin.android' version '1.7.0' apply false
+ id 'com.google.dagger.hilt.android' version '2.42' apply false
+ //id 'org.jlleitschuh.gradle:ktlint-gradle' version '10.2.0' apply false
+}
\ No newline at end of file
diff --git a/gradle.properties b/gradle.properties
new file mode 100644
index 000000000..3c5031eb7
--- /dev/null
+++ b/gradle.properties
@@ -0,0 +1,23 @@
+# Project-wide Gradle settings.
+# IDE (e.g. Android Studio) users:
+# Gradle settings configured through the IDE *will override*
+# any settings specified in this file.
+# For more details on how to configure your build environment visit
+# http://www.gradle.org/docs/current/userguide/build_environment.html
+# Specifies the JVM arguments used for the daemon process.
+# The setting is particularly useful for tweaking memory settings.
+org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8
+# When configured, Gradle will run in incubating parallel mode.
+# This option should only be used with decoupled projects. More details, visit
+# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
+# org.gradle.parallel=true
+# AndroidX package structure to make it clearer which packages are bundled with the
+# Android operating system, and which are packaged with your app's APK
+# https://developer.android.com/topic/libraries/support-library/androidx-rn
+android.useAndroidX=true
+# Kotlin code style for this project: "official" or "obsolete":
+kotlin.code.style=official
+# Enables namespacing of each library's R class so that its R class includes only the
+# resources declared in the library itself and none from the library's dependencies,
+# thereby reducing the size of the R class for that library
+android.nonTransitiveRClass=true
\ No newline at end of file
diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar
new file mode 100644
index 000000000..e708b1c02
Binary files /dev/null and b/gradle/wrapper/gradle-wrapper.jar differ
diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties
new file mode 100644
index 000000000..9a712e57f
--- /dev/null
+++ b/gradle/wrapper/gradle-wrapper.properties
@@ -0,0 +1,6 @@
+#Tue Jan 24 12:29:26 CST 2023
+distributionBase=GRADLE_USER_HOME
+distributionUrl=https\://services.gradle.org/distributions/gradle-7.5-bin.zip
+distributionPath=wrapper/dists
+zipStorePath=wrapper/dists
+zipStoreBase=GRADLE_USER_HOME
diff --git a/gradlew b/gradlew
new file mode 100755
index 000000000..4f906e0c8
--- /dev/null
+++ b/gradlew
@@ -0,0 +1,185 @@
+#!/usr/bin/env sh
+
+#
+# Copyright 2015 the original author or authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+##############################################################################
+##
+## Gradle start up script for UN*X
+##
+##############################################################################
+
+# Attempt to set APP_HOME
+# Resolve links: $0 may be a link
+PRG="$0"
+# Need this for relative symlinks.
+while [ -h "$PRG" ] ; do
+ ls=`ls -ld "$PRG"`
+ link=`expr "$ls" : '.*-> \(.*\)$'`
+ if expr "$link" : '/.*' > /dev/null; then
+ PRG="$link"
+ else
+ PRG=`dirname "$PRG"`"/$link"
+ fi
+done
+SAVED="`pwd`"
+cd "`dirname \"$PRG\"`/" >/dev/null
+APP_HOME="`pwd -P`"
+cd "$SAVED" >/dev/null
+
+APP_NAME="Gradle"
+APP_BASE_NAME=`basename "$0"`
+
+# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
+
+# Use the maximum available, or set MAX_FD != -1 to use that value.
+MAX_FD="maximum"
+
+warn () {
+ echo "$*"
+}
+
+die () {
+ echo
+ echo "$*"
+ echo
+ exit 1
+}
+
+# OS specific support (must be 'true' or 'false').
+cygwin=false
+msys=false
+darwin=false
+nonstop=false
+case "`uname`" in
+ CYGWIN* )
+ cygwin=true
+ ;;
+ Darwin* )
+ darwin=true
+ ;;
+ MINGW* )
+ msys=true
+ ;;
+ NONSTOP* )
+ nonstop=true
+ ;;
+esac
+
+CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
+
+
+# Determine the Java command to use to start the JVM.
+if [ -n "$JAVA_HOME" ] ; then
+ if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
+ # IBM's JDK on AIX uses strange locations for the executables
+ JAVACMD="$JAVA_HOME/jre/sh/java"
+ else
+ JAVACMD="$JAVA_HOME/bin/java"
+ fi
+ if [ ! -x "$JAVACMD" ] ; then
+ die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+ fi
+else
+ JAVACMD="java"
+ which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+fi
+
+# Increase the maximum file descriptors if we can.
+if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
+ MAX_FD_LIMIT=`ulimit -H -n`
+ if [ $? -eq 0 ] ; then
+ if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
+ MAX_FD="$MAX_FD_LIMIT"
+ fi
+ ulimit -n $MAX_FD
+ if [ $? -ne 0 ] ; then
+ warn "Could not set maximum file descriptor limit: $MAX_FD"
+ fi
+ else
+ warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
+ fi
+fi
+
+# For Darwin, add options to specify how the application appears in the dock
+if $darwin; then
+ GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
+fi
+
+# For Cygwin or MSYS, switch paths to Windows format before running java
+if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then
+ APP_HOME=`cygpath --path --mixed "$APP_HOME"`
+ CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
+
+ JAVACMD=`cygpath --unix "$JAVACMD"`
+
+ # We build the pattern for arguments to be converted via cygpath
+ ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
+ SEP=""
+ for dir in $ROOTDIRSRAW ; do
+ ROOTDIRS="$ROOTDIRS$SEP$dir"
+ SEP="|"
+ done
+ OURCYGPATTERN="(^($ROOTDIRS))"
+ # Add a user-defined pattern to the cygpath arguments
+ if [ "$GRADLE_CYGPATTERN" != "" ] ; then
+ OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
+ fi
+ # Now convert the arguments - kludge to limit ourselves to /bin/sh
+ i=0
+ for arg in "$@" ; do
+ CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
+ CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
+
+ if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
+ eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
+ else
+ eval `echo args$i`="\"$arg\""
+ fi
+ i=`expr $i + 1`
+ done
+ case $i in
+ 0) set -- ;;
+ 1) set -- "$args0" ;;
+ 2) set -- "$args0" "$args1" ;;
+ 3) set -- "$args0" "$args1" "$args2" ;;
+ 4) set -- "$args0" "$args1" "$args2" "$args3" ;;
+ 5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
+ 6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
+ 7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
+ 8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
+ 9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
+ esac
+fi
+
+# Escape application args
+save () {
+ for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
+ echo " "
+}
+APP_ARGS=`save "$@"`
+
+# Collect all arguments for the java command, following the shell quoting and substitution rules
+eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
+
+exec "$JAVACMD" "$@"
diff --git a/gradlew.bat b/gradlew.bat
new file mode 100644
index 000000000..ac1b06f93
--- /dev/null
+++ b/gradlew.bat
@@ -0,0 +1,89 @@
+@rem
+@rem Copyright 2015 the original author or authors.
+@rem
+@rem Licensed under the Apache License, Version 2.0 (the "License");
+@rem you may not use this file except in compliance with the License.
+@rem You may obtain a copy of the License at
+@rem
+@rem https://www.apache.org/licenses/LICENSE-2.0
+@rem
+@rem Unless required by applicable law or agreed to in writing, software
+@rem distributed under the License is distributed on an "AS IS" BASIS,
+@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+@rem See the License for the specific language governing permissions and
+@rem limitations under the License.
+@rem
+
+@if "%DEBUG%" == "" @echo off
+@rem ##########################################################################
+@rem
+@rem Gradle startup script for Windows
+@rem
+@rem ##########################################################################
+
+@rem Set local scope for the variables with windows NT shell
+if "%OS%"=="Windows_NT" setlocal
+
+set DIRNAME=%~dp0
+if "%DIRNAME%" == "" set DIRNAME=.
+set APP_BASE_NAME=%~n0
+set APP_HOME=%DIRNAME%
+
+@rem Resolve any "." and ".." in APP_HOME to make it shorter.
+for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
+
+@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
+
+@rem Find java.exe
+if defined JAVA_HOME goto findJavaFromJavaHome
+
+set JAVA_EXE=java.exe
+%JAVA_EXE% -version >NUL 2>&1
+if "%ERRORLEVEL%" == "0" goto execute
+
+echo.
+echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:findJavaFromJavaHome
+set JAVA_HOME=%JAVA_HOME:"=%
+set JAVA_EXE=%JAVA_HOME%/bin/java.exe
+
+if exist "%JAVA_EXE%" goto execute
+
+echo.
+echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:execute
+@rem Setup the command line
+
+set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
+
+
+@rem Execute Gradle
+"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
+
+:end
+@rem End local scope for the variables with windows NT shell
+if "%ERRORLEVEL%"=="0" goto mainEnd
+
+:fail
+rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
+rem the _cmd.exe /c_ return code!
+if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
+exit /b 1
+
+:mainEnd
+if "%OS%"=="Windows_NT" endlocal
+
+:omega
diff --git a/settings.gradle b/settings.gradle
new file mode 100644
index 000000000..31df13750
--- /dev/null
+++ b/settings.gradle
@@ -0,0 +1,16 @@
+pluginManagement {
+ repositories {
+ google()
+ mavenCentral()
+ gradlePluginPortal()
+ }
+}
+dependencyResolutionManagement {
+ repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
+ repositories {
+ google()
+ mavenCentral()
+ }
+}
+rootProject.name = "Cryptocurrencies"
+include ':app'