Compare commits

..

23 Commits

Author SHA1 Message Date
4565079f29 More filtering with the search toolbar. 2017-06-07 08:59:13 +02:00
3482092cb2 Filters not reset on resume. 2017-06-07 07:35:55 +02:00
2df5e52de0 Fixes #6 2017-06-07 07:28:17 +02:00
0ef4fc67fa Fixing cache issue. May need some real solution. 2017-06-06 21:13:46 +02:00
e2bfd549d3 Build and changelog. 2017-06-06 20:04:00 +02:00
7071af5fa5 Added back unread count. 2017-06-06 20:02:05 +02:00
95f267f701 Switched primary <=> secodary items for drawer.
Added source icon fetching.
2017-06-06 20:01:33 +02:00
f363bbcc0c Fixed an issue with the article reader. 2017-06-06 18:54:17 +02:00
65a912f271 Finally fixed #16 and fixed #17. 2017-06-05 21:17:51 +02:00
758661f5fb Fixed #16, fixes #17 and fixes #19 2017-06-05 18:50:19 +02:00
2d3c297726 Fixes #14 and fixes #15 2017-06-04 20:26:05 +02:00
ca85e3d3ed Changelog for previous version. 2017-06-04 18:24:31 +02:00
2190ad0387 Forced nl strings to english. 2017-06-04 18:13:29 +02:00
0d067e05af Fixed drawable on landscape and extracted strings to translation. 2017-06-04 18:09:02 +02:00
c3305b7523 Moved some menu items to the drawable. 2017-06-04 16:44:31 +02:00
fb84b31122 Drawer with caching. 2017-06-04 16:09:42 +02:00
9d40026ef7 Build and changeelog. 2017-06-04 09:47:45 +02:00
041a225992 Updated Custom tab code and added buttons at the bottom of the article viewer. 2017-06-03 22:52:01 +02:00
b42dc7f87c Update README.md 2017-05-30 20:37:27 +02:00
0283e49c27 Testing circle ci. 2017-05-30 20:35:01 +02:00
e5e1b2f5a5 Update README.md 2017-05-29 19:42:38 +02:00
813a0ae475 Update README.md 2017-05-29 19:13:42 +02:00
f0e036cdd8 Update README.md 2017-05-28 18:31:39 +02:00
35 changed files with 1027 additions and 187 deletions

View File

@ -1,3 +1,17 @@
**1.5.1**
- Added a drawer for filtering sources and tags.
- You can now search for items from the toolbar.
**1.5.0.2**
- If the content in the article viewer is empty, the article will open in a custom tab.
- Added a share button, and an "open in browser" button to the bottom of the article viewer.
- Updated custom tab code.
**1.5.0.1**
- The release APK wasn't working at all.

View File

@ -1,5 +1,7 @@
# ReaderForSelfoss
[![CircleCI](https://circleci.com/gh/aminecmi/ReaderforSelfoss/tree/master.svg?style=svg)](https://circleci.com/gh/aminecmi/ReaderforSelfoss/tree/master)
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/)
@ -26,4 +28,3 @@ You'll have to:
- [Check what changed](https://github.com/aminecmi/ReaderforSelfoss/blob/master/CHANGELOG.md)
- [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)
- [Help me translate the app](https://poeditor.com/join/project/viHr8ujJ7S)

View File

@ -25,8 +25,8 @@ android {
applicationId "apps.amine.bou.readerforselfoss"
minSdkVersion 16
targetSdkVersion 25
versionCode 1501
versionName "1.5.0.1"
versionCode 1510
versionName "1.5.1"
// Enabling multidex support.
multiDexEnabled true
@ -98,7 +98,7 @@ dependencies {
compile 'com.burgstaller:okhttp-digest:1.12'
// Material-ish things
compile 'com.roughike:bottom-bar:2.2.0'
compile 'com.roughike:bottom-bar:2.3.1'
compile 'com.melnykov:floatingactionbutton:1.3.0'
compile 'com.github.jd-alexander:LikeButton:0.2.1'
compile 'com.amulyakhare:com.amulyakhare.textdrawable:1.0.1'
@ -113,12 +113,28 @@ dependencies {
// For the article reader
compile 'com.klinkerapps:drag-dismiss-activity:1.4.0'
// Drawer
compile('com.mikepenz:materialdrawer:5.9.2@aar') {
transitive = true
}
compile 'com.anupcowkur:reservoir:3.1.0'
}
apply plugin: 'com.google.gms.google-services'
afterEvaluate {
initFabricPropertiesIfNeeded()
}
apply plugin: 'com.google.gms.google-services'
def initFabricPropertiesIfNeeded() {
def propertiesFile = file('fabric.properties')
if (!propertiesFile.exists()) {
def commentMessage = "This is autogenerated fabric property from system environment to prevent key to be committed to source control."
ant.propertyfile(file: "fabric.properties", comment: commentMessage) {
entry(key: "apiSecret", value: crashlyticsdemoApisecret)
entry(key: "apiKey", value: crashlyticsdemoApikey)
}
}
}

View File

@ -31,7 +31,8 @@
<activity android:name=".LoginActivity"
android:label="@string/title_activity_login">
</activity>
<activity android:name=".HomeActivity">
<activity android:name=".HomeActivity"
android:theme="@style/NoBar">
</activity>
<activity
android:name=".settings.SettingsActivity"

View File

@ -4,30 +4,34 @@ import android.app.Activity
import android.content.Context
import android.content.Intent
import android.content.SharedPreferences
import android.graphics.Color
import android.graphics.drawable.GradientDrawable
import android.net.Uri
import android.os.Bundle
import android.preference.PreferenceManager
import android.support.design.widget.CoordinatorLayout
import android.support.v4.view.MenuItemCompat
import android.support.v4.widget.SwipeRefreshLayout
import android.support.v7.app.AppCompatActivity
import android.support.v7.widget.GridLayoutManager
import android.support.v7.widget.RecyclerView
import android.support.v7.widget.StaggeredGridLayoutManager
import android.support.v7.widget.*
import android.support.v7.widget.helper.ItemTouchHelper
import android.view.Menu
import android.view.MenuItem
import android.view.View
import android.widget.Toast
import apps.amine.bou.readerforselfoss.adapters.ItemCardAdapter
import apps.amine.bou.readerforselfoss.adapters.ItemListAdapter
import apps.amine.bou.readerforselfoss.api.selfoss.Item
import apps.amine.bou.readerforselfoss.api.selfoss.SelfossApi
import apps.amine.bou.readerforselfoss.api.selfoss.Stats
import apps.amine.bou.readerforselfoss.api.selfoss.SuccessResponse
import apps.amine.bou.readerforselfoss.api.selfoss.*
import apps.amine.bou.readerforselfoss.settings.SettingsActivity
import apps.amine.bou.readerforselfoss.utils.Config
import apps.amine.bou.readerforselfoss.utils.checkAndDisplayStoreApk
import apps.amine.bou.readerforselfoss.utils.checkApkVersion
import apps.amine.bou.readerforselfoss.utils.customtabs.CustomTabActivityHelper
import apps.amine.bou.readerforselfoss.utils.drawer.CustomUrlPrimaryDrawerItem
import apps.amine.bou.readerforselfoss.utils.longHash
import com.anupcowkur.reservoir.Reservoir
import com.anupcowkur.reservoir.ReservoirGetCallback
import com.anupcowkur.reservoir.ReservoirPutCallback
import com.crashlytics.android.answers.Answers
import com.crashlytics.android.answers.InviteEvent
import com.github.stkent.amplify.prompt.DefaultLayoutPromptView
@ -36,21 +40,30 @@ import com.google.android.gms.appinvite.AppInviteInvitation
import com.google.android.gms.common.ConnectionResult
import com.google.android.gms.common.GoogleApiAvailability
import com.google.firebase.remoteconfig.FirebaseRemoteConfig
import com.google.gson.reflect.TypeToken
import com.mikepenz.aboutlibraries.Libs
import com.mikepenz.aboutlibraries.LibsBuilder
import com.mikepenz.materialdrawer.Drawer
import com.mikepenz.materialdrawer.DrawerBuilder
import com.mikepenz.materialdrawer.holder.BadgeStyle
import com.mikepenz.materialdrawer.model.DividerDrawerItem
import com.mikepenz.materialdrawer.model.PrimaryDrawerItem
import com.mikepenz.materialdrawer.model.SecondaryDrawerItem
import com.roughike.bottombar.BottomBar
import com.roughike.bottombar.BottomBarTab
import retrofit2.Call
import retrofit2.Callback
import retrofit2.Response
import java.lang.Exception
class HomeActivity : AppCompatActivity() {
class HomeActivity : AppCompatActivity(), SearchView.OnQueryTextListener {
private val MENU_PREFERENCES = 12302
private val REQUEST_INVITE = 13231
private val REQUEST_INVITE_BYMAIL = 13232
private val DRAWER_ID_TAGS = 100101L
private val DRAWER_ID_SOURCES = 100110L
private val DRAWER_ID_FILTERS = 100111L
private var mRecyclerView: RecyclerView? = null
private var api: SelfossApi? = null
private var items: ArrayList<Item> = ArrayList()
@ -77,6 +90,13 @@ class HomeActivity : AppCompatActivity() {
private var tabStarred: BottomBarTab? = null
private var mFirebaseRemoteConfig: FirebaseRemoteConfig? = null
private var fullHeightCards: Boolean = false
private var toolbar: Toolbar? = null
private var drawer: Drawer? = null
private var maybeTagFilter: Tag? = null
private var maybeSourceFilter: Sources? = null
private var maybeSearchFilter: String? = null
data class DrawerData(val tags: List<Tag>?, val sources: List<Sources>?)
private fun handleSharedPrefs() {
clickBehavior = this.sharedPref!!.getBoolean("tab_on_tap", false)
@ -91,6 +111,8 @@ class HomeActivity : AppCompatActivity() {
override fun onResume() {
super.onResume()
handleDrawerItems()
sharedPref = PreferenceManager.getDefaultSharedPreferences(this)
val settings = getSharedPreferences(Config.settingsName, Context.MODE_PRIVATE)
@ -110,13 +132,219 @@ class HomeActivity : AppCompatActivity() {
getElementsAccordingToTab()
}
fun handleDrawer() {
drawer = DrawerBuilder()
.withActivity(this)
.withRootView(R.id.drawer_layout)
.withToolbar(toolbar!!)
.withActionBarDrawerToggle(true)
.withActionBarDrawerToggleAnimated(true)
.withShowDrawerOnFirstLaunch(true)
.withOnDrawerListener(object: Drawer.OnDrawerListener {
override fun onDrawerSlide(p0: View?, p1: Float) {
mBottomBar!!.alpha = (1 - p1)
}
override fun onDrawerClosed(p0: View?) {
mBottomBar!!.shySettings.showBar()
}
override fun onDrawerOpened(p0: View?) {
mBottomBar!!.shySettings.hideBar()
}
})
.build()
drawer!!.addStickyFooterItem(
PrimaryDrawerItem()
.withName(R.string.action_about)
.withSelectable(false)
.withIcon(R.drawable.ic_info_outline)
.withOnDrawerItemClickListener { _, _, _ ->
LibsBuilder()
.withActivityStyle(Libs.ActivityStyle.LIGHT_DARK_TOOLBAR)
.withAboutIconShown(true)
.withAboutVersionShown(true)
.start(this@HomeActivity)
false
})
drawer!!.addStickyFooterItem(
PrimaryDrawerItem()
.withName(R.string.title_activity_settings)
.withIcon(R.drawable.ic_settings)
.withOnDrawerItemClickListener { _, _, _ ->
startActivityForResult(Intent(this@HomeActivity, SettingsActivity::class.java), MENU_PREFERENCES)
false
}
)
}
fun handleDrawerItems() {
fun handleDrawerData(maybeDrawerData: DrawerData?, loadedFromCache: Boolean = false) {
fun handleTags(maybeTags: List<Tag>?) {
if (maybeTags == null) {
if (loadedFromCache)
drawer!!.addItem(SecondaryDrawerItem().withName(getString(R.string.drawer_error_loading_tags)).withSelectable(false))
}
else {
for (tag in maybeTags) {
val gd: GradientDrawable = GradientDrawable()
gd.setColor(Color.parseColor(tag.color))
gd.shape = GradientDrawable.RECTANGLE
gd.setSize(30, 30)
gd.cornerRadius = 30F
drawer!!.addItem(
PrimaryDrawerItem()
.withName(tag.tag)
.withIdentifier(longHash(tag.tag))
.withIcon(gd)
.withBadge("${tag.unread}")
.withBadgeStyle(
BadgeStyle().withTextColor(Color.WHITE)
.withColorRes(R.color.colorAccent)
)
.withOnDrawerItemClickListener { _, _, _ ->
maybeTagFilter = tag
getElementsAccordingToTab()
false
}
)
}
}
}
fun handleSources(maybeSources: List<Sources>?) {
if (maybeSources == null) {
if (loadedFromCache)
drawer!!.addItem(SecondaryDrawerItem().withName(getString(R.string.drawer_error_loading_sources)).withSelectable(false))
}
else
for (tag in maybeSources)
drawer!!.addItem(
CustomUrlPrimaryDrawerItem()
.withName(tag.title)
.withIdentifier(tag.id.toLong())
.withIcon(tag.getIcon(this@HomeActivity))
.withOnDrawerItemClickListener { _, _, _ ->
maybeSourceFilter = tag
getElementsAccordingToTab()
false
}
)
}
drawer!!.removeAllItems()
if (maybeDrawerData != null) {
drawer!!.addItem(
SecondaryDrawerItem()
.withName(getString(R.string.drawer_item_filters))
.withSelectable(false)
.withIdentifier(DRAWER_ID_FILTERS)
.withBadge(getString(R.string.drawer_action_clear))
.withOnDrawerItemClickListener { _, _, _ ->
maybeSourceFilter = null
maybeTagFilter = null
getElementsAccordingToTab()
false
}
)
drawer!!.addItem(DividerDrawerItem())
drawer!!.addItem(SecondaryDrawerItem().withName(getString(R.string.drawer_item_tags)).withIdentifier(DRAWER_ID_TAGS).withSelectable(false))
handleTags(maybeDrawerData.tags)
drawer!!.addItem(
SecondaryDrawerItem()
.withName(getString(R.string.drawer_item_sources))
.withIdentifier(DRAWER_ID_TAGS)
.withBadge(getString(R.string.drawer_action_edit))
.withSelectable(false)
.withOnDrawerItemClickListener { _, _, _ ->
startActivity(Intent(this, SourcesActivity::class.java))
false
}
)
handleSources(maybeDrawerData.sources)
if (!loadedFromCache)
Reservoir.putAsync("drawerData", maybeDrawerData, object : ReservoirPutCallback {
override fun onSuccess() {}
override fun onFailure(p0: Exception?) {
}
})
} else {
if (!loadedFromCache) {
drawer!!.addItem(PrimaryDrawerItem().withName(getString(R.string.no_tags_loaded)).withIdentifier(DRAWER_ID_TAGS).withSelectable(false))
drawer!!.addItem(PrimaryDrawerItem().withName(getString(R.string.no_sources_loaded)).withIdentifier(DRAWER_ID_SOURCES).withSelectable(false))
}
}
}
fun drawerApiCalls(maybeDrawerData: DrawerData?) {
var tags: List<Tag>? = null
var sources: List<Sources>?
fun sourcesApiCall() {
api!!.sources.enqueue(object: Callback<List<Sources>> {
override fun onResponse(call: Call<List<Sources>>?, response: Response<List<Sources>>) {
sources = response.body()
val apiDrawerData = DrawerData(tags, sources)
if (maybeDrawerData == null || (maybeDrawerData != null && maybeDrawerData != apiDrawerData))
handleDrawerData(apiDrawerData)
}
override fun onFailure(call: Call<List<Sources>>?, t: Throwable?) {
}
})
}
api!!.tags.enqueue(object: Callback<List<Tag>> {
override fun onResponse(call: Call<List<Tag>>, response: Response<List<Tag>>) {
tags = response.body()
sourcesApiCall()
}
override fun onFailure(call: Call<List<Tag>>?, t: Throwable?) {
sourcesApiCall()
}
})
}
drawer!!.addItem(PrimaryDrawerItem().withName(getString(R.string.drawer_loading)).withSelectable(false))
val resultType = object : TypeToken<DrawerData>() {}.type
Reservoir.getAsync("drawerData", resultType, object: ReservoirGetCallback<DrawerData> {
override fun onSuccess(maybeDrawerData: DrawerData?) {
handleDrawerData(maybeDrawerData, loadedFromCache = true)
drawerApiCalls(maybeDrawerData)
}
override fun onFailure(p0: Exception?) {
drawerApiCalls(null)
}
})
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_home)
toolbar = findViewById(R.id.toolbar) as Toolbar?
setSupportActionBar(toolbar)
if (savedInstanceState == null) {
val promptView = findViewById(R.id.prompt_view) as DefaultLayoutPromptView
Amplify.getSharedInstance().promptIfReady(promptView)
}
@ -130,6 +358,8 @@ class HomeActivity : AppCompatActivity() {
mBottomBar = findViewById(R.id.bottomBar) as BottomBar
handleDrawer()
// TODO: clean this hack
val listenerAlreadySet = booleanArrayOf(false)
mBottomBar!!.setOnTabSelectListener { tabId ->
@ -229,7 +459,6 @@ class HomeActivity : AppCompatActivity() {
}
private fun getElementsAccordingToTab() {
items = ArrayList()
when (elementsShown) {
UNREAD_SHOWN -> getUnRead()
@ -241,15 +470,9 @@ class HomeActivity : AppCompatActivity() {
private fun getUnRead() {
elementsShown = UNREAD_SHOWN
api!!.unreadItems.enqueue(object : Callback<List<Item>> {
api!!.unreadItems(maybeTagFilter?.tag, maybeSourceFilter?.id?.toLong(), maybeSearchFilter).enqueue(object : Callback<List<Item>> {
override fun onResponse(call: Call<List<Item>>, response: Response<List<Item>>) {
if (response.body() != null && response.body()!!.isNotEmpty()) {
items = response.body() as ArrayList<Item>
} else {
items = ArrayList()
}
handleListResult()
mSwipeRefreshLayout!!.isRefreshing = false
handleItemsResponse(response)
}
override fun onFailure(call: Call<List<Item>>, t: Throwable) {
@ -259,17 +482,26 @@ class HomeActivity : AppCompatActivity() {
})
}
private fun handleItemsResponse(response: Response<List<Item>>) {
val didUpdate = (response.body() != items)
if (response.body() != null) {
if (response.body() != items) {
items = response.body() as ArrayList<Item>
}
} else {
items = ArrayList()
}
if (didUpdate)
handleListResult()
if (items.isEmpty()) Toast.makeText(this@HomeActivity, R.string.nothing_here, Toast.LENGTH_SHORT).show()
mSwipeRefreshLayout!!.isRefreshing = false
}
private fun getRead() {
elementsShown = READ_SHOWN
api!!.readItems.enqueue(object : Callback<List<Item>> {
api!!.readItems(maybeTagFilter?.tag, maybeSourceFilter?.id?.toLong(), maybeSearchFilter).enqueue(object : Callback<List<Item>> {
override fun onResponse(call: Call<List<Item>>, response: Response<List<Item>>) {
if (response.body() != null && response.body()!!.isNotEmpty()) {
items = response.body() as ArrayList<Item>
} else {
items = ArrayList()
}
handleListResult()
mSwipeRefreshLayout!!.isRefreshing = false
handleItemsResponse(response)
}
override fun onFailure(call: Call<List<Item>>, t: Throwable) {
@ -281,15 +513,9 @@ class HomeActivity : AppCompatActivity() {
private fun getStarred() {
elementsShown = FAV_SHOWN
api!!.starredItems.enqueue(object : Callback<List<Item>> {
api!!.starredItems(maybeTagFilter?.tag, maybeSourceFilter?.id?.toLong(), maybeSearchFilter).enqueue(object : Callback<List<Item>> {
override fun onResponse(call: Call<List<Item>>, response: Response<List<Item>>) {
if (response.body() != null && response.body()!!.isNotEmpty()) {
items = response.body() as ArrayList<Item>
} else {
items = ArrayList()
}
handleListResult()
mSwipeRefreshLayout!!.isRefreshing = false
handleItemsResponse(response)
}
override fun onFailure(call: Call<List<Item>>, t: Throwable) {
@ -311,8 +537,6 @@ class HomeActivity : AppCompatActivity() {
mRecyclerView!!.adapter = mAdapter
mAdapter.notifyDataSetChanged()
if (items.isEmpty()) Toast.makeText(this@HomeActivity, R.string.nothing_here, Toast.LENGTH_SHORT).show()
reloadBadges()
}
@ -329,6 +553,11 @@ class HomeActivity : AppCompatActivity() {
override fun onCreateOptionsMenu(menu: Menu): Boolean {
val inflater = menuInflater
inflater.inflate(R.menu.home_menu, menu)
val searchItem = menu.findItem(R.id.action_search)
val searchView = MenuItemCompat.getActionView(searchItem) as SearchView
searchView.setOnQueryTextListener(this)
return true
}
@ -370,6 +599,7 @@ class HomeActivity : AppCompatActivity() {
}
})
items = ArrayList()
if (items.isEmpty()) Toast.makeText(this@HomeActivity, R.string.nothing_here, Toast.LENGTH_SHORT).show()
handleListResult()
}
return true
@ -384,24 +614,6 @@ class HomeActivity : AppCompatActivity() {
finish()
return true
}
R.id.action_sources -> {
val intent2 = Intent(this, SourcesActivity::class.java)
startActivity(intent2)
return true
}
R.id.action_settings -> {
val intent3 = Intent(this, SettingsActivity::class.java)
startActivityForResult(intent3, MENU_PREFERENCES)
return true
}
R.id.about -> {
LibsBuilder()
.withActivityStyle(Libs.ActivityStyle.LIGHT_DARK_TOOLBAR)
.withAboutIconShown(true)
.withAboutVersionShown(true)
.start(this)
return true
}
R.id.action_share_the_app -> {
if (GoogleApiAvailability.getInstance().isGooglePlayServicesAvailable(this) == ConnectionResult.SUCCESS) {
val share = AppInviteInvitation.IntentBuilder(getString(R.string.invitation_title))
@ -452,7 +664,10 @@ class HomeActivity : AppCompatActivity() {
override fun onActivityResult(req: Int, result: Int, data: Intent?) {
when (req) {
MENU_PREFERENCES -> recreate()
MENU_PREFERENCES -> {
drawer!!.closeDrawer()
recreate()
}
REQUEST_INVITE -> if (result == Activity.RESULT_OK) {
Answers.getInstance().logInvite(InviteEvent())
}
@ -470,4 +685,18 @@ class HomeActivity : AppCompatActivity() {
val dpWidth = displayMetrics.widthPixels / displayMetrics.density
return (dpWidth / 300).toInt()
}
override fun onQueryTextChange(p0: String?): Boolean {
if (p0.isNullOrBlank()) {
maybeSearchFilter = null
getElementsAccordingToTab()
}
return false
}
override fun onQueryTextSubmit(p0: String?): Boolean {
maybeSearchFilter = p0
getElementsAccordingToTab()
return false
}
}

View File

@ -1,9 +1,19 @@
package apps.amine.bou.readerforselfoss
import android.content.Context
import android.graphics.drawable.Drawable
import android.net.Uri
import android.support.multidex.MultiDexApplication
import android.widget.ImageView
import com.crashlytics.android.Crashlytics
import com.github.stkent.amplify.tracking.Amplify
import io.fabric.sdk.android.Fabric
import com.anupcowkur.reservoir.Reservoir
import com.bumptech.glide.Glide
import com.mikepenz.iconics.IconicsDrawable
import com.mikepenz.materialdrawer.util.AbstractDrawerImageLoader
import com.mikepenz.materialdrawer.util.DrawerImageLoader
import java.io.IOException
class MyApp : MultiDexApplication() {
@ -16,5 +26,25 @@ class MyApp : MultiDexApplication() {
.setFeedbackEmailAddress(getString(R.string.feedback_email))
.setAlwaysShow(BuildConfig.DEBUG)
.applyAllDefaultRules()
try {
Reservoir.init(this, 8192) //in bytes
} catch (e: IOException) {
//failure
}
DrawerImageLoader.init(object : AbstractDrawerImageLoader() {
override fun set(imageView: ImageView?, uri: Uri?, placeholder: Drawable?, tag: String?) {
Glide.with(imageView?.context).load(uri).placeholder(placeholder).into(imageView)
}
override fun cancel(imageView: ImageView?) {
Glide.clear(imageView)
}
override fun placeholder(ctx: Context?, tag: String?): Drawable {
return applicationContext.resources.getDrawable(R.mipmap.ic_launcher)
}
})
}
}

View File

@ -6,6 +6,7 @@ import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.ImageButton
import android.widget.ImageView
import android.widget.TextView
import apps.amine.bou.readerforselfoss.api.mercury.MercuryApi
@ -44,6 +45,9 @@ class ReaderActivity : DragDismissActivity() {
val content = v.findViewById(R.id.content) as HtmlTextView
val url = intent.getStringExtra("url")
val parser = MercuryApi(getString(R.string.mercury))
val browserBtn: ImageButton = v.findViewById(R.id.browserBtn) as ImageButton
val shareBtn: ImageButton = v.findViewById(R.id.shareBtn) as ImageButton
val customTabsIntent = buildCustomTabsIntent(this@ReaderActivity)
mCustomTabActivityHelper = CustomTabActivityHelper()
@ -52,13 +56,30 @@ class ReaderActivity : DragDismissActivity() {
parser.parseUrl(url).enqueue(object : Callback<ParsedContent> {
override fun onResponse(call: Call<ParsedContent>, response: Response<ParsedContent>) {
if (response.body() != null) {
if (response.body() != null && response.body()!!.content != null && response.body()!!.content.isNotEmpty()) {
source.text = response.body()!!.domain
title.text = response.body()!!.title
if (response.body()!!.content != null && !response.body()!!.content.isEmpty())
content.setHtml(response.body()!!.content, HtmlHttpImageGetter(content, null, true))
if (response.body()!!.lead_image_url != null && !response.body()!!.lead_image_url.isEmpty())
Glide.with(applicationContext).load(response.body()!!.lead_image_url).asBitmap().fitCenter().into(image)
shareBtn.setOnClickListener {
val sendIntent = Intent()
sendIntent.flags = Intent.FLAG_ACTIVITY_NEW_TASK
sendIntent.action = Intent.ACTION_SEND
sendIntent.putExtra(Intent.EXTRA_TEXT, response.body()!!.url)
sendIntent.type = "text/plain"
startActivity(Intent.createChooser(sendIntent, getString(R.string.share)).setFlags(Intent.FLAG_ACTIVITY_NEW_TASK))
}
browserBtn.setOnClickListener {
val intent = Intent(Intent.ACTION_VIEW)
intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK
intent.data = Uri.parse(response.body()!!.url)
startActivity(intent)
}
hideProgressBar()
} else {
errorAfterMercuryCall()

View File

@ -72,17 +72,17 @@ class SelfossApi(c: Context) {
return service.loginToSelfoss(config.userLogin, config.userPassword)
}
val readItems: Call<List<Item>>
get() = getItems("read")
fun readItems(tag: String?, sourceId: Long?, search: String?): Call<List<Item>> =
getItems("read", tag, sourceId, search)
val unreadItems: Call<List<Item>>
get() = getItems("unread")
fun unreadItems(tag: String?, sourceId: Long?, search: String?): Call<List<Item>> =
getItems("unread", tag, sourceId, search)
val starredItems: Call<List<Item>>
get() = getItems("starred")
fun starredItems(tag: String?, sourceId: Long?, search: String?): Call<List<Item>> =
getItems("starred", tag, sourceId, search)
private fun getItems(type: String): Call<List<Item>> {
return service.getItems(type, userName, password)
private fun getItems(type: String, tag: String?, sourceId: Long?, search: String?): Call<List<Item>> {
return service.getItems(type, tag, sourceId, search, userName, password)
}
fun markItem(itemId: String): Call<SuccessResponse> {

View File

@ -16,7 +16,12 @@ internal interface SelfossService {
fun loginToSelfoss(@Query("username") username: String, @Query("password") password: String): Call<SuccessResponse>
@GET("items")
fun getItems(@Query("type") type: String, @Query("username") username: String, @Query("password") password: String): Call<List<Item>>
fun getItems(@Query("type") type: String,
@Query("tag") tag: String?,
@Query("source") source: Long?,
@Query("search") search: String?,
@Query("username") username: String,
@Query("password") password: String): Call<List<Item>>
@POST("mark/{id}")
fun markAsRead(@Path("id") id: String, @Query("username") username: String, @Query("password") password: String): Call<SuccessResponse>

View File

@ -85,4 +85,15 @@ private fun isThereAnUpdate(settings: SharedPreferences, editor: SharedPreferenc
alertDialog.show()
}
}
fun longHash(string: String): Long {
var h = 98764321261L
val l = string.length
val chars = string.toCharArray()
for (i in 0..l - 1) {
h = 31 * h + chars[i].toLong()
}
return h
}

View File

@ -1,7 +1,6 @@
package apps.amine.bou.readerforselfoss.utils.customtabs;
import android.app.Activity;
import android.content.ComponentName;
import android.net.Uri;
import android.os.Bundle;
import android.support.customtabs.CustomTabsClient;
@ -11,28 +10,30 @@ import android.support.customtabs.CustomTabsSession;
import java.util.List;
@SuppressWarnings("ALL")
public class CustomTabActivityHelper {
/**
* This is a helper class to manage the connection to the Custom Tabs Service.
*/
public class CustomTabActivityHelper implements ServiceConnectionCallback {
private CustomTabsSession mCustomTabsSession;
private CustomTabsClient mClient;
private CustomTabsServiceConnection mConnection;
private ConnectionCallback mConnectionCallback;
/**
* Opens the URL on a Custom Tab if possible. Otherwise fallsback to opening it on a WebView
* Opens the URL on a Custom Tab if possible. Otherwise fallsback to opening it on a WebView.
*
* @param activity The host activity
* @param customTabsIntent a CustomTabsIntent to be used if Custom Tabs is available
* @param uri the Uri to be opened
* @param fallback a CustomTabFallback to be used if Custom Tabs is not available
* @param activity The host activity.
* @param customTabsIntent a CustomTabsIntent to be used if Custom Tabs is available.
* @param uri the Uri to be opened.
* @param fallback a CustomTabFallback to be used if Custom Tabs is not available.
*/
public static void openCustomTab(Activity activity,
CustomTabsIntent customTabsIntent,
Uri uri,
CustomTabFallback fallback) {
CustomTabsIntent customTabsIntent,
Uri uri,
CustomTabFallback fallback) {
String packageName = CustomTabsHelper.getPackageNameToUse(activity);
//If we cant find a package name, it means there's no browser that supports
//If we cant find a package name, it means theres no browser that supports
//Chrome Custom Tabs installed. So, we fallback to the webview
if (packageName == null) {
if (fallback != null) {
@ -45,22 +46,21 @@ public class CustomTabActivityHelper {
}
/**
* Unbinds the Activity from the Custom Tabs Service
* @param activity the activity that is connected to the service
* Unbinds the Activity from the Custom Tabs Service.
* @param activity the activity that is connected to the service.
*/
public void unbindCustomTabsService(Activity activity) {
try {
if (mConnection == null) return;
activity.unbindService(mConnection);
mClient = null;
mCustomTabsSession = null;
} catch (RuntimeException e) {}
if (mConnection == null) return;
activity.unbindService(mConnection);
mClient = null;
mCustomTabsSession = null;
mConnection = null;
}
/**
* Creates or retrieves an exiting CustomTabsSession
* Creates or retrieves an exiting CustomTabsSession.
*
* @return a CustomTabsSession
* @return a CustomTabsSession.
*/
public CustomTabsSession getSession() {
if (mClient == null) {
@ -72,7 +72,7 @@ public class CustomTabActivityHelper {
}
/**
* Register a Callback to be called when connected or disconnected from the Custom Tabs Service
* Register a Callback to be called when connected or disconnected from the Custom Tabs Service.
* @param connectionCallback
*/
public void setConnectionCallback(ConnectionCallback connectionCallback) {
@ -80,67 +80,72 @@ public class CustomTabActivityHelper {
}
/**
* Binds the Activity to the Custom Tabs Service
* @param activity the activity to be binded to the service
* Binds the Activity to the Custom Tabs Service.
* @param activity the activity to be binded to the service.
*/
public void bindCustomTabsService(Activity activity) {
if (mClient != null) return;
String packageName = CustomTabsHelper.getPackageNameToUse(activity);
if (packageName == null) return;
mConnection = new CustomTabsServiceConnection() {
@Override
public void onCustomTabsServiceConnected(ComponentName name, CustomTabsClient client) {
mClient = client;
mClient.warmup(0L);
if (mConnectionCallback != null) mConnectionCallback.onCustomTabsConnected();
//Initialize a session as soon as possible.
getSession();
}
@Override
public void onServiceDisconnected(ComponentName name) {
mClient = null;
if (mConnectionCallback != null) mConnectionCallback.onCustomTabsDisconnected();
}
};
mConnection = new ServiceConnection(this);
CustomTabsClient.bindCustomTabsService(activity, packageName, mConnection);
}
/**
* @see {@link CustomTabsSession#mayLaunchUrl(Uri, Bundle, List)}.
* @return true if call to mayLaunchUrl was accepted.
*/
public boolean mayLaunchUrl(Uri uri, Bundle extras, List<Bundle> otherLikelyBundles) {
if (mClient == null) return false;
CustomTabsSession session = getSession();
return session != null && session.mayLaunchUrl(uri, extras, otherLikelyBundles);
if (session == null) return false;
return session.mayLaunchUrl(uri, extras, otherLikelyBundles);
}
@Override
public void onServiceConnected(CustomTabsClient client) {
mClient = client;
mClient.warmup(0L);
if (mConnectionCallback != null) mConnectionCallback.onCustomTabsConnected();
}
@Override
public void onServiceDisconnected() {
mClient = null;
mCustomTabsSession = null;
if (mConnectionCallback != null) mConnectionCallback.onCustomTabsDisconnected();
}
/**
* A Callback for when the service is connected or disconnected. Use those callbacks to
* handle UI changes when the service is connected or disconnected
* handle UI changes when the service is connected or disconnected.
*/
public interface ConnectionCallback {
/**
* Called when the service is connected
* Called when the service is connected.
*/
void onCustomTabsConnected();
/**
* Called when the service is disconnected
* Called when the service is disconnected.
*/
void onCustomTabsDisconnected();
}
/**
* To be used as a fallback to open the Uri when Custom Tabs is not available
* To be used as a fallback to open the Uri when Custom Tabs is not available.
*/
public interface CustomTabFallback {
/**
*
* @param activity The Activity that wants to open the Uri
* @param uri The uri to be opened by the fallback
* @param activity The Activity that wants to open the Uri.
* @param uri The uri to be opened by the fallback.
*/
void openUri(Activity activity, Uri uri);
}
}
}

View File

@ -0,0 +1,32 @@
package apps.amine.bou.readerforselfoss.utils.customtabs;
import android.content.ComponentName;
import android.support.customtabs.CustomTabsClient;
import android.support.customtabs.CustomTabsServiceConnection;
import java.lang.ref.WeakReference;
/**
* Implementation for the CustomTabsServiceConnection that avoids leaking the
* ServiceConnectionCallback
*/
public class ServiceConnection extends CustomTabsServiceConnection {
// A weak reference to the ServiceConnectionCallback to avoid leaking it.
private WeakReference<ServiceConnectionCallback> mConnectionCallback;
public ServiceConnection(ServiceConnectionCallback connectionCallback) {
mConnectionCallback = new WeakReference<>(connectionCallback);
}
@Override
public void onCustomTabsServiceConnected(ComponentName name, CustomTabsClient client) {
ServiceConnectionCallback connectionCallback = mConnectionCallback.get();
if (connectionCallback != null) connectionCallback.onServiceConnected(client);
}
@Override
public void onServiceDisconnected(ComponentName name) {
ServiceConnectionCallback connectionCallback = mConnectionCallback.get();
if (connectionCallback != null) connectionCallback.onServiceDisconnected();
}
}

View File

@ -0,0 +1,18 @@
package apps.amine.bou.readerforselfoss.utils.customtabs;
import android.support.customtabs.CustomTabsClient;
public interface ServiceConnectionCallback {
/**
* Called when the service is connected.
* @param client a CustomTabsClient
*/
void onServiceConnected(CustomTabsClient client);
/**
* Called when the service is disconnected.
*/
void onServiceDisconnected();
}

View File

@ -0,0 +1,17 @@
/* From https://github.com/mikepenz/MaterialDrawer/blob/develop/app/src/main/java/com/mikepenz/materialdrawer/app/drawerItems/CustomBaseViewHolder.java */
package apps.amine.bou.readerforselfoss.utils.drawer
import android.support.v7.widget.RecyclerView
import android.view.View
import android.widget.ImageView
import android.widget.TextView
import apps.amine.bou.readerforselfoss.R
open class CustomBaseViewHolder(var view: View) : RecyclerView.ViewHolder(view) {
var icon: ImageView = view.findViewById(R.id.material_drawer_icon) as ImageView
var name: TextView = view.findViewById(R.id.material_drawer_name) as TextView
var description: TextView = view.findViewById(R.id.material_drawer_description) as TextView
}

View File

@ -0,0 +1,108 @@
/* From https://github.com/mikepenz/MaterialDrawer/blob/develop/app/src/main/java/com/mikepenz/materialdrawer/app/drawerItems/CustomUrlBasePrimaryDrawerItem.java */
package apps.amine.bou.readerforselfoss.utils.drawer
import android.content.Context
import android.net.Uri
import android.support.annotation.ColorInt
import android.support.annotation.ColorRes
import android.support.annotation.StringRes
import android.support.v7.widget.RecyclerView
import com.mikepenz.materialdrawer.holder.ColorHolder
import com.mikepenz.materialdrawer.holder.ImageHolder
import com.mikepenz.materialdrawer.holder.StringHolder
import com.mikepenz.materialdrawer.model.BaseDrawerItem
import com.mikepenz.materialdrawer.util.DrawerImageLoader
import com.mikepenz.materialdrawer.util.DrawerUIUtils
import com.mikepenz.materialize.util.UIUtils
abstract class CustomUrlBasePrimaryDrawerItem<T, VH : RecyclerView.ViewHolder> : BaseDrawerItem<T, VH>() {
fun withIcon(url: String): T {
this.icon = ImageHolder(url)
return this as T
}
fun withIcon(uri: Uri): T {
this.icon = ImageHolder(uri)
return this as T
}
var description: StringHolder? = null
private set
var descriptionTextColor: ColorHolder? = null
private set
fun withDescription(description: String): T {
this.description = StringHolder(description)
return this as T
}
fun withDescription(@StringRes descriptionRes: Int): T {
this.description = StringHolder(descriptionRes)
return this as T
}
fun withDescriptionTextColor(@ColorInt color: Int): T {
this.descriptionTextColor = ColorHolder.fromColor(color)
return this as T
}
fun withDescriptionTextColorRes(@ColorRes colorRes: Int): T {
this.descriptionTextColor = ColorHolder.fromColorRes(colorRes)
return this as T
}
/**
* a helper method to have the logic for all secondaryDrawerItems only once
* @param viewHolder
*/
protected fun bindViewHelper(viewHolder: CustomBaseViewHolder) {
val ctx = viewHolder.itemView.context
//set the identifier from the drawerItem here. It can be used to run tests
viewHolder.itemView.id = hashCode()
//set the item selected if it is
viewHolder.itemView.isSelected = isSelected
//get the correct color for the background
val selectedColor = getSelectedColor(ctx)
//get the correct color for the text
val color = getColor(ctx)
val selectedTextColor = getSelectedTextColor(ctx)
//get the correct color for the icon
val iconColor = getIconColor(ctx)
val selectedIconColor = getSelectedIconColor(ctx)
//set the background for the item
UIUtils.setBackground(viewHolder.view, UIUtils.getSelectableBackground(ctx, selectedColor, true))
//set the text for the name
StringHolder.applyTo(this.getName(), viewHolder.name)
//set the text for the description or hide
StringHolder.applyToOrHide(this.description, viewHolder.description)
//set the colors for textViews
viewHolder.name.setTextColor(getTextColorStateList(color, selectedTextColor))
//set the description text color
ColorHolder.applyToOr(descriptionTextColor,
viewHolder.description, getTextColorStateList(color, selectedTextColor))
//define the typeface for our textViews
if (getTypeface() != null) {
viewHolder.name.typeface = getTypeface()
viewHolder.description.typeface = getTypeface()
}
//we make sure we reset the image first before setting the new one in case there is an empty one
DrawerImageLoader.getInstance().cancelImage(viewHolder.icon)
viewHolder.icon.setImageBitmap(null)
//get the drawables for our icon and set it
ImageHolder.applyTo(icon, viewHolder.icon, "customUrlItem")
//for android API 17 --> Padding not applied via xml
DrawerUIUtils.setDrawerVerticalPadding(viewHolder.view)
}
}

View File

@ -0,0 +1,94 @@
/* From https://github.com/mikepenz/MaterialDrawer/blob/develop/app/src/main/java/com/mikepenz/materialdrawer/app/drawerItems/CustomUrlPrimaryDrawerItem.java */
package apps.amine.bou.readerforselfoss.utils.drawer
import android.content.Context
import android.support.annotation.LayoutRes
import android.support.annotation.StringRes
import android.view.View
import android.widget.TextView
import apps.amine.bou.readerforselfoss.R
import com.mikepenz.materialdrawer.holder.BadgeStyle
import com.mikepenz.materialdrawer.holder.StringHolder
import com.mikepenz.materialdrawer.model.interfaces.ColorfulBadgeable
class CustomUrlPrimaryDrawerItem : CustomUrlBasePrimaryDrawerItem<CustomUrlPrimaryDrawerItem, CustomUrlPrimaryDrawerItem.ViewHolder>(), ColorfulBadgeable<CustomUrlPrimaryDrawerItem> {
protected var mBadge: StringHolder = StringHolder("")
protected var mBadgeStyle = BadgeStyle()
override fun withBadge(badge: StringHolder): CustomUrlPrimaryDrawerItem {
this.mBadge = badge
return this
}
override fun withBadge(badge: String): CustomUrlPrimaryDrawerItem {
this.mBadge = StringHolder(badge)
return this
}
override fun withBadge(@StringRes badgeRes: Int): CustomUrlPrimaryDrawerItem {
this.mBadge = StringHolder(badgeRes)
return this
}
override fun withBadgeStyle(badgeStyle: BadgeStyle): CustomUrlPrimaryDrawerItem {
this.mBadgeStyle = badgeStyle
return this
}
override fun getBadge(): StringHolder {
return mBadge
}
override fun getBadgeStyle(): BadgeStyle {
return mBadgeStyle
}
override fun getType(): Int {
return R.id.material_drawer_item_custom_url_item
}
@LayoutRes
override fun getLayoutRes(): Int {
return R.layout.material_drawer_item_primary
}
override fun bindView(viewHolder: ViewHolder, payloads: List<*>?) {
super.bindView(viewHolder, payloads)
val ctx = viewHolder.itemView.context
//bind the basic view parts
bindViewHelper(viewHolder)
//set the text for the badge or hide
val badgeVisible = StringHolder.applyToOrHide(mBadge, viewHolder.badge)
//style the badge if it is visible
if (badgeVisible) {
mBadgeStyle.style(viewHolder.badge, getTextColorStateList(getColor(ctx), getSelectedTextColor(ctx)))
viewHolder.badgeContainer.visibility = View.VISIBLE
} else {
viewHolder.badgeContainer.visibility = View.GONE
}
//define the typeface for our textViews
if (getTypeface() != null) {
viewHolder.badge.typeface = getTypeface()
}
//call the onPostBindView method to trigger post bind view actions (like the listener to modify the item if required)
onPostBindView(this, viewHolder.itemView)
}
override fun getViewHolder(v: View): ViewHolder {
return ViewHolder(v)
}
class ViewHolder(view: View) : CustomBaseViewHolder(view) {
val badgeContainer: View = view.findViewById(R.id.material_drawer_badge_container)
val badge: TextView = view.findViewById(R.id.material_drawer_badge) as TextView
}
}

View File

@ -0,0 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24.0"
android:viewportHeight="24.0">
<path
android:fillColor="#FF000000"
android:pathData="M11,17h2v-6h-2v6zM12,2C6.48,2 2,6.48 2,12s4.48,10 10,10 10,-4.48 10,-10S17.52,2 12,2zM12,20c-4.41,0 -8,-3.59 -8,-8s3.59,-8 8,-8 8,3.59 8,8 -3.59,8 -8,8zM11,9h2L13,7h-2v2z"/>
</vector>

View File

@ -0,0 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24.0"
android:viewportHeight="24.0">
<path
android:fillColor="#FF000000"
android:pathData="M19.43,12.98c0.04,-0.32 0.07,-0.64 0.07,-0.98s-0.03,-0.66 -0.07,-0.98l2.11,-1.65c0.19,-0.15 0.24,-0.42 0.12,-0.64l-2,-3.46c-0.12,-0.22 -0.39,-0.3 -0.61,-0.22l-2.49,1c-0.52,-0.4 -1.08,-0.73 -1.69,-0.98l-0.38,-2.65C14.46,2.18 14.25,2 14,2h-4c-0.25,0 -0.46,0.18 -0.49,0.42l-0.38,2.65c-0.61,0.25 -1.17,0.59 -1.69,0.98l-2.49,-1c-0.23,-0.09 -0.49,0 -0.61,0.22l-2,3.46c-0.13,0.22 -0.07,0.49 0.12,0.64l2.11,1.65c-0.04,0.32 -0.07,0.65 -0.07,0.98s0.03,0.66 0.07,0.98l-2.11,1.65c-0.19,0.15 -0.24,0.42 -0.12,0.64l2,3.46c0.12,0.22 0.39,0.3 0.61,0.22l2.49,-1c0.52,0.4 1.08,0.73 1.69,0.98l0.38,2.65c0.03,0.24 0.24,0.42 0.49,0.42h4c0.25,0 0.46,-0.18 0.49,-0.42l0.38,-2.65c0.61,-0.25 1.17,-0.59 1.69,-0.98l2.49,1c0.23,0.09 0.49,0 0.61,-0.22l2,-3.46c0.12,-0.22 0.07,-0.49 -0.12,-0.64l-2.11,-1.65zM12,15.5c-1.93,0 -3.5,-1.57 -3.5,-3.5s1.57,-3.5 3.5,-3.5 3.5,1.57 3.5,3.5 -1.57,3.5 -3.5,3.5z"/>
</vector>

Binary file not shown.

After

Width:  |  Height:  |  Size: 680 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 442 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 634 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.3 KiB

View File

@ -5,6 +5,7 @@
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fitsSystemWindows="true"
tools:context="apps.amine.bou.readerforselfoss.HomeActivity">
<com.roughike.bottombar.BottomBar
@ -54,19 +55,55 @@
android:layout_toRightOf="@+id/bottomBar"
android:layout_below="@id/prompt_view">
<android.support.v4.widget.SwipeRefreshLayout
android:id="@+id/swipeRefreshLayout"
<android.support.design.widget.CoordinatorLayout
android:id="@+id/intern_coordLayout"
android:layout_width="match_parent"
android:layout_height="match_parent">
<android.support.v7.widget.RecyclerView
android:id="@+id/my_recycler_view"
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@color/background_grey"
android:scrollbars="vertical"
app:layout_behavior="@string/appbar_scrolling_view_behavior" />
</android.support.v4.widget.SwipeRefreshLayout>
android:layout_height="match_parent"
android:orientation="vertical">
<android.support.design.widget.AppBarLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<android.support.v7.widget.Toolbar
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
app:theme="@style/ToolBarStyle"
app:popupTheme="@style/ThemeOverlay.AppCompat.Light"/>
</android.support.design.widget.AppBarLayout>
<FrameLayout
android:id="@+id/drawer_layout"
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent">
<android.support.v4.widget.SwipeRefreshLayout
android:id="@+id/swipeRefreshLayout"
android:layout_width="match_parent"
android:layout_height="match_parent">
<android.support.v7.widget.RecyclerView
android:id="@+id/my_recycler_view"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@color/background_grey"
android:scrollbars="vertical"
app:layout_behavior="@string/appbar_scrolling_view_behavior" />
</android.support.v4.widget.SwipeRefreshLayout>
</FrameLayout>
</LinearLayout>
</android.support.design.widget.CoordinatorLayout>
</android.support.design.widget.CoordinatorLayout>

View File

@ -5,6 +5,7 @@
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="apps.amine.bou.readerforselfoss.HomeActivity"
android:fitsSystemWindows="true"
xmlns:app="http://schemas.android.com/apk/res-auto">
<com.github.stkent.amplify.prompt.DefaultLayoutPromptView
@ -34,22 +35,55 @@
android:layout_height="match_parent"
android:layout_below="@id/prompt_view">
<android.support.v4.widget.SwipeRefreshLayout
android:id="@+id/swipeRefreshLayout"
<android.support.design.widget.CoordinatorLayout
android:id="@+id/intern_coordLayout"
android:layout_width="match_parent"
android:layout_height="match_parent">
<android.support.v7.widget.RecyclerView
android:id="@+id/my_recycler_view"
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@color/background_grey"
android:clipToPadding="false"
android:scrollbars="vertical"
app:layout_behavior="@string/appbar_scrolling_view_behavior" />
</android.support.v4.widget.SwipeRefreshLayout>
android:layout_height="match_parent"
android:orientation="vertical">
<android.support.design.widget.AppBarLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<android.support.v7.widget.Toolbar
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
app:theme="@style/ToolBarStyle"
app:popupTheme="@style/ThemeOverlay.AppCompat.Light"/>
</android.support.design.widget.AppBarLayout>
<FrameLayout
android:id="@+id/drawer_layout"
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent">
<android.support.v4.widget.SwipeRefreshLayout
android:id="@+id/swipeRefreshLayout"
android:layout_width="match_parent"
android:layout_height="match_parent">
<android.support.v7.widget.RecyclerView
android:id="@+id/my_recycler_view"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@color/background_grey"
android:clipToPadding="false"
android:scrollbars="vertical"
app:layout_behavior="@string/appbar_scrolling_view_behavior" />
</android.support.v4.widget.SwipeRefreshLayout>
</FrameLayout>
</LinearLayout>
</android.support.design.widget.CoordinatorLayout>
<com.roughike.bottombar.BottomBar
android:id="@+id/bottomBar"
android:layout_width="match_parent"
@ -62,6 +96,5 @@
app:bb_badgeBackgroundColor="@color/colorPrimary"
app:bb_inActiveTabColor="@color/black"
app:bb_badgesHideWhenActive="false" />
</android.support.design.widget.CoordinatorLayout>
</RelativeLayout>

View File

@ -62,5 +62,64 @@
app:layout_constraintHorizontal_bias="0.0"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toBottomOf="@+id/title" />
app:layout_constraintTop_toBottomOf="@+id/title"
tools:text="Some text @android:string/fingerprint_icon_content_description" />
<android.support.constraint.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="35dp"
android:layout_marginBottom="0dp"
android:layout_marginEnd="0dp"
android:layout_marginRight="16dp"
android:layout_marginTop="16dp"
android:background="#BBBBBB"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.0"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/content"
app:layout_constraintVertical_bias="1.0">
<ImageButton
android:id="@+id/browserBtn"
android:layout_width="35dp"
android:layout_height="35dp"
android:layout_centerVertical="true"
android:layout_marginEnd="16dp"
android:layout_marginRight="16dp"
android:layout_toLeftOf="@+id/shareBtn"
android:layout_toStartOf="@+id/shareBtn"
android:adjustViewBounds="true"
android:background="@android:color/transparent"
android:elevation="5dp"
android:padding="4dp"
android:scaleType="centerCrop"
android:src="@drawable/ic_open_in_browser_black_24dp"
android:tint="#000000"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toStartOf="@+id/shareBtn"
app:layout_constraintHorizontal_bias="0.5"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<ImageButton
android:id="@+id/shareBtn"
android:layout_width="35dp"
android:layout_height="35dp"
android:adjustViewBounds="true"
android:background="@android:color/transparent"
android:elevation="5dp"
android:padding="4dp"
android:scaleType="centerCrop"
android:src="@drawable/ic_share_black_24dp"
android:tint="#000000"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.5"
app:layout_constraintStart_toEndOf="@+id/browserBtn"
app:layout_constraintTop_toTopOf="parent" />
</android.support.constraint.ConstraintLayout>
</android.support.constraint.ConstraintLayout>

View File

@ -54,65 +54,64 @@
android:gravity="start" />
<RelativeLayout
android:id="@+id/actionBar"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:background="#BBBBBB"
android:visibility="gone"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintTop_toBottomOf="@+id/sourceTitleAndDate"
android:id="@+id/actionBar"
android:background="#BBBBBB"
android:visibility="gone">
app:layout_constraintTop_toBottomOf="@+id/sourceTitleAndDate">
<com.like.LikeButton
app:icon_type="heart"
app:icon_size="22dp"
android:id="@+id/favButton"
android:layout_width="35dp"
android:layout_height="35dp"
android:layout_alignParentEnd="true"
android:layout_alignParentRight="true"
android:layout_centerVertical="true"
android:layout_alignParentRight="true"
android:layout_alignParentEnd="true"
android:layout_marginEnd="8dp"
android:layout_marginRight="8dp"
android:elevation="5dp"
android:padding="4dp"
android:layout_marginEnd="8dp"
android:layout_marginRight="8dp"/>
app:icon_size="22dp"
app:icon_type="heart" />
<ImageButton
android:id="@+id/shareBtn"
android:layout_width="35dp"
android:layout_height="35dp"
android:src="@drawable/ic_share_black_24dp"
android:layout_centerVertical="true"
android:layout_marginEnd="16dp"
android:layout_marginRight="16dp"
android:layout_toLeftOf="@+id/favButton"
android:layout_toStartOf="@+id/favButton"
android:id="@+id/shareBtn"
android:adjustViewBounds="true"
android:background="@android:color/transparent"
android:elevation="5dp"
android:layout_marginEnd="16dp"
android:layout_marginRight="16dp"
android:tint="#000000"
android:adjustViewBounds="true"
android:padding="4dp"
android:scaleType="centerCrop"
android:padding="4dp"/>
android:src="@drawable/ic_share_black_24dp"
android:tint="#000000" />
<ImageButton
android:id="@+id/browserBtn"
android:layout_width="35dp"
android:layout_height="35dp"
android:src="@drawable/ic_open_in_browser_black_24dp"
android:layout_centerVertical="true"
android:layout_toLeftOf="@+id/shareBtn"
android:layout_toStartOf="@+id/shareBtn"
android:id="@+id/browserBtn"
android:layout_marginEnd="16dp"
android:layout_marginRight="16dp"
android:elevation="5dp"
android:background="@android:color/transparent"
android:tint="#000000"
android:layout_toLeftOf="@+id/shareBtn"
android:layout_toStartOf="@+id/shareBtn"
android:adjustViewBounds="true"
android:background="@android:color/transparent"
android:elevation="5dp"
android:padding="4dp"
android:scaleType="centerCrop"
android:padding="4dp"/>
android:src="@drawable/ic_open_in_browser_black_24dp"
android:tint="#000000" />
</RelativeLayout>

View File

@ -2,6 +2,12 @@
<menu xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<item android:id="@+id/action_search"
android:title="@string/menu_home_search"
android:icon="@drawable/ic_action_search"
app:showAsAction="ifRoom|collapseActionView"
app:actionViewClass="android.support.v7.widget.SearchView" />
<item android:id="@+id/readAll"
android:icon="@drawable/ic_done_all_white_24dp"
android:title="@string/readAll"
@ -14,24 +20,11 @@
android:orderInCategory="99"
android:title="@string/menu_home_refresh"
app:showAsAction="ifRoom" />
<item android:id="@+id/action_settings"
android:title="@string/title_activity_settings"
android:orderInCategory="100"
app:showAsAction="never"/>
<item android:id="@+id/action_sources"
android:title="@string/action_source"
android:orderInCategory="101"
app:showAsAction="never"/>
<item
android:id="@+id/action_share_the_app"
android:orderInCategory="102"
android:title="@string/menu_share_the_app" />
<item android:id="@+id/about"
android:title="@string/action_about"
android:orderInCategory="103"
app:showAsAction="never"/>
<item android:id="@+id/action_disconnect"
android:title="@string/action_disconnect"

View File

@ -103,4 +103,16 @@
<string name="card_height_off" >La taille de la carte sera fixe</string>
<string name="source_code">Code source</string>
<string name="cant_mark_read">Kan het artikel niet als gelezen markeren</string>
<string name="drawer_error_loading_tags">Erreur lors du chargement des tags…</string>
<string name="drawer_error_loading_sources">Erreur lors du chargement des sources…</string>
<string name="drawer_item_filters">Filtres</string>
<string name="drawer_action_clear">raz</string>
<string name="drawer_item_tags">Tags</string>
<string name="drawer_item_sources">Sources</string>
<string name="drawer_action_edit">editer</string>
<string name="cache_drawer_error">Impossible de mettre en cache les filtres pour le drawer</string>
<string name="no_tags_loaded">Pas de tags chargés</string>
<string name="no_sources_loaded">Pas de sources chargés</string>
<string name="drawer_loading">Chargement …</string>
<string name="menu_home_search">Rechercher</string>
</resources>

View File

@ -103,4 +103,16 @@
<string name="card_height_off" >Vaste hoogte</string>
<string name="source_code">Source code</string>
<string name="cant_mark_read">Impossible de marquer l\'article comme lu</string>
<string name="drawer_error_loading_tags">Error loading tags…</string>
<string name="drawer_error_loading_sources">Error loading sources…</string>
<string name="drawer_item_filters">Filters</string>
<string name="drawer_action_clear">clear</string>
<string name="drawer_item_tags">Tags</string>
<string name="drawer_item_sources">Sources</string>
<string name="drawer_action_edit">edit</string>
<string name="cache_drawer_error">Couldn\'t cache your drawer data</string>
<string name="no_tags_loaded">No tags loaded</string>
<string name="no_sources_loaded">No sources loaded</string>
<string name="drawer_loading">Loading …</string>
<string name="menu_home_search">Zoeken</string>
</resources>

View File

@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<item name="material_drawer_item_custom_url_item" type="id" />
</resources>

View File

@ -105,4 +105,16 @@
<string name="card_height_off">Card height will be fixed</string>
<string name="source_code">Source code</string>
<string name="cant_mark_read">Can\'t mark article as read</string>
<string name="drawer_error_loading_tags">Error loading tags…</string>
<string name="drawer_error_loading_sources">Error loading sources…</string>
<string name="drawer_item_filters">Filters</string>
<string name="drawer_action_clear">clear</string>
<string name="drawer_item_tags">Tags</string>
<string name="drawer_item_sources">Sources</string>
<string name="drawer_action_edit">edit</string>
<string name="cache_drawer_error">Couldn\'t cache your drawer data</string>
<string name="no_tags_loaded">No tags loaded</string>
<string name="no_sources_loaded">No sources loaded</string>
<string name="drawer_loading">Loading …</string>
<string name="menu_home_search">Search</string>
</resources>

View File

@ -13,4 +13,20 @@
<item name="android:windowBackground">@drawable/background_splash</item>
</style>
<style name="NoBar" parent="Theme.AppCompat.Light.NoActionBar">
<item name="colorPrimary">@color/colorPrimary</item>
<item name="colorPrimaryDark">@color/colorPrimaryDark</item>
<item name="colorAccent">@color/colorAccent</item>
<item name="android:textColor">#000000</item>
</style>
<!-- ToolBar -->
<style name="ToolBarStyle" parent="Theme.AppCompat">
<item name="android:textColorPrimary">@color/white</item>
<item name="android:textColorSecondary">@color/white</item>
<item name="actionMenuTextColor">@color/white</item>
<!--<item name="actionOverflowButtonStyle">@style/ActionButtonOverflowStyle</item>
<item name="drawerArrowStyle">@style/DrawerArrowStyle</item>-->
</style>
</resources>

View File

@ -7,7 +7,7 @@ buildscript {
jcenter()
}
dependencies {
classpath 'com.android.tools.build:gradle:3.0.0-alpha2'
classpath 'com.android.tools.build:gradle:3.0.0-alpha3'
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
// NOTE: Do not place your application dependencies here; they belong

43
circle.yml Normal file
View File

@ -0,0 +1,43 @@
machine:
timezone: Europe/Paris
java:
version: 'oraclejdk8'
environment:
ANDROID_HOME: /usr/local/android-sdk-linux
PATH: ANDROID_HOME:$ANDROID_HOME/platform-tools:$ANDROID_BUUILD:$ANDROID_HOME/tools:$PATH
_JAVA_OPTIONS: "-Xms512m -Xmx1024m"
GRADLE_OPTS: '-Dorg.gradle.jvmargs="-Xmx2048m -XX:+HeapDumpOnOutOfMemoryError"'
dependencies:
pre:
- touch app/google-services.json
- echo $GOOGLE_SERVICES_JSON > app/google-services.json
- touch app/src/main/res/values/secrets.xml
- echo $SECRETS_XML > app/src/main/res/values/secrets.xml
- mkdir app/src/main/res/mipmap-hdpi
- convert -size 72x72 xc:white app/src/main/res/mipmap-hdpi/ic_launcher.png
- mkdir app/src/main/res/mipmap-mdpi
- convert -size 48x48 xc:white app/src/main/res/mipmap-mdpi/ic_launcher.png
- mkdir app/src/main/res/mipmap-xhdpi
- convert -size 96x96 xc:white app/src/main/res/mipmap-xhdpi/ic_launcher.png
- mkdir app/src/main/res/mipmap-xxhdpi
- convert -size 192x192 xc:white app/src/main/res/mipmap-xxhdpi/ic_launcher.png
- mkdir app/src/main/res/mipmap-xxxhdpi
- convert -size 512x512 xc:white app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
cache_directories:
- ~/.gradle
- ~/.android
override:
- echo y | android update sdk --no-ui --filter "android-25,build-tools-25.0.3"
- echo y | android update sdk --no-ui --all --filter "platform-tools, tools"
- echo y | android update sdk --no-ui --all --filter "android-25, build-tools-25.0.3"
- echo y | android update sdk --no-ui --all --filter "extra-android-m2repository"
- echo y | android update sdk --no-ui --all --filter "extra-android-support"
- echo y | android update sdk --no-ui --all --filter "extra-google-m2repository"
- echo y | android update sdk --no-ui --all --filter "extra-google-google_play_services"
test:
override:
- gradlew assemble -P crashlyticsdemoApikey=$FABRIC_API_KEY -P crashlyticsdemoApisecret=$FABRIC_API_SECRET