chore: code style.
This commit is contained in:
parent
5035392aff
commit
6b5f6cbbe0
27
.editorconfig
Normal file
27
.editorconfig
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
root = true
|
||||||
|
|
||||||
|
[*]
|
||||||
|
insert_final_newline = true
|
||||||
|
|
||||||
|
[{*.kt,*.kts}]
|
||||||
|
|
||||||
|
[*.{kt,kts}]
|
||||||
|
# Disable wildcard imports entirely
|
||||||
|
ij_kotlin_name_count_to_use_star_import = 2147483647
|
||||||
|
ij_kotlin_name_count_to_use_star_import_for_members = 2147483647
|
||||||
|
|
||||||
|
ij_kotlin_code_style_defaults = KOTLIN_OFFICIAL
|
||||||
|
end_of_line = lf
|
||||||
|
ij_kotlin_allow_trailing_comma = true
|
||||||
|
ij_kotlin_allow_trailing_comma_on_call_site = true
|
||||||
|
ij_kotlin_imports_layout = *, java.**, javax.**, kotlin.**, ^
|
||||||
|
ij_kotlin_packages_to_use_import_on_demand = unset
|
||||||
|
indent_size = 4
|
||||||
|
indent_style = space
|
||||||
|
ktlint_chain_method_rule_force_multiline_when_chain_operator_count_greater_or_equal_than = unset
|
||||||
|
ktlint_class_signature_rule_force_multiline_when_parameter_count_greater_or_equal_than = 1
|
||||||
|
ktlint_code_style = ktlint_official
|
||||||
|
ktlint_function_signature_body_expression_wrapping = multiline
|
||||||
|
ktlint_function_signature_rule_force_multiline_when_parameter_count_greater_or_equal_than = 2
|
||||||
|
ktlint_ignore_back_ticked_identifier = false
|
||||||
|
max_line_length = 140
|
@ -20,7 +20,7 @@ jobs:
|
|||||||
- name: Install detekt
|
- name: Install detekt
|
||||||
run: curl -sSLO https://github.com/detekt/detekt/releases/download/v1.23.1/detekt-cli-1.23.1.zip && unzip detekt-cli-1.23.1.zip
|
run: curl -sSLO https://github.com/detekt/detekt/releases/download/v1.23.1/detekt-cli-1.23.1.zip && unzip detekt-cli-1.23.1.zip
|
||||||
- name: Linting...
|
- name: Linting...
|
||||||
run: ktlint 'shared/**/*.kt' 'androidApp/**/*.kt' '!shared/build' || true
|
run: ktlint 'shared/**/*.kt' 'androidApp/**/*.kt' '!shared/build'
|
||||||
- name: Detecting...
|
- name: Detecting...
|
||||||
run: ./detekt-cli-1.23.1/bin/detekt-cli --all-rules --excludes '**/shared/build/**/*.kt' || true
|
run: ./detekt-cli-1.23.1/bin/detekt-cli --all-rules --excludes '**/shared/build/**/*.kt' || true
|
||||||
build:
|
build:
|
||||||
|
@ -20,65 +20,72 @@ import org.hamcrest.Matchers.hasToString
|
|||||||
fun performLogin(someUrl: String? = null) {
|
fun performLogin(someUrl: String? = null) {
|
||||||
onView(withId(R.id.urlView)).perform(click()).perform(
|
onView(withId(R.id.urlView)).perform(click()).perform(
|
||||||
typeTextIntoFocusedView(
|
typeTextIntoFocusedView(
|
||||||
if (!someUrl.isNullOrEmpty()) someUrl else "http://10.0.2.2:8888"
|
if (!someUrl.isNullOrEmpty()) someUrl else "http://10.0.2.2:8888",
|
||||||
)
|
),
|
||||||
)
|
)
|
||||||
onView(withId(R.id.signInButton)).perform(click())
|
onView(withId(R.id.signInButton)).perform(click())
|
||||||
}
|
}
|
||||||
|
|
||||||
fun loginAndInitHome() {
|
fun loginAndInitHome() {
|
||||||
|
|
||||||
performLogin()
|
performLogin()
|
||||||
onView(withText(R.string.gdpr_dialog_title)).check(matches(isDisplayed()))
|
onView(withText(R.string.gdpr_dialog_title)).check(matches(isDisplayed()))
|
||||||
onView(withText("OK")).perform(click())
|
onView(withText("OK")).perform(click())
|
||||||
}
|
}
|
||||||
|
|
||||||
fun changeAndCancelSetting(oldValue: String, newValue: String, openSettingItem: () -> Unit) {
|
fun changeAndCancelSetting(
|
||||||
|
oldValue: String,
|
||||||
|
newValue: String,
|
||||||
|
openSettingItem: () -> Unit,
|
||||||
|
) {
|
||||||
openSettingItem()
|
openSettingItem()
|
||||||
onView(
|
onView(
|
||||||
withId(android.R.id.edit)
|
withId(android.R.id.edit),
|
||||||
).perform(replaceText(newValue))
|
).perform(replaceText(newValue))
|
||||||
onView(
|
onView(
|
||||||
withId(android.R.id.button2)
|
withId(android.R.id.button2),
|
||||||
).perform(click())
|
).perform(click())
|
||||||
openSettingItem()
|
openSettingItem()
|
||||||
onView(
|
onView(
|
||||||
withId(android.R.id.edit)
|
withId(android.R.id.edit),
|
||||||
).check(matches(withText(oldValue)))
|
).check(matches(withText(oldValue)))
|
||||||
onView(
|
onView(
|
||||||
withText(newValue)
|
withText(newValue),
|
||||||
).check(doesNotExist())
|
).check(doesNotExist())
|
||||||
onView(
|
onView(
|
||||||
withId(android.R.id.button2)
|
withId(android.R.id.button2),
|
||||||
).perform(click())
|
).perform(click())
|
||||||
}
|
}
|
||||||
|
|
||||||
fun changeAndSaveSetting(oldValue: String, newValue: String, openSettingItem: () -> Unit) {
|
fun changeAndSaveSetting(
|
||||||
|
oldValue: String,
|
||||||
|
newValue: String,
|
||||||
|
openSettingItem: () -> Unit,
|
||||||
|
) {
|
||||||
openSettingItem()
|
openSettingItem()
|
||||||
onView(
|
onView(
|
||||||
withId(android.R.id.edit)
|
withId(android.R.id.edit),
|
||||||
).perform(replaceText(newValue))
|
).perform(replaceText(newValue))
|
||||||
onView(
|
onView(
|
||||||
withId(android.R.id.button1)
|
withId(android.R.id.button1),
|
||||||
).perform(click())
|
).perform(click())
|
||||||
openSettingItem()
|
openSettingItem()
|
||||||
onView(
|
onView(
|
||||||
withId(android.R.id.edit)
|
withId(android.R.id.edit),
|
||||||
).check(matches(withText(newValue)))
|
).check(matches(withText(newValue)))
|
||||||
if (oldValue.isNotEmpty()) {
|
if (oldValue.isNotEmpty()) {
|
||||||
onView(
|
onView(
|
||||||
withText(oldValue)
|
withText(oldValue),
|
||||||
).check(doesNotExist())
|
).check(doesNotExist())
|
||||||
}
|
}
|
||||||
onView(
|
onView(
|
||||||
withId(android.R.id.button2)
|
withId(android.R.id.button2),
|
||||||
).perform(click())
|
).perform(click())
|
||||||
}
|
}
|
||||||
|
|
||||||
fun testPreferencesFromArray(
|
fun testPreferencesFromArray(
|
||||||
context: Context,
|
context: Context,
|
||||||
@ArrayRes arrayRes: Int,
|
@ArrayRes arrayRes: Int,
|
||||||
openSettingItem: () -> Unit
|
openSettingItem: () -> Unit,
|
||||||
) {
|
) {
|
||||||
openSettingItem()
|
openSettingItem()
|
||||||
context.resources.getStringArray(arrayRes).forEach { res ->
|
context.resources.getStringArray(arrayRes).forEach { res ->
|
||||||
@ -90,7 +97,10 @@ fun testPreferencesFromArray(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun testAddSourceWithUrl(url: String, sourceName: String) {
|
fun testAddSourceWithUrl(
|
||||||
|
url: String,
|
||||||
|
sourceName: String,
|
||||||
|
) {
|
||||||
onView(withId(R.id.fab))
|
onView(withId(R.id.fab))
|
||||||
.perform(click())
|
.perform(click())
|
||||||
onView(withId(R.id.nameInput))
|
onView(withId(R.id.nameInput))
|
||||||
|
@ -25,8 +25,9 @@ import org.hamcrest.Matcher
|
|||||||
import org.hamcrest.Matchers
|
import org.hamcrest.Matchers
|
||||||
import org.hamcrest.TypeSafeMatcher
|
import org.hamcrest.TypeSafeMatcher
|
||||||
|
|
||||||
|
fun withError(
|
||||||
fun withError(@StringRes id: Int): TypeSafeMatcher<View?> {
|
@StringRes id: Int,
|
||||||
|
): TypeSafeMatcher<View?> {
|
||||||
return object : TypeSafeMatcher<View?>() {
|
return object : TypeSafeMatcher<View?>() {
|
||||||
override fun matchesSafely(view: View?): Boolean {
|
override fun matchesSafely(view: View?): Boolean {
|
||||||
if (view == null) {
|
if (view == null) {
|
||||||
@ -52,7 +53,9 @@ fun isPopupWindow(): Matcher<Root> {
|
|||||||
return isPlatformPopup()
|
return isPlatformPopup()
|
||||||
}
|
}
|
||||||
|
|
||||||
fun withDrawable(@DrawableRes id: Int) = object : TypeSafeMatcher<View>() {
|
fun withDrawable(
|
||||||
|
@DrawableRes id: Int,
|
||||||
|
) = object : TypeSafeMatcher<View>() {
|
||||||
override fun describeTo(description: Description) {
|
override fun describeTo(description: Description) {
|
||||||
description.appendText("ImageView with drawable same as drawable with id $id")
|
description.appendText("ImageView with drawable same as drawable with id $id")
|
||||||
}
|
}
|
||||||
@ -68,43 +71,49 @@ fun withDrawable(@DrawableRes id: Int) = object : TypeSafeMatcher<View>() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun hasBottombarItemText(@StringRes id: Int): Matcher<View>? {
|
fun hasBottombarItemText(
|
||||||
|
@StringRes id: Int,
|
||||||
|
): Matcher<View>? {
|
||||||
return allOf(
|
return allOf(
|
||||||
withResourceName("fixed_bottom_navigation_icon"),
|
withResourceName("fixed_bottom_navigation_icon"),
|
||||||
withParent(
|
withParent(
|
||||||
allOf(
|
allOf(
|
||||||
withResourceName("fixed_bottom_navigation_icon_container"),
|
withResourceName("fixed_bottom_navigation_icon_container"),
|
||||||
hasSibling(withText(id))
|
hasSibling(withText(id)),
|
||||||
)
|
),
|
||||||
)
|
),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun withSettingsCheckboxWidget(@StringRes id: Int): Matcher<View>? {
|
fun withSettingsCheckboxWidget(
|
||||||
|
@StringRes id: Int,
|
||||||
|
): Matcher<View>? {
|
||||||
return allOf(
|
return allOf(
|
||||||
withId(android.R.id.switch_widget),
|
withId(android.R.id.switch_widget),
|
||||||
withParent(
|
withParent(
|
||||||
withSettingsCheckboxFrame(id)
|
withSettingsCheckboxFrame(id),
|
||||||
)
|
),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun withSettingsCheckboxFrame(@StringRes id: Int): Matcher<View>? {
|
fun withSettingsCheckboxFrame(
|
||||||
|
@StringRes id: Int,
|
||||||
|
): Matcher<View>? {
|
||||||
return allOf(
|
return allOf(
|
||||||
withId(android.R.id.widget_frame),
|
withId(android.R.id.widget_frame),
|
||||||
hasSibling(
|
hasSibling(
|
||||||
allOf(
|
allOf(
|
||||||
withClassName(Matchers.equalTo(RelativeLayout::class.java.name)),
|
withClassName(Matchers.equalTo(RelativeLayout::class.java.name)),
|
||||||
withChild(
|
withChild(
|
||||||
withText(id)
|
withText(id),
|
||||||
)
|
),
|
||||||
)
|
),
|
||||||
)
|
),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun openMenu() {
|
fun openMenu() {
|
||||||
openActionBarOverflowOrOptionsMenu(
|
openActionBarOverflowOrOptionsMenu(
|
||||||
ApplicationProvider.getApplicationContext<Context>()
|
ApplicationProvider.getApplicationContext<Context>(),
|
||||||
)
|
)
|
||||||
}
|
}
|
@ -23,7 +23,6 @@ import org.junit.runner.RunWith
|
|||||||
@RunWith(AndroidJUnit4::class)
|
@RunWith(AndroidJUnit4::class)
|
||||||
@LargeTest
|
@LargeTest
|
||||||
class HomeActivityTest {
|
class HomeActivityTest {
|
||||||
|
|
||||||
@get:Rule
|
@get:Rule
|
||||||
val activityRule = ActivityScenarioRule(LoginActivity::class.java)
|
val activityRule = ActivityScenarioRule(LoginActivity::class.java)
|
||||||
|
|
||||||
@ -36,13 +35,13 @@ class HomeActivityTest {
|
|||||||
fun testMenu() {
|
fun testMenu() {
|
||||||
onView(withId(R.id.action_search)).check(matches(isDisplayed())).check(
|
onView(withId(R.id.action_search)).check(matches(isDisplayed())).check(
|
||||||
matches(
|
matches(
|
||||||
isClickable()
|
isClickable(),
|
||||||
)
|
),
|
||||||
)
|
)
|
||||||
onView(withId(R.id.action_filter)).check(matches(isDisplayed())).check(
|
onView(withId(R.id.action_filter)).check(matches(isDisplayed())).check(
|
||||||
matches(
|
matches(
|
||||||
isClickable()
|
isClickable(),
|
||||||
)
|
),
|
||||||
)
|
)
|
||||||
openMenu()
|
openMenu()
|
||||||
onView(withText(R.string.readAll)).check(matches(isDisplayed()))
|
onView(withText(R.string.readAll)).check(matches(isDisplayed()))
|
||||||
@ -57,19 +56,19 @@ class HomeActivityTest {
|
|||||||
fun testMenuActions() {
|
fun testMenuActions() {
|
||||||
onView(withId(R.id.action_search)).perform(click())
|
onView(withId(R.id.action_search)).perform(click())
|
||||||
onView(
|
onView(
|
||||||
withId(R.id.search_src_text)
|
withId(R.id.search_src_text),
|
||||||
).check(matches(isFocused()))
|
).check(matches(isFocused()))
|
||||||
onView(isRoot()).perform(ViewActions.pressBack())
|
onView(isRoot()).perform(ViewActions.pressBack())
|
||||||
|
|
||||||
onView(withId(R.id.action_filter)).perform(click())
|
onView(withId(R.id.action_filter)).perform(click())
|
||||||
onView(
|
onView(
|
||||||
withText(R.string.filter_item_sources)
|
withText(R.string.filter_item_sources),
|
||||||
).check(matches(isDisplayed()))
|
).check(matches(isDisplayed()))
|
||||||
onView(
|
onView(
|
||||||
withText(R.string.filter_item_tags)
|
withText(R.string.filter_item_tags),
|
||||||
).check(matches(isDisplayed()))
|
).check(matches(isDisplayed()))
|
||||||
onView(
|
onView(
|
||||||
withId(R.id.floatingActionButton2)
|
withId(R.id.floatingActionButton2),
|
||||||
).check(matches(isDisplayed()))
|
).check(matches(isDisplayed()))
|
||||||
onView(isRoot()).perform(ViewActions.pressBack())
|
onView(isRoot()).perform(ViewActions.pressBack())
|
||||||
|
|
||||||
@ -107,14 +106,13 @@ class HomeActivityTest {
|
|||||||
fun testEmptyView() {
|
fun testEmptyView() {
|
||||||
onView(withId(R.id.emptyText)).check(matches(isDisplayed()))
|
onView(withId(R.id.emptyText)).check(matches(isDisplayed()))
|
||||||
onView(
|
onView(
|
||||||
hasBottombarItemText(R.string.tab_new)
|
hasBottombarItemText(R.string.tab_new),
|
||||||
).check(matches(isDisplayed())).check(matches(isSelected()))
|
).check(matches(isDisplayed())).check(matches(isSelected()))
|
||||||
onView(
|
onView(
|
||||||
hasBottombarItemText(R.string.tab_read)
|
hasBottombarItemText(R.string.tab_read),
|
||||||
).check(matches(isDisplayed())).check(matches(not(isSelected())))
|
).check(matches(isDisplayed())).check(matches(not(isSelected())))
|
||||||
onView(
|
onView(
|
||||||
hasBottombarItemText(R.string.tab_favs)
|
hasBottombarItemText(R.string.tab_favs),
|
||||||
).check(matches(isDisplayed())).check(matches(not(isSelected())))
|
).check(matches(isDisplayed())).check(matches(not(isSelected())))
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
@ -23,7 +23,6 @@ import org.junit.runner.RunWith
|
|||||||
@RunWith(AndroidJUnit4::class)
|
@RunWith(AndroidJUnit4::class)
|
||||||
@LargeTest
|
@LargeTest
|
||||||
class LoginActivityTest {
|
class LoginActivityTest {
|
||||||
|
|
||||||
@get:Rule
|
@get:Rule
|
||||||
val activityRule = ActivityScenarioRule(LoginActivity::class.java)
|
val activityRule = ActivityScenarioRule(LoginActivity::class.java)
|
||||||
|
|
||||||
@ -52,11 +51,11 @@ class LoginActivityTest {
|
|||||||
onView(withId(R.id.urlView)).check(matches(isDisplayed()))
|
onView(withId(R.id.urlView)).check(matches(isDisplayed()))
|
||||||
onView(withId(R.id.selfSigned)).check(matches(isDisplayed())).check(matches(isNotChecked()))
|
onView(withId(R.id.selfSigned)).check(matches(isDisplayed())).check(matches(isNotChecked()))
|
||||||
.check(
|
.check(
|
||||||
matches(isClickable())
|
matches(isClickable()),
|
||||||
)
|
)
|
||||||
onView(withId(R.id.withLogin)).check(matches(isDisplayed()))
|
onView(withId(R.id.withLogin)).check(matches(isDisplayed()))
|
||||||
.check(matches(isNotChecked())).check(
|
.check(matches(isNotChecked())).check(
|
||||||
matches(isClickable())
|
matches(isClickable()),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -26,11 +26,9 @@ 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)
|
||||||
@LargeTest
|
@LargeTest
|
||||||
class SettingsActivityGeneralTest {
|
class SettingsActivityGeneralTest {
|
||||||
|
|
||||||
@get:Rule
|
@get:Rule
|
||||||
val activityRule = ActivityScenarioRule(LoginActivity::class.java)
|
val activityRule = ActivityScenarioRule(LoginActivity::class.java)
|
||||||
|
|
||||||
@ -38,7 +36,7 @@ class SettingsActivityGeneralTest {
|
|||||||
fun init() {
|
fun init() {
|
||||||
loginAndInitHome()
|
loginAndInitHome()
|
||||||
openActionBarOverflowOrOptionsMenu(
|
openActionBarOverflowOrOptionsMenu(
|
||||||
ApplicationProvider.getApplicationContext()
|
ApplicationProvider.getApplicationContext(),
|
||||||
)
|
)
|
||||||
onView(withText(R.string.title_activity_settings)).perform(click())
|
onView(withText(R.string.title_activity_settings)).perform(click())
|
||||||
onView(withText(R.string.pref_header_general)).perform(click())
|
onView(withText(R.string.pref_header_general)).perform(click())
|
||||||
@ -48,68 +46,75 @@ class SettingsActivityGeneralTest {
|
|||||||
fun testGeneral() {
|
fun testGeneral() {
|
||||||
onView(withText(R.string.pref_api_items_number_title)).check(matches(isDisplayed()))
|
onView(withText(R.string.pref_api_items_number_title)).check(matches(isDisplayed()))
|
||||||
onView(
|
onView(
|
||||||
withSettingsCheckboxWidget(R.string.pref_general_infinite_loading_title)
|
withSettingsCheckboxWidget(R.string.pref_general_infinite_loading_title),
|
||||||
).check(
|
).check(
|
||||||
matches(
|
matches(
|
||||||
allOf(
|
allOf(
|
||||||
isDisplayed(), not(isChecked())
|
isDisplayed(),
|
||||||
)
|
not(isChecked()),
|
||||||
)
|
),
|
||||||
|
),
|
||||||
)
|
)
|
||||||
onView(withText(R.string.pref_general_category_links)).check(matches(isDisplayed()))
|
onView(withText(R.string.pref_general_category_links)).check(matches(isDisplayed()))
|
||||||
onView(withSettingsCheckboxWidget(R.string.pref_article_viewer_title)).check(
|
onView(withSettingsCheckboxWidget(R.string.pref_article_viewer_title)).check(
|
||||||
matches(
|
matches(
|
||||||
allOf(
|
allOf(
|
||||||
isDisplayed(), isChecked()
|
isDisplayed(),
|
||||||
)
|
isChecked(),
|
||||||
)
|
),
|
||||||
|
),
|
||||||
)
|
)
|
||||||
onView(withSettingsCheckboxWidget(R.string.reader_static_bar_title)).check(
|
onView(withSettingsCheckboxWidget(R.string.reader_static_bar_title)).check(
|
||||||
matches(
|
matches(
|
||||||
allOf(
|
allOf(
|
||||||
isDisplayed(), not(isChecked())
|
isDisplayed(),
|
||||||
)
|
not(isChecked()),
|
||||||
)
|
),
|
||||||
|
),
|
||||||
)
|
)
|
||||||
onView(withSettingsCheckboxFrame(R.string.reader_static_bar_title)).check(
|
onView(withSettingsCheckboxFrame(R.string.reader_static_bar_title)).check(
|
||||||
matches(
|
matches(
|
||||||
isEnabled()
|
isEnabled(),
|
||||||
)
|
),
|
||||||
)
|
)
|
||||||
onView(withText(R.string.pref_general_category_displaying)).check(matches(isDisplayed()))
|
onView(withText(R.string.pref_general_category_displaying)).check(matches(isDisplayed()))
|
||||||
onView(withSettingsCheckboxWidget(R.string.pref_switch_card_view_title)).check(
|
onView(withSettingsCheckboxWidget(R.string.pref_switch_card_view_title)).check(
|
||||||
matches(
|
matches(
|
||||||
allOf(
|
allOf(
|
||||||
isDisplayed(), not(isChecked())
|
isDisplayed(),
|
||||||
)
|
not(isChecked()),
|
||||||
)
|
),
|
||||||
|
),
|
||||||
)
|
)
|
||||||
onView(withSettingsCheckboxWidget(R.string.card_height_title)).check(
|
onView(withSettingsCheckboxWidget(R.string.card_height_title)).check(
|
||||||
matches(
|
matches(
|
||||||
allOf(
|
allOf(
|
||||||
isDisplayed(), not(isChecked())
|
isDisplayed(),
|
||||||
)
|
not(isChecked()),
|
||||||
)
|
),
|
||||||
|
),
|
||||||
)
|
)
|
||||||
onView(withSettingsCheckboxFrame(R.string.card_height_title)).check(
|
onView(withSettingsCheckboxFrame(R.string.card_height_title)).check(
|
||||||
matches(
|
matches(
|
||||||
not(isEnabled())
|
not(isEnabled()),
|
||||||
)
|
),
|
||||||
)
|
)
|
||||||
onView(withSettingsCheckboxWidget(R.string.switch_unread_count_title)).check(
|
onView(withSettingsCheckboxWidget(R.string.switch_unread_count_title)).check(
|
||||||
matches(
|
matches(
|
||||||
allOf(
|
allOf(
|
||||||
isDisplayed(), isChecked()
|
isDisplayed(),
|
||||||
)
|
isChecked(),
|
||||||
)
|
),
|
||||||
|
),
|
||||||
)
|
)
|
||||||
onView(withId(R.id.settings)).perform(swipeUp())
|
onView(withId(R.id.settings)).perform(swipeUp())
|
||||||
onView(withSettingsCheckboxWidget(R.string.display_all_counts_title)).check(
|
onView(withSettingsCheckboxWidget(R.string.display_all_counts_title)).check(
|
||||||
matches(
|
matches(
|
||||||
allOf(
|
allOf(
|
||||||
isDisplayed(), not(isChecked())
|
isDisplayed(),
|
||||||
)
|
not(isChecked()),
|
||||||
)
|
),
|
||||||
|
),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -120,25 +125,25 @@ class SettingsActivityGeneralTest {
|
|||||||
|
|
||||||
// Value check
|
// Value check
|
||||||
onView(
|
onView(
|
||||||
withId(android.R.id.edit)
|
withId(android.R.id.edit),
|
||||||
).perform(replaceText("AVC"))
|
).perform(replaceText("AVC"))
|
||||||
.check(matches(withText("")))
|
.check(matches(withText("")))
|
||||||
// TODO: should check message error. Not working for api level 30+
|
// TODO: should check message error. Not working for api level 30+
|
||||||
onView(
|
onView(
|
||||||
withId(android.R.id.edit)
|
withId(android.R.id.edit),
|
||||||
).perform(replaceText("-1"))
|
).perform(replaceText("-1"))
|
||||||
.check(matches(withText("")))
|
.check(matches(withText("")))
|
||||||
// TODO: should check message error. Not working for api level 30+
|
// TODO: should check message error. Not working for api level 30+
|
||||||
onView(
|
onView(
|
||||||
withId(android.R.id.edit)
|
withId(android.R.id.edit),
|
||||||
).perform(replaceText("300"))
|
).perform(replaceText("300"))
|
||||||
.check(matches(withText("")))
|
.check(matches(withText("")))
|
||||||
onView(
|
onView(
|
||||||
withId(android.R.id.edit)
|
withId(android.R.id.edit),
|
||||||
).perform(typeTextIntoFocusedView("300"))
|
).perform(typeTextIntoFocusedView("300"))
|
||||||
.check(matches(withText("30")))
|
.check(matches(withText("30")))
|
||||||
onView(
|
onView(
|
||||||
withId(android.R.id.edit)
|
withId(android.R.id.edit),
|
||||||
).perform(replaceText("10"))
|
).perform(replaceText("10"))
|
||||||
.check(matches(withText("10")))
|
.check(matches(withText("10")))
|
||||||
onView(isRoot()).perform(ViewActions.pressBack())
|
onView(isRoot()).perform(ViewActions.pressBack())
|
||||||
@ -157,14 +162,14 @@ class SettingsActivityGeneralTest {
|
|||||||
// article viewer settings
|
// article viewer settings
|
||||||
onView(withSettingsCheckboxFrame(R.string.reader_static_bar_title)).check(
|
onView(withSettingsCheckboxFrame(R.string.reader_static_bar_title)).check(
|
||||||
matches(
|
matches(
|
||||||
isEnabled()
|
isEnabled(),
|
||||||
)
|
),
|
||||||
)
|
)
|
||||||
onView(withSettingsCheckboxWidget(R.string.pref_article_viewer_title)).perform(click())
|
onView(withSettingsCheckboxWidget(R.string.pref_article_viewer_title)).perform(click())
|
||||||
onView(withSettingsCheckboxFrame(R.string.reader_static_bar_title)).check(
|
onView(withSettingsCheckboxFrame(R.string.reader_static_bar_title)).check(
|
||||||
matches(
|
matches(
|
||||||
not(isEnabled())
|
not(isEnabled()),
|
||||||
)
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
onView(withSettingsCheckboxFrame(R.string.card_height_title)).check(matches(not(isEnabled())))
|
onView(withSettingsCheckboxFrame(R.string.card_height_title)).check(matches(not(isEnabled())))
|
||||||
|
@ -21,11 +21,9 @@ 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)
|
||||||
@LargeTest
|
@LargeTest
|
||||||
class SettingsActivityOfflineTest {
|
class SettingsActivityOfflineTest {
|
||||||
|
|
||||||
@get:Rule
|
@get:Rule
|
||||||
val activityRule = ActivityScenarioRule(LoginActivity::class.java)
|
val activityRule = ActivityScenarioRule(LoginActivity::class.java)
|
||||||
|
|
||||||
@ -38,7 +36,7 @@ class SettingsActivityOfflineTest {
|
|||||||
}
|
}
|
||||||
loginAndInitHome()
|
loginAndInitHome()
|
||||||
openActionBarOverflowOrOptionsMenu(
|
openActionBarOverflowOrOptionsMenu(
|
||||||
ApplicationProvider.getApplicationContext()
|
ApplicationProvider.getApplicationContext(),
|
||||||
)
|
)
|
||||||
onView(withText(R.string.title_activity_settings)).perform(click())
|
onView(withText(R.string.title_activity_settings)).perform(click())
|
||||||
onView(withText(R.string.pref_header_offline)).perform(click())
|
onView(withText(R.string.pref_header_offline)).perform(click())
|
||||||
@ -49,58 +47,63 @@ class SettingsActivityOfflineTest {
|
|||||||
onView(withSettingsCheckboxWidget(R.string.pref_switch_periodic_refresh)).check(
|
onView(withSettingsCheckboxWidget(R.string.pref_switch_periodic_refresh)).check(
|
||||||
matches(
|
matches(
|
||||||
allOf(
|
allOf(
|
||||||
isDisplayed(), not(isChecked())
|
isDisplayed(),
|
||||||
)
|
not(isChecked()),
|
||||||
)
|
),
|
||||||
|
),
|
||||||
)
|
)
|
||||||
onView(withSettingsCheckboxWidget(R.string.pref_switch_items_caching)).check(
|
onView(withSettingsCheckboxWidget(R.string.pref_switch_items_caching)).check(
|
||||||
matches(
|
matches(
|
||||||
allOf(
|
allOf(
|
||||||
isDisplayed(), not(isChecked())
|
isDisplayed(),
|
||||||
)
|
not(isChecked()),
|
||||||
)
|
),
|
||||||
|
),
|
||||||
)
|
)
|
||||||
onView(withSettingsCheckboxFrame(R.string.pref_switch_items_caching)).check(
|
onView(withSettingsCheckboxFrame(R.string.pref_switch_items_caching)).check(
|
||||||
matches(
|
matches(
|
||||||
isEnabled()
|
isEnabled(),
|
||||||
)
|
),
|
||||||
)
|
)
|
||||||
onView(withText(R.string.pref_periodic_refresh_minutes_title)).check(
|
onView(withText(R.string.pref_periodic_refresh_minutes_title)).check(
|
||||||
matches(
|
matches(
|
||||||
allOf(isNotEnabled(), isDisplayed())
|
allOf(isNotEnabled(), isDisplayed()),
|
||||||
)
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
onView(withSettingsCheckboxWidget(R.string.pref_switch_refresh_when_charging)).check(
|
onView(withSettingsCheckboxWidget(R.string.pref_switch_refresh_when_charging)).check(
|
||||||
matches(
|
matches(
|
||||||
allOf(
|
allOf(
|
||||||
isDisplayed(), not(isChecked())
|
isDisplayed(),
|
||||||
)
|
not(isChecked()),
|
||||||
)
|
),
|
||||||
|
),
|
||||||
)
|
)
|
||||||
onView(withSettingsCheckboxFrame(R.string.pref_switch_refresh_when_charging)).check(
|
onView(withSettingsCheckboxFrame(R.string.pref_switch_refresh_when_charging)).check(
|
||||||
matches(
|
matches(
|
||||||
isNotEnabled()
|
isNotEnabled(),
|
||||||
)
|
),
|
||||||
)
|
)
|
||||||
onView(withSettingsCheckboxWidget(R.string.pref_switch_notify_new_items)).check(
|
onView(withSettingsCheckboxWidget(R.string.pref_switch_notify_new_items)).check(
|
||||||
matches(
|
matches(
|
||||||
allOf(
|
allOf(
|
||||||
isDisplayed(), not(isChecked())
|
isDisplayed(),
|
||||||
)
|
not(isChecked()),
|
||||||
)
|
),
|
||||||
|
),
|
||||||
)
|
)
|
||||||
onView(withSettingsCheckboxFrame(R.string.pref_switch_notify_new_items)).check(
|
onView(withSettingsCheckboxFrame(R.string.pref_switch_notify_new_items)).check(
|
||||||
matches(
|
matches(
|
||||||
isNotEnabled()
|
isNotEnabled(),
|
||||||
)
|
),
|
||||||
)
|
)
|
||||||
onView(withSettingsCheckboxWidget(R.string.pref_switch_update_sources)).check(
|
onView(withSettingsCheckboxWidget(R.string.pref_switch_update_sources)).check(
|
||||||
matches(
|
matches(
|
||||||
allOf(
|
allOf(
|
||||||
isDisplayed(), isChecked()
|
isDisplayed(),
|
||||||
)
|
isChecked(),
|
||||||
)
|
),
|
||||||
|
),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -111,50 +114,50 @@ class SettingsActivityOfflineTest {
|
|||||||
onView(withText(R.string.pref_switch_items_caching_on)).check(matches(isDisplayed()))
|
onView(withText(R.string.pref_switch_items_caching_on)).check(matches(isDisplayed()))
|
||||||
onView(withSettingsCheckboxFrame(R.string.pref_switch_items_caching)).check(
|
onView(withSettingsCheckboxFrame(R.string.pref_switch_items_caching)).check(
|
||||||
matches(
|
matches(
|
||||||
isEnabled()
|
isEnabled(),
|
||||||
)
|
),
|
||||||
)
|
)
|
||||||
onView(withText(R.string.pref_periodic_refresh_minutes_title)).check(
|
onView(withText(R.string.pref_periodic_refresh_minutes_title)).check(
|
||||||
matches(
|
matches(
|
||||||
isNotEnabled()
|
isNotEnabled(),
|
||||||
)
|
),
|
||||||
)
|
)
|
||||||
onView(withSettingsCheckboxFrame(R.string.pref_switch_refresh_when_charging)).check(
|
onView(withSettingsCheckboxFrame(R.string.pref_switch_refresh_when_charging)).check(
|
||||||
matches(
|
matches(
|
||||||
isNotEnabled()
|
isNotEnabled(),
|
||||||
)
|
),
|
||||||
)
|
)
|
||||||
onView(withSettingsCheckboxFrame(R.string.pref_switch_notify_new_items)).check(
|
onView(withSettingsCheckboxFrame(R.string.pref_switch_notify_new_items)).check(
|
||||||
matches(
|
matches(
|
||||||
isNotEnabled()
|
isNotEnabled(),
|
||||||
)
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
onView(withText(R.string.pref_switch_periodic_refresh_off)).check(
|
onView(withText(R.string.pref_switch_periodic_refresh_off)).check(
|
||||||
matches(
|
matches(
|
||||||
isDisplayed()
|
isDisplayed(),
|
||||||
)
|
),
|
||||||
)
|
)
|
||||||
onView(withSettingsCheckboxWidget(R.string.pref_switch_periodic_refresh)).perform(click())
|
onView(withSettingsCheckboxWidget(R.string.pref_switch_periodic_refresh)).perform(click())
|
||||||
onView(withText(R.string.pref_switch_periodic_refresh_on)).check(
|
onView(withText(R.string.pref_switch_periodic_refresh_on)).check(
|
||||||
matches(
|
matches(
|
||||||
isDisplayed()
|
isDisplayed(),
|
||||||
)
|
),
|
||||||
)
|
)
|
||||||
onView(withSettingsCheckboxFrame(R.string.pref_periodic_refresh_minutes_title)).check(
|
onView(withSettingsCheckboxFrame(R.string.pref_periodic_refresh_minutes_title)).check(
|
||||||
matches(
|
matches(
|
||||||
isEnabled()
|
isEnabled(),
|
||||||
)
|
),
|
||||||
)
|
)
|
||||||
onView(withSettingsCheckboxFrame(R.string.pref_switch_refresh_when_charging)).check(
|
onView(withSettingsCheckboxFrame(R.string.pref_switch_refresh_when_charging)).check(
|
||||||
matches(
|
matches(
|
||||||
isEnabled()
|
isEnabled(),
|
||||||
)
|
),
|
||||||
)
|
)
|
||||||
onView(withSettingsCheckboxFrame(R.string.pref_switch_notify_new_items)).check(
|
onView(withSettingsCheckboxFrame(R.string.pref_switch_notify_new_items)).check(
|
||||||
matches(
|
matches(
|
||||||
isEnabled()
|
isEnabled(),
|
||||||
)
|
),
|
||||||
)
|
)
|
||||||
changeAndCancelSetting("360", "123") {
|
changeAndCancelSetting("360", "123") {
|
||||||
onView(withText(R.string.pref_periodic_refresh_minutes_title)).perform(click())
|
onView(withText(R.string.pref_periodic_refresh_minutes_title)).perform(click())
|
||||||
|
@ -19,11 +19,9 @@ 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)
|
||||||
@LargeTest
|
@LargeTest
|
||||||
class SettingsActivityReaderTest {
|
class SettingsActivityReaderTest {
|
||||||
|
|
||||||
@get:Rule
|
@get:Rule
|
||||||
val activityRule = ActivityScenarioRule(LoginActivity::class.java)
|
val activityRule = ActivityScenarioRule(LoginActivity::class.java)
|
||||||
|
|
||||||
@ -36,7 +34,7 @@ class SettingsActivityReaderTest {
|
|||||||
}
|
}
|
||||||
loginAndInitHome()
|
loginAndInitHome()
|
||||||
openActionBarOverflowOrOptionsMenu(
|
openActionBarOverflowOrOptionsMenu(
|
||||||
ApplicationProvider.getApplicationContext()
|
ApplicationProvider.getApplicationContext(),
|
||||||
)
|
)
|
||||||
onView(withText(R.string.title_activity_settings)).perform(click())
|
onView(withText(R.string.title_activity_settings)).perform(click())
|
||||||
onView(withText(R.string.pref_header_viewer)).perform(click())
|
onView(withText(R.string.pref_header_viewer)).perform(click())
|
||||||
@ -47,11 +45,12 @@ class SettingsActivityReaderTest {
|
|||||||
onView(withSettingsCheckboxFrame(R.string.pref_switch_actions_pager_scroll)).check(
|
onView(withSettingsCheckboxFrame(R.string.pref_switch_actions_pager_scroll)).check(
|
||||||
matches(
|
matches(
|
||||||
allOf(
|
allOf(
|
||||||
isDisplayed(), not(
|
isDisplayed(),
|
||||||
isChecked()
|
not(
|
||||||
)
|
isChecked(),
|
||||||
)
|
),
|
||||||
)
|
),
|
||||||
|
),
|
||||||
)
|
)
|
||||||
onView(withText(R.string.pref_content_reader_font_size)).check(matches(isDisplayed()))
|
onView(withText(R.string.pref_content_reader_font_size)).check(matches(isDisplayed()))
|
||||||
onView(withText(R.string.settings_reader_font)).check(matches(isDisplayed()))
|
onView(withText(R.string.settings_reader_font)).check(matches(isDisplayed()))
|
||||||
@ -61,14 +60,14 @@ class SettingsActivityReaderTest {
|
|||||||
fun testReaderActions() {
|
fun testReaderActions() {
|
||||||
onView(withText(R.string.pref_switch_actions_pager_scroll_off)).check(
|
onView(withText(R.string.pref_switch_actions_pager_scroll_off)).check(
|
||||||
matches(
|
matches(
|
||||||
isDisplayed()
|
isDisplayed(),
|
||||||
)
|
),
|
||||||
)
|
)
|
||||||
onView(withSettingsCheckboxFrame(R.string.pref_switch_actions_pager_scroll)).perform(click())
|
onView(withSettingsCheckboxFrame(R.string.pref_switch_actions_pager_scroll)).perform(click())
|
||||||
onView(withText(R.string.pref_switch_actions_pager_scroll_on)).check(
|
onView(withText(R.string.pref_switch_actions_pager_scroll_on)).check(
|
||||||
matches(
|
matches(
|
||||||
isDisplayed()
|
isDisplayed(),
|
||||||
)
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
onView(withText(R.string.pref_content_reader_font_size)).perform(click())
|
onView(withText(R.string.pref_content_reader_font_size)).perform(click())
|
||||||
|
@ -20,7 +20,6 @@ import org.junit.runner.RunWith
|
|||||||
@RunWith(AndroidJUnit4::class)
|
@RunWith(AndroidJUnit4::class)
|
||||||
@LargeTest
|
@LargeTest
|
||||||
class SettingsActivityTest {
|
class SettingsActivityTest {
|
||||||
|
|
||||||
@get:Rule
|
@get:Rule
|
||||||
val activityRule = ActivityScenarioRule(LoginActivity::class.java)
|
val activityRule = ActivityScenarioRule(LoginActivity::class.java)
|
||||||
lateinit var context: Context
|
lateinit var context: Context
|
||||||
@ -35,10 +34,8 @@ class SettingsActivityTest {
|
|||||||
onView(withText(R.string.title_activity_settings)).perform(click())
|
onView(withText(R.string.title_activity_settings)).perform(click())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun testAllSettings() {
|
fun testAllSettings() {
|
||||||
|
|
||||||
onView(withText(R.string.pref_header_general)).check(matches(isDisplayed()))
|
onView(withText(R.string.pref_header_general)).check(matches(isDisplayed()))
|
||||||
onView(withText(R.string.pref_header_viewer)).check(matches(isDisplayed()))
|
onView(withText(R.string.pref_header_viewer)).check(matches(isDisplayed()))
|
||||||
onView(withText(R.string.pref_header_offline)).check(matches(isDisplayed()))
|
onView(withText(R.string.pref_header_offline)).check(matches(isDisplayed()))
|
||||||
@ -48,14 +45,13 @@ class SettingsActivityTest {
|
|||||||
matches(
|
matches(
|
||||||
allOf(
|
allOf(
|
||||||
isDisplayed(),
|
isDisplayed(),
|
||||||
not(isSelected())
|
not(isSelected()),
|
||||||
)
|
),
|
||||||
)
|
),
|
||||||
)
|
)
|
||||||
onView(withText(R.string.action_about)).check(matches(isDisplayed()))
|
onView(withText(R.string.action_about)).check(matches(isDisplayed()))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun testThemes() {
|
fun testThemes() {
|
||||||
testPreferencesFromArray(context, R.array.ModeTitles) {
|
testPreferencesFromArray(context, R.array.ModeTitles) {
|
||||||
@ -63,7 +59,6 @@ class SettingsActivityTest {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun testExperimentail() {
|
fun testExperimentail() {
|
||||||
onView(withText(R.string.pref_header_experimental)).perform(click())
|
onView(withText(R.string.pref_header_experimental)).perform(click())
|
||||||
@ -75,13 +70,11 @@ class SettingsActivityTest {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun testBugReports() {
|
fun testBugReports() {
|
||||||
onView(withText(R.string.pref_switch_disable_acra)).perform(click())
|
onView(withText(R.string.pref_switch_disable_acra)).perform(click())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun testLinks() {
|
fun testLinks() {
|
||||||
onView(withText(R.string.pref_header_links)).perform(click())
|
onView(withText(R.string.pref_header_links)).perform(click())
|
||||||
@ -91,7 +84,6 @@ class SettingsActivityTest {
|
|||||||
onView(withText(R.string.translation)).check(matches(isDisplayed()))
|
onView(withText(R.string.translation)).check(matches(isDisplayed()))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun testAbout() {
|
fun testAbout() {
|
||||||
onView(withText(R.string.action_about)).perform(click())
|
onView(withText(R.string.action_about)).perform(click())
|
||||||
|
@ -41,11 +41,10 @@ class SourcesActivityTest {
|
|||||||
fun addSource() {
|
fun addSource() {
|
||||||
testAddSourceWithUrl(
|
testAddSourceWithUrl(
|
||||||
"https://lorem-rss.herokuapp.com/feed?unit=year&interval=1&length=10",
|
"https://lorem-rss.herokuapp.com/feed?unit=year&interval=1&length=10",
|
||||||
sourceName
|
sourceName,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun addSourceCheckContent() {
|
fun addSourceCheckContent() {
|
||||||
testAddSourceWithUrl("https://news.google.com/rss?hl=en-US&gl=US&ceid=US:en", sourceName)
|
testAddSourceWithUrl("https://news.google.com/rss?hl=en-US&gl=US&ceid=US:en", sourceName)
|
||||||
@ -54,7 +53,7 @@ class SourcesActivityTest {
|
|||||||
onView(withText(R.string.menu_home_refresh)).perform(click())
|
onView(withText(R.string.menu_home_refresh)).perform(click())
|
||||||
onView(withText(R.string.refresh_dialog_message)).check(matches(isDisplayed()))
|
onView(withText(R.string.refresh_dialog_message)).check(matches(isDisplayed()))
|
||||||
onView(
|
onView(
|
||||||
withId(android.R.id.button1)
|
withId(android.R.id.button1),
|
||||||
).perform(click())
|
).perform(click())
|
||||||
Thread.sleep(10000)
|
Thread.sleep(10000)
|
||||||
onView(withId(R.id.swipeRefreshLayout)).perform(swipeDown())
|
onView(withId(R.id.swipeRefreshLayout)).perform(swipeDown())
|
||||||
@ -74,7 +73,6 @@ class SourcesActivityTest {
|
|||||||
onView(withText(sourceName)).check(doesNotExist())
|
onView(withText(sourceName)).check(doesNotExist())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private fun goToSources() {
|
private fun goToSources() {
|
||||||
openMenu()
|
openMenu()
|
||||||
onView(withText(R.string.menu_home_sources))
|
onView(withText(R.string.menu_home_sources))
|
||||||
|
@ -35,7 +35,7 @@ import bou.amine.apps.readerforselfossv2.android.utils.openUrlInBrowser
|
|||||||
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.AppSettingsService
|
import bou.amine.apps.readerforselfossv2.service.AppSettingsService
|
||||||
import bou.amine.apps.readerforselfossv2.utils.ItemType
|
import bou.amine.apps.readerforselfossv2.utils.Enums.ItemType
|
||||||
import com.ashokvarma.bottomnavigation.BottomNavigationBar
|
import com.ashokvarma.bottomnavigation.BottomNavigationBar
|
||||||
import com.ashokvarma.bottomnavigation.BottomNavigationItem
|
import com.ashokvarma.bottomnavigation.BottomNavigationItem
|
||||||
import com.ashokvarma.bottomnavigation.TextBadgeItem
|
import com.ashokvarma.bottomnavigation.TextBadgeItem
|
||||||
@ -49,7 +49,10 @@ import org.kodein.di.instance
|
|||||||
import java.security.MessageDigest
|
import java.security.MessageDigest
|
||||||
import java.util.concurrent.TimeUnit
|
import java.util.concurrent.TimeUnit
|
||||||
|
|
||||||
class HomeActivity : AppCompatActivity(), SearchView.OnQueryTextListener, DIAware {
|
class HomeActivity :
|
||||||
|
AppCompatActivity(),
|
||||||
|
SearchView.OnQueryTextListener,
|
||||||
|
DIAware {
|
||||||
private var items: ArrayList<SelfossModel.Item> = ArrayList()
|
private var items: ArrayList<SelfossModel.Item> = ArrayList()
|
||||||
|
|
||||||
private var elementsShown: ItemType = ItemType.UNREAD
|
private var elementsShown: ItemType = ItemType.UNREAD
|
||||||
@ -171,7 +174,8 @@ class HomeActivity : AppCompatActivity(), SearchView.OnQueryTextListener, DIAwar
|
|||||||
getElementsAccordingToTab()
|
getElementsAccordingToTab()
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
Toast.makeText(
|
Toast
|
||||||
|
.makeText(
|
||||||
this@HomeActivity,
|
this@HomeActivity,
|
||||||
"Found null when swiping at positon $position.",
|
"Found null when swiping at positon $position.",
|
||||||
Toast.LENGTH_LONG,
|
Toast.LENGTH_LONG,
|
||||||
@ -200,15 +204,18 @@ class HomeActivity : AppCompatActivity(), SearchView.OnQueryTextListener, DIAwar
|
|||||||
tabNewBadge =
|
tabNewBadge =
|
||||||
TextBadgeItem()
|
TextBadgeItem()
|
||||||
.setText("")
|
.setText("")
|
||||||
.setHideOnSelect(false).hide(false)
|
.setHideOnSelect(false)
|
||||||
|
.hide(false)
|
||||||
tabArchiveBadge =
|
tabArchiveBadge =
|
||||||
TextBadgeItem()
|
TextBadgeItem()
|
||||||
.setText("")
|
.setText("")
|
||||||
.setHideOnSelect(false).hide(false)
|
.setHideOnSelect(false)
|
||||||
|
.hide(false)
|
||||||
tabStarredBadge =
|
tabStarredBadge =
|
||||||
TextBadgeItem()
|
TextBadgeItem()
|
||||||
.setText("")
|
.setText("")
|
||||||
.setHideOnSelect(false).hide(false)
|
.setHideOnSelect(false)
|
||||||
|
.hide(false)
|
||||||
|
|
||||||
if (appSettingsService.isDisplayUnreadCountEnabled()) {
|
if (appSettingsService.isDisplayUnreadCountEnabled()) {
|
||||||
lifecycleScope.launch {
|
lifecycleScope.launch {
|
||||||
@ -236,14 +243,12 @@ class HomeActivity : AppCompatActivity(), SearchView.OnQueryTextListener, DIAwar
|
|||||||
BottomNavigationItem(
|
BottomNavigationItem(
|
||||||
R.drawable.ic_tab_fiber_new_black_24dp,
|
R.drawable.ic_tab_fiber_new_black_24dp,
|
||||||
getString(R.string.tab_new),
|
getString(R.string.tab_new),
|
||||||
)
|
).setBadgeItem(tabNewBadge)
|
||||||
.setBadgeItem(tabNewBadge)
|
|
||||||
val tabArchive =
|
val tabArchive =
|
||||||
BottomNavigationItem(
|
BottomNavigationItem(
|
||||||
R.drawable.ic_tab_archive_black_24dp,
|
R.drawable.ic_tab_archive_black_24dp,
|
||||||
getString(R.string.tab_read),
|
getString(R.string.tab_read),
|
||||||
)
|
).setBadgeItem(tabArchiveBadge)
|
||||||
.setBadgeItem(tabArchiveBadge)
|
|
||||||
val tabStarred =
|
val tabStarred =
|
||||||
BottomNavigationItem(
|
BottomNavigationItem(
|
||||||
R.drawable.ic_tab_favorite_black_24dp,
|
R.drawable.ic_tab_favorite_black_24dp,
|
||||||
@ -425,17 +430,17 @@ class HomeActivity : AppCompatActivity(), SearchView.OnQueryTextListener, DIAwar
|
|||||||
binding.recyclerView.addOnScrollListener(recyclerViewScrollListener)
|
binding.recyclerView.addOnScrollListener(recyclerViewScrollListener)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun getLastVisibleItem(): Int {
|
private fun getLastVisibleItem(): Int =
|
||||||
return when (val manager = binding.recyclerView.layoutManager) {
|
when (val manager = binding.recyclerView.layoutManager) {
|
||||||
is StaggeredGridLayoutManager ->
|
is StaggeredGridLayoutManager ->
|
||||||
manager.findLastCompletelyVisibleItemPositions(
|
manager
|
||||||
|
.findLastCompletelyVisibleItemPositions(
|
||||||
null,
|
null,
|
||||||
).last()
|
).last()
|
||||||
|
|
||||||
is GridLayoutManager -> manager.findLastCompletelyVisibleItemPosition()
|
is GridLayoutManager -> manager.findLastCompletelyVisibleItemPosition()
|
||||||
else -> 0
|
else -> 0
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
private fun mayBeEmpty() =
|
private fun mayBeEmpty() =
|
||||||
if (items.isEmpty()) {
|
if (items.isEmpty()) {
|
||||||
@ -577,7 +582,8 @@ class HomeActivity : AppCompatActivity(), SearchView.OnQueryTextListener, DIAwar
|
|||||||
messageRes: Int,
|
messageRes: Int,
|
||||||
doFn: () -> Unit,
|
doFn: () -> Unit,
|
||||||
) {
|
) {
|
||||||
AlertDialog.Builder(this@HomeActivity)
|
AlertDialog
|
||||||
|
.Builder(this@HomeActivity)
|
||||||
.setMessage(messageRes)
|
.setMessage(messageRes)
|
||||||
.setTitle(titleRes)
|
.setTitle(titleRes)
|
||||||
.setPositiveButton(android.R.string.ok) { _, _ -> doFn() }
|
.setPositiveButton(android.R.string.ok) { _, _ -> doFn() }
|
||||||
@ -589,7 +595,7 @@ class HomeActivity : AppCompatActivity(), SearchView.OnQueryTextListener, DIAwar
|
|||||||
override fun onOptionsItemSelected(item: MenuItem): Boolean {
|
override fun onOptionsItemSelected(item: MenuItem): Boolean {
|
||||||
when (item.itemId) {
|
when (item.itemId) {
|
||||||
R.id.issue_tracker -> {
|
R.id.issue_tracker -> {
|
||||||
baseContext.openUrlInBrowser(AppSettingsService.trackerUrl)
|
baseContext.openUrlInBrowser(AppSettingsService.TRACKER_URL)
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -606,14 +612,15 @@ class HomeActivity : AppCompatActivity(), SearchView.OnQueryTextListener, DIAwar
|
|||||||
CoroutineScope(Dispatchers.Main).launch {
|
CoroutineScope(Dispatchers.Main).launch {
|
||||||
val updatedRemote = repository.updateRemote()
|
val updatedRemote = repository.updateRemote()
|
||||||
if (updatedRemote) {
|
if (updatedRemote) {
|
||||||
Toast.makeText(
|
Toast
|
||||||
|
.makeText(
|
||||||
this@HomeActivity,
|
this@HomeActivity,
|
||||||
R.string.refresh_success_response,
|
R.string.refresh_success_response,
|
||||||
Toast.LENGTH_LONG,
|
Toast.LENGTH_LONG,
|
||||||
)
|
).show()
|
||||||
.show()
|
|
||||||
} else {
|
} else {
|
||||||
Toast.makeText(
|
Toast
|
||||||
|
.makeText(
|
||||||
this@HomeActivity,
|
this@HomeActivity,
|
||||||
R.string.refresh_failer_message,
|
R.string.refresh_failer_message,
|
||||||
Toast.LENGTH_SHORT,
|
Toast.LENGTH_SHORT,
|
||||||
@ -633,7 +640,8 @@ class HomeActivity : AppCompatActivity(), SearchView.OnQueryTextListener, DIAwar
|
|||||||
CoroutineScope(Dispatchers.Main).launch {
|
CoroutineScope(Dispatchers.Main).launch {
|
||||||
val success = repository.markAllAsRead(items)
|
val success = repository.markAllAsRead(items)
|
||||||
if (success) {
|
if (success) {
|
||||||
Toast.makeText(
|
Toast
|
||||||
|
.makeText(
|
||||||
this@HomeActivity,
|
this@HomeActivity,
|
||||||
R.string.all_posts_read,
|
R.string.all_posts_read,
|
||||||
Toast.LENGTH_SHORT,
|
Toast.LENGTH_SHORT,
|
||||||
@ -642,7 +650,8 @@ class HomeActivity : AppCompatActivity(), SearchView.OnQueryTextListener, DIAwar
|
|||||||
|
|
||||||
getElementsAccordingToTab()
|
getElementsAccordingToTab()
|
||||||
} else {
|
} else {
|
||||||
Toast.makeText(
|
Toast
|
||||||
|
.makeText(
|
||||||
this@HomeActivity,
|
this@HomeActivity,
|
||||||
R.string.all_posts_not_read,
|
R.string.all_posts_not_read,
|
||||||
Toast.LENGTH_SHORT,
|
Toast.LENGTH_SHORT,
|
||||||
@ -651,7 +660,6 @@ class HomeActivity : AppCompatActivity(), SearchView.OnQueryTextListener, DIAwar
|
|||||||
handleListResult()
|
handleListResult()
|
||||||
binding.swipeRefreshLayout.isRefreshing = false
|
binding.swipeRefreshLayout.isRefreshing = false
|
||||||
CountingIdlingResourceSingleton.decrement()
|
CountingIdlingResourceSingleton.decrement()
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -661,7 +669,7 @@ class HomeActivity : AppCompatActivity(), SearchView.OnQueryTextListener, DIAwar
|
|||||||
R.id.action_disconnect -> {
|
R.id.action_disconnect -> {
|
||||||
needsConfirmation(
|
needsConfirmation(
|
||||||
R.string.confirm_disconnect_title,
|
R.string.confirm_disconnect_title,
|
||||||
R.string.confirm_disconnect_description
|
R.string.confirm_disconnect_description,
|
||||||
) {
|
) {
|
||||||
runBlocking {
|
runBlocking {
|
||||||
repository.logout()
|
repository.logout()
|
||||||
@ -702,7 +710,8 @@ class HomeActivity : AppCompatActivity(), SearchView.OnQueryTextListener, DIAwar
|
|||||||
private fun handleRecurringTask() {
|
private fun handleRecurringTask() {
|
||||||
if (appSettingsService.isPeriodicRefreshEnabled()) {
|
if (appSettingsService.isPeriodicRefreshEnabled()) {
|
||||||
val myConstraints =
|
val myConstraints =
|
||||||
Constraints.Builder()
|
Constraints
|
||||||
|
.Builder()
|
||||||
.setRequiresBatteryNotLow(true)
|
.setRequiresBatteryNotLow(true)
|
||||||
.setRequiresCharging(appSettingsService.isRefreshWhenChargingOnlyEnabled())
|
.setRequiresCharging(appSettingsService.isRefreshWhenChargingOnlyEnabled())
|
||||||
.setRequiresStorageNotLow(true)
|
.setRequiresStorageNotLow(true)
|
||||||
@ -711,18 +720,18 @@ class HomeActivity : AppCompatActivity(), SearchView.OnQueryTextListener, DIAwar
|
|||||||
val backgroundWork =
|
val backgroundWork =
|
||||||
PeriodicWorkRequestBuilder<LoadingWorker>(
|
PeriodicWorkRequestBuilder<LoadingWorker>(
|
||||||
appSettingsService.getRefreshMinutes(),
|
appSettingsService.getRefreshMinutes(),
|
||||||
TimeUnit.MINUTES
|
TimeUnit.MINUTES,
|
||||||
)
|
).setConstraints(myConstraints)
|
||||||
.setConstraints(myConstraints)
|
|
||||||
.addTag("selfoss-loading")
|
.addTag("selfoss-loading")
|
||||||
.build()
|
.build()
|
||||||
|
|
||||||
WorkManager.getInstance(
|
WorkManager
|
||||||
|
.getInstance(
|
||||||
baseContext,
|
baseContext,
|
||||||
).enqueueUniquePeriodicWork(
|
).enqueueUniquePeriodicWork(
|
||||||
"selfoss-loading",
|
"selfoss-loading",
|
||||||
ExistingPeriodicWorkPolicy.KEEP,
|
ExistingPeriodicWorkPolicy.KEEP,
|
||||||
backgroundWork
|
backgroundWork,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -30,7 +30,9 @@ import org.kodein.di.DIAware
|
|||||||
import org.kodein.di.android.closestDI
|
import org.kodein.di.android.closestDI
|
||||||
import org.kodein.di.instance
|
import org.kodein.di.instance
|
||||||
|
|
||||||
class LoginActivity : AppCompatActivity(), DIAware {
|
class LoginActivity :
|
||||||
|
AppCompatActivity(),
|
||||||
|
DIAware {
|
||||||
private var inValidCount: Int = 0
|
private var inValidCount: Int = 0
|
||||||
private var isWithLogin = false
|
private var isWithLogin = false
|
||||||
|
|
||||||
@ -108,7 +110,7 @@ class LoginActivity : AppCompatActivity(), DIAware {
|
|||||||
repository.updateApiInformation()
|
repository.updateApiInformation()
|
||||||
ACRA.errorReporter.putCustomData(
|
ACRA.errorReporter.putCustomData(
|
||||||
"SELFOSS_API_VERSION",
|
"SELFOSS_API_VERSION",
|
||||||
appSettingsService.getApiVersion().toString()
|
appSettingsService.getApiVersion().toString(),
|
||||||
)
|
)
|
||||||
CountingIdlingResourceSingleton.decrement()
|
CountingIdlingResourceSingleton.decrement()
|
||||||
}
|
}
|
||||||
@ -151,7 +153,8 @@ class LoginActivity : AppCompatActivity(), DIAware {
|
|||||||
repository.updateApiInformation()
|
repository.updateApiInformation()
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
if (e.message?.startsWith("No transformation found") == true) {
|
if (e.message?.startsWith("No transformation found") == true) {
|
||||||
Toast.makeText(
|
Toast
|
||||||
|
.makeText(
|
||||||
applicationContext,
|
applicationContext,
|
||||||
R.string.application_selfoss_only,
|
R.string.application_selfoss_only,
|
||||||
Toast.LENGTH_LONG,
|
Toast.LENGTH_LONG,
|
||||||
@ -270,7 +273,7 @@ class LoginActivity : AppCompatActivity(), DIAware {
|
|||||||
return when (item.itemId) {
|
return when (item.itemId) {
|
||||||
R.id.issue_tracker -> {
|
R.id.issue_tracker -> {
|
||||||
val browserIntent =
|
val browserIntent =
|
||||||
Intent(Intent.ACTION_VIEW, Uri.parse(AppSettingsService.trackerUrl))
|
Intent(Intent.ACTION_VIEW, Uri.parse(AppSettingsService.TRACKER_URL))
|
||||||
startActivity(browserIntent)
|
startActivity(browserIntent)
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
@ -280,9 +283,9 @@ class LoginActivity : AppCompatActivity(), DIAware {
|
|||||||
.withAboutIconShown(true)
|
.withAboutIconShown(true)
|
||||||
.withAboutVersionShown(true)
|
.withAboutVersionShown(true)
|
||||||
.withAboutSpecial2("Bug reports")
|
.withAboutSpecial2("Bug reports")
|
||||||
.withAboutSpecial2Description(AppSettingsService.trackerUrl)
|
.withAboutSpecial2Description(AppSettingsService.TRACKER_URL)
|
||||||
.withAboutSpecial1("Project Page")
|
.withAboutSpecial1("Project Page")
|
||||||
.withAboutSpecial1Description(AppSettingsService.sourceUrl)
|
.withAboutSpecial1Description(AppSettingsService.SOURCE_URL)
|
||||||
.start(this)
|
.start(this)
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
|
@ -9,11 +9,11 @@ import androidx.lifecycle.DefaultLifecycleObserver
|
|||||||
import androidx.lifecycle.LifecycleOwner
|
import androidx.lifecycle.LifecycleOwner
|
||||||
import androidx.lifecycle.ProcessLifecycleOwner
|
import androidx.lifecycle.ProcessLifecycleOwner
|
||||||
import androidx.multidex.MultiDexApplication
|
import androidx.multidex.MultiDexApplication
|
||||||
import bou.amine.apps.readerforselfossv2.DI.networkModule
|
|
||||||
import bou.amine.apps.readerforselfossv2.android.testing.TestingHelper
|
import bou.amine.apps.readerforselfossv2.android.testing.TestingHelper
|
||||||
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.di.networkModule
|
||||||
import bou.amine.apps.readerforselfossv2.repository.Repository
|
import bou.amine.apps.readerforselfossv2.repository.Repository
|
||||||
import bou.amine.apps.readerforselfossv2.service.AppSettingsService
|
import bou.amine.apps.readerforselfossv2.service.AppSettingsService
|
||||||
import com.github.ln_12.library.ConnectivityStatus
|
import com.github.ln_12.library.ConnectivityStatus
|
||||||
@ -36,7 +36,9 @@ 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
|
||||||
|
|
||||||
class MyApp : MultiDexApplication(), DIAware {
|
class MyApp :
|
||||||
|
MultiDexApplication(),
|
||||||
|
DIAware {
|
||||||
override val di by DI.lazy {
|
override val di by DI.lazy {
|
||||||
bind<AppSettingsService>() with singleton { AppSettingsService(ACRA.isACRASenderServiceProcess() || TestingHelper().isUnitTest()) }
|
bind<AppSettingsService>() with singleton { AppSettingsService(ACRA.isACRASenderServiceProcess() || TestingHelper().isUnitTest()) }
|
||||||
import(networkModule)
|
import(networkModule)
|
||||||
@ -89,7 +91,8 @@ class MyApp : MultiDexApplication(), DIAware {
|
|||||||
R.string.network_connectivity_lost
|
R.string.network_connectivity_lost
|
||||||
}
|
}
|
||||||
|
|
||||||
Toast.makeText(
|
Toast
|
||||||
|
.makeText(
|
||||||
applicationContext,
|
applicationContext,
|
||||||
toastMessage,
|
toastMessage,
|
||||||
Toast.LENGTH_SHORT,
|
Toast.LENGTH_SHORT,
|
||||||
@ -151,13 +154,13 @@ 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(AppSettingsService.syncChannelId, name, importance)
|
val mChannel = NotificationChannel(AppSettingsService.SYNC_CHANNEL_ID, 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 =
|
val newItemsChannelmChannel =
|
||||||
NotificationChannel(
|
NotificationChannel(
|
||||||
AppSettingsService.newItemsChannelId,
|
AppSettingsService.NEW_ITEMS_CHANNEL_ID,
|
||||||
newItemsChannelname,
|
newItemsChannelname,
|
||||||
newItemsChannelimportance,
|
newItemsChannelimportance,
|
||||||
)
|
)
|
||||||
|
@ -103,8 +103,7 @@ class ReaderActivity : AppCompatActivity(), DIAware {
|
|||||||
FragmentStateAdapter(fa) {
|
FragmentStateAdapter(fa) {
|
||||||
override fun getItemCount(): Int = allItems.size
|
override fun getItemCount(): Int = allItems.size
|
||||||
|
|
||||||
override fun createFragment(position: Int): Fragment =
|
override fun createFragment(position: Int): Fragment = ArticleFragment.newInstance(allItems[position])
|
||||||
ArticleFragment.newInstance(allItems[position])
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onKeyDown(
|
override fun onKeyDown(
|
||||||
|
@ -49,7 +49,10 @@ class ItemCardAdapter(
|
|||||||
return ViewHolder(binding)
|
return ViewHolder(binding)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun handleClickListeners(holderBinding: CardItemBinding, position: Int) {
|
private fun handleClickListeners(
|
||||||
|
holderBinding: CardItemBinding,
|
||||||
|
position: Int,
|
||||||
|
) {
|
||||||
holderBinding.favButton.setOnClickListener {
|
holderBinding.favButton.setOnClickListener {
|
||||||
val item = items[position]
|
val item = items[position]
|
||||||
if (item.starred) {
|
if (item.starred) {
|
||||||
@ -96,7 +99,8 @@ class ItemCardAdapter(
|
|||||||
|
|
||||||
binding.title.setLinkTextColor(c.resources.getColor(R.color.colorAccent))
|
binding.title.setLinkTextColor(c.resources.getColor(R.color.colorAccent))
|
||||||
|
|
||||||
binding.sourceTitleAndDate.text = try {
|
binding.sourceTitleAndDate.text =
|
||||||
|
try {
|
||||||
itm.sourceAuthorAndDate()
|
itm.sourceAuthorAndDate()
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
e.sendSilentlyWithAcraWithName("ItemCardAdapter parse date")
|
e.sendSilentlyWithAcraWithName("ItemCardAdapter parse date")
|
||||||
|
@ -53,7 +53,8 @@ class ItemListAdapter(
|
|||||||
|
|
||||||
binding.title.setLinkTextColor(c.resources.getColor(R.color.colorAccent))
|
binding.title.setLinkTextColor(c.resources.getColor(R.color.colorAccent))
|
||||||
|
|
||||||
binding.sourceTitleAndDate.text = try {
|
binding.sourceTitleAndDate.text =
|
||||||
|
try {
|
||||||
itm.sourceAuthorAndDate()
|
itm.sourceAuthorAndDate()
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
e.sendSilentlyWithAcraWithName("ItemListAdapter parse date")
|
e.sendSilentlyWithAcraWithName("ItemListAdapter parse date")
|
||||||
|
@ -11,14 +11,16 @@ import bou.amine.apps.readerforselfossv2.android.utils.openItemUrl
|
|||||||
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.AppSettingsService
|
import bou.amine.apps.readerforselfossv2.service.AppSettingsService
|
||||||
import bou.amine.apps.readerforselfossv2.utils.ItemType
|
import bou.amine.apps.readerforselfossv2.utils.Enums.ItemType
|
||||||
import com.google.android.material.snackbar.Snackbar
|
import com.google.android.material.snackbar.Snackbar
|
||||||
import kotlinx.coroutines.CoroutineScope
|
import kotlinx.coroutines.CoroutineScope
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import org.kodein.di.DIAware
|
import org.kodein.di.DIAware
|
||||||
|
|
||||||
abstract class ItemsAdapter<VH : RecyclerView.ViewHolder?> : RecyclerView.Adapter<VH>(), DIAware {
|
abstract class ItemsAdapter<VH : RecyclerView.ViewHolder?> :
|
||||||
|
RecyclerView.Adapter<VH>(),
|
||||||
|
DIAware {
|
||||||
abstract val items: ArrayList<SelfossModel.Item>
|
abstract val items: ArrayList<SelfossModel.Item>
|
||||||
abstract val repository: Repository
|
abstract val repository: Repository
|
||||||
abstract val binding: ViewBinding
|
abstract val binding: ViewBinding
|
||||||
@ -45,8 +47,7 @@ abstract class ItemsAdapter<VH : RecyclerView.ViewHolder?> : RecyclerView.Adapte
|
|||||||
app.findViewById(R.id.coordLayout),
|
app.findViewById(R.id.coordLayout),
|
||||||
R.string.marked_as_read,
|
R.string.marked_as_read,
|
||||||
Snackbar.LENGTH_LONG,
|
Snackbar.LENGTH_LONG,
|
||||||
)
|
).setAction(R.string.undo_string) {
|
||||||
.setAction(R.string.undo_string) {
|
|
||||||
unreadItemAtIndex(item, position, false)
|
unreadItemAtIndex(item, position, false)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -66,8 +67,7 @@ abstract class ItemsAdapter<VH : RecyclerView.ViewHolder?> : RecyclerView.Adapte
|
|||||||
app.findViewById(R.id.coordLayout),
|
app.findViewById(R.id.coordLayout),
|
||||||
R.string.marked_as_unread,
|
R.string.marked_as_unread,
|
||||||
Snackbar.LENGTH_LONG,
|
Snackbar.LENGTH_LONG,
|
||||||
)
|
).setAction(R.string.undo_string) {
|
||||||
.setAction(R.string.undo_string) {
|
|
||||||
readItemAtIndex(item, position, false)
|
readItemAtIndex(item, position, false)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -77,7 +77,10 @@ abstract class ItemsAdapter<VH : RecyclerView.ViewHolder?> : RecyclerView.Adapte
|
|||||||
s.show()
|
s.show()
|
||||||
}
|
}
|
||||||
|
|
||||||
protected fun handleLinkOpening(holderBinding: ViewBinding, position: Int) {
|
protected fun handleLinkOpening(
|
||||||
|
holderBinding: ViewBinding,
|
||||||
|
position: Int,
|
||||||
|
) {
|
||||||
holderBinding.root.setOnClickListener {
|
holderBinding.root.setOnClickListener {
|
||||||
repository.setReaderItems(items)
|
repository.setReaderItems(items)
|
||||||
c.openItemUrl(
|
c.openItemUrl(
|
||||||
|
@ -23,11 +23,13 @@ import kotlinx.coroutines.Dispatchers
|
|||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import org.kodein.di.DIAware
|
import org.kodein.di.DIAware
|
||||||
import org.kodein.di.instance
|
import org.kodein.di.instance
|
||||||
import java.util.*
|
import java.util.Timer
|
||||||
import kotlin.concurrent.schedule
|
import kotlin.concurrent.schedule
|
||||||
|
|
||||||
class LoadingWorker(val context: Context, params: WorkerParameters) :
|
class LoadingWorker(
|
||||||
Worker(context, params),
|
val context: Context,
|
||||||
|
params: WorkerParameters,
|
||||||
|
) : Worker(context, params),
|
||||||
DIAware {
|
DIAware {
|
||||||
override val di by lazy { (applicationContext as MyApp).di }
|
override val di by lazy { (applicationContext as MyApp).di }
|
||||||
private val repository: Repository by instance()
|
private val repository: Repository by instance()
|
||||||
@ -40,12 +42,13 @@ class LoadingWorker(val context: Context, params: WorkerParameters) :
|
|||||||
applicationContext.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
|
applicationContext.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
|
||||||
|
|
||||||
val notification =
|
val notification =
|
||||||
NotificationCompat.Builder(applicationContext, AppSettingsService.syncChannelId)
|
NotificationCompat
|
||||||
|
.Builder(applicationContext, AppSettingsService.SYNC_CHANNEL_ID)
|
||||||
.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(AppSettingsService.syncChannelId)
|
.setChannelId(AppSettingsService.SYNC_CHANNEL_ID)
|
||||||
.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())
|
||||||
@ -87,19 +90,18 @@ class LoadingWorker(val context: Context, params: WorkerParameters) :
|
|||||||
PendingIntent.getActivity(context, 0, intent, pflags)
|
PendingIntent.getActivity(context, 0, intent, pflags)
|
||||||
|
|
||||||
val newItemsNotification =
|
val newItemsNotification =
|
||||||
NotificationCompat.Builder(
|
NotificationCompat
|
||||||
|
.Builder(
|
||||||
applicationContext,
|
applicationContext,
|
||||||
AppSettingsService.newItemsChannelId,
|
AppSettingsService.NEW_ITEMS_CHANNEL_ID,
|
||||||
)
|
).setContentTitle(context.getString(R.string.new_items_notification_title))
|
||||||
.setContentTitle(context.getString(R.string.new_items_notification_title))
|
|
||||||
.setContentText(
|
.setContentText(
|
||||||
context.getString(
|
context.getString(
|
||||||
R.string.new_items_notification_text,
|
R.string.new_items_notification_text,
|
||||||
newSize,
|
newSize,
|
||||||
),
|
),
|
||||||
)
|
).setPriority(PRIORITY_DEFAULT)
|
||||||
.setPriority(PRIORITY_DEFAULT)
|
.setChannelId(AppSettingsService.NEW_ITEMS_CHANNEL_ID)
|
||||||
.setChannelId(AppSettingsService.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)
|
@ -115,7 +115,8 @@ class ArticleFragment : Fragment(), DIAware {
|
|||||||
contentText = item.content
|
contentText = item.content
|
||||||
contentTitle = item.title.getHtmlDecoded()
|
contentTitle = item.title.getHtmlDecoded()
|
||||||
contentImage = item.getThumbnail(repository.baseUrl)
|
contentImage = item.getThumbnail(repository.baseUrl)
|
||||||
contentSource = try {
|
contentSource =
|
||||||
|
try {
|
||||||
item.sourceAuthorAndDate()
|
item.sourceAuthorAndDate()
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
e.sendSilentlyWithAcraWithName("Article Fragment parse date")
|
e.sendSilentlyWithAcraWithName("Article Fragment parse date")
|
||||||
@ -337,7 +338,10 @@ class ArticleFragment : Fragment(), DIAware {
|
|||||||
view: WebView?,
|
view: WebView?,
|
||||||
url: String,
|
url: String,
|
||||||
): Boolean {
|
): Boolean {
|
||||||
return if (context != null && url.isUrlValid() && binding.webcontent.hitTestResult.type != WebView.HitTestResult.SRC_IMAGE_ANCHOR_TYPE) {
|
return if (context != null &&
|
||||||
|
url.isUrlValid() &&
|
||||||
|
binding.webcontent.hitTestResult.type != WebView.HitTestResult.SRC_IMAGE_ANCHOR_TYPE
|
||||||
|
) {
|
||||||
requireContext().openUrlInBrowser(url)
|
requireContext().openUrlInBrowser(url)
|
||||||
true
|
true
|
||||||
} else {
|
} else {
|
||||||
@ -422,7 +426,6 @@ class ArticleFragment : Fragment(), DIAware {
|
|||||||
context.theme.resolveAttribute(R.attr.colorOnSurface, colorOnSurface, true)
|
context.theme.resolveAttribute(R.attr.colorOnSurface, colorOnSurface, true)
|
||||||
|
|
||||||
context.theme.resolveAttribute(R.attr.colorSurface, colorSurface, true)
|
context.theme.resolveAttribute(R.attr.colorSurface, colorSurface, true)
|
||||||
|
|
||||||
} catch (e: IllegalStateException) {
|
} catch (e: IllegalStateException) {
|
||||||
e.sendSilentlyWithAcraWithName("Context issue when setting attributes, but context wasn't null before")
|
e.sendSilentlyWithAcraWithName("Context issue when setting attributes, but context wasn't null before")
|
||||||
}
|
}
|
||||||
@ -458,7 +461,7 @@ class ArticleFragment : Fragment(), DIAware {
|
|||||||
|
|
||||||
binding.webcontent.setOnTouchListener { _, event ->
|
binding.webcontent.setOnTouchListener { _, event ->
|
||||||
gestureDetector.onTouchEvent(
|
gestureDetector.onTouchEvent(
|
||||||
event
|
event,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -64,15 +64,14 @@ class SettingsActivity :
|
|||||||
outState.putCharSequence(TITLE_TAG, title)
|
outState.putCharSequence(TITLE_TAG, title)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onSupportNavigateUp(): Boolean {
|
override fun onSupportNavigateUp(): Boolean =
|
||||||
return if (supportFragmentManager.popBackStackImmediate()) {
|
if (supportFragmentManager.popBackStackImmediate()) {
|
||||||
supportActionBar?.title = getText(R.string.title_activity_settings)
|
supportActionBar?.title = getText(R.string.title_activity_settings)
|
||||||
false
|
false
|
||||||
} else {
|
} else {
|
||||||
super.onBackPressed()
|
super.onBackPressed()
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
override fun onPreferenceStartFragment(
|
override fun onPreferenceStartFragment(
|
||||||
caller: PreferenceFragmentCompat,
|
caller: PreferenceFragmentCompat,
|
||||||
@ -81,7 +80,8 @@ class SettingsActivity :
|
|||||||
// Instantiate the new Fragment
|
// Instantiate the new Fragment
|
||||||
val args = pref.extras
|
val args = pref.extras
|
||||||
val fragment =
|
val fragment =
|
||||||
supportFragmentManager.fragmentFactory.instantiate(
|
supportFragmentManager.fragmentFactory
|
||||||
|
.instantiate(
|
||||||
classLoader,
|
classLoader,
|
||||||
pref.fragment.toString(),
|
pref.fragment.toString(),
|
||||||
).apply {
|
).apply {
|
||||||
@ -89,7 +89,8 @@ class SettingsActivity :
|
|||||||
setTargetFragment(caller, 0)
|
setTargetFragment(caller, 0)
|
||||||
}
|
}
|
||||||
// Replace the existing Fragment with the new Fragment
|
// Replace the existing Fragment with the new Fragment
|
||||||
supportFragmentManager.beginTransaction()
|
supportFragmentManager
|
||||||
|
.beginTransaction()
|
||||||
.replace(R.id.settings, fragment)
|
.replace(R.id.settings, fragment)
|
||||||
.addToBackStack(null)
|
.addToBackStack(null)
|
||||||
.commit()
|
.commit()
|
||||||
@ -108,7 +109,7 @@ class SettingsActivity :
|
|||||||
preferenceManager.findPreference<Preference>(CURRENT_THEME)?.onPreferenceChangeListener =
|
preferenceManager.findPreference<Preference>(CURRENT_THEME)?.onPreferenceChangeListener =
|
||||||
Preference.OnPreferenceChangeListener { _, newValue ->
|
Preference.OnPreferenceChangeListener { _, newValue ->
|
||||||
AppCompatDelegate.setDefaultNightMode(
|
AppCompatDelegate.setDefaultNightMode(
|
||||||
newValue.toString().toInt()
|
newValue.toString().toInt(),
|
||||||
) // ListPreference Only takes string-arrays ¯\_(ツ)_/¯
|
) // ListPreference Only takes string-arrays ¯\_(ツ)_/¯
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
@ -144,10 +145,11 @@ class SettingsActivity :
|
|||||||
val input: Int = (dest.toString() + source.toString()).toInt()
|
val input: Int = (dest.toString() + source.toString()).toInt()
|
||||||
if (input in 1..200) return@InputFilter null
|
if (input in 1..200) return@InputFilter null
|
||||||
} catch (nfe: NumberFormatException) {
|
} catch (nfe: NumberFormatException) {
|
||||||
Toast.makeText(
|
Toast
|
||||||
|
.makeText(
|
||||||
activity,
|
activity,
|
||||||
R.string.items_number_should_be_number,
|
R.string.items_number_should_be_number,
|
||||||
Toast.LENGTH_LONG
|
Toast.LENGTH_LONG,
|
||||||
).show()
|
).show()
|
||||||
}
|
}
|
||||||
""
|
""
|
||||||
@ -234,19 +236,19 @@ class SettingsActivity :
|
|||||||
|
|
||||||
preferenceManager.findPreference<Preference>("trackerLink")?.onPreferenceClickListener =
|
preferenceManager.findPreference<Preference>("trackerLink")?.onPreferenceClickListener =
|
||||||
Preference.OnPreferenceClickListener {
|
Preference.OnPreferenceClickListener {
|
||||||
openUrl(AppSettingsService.trackerUrl)
|
openUrl(AppSettingsService.TRACKER_URL)
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
|
|
||||||
preferenceManager.findPreference<Preference>("sourceLink")?.onPreferenceClickListener =
|
preferenceManager.findPreference<Preference>("sourceLink")?.onPreferenceClickListener =
|
||||||
Preference.OnPreferenceClickListener {
|
Preference.OnPreferenceClickListener {
|
||||||
openUrl(AppSettingsService.sourceUrl)
|
openUrl(AppSettingsService.SOURCE_URL)
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
|
|
||||||
preferenceManager.findPreference<Preference>("translation")?.onPreferenceClickListener =
|
preferenceManager.findPreference<Preference>("translation")?.onPreferenceClickListener =
|
||||||
Preference.OnPreferenceClickListener {
|
Preference.OnPreferenceClickListener {
|
||||||
openUrl(AppSettingsService.translationUrl)
|
openUrl(AppSettingsService.TRANSLATION_URL)
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -3,7 +3,6 @@ package bou.amine.apps.readerforselfossv2.android.testing
|
|||||||
import androidx.test.espresso.idling.CountingIdlingResource
|
import androidx.test.espresso.idling.CountingIdlingResource
|
||||||
|
|
||||||
object CountingIdlingResourceSingleton {
|
object CountingIdlingResourceSingleton {
|
||||||
|
|
||||||
private const val RESOURCE = "GLOBAL"
|
private const val RESOURCE = "GLOBAL"
|
||||||
|
|
||||||
@JvmField
|
@JvmField
|
||||||
|
@ -2,7 +2,6 @@ package bou.amine.apps.readerforselfossv2.android.testing
|
|||||||
|
|
||||||
import android.os.Build
|
import android.os.Build
|
||||||
|
|
||||||
|
|
||||||
class TestingHelper {
|
class TestingHelper {
|
||||||
fun isUnitTest(): Boolean {
|
fun isUnitTest(): Boolean {
|
||||||
var device = Build.DEVICE
|
var device = Build.DEVICE
|
||||||
|
@ -18,7 +18,6 @@ import bou.amine.apps.readerforselfossv2.model.SelfossModel
|
|||||||
import bou.amine.apps.readerforselfossv2.utils.toStringUriWithHttp
|
import bou.amine.apps.readerforselfossv2.utils.toStringUriWithHttp
|
||||||
import okhttp3.HttpUrl.Companion.toHttpUrlOrNull
|
import okhttp3.HttpUrl.Companion.toHttpUrlOrNull
|
||||||
|
|
||||||
|
|
||||||
fun Context.openItemUrl(
|
fun Context.openItemUrl(
|
||||||
currentItem: Int,
|
currentItem: Int,
|
||||||
linkDecoded: String,
|
linkDecoded: String,
|
||||||
@ -42,8 +41,7 @@ fun Context.openItemUrl(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun String.isUrlValid(): Boolean =
|
fun String.isUrlValid(): Boolean = this.toHttpUrlOrNull() != null && Patterns.WEB_URL.matcher(this).matches()
|
||||||
this.toHttpUrlOrNull() != null && Patterns.WEB_URL.matcher(this).matches()
|
|
||||||
|
|
||||||
fun String.isBaseUrlInvalid(): Boolean {
|
fun String.isBaseUrlInvalid(): Boolean {
|
||||||
val baseUrl = this.toHttpUrlOrNull()
|
val baseUrl = this.toHttpUrlOrNull()
|
||||||
@ -61,7 +59,6 @@ fun Context.openItemUrlInBrowserAsNewTask(i: SelfossModel.Item) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun Context.openUrlInBrowserAsNewTask(url: String) {
|
fun Context.openUrlInBrowserAsNewTask(url: String) {
|
||||||
|
|
||||||
val intent = Intent(Intent.ACTION_VIEW)
|
val intent = Intent(Intent.ACTION_VIEW)
|
||||||
intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK
|
intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK
|
||||||
intent.data = Uri.parse(url)
|
intent.data = Uri.parse(url)
|
||||||
@ -80,7 +77,6 @@ fun Context.mayBeStartActivity(intent: Intent) {
|
|||||||
} catch (e: ActivityNotFoundException) {
|
} catch (e: ActivityNotFoundException) {
|
||||||
Toast.makeText(this, getString(R.string.no_browser), Toast.LENGTH_SHORT).show()
|
Toast.makeText(this, getString(R.string.no_browser), Toast.LENGTH_SHORT).show()
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
class LinkOnTouchListener : View.OnTouchListener {
|
class LinkOnTouchListener : View.OnTouchListener {
|
||||||
|
@ -8,22 +8,19 @@ import org.acra.config.CoreConfiguration
|
|||||||
import org.acra.config.ReportingAdministrator
|
import org.acra.config.ReportingAdministrator
|
||||||
import org.acra.data.CrashReportData
|
import org.acra.data.CrashReportData
|
||||||
|
|
||||||
|
|
||||||
@AutoService(ReportingAdministrator::class)
|
@AutoService(ReportingAdministrator::class)
|
||||||
class AcraReportingAdministrator : ReportingAdministrator {
|
class AcraReportingAdministrator : ReportingAdministrator {
|
||||||
override fun shouldStartCollecting(
|
override fun shouldStartCollecting(
|
||||||
context: Context,
|
context: Context,
|
||||||
config: CoreConfiguration,
|
config: CoreConfiguration,
|
||||||
reportBuilder: ReportBuilder
|
reportBuilder: ReportBuilder,
|
||||||
): Boolean {
|
): Boolean =
|
||||||
return reportBuilder.exception !is DeadSystemException && (reportBuilder.exception != null && reportBuilder.exception!!::class.simpleName != "CannotDeliverBroadcastException")
|
reportBuilder.exception !is DeadSystemException &&
|
||||||
}
|
(reportBuilder.exception != null && reportBuilder.exception!!::class.simpleName != "CannotDeliverBroadcastException")
|
||||||
|
|
||||||
override fun shouldSendReport(
|
override fun shouldSendReport(
|
||||||
context: Context,
|
context: Context,
|
||||||
config: CoreConfiguration,
|
config: CoreConfiguration,
|
||||||
crashReportData: CrashReportData
|
crashReportData: CrashReportData,
|
||||||
): Boolean {
|
): Boolean = crashReportData.get("BRAND") != "redroid"
|
||||||
return crashReportData.get("BRAND") != "redroid"
|
|
||||||
}
|
|
||||||
}
|
}
|
@ -19,8 +19,7 @@ class AppViewModel(private val repository: Repository) : ViewModel() {
|
|||||||
if (isConnected && !wasConnected && repository.connectionMonitored) {
|
if (isConnected && !wasConnected && repository.connectionMonitored) {
|
||||||
_networkAvailableProvider.emit(true)
|
_networkAvailableProvider.emit(true)
|
||||||
wasConnected = true
|
wasConnected = true
|
||||||
} else if (!isConnected && wasConnected && repository.connectionMonitored)
|
} else if (!isConnected && wasConnected && repository.connectionMonitored) {
|
||||||
{
|
|
||||||
_networkAvailableProvider.emit(false)
|
_networkAvailableProvider.emit(false)
|
||||||
wasConnected = false
|
wasConnected = false
|
||||||
}
|
}
|
||||||
|
@ -11,13 +11,17 @@ fun dialogMessage(): String {
|
|||||||
return latestDialog.findViewById<TextView>(android.R.id.message)?.text.toString()
|
return latestDialog.findViewById<TextView>(android.R.id.message)?.text.toString()
|
||||||
}
|
}
|
||||||
|
|
||||||
fun Menu.assertClickable(@IdRes id: Int) {
|
fun Menu.assertClickable(
|
||||||
|
@IdRes id: Int,
|
||||||
|
) {
|
||||||
this.assertVisible(id)
|
this.assertVisible(id)
|
||||||
val item = this.findItem(id)
|
val item = this.findItem(id)
|
||||||
assertTrue(item.isEnabled)
|
assertTrue(item.isEnabled)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun Menu.assertVisible(@IdRes id: Int) {
|
fun Menu.assertVisible(
|
||||||
|
@IdRes id: Int,
|
||||||
|
) {
|
||||||
val item = this.findItem(id)
|
val item = this.findItem(id)
|
||||||
assertTrue(item.isVisible)
|
assertTrue(item.isVisible)
|
||||||
}
|
}
|
@ -11,10 +11,8 @@ import org.junit.Test
|
|||||||
import org.junit.runner.RunWith
|
import org.junit.runner.RunWith
|
||||||
import org.robolectric.Robolectric
|
import org.robolectric.Robolectric
|
||||||
|
|
||||||
|
@RunWith(RobotElectriqueRunner::class)
|
||||||
@RunWith(RobotElectriqueRunnerclass::class)
|
|
||||||
class LoginActivityTest {
|
class LoginActivityTest {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun login_shouldDisplay() {
|
fun login_shouldDisplay() {
|
||||||
Robolectric.buildActivity(LoginActivity::class.java).use { controller ->
|
Robolectric.buildActivity(LoginActivity::class.java).use { controller ->
|
||||||
|
@ -3,10 +3,8 @@ package bou.amine.apps.readerforselfossv2.android.tests.robolectric
|
|||||||
import org.robolectric.RobolectricTestRunner
|
import org.robolectric.RobolectricTestRunner
|
||||||
import org.robolectric.annotation.Config
|
import org.robolectric.annotation.Config
|
||||||
|
|
||||||
class RobotElectriqueRunnerclass(testClass: Class<*>?) :
|
class RobotElectriqueRunner(
|
||||||
RobolectricTestRunner(testClass) {
|
testClass: Class<*>?,
|
||||||
|
) : RobolectricTestRunner(testClass) {
|
||||||
override fun buildGlobalConfig(): Config {
|
override fun buildGlobalConfig(): Config = Config.Builder().setSdk(25, 30, 33).build()
|
||||||
return Config.Builder().setSdk(25, 30, 33).build()
|
|
||||||
}
|
|
||||||
}
|
}
|
@ -42,14 +42,15 @@ private const val FEED_URL = "https://test.com/feed"
|
|||||||
|
|
||||||
private const val TAGS = "Test, New"
|
private const val TAGS = "Test, New"
|
||||||
|
|
||||||
|
private const val NUMBER_ARTICLES = 100
|
||||||
|
private const val NUMBER_UNREAD = 50
|
||||||
|
private const val NUMBER_STARRED = 20
|
||||||
|
|
||||||
class RepositoryTest {
|
class RepositoryTest {
|
||||||
private val db = mockk<ReaderForSelfossDB>(relaxed = true)
|
private val db = mockk<ReaderForSelfossDB>(relaxed = true)
|
||||||
private val appSettingsService = mockk<AppSettingsService>()
|
private val appSettingsService = mockk<AppSettingsService>()
|
||||||
private val api = mockk<SelfossApi>()
|
private val api = mockk<SelfossApi>()
|
||||||
|
|
||||||
private val NUMBER_ARTICLES = 100
|
|
||||||
private val NUMBER_UNREAD = 50
|
|
||||||
private val NUMBER_STARRED = 20
|
|
||||||
private lateinit var repository: Repository
|
private lateinit var repository: Repository
|
||||||
|
|
||||||
private fun initializeRepository(
|
private fun initializeRepository(
|
||||||
@ -77,10 +78,11 @@ class RepositoryTest {
|
|||||||
coEvery { api.apiInformation() } returns
|
coEvery { api.apiInformation() } returns
|
||||||
StatusAndData(
|
StatusAndData(
|
||||||
success = true,
|
success = true,
|
||||||
data = SelfossModel.ApiInformation(
|
data =
|
||||||
|
SelfossModel.ApiInformation(
|
||||||
"2.19-ba1e8e3",
|
"2.19-ba1e8e3",
|
||||||
"4.0.0",
|
"4.0.0",
|
||||||
SelfossModel.ApiConfiguration(false, true)
|
SelfossModel.ApiConfiguration(false, true),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
coEvery { api.stats() } returns
|
coEvery { api.stats() } returns
|
||||||
@ -135,10 +137,11 @@ class RepositoryTest {
|
|||||||
coEvery { api.apiInformation() } returns
|
coEvery { api.apiInformation() } returns
|
||||||
StatusAndData(
|
StatusAndData(
|
||||||
success = true,
|
success = true,
|
||||||
data = SelfossModel.ApiInformation(
|
data =
|
||||||
|
SelfossModel.ApiInformation(
|
||||||
"2.19-ba1e8e3",
|
"2.19-ba1e8e3",
|
||||||
"4.0.0",
|
"4.0.0",
|
||||||
SelfossModel.ApiConfiguration(true, true)
|
SelfossModel.ApiConfiguration(true, true),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
every { appSettingsService.getUserName() } returns ""
|
every { appSettingsService.getUserName() } returns ""
|
||||||
@ -155,10 +158,11 @@ class RepositoryTest {
|
|||||||
coEvery { api.apiInformation() } returns
|
coEvery { api.apiInformation() } returns
|
||||||
StatusAndData(
|
StatusAndData(
|
||||||
success = true,
|
success = true,
|
||||||
data = SelfossModel.ApiInformation(
|
data =
|
||||||
|
SelfossModel.ApiInformation(
|
||||||
"2.19-ba1e8e3",
|
"2.19-ba1e8e3",
|
||||||
"4.0.0",
|
"4.0.0",
|
||||||
SelfossModel.ApiConfiguration(true, true)
|
SelfossModel.ApiConfiguration(true, true),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
every { appSettingsService.getUserName() } returns "username"
|
every { appSettingsService.getUserName() } returns "username"
|
||||||
@ -175,10 +179,11 @@ class RepositoryTest {
|
|||||||
coEvery { api.apiInformation() } returns
|
coEvery { api.apiInformation() } returns
|
||||||
StatusAndData(
|
StatusAndData(
|
||||||
success = true,
|
success = true,
|
||||||
data = SelfossModel.ApiInformation(
|
data =
|
||||||
|
SelfossModel.ApiInformation(
|
||||||
"2.19-ba1e8e3",
|
"2.19-ba1e8e3",
|
||||||
"4.0.0",
|
"4.0.0",
|
||||||
SelfossModel.ApiConfiguration(true, false)
|
SelfossModel.ApiConfiguration(true, false),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
every { appSettingsService.getUserName() } returns ""
|
every { appSettingsService.getUserName() } returns ""
|
||||||
@ -195,10 +200,11 @@ class RepositoryTest {
|
|||||||
coEvery { api.apiInformation() } returns
|
coEvery { api.apiInformation() } returns
|
||||||
StatusAndData(
|
StatusAndData(
|
||||||
success = true,
|
success = true,
|
||||||
data = SelfossModel.ApiInformation(
|
data =
|
||||||
|
SelfossModel.ApiInformation(
|
||||||
"2.19-ba1e8e3",
|
"2.19-ba1e8e3",
|
||||||
"4.0.0",
|
"4.0.0",
|
||||||
SelfossModel.ApiConfiguration(false, true)
|
SelfossModel.ApiConfiguration(false, true),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
every { appSettingsService.getUserName() } returns ""
|
every { appSettingsService.getUserName() } returns ""
|
||||||
|
@ -3,8 +3,8 @@ package bou.amine.apps.readerforselfossv2.tests.repository
|
|||||||
import bou.amine.apps.readerforselfossv2.dao.ITEM
|
import bou.amine.apps.readerforselfossv2.dao.ITEM
|
||||||
import bou.amine.apps.readerforselfossv2.model.SelfossModel
|
import bou.amine.apps.readerforselfossv2.model.SelfossModel
|
||||||
|
|
||||||
fun generateTestDBItems(item: FakeItemParameters = FakeItemParameters()): List<ITEM> {
|
fun generateTestDBItems(item: FakeItemParameters = FakeItemParameters()): List<ITEM> =
|
||||||
return listOf(
|
listOf(
|
||||||
ITEM(
|
ITEM(
|
||||||
id = item.id,
|
id = item.id,
|
||||||
datetime = item.datetime,
|
datetime = item.datetime,
|
||||||
@ -20,10 +20,9 @@ fun generateTestDBItems(item: FakeItemParameters = FakeItemParameters()): List<I
|
|||||||
author = item.author,
|
author = item.author,
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
}
|
|
||||||
|
|
||||||
fun generateTestApiItem(item: FakeItemParameters = FakeItemParameters()): List<SelfossModel.Item> {
|
fun generateTestApiItem(item: FakeItemParameters = FakeItemParameters()): List<SelfossModel.Item> =
|
||||||
return listOf(
|
listOf(
|
||||||
SelfossModel.Item(
|
SelfossModel.Item(
|
||||||
id = item.id.toInt(),
|
id = item.id.toInt(),
|
||||||
datetime = item.datetime,
|
datetime = item.datetime,
|
||||||
@ -39,14 +38,13 @@ fun generateTestApiItem(item: FakeItemParameters = FakeItemParameters()): List<S
|
|||||||
author = item.author,
|
author = item.author,
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
}
|
|
||||||
|
|
||||||
class FakeItemParameters {
|
class FakeItemParameters {
|
||||||
var id = "20"
|
var id = "20"
|
||||||
var datetime = "2022-09-09T03:32:01-04:00"
|
var datetime = "2022-09-09T03:32:01-04:00"
|
||||||
val title = "Etica della ricerca sotto i riflettori."
|
val title = "Etica della ricerca sotto i riflettori."
|
||||||
val content =
|
val content =
|
||||||
"<p><strong>Luigi Campanella, già Presidente SCI</strong></p>\n<p>L’etica della scienza è di certo ambito di cui continuiamo a scoprire nuovi aspetti e risvolti.</p>\n<p>L’ultimo è quello delle intelligenze artificiali capaci di creare opere complesse basate su immagini e parole memorizzate con il rischio di fake news e di contenuti disturbanti.</p>\n<p>Per evitare che ciò accada si sta procedendo filtrando secondo criteri di autocensura i dati da cui l’intelligenza artificiale parte.</p>\n<p>Comincia ad intravedersi un futuro prossimo di competizione fra autori umani ed artificiali nel quale sarà importante, quando i loro prodotti saranno indistinguibili, dichiararne l’origine.</p>\n<p>Come si comprende, si conferma che gli aspetti etici dell’innovazione e della ricerca si diversificato sempre di più.</p>\n<p>La biologia molecolare e la genetica già in passato hanno posto all’attenzione comune aspetti di etica della scienza che hanno indotto a nuove riflessioni circa i limiti delle ricerche.</p>\n<p>L’argomento, sempre attuale, torna sulle prime pagine a seguito della pubblicazione di una ricerca della Università di Cambridge che ha sviluppato una struttura cellulare di un topo con un cuore che batte regolarmente.</p>\n<img src=\"https://ilblogdellasci.files.wordpress.com/2022/09/image002-1.png?w=481\" alt=\"\" width=\"697\" height=\"430\" /><img src=\"https://ilblogdellasci.files.wordpress.com/2022/09/image003-1.png?w=906\" alt=\"\" /><p>Magdalena Zernicka-Goetz</p>\n<img src=\"https://ilblogdellasci.files.wordpress.com/2022/09/image004.jpg?w=474\" alt=\"\" width=\"622\" height=\"465\" /><p>Gianluca Amadei</p>\n<p>Del gruppo fa parte anche uno scienziato italiano Gianluca Amadei,che dinnanzi alle obiezioni di natura etica sulla realizzazione della vita artificiale si è affrettato a sostenere che non è creare nuove vite il fine primario della ricerca, ma quello di salvare quelle esistenti, di dare contributi essenziali alla medicina citando il caso del fallimento tuttora non interpretato di alcune gravidanze e di superare la sperimentazione animale, così contribuendo positivamente alla soluzione di un altro dilemma etico.</p>\n<p>L’embrione sintetico ha ovviamente come primo traguardo il contributo ai trapianti oggi drammaticamente carenti nell’offerta rispetto alla domanda, con attese fino a 4 anni per i trapianti di cuore ed a 2 anni per quelli di fegato. Il lavoro dovrebbe adesso continuare presso l’Ateneo di Padova per creare nuovi organi e nuovi farmaci.</p>"
|
"<p><strong>Luigi Campanella, già Presidente SCI</strong></p>\n<p>L’etica della scienza è di certo ambito di cui continuiamo</p>"
|
||||||
var unread = true
|
var unread = true
|
||||||
var starred = true
|
var starred = true
|
||||||
val thumbnail = null
|
val thumbnail = null
|
||||||
|
@ -9,7 +9,7 @@ actual class DriverFactory(private val context: Context) {
|
|||||||
return AndroidSqliteDriver(
|
return AndroidSqliteDriver(
|
||||||
ReaderForSelfossDB.Schema,
|
ReaderForSelfossDB.Schema,
|
||||||
context,
|
context,
|
||||||
"ReaderForSelfossV2-android.db"
|
"ReaderForSelfossV2-android.db",
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -8,16 +8,18 @@ class NaiveTrustManager : X509TrustManager {
|
|||||||
override fun checkClientTrusted(
|
override fun checkClientTrusted(
|
||||||
chain: Array<out X509Certificate>?,
|
chain: Array<out X509Certificate>?,
|
||||||
authType: String?,
|
authType: String?,
|
||||||
) {}
|
) {
|
||||||
|
}
|
||||||
|
|
||||||
override fun checkServerTrusted(
|
override fun checkServerTrusted(
|
||||||
chain: Array<out X509Certificate>?,
|
chain: Array<out X509Certificate>?,
|
||||||
authType: String?,
|
authType: String?,
|
||||||
) {}
|
) {
|
||||||
|
}
|
||||||
|
|
||||||
override fun getAcceptedIssuers(): Array<out X509Certificate> = arrayOf()
|
override fun getAcceptedIssuers(): Array<out X509Certificate> = arrayOf()
|
||||||
}
|
}
|
||||||
|
|
||||||
actual fun setupInsecureHTTPEngine(config: CIOEngineConfig) {
|
actual fun setupInsecureHttpEngine(config: CIOEngineConfig) {
|
||||||
config.https.trustManager = NaiveTrustManager()
|
config.https.trustManager = NaiveTrustManager()
|
||||||
}
|
}
|
||||||
|
@ -1,8 +1,7 @@
|
|||||||
package bou.amine.apps.readerforselfossv2.utils
|
package bou.amine.apps.readerforselfossv2.utils
|
||||||
|
|
||||||
import android.text.format.DateUtils
|
import android.text.format.DateUtils
|
||||||
import io.github.aakira.napier.Napier
|
import kotlinx.datetime.Clock
|
||||||
import kotlinx.datetime.*
|
|
||||||
|
|
||||||
actual class DateUtils {
|
actual class DateUtils {
|
||||||
actual companion object {
|
actual companion object {
|
||||||
|
@ -4,19 +4,13 @@ import android.net.Uri
|
|||||||
import android.text.Html
|
import android.text.Html
|
||||||
import bou.amine.apps.readerforselfossv2.model.SelfossModel
|
import bou.amine.apps.readerforselfossv2.model.SelfossModel
|
||||||
import org.jsoup.Jsoup
|
import org.jsoup.Jsoup
|
||||||
import java.util.*
|
import java.util.Locale
|
||||||
|
|
||||||
actual fun String.getHtmlDecoded(): String {
|
actual fun String.getHtmlDecoded(): String = Html.fromHtml(this).toString()
|
||||||
return Html.fromHtml(this).toString()
|
|
||||||
}
|
|
||||||
|
|
||||||
actual fun SelfossModel.Item.getIcon(baseUrl: String): String {
|
actual fun SelfossModel.Item.getIcon(baseUrl: String): String = constructUrl(baseUrl, "favicons", icon)
|
||||||
return constructUrl(baseUrl, "favicons", icon)
|
|
||||||
}
|
|
||||||
|
|
||||||
actual fun SelfossModel.Item.getThumbnail(baseUrl: String): String {
|
actual fun SelfossModel.Item.getThumbnail(baseUrl: String): String = constructUrl(baseUrl, "thumbnails", thumbnail)
|
||||||
return constructUrl(baseUrl, "thumbnails", thumbnail)
|
|
||||||
}
|
|
||||||
|
|
||||||
actual fun SelfossModel.Item.getImages(): ArrayList<String> {
|
actual fun SelfossModel.Item.getImages(): ArrayList<String> {
|
||||||
val allImages = ArrayList<String>()
|
val allImages = ArrayList<String>()
|
||||||
@ -34,16 +28,14 @@ actual fun SelfossModel.Item.getImages(): ArrayList<String> {
|
|||||||
return allImages
|
return allImages
|
||||||
}
|
}
|
||||||
|
|
||||||
actual fun SelfossModel.Source.getIcon(baseUrl: String): String {
|
actual fun SelfossModel.Source.getIcon(baseUrl: String): String = constructUrl(baseUrl, "favicons", icon)
|
||||||
return constructUrl(baseUrl, "favicons", icon)
|
|
||||||
}
|
|
||||||
|
|
||||||
actual fun constructUrl(
|
actual fun constructUrl(
|
||||||
baseUrl: String,
|
baseUrl: String,
|
||||||
path: String,
|
path: String,
|
||||||
file: String?,
|
file: String?,
|
||||||
): String {
|
): String =
|
||||||
return if (file == null || file == "null" || file.isEmpty()) {
|
if (file == null || file == "null" || file.isEmpty()) {
|
||||||
""
|
""
|
||||||
} else {
|
} else {
|
||||||
val baseUriBuilder = Uri.parse(baseUrl).buildUpon()
|
val baseUriBuilder = Uri.parse(baseUrl).buildUpon()
|
||||||
@ -51,4 +43,3 @@ actual fun constructUrl(
|
|||||||
|
|
||||||
baseUriBuilder.toString()
|
baseUriBuilder.toString()
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
package bou.amine.apps.readerforselfossv2.DI
|
package bou.amine.apps.readerforselfossv2.di
|
||||||
|
|
||||||
import bou.amine.apps.readerforselfossv2.rest.MercuryApi
|
import bou.amine.apps.readerforselfossv2.rest.MercuryApi
|
||||||
import bou.amine.apps.readerforselfossv2.rest.SelfossApi
|
import bou.amine.apps.readerforselfossv2.rest.SelfossApi
|
@ -7,7 +7,7 @@ class MercuryModel {
|
|||||||
class ParsedContent(
|
class ParsedContent(
|
||||||
val title: String? = null,
|
val title: String? = null,
|
||||||
val content: String? = null,
|
val content: String? = null,
|
||||||
val lead_image_url: String? = null, // NOSONAR
|
val lead_image_url: String? = null,
|
||||||
val url: String? = null,
|
val url: String? = null,
|
||||||
val error: Boolean? = null,
|
val error: Boolean? = null,
|
||||||
val message: String? = null,
|
val message: String? = null,
|
||||||
|
@ -3,19 +3,20 @@ package bou.amine.apps.readerforselfossv2.model
|
|||||||
import kotlinx.serialization.Serializable
|
import kotlinx.serialization.Serializable
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
class SuccessResponse(val success: Boolean) {
|
class SuccessResponse(
|
||||||
|
val success: Boolean,
|
||||||
|
) {
|
||||||
val isSuccess: Boolean
|
val isSuccess: Boolean
|
||||||
get() = success
|
get() = success
|
||||||
}
|
}
|
||||||
|
|
||||||
class StatusAndData<T>(val success: Boolean, val data: T? = null) {
|
class StatusAndData<T>(
|
||||||
|
val success: Boolean,
|
||||||
|
val data: T? = null,
|
||||||
|
) {
|
||||||
companion object {
|
companion object {
|
||||||
fun <T> succes(d: T): StatusAndData<T> {
|
fun <T> succes(d: T): StatusAndData<T> = StatusAndData(true, d)
|
||||||
return StatusAndData(true, d)
|
|
||||||
}
|
|
||||||
|
|
||||||
fun <T> error(): StatusAndData<T> {
|
fun <T> error(): StatusAndData<T> = StatusAndData(false)
|
||||||
return StatusAndData(false)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -141,7 +141,7 @@ class SelfossModel {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (stringUrl.isEmptyOrNullOrNullString()) {
|
if (stringUrl.isEmptyOrNullOrNullString()) {
|
||||||
throw Exception("Link ${link} was translated to ${stringUrl}, but was empty. Handle this.")
|
throw Exception("Link $link was translated to $stringUrl, but was empty. Handle this.")
|
||||||
}
|
}
|
||||||
|
|
||||||
return stringUrl
|
return stringUrl
|
||||||
@ -172,12 +172,11 @@ class SelfossModel {
|
|||||||
|
|
||||||
// TODO: this seems to be super slow.
|
// TODO: this seems to be super slow.
|
||||||
object TagsListSerializer : KSerializer<List<String>> {
|
object TagsListSerializer : KSerializer<List<String>> {
|
||||||
override fun deserialize(decoder: Decoder): List<String> {
|
override fun deserialize(decoder: Decoder): List<String> =
|
||||||
return when (val json = ((decoder as JsonDecoder).decodeJsonElement())) {
|
when (val json = ((decoder as JsonDecoder).decodeJsonElement())) {
|
||||||
is JsonArray -> json.toList().map { it.toString().replace("^\"|\"$".toRegex(), "") }
|
is JsonArray -> json.toList().map { it.toString().replace("^\"|\"$".toRegex(), "") }
|
||||||
else -> json.toString().split(",")
|
else -> json.toString().split(",")
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
override val descriptor: SerialDescriptor
|
override val descriptor: SerialDescriptor
|
||||||
get() = PrimitiveSerialDescriptor("tags", PrimitiveKind.STRING)
|
get() = PrimitiveSerialDescriptor("tags", PrimitiveKind.STRING)
|
||||||
@ -188,7 +187,7 @@ class SelfossModel {
|
|||||||
) {
|
) {
|
||||||
encoder.encodeCollection(
|
encoder.encodeCollection(
|
||||||
PrimitiveSerialDescriptor("tags", PrimitiveKind.STRING),
|
PrimitiveSerialDescriptor("tags", PrimitiveKind.STRING),
|
||||||
value.size
|
value.size,
|
||||||
) { this.toString() }
|
) { this.toString() }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -204,9 +203,10 @@ class SelfossModel {
|
|||||||
}
|
}
|
||||||
|
|
||||||
override val descriptor: SerialDescriptor
|
override val descriptor: SerialDescriptor
|
||||||
get() = PrimitiveSerialDescriptor(
|
get() =
|
||||||
|
PrimitiveSerialDescriptor(
|
||||||
"BooleanOrIntForSomeSelfossVersions",
|
"BooleanOrIntForSomeSelfossVersions",
|
||||||
PrimitiveKind.BOOLEAN
|
PrimitiveKind.BOOLEAN,
|
||||||
)
|
)
|
||||||
|
|
||||||
override fun serialize(
|
override fun serialize(
|
||||||
|
@ -1,12 +1,21 @@
|
|||||||
package bou.amine.apps.readerforselfossv2.repository
|
package bou.amine.apps.readerforselfossv2.repository
|
||||||
|
|
||||||
import bou.amine.apps.readerforselfossv2.dao.*
|
import bou.amine.apps.readerforselfossv2.dao.ACTION
|
||||||
|
import bou.amine.apps.readerforselfossv2.dao.DriverFactory
|
||||||
|
import bou.amine.apps.readerforselfossv2.dao.ITEM
|
||||||
|
import bou.amine.apps.readerforselfossv2.dao.ReaderForSelfossDB
|
||||||
|
import bou.amine.apps.readerforselfossv2.dao.SOURCE
|
||||||
|
import bou.amine.apps.readerforselfossv2.dao.TAG
|
||||||
import bou.amine.apps.readerforselfossv2.model.NetworkUnavailableException
|
import bou.amine.apps.readerforselfossv2.model.NetworkUnavailableException
|
||||||
import bou.amine.apps.readerforselfossv2.model.SelfossModel
|
import bou.amine.apps.readerforselfossv2.model.SelfossModel
|
||||||
import bou.amine.apps.readerforselfossv2.model.StatusAndData
|
import bou.amine.apps.readerforselfossv2.model.StatusAndData
|
||||||
import bou.amine.apps.readerforselfossv2.rest.SelfossApi
|
import bou.amine.apps.readerforselfossv2.rest.SelfossApi
|
||||||
import bou.amine.apps.readerforselfossv2.service.AppSettingsService
|
import bou.amine.apps.readerforselfossv2.service.AppSettingsService
|
||||||
import bou.amine.apps.readerforselfossv2.utils.*
|
import bou.amine.apps.readerforselfossv2.utils.Enums.ItemType
|
||||||
|
import bou.amine.apps.readerforselfossv2.utils.getHtmlDecoded
|
||||||
|
import bou.amine.apps.readerforselfossv2.utils.toEntity
|
||||||
|
import bou.amine.apps.readerforselfossv2.utils.toParsedDate
|
||||||
|
import bou.amine.apps.readerforselfossv2.utils.toView
|
||||||
import io.github.aakira.napier.Napier
|
import io.github.aakira.napier.Napier
|
||||||
import kotlinx.coroutines.CoroutineScope
|
import kotlinx.coroutines.CoroutineScope
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
@ -27,26 +36,26 @@ class Repository(
|
|||||||
|
|
||||||
var displayedItems = ItemType.UNREAD
|
var displayedItems = ItemType.UNREAD
|
||||||
|
|
||||||
private var _tagFilter = MutableStateFlow<SelfossModel.Tag?>(null)
|
private var tagFilterFlow = MutableStateFlow<SelfossModel.Tag?>(null)
|
||||||
var tagFilter = _tagFilter.asStateFlow()
|
var tagFilter = tagFilterFlow.asStateFlow()
|
||||||
private var _sourceFilter = MutableStateFlow<SelfossModel.Source?>(null)
|
private var sourceFilterFlow = MutableStateFlow<SelfossModel.Source?>(null)
|
||||||
var sourceFilter = _sourceFilter.asStateFlow()
|
var sourceFilter = sourceFilterFlow.asStateFlow()
|
||||||
var searchFilter: String? = null
|
var searchFilter: String? = null
|
||||||
|
|
||||||
var offlineOverride = false
|
var offlineOverride = false
|
||||||
|
|
||||||
private val _badgeUnread = MutableStateFlow(0)
|
private val badgeUnreadFlow = MutableStateFlow(0)
|
||||||
val badgeUnread = _badgeUnread.asStateFlow()
|
val badgeUnread = badgeUnreadFlow.asStateFlow()
|
||||||
private val _badgeAll = MutableStateFlow(0)
|
private val badgeAllFlow = MutableStateFlow(0)
|
||||||
val badgeAll = _badgeAll.asStateFlow()
|
val badgeAll = badgeAllFlow.asStateFlow()
|
||||||
private val _badgeStarred = MutableStateFlow(0)
|
private val badgeStarredFlow = MutableStateFlow(0)
|
||||||
val badgeStarred = _badgeStarred.asStateFlow()
|
val badgeStarred = badgeStarredFlow.asStateFlow()
|
||||||
|
|
||||||
private var fetchedTags = false
|
private var fetchedTags = false
|
||||||
private var fetchedSources = false
|
private var fetchedSources = false
|
||||||
|
|
||||||
private var _readerItems = ArrayList<SelfossModel.Item>()
|
private var readerItems = ArrayList<SelfossModel.Item>()
|
||||||
private var _selectedSource: SelfossModel.SourceDetail? = null
|
private var selectedSource: SelfossModel.SourceDetail? = null
|
||||||
|
|
||||||
suspend fun getNewerItems(): ArrayList<SelfossModel.Item> {
|
suspend fun getNewerItems(): ArrayList<SelfossModel.Item> {
|
||||||
var fetchedItems: StatusAndData<List<SelfossModel.Item>> = StatusAndData.error()
|
var fetchedItems: StatusAndData<List<SelfossModel.Item>> = StatusAndData.error()
|
||||||
@ -135,17 +144,17 @@ class Repository(
|
|||||||
if (isNetworkAvailable()) {
|
if (isNetworkAvailable()) {
|
||||||
val response = api.stats()
|
val response = api.stats()
|
||||||
if (response.success && response.data != null) {
|
if (response.success && response.data != null) {
|
||||||
_badgeUnread.value = response.data.unread ?: 0
|
badgeUnreadFlow.value = response.data.unread ?: 0
|
||||||
_badgeAll.value = response.data.total
|
badgeAllFlow.value = response.data.total
|
||||||
_badgeStarred.value = response.data.starred ?: 0
|
badgeStarredFlow.value = response.data.starred ?: 0
|
||||||
success = true
|
success = true
|
||||||
}
|
}
|
||||||
} else if (appSettingsService.isItemCachingEnabled()) {
|
} else if (appSettingsService.isItemCachingEnabled()) {
|
||||||
// TODO: do this differently, because it's not efficient
|
// TODO: do this differently, because it's not efficient
|
||||||
val dbItems = getDBItems()
|
val dbItems = getDBItems()
|
||||||
_badgeUnread.value = dbItems.filter { item -> item.unread }.size
|
badgeUnreadFlow.value = dbItems.filter { item -> item.unread }.size
|
||||||
_badgeStarred.value = dbItems.filter { item -> item.starred }.size
|
badgeStarredFlow.value = dbItems.filter { item -> item.starred }.size
|
||||||
_badgeAll.value = dbItems.size
|
badgeAllFlow.value = dbItems.size
|
||||||
success = true
|
success = true
|
||||||
}
|
}
|
||||||
return success
|
return success
|
||||||
@ -170,8 +179,8 @@ class Repository(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
suspend fun getSpouts(): Map<String, SelfossModel.Spout> {
|
suspend fun getSpouts(): Map<String, SelfossModel.Spout> =
|
||||||
return if (isNetworkAvailable()) {
|
if (isNetworkAvailable()) {
|
||||||
val spouts = api.spouts()
|
val spouts = api.spouts()
|
||||||
if (spouts.success && spouts.data != null) {
|
if (spouts.success && spouts.data != null) {
|
||||||
spouts.data
|
spouts.data
|
||||||
@ -181,7 +190,6 @@ class Repository(
|
|||||||
} else {
|
} else {
|
||||||
throw NetworkUnavailableException()
|
throw NetworkUnavailableException()
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
suspend fun getSourcesDetailsOrStats(): ArrayList<SelfossModel.Source> {
|
suspend fun getSourcesDetailsOrStats(): ArrayList<SelfossModel.Source> {
|
||||||
var sources = ArrayList<SelfossModel.Source>()
|
var sources = ArrayList<SelfossModel.Source>()
|
||||||
@ -234,14 +242,13 @@ class Repository(
|
|||||||
return success
|
return success
|
||||||
}
|
}
|
||||||
|
|
||||||
private suspend fun markAsReadById(id: Int): Boolean {
|
private suspend fun markAsReadById(id: Int): Boolean =
|
||||||
return if (isNetworkAvailable()) {
|
if (isNetworkAvailable()) {
|
||||||
api.markAsRead(id.toString()).isSuccess
|
api.markAsRead(id.toString()).isSuccess
|
||||||
} else {
|
} else {
|
||||||
insertDBAction(id.toString(), read = true)
|
insertDBAction(id.toString(), read = true)
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
suspend fun unmarkAsRead(item: SelfossModel.Item): Boolean {
|
suspend fun unmarkAsRead(item: SelfossModel.Item): Boolean {
|
||||||
val success = unmarkAsReadById(item.id)
|
val success = unmarkAsReadById(item.id)
|
||||||
@ -252,14 +259,13 @@ class Repository(
|
|||||||
return success
|
return success
|
||||||
}
|
}
|
||||||
|
|
||||||
private suspend fun unmarkAsReadById(id: Int): Boolean {
|
private suspend fun unmarkAsReadById(id: Int): Boolean =
|
||||||
return if (isNetworkAvailable()) {
|
if (isNetworkAvailable()) {
|
||||||
api.unmarkAsRead(id.toString()).isSuccess
|
api.unmarkAsRead(id.toString()).isSuccess
|
||||||
} else {
|
} else {
|
||||||
insertDBAction(id.toString(), unread = true)
|
insertDBAction(id.toString(), unread = true)
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
suspend fun starr(item: SelfossModel.Item): Boolean {
|
suspend fun starr(item: SelfossModel.Item): Boolean {
|
||||||
val success = starrById(item.id)
|
val success = starrById(item.id)
|
||||||
@ -270,14 +276,13 @@ class Repository(
|
|||||||
return success
|
return success
|
||||||
}
|
}
|
||||||
|
|
||||||
private suspend fun starrById(id: Int): Boolean {
|
private suspend fun starrById(id: Int): Boolean =
|
||||||
return if (isNetworkAvailable()) {
|
if (isNetworkAvailable()) {
|
||||||
api.starr(id.toString()).isSuccess
|
api.starr(id.toString()).isSuccess
|
||||||
} else {
|
} else {
|
||||||
insertDBAction(id.toString(), starred = true)
|
insertDBAction(id.toString(), starred = true)
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
suspend fun unstarr(item: SelfossModel.Item): Boolean {
|
suspend fun unstarr(item: SelfossModel.Item): Boolean {
|
||||||
val success = unstarrById(item.id)
|
val success = unstarrById(item.id)
|
||||||
@ -288,14 +293,13 @@ class Repository(
|
|||||||
return success
|
return success
|
||||||
}
|
}
|
||||||
|
|
||||||
private suspend fun unstarrById(id: Int): Boolean {
|
private suspend fun unstarrById(id: Int): Boolean =
|
||||||
return if (isNetworkAvailable()) {
|
if (isNetworkAvailable()) {
|
||||||
api.unstarr(id.toString()).isSuccess
|
api.unstarr(id.toString()).isSuccess
|
||||||
} else {
|
} else {
|
||||||
insertDBAction(id.toString(), starred = true)
|
insertDBAction(id.toString(), starred = true)
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
suspend fun markAllAsRead(items: ArrayList<SelfossModel.Item>): Boolean {
|
suspend fun markAllAsRead(items: ArrayList<SelfossModel.Item>): Boolean {
|
||||||
var success = false
|
var success = false
|
||||||
@ -312,7 +316,7 @@ class Repository(
|
|||||||
private fun markAsReadLocally(item: SelfossModel.Item) {
|
private fun markAsReadLocally(item: SelfossModel.Item) {
|
||||||
if (item.unread) {
|
if (item.unread) {
|
||||||
item.unread = false
|
item.unread = false
|
||||||
_badgeUnread.value -= 1
|
badgeUnreadFlow.value -= 1
|
||||||
}
|
}
|
||||||
|
|
||||||
CoroutineScope(Dispatchers.Main).launch {
|
CoroutineScope(Dispatchers.Main).launch {
|
||||||
@ -323,7 +327,7 @@ class Repository(
|
|||||||
private fun unmarkAsReadLocally(item: SelfossModel.Item) {
|
private fun unmarkAsReadLocally(item: SelfossModel.Item) {
|
||||||
if (!item.unread) {
|
if (!item.unread) {
|
||||||
item.unread = true
|
item.unread = true
|
||||||
_badgeUnread.value += 1
|
badgeUnreadFlow.value += 1
|
||||||
}
|
}
|
||||||
|
|
||||||
CoroutineScope(Dispatchers.Main).launch {
|
CoroutineScope(Dispatchers.Main).launch {
|
||||||
@ -334,7 +338,7 @@ class Repository(
|
|||||||
private fun starrLocally(item: SelfossModel.Item) {
|
private fun starrLocally(item: SelfossModel.Item) {
|
||||||
if (!item.starred) {
|
if (!item.starred) {
|
||||||
item.starred = true
|
item.starred = true
|
||||||
_badgeStarred.value += 1
|
badgeStarredFlow.value += 1
|
||||||
}
|
}
|
||||||
|
|
||||||
CoroutineScope(Dispatchers.Main).launch {
|
CoroutineScope(Dispatchers.Main).launch {
|
||||||
@ -345,7 +349,7 @@ class Repository(
|
|||||||
private fun unstarrLocally(item: SelfossModel.Item) {
|
private fun unstarrLocally(item: SelfossModel.Item) {
|
||||||
if (item.starred) {
|
if (item.starred) {
|
||||||
item.starred = false
|
item.starred = false
|
||||||
_badgeStarred.value -= 1
|
badgeStarredFlow.value -= 1
|
||||||
}
|
}
|
||||||
|
|
||||||
CoroutineScope(Dispatchers.Main).launch {
|
CoroutineScope(Dispatchers.Main).launch {
|
||||||
@ -361,7 +365,8 @@ class Repository(
|
|||||||
): Boolean {
|
): Boolean {
|
||||||
var response = false
|
var response = false
|
||||||
if (isNetworkAvailable()) {
|
if (isNetworkAvailable()) {
|
||||||
response = api.createSourceForVersion(
|
response = api
|
||||||
|
.createSourceForVersion(
|
||||||
title,
|
title,
|
||||||
url,
|
url,
|
||||||
spout,
|
spout,
|
||||||
@ -407,13 +412,12 @@ class Repository(
|
|||||||
return success
|
return success
|
||||||
}
|
}
|
||||||
|
|
||||||
suspend fun updateRemote(): Boolean {
|
suspend fun updateRemote(): Boolean =
|
||||||
return if (isNetworkAvailable()) {
|
if (isNetworkAvailable()) {
|
||||||
api.update().data.equals("finished")
|
api.update().data.equals("finished")
|
||||||
} else {
|
} else {
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
suspend fun login(): Boolean {
|
suspend fun login(): Boolean {
|
||||||
var result = false
|
var result = false
|
||||||
@ -422,7 +426,7 @@ class Repository(
|
|||||||
val response = api.login()
|
val response = api.login()
|
||||||
result = response.isSuccess == true
|
result = response.isSuccess == true
|
||||||
} catch (cause: Throwable) {
|
} catch (cause: Throwable) {
|
||||||
Napier.e("login failed", cause, tag = "RepositoryImpl.login")
|
Napier.e("login failed", cause, tag = "Repository.login")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return result
|
return result
|
||||||
@ -436,7 +440,7 @@ class Repository(
|
|||||||
// a random rss feed, that would throw a NoTransformationFoundException
|
// a random rss feed, that would throw a NoTransformationFoundException
|
||||||
fetchFailed = !api.getItemsWithoutCatch().success
|
fetchFailed = !api.getItemsWithoutCatch().success
|
||||||
} catch (e: Throwable) {
|
} catch (e: Throwable) {
|
||||||
Napier.e("checkIfFetchFails failed", e, tag = "RepositoryImpl.shouldBeSelfossInstance")
|
Napier.e("checkIfFetchFails failed", e, tag = "Repository.shouldBeSelfossInstance")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -448,10 +452,10 @@ class Repository(
|
|||||||
try {
|
try {
|
||||||
val response = api.logout()
|
val response = api.logout()
|
||||||
if (!response.isSuccess) {
|
if (!response.isSuccess) {
|
||||||
Napier.e("Couldn't logout.", tag = "RepositoryImpl.logout")
|
Napier.e("Couldn't logout.", tag = "Repository.logout")
|
||||||
}
|
}
|
||||||
} catch (cause: Throwable) {
|
} catch (cause: Throwable) {
|
||||||
Napier.e("logout failed", cause, tag = "RepositoryImpl.logout")
|
Napier.e("logout failed", cause, tag = "Repository.logout")
|
||||||
}
|
}
|
||||||
appSettingsService.clearAll()
|
appSettingsService.clearAll()
|
||||||
} else {
|
} else {
|
||||||
@ -578,16 +582,19 @@ class Repository(
|
|||||||
markAsReadById(action.articleid.toInt()),
|
markAsReadById(action.articleid.toInt()),
|
||||||
action,
|
action,
|
||||||
)
|
)
|
||||||
|
|
||||||
action.unread ->
|
action.unread ->
|
||||||
doAndReportOnFail(
|
doAndReportOnFail(
|
||||||
unmarkAsReadById(action.articleid.toInt()),
|
unmarkAsReadById(action.articleid.toInt()),
|
||||||
action,
|
action,
|
||||||
)
|
)
|
||||||
|
|
||||||
action.starred ->
|
action.starred ->
|
||||||
doAndReportOnFail(
|
doAndReportOnFail(
|
||||||
starrById(action.articleid.toInt()),
|
starrById(action.articleid.toInt()),
|
||||||
action,
|
action,
|
||||||
)
|
)
|
||||||
|
|
||||||
action.unstarred ->
|
action.unstarred ->
|
||||||
doAndReportOnFail(
|
doAndReportOnFail(
|
||||||
unstarrById(action.articleid.toInt()),
|
unstarrById(action.articleid.toInt()),
|
||||||
@ -607,34 +614,30 @@ class Repository(
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun setTagFilter(tag: SelfossModel.Tag?) {
|
fun setTagFilter(tag: SelfossModel.Tag?) {
|
||||||
_tagFilter.value = tag
|
tagFilterFlow.value = tag
|
||||||
}
|
}
|
||||||
|
|
||||||
fun setSourceFilter(source: SelfossModel.Source?) {
|
fun setSourceFilter(source: SelfossModel.Source?) {
|
||||||
_sourceFilter.value = source
|
sourceFilterFlow.value = source
|
||||||
}
|
}
|
||||||
|
|
||||||
fun setReaderItems(readerItems: ArrayList<SelfossModel.Item>) {
|
fun setReaderItems(readerItems: ArrayList<SelfossModel.Item>) {
|
||||||
_readerItems = readerItems
|
this.readerItems = readerItems
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getReaderItems(): ArrayList<SelfossModel.Item> {
|
fun getReaderItems(): ArrayList<SelfossModel.Item> = readerItems
|
||||||
return _readerItems
|
|
||||||
}
|
|
||||||
|
|
||||||
fun migrate(driverFactory: DriverFactory) {
|
fun migrate(driverFactory: DriverFactory) {
|
||||||
ReaderForSelfossDB.Schema.migrate(driverFactory.createDriver(), 0, 1)
|
ReaderForSelfossDB.Schema.migrate(driverFactory.createDriver(), 0, 1)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun setSelectedSource(source: SelfossModel.SourceDetail) {
|
fun setSelectedSource(source: SelfossModel.SourceDetail) {
|
||||||
_selectedSource = source
|
selectedSource = source
|
||||||
}
|
}
|
||||||
|
|
||||||
fun unsetSelectedSource() {
|
fun unsetSelectedSource() {
|
||||||
_selectedSource = null
|
selectedSource = null
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getSelectedSource(): SelfossModel.SourceDetail? {
|
fun getSelectedSource(): SelfossModel.SourceDetail? = selectedSource
|
||||||
return _selectedSource
|
|
||||||
}
|
|
||||||
}
|
}
|
@ -3,23 +3,28 @@ package bou.amine.apps.readerforselfossv2.rest
|
|||||||
import bou.amine.apps.readerforselfossv2.model.StatusAndData
|
import bou.amine.apps.readerforselfossv2.model.StatusAndData
|
||||||
import bou.amine.apps.readerforselfossv2.model.SuccessResponse
|
import bou.amine.apps.readerforselfossv2.model.SuccessResponse
|
||||||
import io.github.aakira.napier.Napier
|
import io.github.aakira.napier.Napier
|
||||||
import io.ktor.client.*
|
import io.ktor.client.HttpClient
|
||||||
import io.ktor.client.call.*
|
import io.ktor.client.call.body
|
||||||
import io.ktor.client.request.*
|
import io.ktor.client.request.HttpRequestBuilder
|
||||||
import io.ktor.client.request.forms.*
|
import io.ktor.client.request.delete
|
||||||
import io.ktor.client.statement.*
|
import io.ktor.client.request.forms.submitForm
|
||||||
import io.ktor.http.*
|
import io.ktor.client.request.get
|
||||||
|
import io.ktor.client.request.post
|
||||||
|
import io.ktor.client.request.url
|
||||||
|
import io.ktor.client.statement.HttpResponse
|
||||||
|
import io.ktor.http.HttpStatusCode
|
||||||
|
import io.ktor.http.Parameters
|
||||||
|
import io.ktor.http.isSuccess
|
||||||
|
|
||||||
suspend fun responseOrSuccessIf404(r: HttpResponse?): SuccessResponse {
|
suspend fun responseOrSuccessIf404(r: HttpResponse?): SuccessResponse =
|
||||||
return if (r != null && r.status === HttpStatusCode.NotFound) {
|
if (r != null && r.status === HttpStatusCode.NotFound) {
|
||||||
SuccessResponse(true)
|
SuccessResponse(true)
|
||||||
} else {
|
} else {
|
||||||
maybeResponse(r)
|
maybeResponse(r)
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
suspend fun maybeResponse(r: HttpResponse?): SuccessResponse {
|
suspend fun maybeResponse(r: HttpResponse?): SuccessResponse =
|
||||||
return if (r != null && r.status.isSuccess()) {
|
if (r != null && r.status.isSuccess()) {
|
||||||
r.body()
|
r.body()
|
||||||
} else {
|
} else {
|
||||||
if (r != null) {
|
if (r != null) {
|
||||||
@ -27,7 +32,6 @@ suspend fun maybeResponse(r: HttpResponse?): SuccessResponse {
|
|||||||
}
|
}
|
||||||
SuccessResponse(false)
|
SuccessResponse(false)
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
suspend inline fun <reified T> bodyOrFailure(r: HttpResponse?): StatusAndData<T> {
|
suspend inline fun <reified T> bodyOrFailure(r: HttpResponse?): StatusAndData<T> {
|
||||||
try {
|
try {
|
||||||
|
@ -33,16 +33,18 @@ import kotlinx.coroutines.Dispatchers
|
|||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import kotlinx.serialization.json.Json
|
import kotlinx.serialization.json.Json
|
||||||
|
|
||||||
expect fun setupInsecureHTTPEngine(config: CIOEngineConfig)
|
expect fun setupInsecureHttpEngine(config: CIOEngineConfig)
|
||||||
|
|
||||||
class SelfossApi(private val appSettingsService: AppSettingsService) {
|
class SelfossApi(
|
||||||
|
private val appSettingsService: AppSettingsService,
|
||||||
|
) {
|
||||||
var client = createHttpClient()
|
var client = createHttpClient()
|
||||||
|
|
||||||
fun createHttpClient() =
|
fun createHttpClient() =
|
||||||
HttpClient(CIO) {
|
HttpClient(CIO) {
|
||||||
if (appSettingsService.getSelfSigned()) {
|
if (appSettingsService.getSelfSigned()) {
|
||||||
engine {
|
engine {
|
||||||
setupInsecureHTTPEngine(this)
|
setupInsecureHttpEngine(this)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
install(HttpCache)
|
install(HttpCache)
|
||||||
@ -105,12 +107,14 @@ class SelfossApi(private val appSettingsService: AppSettingsService) {
|
|||||||
|
|
||||||
private fun hasLoginInfo() =
|
private fun hasLoginInfo() =
|
||||||
appSettingsService.getUserName().isNotEmpty() &&
|
appSettingsService.getUserName().isNotEmpty() &&
|
||||||
appSettingsService.getPassword()
|
appSettingsService
|
||||||
|
.getPassword()
|
||||||
.isNotEmpty()
|
.isNotEmpty()
|
||||||
|
|
||||||
suspend fun login(): SuccessResponse =
|
suspend fun login(): SuccessResponse =
|
||||||
if (appSettingsService.getUserName().isNotEmpty() &&
|
if (appSettingsService.getUserName().isNotEmpty() &&
|
||||||
appSettingsService.getPassword()
|
appSettingsService
|
||||||
|
.getPassword()
|
||||||
.isNotEmpty()
|
.isNotEmpty()
|
||||||
) {
|
) {
|
||||||
if (shouldHavePostLogin()) {
|
if (shouldHavePostLogin()) {
|
||||||
@ -127,8 +131,10 @@ class SelfossApi(private val appSettingsService: AppSettingsService) {
|
|||||||
client.tryToGet(url("/login")) {
|
client.tryToGet(url("/login")) {
|
||||||
parameter("username", appSettingsService.getUserName())
|
parameter("username", appSettingsService.getUserName())
|
||||||
parameter("password", appSettingsService.getPassword())
|
parameter("password", appSettingsService.getPassword())
|
||||||
if (appSettingsService.getBasicUserName()
|
if (appSettingsService
|
||||||
.isNotEmpty() && appSettingsService.getBasicPassword().isNotEmpty()
|
.getBasicUserName()
|
||||||
|
.isNotEmpty() &&
|
||||||
|
appSettingsService.getBasicPassword().isNotEmpty()
|
||||||
) {
|
) {
|
||||||
headers {
|
headers {
|
||||||
append(
|
append(
|
||||||
@ -150,8 +156,10 @@ class SelfossApi(private val appSettingsService: AppSettingsService) {
|
|||||||
client.tryToPost(url("/login")) {
|
client.tryToPost(url("/login")) {
|
||||||
parameter("username", appSettingsService.getUserName())
|
parameter("username", appSettingsService.getUserName())
|
||||||
parameter("password", appSettingsService.getPassword())
|
parameter("password", appSettingsService.getPassword())
|
||||||
if (appSettingsService.getBasicUserName()
|
if (appSettingsService
|
||||||
.isNotEmpty() && appSettingsService.getBasicPassword().isNotEmpty()
|
.getBasicUserName()
|
||||||
|
.isNotEmpty() &&
|
||||||
|
appSettingsService.getBasicPassword().isNotEmpty()
|
||||||
) {
|
) {
|
||||||
headers {
|
headers {
|
||||||
append(
|
append(
|
||||||
@ -168,8 +176,7 @@ class SelfossApi(private val appSettingsService: AppSettingsService) {
|
|||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
private fun shouldHaveNewLogout() =
|
private fun shouldHaveNewLogout() = appSettingsService.getApiVersion() >= 5 // We are missing 4.1.0
|
||||||
appSettingsService.getApiVersion() >= 5 // We are missing 4.1.0
|
|
||||||
|
|
||||||
suspend fun logout(): SuccessResponse =
|
suspend fun logout(): SuccessResponse =
|
||||||
if (shouldHaveNewLogout()) {
|
if (shouldHaveNewLogout()) {
|
||||||
@ -181,8 +188,10 @@ class SelfossApi(private val appSettingsService: AppSettingsService) {
|
|||||||
private suspend fun maybeLogoutIfAvailable() =
|
private suspend fun maybeLogoutIfAvailable() =
|
||||||
responseOrSuccessIf404(
|
responseOrSuccessIf404(
|
||||||
client.tryToGet(url("/logout")) {
|
client.tryToGet(url("/logout")) {
|
||||||
if (appSettingsService.getBasicUserName()
|
if (appSettingsService
|
||||||
.isNotEmpty() && appSettingsService.getBasicPassword().isNotEmpty()
|
.getBasicUserName()
|
||||||
|
.isNotEmpty() &&
|
||||||
|
appSettingsService.getBasicPassword().isNotEmpty()
|
||||||
) {
|
) {
|
||||||
headers {
|
headers {
|
||||||
append(
|
append(
|
||||||
@ -202,8 +211,10 @@ class SelfossApi(private val appSettingsService: AppSettingsService) {
|
|||||||
private suspend fun doLogout() =
|
private suspend fun doLogout() =
|
||||||
maybeResponse(
|
maybeResponse(
|
||||||
client.tryToDelete(url("/api/session/current")) {
|
client.tryToDelete(url("/api/session/current")) {
|
||||||
if (appSettingsService.getBasicUserName()
|
if (appSettingsService
|
||||||
.isNotEmpty() && appSettingsService.getBasicPassword().isNotEmpty()
|
.getBasicUserName()
|
||||||
|
.isNotEmpty() &&
|
||||||
|
appSettingsService.getBasicPassword().isNotEmpty()
|
||||||
) {
|
) {
|
||||||
headers {
|
headers {
|
||||||
append(
|
append(
|
||||||
@ -242,8 +253,10 @@ class SelfossApi(private val appSettingsService: AppSettingsService) {
|
|||||||
parameter("updatedsince", updatedSince)
|
parameter("updatedsince", updatedSince)
|
||||||
parameter("items", items ?: appSettingsService.getItemsNumber())
|
parameter("items", items ?: appSettingsService.getItemsNumber())
|
||||||
parameter("offset", offset)
|
parameter("offset", offset)
|
||||||
if (appSettingsService.getBasicUserName()
|
if (appSettingsService
|
||||||
.isNotEmpty() && appSettingsService.getBasicPassword().isNotEmpty()
|
.getBasicUserName()
|
||||||
|
.isNotEmpty() &&
|
||||||
|
appSettingsService.getBasicPassword().isNotEmpty()
|
||||||
) {
|
) {
|
||||||
headers {
|
headers {
|
||||||
append(
|
append(
|
||||||
@ -269,8 +282,10 @@ class SelfossApi(private val appSettingsService: AppSettingsService) {
|
|||||||
}
|
}
|
||||||
parameter("type", "all")
|
parameter("type", "all")
|
||||||
parameter("items", 1)
|
parameter("items", 1)
|
||||||
if (appSettingsService.getBasicUserName()
|
if (appSettingsService
|
||||||
.isNotEmpty() && appSettingsService.getBasicPassword().isNotEmpty()
|
.getBasicUserName()
|
||||||
|
.isNotEmpty() &&
|
||||||
|
appSettingsService.getBasicPassword().isNotEmpty()
|
||||||
) {
|
) {
|
||||||
headers {
|
headers {
|
||||||
append(
|
append(
|
||||||
@ -294,8 +309,10 @@ class SelfossApi(private val appSettingsService: AppSettingsService) {
|
|||||||
parameter("username", appSettingsService.getUserName())
|
parameter("username", appSettingsService.getUserName())
|
||||||
parameter("password", appSettingsService.getPassword())
|
parameter("password", appSettingsService.getPassword())
|
||||||
}
|
}
|
||||||
if (appSettingsService.getBasicUserName()
|
if (appSettingsService
|
||||||
.isNotEmpty() && appSettingsService.getBasicPassword().isNotEmpty()
|
.getBasicUserName()
|
||||||
|
.isNotEmpty() &&
|
||||||
|
appSettingsService.getBasicPassword().isNotEmpty()
|
||||||
) {
|
) {
|
||||||
headers {
|
headers {
|
||||||
append(
|
append(
|
||||||
@ -319,8 +336,10 @@ class SelfossApi(private val appSettingsService: AppSettingsService) {
|
|||||||
parameter("username", appSettingsService.getUserName())
|
parameter("username", appSettingsService.getUserName())
|
||||||
parameter("password", appSettingsService.getPassword())
|
parameter("password", appSettingsService.getPassword())
|
||||||
}
|
}
|
||||||
if (appSettingsService.getBasicUserName()
|
if (appSettingsService
|
||||||
.isNotEmpty() && appSettingsService.getBasicPassword().isNotEmpty()
|
.getBasicUserName()
|
||||||
|
.isNotEmpty() &&
|
||||||
|
appSettingsService.getBasicPassword().isNotEmpty()
|
||||||
) {
|
) {
|
||||||
headers {
|
headers {
|
||||||
append(
|
append(
|
||||||
@ -344,8 +363,10 @@ class SelfossApi(private val appSettingsService: AppSettingsService) {
|
|||||||
parameter("username", appSettingsService.getUserName())
|
parameter("username", appSettingsService.getUserName())
|
||||||
parameter("password", appSettingsService.getPassword())
|
parameter("password", appSettingsService.getPassword())
|
||||||
}
|
}
|
||||||
if (appSettingsService.getBasicUserName()
|
if (appSettingsService
|
||||||
.isNotEmpty() && appSettingsService.getBasicPassword().isNotEmpty()
|
.getBasicUserName()
|
||||||
|
.isNotEmpty() &&
|
||||||
|
appSettingsService.getBasicPassword().isNotEmpty()
|
||||||
) {
|
) {
|
||||||
headers {
|
headers {
|
||||||
append(
|
append(
|
||||||
@ -369,8 +390,10 @@ class SelfossApi(private val appSettingsService: AppSettingsService) {
|
|||||||
parameter("username", appSettingsService.getUserName())
|
parameter("username", appSettingsService.getUserName())
|
||||||
parameter("password", appSettingsService.getPassword())
|
parameter("password", appSettingsService.getPassword())
|
||||||
}
|
}
|
||||||
if (appSettingsService.getBasicUserName()
|
if (appSettingsService
|
||||||
.isNotEmpty() && appSettingsService.getBasicPassword().isNotEmpty()
|
.getBasicUserName()
|
||||||
|
.isNotEmpty() &&
|
||||||
|
appSettingsService.getBasicPassword().isNotEmpty()
|
||||||
) {
|
) {
|
||||||
headers {
|
headers {
|
||||||
append(
|
append(
|
||||||
@ -394,8 +417,10 @@ class SelfossApi(private val appSettingsService: AppSettingsService) {
|
|||||||
parameter("username", appSettingsService.getUserName())
|
parameter("username", appSettingsService.getUserName())
|
||||||
parameter("password", appSettingsService.getPassword())
|
parameter("password", appSettingsService.getPassword())
|
||||||
}
|
}
|
||||||
if (appSettingsService.getBasicUserName()
|
if (appSettingsService
|
||||||
.isNotEmpty() && appSettingsService.getBasicPassword().isNotEmpty()
|
.getBasicUserName()
|
||||||
|
.isNotEmpty() &&
|
||||||
|
appSettingsService.getBasicPassword().isNotEmpty()
|
||||||
) {
|
) {
|
||||||
headers {
|
headers {
|
||||||
append(
|
append(
|
||||||
@ -419,8 +444,10 @@ class SelfossApi(private val appSettingsService: AppSettingsService) {
|
|||||||
parameter("username", appSettingsService.getUserName())
|
parameter("username", appSettingsService.getUserName())
|
||||||
parameter("password", appSettingsService.getPassword())
|
parameter("password", appSettingsService.getPassword())
|
||||||
}
|
}
|
||||||
if (appSettingsService.getBasicUserName()
|
if (appSettingsService
|
||||||
.isNotEmpty() && appSettingsService.getBasicPassword().isNotEmpty()
|
.getBasicUserName()
|
||||||
|
.isNotEmpty() &&
|
||||||
|
appSettingsService.getBasicPassword().isNotEmpty()
|
||||||
) {
|
) {
|
||||||
headers {
|
headers {
|
||||||
append(
|
append(
|
||||||
@ -440,8 +467,10 @@ class SelfossApi(private val appSettingsService: AppSettingsService) {
|
|||||||
suspend fun apiInformation(): StatusAndData<SelfossModel.ApiInformation> =
|
suspend fun apiInformation(): StatusAndData<SelfossModel.ApiInformation> =
|
||||||
bodyOrFailure(
|
bodyOrFailure(
|
||||||
client.tryToGet(url("/api/about")) {
|
client.tryToGet(url("/api/about")) {
|
||||||
if (appSettingsService.getBasicUserName()
|
if (appSettingsService
|
||||||
.isNotEmpty() && appSettingsService.getBasicPassword().isNotEmpty()
|
.getBasicUserName()
|
||||||
|
.isNotEmpty() &&
|
||||||
|
appSettingsService.getBasicPassword().isNotEmpty()
|
||||||
) {
|
) {
|
||||||
headers {
|
headers {
|
||||||
append(
|
append(
|
||||||
@ -465,8 +494,10 @@ class SelfossApi(private val appSettingsService: AppSettingsService) {
|
|||||||
parameter("username", appSettingsService.getUserName())
|
parameter("username", appSettingsService.getUserName())
|
||||||
parameter("password", appSettingsService.getPassword())
|
parameter("password", appSettingsService.getPassword())
|
||||||
}
|
}
|
||||||
if (appSettingsService.getBasicUserName()
|
if (appSettingsService
|
||||||
.isNotEmpty() && appSettingsService.getBasicPassword().isNotEmpty()
|
.getBasicUserName()
|
||||||
|
.isNotEmpty() &&
|
||||||
|
appSettingsService.getBasicPassword().isNotEmpty()
|
||||||
) {
|
) {
|
||||||
headers {
|
headers {
|
||||||
append(
|
append(
|
||||||
@ -490,8 +521,10 @@ class SelfossApi(private val appSettingsService: AppSettingsService) {
|
|||||||
parameter("username", appSettingsService.getUserName())
|
parameter("username", appSettingsService.getUserName())
|
||||||
parameter("password", appSettingsService.getPassword())
|
parameter("password", appSettingsService.getPassword())
|
||||||
}
|
}
|
||||||
if (appSettingsService.getBasicUserName()
|
if (appSettingsService
|
||||||
.isNotEmpty() && appSettingsService.getBasicPassword().isNotEmpty()
|
.getBasicUserName()
|
||||||
|
.isNotEmpty() &&
|
||||||
|
appSettingsService.getBasicPassword().isNotEmpty()
|
||||||
) {
|
) {
|
||||||
headers {
|
headers {
|
||||||
append(
|
append(
|
||||||
@ -515,8 +548,10 @@ class SelfossApi(private val appSettingsService: AppSettingsService) {
|
|||||||
parameter("username", appSettingsService.getUserName())
|
parameter("username", appSettingsService.getUserName())
|
||||||
parameter("password", appSettingsService.getPassword())
|
parameter("password", appSettingsService.getPassword())
|
||||||
}
|
}
|
||||||
if (appSettingsService.getBasicUserName()
|
if (appSettingsService
|
||||||
.isNotEmpty() && appSettingsService.getBasicPassword().isNotEmpty()
|
.getBasicUserName()
|
||||||
|
.isNotEmpty() &&
|
||||||
|
appSettingsService.getBasicPassword().isNotEmpty()
|
||||||
) {
|
) {
|
||||||
headers {
|
headers {
|
||||||
append(
|
append(
|
||||||
@ -540,8 +575,10 @@ class SelfossApi(private val appSettingsService: AppSettingsService) {
|
|||||||
parameter("username", appSettingsService.getUserName())
|
parameter("username", appSettingsService.getUserName())
|
||||||
parameter("password", appSettingsService.getPassword())
|
parameter("password", appSettingsService.getPassword())
|
||||||
}
|
}
|
||||||
if (appSettingsService.getBasicUserName()
|
if (appSettingsService
|
||||||
.isNotEmpty() && appSettingsService.getBasicPassword().isNotEmpty()
|
.getBasicUserName()
|
||||||
|
.isNotEmpty() &&
|
||||||
|
appSettingsService.getBasicPassword().isNotEmpty()
|
||||||
) {
|
) {
|
||||||
headers {
|
headers {
|
||||||
append(
|
append(
|
||||||
@ -571,8 +608,10 @@ class SelfossApi(private val appSettingsService: AppSettingsService) {
|
|||||||
ids.map { append("ids[]", it) }
|
ids.map { append("ids[]", it) }
|
||||||
},
|
},
|
||||||
block = {
|
block = {
|
||||||
if (appSettingsService.getBasicUserName()
|
if (appSettingsService
|
||||||
.isNotEmpty() && appSettingsService.getBasicPassword().isNotEmpty()
|
.getBasicUserName()
|
||||||
|
.isNotEmpty() &&
|
||||||
|
appSettingsService.getBasicPassword().isNotEmpty()
|
||||||
) {
|
) {
|
||||||
headers {
|
headers {
|
||||||
append(
|
append(
|
||||||
@ -625,8 +664,10 @@ class SelfossApi(private val appSettingsService: AppSettingsService) {
|
|||||||
append(tagsParamName, tags)
|
append(tagsParamName, tags)
|
||||||
},
|
},
|
||||||
block = {
|
block = {
|
||||||
if (appSettingsService.getBasicUserName()
|
if (appSettingsService
|
||||||
.isNotEmpty() && appSettingsService.getBasicPassword().isNotEmpty()
|
.getBasicUserName()
|
||||||
|
.isNotEmpty() &&
|
||||||
|
appSettingsService.getBasicPassword().isNotEmpty()
|
||||||
) {
|
) {
|
||||||
headers {
|
headers {
|
||||||
append(
|
append(
|
||||||
@ -680,8 +721,10 @@ class SelfossApi(private val appSettingsService: AppSettingsService) {
|
|||||||
append(tagsParamName, tags)
|
append(tagsParamName, tags)
|
||||||
},
|
},
|
||||||
block = {
|
block = {
|
||||||
if (appSettingsService.getBasicUserName()
|
if (appSettingsService
|
||||||
.isNotEmpty() && appSettingsService.getBasicPassword().isNotEmpty()
|
.getBasicUserName()
|
||||||
|
.isNotEmpty() &&
|
||||||
|
appSettingsService.getBasicPassword().isNotEmpty()
|
||||||
) {
|
) {
|
||||||
headers {
|
headers {
|
||||||
append(
|
append(
|
||||||
@ -705,8 +748,10 @@ class SelfossApi(private val appSettingsService: AppSettingsService) {
|
|||||||
parameter("username", appSettingsService.getUserName())
|
parameter("username", appSettingsService.getUserName())
|
||||||
parameter("password", appSettingsService.getPassword())
|
parameter("password", appSettingsService.getPassword())
|
||||||
}
|
}
|
||||||
if (appSettingsService.getBasicUserName()
|
if (appSettingsService
|
||||||
.isNotEmpty() && appSettingsService.getBasicPassword().isNotEmpty()
|
.getBasicUserName()
|
||||||
|
.isNotEmpty() &&
|
||||||
|
appSettingsService.getBasicPassword().isNotEmpty()
|
||||||
) {
|
) {
|
||||||
headers {
|
headers {
|
||||||
append(
|
append(
|
||||||
|
@ -2,7 +2,9 @@ package bou.amine.apps.readerforselfossv2.service
|
|||||||
|
|
||||||
import com.russhwolf.settings.Settings
|
import com.russhwolf.settings.Settings
|
||||||
|
|
||||||
class AppSettingsService(acraSenderServiceProcess: Boolean = false) {
|
class AppSettingsService(
|
||||||
|
acraSenderServiceProcess: Boolean = false,
|
||||||
|
) {
|
||||||
val settings: Settings =
|
val settings: Settings =
|
||||||
if (acraSenderServiceProcess) {
|
if (acraSenderServiceProcess) {
|
||||||
ACRASettings()
|
ACRASettings()
|
||||||
@ -11,37 +13,37 @@ class AppSettingsService(acraSenderServiceProcess: Boolean = false) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Api related
|
// Api related
|
||||||
private var _apiVersion: Int = -1
|
private var apiVersion: Int = -1
|
||||||
private var _publicAccess: Boolean? = null
|
private var publicAccess: Boolean? = null
|
||||||
private var _selfSigned: Boolean? = null
|
private var selfSigned: Boolean? = null
|
||||||
private var _baseUrl: String = ""
|
private var baseUrl: String = ""
|
||||||
private var _userName: String = ""
|
private var userName: String = ""
|
||||||
private var _basicUserName: String = ""
|
private var basicUserName: String = ""
|
||||||
private var _password: String = ""
|
private var password: String = ""
|
||||||
private var _basicPassword: String = ""
|
private var basicPassword: String = ""
|
||||||
|
|
||||||
// User settings related
|
// User settings related
|
||||||
private var _itemsCaching: Boolean? = null
|
private var itemsCaching: Boolean? = null
|
||||||
private var _articleViewer: Boolean? = null
|
private var articleViewer: Boolean? = null
|
||||||
private var _shouldBeCardView: Boolean? = null
|
private var shouldBeCardView: Boolean? = null
|
||||||
private var _displayUnreadCount: Boolean? = null
|
private var displayUnreadCount: Boolean? = null
|
||||||
private var _displayAllCount: Boolean? = null
|
private var displayAllCount: Boolean? = null
|
||||||
private var _fullHeightCards: Boolean? = null
|
private var fullHeightCards: Boolean? = null
|
||||||
private var _updateSources: Boolean? = null
|
private var updateSources: Boolean? = null
|
||||||
private var _periodicRefresh: Boolean? = null
|
private var periodicRefresh: Boolean? = null
|
||||||
private var _refreshWhenChargingOnly: Boolean? = null
|
private var refreshWhenChargingOnly: Boolean? = null
|
||||||
private var _infiniteLoading: Boolean? = null
|
private var infiniteLoading: Boolean? = null
|
||||||
private var _notifyNewItems: Boolean? = null
|
private var notifyNewItems: Boolean? = null
|
||||||
private var _itemsNumber: Int? = null
|
private var itemsNumber: Int? = null
|
||||||
private var _apiTimeout: Long? = null
|
private var apiTimeout: Long? = null
|
||||||
private var _refreshMinutes: Long = 360
|
private var refreshMinutes: Long = 360
|
||||||
private var _markOnScroll: Boolean? = null
|
private var markOnScroll: Boolean? = null
|
||||||
private var _activeAlignment: Int? = null
|
private var activeAlignment: Int? = null
|
||||||
|
|
||||||
private var _fontSize: Int? = null
|
private var fontSize: Int? = null
|
||||||
private var _staticBar: Boolean? = null
|
private var staticBar: Boolean? = null
|
||||||
private var _font: String = ""
|
private var font: String = ""
|
||||||
private var _theme: Int? = null
|
private var theme: Int? = null
|
||||||
|
|
||||||
init {
|
init {
|
||||||
refreshApiSettings()
|
refreshApiSettings()
|
||||||
@ -49,11 +51,11 @@ class AppSettingsService(acraSenderServiceProcess: Boolean = false) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun getApiVersion(): Int {
|
fun getApiVersion(): Int {
|
||||||
if (_apiVersion == -1) {
|
if (apiVersion == -1) {
|
||||||
refreshApiVersion()
|
refreshApiVersion()
|
||||||
return _apiVersion
|
return apiVersion
|
||||||
}
|
}
|
||||||
return _apiVersion
|
return apiVersion
|
||||||
}
|
}
|
||||||
|
|
||||||
fun updateApiVersion(apiMajorVersion: Int) {
|
fun updateApiVersion(apiMajorVersion: Int) {
|
||||||
@ -62,14 +64,14 @@ class AppSettingsService(acraSenderServiceProcess: Boolean = false) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun refreshApiVersion() {
|
private fun refreshApiVersion() {
|
||||||
_apiVersion = settings.getInt(API_VERSION_MAJOR, -1)
|
apiVersion = settings.getInt(API_VERSION_MAJOR, -1)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getPublicAccess(): Boolean {
|
fun getPublicAccess(): Boolean {
|
||||||
if (_publicAccess == null) {
|
if (publicAccess == null) {
|
||||||
refreshPublicAccess()
|
refreshPublicAccess()
|
||||||
}
|
}
|
||||||
return _publicAccess!!
|
return publicAccess!!
|
||||||
}
|
}
|
||||||
|
|
||||||
fun updatePublicAccess(publicAccess: Boolean) {
|
fun updatePublicAccess(publicAccess: Boolean) {
|
||||||
@ -78,14 +80,14 @@ class AppSettingsService(acraSenderServiceProcess: Boolean = false) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun refreshPublicAccess() {
|
private fun refreshPublicAccess() {
|
||||||
_publicAccess = settings.getBoolean(API_PUBLIC_ACCESS, false)
|
publicAccess = settings.getBoolean(API_PUBLIC_ACCESS, false)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getSelfSigned(): Boolean {
|
fun getSelfSigned(): Boolean {
|
||||||
if (_selfSigned == null) {
|
if (selfSigned == null) {
|
||||||
refreshSelfSigned()
|
refreshSelfSigned()
|
||||||
}
|
}
|
||||||
return _selfSigned!!
|
return selfSigned!!
|
||||||
}
|
}
|
||||||
|
|
||||||
fun updateSelfSigned(selfSigned: Boolean) {
|
fun updateSelfSigned(selfSigned: Boolean) {
|
||||||
@ -94,53 +96,53 @@ class AppSettingsService(acraSenderServiceProcess: Boolean = false) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun refreshSelfSigned() {
|
private fun refreshSelfSigned() {
|
||||||
_selfSigned = settings.getBoolean(API_SELF_SIGNED, false)
|
selfSigned = settings.getBoolean(API_SELF_SIGNED, false)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getBaseUrl(): String {
|
fun getBaseUrl(): String {
|
||||||
if (_baseUrl.isEmpty()) {
|
if (baseUrl.isEmpty()) {
|
||||||
refreshBaseUrl()
|
refreshBaseUrl()
|
||||||
}
|
}
|
||||||
return _baseUrl
|
return baseUrl
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getUserName(): String {
|
fun getUserName(): String {
|
||||||
if (_userName.isEmpty()) {
|
if (userName.isEmpty()) {
|
||||||
refreshUsername()
|
refreshUsername()
|
||||||
}
|
}
|
||||||
return _userName
|
return userName
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getPassword(): String {
|
fun getPassword(): String {
|
||||||
if (_password.isEmpty()) {
|
if (password.isEmpty()) {
|
||||||
refreshPassword()
|
refreshPassword()
|
||||||
}
|
}
|
||||||
return _password
|
return password
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getBasicUserName(): String {
|
fun getBasicUserName(): String {
|
||||||
if (_basicUserName.isEmpty()) {
|
if (basicUserName.isEmpty()) {
|
||||||
refreshBasicUsername()
|
refreshBasicUsername()
|
||||||
}
|
}
|
||||||
return _basicUserName
|
return basicUserName
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getBasicPassword(): String {
|
fun getBasicPassword(): String {
|
||||||
if (_basicPassword.isEmpty()) {
|
if (basicPassword.isEmpty()) {
|
||||||
refreshBasicPassword()
|
refreshBasicPassword()
|
||||||
}
|
}
|
||||||
return _basicPassword
|
return basicPassword
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getItemsNumber(): Int {
|
fun getItemsNumber(): Int {
|
||||||
if (_itemsNumber == null) {
|
if (itemsNumber == null) {
|
||||||
refreshItemsNumber()
|
refreshItemsNumber()
|
||||||
}
|
}
|
||||||
return _itemsNumber!!
|
return itemsNumber!!
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun refreshItemsNumber() {
|
private fun refreshItemsNumber() {
|
||||||
_itemsNumber =
|
itemsNumber =
|
||||||
try {
|
try {
|
||||||
settings.getString(API_ITEMS_NUMBER, "20").toInt()
|
settings.getString(API_ITEMS_NUMBER, "20").toInt()
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
@ -150,16 +152,16 @@ class AppSettingsService(acraSenderServiceProcess: Boolean = false) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun getApiTimeout(): Long {
|
fun getApiTimeout(): Long {
|
||||||
if (_apiTimeout == null) {
|
if (apiTimeout == null) {
|
||||||
refreshApiTimeout()
|
refreshApiTimeout()
|
||||||
}
|
}
|
||||||
return _apiTimeout!!
|
return apiTimeout!!
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun secToMs(n: Long) = n * 1000
|
private fun secToMs(n: Long) = n * 1000
|
||||||
|
|
||||||
private fun refreshApiTimeout() {
|
private fun refreshApiTimeout() {
|
||||||
_apiTimeout =
|
apiTimeout =
|
||||||
secToMs(
|
secToMs(
|
||||||
try {
|
try {
|
||||||
val settingsTimeout = settings.getString(API_TIMEOUT, "60")
|
val settingsTimeout = settings.getString(API_TIMEOUT, "60")
|
||||||
@ -177,229 +179,229 @@ class AppSettingsService(acraSenderServiceProcess: Boolean = false) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun refreshBaseUrl() {
|
private fun refreshBaseUrl() {
|
||||||
_baseUrl = settings.getString(BASE_URL, "")
|
baseUrl = settings.getString(BASE_URL, "")
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun refreshUsername() {
|
private fun refreshUsername() {
|
||||||
_userName = settings.getString(LOGIN, "")
|
userName = settings.getString(LOGIN, "")
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun refreshPassword() {
|
private fun refreshPassword() {
|
||||||
_password = settings.getString(PASSWORD, "")
|
password = settings.getString(PASSWORD, "")
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun refreshBasicUsername() {
|
private fun refreshBasicUsername() {
|
||||||
_basicUserName = settings.getString(BASIC_LOGIN, "")
|
basicUserName = settings.getString(BASIC_LOGIN, "")
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun refreshBasicPassword() {
|
private fun refreshBasicPassword() {
|
||||||
_basicPassword = settings.getString(BASIC_PASSWORD, "")
|
basicPassword = settings.getString(BASIC_PASSWORD, "")
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun refreshArticleViewerEnabled() {
|
private fun refreshArticleViewerEnabled() {
|
||||||
_articleViewer = settings.getBoolean(PREFER_ARTICLE_VIEWER, true)
|
articleViewer = settings.getBoolean(PREFER_ARTICLE_VIEWER, true)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun isArticleViewerEnabled(): Boolean {
|
fun isArticleViewerEnabled(): Boolean {
|
||||||
if (_articleViewer != null) {
|
if (articleViewer != null) {
|
||||||
refreshArticleViewerEnabled()
|
refreshArticleViewerEnabled()
|
||||||
}
|
}
|
||||||
return _articleViewer == true
|
return articleViewer == true
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun refreshShouldBeCardViewEnabled() {
|
private fun refreshShouldBeCardViewEnabled() {
|
||||||
_shouldBeCardView = settings.getBoolean(CARD_VIEW_ACTIVE, false)
|
shouldBeCardView = settings.getBoolean(CARD_VIEW_ACTIVE, false)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun isCardViewEnabled(): Boolean {
|
fun isCardViewEnabled(): Boolean {
|
||||||
if (_shouldBeCardView != null) {
|
if (shouldBeCardView != null) {
|
||||||
refreshShouldBeCardViewEnabled()
|
refreshShouldBeCardViewEnabled()
|
||||||
}
|
}
|
||||||
return _shouldBeCardView == true
|
return shouldBeCardView == true
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun refreshDisplayUnreadCountEnabled() {
|
private fun refreshDisplayUnreadCountEnabled() {
|
||||||
_displayUnreadCount = settings.getBoolean(DISPLAY_UNREAD_COUNT, true)
|
displayUnreadCount = settings.getBoolean(DISPLAY_UNREAD_COUNT, true)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun isDisplayUnreadCountEnabled(): Boolean {
|
fun isDisplayUnreadCountEnabled(): Boolean {
|
||||||
if (_displayUnreadCount != null) {
|
if (displayUnreadCount != null) {
|
||||||
refreshDisplayUnreadCountEnabled()
|
refreshDisplayUnreadCountEnabled()
|
||||||
}
|
}
|
||||||
return _displayUnreadCount == true
|
return displayUnreadCount == true
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun refreshDisplayAllCountEnabled() {
|
private fun refreshDisplayAllCountEnabled() {
|
||||||
_displayAllCount = settings.getBoolean(DISPLAY_OTHER_COUNT, false)
|
displayAllCount = settings.getBoolean(DISPLAY_OTHER_COUNT, false)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun isDisplayAllCountEnabled(): Boolean {
|
fun isDisplayAllCountEnabled(): Boolean {
|
||||||
if (_displayAllCount != null) {
|
if (displayAllCount != null) {
|
||||||
refreshDisplayAllCountEnabled()
|
refreshDisplayAllCountEnabled()
|
||||||
}
|
}
|
||||||
return _displayAllCount == true
|
return displayAllCount == true
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun refreshFullHeightCardsEnabled() {
|
private fun refreshFullHeightCardsEnabled() {
|
||||||
_fullHeightCards = settings.getBoolean(FULL_HEIGHT_CARDS, false)
|
fullHeightCards = settings.getBoolean(FULL_HEIGHT_CARDS, false)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun isFullHeightCardsEnabled(): Boolean {
|
fun isFullHeightCardsEnabled(): Boolean {
|
||||||
if (_fullHeightCards != null) {
|
if (fullHeightCards != null) {
|
||||||
refreshFullHeightCardsEnabled()
|
refreshFullHeightCardsEnabled()
|
||||||
}
|
}
|
||||||
return _fullHeightCards == true
|
return fullHeightCards == true
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun refreshUpdateSourcesEnabled() {
|
private fun refreshUpdateSourcesEnabled() {
|
||||||
_updateSources = settings.getBoolean(UPDATE_SOURCES, true)
|
updateSources = settings.getBoolean(UPDATE_SOURCES, true)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun isUpdateSourcesEnabled(): Boolean {
|
fun isUpdateSourcesEnabled(): Boolean {
|
||||||
if (_updateSources != null) {
|
if (updateSources != null) {
|
||||||
refreshUpdateSourcesEnabled()
|
refreshUpdateSourcesEnabled()
|
||||||
}
|
}
|
||||||
return _updateSources == true
|
return updateSources == true
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun refreshPeriodicRefreshEnabled() {
|
private fun refreshPeriodicRefreshEnabled() {
|
||||||
_periodicRefresh = settings.getBoolean(PERIODIC_REFRESH, false)
|
periodicRefresh = settings.getBoolean(PERIODIC_REFRESH, false)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun isPeriodicRefreshEnabled(): Boolean {
|
fun isPeriodicRefreshEnabled(): Boolean {
|
||||||
if (_periodicRefresh != null) {
|
if (periodicRefresh != null) {
|
||||||
refreshPeriodicRefreshEnabled()
|
refreshPeriodicRefreshEnabled()
|
||||||
}
|
}
|
||||||
return _periodicRefresh == true
|
return periodicRefresh == true
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun refreshRefreshWhenChargingOnlyEnabled() {
|
private fun refreshRefreshWhenChargingOnlyEnabled() {
|
||||||
_refreshWhenChargingOnly = settings.getBoolean(REFRESH_WHEN_CHARGING, false)
|
refreshWhenChargingOnly = settings.getBoolean(REFRESH_WHEN_CHARGING, false)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun isRefreshWhenChargingOnlyEnabled(): Boolean {
|
fun isRefreshWhenChargingOnlyEnabled(): Boolean {
|
||||||
if (_refreshWhenChargingOnly != null) {
|
if (refreshWhenChargingOnly != null) {
|
||||||
refreshRefreshWhenChargingOnlyEnabled()
|
refreshRefreshWhenChargingOnlyEnabled()
|
||||||
}
|
}
|
||||||
return _refreshWhenChargingOnly == true
|
return refreshWhenChargingOnly == true
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun refreshRefreshMinutes() {
|
private fun refreshRefreshMinutes() {
|
||||||
_refreshMinutes = settings.getString(PERIODIC_REFRESH_MINUTES, "360").toLong()
|
refreshMinutes = settings.getString(PERIODIC_REFRESH_MINUTES, "360").toLong()
|
||||||
if (_refreshMinutes <= 15) {
|
if (refreshMinutes <= 15) {
|
||||||
_refreshMinutes = 15
|
refreshMinutes = 15
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getRefreshMinutes(): Long {
|
fun getRefreshMinutes(): Long {
|
||||||
if (_refreshMinutes != 360L) {
|
if (refreshMinutes != 360L) {
|
||||||
refreshRefreshMinutes()
|
refreshRefreshMinutes()
|
||||||
}
|
}
|
||||||
return _refreshMinutes
|
return refreshMinutes
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun refreshInfiniteLoadingEnabled() {
|
private fun refreshInfiniteLoadingEnabled() {
|
||||||
_infiniteLoading = settings.getBoolean(INFINITE_LOADING, false)
|
infiniteLoading = settings.getBoolean(INFINITE_LOADING, false)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun isInfiniteLoadingEnabled(): Boolean {
|
fun isInfiniteLoadingEnabled(): Boolean {
|
||||||
if (_infiniteLoading != null) {
|
if (infiniteLoading != null) {
|
||||||
refreshInfiniteLoadingEnabled()
|
refreshInfiniteLoadingEnabled()
|
||||||
}
|
}
|
||||||
return _infiniteLoading == true
|
return infiniteLoading == true
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun refreshItemCachingEnabled() {
|
private fun refreshItemCachingEnabled() {
|
||||||
_itemsCaching = settings.getBoolean(ITEMS_CACHING, false)
|
itemsCaching = settings.getBoolean(ITEMS_CACHING, false)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun isItemCachingEnabled(): Boolean {
|
fun isItemCachingEnabled(): Boolean {
|
||||||
if (_itemsCaching != null) {
|
if (itemsCaching != null) {
|
||||||
refreshItemCachingEnabled()
|
refreshItemCachingEnabled()
|
||||||
}
|
}
|
||||||
return _itemsCaching == true
|
return itemsCaching == true
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun refreshNotifyNewItemsEnabled() {
|
private fun refreshNotifyNewItemsEnabled() {
|
||||||
_notifyNewItems = settings.getBoolean(NOTIFY_NEW_ITEMS, false)
|
notifyNewItems = settings.getBoolean(NOTIFY_NEW_ITEMS, false)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun isNotifyNewItemsEnabled(): Boolean {
|
fun isNotifyNewItemsEnabled(): Boolean {
|
||||||
if (_notifyNewItems != null) {
|
if (notifyNewItems != null) {
|
||||||
refreshNotifyNewItemsEnabled()
|
refreshNotifyNewItemsEnabled()
|
||||||
}
|
}
|
||||||
return _notifyNewItems == true
|
return notifyNewItems == true
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun refreshMarkOnScrollEnabled() {
|
private fun refreshMarkOnScrollEnabled() {
|
||||||
_markOnScroll = settings.getBoolean(MARK_ON_SCROLL, false)
|
markOnScroll = settings.getBoolean(MARK_ON_SCROLL, false)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun isMarkOnScrollEnabled(): Boolean {
|
fun isMarkOnScrollEnabled(): Boolean {
|
||||||
if (_markOnScroll != null) {
|
if (markOnScroll != null) {
|
||||||
refreshMarkOnScrollEnabled()
|
refreshMarkOnScrollEnabled()
|
||||||
}
|
}
|
||||||
return _markOnScroll == true
|
return markOnScroll == true
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun refreshActiveAllignment() {
|
private fun refreshActiveAllignment() {
|
||||||
_activeAlignment = settings.getInt(TEXT_ALIGN, JUSTIFY)
|
activeAlignment = settings.getInt(TEXT_ALIGN, JUSTIFY)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getActiveAllignment(): Int {
|
fun getActiveAllignment(): Int {
|
||||||
if (_activeAlignment != null) {
|
if (activeAlignment != null) {
|
||||||
refreshActiveAllignment()
|
refreshActiveAllignment()
|
||||||
}
|
}
|
||||||
return _activeAlignment ?: JUSTIFY
|
return activeAlignment ?: JUSTIFY
|
||||||
}
|
}
|
||||||
|
|
||||||
fun changeAllignment(allignment: Int) {
|
fun changeAllignment(allignment: Int) {
|
||||||
settings.putInt(TEXT_ALIGN, allignment)
|
settings.putInt(TEXT_ALIGN, allignment)
|
||||||
_activeAlignment = allignment
|
activeAlignment = allignment
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun refreshFontSize() {
|
private fun refreshFontSize() {
|
||||||
_fontSize = settings.getString(READER_FONT_SIZE, "16").toInt()
|
fontSize = settings.getString(READER_FONT_SIZE, "16").toInt()
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getFontSize(): Int {
|
fun getFontSize(): Int {
|
||||||
if (_fontSize != null) {
|
if (fontSize != null) {
|
||||||
refreshFontSize()
|
refreshFontSize()
|
||||||
}
|
}
|
||||||
return _fontSize ?: 16
|
return fontSize ?: 16
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun refreshStaticBarEnabled() {
|
private fun refreshStaticBarEnabled() {
|
||||||
_staticBar = settings.getBoolean(READER_STATIC_BAR, false)
|
staticBar = settings.getBoolean(READER_STATIC_BAR, false)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun isStaticBarEnabled(): Boolean {
|
fun isStaticBarEnabled(): Boolean {
|
||||||
if (_staticBar != null) {
|
if (staticBar != null) {
|
||||||
refreshStaticBarEnabled()
|
refreshStaticBarEnabled()
|
||||||
}
|
}
|
||||||
return _staticBar == true
|
return staticBar == true
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun refreshFont() {
|
private fun refreshFont() {
|
||||||
_font = settings.getString(READER_FONT, "")
|
font = settings.getString(READER_FONT, "")
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getFont(): String {
|
fun getFont(): String {
|
||||||
if (_font.isEmpty()) {
|
if (font.isEmpty()) {
|
||||||
refreshFont()
|
refreshFont()
|
||||||
}
|
}
|
||||||
return _font
|
return font
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun refreshCurrentTheme() {
|
private fun refreshCurrentTheme() {
|
||||||
_theme = settings.getString(CURRENT_THEME, "-1").toInt()
|
theme = settings.getString(CURRENT_THEME, "-1").toInt()
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getCurrentTheme(): Int {
|
fun getCurrentTheme(): Int {
|
||||||
if (_theme == null) {
|
if (theme == null) {
|
||||||
refreshCurrentTheme()
|
refreshCurrentTheme()
|
||||||
}
|
}
|
||||||
return _theme ?: -1
|
return theme ?: -1
|
||||||
}
|
}
|
||||||
|
|
||||||
fun refreshApiSettings() {
|
fun refreshApiSettings() {
|
||||||
@ -478,15 +480,15 @@ class AppSettingsService(acraSenderServiceProcess: Boolean = false) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
const val translationUrl = "https://crwd.in/readerforselfoss"
|
const val TRANSLATION_URL = "https://crwd.in/readerforselfoss"
|
||||||
|
|
||||||
const val sourceUrl = "https://gitea.amine-bouabdallaoui.fr/Louvorg/ReaderForSelfoss-multiplatform"
|
const val SOURCE_URL = "https://gitea.amine-bouabdallaoui.fr/Louvorg/ReaderForSelfoss-multiplatform"
|
||||||
|
|
||||||
const val trackerUrl = "https://gitea.amine-bouabdallaoui.fr/Louvorg/ReaderForSelfoss-multiplatform/issues"
|
const val TRACKER_URL = "https://gitea.amine-bouabdallaoui.fr/Louvorg/ReaderForSelfoss-multiplatform/issues"
|
||||||
|
|
||||||
const val syncChannelId = "sync-channel-id"
|
const val SYNC_CHANNEL_ID = "sync-channel-id"
|
||||||
|
|
||||||
const val newItemsChannelId = "new-items-channel-id"
|
const val NEW_ITEMS_CHANNEL_ID = "new-items-channel-id"
|
||||||
|
|
||||||
const val JUSTIFY = 1
|
const val JUSTIFY = 1
|
||||||
|
|
||||||
|
@ -1,6 +1,10 @@
|
|||||||
package bou.amine.apps.readerforselfossv2.utils
|
package bou.amine.apps.readerforselfossv2.utils
|
||||||
|
|
||||||
enum class ItemType(val position: Int, val type: String) {
|
object Enums {
|
||||||
|
enum class ItemType(
|
||||||
|
val position: Int,
|
||||||
|
val type: String,
|
||||||
|
) {
|
||||||
UNREAD(1, "unread"),
|
UNREAD(1, "unread"),
|
||||||
ALL(2, "all"),
|
ALL(2, "all"),
|
||||||
STARRED(3, "starred"),
|
STARRED(3, "starred"),
|
||||||
@ -9,4 +13,5 @@ enum class ItemType(val position: Int, val type: String) {
|
|||||||
companion object {
|
companion object {
|
||||||
fun fromInt(value: Int) = values().first { it.position == value }
|
fun fromInt(value: Int) = values().first { it.position == value }
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -14,7 +14,6 @@ class DatesTest {
|
|||||||
private val oldVersionDate = "2013-05-07 13:46:00"
|
private val oldVersionDate = "2013-05-07 13:46:00"
|
||||||
private val oldVersionDateVariant = "2021-03-21 10:32:00.000000"
|
private val oldVersionDateVariant = "2021-03-21 10:32:00.000000"
|
||||||
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun new_version_date_should_be_parsed() {
|
fun new_version_date_should_be_parsed() {
|
||||||
val date = newVersionDate.toParsedDate()
|
val date = newVersionDate.toParsedDate()
|
||||||
@ -24,6 +23,7 @@ class DatesTest {
|
|||||||
|
|
||||||
assertEquals(expected, date)
|
assertEquals(expected, date)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun new_version_date2_should_be_parsed() {
|
fun new_version_date2_should_be_parsed() {
|
||||||
val date = newVersionDate2.toParsedDate()
|
val date = newVersionDate2.toParsedDate()
|
||||||
|
@ -2,5 +2,5 @@ package bou.amine.apps.readerforselfossv2.rest
|
|||||||
|
|
||||||
import io.ktor.client.engine.cio.CIOEngineConfig
|
import io.ktor.client.engine.cio.CIOEngineConfig
|
||||||
|
|
||||||
actual fun setupInsecureHTTPEngine(config: CIOEngineConfig) {
|
actual fun setupInsecureHttpEngine(config: CIOEngineConfig) {
|
||||||
}
|
}
|
@ -2,5 +2,5 @@ package bou.amine.apps.readerforselfossv2.rest
|
|||||||
|
|
||||||
import io.ktor.client.engine.cio.CIOEngineConfig
|
import io.ktor.client.engine.cio.CIOEngineConfig
|
||||||
|
|
||||||
actual fun setupInsecureHTTPEngine(config: CIOEngineConfig) {
|
actual fun setupInsecureHttpEngine(config: CIOEngineConfig) {
|
||||||
}
|
}
|
Loading…
x
Reference in New Issue
Block a user