Compare commits
	
		
			24 Commits
		
	
	
		
			v171812344
			...
			v171901015
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 
						 | 
					ddc754ec25 | ||
| 
						 | 
					134a0766d6 | ||
| 
						 | 
					69da932ab5 | ||
| 
						 | 
					592fb6328a | ||
| 
						 | 
					a0aead6491 | ||
| 
						 | 
					722b6cc06d | ||
| 
						 | 
					6d7c4b40f6 | ||
| 
						 | 
					f538ed39fc | ||
| 
						 | 
					65821492ad | ||
| 
						 | 
					6ede718a9f | ||
| 
						 | 
					f1757937a4 | ||
| 2bd2e0a953 | |||
| 
						 | 
					b5aef28af0 | ||
| 
						 | 
					45747a1506 | ||
| 
						 | 
					c6e2e08bcb | ||
| 
						 | 
					25bf18661e | ||
| 
						 | 
					6b088dcd24 | ||
| 
						 | 
					d2b18e1880 | ||
| 
						 | 
					eec7c94e98 | ||
| 
						 | 
					d1f8fcacc0 | ||
| 
						 | 
					07e4a33cbd | ||
| 
						 | 
					f6317f566e | ||
| 
						 | 
					9f51e4e6a5 | ||
| 
						 | 
					750604a31f | 
@@ -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.
 | 
			
		||||
 
 | 
			
		||||
@@ -67,7 +67,7 @@ android {
 | 
			
		||||
    buildTypes {
 | 
			
		||||
        release {
 | 
			
		||||
            minifyEnabled true
 | 
			
		||||
            shrinkResources true
 | 
			
		||||
            shrinkResources false
 | 
			
		||||
            proguardFiles getDefaultProguardFile('proguard-android.txt'),
 | 
			
		||||
                    'proguard-rules.pro'
 | 
			
		||||
        }
 | 
			
		||||
@@ -112,7 +112,7 @@ dependencies {
 | 
			
		||||
    implementation 'androidx.constraintlayout:constraintlayout:2.0.0-alpha2'
 | 
			
		||||
 | 
			
		||||
    //multidex
 | 
			
		||||
    implementation 'androidx.multidex:multidex:2.0.0'
 | 
			
		||||
    implementation 'androidx.multidex:multidex:2.0.1'
 | 
			
		||||
 | 
			
		||||
    // About
 | 
			
		||||
    implementation('com.mikepenz:aboutlibraries:6.2.0@aar') {
 | 
			
		||||
 
 | 
			
		||||
@@ -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>
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +1,6 @@
 | 
			
		||||
package apps.amine.bou.readerforselfoss
 | 
			
		||||
 | 
			
		||||
import android.content.Context
 | 
			
		||||
import android.content.Intent
 | 
			
		||||
import android.os.Build
 | 
			
		||||
import android.os.Bundle
 | 
			
		||||
@@ -85,10 +86,12 @@ class AddSourceActivity : AppCompatActivity() {
 | 
			
		||||
 | 
			
		||||
        try {
 | 
			
		||||
            val prefs = PreferenceManager.getDefaultSharedPreferences(this)
 | 
			
		||||
            val settings =
 | 
			
		||||
                getSharedPreferences(Config.settingsName, Context.MODE_PRIVATE)
 | 
			
		||||
            api = SelfossApi(
 | 
			
		||||
                this,
 | 
			
		||||
                this@AddSourceActivity,
 | 
			
		||||
                prefs.getBoolean("isSelfSignedCert", false),
 | 
			
		||||
                settings.getBoolean("isSelfSignedCert", false),
 | 
			
		||||
                prefs.getString("api_timeout", "-1").toLong(),
 | 
			
		||||
                prefs.getBoolean("should_log_everything", false)
 | 
			
		||||
            )
 | 
			
		||||
 
 | 
			
		||||
@@ -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(
 | 
			
		||||
@@ -250,14 +247,18 @@ class HomeActivity : AppCompatActivity(), SearchView.OnQueryTextListener {
 | 
			
		||||
                    val i = items.elementAtOrNull(position)
 | 
			
		||||
 | 
			
		||||
                    if (i != null) {
 | 
			
		||||
                        val adapter = recyclerView.adapter
 | 
			
		||||
                        val adapter = recyclerView.adapter as ItemsAdapter<*>
 | 
			
		||||
 | 
			
		||||
                        when (adapter) {
 | 
			
		||||
                            is ItemCardAdapter -> adapter.removeItemAtIndex(position)
 | 
			
		||||
                            is ItemListAdapter -> adapter.removeItemAtIndex(position)
 | 
			
		||||
                        val wasItemUnread = adapter.unreadItemStatusAtIndex(position)
 | 
			
		||||
 | 
			
		||||
                        adapter.handleItemAtIndex(position)
 | 
			
		||||
 | 
			
		||||
                        if (wasItemUnread) {
 | 
			
		||||
                            badgeNew--
 | 
			
		||||
                        } else {
 | 
			
		||||
                            badgeNew++
 | 
			
		||||
                        }
 | 
			
		||||
 | 
			
		||||
                        badgeNew--
 | 
			
		||||
                        reloadBadgeContent()
 | 
			
		||||
 | 
			
		||||
                        val tagHashes = i.tags.tags.split(",").map { it.longHash() }
 | 
			
		||||
@@ -304,19 +305,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 +479,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 +489,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 +532,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 +547,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 +583,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 +598,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 +689,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 +1145,8 @@ class HomeActivity : AppCompatActivity(), SearchView.OnQueryTextListener {
 | 
			
		||||
                            fullHeightCards,
 | 
			
		||||
                            appColors,
 | 
			
		||||
                            debugReadingItems,
 | 
			
		||||
                            userIdentifier
 | 
			
		||||
                            userIdentifier,
 | 
			
		||||
                            config
 | 
			
		||||
                        ) {
 | 
			
		||||
                            updateItems(it)
 | 
			
		||||
                        }
 | 
			
		||||
@@ -1152,7 +1162,8 @@ class HomeActivity : AppCompatActivity(), SearchView.OnQueryTextListener {
 | 
			
		||||
                            articleViewer,
 | 
			
		||||
                            debugReadingItems,
 | 
			
		||||
                            userIdentifier,
 | 
			
		||||
                            appColors
 | 
			
		||||
                            appColors,
 | 
			
		||||
                            config
 | 
			
		||||
                        ) {
 | 
			
		||||
                            updateItems(it)
 | 
			
		||||
                        }
 | 
			
		||||
@@ -1341,12 +1352,7 @@ class HomeActivity : AppCompatActivity(), SearchView.OnQueryTextListener {
 | 
			
		||||
                                        ).show()
 | 
			
		||||
                                        tabNewBadge.removeBadge()
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
                                        tagsBadge = itemsByTag.map {
 | 
			
		||||
                                            (it.key to ((tagsBadge[it.key] ?: it.value) - it.value))
 | 
			
		||||
                                        }.toMap()
 | 
			
		||||
 | 
			
		||||
                                        reloadTagsBadges()
 | 
			
		||||
                                        handleDrawerItems()
 | 
			
		||||
 | 
			
		||||
                                        getElementsAccordingToTab()
 | 
			
		||||
                                    } 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
 | 
			
		||||
@@ -42,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)
 | 
			
		||||
@@ -105,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.Context
 | 
			
		||||
import android.content.SharedPreferences
 | 
			
		||||
import android.graphics.drawable.ColorDrawable
 | 
			
		||||
import android.os.Build
 | 
			
		||||
@@ -27,6 +28,7 @@ import apps.amine.bou.readerforselfoss.persistence.migrations.MIGRATION_2_3
 | 
			
		||||
import apps.amine.bou.readerforselfoss.themes.AppColors
 | 
			
		||||
import apps.amine.bou.readerforselfoss.themes.Toppings
 | 
			
		||||
import apps.amine.bou.readerforselfoss.transformers.DepthPageTransformer
 | 
			
		||||
import apps.amine.bou.readerforselfoss.utils.Config
 | 
			
		||||
import apps.amine.bou.readerforselfoss.utils.maybeHandleSilentException
 | 
			
		||||
import apps.amine.bou.readerforselfoss.utils.network.isNetworkAccessible
 | 
			
		||||
import apps.amine.bou.readerforselfoss.utils.persistence.toEntity
 | 
			
		||||
@@ -94,6 +96,9 @@ class ReaderActivity : AppCompatActivity() {
 | 
			
		||||
        supportActionBar?.setDisplayHomeAsUpEnabled(true)
 | 
			
		||||
        supportActionBar?.setDisplayShowHomeEnabled(true)
 | 
			
		||||
 | 
			
		||||
        val settings =
 | 
			
		||||
            getSharedPreferences(Config.settingsName, Context.MODE_PRIVATE)
 | 
			
		||||
 | 
			
		||||
        prefs = PreferenceManager.getDefaultSharedPreferences(this)
 | 
			
		||||
        editor = prefs.edit()
 | 
			
		||||
 | 
			
		||||
@@ -105,7 +110,7 @@ class ReaderActivity : AppCompatActivity() {
 | 
			
		||||
        api = SelfossApi(
 | 
			
		||||
            this,
 | 
			
		||||
            this@ReaderActivity,
 | 
			
		||||
            prefs.getBoolean("isSelfSignedCert", false),
 | 
			
		||||
            settings.getBoolean("isSelfSignedCert", false),
 | 
			
		||||
            prefs.getString("api_timeout", "-1").toLong(),
 | 
			
		||||
            prefs.getBoolean("should_log_everything", false)
 | 
			
		||||
        )
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +1,6 @@
 | 
			
		||||
package apps.amine.bou.readerforselfoss
 | 
			
		||||
 | 
			
		||||
import android.content.Context
 | 
			
		||||
import android.content.Intent
 | 
			
		||||
import android.content.res.ColorStateList
 | 
			
		||||
import android.os.Build
 | 
			
		||||
@@ -13,6 +14,7 @@ import apps.amine.bou.readerforselfoss.api.selfoss.SelfossApi
 | 
			
		||||
import apps.amine.bou.readerforselfoss.api.selfoss.Source
 | 
			
		||||
import apps.amine.bou.readerforselfoss.themes.AppColors
 | 
			
		||||
import apps.amine.bou.readerforselfoss.themes.Toppings
 | 
			
		||||
import apps.amine.bou.readerforselfoss.utils.Config
 | 
			
		||||
import apps.amine.bou.readerforselfoss.utils.network.isNetworkAccessible
 | 
			
		||||
import com.ftinc.scoop.Scoop
 | 
			
		||||
import kotlinx.android.synthetic.main.activity_sources.*
 | 
			
		||||
@@ -54,12 +56,14 @@ class SourcesActivity : AppCompatActivity() {
 | 
			
		||||
        super.onResume()
 | 
			
		||||
        val mLayoutManager = LinearLayoutManager(this)
 | 
			
		||||
 | 
			
		||||
        val settings =
 | 
			
		||||
            getSharedPreferences(Config.settingsName, Context.MODE_PRIVATE)
 | 
			
		||||
        val prefs = PreferenceManager.getDefaultSharedPreferences(this)
 | 
			
		||||
 | 
			
		||||
        val api = SelfossApi(
 | 
			
		||||
            this,
 | 
			
		||||
            this@SourcesActivity,
 | 
			
		||||
            prefs.getBoolean("isSelfSignedCert", false),
 | 
			
		||||
            settings.getBoolean("isSelfSignedCert", false),
 | 
			
		||||
            prefs.getString("api_timeout", "-1").toLong(),
 | 
			
		||||
            prefs.getBoolean("should_log_everything", false)
 | 
			
		||||
        )
 | 
			
		||||
 
 | 
			
		||||
@@ -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,64 @@ 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) {
 | 
			
		||||
        if (unreadItemStatusAtIndex(position)) {
 | 
			
		||||
            readItemAtIndex(position)
 | 
			
		||||
        } else {
 | 
			
		||||
            unreadItemAtIndex(position)
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fun unreadItemStatusAtIndex(position: Int): Boolean {
 | 
			
		||||
        return items[position].unread
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private fun readItemAtIndex(position: Int) {
 | 
			
		||||
        val i = items[position]
 | 
			
		||||
        items.remove(i)
 | 
			
		||||
        notifyItemRemoved(position)
 | 
			
		||||
@@ -103,7 +161,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 +172,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 +185,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 +197,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,6 +3,8 @@ 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
 | 
			
		||||
@@ -19,6 +21,7 @@ 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
 | 
			
		||||
@@ -34,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
 | 
			
		||||
@@ -44,6 +48,7 @@ import apps.amine.bou.readerforselfoss.utils.succeeded
 | 
			
		||||
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
 | 
			
		||||
@@ -68,6 +73,15 @@ class ArticleFragment : Fragment() {
 | 
			
		||||
    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()
 | 
			
		||||
@@ -78,6 +92,7 @@ class ArticleFragment : Fragment() {
 | 
			
		||||
 | 
			
		||||
    override fun onCreate(savedInstanceState: Bundle?) {
 | 
			
		||||
        appColors = AppColors(activity!!)
 | 
			
		||||
        config = Config(activity!!)
 | 
			
		||||
 | 
			
		||||
        super.onCreate(savedInstanceState)
 | 
			
		||||
 | 
			
		||||
@@ -90,10 +105,6 @@ class ArticleFragment : Fragment() {
 | 
			
		||||
        ).addMigrations(MIGRATION_1_2).addMigrations(MIGRATION_2_3).build()
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private var rootView: ViewGroup? = null
 | 
			
		||||
 | 
			
		||||
    private lateinit var prefs: SharedPreferences
 | 
			
		||||
 | 
			
		||||
    override fun onCreateView(
 | 
			
		||||
        inflater: LayoutInflater,
 | 
			
		||||
        container: ViewGroup?,
 | 
			
		||||
@@ -112,6 +123,19 @@ class ArticleFragment : Fragment() {
 | 
			
		||||
            prefs = PreferenceManager.getDefaultSharedPreferences(activity)
 | 
			
		||||
            editor = prefs.edit()
 | 
			
		||||
            fontSize = prefs.getString("reader_font_size", "16").toInt()
 | 
			
		||||
 | 
			
		||||
            font = prefs.getString("reader_font", "")
 | 
			
		||||
            if (font.isNotEmpty()) {
 | 
			
		||||
                resId = context!!.resources.getIdentifier(font, "font", context!!.packageName)
 | 
			
		||||
                typeface = try {
 | 
			
		||||
                    ResourcesCompat.getFont(context!!, resId)!!
 | 
			
		||||
                } catch (e: java.lang.Exception) {
 | 
			
		||||
                    ACRA.getErrorReporter().maybeHandleSilentException(Throwable("Font loading issue: ${e.message}"), context!!)
 | 
			
		||||
                    // Just to be sure
 | 
			
		||||
                    null
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            refreshAlignment()
 | 
			
		||||
 | 
			
		||||
            val settings = activity!!.getSharedPreferences(Config.settingsName, Context.MODE_PRIVATE)
 | 
			
		||||
@@ -201,11 +225,17 @@ class ArticleFragment : Fragment() {
 | 
			
		||||
            )
 | 
			
		||||
 | 
			
		||||
            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()
 | 
			
		||||
 | 
			
		||||
@@ -214,7 +244,7 @@ class ArticleFragment : Fragment() {
 | 
			
		||||
                    Glide
 | 
			
		||||
                        .with(context!!)
 | 
			
		||||
                        .asBitmap()
 | 
			
		||||
                        .load(contentImage)
 | 
			
		||||
                        .loadMaybeBasicAuth(config, contentImage)
 | 
			
		||||
                        .apply(RequestOptions.fitCenterTransform())
 | 
			
		||||
                        .into(rootView!!.imageView)
 | 
			
		||||
                } else {
 | 
			
		||||
@@ -280,6 +310,9 @@ class ArticleFragment : Fragment() {
 | 
			
		||||
                            if (response.body() != null && response.body()!!.content != null && !response.body()!!.content.isNullOrEmpty()) {
 | 
			
		||||
                                try {
 | 
			
		||||
                                    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)
 | 
			
		||||
@@ -309,7 +342,7 @@ class ArticleFragment : Fragment() {
 | 
			
		||||
                                            Glide
 | 
			
		||||
                                                .with(context!!)
 | 
			
		||||
                                                .asBitmap()
 | 
			
		||||
                                                .load(response.body()!!.lead_image_url)
 | 
			
		||||
                                                .loadMaybeBasicAuth(config, response.body()!!.lead_image_url.orEmpty())
 | 
			
		||||
                                                .apply(RequestOptions.fitCenterTransform())
 | 
			
		||||
                                                .into(rootView!!.imageView)
 | 
			
		||||
                                        } catch (e: Exception) {
 | 
			
		||||
@@ -361,6 +394,11 @@ class ArticleFragment : Fragment() {
 | 
			
		||||
    private fun htmlToWebview() {
 | 
			
		||||
        val stringColor = String.format("#%06X", 0xFFFFFF and appColors.colorAccent)
 | 
			
		||||
 | 
			
		||||
        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) {
 | 
			
		||||
@@ -420,6 +458,24 @@ class ArticleFragment : Fragment() {
 | 
			
		||||
            ACRA.getErrorReporter().maybeHandleSilentException(e, activity!!)
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        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>
 | 
			
		||||
@@ -443,6 +499,7 @@ class ArticleFragment : Fragment() {
 | 
			
		||||
                |        text-align: $textAlignment;
 | 
			
		||||
                |        word-break: break-word;
 | 
			
		||||
                |        overflow:hidden;
 | 
			
		||||
                |        line-height: 1.5em;
 | 
			
		||||
                |      }
 | 
			
		||||
                |      a, pre, code {
 | 
			
		||||
                |        text-align: $textAlignment;
 | 
			
		||||
@@ -453,6 +510,7 @@ class ArticleFragment : Fragment() {
 | 
			
		||||
                |        background-color: $stringBackgroundColor;
 | 
			
		||||
                |      }
 | 
			
		||||
                |   </style>
 | 
			
		||||
                |   $fontLinkAndStyle
 | 
			
		||||
                |</head>
 | 
			
		||||
                |<body>
 | 
			
		||||
                |   $contentText
 | 
			
		||||
 
 | 
			
		||||
@@ -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,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)
 | 
			
		||||
}
 | 
			
		||||
| 
		 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  | 
| 
		 Before Width: | Height: | Size: 1.3 KiB  | 
| 
		 Before Width: | Height: | Size: 1.2 KiB  | 
| 
		 Before Width: | Height: | Size: 242 B  | 
| 
		 Before Width: | Height: | Size: 377 B  | 
| 
		 Before Width: | Height: | Size: 466 B  | 
| 
		 Before Width: | Height: | Size: 263 B  |