├── .github
└── workflows
│ └── test.yaml
├── .gitignore
├── LICENSE
├── README.md
├── _config.yml
├── app
├── .gitignore
├── build.gradle
├── google-services.json
├── proguard-rules.pro
├── release
│ ├── app.aab
│ └── output.json
└── src
│ └── main
│ ├── AndroidManifest.xml
│ ├── assets
│ └── currencyinfo.json
│ ├── ic_launcher-web.png
│ ├── java
│ └── com
│ │ └── binarybricks
│ │ └── coinbit
│ │ ├── CoinBitApplication.kt
│ │ ├── data
│ │ ├── CoinBitCache.kt
│ │ ├── PreferenceManager.kt
│ │ └── database
│ │ │ ├── BigDecimalConverter.kt
│ │ │ ├── CoinBitDatabase.kt
│ │ │ ├── dao
│ │ │ ├── CoinTransactionDao.kt
│ │ │ ├── ExchangeDao.kt
│ │ │ └── WatchedCoinDao.kt
│ │ │ └── entities
│ │ │ ├── Coin.kt
│ │ │ ├── CoinTransaction.kt
│ │ │ ├── Exchange.kt
│ │ │ └── WatchedCoin.kt
│ │ ├── epoxymodels
│ │ ├── AddCoinItemView.kt
│ │ ├── AddCoinTransactionItemView.kt
│ │ ├── ChipGroupItemView.kt
│ │ ├── CoinAboutItemView.kt
│ │ ├── CoinHistoricalChartItemView.kt
│ │ ├── CoinInfoItemView.kt
│ │ ├── CoinItemView.kt
│ │ ├── CoinNewsItemView.kt
│ │ ├── CoinPositionItemView.kt
│ │ ├── CoinSearchItemView.kt
│ │ ├── CoinStatsticsItemView.kt
│ │ ├── CoinTickerItemView.kt
│ │ ├── CoinTickerView.kt
│ │ ├── CoinTransactionHistoryItemView.kt
│ │ ├── DashboardHeaderItemView.kt
│ │ ├── EmptyCoinItemView.kt
│ │ ├── ExchangePairItemView.kt
│ │ ├── ExpandedNewsItemView.kt
│ │ ├── GenericFooterItemView.kt
│ │ ├── LabelItemView.kt
│ │ ├── NewsItemView.kt
│ │ ├── ShortNewsItemView.kt
│ │ └── TopCardItemView.kt
│ │ ├── featurecomponents
│ │ ├── ModuleItem.kt
│ │ ├── cointickermodule
│ │ │ ├── CoinTickerContract.kt
│ │ │ ├── CoinTickerPresenter.kt
│ │ │ └── CoinTickerRepository.kt
│ │ ├── cryptonewsmodule
│ │ │ ├── CryptoNewsContract.kt
│ │ │ ├── CryptoNewsPresenter.kt
│ │ │ └── CryptoNewsRepository.kt
│ │ └── historicalchartmodule
│ │ │ ├── ChartRepository.kt
│ │ │ └── HistoricalChartAdapter.kt
│ │ ├── features
│ │ ├── BasePresenter.kt
│ │ ├── BaseView.kt
│ │ ├── CryptoCompareRepository.kt
│ │ ├── HomeActivity.kt
│ │ ├── coin
│ │ │ ├── CoinContract.kt
│ │ │ ├── CoinFragment.kt
│ │ │ └── CoinPresenter.kt
│ │ ├── coindetails
│ │ │ ├── CoinDetailPagerPresenter.kt
│ │ │ ├── CoinDetailPresenter.kt
│ │ │ ├── CoinDetailsActivity.kt
│ │ │ ├── CoinDetailsContract.kt
│ │ │ ├── CoinDetailsPagerActivity.kt
│ │ │ ├── CoinDetailsPagerAdapter.kt
│ │ │ ├── CoinDetailsPagerContract.kt
│ │ │ └── CoinDetailsPagerRepository.kt
│ │ ├── coinsearch
│ │ │ ├── CoinDiscoveryContract.kt
│ │ │ ├── CoinDiscoveryFragment.kt
│ │ │ ├── CoinDiscoveryPresenter.kt
│ │ │ ├── CoinSearchActivity.kt
│ │ │ ├── CoinSearchContract.kt
│ │ │ └── CoinSearchPresenter.kt
│ │ ├── dashboard
│ │ │ ├── CoinDashboardContract.kt
│ │ │ ├── CoinDashboardFragment.kt
│ │ │ ├── CoinDashboardPresenter.kt
│ │ │ └── DashboardRepository.kt
│ │ ├── exchangesearch
│ │ │ └── ExchangeSearchActivity.kt
│ │ ├── launch
│ │ │ ├── IntroFragment.kt
│ │ │ ├── LaunchActivity.kt
│ │ │ ├── LaunchContract.kt
│ │ │ └── LaunchPresenter.kt
│ │ ├── newslist
│ │ │ └── NewsListActivity.kt
│ │ ├── pairsearch
│ │ │ └── PairSearchActivity.kt
│ │ ├── settings
│ │ │ ├── SettingsContract.kt
│ │ │ ├── SettingsFragment.kt
│ │ │ └── SettingsPresenter.kt
│ │ ├── ticker
│ │ │ └── CoinTickerActivity.kt
│ │ └── transaction
│ │ │ ├── CoinTransactionActivity.kt
│ │ │ ├── CoinTransactionContract.kt
│ │ │ └── CoinTransactionPresenter.kt
│ │ ├── network
│ │ ├── NetworkConstants.kt
│ │ ├── api
│ │ │ ├── API.kt
│ │ │ └── CryptoAPI.kt
│ │ └── models
│ │ │ ├── CCCoin.kt
│ │ │ ├── CoinPair.kt
│ │ │ ├── CoinPrice.kt
│ │ │ ├── CryptoCompareHistoricalResponse.kt
│ │ │ ├── CryptoCompareNews.kt
│ │ │ ├── CryptoPanicNews.kt
│ │ │ ├── CryptoTicker.kt
│ │ │ └── ExchangePair.kt
│ │ └── utils
│ │ ├── CoinBitExtendedCurrency.kt
│ │ ├── Constants.kt
│ │ ├── Extensions.kt
│ │ ├── Formaters.kt
│ │ ├── Parser.kt
│ │ ├── Utils.kt
│ │ ├── resourcemanager
│ │ ├── AndroidResourceManager.kt
│ │ └── AndroidResourceManagerImpl.kt
│ │ └── ui
│ │ ├── IntroPageTransformer.kt
│ │ └── OnVerticalScrollListener.kt
│ └── res
│ ├── animator
│ └── card_down_animation.xml
│ ├── color
│ ├── bottom_nav_button_forg.xml
│ └── chip_color.xml
│ ├── drawable-hdpi
│ └── menu_search.png
│ ├── drawable-mdpi
│ └── menu_search.png
│ ├── drawable-xhdpi
│ ├── add_new.png
│ ├── menu_search.png
│ └── news_icon.png
│ ├── drawable-xxhdpi
│ ├── add_new.png
│ ├── menu_search.png
│ └── news_icon.png
│ ├── drawable-xxxhdpi
│ ├── add_new.png
│ ├── menu_search.png
│ └── news_icon.png
│ ├── drawable
│ ├── background_rounded_all.xml
│ ├── button_back.xml
│ ├── divider_thin_horizontal.xml
│ ├── ic_notification.xml
│ ├── ic_refresh.xml
│ ├── ic_watch.xml
│ ├── ic_watching.xml
│ ├── ic_watchlist.xml
│ ├── range_radio_btn_selector.xml
│ ├── range_radio_btn_selector_background.xml
│ ├── ripple_background.xml
│ ├── ripple_background_rounded_bottom.xml
│ ├── ripple_background_rounded_top.xml
│ ├── search_icon.xml
│ ├── settings_icon.xml
│ └── top_card_background.xml
│ ├── layout
│ ├── activity_coin_details.xml
│ ├── activity_coin_search.xml
│ ├── activity_coin_ticker_list.xml
│ ├── activity_coin_transaction.xml
│ ├── activity_exchange_pair_search.xml
│ ├── activity_home.xml
│ ├── activity_launch.xml
│ ├── activity_news_list.xml
│ ├── activity_pager_coin_details.xml
│ ├── carousal_module.xml
│ ├── chip_group_module.xml
│ ├── coin_about_module.xml
│ ├── coin_add_transaction_module.xml
│ ├── coin_info_module.xml
│ ├── coin_label_module.xml
│ ├── coin_news_module.xml
│ ├── coin_position_card_module.xml
│ ├── coin_search_item.xml
│ ├── coin_statistic_module.xml
│ ├── coin_ticker_module.xml
│ ├── coin_transaction_module.xml
│ ├── dashboard_coin_list_header_module.xml
│ ├── dashboard_coin_module.xml
│ ├── dashboard_empty_card_module.xml
│ ├── dashboard_header_module.xml
│ ├── dashboard_new_coin_module.xml
│ ├── dashboard_news_module.xml
│ ├── discovery_news_module.xml
│ ├── exchange_pair_search_item.xml
│ ├── fragment_coin_details.xml
│ ├── fragment_dashboard.xml
│ ├── fragment_discovery.xml
│ ├── fragment_settings.xml
│ ├── generic_footer_module.xml
│ ├── historical_chart_module.xml
│ ├── intro_fragment_layout.xml
│ ├── news_item.xml
│ ├── ticker_item.xml
│ ├── toolbar.xml
│ └── top_card_module.xml
│ ├── menu
│ ├── bottom_navigation_main.xml
│ ├── coin_details_menu.xml
│ └── home_menu.xml
│ ├── mipmap-anydpi-v26
│ ├── ic_launcher.xml
│ └── ic_launcher_round.xml
│ ├── mipmap-hdpi
│ ├── ic_launcher.png
│ ├── ic_launcher_foreground.png
│ └── ic_launcher_round.png
│ ├── mipmap-mdpi
│ ├── ic_launcher.png
│ ├── ic_launcher_foreground.png
│ └── ic_launcher_round.png
│ ├── mipmap-xhdpi
│ ├── ic_launcher.png
│ ├── ic_launcher_foreground.png
│ └── ic_launcher_round.png
│ ├── mipmap-xxhdpi
│ ├── ic_launcher.png
│ ├── ic_launcher_foreground.png
│ └── ic_launcher_round.png
│ ├── mipmap-xxxhdpi
│ ├── ic_launcher.png
│ ├── ic_launcher_foreground.png
│ └── ic_launcher_round.png
│ ├── raw
│ ├── bell.json
│ ├── graph.json
│ ├── lock.json
│ └── smiley_stack.json
│ └── values
│ ├── colors.xml
│ ├── dimens.xml
│ ├── ic_launcher_background.xml
│ ├── strings.xml
│ └── styles.xml
├── attribution.md
├── build.gradle
├── coinbit_privacy_policy.md
├── gradle.properties
├── gradle
└── wrapper
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
├── gradlew
├── gradlew.bat
├── screenshots
├── variant_1
│ ├── 0.jpg
│ ├── 1.jpg
│ ├── 2.jpg
│ ├── 3.jpg
│ └── 4.jpg
├── variant_3
│ ├── 0.jpg
│ ├── 1.jpg
│ ├── 2.jpg
│ ├── 3.jpg
│ └── 4.jpg
└── variant_main
│ ├── 0.jpg
│ ├── 1.jpg
│ ├── 2.jpg
│ ├── 3.jpg
│ └── 4.jpg
└── settings.gradle
/.github/workflows/test.yaml:
--------------------------------------------------------------------------------
1 | name: Testing Workflow
2 |
3 | # TODO add option to upload to firebase, add option to upload artifact, push to google play when merging to master
4 | # Step 1: Choose the branch or branches you want to run this workflow
5 | on:
6 | push:
7 | branches:
8 | - master
9 | pull_request:
10 | branches:
11 | - master
12 |
13 | jobs:
14 | testing:
15 | name: Lint Check and Testing
16 | runs-on: ubuntu-latest
17 |
18 | steps:
19 | - name: Clone Repo
20 | uses: actions/checkout@v2
21 |
22 | - name: Set up JDK 1.8
23 | uses: actions/setup-java@v1
24 | with:
25 | java-version: 1.8
26 |
27 | # Run ktLint to ensure formatting.
28 | - name: Running ktLint
29 | run: ./gradlew lintKotlin
30 |
31 | # Check the code with Android linter
32 | - name: Run Android Linter
33 | run: ./gradlew lintDebug
34 |
35 | # Running unit test
36 | - name: Run Unit Tests
37 | run: ./gradlew testDebugUnitTest
38 |
39 | # Assemble debug apk to send to firebase test lab
40 | - name: Assemble Debug APK
41 | run: ./gradlew assembleDebug
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Built application files
2 | *.apk
3 | *.ap_
4 |
5 | # Files for the ART/Dalvik VM
6 | *.dex
7 |
8 | # Java class files
9 | *.class
10 |
11 | # Generated files
12 | bin/
13 | gen/
14 | out/
15 |
16 | # Gradle files
17 | .gradle/
18 | build/
19 |
20 | # Local configuration file (sdk path, etc)
21 | local.properties
22 |
23 | # Proguard folder generated by Eclipse
24 | proguard/
25 |
26 | # Log Files
27 | *.log
28 |
29 | # Android Studio Navigation editor temp files
30 | .navigation/
31 |
32 | # Android Studio captures folder
33 | captures/
34 |
35 | # Intellij
36 | *.iml
37 | .idea/workspace.xml
38 | .idea/tasks.xml
39 | .idea/gradle.xml
40 | .idea/dictionaries
41 | .idea/libraries
42 |
43 | # Keystore files
44 | *.jks
45 |
46 | # External native build folder generated in Android Studio 2.2 and later
47 | .externalNativeBuild
48 |
49 |
50 | # Freeline
51 | freeline.py
52 | freeline/
53 | freeline_project_description.json
54 |
55 | target
56 | test-output
57 | .project
58 | .settings
59 | .classpath
60 | .springBeans
61 | .metadata
62 | .idea/
63 | .DS_Store
64 | **/src/generated/*
65 | resourceVersions.properties
66 | Servers/
67 | .gradle
68 | /local.properties
69 | /.idea/workspace.xml
70 | /build
71 | /app/src/main/res/values/com_crashlytics_export_strings.xml
72 | /app/src/main/assets/crashlytics-build.properties
73 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | 
2 |
3 | # CoinBit
4 | CoinBit is a beautiful CryptoCurrency app, completely open sourced and 100% in kotlin. It supports following features
5 |
6 | * Track prices of over 3000+ currencies over 100+ exchanges
7 | * Get top coins, top pairs, top exchanges by volume.
8 | * Track latest news for all the coins and crypto community in general
9 | * Completely secure, your data never leaves your device.
10 | * Choose your home currency and track prices in it.
11 | * Made with ❤️ and help from [open source community](https://github.com/pranayairan/CoinBit/blob/master/attribution.md)
12 | * Open for contribution, please send a pull request.
13 |
14 | App available on Google Play: https://play.google.com/store/apps/details?id=com.binarybricks.coinbit
15 |
16 | ## Work in progress
17 |
18 | * Ability to add transactions
19 | * Ability to change exchanges
20 | * Autorefresh of prices
21 | * Candle charts
22 |
23 | # App Architecture
24 |
25 | Currently the app is using MVP with a repository. We have 2 data source, Room and in memory cache. Data flow is like this
26 |
27 | Fragments/Activities -> Presenter -> Repo -> Network/Cache (room/in memory)
28 |
29 | In coming days I would like to remove inmemory cache and make everything come from Room. Network will keep the Room cache updated. This will give app some offline abilities.
30 |
31 | I am also using a ton of recycler view with [Adapter Delegate Pattern](http://hannesdorfmann.com/android/adapter-delegates). This enables me to plug and play the screens like Lego blocks. I am thinking to replace this with Epoxy in coming days.
32 |
33 |
34 | # Screenshots
35 |
36 |
37 |
38 |
39 |
40 |
41 |
--------------------------------------------------------------------------------
/_config.yml:
--------------------------------------------------------------------------------
1 | theme: jekyll-theme-cayman
--------------------------------------------------------------------------------
/app/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 |
--------------------------------------------------------------------------------
/app/google-services.json:
--------------------------------------------------------------------------------
1 | {
2 | "project_info": {
3 | "project_number": "577274068401",
4 | "firebase_url": "https://coinbit-4d26f.firebaseio.com",
5 | "project_id": "coinbit-4d26f",
6 | "storage_bucket": "coinbit-4d26f.appspot.com"
7 | },
8 | "client": [
9 | {
10 | "client_info": {
11 | "mobilesdk_app_id": "1:577274068401:android:f806e4e77b98053f",
12 | "android_client_info": {
13 | "package_name": "com.binarybricks.coinbit"
14 | }
15 | },
16 | "oauth_client": [
17 | {
18 | "client_id": "577274068401-7cm0ud0b8v96lsq35icvmoncsehvbupm.apps.googleusercontent.com",
19 | "client_type": 3
20 | }
21 | ],
22 | "api_key": [
23 | {
24 | "current_key": "AIzaSyD-mv2zb2UYQ1LbJWzKEd0WZrU1CCfSIEM"
25 | }
26 | ],
27 | "services": {
28 | "appinvite_service": {
29 | "other_platform_oauth_client": [
30 | {
31 | "client_id": "577274068401-7cm0ud0b8v96lsq35icvmoncsehvbupm.apps.googleusercontent.com",
32 | "client_type": 3
33 | }
34 | ]
35 | }
36 | }
37 | }
38 | ],
39 | "configuration_version": "1"
40 | }
--------------------------------------------------------------------------------
/app/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | # Add project specific ProGuard rules here.
2 | # By default, the flags in this file are appended to flags specified
3 | # in /Applications/sdk/tools/proguard/proguard-android.txt
4 | # You can edit the include path and order by changing the proguardFiles
5 | # directive in build.gradle.
6 | #
7 | # For more details, see
8 | # http://developer.android.com/guide/developing/tools/proguard.html
9 |
10 | # Add any project specific keep options here:
11 |
12 | # If your project uses WebView with JS, uncomment the following
13 | # and specify the fully qualified class name to the JavaScript interface
14 | # class:
15 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
16 | # public *;
17 | #}
18 |
19 | -keepclassmembers class * implements android.os.Parcelable {
20 | static ** CREATOR;
21 | }
22 |
23 | #this is for crashlytics logs which will deofubscate errors
24 | -keepattributes *Annotation*
25 | -keepattributes SourceFile,LineNumberTable
26 | -keep public class * extends java.lang.Exception
27 |
28 |
29 | ## --------------- Proguard configuration for Okhttp ----------------------
30 | # JSR 305 annotations are for embedding nullability information.
31 | -dontwarn javax.annotation.**
32 |
33 | # A resource is loaded with a relative path so the package of this class must be preserved.
34 | -keepnames class okhttp3.internal.publicsuffix.PublicSuffixDatabase
35 |
36 | # Animal Sniffer compileOnly dependency to ensure APIs are compatible with older versions of Java.
37 | -dontwarn org.codehaus.mojo.animal_sniffer.*
38 |
39 | # OkHttp platform used only on JVM and when Conscrypt dependency is available.
40 | -dontwarn okhttp3.internal.platform.ConscryptPlatform
41 |
42 | ## --------------- Proguard configuration for Retrofit ----------------------
43 | # don't obfuscate data model objects because GSON needs to rebuild them using reflection
44 | -keep class com.binarybricks.coinbit.network.models.** { *; }
45 | -keepclassmembers enum com.binarybricks.coinbit.network.models.** { *; }
46 |
47 | -keep class com.binarybricks.coinbit.data.database.entities.** { *; }
48 | -keepclassmembers enum com.binarybricks.coinbit.data.database.entities.** { *; }
49 |
50 | # Retrofit does reflection on generic parameters. InnerClasses is required to use Signature and
51 | # EnclosingMethod is required to use InnerClasses.
52 | -keepattributes Signature, InnerClasses, EnclosingMethod
53 |
54 | # Retain service method parameters when optimizing.
55 | -keepclassmembers,allowshrinking,allowobfuscation interface * {
56 | @retrofit2.http.* ;
57 | }
58 |
59 | # Ignore annotation used for build tooling.
60 | -dontwarn org.codehaus.mojo.animal_sniffer.IgnoreJRERequirement
61 |
62 | # Guarded by a NoClassDefFoundError try/catch and only used when on the classpath.
63 | -dontwarn kotlin.Unit
64 |
65 | # Top-level functions that can only be used by Kotlin.
66 | -dontwarn retrofit2.-KotlinExtensions
67 |
68 | ### Glide, Glide Okttp Module, Glide Transformations
69 | -keep public class * implements com.bumptech.glide.module.GlideModule
70 | -keep public class * extends com.bumptech.glide.module.AppGlideModule
71 | -keep public enum com.bumptech.glide.load.resource.bitmap.ImageHeaderParser$** {
72 | **[] $VALUES;
73 | public *;
74 | }
75 |
76 | # Gson specific classes
77 | -keep class sun.misc.Unsafe { *; }
--------------------------------------------------------------------------------
/app/release/app.aab:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pranayairan/CoinBit/ce44fc26aafdc180f0b1db7d93b7a141673cb14c/app/release/app.aab
--------------------------------------------------------------------------------
/app/release/output.json:
--------------------------------------------------------------------------------
1 | [{"outputType":{"type":"APK"},"apkData":{"type":"MAIN","splits":[],"versionCode":6,"versionName":"1.3","enabled":true,"outputFile":"app-release.apk","fullName":"release","baseName":"release"},"path":"app-release.apk","properties":{}}]
--------------------------------------------------------------------------------
/app/src/main/ic_launcher-web.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pranayairan/CoinBit/ce44fc26aafdc180f0b1db7d93b7a141673cb14c/app/src/main/ic_launcher-web.png
--------------------------------------------------------------------------------
/app/src/main/java/com/binarybricks/coinbit/CoinBitApplication.kt:
--------------------------------------------------------------------------------
1 | package com.binarybricks.coinbit
2 |
3 | import android.app.Application
4 | import android.content.Context
5 | import android.util.Log
6 | import androidx.room.Room
7 | import com.binarybricks.coinbit.data.database.CoinBitDatabase
8 | import com.facebook.stetho.Stetho
9 | import com.google.firebase.crashlytics.FirebaseCrashlytics
10 | import timber.log.Timber
11 | import timber.log.Timber.DebugTree
12 |
13 | /**
14 | Created by Pranay Airan
15 | */
16 |
17 | class CoinBitApplication : Application() {
18 |
19 | companion object {
20 |
21 | private const val DATABASE_NAME = "coinbit.db"
22 |
23 | private lateinit var appContext: Context
24 | var database: CoinBitDatabase? = null
25 |
26 | @JvmStatic
27 | fun getGlobalAppContext(): Context {
28 | return appContext
29 | }
30 | }
31 |
32 | override fun onCreate() {
33 | super.onCreate()
34 |
35 | appContext = applicationContext
36 |
37 | if (BuildConfig.DEBUG) {
38 | Timber.plant(DebugTree())
39 | Stetho.initializeWithDefaults(this)
40 | } else {
41 | Timber.plant(CrashReportingTree())
42 | }
43 |
44 | database = Room.databaseBuilder(this, CoinBitDatabase::class.java, DATABASE_NAME).build()
45 | }
46 |
47 | /** A tree which logs important information for crash reporting. */
48 | private class CrashReportingTree : Timber.Tree() {
49 | override fun log(priority: Int, tag: String?, message: String, t: Throwable?) {
50 | if (priority == Log.VERBOSE || priority == Log.DEBUG) {
51 | return
52 | }
53 | if (priority == Log.ERROR) {
54 | FirebaseCrashlytics.getInstance().log("E/$tag:$message")
55 | } else if (priority == Log.WARN) {
56 | FirebaseCrashlytics.getInstance().log("W/$tag:$message")
57 | }
58 | }
59 | }
60 | }
61 |
--------------------------------------------------------------------------------
/app/src/main/java/com/binarybricks/coinbit/data/CoinBitCache.kt:
--------------------------------------------------------------------------------
1 | package com.binarybricks.coinbit.data
2 |
3 | import com.binarybricks.coinbit.network.models.*
4 |
5 | /**
6 | * Created by Pragya Agrawal
7 | *
8 | * In memory cache for certain objects that we want to cache only for the app session
9 | */
10 |
11 | object CoinBitCache {
12 |
13 | // cache the news since we don't want to overload the server.
14 | var newsMap: MutableMap = hashMapOf()
15 |
16 | // crypto compare news
17 | var cyrptoCompareNews: MutableList = ArrayList()
18 |
19 | var coinPriceMap: HashMap = hashMapOf()
20 |
21 | var coinExchangeMap: HashMap> = hashMapOf()
22 |
23 | var topCoinsByTotalVolume: ArrayList = ArrayList()
24 |
25 | var topPairsByVolume: ArrayList = ArrayList()
26 |
27 | var topCoinsByTotalVolume24Hours: ArrayList = ArrayList()
28 |
29 | var ticker: MutableMap> = hashMapOf()
30 |
31 | fun updateCryptoCompareNews(cryptoNews: CryptoCompareNews) {
32 | cyrptoCompareNews.remove(cryptoNews)
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/app/src/main/java/com/binarybricks/coinbit/data/database/BigDecimalConverter.kt:
--------------------------------------------------------------------------------
1 | package com.binarybricks.coinbit.data.database
2 |
3 | import androidx.room.TypeConverter
4 | import java.math.BigDecimal
5 |
6 | /**
7 | * Created by Pranay Airan
8 | */
9 | class BigDecimalConverter {
10 | @TypeConverter
11 | fun fromString(value: String?): BigDecimal {
12 | return if (value == null) BigDecimal.ZERO else BigDecimal(value)
13 | }
14 |
15 | @TypeConverter
16 | fun amountToString(bigDecimal: BigDecimal): String {
17 | return bigDecimal.toPlainString()
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/app/src/main/java/com/binarybricks/coinbit/data/database/CoinBitDatabase.kt:
--------------------------------------------------------------------------------
1 | package com.binarybricks.coinbit.data.database
2 |
3 | import androidx.room.Database
4 | import androidx.room.RoomDatabase
5 | import androidx.room.TypeConverters
6 | import com.binarybricks.coinbit.data.database.dao.CoinTransactionDao
7 | import com.binarybricks.coinbit.data.database.dao.ExchangeDao
8 | import com.binarybricks.coinbit.data.database.dao.WatchedCoinDao
9 | import com.binarybricks.coinbit.data.database.entities.CoinTransaction
10 | import com.binarybricks.coinbit.data.database.entities.Exchange
11 | import com.binarybricks.coinbit.data.database.entities.WatchedCoin
12 |
13 | /**
14 | * Created by Pragya Agrawal
15 | */
16 | @Database(entities = [Exchange::class, WatchedCoin::class, CoinTransaction::class], version = 1, exportSchema = false)
17 | @TypeConverters(BigDecimalConverter::class)
18 | abstract class CoinBitDatabase : RoomDatabase() {
19 |
20 | abstract fun exchangeDao(): ExchangeDao
21 | abstract fun watchedCoinDao(): WatchedCoinDao
22 | abstract fun coinTransactionDao(): CoinTransactionDao
23 | }
24 |
--------------------------------------------------------------------------------
/app/src/main/java/com/binarybricks/coinbit/data/database/dao/CoinTransactionDao.kt:
--------------------------------------------------------------------------------
1 | package com.binarybricks.coinbit.data.database.dao
2 |
3 | import androidx.room.Dao
4 | import androidx.room.Insert
5 | import androidx.room.OnConflictStrategy
6 | import androidx.room.Query
7 | import com.binarybricks.coinbit.data.database.entities.CoinTransaction
8 | import kotlinx.coroutines.flow.Flow
9 |
10 | /**
11 | * Created by Pragya Agrawal
12 | *
13 | * Add queries to read/update coinSymbol transaction data from database.
14 | */
15 | @Dao
16 | interface CoinTransactionDao {
17 |
18 | @Query("select * from cointransaction")
19 | fun getAllCoinTransaction(): Flow>
20 |
21 | @Insert(onConflict = OnConflictStrategy.REPLACE)
22 | suspend fun insertTransaction(coinTransaction: CoinTransaction)
23 |
24 | @Query("SELECT * FROM cointransaction WHERE coinSymbol = :coinSymbol ORDER BY transactionTime ASC")
25 | fun getTransactionsForCoin(coinSymbol: String): Flow>
26 | }
27 |
--------------------------------------------------------------------------------
/app/src/main/java/com/binarybricks/coinbit/data/database/dao/ExchangeDao.kt:
--------------------------------------------------------------------------------
1 | package com.binarybricks.coinbit.data.database.dao
2 |
3 | import androidx.room.Dao
4 | import androidx.room.Insert
5 | import androidx.room.OnConflictStrategy
6 | import androidx.room.Query
7 | import com.binarybricks.coinbit.data.database.entities.Exchange
8 |
9 | /**
10 | * Created by Pragya Agrawal
11 | *
12 | * Add queries to read/update exchange data from database.
13 | */
14 | @Dao
15 | interface ExchangeDao {
16 |
17 | @Query("select * from exchange")
18 | suspend fun getAllExchanges(): List
19 |
20 | @Insert(onConflict = OnConflictStrategy.REPLACE)
21 | suspend fun insertExchanges(list: List)
22 | }
23 |
--------------------------------------------------------------------------------
/app/src/main/java/com/binarybricks/coinbit/data/database/dao/WatchedCoinDao.kt:
--------------------------------------------------------------------------------
1 | package com.binarybricks.coinbit.data.database.dao
2 |
3 | import androidx.room.Dao
4 | import androidx.room.Insert
5 | import androidx.room.OnConflictStrategy
6 | import androidx.room.Query
7 | import com.binarybricks.coinbit.data.database.entities.WatchedCoin
8 | import kotlinx.coroutines.flow.Flow
9 | import java.math.BigDecimal
10 |
11 | /**
12 | * Created by Pragya Agrawal
13 | *
14 | * Add queries to read/update coinSymbol data from database.
15 | */
16 | @Dao
17 | interface WatchedCoinDao {
18 |
19 | @Query("select * from WatchedCoin where purchaseQuantity > 0 OR watched = :watched order by sortOrder")
20 | fun getAllWatchedCoins(watched: Boolean = true): Flow>
21 |
22 | @Query("select * from WatchedCoin where purchaseQuantity > 0 OR watched = :watched order by sortOrder")
23 | suspend fun getAllWatchedCoinsOnetime(watched: Boolean = true): List // this method should be removed
24 |
25 | @Query("select * from WatchedCoin where isTrading = :isTrue order by sortOrder")
26 | fun getAllCoins(isTrue: Boolean = true): Flow>
27 |
28 | @Query("select * from WatchedCoin where symbol = :symbol")
29 | suspend fun getSingleWatchedCoin(symbol: String): List
30 |
31 | @Insert(onConflict = OnConflictStrategy.REPLACE)
32 | suspend fun insertCoinListIntoWatchList(list: List)
33 |
34 | @Insert(onConflict = OnConflictStrategy.REPLACE)
35 | suspend fun insertCoinIntoWatchList(watchedCoin: WatchedCoin)
36 |
37 | @Query("update WatchedCoin set purchaseQuantity = purchaseQuantity + :quantity where symbol=:symbol")
38 | suspend fun addPurchaseQuantityForCoin(quantity: BigDecimal, symbol: String): Int
39 |
40 | @Query("UPDATE WatchedCoin SET watched = :watched WHERE coinId = :coinId")
41 | suspend fun makeCoinWatched(watched: Boolean, coinId: String)
42 | }
43 |
--------------------------------------------------------------------------------
/app/src/main/java/com/binarybricks/coinbit/data/database/entities/Coin.kt:
--------------------------------------------------------------------------------
1 | package com.binarybricks.coinbit.data.database.entities
2 |
3 | import android.os.Parcelable
4 | import androidx.room.ColumnInfo
5 | import androidx.room.Entity
6 | import androidx.room.Index
7 | import androidx.room.PrimaryKey
8 | import kotlinx.android.parcel.Parcelize
9 |
10 | /**
11 | * Created by Pragya Agrawal
12 | */
13 | @Entity(indices = [(Index("coinId", unique = true))])
14 | @Parcelize
15 | data class Coin(
16 | @ColumnInfo(name = "coinId") var id: String,
17 | @ColumnInfo(name = "url") var url: String,
18 | @ColumnInfo(name = "imageUrl") var imageUrl: String?,
19 | @ColumnInfo(name = "name") var name: String,
20 | @ColumnInfo(name = "symbol") var symbol: String,
21 | @ColumnInfo(name = "coinName") var coinName: String,
22 | @ColumnInfo(name = "fullName") var fullName: String,
23 | @ColumnInfo(name = "algorithm") var algorithm: String?,
24 | @ColumnInfo(name = "proofType") var proofType: String?,
25 | @ColumnInfo(name = "fullyPremined") var fullyPremined: String?,
26 | @ColumnInfo(name = "totalCoinSupply") var totalCoinSupply: String?,
27 | @ColumnInfo(name = "preMinedValue") var preMinedValue: String?,
28 | @ColumnInfo(name = "totalCoinsFreeFloat") var totalCoinsFreeFloat: String?,
29 | @ColumnInfo(name = "sortOrder") var sortOrder: Int?,
30 | @ColumnInfo(name = "sponsored") var sponsored: Boolean = false,
31 | @ColumnInfo(name = "isTrading") var isTrading: Boolean = false,
32 | @ColumnInfo(name = "description") var description: String?,
33 | @ColumnInfo(name = "twitter") var twitter: String?,
34 | @ColumnInfo(name = "website") var website: String?,
35 | @ColumnInfo(name = "reddit") var reddit: String?,
36 | @ColumnInfo(name = "forum") var forum: String?,
37 | @ColumnInfo(name = "github") var github: String?,
38 | @ColumnInfo(name = "id") @PrimaryKey(autoGenerate = true) var idKey: Long = 0
39 | ) : Parcelable
40 |
--------------------------------------------------------------------------------
/app/src/main/java/com/binarybricks/coinbit/data/database/entities/CoinTransaction.kt:
--------------------------------------------------------------------------------
1 | package com.binarybricks.coinbit.data.database.entities
2 |
3 | import android.os.Parcelable
4 | import androidx.room.ColumnInfo
5 | import androidx.room.Entity
6 | import androidx.room.Index
7 | import androidx.room.PrimaryKey
8 | import kotlinx.android.parcel.Parcelize
9 | import java.math.BigDecimal
10 |
11 | /**
12 | * Created by Pragya Agrawal
13 | */
14 | @Entity(indices = [(Index("id", unique = true))])
15 | @Parcelize
16 | data class CoinTransaction(
17 | @ColumnInfo(name = "transactionType") var transactionType: Int,
18 | @ColumnInfo(name = "coinSymbol") var coinSymbol: String,
19 | @ColumnInfo(name = "pair") var pair: String,
20 | @ColumnInfo(name = "buyprice") var buyPrice: BigDecimal,
21 | @ColumnInfo(name = "buypriceHomeCurrency") var buypriceHomeCurrency: BigDecimal,
22 | @ColumnInfo(name = "quantity") var quantity: BigDecimal,
23 | @ColumnInfo(name = "transactionTime") var transactionTime: String,
24 | @ColumnInfo(name = "cost") var cost: String,
25 | @ColumnInfo(name = "exchange") var exchange: String,
26 | @ColumnInfo(name = "exchangeFees") var exchangeFees: BigDecimal,
27 | @ColumnInfo(name = "id") @PrimaryKey(autoGenerate = true) var idKey: Long = 0
28 | ) : Parcelable
29 |
--------------------------------------------------------------------------------
/app/src/main/java/com/binarybricks/coinbit/data/database/entities/Exchange.kt:
--------------------------------------------------------------------------------
1 | package com.binarybricks.coinbit.data.database.entities
2 |
3 | import android.os.Parcelable
4 | import androidx.room.ColumnInfo
5 | import androidx.room.Entity
6 | import androidx.room.Index
7 | import androidx.room.PrimaryKey
8 | import kotlinx.android.parcel.Parcelize
9 |
10 | /**
11 | * Created by Pragya Agrawal
12 | */
13 | @Entity(indices = [(Index("exchangeID", unique = true))])
14 | @Parcelize
15 | data class Exchange(
16 | @ColumnInfo(name = "exchangeID") var id: String,
17 | @ColumnInfo(name = "name") var name: String,
18 | @ColumnInfo(name = "url") var url: String?,
19 | @ColumnInfo(name = "logoUrl") var logoUrl: String?,
20 | @ColumnInfo(name = "itemType") var itemType: String?,
21 | @ColumnInfo(name = "internalName") var internalName: String?,
22 | @ColumnInfo(name = "affiliateUrl") var affiliateUrl: String?,
23 | @ColumnInfo(name = "country") var country: String?,
24 | @ColumnInfo(name = "orderBook") var orderBook: Boolean = false,
25 | @ColumnInfo(name = "trades") var trades: Boolean = false,
26 | @ColumnInfo(name = "recommended") var recommended: Boolean = false,
27 | @ColumnInfo(name = "sponsored") var sponsored: Boolean = false,
28 | @ColumnInfo(name = "id") @PrimaryKey(autoGenerate = true) var idKey: Long = 0
29 | ) : Parcelable
30 |
--------------------------------------------------------------------------------
/app/src/main/java/com/binarybricks/coinbit/data/database/entities/WatchedCoin.kt:
--------------------------------------------------------------------------------
1 | package com.binarybricks.coinbit.data.database.entities
2 |
3 | import android.os.Parcelable
4 | import androidx.room.ColumnInfo
5 | import androidx.room.Embedded
6 | import androidx.room.Entity
7 | import androidx.room.Index
8 | import androidx.room.PrimaryKey
9 | import kotlinx.android.parcel.Parcelize
10 | import java.math.BigDecimal
11 |
12 | /**
13 | * Created by Pragya Agrawal
14 | */
15 | @Entity(indices = [(Index("watched_id", unique = true))])
16 | @Parcelize
17 | data class WatchedCoin(
18 | @Embedded
19 | val coin: Coin,
20 | var exchange: String,
21 | var fromCurrency: String,
22 | var purchaseQuantity: BigDecimal = BigDecimal.ZERO,
23 | var watched: Boolean = false,
24 | @ColumnInfo(name = "watched_id") @PrimaryKey(autoGenerate = true) var idKey: Long = 0
25 | ) : Parcelable
26 |
--------------------------------------------------------------------------------
/app/src/main/java/com/binarybricks/coinbit/epoxymodels/AddCoinItemView.kt:
--------------------------------------------------------------------------------
1 | package com.binarybricks.coinbit.epoxymodels
2 |
3 | import android.content.Context
4 | import android.util.AttributeSet
5 | import android.view.View
6 | import androidx.constraintlayout.widget.ConstraintLayout
7 | import com.airbnb.epoxy.CallbackProp
8 | import com.airbnb.epoxy.ModelView
9 | import com.binarybricks.coinbit.R
10 | import com.binarybricks.coinbit.featurecomponents.ModuleItem
11 |
12 | @ModelView(autoLayout = ModelView.Size.MATCH_WIDTH_WRAP_HEIGHT)
13 | class AddCoinItemView @JvmOverloads constructor(
14 | context: Context,
15 | attributeSet: AttributeSet? = null,
16 | defStyle: Int = 0,
17 | ) : ConstraintLayout(context, attributeSet, defStyle) {
18 |
19 | private val addCoinCard: View
20 |
21 | init {
22 | View.inflate(context, R.layout.dashboard_new_coin_module, this)
23 | addCoinCard = findViewById(R.id.addCoinCard)
24 | }
25 |
26 | @CallbackProp
27 | fun setAddCoinClickListener(listener: OnClickListener?) {
28 | addCoinCard.setOnClickListener(listener)
29 | }
30 |
31 | object AddCoinModuleItem : ModuleItem
32 | }
33 |
--------------------------------------------------------------------------------
/app/src/main/java/com/binarybricks/coinbit/epoxymodels/AddCoinTransactionItemView.kt:
--------------------------------------------------------------------------------
1 | package com.binarybricks.coinbit.epoxymodels
2 |
3 | import android.content.Context
4 | import android.util.AttributeSet
5 | import android.view.View
6 | import android.widget.Button
7 | import androidx.constraintlayout.widget.ConstraintLayout
8 | import com.airbnb.epoxy.CallbackProp
9 | import com.airbnb.epoxy.ModelView
10 | import com.binarybricks.coinbit.R
11 | import com.binarybricks.coinbit.featurecomponents.ModuleItem
12 |
13 | @ModelView(autoLayout = ModelView.Size.MATCH_WIDTH_WRAP_HEIGHT)
14 | class AddCoinTransactionItemView @JvmOverloads constructor(
15 | context: Context,
16 | attributeSet: AttributeSet? = null,
17 | defStyle: Int = 0,
18 | ) : ConstraintLayout(context, attributeSet, defStyle) {
19 |
20 | private val btnAddTransaction: Button
21 |
22 | init {
23 | View.inflate(context, R.layout.coin_add_transaction_module, this)
24 | btnAddTransaction = findViewById(R.id.btnAddTransaction)
25 | }
26 |
27 | @CallbackProp
28 | fun setItemClickListener(listener: OnClickListener?) {
29 | btnAddTransaction.setOnClickListener(listener)
30 | }
31 |
32 | object AddCoinTransactionModuleItem : ModuleItem
33 | }
34 |
--------------------------------------------------------------------------------
/app/src/main/java/com/binarybricks/coinbit/epoxymodels/ChipGroupItemView.kt:
--------------------------------------------------------------------------------
1 | package com.binarybricks.coinbit.epoxymodels
2 |
3 | import android.content.Context
4 | import android.util.AttributeSet
5 | import android.view.View
6 | import androidx.constraintlayout.widget.ConstraintLayout
7 | import androidx.core.content.ContextCompat
8 | import com.airbnb.epoxy.CallbackProp
9 | import com.airbnb.epoxy.ModelProp
10 | import com.airbnb.epoxy.ModelView
11 | import com.binarybricks.coinbit.R
12 | import com.binarybricks.coinbit.featurecomponents.ModuleItem
13 | import com.binarybricks.coinbit.network.models.CoinPair
14 | import com.google.android.material.chip.Chip
15 | import com.google.android.material.chip.ChipGroup
16 |
17 | @ModelView(autoLayout = ModelView.Size.MATCH_WIDTH_WRAP_HEIGHT)
18 | class ChipGroupItemView @JvmOverloads constructor(
19 | context: Context,
20 | attributeSet: AttributeSet? = null,
21 | defStyle: Int = 0,
22 | ) : ConstraintLayout(context, attributeSet, defStyle) {
23 |
24 | private val chipGroupFirst: ChipGroup
25 | private val chipGroupSecond: ChipGroup
26 | private val chipGroupThird: ChipGroup
27 |
28 | private var chipItemClickedListener: OnChipItemClickedListener? = null
29 |
30 | init {
31 | View.inflate(context, R.layout.chip_group_module, this)
32 | chipGroupFirst = findViewById(R.id.chipGroupFirst)
33 | chipGroupSecond = findViewById(R.id.chipGroupSecond)
34 | chipGroupThird = findViewById(R.id.chipGroupThird)
35 | }
36 |
37 | @ModelProp
38 | fun setChipData(chipGroupModuleData: ChipGroupModuleData) {
39 | val chunkedList = chipGroupModuleData.data.chunked(15)
40 |
41 | var i = 0
42 | chunkedList.forEach {
43 | val chipGroup = when (i) {
44 | 1 -> chipGroupFirst
45 | 2 -> chipGroupSecond
46 | else -> chipGroupThird
47 | }
48 | it.forEach { coinPair ->
49 | chipGroup.addView(getChip(chipGroup.context, coinPair))
50 | }
51 | i++
52 | }
53 | }
54 |
55 | private fun getChip(context: Context, coinPair: CoinPair): Chip {
56 | val chip = Chip(context)
57 | chip.text = coinPair.fullName
58 | chip.setTextColor(ContextCompat.getColor(context, R.color.primaryTextColor))
59 | chip.isClickable = true
60 | chip.isCheckable = false
61 | chip.chipBackgroundColor = ContextCompat.getColorStateList(context, R.color.chip_color)
62 |
63 | chip.setOnClickListener {
64 | if (coinPair.symbol != null) {
65 | chipItemClickedListener?.onChipClicked(coinPair.symbol)
66 | }
67 | }
68 | return chip
69 | }
70 |
71 | @CallbackProp
72 | fun setChipClickListener(chipItemClickedListener: OnChipItemClickedListener?) {
73 | this.chipItemClickedListener = chipItemClickedListener
74 | }
75 |
76 | interface OnChipItemClickedListener {
77 | fun onChipClicked(coinSymbol: String)
78 | }
79 |
80 | data class ChipGroupModuleData(val data: List) : ModuleItem
81 | }
82 |
--------------------------------------------------------------------------------
/app/src/main/java/com/binarybricks/coinbit/epoxymodels/CoinInfoItemView.kt:
--------------------------------------------------------------------------------
1 | package com.binarybricks.coinbit.epoxymodels
2 |
3 | import android.content.Context
4 | import android.util.AttributeSet
5 | import android.view.View
6 | import android.widget.TextView
7 | import androidx.constraintlayout.widget.ConstraintLayout
8 | import com.airbnb.epoxy.ModelProp
9 | import com.airbnb.epoxy.ModelView
10 | import com.binarybricks.coinbit.R
11 | import com.binarybricks.coinbit.featurecomponents.ModuleItem
12 | import com.binarybricks.coinbit.utils.getDefaultExchangeText
13 |
14 | @ModelView(autoLayout = ModelView.Size.MATCH_WIDTH_WRAP_HEIGHT)
15 | class CoinInfoItemView @JvmOverloads constructor(
16 | context: Context,
17 | attributeSet: AttributeSet? = null,
18 | defStyle: Int = 0,
19 | ) : ConstraintLayout(context, attributeSet, defStyle) {
20 |
21 | private val tvFirstTxnTimeAndExchange: TextView
22 | private val tvAlgorithmName: TextView
23 | private val tvProofTypeName: TextView
24 |
25 | init {
26 | View.inflate(context, R.layout.coin_info_module, this)
27 | tvFirstTxnTimeAndExchange = findViewById(R.id.tvFirstTxnTimeAndExchange)
28 | tvAlgorithmName = findViewById(R.id.tvAlgorithmName)
29 | tvProofTypeName = findViewById(R.id.tvProofTypeName)
30 | }
31 |
32 | @ModelProp(options = [ModelProp.Option.IgnoreRequireHashCode])
33 | fun setExchange(coinInfoModuleData: CoinInfoModuleData) {
34 | var exchange = coinInfoModuleData.exchange
35 | exchange = getDefaultExchangeText(exchange, context)
36 | tvFirstTxnTimeAndExchange.text = exchange
37 | tvAlgorithmName.text = coinInfoModuleData.algorithm
38 | tvProofTypeName.text = coinInfoModuleData.proofType
39 | }
40 |
41 | data class CoinInfoModuleData(val exchange: String, val algorithm: String?, val proofType: String?) : ModuleItem
42 | }
43 |
--------------------------------------------------------------------------------
/app/src/main/java/com/binarybricks/coinbit/epoxymodels/CoinSearchItemView.kt:
--------------------------------------------------------------------------------
1 | package com.binarybricks.coinbit.epoxymodels
2 |
3 | import android.content.Context
4 | import android.util.AttributeSet
5 | import android.view.View
6 | import android.widget.ImageView
7 | import android.widget.TextView
8 | import androidx.appcompat.widget.SwitchCompat
9 | import androidx.constraintlayout.widget.ConstraintLayout
10 | import coil.load
11 | import coil.transform.CircleCropTransformation
12 | import com.airbnb.epoxy.CallbackProp
13 | import com.airbnb.epoxy.ModelProp
14 | import com.airbnb.epoxy.ModelView
15 | import com.binarybricks.coinbit.R
16 | import com.binarybricks.coinbit.data.database.entities.WatchedCoin
17 | import com.binarybricks.coinbit.network.BASE_CRYPTOCOMPARE_IMAGE_URL
18 | import java.math.BigDecimal
19 |
20 | @ModelView(autoLayout = ModelView.Size.MATCH_WIDTH_WRAP_HEIGHT)
21 | class CoinSearchItemView @JvmOverloads constructor(
22 | context: Context,
23 | attributeSet: AttributeSet? = null,
24 | defStyle: Int = 0,
25 | ) : ConstraintLayout(context, attributeSet, defStyle) {
26 |
27 | private val tvCoinName: TextView
28 | private val tvCoinSymbol: TextView
29 | private val ivCoin: ImageView
30 | private val cbWatched: SwitchCompat
31 | private val clCoinInfo: View
32 |
33 | private val cropCircleTransformation by lazy {
34 | CircleCropTransformation()
35 | }
36 |
37 | interface OnSearchItemClickListener {
38 | fun onItemWatchedClicked(watched: Boolean)
39 | }
40 |
41 | init {
42 | View.inflate(context, R.layout.coin_search_item, this)
43 | tvCoinName = findViewById(R.id.tvCoinPercentChange)
44 | tvCoinSymbol = findViewById(R.id.tvCoinName)
45 | ivCoin = findViewById(R.id.ivCoin)
46 | cbWatched = findViewById(R.id.scWatched)
47 | clCoinInfo = findViewById(R.id.clCoinInfo)
48 | }
49 |
50 | @ModelProp
51 | fun setWatchedCoin(watchedCoin: WatchedCoin) {
52 | tvCoinName.text = watchedCoin.coin.coinName
53 | tvCoinSymbol.text = watchedCoin.coin.symbol
54 |
55 | ivCoin.load(BASE_CRYPTOCOMPARE_IMAGE_URL + "${watchedCoin.coin.imageUrl}?width=50") {
56 | crossfade(true)
57 | error(R.mipmap.ic_launcher_round)
58 | transformations(cropCircleTransformation)
59 | }
60 |
61 | val purchaseQuantity = watchedCoin.purchaseQuantity
62 |
63 | cbWatched.isChecked = purchaseQuantity > BigDecimal.ZERO || watchedCoin.watched
64 | }
65 |
66 | @CallbackProp
67 | fun setItemClickListener(listener: OnClickListener?) {
68 | clCoinInfo.setOnClickListener(listener)
69 | }
70 |
71 | @CallbackProp
72 | fun setOnWatchedChecked(listener: OnSearchItemClickListener?) {
73 | cbWatched.setOnClickListener {
74 | listener?.onItemWatchedClicked(cbWatched.isChecked)
75 | }
76 | }
77 | }
78 |
--------------------------------------------------------------------------------
/app/src/main/java/com/binarybricks/coinbit/epoxymodels/CoinTickerView.kt:
--------------------------------------------------------------------------------
1 | package com.binarybricks.coinbit.epoxymodels
2 |
3 | import android.content.Context
4 | import android.util.AttributeSet
5 | import android.view.View
6 | import android.widget.ImageView
7 | import android.widget.TextView
8 | import androidx.constraintlayout.widget.ConstraintLayout
9 | import coil.clear
10 | import coil.load
11 | import coil.transform.CircleCropTransformation
12 | import com.airbnb.epoxy.*
13 | import com.binarybricks.coinbit.R
14 | import com.binarybricks.coinbit.network.BASE_CRYPTOCOMPARE_IMAGE_URL
15 | import com.binarybricks.coinbit.network.models.CryptoTicker
16 |
17 | @ModelView(autoLayout = ModelView.Size.MATCH_WIDTH_WRAP_HEIGHT)
18 | class CoinTickerView @JvmOverloads constructor(
19 | context: Context,
20 | attributeSet: AttributeSet? = null,
21 | defStyle: Int = 0,
22 | ) : ConstraintLayout(context, attributeSet, defStyle) {
23 |
24 | private val ivExchange: ImageView
25 | private val tvFromCoin: TextView
26 | private val tvToPrice: TextView
27 | private val tvExchange: TextView
28 | private val tvPrice: TextView
29 | private val tvVolume: TextView
30 | private val clMarket: View
31 |
32 | private val cropCircleTransformation by lazy {
33 | CircleCropTransformation()
34 | }
35 |
36 | init {
37 | View.inflate(context, R.layout.ticker_item, this)
38 | ivExchange = findViewById(R.id.ivExchange)
39 | tvFromCoin = findViewById(R.id.tvFromCoin)
40 | tvToPrice = findViewById(R.id.tvToPrice)
41 | tvExchange = findViewById(R.id.tvExchange)
42 | tvPrice = findViewById(R.id.tvPrice)
43 | tvVolume = findViewById(R.id.tvVolume)
44 | clMarket = findViewById(R.id.clMarket)
45 | }
46 |
47 | @ModelProp
48 | fun setTicker(ticker: CryptoTicker) {
49 | tvFromCoin.text = ticker.base
50 | tvToPrice.text = ticker.target
51 | tvExchange.text = ticker.marketName
52 | if (ticker.imageUrl.isNotEmpty()) {
53 | ivExchange.load(BASE_CRYPTOCOMPARE_IMAGE_URL + ticker.imageUrl) {
54 | crossfade(true)
55 | error(R.mipmap.ic_launcher_round)
56 | transformations(cropCircleTransformation)
57 | }
58 | } else {
59 | ivExchange.load(R.mipmap.ic_launcher_round)
60 | }
61 | }
62 |
63 | @TextProp
64 | fun setTickerPrice(price: CharSequence) {
65 | tvPrice.text = price
66 | }
67 |
68 | @TextProp
69 | fun setTickerVolume(volume: CharSequence) {
70 | tvVolume.text = volume
71 | }
72 |
73 | @OnViewRecycled
74 | fun viewRecycled() {
75 | ivExchange.clear()
76 | }
77 |
78 | @CallbackProp
79 | fun setItemClickListener(listener: OnClickListener?) {
80 | clMarket.setOnClickListener(listener)
81 | }
82 | }
83 |
--------------------------------------------------------------------------------
/app/src/main/java/com/binarybricks/coinbit/epoxymodels/EmptyCoinItemView.kt:
--------------------------------------------------------------------------------
1 | package com.binarybricks.coinbit.epoxymodels
2 |
3 | import android.content.Context
4 | import android.util.AttributeSet
5 | import android.view.View
6 | import android.widget.Button
7 | import android.widget.TextView
8 | import androidx.constraintlayout.widget.ConstraintLayout
9 | import com.airbnb.epoxy.CallbackProp
10 | import com.airbnb.epoxy.ModelProp
11 | import com.airbnb.epoxy.ModelView
12 | import com.binarybricks.coinbit.R
13 | import com.binarybricks.coinbit.featurecomponents.ModuleItem
14 |
15 | @ModelView(autoLayout = ModelView.Size.MATCH_WIDTH_WRAP_HEIGHT)
16 | class EmptyCoinItemView @JvmOverloads constructor(
17 | context: Context,
18 | attributeSet: AttributeSet? = null,
19 | defStyle: Int = 0,
20 | ) : ConstraintLayout(context, attributeSet, defStyle) {
21 |
22 | private val tvEmptyMessage: TextView
23 | private val btnAddTransaction: Button
24 |
25 | init {
26 | View.inflate(context, R.layout.dashboard_empty_card_module, this)
27 | tvEmptyMessage = findViewById(R.id.tvEmptyMessage)
28 | btnAddTransaction = findViewById(R.id.btnAddTransaction)
29 | }
30 |
31 | @ModelProp
32 | fun setEmptyCardData(dashboardEmptyCoinModuleData: DashboardEmptyCoinModuleData) {
33 | tvEmptyMessage.text = dashboardEmptyCoinModuleData.emptySpaceText
34 |
35 | if (dashboardEmptyCoinModuleData.ctaButtonText.isNotEmpty()) {
36 | btnAddTransaction.text = dashboardEmptyCoinModuleData.ctaButtonText
37 | }
38 | }
39 |
40 | @CallbackProp
41 | fun setItemClickListener(listener: OnClickListener?) {
42 | btnAddTransaction.setOnClickListener(listener)
43 | }
44 |
45 | data class DashboardEmptyCoinModuleData(val emptySpaceText: String, val ctaButtonText: String = "") : ModuleItem
46 | }
47 |
--------------------------------------------------------------------------------
/app/src/main/java/com/binarybricks/coinbit/epoxymodels/ExchangePairItemView.kt:
--------------------------------------------------------------------------------
1 | package com.binarybricks.coinbit.epoxymodels
2 |
3 | import android.content.Context
4 | import android.util.AttributeSet
5 | import android.view.View
6 | import android.widget.TextView
7 | import androidx.constraintlayout.widget.ConstraintLayout
8 | import com.airbnb.epoxy.CallbackProp
9 | import com.airbnb.epoxy.ModelView
10 | import com.airbnb.epoxy.TextProp
11 | import com.binarybricks.coinbit.R
12 |
13 | @ModelView(autoLayout = ModelView.Size.MATCH_WIDTH_WRAP_HEIGHT)
14 | class ExchangePairItemView @JvmOverloads constructor(
15 | context: Context,
16 | attributeSet: AttributeSet? = null,
17 | defStyle: Int = 0,
18 | ) : ConstraintLayout(context, attributeSet, defStyle) {
19 |
20 | private val tvSearchItemName: TextView
21 |
22 | init {
23 | View.inflate(context, R.layout.exchange_pair_search_item, this)
24 | tvSearchItemName = findViewById(R.id.tvArticleTitle)
25 | }
26 |
27 | @TextProp
28 | fun setExchangeName(exchnageName: CharSequence) {
29 | tvSearchItemName.text = exchnageName
30 | }
31 |
32 | @CallbackProp
33 | fun setItemClickListener(listener: OnClickListener?) {
34 | setOnClickListener(listener)
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/app/src/main/java/com/binarybricks/coinbit/epoxymodels/ExpandedNewsItemView.kt:
--------------------------------------------------------------------------------
1 | package com.binarybricks.coinbit.epoxymodels
2 |
3 | import android.content.Context
4 | import android.util.AttributeSet
5 | import android.view.View
6 | import android.widget.ImageView
7 | import android.widget.TextView
8 | import androidx.constraintlayout.widget.ConstraintLayout
9 | import coil.load
10 | import coil.transform.RoundedCornersTransformation
11 | import com.airbnb.epoxy.ModelProp
12 | import com.airbnb.epoxy.ModelView
13 | import com.binarybricks.coinbit.R
14 | import com.binarybricks.coinbit.featurecomponents.ModuleItem
15 | import com.binarybricks.coinbit.network.models.CryptoCompareNews
16 | import com.binarybricks.coinbit.utils.Formaters
17 | import com.binarybricks.coinbit.utils.openCustomTab
18 | import com.binarybricks.coinbit.utils.resourcemanager.AndroidResourceManager
19 | import com.binarybricks.coinbit.utils.resourcemanager.AndroidResourceManagerImpl
20 |
21 | @ModelView(autoLayout = ModelView.Size.MATCH_WIDTH_WRAP_HEIGHT)
22 | class ExpandedNewsItemView @JvmOverloads constructor(
23 | context: Context,
24 | attributeSet: AttributeSet? = null,
25 | defStyle: Int = 0,
26 | ) : ConstraintLayout(context, attributeSet, defStyle) {
27 |
28 | private val tvSource: TextView
29 | private val tvHeadlines: TextView
30 | private val tvTimePeriod: TextView
31 | private val ivNewsCover: ImageView
32 | private val clNewsArticleContainer: View
33 |
34 | private val androidResourceManager: AndroidResourceManager by lazy {
35 | AndroidResourceManagerImpl(context)
36 | }
37 |
38 | init {
39 | View.inflate(context, R.layout.discovery_news_module, this)
40 | tvSource = findViewById(R.id.tvSource)
41 | tvHeadlines = findViewById(R.id.tvHeadlines)
42 | tvTimePeriod = findViewById(R.id.tvTimePeriod)
43 | ivNewsCover = findViewById(R.id.ivNewsCover)
44 | clNewsArticleContainer = findViewById(R.id.clNewsArticleContainer)
45 | }
46 |
47 | @ModelProp
48 | fun setNews(discoveryNewsModuleData: DiscoveryNewsModuleData) {
49 | tvSource.text = discoveryNewsModuleData.coinNews.source
50 | tvHeadlines.text = discoveryNewsModuleData.coinNews.title
51 | if (discoveryNewsModuleData.coinNews.published_on != null) {
52 | tvTimePeriod.text = Formaters(androidResourceManager).formatTransactionDate(discoveryNewsModuleData.coinNews.published_on)
53 | }
54 |
55 | ivNewsCover.load(discoveryNewsModuleData.coinNews.imageurl) {
56 | crossfade(true)
57 | transformations(RoundedCornersTransformation(15f))
58 | }
59 |
60 | clNewsArticleContainer.setOnClickListener {
61 | if (discoveryNewsModuleData.coinNews.url != null) {
62 | openCustomTab(discoveryNewsModuleData.coinNews.url, context)
63 | }
64 | }
65 | }
66 |
67 | data class DiscoveryNewsModuleData(val coinNews: CryptoCompareNews) : ModuleItem
68 | }
69 |
--------------------------------------------------------------------------------
/app/src/main/java/com/binarybricks/coinbit/epoxymodels/GenericFooterItemView.kt:
--------------------------------------------------------------------------------
1 | package com.binarybricks.coinbit.epoxymodels
2 |
3 | import android.content.Context
4 | import android.util.AttributeSet
5 | import android.view.View
6 | import android.widget.TextView
7 | import androidx.constraintlayout.widget.ConstraintLayout
8 | import com.airbnb.epoxy.ModelProp
9 | import com.airbnb.epoxy.ModelView
10 | import com.binarybricks.coinbit.R
11 | import com.binarybricks.coinbit.featurecomponents.ModuleItem
12 | import com.binarybricks.coinbit.utils.openCustomTab
13 | import kotlinx.android.synthetic.main.generic_footer_module.view.*
14 |
15 | @ModelView(autoLayout = ModelView.Size.MATCH_WIDTH_WRAP_HEIGHT)
16 | class GenericFooterItemView @JvmOverloads constructor(
17 | context: Context,
18 | attributeSet: AttributeSet? = null,
19 | defStyle: Int = 0,
20 | ) : ConstraintLayout(context, attributeSet, defStyle) {
21 |
22 | private val tvFooter: TextView
23 |
24 | init {
25 | View.inflate(context, R.layout.generic_footer_module, this)
26 | tvFooter = findViewById(R.id.tvFooter)
27 | }
28 |
29 | @ModelProp(options = [ModelProp.Option.IgnoreRequireHashCode])
30 | fun setFooterContent(footerModuleData: FooterModuleData) {
31 | tvFooter.text = footerModuleData.footerText
32 |
33 | if (footerModuleData.footerUrlLink.isNotEmpty()) {
34 | clFooter.setOnClickListener {
35 | openCustomTab(footerModuleData.footerUrlLink, context)
36 | }
37 | } else {
38 | tvFooter.visibility = View.GONE
39 | }
40 | }
41 |
42 | data class FooterModuleData(val footerText: String = "", val footerUrlLink: String = "") : ModuleItem
43 | }
44 |
--------------------------------------------------------------------------------
/app/src/main/java/com/binarybricks/coinbit/epoxymodels/LabelItemView.kt:
--------------------------------------------------------------------------------
1 | package com.binarybricks.coinbit.epoxymodels
2 |
3 | import android.content.Context
4 | import android.util.AttributeSet
5 | import android.view.View
6 | import android.widget.TextView
7 | import androidx.constraintlayout.widget.ConstraintLayout
8 | import com.airbnb.epoxy.ModelView
9 | import com.airbnb.epoxy.TextProp
10 | import com.binarybricks.coinbit.R
11 | import com.binarybricks.coinbit.featurecomponents.ModuleItem
12 |
13 | @ModelView(autoLayout = ModelView.Size.MATCH_WIDTH_WRAP_HEIGHT)
14 | class LabelItemView @JvmOverloads constructor(
15 | context: Context,
16 | attributeSet: AttributeSet? = null,
17 | defStyle: Int = 0,
18 | ) : ConstraintLayout(context, attributeSet, defStyle) {
19 |
20 | private val tvLabel: TextView
21 |
22 | init {
23 | View.inflate(context, R.layout.coin_label_module, this)
24 | tvLabel = findViewById(R.id.tvLabel)
25 | }
26 |
27 | @TextProp
28 | fun setLabel(label: CharSequence) {
29 | tvLabel.text = label
30 | }
31 |
32 | data class LabelModuleData(val coinLabel: String) : ModuleItem
33 | }
34 |
--------------------------------------------------------------------------------
/app/src/main/java/com/binarybricks/coinbit/epoxymodels/NewsItemView.kt:
--------------------------------------------------------------------------------
1 | package com.binarybricks.coinbit.epoxymodels
2 |
3 | import android.content.Context
4 | import android.util.AttributeSet
5 | import android.view.View
6 | import android.widget.TextView
7 | import androidx.constraintlayout.widget.ConstraintLayout
8 | import com.airbnb.epoxy.CallbackProp
9 | import com.airbnb.epoxy.ModelView
10 | import com.airbnb.epoxy.TextProp
11 | import com.binarybricks.coinbit.R
12 |
13 | @ModelView(autoLayout = ModelView.Size.MATCH_WIDTH_WRAP_HEIGHT)
14 | class NewsItemView @JvmOverloads constructor(
15 | context: Context,
16 | attributeSet: AttributeSet? = null,
17 | defStyle: Int = 0,
18 | ) : ConstraintLayout(context, attributeSet, defStyle) {
19 |
20 | private val title: TextView
21 | private val date: TextView
22 | private val clArticle: View
23 |
24 | init {
25 | View.inflate(context, R.layout.news_item, this)
26 | title = findViewById(R.id.tvArticleTitle)
27 | date = findViewById(R.id.tvArticleTime)
28 | clArticle = findViewById(R.id.clArticle)
29 | }
30 |
31 | @TextProp
32 | fun setTitle(newsTitle: CharSequence) {
33 | title.text = newsTitle
34 | }
35 |
36 | @TextProp
37 | fun setNewsDate(formattedDate: CharSequence) {
38 | date.text = formattedDate
39 | }
40 |
41 | @CallbackProp
42 | fun setItemClickListener(listener: OnClickListener?) {
43 | clArticle.setOnClickListener(listener)
44 | }
45 | }
46 |
--------------------------------------------------------------------------------
/app/src/main/java/com/binarybricks/coinbit/epoxymodels/ShortNewsItemView.kt:
--------------------------------------------------------------------------------
1 | package com.binarybricks.coinbit.epoxymodels
2 |
3 | import android.content.Context
4 | import android.util.AttributeSet
5 | import android.view.View
6 | import android.widget.TextView
7 | import androidx.constraintlayout.widget.ConstraintLayout
8 | import androidx.core.widget.ContentLoadingProgressBar
9 | import com.airbnb.epoxy.CallbackProp
10 | import com.airbnb.epoxy.ModelView
11 | import com.airbnb.epoxy.TextProp
12 | import com.binarybricks.coinbit.R
13 | import com.binarybricks.coinbit.featurecomponents.ModuleItem
14 | import com.binarybricks.coinbit.network.models.CryptoCompareNews
15 |
16 | @ModelView(autoLayout = ModelView.Size.MATCH_WIDTH_WRAP_HEIGHT)
17 | class ShortNewsItemView @JvmOverloads constructor(
18 | context: Context,
19 | attributeSet: AttributeSet? = null,
20 | defStyle: Int = 0,
21 | ) : ConstraintLayout(context, attributeSet, defStyle) {
22 |
23 | private val pbLoading: ContentLoadingProgressBar
24 | private val tvNewsTitle: TextView
25 | private val clNewsArticleContainer: View
26 |
27 | init {
28 | View.inflate(context, R.layout.dashboard_news_module, this)
29 | pbLoading = findViewById(R.id.pbLoading)
30 | tvNewsTitle = findViewById(R.id.tvNewsTitle)
31 | clNewsArticleContainer = findViewById(R.id.clNewsArticleContainer)
32 | }
33 |
34 | @TextProp
35 | fun setNewsDate(news: CharSequence) {
36 | pbLoading.visibility = View.GONE
37 | tvNewsTitle.text = news
38 | }
39 |
40 | @CallbackProp
41 | fun setItemClickListener(listener: OnClickListener?) {
42 | clNewsArticleContainer.setOnClickListener(listener)
43 | }
44 |
45 | data class ShortNewsModuleData(val news: CryptoCompareNews) : ModuleItem
46 | }
47 |
--------------------------------------------------------------------------------
/app/src/main/java/com/binarybricks/coinbit/featurecomponents/ModuleItem.kt:
--------------------------------------------------------------------------------
1 | package com.binarybricks.coinbit.featurecomponents
2 |
3 | /**
4 | * Created by Pranay Airan
5 | */
6 | interface ModuleItem
7 |
--------------------------------------------------------------------------------
/app/src/main/java/com/binarybricks/coinbit/featurecomponents/cointickermodule/CoinTickerContract.kt:
--------------------------------------------------------------------------------
1 | import com.binarybricks.coinbit.features.BaseView
2 | import com.binarybricks.coinbit.network.models.CryptoTicker
3 |
4 | /**
5 | * Created by Pranay Airan
6 | */
7 |
8 | interface CoinTickerContract {
9 |
10 | interface View : BaseView {
11 | fun showOrHideLoadingIndicatorForTicker(showLoading: Boolean = true)
12 | fun onPriceTickersLoaded(tickerData: List)
13 | }
14 |
15 | interface Presenter {
16 | fun getCryptoTickers(coinName: String)
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/app/src/main/java/com/binarybricks/coinbit/featurecomponents/cointickermodule/CoinTickerPresenter.kt:
--------------------------------------------------------------------------------
1 | package com.binarybricks.coinbit.featurecomponents.cointickermodule
2 |
3 | import CoinTickerContract
4 | import com.binarybricks.coinbit.R
5 | import com.binarybricks.coinbit.features.BasePresenter
6 | import com.binarybricks.coinbit.utils.resourcemanager.AndroidResourceManager
7 | import kotlinx.coroutines.launch
8 | import timber.log.Timber
9 |
10 | /**
11 | * Created by Pranay Airan
12 | */
13 |
14 | class CoinTickerPresenter(
15 | private val coinTickerRepository: CoinTickerRepository,
16 | private val androidResourceManager: AndroidResourceManager
17 | ) : BasePresenter(), CoinTickerContract.Presenter {
18 |
19 | /**
20 | * Load the crypto ticker from the crypto panic api
21 | */
22 | override fun getCryptoTickers(coinName: String) {
23 |
24 | var updatedCoinName = coinName
25 |
26 | if (coinName.equals("XRP", true)) {
27 | updatedCoinName = "ripple"
28 | }
29 |
30 | currentView?.showOrHideLoadingIndicatorForTicker(true)
31 |
32 | launch {
33 | try {
34 | val cryptoTickers = coinTickerRepository.getCryptoTickers(updatedCoinName)
35 | if (cryptoTickers != null) {
36 | currentView?.onPriceTickersLoaded(cryptoTickers)
37 | }
38 | } catch (ex: Exception) {
39 | Timber.e(ex.localizedMessage)
40 | currentView?.onNetworkError(androidResourceManager.getString(R.string.error_ticker))
41 | } finally {
42 | currentView?.showOrHideLoadingIndicatorForTicker(false)
43 | }
44 | }
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/app/src/main/java/com/binarybricks/coinbit/featurecomponents/cointickermodule/CoinTickerRepository.kt:
--------------------------------------------------------------------------------
1 | package com.binarybricks.coinbit.featurecomponents.cointickermodule
2 |
3 | import com.binarybricks.coinbit.data.CoinBitCache
4 | import com.binarybricks.coinbit.data.database.CoinBitDatabase
5 | import com.binarybricks.coinbit.data.database.entities.Exchange
6 | import com.binarybricks.coinbit.network.api.api
7 | import com.binarybricks.coinbit.network.models.CryptoTicker
8 | import com.binarybricks.coinbit.utils.getCoinTickerFromJson
9 |
10 | /**
11 | * Created by Pranay Airan
12 | * Repository that interact with coin gecko api to get ticker info.
13 | */
14 |
15 | class CoinTickerRepository(
16 | private val coinBitDatabase: CoinBitDatabase?
17 | ) {
18 |
19 | /**
20 | * Get the ticker info from coin gecko
21 | */
22 | suspend fun getCryptoTickers(coinName: String): List? {
23 |
24 | return if (CoinBitCache.ticker.containsKey(coinName)) {
25 | CoinBitCache.ticker[coinName]
26 | } else {
27 | val exchangeList = loadExchangeList()
28 | val coinTickerFromJson = getCoinTickerFromJson(api.getCoinTicker(coinName), exchangeList)
29 | if (coinTickerFromJson.isNotEmpty()) {
30 | CoinBitCache.ticker[coinName] = coinTickerFromJson
31 | coinTickerFromJson
32 | } else {
33 | null
34 | }
35 | }
36 | }
37 |
38 | /**
39 | * Get list of all exchanges, this is needed for logo
40 | */
41 | private suspend fun loadExchangeList(): List? {
42 | coinBitDatabase?.let {
43 | return it.exchangeDao().getAllExchanges()
44 | }
45 | return null
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/app/src/main/java/com/binarybricks/coinbit/featurecomponents/cryptonewsmodule/CryptoNewsContract.kt:
--------------------------------------------------------------------------------
1 | import com.binarybricks.coinbit.features.BaseView
2 | import com.binarybricks.coinbit.network.models.CryptoPanicNews
3 |
4 | /**
5 | * Created by Pragya Agrawal
6 | */
7 |
8 | interface CryptoNewsContract {
9 |
10 | interface View : BaseView {
11 | fun showOrHideLoadingIndicator(showLoading: Boolean = true)
12 | fun onNewsLoaded(cryptoPanicNews: CryptoPanicNews)
13 | }
14 |
15 | interface Presenter {
16 | fun getCryptoNews(coinSymbol: String)
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/app/src/main/java/com/binarybricks/coinbit/featurecomponents/cryptonewsmodule/CryptoNewsPresenter.kt:
--------------------------------------------------------------------------------
1 | package com.binarybricks.coinbit.featurecomponents.cryptonewsmodule
2 |
3 | import CryptoNewsContract
4 | import com.binarybricks.coinbit.features.BasePresenter
5 | import kotlinx.coroutines.launch
6 | import timber.log.Timber
7 |
8 | /**
9 | * Created by Pragya Agrawal
10 | */
11 |
12 | class CryptoNewsPresenter(private val cryptoNewsRepository: CryptoNewsRepository) :
13 | BasePresenter(), CryptoNewsContract.Presenter {
14 |
15 | /**
16 | * Load the crypto news from the crypto panic api
17 | */
18 | override fun getCryptoNews(coinSymbol: String) {
19 |
20 | currentView?.showOrHideLoadingIndicator(true)
21 |
22 | launch {
23 | try {
24 | val cryptoPanicNews = cryptoNewsRepository.getCryptoPanicNews(coinSymbol)
25 | currentView?.onNewsLoaded(cryptoPanicNews)
26 | } catch (ex: Exception) {
27 | Timber.e(ex.localizedMessage)
28 | } finally {
29 | currentView?.showOrHideLoadingIndicator(false)
30 | }
31 | }
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/app/src/main/java/com/binarybricks/coinbit/featurecomponents/cryptonewsmodule/CryptoNewsRepository.kt:
--------------------------------------------------------------------------------
1 | package com.binarybricks.coinbit.featurecomponents.cryptonewsmodule
2 |
3 | import com.binarybricks.coinbit.data.CoinBitCache
4 | import com.binarybricks.coinbit.network.api.API
5 | import com.binarybricks.coinbit.network.api.cryptoCompareRetrofit
6 | import com.binarybricks.coinbit.network.models.CryptoPanicNews
7 |
8 | /**
9 | * Created by Pragya Agrawal
10 | * Repository that interact with crypto api to get news.
11 | */
12 |
13 | class CryptoNewsRepository {
14 |
15 | /**
16 | * Get the top news for specific coin from cryptopanic
17 | */
18 | suspend fun getCryptoPanicNews(coinSymbol: String): CryptoPanicNews {
19 |
20 | return if (CoinBitCache.newsMap.containsKey(coinSymbol)) {
21 | CoinBitCache.newsMap[coinSymbol]!!
22 | } else {
23 | cryptoCompareRetrofit.create(API::class.java)
24 | .getCryptoNewsForCurrency(coinSymbol, "important", true)
25 | }
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/app/src/main/java/com/binarybricks/coinbit/featurecomponents/historicalchartmodule/ChartRepository.kt:
--------------------------------------------------------------------------------
1 | package com.binarybricks.coinbit.featurecomponents.historicalchartmodule
2 |
3 | import com.binarybricks.coinbit.network.*
4 | import com.binarybricks.coinbit.network.api.api
5 | import com.binarybricks.coinbit.network.models.CryptoCompareHistoricalResponse
6 | import timber.log.Timber
7 |
8 | /**
9 | Created by Pranay Airan
10 | * Repository that interact with crypto api to get charts.
11 | */
12 |
13 | class ChartRepository {
14 |
15 | /**
16 | * Get the historical data for specific crypto currencies. [period] specifies what time period you
17 | * want data from. [fromCurrencySymbol] specifies what currencies data you want for example bitcoin.[toCurrencySymbol]
18 | * is which currency you want data in for like USD
19 | */
20 | suspend fun getCryptoHistoricalData(period: String, fromCurrencySymbol: String?, toCurrencySymbol: String?): Pair, CryptoCompareHistoricalResponse.Data?> {
21 |
22 | val histoPeriod: String
23 | var limit = 30
24 | var aggregate = 1
25 | when (period) {
26 | HOUR -> {
27 | histoPeriod = HISTO_MINUTE
28 | limit = 60
29 | aggregate = 12 // this pulls for 12 hour
30 | }
31 | HOURS24 -> {
32 | histoPeriod = HISTO_HOUR
33 | limit = 24 // 1 day
34 | }
35 | WEEK -> {
36 | histoPeriod = HISTO_HOUR
37 | aggregate = 6 // 1 week limit is 128 hours default that is
38 | }
39 | MONTH -> {
40 | histoPeriod = HISTO_DAY
41 | limit = 30 // 30 days
42 | }
43 | MONTH3 -> {
44 | histoPeriod = HISTO_DAY
45 | limit = 90 // 90 days
46 | }
47 | YEAR -> {
48 | histoPeriod = HISTO_DAY
49 | aggregate = 13 // default limit is 30 so 30*12 365 days
50 | }
51 | ALL -> {
52 | histoPeriod = HISTO_DAY
53 | aggregate = 30
54 | limit = 2000
55 | }
56 | else -> {
57 | histoPeriod = HISTO_HOUR
58 | limit = 24 // 1 day
59 | }
60 | }
61 |
62 | val historicalData = api.getCryptoHistoricalData(histoPeriod, fromCurrencySymbol, toCurrencySymbol, limit, aggregate)
63 | Timber.d("Size of response %s", historicalData.data.size)
64 | val maxClosingValueFromHistoricalData = historicalData.data.maxBy { it.close.toFloat() }
65 | return Pair(historicalData.data, maxClosingValueFromHistoricalData)
66 | }
67 | }
68 |
--------------------------------------------------------------------------------
/app/src/main/java/com/binarybricks/coinbit/featurecomponents/historicalchartmodule/HistoricalChartAdapter.kt:
--------------------------------------------------------------------------------
1 | package com.binarybricks.coinbit.featurecomponents.historicalchartmodule
2 |
3 | import com.binarybricks.coinbit.network.models.CryptoCompareHistoricalResponse
4 | import com.robinhood.spark.SparkAdapter
5 |
6 | /**
7 | Created by Pranay Airan 1/13/18.
8 | */
9 |
10 | class HistoricalChartAdapter(private val historicalData: List, private val baseLineValue: String?) : SparkAdapter() {
11 |
12 | override fun getY(index: Int): Float {
13 | return historicalData[index].close.toFloat()
14 | }
15 |
16 | override fun getItem(index: Int): CryptoCompareHistoricalResponse.Data {
17 | return historicalData[index]
18 | }
19 |
20 | override fun getCount(): Int {
21 | return historicalData.size
22 | }
23 |
24 | override fun hasBaseLine(): Boolean {
25 | return true
26 | }
27 |
28 | override fun getBaseLine(): Float {
29 | return baseLineValue?.toFloat() ?: super.getBaseLine()
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/app/src/main/java/com/binarybricks/coinbit/features/BasePresenter.kt:
--------------------------------------------------------------------------------
1 | package com.binarybricks.coinbit.features
2 |
3 | import androidx.lifecycle.Lifecycle
4 | import androidx.lifecycle.LifecycleObserver
5 | import androidx.lifecycle.OnLifecycleEvent
6 | import kotlinx.coroutines.CoroutineScope
7 | import kotlinx.coroutines.Dispatchers
8 | import kotlinx.coroutines.SupervisorJob
9 | import kotlin.coroutines.CoroutineContext
10 |
11 | /**
12 | * A base class for all our presenters. It provides the basics nuts & bolts of attaching/detaching a presenter to/from a
13 | * view, as well as the strings resolution class.
14 | */
15 |
16 | open class BasePresenter(private val uiContext: CoroutineContext = Dispatchers.Main.immediate) : LifecycleObserver, CoroutineScope {
17 |
18 | protected var currentView: V? = null
19 | private val job = SupervisorJob()
20 |
21 | /**
22 | * Check if the view is attached.
23 | * This checking is only necessary when returning from an asynchronous call
24 | *
25 | * @return true if a view is attached to this presenter. false otherwise.
26 | */
27 | protected val isViewAttached: Boolean get() = currentView != null
28 |
29 | fun attachView(view: V) {
30 |
31 | if (currentView != null) {
32 | currentView = null
33 | }
34 | currentView = view
35 | }
36 |
37 | fun detachView() {
38 | job.cancel()
39 | currentView = null
40 | }
41 |
42 | // cleanup
43 | @OnLifecycleEvent(Lifecycle.Event.ON_DESTROY)
44 | fun cleanYourSelf() {
45 | detachView()
46 | }
47 |
48 | override val coroutineContext: CoroutineContext
49 | get() = uiContext + job
50 | }
51 |
--------------------------------------------------------------------------------
/app/src/main/java/com/binarybricks/coinbit/features/BaseView.kt:
--------------------------------------------------------------------------------
1 | package com.binarybricks.coinbit.features
2 |
3 | /**
4 | * A base view interface used for all views,
5 | * and to signal network errors.
6 | */
7 | interface BaseView {
8 |
9 | /**
10 | * Callback to signal a network error
11 | **/
12 | fun onNetworkError(errorMessage: String)
13 | }
14 |
--------------------------------------------------------------------------------
/app/src/main/java/com/binarybricks/coinbit/features/coin/CoinContract.kt:
--------------------------------------------------------------------------------
1 | import com.binarybricks.coinbit.data.database.entities.CoinTransaction
2 | import com.binarybricks.coinbit.data.database.entities.WatchedCoin
3 | import com.binarybricks.coinbit.features.BaseView
4 | import com.binarybricks.coinbit.network.models.CoinPrice
5 | import com.binarybricks.coinbit.network.models.CryptoCompareHistoricalResponse
6 |
7 | /**
8 | Created by Pranay Airan
9 | */
10 |
11 | interface CoinContract {
12 |
13 | interface View : BaseView {
14 | fun onCoinPriceLoaded(coinPrice: CoinPrice?, watchedCoin: WatchedCoin)
15 | fun onRecentTransactionLoaded(coinTransactionList: List)
16 | fun onCoinWatchedStatusUpdated(watched: Boolean, coinSymbol: String)
17 | fun onHistoricalDataLoaded(period: String, historicalDataPair: Pair, CryptoCompareHistoricalResponse.Data?>)
18 | }
19 |
20 | interface Presenter {
21 | fun loadCurrentCoinPrice(watchedCoin: WatchedCoin, toCurrency: String)
22 | fun loadRecentTransaction(symbol: String)
23 | fun updateCoinWatchedStatus(watched: Boolean, coinID: String, coinSymbol: String)
24 | fun loadHistoricalData(period: String, fromCurrency: String, toCurrency: String)
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/app/src/main/java/com/binarybricks/coinbit/features/coin/CoinPresenter.kt:
--------------------------------------------------------------------------------
1 | package com.binarybricks.coinbit.features.coin
2 |
3 | import CoinContract
4 | import com.binarybricks.coinbit.data.database.entities.WatchedCoin
5 | import com.binarybricks.coinbit.featurecomponents.historicalchartmodule.ChartRepository
6 | import com.binarybricks.coinbit.features.BasePresenter
7 | import com.binarybricks.coinbit.features.CryptoCompareRepository
8 | import kotlinx.coroutines.flow.catch
9 | import kotlinx.coroutines.flow.collect
10 | import kotlinx.coroutines.launch
11 | import timber.log.Timber
12 |
13 | /**
14 | Created by Pranay Airan
15 | */
16 |
17 | class CoinPresenter(
18 | private val coinRepo: CryptoCompareRepository,
19 | private val chartRepo: ChartRepository
20 | ) : BasePresenter(), CoinContract.Presenter {
21 |
22 | /**
23 | * Get the current price of a coinSymbol say btc or eth
24 | */
25 | override fun loadCurrentCoinPrice(watchedCoin: WatchedCoin, toCurrency: String) {
26 | launch {
27 | try {
28 | currentView?.onCoinPriceLoaded(coinRepo.getCoinPriceFull(watchedCoin.coin.symbol, toCurrency), watchedCoin)
29 | } catch (ex: Exception) {
30 | Timber.e(ex.localizedMessage)
31 | }
32 | }
33 | }
34 |
35 | override fun loadRecentTransaction(symbol: String) {
36 | launch {
37 | coinRepo.getRecentTransaction(symbol)
38 | ?.catch {
39 | Timber.e(it.localizedMessage)
40 | }
41 | ?.collect { coinTransactionsList ->
42 | coinTransactionsList?.let {
43 | currentView?.onRecentTransactionLoaded(it)
44 | }
45 | }
46 | }
47 | }
48 |
49 | override fun updateCoinWatchedStatus(watched: Boolean, coinID: String, coinSymbol: String) {
50 |
51 | launch {
52 | try {
53 | coinRepo.updateCoinWatchedStatus(watched, coinID)
54 | Timber.d("Coin status updated")
55 | currentView?.onCoinWatchedStatusUpdated(watched, coinSymbol)
56 | } catch (ex: Exception) {
57 | Timber.e(ex.localizedMessage)
58 | currentView?.onNetworkError(ex.localizedMessage ?: "Error")
59 | }
60 | }
61 | }
62 |
63 | /**
64 | * Load historical data for the coin to show the chart.
65 | */
66 | override fun loadHistoricalData(period: String, fromCurrency: String, toCurrency: String) {
67 | launch {
68 | try {
69 | currentView?.onHistoricalDataLoaded(period, chartRepo.getCryptoHistoricalData(period, fromCurrency, toCurrency))
70 | } catch (ex: Exception) {
71 | Timber.e(ex.localizedMessage)
72 | }
73 | }
74 | }
75 | }
76 |
--------------------------------------------------------------------------------
/app/src/main/java/com/binarybricks/coinbit/features/coindetails/CoinDetailPagerPresenter.kt:
--------------------------------------------------------------------------------
1 | package com.binarybricks.coinbit.features.coindetails
2 |
3 | import CoinDetailsPagerContract
4 | import com.binarybricks.coinbit.features.BasePresenter
5 | import kotlinx.coroutines.launch
6 | import timber.log.Timber
7 |
8 | /**
9 | Created by Pranay Airan
10 | */
11 |
12 | class CoinDetailPagerPresenter(private val coinDetailsPagerRepository: CoinDetailsPagerRepository) :
13 | BasePresenter(), CoinDetailsPagerContract.Presenter {
14 | override fun loadWatchedCoins() {
15 | launch {
16 | try {
17 | currentView?.onWatchedCoinsLoaded(coinDetailsPagerRepository.loadWatchedCoins())
18 | } catch (ex: Exception) {
19 | Timber.e(ex.localizedMessage)
20 | }
21 | }
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/app/src/main/java/com/binarybricks/coinbit/features/coindetails/CoinDetailPresenter.kt:
--------------------------------------------------------------------------------
1 | package com.binarybricks.coinbit.features.coindetails
2 |
3 | import CoinDetailsContract
4 | import com.binarybricks.coinbit.features.BasePresenter
5 | import com.binarybricks.coinbit.features.CryptoCompareRepository
6 | import kotlinx.coroutines.launch
7 | import timber.log.Timber
8 |
9 | /**
10 | Created by Pranay Airan
11 | */
12 |
13 | class CoinDetailPresenter(
14 | private val coinRepo: CryptoCompareRepository
15 | ) : BasePresenter(),
16 | CoinDetailsContract.Presenter {
17 |
18 | override fun getWatchedCoinFromSymbol(symbol: String) {
19 |
20 | currentView?.showOrHideLoadingIndicator(true)
21 |
22 | launch {
23 | try {
24 | val singleCoin = coinRepo.getSingleCoin(symbol)
25 | Timber.d("watched coin loaded")
26 | currentView?.showOrHideLoadingIndicator(false)
27 | if (singleCoin != null) {
28 | currentView?.onWatchedCoinLoaded(singleCoin.first())
29 | } else {
30 | currentView?.onWatchedCoinLoaded(null)
31 | }
32 | } catch (ex: Exception) {
33 | Timber.e(ex.localizedMessage)
34 | currentView?.onNetworkError(ex.localizedMessage)
35 | }
36 | }
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/app/src/main/java/com/binarybricks/coinbit/features/coindetails/CoinDetailsContract.kt:
--------------------------------------------------------------------------------
1 | import com.binarybricks.coinbit.data.database.entities.WatchedCoin
2 | import com.binarybricks.coinbit.features.BaseView
3 |
4 | /**
5 | Created by Pranay Airan
6 | */
7 |
8 | interface CoinDetailsContract {
9 |
10 | interface View : BaseView {
11 | fun showOrHideLoadingIndicator(showLoading: Boolean = true)
12 | fun onWatchedCoinLoaded(coin: WatchedCoin?)
13 | }
14 |
15 | interface Presenter {
16 | fun getWatchedCoinFromSymbol(symbol: String)
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/app/src/main/java/com/binarybricks/coinbit/features/coindetails/CoinDetailsPagerAdapter.kt:
--------------------------------------------------------------------------------
1 | package com.binarybricks.coinbit.features.coindetails
2 |
3 | import androidx.fragment.app.Fragment
4 | import androidx.fragment.app.FragmentManager
5 | import androidx.fragment.app.FragmentStatePagerAdapter
6 | import com.binarybricks.coinbit.data.database.entities.WatchedCoin
7 | import com.binarybricks.coinbit.features.coin.CoinFragment
8 |
9 | /**
10 | * Created by pranay airan on 2/11/18.
11 | */
12 |
13 | class CoinDetailsPagerAdapter(private val watchedCoinList: List?, fm: FragmentManager) : FragmentStatePagerAdapter(fm, FragmentStatePagerAdapter.BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT) {
14 | override fun getItem(position: Int): Fragment {
15 | val coinDetailsFragment = CoinFragment()
16 | if (watchedCoinList != null) {
17 | coinDetailsFragment.arguments = CoinFragment.getArgumentBundle(watchedCoinList[position])
18 | return coinDetailsFragment
19 | }
20 | return coinDetailsFragment
21 | }
22 |
23 | override fun getCount(): Int {
24 | return watchedCoinList?.size ?: 0
25 | }
26 |
27 | override fun getPageTitle(position: Int): CharSequence? {
28 | return watchedCoinList?.get(position)?.coin?.symbol ?: super.getPageTitle(position)
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/app/src/main/java/com/binarybricks/coinbit/features/coindetails/CoinDetailsPagerContract.kt:
--------------------------------------------------------------------------------
1 | import com.binarybricks.coinbit.data.database.entities.WatchedCoin
2 | import com.binarybricks.coinbit.features.BaseView
3 |
4 | /**
5 | Created by Pranay Airan
6 | */
7 |
8 | interface CoinDetailsPagerContract {
9 |
10 | interface View : BaseView {
11 | fun onWatchedCoinsLoaded(watchedCoinList: List?)
12 | }
13 |
14 | interface Presenter {
15 | fun loadWatchedCoins()
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/app/src/main/java/com/binarybricks/coinbit/features/coindetails/CoinDetailsPagerRepository.kt:
--------------------------------------------------------------------------------
1 | package com.binarybricks.coinbit.features.coindetails
2 |
3 | import com.binarybricks.coinbit.data.database.CoinBitDatabase
4 | import com.binarybricks.coinbit.data.database.entities.WatchedCoin
5 |
6 | /**
7 | Created by Pranay Airan
8 | * Repository that interact with crypto api and database for getting data.
9 | */
10 |
11 | class CoinDetailsPagerRepository(
12 | private val coinBitDatabase: CoinBitDatabase?
13 | ) {
14 |
15 | /**
16 | * Get list of all coins that is added in watch list
17 | */
18 | suspend fun loadWatchedCoins(): List? {
19 |
20 | coinBitDatabase?.let {
21 | return it.watchedCoinDao().getAllWatchedCoinsOnetime()
22 | }
23 | return null
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/app/src/main/java/com/binarybricks/coinbit/features/coinsearch/CoinDiscoveryContract.kt:
--------------------------------------------------------------------------------
1 | import com.binarybricks.coinbit.features.BaseView
2 | import com.binarybricks.coinbit.network.models.CoinPair
3 | import com.binarybricks.coinbit.network.models.CoinPrice
4 | import com.binarybricks.coinbit.network.models.CryptoCompareNews
5 |
6 | /**
7 | Created by Pranay Airan
8 | */
9 |
10 | interface CoinDiscoveryContract {
11 |
12 | interface View : BaseView {
13 | fun onTopCoinsByTotalVolumeLoaded(topCoins: List)
14 | fun onTopCoinListByPairVolumeLoaded(topPair: List)
15 | fun onCoinNewsLoaded(coinNews: List)
16 | }
17 |
18 | interface Presenter {
19 | fun getTopCoinListByMarketCap(toCurrencySymbol: String)
20 | fun getTopCoinListByPairVolume()
21 | fun getCryptoCurrencyNews()
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/app/src/main/java/com/binarybricks/coinbit/features/coinsearch/CoinDiscoveryPresenter.kt:
--------------------------------------------------------------------------------
1 | package com.binarybricks.coinbit.features.coinsearch
2 |
3 | import CoinDiscoveryContract
4 | import com.binarybricks.coinbit.features.BasePresenter
5 | import com.binarybricks.coinbit.features.CryptoCompareRepository
6 | import kotlinx.coroutines.launch
7 | import timber.log.Timber
8 |
9 | /**
10 | Created by Pranay Airan
11 | */
12 |
13 | class CoinDiscoveryPresenter(
14 | private val coinRepo: CryptoCompareRepository
15 | ) : BasePresenter(),
16 | CoinDiscoveryContract.Presenter {
17 |
18 | override fun getTopCoinListByMarketCap(toCurrencySymbol: String) {
19 | launch {
20 | try {
21 | currentView?.onTopCoinsByTotalVolumeLoaded(coinRepo.getTopCoinsByTotalVolume(toCurrencySymbol))
22 | } catch (ex: Exception) {
23 | Timber.e(ex.localizedMessage)
24 | }
25 | }
26 | }
27 |
28 | override fun getTopCoinListByPairVolume() {
29 |
30 | launch {
31 | try {
32 | currentView?.onTopCoinListByPairVolumeLoaded(coinRepo.getTopPairsByTotalVolume("BTC"))
33 | Timber.d("Top coins by pair Loaded")
34 | } catch (ex: Exception) {
35 | Timber.e(ex.localizedMessage)
36 | }
37 | }
38 | }
39 |
40 | override fun getCryptoCurrencyNews() {
41 | launch {
42 | try {
43 | val topNewsFromCryptoCompare = coinRepo.getTopNewsFromCryptoCompare()
44 | currentView?.onCoinNewsLoaded(topNewsFromCryptoCompare)
45 | Timber.d("All news Loaded")
46 | } catch (ex: Exception) {
47 | Timber.e(ex.localizedMessage)
48 | }
49 | }
50 | }
51 | }
52 |
--------------------------------------------------------------------------------
/app/src/main/java/com/binarybricks/coinbit/features/coinsearch/CoinSearchContract.kt:
--------------------------------------------------------------------------------
1 | import com.binarybricks.coinbit.data.database.entities.WatchedCoin
2 | import com.binarybricks.coinbit.features.BaseView
3 |
4 | /**
5 | Created by Pranay Airan
6 | */
7 |
8 | interface CoinSearchContract {
9 |
10 | interface View : BaseView {
11 | fun showOrHideLoadingIndicator(showLoading: Boolean = true)
12 | fun onCoinsLoaded(coinList: List)
13 | fun onCoinWatchedStatusUpdated(watched: Boolean, coinSymbol: String)
14 | }
15 |
16 | interface Presenter {
17 | fun loadAllCoins()
18 | fun updateCoinWatchedStatus(watched: Boolean, coinID: String, coinSymbol: String)
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/app/src/main/java/com/binarybricks/coinbit/features/coinsearch/CoinSearchPresenter.kt:
--------------------------------------------------------------------------------
1 | package com.binarybricks.coinbit.features.coinsearch
2 |
3 | import CoinSearchContract
4 | import com.binarybricks.coinbit.features.BasePresenter
5 | import com.binarybricks.coinbit.features.CryptoCompareRepository
6 | import kotlinx.coroutines.flow.catch
7 | import kotlinx.coroutines.flow.collect
8 | import kotlinx.coroutines.launch
9 | import timber.log.Timber
10 |
11 | /**
12 | Created by Pranay Airan
13 | */
14 |
15 | class CoinSearchPresenter(
16 | private val coinRepo: CryptoCompareRepository
17 | ) : BasePresenter(),
18 | CoinSearchContract.Presenter {
19 |
20 | override fun loadAllCoins() {
21 | currentView?.showOrHideLoadingIndicator(true)
22 |
23 | launch {
24 | coinRepo.getAllCoins()
25 | ?.catch {
26 | Timber.e(it)
27 | currentView?.onNetworkError(it.localizedMessage)
28 | }
29 | ?.collect {
30 | Timber.d("All Coins Loaded")
31 | currentView?.showOrHideLoadingIndicator(false)
32 | currentView?.onCoinsLoaded(it)
33 | }
34 | }
35 | }
36 |
37 | override fun updateCoinWatchedStatus(watched: Boolean, coinID: String, coinSymbol: String) {
38 | launch {
39 | try {
40 | coinRepo.updateCoinWatchedStatus(watched, coinID)
41 | Timber.d("Coin status updated")
42 | currentView?.onCoinWatchedStatusUpdated(watched, coinSymbol)
43 | } catch (ex: Exception) {
44 | Timber.e(ex.localizedMessage)
45 | currentView?.onNetworkError(ex.localizedMessage ?: "Error")
46 | }
47 | }
48 | }
49 | }
50 |
--------------------------------------------------------------------------------
/app/src/main/java/com/binarybricks/coinbit/features/dashboard/CoinDashboardContract.kt:
--------------------------------------------------------------------------------
1 | import com.binarybricks.coinbit.data.database.entities.CoinTransaction
2 | import com.binarybricks.coinbit.data.database.entities.WatchedCoin
3 | import com.binarybricks.coinbit.features.BaseView
4 | import com.binarybricks.coinbit.network.models.CoinPrice
5 | import com.binarybricks.coinbit.network.models.CryptoCompareNews
6 |
7 | /**
8 | Created by Pranay Airan
9 | */
10 |
11 | interface CoinDashboardContract {
12 |
13 | interface View : BaseView {
14 | fun onWatchedCoinsAndTransactionsLoaded(watchedCoinList: List, coinTransactionList: List)
15 | fun onCoinPricesLoaded(coinPriceListMap: HashMap)
16 | fun onTopCoinsByTotalVolumeLoaded(topCoins: List)
17 | fun onCoinNewsLoaded(coinNews: List)
18 | }
19 |
20 | interface Presenter {
21 | fun loadWatchedCoinsAndTransactions()
22 | fun loadCoinsPrices(fromCurrencySymbol: String, toCurrencySymbol: String)
23 | fun getTopCoinsByTotalVolume24hours(toCurrencySymbol: String)
24 | fun getLatestNewsFromCryptoCompare()
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/app/src/main/java/com/binarybricks/coinbit/features/dashboard/CoinDashboardPresenter.kt:
--------------------------------------------------------------------------------
1 | package com.binarybricks.coinbit.features.dashboard
2 |
3 | import CoinDashboardContract
4 | import com.binarybricks.coinbit.data.CoinBitCache
5 | import com.binarybricks.coinbit.features.BasePresenter
6 | import com.binarybricks.coinbit.features.CryptoCompareRepository
7 | import com.binarybricks.coinbit.network.models.CoinPrice
8 | import kotlinx.coroutines.flow.catch
9 | import kotlinx.coroutines.flow.collect
10 | import kotlinx.coroutines.flow.zip
11 | import kotlinx.coroutines.launch
12 | import timber.log.Timber
13 |
14 | /**
15 | Created by Pranay Airan
16 | */
17 |
18 | class CoinDashboardPresenter(
19 | private val dashboardRepository: DashboardRepository,
20 | private val coinRepo: CryptoCompareRepository
21 | ) : BasePresenter(),
22 | CoinDashboardContract.Presenter {
23 |
24 | override fun loadWatchedCoinsAndTransactions() {
25 | val watchedCoins = dashboardRepository.loadWatchedCoins()
26 | val transactions = dashboardRepository.loadTransactions()
27 |
28 | if (watchedCoins != null && transactions != null) {
29 | launch {
30 | watchedCoins.zip(transactions) { watchedCoinList, transactionList ->
31 | Pair(watchedCoinList, transactionList)
32 | }.catch {
33 | Timber.e(it.localizedMessage)
34 | }.collect {
35 | currentView?.onWatchedCoinsAndTransactionsLoaded(it.first, it.second)
36 | }
37 | }
38 | }
39 | }
40 |
41 | override fun loadCoinsPrices(fromCurrencySymbol: String, toCurrencySymbol: String) {
42 | launch {
43 | try {
44 | val coinPriceList = dashboardRepository.getCoinPriceFull(fromCurrencySymbol, toCurrencySymbol)
45 | val coinPriceMap: HashMap = hashMapOf()
46 | coinPriceList.forEach { coinPrice ->
47 | coinPrice.fromSymbol?.let { fromCurrencySymbol -> coinPriceMap[fromCurrencySymbol.toUpperCase()] = coinPrice }
48 | }
49 | if (coinPriceMap.isNotEmpty()) {
50 | CoinBitCache.coinPriceMap.putAll(coinPriceMap)
51 | }
52 |
53 | currentView?.onCoinPricesLoaded(coinPriceMap)
54 | } catch (ex: Exception) {
55 | Timber.e(ex.localizedMessage)
56 | }
57 | }
58 | }
59 |
60 | override fun getTopCoinsByTotalVolume24hours(toCurrencySymbol: String) {
61 | launch {
62 | try {
63 | val topCoinsByTotalVolume24hours = coinRepo.getTopCoinsByTotalVolume24hours(toCurrencySymbol)
64 | currentView?.onTopCoinsByTotalVolumeLoaded(topCoinsByTotalVolume24hours)
65 | Timber.d("All Exchange Loaded")
66 | } catch (ex: Exception) {
67 | Timber.e(ex.localizedMessage)
68 | }
69 | }
70 | }
71 |
72 | override fun getLatestNewsFromCryptoCompare() {
73 | launch {
74 | try {
75 | val topNewsFromCryptoCompare = coinRepo.getTopNewsFromCryptoCompare()
76 | currentView?.onCoinNewsLoaded(topNewsFromCryptoCompare)
77 | Timber.d("All news Loaded")
78 | } catch (ex: Exception) {
79 | Timber.e(ex.localizedMessage)
80 | }
81 | }
82 | }
83 | }
84 |
--------------------------------------------------------------------------------
/app/src/main/java/com/binarybricks/coinbit/features/dashboard/DashboardRepository.kt:
--------------------------------------------------------------------------------
1 | package com.binarybricks.coinbit.features.dashboard
2 |
3 | import com.binarybricks.coinbit.data.database.CoinBitDatabase
4 | import com.binarybricks.coinbit.data.database.entities.CoinTransaction
5 | import com.binarybricks.coinbit.data.database.entities.WatchedCoin
6 | import com.binarybricks.coinbit.network.api.api
7 | import com.binarybricks.coinbit.network.models.CoinPrice
8 | import com.binarybricks.coinbit.utils.getCoinPricesFromJson
9 | import kotlinx.coroutines.flow.Flow
10 | import kotlinx.coroutines.flow.distinctUntilChanged
11 |
12 | /**
13 | Created by Pranay Airan
14 | * Repository that interact with crypto api and database for getting data.
15 | */
16 |
17 | class DashboardRepository(
18 | private val coinBitDatabase: CoinBitDatabase?
19 | ) {
20 |
21 | /**
22 | * Get list of all coins that is added in watch list
23 | */
24 | fun loadWatchedCoins(): Flow>? {
25 | coinBitDatabase?.let {
26 | return it.watchedCoinDao().getAllWatchedCoins().distinctUntilChanged()
27 | }
28 | return null
29 | }
30 |
31 | /**
32 | * Get list of all coin transactions
33 | */
34 | fun loadTransactions(): Flow>? {
35 |
36 | coinBitDatabase?.let {
37 | return it.coinTransactionDao().getAllCoinTransaction().distinctUntilChanged()
38 | }
39 | return null
40 | }
41 |
42 | /**
43 | * Get the price of a coin from the API
44 | * want data from. [fromCurrencySymbol] specifies what currencies data you want for example bitcoin.
45 | * [toCurrencySymbol] is which currency you want data in for like USD
46 | */
47 | suspend fun getCoinPriceFull(fromCurrencySymbol: String, toCurrencySymbol: String): ArrayList {
48 | return getCoinPricesFromJson(api.getPricesFull(fromCurrencySymbol, toCurrencySymbol))
49 | }
50 | }
51 |
--------------------------------------------------------------------------------
/app/src/main/java/com/binarybricks/coinbit/features/launch/LaunchContract.kt:
--------------------------------------------------------------------------------
1 | import com.binarybricks.coinbit.features.BaseView
2 |
3 | /**
4 | Created by Pranay Airan 2/3/18.
5 | */
6 |
7 | interface LaunchContract {
8 |
9 | interface View : BaseView {
10 | fun onCoinsLoaded()
11 | fun onAllSupportedCoinsLoaded()
12 | }
13 |
14 | interface Presenter {
15 | fun loadAllCoins()
16 | fun getAllSupportedCoins(defaultCurrency: String)
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/app/src/main/java/com/binarybricks/coinbit/features/launch/LaunchPresenter.kt:
--------------------------------------------------------------------------------
1 | package com.binarybricks.coinbit.features.launch
2 |
3 | import LaunchContract
4 | import com.binarybricks.coinbit.data.database.entities.WatchedCoin
5 | import com.binarybricks.coinbit.features.BasePresenter
6 | import com.binarybricks.coinbit.features.CryptoCompareRepository
7 | import com.binarybricks.coinbit.features.getTop5CoinsToWatch
8 | import com.binarybricks.coinbit.network.models.CCCoin
9 | import com.binarybricks.coinbit.network.models.CoinInfo
10 | import com.binarybricks.coinbit.network.models.getCoinFromCCCoin
11 | import com.binarybricks.coinbit.utils.defaultExchange
12 | import kotlinx.coroutines.launch
13 | import timber.log.Timber
14 |
15 | /**
16 | Created by Pranay Airan
17 | */
18 |
19 | class LaunchPresenter(
20 | private val coinRepo: CryptoCompareRepository
21 | ) : BasePresenter(), LaunchContract.Presenter {
22 |
23 | private var coinList: ArrayList? = null
24 | private var coinInfoMap: Map? = null
25 |
26 | override fun loadAllCoins() {
27 | launch {
28 | try {
29 | val allCoinsFromAPI = coinRepo.getAllCoinsFromAPI(coinList, coinInfoMap)
30 | coinList = allCoinsFromAPI.first
31 | coinInfoMap = allCoinsFromAPI.second
32 | currentView?.onCoinsLoaded()
33 | } catch (ex: Exception) {
34 | Timber.e(ex.localizedMessage)
35 | }
36 | }
37 |
38 | loadExchangeFromAPI()
39 | }
40 |
41 | private fun loadExchangeFromAPI() {
42 | launch {
43 | try {
44 | coinRepo.insertExchangeIntoList(coinRepo.getExchangeInfo())
45 | } catch (ex: Exception) {
46 | Timber.e(ex.localizedMessage)
47 | }
48 | }
49 | }
50 |
51 | override fun getAllSupportedCoins(defaultCurrency: String) {
52 | launch {
53 | try {
54 | val allCoinsFromAPI = coinRepo.getAllCoinsFromAPI(coinList, coinInfoMap)
55 | val coinList: MutableList = mutableListOf()
56 | val ccCoinList = allCoinsFromAPI.first
57 |
58 | ccCoinList.forEach { ccCoin ->
59 | val coinInfo = allCoinsFromAPI.second[ccCoin.symbol.toLowerCase()]
60 | coinList.add(getCoinFromCCCoin(ccCoin, defaultExchange, defaultCurrency, coinInfo))
61 | }
62 |
63 | coinRepo.insertCoinsInWatchList(coinList)
64 |
65 | val top5CoinsToWatch = getTop5CoinsToWatch()
66 | top5CoinsToWatch.forEach { coinId ->
67 | coinRepo.updateCoinWatchedStatus(true, coinId)
68 | }
69 |
70 | Timber.d("Loaded all the coins and inserted in DB")
71 | currentView?.onAllSupportedCoinsLoaded()
72 | } catch (ex: Exception) {
73 | Timber.e(ex.localizedMessage)
74 | }
75 | }
76 | }
77 | }
78 |
--------------------------------------------------------------------------------
/app/src/main/java/com/binarybricks/coinbit/features/settings/SettingsContract.kt:
--------------------------------------------------------------------------------
1 | import com.binarybricks.coinbit.features.BaseView
2 |
3 | /**
4 | Created by Pranay Airan
5 | */
6 |
7 | interface SettingsContract {
8 |
9 | interface View : BaseView {
10 | fun onCoinListRefreshed()
11 | fun onExchangeListRefreshed()
12 | }
13 |
14 | interface Presenter {
15 | fun refreshCoinList(defaultCurrency: String)
16 | fun refreshExchangeList()
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/app/src/main/java/com/binarybricks/coinbit/features/settings/SettingsPresenter.kt:
--------------------------------------------------------------------------------
1 | package com.binarybricks.coinbit.features.settings
2 |
3 | import SettingsContract
4 | import com.binarybricks.coinbit.data.database.entities.WatchedCoin
5 | import com.binarybricks.coinbit.features.BasePresenter
6 | import com.binarybricks.coinbit.features.CryptoCompareRepository
7 | import com.binarybricks.coinbit.network.models.getCoinFromCCCoin
8 | import com.binarybricks.coinbit.utils.defaultExchange
9 | import kotlinx.coroutines.launch
10 | import timber.log.Timber
11 |
12 | /**
13 | Created by Pranay Airan
14 | */
15 |
16 | class SettingsPresenter(
17 | private val coinRepo: CryptoCompareRepository
18 | ) : BasePresenter(), SettingsContract.Presenter {
19 |
20 | override fun refreshCoinList(defaultCurrency: String) {
21 | launch {
22 | try {
23 | val allCoinsFromAPI = coinRepo.getAllCoinsFromAPI()
24 | val coinList: MutableList = mutableListOf()
25 | val ccCoinList = allCoinsFromAPI.first
26 | ccCoinList.forEach { ccCoin ->
27 | val coinInfo = allCoinsFromAPI.second[ccCoin.symbol.toLowerCase()]
28 | coinList.add(getCoinFromCCCoin(ccCoin, defaultExchange, defaultCurrency, coinInfo))
29 | }
30 | Timber.d("Inserted all coins in db with size ${coinList.size}")
31 | currentView?.onCoinListRefreshed()
32 | } catch (ex: Exception) {
33 | Timber.e(ex.localizedMessage)
34 | currentView?.onNetworkError(ex.localizedMessage ?: "")
35 | }
36 | }
37 | }
38 |
39 | override fun refreshExchangeList() {
40 | launch {
41 | try {
42 | coinRepo.insertExchangeIntoList(coinRepo.getExchangeInfo())
43 | Timber.d("all exchanges loaded and inserted into db")
44 | currentView?.onExchangeListRefreshed()
45 | } catch (ex: Exception) {
46 | Timber.e(ex.localizedMessage)
47 | currentView?.onNetworkError(ex.localizedMessage ?: "")
48 | }
49 | }
50 | }
51 | }
52 |
--------------------------------------------------------------------------------
/app/src/main/java/com/binarybricks/coinbit/features/transaction/CoinTransactionContract.kt:
--------------------------------------------------------------------------------
1 | import com.binarybricks.coinbit.data.database.entities.CoinTransaction
2 | import com.binarybricks.coinbit.features.BaseView
3 | import com.binarybricks.coinbit.network.models.ExchangePair
4 | import java.math.BigDecimal
5 | import java.util.HashMap
6 |
7 | /**
8 | Created by Pranay Airan 2/3/18.
9 | */
10 |
11 | interface CoinTransactionContract {
12 |
13 | interface View : BaseView {
14 | fun onAllSupportedExchangesLoaded(exchangeCoinMap: HashMap>)
15 | fun onCoinPriceLoaded(prices: MutableMap)
16 | fun onTransactionAdded()
17 | }
18 |
19 | interface Presenter {
20 | fun getAllSupportedExchanges()
21 | fun getPriceForPair(fromCoin: String, toCoin: String, exchange: String, timeStamp: String)
22 | fun addTransaction(transaction: CoinTransaction)
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/app/src/main/java/com/binarybricks/coinbit/features/transaction/CoinTransactionPresenter.kt:
--------------------------------------------------------------------------------
1 | package com.binarybricks.coinbit.features.transaction
2 |
3 | import CoinTransactionContract
4 | import com.binarybricks.coinbit.data.database.entities.CoinTransaction
5 | import com.binarybricks.coinbit.features.BasePresenter
6 | import com.binarybricks.coinbit.features.CryptoCompareRepository
7 | import kotlinx.coroutines.launch
8 | import timber.log.Timber
9 |
10 | /**
11 | Created by Pranay Airan
12 | */
13 |
14 | class CoinTransactionPresenter(
15 | private val coinRepo: CryptoCompareRepository
16 | ) : BasePresenter(), CoinTransactionContract.Presenter {
17 |
18 | override fun getAllSupportedExchanges() {
19 | launch {
20 | try {
21 | currentView?.onAllSupportedExchangesLoaded(coinRepo.getAllSupportedExchanges())
22 | Timber.d("All Exchange Loaded")
23 | } catch (ex: Exception) {
24 | Timber.e(ex.localizedMessage)
25 | }
26 | }
27 | }
28 |
29 | // to coins is , separated multiple coin list.
30 | override fun getPriceForPair(fromCoin: String, toCoin: String, exchange: String, timeStamp: String) {
31 | if (exchange.isNotEmpty()) {
32 | launch {
33 | try {
34 | currentView?.onCoinPriceLoaded(coinRepo.getCoinPriceForTimeStamp(fromCoin, toCoin, exchange, timeStamp))
35 | } catch (ex: Exception) {
36 | Timber.e(ex.localizedMessage)
37 | }
38 | }
39 | }
40 | }
41 |
42 | override fun addTransaction(transaction: CoinTransaction) {
43 | launch {
44 | try {
45 | coinRepo.insertTransaction(transaction)
46 | Timber.d("Coin Transaction Added")
47 | currentView?.onTransactionAdded()
48 | } catch (ex: Exception) {
49 | Timber.e(ex.localizedMessage)
50 | }
51 | }
52 | }
53 | }
54 |
--------------------------------------------------------------------------------
/app/src/main/java/com/binarybricks/coinbit/network/NetworkConstants.kt:
--------------------------------------------------------------------------------
1 | @file:JvmName("ConstantsKt")
2 |
3 | package com.binarybricks.coinbit.network
4 |
5 | const val BASE_CRYPTOCOMPARE_URL = "https://min-api.cryptocompare.com/data/"
6 | const val BASE_CRYPTOCOMPARE_IMAGE_URL = "https://www.cryptocompare.com"
7 |
8 | const val RAW = "RAW"
9 |
10 | const val USD = "USD"
11 | const val BTC = "BTC"
12 | const val ETH = "ETH"
13 | const val SNT = "SNT"
14 |
15 | const val TO = "to"
16 | const val DATA = "Data"
17 | const val TICKERS = "tickers"
18 |
19 | const val HISTO_MINUTE = "histominute"
20 | const val HISTO_HOUR = "histohour"
21 | const val HISTO_DAY = "histoday"
22 |
23 | const val HOUR = "12 hour"
24 | const val HOURS24 = "1 day"
25 | const val WEEK = "1 week"
26 | const val MONTH = "1 month"
27 | const val MONTH3 = "3 month"
28 | const val YEAR = "1 year"
29 | const val ALL = "All"
30 |
31 | const val APP_NAME = "CoinBit"
32 | const val API_KEY = "authorization: Apikey 525fa1933d3246c3bbd6a8ec96207baf3104c80d12b95a4b4cf9196ece4d3728"
33 |
--------------------------------------------------------------------------------
/app/src/main/java/com/binarybricks/coinbit/network/api/CryptoAPI.kt:
--------------------------------------------------------------------------------
1 | package com.binarybricks.coinbit.network.api
2 |
3 | import com.binarybricks.coinbit.BuildConfig
4 | import com.binarybricks.coinbit.network.BASE_CRYPTOCOMPARE_URL
5 | import com.facebook.stetho.okhttp3.StethoInterceptor
6 | import okhttp3.OkHttpClient
7 | import okhttp3.logging.HttpLoggingInterceptor
8 | import retrofit2.Retrofit
9 | import retrofit2.converter.gson.GsonConverterFactory
10 |
11 | /**
12 | Created by Pranay Airan
13 | api provider for crypto compare and others.
14 | */
15 |
16 | val cryptoCompareRetrofit: Retrofit by lazy {
17 | Retrofit.Builder()
18 | .baseUrl(BASE_CRYPTOCOMPARE_URL)
19 | .client(okHttpClient)
20 | .addConverterFactory(GsonConverterFactory.create())
21 | .build()
22 | }
23 |
24 | val okHttpClient: OkHttpClient by lazy {
25 | val builder = OkHttpClient.Builder()
26 | if (BuildConfig.DEBUG) {
27 | val loggingInterceptor = HttpLoggingInterceptor()
28 | loggingInterceptor.level = HttpLoggingInterceptor.Level.BODY
29 | builder.addInterceptor(loggingInterceptor)
30 | builder.addInterceptor(StethoInterceptor())
31 | }
32 |
33 | builder.build()
34 | }
35 |
36 | val api: API by lazy {
37 | cryptoCompareRetrofit.create(API::class.java)
38 | }
39 |
--------------------------------------------------------------------------------
/app/src/main/java/com/binarybricks/coinbit/network/models/CCCoin.kt:
--------------------------------------------------------------------------------
1 | package com.binarybricks.coinbit.network.models
2 |
3 | import com.binarybricks.coinbit.data.database.entities.Coin
4 | import com.binarybricks.coinbit.data.database.entities.WatchedCoin
5 | import com.google.gson.annotations.SerializedName
6 |
7 | /**
8 | Created by Pranay Airan 1/15/18.
9 | *
10 | * Network object representing Coin from crypto compare
11 | */
12 | data class CCCoin(
13 |
14 | @field:SerializedName("Id") val id: String = "",
15 |
16 | @field:SerializedName("Url") val url: String = "",
17 |
18 | @field:SerializedName("ImageUrl") val imageUrl: String = "",
19 |
20 | @field:SerializedName("Name") val name: String = "",
21 |
22 | @field:SerializedName("Symbol") val symbol: String = "",
23 |
24 | @field:SerializedName("CoinName") val coinName: String = "",
25 |
26 | @field:SerializedName("FullName") val fullName: String = "",
27 |
28 | @field:SerializedName("Algorithm") val algorithm: String = "",
29 |
30 | @field:SerializedName("ProofType") val proofType: String = "",
31 |
32 | @field:SerializedName("FullyPremined") val fullyPremined: String = "",
33 |
34 | @field:SerializedName("TotalCoinSupply") val totalCoinSupply: String = "",
35 |
36 | @field:SerializedName("PreMinedValue") val preMinedValue: String = "",
37 |
38 | @field:SerializedName("TotalCoinsFreeFloat") val totalCoinsFreeFloat: String = "",
39 |
40 | @field:SerializedName("SortOrder") val sortOrder: String = "",
41 |
42 | @field:SerializedName("Sponsored") val sponsored: Boolean = false,
43 |
44 | @field:SerializedName("IsTrading") val isTrading: Boolean = false
45 | )
46 |
47 | data class CoinInfoWithCurrency(val currencyName: String, val info: CoinInfo)
48 | data class CoinInfo(val desc: String, val web: String?, val twt: String?, val reddit: String?, val forum: String?, val github: String?)
49 |
50 | fun getCoinFromCCCoin(ccCoin: CCCoin, defaultExchange: String, defaultCurrency: String, coinInfo: CoinInfo?): WatchedCoin {
51 |
52 | val coin = Coin(
53 | ccCoin.id, ccCoin.url, ccCoin.imageUrl, ccCoin.name, ccCoin.symbol, ccCoin.coinName,
54 | ccCoin.fullName, ccCoin.algorithm, ccCoin.proofType, ccCoin.fullyPremined,
55 | ccCoin.totalCoinSupply, ccCoin.preMinedValue, ccCoin.totalCoinsFreeFloat, ccCoin.sortOrder.toInt(),
56 | ccCoin.sponsored, ccCoin.isTrading, coinInfo?.desc, coinInfo?.twt, coinInfo?.web, coinInfo?.reddit, coinInfo?.forum, coinInfo?.github
57 | )
58 |
59 | return WatchedCoin(coin, defaultExchange, defaultCurrency)
60 | }
61 |
--------------------------------------------------------------------------------
/app/src/main/java/com/binarybricks/coinbit/network/models/CoinPair.kt:
--------------------------------------------------------------------------------
1 | package com.binarybricks.coinbit.network.models
2 |
3 | import android.os.Parcelable
4 | import com.google.gson.annotations.SerializedName
5 | import kotlinx.android.parcel.Parcelize
6 |
7 | /**
8 | Created by Pranay Airan 1/15/18.
9 | */
10 | @Parcelize
11 | data class CoinPair(
12 |
13 | @field:SerializedName("SYMBOL")
14 | val symbol: String? = null,
15 |
16 | @field:SerializedName("SUPPLY")
17 | val supply: String? = null,
18 |
19 | @field:SerializedName("FULLNAME")
20 | val fullName: String? = null,
21 |
22 | @field:SerializedName("NAME")
23 | val name: String? = null,
24 |
25 | @field:SerializedName("VOLUME24HOURTO")
26 | val volume24Hours: String? = null
27 |
28 | ) : Parcelable
29 |
--------------------------------------------------------------------------------
/app/src/main/java/com/binarybricks/coinbit/network/models/CoinPrice.kt:
--------------------------------------------------------------------------------
1 | package com.binarybricks.coinbit.network.models
2 |
3 | import android.os.Parcelable
4 | import com.google.gson.annotations.SerializedName
5 | import kotlinx.android.parcel.Parcelize
6 |
7 | /**
8 | Created by Pranay Airan 1/15/18.
9 | */
10 | @Parcelize
11 | data class CoinPrice(
12 |
13 | @field:SerializedName("LASTTRADEID")
14 | val lastTradedID: String? = null,
15 |
16 | @field:SerializedName("OPEN24HOUR")
17 | val open24Hour: String? = null,
18 |
19 | @field:SerializedName("LOW24HOUR")
20 | val low24Hour: String? = null,
21 |
22 | @field:SerializedName("HIGHDAY")
23 | val highDay: String? = null,
24 |
25 | @field:SerializedName("TOTALVOLUME24H")
26 | val totalVolume24Hour: String? = null,
27 |
28 | @field:SerializedName("TOTALVOLUME24HTO")
29 | val totalVolume24HoursTo: String? = null,
30 |
31 | @field:SerializedName("TOSYMBOL")
32 | val toSymbol: String? = null,
33 |
34 | @field:SerializedName("FROMSYMBOL")
35 | val fromSymbol: String? = null,
36 |
37 | @field:SerializedName("LASTVOLUME")
38 | val lastVolume: String? = null,
39 |
40 | @field:SerializedName("LASTMARKET")
41 | val lastMarket: String? = null,
42 |
43 | @field:SerializedName("MKTCAP")
44 | var marketCap: String? = null,
45 |
46 | @field:SerializedName("LASTUPDATE")
47 | val lastUpdateTime: Int? = null,
48 |
49 | @field:SerializedName("CHANGEDAY")
50 | val changeDay: String? = null,
51 |
52 | @field:SerializedName("FLAGS")
53 | val flags: String? = null,
54 |
55 | @field:SerializedName("SUPPLY")
56 | val supply: Int? = null,
57 |
58 | @field:SerializedName("TYPE")
59 | val type: String? = null,
60 |
61 | @field:SerializedName("VOLUMEDAY")
62 | val volumneDay: String? = null,
63 |
64 | @field:SerializedName("VOLUME24HOUR")
65 | val volume24Hour: String? = null,
66 |
67 | @field:SerializedName("MARKET")
68 | val market: String? = null,
69 |
70 | @field:SerializedName("PRICE")
71 | val price: String? = null,
72 |
73 | @field:SerializedName("CHANGEPCTDAY")
74 | val changePercentageDay: String? = null,
75 |
76 | @field:SerializedName("LASTVOLUMETO")
77 | val lastVolumeTo: String? = null,
78 |
79 | @field:SerializedName("CHANGEPCT24HOUR")
80 | val changePercentage24Hour: String? = null,
81 |
82 | @field:SerializedName("OPENDAY")
83 | val openDay: String? = null,
84 |
85 | @field:SerializedName("VOLUMEDAYTO")
86 | val volumeDayTo: String? = null,
87 |
88 | @field:SerializedName("CHANGE24HOUR")
89 | val change24Hours: String? = null,
90 |
91 | @field:SerializedName("HIGH24HOUR")
92 | val high24Hours: String? = null,
93 |
94 | @field:SerializedName("VOLUME24HOURTO")
95 | val volume24HoursTo: String? = null,
96 |
97 | @field:SerializedName("LOWDAY")
98 | val lowDay: String? = null
99 | ) : Parcelable
100 |
--------------------------------------------------------------------------------
/app/src/main/java/com/binarybricks/coinbit/network/models/CryptoCompareHistoricalResponse.kt:
--------------------------------------------------------------------------------
1 | package com.binarybricks.coinbit.network.models
2 |
3 | import com.google.gson.annotations.SerializedName
4 |
5 | /**
6 | Created by Pranay Airan 1/10/18.
7 | */
8 |
9 | data class CryptoCompareHistoricalResponse(
10 | @SerializedName("FirstValueInArray") val firstValueInArray: String,
11 | @SerializedName("Data") val data: List,
12 | @SerializedName("TimeFrom") val timeFrom: String,
13 | @SerializedName("Type") val type: String,
14 | @SerializedName("Response") val response: String,
15 | @SerializedName("ConversionType") val conversionType: ConversionType,
16 | @SerializedName("TimeTo") val timeTo: String,
17 | @SerializedName("Aggregated") val aggregated: String
18 | ) {
19 |
20 | data class ConversionType(val conversionSymbol: String, val type: String)
21 |
22 | data class Data(
23 | val open: String,
24 | val time: String,
25 | val volumeto: String,
26 | val volumefrom: String,
27 | val high: String,
28 | val low: String,
29 | val close: String
30 | )
31 | }
32 |
--------------------------------------------------------------------------------
/app/src/main/java/com/binarybricks/coinbit/network/models/CryptoCompareNews.kt:
--------------------------------------------------------------------------------
1 | package com.binarybricks.coinbit.network.models
2 |
3 | import android.os.Parcelable
4 | import com.google.gson.annotations.SerializedName
5 | import kotlinx.android.parcel.Parcelize
6 |
7 | /**
8 | Created by Pranay Airan 12/15/18.
9 | */
10 | @Parcelize
11 | data class CryptoCompareNews(
12 |
13 | @field:SerializedName("id")
14 | val id: String? = null,
15 |
16 | @field:SerializedName("guid")
17 | val guid: String? = null,
18 |
19 | @field:SerializedName("published_on")
20 | val published_on: String? = null,
21 |
22 | @field:SerializedName("imageurl")
23 | val imageurl: String? = null,
24 |
25 | @field:SerializedName("title")
26 | val title: String? = null,
27 |
28 | @field:SerializedName("url")
29 | val url: String? = null,
30 |
31 | @field:SerializedName("source")
32 | val source: String? = null,
33 |
34 | @field:SerializedName("body")
35 | val body: String? = null,
36 |
37 | @field:SerializedName("tags")
38 | val tags: String? = null,
39 |
40 | @field:SerializedName("categories")
41 | val categories: String? = null
42 | ) : Parcelable
43 |
--------------------------------------------------------------------------------
/app/src/main/java/com/binarybricks/coinbit/network/models/CryptoPanicNews.kt:
--------------------------------------------------------------------------------
1 | package com.binarybricks.coinbit.network.models
2 |
3 | import android.os.Parcelable
4 | import kotlinx.android.parcel.Parcelize
5 |
6 | /**
7 | * Created by Pragya Agrawal
8 | *
9 | * Data class representing news object
10 | */
11 |
12 | @Parcelize
13 | data class CryptoPanicNews(
14 | val count: String = "",
15 | val results: List?
16 | ) : Parcelable
17 |
18 | @Parcelize
19 | data class Results(
20 | val id: String = "",
21 |
22 | val title: String = "",
23 |
24 | val source: Source = Source(),
25 |
26 | val domain: String = "",
27 |
28 | val created_at: String = "",
29 |
30 | val slug: String = "",
31 |
32 | val url: String = "",
33 |
34 | val published_at: String = ""
35 | ) : Parcelable
36 |
37 | @Parcelize
38 | data class Source(val title: String = "", val domain: String = "") : Parcelable
39 |
--------------------------------------------------------------------------------
/app/src/main/java/com/binarybricks/coinbit/network/models/CryptoTicker.kt:
--------------------------------------------------------------------------------
1 | package com.binarybricks.coinbit.network.models
2 |
3 | import android.os.Parcelable
4 | import kotlinx.android.parcel.Parcelize
5 |
6 | /**
7 | * Created by Pranay Airan
8 | *
9 | * Data class representing price ticker object
10 | */
11 |
12 | @Parcelize
13 | data class CryptoTicker(
14 | val base: String = "",
15 |
16 | val target: String = "",
17 |
18 | val marketName: String = "",
19 |
20 | val marketIdentifier: String = "",
21 |
22 | val last: String = "",
23 |
24 | val volume: String = "",
25 |
26 | val convertedVolumeUSD: String = "",
27 |
28 | val convertedVolumeBTC: String = "",
29 |
30 | val timestamp: String = "",
31 |
32 | val imageUrl: String = "",
33 |
34 | val exchangeUrl: String = ""
35 |
36 | ) : Parcelable
37 |
--------------------------------------------------------------------------------
/app/src/main/java/com/binarybricks/coinbit/network/models/ExchangePair.kt:
--------------------------------------------------------------------------------
1 | package com.binarybricks.coinbit.network.models
2 |
3 | import android.os.Parcelable
4 | import kotlinx.android.parcel.Parcelize
5 |
6 | /**
7 | * Created by Pranay Airan
8 | */
9 |
10 | @Parcelize
11 | data class ExchangePair(val exchangeName: String, val pairList: MutableList) : Parcelable
12 |
--------------------------------------------------------------------------------
/app/src/main/java/com/binarybricks/coinbit/utils/Constants.kt:
--------------------------------------------------------------------------------
1 | package com.binarybricks.coinbit.utils
2 |
3 | /**
4 | Created by Pranay Airan 1/14/18.
5 | */
6 |
7 | const val chartAnimationDuration: Long = 250
8 |
9 | const val defaultExchange: String = "CCCAGG"
10 | const val defaultCurrency: String = "USD"
11 |
12 | const val TRANSACTION_TYPE_BUY: Int = 1
13 | const val TRANSACTION_TYPE_SELL: Int = 2
14 |
--------------------------------------------------------------------------------
/app/src/main/java/com/binarybricks/coinbit/utils/Extensions.kt:
--------------------------------------------------------------------------------
1 | package com.binarybricks.coinbit.utils
2 |
3 | import android.widget.RadioButton
4 | import android.widget.RadioGroup
5 | import androidx.annotation.ColorInt
6 |
7 | /**
8 | Created by Pranay Airan 1/14/18.
9 | */
10 |
11 | fun RadioGroup.changeChildrenColor(@ColorInt color: Int) {
12 | val childCount = this.childCount
13 | var i = 0
14 | while (i < childCount) {
15 | val radioButton = this.getChildAt(i) as RadioButton
16 | if (!radioButton.isChecked) {
17 | radioButton.setTextColor(color)
18 | }
19 | i++
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/app/src/main/java/com/binarybricks/coinbit/utils/resourcemanager/AndroidResourceManager.kt:
--------------------------------------------------------------------------------
1 | package com.binarybricks.coinbit.utils.resourcemanager
2 |
3 | import android.graphics.drawable.Drawable
4 | import androidx.annotation.DrawableRes
5 | import androidx.annotation.PluralsRes
6 | import androidx.annotation.StringRes
7 |
8 | /**
9 | * Created by Pranay Airan 1/16/18.
10 | *
11 | * This is used to inject the resources for testing
12 | * https://medium.com/@daptronic/android-mvp-the-curious-case-of-resources-ddca39c1fccd
13 | */
14 | interface AndroidResourceManager {
15 |
16 | fun getString(@StringRes resId: Int): String
17 |
18 | fun getString(@StringRes resId: Int, vararg formatArgs: Any): String
19 |
20 | fun getQuantityString(@PluralsRes resId: Int, quantity: Int): String
21 |
22 | fun getQuantityString(@PluralsRes resId: Int, quantity: Int, vararg formatArgs: Any): String
23 |
24 | fun getColor(resId: Int): Int
25 |
26 | fun getDrawable(@DrawableRes resId: Int): Drawable?
27 | }
28 |
--------------------------------------------------------------------------------
/app/src/main/java/com/binarybricks/coinbit/utils/resourcemanager/AndroidResourceManagerImpl.kt:
--------------------------------------------------------------------------------
1 | package com.binarybricks.coinbit.utils.resourcemanager
2 |
3 | import android.content.Context
4 | import android.graphics.drawable.Drawable
5 | import androidx.annotation.PluralsRes
6 | import androidx.annotation.StringRes
7 | import androidx.core.content.ContextCompat
8 |
9 | /**
10 | * Created by Pranay Airan 1/16/18.
11 | */
12 | class AndroidResourceManagerImpl(context: Context) : AndroidResourceManager {
13 |
14 | private val appContext: Context = context.applicationContext
15 |
16 | override fun getString(@StringRes resId: Int): String {
17 | return appContext.getString(resId)
18 | }
19 |
20 | override fun getString(@StringRes resId: Int, vararg formatArgs: Any): String {
21 | return appContext.getString(resId, *formatArgs)
22 | }
23 |
24 | override fun getQuantityString(@PluralsRes resId: Int, quantity: Int): String {
25 | return appContext.resources.getQuantityString(resId, quantity)
26 | }
27 |
28 | override fun getQuantityString(@PluralsRes resId: Int, quantity: Int, vararg formatArgs: Any): String {
29 | return appContext.resources.getQuantityString(resId, quantity, *formatArgs)
30 | }
31 |
32 | override fun getColor(resId: Int): Int {
33 | return ContextCompat.getColor(appContext, resId)
34 | }
35 |
36 | override fun getDrawable(resId: Int): Drawable? {
37 | return ContextCompat.getDrawable(appContext, resId)
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/app/src/main/java/com/binarybricks/coinbit/utils/ui/IntroPageTransformer.kt:
--------------------------------------------------------------------------------
1 | package com.binarybricks.coinbit.utils.ui
2 |
3 | import android.view.View
4 | import android.widget.Button
5 | import android.widget.TextView
6 | import androidx.viewpager.widget.ViewPager
7 | import com.airbnb.lottie.LottieAnimationView
8 | import com.binarybricks.coinbit.R
9 |
10 | class IntroPageTransformer : ViewPager.PageTransformer {
11 |
12 | override fun transformPage(page: View, position: Float) {
13 |
14 | // Get the page index from the tag. This makes
15 | // it possible to know which page index you're
16 | // currently transforming - and that can be used
17 | // to make some important performance improvements.
18 | val pagePosition = page.tag as Int
19 |
20 | // Here you can do all kinds of stuff, like get the
21 | // width of the page and perform calculations based
22 | // on how far the user has swiped the page.
23 | val pageWidth = page.width
24 | val pageWidthTimesPosition = pageWidth * position
25 | val absPosition = Math.abs(position)
26 |
27 | // Now it's time for the effects
28 | if (position <= -1.0f || position >= 1.0f) {
29 |
30 | // The page is not visible. This is a good place to stop
31 | // any potential work / animations you may have running.
32 | } else if (position == 0.0f) {
33 |
34 | // The page is selected. This is a good time to reset Views
35 | // after animations as you can't always count on the PageTransformer
36 | // callbacks to match up perfectly.
37 | page.findViewById(R.id.animationView).playAnimation()
38 | } else {
39 |
40 | // The page is currently being scrolled / swiped. This is
41 | // a good place to show animations that react to the user's
42 | // swiping as it provides a good user experience.
43 |
44 | // Let's start by animating the title.
45 | // We want it to fade as it scrolls out
46 | val title = page.findViewById(R.id.tvTitle)
47 | title.translationX = ((-(1 - position) * 0.1 * pageWidthTimesPosition).toFloat())
48 |
49 | // Now the description. We also want this one to
50 | // fade, but the animation should also slowly move
51 | // down and out of the screen
52 | val description = page.findViewById(R.id.tvSubTitle)
53 | description.translationX = (-(1 - position) * 0.2 * pageWidthTimesPosition).toFloat()
54 | description.alpha = 1.0f - absPosition
55 |
56 | // Now, we want the image to move to the right,
57 | // i.e. in the opposite direction of the rest of the
58 | // content while fading out
59 | val animation = page.findViewById(R.id.animationView)
60 | animation.translationX = ((-(1 - position) * .7 * pageWidthTimesPosition)).toFloat()
61 |
62 | val button = page.findViewById