From 0619f72bb0aefa5520e4c199cf1c5e4e8ea1fb57 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mathieu=20M=C3=A9a?= Date: Wed, 11 Mar 2026 17:05:57 -0400 Subject: [PATCH 1/2] Twitter/X news provider > fix cache validity... --- .../commons/provider/TwitterNewsProvider.kt | 28 ++++++------------- .../commons/provider/news/NewsProvider.java | 21 +++++++------- 2 files changed, 20 insertions(+), 29 deletions(-) diff --git a/src/main/java/org/mtransit/android/commons/provider/TwitterNewsProvider.kt b/src/main/java/org/mtransit/android/commons/provider/TwitterNewsProvider.kt index 6857efac..391d0fdb 100644 --- a/src/main/java/org/mtransit/android/commons/provider/TwitterNewsProvider.kt +++ b/src/main/java/org/mtransit/android/commons/provider/TwitterNewsProvider.kt @@ -4,10 +4,8 @@ import android.annotation.SuppressLint import android.content.Context import android.content.UriMatcher import android.net.Uri -import android.os.Build import android.text.TextUtils import androidx.annotation.IntegerRes -import androidx.annotation.RequiresApi import androidx.annotation.StringRes import androidx.core.content.ContentProviderCompat import com.google.gson.GsonBuilder @@ -55,15 +53,15 @@ class TwitterNewsProvider : NewsProvider() { private const val FORCE_REFRESH = false // private const val FORCE_REFRESH = true // DEBUG - private val VALIDITY_DEBUG_FACTOR = if (BuildConfig.DEBUG) 1L else 2L - private val VALIDITY_EXPANSIVE_API_FACTOR = VALIDITY_DEBUG_FACTOR * 1L + // private val VALIDITY_DEBUG_FACTOR = if (BuildConfig.DEBUG) 1L else 1L + private const val VALIDITY_EXPANSIVE_API_FACTOR = 1L // https://docs.x.com/x-api/fundamentals/rate-limits (Basic) // - [GET /2/users/by/username/:username] 500 requests / 24 hours PER APP // - [GET /2/tweets] 15 requests / 15 mins PER APP private val NEWS_MAX_VALIDITY_IN_MS = MAX_CACHE_VALIDITY_MS private val NEWS_VALIDITY_IN_MS = TimeUnit.HOURS.toMillis(24L) * VALIDITY_EXPANSIVE_API_FACTOR - private val NEWS_VALIDITY_IN_FOCUS_IN_MS = TimeUnit.HOURS.toMillis(2L) * VALIDITY_EXPANSIVE_API_FACTOR + private val NEWS_VALIDITY_IN_FOCUS_IN_MS = TimeUnit.HOURS.toMillis(1L) * VALIDITY_EXPANSIVE_API_FACTOR private val NEWS_MIN_DURATION_BETWEEN_REFRESH_IN_MS = TimeUnit.MINUTES.toMillis(30L) * VALIDITY_EXPANSIVE_API_FACTOR private val NEWS_MIN_DURATION_BETWEEN_REFRESH_IN_FOCUS_IN_MS = TimeUnit.MINUTES.toMillis(15L) * VALIDITY_EXPANSIVE_API_FACTOR @@ -215,6 +213,7 @@ class TwitterNewsProvider : NewsProvider() { R.array.twitter_screen_names_severity ).toList() } + @get:IntegerRes private val _userNamesSeverityDefaultResId: Int get() = R.integer.news_provider_severity_info_agency @@ -223,6 +222,7 @@ class TwitterNewsProvider : NewsProvider() { R.array.twitter_screen_names_noteworthy ).toList().map { it.toLong() } } + @get:StringRes private val _userNamesNoteworthyDefaultResId: Int get() = R.string.news_provider_noteworthy_warning @@ -327,16 +327,12 @@ class TwitterNewsProvider : NewsProvider() { } override fun getNewNews(newsFilter: NewsProviderContract.Filter): ArrayList? { - if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) { - return getCachedNews(newsFilter) - } this.providedBearerToken = SecureStringUtils.dec(newsFilter.getProvidedEncryptKey(KeysIds.TWITTER_BEARER_TOKEN)) this.providedCachedApiUrl = SecureStringUtils.dec(newsFilter.getProvidedEncryptKey(KeysIds.TWITTER_CACHED_API_URL)) updateAgencyNewsDataIfRequired(requireContextCompat(), newsFilter.isInFocusOrDefault) return getCachedNews(newsFilter) } - @RequiresApi(Build.VERSION_CODES.O) private fun updateAgencyNewsDataIfRequired(context: Context, inFocus: Boolean) { if (FORCE_REFRESH) { TwitterStorage.saveLastUpdateMs(context, 0L) // force refresh @@ -352,7 +348,6 @@ class TwitterNewsProvider : NewsProvider() { updateAgencyNewsDataIfRequiredSync(context, lastUpdateInMs, inFocus) } - @RequiresApi(Build.VERSION_CODES.O) @Synchronized private fun updateAgencyNewsDataIfRequiredSync( context: Context, @@ -374,14 +369,12 @@ class TwitterNewsProvider : NewsProvider() { deleteAllRequired = true // too old to display } val minUpdateMs = newsMaxValidityInMs.coerceAtMost(getNewsValidityInMs(inFocus)) - if (deleteAllRequired - || lastUpdateInMs + minUpdateMs < nowInMs - ) { - updateAllAgencyNewsDataFromWWW(context, deleteAllRequired) // try to update + if (!deleteAllRequired && lastUpdateInMs + minUpdateMs >= nowInMs) { + return } + updateAllAgencyNewsDataFromWWW(context, deleteAllRequired) // try to update } - @RequiresApi(Build.VERSION_CODES.O) private fun updateAllAgencyNewsDataFromWWW(context: Context, deleteAllRequired: Boolean) { var deleteAllDone = false @Suppress("SimplifyBooleanWithConstants") @@ -407,7 +400,6 @@ class TwitterNewsProvider : NewsProvider() { private fun getTwitterApi(context: Context, baseHostUrl: String) = _twitterApi ?: createTwitterApi(context, baseHostUrl).also { _twitterApi = it } - @RequiresApi(Build.VERSION_CODES.O) private fun loadAgencyNewsDataFromWWW(context: Context): ArrayList? { // @Suppress("ConstantConditionIf") // if (true) { @@ -460,7 +452,6 @@ class TwitterNewsProvider : NewsProvider() { private fun getUserName(screenName: String) = "@$screenName" - @RequiresApi(Build.VERSION_CODES.O) private fun loadUserTimeline( context: Context, twitterApi: TwitterV2Api, @@ -583,7 +574,6 @@ class TwitterNewsProvider : NewsProvider() { return response?.data?.id?.takeIf { it.isNotBlank() } } - @RequiresApi(Build.VERSION_CODES.O) private fun readNews( context: Context, tweet: Tweet, @@ -797,4 +787,4 @@ class TwitterNewsProvider : NewsProvider() { } override fun getNewsLanguages() = _languages -} \ No newline at end of file +} diff --git a/src/main/java/org/mtransit/android/commons/provider/news/NewsProvider.java b/src/main/java/org/mtransit/android/commons/provider/news/NewsProvider.java index 41e5e888..bb2079fb 100644 --- a/src/main/java/org/mtransit/android/commons/provider/news/NewsProvider.java +++ b/src/main/java/org/mtransit/android/commons/provider/news/NewsProvider.java @@ -152,20 +152,20 @@ public static Cursor queryS(@NonNull NewsProviderContract provider, @NonNull Uri @Nullable private static Cursor getNews(@NonNull NewsProviderContract provider, @Nullable String selection) { - NewsProviderContract.Filter newsFilter = NewsProviderContract.Filter.fromJSONString(selection); + final NewsProviderContract.Filter newsFilter = NewsProviderContract.Filter.fromJSONString(selection); if (newsFilter == null) { return getNewsCursor(null); } if (NewsProviderContract.Filter.isUUIDFilter(newsFilter)) { return provider.getNewsFromDB(newsFilter); } - long nowInMs = TimeUtils.currentTimeMillis(); - ArrayList cachedNews = provider.getCachedNews(newsFilter); + final long nowInMs = TimeUtils.currentTimeMillis(); + final ArrayList cachedNews = provider.getCachedNews(newsFilter); boolean purgeNecessary = false; if (cachedNews != null) { - Iterator it = cachedNews.iterator(); + final Iterator it = cachedNews.iterator(); while (it.hasNext()) { - News news = it.next(); + final News news = it.next(); if (news.getLastUpdateInMs() + provider.getNewsMaxValidityInMs() < nowInMs) { it.remove(); purgeNecessary = true; @@ -176,9 +176,9 @@ private static Cursor getNews(@NonNull NewsProviderContract provider, @Nullable provider.purgeUselessCachedNews(); } if (cachedNews != null) { - Iterator it = cachedNews.iterator(); + final Iterator it = cachedNews.iterator(); while (it.hasNext()) { - News news = it.next(); + final News news = it.next(); if (!news.isUseful()) { provider.deleteCachedNews(news.getId()); it.remove(); @@ -189,7 +189,7 @@ private static Cursor getNews(@NonNull NewsProviderContract provider, @Nullable return getNewsCursor(cachedNews); } long cacheValidityInMs = provider.getNewsValidityInMs(newsFilter.isInFocusOrDefault()); - Long filterCacheValidityInMs = newsFilter.getCacheValidityInMsOrNull(); + final Long filterCacheValidityInMs = newsFilter.getCacheValidityInMsOrNull(); if (filterCacheValidityInMs != null && filterCacheValidityInMs > provider.getMinDurationBetweenNewsRefreshInMs(newsFilter.isInFocusOrDefault())) { cacheValidityInMs = filterCacheValidityInMs; } @@ -197,15 +197,16 @@ private static Cursor getNews(@NonNull NewsProviderContract provider, @Nullable if (CollectionUtils.getSize(cachedNews) == 0) { loadNewNews = true; } else if (cachedNews != null) { + final long tooOldNeedLoadingInMs = nowInMs - cacheValidityInMs; for (News oneCachedNews : cachedNews) { - if (oneCachedNews.getLastUpdateInMs() + cacheValidityInMs < nowInMs) { + if (oneCachedNews.getLastUpdateInMs() < tooOldNeedLoadingInMs) { loadNewNews = true; break; } } } if (loadNewNews) { - ArrayList newNews = provider.getNewNews(newsFilter); + final ArrayList newNews = provider.getNewNews(newsFilter); if (CollectionUtils.getSize(newNews) != 0) { return getNewsCursor(newNews); } From 0fa4fbbbf68079e17b3f1c5d2cec38588024b151 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mathieu=20M=C3=A9a?= Date: Wed, 11 Mar 2026 17:10:37 -0400 Subject: [PATCH 2/2] wip --- .../provider/news/NewsProviderContract.java | 26 ++++++++++++++++--- 1 file changed, 23 insertions(+), 3 deletions(-) diff --git a/src/main/java/org/mtransit/android/commons/provider/news/NewsProviderContract.java b/src/main/java/org/mtransit/android/commons/provider/news/NewsProviderContract.java index 753c6c07..866ca37c 100644 --- a/src/main/java/org/mtransit/android/commons/provider/news/NewsProviderContract.java +++ b/src/main/java/org/mtransit/android/commons/provider/news/NewsProviderContract.java @@ -191,6 +191,7 @@ private Filter setUUIDs(@Nullable List uuids) { return this; } + @SuppressWarnings("unused") @Nullable public List getUUIDs() { return uuids; @@ -198,12 +199,17 @@ public List getUUIDs() { @NonNull public static Filter getNewTargetFilter(@NonNull POI poi) { - ArrayList targets = new ArrayList<>(); + return getNewTargetsFilter(makeTargets(poi)); + } + + @NonNull + public static ArrayList makeTargets(@NonNull POI poi) { + final ArrayList targets = new ArrayList<>(); targets.add(poi.getAuthority()); if (poi instanceof RouteDirectionStop) { targets.add(POI.POIUtils.getUUID(poi.getAuthority(), ((RouteDirectionStop) poi).getRoute().getId())); } - return getNewTargetsFilter(targets); + return targets; } @SuppressWarnings("unused") @@ -226,6 +232,7 @@ private Filter setTargets(List targets) { return this; } + @SuppressWarnings("unused") @Nullable public List getTargets() { return targets; @@ -259,6 +266,19 @@ public String toString() { return sb.toString(); } + @SuppressWarnings("unused") + @NonNull + public String toStringTargetsAndUuid() { + final StringBuilder sb = new StringBuilder(Filter.class.getSimpleName()).append('['); + if (isUUIDFilter(this)) { + sb.append("uuids:").append(this.uuids).append(','); + } else if (isTargetFilter(this)) { + sb.append("targets:").append(this.targets).append(','); + } + sb.append(']'); + return sb.toString(); + } + public static boolean isUUIDFilter(@Nullable Filter newsFilter) { return newsFilter != null && CollectionUtils.getSize(newsFilter.uuids) > 0; } @@ -479,4 +499,4 @@ public static JSONObject toJSON(@NonNull Filter newsFilter) { } } } -} \ No newline at end of file +}