diff --git a/android/app/build.gradle b/android/app/build.gradle index 6486bd4..662ef74 100644 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -47,7 +47,7 @@ android { applicationId "com.appttude.h_mal.easycc" // You can update the following values to match your application needs. // For more information, see: https://docs.flutter.dev/deployment/android#reviewing-the-build-configuration. - minSdkVersion flutter.minSdkVersion + minSdkVersion localProperties.getProperty('flutter.minSdkVersion').toInteger() targetSdkVersion flutter.targetSdkVersion versionCode flutterVersionCode.toInteger() versionName flutterVersionName @@ -68,4 +68,5 @@ flutter { dependencies { implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" + implementation 'androidx.cardview:cardview:1.0.0' } diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml index e317dea..581ca1d 100644 --- a/android/app/src/main/AndroidManifest.xml +++ b/android/app/src/main/AndroidManifest.xml @@ -25,8 +25,33 @@ - + + + + + + + + + + + + + + + + + + + + + + + diff --git a/android/app/src/main/kotlin/com/appttude/h_mal/easycc/AppWidgetProvider.kt b/android/app/src/main/kotlin/com/appttude/h_mal/easycc/AppWidgetProvider.kt new file mode 100644 index 0000000..20f59c8 --- /dev/null +++ b/android/app/src/main/kotlin/com/appttude/h_mal/easycc/AppWidgetProvider.kt @@ -0,0 +1,88 @@ +package com.appttude.h_mal.easycc + +import android.appwidget.AppWidgetManager +import android.content.ComponentName +import android.content.Context +import android.content.Intent +import android.content.SharedPreferences +import android.net.Uri +import android.widget.RemoteViews +import android.widget.Toast +import es.antonborri.home_widget.HomeWidgetBackgroundIntent +import es.antonborri.home_widget.HomeWidgetLaunchIntent +import es.antonborri.home_widget.HomeWidgetPlugin +import es.antonborri.home_widget.HomeWidgetProvider + + +class AppWidgetProvider : HomeWidgetProvider(){ + override fun onUpdate( + context: Context, + appWidgetManager: AppWidgetManager, + appWidgetIds: IntArray, + widgetData: SharedPreferences + ) { + appWidgetIds.forEach { widgetId -> + val views = RemoteViews(context.packageName, R.layout.currency_app_widget).apply { + // Data from background operation received + val from: String? = widgetData.getString("${widgetId}_from", null) + val to: String? = widgetData.getString("${widgetId}_to", null) + val rate: String? = widgetData.getString("${widgetId}_rate", null) + + if (from.isNullOrBlank() or to.isNullOrBlank() or rate.isNullOrBlank()) { + Toast.makeText(context, "Unable to review data for widget", Toast.LENGTH_SHORT).show() + return@apply + } + + val titleString = "${from}${to}" + setTextViewText(R.id.exchangeName, titleString) + setTextViewText(R.id.exchangeRate, rate.toString()) + + val uri = Uri.parse("myAppWidget://updatewidget").buildUpon() + .appendQueryParameter("id", widgetId.toString()) + .build() + + setImageViewResource(R.id.refresh_icon, R.drawable.ic_refresh_white_24dp) + val backgroundIntent = HomeWidgetBackgroundIntent.getBroadcast(context, uri) + setOnClickPendingIntent(R.id.refresh_icon, backgroundIntent) + + val pendingIntent = HomeWidgetLaunchIntent.getActivity(context, + MainActivity::class.java) + + setOnClickPendingIntent(R.id.widget_view, pendingIntent) + } + appWidgetManager.updateAppWidget(widgetId, views) + } + } + + override fun onReceive(context: Context?, intent: Intent?) { + super.onReceive(context, intent) + + when (intent?.action) { + + } + val appWidgetManager = AppWidgetManager.getInstance(context); + val appWidgetIds = appWidgetManager.getAppWidgetIds( + context?.let { ComponentName(it, this::class.java) }) + val widgetDate = context?.let { HomeWidgetPlugin.getData(it) } + + appWidgetIds.forEach { widgetId -> + + + } + } + + override fun onDeleted(context: Context?, appWidgetIds: IntArray?) { + super.onDeleted(context, appWidgetIds) + + appWidgetIds?.forEach { widgetId -> + val uri = Uri.parse("myAppWidget://deletewidget") + uri.buildUpon().appendQueryParameter("id", widgetId.toString()).build() + + context?.let { + HomeWidgetBackgroundIntent + .getBroadcast(it, uri) + .send() + } + } + } +} \ No newline at end of file diff --git a/android/app/src/main/kotlin/com/appttude/h_mal/easycc/CurrencyAppWidgetConfigureActivity.kt b/android/app/src/main/kotlin/com/appttude/h_mal/easycc/CurrencyAppWidgetConfigureActivity.kt new file mode 100644 index 0000000..edf8223 --- /dev/null +++ b/android/app/src/main/kotlin/com/appttude/h_mal/easycc/CurrencyAppWidgetConfigureActivity.kt @@ -0,0 +1,119 @@ +package com.appttude.h_mal.easycc + +import android.app.Activity +import android.appwidget.AppWidgetManager +import android.net.Uri +import android.os.Bundle +import android.view.View +import android.widget.TextView +import android.widget.Toast +import es.antonborri.home_widget.HomeWidgetBackgroundIntent + +/** + * The configuration screen for the [CurrencyAppWidgetKotlin] AppWidget. + */ +class CurrencyAppWidgetConfigureActivity : Activity(), + View.OnClickListener { + + private var mAppWidgetId = AppWidgetManager.INVALID_APPWIDGET_ID + + private var top: String? = null + private var bottom: String? = null + + public override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + setContentView(R.layout.currency_app_widget_configure) + + // 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 + } + setupClickListener() + } + + private fun setupClickListener() { + findViewById(R.id.submitWidget).setOnClickListener(this) + findViewById(R.id.currencyOne).setOnClickListener(this) + findViewById(R.id.currencyTwo).setOnClickListener(this) + } + + override fun onClick(view: View?) { + when (view?.tag.toString()) { + "top", "bottom" -> showCustomDialog(view) + "submit" -> displaySubmitDialog() + } + } + + private fun displaySubmitDialog() { + if (top == null || bottom == null) { + Toast.makeText(this, "Selections incomplete", Toast.LENGTH_SHORT).show() + return + } + if (top == bottom) { + Toast.makeText(this, "Selected rates cannot be the same", Toast.LENGTH_SHORT).show() + return + } + + WidgetSubmitDialog(this, getSubmitDialogMessage(), object : DialogSubmit { + override fun onSubmit() { + sendUpdateIntent(top!!, bottom!!) + finishCurrencyWidgetActivity() + } + }).show() + } + + + private fun showCustomDialog(view: View?) { + CustomDialogClass(this) { + (view as TextView).text = it + when (view.tag.toString()) { + "top" -> top = it + "bottom" -> bottom = it + } + }.show() + } + + fun finishCurrencyWidgetActivity() { + // Make sure we pass back the original appWidgetId + val resultValue = intent + resultValue.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, mAppWidgetId) + setResult(RESULT_OK, resultValue) + finish() + } + + fun sendUpdateIntent(from: String, to: String) { + // It is the responsibility of the configuration activity to update the app widget + // Send update broadcast to widget app class + val uri = Uri.parse("myAppWidget://createwidget").buildUpon() + .appendQueryParameter("id", mAppWidgetId.toString()) + .appendQueryParameter("from", from) + .appendQueryParameter("to", to) + .build() + + val backgroundIntent = HomeWidgetBackgroundIntent.getBroadcast(this, uri) + backgroundIntent.send() + } + + private fun getSubmitDialogMessage(): String { + val widgetName = getWidgetStringName() + return StringBuilder().append("Create widget for ") + .append(widgetName) + .append("?").toString() + } + + private fun getWidgetStringName() = "${top!!.substring(0, 3)}${bottom!!.substring(0, 3)}" + +} diff --git a/android/app/src/main/kotlin/com/appttude/h_mal/easycc/CustomDialogClass.kt b/android/app/src/main/kotlin/com/appttude/h_mal/easycc/CustomDialogClass.kt new file mode 100644 index 0000000..bc38a66 --- /dev/null +++ b/android/app/src/main/kotlin/com/appttude/h_mal/easycc/CustomDialogClass.kt @@ -0,0 +1,57 @@ +package com.appttude.h_mal.easycc + +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.ListView +import android.widget.TextView +import com.appttude.h_mal.easycc.R + +/** + * Custom dialog when selecting currencies from list with filter + */ +@Suppress("DEPRECATION") +class CustomDialogClass( + context: Context, + private val onSelect: (String) -> Unit +) : Dialog(context) { + + override fun onCreate(savedInstanceState: Bundle?) { + 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) + + val arrayAdapter = + ArrayAdapter.createFromResource( + context, R.array.currency_arrays, + android.R.layout.simple_list_item_1 + ) + + val list_view = findViewById(R.id.list_view) + list_view.adapter = arrayAdapter + + // Edit text to filter @arrayAdapter + findViewById(R.id.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) {} + }) + + // interface selection back to calling activity + list_view.setOnItemClickListener { adapterView, _, i, _ -> + onSelect.invoke(adapterView.getItemAtPosition(i).toString()) + dismiss() + } + } +} \ No newline at end of file diff --git a/android/app/src/main/kotlin/com/appttude/h_mal/easycc/WidgetSubmitDialog.kt b/android/app/src/main/kotlin/com/appttude/h_mal/easycc/WidgetSubmitDialog.kt new file mode 100644 index 0000000..408b652 --- /dev/null +++ b/android/app/src/main/kotlin/com/appttude/h_mal/easycc/WidgetSubmitDialog.kt @@ -0,0 +1,37 @@ +package com.appttude.h_mal.easycc + +import android.app.Dialog +import android.content.Context +import android.os.Bundle +import android.widget.TextView + + +/** + * Dialog created when submitting the completed selections + * in [CurrencyAppWidgetConfigureActivity] + */ +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) + + findViewById(R.id.confirmText).text = messageString + + // handle dialog buttons + findViewById(R.id.confirmYes).setOnClickListener { dialogInterface.onSubmit() } + findViewById(R.id.confirmNo).setOnClickListener { dismiss() } + } +} + +interface DialogSubmit { + fun onSubmit() +} \ No newline at end of file diff --git a/android/app/src/main/res/drawable/easyycc_widget_preview.png b/android/app/src/main/res/drawable/easyycc_widget_preview.png new file mode 100644 index 0000000..144292e Binary files /dev/null and b/android/app/src/main/res/drawable/easyycc_widget_preview.png differ diff --git a/android/app/src/main/res/drawable/ic_background.xml b/android/app/src/main/res/drawable/ic_background.xml new file mode 100644 index 0000000..21d6752 --- /dev/null +++ b/android/app/src/main/res/drawable/ic_background.xml @@ -0,0 +1,24 @@ + + + + + + + + + + \ No newline at end of file diff --git a/android/app/src/main/res/drawable/ic_refresh_white_24dp.xml b/android/app/src/main/res/drawable/ic_refresh_white_24dp.xml new file mode 100644 index 0000000..a27ba3c --- /dev/null +++ b/android/app/src/main/res/drawable/ic_refresh_white_24dp.xml @@ -0,0 +1,10 @@ + + + diff --git a/android/app/src/main/res/layout/confirm_dialog.xml b/android/app/src/main/res/layout/confirm_dialog.xml new file mode 100644 index 0000000..339dcb0 --- /dev/null +++ b/android/app/src/main/res/layout/confirm_dialog.xml @@ -0,0 +1,64 @@ + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/android/app/src/main/res/layout/currency_app_widget.xml b/android/app/src/main/res/layout/currency_app_widget.xml new file mode 100644 index 0000000..8d9d1b1 --- /dev/null +++ b/android/app/src/main/res/layout/currency_app_widget.xml @@ -0,0 +1,65 @@ + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/android/app/src/main/res/layout/currency_app_widget_configure.xml b/android/app/src/main/res/layout/currency_app_widget_configure.xml new file mode 100644 index 0000000..19460db --- /dev/null +++ b/android/app/src/main/res/layout/currency_app_widget_configure.xml @@ -0,0 +1,69 @@ + + + + + + + + + + + + + + + + + + + + + + + diff --git a/android/app/src/main/res/layout/custom_dialog.xml b/android/app/src/main/res/layout/custom_dialog.xml new file mode 100644 index 0000000..7b6354b --- /dev/null +++ b/android/app/src/main/res/layout/custom_dialog.xml @@ -0,0 +1,39 @@ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/android/app/src/main/res/values-night/colors.xml b/android/app/src/main/res/values-night/colors.xml new file mode 100644 index 0000000..d74b2f1 --- /dev/null +++ b/android/app/src/main/res/values-night/colors.xml @@ -0,0 +1,8 @@ + + + #253031 + #315659 + #2978A0 + #8549ff + #C6E0FF + diff --git a/android/app/src/main/res/values/colors.xml b/android/app/src/main/res/values/colors.xml new file mode 100644 index 0000000..a72e604 --- /dev/null +++ b/android/app/src/main/res/values/colors.xml @@ -0,0 +1,8 @@ + + + #253031 + #315659 + #2978A0 + #8549ff + #C6E0FF + \ No newline at end of file diff --git a/android/app/src/main/res/values/strings.xml b/android/app/src/main/res/values/strings.xml new file mode 100644 index 0000000..4d866f8 --- /dev/null +++ b/android/app/src/main/res/values/strings.xml @@ -0,0 +1,161 @@ + + + + + ALL - Albanian Lek + AFN - Afghan Afghani + DZD - Algerian Dinar + AOA - Angolan Kwanza + ARS - Argentine Peso + AMD - Armenian Dram + AWG - Aruban Florin + AUD - Australian Dollar + AZN - Azerbaijani Manat + BSD - Bahamian Dollar + BHD - Bahraini Dinar + BDT - Bangladeshi Taka + BBD - Barbadian Dollar + BYR - Belarusian Ruble + BZD - Belize Dollar + BTN - Bhutanese Ngultrum + BTC - Bitcoin + BOB - Bolivian Boliviano + BAM - Bosnia And Herzegovina Konvertibilna Marka + BWP - Botswana Pula + BRL - Brazilian Real + GBP - British Pound + BND - Brunei Dollar + BGN - Bulgarian Lev + BIF - Burundi Franc + KHR - Cambodian Riel + CAD - Canadian Dollar + CVE - Cape Verdean Escudo + KYD - Cayman Islands Dollar + XAF - Central African CFA Franc + XPF - CFP Franc + CLP - Chilean Peso + CNY - Chinese Yuan + COP - Colombian Peso + KMF - Comorian Franc + CDF - Congolese Franc + CRC - Costa Rican Colon + HRK - Croatian Kuna + CUP - Cuban Peso + CZK - Czech Koruna + DKK - Danish Krone + DJF - Djiboutian Franc + DOP - Dominican Peso + XCD - East Caribbean Dollar + EGP - Egyptian Pound + ERN - Eritrean Nakfa + ETB - Ethiopian Birr + EUR - Euro + FKP - Falkland Islands Pound + FJD - Fijian Dollar + GMD - Gambian Dalasi + GEL - Georgian Lari + GHS - Ghanaian Cedi + GIP - Gibraltar Pound + GTQ - Guatemalan Quetzal + GNF - Guinean Franc + GYD - Guyanese Dollar + HTG - Haitian Gourde + HNL - Honduran Lempira + HKD - Hong Kong Dollar + HUF - Hungarian Forint + ISK - Icelandic Kr\u00f3na + INR - Indian Rupee + IDR - Indonesian Rupiah + IRR - Iranian Rial + IQD - Iraqi Dinar + ILS - Israeli New Sheqel + JMD - Jamaican Dollar + JPY - Japanese Yen + JOD - Jordanian Dinar + KZT - Kazakhstani Tenge + KES - Kenyan Shilling + KWD - Kuwaiti Dinar + KGS - Kyrgyzstani Som + LAK - Lao Kip + LVL - Latvian Lats + LBP - Lebanese Lira + LSL - Lesotho Loti + LRD - Liberian Dollar + LYD - Libyan Dinar + MOP - Macanese Pataca + MKD - Macedonian Denar + MGA - Malagasy Ariary + MWK - Malawian Kwacha + MYR - Malaysian Ringgit + MVR - Maldivian Rufiyaa + MRO - Mauritanian Ouguiya + MUR - Mauritian Rupee + MXN - Mexican Peso + MDL - Moldovan Leu + MNT - Mongolian Tugrik + MAD - Moroccan Dirham + MZN - Mozambican Metical + MMK - Myanma Kyat + NAD - Namibian Dollar + NPR - Nepalese Rupee + ANG - Netherlands Antillean Gulden + TWD - New Taiwan Dollar + NZD - New Zealand Dollar + NIO - Nicaraguan Cordoba + NGN - Nigerian Naira + KPW - North Korean Won + NOK - Norwegian Krone + OMR - Omani Rial + TOP - Paanga + PKR - Pakistani Rupee + PAB - Panamanian Balboa + PGK - Papua New Guinean Kina + PYG - Paraguayan Guarani + PEN - Peruvian Nuevo Sol + PHP - Philippine Peso + PLN - Polish Zloty + QAR - Qatari Riyal + RON - Romanian Leu + RUB - Russian Ruble + RWF - Rwandan Franc + SHP - Saint Helena Pound + WST - Samoan Tala + STD - Sao Tome And Principe Dobra + SAR - Saudi Riyal + RSD - Serbian Dinar + SCR - Seychellois Rupee + SLL - Sierra Leonean Leone + SGD - Singapore Dollar + SBD - Solomon Islands Dollar + SOS - Somali Shilling + ZAR - South African Rand + KRW - South Korean Won + XDR - Special Drawing Rights + LKR - Sri Lankan Rupee + SDG - Sudanese Pound + SRD - Surinamese Dollar + SZL - Swazi Lilangeni + SEK - Swedish Krona + CHF - Swiss Franc + SYP - Syrian Pound + TJS - Tajikistani Somoni + TZS - Tanzanian Shilling + THB - Thai Baht + TTD - Trinidad and Tobago Dollar + TND - Tunisian Dinar + TRY - Turkish New Lira + TMT - Turkmenistan Manat + AED - UAE Dirham + UGX - Ugandan Shilling + UAH - Ukrainian Hryvnia + USD - United States Dollar + UYU - Uruguayan Peso + UZS - Uzbekistani Som + VUV - Vanuatu Vatu + VEF - Venezuelan Bolivar + VND - Vietnamese Dong + XOF - West African CFA Franc + YER - Yemeni Rial + ZMW - Zambian Kwacha + + \ No newline at end of file diff --git a/android/app/src/main/res/values/styles.xml b/android/app/src/main/res/values/styles.xml index cb1ef88..1ffd453 100644 --- a/android/app/src/main/res/values/styles.xml +++ b/android/app/src/main/res/values/styles.xml @@ -15,4 +15,12 @@ + + diff --git a/android/app/src/main/res/xml/currency_app_widget_info.xml b/android/app/src/main/res/xml/currency_app_widget_info.xml new file mode 100644 index 0000000..1e0409c --- /dev/null +++ b/android/app/src/main/res/xml/currency_app_widget_info.xml @@ -0,0 +1,11 @@ + + \ No newline at end of file diff --git a/android/build.gradle b/android/build.gradle index 58a8c74..6b21153 100644 --- a/android/build.gradle +++ b/android/build.gradle @@ -6,7 +6,7 @@ buildscript { } dependencies { - classpath 'com.android.tools.build:gradle:7.2.0' + classpath 'com.android.tools.build:gradle:4.1.0' classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" } } diff --git a/lib/Utils/currency_utils.dart b/lib/Utils/currency_utils.dart index fb739a6..3169ac9 100644 --- a/lib/Utils/currency_utils.dart +++ b/lib/Utils/currency_utils.dart @@ -2,6 +2,9 @@ extension CurrencyExtension on String { /// Convert currency string into currency code /// eg. "AUD - Australian Dollar" to "AUD" String getCurrencyCode(){ + if (length == 3) { + return this; + } return substring(0,3); } } \ No newline at end of file diff --git a/lib/base_widget.dart b/lib/base_widget.dart index 1bd296e..11ab430 100644 --- a/lib/base_widget.dart +++ b/lib/base_widget.dart @@ -2,10 +2,10 @@ import 'package:flutter/material.dart'; import 'package:stacked/stacked.dart'; import 'package:toast/toast.dart'; -import 'base_viewmodel.dart'; import 'Utils/constants.dart'; import 'Utils/view_state.dart'; import 'Utils/view_utils.dart'; +import 'base_viewmodel.dart'; abstract class BaseStatelessWidget extends StatelessWidget { diff --git a/lib/data/network/app_dio.dart b/lib/data/network/app_dio.dart index 3b4c6df..7414e6a 100644 --- a/lib/data/network/app_dio.dart +++ b/lib/data/network/app_dio.dart @@ -5,8 +5,8 @@ class AppDio { static Dio createDio() { Dio dio = Dio( BaseOptions( - receiveTimeout: 60000, - connectTimeout: 120000, + receiveTimeout: 30000, + connectTimeout: 30000, ) ); dio.interceptors.add(LogInterceptor()); diff --git a/lib/data/repository/repository_impl.dart b/lib/data/repository/repository_impl.dart index 54ce8b8..5d6f3ba 100644 --- a/lib/data/repository/repository_impl.dart +++ b/lib/data/repository/repository_impl.dart @@ -4,7 +4,6 @@ import 'package:easy_cc_flutter/Utils/currency_utils.dart'; import 'package:easy_cc_flutter/data/model/currency.dart'; import 'package:easy_cc_flutter/data/prefs/currency_pair.dart'; -import '../../locator.dart'; import '../../main.dart'; import '../network/backup_currency_api.dart'; import '../network/currency_api.dart'; @@ -13,9 +12,11 @@ import '../prefs/preference_provider.dart'; import 'repository.dart'; class RepositoryImpl extends Repository with SafeApiCall { - final PreferenceProvider _prefs = locator(); - final CurrencyApi _api = locator(); - final BackupCurrencyApi _backupApi = locator(); + final PreferenceProvider _prefs; + final CurrencyApi _api; + final BackupCurrencyApi _backupApi; + + RepositoryImpl(this._prefs, this._api, this._backupApi); @override CurrencyPair getConversionPair() { diff --git a/lib/home.dart b/lib/home.dart index a2fac5a..dc891cb 100644 --- a/lib/home.dart +++ b/lib/home.dart @@ -1,19 +1,20 @@ -import 'package:easy_cc_flutter/main_view_model.dart'; import 'package:easy_cc_flutter/Utils/selection_type.dart'; -import 'package:easy_cc_flutter/views/drop_down_box.dart'; +import 'package:easy_cc_flutter/locator.dart'; +import 'package:easy_cc_flutter/main_view_model.dart'; import 'package:easy_cc_flutter/views/converter_edit_text.dart'; +import 'package:easy_cc_flutter/views/drop_down_box.dart'; import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; -import 'base_widget.dart'; import 'Utils/constants.dart'; +import 'base_widget.dart'; class HomePage extends BaseStatelessWidget { const HomePage({super.key}); @override MainViewModel createViewModel() { - return MainViewModel(); + return locator(); } @override diff --git a/lib/locator.dart b/lib/locator.dart index edb970c..8ba82fb 100644 --- a/lib/locator.dart +++ b/lib/locator.dart @@ -1,10 +1,10 @@ -import 'package:easy_cc_flutter/main_view_model.dart'; import 'package:easy_cc_flutter/data/network/backup_currency_api.dart'; import 'package:easy_cc_flutter/data/network/currency_api.dart'; import 'package:easy_cc_flutter/data/repository/repository_impl.dart'; +import 'package:easy_cc_flutter/main_view_model.dart'; import 'package:get_it/get_it.dart'; - import 'data/prefs/preference_provider.dart'; +import 'data/repository/repository.dart'; GetIt locator = GetIt.instance; @@ -12,6 +12,6 @@ void setupLocator() { locator.registerLazySingleton(() => PreferenceProvider()); locator.registerLazySingleton(() => CurrencyApi.create()); locator.registerLazySingleton(() => BackupCurrencyApi.create()); - locator.registerLazySingleton(() => RepositoryImpl()); - locator.registerFactory(() => MainViewModel()); + locator.registerLazySingleton(() => RepositoryImpl(locator(), locator(),locator())); + locator.registerFactory(() => MainViewModel(locator())); } \ No newline at end of file diff --git a/lib/main.dart b/lib/main.dart index 1b24838..c4a6e3d 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -1,7 +1,14 @@ +import 'package:easy_cc_flutter/Utils/currency_utils.dart'; +import 'package:easy_cc_flutter/data/network/backup_currency_api.dart'; +import 'package:easy_cc_flutter/data/network/currency_api.dart'; import 'package:flutter/material.dart'; +import 'package:home_widget/home_widget.dart'; import 'package:logger/logger.dart'; +import 'data/model/currency.dart'; import 'data/prefs/preference_provider.dart'; +import 'data/repository/repository.dart'; +import 'data/repository/repository_impl.dart'; import 'home.dart'; import 'locator.dart'; @@ -9,13 +16,56 @@ var logger = Logger( printer: PrettyPrinter(), ); -void main() async { +Future main() async { WidgetsFlutterBinding.ensureInitialized(); setupLocator(); await locator().init(); + await HomeWidget.registerBackgroundCallback(backgroundCallback); runApp(const MyApp()); } +Future backgroundCallback(Uri? uri) async { + PreferenceProvider prefs = PreferenceProvider(); + await prefs.init(); + CurrencyApi api = CurrencyApi.create(); + BackupCurrencyApi backupApi = BackupCurrencyApi.create(); + RepositoryImpl repository = RepositoryImpl(prefs, api, backupApi); + + if (uri?.host == 'updatewidget') { + Map? querys = uri?.queryParameters; + String? widgetId = querys?["id"]; + + await updateWidget(widgetId, repository); + } else if (uri?.host == 'createwidget') { + Map? querys = uri?.queryParameters; + String? widgetId = querys?["id"]; + String? from = querys?["from"]?.getCurrencyCode(); + String? to = querys?["to"]?.getCurrencyCode(); + + await HomeWidget.saveWidgetData("${widgetId}_from", from); + await HomeWidget.saveWidgetData("${widgetId}_to", to); + + await updateWidget(widgetId, repository); + } +} + +Future updateWidget(String? widgetId, Repository repository) async { + String? from = await HomeWidget.getWidgetData("${widgetId}_from"); + String? to = await HomeWidget.getWidgetData("${widgetId}_to"); + + if (from == null || to == null) { + return; + } + + Currency currency = await repository.getConversationRateFromApi(from, to); + + await HomeWidget.saveWidgetData("${widgetId}_from", from); + await HomeWidget.saveWidgetData("${widgetId}_to", to); + await HomeWidget.saveWidgetData("${widgetId}_rate", currency.rate.toString()); + + await HomeWidget.updateWidget(name: 'AppWidgetProvider', iOSName: 'AppWidgetProvider'); +} + class MyApp extends StatelessWidget { const MyApp({super.key}); diff --git a/lib/main_view_model.dart b/lib/main_view_model.dart index 7dfb31b..98e522f 100644 --- a/lib/main_view_model.dart +++ b/lib/main_view_model.dart @@ -1,14 +1,14 @@ -import 'package:easy_cc_flutter/base_viewmodel.dart'; import 'package:easy_cc_flutter/Utils/selection_type.dart'; +import 'package:easy_cc_flutter/base_viewmodel.dart'; import 'Utils/constants.dart'; import 'data/prefs/currency_pair.dart'; import 'data/repository/repository.dart'; -import 'data/repository/repository_impl.dart'; -import 'locator.dart'; class MainViewModel extends BaseViewmodel { - final Repository _repository = locator(); + final Repository _repository; + + MainViewModel(this._repository); double conversionRate = 1.0; diff --git a/pubspec.lock b/pubspec.lock index 719248f..83f4454 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -282,6 +282,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "2.1.0" + home_widget: + dependency: "direct main" + description: + name: home_widget + url: "https://pub.dartlang.org" + source: hosted + version: "0.1.6" http_multi_server: dependency: transitive description: diff --git a/pubspec.yaml b/pubspec.yaml index b107407..5fa8453 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -49,6 +49,7 @@ dependencies: mockito: ^5.3.2 json_annotation: ^4.7.0 flutter_launcher_icons: ^0.10.0 + home_widget: ^0.1.6 dev_dependencies: flutter_test: diff --git a/test/unit_test/repository_test.dart b/test/unit_test/repository_test.dart index db8d905..cc3c59a 100644 --- a/test/unit_test/repository_test.dart +++ b/test/unit_test/repository_test.dart @@ -8,7 +8,6 @@ import 'package:easy_cc_flutter/data/network/currency_api.dart'; import 'package:easy_cc_flutter/data/prefs/currency_pair.dart'; import 'package:easy_cc_flutter/data/prefs/preference_provider.dart'; import 'package:easy_cc_flutter/data/repository/repository_impl.dart'; -import 'package:easy_cc_flutter/locator.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:mockito/annotations.dart'; import 'package:mockito/mockito.dart'; @@ -29,7 +28,8 @@ import 'repository_test.mocks.dart'; MockSpec(onMissingStub: OnMissingStub.returnDefault) ]) void main() { - late RepositoryImpl repository; + late RepositoryImpl sut; + late PreferenceProvider preferenceProvider; late CurrencyApi currencyApi; late BackupCurrencyApi backupCurrencyApi; @@ -44,11 +44,7 @@ void main() { currencyApi = MockCurrencyApi(); backupCurrencyApi = MockBackupCurrencyApi(); - locator.registerLazySingleton(() => preferenceProvider); - locator.registerLazySingleton(() => currencyApi); - locator.registerLazySingleton(() => backupCurrencyApi); - - repository = RepositoryImpl(); + sut = RepositoryImpl(preferenceProvider, currencyApi, backupCurrencyApi); }); test('get currency pair from prefs', () { @@ -59,7 +55,7 @@ void main() { when(preferenceProvider.getConversionPair()).thenReturn(pair); // Then - expect(repository.getConversionPair(), pair); + expect(sut.getConversionPair(), pair); }); test('get currency rate from API', () async { @@ -77,7 +73,7 @@ void main() { // Then Currency retrieved = - await repository.getConversationRateFromApi(fromCurrency, toCurrency); + await sut.getConversationRateFromApi(fromCurrency, toCurrency); expect(retrieved.toString(), currencyObject.toString()); }); @@ -98,7 +94,7 @@ void main() { // Then Currency retrieved = - await repository.getConversationRateFromApi(fromCurrency, toCurrency); + await sut.getConversationRateFromApi(fromCurrency, toCurrency); expect(retrieved.toString(), currencyObject.toString()); }); @@ -115,7 +111,7 @@ void main() { .thenAnswer((_) async => Future.error(backUpError)); // Then - expect(() async => await repository.getConversationRateFromApi(fromCurrency, toCurrency), + expect(() async => await sut.getConversationRateFromApi(fromCurrency, toCurrency), throwsA(predicate((e) => e is HttpException && e.message == 'Error message'))); diff --git a/test/unit_test/viewmodel_test.dart b/test/unit_test/viewmodel_test.dart index 738f1af..9e11ef7 100644 --- a/test/unit_test/viewmodel_test.dart +++ b/test/unit_test/viewmodel_test.dart @@ -1,15 +1,14 @@ import 'dart:io'; -import 'package:easy_cc_flutter/main_view_model.dart'; import 'package:easy_cc_flutter/Utils/selection_type.dart'; +import 'package:easy_cc_flutter/Utils/view_state.dart'; import 'package:easy_cc_flutter/data/model/currency.dart'; import 'package:easy_cc_flutter/data/prefs/currency_pair.dart'; import 'package:easy_cc_flutter/data/repository/repository_impl.dart'; -import 'package:easy_cc_flutter/locator.dart'; +import 'package:easy_cc_flutter/main_view_model.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:mockito/annotations.dart'; import 'package:mockito/mockito.dart'; -import 'package:easy_cc_flutter/Utils/view_state.dart'; import 'viewmodel_test.mocks.dart'; @@ -29,9 +28,7 @@ void main() { // Create mock object. repository = MockRepositoryImpl(); - locator.registerLazySingleton(() => repository); - - mainViewModel = MainViewModel(); + mainViewModel = MainViewModel(repository); }); test('get currency pair from prefs', () {