Skip to content

Commit

Permalink
Updates to scheduling and reduce list of updates. ALso update google …
Browse files Browse the repository at this point in the history
…services and crashlytics
  • Loading branch information
premnirmal committed May 19, 2023
1 parent a895bb0 commit 0fecc3f
Show file tree
Hide file tree
Showing 14 changed files with 78 additions and 95 deletions.
4 changes: 2 additions & 2 deletions app/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,8 @@ buildscript {
maven { url "https://jitpack.io" }
}
dependencies {
classpath "com.google.gms:google-services:4.3.10"
classpath "com.google.firebase:firebase-crashlytics-gradle:2.7.1"
classpath "com.google.gms:google-services:4.3.15"
classpath "com.google.firebase:firebase-crashlytics-gradle:2.9.5"

classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
}
Expand Down
15 changes: 8 additions & 7 deletions app/src/main/kotlin/com/github/premnirmal/ticker/Extensions.kt
Original file line number Diff line number Diff line change
Expand Up @@ -118,40 +118,41 @@ fun Activity.showDialog(
.show()
}

fun Activity.showDialog(message: String): AlertDialog {
fun Activity.showDialog(message: String, cancelable: Boolean = true): AlertDialog {
return AlertDialog.Builder(this)
.setMessage(message)
.setCancelable(false)
.setCancelable(cancelable)
.setNeutralButton(R.string.ok) { dialog: DialogInterface, _: Int -> dialog.dismiss() }
.show()
}

fun Activity.showDialog(
title: String,
message: String
message: String,
cancelable: Boolean = true
): AlertDialog {
return AlertDialog.Builder(this)
.setTitle(title)
.setMessage(message)
.setCancelable(false)
.setCancelable(cancelable)
.setNeutralButton(R.string.ok) { dialog: DialogInterface, _: Int -> dialog.dismiss() }
.show()
}

fun Fragment.showDialog(
message: String,
listener: OnClickListener
listener: OnClickListener,
) {
AlertDialog.Builder(requireContext())
.setMessage(message)
.setNeutralButton(getString(R.string.ok), listener)
.show()
}

fun Fragment.showDialog(message: String): AlertDialog {
fun Fragment.showDialog(message: String, cancelable: Boolean = true): AlertDialog {
return AlertDialog.Builder(requireContext())
.setMessage(message)
.setCancelable(false)
.setCancelable(cancelable)
.setNeutralButton(R.string.ok) { dialog: DialogInterface, _: Int -> dialog.dismiss() }
.show()
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,25 +9,22 @@ import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import javax.inject.Inject
import kotlin.coroutines.CoroutineContext

/**
* Created by premnirmal on 2/27/16.
*/
class UpdateReceiver : BroadcastReceiver(), CoroutineScope {
class UpdateReceiver : BroadcastReceiver() {

@Inject internal lateinit var stocksProvider: StocksProvider

override val coroutineContext: CoroutineContext
get() = Dispatchers.Main
@Inject internal lateinit var coroutineScope: CoroutineScope

override fun onReceive(
context: Context,
intent: Intent
) {
Injector.appComponent().inject(this)
val pendingResult = goAsync()
launch {
coroutineScope.launch(Dispatchers.Main) {
stocksProvider.fetch()
pendingResult.finish()
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import androidx.work.BackoffPolicy.LINEAR
import androidx.work.Constraints
import androidx.work.ExistingPeriodicWorkPolicy.REPLACE
import androidx.work.NetworkType.CONNECTED
import androidx.work.OneTimeWorkRequestBuilder
import androidx.work.PeriodicWorkRequestBuilder
import androidx.work.WorkInfo.State.ENQUEUED
import androidx.work.WorkInfo.State.RUNNING
Expand Down Expand Up @@ -137,6 +138,16 @@ class AlarmScheduler @Inject constructor(
alarmManager.set(
AlarmManager.ELAPSED_REALTIME_WAKEUP, clock.elapsedRealtime() + msToNextAlarm, pendingIntent
)
val constraints = Constraints.Builder()
.setRequiredNetworkType(CONNECTED)
.build()
val request = OneTimeWorkRequestBuilder<RefreshWorker>()
.setInitialDelay(msToNextAlarm, MILLISECONDS)
.addTag(RefreshWorker.TAG)
.setBackoffCriteria(LINEAR, 1L, MINUTES)
.setConstraints(constraints)
.build()
workManager.enqueue(request)
return nextAlarmDate
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,11 +21,10 @@ class RefreshWorker(context: Context, params: WorkerParameters) : CoroutineWorke
Injector.appComponent().inject(this)
val result = stocksProvider.fetch()
if (result.hasError) {
Result.failure()
Result.retry()
} else {
Result.success()
}
Result.success()
} else {
Result.retry()
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,10 +35,10 @@ class StocksProvider @Inject constructor(
@ApplicationContext private val context: Context,
private val api: StocksApi,
private val preferences: SharedPreferences,
private val clock: AppClock,
private val appPreferences: AppPreferences,
private val widgetDataProvider: WidgetDataProvider,
private val alarmScheduler: AlarmScheduler,
private val clock: AppClock,
private val storage: StocksStorage,
private val coroutineScope: CoroutineScope,
private val exponentialBackoff: ExponentialBackoff
Expand All @@ -55,7 +55,7 @@ class StocksProvider @Inject constructor(
private val quoteMap: MutableMap<String, Quote> = HashMap()
private val _fetchState = MutableStateFlow<FetchState>(FetchState.NotFetched)
private val _nextFetch = MutableStateFlow<Long>(0)
private val _lastFetched = MutableStateFlow<Long>(0)
private var lastFetched = 0L
private val _tickers = MutableStateFlow<List<String>>(emptyList())
private val _portfolio = MutableStateFlow<List<Quote>>(emptyList())

Expand All @@ -66,8 +66,8 @@ class StocksProvider @Inject constructor(
this.tickerSet.addAll(DEFAULT_STOCKS)
}
_tickers.tryEmit(tickerSet.toList())
val lastFetched = preferences.getLong(LAST_FETCHED, 0L)
_lastFetched.tryEmit(lastFetched)
val lastFetchedLoaded = preferences.getLong(LAST_FETCHED, 0L)
lastFetched = lastFetchedLoaded
val nextFetch = preferences.getLong(NEXT_FETCH, 0L)
_nextFetch.tryEmit(nextFetch)
runBlocking { fetchLocal() }
Expand All @@ -94,7 +94,7 @@ class StocksProvider @Inject constructor(

private fun saveLastFetched() {
preferences.edit()
.putLong(LAST_FETCHED, _lastFetched.value)
.putLong(LAST_FETCHED, lastFetched)
.apply()
}

Expand All @@ -103,7 +103,7 @@ class StocksProvider @Inject constructor(
}

private fun scheduleUpdate() {
scheduleUpdateWithMs(alarmScheduler.msToNextAlarm(_lastFetched.value))
scheduleUpdateWithMs(alarmScheduler.msToNextAlarm(lastFetched))
}

private fun scheduleUpdateWithMs(
Expand All @@ -112,7 +112,7 @@ class StocksProvider @Inject constructor(
val updateTime = alarmScheduler.scheduleUpdate(msToNextAlarm, context)
_nextFetch.tryEmit(updateTime.toInstant().toEpochMilli())
preferences.edit()
.putLong(NEXT_FETCH, _nextFetch.value)
.putLong(NEXT_FETCH, updateTime.toInstant().toEpochMilli())
.apply()
appPreferences.setRefreshing(false)
widgetDataProvider.broadcastUpdateAllWidgets()
Expand Down Expand Up @@ -178,16 +178,16 @@ class StocksProvider @Inject constructor(
}
_tickers.emit(tickerSet.toList())
// clean up existing tickers
ArrayList(tickerSet).forEach { ticker ->
tickerSet.toSet().forEach { ticker ->
if (!widgetDataProvider.containsTicker(ticker)) {
removeStock(ticker)
}
}
storage.saveQuotes(fetchedStocks)
fetchLocal()
if (allowScheduling) {
_lastFetched.emit(api.lastFetched)
_fetchState.emit(FetchState.Success(api.lastFetched))
lastFetched = clock.currentTimeMillis()
_fetchState.emit(FetchState.Success(lastFetched))
saveLastFetched()
exponentialBackoff.reset()
scheduleUpdate()
Expand All @@ -209,16 +209,14 @@ class StocksProvider @Inject constructor(
}
FetchResult.failure<List<Quote>>(FetchException("Failed to fetch", ex))
} finally {
if (allowScheduling) {
appPreferences.setRefreshing(false)
}
appPreferences.setRefreshing(false)
}
}
}

fun schedule() {
scheduleUpdate()
coroutineScope.launch {
runBlocking {
alarmScheduler.enqueuePeriodicRefresh(force = true)
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,9 +46,13 @@ class CommitsProvider @Inject constructor(
suspend fun fetchWhatsNew(): FetchResult<List<String>> {
with(fetchRepoCommits()) {
return if (wasSuccessful) {
FetchResult.success(data.map {
it.commit.message.replace("\n", "").capitalize()
})
FetchResult.success(
data.filterNot {
it.commit.message.contains("Vcode++") || it.commit.message.contains("vcode++")
}.map { commit ->
commit.commit.message.replace("\n", "").replaceFirstChar { if (it.isLowerCase()) it.titlecase() else it.toString() }
}
)
} else FetchResult.failure(error)
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -107,50 +107,26 @@ class NetworkModule {

@Provides @Singleton internal fun provideYahooFinanceInitialLoad(
@ApplicationContext context: Context,
cookieJar: YahooFinanceCookies
@Named("yahoo") okHttpClient: OkHttpClient
): YahooFinanceInitialLoad {
val retrofit = Retrofit.Builder()
.client(OkHttpClient().newBuilder()
.addInterceptor { chain ->
val original = chain.request()
val newRequest = original
.newBuilder()
.removeHeader("Accept")
.addHeader("Accept", "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8")
.build()
chain.proceed(newRequest)
}
.cookieJar(cookieJar)
.client(okHttpClient)
.addConverterFactory(ScalarsConverterFactory.create())
.baseUrl(context.getString(R.string.yahoo_initial_load_endpoint))
.build()
)
.addConverterFactory(ScalarsConverterFactory.create())
.baseUrl(context.getString(R.string.yahoo_initial_load_endpoint))
.build()
val yahooFinance = retrofit.create(YahooFinanceInitialLoad::class.java)
return yahooFinance
}

@Provides @Singleton internal fun provideYahooFinanceCrumb(
@ApplicationContext context: Context,
cookieJar: YahooFinanceCookies
@Named("yahoo") okHttpClient: OkHttpClient
): YahooFinanceCrumb {
val retrofit = Retrofit.Builder()
.client(OkHttpClient().newBuilder()
.addInterceptor { chain ->
val original = chain.request()
val newRequest = original
.newBuilder()
.removeHeader("Accept")
.addHeader("Accept", "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8")
.build()
chain.proceed(newRequest)
}
.cookieJar(cookieJar)
.client(okHttpClient)
.addConverterFactory(ScalarsConverterFactory.create())
.baseUrl(context.getString(R.string.yahoo_endpoint))
.build()
)
.addConverterFactory(ScalarsConverterFactory.create())
.baseUrl(context.getString(R.string.yahoo_endpoint))
.build()
val yahooFinance = retrofit.create(YahooFinanceCrumb::class.java)
return yahooFinance
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,17 +1,14 @@
package com.github.premnirmal.ticker.network

import com.github.premnirmal.ticker.AppPreferences
import com.github.premnirmal.ticker.components.AppClock
import com.github.premnirmal.ticker.model.FetchException
import com.github.premnirmal.ticker.model.FetchResult
import com.github.premnirmal.ticker.network.data.Quote
import com.github.premnirmal.ticker.network.data.QuoteSummary
import com.github.premnirmal.ticker.network.data.SuggestionsNet.SuggestionNet
import com.github.premnirmal.ticker.network.data.YahooQuoteNet
import com.github.premnirmal.ticker.network.data.YahooResponse
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import okhttp3.FormBody
import timber.log.Timber
Expand All @@ -26,15 +23,11 @@ class StocksApi @Inject constructor(
private val yahooFinanceInitialLoad: YahooFinanceInitialLoad,
private val yahooFinanceCrumb: YahooFinanceCrumb,
private val yahooFinance: YahooFinance,
private val coroutineScope: CoroutineScope,
private val appPreferences: AppPreferences,
private val yahooQuoteDetails: YahooQuoteDetails,
private val suggestionApi: SuggestionApi,
private val clock: AppClock
private val suggestionApi: SuggestionApi
) {

var lastFetched: Long = 0

val csrfTokenMatchPattern by lazy {
Regex("csrfToken\" value=\"(.+)\">")
}
Expand Down Expand Up @@ -95,7 +88,6 @@ class StocksApi @Inject constructor(
withContext(Dispatchers.IO) {
try {
val quoteNets = getStocksYahoo(tickerList)
lastFetched = clock.currentTimeMillis()
return@withContext FetchResult.success(quoteNets.toQuoteMap().toOrderedList(tickerList))
} catch (ex: Exception) {
Timber.e(ex)
Expand Down Expand Up @@ -134,7 +126,10 @@ class StocksApi @Inject constructor(
}
}

private suspend fun getStocksYahoo(tickerList: List<String>) = withContext(Dispatchers.IO) {
private suspend fun getStocksYahoo(
tickerList: List<String>,
invocationCount: Int = 1
): List<YahooQuoteNet> = withContext(Dispatchers.IO) {
val crumb = appPreferences.getCrumb()
if (crumb.isNullOrEmpty()) {
loadCrumb()
Expand All @@ -146,6 +141,9 @@ class StocksApi @Inject constructor(
if (quotesResponse.code() == 401) {
appPreferences.setCrumb(null)
loadCrumb()
if (invocationCount == 1) {
return@withContext getStocksYahoo(tickerList, invocationCount + 1)
}
}
} catch (ex: Exception) {
Timber.e(ex)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,25 +9,22 @@ import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import javax.inject.Inject
import kotlin.coroutines.CoroutineContext

/**
* Created by premnirmal on 2/26/16.
*/
class RefreshReceiver : BroadcastReceiver(), CoroutineScope {
class RefreshReceiver : BroadcastReceiver() {

@Inject internal lateinit var stocksProvider: StocksProvider

override val coroutineContext: CoroutineContext
get() = Dispatchers.Main
@Inject internal lateinit var coroutineScope: CoroutineScope

override fun onReceive(
context: Context,
intent: Intent
) {
Injector.appComponent().inject(this)
val pendingResult = goAsync()
launch {
coroutineScope.launch(Dispatchers.Main) {
stocksProvider.fetch()
pendingResult.finish()
}
Expand Down
Loading

0 comments on commit 0fecc3f

Please sign in to comment.