mirror of
https://github.com/hmalik144/EasyCC_Master.git
synced 2026-01-31 02:41:47 +00:00
Widget Viewmodel updated to use live data
Widget activity, dialog and viewmodel cleaned up folder structure changed removal of legacy java code
This commit is contained in:
@@ -1,8 +1,6 @@
|
||||
package com.appttude.h_mal.easycc;
|
||||
|
||||
|
||||
import org.junit.Before;
|
||||
|
||||
public class MainActivityTest {
|
||||
|
||||
|
||||
|
||||
@@ -13,14 +13,14 @@
|
||||
android:anyDensity="true" />
|
||||
|
||||
<application
|
||||
android:name=".mvvm.application.AppClass"
|
||||
android:name="com.appttude.h_mal.easycc.application.AppClass"
|
||||
android:allowBackup="true"
|
||||
android:icon="@mipmap/ic_launcher"
|
||||
android:label="@string/app_name"
|
||||
android:roundIcon="@mipmap/ic_launcher"
|
||||
android:supportsRtl="true"
|
||||
android:theme="@style/AppTheme">
|
||||
<receiver android:name="com.appttude.h_mal.easycc.mvvm.ui.widget.CurrencyAppWidgetKotlin">
|
||||
<receiver android:name="com.appttude.h_mal.easycc.widget.CurrencyAppWidgetKotlin">
|
||||
<intent-filter>
|
||||
<action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
|
||||
</intent-filter>
|
||||
@@ -30,12 +30,12 @@
|
||||
android:resource="@xml/currency_app_widget_info" />
|
||||
</receiver>
|
||||
|
||||
<activity android:name="com.appttude.h_mal.easycc.mvvm.ui.widget.CurrencyAppWidgetConfigureActivityKotlin">
|
||||
<activity android:name="com.appttude.h_mal.easycc.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">
|
||||
<activity android:name="com.appttude.h_mal.easycc.ui.main.MainActivity">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN" />
|
||||
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
package com.appttude.h_mal.easycc.mvvm.application
|
||||
package com.appttude.h_mal.easycc.application
|
||||
|
||||
import android.app.Application
|
||||
import com.appttude.h_mal.easycc.mvvm.data.repository.RepositoryImpl
|
||||
import com.appttude.h_mal.easycc.mvvm.data.network.interceptors.NetworkConnectionInterceptor
|
||||
import com.appttude.h_mal.easycc.mvvm.data.network.interceptors.QueryInterceptor
|
||||
import com.appttude.h_mal.easycc.mvvm.data.network.api.CurrencyApi
|
||||
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 com.appttude.h_mal.easycc.data.network.api.CurrencyApi
|
||||
import com.appttude.h_mal.easycc.data.network.interceptors.NetworkConnectionInterceptor
|
||||
import com.appttude.h_mal.easycc.data.network.interceptors.QueryInterceptor
|
||||
import com.appttude.h_mal.easycc.data.prefs.PreferenceProvider
|
||||
import com.appttude.h_mal.easycc.data.repository.RepositoryImpl
|
||||
import com.appttude.h_mal.easycc.ui.main.MainViewModelFactory
|
||||
import com.appttude.h_mal.easycc.ui.widget.WidgetViewModelFactory
|
||||
import org.kodein.di.Kodein
|
||||
import org.kodein.di.KodeinAware
|
||||
import org.kodein.di.android.x.androidXModule
|
||||
@@ -18,9 +18,11 @@ import org.kodein.di.generic.singleton
|
||||
|
||||
class AppClass : Application(), KodeinAware {
|
||||
|
||||
// Kodein Dependecy Injection created in Application class
|
||||
override val kodein by Kodein.lazy {
|
||||
import(androidXModule(this@AppClass))
|
||||
|
||||
// instance() can be context or other binding created
|
||||
bind() from singleton { NetworkConnectionInterceptor(instance()) }
|
||||
bind() from singleton { QueryInterceptor() }
|
||||
bind() from singleton { CurrencyApi(instance(),instance()) }
|
||||
@@ -1,4 +1,4 @@
|
||||
package com.appttude.h_mal.easycc.mvvm.data.network
|
||||
package com.appttude.h_mal.easycc.data.network
|
||||
|
||||
import android.util.Log
|
||||
import org.json.JSONException
|
||||
@@ -1,8 +1,8 @@
|
||||
package com.appttude.h_mal.easycc.mvvm.data.network.api
|
||||
package com.appttude.h_mal.easycc.data.network.api
|
||||
|
||||
import com.appttude.h_mal.easycc.mvvm.data.network.response.ResponseObject
|
||||
import com.appttude.h_mal.easycc.mvvm.data.network.interceptors.NetworkConnectionInterceptor
|
||||
import com.appttude.h_mal.easycc.mvvm.data.network.interceptors.QueryInterceptor
|
||||
import com.appttude.h_mal.easycc.data.network.interceptors.NetworkConnectionInterceptor
|
||||
import com.appttude.h_mal.easycc.data.network.interceptors.QueryInterceptor
|
||||
import com.appttude.h_mal.easycc.data.network.response.ResponseObject
|
||||
import okhttp3.OkHttpClient
|
||||
import retrofit2.Response
|
||||
import retrofit2.Retrofit
|
||||
@@ -10,20 +10,29 @@ import retrofit2.converter.gson.GsonConverterFactory
|
||||
import retrofit2.http.GET
|
||||
import retrofit2.http.Query
|
||||
|
||||
|
||||
/**
|
||||
* Retrofit2 Network class to create network requests
|
||||
*/
|
||||
interface CurrencyApi {
|
||||
|
||||
// Get rate from server with arguments passed in Repository
|
||||
@GET("convert?")
|
||||
suspend fun getCurrencyRate(@Query("q") currency: String): Response<ResponseObject>
|
||||
|
||||
// interface invokation to be used in application class
|
||||
companion object{
|
||||
operator fun invoke(
|
||||
networkConnectionInterceptor: NetworkConnectionInterceptor,
|
||||
queryInterceptor: QueryInterceptor
|
||||
) : CurrencyApi{
|
||||
|
||||
// okkHttpclient with injected interceptors
|
||||
val okkHttpclient = OkHttpClient.Builder()
|
||||
.addInterceptor(queryInterceptor)
|
||||
.addNetworkInterceptor(networkConnectionInterceptor)
|
||||
.build()
|
||||
|
||||
// Build retrofit
|
||||
return Retrofit.Builder()
|
||||
.client(okkHttpclient)
|
||||
.baseUrl("https://free.currencyconverterapi.com/api/v3/")
|
||||
@@ -33,7 +42,6 @@ interface CurrencyApi {
|
||||
}
|
||||
}
|
||||
|
||||
@GET("convert?")
|
||||
suspend fun getCurrencyRate(@Query("q") currency: String): Response<ResponseObject>
|
||||
|
||||
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
package com.appttude.h_mal.easycc.mvvm.data.network.interceptors
|
||||
package com.appttude.h_mal.easycc.data.network.interceptors
|
||||
|
||||
import android.content.Context
|
||||
import android.net.ConnectivityManager
|
||||
@@ -1,8 +1,5 @@
|
||||
package com.appttude.h_mal.easycc.mvvm.data.network.interceptors
|
||||
package com.appttude.h_mal.easycc.data.network.interceptors
|
||||
|
||||
import android.content.Context
|
||||
import android.net.ConnectivityManager
|
||||
import android.net.NetworkCapabilities
|
||||
import com.appttude.h_mal.easycc.BuildConfig
|
||||
import okhttp3.HttpUrl
|
||||
import okhttp3.Interceptor
|
||||
@@ -1,6 +1,6 @@
|
||||
package com.appttude.h_mal.easycc.mvvm.data.network.response
|
||||
package com.appttude.h_mal.easycc.data.network.response
|
||||
|
||||
import com.appttude.h_mal.easycc.mvvm.models.CurrencyObject
|
||||
import com.appttude.h_mal.easycc.models.CurrencyObject
|
||||
import com.google.gson.annotations.SerializedName
|
||||
|
||||
class ResponseObject(
|
||||
@@ -1,4 +1,4 @@
|
||||
package com.appttude.h_mal.easycc.mvvm.data.prefs
|
||||
package com.appttude.h_mal.easycc.data.prefs
|
||||
|
||||
import android.content.Context
|
||||
import android.content.SharedPreferences
|
||||
@@ -1,6 +1,6 @@
|
||||
package com.appttude.h_mal.easycc.mvvm.data.repository
|
||||
package com.appttude.h_mal.easycc.data.repository
|
||||
|
||||
import com.appttude.h_mal.easycc.mvvm.data.network.response.ResponseObject
|
||||
import com.appttude.h_mal.easycc.data.network.response.ResponseObject
|
||||
|
||||
/**
|
||||
* Main entry point for accessing currency data.
|
||||
@@ -1,12 +1,12 @@
|
||||
package com.appttude.h_mal.easycc.mvvm.data.repository
|
||||
package com.appttude.h_mal.easycc.data.repository
|
||||
|
||||
import android.content.Context
|
||||
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.CurrencyApi
|
||||
import com.appttude.h_mal.easycc.mvvm.utils.convertPairsListToString
|
||||
import com.appttude.h_mal.easycc.data.network.SafeApiRequest
|
||||
import com.appttude.h_mal.easycc.data.network.api.CurrencyApi
|
||||
import com.appttude.h_mal.easycc.data.network.response.ResponseObject
|
||||
import com.appttude.h_mal.easycc.data.prefs.PreferenceProvider
|
||||
import com.appttude.h_mal.easycc.utils.convertPairsListToString
|
||||
|
||||
/**
|
||||
* Default implementation of [Repository]. Single entry point for managing currency' data.
|
||||
@@ -1,161 +0,0 @@
|
||||
package com.appttude.h_mal.easycc.legacy;
|
||||
|
||||
import android.app.PendingIntent;
|
||||
import android.appwidget.AppWidgetManager;
|
||||
import android.appwidget.AppWidgetProvider;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.os.AsyncTask;
|
||||
import android.text.TextUtils;
|
||||
import android.util.Log;
|
||||
import android.widget.RemoteViews;
|
||||
|
||||
import com.appttude.h_mal.easycc.R;
|
||||
|
||||
import org.json.JSONException;
|
||||
import org.json.JSONObject;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.URL;
|
||||
|
||||
import static com.appttude.h_mal.easycc.legacy.CurrencyAppWidgetConfigureActivity.loadTitlePref;
|
||||
import static com.appttude.h_mal.easycc.legacy.PublicMethods.UriBuilder;
|
||||
import static com.appttude.h_mal.easycc.legacy.PublicMethods.createUrl;
|
||||
import static com.appttude.h_mal.easycc.legacy.PublicMethods.makeHttpRequest;
|
||||
import static com.appttude.h_mal.easycc.legacy.PublicMethods.round;
|
||||
|
||||
/**
|
||||
* Implementation of App Widget functionality.
|
||||
* App Widget Configuration implemented in {@link CurrencyAppWidgetConfigureActivity CurrencyAppWidgetConfigureActivity}
|
||||
*/
|
||||
public class CurrencyAppWidget extends AppWidgetProvider {
|
||||
|
||||
static String LOG_TAG = CurrencyAppWidget.class.getSimpleName();
|
||||
|
||||
static void updateAppWidget(Context context, AppWidgetManager appWidgetManager,
|
||||
int appWidgetId) {
|
||||
|
||||
String s1 = loadTitlePref(context,appWidgetId,0);
|
||||
String s2 = loadTitlePref(context,appWidgetId,1);
|
||||
|
||||
String URL = UriBuilder(s1,s2);
|
||||
WidgetAsyncTask widgetAsyncTask = new WidgetAsyncTask(context,appWidgetId,appWidgetManager,s1.substring(0,3),s2.substring(0,3));
|
||||
widgetAsyncTask.execute(URL);
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) {
|
||||
// There may be multiple widgets active, so update all of them
|
||||
for (int appWidgetId : appWidgetIds) {
|
||||
updateAppWidget(context, appWidgetManager, appWidgetId);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDeleted(Context context, int[] appWidgetIds) {
|
||||
// When the user deletes the widget, delete the preference associated with it.
|
||||
for (int appWidgetId : appWidgetIds) {
|
||||
CurrencyAppWidgetConfigureActivity.deleteTitlePref(context, appWidgetId);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onEnabled(Context context) {
|
||||
// Enter relevant functionality for when the first widget is created
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDisabled(Context context) {
|
||||
// Enter relevant functionality for when the last widget is disabled
|
||||
}
|
||||
|
||||
private static class WidgetAsyncTask extends AsyncTask<String, Void, Double> {
|
||||
|
||||
private Context context;
|
||||
private int appWidgetId;
|
||||
private AppWidgetManager appWidgetManager;
|
||||
private String s1;
|
||||
private String s2;
|
||||
|
||||
public WidgetAsyncTask(Context context, int appWidgetId, AppWidgetManager appWidgetManager, String s1, String s2) {
|
||||
this.context = context;
|
||||
this.appWidgetId = appWidgetId;
|
||||
this.appWidgetManager = appWidgetManager;
|
||||
this.s1 = s1;
|
||||
this.s2 = s2;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Double doInBackground(String... urlString) {
|
||||
String jsonResponse = null;
|
||||
|
||||
if (urlString.length < 1 || urlString[0] == null) {
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
URL url = createUrl(urlString[0]);
|
||||
jsonResponse = makeHttpRequest(url);
|
||||
|
||||
} catch (IOException e) {
|
||||
Log.e(getClass().getSimpleName(), "Problem making the HTTP request.", e);
|
||||
}
|
||||
|
||||
|
||||
return extractFeatureFromJson(jsonResponse, s1, s2);
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPostExecute(Double result) {
|
||||
super.onPostExecute(result);
|
||||
|
||||
RemoteViews views = new RemoteViews(context.getPackageName(), R.layout.currency_app_widget);
|
||||
views.setTextViewText(R.id.exchangeName, s1 + s2);
|
||||
views.setTextViewText(R.id.exchangeRate, round(result,2)+"");
|
||||
|
||||
float opacity = 0.3f; //opacity = 0: fully transparent, opacity = 1: no transparancy
|
||||
int backgroundColor = 0x000000; //background color (here black)
|
||||
views.setInt( R.id.widget_view, "setBackgroundColor", (int)(opacity * 0xFF) << 24 | backgroundColor);
|
||||
|
||||
Intent clickIntentTemplate = new Intent(context, MainActivityJava.class);
|
||||
|
||||
clickIntentTemplate.setAction(Intent.ACTION_MAIN);
|
||||
clickIntentTemplate.addCategory(Intent.CATEGORY_LAUNCHER);
|
||||
clickIntentTemplate.putExtra("parse_1",s1);
|
||||
clickIntentTemplate.putExtra("parse_2",s2);
|
||||
clickIntentTemplate.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_SINGLE_TOP);
|
||||
|
||||
PendingIntent configPendingIntent = PendingIntent.getActivity(context, 0, clickIntentTemplate, PendingIntent.FLAG_UPDATE_CURRENT);
|
||||
views.setOnClickPendingIntent(R.id.widget_view, configPendingIntent);
|
||||
|
||||
// Instruct the widget manager to update the widget
|
||||
appWidgetManager.updateAppWidget(appWidgetId, views);
|
||||
|
||||
}
|
||||
|
||||
private static double extractFeatureFromJson(String newsJSON, String s1, String s2) {
|
||||
double conversionValue = 0.00;
|
||||
|
||||
Log.i(LOG_TAG, "extractFeatureFromJson: " + newsJSON);
|
||||
|
||||
Log.i(LOG_TAG, "extractFeatureFromJson: " + s1 + "_" + s2);
|
||||
|
||||
if (TextUtils.isEmpty(newsJSON)) {
|
||||
return 0.00;
|
||||
}
|
||||
|
||||
try {
|
||||
JSONObject jObject = new JSONObject(newsJSON);
|
||||
conversionValue = jObject.getDouble(s1 + "_" + s2);
|
||||
|
||||
} catch (JSONException e) {
|
||||
|
||||
Log.e("MainActivityJava", "Problem parsing the JSON results", e);
|
||||
}
|
||||
return conversionValue;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,227 +0,0 @@
|
||||
package com.appttude.h_mal.easycc.legacy;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.app.Dialog;
|
||||
import android.appwidget.AppWidgetManager;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.SharedPreferences;
|
||||
import android.os.Bundle;
|
||||
import androidx.annotation.NonNull;
|
||||
import android.text.Editable;
|
||||
import android.text.TextWatcher;
|
||||
import android.view.View;
|
||||
import android.view.Window;
|
||||
import android.view.WindowManager;
|
||||
import android.widget.AdapterView;
|
||||
import android.widget.ArrayAdapter;
|
||||
import android.widget.EditText;
|
||||
import android.widget.ListView;
|
||||
import android.widget.TextView;
|
||||
|
||||
import com.appttude.h_mal.easycc.R;
|
||||
|
||||
/**
|
||||
* The configuration screen for the {@link CurrencyAppWidget CurrencyAppWidget} AppWidget.
|
||||
*/
|
||||
public class CurrencyAppWidgetConfigureActivity extends Activity {
|
||||
|
||||
private static final String PREFS_NAME = "com.example.haimalik.myapplication.CurrencyAppWidget";
|
||||
private static final String PREF_PREFIX_KEY = "appwidget_";
|
||||
private static final int PRIMARY = 0;
|
||||
private static final int SECONDARY = 1;
|
||||
int mAppWidgetId = AppWidgetManager.INVALID_APPWIDGET_ID;
|
||||
|
||||
|
||||
View.OnClickListener mOnClickListener = new View.OnClickListener() {
|
||||
public void onClick(View v) {
|
||||
final Context context = CurrencyAppWidgetConfigureActivity.this;
|
||||
|
||||
final Dialog dialog = new Dialog(context);
|
||||
dialog.requestWindowFeature(Window.FEATURE_NO_TITLE);
|
||||
dialog.getWindow().setBackgroundDrawableResource(android.R.color.transparent);
|
||||
dialog.setCancelable(false);
|
||||
dialog.setContentView(R.layout.confirm_dialog);
|
||||
|
||||
TextView text = (TextView) dialog.findViewById(R.id.confirm_text);
|
||||
text.setText(new StringBuilder().append("Create widget for ")
|
||||
.append(loadTitlePref(context, mAppWidgetId, PRIMARY))
|
||||
.append(loadTitlePref(context, mAppWidgetId, SECONDARY))
|
||||
.append("?").toString());
|
||||
|
||||
TextView yes = dialog.findViewById(R.id.confirm_yes);
|
||||
yes.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View view) {
|
||||
//do for yes
|
||||
// It is the responsibility of the configuration activity to update the app widget
|
||||
AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(context);
|
||||
CurrencyAppWidget.updateAppWidget(context, appWidgetManager, mAppWidgetId);
|
||||
|
||||
// Make sure we pass back the original appWidgetId
|
||||
Intent resultValue = new Intent();
|
||||
resultValue.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, mAppWidgetId);
|
||||
setResult(RESULT_OK, resultValue);
|
||||
finish();
|
||||
}
|
||||
});
|
||||
|
||||
TextView no = dialog.findViewById(R.id.confirm_no);
|
||||
no.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View view) {
|
||||
dialog.dismiss();
|
||||
}
|
||||
});
|
||||
|
||||
dialog.show();
|
||||
|
||||
}
|
||||
};
|
||||
|
||||
public CurrencyAppWidgetConfigureActivity() {
|
||||
super();
|
||||
}
|
||||
|
||||
// Write the prefix to the SharedPreferences object for this widget
|
||||
static void saveCurrencyPref(Context context, int appWidgetId, String text, int item) {
|
||||
SharedPreferences.Editor prefs = context.getSharedPreferences(PREFS_NAME, 0).edit();
|
||||
prefs.putString(PREF_PREFIX_KEY + appWidgetId + "_" + item, text);
|
||||
prefs.apply();
|
||||
}
|
||||
|
||||
// Read the prefix from the SharedPreferences object for this widget.
|
||||
// If there is no preference saved, get the default from a resource
|
||||
public static String loadTitlePref(Context context, int appWidgetId, int item) {
|
||||
SharedPreferences prefs = context.getSharedPreferences(PREFS_NAME, 0);
|
||||
String titleValue = prefs.getString(PREF_PREFIX_KEY + appWidgetId + "_" + item, null);
|
||||
if (titleValue != null) {
|
||||
return titleValue;
|
||||
} else {
|
||||
return context.getResources().getStringArray(R.array.currency_arrays)[0].substring(0,3);
|
||||
}
|
||||
}
|
||||
|
||||
static void deleteTitlePref(Context context, int appWidgetId) {
|
||||
SharedPreferences.Editor prefs = context.getSharedPreferences(PREFS_NAME, 0).edit();
|
||||
prefs.remove(PREF_PREFIX_KEY+ appWidgetId + "_0");
|
||||
prefs.remove(PREF_PREFIX_KEY+ appWidgetId + "_1");
|
||||
prefs.apply();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreate(Bundle icicle) {
|
||||
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);
|
||||
|
||||
setContentView(R.layout.currency_app_widget_configure);
|
||||
|
||||
final TextView currencyOne = findViewById(R.id.currency_one);
|
||||
final TextView currencyTwo = findViewById(R.id.currency_two);
|
||||
TextView submit = findViewById(R.id.submit_widget);
|
||||
|
||||
// Find the widget id from the intent.
|
||||
Intent intent = getIntent();
|
||||
Bundle extras = intent.getExtras();
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
currencyOne.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View view) {
|
||||
CustomDialogClass dialogClass = new CustomDialogClass(CurrencyAppWidgetConfigureActivity.this,currencyOne,PRIMARY);
|
||||
dialogClass.show();
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
currencyTwo.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View view) {
|
||||
CustomDialogClass dialogClass = new CustomDialogClass(CurrencyAppWidgetConfigureActivity.this,currencyTwo,SECONDARY);
|
||||
dialogClass.show();
|
||||
}
|
||||
});
|
||||
|
||||
submit.setOnClickListener(mOnClickListener);
|
||||
|
||||
}
|
||||
|
||||
private class CustomDialogClass extends Dialog implements android.view.View.OnClickListener{
|
||||
|
||||
Context context;
|
||||
ListView listView;
|
||||
TextView textView;
|
||||
EditText editText;
|
||||
int item;
|
||||
|
||||
public CustomDialogClass(@NonNull Context context, TextView textView, int item) {
|
||||
super(context);
|
||||
this.context = context;
|
||||
this.textView = textView;
|
||||
this.item = item;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
setContentView(R.layout.custom_dialog);
|
||||
|
||||
getWindow().setBackgroundDrawableResource(android.R.color.transparent);
|
||||
|
||||
getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE);
|
||||
|
||||
listView = (ListView) findViewById(R.id.list_view);
|
||||
editText = (EditText) findViewById(R.id.search_text) ;
|
||||
|
||||
final ArrayAdapter<CharSequence> arrayAdapter = ArrayAdapter.createFromResource(context,R.array.currency_arrays,android.R.layout.simple_list_item_1);
|
||||
listView.setAdapter(arrayAdapter);
|
||||
|
||||
editText.addTextChangedListener(new TextWatcher() {
|
||||
@Override
|
||||
public void beforeTextChanged(CharSequence charSequence, int i, int i1, int i2) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTextChanged(CharSequence charSequence, int i, int i1, int i2) {
|
||||
arrayAdapter.getFilter().filter(charSequence);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void afterTextChanged(Editable editable) {
|
||||
|
||||
}
|
||||
});
|
||||
|
||||
listView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
|
||||
@Override
|
||||
public void onItemClick(AdapterView<?> adapterView, View view, int i, long l) {
|
||||
String text = adapterView.getItemAtPosition(i).toString().substring(0,3);
|
||||
textView.setText(adapterView.getItemAtPosition(i).toString());
|
||||
saveCurrencyPref(context,mAppWidgetId,text,item);
|
||||
dismiss();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onClick(View view) {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,378 +0,0 @@
|
||||
package com.appttude.h_mal.easycc.legacy;
|
||||
|
||||
import android.app.Dialog;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.SharedPreferences;
|
||||
import android.os.AsyncTask;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.appcompat.app.AppCompatActivity;
|
||||
import android.os.Bundle;
|
||||
import android.text.Editable;
|
||||
import android.text.TextUtils;
|
||||
import android.text.TextWatcher;
|
||||
import android.util.Log;
|
||||
import android.view.View;
|
||||
import android.view.WindowManager;
|
||||
import android.view.animation.AlphaAnimation;
|
||||
import android.widget.AdapterView;
|
||||
import android.widget.ArrayAdapter;
|
||||
import android.widget.EditText;
|
||||
import android.widget.LinearLayout;
|
||||
import android.widget.ListView;
|
||||
import android.widget.ProgressBar;
|
||||
import android.widget.TextView;
|
||||
import android.widget.Toast;
|
||||
|
||||
import com.appttude.h_mal.easycc.R;
|
||||
|
||||
import org.json.JSONException;
|
||||
import org.json.JSONObject;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.URL;
|
||||
|
||||
import java.text.DecimalFormat;
|
||||
|
||||
import static com.appttude.h_mal.easycc.legacy.PublicMethods.UriBuilder;
|
||||
import static com.appttude.h_mal.easycc.legacy.PublicMethods.createUrl;
|
||||
import static com.appttude.h_mal.easycc.legacy.PublicMethods.makeHttpRequest;
|
||||
|
||||
public class MainActivityJava extends AppCompatActivity {
|
||||
|
||||
EditText currencyOneEditText;
|
||||
EditText currencyTwoEditText;
|
||||
TextView currencyOne;
|
||||
TextView currencyTwo;
|
||||
double conversionRateOne;
|
||||
ProgressBar spinner;
|
||||
LinearLayout wholeView;
|
||||
|
||||
private String URL = "https://free.currencyconverterapi.com/api/v3/convert?";
|
||||
private String CURRENCY_ONE = "currency_one_pref";
|
||||
private String CURRENCY_TWO = "currency_two_pref";
|
||||
|
||||
private static final String LOG_TAG = MainActivityJava.class.getSimpleName();
|
||||
|
||||
SharedPreferences pref;
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
setContentView(R.layout.activity_main);
|
||||
|
||||
pref = getApplicationContext().getSharedPreferences("MyPref", MODE_PRIVATE);
|
||||
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);
|
||||
|
||||
currencyOneEditText = (EditText) findViewById(R.id.topInsertValue);
|
||||
currencyTwoEditText = (EditText) findViewById(R.id.bottomInsertValues);
|
||||
|
||||
currencyOne = (TextView) findViewById(R.id.currency_one);
|
||||
currencyTwo = (TextView) findViewById(R.id.currency_two);
|
||||
|
||||
if (getIntent().getExtras() != null) {
|
||||
Bundle b = getIntent().getExtras();
|
||||
currencyOne.setText(arrayEntry(b.getString("parse_1")));
|
||||
currencyTwo.setText(arrayEntry(b.getString("parse_2")));
|
||||
}else{
|
||||
if (pref != null) {
|
||||
currencyOne.setText(pref.getString(CURRENCY_ONE, String.valueOf(getResources().getTextArray(R.array.currency_arrays)[0])));
|
||||
currencyTwo.setText(pref.getString(CURRENCY_TWO, String.valueOf(getResources().getTextArray(R.array.currency_arrays)[0])));
|
||||
}else{
|
||||
currencyOne.setText(getResources().getTextArray(R.array.currency_arrays)[0]);
|
||||
currencyTwo.setText(getResources().getTextArray(R.array.currency_arrays)[0]);
|
||||
}
|
||||
}
|
||||
|
||||
currencyOneEditText.addTextChangedListener(TextWatcherClass);
|
||||
currencyTwoEditText.addTextChangedListener(TextWatcherClass2);
|
||||
|
||||
spinner = (ProgressBar) findViewById(R.id.progressBar);
|
||||
spinner.setVisibility(View.GONE);
|
||||
|
||||
wholeView = findViewById(R.id.whole_view);
|
||||
|
||||
// addListenerOnSpinnerItemSelection();
|
||||
|
||||
currencyOne.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View view) {
|
||||
CustomDialogClass dialogClass = new CustomDialogClass(MainActivityJava.this,currencyOne);
|
||||
dialogClass.show();
|
||||
}
|
||||
});
|
||||
|
||||
currencyTwo.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View view) {
|
||||
CustomDialogClass dialogClass = new CustomDialogClass(MainActivityJava.this,currencyTwo);
|
||||
dialogClass.show();
|
||||
}
|
||||
});
|
||||
|
||||
String stringURL = UriBuilder(currencyOne.getText().toString().substring(0,3),
|
||||
currencyTwo.getText().toString().substring(0,3));
|
||||
MyAsyncTask task = new MyAsyncTask();
|
||||
task.execute(stringURL);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onNewIntent(Intent intent) {
|
||||
super.onNewIntent(intent);
|
||||
|
||||
if (getIntent().getExtras() != null) {
|
||||
Bundle b = getIntent().getExtras();
|
||||
currencyOne.setText(arrayEntry(b.getString("parse_1")));
|
||||
currencyTwo.setText(arrayEntry(b.getString("parse_2")));
|
||||
}
|
||||
}
|
||||
|
||||
private TextWatcher TextWatcherClass = new TextWatcher() {
|
||||
|
||||
@Override
|
||||
public void onTextChanged(CharSequence s, int start, int before,
|
||||
int count) {
|
||||
currencyTwoEditText.removeTextChangedListener(TextWatcherClass2);
|
||||
if(currencyOneEditText.getText().toString().isEmpty()){
|
||||
currencyTwoEditText.setText("");
|
||||
return;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void beforeTextChanged(CharSequence s, int start, int count,
|
||||
int after) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void afterTextChanged(Editable s) {
|
||||
|
||||
try{
|
||||
|
||||
Double topValue = Double.parseDouble(currencyOneEditText.getText().toString());
|
||||
Double bottomValue = topValue * conversionRateOne;
|
||||
DecimalFormat df = new DecimalFormat("#.##");
|
||||
bottomValue = Double.valueOf(df.format(bottomValue));
|
||||
currencyTwoEditText.setText(bottomValue.toString());
|
||||
|
||||
}
|
||||
catch (NumberFormatException e){
|
||||
Log.e(LOG_TAG, "no numbers inserted");
|
||||
}
|
||||
currencyTwoEditText.addTextChangedListener(TextWatcherClass2);
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
private TextWatcher TextWatcherClass2 = new TextWatcher() {
|
||||
|
||||
@Override
|
||||
public void onTextChanged(CharSequence s, int start, int before,
|
||||
int count) {
|
||||
currencyOneEditText.removeTextChangedListener(TextWatcherClass);
|
||||
if(currencyTwoEditText.getText().toString().isEmpty()){
|
||||
currencyOneEditText.setText("");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void beforeTextChanged(CharSequence s, int start, int count,
|
||||
int after) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void afterTextChanged(Editable s) {
|
||||
|
||||
try{
|
||||
|
||||
Double bottomValue = Double.parseDouble(currencyTwoEditText.getText().toString());
|
||||
Double topValue = bottomValue * (1 / conversionRateOne);
|
||||
DecimalFormat df = new DecimalFormat("#.##");
|
||||
topValue = Double.valueOf(df.format(topValue));
|
||||
currencyOneEditText.setText(topValue.toString());
|
||||
|
||||
|
||||
}
|
||||
catch (NumberFormatException e){
|
||||
Log.e(LOG_TAG, "no numbers inserted");
|
||||
}
|
||||
currencyOneEditText.addTextChangedListener(TextWatcherClass);
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
|
||||
|
||||
private double extractFeatureFromJson(String newsJSON) {
|
||||
double conversionValue = 0.00;
|
||||
|
||||
Log.i(LOG_TAG, "extractFeatureFromJson: " + newsJSON);
|
||||
|
||||
String currencyOneVal = currencyOne.getText().toString().substring(0,3);
|
||||
String currencyTwoVal = currencyTwo.getText().toString().substring(0,3);
|
||||
|
||||
Log.i(LOG_TAG, "extractFeatureFromJson: " + currencyOneVal + "_" + currencyTwoVal);
|
||||
|
||||
if (TextUtils.isEmpty(newsJSON)) {
|
||||
return 0.00;
|
||||
}
|
||||
|
||||
try {
|
||||
JSONObject jObject = new JSONObject(newsJSON);
|
||||
conversionValue = jObject.getDouble(currencyOneVal + "_" + currencyTwoVal);
|
||||
|
||||
} catch (JSONException e) {
|
||||
|
||||
Log.e("MainActivityJava", "Problem parsing the JSON results", e);
|
||||
}
|
||||
return conversionValue;
|
||||
}
|
||||
|
||||
class MyAsyncTask extends AsyncTask<String, Void, Double> {
|
||||
|
||||
@Override
|
||||
public Double doInBackground(String... urlString) {
|
||||
String jsonResponse = null;
|
||||
|
||||
if (urlString.length < 1 || urlString[0] == null) {
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
URL url = createUrl(urlString[0]);
|
||||
jsonResponse = makeHttpRequest(url);
|
||||
|
||||
} catch (IOException e) {
|
||||
Log.e(LOG_TAG, "Problem making the HTTP request.", e);
|
||||
}
|
||||
|
||||
if (jsonResponse.equals("")){
|
||||
return null;
|
||||
}
|
||||
|
||||
return extractFeatureFromJson(jsonResponse);
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPreExecute() {
|
||||
super.onPreExecute();
|
||||
spinner.setVisibility(View.VISIBLE);
|
||||
AlphaAnimation animation1 = new AlphaAnimation(1.0f, 0.2f);
|
||||
animation1.setDuration(200);
|
||||
wholeView.startAnimation(animation1);
|
||||
wholeView.setAlpha(0.2f);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPostExecute(Double result) {
|
||||
super.onPostExecute(result);
|
||||
|
||||
spinner.setVisibility(View.GONE);
|
||||
wholeView.setAlpha(1.0f);
|
||||
|
||||
if (result == null){
|
||||
Toast.makeText(MainActivityJava.this, "Failed to retrieve exchange rate", Toast.LENGTH_SHORT).show();
|
||||
}else{
|
||||
conversionRateOne = result;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private class CustomDialogClass extends Dialog implements android.view.View.OnClickListener{
|
||||
|
||||
Context context;
|
||||
ListView listView;
|
||||
TextView textView;
|
||||
EditText editText;
|
||||
int selection;
|
||||
|
||||
public CustomDialogClass(@NonNull Context context, TextView textView) {
|
||||
super(context);
|
||||
this.context = context;
|
||||
this.textView = textView;
|
||||
if (textView.getId() == R.id.currency_one){
|
||||
selection = 1;
|
||||
}else{
|
||||
selection = 2;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
setContentView(R.layout.custom_dialog);
|
||||
|
||||
getWindow().setBackgroundDrawableResource(android.R.color.transparent);
|
||||
|
||||
getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE);
|
||||
|
||||
listView = (ListView) findViewById(R.id.list_view);
|
||||
editText = (EditText) findViewById(R.id.search_text) ;
|
||||
|
||||
final ArrayAdapter<CharSequence> arrayAdapter = ArrayAdapter.createFromResource(context,R.array.currency_arrays,android.R.layout.simple_list_item_1);
|
||||
listView.setAdapter(arrayAdapter);
|
||||
|
||||
editText.addTextChangedListener(new TextWatcher() {
|
||||
@Override
|
||||
public void beforeTextChanged(CharSequence charSequence, int i, int i1, int i2) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTextChanged(CharSequence charSequence, int i, int i1, int i2) {
|
||||
arrayAdapter.getFilter().filter(charSequence);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void afterTextChanged(Editable editable) {
|
||||
|
||||
}
|
||||
});
|
||||
|
||||
listView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
|
||||
@Override
|
||||
public void onItemClick(AdapterView<?> adapterView, View view, int i, long l) {
|
||||
textView.setText(adapterView.getItemAtPosition(i).toString());
|
||||
SharedPreferences.Editor editor = pref.edit();
|
||||
if (selection == 1) {
|
||||
editor.putString(CURRENCY_ONE,adapterView.getItemAtPosition(i).toString());
|
||||
}else{
|
||||
editor.putString(CURRENCY_TWO,adapterView.getItemAtPosition(i).toString());
|
||||
}
|
||||
editor.apply();
|
||||
currencyOneEditText.setText("");
|
||||
currencyTwoEditText.setText("");
|
||||
String stringURL = UriBuilder(currencyOne.getText().toString().substring(0,3),
|
||||
currencyTwo.getText().toString().substring(0,3));
|
||||
MyAsyncTask task = new MyAsyncTask();
|
||||
task.execute(stringURL);
|
||||
dismiss();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onClick(View view) {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
private String arrayEntry (String s){
|
||||
String[] strings = getResources().getStringArray(R.array.currency_arrays);
|
||||
String returnString = strings[0];
|
||||
for (String string : strings) {
|
||||
if (s.equals(string.substring(0, 3))) {
|
||||
returnString = string;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
return returnString;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -1,108 +0,0 @@
|
||||
package com.appttude.h_mal.easycc.legacy;
|
||||
|
||||
import android.net.Uri;
|
||||
import android.util.Log;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.net.HttpURLConnection;
|
||||
import java.net.MalformedURLException;
|
||||
import java.net.URL;
|
||||
import java.nio.charset.Charset;
|
||||
|
||||
public class PublicMethods {
|
||||
|
||||
private static String TAG = PublicMethods.class.getSimpleName();
|
||||
private static String URL = "https://free.currencyconverterapi.com/api/v3/convert?";
|
||||
|
||||
public PublicMethods() {
|
||||
}
|
||||
|
||||
public static String UriBuilder(String s1, String s2){
|
||||
s1 = s1.substring(0,3);
|
||||
s2 = s2.substring(0,3);
|
||||
|
||||
Uri baseUri = Uri.parse(URL);
|
||||
Uri.Builder builder = baseUri.buildUpon();
|
||||
builder.appendQueryParameter("q", s1 + "_" + s2)
|
||||
.appendQueryParameter("compact", "ultra")
|
||||
.appendQueryParameter("apiKey", "a4f93cc2ff05dd772321");
|
||||
|
||||
return builder.build().toString();
|
||||
|
||||
}
|
||||
|
||||
public static java.net.URL createUrl(String stringUrl) {
|
||||
URL url = null;
|
||||
try {
|
||||
url = new URL(stringUrl);
|
||||
} catch (MalformedURLException e) {
|
||||
Log.e(TAG, "Error with creating URL ", e);
|
||||
}
|
||||
return url;
|
||||
}
|
||||
|
||||
|
||||
public static String makeHttpRequest(URL url) throws IOException {
|
||||
String jsonResponse = "";
|
||||
|
||||
if (url == null) {
|
||||
return jsonResponse;
|
||||
}
|
||||
|
||||
HttpURLConnection urlConnection = null;
|
||||
InputStream inputStream = null;
|
||||
try {
|
||||
urlConnection = (HttpURLConnection) url.openConnection();
|
||||
urlConnection.setReadTimeout(30000);
|
||||
urlConnection.setConnectTimeout(30000);
|
||||
urlConnection.setRequestMethod("GET");
|
||||
urlConnection.connect();
|
||||
|
||||
if (urlConnection.getResponseCode() == 200) {
|
||||
inputStream = urlConnection.getInputStream();
|
||||
jsonResponse = readFromStream(inputStream);
|
||||
} else {
|
||||
Log.e(TAG, "Error response code: " + urlConnection.getResponseCode());
|
||||
}
|
||||
} catch (IOException e) {
|
||||
Log.e(TAG, "Problem retrieving the JSON results.", e);
|
||||
} finally {
|
||||
if (urlConnection != null) {
|
||||
urlConnection.disconnect();
|
||||
}
|
||||
if (inputStream != null) {
|
||||
inputStream.close();
|
||||
}
|
||||
}
|
||||
return jsonResponse;
|
||||
}
|
||||
|
||||
private static String readFromStream(InputStream inputStream) throws IOException {
|
||||
StringBuilder output = new StringBuilder();
|
||||
if (inputStream != null) {
|
||||
InputStreamReader inputStreamReader = new InputStreamReader(inputStream, Charset.forName("UTF-8"));
|
||||
BufferedReader reader = new BufferedReader(inputStreamReader);
|
||||
String line = "";
|
||||
while (line != null) {
|
||||
output.append(line);
|
||||
line = reader.readLine();
|
||||
}
|
||||
}
|
||||
Log.d(TAG, output.toString());
|
||||
return output.toString();
|
||||
|
||||
}
|
||||
|
||||
public static double round(double value, int places) {
|
||||
if (places < 0) throw new IllegalArgumentException();
|
||||
|
||||
long factor = (long) Math.pow(10, places);
|
||||
value = value * factor;
|
||||
long tmp = Math.round(value);
|
||||
return (double) tmp / factor;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
package com.appttude.h_mal.easycc.mvvm.models
|
||||
package com.appttude.h_mal.easycc.models
|
||||
|
||||
import com.google.gson.annotations.SerializedName
|
||||
|
||||
@@ -1,7 +0,0 @@
|
||||
package com.appttude.h_mal.easycc.mvvm.ui.app
|
||||
|
||||
interface RateListener {
|
||||
fun onStarted()
|
||||
fun onSuccess()
|
||||
fun onFailure(message: String)
|
||||
}
|
||||
@@ -1,70 +0,0 @@
|
||||
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.mvvm.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)
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,59 +0,0 @@
|
||||
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 com.appttude.h_mal.easycc.mvvm.utils.transformIntToArray
|
||||
import kotlinx.android.synthetic.main.confirm_dialog.*
|
||||
|
||||
|
||||
/**
|
||||
* Dialog created when submitting the completed selections
|
||||
* in [CurrencyAppWidgetConfigureActivityKotlin]
|
||||
*/
|
||||
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)
|
||||
// layer behind dialog to be transparent
|
||||
window!!.setBackgroundDrawableResource(android.R.color.transparent)
|
||||
// Dialog cannot be cancelled by clicking away
|
||||
setCancelable(false)
|
||||
|
||||
confirm_text.text = StringBuilder().append("Create widget for ")
|
||||
.append(viewModel.getWidgetStringName())
|
||||
.append("?").toString()
|
||||
|
||||
confirm_yes.setOnClickListener {
|
||||
// It is the responsibility of the configuration activity to update the app widget
|
||||
// Send update broadcast to widget app class
|
||||
Intent(AppWidgetManager.ACTION_APPWIDGET_UPDATE,
|
||||
null,
|
||||
context,
|
||||
CurrencyAppWidgetKotlin::class.java).apply {
|
||||
// Save current widget pairs
|
||||
viewModel.setWidgetStored()
|
||||
// Put current app widget ID into extras and send broadcast
|
||||
putExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS, transformIntToArray(appWidgetId) )
|
||||
activity.sendBroadcast(this)
|
||||
}
|
||||
|
||||
|
||||
// Make sure we pass back the original appWidgetId
|
||||
val resultValue = activity.intent
|
||||
resultValue.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetId)
|
||||
activity.setResult(Activity.RESULT_OK, resultValue)
|
||||
activity.finish()
|
||||
}
|
||||
|
||||
confirm_no.setOnClickListener { dismiss() }
|
||||
}
|
||||
}
|
||||
@@ -1,83 +0,0 @@
|
||||
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.RepositoryImpl
|
||||
import com.appttude.h_mal.easycc.mvvm.ui.app.RateListener
|
||||
|
||||
class WidgetViewModel(
|
||||
private val repository: RepositoryImpl
|
||||
) : 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)
|
||||
|
||||
rateIdFrom.value = widgetString.first
|
||||
rateIdTo.value = widgetString.second
|
||||
|
||||
}
|
||||
|
||||
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)
|
||||
|
||||
|
||||
}
|
||||
@@ -1,22 +0,0 @@
|
||||
package com.appttude.h_mal.easycc.mvvm.utils
|
||||
|
||||
import java.text.DecimalFormat
|
||||
|
||||
fun transformIntToArray(int: Int): IntArray{
|
||||
return intArrayOf(int)
|
||||
}
|
||||
|
||||
fun String.trimToThree(): String{
|
||||
if (this.length > 3){
|
||||
return this.substring(0, 3)
|
||||
}
|
||||
return this
|
||||
}
|
||||
|
||||
fun convertPairsListToString(s1: String, s2: String): String =
|
||||
"${s1.trimToThree()}_${s2.trimToThree()}"
|
||||
|
||||
fun Double.toTwoDp() = run {
|
||||
val df = DecimalFormat("#.##")
|
||||
java.lang.Double.valueOf(df.format(this))
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
package com.appttude.h_mal.easycc.mvvm.ui.app
|
||||
package com.appttude.h_mal.easycc.ui.main
|
||||
|
||||
import android.app.Dialog
|
||||
import android.content.Context
|
||||
@@ -10,6 +10,9 @@ import android.widget.ArrayAdapter
|
||||
import com.appttude.h_mal.easycc.R
|
||||
import kotlinx.android.synthetic.main.custom_dialog.*
|
||||
|
||||
/**
|
||||
* Custom dialog when selecting currencies from list with filter
|
||||
*/
|
||||
class CustomDialogClass(
|
||||
context: Context,
|
||||
private val clickListener: ClickListener
|
||||
@@ -19,9 +22,12 @@ class CustomDialogClass(
|
||||
super.onCreate(savedInstanceState)
|
||||
setContentView(R.layout.custom_dialog)
|
||||
|
||||
// Transparent background
|
||||
window!!.setBackgroundDrawableResource(android.R.color.transparent)
|
||||
// Keyboard not to overlap dialog
|
||||
window!!.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE)
|
||||
|
||||
// array adapter for list of currencies in R.Strings
|
||||
val arrayAdapter =
|
||||
ArrayAdapter.createFromResource(
|
||||
context, R.array.currency_arrays,
|
||||
@@ -29,6 +35,7 @@ class CustomDialogClass(
|
||||
|
||||
list_view.adapter = arrayAdapter
|
||||
|
||||
// Edit text to filter @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) {
|
||||
@@ -37,6 +44,7 @@ class CustomDialogClass(
|
||||
override fun afterTextChanged(editable: Editable) {}
|
||||
})
|
||||
|
||||
// interface selection back to calling activity
|
||||
list_view.setOnItemClickListener{ adapterView, _, i, _ ->
|
||||
clickListener.onText(adapterView.getItemAtPosition(i).toString())
|
||||
dismiss()
|
||||
@@ -44,6 +52,7 @@ class CustomDialogClass(
|
||||
}
|
||||
}
|
||||
|
||||
// Interface to handle selection within dialog
|
||||
interface ClickListener{
|
||||
fun onText(currencyName: String)
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
package com.appttude.h_mal.easycc.mvvm.ui.app
|
||||
package com.appttude.h_mal.easycc.ui.main
|
||||
|
||||
import android.os.Bundle
|
||||
import android.text.Editable
|
||||
@@ -12,15 +12,15 @@ import androidx.lifecycle.Observer
|
||||
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.mvvm.utils.DisplayToast
|
||||
import com.appttude.h_mal.easycc.mvvm.utils.clearEditText
|
||||
import com.appttude.h_mal.easycc.mvvm.utils.hideView
|
||||
import com.appttude.h_mal.easycc.utils.clearEditText
|
||||
import com.appttude.h_mal.easycc.utils.displayToast
|
||||
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 {
|
||||
class MainActivity : AppCompatActivity(), KodeinAware, View.OnClickListener {
|
||||
|
||||
override val kodein by kodein()
|
||||
// Retrieve MainViewModelFactory via dependency injection
|
||||
@@ -56,15 +56,19 @@ class MainActivity : AppCompatActivity(), RateListener, KodeinAware, View.OnClic
|
||||
|
||||
private fun setUpObservers() {
|
||||
viewModel.operationStartedListener.observe(this, Observer {
|
||||
// Show progress bar
|
||||
progressBar.hideView(false)
|
||||
})
|
||||
viewModel.operationFinishedListener.observe(this, Observer { pair ->
|
||||
// hide progress bar
|
||||
progressBar.hideView(true)
|
||||
if (pair.first){
|
||||
// Operation was successful remove text in EditTexts
|
||||
bottomInsertValues.clearEditText()
|
||||
topInsertValue.clearEditText()
|
||||
}else{
|
||||
pair.second?.let { DisplayToast(it) }
|
||||
// Display Toast with error message returned from Viewmodel
|
||||
pair.second?.let { displayToast(it) }
|
||||
}
|
||||
})
|
||||
}
|
||||
@@ -78,38 +82,25 @@ class MainActivity : AppCompatActivity(), RateListener, KodeinAware, View.OnClic
|
||||
}
|
||||
|
||||
private fun showCustomDialog(view: View?) {
|
||||
|
||||
val dialogClass = CustomDialogClass(this, object : ClickListener {
|
||||
CustomDialogClass(this, object : ClickListener {
|
||||
override fun onText(currencyName: String) {
|
||||
(view as TextView).text = currencyName
|
||||
viewModel.setCurrencyName(view.tag, currencyName)
|
||||
}
|
||||
})
|
||||
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)
|
||||
}).show()
|
||||
}
|
||||
|
||||
override fun onClick(view: View?) {
|
||||
showCustomDialog(view)
|
||||
}
|
||||
|
||||
// Text watcher applied to EditText @topInsertValue
|
||||
private val textWatcherClass: TextWatcher = object : TextWatcher {
|
||||
override fun onTextChanged(s: CharSequence, st: Int, b: Int, c: Int) {
|
||||
// Remove text watcher on other text watcher to prevent infinite loop
|
||||
bottomInsertValues.removeTextChangedListener(textWatcherClass2)
|
||||
// Clear any values if current EditText is empty
|
||||
if (topInsertValue.text.isNullOrEmpty())
|
||||
bottomInsertValues.setText("")
|
||||
}
|
||||
@@ -117,12 +108,14 @@ class MainActivity : AppCompatActivity(), RateListener, KodeinAware, View.OnClic
|
||||
override fun beforeTextChanged(s: CharSequence, start: Int, count: Int, after: Int) {}
|
||||
override fun afterTextChanged(s: Editable) {
|
||||
bottomInsertValues.setText(viewModel.getConversion(s.toString()))
|
||||
// add Text watcher back as it is safe to do so
|
||||
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()
|
||||
@@ -1,26 +1,28 @@
|
||||
package com.appttude.h_mal.easycc.mvvm.ui.app
|
||||
package com.appttude.h_mal.easycc.ui.main
|
||||
|
||||
import android.os.Bundle
|
||||
import android.util.Log
|
||||
import android.widget.EditText
|
||||
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.utils.toTwoDp
|
||||
import kotlinx.android.synthetic.main.activity_main.*
|
||||
import com.appttude.h_mal.easycc.data.repository.Repository
|
||||
import com.appttude.h_mal.easycc.utils.toTwoDpString
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.launch
|
||||
import java.io.IOException
|
||||
import java.text.DecimalFormat
|
||||
|
||||
/**
|
||||
* ViewModel for the task Main Activity Screen
|
||||
*/
|
||||
private const val TAG = "MainViewModel"
|
||||
class MainViewModel(
|
||||
// Repository injected via Viewmodel factory
|
||||
private val repository: Repository
|
||||
) : ViewModel(){
|
||||
|
||||
private val conversionPairs by lazy { repository.getConversionPair() }
|
||||
|
||||
// Viewbinding to textviews in @activity_main.xml
|
||||
var rateIdFrom: String? = null
|
||||
var rateIdTo: String? = null
|
||||
|
||||
@@ -30,36 +32,42 @@ class MainViewModel(
|
||||
|
||||
private var conversionRate: Double = 0.00
|
||||
|
||||
fun getExchangeRate(){
|
||||
|
||||
operationStartedListener.postValue(false)
|
||||
private fun getExchangeRate(){
|
||||
operationStartedListener.postValue(true)
|
||||
|
||||
// Null check on currency values
|
||||
if (rateIdFrom.isNullOrEmpty() || rateIdTo.isNullOrEmpty()){
|
||||
operationFinishedListener.postValue(Pair(false, "Select currencies"))
|
||||
return
|
||||
}
|
||||
|
||||
// No need to call api as it will return exchange rate as 1
|
||||
if (rateIdFrom == rateIdTo){
|
||||
conversionRate = 1.00
|
||||
operationFinishedListener.postValue(Pair(true, null))
|
||||
return
|
||||
}
|
||||
|
||||
// Open Coroutine on IO thread to carry out async task
|
||||
CoroutineScope(Dispatchers.IO).launch {
|
||||
try {
|
||||
// Non-null assertion (!!) as values have been null checked and have not changed
|
||||
val exchangeResponse = repository.getData(rateIdFrom!!, rateIdTo!!)
|
||||
repository.setConversionPair(rateIdFrom!!, rateIdTo!!)
|
||||
|
||||
exchangeResponse.results?.iterator()?.next()?.value?.let {
|
||||
// Response Successful and contains @param CurrencyObject
|
||||
repository.setConversionPair(rateIdFrom!!, rateIdTo!!)
|
||||
|
||||
operationFinishedListener.postValue(Pair(true, null))
|
||||
conversionRate = it.value
|
||||
return@launch
|
||||
}
|
||||
}catch(e: IOException){
|
||||
operationFinishedListener.postValue(Pair(false, e.message ?: "Currency Retrieval failed"))
|
||||
e.message?.let {
|
||||
operationFinishedListener.postValue(Pair(false, it))
|
||||
return@launch
|
||||
}
|
||||
|
||||
}
|
||||
operationFinishedListener.postValue(Pair(false, "Failed to retrieve rate"))
|
||||
}
|
||||
}
|
||||
@@ -67,8 +75,8 @@ class MainViewModel(
|
||||
fun getConversion(fromValue: String): String? {
|
||||
return try {
|
||||
val fromValDouble = fromValue.toDouble()
|
||||
val bottomVal1 = (fromValDouble * conversionRate).toTwoDp()
|
||||
bottomVal1.toBigDecimal().toPlainString()
|
||||
val bottomVal1 = (fromValDouble * conversionRate)
|
||||
bottomVal1.toTwoDpString()
|
||||
}catch (e: NumberFormatException) {
|
||||
Log.e(TAG, "no numbers inserted")
|
||||
null
|
||||
@@ -78,23 +86,26 @@ class MainViewModel(
|
||||
fun getReciprocalConversion(toValue: String): String? {
|
||||
return try {
|
||||
val toDoubleVal = toValue.toDouble()
|
||||
val newTopVal = toDoubleVal.times((1/conversionRate)).toTwoDp()
|
||||
newTopVal.toBigDecimal().toPlainString()
|
||||
val newTopVal = toDoubleVal.times((1/conversionRate))
|
||||
newTopVal.toTwoDpString()
|
||||
} catch (e: NumberFormatException) {
|
||||
Log.e(TAG, "no numbers inserted")
|
||||
null
|
||||
}
|
||||
}
|
||||
|
||||
// Start operation based on dialog selection
|
||||
fun setCurrencyName(tag: Any?, currencyName: String){
|
||||
if (tag.toString() == "top"){
|
||||
rateIdFrom = currencyName
|
||||
}else{
|
||||
rateIdTo = currencyName
|
||||
when(tag.toString()){
|
||||
"top" -> rateIdFrom = currencyName
|
||||
"bottom" -> rateIdTo = currencyName
|
||||
else -> { return }
|
||||
}
|
||||
|
||||
getExchangeRate()
|
||||
}
|
||||
|
||||
// Start operation based on possible values stored in bundle or retrieve from repository
|
||||
fun initiate(extras: Bundle?) {
|
||||
rateIdFrom = extras?.getString("parse_1") ?: conversionPairs.first
|
||||
rateIdTo = extras?.getString("parse_2") ?: conversionPairs.second
|
||||
@@ -1,9 +1,13 @@
|
||||
package com.appttude.h_mal.easycc.mvvm.ui.app
|
||||
package com.appttude.h_mal.easycc.ui.main
|
||||
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.ViewModelProvider
|
||||
import com.appttude.h_mal.easycc.mvvm.data.repository.RepositoryImpl
|
||||
import com.appttude.h_mal.easycc.data.repository.RepositoryImpl
|
||||
|
||||
/**
|
||||
* Viewmodel factory for [MainViewModel]
|
||||
* inject repository into viewmodel
|
||||
*/
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
class MainViewModelFactory (
|
||||
private val repository: RepositoryImpl
|
||||
@@ -0,0 +1,151 @@
|
||||
package com.appttude.h_mal.easycc.ui.widget
|
||||
|
||||
import android.app.Activity
|
||||
import android.appwidget.AppWidgetManager
|
||||
import android.content.Intent
|
||||
import android.os.Bundle
|
||||
import android.view.View
|
||||
import android.widget.TextView
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.databinding.DataBindingUtil
|
||||
import androidx.lifecycle.Observer
|
||||
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.ui.main.ClickListener
|
||||
import com.appttude.h_mal.easycc.ui.main.CustomDialogClass
|
||||
import com.appttude.h_mal.easycc.utils.displayToast
|
||||
import com.appttude.h_mal.easycc.utils.transformIntToArray
|
||||
import com.appttude.h_mal.easycc.widget.CurrencyAppWidgetKotlin
|
||||
import kotlinx.android.synthetic.main.currency_app_widget_configure.*
|
||||
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, View.OnClickListener {
|
||||
|
||||
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 setup
|
||||
viewModel = ViewModelProviders.of(this, factory).get(WidgetViewModel::class.java)
|
||||
viewModel.initiate(mAppWidgetId)
|
||||
|
||||
setupDataBinding()
|
||||
setupObserver()
|
||||
setupClickListener()
|
||||
}
|
||||
|
||||
private fun setupClickListener() {
|
||||
submit_widget.setOnClickListener(this)
|
||||
currency_one.setOnClickListener(this)
|
||||
currency_two.setOnClickListener(this)
|
||||
}
|
||||
|
||||
private fun setupObserver() {
|
||||
viewModel.operationFinishedListener.observe(this, Observer {
|
||||
|
||||
// it.first is a the success of the operation
|
||||
if (it.first){
|
||||
displaySubmitDialog()
|
||||
}else{
|
||||
// failed operation - display toast with message from it.second
|
||||
it.second?.let { message -> displayToast(message) }
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
private fun setupDataBinding() {
|
||||
// data binding to @R.layout.currency_app_widget_configure
|
||||
DataBindingUtil.setContentView<CurrencyAppWidgetConfigureBinding>(
|
||||
this,
|
||||
R.layout.currency_app_widget_configure
|
||||
).apply {
|
||||
viewmodel = viewModel
|
||||
lifecycleOwner = this@CurrencyAppWidgetConfigureActivityKotlin
|
||||
}
|
||||
}
|
||||
|
||||
override fun onClick(view: View?) {
|
||||
when (view?.tag.toString()) {
|
||||
"top", "bottom" -> showCustomDialog(view)
|
||||
"submit" -> viewModel.submitSelectionOnClick()
|
||||
else -> {
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun displaySubmitDialog() {
|
||||
val message = viewModel.getSubmitDialogMessage()
|
||||
WidgetSubmitDialog(this, message, object : DialogSubmit {
|
||||
override fun onSubmit() {
|
||||
sendUpdateIntent()
|
||||
finishCurrencyWidgetActivity()
|
||||
}
|
||||
}).show()
|
||||
}
|
||||
|
||||
|
||||
private fun showCustomDialog(view: View?) {
|
||||
CustomDialogClass(this, object : ClickListener {
|
||||
override fun onText(currencyName: String) {
|
||||
(view as TextView).text = currencyName
|
||||
viewModel.setCurrencyName(view.tag, currencyName)
|
||||
}
|
||||
}).show()
|
||||
}
|
||||
|
||||
fun finishCurrencyWidgetActivity(){
|
||||
// Make sure we pass back the original appWidgetId
|
||||
val resultValue = intent
|
||||
resultValue.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, mAppWidgetId)
|
||||
setResult(Activity.RESULT_OK, resultValue)
|
||||
finish()
|
||||
}
|
||||
|
||||
fun sendUpdateIntent() {
|
||||
// It is the responsibility of the configuration activity to update the app widget
|
||||
// Send update broadcast to widget app class
|
||||
Intent(this@CurrencyAppWidgetConfigureActivityKotlin,
|
||||
CurrencyAppWidgetKotlin::class.java
|
||||
).apply {
|
||||
action = AppWidgetManager.ACTION_APPWIDGET_UPDATE
|
||||
viewModel.setWidgetStored()
|
||||
|
||||
// Put current app widget ID into extras and send broadcast
|
||||
putExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS, transformIntToArray(mAppWidgetId))
|
||||
sendBroadcast(this)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
package com.appttude.h_mal.easycc.mvvm.ui.widget
|
||||
package com.appttude.h_mal.easycc.ui.widget
|
||||
|
||||
import android.app.Dialog
|
||||
import android.content.Context
|
||||
@@ -15,7 +15,7 @@ widget for when submitting the completed selections
|
||||
*/
|
||||
class WidgetItemSelectDialog(
|
||||
context: Context,
|
||||
val dialogResult: DialogResult
|
||||
private val dialogResult: DialogResult
|
||||
) :Dialog(context){
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
@@ -0,0 +1,38 @@
|
||||
package com.appttude.h_mal.easycc.ui.widget
|
||||
|
||||
import android.app.Dialog
|
||||
import android.content.Context
|
||||
import android.os.Bundle
|
||||
import com.appttude.h_mal.easycc.R
|
||||
import kotlinx.android.synthetic.main.confirm_dialog.*
|
||||
|
||||
|
||||
/**
|
||||
* Dialog created when submitting the completed selections
|
||||
* in [CurrencyAppWidgetConfigureActivityKotlin]
|
||||
*/
|
||||
class WidgetSubmitDialog(
|
||||
context: Context,
|
||||
private val messageString: String,
|
||||
private val dialogInterface: DialogSubmit
|
||||
) :Dialog(context){
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
setContentView(R.layout.confirm_dialog)
|
||||
// layer behind dialog to be transparent
|
||||
window!!.setBackgroundDrawableResource(android.R.color.transparent)
|
||||
// Dialog cannot be cancelled by clicking away
|
||||
setCancelable(false)
|
||||
|
||||
confirm_text.text = messageString
|
||||
|
||||
// handle dialog buttons
|
||||
confirm_yes.setOnClickListener { dialogInterface.onSubmit() }
|
||||
confirm_no.setOnClickListener { dismiss() }
|
||||
}
|
||||
}
|
||||
|
||||
interface DialogSubmit{
|
||||
fun onSubmit()
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
package com.appttude.h_mal.easycc.ui.widget
|
||||
|
||||
import androidx.lifecycle.MutableLiveData
|
||||
import androidx.lifecycle.ViewModel
|
||||
import com.appttude.h_mal.easycc.data.repository.Repository
|
||||
import com.appttude.h_mal.easycc.utils.trimToThree
|
||||
|
||||
class WidgetViewModel(
|
||||
private val repository: Repository
|
||||
) : ViewModel(){
|
||||
|
||||
private val defaultCurrency: String by lazy { repository.getArrayList()[0] }
|
||||
var appWidgetId: Int? = null
|
||||
|
||||
// data binding to @R.layout.currency_app_widget_configure
|
||||
var rateIdFrom: String? = null
|
||||
var rateIdTo: String? = null
|
||||
|
||||
// Live data to feedback to @CurrencyAppWidgetConfigureActivityKotlin
|
||||
val operationFinishedListener = MutableLiveData<Pair<Boolean, String?>>()
|
||||
|
||||
// Setup viewmodel app widget ID
|
||||
// Set default values for text views
|
||||
fun initiate(appId: Int){
|
||||
appWidgetId = appId
|
||||
val widgetString
|
||||
= repository.getWidgetConversionPairs(appId)
|
||||
|
||||
rateIdFrom = widgetString.first ?: defaultCurrency
|
||||
rateIdTo = widgetString.second ?: defaultCurrency
|
||||
|
||||
}
|
||||
|
||||
// Retrieve name for submit dialog (eg. AUDGBP)
|
||||
fun getSubmitDialogMessage(): String {
|
||||
val widgetName = getWidgetStringName()
|
||||
return StringBuilder().append("Create widget for ")
|
||||
.append(widgetName)
|
||||
.append("?").toString()
|
||||
}
|
||||
|
||||
fun submitSelectionOnClick(){
|
||||
if (rateIdTo == null || rateIdFrom == null){
|
||||
operationFinishedListener.value = Pair(false, "Selections incomplete")
|
||||
return
|
||||
}
|
||||
if (rateIdFrom == rateIdTo){
|
||||
operationFinishedListener.value =
|
||||
Pair(false, "Selected rates cannot be the same ${rateIdFrom}${rateIdTo}")
|
||||
return
|
||||
}
|
||||
operationFinishedListener.value = Pair(true, null)
|
||||
}
|
||||
|
||||
fun setWidgetStored() {
|
||||
repository.setWidgetConversionPairs(rateIdFrom!!,rateIdTo!!,appWidgetId!!)
|
||||
}
|
||||
|
||||
// Start operation based on dialog selection
|
||||
fun setCurrencyName(tag: Any?, currencyName: String){
|
||||
when(tag.toString()){
|
||||
"top" -> rateIdFrom = currencyName
|
||||
"bottom" -> rateIdTo = currencyName
|
||||
else -> { return }
|
||||
}
|
||||
}
|
||||
|
||||
private fun getWidgetStringName() = "${rateIdFrom!!.trimToThree()}${rateIdTo!!.trimToThree()}"
|
||||
|
||||
}
|
||||
@@ -1,8 +1,8 @@
|
||||
package com.appttude.h_mal.easycc.mvvm.ui.widget
|
||||
package com.appttude.h_mal.easycc.ui.widget
|
||||
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.ViewModelProvider
|
||||
import com.appttude.h_mal.easycc.mvvm.data.repository.RepositoryImpl
|
||||
import com.appttude.h_mal.easycc.data.repository.RepositoryImpl
|
||||
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
class WidgetViewModelFactory (
|
||||
@@ -0,0 +1,36 @@
|
||||
package com.appttude.h_mal.easycc.utils
|
||||
|
||||
import java.lang.Double.valueOf
|
||||
import java.text.DecimalFormat
|
||||
import java.util.*
|
||||
|
||||
fun transformIntToArray(int: Int): IntArray{
|
||||
return intArrayOf(int)
|
||||
}
|
||||
|
||||
fun String.trimToThree(): String{
|
||||
val size = length
|
||||
return when {
|
||||
size > 3 -> substring(0, 3)
|
||||
else -> this
|
||||
}
|
||||
}
|
||||
|
||||
fun convertPairsListToString(s1: String, s2: String): String =
|
||||
"${s1.trimToThree()}_${s2.trimToThree()}"
|
||||
|
||||
fun Double.toTwoDp() = run {
|
||||
try {
|
||||
val df = DecimalFormat("0.00")
|
||||
df.currency = Currency.getInstance(Locale.getDefault())
|
||||
valueOf(df.format(this))
|
||||
}catch (e: NumberFormatException){
|
||||
e.printStackTrace()
|
||||
this
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
fun Double.toTwoDpString(): String{
|
||||
return this.toTwoDp().toBigDecimal().toPlainString()
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
package com.appttude.h_mal.easycc.mvvm.utils
|
||||
package com.appttude.h_mal.easycc.utils
|
||||
|
||||
import android.content.Context
|
||||
import android.view.View
|
||||
@@ -13,6 +13,6 @@ fun View.hideView(vis : Boolean){
|
||||
visibility = if (vis){ View.GONE } else { View.VISIBLE }
|
||||
}
|
||||
|
||||
fun Context.DisplayToast(message: String){
|
||||
fun Context.displayToast(message: String){
|
||||
Toast.makeText(this, message, Toast.LENGTH_LONG).show()
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
package com.appttude.h_mal.easycc.mvvm.ui.widget
|
||||
package com.appttude.h_mal.easycc.widget
|
||||
|
||||
import android.app.PendingIntent
|
||||
import android.appwidget.AppWidgetManager
|
||||
@@ -10,9 +10,9 @@ import android.util.Log
|
||||
import android.widget.RemoteViews
|
||||
import android.widget.Toast
|
||||
import com.appttude.h_mal.easycc.R
|
||||
import com.appttude.h_mal.easycc.mvvm.data.repository.RepositoryImpl
|
||||
import com.appttude.h_mal.easycc.mvvm.ui.app.MainActivity
|
||||
import com.appttude.h_mal.easycc.mvvm.utils.transformIntToArray
|
||||
import com.appttude.h_mal.easycc.data.repository.RepositoryImpl
|
||||
import com.appttude.h_mal.easycc.ui.main.MainActivity
|
||||
import com.appttude.h_mal.easycc.utils.transformIntToArray
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.launch
|
||||
@@ -1,13 +1,11 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<layout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:card_view="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools">
|
||||
|
||||
<data>
|
||||
<variable
|
||||
name="viewmodel"
|
||||
type="com.appttude.h_mal.easycc.mvvm.ui.app.MainViewModel" />
|
||||
type="com.appttude.h_mal.easycc.ui.main.MainViewModel" />
|
||||
</data>
|
||||
|
||||
<RelativeLayout
|
||||
@@ -16,7 +14,7 @@
|
||||
android:orientation="vertical"
|
||||
android:focusable="false"
|
||||
android:focusableInTouchMode="true"
|
||||
tools:context="com.appttude.h_mal.easycc.mvvm.ui.app.MainActivity">
|
||||
tools:context=".ui.main.MainActivity">
|
||||
|
||||
<RelativeLayout
|
||||
android:layout_width="wrap_content"
|
||||
@@ -44,7 +42,7 @@
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_margin="12dp"
|
||||
android:text="@{viewmodel.rateIdFrom}"
|
||||
android:text="@={viewmodel.rateIdFrom}"
|
||||
android:textColor="@color/colour_five"
|
||||
android:textSize="18sp" />
|
||||
</androidx.cardview.widget.CardView>
|
||||
@@ -72,15 +70,14 @@
|
||||
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">
|
||||
<androidx.cardview.widget.CardView 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:text="@={viewmodel.rateIdTo}"
|
||||
android:tag="bottom"
|
||||
android:textColor="@color/colour_five"
|
||||
android:textSize="18sp" />
|
||||
|
||||
@@ -1,14 +1,11 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<layout
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:card_view="http://schemas.android.com/apk/res-auto"
|
||||
<layout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools">
|
||||
|
||||
<data>
|
||||
<variable
|
||||
name="viewmodel"
|
||||
type="com.appttude.h_mal.easycc.mvvm.ui.widget.WidgetViewModel" />
|
||||
type="com.appttude.h_mal.easycc.ui.widget.WidgetViewModel" />
|
||||
</data>
|
||||
|
||||
<RelativeLayout
|
||||
@@ -17,7 +14,7 @@
|
||||
android:orientation="vertical"
|
||||
android:focusable="false"
|
||||
android:focusableInTouchMode="true"
|
||||
tools:context=".mvvm.ui.widget.CurrencyAppWidgetConfigureActivityKotlin">
|
||||
tools:context=".ui.widget.CurrencyAppWidgetConfigureActivityKotlin">
|
||||
|
||||
<RelativeLayout
|
||||
android:layout_width="match_parent"
|
||||
@@ -40,7 +37,6 @@
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_margin="12dp"
|
||||
android:onClick="@{viewmodel::selectCurrencyOnClick}"
|
||||
android:tag="top"
|
||||
android:text="@={viewmodel.rateIdFrom}"
|
||||
android:textColor="@color/colour_five"
|
||||
@@ -58,7 +54,6 @@
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_margin="12dp"
|
||||
android:onClick="@{viewmodel::selectCurrencyOnClick}"
|
||||
android:tag="bottom"
|
||||
android:text="@={viewmodel.rateIdTo}"
|
||||
android:textColor="@color/colour_five"
|
||||
@@ -69,15 +64,15 @@
|
||||
</LinearLayout>
|
||||
|
||||
<TextView
|
||||
android:layout_marginTop="12dp"
|
||||
android:layout_marginEnd="22dp"
|
||||
android:id="@+id/submit_widget"
|
||||
android:tag="submit"
|
||||
android:padding="12dp"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_below="@id/whole_view"
|
||||
android:layout_alignParentEnd="true"
|
||||
android:textColor="@color/colour_five"
|
||||
android:onClick="@{viewmodel::submitSelectionOnClick}"
|
||||
android:text="Submit" />
|
||||
|
||||
</RelativeLayout>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<?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:configure="com.appttude.h_mal.easycc.ui.widget.CurrencyAppWidgetConfigureActivityKotlin"
|
||||
android:initialKeyguardLayout="@layout/currency_app_widget"
|
||||
android:initialLayout="@layout/currency_app_widget"
|
||||
android:minHeight="40dp"
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<?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:configure="com.appttude.h_mal.easycc.ui.widget.CurrencyAppWidgetConfigureActivityKotlin"
|
||||
android:initialKeyguardLayout="@layout/currency_kotlin_app_widget"
|
||||
android:initialLayout="@layout/currency_kotlin_app_widget"
|
||||
android:minWidth="110dp"
|
||||
|
||||
@@ -2,7 +2,7 @@ package com.appttude.h_mal.easycc;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
||||
/**
|
||||
* Example local unit test, which will execute on the development machine (host).
|
||||
|
||||
@@ -1,16 +1,14 @@
|
||||
package com.appttude.h_mal.easycc.repository
|
||||
|
||||
import android.content.Context
|
||||
import com.appttude.h_mal.easycc.BuildConfig
|
||||
import com.appttude.h_mal.easycc.mvvm.data.network.api.CurrencyApi
|
||||
import com.appttude.h_mal.easycc.mvvm.data.network.response.ResponseObject
|
||||
import com.appttude.h_mal.easycc.mvvm.data.prefs.PreferenceProvider
|
||||
import com.appttude.h_mal.easycc.mvvm.data.repository.Repository
|
||||
import com.appttude.h_mal.easycc.mvvm.data.repository.RepositoryImpl
|
||||
import com.appttude.h_mal.easycc.mvvm.utils.convertPairsListToString
|
||||
import com.appttude.h_mal.easycc.data.network.api.CurrencyApi
|
||||
import com.appttude.h_mal.easycc.data.network.response.ResponseObject
|
||||
import com.appttude.h_mal.easycc.data.prefs.PreferenceProvider
|
||||
import com.appttude.h_mal.easycc.data.repository.Repository
|
||||
import com.appttude.h_mal.easycc.data.repository.RepositoryImpl
|
||||
import com.appttude.h_mal.easycc.utils.convertPairsListToString
|
||||
import kotlinx.coroutines.runBlocking
|
||||
import okhttp3.ResponseBody
|
||||
|
||||
import org.junit.Assert.assertEquals
|
||||
import org.junit.Assert.assertNotNull
|
||||
import org.junit.Before
|
||||
@@ -20,7 +18,6 @@ import org.mockito.Mockito
|
||||
import org.mockito.Mockito.mock
|
||||
import org.mockito.MockitoAnnotations
|
||||
import retrofit2.Response
|
||||
|
||||
import java.io.IOException
|
||||
import kotlin.test.assertFailsWith
|
||||
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
package com.appttude.h_mal.easycc.repository
|
||||
|
||||
import android.content.Context
|
||||
import com.appttude.h_mal.easycc.mvvm.data.network.api.CurrencyApi
|
||||
import com.appttude.h_mal.easycc.mvvm.data.prefs.PreferenceProvider
|
||||
import com.appttude.h_mal.easycc.mvvm.data.repository.Repository
|
||||
import com.appttude.h_mal.easycc.mvvm.data.repository.RepositoryImpl
|
||||
import com.appttude.h_mal.easycc.data.network.api.CurrencyApi
|
||||
import com.appttude.h_mal.easycc.data.prefs.PreferenceProvider
|
||||
import com.appttude.h_mal.easycc.data.repository.Repository
|
||||
import com.appttude.h_mal.easycc.data.repository.RepositoryImpl
|
||||
import org.junit.Before
|
||||
import org.junit.Test
|
||||
import org.mockito.Mock
|
||||
|
||||
Reference in New Issue
Block a user