Compare commits

...

20 Commits

Author SHA1 Message Date
Amine Bou
da0696cac0
Update README.md 2022-05-21 22:13:13 +02:00
288970483b
Fix broken tests (#406)
* Correct Home Activity Test to prevent crashing

* Fix broken tests

* Fix tests
2022-02-09 15:43:22 +01:00
5b540dbc38
Remove deprecated functions (#404) 2022-02-05 18:18:25 +01:00
def75b6431
Fix setting the number of articles downloaded (#403) 2022-02-05 18:17:35 +01:00
4826ed0355
Scroll articles using the volume keys (#401) 2022-02-02 09:04:26 +01:00
3b47a4a2f0
Upgrade dependencies (#398) 2022-01-24 21:50:25 +01:00
9693dec807
Make the selection of tags and source filters exclusive (#399)
* Make the selection of tags and source filters exclusive

* Update changelog
2022-01-16 20:49:54 +01:00
ca5186d20a
Clear settings on logout (#395) 2022-01-08 16:26:34 +01:00
78d5744139
Fix button readall not working as soon as the app was started but only after switching category. (#393) 2022-01-07 20:46:06 +01:00
b2609554e6
Fix dark dialog background (#392) 2022-01-06 19:50:19 +01:00
5db312bbb8
Simplify ArticleFragment logic (#388)
* Send a single article to ArticleFragment

* Allow using the internal browser
2021-12-14 20:09:50 +01:00
7592ab512b
Migrate to Viewpager2 (#387)
* Migrate ReaderActivity to ViewPager2

* Use tint on icons in place of filters

* Prevent crash when opening the first article

* Add the correct background color to the article reader

* Use consistent colors in the article reader

* Migrate ImageActivity to ViewPager2
2021-12-13 20:29:38 +01:00
000b346529
Fix crash when editing sources (#385) 2021-12-09 20:17:45 +01:00
1bb975c584
Substitute some deprecated functions (#386)
* Deprecated color filter

* Various deprecated functions
2021-12-09 20:17:16 +01:00
69aaa323e2
Theme (#375)
* Fix sources colors

* Fix issues with sources

* Simplify card view theme

* Simplify list item theming

* Simplify article viewer theming

* Fix issues with the color selection.

* Correct the color of sources text in article reader
2021-11-29 20:26:18 +01:00
cedb207eca
Fix login activity layout (#381) 2021-11-22 20:59:36 +01:00
Amine Bou
b9e91f30ef
New Crowdin updates (#384)
* New translations strings.xml (Sinhala)

* New translations strings.xml (Sinhala)

* New translations strings.xml (French)

* New translations strings.xml (Spanish)

* New translations strings.xml (Catalan)

* New translations strings.xml (German)

* New translations strings.xml (Italian)

* New translations strings.xml (Korean)

* New translations strings.xml (Dutch)

* New translations strings.xml (Portuguese)

* New translations strings.xml (Turkish)

* New translations strings.xml (Chinese Simplified)

* New translations strings.xml (Chinese Traditional)

* New translations strings.xml (Galician)

* New translations strings.xml (Portuguese, Brazilian)

* New translations strings.xml (Indonesian)

* New translations strings.xml (Persian)

* New translations strings.xml (Sinhala)

* New translations strings.xml (Chinese Simplified)

* New translations strings.xml (Chinese Simplified)

* New translations strings.xml (French)

* New translations strings.xml (Spanish)

* New translations strings.xml (Catalan)

* New translations strings.xml (German)

* New translations strings.xml (Italian)

* New translations strings.xml (Korean)

* New translations strings.xml (Dutch)

* New translations strings.xml (Portuguese)

* New translations strings.xml (Turkish)

* New translations strings.xml (Chinese Simplified)

* New translations strings.xml (Chinese Traditional)

* New translations strings.xml (Galician)

* New translations strings.xml (Portuguese, Brazilian)

* New translations strings.xml (Indonesian)

* New translations strings.xml (Persian)

* New translations strings.xml (Sinhala)

* New translations strings.xml (French)

* New translations strings.xml (Galician)

* New translations strings.xml (Galician)

* New translations strings.xml (Chinese Simplified)

* New translations strings.xml (French)

* New translations strings.xml (Spanish)

* New translations strings.xml (Catalan)

* New translations strings.xml (German)

* New translations strings.xml (Italian)

* New translations strings.xml (Korean)

* New translations strings.xml (Dutch)

* New translations strings.xml (Portuguese)

* New translations strings.xml (Turkish)

* New translations strings.xml (Chinese Simplified)

* New translations strings.xml (Chinese Traditional)

* New translations strings.xml (Galician)

* New translations strings.xml (Portuguese, Brazilian)

* New translations strings.xml (Indonesian)

* New translations strings.xml (Persian)

* New translations strings.xml (Sinhala)

* New translations strings.xml (Chinese Simplified)

* New translations strings.xml (Galician)

Co-authored-by: Amine Bou <aminecmi@gmail.com>
2021-11-22 20:32:02 +01:00
6a8c2d7fcd
Trim sources title when too long (#383) 2021-11-22 07:09:12 +01:00
60a908a44a
Clear api version when disconnecting. (#382)
Closes #354
2021-11-22 07:07:20 +01:00
6b887ff74b
Date utils test (#380)
* Add test for date parsing

* Add test for the older api

* Correct milliseconds format
2021-11-16 20:04:24 +01:00
55 changed files with 426 additions and 542 deletions

View File

@ -44,8 +44,12 @@
- Closing #236. New sources can be added in Selfoss 2.19. - Closing #236. New sources can be added in Selfoss 2.19.
- Closing #397 and #355. Tag and Sources filters are now exclusive.
- Dropped support for android 4, the last version supporting it is v1721030811 - Dropped support for android 4, the last version supporting it is v1721030811
- Added ability to scroll articles up and down using the volume keys #400
**1.6.x** **1.6.x**
- Handling hidden tags. - Handling hidden tags.

View File

@ -1,47 +1 @@
# ReaderForSelfoss **(Only available from F-Droid)** # Project moved to https://github.com/aminecmi/ReaderforSelfoss-multiplatform
[![Crowdin](https://d322cqt584bo4o.cloudfront.net/readerforselfoss/localized.svg)](https://crowdin.com/project/readerforselfoss)
It's an RSS Reader for Android, that **only** works with [Selfoss](https://selfoss.aditu.de/)
**The project is not dead at all.**
I still want to work on it, but for the last few months, I didn't have that much time to do so.
If you are a developer, don't hesitate to help with PRs.
If you are a user, you can still create new issues. I'll fix them when I can.
<a href="https://f-droid.org/packages/apps.amine.bou.readerforselfoss"><img src="https://f-droid.org/badge/get-it-on.png" alt="Get it on F-Droid" height="100"></a>
## Screen captures
<img src="res//fr-card.png?raw=true" alt="card view" width="400"/> <img src="res//fr-list.png?raw=true" alt="list view" width="400"/>
## Like my app ?
<a href="https://www.buymeacoffee.com/aminecmi" target="_blank"><img src="https://cdn.buymeacoffee.com/buttons/lato-orange.png" alt="Buy Me A Coffee" style="height: 51px !important;width: 217px !important;" ></a>
## Want to help ?
1. **You'll have to have a Selfoss instance running.** You'll find everything you need to install it [here](https://selfoss.aditu.de/).
2. Check the [Contribution guide](https://github.com/aminecmi/ReaderforSelfoss/blob/master/.github/CONTRIBUTING.md).
3. Build the project by following [these steps](https://github.com/aminecmi/ReaderforSelfoss/blob/master/.github/CONTRIBUTING.md#build-the-project) (you should have read them after the contribution guide)
## Useful links
- [Check what changed](https://github.com/aminecmi/ReaderforSelfoss/blob/master/CHANGELOG.md)
- [See what I'm doing](https://github.com/aminecmi/ReaderforSelfoss/projects/1)
- [Create an issue, or request a new feature](https://github.com/aminecmi/ReaderforSelfoss/issues)
- [Help translation the app](https://crowdin.com/project/readerforselfoss)
## Contributors (Alphabetical order) ❤️
- [@aancel](https://github.com/aancel)
- [@Binnette](https://github.com/Binnette)
- [@davidoskky](https://github.com/davidoskky)
- [@hectorgabucio](https://github.com/hectorgabucio)
- [@licaon-kter](https://github.com/licaon-kter)
- [@sergey-babkin](https://github.com/sergey-babkin)

View File

@ -104,18 +104,18 @@ dependencies {
// Espresso-intents for validation and stubbing of Intents // Espresso-intents for validation and stubbing of Intents
androidTestImplementation 'androidx.test.espresso:espresso-intents:3.4.0-alpha02' androidTestImplementation 'androidx.test.espresso:espresso-intents:3.4.0-alpha02'
implementation fileTree(include: ['*.jar'], dir: 'libs') implementation fileTree(include: ['*.jar'], dir: 'libs')
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
// Android Support // Android Support
implementation "androidx.appcompat:appcompat:1.4.0-beta01" implementation 'androidx.appcompat:appcompat:1.4.1'
implementation 'com.google.android.material:material:1.5.0-alpha04' implementation 'com.google.android.material:material:1.5.0'
implementation 'androidx.recyclerview:recyclerview:1.3.0-alpha01' implementation 'androidx.recyclerview:recyclerview:1.3.0-alpha01'
implementation "androidx.legacy:legacy-support-v4:$android_version" implementation "androidx.legacy:legacy-support-v4:$android_version"
implementation 'androidx.vectordrawable:vectordrawable:1.2.0-alpha02' implementation 'androidx.vectordrawable:vectordrawable:1.2.0-alpha02'
implementation "androidx.browser:browser:1.3.0" implementation 'androidx.browser:browser:1.4.0'
implementation "androidx.cardview:cardview:$android_version" implementation "androidx.cardview:cardview:$android_version"
implementation "androidx.annotation:annotation:1.2.0" implementation 'androidx.annotation:annotation:1.3.0'
implementation 'androidx.work:work-runtime-ktx:2.7.0' implementation 'androidx.work:work-runtime-ktx:2.7.1'
implementation 'androidx.constraintlayout:constraintlayout:2.1.1' implementation 'androidx.constraintlayout:constraintlayout:2.1.3'
implementation 'org.jsoup:jsoup:1.14.3' implementation 'org.jsoup:jsoup:1.14.3'
coreLibraryDesugaring("com.android.tools:desugar_jdk_libs:1.1.5") coreLibraryDesugaring("com.android.tools:desugar_jdk_libs:1.1.5")
@ -129,11 +129,11 @@ dependencies {
implementation "com.mikepenz:aboutlibraries-definitions:8.9.4" implementation "com.mikepenz:aboutlibraries-definitions:8.9.4"
// Async // Async
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.5.2' implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.6.0'
// Retrofit + http logging + okhttp // Retrofit + http logging + okhttp
implementation 'com.squareup.retrofit2:retrofit:2.9.0' implementation 'com.squareup.retrofit2:retrofit:2.9.0'
implementation 'com.squareup.okhttp3:logging-interceptor:4.9.1' implementation 'com.squareup.okhttp3:logging-interceptor:5.0.0-alpha.3'
implementation 'com.squareup.retrofit2:converter-gson:2.9.0' implementation 'com.squareup.retrofit2:converter-gson:2.9.0'
implementation 'com.burgstaller:okhttp-digest:2.5' implementation 'com.burgstaller:okhttp-digest:2.5'
@ -146,7 +146,7 @@ dependencies {
implementation 'com.github.bumptech.glide:okhttp3-integration:4.1.1' implementation 'com.github.bumptech.glide:okhttp3-integration:4.1.1'
// Drawer // Drawer
implementation 'com.mikepenz:materialdrawer:8.4.4' implementation 'com.mikepenz:materialdrawer:8.4.5'
// Themes // Themes
implementation 'com.52inc:scoops:1.0.0' implementation 'com.52inc:scoops:1.0.0'
@ -155,14 +155,15 @@ dependencies {
// Pager // Pager
implementation 'me.relex:circleindicator:2.1.6' implementation 'me.relex:circleindicator:2.1.6'
implementation "androidx.viewpager2:viewpager2:1.1.0-beta01"
//PhotoView //PhotoView
implementation 'com.github.chrisbanes:PhotoView:2.3.0' implementation 'com.github.chrisbanes:PhotoView:2.3.0'
implementation 'androidx.core:core-ktx:1.7.0-rc01' implementation 'androidx.core:core-ktx:1.7.0'
implementation "androidx.lifecycle:lifecycle-livedata-ktx:2.4.0-rc01" implementation 'androidx.lifecycle:lifecycle-livedata-ktx:2.4.0'
implementation "androidx.lifecycle:lifecycle-common-java8:2.4.0-rc01" implementation 'androidx.lifecycle:lifecycle-common-java8:2.4.0'
implementation "androidx.room:room-ktx:2.4.0-beta01" implementation "androidx.room:room-ktx:2.4.0-beta01"
kapt "androidx.room:room-compiler:2.4.0-beta01" kapt "androidx.room:room-compiler:2.4.0-beta01"

View File

@ -7,23 +7,20 @@ import androidx.test.espresso.Espresso.onView
import androidx.test.espresso.Espresso.openActionBarOverflowOrOptionsMenu import androidx.test.espresso.Espresso.openActionBarOverflowOrOptionsMenu
import androidx.test.espresso.action.ViewActions.click import androidx.test.espresso.action.ViewActions.click
import androidx.test.espresso.action.ViewActions.closeSoftKeyboard import androidx.test.espresso.action.ViewActions.closeSoftKeyboard
import androidx.test.espresso.action.ViewActions.pressBack
import androidx.test.espresso.action.ViewActions.pressKey import androidx.test.espresso.action.ViewActions.pressKey
import androidx.test.espresso.action.ViewActions.typeText import androidx.test.espresso.action.ViewActions.typeText
import androidx.test.espresso.assertion.ViewAssertions.matches import androidx.test.espresso.assertion.ViewAssertions.matches
import androidx.test.espresso.contrib.DrawerActions
import androidx.test.espresso.intent.Intents import androidx.test.espresso.intent.Intents
import androidx.test.espresso.intent.Intents.intended import androidx.test.espresso.intent.Intents.intended
import androidx.test.espresso.intent.Intents.times
import androidx.test.espresso.intent.matcher.IntentMatchers.hasComponent import androidx.test.espresso.intent.matcher.IntentMatchers.hasComponent
import androidx.test.espresso.matcher.ViewMatchers.isDisplayed import androidx.test.espresso.matcher.ViewMatchers.isDisplayed
import androidx.test.espresso.matcher.ViewMatchers.isRoot
import androidx.test.espresso.matcher.ViewMatchers.withContentDescription import androidx.test.espresso.matcher.ViewMatchers.withContentDescription
import androidx.test.espresso.matcher.ViewMatchers.withId import androidx.test.espresso.matcher.ViewMatchers.withId
import androidx.test.espresso.matcher.ViewMatchers.withText import androidx.test.espresso.matcher.ViewMatchers.withText
import androidx.test.rule.ActivityTestRule import androidx.test.rule.ActivityTestRule
import androidx.test.runner.AndroidJUnit4 import androidx.test.runner.AndroidJUnit4
import android.view.KeyEvent import android.view.KeyEvent
import androidx.test.espresso.matcher.RootMatchers.isDialog
import apps.amine.bou.readerforselfoss.utils.Config import apps.amine.bou.readerforselfoss.utils.Config
import org.junit.After import org.junit.After
import org.junit.Before import org.junit.Before
@ -84,11 +81,14 @@ class HomeActivityEspressoTest {
onView(withMenu(id = R.id.refresh, titleId = R.string.menu_home_refresh)) onView(withMenu(id = R.id.refresh, titleId = R.string.menu_home_refresh))
.perform(click()) .perform(click())
onView(withText(android.R.string.ok))
.inRoot(isDialog()).check(matches(isDisplayed())).perform(click())
openActionBarOverflowOrOptionsMenu(context) openActionBarOverflowOrOptionsMenu(context)
onView(withText(R.string.action_disconnect)).perform(click()) onView(withText(R.string.action_disconnect)).perform(click())
intended(hasComponent(LoginActivity::class.java.name), times(1)) intended(hasComponent(LoginActivity::class.java.name))
} }
// TODO: test articles opening and actions for cards and lists // TODO: test articles opening and actions for cards and lists

View File

@ -85,7 +85,7 @@ class LoginActivityEspressoTest {
onView(withId(R.id.signInButton)).perform(click()) onView(withId(R.id.signInButton)).perform(click())
onView(withId(R.id.urlLayout)).check(matches(isHintOrErrorEnabled())) onView(withId(R.id.urlView)).check(matches(isHintOrErrorEnabled()))
} }
// TODO: Add tests for multiple false urls with dialog // TODO: Add tests for multiple false urls with dialog
@ -101,19 +101,19 @@ class LoginActivityEspressoTest {
onView(withId(R.id.signInButton)).perform(click()) onView(withId(R.id.signInButton)).perform(click())
onView(withId(R.id.loginLayout)).check(matches(isHintOrErrorEnabled())) onView(withId(R.id.loginView)).check(matches(isHintOrErrorEnabled()))
onView(withId(R.id.passwordLayout)).check(matches(isHintOrErrorEnabled())) onView(withId(R.id.passwordView)).check(matches(isHintOrErrorEnabled()))
onView(withId(R.id.loginView)).perform(click()).perform( onView(withId(R.id.loginView)).perform(click()).perform(
typeText(username), typeText(username),
closeSoftKeyboard() closeSoftKeyboard()
) )
onView(withId(R.id.passwordLayout)).check(matches(isHintOrErrorEnabled())) onView(withId(R.id.passwordView)).check(matches(isHintOrErrorEnabled()))
onView(withId(R.id.signInButton)).perform(click()) onView(withId(R.id.signInButton)).perform(click())
onView(withId(R.id.passwordLayout)).check( onView(withId(R.id.passwordView)).check(
matches( matches(
isHintOrErrorEnabled() isHintOrErrorEnabled()
) )
@ -141,9 +141,9 @@ class LoginActivityEspressoTest {
onView(withId(R.id.signInButton)).perform(click()) onView(withId(R.id.signInButton)).perform(click())
onView(withId(R.id.urlLayout)).check(matches(isHintOrErrorEnabled())) onView(withId(R.id.urlView)).check(matches(isHintOrErrorEnabled()))
onView(withId(R.id.loginLayout)).check(matches(isHintOrErrorEnabled())) onView(withId(R.id.loginView)).check(matches(isHintOrErrorEnabled()))
onView(withId(R.id.passwordLayout)).check(matches(isHintOrErrorEnabled())) onView(withId(R.id.passwordView)).check(matches(isHintOrErrorEnabled()))
} }
@Test @Test
@ -167,6 +167,7 @@ class LoginActivityEspressoTest {
onView(withId(R.id.signInButton)).perform(click()) onView(withId(R.id.signInButton)).perform(click())
Thread.sleep(2000)
intended(hasComponent(HomeActivity::class.java.name)) intended(hasComponent(HomeActivity::class.java.name))
} }

View File

@ -1,5 +1,6 @@
package apps.amine.bou.readerforselfoss package apps.amine.bou.readerforselfoss
import android.content.Context
import android.content.Intent import android.content.Intent
import android.content.SharedPreferences import android.content.SharedPreferences
import android.preference.PreferenceManager import android.preference.PreferenceManager
@ -10,6 +11,7 @@ import androidx.test.espresso.intent.Intents.times
import androidx.test.espresso.intent.matcher.IntentMatchers.hasComponent import androidx.test.espresso.intent.matcher.IntentMatchers.hasComponent
import androidx.test.rule.ActivityTestRule import androidx.test.rule.ActivityTestRule
import androidx.test.runner.AndroidJUnit4 import androidx.test.runner.AndroidJUnit4
import apps.amine.bou.readerforselfoss.utils.Config
import org.junit.After import org.junit.After
import org.junit.Before import org.junit.Before
@ -22,6 +24,9 @@ class MainActivityEspressoTest {
lateinit var intent: Intent lateinit var intent: Intent
lateinit var preferencesEditor: SharedPreferences.Editor lateinit var preferencesEditor: SharedPreferences.Editor
private lateinit var url: String
private lateinit var username: String
private lateinit var password: String
@Rule @JvmField @Rule @JvmField
val rule = ActivityTestRule(MainActivity::class.java, true, false) val rule = ActivityTestRule(MainActivity::class.java, true, false)
@ -32,31 +37,39 @@ class MainActivityEspressoTest {
val context = getInstrumentation().targetContext val context = getInstrumentation().targetContext
// create a SharedPreferences editor // create a SharedPreferences editor
preferencesEditor = PreferenceManager.getDefaultSharedPreferences(context).edit() preferencesEditor = context.getSharedPreferences(Config.settingsName, Context.MODE_PRIVATE).edit()
url = BuildConfig.LOGIN_URL
username = BuildConfig.LOGIN_USERNAME
password = BuildConfig.LOGIN_PASSWORD
Intents.init() Intents.init()
} }
@Test @Test
fun checkFirstOpenLaunchesIntro() { fun checkFirstOpenLaunchesIntro() {
preferencesEditor.putBoolean("firstStart", true) preferencesEditor.putString("url", "")
preferencesEditor.putString("password", "")
preferencesEditor.putString("login", "")
preferencesEditor.commit() preferencesEditor.commit()
rule.launchActivity(intent) rule.launchActivity(intent)
intended(hasComponent(MainActivity::class.java.name)) intended(hasComponent(LoginActivity::class.java.name))
intended(hasComponent(LoginActivity::class.java.name), times(0)) intended(hasComponent(HomeActivity::class.java.name), times(0))
} }
@Test @Test
fun checkNotFirstOpenLaunchesLogin() { fun checkNotFirstOpenLaunchesLogin() {
preferencesEditor.putBoolean("firstStart", false) preferencesEditor.putString("url", url)
preferencesEditor.putString("password", password)
preferencesEditor.putString("login", username)
preferencesEditor.commit() preferencesEditor.commit()
rule.launchActivity(intent) rule.launchActivity(intent)
intended(hasComponent(MainActivity::class.java.name)) intended(hasComponent(MainActivity::class.java.name))
intended(hasComponent(LoginActivity::class.java.name)) intended(hasComponent(HomeActivity::class.java.name))
} }
@After @After

View File

@ -1,8 +1,8 @@
package apps.amine.bou.readerforselfoss package apps.amine.bou.readerforselfoss
import com.google.android.material.textfield.TextInputLayout
import androidx.test.espresso.matcher.ViewMatchers import androidx.test.espresso.matcher.ViewMatchers
import android.view.View import android.view.View
import android.widget.EditText
import org.hamcrest.Description import org.hamcrest.Description
import org.hamcrest.Matcher import org.hamcrest.Matcher
import org.hamcrest.Matchers import org.hamcrest.Matchers
@ -14,11 +14,11 @@ fun isHintOrErrorEnabled(): Matcher<View> =
} }
override fun matchesSafely(item: View?): Boolean { override fun matchesSafely(item: View?): Boolean {
if (item !is TextInputLayout) { if (item !is EditText) {
return false return false
} }
return item.isHintEnabled || item.isErrorEnabled return item.error.isNotEmpty()
} }
} }

View File

@ -0,0 +1,29 @@
package apps.amine.bou.readerforselfoss.utils
import org.junit.Test
class DateUtilsTest {
@Test
fun parseDateV4() {
Config.apiVersion = 4
val dateString = "2013-04-07T13:43:00+01:00"
val milliseconds = parseDate(dateString).toEpochMilli()
val correctMilliseconds : Long = 1365338580000
assert(milliseconds == correctMilliseconds)
}
@Test
fun parseDateV1() {
Config.apiVersion = 0
val dateString = "2013-04-07 13:43:00"
val milliseconds = parseDate(dateString).toEpochMilli()
val correctMilliseconds = 1365342180000
assert(milliseconds == correctMilliseconds)
}
}

View File

@ -52,19 +52,19 @@ class AddSourceActivity : AppCompatActivity() {
scoop.bindStatusBar(this, Toppings.PRIMARY_DARK.value) scoop.bindStatusBar(this, Toppings.PRIMARY_DARK.value)
val drawable = binding.nameInput.background val drawable = binding.nameInput.background
drawable.setColorFilter(appColors.colorAccent, PorterDuff.Mode.SRC_ATOP) drawable.setTint(appColors.colorAccent)
// TODO: clean // TODO: clean
binding.nameInput.background = drawable binding.nameInput.background = drawable
val drawable1 = binding.sourceUri.background val drawable1 = binding.sourceUri.background
drawable1.setColorFilter(appColors.colorAccent, PorterDuff.Mode.SRC_ATOP) drawable1.setTint(appColors.colorAccent)
binding.sourceUri.background = drawable1 binding.sourceUri.background = drawable1
val drawable2 = binding.tags.background val drawable2 = binding.tags.background
drawable2.setColorFilter(appColors.colorAccent, PorterDuff.Mode.SRC_ATOP) drawable2.setTint(appColors.colorAccent)
binding.tags.background = drawable2 binding.tags.background = drawable2

View File

@ -18,7 +18,6 @@ import androidx.appcompat.app.ActionBarDrawerToggle
import androidx.appcompat.app.AlertDialog import androidx.appcompat.app.AlertDialog
import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.app.AppCompatActivity
import androidx.appcompat.widget.SearchView import androidx.appcompat.widget.SearchView
import androidx.core.view.MenuItemCompat
import androidx.core.view.doOnNextLayout import androidx.core.view.doOnNextLayout
import androidx.drawerlayout.widget.DrawerLayout import androidx.drawerlayout.widget.DrawerLayout
import androidx.recyclerview.widget.* import androidx.recyclerview.widget.*
@ -100,10 +99,7 @@ class HomeActivity : AppCompatActivity(), SearchView.OnQueryTextListener {
private var displayAllCount = false private var displayAllCount = false
private var fullHeightCards: Boolean = false private var fullHeightCards: Boolean = false
private var itemsNumber: Int = 200 private var itemsNumber: Int = 200
private var elementsShown: Int = 0 private var elementsShown: Int = 1
private var maybeTagFilter: Tag? = null
private var maybeSourceFilter: Source? = null
private var maybeSearchFilter: String? = null
private var userIdentifier: String = "" private var userIdentifier: String = ""
private var displayAccountHeader: Boolean = false private var displayAccountHeader: Boolean = false
private var infiniteScroll: Boolean = false private var infiniteScroll: Boolean = false
@ -220,7 +216,7 @@ class HomeActivity : AppCompatActivity(), SearchView.OnQueryTextListener {
lastFetchDone = false lastFetchDone = false
handleDrawerItems() handleDrawerItems()
CoroutineScope(Dispatchers.Main).launch { CoroutineScope(Dispatchers.Main).launch {
refreshFocusedItems(applicationContext, api, db) refreshFocusedItems(applicationContext, api, db, itemsNumber)
getElementsAccordingToTab() getElementsAccordingToTab()
binding.swipeRefreshLayout.isRefreshing = false binding.swipeRefreshLayout.isRefreshing = false
} }
@ -251,7 +247,7 @@ class HomeActivity : AppCompatActivity(), SearchView.OnQueryTextListener {
): Boolean = false ): Boolean = false
override fun onSwiped(viewHolder: RecyclerView.ViewHolder, swipeDir: Int) { override fun onSwiped(viewHolder: RecyclerView.ViewHolder, swipeDir: Int) {
val position = viewHolder.adapterPosition val position = viewHolder.bindingAdapterPosition
val i = items.elementAtOrNull(position) val i = items.elementAtOrNull(position)
if (i != null) { if (i != null) {
@ -563,8 +559,9 @@ class HomeActivity : AppCompatActivity(), SearchView.OnQueryTextListener {
color = ColorHolder.fromColor(appColors.colorAccent) } color = ColorHolder.fromColor(appColors.colorAccent) }
onDrawerItemClickListener = { _,_,_ -> onDrawerItemClickListener = { _,_,_ ->
allItems = ArrayList() allItems = ArrayList()
maybeTagFilter = it
SharedItems.tagFilter = it.tag SharedItems.tagFilter = it.tag
SharedItems.sourceFilter = null
SharedItems.sourceIDFilter = null
getElementsAccordingToTab() getElementsAccordingToTab()
fetchOnEmptyList() fetchOnEmptyList()
false false
@ -615,8 +612,9 @@ class HomeActivity : AppCompatActivity(), SearchView.OnQueryTextListener {
color = ColorHolder.fromColor(appColors.colorAccent) } color = ColorHolder.fromColor(appColors.colorAccent) }
onDrawerItemClickListener = { _,_,_ -> onDrawerItemClickListener = { _,_,_ ->
allItems = ArrayList() allItems = ArrayList()
maybeTagFilter = it
SharedItems.tagFilter = it.tag SharedItems.tagFilter = it.tag
SharedItems.sourceFilter = null
SharedItems.sourceIDFilter = null
getElementsAccordingToTab() getElementsAccordingToTab()
fetchOnEmptyList() fetchOnEmptyList()
false false
@ -650,9 +648,9 @@ class HomeActivity : AppCompatActivity(), SearchView.OnQueryTextListener {
iconUrl = source.getIcon(this@HomeActivity) iconUrl = source.getIcon(this@HomeActivity)
onDrawerItemClickListener = { _,_,_ -> onDrawerItemClickListener = { _,_,_ ->
allItems = ArrayList() allItems = ArrayList()
maybeSourceFilter = source
SharedItems.sourceIDFilter = source.id.toLong() SharedItems.sourceIDFilter = source.id.toLong()
SharedItems.sourceFilter = source.title SharedItems.sourceFilter = source.title
SharedItems.tagFilter = null
getElementsAccordingToTab() getElementsAccordingToTab()
fetchOnEmptyList() fetchOnEmptyList()
false false
@ -673,11 +671,10 @@ class HomeActivity : AppCompatActivity(), SearchView.OnQueryTextListener {
badgeRes = R.string.drawer_action_clear badgeRes = R.string.drawer_action_clear
onDrawerItemClickListener = { _,_,_ -> onDrawerItemClickListener = { _,_,_ ->
allItems = ArrayList() allItems = ArrayList()
maybeSourceFilter = null
SharedItems.sourceFilter = null SharedItems.sourceFilter = null
SharedItems.sourceIDFilter = null SharedItems.sourceIDFilter = null
maybeTagFilter = null
SharedItems.tagFilter = null SharedItems.tagFilter = null
binding.mainDrawer.setSelectionAtPosition(-1)
getElementsAccordingToTab() getElementsAccordingToTab()
fetchOnEmptyList() fetchOnEmptyList()
false false
@ -984,7 +981,7 @@ class HomeActivity : AppCompatActivity(), SearchView.OnQueryTextListener {
CoroutineScope(Dispatchers.Main).launch { CoroutineScope(Dispatchers.Main).launch {
if (appendResults || !SharedItems.fetchedUnread) { if (appendResults || !SharedItems.fetchedUnread) {
binding.swipeRefreshLayout.isRefreshing = true binding.swipeRefreshLayout.isRefreshing = true
getUnreadItems(applicationContext, api, db, offset) getUnreadItems(applicationContext, api, db, itemsNumber, offset)
binding.swipeRefreshLayout.isRefreshing = false binding.swipeRefreshLayout.isRefreshing = false
} }
SharedItems.getUnRead() SharedItems.getUnRead()
@ -997,7 +994,7 @@ class HomeActivity : AppCompatActivity(), SearchView.OnQueryTextListener {
CoroutineScope(Dispatchers.Main).launch { CoroutineScope(Dispatchers.Main).launch {
if (appendResults || !SharedItems.fetchedAll) { if (appendResults || !SharedItems.fetchedAll) {
binding.swipeRefreshLayout.isRefreshing = true binding.swipeRefreshLayout.isRefreshing = true
getReadItems(applicationContext, api, db, offset) getReadItems(applicationContext, api, db, itemsNumber, offset)
binding.swipeRefreshLayout.isRefreshing = false binding.swipeRefreshLayout.isRefreshing = false
} }
SharedItems.getAll() SharedItems.getAll()
@ -1010,7 +1007,7 @@ class HomeActivity : AppCompatActivity(), SearchView.OnQueryTextListener {
CoroutineScope(Dispatchers.Main).launch { CoroutineScope(Dispatchers.Main).launch {
if (appendResults || !SharedItems.fetchedStarred) { if (appendResults || !SharedItems.fetchedStarred) {
binding.swipeRefreshLayout.isRefreshing = true binding.swipeRefreshLayout.isRefreshing = true
getStarredItems(applicationContext, api, db, offset) getStarredItems(applicationContext, api, db, itemsNumber, offset)
binding.swipeRefreshLayout.isRefreshing = false binding.swipeRefreshLayout.isRefreshing = false
} }
SharedItems.getStarred() SharedItems.getStarred()
@ -1122,7 +1119,6 @@ class HomeActivity : AppCompatActivity(), SearchView.OnQueryTextListener {
override fun onQueryTextChange(p0: String?): Boolean { override fun onQueryTextChange(p0: String?): Boolean {
if (p0.isNullOrBlank()) { if (p0.isNullOrBlank()) {
maybeSearchFilter = null
SharedItems.searchFilter = null SharedItems.searchFilter = null
getElementsAccordingToTab() getElementsAccordingToTab()
fetchOnEmptyList() fetchOnEmptyList()
@ -1131,29 +1127,18 @@ class HomeActivity : AppCompatActivity(), SearchView.OnQueryTextListener {
} }
override fun onQueryTextSubmit(p0: String?): Boolean { override fun onQueryTextSubmit(p0: String?): Boolean {
maybeSearchFilter = p0
SharedItems.searchFilter = p0 SharedItems.searchFilter = p0
getElementsAccordingToTab() getElementsAccordingToTab()
fetchOnEmptyList() fetchOnEmptyList()
return false return false
} }
override fun onActivityResult(req: Int, result: Int, data: Intent?) {
when (req) {
MENU_PREFERENCES -> {
//drawer.closeDrawer()
recreate()
}
else -> super.onActivityResult(req, result, data)
}
}
override fun onCreateOptionsMenu(menu: Menu): Boolean { override fun onCreateOptionsMenu(menu: Menu): Boolean {
val inflater = menuInflater val inflater = menuInflater
inflater.inflate(R.menu.home_menu, menu) inflater.inflate(R.menu.home_menu, menu)
val searchItem = menu.findItem(R.id.action_search) val searchItem = menu.findItem(R.id.action_search)
val searchView = MenuItemCompat.getActionView(searchItem) as SearchView val searchView = searchItem.getActionView() as SearchView
searchView.setOnQueryTextListener(this) searchView.setOnQueryTextListener(this)
return true return true
@ -1268,8 +1253,7 @@ class HomeActivity : AppCompatActivity(), SearchView.OnQueryTextListener {
.addTag("selfoss-loading") .addTag("selfoss-loading")
.build() .build()
WorkManager.getInstance(baseContext).enqueueUniquePeriodicWork("selfoss-loading", ExistingPeriodicWorkPolicy.KEEP, backgroundWork)
WorkManager.getInstance().enqueueUniquePeriodicWork("selfoss-loading", ExistingPeriodicWorkPolicy.KEEP, backgroundWork)
} }
} }

View File

@ -3,8 +3,9 @@ package apps.amine.bou.readerforselfoss
import android.os.Bundle import android.os.Bundle
import android.view.MenuItem import android.view.MenuItem
import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.app.AppCompatActivity
import androidx.fragment.app.FragmentManager import androidx.fragment.app.Fragment
import androidx.fragment.app.FragmentStatePagerAdapter import androidx.fragment.app.FragmentActivity
import androidx.viewpager2.adapter.FragmentStateAdapter
import apps.amine.bou.readerforselfoss.databinding.ActivityImageBinding import apps.amine.bou.readerforselfoss.databinding.ActivityImageBinding
import apps.amine.bou.readerforselfoss.fragments.ImageFragment import apps.amine.bou.readerforselfoss.fragments.ImageFragment
@ -28,8 +29,8 @@ class ImageActivity : AppCompatActivity() {
allImages = intent.getStringArrayListExtra("allImages") as ArrayList<String> allImages = intent.getStringArrayListExtra("allImages") as ArrayList<String>
position = intent.getIntExtra("position", 0) position = intent.getIntExtra("position", 0)
binding.pager.adapter = ScreenSlidePagerAdapter(supportFragmentManager) binding.pager.adapter = ScreenSlidePagerAdapter(this)
binding.pager.currentItem = position binding.pager.setCurrentItem(position, false)
} }
override fun onOptionsItemSelected(item: MenuItem): Boolean { override fun onOptionsItemSelected(item: MenuItem): Boolean {
@ -43,14 +44,10 @@ class ImageActivity : AppCompatActivity() {
return super.onOptionsItemSelected(item) return super.onOptionsItemSelected(item)
} }
private inner class ScreenSlidePagerAdapter(fm: FragmentManager) : FragmentStatePagerAdapter(fm, FragmentStatePagerAdapter.BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT) { private inner class ScreenSlidePagerAdapter(fa: FragmentActivity) : FragmentStateAdapter(fa) {
override fun getCount(): Int { override fun getItemCount(): Int = allImages.size
return allImages.size
}
override fun getItem(position: Int): ImageFragment { override fun createFragment(position: Int): Fragment = ImageFragment.newInstance(allImages[position])
return ImageFragment.newInstance(allImages[position])
}
} }
} }

View File

@ -14,15 +14,13 @@ import android.view.MenuItem
import android.view.View import android.view.View
import android.view.inputmethod.EditorInfo import android.view.inputmethod.EditorInfo
import android.widget.TextView import android.widget.TextView
import android.widget.Toast import androidx.preference.PreferenceManager
import apps.amine.bou.readerforselfoss.api.selfoss.SelfossApi import apps.amine.bou.readerforselfoss.api.selfoss.SelfossApi
import apps.amine.bou.readerforselfoss.api.selfoss.SuccessResponse import apps.amine.bou.readerforselfoss.api.selfoss.SuccessResponse
import apps.amine.bou.readerforselfoss.databinding.ActivityLoginBinding import apps.amine.bou.readerforselfoss.databinding.ActivityLoginBinding
import apps.amine.bou.readerforselfoss.themes.AppColors import apps.amine.bou.readerforselfoss.themes.AppColors
import apps.amine.bou.readerforselfoss.utils.Config
import apps.amine.bou.readerforselfoss.utils.isBaseUrlValid import apps.amine.bou.readerforselfoss.utils.isBaseUrlValid
import apps.amine.bou.readerforselfoss.utils.network.isNetworkAccessible import apps.amine.bou.readerforselfoss.utils.network.isNetworkAccessible
import com.mikepenz.aboutlibraries.Libs
import com.mikepenz.aboutlibraries.LibsBuilder import com.mikepenz.aboutlibraries.LibsBuilder
import retrofit2.Call import retrofit2.Call
import retrofit2.Callback import retrofit2.Callback
@ -54,7 +52,7 @@ class LoginActivity : AppCompatActivity() {
handleBaseUrlFail() handleBaseUrlFail()
settings = getSharedPreferences(Config.settingsName, Context.MODE_PRIVATE) settings = PreferenceManager.getDefaultSharedPreferences(applicationContext)
userIdentifier = settings.getString("unique_id", "")!! userIdentifier = settings.getString("unique_id", "")!!
editor = settings.edit() editor = settings.edit()
@ -91,16 +89,16 @@ class LoginActivity : AppCompatActivity() {
isWithLogin = !isWithLogin isWithLogin = !isWithLogin
val visi: Int = if (b) View.VISIBLE else View.GONE val visi: Int = if (b) View.VISIBLE else View.GONE
binding.loginLayout.visibility = visi binding.loginView.visibility = visi
binding.passwordLayout.visibility = visi binding.passwordView.visibility = visi
} }
binding.withHttpLogin.setOnCheckedChangeListener { _, b -> binding.withHttpLogin.setOnCheckedChangeListener { _, b ->
isWithHTTPLogin = !isWithHTTPLogin isWithHTTPLogin = !isWithHTTPLogin
val visi: Int = if (b) View.VISIBLE else View.GONE val visi: Int = if (b) View.VISIBLE else View.GONE
binding.httpLoginInput.visibility = visi binding.httpLoginView.visibility = visi
binding.httpPasswordInput.visibility = visi binding.httpPasswordView.visibility = visi
} }
} }
@ -111,9 +109,8 @@ class LoginActivity : AppCompatActivity() {
alertDialog.setMessage(getString(R.string.base_url_error)) alertDialog.setMessage(getString(R.string.base_url_error))
alertDialog.setButton( alertDialog.setButton(
AlertDialog.BUTTON_NEUTRAL, AlertDialog.BUTTON_NEUTRAL,
"OK", "OK"
{ dialog, _ -> dialog.dismiss() } ) { dialog, _ -> dialog.dismiss() }
)
alertDialog.show() alertDialog.show()
} }
} }
@ -154,9 +151,8 @@ class LoginActivity : AppCompatActivity() {
alertDialog.setMessage(getString(R.string.text_wrong_url)) alertDialog.setMessage(getString(R.string.text_wrong_url))
alertDialog.setButton( alertDialog.setButton(
AlertDialog.BUTTON_NEUTRAL, AlertDialog.BUTTON_NEUTRAL,
"OK", "OK"
{ dialog, _ -> dialog.dismiss() } ) { dialog, _ -> dialog.dismiss() }
)
alertDialog.show() alertDialog.show()
inValidCount = 0 inValidCount = 0
} }

View File

@ -61,14 +61,14 @@ class MyApp : MultiDexApplication() {
private fun initDrawerImageLoader() { private fun initDrawerImageLoader() {
DrawerImageLoader.init(object : AbstractDrawerImageLoader() { DrawerImageLoader.init(object : AbstractDrawerImageLoader() {
override fun set(imageView: ImageView, uri: Uri, placeholder: Drawable, tag: String?) { override fun set(imageView: ImageView, uri: Uri, placeholder: Drawable, tag: String?) {
Glide.with(imageView?.context) Glide.with(imageView.context)
.loadMaybeBasicAuth(config, uri.toString()) .loadMaybeBasicAuth(config, uri.toString())
.apply(RequestOptions.fitCenterTransform().placeholder(placeholder)) .apply(RequestOptions.fitCenterTransform().placeholder(placeholder))
.into(imageView) .into(imageView)
} }
override fun cancel(imageView: ImageView) { override fun cancel(imageView: ImageView) {
Glide.with(imageView?.context).clear(imageView) Glide.with(imageView.context).clear(imageView)
} }
override fun placeholder(ctx: Context, tag: String?): Drawable { override fun placeholder(ctx: Context, tag: String?): Drawable {

View File

@ -3,21 +3,17 @@ package apps.amine.bou.readerforselfoss
import android.content.Context import android.content.Context
import android.content.SharedPreferences import android.content.SharedPreferences
import android.graphics.Color import android.graphics.Color
import android.graphics.PorterDuff
import android.graphics.PorterDuffColorFilter
import android.graphics.drawable.ColorDrawable
import android.os.Build
import android.os.Bundle import android.os.Bundle
import android.view.KeyEvent
import androidx.preference.PreferenceManager import androidx.preference.PreferenceManager
import androidx.fragment.app.FragmentManager
import androidx.fragment.app.FragmentStatePagerAdapter
import androidx.core.content.ContextCompat
import androidx.viewpager.widget.ViewPager
import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.app.AppCompatActivity
import android.view.Menu import android.view.Menu
import android.view.MenuItem import android.view.MenuItem
import android.view.ViewGroup import androidx.fragment.app.Fragment
import androidx.fragment.app.FragmentActivity
import androidx.room.Room import androidx.room.Room
import androidx.viewpager2.adapter.FragmentStateAdapter
import androidx.viewpager2.widget.ViewPager2
import apps.amine.bou.readerforselfoss.api.selfoss.Item import apps.amine.bou.readerforselfoss.api.selfoss.Item
import apps.amine.bou.readerforselfoss.api.selfoss.SelfossApi import apps.amine.bou.readerforselfoss.api.selfoss.SelfossApi
import apps.amine.bou.readerforselfoss.databinding.ActivityReaderBinding import apps.amine.bou.readerforselfoss.databinding.ActivityReaderBinding
@ -28,18 +24,17 @@ import apps.amine.bou.readerforselfoss.persistence.migrations.MIGRATION_2_3
import apps.amine.bou.readerforselfoss.persistence.migrations.MIGRATION_3_4 import apps.amine.bou.readerforselfoss.persistence.migrations.MIGRATION_3_4
import apps.amine.bou.readerforselfoss.themes.AppColors import apps.amine.bou.readerforselfoss.themes.AppColors
import apps.amine.bou.readerforselfoss.themes.Toppings import apps.amine.bou.readerforselfoss.themes.Toppings
import apps.amine.bou.readerforselfoss.transformers.DepthPageTransformer
import apps.amine.bou.readerforselfoss.utils.Config import apps.amine.bou.readerforselfoss.utils.Config
import apps.amine.bou.readerforselfoss.utils.SharedItems import apps.amine.bou.readerforselfoss.utils.SharedItems
import apps.amine.bou.readerforselfoss.utils.toggleStar import apps.amine.bou.readerforselfoss.utils.toggleStar
import com.ftinc.scoop.Scoop import com.ftinc.scoop.Scoop
import me.relex.circleindicator.CircleIndicator
class ReaderActivity : AppCompatActivity() { class ReaderActivity : AppCompatActivity() {
private var markOnScroll: Boolean = false private var markOnScroll: Boolean = false
private var currentItem: Int = 0 private var currentItem: Int = 0
private lateinit var userIdentifier: String private lateinit var userIdentifier: String
private lateinit var appColors: AppColors
private lateinit var api: SelfossApi private lateinit var api: SelfossApi
@ -50,14 +45,14 @@ class ReaderActivity : AppCompatActivity() {
private lateinit var binding: ActivityReaderBinding private lateinit var binding: ActivityReaderBinding
private var activeAlignment: Int = 1 private var activeAlignment: Int = 1
val JUSTIFY = 1 private val JUSTIFY = 1
val ALIGN_LEFT = 2 private val ALIGN_LEFT = 2
private fun showMenuItem(willAddToFavorite: Boolean) { private fun showMenuItem(willAddToFavorite: Boolean) {
if (willAddToFavorite) { if (willAddToFavorite) {
toolbarMenu.findItem(R.id.star).icon.colorFilter = PorterDuffColorFilter(Color.WHITE, PorterDuff.Mode.SRC_ATOP) toolbarMenu.findItem(R.id.star).icon.setTint(Color.WHITE)
} else { } else {
toolbarMenu.findItem(R.id.star).icon.colorFilter = PorterDuffColorFilter(Color.RED, PorterDuff.Mode.SRC_ATOP) toolbarMenu.findItem(R.id.star).icon.setTint(Color.RED)
} }
} }
@ -73,6 +68,7 @@ class ReaderActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
appColors = AppColors(this)
binding = ActivityReaderBinding.inflate(layoutInflater) binding = ActivityReaderBinding.inflate(layoutInflater)
val view = binding.root val view = binding.root
@ -116,33 +112,14 @@ class ReaderActivity : AppCompatActivity() {
readItem(allItems[currentItem]) readItem(allItems[currentItem])
binding.pager.adapter = binding.pager.adapter = ScreenSlidePagerAdapter(this)
ScreenSlidePagerAdapter(supportFragmentManager, AppColors(this@ReaderActivity)) binding.pager.setCurrentItem(currentItem, false)
binding.pager.currentItem = currentItem
} }
override fun onResume() { override fun onResume() {
super.onResume() super.onResume()
notifyAdapter() binding.indicator.setViewPager(binding.pager)
binding.pager.setPageTransformer(true, DepthPageTransformer())
(binding.indicator as CircleIndicator).setViewPager(binding.pager)
binding.pager.addOnPageChangeListener(
object : ViewPager.SimpleOnPageChangeListener() {
override fun onPageSelected(position: Int) {
if (allItems[position].starred) {
canRemoveFromFavorite()
} else {
canFavorite()
}
readItem(allItems[position])
}
}
)
} }
private fun readItem(item: Item) { private fun readItem(item: Item) {
@ -151,46 +128,39 @@ class ReaderActivity : AppCompatActivity() {
} }
} }
private fun notifyAdapter() {
(binding.pager.adapter as ScreenSlidePagerAdapter).notifyDataSetChanged()
}
override fun onPause() {
super.onPause()
if (markOnScroll) {
binding.pager.clearOnPageChangeListeners()
}
}
override fun onSaveInstanceState(oldInstanceState: Bundle) { override fun onSaveInstanceState(oldInstanceState: Bundle) {
super.onSaveInstanceState(oldInstanceState) super.onSaveInstanceState(oldInstanceState)
oldInstanceState.clear() oldInstanceState.clear()
} }
private inner class ScreenSlidePagerAdapter(fm: FragmentManager, val appColors: AppColors) : private inner class ScreenSlidePagerAdapter(fa: FragmentActivity) :
FragmentStatePagerAdapter(fm) { FragmentStateAdapter(fa) {
override fun getItemCount(): Int = allItems.size
override fun createFragment(position: Int): Fragment = ArticleFragment.newInstance(allItems[position])
override fun getCount(): Int {
return allItems.size
} }
override fun getItem(position: Int): ArticleFragment { override fun onKeyDown(keyCode: Int, event: KeyEvent?): Boolean {
return ArticleFragment.newInstance(position, allItems) return when (keyCode) {
KeyEvent.KEYCODE_VOLUME_DOWN -> {
val currentFragment = supportFragmentManager.findFragmentByTag("f" + binding.pager.currentItem) as ArticleFragment
currentFragment.scrollDown()
true
}
KeyEvent.KEYCODE_VOLUME_UP -> {
val currentFragment = supportFragmentManager.findFragmentByTag("f" + binding.pager.currentItem) as ArticleFragment
currentFragment.scrollUp()
true
}
else -> {
super.onKeyDown(keyCode, event)
} }
override fun startUpdate(container: ViewGroup) {
super.startUpdate(container)
container.background = ColorDrawable(
ContextCompat.getColor(
this@ReaderActivity,
appColors.colorBackground
)
)
} }
} }
fun alignmentMenu(showJustify: Boolean) { private fun alignmentMenu(showJustify: Boolean) {
toolbarMenu.findItem(R.id.align_left).isVisible = !showJustify toolbarMenu.findItem(R.id.align_left).isVisible = !showJustify
toolbarMenu.findItem(R.id.align_justify).isVisible = showJustify toolbarMenu.findItem(R.id.align_justify).isVisible = showJustify
} }
@ -200,7 +170,7 @@ class ReaderActivity : AppCompatActivity() {
inflater.inflate(R.menu.reader_menu, menu) inflater.inflate(R.menu.reader_menu, menu)
toolbarMenu = menu toolbarMenu = menu
if (!allItems.isEmpty() && allItems[currentItem].starred) { if (allItems.isNotEmpty() && allItems[currentItem].starred) {
canRemoveFromFavorite() canRemoveFromFavorite()
} else { } else {
canFavorite() canFavorite()
@ -211,6 +181,22 @@ class ReaderActivity : AppCompatActivity() {
alignmentMenu(true) alignmentMenu(true)
} }
binding.pager.registerOnPageChangeCallback(
object : ViewPager2.OnPageChangeCallback() {
override fun onPageSelected(position: Int) {
super.onPageSelected(position)
if (allItems[position].starred) {
canRemoveFromFavorite()
} else {
canFavorite()
}
readItem(allItems[position])
}
}
)
return true return true
} }
@ -218,13 +204,11 @@ class ReaderActivity : AppCompatActivity() {
fun afterSave() { fun afterSave() {
allItems[binding.pager.currentItem] = allItems[binding.pager.currentItem] =
allItems[binding.pager.currentItem].toggleStar() allItems[binding.pager.currentItem].toggleStar()
notifyAdapter()
canRemoveFromFavorite() canRemoveFromFavorite()
} }
fun afterUnsave() { fun afterUnsave() {
allItems[binding.pager.currentItem] = allItems[binding.pager.currentItem].toggleStar() allItems[binding.pager.currentItem] = allItems[binding.pager.currentItem].toggleStar()
notifyAdapter()
canFavorite() canFavorite()
} }

View File

@ -28,17 +28,17 @@ class SourcesActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
appColors = AppColors(this@SourcesActivity) appColors = AppColors(this@SourcesActivity)
super.onCreate(savedInstanceState)
binding = ActivitySourcesBinding.inflate(layoutInflater) binding = ActivitySourcesBinding.inflate(layoutInflater)
val view = binding.root val view = binding.root
setContentView(view)
val scoop = Scoop.getInstance() val scoop = Scoop.getInstance()
scoop.bind(this, Toppings.PRIMARY.value, binding.toolbar) scoop.bind(this, Toppings.PRIMARY.value, binding.toolbar)
scoop.bindStatusBar(this, Toppings.PRIMARY_DARK.value) scoop.bindStatusBar(this, Toppings.PRIMARY_DARK.value)
super.onCreate(savedInstanceState)
setContentView(view)
setSupportActionBar(binding.toolbar) setSupportActionBar(binding.toolbar)
supportActionBar?.setDisplayHomeAsUpEnabled(true) supportActionBar?.setDisplayHomeAsUpEnabled(true)
supportActionBar?.setDisplayShowHomeEnabled(true) supportActionBar?.setDisplayShowHomeEnabled(true)
@ -71,7 +71,7 @@ class SourcesActivity : AppCompatActivity() {
binding.recyclerView.setHasFixedSize(true) binding.recyclerView.setHasFixedSize(true)
binding.recyclerView.layoutManager = mLayoutManager binding.recyclerView.layoutManager = mLayoutManager
if (this@SourcesActivity.isNetworkAccessible(this@SourcesActivity.findViewById(R.id.recyclerView))) { if (this@SourcesActivity.isNetworkAccessible(binding.recyclerView)) {
api.sources.enqueue(object : Callback<List<Source>> { api.sources.enqueue(object : Callback<List<Source>> {
override fun onResponse( override fun onResponse(
call: Call<List<Source>>, call: Call<List<Source>>,

View File

@ -7,7 +7,6 @@ import android.view.LayoutInflater
import android.view.View import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import android.widget.ImageView.ScaleType import android.widget.ImageView.ScaleType
import androidx.core.content.ContextCompat
import apps.amine.bou.readerforselfoss.R import apps.amine.bou.readerforselfoss.R
import apps.amine.bou.readerforselfoss.api.selfoss.Item import apps.amine.bou.readerforselfoss.api.selfoss.Item
import apps.amine.bou.readerforselfoss.api.selfoss.SelfossApi import apps.amine.bou.readerforselfoss.api.selfoss.SelfossApi
@ -61,10 +60,6 @@ class ItemCardAdapter(
binding.favButton.isSelected = itm.starred binding.favButton.isSelected = itm.starred
binding.title.text = itm.getTitleDecoded() binding.title.text = itm.getTitleDecoded()
binding.title.setTextColor(ContextCompat.getColor(
c,
appColors.textColor
))
binding.title.setOnTouchListener(LinkOnTouchListener()) binding.title.setOnTouchListener(LinkOnTouchListener())
@ -72,11 +67,6 @@ class ItemCardAdapter(
binding.sourceTitleAndDate.text = itm.sourceAndDateText() binding.sourceTitleAndDate.text = itm.sourceAndDateText()
binding.sourceTitleAndDate.setTextColor(ContextCompat.getColor(
c,
appColors.textColor
))
if (!fullHeightCards) { if (!fullHeightCards) {
binding.itemImage.maxHeight = imageMaxHeight binding.itemImage.maxHeight = imageMaxHeight
binding.itemImage.scaleType = ScaleType.CENTER_CROP binding.itemImage.scaleType = ScaleType.CENTER_CROP
@ -112,7 +102,6 @@ class ItemCardAdapter(
inner class ViewHolder(val binding: CardItemBinding) : RecyclerView.ViewHolder(binding.root) { inner class ViewHolder(val binding: CardItemBinding) : RecyclerView.ViewHolder(binding.root) {
init { init {
binding.root.setCardBackgroundColor(appColors.cardBackgroundColor)
handleClickListeners() handleClickListeners()
handleCustomTabActions() handleCustomTabActions()
} }

View File

@ -5,7 +5,6 @@ import android.content.Context
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.ViewGroup import android.view.ViewGroup
import androidx.core.content.ContextCompat
import apps.amine.bou.readerforselfoss.api.selfoss.Item import apps.amine.bou.readerforselfoss.api.selfoss.Item
import apps.amine.bou.readerforselfoss.api.selfoss.SelfossApi import apps.amine.bou.readerforselfoss.api.selfoss.SelfossApi
import apps.amine.bou.readerforselfoss.databinding.ListItemBinding import apps.amine.bou.readerforselfoss.databinding.ListItemBinding
@ -49,25 +48,14 @@ class ItemListAdapter(
with(holder) { with(holder) {
val itm = items[position] val itm = items[position]
binding.title.text = itm.getTitleDecoded() binding.title.text = itm.getTitleDecoded()
binding.title.setTextColor(ContextCompat.getColor(
c,
appColors.textColor
))
binding.title.setOnTouchListener(LinkOnTouchListener()) binding.title.setOnTouchListener(LinkOnTouchListener())
binding.title.setLinkTextColor(appColors.colorAccent) binding.title.setLinkTextColor(appColors.colorAccent)
binding.sourceTitleAndDate.text = itm.sourceAndDateText() binding.sourceTitleAndDate.text = itm.sourceAndDateText()
binding.sourceTitleAndDate.setTextColor(ContextCompat.getColor(
c,
appColors.textColor
))
if (itm.getThumbnail(c).isEmpty()) { if (itm.getThumbnail(c).isEmpty()) {
if (itm.getIcon(c).isEmpty()) { if (itm.getIcon(c).isEmpty()) {

View File

@ -39,13 +39,13 @@ suspend fun updateItems(context: Context, api: SelfossApi, db: AppDatabase) = co
} }
} }
suspend fun refreshFocusedItems(context: Context, api: SelfossApi, db: AppDatabase) = withContext(Dispatchers.IO) { suspend fun refreshFocusedItems(context: Context, api: SelfossApi, db: AppDatabase, itemsNumber: Int) = withContext(Dispatchers.IO) {
if (isNetworkAvailable(context)) { if (isNetworkAvailable(context)) {
val response = when (SharedItems.displayedItems) { val response = when (SharedItems.displayedItems) {
"read" -> api.readItems(200, 0) "read" -> api.readItems(itemsNumber, 0)
"unread" -> api.newItems(200, 0) "unread" -> api.newItems(itemsNumber, 0)
"starred" -> api.starredItems(200, 0) "starred" -> api.starredItems(itemsNumber, 0)
else -> api.readItems(200, 0) else -> api.readItems(itemsNumber, 0)
} }
if (response.isSuccessful) { if (response.isSuccessful) {
@ -55,33 +55,33 @@ suspend fun refreshFocusedItems(context: Context, api: SelfossApi, db: AppDataba
} }
} }
suspend fun getReadItems(context: Context, api: SelfossApi, db: AppDatabase, offset: Int) = withContext(Dispatchers.IO) { suspend fun getReadItems(context: Context, api: SelfossApi, db: AppDatabase, itemsNumber: Int, offset: Int) = withContext(Dispatchers.IO) {
if (isNetworkAvailable(context)) { if (isNetworkAvailable(context)) {
try { try {
enqueueArticles(api.readItems( 200, offset), db, false) enqueueArticles(api.readItems( itemsNumber, offset), db, false)
SharedItems.fetchedAll = true SharedItems.fetchedAll = true
SharedItems.updateDatabase(db) SharedItems.updateDatabase(db)
} catch (e: Throwable) {} } catch (e: Throwable) {}
} }
} }
suspend fun getUnreadItems(context: Context, api: SelfossApi, db: AppDatabase, offset: Int) = withContext(Dispatchers.IO) { suspend fun getUnreadItems(context: Context, api: SelfossApi, db: AppDatabase, itemsNumber: Int, offset: Int) = withContext(Dispatchers.IO) {
if (isNetworkAvailable(context)) { if (isNetworkAvailable(context)) {
try { try {
if (!SharedItems.fetchedUnread) { if (!SharedItems.fetchedUnread) {
SharedItems.clearDBItems(db) SharedItems.clearDBItems(db)
} }
enqueueArticles(api.newItems(200, offset), db, false) enqueueArticles(api.newItems(itemsNumber, offset), db, false)
SharedItems.fetchedUnread = true SharedItems.fetchedUnread = true
} catch (e: Throwable) {} } catch (e: Throwable) {}
} }
SharedItems.updateDatabase(db) SharedItems.updateDatabase(db)
} }
suspend fun getStarredItems(context: Context, api: SelfossApi, db: AppDatabase, offset: Int) = withContext(Dispatchers.IO) { suspend fun getStarredItems(context: Context, api: SelfossApi, db: AppDatabase, itemsNumber: Int, offset: Int) = withContext(Dispatchers.IO) {
if (isNetworkAvailable(context)) { if (isNetworkAvailable(context)) {
try { try {
enqueueArticles(api.starredItems(200, offset), db, false) enqueueArticles(api.starredItems(itemsNumber, offset), db, false)
SharedItems.fetchedStarred = true SharedItems.fetchedStarred = true
SharedItems.updateDatabase(db) SharedItems.updateDatabase(db)
} catch (e: Throwable) { } catch (e: Throwable) {

View File

@ -14,6 +14,8 @@ import com.bumptech.glide.Glide
import com.bumptech.glide.load.engine.DiskCacheStrategy import com.bumptech.glide.load.engine.DiskCacheStrategy
import com.bumptech.glide.request.RequestOptions import com.bumptech.glide.request.RequestOptions
import com.google.gson.annotations.SerializedName import com.google.gson.annotations.SerializedName
import java.util.*
import kotlin.collections.ArrayList
private fun constructUrl(config: Config?, path: String, file: String?): String { private fun constructUrl(config: Config?, path: String, file: String?): String {
return if (file.isEmptyOrNullOrNullString()) { return if (file.isEmptyOrNullOrNullString()) {
@ -155,14 +157,14 @@ data class Item(
} }
fun getImages() : ArrayList<String> { fun getImages() : ArrayList<String> {
var allImages = ArrayList<String>() val allImages = ArrayList<String>()
for ( image in Jsoup.parse(content).getElementsByTag("img")) { for ( image in Jsoup.parse(content).getElementsByTag("img")) {
val url = image.attr("src") val url = image.attr("src")
if (url.toLowerCase().contains(".jpg") || if (url.lowercase(Locale.US).contains(".jpg") ||
url.toLowerCase().contains(".jpeg") || url.lowercase(Locale.US).contains(".jpeg") ||
url.toLowerCase().contains(".png") || url.lowercase(Locale.US).contains(".png") ||
url.toLowerCase().contains(".webp")) url.lowercase(Locale.US).contains(".webp"))
{ {
allImages.add(url) allImages.add(url)
} }

View File

@ -14,12 +14,11 @@ import androidx.preference.PreferenceManager
import android.view.* import android.view.*
import android.webkit.* import android.webkit.*
import android.widget.Toast import android.widget.Toast
import androidx.browser.customtabs.CustomTabsIntent
import com.google.android.material.floatingactionbutton.FloatingActionButton import com.google.android.material.floatingactionbutton.FloatingActionButton
import androidx.fragment.app.Fragment import androidx.fragment.app.Fragment
import androidx.core.content.ContextCompat
import androidx.core.widget.NestedScrollView import androidx.core.widget.NestedScrollView
import androidx.appcompat.app.AlertDialog import androidx.appcompat.app.AlertDialog
import androidx.browser.customtabs.CustomTabsIntent
import androidx.core.content.res.ResourcesCompat import androidx.core.content.res.ResourcesCompat
import androidx.room.Room import androidx.room.Room
import apps.amine.bou.readerforselfoss.ImageActivity import apps.amine.bou.readerforselfoss.ImageActivity
@ -48,14 +47,14 @@ import retrofit2.Callback
import retrofit2.Response import retrofit2.Response
import java.net.MalformedURLException import java.net.MalformedURLException
import java.net.URL import java.net.URL
import java.util.*
import java.util.concurrent.ExecutionException import java.util.concurrent.ExecutionException
import kotlin.collections.ArrayList import kotlin.collections.ArrayList
class ArticleFragment : Fragment() { class ArticleFragment : Fragment() {
private lateinit var pageNumber: Number
private var fontSize: Int = 16 private var fontSize: Int = 16
private lateinit var allItems: ArrayList<Item> private lateinit var item: Item
private var mCustomTabActivityHelper: CustomTabActivityHelper? = null; private var mCustomTabActivityHelper: CustomTabActivityHelper? = null
private lateinit var url: String private lateinit var url: String
private lateinit var contentText: String private lateinit var contentText: String
private lateinit var contentSource: String private lateinit var contentSource: String
@ -91,8 +90,7 @@ class ArticleFragment : Fragment() {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
pageNumber = requireArguments().getInt(ARG_POSITION) item = requireArguments().getParcelable(ARG_ITEMS)!!
allItems = requireArguments().getParcelableArrayList<Item>(ARG_ITEMS) as ArrayList<Item>
db = Room.databaseBuilder( db = Room.databaseBuilder(
requireContext(), requireContext(),
@ -104,16 +102,16 @@ class ArticleFragment : Fragment() {
inflater: LayoutInflater, inflater: LayoutInflater,
container: ViewGroup?, container: ViewGroup?,
savedInstanceState: Bundle? savedInstanceState: Bundle?
): View? { ): View {
try { try {
_binding = FragmentArticleBinding.inflate(inflater, container, false) _binding = FragmentArticleBinding.inflate(inflater, container, false)
url = allItems[pageNumber.toInt()].getLinkDecoded() url = item.getLinkDecoded()
contentText = allItems[pageNumber.toInt()].content contentText = item.content
contentTitle = allItems[pageNumber.toInt()].getTitleDecoded() contentTitle = item.getTitleDecoded()
contentImage = allItems[pageNumber.toInt()].getThumbnail(requireActivity()) contentImage = item.getThumbnail(requireActivity())
contentSource = allItems[pageNumber.toInt()].sourceAndDateText() contentSource = item.sourceAndDateText()
allImages = allItems[pageNumber.toInt()].getImages() allImages = item.getImages()
prefs = PreferenceManager.getDefaultSharedPreferences(activity) prefs = PreferenceManager.getDefaultSharedPreferences(activity)
editor = prefs.edit() editor = prefs.edit()
@ -163,26 +161,18 @@ class ArticleFragment : Fragment() {
object : FloatingToolbar.ItemClickListener { object : FloatingToolbar.ItemClickListener {
override fun onItemClick(item: MenuItem) { override fun onItemClick(item: MenuItem) {
when (item.itemId) { when (item.itemId) {
R.id.more_action -> getContentFromMercury(customTabsIntent, prefs) R.id.more_action -> getContentFromMercury(customTabsIntent)
R.id.share_action -> requireActivity().shareLink(url, contentTitle) R.id.share_action -> requireActivity().shareLink(url, contentTitle)
R.id.open_action -> requireActivity().openItemUrl( R.id.open_action -> requireActivity().openInBrowserAsNewTask(this@ArticleFragment.item)
allItems,
pageNumber.toInt(),
url,
customTabsIntent,
false,
false,
requireActivity()
)
R.id.unread_action -> if (context != null) { R.id.unread_action -> if (context != null) {
if (allItems[pageNumber.toInt()].unread) { if (this@ArticleFragment.item.unread) {
SharedItems.readItem( SharedItems.readItem(
context!!, context!!,
api, api,
db, db,
allItems[pageNumber.toInt()] this@ArticleFragment.item
) )
allItems[pageNumber.toInt()].unread = false this@ArticleFragment.item.unread = false
Toast.makeText( Toast.makeText(
context, context,
R.string.marked_as_read, R.string.marked_as_read,
@ -193,9 +183,9 @@ class ArticleFragment : Fragment() {
context!!, context!!,
api, api,
db, db,
allItems[pageNumber.toInt()] this@ArticleFragment.item
) )
allItems[pageNumber.toInt()].unread = true this@ArticleFragment.item.unread = true
Toast.makeText( Toast.makeText(
context, context,
R.string.marked_as_unread, R.string.marked_as_unread,
@ -223,7 +213,7 @@ class ArticleFragment : Fragment() {
} }
if (contentText.isEmptyOrNullOrNullString()) { if (contentText.isEmptyOrNullOrNullString()) {
getContentFromMercury(customTabsIntent, prefs) getContentFromMercury(customTabsIntent)
} else { } else {
binding.titleView.text = contentTitle binding.titleView.text = contentTitle
if (typeface != null) { if (typeface != null) {
@ -265,11 +255,11 @@ class ArticleFragment : Fragment() {
.setMessage(requireContext().getString(R.string.webview_dialog_issue_message)) .setMessage(requireContext().getString(R.string.webview_dialog_issue_message))
.setTitle(requireContext().getString(R.string.webview_dialog_issue_title)) .setTitle(requireContext().getString(R.string.webview_dialog_issue_title))
.setPositiveButton(android.R.string.ok .setPositiveButton(android.R.string.ok
) { dialog, which -> ) { _, _ ->
val sharedPref = PreferenceManager.getDefaultSharedPreferences(requireContext()) val sharedPref = PreferenceManager.getDefaultSharedPreferences(requireContext())
val editor = sharedPref.edit() val editor = sharedPref.edit()
editor.putBoolean("prefer_article_viewer", false) editor.putBoolean("prefer_article_viewer", false)
editor.commit() editor.apply()
requireActivity().finish() requireActivity().finish()
} }
.create() .create()
@ -292,10 +282,7 @@ class ArticleFragment : Fragment() {
} }
} }
private fun getContentFromMercury( private fun getContentFromMercury(customTabsIntent: CustomTabsIntent) {
customTabsIntent: CustomTabsIntent,
prefs: SharedPreferences
) {
if ((context != null && requireContext().isNetworkAccessible(null)) || context == null) { if ((context != null && requireContext().isNetworkAccessible(null)) || context == null) {
binding.progressBar.visibility = View.VISIBLE binding.progressBar.visibility = View.VISIBLE
val parser = MercuryApi() val parser = MercuryApi()
@ -390,42 +377,12 @@ class ArticleFragment : Fragment() {
binding.webcontent.settings.standardFontFamily = a.getString(0) binding.webcontent.settings.standardFontFamily = a.getString(0)
binding.webcontent.visibility = View.VISIBLE binding.webcontent.visibility = View.VISIBLE
val (textColor, backgroundColor) = if (appColors.isDarkTheme) {
if (context != null) {
binding.webcontent.setBackgroundColor(
ContextCompat.getColor(
requireContext(),
R.color.dark_webview
)
)
Pair(ContextCompat.getColor(requireContext(), R.color.dark_webview_text), ContextCompat.getColor(requireContext(), R.color.dark_webview))
} else {
Pair(null, null)
}
} else {
if (context != null) {
binding.webcontent.setBackgroundColor(
ContextCompat.getColor(
requireContext(),
R.color.light_webview
)
)
Pair(ContextCompat.getColor(requireContext(), R.color.light_webview_text), ContextCompat.getColor(requireContext(), R.color.light_webview))
} else {
Pair(null, null)
}
}
val stringTextColor: String = if (textColor != null) { // TODO: Set the color strings programmatically
String.format("#%06X", 0xFFFFFF and textColor) val (stringTextColor, stringBackgroundColor) = if (appColors.isDarkTheme) {
Pair("#FFFFFF", "#303030")
} else { } else {
"#000000" Pair("#212121", "#FAFAFA")
}
val stringBackgroundColor = if (backgroundColor != null) {
String.format("#%06X", 0xFFFFFF and backgroundColor)
} else {
"#FFFFFF"
} }
binding.webcontent.settings.useWideViewPort = true binding.webcontent.settings.useWideViewPort = true
@ -442,19 +399,19 @@ class ArticleFragment : Fragment() {
override fun shouldInterceptRequest(view: WebView?, url: String): WebResourceResponse? { override fun shouldInterceptRequest(view: WebView?, url: String): WebResourceResponse? {
val glideOptions = RequestOptions.diskCacheStrategyOf(DiskCacheStrategy.ALL) val glideOptions = RequestOptions.diskCacheStrategyOf(DiskCacheStrategy.ALL)
if (url.toLowerCase().contains(".jpg") || url.toLowerCase().contains(".jpeg")) { if (url.lowercase(Locale.US).contains(".jpg") || url.lowercase(Locale.US).contains(".jpeg")) {
try { try {
val image = Glide.with(view).asBitmap().apply(glideOptions).load(url).submit().get() val image = Glide.with(view).asBitmap().apply(glideOptions).load(url).submit().get()
return WebResourceResponse("image/jpg", "UTF-8", getBitmapInputStream(image, Bitmap.CompressFormat.JPEG)) return WebResourceResponse("image/jpg", "UTF-8", getBitmapInputStream(image, Bitmap.CompressFormat.JPEG))
}catch ( e : ExecutionException) {} }catch ( e : ExecutionException) {}
} }
else if (url.toLowerCase().contains(".png")) { else if (url.lowercase(Locale.US).contains(".png")) {
try { try {
val image = Glide.with(view).asBitmap().apply(glideOptions).load(url).submit().get() val image = Glide.with(view).asBitmap().apply(glideOptions).load(url).submit().get()
return WebResourceResponse("image/jpg", "UTF-8", getBitmapInputStream(image, Bitmap.CompressFormat.PNG)) return WebResourceResponse("image/jpg", "UTF-8", getBitmapInputStream(image, Bitmap.CompressFormat.PNG))
}catch ( e : ExecutionException) {} }catch ( e : ExecutionException) {}
} }
else if (url.toLowerCase().contains(".webp")) { else if (url.lowercase(Locale.US).contains(".webp")) {
try { try {
val image = Glide.with(view).asBitmap().apply(glideOptions).load(url).submit().get() val image = Glide.with(view).asBitmap().apply(glideOptions).load(url).submit().get()
return WebResourceResponse("image/jpg", "UTF-8", getBitmapInputStream(image, Bitmap.CompressFormat.WEBP)) return WebResourceResponse("image/jpg", "UTF-8", getBitmapInputStream(image, Bitmap.CompressFormat.WEBP))
@ -554,31 +511,34 @@ class ArticleFragment : Fragment() {
) )
} }
fun scrollDown() {
val height = binding.nestedScrollView.measuredHeight
binding.nestedScrollView.smoothScrollBy(0, height/2)
}
fun scrollUp() {
val height = binding.nestedScrollView.measuredHeight
binding.nestedScrollView.smoothScrollBy(0, -height/2)
}
private fun openInBrowserAfterFailing(customTabsIntent: CustomTabsIntent) { private fun openInBrowserAfterFailing(customTabsIntent: CustomTabsIntent) {
binding.progressBar.visibility = View.GONE binding.progressBar.visibility = View.GONE
requireActivity().openItemUrl( requireActivity().openItemUrlInternalBrowser(
allItems,
pageNumber.toInt(),
url, url,
customTabsIntent, customTabsIntent,
true,
false,
requireActivity() requireActivity()
) )
} }
companion object { companion object {
private const val ARG_POSITION = "position"
private const val ARG_ITEMS = "items" private const val ARG_ITEMS = "items"
fun newInstance( fun newInstance(
position: Int, item: Item
allItems: ArrayList<Item>
): ArticleFragment { ): ArticleFragment {
val fragment = ArticleFragment() val fragment = ArticleFragment()
val args = Bundle() val args = Bundle()
args.putInt(ARG_POSITION, position) args.putParcelable(ARG_ITEMS, item)
args.putParcelableArrayList(ARG_ITEMS, allItems)
fragment.arguments = args fragment.arguments = args
return fragment return fragment
} }

View File

@ -13,7 +13,10 @@ import androidx.core.widget.addTextChangedListener
import androidx.preference.Preference import androidx.preference.Preference
import androidx.preference.PreferenceFragmentCompat import androidx.preference.PreferenceFragmentCompat
import apps.amine.bou.readerforselfoss.R import apps.amine.bou.readerforselfoss.R
import apps.amine.bou.readerforselfoss.databinding.ActivitySettingsBinding
import apps.amine.bou.readerforselfoss.themes.Toppings
import apps.amine.bou.readerforselfoss.utils.Config import apps.amine.bou.readerforselfoss.utils.Config
import com.ftinc.scoop.Scoop
import java.lang.NumberFormatException import java.lang.NumberFormatException
private const val TITLE_TAG = "settingsActivityTitle" private const val TITLE_TAG = "settingsActivityTitle"
@ -26,7 +29,13 @@ class SettingsActivity : AppCompatActivity(),
if (PreferenceManager.getDefaultSharedPreferences(this).getBoolean("dark_theme", false)) { if (PreferenceManager.getDefaultSharedPreferences(this).getBoolean("dark_theme", false)) {
setTheme(R.style.NoBarDark) setTheme(R.style.NoBarDark)
} }
setContentView(R.layout.activity_settings) val binding = ActivitySettingsBinding.inflate(layoutInflater)
val scoop = Scoop.getInstance()
scoop.bind(this, Toppings.PRIMARY.value, binding.toolbar)
scoop.bindStatusBar(this, Toppings.PRIMARY_DARK.value)
setContentView(binding.root)
if (savedInstanceState == null) { if (savedInstanceState == null) {
supportFragmentManager supportFragmentManager
.beginTransaction() .beginTransaction()
@ -41,7 +50,7 @@ class SettingsActivity : AppCompatActivity(),
} }
} }
setSupportActionBar(findViewById(R.id.toolbar)) setSupportActionBar(binding.toolbar)
supportActionBar?.setDisplayHomeAsUpEnabled(true) supportActionBar?.setDisplayHomeAsUpEnabled(true)
supportActionBar?.setDisplayShowHomeEnabled(true) supportActionBar?.setDisplayShowHomeEnabled(true)

View File

@ -1,14 +1,9 @@
package apps.amine.bou.readerforselfoss.themes package apps.amine.bou.readerforselfoss.themes
import android.app.Activity import android.app.Activity
import android.content.Context
import androidx.preference.PreferenceManager
import androidx.annotation.ColorInt import androidx.annotation.ColorInt
import androidx.appcompat.view.ContextThemeWrapper import androidx.preference.PreferenceManager
import android.util.TypedValue
import apps.amine.bou.readerforselfoss.R import apps.amine.bou.readerforselfoss.R
import android.view.LayoutInflater
import android.view.ViewGroup
class AppColors(a: Activity) { class AppColors(a: Activity) {
@ -16,7 +11,6 @@ class AppColors(a: Activity) {
@ColorInt val colorPrimaryDark: Int @ColorInt val colorPrimaryDark: Int
@ColorInt val colorAccent: Int @ColorInt val colorAccent: Int
@ColorInt val colorAccentDark: Int @ColorInt val colorAccentDark: Int
@ColorInt val cardBackgroundColor: Int
@ColorInt val colorBackground: Int @ColorInt val colorBackground: Int
@ColorInt val textColor: Int @ColorInt val textColor: Int
val isDarkTheme: Boolean val isDarkTheme: Boolean
@ -55,7 +49,7 @@ class AppColors(a: Activity) {
R.color.darkBackground R.color.darkBackground
} else { } else {
a.setTheme(R.style.NoBar) a.setTheme(R.style.NoBar)
android.R.color.background_light R.color.grey_50
} }
textColor = if (isDarkTheme) { textColor = if (isDarkTheme) {
@ -63,14 +57,5 @@ class AppColors(a: Activity) {
} else { } else {
R.color.grey_900 R.color.grey_900
} }
val wrapper = Context::class.java
val method = wrapper!!.getMethod("getThemeResId")
method.isAccessible = true
val typedCardBackground = TypedValue()
a.theme.resolveAttribute(R.attr.cardBackgroundColor, typedCardBackground, true)
cardBackgroundColor = typedCardBackground.data
} }
} }

View File

@ -1,43 +0,0 @@
package apps.amine.bou.readerforselfoss.transformers
import androidx.viewpager.widget.ViewPager
import android.view.View
class DepthPageTransformer : ViewPager.PageTransformer {
override fun transformPage(view: View, position: Float) {
val pageWidth = view.width
when {
position < -1 -> // [-Infinity,-1)
// This page is way off-screen to the left.
view.alpha = 0F
position <= 0 -> { // [-1,0]
// Use the default slide transition when moving to the left page
view.alpha = 1F
view.translationX = 0F
view.scaleX = 1F
view.scaleY = 1F
}
position <= 1 -> { // (0,1]
// Fade the page out.
view.alpha = 1 - position
// Counteract the default slide transition
view.translationX = pageWidth * -position
// Scale the page down (between MIN_SCALE and 1)
val scaleFactor = MIN_SCALE + (1 - MIN_SCALE) * (1 - Math.abs(position))
view.scaleX = scaleFactor
view.scaleY = scaleFactor
}
else -> // (1,+Infinity]
// This page is way off-screen to the right.
view.alpha = 0F
}
}
companion object {
private val MIN_SCALE = 0.75f
}
}

View File

@ -13,7 +13,7 @@ fun String.longHash(): Long {
val chars = this.toCharArray() val chars = this.toCharArray()
for (i in 0 until l) { for (i in 0 until l) {
h = 31 * h + chars[i].toLong() h = 31 * h + chars[i].code.toLong()
} }
return h return h
} }

View File

@ -4,11 +4,12 @@ import android.app.Activity
import android.content.Context import android.content.Context
import android.content.Intent import android.content.Intent
import android.content.SharedPreferences import android.content.SharedPreferences
import androidx.preference.PreferenceManager
import apps.amine.bou.readerforselfoss.LoginActivity import apps.amine.bou.readerforselfoss.LoginActivity
class Config(c: Context) { class Config(c: Context) {
val settings: SharedPreferences = c.getSharedPreferences(settingsName, Context.MODE_PRIVATE) val settings: SharedPreferences = PreferenceManager.getDefaultSharedPreferences(c)
val baseUrl: String val baseUrl: String
get() = settings.getString("url", "")!! get() = settings.getString("url", "")!!
@ -42,16 +43,15 @@ class Config(c: Context) {
var apiVersion = 0 var apiVersion = 0
/* Execute logout and clear all settings to default */
fun logoutAndRedirect( fun logoutAndRedirect(
c: Context, c: Context,
callingActivity: Activity, callingActivity: Activity,
editor: SharedPreferences.Editor, editor: SharedPreferences.Editor,
baseUrlFail: Boolean = false baseUrlFail: Boolean = false
): Boolean { ): Boolean {
editor.remove("url") val settings = PreferenceManager.getDefaultSharedPreferences(c)
editor.remove("login") settings.edit().clear().commit()
editor.remove("password")
editor.apply()
val intent = Intent(c, LoginActivity::class.java) val intent = Intent(c, LoginActivity::class.java)
if (baseUrlFail) { if (baseUrlFail) {
intent.putExtra("baseUrlFail", baseUrlFail) intent.putExtra("baseUrlFail", baseUrlFail)

View File

@ -86,6 +86,18 @@ fun Context.openItemUrlInternally(
intent.putExtra("currentItem", currentItem) intent.putExtra("currentItem", currentItem)
app.startActivity(intent) app.startActivity(intent)
} else { } else {
this.openItemUrlInternalBrowser(
linkDecoded,
customTabsIntent,
app)
}
}
fun Context.openItemUrlInternalBrowser(
linkDecoded: String,
customTabsIntent: CustomTabsIntent,
app: Activity
) {
try { try {
CustomTabActivityHelper.openCustomTab( CustomTabActivityHelper.openCustomTab(
app, app,
@ -99,7 +111,6 @@ fun Context.openItemUrlInternally(
} catch (e: Exception) { } catch (e: Exception) {
openInBrowser(linkDecoded, app) openInBrowser(linkDecoded, app)
} }
}
} }
fun Context.openItemUrl( fun Context.openItemUrl(
@ -121,7 +132,7 @@ fun Context.openItemUrl(
} else { } else {
if (!internalBrowser) { if (!internalBrowser) {
openInBrowser(linkDecoded, app) openInBrowser(linkDecoded, app)
} else { } else if (articleViewer) {
this.openItemUrlInternally( this.openItemUrlInternally(
allItems, allItems,
currentItem, currentItem,
@ -130,6 +141,12 @@ fun Context.openItemUrl(
articleViewer, articleViewer,
app app
) )
} else {
this.openItemUrlInternalBrowser(
linkDecoded,
customTabsIntent,
app
)
} }
} }
} }

View File

@ -7,7 +7,7 @@
tools:context="apps.amine.bou.readerforselfoss.AddSourceActivity"> tools:context="apps.amine.bou.readerforselfoss.AddSourceActivity">
<LinearLayout <LinearLayout
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="wrap_content"
android:orientation="vertical"> android:orientation="vertical">
<com.google.android.material.appbar.AppBarLayout <com.google.android.material.appbar.AppBarLayout
@ -55,7 +55,8 @@
android:layout_marginRight="16dp" android:layout_marginRight="16dp"
android:layout_marginStart="16dp" android:layout_marginStart="16dp"
app:layout_constraintLeft_toLeftOf="parent" app:layout_constraintLeft_toLeftOf="parent"
android:layout_marginLeft="16dp"/> android:layout_marginLeft="16dp"
android:gravity="center_horizontal" />
<EditText <EditText
android:layout_width="match_parent" android:layout_width="match_parent"
@ -67,7 +68,9 @@
app:layout_constraintLeft_toLeftOf="parent" app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent" app:layout_constraintRight_toRightOf="parent"
android:inputType="text" android:inputType="text"
android:hint="@string/add_source_hint_name"/> android:hint="@string/add_source_hint_name"
android:textColorHint="?android:textColorPrimary"
android:autofillHints="false" />
<EditText <EditText
android:layout_width="match_parent" android:layout_width="match_parent"
@ -76,10 +79,12 @@
android:ems="10" android:ems="10"
android:id="@+id/sourceUri" android:id="@+id/sourceUri"
android:hint="@string/add_source_hint_url" android:hint="@string/add_source_hint_url"
android:textColorHint="?android:textColorPrimary"
android:layout_marginTop="16dp" android:layout_marginTop="16dp"
app:layout_constraintTop_toBottomOf="@+id/nameInput" app:layout_constraintTop_toBottomOf="@+id/nameInput"
app:layout_constraintRight_toRightOf="parent" app:layout_constraintRight_toRightOf="parent"
app:layout_constraintLeft_toLeftOf="parent"/> app:layout_constraintLeft_toLeftOf="parent"
android:autofillHints="false" />
<EditText <EditText
android:layout_width="match_parent" android:layout_width="match_parent"
@ -91,7 +96,9 @@
android:layout_marginTop="16dp" android:layout_marginTop="16dp"
app:layout_constraintTop_toBottomOf="@+id/sourceUri" app:layout_constraintTop_toBottomOf="@+id/sourceUri"
android:hint="@string/add_source_hint_tags" android:hint="@string/add_source_hint_tags"
android:inputType="text"/> android:textColorHint="?android:textColorPrimary"
android:inputType="text"
android:autofillHints="false" />
<Spinner <Spinner
android:layout_width="match_parent" android:layout_width="match_parent"
@ -100,7 +107,8 @@
app:layout_constraintTop_toBottomOf="@+id/tags" app:layout_constraintTop_toBottomOf="@+id/tags"
app:layout_constraintRight_toRightOf="parent" app:layout_constraintRight_toRightOf="parent"
app:layout_constraintLeft_toLeftOf="parent" app:layout_constraintLeft_toLeftOf="parent"
android:layout_height="40dp"/> android:layout_height="40dp"
android:theme="@style/App.Spinner"/>
<Button <Button
android:text="@string/add_source_save" android:text="@string/add_source_save"

View File

@ -21,7 +21,7 @@
</com.google.android.material.appbar.AppBarLayout> </com.google.android.material.appbar.AppBarLayout>
<androidx.viewpager.widget.ViewPager <androidx.viewpager2.widget.ViewPager2
android:id="@+id/pager" android:id="@+id/pager"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="0dp" android:layout_height="0dp"

View File

@ -45,98 +45,69 @@
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:orientation="vertical"> android:orientation="vertical">
<com.google.android.material.textfield.TextInputLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="@+id/urlLayout"
>
<EditText <EditText
android:id="@+id/urlView" android:id="@+id/urlView"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:hint="@string/prompt_url" android:hint="@string/prompt_url"
android:imeOptions="actionUnspecified" android:imeOptions="actionUnspecified"
android:importantForAutofill="no"
android:inputType="textUri" android:inputType="textUri"
android:maxLines="1" /> android:maxLines="1" />
</com.google.android.material.textfield.TextInputLayout> <com.google.android.material.switchmaterial.SwitchMaterial
<Switch
android:text="@string/withLoginSwitch" android:text="@string/withLoginSwitch"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="0dp" android:layout_height="0dp"
android:id="@+id/withLogin" android:id="@+id/withLogin"
android:layout_weight="1"/> android:layout_weight="1"/>
<com.google.android.material.textfield.TextInputLayout <EditText
android:id="@+id/loginLayout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:visibility="gone">
<AutoCompleteTextView
android:id="@+id/loginView" android:id="@+id/loginView"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:autofillHints="username"
android:hint="@string/prompt_login" android:hint="@string/prompt_login"
android:inputType="text" android:inputType="text"
android:maxLines="1" /> android:maxLines="1"
android:visibility="gone" />
</com.google.android.material.textfield.TextInputLayout>
<com.google.android.material.textfield.TextInputLayout
android:id="@+id/passwordLayout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:visibility="gone">
<EditText <EditText
android:id="@+id/passwordView" android:id="@+id/passwordView"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:autofillHints="password"
android:hint="@string/prompt_password" android:hint="@string/prompt_password"
android:inputType="textPassword" android:inputType="textPassword"
android:maxLines="1" /> android:maxLines="1"
android:visibility="gone" />
</com.google.android.material.textfield.TextInputLayout> <com.google.android.material.switchmaterial.SwitchMaterial
<Switch
android:id="@+id/withHttpLogin" android:id="@+id/withHttpLogin"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_weight="1" android:layout_weight="1"
android:text="@string/withHttpLoginSwitch" /> android:text="@string/withHttpLoginSwitch" />
<com.google.android.material.textfield.TextInputLayout
android:id="@+id/httpLoginInput"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:visibility="gone">
<EditText <EditText
android:inputType="text"
android:id="@+id/httpLoginView" android:id="@+id/httpLoginView"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:hint="@string/prompt_http_login" /> android:autofillHints="username"
</com.google.android.material.textfield.TextInputLayout> android:hint="@string/prompt_http_login"
android:inputType="text"
<com.google.android.material.textfield.TextInputLayout android:visibility="gone" />
android:id="@+id/httpPasswordInput"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:visibility="gone">
<EditText <EditText
android:id="@+id/httpPasswordView" android:id="@+id/httpPasswordView"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:autofillHints="password"
android:hint="@string/prompt_http_password" android:hint="@string/prompt_http_password"
android:inputType="textPassword" /> android:inputType="textPassword"
</com.google.android.material.textfield.TextInputLayout> android:visibility="gone" />
<Switch <com.google.android.material.switchmaterial.SwitchMaterial
android:id="@+id/withSelfhostedCert" android:id="@+id/withSelfhostedCert"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
@ -155,8 +126,8 @@
style="?android:textAppearanceSmall" style="?android:textAppearanceSmall"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginBottom="16dp"
android:layout_marginTop="16dp" android:layout_marginTop="16dp"
android:layout_marginBottom="16dp"
android:text="@string/action_sign_in" android:text="@string/action_sign_in"
android:textStyle="bold" /> android:textStyle="bold" />

View File

@ -22,7 +22,7 @@
</com.google.android.material.appbar.AppBarLayout> </com.google.android.material.appbar.AppBarLayout>
<androidx.viewpager.widget.ViewPager <androidx.viewpager2.widget.ViewPager2
android:id="@+id/pager" android:id="@+id/pager"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="0dp" android:layout_height="0dp"
@ -33,7 +33,7 @@
app:layout_constraintTop_toBottomOf="@+id/appBarLayout" /> app:layout_constraintTop_toBottomOf="@+id/appBarLayout" />
<me.relex.circleindicator.CircleIndicator <me.relex.circleindicator.CircleIndicator3
android:id="@+id/indicator" android:id="@+id/indicator"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="20dp" android:layout_height="20dp"

View File

@ -31,14 +31,13 @@
android:id="@+id/fab" android:id="@+id/fab"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_gravity="end|bottom|right" android:layout_gravity="end|bottom|end"
android:src="@drawable/ic_add_white_24dp" app:srcCompat="@drawable/ic_add_white_24dp"
android:paddingBottom="@dimen/activity_vertical_margin" android:paddingBottom="@dimen/activity_vertical_margin"
android:paddingTop="@dimen/activity_vertical_margin" android:paddingTop="@dimen/activity_vertical_margin"
android:layout_alignParentBottom="true" android:layout_alignParentBottom="true"
android:layout_alignParentRight="true"
android:layout_alignParentEnd="true" android:layout_alignParentEnd="true"
android:layout_marginBottom="16dp" android:layout_marginBottom="16dp"
android:layout_marginEnd="16dp" android:layout_marginEnd="16dp"
android:layout_marginRight="16dp"/> android:contentDescription="@string/add_source" />
</androidx.coordinatorlayout.widget.CoordinatorLayout> </androidx.coordinatorlayout.widget.CoordinatorLayout>

View File

@ -16,7 +16,8 @@
app:layout_constraintTop_toTopOf="parent" app:layout_constraintTop_toTopOf="parent"
card_view:cardElevation="2dp" card_view:cardElevation="2dp"
card_view:cardUseCompatPadding="true" card_view:cardUseCompatPadding="true"
card_view:layout_constraintBottom_toBottomOf="parent"> card_view:layout_constraintBottom_toBottomOf="parent"
app:cardBackgroundColor="?cardBackgroundColor">
<androidx.constraintlayout.widget.ConstraintLayout <androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent" android:layout_width="match_parent"
@ -65,6 +66,7 @@
android:gravity="start" android:gravity="start"
android:textAlignment="viewStart" android:textAlignment="viewStart"
android:textStyle="bold" android:textStyle="bold"
android:textColor="?android:textColorPrimary"
app:layout_constraintHorizontal_bias="0.0" app:layout_constraintHorizontal_bias="0.0"
app:layout_constraintLeft_toRightOf="@+id/sourceImage" app:layout_constraintLeft_toRightOf="@+id/sourceImage"
app:layout_constraintRight_toRightOf="parent" app:layout_constraintRight_toRightOf="parent"
@ -79,6 +81,7 @@
android:gravity="start" android:gravity="start"
android:textAlignment="viewStart" android:textAlignment="viewStart"
android:textSize="14sp" android:textSize="14sp"
android:textColor="?android:textColorPrimary"
app:layout_constraintLeft_toLeftOf="@+id/title" app:layout_constraintLeft_toLeftOf="@+id/title"
app:layout_constraintTop_toBottomOf="@+id/title" app:layout_constraintTop_toBottomOf="@+id/title"
tools:text="Google Actualité Il y a 5h" /> tools:text="Google Actualité Il y a 5h" />

View File

@ -4,6 +4,7 @@
xmlns:tools="http://schemas.android.com/tools" xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
android:background="?android:colorBackground"
android:descendantFocusability="blocksDescendants"> android:descendantFocusability="blocksDescendants">
<androidx.core.widget.NestedScrollView <androidx.core.widget.NestedScrollView
@ -33,7 +34,7 @@
android:layout_marginLeft="16dp" android:layout_marginLeft="16dp"
android:layout_marginTop="8dp" android:layout_marginTop="8dp"
android:layout_marginRight="16dp" android:layout_marginRight="16dp"
android:textAppearance="@style/TextAppearance.AppCompat.Small" android:textColor="?android:textColorSecondary"
android:textSize="12sp" android:textSize="12sp"
app:layout_constraintHorizontal_bias="0.0" app:layout_constraintHorizontal_bias="0.0"
app:layout_constraintLeft_toLeftOf="parent" app:layout_constraintLeft_toLeftOf="parent"
@ -65,6 +66,7 @@
android:layout_marginRight="16dp" android:layout_marginRight="16dp"
android:layout_marginTop="24dp" android:layout_marginTop="24dp"
android:paddingBottom="48dp" android:paddingBottom="48dp"
android:background="?android:colorBackground"
app:layout_constraintHorizontal_bias="0.0" app:layout_constraintHorizontal_bias="0.0"
app:layout_constraintLeft_toLeftOf="parent" app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent" app:layout_constraintRight_toRightOf="parent"

View File

@ -12,7 +12,8 @@
android:layout_marginStart="8dp" android:layout_marginStart="8dp"
android:layout_marginTop="21dp" android:layout_marginTop="21dp"
app:layout_constraintStart_toStartOf="parent" app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" /> app:layout_constraintTop_toTopOf="parent"
android:layout_marginLeft="8dp" />
<TextView <TextView
android:id="@+id/title" android:id="@+id/title"
@ -29,11 +30,14 @@
android:textAllCaps="false" android:textAllCaps="false"
android:textSize="16sp" android:textSize="16sp"
android:textStyle="bold" android:textStyle="bold"
android:textColor="?android:textColorPrimary"
app:layout_constraintEnd_toEndOf="parent" app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.0" app:layout_constraintHorizontal_bias="0.0"
app:layout_constraintStart_toEndOf="@+id/itemImage" app:layout_constraintStart_toEndOf="@+id/itemImage"
app:layout_constraintTop_toTopOf="parent" app:layout_constraintTop_toTopOf="parent"
tools:text="Titre" /> tools:text="Titre"
android:layout_marginLeft="8dp"
android:layout_marginRight="16dp" />
<TextView <TextView
android:id="@+id/sourceTitleAndDate" android:id="@+id/sourceTitleAndDate"
@ -46,10 +50,13 @@
android:maxLines="1" android:maxLines="1"
android:textAlignment="viewStart" android:textAlignment="viewStart"
android:textSize="14sp" android:textSize="14sp"
android:textColor="?android:textColorPrimary"
app:layout_constraintEnd_toEndOf="parent" app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.0" app:layout_constraintHorizontal_bias="0.0"
app:layout_constraintStart_toEndOf="@+id/itemImage" app:layout_constraintStart_toEndOf="@+id/itemImage"
app:layout_constraintTop_toTopOf="parent" app:layout_constraintTop_toTopOf="parent"
tools:text="Google Actualité Il y a 5h" /> tools:text="Google Actualité Il y a 5h"
android:layout_marginLeft="8dp"
android:layout_marginRight="16dp" />
</androidx.constraintlayout.widget.ConstraintLayout> </androidx.constraintlayout.widget.ConstraintLayout>

View File

@ -1,55 +1,50 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout <androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools" xmlns:tools="http://schemas.android.com/tools"
android:orientation="vertical"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="48dp"> android:layout_height="48dp"
android:orientation="vertical">
<ImageView <ImageView
android:id="@+id/itemImage" android:id="@+id/itemImage"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
android:layout_marginStart="16dp"
app:layout_constraintLeft_toLeftOf="parent"
android:layout_marginLeft="16dp"
android:layout_width="36dp" android:layout_width="36dp"
android:layout_height="36dp"/> android:layout_height="36dp"
android:importantForAccessibility="no"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<TextView <TextView
android:layout_width="wrap_content"
android:layout_height="17dp"
android:id="@+id/sourceTitle" android:id="@+id/sourceTitle"
app:layout_constraintTop_toTopOf="parent" android:layout_width="0dp"
app:layout_constraintBottom_toBottomOf="parent" android:layout_height="17dp"
app:layout_constraintLeft_toRightOf="@+id/itemImage" android:layout_marginStart="8dp"
android:layout_marginStart="16dp" android:ellipsize="end"
android:layout_marginLeft="16dp" android:gravity="start"
android:maxLines="1"
android:textAlignment="textStart"
android:textSize="13sp" android:textSize="13sp"
android:textAlignment="viewStart" android:textColor="?android:textColorPrimary"
tools:text="source title " app:layout_constraintBottom_toBottomOf="parent"
android:gravity="start" /> app:layout_constraintEnd_toStartOf="@+id/deleteBtn"
app:layout_constraintStart_toEndOf="@+id/itemImage"
app:layout_constraintTop_toTopOf="parent"
tools:text="source title" />
<Button <Button
android:id="@+id/deleteBtn" android:id="@+id/deleteBtn"
android:background="@drawable/ic_remove_circle_outline_black_24dp"
style="@style/Widget.AppCompat.Button.Borderless" style="@style/Widget.AppCompat.Button.Borderless"
android:layout_width="34dp"
android:layout_height="34dp"
android:layout_marginEnd="8dp"
android:background="@drawable/ic_remove_circle_outline_black_24dp"
android:backgroundTint="?android:textColorSecondary" android:backgroundTint="?android:textColorSecondary"
android:elevation="4dp" android:elevation="4dp"
android:layout_marginTop="16dp" android:contentDescription="@string/remove_source"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintBottom_toBottomOf="parent"
android:layout_marginBottom="16dp" app:layout_constraintEnd_toEndOf="parent"
android:layout_marginEnd="16dp" app:layout_constraintTop_toTopOf="parent" />
app:layout_constraintRight_toRightOf="parent"
android:layout_marginRight="16dp"
android:layout_marginStart="8dp"
app:layout_constraintLeft_toRightOf="@+id/sourceTitle"
android:layout_marginLeft="8dp"
app:layout_constraintHorizontal_bias="1.0"
android:layout_width="34dp"
android:layout_height="34dp"/>
</androidx.constraintlayout.widget.ConstraintLayout> </androidx.constraintlayout.widget.ConstraintLayout>

View File

@ -165,4 +165,5 @@
<string name="reader_static_bar_title">Static bottom bar in the article viewer</string> <string name="reader_static_bar_title">Static bottom bar in the article viewer</string>
<string name="reader_static_bar_on">The bottom bar will always be displayed</string> <string name="reader_static_bar_on">The bottom bar will always be displayed</string>
<string name="reader_static_bar_off">The bottom bar can be shown through the floating button</string> <string name="reader_static_bar_off">The bottom bar can be shown through the floating button</string>
<string name="remove_source">Remove source</string>
</resources> </resources>

View File

@ -165,4 +165,5 @@
<string name="reader_static_bar_title">Static bottom bar in the article viewer</string> <string name="reader_static_bar_title">Static bottom bar in the article viewer</string>
<string name="reader_static_bar_on">The bottom bar will always be displayed</string> <string name="reader_static_bar_on">The bottom bar will always be displayed</string>
<string name="reader_static_bar_off">The bottom bar can be shown through the floating button</string> <string name="reader_static_bar_off">The bottom bar can be shown through the floating button</string>
<string name="remove_source">Remove source</string>
</resources> </resources>

View File

@ -165,4 +165,5 @@
<string name="reader_static_bar_title">Static bottom bar in the article viewer</string> <string name="reader_static_bar_title">Static bottom bar in the article viewer</string>
<string name="reader_static_bar_on">The bottom bar will always be displayed</string> <string name="reader_static_bar_on">The bottom bar will always be displayed</string>
<string name="reader_static_bar_off">The bottom bar can be shown through the floating button</string> <string name="reader_static_bar_off">The bottom bar can be shown through the floating button</string>
<string name="remove_source">Remove source</string>
</resources> </resources>

View File

@ -165,4 +165,5 @@
<string name="reader_static_bar_title">Static bottom bar in the article viewer</string> <string name="reader_static_bar_title">Static bottom bar in the article viewer</string>
<string name="reader_static_bar_on">The bottom bar will always be displayed</string> <string name="reader_static_bar_on">The bottom bar will always be displayed</string>
<string name="reader_static_bar_off">The bottom bar can be shown through the floating button</string> <string name="reader_static_bar_off">The bottom bar can be shown through the floating button</string>
<string name="remove_source">Remove source</string>
</resources> </resources>

View File

@ -165,4 +165,5 @@
<string name="reader_static_bar_title">Barre statique pour le visionneur d\'articles</string> <string name="reader_static_bar_title">Barre statique pour le visionneur d\'articles</string>
<string name="reader_static_bar_on">La barre sera affichée</string> <string name="reader_static_bar_on">La barre sera affichée</string>
<string name="reader_static_bar_off">La barre sera affichée grâce au bouton</string> <string name="reader_static_bar_off">La barre sera affichée grâce au bouton</string>
<string name="remove_source">Remove source</string>
</resources> </resources>

View File

@ -165,4 +165,5 @@
<string name="reader_static_bar_title">Barra inferior estática na vista de artigos</string> <string name="reader_static_bar_title">Barra inferior estática na vista de artigos</string>
<string name="reader_static_bar_on">A barra inferior mostrarase sempre</string> <string name="reader_static_bar_on">A barra inferior mostrarase sempre</string>
<string name="reader_static_bar_off">A barra inferior pode mostrarse a través do botón flotante</string> <string name="reader_static_bar_off">A barra inferior pode mostrarse a través do botón flotante</string>
<string name="remove_source">Eliminar fonte</string>
</resources> </resources>

View File

@ -165,4 +165,5 @@
<string name="reader_static_bar_title">Static bottom bar in the article viewer</string> <string name="reader_static_bar_title">Static bottom bar in the article viewer</string>
<string name="reader_static_bar_on">The bottom bar will always be displayed</string> <string name="reader_static_bar_on">The bottom bar will always be displayed</string>
<string name="reader_static_bar_off">The bottom bar can be shown through the floating button</string> <string name="reader_static_bar_off">The bottom bar can be shown through the floating button</string>
<string name="remove_source">Remove source</string>
</resources> </resources>

View File

@ -165,4 +165,5 @@
<string name="reader_static_bar_title">Static bottom bar in the article viewer</string> <string name="reader_static_bar_title">Static bottom bar in the article viewer</string>
<string name="reader_static_bar_on">The bottom bar will always be displayed</string> <string name="reader_static_bar_on">The bottom bar will always be displayed</string>
<string name="reader_static_bar_off">The bottom bar can be shown through the floating button</string> <string name="reader_static_bar_off">The bottom bar can be shown through the floating button</string>
<string name="remove_source">Remove source</string>
</resources> </resources>

View File

@ -165,4 +165,5 @@
<string name="reader_static_bar_title">Static bottom bar in the article viewer</string> <string name="reader_static_bar_title">Static bottom bar in the article viewer</string>
<string name="reader_static_bar_on">The bottom bar will always be displayed</string> <string name="reader_static_bar_on">The bottom bar will always be displayed</string>
<string name="reader_static_bar_off">The bottom bar can be shown through the floating button</string> <string name="reader_static_bar_off">The bottom bar can be shown through the floating button</string>
<string name="remove_source">Remove source</string>
</resources> </resources>

View File

@ -165,4 +165,5 @@
<string name="reader_static_bar_title">Static bottom bar in the article viewer</string> <string name="reader_static_bar_title">Static bottom bar in the article viewer</string>
<string name="reader_static_bar_on">The bottom bar will always be displayed</string> <string name="reader_static_bar_on">The bottom bar will always be displayed</string>
<string name="reader_static_bar_off">The bottom bar can be shown through the floating button</string> <string name="reader_static_bar_off">The bottom bar can be shown through the floating button</string>
<string name="remove_source">Remove source</string>
</resources> </resources>

View File

@ -165,4 +165,5 @@
<string name="reader_static_bar_title">Static bottom bar in the article viewer</string> <string name="reader_static_bar_title">Static bottom bar in the article viewer</string>
<string name="reader_static_bar_on">The bottom bar will always be displayed</string> <string name="reader_static_bar_on">The bottom bar will always be displayed</string>
<string name="reader_static_bar_off">The bottom bar can be shown through the floating button</string> <string name="reader_static_bar_off">The bottom bar can be shown through the floating button</string>
<string name="remove_source">Remove source</string>
</resources> </resources>

View File

@ -165,4 +165,5 @@
<string name="reader_static_bar_title">Static bottom bar in the article viewer</string> <string name="reader_static_bar_title">Static bottom bar in the article viewer</string>
<string name="reader_static_bar_on">The bottom bar will always be displayed</string> <string name="reader_static_bar_on">The bottom bar will always be displayed</string>
<string name="reader_static_bar_off">The bottom bar can be shown through the floating button</string> <string name="reader_static_bar_off">The bottom bar can be shown through the floating button</string>
<string name="remove_source">Remove source</string>
</resources> </resources>

View File

@ -165,4 +165,5 @@
<string name="reader_static_bar_title">Static bottom bar in the article viewer</string> <string name="reader_static_bar_title">Static bottom bar in the article viewer</string>
<string name="reader_static_bar_on">The bottom bar will always be displayed</string> <string name="reader_static_bar_on">The bottom bar will always be displayed</string>
<string name="reader_static_bar_off">The bottom bar can be shown through the floating button</string> <string name="reader_static_bar_off">The bottom bar can be shown through the floating button</string>
<string name="remove_source">Remove source</string>
</resources> </resources>

View File

@ -165,4 +165,5 @@
<string name="reader_static_bar_title">Static bottom bar in the article viewer</string> <string name="reader_static_bar_title">Static bottom bar in the article viewer</string>
<string name="reader_static_bar_on">The bottom bar will always be displayed</string> <string name="reader_static_bar_on">The bottom bar will always be displayed</string>
<string name="reader_static_bar_off">The bottom bar can be shown through the floating button</string> <string name="reader_static_bar_off">The bottom bar can be shown through the floating button</string>
<string name="remove_source">Remove source</string>
</resources> </resources>

View File

@ -165,4 +165,5 @@
<string name="reader_static_bar_title">文章查看器中的静态底部栏</string> <string name="reader_static_bar_title">文章查看器中的静态底部栏</string>
<string name="reader_static_bar_on">底部栏将始终显示</string> <string name="reader_static_bar_on">底部栏将始终显示</string>
<string name="reader_static_bar_off">底部栏可以通过浮动按钮显示</string> <string name="reader_static_bar_off">底部栏可以通过浮动按钮显示</string>
<string name="remove_source">删除源</string>
</resources> </resources>

View File

@ -165,4 +165,5 @@
<string name="reader_static_bar_title">Static bottom bar in the article viewer</string> <string name="reader_static_bar_title">Static bottom bar in the article viewer</string>
<string name="reader_static_bar_on">The bottom bar will always be displayed</string> <string name="reader_static_bar_on">The bottom bar will always be displayed</string>
<string name="reader_static_bar_off">The bottom bar can be shown through the floating button</string> <string name="reader_static_bar_off">The bottom bar can be shown through the floating button</string>
<string name="remove_source">Remove source</string>
</resources> </resources>

View File

@ -5,7 +5,7 @@
<color name="colorAccent">#FFff5722</color> <color name="colorAccent">#FFff5722</color>
<color name="colorAccentDark">#FFbf360c</color> <color name="colorAccentDark">#FFbf360c</color>
<color name="pink">#FFe91e63</color> <color name="pink">#FFe91e63</color>
<color name="white">#FFFFFFFF</color> <color name="white">#FFFFFF</color>
<color name="black">#FF000000</color> <color name="black">#FF000000</color>
<color name="red">#FF0000</color> <color name="red">#FF0000</color>
<color name="refresh_progress_1">@color/colorAccentDark</color> <color name="refresh_progress_1">@color/colorAccentDark</color>
@ -20,5 +20,5 @@
<color name="cardBackgroundColor">#FFFFFFFF</color> <color name="cardBackgroundColor">#FFFFFFFF</color>
<color name="materialDrawerHeaderSelectionText">#212121</color> <color name="materialDrawerHeaderSelectionText">#212121</color>
<color name="darkBackground">#FF303030</color> <color name="darkBackground">#303030</color>
</resources> </resources>

View File

@ -168,4 +168,5 @@
<string name="reader_static_bar_title">Static bottom bar in the article viewer</string> <string name="reader_static_bar_title">Static bottom bar in the article viewer</string>
<string name="reader_static_bar_on">The bottom bar will always be displayed</string> <string name="reader_static_bar_on">The bottom bar will always be displayed</string>
<string name="reader_static_bar_off">The bottom bar can be shown through the floating button</string> <string name="reader_static_bar_off">The bottom bar can be shown through the floating button</string>
<string name="remove_source">Remove source</string>
</resources> </resources>

View File

@ -28,6 +28,7 @@
<item name="cardBackgroundColor">@color/grey_800</item> <item name="cardBackgroundColor">@color/grey_800</item>
<item name="android:colorBackground">@color/darkBackground</item> <item name="android:colorBackground">@color/darkBackground</item>
<item name="colorSurface">@color/darkBackground</item> <item name="colorSurface">@color/darkBackground</item>
<item name="alertDialogTheme">@style/AlertDialogDark</item>
<item name="bnbBackgroundColor">@color/grey_900</item> <item name="bnbBackgroundColor">@color/grey_900</item>
<item name="android:textColorPrimary">@color/white</item> <item name="android:textColorPrimary">@color/white</item>
<item name="android:textColorSecondary">@color/grey_600</item> <item name="android:textColorSecondary">@color/grey_600</item>
@ -58,4 +59,15 @@
<item name="android:tint">?android:textColorPrimary</item> <item name="android:tint">?android:textColorPrimary</item>
</style> </style>
<!-- Spinner Theme -->
<style name="App.Spinner" parent="Widget.AppCompat.Light.DropDownItem.Spinner">
<item name="android:textColor">?android:textColorPrimary</item>
</style>
<!-- Alert dialog Theme -->
<style name="AlertDialogDark" parent="Theme.MaterialComponents.Dialog">
<item name="android:background">@color/darkBackground</item>
</style>
</resources> </resources>

View File

@ -2,7 +2,7 @@
buildscript { buildscript {
ext { ext {
kotlin_version = '1.5.31' kotlin_version = '1.6.10'
android_version = '1.0.0' android_version = '1.0.0'
androidx_version = '1.1.0-alpha05' androidx_version = '1.1.0-alpha05'
lifecycle_version = '2.2.0-alpha01' lifecycle_version = '2.2.0-alpha01'
@ -15,7 +15,7 @@ buildscript {
maven { url "https://www.jitpack.io" } maven { url "https://www.jitpack.io" }
} }
dependencies { dependencies {
classpath 'com.android.tools.build:gradle:7.0.3' classpath 'com.android.tools.build:gradle:7.0.4'
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
} }
} }
@ -23,9 +23,9 @@ buildscript {
allprojects { allprojects {
repositories { repositories {
google() google()
mavenCentral()
jcenter() jcenter()
maven { url "https://www.jitpack.io" } maven { url "https://www.jitpack.io" }
mavenCentral()
} }
} }