diff --git a/CHANGELOG.md b/CHANGELOG.md index 54942dd..5577434 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,11 @@ +**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** diff --git a/app/build.gradle b/app/build.gradle index f9db75f..86f1b70 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -131,7 +131,7 @@ dependencies { // Retrofit + http logging + okhttp 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.burgstaller:okhttp-digest:1.12' @@ -143,10 +143,8 @@ dependencies { compile 'org.sufficientlysecure:html-textview:3.3' // glide - compile('com.github.bumptech.glide:glide:4.1.0@aar') { - transitive = true; - } - compile('com.github.bumptech.glide:okhttp3-integration:4.1.0@aar') + compile 'com.github.bumptech.glide:glide:4.1.1' + compile 'com.github.bumptech.glide:okhttp3-integration:4.1.1' // Asking politely users to rate the app compile 'com.github.stkent:amplify:2.1.0' diff --git a/app/proguard-rules.pro b/app/proguard-rules.pro index 65e1e38..bac48d3 100644 --- a/app/proguard-rules.pro +++ b/app/proguard-rules.pro @@ -61,6 +61,11 @@ # self signed 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.** diff --git a/app/src/main/java/apps/amine/bou/readerforselfoss/HomeActivity.kt b/app/src/main/java/apps/amine/bou/readerforselfoss/HomeActivity.kt index d7e6f27..42596fb 100644 --- a/app/src/main/java/apps/amine/bou/readerforselfoss/HomeActivity.kt +++ b/app/src/main/java/apps/amine/bou/readerforselfoss/HomeActivity.kt @@ -97,6 +97,7 @@ class HomeActivity : AppCompatActivity(), SearchView.OnQueryTextListener { private var maybeSearchFilter: String? = null private var userIdentifier: String = "" private var displayAccountHeader: Boolean = false + private var infiniteScroll: Boolean = false private lateinit var emptyText: TextView private lateinit var recyclerView: RecyclerView @@ -114,6 +115,10 @@ class HomeActivity : AppCompatActivity(), SearchView.OnQueryTextListener { private lateinit var sharedPref: SharedPreferences private lateinit var firebaseRemoteConfig: FirebaseRemoteConfig private lateinit var appColors: AppColors + private var offset: Int = 0 + private var firstVisible: Int = 0 + private var recyclerViewScrollListener: RecyclerView.OnScrollListener? = null + @@ -294,6 +299,7 @@ class HomeActivity : AppCompatActivity(), SearchView.OnQueryTextListener { itemsNumber = sharedPref.getString("prefer_api_items_number", "200").toInt() userIdentifier = sharedPref.getString("unique_id", "") displayAccountHeader = sharedPref.getBoolean("account_header_displaying", false) + infiniteScroll = sharedPref.getBoolean("infinite_loading", false) } private fun handleDrawer(dirtyPref: SharedPreferences) { @@ -374,7 +380,7 @@ class HomeActivity : AppCompatActivity(), SearchView.OnQueryTextListener { } else { for (tag in maybeTags) { - val gd: GradientDrawable = GradientDrawable() + val gd = GradientDrawable() gd.setColor(Color.parseColor(tag.color)) gd.shape = GradientDrawable.RECTANGLE gd.setSize(30, 30) @@ -567,6 +573,30 @@ class HomeActivity : AppCompatActivity(), SearchView.OnQueryTextListener { recyclerView.layoutManager = mLayoutManager 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 { override fun onTabUnselected(position: Int) = Unit @@ -605,26 +635,31 @@ class HomeActivity : AppCompatActivity(), SearchView.OnQueryTextListener { recyclerView.visibility = View.VISIBLE } - private fun getElementsAccordingToTab() = + private fun getElementsAccordingToTab(appendResults: Boolean = false) = when (elementsShown) { - UNREAD_SHOWN -> getUnRead() - READ_SHOWN -> getRead() - FAV_SHOWN -> getStarred() - else -> getUnRead() + UNREAD_SHOWN -> getUnRead(appendResults) + READ_SHOWN -> getRead(appendResults) + FAV_SHOWN -> getStarred(appendResults) + else -> getUnRead(appendResults) } - private fun doCallTo(toastMessage: Int, call: (String?, Long?, String?) -> Call>) { + private fun doCallTo(appendResults: Boolean, toastMessage: Int, call: (String?, Long?, String?) -> Call>) { fun handleItemsResponse(response: Response>) { val didUpdate = (response.body() != items) if (response.body() != null) { if (response.body() != items) { - items = response.body() as ArrayList + if (appendResults) + items.addAll(response.body() as ArrayList) + else + items = response.body() as ArrayList } } else { - items = ArrayList() + if (!appendResults) + items = ArrayList() } if (didUpdate) - handleListResult() + handleListResult(appendResults) + mayBeEmpty() 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 - 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 - 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 - 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() val mAdapter: RecyclerView.Adapter<*> @@ -693,6 +746,10 @@ class HomeActivity : AppCompatActivity(), SearchView.OnQueryTextListener { recyclerView.adapter = mAdapter mAdapter.notifyDataSetChanged() + if (appendResults) { + recyclerView.scrollToPosition(firstVisible!!) + } + reloadBadges() } diff --git a/app/src/main/java/apps/amine/bou/readerforselfoss/IntroActivity.kt b/app/src/main/java/apps/amine/bou/readerforselfoss/IntroActivity.kt index 641abb4..16aa015 100644 --- a/app/src/main/java/apps/amine/bou/readerforselfoss/IntroActivity.kt +++ b/app/src/main/java/apps/amine/bou/readerforselfoss/IntroActivity.kt @@ -17,7 +17,7 @@ class IntroActivity : MaterialIntroActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) - AppCompatDelegate.setCompatVectorFromResourcesEnabled(true); + AppCompatDelegate.setCompatVectorFromResourcesEnabled(true) addSlide(SlideFragmentBuilder() .backgroundColor(R.color.colorPrimary) diff --git a/app/src/main/java/apps/amine/bou/readerforselfoss/LoginActivity.kt b/app/src/main/java/apps/amine/bou/readerforselfoss/LoginActivity.kt index 7c538c5..7e62b7e 100644 --- a/app/src/main/java/apps/amine/bou/readerforselfoss/LoginActivity.kt +++ b/app/src/main/java/apps/amine/bou/readerforselfoss/LoginActivity.kt @@ -16,14 +16,6 @@ import android.view.MenuItem import android.view.View import android.view.inputmethod.EditorInfo 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.SuccessResponse 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 com.crashlytics.android.Crashlytics 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() { diff --git a/app/src/main/java/apps/amine/bou/readerforselfoss/ReaderActivity.kt b/app/src/main/java/apps/amine/bou/readerforselfoss/ReaderActivity.kt index c91c0b3..077c93f 100644 --- a/app/src/main/java/apps/amine/bou/readerforselfoss/ReaderActivity.kt +++ b/app/src/main/java/apps/amine/bou/readerforselfoss/ReaderActivity.kt @@ -1,7 +1,5 @@ package apps.amine.bou.readerforselfoss -import android.content.Intent -import android.net.Uri import android.os.Bundle import android.view.LayoutInflater import android.view.View @@ -9,23 +7,21 @@ import android.view.ViewGroup import android.widget.ImageButton import android.widget.ImageView 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.ParsedContent import apps.amine.bou.readerforselfoss.utils.buildCustomTabsIntent import apps.amine.bou.readerforselfoss.utils.customtabs.CustomTabActivityHelper import apps.amine.bou.readerforselfoss.utils.openItemUrl import apps.amine.bou.readerforselfoss.utils.shareLink +import com.bumptech.glide.Glide import com.bumptech.glide.request.RequestOptions 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() { diff --git a/app/src/main/java/apps/amine/bou/readerforselfoss/api/selfoss/SelfossApi.kt b/app/src/main/java/apps/amine/bou/readerforselfoss/api/selfoss/SelfossApi.kt index 22cefe6..9039ef8 100644 --- a/app/src/main/java/apps/amine/bou/readerforselfoss/api/selfoss/SelfossApi.kt +++ b/app/src/main/java/apps/amine/bou/readerforselfoss/api/selfoss/SelfossApi.kt @@ -99,17 +99,17 @@ class SelfossApi(c: Context, callingActivity: Activity, isWithSelfSignedCert: Bo fun login(): Call = service.loginToSelfoss(config.userLogin, config.userPassword) - fun readItems(tag: String?, sourceId: Long?, search: String?): Call> = - getItems("read", tag, sourceId, search, 200) + fun readItems(tag: String?, sourceId: Long?, search: String?, itemsNumber: Int, offset: Int): Call> = + getItems("read", tag, sourceId, search, itemsNumber, offset) - fun newItems(tag: String?, sourceId: Long?, search: String?, itemsNumber: Int): Call> = - getItems("unread", tag, sourceId, search, itemsNumber) + fun newItems(tag: String?, sourceId: Long?, search: String?, itemsNumber: Int, offset: Int): Call> = + getItems("unread", tag, sourceId, search, itemsNumber, offset) - fun starredItems(tag: String?, sourceId: Long?, search: String?): Call> = - getItems("starred", tag, sourceId, search, 200) + fun starredItems(tag: String?, sourceId: Long?, search: String?, itemsNumber: Int, offset: Int): Call> = + getItems("starred", tag, sourceId, search, itemsNumber, offset) - private fun getItems(type: String, tag: String?, sourceId: Long?, search: String?, items: Int): Call> = - service.getItems(type, tag, sourceId, search, userName, password, items) + private fun getItems(type: String, tag: String?, sourceId: Long?, search: String?, items: Int, offset: Int): Call> = + service.getItems(type, tag, sourceId, search, userName, password, items, offset) fun markItem(itemId: String): Call = service.markAsRead(itemId, userName, password) diff --git a/app/src/main/java/apps/amine/bou/readerforselfoss/api/selfoss/SelfossModels.kt b/app/src/main/java/apps/amine/bou/readerforselfoss/api/selfoss/SelfossModels.kt index f1f12e2..84ff9c0 100644 --- a/app/src/main/java/apps/amine/bou/readerforselfoss/api/selfoss/SelfossModels.kt +++ b/app/src/main/java/apps/amine/bou/readerforselfoss/api/selfoss/SelfossModels.kt @@ -114,14 +114,14 @@ data class Item(@SerializedName("id") val id: String, // TODO: maybe find a better way to handle these kind of urls fun getLinkDecoded(): 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=")) { - stringUrl = link.substringAfter("&url=") + link.substringAfter("&url=") } else { - stringUrl = this.link.replace("&", "&") + this.link.replace("&", "&") } } else { - stringUrl = this.link.replace("&", "&") + this.link.replace("&", "&") } // handle :443 => https diff --git a/app/src/main/java/apps/amine/bou/readerforselfoss/api/selfoss/SelfossService.kt b/app/src/main/java/apps/amine/bou/readerforselfoss/api/selfoss/SelfossService.kt index 0114d0b..1d8488b 100644 --- a/app/src/main/java/apps/amine/bou/readerforselfoss/api/selfoss/SelfossService.kt +++ b/app/src/main/java/apps/amine/bou/readerforselfoss/api/selfoss/SelfossService.kt @@ -23,7 +23,8 @@ internal interface SelfossService { @Query("search") search: String?, @Query("username") username: String, @Query("password") password: String, - @Query("items") items: Int): Call> + @Query("items") items: Int, + @Query("offset") offset: Int): Call> @POST("mark/{id}") fun markAsRead(@Path("id") id: String, diff --git a/app/src/main/java/apps/amine/bou/readerforselfoss/themes/AppColors.kt b/app/src/main/java/apps/amine/bou/readerforselfoss/themes/AppColors.kt index e9ca76a..43ae8ef 100644 --- a/app/src/main/java/apps/amine/bou/readerforselfoss/themes/AppColors.kt +++ b/app/src/main/java/apps/amine/bou/readerforselfoss/themes/AppColors.kt @@ -5,8 +5,6 @@ import android.content.Context import android.support.annotation.ColorInt import android.util.TypedValue import apps.amine.bou.readerforselfoss.R -import java.lang.reflect.AccessibleObject.setAccessible - class AppColors(a: Activity) { diff --git a/app/src/main/java/apps/amine/bou/readerforselfoss/utils/AppUtils.kt b/app/src/main/java/apps/amine/bou/readerforselfoss/utils/AppUtils.kt index 0f88da2..9db6c06 100644 --- a/app/src/main/java/apps/amine/bou/readerforselfoss/utils/AppUtils.kt +++ b/app/src/main/java/apps/amine/bou/readerforselfoss/utils/AppUtils.kt @@ -97,7 +97,7 @@ fun String.longHash(): Long { val l = this.length val chars = this.toCharArray() - for (i in 0..l - 1) { + for (i in 0 until l) { h = 31 * h + chars[i].toLong() } return h diff --git a/app/src/main/java/apps/amine/bou/readerforselfoss/utils/ItemsUtils.kt b/app/src/main/java/apps/amine/bou/readerforselfoss/utils/ItemsUtils.kt index 4a598fa..f4eaa3a 100644 --- a/app/src/main/java/apps/amine/bou/readerforselfoss/utils/ItemsUtils.kt +++ b/app/src/main/java/apps/amine/bou/readerforselfoss/utils/ItemsUtils.kt @@ -16,7 +16,7 @@ fun String.toTextDrawableString(): String { } fun Item.sourceAndDateText(): String { - var formattedDate: String = try { + val formattedDate: String = try { " " + DateUtils.getRelativeTimeSpanString( SimpleDateFormat("yyyy-MM-dd HH:mm:ss").parse(this.datetime).time, Date().time, diff --git a/app/src/main/java/apps/amine/bou/readerforselfoss/utils/LinksUtils.kt b/app/src/main/java/apps/amine/bou/readerforselfoss/utils/LinksUtils.kt index c033d3c..351601b 100644 --- a/app/src/main/java/apps/amine/bou/readerforselfoss/utils/LinksUtils.kt +++ b/app/src/main/java/apps/amine/bou/readerforselfoss/utils/LinksUtils.kt @@ -7,14 +7,10 @@ import android.content.Intent import android.graphics.BitmapFactory import android.net.Uri import android.support.customtabs.CustomTabsIntent - -import xyz.klinker.android.drag_dismiss.DragDismissIntentBuilder - import apps.amine.bou.readerforselfoss.R import apps.amine.bou.readerforselfoss.ReaderActivity -import apps.amine.bou.readerforselfoss.api.selfoss.Item import apps.amine.bou.readerforselfoss.utils.customtabs.CustomTabActivityHelper - +import xyz.klinker.android.drag_dismiss.DragDismissIntentBuilder fun Context.buildCustomTabsIntent(): CustomTabsIntent { diff --git a/app/src/main/res/values-fr/strings.xml b/app/src/main/res/values-fr/strings.xml index 5056ba9..b4d96e5 100644 --- a/app/src/main/res/values-fr/strings.xml +++ b/app/src/main/res/values-fr/strings.xml @@ -150,4 +150,5 @@ Log de tous les appels à l\'API Aucun appel à l\'API ne sera logué Tous les appels à l\'API vont êtres logués + (BETA) Charger plus d\'articles au scroll \ No newline at end of file diff --git a/app/src/main/res/values-nl/strings.xml b/app/src/main/res/values-nl/strings.xml index b2f7be7..545dd9b 100644 --- a/app/src/main/res/values-nl/strings.xml +++ b/app/src/main/res/values-nl/strings.xml @@ -151,4 +151,5 @@ Logging every api calls No api call will be logged This will log every api call for debug purpose. + (BETA) Load more articles on scroll \ No newline at end of file diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 0ba49fd..a4172fe 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -153,4 +153,5 @@ Logging every api calls This will log every api call for debug purpose. No api call will be logged + (BETA) Load more articles on scroll \ No newline at end of file diff --git a/app/src/main/res/xml/pref_general.xml b/app/src/main/res/xml/pref_general.xml index 14c2ac3..f007212 100644 --- a/app/src/main/res/xml/pref_general.xml +++ b/app/src/main/res/xml/pref_general.xml @@ -1,5 +1,4 @@ - +