mirror of
https://github.com/samuelclay/NewsBlur.git
synced 2025-04-13 09:42:01 +00:00
Merge branch 'sictiru'
* sictiru: (22 commits) Android 13.3.2 230 #1886 Support for themed icons Add nullability annotations to ReadingAction Android 13.3.1 229 #1891 Handle Android 9 and below for OPML file export Add nullability annotation to help with Kotlin conversion Revert binding view destroy Android v13.3.0 Android v13.2.11 Revert db mutex changes Revert "#1853 Remove read write db mutex" Android v13.2.10. Clear web chrome client on web view destroy Android v13.2.9. Destroy webview on fragment view destroy Android v13.2.8 Cleanup web view binding on destroy view Android v13.2.7 Remove play core in favor or play review. Update other dependencies Android v13.2.6 Catch OperationCanceledException exception when loading active stories #1853 Remove read write db mutex Android v13.2.5 ...
This commit is contained in:
commit
5af380136c
26 changed files with 540 additions and 282 deletions
|
@ -1,6 +1,7 @@
|
||||||
package com.newsblur.benchmark
|
package com.newsblur.benchmark
|
||||||
|
|
||||||
import androidx.benchmark.macro.ExperimentalBaselineProfilesApi
|
import android.os.Build
|
||||||
|
import androidx.annotation.RequiresApi
|
||||||
import androidx.benchmark.macro.junit4.BaselineProfileRule
|
import androidx.benchmark.macro.junit4.BaselineProfileRule
|
||||||
import androidx.test.ext.junit.runners.AndroidJUnit4
|
import androidx.test.ext.junit.runners.AndroidJUnit4
|
||||||
import org.junit.Rule
|
import org.junit.Rule
|
||||||
|
@ -10,7 +11,7 @@ import org.junit.runner.RunWith
|
||||||
/**
|
/**
|
||||||
* Runs in its own process
|
* Runs in its own process
|
||||||
*/
|
*/
|
||||||
@OptIn(ExperimentalBaselineProfilesApi::class)
|
@RequiresApi(Build.VERSION_CODES.P)
|
||||||
@RunWith(AndroidJUnit4::class)
|
@RunWith(AndroidJUnit4::class)
|
||||||
class BaselineProfileGenerator {
|
class BaselineProfileGenerator {
|
||||||
|
|
||||||
|
@ -19,7 +20,7 @@ class BaselineProfileGenerator {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun generateSimpleStartupProfile() {
|
fun generateSimpleStartupProfile() {
|
||||||
rule.collectBaselineProfile(packageName = "com.newsblur") {
|
rule.collect(packageName = "com.newsblur") {
|
||||||
pressHome()
|
pressHome()
|
||||||
startActivityAndWait()
|
startActivityAndWait()
|
||||||
}
|
}
|
||||||
|
@ -28,7 +29,7 @@ class BaselineProfileGenerator {
|
||||||
@Test
|
@Test
|
||||||
fun generateUserJourneyProfile() {
|
fun generateUserJourneyProfile() {
|
||||||
var needsLogin = true
|
var needsLogin = true
|
||||||
rule.collectBaselineProfile(packageName = "com.newsblur") {
|
rule.collect(packageName = "com.newsblur") {
|
||||||
pressHome()
|
pressHome()
|
||||||
startActivityAndWait()
|
startActivityAndWait()
|
||||||
|
|
||||||
|
|
|
@ -57,7 +57,7 @@ dependencies {
|
||||||
implementation(Dependencies.okHttp)
|
implementation(Dependencies.okHttp)
|
||||||
implementation(Dependencies.gson)
|
implementation(Dependencies.gson)
|
||||||
implementation(Dependencies.billing)
|
implementation(Dependencies.billing)
|
||||||
implementation(Dependencies.playCore)
|
implementation(Dependencies.playReview)
|
||||||
implementation(Dependencies.material)
|
implementation(Dependencies.material)
|
||||||
implementation(Dependencies.preference)
|
implementation(Dependencies.preference)
|
||||||
implementation(Dependencies.browser)
|
implementation(Dependencies.browser)
|
||||||
|
|
|
@ -1,22 +1,25 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<manifest xmlns:tools="http://schemas.android.com/tools"
|
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
xmlns:android="http://schemas.android.com/apk/res/android">
|
xmlns:tools="http://schemas.android.com/tools">
|
||||||
|
|
||||||
<uses-permission android:name="android.permission.INTERNET" />
|
<uses-permission android:name="android.permission.INTERNET" />
|
||||||
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
|
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
|
||||||
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
|
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
|
||||||
<uses-permission android:name="android.permission.WAKE_LOCK" />
|
<uses-permission android:name="android.permission.WAKE_LOCK" />
|
||||||
<uses-permission android:name="android.permission.POST_NOTIFICATIONS"/>
|
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
|
||||||
|
<uses-permission
|
||||||
|
android:name="android.permission.WRITE_EXTERNAL_STORAGE"
|
||||||
|
android:maxSdkVersion="28" />
|
||||||
|
|
||||||
<application
|
<application
|
||||||
android:icon="@drawable/logo"
|
|
||||||
android:label="@string/newsblur"
|
|
||||||
android:theme="@style/Theme.MaterialComponents.DayNight.NoActionBar"
|
|
||||||
android:fullBackupContent="@xml/backupscheme"
|
|
||||||
android:name=".NbApplication"
|
android:name=".NbApplication"
|
||||||
android:dataExtractionRules="@xml/data_extraction_rules"
|
|
||||||
android:allowBackup="true"
|
android:allowBackup="true"
|
||||||
android:fullBackupOnly="true">
|
android:dataExtractionRules="@xml/data_extraction_rules"
|
||||||
|
android:fullBackupContent="@xml/backupscheme"
|
||||||
|
android:fullBackupOnly="true"
|
||||||
|
android:icon="@mipmap/ic_launcher"
|
||||||
|
android:label="@string/newsblur"
|
||||||
|
android:theme="@style/Theme.MaterialComponents.DayNight.NoActionBar">
|
||||||
|
|
||||||
<profileable
|
<profileable
|
||||||
android:shell="true"
|
android:shell="true"
|
||||||
|
@ -24,15 +27,16 @@
|
||||||
|
|
||||||
<activity
|
<activity
|
||||||
android:name=".activity.InitActivity"
|
android:name=".activity.InitActivity"
|
||||||
android:theme="@style/splashScreen"
|
android:exported="true"
|
||||||
android:noHistory="true"
|
android:noHistory="true"
|
||||||
android:exported="true">
|
android:theme="@style/splashScreen">
|
||||||
<intent-filter>
|
<intent-filter>
|
||||||
<category android:name="android.intent.category.LAUNCHER" />
|
<category android:name="android.intent.category.LAUNCHER" />
|
||||||
<action android:name="android.intent.action.MAIN" />
|
<action android:name="android.intent.action.MAIN" />
|
||||||
</intent-filter>
|
</intent-filter>
|
||||||
|
|
||||||
<meta-data android:name="android.app.shortcuts"
|
<meta-data
|
||||||
|
android:name="android.app.shortcuts"
|
||||||
android:resource="@xml/shortcuts" />
|
android:resource="@xml/shortcuts" />
|
||||||
|
|
||||||
</activity>
|
</activity>
|
||||||
|
@ -48,143 +52,139 @@
|
||||||
|
|
||||||
<activity
|
<activity
|
||||||
android:name=".activity.RegisterProgress"
|
android:name=".activity.RegisterProgress"
|
||||||
android:noHistory="true"
|
android:label="@string/get_started"
|
||||||
android:label="@string/get_started" />
|
android:noHistory="true" />
|
||||||
|
|
||||||
<activity
|
<activity
|
||||||
android:name=".activity.Main"
|
android:name=".activity.Main"
|
||||||
android:launchMode="singleTask"
|
android:alwaysRetainTaskState="true"
|
||||||
android:alwaysRetainTaskState="true" />
|
android:launchMode="singleTask" />
|
||||||
|
|
||||||
<activity
|
<activity
|
||||||
android:name=".activity.Profile"
|
android:name=".activity.Profile"
|
||||||
android:label="@string/profile"/>
|
android:label="@string/profile" />
|
||||||
|
|
||||||
<activity
|
<activity
|
||||||
android:name=".activity.Settings"
|
android:name=".activity.Settings"
|
||||||
android:label="@string/settings"/>
|
android:label="@string/settings" />
|
||||||
|
|
||||||
<activity android:name=".activity.ImportExportActivity" />
|
<activity android:name=".activity.ImportExportActivity" />
|
||||||
<activity android:name=".activity.NotificationsActivity" />
|
<activity android:name=".activity.NotificationsActivity" />
|
||||||
<activity
|
<activity
|
||||||
android:name=".activity.WidgetConfig"
|
android:name=".activity.WidgetConfig"
|
||||||
android:launchMode="singleTask"
|
android:label="@string/menu_widget"
|
||||||
android:label="@string/menu_widget" />
|
android:launchMode="singleTask" />
|
||||||
|
|
||||||
<activity
|
<activity android:name=".activity.FeedItemsList" />
|
||||||
android:name=".activity.FeedItemsList" />
|
|
||||||
|
|
||||||
<activity
|
<activity
|
||||||
android:name=".activity.AllStoriesItemsList"
|
android:name=".activity.AllStoriesItemsList"
|
||||||
android:launchMode="singleTask"/>
|
android:launchMode="singleTask" />
|
||||||
|
|
||||||
<activity
|
<activity android:name=".activity.InfrequentItemsList" />
|
||||||
android:name=".activity.InfrequentItemsList" />
|
|
||||||
|
|
||||||
<activity
|
<activity android:name=".activity.ReadStoriesItemsList" />
|
||||||
android:name=".activity.ReadStoriesItemsList" />
|
|
||||||
|
|
||||||
<activity
|
<activity android:name=".activity.SavedStoriesItemsList" />
|
||||||
android:name=".activity.SavedStoriesItemsList" />
|
|
||||||
|
|
||||||
<activity
|
<activity android:name=".activity.AllSharedStoriesItemsList" />
|
||||||
android:name=".activity.AllSharedStoriesItemsList" />
|
|
||||||
|
|
||||||
<activity
|
<activity android:name=".activity.GlobalSharedStoriesItemsList" />
|
||||||
android:name=".activity.GlobalSharedStoriesItemsList" />
|
|
||||||
|
|
||||||
<activity
|
<activity android:name=".activity.FolderItemsList" />
|
||||||
android:name=".activity.FolderItemsList" />
|
|
||||||
|
|
||||||
<activity
|
<activity android:name=".activity.SocialFeedItemsList" />
|
||||||
android:name=".activity.SocialFeedItemsList" />
|
|
||||||
|
|
||||||
<activity
|
<activity android:name=".activity.FeedReading" />
|
||||||
android:name=".activity.FeedReading"/>
|
|
||||||
|
|
||||||
<activity
|
<activity android:name=".activity.AllStoriesReading" />
|
||||||
android:name=".activity.AllStoriesReading"/>
|
|
||||||
|
|
||||||
<activity
|
<activity android:name=".activity.InfrequentReading" />
|
||||||
android:name=".activity.InfrequentReading"/>
|
|
||||||
|
|
||||||
<activity
|
<activity android:name=".activity.ReadStoriesReading" />
|
||||||
android:name=".activity.ReadStoriesReading"/>
|
|
||||||
|
|
||||||
<activity
|
<activity android:name=".activity.SavedStoriesReading" />
|
||||||
android:name=".activity.SavedStoriesReading"/>
|
|
||||||
|
|
||||||
<activity
|
<activity android:name=".activity.AllSharedStoriesReading" />
|
||||||
android:name=".activity.AllSharedStoriesReading"/>
|
|
||||||
|
|
||||||
<activity
|
<activity android:name=".activity.GlobalSharedStoriesReading" />
|
||||||
android:name=".activity.GlobalSharedStoriesReading"/>
|
|
||||||
|
|
||||||
<activity
|
<activity android:name=".activity.FolderReading" />
|
||||||
android:name=".activity.FolderReading"/>
|
|
||||||
|
|
||||||
<activity
|
<activity android:name=".activity.SubscriptionActivity" />
|
||||||
android:name=".activity.SubscriptionActivity" />
|
|
||||||
|
|
||||||
<activity
|
<activity
|
||||||
android:name=".activity.MuteConfig"
|
android:name=".activity.MuteConfig"
|
||||||
android:launchMode="singleTask"
|
android:label="@string/mute_sites"
|
||||||
android:label="@string/mute_sites"/>
|
android:launchMode="singleTask" />
|
||||||
|
|
||||||
<activity
|
<activity
|
||||||
android:name=".activity.FeedSearchActivity"
|
android:name=".activity.FeedSearchActivity"
|
||||||
android:launchMode="singleTop" />
|
android:launchMode="singleTop" />
|
||||||
|
|
||||||
<activity
|
<activity android:name=".activity.SocialFeedReading" />
|
||||||
android:name=".activity.SocialFeedReading"/>
|
|
||||||
|
|
||||||
<service
|
<service
|
||||||
android:name=".service.NBSyncService"
|
android:name=".service.NBSyncService"
|
||||||
android:permission="android.permission.BIND_JOB_SERVICE" />
|
android:permission="android.permission.BIND_JOB_SERVICE" />
|
||||||
<service android:name=".widget.WidgetRemoteViewsService"
|
<service
|
||||||
|
android:name=".widget.WidgetRemoteViewsService"
|
||||||
android:permission="android.permission.BIND_REMOTEVIEWS" />
|
android:permission="android.permission.BIND_REMOTEVIEWS" />
|
||||||
<service
|
<service
|
||||||
android:name=".service.SubscriptionSyncService"
|
android:name=".service.SubscriptionSyncService"
|
||||||
android:permission="android.permission.BIND_JOB_SERVICE"
|
android:exported="false"
|
||||||
android:exported="false" />
|
android:permission="android.permission.BIND_JOB_SERVICE" />
|
||||||
|
|
||||||
<receiver android:name=".service.BootReceiver"
|
<receiver
|
||||||
|
android:name=".service.BootReceiver"
|
||||||
android:exported="true">
|
android:exported="true">
|
||||||
<intent-filter>
|
<intent-filter>
|
||||||
<action android:name="android.intent.action.BOOT_COMPLETED" />
|
<action android:name="android.intent.action.BOOT_COMPLETED" />
|
||||||
</intent-filter>
|
</intent-filter>
|
||||||
</receiver>
|
</receiver>
|
||||||
|
|
||||||
<receiver android:name=".util.DownloadCompleteReceiver"
|
<receiver
|
||||||
|
android:name=".util.DownloadCompleteReceiver"
|
||||||
android:exported="true">
|
android:exported="true">
|
||||||
<intent-filter>
|
<intent-filter>
|
||||||
<action android:name="android.intent.action.DOWNLOAD_COMPLETE" />
|
<action android:name="android.intent.action.DOWNLOAD_COMPLETE" />
|
||||||
</intent-filter>
|
</intent-filter>
|
||||||
</receiver>
|
</receiver>
|
||||||
|
|
||||||
<receiver android:name=".util.NotifyDismissReceiver" android:exported="false" />
|
<receiver
|
||||||
<receiver android:name=".util.NotifySaveReceiver" android:exported="false" />
|
android:name=".util.NotifyDismissReceiver"
|
||||||
<receiver android:name=".util.NotifyMarkreadReceiver" android:exported="false" />
|
android:exported="false" />
|
||||||
<receiver android:name=".util.NotifyShareReceiver" android:exported="false" />
|
<receiver
|
||||||
<receiver android:name=".widget.WidgetProvider"
|
android:name=".util.NotifySaveReceiver"
|
||||||
|
android:exported="false" />
|
||||||
|
<receiver
|
||||||
|
android:name=".util.NotifyMarkreadReceiver"
|
||||||
|
android:exported="false" />
|
||||||
|
<receiver
|
||||||
|
android:name=".util.NotifyShareReceiver"
|
||||||
|
android:exported="false" />
|
||||||
|
<receiver
|
||||||
|
android:name=".widget.WidgetProvider"
|
||||||
android:exported="false">
|
android:exported="false">
|
||||||
<intent-filter>
|
<intent-filter>
|
||||||
<action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
|
<action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
|
||||||
</intent-filter>
|
</intent-filter>
|
||||||
<meta-data android:name="android.appwidget.provider"
|
<meta-data
|
||||||
|
android:name="android.appwidget.provider"
|
||||||
android:resource="@xml/newsblur_appwidget_info" />
|
android:resource="@xml/newsblur_appwidget_info" />
|
||||||
</receiver>
|
</receiver>
|
||||||
<receiver android:name=".service.TimeChangeReceiver"
|
<receiver
|
||||||
|
android:name=".service.TimeChangeReceiver"
|
||||||
android:exported="true">
|
android:exported="true">
|
||||||
<intent-filter >
|
<intent-filter>
|
||||||
<action android:name="android.intent.action.TIME_SET"/>
|
<action android:name="android.intent.action.TIME_SET" />
|
||||||
</intent-filter>
|
</intent-filter>
|
||||||
</receiver>
|
</receiver>
|
||||||
<receiver
|
<receiver
|
||||||
android:name=".widget.WidgetUpdateReceiver"
|
android:name=".widget.WidgetUpdateReceiver"
|
||||||
android:enabled="true"
|
android:enabled="true"
|
||||||
android:exported="false">
|
android:exported="false"></receiver>
|
||||||
</receiver>
|
|
||||||
<provider
|
<provider
|
||||||
android:name="androidx.core.content.FileProvider"
|
android:name="androidx.core.content.FileProvider"
|
||||||
android:authorities="com.newsblur.fileprovider"
|
android:authorities="com.newsblur.fileprovider"
|
||||||
|
@ -195,59 +195,70 @@
|
||||||
android:resource="@xml/file_paths" />
|
android:resource="@xml/file_paths" />
|
||||||
</provider>
|
</provider>
|
||||||
|
|
||||||
<activity android:name=".activity.AddFeedExternal"
|
<activity
|
||||||
|
android:name=".activity.AddFeedExternal"
|
||||||
android:exported="true">
|
android:exported="true">
|
||||||
<intent-filter>
|
<intent-filter>
|
||||||
<action android:name="android.intent.action.VIEW" />
|
<action android:name="android.intent.action.VIEW" />
|
||||||
|
|
||||||
<category android:name="android.intent.category.DEFAULT" />
|
<category android:name="android.intent.category.DEFAULT" />
|
||||||
<category android:name="android.intent.category.BROWSABLE" />
|
<category android:name="android.intent.category.BROWSABLE" />
|
||||||
<data android:scheme="http"/>
|
|
||||||
<data android:scheme="https"/>
|
<data android:scheme="http" />
|
||||||
<data android:host="*"/>
|
<data android:scheme="https" />
|
||||||
<data android:pathPattern=".*xml"/>
|
<data android:host="*" />
|
||||||
<data android:pathPattern=".*rss"/>
|
<data android:pathPattern=".*xml" />
|
||||||
<data android:pathPattern=".*atom"/>
|
<data android:pathPattern=".*rss" />
|
||||||
<data android:pathPattern=".*json"/>
|
<data android:pathPattern=".*atom" />
|
||||||
<data android:pathPattern=".*/feed.*"/>
|
<data android:pathPattern=".*json" />
|
||||||
|
<data android:pathPattern=".*/feed.*" />
|
||||||
</intent-filter>
|
</intent-filter>
|
||||||
<intent-filter>
|
<intent-filter>
|
||||||
<action android:name="android.intent.action.VIEW" />
|
<action android:name="android.intent.action.VIEW" />
|
||||||
|
|
||||||
<category android:name="android.intent.category.DEFAULT" />
|
<category android:name="android.intent.category.DEFAULT" />
|
||||||
<category android:name="android.intent.category.BROWSABLE" />
|
<category android:name="android.intent.category.BROWSABLE" />
|
||||||
|
|
||||||
<data android:scheme="feed" />
|
<data android:scheme="feed" />
|
||||||
<data android:scheme="rss" />
|
<data android:scheme="rss" />
|
||||||
</intent-filter>
|
</intent-filter>
|
||||||
<intent-filter>
|
<intent-filter>
|
||||||
<action android:name="android.intent.action.VIEW" />
|
<action android:name="android.intent.action.VIEW" />
|
||||||
|
|
||||||
<category android:name="android.intent.category.DEFAULT" />
|
<category android:name="android.intent.category.DEFAULT" />
|
||||||
<category android:name="android.intent.category.BROWSABLE" />
|
<category android:name="android.intent.category.BROWSABLE" />
|
||||||
<data android:scheme="http"/>
|
|
||||||
<data android:scheme="https"/>
|
<data android:scheme="http" />
|
||||||
<data android:host="feeds.feedburner.com"/>
|
<data android:scheme="https" />
|
||||||
<data android:host="feeds2.feedburner.com"/>
|
<data android:host="feeds.feedburner.com" />
|
||||||
|
<data android:host="feeds2.feedburner.com" />
|
||||||
</intent-filter>
|
</intent-filter>
|
||||||
<intent-filter>
|
<intent-filter>
|
||||||
<data android:scheme="http"/>
|
<data android:scheme="http" />
|
||||||
<data android:scheme="https"/>
|
<data android:scheme="https" />
|
||||||
|
|
||||||
<action android:name="android.intent.action.VIEW" />
|
<action android:name="android.intent.action.VIEW" />
|
||||||
|
|
||||||
<category android:name="android.intent.category.DEFAULT" />
|
<category android:name="android.intent.category.DEFAULT" />
|
||||||
<category android:name="android.intent.category.BROWSABLE" />
|
<category android:name="android.intent.category.BROWSABLE" />
|
||||||
<data android:mimeType="text/xml"/>
|
|
||||||
<data android:mimeType="application/rss+xml"/>
|
<data android:mimeType="text/xml" />
|
||||||
<data android:mimeType="application/atom+xml"/>
|
<data android:mimeType="application/rss+xml" />
|
||||||
<data android:mimeType="application/xml"/>
|
<data android:mimeType="application/atom+xml" />
|
||||||
<data android:mimeType="application/json"/>
|
<data android:mimeType="application/xml" />
|
||||||
<data android:mimeType="application/feed+json"/>
|
<data android:mimeType="application/json" />
|
||||||
<data android:scheme="http"/>
|
<data android:mimeType="application/feed+json" />
|
||||||
<data android:scheme="https"/>
|
<data android:scheme="http" />
|
||||||
|
<data android:scheme="https" />
|
||||||
</intent-filter>
|
</intent-filter>
|
||||||
</activity>
|
</activity>
|
||||||
|
|
||||||
<activity android:name=".activity.ShareExternalStoryActivity"
|
<activity
|
||||||
android:theme="@style/Theme.Translucent"
|
android:name=".activity.ShareExternalStoryActivity"
|
||||||
android:windowSoftInputMode="adjustResize"
|
android:exported="true"
|
||||||
android:launchMode="singleInstance"
|
android:launchMode="singleInstance"
|
||||||
android:exported="true">
|
android:theme="@style/Theme.Translucent"
|
||||||
|
android:windowSoftInputMode="adjustResize">
|
||||||
<intent-filter>
|
<intent-filter>
|
||||||
<action android:name="android.intent.action.SEND" />
|
<action android:name="android.intent.action.SEND" />
|
||||||
<category android:name="android.intent.category.DEFAULT" />
|
<category android:name="android.intent.category.DEFAULT" />
|
||||||
|
|
|
@ -10,10 +10,10 @@ import androidx.fragment.app.DialogFragment;
|
||||||
import android.view.Menu;
|
import android.view.Menu;
|
||||||
import android.view.MenuItem;
|
import android.view.MenuItem;
|
||||||
|
|
||||||
|
import com.google.android.gms.tasks.Task;
|
||||||
import com.google.android.play.core.review.ReviewInfo;
|
import com.google.android.play.core.review.ReviewInfo;
|
||||||
import com.google.android.play.core.review.ReviewManager;
|
import com.google.android.play.core.review.ReviewManager;
|
||||||
import com.google.android.play.core.review.ReviewManagerFactory;
|
import com.google.android.play.core.review.ReviewManagerFactory;
|
||||||
import com.google.android.play.core.tasks.Task;
|
|
||||||
import com.newsblur.R;
|
import com.newsblur.R;
|
||||||
import com.newsblur.di.IconLoader;
|
import com.newsblur.di.IconLoader;
|
||||||
import com.newsblur.domain.Feed;
|
import com.newsblur.domain.Feed;
|
||||||
|
|
|
@ -1,12 +1,16 @@
|
||||||
package com.newsblur.activity
|
package com.newsblur.activity
|
||||||
|
|
||||||
|
import android.Manifest
|
||||||
import android.app.Activity
|
import android.app.Activity
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
|
import android.content.pm.PackageManager
|
||||||
import android.net.Uri
|
import android.net.Uri
|
||||||
|
import android.os.Build
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.webkit.MimeTypeMap
|
import android.webkit.MimeTypeMap
|
||||||
import android.widget.Toast
|
import android.widget.Toast
|
||||||
import androidx.activity.result.contract.ActivityResultContracts
|
import androidx.activity.result.contract.ActivityResultContracts
|
||||||
|
import androidx.core.content.ContextCompat
|
||||||
import com.google.android.material.snackbar.Snackbar
|
import com.google.android.material.snackbar.Snackbar
|
||||||
import com.newsblur.R
|
import com.newsblur.R
|
||||||
import com.newsblur.databinding.ActivityImportExportBinding
|
import com.newsblur.databinding.ActivityImportExportBinding
|
||||||
|
@ -40,6 +44,16 @@ class ImportExportActivity : NbActivity() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// used for Android 9 and below
|
||||||
|
private val requestWriteStoragePermissionLauncher = registerForActivityResult(
|
||||||
|
ActivityResultContracts.RequestPermission()) { isGranted ->
|
||||||
|
if (isGranted) {
|
||||||
|
exportOpmlFile()
|
||||||
|
} else {
|
||||||
|
Toast.makeText(this, R.string.write_storage_permission_opml, Toast.LENGTH_LONG).show()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
binding = ActivityImportExportBinding.inflate(layoutInflater)
|
binding = ActivityImportExportBinding.inflate(layoutInflater)
|
||||||
|
@ -55,7 +69,13 @@ class ImportExportActivity : NbActivity() {
|
||||||
|
|
||||||
private fun setupListeners() {
|
private fun setupListeners() {
|
||||||
binding.btnUpload.setOnClickListener { pickOpmlFile() }
|
binding.btnUpload.setOnClickListener { pickOpmlFile() }
|
||||||
binding.btnDownload.setOnClickListener { exportOpmlFile() }
|
binding.btnDownload.setOnClickListener {
|
||||||
|
if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.P) {
|
||||||
|
checkAndRequestWriteStoragePermission()
|
||||||
|
} else {
|
||||||
|
exportOpmlFile()
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun pickOpmlFile() {
|
private fun pickOpmlFile() {
|
||||||
|
@ -131,4 +151,16 @@ class ImportExportActivity : NbActivity() {
|
||||||
override fun handleUpdate(updateType: Int) {
|
override fun handleUpdate(updateType: Int) {
|
||||||
// ignore
|
// ignore
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Android 9 and below
|
||||||
|
private fun checkAndRequestWriteStoragePermission() {
|
||||||
|
if (ContextCompat.checkSelfPermission(
|
||||||
|
this,
|
||||||
|
Manifest.permission.WRITE_EXTERNAL_STORAGE
|
||||||
|
) == PackageManager.PERMISSION_GRANTED) {
|
||||||
|
exportOpmlFile()
|
||||||
|
} else {
|
||||||
|
requestWriteStoragePermissionLauncher.launch(Manifest.permission.WRITE_EXTERNAL_STORAGE)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
|
@ -35,6 +35,7 @@ import com.newsblur.util.AppConstants;
|
||||||
import com.newsblur.util.FeedSet;
|
import com.newsblur.util.FeedSet;
|
||||||
import com.newsblur.util.FeedUtils;
|
import com.newsblur.util.FeedUtils;
|
||||||
import com.newsblur.util.Log;
|
import com.newsblur.util.Log;
|
||||||
|
import com.newsblur.util.PendingTransitionUtils;
|
||||||
import com.newsblur.util.ReadingActionListener;
|
import com.newsblur.util.ReadingActionListener;
|
||||||
import com.newsblur.util.PrefsUtils;
|
import com.newsblur.util.PrefsUtils;
|
||||||
import com.newsblur.util.Session;
|
import com.newsblur.util.Session;
|
||||||
|
@ -82,7 +83,7 @@ public abstract class ItemsList extends NbActivity implements ReadingActionListe
|
||||||
Trace.beginSection("ItemsListOnCreate");
|
Trace.beginSection("ItemsListOnCreate");
|
||||||
super.onCreate(bundle);
|
super.onCreate(bundle);
|
||||||
|
|
||||||
overridePendingTransition(R.anim.slide_in_from_right, R.anim.slide_out_to_left);
|
PendingTransitionUtils.overrideEnterTransition(this);
|
||||||
|
|
||||||
contextMenuDelegate = new ItemListContextMenuDelegateImpl(this, feedUtils);
|
contextMenuDelegate = new ItemListContextMenuDelegateImpl(this, feedUtils);
|
||||||
viewModel = new ViewModelProvider(this).get(ItemListViewModel.class);
|
viewModel = new ViewModelProvider(this).get(ItemListViewModel.class);
|
||||||
|
@ -313,13 +314,7 @@ public abstract class ItemsList extends NbActivity implements ReadingActionListe
|
||||||
@Override
|
@Override
|
||||||
public void finish() {
|
public void finish() {
|
||||||
super.finish();
|
super.finish();
|
||||||
/*
|
PendingTransitionUtils.overrideExitTransition(this);
|
||||||
* Animate out the list by sliding it to the right and the Main activity in from
|
|
||||||
* the left. Do this when going back to Main as a subtle hint to the swipe gesture,
|
|
||||||
* to make the gesture feel more natural, and to override the really ugly transition
|
|
||||||
* used in some of the newer platforms.
|
|
||||||
*/
|
|
||||||
overridePendingTransition(R.anim.slide_in_from_left, R.anim.slide_out_to_right);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
abstract String getSaveSearchFeedId();
|
abstract String getSaveSearchFeedId();
|
||||||
|
|
|
@ -65,7 +65,7 @@ open class NbActivity : AppCompatActivity() {
|
||||||
|
|
||||||
// Facilitates the db updates by the sync service on the UI
|
// Facilitates the db updates by the sync service on the UI
|
||||||
lifecycleScope.launch {
|
lifecycleScope.launch {
|
||||||
repeatOnLifecycle(Lifecycle.State.CREATED) {
|
repeatOnLifecycle(Lifecycle.State.STARTED) {
|
||||||
launch {
|
launch {
|
||||||
NbSyncManager.state.collectLatest {
|
NbSyncManager.state.collectLatest {
|
||||||
withContext(Dispatchers.Main) {
|
withContext(Dispatchers.Main) {
|
||||||
|
|
|
@ -388,7 +388,7 @@ abstract class Reading : NbActivity(), OnPageChangeListener, ScrollChangeListene
|
||||||
get() {
|
get() {
|
||||||
// saved stories and global shared stories don't have unreads
|
// saved stories and global shared stories don't have unreads
|
||||||
if (fs!!.isAllSaved || fs!!.isGlobalShared) return 0
|
if (fs!!.isAllSaved || fs!!.isGlobalShared) return 0
|
||||||
val result = dbHelper.getUnreadCount(fs, intelState)
|
val result = dbHelper.getUnreadCount(fs!!, intelState)
|
||||||
return if (result < 0) 0 else result
|
return if (result < 0) 0 else result
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -7,6 +7,8 @@ import android.content.Context;
|
||||||
import android.database.Cursor;
|
import android.database.Cursor;
|
||||||
import android.database.sqlite.SQLiteDatabase;
|
import android.database.sqlite.SQLiteDatabase;
|
||||||
import android.os.CancellationSignal;
|
import android.os.CancellationSignal;
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
|
|
||||||
import android.text.TextUtils;
|
import android.text.TextUtils;
|
||||||
|
@ -46,13 +48,13 @@ import java.util.concurrent.Executors;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Utility class for executing DB operations on the local, private NB database.
|
* Utility class for executing DB operations on the local, private NB database.
|
||||||
*
|
|
||||||
* It is the intent of this class to be the single location of SQL executed on
|
* It is the intent of this class to be the single location of SQL executed on
|
||||||
* our DB, replacing the deprecated ContentProvider access pattern.
|
* our DB, replacing the deprecated ContentProvider access pattern.
|
||||||
*/
|
*/
|
||||||
public class BlurDatabaseHelper {
|
public class BlurDatabaseHelper {
|
||||||
|
|
||||||
// manual synchro isn't needed if you only use one DBHelper, but at present the app uses several
|
// Removing the manual synchro will cause ANRs
|
||||||
|
// because the db transactions are made on the main thread
|
||||||
public final static Object RW_MUTEX = new Object();
|
public final static Object RW_MUTEX = new Object();
|
||||||
|
|
||||||
private final BlurDatabase dbWrapper;
|
private final BlurDatabase dbWrapper;
|
||||||
|
@ -85,6 +87,7 @@ public class BlurDatabaseHelper {
|
||||||
com.newsblur.util.Log.i(this.getClass().getName(), ". . . tables recreated.");
|
com.newsblur.util.Log.i(this.getClass().getName(), ". . . tables recreated.");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
public String getEngineVersion() {
|
public String getEngineVersion() {
|
||||||
String engineVersion = "";
|
String engineVersion = "";
|
||||||
try {
|
try {
|
||||||
|
@ -99,10 +102,12 @@ public class BlurDatabaseHelper {
|
||||||
return engineVersion;
|
return engineVersion;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@NonNull
|
||||||
public Set<String> getAllFeeds() {
|
public Set<String> getAllFeeds() {
|
||||||
return getAllFeeds(false);
|
return getAllFeeds(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@NonNull
|
||||||
private Set<String> getAllFeeds(boolean activeOnly) {
|
private Set<String> getAllFeeds(boolean activeOnly) {
|
||||||
String q1 = "SELECT " + DatabaseConstants.FEED_ID +
|
String q1 = "SELECT " + DatabaseConstants.FEED_ID +
|
||||||
" FROM " + DatabaseConstants.FEED_TABLE;
|
" FROM " + DatabaseConstants.FEED_TABLE;
|
||||||
|
@ -118,10 +123,12 @@ public class BlurDatabaseHelper {
|
||||||
return feedIds;
|
return feedIds;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@NonNull
|
||||||
public Set<String> getAllActiveFeeds() {
|
public Set<String> getAllActiveFeeds() {
|
||||||
return getAllFeeds(true);
|
return getAllFeeds(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@NonNull
|
||||||
private List<String> getAllSocialFeeds() {
|
private List<String> getAllSocialFeeds() {
|
||||||
String q1 = "SELECT " + DatabaseConstants.SOCIAL_FEED_ID +
|
String q1 = "SELECT " + DatabaseConstants.SOCIAL_FEED_ID +
|
||||||
" FROM " + DatabaseConstants.SOCIALFEED_TABLE;
|
" FROM " + DatabaseConstants.SOCIALFEED_TABLE;
|
||||||
|
@ -181,20 +188,20 @@ public class BlurDatabaseHelper {
|
||||||
synchronized (RW_MUTEX) {dbRW.execSQL("VACUUM");}
|
synchronized (RW_MUTEX) {dbRW.execSQL("VACUUM");}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void deleteFeed(String feedId) {
|
public void deleteFeed(@Nullable String feedId) {
|
||||||
String[] selArgs = new String[] {feedId};
|
String[] selArgs = new String[] {feedId};
|
||||||
synchronized (RW_MUTEX) {dbRW.delete(DatabaseConstants.FEED_TABLE, DatabaseConstants.FEED_ID + " = ?", selArgs);}
|
synchronized (RW_MUTEX) {dbRW.delete(DatabaseConstants.FEED_TABLE, DatabaseConstants.FEED_ID + " = ?", selArgs);}
|
||||||
synchronized (RW_MUTEX) {dbRW.delete(DatabaseConstants.STORY_TABLE, DatabaseConstants.STORY_FEED_ID + " = ?", selArgs);}
|
synchronized (RW_MUTEX) {dbRW.delete(DatabaseConstants.STORY_TABLE, DatabaseConstants.STORY_FEED_ID + " = ?", selArgs);}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void deleteSocialFeed(String userId) {
|
public void deleteSocialFeed(@Nullable String userId) {
|
||||||
String[] selArgs = new String[] {userId};
|
String[] selArgs = new String[] {userId};
|
||||||
synchronized (RW_MUTEX) {dbRW.delete(DatabaseConstants.SOCIALFEED_TABLE, DatabaseConstants.SOCIAL_FEED_ID + " = ?", selArgs);}
|
synchronized (RW_MUTEX) {dbRW.delete(DatabaseConstants.SOCIALFEED_TABLE, DatabaseConstants.SOCIAL_FEED_ID + " = ?", selArgs);}
|
||||||
synchronized (RW_MUTEX) {dbRW.delete(DatabaseConstants.STORY_TABLE, DatabaseConstants.STORY_FEED_ID + " = ?", selArgs);}
|
synchronized (RW_MUTEX) {dbRW.delete(DatabaseConstants.STORY_TABLE, DatabaseConstants.STORY_FEED_ID + " = ?", selArgs);}
|
||||||
synchronized (RW_MUTEX) {dbRW.delete(DatabaseConstants.SOCIALFEED_STORY_MAP_TABLE, DatabaseConstants.SOCIALFEED_STORY_USER_ID + " = ?", selArgs);}
|
synchronized (RW_MUTEX) {dbRW.delete(DatabaseConstants.SOCIALFEED_STORY_MAP_TABLE, DatabaseConstants.SOCIALFEED_STORY_USER_ID + " = ?", selArgs);}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void deleteSavedSearch(String feedId, String query) {
|
public void deleteSavedSearch(@Nullable String feedId, @Nullable String query) {
|
||||||
String q = "DELETE FROM " + DatabaseConstants.SAVED_SEARCH_TABLE +
|
String q = "DELETE FROM " + DatabaseConstants.SAVED_SEARCH_TABLE +
|
||||||
" WHERE " + DatabaseConstants.SAVED_SEARCH_FEED_ID + " = '" + feedId + "'" +
|
" WHERE " + DatabaseConstants.SAVED_SEARCH_FEED_ID + " = '" + feedId + "'" +
|
||||||
" AND " + DatabaseConstants.SAVED_SEARCH_QUERY + " = '" + query + "'";
|
" AND " + DatabaseConstants.SAVED_SEARCH_QUERY + " = '" + query + "'";
|
||||||
|
@ -207,7 +214,8 @@ public class BlurDatabaseHelper {
|
||||||
synchronized (RW_MUTEX) {dbRW.delete(DatabaseConstants.STORY_TEXT_TABLE, null, null);}
|
synchronized (RW_MUTEX) {dbRW.delete(DatabaseConstants.STORY_TEXT_TABLE, null, null);}
|
||||||
}
|
}
|
||||||
|
|
||||||
public Feed getFeed(String feedId) {
|
@Nullable
|
||||||
|
public Feed getFeed(@Nullable String feedId) {
|
||||||
Cursor c = dbRO.query(DatabaseConstants.FEED_TABLE, null, DatabaseConstants.FEED_ID + " = ?", new String[] {feedId}, null, null, null);
|
Cursor c = dbRO.query(DatabaseConstants.FEED_TABLE, null, DatabaseConstants.FEED_ID + " = ?", new String[] {feedId}, null, null, null);
|
||||||
Feed result = null;
|
Feed result = null;
|
||||||
while (c.moveToNext()) {
|
while (c.moveToNext()) {
|
||||||
|
@ -217,13 +225,13 @@ public class BlurDatabaseHelper {
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void updateFeed(Feed feed) {
|
public void updateFeed(@NonNull Feed feed) {
|
||||||
synchronized (RW_MUTEX) {
|
synchronized (RW_MUTEX) {
|
||||||
dbRW.insertWithOnConflict(DatabaseConstants.FEED_TABLE, null, feed.getValues(), SQLiteDatabase.CONFLICT_REPLACE);
|
dbRW.insertWithOnConflict(DatabaseConstants.FEED_TABLE, null, feed.getValues(), SQLiteDatabase.CONFLICT_REPLACE);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void bulkInsertValues(String table, List<ContentValues> valuesList) {
|
private void bulkInsertValues(@NonNull String table, @NonNull List<ContentValues> valuesList) {
|
||||||
if (valuesList.size() < 1) return;
|
if (valuesList.size() < 1) return;
|
||||||
synchronized (RW_MUTEX) {
|
synchronized (RW_MUTEX) {
|
||||||
dbRW.beginTransaction();
|
dbRW.beginTransaction();
|
||||||
|
@ -239,18 +247,18 @@ public class BlurDatabaseHelper {
|
||||||
}
|
}
|
||||||
|
|
||||||
// just like bulkInsertValues, but leaves sync/transactioning to the caller
|
// just like bulkInsertValues, but leaves sync/transactioning to the caller
|
||||||
private void bulkInsertValuesExtSync(String table, List<ContentValues> valuesList) {
|
private void bulkInsertValuesExtSync(@NonNull String table, @NonNull List<ContentValues> valuesList) {
|
||||||
if (valuesList.size() < 1) return;
|
if (valuesList.size() < 1) return;
|
||||||
for (ContentValues values : valuesList) {
|
for (ContentValues values : valuesList) {
|
||||||
dbRW.insertWithOnConflict(table, null, values, SQLiteDatabase.CONFLICT_REPLACE);
|
dbRW.insertWithOnConflict(table, null, values, SQLiteDatabase.CONFLICT_REPLACE);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setFeedsFolders(List<ContentValues> folderValues,
|
public void setFeedsFolders(@NonNull List<ContentValues> folderValues,
|
||||||
List<ContentValues> feedValues,
|
@NonNull List<ContentValues> feedValues,
|
||||||
List<ContentValues> socialFeedValues,
|
@NonNull List<ContentValues> socialFeedValues,
|
||||||
List<ContentValues> starredCountValues,
|
@NonNull List<ContentValues> starredCountValues,
|
||||||
List<ContentValues> savedSearchValues) {
|
@NonNull List<ContentValues> savedSearchValues) {
|
||||||
synchronized (RW_MUTEX) {
|
synchronized (RW_MUTEX) {
|
||||||
dbRW.beginTransaction();
|
dbRW.beginTransaction();
|
||||||
try {
|
try {
|
||||||
|
@ -276,6 +284,7 @@ public class BlurDatabaseHelper {
|
||||||
|
|
||||||
// note method name: this gets a set rather than a list, in case the caller wants to
|
// note method name: this gets a set rather than a list, in case the caller wants to
|
||||||
// spend the up-front cost of hashing for better lookup speed rather than iteration!
|
// spend the up-front cost of hashing for better lookup speed rather than iteration!
|
||||||
|
@NonNull
|
||||||
public Set<String> getUnreadStoryHashesAsSet() {
|
public Set<String> getUnreadStoryHashesAsSet() {
|
||||||
String q = "SELECT " + DatabaseConstants.STORY_HASH +
|
String q = "SELECT " + DatabaseConstants.STORY_HASH +
|
||||||
" FROM " + DatabaseConstants.STORY_TABLE +
|
" FROM " + DatabaseConstants.STORY_TABLE +
|
||||||
|
@ -289,6 +298,7 @@ public class BlurDatabaseHelper {
|
||||||
return hashes;
|
return hashes;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@NonNull
|
||||||
public Set<String> getStarredStoryHashes() {
|
public Set<String> getStarredStoryHashes() {
|
||||||
String q = "SELECT " + DatabaseConstants.STORY_HASH +
|
String q = "SELECT " + DatabaseConstants.STORY_HASH +
|
||||||
" FROM " + DatabaseConstants.STORY_TABLE +
|
" FROM " + DatabaseConstants.STORY_TABLE +
|
||||||
|
@ -302,6 +312,7 @@ public class BlurDatabaseHelper {
|
||||||
return hashes;
|
return hashes;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@NonNull
|
||||||
public Set<String> getAllStoryImages() {
|
public Set<String> getAllStoryImages() {
|
||||||
Cursor c = dbRO.query(DatabaseConstants.STORY_TABLE, new String[]{DatabaseConstants.STORY_IMAGE_URLS}, null, null, null, null, null);
|
Cursor c = dbRO.query(DatabaseConstants.STORY_TABLE, new String[]{DatabaseConstants.STORY_IMAGE_URLS}, null, null, null, null, null);
|
||||||
Set<String> urls = new HashSet<String>(c.getCount());
|
Set<String> urls = new HashSet<String>(c.getCount());
|
||||||
|
@ -312,6 +323,7 @@ public class BlurDatabaseHelper {
|
||||||
return urls;
|
return urls;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@NonNull
|
||||||
public Set<String> getAllStoryThumbnails() {
|
public Set<String> getAllStoryThumbnails() {
|
||||||
Cursor c = dbRO.query(DatabaseConstants.STORY_TABLE, new String[]{DatabaseConstants.STORY_THUMBNAIL_URL}, null, null, null, null, null);
|
Cursor c = dbRO.query(DatabaseConstants.STORY_TABLE, new String[]{DatabaseConstants.STORY_THUMBNAIL_URL}, null, null, null, null, null);
|
||||||
Set<String> urls = new HashSet<String>(c.getCount());
|
Set<String> urls = new HashSet<String>(c.getCount());
|
||||||
|
@ -325,7 +337,7 @@ public class BlurDatabaseHelper {
|
||||||
return urls;
|
return urls;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void insertStories(StoriesResponse apiResponse, StateFilter stateFilter, boolean forImmediateReading) {
|
public void insertStories(@NonNull StoriesResponse apiResponse, @NonNull StateFilter stateFilter, boolean forImmediateReading) {
|
||||||
synchronized (RW_MUTEX) {
|
synchronized (RW_MUTEX) {
|
||||||
// do not attempt to use beginTransactionNonExclusive() to reduce lock time for this very heavy set
|
// do not attempt to use beginTransactionNonExclusive() to reduce lock time for this very heavy set
|
||||||
// of calls. most versions of Android incorrectly implement the underlying SQLite calls and will
|
// of calls. most versions of Android incorrectly implement the underlying SQLite calls and will
|
||||||
|
@ -428,7 +440,7 @@ public class BlurDatabaseHelper {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void insertSingleStoryExtSync(Story story) {
|
private void insertSingleStoryExtSync(@NonNull Story story) {
|
||||||
// pick a thumbnail for the story
|
// pick a thumbnail for the story
|
||||||
story.thumbnailUrl = Story.guessStoryThumbnailURL(story);
|
story.thumbnailUrl = Story.guessStoryThumbnailURL(story);
|
||||||
// insert the story data
|
// insert the story data
|
||||||
|
@ -459,7 +471,7 @@ public class BlurDatabaseHelper {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void insertSingleCommentExtSync(Comment comment) {
|
private void insertSingleCommentExtSync(@NonNull Comment comment) {
|
||||||
// real comments replace placeholders
|
// real comments replace placeholders
|
||||||
int count = dbRW.delete(DatabaseConstants.COMMENT_TABLE, DatabaseConstants.COMMENT_ISPLACEHOLDER + " = ?", new String[]{"true"});
|
int count = dbRW.delete(DatabaseConstants.COMMENT_TABLE, DatabaseConstants.COMMENT_ISPLACEHOLDER + " = ?", new String[]{"true"});
|
||||||
// comments always come with an updated set of replies, so remove old ones first
|
// comments always come with an updated set of replies, so remove old ones first
|
||||||
|
@ -477,7 +489,7 @@ public class BlurDatabaseHelper {
|
||||||
* to reflect a social action, but that the new copy is missing some fields. Attempt to merge the
|
* to reflect a social action, but that the new copy is missing some fields. Attempt to merge the
|
||||||
* new story with the old one.
|
* new story with the old one.
|
||||||
*/
|
*/
|
||||||
public void updateStory(StoriesResponse apiResponse, StateFilter stateFilter, boolean forImmediateReading) {
|
public void updateStory(@NonNull StoriesResponse apiResponse, @NonNull StateFilter stateFilter, boolean forImmediateReading) {
|
||||||
if (apiResponse.story == null) {
|
if (apiResponse.story == null) {
|
||||||
com.newsblur.util.Log.e(this, "updateStory called on response with missing single story");
|
com.newsblur.util.Log.e(this, "updateStory called on response with missing single story");
|
||||||
return;
|
return;
|
||||||
|
@ -503,7 +515,7 @@ public class BlurDatabaseHelper {
|
||||||
* Update an existing comment and associated replies based upon a new copy received from a social
|
* Update an existing comment and associated replies based upon a new copy received from a social
|
||||||
* API. Most social APIs vend an updated view that replaces any old or placeholder records.
|
* API. Most social APIs vend an updated view that replaces any old or placeholder records.
|
||||||
*/
|
*/
|
||||||
public void updateComment(CommentResponse apiResponse, String storyId) {
|
public void updateComment(@NonNull CommentResponse apiResponse, @Nullable String storyId) {
|
||||||
synchronized (RW_MUTEX) {
|
synchronized (RW_MUTEX) {
|
||||||
// comments often contain enclosed replies, so batch them.
|
// comments often contain enclosed replies, so batch them.
|
||||||
dbRW.beginTransaction();
|
dbRW.beginTransaction();
|
||||||
|
@ -528,7 +540,7 @@ public class BlurDatabaseHelper {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void fixMissingStoryFeeds(Story[] stories) {
|
public void fixMissingStoryFeeds(@Nullable Story[] stories) {
|
||||||
// start off with feeds mentioned by the set of stories
|
// start off with feeds mentioned by the set of stories
|
||||||
Set<String> feedIds = new HashSet<String>();
|
Set<String> feedIds = new HashSet<String>();
|
||||||
for (Story story : stories) {
|
for (Story story : stories) {
|
||||||
|
@ -564,7 +576,7 @@ public class BlurDatabaseHelper {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public Folder getFolder(String folderName) {
|
public Folder getFolder(@NonNull String folderName) {
|
||||||
String[] selArgs = new String[] {folderName};
|
String[] selArgs = new String[] {folderName};
|
||||||
String selection = DatabaseConstants.FOLDER_NAME + " = ?";
|
String selection = DatabaseConstants.FOLDER_NAME + " = ?";
|
||||||
Cursor c = dbRO.query(DatabaseConstants.FOLDER_TABLE, null, selection, selArgs, null, null, null);
|
Cursor c = dbRO.query(DatabaseConstants.FOLDER_TABLE, null, selection, selArgs, null, null, null);
|
||||||
|
@ -577,13 +589,13 @@ public class BlurDatabaseHelper {
|
||||||
return folder;
|
return folder;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void touchStory(String hash) {
|
public void touchStory(@Nullable String hash) {
|
||||||
ContentValues values = new ContentValues();
|
ContentValues values = new ContentValues();
|
||||||
values.put(DatabaseConstants.STORY_LAST_READ_DATE, (new Date()).getTime());
|
values.put(DatabaseConstants.STORY_LAST_READ_DATE, (new Date()).getTime());
|
||||||
synchronized (RW_MUTEX) {dbRW.update(DatabaseConstants.STORY_TABLE, values, DatabaseConstants.STORY_LAST_READ_DATE + " < 1 AND " + DatabaseConstants.STORY_HASH + " = ?", new String[]{hash});}
|
synchronized (RW_MUTEX) {dbRW.update(DatabaseConstants.STORY_TABLE, values, DatabaseConstants.STORY_LAST_READ_DATE + " < 1 AND " + DatabaseConstants.STORY_HASH + " = ?", new String[]{hash});}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void markStoryHashesRead(Collection<String> hashes) {
|
public void markStoryHashesRead(@NonNull Collection<String> hashes) {
|
||||||
synchronized (RW_MUTEX) {
|
synchronized (RW_MUTEX) {
|
||||||
dbRW.beginTransaction();
|
dbRW.beginTransaction();
|
||||||
try {
|
try {
|
||||||
|
@ -599,7 +611,7 @@ public class BlurDatabaseHelper {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void markStoryHashesStarred(Collection<String> hashes, boolean isStarred) {
|
public void markStoryHashesStarred(@NonNull Collection<String> hashes, boolean isStarred) {
|
||||||
synchronized (RW_MUTEX) {
|
synchronized (RW_MUTEX) {
|
||||||
dbRW.beginTransaction();
|
dbRW.beginTransaction();
|
||||||
try {
|
try {
|
||||||
|
@ -615,7 +627,7 @@ public class BlurDatabaseHelper {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setFeedsActive(Set<String> feedIds, boolean active) {
|
public void setFeedsActive(@NonNull Set<String> feedIds, boolean active) {
|
||||||
synchronized (RW_MUTEX) {
|
synchronized (RW_MUTEX) {
|
||||||
dbRW.beginTransaction();
|
dbRW.beginTransaction();
|
||||||
try {
|
try {
|
||||||
|
@ -631,13 +643,13 @@ public class BlurDatabaseHelper {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setFeedFetchPending(String feedId) {
|
public void setFeedFetchPending(@NonNull String feedId) {
|
||||||
ContentValues values = new ContentValues();
|
ContentValues values = new ContentValues();
|
||||||
values.put(DatabaseConstants.FEED_FETCH_PENDING, true);
|
values.put(DatabaseConstants.FEED_FETCH_PENDING, true);
|
||||||
synchronized (RW_MUTEX) {dbRW.update(DatabaseConstants.FEED_TABLE, values, DatabaseConstants.FEED_ID + " = ?", new String[]{feedId});}
|
synchronized (RW_MUTEX) {dbRW.update(DatabaseConstants.FEED_TABLE, values, DatabaseConstants.FEED_ID + " = ?", new String[]{feedId});}
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isFeedSetFetchPending(FeedSet fs) {
|
public boolean isFeedSetFetchPending(@NonNull FeedSet fs) {
|
||||||
if (fs.getSingleFeed() != null) {
|
if (fs.getSingleFeed() != null) {
|
||||||
String feedId = fs.getSingleFeed();
|
String feedId = fs.getSingleFeed();
|
||||||
Cursor c = dbRO.query(DatabaseConstants.FEED_TABLE,
|
Cursor c = dbRO.query(DatabaseConstants.FEED_TABLE,
|
||||||
|
@ -657,7 +669,7 @@ public class BlurDatabaseHelper {
|
||||||
/**
|
/**
|
||||||
* Marks a story (un)read but does not adjust counts. Must stay idempotent an time-insensitive.
|
* Marks a story (un)read but does not adjust counts. Must stay idempotent an time-insensitive.
|
||||||
*/
|
*/
|
||||||
public void setStoryReadState(String hash, boolean read) {
|
public void setStoryReadState(@Nullable String hash, boolean read) {
|
||||||
ContentValues values = new ContentValues();
|
ContentValues values = new ContentValues();
|
||||||
values.put(DatabaseConstants.STORY_READ, read);
|
values.put(DatabaseConstants.STORY_READ, read);
|
||||||
synchronized (RW_MUTEX) {dbRW.update(DatabaseConstants.STORY_TABLE, values, DatabaseConstants.STORY_HASH + " = ?", new String[]{hash});}
|
synchronized (RW_MUTEX) {dbRW.update(DatabaseConstants.STORY_TABLE, values, DatabaseConstants.STORY_HASH + " = ?", new String[]{hash});}
|
||||||
|
@ -668,7 +680,8 @@ public class BlurDatabaseHelper {
|
||||||
*
|
*
|
||||||
* @return the set of feed IDs that potentially have counts impacted by the mark.
|
* @return the set of feed IDs that potentially have counts impacted by the mark.
|
||||||
*/
|
*/
|
||||||
public Set<FeedSet> setStoryReadState(Story story, boolean read) {
|
@NonNull
|
||||||
|
public Set<FeedSet> setStoryReadState(@NonNull Story story, boolean read) {
|
||||||
// calculate the impact surface so the caller can re-check counts if needed
|
// calculate the impact surface so the caller can re-check counts if needed
|
||||||
Set<FeedSet> impactedFeeds = new HashSet<FeedSet>();
|
Set<FeedSet> impactedFeeds = new HashSet<FeedSet>();
|
||||||
impactedFeeds.add(FeedSet.singleFeed(story.feedId));
|
impactedFeeds.add(FeedSet.singleFeed(story.feedId));
|
||||||
|
@ -746,7 +759,7 @@ public class BlurDatabaseHelper {
|
||||||
* Marks a range of stories in a subset of feeds as read. Does not update unread counts;
|
* Marks a range of stories in a subset of feeds as read. Does not update unread counts;
|
||||||
* the caller must use updateLocalFeedCounts() or the /reader/feed_unread_count API.
|
* the caller must use updateLocalFeedCounts() or the /reader/feed_unread_count API.
|
||||||
*/
|
*/
|
||||||
public void markStoriesRead(FeedSet fs, Long olderThan, Long newerThan) {
|
public void markStoriesRead(@NonNull FeedSet fs, @Nullable Long olderThan, @Nullable Long newerThan) {
|
||||||
ContentValues values = new ContentValues();
|
ContentValues values = new ContentValues();
|
||||||
values.put(DatabaseConstants.STORY_READ, true);
|
values.put(DatabaseConstants.STORY_READ, true);
|
||||||
String rangeSelection = null;
|
String rangeSelection = null;
|
||||||
|
@ -774,7 +787,7 @@ public class BlurDatabaseHelper {
|
||||||
/**
|
/**
|
||||||
* Get the unread count for the given feedset based on the totals in the feeds table.
|
* Get the unread count for the given feedset based on the totals in the feeds table.
|
||||||
*/
|
*/
|
||||||
public int getUnreadCount(FeedSet fs, StateFilter stateFilter) {
|
public int getUnreadCount(@NonNull FeedSet fs, @NonNull StateFilter stateFilter) {
|
||||||
// if reading in starred-only mode, there are no unreads, since stories vended as starred are never unread
|
// if reading in starred-only mode, there are no unreads, since stories vended as starred are never unread
|
||||||
if (fs.isFilterSaved()) return 0;
|
if (fs.isFilterSaved()) return 0;
|
||||||
if (fs.isAllNormal()) {
|
if (fs.isAllNormal()) {
|
||||||
|
@ -802,7 +815,7 @@ public class BlurDatabaseHelper {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private int getFeedsUnreadCount(StateFilter stateFilter, String selection, String[] selArgs) {
|
private int getFeedsUnreadCount(@NonNull StateFilter stateFilter, @Nullable String selection, @Nullable String[] selArgs) {
|
||||||
int result = 0;
|
int result = 0;
|
||||||
Cursor c = dbRO.query(DatabaseConstants.FEED_TABLE, null, selection, selArgs, null, null, null);
|
Cursor c = dbRO.query(DatabaseConstants.FEED_TABLE, null, selection, selArgs, null, null, null);
|
||||||
while (c.moveToNext()) {
|
while (c.moveToNext()) {
|
||||||
|
@ -816,7 +829,7 @@ public class BlurDatabaseHelper {
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
private int getSocialFeedsUnreadCount(StateFilter stateFilter, String selection, String[] selArgs) {
|
private int getSocialFeedsUnreadCount(@NonNull StateFilter stateFilter, @NonNull String selection, @Nullable String[] selArgs) {
|
||||||
int result = 0;
|
int result = 0;
|
||||||
Cursor c = dbRO.query(DatabaseConstants.SOCIALFEED_TABLE, null, selection, selArgs, null, null, null);
|
Cursor c = dbRO.query(DatabaseConstants.SOCIALFEED_TABLE, null, selection, selArgs, null, null, null);
|
||||||
while (c.moveToNext()) {
|
while (c.moveToNext()) {
|
||||||
|
@ -829,18 +842,18 @@ public class BlurDatabaseHelper {
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void updateFeedCounts(String feedId, ContentValues values) {
|
public void updateFeedCounts(@Nullable String feedId, @Nullable ContentValues values) {
|
||||||
synchronized (RW_MUTEX) {dbRW.update(DatabaseConstants.FEED_TABLE, values, DatabaseConstants.FEED_ID + " = ?", new String[]{feedId});}
|
synchronized (RW_MUTEX) {dbRW.update(DatabaseConstants.FEED_TABLE, values, DatabaseConstants.FEED_ID + " = ?", new String[]{feedId});}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void updateSocialFeedCounts(String feedId, ContentValues values) {
|
public void updateSocialFeedCounts(@Nullable String feedId, @Nullable ContentValues values) {
|
||||||
synchronized (RW_MUTEX) {dbRW.update(DatabaseConstants.SOCIALFEED_TABLE, values, DatabaseConstants.SOCIAL_FEED_ID + " = ?", new String[]{feedId});}
|
synchronized (RW_MUTEX) {dbRW.update(DatabaseConstants.SOCIALFEED_TABLE, values, DatabaseConstants.SOCIAL_FEED_ID + " = ?", new String[]{feedId});}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Refreshes the counts in the feeds/socialfeeds tables by counting stories in the story table.
|
* Refreshes the counts in the feeds/socialfeeds tables by counting stories in the story table.
|
||||||
*/
|
*/
|
||||||
public void updateLocalFeedCounts(FeedSet fs) {
|
public void updateLocalFeedCounts(@NonNull FeedSet fs) {
|
||||||
// decompose the FeedSet into a list of single feeds that need to be recounted
|
// decompose the FeedSet into a list of single feeds that need to be recounted
|
||||||
List<String> feedIds = new ArrayList<String>();
|
List<String> feedIds = new ArrayList<String>();
|
||||||
List<String> socialFeedIds = new ArrayList<String>();
|
List<String> socialFeedIds = new ArrayList<String>();
|
||||||
|
@ -883,7 +896,7 @@ public class BlurDatabaseHelper {
|
||||||
/**
|
/**
|
||||||
* Get the unread count for the given feedset based on local story state.
|
* Get the unread count for the given feedset based on local story state.
|
||||||
*/
|
*/
|
||||||
public int getLocalUnreadCount(FeedSet fs, StateFilter stateFilter) {
|
public int getLocalUnreadCount(@NonNull FeedSet fs, @NonNull StateFilter stateFilter) {
|
||||||
StringBuilder sel = new StringBuilder();
|
StringBuilder sel = new StringBuilder();
|
||||||
ArrayList<String> selArgs = new ArrayList<String>();
|
ArrayList<String> selArgs = new ArrayList<String>();
|
||||||
getLocalStorySelectionAndArgs(sel, selArgs, fs, stateFilter, ReadFilter.UNREAD);
|
getLocalStorySelectionAndArgs(sel, selArgs, fs, stateFilter, ReadFilter.UNREAD);
|
||||||
|
@ -900,16 +913,17 @@ public class BlurDatabaseHelper {
|
||||||
synchronized (RW_MUTEX) {dbRW.update(DatabaseConstants.STORY_TABLE, values, null, null);}
|
synchronized (RW_MUTEX) {dbRW.update(DatabaseConstants.STORY_TABLE, values, null, null);}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void enqueueAction(ReadingAction ra) {
|
public void enqueueAction(@NonNull ReadingAction ra) {
|
||||||
synchronized (RW_MUTEX) {dbRW.insertOrThrow(DatabaseConstants.ACTION_TABLE, null, ra.toContentValues());}
|
synchronized (RW_MUTEX) {dbRW.insertOrThrow(DatabaseConstants.ACTION_TABLE, null, ra.toContentValues());}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@NonNull
|
||||||
public Cursor getActions() {
|
public Cursor getActions() {
|
||||||
String q = "SELECT * FROM " + DatabaseConstants.ACTION_TABLE;
|
String q = "SELECT * FROM " + DatabaseConstants.ACTION_TABLE;
|
||||||
return dbRO.rawQuery(q, null);
|
return dbRO.rawQuery(q, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void incrementActionTried(String actionId) {
|
public void incrementActionTried(@Nullable String actionId) {
|
||||||
synchronized (RW_MUTEX) {
|
synchronized (RW_MUTEX) {
|
||||||
String q = "UPDATE " + DatabaseConstants.ACTION_TABLE +
|
String q = "UPDATE " + DatabaseConstants.ACTION_TABLE +
|
||||||
" SET " + DatabaseConstants.ACTION_TRIED + " = " + DatabaseConstants.ACTION_TRIED + " + 1" +
|
" SET " + DatabaseConstants.ACTION_TRIED + " = " + DatabaseConstants.ACTION_TRIED + " + 1" +
|
||||||
|
@ -926,7 +940,7 @@ public class BlurDatabaseHelper {
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void clearAction(String actionId) {
|
public void clearAction(@Nullable String actionId) {
|
||||||
synchronized (RW_MUTEX) {dbRW.delete(DatabaseConstants.ACTION_TABLE, DatabaseConstants.ACTION_ID + " = ?", new String[]{actionId});}
|
synchronized (RW_MUTEX) {dbRW.delete(DatabaseConstants.ACTION_TABLE, DatabaseConstants.ACTION_ID + " = ?", new String[]{actionId});}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -978,7 +992,7 @@ public class BlurDatabaseHelper {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setStoryShared(String hash, @Nullable String currentUserId, boolean shared) {
|
public void setStoryShared(@Nullable String hash, @Nullable String currentUserId, boolean shared) {
|
||||||
// get a fresh copy of the story from the DB so we can append to the shared ID set
|
// get a fresh copy of the story from the DB so we can append to the shared ID set
|
||||||
Cursor c = dbRO.query(DatabaseConstants.STORY_TABLE,
|
Cursor c = dbRO.query(DatabaseConstants.STORY_TABLE,
|
||||||
new String[]{DatabaseConstants.STORY_SHARED_USER_IDS},
|
new String[]{DatabaseConstants.STORY_SHARED_USER_IDS},
|
||||||
|
@ -1007,7 +1021,8 @@ public class BlurDatabaseHelper {
|
||||||
synchronized (RW_MUTEX) {dbRW.update(DatabaseConstants.STORY_TABLE, values, DatabaseConstants.STORY_HASH + " = ?", new String[]{hash});}
|
synchronized (RW_MUTEX) {dbRW.update(DatabaseConstants.STORY_TABLE, values, DatabaseConstants.STORY_HASH + " = ?", new String[]{hash});}
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getStoryText(String hash) {
|
@Nullable
|
||||||
|
public String getStoryText(@Nullable String hash) {
|
||||||
String q = "SELECT " + DatabaseConstants.STORY_TEXT_STORY_TEXT +
|
String q = "SELECT " + DatabaseConstants.STORY_TEXT_STORY_TEXT +
|
||||||
" FROM " + DatabaseConstants.STORY_TEXT_TABLE +
|
" FROM " + DatabaseConstants.STORY_TEXT_TABLE +
|
||||||
" WHERE " + DatabaseConstants.STORY_TEXT_STORY_HASH + " = ?";
|
" WHERE " + DatabaseConstants.STORY_TEXT_STORY_HASH + " = ?";
|
||||||
|
@ -1023,7 +1038,8 @@ public class BlurDatabaseHelper {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getStoryContent(String hash) {
|
@Nullable
|
||||||
|
public String getStoryContent(@Nullable String hash) {
|
||||||
String q = "SELECT " + DatabaseConstants.STORY_CONTENT +
|
String q = "SELECT " + DatabaseConstants.STORY_CONTENT +
|
||||||
" FROM " + DatabaseConstants.STORY_TABLE +
|
" FROM " + DatabaseConstants.STORY_TABLE +
|
||||||
" WHERE " + DatabaseConstants.STORY_HASH + " = ?";
|
" WHERE " + DatabaseConstants.STORY_HASH + " = ?";
|
||||||
|
@ -1040,18 +1056,20 @@ public class BlurDatabaseHelper {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void putStoryText(String hash, String text) {
|
public void putStoryText(@Nullable String hash, @NonNull String text) {
|
||||||
ContentValues values = new ContentValues();
|
ContentValues values = new ContentValues();
|
||||||
values.put(DatabaseConstants.STORY_TEXT_STORY_HASH, hash);
|
values.put(DatabaseConstants.STORY_TEXT_STORY_HASH, hash);
|
||||||
values.put(DatabaseConstants.STORY_TEXT_STORY_TEXT, text);
|
values.put(DatabaseConstants.STORY_TEXT_STORY_TEXT, text);
|
||||||
synchronized (RW_MUTEX) {dbRW.insertOrThrow(DatabaseConstants.STORY_TEXT_TABLE, null, values);}
|
synchronized (RW_MUTEX) {dbRW.insertOrThrow(DatabaseConstants.STORY_TEXT_TABLE, null, values);}
|
||||||
}
|
}
|
||||||
|
|
||||||
public Cursor getSocialFeedsCursor(CancellationSignal cancellationSignal) {
|
@NonNull
|
||||||
|
public Cursor getSocialFeedsCursor(@NonNull CancellationSignal cancellationSignal) {
|
||||||
return query(false, DatabaseConstants.SOCIALFEED_TABLE, null, null, null, null, null, "UPPER(" + DatabaseConstants.SOCIAL_FEED_TITLE + ") ASC", null, cancellationSignal);
|
return query(false, DatabaseConstants.SOCIALFEED_TABLE, null, null, null, null, null, "UPPER(" + DatabaseConstants.SOCIAL_FEED_TITLE + ") ASC", null, cancellationSignal);
|
||||||
}
|
}
|
||||||
|
|
||||||
public SocialFeed getSocialFeed(String feedId) {
|
@Nullable
|
||||||
|
public SocialFeed getSocialFeed(@Nullable String feedId) {
|
||||||
Cursor c = dbRO.query(DatabaseConstants.SOCIALFEED_TABLE, null, DatabaseConstants.SOCIAL_FEED_ID + " = ?", new String[] {feedId}, null, null, null);
|
Cursor c = dbRO.query(DatabaseConstants.SOCIALFEED_TABLE, null, DatabaseConstants.SOCIAL_FEED_ID + " = ?", new String[] {feedId}, null, null, null);
|
||||||
SocialFeed result = null;
|
SocialFeed result = null;
|
||||||
while (c.moveToNext()) {
|
while (c.moveToNext()) {
|
||||||
|
@ -1062,7 +1080,7 @@ public class BlurDatabaseHelper {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
public StarredCount getStarredFeedByTag(String tag) {
|
public StarredCount getStarredFeedByTag(@NonNull String tag) {
|
||||||
Cursor c = dbRO.query(DatabaseConstants.STARREDCOUNTS_TABLE, null, DatabaseConstants.STARREDCOUNTS_TAG + " = ?", new String[] {tag}, null, null, null);
|
Cursor c = dbRO.query(DatabaseConstants.STARREDCOUNTS_TABLE, null, DatabaseConstants.STARREDCOUNTS_TAG + " = ?", new String[] {tag}, null, null, null);
|
||||||
StarredCount result = null;
|
StarredCount result = null;
|
||||||
while (c.moveToNext()) {
|
while (c.moveToNext()) {
|
||||||
|
@ -1082,30 +1100,37 @@ public class BlurDatabaseHelper {
|
||||||
return folders;
|
return folders;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Cursor getFoldersCursor(CancellationSignal cancellationSignal) {
|
@NonNull
|
||||||
|
public Cursor getFoldersCursor(@Nullable CancellationSignal cancellationSignal) {
|
||||||
return query(false, DatabaseConstants.FOLDER_TABLE, null, null, null, null, null, null, null, cancellationSignal);
|
return query(false, DatabaseConstants.FOLDER_TABLE, null, null, null, null, null, null, null, cancellationSignal);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Cursor getFeedsCursor(CancellationSignal cancellationSignal) {
|
@NonNull
|
||||||
|
public Cursor getFeedsCursor(@NonNull CancellationSignal cancellationSignal) {
|
||||||
return query(false, DatabaseConstants.FEED_TABLE, null, null, null, null, null, "UPPER(" + DatabaseConstants.FEED_TITLE + ") ASC", null, cancellationSignal);
|
return query(false, DatabaseConstants.FEED_TABLE, null, null, null, null, null, "UPPER(" + DatabaseConstants.FEED_TITLE + ") ASC", null, cancellationSignal);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Cursor getSavedStoryCountsCursor(CancellationSignal cancellationSignal) {
|
@NonNull
|
||||||
|
public Cursor getSavedStoryCountsCursor(@NonNull CancellationSignal cancellationSignal) {
|
||||||
return query(false, DatabaseConstants.STARREDCOUNTS_TABLE, null, null, null, null, null, null, null, cancellationSignal);
|
return query(false, DatabaseConstants.STARREDCOUNTS_TABLE, null, null, null, null, null, null, null, cancellationSignal);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Cursor getSavedSearchCursor(CancellationSignal cancellationSignal) {
|
@NonNull
|
||||||
|
public Cursor getSavedSearchCursor(@NonNull CancellationSignal cancellationSignal) {
|
||||||
return query(false, DatabaseConstants.SAVED_SEARCH_TABLE, null, null, null, null, null, null, null, cancellationSignal);
|
return query(false, DatabaseConstants.SAVED_SEARCH_TABLE, null, null, null, null, null, null, null, cancellationSignal);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
public Cursor getNotifyFocusStoriesCursor() {
|
public Cursor getNotifyFocusStoriesCursor() {
|
||||||
return rawQuery(DatabaseConstants.NOTIFY_FOCUS_STORY_QUERY, null, null);
|
return rawQuery(DatabaseConstants.NOTIFY_FOCUS_STORY_QUERY, null, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
public Cursor getNotifyUnreadStoriesCursor() {
|
public Cursor getNotifyUnreadStoriesCursor() {
|
||||||
return rawQuery(DatabaseConstants.NOTIFY_UNREAD_STORY_QUERY, null, null);
|
return rawQuery(DatabaseConstants.NOTIFY_UNREAD_STORY_QUERY, null, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@NonNull
|
||||||
public Set<String> getNotifyFeeds() {
|
public Set<String> getNotifyFeeds() {
|
||||||
String q = "SELECT " + DatabaseConstants.FEED_ID + " FROM " + DatabaseConstants.FEED_TABLE +
|
String q = "SELECT " + DatabaseConstants.FEED_ID + " FROM " + DatabaseConstants.FEED_TABLE +
|
||||||
" WHERE " + DatabaseConstants.FEED_NOTIFICATION_FILTER + " = '" + Feed.NOTIFY_FILTER_FOCUS + "'" +
|
" WHERE " + DatabaseConstants.FEED_NOTIFICATION_FILTER + " = '" + Feed.NOTIFY_FILTER_FOCUS + "'" +
|
||||||
|
@ -1122,25 +1147,8 @@ public class BlurDatabaseHelper {
|
||||||
return feedIds;
|
return feedIds;
|
||||||
}
|
}
|
||||||
|
|
||||||
private Cursor getStoriesCursor(@Nullable FeedSet fs, CancellationSignal cancellationSignal) {
|
@NonNull
|
||||||
StringBuilder q = new StringBuilder(DatabaseConstants.STORY_QUERY_BASE_0);
|
public Cursor getActiveStoriesCursor(@NonNull FeedSet fs, @NonNull CursorFilters cursorFilters, @NonNull CancellationSignal cancellationSignal) {
|
||||||
|
|
||||||
if (fs != null && !TextUtils.isEmpty(fs.getSingleFeed())) {
|
|
||||||
q.append(DatabaseConstants.STORY_FEED_ID);
|
|
||||||
q.append(" = ");
|
|
||||||
q.append(fs.getSingleFeed());
|
|
||||||
} else {
|
|
||||||
q.append(DatabaseConstants.FEED_ACTIVE);
|
|
||||||
q.append(" = 1");
|
|
||||||
}
|
|
||||||
|
|
||||||
q.append(" ORDER BY ");
|
|
||||||
q.append(DatabaseConstants.STORY_TIMESTAMP);
|
|
||||||
q.append(" DESC LIMIT 20");
|
|
||||||
return rawQuery(q.toString(), null, cancellationSignal);
|
|
||||||
}
|
|
||||||
|
|
||||||
public Cursor getActiveStoriesCursor(FeedSet fs, CursorFilters cursorFilters, CancellationSignal cancellationSignal) {
|
|
||||||
// get the stories for this FS
|
// get the stories for this FS
|
||||||
Cursor result = getActiveStoriesCursorNoPrep(fs, cursorFilters.getStoryOrder(), cancellationSignal);
|
Cursor result = getActiveStoriesCursorNoPrep(fs, cursorFilters.getStoryOrder(), cancellationSignal);
|
||||||
// if the result is blank, try to prime the session table with existing stories, in case we
|
// if the result is blank, try to prime the session table with existing stories, in case we
|
||||||
|
@ -1153,8 +1161,9 @@ public class BlurDatabaseHelper {
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
private Cursor getActiveStoriesCursorNoPrep(FeedSet fs, StoryOrder order, CancellationSignal cancellationSignal) {
|
@NonNull
|
||||||
|
private Cursor getActiveStoriesCursorNoPrep(@NonNull FeedSet fs, @NonNull StoryOrder order, @NonNull CancellationSignal cancellationSignal) {
|
||||||
// stories aren't actually queried directly via the FeedSet and filters set in the UI. rather,
|
// stories aren't actually queried directly via the FeedSet and filters set in the UI. rather,
|
||||||
// those filters are use to push live or cached story hashes into the reading session table, and
|
// those filters are use to push live or cached story hashes into the reading session table, and
|
||||||
// those hashes are used to pull story data from the story table
|
// those hashes are used to pull story data from the story table
|
||||||
|
@ -1182,7 +1191,7 @@ public class BlurDatabaseHelper {
|
||||||
* criteria for the given FeedSet and filters; these hashes will be supplemented by hashes
|
* criteria for the given FeedSet and filters; these hashes will be supplemented by hashes
|
||||||
* fetched via the API and used to actually select story data when rendering story lists.
|
* fetched via the API and used to actually select story data when rendering story lists.
|
||||||
*/
|
*/
|
||||||
public void prepareReadingSession(FeedSet fs, StateFilter stateFilter, ReadFilter readFilter) {
|
public void prepareReadingSession(@NonNull FeedSet fs, @NonNull StateFilter stateFilter, @NonNull ReadFilter readFilter) {
|
||||||
// a selection filter that will be used to pull active story hashes from the stories table into the reading session table
|
// a selection filter that will be used to pull active story hashes from the stories table into the reading session table
|
||||||
StringBuilder sel = new StringBuilder();
|
StringBuilder sel = new StringBuilder();
|
||||||
// any selection args that need to be used within the inner select statement
|
// any selection args that need to be used within the inner select statement
|
||||||
|
@ -1202,7 +1211,7 @@ public class BlurDatabaseHelper {
|
||||||
* Gets hashes of already-fetched stories that satisfy the given FeedSet and filters. Can be used
|
* Gets hashes of already-fetched stories that satisfy the given FeedSet and filters. Can be used
|
||||||
* both to populate a reading session or to count local unreads.
|
* both to populate a reading session or to count local unreads.
|
||||||
*/
|
*/
|
||||||
private void getLocalStorySelectionAndArgs(StringBuilder sel, List<String> selArgs, FeedSet fs, StateFilter stateFilter, ReadFilter readFilter) {
|
private void getLocalStorySelectionAndArgs(@NonNull StringBuilder sel, @NonNull List<String> selArgs, @NonNull FeedSet fs, @NonNull StateFilter stateFilter, @NonNull ReadFilter readFilter) {
|
||||||
// if the user has requested saved stories, ignore the unreads filter, as saveds do not have this state
|
// if the user has requested saved stories, ignore the unreads filter, as saveds do not have this state
|
||||||
if (fs.isFilterSaved()) {
|
if (fs.isFilterSaved()) {
|
||||||
readFilter = ReadFilter.ALL;
|
readFilter = ReadFilter.ALL;
|
||||||
|
@ -1283,7 +1292,7 @@ public class BlurDatabaseHelper {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setSessionFeedSet(FeedSet fs) {
|
public void setSessionFeedSet(@Nullable FeedSet fs) {
|
||||||
if (fs == null) {
|
if (fs == null) {
|
||||||
synchronized (RW_MUTEX) {dbRW.delete(DatabaseConstants.SYNC_METADATA_TABLE, DatabaseConstants.SYNC_METADATA_KEY + " = ?", new String[] {DatabaseConstants.SYNC_METADATA_KEY_SESSION_FEED_SET});}
|
synchronized (RW_MUTEX) {dbRW.delete(DatabaseConstants.SYNC_METADATA_TABLE, DatabaseConstants.SYNC_METADATA_KEY + " = ?", new String[] {DatabaseConstants.SYNC_METADATA_KEY_SESSION_FEED_SET});}
|
||||||
} else {
|
} else {
|
||||||
|
@ -1293,9 +1302,10 @@ public class BlurDatabaseHelper {
|
||||||
synchronized (RW_MUTEX) {dbRW.insertWithOnConflict(DatabaseConstants.SYNC_METADATA_TABLE, null, values, SQLiteDatabase.CONFLICT_REPLACE);}
|
synchronized (RW_MUTEX) {dbRW.insertWithOnConflict(DatabaseConstants.SYNC_METADATA_TABLE, null, values, SQLiteDatabase.CONFLICT_REPLACE);}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
public FeedSet getSessionFeedSet() {
|
public FeedSet getSessionFeedSet() {
|
||||||
FeedSet fs = null;
|
FeedSet fs;
|
||||||
Cursor c = dbRO.query(DatabaseConstants.SYNC_METADATA_TABLE, null, DatabaseConstants.SYNC_METADATA_KEY + " = ?", new String[] {DatabaseConstants.SYNC_METADATA_KEY_SESSION_FEED_SET}, null, null, null, null);
|
Cursor c = dbRO.query(DatabaseConstants.SYNC_METADATA_TABLE, null, DatabaseConstants.SYNC_METADATA_KEY + " = ?", new String[] {DatabaseConstants.SYNC_METADATA_KEY_SESSION_FEED_SET}, null, null, null, null);
|
||||||
if (c.getCount() < 1) return null;
|
if (c.getCount() < 1) return null;
|
||||||
c.moveToFirst();
|
c.moveToFirst();
|
||||||
|
@ -1304,20 +1314,21 @@ public class BlurDatabaseHelper {
|
||||||
return fs;
|
return fs;
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isFeedSetReady(FeedSet fs) {
|
public boolean isFeedSetReady(@Nullable FeedSet fs) {
|
||||||
return fs.equals(getSessionFeedSet());
|
return fs.equals(getSessionFeedSet());
|
||||||
}
|
}
|
||||||
|
|
||||||
public void clearClassifiersForFeed(String feedId) {
|
public void clearClassifiersForFeed(@Nullable String feedId) {
|
||||||
String[] selArgs = new String[] {feedId};
|
String[] selArgs = new String[] {feedId};
|
||||||
synchronized (RW_MUTEX) {dbRW.delete(DatabaseConstants.CLASSIFIER_TABLE, DatabaseConstants.CLASSIFIER_ID + " = ?", selArgs);}
|
synchronized (RW_MUTEX) {dbRW.delete(DatabaseConstants.CLASSIFIER_TABLE, DatabaseConstants.CLASSIFIER_ID + " = ?", selArgs);}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void insertClassifier(Classifier classifier) {
|
public void insertClassifier(@NonNull Classifier classifier) {
|
||||||
bulkInsertValues(DatabaseConstants.CLASSIFIER_TABLE, classifier.getContentValues());
|
bulkInsertValues(DatabaseConstants.CLASSIFIER_TABLE, classifier.getContentValues());
|
||||||
}
|
}
|
||||||
|
|
||||||
public Classifier getClassifierForFeed(String feedId) {
|
@NonNull
|
||||||
|
public Classifier getClassifierForFeed(@Nullable String feedId) {
|
||||||
String[] selArgs = new String[] {feedId};
|
String[] selArgs = new String[] {feedId};
|
||||||
Cursor c = dbRO.query(DatabaseConstants.CLASSIFIER_TABLE, null, DatabaseConstants.CLASSIFIER_ID + " = ?", selArgs, null, null, null);
|
Cursor c = dbRO.query(DatabaseConstants.CLASSIFIER_TABLE, null, DatabaseConstants.CLASSIFIER_ID + " = ?", selArgs, null, null, null);
|
||||||
Classifier classifier = Classifier.fromCursor(c);
|
Classifier classifier = Classifier.fromCursor(c);
|
||||||
|
@ -1326,7 +1337,8 @@ public class BlurDatabaseHelper {
|
||||||
return classifier;
|
return classifier;
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<Comment> getComments(String storyId) {
|
@NonNull
|
||||||
|
public List<Comment> getComments(@NonNull String storyId) {
|
||||||
String[] selArgs = new String[] {storyId};
|
String[] selArgs = new String[] {storyId};
|
||||||
String selection = DatabaseConstants.COMMENT_STORYID + " = ?";
|
String selection = DatabaseConstants.COMMENT_STORYID + " = ?";
|
||||||
Cursor c = dbRO.query(DatabaseConstants.COMMENT_TABLE, null, selection, selArgs, null, null, null);
|
Cursor c = dbRO.query(DatabaseConstants.COMMENT_TABLE, null, selection, selArgs, null, null, null);
|
||||||
|
@ -1338,7 +1350,8 @@ public class BlurDatabaseHelper {
|
||||||
return comments;
|
return comments;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Comment getComment(String storyId, String userId) {
|
@Nullable
|
||||||
|
public Comment getComment(@Nullable String storyId, @Nullable String userId) {
|
||||||
String selection = DatabaseConstants.COMMENT_STORYID + " = ? AND " + DatabaseConstants.COMMENT_USERID + " = ?";
|
String selection = DatabaseConstants.COMMENT_STORYID + " = ? AND " + DatabaseConstants.COMMENT_USERID + " = ?";
|
||||||
String[] selArgs = new String[] {storyId, userId};
|
String[] selArgs = new String[] {storyId, userId};
|
||||||
Cursor c = dbRO.query(DatabaseConstants.COMMENT_TABLE, null, selection, selArgs, null, null, null);
|
Cursor c = dbRO.query(DatabaseConstants.COMMENT_TABLE, null, selection, selArgs, null, null, null);
|
||||||
|
@ -1354,7 +1367,7 @@ public class BlurDatabaseHelper {
|
||||||
* will show up in the UI with reduced functionality until the server gets back to us with
|
* will show up in the UI with reduced functionality until the server gets back to us with
|
||||||
* an ID at which time the placeholder will be removed.
|
* an ID at which time the placeholder will be removed.
|
||||||
*/
|
*/
|
||||||
public void insertCommentPlaceholder(String storyId, @Nullable String userId, String commentText) {
|
public void insertCommentPlaceholder(@Nullable String storyId, @Nullable String userId, @Nullable String commentText) {
|
||||||
Comment comment = new Comment();
|
Comment comment = new Comment();
|
||||||
comment.isPlaceholder = true;
|
comment.isPlaceholder = true;
|
||||||
comment.id = Comment.PLACEHOLDER_COMMENT_ID + storyId + userId;
|
comment.id = Comment.PLACEHOLDER_COMMENT_ID + storyId + userId;
|
||||||
|
@ -1376,23 +1389,23 @@ public class BlurDatabaseHelper {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void editReply(String replyId, String replyText) {
|
public void editReply(@Nullable String replyId, @Nullable String replyText) {
|
||||||
ContentValues values = new ContentValues();
|
ContentValues values = new ContentValues();
|
||||||
values.put(DatabaseConstants.REPLY_TEXT, replyText);
|
values.put(DatabaseConstants.REPLY_TEXT, replyText);
|
||||||
synchronized (RW_MUTEX) {dbRW.update(DatabaseConstants.REPLY_TABLE, values, DatabaseConstants.REPLY_ID + " = ?", new String[]{replyId});}
|
synchronized (RW_MUTEX) {dbRW.update(DatabaseConstants.REPLY_TABLE, values, DatabaseConstants.REPLY_ID + " = ?", new String[]{replyId});}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void deleteReply(String replyId) {
|
public void deleteReply(@Nullable String replyId) {
|
||||||
synchronized (RW_MUTEX) {dbRW.delete(DatabaseConstants.REPLY_TABLE, DatabaseConstants.REPLY_ID + " = ?", new String[]{replyId});}
|
synchronized (RW_MUTEX) {dbRW.delete(DatabaseConstants.REPLY_TABLE, DatabaseConstants.REPLY_ID + " = ?", new String[]{replyId});}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void clearSelfComments(String storyId, @Nullable String userId) {
|
public void clearSelfComments(@Nullable String storyId, @Nullable String userId) {
|
||||||
synchronized (RW_MUTEX) {dbRW.delete(DatabaseConstants.COMMENT_TABLE,
|
synchronized (RW_MUTEX) {dbRW.delete(DatabaseConstants.COMMENT_TABLE,
|
||||||
DatabaseConstants.COMMENT_STORYID + " = ? AND " + DatabaseConstants.COMMENT_USERID + " = ?",
|
DatabaseConstants.COMMENT_STORYID + " = ? AND " + DatabaseConstants.COMMENT_USERID + " = ?",
|
||||||
new String[]{storyId, userId});}
|
new String[]{storyId, userId});}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setCommentLiked(String storyId, String commentUserId, @Nullable String currentUserId, boolean liked) {
|
public void setCommentLiked(@Nullable String storyId, @Nullable String commentUserId, @Nullable String currentUserId, boolean liked) {
|
||||||
// get a fresh copy of the story from the DB so we can append to the shared ID set
|
// get a fresh copy of the story from the DB so we can append to the shared ID set
|
||||||
Cursor c = dbRO.query(DatabaseConstants.COMMENT_TABLE,
|
Cursor c = dbRO.query(DatabaseConstants.COMMENT_TABLE,
|
||||||
null,
|
null,
|
||||||
|
@ -1421,7 +1434,8 @@ public class BlurDatabaseHelper {
|
||||||
synchronized (RW_MUTEX) {dbRW.update(DatabaseConstants.COMMENT_TABLE, values, DatabaseConstants.COMMENT_ID + " = ?", new String[]{comment.id});}
|
synchronized (RW_MUTEX) {dbRW.update(DatabaseConstants.COMMENT_TABLE, values, DatabaseConstants.COMMENT_ID + " = ?", new String[]{comment.id});}
|
||||||
}
|
}
|
||||||
|
|
||||||
public UserProfile getUserProfile(String userId) {
|
@Nullable
|
||||||
|
public UserProfile getUserProfile(@Nullable String userId) {
|
||||||
String[] selArgs = new String[] {userId};
|
String[] selArgs = new String[] {userId};
|
||||||
String selection = DatabaseConstants.USER_USERID + " = ?";
|
String selection = DatabaseConstants.USER_USERID + " = ?";
|
||||||
Cursor c = dbRO.query(DatabaseConstants.USER_TABLE, null, selection, selArgs, null, null, null);
|
Cursor c = dbRO.query(DatabaseConstants.USER_TABLE, null, selection, selArgs, null, null, null);
|
||||||
|
@ -1430,7 +1444,8 @@ public class BlurDatabaseHelper {
|
||||||
return profile;
|
return profile;
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<Reply> getCommentReplies(String commentId) {
|
@NonNull
|
||||||
|
public List<Reply> getCommentReplies(@Nullable String commentId) {
|
||||||
String[] selArgs = new String[] {commentId};
|
String[] selArgs = new String[] {commentId};
|
||||||
String selection = DatabaseConstants.REPLY_COMMENTID+ " = ?";
|
String selection = DatabaseConstants.REPLY_COMMENTID+ " = ?";
|
||||||
Cursor c = dbRO.query(DatabaseConstants.REPLY_TABLE, null, selection, selArgs, null, null, DatabaseConstants.REPLY_DATE + " ASC");
|
Cursor c = dbRO.query(DatabaseConstants.REPLY_TABLE, null, selection, selArgs, null, null, DatabaseConstants.REPLY_DATE + " ASC");
|
||||||
|
@ -1442,7 +1457,7 @@ public class BlurDatabaseHelper {
|
||||||
return replies;
|
return replies;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void insertReplyPlaceholder(String storyId, @Nullable String userId, String commentUserId, String replyText) {
|
public void insertReplyPlaceholder(@Nullable String storyId, @Nullable String userId, @Nullable String commentUserId, @Nullable String replyText) {
|
||||||
// get a fresh copy of the comment so we can discover the ID
|
// get a fresh copy of the comment so we can discover the ID
|
||||||
Cursor c = dbRO.query(DatabaseConstants.COMMENT_TABLE,
|
Cursor c = dbRO.query(DatabaseConstants.COMMENT_TABLE,
|
||||||
null,
|
null,
|
||||||
|
@ -1467,14 +1482,14 @@ public class BlurDatabaseHelper {
|
||||||
synchronized (RW_MUTEX) {dbRW.insertWithOnConflict(DatabaseConstants.REPLY_TABLE, null, reply.getValues(), SQLiteDatabase.CONFLICT_REPLACE);}
|
synchronized (RW_MUTEX) {dbRW.insertWithOnConflict(DatabaseConstants.REPLY_TABLE, null, reply.getValues(), SQLiteDatabase.CONFLICT_REPLACE);}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void putStoryDismissed(String storyHash) {
|
public void putStoryDismissed(@Nullable String storyHash) {
|
||||||
ContentValues values = new ContentValues();
|
ContentValues values = new ContentValues();
|
||||||
values.put(DatabaseConstants.NOTIFY_DISMISS_STORY_HASH, storyHash);
|
values.put(DatabaseConstants.NOTIFY_DISMISS_STORY_HASH, storyHash);
|
||||||
values.put(DatabaseConstants.NOTIFY_DISMISS_TIME, Calendar.getInstance().getTime().getTime());
|
values.put(DatabaseConstants.NOTIFY_DISMISS_TIME, Calendar.getInstance().getTime().getTime());
|
||||||
synchronized (RW_MUTEX) {dbRW.insertOrThrow(DatabaseConstants.NOTIFY_DISMISS_TABLE, null, values);}
|
synchronized (RW_MUTEX) {dbRW.insertOrThrow(DatabaseConstants.NOTIFY_DISMISS_TABLE, null, values);}
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isStoryDismissed(String storyHash) {
|
public boolean isStoryDismissed(@Nullable String storyHash) {
|
||||||
String[] selArgs = new String[] {storyHash};
|
String[] selArgs = new String[] {storyHash};
|
||||||
String selection = DatabaseConstants.NOTIFY_DISMISS_STORY_HASH + " = ?";
|
String selection = DatabaseConstants.NOTIFY_DISMISS_STORY_HASH + " = ?";
|
||||||
Cursor c = dbRO.query(DatabaseConstants.NOTIFY_DISMISS_TABLE, null, selection, selArgs, null, null, null);
|
Cursor c = dbRO.query(DatabaseConstants.NOTIFY_DISMISS_TABLE, null, selection, selArgs, null, null, null);
|
||||||
|
@ -1494,7 +1509,7 @@ public class BlurDatabaseHelper {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void putFeedTagsExtSync(String feedId, Collection<String> tags) {
|
private void putFeedTagsExtSync(@Nullable String feedId, @NonNull Collection<String> tags) {
|
||||||
dbRW.delete(DatabaseConstants.FEED_TAGS_TABLE,
|
dbRW.delete(DatabaseConstants.FEED_TAGS_TABLE,
|
||||||
DatabaseConstants.FEED_TAGS_FEEDID + " = ?",
|
DatabaseConstants.FEED_TAGS_FEEDID + " = ?",
|
||||||
new String[]{feedId}
|
new String[]{feedId}
|
||||||
|
@ -1509,7 +1524,8 @@ public class BlurDatabaseHelper {
|
||||||
bulkInsertValuesExtSync(DatabaseConstants.FEED_TAGS_TABLE, valuesList);
|
bulkInsertValuesExtSync(DatabaseConstants.FEED_TAGS_TABLE, valuesList);
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<String> getTagsForFeed(String feedId) {
|
@NonNull
|
||||||
|
public List<String> getTagsForFeed(@Nullable String feedId) {
|
||||||
Cursor c = dbRO.query(DatabaseConstants.FEED_TAGS_TABLE,
|
Cursor c = dbRO.query(DatabaseConstants.FEED_TAGS_TABLE,
|
||||||
new String[]{DatabaseConstants.FEED_TAGS_TAG},
|
new String[]{DatabaseConstants.FEED_TAGS_TAG},
|
||||||
DatabaseConstants.FEED_TAGS_FEEDID + " = ?",
|
DatabaseConstants.FEED_TAGS_FEEDID + " = ?",
|
||||||
|
@ -1526,7 +1542,7 @@ public class BlurDatabaseHelper {
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void putFeedAuthorsExtSync(String feedId, Collection<String> authors) {
|
private void putFeedAuthorsExtSync(@Nullable String feedId, @NonNull Collection<String> authors) {
|
||||||
dbRW.delete(DatabaseConstants.FEED_AUTHORS_TABLE,
|
dbRW.delete(DatabaseConstants.FEED_AUTHORS_TABLE,
|
||||||
DatabaseConstants.FEED_AUTHORS_FEEDID + " = ?",
|
DatabaseConstants.FEED_AUTHORS_FEEDID + " = ?",
|
||||||
new String[]{feedId}
|
new String[]{feedId}
|
||||||
|
@ -1541,7 +1557,8 @@ public class BlurDatabaseHelper {
|
||||||
bulkInsertValuesExtSync(DatabaseConstants.FEED_AUTHORS_TABLE, valuesList);
|
bulkInsertValuesExtSync(DatabaseConstants.FEED_AUTHORS_TABLE, valuesList);
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<String> getAuthorsForFeed(String feedId) {
|
@NonNull
|
||||||
|
public List<String> getAuthorsForFeed(@Nullable String feedId) {
|
||||||
Cursor c = dbRO.query(DatabaseConstants.FEED_AUTHORS_TABLE,
|
Cursor c = dbRO.query(DatabaseConstants.FEED_AUTHORS_TABLE,
|
||||||
new String[]{DatabaseConstants.FEED_AUTHORS_AUTHOR},
|
new String[]{DatabaseConstants.FEED_AUTHORS_AUTHOR},
|
||||||
DatabaseConstants.FEED_AUTHORS_FEEDID + " = ?",
|
DatabaseConstants.FEED_AUTHORS_FEEDID + " = ?",
|
||||||
|
@ -1558,18 +1575,19 @@ public class BlurDatabaseHelper {
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void renameFeed(String feedId, String newFeedName) {
|
public void renameFeed(@Nullable String feedId, @Nullable String newFeedName) {
|
||||||
ContentValues values = new ContentValues();
|
ContentValues values = new ContentValues();
|
||||||
values.put(DatabaseConstants.FEED_TITLE, newFeedName);
|
values.put(DatabaseConstants.FEED_TITLE, newFeedName);
|
||||||
synchronized (RW_MUTEX) {dbRW.update(DatabaseConstants.FEED_TABLE, values, DatabaseConstants.FEED_ID + " = ?", new String[]{feedId});}
|
synchronized (RW_MUTEX) {dbRW.update(DatabaseConstants.FEED_TABLE, values, DatabaseConstants.FEED_ID + " = ?", new String[]{feedId});}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void closeQuietly(Cursor c) {
|
public static void closeQuietly(@Nullable Cursor c) {
|
||||||
if (c == null) return;
|
if (c == null) return;
|
||||||
try {c.close();} catch (Exception e) {
|
try {c.close();} catch (Exception e) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
private static String conjoinSelections(CharSequence... args) {
|
private static String conjoinSelections(CharSequence... args) {
|
||||||
StringBuilder s = null;
|
StringBuilder s = null;
|
||||||
for (CharSequence c : args) {
|
for (CharSequence c : args) {
|
||||||
|
@ -1589,7 +1607,8 @@ public class BlurDatabaseHelper {
|
||||||
* Invoke the rawQuery() method on our read-only SQLiteDatabase memeber using the provided CancellationSignal
|
* Invoke the rawQuery() method on our read-only SQLiteDatabase memeber using the provided CancellationSignal
|
||||||
* only if the device's platform provides support.
|
* only if the device's platform provides support.
|
||||||
*/
|
*/
|
||||||
private Cursor rawQuery(String sql, String[] selectionArgs, CancellationSignal cancellationSignal) {
|
@Nullable
|
||||||
|
private Cursor rawQuery(@NonNull String sql, @Nullable String[] selectionArgs, @Nullable CancellationSignal cancellationSignal) {
|
||||||
if (AppConstants.VERBOSE_LOG_DB) {
|
if (AppConstants.VERBOSE_LOG_DB) {
|
||||||
Log.d(this.getClass().getName(), String.format("DB rawQuery: '%s' with args: %s", sql, java.util.Arrays.toString(selectionArgs)));
|
Log.d(this.getClass().getName(), String.format("DB rawQuery: '%s' with args: %s", sql, java.util.Arrays.toString(selectionArgs)));
|
||||||
}
|
}
|
||||||
|
@ -1600,15 +1619,18 @@ public class BlurDatabaseHelper {
|
||||||
* Invoke the query() method on our read-only SQLiteDatabase memeber using the provided CancellationSignal
|
* Invoke the query() method on our read-only SQLiteDatabase memeber using the provided CancellationSignal
|
||||||
* only if the device's platform provides support.
|
* only if the device's platform provides support.
|
||||||
*/
|
*/
|
||||||
private Cursor query(boolean distinct, String table, String[] columns, String selection, String[] selectionArgs, String groupBy, String having, String orderBy, String limit, CancellationSignal cancellationSignal) {
|
@NonNull
|
||||||
|
private Cursor query(boolean distinct, @NonNull String table, @Nullable String[] columns, @Nullable String selection, @Nullable String[] selectionArgs, @Nullable String groupBy, @Nullable String having, @Nullable String orderBy, @Nullable String limit, @NonNull CancellationSignal cancellationSignal) {
|
||||||
return dbRO.query(distinct, table, columns, selection, selectionArgs, groupBy, having, orderBy, limit, cancellationSignal);
|
return dbRO.query(distinct, table, columns, selection, selectionArgs, groupBy, having, orderBy, limit, cancellationSignal);
|
||||||
}
|
}
|
||||||
|
|
||||||
public FeedSet feedSetFromFolderName(String folderName) {
|
@NonNull
|
||||||
|
public FeedSet feedSetFromFolderName(@NonNull String folderName) {
|
||||||
return FeedSet.folder(folderName, getFeedIdsRecursive(folderName));
|
return FeedSet.folder(folderName, getFeedIdsRecursive(folderName));
|
||||||
}
|
}
|
||||||
|
|
||||||
private Set<String> getFeedIdsRecursive(String folderName) {
|
@NonNull
|
||||||
|
private Set<String> getFeedIdsRecursive(@NonNull String folderName) {
|
||||||
Folder folder = getFolder(folderName);
|
Folder folder = getFolder(folderName);
|
||||||
if (folder == null) return emptySet();
|
if (folder == null) return emptySet();
|
||||||
Set<String> feedIds = new HashSet<>(folder.feedIds);
|
Set<String> feedIds = new HashSet<>(folder.feedIds);
|
||||||
|
|
|
@ -275,7 +275,7 @@ public class ItemSetFragment extends NbFragment {
|
||||||
}
|
}
|
||||||
|
|
||||||
protected FeedSet getFeedSet() {
|
protected FeedSet getFeedSet() {
|
||||||
return ((ItemsList) getActivity()).getFeedSet();
|
return ((ItemsList) requireActivity()).getFeedSet();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void hasUpdated() {
|
public void hasUpdated() {
|
||||||
|
@ -299,7 +299,7 @@ public class ItemSetFragment extends NbFragment {
|
||||||
ensureSufficientStories();
|
ensureSufficientStories();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void setCursor(Cursor cursor) {
|
private void setCursor(@Nullable Cursor cursor) {
|
||||||
if (cursor != null) {
|
if (cursor != null) {
|
||||||
if (!dbHelper.isFeedSetReady(getFeedSet())) {
|
if (!dbHelper.isFeedSetReady(getFeedSet())) {
|
||||||
// the DB hasn't caught up yet from the last story list; don't display stale stories.
|
// the DB hasn't caught up yet from the last story list; don't display stale stories.
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
package com.newsblur.fragment
|
package com.newsblur.fragment
|
||||||
|
|
||||||
import android.content.*
|
import android.content.Context
|
||||||
|
import android.content.DialogInterface
|
||||||
|
import android.content.Intent
|
||||||
import android.content.res.Configuration
|
import android.content.res.Configuration
|
||||||
import android.graphics.Color
|
import android.graphics.Color
|
||||||
import android.graphics.Typeface
|
import android.graphics.Typeface
|
||||||
|
@ -9,8 +11,12 @@ import android.net.Uri
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.text.TextUtils
|
import android.text.TextUtils
|
||||||
import android.util.Log
|
import android.util.Log
|
||||||
import android.view.*
|
import android.view.ContextMenu
|
||||||
import android.view.ContextMenu.ContextMenuInfo
|
import android.view.ContextMenu.ContextMenuInfo
|
||||||
|
import android.view.LayoutInflater
|
||||||
|
import android.view.MenuItem
|
||||||
|
import android.view.View
|
||||||
|
import android.view.ViewGroup
|
||||||
import android.webkit.WebView.HitTestResult
|
import android.webkit.WebView.HitTestResult
|
||||||
import androidx.appcompat.app.AlertDialog
|
import androidx.appcompat.app.AlertDialog
|
||||||
import androidx.appcompat.widget.PopupMenu
|
import androidx.appcompat.widget.PopupMenu
|
||||||
|
@ -38,8 +44,20 @@ import com.newsblur.service.NbSyncManager.UPDATE_SOCIAL
|
||||||
import com.newsblur.service.NbSyncManager.UPDATE_STORY
|
import com.newsblur.service.NbSyncManager.UPDATE_STORY
|
||||||
import com.newsblur.service.NbSyncManager.UPDATE_TEXT
|
import com.newsblur.service.NbSyncManager.UPDATE_TEXT
|
||||||
import com.newsblur.service.OriginalTextService
|
import com.newsblur.service.OriginalTextService
|
||||||
import com.newsblur.util.*
|
import com.newsblur.util.DefaultFeedView
|
||||||
|
import com.newsblur.util.FeedSet
|
||||||
|
import com.newsblur.util.FeedUtils
|
||||||
|
import com.newsblur.util.FileCache
|
||||||
|
import com.newsblur.util.Font
|
||||||
|
import com.newsblur.util.ImageLoader
|
||||||
|
import com.newsblur.util.MarkStoryReadBehavior
|
||||||
import com.newsblur.util.PrefConstants.ThemeValue
|
import com.newsblur.util.PrefConstants.ThemeValue
|
||||||
|
import com.newsblur.util.PrefsUtils
|
||||||
|
import com.newsblur.util.ReadingTextSize
|
||||||
|
import com.newsblur.util.StoryChangesState
|
||||||
|
import com.newsblur.util.StoryUtils
|
||||||
|
import com.newsblur.util.UIUtils
|
||||||
|
import com.newsblur.util.executeAsyncTask
|
||||||
import dagger.hilt.android.AndroidEntryPoint
|
import dagger.hilt.android.AndroidEntryPoint
|
||||||
import java.util.regex.Pattern
|
import java.util.regex.Pattern
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
@ -107,6 +125,7 @@ class ReadingItemFragment : NbFragment(), PopupMenu.OnMenuItemClickListener {
|
||||||
private lateinit var binding: FragmentReadingitemBinding
|
private lateinit var binding: FragmentReadingitemBinding
|
||||||
private lateinit var readingItemActionsBinding: ReadingItemActionsBinding
|
private lateinit var readingItemActionsBinding: ReadingItemActionsBinding
|
||||||
|
|
||||||
|
|
||||||
private lateinit var markStoryReadBehavior: MarkStoryReadBehavior
|
private lateinit var markStoryReadBehavior: MarkStoryReadBehavior
|
||||||
private var sampledQueue: SampledQueue? = null
|
private var sampledQueue: SampledQueue? = null
|
||||||
|
|
||||||
|
@ -148,13 +167,6 @@ class ReadingItemFragment : NbFragment(), PopupMenu.OnMenuItemClickListener {
|
||||||
super.onDestroyView()
|
super.onDestroyView()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onDestroy() {
|
|
||||||
binding.readingWebview.setOnTouchListener(null)
|
|
||||||
binding.root.setOnTouchListener(null)
|
|
||||||
requireActivity().window.decorView.setOnSystemUiVisibilityChangeListener(null)
|
|
||||||
super.onDestroy()
|
|
||||||
}
|
|
||||||
|
|
||||||
// WebViews don't automatically pause content like audio and video when they lose focus. Chain our own
|
// WebViews don't automatically pause content like audio and video when they lose focus. Chain our own
|
||||||
// state into the webview so it behaves.
|
// state into the webview so it behaves.
|
||||||
override fun onPause() {
|
override fun onPause() {
|
||||||
|
@ -169,8 +181,7 @@ class ReadingItemFragment : NbFragment(), PopupMenu.OnMenuItemClickListener {
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
|
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
|
||||||
val view = inflater.inflate(R.layout.fragment_readingitem, container, false)
|
binding = FragmentReadingitemBinding.inflate(inflater, container, false)
|
||||||
binding = FragmentReadingitemBinding.bind(view)
|
|
||||||
readingItemActionsBinding = ReadingItemActionsBinding.bind(binding.root)
|
readingItemActionsBinding = ReadingItemActionsBinding.bind(binding.root)
|
||||||
|
|
||||||
val readingActivity = requireActivity() as Reading
|
val readingActivity = requireActivity() as Reading
|
||||||
|
@ -194,7 +205,7 @@ class ReadingItemFragment : NbFragment(), PopupMenu.OnMenuItemClickListener {
|
||||||
|
|
||||||
binding.readingScrollview.registerScrollChangeListener(readingActivity)
|
binding.readingScrollview.registerScrollChangeListener(readingActivity)
|
||||||
|
|
||||||
return view
|
return binding.root
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||||
|
@ -423,8 +434,10 @@ class ReadingItemFragment : NbFragment(), PopupMenu.OnMenuItemClickListener {
|
||||||
}
|
}
|
||||||
R.id.menu_go_to_feed -> {
|
R.id.menu_go_to_feed -> {
|
||||||
val feed = dbHelper.getFeed(story!!.feedId)
|
val feed = dbHelper.getFeed(story!!.feedId)
|
||||||
val fs = FeedSet.singleFeed(feed.feedId)
|
feed?.let {
|
||||||
FeedItemsList.startActivity(requireContext(), fs, feed, null, null)
|
val fs = FeedSet.singleFeed(it.feedId)
|
||||||
|
FeedItemsList.startActivity(requireContext(), fs, it, null, null)
|
||||||
|
}
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
else -> {
|
else -> {
|
||||||
|
|
|
@ -56,7 +56,7 @@ class SetupCommentSectionTask(private val fragment: ReadingItemFragment, view: V
|
||||||
|
|
||||||
private fun doInBackground() {
|
private fun doInBackground() {
|
||||||
if (context == null || story == null || story.id.isNullOrEmpty()) return
|
if (context == null || story == null || story.id.isNullOrEmpty()) return
|
||||||
comments.addAll(fragment.dbHelper.getComments(story.id))
|
comments.addAll(fragment.dbHelper.getComments(story.id!!))
|
||||||
|
|
||||||
// users by whom we saw non-pseudo comments
|
// users by whom we saw non-pseudo comments
|
||||||
val commentingUserIds: MutableSet<String> = HashSet()
|
val commentingUserIds: MutableSet<String> = HashSet()
|
||||||
|
|
|
@ -144,7 +144,7 @@ public class NotificationUtils {
|
||||||
NotificationCompat.Builder nb = new NotificationCompat.Builder(context, context.getString(R.string.story_notification_channel_id))
|
NotificationCompat.Builder nb = new NotificationCompat.Builder(context, context.getString(R.string.story_notification_channel_id))
|
||||||
.setContentTitle(title.toString())
|
.setContentTitle(title.toString())
|
||||||
.setContentText(story.shortContent)
|
.setContentText(story.shortContent)
|
||||||
.setSmallIcon(R.drawable.logo_monochrome)
|
.setSmallIcon(R.drawable.ic_logo_monochrome)
|
||||||
.setContentIntent(pendingIntent)
|
.setContentIntent(pendingIntent)
|
||||||
.setDeleteIntent(dismissPendingIntent)
|
.setDeleteIntent(dismissPendingIntent)
|
||||||
.setAutoCancel(true)
|
.setAutoCancel(true)
|
||||||
|
|
|
@ -0,0 +1,68 @@
|
||||||
|
@file:Suppress("DEPRECATION")
|
||||||
|
|
||||||
|
package com.newsblur.util
|
||||||
|
|
||||||
|
import android.app.Activity
|
||||||
|
import android.os.Build
|
||||||
|
import com.newsblur.R
|
||||||
|
|
||||||
|
object PendingTransitionUtils {
|
||||||
|
|
||||||
|
@JvmStatic
|
||||||
|
fun overrideEnterTransition(activity: Activity) {
|
||||||
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE) {
|
||||||
|
activity.overrideActivityTransition(
|
||||||
|
Activity.OVERRIDE_TRANSITION_OPEN,
|
||||||
|
R.anim.slide_in_from_right,
|
||||||
|
R.anim.slide_out_to_left,
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
activity.overridePendingTransition(
|
||||||
|
R.anim.slide_in_from_right,
|
||||||
|
R.anim.slide_out_to_left,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@JvmStatic
|
||||||
|
fun overrideNoEnterTransition(activity: Activity) {
|
||||||
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE) {
|
||||||
|
activity.overrideActivityTransition(
|
||||||
|
Activity.OVERRIDE_TRANSITION_OPEN,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
activity.overridePendingTransition(
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@JvmStatic
|
||||||
|
fun overrideExitTransition(activity: Activity) {
|
||||||
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE) {
|
||||||
|
activity.overrideActivityTransition(
|
||||||
|
Activity.OVERRIDE_TRANSITION_CLOSE,
|
||||||
|
R.anim.slide_in_from_left,
|
||||||
|
R.anim.slide_out_to_right,
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
activity.overridePendingTransition(R.anim.slide_in_from_left, R.anim.slide_out_to_right)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@JvmStatic
|
||||||
|
fun overrideNoExitTransition(activity: Activity) {
|
||||||
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE) {
|
||||||
|
activity.overrideActivityTransition(
|
||||||
|
Activity.OVERRIDE_TRANSITION_CLOSE,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
activity.overridePendingTransition(0, 0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -9,6 +9,9 @@ import android.content.ContentValues;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.database.Cursor;
|
import android.database.Cursor;
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
import androidx.annotation.Nullable;
|
||||||
|
|
||||||
import java.io.Serializable;
|
import java.io.Serializable;
|
||||||
|
|
||||||
import com.newsblur.database.BlurDatabaseHelper;
|
import com.newsblur.database.BlurDatabaseHelper;
|
||||||
|
@ -88,21 +91,21 @@ public class ReadingAction implements Serializable {
|
||||||
return tried;
|
return tried;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static ReadingAction markStoryRead(String hash) {
|
public static ReadingAction markStoryRead(@Nullable String hash) {
|
||||||
ReadingAction ra = new ReadingAction();
|
ReadingAction ra = new ReadingAction();
|
||||||
ra.type = ActionType.MARK_READ;
|
ra.type = ActionType.MARK_READ;
|
||||||
ra.storyHash = hash;
|
ra.storyHash = hash;
|
||||||
return ra;
|
return ra;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static ReadingAction markStoryUnread(String hash) {
|
public static ReadingAction markStoryUnread(@Nullable String hash) {
|
||||||
ReadingAction ra = new ReadingAction();
|
ReadingAction ra = new ReadingAction();
|
||||||
ra.type = ActionType.MARK_UNREAD;
|
ra.type = ActionType.MARK_UNREAD;
|
||||||
ra.storyHash = hash;
|
ra.storyHash = hash;
|
||||||
return ra;
|
return ra;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static ReadingAction saveStory(String hash, List<String> userTags) {
|
public static ReadingAction saveStory(@Nullable String hash, @Nullable List<String> userTags) {
|
||||||
ReadingAction ra = new ReadingAction();
|
ReadingAction ra = new ReadingAction();
|
||||||
ra.type = ActionType.SAVE;
|
ra.type = ActionType.SAVE;
|
||||||
ra.storyHash = hash;
|
ra.storyHash = hash;
|
||||||
|
@ -114,14 +117,14 @@ public class ReadingAction implements Serializable {
|
||||||
return ra;
|
return ra;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static ReadingAction unsaveStory(String hash) {
|
public static ReadingAction unsaveStory(@Nullable String hash) {
|
||||||
ReadingAction ra = new ReadingAction();
|
ReadingAction ra = new ReadingAction();
|
||||||
ra.type = ActionType.UNSAVE;
|
ra.type = ActionType.UNSAVE;
|
||||||
ra.storyHash = hash;
|
ra.storyHash = hash;
|
||||||
return ra;
|
return ra;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static ReadingAction markFeedRead(FeedSet fs, Long olderThan, Long newerThan) {
|
public static ReadingAction markFeedRead(@NonNull FeedSet fs, @Nullable Long olderThan, @Nullable Long newerThan) {
|
||||||
ReadingAction ra = new ReadingAction();
|
ReadingAction ra = new ReadingAction();
|
||||||
ra.type = ActionType.MARK_READ;
|
ra.type = ActionType.MARK_READ;
|
||||||
ra.feedSet = fs;
|
ra.feedSet = fs;
|
||||||
|
@ -130,7 +133,7 @@ public class ReadingAction implements Serializable {
|
||||||
return ra;
|
return ra;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static ReadingAction shareStory(String hash, String storyId, String feedId, String sourceUserId, String commentReplyText) {
|
public static ReadingAction shareStory(@Nullable String hash, @Nullable String storyId, @Nullable String feedId, @Nullable String sourceUserId, @Nullable String commentReplyText) {
|
||||||
ReadingAction ra = new ReadingAction();
|
ReadingAction ra = new ReadingAction();
|
||||||
ra.type = ActionType.SHARE;
|
ra.type = ActionType.SHARE;
|
||||||
ra.storyHash = hash;
|
ra.storyHash = hash;
|
||||||
|
@ -141,7 +144,7 @@ public class ReadingAction implements Serializable {
|
||||||
return ra;
|
return ra;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static ReadingAction unshareStory(String hash, String storyId, String feedId) {
|
public static ReadingAction unshareStory(@Nullable String hash, @Nullable String storyId, @Nullable String feedId) {
|
||||||
ReadingAction ra = new ReadingAction();
|
ReadingAction ra = new ReadingAction();
|
||||||
ra.type = ActionType.UNSHARE;
|
ra.type = ActionType.UNSHARE;
|
||||||
ra.storyHash = hash;
|
ra.storyHash = hash;
|
||||||
|
@ -150,7 +153,7 @@ public class ReadingAction implements Serializable {
|
||||||
return ra;
|
return ra;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static ReadingAction likeComment(String storyId, String commentUserId, String feedId) {
|
public static ReadingAction likeComment(@Nullable String storyId, @Nullable String commentUserId, @Nullable String feedId) {
|
||||||
ReadingAction ra = new ReadingAction();
|
ReadingAction ra = new ReadingAction();
|
||||||
ra.type = ActionType.LIKE_COMMENT;
|
ra.type = ActionType.LIKE_COMMENT;
|
||||||
ra.storyId = storyId;
|
ra.storyId = storyId;
|
||||||
|
@ -159,7 +162,7 @@ public class ReadingAction implements Serializable {
|
||||||
return ra;
|
return ra;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static ReadingAction unlikeComment(String storyId, String commentUserId, String feedId) {
|
public static ReadingAction unlikeComment(@Nullable String storyId, @Nullable String commentUserId, @Nullable String feedId) {
|
||||||
ReadingAction ra = new ReadingAction();
|
ReadingAction ra = new ReadingAction();
|
||||||
ra.type = ActionType.UNLIKE_COMMENT;
|
ra.type = ActionType.UNLIKE_COMMENT;
|
||||||
ra.storyId = storyId;
|
ra.storyId = storyId;
|
||||||
|
@ -168,7 +171,7 @@ public class ReadingAction implements Serializable {
|
||||||
return ra;
|
return ra;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static ReadingAction replyToComment(String storyId, String feedId, String commentUserId, String commentReplyText) {
|
public static ReadingAction replyToComment(@Nullable String storyId, @Nullable String feedId, @Nullable String commentUserId, @Nullable String commentReplyText) {
|
||||||
ReadingAction ra = new ReadingAction();
|
ReadingAction ra = new ReadingAction();
|
||||||
ra.type = ActionType.REPLY;
|
ra.type = ActionType.REPLY;
|
||||||
ra.storyId = storyId;
|
ra.storyId = storyId;
|
||||||
|
@ -178,7 +181,7 @@ public class ReadingAction implements Serializable {
|
||||||
return ra;
|
return ra;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static ReadingAction updateReply(String storyId, String feedId, String commentUserId, String replyId, String commentReplyText) {
|
public static ReadingAction updateReply(@Nullable String storyId, @Nullable String feedId, @Nullable String commentUserId, @Nullable String replyId, @Nullable String commentReplyText) {
|
||||||
ReadingAction ra = new ReadingAction();
|
ReadingAction ra = new ReadingAction();
|
||||||
ra.type = ActionType.EDIT_REPLY;
|
ra.type = ActionType.EDIT_REPLY;
|
||||||
ra.storyId = storyId;
|
ra.storyId = storyId;
|
||||||
|
@ -189,7 +192,7 @@ public class ReadingAction implements Serializable {
|
||||||
return ra;
|
return ra;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static ReadingAction deleteReply(String storyId, String feedId, String commentUserId, String replyId) {
|
public static ReadingAction deleteReply(@Nullable String storyId, @Nullable String feedId, @Nullable String commentUserId, @Nullable String replyId) {
|
||||||
ReadingAction ra = new ReadingAction();
|
ReadingAction ra = new ReadingAction();
|
||||||
ra.type = ActionType.DELETE_REPLY;
|
ra.type = ActionType.DELETE_REPLY;
|
||||||
ra.storyId = storyId;
|
ra.storyId = storyId;
|
||||||
|
@ -199,7 +202,7 @@ public class ReadingAction implements Serializable {
|
||||||
return ra;
|
return ra;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static ReadingAction muteFeeds(Set<String> activeFeedIds, Set<String> modifiedFeedIds) {
|
public static ReadingAction muteFeeds(@NonNull Set<String> activeFeedIds, @NonNull Set<String> modifiedFeedIds) {
|
||||||
ReadingAction ra = new ReadingAction();
|
ReadingAction ra = new ReadingAction();
|
||||||
ra.type = ActionType.MUTE_FEEDS;
|
ra.type = ActionType.MUTE_FEEDS;
|
||||||
ra.activeFeedIds = activeFeedIds;
|
ra.activeFeedIds = activeFeedIds;
|
||||||
|
@ -207,7 +210,7 @@ public class ReadingAction implements Serializable {
|
||||||
return ra;
|
return ra;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static ReadingAction unmuteFeeds(Set<String> activeFeedIds, Set<String> modifiedFeedIds) {
|
public static ReadingAction unmuteFeeds(@NonNull Set<String> activeFeedIds, @NonNull Set<String> modifiedFeedIds) {
|
||||||
ReadingAction ra = new ReadingAction();
|
ReadingAction ra = new ReadingAction();
|
||||||
ra.type = ActionType.UNMUTE_FEEDS;
|
ra.type = ActionType.UNMUTE_FEEDS;
|
||||||
ra.activeFeedIds = activeFeedIds;
|
ra.activeFeedIds = activeFeedIds;
|
||||||
|
@ -215,7 +218,7 @@ public class ReadingAction implements Serializable {
|
||||||
return ra;
|
return ra;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static ReadingAction setNotify(String feedId, List<String> notifyTypes, String notifyFilter) {
|
public static ReadingAction setNotify(@Nullable String feedId, @Nullable List<String> notifyTypes, @Nullable String notifyFilter) {
|
||||||
ReadingAction ra = new ReadingAction();
|
ReadingAction ra = new ReadingAction();
|
||||||
ra.type = ActionType.SET_NOTIFY;
|
ra.type = ActionType.SET_NOTIFY;
|
||||||
ra.feedId = feedId;
|
ra.feedId = feedId;
|
||||||
|
@ -228,14 +231,14 @@ public class ReadingAction implements Serializable {
|
||||||
return ra;
|
return ra;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static ReadingAction instaFetch(String feedId) {
|
public static ReadingAction instaFetch(@Nullable String feedId) {
|
||||||
ReadingAction ra = new ReadingAction();
|
ReadingAction ra = new ReadingAction();
|
||||||
ra.type = ActionType.INSTA_FETCH;
|
ra.type = ActionType.INSTA_FETCH;
|
||||||
ra.feedId = feedId;
|
ra.feedId = feedId;
|
||||||
return ra;
|
return ra;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static ReadingAction updateIntel(String feedId, Classifier classifier, FeedSet fs) {
|
public static ReadingAction updateIntel(@Nullable String feedId, @Nullable Classifier classifier, @Nullable FeedSet fs) {
|
||||||
ReadingAction ra = new ReadingAction();
|
ReadingAction ra = new ReadingAction();
|
||||||
ra.type = ActionType.UPDATE_INTEL;
|
ra.type = ActionType.UPDATE_INTEL;
|
||||||
ra.feedId = feedId;
|
ra.feedId = feedId;
|
||||||
|
@ -244,7 +247,7 @@ public class ReadingAction implements Serializable {
|
||||||
return ra;
|
return ra;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static ReadingAction renameFeed(String feedId, String newFeedName) {
|
public static ReadingAction renameFeed(@Nullable String feedId, @Nullable String newFeedName) {
|
||||||
ReadingAction ra = new ReadingAction();
|
ReadingAction ra = new ReadingAction();
|
||||||
ra.type = ActionType.RENAME_FEED;
|
ra.type = ActionType.RENAME_FEED;
|
||||||
ra.feedId = feedId;
|
ra.feedId = feedId;
|
||||||
|
@ -265,7 +268,7 @@ public class ReadingAction implements Serializable {
|
||||||
return values;
|
return values;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static ReadingAction fromCursor(Cursor c) {
|
public static ReadingAction fromCursor(@NonNull Cursor c) {
|
||||||
long time = c.getLong(c.getColumnIndexOrThrow(DatabaseConstants.ACTION_TIME));
|
long time = c.getLong(c.getColumnIndexOrThrow(DatabaseConstants.ACTION_TIME));
|
||||||
int tried = c.getInt(c.getColumnIndexOrThrow(DatabaseConstants.ACTION_TRIED));
|
int tried = c.getInt(c.getColumnIndexOrThrow(DatabaseConstants.ACTION_TRIED));
|
||||||
String params = c.getString(c.getColumnIndexOrThrow(DatabaseConstants.ACTION_PARAMS));
|
String params = c.getString(c.getColumnIndexOrThrow(DatabaseConstants.ACTION_PARAMS));
|
||||||
|
@ -278,7 +281,7 @@ public class ReadingAction implements Serializable {
|
||||||
/**
|
/**
|
||||||
* Execute this action remotely via the API.
|
* Execute this action remotely via the API.
|
||||||
*/
|
*/
|
||||||
public NewsBlurResponse doRemote(APIManager apiManager, BlurDatabaseHelper dbHelper, StateFilter stateFilter) {
|
public NewsBlurResponse doRemote(@NonNull APIManager apiManager, @NonNull BlurDatabaseHelper dbHelper, @NonNull StateFilter stateFilter) {
|
||||||
// generic response to return
|
// generic response to return
|
||||||
NewsBlurResponse result = null;
|
NewsBlurResponse result = null;
|
||||||
// optional specific responses that are locally actionable
|
// optional specific responses that are locally actionable
|
||||||
|
@ -392,7 +395,7 @@ public class ReadingAction implements Serializable {
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
public int doLocal(Context context, BlurDatabaseHelper dbHelper) {
|
public int doLocal(@NonNull Context context, @NonNull BlurDatabaseHelper dbHelper) {
|
||||||
return doLocal(context, dbHelper, false);
|
return doLocal(context, dbHelper, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -403,7 +406,7 @@ public class ReadingAction implements Serializable {
|
||||||
*
|
*
|
||||||
* @return the union of update impact flags that resulted from this action.
|
* @return the union of update impact flags that resulted from this action.
|
||||||
*/
|
*/
|
||||||
public int doLocal(Context context, BlurDatabaseHelper dbHelper, boolean isFollowup) {
|
public int doLocal(@NonNull Context context, @NonNull BlurDatabaseHelper dbHelper, boolean isFollowup) {
|
||||||
String userId = PrefsUtils.getUserId(context);
|
String userId = PrefsUtils.getUserId(context);
|
||||||
int impact = 0;
|
int impact = 0;
|
||||||
switch (type) {
|
switch (type) {
|
||||||
|
|
|
@ -232,10 +232,10 @@ public class UIUtils {
|
||||||
public void run() {
|
public void run() {
|
||||||
Intent intent = activity.getIntent();
|
Intent intent = activity.getIntent();
|
||||||
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_NO_ANIMATION);
|
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_NO_ANIMATION);
|
||||||
activity.overridePendingTransition(0, 0);
|
PendingTransitionUtils.overrideNoExitTransition(activity);
|
||||||
activity.finish();
|
activity.finish();
|
||||||
|
|
||||||
activity.overridePendingTransition(0, 0);
|
PendingTransitionUtils.overrideNoEnterTransition(activity);
|
||||||
activity.startActivity(intent);
|
activity.startActivity(intent);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
|
@ -2,6 +2,7 @@ package com.newsblur.viewModel
|
||||||
|
|
||||||
import android.database.Cursor
|
import android.database.Cursor
|
||||||
import android.os.CancellationSignal
|
import android.os.CancellationSignal
|
||||||
|
import android.os.OperationCanceledException
|
||||||
import androidx.lifecycle.LiveData
|
import androidx.lifecycle.LiveData
|
||||||
import androidx.lifecycle.MutableLiveData
|
import androidx.lifecycle.MutableLiveData
|
||||||
import androidx.lifecycle.ViewModel
|
import androidx.lifecycle.ViewModel
|
||||||
|
@ -9,6 +10,7 @@ import androidx.lifecycle.viewModelScope
|
||||||
import com.newsblur.database.BlurDatabaseHelper
|
import com.newsblur.database.BlurDatabaseHelper
|
||||||
import com.newsblur.util.CursorFilters
|
import com.newsblur.util.CursorFilters
|
||||||
import com.newsblur.util.FeedSet
|
import com.newsblur.util.FeedSet
|
||||||
|
import com.newsblur.util.Log
|
||||||
import dagger.hilt.android.lifecycle.HiltViewModel
|
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
|
@ -24,8 +26,12 @@ class StoriesViewModel
|
||||||
|
|
||||||
fun getActiveStories(fs: FeedSet, cursorFilters: CursorFilters) {
|
fun getActiveStories(fs: FeedSet, cursorFilters: CursorFilters) {
|
||||||
viewModelScope.launch(Dispatchers.IO) {
|
viewModelScope.launch(Dispatchers.IO) {
|
||||||
dbHelper.getActiveStoriesCursor(fs, cursorFilters, cancellationSignal).let {
|
try {
|
||||||
_activeStoriesLiveData.postValue(it)
|
dbHelper.getActiveStoriesCursor(fs, cursorFilters, cancellationSignal).let {
|
||||||
|
_activeStoriesLiveData.postValue(it)
|
||||||
|
}
|
||||||
|
} catch (e: OperationCanceledException) {
|
||||||
|
Log.e(this.javaClass.name, "Caught ${e.javaClass.name} in getActiveStories.")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,33 @@
|
||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:aapt="http://schemas.android.com/aapt"
|
||||||
|
android:width="48dp"
|
||||||
|
android:height="48dp"
|
||||||
|
android:viewportWidth="432"
|
||||||
|
android:viewportHeight="432">
|
||||||
|
|
||||||
|
<path android:pathData="M0,0h432v432h-432z">
|
||||||
|
|
||||||
|
<aapt:attr name="android:fillColor">
|
||||||
|
|
||||||
|
<gradient
|
||||||
|
android:endX="216"
|
||||||
|
android:endY="432"
|
||||||
|
android:startX="216"
|
||||||
|
android:startY="0"
|
||||||
|
android:type="linear">
|
||||||
|
|
||||||
|
<item
|
||||||
|
android:color="#FFE6A33F"
|
||||||
|
android:offset="0" />
|
||||||
|
|
||||||
|
<item
|
||||||
|
android:color="#FFC35F28"
|
||||||
|
android:offset="1" />
|
||||||
|
|
||||||
|
</gradient>
|
||||||
|
|
||||||
|
</aapt:attr>
|
||||||
|
|
||||||
|
</path>
|
||||||
|
|
||||||
|
</vector>
|
|
@ -0,0 +1,51 @@
|
||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:aapt="http://schemas.android.com/aapt"
|
||||||
|
android:width="48dp"
|
||||||
|
android:height="48dp"
|
||||||
|
android:viewportWidth="432"
|
||||||
|
android:viewportHeight="432">
|
||||||
|
|
||||||
|
<path
|
||||||
|
android:fillType="evenOdd"
|
||||||
|
android:pathData="M237.9,135.78L243.89,148.68L257.15,143.83C270.23,151.34 281.11,162.28 288.54,175.42L283.74,188.53L296.37,194.4C298.31,201.45 299.34,208.88 299.34,216.55C299.34,224.35 298.27,231.9 296.27,239.07L283.74,244.89L288.46,257.8C280.97,271 270,281.97 256.8,289.46L243.89,284.74L238.07,297.27C230.9,299.27 223.35,300.34 215.55,300.34C207.88,300.34 200.45,299.31 193.39,297.37L187.53,284.74L174.42,289.54C161.28,282.11 150.34,271.23 142.83,258.15L147.68,244.89L134.78,238.9C132.8,231.78 131.75,224.28 131.75,216.55C131.75,208.94 132.77,201.58 134.67,194.57L147.68,188.53L142.75,175.07C150.2,162.05 161.04,151.2 174.07,143.75L187.53,148.68L193.57,135.67C200.57,133.77 207.94,132.75 215.55,132.75C223.28,132.75 230.78,133.8 237.9,135.78ZM263.87,141.37C274.76,148.4 284.04,157.73 291.01,168.67L295.09,157.49C289.4,149.84 282.62,143.03 275,137.3L263.87,141.37ZM302.88,197.42L313.65,202.42C314.31,207.03 314.65,211.75 314.65,216.55C314.65,221.46 314.29,226.3 313.6,231.02L302.8,236.04C304.2,229.76 304.94,223.24 304.94,216.55C304.94,209.98 304.23,203.58 302.88,197.42ZM321.27,216.55C321.27,220.32 321.07,224.06 320.68,227.74L330.49,223.18C330.61,220.99 330.68,218.77 330.68,216.55C330.68,214.43 330.62,212.33 330.51,210.25L320.71,205.7C321.08,209.27 321.27,212.88 321.27,216.55ZM304.48,131.84L306.72,125.7L300.59,127.94C301.2,128.52 301.8,129.11 302.39,129.7C303.1,130.41 303.79,131.12 304.48,131.84ZM218.39,93.76L215.71,88L213.04,93.75C213.87,93.74 214.71,93.73 215.55,93.73C216.49,93.73 217.44,93.74 218.39,93.76ZM209.4,101.58C211.44,101.47 213.49,101.42 215.55,101.42C217.72,101.42 219.88,101.47 222.02,101.6L226.57,111.4C222.95,111.02 219.27,110.82 215.55,110.82C211.94,110.82 208.38,111.01 204.86,111.36L209.4,101.58ZM138.75,130.84L148.89,134.54C143.27,139.12 138.12,144.27 133.54,149.89L129.84,139.75C132.65,136.62 135.62,133.65 138.75,130.84ZM130.59,127.85L124.7,125.7L126.85,131.59C127.46,130.95 128.08,130.32 128.7,129.7C129.32,129.08 129.96,128.46 130.59,127.85ZM292.43,130.92C295.62,133.78 298.64,136.82 301.49,140.01L297.77,150.17C293.17,144.47 287.98,139.26 282.31,134.63L292.43,130.92ZM338.33,213.88C338.35,214.77 338.36,215.66 338.36,216.55C338.36,217.55 338.35,218.54 338.33,219.54L344.42,216.71L338.33,213.88ZM301.4,293.17L297.69,283.03C293.03,288.78 287.78,294.03 282.03,298.69L292.17,302.4C295.42,299.5 298.5,296.42 301.4,293.17ZM300.35,305.39C301.03,304.73 301.71,304.07 302.39,303.39C303.07,302.71 303.73,302.03 304.39,301.35L306.72,307.72L300.35,305.39ZM222.18,331.49L226.74,321.68C223.06,322.07 219.32,322.27 215.55,322.27C211.88,322.27 208.27,322.08 204.7,321.71L209.25,331.51C211.33,331.62 213.43,331.68 215.55,331.68C217.77,331.68 219.99,331.61 222.18,331.49ZM212.88,339.33C213.77,339.35 214.66,339.36 215.55,339.36C216.55,339.36 217.54,339.35 218.54,339.33L215.71,345.42L212.88,339.33ZM139.01,302.49L149.17,298.77C143.47,294.17 138.26,288.98 133.63,283.31L129.92,293.43C132.78,296.62 135.82,299.64 139.01,302.49ZM100.6,223.02C100.48,220.88 100.42,218.72 100.42,216.55C100.42,214.49 100.47,212.44 100.58,210.4L110.36,205.86C110.01,209.38 109.82,212.94 109.82,216.55C109.82,220.27 110.02,223.95 110.4,227.58L100.6,223.02ZM92.75,214.04L87,216.71L92.76,219.39C92.74,218.44 92.73,217.49 92.73,216.54C92.73,215.71 92.74,214.87 92.75,214.04ZM126.94,301.59C127.52,302.2 128.11,302.8 128.7,303.39C129.41,304.1 130.12,304.79 130.84,305.48L124.7,307.72L126.94,301.59ZM290.93,264.54L295.01,275.7C289.26,283.41 282.41,290.26 274.7,296.01L263.54,291.93C274.54,284.9 283.9,275.54 290.93,264.54ZM235.04,303.79L230.02,314.6C225.3,315.29 220.46,315.65 215.55,315.65C210.75,315.65 206.03,315.31 201.42,314.65L196.41,303.88C202.58,305.22 208.98,305.94 215.55,305.94C222.24,305.94 228.76,305.2 235.04,303.79ZM167.67,292.01L156.5,296.09C148.84,290.4 142.03,283.62 136.3,276L140.37,264.87C147.4,275.76 156.73,285.04 167.67,292.01ZM128.26,235.87L117.47,230.86C116.79,226.19 116.44,221.41 116.44,216.55C116.44,211.81 116.78,207.15 117.42,202.58L128.18,197.59C126.85,203.7 126.16,210.04 126.16,216.55C126.16,223.18 126.88,229.65 128.26,235.87ZM167.34,141.29L156.2,137.22C148.63,142.9 141.9,149.63 136.22,157.2L140.29,168.34C147.26,157.51 156.51,148.26 167.34,141.29ZM215.55,117.44C210.81,117.44 206.15,117.78 201.58,118.42L196.59,129.18C202.7,127.85 209.04,127.16 215.55,127.16C222.18,127.16 228.65,127.88 234.87,129.26L229.86,118.47C225.19,117.79 220.41,117.44 215.55,117.44ZM215.55,283.71C178.51,283.71 148.38,253.58 148.38,216.55C148.38,179.51 178.51,149.38 215.55,149.38C252.58,149.38 282.71,179.51 282.71,216.55C282.71,253.58 252.58,283.71 215.55,283.71ZM215.55,160.48C184.63,160.48 159.48,185.63 159.48,216.55C159.48,247.46 184.63,272.61 215.55,272.61C246.46,272.61 271.61,247.46 271.61,216.55C271.61,185.63 246.46,160.48 215.55,160.48Z">
|
||||||
|
|
||||||
|
<aapt:attr name="android:fillColor">
|
||||||
|
|
||||||
|
<gradient
|
||||||
|
android:endX="124.5"
|
||||||
|
android:endY="52"
|
||||||
|
android:startX="252.5"
|
||||||
|
android:startY="276.5"
|
||||||
|
android:type="linear">
|
||||||
|
|
||||||
|
<item
|
||||||
|
android:color="#FFEEDA5C"
|
||||||
|
android:offset="0" />
|
||||||
|
|
||||||
|
<item
|
||||||
|
android:color="#FFF0DE81"
|
||||||
|
android:offset="0.23" />
|
||||||
|
|
||||||
|
<item
|
||||||
|
android:color="#FFF2E19C"
|
||||||
|
android:offset="0.45" />
|
||||||
|
|
||||||
|
<item
|
||||||
|
android:color="#FFF3E3B0"
|
||||||
|
android:offset="0.66" />
|
||||||
|
|
||||||
|
<item
|
||||||
|
android:color="#FFF4E5BC"
|
||||||
|
android:offset="0.85" />
|
||||||
|
|
||||||
|
<item
|
||||||
|
android:color="#FFF3E3AA"
|
||||||
|
android:offset="1" />
|
||||||
|
|
||||||
|
</gradient>
|
||||||
|
|
||||||
|
</aapt:attr>
|
||||||
|
|
||||||
|
</path>
|
||||||
|
|
||||||
|
</vector>
|
|
@ -0,0 +1,10 @@
|
||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="48dp"
|
||||||
|
android:height="48dp"
|
||||||
|
android:viewportWidth="432"
|
||||||
|
android:viewportHeight="432">
|
||||||
|
<path
|
||||||
|
android:fillColor="#ffffff"
|
||||||
|
android:fillType="evenOdd"
|
||||||
|
android:pathData="M237.9,135.78L243.89,148.68L257.15,143.83C270.23,151.34 281.11,162.28 288.54,175.42L283.74,188.53L296.37,194.4C298.31,201.45 299.34,208.88 299.34,216.55C299.34,224.35 298.27,231.9 296.27,239.07L283.74,244.89L288.46,257.8C280.97,271 270,281.97 256.8,289.46L243.89,284.74L238.07,297.27C230.9,299.27 223.35,300.34 215.55,300.34C207.88,300.34 200.45,299.31 193.39,297.37L187.53,284.74L174.42,289.54C161.28,282.11 150.34,271.23 142.83,258.15L147.68,244.89L134.78,238.9C132.8,231.78 131.75,224.28 131.75,216.55C131.75,208.94 132.77,201.58 134.67,194.57L147.68,188.53L142.75,175.07C150.2,162.05 161.04,151.2 174.07,143.75L187.53,148.68L193.57,135.67C200.57,133.77 207.94,132.75 215.55,132.75C223.28,132.75 230.78,133.8 237.9,135.78ZM263.87,141.37C274.76,148.4 284.04,157.73 291.01,168.67L295.09,157.49C289.4,149.84 282.62,143.03 275,137.3L263.87,141.37ZM302.88,197.42L313.65,202.42C314.31,207.03 314.65,211.75 314.65,216.55C314.65,221.46 314.29,226.3 313.6,231.02L302.8,236.04C304.2,229.76 304.94,223.24 304.94,216.55C304.94,209.98 304.23,203.58 302.88,197.42ZM321.27,216.55C321.27,220.32 321.07,224.06 320.68,227.74L330.49,223.18C330.61,220.99 330.68,218.77 330.68,216.55C330.68,214.43 330.62,212.33 330.51,210.25L320.71,205.7C321.08,209.27 321.27,212.88 321.27,216.55ZM304.48,131.84L306.72,125.7L300.59,127.94C301.2,128.52 301.8,129.11 302.39,129.7C303.1,130.41 303.79,131.12 304.48,131.84ZM218.39,93.76L215.71,88L213.04,93.75C213.87,93.74 214.71,93.73 215.55,93.73C216.49,93.73 217.44,93.74 218.39,93.76ZM209.4,101.58C211.44,101.47 213.49,101.42 215.55,101.42C217.72,101.42 219.88,101.47 222.02,101.6L226.57,111.4C222.95,111.02 219.27,110.82 215.55,110.82C211.94,110.82 208.38,111.01 204.86,111.36L209.4,101.58ZM138.75,130.84L148.89,134.54C143.27,139.12 138.12,144.27 133.54,149.89L129.84,139.75C132.65,136.62 135.62,133.65 138.75,130.84ZM130.59,127.85L124.7,125.7L126.85,131.59C127.46,130.95 128.08,130.32 128.7,129.7C129.32,129.08 129.96,128.46 130.59,127.85ZM292.43,130.92C295.62,133.78 298.64,136.82 301.49,140.01L297.77,150.17C293.17,144.47 287.98,139.26 282.31,134.63L292.43,130.92ZM338.33,213.88C338.35,214.77 338.36,215.66 338.36,216.55C338.36,217.55 338.35,218.54 338.33,219.54L344.42,216.71L338.33,213.88ZM301.4,293.17L297.69,283.03C293.03,288.78 287.78,294.03 282.03,298.69L292.17,302.4C295.42,299.5 298.5,296.42 301.4,293.17ZM300.35,305.39C301.03,304.73 301.71,304.07 302.39,303.39C303.07,302.71 303.73,302.03 304.39,301.35L306.72,307.72L300.35,305.39ZM222.18,331.49L226.74,321.68C223.06,322.07 219.32,322.27 215.55,322.27C211.88,322.27 208.27,322.08 204.7,321.71L209.25,331.51C211.33,331.62 213.43,331.68 215.55,331.68C217.77,331.68 219.99,331.61 222.18,331.49ZM212.88,339.33C213.77,339.35 214.66,339.36 215.55,339.36C216.55,339.36 217.54,339.35 218.54,339.33L215.71,345.42L212.88,339.33ZM139.01,302.49L149.17,298.77C143.47,294.17 138.26,288.98 133.63,283.31L129.92,293.43C132.78,296.62 135.82,299.64 139.01,302.49ZM100.6,223.02C100.48,220.88 100.42,218.72 100.42,216.55C100.42,214.49 100.47,212.44 100.58,210.4L110.36,205.86C110.01,209.38 109.82,212.94 109.82,216.55C109.82,220.27 110.02,223.95 110.4,227.58L100.6,223.02ZM92.75,214.04L87,216.71L92.76,219.39C92.74,218.44 92.73,217.49 92.73,216.54C92.73,215.71 92.74,214.87 92.75,214.04ZM126.94,301.59C127.52,302.2 128.11,302.8 128.7,303.39C129.41,304.1 130.12,304.79 130.84,305.48L124.7,307.72L126.94,301.59ZM290.93,264.54L295.01,275.7C289.26,283.41 282.41,290.26 274.7,296.01L263.54,291.93C274.54,284.9 283.9,275.54 290.93,264.54ZM235.04,303.79L230.02,314.6C225.3,315.29 220.46,315.65 215.55,315.65C210.75,315.65 206.03,315.31 201.42,314.65L196.41,303.88C202.58,305.22 208.98,305.94 215.55,305.94C222.24,305.94 228.76,305.2 235.04,303.79ZM167.67,292.01L156.5,296.09C148.84,290.4 142.03,283.62 136.3,276L140.37,264.87C147.4,275.76 156.73,285.04 167.67,292.01ZM128.26,235.87L117.47,230.86C116.79,226.19 116.44,221.41 116.44,216.55C116.44,211.81 116.78,207.15 117.42,202.58L128.18,197.59C126.85,203.7 126.16,210.04 126.16,216.55C126.16,223.18 126.88,229.65 128.26,235.87ZM167.34,141.29L156.2,137.22C148.63,142.9 141.9,149.63 136.22,157.2L140.29,168.34C147.26,157.51 156.51,148.26 167.34,141.29ZM215.55,117.44C210.81,117.44 206.15,117.78 201.58,118.42L196.59,129.18C202.7,127.85 209.04,127.16 215.55,127.16C222.18,127.16 228.65,127.88 234.87,129.26L229.86,118.47C225.19,117.79 220.41,117.44 215.55,117.44ZM215.55,283.71C178.51,283.71 148.38,253.58 148.38,216.55C148.38,179.51 178.51,149.38 215.55,149.38C252.58,149.38 282.71,179.51 282.71,216.55C282.71,253.58 252.58,283.71 215.55,283.71ZM215.55,160.48C184.63,160.48 159.48,185.63 159.48,216.55C159.48,247.46 184.63,272.61 215.55,272.61C246.46,272.61 271.61,247.46 271.61,216.55C271.61,185.63 246.46,160.48 215.55,160.48Z" />
|
||||||
|
</vector>
|
|
@ -0,0 +1,6 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<background android:drawable="@drawable/ic_logo_background" />
|
||||||
|
<foreground android:drawable="@drawable/ic_logo_foreground" />
|
||||||
|
<monochrome android:drawable="@drawable/ic_logo_monochrome" />
|
||||||
|
</adaptive-icon>
|
|
@ -0,0 +1,5 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<background android:drawable="@drawable/ic_logo_background" />
|
||||||
|
<foreground android:drawable="@drawable/ic_logo_foreground" />
|
||||||
|
</adaptive-icon>
|
|
@ -745,7 +745,9 @@
|
||||||
|
|
||||||
<string name="notification_permissions_context">Permissions is required for posting notifications</string>
|
<string name="notification_permissions_context">Permissions is required for posting notifications</string>
|
||||||
<string name="notification_permissions_rationale">Notifications permission must be added manually in the app\'s settings before trying again to enable notifications</string>
|
<string name="notification_permissions_rationale">Notifications permission must be added manually in the app\'s settings before trying again to enable notifications</string>
|
||||||
|
|
||||||
|
<string name="write_storage_permission_opml">Write storage permission is required for OPML export</string>
|
||||||
|
|
||||||
<string name="story_saved">Story marked as saved</string>
|
<string name="story_saved">Story marked as saved</string>
|
||||||
<string name="story_unsaved">Story marked as unsaved</string>
|
<string name="story_unsaved">Story marked as unsaved</string>
|
||||||
<string name="story_read">Story marked as read</string>
|
<string name="story_read">Story marked as read</string>
|
||||||
|
|
|
@ -3,12 +3,12 @@ import org.gradle.api.JavaVersion
|
||||||
object Config {
|
object Config {
|
||||||
|
|
||||||
const val compileSdk = 34
|
const val compileSdk = 34
|
||||||
const val minSdk = 24
|
const val minSdk = 26
|
||||||
const val targetSdk = 34
|
const val targetSdk = 34
|
||||||
const val versionCode = 220
|
const val versionCode = 230
|
||||||
const val versionName = "13.2.4"
|
const val versionName = "13.3.2"
|
||||||
|
|
||||||
const val androidTestInstrumentation = "androidx.test.runner.AndroidJUnitRunner"
|
const val androidTestInstrumentation = "androidx.test.runner.AndroidJUnitRunner"
|
||||||
|
|
||||||
val javaVersion = JavaVersion.VERSION_17
|
val javaVersion = JavaVersion.VERSION_21
|
||||||
}
|
}
|
|
@ -6,7 +6,6 @@ object Dependencies {
|
||||||
const val okHttp = "com.squareup.okhttp3:okhttp:${Version.okHttp}"
|
const val okHttp = "com.squareup.okhttp3:okhttp:${Version.okHttp}"
|
||||||
const val gson = "com.google.code.gson:gson:${Version.gson}"
|
const val gson = "com.google.code.gson:gson:${Version.gson}"
|
||||||
const val billing = "com.android.billingclient:billing:${Version.billing}"
|
const val billing = "com.android.billingclient:billing:${Version.billing}"
|
||||||
const val playCore = "com.google.android.play:core:${Version.playCore}"
|
|
||||||
const val material = "com.google.android.material:material:${Version.material}"
|
const val material = "com.google.android.material:material:${Version.material}"
|
||||||
const val preference = "androidx.preference:preference-ktx:${Version.preference}"
|
const val preference = "androidx.preference:preference-ktx:${Version.preference}"
|
||||||
const val browser = "androidx.browser:browser:${Version.browser}"
|
const val browser = "androidx.browser:browser:${Version.browser}"
|
||||||
|
@ -16,6 +15,7 @@ object Dependencies {
|
||||||
const val hiltAndroid = "com.google.dagger:hilt-android:${Version.hilt}"
|
const val hiltAndroid = "com.google.dagger:hilt-android:${Version.hilt}"
|
||||||
const val hiltCompiler = "com.google.dagger:hilt-compiler:${Version.hilt}"
|
const val hiltCompiler = "com.google.dagger:hilt-compiler:${Version.hilt}"
|
||||||
const val profileInstaller = "androidx.profileinstaller:profileinstaller:${Version.profileInstaller}"
|
const val profileInstaller = "androidx.profileinstaller:profileinstaller:${Version.profileInstaller}"
|
||||||
|
const val playReview = "com.google.android.play:review:${Version.playReview}"
|
||||||
|
|
||||||
// test
|
// test
|
||||||
const val junit = "junit:junit:${Version.junit}"
|
const val junit = "junit:junit:${Version.junit}"
|
||||||
|
|
|
@ -1,35 +1,35 @@
|
||||||
object Version {
|
object Version {
|
||||||
|
|
||||||
const val android = "8.3.1"
|
const val android = "8.5.0"
|
||||||
|
|
||||||
const val kotlin = "1.9.23"
|
const val kotlin = "1.9.23"
|
||||||
|
|
||||||
const val fragment = "1.6.2"
|
const val fragment = "1.8.0"
|
||||||
const val recyclerView = "1.3.2"
|
const val recyclerView = "1.3.2"
|
||||||
const val swipeRefreshLayout = "1.1.0"
|
const val swipeRefreshLayout = "1.1.0"
|
||||||
|
|
||||||
const val okHttp = "4.12.0"
|
const val okHttp = "4.12.0"
|
||||||
const val gson = "2.10.1"
|
const val gson = "2.11.0"
|
||||||
const val billing = "6.2.0"
|
const val billing = "6.2.0"
|
||||||
const val playCore = "1.10.3"
|
const val playReview = "2.0.1"
|
||||||
const val material = "1.11.0"
|
const val material = "1.12.0"
|
||||||
|
|
||||||
const val preference = "1.2.1"
|
const val preference = "1.2.1"
|
||||||
const val browser = "1.8.0"
|
const val browser = "1.8.0"
|
||||||
const val lifecycle = "2.7.0"
|
const val lifecycle = "2.8.2"
|
||||||
const val splashScreen = "1.0.1"
|
const val splashScreen = "1.0.1"
|
||||||
|
|
||||||
const val hilt = "2.51"
|
const val hilt = "2.51.1"
|
||||||
const val profileInstaller = "1.3.1"
|
const val profileInstaller = "1.3.1"
|
||||||
|
|
||||||
const val junit = "4.13.2"
|
const val junit = "4.13.2"
|
||||||
const val mockk = "1.13.10"
|
const val mockk = "1.13.11"
|
||||||
|
|
||||||
const val junitExt = "1.1.5"
|
const val junitExt = "1.1.5"
|
||||||
const val espresso = "3.5.1"
|
const val espresso = "3.5.1"
|
||||||
|
|
||||||
const val uiAutomator = "2.3.0"
|
const val uiAutomator = "2.3.0"
|
||||||
const val benchmarkMacroJunit4 = "1.2.3"
|
const val benchmarkMacroJunit4 = "1.2.4"
|
||||||
|
|
||||||
const val benManesVersions = "0.51.0"
|
const val benManesVersions = "0.51.0"
|
||||||
}
|
}
|
Loading…
Add table
Reference in a new issue