mirror of
https://github.com/hmalik144/EasyCC_Master.git
synced 2025-12-10 03:05:29 +00:00
Update gradle dependencies (#7)
- Synthetic views replaced with latest view binding - Hilt DI replaces kodein - Viewmodel factories removed - Updated to latest android version - Migrated to Hilt instrumentation testing
This commit is contained in:
@@ -1,36 +1,53 @@
|
|||||||
version: 2
|
# Use the latest 2.1 version of CircleCI pipeline process engine.
|
||||||
|
# See: https://circleci.com/docs/2.0/configuration-reference
|
||||||
|
# For a detailed guide to building and testing on Android, read the docs:
|
||||||
|
# https://circleci.com/docs/2.0/language-android/ for more details.
|
||||||
|
version: 2.1
|
||||||
|
|
||||||
|
# Orbs are reusable packages of CircleCI configuration that you may share across projects, enabling you to create encapsulated, parameterized commands, jobs, and executors that can be used across multiple projects.
|
||||||
|
# See: https://circleci.com/docs/2.0/orb-intro/
|
||||||
|
orbs:
|
||||||
|
android: circleci/android@1.0.3
|
||||||
|
|
||||||
|
# Define a job to be invoked later in a workflow.
|
||||||
|
# See: https://circleci.com/docs/2.0/configuration-reference/#jobs
|
||||||
jobs:
|
jobs:
|
||||||
build:
|
# Below is the definition of your job to build and test your app, you can rename and customize it as you want.
|
||||||
working_directory: ~/code
|
build-and-test:
|
||||||
docker:
|
# These next lines define the Android machine image executor.
|
||||||
- image: circleci/android:api-29
|
# See: https://circleci.com/docs/2.0/executor-types/
|
||||||
environment:
|
executor:
|
||||||
- CIRCLE_COMPARE_URL: https://github.com/hmalik144/EasyCC_Master/compare/7e995468c9fdc5528a6d1a5489ba301bb9f14c00...14293254ec6d68b93bba50eea79df8808aec9de9
|
name: android/android-machine
|
||||||
- JVM_OPTS: -Xmx3200m
|
|
||||||
|
# Add steps to the job
|
||||||
|
# See: https://circleci.com/docs/2.0/configuration-reference/#steps
|
||||||
steps:
|
steps:
|
||||||
- checkout
|
- checkout
|
||||||
- restore_cache:
|
- restore_cache:
|
||||||
key: jars-{{ checksum "build.gradle" }}-{{ checksum "app/build.gradle" }}
|
key: jars-{{ checksum "build.gradle" }}-{{ checksum "app/build.gradle" }}
|
||||||
- run:
|
- run:
|
||||||
name: Chmod permissions
|
name: Chmod permissions
|
||||||
command: sudo chmod +x ./gradlew
|
command: sudo chmod +x ./gradlew
|
||||||
- run:
|
- run:
|
||||||
name: Download Dependencies
|
name: Download Dependencies
|
||||||
command: ./gradlew androidDependencies
|
command: ./gradlew androidDependencies
|
||||||
- save_cache:
|
- save_cache:
|
||||||
paths:
|
paths:
|
||||||
- ~/.gradle
|
- ~/.gradle
|
||||||
key: jars-{{ checksum "build.gradle" }}-{{ checksum "app/build.gradle" }}
|
key: jars-{{ checksum "build.gradle" }}-{{ checksum "app/build.gradle" }}
|
||||||
- run:
|
- run:
|
||||||
name: Run Tests
|
name: Run Tests
|
||||||
command: ./gradlew lint test
|
command: ./gradlew lint test
|
||||||
- store_artifacts:
|
- store_artifacts:
|
||||||
path: app/build/reports
|
path: app/build/reports
|
||||||
destination: reports
|
destination: reports
|
||||||
- store_test_results:
|
- store_test_results:
|
||||||
path: app/build/test-results
|
path: app/build/test-results
|
||||||
|
|
||||||
|
# Invoke jobs via workflows
|
||||||
|
# See: https://circleci.com/docs/2.0/configuration-reference/#workflows
|
||||||
workflows:
|
workflows:
|
||||||
version: 2
|
sample: # This is the name of the workflow, feel free to change it to better match your workflow.
|
||||||
workflow:
|
# Inside the workflow, you define the jobs you want to run.
|
||||||
jobs:
|
jobs:
|
||||||
- build
|
- build-and-test
|
||||||
|
|||||||
@@ -1,14 +1,16 @@
|
|||||||
apply plugin: 'com.android.application'
|
plugins {
|
||||||
apply plugin: 'kotlin-android'
|
id 'com.android.application'
|
||||||
apply plugin: 'kotlin-android-extensions'
|
id 'org.jetbrains.kotlin.android'
|
||||||
apply plugin: 'kotlin-kapt'
|
id 'com.google.dagger.hilt.android'
|
||||||
|
id 'kotlin-kapt'
|
||||||
|
}
|
||||||
|
|
||||||
android {
|
android {
|
||||||
compileSdkVersion 30
|
compileSdkVersion 32
|
||||||
defaultConfig {
|
defaultConfig {
|
||||||
applicationId "com.appttude.h_mal.easycc"
|
applicationId "com.appttude.h_mal.easycc"
|
||||||
minSdkVersion 21
|
minSdkVersion 21
|
||||||
targetSdkVersion 30
|
targetSdkVersion 32
|
||||||
versionCode 5
|
versionCode 5
|
||||||
versionName "4.1"
|
versionName "4.1"
|
||||||
|
|
||||||
@@ -20,35 +22,36 @@ android {
|
|||||||
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
|
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
dataBinding {
|
|
||||||
enabled = true
|
|
||||||
}
|
|
||||||
|
|
||||||
viewBinding.enabled = true
|
|
||||||
|
|
||||||
compileOptions {
|
compileOptions {
|
||||||
sourceCompatibility JavaVersion.VERSION_1_8
|
sourceCompatibility JavaVersion.VERSION_1_8
|
||||||
targetCompatibility JavaVersion.VERSION_1_8
|
targetCompatibility JavaVersion.VERSION_1_8
|
||||||
}
|
}
|
||||||
kotlinOptions {
|
kotlinOptions {
|
||||||
jvmTarget = "1.8"
|
jvmTarget = '1.8'
|
||||||
|
}
|
||||||
|
buildFeatures {
|
||||||
|
viewBinding true
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
implementation fileTree(include: ['*.jar'], dir: 'libs')
|
implementation 'androidx.core:core-ktx:1.8.0'
|
||||||
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
|
implementation 'androidx.appcompat:appcompat:1.4.2'
|
||||||
implementation 'androidx.appcompat:appcompat:1.1.0'
|
implementation 'com.google.android.material:material:1.6.1'
|
||||||
implementation 'androidx.core:core-ktx:1.1.0'
|
implementation 'androidx.annotation:annotation:1.4.0'
|
||||||
implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
|
implementation 'androidx.constraintlayout:constraintlayout:2.1.4'
|
||||||
implementation 'androidx.legacy:legacy-support-v4:1.0.0'
|
implementation 'androidx.lifecycle:lifecycle-livedata-ktx:2.5.1'
|
||||||
implementation 'androidx.lifecycle:lifecycle-viewmodel-ktx:2.1.0'
|
implementation 'androidx.lifecycle:lifecycle-viewmodel-ktx:2.5.1'
|
||||||
testImplementation 'junit:junit:4.12'
|
implementation 'androidx.activity:activity-ktx:1.5.1'
|
||||||
androidTestImplementation 'androidx.test:runner:1.2.0'
|
testImplementation 'junit:junit:4.13.2'
|
||||||
androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0'
|
androidTestImplementation 'androidx.test.ext:junit:1.1.3'
|
||||||
testImplementation "org.jetbrains.kotlin:kotlin-test-junit:$kotlin_version"
|
androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0'
|
||||||
androidTestImplementation 'androidx.test:rules:1.4.0-beta02'
|
androidTestImplementation 'androidx.test:rules:1.4.0'
|
||||||
|
implementation 'org.jetbrains.kotlin:kotlin-test:1.7.10'
|
||||||
|
|
||||||
|
// Coroutines
|
||||||
|
def coroutines_version = "1.6.2"
|
||||||
|
testImplementation "org.jetbrains.kotlinx:kotlinx-coroutines-test:$coroutines_version"
|
||||||
|
|
||||||
//Retrofit and GSON
|
//Retrofit and GSON
|
||||||
def retrofit_ver = "2.8.1"
|
def retrofit_ver = "2.8.1"
|
||||||
@@ -56,32 +59,27 @@ dependencies {
|
|||||||
implementation "com.squareup.retrofit2:converter-gson:$retrofit_ver"
|
implementation "com.squareup.retrofit2:converter-gson:$retrofit_ver"
|
||||||
implementation "com.squareup.okhttp3:logging-interceptor:4.9.0"
|
implementation "com.squareup.okhttp3:logging-interceptor:4.9.0"
|
||||||
|
|
||||||
//Kotlin Coroutines
|
|
||||||
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:1.3.0"
|
|
||||||
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:1.3.0"
|
|
||||||
|
|
||||||
// ViewModel and LiveData
|
// ViewModel and LiveData
|
||||||
implementation "androidx.lifecycle:lifecycle-extensions:2.1.0"
|
implementation "androidx.lifecycle:lifecycle-extensions:2.2.0"
|
||||||
|
|
||||||
//New Material Design
|
//New Material Design
|
||||||
implementation 'com.google.android.material:material:1.1.0-alpha10'
|
implementation 'com.google.android.material:material:1.6.1'
|
||||||
|
|
||||||
//Kodein Dependency Injection
|
// Hilt dependency injection
|
||||||
implementation "org.kodein.di:kodein-di-generic-jvm:6.2.1"
|
def hilt_ver = "2.43.2"
|
||||||
implementation "org.kodein.di:kodein-di-framework-android-x:6.2.1"
|
implementation "com.google.dagger:hilt-android:$hilt_ver"
|
||||||
|
kapt "com.google.dagger:hilt-compiler:$hilt_ver"
|
||||||
|
androidTestImplementation "com.google.dagger:hilt-android-testing:$hilt_ver"
|
||||||
|
kaptAndroidTest "com.google.dagger:hilt-android-compiler:$hilt_ver"
|
||||||
|
|
||||||
//mockito and livedata testing
|
//mockito and livedata testing
|
||||||
testImplementation 'org.mockito:mockito-inline:2.13.0'
|
testImplementation 'org.mockito:mockito-inline:2.13.0'
|
||||||
testImplementation 'androidx.arch.core:core-testing:2.1.0'
|
testImplementation 'androidx.arch.core:core-testing:2.1.0'
|
||||||
|
|
||||||
//Android Room
|
implementation "androidx.preference:preference-ktx:1.2.0"
|
||||||
implementation "androidx.room:room-runtime:2.2.0-rc01"
|
|
||||||
implementation "androidx.room:room-ktx:2.2.0-rc01"
|
|
||||||
kapt "androidx.room:room-compiler:2.2.0-rc01"
|
|
||||||
|
|
||||||
implementation "androidx.preference:preference-ktx:1.1.0"
|
|
||||||
|
|
||||||
//mock websever for testing retrofit responses
|
//mock websever for testing retrofit responses
|
||||||
testImplementation "com.squareup.okhttp3:mockwebserver:4.6.0"
|
testImplementation "com.squareup.okhttp3:mockwebserver:4.6.0"
|
||||||
testImplementation "com.nhaarman.mockitokotlin2:mockito-kotlin:2.2.0"
|
testImplementation "com.nhaarman.mockitokotlin2:mockito-kotlin:2.2.0"
|
||||||
|
implementation "org.jetbrains.kotlin:kotlin-test:1.7.10"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,31 +0,0 @@
|
|||||||
package com.appttude.h_mal.easycc.application
|
|
||||||
|
|
||||||
import android.app.Application
|
|
||||||
import androidx.test.espresso.idling.CountingIdlingResource
|
|
||||||
import com.appttude.h_mal.easycc.application.modules.MockRepository
|
|
||||||
import com.appttude.h_mal.easycc.helper.CurrencyDataHelper
|
|
||||||
import com.appttude.h_mal.easycc.ui.main.MainViewModelFactory
|
|
||||||
import org.kodein.di.Kodein
|
|
||||||
import org.kodein.di.KodeinAware
|
|
||||||
import org.kodein.di.android.x.androidXModule
|
|
||||||
import org.kodein.di.generic.bind
|
|
||||||
import org.kodein.di.generic.instance
|
|
||||||
import org.kodein.di.generic.provider
|
|
||||||
import org.kodein.di.generic.singleton
|
|
||||||
|
|
||||||
class TestApplication : Application(), KodeinAware {
|
|
||||||
|
|
||||||
companion object {
|
|
||||||
val idlingResources = CountingIdlingResource("Data_loader")
|
|
||||||
}
|
|
||||||
|
|
||||||
// KODEIN DI components declaration
|
|
||||||
override val kodein by Kodein.lazy {
|
|
||||||
import(androidXModule(this@TestApplication))
|
|
||||||
|
|
||||||
bind() from singleton { MockRepository() }
|
|
||||||
bind() from singleton { CurrencyDataHelper(instance()) }
|
|
||||||
bind() from provider { MainViewModelFactory(instance(), instance()) }
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -2,11 +2,18 @@ package com.appttude.h_mal.easycc.application
|
|||||||
|
|
||||||
import android.app.Application
|
import android.app.Application
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
|
import androidx.test.espresso.idling.CountingIdlingResource
|
||||||
|
|
||||||
import androidx.test.runner.AndroidJUnitRunner
|
import androidx.test.runner.AndroidJUnitRunner
|
||||||
|
import dagger.hilt.android.testing.HiltTestApplication
|
||||||
|
|
||||||
|
|
||||||
class TestRunner : AndroidJUnitRunner() {
|
class TestRunner : AndroidJUnitRunner() {
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
val idlingResources = CountingIdlingResource("Data_loader")
|
||||||
|
}
|
||||||
|
|
||||||
@Throws(
|
@Throws(
|
||||||
InstantiationException::class,
|
InstantiationException::class,
|
||||||
IllegalAccessException::class,
|
IllegalAccessException::class,
|
||||||
@@ -17,6 +24,6 @@ class TestRunner : AndroidJUnitRunner() {
|
|||||||
className: String?,
|
className: String?,
|
||||||
context: Context?
|
context: Context?
|
||||||
): Application {
|
): Application {
|
||||||
return super.newApplication(cl, TestApplication::class.java.name, context)
|
return super.newApplication(cl, HiltTestApplication::class.java.name, context)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,13 +1,14 @@
|
|||||||
package com.appttude.h_mal.easycc.application.modules
|
package com.appttude.h_mal.easycc.application.modules
|
||||||
|
|
||||||
import com.appttude.h_mal.easycc.application.TestApplication.Companion.idlingResources
|
import com.appttude.h_mal.easycc.application.TestRunner.Companion.idlingResources
|
||||||
import com.appttude.h_mal.easycc.data.network.response.CurrencyResponse
|
import com.appttude.h_mal.easycc.data.network.response.CurrencyResponse
|
||||||
import com.appttude.h_mal.easycc.data.network.response.ResponseObject
|
import com.appttude.h_mal.easycc.data.network.response.ResponseObject
|
||||||
import com.appttude.h_mal.easycc.data.repository.Repository
|
import com.appttude.h_mal.easycc.data.repository.Repository
|
||||||
import com.appttude.h_mal.easycc.models.CurrencyObject
|
import com.appttude.h_mal.easycc.models.CurrencyObject
|
||||||
import kotlinx.coroutines.delay
|
import kotlinx.coroutines.delay
|
||||||
|
import javax.inject.Inject
|
||||||
|
|
||||||
class MockRepository : Repository {
|
class MockRepository @Inject constructor() : Repository {
|
||||||
|
|
||||||
override suspend fun getDataFromApi(fromCurrency: String, toCurrency: String): ResponseObject {
|
override suspend fun getDataFromApi(fromCurrency: String, toCurrency: String): ResponseObject {
|
||||||
idlingResources.increment()
|
idlingResources.increment()
|
||||||
|
|||||||
@@ -18,11 +18,11 @@ fun currencyRobot(func: CurrencyRobot.() -> Unit) = CurrencyRobot()
|
|||||||
class CurrencyRobot {
|
class CurrencyRobot {
|
||||||
|
|
||||||
fun clickOnTopList() {
|
fun clickOnTopList() {
|
||||||
Espresso.onView(ViewMatchers.withId(R.id.currency_one)).perform(ViewActions.click())
|
Espresso.onView(ViewMatchers.withId(R.id.currencyOne)).perform(ViewActions.click())
|
||||||
}
|
}
|
||||||
|
|
||||||
fun clickOnBottomList() {
|
fun clickOnBottomList() {
|
||||||
Espresso.onView(ViewMatchers.withId(R.id.currency_two)).perform(ViewActions.click())
|
Espresso.onView(ViewMatchers.withId(R.id.currencyTwo)).perform(ViewActions.click())
|
||||||
}
|
}
|
||||||
|
|
||||||
fun searchInCurrencyList(search: String) {
|
fun searchInCurrencyList(search: String) {
|
||||||
|
|||||||
@@ -2,23 +2,47 @@
|
|||||||
|
|
||||||
package com.appttude.h_mal.easycc.ui.main
|
package com.appttude.h_mal.easycc.ui.main
|
||||||
|
|
||||||
|
|
||||||
import androidx.test.filters.LargeTest
|
|
||||||
import androidx.test.rule.ActivityTestRule
|
import androidx.test.rule.ActivityTestRule
|
||||||
import androidx.test.runner.AndroidJUnit4
|
import com.appttude.h_mal.easycc.application.modules.MockRepository
|
||||||
|
import com.appttude.h_mal.easycc.data.repository.Repository
|
||||||
|
import com.appttude.h_mal.easycc.data.repository.RepositoryImpl
|
||||||
|
import com.appttude.h_mal.easycc.di.AppModule
|
||||||
import com.appttude.h_mal.easycc.robots.currencyRobot
|
import com.appttude.h_mal.easycc.robots.currencyRobot
|
||||||
|
import dagger.Module
|
||||||
|
import dagger.Provides
|
||||||
|
import dagger.hilt.InstallIn
|
||||||
|
import dagger.hilt.android.testing.HiltAndroidRule
|
||||||
|
import dagger.hilt.android.testing.HiltAndroidTest
|
||||||
|
import dagger.hilt.android.testing.UninstallModules
|
||||||
|
import dagger.hilt.components.SingletonComponent
|
||||||
|
import org.junit.Before
|
||||||
import org.junit.Rule
|
import org.junit.Rule
|
||||||
import org.junit.Test
|
import org.junit.Test
|
||||||
import org.junit.runner.RunWith
|
|
||||||
|
|
||||||
@LargeTest
|
@HiltAndroidTest
|
||||||
@RunWith(AndroidJUnit4::class)
|
@UninstallModules(AppModule::class)
|
||||||
class MainActivityTest {
|
class MainActivityTest {
|
||||||
|
|
||||||
@Rule
|
@get:Rule(order = 0)
|
||||||
@JvmField
|
var hiltAndroidRule = HiltAndroidRule(this)
|
||||||
|
|
||||||
|
@get:Rule(order = 1)
|
||||||
var mActivityTestRule = ActivityTestRule(MainActivity::class.java)
|
var mActivityTestRule = ActivityTestRule(MainActivity::class.java)
|
||||||
|
|
||||||
|
@Before
|
||||||
|
fun setUp() {
|
||||||
|
hiltAndroidRule.inject()
|
||||||
|
}
|
||||||
|
|
||||||
|
@Module
|
||||||
|
@InstallIn(SingletonComponent::class)
|
||||||
|
object TestAppModule {
|
||||||
|
@Provides
|
||||||
|
fun provideRepository(impl: MockRepository): Repository {
|
||||||
|
return impl
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun mainActivityTest() {
|
fun mainActivityTest() {
|
||||||
currencyRobot {
|
currencyRobot {
|
||||||
|
|||||||
@@ -20,7 +20,8 @@
|
|||||||
android:roundIcon="@mipmap/ic_launcher"
|
android:roundIcon="@mipmap/ic_launcher"
|
||||||
android:supportsRtl="true"
|
android:supportsRtl="true"
|
||||||
android:theme="@style/AppTheme">
|
android:theme="@style/AppTheme">
|
||||||
<receiver android:name="com.appttude.h_mal.easycc.widget.CurrencyAppWidgetKotlin">
|
<receiver android:name="com.appttude.h_mal.easycc.widget.CurrencyAppWidgetKotlin"
|
||||||
|
android:exported="true">
|
||||||
<intent-filter>
|
<intent-filter>
|
||||||
<action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
|
<action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
|
||||||
</intent-filter>
|
</intent-filter>
|
||||||
@@ -30,12 +31,14 @@
|
|||||||
android:resource="@xml/currency_app_widget_info" />
|
android:resource="@xml/currency_app_widget_info" />
|
||||||
</receiver>
|
</receiver>
|
||||||
|
|
||||||
<activity android:name="com.appttude.h_mal.easycc.ui.widget.CurrencyAppWidgetConfigureActivityKotlin">
|
<activity android:name="com.appttude.h_mal.easycc.ui.widget.CurrencyAppWidgetConfigureActivityKotlin"
|
||||||
|
android:exported="true">
|
||||||
<intent-filter>
|
<intent-filter>
|
||||||
<action android:name="android.appwidget.action.APPWIDGET_CONFIGURE" />
|
<action android:name="android.appwidget.action.APPWIDGET_CONFIGURE" />
|
||||||
</intent-filter>
|
</intent-filter>
|
||||||
</activity>
|
</activity>
|
||||||
<activity android:name="com.appttude.h_mal.easycc.ui.main.MainActivity">
|
<activity android:name="com.appttude.h_mal.easycc.ui.main.MainActivity"
|
||||||
|
android:exported="true">
|
||||||
<intent-filter>
|
<intent-filter>
|
||||||
<action android:name="android.intent.action.MAIN" />
|
<action android:name="android.intent.action.MAIN" />
|
||||||
|
|
||||||
|
|||||||
@@ -1,42 +1,7 @@
|
|||||||
package com.appttude.h_mal.easycc.application
|
package com.appttude.h_mal.easycc.application
|
||||||
|
|
||||||
import android.app.Application
|
import android.app.Application
|
||||||
import com.appttude.h_mal.easycc.data.network.api.BackupCurrencyApi
|
import dagger.hilt.android.HiltAndroidApp
|
||||||
import com.appttude.h_mal.easycc.data.network.api.CurrencyApi
|
|
||||||
import com.appttude.h_mal.easycc.data.network.interceptors.NetworkConnectionInterceptor
|
|
||||||
import com.appttude.h_mal.easycc.data.network.interceptors.QueryInterceptor
|
|
||||||
import com.appttude.h_mal.easycc.data.network.interceptors.loggingInterceptor
|
|
||||||
import com.appttude.h_mal.easycc.data.prefs.PreferenceProvider
|
|
||||||
import com.appttude.h_mal.easycc.data.repository.RepositoryImpl
|
|
||||||
import com.appttude.h_mal.easycc.helper.CurrencyDataHelper
|
|
||||||
import com.appttude.h_mal.easycc.helper.WidgetHelper
|
|
||||||
import com.appttude.h_mal.easycc.ui.main.MainViewModelFactory
|
|
||||||
import com.appttude.h_mal.easycc.ui.widget.WidgetViewModelFactory
|
|
||||||
import org.kodein.di.Kodein
|
|
||||||
import org.kodein.di.KodeinAware
|
|
||||||
import org.kodein.di.android.x.androidXModule
|
|
||||||
import org.kodein.di.generic.bind
|
|
||||||
import org.kodein.di.generic.instance
|
|
||||||
import org.kodein.di.generic.provider
|
|
||||||
import org.kodein.di.generic.singleton
|
|
||||||
|
|
||||||
class AppClass : Application(), KodeinAware {
|
@HiltAndroidApp
|
||||||
|
class AppClass : Application()
|
||||||
// KODEIN DI components declaration
|
|
||||||
override val kodein by Kodein.lazy {
|
|
||||||
import(androidXModule(this@AppClass))
|
|
||||||
|
|
||||||
bind() from singleton { NetworkConnectionInterceptor(instance()) }
|
|
||||||
bind() from singleton { loggingInterceptor() }
|
|
||||||
bind() from singleton { QueryInterceptor(instance()) }
|
|
||||||
bind() from singleton { CurrencyApi(instance(), instance(), instance()) }
|
|
||||||
bind() from singleton { BackupCurrencyApi(instance(), instance()) }
|
|
||||||
bind() from singleton { PreferenceProvider(instance()) }
|
|
||||||
bind() from singleton { RepositoryImpl(instance(), instance(), instance()) }
|
|
||||||
bind() from singleton { CurrencyDataHelper(instance()) }
|
|
||||||
bind() from singleton { WidgetHelper(instance(), instance()) }
|
|
||||||
bind() from provider { MainViewModelFactory(instance(), instance()) }
|
|
||||||
bind() from provider { WidgetViewModelFactory(instance()) }
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
package com.appttude.h_mal.easycc.data.network.api
|
||||||
|
|
||||||
|
interface Api
|
||||||
@@ -13,7 +13,7 @@ import retrofit2.http.Query
|
|||||||
/**
|
/**
|
||||||
* Retrofit Network class to currency api calls
|
* Retrofit Network class to currency api calls
|
||||||
*/
|
*/
|
||||||
interface BackupCurrencyApi {
|
interface BackupCurrencyApi : Api {
|
||||||
|
|
||||||
@GET("latest?")
|
@GET("latest?")
|
||||||
suspend fun getCurrencyRate(
|
suspend fun getCurrencyRate(
|
||||||
@@ -21,24 +21,4 @@ interface BackupCurrencyApi {
|
|||||||
@Query("to") currencyTo: String
|
@Query("to") currencyTo: String
|
||||||
): Response<CurrencyResponse>
|
): Response<CurrencyResponse>
|
||||||
|
|
||||||
companion object {
|
|
||||||
operator fun invoke(
|
|
||||||
networkConnectionInterceptor: NetworkConnectionInterceptor,
|
|
||||||
interceptor: HttpLoggingInterceptor
|
|
||||||
): BackupCurrencyApi {
|
|
||||||
|
|
||||||
val okkHttpclient = OkHttpClient.Builder()
|
|
||||||
.addInterceptor(interceptor)
|
|
||||||
.addNetworkInterceptor(networkConnectionInterceptor)
|
|
||||||
.build()
|
|
||||||
|
|
||||||
return Retrofit.Builder()
|
|
||||||
.client(okkHttpclient)
|
|
||||||
.baseUrl("https://api.frankfurter.app/")
|
|
||||||
.addConverterFactory(GsonConverterFactory.create())
|
|
||||||
.build()
|
|
||||||
.create(BackupCurrencyApi::class.java)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -14,32 +14,10 @@ import retrofit2.http.Query
|
|||||||
/**
|
/**
|
||||||
* Retrofit Network class to currency api calls
|
* Retrofit Network class to currency api calls
|
||||||
*/
|
*/
|
||||||
interface CurrencyApi {
|
interface CurrencyApi : Api{
|
||||||
|
|
||||||
// Get rate from server with arguments passed in Repository
|
// Get rate from server with arguments passed in Repository
|
||||||
@GET("convert?")
|
@GET("convert?")
|
||||||
suspend fun getCurrencyRate(@Query("q") currency: String): Response<ResponseObject>
|
suspend fun getCurrencyRate(@Query("q") currency: String): Response<ResponseObject>
|
||||||
|
|
||||||
// interface invokation to be used in application class
|
|
||||||
companion object {
|
|
||||||
operator fun invoke(
|
|
||||||
networkConnectionInterceptor: NetworkConnectionInterceptor,
|
|
||||||
queryInterceptor: QueryInterceptor,
|
|
||||||
interceptor: HttpLoggingInterceptor
|
|
||||||
): CurrencyApi {
|
|
||||||
|
|
||||||
val okkHttpclient = OkHttpClient.Builder()
|
|
||||||
.addInterceptor(interceptor)
|
|
||||||
.addInterceptor(queryInterceptor)
|
|
||||||
.addNetworkInterceptor(networkConnectionInterceptor)
|
|
||||||
.build()
|
|
||||||
|
|
||||||
return Retrofit.Builder()
|
|
||||||
.client(okkHttpclient)
|
|
||||||
.baseUrl("https://free.currencyconverterapi.com/api/v3/")
|
|
||||||
.addConverterFactory(GsonConverterFactory.create())
|
|
||||||
.build()
|
|
||||||
.create(CurrencyApi::class.java)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
package com.appttude.h_mal.easycc.data.network.api
|
||||||
|
|
||||||
|
import okhttp3.OkHttpClient
|
||||||
|
import retrofit2.Retrofit
|
||||||
|
import retrofit2.converter.gson.GsonConverterFactory
|
||||||
|
import javax.inject.Inject
|
||||||
|
|
||||||
|
class RemoteDataSource @Inject constructor(){
|
||||||
|
|
||||||
|
fun <Api> buildApi(
|
||||||
|
okkHttpclient: OkHttpClient,
|
||||||
|
baseUrl: String,
|
||||||
|
api: Class<Api>
|
||||||
|
): Api {
|
||||||
|
|
||||||
|
return Retrofit.Builder()
|
||||||
|
.client(okkHttpclient)
|
||||||
|
.baseUrl(baseUrl)
|
||||||
|
.addConverterFactory(GsonConverterFactory.create())
|
||||||
|
.build()
|
||||||
|
.create(api)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -4,17 +4,19 @@ import android.content.Context
|
|||||||
import android.net.ConnectivityManager
|
import android.net.ConnectivityManager
|
||||||
import android.net.NetworkCapabilities
|
import android.net.NetworkCapabilities
|
||||||
import android.os.Build
|
import android.os.Build
|
||||||
|
import dagger.hilt.android.qualifiers.ApplicationContext
|
||||||
import okhttp3.Interceptor
|
import okhttp3.Interceptor
|
||||||
import okhttp3.Response
|
import okhttp3.Response
|
||||||
import java.io.IOException
|
import java.io.IOException
|
||||||
|
import javax.inject.Inject
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Interceptor used in network classes to check network status
|
* Interceptor used in network classes to check network status
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
@Suppress("DEPRECATION")
|
@Suppress("DEPRECATION")
|
||||||
class NetworkConnectionInterceptor(
|
class NetworkConnectionInterceptor @Inject constructor(
|
||||||
context: Context
|
@ApplicationContext context: Context
|
||||||
) : Interceptor {
|
) : Interceptor {
|
||||||
|
|
||||||
private val applicationContext = context.applicationContext
|
private val applicationContext = context.applicationContext
|
||||||
@@ -27,13 +29,12 @@ class NetworkConnectionInterceptor(
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun isInternetAvailable(): Boolean {
|
private fun isInternetAvailable(): Boolean {
|
||||||
var result = false
|
|
||||||
val connectivityManager =
|
val connectivityManager =
|
||||||
applicationContext.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager?
|
applicationContext.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager?
|
||||||
connectivityManager?.let {
|
return connectivityManager?.let {
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
|
||||||
it.getNetworkCapabilities(connectivityManager.activeNetwork)?.apply {
|
it.getNetworkCapabilities(connectivityManager.activeNetwork)?.run {
|
||||||
result = when {
|
when {
|
||||||
hasTransport(NetworkCapabilities.TRANSPORT_WIFI) -> true
|
hasTransport(NetworkCapabilities.TRANSPORT_WIFI) -> true
|
||||||
hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR) -> true
|
hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR) -> true
|
||||||
else -> false
|
else -> false
|
||||||
@@ -41,7 +42,7 @@ class NetworkConnectionInterceptor(
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
it.activeNetworkInfo?.run {
|
it.activeNetworkInfo?.run {
|
||||||
result = when (type) {
|
when (type) {
|
||||||
ConnectivityManager.TYPE_WIFI -> true
|
ConnectivityManager.TYPE_WIFI -> true
|
||||||
ConnectivityManager.TYPE_MOBILE -> true
|
ConnectivityManager.TYPE_MOBILE -> true
|
||||||
ConnectivityManager.TYPE_ETHERNET -> true
|
ConnectivityManager.TYPE_ETHERNET -> true
|
||||||
@@ -49,8 +50,7 @@ class NetworkConnectionInterceptor(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
} ?: false
|
||||||
return result
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -2,17 +2,19 @@ package com.appttude.h_mal.easycc.data.network.interceptors
|
|||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import com.appttude.h_mal.easycc.R
|
import com.appttude.h_mal.easycc.R
|
||||||
|
import dagger.hilt.android.qualifiers.ApplicationContext
|
||||||
import okhttp3.HttpUrl
|
import okhttp3.HttpUrl
|
||||||
import okhttp3.Interceptor
|
import okhttp3.Interceptor
|
||||||
import okhttp3.Request
|
import okhttp3.Request
|
||||||
import okhttp3.Response
|
import okhttp3.Response
|
||||||
|
import javax.inject.Inject
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Interceptor used in CurrencyApi
|
* Interceptor used in CurrencyApi
|
||||||
* Adds apiKey to query parameters
|
* Adds apiKey to query parameters
|
||||||
*/
|
*/
|
||||||
class QueryInterceptor(
|
class QueryInterceptor @Inject constructor(
|
||||||
val context: Context
|
@ApplicationContext val context: Context
|
||||||
) : Interceptor {
|
) : Interceptor {
|
||||||
|
|
||||||
override fun intercept(chain: Interceptor.Chain): Response {
|
override fun intercept(chain: Interceptor.Chain): Response {
|
||||||
|
|||||||
@@ -6,18 +6,18 @@ import com.appttude.h_mal.easycc.models.CurrencyObject
|
|||||||
import com.google.gson.annotations.SerializedName
|
import com.google.gson.annotations.SerializedName
|
||||||
|
|
||||||
data class ResponseObject(
|
data class ResponseObject(
|
||||||
@field:SerializedName("query")
|
@field:SerializedName("query")
|
||||||
var query: Any? = null,
|
var query: Any? = null,
|
||||||
@field:SerializedName("results")
|
@field:SerializedName("results")
|
||||||
var results: Map<String, CurrencyObject>? = null
|
var results: Map<String, CurrencyObject>? = null
|
||||||
) : CurrencyModelInterface {
|
) : CurrencyModelInterface {
|
||||||
|
|
||||||
override fun getCurrencyModel(): CurrencyModel {
|
override fun getCurrencyModel(): CurrencyModel {
|
||||||
val res = results?.iterator()?.next()?.value
|
val res = results?.iterator()?.next()?.value
|
||||||
return CurrencyModel(
|
return CurrencyModel(
|
||||||
res?.fr,
|
res?.fr,
|
||||||
res?.to,
|
res?.to,
|
||||||
res?.value ?: 0.0
|
res?.value ?: 0.0
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -4,6 +4,8 @@ import android.content.Context
|
|||||||
import android.content.SharedPreferences
|
import android.content.SharedPreferences
|
||||||
import androidx.preference.PreferenceManager
|
import androidx.preference.PreferenceManager
|
||||||
import com.appttude.h_mal.easycc.R
|
import com.appttude.h_mal.easycc.R
|
||||||
|
import dagger.hilt.android.qualifiers.ApplicationContext
|
||||||
|
import javax.inject.Inject
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Shared prefs class used for storing conversion name values as pairs
|
* Shared prefs class used for storing conversion name values as pairs
|
||||||
@@ -13,7 +15,7 @@ import com.appttude.h_mal.easycc.R
|
|||||||
private const val CURRENCY_ONE = "conversion_one"
|
private const val CURRENCY_ONE = "conversion_one"
|
||||||
private const val CURRENCY_TWO = "conversion_two"
|
private const val CURRENCY_TWO = "conversion_two"
|
||||||
|
|
||||||
class PreferenceProvider(context: Context) {
|
class PreferenceProvider @Inject constructor(@ApplicationContext context: Context) {
|
||||||
|
|
||||||
private val appContext = context.applicationContext
|
private val appContext = context.applicationContext
|
||||||
|
|
||||||
|
|||||||
@@ -9,11 +9,12 @@ import com.appttude.h_mal.easycc.data.network.response.CurrencyResponse
|
|||||||
import com.appttude.h_mal.easycc.data.network.response.ResponseObject
|
import com.appttude.h_mal.easycc.data.network.response.ResponseObject
|
||||||
import com.appttude.h_mal.easycc.data.prefs.PreferenceProvider
|
import com.appttude.h_mal.easycc.data.prefs.PreferenceProvider
|
||||||
import com.appttude.h_mal.easycc.utils.convertPairsListToString
|
import com.appttude.h_mal.easycc.utils.convertPairsListToString
|
||||||
|
import javax.inject.Inject
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Default implementation of [Repository]. Single entry point for managing currency' data.
|
* Default implementation of [Repository]. Single entry point for managing currency' data.
|
||||||
*/
|
*/
|
||||||
class RepositoryImpl(
|
class RepositoryImpl @Inject constructor(
|
||||||
private val api: CurrencyApi,
|
private val api: CurrencyApi,
|
||||||
private val backUpApi: BackupCurrencyApi,
|
private val backUpApi: BackupCurrencyApi,
|
||||||
private val prefs: PreferenceProvider
|
private val prefs: PreferenceProvider
|
||||||
|
|||||||
60
app/src/main/java/com/appttude/h_mal/easycc/di/AppModule.kt
Normal file
60
app/src/main/java/com/appttude/h_mal/easycc/di/AppModule.kt
Normal file
@@ -0,0 +1,60 @@
|
|||||||
|
package com.appttude.h_mal.easycc.di
|
||||||
|
|
||||||
|
import com.appttude.h_mal.easycc.data.network.api.BackupCurrencyApi
|
||||||
|
import com.appttude.h_mal.easycc.data.network.api.CurrencyApi
|
||||||
|
import com.appttude.h_mal.easycc.data.network.api.RemoteDataSource
|
||||||
|
import com.appttude.h_mal.easycc.data.network.interceptors.NetworkConnectionInterceptor
|
||||||
|
import com.appttude.h_mal.easycc.data.repository.Repository
|
||||||
|
import com.appttude.h_mal.easycc.data.repository.RepositoryImpl
|
||||||
|
import dagger.Module
|
||||||
|
import dagger.Provides
|
||||||
|
import dagger.hilt.InstallIn
|
||||||
|
import dagger.hilt.components.SingletonComponent
|
||||||
|
import okhttp3.OkHttpClient
|
||||||
|
import okhttp3.logging.HttpLoggingInterceptor
|
||||||
|
|
||||||
|
@Module
|
||||||
|
@InstallIn(SingletonComponent::class)
|
||||||
|
class AppModule {
|
||||||
|
|
||||||
|
@Provides
|
||||||
|
fun provideHttpLoggingInterceptor(): HttpLoggingInterceptor = HttpLoggingInterceptor()
|
||||||
|
|
||||||
|
@Provides
|
||||||
|
fun provideOkHttpclient(
|
||||||
|
interceptor: HttpLoggingInterceptor,
|
||||||
|
networkConnectionInterceptor: NetworkConnectionInterceptor,
|
||||||
|
) = OkHttpClient.Builder()
|
||||||
|
.addInterceptor(interceptor)
|
||||||
|
.addNetworkInterceptor(networkConnectionInterceptor)
|
||||||
|
.build()
|
||||||
|
|
||||||
|
@Provides
|
||||||
|
fun provideCurrencyApi(
|
||||||
|
remoteDataSource: RemoteDataSource,
|
||||||
|
okHttpClient: OkHttpClient
|
||||||
|
): CurrencyApi {
|
||||||
|
return remoteDataSource.buildApi(
|
||||||
|
okHttpClient,
|
||||||
|
"https://free.currencyconverterapi.com/api/v3/",
|
||||||
|
CurrencyApi::class.java
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Provides
|
||||||
|
fun provideBackupCurrencyApi(
|
||||||
|
remoteDataSource: RemoteDataSource,
|
||||||
|
okHttpClient: OkHttpClient
|
||||||
|
): BackupCurrencyApi {
|
||||||
|
return remoteDataSource.buildApi(
|
||||||
|
okHttpClient,
|
||||||
|
"https://api.frankfurter.app/",
|
||||||
|
BackupCurrencyApi::class.java
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Provides
|
||||||
|
fun provideRepository(impl: RepositoryImpl): Repository {
|
||||||
|
return impl
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -2,8 +2,9 @@ package com.appttude.h_mal.easycc.helper
|
|||||||
|
|
||||||
import com.appttude.h_mal.easycc.data.repository.Repository
|
import com.appttude.h_mal.easycc.data.repository.Repository
|
||||||
import com.appttude.h_mal.easycc.models.CurrencyModelInterface
|
import com.appttude.h_mal.easycc.models.CurrencyModelInterface
|
||||||
|
import javax.inject.Inject
|
||||||
|
|
||||||
class CurrencyDataHelper(
|
class CurrencyDataHelper @Inject constructor(
|
||||||
val repository: Repository
|
val repository: Repository
|
||||||
) {
|
) {
|
||||||
|
|
||||||
|
|||||||
@@ -3,8 +3,9 @@ package com.appttude.h_mal.easycc.helper
|
|||||||
import com.appttude.h_mal.easycc.data.repository.Repository
|
import com.appttude.h_mal.easycc.data.repository.Repository
|
||||||
import com.appttude.h_mal.easycc.models.CurrencyModel
|
import com.appttude.h_mal.easycc.models.CurrencyModel
|
||||||
import com.appttude.h_mal.easycc.utils.trimToThree
|
import com.appttude.h_mal.easycc.utils.trimToThree
|
||||||
|
import javax.inject.Inject
|
||||||
|
|
||||||
class WidgetHelper(
|
class WidgetHelper @Inject constructor(
|
||||||
private val helper: CurrencyDataHelper,
|
private val helper: CurrencyDataHelper,
|
||||||
val repository: Repository
|
val repository: Repository
|
||||||
) {
|
) {
|
||||||
|
|||||||
@@ -7,8 +7,10 @@ import android.text.Editable
|
|||||||
import android.text.TextWatcher
|
import android.text.TextWatcher
|
||||||
import android.view.WindowManager
|
import android.view.WindowManager
|
||||||
import android.widget.ArrayAdapter
|
import android.widget.ArrayAdapter
|
||||||
|
import android.widget.ListView
|
||||||
|
import android.widget.TextView
|
||||||
|
import androidx.appcompat.widget.SearchView
|
||||||
import com.appttude.h_mal.easycc.R
|
import com.appttude.h_mal.easycc.R
|
||||||
import kotlinx.android.synthetic.main.custom_dialog.*
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Custom dialog when selecting currencies from list with filter
|
* Custom dialog when selecting currencies from list with filter
|
||||||
@@ -35,10 +37,11 @@ class CustomDialogClass(
|
|||||||
android.R.layout.simple_list_item_1
|
android.R.layout.simple_list_item_1
|
||||||
)
|
)
|
||||||
|
|
||||||
|
val list_view = findViewById<ListView>(R.id.list_view)
|
||||||
list_view.adapter = arrayAdapter
|
list_view.adapter = arrayAdapter
|
||||||
|
|
||||||
// Edit text to filter @arrayAdapter
|
// Edit text to filter @arrayAdapter
|
||||||
search_text.addTextChangedListener(object : TextWatcher {
|
findViewById<TextView>(R.id.search_text).addTextChangedListener(object : TextWatcher {
|
||||||
override fun beforeTextChanged(charSequence: CharSequence, i: Int, i1: Int, i2: Int) {}
|
override fun beforeTextChanged(charSequence: CharSequence, i: Int, i1: Int, i2: Int) {}
|
||||||
override fun onTextChanged(charSequence: CharSequence, i: Int, i1: Int, i2: Int) {
|
override fun onTextChanged(charSequence: CharSequence, i: Int, i1: Int, i2: Int) {
|
||||||
arrayAdapter.filter.filter(charSequence)
|
arrayAdapter.filter.filter(charSequence)
|
||||||
|
|||||||
@@ -6,77 +6,66 @@ import android.text.TextWatcher
|
|||||||
import android.view.View
|
import android.view.View
|
||||||
import android.view.WindowManager
|
import android.view.WindowManager
|
||||||
import android.widget.TextView
|
import android.widget.TextView
|
||||||
|
import androidx.activity.viewModels
|
||||||
import androidx.appcompat.app.AppCompatActivity
|
import androidx.appcompat.app.AppCompatActivity
|
||||||
import androidx.databinding.DataBindingUtil
|
|
||||||
import androidx.lifecycle.ViewModelProviders
|
|
||||||
import com.appttude.h_mal.easycc.R
|
import com.appttude.h_mal.easycc.R
|
||||||
import com.appttude.h_mal.easycc.databinding.ActivityMainBinding
|
import com.appttude.h_mal.easycc.databinding.ActivityMainBinding
|
||||||
import com.appttude.h_mal.easycc.utils.clearEditText
|
import com.appttude.h_mal.easycc.utils.clearEditText
|
||||||
import com.appttude.h_mal.easycc.utils.displayToast
|
import com.appttude.h_mal.easycc.utils.displayToast
|
||||||
import com.appttude.h_mal.easycc.utils.hideView
|
import com.appttude.h_mal.easycc.utils.hideView
|
||||||
import kotlinx.android.synthetic.main.activity_main.*
|
import dagger.hilt.android.AndroidEntryPoint
|
||||||
import org.kodein.di.KodeinAware
|
|
||||||
import org.kodein.di.android.kodein
|
|
||||||
import org.kodein.di.generic.instance
|
|
||||||
|
|
||||||
@Suppress("DEPRECATION")
|
@AndroidEntryPoint
|
||||||
class MainActivity : AppCompatActivity(), KodeinAware, View.OnClickListener {
|
class MainActivity : AppCompatActivity(), View.OnClickListener {
|
||||||
|
|
||||||
// Retrieve MainViewModelFactory via dependency injection
|
private val viewModel: MainViewModel by viewModels()
|
||||||
override val kodein by kodein()
|
private lateinit var binding: ActivityMainBinding
|
||||||
private val factory: MainViewModelFactory by instance()
|
|
||||||
|
|
||||||
lateinit var viewModel: MainViewModel
|
|
||||||
|
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
// Keyboard is not overlapping views
|
binding = ActivityMainBinding.inflate(layoutInflater)
|
||||||
|
setContentView(binding.root)
|
||||||
|
/*
|
||||||
|
* Prevent keyboard overlapping views
|
||||||
|
*/
|
||||||
window.setSoftInputMode(
|
window.setSoftInputMode(
|
||||||
WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_HIDDEN or
|
WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_HIDDEN or
|
||||||
WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE
|
WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE
|
||||||
)
|
)
|
||||||
|
|
||||||
viewModel = ViewModelProviders.of(this, factory)
|
|
||||||
.get(MainViewModel::class.java)
|
|
||||||
|
|
||||||
// Bind viewmodel to layout with view binding
|
|
||||||
DataBindingUtil
|
|
||||||
.setContentView<ActivityMainBinding>(this, R.layout.activity_main)
|
|
||||||
.apply {
|
|
||||||
viewmodel = viewModel
|
|
||||||
lifecycleOwner = this@MainActivity
|
|
||||||
}
|
|
||||||
|
|
||||||
viewModel.initiate(intent.extras)
|
viewModel.initiate(intent.extras)
|
||||||
|
|
||||||
|
binding.currencyOne.text = viewModel.rateIdTo
|
||||||
|
binding.currencyTwo.text = viewModel.rateIdFrom
|
||||||
|
|
||||||
setUpListeners()
|
setUpListeners()
|
||||||
setUpObservers()
|
setUpObservers()
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun setUpObservers() {
|
private fun setUpObservers() {
|
||||||
viewModel.operationStartedListener.observe(this, {
|
viewModel.operationStartedListener.observe(this) {
|
||||||
progressBar.hideView(false)
|
binding.progressBar.hideView(false)
|
||||||
})
|
}
|
||||||
viewModel.operationFinishedListener.observe(this, { pair ->
|
viewModel.operationFinishedListener.observe(this) { pair ->
|
||||||
// hide progress bar
|
// hide progress bar
|
||||||
progressBar.hideView(true)
|
binding.progressBar.hideView(true)
|
||||||
if (pair.first) {
|
if (pair.first) {
|
||||||
// Operation was successful remove text in EditTexts
|
// Operation was successful remove text in EditTexts
|
||||||
bottomInsertValues.clearEditText()
|
binding.bottomInsertValues.clearEditText()
|
||||||
topInsertValue.clearEditText()
|
binding.topInsertValue.clearEditText()
|
||||||
} else {
|
} else {
|
||||||
// Display Toast with error message returned from Viewmodel
|
// Display Toast with error message returned from Viewmodel
|
||||||
pair.second?.let { displayToast(it) }
|
pair.second?.let { displayToast(it) }
|
||||||
}
|
}
|
||||||
})
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun setUpListeners() {
|
private fun setUpListeners() {
|
||||||
topInsertValue.addTextChangedListener(textWatcherClass)
|
binding.topInsertValue.addTextChangedListener(textWatcherClass)
|
||||||
bottomInsertValues.addTextChangedListener(textWatcherClass2)
|
binding.bottomInsertValues.addTextChangedListener(textWatcherClass2)
|
||||||
|
|
||||||
currency_one.setOnClickListener(this)
|
binding.currencyOne.setOnClickListener(this)
|
||||||
currency_two.setOnClickListener(this)
|
binding.currencyTwo.setOnClickListener(this)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun showCustomDialog(view: View?) {
|
private fun showCustomDialog(view: View?) {
|
||||||
@@ -97,32 +86,32 @@ class MainActivity : AppCompatActivity(), KodeinAware, View.OnClickListener {
|
|||||||
private val textWatcherClass: TextWatcher = object : TextWatcher {
|
private val textWatcherClass: TextWatcher = object : TextWatcher {
|
||||||
override fun onTextChanged(s: CharSequence, st: Int, b: Int, c: Int) {
|
override fun onTextChanged(s: CharSequence, st: Int, b: Int, c: Int) {
|
||||||
// Remove text watcher on other text watcher to prevent infinite loop
|
// Remove text watcher on other text watcher to prevent infinite loop
|
||||||
bottomInsertValues.removeTextChangedListener(textWatcherClass2)
|
binding.bottomInsertValues.removeTextChangedListener(textWatcherClass2)
|
||||||
// Clear any values if current EditText is empty
|
// Clear any values if current EditText is empty
|
||||||
if (topInsertValue.text.isNullOrEmpty())
|
if (binding.topInsertValue.text.isNullOrEmpty())
|
||||||
bottomInsertValues.setText("")
|
binding.bottomInsertValues.setText("")
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun beforeTextChanged(s: CharSequence, start: Int, count: Int, after: Int) {}
|
override fun beforeTextChanged(s: CharSequence, start: Int, count: Int, after: Int) {}
|
||||||
override fun afterTextChanged(s: Editable) {
|
override fun afterTextChanged(s: Editable) {
|
||||||
bottomInsertValues.setText(viewModel.getConversion(s.toString()))
|
binding.bottomInsertValues.setText(viewModel.getConversion(s.toString()))
|
||||||
// add Text watcher back as it is safe to do so
|
// add Text watcher back as it is safe to do so
|
||||||
bottomInsertValues.addTextChangedListener(textWatcherClass2)
|
binding.bottomInsertValues.addTextChangedListener(textWatcherClass2)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private val textWatcherClass2: TextWatcher = object : TextWatcher {
|
private val textWatcherClass2: TextWatcher = object : TextWatcher {
|
||||||
override fun onTextChanged(s: CharSequence, st: Int, b: Int, c: Int) {
|
override fun onTextChanged(s: CharSequence, st: Int, b: Int, c: Int) {
|
||||||
|
|
||||||
topInsertValue.removeTextChangedListener(textWatcherClass)
|
binding.topInsertValue.removeTextChangedListener(textWatcherClass)
|
||||||
if (bottomInsertValues.text.isNullOrEmpty())
|
if (binding.bottomInsertValues.text.isNullOrEmpty())
|
||||||
topInsertValue.clearEditText()
|
binding.topInsertValue.clearEditText()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun beforeTextChanged(s: CharSequence, start: Int, count: Int, after: Int) {}
|
override fun beforeTextChanged(s: CharSequence, start: Int, count: Int, after: Int) {}
|
||||||
override fun afterTextChanged(s: Editable) {
|
override fun afterTextChanged(s: Editable) {
|
||||||
topInsertValue.setText(viewModel.getReciprocalConversion(s.toString()))
|
binding.topInsertValue.setText(viewModel.getReciprocalConversion(s.toString()))
|
||||||
topInsertValue.addTextChangedListener(textWatcherClass)
|
binding.topInsertValue.addTextChangedListener(textWatcherClass)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -2,20 +2,25 @@ package com.appttude.h_mal.easycc.ui.main
|
|||||||
|
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import androidx.lifecycle.MutableLiveData
|
import androidx.lifecycle.MutableLiveData
|
||||||
|
import androidx.lifecycle.SavedStateHandle
|
||||||
import androidx.lifecycle.ViewModel
|
import androidx.lifecycle.ViewModel
|
||||||
|
import androidx.lifecycle.viewModelScope
|
||||||
import com.appttude.h_mal.easycc.data.repository.Repository
|
import com.appttude.h_mal.easycc.data.repository.Repository
|
||||||
import com.appttude.h_mal.easycc.helper.CurrencyDataHelper
|
import com.appttude.h_mal.easycc.helper.CurrencyDataHelper
|
||||||
import com.appttude.h_mal.easycc.utils.toTwoDpString
|
import com.appttude.h_mal.easycc.utils.toTwoDpString
|
||||||
import com.appttude.h_mal.easycc.utils.trimToThree
|
import com.appttude.h_mal.easycc.utils.trimToThree
|
||||||
|
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||||
import kotlinx.coroutines.CoroutineScope
|
import kotlinx.coroutines.CoroutineScope
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import java.io.IOException
|
import java.io.IOException
|
||||||
|
import javax.inject.Inject
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* ViewModel for the task Main Activity Screen
|
* ViewModel for the task Main Activity Screen
|
||||||
*/
|
*/
|
||||||
class MainViewModel(
|
@HiltViewModel
|
||||||
|
class MainViewModel @Inject constructor(
|
||||||
private val currencyDataHelper: CurrencyDataHelper,
|
private val currencyDataHelper: CurrencyDataHelper,
|
||||||
private val repository: Repository
|
private val repository: Repository
|
||||||
) : ViewModel() {
|
) : ViewModel() {
|
||||||
@@ -48,7 +53,7 @@ class MainViewModel(
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
CoroutineScope(Dispatchers.IO).launch {
|
viewModelScope.launch {
|
||||||
try {
|
try {
|
||||||
// Non-null assertion (!!) as values have been null checked and have not changed
|
// Non-null assertion (!!) as values have been null checked and have not changed
|
||||||
val exchangeResponse = currencyDataHelper.getDataFromApi(
|
val exchangeResponse = currencyDataHelper.getDataFromApi(
|
||||||
|
|||||||
@@ -1,21 +0,0 @@
|
|||||||
package com.appttude.h_mal.easycc.ui.main
|
|
||||||
|
|
||||||
import androidx.lifecycle.ViewModel
|
|
||||||
import androidx.lifecycle.ViewModelProvider
|
|
||||||
import com.appttude.h_mal.easycc.data.repository.Repository
|
|
||||||
import com.appttude.h_mal.easycc.helper.CurrencyDataHelper
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Viewmodel factory for [MainViewModel]
|
|
||||||
* inject repository into viewmodel
|
|
||||||
*/
|
|
||||||
@Suppress("UNCHECKED_CAST")
|
|
||||||
class MainViewModelFactory(
|
|
||||||
private val repository: Repository,
|
|
||||||
private val helper: CurrencyDataHelper
|
|
||||||
) : ViewModelProvider.NewInstanceFactory() {
|
|
||||||
|
|
||||||
override fun <T : ViewModel?> create(modelClass: Class<T>): T {
|
|
||||||
return MainViewModel(helper, repository) as T
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -6,38 +6,32 @@ import android.content.Intent
|
|||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import android.widget.TextView
|
import android.widget.TextView
|
||||||
|
import androidx.activity.viewModels
|
||||||
import androidx.appcompat.app.AppCompatActivity
|
import androidx.appcompat.app.AppCompatActivity
|
||||||
import androidx.databinding.DataBindingUtil
|
|
||||||
import androidx.lifecycle.ViewModelProviders
|
|
||||||
import com.appttude.h_mal.easycc.R
|
|
||||||
import com.appttude.h_mal.easycc.databinding.CurrencyAppWidgetConfigureBinding
|
import com.appttude.h_mal.easycc.databinding.CurrencyAppWidgetConfigureBinding
|
||||||
import com.appttude.h_mal.easycc.ui.main.ClickListener
|
import com.appttude.h_mal.easycc.ui.main.ClickListener
|
||||||
import com.appttude.h_mal.easycc.ui.main.CustomDialogClass
|
import com.appttude.h_mal.easycc.ui.main.CustomDialogClass
|
||||||
import com.appttude.h_mal.easycc.utils.displayToast
|
import com.appttude.h_mal.easycc.utils.displayToast
|
||||||
import com.appttude.h_mal.easycc.utils.transformIntToArray
|
import com.appttude.h_mal.easycc.utils.transformIntToArray
|
||||||
import com.appttude.h_mal.easycc.widget.CurrencyAppWidgetKotlin
|
import com.appttude.h_mal.easycc.widget.CurrencyAppWidgetKotlin
|
||||||
import kotlinx.android.synthetic.main.currency_app_widget_configure.*
|
import dagger.hilt.android.AndroidEntryPoint
|
||||||
import org.kodein.di.KodeinAware
|
|
||||||
import org.kodein.di.android.kodein
|
|
||||||
import org.kodein.di.generic.instance
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The configuration screen for the [CurrencyAppWidgetKotlin] AppWidget.
|
* The configuration screen for the [CurrencyAppWidgetKotlin] AppWidget.
|
||||||
*/
|
*/
|
||||||
class CurrencyAppWidgetConfigureActivityKotlin : AppCompatActivity(), KodeinAware,
|
@AndroidEntryPoint
|
||||||
|
class CurrencyAppWidgetConfigureActivityKotlin : AppCompatActivity(),
|
||||||
View.OnClickListener {
|
View.OnClickListener {
|
||||||
|
|
||||||
override val kodein by kodein()
|
val viewModel: WidgetViewModel by viewModels()
|
||||||
private val factory: WidgetViewModelFactory by instance()
|
|
||||||
|
|
||||||
var mAppWidgetId = AppWidgetManager.INVALID_APPWIDGET_ID
|
private lateinit var binding: CurrencyAppWidgetConfigureBinding
|
||||||
|
private var mAppWidgetId = AppWidgetManager.INVALID_APPWIDGET_ID
|
||||||
companion object {
|
|
||||||
lateinit var viewModel: WidgetViewModel
|
|
||||||
}
|
|
||||||
|
|
||||||
public override fun onCreate(icicle: Bundle?) {
|
public override fun onCreate(icicle: Bundle?) {
|
||||||
super.onCreate(icicle)
|
super.onCreate(icicle)
|
||||||
|
binding = CurrencyAppWidgetConfigureBinding.inflate(layoutInflater)
|
||||||
|
setContentView(binding.root)
|
||||||
|
|
||||||
// Set the result to CANCELED. This will cause the widget host to cancel
|
// Set the result to CANCELED. This will cause the widget host to cancel
|
||||||
// out of the widget placement if the user presses the back button.
|
// out of the widget placement if the user presses the back button.
|
||||||
@@ -57,23 +51,20 @@ class CurrencyAppWidgetConfigureActivityKotlin : AppCompatActivity(), KodeinAwar
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// ViewModel setup
|
|
||||||
viewModel = ViewModelProviders.of(this, factory).get(WidgetViewModel::class.java)
|
|
||||||
viewModel.initiate(mAppWidgetId)
|
viewModel.initiate(mAppWidgetId)
|
||||||
|
|
||||||
setupDataBinding()
|
|
||||||
setupObserver()
|
setupObserver()
|
||||||
setupClickListener()
|
setupClickListener()
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun setupClickListener() {
|
private fun setupClickListener() {
|
||||||
submit_widget.setOnClickListener(this)
|
binding.submitWidget.setOnClickListener(this)
|
||||||
currency_one.setOnClickListener(this)
|
binding.currencyOne.setOnClickListener(this)
|
||||||
currency_two.setOnClickListener(this)
|
binding.currencyTwo.setOnClickListener(this)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun setupObserver() {
|
private fun setupObserver() {
|
||||||
viewModel.operationFinishedListener.observe(this, {
|
viewModel.operationFinishedListener.observe(this) {
|
||||||
|
|
||||||
// it.first is a the success of the operation
|
// it.first is a the success of the operation
|
||||||
if (it.first) {
|
if (it.first) {
|
||||||
@@ -82,17 +73,6 @@ class CurrencyAppWidgetConfigureActivityKotlin : AppCompatActivity(), KodeinAwar
|
|||||||
// failed operation - display toast with message from it.second
|
// failed operation - display toast with message from it.second
|
||||||
it.second?.let { message -> displayToast(message) }
|
it.second?.let { message -> displayToast(message) }
|
||||||
}
|
}
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun setupDataBinding() {
|
|
||||||
// data binding to @R.layout.currency_app_widget_configure
|
|
||||||
DataBindingUtil.setContentView<CurrencyAppWidgetConfigureBinding>(
|
|
||||||
this,
|
|
||||||
R.layout.currency_app_widget_configure
|
|
||||||
).apply {
|
|
||||||
viewmodel = viewModel
|
|
||||||
lifecycleOwner = this@CurrencyAppWidgetConfigureActivityKotlin
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -4,7 +4,8 @@ import android.app.Dialog
|
|||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import com.appttude.h_mal.easycc.R
|
import com.appttude.h_mal.easycc.R
|
||||||
import kotlinx.android.synthetic.main.confirm_dialog.*
|
import com.appttude.h_mal.easycc.databinding.ActivityMainBinding
|
||||||
|
import com.appttude.h_mal.easycc.databinding.ConfirmDialogBinding
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -17,19 +18,22 @@ class WidgetSubmitDialog(
|
|||||||
private val dialogInterface: DialogSubmit
|
private val dialogInterface: DialogSubmit
|
||||||
) : Dialog(context) {
|
) : Dialog(context) {
|
||||||
|
|
||||||
|
private lateinit var binding: ConfirmDialogBinding
|
||||||
|
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
setContentView(R.layout.confirm_dialog)
|
binding = ConfirmDialogBinding.inflate(layoutInflater)
|
||||||
|
setContentView(binding.root)
|
||||||
// layer behind dialog to be transparent
|
// layer behind dialog to be transparent
|
||||||
window!!.setBackgroundDrawableResource(android.R.color.transparent)
|
window!!.setBackgroundDrawableResource(android.R.color.transparent)
|
||||||
// Dialog cannot be cancelled by clicking away
|
// Dialog cannot be cancelled by clicking away
|
||||||
setCancelable(false)
|
setCancelable(false)
|
||||||
|
|
||||||
confirm_text.text = messageString
|
binding.confirmText.text = messageString
|
||||||
|
|
||||||
// handle dialog buttons
|
// handle dialog buttons
|
||||||
confirm_yes.setOnClickListener { dialogInterface.onSubmit() }
|
binding.confirmYes.setOnClickListener { dialogInterface.onSubmit() }
|
||||||
confirm_no.setOnClickListener { dismiss() }
|
binding.confirmNo.setOnClickListener { dismiss() }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -4,8 +4,11 @@ import androidx.lifecycle.MutableLiveData
|
|||||||
import androidx.lifecycle.ViewModel
|
import androidx.lifecycle.ViewModel
|
||||||
import com.appttude.h_mal.easycc.data.repository.Repository
|
import com.appttude.h_mal.easycc.data.repository.Repository
|
||||||
import com.appttude.h_mal.easycc.utils.trimToThree
|
import com.appttude.h_mal.easycc.utils.trimToThree
|
||||||
|
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||||
|
import javax.inject.Inject
|
||||||
|
|
||||||
class WidgetViewModel(
|
@HiltViewModel
|
||||||
|
class WidgetViewModel @Inject constructor(
|
||||||
private val repository: Repository
|
private val repository: Repository
|
||||||
) : ViewModel() {
|
) : ViewModel() {
|
||||||
|
|
||||||
|
|||||||
@@ -1,15 +0,0 @@
|
|||||||
package com.appttude.h_mal.easycc.ui.widget
|
|
||||||
|
|
||||||
import androidx.lifecycle.ViewModel
|
|
||||||
import androidx.lifecycle.ViewModelProvider
|
|
||||||
import com.appttude.h_mal.easycc.data.repository.RepositoryImpl
|
|
||||||
|
|
||||||
@Suppress("UNCHECKED_CAST")
|
|
||||||
class WidgetViewModelFactory(
|
|
||||||
private val repository: RepositoryImpl
|
|
||||||
) : ViewModelProvider.NewInstanceFactory() {
|
|
||||||
|
|
||||||
override fun <T : ViewModel?> create(modelClass: Class<T>): T {
|
|
||||||
return WidgetViewModel(repository) as T
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -6,21 +6,19 @@ import android.content.Context
|
|||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import com.appttude.h_mal.easycc.helper.WidgetHelper
|
import com.appttude.h_mal.easycc.helper.WidgetHelper
|
||||||
import com.appttude.h_mal.easycc.widget.WidgetServiceIntent.Companion.enqueueWork
|
import com.appttude.h_mal.easycc.widget.WidgetServiceIntent.Companion.enqueueWork
|
||||||
import org.kodein.di.KodeinAware
|
import dagger.hilt.android.AndroidEntryPoint
|
||||||
import org.kodein.di.LateInitKodein
|
import javax.inject.Inject
|
||||||
import org.kodein.di.generic.instance
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Implementation of App Widget functionality.
|
* Implementation of App Widget functionality.
|
||||||
* App Widget Configuration implemented in [CurrencyAppWidgetKotlin]
|
* App Widget Configuration implemented in [CurrencyAppWidgetKotlin]
|
||||||
*/
|
*/
|
||||||
|
@AndroidEntryPoint
|
||||||
class CurrencyAppWidgetKotlin : AppWidgetProvider() {
|
class CurrencyAppWidgetKotlin : AppWidgetProvider() {
|
||||||
|
|
||||||
//DI with kodein to use in CurrencyAppWidgetKotlin
|
@Inject
|
||||||
private val kodein = LateInitKodein()
|
lateinit var helper: WidgetHelper
|
||||||
private val repository: WidgetHelper by kodein.instance()
|
|
||||||
|
|
||||||
//update trigger either on timed update or from from first start
|
//update trigger either on timed update or from from first start
|
||||||
override fun onUpdate(
|
override fun onUpdate(
|
||||||
@@ -33,10 +31,9 @@ class CurrencyAppWidgetKotlin : AppWidgetProvider() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun onDeleted(context: Context, appWidgetIds: IntArray) {
|
override fun onDeleted(context: Context, appWidgetIds: IntArray) {
|
||||||
kodein.baseKodein = (context.applicationContext as KodeinAware).kodein
|
|
||||||
// When the user deletes the widget, delete the preference associated with it.
|
// When the user deletes the widget, delete the preference associated with it.
|
||||||
for (appWidgetId in appWidgetIds) {
|
for (appWidgetId in appWidgetIds) {
|
||||||
repository.removeWidgetData(appWidgetId)
|
helper.removeWidgetData(appWidgetId)
|
||||||
}
|
}
|
||||||
super.onDeleted(context, appWidgetIds)
|
super.onDeleted(context, appWidgetIds)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
package com.appttude.h_mal.easycc.widget
|
package com.appttude.h_mal.easycc.widget
|
||||||
|
|
||||||
import android.app.PendingIntent
|
import android.app.PendingIntent
|
||||||
|
import android.app.PendingIntent.FLAG_IMMUTABLE
|
||||||
|
import android.app.PendingIntent.FLAG_UPDATE_CURRENT
|
||||||
import android.appwidget.AppWidgetManager
|
import android.appwidget.AppWidgetManager
|
||||||
import android.content.ComponentName
|
import android.content.ComponentName
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
@@ -11,22 +13,20 @@ import com.appttude.h_mal.easycc.R
|
|||||||
import com.appttude.h_mal.easycc.helper.WidgetHelper
|
import com.appttude.h_mal.easycc.helper.WidgetHelper
|
||||||
import com.appttude.h_mal.easycc.ui.main.MainActivity
|
import com.appttude.h_mal.easycc.ui.main.MainActivity
|
||||||
import com.appttude.h_mal.easycc.utils.transformIntToArray
|
import com.appttude.h_mal.easycc.utils.transformIntToArray
|
||||||
|
import dagger.hilt.android.AndroidEntryPoint
|
||||||
import kotlinx.coroutines.CoroutineScope
|
import kotlinx.coroutines.CoroutineScope
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import org.kodein.di.KodeinAware
|
import javax.inject.Inject
|
||||||
import org.kodein.di.LateInitKodein
|
|
||||||
import org.kodein.di.generic.instance
|
|
||||||
|
|
||||||
|
@AndroidEntryPoint
|
||||||
class WidgetServiceIntent : JobIntentService() {
|
class WidgetServiceIntent : JobIntentService() {
|
||||||
|
|
||||||
//DI with kodein to use in CurrencyAppWidgetKotlin
|
//DI with kodein to use in CurrencyAppWidgetKotlin
|
||||||
private val kodein = LateInitKodein()
|
@Inject
|
||||||
private val repository: WidgetHelper by kodein.instance()
|
lateinit var helper: WidgetHelper
|
||||||
|
|
||||||
override fun onHandleWork(intent: Intent) {
|
override fun onHandleWork(intent: Intent) {
|
||||||
kodein.baseKodein = (application as KodeinAware).kodein
|
|
||||||
|
|
||||||
val appWidgetManager = AppWidgetManager.getInstance(this)
|
val appWidgetManager = AppWidgetManager.getInstance(this)
|
||||||
val thisAppWidget = ComponentName(packageName, CurrencyAppWidgetKotlin::class.java.name)
|
val thisAppWidget = ComponentName(packageName, CurrencyAppWidgetKotlin::class.java.name)
|
||||||
val appWidgetIds = appWidgetManager.getAppWidgetIds(thisAppWidget)
|
val appWidgetIds = appWidgetManager.getAppWidgetIds(thisAppWidget)
|
||||||
@@ -45,7 +45,7 @@ class WidgetServiceIntent : JobIntentService() {
|
|||||||
val views = RemoteViews(context.packageName, R.layout.currency_app_widget)
|
val views = RemoteViews(context.packageName, R.layout.currency_app_widget)
|
||||||
|
|
||||||
CoroutineScope(Dispatchers.Main).launch {
|
CoroutineScope(Dispatchers.Main).launch {
|
||||||
val exchangeResponse = repository.getWidgetData()
|
val exchangeResponse = helper.getWidgetData()
|
||||||
|
|
||||||
exchangeResponse?.let {
|
exchangeResponse?.let {
|
||||||
val titleString = "${it.from}${it.to}"
|
val titleString = "${it.from}${it.to}"
|
||||||
@@ -63,7 +63,7 @@ class WidgetServiceIntent : JobIntentService() {
|
|||||||
val configPendingIntent =
|
val configPendingIntent =
|
||||||
PendingIntent.getActivity(
|
PendingIntent.getActivity(
|
||||||
context, appWidgetId, clickIntentTemplate,
|
context, appWidgetId, clickIntentTemplate,
|
||||||
PendingIntent.FLAG_UPDATE_CURRENT
|
PendingIntent.FLAG_UPDATE_CURRENT or FLAG_IMMUTABLE
|
||||||
)
|
)
|
||||||
views.setOnClickPendingIntent(R.id.widget_view, configPendingIntent)
|
views.setOnClickPendingIntent(R.id.widget_view, configPendingIntent)
|
||||||
}
|
}
|
||||||
@@ -87,14 +87,14 @@ class WidgetServiceIntent : JobIntentService() {
|
|||||||
context,
|
context,
|
||||||
appWidgetId,
|
appWidgetId,
|
||||||
updateIntent,
|
updateIntent,
|
||||||
PendingIntent.FLAG_UPDATE_CURRENT
|
FLAG_UPDATE_CURRENT or FLAG_IMMUTABLE
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun clickingIntent(
|
private fun clickingIntent(
|
||||||
context: Context
|
context: Context
|
||||||
): Intent {
|
): Intent {
|
||||||
val pair = repository.repository.getConversionPair()
|
val pair = helper.repository.getConversionPair()
|
||||||
val s1 = pair.first
|
val s1 = pair.first
|
||||||
val s2 = pair.second
|
val s2 = pair.second
|
||||||
return Intent(context, MainActivity::class.java).apply {
|
return Intent(context, MainActivity::class.java).apply {
|
||||||
|
|||||||
@@ -1,126 +1,113 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<layout xmlns:android="http://schemas.android.com/apk/res/android"
|
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
xmlns:tools="http://schemas.android.com/tools">
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:background="@drawable/ic_background"
|
||||||
|
android:focusable="false"
|
||||||
|
android:focusableInTouchMode="true"
|
||||||
|
android:orientation="vertical"
|
||||||
|
tools:context=".ui.main.MainActivity">
|
||||||
|
|
||||||
<data>
|
<RelativeLayout
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_margin="12dp"
|
||||||
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
|
app:layout_constraintLeft_toLeftOf="parent"
|
||||||
|
app:layout_constraintRight_toRightOf="parent"
|
||||||
|
app:layout_constraintTop_toTopOf="parent">
|
||||||
|
|
||||||
<variable
|
<LinearLayout
|
||||||
name="viewmodel"
|
android:id="@+id/whole_view"
|
||||||
type="com.appttude.h_mal.easycc.ui.main.MainViewModel" />
|
android:layout_width="match_parent"
|
||||||
</data>
|
|
||||||
|
|
||||||
<androidx.constraintlayout.widget.ConstraintLayout
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="match_parent"
|
|
||||||
android:background="@drawable/ic_background"
|
|
||||||
android:focusable="false"
|
|
||||||
android:focusableInTouchMode="true"
|
|
||||||
android:orientation="vertical"
|
|
||||||
tools:context=".ui.main.MainActivity">
|
|
||||||
|
|
||||||
<RelativeLayout
|
|
||||||
android:layout_width="0dp"
|
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_margin="12dp"
|
android:orientation="vertical">
|
||||||
app:layout_constraintBottom_toBottomOf="parent"
|
|
||||||
app:layout_constraintLeft_toLeftOf="parent"
|
<LinearLayout
|
||||||
app:layout_constraintRight_toRightOf="parent"
|
android:layout_width="match_parent"
|
||||||
app:layout_constraintTop_toTopOf="parent">
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginBottom="18dp"
|
||||||
|
android:orientation="vertical">
|
||||||
|
|
||||||
|
<androidx.cardview.widget.CardView style="@style/cardview_theme">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/currencyOne"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_margin="12dp"
|
||||||
|
android:autoSizeMaxTextSize="12dp"
|
||||||
|
android:tag="top"
|
||||||
|
android:textColor="@color/colour_five"
|
||||||
|
android:textSize="18sp" />
|
||||||
|
</androidx.cardview.widget.CardView>
|
||||||
|
|
||||||
|
|
||||||
|
<EditText
|
||||||
|
android:id="@+id/topInsertValue"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="6dp"
|
||||||
|
android:layout_marginBottom="6dp"
|
||||||
|
android:background="@drawable/round_edit_text"
|
||||||
|
android:ems="10"
|
||||||
|
android:hint="insert value one"
|
||||||
|
android:inputType="numberDecimal"
|
||||||
|
android:padding="12dp"
|
||||||
|
android:selectAllOnFocus="true"
|
||||||
|
android:tag="from"
|
||||||
|
android:textColorHighlight="#608d91" />
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
<LinearLayout
|
<LinearLayout
|
||||||
android:id="@+id/whole_view"
|
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:orientation="vertical">
|
android:orientation="vertical">
|
||||||
|
|
||||||
<LinearLayout
|
<androidx.cardview.widget.CardView style="@style/cardview_theme">
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginBottom="18dp"
|
|
||||||
android:orientation="vertical">
|
|
||||||
|
|
||||||
<androidx.cardview.widget.CardView style="@style/cardview_theme">
|
<TextView
|
||||||
|
android:id="@+id/currencyTwo"
|
||||||
<TextView
|
|
||||||
android:id="@+id/currency_one"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_margin="12dp"
|
|
||||||
android:autoSizeMaxTextSize="12dp"
|
|
||||||
android:tag="top"
|
|
||||||
android:text="@={viewmodel.rateIdFrom}"
|
|
||||||
android:textColor="@color/colour_five"
|
|
||||||
android:textSize="18sp" />
|
|
||||||
</androidx.cardview.widget.CardView>
|
|
||||||
|
|
||||||
|
|
||||||
<EditText
|
|
||||||
android:id="@+id/topInsertValue"
|
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_marginTop="6dp"
|
android:layout_margin="12dp"
|
||||||
android:layout_marginBottom="6dp"
|
android:tag="bottom"
|
||||||
android:background="@drawable/round_edit_text"
|
android:textColor="@color/colour_five"
|
||||||
android:ems="10"
|
android:textSize="18sp" />
|
||||||
android:hint="insert value one"
|
</androidx.cardview.widget.CardView>
|
||||||
android:inputType="numberDecimal"
|
|
||||||
android:padding="12dp"
|
|
||||||
android:selectAllOnFocus="true"
|
|
||||||
android:tag="from"
|
|
||||||
android:textColorHighlight="#608d91" />
|
|
||||||
|
|
||||||
</LinearLayout>
|
|
||||||
|
|
||||||
<LinearLayout
|
<EditText
|
||||||
|
android:id="@+id/bottomInsertValues"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:orientation="vertical">
|
android:layout_marginTop="6dp"
|
||||||
|
android:layout_weight="7"
|
||||||
<androidx.cardview.widget.CardView style="@style/cardview_theme">
|
android:background="@drawable/round_edit_text"
|
||||||
|
android:ems="10"
|
||||||
<TextView
|
android:hint="insert value two"
|
||||||
android:id="@+id/currency_two"
|
android:inputType="numberDecimal"
|
||||||
android:layout_width="match_parent"
|
android:padding="12dp"
|
||||||
android:layout_height="wrap_content"
|
android:selectAllOnFocus="true"
|
||||||
android:layout_margin="12dp"
|
android:tag="to"
|
||||||
android:tag="bottom"
|
android:textColorHighlight="#608d91" />
|
||||||
android:text="@={viewmodel.rateIdTo}"
|
|
||||||
android:textColor="@color/colour_five"
|
|
||||||
android:textSize="18sp" />
|
|
||||||
</androidx.cardview.widget.CardView>
|
|
||||||
|
|
||||||
|
|
||||||
<EditText
|
|
||||||
android:id="@+id/bottomInsertValues"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginTop="6dp"
|
|
||||||
android:layout_weight="7"
|
|
||||||
android:background="@drawable/round_edit_text"
|
|
||||||
android:ems="10"
|
|
||||||
android:hint="insert value two"
|
|
||||||
android:inputType="numberDecimal"
|
|
||||||
android:padding="12dp"
|
|
||||||
android:selectAllOnFocus="true"
|
|
||||||
android:tag="to"
|
|
||||||
android:textColorHighlight="#608d91" />
|
|
||||||
|
|
||||||
</LinearLayout>
|
|
||||||
|
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|
||||||
<ProgressBar
|
</LinearLayout>
|
||||||
android:id="@+id/progressBar"
|
|
||||||
style="?android:attr/progressBarStyle"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_centerInParent="true"
|
|
||||||
android:indeterminate="true"
|
|
||||||
android:indeterminateTint="@color/colour_four"
|
|
||||||
android:indeterminateTintMode="src_atop"
|
|
||||||
android:visibility="gone" />
|
|
||||||
</RelativeLayout>
|
|
||||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
|
||||||
|
|
||||||
</layout>
|
<ProgressBar
|
||||||
|
android:id="@+id/progressBar"
|
||||||
|
style="?android:attr/progressBarStyle"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_centerInParent="true"
|
||||||
|
android:indeterminate="true"
|
||||||
|
android:indeterminateTint="@color/colour_four"
|
||||||
|
android:indeterminateTintMode="src_atop"
|
||||||
|
android:visibility="gone" />
|
||||||
|
</RelativeLayout>
|
||||||
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||||
|
|||||||
@@ -1,128 +1,115 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<layout xmlns:android="http://schemas.android.com/apk/res/android"
|
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
xmlns:tools="http://schemas.android.com/tools">
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:background="@drawable/ic_background"
|
||||||
|
android:focusable="false"
|
||||||
|
android:focusableInTouchMode="true"
|
||||||
|
android:orientation="vertical"
|
||||||
|
tools:context=".ui.main.MainActivity">
|
||||||
|
|
||||||
<data>
|
<LinearLayout
|
||||||
|
android:layout_width="0dp"
|
||||||
<variable
|
android:layout_height="wrap_content"
|
||||||
name="viewmodel"
|
android:layout_marginBottom="9dp"
|
||||||
type="com.appttude.h_mal.easycc.ui.main.MainViewModel" />
|
|
||||||
</data>
|
|
||||||
|
|
||||||
<androidx.constraintlayout.widget.ConstraintLayout
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="match_parent"
|
|
||||||
android:background="@drawable/ic_background"
|
|
||||||
android:focusable="false"
|
|
||||||
android:focusableInTouchMode="true"
|
|
||||||
android:orientation="vertical"
|
android:orientation="vertical"
|
||||||
tools:context=".ui.main.MainActivity">
|
app:layout_constraintBottom_toTopOf="@id/middle"
|
||||||
|
app:layout_constraintLeft_toLeftOf="parent"
|
||||||
|
app:layout_constraintRight_toRightOf="parent"
|
||||||
|
app:layout_constraintWidth_percent=".9">
|
||||||
|
|
||||||
<LinearLayout
|
<androidx.cardview.widget.CardView style="@style/cardview_theme">
|
||||||
android:layout_width="0dp"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
app:layout_constraintWidth_percent=".9"
|
|
||||||
android:layout_marginBottom="9dp"
|
|
||||||
android:orientation="vertical"
|
|
||||||
app:layout_constraintBottom_toTopOf="@id/middle"
|
|
||||||
app:layout_constraintLeft_toLeftOf="parent"
|
|
||||||
app:layout_constraintRight_toRightOf="parent">
|
|
||||||
|
|
||||||
<androidx.cardview.widget.CardView style="@style/cardview_theme">
|
<TextView
|
||||||
|
android:id="@+id/currencyOne"
|
||||||
<TextView
|
|
||||||
android:id="@+id/currency_one"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_margin="12dp"
|
|
||||||
android:tag="top"
|
|
||||||
android:text="@={viewmodel.rateIdFrom}"
|
|
||||||
android:textColor="@color/colour_five"
|
|
||||||
android:textSize="18sp" />
|
|
||||||
</androidx.cardview.widget.CardView>
|
|
||||||
|
|
||||||
|
|
||||||
<EditText
|
|
||||||
android:id="@+id/topInsertValue"
|
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_marginTop="6dp"
|
android:layout_margin="12dp"
|
||||||
android:layout_marginBottom="6dp"
|
android:tag="top"
|
||||||
android:background="@drawable/round_edit_text"
|
android:textColor="@color/colour_five"
|
||||||
android:ems="10"
|
android:textSize="18sp" />
|
||||||
android:hint="insert value one"
|
</androidx.cardview.widget.CardView>
|
||||||
android:inputType="numberDecimal"
|
|
||||||
android:padding="12dp"
|
|
||||||
android:selectAllOnFocus="true"
|
|
||||||
android:tag="from"
|
|
||||||
android:textColorHighlight="#608d91" />
|
|
||||||
|
|
||||||
</LinearLayout>
|
|
||||||
|
|
||||||
|
|
||||||
<android.widget.Space
|
<EditText
|
||||||
android:id="@+id/middle"
|
android:id="@+id/topInsertValue"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="1dp"
|
|
||||||
app:layout_constraintBottom_toBottomOf="parent"
|
|
||||||
app:layout_constraintTop_toTopOf="parent" />
|
|
||||||
|
|
||||||
|
|
||||||
<LinearLayout
|
|
||||||
android:layout_width="0dp"
|
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
app:layout_constraintWidth_percent=".9"
|
android:layout_marginTop="6dp"
|
||||||
android:layout_marginTop="9dp"
|
android:layout_marginBottom="6dp"
|
||||||
android:orientation="vertical"
|
android:background="@drawable/round_edit_text"
|
||||||
app:layout_constraintLeft_toLeftOf="parent"
|
android:ems="10"
|
||||||
app:layout_constraintRight_toRightOf="parent"
|
android:hint="insert value one"
|
||||||
app:layout_constraintTop_toBottomOf="@id/middle">
|
android:inputType="numberDecimal"
|
||||||
|
android:padding="12dp"
|
||||||
|
android:selectAllOnFocus="true"
|
||||||
|
android:tag="from"
|
||||||
|
android:textColorHighlight="#608d91" />
|
||||||
|
|
||||||
<androidx.cardview.widget.CardView style="@style/cardview_theme">
|
</LinearLayout>
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:id="@+id/currency_two"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_margin="12dp"
|
|
||||||
android:tag="bottom"
|
|
||||||
android:text="@={viewmodel.rateIdTo}"
|
|
||||||
android:textColor="@color/colour_five"
|
|
||||||
android:textSize="18sp" />
|
|
||||||
</androidx.cardview.widget.CardView>
|
|
||||||
|
|
||||||
|
|
||||||
<EditText
|
<android.widget.Space
|
||||||
android:id="@+id/bottomInsertValues"
|
android:id="@+id/middle"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="1dp"
|
||||||
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
|
app:layout_constraintTop_toTopOf="parent" />
|
||||||
|
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="9dp"
|
||||||
|
android:orientation="vertical"
|
||||||
|
app:layout_constraintLeft_toLeftOf="parent"
|
||||||
|
app:layout_constraintRight_toRightOf="parent"
|
||||||
|
app:layout_constraintTop_toBottomOf="@id/middle"
|
||||||
|
app:layout_constraintWidth_percent=".9">
|
||||||
|
|
||||||
|
<androidx.cardview.widget.CardView style="@style/cardview_theme">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/currencyTwo"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_marginTop="6dp"
|
android:layout_margin="12dp"
|
||||||
android:layout_weight="7"
|
android:tag="bottom"
|
||||||
android:background="@drawable/round_edit_text"
|
android:textColor="@color/colour_five"
|
||||||
android:ems="10"
|
android:textSize="18sp" />
|
||||||
android:hint="insert value two"
|
</androidx.cardview.widget.CardView>
|
||||||
android:inputType="numberDecimal"
|
|
||||||
android:padding="12dp"
|
|
||||||
android:selectAllOnFocus="true"
|
|
||||||
android:tag="to"
|
|
||||||
android:textColorHighlight="#608d91" />
|
|
||||||
|
|
||||||
</LinearLayout>
|
|
||||||
|
|
||||||
<ProgressBar
|
<EditText
|
||||||
android:id="@+id/progressBar"
|
android:id="@+id/bottomInsertValues"
|
||||||
style="?android:attr/progressBarStyle"
|
android:layout_width="match_parent"
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:indeterminate="true"
|
android:layout_marginTop="6dp"
|
||||||
android:indeterminateTint="@color/colour_four"
|
android:layout_weight="7"
|
||||||
android:indeterminateTintMode="src_atop"
|
android:background="@drawable/round_edit_text"
|
||||||
android:visibility="gone"
|
android:ems="10"
|
||||||
app:layout_constraintBottom_toBottomOf="parent"
|
android:hint="insert value two"
|
||||||
app:layout_constraintLeft_toLeftOf="parent"
|
android:inputType="numberDecimal"
|
||||||
app:layout_constraintRight_toRightOf="parent"
|
android:padding="12dp"
|
||||||
app:layout_constraintTop_toTopOf="parent" />
|
android:selectAllOnFocus="true"
|
||||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
android:tag="to"
|
||||||
|
android:textColorHighlight="#608d91" />
|
||||||
|
|
||||||
</layout>
|
</LinearLayout>
|
||||||
|
|
||||||
|
<ProgressBar
|
||||||
|
android:id="@+id/progressBar"
|
||||||
|
style="?android:attr/progressBarStyle"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:indeterminate="true"
|
||||||
|
android:indeterminateTint="@color/colour_four"
|
||||||
|
android:indeterminateTintMode="src_atop"
|
||||||
|
android:visibility="gone"
|
||||||
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
|
app:layout_constraintLeft_toLeftOf="parent"
|
||||||
|
app:layout_constraintRight_toRightOf="parent"
|
||||||
|
app:layout_constraintTop_toTopOf="parent" />
|
||||||
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||||
|
|||||||
@@ -19,7 +19,7 @@
|
|||||||
android:orientation="vertical">
|
android:orientation="vertical">
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
android:id="@+id/confirm_text"
|
android:id="@+id/confirmText"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_marginTop="12dp"
|
android:layout_marginTop="12dp"
|
||||||
@@ -36,7 +36,7 @@
|
|||||||
android:orientation="horizontal">
|
android:orientation="horizontal">
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
android:id="@+id/confirm_yes"
|
android:id="@+id/confirmYes"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_marginTop="8dp"
|
android:layout_marginTop="8dp"
|
||||||
@@ -48,7 +48,7 @@
|
|||||||
android:textColor="@color/colour_five" />
|
android:textColor="@color/colour_five" />
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
android:id="@+id/confirm_no"
|
android:id="@+id/confirmNo"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_margin="8dp"
|
android:layout_margin="8dp"
|
||||||
|
|||||||
@@ -1,83 +1,68 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<layout xmlns:android="http://schemas.android.com/apk/res/android"
|
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
xmlns:tools="http://schemas.android.com/tools">
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
|
android:layout_width="match_parent"
|
||||||
<data>
|
android:layout_height="match_parent"
|
||||||
|
android:focusable="false"
|
||||||
<variable
|
android:focusableInTouchMode="true"
|
||||||
name="viewmodel"
|
android:orientation="vertical"
|
||||||
type="com.appttude.h_mal.easycc.ui.widget.WidgetViewModel" />
|
tools:context=".ui.widget.CurrencyAppWidgetConfigureActivityKotlin">
|
||||||
</data>
|
|
||||||
|
|
||||||
<RelativeLayout
|
<RelativeLayout
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="wrap_content"
|
||||||
android:orientation="vertical"
|
android:layout_centerInParent="true"
|
||||||
android:focusable="false"
|
android:layout_margin="12dp">
|
||||||
android:focusableInTouchMode="true"
|
|
||||||
tools:context=".ui.widget.CurrencyAppWidgetConfigureActivityKotlin">
|
|
||||||
|
|
||||||
<RelativeLayout
|
<LinearLayout
|
||||||
|
android:id="@+id/whole_view"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_centerInParent="true"
|
android:orientation="vertical">
|
||||||
android:layout_margin="12dp">
|
|
||||||
|
|
||||||
<LinearLayout
|
<androidx.cardview.widget.CardView
|
||||||
android:layout_width="match_parent"
|
style="@style/cardview_theme"
|
||||||
android:layout_height="wrap_content"
|
android:layout_margin="11dp">
|
||||||
android:orientation="vertical"
|
|
||||||
android:id="@+id/whole_view">
|
|
||||||
|
|
||||||
<androidx.cardview.widget.CardView
|
<TextView
|
||||||
style="@style/cardview_theme"
|
android:id="@+id/currencyOne"
|
||||||
android:layout_margin="11dp">
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
<TextView
|
android:layout_margin="12dp"
|
||||||
android:id="@+id/currency_one"
|
android:tag="top"
|
||||||
android:layout_width="match_parent"
|
android:textColor="@color/colour_five"
|
||||||
android:layout_height="wrap_content"
|
android:textSize="18sp"
|
||||||
android:layout_margin="12dp"
|
tools:text="Currency One" />
|
||||||
android:tag="top"
|
</androidx.cardview.widget.CardView>
|
||||||
android:text="@={viewmodel.rateIdFrom}"
|
|
||||||
android:textColor="@color/colour_five"
|
|
||||||
android:textSize="18sp"
|
|
||||||
tools:text="Currency One" />
|
|
||||||
</androidx.cardview.widget.CardView>
|
|
||||||
|
|
||||||
|
|
||||||
<androidx.cardview.widget.CardView
|
<androidx.cardview.widget.CardView
|
||||||
style="@style/cardview_theme"
|
style="@style/cardview_theme"
|
||||||
android:layout_margin="11dp">
|
android:layout_margin="11dp">
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
android:id="@+id/currency_two"
|
android:id="@+id/currencyTwo"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_margin="12dp"
|
android:layout_margin="12dp"
|
||||||
android:tag="bottom"
|
android:tag="bottom"
|
||||||
android:text="@={viewmodel.rateIdTo}"
|
android:textColor="@color/colour_five"
|
||||||
android:textColor="@color/colour_five"
|
android:textSize="18sp"
|
||||||
android:textSize="18sp"
|
tools:text="Currency Two" />
|
||||||
tools:text="Currency Two" />
|
</androidx.cardview.widget.CardView>
|
||||||
</androidx.cardview.widget.CardView>
|
|
||||||
|
|
||||||
</LinearLayout>
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:layout_marginEnd="22dp"
|
|
||||||
android:id="@+id/submit_widget"
|
|
||||||
android:tag="submit"
|
|
||||||
android:padding="12dp"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_below="@id/whole_view"
|
|
||||||
android:layout_alignParentEnd="true"
|
|
||||||
android:textColor="@color/colour_five"
|
|
||||||
android:text="Submit" />
|
|
||||||
|
|
||||||
</RelativeLayout>
|
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/submitWidget"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_below="@id/whole_view"
|
||||||
|
android:layout_alignParentEnd="true"
|
||||||
|
android:layout_marginEnd="22dp"
|
||||||
|
android:padding="12dp"
|
||||||
|
android:tag="submit"
|
||||||
|
android:text="Submit"
|
||||||
|
android:textColor="@color/colour_five" />
|
||||||
</RelativeLayout>
|
</RelativeLayout>
|
||||||
</layout>
|
</RelativeLayout>
|
||||||
|
|||||||
@@ -6,7 +6,9 @@ import androidx.arch.core.executor.testing.InstantTaskExecutorRule
|
|||||||
import com.appttude.h_mal.easycc.data.network.response.ResponseObject
|
import com.appttude.h_mal.easycc.data.network.response.ResponseObject
|
||||||
import com.appttude.h_mal.easycc.data.repository.Repository
|
import com.appttude.h_mal.easycc.data.repository.Repository
|
||||||
import com.appttude.h_mal.easycc.helper.CurrencyDataHelper
|
import com.appttude.h_mal.easycc.helper.CurrencyDataHelper
|
||||||
|
import com.appttude.h_mal.easycc.utils.MainCoroutineRule
|
||||||
import com.appttude.h_mal.easycc.utils.observeOnce
|
import com.appttude.h_mal.easycc.utils.observeOnce
|
||||||
|
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||||
import kotlinx.coroutines.runBlocking
|
import kotlinx.coroutines.runBlocking
|
||||||
import org.junit.Assert.*
|
import org.junit.Assert.*
|
||||||
import org.junit.Before
|
import org.junit.Before
|
||||||
@@ -23,6 +25,9 @@ class MainViewModelTest {
|
|||||||
@get:Rule
|
@get:Rule
|
||||||
val instantTaskExecutorRule: InstantTaskExecutorRule = InstantTaskExecutorRule()
|
val instantTaskExecutorRule: InstantTaskExecutorRule = InstantTaskExecutorRule()
|
||||||
|
|
||||||
|
@get:Rule
|
||||||
|
var mainCoroutineRule = MainCoroutineRule()
|
||||||
|
|
||||||
lateinit var viewModel: MainViewModel
|
lateinit var viewModel: MainViewModel
|
||||||
|
|
||||||
@Mock
|
@Mock
|
||||||
|
|||||||
@@ -0,0 +1,22 @@
|
|||||||
|
package com.appttude.h_mal.easycc.utils
|
||||||
|
|
||||||
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||||
|
import kotlinx.coroutines.test.*
|
||||||
|
import org.junit.rules.TestWatcher
|
||||||
|
import org.junit.runner.Description
|
||||||
|
|
||||||
|
@ExperimentalCoroutinesApi
|
||||||
|
class MainCoroutineRule(private val dispatcher: TestDispatcher = StandardTestDispatcher()) :
|
||||||
|
TestWatcher() {
|
||||||
|
|
||||||
|
override fun starting(description: Description) {
|
||||||
|
super.starting(description)
|
||||||
|
Dispatchers.setMain(dispatcher)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun finished(description: Description) {
|
||||||
|
super.finished(description)
|
||||||
|
Dispatchers.resetMain()
|
||||||
|
}
|
||||||
|
}
|
||||||
26
build.gradle
26
build.gradle
@@ -1,25 +1,9 @@
|
|||||||
// Top-level build file where you can add configuration options common to all sub-projects/modules.
|
// Top-level build file where you can add configuration options common to all sub-projects/modules.
|
||||||
|
plugins {
|
||||||
buildscript {
|
id 'com.android.application' version '7.2.2' apply false
|
||||||
ext.kotlin_version = '1.4.10'
|
id 'com.android.library' version '7.2.2' apply false
|
||||||
|
id 'org.jetbrains.kotlin.android' version '1.7.10' apply false
|
||||||
repositories {
|
id 'com.google.dagger.hilt.android' version '2.43.2' apply false
|
||||||
jcenter()
|
|
||||||
google()
|
|
||||||
}
|
|
||||||
dependencies {
|
|
||||||
classpath 'com.android.tools.build:gradle:4.0.2'
|
|
||||||
|
|
||||||
// NOTE: Do not place your application dependencies here; they belong
|
|
||||||
// in the individual module build.gradle files
|
|
||||||
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" }
|
|
||||||
}
|
|
||||||
|
|
||||||
allprojects {
|
|
||||||
repositories {
|
|
||||||
jcenter()
|
|
||||||
google()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
task clean(type: Delete) {
|
task clean(type: Delete) {
|
||||||
|
|||||||
6
gradle/wrapper/gradle-wrapper.properties
vendored
6
gradle/wrapper/gradle-wrapper.properties
vendored
@@ -1,6 +1,6 @@
|
|||||||
#Sat Jun 12 22:27:25 BST 2021
|
#Thu Aug 04 22:17:29 BST 2022
|
||||||
distributionBase=GRADLE_USER_HOME
|
distributionBase=GRADLE_USER_HOME
|
||||||
|
distributionUrl=https\://services.gradle.org/distributions/gradle-7.3.3-bin.zip
|
||||||
distributionPath=wrapper/dists
|
distributionPath=wrapper/dists
|
||||||
zipStoreBase=GRADLE_USER_HOME
|
|
||||||
zipStorePath=wrapper/dists
|
zipStorePath=wrapper/dists
|
||||||
distributionUrl=https\://services.gradle.org/distributions/gradle-6.7.1-all.zip
|
zipStoreBase=GRADLE_USER_HOME
|
||||||
|
|||||||
@@ -1 +1,16 @@
|
|||||||
|
pluginManagement {
|
||||||
|
repositories {
|
||||||
|
gradlePluginPortal()
|
||||||
|
google()
|
||||||
|
mavenCentral()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
dependencyResolutionManagement {
|
||||||
|
repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
|
||||||
|
repositories {
|
||||||
|
google()
|
||||||
|
mavenCentral()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
rootProject.name = "EasyCC"
|
||||||
include ':app'
|
include ':app'
|
||||||
|
|||||||
Reference in New Issue
Block a user