WIP: Big settings cleaning.
This commit is contained in:
parent
fbcb428e96
commit
5531034086
@ -1,3 +0,0 @@
|
|||||||
package bou.amine.apps.readerforselfossv2.android
|
|
||||||
|
|
||||||
// TODO: test source adding
|
|
@ -1,102 +0,0 @@
|
|||||||
package bou.amine.apps.readerforselfossv2.android
|
|
||||||
|
|
||||||
import android.content.Context
|
|
||||||
import android.content.Intent
|
|
||||||
import androidx.test.InstrumentationRegistry
|
|
||||||
import androidx.test.espresso.Espresso.onView
|
|
||||||
import androidx.test.espresso.Espresso.openActionBarOverflowOrOptionsMenu
|
|
||||||
import androidx.test.espresso.action.ViewActions.click
|
|
||||||
import androidx.test.espresso.action.ViewActions.closeSoftKeyboard
|
|
||||||
import androidx.test.espresso.action.ViewActions.pressKey
|
|
||||||
import androidx.test.espresso.action.ViewActions.typeText
|
|
||||||
import androidx.test.espresso.assertion.ViewAssertions.matches
|
|
||||||
import androidx.test.espresso.intent.Intents
|
|
||||||
import androidx.test.espresso.intent.Intents.intended
|
|
||||||
import androidx.test.espresso.intent.matcher.IntentMatchers.hasComponent
|
|
||||||
import androidx.test.espresso.matcher.ViewMatchers.isDisplayed
|
|
||||||
import androidx.test.espresso.matcher.ViewMatchers.withContentDescription
|
|
||||||
import androidx.test.espresso.matcher.ViewMatchers.withId
|
|
||||||
import androidx.test.espresso.matcher.ViewMatchers.withText
|
|
||||||
import androidx.test.rule.ActivityTestRule
|
|
||||||
import androidx.test.runner.AndroidJUnit4
|
|
||||||
import android.view.KeyEvent
|
|
||||||
import androidx.test.espresso.matcher.RootMatchers.isDialog
|
|
||||||
import bou.amine.apps.readerforselfossv2.android.HomeActivity
|
|
||||||
import bou.amine.apps.readerforselfossv2.android.LoginActivity
|
|
||||||
import bou.amine.apps.readerforselfossv2.android.utils.Config
|
|
||||||
import org.junit.After
|
|
||||||
import org.junit.Before
|
|
||||||
import org.junit.Rule
|
|
||||||
import org.junit.Test
|
|
||||||
import org.junit.runner.RunWith
|
|
||||||
|
|
||||||
@RunWith(AndroidJUnit4::class)
|
|
||||||
class HomeActivityEspressoTest {
|
|
||||||
lateinit var context: Context
|
|
||||||
|
|
||||||
@Rule @JvmField
|
|
||||||
val rule = ActivityTestRule(HomeActivity::class.java, true, false)
|
|
||||||
|
|
||||||
@Before
|
|
||||||
fun clearData() {
|
|
||||||
context = InstrumentationRegistry.getInstrumentation().targetContext
|
|
||||||
|
|
||||||
val editor =
|
|
||||||
context
|
|
||||||
.getSharedPreferences(Config.settingsName, Context.MODE_PRIVATE)
|
|
||||||
.edit()
|
|
||||||
editor.clear()
|
|
||||||
|
|
||||||
editor.putString("url", BuildConfig.LOGIN_URL)
|
|
||||||
editor.putString("login", BuildConfig.LOGIN_USERNAME)
|
|
||||||
editor.putString("password", BuildConfig.LOGIN_PASSWORD)
|
|
||||||
|
|
||||||
editor.commit()
|
|
||||||
|
|
||||||
Intents.init()
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun menuItems() {
|
|
||||||
|
|
||||||
rule.launchActivity(Intent())
|
|
||||||
|
|
||||||
onView(
|
|
||||||
withMenu(
|
|
||||||
id = R.id.action_search,
|
|
||||||
titleId = R.string.menu_home_search
|
|
||||||
)
|
|
||||||
).perform(click())
|
|
||||||
|
|
||||||
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(withContentDescription(R.string.abc_toolbar_collapse_description)).perform(click())
|
|
||||||
|
|
||||||
openActionBarOverflowOrOptionsMenu(context)
|
|
||||||
|
|
||||||
onView(withMenu(id = R.id.refresh, titleId = R.string.menu_home_refresh))
|
|
||||||
.perform(click())
|
|
||||||
|
|
||||||
onView(withText(android.R.string.ok))
|
|
||||||
.inRoot(isDialog()).check(matches(isDisplayed())).perform(click())
|
|
||||||
|
|
||||||
openActionBarOverflowOrOptionsMenu(context)
|
|
||||||
|
|
||||||
onView(withText(R.string.action_disconnect)).perform(click())
|
|
||||||
|
|
||||||
intended(hasComponent(LoginActivity::class.java.name))
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: test articles opening and actions for cards and lists
|
|
||||||
|
|
||||||
@After
|
|
||||||
fun releaseIntents() {
|
|
||||||
Intents.release()
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,180 +0,0 @@
|
|||||||
package bou.amine.apps.readerforselfossv2.android
|
|
||||||
|
|
||||||
import android.content.Context
|
|
||||||
import android.content.Intent
|
|
||||||
import androidx.test.InstrumentationRegistry
|
|
||||||
import androidx.test.espresso.Espresso.onView
|
|
||||||
import androidx.test.espresso.Espresso.openActionBarOverflowOrOptionsMenu
|
|
||||||
import androidx.test.espresso.action.ViewActions.click
|
|
||||||
import androidx.test.espresso.action.ViewActions.closeSoftKeyboard
|
|
||||||
import androidx.test.espresso.action.ViewActions.pressBack
|
|
||||||
import androidx.test.espresso.action.ViewActions.typeText
|
|
||||||
import androidx.test.espresso.assertion.ViewAssertions.matches
|
|
||||||
import androidx.test.espresso.intent.Intents
|
|
||||||
import androidx.test.espresso.intent.Intents.intended
|
|
||||||
import androidx.test.espresso.intent.Intents.times
|
|
||||||
import androidx.test.espresso.intent.matcher.IntentMatchers.hasComponent
|
|
||||||
import androidx.test.espresso.matcher.ViewMatchers
|
|
||||||
import androidx.test.espresso.matcher.ViewMatchers.isRoot
|
|
||||||
import androidx.test.espresso.matcher.ViewMatchers.withEffectiveVisibility
|
|
||||||
import androidx.test.espresso.matcher.ViewMatchers.withId
|
|
||||||
import androidx.test.espresso.matcher.ViewMatchers.withText
|
|
||||||
import androidx.test.rule.ActivityTestRule
|
|
||||||
import androidx.test.runner.AndroidJUnit4
|
|
||||||
import bou.amine.apps.readerforselfossv2.android.HomeActivity
|
|
||||||
import bou.amine.apps.readerforselfossv2.android.LoginActivity
|
|
||||||
import bou.amine.apps.readerforselfossv2.android.utils.Config
|
|
||||||
import com.mikepenz.aboutlibraries.ui.LibsActivity
|
|
||||||
import org.junit.After
|
|
||||||
import org.junit.Before
|
|
||||||
import org.junit.Rule
|
|
||||||
import org.junit.Test
|
|
||||||
import org.junit.runner.RunWith
|
|
||||||
|
|
||||||
@RunWith(AndroidJUnit4::class)
|
|
||||||
class LoginActivityEspressoTest {
|
|
||||||
|
|
||||||
@Rule @JvmField
|
|
||||||
val rule = ActivityTestRule(LoginActivity::class.java, true, false)
|
|
||||||
|
|
||||||
private lateinit var context: Context
|
|
||||||
private lateinit var url: String
|
|
||||||
private lateinit var username: String
|
|
||||||
private lateinit var password: String
|
|
||||||
|
|
||||||
@Before
|
|
||||||
fun setUp() {
|
|
||||||
context = InstrumentationRegistry.getInstrumentation().targetContext
|
|
||||||
val editor =
|
|
||||||
context
|
|
||||||
.getSharedPreferences(Config.settingsName, Context.MODE_PRIVATE)
|
|
||||||
.edit()
|
|
||||||
editor.clear()
|
|
||||||
editor.commit()
|
|
||||||
|
|
||||||
|
|
||||||
url = BuildConfig.LOGIN_URL
|
|
||||||
username = BuildConfig.LOGIN_USERNAME
|
|
||||||
password = BuildConfig.LOGIN_PASSWORD
|
|
||||||
|
|
||||||
Intents.init()
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun menuItems() {
|
|
||||||
|
|
||||||
rule.launchActivity(Intent())
|
|
||||||
|
|
||||||
openActionBarOverflowOrOptionsMenu(context)
|
|
||||||
|
|
||||||
onView(withText(R.string.action_about)).perform(click())
|
|
||||||
|
|
||||||
intended(hasComponent(LibsActivity::class.java.name), times(1))
|
|
||||||
|
|
||||||
onView(isRoot()).perform(pressBack())
|
|
||||||
|
|
||||||
intended(hasComponent(LoginActivity::class.java.name))
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun wrongLoginUrl() {
|
|
||||||
rule.launchActivity(Intent())
|
|
||||||
|
|
||||||
onView(withId(R.id.loginProgress))
|
|
||||||
.check(matches(withEffectiveVisibility(ViewMatchers.Visibility.GONE)))
|
|
||||||
|
|
||||||
onView(withId(R.id.urlView)).perform(click()).perform(typeText("WRONGURL"))
|
|
||||||
|
|
||||||
onView(withId(R.id.signInButton)).perform(click())
|
|
||||||
|
|
||||||
onView(withId(R.id.urlView)).check(matches(isHintOrErrorEnabled()))
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: Add tests for multiple false urls with dialog
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun emptyAuthData() {
|
|
||||||
|
|
||||||
rule.launchActivity(Intent())
|
|
||||||
|
|
||||||
onView(withId(R.id.urlView)).perform(click()).perform(typeText(url), closeSoftKeyboard())
|
|
||||||
|
|
||||||
onView(withId(R.id.withLogin)).perform(click())
|
|
||||||
|
|
||||||
onView(withId(R.id.signInButton)).perform(click())
|
|
||||||
|
|
||||||
onView(withId(R.id.loginView)).check(matches(isHintOrErrorEnabled()))
|
|
||||||
onView(withId(R.id.passwordView)).check(matches(isHintOrErrorEnabled()))
|
|
||||||
|
|
||||||
onView(withId(R.id.loginView)).perform(click()).perform(
|
|
||||||
typeText(username),
|
|
||||||
closeSoftKeyboard()
|
|
||||||
)
|
|
||||||
|
|
||||||
onView(withId(R.id.passwordView)).check(matches(isHintOrErrorEnabled()))
|
|
||||||
|
|
||||||
onView(withId(R.id.signInButton)).perform(click())
|
|
||||||
|
|
||||||
onView(withId(R.id.passwordView)).check(
|
|
||||||
matches(
|
|
||||||
isHintOrErrorEnabled()
|
|
||||||
)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun wrongAuthData() {
|
|
||||||
|
|
||||||
rule.launchActivity(Intent())
|
|
||||||
|
|
||||||
onView(withId(R.id.urlView)).perform(click()).perform(typeText(url), closeSoftKeyboard())
|
|
||||||
|
|
||||||
onView(withId(R.id.withLogin)).perform(click())
|
|
||||||
|
|
||||||
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.signInButton)).perform(click())
|
|
||||||
|
|
||||||
onView(withId(R.id.urlView)).check(matches(isHintOrErrorEnabled()))
|
|
||||||
onView(withId(R.id.loginView)).check(matches(isHintOrErrorEnabled()))
|
|
||||||
onView(withId(R.id.passwordView)).check(matches(isHintOrErrorEnabled()))
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun workingAuth() {
|
|
||||||
|
|
||||||
rule.launchActivity(Intent())
|
|
||||||
|
|
||||||
onView(withId(R.id.urlView)).perform(click()).perform(typeText(url), closeSoftKeyboard())
|
|
||||||
|
|
||||||
onView(withId(R.id.withLogin)).perform(click())
|
|
||||||
|
|
||||||
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.signInButton)).perform(click())
|
|
||||||
|
|
||||||
Thread.sleep(2000)
|
|
||||||
intended(hasComponent(HomeActivity::class.java.name))
|
|
||||||
}
|
|
||||||
|
|
||||||
@After
|
|
||||||
fun releaseIntents() {
|
|
||||||
Intents.release()
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,81 +0,0 @@
|
|||||||
package bou.amine.apps.readerforselfossv2.android
|
|
||||||
|
|
||||||
import android.content.Context
|
|
||||||
import android.content.Intent
|
|
||||||
import android.content.SharedPreferences
|
|
||||||
import androidx.test.InstrumentationRegistry.getInstrumentation
|
|
||||||
import androidx.test.espresso.intent.Intents
|
|
||||||
import androidx.test.espresso.intent.Intents.intended
|
|
||||||
import androidx.test.espresso.intent.Intents.times
|
|
||||||
import androidx.test.espresso.intent.matcher.IntentMatchers.hasComponent
|
|
||||||
import androidx.test.rule.ActivityTestRule
|
|
||||||
import androidx.test.runner.AndroidJUnit4
|
|
||||||
import bou.amine.apps.readerforselfossv2.android.HomeActivity
|
|
||||||
import bou.amine.apps.readerforselfossv2.android.LoginActivity
|
|
||||||
import bou.amine.apps.readerforselfossv2.android.MainActivity
|
|
||||||
import bou.amine.apps.readerforselfossv2.android.utils.Config
|
|
||||||
import org.junit.After
|
|
||||||
|
|
||||||
import org.junit.Before
|
|
||||||
import org.junit.Rule
|
|
||||||
import org.junit.Test
|
|
||||||
import org.junit.runner.RunWith
|
|
||||||
|
|
||||||
@RunWith(AndroidJUnit4::class)
|
|
||||||
class MainActivityEspressoTest {
|
|
||||||
|
|
||||||
lateinit var intent: Intent
|
|
||||||
lateinit var preferencesEditor: SharedPreferences.Editor
|
|
||||||
private lateinit var url: String
|
|
||||||
private lateinit var username: String
|
|
||||||
private lateinit var password: String
|
|
||||||
|
|
||||||
@Rule @JvmField
|
|
||||||
val rule = ActivityTestRule(MainActivity::class.java, true, false)
|
|
||||||
|
|
||||||
@Before
|
|
||||||
fun setUp() {
|
|
||||||
intent = Intent()
|
|
||||||
val context = getInstrumentation().targetContext
|
|
||||||
|
|
||||||
// create a SharedPreferences editor
|
|
||||||
preferencesEditor = context.getSharedPreferences(Config.settingsName, Context.MODE_PRIVATE).edit()
|
|
||||||
|
|
||||||
url = BuildConfig.LOGIN_URL
|
|
||||||
username = BuildConfig.LOGIN_USERNAME
|
|
||||||
password = BuildConfig.LOGIN_PASSWORD
|
|
||||||
|
|
||||||
Intents.init()
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun checkFirstOpenLaunchesIntro() {
|
|
||||||
preferencesEditor.putString("url", "")
|
|
||||||
preferencesEditor.putString("password", "")
|
|
||||||
preferencesEditor.putString("login", "")
|
|
||||||
preferencesEditor.commit()
|
|
||||||
|
|
||||||
rule.launchActivity(intent)
|
|
||||||
|
|
||||||
intended(hasComponent(LoginActivity::class.java.name))
|
|
||||||
intended(hasComponent(HomeActivity::class.java.name), times(0))
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun checkNotFirstOpenLaunchesLogin() {
|
|
||||||
preferencesEditor.putString("url", url)
|
|
||||||
preferencesEditor.putString("password", password)
|
|
||||||
preferencesEditor.putString("login", username)
|
|
||||||
preferencesEditor.commit()
|
|
||||||
|
|
||||||
rule.launchActivity(intent)
|
|
||||||
|
|
||||||
intended(hasComponent(MainActivity::class.java.name))
|
|
||||||
intended(hasComponent(HomeActivity::class.java.name))
|
|
||||||
}
|
|
||||||
|
|
||||||
@After
|
|
||||||
fun releaseIntents() {
|
|
||||||
Intents.release()
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,29 +0,0 @@
|
|||||||
package bou.amine.apps.readerforselfossv2.android
|
|
||||||
|
|
||||||
import androidx.test.espresso.matcher.ViewMatchers
|
|
||||||
import android.view.View
|
|
||||||
import android.widget.EditText
|
|
||||||
import org.hamcrest.Description
|
|
||||||
import org.hamcrest.Matcher
|
|
||||||
import org.hamcrest.Matchers
|
|
||||||
import org.hamcrest.TypeSafeMatcher
|
|
||||||
|
|
||||||
fun isHintOrErrorEnabled(): Matcher<View> =
|
|
||||||
object : TypeSafeMatcher<View>() {
|
|
||||||
override fun describeTo(description: Description?) {
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun matchesSafely(item: View?): Boolean {
|
|
||||||
if (item !is EditText) {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
return item.error.isNotEmpty()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun withMenu(id: Int, titleId: Int): Matcher<View> =
|
|
||||||
Matchers.anyOf(
|
|
||||||
ViewMatchers.withId(id),
|
|
||||||
ViewMatchers.withText(titleId)
|
|
||||||
)
|
|
@ -9,10 +9,10 @@ import androidx.constraintlayout.widget.ConstraintLayout
|
|||||||
import bou.amine.apps.readerforselfossv2.android.databinding.ActivityAddSourceBinding
|
import bou.amine.apps.readerforselfossv2.android.databinding.ActivityAddSourceBinding
|
||||||
import bou.amine.apps.readerforselfossv2.android.themes.AppColors
|
import bou.amine.apps.readerforselfossv2.android.themes.AppColors
|
||||||
import bou.amine.apps.readerforselfossv2.android.themes.Toppings
|
import bou.amine.apps.readerforselfossv2.android.themes.Toppings
|
||||||
import bou.amine.apps.readerforselfossv2.android.utils.Config
|
|
||||||
import bou.amine.apps.readerforselfossv2.android.utils.isBaseUrlValid
|
import bou.amine.apps.readerforselfossv2.android.utils.isBaseUrlValid
|
||||||
import bou.amine.apps.readerforselfossv2.model.NetworkUnavailableException
|
import bou.amine.apps.readerforselfossv2.model.NetworkUnavailableException
|
||||||
import bou.amine.apps.readerforselfossv2.repository.Repository
|
import bou.amine.apps.readerforselfossv2.repository.Repository
|
||||||
|
import bou.amine.apps.readerforselfossv2.service.ApiDetailsService
|
||||||
import com.ftinc.scoop.Scoop
|
import com.ftinc.scoop.Scoop
|
||||||
import kotlinx.coroutines.CoroutineScope
|
import kotlinx.coroutines.CoroutineScope
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
@ -31,6 +31,7 @@ class AddSourceActivity : AppCompatActivity(), DIAware {
|
|||||||
|
|
||||||
override val di by closestDI()
|
override val di by closestDI()
|
||||||
private val repository : Repository by instance()
|
private val repository : Repository by instance()
|
||||||
|
private val apiDetailsService : ApiDetailsService by instance()
|
||||||
|
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
appColors = AppColors(this@AddSourceActivity)
|
appColors = AppColors(this@AddSourceActivity)
|
||||||
@ -81,9 +82,9 @@ class AddSourceActivity : AppCompatActivity(), DIAware {
|
|||||||
|
|
||||||
override fun onResume() {
|
override fun onResume() {
|
||||||
super.onResume()
|
super.onResume()
|
||||||
val config = Config()
|
|
||||||
|
|
||||||
if (config.baseUrl.isEmpty() || !config.baseUrl.isBaseUrlValid(this@AddSourceActivity)) {
|
val baseUrl = apiDetailsService.getBaseUrl()
|
||||||
|
if (baseUrl.isEmpty() || !baseUrl.isBaseUrlValid(this@AddSourceActivity)) {
|
||||||
mustLoginToAddSource()
|
mustLoginToAddSource()
|
||||||
} else {
|
} else {
|
||||||
handleSpoutsSpinner(binding.spoutsSpinner, binding.progress, binding.formContainer)
|
handleSpoutsSpinner(binding.spoutsSpinner, binding.progress, binding.formContainer)
|
||||||
|
@ -30,13 +30,12 @@ import bou.amine.apps.readerforselfossv2.android.databinding.ActivityHomeBinding
|
|||||||
import bou.amine.apps.readerforselfossv2.android.settings.SettingsActivity
|
import bou.amine.apps.readerforselfossv2.android.settings.SettingsActivity
|
||||||
import bou.amine.apps.readerforselfossv2.android.themes.AppColors
|
import bou.amine.apps.readerforselfossv2.android.themes.AppColors
|
||||||
import bou.amine.apps.readerforselfossv2.android.themes.Toppings
|
import bou.amine.apps.readerforselfossv2.android.themes.Toppings
|
||||||
import bou.amine.apps.readerforselfossv2.android.utils.Config
|
|
||||||
import bou.amine.apps.readerforselfossv2.android.utils.bottombar.maybeShow
|
import bou.amine.apps.readerforselfossv2.android.utils.bottombar.maybeShow
|
||||||
import bou.amine.apps.readerforselfossv2.android.utils.bottombar.removeBadge
|
import bou.amine.apps.readerforselfossv2.android.utils.bottombar.removeBadge
|
||||||
import bou.amine.apps.readerforselfossv2.android.utils.customtabs.CustomTabActivityHelper
|
import bou.amine.apps.readerforselfossv2.android.utils.customtabs.CustomTabActivityHelper
|
||||||
import bou.amine.apps.readerforselfossv2.dao.ACTION
|
|
||||||
import bou.amine.apps.readerforselfossv2.repository.Repository
|
import bou.amine.apps.readerforselfossv2.repository.Repository
|
||||||
import bou.amine.apps.readerforselfossv2.model.SelfossModel
|
import bou.amine.apps.readerforselfossv2.model.SelfossModel
|
||||||
|
import bou.amine.apps.readerforselfossv2.service.ApiDetailsService
|
||||||
import bou.amine.apps.readerforselfossv2.utils.*
|
import bou.amine.apps.readerforselfossv2.utils.*
|
||||||
import com.ashokvarma.bottomnavigation.BottomNavigationBar
|
import com.ashokvarma.bottomnavigation.BottomNavigationBar
|
||||||
import com.ashokvarma.bottomnavigation.BottomNavigationItem
|
import com.ashokvarma.bottomnavigation.BottomNavigationItem
|
||||||
@ -84,10 +83,7 @@ class HomeActivity : AppCompatActivity(), SearchView.OnQueryTextListener, DIAwar
|
|||||||
private var displayUnreadCount = false
|
private var displayUnreadCount = false
|
||||||
private var displayAllCount = false
|
private var displayAllCount = false
|
||||||
private var fullHeightCards: Boolean = false
|
private var fullHeightCards: Boolean = false
|
||||||
private var itemsNumber: Int = 200
|
|
||||||
private var elementsShown: ItemType = ItemType.UNREAD
|
private var elementsShown: ItemType = ItemType.UNREAD
|
||||||
private var displayAccountHeader: Boolean = false
|
|
||||||
private var infiniteScroll: Boolean = false
|
|
||||||
private var lastFetchDone: Boolean = false
|
private var lastFetchDone: Boolean = false
|
||||||
private var updateSources: Boolean = true
|
private var updateSources: Boolean = true
|
||||||
private var markOnScroll: Boolean = false
|
private var markOnScroll: Boolean = false
|
||||||
@ -114,8 +110,6 @@ class HomeActivity : AppCompatActivity(), SearchView.OnQueryTextListener, DIAwar
|
|||||||
|
|
||||||
private lateinit var tagsBadge: Map<Long, Int>
|
private lateinit var tagsBadge: Map<Long, Int>
|
||||||
|
|
||||||
private lateinit var config: Config
|
|
||||||
|
|
||||||
override val di by closestDI()
|
override val di by closestDI()
|
||||||
private val repository : Repository by instance()
|
private val repository : Repository by instance()
|
||||||
|
|
||||||
@ -128,7 +122,6 @@ class HomeActivity : AppCompatActivity(), SearchView.OnQueryTextListener, DIAwar
|
|||||||
|
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
appColors = AppColors(this@HomeActivity)
|
appColors = AppColors(this@HomeActivity)
|
||||||
config = Config()
|
|
||||||
|
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
binding = ActivityHomeBinding.inflate(layoutInflater)
|
binding = ActivityHomeBinding.inflate(layoutInflater)
|
||||||
@ -308,7 +301,7 @@ class HomeActivity : AppCompatActivity(), SearchView.OnQueryTextListener, DIAwar
|
|||||||
|
|
||||||
reloadLayoutManager()
|
reloadLayoutManager()
|
||||||
|
|
||||||
if (!infiniteScroll) {
|
if (!settings.getBoolean("infinite_loading", false)) {
|
||||||
binding.recyclerView.setHasFixedSize(true)
|
binding.recyclerView.setHasFixedSize(true)
|
||||||
} else {
|
} else {
|
||||||
handleInfiniteScroll()
|
handleInfiniteScroll()
|
||||||
@ -331,15 +324,14 @@ class HomeActivity : AppCompatActivity(), SearchView.OnQueryTextListener, DIAwar
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun handleSettings() {
|
private fun handleSettings() {
|
||||||
|
// TODO : clean this
|
||||||
internalBrowser = settings.getBoolean("prefer_internal_browser", true)
|
internalBrowser = settings.getBoolean("prefer_internal_browser", true)
|
||||||
articleViewer = settings.getBoolean("prefer_article_viewer", true)
|
articleViewer = settings.getBoolean("prefer_article_viewer", true)
|
||||||
shouldBeCardView = settings.getBoolean("card_view_active", false)
|
shouldBeCardView = settings.getBoolean("card_view_active", false)
|
||||||
displayUnreadCount = settings.getBoolean("display_unread_count", true)
|
displayUnreadCount = settings.getBoolean("display_unread_count", true)
|
||||||
displayAllCount = settings.getBoolean("display_other_count", false)
|
displayAllCount = settings.getBoolean("display_other_count", false)
|
||||||
fullHeightCards = settings.getBoolean("full_height_cards", false)
|
fullHeightCards = settings.getBoolean("full_height_cards", false)
|
||||||
itemsNumber = settings.getString("prefer_api_items_number", "200").toInt()
|
|
||||||
displayAccountHeader = settings.getBoolean("account_header_displaying", false)
|
|
||||||
infiniteScroll = settings.getBoolean("infinite_loading", false)
|
|
||||||
updateSources = settings.getBoolean("update_sources", true)
|
updateSources = settings.getBoolean("update_sources", true)
|
||||||
markOnScroll = settings.getBoolean("mark_on_scroll", false)
|
markOnScroll = settings.getBoolean("mark_on_scroll", false)
|
||||||
hiddenTags = if (settings.getString("hidden_tags", "").isNotEmpty()) {
|
hiddenTags = if (settings.getString("hidden_tags", "").isNotEmpty()) {
|
||||||
@ -409,7 +401,7 @@ class HomeActivity : AppCompatActivity(), SearchView.OnQueryTextListener, DIAwar
|
|||||||
|
|
||||||
binding.drawerContainer.addDrawerListener(drawerListener)
|
binding.drawerContainer.addDrawerListener(drawerListener)
|
||||||
|
|
||||||
displayAccountHeader =
|
val displayAccountHeader =
|
||||||
settings.getBoolean("account_header_displaying", false)
|
settings.getBoolean("account_header_displaying", false)
|
||||||
|
|
||||||
binding.mainDrawer.addStickyFooterItem(
|
binding.mainDrawer.addStickyFooterItem(
|
||||||
@ -418,7 +410,7 @@ class HomeActivity : AppCompatActivity(), SearchView.OnQueryTextListener, DIAwar
|
|||||||
iconRes = R.drawable.ic_bug_report_black_24dp
|
iconRes = R.drawable.ic_bug_report_black_24dp
|
||||||
isIconTinted = true
|
isIconTinted = true
|
||||||
onDrawerItemClickListener = { _, _, _ ->
|
onDrawerItemClickListener = { _, _, _ ->
|
||||||
val browserIntent = Intent(Intent.ACTION_VIEW, Uri.parse(Config.trackerUrl))
|
val browserIntent = Intent(Intent.ACTION_VIEW, Uri.parse(ApiDetailsService.trackerUrl))
|
||||||
startActivity(browserIntent)
|
startActivity(browserIntent)
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
@ -451,6 +443,7 @@ class HomeActivity : AppCompatActivity(), SearchView.OnQueryTextListener, DIAwar
|
|||||||
}
|
}
|
||||||
|
|
||||||
// TODO: refactor this.
|
// TODO: refactor this.
|
||||||
|
// TODO: use updateSources
|
||||||
private fun handleDrawerItems() {
|
private fun handleDrawerItems() {
|
||||||
tagsBadge = emptyMap()
|
tagsBadge = emptyMap()
|
||||||
fun handleDrawerData(maybeDrawerData: DrawerData?, loadedFromCache: Boolean = false) {
|
fun handleDrawerData(maybeDrawerData: DrawerData?, loadedFromCache: Boolean = false) {
|
||||||
@ -875,11 +868,10 @@ class HomeActivity : AppCompatActivity(), SearchView.OnQueryTextListener, DIAwar
|
|||||||
this,
|
this,
|
||||||
items,
|
items,
|
||||||
customTabActivityHelper,
|
customTabActivityHelper,
|
||||||
internalBrowser,
|
internalBrowser, // TODO remove and use from apidetailsservice
|
||||||
articleViewer,
|
articleViewer, // TODO remove and use from apidetailsservice
|
||||||
fullHeightCards,
|
fullHeightCards, // TODO remove and use from apidetailsservice
|
||||||
appColors,
|
appColors,
|
||||||
config
|
|
||||||
) {
|
) {
|
||||||
updateItems(it)
|
updateItems(it)
|
||||||
}
|
}
|
||||||
@ -889,10 +881,9 @@ class HomeActivity : AppCompatActivity(), SearchView.OnQueryTextListener, DIAwar
|
|||||||
this,
|
this,
|
||||||
items,
|
items,
|
||||||
customTabActivityHelper,
|
customTabActivityHelper,
|
||||||
internalBrowser,
|
internalBrowser, // TODO remove and use from apidetailsservice
|
||||||
articleViewer,
|
articleViewer, // TODO remove and use from apidetailsservice
|
||||||
appColors,
|
appColors,
|
||||||
config
|
|
||||||
) {
|
) {
|
||||||
updateItems(it)
|
updateItems(it)
|
||||||
}
|
}
|
||||||
@ -1047,7 +1038,12 @@ class HomeActivity : AppCompatActivity(), SearchView.OnQueryTextListener, DIAwar
|
|||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
R.id.action_disconnect -> {
|
R.id.action_disconnect -> {
|
||||||
return Config.logoutAndRedirect(this, this@HomeActivity)
|
val settings = Settings()
|
||||||
|
settings.clear()
|
||||||
|
val intent = Intent(this, LoginActivity::class.java)
|
||||||
|
this.startActivity(intent)
|
||||||
|
this@HomeActivity.finish()
|
||||||
|
return true
|
||||||
}
|
}
|
||||||
else -> return super.onOptionsItemSelected(item)
|
else -> return super.onOptionsItemSelected(item)
|
||||||
}
|
}
|
||||||
|
@ -14,12 +14,11 @@ import androidx.lifecycle.ProcessLifecycleOwner
|
|||||||
import androidx.multidex.MultiDexApplication
|
import androidx.multidex.MultiDexApplication
|
||||||
import androidx.preference.PreferenceManager
|
import androidx.preference.PreferenceManager
|
||||||
import bou.amine.apps.readerforselfossv2.DI.networkModule
|
import bou.amine.apps.readerforselfossv2.DI.networkModule
|
||||||
import bou.amine.apps.readerforselfossv2.android.utils.Config
|
|
||||||
import bou.amine.apps.readerforselfossv2.android.utils.glide.loadMaybeBasicAuth
|
|
||||||
import bou.amine.apps.readerforselfossv2.android.viewmodel.AppViewModel
|
import bou.amine.apps.readerforselfossv2.android.viewmodel.AppViewModel
|
||||||
import bou.amine.apps.readerforselfossv2.dao.DriverFactory
|
import bou.amine.apps.readerforselfossv2.dao.DriverFactory
|
||||||
import bou.amine.apps.readerforselfossv2.dao.ReaderForSelfossDB
|
import bou.amine.apps.readerforselfossv2.dao.ReaderForSelfossDB
|
||||||
import bou.amine.apps.readerforselfossv2.repository.Repository
|
import bou.amine.apps.readerforselfossv2.repository.Repository
|
||||||
|
import bou.amine.apps.readerforselfossv2.service.ApiDetailsService
|
||||||
import com.bumptech.glide.Glide
|
import com.bumptech.glide.Glide
|
||||||
import com.bumptech.glide.request.RequestOptions
|
import com.bumptech.glide.request.RequestOptions
|
||||||
import com.ftinc.scoop.Scoop
|
import com.ftinc.scoop.Scoop
|
||||||
@ -49,13 +48,11 @@ class MyApp : MultiDexApplication(), DIAware {
|
|||||||
private val viewModel: AppViewModel by instance()
|
private val viewModel: AppViewModel by instance()
|
||||||
private val connectivityStatus: ConnectivityStatus by instance()
|
private val connectivityStatus: ConnectivityStatus by instance()
|
||||||
private val driverFactory: DriverFactory by instance()
|
private val driverFactory: DriverFactory by instance()
|
||||||
private lateinit var config: Config
|
|
||||||
private lateinit var settings : Settings
|
private lateinit var settings : Settings
|
||||||
|
|
||||||
override fun onCreate() {
|
override fun onCreate() {
|
||||||
super.onCreate()
|
super.onCreate()
|
||||||
Napier.base(DebugAntilog())
|
Napier.base(DebugAntilog())
|
||||||
config = Config()
|
|
||||||
settings = Settings()
|
settings = Settings()
|
||||||
|
|
||||||
initDrawerImageLoader()
|
initDrawerImageLoader()
|
||||||
@ -93,11 +90,11 @@ class MyApp : MultiDexApplication(), DIAware {
|
|||||||
|
|
||||||
val name = getString(R.string.notification_channel_sync)
|
val name = getString(R.string.notification_channel_sync)
|
||||||
val importance = NotificationManager.IMPORTANCE_LOW
|
val importance = NotificationManager.IMPORTANCE_LOW
|
||||||
val mChannel = NotificationChannel(Config.syncChannelId, name, importance)
|
val mChannel = NotificationChannel(ApiDetailsService.syncChannelId, name, importance)
|
||||||
|
|
||||||
val newItemsChannelname = getString(R.string.new_items_channel_sync)
|
val newItemsChannelname = getString(R.string.new_items_channel_sync)
|
||||||
val newItemsChannelimportance = NotificationManager.IMPORTANCE_DEFAULT
|
val newItemsChannelimportance = NotificationManager.IMPORTANCE_DEFAULT
|
||||||
val newItemsChannelmChannel = NotificationChannel(Config.newItemsChannelId, newItemsChannelname, newItemsChannelimportance)
|
val newItemsChannelmChannel = NotificationChannel(ApiDetailsService.newItemsChannelId, newItemsChannelname, newItemsChannelimportance)
|
||||||
|
|
||||||
notificationManager.createNotificationChannel(mChannel)
|
notificationManager.createNotificationChannel(mChannel)
|
||||||
notificationManager.createNotificationChannel(newItemsChannelmChannel)
|
notificationManager.createNotificationChannel(newItemsChannelmChannel)
|
||||||
@ -108,7 +105,7 @@ class MyApp : MultiDexApplication(), DIAware {
|
|||||||
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)
|
||||||
.loadMaybeBasicAuth(config, uri.toString())
|
.load(uri.toString())
|
||||||
.apply(RequestOptions.fitCenterTransform().placeholder(placeholder))
|
.apply(RequestOptions.fitCenterTransform().placeholder(placeholder))
|
||||||
.into(imageView)
|
.into(imageView)
|
||||||
}
|
}
|
||||||
|
@ -38,7 +38,6 @@ class ItemCardAdapter(
|
|||||||
private val articleViewer: Boolean,
|
private val articleViewer: Boolean,
|
||||||
private val fullHeightCards: Boolean,
|
private val fullHeightCards: Boolean,
|
||||||
override val appColors: AppColors,
|
override val appColors: AppColors,
|
||||||
override val config: Config,
|
|
||||||
override val updateItems: (ArrayList<SelfossModel.Item>) -> Unit
|
override val updateItems: (ArrayList<SelfossModel.Item>) -> Unit
|
||||||
) : ItemsAdapter<ItemCardAdapter.ViewHolder>() {
|
) : ItemsAdapter<ItemCardAdapter.ViewHolder>() {
|
||||||
private val c: Context = app.baseContext
|
private val c: Context = app.baseContext
|
||||||
@ -78,7 +77,7 @@ class ItemCardAdapter(
|
|||||||
binding.itemImage.setImageDrawable(null)
|
binding.itemImage.setImageDrawable(null)
|
||||||
} else {
|
} else {
|
||||||
binding.itemImage.visibility = View.VISIBLE
|
binding.itemImage.visibility = View.VISIBLE
|
||||||
c.bitmapCenterCrop(config, itm.getThumbnail(repository.baseUrl), binding.itemImage)
|
c.bitmapCenterCrop(itm.getThumbnail(repository.baseUrl), binding.itemImage)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (itm.getIcon(repository.baseUrl).isEmpty()) {
|
if (itm.getIcon(repository.baseUrl).isEmpty()) {
|
||||||
@ -91,7 +90,7 @@ class ItemCardAdapter(
|
|||||||
.build(itm.title.getHtmlDecoded().toTextDrawableString(), color)
|
.build(itm.title.getHtmlDecoded().toTextDrawableString(), color)
|
||||||
binding.sourceImage.setImageDrawable(drawable)
|
binding.sourceImage.setImageDrawable(drawable)
|
||||||
} else {
|
} else {
|
||||||
c.circularBitmapDrawable(config, itm.getIcon(repository.baseUrl), binding.sourceImage)
|
c.circularBitmapDrawable(itm.getIcon(repository.baseUrl), binding.sourceImage)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -30,7 +30,6 @@ class ItemListAdapter(
|
|||||||
private val internalBrowser: Boolean,
|
private val internalBrowser: Boolean,
|
||||||
private val articleViewer: Boolean,
|
private val articleViewer: Boolean,
|
||||||
override val appColors: AppColors,
|
override val appColors: AppColors,
|
||||||
override val config: Config,
|
|
||||||
override val updateItems: (ArrayList<SelfossModel.Item>) -> Unit
|
override val updateItems: (ArrayList<SelfossModel.Item>) -> Unit
|
||||||
) : ItemsAdapter<ItemListAdapter.ViewHolder>() {
|
) : ItemsAdapter<ItemListAdapter.ViewHolder>() {
|
||||||
private val generator: ColorGenerator = ColorGenerator.MATERIAL
|
private val generator: ColorGenerator = ColorGenerator.MATERIAL
|
||||||
@ -69,10 +68,10 @@ class ItemListAdapter(
|
|||||||
|
|
||||||
binding.itemImage.setImageDrawable(drawable)
|
binding.itemImage.setImageDrawable(drawable)
|
||||||
} else {
|
} else {
|
||||||
c.circularBitmapDrawable(config, itm.getIcon(repository.baseUrl), binding.itemImage)
|
c.circularBitmapDrawable(itm.getIcon(repository.baseUrl), binding.itemImage)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
c.bitmapCenterCrop(config, itm.getThumbnail(repository.baseUrl), binding.itemImage)
|
c.bitmapCenterCrop(itm.getThumbnail(repository.baseUrl), binding.itemImage)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -6,7 +6,6 @@ import android.widget.TextView
|
|||||||
import androidx.recyclerview.widget.RecyclerView
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
import bou.amine.apps.readerforselfossv2.android.R
|
import bou.amine.apps.readerforselfossv2.android.R
|
||||||
import bou.amine.apps.readerforselfossv2.android.themes.AppColors
|
import bou.amine.apps.readerforselfossv2.android.themes.AppColors
|
||||||
import bou.amine.apps.readerforselfossv2.android.utils.Config
|
|
||||||
import bou.amine.apps.readerforselfossv2.repository.Repository
|
import bou.amine.apps.readerforselfossv2.repository.Repository
|
||||||
import bou.amine.apps.readerforselfossv2.model.SelfossModel
|
import bou.amine.apps.readerforselfossv2.model.SelfossModel
|
||||||
import bou.amine.apps.readerforselfossv2.utils.ItemType
|
import bou.amine.apps.readerforselfossv2.utils.ItemType
|
||||||
@ -21,7 +20,6 @@ abstract class ItemsAdapter<VH : RecyclerView.ViewHolder?> : RecyclerView.Adapte
|
|||||||
abstract val repository: Repository
|
abstract val repository: Repository
|
||||||
abstract val app: Activity
|
abstract val app: Activity
|
||||||
abstract val appColors: AppColors
|
abstract val appColors: AppColors
|
||||||
abstract val config: Config
|
|
||||||
abstract val updateItems: (ArrayList<SelfossModel.Item>) -> Unit
|
abstract val updateItems: (ArrayList<SelfossModel.Item>) -> Unit
|
||||||
|
|
||||||
fun updateAllItems(items: ArrayList<SelfossModel.Item>) {
|
fun updateAllItems(items: ArrayList<SelfossModel.Item>) {
|
||||||
|
@ -11,7 +11,6 @@ import androidx.recyclerview.widget.RecyclerView
|
|||||||
import bou.amine.apps.readerforselfossv2.android.R
|
import bou.amine.apps.readerforselfossv2.android.R
|
||||||
import bou.amine.apps.readerforselfossv2.android.databinding.SourceListItemBinding
|
import bou.amine.apps.readerforselfossv2.android.databinding.SourceListItemBinding
|
||||||
import bou.amine.apps.readerforselfossv2.android.model.toTextDrawableString
|
import bou.amine.apps.readerforselfossv2.android.model.toTextDrawableString
|
||||||
import bou.amine.apps.readerforselfossv2.android.utils.Config
|
|
||||||
import bou.amine.apps.readerforselfossv2.android.utils.glide.circularBitmapDrawable
|
import bou.amine.apps.readerforselfossv2.android.utils.glide.circularBitmapDrawable
|
||||||
import bou.amine.apps.readerforselfossv2.repository.Repository
|
import bou.amine.apps.readerforselfossv2.repository.Repository
|
||||||
import bou.amine.apps.readerforselfossv2.model.SelfossModel
|
import bou.amine.apps.readerforselfossv2.model.SelfossModel
|
||||||
@ -33,7 +32,6 @@ class SourcesListAdapter(
|
|||||||
) : RecyclerView.Adapter<SourcesListAdapter.ViewHolder>(), DIAware {
|
) : RecyclerView.Adapter<SourcesListAdapter.ViewHolder>(), DIAware {
|
||||||
private val c: Context = app.baseContext
|
private val c: Context = app.baseContext
|
||||||
private val generator: ColorGenerator = ColorGenerator.MATERIAL
|
private val generator: ColorGenerator = ColorGenerator.MATERIAL
|
||||||
private lateinit var config: Config
|
|
||||||
private lateinit var binding: SourceListItemBinding
|
private lateinit var binding: SourceListItemBinding
|
||||||
|
|
||||||
override val di: DI by closestDI(app)
|
override val di: DI by closestDI(app)
|
||||||
@ -46,7 +44,6 @@ class SourcesListAdapter(
|
|||||||
|
|
||||||
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
|
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
|
||||||
val itm = items[position]
|
val itm = items[position]
|
||||||
config = Config()
|
|
||||||
|
|
||||||
if (itm.getIcon(repository.baseUrl).isEmpty()) {
|
if (itm.getIcon(repository.baseUrl).isEmpty()) {
|
||||||
val color = generator.getColor(itm.title.getHtmlDecoded())
|
val color = generator.getColor(itm.title.getHtmlDecoded())
|
||||||
@ -58,7 +55,7 @@ class SourcesListAdapter(
|
|||||||
.build(itm.title.getHtmlDecoded().toTextDrawableString(), color)
|
.build(itm.title.getHtmlDecoded().toTextDrawableString(), color)
|
||||||
binding.itemImage.setImageDrawable(drawable)
|
binding.itemImage.setImageDrawable(drawable)
|
||||||
} else {
|
} else {
|
||||||
c.circularBitmapDrawable(config, itm.getIcon(repository.baseUrl), binding.itemImage)
|
c.circularBitmapDrawable(itm.getIcon(repository.baseUrl), binding.itemImage)
|
||||||
}
|
}
|
||||||
|
|
||||||
binding.sourceTitle.text = itm.title.getHtmlDecoded()
|
binding.sourceTitle.text = itm.title.getHtmlDecoded()
|
||||||
|
@ -14,11 +14,10 @@ import bou.amine.apps.readerforselfossv2.android.MainActivity
|
|||||||
import bou.amine.apps.readerforselfossv2.android.MyApp
|
import bou.amine.apps.readerforselfossv2.android.MyApp
|
||||||
import bou.amine.apps.readerforselfossv2.android.R
|
import bou.amine.apps.readerforselfossv2.android.R
|
||||||
import bou.amine.apps.readerforselfossv2.android.model.preloadImages
|
import bou.amine.apps.readerforselfossv2.android.model.preloadImages
|
||||||
import bou.amine.apps.readerforselfossv2.android.utils.Config
|
|
||||||
import bou.amine.apps.readerforselfossv2.android.utils.network.isNetworkAccessible
|
import bou.amine.apps.readerforselfossv2.android.utils.network.isNetworkAccessible
|
||||||
import bou.amine.apps.readerforselfossv2.dao.ACTION
|
|
||||||
import bou.amine.apps.readerforselfossv2.model.SelfossModel
|
import bou.amine.apps.readerforselfossv2.model.SelfossModel
|
||||||
import bou.amine.apps.readerforselfossv2.repository.Repository
|
import bou.amine.apps.readerforselfossv2.repository.Repository
|
||||||
|
import bou.amine.apps.readerforselfossv2.service.ApiDetailsService
|
||||||
import com.russhwolf.settings.Settings
|
import com.russhwolf.settings.Settings
|
||||||
import kotlinx.coroutines.CoroutineScope
|
import kotlinx.coroutines.CoroutineScope
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
@ -27,7 +26,6 @@ import org.kodein.di.DIAware
|
|||||||
import org.kodein.di.instance
|
import org.kodein.di.instance
|
||||||
import java.util.*
|
import java.util.*
|
||||||
import kotlin.concurrent.schedule
|
import kotlin.concurrent.schedule
|
||||||
import kotlin.concurrent.thread
|
|
||||||
|
|
||||||
class LoadingWorker(val context: Context, params: WorkerParameters) : Worker(context, params), DIAware {
|
class LoadingWorker(val context: Context, params: WorkerParameters) : Worker(context, params), DIAware {
|
||||||
|
|
||||||
@ -44,12 +42,12 @@ override fun doWork(): Result {
|
|||||||
applicationContext.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
|
applicationContext.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
|
||||||
|
|
||||||
val notification =
|
val notification =
|
||||||
NotificationCompat.Builder(applicationContext, Config.syncChannelId)
|
NotificationCompat.Builder(applicationContext, ApiDetailsService.syncChannelId)
|
||||||
.setContentTitle(context.getString(R.string.loading_notification_title))
|
.setContentTitle(context.getString(R.string.loading_notification_title))
|
||||||
.setContentText(context.getString(R.string.loading_notification_text))
|
.setContentText(context.getString(R.string.loading_notification_text))
|
||||||
.setOngoing(true)
|
.setOngoing(true)
|
||||||
.setPriority(PRIORITY_LOW)
|
.setPriority(PRIORITY_LOW)
|
||||||
.setChannelId(Config.syncChannelId)
|
.setChannelId(ApiDetailsService.syncChannelId)
|
||||||
.setSmallIcon(R.drawable.ic_stat_cloud_download_black_24dp)
|
.setSmallIcon(R.drawable.ic_stat_cloud_download_black_24dp)
|
||||||
|
|
||||||
notificationManager.notify(1, notification.build())
|
notificationManager.notify(1, notification.build())
|
||||||
@ -89,7 +87,7 @@ override fun doWork(): Result {
|
|||||||
val pendingIntent: PendingIntent = PendingIntent.getActivity(context, 0, intent, pflags)
|
val pendingIntent: PendingIntent = PendingIntent.getActivity(context, 0, intent, pflags)
|
||||||
|
|
||||||
val newItemsNotification =
|
val newItemsNotification =
|
||||||
NotificationCompat.Builder(applicationContext, Config.newItemsChannelId)
|
NotificationCompat.Builder(applicationContext, ApiDetailsService.newItemsChannelId)
|
||||||
.setContentTitle(context.getString(R.string.new_items_notification_title))
|
.setContentTitle(context.getString(R.string.new_items_notification_title))
|
||||||
.setContentText(
|
.setContentText(
|
||||||
context.getString(
|
context.getString(
|
||||||
@ -98,7 +96,7 @@ override fun doWork(): Result {
|
|||||||
)
|
)
|
||||||
)
|
)
|
||||||
.setPriority(PRIORITY_DEFAULT)
|
.setPriority(PRIORITY_DEFAULT)
|
||||||
.setChannelId(Config.newItemsChannelId)
|
.setChannelId(ApiDetailsService.newItemsChannelId)
|
||||||
.setContentIntent(pendingIntent)
|
.setContentIntent(pendingIntent)
|
||||||
.setAutoCancel(true)
|
.setAutoCancel(true)
|
||||||
.setSmallIcon(R.drawable.ic_tab_fiber_new_black_24dp)
|
.setSmallIcon(R.drawable.ic_tab_fiber_new_black_24dp)
|
||||||
|
@ -29,7 +29,6 @@ import bou.amine.apps.readerforselfossv2.android.themes.AppColors
|
|||||||
import bou.amine.apps.readerforselfossv2.android.utils.*
|
import bou.amine.apps.readerforselfossv2.android.utils.*
|
||||||
import bou.amine.apps.readerforselfossv2.android.utils.customtabs.CustomTabActivityHelper
|
import bou.amine.apps.readerforselfossv2.android.utils.customtabs.CustomTabActivityHelper
|
||||||
import bou.amine.apps.readerforselfossv2.android.utils.glide.getBitmapInputStream
|
import bou.amine.apps.readerforselfossv2.android.utils.glide.getBitmapInputStream
|
||||||
import bou.amine.apps.readerforselfossv2.android.utils.glide.loadMaybeBasicAuth
|
|
||||||
import bou.amine.apps.readerforselfossv2.repository.Repository
|
import bou.amine.apps.readerforselfossv2.repository.Repository
|
||||||
import bou.amine.apps.readerforselfossv2.model.SelfossModel
|
import bou.amine.apps.readerforselfossv2.model.SelfossModel
|
||||||
import bou.amine.apps.readerforselfossv2.utils.getHtmlDecoded
|
import bou.amine.apps.readerforselfossv2.utils.getHtmlDecoded
|
||||||
@ -70,7 +69,6 @@ class ArticleFragment : Fragment(), DIAware {
|
|||||||
private lateinit var fab: FloatingActionButton
|
private lateinit var fab: FloatingActionButton
|
||||||
private lateinit var appColors: AppColors
|
private lateinit var appColors: AppColors
|
||||||
private lateinit var textAlignment: String
|
private lateinit var textAlignment: String
|
||||||
private lateinit var config: Config
|
|
||||||
private var _binding: FragmentArticleBinding? = null
|
private var _binding: FragmentArticleBinding? = null
|
||||||
private val binding get() = _binding!!
|
private val binding get() = _binding!!
|
||||||
|
|
||||||
@ -93,7 +91,6 @@ class ArticleFragment : Fragment(), DIAware {
|
|||||||
|
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
appColors = AppColors(requireActivity())
|
appColors = AppColors(requireActivity())
|
||||||
config = Config()
|
|
||||||
|
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
|
|
||||||
@ -213,7 +210,7 @@ class ArticleFragment : Fragment(), DIAware {
|
|||||||
Glide
|
Glide
|
||||||
.with(requireContext())
|
.with(requireContext())
|
||||||
.asBitmap()
|
.asBitmap()
|
||||||
.loadMaybeBasicAuth(config, contentImage)
|
.load(contentImage)
|
||||||
.apply(RequestOptions.fitCenterTransform())
|
.apply(RequestOptions.fitCenterTransform())
|
||||||
.into(binding.imageView)
|
.into(binding.imageView)
|
||||||
} else {
|
} else {
|
||||||
@ -307,8 +304,7 @@ class ArticleFragment : Fragment(), DIAware {
|
|||||||
Glide
|
Glide
|
||||||
.with(requireContext())
|
.with(requireContext())
|
||||||
.asBitmap()
|
.asBitmap()
|
||||||
.loadMaybeBasicAuth(
|
.load(
|
||||||
config,
|
|
||||||
response.body()!!.lead_image_url.orEmpty()
|
response.body()!!.lead_image_url.orEmpty()
|
||||||
)
|
)
|
||||||
.apply(RequestOptions.fitCenterTransform())
|
.apply(RequestOptions.fitCenterTransform())
|
||||||
|
@ -15,7 +15,7 @@ import androidx.preference.PreferenceFragmentCompat
|
|||||||
import bou.amine.apps.readerforselfossv2.android.R
|
import bou.amine.apps.readerforselfossv2.android.R
|
||||||
import bou.amine.apps.readerforselfossv2.android.databinding.ActivitySettingsBinding
|
import bou.amine.apps.readerforselfossv2.android.databinding.ActivitySettingsBinding
|
||||||
import bou.amine.apps.readerforselfossv2.android.themes.Toppings
|
import bou.amine.apps.readerforselfossv2.android.themes.Toppings
|
||||||
import bou.amine.apps.readerforselfossv2.android.utils.Config
|
import bou.amine.apps.readerforselfossv2.service.ApiDetailsService
|
||||||
import com.ftinc.scoop.Scoop
|
import com.ftinc.scoop.Scoop
|
||||||
import com.russhwolf.settings.Settings
|
import com.russhwolf.settings.Settings
|
||||||
import java.lang.NumberFormatException
|
import java.lang.NumberFormatException
|
||||||
@ -196,17 +196,17 @@ class SettingsActivity : AppCompatActivity(),
|
|||||||
setPreferencesFromResource(R.xml.pref_links, rootKey)
|
setPreferencesFromResource(R.xml.pref_links, rootKey)
|
||||||
|
|
||||||
preferenceManager.findPreference<Preference>("trackerLink")?.onPreferenceClickListener = Preference.OnPreferenceClickListener {
|
preferenceManager.findPreference<Preference>("trackerLink")?.onPreferenceClickListener = Preference.OnPreferenceClickListener {
|
||||||
openUrl(Uri.parse(Config.trackerUrl))
|
openUrl(Uri.parse(ApiDetailsService.trackerUrl))
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
|
|
||||||
preferenceManager.findPreference<Preference>("sourceLink")?.onPreferenceClickListener = Preference.OnPreferenceClickListener {
|
preferenceManager.findPreference<Preference>("sourceLink")?.onPreferenceClickListener = Preference.OnPreferenceClickListener {
|
||||||
openUrl(Uri.parse(Config.sourceUrl))
|
openUrl(Uri.parse(ApiDetailsService.sourceUrl))
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
|
|
||||||
preferenceManager.findPreference<Preference>("translation")?.onPreferenceClickListener = Preference.OnPreferenceClickListener {
|
preferenceManager.findPreference<Preference>("translation")?.onPreferenceClickListener = Preference.OnPreferenceClickListener {
|
||||||
openUrl(Uri.parse(Config.translationUrl))
|
openUrl(Uri.parse(ApiDetailsService.translationUrl))
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,62 +0,0 @@
|
|||||||
package bou.amine.apps.readerforselfossv2.android.utils
|
|
||||||
|
|
||||||
import android.app.Activity
|
|
||||||
import android.content.Context
|
|
||||||
import android.content.Intent
|
|
||||||
import bou.amine.apps.readerforselfossv2.android.LoginActivity
|
|
||||||
import com.russhwolf.settings.Settings
|
|
||||||
|
|
||||||
class Config {
|
|
||||||
|
|
||||||
val settings = Settings()
|
|
||||||
|
|
||||||
val baseUrl: String
|
|
||||||
get() = settings.getString("url", "")
|
|
||||||
|
|
||||||
val userLogin: String
|
|
||||||
get() = settings.getString("login", "")
|
|
||||||
|
|
||||||
val userPassword: String
|
|
||||||
get() = settings.getString("password", "")
|
|
||||||
|
|
||||||
val httpUserLogin: String
|
|
||||||
get() = settings.getString("httpUserName", "")
|
|
||||||
|
|
||||||
val httpUserPassword: String
|
|
||||||
get() = settings.getString("httpPassword", "")
|
|
||||||
|
|
||||||
companion object {
|
|
||||||
const val settingsName = "paramsselfoss"
|
|
||||||
|
|
||||||
const val feedbackEmail = "aminecmi@pm.me.com"
|
|
||||||
|
|
||||||
const val translationUrl = "https://crwd.in/readerforselfoss"
|
|
||||||
|
|
||||||
const val sourceUrl = "https://gitea.amine-louveau.fr/Louvorg/ReaderForSelfoss-multiplatform"
|
|
||||||
|
|
||||||
const val trackerUrl = "https://gitea.amine-louveau.fr/Louvorg/ReaderForSelfoss-multiplatform/issues"
|
|
||||||
|
|
||||||
const val syncChannelId = "sync-channel-id"
|
|
||||||
|
|
||||||
const val newItemsChannelId = "new-items-channel-id"
|
|
||||||
|
|
||||||
var apiVersion = 0
|
|
||||||
|
|
||||||
/* Execute logout and clear all settings to default */
|
|
||||||
fun logoutAndRedirect(
|
|
||||||
c: Context,
|
|
||||||
callingActivity: Activity,
|
|
||||||
baseUrlFail: Boolean = false
|
|
||||||
): Boolean {
|
|
||||||
val settings = Settings()
|
|
||||||
settings.clear()
|
|
||||||
val intent = Intent(c, LoginActivity::class.java)
|
|
||||||
if (baseUrlFail) {
|
|
||||||
intent.putExtra("baseUrlFail", baseUrlFail)
|
|
||||||
}
|
|
||||||
c.startActivity(intent)
|
|
||||||
callingActivity.finish()
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -2,33 +2,26 @@ package bou.amine.apps.readerforselfossv2.android.utils.glide
|
|||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.graphics.Bitmap
|
import android.graphics.Bitmap
|
||||||
import android.graphics.drawable.Drawable
|
|
||||||
import android.util.Base64
|
|
||||||
import androidx.core.graphics.drawable.RoundedBitmapDrawableFactory
|
|
||||||
import android.widget.ImageView
|
import android.widget.ImageView
|
||||||
import bou.amine.apps.readerforselfossv2.android.utils.Config
|
import androidx.core.graphics.drawable.RoundedBitmapDrawableFactory
|
||||||
import com.bumptech.glide.Glide
|
import com.bumptech.glide.Glide
|
||||||
import com.bumptech.glide.RequestBuilder
|
|
||||||
import com.bumptech.glide.RequestManager
|
|
||||||
import com.bumptech.glide.load.model.GlideUrl
|
|
||||||
import com.bumptech.glide.load.model.LazyHeaders
|
|
||||||
import com.bumptech.glide.request.RequestOptions
|
import com.bumptech.glide.request.RequestOptions
|
||||||
import com.bumptech.glide.request.target.BitmapImageViewTarget
|
import com.bumptech.glide.request.target.BitmapImageViewTarget
|
||||||
import java.io.ByteArrayInputStream
|
import java.io.ByteArrayInputStream
|
||||||
import java.io.ByteArrayOutputStream
|
import java.io.ByteArrayOutputStream
|
||||||
import java.io.InputStream
|
import java.io.InputStream
|
||||||
|
|
||||||
fun Context.bitmapCenterCrop(config: Config, url: String, iv: ImageView) =
|
fun Context.bitmapCenterCrop(url: String, iv: ImageView) =
|
||||||
Glide.with(this)
|
Glide.with(this)
|
||||||
.asBitmap()
|
.asBitmap()
|
||||||
.loadMaybeBasicAuth(config, url)
|
.load(url)
|
||||||
.apply(RequestOptions.centerCropTransform())
|
.apply(RequestOptions.centerCropTransform())
|
||||||
.into(iv)
|
.into(iv)
|
||||||
|
|
||||||
fun Context.circularBitmapDrawable(config: Config, url: String, iv: ImageView) =
|
fun Context.circularBitmapDrawable(url: String, iv: ImageView) =
|
||||||
Glide.with(this)
|
Glide.with(this)
|
||||||
.asBitmap()
|
.asBitmap()
|
||||||
.loadMaybeBasicAuth(config, 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?) {
|
||||||
@ -41,26 +34,6 @@ fun Context.circularBitmapDrawable(config: Config, url: String, iv: ImageView) =
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
fun RequestBuilder<Bitmap>.loadMaybeBasicAuth(config: Config, url: String): RequestBuilder<Bitmap> {
|
|
||||||
val builder: LazyHeaders.Builder = LazyHeaders.Builder()
|
|
||||||
if (config.httpUserLogin.isNotEmpty() || config.httpUserPassword.isNotEmpty()) {
|
|
||||||
val basicAuth = "Basic " + Base64.encodeToString("${config.httpUserLogin}:${config.httpUserPassword}".toByteArray(), Base64.NO_WRAP)
|
|
||||||
builder.addHeader("Authorization", basicAuth)
|
|
||||||
}
|
|
||||||
val glideUrl = GlideUrl(url, builder.build())
|
|
||||||
return this.load(glideUrl)
|
|
||||||
}
|
|
||||||
|
|
||||||
fun RequestManager.loadMaybeBasicAuth(config: Config, url: String): RequestBuilder<Drawable> {
|
|
||||||
val builder: LazyHeaders.Builder = LazyHeaders.Builder()
|
|
||||||
if (config.httpUserLogin.isNotEmpty() || config.httpUserPassword.isNotEmpty()) {
|
|
||||||
val basicAuth = "Basic " + Base64.encodeToString("${config.httpUserLogin}:${config.httpUserPassword}".toByteArray(), Base64.NO_WRAP)
|
|
||||||
builder.addHeader("Authorization", basicAuth)
|
|
||||||
}
|
|
||||||
val glideUrl = GlideUrl(url, builder.build())
|
|
||||||
return this.load(glideUrl)
|
|
||||||
}
|
|
||||||
|
|
||||||
fun getBitmapInputStream(bitmap:Bitmap,compressFormat: Bitmap.CompressFormat): InputStream {
|
fun getBitmapInputStream(bitmap:Bitmap,compressFormat: Bitmap.CompressFormat): InputStream {
|
||||||
val byteArrayOutputStream = ByteArrayOutputStream()
|
val byteArrayOutputStream = ByteArrayOutputStream()
|
||||||
bitmap.compress(compressFormat, 80, byteArrayOutputStream)
|
bitmap.compress(compressFormat, 80, byteArrayOutputStream)
|
||||||
|
@ -1,18 +1,21 @@
|
|||||||
package bou.amine.apps.readerforselfossv2.utils
|
package bou.amine.apps.readerforselfossv2.utils
|
||||||
|
|
||||||
import android.text.format.DateUtils
|
import android.text.format.DateUtils
|
||||||
|
import bou.amine.apps.readerforselfossv2.service.ApiDetailsService
|
||||||
import java.time.Instant
|
import java.time.Instant
|
||||||
import java.time.LocalDateTime
|
import java.time.LocalDateTime
|
||||||
import java.time.OffsetDateTime
|
import java.time.OffsetDateTime
|
||||||
import java.time.ZoneOffset
|
import java.time.ZoneOffset
|
||||||
import java.time.format.DateTimeFormatter
|
import java.time.format.DateTimeFormatter
|
||||||
|
|
||||||
actual class DateUtils actual constructor(private val apiMajorVersion: Int) {
|
actual class DateUtils actual constructor(apiDetailsService: ApiDetailsService) {
|
||||||
|
val ads: ApiDetailsService = apiDetailsService // TODO: why is this needed now ?
|
||||||
|
|
||||||
actual fun parseDate(dateString: String): Long {
|
actual fun parseDate(dateString: String): Long {
|
||||||
|
|
||||||
val FORMATTERV1 = "yyyy-MM-dd HH:mm:ss"
|
val FORMATTERV1 = "yyyy-MM-dd HH:mm:ss"
|
||||||
|
|
||||||
return if (apiMajorVersion >= 4) {
|
return if (ads.getApiVersion() >= 4) {
|
||||||
OffsetDateTime.parse(dateString).toInstant().toEpochMilli()
|
OffsetDateTime.parse(dateString).toInstant().toEpochMilli()
|
||||||
} else {
|
} else {
|
||||||
LocalDateTime.parse(dateString, DateTimeFormatter.ofPattern(FORMATTERV1)).toInstant(
|
LocalDateTime.parse(dateString, DateTimeFormatter.ofPattern(FORMATTERV1)).toInstant(
|
||||||
|
@ -2,13 +2,12 @@ package bou.amine.apps.readerforselfossv2.DI
|
|||||||
|
|
||||||
import bou.amine.apps.readerforselfossv2.rest.SelfossApi
|
import bou.amine.apps.readerforselfossv2.rest.SelfossApi
|
||||||
import bou.amine.apps.readerforselfossv2.service.ApiDetailsService
|
import bou.amine.apps.readerforselfossv2.service.ApiDetailsService
|
||||||
import bou.amine.apps.readerforselfossv2.service.ApiDetailsServiceImpl
|
|
||||||
import org.kodein.di.DI
|
import org.kodein.di.DI
|
||||||
import org.kodein.di.bind
|
import org.kodein.di.bind
|
||||||
import org.kodein.di.instance
|
import org.kodein.di.instance
|
||||||
import org.kodein.di.singleton
|
import org.kodein.di.singleton
|
||||||
|
|
||||||
val networkModule by DI.Module {
|
val networkModule by DI.Module {
|
||||||
bind<ApiDetailsService>() with singleton { ApiDetailsServiceImpl() }
|
bind<ApiDetailsService>() with singleton { ApiDetailsService() }
|
||||||
bind<SelfossApi>() with singleton { SelfossApi(instance()) }
|
bind<SelfossApi>() with singleton { SelfossApi(instance()) }
|
||||||
}
|
}
|
@ -32,7 +32,6 @@ class Repository(private val api: SelfossApi, private val apiDetails: ApiDetails
|
|||||||
var itemsCaching = settings.getBoolean("items_caching", false)
|
var itemsCaching = settings.getBoolean("items_caching", false)
|
||||||
var offlineOverride = false
|
var offlineOverride = false
|
||||||
|
|
||||||
var apiMajorVersion = 0
|
|
||||||
var badgeUnread = 0
|
var badgeUnread = 0
|
||||||
set(value) {field = if (value < 0) { 0 } else { value } }
|
set(value) {field = if (value < 0) { 0 } else { value } }
|
||||||
var badgeAll = 0
|
var badgeAll = 0
|
||||||
@ -44,6 +43,7 @@ class Repository(private val api: SelfossApi, private val apiDetails: ApiDetails
|
|||||||
// TODO: Dispatchers.IO not available in KMM, an alternative solution should be found
|
// TODO: Dispatchers.IO not available in KMM, an alternative solution should be found
|
||||||
CoroutineScope(Dispatchers.Main).launch {
|
CoroutineScope(Dispatchers.Main).launch {
|
||||||
updateApiVersion()
|
updateApiVersion()
|
||||||
|
dateUtils = DateUtils(apiDetails)
|
||||||
reloadBadges()
|
reloadBadges()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -54,7 +54,6 @@ class Repository(private val api: SelfossApi, private val apiDetails: ApiDetails
|
|||||||
if (isNetworkAvailable()) {
|
if (isNetworkAvailable()) {
|
||||||
fetchedItems = api.getItems(
|
fetchedItems = api.getItems(
|
||||||
displayedItems.type,
|
displayedItems.type,
|
||||||
settings.getString("prefer_api_items_number", "200").toInt(),
|
|
||||||
offset = 0,
|
offset = 0,
|
||||||
tagFilter?.tag,
|
tagFilter?.tag,
|
||||||
sourceFilter?.id?.toLong(),
|
sourceFilter?.id?.toLong(),
|
||||||
@ -84,7 +83,6 @@ class Repository(private val api: SelfossApi, private val apiDetails: ApiDetails
|
|||||||
val offset = items.size
|
val offset = items.size
|
||||||
fetchedItems = api.getItems(
|
fetchedItems = api.getItems(
|
||||||
displayedItems.type,
|
displayedItems.type,
|
||||||
settings.getString("prefer_api_items_number", "200").toInt(),
|
|
||||||
offset,
|
offset,
|
||||||
tagFilter?.tag,
|
tagFilter?.tag,
|
||||||
sourceFilter?.id?.toLong(),
|
sourceFilter?.id?.toLong(),
|
||||||
@ -104,12 +102,12 @@ class Repository(private val api: SelfossApi, private val apiDetails: ApiDetails
|
|||||||
return if (isNetworkAvailable()) {
|
return if (isNetworkAvailable()) {
|
||||||
api.getItems(
|
api.getItems(
|
||||||
itemType.type,
|
itemType.type,
|
||||||
200,
|
|
||||||
0,
|
0,
|
||||||
tagFilter?.tag,
|
tagFilter?.tag,
|
||||||
sourceFilter?.id?.toLong(),
|
sourceFilter?.id?.toLong(),
|
||||||
searchFilter,
|
searchFilter,
|
||||||
null
|
null,
|
||||||
|
200
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
emptyList()
|
emptyList()
|
||||||
@ -309,7 +307,7 @@ class Repository(private val api: SelfossApi, private val apiDetails: ApiDetails
|
|||||||
spout,
|
spout,
|
||||||
tags,
|
tags,
|
||||||
filter,
|
filter,
|
||||||
apiMajorVersion
|
apiDetails.getApiVersion()
|
||||||
)?.isSuccess == true
|
)?.isSuccess == true
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -366,13 +364,13 @@ class Repository(private val api: SelfossApi, private val apiDetails: ApiDetails
|
|||||||
}
|
}
|
||||||
|
|
||||||
private suspend fun updateApiVersion() {
|
private suspend fun updateApiVersion() {
|
||||||
apiMajorVersion = settings.getInt("apiVersionMajor", 0)
|
val apiMajorVersion = apiDetails.getApiVersion()
|
||||||
|
|
||||||
if (isNetworkAvailable()) {
|
if (isNetworkAvailable()) {
|
||||||
val fetchedVersion = api.version()
|
val fetchedVersion = api.version()
|
||||||
if (fetchedVersion != null) {
|
if (fetchedVersion != null && fetchedVersion.getApiMajorVersion() != apiMajorVersion) {
|
||||||
apiMajorVersion = fetchedVersion.getApiMajorVersion()
|
settings.putInt("apiVersionMajor", fetchedVersion.getApiMajorVersion())
|
||||||
settings.putInt("apiVersionMajor", apiMajorVersion)
|
apiDetails.refreshApiVersion()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
dateUtils = DateUtils(apiMajorVersion)
|
dateUtils = DateUtils(apiMajorVersion)
|
||||||
|
@ -4,6 +4,7 @@ import bou.amine.apps.readerforselfossv2.model.SelfossModel
|
|||||||
import bou.amine.apps.readerforselfossv2.service.ApiDetailsService
|
import bou.amine.apps.readerforselfossv2.service.ApiDetailsService
|
||||||
import io.ktor.client.*
|
import io.ktor.client.*
|
||||||
import io.ktor.client.call.*
|
import io.ktor.client.call.*
|
||||||
|
import io.ktor.client.plugins.*
|
||||||
import io.ktor.client.plugins.cache.*
|
import io.ktor.client.plugins.cache.*
|
||||||
import io.ktor.client.request.*
|
import io.ktor.client.request.*
|
||||||
import io.ktor.client.request.forms.*
|
import io.ktor.client.request.forms.*
|
||||||
@ -35,6 +36,9 @@ class SelfossApi(private val apiDetailsService: ApiDetailsService) {
|
|||||||
}
|
}
|
||||||
level = LogLevel.ALL
|
level = LogLevel.ALL
|
||||||
}
|
}
|
||||||
|
install(HttpTimeout) {
|
||||||
|
requestTimeoutMillis = apiDetailsService.getApiTimeout()
|
||||||
|
}
|
||||||
/* TODO: Auth as basic
|
/* TODO: Auth as basic
|
||||||
if (apiDetailsService.getUserName().isNotEmpty() && apiDetailsService.getPassword().isNotEmpty()) {
|
if (apiDetailsService.getUserName().isNotEmpty() && apiDetailsService.getPassword().isNotEmpty()) {
|
||||||
|
|
||||||
@ -69,12 +73,12 @@ class SelfossApi(private val apiDetailsService: ApiDetailsService) {
|
|||||||
|
|
||||||
suspend fun getItems(
|
suspend fun getItems(
|
||||||
type: String,
|
type: String,
|
||||||
items: Int,
|
|
||||||
offset: Int,
|
offset: Int,
|
||||||
tag: String?,
|
tag: String?,
|
||||||
source: Long?,
|
source: Long?,
|
||||||
search: String?,
|
search: String?,
|
||||||
updatedSince: String?
|
updatedSince: String?,
|
||||||
|
items: Int? = null
|
||||||
): List<SelfossModel.Item>? =
|
): List<SelfossModel.Item>? =
|
||||||
client.get(url("/items")) {
|
client.get(url("/items")) {
|
||||||
parameter("username", apiDetailsService.getUserName())
|
parameter("username", apiDetailsService.getUserName())
|
||||||
@ -84,7 +88,7 @@ class SelfossApi(private val apiDetailsService: ApiDetailsService) {
|
|||||||
parameter("source", source)
|
parameter("source", source)
|
||||||
parameter("search", search)
|
parameter("search", search)
|
||||||
parameter("updatedsince", updatedSince)
|
parameter("updatedsince", updatedSince)
|
||||||
parameter("items", items)
|
parameter("items", items ?: apiDetailsService.getItemsNumber())
|
||||||
parameter("offset", offset)
|
parameter("offset", offset)
|
||||||
}.body()
|
}.body()
|
||||||
|
|
||||||
|
@ -1,10 +1,97 @@
|
|||||||
package bou.amine.apps.readerforselfossv2.service
|
package bou.amine.apps.readerforselfossv2.service
|
||||||
|
|
||||||
interface ApiDetailsService {
|
import com.russhwolf.settings.Settings
|
||||||
fun logApiCalls(message: String)
|
import io.github.aakira.napier.Napier
|
||||||
fun getApiVersion(): Int
|
import io.ktor.client.plugins.*
|
||||||
fun getBaseUrl(): String
|
|
||||||
fun getUserName(): String
|
class ApiDetailsService {
|
||||||
fun getPassword(): String
|
val settings: Settings = Settings()
|
||||||
fun refresh()
|
private var _apiVersion: Int = -1
|
||||||
|
private var _baseUrl: String = ""
|
||||||
|
private var _userName: String = ""
|
||||||
|
private var _password: String = ""
|
||||||
|
private var _itemsNumber: Int? = null
|
||||||
|
private var _apiTimeout: Long? = null
|
||||||
|
|
||||||
|
fun logApiCalls(message: String) {
|
||||||
|
Napier.d(message, tag = "LogApiCalls")
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
fun getApiVersion(): Int {
|
||||||
|
if (_apiVersion == -1) {
|
||||||
|
refreshApiVersion()
|
||||||
|
return _apiVersion
|
||||||
|
}
|
||||||
|
return _apiVersion
|
||||||
|
}
|
||||||
|
|
||||||
|
fun refreshApiVersion() {
|
||||||
|
_apiVersion = settings.getInt("apiVersionMajor", -1)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getBaseUrl(): String {
|
||||||
|
if (_baseUrl.isEmpty()) {
|
||||||
|
_baseUrl = settings.getString("url", "")
|
||||||
|
}
|
||||||
|
return _baseUrl
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getUserName(): String {
|
||||||
|
if (_userName.isEmpty()) {
|
||||||
|
_userName = settings.getString("login", "")
|
||||||
|
}
|
||||||
|
return _userName
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getPassword(): String {
|
||||||
|
if (_password.isEmpty()) {
|
||||||
|
_password = settings.getString("password", "")
|
||||||
|
}
|
||||||
|
return _password
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getItemsNumber(): Int {
|
||||||
|
if (_itemsNumber == null) {
|
||||||
|
refreshItemsNumber()
|
||||||
|
}
|
||||||
|
return _itemsNumber!!
|
||||||
|
}
|
||||||
|
|
||||||
|
fun refreshItemsNumber() {
|
||||||
|
_itemsNumber = settings.getString("prefer_api_items_number", "200").toInt()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getApiTimeout(): Long {
|
||||||
|
if (_apiTimeout == null) {
|
||||||
|
refreshApiTimeout()
|
||||||
|
}
|
||||||
|
return _apiTimeout!!
|
||||||
|
}
|
||||||
|
|
||||||
|
fun refreshApiTimeout() {
|
||||||
|
val settingsTimeout = settings.getLong("api_timeout", HttpTimeout.INFINITE_TIMEOUT_MS)
|
||||||
|
_apiTimeout = if (settingsTimeout > 0) settingsTimeout else HttpTimeout.INFINITE_TIMEOUT_MS
|
||||||
|
}
|
||||||
|
|
||||||
|
fun refresh() {
|
||||||
|
_password = settings.getString("password", "")
|
||||||
|
_userName = settings.getString("login", "")
|
||||||
|
_baseUrl = settings.getString("url", "")
|
||||||
|
refreshApiVersion()
|
||||||
|
refreshItemsNumber()
|
||||||
|
refreshApiTimeout()
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
const val translationUrl = "https://crwd.in/readerforselfoss"
|
||||||
|
|
||||||
|
const val sourceUrl = "https://gitea.amine-louveau.fr/Louvorg/ReaderForSelfoss-multiplatform"
|
||||||
|
|
||||||
|
const val trackerUrl = "https://gitea.amine-louveau.fr/Louvorg/ReaderForSelfoss-multiplatform/issues"
|
||||||
|
|
||||||
|
const val syncChannelId = "sync-channel-id"
|
||||||
|
|
||||||
|
const val newItemsChannelId = "new-items-channel-id"
|
||||||
|
}
|
||||||
}
|
}
|
@ -1,52 +0,0 @@
|
|||||||
package bou.amine.apps.readerforselfossv2.service
|
|
||||||
|
|
||||||
import com.russhwolf.settings.Settings
|
|
||||||
import io.github.aakira.napier.Napier
|
|
||||||
|
|
||||||
class ApiDetailsServiceImpl : ApiDetailsService {
|
|
||||||
val settings: Settings = Settings()
|
|
||||||
private var _apiVersion: Int = -1
|
|
||||||
private var _baseUrl: String = ""
|
|
||||||
private var _userName: String = ""
|
|
||||||
private var _password: String = ""
|
|
||||||
override fun logApiCalls(message: String) {
|
|
||||||
Napier.d(message, tag = "LogApiCalls")
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
override fun getApiVersion(): Int {
|
|
||||||
if (_apiVersion == -1) {
|
|
||||||
_apiVersion = settings.getInt("apiVersionMajor", -1)
|
|
||||||
return _apiVersion
|
|
||||||
}
|
|
||||||
return _apiVersion
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun getBaseUrl(): String {
|
|
||||||
if (_baseUrl.isEmpty()) {
|
|
||||||
_baseUrl = settings.getString("url", "")
|
|
||||||
}
|
|
||||||
return _baseUrl
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun getUserName(): String {
|
|
||||||
if (_userName.isEmpty()) {
|
|
||||||
_userName = settings.getString("login", "")
|
|
||||||
}
|
|
||||||
return _userName
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun getPassword(): String {
|
|
||||||
if (_password.isEmpty()) {
|
|
||||||
_password = settings.getString("password", "")
|
|
||||||
}
|
|
||||||
return _password
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun refresh() {
|
|
||||||
_password = settings.getString("password", "")
|
|
||||||
_userName = settings.getString("login", "")
|
|
||||||
_baseUrl = settings.getString("url", "")
|
|
||||||
_apiVersion = settings.getInt("apiVersionMajor", -1)
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,12 +1,13 @@
|
|||||||
package bou.amine.apps.readerforselfossv2.utils
|
package bou.amine.apps.readerforselfossv2.utils
|
||||||
|
|
||||||
import bou.amine.apps.readerforselfossv2.model.SelfossModel
|
import bou.amine.apps.readerforselfossv2.model.SelfossModel
|
||||||
|
import bou.amine.apps.readerforselfossv2.service.ApiDetailsService
|
||||||
|
|
||||||
|
|
||||||
fun SelfossModel.Item.parseDate(dateUtils: DateUtils): Long =
|
fun SelfossModel.Item.parseDate(dateUtils: DateUtils): Long =
|
||||||
dateUtils.parseDate(this.datetime)
|
dateUtils.parseDate(this.datetime)
|
||||||
|
|
||||||
expect class DateUtils(apiMajorVersion: Int) {
|
expect class DateUtils(apiDetailsService: ApiDetailsService) {
|
||||||
fun parseDate(dateString: String): Long
|
fun parseDate(dateString: String): Long
|
||||||
|
|
||||||
fun parseRelativeDate(dateString: String): String
|
fun parseRelativeDate(dateString: String): String
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
package bou.amine.apps.readerforselfossv2.utils
|
package bou.amine.apps.readerforselfossv2.utils
|
||||||
|
|
||||||
actual class DateUtils actual constructor(apiMajorVersion: Int) {
|
import bou.amine.apps.readerforselfossv2.service.ApiDetailsService
|
||||||
|
|
||||||
|
actual class DateUtils actual constructor(apiDetailsService: ApiDetailsService) {
|
||||||
actual fun parseDate(dateString: String): Long {
|
actual fun parseDate(dateString: String): Long {
|
||||||
TODO("Not yet implemented")
|
TODO("Not yet implemented")
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
package bou.amine.apps.readerforselfossv2.utils
|
package bou.amine.apps.readerforselfossv2.utils
|
||||||
|
|
||||||
actual class DateUtils actual constructor(apiMajorVersion: Int) {
|
import bou.amine.apps.readerforselfossv2.service.ApiDetailsService
|
||||||
|
|
||||||
|
actual class DateUtils actual constructor(apiDetailsService: ApiDetailsService) {
|
||||||
actual fun parseDate(dateString: String): Long {
|
actual fun parseDate(dateString: String): Long {
|
||||||
TODO("Not yet implemented")
|
TODO("Not yet implemented")
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user