├── .github
└── workflows
│ └── main.yml
├── .gitignore
├── CONTRIBUTORS
├── LICENSE
├── Readme.md
├── app
├── .gitignore
├── build.gradle
├── lint.xml
├── proguard-rules.pro
└── src
│ ├── main
│ ├── AndroidManifest.xml
│ ├── assets
│ │ └── about
│ │ │ └── about.html
│ ├── ic_launcher-web.png
│ ├── java
│ │ └── me
│ │ │ └── murks
│ │ │ └── feedwatcher
│ │ │ ├── AndroidApplication.kt
│ │ │ ├── AndroidEnvironment.kt
│ │ │ ├── AndroidSettings.kt
│ │ │ ├── Constants.kt
│ │ │ ├── Either.kt
│ │ │ ├── Environment.kt
│ │ │ ├── FeedItems.kt
│ │ │ ├── FeedWatcherApp.kt
│ │ │ ├── FeedwatcherLog.kt
│ │ │ ├── HtmlTags.kt
│ │ │ ├── Jobs.kt
│ │ │ ├── Lookup.kt
│ │ │ ├── Notifications.kt
│ │ │ ├── ResourceTracker.kt
│ │ │ ├── Settings.kt
│ │ │ ├── Texts.kt
│ │ │ ├── activities
│ │ │ ├── AboutActivity.kt
│ │ │ ├── Activities.kt
│ │ │ ├── DateTimePickerDialogFragment.kt
│ │ │ ├── Dialogs.kt
│ │ │ ├── FeedActivity.kt
│ │ │ ├── FeedExportActivity.kt
│ │ │ ├── FeedExportRecyclerViewAdapter.kt
│ │ │ ├── FeedImportActivity.kt
│ │ │ ├── FeedImportFragment.kt
│ │ │ ├── FeedImportRecyclerViewAdapter.kt
│ │ │ ├── FeedUiContainer.kt
│ │ │ ├── FeedWatcherAsyncLoadingFragment.kt
│ │ │ ├── FeedWatcherBaseActivity.kt
│ │ │ ├── FeedWatcherBaseFragment.kt
│ │ │ ├── FeedsFragment.kt
│ │ │ ├── FeedsRecyclerViewAdapter.kt
│ │ │ ├── FilterRecyclerViewAdapter.kt
│ │ │ ├── FilterUiModel.kt
│ │ │ ├── Formatter.kt
│ │ │ ├── ListRecyclerViewAdapter.kt
│ │ │ ├── OverviewActivity.kt
│ │ │ ├── PreferencesActivity.kt
│ │ │ ├── PreferencesFragment.kt
│ │ │ ├── QueriesFragment.kt
│ │ │ ├── QueryActivity.kt
│ │ │ ├── QueryRecyclerViewAdapter.kt
│ │ │ ├── ResultActivity.kt
│ │ │ ├── ResultsFragment.kt
│ │ │ ├── ResultsRecyclerViewAdapter.kt
│ │ │ └── SelectableRecyclerViewAdapter.kt
│ │ │ ├── data
│ │ │ ├── AddFeeds.kt
│ │ │ ├── ClearResults.kt
│ │ │ ├── Cursors.kt
│ │ │ ├── DataStore.kt
│ │ │ ├── DeleteFeed.kt
│ │ │ ├── DeleteResult.kt
│ │ │ ├── FeedWatcherSchema.java
│ │ │ ├── RecordScan.kt
│ │ │ ├── UnitOfWork.kt
│ │ │ └── UpdateResult.kt
│ │ │ ├── model
│ │ │ ├── ContainsFilter.kt
│ │ │ ├── Feed.kt
│ │ │ ├── FeedFilter.kt
│ │ │ ├── Filter.kt
│ │ │ ├── FilterFactory.kt
│ │ │ ├── FilterParameter.kt
│ │ │ ├── FilterType.kt
│ │ │ ├── FilterTypeCallback.kt
│ │ │ ├── NewEntryFilter.kt
│ │ │ ├── Query.kt
│ │ │ ├── Result.kt
│ │ │ ├── Scan.kt
│ │ │ └── ScanInterval.kt
│ │ │ └── tasks
│ │ │ ├── ActionTask.kt
│ │ │ ├── FeedsFilter.kt
│ │ │ ├── FilterFeedsJob.kt
│ │ │ ├── StreamingTask.kt
│ │ │ ├── TaskListener.kt
│ │ │ └── Tasks.kt
│ └── res
│ │ ├── drawable
│ │ ├── ic_feed_icon.xml
│ │ ├── ic_feedwatcher_notification.xml
│ │ ├── ic_launcher_background.xml
│ │ ├── ic_launcher_foreground.xml
│ │ └── ic_menu.xml
│ │ ├── layout
│ │ ├── activity_about.xml
│ │ ├── activity_feed.xml
│ │ ├── activity_feed_export.xml
│ │ ├── activity_feed_export_list_item.xml
│ │ ├── activity_feed_import.xml
│ │ ├── activity_feed_import_list_item.xml
│ │ ├── activity_overview.xml
│ │ ├── activity_query.xml
│ │ ├── activity_result.xml
│ │ ├── fragment_feed_import.xml
│ │ ├── fragment_feeds_list.xml
│ │ ├── fragment_feeds_list_item.xml
│ │ ├── fragment_queries_list.xml
│ │ ├── fragment_queries_list_item.xml
│ │ ├── fragment_results_list.xml
│ │ ├── fragment_results_list_item.xml
│ │ ├── layout_date_time_picker.xml
│ │ ├── preferences_activity.xml
│ │ └── query_filter_list_item.xml
│ │ ├── menu
│ │ ├── activity_query_menu.xml
│ │ ├── fragment_feeds_menu.xml
│ │ ├── fragment_results_menu.xml
│ │ └── navigation.xml
│ │ ├── mipmap-anydpi-v26
│ │ ├── ic_launcher.xml
│ │ └── ic_launcher_round.xml
│ │ ├── mipmap-hdpi
│ │ ├── ic_launcher.png
│ │ ├── ic_launcher_foreground.png
│ │ └── ic_launcher_round.png
│ │ ├── mipmap-mdpi
│ │ ├── ic_launcher.png
│ │ ├── ic_launcher_foreground.png
│ │ └── ic_launcher_round.png
│ │ ├── mipmap-xhdpi
│ │ ├── ic_launcher.png
│ │ ├── ic_launcher_foreground.png
│ │ └── ic_launcher_round.png
│ │ ├── mipmap-xxhdpi
│ │ ├── ic_launcher.png
│ │ ├── ic_launcher_foreground.png
│ │ └── ic_launcher_round.png
│ │ ├── mipmap-xxxhdpi
│ │ ├── ic_launcher.png
│ │ ├── ic_launcher_foreground.png
│ │ └── ic_launcher_round.png
│ │ ├── values-zh
│ │ └── strings.xml
│ │ ├── values
│ │ ├── colors.xml
│ │ ├── dimens.xml
│ │ ├── strings.xml
│ │ └── styles.xml
│ │ └── xml
│ │ └── preferences.xml
│ └── test
│ └── java
│ └── me
│ └── murks
│ └── feedwatcher
│ └── TextsTests.kt
├── atomrss-cli
├── .gitignore
├── build.gradle
└── src
│ └── main
│ └── java
│ └── me
│ └── murks
│ └── feedwatcher
│ └── atomrss
│ └── cli
│ ├── AtomRssFileVisitor.java
│ └── FeedwatcherAtomRssCli.java
├── atomrss
├── .gitignore
├── build.gradle
└── src
│ ├── main
│ └── java
│ │ └── me
│ │ └── murks
│ │ └── feedwatcher
│ │ └── atomrss
│ │ ├── FeedItem.kt
│ │ ├── FeedParser.kt
│ │ ├── LazyParser.kt
│ │ └── ParserNode.kt
│ └── test
│ └── java
│ └── me
│ └── murks
│ └── feedwatcher
│ └── atomrss
│ └── FeedParserTests.kt
├── build.gradle
├── fastlane
└── metadata
│ └── android
│ └── en
│ ├── full_description.txt
│ ├── name.txt
│ └── short_description.txt
├── feed-icon.svg
├── feedwatcher-icon.png
├── feedwatcher-icon.svg
├── feedwatcher-notification-path.svg
├── feedwatcher-notification.svg
├── gradle.properties
├── gradle
└── wrapper
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
├── gradlew
├── gradlew.bat
└── settings.gradle
/.github/workflows/main.yml:
--------------------------------------------------------------------------------
1 | # This is a basic workflow to help you get started with Actions
2 |
3 | name: Gradle build
4 |
5 | # Controls when the action will run. Triggers the workflow on push or pull request
6 | # events but only for the master branch
7 | on: [push, pull_request]
8 |
9 | # A workflow run is made up of one or more jobs that can run sequentially or in parallel
10 | jobs:
11 | # This workflow contains a single job called "build"
12 | build:
13 | # The type of runner that the job will run on
14 | runs-on: ubuntu-latest
15 |
16 | # Steps represent a sequence of tasks that will be executed as part of the job
17 | steps:
18 | # Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it
19 | - uses: actions/checkout@v2
20 |
21 | # Runs a single command using the runners shell
22 | - name: Run a one-line script
23 | run: ./gradlew build
24 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | *.iml
2 | .gradle
3 | /local.properties
4 | /.idea/**
5 | .DS_Store
6 | /build
7 | /captures
8 | .externalNativeBuild
9 | .idea/caches/build_file_checksums.ser
10 | .idea/assetWizardSettings.xml
11 |
--------------------------------------------------------------------------------
/CONTRIBUTORS:
--------------------------------------------------------------------------------
1 | zouroboros
2 | sr093906
3 |
--------------------------------------------------------------------------------
/Readme.md:
--------------------------------------------------------------------------------
1 | FeedWatcher
2 | ===========
3 |
4 | [](https://f-droid.org/packages/me.murks.feedwatcher)
7 |
8 | Official repository for the FeedWatcher app. FeedWatcher allows you to monitor
9 | RSS/ATOM feeds with customizable queries.
10 |
11 | FeedWatcher is currently under development towards an initial release and
12 | still contains a lot of bugs. If you find any bugs you are welcome to create a
13 | new issue.
14 |
15 | FeedWatcher is licensed under the GNU General Public License v3.0 (GNU GPLv3) or any later version. You can find the license text in the LICENSE file.
16 |
--------------------------------------------------------------------------------
/app/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 |
--------------------------------------------------------------------------------
/app/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'com.android.application'
2 |
3 | apply plugin: 'kotlin-android'
4 |
5 | apply plugin: 'org.jetbrains.dokka'
6 |
7 | apply plugin: 'kotlin-kapt'
8 |
9 | apply plugin: "de.mannodermaus.android-junit5"
10 |
11 | android {
12 | compileSdkVersion 31
13 | defaultConfig {
14 | applicationId "me.murks.feedwatcher"
15 | minSdkVersion 24
16 | targetSdkVersion 31
17 | versionCode 27
18 | versionName "0.0.27-alpha"
19 | buildFeatures {
20 | dataBinding true
21 | viewBinding true
22 | }
23 | testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
24 | }
25 |
26 | buildTypes {
27 | release {
28 | minifyEnabled false
29 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
30 | }
31 | }
32 |
33 |
34 | // To inline the bytecode built with JVM target 1.8 into
35 | // bytecode that is being built with JVM target 1.6. (e.g. navArgs)
36 | compileOptions {
37 | sourceCompatibility JavaVersion.VERSION_1_8
38 | targetCompatibility JavaVersion.VERSION_1_8
39 | }
40 |
41 | kotlinOptions {
42 | jvmTarget = "1.8"
43 | }
44 |
45 | lint {
46 | disable 'MissingTranslation'
47 | showAll true
48 | textOutput file('stdout')
49 | textReport true
50 | }
51 | }
52 |
53 | dokka {
54 | outputFormat = 'html'
55 | outputDirectory = "$buildDir/javadoc"
56 | }
57 |
58 | dependencies {
59 | implementation fileTree(dir: 'libs', include: ['*.jar'])
60 | implementation project(':atomrss')
61 |
62 | implementation 'com.github.zouroboros:sql-schema-spec:master-SNAPSHOT'
63 | implementation 'com.github.zouroboros:jopl:0.0.4-alpha'
64 |
65 | implementation 'org.jsoup:jsoup:1.11.3'
66 |
67 | // okhttp dependencies
68 | implementation(platform("com.squareup.okhttp3:okhttp-bom:4.9.1"))
69 | // define any required OkHttp artifacts without version
70 | implementation("com.squareup.okhttp3:okhttp")
71 | implementation("com.squareup.okhttp3:logging-interceptor")
72 |
73 | implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
74 | implementation 'androidx.appcompat:appcompat:1.3.1'
75 | implementation 'androidx.constraintlayout:constraintlayout:2.1.0'
76 | implementation 'androidx.legacy:legacy-support-v4:1.0.0'
77 | implementation 'androidx.recyclerview:recyclerview:1.2.1'
78 | implementation 'androidx.preference:preference-ktx:1.1.1'
79 | implementation 'com.google.android.material:material:1.5.0-alpha01'
80 |
81 | implementation 'androidx.navigation:navigation-fragment-ktx:2.3.5'
82 | implementation 'androidx.navigation:navigation-ui-ktx:2.3.5'
83 |
84 | // (Required) Writing and executing Unit Tests on the JUnit Platform
85 | testImplementation("org.junit.jupiter:junit-jupiter-api:5.8.0")
86 | testRuntimeOnly("org.junit.jupiter:junit-jupiter-engine:5.8.0")
87 | testImplementation 'net.sf.kxml:kxml2:2.3.0'
88 |
89 | androidTestImplementation 'androidx.test:runner:1.4.0'
90 | androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0'
91 | }
92 |
--------------------------------------------------------------------------------
/app/lint.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/app/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | # Add project specific ProGuard rules here.
2 | # You can control the set of applied configuration files using the
3 | # proguardFiles setting in build.gradle.
4 | #
5 | # For more details, see
6 | # http://developer.android.com/guide/developing/tools/proguard.html
7 |
8 | # If your project uses WebView with JS, uncomment the following
9 | # and specify the fully qualified class name to the JavaScript interface
10 | # class:
11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
12 | # public *;
13 | #}
14 |
15 | # Uncomment this to preserve the line number information for
16 | # debugging stack traces.
17 | #-keepattributes SourceFile,LineNumberTable
18 |
19 | # If you keep the line number information, uncomment this to
20 | # hide the original source file name.
21 | #-renamesourcefileattribute SourceFile
22 |
--------------------------------------------------------------------------------
/app/src/main/ic_launcher-web.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zouroboros/feed-watcher/9e8f0b3ac07c5f95ec407a390283537a3029076a/app/src/main/ic_launcher-web.png
--------------------------------------------------------------------------------
/app/src/main/java/me/murks/feedwatcher/AndroidApplication.kt:
--------------------------------------------------------------------------------
1 | /*
2 | This file is part of FeedWatcher.
3 |
4 | FeedWatcher is free software: you can redistribute it and/or modify
5 | it under the terms of the GNU General Public License as published by
6 | the Free Software Foundation, either version 3 of the License, or
7 | (at your option) any later version.
8 |
9 | FeedWatcher is distributed in the hope that it will be useful,
10 | but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 | GNU General Public License for more details.
13 |
14 | You should have received a copy of the GNU General Public License
15 | along with FeedWatcher. If not, see .
16 | Copyright 2021 Zouroboros
17 | */
18 | package me.murks.feedwatcher
19 |
20 | import android.app.Application
21 |
22 | /**
23 | * @author zouroboros
24 | */
25 | @Suppress
26 | class AndroidApplication(): Application() {
27 | override fun onCreate() {
28 | super.onCreate()
29 |
30 | Notifications(this).createNotificationChannel()
31 |
32 | val app = FeedWatcherApp(AndroidEnvironment(this))
33 | app.rescheduleJobs()
34 | app.environment.close()
35 | }
36 | }
--------------------------------------------------------------------------------
/app/src/main/java/me/murks/feedwatcher/AndroidEnvironment.kt:
--------------------------------------------------------------------------------
1 | /*
2 | This file is part of FeedWatcher.
3 |
4 | FeedWatcher is free software: you can redistribute it and/or modify
5 | it under the terms of the GNU General Public License as published by
6 | the Free Software Foundation, either version 3 of the License, or
7 | (at your option) any later version.
8 |
9 | FeedWatcher is distributed in the hope that it will be useful,
10 | but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 | GNU General Public License for more details.
13 |
14 | You should have received a copy of the GNU General Public License
15 | along with FeedWatcher. If not, see .
16 | Copyright 2019-2020 Zouroboros
17 | */
18 | package me.murks.feedwatcher
19 |
20 | import android.content.Context
21 | import me.murks.feedwatcher.data.DataStore
22 |
23 | /**
24 | * @author zouroboros
25 | */
26 | class AndroidEnvironment(context: Context): Environment {
27 | override val dataStore = DataStore(context)
28 | override val settings = AndroidSettings(context)
29 | override val jobs = Jobs(context)
30 | override val notifications = Notifications(context)
31 | override val log = FeedwatcherLog()
32 | override fun close() {
33 | dataStore.close()
34 | }
35 | }
--------------------------------------------------------------------------------
/app/src/main/java/me/murks/feedwatcher/AndroidSettings.kt:
--------------------------------------------------------------------------------
1 | /*
2 | This file is part of FeedWatcher.
3 |
4 | FeedWatcher is free software: you can redistribute it and/or modify
5 | it under the terms of the GNU General Public License as published by
6 | the Free Software Foundation, either version 3 of the License, or
7 | (at your option) any later version.
8 |
9 | FeedWatcher is distributed in the hope that it will be useful,
10 | but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 | GNU General Public License for more details.
13 |
14 | You should have received a copy of the GNU General Public License
15 | along with FeedWatcher. If not, see .
16 | Copyright 2021 Zouroboros
17 | */
18 | package me.murks.feedwatcher
19 |
20 | import android.content.Context
21 | import androidx.preference.PreferenceManager
22 |
23 | class AndroidSettings(val context: Context): Settings {
24 | private val preferences = PreferenceManager.getDefaultSharedPreferences(context)
25 |
26 | init {
27 | // perform migrations
28 | if (!preferences.contains(Constants.scanIntervalTableIdPreferencesKey)) {
29 | val edit = preferences.edit()
30 | edit.putInt(Constants.scanIntervalTableIdPreferencesKey, 0)
31 | edit.putInt(Constants.scanIntervalIdPreferencesKey,
32 | preferences.getInt(Constants.scanIntervalPreferencesKey, 3) - 1)
33 | edit.remove(Constants.scanIntervalPreferencesKey)
34 | edit.commit()
35 | }
36 |
37 | // update scan interval table id
38 | if (preferences.getInt(Constants.scanIntervalTableIdPreferencesKey, -1) != 1) {
39 | val edit = preferences.edit()
40 | edit.putInt(Constants.scanIntervalTableIdPreferencesKey, 1)
41 | edit.putInt(Constants.scanIntervalIdPreferencesKey,
42 | preferences.getInt(Constants.scanIntervalIdPreferencesKey, 0) + 2)
43 | edit.commit()
44 | }
45 | }
46 |
47 | override val showNotifications: Boolean
48 | get() = preferences.getBoolean(Constants.notificationsPreferencesKey, true)
49 | override val backgroundScanning: Boolean
50 | get() = preferences.getBoolean(Constants.backgroundScanningPreferencesKey, true)
51 | override val scanIntervalTableId: Int
52 | get() = preferences.getInt(Constants.scanIntervalTableIdPreferencesKey, 0)
53 | override val scanIntervalId: Int
54 | get() = preferences.getInt(Constants.scanIntervalIdPreferencesKey, 2)
55 | }
--------------------------------------------------------------------------------
/app/src/main/java/me/murks/feedwatcher/Constants.kt:
--------------------------------------------------------------------------------
1 | /*
2 | This file is part of FeedWatcher.
3 |
4 | FeedWatcher is free software: you can redistribute it and/or modify
5 | it under the terms of the GNU General Public License as published by
6 | the Free Software Foundation, either version 3 of the License, or
7 | (at your option) any later version.
8 |
9 | FeedWatcher is distributed in the hope that it will be useful,
10 | but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 | GNU General Public License for more details.
13 |
14 | You should have received a copy of the GNU General Public License
15 | along with FeedWatcher. If not, see .
16 | Copyright 2021 Zouroboros
17 | */
18 | package me.murks.feedwatcher
19 |
20 | /**
21 | * Object for holding constants that are used in xml resources as well as code
22 | * @author zouroboros
23 | */
24 | object Constants {
25 | val scanIntervalPreferencesKey = "scan_interval"
26 | val backgroundScanningPreferencesKey = "scan_background_scan"
27 | val notificationsPreferencesKey = "new_results_notification"
28 | val scanIntervalTableIdPreferencesKey = "scan_interval_table_id"
29 | val scanIntervalIdPreferencesKey = "scan_interval_id"
30 | val scanIntervalInfo = "scan_interval_info"
31 | }
--------------------------------------------------------------------------------
/app/src/main/java/me/murks/feedwatcher/Either.kt:
--------------------------------------------------------------------------------
1 | /*
2 | This file is part of FeedWatcher.
3 |
4 | FeedWatcher is free software: you can redistribute it and/or modify
5 | it under the terms of the GNU General Public License as published by
6 | the Free Software Foundation, either version 3 of the License, or
7 | (at your option) any later version.
8 |
9 | FeedWatcher is distributed in the hope that it will be useful,
10 | but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 | GNU General Public License for more details.
13 |
14 | You should have received a copy of the GNU General Public License
15 | along with FeedWatcher. If not, see .
16 | Copyright 2020 Zouroboros
17 | */
18 | package me.murks.feedwatcher
19 |
20 | /**
21 | * Classes representing a success value (Right) or a error value (Left)
22 | * @author zouroboros
23 | */
24 | sealed class Either(protected val leftValue: TL?, protected val rightValue: TR?) {
25 | fun isLeft() = leftValue != null
26 | fun isRight() = rightValue != null
27 |
28 | fun either(leftMapper: (TL) -> TResult, rightMapper: (TR) -> TResult)
29 | = if (isLeft()) leftMapper(leftValue!!) else rightMapper(rightValue!!)
30 | }
31 |
32 | data class Left(val value: TL) : Either(value, null)
33 |
34 | data class Right(val value: TR) : Either(null, value)
--------------------------------------------------------------------------------
/app/src/main/java/me/murks/feedwatcher/Environment.kt:
--------------------------------------------------------------------------------
1 | /*
2 | This file is part of FeedWatcher.
3 |
4 | FeedWatcher is free software: you can redistribute it and/or modify
5 | it under the terms of the GNU General Public License as published by
6 | the Free Software Foundation, either version 3 of the License, or
7 | (at your option) any later version.
8 |
9 | FeedWatcher is distributed in the hope that it will be useful,
10 | but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 | GNU General Public License for more details.
13 |
14 | You should have received a copy of the GNU General Public License
15 | along with FeedWatcher. If not, see .
16 | Copyright 2019-2020 Zouroboros
17 | */
18 | package me.murks.feedwatcher
19 |
20 | import me.murks.feedwatcher.data.DataStore
21 |
22 | /**
23 | * @author zouroboros
24 | */
25 | interface Environment: AutoCloseable {
26 | val dataStore: DataStore
27 | val settings: Settings
28 | val jobs: Jobs
29 | val notifications: Notifications
30 | val log: FeedwatcherLog
31 | }
--------------------------------------------------------------------------------
/app/src/main/java/me/murks/feedwatcher/FeedItems.kt:
--------------------------------------------------------------------------------
1 | /*
2 | This file is part of FeedWatcher.
3 |
4 | FeedWatcher is free software: you can redistribute it and/or modify
5 | it under the terms of the GNU General Public License as published by
6 | the Free Software Foundation, either version 3 of the License, or
7 | (at your option) any later version.
8 |
9 | FeedWatcher is distributed in the hope that it will be useful,
10 | but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 | GNU General Public License for more details.
13 |
14 | You should have received a copy of the GNU General Public License
15 | along with FeedWatcher. If not, see .
16 | Copyright 2021 Zouroboros
17 | */
18 | package me.murks.feedwatcher
19 |
20 | import android.content.Context
21 | import me.murks.feedwatcher.atomrss.FeedItem
22 |
23 | fun FeedItem.itemTitle(context: Context) =
24 | this.title ?: this.description ?: context.getString(R.string.feed_item_no_tile)
25 |
26 | fun FeedItem.itemDescription(context: Context) =
27 | this.description ?: this.title ?: context.getString(R.string.feed_item_no_description)
--------------------------------------------------------------------------------
/app/src/main/java/me/murks/feedwatcher/FeedwatcherLog.kt:
--------------------------------------------------------------------------------
1 | /*
2 | This file is part of FeedWatcher.
3 |
4 | FeedWatcher is free software: you can redistribute it and/or modify
5 | it under the terms of the GNU General Public License as published by
6 | the Free Software Foundation, either version 3 of the License, or
7 | (at your option) any later version.
8 |
9 | FeedWatcher is distributed in the hope that it will be useful,
10 | but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 | GNU General Public License for more details.
13 |
14 | You should have received a copy of the GNU General Public License
15 | along with FeedWatcher. If not, see .
16 | Copyright 2020 Zouroboros
17 | */
18 | package me.murks.feedwatcher
19 |
20 | import android.util.Log
21 |
22 | /**
23 | * Wrapper for the android log functions that automatically generates tag based on the calling class and method.
24 | * @author zouroboros
25 | */
26 | class FeedwatcherLog {
27 | fun info(message: String) {
28 | Log.v(tag(), message)
29 | }
30 |
31 | fun error(message: String) {
32 | Log.e(tag(), message)
33 | }
34 |
35 | fun error(message: String, exception: Throwable) {
36 | Log.e(tag(), message, exception)
37 | }
38 |
39 | private fun tag(): String {
40 | val caller = Thread.currentThread().stackTrace.first { it.className.startsWith("me.murks.feedwatcher") && it.className != javaClass.name }
41 | return "${caller.className}.${caller.methodName}"
42 | }
43 | }
--------------------------------------------------------------------------------
/app/src/main/java/me/murks/feedwatcher/HtmlTags.kt:
--------------------------------------------------------------------------------
1 | package me.murks.feedwatcher
2 |
3 | import org.jsoup.Jsoup
4 |
5 | /**
6 | * @author zouroboros
7 | */
8 | object HtmlTags {
9 | fun text(html: String): String {
10 | return Jsoup.parseBodyFragment(html).text()
11 | }
12 |
13 | fun wrapInDocument(html: String, backgroundColor: Int): String {
14 | val document = Jsoup.parse(html)
15 | val color = cssColor(backgroundColor)
16 | document.body().attr("style",
17 | "padding: 0; margin: 0; background-color: $color;")
18 | return document.html()
19 | }
20 |
21 | private fun cssColor(color: Int): String {
22 | val A = color shr 24 and 0xff // or color >>> 24
23 | val R = color shr 16 and 0xff
24 | val G = color shr 8 and 0xff
25 | val B = color and 0xff
26 | return "rgba($R, $G, $B, $A);"
27 | }
28 | }
--------------------------------------------------------------------------------
/app/src/main/java/me/murks/feedwatcher/Jobs.kt:
--------------------------------------------------------------------------------
1 | /*
2 | This file is part of FeedWatcher.
3 |
4 | FeedWatcher is free software: you can redistribute it and/or modify
5 | it under the terms of the GNU General Public License as published by
6 | the Free Software Foundation, either version 3 of the License, or
7 | (at your option) any later version.
8 |
9 | FeedWatcher is distributed in the hope that it will be useful,
10 | but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 | GNU General Public License for more details.
13 |
14 | You should have received a copy of the GNU General Public License
15 | along with FeedWatcher. If not, see .
16 | Copyright 2021 Zouroboros
17 | */
18 | package me.murks.feedwatcher
19 |
20 | import android.app.job.JobInfo
21 | import android.app.job.JobScheduler
22 | import android.content.ComponentName
23 | import android.content.Context
24 | import me.murks.feedwatcher.model.ScanInterval
25 | import me.murks.feedwatcher.tasks.FilterFeedsJob
26 | import java.lang.RuntimeException
27 | import kotlin.math.max
28 |
29 | /**
30 | * Class for managing background jobs
31 | * @author zouroboros
32 | */
33 | class Jobs(private val context: Context) {
34 | /**
35 | * minimum intervals for background scanning in milliseconds
36 | */
37 | val minimumInterval = JobInfo.getMinPeriodMillis()
38 |
39 | /**
40 | * accuracy for scanning intervals in milliseconds
41 | */
42 | val intervalAccuracy = JobInfo.getMinFlexMillis()
43 |
44 | fun rescheduleJobs(scanInterval: ScanInterval) {
45 | val jobScheduler = context.getSystemService(JobScheduler::class.java)
46 |
47 | jobScheduler.cancelAll()
48 |
49 | if(jobScheduler.allPendingJobs.isEmpty()) {
50 | val jobBuilder = JobInfo.Builder(1, ComponentName(context, FilterFeedsJob::class.java))
51 |
52 | val interval = scanInterval.hours * 60 * 60 * 1000L + scanInterval.minutes * 60 * 1000L
53 |
54 | jobBuilder.setPeriodic(interval, max(JobInfo.getMinFlexMillis(), interval/10))
55 | .setPersisted(true)
56 | .setRequiredNetworkType(JobInfo.NETWORK_TYPE_ANY)
57 | val result = jobScheduler.schedule(jobBuilder.build())
58 | if(result != JobScheduler.RESULT_SUCCESS) {
59 | throw RuntimeException("Couldn't schedule job!")
60 | }
61 | // TODO only schedule job when at least one query is set up
62 | }
63 | }
64 |
65 | fun removeSchedule() {
66 | val jobScheduler = context.getSystemService(JobScheduler::class.java)
67 | jobScheduler.cancelAll()
68 | }
69 | }
--------------------------------------------------------------------------------
/app/src/main/java/me/murks/feedwatcher/Lookup.kt:
--------------------------------------------------------------------------------
1 | /*
2 | This file is part of FeedWatcher.
3 |
4 | FeedWatcher is free software: you can redistribute it and/or modify
5 | it under the terms of the GNU General Public License as published by
6 | the Free Software Foundation, either version 3 of the License, or
7 | (at your option) any later version.
8 |
9 | FeedWatcher is distributed in the hope that it will be useful,
10 | but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 | GNU General Public License for more details.
13 |
14 | You should have received a copy of the GNU General Public License
15 | along with FeedWatcher. If not, see .
16 | Copyright 2021 Zouroboros
17 | */
18 | package me.murks.feedwatcher
19 |
20 | import java.util.*
21 | import kotlin.collections.HashMap
22 |
23 | /**
24 | * Associates a key with a collection of values.
25 | *
26 | * @author zouroboros
27 | */
28 | class Lookup(private val map: MutableMap>): Iterable>> {
29 | fun append(key: K, value: V) {
30 | if(!map.containsKey(key)) {
31 | map[key] = LinkedList()
32 | }
33 | map[key]!!.add(value)
34 | }
35 |
36 | fun values(key: K) = map.get(key)
37 |
38 | fun flatList(): Collection> = map.entries.map {
39 | it.value.map { value -> AbstractMap.SimpleEntry(it.key, value) } }.flatten()
40 |
41 | override fun iterator(): Iterator>> = map.asIterable().iterator()
42 |
43 | fun merge(otherLookup: Lookup): Lookup {
44 | otherLookup.forEach {
45 | if (map.containsKey(it.key)) {
46 | map[it.key]!!.addAll(it.value)
47 | } else {
48 | map[it.key] = LinkedList(it.value)
49 | }
50 | }
51 |
52 | return this
53 | }
54 | }
55 |
56 | fun Map>.toLookup(): Lookup {
57 | return Lookup(HashMap(this.mapValues { LinkedList(it.value) }))
58 | }
59 |
60 | fun Collection.toLookup(key: (T) -> K, value: (T) -> V): Lookup {
61 | return this.groupBy(key, value).toLookup()
62 | }
--------------------------------------------------------------------------------
/app/src/main/java/me/murks/feedwatcher/Notifications.kt:
--------------------------------------------------------------------------------
1 | /*
2 | This file is part of FeedWatcher.
3 |
4 | FeedWatcher is free software: you can redistribute it and/or modify
5 | it under the terms of the GNU General Public License as published by
6 | the Free Software Foundation, either version 3 of the License, or
7 | (at your option) any later version.
8 |
9 | FeedWatcher is distributed in the hope that it will be useful,
10 | but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 | GNU General Public License for more details.
13 |
14 | You should have received a copy of the GNU General Public License
15 | along with FeedWatcher. If not, see .
16 | Copyright 2019 Zouroboros
17 | */
18 | package me.murks.feedwatcher
19 |
20 | import android.app.NotificationChannel
21 | import android.app.NotificationManager
22 | import android.app.PendingIntent
23 | import android.content.Context
24 | import android.content.Intent
25 | import android.os.Build
26 | import androidx.core.app.NotificationCompat
27 | import androidx.core.app.NotificationManagerCompat
28 | import me.murks.feedwatcher.activities.OverviewActivity
29 | import me.murks.feedwatcher.tasks.FilterFeedsJob
30 | import me.murks.feedwatcher.model.Result
31 |
32 | /**
33 | * Class for managing notifications
34 | * @author zouroboros
35 | */
36 | class Notifications(private val context: Context) {
37 |
38 | companion object {
39 | const val CHANNEL_ID = "result.notification.channel"
40 | }
41 |
42 | fun createNotificationChannel() {
43 | // Create the NotificationChannel, but only on API 26+ because
44 | // the NotificationChannel class is new and not in the support library
45 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
46 | val name = context.getString(R.string.result_notification_channel_name)
47 | val description = context.getString(R.string.result_notification_channel_description)
48 | val importance = NotificationManager.IMPORTANCE_DEFAULT
49 | val channel = NotificationChannel(CHANNEL_ID, name, importance)
50 | channel.description = description
51 | channel.importance = NotificationManager.IMPORTANCE_LOW
52 | val notificationManager = context.getSystemService(NotificationManager::class.java)
53 | notificationManager!!.createNotificationChannel(channel)
54 | }
55 | }
56 |
57 | fun newResults(results: List, settings: Settings) {
58 | val notificationBuilder = NotificationCompat.Builder(context, CHANNEL_ID)
59 | notificationBuilder.setSmallIcon(R.drawable.ic_feedwatcher_notification)
60 | notificationBuilder.setContentTitle(context.getString(R.string.result_notification_title))
61 |
62 | val feeds = results.map { it.feed.name }.distinct().joinToString(", ")
63 |
64 | notificationBuilder.setContentText(
65 | String.format(context.getString(R.string.result_notification_content), feeds))
66 |
67 | notificationBuilder.setAutoCancel(true)
68 |
69 | val intent = Intent(context, OverviewActivity::class.java)
70 | intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
71 | intent.putExtra(OverviewActivity.CURRENT_FRAGMENT, R.id.nav_results)
72 |
73 | notificationBuilder.setContentIntent(
74 | PendingIntent.getActivity(context, 0, intent,
75 | PendingIntent.FLAG_IMMUTABLE))
76 |
77 | notificationBuilder.setAutoCancel(true)
78 |
79 | val notificationManager = NotificationManagerCompat.from(context)
80 | notificationManager.notify(FilterFeedsJob.NOTIFICATION_ID, notificationBuilder.build())
81 | }
82 | }
--------------------------------------------------------------------------------
/app/src/main/java/me/murks/feedwatcher/ResourceTracker.kt:
--------------------------------------------------------------------------------
1 | /*
2 | This file is part of FeedWatcher.
3 |
4 | FeedWatcher is free software: you can redistribute it and/or modify
5 | it under the terms of the GNU General Public License as published by
6 | the Free Software Foundation, either version 3 of the License, or
7 | (at your option) any later version.
8 |
9 | FeedWatcher is distributed in the hope that it will be useful,
10 | but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 | GNU General Public License for more details.
13 |
14 | You should have received a copy of the GNU General Public License
15 | along with FeedWatcher. If not, see .
16 | Copyright 2019 Zouroboros
17 | */
18 | package me.murks.feedwatcher
19 |
20 | import java.net.HttpURLConnection
21 | import java.util.*
22 |
23 | /**
24 | * Resource tracker for closeable resources based on a
25 | * [thread](https://discuss.kotlinlang.org/t/kotlin-needs-try-with-resources/214/20)
26 | * in the kotlin language forum.
27 | * @author zouroboros
28 | */
29 | class ResourceTracker : AutoCloseable {
30 | private val resources = LinkedList()
31 | private val connections = LinkedList()
32 |
33 | fun T.track(): T {
34 | resources.add(this)
35 | return this
36 | }
37 |
38 | fun T.track(): HttpURLConnection {
39 | connections.add(this)
40 | return this
41 | }
42 |
43 | override fun close() {
44 | resources.forEach { it.close() }
45 | connections.forEach { it.inputStream.close(); it.disconnect()}
46 | }
47 |
48 | }
49 |
50 | /**
51 | * Using block in which resources can be tracked using the [ResourceTracker.track] function
52 | * @see [ResourceTracker.track]
53 | */
54 | fun using(block: ResourceTracker.() -> R): R {
55 | val holder = ResourceTracker()
56 | try {
57 | return holder.block()
58 | } finally {
59 | holder.close()
60 | }
61 | }
62 |
63 |
--------------------------------------------------------------------------------
/app/src/main/java/me/murks/feedwatcher/Settings.kt:
--------------------------------------------------------------------------------
1 | /*
2 | This file is part of FeedWatcher.
3 |
4 | FeedWatcher is free software: you can redistribute it and/or modify
5 | it under the terms of the GNU General Public License as published by
6 | the Free Software Foundation, either version 3 of the License, or
7 | (at your option) any later version.
8 |
9 | FeedWatcher is distributed in the hope that it will be useful,
10 | but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 | GNU General Public License for more details.
13 |
14 | You should have received a copy of the GNU General Public License
15 | along with FeedWatcher. If not, see .
16 | Copyright 2021 Zouroboros
17 | */
18 | package me.murks.feedwatcher
19 |
20 | /**
21 | * @author zouroboros
22 | */
23 | interface Settings {
24 | val showNotifications: Boolean
25 | val backgroundScanning: Boolean
26 | val scanIntervalTableId: Int
27 | val scanIntervalId: Int
28 | }
--------------------------------------------------------------------------------
/app/src/main/java/me/murks/feedwatcher/Texts.kt:
--------------------------------------------------------------------------------
1 | /*
2 | This file is part of FeedWatcher.
3 |
4 | FeedWatcher is free software: you can redistribute it and/or modify
5 | it under the terms of the GNU General Public License as published by
6 | the Free Software Foundation, either version 3 of the License, or
7 | (at your option) any later version.
8 |
9 | FeedWatcher is distributed in the hope that it will be useful,
10 | but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 | GNU General Public License for more details.
13 |
14 | You should have received a copy of the GNU General Public License
15 | along with FeedWatcher. If not, see .
16 | Copyright 2019 Zouroboros
17 | */
18 | package me.murks.feedwatcher
19 |
20 | import java.net.MalformedURLException
21 | import java.net.URL
22 | import kotlin.math.max
23 |
24 | /**
25 | * Utility methods for texts
26 | * @author zouroboros
27 | */
28 | object Texts {
29 |
30 | /**
31 | * Shortens a text to text prefix that is most maxLength long
32 | * @param text The string to shorten
33 | * @param maxLength the maximum length of the preview
34 | * @param suffix a suffix to be appended to the shortened string
35 | */
36 | fun preview(text: String, maxLength: Int, suffix: String): String {
37 | if(text.length <= maxLength) {
38 | return text
39 | }
40 | val lastSpace = text.substring(0, maxLength - suffix.length).lastIndexOf(" ")
41 | return text.substring(0, lastSpace) + suffix
42 | }
43 |
44 | /**
45 | * Looks for urls in the given text and returns the first one.
46 | * The URL needs to start with a protocol
47 | * @text the text in which to look
48 | */
49 | fun findUrl(text: String): URL? {
50 | val words = text.split(Regex("\\s"));
51 |
52 | return words.firstNotNullOfOrNull {
53 | try { URL(it) }
54 | catch (e: MalformedURLException) { null } }
55 | }
56 | }
--------------------------------------------------------------------------------
/app/src/main/java/me/murks/feedwatcher/activities/AboutActivity.kt:
--------------------------------------------------------------------------------
1 | /*
2 | This file is part of FeedWatcher.
3 |
4 | FeedWatcher is free software: you can redistribute it and/or modify
5 | it under the terms of the GNU General Public License as published by
6 | the Free Software Foundation, either version 3 of the License, or
7 | (at your option) any later version.
8 |
9 | FeedWatcher is distributed in the hope that it will be useful,
10 | but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 | GNU General Public License for more details.
13 |
14 | You should have received a copy of the GNU General Public License
15 | along with FeedWatcher. If not, see .
16 | Copyright 2021 Zouroboros
17 | */
18 | package me.murks.feedwatcher.activities
19 |
20 | import android.os.Bundle
21 | import me.murks.feedwatcher.databinding.ActivityAboutBinding
22 |
23 | class AboutActivity : FeedWatcherBaseActivity() {
24 | private lateinit var binding: ActivityAboutBinding
25 |
26 | override fun onCreate(savedInstanceState: Bundle?) {
27 | super.onCreate(savedInstanceState)
28 | binding = ActivityAboutBinding.inflate(layoutInflater)
29 | binding.aboutWebView.loadUrl("file:///android_asset/about/about.html")
30 | setContentView(binding.root)
31 | }
32 | }
--------------------------------------------------------------------------------
/app/src/main/java/me/murks/feedwatcher/activities/Activities.kt:
--------------------------------------------------------------------------------
1 | package me.murks.feedwatcher.activities
2 |
3 | import android.app.Activity
4 | import android.content.Context
5 | import android.content.Intent
6 | import me.murks.feedwatcher.R
7 |
8 | /**
9 | * Starts the specified activity
10 | * @author zouroboros
11 | */
12 | fun Context.startActivity(activityClass: Class) {
13 | val intent = Intent(this, activityClass)
14 | startActivity(intent)
15 | }
16 |
17 | /**
18 | * Opens the feeds list in the overview activity
19 | */
20 | fun Context.openFeeds() {
21 | val intent = Intent(this, OverviewActivity::class.java)
22 | intent.putExtra(OverviewActivity.CURRENT_FRAGMENT, R.id.nav_feeds)
23 | startActivity(intent)
24 | }
--------------------------------------------------------------------------------
/app/src/main/java/me/murks/feedwatcher/activities/DateTimePickerDialogFragment.kt:
--------------------------------------------------------------------------------
1 | /*
2 | This file is part of FeedWatcher.
3 |
4 | FeedWatcher is free software: you can redistribute it and/or modify
5 | it under the terms of the GNU General Public License as published by
6 | the Free Software Foundation, either version 3 of the License, or
7 | (at your option) any later version.
8 |
9 | FeedWatcher is distributed in the hope that it will be useful,
10 | but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 | GNU General Public License for more details.
13 |
14 | You should have received a copy of the GNU General Public License
15 | along with FeedWatcher. If not, see .
16 | Copyright 2019 Zouroboros
17 | */
18 | package me.murks.feedwatcher.activities
19 |
20 | import android.app.Dialog
21 | import android.content.DialogInterface
22 | import android.os.Bundle
23 | import android.view.View
24 | import android.widget.DatePicker
25 | import android.widget.TimePicker
26 | import androidx.appcompat.app.AlertDialog
27 | import androidx.fragment.app.DialogFragment
28 | import me.murks.feedwatcher.R
29 | import java.util.*
30 |
31 | /**
32 | * @author zouroboros
33 | */
34 | class DateTimePickerDialogFragment: DialogFragment() {
35 |
36 | var listener: DateTimePickerDialogListener? = null
37 |
38 | override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
39 | val view = View.inflate(context, R.layout.layout_date_time_picker, null)
40 | val datePicker = view.findViewById(R.id.date_time_date_picker)
41 | val timePicker = view.findViewById(R.id.date_time_time_picker)
42 | timePicker.visibility = View.GONE
43 |
44 | val date = Calendar.getInstance()
45 | datePicker.init(date.get(Calendar.YEAR), date.get(Calendar.MONTH),
46 | date.get(Calendar.DAY_OF_MONTH)) { v, _, _, _ ->
47 | v.visibility = View.GONE
48 | timePicker.visibility = View.VISIBLE
49 | }
50 |
51 | val builder = AlertDialog.Builder(requireContext())
52 | .setView(view)
53 | .setPositiveButton(R.string.date_time_confirm, DialogInterface.OnClickListener
54 | { _, _ ->
55 | val selectedDate = Calendar.getInstance()
56 | selectedDate.set(datePicker.year, datePicker.month, datePicker.dayOfMonth,
57 | timePicker.hour, timePicker.minute)
58 | listener?.dateTimeSelected(selectedDate)
59 | })
60 | .setNegativeButton(R.string.date_time_cancel, DialogInterface.OnClickListener
61 | { _, _ -> })
62 | return builder.create()
63 | }
64 |
65 | interface DateTimePickerDialogListener {
66 | fun dateTimeSelected(date: Calendar)
67 | }
68 | }
--------------------------------------------------------------------------------
/app/src/main/java/me/murks/feedwatcher/activities/Dialogs.kt:
--------------------------------------------------------------------------------
1 | package me.murks.feedwatcher.activities
2 |
3 | import android.app.AlertDialog
4 | import android.content.Context
5 | import android.content.DialogInterface
6 | import me.murks.feedwatcher.R
7 |
8 | /**
9 | * Shows an error message to the user
10 | * @author zouroboros
11 | */
12 | fun Context.errorDialog(errorResourceId: Int, additionalInfos: String, listener: DialogInterface.OnClickListener? = null){
13 | val builder = AlertDialog.Builder(this)
14 | builder.setMessage(this.resources.getString(errorResourceId, additionalInfos))
15 | builder.setPositiveButton(R.string.error_dialog_ok_button) { dialog, which ->
16 | dialog.cancel()
17 | listener?.onClick(dialog, which)
18 | }
19 | builder.create().show()
20 | }
--------------------------------------------------------------------------------
/app/src/main/java/me/murks/feedwatcher/activities/FeedExportRecyclerViewAdapter.kt:
--------------------------------------------------------------------------------
1 | /*
2 | This file is part of FeedWatcher.
3 |
4 | FeedWatcher is free software: you can redistribute it and/or modify
5 | it under the terms of the GNU General Public License as published by
6 | the Free Software Foundation, either version 3 of the License, or
7 | (at your option) any later version.
8 |
9 | FeedWatcher is distributed in the hope that it will be useful,
10 | but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 | GNU General Public License for more details.
13 |
14 | You should have received a copy of the GNU General Public License
15 | along with FeedWatcher. If not, see .
16 | Copyright 2021 Zouroboros
17 | */
18 | package me.murks.feedwatcher.activities
19 |
20 | import android.view.LayoutInflater
21 | import android.view.View
22 | import android.view.ViewGroup
23 | import me.murks.feedwatcher.databinding.ActivityFeedExportListItemBinding
24 | import me.murks.feedwatcher.R
25 | import me.murks.feedwatcher.model.Feed
26 |
27 | /**
28 | * Adapter for selecting feeds for import
29 | * @author zouroboros
30 | */
31 | class FeedExportRecyclerViewAdapter(feeds: List):
32 | SelectableRecyclerViewAdapter(feeds) {
33 |
34 | inner class ViewHolder(mView: View) : androidx.recyclerview.widget.RecyclerView.ViewHolder(mView) {
35 | private val binding = ActivityFeedExportListItemBinding.bind(mView)
36 | val text = binding.activityFeedExportListItemText
37 | val checkBox = binding.activityFeedExportListItemCheckbox
38 | }
39 |
40 | override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
41 | val view = LayoutInflater.from(parent.context)
42 | .inflate(R.layout.activity_feed_export_list_item, parent, false)
43 |
44 | return ViewHolder(view)
45 | }
46 |
47 | override fun onBindViewHolder(holder: ViewHolder, position: Int) {
48 | holder.text.text = items[position].name
49 | holder.checkBox.isChecked = selectedItems.contains(items[position])
50 | holder.checkBox.setOnCheckedChangeListener { _, isChecked ->
51 | if(isChecked) {
52 | select(holder.bindingAdapterPosition)
53 | } else {
54 | deselect(holder.bindingAdapterPosition)
55 | }
56 | }
57 | }
58 | }
--------------------------------------------------------------------------------
/app/src/main/java/me/murks/feedwatcher/activities/FeedImportFragment.kt:
--------------------------------------------------------------------------------
1 | /*
2 | This file is part of FeedWatcher.
3 |
4 | FeedWatcher is free software: you can redistribute it and/or modify
5 | it under the terms of the GNU General Public License as published by
6 | the Free Software Foundation, either version 3 of the License, or
7 | (at your option) any later version.
8 |
9 | FeedWatcher is distributed in the hope that it will be useful,
10 | but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 | GNU General Public License for more details.
13 |
14 | You should have received a copy of the GNU General Public License
15 | along with FeedWatcher. If not, see .
16 | Copyright 2021 Zouroboros
17 | */
18 | package me.murks.feedwatcher.activities
19 |
20 | import android.app.Activity
21 | import android.content.Intent
22 | import android.os.Bundle
23 | import androidx.fragment.app.Fragment
24 | import android.view.LayoutInflater
25 | import android.view.View
26 | import android.view.ViewGroup
27 |
28 | import me.murks.feedwatcher.R
29 | import me.murks.feedwatcher.databinding.FragmentFeedImportBinding
30 |
31 | class FeedImportFragment : Fragment() {
32 | private lateinit var binding: FragmentFeedImportBinding
33 |
34 | override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?,
35 | savedInstanceState: Bundle?): View {
36 | binding = FragmentFeedImportBinding.inflate(inflater, container, false)
37 |
38 | return binding.root
39 | }
40 |
41 | override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
42 | super.onViewCreated(view, savedInstanceState)
43 |
44 | binding.feedImportFragmentButton.setOnClickListener {
45 | val intent = Intent()
46 | intent.action = Intent.ACTION_OPEN_DOCUMENT
47 | intent.type = "*/*"
48 | startActivityForResult(Intent.createChooser(intent,
49 | resources.getString(R.string.feed_import_chooser_title)), OverviewActivity.FEED_IMPORT_SELECT_FILE_REQUEST_CODE)
50 | }
51 | }
52 |
53 | override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
54 | super.onActivityResult(requestCode, resultCode, data)
55 | if (requestCode == OverviewActivity.FEED_IMPORT_SELECT_FILE_REQUEST_CODE) {
56 | if(resultCode == Activity.RESULT_OK) {
57 | val selectedFile = data?.data
58 | val intent = Intent(context, FeedImportActivity::class.java)
59 | intent.data = selectedFile
60 | startActivity(intent)
61 | }
62 | }
63 | }
64 | }
65 |
--------------------------------------------------------------------------------
/app/src/main/java/me/murks/feedwatcher/activities/FeedImportRecyclerViewAdapter.kt:
--------------------------------------------------------------------------------
1 | /*
2 | This file is part of FeedWatcher.
3 |
4 | FeedWatcher is free software: you can redistribute it and/or modify
5 | it under the terms of the GNU General Public License as published by
6 | the Free Software Foundation, either version 3 of the License, or
7 | (at your option) any later version.
8 |
9 | FeedWatcher is distributed in the hope that it will be useful,
10 | but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 | GNU General Public License for more details.
13 |
14 | You should have received a copy of the GNU General Public License
15 | along with FeedWatcher. If not, see .
16 | Copyright 2021 Zouroboros
17 | */
18 | package me.murks.feedwatcher.activities
19 |
20 | import android.view.LayoutInflater
21 | import android.view.View
22 | import android.view.ViewGroup
23 |
24 | import me.murks.feedwatcher.databinding.ActivityFeedImportListItemBinding
25 | import me.murks.feedwatcher.R
26 | import me.murks.jopl.OpOutline
27 |
28 | /**
29 | * Adapter for selecting feeds for import
30 | * @author zouroboros
31 | */
32 | class FeedImportRecyclerViewAdapter(outlines: List):
33 | SelectableRecyclerViewAdapter(outlines) {
34 |
35 | inner class ViewHolder(mView: View) : androidx.recyclerview.widget.RecyclerView.ViewHolder(mView) {
36 | private val binding = ActivityFeedImportListItemBinding.bind(mView)
37 | val text = binding.activityFeedImportListItemText
38 | val checkBox = binding.activityFeedImportListItemCheckbox
39 | }
40 |
41 | override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
42 | val view = LayoutInflater.from(parent.context)
43 | .inflate(R.layout.activity_feed_import_list_item, parent, false)
44 |
45 | return ViewHolder(view)
46 | }
47 |
48 | override fun onBindViewHolder(holder: ViewHolder, position: Int) {
49 | holder.text.text = items[position].title
50 | holder.checkBox.isChecked = selectedItems.contains(items[position])
51 | holder.checkBox.setOnCheckedChangeListener { _, isChecked ->
52 | if(isChecked) {
53 | select(holder.bindingAdapterPosition)
54 | } else {
55 | deselect(holder.bindingAdapterPosition)
56 | }
57 | }
58 | }
59 | }
--------------------------------------------------------------------------------
/app/src/main/java/me/murks/feedwatcher/activities/FeedUiContainer.kt:
--------------------------------------------------------------------------------
1 | /*
2 | This file is part of FeedWatcher.
3 |
4 | FeedWatcher is free software: you can redistribute it and/or modify
5 | it under the terms of the GNU General Public License as published by
6 | the Free Software Foundation, either version 3 of the License, or
7 | (at your option) any later version.
8 |
9 | FeedWatcher is distributed in the hope that it will be useful,
10 | but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 | GNU General Public License for more details.
13 |
14 | You should have received a copy of the GNU General Public License
15 | along with FeedWatcher. If not, see .
16 | Copyright 2019 - 2021 Zouroboros
17 | */
18 | package me.murks.feedwatcher.activities
19 |
20 | import me.murks.feedwatcher.atomrss.FeedParser
21 | import me.murks.feedwatcher.model.Feed
22 | import me.murks.feedwatcher.model.Scan
23 | import java.net.URL
24 | import java.util.*
25 |
26 | /**
27 | * Container holding all information shown in the ui for a feed
28 | * @author zouroboros
29 | */
30 | // TODO optimize for lazyfeed parser
31 | data class FeedUiContainer(val feed: Feed?, val name: String, val icon: URL?,
32 | val description: String?, val url: URL, val updated: Date?,
33 | val scans: Collection) {
34 |
35 | constructor(feed: Feed, feedParser: FeedParser, scans: Collection):
36 | this(feed, feed.name,
37 | if (feedParser.iconUrl != null)
38 | feed.url.toURI().resolve(feedParser.iconUrl)?.toURL() else null,
39 | feedParser.description, feed.url, feed.lastUpdate, scans)
40 |
41 | constructor(feed: Feed, scans: Collection):
42 | this(feed, feed.name, null, null, feed.url, feed.lastUpdate, scans)
43 |
44 | constructor(url: URL, updated: Date?, feed: FeedParser):
45 | this(null, feed.name ?: url.toString(),
46 | if (feed.iconUrl != null) url.toURI().resolve(feed.iconUrl)?.toURL() else null,
47 | feed.description, url, updated, emptyList())
48 | }
--------------------------------------------------------------------------------
/app/src/main/java/me/murks/feedwatcher/activities/FeedWatcherAsyncLoadingFragment.kt:
--------------------------------------------------------------------------------
1 | /*
2 | This file is part of FeedWatcher.
3 |
4 | FeedWatcher is free software: you can redistribute it and/or modify
5 | it under the terms of the GNU General Public License as published by
6 | the Free Software Foundation, either version 3 of the License, or
7 | (at your option) any later version.
8 |
9 | FeedWatcher is distributed in the hope that it will be useful,
10 | but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 | GNU General Public License for more details.
13 |
14 | You should have received a copy of the GNU General Public License
15 | along with FeedWatcher. If not, see .
16 | Copyright 2020 Zouroboros
17 | */
18 | package me.murks.feedwatcher.activities
19 |
20 | import android.os.Handler
21 | import android.os.Looper
22 |
23 | /**
24 | * Base class for fragments that load data asynchronously.
25 | * @author zouroboros
26 | */
27 | abstract class FeedWatcherAsyncLoadingFragment: FeedWatcherBaseFragment() {
28 | private var loadingThread: Thread? = null
29 | val handler = Handler(Looper.getMainLooper())
30 |
31 | private fun createThread() = Thread {
32 | handler.post { onLoadingStart() }
33 | loadData()
34 | handler.post { onLoadingFinished() }
35 | }
36 |
37 | abstract fun loadData()
38 |
39 | abstract fun processResult(result: TResult)
40 |
41 | abstract fun onLoadingStart()
42 |
43 | abstract fun onLoadingFinished()
44 |
45 | protected fun reload() {
46 | if (!(loadingThread?.isAlive == true)) {
47 | loadingThread = createThread()
48 | loadingThread!!.start()
49 | }
50 | }
51 |
52 | protected fun publishResult(result: TResult) {
53 | handler.post { processResult(result) }
54 | }
55 | }
--------------------------------------------------------------------------------
/app/src/main/java/me/murks/feedwatcher/activities/FeedWatcherBaseActivity.kt:
--------------------------------------------------------------------------------
1 | /*
2 | This file is part of FeedWatcher.
3 |
4 | FeedWatcher is free software: you can redistribute it and/or modify
5 | it under the terms of the GNU General Public License as published by
6 | the Free Software Foundation, either version 3 of the License, or
7 | (at your option) any later version.
8 |
9 | FeedWatcher is distributed in the hope that it will be useful,
10 | but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 | GNU General Public License for more details.
13 |
14 | You should have received a copy of the GNU General Public License
15 | along with FeedWatcher. If not, see .
16 | Copyright 2019 Zouroboros
17 | */
18 | package me.murks.feedwatcher.activities
19 |
20 | import android.os.Bundle
21 | import androidx.appcompat.app.AppCompatActivity
22 | import me.murks.feedwatcher.AndroidEnvironment
23 | import me.murks.feedwatcher.FeedWatcherApp
24 |
25 | /**
26 | * Base activity for all activities of the feed watcher app.
27 | * Provides common fields and methods
28 | * @author zouroboros
29 | */
30 | abstract class FeedWatcherBaseActivity: AppCompatActivity() {
31 | lateinit var app: FeedWatcherApp
32 |
33 | override fun onCreate(savedInstanceState: Bundle?) {
34 | super.onCreate(savedInstanceState)
35 | app = FeedWatcherApp(AndroidEnvironment(this))
36 | }
37 |
38 | override fun onDestroy() {
39 | super.onDestroy()
40 | app.environment.close()
41 | }
42 | }
--------------------------------------------------------------------------------
/app/src/main/java/me/murks/feedwatcher/activities/FeedWatcherBaseFragment.kt:
--------------------------------------------------------------------------------
1 | /*
2 | This file is part of FeedWatcher.
3 |
4 | FeedWatcher is free software: you can redistribute it and/or modify
5 | it under the terms of the GNU General Public License as published by
6 | the Free Software Foundation, either version 3 of the License, or
7 | (at your option) any later version.
8 |
9 | FeedWatcher is distributed in the hope that it will be useful,
10 | but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 | GNU General Public License for more details.
13 |
14 | You should have received a copy of the GNU General Public License
15 | along with FeedWatcher. If not, see .
16 | Copyright 2019 Zouroboros
17 | */
18 | package me.murks.feedwatcher.activities
19 |
20 | import android.os.Bundle
21 | import me.murks.feedwatcher.AndroidEnvironment
22 | import me.murks.feedwatcher.FeedWatcherApp
23 |
24 | /**
25 | * @author zouroboros
26 | */
27 | abstract class FeedWatcherBaseFragment: androidx.fragment.app.Fragment() {
28 | lateinit var app: FeedWatcherApp
29 |
30 | override fun onCreate(savedInstanceState: Bundle?) {
31 | super.onCreate(savedInstanceState)
32 | app = FeedWatcherApp(AndroidEnvironment(requireContext()))
33 | }
34 |
35 | override fun onDestroy() {
36 | app.environment.close()
37 | super.onDestroy()
38 | }
39 | }
--------------------------------------------------------------------------------
/app/src/main/java/me/murks/feedwatcher/activities/FeedsRecyclerViewAdapter.kt:
--------------------------------------------------------------------------------
1 | /*
2 | This file is part of FeedWatcher.
3 |
4 | FeedWatcher is free software: you can redistribute it and/or modify
5 | it under the terms of the GNU General Public License as published by
6 | the Free Software Foundation, either version 3 of the License, or
7 | (at your option) any later version.
8 |
9 | FeedWatcher is distributed in the hope that it will be useful,
10 | but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 | GNU General Public License for more details.
13 |
14 | You should have received a copy of the GNU General Public License
15 | along with FeedWatcher. If not, see .
16 | Copyright 2019 - 2021 Zouroboros
17 | */
18 | package me.murks.feedwatcher.activities
19 |
20 | import android.graphics.Bitmap
21 | import android.view.LayoutInflater
22 | import android.view.View
23 | import android.view.ViewGroup
24 | import android.widget.ImageView
25 | import android.widget.TextView
26 | import androidx.core.content.ContextCompat
27 |
28 | import me.murks.feedwatcher.R
29 | import me.murks.feedwatcher.databinding.FragmentFeedsListItemBinding
30 | import me.murks.feedwatcher.model.Feed
31 | import me.murks.feedwatcher.tasks.Tasks
32 | import java.net.URL
33 |
34 | /**
35 | * Adapter for displaying a list of feeds
36 | * @see [Feed]
37 | * @author zouroboros
38 | */
39 | class FeedsRecyclerViewAdapter(listener: FeedListInteractionListener?)
40 | : ListRecyclerViewAdapter(listOf()) {
41 |
42 | private val iconsLoading = mutableSetOf()
43 |
44 | private val onClickListener: View.OnClickListener = View.OnClickListener { v ->
45 | val item = v.tag as FeedUiContainer
46 | listener?.onOpenFeed(item)
47 | }
48 | private val images: MutableMap = mutableMapOf()
49 |
50 | override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
51 | val view = LayoutInflater.from(parent.context)
52 | .inflate(R.layout.fragment_feeds_list_item, parent, false)
53 | return ViewHolder(view)
54 | }
55 |
56 | override fun onBindViewHolder(holder: ViewHolder, position: Int) {
57 | val item = items[position]
58 | holder.feedName.text = item.name
59 | holder.feedIcon.setImageResource(R.drawable.ic_feed_icon)
60 | if(item.icon != null && images.containsKey(item.icon)) {
61 | holder.feedIcon.setImageBitmap(images[item.icon])
62 | } else if (item.icon != null && !iconsLoading.contains(item.icon)) {
63 | iconsLoading.add(item.icon)
64 | Tasks.loadImage(item.icon, holder.feedIcon.layoutParams.width, holder.feedIcon.layoutParams.height)
65 | .thenAcceptAsync({
66 | val index = items.indexOfFirst { item.icon == it.icon }
67 | images[item.icon] = it
68 | notifyItemChanged(index)
69 | }, ContextCompat.getMainExecutor(holder.itemView.context))
70 | }
71 |
72 | with(holder.mView) {
73 | tag = item
74 | setOnClickListener(onClickListener)
75 | }
76 | }
77 |
78 | inner class ViewHolder(val mView: View) : androidx.recyclerview.widget.RecyclerView.ViewHolder(mView) {
79 | private val binding = FragmentFeedsListItemBinding.bind(mView)
80 | val feedName: TextView = binding.feedName
81 | val feedIcon: ImageView = binding.feedIcon
82 | }
83 |
84 | interface FeedListInteractionListener {
85 | fun onOpenFeed(feed: FeedUiContainer)
86 | }
87 | }
88 |
--------------------------------------------------------------------------------
/app/src/main/java/me/murks/feedwatcher/activities/FilterUiModel.kt:
--------------------------------------------------------------------------------
1 | /*
2 | This file is part of FeedWatcher.
3 |
4 | FeedWatcher is free software: you can redistribute it and/or modify
5 | it under the terms of the GNU General Public License as published by
6 | the Free Software Foundation, either version 3 of the License, or
7 | (at your option) any later version.
8 |
9 | FeedWatcher is distributed in the hope that it will be useful,
10 | but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 | GNU General Public License for more details.
13 |
14 | You should have received a copy of the GNU General Public License
15 | along with FeedWatcher. If not, see .
16 | Copyright 2019 Zouroboros
17 | */
18 | package me.murks.feedwatcher.activities
19 |
20 | import me.murks.feedwatcher.model.*
21 | import java.lang.IllegalStateException
22 | import java.util.*
23 |
24 | /**
25 | * @author zouroboros
26 | */
27 | class FilterUiModel(var type: FilterType, val feeds: List): FilterTypeCallback {
28 | var containsText: String = ""
29 | var selectedType: Int
30 | get() = FilterType.values().indexOf(type)
31 | set(value) { type = FilterType.values()[value] }
32 | var selectedFeed: Int? = null
33 | var startDate: Date = Date()
34 | val feedNames = feeds.map { it.name }
35 |
36 | constructor(filter: Filter, feeds: List): this(filter.type, feeds) {
37 | filter.filterCallback(this)
38 | }
39 |
40 | fun filter(index: Int): Filter {
41 | if(type == FilterType.CONTAINS) {
42 | return ContainsFilter(index, containsText)
43 | } else if(type == FilterType.FEED) {
44 | return FeedFilter(index, if (selectedFeed != null) feeds[selectedFeed!!].url else null)
45 | } else if(type == FilterType.NEW) {
46 | return NewEntryFilter(index, startDate)
47 | }
48 | throw IllegalStateException()
49 | }
50 |
51 | override fun filter(filter: FeedFilter) {
52 | selectedFeed = feeds.map { it.url }.indexOf(filter.feedUrl)
53 | }
54 |
55 | override fun filter(filter: ContainsFilter) {
56 | containsText = filter.text?: ""
57 | }
58 |
59 | override fun filter(filter: NewEntryFilter) {
60 | startDate = filter.start
61 | }
62 |
63 |
64 | }
--------------------------------------------------------------------------------
/app/src/main/java/me/murks/feedwatcher/activities/Formatter.kt:
--------------------------------------------------------------------------------
1 | /*
2 | This file is part of FeedWatcher.
3 |
4 | FeedWatcher is free software: you can redistribute it and/or modify
5 | it under the terms of the GNU General Public License as published by
6 | the Free Software Foundation, either version 3 of the License, or
7 | (at your option) any later version.
8 |
9 | FeedWatcher is distributed in the hope that it will be useful,
10 | but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 | GNU General Public License for more details.
13 |
14 | You should have received a copy of the GNU General Public License
15 | along with FeedWatcher. If not, see .
16 | Copyright 2019 Zouroboros
17 | */
18 | package me.murks.feedwatcher.activities
19 |
20 | import android.content.Context
21 | import android.text.format.DateFormat
22 | import java.util.*
23 |
24 | /**
25 | * Formats value into strings that can be shown in the ui
26 | * @author zouroboros
27 | */
28 | object Formatter {
29 | @JvmStatic
30 | fun dateToString(context: Context, value: Date): String {
31 | return "${DateFormat.getDateFormat(context).format(value)} " +
32 | DateFormat.getTimeFormat(context).format(value)
33 | }
34 |
35 | }
--------------------------------------------------------------------------------
/app/src/main/java/me/murks/feedwatcher/activities/ListRecyclerViewAdapter.kt:
--------------------------------------------------------------------------------
1 | /*
2 | This file is part of FeedWatcher.
3 |
4 | FeedWatcher is free software: you can redistribute it and/or modify
5 | it under the terms of the GNU General Public License as published by
6 | the Free Software Foundation, either version 3 of the License, or
7 | (at your option) any later version.
8 |
9 | FeedWatcher is distributed in the hope that it will be useful,
10 | but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 | GNU General Public License for more details.
13 |
14 | You should have received a copy of the GNU General Public License
15 | along with FeedWatcher. If not, see .
16 | Copyright 2019 Zouroboros
17 | */
18 | package me.murks.feedwatcher.activities
19 |
20 | import androidx.recyclerview.widget.RecyclerView
21 | import java.util.*
22 |
23 | /**
24 | * Base class for list adapter
25 | * @author zouroboros
26 | */
27 | abstract class ListRecyclerViewAdapter(items: List):
28 | RecyclerView.Adapter() {
29 |
30 | private var list: MutableList = LinkedList(items)
31 |
32 | var items: MutableList
33 | set(value) {
34 | list = value
35 | notifyDataSetChanged()
36 | }
37 | get() = list
38 |
39 | final override fun getItemCount() = items.size
40 |
41 | fun append(item: I) {
42 | list.add(item)
43 | notifyItemInserted(list.size - 1)
44 | }
45 | }
--------------------------------------------------------------------------------
/app/src/main/java/me/murks/feedwatcher/activities/PreferencesActivity.kt:
--------------------------------------------------------------------------------
1 | package me.murks.feedwatcher.activities
2 |
3 | import android.app.Activity
4 | import android.content.Intent
5 | import android.os.Bundle
6 | import android.widget.Toast
7 | import me.murks.feedwatcher.R
8 | import me.murks.feedwatcher.databinding.PreferencesActivityBinding
9 | import me.murks.feedwatcher.tasks.Tasks
10 | import java.io.FileOutputStream
11 |
12 | class PreferencesActivity : FeedWatcherBaseActivity() {
13 |
14 | companion object {
15 | const val DATABASE_EXPORT_SELECT_FILE_REQUEST_CODE = 1234
16 | }
17 |
18 | private lateinit var binding: PreferencesActivityBinding
19 |
20 | override fun onCreate(savedInstanceState: Bundle?) {
21 | super.onCreate(savedInstanceState)
22 | binding = PreferencesActivityBinding.inflate(layoutInflater)
23 | setContentView(binding.root)
24 |
25 | supportActionBar?.setDisplayHomeAsUpEnabled(true)
26 |
27 | binding.openAboutActivityButton.setOnClickListener {
28 | val intent = Intent(this, AboutActivity::class.java)
29 | startActivity(intent)
30 | }
31 |
32 | binding.exportDatabaseButton.setOnClickListener {
33 | val intent = Intent()
34 | intent.action = Intent.ACTION_CREATE_DOCUMENT
35 | intent.type = "*/*"
36 | startActivityForResult(Intent.createChooser(intent,
37 | resources.getString(R.string.export_database)),
38 | DATABASE_EXPORT_SELECT_FILE_REQUEST_CODE)
39 | }
40 | }
41 |
42 | override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
43 | super.onActivityResult(requestCode, resultCode, data)
44 |
45 | if (requestCode == DATABASE_EXPORT_SELECT_FILE_REQUEST_CODE) {
46 | if (resultCode == Activity.RESULT_OK) {
47 | val selectedFile = data!!.data!!
48 | Tasks.run({
49 | val file = contentResolver.openFileDescriptor(selectedFile, "w")
50 | FileOutputStream(file!!.fileDescriptor).use {
51 | app.exportDatabase(it)
52 | }
53 | }, {
54 | Toast.makeText(this, R.string.database_successfully_exported, Toast.LENGTH_SHORT).show()
55 | }, {
56 | Toast.makeText(this, R.string.database_export_failed, Toast.LENGTH_SHORT).show()
57 | app.environment.log.error("Database export to ${selectedFile.path} failed.", it)
58 | }).execute(Unit)
59 |
60 | }
61 | }
62 | }
63 | }
--------------------------------------------------------------------------------
/app/src/main/java/me/murks/feedwatcher/activities/PreferencesFragment.kt:
--------------------------------------------------------------------------------
1 | /*
2 | This file is part of FeedWatcher.
3 |
4 | FeedWatcher is free software: you can redistribute it and/or modify
5 | it under the terms of the GNU General Public License as published by
6 | the Free Software Foundation, either version 3 of the License, or
7 | (at your option) any later version.
8 |
9 | FeedWatcher is distributed in the hope that it will be useful,
10 | but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 | GNU General Public License for more details.
13 |
14 | You should have received a copy of the GNU General Public License
15 | along with FeedWatcher. If not, see .
16 | Copyright 2019 - 2021 Zouroboros
17 | */
18 | package me.murks.feedwatcher.activities
19 |
20 |
21 | import android.content.SharedPreferences
22 | import android.os.Bundle
23 | import androidx.preference.Preference
24 | import androidx.preference.PreferenceFragmentCompat
25 | import androidx.preference.SeekBarPreference
26 | import me.murks.feedwatcher.AndroidEnvironment
27 | import me.murks.feedwatcher.Constants
28 | import me.murks.feedwatcher.FeedWatcherApp
29 | import me.murks.feedwatcher.R
30 |
31 | class PreferencesFragment : PreferenceFragmentCompat(), Preference.OnPreferenceChangeListener,
32 | SharedPreferences.OnSharedPreferenceChangeListener {
33 |
34 | private lateinit var app: FeedWatcherApp
35 |
36 | override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
37 | setPreferencesFromResource(R.xml.preferences, rootKey)
38 |
39 | app = FeedWatcherApp(AndroidEnvironment(requireContext()))
40 |
41 | val scanInterval = findPreference(Constants.scanIntervalIdPreferencesKey)!!
42 | val intervals = app.getCurrentScanIntervals()
43 | scanInterval.onPreferenceChangeListener = this
44 | scanInterval.min = 0
45 | scanInterval.max = intervals.size - 1
46 |
47 | val minutesSummary = resources.getString(R.string.preferences_scanning_interval_summary_minutes)
48 | val hoursSummary = resources.getString(R.string.preferences_scanning_interval_summary_hours)
49 | val hoursMinutes = resources.getString(R.string.preferences_scanning_interval_summary_hours_minutes)
50 |
51 | scanInterval.summaryProvider = Preference.SummaryProvider {
52 | val interval = intervals[it.value]
53 | when {
54 | interval.hours == 0 -> String.format(minutesSummary, interval.minutes)
55 | interval.minutes == 0 -> String.format(hoursSummary, interval.hours)
56 | else -> String.format(hoursMinutes, interval.hours, interval.minutes)
57 | }
58 | }
59 |
60 | val scanIntervalInfo = findPreference(Constants.scanIntervalInfo)!!
61 | scanIntervalInfo.title = String.format(scanIntervalInfo.title.toString(),
62 | app.environment.jobs.minimumInterval / 1000 / 60,
63 | app.environment.jobs.intervalAccuracy / 1000 / 60)
64 | }
65 |
66 | override fun onPreferenceChange(preference: Preference, newValue: Any): Boolean {
67 | if(preference is SeekBarPreference) {
68 | // the SeekBarPreference is not updating its summary by itself -> we need to do it manually
69 | preference.value = newValue as Int
70 | }
71 | return true
72 | }
73 |
74 | override fun onResume() {
75 | super.onResume()
76 | preferenceManager.sharedPreferences.registerOnSharedPreferenceChangeListener(this)
77 | }
78 |
79 | override fun onPause() {
80 | super.onPause()
81 | preferenceManager.sharedPreferences.registerOnSharedPreferenceChangeListener(this)
82 | }
83 |
84 | override fun onSharedPreferenceChanged(sharedPreferences: SharedPreferences, key: String) {
85 | when (key) {
86 | Constants.scanIntervalIdPreferencesKey -> app.rescheduleJobs()
87 | Constants.backgroundScanningPreferencesKey -> app.rescheduleJobs()
88 | }
89 | }
90 |
91 | override fun onDestroy() {
92 | super.onDestroy()
93 | app.environment.close()
94 | }
95 | }
96 |
--------------------------------------------------------------------------------
/app/src/main/java/me/murks/feedwatcher/activities/QueriesFragment.kt:
--------------------------------------------------------------------------------
1 | /*
2 | This file is part of FeedWatcher.
3 |
4 | FeedWatcher is free software: you can redistribute it and/or modify
5 | it under the terms of the GNU General Public License as published by
6 | the Free Software Foundation, either version 3 of the License, or
7 | (at your option) any later version.
8 |
9 | FeedWatcher is distributed in the hope that it will be useful,
10 | but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 | GNU General Public License for more details.
13 |
14 | You should have received a copy of the GNU General Public License
15 | along with FeedWatcher. If not, see .
16 | Copyright 2019-2020 Zouroboros
17 | */
18 | package me.murks.feedwatcher.activities
19 |
20 | import android.content.Context
21 | import android.os.Bundle
22 | import android.view.LayoutInflater
23 | import android.view.View
24 | import android.view.ViewGroup
25 | import android.widget.ProgressBar
26 | import androidx.recyclerview.widget.DividerItemDecoration
27 | import androidx.recyclerview.widget.ItemTouchHelper
28 | import androidx.recyclerview.widget.RecyclerView
29 | import me.murks.feedwatcher.R
30 | import me.murks.feedwatcher.model.Query
31 |
32 | /**
33 | * A fragment representing a list of Queries.
34 | */
35 | class QueriesFragment : FeedWatcherAsyncLoadingFragment() {
36 |
37 | private var listener: OnListFragmentInteractionListener? = null
38 | private lateinit var adapter: QueryRecyclerViewAdapter
39 | private lateinit var progressBar: ProgressBar
40 |
41 | override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?,
42 | savedInstanceState: Bundle?): View? {
43 | val view = inflater.inflate(R.layout.fragment_queries_list, container, false)
44 | progressBar = view.findViewById(R.id.queries_fragment_progress_bar)
45 | val queryList = view.findViewById(R.id.queries_fragment_query_list)
46 |
47 | adapter = QueryRecyclerViewAdapter(app.queries(), listener)
48 |
49 | queryList.layoutManager = androidx.recyclerview.widget.LinearLayoutManager(context)
50 | queryList.adapter = adapter
51 |
52 | queryList.addItemDecoration(DividerItemDecoration(context, DividerItemDecoration.VERTICAL))
53 |
54 | val swipeHelper = ItemTouchHelper(
55 | object : ItemTouchHelper.SimpleCallback(0, ItemTouchHelper.RIGHT) {
56 | override fun onMove(recyclerView: RecyclerView,
57 | viewHolder: RecyclerView.ViewHolder,
58 | target: RecyclerView.ViewHolder): Boolean {
59 | return false
60 | }
61 |
62 | override fun onSwiped(viewHolder: RecyclerView.ViewHolder, direction: Int) {
63 | app.delete(adapter.items[viewHolder.adapterPosition])
64 | adapter.items.removeAt(viewHolder.adapterPosition)
65 | adapter.notifyItemRemoved(viewHolder.adapterPosition)
66 | }
67 | })
68 |
69 | swipeHelper.attachToRecyclerView(queryList)
70 |
71 | return view
72 | }
73 |
74 | override fun onAttach(context: Context) {
75 | super.onAttach(context)
76 | if (context is OnListFragmentInteractionListener) {
77 | listener = context
78 | }
79 | }
80 |
81 | override fun onDetach() {
82 | super.onDetach()
83 | listener = null
84 | }
85 |
86 | override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
87 | super.onViewCreated(view, savedInstanceState)
88 | reload()
89 | }
90 |
91 | override fun onResume() {
92 | super.onResume()
93 | reload()
94 | }
95 |
96 | interface OnListFragmentInteractionListener {
97 | fun onOpenQuery(item: Query)
98 | }
99 |
100 | override fun loadData() {
101 | for (query in app.queries()) {
102 | publishResult(query)
103 | }
104 | }
105 |
106 | override fun processResult(result: Query) {
107 | adapter.append(result)
108 | }
109 |
110 | override fun onLoadingStart() {
111 | progressBar.visibility = View.VISIBLE
112 | adapter.items.clear()
113 | adapter.notifyDataSetChanged()
114 | }
115 |
116 | override fun onLoadingFinished() {
117 | progressBar.visibility = View.GONE
118 | }
119 |
120 | }
121 |
--------------------------------------------------------------------------------
/app/src/main/java/me/murks/feedwatcher/activities/QueryRecyclerViewAdapter.kt:
--------------------------------------------------------------------------------
1 | package me.murks.feedwatcher.activities
2 |
3 | import android.view.LayoutInflater
4 | import android.view.View
5 | import android.view.ViewGroup
6 | import android.widget.TextView
7 |
8 | import me.murks.feedwatcher.R
9 | import me.murks.feedwatcher.databinding.FragmentQueriesListItemBinding
10 |
11 | import me.murks.feedwatcher.activities.QueriesFragment.OnListFragmentInteractionListener
12 |
13 | import me.murks.feedwatcher.model.Query
14 |
15 | class QueryRecyclerViewAdapter(queries: List,
16 | private val listener: OnListFragmentInteractionListener?)
17 | : ListRecyclerViewAdapter(queries) {
18 |
19 | private val onClickListener: View.OnClickListener
20 |
21 | init {
22 | onClickListener = View.OnClickListener { v ->
23 | val item = v.tag as Query
24 | listener?.onOpenQuery(item)
25 | }
26 | }
27 |
28 | override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
29 | val view = LayoutInflater.from(parent.context)
30 | .inflate(R.layout.fragment_queries_list_item, parent, false)
31 | return ViewHolder(view)
32 | }
33 |
34 | override fun onBindViewHolder(holder: ViewHolder, position: Int) {
35 | val item = items[position]
36 | holder.name.text = item.name
37 |
38 | with(holder.mView) {
39 | tag = item
40 | setOnClickListener(onClickListener)
41 | }
42 | }
43 |
44 | inner class ViewHolder(val mView: View) : androidx.recyclerview.widget.RecyclerView.ViewHolder(mView) {
45 | val binding = FragmentQueriesListItemBinding.bind(mView)
46 | val name: TextView = binding.queryName
47 |
48 | override fun toString(): String {
49 | return super.toString() + " '" + name.text + "'"
50 | }
51 | }
52 | }
53 |
--------------------------------------------------------------------------------
/app/src/main/java/me/murks/feedwatcher/activities/ResultActivity.kt:
--------------------------------------------------------------------------------
1 | package me.murks.feedwatcher.activities
2 |
3 | import android.os.Bundle
4 | import android.text.format.DateFormat
5 | import android.webkit.WebView
6 | import android.widget.TextView
7 | import me.murks.feedwatcher.HtmlTags
8 | import me.murks.feedwatcher.R
9 | import me.murks.feedwatcher.itemDescription
10 |
11 | class ResultActivity : FeedWatcherBaseActivity() {
12 |
13 | private lateinit var resultName: TextView
14 | private lateinit var resultDescription: WebView
15 | private lateinit var resultFeed: TextView
16 | private lateinit var resultDate: TextView
17 | private lateinit var resultLink: TextView
18 |
19 | override fun onCreate(savedInstanceState: Bundle?) {
20 | super.onCreate(savedInstanceState)
21 | setContentView(R.layout.activity_result)
22 |
23 | resultName = findViewById(R.id.result_result_name)
24 | resultDescription = findViewById(R.id.result_result_description)
25 | resultFeed = findViewById(R.id.result_result_feed)
26 | resultDate = findViewById(R.id.result_result_date)
27 | resultLink = findViewById(R.id.result_result_link)
28 |
29 | val resultId = intent.getLongExtra(RESULT_EXTRA_NAME, -1)
30 | val result = app.result(resultId)
31 |
32 | resultName.text = result.item.title
33 | resultDescription.loadData(HtmlTags.wrapInDocument(result.item.itemDescription(this),
34 | backgroundColor()), "text/html", "UTF-8")
35 | resultFeed.text = result.feed.name
36 | resultDate.text = DateFormat.getDateFormat(this).format(result.found) +
37 | " " + DateFormat.getTimeFormat(this).format(result.found)
38 | resultLink.text = result.item.link?.resolve(result.feed.url.toURI())?.toString() ?: ""
39 |
40 | app.markAsRead(result)
41 | }
42 |
43 | private fun backgroundColor(): Int {
44 | val array = theme.obtainStyledAttributes(arrayOf(android.R.attr.colorBackground).toIntArray())
45 | return array.getColor(0, 0)
46 | }
47 |
48 | companion object {
49 | const val RESULT_EXTRA_NAME = "result"
50 | }
51 | }
52 |
--------------------------------------------------------------------------------
/app/src/main/java/me/murks/feedwatcher/activities/ResultsRecyclerViewAdapter.kt:
--------------------------------------------------------------------------------
1 | /*
2 | This file is part of FeedWatcher.
3 |
4 | FeedWatcher is free software: you can redistribute it and/or modify
5 | it under the terms of the GNU General Public License as published by
6 | the Free Software Foundation, either version 3 of the License, or
7 | (at your option) any later version.
8 |
9 | FeedWatcher is distributed in the hope that it will be useful,
10 | but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 | GNU General Public License for more details.
13 |
14 | You should have received a copy of the GNU General Public License
15 | along with FeedWatcher. If not, see .
16 | Copyright 2019 - 2021 Zouroboros
17 | */
18 | package me.murks.feedwatcher.activities
19 |
20 | import android.graphics.Typeface
21 | import android.text.format.DateFormat
22 | import android.view.LayoutInflater
23 | import android.view.View
24 | import android.view.ViewGroup
25 | import me.murks.feedwatcher.*
26 |
27 | import me.murks.feedwatcher.databinding.FragmentResultsListItemBinding
28 | import me.murks.feedwatcher.activities.ResultsFragment.OnListFragmentInteractionListener
29 | import me.murks.feedwatcher.model.Result
30 |
31 | /**
32 | * Adapter for displaying a list of results
33 | */
34 | class ResultsRecyclerViewAdapter(
35 | results: List,
36 | private val listener: OnListFragmentInteractionListener?)
37 | : ListRecyclerViewAdapter(results) {
38 |
39 | private val onClickListener: View.OnClickListener
40 |
41 | init {
42 | onClickListener = View.OnClickListener { v ->
43 | val item = v.tag as Result
44 | listener?.onOpenResult(item)
45 | }
46 | }
47 |
48 | override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
49 | val view = LayoutInflater.from(parent.context)
50 | .inflate(R.layout.fragment_results_list_item, parent, false)
51 | return ViewHolder(view)
52 | }
53 |
54 | override fun onBindViewHolder(holder: ViewHolder, position: Int) {
55 | val result = items[position]
56 | holder.resultName.text = result.item.title
57 | holder.resultDescription.text = Texts.preview(
58 | HtmlTags.text(result.item.itemDescription(holder.mView.context)), 200, "...")
59 | holder.resultFeed.text = result.feed.name
60 | holder.resultDate.text = DateFormat.getDateFormat(holder.mView.context).format(result.found)
61 |
62 | if (result.unread) {
63 | holder.resultName.typeface = Typeface.defaultFromStyle(Typeface.BOLD)
64 | holder.resultDescription.typeface = Typeface.defaultFromStyle(Typeface.BOLD)
65 | holder.resultFeed.typeface = Typeface.defaultFromStyle(Typeface.BOLD)
66 | holder.resultDate.typeface = Typeface.defaultFromStyle(Typeface.BOLD)
67 | holder.foundIn.typeface = Typeface.defaultFromStyle(Typeface.BOLD)
68 | } else {
69 | holder.resultName.typeface = Typeface.defaultFromStyle(Typeface.NORMAL)
70 | holder.resultDescription.typeface = Typeface.defaultFromStyle(Typeface.NORMAL)
71 | holder.resultFeed.typeface = Typeface.defaultFromStyle(Typeface.NORMAL)
72 | holder.resultDate.typeface = Typeface.defaultFromStyle(Typeface.NORMAL)
73 | holder.foundIn.typeface = Typeface.defaultFromStyle(Typeface.NORMAL)
74 | }
75 |
76 | with(holder.mView) {
77 | tag = result
78 | setOnClickListener(onClickListener)
79 | }
80 | }
81 |
82 | inner class ViewHolder(val mView: View) : androidx.recyclerview.widget.RecyclerView.ViewHolder(mView) {
83 | private val binding = FragmentResultsListItemBinding.bind(mView)
84 | val resultName = binding.resultsResultName
85 | val resultDescription = binding.resultsResultDescription
86 | val resultFeed = binding.resultsResultFeed
87 | val resultDate = binding.resultsResultDate
88 | val foundIn = binding.resultsFoundIn
89 | }
90 | }
91 |
--------------------------------------------------------------------------------
/app/src/main/java/me/murks/feedwatcher/activities/SelectableRecyclerViewAdapter.kt:
--------------------------------------------------------------------------------
1 | /*
2 | This file is part of FeedWatcher.
3 |
4 | FeedWatcher is free software: you can redistribute it and/or modify
5 | it under the terms of the GNU General Public License as published by
6 | the Free Software Foundation, either version 3 of the License, or
7 | (at your option) any later version.
8 |
9 | FeedWatcher is distributed in the hope that it will be useful,
10 | but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 | GNU General Public License for more details.
13 |
14 | You should have received a copy of the GNU General Public License
15 | along with FeedWatcher. If not, see .
16 | Copyright 2021 Zouroboros
17 | */
18 | package me.murks.feedwatcher.activities
19 |
20 | import androidx.recyclerview.widget.RecyclerView
21 |
22 | /**
23 | * Abstract class that provides a basic framework for selectable lists.
24 | *
25 | * @author zouroboros
26 | */
27 | abstract class SelectableRecyclerViewAdapter(
28 | items: List):
29 | ListRecyclerViewAdapter(items) {
30 |
31 | val selectedItems = mutableSetOf()
32 |
33 | fun select(itemIndex: Int) {
34 | if(selectedItems.add(items[itemIndex])) {
35 | notifyItemChanged(itemIndex)
36 | }
37 | }
38 |
39 | fun deselect(itemIndex: Int) {
40 | if(selectedItems.remove(items[itemIndex])) {
41 | notifyItemChanged(itemIndex)
42 | }
43 | }
44 |
45 | fun selectAll() {
46 | if(selectedItems.addAll(items)) {
47 | notifyDataSetChanged()
48 | }
49 | }
50 |
51 | fun deselectAll() {
52 | if(selectedItems.removeAll(items)) {
53 | notifyDataSetChanged()
54 | }
55 | }
56 | }
--------------------------------------------------------------------------------
/app/src/main/java/me/murks/feedwatcher/data/AddFeeds.kt:
--------------------------------------------------------------------------------
1 | package me.murks.feedwatcher.data
2 |
3 | import me.murks.feedwatcher.model.Feed
4 |
5 | /**
6 | * Unit of Work for adding feeds
7 | * @author zouroboros
8 | */
9 | class AddFeeds(private val feeds: Collection): UnitOfWork {
10 | override fun execute(store: DataStore) {
11 | store.startTransaction()
12 | feeds.forEach { feed ->
13 | store.addFeed(feed)
14 | }
15 | store.commitTransaction()
16 | }
17 | }
--------------------------------------------------------------------------------
/app/src/main/java/me/murks/feedwatcher/data/ClearResults.kt:
--------------------------------------------------------------------------------
1 | /*
2 | This file is part of FeedWatcher.
3 |
4 | FeedWatcher is free software: you can redistribute it and/or modify
5 | it under the terms of the GNU General Public License as published by
6 | the Free Software Foundation, either version 3 of the License, or
7 | (at your option) any later version.
8 |
9 | FeedWatcher is distributed in the hope that it will be useful,
10 | but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 | GNU General Public License for more details.
13 |
14 | You should have received a copy of the GNU General Public License
15 | along with FeedWatcher. If not, see .
16 | Copyright 2020 Zouroboros
17 | */
18 | package me.murks.feedwatcher.data
19 |
20 | /**
21 | * @author zouroboros
22 | */
23 | class ClearResults : UnitOfWork{
24 | override fun execute(store: DataStore) {
25 | store.startTransaction()
26 | for (result in store.getResults()) {
27 | store.delete(result)
28 | }
29 | store.commitTransaction()
30 | }
31 |
32 | }
--------------------------------------------------------------------------------
/app/src/main/java/me/murks/feedwatcher/data/Cursors.kt:
--------------------------------------------------------------------------------
1 | /*
2 | This file is part of FeedWatcher.
3 |
4 | FeedWatcher is free software: you can redistribute it and/or modify
5 | it under the terms of the GNU General Public License as published by
6 | the Free Software Foundation, either version 3 of the License, or
7 | (at your option) any later version.
8 |
9 | FeedWatcher is distributed in the hope that it will be useful,
10 | but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 | GNU General Public License for more details.
13 |
14 | You should have received a copy of the GNU General Public License
15 | along with FeedWatcher. If not, see .
16 | Copyright 2020 - 2021 Zouroboros
17 | */
18 | package me.murks.feedwatcher.data
19 |
20 | import android.database.Cursor
21 | import java.util.*
22 |
23 | /**
24 | * Utility functions for {@see android.database.Cursor} objects
25 | * @author zouroboros
26 | */
27 |
28 | /**
29 | * Reads the values of one column into a list
30 | */
31 | fun Cursor.getColumnValues(columnName: String, f: Function2): Collection {
32 | val values = LinkedList()
33 |
34 | while (this.moveToNext()) {
35 | values.add(f(this, this.getColumnIndex(columnName)))
36 | }
37 |
38 | return values
39 | }
40 |
41 | fun Cursor.selectCount(): Int {
42 | return this.use {
43 | it.moveToFirst()
44 | return@use it.getInt(0)
45 | }
46 | }
--------------------------------------------------------------------------------
/app/src/main/java/me/murks/feedwatcher/data/DeleteFeed.kt:
--------------------------------------------------------------------------------
1 | /*
2 | This file is part of FeedWatcher.
3 |
4 | FeedWatcher is free software: you can redistribute it and/or modify
5 | it under the terms of the GNU General Public License as published by
6 | the Free Software Foundation, either version 3 of the License, or
7 | (at your option) any later version.
8 |
9 | FeedWatcher is distributed in the hope that it will be useful,
10 | but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 | GNU General Public License for more details.
13 |
14 | You should have received a copy of the GNU General Public License
15 | along with FeedWatcher. If not, see .
16 | Copyright 2021 Zouroboros
17 | */
18 | package me.murks.feedwatcher.data
19 |
20 | import me.murks.feedwatcher.model.Feed
21 |
22 | /**
23 | * Implements the logic for deleting feeds.
24 | *
25 | * @author zouroboros
26 | */
27 | class DeleteFeed(val feed: Feed): UnitOfWork {
28 | override fun execute(store: DataStore) {
29 | store.startTransaction()
30 | val results = store.getResultsForFeed(feed)
31 | val scans = store.getScansForFeed(feed)
32 |
33 | // in any case we delete all scans
34 | for (scan in scans) {
35 | store.delete(scan)
36 | }
37 |
38 | // now if a feed still has results we only mark the feed as deleted
39 | // if not we can delete the feed directly
40 | if (results.isEmpty()) {
41 | store.delete(feed)
42 | } else {
43 | store.markDeleted(feed)
44 | }
45 |
46 | store.commitTransaction()
47 | }
48 | }
--------------------------------------------------------------------------------
/app/src/main/java/me/murks/feedwatcher/data/DeleteResult.kt:
--------------------------------------------------------------------------------
1 | /*
2 | This file is part of FeedWatcher.
3 |
4 | FeedWatcher is free software: you can redistribute it and/or modify
5 | it under the terms of the GNU General Public License as published by
6 | the Free Software Foundation, either version 3 of the License, or
7 | (at your option) any later version.
8 |
9 | FeedWatcher is distributed in the hope that it will be useful,
10 | but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 | GNU General Public License for more details.
13 |
14 | You should have received a copy of the GNU General Public License
15 | along with FeedWatcher. If not, see .
16 | Copyright 2020 Zouroboros
17 | */
18 | package me.murks.feedwatcher.data
19 |
20 | import me.murks.feedwatcher.model.*
21 |
22 | /**
23 | * @author zouroboros
24 | */
25 | class DeleteResult(val result: Result) : UnitOfWork {
26 | override fun execute(store: DataStore) {
27 | store.startTransaction()
28 | store.delete(result)
29 | store.commitTransaction()
30 | }
31 | }
--------------------------------------------------------------------------------
/app/src/main/java/me/murks/feedwatcher/data/FeedWatcherSchema.java:
--------------------------------------------------------------------------------
1 | /*
2 | This file is part of FeedWatcher.
3 |
4 | FeedWatcher is free software: you can redistribute it and/or modify
5 | it under the terms of the GNU General Public License as published by
6 | the Free Software Foundation, either version 3 of the License, or
7 | (at your option) any later version.
8 |
9 | FeedWatcher is distributed in the hope that it will be useful,
10 | but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 | GNU General Public License for more details.
13 |
14 | You should have received a copy of the GNU General Public License
15 | along with FeedWatcher. If not, see .
16 | Copyright 2020 - 2022 Zouroboros
17 | */
18 | package me.murks.feedwatcher.data;
19 |
20 | import android.database.sqlite.SQLiteDatabase;
21 | import android.util.Log;
22 |
23 | import me.murks.sqlschemaspec.ColumnSpec;
24 | import me.murks.sqlschemaspec.SchemaSpec;
25 | import me.murks.sqlschemaspec.TableSpec;
26 | import me.murks.sqlschemaspec.Type;
27 | import me.murks.sqlschemaspec.templates.TemplateCompiler;
28 |
29 | /**
30 | * Database schema of the FeedWatcherApp
31 | * @author zouroboros
32 | */
33 | public class FeedWatcherSchema extends SchemaSpec {
34 |
35 | class Feeds extends TableSpec {
36 | ColumnSpec id = primaryKey(Type.Integer);
37 | ColumnSpec name = column(Type.String);
38 | ColumnSpec url = column(Type.String);
39 | ColumnSpec lastUpdated = column(Type.Integer, true);
40 | ColumnSpec deleted = column(Type.Boolean);
41 | }
42 |
43 | Feeds feeds = new Feeds();
44 |
45 | class Queries extends TableSpec {
46 | ColumnSpec id = primaryKey(Type.Integer);
47 | ColumnSpec name = column(Type.String);
48 | ColumnSpec deleted = column(Type.Boolean);
49 | }
50 |
51 | Queries queries = new Queries();
52 |
53 | class Filters extends TableSpec {
54 | ColumnSpec id = primaryKey(Type.Integer);
55 | ColumnSpec type = column(Type.String);
56 | ColumnSpec index = column(Type.Integer);
57 | ColumnSpec queryId = foreignKey(queries.id);
58 | }
59 |
60 | Filters filters = new Filters();
61 |
62 | /**
63 | * Table that connects filters and their parameter
64 | */
65 | class FilterParameters extends TableSpec {
66 | ColumnSpec id = primaryKey(Type.Integer);
67 | ColumnSpec name = column(Type.String);
68 | ColumnSpec stringValue = column(Type.String, true);
69 | ColumnSpec filterId = foreignKey(filters.id);
70 | ColumnSpec dateValue = column(Type.Integer, true);
71 | }
72 |
73 | FilterParameters filterParameters = new FilterParameters();
74 |
75 | class Results extends TableSpec {
76 | ColumnSpec id = primaryKey(Type.Integer);
77 | ColumnSpec feedId = foreignKey(feeds.id);
78 | ColumnSpec title = column(Type.String);
79 | ColumnSpec description = column(Type.String, true);
80 | ColumnSpec link = column(Type.String, true);
81 | ColumnSpec date = column(Type.Integer, true);
82 | ColumnSpec found = column(Type.Integer);
83 | ColumnSpec unread = column(Type.Boolean);
84 | }
85 |
86 | Results results = new Results();
87 |
88 | /**
89 | * Table that connects results and the query that produced the result.
90 | */
91 | class ResultQueries extends TableSpec {
92 | ColumnSpec id = primaryKey(Type.Integer);
93 | ColumnSpec resultId = foreignKey(results.id);
94 | ColumnSpec queryId = foreignKey(queries.id);
95 | }
96 |
97 | ResultQueries resultQueries = new ResultQueries();
98 |
99 | class Scans extends TableSpec {
100 | ColumnSpec id = primaryKey(Type.Integer);
101 | ColumnSpec feedId = foreignKey(feeds.id);
102 | ColumnSpec successfully = column(Type.Boolean);
103 | ColumnSpec errorText = column(Type.String, true);
104 | ColumnSpec scanDate = column(Type.Integer);
105 | }
106 |
107 | Scans scans = new Scans();
108 |
109 | public FeedWatcherSchema() {
110 | TemplateCompiler compiler = new TemplateCompiler();
111 | compiler.compileTemplate(this, this);
112 | }
113 |
114 | public void createSchema(SQLiteDatabase db) {
115 | db.beginTransaction();
116 | for (String statement: createStatement()) {
117 | Log.d(getClass().toString(), statement);
118 | db.execSQL(statement);
119 | }
120 | db.setTransactionSuccessful();
121 | db.endTransaction();
122 | }
123 | }
124 |
--------------------------------------------------------------------------------
/app/src/main/java/me/murks/feedwatcher/data/RecordScan.kt:
--------------------------------------------------------------------------------
1 | /*
2 | This file is part of FeedWatcher.
3 |
4 | FeedWatcher is free software: you can redistribute it and/or modify
5 | it under the terms of the GNU General Public License as published by
6 | the Free Software Foundation, either version 3 of the License, or
7 | (at your option) any later version.
8 |
9 | FeedWatcher is distributed in the hope that it will be useful,
10 | but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 | GNU General Public License for more details.
13 |
14 | You should have received a copy of the GNU General Public License
15 | along with FeedWatcher. If not, see .
16 | Copyright 2019 Zouroboros
17 | */
18 | package me.murks.feedwatcher.data
19 |
20 | import me.murks.feedwatcher.model.*
21 |
22 | import java.util.*
23 |
24 | /**
25 | * Unit of work to insert the results and mark all feeds as updated.
26 | * @author zouroboros
27 | */
28 | class RecordScan(private val results: List, private val scans: List, private val updateDate: Date) : UnitOfWork {
29 | /**
30 | * Maximum number of scans that is kept for each feed.
31 | */
32 | private val MaxScansForFeed = 3
33 |
34 | override fun execute(store: DataStore) {
35 | val feeds = store.getFeeds()
36 | val newFeeds = feeds.map { Feed(it.url, updateDate, it.name) }
37 |
38 | store.startTransaction()
39 |
40 | for (result in results) {
41 | store.addResult(result)
42 | }
43 |
44 | for (feed in newFeeds) {
45 | store.updateFeed(feed)
46 | }
47 |
48 | for (scan in scans) {
49 | store.addScan(scan)
50 | }
51 |
52 | for (feed in feeds) {
53 | val scans = store.getScansForFeed(feed)
54 | if (scans.size > MaxScansForFeed) {
55 | val numberOfScansToDelete = scans.size - MaxScansForFeed
56 | val scansToDelete = scans
57 | .sortedBy { it.scanDate }
58 | .take(numberOfScansToDelete)
59 | for (scan in scansToDelete) {
60 | store.delete(scan)
61 | }
62 | }
63 | }
64 | store.commitTransaction()
65 | }
66 | }
--------------------------------------------------------------------------------
/app/src/main/java/me/murks/feedwatcher/data/UnitOfWork.kt:
--------------------------------------------------------------------------------
1 | /*
2 | This file is part of FeedWatcher.
3 |
4 | FeedWatcher is free software: you can redistribute it and/or modify
5 | it under the terms of the GNU General Public License as published by
6 | the Free Software Foundation, either version 3 of the License, or
7 | (at your option) any later version.
8 |
9 | FeedWatcher is distributed in the hope that it will be useful,
10 | but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 | GNU General Public License for more details.
13 |
14 | You should have received a copy of the GNU General Public License
15 | along with FeedWatcher. If not, see .
16 | Copyright 2019 Zouroboros
17 | */
18 | package me.murks.feedwatcher.data
19 |
20 | /**
21 | * Interface for objects that represent units of work for the database e.g: Queries, Inserts, Deletes
22 | * @author zouroboros
23 | */
24 | interface UnitOfWork {
25 | fun execute(store: DataStore)
26 | }
--------------------------------------------------------------------------------
/app/src/main/java/me/murks/feedwatcher/data/UpdateResult.kt:
--------------------------------------------------------------------------------
1 | /*
2 | This file is part of FeedWatcher.
3 |
4 | FeedWatcher is free software: you can redistribute it and/or modify
5 | it under the terms of the GNU General Public License as published by
6 | the Free Software Foundation, either version 3 of the License, or
7 | (at your option) any later version.
8 |
9 | FeedWatcher is distributed in the hope that it will be useful,
10 | but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 | GNU General Public License for more details.
13 |
14 | You should have received a copy of the GNU General Public License
15 | along with FeedWatcher. If not, see .
16 | Copyright 2022 Zouroboros
17 | */
18 | package me.murks.feedwatcher.data
19 |
20 | import me.murks.feedwatcher.model.*
21 |
22 | /**
23 | * @author zouroboros
24 | */
25 | class UpdateResult(val result: Result) : UnitOfWork {
26 | override fun execute(store: DataStore) {
27 | store.startTransaction()
28 | store.update(result)
29 | store.commitTransaction()
30 | }
31 | }
--------------------------------------------------------------------------------
/app/src/main/java/me/murks/feedwatcher/model/ContainsFilter.kt:
--------------------------------------------------------------------------------
1 | /*
2 | This file is part of FeedWatcher.
3 |
4 | FeedWatcher is free software: you can redistribute it and/or modify
5 | it under the terms of the GNU General Public License as published by
6 | the Free Software Foundation, either version 3 of the License, or
7 | (at your option) any later version.
8 |
9 | FeedWatcher is distributed in the hope that it will be useful,
10 | but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 | GNU General Public License for more details.
13 |
14 | You should have received a copy of the GNU General Public License
15 | along with FeedWatcher. If not, see .
16 | Copyright 2019 - 2021 Zouroboros
17 | */
18 | package me.murks.feedwatcher.model
19 |
20 | import me.murks.feedwatcher.atomrss.FeedItem
21 |
22 | class ContainsFilter(index: Int, val text: String?): Filter(FilterType.CONTAINS, index) {
23 |
24 | companion object {
25 | const val textParameterName = "text"
26 | }
27 |
28 | override fun filterItems(feed: Feed, items: List): List {
29 | return items.filter { it.title?.contains(text ?: "", true) == true
30 | || it.description?.contains(text ?: "", true) == true }
31 | }
32 |
33 | override fun filterCallback(callback: FilterTypeCallback): R {
34 | return callback.filter(this)
35 | }
36 |
37 | override fun parameter(): List {
38 | return listOf(FilterParameter(textParameterName, text, null))
39 | }
40 |
41 | override fun equals(other: Any?): Boolean {
42 | if (this === other) return true
43 | if (javaClass != other?.javaClass) return false
44 |
45 | other as ContainsFilter
46 |
47 | if (text != other.text) return false
48 |
49 | return super.equals(other)
50 | }
51 |
52 | override fun hashCode(): Int {
53 | var result = super.hashCode()
54 | result = 31 * result + text.hashCode()
55 | return result
56 | }
57 | }
--------------------------------------------------------------------------------
/app/src/main/java/me/murks/feedwatcher/model/Feed.kt:
--------------------------------------------------------------------------------
1 | package me.murks.feedwatcher.model
2 |
3 | import java.net.URL
4 | import java.util.*
5 |
6 | /**
7 | * @author zouroboros
8 | * @date 8/13/18.
9 | */
10 | data class Feed(val url: URL, val lastUpdate : Date?, val name: String)
--------------------------------------------------------------------------------
/app/src/main/java/me/murks/feedwatcher/model/FeedFilter.kt:
--------------------------------------------------------------------------------
1 | /*
2 | This file is part of FeedWatcher.
3 |
4 | FeedWatcher is free software: you can redistribute it and/or modify
5 | it under the terms of the GNU General Public License as published by
6 | the Free Software Foundation, either version 3 of the License, or
7 | (at your option) any later version.
8 |
9 | FeedWatcher is distributed in the hope that it will be useful,
10 | but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 | GNU General Public License for more details.
13 |
14 | You should have received a copy of the GNU General Public License
15 | along with FeedWatcher. If not, see .
16 | Copyright 2019 - 2021 Zouroboros
17 | */
18 | package me.murks.feedwatcher.model
19 |
20 | import me.murks.feedwatcher.atomrss.FeedItem
21 | import java.net.URL
22 |
23 | /**
24 | * @author zouroboros
25 | */
26 | class FeedFilter(index: Int, val feedUrl: URL?): Filter(FilterType.FEED, index) {
27 |
28 | companion object {
29 | val feedUrlParameterName = "feedUrl"
30 | }
31 |
32 | override fun filterItems(feed: Feed, items: List): List {
33 | return if(feed.url == feedUrl) {
34 | items
35 | } else {
36 | emptyList()
37 | }
38 | }
39 |
40 | override fun filterCallback(callback: FilterTypeCallback): R {
41 | return callback.filter(this)
42 | }
43 |
44 | override fun parameter(): List {
45 | return listOf(FilterParameter(feedUrlParameterName, feedUrl?.toString(), null))
46 | }
47 |
48 | override fun equals(other: Any?): Boolean {
49 | if (this === other) return true
50 | if (javaClass != other?.javaClass) return false
51 | if (!super.equals(other)) return false
52 |
53 | other as FeedFilter
54 |
55 | if (feedUrl != other.feedUrl) return false
56 |
57 | return super.equals(other)
58 | }
59 |
60 | override fun hashCode(): Int {
61 | var result = super.hashCode()
62 | result = 31 * result + feedUrl.hashCode()
63 | return result
64 | }
65 |
66 | }
--------------------------------------------------------------------------------
/app/src/main/java/me/murks/feedwatcher/model/Filter.kt:
--------------------------------------------------------------------------------
1 | /*
2 | This file is part of FeedWatcher.
3 |
4 | FeedWatcher is free software: you can redistribute it and/or modify
5 | it under the terms of the GNU General Public License as published by
6 | the Free Software Foundation, either version 3 of the License, or
7 | (at your option) any later version.
8 |
9 | FeedWatcher is distributed in the hope that it will be useful,
10 | but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 | GNU General Public License for more details.
13 |
14 | You should have received a copy of the GNU General Public License
15 | along with FeedWatcher. If not, see .
16 | Copyright 2019 - 2021 Zouroboros
17 | */
18 | package me.murks.feedwatcher.model
19 |
20 | import me.murks.feedwatcher.atomrss.FeedItem
21 |
22 | /**
23 | * Base class for filters
24 | * @author zouroboros
25 | */
26 | abstract class Filter(val type: FilterType, val index: Int) {
27 |
28 | abstract fun filterItems(feed: Feed, items: List): List
29 |
30 | abstract fun filterCallback(callback: FilterTypeCallback): R
31 |
32 | /**
33 | * Returns the parameter of a filter
34 | */
35 | abstract fun parameter(): List
36 |
37 | override fun equals(other: Any?): Boolean {
38 | if (this === other) return true
39 | if (javaClass != other?.javaClass) return false
40 |
41 | other as Filter
42 |
43 | if (type != other.type) return false
44 | if (index != other.index) return false
45 |
46 | return true
47 | }
48 |
49 | override fun hashCode(): Int {
50 | var result = type.hashCode()
51 | result = 31 * result + index
52 | return result
53 | }
54 | }
--------------------------------------------------------------------------------
/app/src/main/java/me/murks/feedwatcher/model/FilterFactory.kt:
--------------------------------------------------------------------------------
1 | /*
2 | This file is part of FeedWatcher.
3 |
4 | FeedWatcher is free software: you can redistribute it and/or modify
5 | it under the terms of the GNU General Public License as published by
6 | the Free Software Foundation, either version 3 of the License, or
7 | (at your option) any later version.
8 |
9 | FeedWatcher is distributed in the hope that it will be useful,
10 | but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 | GNU General Public License for more details.
13 |
14 | You should have received a copy of the GNU General Public License
15 | along with FeedWatcher. If not, see .
16 | Copyright 2019 Zouroboros
17 | */
18 | package me.murks.feedwatcher.model
19 |
20 | import java.lang.IllegalArgumentException
21 | import java.net.URL
22 |
23 | /**
24 | * Factory for [Filter]
25 | * @author zouroboros
26 | */
27 | object FilterFactory {
28 | fun new(index: Int, type: FilterType, parameter: List): Filter {
29 | if(type == FilterType.CONTAINS) {
30 | val text = parameter.find { it.name == ContainsFilter.textParameterName }!!.stringValue
31 | return ContainsFilter(index, text)
32 | } else if (type == FilterType.FEED) {
33 | val feedUrl = parameter.find { it.name == FeedFilter.feedUrlParameterName }!!.stringValue
34 | return FeedFilter(index, if (feedUrl != null) URL(feedUrl) else null)
35 | } else if (type == FilterType.NEW) {
36 | val startDate = parameter.find { it.name == NewEntryFilter.startDateParameterName }!!.dateValue!!
37 | return NewEntryFilter(index, startDate)
38 | }
39 | throw IllegalArgumentException("Illegal filter type ${type}!")
40 | }
41 | }
--------------------------------------------------------------------------------
/app/src/main/java/me/murks/feedwatcher/model/FilterParameter.kt:
--------------------------------------------------------------------------------
1 | /*
2 | This file is part of FeedWatcher.
3 |
4 | FeedWatcher is free software: you can redistribute it and/or modify
5 | it under the terms of the GNU General Public License as published by
6 | the Free Software Foundation, either version 3 of the License, or
7 | (at your option) any later version.
8 |
9 | FeedWatcher is distributed in the hope that it will be useful,
10 | but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 | GNU General Public License for more details.
13 |
14 | You should have received a copy of the GNU General Public License
15 | along with FeedWatcher. If not, see .
16 | Copyright 2019 Zouroboros
17 | */
18 | package me.murks.feedwatcher.model
19 |
20 | import java.util.*
21 |
22 | /**
23 | * Class that represents a filter parameter and its value.
24 | * @author zouroboros
25 | */
26 | data class FilterParameter(val name: String, var stringValue: String?, var dateValue: Date?) {}
--------------------------------------------------------------------------------
/app/src/main/java/me/murks/feedwatcher/model/FilterType.kt:
--------------------------------------------------------------------------------
1 | package me.murks.feedwatcher.model
2 |
3 | /**
4 | * @author zouroboros
5 | * @date 8/13/18.
6 | */
7 | enum class FilterType {
8 | CONTAINS,
9 | FEED,
10 | NEW
11 | }
--------------------------------------------------------------------------------
/app/src/main/java/me/murks/feedwatcher/model/FilterTypeCallback.kt:
--------------------------------------------------------------------------------
1 | /*
2 | This file is part of FeedWatcher.
3 |
4 | FeedWatcher is free software: you can redistribute it and/or modify
5 | it under the terms of the GNU General Public License as published by
6 | the Free Software Foundation, either version 3 of the License, or
7 | (at your option) any later version.
8 |
9 | FeedWatcher is distributed in the hope that it will be useful,
10 | but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 | GNU General Public License for more details.
13 |
14 | You should have received a copy of the GNU General Public License
15 | along with FeedWatcher. If not, see .
16 | Copyright 2019 Zouroboros
17 | */
18 | package me.murks.feedwatcher.model
19 |
20 | /**
21 | * Interface that allows the implementation of the visitor pattern for
22 | * [me.murks.feedwatcher.model.Filter].
23 | * @author zouroboros
24 | */
25 | interface FilterTypeCallback {
26 | fun filter(filter: ContainsFilter): R
27 | fun filter(filter: FeedFilter): R
28 | fun filter(filter: NewEntryFilter): R
29 | }
--------------------------------------------------------------------------------
/app/src/main/java/me/murks/feedwatcher/model/NewEntryFilter.kt:
--------------------------------------------------------------------------------
1 | /*
2 | This file is part of FeedWatcher.
3 |
4 | FeedWatcher is free software: you can redistribute it and/or modify
5 | it under the terms of the GNU General Public License as published by
6 | the Free Software Foundation, either version 3 of the License, or
7 | (at your option) any later version.
8 |
9 | FeedWatcher is distributed in the hope that it will be useful,
10 | but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 | GNU General Public License for more details.
13 |
14 | You should have received a copy of the GNU General Public License
15 | along with FeedWatcher. If not, see .
16 | Copyright 2019 - 2021 Zouroboros
17 | */
18 | package me.murks.feedwatcher.model
19 |
20 | import me.murks.feedwatcher.atomrss.FeedItem
21 | import java.util.*
22 |
23 | /**
24 | * Filter for new entries in a feed. Filters all entries after a certain date
25 | * @author zouroboros
26 | */
27 | class NewEntryFilter(index: Int, val start: Date): Filter(FilterType.NEW, index) {
28 |
29 | companion object {
30 | const val startDateParameterName = "startDate"
31 | }
32 |
33 | override fun filterCallback(callback: FilterTypeCallback): R {
34 | return callback.filter(this)
35 | }
36 |
37 | override fun filterItems(feed: Feed, items: List): List {
38 | return items.filter { it.date?.after(start) == true }
39 | }
40 |
41 | override fun parameter(): List {
42 | return listOf(FilterParameter(startDateParameterName, null, start))
43 | }
44 | }
--------------------------------------------------------------------------------
/app/src/main/java/me/murks/feedwatcher/model/Query.kt:
--------------------------------------------------------------------------------
1 | package me.murks.feedwatcher.model
2 |
3 | /**
4 | * @author zouroboros
5 | * @date 8/17/18.
6 | */
7 | data class Query(val id: Long, val name: String, val filter: List)
--------------------------------------------------------------------------------
/app/src/main/java/me/murks/feedwatcher/model/Result.kt:
--------------------------------------------------------------------------------
1 | /*
2 | This file is part of FeedWatcher.
3 |
4 | FeedWatcher is free software: you can redistribute it and/or modify
5 | it under the terms of the GNU General Public License as published by
6 | the Free Software Foundation, either version 3 of the License, or
7 | (at your option) any later version.
8 |
9 | FeedWatcher is distributed in the hope that it will be useful,
10 | but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 | GNU General Public License for more details.
13 |
14 | You should have received a copy of the GNU General Public License
15 | along with FeedWatcher. If not, see .
16 | Copyright 2021 - 2022 Zouroboros
17 | */
18 | package me.murks.feedwatcher.model
19 |
20 | import me.murks.feedwatcher.atomrss.FeedItem
21 | import java.util.*
22 |
23 | /**
24 | * @author zouroboros
25 | */
26 | data class Result(val id: Long, val feed: Feed, val queries: Collection, val item: FeedItem,
27 | val found: Date, val unread: Boolean)
--------------------------------------------------------------------------------
/app/src/main/java/me/murks/feedwatcher/model/Scan.kt:
--------------------------------------------------------------------------------
1 | /*
2 | This file is part of FeedWatcher.
3 |
4 | FeedWatcher is free software: you can redistribute it and/or modify
5 | it under the terms of the GNU General Public License as published by
6 | the Free Software Foundation, either version 3 of the License, or
7 | (at your option) any later version.
8 |
9 | FeedWatcher is distributed in the hope that it will be useful,
10 | but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 | GNU General Public License for more details.
13 |
14 | You should have received a copy of the GNU General Public License
15 | along with FeedWatcher. If not, see .
16 | Copyright 2021 Zouroboros
17 | */
18 | package me.murks.feedwatcher.model
19 |
20 | import java.util.*
21 |
22 | /**
23 | * Class represents a record about a scan of a feed.
24 | * @author zouroboros
25 | */
26 | data class Scan(val feed: Feed, val sucessfully: Boolean, val error: String?, val scanDate: Date)
27 |
--------------------------------------------------------------------------------
/app/src/main/java/me/murks/feedwatcher/model/ScanInterval.kt:
--------------------------------------------------------------------------------
1 | /*
2 | This file is part of FeedWatcher.
3 |
4 | FeedWatcher is free software: you can redistribute it and/or modify
5 | it under the terms of the GNU General Public License as published by
6 | the Free Software Foundation, either version 3 of the License, or
7 | (at your option) any later version.
8 |
9 | FeedWatcher is distributed in the hope that it will be useful,
10 | but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 | GNU General Public License for more details.
13 |
14 | You should have received a copy of the GNU General Public License
15 | along with FeedWatcher. If not, see .
16 | Copyright 2021 Zouroboros
17 | */
18 | package me.murks.feedwatcher.model
19 |
20 | /**
21 | * Container representing a scan interval
22 | * @author zouroboros
23 | */
24 | data class ScanInterval(val minutes: Int, val hours: Int)
--------------------------------------------------------------------------------
/app/src/main/java/me/murks/feedwatcher/tasks/ActionTask.kt:
--------------------------------------------------------------------------------
1 | package me.murks.feedwatcher.tasks
2 |
3 | import android.os.AsyncTask
4 | import me.murks.feedwatcher.Either
5 | import me.murks.feedwatcher.Left
6 | import me.murks.feedwatcher.Right
7 |
8 | /**
9 | * @author zouroboros
10 | */
11 | class ActionTask(private val f: () -> TResult,
12 | listener: ErrorHandlingTaskListener) :
13 | AsyncTask>() {
14 |
15 | private val _listener
16 | = ErrorHandlingTaskListenerWrapper(listener)
17 |
18 | override fun doInBackground(vararg args: Void): Either {
19 | try {
20 | return Right(f())
21 | } catch (e: Exception) {
22 | return Left(e)
23 | }
24 | }
25 |
26 | override fun onProgressUpdate(vararg values: TResult) {
27 | for (value in values) {
28 | _listener.onProgress(value)
29 | }
30 | super.onProgressUpdate(*values)
31 | }
32 |
33 | override fun onPostExecute(result: Either) {
34 | super.onPostExecute(result)
35 | _listener.onResult(result)
36 | }
37 | }
--------------------------------------------------------------------------------
/app/src/main/java/me/murks/feedwatcher/tasks/FeedsFilter.kt:
--------------------------------------------------------------------------------
1 | /*
2 | This file is part of FeedWatcher.
3 |
4 | FeedWatcher is free software: you can redistribute it and/or modify
5 | it under the terms of the GNU General Public License as published by
6 | the Free Software Foundation, either version 3 of the License, or
7 | (at your option) any later version.
8 |
9 | FeedWatcher is distributed in the hope that it will be useful,
10 | but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 | GNU General Public License for more details.
13 |
14 | You should have received a copy of the GNU General Public License
15 | along with FeedWatcher. If not, see .
16 | Copyright 2019 - 2022 Zouroboros
17 | */
18 | package me.murks.feedwatcher.tasks
19 |
20 | import android.util.Xml
21 | import me.murks.feedwatcher.Either
22 | import me.murks.feedwatcher.Left
23 | import me.murks.feedwatcher.Right
24 | import me.murks.feedwatcher.atomrss.FeedParser
25 | import me.murks.feedwatcher.model.Feed
26 | import me.murks.feedwatcher.model.Query
27 | import me.murks.feedwatcher.model.Result
28 | import okhttp3.OkHttpClient
29 | import okhttp3.Request
30 | import java.io.IOException
31 | import java.lang.Exception
32 | import java.util.*
33 |
34 | typealias FilterResult = Either, Pair>>
35 |
36 | /**
37 | * Object which contains method for filtering feeds
38 | * @author zouroboros
39 | */
40 | object FeedsFilter {
41 | fun filterFeeds(feeds: Collection, queries: Collection): Sequence, Pair>>> {
42 | val client = OkHttpClient()
43 | return feeds.asSequence().map {feed ->
44 | try {
45 | val request = Request.Builder().url(feed.url).build()
46 | val response = client.newCall(request).execute()
47 |
48 | if (response.isSuccessful) {
49 | response.body!!.use {
50 | it.byteStream().use {
51 | stream ->
52 | val feedIo = FeedParser(stream, Xml.newPullParser(), Xml.newSerializer())
53 | val items = feedIo.items(feed.lastUpdate?: Date(0))
54 | // we only want items with a date.
55 | // this could be done more intelligently but for now we rely on the
56 | // feeds to provde a date.
57 | .filter { it.date != null }
58 |
59 | val matchingItems = queries.associateBy({query -> query},
60 | { query ->
61 | query.filter.fold(items) {
62 | acc, filter -> filter.filterItems(feed, acc)}})
63 | .entries.map { entry -> entry.value.map {
64 | item -> AbstractMap.SimpleEntry(entry.key, item) } }
65 | .flatten()
66 | .groupBy({ it.value }) { it.key }
67 |
68 | val results = matchingItems.entries.map { Result(0, feed, it.value, it.key, Date(), true) }
69 | return@map Right(Pair(feed, results))
70 | }
71 | }
72 | } else {
73 | return@map Left(Pair(feed, IOException("${feed.url} returned status code ${response.code}.")))
74 | }
75 |
76 | } catch (e: Exception) {
77 | return@map Left(Pair(feed, e))
78 | }
79 | }
80 | }
81 | }
--------------------------------------------------------------------------------
/app/src/main/java/me/murks/feedwatcher/tasks/FilterFeedsJob.kt:
--------------------------------------------------------------------------------
1 | /*
2 | This file is part of FeedWatcher.
3 |
4 | FeedWatcher is free software: you can redistribute it and/or modify
5 | it under the terms of the GNU General Public License as published by
6 | the Free Software Foundation, either version 3 of the License, or
7 | (at your option) any later version.
8 |
9 | FeedWatcher is distributed in the hope that it will be useful,
10 | but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 | GNU General Public License for more details.
13 |
14 | You should have received a copy of the GNU General Public License
15 | along with FeedWatcher. If not, see .
16 | Copyright 2019-2020 Zouroboros
17 | */
18 | package me.murks.feedwatcher.tasks
19 |
20 | import android.app.job.JobParameters
21 | import android.app.job.JobService
22 | import me.murks.feedwatcher.AndroidEnvironment
23 | import me.murks.feedwatcher.FeedWatcherApp
24 | import java.util.concurrent.CompletableFuture
25 |
26 | /**
27 | * @author zouroboros
28 | */
29 | class FilterFeedsJob(): JobService() {
30 |
31 | private lateinit var app: FeedWatcherApp
32 | private lateinit var parameter: JobParameters
33 | private lateinit var future: CompletableFuture
34 |
35 | override fun onStartJob(p0: JobParameters): Boolean {
36 | parameter = p0
37 | app = FeedWatcherApp(AndroidEnvironment(this))
38 | app.environment.log.info("Starting ${FilterFeedsJob::class.qualifiedName}.")
39 | future = Tasks.filterFeeds(app)
40 | future.thenAccept { jobFinished(p0, false) }
41 | app.environment.log.info("${FilterFeedsJob::class.qualifiedName} started.")
42 | return true // job may still be running
43 | }
44 |
45 | override fun onStopJob(p0: JobParameters?): Boolean {
46 | app.environment.log.info("${FilterFeedsJob::class.qualifiedName} canceled.")
47 | future.cancel(true)
48 | return false // no rescheduling
49 | }
50 |
51 | override fun onDestroy() {
52 | super.onDestroy()
53 | if(::app.isInitialized) {
54 | app.environment.close()
55 | }
56 | }
57 |
58 | companion object {
59 | const val NOTIFICATION_ID = 1
60 | }
61 | }
--------------------------------------------------------------------------------
/app/src/main/java/me/murks/feedwatcher/tasks/StreamingTask.kt:
--------------------------------------------------------------------------------
1 | /*
2 | This file is part of FeedWatcher.
3 |
4 | FeedWatcher is free software: you can redistribute it and/or modify
5 | it under the terms of the GNU General Public License as published by
6 | the Free Software Foundation, either version 3 of the License, or
7 | (at your option) any later version.
8 |
9 | FeedWatcher is distributed in the hope that it will be useful,
10 | but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 | GNU General Public License for more details.
13 |
14 | You should have received a copy of the GNU General Public License
15 | along with FeedWatcher. If not, see .
16 | Copyright 2020 Zouroboros
17 | */
18 | package me.murks.feedwatcher.tasks
19 |
20 | import android.os.AsyncTask
21 | import me.murks.feedwatcher.Either
22 | import me.murks.feedwatcher.Left
23 | import me.murks.feedwatcher.Right
24 | import java.lang.Exception
25 |
26 | /**
27 | * Task for processing lists.
28 | *
29 | * @author zouroboros
30 | *
31 | * @param func The function that processes a list item.
32 | * @param listener The listener that processes the result.
33 | */
34 | class StreamingTask(private val func: (input: TInput) -> TOutput,
35 | private val listener: Listener) :
36 | AsyncTask, TOutput>, Unit>() {
37 |
38 | /**
39 | * Class that hold an error and the item that was processed when the error occured.
40 | */
41 | class Error(val item: TInput, val error: Exception)
42 |
43 | /***
44 | * Interface for listener that process the results.
45 | */
46 | interface Listener {
47 | fun onResult(item: TOutput)
48 | fun onError(item: Error)
49 | fun onFinished()
50 | }
51 |
52 | override fun doInBackground(vararg inputs: TInput) {
53 | inputs.forEach {
54 | try {
55 | publishProgress(Right(func(it)))
56 | } catch (e: Exception) {
57 | publishProgress(Left(Error(it, e)))
58 | }
59 | }
60 | }
61 |
62 | override fun onProgressUpdate(vararg values: Either, TOutput>) {
63 | for (value in values) {
64 | when(value) {
65 | is Left -> listener.onError(value.value)
66 | is Right -> listener.onResult(value.value)
67 | }
68 | }
69 | super.onProgressUpdate(*values)
70 | }
71 |
72 | override fun onPostExecute(result: Unit?) {
73 | listener.onFinished()
74 | super.onPostExecute(result)
75 | }
76 | }
--------------------------------------------------------------------------------
/app/src/main/java/me/murks/feedwatcher/tasks/TaskListener.kt:
--------------------------------------------------------------------------------
1 | package me.murks.feedwatcher.tasks
2 |
3 | import me.murks.feedwatcher.Either
4 | import me.murks.feedwatcher.Left
5 | import me.murks.feedwatcher.Right
6 |
7 | /**
8 | * @author zouroboros
9 | */
10 | interface TaskListener {
11 | fun onProgress(progress: TP)
12 | fun onResult(result: TR)
13 | }
14 |
15 | interface ErrorHandlingTaskListener {
16 | fun onSuccessResult(result: TR)
17 | fun onErrorResult(error: TE)
18 | fun onProgress(progress: TP)
19 | }
20 |
21 | class ErrorHandlingTaskListenerWrapper(
22 | val listener: ErrorHandlingTaskListener) : TaskListener> {
23 |
24 | override fun onProgress(progress: TP) {
25 | listener.onProgress(progress)
26 | }
27 |
28 | override fun onResult(result: Either) {
29 | if(result.isLeft()) {
30 | listener.onErrorResult((result as Left).value)
31 | } else {
32 | listener.onSuccessResult((result as Right).value)
33 | }
34 | }
35 | }
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_feed_icon.xml:
--------------------------------------------------------------------------------
1 |
4 |
7 |
8 |
9 |
12 |
13 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_feedwatcher_notification.xml:
--------------------------------------------------------------------------------
1 |
3 |
6 |
9 |
10 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_launcher_foreground.xml:
--------------------------------------------------------------------------------
1 |
7 |
12 |
13 |
19 |
22 |
25 |
26 |
27 |
28 |
34 |
35 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_menu.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
10 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_about.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
12 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_feed.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
17 |
18 |
30 |
31 |
40 |
41 |
50 |
51 |
60 |
61 |
69 |
70 |
79 |
80 |
88 |
89 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_feed_export.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
16 |
17 |
24 |
25 |
26 |
33 |
34 |
42 |
43 |
49 |
50 |
58 |
59 |
60 |
68 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_feed_export_list_item.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
13 |
14 |
21 |
22 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_feed_import.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
16 |
17 |
24 |
25 |
26 |
33 |
34 |
42 |
43 |
49 |
50 |
58 |
59 |
60 |
68 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_feed_import_list_item.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
13 |
14 |
21 |
22 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_overview.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
11 |
12 |
13 |
18 |
24 |
29 |
30 |
31 |
32 |
39 |
40 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_query.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
15 |
16 |
21 |
22 |
29 |
30 |
35 |
36 |
37 |
38 |
44 |
45 |
49 |
54 |
55 |
56 |
57 |
58 |
59 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_result.xml:
--------------------------------------------------------------------------------
1 |
2 |
9 |
10 |
21 |
22 |
31 |
32 |
41 |
42 |
52 |
53 |
62 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/fragment_feed_import.xml:
--------------------------------------------------------------------------------
1 |
2 |
9 |
10 |
18 |
19 |
28 |
29 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/fragment_feeds_list.xml:
--------------------------------------------------------------------------------
1 |
2 |
9 |
10 |
16 |
17 |
22 |
23 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/fragment_feeds_list_item.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
8 |
9 |
16 |
17 |
25 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/fragment_queries_list.xml:
--------------------------------------------------------------------------------
1 |
2 |
9 |
10 |
16 |
17 |
22 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/fragment_queries_list_item.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
15 |
16 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/fragment_results_list.xml:
--------------------------------------------------------------------------------
1 |
2 |
9 |
10 |
16 |
17 |
22 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/fragment_results_list_item.xml:
--------------------------------------------------------------------------------
1 |
2 |
9 |
10 |
19 |
20 |
28 |
29 |
38 |
39 |
49 |
50 |
58 |
59 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/layout_date_time_picker.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
10 |
11 |
12 |
13 |
17 |
18 |
19 |
20 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/preferences_activity.xml:
--------------------------------------------------------------------------------
1 |
5 |
6 |
12 |
13 |
20 |
21 |
28 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/query_filter_list_item.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
16 |
17 |
24 |
25 |
32 |
33 |
39 |
40 |
45 |
46 |
53 |
54 |
55 |
56 |
57 |
63 |
64 |
69 |
74 |
75 |
76 |
82 |
83 |
90 |
91 |
92 |
--------------------------------------------------------------------------------
/app/src/main/res/menu/activity_query_menu.xml:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/app/src/main/res/menu/fragment_feeds_menu.xml:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/app/src/main/res/menu/fragment_results_menu.xml:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/app/src/main/res/menu/navigation.xml:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-hdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zouroboros/feed-watcher/9e8f0b3ac07c5f95ec407a390283537a3029076a/app/src/main/res/mipmap-hdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-hdpi/ic_launcher_foreground.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zouroboros/feed-watcher/9e8f0b3ac07c5f95ec407a390283537a3029076a/app/src/main/res/mipmap-hdpi/ic_launcher_foreground.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-hdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zouroboros/feed-watcher/9e8f0b3ac07c5f95ec407a390283537a3029076a/app/src/main/res/mipmap-hdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-mdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zouroboros/feed-watcher/9e8f0b3ac07c5f95ec407a390283537a3029076a/app/src/main/res/mipmap-mdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-mdpi/ic_launcher_foreground.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zouroboros/feed-watcher/9e8f0b3ac07c5f95ec407a390283537a3029076a/app/src/main/res/mipmap-mdpi/ic_launcher_foreground.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-mdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zouroboros/feed-watcher/9e8f0b3ac07c5f95ec407a390283537a3029076a/app/src/main/res/mipmap-mdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zouroboros/feed-watcher/9e8f0b3ac07c5f95ec407a390283537a3029076a/app/src/main/res/mipmap-xhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zouroboros/feed-watcher/9e8f0b3ac07c5f95ec407a390283537a3029076a/app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zouroboros/feed-watcher/9e8f0b3ac07c5f95ec407a390283537a3029076a/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zouroboros/feed-watcher/9e8f0b3ac07c5f95ec407a390283537a3029076a/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zouroboros/feed-watcher/9e8f0b3ac07c5f95ec407a390283537a3029076a/app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zouroboros/feed-watcher/9e8f0b3ac07c5f95ec407a390283537a3029076a/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zouroboros/feed-watcher/9e8f0b3ac07c5f95ec407a390283537a3029076a/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zouroboros/feed-watcher/9e8f0b3ac07c5f95ec407a390283537a3029076a/app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zouroboros/feed-watcher/9e8f0b3ac07c5f95ec407a390283537a3029076a/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/values-zh/strings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | FeedWatcher
4 | 结果
5 | 查询
6 | 订阅源
7 | 添加源
8 | 添加查询
9 | 保存
10 | 过滤器类型
11 | 添加过滤器
12 | 输入源url
13 | 订阅
14 | 取消订阅
15 | 加载失败
16 | 找到的结果来自:
17 | 新结果
18 | 新结果的通知
19 | 找到了新的订阅源条目
20 | 在 %s 中发现了新的结果
21 |
22 | 包含
23 | 源
24 | 新条目
25 |
26 | 你已经订阅了这个源。
27 | 订阅源图标
28 | 文本
29 | 源
30 | 查询编辑器
31 | 查询名称
32 | 选项
33 | 通知
34 | 显示通知
35 | 订阅源扫描
36 | 启用后台扫描
37 | 扫描间隔
38 | 每隔 %s 小时扫描
39 | 每隔 %s 分钟扫描
40 | 每隔 %s 小时扫描
41 | 每隔 %s 小时%s 分钟扫描
42 | 你的设备支持的最短扫描间隔为 %s分钟,精度 %s 分钟。
43 | %s 之后找到的新条目
44 | 好
45 | 取消
46 | 无效 URL
47 | 从未更新
48 | 编辑订阅源
49 | 导入源
50 | 导出源
51 | 源导入活动
52 | 你可以用一个 OPML 文件导入你的源。
53 | 选择 OPML 文件
54 | 请选择一个 OPML 文件。
55 | 读取 OPML 文件 (%s) 失败。请选择另一个文件。
56 | 选择你想要添加到 FeedWatcher 的源。
57 | 导入
58 | 导入选中的源失败 (%s)。请选择其他源。
59 | 选择全部
60 | 好
61 | 选择要导出的源。
62 | 导出选中的源失败 (%s)。
63 | 选择文件来导出源
64 | Feedwatcher 源导出
65 | 源已导出。
66 | 清除结果
67 | 结果已清除。
68 | 撤销操作?
69 | 关于 Feedwatcher
70 | 导出数据库
71 | 成功导出了数据库
72 | 数据库导出失败
73 | 源图标
74 | 源名称
75 | 扫描订阅源
76 | 上次扫描: %s.
77 | 一些扫描失败了。上次扫描: %s。上次成功扫描: %s。
78 | 所有扫描均失败。上次失败的扫描: %s.
79 | 尚未扫描订阅源。
80 | 无标题
81 | 未提供描述
82 |
83 |
--------------------------------------------------------------------------------
/app/src/main/res/values/colors.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | #78909c
4 | #a7c0cd
5 | #4b636e
6 | #000000
7 |
8 |
--------------------------------------------------------------------------------
/app/src/main/res/values/dimens.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | 16dp
4 | 16dp
5 |
6 |
--------------------------------------------------------------------------------
/app/src/main/res/values/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
9 |
10 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
--------------------------------------------------------------------------------
/app/src/main/res/xml/preferences.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
7 |
8 |
12 |
13 |
14 |
17 |
18 |
22 |
23 |
27 |
28 |
32 |
33 |
34 |
35 |
--------------------------------------------------------------------------------
/app/src/test/java/me/murks/feedwatcher/TextsTests.kt:
--------------------------------------------------------------------------------
1 | /*
2 | This file is part of FeedWatcher.
3 |
4 | FeedWatcher is free software: you can redistribute it and/or modify
5 | it under the terms of the GNU General Public License as published by
6 | the Free Software Foundation, either version 3 of the License, or
7 | (at your option) any later version.
8 |
9 | FeedWatcher is distributed in the hope that it will be useful,
10 | but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 | GNU General Public License for more details.
13 |
14 | You should have received a copy of the GNU General Public License
15 | along with FeedWatcher. If not, see .
16 | Copyright 2019 Zouroboros
17 | */
18 | package me.murks.feedwatcher
19 |
20 | import org.junit.jupiter.api.Test
21 | import org.junit.jupiter.api.Assertions.*
22 |
23 | /**
24 | * @author zouroboros
25 | */
26 | class TextsTests {
27 | @Test
28 | fun findUrlTest1() {
29 | val ddgUrlString = "https://ddg.gg"
30 | val ddg = Texts.findUrl(ddgUrlString)
31 | assertNotNull(ddg, "A URL string should result in a valid url")
32 | assertEquals(ddg.toString(), ddgUrlString, "findUrl on a valid string should produce the string as URL")
33 | }
34 |
35 | @Test
36 | fun findUrlTest2() {
37 | val urlWithText = "Hello: https://github.com"
38 | val url = Texts.findUrl(urlWithText)
39 | assertNotNull(url, "A text with a url should produce the url")
40 | assertEquals(url.toString(), "https://github.com", "Complete URL should be extracted")
41 | }
42 |
43 | @Test
44 | fun findUrlTest3() {
45 | val textWithNoUrl = "hello:/"
46 |
47 | val url = Texts.findUrl(textWithNoUrl)
48 |
49 | assertNull(url, "Text without an url should not produce a url")
50 | }
51 |
52 | @Test
53 | fun findUrlTest4() {
54 | val textWithMultipleUrls = """Test Pool
55 | https://example.org/podcasts/506
56 |
57 | RSS address: https://example.org/podcasts/506/feed.xml"""
58 |
59 | val url = Texts.findUrl(textWithMultipleUrls)
60 |
61 | assertEquals(url.toString(), "https://example.org/podcasts/506", "The first url should be returned")
62 | }
63 | }
--------------------------------------------------------------------------------
/atomrss-cli/.gitignore:
--------------------------------------------------------------------------------
1 | /build
--------------------------------------------------------------------------------
/atomrss-cli/build.gradle:
--------------------------------------------------------------------------------
1 | plugins {
2 | id 'application'
3 | }
4 |
5 | repositories {
6 | mavenCentral()
7 | }
8 |
9 | dependencies {
10 | implementation project(':atomrss')
11 | implementation 'xmlpull:xmlpull:1.1.3.1'
12 | implementation 'net.sf.kxml:kxml2:2.3.0'
13 | }
14 |
15 | application {
16 | mainClass = 'me.murks.feedwatcher.atomrss.cli.FeedwatcherAtomRssCli'
17 | }
--------------------------------------------------------------------------------
/atomrss-cli/src/main/java/me/murks/feedwatcher/atomrss/cli/AtomRssFileVisitor.java:
--------------------------------------------------------------------------------
1 | /*
2 | This file is part of FeedWatcher.
3 |
4 | FeedWatcher is free software: you can redistribute it and/or modify
5 | it under the terms of the GNU General Public License as published by
6 | the Free Software Foundation, either version 3 of the License, or
7 | (at your option) any later version.
8 |
9 | FeedWatcher is distributed in the hope that it will be useful,
10 | but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 | GNU General Public License for more details.
13 |
14 | You should have received a copy of the GNU General Public License
15 | along with FeedWatcher. If not, see .
16 | Copyright 2021 Zouroboros
17 | */
18 | package me.murks.feedwatcher.atomrss.cli;
19 |
20 | import me.murks.feedwatcher.atomrss.FeedItem;
21 |
22 | import org.kxml2.io.KXmlSerializer;
23 | import org.xmlpull.v1.XmlPullParser;
24 | import org.xmlpull.v1.XmlPullParserException;
25 | import org.xmlpull.v1.XmlPullParserFactory;
26 |
27 | import java.io.FileInputStream;
28 | import java.io.IOException;
29 | import java.nio.file.FileVisitResult;
30 | import java.nio.file.Path;
31 | import java.nio.file.SimpleFileVisitor;
32 | import java.nio.file.attribute.BasicFileAttributes;
33 | import java.util.Collection;
34 | import java.util.Date;
35 | import java.util.LinkedList;
36 |
37 | import me.murks.feedwatcher.atomrss.FeedParser;
38 |
39 | public class AtomRssFileVisitor extends SimpleFileVisitor {
40 | private final Collection testedFiles;
41 | private final Collection sucessfullFiles;
42 | private final Collection failedFiles;
43 |
44 | public AtomRssFileVisitor() {
45 | testedFiles = new LinkedList<>();
46 | sucessfullFiles = new LinkedList<>();
47 | failedFiles = new LinkedList<>();
48 | }
49 |
50 | @Override
51 | public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
52 | if (file.toString().endsWith(".xml") || file.toString().endsWith(".rss")
53 | || file.toString().endsWith(".atom")) {
54 | testedFiles.add(file);
55 | try {
56 | XmlPullParserFactory factory = XmlPullParserFactory.newInstance();
57 | XmlPullParser parser = factory.newPullParser();
58 | FeedParser feedParser = new FeedParser(new FileInputStream(file.toFile()), parser,
59 | new KXmlSerializer());
60 | checkParser(feedParser);
61 | checkEntries(feedParser);
62 | sucessfullFiles.add(file);
63 | } catch (Exception e) {
64 | failedFiles.add(file);
65 | }
66 | }
67 | return FileVisitResult.CONTINUE;
68 | }
69 |
70 | private void checkParser(FeedParser parser) {
71 | parser.getName();
72 | parser.getDescription();
73 | parser.getIconUrl();
74 | }
75 |
76 | private void checkEntries(FeedParser parser) {
77 | for (FeedItem item : parser.items(new Date(0))) {
78 | item.getTitle();
79 | item.getDescription();
80 | item.getDate();
81 | item.getLink();
82 | }
83 | }
84 |
85 | public Collection getTestedFiles() {
86 | return testedFiles;
87 | }
88 |
89 | public Collection getFailedFiles() {
90 | return failedFiles;
91 | }
92 |
93 | public Collection getSucessfullFiles() {
94 | return sucessfullFiles;
95 | }
96 | }
97 |
--------------------------------------------------------------------------------
/atomrss-cli/src/main/java/me/murks/feedwatcher/atomrss/cli/FeedwatcherAtomRssCli.java:
--------------------------------------------------------------------------------
1 | /*
2 | This file is part of FeedWatcher.
3 |
4 | FeedWatcher is free software: you can redistribute it and/or modify
5 | it under the terms of the GNU General Public License as published by
6 | the Free Software Foundation, either version 3 of the License, or
7 | (at your option) any later version.
8 |
9 | FeedWatcher is distributed in the hope that it will be useful,
10 | but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 | GNU General Public License for more details.
13 |
14 | You should have received a copy of the GNU General Public License
15 | along with FeedWatcher. If not, see .
16 | Copyright 2021 Zouroboros
17 | */
18 | package me.murks.feedwatcher.atomrss.cli;
19 |
20 | import java.io.IOException;
21 | import java.nio.file.FileSystems;
22 | import java.nio.file.Files;
23 | import java.nio.file.Path;
24 |
25 | public class FeedwatcherAtomRssCli {
26 | public static void main(String[] args) throws IOException {
27 | for (String path: args) {
28 | AtomRssFileVisitor visitor = new AtomRssFileVisitor();
29 | Files.walkFileTree(FileSystems.getDefault().getPath(path), visitor);
30 |
31 | System.out.println("Tested " + visitor.getTestedFiles().size() + " files.");
32 | System.out.println("Parsing "+ visitor.getFailedFiles().size() + "/" + visitor.getTestedFiles().size() + " failed.");
33 | System.out.println("Parsing "+ visitor.getSucessfullFiles().size() + "/" + visitor.getTestedFiles().size() + " succeeded.");
34 |
35 | visitor.getFailedFiles().stream().map(p -> p.toString())
36 | .sorted()
37 | .forEach(fileName ->
38 | System.err.println("Parsing " + fileName + " failed.")
39 | );
40 | }
41 | }
42 | }
--------------------------------------------------------------------------------
/atomrss/.gitignore:
--------------------------------------------------------------------------------
1 | /build
--------------------------------------------------------------------------------
/atomrss/build.gradle:
--------------------------------------------------------------------------------
1 | plugins {
2 | id 'java-library'
3 | id 'kotlin'
4 | }
5 |
6 | java {
7 | sourceCompatibility = JavaVersion.VERSION_1_8
8 | targetCompatibility = JavaVersion.VERSION_1_8
9 | }
10 |
11 | test {
12 | useJUnitPlatform()
13 | }
14 |
15 | dependencies {
16 | implementation 'xmlpull:xmlpull:1.1.3.1'
17 |
18 | // (Required) Writing and executing Unit Tests on the JUnit Platform
19 | testImplementation("org.junit.jupiter:junit-jupiter-api:5.8.0")
20 | testRuntimeOnly("org.junit.jupiter:junit-jupiter-engine:5.8.0")
21 | testImplementation 'net.sf.kxml:kxml2:2.3.0'
22 | }
--------------------------------------------------------------------------------
/atomrss/src/main/java/me/murks/feedwatcher/atomrss/FeedItem.kt:
--------------------------------------------------------------------------------
1 | /*
2 | This file is part of FeedWatcher.
3 |
4 | FeedWatcher is free software: you can redistribute it and/or modify
5 | it under the terms of the GNU General Public License as published by
6 | the Free Software Foundation, either version 3 of the License, or
7 | (at your option) any later version.
8 |
9 | FeedWatcher is distributed in the hope that it will be useful,
10 | but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 | GNU General Public License for more details.
13 |
14 | You should have received a copy of the GNU General Public License
15 | along with FeedWatcher. If not, see .
16 | Copyright 2019 - 2021 Zouroboros
17 | */
18 |
19 | package me.murks.feedwatcher.atomrss
20 |
21 | import java.net.URI
22 | import java.util.*
23 |
24 | /**
25 | * @author zouroboros
26 | */
27 | data class FeedItem(val title: String?, val description: String?, val link: URI?, val date: Date?)
--------------------------------------------------------------------------------
/atomrss/src/main/java/me/murks/feedwatcher/atomrss/LazyParser.kt:
--------------------------------------------------------------------------------
1 | /*
2 | This file is part of FeedWatcher.
3 |
4 | FeedWatcher is free software: you can redistribute it and/or modify
5 | it under the terms of the GNU General Public License as published by
6 | the Free Software Foundation, either version 3 of the License, or
7 | (at your option) any later version.
8 |
9 | FeedWatcher is distributed in the hope that it will be useful,
10 | but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 | GNU General Public License for more details.
13 |
14 | You should have received a copy of the GNU General Public License
15 | along with FeedWatcher. If not, see .
16 | Copyright 2020 - 2021 Zouroboros
17 | */
18 | package me.murks.feedwatcher.atomrss
19 |
20 | import org.xmlpull.v1.XmlPullParser
21 | import java.util.*
22 |
23 | /**
24 | * Class which implements a parser on top of a {@see XmlPullParser} to allows lazy parsing of xml streams.
25 | * @author zouroboros
26 | */
27 | class LazyParser(private val parser: XmlPullParser, newNodes: Collection) {
28 | val states = newNodes.associateBy { it.tag }
29 | val stack = Stack()
30 |
31 | fun parseUntil(predicate: () -> Boolean) {
32 | while (parser.nextToken() != XmlPullParser.END_DOCUMENT && !predicate()) {
33 | if(parser.eventType == XmlPullParser.START_TAG) {
34 | if(stack.empty()) {
35 | if(states.contains(parser.name)) {
36 | stack.push(states[parser.name])
37 | }
38 | } else {
39 | if(stack.peek().subStates.containsKey(parser.name)) {
40 | stack.push(stack.peek().subStates[parser.name])
41 | }
42 | }
43 | }
44 |
45 | if(!stack.empty()) {
46 | stack.peek().action(parser)
47 | }
48 |
49 | if(parser.eventType == XmlPullParser.END_TAG) {
50 | if(!stack.empty() && stack.peek().tag == parser.name) {
51 | stack.pop()
52 | }
53 | }
54 | }
55 | }
56 | }
--------------------------------------------------------------------------------
/atomrss/src/main/java/me/murks/feedwatcher/atomrss/ParserNode.kt:
--------------------------------------------------------------------------------
1 | /*
2 | This file is part of FeedWatcher.
3 |
4 | FeedWatcher is free software: you can redistribute it and/or modify
5 | it under the terms of the GNU General Public License as published by
6 | the Free Software Foundation, either version 3 of the License, or
7 | (at your option) any later version.
8 |
9 | FeedWatcher is distributed in the hope that it will be useful,
10 | but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 | GNU General Public License for more details.
13 |
14 | You should have received a copy of the GNU General Public License
15 | along with FeedWatcher. If not, see .
16 | Copyright 2020 - 2021 Zouroboros
17 | */
18 | package me.murks.feedwatcher.atomrss
19 |
20 | import org.xmlpull.v1.XmlPullParser
21 |
22 | /**
23 | * Class that represents a node in the in the definition of a hierarchical parser.
24 | * @author zouroboros
25 | */
26 | class ParserNode(val tag: String, val action: (parser: XmlPullParser) -> Unit, newSubNodes: Collection = listOf()) {
27 | val subStates = newSubNodes.associateBy({ it.tag })
28 | }
29 |
30 |
--------------------------------------------------------------------------------
/build.gradle:
--------------------------------------------------------------------------------
1 | // Top-level build file where you can add configuration options common to all sub-projects/modules.
2 |
3 | buildscript {
4 | ext.kotlin_version = '1.5.21'
5 | repositories {
6 | google()
7 | mavenCentral()
8 | maven { url 'https://jitpack.io' }
9 | }
10 | dependencies {
11 | classpath 'com.android.tools.build:gradle:7.1.2'
12 | classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:1.6.0"
13 | classpath "org.jetbrains.dokka:dokka-gradle-plugin:0.9.17"
14 | classpath "de.mannodermaus.gradle.plugins:android-junit5:1.8.0.0"
15 |
16 | // NOTE: Do not place your application dependencies here; they belong
17 | // in the individual module build.gradle files
18 | }
19 | }
20 |
21 | allprojects {
22 | repositories {
23 | google()
24 | mavenCentral()
25 | maven { url 'https://jitpack.io' }
26 | }
27 | }
28 |
29 | task clean(type: Delete) {
30 | delete rootProject.buildDir
31 | }
32 |
--------------------------------------------------------------------------------
/fastlane/metadata/android/en/full_description.txt:
--------------------------------------------------------------------------------
1 | Create custom queries to scan RSS and Atom feeds. You will get a notification when new entries matching your queries are found.
2 |
--------------------------------------------------------------------------------
/fastlane/metadata/android/en/name.txt:
--------------------------------------------------------------------------------
1 | FeedWatcher
2 |
--------------------------------------------------------------------------------
/fastlane/metadata/android/en/short_description.txt:
--------------------------------------------------------------------------------
1 | Allows scanning RSS and Atom feeds with custom queries.
2 |
--------------------------------------------------------------------------------
/feed-icon.svg:
--------------------------------------------------------------------------------
1 |
2 |
115 |
--------------------------------------------------------------------------------
/feedwatcher-icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zouroboros/feed-watcher/9e8f0b3ac07c5f95ec407a390283537a3029076a/feedwatcher-icon.png
--------------------------------------------------------------------------------
/feedwatcher-icon.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
83 |
--------------------------------------------------------------------------------
/feedwatcher-notification-path.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
72 |
--------------------------------------------------------------------------------
/feedwatcher-notification.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
76 |
--------------------------------------------------------------------------------
/gradle.properties:
--------------------------------------------------------------------------------
1 | # Project-wide Gradle settings.
2 | # IDE (e.g. Android Studio) users:
3 | # Gradle settings configured through the IDE *will override*
4 | # any settings specified in this file.
5 | # For more details on how to configure your build environment visit
6 | # http://www.gradle.org/docs/current/userguide/build_environment.html
7 | # Specifies the JVM arguments used for the daemon process.
8 | # The setting is particularly useful for tweaking memory settings.
9 | org.gradle.jvmargs=-Xmx1536m
10 | # When configured, Gradle will run in incubating parallel mode.
11 | # This option should only be used with decoupled projects. More details, visit
12 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
13 | # org.gradle.parallel=true
14 | android.useAndroidX=true
15 | android.enableJetifier=true
16 |
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zouroboros/feed-watcher/9e8f0b3ac07c5f95ec407a390283537a3029076a/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | #Sun Oct 18 19:33:38 CEST 2020
2 | distributionBase=GRADLE_USER_HOME
3 | distributionPath=wrapper/dists
4 | zipStoreBase=GRADLE_USER_HOME
5 | zipStorePath=wrapper/dists
6 | distributionUrl=https\://services.gradle.org/distributions/gradle-7.2-all.zip
7 |
--------------------------------------------------------------------------------
/gradlew.bat:
--------------------------------------------------------------------------------
1 | @if "%DEBUG%" == "" @echo off
2 | @rem ##########################################################################
3 | @rem
4 | @rem Gradle startup script for Windows
5 | @rem
6 | @rem ##########################################################################
7 |
8 | @rem Set local scope for the variables with windows NT shell
9 | if "%OS%"=="Windows_NT" setlocal
10 |
11 | set DIRNAME=%~dp0
12 | if "%DIRNAME%" == "" set DIRNAME=.
13 | set APP_BASE_NAME=%~n0
14 | set APP_HOME=%DIRNAME%
15 |
16 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
17 | set DEFAULT_JVM_OPTS=
18 |
19 | @rem Find java.exe
20 | if defined JAVA_HOME goto findJavaFromJavaHome
21 |
22 | set JAVA_EXE=java.exe
23 | %JAVA_EXE% -version >NUL 2>&1
24 | if "%ERRORLEVEL%" == "0" goto init
25 |
26 | echo.
27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
28 | echo.
29 | echo Please set the JAVA_HOME variable in your environment to match the
30 | echo location of your Java installation.
31 |
32 | goto fail
33 |
34 | :findJavaFromJavaHome
35 | set JAVA_HOME=%JAVA_HOME:"=%
36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe
37 |
38 | if exist "%JAVA_EXE%" goto init
39 |
40 | echo.
41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
42 | echo.
43 | echo Please set the JAVA_HOME variable in your environment to match the
44 | echo location of your Java installation.
45 |
46 | goto fail
47 |
48 | :init
49 | @rem Get command-line arguments, handling Windows variants
50 |
51 | if not "%OS%" == "Windows_NT" goto win9xME_args
52 |
53 | :win9xME_args
54 | @rem Slurp the command line arguments.
55 | set CMD_LINE_ARGS=
56 | set _SKIP=2
57 |
58 | :win9xME_args_slurp
59 | if "x%~1" == "x" goto execute
60 |
61 | set CMD_LINE_ARGS=%*
62 |
63 | :execute
64 | @rem Setup the command line
65 |
66 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
67 |
68 | @rem Execute Gradle
69 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
70 |
71 | :end
72 | @rem End local scope for the variables with windows NT shell
73 | if "%ERRORLEVEL%"=="0" goto mainEnd
74 |
75 | :fail
76 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
77 | rem the _cmd.exe /c_ return code!
78 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
79 | exit /b 1
80 |
81 | :mainEnd
82 | if "%OS%"=="Windows_NT" endlocal
83 |
84 | :omega
85 |
--------------------------------------------------------------------------------
/settings.gradle:
--------------------------------------------------------------------------------
1 | include ':app'
2 | include ':atomrss'
3 | include ':atomrss-cli'
4 |
--------------------------------------------------------------------------------