From 5309eda9141ef9f3809a2794bfa77419572e3b20 Mon Sep 17 00:00:00 2001 From: Prem Nirmal Date: Tue, 1 Feb 2022 15:37:22 +0000 Subject: [PATCH] Clean up stocks provider usage --- .../com/github/premnirmal/ticker/StocksApp.kt | 2 - .../ticker/components/AppComponent.kt | 8 +- .../premnirmal/ticker/components/AppModule.kt | 11 +-- .../premnirmal/ticker/home/HomeFragment.kt | 2 +- .../ticker/model/IStocksProvider.kt | 4 +- .../premnirmal/ticker/model/StocksProvider.kt | 78 +++++++++---------- .../ticker/network/CommitsProvider.kt | 7 +- .../premnirmal/ticker/network/NewsProvider.kt | 2 +- .../ticker/news/QuoteDetailViewModel.kt | 5 +- .../notifications/NotificationsHandler.kt | 7 +- .../ticker/portfolio/PortfolioFragment.kt | 43 +++------- .../ticker/portfolio/PortfolioViewModel.kt | 44 +++++++++++ .../premnirmal/ticker/widget/StockWidget.kt | 17 ++-- .../premnirmal/ticker/widget/WidgetData.kt | 3 - .../ticker/widget/WidgetDataProvider.kt | 2 +- .../premnirmal/ticker/mock/MockAppModule.kt | 13 ++-- app/version.properties | 4 +- 17 files changed, 136 insertions(+), 116 deletions(-) create mode 100644 app/src/main/kotlin/com/github/premnirmal/ticker/portfolio/PortfolioViewModel.kt diff --git a/app/src/main/kotlin/com/github/premnirmal/ticker/StocksApp.kt b/app/src/main/kotlin/com/github/premnirmal/ticker/StocksApp.kt index 125cf277..056c0e7d 100644 --- a/app/src/main/kotlin/com/github/premnirmal/ticker/StocksApp.kt +++ b/app/src/main/kotlin/com/github/premnirmal/ticker/StocksApp.kt @@ -28,8 +28,6 @@ open class StocksApp : MultiDexApplication() { class InjectionHolder { @Inject lateinit var analytics: Analytics @Inject lateinit var appPreferences: AppPreferences - @Inject lateinit var newsProvider: NewsProvider - @Inject lateinit var commitsProvider: CommitsProvider @Inject lateinit var notificationsHandler: NotificationsHandler @Inject lateinit var widgetDataProvider: WidgetDataProvider } diff --git a/app/src/main/kotlin/com/github/premnirmal/ticker/components/AppComponent.kt b/app/src/main/kotlin/com/github/premnirmal/ticker/components/AppComponent.kt index 04c5457a..bf3779c1 100644 --- a/app/src/main/kotlin/com/github/premnirmal/ticker/components/AppComponent.kt +++ b/app/src/main/kotlin/com/github/premnirmal/ticker/components/AppComponent.kt @@ -31,7 +31,7 @@ import com.github.premnirmal.ticker.portfolio.AddNotesActivity import com.github.premnirmal.ticker.portfolio.AddPositionActivity import com.github.premnirmal.ticker.portfolio.AlertsViewModel import com.github.premnirmal.ticker.portfolio.NotesViewModel -import com.github.premnirmal.ticker.portfolio.PortfolioFragment +import com.github.premnirmal.ticker.portfolio.PortfolioViewModel import com.github.premnirmal.ticker.portfolio.StocksAdapter import com.github.premnirmal.ticker.portfolio.search.SearchActivity import com.github.premnirmal.ticker.portfolio.search.SearchFragment @@ -53,7 +53,7 @@ import com.google.gson.Gson * Created by premnirmal on 3/3/16. */ @javax.inject.Singleton -@dagger.Component(modules = arrayOf(AppModule::class)) +@dagger.Component(modules = [AppModule::class]) interface AppComponent { // Activities @@ -128,8 +128,6 @@ interface AppComponent { fun inject(holder: BaseFragment.InjectionHolder) - fun inject(holder: PortfolioFragment.InjectionHolder) - fun inject(homeFragment: HomeFragment) fun inject(fragment: SearchFragment) @@ -163,4 +161,6 @@ interface AppComponent { fun inject(alertsViewModel: AlertsViewModel) fun inject(searchViewModel: SearchViewModel) + + fun inject(viewModel: PortfolioViewModel) } diff --git a/app/src/main/kotlin/com/github/premnirmal/ticker/components/AppModule.kt b/app/src/main/kotlin/com/github/premnirmal/ticker/components/AppModule.kt index df98b8a2..5442331d 100644 --- a/app/src/main/kotlin/com/github/premnirmal/ticker/components/AppModule.kt +++ b/app/src/main/kotlin/com/github/premnirmal/ticker/components/AppModule.kt @@ -4,8 +4,6 @@ import android.appwidget.AppWidgetManager import android.content.Context import android.content.Context.MODE_PRIVATE import android.content.SharedPreferences -import android.os.Handler -import android.os.Looper import androidx.room.Room import com.github.premnirmal.ticker.AppPreferences import com.github.premnirmal.ticker.StocksApp @@ -20,6 +18,8 @@ import com.github.premnirmal.ticker.repo.migrations.MIGRATION_1_2 import com.github.premnirmal.ticker.widget.WidgetDataProvider import dagger.Module import dagger.Provides +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers import javax.inject.Singleton /** @@ -30,10 +30,11 @@ class AppModule(private val app: StocksApp) { @Provides fun provideApplicationContext(): Context = app - @Provides @Singleton fun provideClock(): AppClock = AppClockImpl + @Singleton @Provides fun provideApplicationScope(): CoroutineScope { + return CoroutineScope(Dispatchers.Unconfined) + } - @Provides @Singleton fun provideMainThreadHandler(): Handler = - Handler(Looper.getMainLooper()) + @Provides @Singleton fun provideClock(): AppClock = AppClockImpl @Provides @Singleton fun provideDefaultSharedPreferences( context: Context diff --git a/app/src/main/kotlin/com/github/premnirmal/ticker/home/HomeFragment.kt b/app/src/main/kotlin/com/github/premnirmal/ticker/home/HomeFragment.kt index 2352288d..b0499d41 100644 --- a/app/src/main/kotlin/com/github/premnirmal/ticker/home/HomeFragment.kt +++ b/app/src/main/kotlin/com/github/premnirmal/ticker/home/HomeFragment.kt @@ -181,7 +181,7 @@ class HomeFragment : BaseFragment(), ChildFragment, PortfolioFragment.Parent { private var isTitleShowing = true override fun onOffsetChanged(appBarLayout: AppBarLayout?, verticalOffset: Int) { - val show = verticalOffset > -20 + val show = verticalOffset > -tabs.height / 2 if (show && !isTitleShowing) { subtitle.animate().alpha(1f).start() tabs.animate().alpha(1f).start() diff --git a/app/src/main/kotlin/com/github/premnirmal/ticker/model/IStocksProvider.kt b/app/src/main/kotlin/com/github/premnirmal/ticker/model/IStocksProvider.kt index 441b6427..a8c05e50 100644 --- a/app/src/main/kotlin/com/github/premnirmal/ticker/model/IStocksProvider.kt +++ b/app/src/main/kotlin/com/github/premnirmal/ticker/model/IStocksProvider.kt @@ -33,9 +33,9 @@ interface IStocksProvider { fun fetchStock(ticker: String): Flow> - fun removeStock(ticker: String): Collection + suspend fun removeStock(ticker: String): Collection - fun removeStocks(symbols: Collection) + suspend fun removeStocks(symbols: Collection) fun hasTicker(ticker: String): Boolean diff --git a/app/src/main/kotlin/com/github/premnirmal/ticker/model/StocksProvider.kt b/app/src/main/kotlin/com/github/premnirmal/ticker/model/StocksProvider.kt index 2548ced5..dc401d90 100644 --- a/app/src/main/kotlin/com/github/premnirmal/ticker/model/StocksProvider.kt +++ b/app/src/main/kotlin/com/github/premnirmal/ticker/model/StocksProvider.kt @@ -26,13 +26,12 @@ import kotlinx.coroutines.withContext import timber.log.Timber import javax.inject.Inject import javax.inject.Singleton -import kotlin.coroutines.CoroutineContext /** * Created by premnirmal on 2/28/16. */ @Singleton -class StocksProvider : IStocksProvider, CoroutineScope { +class StocksProvider : IStocksProvider { companion object { @@ -49,9 +48,8 @@ class StocksProvider : IStocksProvider, CoroutineScope { @Inject lateinit var alarmScheduler: AlarmScheduler @Inject lateinit var clock: AppClock @Inject lateinit var storage: StocksStorage + @Inject lateinit var coroutineScope: CoroutineScope - override val coroutineContext: CoroutineContext - get() = Dispatchers.Main private val exponentialBackoff: ExponentialBackoff private val tickerSet: MutableSet = HashSet() @@ -75,12 +73,12 @@ class StocksProvider : IStocksProvider, CoroutineScope { _lastFetched.tryEmit(lastFetched) val nextFetch = preferences.getLong(NEXT_FETCH, 0L) _nextFetch.tryEmit(nextFetch) - launch { + coroutineScope.launch { alarmScheduler.enqueuePeriodicRefresh(context) } runBlocking { fetchLocal() } if (lastFetched == 0L) { - launch { + coroutineScope.launch { fetch().collect() } } else { @@ -144,9 +142,7 @@ class StocksProvider : IStocksProvider, CoroutineScope { ///////////////////// override fun hasTicker(ticker: String): Boolean { - synchronized(tickerSet) { - return tickerSet.contains(ticker) - } + return tickerSet.contains(ticker) } override fun fetch(): Flow>> = flow { @@ -169,6 +165,12 @@ class StocksProvider : IStocksProvider, CoroutineScope { tickerSet.addAll(fetchedStocks.map { it.symbol }) } _tickers.emit(tickerSet.toList()) + // clean up existing tickers + ArrayList(tickerSet).forEach { ticker -> + if (!widgetDataProvider.containsTicker(ticker)) { + removeStock(ticker) + } + } storage.saveQuotes(fetchedStocks) fetchLocal() _lastFetched.emit(api.lastFetched) @@ -192,7 +194,7 @@ class StocksProvider : IStocksProvider, CoroutineScope { override fun schedule() { scheduleUpdate() - launch { + coroutineScope.launch { alarmScheduler.enqueuePeriodicRefresh(context, force = true) } } @@ -205,17 +207,17 @@ class StocksProvider : IStocksProvider, CoroutineScope { quote.symbol = ticker quoteMap[ticker] = quote saveTickers() - _tickers.tryEmit(tickerSet.toList()) - _portfolio.tryEmit(quoteMap.values.toList()) - launch { - fetchStockInternal(ticker, false).collect { result -> - if (result.wasSuccessful) { - val data = result.data - quoteMap[ticker] = data - storage.saveQuote(result.data) - _portfolio.tryEmit(quoteMap.values.toList()) - } - } + } + } + _tickers.tryEmit(tickerSet.toList()) + _portfolio.tryEmit(quoteMap.values.toList()) + coroutineScope.launch { + fetchStockInternal(ticker, false).collect { result -> + if (result.wasSuccessful) { + val data = result.data + quoteMap[ticker] = data + storage.saveQuote(result.data) + _portfolio.tryEmit(quoteMap.values.toList()) } } } @@ -240,16 +242,16 @@ class StocksProvider : IStocksProvider, CoroutineScope { position = getPosition(ticker) ?: Position(ticker) if (!tickerSet.contains(ticker)) { tickerSet.add(ticker) - _tickers.tryEmit(tickerSet.toList()) - saveTickers() } } + _tickers.emit(tickerSet.toList()) + saveTickers() val holding = Holding(ticker, shares, price) position.add(holding) quote?.position = position val id = storage.addHolding(holding) holding.id = id - _portfolio.tryEmit(quoteMap.values.toList()) + _portfolio.emit(quoteMap.values.toList()) return holding } @@ -273,43 +275,39 @@ class StocksProvider : IStocksProvider, CoroutineScope { filterNot.forEach { this.tickerSet.add(it) } saveTickers() if (filterNot.isNotEmpty()) { - launch { + coroutineScope.launch { fetch().collect() } } - _tickers.tryEmit(tickerSet.toList()) - _portfolio.tryEmit(quoteMap.values.toList()) } + _tickers.tryEmit(tickerSet.toList()) + _portfolio.tryEmit(quoteMap.values.toList()) return this.tickerSet } - override fun removeStock(ticker: String): Collection { + override suspend fun removeStock(ticker: String): Collection { synchronized(quoteMap) { tickerSet.remove(ticker) saveTickers() quoteMap.remove(ticker) - _tickers.tryEmit(tickerSet.toList()) - _portfolio.tryEmit(quoteMap.values.toList()) - } - launch { - storage.removeQuoteBySymbol(ticker) } + storage.removeQuoteBySymbol(ticker) + _tickers.emit(tickerSet.toList()) + _portfolio.emit(quoteMap.values.toList()) return tickerSet } - override fun removeStocks(symbols: Collection) { + override suspend fun removeStocks(symbols: Collection) { synchronized(quoteMap) { symbols.forEach { tickerSet.remove(it) quoteMap.remove(it) } - _tickers.tryEmit(tickerSet.toList()) - _portfolio.tryEmit(quoteMap.values.toList()) } + storage.removeQuotesBySymbol(symbols.toList()) + _tickers.emit(tickerSet.toList()) + _portfolio.emit(quoteMap.values.toList()) saveTickers() - launch { - storage.removeQuotesBySymbol(symbols.toList()) - } } override fun fetchStock(ticker: String): Flow> { @@ -334,7 +332,7 @@ class StocksProvider : IStocksProvider, CoroutineScope { } saveTickers() widgetDataProvider.updateWidgets(tickerSet.toList()) - launch { + coroutineScope.launch { storage.saveQuotes(portfolio) fetchLocal() fetch().collect() diff --git a/app/src/main/kotlin/com/github/premnirmal/ticker/network/CommitsProvider.kt b/app/src/main/kotlin/com/github/premnirmal/ticker/network/CommitsProvider.kt index 4226d6b8..e285023a 100644 --- a/app/src/main/kotlin/com/github/premnirmal/ticker/network/CommitsProvider.kt +++ b/app/src/main/kotlin/com/github/premnirmal/ticker/network/CommitsProvider.kt @@ -13,9 +13,10 @@ import javax.inject.Inject import javax.inject.Singleton @Singleton -class CommitsProvider @Inject constructor(private val githubApi: GithubApi) { - - private val coroutineScope = CoroutineScope(Dispatchers.IO) +class CommitsProvider @Inject constructor( + private val githubApi: GithubApi, + private val coroutineScope: CoroutineScope +) { private var cachedChanges: List? = null fun initCache() { diff --git a/app/src/main/kotlin/com/github/premnirmal/ticker/network/NewsProvider.kt b/app/src/main/kotlin/com/github/premnirmal/ticker/network/NewsProvider.kt index bd39e348..48326b1b 100644 --- a/app/src/main/kotlin/com/github/premnirmal/ticker/network/NewsProvider.kt +++ b/app/src/main/kotlin/com/github/premnirmal/ticker/network/NewsProvider.kt @@ -15,7 +15,7 @@ import javax.inject.Singleton @Singleton class NewsProvider { - private val coroutineScope = CoroutineScope(Dispatchers.IO) + @Inject lateinit var coroutineScope: CoroutineScope @Inject internal lateinit var newsApi: NewsApi private var cachedBusinessArticles: List = emptyList() diff --git a/app/src/main/kotlin/com/github/premnirmal/ticker/news/QuoteDetailViewModel.kt b/app/src/main/kotlin/com/github/premnirmal/ticker/news/QuoteDetailViewModel.kt index bf693dce..f1badff7 100644 --- a/app/src/main/kotlin/com/github/premnirmal/ticker/news/QuoteDetailViewModel.kt +++ b/app/src/main/kotlin/com/github/premnirmal/ticker/news/QuoteDetailViewModel.kt @@ -15,7 +15,6 @@ import com.github.premnirmal.ticker.network.data.NewsArticle import com.github.premnirmal.ticker.network.data.Quote import com.github.premnirmal.ticker.widget.WidgetData import com.github.premnirmal.ticker.widget.WidgetDataProvider -import kotlinx.coroutines.flow.collect import kotlinx.coroutines.launch import javax.inject.Inject @@ -77,7 +76,9 @@ class QuoteDetailViewModel : ViewModel() { fun removeStock(ticker: String) { val widgetData = widgetDataProvider.widgetDataWithStock(ticker) widgetData.forEach { it.removeStock(ticker) } - stocksProvider.removeStock(ticker) + viewModelScope.launch { + stocksProvider.removeStock(ticker) + } } fun fetchChartData(symbol: String, range: Range) { diff --git a/app/src/main/kotlin/com/github/premnirmal/ticker/notifications/NotificationsHandler.kt b/app/src/main/kotlin/com/github/premnirmal/ticker/notifications/NotificationsHandler.kt index 0daad5e6..81723ca8 100644 --- a/app/src/main/kotlin/com/github/premnirmal/ticker/notifications/NotificationsHandler.kt +++ b/app/src/main/kotlin/com/github/premnirmal/ticker/notifications/NotificationsHandler.kt @@ -25,8 +25,6 @@ import com.github.premnirmal.ticker.repo.StocksStorage import com.github.premnirmal.tickerwidget.BuildConfig import com.github.premnirmal.tickerwidget.R import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.flow.collect import kotlinx.coroutines.launch import org.threeten.bp.Duration import org.threeten.bp.Instant @@ -46,7 +44,8 @@ class NotificationsHandler @Inject constructor( private val stocksStorage: StocksStorage, private val alarmScheduler: AlarmScheduler, private val appPreferences: AppPreferences, - private val clock: AppClock + private val clock: AppClock, + private val coroutineScope: CoroutineScope ) { companion object { @@ -56,8 +55,6 @@ class NotificationsHandler @Inject constructor( private const val PREFS_NOTIFICATIONS = "${BuildConfig.APPLICATION_ID}.notifications.PREFS" } - private val coroutineScope = CoroutineScope(Dispatchers.Default) - private val notificationFactory: NotificationFactory by lazy { NotificationFactory(context, appPreferences) } diff --git a/app/src/main/kotlin/com/github/premnirmal/ticker/portfolio/PortfolioFragment.kt b/app/src/main/kotlin/com/github/premnirmal/ticker/portfolio/PortfolioFragment.kt index b043fa75..24bb832b 100644 --- a/app/src/main/kotlin/com/github/premnirmal/ticker/portfolio/PortfolioFragment.kt +++ b/app/src/main/kotlin/com/github/premnirmal/ticker/portfolio/PortfolioFragment.kt @@ -9,26 +9,21 @@ import android.view.View import android.view.ViewGroup import android.widget.PopupMenu import androidx.appcompat.app.AlertDialog -import androidx.lifecycle.lifecycleScope +import androidx.fragment.app.viewModels import androidx.recyclerview.widget.ItemTouchHelper import com.github.premnirmal.ticker.analytics.ClickEvent import com.github.premnirmal.ticker.base.BaseFragment import com.github.premnirmal.ticker.components.InAppMessage -import com.github.premnirmal.ticker.components.Injector import com.github.premnirmal.ticker.home.ChildFragment -import com.github.premnirmal.ticker.model.IStocksProvider import com.github.premnirmal.ticker.network.data.Quote import com.github.premnirmal.ticker.news.QuoteDetailActivity import com.github.premnirmal.ticker.portfolio.StocksAdapter.QuoteClickListener import com.github.premnirmal.ticker.portfolio.drag_drop.OnStartDragListener import com.github.premnirmal.ticker.portfolio.drag_drop.SimpleItemTouchHelperCallback import com.github.premnirmal.ticker.ui.SpacingDecoration -import com.github.premnirmal.ticker.widget.WidgetDataProvider import com.github.premnirmal.tickerwidget.R import kotlinx.android.synthetic.main.fragment_portfolio.stockList import kotlinx.android.synthetic.main.fragment_portfolio.view_flipper -import kotlinx.coroutines.launch -import javax.inject.Inject /** * Created by premnirmal on 2/25/16. @@ -61,27 +56,13 @@ class PortfolioFragment : BaseFragment(), ChildFragment, QuoteClickListener, OnS } } - /** - * Using this injection holder because in unit tests, we use a mockito subclass of this fragment. - * Without this holder, dagger is unable to inject dependencies into this class. - */ - class InjectionHolder { - - @Inject internal lateinit var widgetDataProvider: WidgetDataProvider - @Inject internal lateinit var stocksProvider: IStocksProvider - - init { - Injector.appComponent.inject(this) - } - } - override val simpleName: String = "PortfolioFragment" - private lateinit var holder: InjectionHolder + private val viewModel: PortfolioViewModel by viewModels() private val parent: Parent get() = parentFragment as Parent private var widgetId = AppWidgetManager.INVALID_APPWIDGET_ID private val stocksAdapter by lazy { - val widgetData = holder.widgetDataProvider.dataForWidgetId(widgetId) + val widgetData = viewModel.dataForWidgetId(widgetId) StocksAdapter(widgetData, this as QuoteClickListener, this as OnStartDragListener) } private var itemTouchHelper: ItemTouchHelper? = null @@ -117,7 +98,6 @@ class PortfolioFragment : BaseFragment(), ChildFragment, QuoteClickListener, OnS override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) - holder = InjectionHolder() widgetId = requireArguments().getInt(KEY_WIDGET_ID) } @@ -148,16 +128,14 @@ class PortfolioFragment : BaseFragment(), ChildFragment, QuoteClickListener, OnS val listViewState = state.getParcelable(LIST_INSTANCE_STATE) listViewState?.let { stockList?.layoutManager?.onRestoreInstanceState(it) } } - val widgetData = holder.widgetDataProvider.dataForWidgetId(widgetId) + val widgetData = viewModel.dataForWidgetId(widgetId) if (widgetData.getTickers().isEmpty()) { view_flipper.displayedChild = 0 } else { view_flipper.displayedChild = 1 } - lifecycleScope.launch { - holder.stocksProvider.portfolio.collect { - update() - } + viewModel.portfolio.observe(viewLifecycleOwner) { + update() } } @@ -172,14 +150,13 @@ class PortfolioFragment : BaseFragment(), ChildFragment, QuoteClickListener, OnS private fun promptRemove(quote: Quote?) { quote?.let { - val widgetData = holder.widgetDataProvider.dataForWidgetId(widgetId) + val widgetData = viewModel.dataForWidgetId(widgetId) AlertDialog.Builder(requireContext()) .setTitle(R.string.remove) .setMessage(getString(R.string.remove_prompt, it.symbol)) .setPositiveButton(R.string.remove) { dialog, _ -> - widgetData.removeStock(it.symbol) + viewModel.removeStock(widgetId, it.symbol) stocksAdapter.remove(it) - holder.widgetDataProvider.broadcastUpdateWidget(widgetId) dialog.dismiss() } .setNegativeButton(R.string.cancel) { dialog, _ -> dialog.dismiss() } @@ -197,11 +174,11 @@ class PortfolioFragment : BaseFragment(), ChildFragment, QuoteClickListener, OnS override fun onStartDrag(viewHolder: androidx.recyclerview.widget.RecyclerView.ViewHolder) { parent.onDragStarted() - val widgetData = holder.widgetDataProvider.dataForWidgetId(widgetId) + val widgetData = viewModel.dataForWidgetId(widgetId) if (widgetData.autoSortEnabled()) { widgetData.setAutoSort(false) update() - holder.widgetDataProvider.broadcastUpdateWidget(widgetId) + viewModel.broadcastUpdateWidget(widgetId) InAppMessage.showMessage(requireView(), R.string.autosort_disabled) } else { itemTouchHelper?.startDrag(viewHolder) diff --git a/app/src/main/kotlin/com/github/premnirmal/ticker/portfolio/PortfolioViewModel.kt b/app/src/main/kotlin/com/github/premnirmal/ticker/portfolio/PortfolioViewModel.kt new file mode 100644 index 00000000..c46a7f8e --- /dev/null +++ b/app/src/main/kotlin/com/github/premnirmal/ticker/portfolio/PortfolioViewModel.kt @@ -0,0 +1,44 @@ +package com.github.premnirmal.ticker.portfolio + +import androidx.lifecycle.LiveData +import androidx.lifecycle.ViewModel +import androidx.lifecycle.asLiveData +import androidx.lifecycle.viewModelScope +import com.github.premnirmal.ticker.components.Injector +import com.github.premnirmal.ticker.model.IStocksProvider +import com.github.premnirmal.ticker.network.data.Quote +import com.github.premnirmal.ticker.widget.WidgetData +import com.github.premnirmal.ticker.widget.WidgetDataProvider +import kotlinx.coroutines.launch +import javax.inject.Inject + +class PortfolioViewModel : ViewModel() { + + @Inject internal lateinit var widgetDataProvider: WidgetDataProvider + @Inject internal lateinit var stocksProvider: IStocksProvider + + init { + Injector.appComponent.inject(this) + } + + val portfolio: LiveData> by lazy { + stocksProvider.portfolio.asLiveData() + } + + fun dataForWidgetId(widgetId: Int): WidgetData { + return widgetDataProvider.dataForWidgetId(widgetId) + } + + fun removeStock(widgetId: Int, ticker: String) { + viewModelScope.launch { + dataForWidgetId(widgetId).removeStock(ticker) + if (!widgetDataProvider.containsTicker(ticker)) { + stocksProvider.removeStock(ticker) + } + } + } + + fun broadcastUpdateWidget(widgetId: Int) { + widgetDataProvider.broadcastUpdateWidget(widgetId) + } +} \ No newline at end of file diff --git a/app/src/main/kotlin/com/github/premnirmal/ticker/widget/StockWidget.kt b/app/src/main/kotlin/com/github/premnirmal/ticker/widget/StockWidget.kt index 3c61fb71..0150ce9e 100644 --- a/app/src/main/kotlin/com/github/premnirmal/ticker/widget/StockWidget.kt +++ b/app/src/main/kotlin/com/github/premnirmal/ticker/widget/StockWidget.kt @@ -16,6 +16,8 @@ import com.github.premnirmal.ticker.home.ParanormalActivity import com.github.premnirmal.ticker.model.IStocksProvider import com.github.premnirmal.ticker.model.IStocksProvider.FetchState import com.github.premnirmal.tickerwidget.R +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.launch import org.threeten.bp.Instant import org.threeten.bp.ZoneId import org.threeten.bp.ZonedDateTime @@ -33,6 +35,7 @@ class StockWidget : AppWidgetProvider() { @Inject internal lateinit var stocksProvider: IStocksProvider @Inject internal lateinit var widgetDataProvider: WidgetDataProvider @Inject internal lateinit var appPreferences: AppPreferences + @Inject internal lateinit var coroutineScope: CoroutineScope var injected = false @@ -97,12 +100,14 @@ class StockWidget : AppWidgetProvider() { id.forEach { widgetId -> val removed = widgetDataProvider.removeWidget(widgetId) if (widgetDataProvider.getAppWidgetIds().isNotEmpty()) { - removed?.getTickers() - ?.forEach { ticker -> - if (!widgetDataProvider.containsTicker(ticker)) { - stocksProvider.removeStock(ticker) - } - } +// removed?.getTickers() +// ?.forEach { ticker -> +// coroutineScope.launch { +// if (!widgetDataProvider.containsTicker(ticker)) { +// stocksProvider.removeStock(ticker) +// } +// } +// } } } } diff --git a/app/src/main/kotlin/com/github/premnirmal/ticker/widget/WidgetData.kt b/app/src/main/kotlin/com/github/premnirmal/ticker/widget/WidgetData.kt index 80cf52f9..a6bd9227 100644 --- a/app/src/main/kotlin/com/github/premnirmal/ticker/widget/WidgetData.kt +++ b/app/src/main/kotlin/com/github/premnirmal/ticker/widget/WidgetData.kt @@ -315,9 +315,6 @@ class WidgetData { synchronized(tickerList) { tickerList.remove(ticker) } - if (!widgetDataProvider.containsTicker(ticker)) { - stocksProvider.removeStock(ticker) - } save() } diff --git a/app/src/main/kotlin/com/github/premnirmal/ticker/widget/WidgetDataProvider.kt b/app/src/main/kotlin/com/github/premnirmal/ticker/widget/WidgetDataProvider.kt index fda4ff8a..c895647b 100644 --- a/app/src/main/kotlin/com/github/premnirmal/ticker/widget/WidgetDataProvider.kt +++ b/app/src/main/kotlin/com/github/premnirmal/ticker/widget/WidgetDataProvider.kt @@ -71,7 +71,7 @@ class WidgetDataProvider { removed?.let { if (widgetCount == 0) { val widget = dataForWidgetId(AppWidgetManager.INVALID_APPWIDGET_ID) - widget.addTickers(it.getTickers()) + widget.addAllFromStocksProvider() } it.onWidgetRemoved() } diff --git a/app/src/test/kotlin/com/github/premnirmal/ticker/mock/MockAppModule.kt b/app/src/test/kotlin/com/github/premnirmal/ticker/mock/MockAppModule.kt index 7063f83c..753664bf 100644 --- a/app/src/test/kotlin/com/github/premnirmal/ticker/mock/MockAppModule.kt +++ b/app/src/test/kotlin/com/github/premnirmal/ticker/mock/MockAppModule.kt @@ -3,8 +3,6 @@ package com.github.premnirmal.ticker.mock import android.appwidget.AppWidgetManager import android.content.Context import android.content.SharedPreferences -import android.os.Handler -import android.os.Looper import com.github.premnirmal.ticker.AppPreferences import com.github.premnirmal.ticker.StocksApp import com.github.premnirmal.ticker.analytics.Analytics @@ -14,20 +12,23 @@ import com.github.premnirmal.ticker.repo.QuotesDB import com.github.premnirmal.ticker.repo.StocksStorage import dagger.Module import dagger.Provides +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers import javax.inject.Singleton /** * Created by premnirmal on 3/22/17. */ -@Module(includes = arrayOf(MockNetworkModule::class)) +@Module(includes = [MockNetworkModule::class]) class MockAppModule(private val app: StocksApp) { @Provides internal fun provideApplicationContext(): Context = app - @Provides @Singleton internal fun provideClock(): AppClock = Mocker.provide(AppClock::class) + @Singleton @Provides internal fun provideCoroutineScope(): CoroutineScope { + return CoroutineScope(Dispatchers.Unconfined) + } - @Provides @Singleton internal fun provideMainThreadHandler(): Handler = - Handler(Looper.getMainLooper()) + @Provides @Singleton internal fun provideClock(): AppClock = Mocker.provide(AppClock::class) @Provides @Singleton internal fun provideDefaultSharedPreferences( context: Context): SharedPreferences { diff --git a/app/version.properties b/app/version.properties index 3878d982..b2cd6f7e 100644 --- a/app/version.properties +++ b/app/version.properties @@ -1,2 +1,2 @@ -versionName=3.9.763 -versionCode=300900763 \ No newline at end of file +versionName=3.9.764 +versionCode=300900764 \ No newline at end of file