Compare commits

..

34 Commits

Author SHA1 Message Date
1485cc05f4 Fixing dark background webview. 2019-04-06 12:50:30 +02:00
d1dad3e61a Cleaning and update. 2019-04-06 12:42:25 +02:00
e5024b0420 Update README.md 2019-04-04 10:17:51 +02:00
9b01692c55 Update README.md 2019-03-16 19:19:29 +01:00
33aa587d36 Update index.html 2019-03-16 11:26:57 +01:00
12e0766803 No more playstore. 2019-03-16 09:06:06 +01:00
a8721ad7a4 Should intercept socket timeout errors. 2019-01-30 21:11:24 +01:00
bc5e882894 New translations strings.xml (Chinese Traditional) (#280) 2019-01-29 08:39:28 +01:00
e3460322b1 Closes #279. 2019-01-27 13:59:28 +01:00
7e3288a076 For now not sending issue report for #278. 2019-01-27 13:47:41 +01:00
ddc754ec25 Closes #270. 2019-01-15 19:54:46 +01:00
134a0766d6 Updates. 2019-01-14 20:56:12 +01:00
69da932ab5 Just to be sure. 2019-01-14 19:39:54 +01:00
592fb6328a Workaround for #275. 2019-01-14 19:37:43 +01:00
a0aead6491 Closes #271. 2019-01-13 15:59:38 +01:00
722b6cc06d Fixed issue with read/unread cont when swiping. 2019-01-13 15:59:38 +01:00
6d7c4b40f6 Wip fixing the badges reloading when reading all the items. 2019-01-13 15:59:38 +01:00
f538ed39fc New translations strings.xml (Chinese Traditional) (#274) 2019-01-11 14:20:35 +01:00
65821492ad New Crowdin translations (#273)
* New translations strings.xml (Catalan)

* New translations strings.xml (Chinese Simplified)

* New translations strings.xml (Chinese Traditional)

* New translations strings.xml (Dutch)

* New translations strings.xml (French)

* New translations strings.xml (Galician)

* New translations strings.xml (German)

* New translations strings.xml (Indonesian)

* New translations strings.xml (Italian)

* New translations strings.xml (Korean)

* New translations strings.xml (Portuguese)

* New translations strings.xml (Portuguese, Brazilian)

* New translations strings.xml (Spanish)

* New translations strings.xml (Turkish)

* New translations strings.xml (Galician)

* New translations strings.xml (Spanish)

* New translations strings.xml (French)
2019-01-10 11:19:46 +01:00
6ede718a9f Closes #269. 2019-01-09 21:28:55 +01:00
f1757937a4 Disabled resources shrinking after #262. 2019-01-09 13:33:02 +01:00
2bd2e0a953 Vector assets (#262) 2019-01-09 10:43:49 +01:00
b5aef28af0 New Crowdin translations (#268)
* New translations strings.xml (Galician)

* New translations strings.xml (Spanish)
2019-01-08 10:00:28 +01:00
45747a1506 New Crowdin translations (#267)
* New translations strings.xml (Catalan)

* New translations strings.xml (Chinese Simplified)

* New translations strings.xml (Chinese Traditional)

* New translations strings.xml (Dutch)

* New translations strings.xml (French)

* New translations strings.xml (Galician)

* New translations strings.xml (German)

* New translations strings.xml (Indonesian)

* New translations strings.xml (Italian)

* New translations strings.xml (Korean)

* New translations strings.xml (Portuguese)

* New translations strings.xml (Portuguese, Brazilian)

* New translations strings.xml (Spanish)

* New translations strings.xml (Turkish)

* New translations strings.xml (French)
2019-01-06 18:46:38 +01:00
c6e2e08bcb Merge pull request #265 from aminecmi/feature/261
WIP: Webview font
2019-01-06 18:25:26 +01:00
25bf18661e Adde a bigger line-height. 2019-01-06 18:17:10 +01:00
6b088dcd24 Font family. 2019-01-06 18:12:18 +01:00
d2b18e1880 Removed lazy loaded fonts. 2019-01-06 16:26:40 +01:00
eec7c94e98 WIP. 2019-01-06 16:16:35 +01:00
d1f8fcacc0 Changelog. 2019-01-05 21:47:45 +01:00
07e4a33cbd Closes #266. 2019-01-05 21:43:09 +01:00
f6317f566e Same change for hidden tags. 2019-01-02 21:30:26 +01:00
9f51e4e6a5 Closes #264 2019-01-02 21:21:32 +01:00
750604a31f Languages cleaning. (#260) 2018-12-12 21:49:38 +01:00
233 changed files with 752 additions and 3204 deletions

View File

@ -1,5 +1,13 @@
**1.7.x** **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. - Added experimental issue to set a default timeout. Should work for #238.
- Closing #220. - Closing #220.

View File

@ -1,20 +1,10 @@
# ReaderForSelfoss # ReaderForSelfoss **(Only available from F-Droid)**
[![Slack Channel](https://img.shields.io/badge/chat-slack-green.svg)](https://join.slack.com/t/readerforselfoss/shared_invite/enQtMjkyNzc3NjM2Mjc1LTUzZTZhOGM5YjQ1MTI5MWZiODRjMjE1ZDBmMzQxZmQ3NWZhYTNhMTBjNGEwNmE2ZGFjODU5NjUxZjBkMWJmMDQ) [![Build Status](https://jenkins.amine-bou.fr/job/ReaderForSelfoss/badge/icon)](https://jenkins.amine-bou.fr/job/ReaderForSelfoss/) [![Code Triagers Badge](https://www.codetriage.com/aminecmi/readerforselfoss/badges/users.svg)](https://www.codetriage.com/aminecmi/readerforselfoss) [![Crowdin](https://d322cqt584bo4o.cloudfront.net/readerforselfoss/localized.svg)](https://crowdin.com/project/readerforselfoss) [![Crowdin](https://d322cqt584bo4o.cloudfront.net/readerforselfoss/localized.svg)](https://crowdin.com/project/readerforselfoss)
It's an RSS Reader for Android, that **only** works with [Selfoss](https://selfoss.aditu.de/) It's an RSS Reader for Android, that **only** works with [Selfoss](https://selfoss.aditu.de/)
<a href='https://play.google.com/store/apps/details?id=apps.amine.bou.readerforselfoss'><img alt='Get it on Google Play' src='https://play.google.com/intl/en_us/badges/images/generic/en_badge_web_generic.png' height="100"/></a> <a href="https://f-droid.org/packages/apps.amine.bou.readerforselfoss"><img src="https://f-droid.org/badge/get-it-on.png" alt="Get it on F-Droid" height="100"></a> <a href="https://f-droid.org/packages/apps.amine.bou.readerforselfoss"><img src="https://f-droid.org/badge/get-it-on.png" alt="Get it on F-Droid" height="100"></a>
Also, the last APK built from source is available [here](https://jenkins.amine-bou.fr/job/ReaderForSelfoss/lastSuccessfulBuild/artifact/SignApksBuilder-out/selfoss-key/selfoss/app-githubConfig-release-unsigned.apk/app-githubConfig-release.apk).
## Join the alpha channel
**Keep in mind, it could be instable, but you'll have the new updates faster**
- First, join the google [group](https://groups.google.com/d/forum/reader-for-selfoss-alpha-testing).
- Then, join the [alpha channel](https://play.google.com/apps/testing/apps.amine.bou.readerforselfoss) of the app.
- You'll be able to update the app for the current alpha version.
## Want to help ? ## Want to help ?
@ -30,4 +20,3 @@ Also, the last APK built from source is available [here](https://jenkins.amine-b
- [See what I'm doing](https://github.com/aminecmi/ReaderforSelfoss/projects/1) - [See what I'm doing](https://github.com/aminecmi/ReaderforSelfoss/projects/1)
- [Create an issue, or request a new feature](https://github.com/aminecmi/ReaderforSelfoss/issues) - [Create an issue, or request a new feature](https://github.com/aminecmi/ReaderforSelfoss/issues)
- [Help translation the app](https://crowdin.com/project/readerforselfoss) - [Help translation the app](https://crowdin.com/project/readerforselfoss)
- [Ask for help](https://join.slack.com/t/readerforselfoss/shared_invite/enQtMjkyNzc3NjM2Mjc1LTUzZTZhOGM5YjQ1MTI5MWZiODRjMjE1ZDBmMzQxZmQ3NWZhYTNhMTBjNGEwNmE2ZGFjODU5NjUxZjBkMWJmMDQ)

View File

@ -67,14 +67,11 @@ android {
buildTypes { buildTypes {
release { release {
minifyEnabled true minifyEnabled true
shrinkResources true shrinkResources false
proguardFiles getDefaultProguardFile('proguard-android.txt'), proguardFiles getDefaultProguardFile('proguard-android.txt'),
'proguard-rules.pro' 'proguard-rules.pro'
} }
debug { debug {
buildConfigField "String", "LOGIN_URL", appLoginUrl
buildConfigField "String", "LOGIN_USERNAME", appLoginUsername
buildConfigField "String", "LOGIN_PASSWORD", appLoginPassword
} }
} }
flavorDimensions "build" flavorDimensions "build"
@ -83,11 +80,6 @@ android {
versionNameSuffix '-github' versionNameSuffix '-github'
dimension "build" dimension "build"
} }
storeConfig {
// As jenkins publishes to alpha first, this is the default suffix now.
versionNameSuffix '-store'
dimension "build"
}
} }
} }
@ -102,17 +94,17 @@ dependencies {
implementation fileTree(include: ['*.jar'], dir: 'libs') implementation fileTree(include: ['*.jar'], dir: 'libs')
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
// Android Support // Android Support
implementation "androidx.appcompat:appcompat:$android_version" implementation "androidx.appcompat:appcompat:$androidx_version"
implementation "com.google.android.material:material:$android_version" implementation "com.google.android.material:material:$android_version"
implementation "androidx.recyclerview:recyclerview:$android_version" implementation "androidx.recyclerview:recyclerview:$android_version"
implementation "androidx.legacy:legacy-support-v4:$android_version" implementation "androidx.legacy:legacy-support-v4:$android_version"
implementation "androidx.vectordrawable:vectordrawable:$android_version" implementation "androidx.vectordrawable:vectordrawable:$android_version"
implementation "androidx.browser:browser:$android_version" implementation "androidx.browser:browser:$android_version"
implementation "androidx.cardview:cardview:$android_version" implementation "androidx.cardview:cardview:$android_version"
implementation 'androidx.constraintlayout:constraintlayout:2.0.0-alpha2' implementation 'androidx.constraintlayout:constraintlayout:2.0.0-alpha4'
//multidex //multidex
implementation 'androidx.multidex:multidex:2.0.0' implementation 'androidx.multidex:multidex:2.0.1'
// About // About
implementation('com.mikepenz:aboutlibraries:6.2.0@aar') { implementation('com.mikepenz:aboutlibraries:6.2.0@aar') {
@ -134,9 +126,6 @@ dependencies {
implementation 'com.github.bumptech.glide:glide:4.1.1' implementation 'com.github.bumptech.glide:glide:4.1.1'
implementation 'com.github.bumptech.glide:okhttp3-integration:4.1.1' implementation 'com.github.bumptech.glide:okhttp3-integration:4.1.1'
// Asking politely users to rate the app
implementation 'com.github.stkent:amplify:2.2.0'
// Drawer // Drawer
implementation 'co.zsmb:materialdrawer-kt:2.0.1' implementation 'co.zsmb:materialdrawer-kt:2.0.1'
@ -161,21 +150,4 @@ dependencies {
kapt "androidx.room:room-compiler:$room_version" kapt "androidx.room:room-compiler:$room_version"
implementation "android.arch.work:work-runtime-ktx:$work_version" implementation "android.arch.work:work-runtime-ktx:$work_version"
} }
afterEvaluate {
initAppLoginPropertiesIfNeeded()
}
def initAppLoginPropertiesIfNeeded() {
def propertiesFile = file(System.getProperty("user.home") + '/.gradle/gradle.properties')
if (!propertiesFile.exists()) {
def commentMessage = "This is autogenerated local property from system environment to prevent key to be committed to source control."
ant.propertyfile(file: System.getProperty("user.home") + "/.gradle/gradle.properties", comment: commentMessage) {
entry(key: "appLoginUrl", value: System.getProperty("appLoginUrl"))
entry(key: "appLoginUsername", value: System.getProperty("appLoginUsername"))
entry(key: "appLoginPassword", value: System.getProperty("appLoginPassword"))
}
}
}

View File

@ -1,7 +1,6 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android" <manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="apps.amine.bou.readerforselfoss" package="apps.amine.bou.readerforselfoss">
xmlns:tools="http://schemas.android.com/tools">
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" /> <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.INTERNET" /> <uses-permission android:name="android.permission.INTERNET" />
@ -19,11 +18,11 @@
android:theme="@style/SplashTheme"> android:theme="@style/SplashTheme">
<intent-filter> <intent-filter>
<action android:name="android.intent.action.MAIN" /> <action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" /> <category android:name="android.intent.category.LAUNCHER" />
</intent-filter> </intent-filter>
<meta-data android:name="android.app.shortcuts" <meta-data
android:name="android.app.shortcuts"
android:resource="@xml/shortcuts" /> android:resource="@xml/shortcuts" />
</activity> </activity>
<activity <activity
@ -38,7 +37,7 @@
android:parentActivityName=".HomeActivity"> android:parentActivityName=".HomeActivity">
<meta-data <meta-data
android:name="android.support.PARENT_ACTIVITY" android:name="android.support.PARENT_ACTIVITY"
android:value="apps.amine.bou.readerforselfoss.HomeActivity" /> android:value=".HomeActivity" />
</activity> </activity>
<activity <activity
android:name=".SourcesActivity" android:name=".SourcesActivity"
@ -56,9 +55,7 @@
<intent-filter> <intent-filter>
<action android:name="android.intent.action.SEND" /> <action android:name="android.intent.action.SEND" />
<category android:name="android.intent.category.DEFAULT" /> <category android:name="android.intent.category.DEFAULT" />
<data android:mimeType="text/plain" /> <data android:mimeType="text/plain" />
</intent-filter> </intent-filter>
</activity> </activity>
@ -77,6 +74,9 @@
android:value="true" /> android:value="true" />
<meta-data android:name="android.max_aspect" android:value="2.1" /> <meta-data android:name="android.max_aspect" android:value="2.1" />
<meta-data
android:name="preloaded_fonts"
android:resource="@array/preloaded_fonts" />
</application> </application>
</manifest> </manifest>

View File

@ -1,5 +1,6 @@
package apps.amine.bou.readerforselfoss package apps.amine.bou.readerforselfoss
import android.content.Context
import android.content.Intent import android.content.Intent
import android.os.Build import android.os.Build
import android.os.Bundle import android.os.Bundle
@ -85,10 +86,12 @@ class AddSourceActivity : AppCompatActivity() {
try { try {
val prefs = PreferenceManager.getDefaultSharedPreferences(this) val prefs = PreferenceManager.getDefaultSharedPreferences(this)
val settings =
getSharedPreferences(Config.settingsName, Context.MODE_PRIVATE)
api = SelfossApi( api = SelfossApi(
this, this,
this@AddSourceActivity, this@AddSourceActivity,
prefs.getBoolean("isSelfSignedCert", false), settings.getBoolean("isSelfSignedCert", false),
prefs.getString("api_timeout", "-1").toLong(), prefs.getString("api_timeout", "-1").toLong(),
prefs.getBoolean("should_log_everything", false) prefs.getBoolean("should_log_everything", false)
) )

View File

@ -18,17 +18,13 @@ import androidx.recyclerview.widget.RecyclerView
import androidx.appcompat.widget.SearchView import androidx.appcompat.widget.SearchView
import androidx.recyclerview.widget.StaggeredGridLayoutManager import androidx.recyclerview.widget.StaggeredGridLayoutManager
import androidx.recyclerview.widget.ItemTouchHelper import androidx.recyclerview.widget.ItemTouchHelper
import android.util.Log
import android.view.Menu import android.view.Menu
import android.view.MenuItem import android.view.MenuItem
import android.view.View import android.view.View
import android.widget.Toast import android.widget.Toast
import androidx.room.Room import androidx.room.Room
import androidx.room.RoomDatabase
import androidx.work.Constraints import androidx.work.Constraints
import androidx.work.ExistingPeriodicWorkPolicy import androidx.work.ExistingPeriodicWorkPolicy
import androidx.work.NetworkType
import androidx.work.OneTimeWorkRequestBuilder
import androidx.work.PeriodicWorkRequestBuilder import androidx.work.PeriodicWorkRequestBuilder
import androidx.work.WorkManager import androidx.work.WorkManager
import apps.amine.bou.readerforselfoss.adapters.ItemCardAdapter import apps.amine.bou.readerforselfoss.adapters.ItemCardAdapter
@ -68,8 +64,6 @@ import com.ashokvarma.bottomnavigation.BottomNavigationBar
import com.ashokvarma.bottomnavigation.BottomNavigationItem import com.ashokvarma.bottomnavigation.BottomNavigationItem
import com.ashokvarma.bottomnavigation.TextBadgeItem import com.ashokvarma.bottomnavigation.TextBadgeItem
import com.ftinc.scoop.Scoop 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.Libs
import com.mikepenz.aboutlibraries.LibsBuilder import com.mikepenz.aboutlibraries.LibsBuilder
import com.mikepenz.materialdrawer.Drawer import com.mikepenz.materialdrawer.Drawer
@ -79,7 +73,6 @@ import com.mikepenz.materialdrawer.model.DividerDrawerItem
import com.mikepenz.materialdrawer.model.PrimaryDrawerItem import com.mikepenz.materialdrawer.model.PrimaryDrawerItem
import com.mikepenz.materialdrawer.model.SecondaryDrawerItem import com.mikepenz.materialdrawer.model.SecondaryDrawerItem
import kotlinx.android.synthetic.main.activity_home.* import kotlinx.android.synthetic.main.activity_home.*
import kotlinx.android.synthetic.main.fragment_article.*
import org.acra.ACRA import org.acra.ACRA
import retrofit2.Call import retrofit2.Call
import retrofit2.Callback import retrofit2.Callback
@ -152,6 +145,8 @@ class HomeActivity : AppCompatActivity(), SearchView.OnQueryTextListener {
private lateinit var db: AppDatabase private lateinit var db: AppDatabase
private lateinit var config: Config
data class DrawerData(val tags: List<Tag>?, val sources: List<Source>?) data class DrawerData(val tags: List<Tag>?, val sources: List<Source>?)
override fun onStart() { override fun onStart() {
@ -161,6 +156,7 @@ class HomeActivity : AppCompatActivity(), SearchView.OnQueryTextListener {
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
appColors = AppColors(this@HomeActivity) appColors = AppColors(this@HomeActivity)
config = Config(this@HomeActivity)
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
@ -176,9 +172,6 @@ class HomeActivity : AppCompatActivity(), SearchView.OnQueryTextListener {
handleThemeBinding() handleThemeBinding()
setSupportActionBar(toolBar) setSupportActionBar(toolBar)
if (savedInstanceState == null) {
Amplify.getSharedInstance().promptIfReady(promptView)
}
db = Room.databaseBuilder( db = Room.databaseBuilder(
applicationContext, applicationContext,
@ -230,7 +223,7 @@ class HomeActivity : AppCompatActivity(), SearchView.OnQueryTextListener {
recyclerView: RecyclerView, recyclerView: RecyclerView,
viewHolder: RecyclerView.ViewHolder viewHolder: RecyclerView.ViewHolder
): Int = ): Int =
if (elementsShown != UNREAD_SHOWN) { if (elementsShown != UNREAD_SHOWN && elementsShown != READ_SHOWN) {
0 0
} else { } else {
super.getSwipeDirs( super.getSwipeDirs(
@ -250,14 +243,18 @@ class HomeActivity : AppCompatActivity(), SearchView.OnQueryTextListener {
val i = items.elementAtOrNull(position) val i = items.elementAtOrNull(position)
if (i != null) { if (i != null) {
val adapter = recyclerView.adapter val adapter = recyclerView.adapter as ItemsAdapter<*>
when (adapter) { val wasItemUnread = adapter.unreadItemStatusAtIndex(position)
is ItemCardAdapter -> adapter.removeItemAtIndex(position)
is ItemListAdapter -> adapter.removeItemAtIndex(position) adapter.handleItemAtIndex(position)
if (wasItemUnread) {
badgeNew--
} else {
badgeNew++
} }
badgeNew--
reloadBadgeContent() reloadBadgeContent()
val tagHashes = i.tags.tags.split(",").map { it.longHash() } val tagHashes = i.tags.tags.split(",").map { it.longHash() }
@ -304,19 +301,19 @@ class HomeActivity : AppCompatActivity(), SearchView.OnQueryTextListener {
val tabNew = val tabNew =
BottomNavigationItem( BottomNavigationItem(
R.drawable.ic_fiber_new_black_24dp, R.drawable.ic_tab_fiber_new_black_24dp,
getString(R.string.tab_new) getString(R.string.tab_new)
).setActiveColor(appColors.colorAccent) ).setActiveColor(appColors.colorAccent)
.setBadgeItem(tabNewBadge) .setBadgeItem(tabNewBadge)
val tabArchive = val tabArchive =
BottomNavigationItem( BottomNavigationItem(
R.drawable.ic_archive_black_24dp, R.drawable.ic_tab_archive_black_24dp,
getString(R.string.tab_read) getString(R.string.tab_read)
).setActiveColor(appColors.colorAccentDark) ).setActiveColor(appColors.colorAccentDark)
.setBadgeItem(tabArchiveBadge) .setBadgeItem(tabArchiveBadge)
val tabStarred = val tabStarred =
BottomNavigationItem( BottomNavigationItem(
R.drawable.ic_favorite_black_24dp, R.drawable.ic_tab_favorite_black_24dp,
getString(R.string.tab_favs) getString(R.string.tab_favs)
).setActiveColorResource(R.color.pink) ).setActiveColorResource(R.color.pink)
.setBadgeItem(tabStarredBadge) .setBadgeItem(tabStarredBadge)
@ -478,7 +475,7 @@ class HomeActivity : AppCompatActivity(), SearchView.OnQueryTextListener {
footer { footer {
primaryItem(R.string.drawer_report_bug) { primaryItem(R.string.drawer_report_bug) {
icon = R.drawable.ic_bug_report icon = R.drawable.ic_bug_report_black_24dp
iconTintingEnabled = true iconTintingEnabled = true
onClick { _ -> onClick { _ ->
val browserIntent = Intent(Intent.ACTION_VIEW, Uri.parse(Config.trackerUrl)) 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) { primaryItem(R.string.title_activity_settings) {
icon = R.drawable.ic_settings icon = R.drawable.ic_settings_black_24dp
iconTintingEnabled = true iconTintingEnabled = true
onClick { _ -> onClick { _ ->
startActivityForResult( startActivityForResult(
@ -518,7 +515,9 @@ class HomeActivity : AppCompatActivity(), SearchView.OnQueryTextListener {
) )
} }
} else { } else {
val filteredTags = maybeTags.filterNot { hiddenTags.contains(it.tag) } val filteredTags = maybeTags
.filterNot { hiddenTags.contains(it.tag) }
.sortedBy { it.unread == 0 }
tagsBadge = filteredTags.map { tagsBadge = filteredTags.map {
val gd = GradientDrawable() val gd = GradientDrawable()
val color = try { val color = try {
@ -531,12 +530,11 @@ class HomeActivity : AppCompatActivity(), SearchView.OnQueryTextListener {
gd.shape = GradientDrawable.RECTANGLE gd.shape = GradientDrawable.RECTANGLE
gd.setSize(30, 30) gd.setSize(30, 30)
gd.cornerRadius = 30F gd.cornerRadius = 30F
drawer.addItem( var drawerItem =
PrimaryDrawerItem() PrimaryDrawerItem()
.withName(it.tag) .withName(it.tag)
.withIdentifier(it.tag.longHash()) .withIdentifier(it.tag.longHash())
.withIcon(gd) .withIcon(gd)
.withBadge("${it.unread}")
.withBadgeStyle( .withBadgeStyle(
BadgeStyle().withTextColor(Color.WHITE) BadgeStyle().withTextColor(Color.WHITE)
.withColor(appColors.colorAccent) .withColor(appColors.colorAccent)
@ -547,6 +545,11 @@ class HomeActivity : AppCompatActivity(), SearchView.OnQueryTextListener {
getElementsAccordingToTab() getElementsAccordingToTab()
false false
} }
if (it.unread > 0) {
drawerItem = drawerItem.withBadge("${it.unread}")
}
drawer.addItem(
drawerItem
) )
(it.tag.longHash() to it.unread) (it.tag.longHash() to it.unread)
@ -578,12 +581,11 @@ class HomeActivity : AppCompatActivity(), SearchView.OnQueryTextListener {
gd.shape = GradientDrawable.RECTANGLE gd.shape = GradientDrawable.RECTANGLE
gd.setSize(30, 30) gd.setSize(30, 30)
gd.cornerRadius = 30F gd.cornerRadius = 30F
drawer.addItem( var drawerItem =
PrimaryDrawerItem() PrimaryDrawerItem()
.withName(it.tag) .withName(it.tag)
.withIdentifier(it.tag.longHash()) .withIdentifier(it.tag.longHash())
.withIcon(gd) .withIcon(gd)
.withBadge("${it.unread}")
.withBadgeStyle( .withBadgeStyle(
BadgeStyle().withTextColor(Color.WHITE) BadgeStyle().withTextColor(Color.WHITE)
.withColor(appColors.colorAccent) .withColor(appColors.colorAccent)
@ -594,6 +596,11 @@ class HomeActivity : AppCompatActivity(), SearchView.OnQueryTextListener {
getElementsAccordingToTab() getElementsAccordingToTab()
false false
} }
if (it.unread > 0) {
drawerItem = drawerItem.withBadge("${it.unread}")
}
drawer.addItem(
drawerItem
) )
(it.tag.longHash() to it.unread) (it.tag.longHash() to it.unread)
@ -680,7 +687,7 @@ class HomeActivity : AppCompatActivity(), SearchView.OnQueryTextListener {
PrimaryDrawerItem() PrimaryDrawerItem()
.withName(R.string.action_about) .withName(R.string.action_about)
.withSelectable(false) .withSelectable(false)
.withIcon(R.drawable.ic_info_outline) .withIcon(R.drawable.ic_info_outline_white_24dp)
.withIconTintingEnabled(true) .withIconTintingEnabled(true)
.withOnDrawerItemClickListener { _, _, _ -> .withOnDrawerItemClickListener { _, _, _ ->
LibsBuilder() LibsBuilder()
@ -1136,7 +1143,8 @@ class HomeActivity : AppCompatActivity(), SearchView.OnQueryTextListener {
fullHeightCards, fullHeightCards,
appColors, appColors,
debugReadingItems, debugReadingItems,
userIdentifier userIdentifier,
config
) { ) {
updateItems(it) updateItems(it)
} }
@ -1152,7 +1160,8 @@ class HomeActivity : AppCompatActivity(), SearchView.OnQueryTextListener {
articleViewer, articleViewer,
debugReadingItems, debugReadingItems,
userIdentifier, userIdentifier,
appColors appColors,
config
) { ) {
updateItems(it) updateItems(it)
} }
@ -1341,12 +1350,7 @@ class HomeActivity : AppCompatActivity(), SearchView.OnQueryTextListener {
).show() ).show()
tabNewBadge.removeBadge() tabNewBadge.removeBadge()
handleDrawerItems()
tagsBadge = itemsByTag.map {
(it.key to ((tagsBadge[it.key] ?: it.value) - it.value))
}.toMap()
reloadTagsBadges()
getElementsAccordingToTab() getElementsAccordingToTab()
} else { } else {

View File

@ -10,12 +10,10 @@ import android.preference.PreferenceManager
import androidx.multidex.MultiDexApplication import androidx.multidex.MultiDexApplication
import android.widget.ImageView import android.widget.ImageView
import apps.amine.bou.readerforselfoss.utils.Config import apps.amine.bou.readerforselfoss.utils.Config
import apps.amine.bou.readerforselfoss.utils.glide.loadMaybeBasicAuth
import com.bumptech.glide.Glide import com.bumptech.glide.Glide
import com.bumptech.glide.request.RequestOptions import com.bumptech.glide.request.RequestOptions
import com.ftinc.scoop.Scoop import com.ftinc.scoop.Scoop
import com.github.stkent.amplify.feedback.DefaultEmailFeedbackCollector
import com.github.stkent.amplify.feedback.GooglePlayStoreFeedbackCollector
import com.github.stkent.amplify.tracking.Amplify
import com.mikepenz.materialdrawer.util.AbstractDrawerImageLoader import com.mikepenz.materialdrawer.util.AbstractDrawerImageLoader
import com.mikepenz.materialdrawer.util.DrawerImageLoader import com.mikepenz.materialdrawer.util.DrawerImageLoader
import org.acra.ACRA import org.acra.ACRA
@ -24,7 +22,6 @@ import org.acra.annotation.AcraCore
import org.acra.annotation.AcraDialog import org.acra.annotation.AcraDialog
import org.acra.annotation.AcraHttpSender import org.acra.annotation.AcraHttpSender
import org.acra.sender.HttpSender import org.acra.sender.HttpSender
import java.io.IOException
import java.util.UUID.randomUUID import java.util.UUID.randomUUID
@ -42,11 +39,11 @@ import java.util.UUID.randomUUID
ReportField.USER_APP_START_DATE, ReportField.USER_COMMENT, ReportField.USER_CRASH_DATE, ReportField.USER_EMAIL, ReportField.CUSTOM_DATA], ReportField.USER_APP_START_DATE, ReportField.USER_COMMENT, ReportField.USER_CRASH_DATE, ReportField.USER_EMAIL, ReportField.CUSTOM_DATA],
buildConfigClass = BuildConfig::class) buildConfigClass = BuildConfig::class)
class MyApp : MultiDexApplication() { class MyApp : MultiDexApplication() {
private lateinit var config: Config
override fun onCreate() { override fun onCreate() {
super.onCreate() super.onCreate()
config = Config(baseContext)
initAmplify()
val prefs = getSharedPreferences(Config.settingsName, Context.MODE_PRIVATE) val prefs = getSharedPreferences(Config.settingsName, Context.MODE_PRIVATE)
if (prefs.getString("unique_id", "").isEmpty()) { if (prefs.getString("unique_id", "").isEmpty()) {
@ -89,13 +86,6 @@ class MyApp : MultiDexApplication() {
} }
private fun initAmplify() {
Amplify.initSharedInstance(this)
.setPositiveFeedbackCollectors(GooglePlayStoreFeedbackCollector())
.setCriticalFeedbackCollectors(DefaultEmailFeedbackCollector(Config.feedbackEmail))
.applyAllDefaultRules()
}
private fun initDrawerImageLoader() { private fun initDrawerImageLoader() {
DrawerImageLoader.init(object : AbstractDrawerImageLoader() { DrawerImageLoader.init(object : AbstractDrawerImageLoader() {
override fun set( override fun set(
@ -105,7 +95,7 @@ class MyApp : MultiDexApplication() {
tag: String? tag: String?
) { ) {
Glide.with(imageView?.context) Glide.with(imageView?.context)
.load(uri) .loadMaybeBasicAuth(config, uri.toString())
.apply(RequestOptions.fitCenterTransform().placeholder(placeholder)) .apply(RequestOptions.fitCenterTransform().placeholder(placeholder))
.into(imageView) .into(imageView)
} }

View File

@ -1,5 +1,6 @@
package apps.amine.bou.readerforselfoss package apps.amine.bou.readerforselfoss
import android.content.Context
import android.content.SharedPreferences import android.content.SharedPreferences
import android.graphics.drawable.ColorDrawable import android.graphics.drawable.ColorDrawable
import android.os.Build 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.AppColors
import apps.amine.bou.readerforselfoss.themes.Toppings import apps.amine.bou.readerforselfoss.themes.Toppings
import apps.amine.bou.readerforselfoss.transformers.DepthPageTransformer import apps.amine.bou.readerforselfoss.transformers.DepthPageTransformer
import apps.amine.bou.readerforselfoss.utils.Config
import apps.amine.bou.readerforselfoss.utils.maybeHandleSilentException import apps.amine.bou.readerforselfoss.utils.maybeHandleSilentException
import apps.amine.bou.readerforselfoss.utils.network.isNetworkAccessible import apps.amine.bou.readerforselfoss.utils.network.isNetworkAccessible
import apps.amine.bou.readerforselfoss.utils.persistence.toEntity import apps.amine.bou.readerforselfoss.utils.persistence.toEntity
@ -94,6 +96,9 @@ class ReaderActivity : AppCompatActivity() {
supportActionBar?.setDisplayHomeAsUpEnabled(true) supportActionBar?.setDisplayHomeAsUpEnabled(true)
supportActionBar?.setDisplayShowHomeEnabled(true) supportActionBar?.setDisplayShowHomeEnabled(true)
val settings =
getSharedPreferences(Config.settingsName, Context.MODE_PRIVATE)
prefs = PreferenceManager.getDefaultSharedPreferences(this) prefs = PreferenceManager.getDefaultSharedPreferences(this)
editor = prefs.edit() editor = prefs.edit()
@ -105,7 +110,7 @@ class ReaderActivity : AppCompatActivity() {
api = SelfossApi( api = SelfossApi(
this, this,
this@ReaderActivity, this@ReaderActivity,
prefs.getBoolean("isSelfSignedCert", false), settings.getBoolean("isSelfSignedCert", false),
prefs.getString("api_timeout", "-1").toLong(), prefs.getString("api_timeout", "-1").toLong(),
prefs.getBoolean("should_log_everything", false) prefs.getBoolean("should_log_everything", false)
) )

View File

@ -1,5 +1,6 @@
package apps.amine.bou.readerforselfoss package apps.amine.bou.readerforselfoss
import android.content.Context
import android.content.Intent import android.content.Intent
import android.content.res.ColorStateList import android.content.res.ColorStateList
import android.os.Build 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.api.selfoss.Source
import apps.amine.bou.readerforselfoss.themes.AppColors import apps.amine.bou.readerforselfoss.themes.AppColors
import apps.amine.bou.readerforselfoss.themes.Toppings import apps.amine.bou.readerforselfoss.themes.Toppings
import apps.amine.bou.readerforselfoss.utils.Config
import apps.amine.bou.readerforselfoss.utils.network.isNetworkAccessible import apps.amine.bou.readerforselfoss.utils.network.isNetworkAccessible
import com.ftinc.scoop.Scoop import com.ftinc.scoop.Scoop
import kotlinx.android.synthetic.main.activity_sources.* import kotlinx.android.synthetic.main.activity_sources.*
@ -54,12 +56,14 @@ class SourcesActivity : AppCompatActivity() {
super.onResume() super.onResume()
val mLayoutManager = LinearLayoutManager(this) val mLayoutManager = LinearLayoutManager(this)
val settings =
getSharedPreferences(Config.settingsName, Context.MODE_PRIVATE)
val prefs = PreferenceManager.getDefaultSharedPreferences(this) val prefs = PreferenceManager.getDefaultSharedPreferences(this)
val api = SelfossApi( val api = SelfossApi(
this, this,
this@SourcesActivity, this@SourcesActivity,
prefs.getBoolean("isSelfSignedCert", false), settings.getBoolean("isSelfSignedCert", false),
prefs.getString("api_timeout", "-1").toLong(), prefs.getString("api_timeout", "-1").toLong(),
prefs.getBoolean("should_log_everything", false) prefs.getBoolean("should_log_everything", false)
) )

View File

@ -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.database.AppDatabase
import apps.amine.bou.readerforselfoss.persistence.entities.ActionEntity import apps.amine.bou.readerforselfoss.persistence.entities.ActionEntity
import apps.amine.bou.readerforselfoss.themes.AppColors import apps.amine.bou.readerforselfoss.themes.AppColors
import apps.amine.bou.readerforselfoss.utils.Config
import apps.amine.bou.readerforselfoss.utils.LinkOnTouchListener import apps.amine.bou.readerforselfoss.utils.LinkOnTouchListener
import apps.amine.bou.readerforselfoss.utils.buildCustomTabsIntent import apps.amine.bou.readerforselfoss.utils.buildCustomTabsIntent
import apps.amine.bou.readerforselfoss.utils.customtabs.CustomTabActivityHelper import apps.amine.bou.readerforselfoss.utils.customtabs.CustomTabActivityHelper
@ -51,6 +52,7 @@ class ItemCardAdapter(
override val appColors: AppColors, override val appColors: AppColors,
override val debugReadingItems: Boolean, override val debugReadingItems: Boolean,
override val userIdentifier: String, override val userIdentifier: String,
override val config: Config,
override val updateItems: (ArrayList<Item>) -> Unit override val updateItems: (ArrayList<Item>) -> Unit
) : ItemsAdapter<ItemCardAdapter.ViewHolder>() { ) : ItemsAdapter<ItemCardAdapter.ViewHolder>() {
private val c: Context = app.baseContext private val c: Context = app.baseContext
@ -86,7 +88,7 @@ class ItemCardAdapter(
holder.mView.itemImage.setImageDrawable(null) holder.mView.itemImage.setImageDrawable(null)
} else { } else {
holder.mView.itemImage.visibility = View.VISIBLE 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()) { if (itm.getIcon(c).isEmpty()) {
@ -99,7 +101,7 @@ class ItemCardAdapter(
.build(itm.sourcetitle.toTextDrawableString(c), color) .build(itm.sourcetitle.toTextDrawableString(c), color)
holder.mView.sourceImage.setImageDrawable(drawable) holder.mView.sourceImage.setImageDrawable(drawable)
} else { } else {
c.circularBitmapDrawable(itm.getIcon(c), holder.mView.sourceImage) c.circularBitmapDrawable(config, itm.getIcon(c), holder.mView.sourceImage)
} }
holder.mView.favButton.isLiked = itm.starred holder.mView.favButton.isLiked = itm.starred

View File

@ -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.api.selfoss.SuccessResponse
import apps.amine.bou.readerforselfoss.persistence.database.AppDatabase import apps.amine.bou.readerforselfoss.persistence.database.AppDatabase
import apps.amine.bou.readerforselfoss.themes.AppColors import apps.amine.bou.readerforselfoss.themes.AppColors
import apps.amine.bou.readerforselfoss.utils.Config
import apps.amine.bou.readerforselfoss.utils.LinkOnTouchListener import apps.amine.bou.readerforselfoss.utils.LinkOnTouchListener
import apps.amine.bou.readerforselfoss.utils.buildCustomTabsIntent import apps.amine.bou.readerforselfoss.utils.buildCustomTabsIntent
import apps.amine.bou.readerforselfoss.utils.customtabs.CustomTabActivityHelper import apps.amine.bou.readerforselfoss.utils.customtabs.CustomTabActivityHelper
@ -52,6 +53,7 @@ class ItemListAdapter(
override val debugReadingItems: Boolean, override val debugReadingItems: Boolean,
override val userIdentifier: String, override val userIdentifier: String,
override val appColors: AppColors, override val appColors: AppColors,
override val config: Config,
override val updateItems: (ArrayList<Item>) -> Unit override val updateItems: (ArrayList<Item>) -> Unit
) : ItemsAdapter<ItemListAdapter.ViewHolder>() { ) : ItemsAdapter<ItemListAdapter.ViewHolder>() {
private val generator: ColorGenerator = ColorGenerator.MATERIAL private val generator: ColorGenerator = ColorGenerator.MATERIAL
@ -108,10 +110,10 @@ class ItemListAdapter(
holder.mView.itemImage.setImageDrawable(drawable) holder.mView.itemImage.setImageDrawable(drawable)
} else { } else {
c.circularBitmapDrawable(itm.getIcon(c), holder.mView.itemImage) c.circularBitmapDrawable(config, itm.getIcon(c), holder.mView.itemImage)
} }
} else { } else {
c.bitmapCenterCrop(itm.getThumbnail(c), holder.mView.itemImage) c.bitmapCenterCrop(config, itm.getThumbnail(c), holder.mView.itemImage)
} }
} }

View File

@ -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.database.AppDatabase
import apps.amine.bou.readerforselfoss.persistence.entities.ActionEntity import apps.amine.bou.readerforselfoss.persistence.entities.ActionEntity
import apps.amine.bou.readerforselfoss.themes.AppColors import apps.amine.bou.readerforselfoss.themes.AppColors
import apps.amine.bou.readerforselfoss.utils.Config
import apps.amine.bou.readerforselfoss.utils.maybeHandleSilentException import apps.amine.bou.readerforselfoss.utils.maybeHandleSilentException
import apps.amine.bou.readerforselfoss.utils.network.isNetworkAccessible import apps.amine.bou.readerforselfoss.utils.network.isNetworkAccessible
import apps.amine.bou.readerforselfoss.utils.persistence.toEntity 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 userIdentifier: String
abstract val app: Activity abstract val app: Activity
abstract val appColors: AppColors abstract val appColors: AppColors
abstract val config: Config
abstract val updateItems: (ArrayList<Item>) -> Unit abstract val updateItems: (ArrayList<Item>) -> Unit
fun updateAllItems(newItems: ArrayList<Item>) { fun updateAllItems(newItems: ArrayList<Item>) {
@ -39,7 +41,7 @@ abstract class ItemsAdapter<VH : RecyclerView.ViewHolder?> : RecyclerView.Adapte
updateItems(items) updateItems(items)
} }
private fun doUnmark(i: Item, position: Int) { private fun unmarkSnackbar(i: Item, position: Int) {
val s = Snackbar val s = Snackbar
.make( .make(
app.findViewById(R.id.coordLayout), app.findViewById(R.id.coordLayout),
@ -69,12 +71,11 @@ abstract class ItemsAdapter<VH : RecyclerView.ViewHolder?> : RecyclerView.Adapte
} }
notifyItemRemoved(position) notifyItemRemoved(position)
updateItems(items) updateItems(items)
doUnmark(i, position)
} }
}) })
} else { } else {
thread { 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() 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] val i = items[position]
items.remove(i) items.remove(i)
notifyItemRemoved(position) notifyItemRemoved(position)
@ -103,7 +161,7 @@ abstract class ItemsAdapter<VH : RecyclerView.ViewHolder?> : RecyclerView.Adapte
) { ) {
if (!response.succeeded() && debugReadingItems) { if (!response.succeeded() && debugReadingItems) {
val message = val message =
"message: ${response.message()} " + "MARK message: ${response.message()} " +
"response isSuccess: ${response.isSuccessful} " + "response isSuccess: ${response.isSuccessful} " +
"response code: ${response.code()} " + "response code: ${response.code()} " +
"response message: ${response.message()} " + "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() Toast.makeText(app.baseContext, message, Toast.LENGTH_LONG).show()
} }
doUnmark(i, position) unmarkSnackbar(i, position)
} }
override fun onFailure(call: Call<SuccessResponse>, t: Throwable) { 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), app.getString(R.string.cant_mark_read),
Toast.LENGTH_SHORT Toast.LENGTH_SHORT
).show() ).show()
items.add(i) items.add(position, i)
notifyItemInserted(position) notifyItemInserted(position)
updateItems(items) updateItems(items)
@ -139,7 +197,64 @@ abstract class ItemsAdapter<VH : RecyclerView.ViewHolder?> : RecyclerView.Adapte
} else { } else {
thread { thread {
db.actionsDao().insertAllActions(ActionEntity(i.id, true, false, false, false)) 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))
} }
} }
} }

View File

@ -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.SelfossApi
import apps.amine.bou.readerforselfoss.api.selfoss.Source import apps.amine.bou.readerforselfoss.api.selfoss.Source
import apps.amine.bou.readerforselfoss.api.selfoss.SuccessResponse 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.glide.circularBitmapDrawable
import apps.amine.bou.readerforselfoss.utils.network.isNetworkAccessible import apps.amine.bou.readerforselfoss.utils.network.isNetworkAccessible
import apps.amine.bou.readerforselfoss.utils.toTextDrawableString import apps.amine.bou.readerforselfoss.utils.toTextDrawableString
@ -29,6 +30,7 @@ class SourcesListAdapter(
) : RecyclerView.Adapter<SourcesListAdapter.ViewHolder>() { ) : RecyclerView.Adapter<SourcesListAdapter.ViewHolder>() {
private val c: Context = app.baseContext private val c: Context = app.baseContext
private val generator: ColorGenerator = ColorGenerator.MATERIAL private val generator: ColorGenerator = ColorGenerator.MATERIAL
private lateinit var config: Config
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder { override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
val v = LayoutInflater.from(c).inflate( val v = LayoutInflater.from(c).inflate(
@ -41,6 +43,7 @@ class SourcesListAdapter(
override fun onBindViewHolder(holder: ViewHolder, position: Int) { override fun onBindViewHolder(holder: ViewHolder, position: Int) {
val itm = items[position] val itm = items[position]
config = Config(c)
if (itm.getIcon(c).isEmpty()) { if (itm.getIcon(c).isEmpty()) {
val color = generator.getColor(itm.title) val color = generator.getColor(itm.title)
@ -52,7 +55,7 @@ class SourcesListAdapter(
.build(itm.title.toTextDrawableString(c), color) .build(itm.title.toTextDrawableString(c), color)
holder.mView.itemImage.setImageDrawable(drawable) holder.mView.itemImage.setImageDrawable(drawable)
} else { } else {
c.circularBitmapDrawable(itm.getIcon(c), holder.mView.itemImage) c.circularBitmapDrawable(config, itm.getIcon(c), holder.mView.itemImage)
} }
holder.mView.sourceTitle.text = itm.title holder.mView.sourceTitle.text = itm.title

View File

@ -12,7 +12,10 @@ import com.burgstaller.okhttp.digest.CachingAuthenticator
import com.burgstaller.okhttp.digest.Credentials import com.burgstaller.okhttp.digest.Credentials
import com.burgstaller.okhttp.digest.DigestAuthenticator import com.burgstaller.okhttp.digest.DigestAuthenticator
import com.google.gson.GsonBuilder import com.google.gson.GsonBuilder
import okhttp3.Interceptor
import okhttp3.OkHttpClient import okhttp3.OkHttpClient
import okhttp3.Request
import okhttp3.Response
import okhttp3.logging.HttpLoggingInterceptor import okhttp3.logging.HttpLoggingInterceptor
import retrofit2.Call import retrofit2.Call
import retrofit2.Retrofit import retrofit2.Retrofit
@ -62,6 +65,17 @@ class SelfossApi(
.maybeWithSelfSigned(isWithSelfSignedCert) .maybeWithSelfSigned(isWithSelfSignedCert)
.authenticator(CachingAuthenticatorDecorator(this, authCache)) .authenticator(CachingAuthenticatorDecorator(this, authCache))
.addInterceptor(AuthenticationCacheInterceptor(authCache)) .addInterceptor(AuthenticationCacheInterceptor(authCache))
.addInterceptor(object: Interceptor {
override fun intercept(chain: Interceptor.Chain): Response {
val request: Request = chain.request()
val response: Response = chain.proceed(request)
if (response.code() == 408) {
return response
}
return response
}
})
} }
init { init {

View File

@ -46,7 +46,7 @@ class LoadingWorker(val context: Context, params: WorkerParameters) : Worker(con
.setOngoing(true) .setOngoing(true)
.setPriority(PRIORITY_LOW) .setPriority(PRIORITY_LOW)
.setChannelId(Config.syncChannelId) .setChannelId(Config.syncChannelId)
.setSmallIcon(R.drawable.ic_cloud_download) .setSmallIcon(R.drawable.ic_stat_cloud_download_black_24dp)
notificationManager.notify(1, notification.build()) notificationManager.notify(1, notification.build())
@ -101,7 +101,7 @@ class LoadingWorker(val context: Context, params: WorkerParameters) : Worker(con
.setChannelId(Config.newItemsChannelId) .setChannelId(Config.newItemsChannelId)
.setContentIntent(pendingIntent) .setContentIntent(pendingIntent)
.setAutoCancel(true) .setAutoCancel(true)
.setSmallIcon(R.drawable.ic_fiber_new_black_24dp) .setSmallIcon(R.drawable.ic_tab_fiber_new_black_24dp)
Timer("", false).schedule(4000) { Timer("", false).schedule(4000) {
notificationManager.notify(2, newItemsNotification.build()) notificationManager.notify(2, newItemsNotification.build())
@ -130,7 +130,7 @@ class LoadingWorker(val context: Context, params: WorkerParameters) : Worker(con
} }
} }
} }
return Result.SUCCESS return Result.success()
} }
private fun <T> doAndReportOnFail(call: Call<T>, action: ActionEntity) { private fun <T> doAndReportOnFail(call: Call<T>, action: ActionEntity) {

View File

@ -3,6 +3,8 @@ package apps.amine.bou.readerforselfoss.fragments
import android.content.Context import android.content.Context
import android.content.SharedPreferences import android.content.SharedPreferences
import android.content.res.ColorStateList import android.content.res.ColorStateList
import android.content.res.TypedArray
import android.graphics.Typeface
import android.graphics.drawable.ColorDrawable import android.graphics.drawable.ColorDrawable
import android.os.Build import android.os.Build
import android.os.Bundle import android.os.Bundle
@ -19,6 +21,7 @@ import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import android.webkit.WebSettings import android.webkit.WebSettings
import androidx.appcompat.app.AlertDialog import androidx.appcompat.app.AlertDialog
import androidx.core.content.res.ResourcesCompat
import androidx.room.Room import androidx.room.Room
import apps.amine.bou.readerforselfoss.R import apps.amine.bou.readerforselfoss.R
import apps.amine.bou.readerforselfoss.api.mercury.MercuryApi 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.Config
import apps.amine.bou.readerforselfoss.utils.buildCustomTabsIntent import apps.amine.bou.readerforselfoss.utils.buildCustomTabsIntent
import apps.amine.bou.readerforselfoss.utils.customtabs.CustomTabActivityHelper 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.isEmptyOrNullOrNullString
import apps.amine.bou.readerforselfoss.utils.maybeHandleSilentException import apps.amine.bou.readerforselfoss.utils.maybeHandleSilentException
import apps.amine.bou.readerforselfoss.utils.network.isNetworkAccessible import apps.amine.bou.readerforselfoss.utils.network.isNetworkAccessible
@ -68,6 +72,15 @@ class ArticleFragment : Fragment() {
private lateinit var appColors: AppColors private lateinit var appColors: AppColors
private lateinit var db: AppDatabase private lateinit var db: AppDatabase
private lateinit var textAlignment: String 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() { override fun onStop() {
super.onStop() super.onStop()
@ -78,6 +91,7 @@ class ArticleFragment : Fragment() {
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
appColors = AppColors(activity!!) appColors = AppColors(activity!!)
config = Config(activity!!)
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
@ -90,10 +104,6 @@ class ArticleFragment : Fragment() {
).addMigrations(MIGRATION_1_2).addMigrations(MIGRATION_2_3).build() ).addMigrations(MIGRATION_1_2).addMigrations(MIGRATION_2_3).build()
} }
private var rootView: ViewGroup? = null
private lateinit var prefs: SharedPreferences
override fun onCreateView( override fun onCreateView(
inflater: LayoutInflater, inflater: LayoutInflater,
container: ViewGroup?, container: ViewGroup?,
@ -112,6 +122,19 @@ class ArticleFragment : Fragment() {
prefs = PreferenceManager.getDefaultSharedPreferences(activity) prefs = PreferenceManager.getDefaultSharedPreferences(activity)
editor = prefs.edit() editor = prefs.edit()
fontSize = prefs.getString("reader_font_size", "16").toInt() 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() refreshAlignment()
val settings = activity!!.getSharedPreferences(Config.settingsName, Context.MODE_PRIVATE) val settings = activity!!.getSharedPreferences(Config.settingsName, Context.MODE_PRIVATE)
@ -201,11 +224,17 @@ class ArticleFragment : Fragment() {
) )
rootView!!.source.text = contentSource rootView!!.source.text = contentSource
if (typeface != null) {
rootView!!.source.typeface = typeface
}
if (contentText.isEmptyOrNullOrNullString()) { if (contentText.isEmptyOrNullOrNullString()) {
getContentFromMercury(customTabsIntent, prefs) getContentFromMercury(customTabsIntent, prefs)
} else { } else {
rootView!!.titleView.text = contentTitle rootView!!.titleView.text = contentTitle
if (typeface != null) {
rootView!!.titleView.typeface = typeface
}
htmlToWebview() htmlToWebview()
@ -214,7 +243,7 @@ class ArticleFragment : Fragment() {
Glide Glide
.with(context!!) .with(context!!)
.asBitmap() .asBitmap()
.load(contentImage) .loadMaybeBasicAuth(config, contentImage)
.apply(RequestOptions.fitCenterTransform()) .apply(RequestOptions.fitCenterTransform())
.into(rootView!!.imageView) .into(rootView!!.imageView)
} else { } else {
@ -280,6 +309,9 @@ class ArticleFragment : Fragment() {
if (response.body() != null && response.body()!!.content != null && !response.body()!!.content.isNullOrEmpty()) { if (response.body() != null && response.body()!!.content != null && !response.body()!!.content.isNullOrEmpty()) {
try { try {
rootView!!.titleView.text = response.body()!!.title rootView!!.titleView.text = response.body()!!.title
if (typeface != null) {
rootView!!.titleView.typeface = typeface
}
try { try {
// Note: Mercury may return relative urls... If it does the url val will not be changed. // Note: Mercury may return relative urls... If it does the url val will not be changed.
URL(response.body()!!.url) URL(response.body()!!.url)
@ -309,7 +341,7 @@ class ArticleFragment : Fragment() {
Glide Glide
.with(context!!) .with(context!!)
.asBitmap() .asBitmap()
.load(response.body()!!.lead_image_url) .loadMaybeBasicAuth(config, response.body()!!.lead_image_url.orEmpty())
.apply(RequestOptions.fitCenterTransform()) .apply(RequestOptions.fitCenterTransform())
.into(rootView!!.imageView) .into(rootView!!.imageView)
} catch (e: Exception) { } catch (e: Exception) {
@ -361,6 +393,11 @@ class ArticleFragment : Fragment() {
private fun htmlToWebview() { private fun htmlToWebview() {
val stringColor = String.format("#%06X", 0xFFFFFF and appColors.colorAccent) 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 rootView!!.webcontent.visibility = View.VISIBLE
val (textColor, backgroundColor) = if (appColors.isDarkTheme) { val (textColor, backgroundColor) = if (appColors.isDarkTheme) {
if (context != null) { if (context != null) {
@ -420,6 +457,24 @@ class ArticleFragment : Fragment() {
ACRA.getErrorReporter().maybeHandleSilentException(e, activity!!) 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( rootView!!.webcontent.loadDataWithBaseURL(
baseUrl, baseUrl,
"""<html> """<html>
@ -443,6 +498,7 @@ class ArticleFragment : Fragment() {
| text-align: $textAlignment; | text-align: $textAlignment;
| word-break: break-word; | word-break: break-word;
| overflow:hidden; | overflow:hidden;
| line-height: 1.5em;
| } | }
| a, pre, code { | a, pre, code {
| text-align: $textAlignment; | text-align: $textAlignment;
@ -453,6 +509,7 @@ class ArticleFragment : Fragment() {
| background-color: $stringBackgroundColor; | background-color: $stringBackgroundColor;
| } | }
| </style> | </style>
| $fontLinkAndStyle
|</head> |</head>
|<body> |<body>
| $contentText | $contentText

View File

@ -8,6 +8,7 @@ import android.content.Context;
import android.content.Intent; import android.content.Intent;
import android.content.SharedPreferences; import android.content.SharedPreferences;
import android.content.res.Configuration; import android.content.res.Configuration;
import android.content.res.Resources;
import android.net.Uri; import android.net.Uri;
import android.os.Build; import android.os.Build;
import android.os.Bundle; import android.os.Bundle;
@ -24,6 +25,7 @@ import android.text.Editable;
import android.text.InputFilter; import android.text.InputFilter;
import android.text.Spanned; import android.text.Spanned;
import android.text.TextWatcher; import android.text.TextWatcher;
import android.util.Log;
import android.view.Menu; import android.view.Menu;
import android.view.MenuInflater; import android.view.MenuInflater;
import android.view.MenuItem; import android.view.MenuItem;
@ -124,6 +126,27 @@ public class SettingsActivity extends AppCompatPreferenceActivity {
@TargetApi(Build.VERSION_CODES.HONEYCOMB) @TargetApi(Build.VERSION_CODES.HONEYCOMB)
public void onBuildHeaders(List<Header> target) { public void onBuildHeaders(List<Header> target) {
loadHeadersFromResource(R.xml.pref_headers, 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);
}
} }
/** /**

View File

@ -2,30 +2,30 @@ package apps.amine.bou.readerforselfoss.utils.glide
import android.content.Context import android.content.Context
import android.graphics.Bitmap import android.graphics.Bitmap
import android.graphics.drawable.Drawable
import android.util.Base64
import androidx.core.graphics.drawable.RoundedBitmapDrawableFactory import androidx.core.graphics.drawable.RoundedBitmapDrawableFactory
import android.widget.ImageView import android.widget.ImageView
import apps.amine.bou.readerforselfoss.utils.Config
import com.bumptech.glide.Glide 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.RequestOptions
import com.bumptech.glide.request.target.BitmapImageViewTarget 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) Glide.with(this)
.asBitmap() .asBitmap()
.load(url) .loadMaybeBasicAuth(config, url)
.apply(RequestOptions.centerCropTransform()) .apply(RequestOptions.centerCropTransform())
.into(iv) .into(iv)
fun Context.bitmapFitCenter(url: String, iv: ImageView) = fun Context.circularBitmapDrawable(config: Config, url: String, iv: ImageView) =
Glide.with(this) Glide.with(this)
.asBitmap() .asBitmap()
.load(url) .loadMaybeBasicAuth(config, url)
.apply(RequestOptions.fitCenterTransform())
.into(iv)
fun Context.circularBitmapDrawable(url: String, iv: ImageView) =
Glide.with(this)
.asBitmap()
.load(url)
.apply(RequestOptions.centerCropTransform()) .apply(RequestOptions.centerCropTransform())
.into(object : BitmapImageViewTarget(iv) { .into(object : BitmapImageViewTarget(iv) {
override fun setResource(resource: Bitmap?) { override fun setResource(resource: Bitmap?) {
@ -36,4 +36,24 @@ fun Context.circularBitmapDrawable(url: String, iv: ImageView) =
circularBitmapDrawable.isCircular = true circularBitmapDrawable.isCircular = true
iv.setImageDrawable(circularBitmapDrawable) 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)
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 683 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 680 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 134 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 239 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 271 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 216 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 206 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 221 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 334 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 458 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 275 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 361 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 324 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 301 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 551 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 355 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 551 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 204 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 187 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 422 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 473 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 498 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 453 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 398 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 397 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 523 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 409 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 442 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 116 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 174 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 212 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 136 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 134 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 175 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 228 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 268 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 213 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 247 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 215 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 208 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 352 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 241 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 355 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 157 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 144 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 276 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 309 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 339 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 322 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 262 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 268 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 361 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 871 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 634 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 168 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 261 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 312 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 171 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 174 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 257 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 380 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 504 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 300 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 437 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 327 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 308 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 684 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 464 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 725 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 230 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 208 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 557 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 625 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 606 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 557 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 483 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 496 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 660 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 242 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 377 B

Some files were not shown because too many files have changed in this diff Show More