Compare commits
8 Commits
Author | SHA1 | Date | |
---|---|---|---|
8157146498 | |||
94d23888b1 | |||
737fe9bb4a | |||
0051ed2e73 | |||
e0595957e2 | |||
8d09ff7fdb | |||
04feb66b07 | |||
54b2ac7f24 |
15
.github/CONTRIBUTING.md
vendored
15
.github/CONTRIBUTING.md
vendored
@ -52,20 +52,21 @@ You'll have to:
|
|||||||
|
|
||||||
- mercuryApiKey: A [Mercury](https://mercury.postlight.com/web-parser/) web parser api key for the internal browser
|
- mercuryApiKey: A [Mercury](https://mercury.postlight.com/web-parser/) web parser api key for the internal browser
|
||||||
- feedbackEmail: An email to receive users feedback.
|
- feedbackEmail: An email to receive users feedback.
|
||||||
- sourceUrl: an url to the source code, used in the settings
|
- sourceUrl: an url to the source code, used in the settings. **It can be empty.**
|
||||||
- trackerUrl: an url to the tracker, used in the settings
|
- trackerUrl: an url to the tracker, used in the settings. **It can be empty.**
|
||||||
|
- appLoginUrl, appLoginUsername and appLoginPassword: url, username and password of a selfoss instance. **These are only used for tests. They can be empty if you don't test API calls.**
|
||||||
|
|
||||||
### Examples:
|
### Examples:
|
||||||
#### Inside ~/.gradle/gradle.properties
|
#### Inside ~/.gradle/gradle.properties
|
||||||
|
|
||||||
```
|
```
|
||||||
appLoginUrl="URL"
|
appLoginUrl="URL" # It can be empty.
|
||||||
appLoginUsername="LOGIN"
|
appLoginUsername="LOGIN" # It can be empty.
|
||||||
appLoginPassword="PASS"
|
appLoginPassword="PASS" # It can be empty.
|
||||||
mercuryApiKey="LONGAPIKEY"
|
mercuryApiKey="LONGAPIKEY"
|
||||||
feedbackEmail="EMAIL"
|
feedbackEmail="EMAIL"
|
||||||
sourceUrl="URLSOURCE"
|
sourceUrl="URLSOURCE" # It can be empty.
|
||||||
trackerUrl="URLTRACKER"
|
trackerUrl="URLTRACKER" # It can be empty.
|
||||||
```
|
```
|
||||||
|
|
||||||
#### As gradle parameters
|
#### As gradle parameters
|
||||||
|
4
.gitignore
vendored
4
.gitignore
vendored
@ -214,4 +214,6 @@ gradle-app.setting
|
|||||||
|
|
||||||
# End of https://www.gitignore.io/api/java,gradle,android,androidstudio
|
# End of https://www.gitignore.io/api/java,gradle,android,androidstudio
|
||||||
|
|
||||||
release/
|
release/
|
||||||
|
|
||||||
|
crowdin.properties
|
15
CHANGELOG.md
15
CHANGELOG.md
@ -1,3 +1,18 @@
|
|||||||
|
**1.5.3.01/02**
|
||||||
|
|
||||||
|
- Added translation link to the settings page.
|
||||||
|
|
||||||
|
- Added the translation link to the README.
|
||||||
|
|
||||||
|
**1.5.3.00**
|
||||||
|
|
||||||
|
- (BETA) Added pull from bottom to load more pages of results. May be buggy.
|
||||||
|
|
||||||
|
**1.5.2.18/19**
|
||||||
|
|
||||||
|
- APK minification finally working. That means less space taken !
|
||||||
|
- Added an option to log every API call.
|
||||||
|
|
||||||
**1.5.2.17**
|
**1.5.2.17**
|
||||||
|
|
||||||
- Source code and tracker links weren't being set, and updated the contributing doc.
|
- Source code and tracker links weren't being set, and updated the contributing doc.
|
||||||
|
10
README.md
10
README.md
@ -1,17 +1,17 @@
|
|||||||
# ReaderForSelfoss
|
# ReaderForSelfoss
|
||||||
|
|
||||||
|
[](https://crowdin.com/project/readerforselfoss) [](https://gitter.im/amine-bou/ReaderForSelfoss)
|
||||||
|
|
||||||
[](http://jenkins.amine-bou.fr/job/ReaderForSelfoss/)
|
[](http://jenkins.amine-bou.fr/job/ReaderForSelfoss/)
|
||||||
|
|
||||||
[](https://gitter.im/amine-bou/ReaderForSelfoss)
|
[](https://codebeat.co/projects/github-com-aminecmi-readerforselfoss-master) [](https://www.codetriage.com/aminecmi/readerforselfoss)
|
||||||
|
|
||||||
[](https://codebeat.co/projects/github-com-aminecmi-readerforselfoss-master)
|
|
||||||
|
|
||||||
[](https://www.codetriage.com/aminecmi/readerforselfoss)
|
|
||||||
|
|
||||||
This is the repo of [Reader For Selfoss](https://play.google.com/store/apps/details?id=apps.amine.bou.readerforselfoss&hl=en).
|
This is the repo of [Reader For Selfoss](https://play.google.com/store/apps/details?id=apps.amine.bou.readerforselfoss&hl=en).
|
||||||
|
|
||||||
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/)
|
||||||
|
|
||||||
|
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).
|
||||||
|
|
||||||
|
|
||||||
## Want to help ?
|
## Want to help ?
|
||||||
|
|
||||||
|
@ -23,6 +23,8 @@ def versionNameFromGit() {
|
|||||||
return gitVersion().trim()
|
return gitVersion().trim()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
apply plugin: 'org.sonarqube'
|
||||||
|
|
||||||
apply plugin: 'com.android.application'
|
apply plugin: 'com.android.application'
|
||||||
|
|
||||||
apply plugin: 'io.fabric'
|
apply plugin: 'io.fabric'
|
||||||
@ -30,12 +32,14 @@ apply plugin: 'io.fabric'
|
|||||||
apply plugin: 'kotlin-android'
|
apply plugin: 'kotlin-android'
|
||||||
|
|
||||||
repositories {
|
repositories {
|
||||||
maven { url 'https://maven.fabric.io/public' }
|
maven {
|
||||||
|
url 'https://maven.fabric.io/public'
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
android {
|
android {
|
||||||
compileSdkVersion 26
|
compileSdkVersion 26
|
||||||
buildToolsVersion "26.0.1"
|
buildToolsVersion '26.0.2'
|
||||||
defaultConfig {
|
defaultConfig {
|
||||||
applicationId "apps.amine.bou.readerforselfoss"
|
applicationId "apps.amine.bou.readerforselfoss"
|
||||||
minSdkVersion 16
|
minSdkVersion 16
|
||||||
@ -58,6 +62,7 @@ android {
|
|||||||
buildConfigField "String", "FEEDBACK_EMAIL", feedbackEmail
|
buildConfigField "String", "FEEDBACK_EMAIL", feedbackEmail
|
||||||
buildConfigField "String", "SOURCE_URL", sourceUrl
|
buildConfigField "String", "SOURCE_URL", sourceUrl
|
||||||
buildConfigField "String", "TRACKER_URL", trackerUrl
|
buildConfigField "String", "TRACKER_URL", trackerUrl
|
||||||
|
buildConfigField "String", "TRANSLATION_URL", translationUrl
|
||||||
}
|
}
|
||||||
buildTypes {
|
buildTypes {
|
||||||
release {
|
release {
|
||||||
@ -131,7 +136,7 @@ dependencies {
|
|||||||
|
|
||||||
// Retrofit + http logging + okhttp
|
// Retrofit + http logging + okhttp
|
||||||
compile 'com.squareup.retrofit2:retrofit:2.3.0'
|
compile 'com.squareup.retrofit2:retrofit:2.3.0'
|
||||||
compile 'com.squareup.okhttp3:logging-interceptor:3.8.0'
|
compile 'com.squareup.okhttp3:logging-interceptor:3.9.0'
|
||||||
compile 'com.squareup.retrofit2:converter-gson:2.3.0'
|
compile 'com.squareup.retrofit2:converter-gson:2.3.0'
|
||||||
compile 'com.burgstaller:okhttp-digest:1.12'
|
compile 'com.burgstaller:okhttp-digest:1.12'
|
||||||
|
|
||||||
@ -143,13 +148,11 @@ dependencies {
|
|||||||
compile 'org.sufficientlysecure:html-textview:3.3'
|
compile 'org.sufficientlysecure:html-textview:3.3'
|
||||||
|
|
||||||
// glide
|
// glide
|
||||||
compile('com.github.bumptech.glide:glide:4.1.0@aar') {
|
compile 'com.github.bumptech.glide:glide:4.1.1'
|
||||||
transitive = true;
|
compile 'com.github.bumptech.glide:okhttp3-integration:4.1.1'
|
||||||
}
|
|
||||||
compile('com.github.bumptech.glide:okhttp3-integration:4.1.0@aar')
|
|
||||||
|
|
||||||
// Asking politely users to rate the app
|
// Asking politely users to rate the app
|
||||||
compile 'com.github.stkent:amplify:1.5.0'
|
compile 'com.github.stkent:amplify:2.1.0'
|
||||||
|
|
||||||
// For the article reader
|
// For the article reader
|
||||||
compile 'com.klinkerapps:drag-dismiss-activity:1.4.3'
|
compile 'com.klinkerapps:drag-dismiss-activity:1.4.3'
|
||||||
@ -206,6 +209,7 @@ def initAppForSecretPropertiesIfNeeded() {
|
|||||||
entry(key: "feedbackEmail", value: System.getProperty("feedbackEmail"))
|
entry(key: "feedbackEmail", value: System.getProperty("feedbackEmail"))
|
||||||
entry(key: "sourceUrl", value: System.getProperty("sourceUrl"))
|
entry(key: "sourceUrl", value: System.getProperty("sourceUrl"))
|
||||||
entry(key: "trackerUrl", value: System.getProperty("trackerUrl"))
|
entry(key: "trackerUrl", value: System.getProperty("trackerUrl"))
|
||||||
|
entry(key: "translationUrl", value: System.getProperty("translationUrl"))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
5
app/proguard-rules.pro
vendored
5
app/proguard-rules.pro
vendored
@ -61,6 +61,11 @@
|
|||||||
|
|
||||||
# self signed glidemodule
|
# self signed glidemodule
|
||||||
-keep public class * implements com.bumptech.glide.module.GlideModule
|
-keep public class * implements com.bumptech.glide.module.GlideModule
|
||||||
|
-keep public class * extends com.bumptech.glide.AppGlideModule
|
||||||
|
-keep public enum com.bumptech.glide.load.resource.bitmap.ImageHeaderParser$** {
|
||||||
|
**[] $VALUES;
|
||||||
|
public *;
|
||||||
|
}
|
||||||
|
|
||||||
-dontwarn com.anupcowkur.reservoir.**
|
-dontwarn com.anupcowkur.reservoir.**
|
||||||
|
|
||||||
|
@ -28,7 +28,8 @@
|
|||||||
android:name=".IntroActivity"
|
android:name=".IntroActivity"
|
||||||
android:theme="@style/Theme.Intro">
|
android:theme="@style/Theme.Intro">
|
||||||
</activity>
|
</activity>
|
||||||
<activity android:name=".LoginActivity"
|
<activity
|
||||||
|
android:name=".LoginActivity"
|
||||||
android:label="@string/title_activity_login">
|
android:label="@string/title_activity_login">
|
||||||
</activity>
|
</activity>
|
||||||
<activity android:name=".HomeActivity">
|
<activity android:name=".HomeActivity">
|
||||||
@ -41,13 +42,15 @@
|
|||||||
android:name="android.support.PARENT_ACTIVITY"
|
android:name="android.support.PARENT_ACTIVITY"
|
||||||
android:value="apps.amine.bou.readerforselfoss.HomeActivity" />
|
android:value="apps.amine.bou.readerforselfoss.HomeActivity" />
|
||||||
</activity>
|
</activity>
|
||||||
<activity android:name=".SourcesActivity"
|
<activity
|
||||||
|
android:name=".SourcesActivity"
|
||||||
android:parentActivityName=".HomeActivity">
|
android:parentActivityName=".HomeActivity">
|
||||||
<meta-data
|
<meta-data
|
||||||
android:name="android.support.PARENT_ACTIVITY"
|
android:name="android.support.PARENT_ACTIVITY"
|
||||||
android:value=".HomeActivity" />
|
android:value=".HomeActivity" />
|
||||||
</activity>
|
</activity>
|
||||||
<activity android:name=".AddSourceActivity"
|
<activity
|
||||||
|
android:name=".AddSourceActivity"
|
||||||
android:parentActivityName=".SourcesActivity">
|
android:parentActivityName=".SourcesActivity">
|
||||||
<meta-data
|
<meta-data
|
||||||
android:name="android.support.PARENT_ACTIVITY"
|
android:name="android.support.PARENT_ACTIVITY"
|
||||||
@ -61,9 +64,11 @@
|
|||||||
<data android:mimeType="text/plain" />
|
<data android:mimeType="text/plain" />
|
||||||
</intent-filter>
|
</intent-filter>
|
||||||
</activity>
|
</activity>
|
||||||
<activity android:name=".ReaderActivity"
|
<activity
|
||||||
|
android:name=".ReaderActivity"
|
||||||
android:theme="@style/DragDismissTheme">
|
android:theme="@style/DragDismissTheme">
|
||||||
</activity>
|
</activity>
|
||||||
|
|
||||||
<meta-data
|
<meta-data
|
||||||
android:name="apps.amine.bou.readerforselfoss.utils.glide.SelfSignedGlideModule"
|
android:name="apps.amine.bou.readerforselfoss.utils.glide.SelfSignedGlideModule"
|
||||||
android:value="GlideModule" />
|
android:value="GlideModule" />
|
||||||
|
@ -45,7 +45,7 @@ class AddSourceActivity : AppCompatActivity() {
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
val prefs = PreferenceManager.getDefaultSharedPreferences(this)
|
val prefs = PreferenceManager.getDefaultSharedPreferences(this)
|
||||||
api = SelfossApi(this, this@AddSourceActivity, prefs.getBoolean("isSelfSignedCert", false))
|
api = SelfossApi(this, this@AddSourceActivity, prefs.getBoolean("isSelfSignedCert", false), prefs.getBoolean("should_log_everything", false))
|
||||||
} catch (e: IllegalArgumentException) {
|
} catch (e: IllegalArgumentException) {
|
||||||
mustLoginToAddSource()
|
mustLoginToAddSource()
|
||||||
}
|
}
|
||||||
|
@ -97,6 +97,7 @@ class HomeActivity : AppCompatActivity(), SearchView.OnQueryTextListener {
|
|||||||
private var maybeSearchFilter: String? = null
|
private var maybeSearchFilter: String? = null
|
||||||
private var userIdentifier: String = ""
|
private var userIdentifier: String = ""
|
||||||
private var displayAccountHeader: Boolean = false
|
private var displayAccountHeader: Boolean = false
|
||||||
|
private var infiniteScroll: Boolean = false
|
||||||
|
|
||||||
private lateinit var emptyText: TextView
|
private lateinit var emptyText: TextView
|
||||||
private lateinit var recyclerView: RecyclerView
|
private lateinit var recyclerView: RecyclerView
|
||||||
@ -114,6 +115,10 @@ class HomeActivity : AppCompatActivity(), SearchView.OnQueryTextListener {
|
|||||||
private lateinit var sharedPref: SharedPreferences
|
private lateinit var sharedPref: SharedPreferences
|
||||||
private lateinit var firebaseRemoteConfig: FirebaseRemoteConfig
|
private lateinit var firebaseRemoteConfig: FirebaseRemoteConfig
|
||||||
private lateinit var appColors: AppColors
|
private lateinit var appColors: AppColors
|
||||||
|
private var offset: Int = 0
|
||||||
|
private var firstVisible: Int = 0
|
||||||
|
private var recyclerViewScrollListener: RecyclerView.OnScrollListener? = null
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@ -147,7 +152,7 @@ class HomeActivity : AppCompatActivity(), SearchView.OnQueryTextListener {
|
|||||||
customTabActivityHelper = CustomTabActivityHelper()
|
customTabActivityHelper = CustomTabActivityHelper()
|
||||||
|
|
||||||
val dirtyPref = getSharedPreferences(Config.settingsName, Context.MODE_PRIVATE)
|
val dirtyPref = getSharedPreferences(Config.settingsName, Context.MODE_PRIVATE)
|
||||||
api = SelfossApi(this, this@HomeActivity, dirtyPref.getBoolean("isSelfSignedCert", false))
|
api = SelfossApi(this, this@HomeActivity, dirtyPref.getBoolean("isSelfSignedCert", false), dirtyPref.getBoolean("should_log_everything", false))
|
||||||
items = ArrayList()
|
items = ArrayList()
|
||||||
|
|
||||||
appColors = AppColors(this@HomeActivity)
|
appColors = AppColors(this@HomeActivity)
|
||||||
@ -294,6 +299,7 @@ class HomeActivity : AppCompatActivity(), SearchView.OnQueryTextListener {
|
|||||||
itemsNumber = sharedPref.getString("prefer_api_items_number", "200").toInt()
|
itemsNumber = sharedPref.getString("prefer_api_items_number", "200").toInt()
|
||||||
userIdentifier = sharedPref.getString("unique_id", "")
|
userIdentifier = sharedPref.getString("unique_id", "")
|
||||||
displayAccountHeader = sharedPref.getBoolean("account_header_displaying", false)
|
displayAccountHeader = sharedPref.getBoolean("account_header_displaying", false)
|
||||||
|
infiniteScroll = sharedPref.getBoolean("infinite_loading", false)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun handleDrawer(dirtyPref: SharedPreferences) {
|
private fun handleDrawer(dirtyPref: SharedPreferences) {
|
||||||
@ -374,7 +380,7 @@ class HomeActivity : AppCompatActivity(), SearchView.OnQueryTextListener {
|
|||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
for (tag in maybeTags) {
|
for (tag in maybeTags) {
|
||||||
val gd: GradientDrawable = GradientDrawable()
|
val gd = GradientDrawable()
|
||||||
gd.setColor(Color.parseColor(tag.color))
|
gd.setColor(Color.parseColor(tag.color))
|
||||||
gd.shape = GradientDrawable.RECTANGLE
|
gd.shape = GradientDrawable.RECTANGLE
|
||||||
gd.setSize(30, 30)
|
gd.setSize(30, 30)
|
||||||
@ -567,6 +573,30 @@ class HomeActivity : AppCompatActivity(), SearchView.OnQueryTextListener {
|
|||||||
recyclerView.layoutManager = mLayoutManager
|
recyclerView.layoutManager = mLayoutManager
|
||||||
recyclerView.setHasFixedSize(true)
|
recyclerView.setHasFixedSize(true)
|
||||||
|
|
||||||
|
if (infiniteScroll) {
|
||||||
|
if (recyclerViewScrollListener == null)
|
||||||
|
recyclerViewScrollListener = object: RecyclerView.OnScrollListener() {
|
||||||
|
override fun onScrolled(localRecycler: RecyclerView?, dx: Int, dy: Int) {
|
||||||
|
if (dy > 0) {
|
||||||
|
if (localRecycler != null) {
|
||||||
|
val lastVisibleItem: Int = when (mLayoutManager) {
|
||||||
|
is StaggeredGridLayoutManager -> mLayoutManager.findLastCompletelyVisibleItemPositions(null).last()
|
||||||
|
is GridLayoutManager -> mLayoutManager.findLastCompletelyVisibleItemPosition()
|
||||||
|
else -> 0
|
||||||
|
}
|
||||||
|
|
||||||
|
if (lastVisibleItem == (items.size - 1)) {
|
||||||
|
getElementsAccordingToTab(appendResults = true)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
recyclerView.clearOnScrollListeners()
|
||||||
|
recyclerView.addOnScrollListener(recyclerViewScrollListener)
|
||||||
|
}
|
||||||
|
|
||||||
bottomBar.setTabSelectedListener(object: BottomNavigationBar.OnTabSelectedListener {
|
bottomBar.setTabSelectedListener(object: BottomNavigationBar.OnTabSelectedListener {
|
||||||
override fun onTabUnselected(position: Int) = Unit
|
override fun onTabUnselected(position: Int) = Unit
|
||||||
|
|
||||||
@ -605,26 +635,31 @@ class HomeActivity : AppCompatActivity(), SearchView.OnQueryTextListener {
|
|||||||
recyclerView.visibility = View.VISIBLE
|
recyclerView.visibility = View.VISIBLE
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun getElementsAccordingToTab() =
|
private fun getElementsAccordingToTab(appendResults: Boolean = false) =
|
||||||
when (elementsShown) {
|
when (elementsShown) {
|
||||||
UNREAD_SHOWN -> getUnRead()
|
UNREAD_SHOWN -> getUnRead(appendResults)
|
||||||
READ_SHOWN -> getRead()
|
READ_SHOWN -> getRead(appendResults)
|
||||||
FAV_SHOWN -> getStarred()
|
FAV_SHOWN -> getStarred(appendResults)
|
||||||
else -> getUnRead()
|
else -> getUnRead(appendResults)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun doCallTo(toastMessage: Int, call: (String?, Long?, String?) -> Call<List<Item>>) {
|
private fun doCallTo(appendResults: Boolean, toastMessage: Int, call: (String?, Long?, String?) -> Call<List<Item>>) {
|
||||||
fun handleItemsResponse(response: Response<List<Item>>) {
|
fun handleItemsResponse(response: Response<List<Item>>) {
|
||||||
val didUpdate = (response.body() != items)
|
val didUpdate = (response.body() != items)
|
||||||
if (response.body() != null) {
|
if (response.body() != null) {
|
||||||
if (response.body() != items) {
|
if (response.body() != items) {
|
||||||
items = response.body() as ArrayList<Item>
|
if (appendResults)
|
||||||
|
items.addAll(response.body() as ArrayList<Item>)
|
||||||
|
else
|
||||||
|
items = response.body() as ArrayList<Item>
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
items = ArrayList()
|
if (!appendResults)
|
||||||
|
items = ArrayList()
|
||||||
}
|
}
|
||||||
if (didUpdate)
|
if (didUpdate)
|
||||||
handleListResult()
|
handleListResult(appendResults)
|
||||||
|
|
||||||
mayBeEmpty()
|
mayBeEmpty()
|
||||||
swipeRefreshLayout.isRefreshing = false
|
swipeRefreshLayout.isRefreshing = false
|
||||||
}
|
}
|
||||||
@ -645,22 +680,40 @@ class HomeActivity : AppCompatActivity(), SearchView.OnQueryTextListener {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun getUnRead() {
|
private fun getUnRead(appendResults: Boolean = false) {
|
||||||
|
offset = if (appendResults) (offset + itemsNumber)
|
||||||
|
else 0
|
||||||
|
firstVisible = if (appendResults) firstVisible else 0
|
||||||
elementsShown = UNREAD_SHOWN
|
elementsShown = UNREAD_SHOWN
|
||||||
doCallTo(R.string.cant_get_new_elements){t, id, f -> api.newItems(t, id, f, itemsNumber)}
|
doCallTo(appendResults, R.string.cant_get_new_elements){t, id, f -> api.newItems(t, id, f, itemsNumber, offset)}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun getRead() {
|
private fun getRead(appendResults: Boolean = false) {
|
||||||
|
offset = if (appendResults) (offset + itemsNumber)
|
||||||
|
else 0
|
||||||
|
firstVisible = if (appendResults) firstVisible else 0
|
||||||
elementsShown = READ_SHOWN
|
elementsShown = READ_SHOWN
|
||||||
doCallTo(R.string.cant_get_read){t, id, f -> api.readItems(t, id, f)}
|
doCallTo(appendResults, R.string.cant_get_read){t, id, f -> api.readItems(t, id, f, itemsNumber, offset)}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun getStarred() {
|
private fun getStarred(appendResults: Boolean = false) {
|
||||||
|
offset = if (appendResults) (offset + itemsNumber)
|
||||||
|
else 0
|
||||||
|
firstVisible = if (appendResults) firstVisible else 0
|
||||||
elementsShown = FAV_SHOWN
|
elementsShown = FAV_SHOWN
|
||||||
doCallTo(R.string.cant_get_favs){t, id, f -> api.starredItems(t, id, f)}
|
doCallTo(appendResults, R.string.cant_get_favs){t, id, f -> api.starredItems(t, id, f, itemsNumber, offset)}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun handleListResult() {
|
private fun handleListResult(appendResults: Boolean = false) {
|
||||||
|
if (appendResults) {
|
||||||
|
val oldManager = recyclerView.layoutManager
|
||||||
|
firstVisible = if ((oldManager is StaggeredGridLayoutManager)) {
|
||||||
|
oldManager.findFirstCompletelyVisibleItemPositions(null).last()
|
||||||
|
}
|
||||||
|
else
|
||||||
|
(oldManager as GridLayoutManager)?.findFirstCompletelyVisibleItemPosition()
|
||||||
|
}
|
||||||
|
|
||||||
reloadLayoutManager()
|
reloadLayoutManager()
|
||||||
|
|
||||||
val mAdapter: RecyclerView.Adapter<*>
|
val mAdapter: RecyclerView.Adapter<*>
|
||||||
@ -693,6 +746,10 @@ class HomeActivity : AppCompatActivity(), SearchView.OnQueryTextListener {
|
|||||||
recyclerView.adapter = mAdapter
|
recyclerView.adapter = mAdapter
|
||||||
mAdapter.notifyDataSetChanged()
|
mAdapter.notifyDataSetChanged()
|
||||||
|
|
||||||
|
if (appendResults) {
|
||||||
|
recyclerView.scrollToPosition(firstVisible!!)
|
||||||
|
}
|
||||||
|
|
||||||
reloadBadges()
|
reloadBadges()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -17,7 +17,7 @@ class IntroActivity : MaterialIntroActivity() {
|
|||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
|
|
||||||
AppCompatDelegate.setCompatVectorFromResourcesEnabled(true);
|
AppCompatDelegate.setCompatVectorFromResourcesEnabled(true)
|
||||||
|
|
||||||
addSlide(SlideFragmentBuilder()
|
addSlide(SlideFragmentBuilder()
|
||||||
.backgroundColor(R.color.colorPrimary)
|
.backgroundColor(R.color.colorPrimary)
|
||||||
|
@ -16,14 +16,6 @@ import android.view.MenuItem
|
|||||||
import android.view.View
|
import android.view.View
|
||||||
import android.view.inputmethod.EditorInfo
|
import android.view.inputmethod.EditorInfo
|
||||||
import android.widget.*
|
import android.widget.*
|
||||||
|
|
||||||
import com.google.firebase.analytics.FirebaseAnalytics
|
|
||||||
import com.mikepenz.aboutlibraries.Libs
|
|
||||||
import com.mikepenz.aboutlibraries.LibsBuilder
|
|
||||||
import retrofit2.Call
|
|
||||||
import retrofit2.Callback
|
|
||||||
import retrofit2.Response
|
|
||||||
|
|
||||||
import apps.amine.bou.readerforselfoss.api.selfoss.SelfossApi
|
import apps.amine.bou.readerforselfoss.api.selfoss.SelfossApi
|
||||||
import apps.amine.bou.readerforselfoss.api.selfoss.SuccessResponse
|
import apps.amine.bou.readerforselfoss.api.selfoss.SuccessResponse
|
||||||
import apps.amine.bou.readerforselfoss.utils.Config
|
import apps.amine.bou.readerforselfoss.utils.Config
|
||||||
@ -31,7 +23,12 @@ import apps.amine.bou.readerforselfoss.utils.checkAndDisplayStoreApk
|
|||||||
import apps.amine.bou.readerforselfoss.utils.isBaseUrlValid
|
import apps.amine.bou.readerforselfoss.utils.isBaseUrlValid
|
||||||
import com.crashlytics.android.Crashlytics
|
import com.crashlytics.android.Crashlytics
|
||||||
import com.ftinc.scoop.Scoop
|
import com.ftinc.scoop.Scoop
|
||||||
import io.fabric.sdk.android.Fabric
|
import com.google.firebase.analytics.FirebaseAnalytics
|
||||||
|
import com.mikepenz.aboutlibraries.Libs
|
||||||
|
import com.mikepenz.aboutlibraries.LibsBuilder
|
||||||
|
import retrofit2.Call
|
||||||
|
import retrofit2.Callback
|
||||||
|
import retrofit2.Response
|
||||||
|
|
||||||
|
|
||||||
class LoginActivity : AppCompatActivity() {
|
class LoginActivity : AppCompatActivity() {
|
||||||
@ -211,7 +208,7 @@ class LoginActivity : AppCompatActivity() {
|
|||||||
editor.putBoolean("isSelfSignedCert", isWithSelfSignedCert)
|
editor.putBoolean("isSelfSignedCert", isWithSelfSignedCert)
|
||||||
editor.apply()
|
editor.apply()
|
||||||
|
|
||||||
val api = SelfossApi(this, this@LoginActivity, isWithSelfSignedCert)
|
val api = SelfossApi(this, this@LoginActivity, isWithSelfSignedCert, isWithSelfSignedCert)
|
||||||
api.login().enqueue(object : Callback<SuccessResponse> {
|
api.login().enqueue(object : Callback<SuccessResponse> {
|
||||||
private fun preferenceError(t: Throwable) {
|
private fun preferenceError(t: Throwable) {
|
||||||
editor.remove("url")
|
editor.remove("url")
|
||||||
|
@ -12,6 +12,8 @@ import com.bumptech.glide.Glide
|
|||||||
import com.bumptech.glide.request.RequestOptions
|
import com.bumptech.glide.request.RequestOptions
|
||||||
import com.crashlytics.android.Crashlytics
|
import com.crashlytics.android.Crashlytics
|
||||||
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.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
|
||||||
@ -49,7 +51,8 @@ class MyApp : MultiDexApplication() {
|
|||||||
|
|
||||||
private fun initAmplify() {
|
private fun initAmplify() {
|
||||||
Amplify.initSharedInstance(this)
|
Amplify.initSharedInstance(this)
|
||||||
.setFeedbackEmailAddress(BuildConfig.FEEDBACK_EMAIL)
|
.setPositiveFeedbackCollectors(GooglePlayStoreFeedbackCollector())
|
||||||
|
.setCriticalFeedbackCollectors(DefaultEmailFeedbackCollector(BuildConfig.FEEDBACK_EMAIL))
|
||||||
.applyAllDefaultRules()
|
.applyAllDefaultRules()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,7 +1,5 @@
|
|||||||
package apps.amine.bou.readerforselfoss
|
package apps.amine.bou.readerforselfoss
|
||||||
|
|
||||||
import android.content.Intent
|
|
||||||
import android.net.Uri
|
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.view.LayoutInflater
|
import android.view.LayoutInflater
|
||||||
import android.view.View
|
import android.view.View
|
||||||
@ -9,23 +7,21 @@ import android.view.ViewGroup
|
|||||||
import android.widget.ImageButton
|
import android.widget.ImageButton
|
||||||
import android.widget.ImageView
|
import android.widget.ImageView
|
||||||
import android.widget.TextView
|
import android.widget.TextView
|
||||||
|
|
||||||
import com.bumptech.glide.Glide
|
|
||||||
import org.sufficientlysecure.htmltextview.HtmlHttpImageGetter
|
|
||||||
import org.sufficientlysecure.htmltextview.HtmlTextView
|
|
||||||
import retrofit2.Call
|
|
||||||
import retrofit2.Callback
|
|
||||||
import retrofit2.Response
|
|
||||||
import xyz.klinker.android.drag_dismiss.activity.DragDismissActivity
|
|
||||||
|
|
||||||
import apps.amine.bou.readerforselfoss.api.mercury.MercuryApi
|
import apps.amine.bou.readerforselfoss.api.mercury.MercuryApi
|
||||||
import apps.amine.bou.readerforselfoss.api.mercury.ParsedContent
|
import apps.amine.bou.readerforselfoss.api.mercury.ParsedContent
|
||||||
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.openItemUrl
|
import apps.amine.bou.readerforselfoss.utils.openItemUrl
|
||||||
import apps.amine.bou.readerforselfoss.utils.shareLink
|
import apps.amine.bou.readerforselfoss.utils.shareLink
|
||||||
|
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 org.sufficientlysecure.htmltextview.HtmlHttpImageGetter
|
||||||
|
import org.sufficientlysecure.htmltextview.HtmlTextView
|
||||||
|
import retrofit2.Call
|
||||||
|
import retrofit2.Callback
|
||||||
|
import retrofit2.Response
|
||||||
|
import xyz.klinker.android.drag_dismiss.activity.DragDismissActivity
|
||||||
|
|
||||||
|
|
||||||
class ReaderActivity : DragDismissActivity() {
|
class ReaderActivity : DragDismissActivity() {
|
||||||
|
@ -40,7 +40,7 @@ class SourcesActivity : AppCompatActivity() {
|
|||||||
|
|
||||||
val prefs = PreferenceManager.getDefaultSharedPreferences(this)
|
val prefs = PreferenceManager.getDefaultSharedPreferences(this)
|
||||||
|
|
||||||
val api = SelfossApi(this, this@SourcesActivity, prefs.getBoolean("isSelfSignedCert", false))
|
val api = SelfossApi(this, this@SourcesActivity, prefs.getBoolean("isSelfSignedCert", false), prefs.getBoolean("should_log_everything", false))
|
||||||
var items: ArrayList<Sources> = ArrayList()
|
var items: ArrayList<Sources> = ArrayList()
|
||||||
|
|
||||||
mFab.attachToRecyclerView(mRecyclerView)
|
mFab.attachToRecyclerView(mRecyclerView)
|
||||||
|
@ -2,20 +2,20 @@ package apps.amine.bou.readerforselfoss.api.mercury
|
|||||||
|
|
||||||
import android.os.Parcel
|
import android.os.Parcel
|
||||||
import android.os.Parcelable
|
import android.os.Parcelable
|
||||||
|
import com.google.gson.annotations.SerializedName
|
||||||
|
|
||||||
|
|
||||||
|
class ParsedContent(@SerializedName("title") val title: String,
|
||||||
class ParsedContent(val title: String,
|
@SerializedName("content") val content: String,
|
||||||
val content: String,
|
@SerializedName("date_published") val date_published: String,
|
||||||
val date_published: String,
|
@SerializedName("lead_image_url") val lead_image_url: String,
|
||||||
val lead_image_url: String,
|
@SerializedName("dek") val dek: String,
|
||||||
val dek: String,
|
@SerializedName("url") val url: String,
|
||||||
val url: String,
|
@SerializedName("domain") val domain: String,
|
||||||
val domain: String,
|
@SerializedName("excerpt") val excerpt: String,
|
||||||
val excerpt: String,
|
@SerializedName("total_pages") val total_pages: Int,
|
||||||
val total_pages: Int,
|
@SerializedName("rendered_pages") val rendered_pages: Int,
|
||||||
val rendered_pages: Int,
|
@SerializedName("next_page_url") val next_page_url: String) : Parcelable {
|
||||||
val next_page_url: String) : Parcelable {
|
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
@JvmField val CREATOR: Parcelable.Creator<ParsedContent> = object : Parcelable.Creator<ParsedContent> {
|
@JvmField val CREATOR: Parcelable.Creator<ParsedContent> = object : Parcelable.Creator<ParsedContent> {
|
||||||
|
@ -19,10 +19,13 @@ import retrofit2.converter.gson.GsonConverterFactory
|
|||||||
|
|
||||||
import apps.amine.bou.readerforselfoss.utils.Config
|
import apps.amine.bou.readerforselfoss.utils.Config
|
||||||
import apps.amine.bou.readerforselfoss.utils.getUnsafeHttpClient
|
import apps.amine.bou.readerforselfoss.utils.getUnsafeHttpClient
|
||||||
|
import okhttp3.logging.HttpLoggingInterceptor
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// codebeat:disable[ARITY,TOO_MANY_FUNCTIONS]
|
// codebeat:disable[ARITY,TOO_MANY_FUNCTIONS]
|
||||||
class SelfossApi(c: Context, callingActivity: Activity, isWithSelfSignedCert: Boolean) {
|
class SelfossApi(c: Context, callingActivity: Activity, isWithSelfSignedCert: Boolean, shouldLog: Boolean) {
|
||||||
|
|
||||||
private lateinit var service: SelfossService
|
private lateinit var service: SelfossService
|
||||||
private val config: Config = Config(c)
|
private val config: Config = Config(c)
|
||||||
@ -42,14 +45,13 @@ class SelfossApi(c: Context, callingActivity: Activity, isWithSelfSignedCert: Bo
|
|||||||
.with("basic", BasicAuthenticator(this))
|
.with("basic", BasicAuthenticator(this))
|
||||||
.build()
|
.build()
|
||||||
|
|
||||||
fun DispatchingAuthenticator.getHttpClien(isWithSelfSignedCert: Boolean): OkHttpClient {
|
fun DispatchingAuthenticator.getHttpClien(isWithSelfSignedCert: Boolean): OkHttpClient.Builder {
|
||||||
val authCache = ConcurrentHashMap<String, CachingAuthenticator>()
|
val authCache = ConcurrentHashMap<String, CachingAuthenticator>()
|
||||||
return OkHttpClient
|
return OkHttpClient
|
||||||
.Builder()
|
.Builder()
|
||||||
.maybeWithSelfSigned(isWithSelfSignedCert)
|
.maybeWithSelfSigned(isWithSelfSignedCert)
|
||||||
.authenticator(CachingAuthenticatorDecorator(this, authCache))
|
.authenticator(CachingAuthenticatorDecorator(this, authCache))
|
||||||
.addInterceptor(AuthenticationCacheInterceptor(authCache))
|
.addInterceptor(AuthenticationCacheInterceptor(authCache))
|
||||||
.build()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -69,13 +71,23 @@ class SelfossApi(c: Context, callingActivity: Activity, isWithSelfSignedCert: Bo
|
|||||||
.setLenient()
|
.setLenient()
|
||||||
.create()
|
.create()
|
||||||
|
|
||||||
|
val logging = HttpLoggingInterceptor()
|
||||||
|
|
||||||
|
logging.level = if (shouldLog)
|
||||||
|
HttpLoggingInterceptor.Level.BODY
|
||||||
|
else
|
||||||
|
HttpLoggingInterceptor.Level.NONE
|
||||||
|
|
||||||
|
val httpClient = authenticator.getHttpClien(isWithSelfSignedCert)
|
||||||
|
|
||||||
|
httpClient.addInterceptor(logging)
|
||||||
|
|
||||||
try {
|
try {
|
||||||
val retrofit =
|
val retrofit =
|
||||||
Retrofit
|
Retrofit
|
||||||
.Builder()
|
.Builder()
|
||||||
.baseUrl(config.baseUrl)
|
.baseUrl(config.baseUrl)
|
||||||
.client(authenticator.getHttpClien(isWithSelfSignedCert))
|
.client(httpClient.build())
|
||||||
.addConverterFactory(GsonConverterFactory.create(gson))
|
.addConverterFactory(GsonConverterFactory.create(gson))
|
||||||
.build()
|
.build()
|
||||||
service = retrofit.create(SelfossService::class.java)
|
service = retrofit.create(SelfossService::class.java)
|
||||||
@ -87,17 +99,17 @@ class SelfossApi(c: Context, callingActivity: Activity, isWithSelfSignedCert: Bo
|
|||||||
fun login(): Call<SuccessResponse> =
|
fun login(): Call<SuccessResponse> =
|
||||||
service.loginToSelfoss(config.userLogin, config.userPassword)
|
service.loginToSelfoss(config.userLogin, config.userPassword)
|
||||||
|
|
||||||
fun readItems(tag: String?, sourceId: Long?, search: String?): Call<List<Item>> =
|
fun readItems(tag: String?, sourceId: Long?, search: String?, itemsNumber: Int, offset: Int): Call<List<Item>> =
|
||||||
getItems("read", tag, sourceId, search, 200)
|
getItems("read", tag, sourceId, search, itemsNumber, offset)
|
||||||
|
|
||||||
fun newItems(tag: String?, sourceId: Long?, search: String?, itemsNumber: Int): Call<List<Item>> =
|
fun newItems(tag: String?, sourceId: Long?, search: String?, itemsNumber: Int, offset: Int): Call<List<Item>> =
|
||||||
getItems("unread", tag, sourceId, search, itemsNumber)
|
getItems("unread", tag, sourceId, search, itemsNumber, offset)
|
||||||
|
|
||||||
fun starredItems(tag: String?, sourceId: Long?, search: String?): Call<List<Item>> =
|
fun starredItems(tag: String?, sourceId: Long?, search: String?, itemsNumber: Int, offset: Int): Call<List<Item>> =
|
||||||
getItems("starred", tag, sourceId, search, 200)
|
getItems("starred", tag, sourceId, search, itemsNumber, offset)
|
||||||
|
|
||||||
private fun getItems(type: String, tag: String?, sourceId: Long?, search: String?, items: Int): Call<List<Item>> =
|
private fun getItems(type: String, tag: String?, sourceId: Long?, search: String?, items: Int, offset: Int): Call<List<Item>> =
|
||||||
service.getItems(type, tag, sourceId, search, userName, password, items)
|
service.getItems(type, tag, sourceId, search, userName, password, items, offset)
|
||||||
|
|
||||||
fun markItem(itemId: String): Call<SuccessResponse> =
|
fun markItem(itemId: String): Call<SuccessResponse> =
|
||||||
service.markAsRead(itemId, userName, password)
|
service.markAsRead(itemId, userName, password)
|
||||||
|
@ -7,7 +7,7 @@ import android.os.Parcelable
|
|||||||
|
|
||||||
import apps.amine.bou.readerforselfoss.utils.Config
|
import apps.amine.bou.readerforselfoss.utils.Config
|
||||||
import apps.amine.bou.readerforselfoss.utils.isEmptyOrNullOrNullString
|
import apps.amine.bou.readerforselfoss.utils.isEmptyOrNullOrNullString
|
||||||
|
import com.google.gson.annotations.SerializedName
|
||||||
|
|
||||||
|
|
||||||
private fun constructUrl(config: Config?, path: String, file: String): String {
|
private fun constructUrl(config: Config?, path: String, file: String): String {
|
||||||
@ -19,23 +19,28 @@ private fun constructUrl(config: Config?, path: String, file: String): String {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
data class Tag(val tag: String, val color: String, val unread: Int)
|
data class Tag(@SerializedName("tag") val tag: String,
|
||||||
|
@SerializedName("color") val color: String,
|
||||||
|
@SerializedName("unread") val unread: Int)
|
||||||
|
|
||||||
class SuccessResponse(val success: Boolean) {
|
class SuccessResponse(@SerializedName("success") val success: Boolean) {
|
||||||
val isSuccess: Boolean
|
val isSuccess: Boolean
|
||||||
get() = success
|
get() = success
|
||||||
}
|
}
|
||||||
|
|
||||||
class Stats(val total: Int, val unread: Int, val starred: Int)
|
class Stats(@SerializedName("total") val total: Int,
|
||||||
|
@SerializedName("unread") val unread: Int,
|
||||||
|
@SerializedName("starred") val starred: Int)
|
||||||
|
|
||||||
data class Spout(val name: String, val description: String)
|
data class Spout(@SerializedName("name") val name: String,
|
||||||
|
@SerializedName("description") val description: String)
|
||||||
|
|
||||||
data class Sources(val id: String,
|
data class Sources(@SerializedName("id") val id: String,
|
||||||
val title: String,
|
@SerializedName("title") val title: String,
|
||||||
val tags: String,
|
@SerializedName("tags") val tags: String,
|
||||||
val spout: String,
|
@SerializedName("spout") val spout: String,
|
||||||
val error: String,
|
@SerializedName("error") val error: String,
|
||||||
val icon: String) {
|
@SerializedName("icon") val icon: String) {
|
||||||
var config: Config? = null
|
var config: Config? = null
|
||||||
|
|
||||||
fun getIcon(app: Context): String {
|
fun getIcon(app: Context): String {
|
||||||
@ -47,15 +52,15 @@ data class Sources(val id: String,
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
data class Item(val id: String,
|
data class Item(@SerializedName("id") val id: String,
|
||||||
val datetime: String,
|
@SerializedName("datetime") val datetime: String,
|
||||||
val title: String,
|
@SerializedName("title") val title: String,
|
||||||
val unread: Boolean,
|
@SerializedName("unread") val unread: Boolean,
|
||||||
val starred: Boolean,
|
@SerializedName("starred") val starred: Boolean,
|
||||||
val thumbnail: String,
|
@SerializedName("thumbnail") val thumbnail: String,
|
||||||
val icon: String,
|
@SerializedName("icon") val icon: String,
|
||||||
val link: String,
|
@SerializedName("link") val link: String,
|
||||||
val sourcetitle: String) : Parcelable {
|
@SerializedName("sourcetitle") val sourcetitle: String) : Parcelable {
|
||||||
|
|
||||||
var config: Config? = null
|
var config: Config? = null
|
||||||
|
|
||||||
@ -109,14 +114,14 @@ data class Item(val id: String,
|
|||||||
// TODO: maybe find a better way to handle these kind of urls
|
// TODO: maybe find a better way to handle these kind of urls
|
||||||
fun getLinkDecoded(): String {
|
fun getLinkDecoded(): String {
|
||||||
var stringUrl: String
|
var stringUrl: String
|
||||||
if (link.startsWith("http://news.google.com/news/") || link.startsWith("https://news.google.com/news/")) {
|
stringUrl = if (link.startsWith("http://news.google.com/news/") || link.startsWith("https://news.google.com/news/")) {
|
||||||
if (link.contains("&url=")) {
|
if (link.contains("&url=")) {
|
||||||
stringUrl = link.substringAfter("&url=")
|
link.substringAfter("&url=")
|
||||||
} else {
|
} else {
|
||||||
stringUrl = this.link.replace("&", "&")
|
this.link.replace("&", "&")
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
stringUrl = this.link.replace("&", "&")
|
this.link.replace("&", "&")
|
||||||
}
|
}
|
||||||
|
|
||||||
// handle :443 => https
|
// handle :443 => https
|
||||||
|
@ -23,7 +23,8 @@ internal interface SelfossService {
|
|||||||
@Query("search") search: String?,
|
@Query("search") search: String?,
|
||||||
@Query("username") username: String,
|
@Query("username") username: String,
|
||||||
@Query("password") password: String,
|
@Query("password") password: String,
|
||||||
@Query("items") items: Int): Call<List<Item>>
|
@Query("items") items: Int,
|
||||||
|
@Query("offset") offset: Int): Call<List<Item>>
|
||||||
|
|
||||||
@POST("mark/{id}")
|
@POST("mark/{id}")
|
||||||
fun markAsRead(@Path("id") id: String,
|
fun markAsRead(@Path("id") id: String,
|
||||||
|
@ -255,6 +255,14 @@ public class SettingsActivity extends AppCompatPreferenceActivity {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
findPreference("translation").setOnPreferenceClickListener(new Preference.OnPreferenceClickListener() {
|
||||||
|
@Override
|
||||||
|
public boolean onPreferenceClick(Preference preference) {
|
||||||
|
openUrl(Uri.parse(BuildConfig.TRANSLATION_URL));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -5,8 +5,6 @@ import android.content.Context
|
|||||||
import android.support.annotation.ColorInt
|
import android.support.annotation.ColorInt
|
||||||
import android.util.TypedValue
|
import android.util.TypedValue
|
||||||
import apps.amine.bou.readerforselfoss.R
|
import apps.amine.bou.readerforselfoss.R
|
||||||
import java.lang.reflect.AccessibleObject.setAccessible
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class AppColors(a: Activity) {
|
class AppColors(a: Activity) {
|
||||||
|
@ -97,7 +97,7 @@ fun String.longHash(): Long {
|
|||||||
val l = this.length
|
val l = this.length
|
||||||
val chars = this.toCharArray()
|
val chars = this.toCharArray()
|
||||||
|
|
||||||
for (i in 0..l - 1) {
|
for (i in 0 until l) {
|
||||||
h = 31 * h + chars[i].toLong()
|
h = 31 * h + chars[i].toLong()
|
||||||
}
|
}
|
||||||
return h
|
return h
|
||||||
|
@ -16,7 +16,7 @@ fun String.toTextDrawableString(): String {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun Item.sourceAndDateText(): String {
|
fun Item.sourceAndDateText(): String {
|
||||||
var formattedDate: String = try {
|
val formattedDate: String = try {
|
||||||
" " + DateUtils.getRelativeTimeSpanString(
|
" " + DateUtils.getRelativeTimeSpanString(
|
||||||
SimpleDateFormat("yyyy-MM-dd HH:mm:ss").parse(this.datetime).time,
|
SimpleDateFormat("yyyy-MM-dd HH:mm:ss").parse(this.datetime).time,
|
||||||
Date().time,
|
Date().time,
|
||||||
|
@ -7,14 +7,10 @@ import android.content.Intent
|
|||||||
import android.graphics.BitmapFactory
|
import android.graphics.BitmapFactory
|
||||||
import android.net.Uri
|
import android.net.Uri
|
||||||
import android.support.customtabs.CustomTabsIntent
|
import android.support.customtabs.CustomTabsIntent
|
||||||
|
|
||||||
import xyz.klinker.android.drag_dismiss.DragDismissIntentBuilder
|
|
||||||
|
|
||||||
import apps.amine.bou.readerforselfoss.R
|
import apps.amine.bou.readerforselfoss.R
|
||||||
import apps.amine.bou.readerforselfoss.ReaderActivity
|
import apps.amine.bou.readerforselfoss.ReaderActivity
|
||||||
import apps.amine.bou.readerforselfoss.api.selfoss.Item
|
|
||||||
import apps.amine.bou.readerforselfoss.utils.customtabs.CustomTabActivityHelper
|
import apps.amine.bou.readerforselfoss.utils.customtabs.CustomTabActivityHelper
|
||||||
|
import xyz.klinker.android.drag_dismiss.DragDismissIntentBuilder
|
||||||
|
|
||||||
|
|
||||||
fun Context.buildCustomTabsIntent(): CustomTabsIntent {
|
fun Context.buildCustomTabsIntent(): CustomTabsIntent {
|
||||||
|
@ -31,7 +31,7 @@
|
|||||||
<string name="cant_get_read">"Impossible de récupérer les éléments lus."</string>
|
<string name="cant_get_read">"Impossible de récupérer les éléments lus."</string>
|
||||||
<string name="nothing_here">"Il n'y a rien ici !"</string>
|
<string name="nothing_here">"Il n'y a rien ici !"</string>
|
||||||
<string name="tab_new">"Non lus"</string>
|
<string name="tab_new">"Non lus"</string>
|
||||||
<string name="tab_read">"Lus"</string>
|
<string name="tab_read">"Tous"</string>
|
||||||
<string name="tab_favs">"Favoris"</string>
|
<string name="tab_favs">"Favoris"</string>
|
||||||
<string name="action_about">"À propos"</string>
|
<string name="action_about">"À propos"</string>
|
||||||
<string name="marked_as_read">"Marqué comme lu"</string>
|
<string name="marked_as_read">"Marqué comme lu"</string>
|
||||||
@ -147,4 +147,9 @@
|
|||||||
<string name="unique_id_to_clipboard">Texte copié</string>
|
<string name="unique_id_to_clipboard">Texte copié</string>
|
||||||
<string name="display_header_drawer_summary">Afficher une entête avec l\'url de votre instance de Selfoss en haut du drawer lateral.</string>
|
<string name="display_header_drawer_summary">Afficher une entête avec l\'url de votre instance de Selfoss en haut du drawer lateral.</string>
|
||||||
<string name="display_header_drawer_title">Entête de compte</string>
|
<string name="display_header_drawer_title">Entête de compte</string>
|
||||||
|
<string name="login_everything_title">Log de tous les appels à l\'API</string>
|
||||||
|
<string name="login_everything_off">Aucun appel à l\'API ne sera logué</string>
|
||||||
|
<string name="login_everything_on">Tous les appels à l\'API vont êtres logués</string>
|
||||||
|
<string name="pref_general_infinite_loading_title">(BETA) Charger plus d\'articles au scroll</string>
|
||||||
|
<string name="translation">Traduction</string>
|
||||||
</resources>
|
</resources>
|
@ -148,4 +148,9 @@
|
|||||||
<string
|
<string
|
||||||
name="display_header_drawer_summary">Display a header with the selfoss instance url on the lateral drawer.</string>
|
name="display_header_drawer_summary">Display a header with the selfoss instance url on the lateral drawer.</string>
|
||||||
<string name="display_header_drawer_title">Account header</string>
|
<string name="display_header_drawer_title">Account header</string>
|
||||||
|
<string name="login_everything_title">Logging every api calls</string>
|
||||||
|
<string name="login_everything_off">No api call will be logged</string>
|
||||||
|
<string name="login_everything_on">This will log every api call for debug purpose.</string>
|
||||||
|
<string name="pref_general_infinite_loading_title">(BETA) Load more articles on scroll</string>
|
||||||
|
<string name="translation">Vertaling</string>
|
||||||
</resources>
|
</resources>
|
@ -31,7 +31,7 @@
|
|||||||
<string name="cant_get_read">"Can't get read articles"</string>
|
<string name="cant_get_read">"Can't get read articles"</string>
|
||||||
<string name="nothing_here">"Nothing here"</string>
|
<string name="nothing_here">"Nothing here"</string>
|
||||||
<string name="tab_new">"New"</string>
|
<string name="tab_new">"New"</string>
|
||||||
<string name="tab_read">"Read"</string>
|
<string name="tab_read">"All"</string>
|
||||||
<string name="tab_favs">"Favorites"</string>
|
<string name="tab_favs">"Favorites"</string>
|
||||||
<string name="action_about">"About"</string>
|
<string name="action_about">"About"</string>
|
||||||
<string name="marked_as_read">"Item read"</string>
|
<string name="marked_as_read">"Item read"</string>
|
||||||
@ -150,4 +150,9 @@
|
|||||||
<string
|
<string
|
||||||
name="display_header_drawer_summary">Display a header with the selfoss instance url on the lateral drawer.</string>
|
name="display_header_drawer_summary">Display a header with the selfoss instance url on the lateral drawer.</string>
|
||||||
<string name="display_header_drawer_title">Account header</string>
|
<string name="display_header_drawer_title">Account header</string>
|
||||||
|
<string name="login_everything_title">Logging every api calls</string>
|
||||||
|
<string name="login_everything_on">This will log every api call for debug purpose.</string>
|
||||||
|
<string name="login_everything_off">No api call will be logged</string>
|
||||||
|
<string name="pref_general_infinite_loading_title">(BETA) Load more articles on scroll</string>
|
||||||
|
<string name="translation">Translation</string>
|
||||||
</resources>
|
</resources>
|
@ -1,6 +1,13 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">
|
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
|
||||||
|
<SwitchPreference
|
||||||
|
android:defaultValue="false"
|
||||||
|
android:key="should_log_everything"
|
||||||
|
android:summaryOff="@string/login_everything_off"
|
||||||
|
android:summaryOn="@string/login_everything_on"
|
||||||
|
android:title="@string/login_everything_title" />
|
||||||
|
|
||||||
<SwitchPreference
|
<SwitchPreference
|
||||||
android:defaultValue="false"
|
android:defaultValue="false"
|
||||||
android:key="loging_debug"
|
android:key="loging_debug"
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
<PreferenceScreen xmlns:tools="http://schemas.android.com/tools"
|
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
xmlns:android="http://schemas.android.com/apk/res/android">
|
|
||||||
|
|
||||||
<!--<SwitchPreference
|
<!--<SwitchPreference
|
||||||
android:defaultValue="false"
|
android:defaultValue="false"
|
||||||
@ -18,6 +17,11 @@
|
|||||||
android:selectAllOnFocus="true"
|
android:selectAllOnFocus="true"
|
||||||
android:singleLine="true"
|
android:singleLine="true"
|
||||||
android:title="@string/pref_api_items_number_title" />
|
android:title="@string/pref_api_items_number_title" />
|
||||||
|
|
||||||
|
<SwitchPreference
|
||||||
|
android:defaultValue="false"
|
||||||
|
android:key="infinite_loading"
|
||||||
|
android:title="@string/pref_general_infinite_loading_title" />
|
||||||
<PreferenceCategory
|
<PreferenceCategory
|
||||||
android:title="@string/pref_general_category_links">
|
android:title="@string/pref_general_category_links">
|
||||||
|
|
||||||
|
@ -7,4 +7,7 @@
|
|||||||
|
|
||||||
<Preference android:title="@string/source_code"
|
<Preference android:title="@string/source_code"
|
||||||
android:key="sourceLink" />
|
android:key="sourceLink" />
|
||||||
|
|
||||||
|
<Preference android:title="@string/translation"
|
||||||
|
android:key="translation" />
|
||||||
</PreferenceScreen>
|
</PreferenceScreen>
|
@ -5,14 +5,20 @@ buildscript {
|
|||||||
repositories {
|
repositories {
|
||||||
jcenter()
|
jcenter()
|
||||||
google()
|
google()
|
||||||
|
maven {
|
||||||
|
url "https://jitpack.io"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
dependencies {
|
dependencies {
|
||||||
classpath 'com.android.tools.build:gradle:3.0.0-beta5'
|
classpath 'com.android.tools.build:gradle:3.0.0-rc2'
|
||||||
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
|
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
|
||||||
|
|
||||||
// NOTE: Do not place your application dependencies here; they belong
|
// NOTE: Do not place your application dependencies here; they belong
|
||||||
// in the individual module build.gradle files
|
// in the individual module build.gradle files
|
||||||
classpath 'com.google.gms:google-services:3.1.0'
|
classpath 'com.google.gms:google-services:3.1.0'
|
||||||
|
|
||||||
|
// Not the official version https://stackoverflow.com/questions/46525040/sonarqube-android-not-working-for-gradle-3-0-0/46813528#46813528
|
||||||
|
classpath "com.github.Shusshu:sonar-scanner-gradle:feature~support-android-gradle-3-SNAPSHOT"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
5
crowdin.yml
Normal file
5
crowdin.yml
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
files:
|
||||||
|
- source: /app/src/main/res/values/strings.xml
|
||||||
|
translation: /app/src/main/res/values-%two_letters_code%/%original_file_name%
|
||||||
|
translate_attributes: '0'
|
||||||
|
content_segmentation: '0'
|
Reference in New Issue
Block a user