Kotlin style guide. (#125)

This commit is contained in:
Amine Bou 2017-11-25 13:12:12 +01:00 committed by GitHub
parent 68098f4d84
commit 69ac2e2b44
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
38 changed files with 1150 additions and 703 deletions

View File

@ -1,4 +1,3 @@
package apps.amine.bou.readerforselfoss package apps.amine.bou.readerforselfoss
// TODO: test source adding // TODO: test source adding

View File

@ -5,29 +5,33 @@ import android.content.Intent
import android.support.test.InstrumentationRegistry import android.support.test.InstrumentationRegistry
import android.support.test.espresso.Espresso.onView import android.support.test.espresso.Espresso.onView
import android.support.test.espresso.Espresso.openActionBarOverflowOrOptionsMenu import android.support.test.espresso.Espresso.openActionBarOverflowOrOptionsMenu
import android.support.test.espresso.action.ViewActions.* import android.support.test.espresso.action.ViewActions.click
import android.support.test.espresso.action.ViewActions.closeSoftKeyboard
import android.support.test.espresso.action.ViewActions.pressBack
import android.support.test.espresso.action.ViewActions.pressKey
import android.support.test.espresso.action.ViewActions.typeText
import android.support.test.espresso.assertion.ViewAssertions.matches import android.support.test.espresso.assertion.ViewAssertions.matches
import android.support.test.espresso.contrib.DrawerActions import android.support.test.espresso.contrib.DrawerActions
import android.support.test.espresso.intent.Intents import android.support.test.espresso.intent.Intents
import android.support.test.espresso.intent.Intents.intended import android.support.test.espresso.intent.Intents.intended
import android.support.test.espresso.intent.Intents.times import android.support.test.espresso.intent.Intents.times
import android.support.test.espresso.intent.matcher.IntentMatchers.hasComponent import android.support.test.espresso.intent.matcher.IntentMatchers.hasComponent
import android.support.test.espresso.matcher.ViewMatchers.* import android.support.test.espresso.matcher.ViewMatchers.isDisplayed
import android.support.test.espresso.matcher.ViewMatchers.isRoot
import android.support.test.espresso.matcher.ViewMatchers.withContentDescription
import android.support.test.espresso.matcher.ViewMatchers.withId
import android.support.test.espresso.matcher.ViewMatchers.withText
import android.support.test.rule.ActivityTestRule import android.support.test.rule.ActivityTestRule
import android.support.test.runner.AndroidJUnit4 import android.support.test.runner.AndroidJUnit4
import android.view.KeyEvent import android.view.KeyEvent
import apps.amine.bou.readerforselfoss.utils.Config
import com.mikepenz.aboutlibraries.ui.LibsActivity import com.heinrichreimersoftware.androidissuereporter.IssueReporterLauncher
import org.junit.After
import org.junit.Before import org.junit.Before
import org.junit.Rule import org.junit.Rule
import org.junit.Test import org.junit.Test
import org.junit.runner.RunWith import org.junit.runner.RunWith
import apps.amine.bou.readerforselfoss.utils.Config
import com.heinrichreimersoftware.androidissuereporter.IssueReporterLauncher
import org.junit.After
@RunWith(AndroidJUnit4::class) @RunWith(AndroidJUnit4::class)
class HomeActivityEspressoTest { class HomeActivityEspressoTest {
lateinit var context: Context lateinit var context: Context
@ -40,9 +44,9 @@ class HomeActivityEspressoTest {
context = InstrumentationRegistry.getInstrumentation().targetContext context = InstrumentationRegistry.getInstrumentation().targetContext
val editor = val editor =
context context
.getSharedPreferences(Config.settingsName, Context.MODE_PRIVATE) .getSharedPreferences(Config.settingsName, Context.MODE_PRIVATE)
.edit() .edit()
editor.clear() editor.clear()
editor.putString("url", BuildConfig.LOGIN_URL) editor.putString("url", BuildConfig.LOGIN_URL)
@ -60,29 +64,32 @@ class HomeActivityEspressoTest {
rule.launchActivity(Intent()) rule.launchActivity(Intent())
onView( onView(
withMenu( withMenu(
id = R.id.action_search, id = R.id.action_search,
titleId = R.string.menu_home_search titleId = R.string.menu_home_search
) )
).perform(click()) ).perform(click())
onView(withId(R.id.search_bar)).check(matches(isDisplayed())) onView(withId(R.id.search_bar)).check(matches(isDisplayed()))
onView(withId(R.id.search_src_text)).perform(typeText("android"), pressKey(KeyEvent.KEYCODE_SEARCH), closeSoftKeyboard()) onView(withId(R.id.search_src_text)).perform(
typeText("android"),
pressKey(KeyEvent.KEYCODE_SEARCH),
closeSoftKeyboard()
)
onView(withContentDescription(R.string.abc_toolbar_collapse_description)).perform(click()) onView(withContentDescription(R.string.abc_toolbar_collapse_description)).perform(click())
openActionBarOverflowOrOptionsMenu(context) openActionBarOverflowOrOptionsMenu(context)
onView(withMenu(id = R.id.refresh, titleId = R.string.menu_home_refresh)) onView(withMenu(id = R.id.refresh, titleId = R.string.menu_home_refresh))
.perform(click()) .perform(click())
openActionBarOverflowOrOptionsMenu(context) openActionBarOverflowOrOptionsMenu(context)
onView(withText(R.string.action_disconnect)).perform(click()) onView(withText(R.string.action_disconnect)).perform(click())
intended(hasComponent(LoginActivity::class.java.name), times(1)) intended(hasComponent(LoginActivity::class.java.name), times(1))
} }
@Test @Test
@ -102,7 +109,6 @@ class HomeActivityEspressoTest {
onView(withId(R.id.material_drawer_layout)).perform(DrawerActions.open()) onView(withId(R.id.material_drawer_layout)).perform(DrawerActions.open())
onView(withText(R.string.drawer_action_clear)).perform(click()) onView(withText(R.string.drawer_action_clear)).perform(click())
} }
// TODO: test articles opening and actions for cards and lists // TODO: test articles opening and actions for cards and lists

View File

@ -1,7 +1,5 @@
package apps.amine.bou.readerforselfoss package apps.amine.bou.readerforselfoss
import java.util.*
import android.content.Context import android.content.Context
import android.content.Intent import android.content.Intent
import android.support.test.InstrumentationRegistry.getInstrumentation import android.support.test.InstrumentationRegistry.getInstrumentation
@ -11,22 +9,19 @@ import android.support.test.espresso.assertion.ViewAssertions.matches
import android.support.test.espresso.intent.Intents import android.support.test.espresso.intent.Intents
import android.support.test.espresso.intent.Intents.intended import android.support.test.espresso.intent.Intents.intended
import android.support.test.espresso.intent.Intents.times import android.support.test.espresso.intent.Intents.times
import android.support.test.espresso.intent.matcher.IntentMatchers.* import android.support.test.espresso.intent.matcher.IntentMatchers.hasComponent
import android.support.test.espresso.intent.matcher.UriMatchers.hasHost import android.support.test.espresso.matcher.ViewMatchers.isDisplayed
import android.support.test.espresso.matcher.ViewMatchers.* import android.support.test.espresso.matcher.ViewMatchers.withId
import android.support.test.espresso.matcher.ViewMatchers.withText
import android.support.test.rule.ActivityTestRule import android.support.test.rule.ActivityTestRule
import android.support.test.runner.AndroidJUnit4 import android.support.test.runner.AndroidJUnit4
import apps.amine.bou.readerforselfoss.utils.Config
import org.hamcrest.Matchers.allOf import org.junit.After
import org.hamcrest.Matchers.equalTo
import org.junit.Before import org.junit.Before
import org.junit.Rule import org.junit.Rule
import org.junit.Test import org.junit.Test
import org.junit.runner.RunWith import org.junit.runner.RunWith
import java.util.*
import apps.amine.bou.readerforselfoss.utils.Config
import org.junit.After
@RunWith(AndroidJUnit4::class) @RunWith(AndroidJUnit4::class)
class IntroActivityEspressoTest { class IntroActivityEspressoTest {
@ -37,9 +32,9 @@ class IntroActivityEspressoTest {
@Before @Before
fun clearData() { fun clearData() {
val editor = val editor =
getInstrumentation().targetContext getInstrumentation().targetContext
.getSharedPreferences(Config.settingsName, Context.MODE_PRIVATE) .getSharedPreferences(Config.settingsName, Context.MODE_PRIVATE)
.edit() .edit()
editor.clear() editor.clear()
editor.commit() editor.commit()
@ -60,7 +55,6 @@ class IntroActivityEspressoTest {
intended(hasComponent(IntroActivity::class.java.name), times(1)) intended(hasComponent(IntroActivity::class.java.name), times(1))
intended(hasComponent(LoginActivity::class.java.name), times(1)) intended(hasComponent(LoginActivity::class.java.name), times(1))
} }
@Test @Test
@ -75,7 +69,7 @@ class IntroActivityEspressoTest {
onView(withText(R.string.intro_hello_title)).check(matches(isDisplayed())) onView(withText(R.string.intro_hello_title)).check(matches(isDisplayed()))
onView(withId(R.id.button_next)).perform(click()) onView(withId(R.id.button_next)).perform(click())
repeat(random) {_ -> repeat(random) { _ ->
onView(withText(R.string.intro_needs_selfoss_message)).check(matches(isDisplayed())) onView(withText(R.string.intro_needs_selfoss_message)).check(matches(isDisplayed()))
onView(withId(R.id.button_next)).perform(click()) onView(withId(R.id.button_next)).perform(click())
onView(withText(R.string.intro_all_set_message)).check(matches(isDisplayed())) onView(withText(R.string.intro_all_set_message)).check(matches(isDisplayed()))
@ -88,7 +82,6 @@ class IntroActivityEspressoTest {
intended(hasComponent(IntroActivity::class.java.name), times(1)) intended(hasComponent(IntroActivity::class.java.name), times(1))
intended(hasComponent(LoginActivity::class.java.name), times(1)) intended(hasComponent(LoginActivity::class.java.name), times(1))
} }
@After @After

View File

@ -5,27 +5,30 @@ import android.content.Intent
import android.support.test.InstrumentationRegistry import android.support.test.InstrumentationRegistry
import android.support.test.espresso.Espresso.onView import android.support.test.espresso.Espresso.onView
import android.support.test.espresso.Espresso.openActionBarOverflowOrOptionsMenu import android.support.test.espresso.Espresso.openActionBarOverflowOrOptionsMenu
import android.support.test.espresso.action.ViewActions.* import android.support.test.espresso.action.ViewActions.click
import android.support.test.espresso.action.ViewActions.closeSoftKeyboard
import android.support.test.espresso.action.ViewActions.pressBack
import android.support.test.espresso.action.ViewActions.typeText
import android.support.test.espresso.assertion.ViewAssertions.matches import android.support.test.espresso.assertion.ViewAssertions.matches
import android.support.test.espresso.intent.Intents import android.support.test.espresso.intent.Intents
import android.support.test.espresso.intent.Intents.intended import android.support.test.espresso.intent.Intents.intended
import android.support.test.espresso.intent.Intents.times import android.support.test.espresso.intent.Intents.times
import android.support.test.espresso.intent.matcher.IntentMatchers.hasComponent import android.support.test.espresso.intent.matcher.IntentMatchers.hasComponent
import android.support.test.espresso.matcher.ViewMatchers import android.support.test.espresso.matcher.ViewMatchers
import android.support.test.espresso.matcher.ViewMatchers.* import android.support.test.espresso.matcher.ViewMatchers.isRoot
import android.support.test.espresso.matcher.ViewMatchers.withEffectiveVisibility
import android.support.test.espresso.matcher.ViewMatchers.withId
import android.support.test.espresso.matcher.ViewMatchers.withText
import android.support.test.rule.ActivityTestRule import android.support.test.rule.ActivityTestRule
import android.support.test.runner.AndroidJUnit4 import android.support.test.runner.AndroidJUnit4
import apps.amine.bou.readerforselfoss.utils.Config
import com.mikepenz.aboutlibraries.ui.LibsActivity import com.mikepenz.aboutlibraries.ui.LibsActivity
import org.junit.After
import org.junit.Before import org.junit.Before
import org.junit.Rule import org.junit.Rule
import org.junit.Test import org.junit.Test
import org.junit.runner.RunWith import org.junit.runner.RunWith
import apps.amine.bou.readerforselfoss.utils.Config
import org.junit.After
@RunWith(AndroidJUnit4::class) @RunWith(AndroidJUnit4::class)
class LoginActivityEspressoTest { class LoginActivityEspressoTest {
@ -69,10 +72,8 @@ class LoginActivityEspressoTest {
onView(isRoot()).perform(pressBack()) onView(isRoot()).perform(pressBack())
intended(hasComponent(LoginActivity::class.java.name)) intended(hasComponent(LoginActivity::class.java.name))
} }
@Test @Test
fun wrongLoginUrl() { fun wrongLoginUrl() {
rule.launchActivity(Intent()) rule.launchActivity(Intent())
@ -103,7 +104,10 @@ class LoginActivityEspressoTest {
onView(withId(R.id.loginLayout)).check(matches(isHintOrErrorEnabled())) onView(withId(R.id.loginLayout)).check(matches(isHintOrErrorEnabled()))
onView(withId(R.id.passwordLayout)).check(matches(isHintOrErrorEnabled())) onView(withId(R.id.passwordLayout)).check(matches(isHintOrErrorEnabled()))
onView(withId(R.id.loginView)).perform(click()).perform(typeText(username), closeSoftKeyboard()) onView(withId(R.id.loginView)).perform(click()).perform(
typeText(username),
closeSoftKeyboard()
)
onView(withId(R.id.passwordLayout)).check(matches(isHintOrErrorEnabled())) onView(withId(R.id.passwordLayout)).check(matches(isHintOrErrorEnabled()))
@ -111,9 +115,9 @@ class LoginActivityEspressoTest {
onView(withId(R.id.passwordLayout)).check( onView(withId(R.id.passwordLayout)).check(
matches( matches(
isHintOrErrorEnabled()) isHintOrErrorEnabled()
)
) )
} }
@Test @Test
@ -125,16 +129,21 @@ class LoginActivityEspressoTest {
onView(withId(R.id.withLogin)).perform(click()) onView(withId(R.id.withLogin)).perform(click())
onView(withId(R.id.loginView)).perform(click()).perform(typeText(username), closeSoftKeyboard()) onView(withId(R.id.loginView)).perform(click()).perform(
typeText(username),
closeSoftKeyboard()
)
onView(withId(R.id.passwordView)).perform(click()).perform(typeText("WRONGPASS"), closeSoftKeyboard()) onView(withId(R.id.passwordView)).perform(click()).perform(
typeText("WRONGPASS"),
closeSoftKeyboard()
)
onView(withId(R.id.signInButton)).perform(click()) onView(withId(R.id.signInButton)).perform(click())
onView(withId(R.id.urlLayout)).check(matches(isHintOrErrorEnabled())) onView(withId(R.id.urlLayout)).check(matches(isHintOrErrorEnabled()))
onView(withId(R.id.loginLayout)).check(matches(isHintOrErrorEnabled())) onView(withId(R.id.loginLayout)).check(matches(isHintOrErrorEnabled()))
onView(withId(R.id.passwordLayout)).check(matches(isHintOrErrorEnabled())) onView(withId(R.id.passwordLayout)).check(matches(isHintOrErrorEnabled()))
} }
@Test @Test
@ -146,19 +155,23 @@ class LoginActivityEspressoTest {
onView(withId(R.id.withLogin)).perform(click()) onView(withId(R.id.withLogin)).perform(click())
onView(withId(R.id.loginView)).perform(click()).perform(typeText(username), closeSoftKeyboard()) onView(withId(R.id.loginView)).perform(click()).perform(
typeText(username),
closeSoftKeyboard()
)
onView(withId(R.id.passwordView)).perform(click()).perform(typeText(password), closeSoftKeyboard()) onView(withId(R.id.passwordView)).perform(click()).perform(
typeText(password),
closeSoftKeyboard()
)
onView(withId(R.id.signInButton)).perform(click()) onView(withId(R.id.signInButton)).perform(click())
intended(hasComponent(HomeActivity::class.java.name)) intended(hasComponent(HomeActivity::class.java.name))
} }
@After @After
fun releaseIntents() { fun releaseIntents() {
Intents.release() Intents.release()
} }
} }

View File

@ -17,7 +17,6 @@ import org.junit.Rule
import org.junit.Test import org.junit.Test
import org.junit.runner.RunWith import org.junit.runner.RunWith
@RunWith(AndroidJUnit4::class) @RunWith(AndroidJUnit4::class)
class MainActivityEspressoTest { class MainActivityEspressoTest {
@ -48,7 +47,6 @@ class MainActivityEspressoTest {
intended(hasComponent(MainActivity::class.java.name)) intended(hasComponent(MainActivity::class.java.name))
intended(hasComponent(IntroActivity::class.java.name)) intended(hasComponent(IntroActivity::class.java.name))
intended(hasComponent(LoginActivity::class.java.name), times(0)) intended(hasComponent(LoginActivity::class.java.name), times(0))
} }
@Test @Test
@ -61,13 +59,10 @@ class MainActivityEspressoTest {
intended(hasComponent(MainActivity::class.java.name)) intended(hasComponent(MainActivity::class.java.name))
intended(hasComponent(LoginActivity::class.java.name)) intended(hasComponent(LoginActivity::class.java.name))
intended(hasComponent(IntroActivity::class.java.name), times(0)) intended(hasComponent(IntroActivity::class.java.name), times(0))
} }
@After @After
fun releaseIntents() { fun releaseIntents() {
Intents.release() Intents.release()
} }
} }

View File

@ -1,31 +1,29 @@
package apps.amine.bou.readerforselfoss package apps.amine.bou.readerforselfoss
import android.view.View
import android.support.design.widget.TextInputLayout import android.support.design.widget.TextInputLayout
import android.support.test.espresso.matcher.ViewMatchers import android.support.test.espresso.matcher.ViewMatchers
import android.view.View
import org.hamcrest.Description import org.hamcrest.Description
import org.hamcrest.Matcher import org.hamcrest.Matcher
import org.hamcrest.TypeSafeMatcher
import org.hamcrest.Matchers import org.hamcrest.Matchers
import org.hamcrest.TypeSafeMatcher
fun isHintOrErrorEnabled(): Matcher<View> = fun isHintOrErrorEnabled(): Matcher<View> =
object : TypeSafeMatcher<View>() { object : TypeSafeMatcher<View>() {
override fun describeTo(description: Description?) {} override fun describeTo(description: Description?) {
override fun matchesSafely(item: View?): Boolean {
if (item !is TextInputLayout) {
return false
} }
return item.isHintEnabled || item.isErrorEnabled override fun matchesSafely(item: View?): Boolean {
if (item !is TextInputLayout) {
return false
}
return item.isHintEnabled || item.isErrorEnabled
}
} }
}
fun withMenu(id: Int, titleId: Int): Matcher<View> = fun withMenu(id: Int, titleId: Int): Matcher<View> =
Matchers.anyOf( Matchers.anyOf(
ViewMatchers.withId(id), ViewMatchers.withId(id),
ViewMatchers.withText(titleId) ViewMatchers.withText(titleId)
) )

View File

@ -24,7 +24,6 @@ import retrofit2.Call
import retrofit2.Callback import retrofit2.Callback
import retrofit2.Response import retrofit2.Response
class AddSourceActivity : AppCompatActivity() { class AddSourceActivity : AppCompatActivity() {
private var mSpoutsValue: String? = null private var mSpoutsValue: String? = null
@ -67,7 +66,12 @@ class AddSourceActivity : AppCompatActivity() {
} }
} }
private fun handleSpoutsSpinner(spoutsSpinner: Spinner, api: SelfossApi?, mProgress: ProgressBar, formContainer: ConstraintLayout) { private fun handleSpoutsSpinner(
spoutsSpinner: Spinner,
api: SelfossApi?,
mProgress: ProgressBar,
formContainer: ConstraintLayout
) {
val spoutsKV = HashMap<String, String>() val spoutsKV = HashMap<String, String>()
spoutsSpinner.onItemSelectedListener = object : AdapterView.OnItemSelectedListener { spoutsSpinner.onItemSelectedListener = object : AdapterView.OnItemSelectedListener {
override fun onItemSelected(adapterView: AdapterView<*>, view: View, i: Int, l: Long) { override fun onItemSelected(adapterView: AdapterView<*>, view: View, i: Int, l: Long) {
@ -82,7 +86,10 @@ class AddSourceActivity : AppCompatActivity() {
var items: Map<String, Spout> var items: Map<String, Spout>
api!!.spouts().enqueue(object : Callback<Map<String, Spout>> { api!!.spouts().enqueue(object : Callback<Map<String, Spout>> {
override fun onResponse(call: Call<Map<String, Spout>>, response: Response<Map<String, Spout>>) { override fun onResponse(
call: Call<Map<String, Spout>>,
response: Response<Map<String, Spout>>
) {
if (response.body() != null) { if (response.body() != null) {
items = response.body()!! items = response.body()!!
@ -98,10 +105,10 @@ class AddSourceActivity : AppCompatActivity() {
ArrayAdapter( ArrayAdapter(
this@AddSourceActivity, this@AddSourceActivity,
android.R.layout.simple_spinner_item, android.R.layout.simple_spinner_item,
itemsStrings) itemsStrings
)
spinnerArrayAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item) spinnerArrayAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item)
spoutsSpinner.adapter = spinnerArrayAdapter spoutsSpinner.adapter = spinnerArrayAdapter
} else { } else {
handleProblemWithSpouts() handleProblemWithSpouts()
} }
@ -112,13 +119,21 @@ class AddSourceActivity : AppCompatActivity() {
} }
private fun handleProblemWithSpouts() { private fun handleProblemWithSpouts() {
Toast.makeText(this@AddSourceActivity, R.string.cant_get_spouts, Toast.LENGTH_SHORT).show() Toast.makeText(
this@AddSourceActivity,
R.string.cant_get_spouts,
Toast.LENGTH_SHORT
).show()
mProgress.visibility = View.GONE mProgress.visibility = View.GONE
} }
}) })
} }
private fun maybeGetDetailsFromIntentSharing(intent: Intent, sourceUri: EditText, nameInput: EditText) { private fun maybeGetDetailsFromIntentSharing(
intent: Intent,
sourceUri: EditText,
nameInput: EditText
) {
if (Intent.ACTION_SEND == intent.action && "text/plain" == intent.type) { if (Intent.ACTION_SEND == intent.action && "text/plain" == intent.type) {
sourceUri.setText(intent.getStringExtra(Intent.EXTRA_TEXT)) sourceUri.setText(intent.getStringExtra(Intent.EXTRA_TEXT))
nameInput.setText(intent.getStringExtra(Intent.EXTRA_TITLE)) nameInput.setText(intent.getStringExtra(Intent.EXTRA_TITLE))
@ -146,16 +161,27 @@ class AddSourceActivity : AppCompatActivity() {
tags.text.toString(), tags.text.toString(),
"" ""
).enqueue(object : Callback<SuccessResponse> { ).enqueue(object : Callback<SuccessResponse> {
override fun onResponse(call: Call<SuccessResponse>, response: Response<SuccessResponse>) { override fun onResponse(
call: Call<SuccessResponse>,
response: Response<SuccessResponse>
) {
if (response.body() != null && response.body()!!.isSuccess) { if (response.body() != null && response.body()!!.isSuccess) {
finish() finish()
} else { } else {
Toast.makeText(this@AddSourceActivity, R.string.cant_create_source, Toast.LENGTH_SHORT).show() Toast.makeText(
this@AddSourceActivity,
R.string.cant_create_source,
Toast.LENGTH_SHORT
).show()
} }
} }
override fun onFailure(call: Call<SuccessResponse>, t: Throwable) { override fun onFailure(call: Call<SuccessResponse>, t: Throwable) {
Toast.makeText(this@AddSourceActivity, R.string.cant_create_source, Toast.LENGTH_SHORT).show() Toast.makeText(
this@AddSourceActivity,
R.string.cant_create_source,
Toast.LENGTH_SHORT
).show()
} }
}) })
} }

View File

@ -11,7 +11,11 @@ import android.os.Bundle
import android.preference.PreferenceManager import android.preference.PreferenceManager
import android.support.v4.view.MenuItemCompat import android.support.v4.view.MenuItemCompat
import android.support.v7.app.AppCompatActivity import android.support.v7.app.AppCompatActivity
import android.support.v7.widget.* import android.support.v7.widget.DividerItemDecoration
import android.support.v7.widget.GridLayoutManager
import android.support.v7.widget.RecyclerView
import android.support.v7.widget.SearchView
import android.support.v7.widget.StaggeredGridLayoutManager
import android.support.v7.widget.helper.ItemTouchHelper import android.support.v7.widget.helper.ItemTouchHelper
import android.view.Menu import android.view.Menu
import android.view.MenuItem import android.view.MenuItem
@ -63,10 +67,10 @@ import com.mikepenz.materialdrawer.holder.BadgeStyle
import com.mikepenz.materialdrawer.model.DividerDrawerItem import com.mikepenz.materialdrawer.model.DividerDrawerItem
import com.mikepenz.materialdrawer.model.PrimaryDrawerItem import com.mikepenz.materialdrawer.model.PrimaryDrawerItem
import com.mikepenz.materialdrawer.model.SecondaryDrawerItem import com.mikepenz.materialdrawer.model.SecondaryDrawerItem
import kotlinx.android.synthetic.main.activity_home.*
import retrofit2.Call import retrofit2.Call
import retrofit2.Callback import retrofit2.Callback
import retrofit2.Response import retrofit2.Response
import kotlinx.android.synthetic.main.activity_home.*
class HomeActivity : AppCompatActivity(), SearchView.OnQueryTextListener { class HomeActivity : AppCompatActivity(), SearchView.OnQueryTextListener {
@ -117,13 +121,8 @@ class HomeActivity : AppCompatActivity(), SearchView.OnQueryTextListener {
private var badgeAll: Int = -1 private var badgeAll: Int = -1
private var badgeFavs: Int = -1 private var badgeFavs: Int = -1
data class DrawerData(val tags: List<Tag>?, val sources: List<Sources>?) data class DrawerData(val tags: List<Tag>?, val sources: List<Sources>?)
override fun onStart() { override fun onStart() {
super.onStart() super.onStart()
customTabActivityHelper.bindCustomTabsService(this) customTabActivityHelper.bindCustomTabsService(this)
@ -147,10 +146,15 @@ class HomeActivity : AppCompatActivity(), SearchView.OnQueryTextListener {
settings = getSharedPreferences(Config.settingsName, Context.MODE_PRIVATE) settings = getSharedPreferences(Config.settingsName, Context.MODE_PRIVATE)
sharedPref = PreferenceManager.getDefaultSharedPreferences(this) sharedPref = PreferenceManager.getDefaultSharedPreferences(this)
api = SelfossApi(this, this@HomeActivity, settings.getBoolean("isSelfSignedCert", false), sharedPref.getBoolean("should_log_everything", false)) api = SelfossApi(
this,
this@HomeActivity,
settings.getBoolean("isSelfSignedCert", false),
sharedPref.getBoolean("should_log_everything", false)
)
items = ArrayList() items = ArrayList()
appColors = AppColors(this@HomeActivity) appColors = AppColors(this@HomeActivity)
handleBottomBar() handleBottomBar()
handleDrawer() handleDrawer()
@ -163,21 +167,36 @@ class HomeActivity : AppCompatActivity(), SearchView.OnQueryTextListener {
swipeRefreshLayout.setColorSchemeResources( swipeRefreshLayout.setColorSchemeResources(
R.color.refresh_progress_1, R.color.refresh_progress_1,
R.color.refresh_progress_2, R.color.refresh_progress_2,
R.color.refresh_progress_3) R.color.refresh_progress_3
)
swipeRefreshLayout.setOnRefreshListener { swipeRefreshLayout.setOnRefreshListener {
handleDrawerItems() handleDrawerItems()
getElementsAccordingToTab() getElementsAccordingToTab()
} }
val simpleItemTouchCallback = val simpleItemTouchCallback =
object : ItemTouchHelper.SimpleCallback(0, ItemTouchHelper.LEFT or ItemTouchHelper.RIGHT) { object : ItemTouchHelper.SimpleCallback(
0,
ItemTouchHelper.LEFT or ItemTouchHelper.RIGHT
) {
override fun getSwipeDirs(
recyclerView: RecyclerView?,
viewHolder: RecyclerView.ViewHolder?
): Int =
if (elementsShown != UNREAD_SHOWN) {
0
} else {
super.getSwipeDirs(
recyclerView,
viewHolder
)
}
override fun getSwipeDirs(recyclerView: RecyclerView?, viewHolder: RecyclerView.ViewHolder?): Int = override fun onMove(
if (elementsShown != UNREAD_SHOWN) 0 else super.getSwipeDirs(recyclerView, viewHolder) recyclerView: RecyclerView,
viewHolder: RecyclerView.ViewHolder,
override fun onMove(recyclerView: RecyclerView, target: RecyclerView.ViewHolder
viewHolder: RecyclerView.ViewHolder, ): Boolean = false
target: RecyclerView.ViewHolder): Boolean = false
override fun onSwiped(viewHolder: RecyclerView.ViewHolder, swipeDir: Int) { override fun onSwiped(viewHolder: RecyclerView.ViewHolder, swipeDir: Int) {
try { try {
@ -193,28 +212,37 @@ class HomeActivity : AppCompatActivity(), SearchView.OnQueryTextListener {
if (items.size > 0) { if (items.size > 0) {
badgeNew-- badgeNew--
reloadBadgeContent() reloadBadgeContent()
} } else {
else
tabNewBadge.hide() tabNewBadge.hide()
}
val manager = recyclerView.layoutManager val manager = recyclerView.layoutManager
val lastVisibleItem: Int = when (manager) { val lastVisibleItem: Int = when (manager) {
is StaggeredGridLayoutManager -> manager.findLastCompletelyVisibleItemPositions(null).last() is StaggeredGridLayoutManager -> manager.findLastCompletelyVisibleItemPositions(
null
).last()
is GridLayoutManager -> manager.findLastCompletelyVisibleItemPosition() is GridLayoutManager -> manager.findLastCompletelyVisibleItemPosition()
else -> 0 else -> 0
} }
if (lastVisibleItem === items.size && items.size <= maxItemNumber() && maxItemNumber() >= itemsNumber) { if (lastVisibleItem === items.size &&
getElementsAccordingToTab(appendResults = true, offsetOverride = lastVisibleItem) items.size <= maxItemNumber() &&
maxItemNumber() >= itemsNumber
) {
getElementsAccordingToTab(
appendResults = true,
offsetOverride = lastVisibleItem
)
} }
} catch (e: IndexOutOfBoundsException) { } catch (e: IndexOutOfBoundsException) {
Crashlytics.setUserIdentifier(userIdentifier) Crashlytics.setUserIdentifier(userIdentifier)
Crashlytics.log(100, "SWIPE_INDEX_OUT_OF_BOUND", "IndexOutOfBoundsException when swiping") Crashlytics.log(
100,
"SWIPE_INDEX_OUT_OF_BOUND",
"IndexOutOfBoundsException when swiping"
)
Crashlytics.logException(e) Crashlytics.logException(e)
} }
} }
} }
@ -264,7 +292,6 @@ class HomeActivity : AppCompatActivity(), SearchView.OnQueryTextListener {
bottomBar.setMode(BottomNavigationBar.MODE_SHIFTING) bottomBar.setMode(BottomNavigationBar.MODE_SHIFTING)
bottomBar.setBackgroundStyle(BottomNavigationBar.BACKGROUND_STYLE_STATIC) bottomBar.setBackgroundStyle(BottomNavigationBar.BACKGROUND_STYLE_STATIC)
} }
override fun onResume() { override fun onResume() {
@ -290,7 +317,6 @@ class HomeActivity : AppCompatActivity(), SearchView.OnQueryTextListener {
customTabActivityHelper.unbindCustomTabsService(this) customTabActivityHelper.unbindCustomTabsService(this)
} }
private fun handleSharedPrefs() { private fun handleSharedPrefs() {
debugReadingItems = sharedPref.getBoolean("read_debug", false) debugReadingItems = sharedPref.getBoolean("read_debug", false)
clickBehavior = sharedPref.getBoolean("tab_on_tap", false) clickBehavior = sharedPref.getBoolean("tab_on_tap", false)
@ -327,7 +353,7 @@ class HomeActivity : AppCompatActivity(), SearchView.OnQueryTextListener {
bottomBar.hide() bottomBar.hide()
} }
if (displayAccountHeader) if (displayAccountHeader) {
accountHeader { accountHeader {
background = R.drawable.bg background = R.drawable.bg
profile(settings.getString("url", "")) { profile(settings.getString("url", "")) {
@ -335,13 +361,17 @@ class HomeActivity : AppCompatActivity(), SearchView.OnQueryTextListener {
} }
selectionListEnabledForSingleProfile = false selectionListEnabledForSingleProfile = false
} }
}
footer { footer {
primaryItem(R.string.drawer_report_bug) { primaryItem(R.string.drawer_report_bug) {
icon = R.drawable.ic_bug_report icon = R.drawable.ic_bug_report
iconTintingEnabled = true iconTintingEnabled = true
onClick { _ -> onClick { _ ->
IssueReporterLauncher.forTarget(getString(R.string.report_github_user), getString(R.string.report_github_repo)) IssueReporterLauncher.forTarget(
getString(R.string.report_github_user),
getString(R.string.report_github_repo)
)
.theme(R.style.Theme_App_Light) .theme(R.style.Theme_App_Light)
.guestToken(BuildConfig.GITHUB_TOKEN) .guestToken(BuildConfig.GITHUB_TOKEN)
.guestEmailRequired(true) .guestEmailRequired(true)
@ -370,21 +400,20 @@ class HomeActivity : AppCompatActivity(), SearchView.OnQueryTextListener {
} }
} }
} }
} }
private fun handleDrawerItems() { private fun handleDrawerItems() {
fun handleDrawerData(maybeDrawerData: DrawerData?, loadedFromCache: Boolean = false) { fun handleDrawerData(maybeDrawerData: DrawerData?, loadedFromCache: Boolean = false) {
fun handleTags(maybeTags: List<Tag>?) { fun handleTags(maybeTags: List<Tag>?) {
if (maybeTags == null) { if (maybeTags == null) {
if (loadedFromCache) if (loadedFromCache) {
drawer.addItem( drawer.addItem(
SecondaryDrawerItem() SecondaryDrawerItem()
.withName(getString(R.string.drawer_error_loading_tags)) .withName(getString(R.string.drawer_error_loading_tags))
.withSelectable(false)) .withSelectable(false)
} )
else { }
} else {
for (tag in maybeTags) { for (tag in maybeTags) {
val gd = GradientDrawable() val gd = GradientDrawable()
gd.setColor(Color.parseColor(tag.color)) gd.setColor(Color.parseColor(tag.color))
@ -409,19 +438,19 @@ class HomeActivity : AppCompatActivity(), SearchView.OnQueryTextListener {
) )
} }
} }
} }
fun handleSources(maybeSources: List<Sources>?) { fun handleSources(maybeSources: List<Sources>?) {
if (maybeSources == null) { if (maybeSources == null) {
if (loadedFromCache) if (loadedFromCache) {
drawer.addItem( drawer.addItem(
SecondaryDrawerItem() SecondaryDrawerItem()
.withName(getString(R.string.drawer_error_loading_sources)) .withName(getString(R.string.drawer_error_loading_sources))
.withSelectable(false)) .withSelectable(false)
} )
else }
for (tag in maybeSources) } else {
for (tag in maybeSources) {
drawer.addItem( drawer.addItem(
CustomUrlPrimaryDrawerItem() CustomUrlPrimaryDrawerItem()
.withName(tag.title) .withName(tag.title)
@ -433,7 +462,8 @@ class HomeActivity : AppCompatActivity(), SearchView.OnQueryTextListener {
false false
} }
) )
}
}
} }
drawer.removeAllItems() drawer.removeAllItems()
@ -456,7 +486,8 @@ class HomeActivity : AppCompatActivity(), SearchView.OnQueryTextListener {
SecondaryDrawerItem() SecondaryDrawerItem()
.withName(getString(R.string.drawer_item_tags)) .withName(getString(R.string.drawer_item_tags))
.withIdentifier(DRAWER_ID_TAGS) .withIdentifier(DRAWER_ID_TAGS)
.withSelectable(false)) .withSelectable(false)
)
handleTags(maybeDrawerData.tags) handleTags(maybeDrawerData.tags)
drawer.addItem(DividerDrawerItem()) drawer.addItem(DividerDrawerItem())
drawer.addItem( drawer.addItem(
@ -481,41 +512,46 @@ class HomeActivity : AppCompatActivity(), SearchView.OnQueryTextListener {
.withOnDrawerItemClickListener { _, _, _ -> .withOnDrawerItemClickListener { _, _, _ ->
LibsBuilder() LibsBuilder()
.withActivityStyle( .withActivityStyle(
if (appColors.isDarkTheme) if (appColors.isDarkTheme) {
Libs.ActivityStyle.LIGHT_DARK_TOOLBAR Libs.ActivityStyle.LIGHT_DARK_TOOLBAR
else } else {
Libs.ActivityStyle.DARK Libs.ActivityStyle.DARK
}
) )
.withAboutIconShown(true) .withAboutIconShown(true)
.withAboutVersionShown(true) .withAboutVersionShown(true)
.start(this@HomeActivity) .start(this@HomeActivity)
false false
}) }
)
if (!loadedFromCache) if (!loadedFromCache) {
Reservoir.putAsync("drawerData", maybeDrawerData, object : ReservoirPutCallback { Reservoir.putAsync(
override fun onSuccess() {} "drawerData", maybeDrawerData, object : ReservoirPutCallback {
override fun onSuccess() {
}
override fun onFailure(p0: Exception?) { override fun onFailure(p0: Exception?) {
} }
}) })
}
} else { } else {
if (!loadedFromCache) { if (!loadedFromCache) {
drawer.addItem( drawer.addItem(
PrimaryDrawerItem() PrimaryDrawerItem()
.withName(getString(R.string.no_tags_loaded)) .withName(getString(R.string.no_tags_loaded))
.withIdentifier(DRAWER_ID_TAGS) .withIdentifier(DRAWER_ID_TAGS)
.withSelectable(false)) .withSelectable(false)
)
drawer.addItem( drawer.addItem(
PrimaryDrawerItem() PrimaryDrawerItem()
.withName(getString(R.string.no_sources_loaded)) .withName(getString(R.string.no_sources_loaded))
.withIdentifier(DRAWER_ID_SOURCES) .withIdentifier(DRAWER_ID_SOURCES)
.withSelectable(false)) .withSelectable(false)
)
} }
} }
} }
fun drawerApiCalls(maybeDrawerData: DrawerData?) { fun drawerApiCalls(maybeDrawerData: DrawerData?) {
@ -523,23 +559,28 @@ class HomeActivity : AppCompatActivity(), SearchView.OnQueryTextListener {
var sources: List<Sources>? var sources: List<Sources>?
fun sourcesApiCall() { fun sourcesApiCall() {
api.sources.enqueue(object: Callback<List<Sources>> { api.sources.enqueue(object : Callback<List<Sources>> {
override fun onResponse(call: Call<List<Sources>>?, response: Response<List<Sources>>) { override fun onResponse(
call: Call<List<Sources>>?,
response: Response<List<Sources>>
) {
sources = response.body() sources = response.body()
val apiDrawerData = DrawerData(tags, sources) val apiDrawerData = DrawerData(tags, sources)
if ((maybeDrawerData != null && maybeDrawerData != apiDrawerData) || maybeDrawerData == null) if ((maybeDrawerData != null && maybeDrawerData != apiDrawerData) || maybeDrawerData == null) {
handleDrawerData(apiDrawerData) handleDrawerData(apiDrawerData)
}
} }
override fun onFailure(call: Call<List<Sources>>?, t: Throwable?) { override fun onFailure(call: Call<List<Sources>>?, t: Throwable?) {
} }
}) })
} }
api.tags.enqueue(object: Callback<List<Tag>> { api.tags.enqueue(object : Callback<List<Tag>> {
override fun onResponse(call: Call<List<Tag>>, response: Response<List<Tag>>) { override fun onResponse(
call: Call<List<Tag>>,
response: Response<List<Tag>>
) {
tags = response.body() tags = response.body()
sourcesApiCall() sourcesApiCall()
} }
@ -547,14 +588,18 @@ class HomeActivity : AppCompatActivity(), SearchView.OnQueryTextListener {
override fun onFailure(call: Call<List<Tag>>?, t: Throwable?) { override fun onFailure(call: Call<List<Tag>>?, t: Throwable?) {
sourcesApiCall() sourcesApiCall()
} }
}) })
} }
drawer.addItem(PrimaryDrawerItem().withName(getString(R.string.drawer_loading)).withSelectable(false)) drawer.addItem(
PrimaryDrawerItem().withName(getString(R.string.drawer_loading)).withSelectable(
false
)
)
val resultType = object : TypeToken<DrawerData>() {}.type //NOSONAR val resultType = object : TypeToken<DrawerData>() {}.type //NOSONAR
Reservoir.getAsync("drawerData", resultType, object: ReservoirGetCallback<DrawerData> { Reservoir.getAsync(
"drawerData", resultType, object : ReservoirGetCallback<DrawerData> {
override fun onSuccess(maybeDrawerData: DrawerData?) { override fun onSuccess(maybeDrawerData: DrawerData?) {
handleDrawerData(maybeDrawerData, loadedFromCache = true) handleDrawerData(maybeDrawerData, loadedFromCache = true)
drawerApiCalls(maybeDrawerData) drawerApiCalls(maybeDrawerData)
@ -563,14 +608,16 @@ class HomeActivity : AppCompatActivity(), SearchView.OnQueryTextListener {
override fun onFailure(p0: Exception?) { override fun onFailure(p0: Exception?) {
drawerApiCalls(null) drawerApiCalls(null)
} }
}) })
} }
private fun reloadLayoutManager() { private fun reloadLayoutManager() {
val mLayoutManager: RecyclerView.LayoutManager val mLayoutManager: RecyclerView.LayoutManager
if (shouldBeCardView) { if (shouldBeCardView) {
mLayoutManager = StaggeredGridLayoutManager(calculateNoOfColumns(), StaggeredGridLayoutManager.VERTICAL) mLayoutManager = StaggeredGridLayoutManager(
calculateNoOfColumns(),
StaggeredGridLayoutManager.VERTICAL
)
mLayoutManager.gapStrategy = StaggeredGridLayoutManager.GAP_HANDLING_MOVE_ITEMS_BETWEEN_SPANS mLayoutManager.gapStrategy = StaggeredGridLayoutManager.GAP_HANDLING_MOVE_ITEMS_BETWEEN_SPANS
} else { } else {
mLayoutManager = GridLayoutManager(this, calculateNoOfColumns()) mLayoutManager = GridLayoutManager(this, calculateNoOfColumns())
@ -616,18 +663,19 @@ class HomeActivity : AppCompatActivity(), SearchView.OnQueryTextListener {
else -> Unit else -> Unit
} }
} }
}) })
} }
private fun handleInfiniteScroll() { private fun handleInfiniteScroll() {
if (recyclerViewScrollListener == null) if (recyclerViewScrollListener == null) {
recyclerViewScrollListener = object : RecyclerView.OnScrollListener() { recyclerViewScrollListener = object : RecyclerView.OnScrollListener() {
override fun onScrolled(localRecycler: RecyclerView?, dx: Int, dy: Int) { override fun onScrolled(localRecycler: RecyclerView?, dx: Int, dy: Int) {
if (localRecycler != null && dy > 0) { if (localRecycler != null && dy > 0) {
val manager = recyclerView.layoutManager val manager = recyclerView.layoutManager
val lastVisibleItem: Int = when (manager) { val lastVisibleItem: Int = when (manager) {
is StaggeredGridLayoutManager -> manager.findLastCompletelyVisibleItemPositions(null).last() is StaggeredGridLayoutManager -> manager.findLastCompletelyVisibleItemPositions(
null
).last()
is GridLayoutManager -> manager.findLastCompletelyVisibleItemPosition() is GridLayoutManager -> manager.findLastCompletelyVisibleItemPosition()
else -> 0 else -> 0
} }
@ -638,6 +686,7 @@ class HomeActivity : AppCompatActivity(), SearchView.OnQueryTextListener {
} }
} }
} }
}
recyclerView.clearOnScrollListeners() recyclerView.clearOnScrollListeners()
recyclerView.addOnScrollListener(recyclerViewScrollListener) recyclerView.addOnScrollListener(recyclerViewScrollListener)
@ -652,8 +701,11 @@ class HomeActivity : AppCompatActivity(), SearchView.OnQueryTextListener {
recyclerView.visibility = View.VISIBLE recyclerView.visibility = View.VISIBLE
} }
private fun getElementsAccordingToTab(appendResults: Boolean = false, offsetOverride: Int? = null) { private fun getElementsAccordingToTab(
offset = if (appendResults && offsetOverride === null) { appendResults: Boolean = false,
offsetOverride: Int? = null
) {
offset = if (appendResults && offsetOverride === null) {
(offset + itemsNumber) (offset + itemsNumber)
} else { } else {
offsetOverride ?: 0 offsetOverride ?: 0
@ -668,66 +720,107 @@ class HomeActivity : AppCompatActivity(), SearchView.OnQueryTextListener {
} }
} }
private fun doCallTo(appendResults: Boolean, 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) {
if (appendResults) if (appendResults) {
items.addAll(response.body() as ArrayList<Item>) items.addAll(response.body() as ArrayList<Item>)
else } else {
items = response.body() as ArrayList<Item> items = response.body() as ArrayList<Item>
}
} }
} else { } else {
if (!appendResults) if (!appendResults) {
items = ArrayList() items = ArrayList()
}
} }
if (didUpdate) if (didUpdate) {
handleListResult(appendResults) handleListResult(appendResults)
}
mayBeEmpty() mayBeEmpty()
swipeRefreshLayout.isRefreshing = false swipeRefreshLayout.isRefreshing = false
} }
if (!swipeRefreshLayout.isRefreshing) if (!swipeRefreshLayout.isRefreshing) {
swipeRefreshLayout.post { swipeRefreshLayout.isRefreshing = true } swipeRefreshLayout.post { swipeRefreshLayout.isRefreshing = true }
}
call(maybeTagFilter?.tag, maybeSourceFilter?.id?.toLong(), maybeSearchFilter) call(maybeTagFilter?.tag, maybeSourceFilter?.id?.toLong(), maybeSearchFilter)
.enqueue(object : Callback<List<Item>> { .enqueue(object : Callback<List<Item>> {
override fun onResponse(call: Call<List<Item>>, response: Response<List<Item>>) { override fun onResponse(
call: Call<List<Item>>,
response: Response<List<Item>>
) {
handleItemsResponse(response) handleItemsResponse(response)
} }
override fun onFailure(call: Call<List<Item>>, t: Throwable) { override fun onFailure(call: Call<List<Item>>, t: Throwable) {
swipeRefreshLayout.isRefreshing = false swipeRefreshLayout.isRefreshing = false
Toast.makeText(this@HomeActivity, toastMessage, Toast.LENGTH_SHORT).show() Toast.makeText(
this@HomeActivity,
toastMessage,
Toast.LENGTH_SHORT
).show()
} }
}) })
} }
private fun getUnRead(appendResults: Boolean = false) { private fun getUnRead(appendResults: Boolean = false) {
elementsShown = UNREAD_SHOWN elementsShown = UNREAD_SHOWN
doCallTo(appendResults, R.string.cant_get_new_elements){t, id, f -> api.newItems(t, id, f, itemsNumber, offset)} doCallTo(appendResults, R.string.cant_get_new_elements) { t, id, f ->
api.newItems(
t,
id,
f,
itemsNumber,
offset
)
}
} }
private fun getRead(appendResults: Boolean = false) { private fun getRead(appendResults: Boolean = false) {
elementsShown = READ_SHOWN elementsShown = READ_SHOWN
doCallTo(appendResults, R.string.cant_get_read){t, id, f -> api.readItems(t, id, f, itemsNumber, offset)} doCallTo(appendResults, R.string.cant_get_read) { t, id, f ->
api.readItems(
t,
id,
f,
itemsNumber,
offset
)
}
} }
private fun getStarred(appendResults: Boolean = false) { private fun getStarred(appendResults: Boolean = false) {
elementsShown = FAV_SHOWN elementsShown = FAV_SHOWN
doCallTo(appendResults, R.string.cant_get_favs){t, id, f -> api.starredItems(t, id, f, itemsNumber, offset)} doCallTo(appendResults, R.string.cant_get_favs) { t, id, f ->
api.starredItems(
t,
id,
f,
itemsNumber,
offset
)
}
} }
private fun handleListResult(appendResults: Boolean = false) { private fun handleListResult(appendResults: Boolean = false) {
if (appendResults) { if (appendResults) {
val oldManager = recyclerView.layoutManager val oldManager = recyclerView.layoutManager
firstVisible = if ((oldManager is StaggeredGridLayoutManager)) { firstVisible = when (oldManager) {
oldManager.findFirstCompletelyVisibleItemPositions(null).last() is StaggeredGridLayoutManager ->
oldManager.findFirstCompletelyVisibleItemPositions(null).last()
is GridLayoutManager ->
oldManager.findFirstCompletelyVisibleItemPosition()
else -> 0
} }
else
(oldManager as GridLayoutManager)?.findFirstCompletelyVisibleItemPosition()
} }
reloadLayoutManager() reloadLayoutManager()
@ -745,7 +838,8 @@ class HomeActivity : AppCompatActivity(), SearchView.OnQueryTextListener {
fullHeightCards, fullHeightCards,
appColors, appColors,
debugReadingItems, debugReadingItems,
userIdentifier) userIdentifier
)
} else { } else {
mAdapter = mAdapter =
ItemListAdapter( ItemListAdapter(
@ -757,10 +851,15 @@ class HomeActivity : AppCompatActivity(), SearchView.OnQueryTextListener {
internalBrowser, internalBrowser,
articleViewer, articleViewer,
debugReadingItems, debugReadingItems,
userIdentifier) userIdentifier
)
recyclerView.addItemDecoration(DividerItemDecoration(this@HomeActivity, recyclerView.addItemDecoration(
DividerItemDecoration.VERTICAL)) DividerItemDecoration(
this@HomeActivity,
DividerItemDecoration.VERTICAL
)
)
} }
recyclerView.adapter = mAdapter recyclerView.adapter = mAdapter
mAdapter.notifyDataSetChanged() mAdapter.notifyDataSetChanged()
@ -785,7 +884,8 @@ class HomeActivity : AppCompatActivity(), SearchView.OnQueryTextListener {
} }
} }
override fun onFailure(call: Call<Stats>, t: Throwable) {} override fun onFailure(call: Call<Stats>, t: Throwable) {
}
}) })
} else { } else {
reloadBadgeContent(succeeded = false) reloadBadgeContent(succeeded = false)
@ -794,10 +894,11 @@ class HomeActivity : AppCompatActivity(), SearchView.OnQueryTextListener {
private fun reloadBadgeContent(succeeded: Boolean = true) { private fun reloadBadgeContent(succeeded: Boolean = true) {
if (succeeded) { if (succeeded) {
if (displayUnreadCount) if (displayUnreadCount) {
tabNewBadge tabNewBadge
.setText(badgeNew.toString()) .setText(badgeNew.toString())
.maybeShow() .maybeShow()
}
if (displayAllCount) { if (displayAllCount) {
tabArchiveBadge tabArchiveBadge
.setText(badgeAll.toString()) .setText(badgeAll.toString())
@ -851,7 +952,6 @@ class HomeActivity : AppCompatActivity(), SearchView.OnQueryTextListener {
} }
else -> super.onActivityResult(req, result, data) else -> super.onActivityResult(req, result, data)
} }
} }
override fun onCreateOptionsMenu(menu: Menu): Boolean { override fun onCreateOptionsMenu(menu: Menu): Boolean {
@ -869,14 +969,23 @@ class HomeActivity : AppCompatActivity(), SearchView.OnQueryTextListener {
when (item.itemId) { when (item.itemId) {
R.id.refresh -> { R.id.refresh -> {
api.update().enqueue(object : Callback<String> { api.update().enqueue(object : Callback<String> {
override fun onResponse(call: Call<String>, response: Response<String>) { override fun onResponse(
Toast.makeText(this@HomeActivity, call: Call<String>,
R.string.refresh_success_response, Toast.LENGTH_LONG) response: Response<String>
) {
Toast.makeText(
this@HomeActivity,
R.string.refresh_success_response, Toast.LENGTH_LONG
)
.show() .show()
} }
override fun onFailure(call: Call<String>, t: Throwable) { override fun onFailure(call: Call<String>, t: Throwable) {
Toast.makeText(this@HomeActivity, R.string.refresh_failer_message, Toast.LENGTH_SHORT).show() Toast.makeText(
this@HomeActivity,
R.string.refresh_failer_message,
Toast.LENGTH_SHORT
).show()
} }
}) })
Toast.makeText(this, R.string.refresh_in_progress, Toast.LENGTH_SHORT).show() Toast.makeText(this, R.string.refresh_in_progress, Toast.LENGTH_SHORT).show()
@ -888,25 +997,45 @@ class HomeActivity : AppCompatActivity(), SearchView.OnQueryTextListener {
val ids = items.map { it.id } val ids = items.map { it.id }
api.readAll(ids).enqueue(object : Callback<SuccessResponse> { api.readAll(ids).enqueue(object : Callback<SuccessResponse> {
override fun onResponse(call: Call<SuccessResponse>, response: Response<SuccessResponse>) { override fun onResponse(
call: Call<SuccessResponse>,
response: Response<SuccessResponse>
) {
if (response.body() != null && response.body()!!.isSuccess) { if (response.body() != null && response.body()!!.isSuccess) {
Toast.makeText(this@HomeActivity, R.string.all_posts_read, Toast.LENGTH_SHORT).show() Toast.makeText(
this@HomeActivity,
R.string.all_posts_read,
Toast.LENGTH_SHORT
).show()
tabNewBadge.removeBadge() tabNewBadge.removeBadge()
} else {
Toast.makeText(
this@HomeActivity,
R.string.all_posts_not_read,
Toast.LENGTH_SHORT
).show()
} }
else
Toast.makeText(this@HomeActivity, R.string.all_posts_not_read, Toast.LENGTH_SHORT).show()
swipeRefreshLayout.isRefreshing = false swipeRefreshLayout.isRefreshing = false
} }
override fun onFailure(call: Call<SuccessResponse>, t: Throwable) { override fun onFailure(call: Call<SuccessResponse>, t: Throwable) {
Toast.makeText(this@HomeActivity, R.string.all_posts_not_read, Toast.LENGTH_SHORT).show() Toast.makeText(
this@HomeActivity,
R.string.all_posts_not_read,
Toast.LENGTH_SHORT
).show()
swipeRefreshLayout.isRefreshing = false swipeRefreshLayout.isRefreshing = false
} }
}) })
items = ArrayList() items = ArrayList()
if (items.isEmpty()) if (items.isEmpty()) {
Toast.makeText(this@HomeActivity, R.string.nothing_here, Toast.LENGTH_SHORT).show() Toast.makeText(
this@HomeActivity,
R.string.nothing_here,
Toast.LENGTH_SHORT
).show()
}
handleListResult() handleListResult()
} }
return true return true
@ -939,10 +1068,10 @@ class HomeActivity : AppCompatActivity(), SearchView.OnQueryTextListener {
} }
private fun maxItemNumber(): Int = private fun maxItemNumber(): Int =
when (elementsShown) { when (elementsShown) {
UNREAD_SHOWN -> badgeNew UNREAD_SHOWN -> badgeNew
READ_SHOWN -> badgeAll READ_SHOWN -> badgeAll
FAV_SHOWN -> badgeFavs FAV_SHOWN -> badgeFavs
else -> badgeNew // if !elementsShown then unread are fetched. else -> badgeNew // if !elementsShown then unread are fetched.
} }
} }

View File

@ -10,7 +10,6 @@ import android.preference.PreferenceManager
import android.support.v7.app.AppCompatDelegate import android.support.v7.app.AppCompatDelegate
import android.view.View import android.view.View
class IntroActivity : MaterialIntroActivity() { class IntroActivity : MaterialIntroActivity() {
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
@ -18,33 +17,44 @@ class IntroActivity : MaterialIntroActivity() {
AppCompatDelegate.setCompatVectorFromResourcesEnabled(true) AppCompatDelegate.setCompatVectorFromResourcesEnabled(true)
addSlide(SlideFragmentBuilder() addSlide(
.backgroundColor(R.color.colorPrimary) SlideFragmentBuilder()
.buttonsColor(R.color.colorAccent) .backgroundColor(R.color.colorPrimary)
.image(R.drawable.web_hi_res_512) .buttonsColor(R.color.colorAccent)
.title(getString(R.string.intro_hello_title)) .image(R.drawable.web_hi_res_512)
.description(getString(R.string.intro_hello_message)) .title(getString(R.string.intro_hello_title))
.build()) .description(getString(R.string.intro_hello_message))
.build()
)
addSlide(SlideFragmentBuilder() addSlide(
.backgroundColor(R.color.colorAccent) SlideFragmentBuilder()
.buttonsColor(R.color.colorPrimary) .backgroundColor(R.color.colorAccent)
.image(R.drawable.ic_info_outline_white_48px) .buttonsColor(R.color.colorPrimary)
.title(getString(R.string.intro_needs_selfoss_title)) .image(R.drawable.ic_info_outline_white_48px)
.description(getString(R.string.intro_needs_selfoss_message)) .title(getString(R.string.intro_needs_selfoss_title))
.build(), .description(getString(R.string.intro_needs_selfoss_message))
MessageButtonBehaviour(View.OnClickListener { .build(),
val browserIntent = Intent(Intent.ACTION_VIEW, Uri.parse("https://selfoss.aditu.de")) MessageButtonBehaviour(
startActivity(browserIntent) View.OnClickListener {
}, getString(R.string.intro_needs_selfoss_link))) val browserIntent = Intent(
Intent.ACTION_VIEW,
Uri.parse("https://selfoss.aditu.de")
)
startActivity(browserIntent)
}, getString(R.string.intro_needs_selfoss_link)
)
)
addSlide(SlideFragmentBuilder() addSlide(
.backgroundColor(R.color.colorPrimaryDark) SlideFragmentBuilder()
.buttonsColor(R.color.colorAccentDark) .backgroundColor(R.color.colorPrimaryDark)
.image(R.drawable.ic_thumb_up_white_48px) .buttonsColor(R.color.colorAccentDark)
.title(getString(R.string.intro_all_set_title)) .image(R.drawable.ic_thumb_up_white_48px)
.description(getString(R.string.intro_all_set_message)) .title(getString(R.string.intro_all_set_title))
.build()) .description(getString(R.string.intro_all_set_message))
.build()
)
} }
override fun onFinish() { override fun onFinish() {

View File

@ -8,7 +8,6 @@ import android.content.SharedPreferences
import android.os.Bundle import android.os.Bundle
import android.support.v7.app.AlertDialog import android.support.v7.app.AlertDialog
import android.support.v7.app.AppCompatActivity import android.support.v7.app.AppCompatActivity
import android.support.v7.widget.Toolbar
import android.text.TextUtils import android.text.TextUtils
import android.view.Menu import android.view.Menu
import android.view.MenuItem import android.view.MenuItem
@ -25,12 +24,10 @@ import com.ftinc.scoop.Scoop
import com.google.firebase.analytics.FirebaseAnalytics import com.google.firebase.analytics.FirebaseAnalytics
import com.mikepenz.aboutlibraries.Libs import com.mikepenz.aboutlibraries.Libs
import com.mikepenz.aboutlibraries.LibsBuilder import com.mikepenz.aboutlibraries.LibsBuilder
import kotlinx.android.synthetic.main.activity_login.*
import retrofit2.Call import retrofit2.Call
import retrofit2.Callback import retrofit2.Callback
import retrofit2.Response import retrofit2.Response
import kotlinx.android.synthetic.main.activity_login.*
class LoginActivity : AppCompatActivity() { class LoginActivity : AppCompatActivity() {
@ -45,8 +42,6 @@ class LoginActivity : AppCompatActivity() {
private lateinit var userIdentifier: String private lateinit var userIdentifier: String
private var logErrors: Boolean = false private var logErrors: Boolean = false
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
Scoop.getInstance().apply(this) Scoop.getInstance().apply(this)
@ -81,13 +76,15 @@ class LoginActivity : AppCompatActivity() {
warningText.visibility = visi warningText.visibility = visi
} }
passwordView.setOnEditorActionListener(TextView.OnEditorActionListener { _, id, _ -> passwordView.setOnEditorActionListener(
if (id == R.id.loginView || id == EditorInfo.IME_NULL) { TextView.OnEditorActionListener { _, id, _ ->
attemptLogin() if (id == R.id.loginView || id == EditorInfo.IME_NULL) {
return@OnEditorActionListener true attemptLogin()
} return@OnEditorActionListener true
false }
}) false
}
)
signInButton.setOnClickListener { attemptLogin() } signInButton.setOnClickListener { attemptLogin() }
@ -116,7 +113,8 @@ class LoginActivity : AppCompatActivity() {
alertDialog.setButton( alertDialog.setButton(
AlertDialog.BUTTON_NEUTRAL, AlertDialog.BUTTON_NEUTRAL,
"OK", "OK",
{ dialog, _ -> dialog.dismiss() }) { dialog, _ -> dialog.dismiss() }
)
alertDialog.show() alertDialog.show()
} }
} }
@ -158,7 +156,8 @@ class LoginActivity : AppCompatActivity() {
alertDialog.setButton( alertDialog.setButton(
AlertDialog.BUTTON_NEUTRAL, AlertDialog.BUTTON_NEUTRAL,
"OK", "OK",
{ dialog, _ -> dialog.dismiss() }) { dialog, _ -> dialog.dismiss() }
)
alertDialog.show() alertDialog.show()
inValidCount = 0 inValidCount = 0
} }
@ -191,7 +190,12 @@ class LoginActivity : AppCompatActivity() {
editor.putBoolean("isSelfSignedCert", isWithSelfSignedCert) editor.putBoolean("isSelfSignedCert", isWithSelfSignedCert)
editor.apply() editor.apply()
val api = SelfossApi(this, this@LoginActivity, isWithSelfSignedCert, 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")
@ -209,12 +213,19 @@ class LoginActivity : AppCompatActivity() {
Crashlytics.setUserIdentifier(userIdentifier) Crashlytics.setUserIdentifier(userIdentifier)
Crashlytics.log(100, "LOGIN_DEBUG_ERRROR", t.message) Crashlytics.log(100, "LOGIN_DEBUG_ERRROR", t.message)
Crashlytics.logException(t) Crashlytics.logException(t)
Toast.makeText(this@LoginActivity, t.message, Toast.LENGTH_LONG).show() Toast.makeText(
this@LoginActivity,
t.message,
Toast.LENGTH_LONG
).show()
} }
showProgress(false) showProgress(false)
} }
override fun onResponse(call: Call<SuccessResponse>, response: Response<SuccessResponse>) { override fun onResponse(
call: Call<SuccessResponse>,
response: Response<SuccessResponse>
) {
if (response.body() != null && response.body()!!.isSuccess) { if (response.body() != null && response.body()!!.isSuccess) {
firebaseAnalytics.logEvent(FirebaseAnalytics.Event.LOGIN, Bundle()) firebaseAnalytics.logEvent(FirebaseAnalytics.Event.LOGIN, Bundle())
goToMain() goToMain()
@ -230,7 +241,6 @@ class LoginActivity : AppCompatActivity() {
} }
} }
private fun showProgress(show: Boolean) { private fun showProgress(show: Boolean) {
val shortAnimTime = resources.getInteger(android.R.integer.config_shortAnimTime) val shortAnimTime = resources.getInteger(android.R.integer.config_shortAnimTime)
@ -241,10 +251,11 @@ class LoginActivity : AppCompatActivity() {
.alpha( .alpha(
if (show) 0F else 1F if (show) 0F else 1F
).setListener(object : AnimatorListenerAdapter() { ).setListener(object : AnimatorListenerAdapter() {
override fun onAnimationEnd(animation: Animator) { override fun onAnimationEnd(animation: Animator) {
loginForm.visibility = if (show) View.GONE else View.VISIBLE loginForm.visibility = if (show) View.GONE else View.VISIBLE
} }
}) }
)
loginProgress.visibility = if (show) View.VISIBLE else View.GONE loginProgress.visibility = if (show) View.VISIBLE else View.GONE
loginProgress loginProgress
@ -253,10 +264,11 @@ class LoginActivity : AppCompatActivity() {
.alpha( .alpha(
if (show) 1F else 0F if (show) 1F else 0F
).setListener(object : AnimatorListenerAdapter() { ).setListener(object : AnimatorListenerAdapter() {
override fun onAnimationEnd(animation: Animator) { override fun onAnimationEnd(animation: Animator) {
loginProgress.visibility = if (show) View.VISIBLE else View.GONE loginProgress.visibility = if (show) View.VISIBLE else View.GONE
} }
}) }
)
} }
override fun onCreateOptionsMenu(menu: Menu): Boolean { override fun onCreateOptionsMenu(menu: Menu): Boolean {

View File

@ -5,15 +5,16 @@ import android.os.Bundle
import android.preference.PreferenceManager import android.preference.PreferenceManager
import android.support.v7.app.AppCompatActivity import android.support.v7.app.AppCompatActivity
class MainActivity : AppCompatActivity() { class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main) setContentView(R.layout.activity_main)
if (PreferenceManager.getDefaultSharedPreferences(baseContext).getBoolean("firstStart", true)) { if (PreferenceManager.getDefaultSharedPreferences(baseContext).getBoolean(
"firstStart",
true
)) {
val i = Intent(this@MainActivity, IntroActivity::class.java) val i = Intent(this@MainActivity, IntroActivity::class.java)
startActivity(i) startActivity(i)
} else { } else {
@ -22,6 +23,5 @@ class MainActivity : AppCompatActivity() {
} }
finish() finish()
} }
} }

View File

@ -21,9 +21,6 @@ import io.fabric.sdk.android.Fabric
import java.io.IOException import java.io.IOException
import java.util.UUID.randomUUID import java.util.UUID.randomUUID
class MyApp : MultiDexApplication() { class MyApp : MultiDexApplication() {
override fun onCreate() { override fun onCreate() {
@ -46,14 +43,13 @@ class MyApp : MultiDexApplication() {
initTheme() initTheme()
tryToHandleBug() tryToHandleBug()
} }
private fun initAmplify() { private fun initAmplify() {
Amplify.initSharedInstance(this) Amplify.initSharedInstance(this)
.setPositiveFeedbackCollectors(GooglePlayStoreFeedbackCollector()) .setPositiveFeedbackCollectors(GooglePlayStoreFeedbackCollector())
.setCriticalFeedbackCollectors(DefaultEmailFeedbackCollector(BuildConfig.FEEDBACK_EMAIL)) .setCriticalFeedbackCollectors(DefaultEmailFeedbackCollector(BuildConfig.FEEDBACK_EMAIL))
.applyAllDefaultRules() .applyAllDefaultRules()
} }
private fun initCache() { private fun initCache() {
@ -66,12 +62,16 @@ class MyApp : MultiDexApplication() {
private fun initDrawerImageLoader() { private fun initDrawerImageLoader() {
DrawerImageLoader.init(object : AbstractDrawerImageLoader() { DrawerImageLoader.init(object : AbstractDrawerImageLoader() {
override fun set(imageView: ImageView?, uri: Uri?, placeholder: Drawable?, tag: String?) { override fun set(
imageView: ImageView?,
uri: Uri?,
placeholder: Drawable?,
tag: String?
) {
Glide.with(imageView?.context) Glide.with(imageView?.context)
.load(uri) .load(uri)
.apply(RequestOptions.fitCenterTransform() .apply(RequestOptions.fitCenterTransform().placeholder(placeholder))
.placeholder(placeholder)) .into(imageView)
.into(imageView)
} }
override fun cancel(imageView: ImageView?) { override fun cancel(imageView: ImageView?) {
@ -86,33 +86,35 @@ class MyApp : MultiDexApplication() {
private fun initTheme() { private fun initTheme() {
Scoop.waffleCone() Scoop.waffleCone()
.addFlavor(getString(R.string.default_theme), R.style.NoBar, true) .addFlavor(getString(R.string.default_theme), R.style.NoBar, true)
.addFlavor(getString(R.string.default_dark_theme), R.style.NoBarDark) .addFlavor(getString(R.string.default_dark_theme), R.style.NoBarDark)
.addFlavor(getString(R.string.teal_orange_theme), R.style.NoBarTealOrange) .addFlavor(getString(R.string.teal_orange_theme), R.style.NoBarTealOrange)
.addFlavor(getString(R.string.teal_orange_dark_theme), R.style.NoBarTealOrangeDark) .addFlavor(getString(R.string.teal_orange_dark_theme), R.style.NoBarTealOrangeDark)
.addFlavor(getString(R.string.cyan_pink_theme), R.style.NoBarCyanPink) .addFlavor(getString(R.string.cyan_pink_theme), R.style.NoBarCyanPink)
.addFlavor(getString(R.string.cyan_pink_dark_theme), R.style.NoBarCyanPinkDark) .addFlavor(getString(R.string.cyan_pink_dark_theme), R.style.NoBarCyanPinkDark)
.addFlavor(getString(R.string.grey_orange_theme), R.style.NoBarGreyOrange) .addFlavor(getString(R.string.grey_orange_theme), R.style.NoBarGreyOrange)
.addFlavor(getString(R.string.grey_orange_dark_theme), R.style.NoBarGreyOrangeDark) .addFlavor(getString(R.string.grey_orange_dark_theme), R.style.NoBarGreyOrangeDark)
.addFlavor(getString(R.string.blue_amber_theme), R.style.NoBarBlueAmber) .addFlavor(getString(R.string.blue_amber_theme), R.style.NoBarBlueAmber)
.addFlavor(getString(R.string.blue_amber_dark_theme), R.style.NoBarBlueAmberDark) .addFlavor(getString(R.string.blue_amber_dark_theme), R.style.NoBarBlueAmberDark)
.addFlavor(getString(R.string.indigo_pink_theme), R.style.NoBarIndigoPink) .addFlavor(getString(R.string.indigo_pink_theme), R.style.NoBarIndigoPink)
.addFlavor(getString(R.string.indigo_pink_dark_theme), R.style.NoBarIndigoPinkDark) .addFlavor(getString(R.string.indigo_pink_dark_theme), R.style.NoBarIndigoPinkDark)
.addFlavor(getString(R.string.red_teal_theme), R.style.NoBarRedTeal) .addFlavor(getString(R.string.red_teal_theme), R.style.NoBarRedTeal)
.addFlavor(getString(R.string.red_teal_dark_theme), R.style.NoBarRedTealDark) .addFlavor(getString(R.string.red_teal_dark_theme), R.style.NoBarRedTealDark)
.setSharedPreferences(PreferenceManager.getDefaultSharedPreferences(this)) .setSharedPreferences(PreferenceManager.getDefaultSharedPreferences(this))
.initialize() .initialize()
} }
private fun tryToHandleBug() { private fun tryToHandleBug() {
val oldHandler = Thread.getDefaultUncaughtExceptionHandler() val oldHandler = Thread.getDefaultUncaughtExceptionHandler()
Thread.setDefaultUncaughtExceptionHandler { thread, e -> Thread.setDefaultUncaughtExceptionHandler { thread, e ->
if (e is java.lang.NoClassDefFoundError && e.stackTrace.asList().any { it.toString().contains("android.view.ViewDebug") }) if (e is java.lang.NoClassDefFoundError && e.stackTrace.asList().any {
it.toString().contains("android.view.ViewDebug")
}) {
Unit Unit
else } else {
oldHandler.uncaughtException(thread, e) oldHandler.uncaughtException(thread, e)
}
} }
} }
} }

View File

@ -23,12 +23,11 @@ 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.rubensousa.floatingtoolbar.FloatingToolbar import com.github.rubensousa.floatingtoolbar.FloatingToolbar
import kotlinx.android.synthetic.main.activity_reader.*
import org.sufficientlysecure.htmltextview.HtmlHttpImageGetter import org.sufficientlysecure.htmltextview.HtmlHttpImageGetter
import retrofit2.Call import retrofit2.Call
import retrofit2.Callback import retrofit2.Callback
import retrofit2.Response import retrofit2.Response
import kotlinx.android.synthetic.main.activity_reader.*
class ReaderActivity : AppCompatActivity() { class ReaderActivity : AppCompatActivity() {
private lateinit var mCustomTabActivityHelper: CustomTabActivityHelper private lateinit var mCustomTabActivityHelper: CustomTabActivityHelper
@ -40,7 +39,6 @@ class ReaderActivity : AppCompatActivity() {
private lateinit var contentTitle: String private lateinit var contentTitle: String
private lateinit var fab: FloatingActionButton private lateinit var fab: FloatingActionButton
override fun onStop() { override fun onStop() {
super.onStop() super.onStop()
mCustomTabActivityHelper.unbindCustomTabsService(this) mCustomTabActivityHelper.unbindCustomTabsService(this)
@ -81,7 +79,8 @@ class ReaderActivity : AppCompatActivity() {
customTabsIntent, customTabsIntent,
false, false,
false, false,
this@ReaderActivity) this@ReaderActivity
)
else -> Unit else -> Unit
} }
} }
@ -111,23 +110,34 @@ class ReaderActivity : AppCompatActivity() {
} }
} }
nestedScrollView.setOnScrollChangeListener(NestedScrollView.OnScrollChangeListener { _, _, scrollY, _, oldScrollY -> nestedScrollView.setOnScrollChangeListener(
if (scrollY > oldScrollY) { NestedScrollView.OnScrollChangeListener { _, _, scrollY, _, oldScrollY ->
fab.hide() if (scrollY > oldScrollY) {
} else { fab.hide()
if (mFloatingToolbar.isShowing) mFloatingToolbar.hide() else fab.show() } else {
} if (mFloatingToolbar.isShowing) mFloatingToolbar.hide() else fab.show()
}) }
}
)
content.movementMethod = LinkMovementMethod.getInstance() content.movementMethod = LinkMovementMethod.getInstance()
} }
private fun getContentFromMercury(customTabsIntent: CustomTabsIntent, prefs: SharedPreferences) { private fun getContentFromMercury(
customTabsIntent: CustomTabsIntent,
prefs: SharedPreferences
) {
progressBar.visibility = View.VISIBLE progressBar.visibility = View.VISIBLE
val parser = MercuryApi(BuildConfig.MERCURY_KEY, prefs.getBoolean("should_log_everything", false)) val parser = MercuryApi(
BuildConfig.MERCURY_KEY,
prefs.getBoolean("should_log_everything", false)
)
parser.parseUrl(url).enqueue(object : Callback<ParsedContent> { parser.parseUrl(url).enqueue(object : Callback<ParsedContent> {
override fun onResponse(call: Call<ParsedContent>, response: Response<ParsedContent>) { override fun onResponse(
call: Call<ParsedContent>,
response: Response<ParsedContent>
) {
if (response.body() != null && response.body()!!.content != null && response.body()!!.content.isNotEmpty()) { if (response.body() != null && response.body()!!.content != null && response.body()!!.content.isNotEmpty()) {
source.text = response.body()!!.domain source.text = response.body()!!.domain
titleView.text = response.body()!!.title titleView.text = response.body()!!.title
@ -152,14 +162,23 @@ class ReaderActivity : AppCompatActivity() {
nestedScrollView.scrollTo(0, 0) nestedScrollView.scrollTo(0, 0)
progressBar.visibility = View.GONE progressBar.visibility = View.GONE
} else openInBrowserAfterFailing(customTabsIntent) } else {
openInBrowserAfterFailing(customTabsIntent)
}
} }
override fun onFailure(call: Call<ParsedContent>, t: Throwable) = openInBrowserAfterFailing(customTabsIntent) override fun onFailure(
call: Call<ParsedContent>,
t: Throwable
) = openInBrowserAfterFailing(customTabsIntent)
}) })
} }
private fun tryToHandleHtml(c: String, customTabsIntent: CustomTabsIntent, prefs: SharedPreferences) { private fun tryToHandleHtml(
c: String,
customTabsIntent: CustomTabsIntent,
prefs: SharedPreferences
) {
try { try {
content.text = Html.fromHtml(c, HtmlHttpImageGetter(content, null, true), null) content.text = Html.fromHtml(c, HtmlHttpImageGetter(content, null, true), null)

View File

@ -10,14 +10,13 @@ import apps.amine.bou.readerforselfoss.adapters.SourcesListAdapter
import apps.amine.bou.readerforselfoss.api.selfoss.SelfossApi import apps.amine.bou.readerforselfoss.api.selfoss.SelfossApi
import apps.amine.bou.readerforselfoss.api.selfoss.Sources import apps.amine.bou.readerforselfoss.api.selfoss.Sources
import com.ftinc.scoop.Scoop import com.ftinc.scoop.Scoop
import kotlinx.android.synthetic.main.activity_sources.*
import retrofit2.Call import retrofit2.Call
import retrofit2.Callback import retrofit2.Callback
import retrofit2.Response import retrofit2.Response
import kotlinx.android.synthetic.main.activity_sources.*
class SourcesActivity : AppCompatActivity() { class SourcesActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
Scoop.getInstance().apply(this) Scoop.getInstance().apply(this)
@ -39,25 +38,43 @@ class SourcesActivity : AppCompatActivity() {
val prefs = PreferenceManager.getDefaultSharedPreferences(this) val prefs = PreferenceManager.getDefaultSharedPreferences(this)
val api = SelfossApi(this, this@SourcesActivity, prefs.getBoolean("isSelfSignedCert", false), prefs.getBoolean("should_log_everything", 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()
recyclerView.setHasFixedSize(true) recyclerView.setHasFixedSize(true)
recyclerView.layoutManager = mLayoutManager recyclerView.layoutManager = mLayoutManager
api.sources.enqueue(object : Callback<List<Sources>> { api.sources.enqueue(object : Callback<List<Sources>> {
override fun onResponse(call: Call<List<Sources>>, response: Response<List<Sources>>) { override fun onResponse(
call: Call<List<Sources>>,
response: Response<List<Sources>>
) {
if (response.body() != null && response.body()!!.isNotEmpty()) { if (response.body() != null && response.body()!!.isNotEmpty()) {
items = response.body() as ArrayList<Sources> items = response.body() as ArrayList<Sources>
} }
val mAdapter = SourcesListAdapter(this@SourcesActivity, items, api) val mAdapter = SourcesListAdapter(this@SourcesActivity, items, api)
recyclerView.adapter = mAdapter recyclerView.adapter = mAdapter
mAdapter.notifyDataSetChanged() mAdapter.notifyDataSetChanged()
if (items.isEmpty()) Toast.makeText(this@SourcesActivity, R.string.nothing_here, Toast.LENGTH_SHORT).show() if (items.isEmpty()) {
Toast.makeText(
this@SourcesActivity,
R.string.nothing_here,
Toast.LENGTH_SHORT
).show()
}
} }
override fun onFailure(call: Call<List<Sources>>, t: Throwable) { override fun onFailure(call: Call<List<Sources>>, t: Throwable) {
Toast.makeText(this@SourcesActivity, R.string.cant_get_sources, Toast.LENGTH_SHORT).show() Toast.makeText(
this@SourcesActivity,
R.string.cant_get_sources,
Toast.LENGTH_SHORT
).show()
} }
}) })

View File

@ -33,21 +33,23 @@ import com.bumptech.glide.Glide
import com.crashlytics.android.Crashlytics import com.crashlytics.android.Crashlytics
import com.like.LikeButton import com.like.LikeButton
import com.like.OnLikeListener import com.like.OnLikeListener
import kotlinx.android.synthetic.main.card_item.view.*
import retrofit2.Call import retrofit2.Call
import retrofit2.Callback import retrofit2.Callback
import retrofit2.Response import retrofit2.Response
import kotlinx.android.synthetic.main.card_item.view.*
class ItemCardAdapter(private val app: Activity, class ItemCardAdapter(
private val items: ArrayList<Item>, private val app: Activity,
private val api: SelfossApi, private val items: ArrayList<Item>,
private val helper: CustomTabActivityHelper, private val api: SelfossApi,
private val internalBrowser: Boolean, private val helper: CustomTabActivityHelper,
private val articleViewer: Boolean, private val internalBrowser: Boolean,
private val fullHeightCards: Boolean, private val articleViewer: Boolean,
private val appColors: AppColors, private val fullHeightCards: Boolean,
val debugReadingItems: Boolean, private val appColors: AppColors,
val userIdentifier: String) : RecyclerView.Adapter<ItemCardAdapter.ViewHolder>() { val debugReadingItems: Boolean,
val userIdentifier: String
) : RecyclerView.Adapter<ItemCardAdapter.ViewHolder>() {
private val c: Context = app.baseContext private val c: Context = app.baseContext
private val generator: ColorGenerator = ColorGenerator.MATERIAL private val generator: ColorGenerator = ColorGenerator.MATERIAL
@ -94,13 +96,21 @@ class ItemCardAdapter(private val app: Activity,
private fun doUnmark(i: Item, position: Int) { private fun doUnmark(i: Item, position: Int) {
val s = Snackbar val s = Snackbar
.make(app.findViewById(R.id.coordLayout), R.string.marked_as_read, Snackbar.LENGTH_LONG) .make(
app.findViewById(R.id.coordLayout),
R.string.marked_as_read,
Snackbar.LENGTH_LONG
)
.setAction(R.string.undo_string) { .setAction(R.string.undo_string) {
items.add(position, i) items.add(position, i)
notifyItemInserted(position) notifyItemInserted(position)
api.unmarkItem(i.id).enqueue(object : Callback<SuccessResponse> { api.unmarkItem(i.id).enqueue(object : Callback<SuccessResponse> {
override fun onResponse(call: Call<SuccessResponse>, response: Response<SuccessResponse>) {} override fun onResponse(
call: Call<SuccessResponse>,
response: Response<SuccessResponse>
) {
}
override fun onFailure(call: Call<SuccessResponse>, t: Throwable) { override fun onFailure(call: Call<SuccessResponse>, t: Throwable) {
items.remove(i) items.remove(i)
@ -124,7 +134,10 @@ class ItemCardAdapter(private val app: Activity,
notifyItemRemoved(position) notifyItemRemoved(position)
api.markItem(i.id).enqueue(object : Callback<SuccessResponse> { api.markItem(i.id).enqueue(object : Callback<SuccessResponse> {
override fun onResponse(call: Call<SuccessResponse>, response: Response<SuccessResponse>) { override fun onResponse(
call: Call<SuccessResponse>,
response: Response<SuccessResponse>
) {
if (!response.succeeded() && debugReadingItems) { if (!response.succeeded() && debugReadingItems) {
val message = val message =
"message: ${response.message()} " + "message: ${response.message()} " +
@ -150,12 +163,15 @@ class ItemCardAdapter(private val app: Activity,
Crashlytics.logException(t) Crashlytics.logException(t)
Toast.makeText(c, t.message, Toast.LENGTH_LONG).show() Toast.makeText(c, t.message, Toast.LENGTH_LONG).show()
} }
Toast.makeText(app, app.getString(R.string.cant_mark_read), Toast.LENGTH_SHORT).show() Toast.makeText(
app,
app.getString(R.string.cant_mark_read),
Toast.LENGTH_SHORT
).show()
items.add(i) items.add(i)
notifyItemInserted(position) notifyItemInserted(position)
} }
}) })
} }
inner class ViewHolder(val mView: CardView) : RecyclerView.ViewHolder(mView) { inner class ViewHolder(val mView: CardView) : RecyclerView.ViewHolder(mView) {
@ -176,11 +192,22 @@ class ItemCardAdapter(private val app: Activity,
override fun liked(likeButton: LikeButton) { override fun liked(likeButton: LikeButton) {
val (id) = items[adapterPosition] val (id) = items[adapterPosition]
api.starrItem(id).enqueue(object : Callback<SuccessResponse> { api.starrItem(id).enqueue(object : Callback<SuccessResponse> {
override fun onResponse(call: Call<SuccessResponse>, response: Response<SuccessResponse>) {} override fun onResponse(
call: Call<SuccessResponse>,
response: Response<SuccessResponse>
) {
}
override fun onFailure(call: Call<SuccessResponse>, t: Throwable) { override fun onFailure(
call: Call<SuccessResponse>,
t: Throwable
) {
mView.favButton.isLiked = false mView.favButton.isLiked = false
Toast.makeText(c, R.string.cant_mark_favortie, Toast.LENGTH_SHORT).show() Toast.makeText(
c,
R.string.cant_mark_favortie,
Toast.LENGTH_SHORT
).show()
} }
}) })
} }
@ -188,11 +215,22 @@ class ItemCardAdapter(private val app: Activity,
override fun unLiked(likeButton: LikeButton) { override fun unLiked(likeButton: LikeButton) {
val (id) = items[adapterPosition] val (id) = items[adapterPosition]
api.unstarrItem(id).enqueue(object : Callback<SuccessResponse> { api.unstarrItem(id).enqueue(object : Callback<SuccessResponse> {
override fun onResponse(call: Call<SuccessResponse>, response: Response<SuccessResponse>) {} override fun onResponse(
call: Call<SuccessResponse>,
response: Response<SuccessResponse>
) {
}
override fun onFailure(call: Call<SuccessResponse>, t: Throwable) { override fun onFailure(
call: Call<SuccessResponse>,
t: Throwable
) {
mView.favButton.isLiked = true mView.favButton.isLiked = true
Toast.makeText(c, R.string.cant_unmark_favortie, Toast.LENGTH_SHORT).show() Toast.makeText(
c,
R.string.cant_unmark_favortie,
Toast.LENGTH_SHORT
).show()
} }
}) })
} }
@ -212,7 +250,8 @@ class ItemCardAdapter(private val app: Activity,
helper.bindCustomTabsService(app) helper.bindCustomTabsService(app)
mView.setOnClickListener { mView.setOnClickListener {
c.openItemUrl(items[adapterPosition].getLinkDecoded(), c.openItemUrl(
items[adapterPosition].getLinkDecoded(),
items[adapterPosition].content, items[adapterPosition].content,
items[adapterPosition].getThumbnail(c), items[adapterPosition].getThumbnail(c),
items[adapterPosition].title, items[adapterPosition].title,
@ -220,7 +259,8 @@ class ItemCardAdapter(private val app: Activity,
customTabsIntent, customTabsIntent,
internalBrowser, internalBrowser,
articleViewer, articleViewer,
app) app
)
} }
} }
} }

View File

@ -1,6 +1,5 @@
package apps.amine.bou.readerforselfoss.adapters package apps.amine.bou.readerforselfoss.adapters
import android.app.Activity import android.app.Activity
import android.content.Context import android.content.Context
import android.graphics.Color import android.graphics.Color
@ -32,28 +31,34 @@ import com.amulyakhare.textdrawable.util.ColorGenerator
import com.crashlytics.android.Crashlytics import com.crashlytics.android.Crashlytics
import com.like.LikeButton import com.like.LikeButton
import com.like.OnLikeListener import com.like.OnLikeListener
import kotlinx.android.synthetic.main.list_item.view.*
import retrofit2.Call import retrofit2.Call
import retrofit2.Callback import retrofit2.Callback
import retrofit2.Response import retrofit2.Response
import java.util.* import java.util.*
import kotlin.collections.ArrayList import kotlin.collections.ArrayList
import kotlinx.android.synthetic.main.list_item.view.*
class ItemListAdapter(private val app: Activity, class ItemListAdapter(
private val items: ArrayList<Item>, private val app: Activity,
private val api: SelfossApi, private val items: ArrayList<Item>,
private val helper: CustomTabActivityHelper, private val api: SelfossApi,
private val clickBehavior: Boolean, private val helper: CustomTabActivityHelper,
private val internalBrowser: Boolean, private val clickBehavior: Boolean,
private val articleViewer: Boolean, private val internalBrowser: Boolean,
val debugReadingItems: Boolean, private val articleViewer: Boolean,
val userIdentifier: String) : RecyclerView.Adapter<ItemListAdapter.ViewHolder>() { val debugReadingItems: Boolean,
val userIdentifier: String
) : RecyclerView.Adapter<ItemListAdapter.ViewHolder>() {
private val generator: ColorGenerator = ColorGenerator.MATERIAL private val generator: ColorGenerator = ColorGenerator.MATERIAL
private val c: Context = app.baseContext private val c: Context = app.baseContext
private val bars: ArrayList<Boolean> = ArrayList(Collections.nCopies(items.size + 1, false)) private val bars: ArrayList<Boolean> = ArrayList(Collections.nCopies(items.size + 1, false))
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder { override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
val v = LayoutInflater.from(c).inflate(R.layout.list_item, parent, false) as ConstraintLayout val v = LayoutInflater.from(c).inflate(
R.layout.list_item,
parent,
false
) as ConstraintLayout
return ViewHolder(v) return ViewHolder(v)
} }
@ -70,12 +75,14 @@ class ItemListAdapter(private val app: Activity,
val sizeInInt = 46 val sizeInInt = 46
val sizeInDp = TypedValue.applyDimension( val sizeInDp = TypedValue.applyDimension(
TypedValue.COMPLEX_UNIT_DIP, sizeInInt.toFloat(), c.resources TypedValue.COMPLEX_UNIT_DIP, sizeInInt.toFloat(), c.resources
.displayMetrics).toInt() .displayMetrics
).toInt()
val marginInInt = 16 val marginInInt = 16
val marginInDp = TypedValue.applyDimension( val marginInDp = TypedValue.applyDimension(
TypedValue.COMPLEX_UNIT_DIP, marginInInt.toFloat(), c.resources TypedValue.COMPLEX_UNIT_DIP, marginInInt.toFloat(), c.resources
.displayMetrics).toInt() .displayMetrics
).toInt()
val params = holder.mView.itemImage.layoutParams as ViewGroup.MarginLayoutParams val params = holder.mView.itemImage.layoutParams as ViewGroup.MarginLayoutParams
params.height = sizeInDp params.height = sizeInDp
@ -101,23 +108,34 @@ class ItemListAdapter(private val app: Activity,
c.bitmapCenterCrop(itm.getThumbnail(c), holder.mView.itemImage) c.bitmapCenterCrop(itm.getThumbnail(c), holder.mView.itemImage)
} }
if (bars[position]) holder.mView.actionBar.visibility = View.VISIBLE else holder.mView.actionBar.visibility = View.GONE if (bars[position]) {
holder.mView.actionBar.visibility = View.VISIBLE
} else {
holder.mView.actionBar.visibility = View.GONE
}
holder.mView.favButton.isLiked = itm.starred holder.mView.favButton.isLiked = itm.starred
} }
override fun getItemCount(): Int = items.size override fun getItemCount(): Int = items.size
private fun doUnmark(i: Item, position: Int) { private fun doUnmark(i: Item, position: Int) {
val s = Snackbar val s = Snackbar
.make(app.findViewById(R.id.coordLayout), R.string.marked_as_read, Snackbar.LENGTH_LONG) .make(
app.findViewById(R.id.coordLayout),
R.string.marked_as_read,
Snackbar.LENGTH_LONG
)
.setAction(R.string.undo_string) { .setAction(R.string.undo_string) {
items.add(position, i) items.add(position, i)
notifyItemInserted(position) notifyItemInserted(position)
api.unmarkItem(i.id).enqueue(object : Callback<SuccessResponse> { api.unmarkItem(i.id).enqueue(object : Callback<SuccessResponse> {
override fun onResponse(call: Call<SuccessResponse>, response: Response<SuccessResponse>) {} override fun onResponse(
call: Call<SuccessResponse>,
response: Response<SuccessResponse>
) {
}
override fun onFailure(call: Call<SuccessResponse>, t: Throwable) { override fun onFailure(call: Call<SuccessResponse>, t: Throwable) {
items.remove(i) items.remove(i)
@ -141,7 +159,10 @@ class ItemListAdapter(private val app: Activity,
notifyItemRemoved(position) notifyItemRemoved(position)
api.markItem(i.id).enqueue(object : Callback<SuccessResponse> { api.markItem(i.id).enqueue(object : Callback<SuccessResponse> {
override fun onResponse(call: Call<SuccessResponse>, response: Response<SuccessResponse>) { override fun onResponse(
call: Call<SuccessResponse>,
response: Response<SuccessResponse>
) {
if (!response.succeeded() && debugReadingItems) { if (!response.succeeded() && debugReadingItems) {
val message = val message =
"message: ${response.message()} " + "message: ${response.message()} " +
@ -157,7 +178,6 @@ class ItemListAdapter(private val app: Activity,
Toast.makeText(c, message, Toast.LENGTH_LONG).show() Toast.makeText(c, message, Toast.LENGTH_LONG).show()
} }
doUnmark(i, position) doUnmark(i, position)
} }
override fun onFailure(call: Call<SuccessResponse>, t: Throwable) { override fun onFailure(call: Call<SuccessResponse>, t: Throwable) {
@ -167,12 +187,15 @@ class ItemListAdapter(private val app: Activity,
Crashlytics.logException(t) Crashlytics.logException(t)
Toast.makeText(c, t.message, Toast.LENGTH_LONG).show() Toast.makeText(c, t.message, Toast.LENGTH_LONG).show()
} }
Toast.makeText(app, app.getString(R.string.cant_mark_read), Toast.LENGTH_SHORT).show() Toast.makeText(
app,
app.getString(R.string.cant_mark_read),
Toast.LENGTH_SHORT
).show()
items.add(i) items.add(i)
notifyItemInserted(position) notifyItemInserted(position)
} }
}) })
} }
inner class ViewHolder(val mView: ConstraintLayout) : RecyclerView.ViewHolder(mView) { inner class ViewHolder(val mView: ConstraintLayout) : RecyclerView.ViewHolder(mView) {
@ -188,11 +211,22 @@ class ItemListAdapter(private val app: Activity,
override fun liked(likeButton: LikeButton) { override fun liked(likeButton: LikeButton) {
val (id) = items[adapterPosition] val (id) = items[adapterPosition]
api.starrItem(id).enqueue(object : Callback<SuccessResponse> { api.starrItem(id).enqueue(object : Callback<SuccessResponse> {
override fun onResponse(call: Call<SuccessResponse>, response: Response<SuccessResponse>) {} override fun onResponse(
call: Call<SuccessResponse>,
response: Response<SuccessResponse>
) {
}
override fun onFailure(call: Call<SuccessResponse>, t: Throwable) { override fun onFailure(
call: Call<SuccessResponse>,
t: Throwable
) {
mView.favButton.isLiked = false mView.favButton.isLiked = false
Toast.makeText(c, R.string.cant_mark_favortie, Toast.LENGTH_SHORT).show() Toast.makeText(
c,
R.string.cant_mark_favortie,
Toast.LENGTH_SHORT
).show()
} }
}) })
} }
@ -200,11 +234,22 @@ class ItemListAdapter(private val app: Activity,
override fun unLiked(likeButton: LikeButton) { override fun unLiked(likeButton: LikeButton) {
val (id) = items[adapterPosition] val (id) = items[adapterPosition]
api.unstarrItem(id).enqueue(object : Callback<SuccessResponse> { api.unstarrItem(id).enqueue(object : Callback<SuccessResponse> {
override fun onResponse(call: Call<SuccessResponse>, response: Response<SuccessResponse>) {} override fun onResponse(
call: Call<SuccessResponse>,
response: Response<SuccessResponse>
) {
}
override fun onFailure(call: Call<SuccessResponse>, t: Throwable) { override fun onFailure(
call: Call<SuccessResponse>,
t: Throwable
) {
mView.favButton.isLiked = true mView.favButton.isLiked = true
Toast.makeText(c, R.string.cant_unmark_favortie, Toast.LENGTH_SHORT).show() Toast.makeText(
c,
R.string.cant_unmark_favortie,
Toast.LENGTH_SHORT
).show()
} }
}) })
} }
@ -220,7 +265,6 @@ class ItemListAdapter(private val app: Activity,
} }
} }
private fun handleCustomTabActions() { private fun handleCustomTabActions() {
val customTabsIntent = c.buildCustomTabsIntent() val customTabsIntent = c.buildCustomTabsIntent()
helper.bindCustomTabsService(app) helper.bindCustomTabsService(app)
@ -228,7 +272,8 @@ class ItemListAdapter(private val app: Activity,
if (!clickBehavior) { if (!clickBehavior) {
mView.setOnClickListener { mView.setOnClickListener {
c.openItemUrl(items[adapterPosition].getLinkDecoded(), c.openItemUrl(
items[adapterPosition].getLinkDecoded(),
items[adapterPosition].content, items[adapterPosition].content,
items[adapterPosition].getThumbnail(c), items[adapterPosition].getThumbnail(c),
items[adapterPosition].title, items[adapterPosition].title,
@ -236,7 +281,8 @@ class ItemListAdapter(private val app: Activity,
customTabsIntent, customTabsIntent,
internalBrowser, internalBrowser,
articleViewer, articleViewer,
app) app
)
} }
mView.setOnLongClickListener { mView.setOnLongClickListener {
actionBarShowHide() actionBarShowHide()
@ -245,7 +291,8 @@ class ItemListAdapter(private val app: Activity,
} else { } else {
mView.setOnClickListener { actionBarShowHide() } mView.setOnClickListener { actionBarShowHide() }
mView.setOnLongClickListener { mView.setOnLongClickListener {
c.openItemUrl(items[adapterPosition].getLinkDecoded(), c.openItemUrl(
items[adapterPosition].getLinkDecoded(),
items[adapterPosition].content, items[adapterPosition].content,
items[adapterPosition].getThumbnail(c), items[adapterPosition].getThumbnail(c),
items[adapterPosition].title, items[adapterPosition].title,
@ -253,7 +300,8 @@ class ItemListAdapter(private val app: Activity,
customTabsIntent, customTabsIntent,
internalBrowser, internalBrowser,
articleViewer, articleViewer,
app) app
)
true true
} }
} }

View File

@ -7,8 +7,6 @@ import android.support.v7.widget.RecyclerView
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.ViewGroup import android.view.ViewGroup
import android.widget.Button import android.widget.Button
import android.widget.ImageView
import android.widget.TextView
import android.widget.Toast import android.widget.Toast
import apps.amine.bou.readerforselfoss.R import apps.amine.bou.readerforselfoss.R
import apps.amine.bou.readerforselfoss.api.selfoss.SelfossApi import apps.amine.bou.readerforselfoss.api.selfoss.SelfossApi
@ -18,20 +16,25 @@ import apps.amine.bou.readerforselfoss.utils.glide.circularBitmapDrawable
import apps.amine.bou.readerforselfoss.utils.toTextDrawableString import apps.amine.bou.readerforselfoss.utils.toTextDrawableString
import com.amulyakhare.textdrawable.TextDrawable import com.amulyakhare.textdrawable.TextDrawable
import com.amulyakhare.textdrawable.util.ColorGenerator import com.amulyakhare.textdrawable.util.ColorGenerator
import kotlinx.android.synthetic.main.source_list_item.view.*
import retrofit2.Call import retrofit2.Call
import retrofit2.Callback import retrofit2.Callback
import retrofit2.Response import retrofit2.Response
import kotlinx.android.synthetic.main.source_list_item.view.*
class SourcesListAdapter(
class SourcesListAdapter(private val app: Activity, private val app: Activity,
private val items: ArrayList<Sources>, private val items: ArrayList<Sources>,
private val api: SelfossApi) : RecyclerView.Adapter<SourcesListAdapter.ViewHolder>() { private val api: SelfossApi
) : RecyclerView.Adapter<SourcesListAdapter.ViewHolder>() {
private val c: Context = app.baseContext private val c: Context = app.baseContext
private val generator: ColorGenerator = ColorGenerator.MATERIAL private val generator: ColorGenerator = ColorGenerator.MATERIAL
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder { override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
val v = LayoutInflater.from(c).inflate(R.layout.source_list_item, parent, false) as ConstraintLayout val v = LayoutInflater.from(c).inflate(
R.layout.source_list_item,
parent,
false
) as ConstraintLayout
return ViewHolder(v) return ViewHolder(v)
} }
@ -69,23 +72,32 @@ class SourcesListAdapter(private val app: Activity,
deleteBtn.setOnClickListener { deleteBtn.setOnClickListener {
val (id) = items[adapterPosition] val (id) = items[adapterPosition]
api.deleteSource(id).enqueue(object : Callback<SuccessResponse> { api.deleteSource(id).enqueue(object : Callback<SuccessResponse> {
override fun onResponse(call: Call<SuccessResponse>, response: Response<SuccessResponse>) { override fun onResponse(
call: Call<SuccessResponse>,
response: Response<SuccessResponse>
) {
if (response.body() != null && response.body()!!.isSuccess) { if (response.body() != null && response.body()!!.isSuccess) {
items.removeAt(adapterPosition) items.removeAt(adapterPosition)
notifyItemRemoved(adapterPosition) notifyItemRemoved(adapterPosition)
notifyItemRangeChanged(adapterPosition, itemCount) notifyItemRangeChanged(adapterPosition, itemCount)
} else { } else {
Toast.makeText(app, R.string.can_delete_source, Toast.LENGTH_SHORT).show() Toast.makeText(
app,
R.string.can_delete_source,
Toast.LENGTH_SHORT
).show()
} }
} }
override fun onFailure(call: Call<SuccessResponse>, t: Throwable) { override fun onFailure(call: Call<SuccessResponse>, t: Throwable) {
Toast.makeText(app, R.string.can_delete_source, Toast.LENGTH_SHORT).show() Toast.makeText(
app,
R.string.can_delete_source,
Toast.LENGTH_SHORT
).show()
} }
}) })
} }
} }
} }
} }

View File

@ -7,30 +7,29 @@ import retrofit2.Call
import retrofit2.Retrofit import retrofit2.Retrofit
import retrofit2.converter.gson.GsonConverterFactory import retrofit2.converter.gson.GsonConverterFactory
class MercuryApi(private val key: String, shouldLog: Boolean) { class MercuryApi(private val key: String, shouldLog: Boolean) {
private val service: MercuryService private val service: MercuryService
init { init {
val interceptor = HttpLoggingInterceptor() val interceptor = HttpLoggingInterceptor()
interceptor.level = if (shouldLog) interceptor.level = if (shouldLog) {
HttpLoggingInterceptor.Level.BODY HttpLoggingInterceptor.Level.BODY
else } else {
HttpLoggingInterceptor.Level.NONE HttpLoggingInterceptor.Level.NONE
}
val client = OkHttpClient.Builder().addInterceptor(interceptor).build() val client = OkHttpClient.Builder().addInterceptor(interceptor).build()
val gson = GsonBuilder() val gson = GsonBuilder()
.setLenient() .setLenient()
.create() .create()
val retrofit = val retrofit =
Retrofit Retrofit
.Builder() .Builder()
.baseUrl("https://mercury.postlight.com") .baseUrl("https://mercury.postlight.com")
.client(client) .client(client)
.addConverterFactory(GsonConverterFactory.create(gson)) .addConverterFactory(GsonConverterFactory.create(gson))
.build() .build()
service = retrofit.create(MercuryService::class.java) service = retrofit.create(MercuryService::class.java)
} }

View File

@ -4,38 +4,40 @@ import android.os.Parcel
import android.os.Parcelable import android.os.Parcelable
import com.google.gson.annotations.SerializedName import com.google.gson.annotations.SerializedName
class ParsedContent(
class ParsedContent(@SerializedName("title") val title: String, @SerializedName("title") val title: String,
@SerializedName("content") val content: String, @SerializedName("content") val content: String,
@SerializedName("date_published") val date_published: String, @SerializedName("date_published") val date_published: String,
@SerializedName("lead_image_url") val lead_image_url: String, @SerializedName("lead_image_url") val lead_image_url: String,
@SerializedName("dek") val dek: String, @SerializedName("dek") val dek: String,
@SerializedName("url") val url: String, @SerializedName("url") val url: String,
@SerializedName("domain") val domain: String, @SerializedName("domain") val domain: String,
@SerializedName("excerpt") val excerpt: String, @SerializedName("excerpt") val excerpt: String,
@SerializedName("total_pages") val total_pages: Int, @SerializedName("total_pages") val total_pages: Int,
@SerializedName("rendered_pages") val rendered_pages: Int, @SerializedName("rendered_pages") val rendered_pages: Int,
@SerializedName("next_page_url") val next_page_url: String) : Parcelable { @SerializedName("next_page_url") 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> {
override fun createFromParcel(source: Parcel): ParsedContent = ParsedContent(source) override fun createFromParcel(source: Parcel): ParsedContent = ParsedContent(source)
override fun newArray(size: Int): Array<ParsedContent?> = arrayOfNulls(size) override fun newArray(size: Int): Array<ParsedContent?> = arrayOfNulls(size)
} }
} }
constructor(source: Parcel) : this( constructor(source: Parcel) : this(
title = source.readString(), title = source.readString(),
content = source.readString(), content = source.readString(),
date_published = source.readString(), date_published = source.readString(),
lead_image_url = source.readString(), lead_image_url = source.readString(),
dek = source.readString(), dek = source.readString(),
url = source.readString(), url = source.readString(),
domain = source.readString(), domain = source.readString(),
excerpt = source.readString(), excerpt = source.readString(),
total_pages = source.readInt(), total_pages = source.readInt(),
rendered_pages = source.readInt(), rendered_pages = source.readInt(),
next_page_url = source.readString() next_page_url = source.readString()
) )
override fun describeContents() = 0 override fun describeContents() = 0

View File

@ -1,13 +1,10 @@
package apps.amine.bou.readerforselfoss.api.mercury package apps.amine.bou.readerforselfoss.api.mercury
import retrofit2.Call import retrofit2.Call
import retrofit2.http.GET import retrofit2.http.GET
import retrofit2.http.Header import retrofit2.http.Header
import retrofit2.http.Query import retrofit2.http.Query
interface MercuryService { interface MercuryService {
@GET("parser") @GET("parser")
fun parseUrl(@Query("url") url: String, @Header("x-api-key") key: String): Call<ParsedContent> fun parseUrl(@Query("url") url: String, @Header("x-api-key") key: String): Call<ParsedContent>

View File

@ -6,14 +6,17 @@ import com.google.gson.JsonElement
import com.google.gson.JsonParseException import com.google.gson.JsonParseException
import java.lang.reflect.Type import java.lang.reflect.Type
internal class BooleanTypeAdapter : JsonDeserializer<Boolean> { internal class BooleanTypeAdapter : JsonDeserializer<Boolean> {
@Throws(JsonParseException::class) @Throws(JsonParseException::class)
override fun deserialize(json: JsonElement, typeOfT: Type, context: JsonDeserializationContext): Boolean? = override fun deserialize(
try { json: JsonElement,
json.asInt == 1 typeOfT: Type,
} catch (e: Exception) { context: JsonDeserializationContext
json.asBoolean ): Boolean? =
} try {
json.asInt == 1
} catch (e: Exception) {
json.asBoolean
}
} }

View File

@ -19,8 +19,12 @@ import retrofit2.Retrofit
import retrofit2.converter.gson.GsonConverterFactory import retrofit2.converter.gson.GsonConverterFactory
import java.util.concurrent.ConcurrentHashMap import java.util.concurrent.ConcurrentHashMap
class SelfossApi(
class SelfossApi(c: Context, callingActivity: Activity, isWithSelfSignedCert: Boolean, shouldLog: Boolean) { 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)
@ -28,50 +32,50 @@ class SelfossApi(c: Context, callingActivity: Activity, isWithSelfSignedCert: Bo
private val password: String private val password: String
fun OkHttpClient.Builder.maybeWithSelfSigned(isWithSelfSignedCert: Boolean): OkHttpClient.Builder = fun OkHttpClient.Builder.maybeWithSelfSigned(isWithSelfSignedCert: Boolean): OkHttpClient.Builder =
if (isWithSelfSignedCert) { if (isWithSelfSignedCert) {
getUnsafeHttpClient() getUnsafeHttpClient()
} else { } else {
this this
} }
fun Credentials.createAuthenticator(): DispatchingAuthenticator = fun Credentials.createAuthenticator(): DispatchingAuthenticator =
DispatchingAuthenticator.Builder() DispatchingAuthenticator.Builder()
.with("digest", DigestAuthenticator(this)) .with("digest", DigestAuthenticator(this))
.with("basic", BasicAuthenticator(this)) .with("basic", BasicAuthenticator(this))
.build() .build()
fun DispatchingAuthenticator.getHttpClien(isWithSelfSignedCert: Boolean): OkHttpClient.Builder { 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))
} }
init { init {
userName = config.userLogin userName = config.userLogin
password = config.userPassword password = config.userPassword
val authenticator = val authenticator =
Credentials( Credentials(
config.httpUserLogin, config.httpUserLogin,
config.httpUserPassword config.httpUserPassword
).createAuthenticator() ).createAuthenticator()
val gson = val gson =
GsonBuilder() GsonBuilder()
.registerTypeAdapter(Boolean::class.javaPrimitiveType, BooleanTypeAdapter()) .registerTypeAdapter(Boolean::class.javaPrimitiveType, BooleanTypeAdapter())
.setLenient() .setLenient()
.create() .create()
val logging = HttpLoggingInterceptor() val logging = HttpLoggingInterceptor()
logging.level = if (shouldLog) logging.level = if (shouldLog) {
HttpLoggingInterceptor.Level.BODY HttpLoggingInterceptor.Level.BODY
else } else {
HttpLoggingInterceptor.Level.NONE HttpLoggingInterceptor.Level.NONE
}
val httpClient = authenticator.getHttpClien(isWithSelfSignedCert) val httpClient = authenticator.getHttpClien(isWithSelfSignedCert)
@ -79,12 +83,12 @@ class SelfossApi(c: Context, callingActivity: Activity, isWithSelfSignedCert: Bo
try { try {
val retrofit = val retrofit =
Retrofit Retrofit
.Builder() .Builder()
.baseUrl(config.baseUrl) .baseUrl(config.baseUrl)
.client(httpClient.build()) .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)
} catch (e: IllegalArgumentException) { } catch (e: IllegalArgumentException) {
Config.logoutAndRedirect(c, callingActivity, config.settings.edit(), baseUrlFail = true) Config.logoutAndRedirect(c, callingActivity, config.settings.edit(), baseUrlFail = true)
@ -92,34 +96,59 @@ 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?, itemsNumber: Int, offset: Int): Call<List<Item>> = fun readItems(
getItems("read", tag, sourceId, search, itemsNumber, offset) tag: String?,
sourceId: Long?,
search: String?,
itemsNumber: Int,
offset: Int
): Call<List<Item>> =
getItems("read", tag, sourceId, search, itemsNumber, offset)
fun newItems(tag: String?, sourceId: Long?, search: String?, itemsNumber: Int, offset: Int): Call<List<Item>> = fun newItems(
getItems("unread", tag, sourceId, search, itemsNumber, offset) tag: String?,
sourceId: Long?,
search: String?,
itemsNumber: Int,
offset: Int
): Call<List<Item>> =
getItems("unread", tag, sourceId, search, itemsNumber, offset)
fun starredItems(tag: String?, sourceId: Long?, search: String?, itemsNumber: Int, offset: Int): Call<List<Item>> = fun starredItems(
getItems("starred", tag, sourceId, search, itemsNumber, offset) tag: String?,
sourceId: Long?,
search: String?,
itemsNumber: Int,
offset: Int
): Call<List<Item>> =
getItems("starred", tag, sourceId, search, itemsNumber, offset)
private fun getItems(type: String, tag: String?, sourceId: Long?, search: String?, items: Int, offset: Int): Call<List<Item>> = private fun getItems(
service.getItems(type, tag, sourceId, search, userName, password, items, offset) type: String,
tag: String?,
sourceId: Long?,
search: String?,
items: Int,
offset: Int
): Call<List<Item>> =
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)
fun unmarkItem(itemId: String): Call<SuccessResponse> = fun unmarkItem(itemId: String): Call<SuccessResponse> =
service.unmarkAsRead(itemId, userName, password) service.unmarkAsRead(itemId, userName, password)
fun readAll(ids: List<String>): Call<SuccessResponse> = fun readAll(ids: List<String>): Call<SuccessResponse> =
service.markAllAsRead(ids, userName, password) service.markAllAsRead(ids, userName, password)
fun starrItem(itemId: String): Call<SuccessResponse> = fun starrItem(itemId: String): Call<SuccessResponse> =
service.starr(itemId, userName, password) service.starr(itemId, userName, password)
fun unstarrItem(itemId: String): Call<SuccessResponse> = fun unstarrItem(itemId: String): Call<SuccessResponse> =
service.unstarr(itemId, userName, password) service.unstarr(itemId, userName, password)
val stats: Call<Stats> val stats: Call<Stats>
get() = service.stats(userName, password) get() = service.stats(userName, password)
@ -128,18 +157,23 @@ class SelfossApi(c: Context, callingActivity: Activity, isWithSelfSignedCert: Bo
get() = service.tags(userName, password) get() = service.tags(userName, password)
fun update(): Call<String> = fun update(): Call<String> =
service.update(userName, password) service.update(userName, password)
val sources: Call<List<Sources>> val sources: Call<List<Sources>>
get() = service.sources(userName, password) get() = service.sources(userName, password)
fun deleteSource(id: String): Call<SuccessResponse> = fun deleteSource(id: String): Call<SuccessResponse> =
service.deleteSource(id, userName, password) service.deleteSource(id, userName, password)
fun spouts(): Call<Map<String, Spout>> = fun spouts(): Call<Map<String, Spout>> =
service.spouts(userName, password) service.spouts(userName, password)
fun createSource(title: String, url: String, spout: String, tags: String, filter: String): Call<SuccessResponse> =
service.createSource(title, url, spout, tags, filter, userName, password)
fun createSource(
title: String,
url: String,
spout: String,
tags: String,
filter: String
): Call<SuccessResponse> =
service.createSource(title, url, spout, tags, filter, userName, password)
} }

View File

@ -9,59 +9,69 @@ 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 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 {
val baseUriBuilder = Uri.parse(config!!.baseUrl).buildUpon() val baseUriBuilder = Uri.parse(config!!.baseUrl).buildUpon()
baseUriBuilder.appendPath(path).appendPath(file) baseUriBuilder.appendPath(path).appendPath(file)
return if (file.isEmptyOrNullOrNullString()) "" return if (file.isEmptyOrNullOrNullString()) {
else baseUriBuilder.toString() ""
} else {
baseUriBuilder.toString()
}
} }
data class Tag(
data class Tag(@SerializedName("tag") val tag: String, @SerializedName("tag") val tag: String,
@SerializedName("color") val color: String, @SerializedName("color") val color: String,
@SerializedName("unread") val unread: Int) @SerializedName("unread") val unread: Int
)
class SuccessResponse(@SerializedName("success") val success: Boolean) { class SuccessResponse(@SerializedName("success") val success: Boolean) {
val isSuccess: Boolean val isSuccess: Boolean
get() = success get() = success
} }
class Stats(@SerializedName("total") val total: Int, class Stats(
@SerializedName("unread") val unread: Int, @SerializedName("total") val total: Int,
@SerializedName("starred") val starred: Int) @SerializedName("unread") val unread: Int,
@SerializedName("starred") val starred: Int
)
data class Spout(@SerializedName("name") val name: String, data class Spout(
@SerializedName("description") val description: String) @SerializedName("name") val name: String,
@SerializedName("description") val description: String
)
data class Sources(@SerializedName("id") val id: String, data class Sources(
@SerializedName("title") val title: String, @SerializedName("id") val id: String,
@SerializedName("tags") val tags: String, @SerializedName("title") val title: String,
@SerializedName("spout") val spout: String, @SerializedName("tags") val tags: String,
@SerializedName("error") val error: String, @SerializedName("spout") val spout: String,
@SerializedName("icon") val icon: String) { @SerializedName("error") val error: String,
@SerializedName("icon") val icon: String
) {
var config: Config? = null var config: Config? = null
fun getIcon(app: Context): String { fun getIcon(app: Context): String {
if (config == null) { if (config == null) {
config = Config(app) config = Config(app)
} }
return constructUrl(config,"favicons", icon) return constructUrl(config, "favicons", icon)
} }
} }
data class Item(@SerializedName("id") val id: String, data class Item(
@SerializedName("datetime") val datetime: String, @SerializedName("id") val id: String,
@SerializedName("title") val title: String, @SerializedName("datetime") val datetime: String,
@SerializedName("content") val content: String, @SerializedName("title") val title: String,
@SerializedName("unread") val unread: Boolean, @SerializedName("content") val content: String,
@SerializedName("starred") val starred: Boolean, @SerializedName("unread") val unread: Boolean,
@SerializedName("thumbnail") val thumbnail: String, @SerializedName("starred") val starred: Boolean,
@SerializedName("icon") val icon: String, @SerializedName("thumbnail") val thumbnail: String,
@SerializedName("link") val link: String, @SerializedName("icon") val icon: String,
@SerializedName("sourcetitle") val sourcetitle: String) : Parcelable { @SerializedName("link") val link: String,
@SerializedName("sourcetitle") val sourcetitle: String
) : Parcelable {
var config: Config? = null var config: Config? = null
@ -139,5 +149,4 @@ data class Item(@SerializedName("id") val id: String,
return stringUrl return stringUrl
} }
} }

View File

@ -10,97 +10,109 @@ import retrofit2.http.POST
import retrofit2.http.Path import retrofit2.http.Path
import retrofit2.http.Query import retrofit2.http.Query
internal interface SelfossService { internal interface SelfossService {
@GET("login") @GET("login")
fun loginToSelfoss(@Query("username") username: String, @Query("password") password: String): Call<SuccessResponse> fun loginToSelfoss(@Query("username") username: String, @Query("password") password: String): Call<SuccessResponse>
@GET("items") @GET("items")
fun getItems(@Query("type") type: String, fun getItems(
@Query("tag") tag: String?, @Query("type") type: String,
@Query("source") source: Long?, @Query("tag") tag: String?,
@Query("search") search: String?, @Query("source") source: Long?,
@Query("username") username: String, @Query("search") search: String?,
@Query("password") password: String, @Query("username") username: String,
@Query("items") items: Int, @Query("password") password: String,
@Query("offset") offset: Int): Call<List<Item>> @Query("items") items: Int,
@Query("offset") offset: Int
): Call<List<Item>>
@Headers("Content-Type: application/x-www-form-urlencoded") @Headers("Content-Type: application/x-www-form-urlencoded")
@POST("mark/{id}") @POST("mark/{id}")
fun markAsRead(@Path("id") id: String, fun markAsRead(
@Query("username") username: String, @Path("id") id: String,
@Query("password") password: String): Call<SuccessResponse> @Query("username") username: String,
@Query("password") password: String
): Call<SuccessResponse>
@Headers("Content-Type: application/x-www-form-urlencoded") @Headers("Content-Type: application/x-www-form-urlencoded")
@POST("unmark/{id}") @POST("unmark/{id}")
fun unmarkAsRead(@Path("id") id: String, fun unmarkAsRead(
@Query("username") username: String, @Path("id") id: String,
@Query("password") password: String): Call<SuccessResponse> @Query("username") username: String,
@Query("password") password: String
): Call<SuccessResponse>
@FormUrlEncoded @FormUrlEncoded
@POST("mark") @POST("mark")
fun markAllAsRead(@Field("ids[]") ids: List<String>, fun markAllAsRead(
@Query("username") username: String, @Field("ids[]") ids: List<String>,
@Query("password") password: String): Call<SuccessResponse> @Query("username") username: String,
@Query("password") password: String
): Call<SuccessResponse>
@Headers("Content-Type: application/x-www-form-urlencoded") @Headers("Content-Type: application/x-www-form-urlencoded")
@POST("starr/{id}") @POST("starr/{id}")
fun starr(@Path("id") id: String, fun starr(
@Query("username") username: String, @Path("id") id: String,
@Query("password") password: String): Call<SuccessResponse> @Query("username") username: String,
@Query("password") password: String
): Call<SuccessResponse>
@Headers("Content-Type: application/x-www-form-urlencoded") @Headers("Content-Type: application/x-www-form-urlencoded")
@POST("unstarr/{id}") @POST("unstarr/{id}")
fun unstarr(@Path("id") id: String, fun unstarr(
@Query("username") username: String, @Path("id") id: String,
@Query("password") password: String): Call<SuccessResponse> @Query("username") username: String,
@Query("password") password: String
): Call<SuccessResponse>
@GET("stats") @GET("stats")
fun stats(@Query("username") username: String, fun stats(
@Query("password") password: String): Call<Stats> @Query("username") username: String,
@Query("password") password: String
): Call<Stats>
@GET("tags") @GET("tags")
fun tags(@Query("username") username: String, fun tags(
@Query("password") password: String): Call<List<Tag>> @Query("username") username: String,
@Query("password") password: String
): Call<List<Tag>>
@GET("update") @GET("update")
fun update(@Query("username") username: String, fun update(
@Query("password") password: String): Call<String> @Query("username") username: String,
@Query("password") password: String
): Call<String>
@GET("sources/spouts") @GET("sources/spouts")
fun spouts(@Query("username") username: String, fun spouts(
@Query("password") password: String): Call<Map<String, Spout>> @Query("username") username: String,
@Query("password") password: String
): Call<Map<String, Spout>>
@GET("sources/list") @GET("sources/list")
fun sources(@Query("username") username: String, fun sources(
@Query("password") password: String): Call<List<Sources>> @Query("username") username: String,
@Query("password") password: String
): Call<List<Sources>>
@DELETE("source/{id}") @DELETE("source/{id}")
fun deleteSource(@Path("id") id: String, fun deleteSource(
@Query("username") username: String, @Path("id") id: String,
@Query("password") password: String): Call<SuccessResponse> @Query("username") username: String,
@Query("password") password: String
): Call<SuccessResponse>
@FormUrlEncoded @FormUrlEncoded
@POST("source") @POST("source")
fun createSource(@Field("title") title: String, fun createSource(
@Field("url") url: String, @Field("title") title: String,
@Field("spout") spout: String, @Field("url") url: String,
@Field("tags") tags: String, @Field("spout") spout: String,
@Field("filter") filter: String, @Field("tags") tags: String,
@Query("username") username: String, @Field("filter") filter: String,
@Query("password") password: String): Call<SuccessResponse> @Query("username") username: String,
@Query("password") password: String
): Call<SuccessResponse>
} }

View File

@ -6,7 +6,6 @@ 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
class AppColors(a: Activity) { class AppColors(a: Activity) {
@ColorInt val accent: Int @ColorInt val accent: Int
@ColorInt val dark: Int @ColorInt val dark: Int
@ -20,7 +19,7 @@ class AppColors(a: Activity) {
val method = wrapper!!.getMethod("getThemeResId") val method = wrapper!!.getMethod("getThemeResId")
method.isAccessible = true method.isAccessible = true
isDarkTheme = when(method.invoke(a.baseContext)) { isDarkTheme = when (method.invoke(a.baseContext)) {
R.style.NoBarTealOrangeDark, R.style.NoBarTealOrangeDark,
R.style.NoBarDark, R.style.NoBarDark,
R.style.NoBarBlueAmberDark, R.style.NoBarBlueAmberDark,

View File

@ -4,4 +4,4 @@ import apps.amine.bou.readerforselfoss.api.selfoss.SuccessResponse
import retrofit2.Response import retrofit2.Response
fun Response<SuccessResponse>.succeeded(): Boolean = fun Response<SuccessResponse>.succeeded(): Boolean =
this.code() === 200 && this.body() != null && this.body()!!.isSuccess this.code() === 200 && this.body() != null && this.body()!!.isSuccess

View File

@ -8,13 +8,14 @@ import android.support.v7.app.AlertDialog
import apps.amine.bou.readerforselfoss.R import apps.amine.bou.readerforselfoss.R
import com.google.firebase.remoteconfig.FirebaseRemoteConfig import com.google.firebase.remoteconfig.FirebaseRemoteConfig
fun String?.isEmptyOrNullOrNullString(): Boolean = fun String?.isEmptyOrNullOrNullString(): Boolean =
this == null || this == "null" || this.isEmpty() this == null || this == "null" || this.isEmpty()
fun Context.checkApkVersion(settings: SharedPreferences, fun Context.checkApkVersion(
editor: SharedPreferences.Editor, settings: SharedPreferences,
mFirebaseRemoteConfig: FirebaseRemoteConfig) = { editor: SharedPreferences.Editor,
mFirebaseRemoteConfig: FirebaseRemoteConfig
) = {
fun isThereAnUpdate() { fun isThereAnUpdate() {
val APK_LINK = "github_apk" val APK_LINK = "github_apk"
@ -24,31 +25,35 @@ fun Context.checkApkVersion(settings: SharedPreferences,
val alertDialog = AlertDialog.Builder(this).create() val alertDialog = AlertDialog.Builder(this).create()
alertDialog.setTitle(getString(R.string.new_apk_available_title)) alertDialog.setTitle(getString(R.string.new_apk_available_title))
alertDialog.setMessage(getString(R.string.new_apk_available_message)) alertDialog.setMessage(getString(R.string.new_apk_available_message))
alertDialog.setButton(AlertDialog.BUTTON_POSITIVE, getString(R.string.new_apk_available_get)) { _, _ -> alertDialog.setButton(
AlertDialog.BUTTON_POSITIVE,
getString(R.string.new_apk_available_get)
) { _, _ ->
editor.putString(APK_LINK, apkLink) editor.putString(APK_LINK, apkLink)
editor.apply() editor.apply()
val browserIntent = Intent(Intent.ACTION_VIEW, Uri.parse(apkLink)) val browserIntent = Intent(Intent.ACTION_VIEW, Uri.parse(apkLink))
startActivity(browserIntent) startActivity(browserIntent)
} }
alertDialog.setButton(AlertDialog.BUTTON_NEUTRAL, getString(R.string.new_apk_available_no), alertDialog.setButton(
{ dialog, _ -> AlertDialog.BUTTON_NEUTRAL, getString(R.string.new_apk_available_no),
editor.putString(APK_LINK, apkLink) { dialog, _ ->
editor.apply() editor.putString(APK_LINK, apkLink)
dialog.dismiss() editor.apply()
}) dialog.dismiss()
}
)
alertDialog.show() alertDialog.show()
} }
} }
mFirebaseRemoteConfig.fetch(43200) mFirebaseRemoteConfig.fetch(43200)
.addOnCompleteListener { task -> .addOnCompleteListener { task ->
if (task.isSuccessful) { if (task.isSuccessful) {
mFirebaseRemoteConfig.activateFetched() mFirebaseRemoteConfig.activateFetched()
} }
isThereAnUpdate() isThereAnUpdate()
} }
} }
fun String.longHash(): Long { fun String.longHash(): Long {
@ -62,11 +67,12 @@ fun String.longHash(): Long {
return h return h
} }
fun String.toStringUriWithHttp() = fun String.toStringUriWithHttp(): String =
if (!this.startsWith("https://") && !this.startsWith("http://")) if (!this.startsWith("https://") && !this.startsWith("http://")) {
"http://" + this "http://" + this
else } else {
this this
}
fun Context.shareLink(itemUrl: String) { fun Context.shareLink(itemUrl: String) {
val sendIntent = Intent() val sendIntent = Intent()
@ -74,5 +80,10 @@ fun Context.shareLink(itemUrl: String) {
sendIntent.action = Intent.ACTION_SEND sendIntent.action = Intent.ACTION_SEND
sendIntent.putExtra(Intent.EXTRA_TEXT, itemUrl.toStringUriWithHttp()) sendIntent.putExtra(Intent.EXTRA_TEXT, itemUrl.toStringUriWithHttp())
sendIntent.type = "text/plain" sendIntent.type = "text/plain"
startActivity(Intent.createChooser(sendIntent, getString(R.string.share)).setFlags(Intent.FLAG_ACTIVITY_NEW_TASK)) startActivity(
Intent.createChooser(
sendIntent,
getString(R.string.share)
).setFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
)
} }

View File

@ -6,7 +6,6 @@ import android.content.Intent
import android.content.SharedPreferences import android.content.SharedPreferences
import apps.amine.bou.readerforselfoss.LoginActivity import apps.amine.bou.readerforselfoss.LoginActivity
class Config(c: Context) { class Config(c: Context) {
val settings: SharedPreferences = c.getSharedPreferences(settingsName, Context.MODE_PRIVATE) val settings: SharedPreferences = c.getSharedPreferences(settingsName, Context.MODE_PRIVATE)
@ -26,21 +25,23 @@ class Config(c: Context) {
val httpUserPassword: String val httpUserPassword: String
get() = settings.getString("httpPassword", "") get() = settings.getString("httpPassword", "")
companion object { companion object {
val settingsName = "paramsselfoss" val settingsName = "paramsselfoss"
fun logoutAndRedirect(c: Context, fun logoutAndRedirect(
callingActivity: Activity, c: Context,
editor: SharedPreferences.Editor, callingActivity: Activity,
baseUrlFail: Boolean = false): Boolean { editor: SharedPreferences.Editor,
baseUrlFail: Boolean = false
): Boolean {
editor.remove("url") editor.remove("url")
editor.remove("login") editor.remove("login")
editor.remove("password") editor.remove("password")
editor.apply() editor.apply()
val intent = Intent(c, LoginActivity::class.java) val intent = Intent(c, LoginActivity::class.java)
if (baseUrlFail) if (baseUrlFail) {
intent.putExtra("baseUrlFail", baseUrlFail) intent.putExtra("baseUrlFail", baseUrlFail)
}
c.startActivity(intent) c.startActivity(intent)
callingActivity.finish() callingActivity.finish()
return true return true

View File

@ -8,32 +8,36 @@ import javax.net.ssl.TrustManager
import javax.net.ssl.X509TrustManager import javax.net.ssl.X509TrustManager
fun getUnsafeHttpClient() = fun getUnsafeHttpClient() =
try { try {
// Create a trust manager that does not validate certificate chains // Create a trust manager that does not validate certificate chains
val trustAllCerts = arrayOf<TrustManager>(object : X509TrustManager { val trustAllCerts = arrayOf<TrustManager>(object : X509TrustManager {
override fun getAcceptedIssuers(): Array<X509Certificate> = override fun getAcceptedIssuers(): Array<X509Certificate> =
arrayOf() arrayOf()
@Throws(CertificateException::class) @Throws(CertificateException::class)
override fun checkClientTrusted(chain: Array<java.security.cert.X509Certificate>, authType: String) { override fun checkClientTrusted(
} chain: Array<java.security.cert.X509Certificate>,
authType: String
) {
}
@Throws(CertificateException::class) @Throws(CertificateException::class)
override fun checkServerTrusted(chain: Array<java.security.cert.X509Certificate>, authType: String) { override fun checkServerTrusted(
} chain: Array<java.security.cert.X509Certificate>,
authType: String
) {
}
})
}) // Install the all-trusting trust manager
val sslContext = SSLContext.getInstance("SSL")
sslContext.init(null, trustAllCerts, java.security.SecureRandom())
// Install the all-trusting trust manager val sslSocketFactory = sslContext.socketFactory
val sslContext = SSLContext.getInstance("SSL")
sslContext.init(null, trustAllCerts, java.security.SecureRandom())
val sslSocketFactory = sslContext.socketFactory OkHttpClient.Builder()
.sslSocketFactory(sslSocketFactory, trustAllCerts[0] as X509TrustManager)
OkHttpClient.Builder() .hostnameVerifier { _, _ -> true }
.sslSocketFactory(sslSocketFactory, trustAllCerts[0] as X509TrustManager) } catch (e: Exception) {
.hostnameVerifier { _, _ -> true } throw RuntimeException(e)
}
} catch (e: Exception) {
throw RuntimeException(e)
}

View File

@ -6,7 +6,6 @@ import java.text.ParseException
import java.text.SimpleDateFormat import java.text.SimpleDateFormat
import java.util.* import java.util.*
fun String.toTextDrawableString(): String { fun String.toTextDrawableString(): String {
val textDrawable = StringBuilder() val textDrawable = StringBuilder()
for (s in this.split(" ".toRegex()).dropLastWhile { it.isEmpty() }.toTypedArray()) { for (s in this.split(" ".toRegex()).dropLastWhile { it.isEmpty() }.toTypedArray()) {
@ -18,10 +17,10 @@ fun String.toTextDrawableString(): String {
fun Item.sourceAndDateText(): String { fun Item.sourceAndDateText(): String {
val 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,
DateUtils.MINUTE_IN_MILLIS, DateUtils.MINUTE_IN_MILLIS,
DateUtils.FORMAT_ABBREV_RELATIVE DateUtils.FORMAT_ABBREV_RELATIVE
) )
} catch (e: ParseException) { } catch (e: ParseException) {
e.printStackTrace() e.printStackTrace()

View File

@ -14,15 +14,17 @@ import apps.amine.bou.readerforselfoss.ReaderActivity
import apps.amine.bou.readerforselfoss.api.selfoss.Item 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 okhttp3.HttpUrl import okhttp3.HttpUrl
import xyz.klinker.android.drag_dismiss.DragDismissIntentBuilder
fun Context.buildCustomTabsIntent(): CustomTabsIntent { fun Context.buildCustomTabsIntent(): CustomTabsIntent {
val actionIntent = Intent(Intent.ACTION_SEND) val actionIntent = Intent(Intent.ACTION_SEND)
actionIntent.type = "text/plain" actionIntent.type = "text/plain"
val createPendingShareIntent: PendingIntent = PendingIntent.getActivity(this, 0, actionIntent, 0) val createPendingShareIntent: PendingIntent = PendingIntent.getActivity(
this,
0,
actionIntent,
0
)
val intentBuilder = CustomTabsIntent.Builder() val intentBuilder = CustomTabsIntent.Builder()
@ -32,32 +34,40 @@ fun Context.buildCustomTabsIntent(): CustomTabsIntent {
intentBuilder.setShowTitle(true) intentBuilder.setShowTitle(true)
intentBuilder.setStartAnimations(this, intentBuilder.setStartAnimations(
this,
R.anim.slide_in_right, R.anim.slide_in_right,
R.anim.slide_out_left) R.anim.slide_out_left
intentBuilder.setExitAnimations(this, )
intentBuilder.setExitAnimations(
this,
android.R.anim.slide_in_left, android.R.anim.slide_in_left,
android.R.anim.slide_out_right) android.R.anim.slide_out_right
)
val closeicon = BitmapFactory.decodeResource(resources, R.drawable.ic_close_white_24dp) val closeicon = BitmapFactory.decodeResource(resources, R.drawable.ic_close_white_24dp)
intentBuilder.setCloseButtonIcon(closeicon) intentBuilder.setCloseButtonIcon(closeicon)
val shareLabel = this.getString(R.string.label_share) val shareLabel = this.getString(R.string.label_share)
val icon = BitmapFactory.decodeResource(resources, val icon = BitmapFactory.decodeResource(
R.drawable.ic_share_white_24dp) resources,
R.drawable.ic_share_white_24dp
)
intentBuilder.setActionButton(icon, shareLabel, createPendingShareIntent) intentBuilder.setActionButton(icon, shareLabel, createPendingShareIntent)
return intentBuilder.build() return intentBuilder.build()
} }
fun Context.openItemUrlInternally(linkDecoded: String, fun Context.openItemUrlInternally(
content: String, linkDecoded: String,
image: String, content: String,
title: String, image: String,
source: String, title: String,
customTabsIntent: CustomTabsIntent, source: String,
articleViewer: Boolean, customTabsIntent: CustomTabsIntent,
app: Activity) { articleViewer: Boolean,
app: Activity
) {
if (articleViewer) { if (articleViewer) {
val intent = Intent(this, ReaderActivity::class.java) val intent = Intent(this, ReaderActivity::class.java)
@ -76,7 +86,10 @@ fun Context.openItemUrlInternally(linkDecoded: String,
app.startActivity(intent) app.startActivity(intent)
} else { } else {
try { try {
CustomTabActivityHelper.openCustomTab(app, customTabsIntent, Uri.parse(linkDecoded) CustomTabActivityHelper.openCustomTab(
app,
customTabsIntent,
Uri.parse(linkDecoded)
) { _, uri -> ) { _, uri ->
val intent = Intent(Intent.ACTION_VIEW, uri) val intent = Intent(Intent.ACTION_VIEW, uri)
intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK
@ -88,23 +101,38 @@ fun Context.openItemUrlInternally(linkDecoded: String,
} }
} }
fun Context.openItemUrl(linkDecoded: String, fun Context.openItemUrl(
content: String, linkDecoded: String,
image: String, content: String,
title: String, image: String,
source: String, title: String,
customTabsIntent: CustomTabsIntent, source: String,
internalBrowser: Boolean, customTabsIntent: CustomTabsIntent,
articleViewer: Boolean, internalBrowser: Boolean,
app: Activity) { articleViewer: Boolean,
app: Activity
) {
if (!linkDecoded.isUrlValid()) { if (!linkDecoded.isUrlValid()) {
Toast.makeText(this, this.getString(R.string.cant_open_invalid_url), Toast.LENGTH_LONG).show() Toast.makeText(
this,
this.getString(R.string.cant_open_invalid_url),
Toast.LENGTH_LONG
).show()
} else { } else {
if (!internalBrowser) { if (!internalBrowser) {
openInBrowser(linkDecoded, app) openInBrowser(linkDecoded, app)
} else { } else {
this.openItemUrlInternally(linkDecoded, content, image, title, source, customTabsIntent, articleViewer, app) this.openItemUrlInternally(
linkDecoded,
content,
image,
title,
source,
customTabsIntent,
articleViewer,
app
)
} }
} }
} }

View File

@ -6,18 +6,39 @@ import android.support.design.widget.FloatingActionButton
import android.util.AttributeSet import android.util.AttributeSet
import android.view.View import android.view.View
class ScrollAwareFABBehavior(context: Context, attrs: AttributeSet) : CoordinatorLayout.Behavior<FloatingActionButton>() { class ScrollAwareFABBehavior(
context: Context,
attrs: AttributeSet
) : CoordinatorLayout.Behavior<FloatingActionButton>() {
override fun onStartNestedScroll(coordinatorLayout: CoordinatorLayout, child: FloatingActionButton, override fun onStartNestedScroll(
directTargetChild: View, target: View, nestedScrollAxes: Int): Boolean { coordinatorLayout: CoordinatorLayout,
child: FloatingActionButton,
directTargetChild: View,
target: View,
nestedScrollAxes: Int
): Boolean {
return true return true
} }
override fun onNestedScroll(coordinatorLayout: CoordinatorLayout, override fun onNestedScroll(
child: FloatingActionButton, coordinatorLayout: CoordinatorLayout,
target: View, dxConsumed: Int, dyConsumed: Int, child: FloatingActionButton,
dxUnconsumed: Int, dyUnconsumed: Int) { target: View,
super.onNestedScroll(coordinatorLayout, child, target, dxConsumed, dyConsumed, dxUnconsumed, dyUnconsumed) dxConsumed: Int,
dyConsumed: Int,
dxUnconsumed: Int,
dyUnconsumed: Int
) {
super.onNestedScroll(
coordinatorLayout,
child,
target,
dxConsumed,
dyConsumed,
dxUnconsumed,
dyUnconsumed
)
if (dyConsumed > 0 && child.visibility == View.VISIBLE) { if (dyConsumed > 0 && child.visibility == View.VISIBLE) {
child.hide(object : FloatingActionButton.OnVisibilityChangedListener() { child.hide(object : FloatingActionButton.OnVisibilityChangedListener() {
override fun onHidden(fab: FloatingActionButton?) { override fun onHidden(fab: FloatingActionButton?) {
@ -25,7 +46,6 @@ class ScrollAwareFABBehavior(context: Context, attrs: AttributeSet) : Coordinato
fab!!.visibility = View.INVISIBLE fab!!.visibility = View.INVISIBLE
} }
}) })
} else if (dyConsumed < 0 && child.visibility != View.VISIBLE) { } else if (dyConsumed < 0 && child.visibility != View.VISIBLE) {
child.show() child.show()
} }

View File

@ -2,7 +2,6 @@ package apps.amine.bou.readerforselfoss.utils.bottombar
import com.ashokvarma.bottomnavigation.TextBadgeItem import com.ashokvarma.bottomnavigation.TextBadgeItem
fun TextBadgeItem.removeBadge(): TextBadgeItem { fun TextBadgeItem.removeBadge(): TextBadgeItem {
this.setText("") this.setText("")
this.hide() this.hide()
@ -10,7 +9,4 @@ fun TextBadgeItem.removeBadge(): TextBadgeItem {
} }
fun TextBadgeItem.maybeShow(): TextBadgeItem = fun TextBadgeItem.maybeShow(): TextBadgeItem =
if (this.isHidden) if (this.isHidden) this.show() else this
this.show()
else
this

View File

@ -8,8 +8,6 @@ import android.widget.TextView
import apps.amine.bou.readerforselfoss.R import apps.amine.bou.readerforselfoss.R
open class CustomBaseViewHolder(var view: View) : RecyclerView.ViewHolder(view) { open class CustomBaseViewHolder(var view: View) : RecyclerView.ViewHolder(view) {
var icon: ImageView = view.findViewById(R.id.material_drawer_icon) var icon: ImageView = view.findViewById(R.id.material_drawer_icon)
var name: TextView = view.findViewById(R.id.material_drawer_name) var name: TextView = view.findViewById(R.id.material_drawer_name)

View File

@ -15,8 +15,6 @@ import com.mikepenz.materialdrawer.util.DrawerImageLoader
import com.mikepenz.materialdrawer.util.DrawerUIUtils import com.mikepenz.materialdrawer.util.DrawerUIUtils
import com.mikepenz.materialize.util.UIUtils import com.mikepenz.materialize.util.UIUtils
abstract class CustomUrlBasePrimaryDrawerItem<T, VH : RecyclerView.ViewHolder> : BaseDrawerItem<T, VH>() { abstract class CustomUrlBasePrimaryDrawerItem<T, VH : RecyclerView.ViewHolder> : BaseDrawerItem<T, VH>() {
fun withIcon(url: String): T { fun withIcon(url: String): T {
this.icon = ImageHolder(url) this.icon = ImageHolder(url)
@ -77,7 +75,10 @@ abstract class CustomUrlBasePrimaryDrawerItem<T, VH : RecyclerView.ViewHolder> :
val selectedIconColor = getSelectedIconColor(ctx) val selectedIconColor = getSelectedIconColor(ctx)
//set the background for the item //set the background for the item
UIUtils.setBackground(viewHolder.view, UIUtils.getSelectableBackground(ctx, selectedColor, true)) UIUtils.setBackground(
viewHolder.view,
UIUtils.getSelectableBackground(ctx, selectedColor, true)
)
//set the text for the name //set the text for the name
StringHolder.applyTo(this.getName(), viewHolder.name) StringHolder.applyTo(this.getName(), viewHolder.name)
//set the text for the description or hide //set the text for the description or hide
@ -86,8 +87,11 @@ abstract class CustomUrlBasePrimaryDrawerItem<T, VH : RecyclerView.ViewHolder> :
//set the colors for textViews //set the colors for textViews
viewHolder.name.setTextColor(getTextColorStateList(color, selectedTextColor)) viewHolder.name.setTextColor(getTextColorStateList(color, selectedTextColor))
//set the description text color //set the description text color
ColorHolder.applyToOr(descriptionTextColor, ColorHolder.applyToOr(
viewHolder.description, getTextColorStateList(color, selectedTextColor)) descriptionTextColor,
viewHolder.description,
getTextColorStateList(color, selectedTextColor)
)
//define the typeface for our textViews //define the typeface for our textViews
if (getTypeface() != null) { if (getTypeface() != null) {

View File

@ -10,7 +10,6 @@ import com.mikepenz.materialdrawer.holder.BadgeStyle
import com.mikepenz.materialdrawer.holder.StringHolder import com.mikepenz.materialdrawer.holder.StringHolder
import com.mikepenz.materialdrawer.model.interfaces.ColorfulBadgeable import com.mikepenz.materialdrawer.model.interfaces.ColorfulBadgeable
class CustomUrlPrimaryDrawerItem : CustomUrlBasePrimaryDrawerItem<CustomUrlPrimaryDrawerItem, CustomUrlPrimaryDrawerItem.ViewHolder>(), ColorfulBadgeable<CustomUrlPrimaryDrawerItem> { class CustomUrlPrimaryDrawerItem : CustomUrlBasePrimaryDrawerItem<CustomUrlPrimaryDrawerItem, CustomUrlPrimaryDrawerItem.ViewHolder>(), ColorfulBadgeable<CustomUrlPrimaryDrawerItem> {
protected var mBadge: StringHolder = StringHolder("") protected var mBadge: StringHolder = StringHolder("")
protected var mBadgeStyle = BadgeStyle() protected var mBadgeStyle = BadgeStyle()
@ -64,7 +63,10 @@ class CustomUrlPrimaryDrawerItem : CustomUrlBasePrimaryDrawerItem<CustomUrlPrima
val badgeVisible = StringHolder.applyToOrHide(mBadge, viewHolder.badge) val badgeVisible = StringHolder.applyToOrHide(mBadge, viewHolder.badge)
//style the badge if it is visible //style the badge if it is visible
if (badgeVisible) { if (badgeVisible) {
mBadgeStyle.style(viewHolder.badge, getTextColorStateList(getColor(ctx), getSelectedTextColor(ctx))) mBadgeStyle.style(
viewHolder.badge,
getTextColorStateList(getColor(ctx), getSelectedTextColor(ctx))
)
viewHolder.badgeContainer.visibility = View.VISIBLE viewHolder.badgeContainer.visibility = View.VISIBLE
} else { } else {
viewHolder.badgeContainer.visibility = View.GONE viewHolder.badgeContainer.visibility = View.GONE
@ -86,6 +88,5 @@ class CustomUrlPrimaryDrawerItem : CustomUrlBasePrimaryDrawerItem<CustomUrlPrima
class ViewHolder(view: View) : CustomBaseViewHolder(view) { class ViewHolder(view: View) : CustomBaseViewHolder(view) {
val badgeContainer: View = view.findViewById(R.id.material_drawer_badge_container) val badgeContainer: View = view.findViewById(R.id.material_drawer_badge_container)
val badge: TextView = view.findViewById(R.id.material_drawer_badge) val badge: TextView = view.findViewById(R.id.material_drawer_badge)
} }
} }

View File

@ -8,22 +8,32 @@ import com.bumptech.glide.Glide
import com.bumptech.glide.request.RequestOptions import com.bumptech.glide.request.RequestOptions
import com.bumptech.glide.request.target.BitmapImageViewTarget import com.bumptech.glide.request.target.BitmapImageViewTarget
fun Context.bitmapCenterCrop(url: String, iv: ImageView) = fun Context.bitmapCenterCrop(url: String, iv: ImageView) =
Glide.with(this).asBitmap().load(url).apply(RequestOptions.centerCropTransform()).into(iv) Glide.with(this)
.asBitmap()
.load(url)
.apply(RequestOptions.centerCropTransform())
.into(iv)
fun Context.bitmapFitCenter(url: String, iv: ImageView) = fun Context.bitmapFitCenter(url: String, iv: ImageView) =
Glide.with(this).asBitmap().load(url).apply(RequestOptions.fitCenterTransform()).into(iv) Glide.with(this)
.asBitmap()
.load(url)
.apply(RequestOptions.fitCenterTransform())
.into(iv)
fun Context.circularBitmapDrawable(url: String, iv: ImageView) = fun Context.circularBitmapDrawable(url: String, iv: ImageView) =
Glide.with(this) Glide.with(this)
.asBitmap() .asBitmap()
.load(url) .load(url)
.apply(RequestOptions.centerCropTransform()). .apply(RequestOptions.centerCropTransform())
into(object : BitmapImageViewTarget(iv) { .into(object : BitmapImageViewTarget(iv) {
override fun setResource(resource: Bitmap?) { override fun setResource(resource: Bitmap?) {
val circularBitmapDrawable = RoundedBitmapDrawableFactory.create(resources, resource) val circularBitmapDrawable = RoundedBitmapDrawableFactory.create(
circularBitmapDrawable.isCircular = true resources,
iv.setImageDrawable(circularBitmapDrawable) resource
} )
}) circularBitmapDrawable.isCircular = true
iv.setImageDrawable(circularBitmapDrawable)
}
})

View File

@ -10,10 +10,10 @@ import com.bumptech.glide.load.model.GlideUrl
import com.bumptech.glide.module.GlideModule import com.bumptech.glide.module.GlideModule
import java.io.InputStream import java.io.InputStream
class SelfSignedGlideModule : GlideModule { class SelfSignedGlideModule : GlideModule {
override fun applyOptions(context: Context?, builder: GlideBuilder?) {} override fun applyOptions(context: Context?, builder: GlideBuilder?) {
}
override fun registerComponents(context: Context?, glide: Glide?, registry: Registry?) { override fun registerComponents(context: Context?, glide: Glide?, registry: Registry?) {
@ -22,11 +22,12 @@ class SelfSignedGlideModule : GlideModule {
if (pref.getBoolean("isSelfSignedCert", false)) { if (pref.getBoolean("isSelfSignedCert", false)) {
val client = getUnsafeHttpClient().build() val client = getUnsafeHttpClient().build()
registry?.append(GlideUrl::class.java, InputStream::class.java, registry?.append(
com.bumptech.glide.integration.okhttp3.OkHttpUrlLoader.Factory(client)) GlideUrl::class.java,
InputStream::class.java,
com.bumptech.glide.integration.okhttp3.OkHttpUrlLoader.Factory(client)
)
} }
} }
} }
} }