Compare commits
	
		
			39 Commits
		
	
	
		
			v171811325
			...
			v171901010
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 
						 | 
					65821492ad | ||
| 
						 | 
					6ede718a9f | ||
| 
						 | 
					f1757937a4 | ||
| 2bd2e0a953 | |||
| 
						 | 
					b5aef28af0 | ||
| 
						 | 
					45747a1506 | ||
| 
						 | 
					c6e2e08bcb | ||
| 
						 | 
					25bf18661e | ||
| 
						 | 
					6b088dcd24 | ||
| 
						 | 
					d2b18e1880 | ||
| 
						 | 
					eec7c94e98 | ||
| 
						 | 
					d1f8fcacc0 | ||
| 
						 | 
					07e4a33cbd | ||
| 
						 | 
					f6317f566e | ||
| 
						 | 
					9f51e4e6a5 | ||
| 
						 | 
					750604a31f | ||
| 
						 | 
					392eee0ad4 | ||
| 
						 | 
					37e7b987ee | ||
| 
						 | 
					9eac51e729 | ||
| 
						 | 
					fa9cce6783 | ||
| 
						 | 
					f0d4b63a97 | ||
| 
						 | 
					83eeb11388 | ||
| 
						 | 
					01f746f33d | ||
| 
						 | 
					200851894b | ||
| 
						 | 
					862e5cf4ab | ||
| 
						 | 
					0b07f2a407 | ||
| 
						 | 
					9ba6feef0b | ||
| 
						 | 
					63a0638522 | ||
| 
						 | 
					f9a4e6e363 | ||
| 
						 | 
					6b40fd4bdc | ||
| 
						 | 
					04c7776466 | ||
| 
						 | 
					92c335b4e1 | ||
| 
						 | 
					17251e576b | ||
| 
						 | 
					62ea782429 | ||
| 
						 | 
					f99474e3c1 | ||
| 
						 | 
					57ac8f428f | ||
| 
						 | 
					9cc1adbf15 | ||
| 
						 | 
					1d9a440ae7 | ||
| 
						 | 
					511553806c | 
							
								
								
									
										6
									
								
								.github/CONTRIBUTING.md
									
									
									
									
										vendored
									
									
								
							
							
						
						@@ -41,6 +41,12 @@ Always check if the web version of your instance is working.
 | 
			
		||||
* Remember that PR review can take time.
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
# Install Selfoss (if you don't have an instance)
 | 
			
		||||
 | 
			
		||||
I won't provide any selfoss instance url. If you want to help, but to not have one, you'll have to install one, and use it.
 | 
			
		||||
 | 
			
		||||
All the details to need are [here](https://selfoss.aditu.de/).
 | 
			
		||||
 | 
			
		||||
# Build the project
 | 
			
		||||
 | 
			
		||||
You can directly import this project into IntellIJ/Android Studio.
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +1,13 @@
 | 
			
		||||
**1.7.x**
 | 
			
		||||
 | 
			
		||||
- Hiding tags with 0 articles
 | 
			
		||||
 | 
			
		||||
- Fixed issue with basic auth and images loading
 | 
			
		||||
 | 
			
		||||
- Added the ability to justify or left align the reader text
 | 
			
		||||
 | 
			
		||||
- Fixed #251
 | 
			
		||||
 | 
			
		||||
- Added experimental issue to set a default timeout. Should work for #238.
 | 
			
		||||
 | 
			
		||||
- Closing #220.
 | 
			
		||||
 
 | 
			
		||||
@@ -18,7 +18,11 @@ Also, the last APK built from source is available [here](https://jenkins.amine-b
 | 
			
		||||
 | 
			
		||||
## Want to help ?
 | 
			
		||||
 | 
			
		||||
Check the [Contribution guide](https://github.com/aminecmi/ReaderforSelfoss/blob/master/.github/CONTRIBUTING.md)
 | 
			
		||||
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
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -2,7 +2,15 @@ buildscript {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
def gitVersion() {
 | 
			
		||||
    def process = "git for-each-ref refs/tags --sort=-taggerdate --format='%(refname:short)' --count=1".execute()
 | 
			
		||||
    def process
 | 
			
		||||
    def maybeTagOfCurrentCommit = 'git describe --contains HEAD'.execute()
 | 
			
		||||
    if (maybeTagOfCurrentCommit.text.isEmpty()) {
 | 
			
		||||
        println "No tag on current commit. Will take the latest one."
 | 
			
		||||
        process = "git for-each-ref refs/tags --sort=-authordate --format='%(refname:short)' --count=1".execute()
 | 
			
		||||
    } else {
 | 
			
		||||
        println "Tag found on current commit"
 | 
			
		||||
        process = 'git describe --contains HEAD'.execute()
 | 
			
		||||
    }
 | 
			
		||||
    return process.text.replaceAll("'", "").substring(1).replaceAll("\\.", "").trim()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -59,7 +67,7 @@ android {
 | 
			
		||||
    buildTypes {
 | 
			
		||||
        release {
 | 
			
		||||
            minifyEnabled true
 | 
			
		||||
            shrinkResources true
 | 
			
		||||
            shrinkResources false
 | 
			
		||||
            proguardFiles getDefaultProguardFile('proguard-android.txt'),
 | 
			
		||||
                    'proguard-rules.pro'
 | 
			
		||||
        }
 | 
			
		||||
@@ -170,4 +178,4 @@ def initAppLoginPropertiesIfNeeded() {
 | 
			
		||||
            entry(key: "appLoginPassword", value: System.getProperty("appLoginPassword"))
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -1,7 +1,6 @@
 | 
			
		||||
<?xml version="1.0" encoding="utf-8"?>
 | 
			
		||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
 | 
			
		||||
    package="apps.amine.bou.readerforselfoss"
 | 
			
		||||
    xmlns:tools="http://schemas.android.com/tools">
 | 
			
		||||
    package="apps.amine.bou.readerforselfoss">
 | 
			
		||||
 | 
			
		||||
    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
 | 
			
		||||
    <uses-permission android:name="android.permission.INTERNET" />
 | 
			
		||||
@@ -19,11 +18,11 @@
 | 
			
		||||
            android:theme="@style/SplashTheme">
 | 
			
		||||
            <intent-filter>
 | 
			
		||||
                <action android:name="android.intent.action.MAIN" />
 | 
			
		||||
 | 
			
		||||
                <category android:name="android.intent.category.LAUNCHER" />
 | 
			
		||||
            </intent-filter>
 | 
			
		||||
 | 
			
		||||
            <meta-data android:name="android.app.shortcuts"
 | 
			
		||||
            <meta-data
 | 
			
		||||
                android:name="android.app.shortcuts"
 | 
			
		||||
                android:resource="@xml/shortcuts" />
 | 
			
		||||
        </activity>
 | 
			
		||||
        <activity
 | 
			
		||||
@@ -38,7 +37,7 @@
 | 
			
		||||
            android:parentActivityName=".HomeActivity">
 | 
			
		||||
            <meta-data
 | 
			
		||||
                android:name="android.support.PARENT_ACTIVITY"
 | 
			
		||||
                android:value="apps.amine.bou.readerforselfoss.HomeActivity" />
 | 
			
		||||
                android:value=".HomeActivity" />
 | 
			
		||||
        </activity>
 | 
			
		||||
        <activity
 | 
			
		||||
            android:name=".SourcesActivity"
 | 
			
		||||
@@ -56,9 +55,7 @@
 | 
			
		||||
 | 
			
		||||
            <intent-filter>
 | 
			
		||||
                <action android:name="android.intent.action.SEND" />
 | 
			
		||||
 | 
			
		||||
                <category android:name="android.intent.category.DEFAULT" />
 | 
			
		||||
 | 
			
		||||
                <data android:mimeType="text/plain" />
 | 
			
		||||
            </intent-filter>
 | 
			
		||||
        </activity>
 | 
			
		||||
@@ -77,6 +74,9 @@
 | 
			
		||||
            android:value="true" />
 | 
			
		||||
 | 
			
		||||
        <meta-data android:name="android.max_aspect" android:value="2.1" />
 | 
			
		||||
        <meta-data
 | 
			
		||||
            android:name="preloaded_fonts"
 | 
			
		||||
            android:resource="@array/preloaded_fonts" />
 | 
			
		||||
    </application>
 | 
			
		||||
 | 
			
		||||
</manifest>
 | 
			
		||||
 
 | 
			
		||||
@@ -109,7 +109,7 @@ class AddSourceActivity : AppCompatActivity() {
 | 
			
		||||
        super.onResume()
 | 
			
		||||
        val config = Config(this)
 | 
			
		||||
 | 
			
		||||
        if (config.baseUrl.isEmpty() || !config.baseUrl.isBaseUrlValid(false)) {
 | 
			
		||||
        if (config.baseUrl.isEmpty() || !config.baseUrl.isBaseUrlValid(false, this@AddSourceActivity)) {
 | 
			
		||||
            mustLoginToAddSource()
 | 
			
		||||
        } else {
 | 
			
		||||
            handleSpoutsSpinner(spoutsSpinner, api, progress, formContainer)
 | 
			
		||||
 
 | 
			
		||||
@@ -18,17 +18,13 @@ import androidx.recyclerview.widget.RecyclerView
 | 
			
		||||
import androidx.appcompat.widget.SearchView
 | 
			
		||||
import androidx.recyclerview.widget.StaggeredGridLayoutManager
 | 
			
		||||
import androidx.recyclerview.widget.ItemTouchHelper
 | 
			
		||||
import android.util.Log
 | 
			
		||||
import android.view.Menu
 | 
			
		||||
import android.view.MenuItem
 | 
			
		||||
import android.view.View
 | 
			
		||||
import android.widget.Toast
 | 
			
		||||
import androidx.room.Room
 | 
			
		||||
import androidx.room.RoomDatabase
 | 
			
		||||
import androidx.work.Constraints
 | 
			
		||||
import androidx.work.ExistingPeriodicWorkPolicy
 | 
			
		||||
import androidx.work.NetworkType
 | 
			
		||||
import androidx.work.OneTimeWorkRequestBuilder
 | 
			
		||||
import androidx.work.PeriodicWorkRequestBuilder
 | 
			
		||||
import androidx.work.WorkManager
 | 
			
		||||
import apps.amine.bou.readerforselfoss.adapters.ItemCardAdapter
 | 
			
		||||
@@ -69,7 +65,6 @@ import com.ashokvarma.bottomnavigation.BottomNavigationItem
 | 
			
		||||
import com.ashokvarma.bottomnavigation.TextBadgeItem
 | 
			
		||||
import com.ftinc.scoop.Scoop
 | 
			
		||||
import com.github.stkent.amplify.tracking.Amplify
 | 
			
		||||
import com.google.gson.reflect.TypeToken
 | 
			
		||||
import com.mikepenz.aboutlibraries.Libs
 | 
			
		||||
import com.mikepenz.aboutlibraries.LibsBuilder
 | 
			
		||||
import com.mikepenz.materialdrawer.Drawer
 | 
			
		||||
@@ -79,7 +74,6 @@ import com.mikepenz.materialdrawer.model.DividerDrawerItem
 | 
			
		||||
import com.mikepenz.materialdrawer.model.PrimaryDrawerItem
 | 
			
		||||
import com.mikepenz.materialdrawer.model.SecondaryDrawerItem
 | 
			
		||||
import kotlinx.android.synthetic.main.activity_home.*
 | 
			
		||||
import kotlinx.android.synthetic.main.fragment_article.*
 | 
			
		||||
import org.acra.ACRA
 | 
			
		||||
import retrofit2.Call
 | 
			
		||||
import retrofit2.Callback
 | 
			
		||||
@@ -152,6 +146,8 @@ class HomeActivity : AppCompatActivity(), SearchView.OnQueryTextListener {
 | 
			
		||||
 | 
			
		||||
    private lateinit var db: AppDatabase
 | 
			
		||||
 | 
			
		||||
    private lateinit var config: Config
 | 
			
		||||
 | 
			
		||||
    data class DrawerData(val tags: List<Tag>?, val sources: List<Source>?)
 | 
			
		||||
 | 
			
		||||
    override fun onStart() {
 | 
			
		||||
@@ -161,6 +157,7 @@ class HomeActivity : AppCompatActivity(), SearchView.OnQueryTextListener {
 | 
			
		||||
 | 
			
		||||
    override fun onCreate(savedInstanceState: Bundle?) {
 | 
			
		||||
        appColors = AppColors(this@HomeActivity)
 | 
			
		||||
        config = Config(this@HomeActivity)
 | 
			
		||||
 | 
			
		||||
        super.onCreate(savedInstanceState)
 | 
			
		||||
 | 
			
		||||
@@ -230,7 +227,7 @@ class HomeActivity : AppCompatActivity(), SearchView.OnQueryTextListener {
 | 
			
		||||
                    recyclerView: RecyclerView,
 | 
			
		||||
                    viewHolder: RecyclerView.ViewHolder
 | 
			
		||||
                ): Int =
 | 
			
		||||
                    if (elementsShown != UNREAD_SHOWN) {
 | 
			
		||||
                    if (elementsShown != UNREAD_SHOWN && elementsShown != READ_SHOWN) {
 | 
			
		||||
                        0
 | 
			
		||||
                    } else {
 | 
			
		||||
                        super.getSwipeDirs(
 | 
			
		||||
@@ -253,8 +250,8 @@ class HomeActivity : AppCompatActivity(), SearchView.OnQueryTextListener {
 | 
			
		||||
                        val adapter = recyclerView.adapter
 | 
			
		||||
 | 
			
		||||
                        when (adapter) {
 | 
			
		||||
                            is ItemCardAdapter -> adapter.removeItemAtIndex(position)
 | 
			
		||||
                            is ItemListAdapter -> adapter.removeItemAtIndex(position)
 | 
			
		||||
                            is ItemCardAdapter -> adapter.handleItemAtIndex(position)
 | 
			
		||||
                            is ItemListAdapter -> adapter.handleItemAtIndex(position)
 | 
			
		||||
                        }
 | 
			
		||||
 | 
			
		||||
                        badgeNew--
 | 
			
		||||
@@ -304,19 +301,19 @@ class HomeActivity : AppCompatActivity(), SearchView.OnQueryTextListener {
 | 
			
		||||
 | 
			
		||||
        val tabNew =
 | 
			
		||||
            BottomNavigationItem(
 | 
			
		||||
                R.drawable.ic_fiber_new_black_24dp,
 | 
			
		||||
                R.drawable.ic_tab_fiber_new_black_24dp,
 | 
			
		||||
                getString(R.string.tab_new)
 | 
			
		||||
            ).setActiveColor(appColors.colorAccent)
 | 
			
		||||
                .setBadgeItem(tabNewBadge)
 | 
			
		||||
        val tabArchive =
 | 
			
		||||
            BottomNavigationItem(
 | 
			
		||||
                R.drawable.ic_archive_black_24dp,
 | 
			
		||||
                R.drawable.ic_tab_archive_black_24dp,
 | 
			
		||||
                getString(R.string.tab_read)
 | 
			
		||||
            ).setActiveColor(appColors.colorAccentDark)
 | 
			
		||||
                .setBadgeItem(tabArchiveBadge)
 | 
			
		||||
        val tabStarred =
 | 
			
		||||
            BottomNavigationItem(
 | 
			
		||||
                R.drawable.ic_favorite_black_24dp,
 | 
			
		||||
                R.drawable.ic_tab_favorite_black_24dp,
 | 
			
		||||
                getString(R.string.tab_favs)
 | 
			
		||||
            ).setActiveColorResource(R.color.pink)
 | 
			
		||||
                .setBadgeItem(tabStarredBadge)
 | 
			
		||||
@@ -478,7 +475,7 @@ class HomeActivity : AppCompatActivity(), SearchView.OnQueryTextListener {
 | 
			
		||||
 | 
			
		||||
            footer {
 | 
			
		||||
                primaryItem(R.string.drawer_report_bug) {
 | 
			
		||||
                    icon = R.drawable.ic_bug_report
 | 
			
		||||
                    icon = R.drawable.ic_bug_report_black_24dp
 | 
			
		||||
                    iconTintingEnabled = true
 | 
			
		||||
                    onClick { _ ->
 | 
			
		||||
                        val browserIntent = Intent(Intent.ACTION_VIEW, Uri.parse(Config.trackerUrl))
 | 
			
		||||
@@ -488,7 +485,7 @@ class HomeActivity : AppCompatActivity(), SearchView.OnQueryTextListener {
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                primaryItem(R.string.title_activity_settings) {
 | 
			
		||||
                    icon = R.drawable.ic_settings
 | 
			
		||||
                    icon = R.drawable.ic_settings_black_24dp
 | 
			
		||||
                    iconTintingEnabled = true
 | 
			
		||||
                    onClick { _ ->
 | 
			
		||||
                        startActivityForResult(
 | 
			
		||||
@@ -531,12 +528,11 @@ class HomeActivity : AppCompatActivity(), SearchView.OnQueryTextListener {
 | 
			
		||||
                        gd.shape = GradientDrawable.RECTANGLE
 | 
			
		||||
                        gd.setSize(30, 30)
 | 
			
		||||
                        gd.cornerRadius = 30F
 | 
			
		||||
                        drawer.addItem(
 | 
			
		||||
                        var drawerItem =
 | 
			
		||||
                            PrimaryDrawerItem()
 | 
			
		||||
                                .withName(it.tag)
 | 
			
		||||
                                .withIdentifier(it.tag.longHash())
 | 
			
		||||
                                .withIcon(gd)
 | 
			
		||||
                                .withBadge("${it.unread}")
 | 
			
		||||
                                .withBadgeStyle(
 | 
			
		||||
                                    BadgeStyle().withTextColor(Color.WHITE)
 | 
			
		||||
                                        .withColor(appColors.colorAccent)
 | 
			
		||||
@@ -547,6 +543,11 @@ class HomeActivity : AppCompatActivity(), SearchView.OnQueryTextListener {
 | 
			
		||||
                                    getElementsAccordingToTab()
 | 
			
		||||
                                    false
 | 
			
		||||
                                }
 | 
			
		||||
                        if (it.unread > 0) {
 | 
			
		||||
                            drawerItem = drawerItem.withBadge("${it.unread}")
 | 
			
		||||
                        }
 | 
			
		||||
                        drawer.addItem(
 | 
			
		||||
                            drawerItem
 | 
			
		||||
                        )
 | 
			
		||||
 | 
			
		||||
                        (it.tag.longHash() to it.unread)
 | 
			
		||||
@@ -578,12 +579,11 @@ class HomeActivity : AppCompatActivity(), SearchView.OnQueryTextListener {
 | 
			
		||||
                        gd.shape = GradientDrawable.RECTANGLE
 | 
			
		||||
                        gd.setSize(30, 30)
 | 
			
		||||
                        gd.cornerRadius = 30F
 | 
			
		||||
                        drawer.addItem(
 | 
			
		||||
                        var drawerItem =
 | 
			
		||||
                            PrimaryDrawerItem()
 | 
			
		||||
                                .withName(it.tag)
 | 
			
		||||
                                .withIdentifier(it.tag.longHash())
 | 
			
		||||
                                .withIcon(gd)
 | 
			
		||||
                                .withBadge("${it.unread}")
 | 
			
		||||
                                .withBadgeStyle(
 | 
			
		||||
                                    BadgeStyle().withTextColor(Color.WHITE)
 | 
			
		||||
                                        .withColor(appColors.colorAccent)
 | 
			
		||||
@@ -594,6 +594,11 @@ class HomeActivity : AppCompatActivity(), SearchView.OnQueryTextListener {
 | 
			
		||||
                                    getElementsAccordingToTab()
 | 
			
		||||
                                    false
 | 
			
		||||
                                }
 | 
			
		||||
                        if (it.unread > 0) {
 | 
			
		||||
                            drawerItem = drawerItem.withBadge("${it.unread}")
 | 
			
		||||
                        }
 | 
			
		||||
                        drawer.addItem(
 | 
			
		||||
                            drawerItem
 | 
			
		||||
                        )
 | 
			
		||||
 | 
			
		||||
                        (it.tag.longHash() to it.unread)
 | 
			
		||||
@@ -680,7 +685,7 @@ class HomeActivity : AppCompatActivity(), SearchView.OnQueryTextListener {
 | 
			
		||||
                    PrimaryDrawerItem()
 | 
			
		||||
                        .withName(R.string.action_about)
 | 
			
		||||
                        .withSelectable(false)
 | 
			
		||||
                        .withIcon(R.drawable.ic_info_outline)
 | 
			
		||||
                        .withIcon(R.drawable.ic_info_outline_white_24dp)
 | 
			
		||||
                        .withIconTintingEnabled(true)
 | 
			
		||||
                        .withOnDrawerItemClickListener { _, _, _ ->
 | 
			
		||||
                            LibsBuilder()
 | 
			
		||||
@@ -1136,7 +1141,8 @@ class HomeActivity : AppCompatActivity(), SearchView.OnQueryTextListener {
 | 
			
		||||
                            fullHeightCards,
 | 
			
		||||
                            appColors,
 | 
			
		||||
                            debugReadingItems,
 | 
			
		||||
                            userIdentifier
 | 
			
		||||
                            userIdentifier,
 | 
			
		||||
                            config
 | 
			
		||||
                        ) {
 | 
			
		||||
                            updateItems(it)
 | 
			
		||||
                        }
 | 
			
		||||
@@ -1152,7 +1158,8 @@ class HomeActivity : AppCompatActivity(), SearchView.OnQueryTextListener {
 | 
			
		||||
                            articleViewer,
 | 
			
		||||
                            debugReadingItems,
 | 
			
		||||
                            userIdentifier,
 | 
			
		||||
                            appColors
 | 
			
		||||
                            appColors,
 | 
			
		||||
                            config
 | 
			
		||||
                        ) {
 | 
			
		||||
                            updateItems(it)
 | 
			
		||||
                        }
 | 
			
		||||
 
 | 
			
		||||
@@ -54,7 +54,6 @@ class LoginActivity : AppCompatActivity() {
 | 
			
		||||
 | 
			
		||||
        handleBaseUrlFail()
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
        settings = getSharedPreferences(Config.settingsName, Context.MODE_PRIVATE)
 | 
			
		||||
        userIdentifier = settings.getString("unique_id", "")
 | 
			
		||||
        logErrors = settings.getBoolean("login_debug", false)
 | 
			
		||||
@@ -145,7 +144,7 @@ class LoginActivity : AppCompatActivity() {
 | 
			
		||||
        var cancel = false
 | 
			
		||||
        var focusView: View? = null
 | 
			
		||||
 | 
			
		||||
        if (!url.isBaseUrlValid(logErrors)) {
 | 
			
		||||
        if (!url.isBaseUrlValid(logErrors, this@LoginActivity)) {
 | 
			
		||||
            urlView.error = getString(R.string.login_url_problem)
 | 
			
		||||
            focusView = urlView
 | 
			
		||||
            cancel = true
 | 
			
		||||
@@ -164,7 +163,7 @@ class LoginActivity : AppCompatActivity() {
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (isWithLogin || isWithHTTPLogin) {
 | 
			
		||||
        if (isWithLogin) {
 | 
			
		||||
            if (TextUtils.isEmpty(password)) {
 | 
			
		||||
                passwordView.error = getString(R.string.error_invalid_password)
 | 
			
		||||
                focusView = passwordView
 | 
			
		||||
@@ -178,6 +177,20 @@ class LoginActivity : AppCompatActivity() {
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (isWithHTTPLogin) {
 | 
			
		||||
            if (TextUtils.isEmpty(httpPassword)) {
 | 
			
		||||
                httpPasswordView.error = getString(R.string.error_invalid_password)
 | 
			
		||||
                focusView = httpPasswordView
 | 
			
		||||
                cancel = true
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            if (TextUtils.isEmpty(httpLogin)) {
 | 
			
		||||
                httpLoginView.error = getString(R.string.error_field_required)
 | 
			
		||||
                focusView = httpLoginView
 | 
			
		||||
                cancel = true
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (cancel) {
 | 
			
		||||
            focusView?.requestFocus()
 | 
			
		||||
        } else {
 | 
			
		||||
 
 | 
			
		||||
@@ -10,6 +10,7 @@ import android.preference.PreferenceManager
 | 
			
		||||
import androidx.multidex.MultiDexApplication
 | 
			
		||||
import android.widget.ImageView
 | 
			
		||||
import apps.amine.bou.readerforselfoss.utils.Config
 | 
			
		||||
import apps.amine.bou.readerforselfoss.utils.glide.loadMaybeBasicAuth
 | 
			
		||||
import com.bumptech.glide.Glide
 | 
			
		||||
import com.bumptech.glide.request.RequestOptions
 | 
			
		||||
import com.ftinc.scoop.Scoop
 | 
			
		||||
@@ -28,10 +29,8 @@ import java.io.IOException
 | 
			
		||||
import java.util.UUID.randomUUID
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@AcraHttpSender(uri = "http://amine-bou.fr:5984/acra-selfoss/_design/acra-storage/_update/report",
 | 
			
		||||
                basicAuthLogin = "selfoss",
 | 
			
		||||
                basicAuthPassword = "selfoss",
 | 
			
		||||
                httpMethod = HttpSender.Method.PUT)
 | 
			
		||||
@AcraHttpSender(uri = "http://37.187.110.167/amine/acra/simplest-acra.php",
 | 
			
		||||
                httpMethod = HttpSender.Method.POST)
 | 
			
		||||
@AcraDialog(resText = R.string.crash_dialog_text,
 | 
			
		||||
            resCommentPrompt = R.string.crash_dialog_comment,
 | 
			
		||||
            resTheme = android.R.style.Theme_DeviceDefault_Dialog)
 | 
			
		||||
@@ -44,10 +43,11 @@ import java.util.UUID.randomUUID
 | 
			
		||||
    ReportField.USER_APP_START_DATE, ReportField.USER_COMMENT, ReportField.USER_CRASH_DATE, ReportField.USER_EMAIL, ReportField.CUSTOM_DATA],
 | 
			
		||||
          buildConfigClass = BuildConfig::class)
 | 
			
		||||
class MyApp : MultiDexApplication() {
 | 
			
		||||
    private lateinit var config: Config
 | 
			
		||||
 | 
			
		||||
    override fun onCreate() {
 | 
			
		||||
        super.onCreate()
 | 
			
		||||
 | 
			
		||||
        config = Config(baseContext)
 | 
			
		||||
        initAmplify()
 | 
			
		||||
 | 
			
		||||
        val prefs = getSharedPreferences(Config.settingsName, Context.MODE_PRIVATE)
 | 
			
		||||
@@ -107,7 +107,7 @@ class MyApp : MultiDexApplication() {
 | 
			
		||||
                tag: String?
 | 
			
		||||
            ) {
 | 
			
		||||
                Glide.with(imageView?.context)
 | 
			
		||||
                    .load(uri)
 | 
			
		||||
                    .loadMaybeBasicAuth(config, uri.toString())
 | 
			
		||||
                    .apply(RequestOptions.fitCenterTransform().placeholder(placeholder))
 | 
			
		||||
                    .into(imageView)
 | 
			
		||||
            }
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +1,6 @@
 | 
			
		||||
package apps.amine.bou.readerforselfoss
 | 
			
		||||
 | 
			
		||||
import android.content.SharedPreferences
 | 
			
		||||
import android.graphics.drawable.ColorDrawable
 | 
			
		||||
import android.os.Build
 | 
			
		||||
import android.os.Bundle
 | 
			
		||||
@@ -13,6 +14,7 @@ import android.view.Menu
 | 
			
		||||
import android.view.MenuItem
 | 
			
		||||
import android.view.ViewGroup
 | 
			
		||||
import android.widget.Toast
 | 
			
		||||
import androidx.fragment.app.Fragment
 | 
			
		||||
import androidx.room.Room
 | 
			
		||||
import apps.amine.bou.readerforselfoss.api.selfoss.Item
 | 
			
		||||
import apps.amine.bou.readerforselfoss.api.selfoss.SelfossApi
 | 
			
		||||
@@ -51,6 +53,11 @@ class ReaderActivity : AppCompatActivity() {
 | 
			
		||||
    private lateinit var toolbarMenu: Menu
 | 
			
		||||
 | 
			
		||||
    private lateinit var db: AppDatabase
 | 
			
		||||
    private lateinit var prefs: SharedPreferences
 | 
			
		||||
 | 
			
		||||
    private var activeAlignment: Int = 1
 | 
			
		||||
    val JUSTIFY = 1
 | 
			
		||||
    val ALIGN_LEFT = 2
 | 
			
		||||
 | 
			
		||||
    private fun showMenuItem(willAddToFavorite: Boolean) {
 | 
			
		||||
        toolbarMenu.findItem(R.id.save).isVisible = willAddToFavorite
 | 
			
		||||
@@ -65,6 +72,8 @@ class ReaderActivity : AppCompatActivity() {
 | 
			
		||||
        showMenuItem(false)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private lateinit var editor: SharedPreferences.Editor
 | 
			
		||||
 | 
			
		||||
    override fun onCreate(savedInstanceState: Bundle?) {
 | 
			
		||||
        super.onCreate(savedInstanceState)
 | 
			
		||||
 | 
			
		||||
@@ -85,11 +94,13 @@ class ReaderActivity : AppCompatActivity() {
 | 
			
		||||
        supportActionBar?.setDisplayHomeAsUpEnabled(true)
 | 
			
		||||
        supportActionBar?.setDisplayShowHomeEnabled(true)
 | 
			
		||||
 | 
			
		||||
        val prefs = PreferenceManager.getDefaultSharedPreferences(this)
 | 
			
		||||
        prefs = PreferenceManager.getDefaultSharedPreferences(this)
 | 
			
		||||
        editor = prefs.edit()
 | 
			
		||||
 | 
			
		||||
        debugReadingItems = prefs.getBoolean("read_debug", false)
 | 
			
		||||
        userIdentifier = prefs.getString("unique_id", "")
 | 
			
		||||
        markOnScroll = prefs.getBoolean("mark_on_scroll", false)
 | 
			
		||||
        activeAlignment = prefs.getInt("text_align", JUSTIFY)
 | 
			
		||||
 | 
			
		||||
        api = SelfossApi(
 | 
			
		||||
            this,
 | 
			
		||||
@@ -223,6 +234,11 @@ class ReaderActivity : AppCompatActivity() {
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fun alignmentMenu(showJustify: Boolean) {
 | 
			
		||||
        toolbarMenu.findItem(R.id.align_left).isVisible = !showJustify
 | 
			
		||||
        toolbarMenu.findItem(R.id.align_justify).isVisible = showJustify
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    override fun onCreateOptionsMenu(menu: Menu): Boolean {
 | 
			
		||||
        val inflater = menuInflater
 | 
			
		||||
        inflater.inflate(R.menu.reader_menu, menu)
 | 
			
		||||
@@ -233,6 +249,11 @@ class ReaderActivity : AppCompatActivity() {
 | 
			
		||||
        } else {
 | 
			
		||||
            canFavorite()
 | 
			
		||||
        }
 | 
			
		||||
        if (activeAlignment == JUSTIFY) {
 | 
			
		||||
            alignmentMenu(false)
 | 
			
		||||
        } else {
 | 
			
		||||
            alignmentMenu(true)
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return true
 | 
			
		||||
    }
 | 
			
		||||
@@ -314,10 +335,29 @@ class ReaderActivity : AppCompatActivity() {
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            R.id.align_left -> {
 | 
			
		||||
                editor.putInt("text_align", ALIGN_LEFT)
 | 
			
		||||
                editor.apply()
 | 
			
		||||
                alignmentMenu(true)
 | 
			
		||||
                refreshFragment()
 | 
			
		||||
            }
 | 
			
		||||
            R.id.align_justify -> {
 | 
			
		||||
                editor.putInt("text_align", JUSTIFY)
 | 
			
		||||
                editor.apply()
 | 
			
		||||
                alignmentMenu(false)
 | 
			
		||||
                refreshFragment()
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        return super.onOptionsItemSelected(item)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private fun refreshFragment() {
 | 
			
		||||
        finish()
 | 
			
		||||
        overridePendingTransition(0, 0)
 | 
			
		||||
        startActivity(intent)
 | 
			
		||||
        overridePendingTransition(0, 0)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    companion object {
 | 
			
		||||
        var allItems: ArrayList<Item> = ArrayList()
 | 
			
		||||
    }
 | 
			
		||||
 
 | 
			
		||||
@@ -17,6 +17,7 @@ import apps.amine.bou.readerforselfoss.api.selfoss.SuccessResponse
 | 
			
		||||
import apps.amine.bou.readerforselfoss.persistence.database.AppDatabase
 | 
			
		||||
import apps.amine.bou.readerforselfoss.persistence.entities.ActionEntity
 | 
			
		||||
import apps.amine.bou.readerforselfoss.themes.AppColors
 | 
			
		||||
import apps.amine.bou.readerforselfoss.utils.Config
 | 
			
		||||
import apps.amine.bou.readerforselfoss.utils.LinkOnTouchListener
 | 
			
		||||
import apps.amine.bou.readerforselfoss.utils.buildCustomTabsIntent
 | 
			
		||||
import apps.amine.bou.readerforselfoss.utils.customtabs.CustomTabActivityHelper
 | 
			
		||||
@@ -51,6 +52,7 @@ class ItemCardAdapter(
 | 
			
		||||
    override val appColors: AppColors,
 | 
			
		||||
    override val debugReadingItems: Boolean,
 | 
			
		||||
    override val userIdentifier: String,
 | 
			
		||||
    override val config: Config,
 | 
			
		||||
    override val updateItems: (ArrayList<Item>) -> Unit
 | 
			
		||||
) : ItemsAdapter<ItemCardAdapter.ViewHolder>() {
 | 
			
		||||
    private val c: Context = app.baseContext
 | 
			
		||||
@@ -86,7 +88,7 @@ class ItemCardAdapter(
 | 
			
		||||
            holder.mView.itemImage.setImageDrawable(null)
 | 
			
		||||
        } else {
 | 
			
		||||
            holder.mView.itemImage.visibility = View.VISIBLE
 | 
			
		||||
            c.bitmapCenterCrop(itm.getThumbnail(c), holder.mView.itemImage)
 | 
			
		||||
            c.bitmapCenterCrop(config, itm.getThumbnail(c), holder.mView.itemImage)
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (itm.getIcon(c).isEmpty()) {
 | 
			
		||||
@@ -99,7 +101,7 @@ class ItemCardAdapter(
 | 
			
		||||
                    .build(itm.sourcetitle.toTextDrawableString(c), color)
 | 
			
		||||
            holder.mView.sourceImage.setImageDrawable(drawable)
 | 
			
		||||
        } else {
 | 
			
		||||
            c.circularBitmapDrawable(itm.getIcon(c), holder.mView.sourceImage)
 | 
			
		||||
            c.circularBitmapDrawable(config, itm.getIcon(c), holder.mView.sourceImage)
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        holder.mView.favButton.isLiked = itm.starred
 | 
			
		||||
 
 | 
			
		||||
@@ -20,6 +20,7 @@ import apps.amine.bou.readerforselfoss.api.selfoss.SelfossApi
 | 
			
		||||
import apps.amine.bou.readerforselfoss.api.selfoss.SuccessResponse
 | 
			
		||||
import apps.amine.bou.readerforselfoss.persistence.database.AppDatabase
 | 
			
		||||
import apps.amine.bou.readerforselfoss.themes.AppColors
 | 
			
		||||
import apps.amine.bou.readerforselfoss.utils.Config
 | 
			
		||||
import apps.amine.bou.readerforselfoss.utils.LinkOnTouchListener
 | 
			
		||||
import apps.amine.bou.readerforselfoss.utils.buildCustomTabsIntent
 | 
			
		||||
import apps.amine.bou.readerforselfoss.utils.customtabs.CustomTabActivityHelper
 | 
			
		||||
@@ -52,6 +53,7 @@ class ItemListAdapter(
 | 
			
		||||
    override val debugReadingItems: Boolean,
 | 
			
		||||
    override val userIdentifier: String,
 | 
			
		||||
    override val appColors: AppColors,
 | 
			
		||||
    override val config: Config,
 | 
			
		||||
    override val updateItems: (ArrayList<Item>) -> Unit
 | 
			
		||||
) : ItemsAdapter<ItemListAdapter.ViewHolder>() {
 | 
			
		||||
    private val generator: ColorGenerator = ColorGenerator.MATERIAL
 | 
			
		||||
@@ -108,10 +110,10 @@ class ItemListAdapter(
 | 
			
		||||
 | 
			
		||||
                holder.mView.itemImage.setImageDrawable(drawable)
 | 
			
		||||
            } else {
 | 
			
		||||
                c.circularBitmapDrawable(itm.getIcon(c), holder.mView.itemImage)
 | 
			
		||||
                c.circularBitmapDrawable(config, itm.getIcon(c), holder.mView.itemImage)
 | 
			
		||||
            }
 | 
			
		||||
        } else {
 | 
			
		||||
            c.bitmapCenterCrop(itm.getThumbnail(c), holder.mView.itemImage)
 | 
			
		||||
            c.bitmapCenterCrop(config, itm.getThumbnail(c), holder.mView.itemImage)
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -13,6 +13,7 @@ import apps.amine.bou.readerforselfoss.api.selfoss.SuccessResponse
 | 
			
		||||
import apps.amine.bou.readerforselfoss.persistence.database.AppDatabase
 | 
			
		||||
import apps.amine.bou.readerforselfoss.persistence.entities.ActionEntity
 | 
			
		||||
import apps.amine.bou.readerforselfoss.themes.AppColors
 | 
			
		||||
import apps.amine.bou.readerforselfoss.utils.Config
 | 
			
		||||
import apps.amine.bou.readerforselfoss.utils.maybeHandleSilentException
 | 
			
		||||
import apps.amine.bou.readerforselfoss.utils.network.isNetworkAccessible
 | 
			
		||||
import apps.amine.bou.readerforselfoss.utils.persistence.toEntity
 | 
			
		||||
@@ -31,6 +32,7 @@ abstract class ItemsAdapter<VH : RecyclerView.ViewHolder?> : RecyclerView.Adapte
 | 
			
		||||
    abstract val userIdentifier: String
 | 
			
		||||
    abstract val app: Activity
 | 
			
		||||
    abstract val appColors: AppColors
 | 
			
		||||
    abstract val config: Config
 | 
			
		||||
    abstract val updateItems: (ArrayList<Item>) -> Unit
 | 
			
		||||
 | 
			
		||||
    fun updateAllItems(newItems: ArrayList<Item>) {
 | 
			
		||||
@@ -39,7 +41,7 @@ abstract class ItemsAdapter<VH : RecyclerView.ViewHolder?> : RecyclerView.Adapte
 | 
			
		||||
        updateItems(items)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private fun doUnmark(i: Item, position: Int) {
 | 
			
		||||
    private fun unmarkSnackbar(i: Item, position: Int) {
 | 
			
		||||
        val s = Snackbar
 | 
			
		||||
            .make(
 | 
			
		||||
                app.findViewById(R.id.coordLayout),
 | 
			
		||||
@@ -69,12 +71,11 @@ abstract class ItemsAdapter<VH : RecyclerView.ViewHolder?> : RecyclerView.Adapte
 | 
			
		||||
                            }
 | 
			
		||||
                            notifyItemRemoved(position)
 | 
			
		||||
                            updateItems(items)
 | 
			
		||||
                            doUnmark(i, position)
 | 
			
		||||
                        }
 | 
			
		||||
                    })
 | 
			
		||||
                } else {
 | 
			
		||||
                    thread {
 | 
			
		||||
                        db.actionsDao().deleteReadActionForArticle(i.id)
 | 
			
		||||
                        db.actionsDao().insertAllActions(ActionEntity(i.id, false, true, false, false))
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
@@ -85,7 +86,62 @@ abstract class ItemsAdapter<VH : RecyclerView.ViewHolder?> : RecyclerView.Adapte
 | 
			
		||||
        s.show()
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fun removeItemAtIndex(position: Int) {
 | 
			
		||||
    private fun markSnackbar(i: Item, position: Int) {
 | 
			
		||||
        val s = Snackbar
 | 
			
		||||
            .make(
 | 
			
		||||
                app.findViewById(R.id.coordLayout),
 | 
			
		||||
                R.string.marked_as_unread,
 | 
			
		||||
                Snackbar.LENGTH_LONG
 | 
			
		||||
            )
 | 
			
		||||
            .setAction(R.string.undo_string) {
 | 
			
		||||
                items.add(position, i)
 | 
			
		||||
                thread {
 | 
			
		||||
                    db.itemsDao().delete(i.toEntity())
 | 
			
		||||
                }
 | 
			
		||||
                notifyItemInserted(position)
 | 
			
		||||
                updateItems(items)
 | 
			
		||||
 | 
			
		||||
                if (app.isNetworkAccessible(null)) {
 | 
			
		||||
                    api.markItem(i.id).enqueue(object : Callback<SuccessResponse> {
 | 
			
		||||
                        override fun onResponse(
 | 
			
		||||
                            call: Call<SuccessResponse>,
 | 
			
		||||
                            response: Response<SuccessResponse>
 | 
			
		||||
                        ) {
 | 
			
		||||
                        }
 | 
			
		||||
 | 
			
		||||
                        override fun onFailure(call: Call<SuccessResponse>, t: Throwable) {
 | 
			
		||||
                            items.remove(i)
 | 
			
		||||
                            thread {
 | 
			
		||||
                                db.itemsDao().insertAllItems(i.toEntity())
 | 
			
		||||
                            }
 | 
			
		||||
                            notifyItemRemoved(position)
 | 
			
		||||
                            updateItems(items)
 | 
			
		||||
                        }
 | 
			
		||||
                    })
 | 
			
		||||
                } else {
 | 
			
		||||
                    thread {
 | 
			
		||||
                        db.actionsDao().insertAllActions(ActionEntity(i.id, true, false, false, false))
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
        val view = s.view
 | 
			
		||||
        val tv: TextView = view.findViewById(com.google.android.material.R.id.snackbar_text)
 | 
			
		||||
        tv.setTextColor(Color.WHITE)
 | 
			
		||||
        s.show()
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fun handleItemAtIndex(position: Int) {
 | 
			
		||||
        val i = items[position]
 | 
			
		||||
 | 
			
		||||
        if (i.unread) {
 | 
			
		||||
            readItemAtIndex(position)
 | 
			
		||||
        } else {
 | 
			
		||||
            unreadItemAtIndex(position)
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private fun readItemAtIndex(position: Int) {
 | 
			
		||||
        val i = items[position]
 | 
			
		||||
        items.remove(i)
 | 
			
		||||
        notifyItemRemoved(position)
 | 
			
		||||
@@ -103,7 +159,7 @@ abstract class ItemsAdapter<VH : RecyclerView.ViewHolder?> : RecyclerView.Adapte
 | 
			
		||||
                ) {
 | 
			
		||||
                    if (!response.succeeded() && debugReadingItems) {
 | 
			
		||||
                        val message =
 | 
			
		||||
                            "message: ${response.message()} " +
 | 
			
		||||
                            "MARK message: ${response.message()} " +
 | 
			
		||||
                                    "response isSuccess: ${response.isSuccessful} " +
 | 
			
		||||
                                    "response code: ${response.code()} " +
 | 
			
		||||
                                    "response message: ${response.message()} " +
 | 
			
		||||
@@ -114,7 +170,7 @@ abstract class ItemsAdapter<VH : RecyclerView.ViewHolder?> : RecyclerView.Adapte
 | 
			
		||||
                        Toast.makeText(app.baseContext, message, Toast.LENGTH_LONG).show()
 | 
			
		||||
                    }
 | 
			
		||||
 | 
			
		||||
                    doUnmark(i, position)
 | 
			
		||||
                    unmarkSnackbar(i, position)
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                override fun onFailure(call: Call<SuccessResponse>, t: Throwable) {
 | 
			
		||||
@@ -127,7 +183,7 @@ abstract class ItemsAdapter<VH : RecyclerView.ViewHolder?> : RecyclerView.Adapte
 | 
			
		||||
                        app.getString(R.string.cant_mark_read),
 | 
			
		||||
                        Toast.LENGTH_SHORT
 | 
			
		||||
                    ).show()
 | 
			
		||||
                    items.add(i)
 | 
			
		||||
                    items.add(position, i)
 | 
			
		||||
                    notifyItemInserted(position)
 | 
			
		||||
                    updateItems(items)
 | 
			
		||||
 | 
			
		||||
@@ -139,7 +195,64 @@ abstract class ItemsAdapter<VH : RecyclerView.ViewHolder?> : RecyclerView.Adapte
 | 
			
		||||
        } else {
 | 
			
		||||
            thread {
 | 
			
		||||
                db.actionsDao().insertAllActions(ActionEntity(i.id, true, false, false, false))
 | 
			
		||||
                doUnmark(i, position)
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private fun unreadItemAtIndex(position: Int) {
 | 
			
		||||
        val i = items[position]
 | 
			
		||||
        items.remove(i)
 | 
			
		||||
        notifyItemRemoved(position)
 | 
			
		||||
        updateItems(items)
 | 
			
		||||
 | 
			
		||||
        thread {
 | 
			
		||||
            db.itemsDao().insertAllItems(i.toEntity())
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (app.isNetworkAccessible(null)) {
 | 
			
		||||
            api.unmarkItem(i.id).enqueue(object : Callback<SuccessResponse> {
 | 
			
		||||
                override fun onResponse(
 | 
			
		||||
                    call: Call<SuccessResponse>,
 | 
			
		||||
                    response: Response<SuccessResponse>
 | 
			
		||||
                ) {
 | 
			
		||||
                    if (!response.succeeded() && debugReadingItems) {
 | 
			
		||||
                        val message =
 | 
			
		||||
                            "UNMARK message: ${response.message()} " +
 | 
			
		||||
                                    "response isSuccess: ${response.isSuccessful} " +
 | 
			
		||||
                                    "response code: ${response.code()} " +
 | 
			
		||||
                                    "response message: ${response.message()} " +
 | 
			
		||||
                                    "response errorBody: ${response.errorBody()?.string()} " +
 | 
			
		||||
                                    "body success: ${response.body()?.success} " +
 | 
			
		||||
                                    "body isSuccess: ${response.body()?.isSuccess}"
 | 
			
		||||
                        ACRA.getErrorReporter().maybeHandleSilentException(Exception(message), app)
 | 
			
		||||
                        Toast.makeText(app.baseContext, message, Toast.LENGTH_LONG).show()
 | 
			
		||||
                    }
 | 
			
		||||
 | 
			
		||||
                    markSnackbar(i, position)
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                override fun onFailure(call: Call<SuccessResponse>, t: Throwable) {
 | 
			
		||||
                    if (debugReadingItems) {
 | 
			
		||||
                        ACRA.getErrorReporter().maybeHandleSilentException(t, app)
 | 
			
		||||
                        Toast.makeText(app.baseContext, t.message, Toast.LENGTH_LONG).show()
 | 
			
		||||
                    }
 | 
			
		||||
                    Toast.makeText(
 | 
			
		||||
                        app,
 | 
			
		||||
                        app.getString(R.string.cant_mark_unread),
 | 
			
		||||
                        Toast.LENGTH_SHORT
 | 
			
		||||
                    ).show()
 | 
			
		||||
                    items.add(i)
 | 
			
		||||
                    notifyItemInserted(position)
 | 
			
		||||
                    updateItems(items)
 | 
			
		||||
 | 
			
		||||
                    thread {
 | 
			
		||||
                        db.itemsDao().delete(i.toEntity())
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
            })
 | 
			
		||||
        } else {
 | 
			
		||||
            thread {
 | 
			
		||||
                db.actionsDao().insertAllActions(ActionEntity(i.id, false, true, false, false))
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 
 | 
			
		||||
@@ -12,6 +12,7 @@ import apps.amine.bou.readerforselfoss.R
 | 
			
		||||
import apps.amine.bou.readerforselfoss.api.selfoss.SelfossApi
 | 
			
		||||
import apps.amine.bou.readerforselfoss.api.selfoss.Source
 | 
			
		||||
import apps.amine.bou.readerforselfoss.api.selfoss.SuccessResponse
 | 
			
		||||
import apps.amine.bou.readerforselfoss.utils.Config
 | 
			
		||||
import apps.amine.bou.readerforselfoss.utils.glide.circularBitmapDrawable
 | 
			
		||||
import apps.amine.bou.readerforselfoss.utils.network.isNetworkAccessible
 | 
			
		||||
import apps.amine.bou.readerforselfoss.utils.toTextDrawableString
 | 
			
		||||
@@ -29,6 +30,7 @@ class SourcesListAdapter(
 | 
			
		||||
) : RecyclerView.Adapter<SourcesListAdapter.ViewHolder>() {
 | 
			
		||||
    private val c: Context = app.baseContext
 | 
			
		||||
    private val generator: ColorGenerator = ColorGenerator.MATERIAL
 | 
			
		||||
    private lateinit var config: Config
 | 
			
		||||
 | 
			
		||||
    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
 | 
			
		||||
        val v = LayoutInflater.from(c).inflate(
 | 
			
		||||
@@ -41,6 +43,7 @@ class SourcesListAdapter(
 | 
			
		||||
 | 
			
		||||
    override fun onBindViewHolder(holder: ViewHolder, position: Int) {
 | 
			
		||||
        val itm = items[position]
 | 
			
		||||
        config = Config(c)
 | 
			
		||||
 | 
			
		||||
        if (itm.getIcon(c).isEmpty()) {
 | 
			
		||||
            val color = generator.getColor(itm.title)
 | 
			
		||||
@@ -52,7 +55,7 @@ class SourcesListAdapter(
 | 
			
		||||
                    .build(itm.title.toTextDrawableString(c), color)
 | 
			
		||||
            holder.mView.itemImage.setImageDrawable(drawable)
 | 
			
		||||
        } else {
 | 
			
		||||
            c.circularBitmapDrawable(itm.getIcon(c), holder.mView.itemImage)
 | 
			
		||||
            c.circularBitmapDrawable(config, itm.getIcon(c), holder.mView.itemImage)
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        holder.mView.sourceTitle.text = itm.title
 | 
			
		||||
 
 | 
			
		||||
@@ -46,7 +46,7 @@ class LoadingWorker(val context: Context, params: WorkerParameters) : Worker(con
 | 
			
		||||
                .setOngoing(true)
 | 
			
		||||
                .setPriority(PRIORITY_LOW)
 | 
			
		||||
                .setChannelId(Config.syncChannelId)
 | 
			
		||||
                .setSmallIcon(R.drawable.ic_cloud_download)
 | 
			
		||||
                .setSmallIcon(R.drawable.ic_stat_cloud_download_black_24dp)
 | 
			
		||||
 | 
			
		||||
            notificationManager.notify(1, notification.build())
 | 
			
		||||
 | 
			
		||||
@@ -101,7 +101,7 @@ class LoadingWorker(val context: Context, params: WorkerParameters) : Worker(con
 | 
			
		||||
                                    .setChannelId(Config.newItemsChannelId)
 | 
			
		||||
                                    .setContentIntent(pendingIntent)
 | 
			
		||||
                                    .setAutoCancel(true)
 | 
			
		||||
                                    .setSmallIcon(R.drawable.ic_fiber_new_black_24dp)
 | 
			
		||||
                                    .setSmallIcon(R.drawable.ic_tab_fiber_new_black_24dp)
 | 
			
		||||
 | 
			
		||||
                                Timer("", false).schedule(4000) {
 | 
			
		||||
                                    notificationManager.notify(2, newItemsNotification.build())
 | 
			
		||||
 
 | 
			
		||||
@@ -3,10 +3,13 @@ package apps.amine.bou.readerforselfoss.fragments
 | 
			
		||||
import android.content.Context
 | 
			
		||||
import android.content.SharedPreferences
 | 
			
		||||
import android.content.res.ColorStateList
 | 
			
		||||
import android.content.res.TypedArray
 | 
			
		||||
import android.graphics.Typeface
 | 
			
		||||
import android.graphics.drawable.ColorDrawable
 | 
			
		||||
import android.os.Build
 | 
			
		||||
import android.os.Bundle
 | 
			
		||||
import android.preference.PreferenceManager
 | 
			
		||||
import android.view.InflateException
 | 
			
		||||
import androidx.browser.customtabs.CustomTabsIntent
 | 
			
		||||
import com.google.android.material.floatingactionbutton.FloatingActionButton
 | 
			
		||||
import androidx.fragment.app.Fragment
 | 
			
		||||
@@ -17,6 +20,8 @@ import android.view.MenuItem
 | 
			
		||||
import android.view.View
 | 
			
		||||
import android.view.ViewGroup
 | 
			
		||||
import android.webkit.WebSettings
 | 
			
		||||
import androidx.appcompat.app.AlertDialog
 | 
			
		||||
import androidx.core.content.res.ResourcesCompat
 | 
			
		||||
import androidx.room.Room
 | 
			
		||||
import apps.amine.bou.readerforselfoss.R
 | 
			
		||||
import apps.amine.bou.readerforselfoss.api.mercury.MercuryApi
 | 
			
		||||
@@ -32,6 +37,7 @@ import apps.amine.bou.readerforselfoss.themes.AppColors
 | 
			
		||||
import apps.amine.bou.readerforselfoss.utils.Config
 | 
			
		||||
import apps.amine.bou.readerforselfoss.utils.buildCustomTabsIntent
 | 
			
		||||
import apps.amine.bou.readerforselfoss.utils.customtabs.CustomTabActivityHelper
 | 
			
		||||
import apps.amine.bou.readerforselfoss.utils.glide.loadMaybeBasicAuth
 | 
			
		||||
import apps.amine.bou.readerforselfoss.utils.isEmptyOrNullOrNullString
 | 
			
		||||
import apps.amine.bou.readerforselfoss.utils.maybeHandleSilentException
 | 
			
		||||
import apps.amine.bou.readerforselfoss.utils.network.isNetworkAccessible
 | 
			
		||||
@@ -39,10 +45,10 @@ import apps.amine.bou.readerforselfoss.utils.openItemUrl
 | 
			
		||||
import apps.amine.bou.readerforselfoss.utils.shareLink
 | 
			
		||||
import apps.amine.bou.readerforselfoss.utils.sourceAndDateText
 | 
			
		||||
import apps.amine.bou.readerforselfoss.utils.succeeded
 | 
			
		||||
import apps.amine.bou.readerforselfoss.utils.toPx
 | 
			
		||||
import com.bumptech.glide.Glide
 | 
			
		||||
import com.bumptech.glide.request.RequestOptions
 | 
			
		||||
import com.github.rubensousa.floatingtoolbar.FloatingToolbar
 | 
			
		||||
import kotlinx.android.synthetic.main.fragment_article.*
 | 
			
		||||
import kotlinx.android.synthetic.main.fragment_article.view.*
 | 
			
		||||
import org.acra.ACRA
 | 
			
		||||
import retrofit2.Call
 | 
			
		||||
@@ -54,9 +60,9 @@ import kotlin.concurrent.thread
 | 
			
		||||
 | 
			
		||||
class ArticleFragment : Fragment() {
 | 
			
		||||
    private lateinit var pageNumber: Number
 | 
			
		||||
    private var fontSize: Int = 14
 | 
			
		||||
    private var fontSize: Int = 16
 | 
			
		||||
    private lateinit var allItems: ArrayList<Item>
 | 
			
		||||
    private lateinit var mCustomTabActivityHelper: CustomTabActivityHelper
 | 
			
		||||
    private var mCustomTabActivityHelper: CustomTabActivityHelper? = null;
 | 
			
		||||
    private lateinit var url: String
 | 
			
		||||
    private lateinit var contentText: String
 | 
			
		||||
    private lateinit var contentSource: String
 | 
			
		||||
@@ -66,14 +72,27 @@ class ArticleFragment : Fragment() {
 | 
			
		||||
    private lateinit var fab: FloatingActionButton
 | 
			
		||||
    private lateinit var appColors: AppColors
 | 
			
		||||
    private lateinit var db: AppDatabase
 | 
			
		||||
    private lateinit var textAlignment: String
 | 
			
		||||
    private lateinit var config: Config
 | 
			
		||||
 | 
			
		||||
    private var rootView: ViewGroup? = null
 | 
			
		||||
 | 
			
		||||
    private lateinit var prefs: SharedPreferences
 | 
			
		||||
 | 
			
		||||
    private var typeface: Typeface? = null
 | 
			
		||||
    private var resId: Int = 0
 | 
			
		||||
    private var font = ""
 | 
			
		||||
 | 
			
		||||
    override fun onStop() {
 | 
			
		||||
        super.onStop()
 | 
			
		||||
        mCustomTabActivityHelper.unbindCustomTabsService(activity)
 | 
			
		||||
        if (mCustomTabActivityHelper != null) {
 | 
			
		||||
            mCustomTabActivityHelper!!.unbindCustomTabsService(activity)
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    override fun onCreate(savedInstanceState: Bundle?) {
 | 
			
		||||
        appColors = AppColors(activity!!)
 | 
			
		||||
        config = Config(activity!!)
 | 
			
		||||
 | 
			
		||||
        super.onCreate(savedInstanceState)
 | 
			
		||||
 | 
			
		||||
@@ -86,154 +105,190 @@ class ArticleFragment : Fragment() {
 | 
			
		||||
        ).addMigrations(MIGRATION_1_2).addMigrations(MIGRATION_2_3).build()
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private lateinit var rootView: ViewGroup
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    override fun onCreateView(
 | 
			
		||||
        inflater: LayoutInflater,
 | 
			
		||||
        container: ViewGroup?,
 | 
			
		||||
        savedInstanceState: Bundle?
 | 
			
		||||
    ): View? {
 | 
			
		||||
        rootView = inflater
 | 
			
		||||
            .inflate(R.layout.fragment_article, container, false) as ViewGroup
 | 
			
		||||
        try {
 | 
			
		||||
            rootView = inflater
 | 
			
		||||
                .inflate(R.layout.fragment_article, container, false) as ViewGroup
 | 
			
		||||
 | 
			
		||||
        url = allItems[pageNumber.toInt()].getLinkDecoded()
 | 
			
		||||
        contentText = allItems[pageNumber.toInt()].content
 | 
			
		||||
        contentTitle = allItems[pageNumber.toInt()].title
 | 
			
		||||
        contentImage = allItems[pageNumber.toInt()].getThumbnail(activity!!)
 | 
			
		||||
        contentSource = allItems[pageNumber.toInt()].sourceAndDateText()
 | 
			
		||||
            url = allItems[pageNumber.toInt()].getLinkDecoded()
 | 
			
		||||
            contentText = allItems[pageNumber.toInt()].content
 | 
			
		||||
            contentTitle = allItems[pageNumber.toInt()].title
 | 
			
		||||
            contentImage = allItems[pageNumber.toInt()].getThumbnail(activity!!)
 | 
			
		||||
            contentSource = allItems[pageNumber.toInt()].sourceAndDateText()
 | 
			
		||||
 | 
			
		||||
        val prefs = PreferenceManager.getDefaultSharedPreferences(activity)
 | 
			
		||||
        editor = prefs.edit()
 | 
			
		||||
        fontSize = prefs.getString("reader_font_size", "14").toInt()
 | 
			
		||||
            prefs = PreferenceManager.getDefaultSharedPreferences(activity)
 | 
			
		||||
            editor = prefs.edit()
 | 
			
		||||
            fontSize = prefs.getString("reader_font_size", "16").toInt()
 | 
			
		||||
 | 
			
		||||
        val settings = activity!!.getSharedPreferences(Config.settingsName, Context.MODE_PRIVATE)
 | 
			
		||||
        val debugReadingItems = prefs.getBoolean("read_debug", false)
 | 
			
		||||
            font = prefs.getString("reader_font", "")
 | 
			
		||||
            if (font.isNotEmpty()) {
 | 
			
		||||
                resId = context!!.resources.getIdentifier(font, "font", context!!.packageName)
 | 
			
		||||
                typeface = ResourcesCompat.getFont(context!!, resId)!!
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
        val api = SelfossApi(
 | 
			
		||||
            context!!,
 | 
			
		||||
            activity!!,
 | 
			
		||||
            settings.getBoolean("isSelfSignedCert", false),
 | 
			
		||||
            prefs.getString("api_timeout", "-1").toLong(),
 | 
			
		||||
            prefs.getBoolean("should_log_everything", false)
 | 
			
		||||
        )
 | 
			
		||||
            refreshAlignment()
 | 
			
		||||
 | 
			
		||||
        fab = rootView.fab
 | 
			
		||||
            val settings = activity!!.getSharedPreferences(Config.settingsName, Context.MODE_PRIVATE)
 | 
			
		||||
            val debugReadingItems = prefs.getBoolean("read_debug", false)
 | 
			
		||||
 | 
			
		||||
        fab.backgroundTintList = ColorStateList.valueOf(appColors.colorAccent)
 | 
			
		||||
            val api = SelfossApi(
 | 
			
		||||
                context!!,
 | 
			
		||||
                activity!!,
 | 
			
		||||
                settings.getBoolean("isSelfSignedCert", false),
 | 
			
		||||
                prefs.getString("api_timeout", "-1").toLong(),
 | 
			
		||||
                prefs.getBoolean("should_log_everything", false)
 | 
			
		||||
            )
 | 
			
		||||
 | 
			
		||||
        fab.rippleColor = appColors.colorAccentDark
 | 
			
		||||
            fab = rootView!!.fab
 | 
			
		||||
 | 
			
		||||
        val floatingToolbar: FloatingToolbar = rootView.floatingToolbar
 | 
			
		||||
        floatingToolbar.attachFab(fab)
 | 
			
		||||
            fab.backgroundTintList = ColorStateList.valueOf(appColors.colorAccent)
 | 
			
		||||
 | 
			
		||||
        floatingToolbar.background = ColorDrawable(appColors.colorAccent)
 | 
			
		||||
            fab.rippleColor = appColors.colorAccentDark
 | 
			
		||||
 | 
			
		||||
        val customTabsIntent = activity!!.buildCustomTabsIntent()
 | 
			
		||||
        mCustomTabActivityHelper = CustomTabActivityHelper()
 | 
			
		||||
        mCustomTabActivityHelper.bindCustomTabsService(activity)
 | 
			
		||||
            val floatingToolbar: FloatingToolbar = rootView!!.floatingToolbar
 | 
			
		||||
            floatingToolbar.attachFab(fab)
 | 
			
		||||
 | 
			
		||||
            floatingToolbar.background = ColorDrawable(appColors.colorAccent)
 | 
			
		||||
 | 
			
		||||
            val customTabsIntent = activity!!.buildCustomTabsIntent()
 | 
			
		||||
            mCustomTabActivityHelper = CustomTabActivityHelper()
 | 
			
		||||
            mCustomTabActivityHelper!!.bindCustomTabsService(activity)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
        floatingToolbar.setClickListener(
 | 
			
		||||
            object : FloatingToolbar.ItemClickListener {
 | 
			
		||||
                override fun onItemClick(item: MenuItem) {
 | 
			
		||||
                    when (item.itemId) {
 | 
			
		||||
                        R.id.more_action -> getContentFromMercury(customTabsIntent, prefs)
 | 
			
		||||
                        R.id.share_action -> activity!!.shareLink(url, contentTitle)
 | 
			
		||||
                        R.id.open_action -> activity!!.openItemUrl(
 | 
			
		||||
                            allItems,
 | 
			
		||||
                            pageNumber.toInt(),
 | 
			
		||||
                            url,
 | 
			
		||||
                            customTabsIntent,
 | 
			
		||||
                            false,
 | 
			
		||||
                            false,
 | 
			
		||||
                            activity!!
 | 
			
		||||
                        )
 | 
			
		||||
                        R.id.unread_action -> if ((context != null && context!!.isNetworkAccessible(null)) || context == null) {
 | 
			
		||||
                            api.unmarkItem(allItems[pageNumber.toInt()].id).enqueue(
 | 
			
		||||
                                object : Callback<SuccessResponse> {
 | 
			
		||||
                                    override fun onResponse(
 | 
			
		||||
                                        call: Call<SuccessResponse>,
 | 
			
		||||
                                        response: Response<SuccessResponse>
 | 
			
		||||
                                    ) {
 | 
			
		||||
                                        if (!response.succeeded() && debugReadingItems) {
 | 
			
		||||
                                            val message =
 | 
			
		||||
                                                "message: ${response.message()} " +
 | 
			
		||||
                                                        "response isSuccess: ${response.isSuccessful} " +
 | 
			
		||||
                                                        "response code: ${response.code()} " +
 | 
			
		||||
                                                        "response message: ${response.message()} " +
 | 
			
		||||
                                                        "response errorBody: ${response.errorBody()?.string()} " +
 | 
			
		||||
                                                        "body success: ${response.body()?.success} " +
 | 
			
		||||
                                                        "body isSuccess: ${response.body()?.isSuccess}"
 | 
			
		||||
                                            ACRA.getErrorReporter().maybeHandleSilentException(Exception(message), activity!!)
 | 
			
		||||
                                        }
 | 
			
		||||
                                    }
 | 
			
		||||
 | 
			
		||||
                                    override fun onFailure(
 | 
			
		||||
                                        call: Call<SuccessResponse>,
 | 
			
		||||
                                        t: Throwable
 | 
			
		||||
                                    ) {
 | 
			
		||||
                                        if (debugReadingItems) {
 | 
			
		||||
                                            ACRA.getErrorReporter().maybeHandleSilentException(t, activity!!)
 | 
			
		||||
                                        }
 | 
			
		||||
                                    }
 | 
			
		||||
                                }
 | 
			
		||||
            floatingToolbar.setClickListener(
 | 
			
		||||
                object : FloatingToolbar.ItemClickListener {
 | 
			
		||||
                    override fun onItemClick(item: MenuItem) {
 | 
			
		||||
                        when (item.itemId) {
 | 
			
		||||
                            R.id.more_action -> getContentFromMercury(customTabsIntent, prefs)
 | 
			
		||||
                            R.id.share_action -> activity!!.shareLink(url, contentTitle)
 | 
			
		||||
                            R.id.open_action -> activity!!.openItemUrl(
 | 
			
		||||
                                allItems,
 | 
			
		||||
                                pageNumber.toInt(),
 | 
			
		||||
                                url,
 | 
			
		||||
                                customTabsIntent,
 | 
			
		||||
                                false,
 | 
			
		||||
                                false,
 | 
			
		||||
                                activity!!
 | 
			
		||||
                            )
 | 
			
		||||
                        } else {
 | 
			
		||||
                            thread {
 | 
			
		||||
                                db.actionsDao().insertAllActions(ActionEntity(allItems[pageNumber.toInt()].id, false, true, false, false))
 | 
			
		||||
                            R.id.unread_action -> if ((context != null && context!!.isNetworkAccessible(null)) || context == null) {
 | 
			
		||||
                                api.unmarkItem(allItems[pageNumber.toInt()].id).enqueue(
 | 
			
		||||
                                    object : Callback<SuccessResponse> {
 | 
			
		||||
                                        override fun onResponse(
 | 
			
		||||
                                            call: Call<SuccessResponse>,
 | 
			
		||||
                                            response: Response<SuccessResponse>
 | 
			
		||||
                                        ) {
 | 
			
		||||
                                            if (!response.succeeded() && debugReadingItems) {
 | 
			
		||||
                                                val message =
 | 
			
		||||
                                                    "message: ${response.message()} " +
 | 
			
		||||
                                                            "response isSuccess: ${response.isSuccessful} " +
 | 
			
		||||
                                                            "response code: ${response.code()} " +
 | 
			
		||||
                                                            "response message: ${response.message()} " +
 | 
			
		||||
                                                            "response errorBody: ${response.errorBody()?.string()} " +
 | 
			
		||||
                                                            "body success: ${response.body()?.success} " +
 | 
			
		||||
                                                            "body isSuccess: ${response.body()?.isSuccess}"
 | 
			
		||||
                                                ACRA.getErrorReporter().maybeHandleSilentException(Exception(message), activity!!)
 | 
			
		||||
                                            }
 | 
			
		||||
                                        }
 | 
			
		||||
 | 
			
		||||
                                        override fun onFailure(
 | 
			
		||||
                                            call: Call<SuccessResponse>,
 | 
			
		||||
                                            t: Throwable
 | 
			
		||||
                                        ) {
 | 
			
		||||
                                            if (debugReadingItems) {
 | 
			
		||||
                                                ACRA.getErrorReporter().maybeHandleSilentException(t, activity!!)
 | 
			
		||||
                                            }
 | 
			
		||||
                                        }
 | 
			
		||||
                                    }
 | 
			
		||||
                                )
 | 
			
		||||
                            } else {
 | 
			
		||||
                                thread {
 | 
			
		||||
                                    db.actionsDao().insertAllActions(ActionEntity(allItems[pageNumber.toInt()].id, false, true, false, false))
 | 
			
		||||
                                }
 | 
			
		||||
                            }
 | 
			
		||||
                            else -> Unit
 | 
			
		||||
                        }
 | 
			
		||||
                        else -> Unit
 | 
			
		||||
                    }
 | 
			
		||||
 | 
			
		||||
                    override fun onItemLongClick(item: MenuItem?) {
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
            )
 | 
			
		||||
 | 
			
		||||
                override fun onItemLongClick(item: MenuItem?) {
 | 
			
		||||
            rootView!!.source.text = contentSource
 | 
			
		||||
            if (typeface != null) {
 | 
			
		||||
                rootView!!.source.typeface = typeface
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            if (contentText.isEmptyOrNullOrNullString()) {
 | 
			
		||||
                getContentFromMercury(customTabsIntent, prefs)
 | 
			
		||||
            } else {
 | 
			
		||||
                rootView!!.titleView.text = contentTitle
 | 
			
		||||
                if (typeface != null) {
 | 
			
		||||
                    rootView!!.titleView.typeface = typeface
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                htmlToWebview()
 | 
			
		||||
 | 
			
		||||
                if (!contentImage.isEmptyOrNullOrNullString() && context != null) {
 | 
			
		||||
                    rootView!!.imageView.visibility = View.VISIBLE
 | 
			
		||||
                    Glide
 | 
			
		||||
                        .with(context!!)
 | 
			
		||||
                        .asBitmap()
 | 
			
		||||
                        .loadMaybeBasicAuth(config, contentImage)
 | 
			
		||||
                        .apply(RequestOptions.fitCenterTransform())
 | 
			
		||||
                        .into(rootView!!.imageView)
 | 
			
		||||
                } else {
 | 
			
		||||
                    rootView!!.imageView.visibility = View.GONE
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
        rootView.source.text = contentSource
 | 
			
		||||
            rootView!!.nestedScrollView.setOnScrollChangeListener(
 | 
			
		||||
                NestedScrollView.OnScrollChangeListener { _, _, scrollY, _, oldScrollY ->
 | 
			
		||||
                    if (scrollY > oldScrollY) {
 | 
			
		||||
                        fab.hide()
 | 
			
		||||
                    } else {
 | 
			
		||||
                        if (floatingToolbar.isShowing) floatingToolbar.hide() else fab.show()
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
            )
 | 
			
		||||
 | 
			
		||||
        if (contentText.isEmptyOrNullOrNullString()) {
 | 
			
		||||
            getContentFromMercury(customTabsIntent, prefs)
 | 
			
		||||
        } else {
 | 
			
		||||
            rootView.titleView.text = contentTitle
 | 
			
		||||
 | 
			
		||||
            htmlToWebview(contentText, prefs)
 | 
			
		||||
 | 
			
		||||
            if (!contentImage.isEmptyOrNullOrNullString() && context != null) {
 | 
			
		||||
                rootView.imageView.visibility = View.VISIBLE
 | 
			
		||||
                Glide
 | 
			
		||||
                    .with(context!!)
 | 
			
		||||
                    .asBitmap()
 | 
			
		||||
                    .load(contentImage)
 | 
			
		||||
                    .apply(RequestOptions.fitCenterTransform())
 | 
			
		||||
                    .into(rootView.imageView)
 | 
			
		||||
            } else {
 | 
			
		||||
                rootView.imageView.visibility = View.GONE
 | 
			
		||||
            }
 | 
			
		||||
        } catch (e: InflateException) {
 | 
			
		||||
            AlertDialog.Builder(context!!)
 | 
			
		||||
                .setMessage(context!!.getString(R.string.webview_dialog_issue_message))
 | 
			
		||||
                .setTitle(context!!.getString(R.string.webview_dialog_issue_title))
 | 
			
		||||
                .setPositiveButton(android.R.string.ok
 | 
			
		||||
                ) { dialog, which ->
 | 
			
		||||
                    val sharedPref = PreferenceManager.getDefaultSharedPreferences(context!!)
 | 
			
		||||
                    val editor = sharedPref.edit()
 | 
			
		||||
                    editor.putBoolean("prefer_article_viewer", false)
 | 
			
		||||
                    editor.commit()
 | 
			
		||||
                    activity!!.finish()
 | 
			
		||||
                }
 | 
			
		||||
                .create()
 | 
			
		||||
                .show()
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        rootView.nestedScrollView.setOnScrollChangeListener(
 | 
			
		||||
            NestedScrollView.OnScrollChangeListener { _, _, scrollY, _, oldScrollY ->
 | 
			
		||||
                if (scrollY > oldScrollY) {
 | 
			
		||||
                    fab.hide()
 | 
			
		||||
                } else {
 | 
			
		||||
                    if (floatingToolbar.isShowing) floatingToolbar.hide() else fab.show()
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
        return rootView
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private fun refreshAlignment() {
 | 
			
		||||
        textAlignment = when (prefs.getInt("text_align", 1)) {
 | 
			
		||||
            1 -> "justify"
 | 
			
		||||
            2 -> "left"
 | 
			
		||||
            else -> "justify"
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private fun getContentFromMercury(
 | 
			
		||||
        customTabsIntent: CustomTabsIntent,
 | 
			
		||||
        prefs: SharedPreferences
 | 
			
		||||
    ) {
 | 
			
		||||
        if ((context != null && context!!.isNetworkAccessible(null)) || context == null) {
 | 
			
		||||
            rootView.progressBar.visibility = View.VISIBLE
 | 
			
		||||
            rootView!!.progressBar.visibility = View.VISIBLE
 | 
			
		||||
            val parser = MercuryApi(
 | 
			
		||||
                prefs.getBoolean("should_log_everything", false)
 | 
			
		||||
            )
 | 
			
		||||
@@ -248,13 +303,16 @@ class ArticleFragment : Fragment() {
 | 
			
		||||
                        try {
 | 
			
		||||
                            if (response.body() != null && response.body()!!.content != null && !response.body()!!.content.isNullOrEmpty()) {
 | 
			
		||||
                                try {
 | 
			
		||||
                                    rootView.titleView.text = response.body()!!.title
 | 
			
		||||
                                    rootView!!.titleView.text = response.body()!!.title
 | 
			
		||||
                                    if (typeface != null) {
 | 
			
		||||
                                        rootView!!.titleView.typeface = typeface
 | 
			
		||||
                                    }
 | 
			
		||||
                                    try {
 | 
			
		||||
                                        // Note: Mercury may return relative urls... If it does the url val will not be changed.
 | 
			
		||||
                                        URL(response.body()!!.url)
 | 
			
		||||
                                        url = response.body()!!.url
 | 
			
		||||
                                    } catch (e: MalformedURLException) {
 | 
			
		||||
                                        ACRA.getErrorReporter().maybeHandleSilentException(e, activity!!)
 | 
			
		||||
                                        // Mercury returned a relative url. We do nothing.
 | 
			
		||||
                                    }
 | 
			
		||||
                                } catch (e: Exception) {
 | 
			
		||||
                                    if (context != null) {
 | 
			
		||||
@@ -263,7 +321,8 @@ class ArticleFragment : Fragment() {
 | 
			
		||||
                                }
 | 
			
		||||
 | 
			
		||||
                                try {
 | 
			
		||||
                                    htmlToWebview(response.body()!!.content.orEmpty(), prefs)
 | 
			
		||||
                                    contentText = response.body()!!.content.orEmpty()
 | 
			
		||||
                                    htmlToWebview()
 | 
			
		||||
                                } catch (e: Exception) {
 | 
			
		||||
                                    if (context != null) {
 | 
			
		||||
                                        ACRA.getErrorReporter().maybeHandleSilentException(e, context!!)
 | 
			
		||||
@@ -272,19 +331,19 @@ class ArticleFragment : Fragment() {
 | 
			
		||||
 | 
			
		||||
                                try {
 | 
			
		||||
                                    if (response.body()!!.lead_image_url != null && !response.body()!!.lead_image_url.isNullOrEmpty() && context != null) {
 | 
			
		||||
                                        rootView.imageView.visibility = View.VISIBLE
 | 
			
		||||
                                        rootView!!.imageView.visibility = View.VISIBLE
 | 
			
		||||
                                        try {
 | 
			
		||||
                                            Glide
 | 
			
		||||
                                                .with(context!!)
 | 
			
		||||
                                                .asBitmap()
 | 
			
		||||
                                                .load(response.body()!!.lead_image_url)
 | 
			
		||||
                                                .loadMaybeBasicAuth(config, response.body()!!.lead_image_url.orEmpty())
 | 
			
		||||
                                                .apply(RequestOptions.fitCenterTransform())
 | 
			
		||||
                                                .into(rootView.imageView)
 | 
			
		||||
                                                .into(rootView!!.imageView)
 | 
			
		||||
                                        } catch (e: Exception) {
 | 
			
		||||
                                            ACRA.getErrorReporter().maybeHandleSilentException(e, context!!)
 | 
			
		||||
                                        }
 | 
			
		||||
                                    } else {
 | 
			
		||||
                                        rootView.imageView.visibility = View.GONE
 | 
			
		||||
                                        rootView!!.imageView.visibility = View.GONE
 | 
			
		||||
                                    }
 | 
			
		||||
                                } catch (e: Exception) {
 | 
			
		||||
                                    if (context != null) {
 | 
			
		||||
@@ -293,9 +352,9 @@ class ArticleFragment : Fragment() {
 | 
			
		||||
                                }
 | 
			
		||||
 | 
			
		||||
                                try {
 | 
			
		||||
                                    rootView.nestedScrollView.scrollTo(0, 0)
 | 
			
		||||
                                    rootView!!.nestedScrollView.scrollTo(0, 0)
 | 
			
		||||
 | 
			
		||||
                                    rootView.progressBar.visibility = View.GONE
 | 
			
		||||
                                    rootView!!.progressBar.visibility = View.GONE
 | 
			
		||||
                                } catch (e: Exception) {
 | 
			
		||||
                                    if (context != null) {
 | 
			
		||||
                                        ACRA.getErrorReporter().maybeHandleSilentException(e, context!!)
 | 
			
		||||
@@ -326,13 +385,18 @@ class ArticleFragment : Fragment() {
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private fun htmlToWebview(c: String, prefs: SharedPreferences) {
 | 
			
		||||
    private fun htmlToWebview() {
 | 
			
		||||
        val stringColor = String.format("#%06X", 0xFFFFFF and appColors.colorAccent)
 | 
			
		||||
 | 
			
		||||
        rootView.webcontent.visibility = View.VISIBLE
 | 
			
		||||
        val attrs: IntArray = intArrayOf(android.R.attr.fontFamily)
 | 
			
		||||
        val a: TypedArray = context!!.obtainStyledAttributes(resId, attrs)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
        rootView!!.webcontent.settings.standardFontFamily = a.getString(0)
 | 
			
		||||
        rootView!!.webcontent.visibility = View.VISIBLE
 | 
			
		||||
        val (textColor, backgroundColor) = if (appColors.isDarkTheme) {
 | 
			
		||||
            if (context != null) {
 | 
			
		||||
                rootView.webcontent.setBackgroundColor(
 | 
			
		||||
                rootView!!.webcontent.setBackgroundColor(
 | 
			
		||||
                    ContextCompat.getColor(
 | 
			
		||||
                        context!!,
 | 
			
		||||
                        R.color.dark_webview
 | 
			
		||||
@@ -344,7 +408,7 @@ class ArticleFragment : Fragment() {
 | 
			
		||||
            }
 | 
			
		||||
        } else {
 | 
			
		||||
            if (context != null) {
 | 
			
		||||
                rootView.webcontent.setBackgroundColor(
 | 
			
		||||
                rootView!!.webcontent.setBackgroundColor(
 | 
			
		||||
                    ContextCompat.getColor(
 | 
			
		||||
                        context!!,
 | 
			
		||||
                        R.color.light_webview
 | 
			
		||||
@@ -368,15 +432,15 @@ class ArticleFragment : Fragment() {
 | 
			
		||||
            "#FFFFFF"
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        rootView.webcontent.settings.useWideViewPort = true
 | 
			
		||||
        rootView.webcontent.settings.loadWithOverviewMode = true
 | 
			
		||||
        rootView.webcontent.settings.javaScriptEnabled = false
 | 
			
		||||
        rootView!!.webcontent.settings.useWideViewPort = true
 | 
			
		||||
        rootView!!.webcontent.settings.loadWithOverviewMode = true
 | 
			
		||||
        rootView!!.webcontent.settings.javaScriptEnabled = false
 | 
			
		||||
 | 
			
		||||
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
 | 
			
		||||
            rootView.webcontent.settings.layoutAlgorithm =
 | 
			
		||||
            rootView!!.webcontent.settings.layoutAlgorithm =
 | 
			
		||||
                    WebSettings.LayoutAlgorithm.TEXT_AUTOSIZING
 | 
			
		||||
        } else {
 | 
			
		||||
            rootView.webcontent.settings.layoutAlgorithm = WebSettings.LayoutAlgorithm.SINGLE_COLUMN
 | 
			
		||||
            rootView!!.webcontent.settings.layoutAlgorithm = WebSettings.LayoutAlgorithm.SINGLE_COLUMN
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        var baseUrl: String? = null
 | 
			
		||||
@@ -388,10 +452,29 @@ class ArticleFragment : Fragment() {
 | 
			
		||||
            ACRA.getErrorReporter().maybeHandleSilentException(e, activity!!)
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        rootView.webcontent.loadDataWithBaseURL(
 | 
			
		||||
        val fontName =  when (font) {
 | 
			
		||||
            getString(R.string.open_sans_font_id) -> "Open Sans"
 | 
			
		||||
            getString(R.string.roboto_font_id) -> "Roboto"
 | 
			
		||||
            else -> ""
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        val fontLinkAndStyle = if (font.isNotEmpty()) {
 | 
			
		||||
            """<link href="https://fonts.googleapis.com/css?family=${fontName.replace(" ", "+")}" rel="stylesheet">
 | 
			
		||||
                |<style>
 | 
			
		||||
                |   * {
 | 
			
		||||
                |       font-family: '$fontName';
 | 
			
		||||
                |   }
 | 
			
		||||
                |</style>
 | 
			
		||||
            """.trimMargin()
 | 
			
		||||
        } else {
 | 
			
		||||
            ""
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        rootView!!.webcontent.loadDataWithBaseURL(
 | 
			
		||||
            baseUrl,
 | 
			
		||||
            """<html>
 | 
			
		||||
                |<head>
 | 
			
		||||
                |   <meta name="viewport" content="width=device-width, initial-scale=1">
 | 
			
		||||
                |   <style>
 | 
			
		||||
                |      img {
 | 
			
		||||
                |        display: inline-block;
 | 
			
		||||
@@ -406,13 +489,14 @@ class ArticleFragment : Fragment() {
 | 
			
		||||
                |        color: $stringTextColor;
 | 
			
		||||
                |      }
 | 
			
		||||
                |      * {
 | 
			
		||||
                |        font-size: ${fontSize.toPx}px;
 | 
			
		||||
                |        text-align: justify;
 | 
			
		||||
                |        font-size: ${fontSize}px;
 | 
			
		||||
                |        text-align: $textAlignment;
 | 
			
		||||
                |        word-break: break-word;
 | 
			
		||||
                |        overflow:hidden;
 | 
			
		||||
                |        line-height: 1.5em;
 | 
			
		||||
                |      }
 | 
			
		||||
                |      a, pre, code {
 | 
			
		||||
                |        text-align: left;
 | 
			
		||||
                |        text-align: $textAlignment;
 | 
			
		||||
                |      }
 | 
			
		||||
                |      pre, code {
 | 
			
		||||
                |        white-space: pre-wrap;
 | 
			
		||||
@@ -420,9 +504,10 @@ class ArticleFragment : Fragment() {
 | 
			
		||||
                |        background-color: $stringBackgroundColor;
 | 
			
		||||
                |      }
 | 
			
		||||
                |   </style>
 | 
			
		||||
                |   $fontLinkAndStyle
 | 
			
		||||
                |</head>
 | 
			
		||||
                |<body>
 | 
			
		||||
                |   $c
 | 
			
		||||
                |   $contentText
 | 
			
		||||
                |</body>""".trimMargin(),
 | 
			
		||||
            "text/html",
 | 
			
		||||
            "utf-8",
 | 
			
		||||
@@ -431,7 +516,7 @@ class ArticleFragment : Fragment() {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private fun openInBrowserAfterFailing(customTabsIntent: CustomTabsIntent) {
 | 
			
		||||
        rootView.progressBar.visibility = View.GONE
 | 
			
		||||
        rootView!!.progressBar.visibility = View.GONE
 | 
			
		||||
        activity!!.openItemUrl(
 | 
			
		||||
            allItems,
 | 
			
		||||
            pageNumber.toInt(),
 | 
			
		||||
 
 | 
			
		||||
@@ -8,6 +8,7 @@ import android.content.Context;
 | 
			
		||||
import android.content.Intent;
 | 
			
		||||
import android.content.SharedPreferences;
 | 
			
		||||
import android.content.res.Configuration;
 | 
			
		||||
import android.content.res.Resources;
 | 
			
		||||
import android.net.Uri;
 | 
			
		||||
import android.os.Build;
 | 
			
		||||
import android.os.Bundle;
 | 
			
		||||
@@ -24,6 +25,7 @@ import android.text.Editable;
 | 
			
		||||
import android.text.InputFilter;
 | 
			
		||||
import android.text.Spanned;
 | 
			
		||||
import android.text.TextWatcher;
 | 
			
		||||
import android.util.Log;
 | 
			
		||||
import android.view.Menu;
 | 
			
		||||
import android.view.MenuInflater;
 | 
			
		||||
import android.view.MenuItem;
 | 
			
		||||
@@ -124,6 +126,27 @@ public class SettingsActivity extends AppCompatPreferenceActivity {
 | 
			
		||||
    @TargetApi(Build.VERSION_CODES.HONEYCOMB)
 | 
			
		||||
    public void onBuildHeaders(List<Header> target) {
 | 
			
		||||
        loadHeadersFromResource(R.xml.pref_headers, target);
 | 
			
		||||
 | 
			
		||||
        AppColors appColors = new AppColors(this);
 | 
			
		||||
        if (appColors != null && appColors.isDarkTheme()) {
 | 
			
		||||
            for (Header header : target) {
 | 
			
		||||
                tryLoadIconDark(header);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private void tryLoadIconDark(Header header){
 | 
			
		||||
        try{
 | 
			
		||||
            if (header.fragmentArguments != null) {
 | 
			
		||||
                String iconDark = header.fragmentArguments.getString("iconDark");
 | 
			
		||||
                int iconDarkId = getResources().getIdentifier(iconDark, "drawable", getPackageName());
 | 
			
		||||
                if (iconDarkId != 0) {
 | 
			
		||||
                    header.iconRes = iconDarkId;
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        } catch (Exception e) {
 | 
			
		||||
            Log.e("SettingsActivity", "Can not load dark icon", e);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
 
 | 
			
		||||
@@ -2,15 +2,21 @@ package apps.amine.bou.readerforselfoss.utils
 | 
			
		||||
 | 
			
		||||
import android.content.Context
 | 
			
		||||
import android.preference.PreferenceManager
 | 
			
		||||
import android.provider.Settings
 | 
			
		||||
import org.acra.ErrorReporter
 | 
			
		||||
 | 
			
		||||
fun ErrorReporter.maybeHandleSilentException(throwable: Throwable, ctx: Context) {
 | 
			
		||||
    val sharedPref = PreferenceManager.getDefaultSharedPreferences(ctx)
 | 
			
		||||
    if (sharedPref.getBoolean("acra_should_log", false)) {
 | 
			
		||||
    val isTestLab = Settings.System.getString(ctx.contentResolver, "firebase.test.lab") ==  "true"
 | 
			
		||||
 | 
			
		||||
    if (sharedPref.getBoolean("acra_should_log", false) && !isTestLab) {
 | 
			
		||||
        this.handleSilentException(throwable)
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
fun ErrorReporter.doHandleSilentException(throwable: Throwable) {
 | 
			
		||||
    this.handleSilentException(throwable)
 | 
			
		||||
fun ErrorReporter.doHandleSilentException(throwable: Throwable, ctx: Context) {
 | 
			
		||||
    val isTestLab = Settings.System.getString(ctx.contentResolver, "firebase.test.lab") ==  "true"
 | 
			
		||||
    if (!isTestLab) {
 | 
			
		||||
        this.handleSilentException(throwable)
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -2,6 +2,7 @@ package apps.amine.bou.readerforselfoss.utils
 | 
			
		||||
 | 
			
		||||
import android.app.Activity
 | 
			
		||||
import android.app.PendingIntent
 | 
			
		||||
import android.content.ActivityNotFoundException
 | 
			
		||||
import android.content.Context
 | 
			
		||||
import android.content.Intent
 | 
			
		||||
import android.graphics.BitmapFactory
 | 
			
		||||
@@ -129,13 +130,17 @@ fun Context.openItemUrl(
 | 
			
		||||
private fun openInBrowser(linkDecoded: String, app: Activity) {
 | 
			
		||||
    val intent = Intent(Intent.ACTION_VIEW)
 | 
			
		||||
    intent.data = Uri.parse(linkDecoded)
 | 
			
		||||
    app.startActivity(intent)
 | 
			
		||||
    try {
 | 
			
		||||
        app.startActivity(intent)
 | 
			
		||||
    } catch (e: ActivityNotFoundException) {
 | 
			
		||||
        Toast.makeText(app.baseContext, e.message, Toast.LENGTH_LONG).show()
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
fun String.isUrlValid(): Boolean =
 | 
			
		||||
    HttpUrl.parse(this) != null && Patterns.WEB_URL.matcher(this).matches()
 | 
			
		||||
 | 
			
		||||
fun String.isBaseUrlValid(logErrors: Boolean): Boolean {
 | 
			
		||||
fun String.isBaseUrlValid(logErrors: Boolean, ctx: Context): Boolean {
 | 
			
		||||
    val baseUrl = HttpUrl.parse(this)
 | 
			
		||||
    var existsAndEndsWithSlash = false
 | 
			
		||||
    if (baseUrl != null) {
 | 
			
		||||
@@ -143,11 +148,7 @@ fun String.isBaseUrlValid(logErrors: Boolean): Boolean {
 | 
			
		||||
        existsAndEndsWithSlash = "" == pathSegments[pathSegments.size - 1]
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    val isValid = Patterns.WEB_URL.matcher(this).matches() && existsAndEndsWithSlash
 | 
			
		||||
    if (!isValid && logErrors) {
 | 
			
		||||
        ACRA.getErrorReporter().doHandleSilentException(java.lang.Exception("Patterns.WEB_URL.matcher(this).matches() == ${Patterns.WEB_URL.matcher(this).matches()} && existsAndEndsWithSlash == $existsAndEndsWithSlash && baseUrl.pathSegments() == ${baseUrl?.pathSegments()}"))
 | 
			
		||||
    }
 | 
			
		||||
    return isValid
 | 
			
		||||
    return Patterns.WEB_URL.matcher(this).matches() && existsAndEndsWithSlash
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
fun Context.openInBrowserAsNewTask(i: Item) {
 | 
			
		||||
 
 | 
			
		||||
@@ -2,30 +2,30 @@ package apps.amine.bou.readerforselfoss.utils.glide
 | 
			
		||||
 | 
			
		||||
import android.content.Context
 | 
			
		||||
import android.graphics.Bitmap
 | 
			
		||||
import android.graphics.drawable.Drawable
 | 
			
		||||
import android.util.Base64
 | 
			
		||||
import androidx.core.graphics.drawable.RoundedBitmapDrawableFactory
 | 
			
		||||
import android.widget.ImageView
 | 
			
		||||
import apps.amine.bou.readerforselfoss.utils.Config
 | 
			
		||||
import com.bumptech.glide.Glide
 | 
			
		||||
import com.bumptech.glide.RequestBuilder
 | 
			
		||||
import com.bumptech.glide.RequestManager
 | 
			
		||||
import com.bumptech.glide.load.model.GlideUrl
 | 
			
		||||
import com.bumptech.glide.load.model.LazyHeaders
 | 
			
		||||
import com.bumptech.glide.request.RequestOptions
 | 
			
		||||
import com.bumptech.glide.request.target.BitmapImageViewTarget
 | 
			
		||||
 | 
			
		||||
fun Context.bitmapCenterCrop(url: String, iv: ImageView) =
 | 
			
		||||
fun Context.bitmapCenterCrop(config: Config, url: String, iv: ImageView) =
 | 
			
		||||
    Glide.with(this)
 | 
			
		||||
        .asBitmap()
 | 
			
		||||
        .load(url)
 | 
			
		||||
        .loadMaybeBasicAuth(config, url)
 | 
			
		||||
        .apply(RequestOptions.centerCropTransform())
 | 
			
		||||
        .into(iv)
 | 
			
		||||
 | 
			
		||||
fun Context.bitmapFitCenter(url: String, iv: ImageView) =
 | 
			
		||||
fun Context.circularBitmapDrawable(config: Config, url: String, iv: ImageView) =
 | 
			
		||||
    Glide.with(this)
 | 
			
		||||
        .asBitmap()
 | 
			
		||||
        .load(url)
 | 
			
		||||
        .apply(RequestOptions.fitCenterTransform())
 | 
			
		||||
        .into(iv)
 | 
			
		||||
 | 
			
		||||
fun Context.circularBitmapDrawable(url: String, iv: ImageView) =
 | 
			
		||||
    Glide.with(this)
 | 
			
		||||
        .asBitmap()
 | 
			
		||||
        .load(url)
 | 
			
		||||
        .loadMaybeBasicAuth(config, url)
 | 
			
		||||
        .apply(RequestOptions.centerCropTransform())
 | 
			
		||||
        .into(object : BitmapImageViewTarget(iv) {
 | 
			
		||||
            override fun setResource(resource: Bitmap?) {
 | 
			
		||||
@@ -36,4 +36,24 @@ fun Context.circularBitmapDrawable(url: String, iv: ImageView) =
 | 
			
		||||
                circularBitmapDrawable.isCircular = true
 | 
			
		||||
                iv.setImageDrawable(circularBitmapDrawable)
 | 
			
		||||
            }
 | 
			
		||||
        })
 | 
			
		||||
        })
 | 
			
		||||
 | 
			
		||||
fun RequestBuilder<Bitmap>.loadMaybeBasicAuth(config: Config, url: String): RequestBuilder<Bitmap> {
 | 
			
		||||
    val builder: LazyHeaders.Builder = LazyHeaders.Builder()
 | 
			
		||||
    if (config.httpUserLogin.isNotEmpty() || config.httpUserPassword.isNotEmpty()) {
 | 
			
		||||
        val basicAuth = "Basic " + Base64.encodeToString("${config.httpUserLogin}:${config.httpUserPassword}".toByteArray(), Base64.NO_WRAP)
 | 
			
		||||
        builder.addHeader("Authorization", basicAuth)
 | 
			
		||||
    }
 | 
			
		||||
    val glideUrl = GlideUrl(url, builder.build())
 | 
			
		||||
    return this.load(glideUrl)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
fun RequestManager.loadMaybeBasicAuth(config: Config, url: String): RequestBuilder<Drawable> {
 | 
			
		||||
    val builder: LazyHeaders.Builder = LazyHeaders.Builder()
 | 
			
		||||
    if (config.httpUserLogin.isNotEmpty() || config.httpUserPassword.isNotEmpty()) {
 | 
			
		||||
        val basicAuth = "Basic " + Base64.encodeToString("${config.httpUserLogin}:${config.httpUserPassword}".toByteArray(), Base64.NO_WRAP)
 | 
			
		||||
        builder.addHeader("Authorization", basicAuth)
 | 
			
		||||
    }
 | 
			
		||||
    val glideUrl = GlideUrl(url, builder.build())
 | 
			
		||||
    return this.load(glideUrl)
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,9 @@
 | 
			
		||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
 | 
			
		||||
        android:width="24dp"
 | 
			
		||||
        android:height="24dp"
 | 
			
		||||
        android:viewportWidth="24.0"
 | 
			
		||||
        android:viewportHeight="24.0">
 | 
			
		||||
    <path
 | 
			
		||||
        android:fillColor="#FFFFFFFF"
 | 
			
		||||
        android:pathData="M3,21h18v-2L3,19v2zM3,17h18v-2L3,15v2zM3,13h18v-2L3,11v2zM3,9h18L21,7L3,7v2zM3,3v2h18L21,3L3,3z"/>
 | 
			
		||||
</vector>
 | 
			
		||||
@@ -0,0 +1,9 @@
 | 
			
		||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
 | 
			
		||||
        android:width="24dp"
 | 
			
		||||
        android:height="24dp"
 | 
			
		||||
        android:viewportWidth="24.0"
 | 
			
		||||
        android:viewportHeight="24.0">
 | 
			
		||||
    <path
 | 
			
		||||
        android:fillColor="#FFFFFFFF"
 | 
			
		||||
        android:pathData="M15,15L3,15v2h12v-2zM15,7L3,7v2h12L15,7zM3,13h18v-2L3,11v2zM3,21h18v-2L3,19v2zM3,3v2h18L21,3L3,3z"/>
 | 
			
		||||
</vector>
 | 
			
		||||
| 
		 Before Width: | Height: | Size: 683 B  | 
| 
		 Before Width: | Height: | Size: 680 B  | 
| 
		 Before Width: | Height: | Size: 134 B  | 
| 
		 Before Width: | Height: | Size: 239 B  | 
| 
		 Before Width: | Height: | Size: 271 B  | 
| 
		 Before Width: | Height: | Size: 216 B  | 
| 
		 Before Width: | Height: | Size: 206 B  | 
| 
		 Before Width: | Height: | Size: 221 B  | 
| 
		 Before Width: | Height: | Size: 334 B  | 
| 
		 Before Width: | Height: | Size: 458 B  | 
| 
		 Before Width: | Height: | Size: 275 B  | 
| 
		 Before Width: | Height: | Size: 361 B  | 
| 
		 Before Width: | Height: | Size: 324 B  | 
| 
		 Before Width: | Height: | Size: 301 B  | 
| 
		 Before Width: | Height: | Size: 551 B  | 
| 
		 Before Width: | Height: | Size: 355 B  | 
| 
		 Before Width: | Height: | Size: 551 B  | 
| 
		 Before Width: | Height: | Size: 204 B  | 
| 
		 Before Width: | Height: | Size: 187 B  | 
| 
		 Before Width: | Height: | Size: 422 B  | 
| 
		 Before Width: | Height: | Size: 473 B  | 
| 
		 Before Width: | Height: | Size: 498 B  | 
| 
		 Before Width: | Height: | Size: 453 B  | 
| 
		 Before Width: | Height: | Size: 398 B  | 
| 
		 Before Width: | Height: | Size: 397 B  | 
| 
		 Before Width: | Height: | Size: 523 B  | 
| 
		 Before Width: | Height: | Size: 409 B  | 
| 
		 Before Width: | Height: | Size: 442 B  | 
| 
		 Before Width: | Height: | Size: 116 B  | 
| 
		 Before Width: | Height: | Size: 174 B  | 
| 
		 Before Width: | Height: | Size: 212 B  | 
| 
		 Before Width: | Height: | Size: 136 B  | 
| 
		 Before Width: | Height: | Size: 134 B  | 
| 
		 Before Width: | Height: | Size: 175 B  | 
| 
		 Before Width: | Height: | Size: 228 B  | 
| 
		 Before Width: | Height: | Size: 268 B  | 
| 
		 Before Width: | Height: | Size: 213 B  | 
| 
		 Before Width: | Height: | Size: 247 B  | 
| 
		 Before Width: | Height: | Size: 215 B  | 
| 
		 Before Width: | Height: | Size: 208 B  | 
| 
		 Before Width: | Height: | Size: 352 B  | 
| 
		 Before Width: | Height: | Size: 241 B  | 
| 
		 Before Width: | Height: | Size: 355 B  | 
| 
		 Before Width: | Height: | Size: 157 B  | 
| 
		 Before Width: | Height: | Size: 144 B  | 
| 
		 Before Width: | Height: | Size: 276 B  | 
| 
		 Before Width: | Height: | Size: 309 B  | 
| 
		 Before Width: | Height: | Size: 339 B  | 
| 
		 Before Width: | Height: | Size: 322 B  | 
| 
		 Before Width: | Height: | Size: 262 B  | 
| 
		 Before Width: | Height: | Size: 268 B  | 
| 
		 Before Width: | Height: | Size: 361 B  | 
| 
		 Before Width: | Height: | Size: 871 B  | 
| 
		 Before Width: | Height: | Size: 634 B  | 
| 
		 Before Width: | Height: | Size: 168 B  | 
| 
		 Before Width: | Height: | Size: 261 B  | 
| 
		 Before Width: | Height: | Size: 312 B  | 
| 
		 Before Width: | Height: | Size: 171 B  | 
| 
		 Before Width: | Height: | Size: 174 B  | 
| 
		 Before Width: | Height: | Size: 257 B  | 
| 
		 Before Width: | Height: | Size: 380 B  | 
| 
		 Before Width: | Height: | Size: 504 B  | 
| 
		 Before Width: | Height: | Size: 300 B  | 
| 
		 Before Width: | Height: | Size: 437 B  | 
| 
		 Before Width: | Height: | Size: 327 B  | 
| 
		 Before Width: | Height: | Size: 308 B  | 
| 
		 Before Width: | Height: | Size: 684 B  | 
| 
		 Before Width: | Height: | Size: 464 B  | 
| 
		 Before Width: | Height: | Size: 725 B  | 
| 
		 Before Width: | Height: | Size: 230 B  | 
| 
		 Before Width: | Height: | Size: 208 B  | 
| 
		 Before Width: | Height: | Size: 557 B  | 
| 
		 Before Width: | Height: | Size: 625 B  | 
| 
		 Before Width: | Height: | Size: 606 B  | 
| 
		 Before Width: | Height: | Size: 557 B  | 
| 
		 Before Width: | Height: | Size: 483 B  | 
| 
		 Before Width: | Height: | Size: 496 B  | 
| 
		 Before Width: | Height: | Size: 660 B  |