mirror of
https://github.com/hmalik144/EasyCC_Master.git
synced 2026-01-31 02:41:47 +00:00
New Commit of kotlin version - MVVM, retrofit2, Kodein
This commit is contained in:
BIN
.idea/caches/build_file_checksums.ser
generated
BIN
.idea/caches/build_file_checksums.ser
generated
Binary file not shown.
137
.idea/codeStyles/Project.xml
generated
137
.idea/codeStyles/Project.xml
generated
@@ -1,29 +1,116 @@
|
|||||||
<component name="ProjectCodeStyleConfiguration">
|
<component name="ProjectCodeStyleConfiguration">
|
||||||
<code_scheme name="Project" version="173">
|
<code_scheme name="Project" version="173">
|
||||||
<Objective-C-extensions>
|
<codeStyleSettings language="XML">
|
||||||
<file>
|
<indentOptions>
|
||||||
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Import" />
|
<option name="CONTINUATION_INDENT_SIZE" value="4" />
|
||||||
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Macro" />
|
</indentOptions>
|
||||||
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Typedef" />
|
<arrangement>
|
||||||
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Enum" />
|
<rules>
|
||||||
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Constant" />
|
<section>
|
||||||
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Global" />
|
<rule>
|
||||||
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Struct" />
|
<match>
|
||||||
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="FunctionPredecl" />
|
<AND>
|
||||||
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Function" />
|
<NAME>xmlns:android</NAME>
|
||||||
</file>
|
<XML_ATTRIBUTE />
|
||||||
<class>
|
<XML_NAMESPACE>^$</XML_NAMESPACE>
|
||||||
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Property" />
|
</AND>
|
||||||
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Synthesize" />
|
</match>
|
||||||
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="InitMethod" />
|
</rule>
|
||||||
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="StaticMethod" />
|
</section>
|
||||||
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="InstanceMethod" />
|
<section>
|
||||||
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="DeallocMethod" />
|
<rule>
|
||||||
</class>
|
<match>
|
||||||
<extensions>
|
<AND>
|
||||||
<pair source="cpp" header="h" fileNamingConvention="NONE" />
|
<NAME>xmlns:.*</NAME>
|
||||||
<pair source="c" header="h" fileNamingConvention="NONE" />
|
<XML_ATTRIBUTE />
|
||||||
</extensions>
|
<XML_NAMESPACE>^$</XML_NAMESPACE>
|
||||||
</Objective-C-extensions>
|
</AND>
|
||||||
|
</match>
|
||||||
|
<order>BY_NAME</order>
|
||||||
|
</rule>
|
||||||
|
</section>
|
||||||
|
<section>
|
||||||
|
<rule>
|
||||||
|
<match>
|
||||||
|
<AND>
|
||||||
|
<NAME>.*:id</NAME>
|
||||||
|
<XML_ATTRIBUTE />
|
||||||
|
<XML_NAMESPACE>http://schemas.android.com/apk/res/android</XML_NAMESPACE>
|
||||||
|
</AND>
|
||||||
|
</match>
|
||||||
|
</rule>
|
||||||
|
</section>
|
||||||
|
<section>
|
||||||
|
<rule>
|
||||||
|
<match>
|
||||||
|
<AND>
|
||||||
|
<NAME>.*:name</NAME>
|
||||||
|
<XML_ATTRIBUTE />
|
||||||
|
<XML_NAMESPACE>http://schemas.android.com/apk/res/android</XML_NAMESPACE>
|
||||||
|
</AND>
|
||||||
|
</match>
|
||||||
|
</rule>
|
||||||
|
</section>
|
||||||
|
<section>
|
||||||
|
<rule>
|
||||||
|
<match>
|
||||||
|
<AND>
|
||||||
|
<NAME>name</NAME>
|
||||||
|
<XML_ATTRIBUTE />
|
||||||
|
<XML_NAMESPACE>^$</XML_NAMESPACE>
|
||||||
|
</AND>
|
||||||
|
</match>
|
||||||
|
</rule>
|
||||||
|
</section>
|
||||||
|
<section>
|
||||||
|
<rule>
|
||||||
|
<match>
|
||||||
|
<AND>
|
||||||
|
<NAME>style</NAME>
|
||||||
|
<XML_ATTRIBUTE />
|
||||||
|
<XML_NAMESPACE>^$</XML_NAMESPACE>
|
||||||
|
</AND>
|
||||||
|
</match>
|
||||||
|
</rule>
|
||||||
|
</section>
|
||||||
|
<section>
|
||||||
|
<rule>
|
||||||
|
<match>
|
||||||
|
<AND>
|
||||||
|
<NAME>.*</NAME>
|
||||||
|
<XML_ATTRIBUTE />
|
||||||
|
<XML_NAMESPACE>^$</XML_NAMESPACE>
|
||||||
|
</AND>
|
||||||
|
</match>
|
||||||
|
<order>BY_NAME</order>
|
||||||
|
</rule>
|
||||||
|
</section>
|
||||||
|
<section>
|
||||||
|
<rule>
|
||||||
|
<match>
|
||||||
|
<AND>
|
||||||
|
<NAME>.*</NAME>
|
||||||
|
<XML_ATTRIBUTE />
|
||||||
|
<XML_NAMESPACE>http://schemas.android.com/apk/res/android</XML_NAMESPACE>
|
||||||
|
</AND>
|
||||||
|
</match>
|
||||||
|
<order>ANDROID_ATTRIBUTE_ORDER</order>
|
||||||
|
</rule>
|
||||||
|
</section>
|
||||||
|
<section>
|
||||||
|
<rule>
|
||||||
|
<match>
|
||||||
|
<AND>
|
||||||
|
<NAME>.*</NAME>
|
||||||
|
<XML_ATTRIBUTE />
|
||||||
|
<XML_NAMESPACE>.*</XML_NAMESPACE>
|
||||||
|
</AND>
|
||||||
|
</match>
|
||||||
|
<order>BY_NAME</order>
|
||||||
|
</rule>
|
||||||
|
</section>
|
||||||
|
</rules>
|
||||||
|
</arrangement>
|
||||||
|
</codeStyleSettings>
|
||||||
</code_scheme>
|
</code_scheme>
|
||||||
</component>
|
</component>
|
||||||
4
.idea/encodings.xml
generated
Normal file
4
.idea/encodings.xml
generated
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="Encoding" addBOMForNewFiles="with NO BOM" />
|
||||||
|
</project>
|
||||||
3
.idea/gradle.xml
generated
3
.idea/gradle.xml
generated
@@ -1,8 +1,11 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<project version="4">
|
<project version="4">
|
||||||
|
<component name="GradleMigrationSettings" migrationVersion="1" />
|
||||||
<component name="GradleSettings">
|
<component name="GradleSettings">
|
||||||
<option name="linkedExternalProjectsSettings">
|
<option name="linkedExternalProjectsSettings">
|
||||||
<GradleProjectSettings>
|
<GradleProjectSettings>
|
||||||
|
<option name="delegatedBuild" value="false" />
|
||||||
|
<option name="testRunner" value="PLATFORM" />
|
||||||
<option name="distributionType" value="DEFAULT_WRAPPED" />
|
<option name="distributionType" value="DEFAULT_WRAPPED" />
|
||||||
<option name="externalProjectPath" value="$PROJECT_DIR$" />
|
<option name="externalProjectPath" value="$PROJECT_DIR$" />
|
||||||
<option name="modules">
|
<option name="modules">
|
||||||
|
|||||||
18
.idea/misc.xml
generated
18
.idea/misc.xml
generated
@@ -5,22 +5,36 @@
|
|||||||
<option name="myDefaultNotNull" value="android.support.annotation.NonNull" />
|
<option name="myDefaultNotNull" value="android.support.annotation.NonNull" />
|
||||||
<option name="myNullables">
|
<option name="myNullables">
|
||||||
<value>
|
<value>
|
||||||
<list size="5">
|
<list size="12">
|
||||||
<item index="0" class="java.lang.String" itemvalue="org.jetbrains.annotations.Nullable" />
|
<item index="0" class="java.lang.String" itemvalue="org.jetbrains.annotations.Nullable" />
|
||||||
<item index="1" class="java.lang.String" itemvalue="javax.annotation.Nullable" />
|
<item index="1" class="java.lang.String" itemvalue="javax.annotation.Nullable" />
|
||||||
<item index="2" class="java.lang.String" itemvalue="javax.annotation.CheckForNull" />
|
<item index="2" class="java.lang.String" itemvalue="javax.annotation.CheckForNull" />
|
||||||
<item index="3" class="java.lang.String" itemvalue="edu.umd.cs.findbugs.annotations.Nullable" />
|
<item index="3" class="java.lang.String" itemvalue="edu.umd.cs.findbugs.annotations.Nullable" />
|
||||||
<item index="4" class="java.lang.String" itemvalue="android.support.annotation.Nullable" />
|
<item index="4" class="java.lang.String" itemvalue="android.support.annotation.Nullable" />
|
||||||
|
<item index="5" class="java.lang.String" itemvalue="androidx.annotation.Nullable" />
|
||||||
|
<item index="6" class="java.lang.String" itemvalue="androidx.annotation.RecentlyNullable" />
|
||||||
|
<item index="7" class="java.lang.String" itemvalue="org.checkerframework.checker.nullness.qual.Nullable" />
|
||||||
|
<item index="8" class="java.lang.String" itemvalue="org.checkerframework.checker.nullness.compatqual.NullableDecl" />
|
||||||
|
<item index="9" class="java.lang.String" itemvalue="org.checkerframework.checker.nullness.compatqual.NullableType" />
|
||||||
|
<item index="10" class="java.lang.String" itemvalue="android.annotation.Nullable" />
|
||||||
|
<item index="11" class="java.lang.String" itemvalue="com.android.annotations.Nullable" />
|
||||||
</list>
|
</list>
|
||||||
</value>
|
</value>
|
||||||
</option>
|
</option>
|
||||||
<option name="myNotNulls">
|
<option name="myNotNulls">
|
||||||
<value>
|
<value>
|
||||||
<list size="4">
|
<list size="11">
|
||||||
<item index="0" class="java.lang.String" itemvalue="org.jetbrains.annotations.NotNull" />
|
<item index="0" class="java.lang.String" itemvalue="org.jetbrains.annotations.NotNull" />
|
||||||
<item index="1" class="java.lang.String" itemvalue="javax.annotation.Nonnull" />
|
<item index="1" class="java.lang.String" itemvalue="javax.annotation.Nonnull" />
|
||||||
<item index="2" class="java.lang.String" itemvalue="edu.umd.cs.findbugs.annotations.NonNull" />
|
<item index="2" class="java.lang.String" itemvalue="edu.umd.cs.findbugs.annotations.NonNull" />
|
||||||
<item index="3" class="java.lang.String" itemvalue="android.support.annotation.NonNull" />
|
<item index="3" class="java.lang.String" itemvalue="android.support.annotation.NonNull" />
|
||||||
|
<item index="4" class="java.lang.String" itemvalue="androidx.annotation.NonNull" />
|
||||||
|
<item index="5" class="java.lang.String" itemvalue="androidx.annotation.RecentlyNonNull" />
|
||||||
|
<item index="6" class="java.lang.String" itemvalue="org.checkerframework.checker.nullness.qual.NonNull" />
|
||||||
|
<item index="7" class="java.lang.String" itemvalue="org.checkerframework.checker.nullness.compatqual.NonNullDecl" />
|
||||||
|
<item index="8" class="java.lang.String" itemvalue="org.checkerframework.checker.nullness.compatqual.NonNullType" />
|
||||||
|
<item index="9" class="java.lang.String" itemvalue="android.annotation.NonNull" />
|
||||||
|
<item index="10" class="java.lang.String" itemvalue="com.android.annotations.NonNull" />
|
||||||
</list>
|
</list>
|
||||||
</value>
|
</value>
|
||||||
</option>
|
</option>
|
||||||
|
|||||||
4
.idea/modules.xml
generated
4
.idea/modules.xml
generated
@@ -2,8 +2,8 @@
|
|||||||
<project version="4">
|
<project version="4">
|
||||||
<component name="ProjectModuleManager">
|
<component name="ProjectModuleManager">
|
||||||
<modules>
|
<modules>
|
||||||
<module fileurl="file://$PROJECT_DIR$/Personal_Project---Currency_Converter-master.iml" filepath="$PROJECT_DIR$/Personal_Project---Currency_Converter-master.iml" />
|
<module fileurl="file://$PROJECT_DIR$/Personal_Project---Currency_Converter-master.iml" filepath="$PROJECT_DIR$/Personal_Project---Currency_Converter-master.iml" group="Personal_Project---Currency_Converter-master" />
|
||||||
<module fileurl="file://$PROJECT_DIR$/app/app.iml" filepath="$PROJECT_DIR$/app/app.iml" />
|
<module fileurl="file://$PROJECT_DIR$/app/app.iml" filepath="$PROJECT_DIR$/app/app.iml" group="Personal_Project---Currency_Converter-master/app" />
|
||||||
</modules>
|
</modules>
|
||||||
</component>
|
</component>
|
||||||
</project>
|
</project>
|
||||||
@@ -1,15 +1,17 @@
|
|||||||
apply plugin: 'com.android.application'
|
apply plugin: 'com.android.application'
|
||||||
|
apply plugin: 'kotlin-android'
|
||||||
|
apply plugin: 'kotlin-android-extensions'
|
||||||
|
apply plugin: 'kotlin-kapt'
|
||||||
|
|
||||||
android {
|
android {
|
||||||
compileSdkVersion 27
|
compileSdkVersion 29
|
||||||
buildToolsVersion '27.0.3'
|
|
||||||
defaultConfig {
|
defaultConfig {
|
||||||
applicationId "com.appttude.h_mal.easycc"
|
applicationId "com.appttude.h_mal.easycc"
|
||||||
minSdkVersion 16
|
minSdkVersion 23
|
||||||
targetSdkVersion 27
|
targetSdkVersion 29
|
||||||
versionCode 2
|
versionCode 2
|
||||||
versionName "1.1"
|
versionName "1.1"
|
||||||
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
|
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
||||||
}
|
}
|
||||||
buildTypes {
|
buildTypes {
|
||||||
release {
|
release {
|
||||||
@@ -20,15 +22,66 @@ android {
|
|||||||
testOptions {
|
testOptions {
|
||||||
unitTests.returnDefaultValues = true
|
unitTests.returnDefaultValues = true
|
||||||
}
|
}
|
||||||
|
dataBinding {
|
||||||
|
enabled = true
|
||||||
|
}
|
||||||
|
|
||||||
|
viewBinding.enabled = true
|
||||||
|
|
||||||
|
buildTypes.each {
|
||||||
|
Properties properties = new Properties()
|
||||||
|
properties.load(project.rootProject.file("local.properties").newDataInputStream())
|
||||||
|
def ccApiKey = properties.getProperty("cc_api_key", "")
|
||||||
|
|
||||||
|
it.buildConfigField 'String', "CC_API_KEY", ccApiKey
|
||||||
|
|
||||||
|
it.resValue 'string', "api_key", ccApiKey
|
||||||
|
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
implementation fileTree(include: ['*.jar'], dir: 'libs')
|
implementation fileTree(include: ['*.jar'], dir: 'libs')
|
||||||
implementation 'com.android.support:appcompat-v7:27.1.1'
|
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
|
||||||
implementation 'com.android.support:cardview-v7:27.1.1'
|
implementation 'androidx.appcompat:appcompat:1.1.0'
|
||||||
|
implementation 'androidx.core:core-ktx:1.1.0'
|
||||||
|
implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
|
||||||
|
implementation 'androidx.legacy:legacy-support-v4:1.0.0'
|
||||||
|
implementation 'androidx.lifecycle:lifecycle-viewmodel-ktx:2.1.0'
|
||||||
testImplementation 'junit:junit:4.12'
|
testImplementation 'junit:junit:4.12'
|
||||||
androidTestImplementation 'com.android.support.test:runner:1.0.2'
|
androidTestImplementation 'androidx.test:runner:1.2.0'
|
||||||
androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2'
|
androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0'
|
||||||
androidTestImplementation 'com.android.support.test:rules:1.0.2'
|
|
||||||
|
//Retrofit and GSON
|
||||||
|
implementation 'com.squareup.retrofit2:retrofit:2.6.0'
|
||||||
|
implementation 'com.squareup.retrofit2:converter-gson:2.6.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
|
||||||
|
implementation "androidx.lifecycle:lifecycle-extensions:2.1.0"
|
||||||
|
|
||||||
|
//New Material Design
|
||||||
|
implementation 'com.google.android.material:material:1.1.0-alpha10'
|
||||||
|
|
||||||
|
//Kodein Dependency Injection
|
||||||
|
implementation "org.kodein.di:kodein-di-generic-jvm:6.2.1"
|
||||||
|
implementation "org.kodein.di:kodein-di-framework-android-x:6.2.1"
|
||||||
|
|
||||||
|
//Android Room
|
||||||
|
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 'com.xwray:groupie:2.3.0'
|
||||||
|
implementation 'com.xwray:groupie-kotlin-android-extensions:2.3.0'
|
||||||
|
implementation 'com.xwray:groupie-databinding:2.3.0'
|
||||||
|
|
||||||
|
implementation "androidx.preference:preference-ktx:1.1.0"
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,26 +0,0 @@
|
|||||||
package com.appttude.h_mal.easycc;
|
|
||||||
|
|
||||||
import android.content.Context;
|
|
||||||
import android.support.test.InstrumentationRegistry;
|
|
||||||
import android.support.test.runner.AndroidJUnit4;
|
|
||||||
|
|
||||||
import org.junit.Test;
|
|
||||||
import org.junit.runner.RunWith;
|
|
||||||
|
|
||||||
import static junit.framework.Assert.*;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Instrumentation test, which will execute on an Android device.
|
|
||||||
*
|
|
||||||
* @see <a href="http://d.android.com/tools/testing">Testing documentation</a>
|
|
||||||
*/
|
|
||||||
@RunWith(AndroidJUnit4.class)
|
|
||||||
public class ExampleInstrumentedTest {
|
|
||||||
@Test
|
|
||||||
public void useAppContext() throws Exception {
|
|
||||||
// Context of the app under test.
|
|
||||||
Context appContext = InstrumentationRegistry.getTargetContext();
|
|
||||||
|
|
||||||
assertEquals("com.appttude.h_mal.easycc", appContext.getPackageName());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,36 +1,10 @@
|
|||||||
package com.appttude.h_mal.easycc;
|
package com.appttude.h_mal.easycc;
|
||||||
|
|
||||||
import android.support.test.rule.ActivityTestRule;
|
|
||||||
import android.view.View;
|
|
||||||
|
|
||||||
import org.junit.After;
|
|
||||||
import org.junit.Before;
|
import org.junit.Before;
|
||||||
import org.junit.Rule;
|
|
||||||
import org.junit.Test;
|
|
||||||
|
|
||||||
import static org.junit.Assert.*;
|
|
||||||
|
|
||||||
public class MainActivityTest {
|
public class MainActivityTest {
|
||||||
|
|
||||||
@Rule
|
|
||||||
public ActivityTestRule<MainActivity> activityActivityTestRule = new ActivityTestRule<MainActivity>(MainActivity.class);
|
|
||||||
|
|
||||||
private MainActivity mainActivity = null;
|
|
||||||
|
|
||||||
@Before
|
|
||||||
public void setUp() throws Exception {
|
|
||||||
mainActivity = activityActivityTestRule.getActivity();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testViews(){
|
|
||||||
View view = mainActivity.findViewById(R.id.editText);
|
|
||||||
|
|
||||||
assertNotNull(view);
|
|
||||||
}
|
|
||||||
|
|
||||||
@After
|
|
||||||
public void TearDown() throws Exception{
|
|
||||||
mainActivity = null;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
@@ -5,22 +5,46 @@
|
|||||||
<uses-permission android:name="android.permission.INTERNET" />
|
<uses-permission android:name="android.permission.INTERNET" />
|
||||||
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
|
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
|
||||||
|
|
||||||
|
<supports-screens
|
||||||
|
android:smallScreens="true"
|
||||||
|
android:normalScreens="true"
|
||||||
|
android:largeScreens="true"
|
||||||
|
android:xlargeScreens="true"
|
||||||
|
android:anyDensity="true" />
|
||||||
|
|
||||||
<application
|
<application
|
||||||
|
android:name=".mvvm.AppClass"
|
||||||
android:allowBackup="true"
|
android:allowBackup="true"
|
||||||
android:icon="@mipmap/ic_launcher"
|
android:icon="@mipmap/ic_launcher"
|
||||||
android:label="@string/app_name"
|
android:label="@string/app_name"
|
||||||
android:roundIcon="@mipmap/ic_launcher"
|
android:roundIcon="@mipmap/ic_launcher"
|
||||||
android:supportsRtl="true"
|
android:supportsRtl="true"
|
||||||
android:theme="@style/AppTheme">
|
android:theme="@style/AppTheme">
|
||||||
<activity android:name="com.appttude.h_mal.easycc.MainActivity">
|
<receiver android:name="com.appttude.h_mal.easycc.mvvm.ui.widget.CurrencyAppWidgetKotlin">
|
||||||
|
<intent-filter>
|
||||||
|
<action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
|
||||||
|
</intent-filter>
|
||||||
|
|
||||||
|
<meta-data
|
||||||
|
android:name="android.appwidget.provider"
|
||||||
|
android:resource="@xml/currency_kotlin_app_widget_info" />
|
||||||
|
</receiver>
|
||||||
|
|
||||||
|
<activity android:name="com.appttude.h_mal.easycc.mvvm.ui.widget.CurrencyAppWidgetConfigureActivityKotlin">
|
||||||
|
<intent-filter>
|
||||||
|
<action android:name="android.appwidget.action.APPWIDGET_CONFIGURE" />
|
||||||
|
</intent-filter>
|
||||||
|
</activity>
|
||||||
|
<activity android:name="com.appttude.h_mal.easycc.mvvm.ui.app.MainActivity">
|
||||||
<intent-filter>
|
<intent-filter>
|
||||||
<action android:name="android.intent.action.MAIN" />
|
<action android:name="android.intent.action.MAIN" />
|
||||||
|
|
||||||
<category android:name="android.intent.category.LAUNCHER" />
|
<category android:name="android.intent.category.LAUNCHER" />
|
||||||
</intent-filter>
|
</intent-filter>
|
||||||
</activity>
|
</activity>
|
||||||
|
<activity android:name=".legacy.MainActivityJava"></activity>
|
||||||
|
|
||||||
<receiver android:name="com.appttude.h_mal.easycc.CurrencyAppWidget">
|
<receiver android:name=".legacy.CurrencyAppWidget">
|
||||||
<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,7 +54,7 @@
|
|||||||
android:resource="@xml/currency_app_widget_info" />
|
android:resource="@xml/currency_app_widget_info" />
|
||||||
</receiver>
|
</receiver>
|
||||||
|
|
||||||
<activity android:name="com.appttude.h_mal.easycc.CurrencyAppWidgetConfigureActivity">
|
<activity android:name=".legacy.CurrencyAppWidgetConfigureActivity">
|
||||||
<intent-filter>
|
<intent-filter>
|
||||||
<action android:name="android.appwidget.action.APPWIDGET_CONFIGURE" />
|
<action android:name="android.appwidget.action.APPWIDGET_CONFIGURE" />
|
||||||
</intent-filter>
|
</intent-filter>
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
package com.appttude.h_mal.easycc;
|
package com.appttude.h_mal.easycc.legacy;
|
||||||
|
|
||||||
import android.app.PendingIntent;
|
import android.app.PendingIntent;
|
||||||
import android.appwidget.AppWidgetManager;
|
import android.appwidget.AppWidgetManager;
|
||||||
@@ -10,17 +10,19 @@ import android.text.TextUtils;
|
|||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
import android.widget.RemoteViews;
|
import android.widget.RemoteViews;
|
||||||
|
|
||||||
|
import com.appttude.h_mal.easycc.R;
|
||||||
|
|
||||||
import org.json.JSONException;
|
import org.json.JSONException;
|
||||||
import org.json.JSONObject;
|
import org.json.JSONObject;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.net.URL;
|
import java.net.URL;
|
||||||
|
|
||||||
import static com.appttude.h_mal.easycc.CurrencyAppWidgetConfigureActivity.loadTitlePref;
|
import static com.appttude.h_mal.easycc.legacy.CurrencyAppWidgetConfigureActivity.loadTitlePref;
|
||||||
import static com.appttude.h_mal.easycc.PublicMethods.UriBuilder;
|
import static com.appttude.h_mal.easycc.legacy.PublicMethods.UriBuilder;
|
||||||
import static com.appttude.h_mal.easycc.PublicMethods.createUrl;
|
import static com.appttude.h_mal.easycc.legacy.PublicMethods.createUrl;
|
||||||
import static com.appttude.h_mal.easycc.PublicMethods.makeHttpRequest;
|
import static com.appttude.h_mal.easycc.legacy.PublicMethods.makeHttpRequest;
|
||||||
import static com.appttude.h_mal.easycc.PublicMethods.round;
|
import static com.appttude.h_mal.easycc.legacy.PublicMethods.round;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Implementation of App Widget functionality.
|
* Implementation of App Widget functionality.
|
||||||
@@ -116,7 +118,7 @@ public class CurrencyAppWidget extends AppWidgetProvider {
|
|||||||
int backgroundColor = 0x000000; //background color (here black)
|
int backgroundColor = 0x000000; //background color (here black)
|
||||||
views.setInt( R.id.widget_view, "setBackgroundColor", (int)(opacity * 0xFF) << 24 | backgroundColor);
|
views.setInt( R.id.widget_view, "setBackgroundColor", (int)(opacity * 0xFF) << 24 | backgroundColor);
|
||||||
|
|
||||||
Intent clickIntentTemplate = new Intent(context, MainActivity.class);
|
Intent clickIntentTemplate = new Intent(context, MainActivityJava.class);
|
||||||
|
|
||||||
clickIntentTemplate.setAction(Intent.ACTION_MAIN);
|
clickIntentTemplate.setAction(Intent.ACTION_MAIN);
|
||||||
clickIntentTemplate.addCategory(Intent.CATEGORY_LAUNCHER);
|
clickIntentTemplate.addCategory(Intent.CATEGORY_LAUNCHER);
|
||||||
@@ -149,7 +151,7 @@ public class CurrencyAppWidget extends AppWidgetProvider {
|
|||||||
|
|
||||||
} catch (JSONException e) {
|
} catch (JSONException e) {
|
||||||
|
|
||||||
Log.e("MainActivity", "Problem parsing the JSON results", e);
|
Log.e("MainActivityJava", "Problem parsing the JSON results", e);
|
||||||
}
|
}
|
||||||
return conversionValue;
|
return conversionValue;
|
||||||
}
|
}
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
package com.appttude.h_mal.easycc;
|
package com.appttude.h_mal.easycc.legacy;
|
||||||
|
|
||||||
import android.app.Activity;
|
import android.app.Activity;
|
||||||
import android.app.Dialog;
|
import android.app.Dialog;
|
||||||
@@ -7,7 +7,7 @@ import android.content.Context;
|
|||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.content.SharedPreferences;
|
import android.content.SharedPreferences;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.support.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
import android.text.Editable;
|
import android.text.Editable;
|
||||||
import android.text.TextWatcher;
|
import android.text.TextWatcher;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
@@ -19,6 +19,8 @@ import android.widget.EditText;
|
|||||||
import android.widget.ListView;
|
import android.widget.ListView;
|
||||||
import android.widget.TextView;
|
import android.widget.TextView;
|
||||||
|
|
||||||
|
import com.appttude.h_mal.easycc.R;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The configuration screen for the {@link CurrencyAppWidget CurrencyAppWidget} AppWidget.
|
* The configuration screen for the {@link CurrencyAppWidget CurrencyAppWidget} AppWidget.
|
||||||
*/
|
*/
|
||||||
@@ -90,7 +92,7 @@ public class CurrencyAppWidgetConfigureActivity extends Activity {
|
|||||||
|
|
||||||
// Read the prefix from the SharedPreferences object for this widget.
|
// Read the prefix from the SharedPreferences object for this widget.
|
||||||
// If there is no preference saved, get the default from a resource
|
// If there is no preference saved, get the default from a resource
|
||||||
static String loadTitlePref(Context context, int appWidgetId, int item) {
|
public static String loadTitlePref(Context context, int appWidgetId, int item) {
|
||||||
SharedPreferences prefs = context.getSharedPreferences(PREFS_NAME, 0);
|
SharedPreferences prefs = context.getSharedPreferences(PREFS_NAME, 0);
|
||||||
String titleValue = prefs.getString(PREF_PREFIX_KEY + appWidgetId + "_" + item, null);
|
String titleValue = prefs.getString(PREF_PREFIX_KEY + appWidgetId + "_" + item, null);
|
||||||
if (titleValue != null) {
|
if (titleValue != null) {
|
||||||
@@ -1,12 +1,12 @@
|
|||||||
package com.appttude.h_mal.easycc;
|
package com.appttude.h_mal.easycc.legacy;
|
||||||
|
|
||||||
import android.app.Dialog;
|
import android.app.Dialog;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.content.SharedPreferences;
|
import android.content.SharedPreferences;
|
||||||
import android.os.AsyncTask;
|
import android.os.AsyncTask;
|
||||||
import android.support.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
import android.support.v7.app.AppCompatActivity;
|
import androidx.appcompat.app.AppCompatActivity;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.text.Editable;
|
import android.text.Editable;
|
||||||
import android.text.TextUtils;
|
import android.text.TextUtils;
|
||||||
@@ -24,6 +24,8 @@ import android.widget.ProgressBar;
|
|||||||
import android.widget.TextView;
|
import android.widget.TextView;
|
||||||
import android.widget.Toast;
|
import android.widget.Toast;
|
||||||
|
|
||||||
|
import com.appttude.h_mal.easycc.R;
|
||||||
|
|
||||||
import org.json.JSONException;
|
import org.json.JSONException;
|
||||||
import org.json.JSONObject;
|
import org.json.JSONObject;
|
||||||
|
|
||||||
@@ -32,11 +34,11 @@ import java.net.URL;
|
|||||||
|
|
||||||
import java.text.DecimalFormat;
|
import java.text.DecimalFormat;
|
||||||
|
|
||||||
import static com.appttude.h_mal.easycc.PublicMethods.UriBuilder;
|
import static com.appttude.h_mal.easycc.legacy.PublicMethods.UriBuilder;
|
||||||
import static com.appttude.h_mal.easycc.PublicMethods.createUrl;
|
import static com.appttude.h_mal.easycc.legacy.PublicMethods.createUrl;
|
||||||
import static com.appttude.h_mal.easycc.PublicMethods.makeHttpRequest;
|
import static com.appttude.h_mal.easycc.legacy.PublicMethods.makeHttpRequest;
|
||||||
|
|
||||||
public class MainActivity extends AppCompatActivity {
|
public class MainActivityJava extends AppCompatActivity {
|
||||||
|
|
||||||
EditText currencyOneEditText;
|
EditText currencyOneEditText;
|
||||||
EditText currencyTwoEditText;
|
EditText currencyTwoEditText;
|
||||||
@@ -50,7 +52,7 @@ public class MainActivity extends AppCompatActivity {
|
|||||||
private String CURRENCY_ONE = "currency_one_pref";
|
private String CURRENCY_ONE = "currency_one_pref";
|
||||||
private String CURRENCY_TWO = "currency_two_pref";
|
private String CURRENCY_TWO = "currency_two_pref";
|
||||||
|
|
||||||
private static final String LOG_TAG = MainActivity.class.getSimpleName();
|
private static final String LOG_TAG = MainActivityJava.class.getSimpleName();
|
||||||
|
|
||||||
SharedPreferences pref;
|
SharedPreferences pref;
|
||||||
|
|
||||||
@@ -63,8 +65,8 @@ public class MainActivity extends AppCompatActivity {
|
|||||||
this.getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_HIDDEN | WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE);
|
this.getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_HIDDEN | WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE);
|
||||||
// getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_HIDDEN);
|
// getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_HIDDEN);
|
||||||
|
|
||||||
currencyOneEditText = (EditText) findViewById(R.id.editText);
|
currencyOneEditText = (EditText) findViewById(R.id.topInsertValue);
|
||||||
currencyTwoEditText = (EditText) findViewById(R.id.editText2);
|
currencyTwoEditText = (EditText) findViewById(R.id.bottomInsertValues);
|
||||||
|
|
||||||
currencyOne = (TextView) findViewById(R.id.currency_one);
|
currencyOne = (TextView) findViewById(R.id.currency_one);
|
||||||
currencyTwo = (TextView) findViewById(R.id.currency_two);
|
currencyTwo = (TextView) findViewById(R.id.currency_two);
|
||||||
@@ -96,7 +98,7 @@ public class MainActivity extends AppCompatActivity {
|
|||||||
currencyOne.setOnClickListener(new View.OnClickListener() {
|
currencyOne.setOnClickListener(new View.OnClickListener() {
|
||||||
@Override
|
@Override
|
||||||
public void onClick(View view) {
|
public void onClick(View view) {
|
||||||
CustomDialogClass dialogClass = new CustomDialogClass(MainActivity.this,currencyOne);
|
CustomDialogClass dialogClass = new CustomDialogClass(MainActivityJava.this,currencyOne);
|
||||||
dialogClass.show();
|
dialogClass.show();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -104,7 +106,7 @@ public class MainActivity extends AppCompatActivity {
|
|||||||
currencyTwo.setOnClickListener(new View.OnClickListener() {
|
currencyTwo.setOnClickListener(new View.OnClickListener() {
|
||||||
@Override
|
@Override
|
||||||
public void onClick(View view) {
|
public void onClick(View view) {
|
||||||
CustomDialogClass dialogClass = new CustomDialogClass(MainActivity.this,currencyTwo);
|
CustomDialogClass dialogClass = new CustomDialogClass(MainActivityJava.this,currencyTwo);
|
||||||
dialogClass.show();
|
dialogClass.show();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -223,15 +225,15 @@ public class MainActivity extends AppCompatActivity {
|
|||||||
|
|
||||||
} catch (JSONException e) {
|
} catch (JSONException e) {
|
||||||
|
|
||||||
Log.e("MainActivity", "Problem parsing the JSON results", e);
|
Log.e("MainActivityJava", "Problem parsing the JSON results", e);
|
||||||
}
|
}
|
||||||
return conversionValue;
|
return conversionValue;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected class MyAsyncTask extends AsyncTask<String, Void, Double> {
|
class MyAsyncTask extends AsyncTask<String, Void, Double> {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected Double doInBackground(String... urlString) {
|
public Double doInBackground(String... urlString) {
|
||||||
String jsonResponse = null;
|
String jsonResponse = null;
|
||||||
|
|
||||||
if (urlString.length < 1 || urlString[0] == null) {
|
if (urlString.length < 1 || urlString[0] == null) {
|
||||||
@@ -271,7 +273,7 @@ public class MainActivity extends AppCompatActivity {
|
|||||||
wholeView.setAlpha(1.0f);
|
wholeView.setAlpha(1.0f);
|
||||||
|
|
||||||
if (result == null){
|
if (result == null){
|
||||||
Toast.makeText(MainActivity.this, "Failed to retrieve exchange rate", Toast.LENGTH_SHORT).show();
|
Toast.makeText(MainActivityJava.this, "Failed to retrieve exchange rate", Toast.LENGTH_SHORT).show();
|
||||||
}else{
|
}else{
|
||||||
conversionRateOne = result;
|
conversionRateOne = result;
|
||||||
}
|
}
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
package com.appttude.h_mal.easycc;
|
package com.appttude.h_mal.easycc.legacy;
|
||||||
|
|
||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
@@ -20,7 +20,7 @@ public class PublicMethods {
|
|||||||
public PublicMethods() {
|
public PublicMethods() {
|
||||||
}
|
}
|
||||||
|
|
||||||
static String UriBuilder(String s1, String s2){
|
public static String UriBuilder(String s1, String s2){
|
||||||
s1 = s1.substring(0,3);
|
s1 = s1.substring(0,3);
|
||||||
s2 = s2.substring(0,3);
|
s2 = s2.substring(0,3);
|
||||||
|
|
||||||
@@ -34,7 +34,7 @@ public class PublicMethods {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static java.net.URL createUrl(String stringUrl) {
|
public static java.net.URL createUrl(String stringUrl) {
|
||||||
URL url = null;
|
URL url = null;
|
||||||
try {
|
try {
|
||||||
url = new URL(stringUrl);
|
url = new URL(stringUrl);
|
||||||
@@ -45,7 +45,7 @@ public class PublicMethods {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static String makeHttpRequest(URL url) throws IOException {
|
public static String makeHttpRequest(URL url) throws IOException {
|
||||||
String jsonResponse = "";
|
String jsonResponse = "";
|
||||||
|
|
||||||
if (url == null) {
|
if (url == null) {
|
||||||
33
app/src/main/java/com/appttude/h_mal/easycc/mvvm/AppClass.kt
Normal file
33
app/src/main/java/com/appttude/h_mal/easycc/mvvm/AppClass.kt
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
package com.appttude.h_mal.easycc.mvvm
|
||||||
|
|
||||||
|
import android.app.Application
|
||||||
|
import com.appttude.h_mal.easycc.mvvm.data.Repository.Repository
|
||||||
|
import com.appttude.h_mal.easycc.mvvm.data.network.NetworkConnectionInterceptor
|
||||||
|
import com.appttude.h_mal.easycc.mvvm.data.network.api.GetData
|
||||||
|
import com.appttude.h_mal.easycc.mvvm.data.prefs.PreferenceProvider
|
||||||
|
import com.appttude.h_mal.easycc.mvvm.ui.app.MainViewModelFactory
|
||||||
|
import com.appttude.h_mal.easycc.mvvm.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{
|
||||||
|
|
||||||
|
override val kodein = Kodein.lazy {
|
||||||
|
import(androidXModule(this@AppClass))
|
||||||
|
|
||||||
|
bind() from singleton { NetworkConnectionInterceptor(instance()) }
|
||||||
|
bind() from singleton { GetData(instance()) }
|
||||||
|
bind() from singleton { PreferenceProvider(instance()) }
|
||||||
|
bind() from singleton { Repository(instance(), instance(), instance()) }
|
||||||
|
bind() from provider { MainViewModelFactory(instance()) }
|
||||||
|
bind() from provider { WidgetViewModelFactory(instance()) }
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,43 @@
|
|||||||
|
package com.appttude.h_mal.easycc.mvvm.data.Repository
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import com.appttude.h_mal.easycc.BuildConfig
|
||||||
|
import com.appttude.h_mal.easycc.mvvm.data.prefs.PreferenceProvider
|
||||||
|
import com.appttude.h_mal.easycc.R
|
||||||
|
import com.appttude.h_mal.easycc.mvvm.data.network.response.ResponseObject
|
||||||
|
import com.appttude.h_mal.easycc.mvvm.data.network.SafeApiRequest
|
||||||
|
import com.appttude.h_mal.easycc.mvvm.data.network.api.GetData
|
||||||
|
|
||||||
|
class Repository (
|
||||||
|
private val api: GetData,
|
||||||
|
private val prefs: PreferenceProvider,
|
||||||
|
context: Context
|
||||||
|
): SafeApiRequest(){
|
||||||
|
|
||||||
|
var ccApiKey = BuildConfig.CC_API_KEY
|
||||||
|
|
||||||
|
private val appContext = context.applicationContext
|
||||||
|
|
||||||
|
suspend fun getData(s1: String, s2: String): ResponseObject?{
|
||||||
|
return apiRequest{ api.getCurrencyRate(convertPairsListToString(s1, s2),ccApiKey)}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getConversionPair(): List<String?> {
|
||||||
|
return prefs.getConversionPair()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun setConversionPair(s1: String, s2: String){
|
||||||
|
prefs.saveConversionPair(s1, s2)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun convertPairsListToString(s1: String, s2: String): String = "${s1.substring(0,3)}_${s2.substring(0,3)}"
|
||||||
|
|
||||||
|
fun getArrayList(): Array<String> = appContext.resources.getStringArray(R.array.currency_arrays)
|
||||||
|
|
||||||
|
fun getWidgetConversionPairs(id: Int): List<String?> = prefs.getWidgetConversionPair(id)
|
||||||
|
|
||||||
|
fun setWidgetConversionPairs(s1: String, s2: String, id: Int) = prefs.saveWidgetConversionPair(s1, s2, id)
|
||||||
|
|
||||||
|
fun removeWidgetConversionPairs(id: Int) = prefs.removeWidgetConversion(id)
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,39 @@
|
|||||||
|
package com.appttude.h_mal.easycc.mvvm.data.network
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import android.net.ConnectivityManager
|
||||||
|
import android.net.NetworkCapabilities
|
||||||
|
import okhttp3.Interceptor
|
||||||
|
import okhttp3.Response
|
||||||
|
import java.io.IOException
|
||||||
|
|
||||||
|
class NetworkConnectionInterceptor(
|
||||||
|
context: Context
|
||||||
|
) : Interceptor {
|
||||||
|
|
||||||
|
private val applicationContext = context.applicationContext
|
||||||
|
|
||||||
|
override fun intercept(chain: Interceptor.Chain): Response {
|
||||||
|
if (!isInternetAvailable()){
|
||||||
|
throw IOException("Make sure you have an active data connection")
|
||||||
|
}
|
||||||
|
return chain.proceed(chain.request())
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun isInternetAvailable(): Boolean {
|
||||||
|
var result = false
|
||||||
|
val connectivityManager =
|
||||||
|
applicationContext.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager?
|
||||||
|
connectivityManager?.let {
|
||||||
|
it.getNetworkCapabilities(connectivityManager.activeNetwork)?.apply {
|
||||||
|
result = when {
|
||||||
|
hasTransport(NetworkCapabilities.TRANSPORT_WIFI) -> true
|
||||||
|
hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR) -> true
|
||||||
|
else -> false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
package com.appttude.h_mal.easycc.mvvm.data.network
|
||||||
|
|
||||||
|
import org.json.JSONException
|
||||||
|
import org.json.JSONObject
|
||||||
|
import retrofit2.Response
|
||||||
|
import java.io.IOException
|
||||||
|
|
||||||
|
abstract class SafeApiRequest {
|
||||||
|
|
||||||
|
suspend fun<T: Any> apiRequest(call: suspend () -> Response<T>) : T{
|
||||||
|
val response = call.invoke()
|
||||||
|
if(response.isSuccessful){
|
||||||
|
return response.body()!!
|
||||||
|
}else{
|
||||||
|
val error = response.errorBody()?.string()
|
||||||
|
|
||||||
|
val message = StringBuilder()
|
||||||
|
error?.let{
|
||||||
|
try{
|
||||||
|
message.append(JSONObject(it).getString("error"))
|
||||||
|
}catch(e: JSONException){ }
|
||||||
|
message.append("\n")
|
||||||
|
}
|
||||||
|
message.append("Error Code: ${response.code()}")
|
||||||
|
throw IOException(message.toString())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,36 @@
|
|||||||
|
package com.appttude.h_mal.easycc.mvvm.data.network.api
|
||||||
|
|
||||||
|
import com.appttude.h_mal.easycc.mvvm.data.network.response.ResponseObject
|
||||||
|
import com.appttude.h_mal.easycc.mvvm.data.network.NetworkConnectionInterceptor
|
||||||
|
import okhttp3.OkHttpClient
|
||||||
|
import retrofit2.Response
|
||||||
|
import retrofit2.Retrofit
|
||||||
|
import retrofit2.converter.gson.GsonConverterFactory
|
||||||
|
import retrofit2.http.GET
|
||||||
|
import retrofit2.http.Query
|
||||||
|
|
||||||
|
|
||||||
|
interface GetData {
|
||||||
|
|
||||||
|
companion object{
|
||||||
|
operator fun invoke(
|
||||||
|
networkConnectionInterceptor: NetworkConnectionInterceptor
|
||||||
|
) : GetData{
|
||||||
|
|
||||||
|
val okkHttpclient = OkHttpClient.Builder()
|
||||||
|
.addNetworkInterceptor(networkConnectionInterceptor)
|
||||||
|
.build()
|
||||||
|
|
||||||
|
return Retrofit.Builder()
|
||||||
|
.client(okkHttpclient)
|
||||||
|
.baseUrl("https://free.currencyconverterapi.com/api/v3/")
|
||||||
|
.addConverterFactory(GsonConverterFactory.create())
|
||||||
|
.build()
|
||||||
|
.create(GetData::class.java)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@GET("convert?")
|
||||||
|
suspend fun getCurrencyRate(@Query("q") currency: String, @Query("apiKey") api: String): Response<ResponseObject>
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
package com.appttude.h_mal.easycc.mvvm.data.network.response
|
||||||
|
|
||||||
|
import com.appttude.h_mal.easycc.mvvm.models.CurrencyObject
|
||||||
|
import com.google.gson.annotations.SerializedName
|
||||||
|
|
||||||
|
class ResponseObject(
|
||||||
|
@SerializedName("query")
|
||||||
|
var query : Any,
|
||||||
|
@SerializedName("results")
|
||||||
|
var results : Map<String, CurrencyObject>
|
||||||
|
)
|
||||||
@@ -0,0 +1,80 @@
|
|||||||
|
package com.appttude.h_mal.easycc.mvvm.data.prefs
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import android.content.SharedPreferences
|
||||||
|
import androidx.preference.PreferenceManager
|
||||||
|
import com.appttude.h_mal.easycc.R
|
||||||
|
|
||||||
|
private const val CONVERSION_ONE = "conversion_one"
|
||||||
|
private const val CONVERSION_TWO = "conversion_two"
|
||||||
|
private const val CONVERSION_ONE_WIDGET = "conversion_one_widget"
|
||||||
|
private const val CONVERSION_TWO_WIDGET = "conversion_two_widget"
|
||||||
|
|
||||||
|
class PreferenceProvider(
|
||||||
|
context: Context
|
||||||
|
) {
|
||||||
|
|
||||||
|
private val appContext = context.applicationContext
|
||||||
|
|
||||||
|
private val preference: SharedPreferences
|
||||||
|
get() = PreferenceManager.getDefaultSharedPreferences(appContext)
|
||||||
|
|
||||||
|
private val defaultRate: String = context.resources.getStringArray(R.array.currency_arrays)[0]
|
||||||
|
|
||||||
|
|
||||||
|
fun saveConversionPair(s1: String, s2: String) {
|
||||||
|
preference.edit().putString(
|
||||||
|
CONVERSION_ONE,
|
||||||
|
s1
|
||||||
|
).putString(
|
||||||
|
CONVERSION_TWO,
|
||||||
|
s2
|
||||||
|
).apply()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getConversionPair(): List<String?> {
|
||||||
|
val s1 = getLastConversionOne()
|
||||||
|
val s2 = getLastConversionTwo()
|
||||||
|
|
||||||
|
return listOf(s1,s2)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun getLastConversionOne(): String? {
|
||||||
|
return preference.getString(CONVERSION_ONE, defaultRate)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun getLastConversionTwo(): String? {
|
||||||
|
return preference.getString(CONVERSION_TWO, defaultRate)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun saveWidgetConversionPair(s1: String, s2: String, id: Int) {
|
||||||
|
preference.edit().putString(
|
||||||
|
"${id}_$CONVERSION_ONE",
|
||||||
|
s1
|
||||||
|
).putString(
|
||||||
|
"${id}_$CONVERSION_TWO",
|
||||||
|
s2
|
||||||
|
).apply()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getWidgetConversionPair(id: Int): List<String?> {
|
||||||
|
val s1 = getWidgetLastConversionOne(id)
|
||||||
|
val s2 = getWidgetLastConversionTwo(id)
|
||||||
|
|
||||||
|
return listOf(s1,s2)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun getWidgetLastConversionOne(id: Int): String? {
|
||||||
|
return preference.getString("${id}_$CONVERSION_ONE", defaultRate)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun getWidgetLastConversionTwo(id: Int): String? {
|
||||||
|
return preference.getString("${id}_$CONVERSION_TWO", defaultRate)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun removeWidgetConversion(id: Int){
|
||||||
|
preference.edit().remove("${id}_$CONVERSION_ONE").apply()
|
||||||
|
preference.edit().remove("${id}_$CONVERSION_TWO").apply()
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
package com.appttude.h_mal.easycc.mvvm.models
|
||||||
|
|
||||||
|
import com.google.gson.annotations.SerializedName
|
||||||
|
|
||||||
|
data class CurrencyObject (
|
||||||
|
@SerializedName("id")
|
||||||
|
var id : String,
|
||||||
|
@SerializedName("fr")
|
||||||
|
var fr : String,
|
||||||
|
@SerializedName("to")
|
||||||
|
var to : String,
|
||||||
|
@SerializedName("val")
|
||||||
|
var value: Double
|
||||||
|
)
|
||||||
@@ -0,0 +1,50 @@
|
|||||||
|
package com.appttude.h_mal.easycc.mvvm.ui.app
|
||||||
|
|
||||||
|
import android.app.Dialog
|
||||||
|
import android.content.Context
|
||||||
|
import android.os.Bundle
|
||||||
|
import android.text.Editable
|
||||||
|
import android.text.TextWatcher
|
||||||
|
import android.view.WindowManager
|
||||||
|
import android.widget.ArrayAdapter
|
||||||
|
import android.widget.TextView
|
||||||
|
import com.appttude.h_mal.easycc.R
|
||||||
|
import kotlinx.android.synthetic.main.custom_dialog.*
|
||||||
|
|
||||||
|
class CustomDialogClass(
|
||||||
|
context: Context,
|
||||||
|
val textView: TextView,
|
||||||
|
val viewModel: MainViewModel
|
||||||
|
) : Dialog(context) {
|
||||||
|
|
||||||
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
|
super.onCreate(savedInstanceState)
|
||||||
|
setContentView(R.layout.custom_dialog)
|
||||||
|
|
||||||
|
window!!.setBackgroundDrawableResource(android.R.color.transparent)
|
||||||
|
window!!.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE)
|
||||||
|
|
||||||
|
val arrayAdapter = ArrayAdapter.createFromResource(context, R.array.currency_arrays, android.R.layout.simple_list_item_1)
|
||||||
|
list_view.adapter = arrayAdapter
|
||||||
|
|
||||||
|
search_text.addTextChangedListener(object : TextWatcher {
|
||||||
|
override fun beforeTextChanged(charSequence: CharSequence, i: Int, i1: Int, i2: Int) {}
|
||||||
|
override fun onTextChanged(charSequence: CharSequence, i: Int, i1: Int, i2: Int) {
|
||||||
|
arrayAdapter.filter.filter(charSequence)
|
||||||
|
}
|
||||||
|
override fun afterTextChanged(editable: Editable) {}
|
||||||
|
})
|
||||||
|
|
||||||
|
list_view.setOnItemClickListener{ adapterView, _, i, _ ->
|
||||||
|
if (textView.tag == "top"){
|
||||||
|
viewModel.rateIdFrom = adapterView.getItemAtPosition(i).toString()
|
||||||
|
}else{
|
||||||
|
viewModel.rateIdTo = adapterView.getItemAtPosition(i).toString()
|
||||||
|
}
|
||||||
|
textView.text = adapterView.getItemAtPosition(i).toString()
|
||||||
|
viewModel.getExchangeRate()
|
||||||
|
|
||||||
|
dismiss()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,127 @@
|
|||||||
|
package com.appttude.h_mal.easycc.mvvm.ui.app
|
||||||
|
|
||||||
|
import android.os.Bundle
|
||||||
|
import android.text.Editable
|
||||||
|
import android.text.TextWatcher
|
||||||
|
import android.util.Log
|
||||||
|
import android.view.View
|
||||||
|
import android.view.WindowManager
|
||||||
|
import android.widget.TextView
|
||||||
|
import android.widget.Toast
|
||||||
|
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.ActivityMainBinding
|
||||||
|
import com.appttude.h_mal.easycc.utils.DisplayToast
|
||||||
|
import com.appttude.h_mal.easycc.utils.clearEditText
|
||||||
|
import com.appttude.h_mal.easycc.utils.hideView
|
||||||
|
import kotlinx.android.synthetic.main.activity_main.*
|
||||||
|
import org.kodein.di.KodeinAware
|
||||||
|
import org.kodein.di.android.kodein
|
||||||
|
import org.kodein.di.generic.instance
|
||||||
|
|
||||||
|
class MainActivity : AppCompatActivity(), RateListener, KodeinAware, View.OnClickListener{
|
||||||
|
|
||||||
|
override val kodein by kodein()
|
||||||
|
private val factory : MainViewModelFactory by instance()
|
||||||
|
|
||||||
|
companion object{
|
||||||
|
lateinit var viewModel: MainViewModel
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
|
super.onCreate(savedInstanceState)
|
||||||
|
|
||||||
|
this.window.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_HIDDEN or WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE)
|
||||||
|
|
||||||
|
val binding: ActivityMainBinding = DataBindingUtil.setContentView(this, R.layout.activity_main)
|
||||||
|
viewModel = ViewModelProviders.of(this, factory).get(MainViewModel::class.java)
|
||||||
|
|
||||||
|
binding.viewmodel = viewModel
|
||||||
|
binding.lifecycleOwner = this
|
||||||
|
|
||||||
|
viewModel.rateListener = this
|
||||||
|
|
||||||
|
intent.extras?.apply {
|
||||||
|
val itemOne = getString("parse_1")
|
||||||
|
val itemTwo = getString("parse_2")
|
||||||
|
|
||||||
|
if (!itemOne.isNullOrEmpty() && !itemTwo.isNullOrEmpty()){
|
||||||
|
viewModel.rateIdTo = itemOne
|
||||||
|
viewModel.rateIdFrom = itemTwo
|
||||||
|
|
||||||
|
viewModel.getExchangeRate()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
viewModel.start()
|
||||||
|
|
||||||
|
topInsertValue.addTextChangedListener(textWatcherClass)
|
||||||
|
bottomInsertValues.addTextChangedListener(textWatcherClass2)
|
||||||
|
|
||||||
|
currency_one.setOnClickListener(this)
|
||||||
|
currency_two.setOnClickListener(this)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun showCustomDialog(view: View?){
|
||||||
|
val dialogClass = CustomDialogClass(this, view as TextView, viewModel)
|
||||||
|
dialogClass.show()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onStarted() {
|
||||||
|
progressBar.hideView(false)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onSuccess() {
|
||||||
|
progressBar.hideView(true)
|
||||||
|
bottomInsertValues.clearEditText()
|
||||||
|
topInsertValue.clearEditText()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onFailure(message: String) {
|
||||||
|
progressBar.hideView(true)
|
||||||
|
DisplayToast(message)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onClick(view: View?) {
|
||||||
|
showCustomDialog(view)
|
||||||
|
}
|
||||||
|
|
||||||
|
private val textWatcherClass: TextWatcher = object : TextWatcher {
|
||||||
|
override fun onTextChanged(s: CharSequence, st: Int, b: Int, c: Int) {
|
||||||
|
bottomInsertValues.removeTextChangedListener(textWatcherClass2)
|
||||||
|
if (topInsertValue.text.isNullOrEmpty()) {
|
||||||
|
bottomInsertValues.setText("")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
override fun beforeTextChanged(s: CharSequence, start: Int, count: Int, after: Int) {}
|
||||||
|
override fun afterTextChanged(s: Editable) {
|
||||||
|
try {
|
||||||
|
viewModel.setBottomValue(s.toString(), bottomInsertValues)
|
||||||
|
} catch (e: NumberFormatException) {
|
||||||
|
Log.e(this.javaClass.simpleName, "no numbers inserted")
|
||||||
|
}
|
||||||
|
bottomInsertValues.addTextChangedListener(textWatcherClass2)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private val textWatcherClass2: TextWatcher = object : TextWatcher {
|
||||||
|
override fun onTextChanged(s: CharSequence, st: Int, b: Int, c: Int) {
|
||||||
|
topInsertValue.removeTextChangedListener(textWatcherClass)
|
||||||
|
if (bottomInsertValues.text.isNullOrEmpty()) {
|
||||||
|
topInsertValue.clearEditText()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
override fun beforeTextChanged(s: CharSequence, start: Int, count: Int, after: Int) {}
|
||||||
|
override fun afterTextChanged(s: Editable) {
|
||||||
|
try {
|
||||||
|
viewModel.setTopValue(s.toString(), topInsertValue)
|
||||||
|
} catch (e: NumberFormatException) {
|
||||||
|
Log.e(this.javaClass.simpleName, "no numbers inserted")
|
||||||
|
}
|
||||||
|
topInsertValue.addTextChangedListener(textWatcherClass)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,78 @@
|
|||||||
|
package com.appttude.h_mal.easycc.mvvm.ui.app
|
||||||
|
|
||||||
|
import android.widget.EditText
|
||||||
|
import androidx.lifecycle.MutableLiveData
|
||||||
|
import androidx.lifecycle.ViewModel
|
||||||
|
import com.appttude.h_mal.easycc.mvvm.data.Repository.Repository
|
||||||
|
import kotlinx.coroutines.CoroutineScope
|
||||||
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
|
import java.io.IOException
|
||||||
|
import java.text.DecimalFormat
|
||||||
|
import java.text.NumberFormat
|
||||||
|
|
||||||
|
class MainViewModel(
|
||||||
|
private val repository: Repository
|
||||||
|
) : ViewModel(){
|
||||||
|
|
||||||
|
private val defaultValue by lazy { repository.getArrayList()[0] }
|
||||||
|
private val conversionPairs by lazy { repository.getConversionPair() }
|
||||||
|
|
||||||
|
var rateIdFrom: String? = conversionPairs[0] ?: defaultValue
|
||||||
|
var rateIdTo: String? = conversionPairs[1] ?: defaultValue
|
||||||
|
|
||||||
|
var topVal: String? = null
|
||||||
|
var bottomVal: String? = null
|
||||||
|
|
||||||
|
var rateListener: RateListener? = null
|
||||||
|
|
||||||
|
private var conversionRate: Double = 0.00
|
||||||
|
|
||||||
|
fun getExchangeRate(){
|
||||||
|
rateListener?.onStarted()
|
||||||
|
if (rateIdFrom.isNullOrEmpty() || rateIdTo.isNullOrEmpty()){
|
||||||
|
rateListener?.onFailure("Select currencies")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
CoroutineScope(Dispatchers.Main).launch {
|
||||||
|
try {
|
||||||
|
val exchangeResponse = repository.getData(rateIdFrom!!, rateIdTo!!)
|
||||||
|
repository.setConversionPair(rateIdFrom!!, rateIdTo!!)
|
||||||
|
|
||||||
|
exchangeResponse?.results?.iterator()?.next()?.value?.let {
|
||||||
|
rateListener?.onSuccess()
|
||||||
|
conversionRate = it.value
|
||||||
|
return@launch
|
||||||
|
}
|
||||||
|
rateListener?.onFailure("Failed to retrieve rate")
|
||||||
|
}catch(e: IOException){
|
||||||
|
rateListener?.onFailure(e.message!!)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun setBottomValue(fromValue : String, editText: EditText) {
|
||||||
|
val fromValDouble = fromValue.toDouble()
|
||||||
|
val bottomVal1 = (fromValDouble * conversionRate).toTwoDp()
|
||||||
|
editText.setText(bottomVal1.toBigDecimal().toPlainString())
|
||||||
|
}
|
||||||
|
|
||||||
|
fun setTopValue(toValue : String, editText: EditText) {
|
||||||
|
val toDoubleVal = toValue.toDouble()
|
||||||
|
val newTopVal = toDoubleVal.times((1/conversionRate)).toTwoDp()
|
||||||
|
editText.setText(newTopVal.toBigDecimal().toPlainString())
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun Double.toTwoDp() = run {
|
||||||
|
val df = DecimalFormat("#.##")
|
||||||
|
java.lang.Double.valueOf(df.format(this))
|
||||||
|
}
|
||||||
|
|
||||||
|
fun start(){
|
||||||
|
if (rateIdFrom != rateIdTo){
|
||||||
|
getExchangeRate()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
package com.appttude.h_mal.easycc.mvvm.ui.app
|
||||||
|
|
||||||
|
import androidx.lifecycle.ViewModel
|
||||||
|
import androidx.lifecycle.ViewModelProvider
|
||||||
|
import com.appttude.h_mal.easycc.mvvm.data.Repository.Repository
|
||||||
|
|
||||||
|
@Suppress("UNCHECKED_CAST")
|
||||||
|
class MainViewModelFactory (
|
||||||
|
private val repository: Repository
|
||||||
|
): ViewModelProvider.NewInstanceFactory(){
|
||||||
|
|
||||||
|
override fun <T : ViewModel?> create(modelClass: Class<T>): T {
|
||||||
|
return MainViewModel(repository) as T
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
package com.appttude.h_mal.easycc.mvvm.ui.app
|
||||||
|
|
||||||
|
interface RateListener {
|
||||||
|
fun onStarted()
|
||||||
|
fun onSuccess()
|
||||||
|
fun onFailure(message: String)
|
||||||
|
}
|
||||||
@@ -0,0 +1,70 @@
|
|||||||
|
package com.appttude.h_mal.easycc.mvvm.ui.widget
|
||||||
|
|
||||||
|
import android.appwidget.AppWidgetManager
|
||||||
|
import android.os.Bundle
|
||||||
|
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.mvvm.ui.app.RateListener
|
||||||
|
import com.appttude.h_mal.easycc.utils.DisplayToast
|
||||||
|
import org.kodein.di.KodeinAware
|
||||||
|
import org.kodein.di.android.kodein
|
||||||
|
import org.kodein.di.generic.instance
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The configuration screen for the [CurrencyAppWidgetKotlin] AppWidget.
|
||||||
|
*/
|
||||||
|
class CurrencyAppWidgetConfigureActivityKotlin : AppCompatActivity(), KodeinAware, RateListener {
|
||||||
|
|
||||||
|
override val kodein by kodein()
|
||||||
|
private val factory : WidgetViewModelFactory by instance()
|
||||||
|
|
||||||
|
var mAppWidgetId = AppWidgetManager.INVALID_APPWIDGET_ID
|
||||||
|
|
||||||
|
companion object{
|
||||||
|
lateinit var viewModel: WidgetViewModel
|
||||||
|
}
|
||||||
|
|
||||||
|
public override fun onCreate(icicle: Bundle?) {
|
||||||
|
super.onCreate(icicle)
|
||||||
|
|
||||||
|
// 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.
|
||||||
|
setResult(RESULT_CANCELED)
|
||||||
|
|
||||||
|
// Find the widget id from the intent.
|
||||||
|
val extras = intent.extras
|
||||||
|
if (extras != null) {
|
||||||
|
mAppWidgetId = extras.getInt(
|
||||||
|
AppWidgetManager.EXTRA_APPWIDGET_ID, AppWidgetManager.INVALID_APPWIDGET_ID)
|
||||||
|
}
|
||||||
|
|
||||||
|
// If this activity was started with an intent without an app widget ID, finish with an error.
|
||||||
|
if (mAppWidgetId == AppWidgetManager.INVALID_APPWIDGET_ID) {
|
||||||
|
finish()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
viewModel = ViewModelProviders.of(this, factory).get(WidgetViewModel::class.java)
|
||||||
|
val binding: CurrencyAppWidgetConfigureBinding = DataBindingUtil.setContentView(this, R.layout.currency_app_widget_configure)
|
||||||
|
binding.viewmodel = viewModel
|
||||||
|
binding.lifecycleOwner = this
|
||||||
|
|
||||||
|
viewModel.initialise(mAppWidgetId)
|
||||||
|
viewModel.rateListener = this
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onStarted() {}
|
||||||
|
|
||||||
|
override fun onSuccess() {
|
||||||
|
WidgetSubmitDialog(this,mAppWidgetId, viewModel).show()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onFailure(message: String) {
|
||||||
|
DisplayToast(message)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,114 @@
|
|||||||
|
package com.appttude.h_mal.easycc.mvvm.ui.widget
|
||||||
|
|
||||||
|
import android.app.PendingIntent
|
||||||
|
import android.appwidget.AppWidgetManager
|
||||||
|
import android.appwidget.AppWidgetProvider
|
||||||
|
import android.content.Context
|
||||||
|
import android.content.Intent
|
||||||
|
import android.widget.RemoteViews
|
||||||
|
import android.widget.Toast
|
||||||
|
import com.appttude.h_mal.easycc.legacy.MainActivityJava
|
||||||
|
import com.appttude.h_mal.easycc.R
|
||||||
|
import com.appttude.h_mal.easycc.mvvm.data.Repository.Repository
|
||||||
|
import com.appttude.h_mal.easycc.mvvm.data.network.NetworkConnectionInterceptor
|
||||||
|
import com.appttude.h_mal.easycc.mvvm.data.network.api.GetData
|
||||||
|
import com.appttude.h_mal.easycc.mvvm.data.prefs.PreferenceProvider
|
||||||
|
import kotlinx.coroutines.CoroutineScope
|
||||||
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
|
import org.kodein.di.KodeinAware
|
||||||
|
import org.kodein.di.LateInitKodein
|
||||||
|
import org.kodein.di.generic.instance
|
||||||
|
import java.io.IOException
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Implementation of App Widget functionality.
|
||||||
|
* App Widget Configuration implemented in [CurrencyAppWidgetConfigureActivityKotlin]
|
||||||
|
*/
|
||||||
|
class CurrencyAppWidgetKotlin : AppWidgetProvider() {
|
||||||
|
|
||||||
|
private val kodein = LateInitKodein()
|
||||||
|
private val repository : Repository by kodein.instance()
|
||||||
|
|
||||||
|
override fun onUpdate(context: Context, appWidgetManager: AppWidgetManager, appWidgetIds: IntArray) {
|
||||||
|
kodein.baseKodein = (context.applicationContext as KodeinAware).kodein
|
||||||
|
// There may be multiple widgets active, so update all of them
|
||||||
|
for (appWidgetId in appWidgetIds) {
|
||||||
|
updateAppWidget(context, appWidgetManager, appWidgetId)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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.
|
||||||
|
for (appWidgetId in appWidgetIds) {
|
||||||
|
repository.removeWidgetConversionPairs(appWidgetId)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onEnabled(context: Context) {
|
||||||
|
kodein.baseKodein = (context.applicationContext as KodeinAware).kodein
|
||||||
|
// Enter relevant functionality for when the first widget is created
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onDisabled(context: Context) {
|
||||||
|
kodein.baseKodein = (context.applicationContext as KodeinAware).kodein
|
||||||
|
// Enter relevant functionality for when the last widget is disabled
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
fun updateAppWidget(context: Context, appWidgetManager: AppWidgetManager, appWidgetId: Int) {
|
||||||
|
//todo: get value from repository
|
||||||
|
val stringList = repository.getWidgetConversionPairs(appWidgetId)
|
||||||
|
val s1 = stringList[0]
|
||||||
|
val s2 = stringList[1]
|
||||||
|
|
||||||
|
// Construct the RemoteViews object
|
||||||
|
val views = RemoteViews(context.packageName, R.layout.currency_app_widget)
|
||||||
|
views.setTextViewText(R.id.exchangeName, "Rates")
|
||||||
|
views.setTextViewText(R.id.exchangeRate, "not set")
|
||||||
|
|
||||||
|
//todo: async task to get rate
|
||||||
|
CoroutineScope(Dispatchers.Main).launch {
|
||||||
|
try {
|
||||||
|
val response = repository.getData(s1!!.substring(0,3),s2!!.substring(0,3))
|
||||||
|
|
||||||
|
response?.results?.iterator()?.next()?.value?.let {
|
||||||
|
val titleString = "${it.fr}${it.to}"
|
||||||
|
views.setTextViewText(R.id.exchangeName, titleString)
|
||||||
|
views.setTextViewText(R.id.exchangeRate, it.value.toString())
|
||||||
|
}
|
||||||
|
}catch (io : IOException){
|
||||||
|
Toast.makeText(context,io.message, Toast.LENGTH_LONG).show()
|
||||||
|
}finally {
|
||||||
|
// Instruct the widget manager to update the widget
|
||||||
|
appWidgetManager.updateAppWidget(appWidgetId, views)
|
||||||
|
|
||||||
|
val opacity = 0.3f //opacity = 0: fully transparent, opacity = 1: no transparancy
|
||||||
|
val backgroundColor = 0x000000 //background color (here black)
|
||||||
|
|
||||||
|
views.setInt(R.id.widget_view, "setBackgroundColor", (opacity * 0xFF).toInt() shl 24 or backgroundColor)
|
||||||
|
|
||||||
|
val clickIntentTemplate = Intent(context, MainActivityJava::class.java).apply {
|
||||||
|
action = Intent.ACTION_MAIN
|
||||||
|
addCategory(Intent.CATEGORY_LAUNCHER)
|
||||||
|
putExtra("parse_1", s1)
|
||||||
|
putExtra("parse_2", s2)
|
||||||
|
addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP or Intent.FLAG_ACTIVITY_SINGLE_TOP)
|
||||||
|
}
|
||||||
|
|
||||||
|
val configPendingIntent = PendingIntent.getActivity(context, 0, clickIntentTemplate, PendingIntent.FLAG_UPDATE_CURRENT)
|
||||||
|
views.setOnClickPendingIntent(R.id.widget_view, configPendingIntent)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
fun setupRepository(context: Context): Repository {
|
||||||
|
val networkInterceptor = NetworkConnectionInterceptor(context)
|
||||||
|
val getData = GetData(networkInterceptor)
|
||||||
|
val prefs = PreferenceProvider(context)
|
||||||
|
return Repository(getData,prefs,context)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@@ -0,0 +1,48 @@
|
|||||||
|
package com.appttude.h_mal.easycc.mvvm.ui.widget
|
||||||
|
|
||||||
|
import android.app.Dialog
|
||||||
|
import android.content.Context
|
||||||
|
import android.os.Bundle
|
||||||
|
import android.text.Editable
|
||||||
|
import android.text.TextWatcher
|
||||||
|
import android.view.WindowManager
|
||||||
|
import android.widget.ArrayAdapter
|
||||||
|
import com.appttude.h_mal.easycc.R
|
||||||
|
import kotlinx.android.synthetic.main.custom_dialog.*
|
||||||
|
|
||||||
|
/*
|
||||||
|
widget for when submitting the completed selections
|
||||||
|
*/
|
||||||
|
class WidgetItemSelectDialog(
|
||||||
|
context: Context,
|
||||||
|
val dialogResult: DialogResult
|
||||||
|
) :Dialog(context){
|
||||||
|
|
||||||
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
|
super.onCreate(savedInstanceState)
|
||||||
|
setContentView(R.layout.custom_dialog)
|
||||||
|
|
||||||
|
window!!.setBackgroundDrawableResource(android.R.color.transparent)
|
||||||
|
window!!.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE)
|
||||||
|
|
||||||
|
val arrayAdapter = ArrayAdapter.createFromResource(context, R.array.currency_arrays, android.R.layout.simple_list_item_1)
|
||||||
|
list_view.adapter = arrayAdapter
|
||||||
|
|
||||||
|
search_text.addTextChangedListener(object : TextWatcher {
|
||||||
|
override fun beforeTextChanged(charSequence: CharSequence, i: Int, i1: Int, i2: Int) {}
|
||||||
|
override fun onTextChanged(charSequence: CharSequence, i: Int, i1: Int, i2: Int) {
|
||||||
|
arrayAdapter.filter.filter(charSequence)
|
||||||
|
}
|
||||||
|
override fun afterTextChanged(editable: Editable) {}
|
||||||
|
})
|
||||||
|
|
||||||
|
list_view.setOnItemClickListener{ adapterView, _, i, _ ->
|
||||||
|
dialogResult.result(adapterView.getItemAtPosition(i).toString())
|
||||||
|
dismiss()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
interface DialogResult{
|
||||||
|
fun result(result : String)
|
||||||
|
}
|
||||||
@@ -0,0 +1,51 @@
|
|||||||
|
package com.appttude.h_mal.easycc.mvvm.ui.widget
|
||||||
|
|
||||||
|
import android.app.Activity
|
||||||
|
import android.app.Dialog
|
||||||
|
import android.appwidget.AppWidgetManager
|
||||||
|
import android.content.Intent
|
||||||
|
import android.os.Bundle
|
||||||
|
import com.appttude.h_mal.easycc.R
|
||||||
|
import kotlinx.android.synthetic.main.confirm_dialog.*
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
widget for when submitting the completed selections
|
||||||
|
*/
|
||||||
|
class WidgetSubmitDialog(
|
||||||
|
private val activity: Activity,
|
||||||
|
private val appWidgetId: Int,
|
||||||
|
private val viewModel: WidgetViewModel
|
||||||
|
) :Dialog(activity){
|
||||||
|
|
||||||
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
|
super.onCreate(savedInstanceState)
|
||||||
|
setContentView(R.layout.confirm_dialog)
|
||||||
|
|
||||||
|
// requestWindowFeature(Window.FEATURE_NO_TITLE)
|
||||||
|
window!!.setBackgroundDrawableResource(android.R.color.transparent)
|
||||||
|
setCancelable(false)
|
||||||
|
|
||||||
|
//todo: amend widget text
|
||||||
|
confirm_text.text = StringBuilder().append("Create widget for ")
|
||||||
|
.append(viewModel.getWidgetStringName())
|
||||||
|
.append("?").toString()
|
||||||
|
|
||||||
|
confirm_yes.setOnClickListener {
|
||||||
|
viewModel.setWidgetStored()
|
||||||
|
|
||||||
|
val intent = Intent(context, CurrencyAppWidgetKotlin::class.java)
|
||||||
|
intent.action = AppWidgetManager.ACTION_APPWIDGET_UPDATE
|
||||||
|
intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS, IntArray(appWidgetId))
|
||||||
|
context.sendBroadcast(intent)
|
||||||
|
|
||||||
|
// Make sure we pass back the original appWidgetId
|
||||||
|
val resultValue = Intent()
|
||||||
|
resultValue.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetId)
|
||||||
|
activity.setResult(Activity.RESULT_OK, resultValue)
|
||||||
|
activity.finish()
|
||||||
|
}
|
||||||
|
|
||||||
|
confirm_no.setOnClickListener { dismiss() }
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,94 @@
|
|||||||
|
package com.appttude.h_mal.easycc.mvvm.ui.widget
|
||||||
|
|
||||||
|
import android.view.View
|
||||||
|
import android.widget.Toast
|
||||||
|
import androidx.lifecycle.MutableLiveData
|
||||||
|
import androidx.lifecycle.ViewModel
|
||||||
|
import com.appttude.h_mal.easycc.mvvm.data.Repository.Repository
|
||||||
|
import com.appttude.h_mal.easycc.mvvm.ui.app.RateListener
|
||||||
|
|
||||||
|
class WidgetViewModel(
|
||||||
|
private val repository: Repository
|
||||||
|
) : ViewModel(){
|
||||||
|
|
||||||
|
var rateListener: RateListener? = null
|
||||||
|
|
||||||
|
var appWidgetId: Int? = null
|
||||||
|
|
||||||
|
var rateIdFrom = MutableLiveData<String>()
|
||||||
|
var rateIdTo = MutableLiveData<String>()
|
||||||
|
|
||||||
|
fun initialise(appId: Int){
|
||||||
|
appWidgetId = appId
|
||||||
|
val widgetString = getWidgetStored(appId)
|
||||||
|
|
||||||
|
if (widgetString.isNotEmpty()){
|
||||||
|
rateIdFrom.value = widgetString[0]
|
||||||
|
rateIdTo.value = widgetString[1]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun selectCurrencyOnClick(view: View){
|
||||||
|
if (appWidgetId == null){
|
||||||
|
Toast.makeText(view.context, "No App Widget ID", Toast.LENGTH_LONG).show()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
WidgetItemSelectDialog(view.context, object : DialogResult {
|
||||||
|
override fun result(result: String) {
|
||||||
|
if (view.tag.toString() == "top"){
|
||||||
|
rateIdFrom.value = result
|
||||||
|
}else{
|
||||||
|
rateIdTo.value = result
|
||||||
|
}
|
||||||
|
Toast.makeText(view.context, result, Toast.LENGTH_LONG).show()
|
||||||
|
}
|
||||||
|
}).show()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun submitSelectionOnClick(view: View){
|
||||||
|
if (appWidgetId == null){
|
||||||
|
rateListener?.onFailure("No App Widget ID")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if (rateIdFrom.value == rateIdTo.value){
|
||||||
|
rateListener?.onFailure("Selected rates cannot be the same")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
rateListener?.onSuccess()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getWidgetStored(id: Int) = repository.getWidgetConversionPairs(id)
|
||||||
|
|
||||||
|
fun setWidgetStored() {
|
||||||
|
if (rateIdTo.value == null && rateIdFrom.value == null){
|
||||||
|
rateListener?.onFailure("Selections incomplete")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if (rateIdFrom.value == rateIdTo.value){
|
||||||
|
rateListener?.onFailure("Selected rates cannot be the same")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
repository.setWidgetConversionPairs(rateIdFrom.value!!,rateIdTo.value!!,appWidgetId!!)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getWidgetStringName() = "${rateIdFrom.value!!.trimToThree()}${rateIdTo.value!!.trimToThree()}"
|
||||||
|
|
||||||
|
private fun String.trimToThree() = this.substring(0,3)
|
||||||
|
|
||||||
|
private fun arrayEntry(s: String?): String? {
|
||||||
|
val strings = repository.getArrayList()
|
||||||
|
var returnString: String? = strings[0]
|
||||||
|
for (string in strings) {
|
||||||
|
if (s == string.substring(0, 3)) {
|
||||||
|
returnString = string
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return returnString
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
package com.appttude.h_mal.easycc.mvvm.ui.widget
|
||||||
|
|
||||||
|
import androidx.lifecycle.ViewModel
|
||||||
|
import androidx.lifecycle.ViewModelProvider
|
||||||
|
import com.appttude.h_mal.easycc.mvvm.data.Repository.Repository
|
||||||
|
|
||||||
|
@Suppress("UNCHECKED_CAST")
|
||||||
|
class WidgetViewModelFactory (
|
||||||
|
private val repository: Repository
|
||||||
|
): ViewModelProvider.NewInstanceFactory(){
|
||||||
|
|
||||||
|
override fun <T : ViewModel?> create(modelClass: Class<T>): T {
|
||||||
|
return WidgetViewModel(repository) as T
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
package com.appttude.h_mal.easycc.utils
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import android.view.View
|
||||||
|
import android.widget.EditText
|
||||||
|
import android.widget.Toast
|
||||||
|
|
||||||
|
fun EditText.clearEditText(){
|
||||||
|
this.setText("")
|
||||||
|
}
|
||||||
|
|
||||||
|
fun View.hideView(vis : Boolean){
|
||||||
|
visibility = if (vis){ View.GONE } else { View.VISIBLE }
|
||||||
|
}
|
||||||
|
|
||||||
|
fun Context.DisplayToast(message: String){
|
||||||
|
Toast.makeText(this, message, Toast.LENGTH_LONG).show()
|
||||||
|
}
|
||||||
BIN
app/src/main/res/drawable-nodpi/example_appwidget_preview.png
Normal file
BIN
app/src/main/res/drawable-nodpi/example_appwidget_preview.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 3.4 KiB |
@@ -5,5 +5,5 @@
|
|||||||
android:startColor="@color/colour_three"
|
android:startColor="@color/colour_three"
|
||||||
android:endColor="@color/colour_two"
|
android:endColor="@color/colour_two"
|
||||||
android:type="linear"
|
android:type="linear"
|
||||||
android:angle="45"/>
|
android:angle="45" />
|
||||||
</shape>
|
</shape>
|
||||||
@@ -1,111 +1,124 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
<layout 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:card_view="http://schemas.android.com/apk/res-auto"
|
xmlns:card_view="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:layout_centerInParent="true"
|
|
||||||
android:orientation="vertical"
|
|
||||||
android:focusable="false"
|
|
||||||
android:focusableInTouchMode="true"
|
|
||||||
tools:context="com.appttude.h_mal.easycc.MainActivity">
|
|
||||||
|
|
||||||
|
<data>
|
||||||
|
<variable
|
||||||
|
name="viewmodel"
|
||||||
|
type="com.appttude.h_mal.easycc.mvvm.ui.app.MainViewModel" />
|
||||||
|
</data>
|
||||||
|
|
||||||
<RelativeLayout
|
<RelativeLayout
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="match_parent"
|
||||||
android:layout_centerInParent="true"
|
android:orientation="vertical"
|
||||||
android:layout_margin="12dp">
|
android:focusable="false"
|
||||||
|
android:focusableInTouchMode="true"
|
||||||
|
tools:context="com.appttude.h_mal.easycc.mvvm.ui.app.MainActivity">
|
||||||
|
|
||||||
<LinearLayout
|
<RelativeLayout
|
||||||
android:id="@+id/whole_view"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:orientation="vertical">
|
|
||||||
|
|
||||||
<LinearLayout
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginBottom="18dp"
|
|
||||||
android:orientation="vertical">
|
|
||||||
|
|
||||||
<android.support.v7.widget.CardView xmlns:card_view="http://schemas.android.com/apk/res-auto"
|
|
||||||
style="@style/cardview_theme">
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:id="@+id/currency_one"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_margin="12dp"
|
|
||||||
android:text="Currency One"
|
|
||||||
android:textColor="@color/colour_five"
|
|
||||||
android:textSize="18sp" />
|
|
||||||
</android.support.v7.widget.CardView>
|
|
||||||
|
|
||||||
|
|
||||||
<EditText
|
|
||||||
android:id="@+id/editText"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginBottom="6dp"
|
|
||||||
android:layout_marginTop="6dp"
|
|
||||||
android:background="@drawable/round_edit_text"
|
|
||||||
android:ems="10"
|
|
||||||
android:hint="insert value one"
|
|
||||||
android:textColorHighlight="#608d91"
|
|
||||||
android:inputType="numberDecimal"
|
|
||||||
android:padding="12dp"
|
|
||||||
android:selectAllOnFocus="true" />
|
|
||||||
|
|
||||||
</LinearLayout>
|
|
||||||
|
|
||||||
<LinearLayout
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:orientation="vertical">
|
|
||||||
|
|
||||||
<android.support.v7.widget.CardView xmlns:card_view="http://schemas.android.com/apk/res-auto"
|
|
||||||
style="@style/cardview_theme">
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:id="@+id/currency_two"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_margin="12dp"
|
|
||||||
android:text="Currency Two"
|
|
||||||
android:textColor="@color/colour_five"
|
|
||||||
android:textSize="18sp" />
|
|
||||||
</android.support.v7.widget.CardView>
|
|
||||||
|
|
||||||
|
|
||||||
<EditText
|
|
||||||
android:id="@+id/editText2"
|
|
||||||
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:textColorHighlight="#608d91"
|
|
||||||
android:inputType="numberDecimal"
|
|
||||||
android:padding="12dp"
|
|
||||||
android:selectAllOnFocus="true" />
|
|
||||||
|
|
||||||
</LinearLayout>
|
|
||||||
|
|
||||||
</LinearLayout>
|
|
||||||
|
|
||||||
<ProgressBar
|
|
||||||
android:id="@+id/progressBar"
|
|
||||||
style="?android:attr/progressBarStyle"
|
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_centerInParent="true"
|
android:layout_centerInParent="true"
|
||||||
android:indeterminate="true"
|
android:layout_margin="12dp">
|
||||||
android:indeterminateTint="@color/colour_four"
|
|
||||||
android:indeterminateTintMode="src_atop" />
|
<LinearLayout
|
||||||
|
android:id="@+id/whole_view"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="vertical">
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
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:tag="top"
|
||||||
|
android:id="@+id/currency_one"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_margin="12dp"
|
||||||
|
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_height="wrap_content"
|
||||||
|
android:layout_marginBottom="6dp"
|
||||||
|
android:layout_marginTop="6dp"
|
||||||
|
android:background="@drawable/round_edit_text"
|
||||||
|
android:ems="10"
|
||||||
|
android:text="@={viewmodel.topVal}"
|
||||||
|
android:hint="insert value one"
|
||||||
|
android:textColorHighlight="#608d91"
|
||||||
|
android:inputType="numberDecimal"
|
||||||
|
android:padding="12dp"
|
||||||
|
android:tag="from"
|
||||||
|
android:selectAllOnFocus="true" />
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="vertical">
|
||||||
|
|
||||||
|
<androidx.cardview.widget.CardView xmlns:card_view="http://schemas.android.com/apk/res-auto"
|
||||||
|
style="@style/cardview_theme">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/currency_two"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_margin="12dp"
|
||||||
|
android:text="@{viewmodel.rateIdTo}"
|
||||||
|
android:tag="bottom"
|
||||||
|
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:text="@={viewmodel.bottomVal}"
|
||||||
|
android:hint="insert value two"
|
||||||
|
android:textColorHighlight="#608d91"
|
||||||
|
android:inputType="numberDecimal"
|
||||||
|
android:padding="12dp"
|
||||||
|
android:tag="to"
|
||||||
|
android:selectAllOnFocus="true" />
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
<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>
|
||||||
</RelativeLayout>
|
</RelativeLayout>
|
||||||
|
|
||||||
</RelativeLayout>
|
</layout>
|
||||||
|
|||||||
@@ -6,7 +6,7 @@
|
|||||||
android:orientation="vertical">
|
android:orientation="vertical">
|
||||||
|
|
||||||
|
|
||||||
<android.support.v7.widget.CardView xmlns:card_view="http://schemas.android.com/apk/res-auto"
|
<androidx.cardview.widget.CardView xmlns:card_view="http://schemas.android.com/apk/res-auto"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
card_view:cardBackgroundColor="@color/colour_three"
|
card_view:cardBackgroundColor="@color/colour_three"
|
||||||
@@ -15,6 +15,7 @@
|
|||||||
<LinearLayout
|
<LinearLayout
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_margin="6dp"
|
||||||
android:orientation="vertical">
|
android:orientation="vertical">
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
@@ -51,6 +52,6 @@
|
|||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|
||||||
</android.support.v7.widget.CardView>
|
</androidx.cardview.widget.CardView>
|
||||||
|
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
@@ -1,26 +1,37 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<RelativeLayout
|
<layout
|
||||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
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"
|
||||||
android:layout_width="match_parent"
|
xmlns:card_view="http://schemas.android.com/apk/res-auto"
|
||||||
android:layout_height="match_parent"
|
xmlns:tools="http://schemas.android.com/tools">
|
||||||
android:layout_centerInParent="true"
|
|
||||||
android:layout_margin="10dp"
|
<data>
|
||||||
android:orientation="vertical">
|
<variable
|
||||||
|
name="viewmodel"
|
||||||
|
type="com.appttude.h_mal.easycc.mvvm.ui.widget.WidgetViewModel" />
|
||||||
|
</data>
|
||||||
|
|
||||||
<RelativeLayout
|
<RelativeLayout
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="match_parent"
|
||||||
android:layout_centerInParent="true">
|
android:orientation="vertical"
|
||||||
|
android:focusable="false"
|
||||||
|
android:focusableInTouchMode="true"
|
||||||
|
tools:context=".mvvm.ui.widget.CurrencyAppWidgetConfigureActivityKotlin">
|
||||||
|
|
||||||
<LinearLayout
|
<RelativeLayout
|
||||||
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_centerInParent="true"
|
||||||
android:id="@+id/whole_view">
|
android:layout_margin="12dp">
|
||||||
|
|
||||||
<android.support.v7.widget.CardView
|
<LinearLayout
|
||||||
xmlns:card_view="http://schemas.android.com/apk/res-auto"
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:id="@+id/whole_view">
|
||||||
|
|
||||||
|
<androidx.cardview.widget.CardView
|
||||||
style="@style/cardview_theme"
|
style="@style/cardview_theme"
|
||||||
android:layout_margin="11dp">
|
android:layout_margin="11dp">
|
||||||
|
|
||||||
@@ -29,14 +40,16 @@
|
|||||||
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:text="Currency One"
|
android:onClick="@{viewmodel::selectCurrencyOnClick}"
|
||||||
|
android:tag="top"
|
||||||
|
android:text="@={viewmodel.rateIdFrom}"
|
||||||
android:textColor="@color/colour_five"
|
android:textColor="@color/colour_five"
|
||||||
android:textSize="18sp" />
|
android:textSize="18sp"
|
||||||
</android.support.v7.widget.CardView>
|
tools:text="Currency One" />
|
||||||
|
</androidx.cardview.widget.CardView>
|
||||||
|
|
||||||
|
|
||||||
<android.support.v7.widget.CardView
|
<androidx.cardview.widget.CardView
|
||||||
xmlns:card_view="http://schemas.android.com/apk/res-auto"
|
|
||||||
style="@style/cardview_theme"
|
style="@style/cardview_theme"
|
||||||
android:layout_margin="11dp">
|
android:layout_margin="11dp">
|
||||||
|
|
||||||
@@ -45,26 +58,31 @@
|
|||||||
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:text="Currency Two"
|
android:onClick="@{viewmodel::selectCurrencyOnClick}"
|
||||||
|
android:tag="bottom"
|
||||||
|
android:text="@={viewmodel.rateIdTo}"
|
||||||
android:textColor="@color/colour_five"
|
android:textColor="@color/colour_five"
|
||||||
android:textSize="18sp" />
|
android:textSize="18sp"
|
||||||
</android.support.v7.widget.CardView>
|
tools:text="Currency Two" />
|
||||||
|
</androidx.cardview.widget.CardView>
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_marginTop="12dp"
|
||||||
|
android:layout_marginEnd="22dp"
|
||||||
|
android:id="@+id/submit_widget"
|
||||||
|
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:onClick="@{viewmodel::submitSelectionOnClick}"
|
||||||
|
android:text="Submit" />
|
||||||
|
|
||||||
|
</RelativeLayout>
|
||||||
|
|
||||||
</LinearLayout>
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:layout_marginTop="12dp"
|
|
||||||
android:layout_marginRight="22dp"
|
|
||||||
android:id="@+id/submit_widget"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_below="@id/whole_view"
|
|
||||||
android:layout_alignParentRight="true"
|
|
||||||
android:textColor="@color/colour_five"
|
|
||||||
android:text="Submit" />
|
|
||||||
|
|
||||||
</RelativeLayout>
|
</RelativeLayout>
|
||||||
|
</layout>
|
||||||
|
|
||||||
|
|
||||||
</RelativeLayout>
|
|
||||||
|
|||||||
21
app/src/main/res/layout/currency_kotlin_app_widget.xml
Normal file
21
app/src/main/res/layout/currency_kotlin_app_widget.xml
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:background="#09C"
|
||||||
|
android:padding="@dimen/widget_margin">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/appwidget_text"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_centerHorizontal="true"
|
||||||
|
android:layout_centerVertical="true"
|
||||||
|
android:layout_margin="8dp"
|
||||||
|
android:background="#09C"
|
||||||
|
android:contentDescription="@string/appwidget_text"
|
||||||
|
android:text="@string/appwidget_text"
|
||||||
|
android:textColor="#ffffff"
|
||||||
|
android:textSize="24sp"
|
||||||
|
android:textStyle="bold|italic" />
|
||||||
|
|
||||||
|
</RelativeLayout>
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:padding="16dp">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginBottom="8dp"
|
||||||
|
android:text="@string/configure" />
|
||||||
|
|
||||||
|
<EditText
|
||||||
|
android:id="@+id/appwidget_text"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:inputType="text" />
|
||||||
|
|
||||||
|
<Button
|
||||||
|
android:id="@+id/add_button"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="8dp"
|
||||||
|
android:text="@string/add_widget" />
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
@@ -6,9 +6,7 @@
|
|||||||
android:backgroundTint="@android:color/transparent"
|
android:backgroundTint="@android:color/transparent"
|
||||||
android:orientation="vertical">
|
android:orientation="vertical">
|
||||||
|
|
||||||
|
<androidx.cardview.widget.CardView
|
||||||
|
|
||||||
<android.support.v7.widget.CardView
|
|
||||||
xmlns:card_view="http://schemas.android.com/apk/res-auto"
|
xmlns:card_view="http://schemas.android.com/apk/res-auto"
|
||||||
android:layout_weight="1"
|
android:layout_weight="1"
|
||||||
android:layout_height="0dp"
|
android:layout_height="0dp"
|
||||||
@@ -23,8 +21,8 @@
|
|||||||
android:layout_height="match_parent">
|
android:layout_height="match_parent">
|
||||||
</ListView>
|
</ListView>
|
||||||
|
|
||||||
</android.support.v7.widget.CardView>
|
</androidx.cardview.widget.CardView>
|
||||||
<android.support.v7.widget.CardView xmlns:card_view="http://schemas.android.com/apk/res-auto"
|
<androidx.cardview.widget.CardView xmlns:card_view="http://schemas.android.com/apk/res-auto"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_marginBottom="45dp"
|
android:layout_marginBottom="45dp"
|
||||||
@@ -38,6 +36,6 @@
|
|||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_margin="8dp"
|
android:layout_margin="8dp"
|
||||||
android:hint="Search Currency" />
|
android:hint="Search Currency" />
|
||||||
</android.support.v7.widget.CardView>
|
</androidx.cardview.widget.CardView>
|
||||||
|
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
@@ -160,4 +160,5 @@
|
|||||||
</string-array>
|
</string-array>
|
||||||
<string name="configure">Configure</string>
|
<string name="configure">Configure</string>
|
||||||
<string name="add_widget">Add widget</string>
|
<string name="add_widget">Add widget</string>
|
||||||
|
<string name="appwidget_text">EXAMPLE</string>
|
||||||
</resources>
|
</resources>
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<appwidget-provider xmlns:android="http://schemas.android.com/apk/res/android"
|
<appwidget-provider xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
android:configure="com.appttude.h_mal.easycc.CurrencyAppWidgetConfigureActivity"
|
android:configure="com.appttude.h_mal.easycc.legacy.CurrencyAppWidgetConfigureActivity"
|
||||||
android:initialKeyguardLayout="@layout/currency_app_widget"
|
android:initialKeyguardLayout="@layout/currency_app_widget"
|
||||||
android:initialLayout="@layout/currency_app_widget"
|
android:initialLayout="@layout/currency_app_widget"
|
||||||
android:minHeight="40dp"
|
android:minHeight="40dp"
|
||||||
|
|||||||
11
app/src/main/res/xml/currency_kotlin_app_widget_info.xml
Normal file
11
app/src/main/res/xml/currency_kotlin_app_widget_info.xml
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<appwidget-provider xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:configure="com.appttude.h_mal.easycc.mvvm.ui.widget.CurrencyAppWidgetConfigureActivityKotlin"
|
||||||
|
android:initialKeyguardLayout="@layout/currency_kotlin_app_widget"
|
||||||
|
android:initialLayout="@layout/currency_kotlin_app_widget"
|
||||||
|
android:minWidth="110dp"
|
||||||
|
android:minHeight="40dp"
|
||||||
|
android:previewImage="@drawable/example_appwidget_preview"
|
||||||
|
android:resizeMode="horizontal|vertical"
|
||||||
|
android:updatePeriodMillis="86400000"
|
||||||
|
android:widgetCategory="home_screen|keyguard"></appwidget-provider>
|
||||||
@@ -1,18 +1,7 @@
|
|||||||
package com.appttude.h_mal.easycc;
|
package com.appttude.h_mal.easycc;
|
||||||
|
|
||||||
import android.content.Context;
|
|
||||||
import android.content.res.Resources;
|
|
||||||
import android.os.AsyncTask;
|
|
||||||
|
|
||||||
import org.junit.Before;
|
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.io.InputStream;
|
|
||||||
|
|
||||||
import static com.appttude.h_mal.easycc.PublicMethods.UriBuilder;
|
|
||||||
import static com.appttude.h_mal.easycc.PublicMethods.createUrl;
|
|
||||||
import static com.appttude.h_mal.easycc.PublicMethods.makeHttpRequest;
|
|
||||||
import static org.junit.Assert.*;
|
import static org.junit.Assert.*;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -1,16 +1,18 @@
|
|||||||
// 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.
|
||||||
|
|
||||||
buildscript {
|
buildscript {
|
||||||
|
ext.kotlin_version = '1.3.61'
|
||||||
|
|
||||||
repositories {
|
repositories {
|
||||||
jcenter()
|
jcenter()
|
||||||
google()
|
google()
|
||||||
}
|
}
|
||||||
dependencies {
|
dependencies {
|
||||||
classpath 'com.android.tools.build:gradle:3.1.4'
|
classpath 'com.android.tools.build:gradle:3.6.1'
|
||||||
|
|
||||||
// NOTE: Do not place your application dependencies here; they belong
|
// NOTE: Do not place your application dependencies here; they belong
|
||||||
// in the individual module build.gradle files
|
// in the individual module build.gradle files
|
||||||
}
|
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" }
|
||||||
}
|
}
|
||||||
|
|
||||||
allprojects {
|
allprojects {
|
||||||
|
|||||||
@@ -9,6 +9,8 @@
|
|||||||
|
|
||||||
# Specifies the JVM arguments used for the daemon process.
|
# Specifies the JVM arguments used for the daemon process.
|
||||||
# The setting is particularly useful for tweaking memory settings.
|
# The setting is particularly useful for tweaking memory settings.
|
||||||
|
android.enableJetifier=true
|
||||||
|
android.useAndroidX=true
|
||||||
org.gradle.jvmargs=-Xmx1536m
|
org.gradle.jvmargs=-Xmx1536m
|
||||||
|
|
||||||
# When configured, Gradle will run in incubating parallel mode.
|
# When configured, Gradle will run in incubating parallel mode.
|
||||||
|
|||||||
4
gradle/wrapper/gradle-wrapper.properties
vendored
4
gradle/wrapper/gradle-wrapper.properties
vendored
@@ -1,6 +1,6 @@
|
|||||||
#Sun Nov 04 16:25:28 AEST 2018
|
#Thu Mar 05 18:27:33 UTC 2020
|
||||||
distributionBase=GRADLE_USER_HOME
|
distributionBase=GRADLE_USER_HOME
|
||||||
distributionPath=wrapper/dists
|
distributionPath=wrapper/dists
|
||||||
zipStoreBase=GRADLE_USER_HOME
|
zipStoreBase=GRADLE_USER_HOME
|
||||||
zipStorePath=wrapper/dists
|
zipStorePath=wrapper/dists
|
||||||
distributionUrl=https\://services.gradle.org/distributions/gradle-4.4-all.zip
|
distributionUrl=https\://services.gradle.org/distributions/gradle-5.6.4-all.zip
|
||||||
|
|||||||
Reference in New Issue
Block a user