chore: code style.
This commit is contained in:
@ -20,65 +20,72 @@ import org.hamcrest.Matchers.hasToString
|
||||
fun performLogin(someUrl: String? = null) {
|
||||
onView(withId(R.id.urlView)).perform(click()).perform(
|
||||
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())
|
||||
}
|
||||
|
||||
fun loginAndInitHome() {
|
||||
|
||||
performLogin()
|
||||
onView(withText(R.string.gdpr_dialog_title)).check(matches(isDisplayed()))
|
||||
onView(withText("OK")).perform(click())
|
||||
}
|
||||
|
||||
fun changeAndCancelSetting(oldValue: String, newValue: String, openSettingItem: () -> Unit) {
|
||||
fun changeAndCancelSetting(
|
||||
oldValue: String,
|
||||
newValue: String,
|
||||
openSettingItem: () -> Unit,
|
||||
) {
|
||||
openSettingItem()
|
||||
onView(
|
||||
withId(android.R.id.edit)
|
||||
withId(android.R.id.edit),
|
||||
).perform(replaceText(newValue))
|
||||
onView(
|
||||
withId(android.R.id.button2)
|
||||
withId(android.R.id.button2),
|
||||
).perform(click())
|
||||
openSettingItem()
|
||||
onView(
|
||||
withId(android.R.id.edit)
|
||||
withId(android.R.id.edit),
|
||||
).check(matches(withText(oldValue)))
|
||||
onView(
|
||||
withText(newValue)
|
||||
withText(newValue),
|
||||
).check(doesNotExist())
|
||||
onView(
|
||||
withId(android.R.id.button2)
|
||||
withId(android.R.id.button2),
|
||||
).perform(click())
|
||||
}
|
||||
|
||||
fun changeAndSaveSetting(oldValue: String, newValue: String, openSettingItem: () -> Unit) {
|
||||
fun changeAndSaveSetting(
|
||||
oldValue: String,
|
||||
newValue: String,
|
||||
openSettingItem: () -> Unit,
|
||||
) {
|
||||
openSettingItem()
|
||||
onView(
|
||||
withId(android.R.id.edit)
|
||||
withId(android.R.id.edit),
|
||||
).perform(replaceText(newValue))
|
||||
onView(
|
||||
withId(android.R.id.button1)
|
||||
withId(android.R.id.button1),
|
||||
).perform(click())
|
||||
openSettingItem()
|
||||
onView(
|
||||
withId(android.R.id.edit)
|
||||
withId(android.R.id.edit),
|
||||
).check(matches(withText(newValue)))
|
||||
if (oldValue.isNotEmpty()) {
|
||||
onView(
|
||||
withText(oldValue)
|
||||
withText(oldValue),
|
||||
).check(doesNotExist())
|
||||
}
|
||||
onView(
|
||||
withId(android.R.id.button2)
|
||||
withId(android.R.id.button2),
|
||||
).perform(click())
|
||||
}
|
||||
|
||||
fun testPreferencesFromArray(
|
||||
context: Context,
|
||||
@ArrayRes arrayRes: Int,
|
||||
openSettingItem: () -> Unit
|
||||
openSettingItem: () -> Unit,
|
||||
) {
|
||||
openSettingItem()
|
||||
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))
|
||||
.perform(click())
|
||||
onView(withId(R.id.nameInput))
|
||||
@ -106,4 +116,4 @@ fun testAddSourceWithUrl(url: String, sourceName: String) {
|
||||
onView(withId(R.id.saveBtn))
|
||||
.perform(click())
|
||||
onView(withText(sourceName)).check(matches(isDisplayed()))
|
||||
}
|
||||
}
|
||||
|
@ -25,8 +25,9 @@ import org.hamcrest.Matcher
|
||||
import org.hamcrest.Matchers
|
||||
import org.hamcrest.TypeSafeMatcher
|
||||
|
||||
|
||||
fun withError(@StringRes id: Int): TypeSafeMatcher<View?> {
|
||||
fun withError(
|
||||
@StringRes id: Int,
|
||||
): TypeSafeMatcher<View?> {
|
||||
return object : TypeSafeMatcher<View?>() {
|
||||
override fun matchesSafely(view: View?): Boolean {
|
||||
if (view == null) {
|
||||
@ -52,7 +53,9 @@ fun isPopupWindow(): Matcher<Root> {
|
||||
return isPlatformPopup()
|
||||
}
|
||||
|
||||
fun withDrawable(@DrawableRes id: Int) = object : TypeSafeMatcher<View>() {
|
||||
fun withDrawable(
|
||||
@DrawableRes id: Int,
|
||||
) = object : TypeSafeMatcher<View>() {
|
||||
override fun describeTo(description: Description) {
|
||||
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(
|
||||
withResourceName("fixed_bottom_navigation_icon"),
|
||||
withParent(
|
||||
allOf(
|
||||
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(
|
||||
withId(android.R.id.switch_widget),
|
||||
withParent(
|
||||
withSettingsCheckboxFrame(id)
|
||||
)
|
||||
withSettingsCheckboxFrame(id),
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
fun withSettingsCheckboxFrame(@StringRes id: Int): Matcher<View>? {
|
||||
fun withSettingsCheckboxFrame(
|
||||
@StringRes id: Int,
|
||||
): Matcher<View>? {
|
||||
return allOf(
|
||||
withId(android.R.id.widget_frame),
|
||||
hasSibling(
|
||||
allOf(
|
||||
withClassName(Matchers.equalTo(RelativeLayout::class.java.name)),
|
||||
withChild(
|
||||
withText(id)
|
||||
)
|
||||
)
|
||||
)
|
||||
withText(id),
|
||||
),
|
||||
),
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
fun openMenu() {
|
||||
openActionBarOverflowOrOptionsMenu(
|
||||
ApplicationProvider.getApplicationContext<Context>()
|
||||
ApplicationProvider.getApplicationContext<Context>(),
|
||||
)
|
||||
}
|
||||
}
|
@ -23,7 +23,6 @@ import org.junit.runner.RunWith
|
||||
@RunWith(AndroidJUnit4::class)
|
||||
@LargeTest
|
||||
class HomeActivityTest {
|
||||
|
||||
@get:Rule
|
||||
val activityRule = ActivityScenarioRule(LoginActivity::class.java)
|
||||
|
||||
@ -36,13 +35,13 @@ class HomeActivityTest {
|
||||
fun testMenu() {
|
||||
onView(withId(R.id.action_search)).check(matches(isDisplayed())).check(
|
||||
matches(
|
||||
isClickable()
|
||||
)
|
||||
isClickable(),
|
||||
),
|
||||
)
|
||||
onView(withId(R.id.action_filter)).check(matches(isDisplayed())).check(
|
||||
matches(
|
||||
isClickable()
|
||||
)
|
||||
isClickable(),
|
||||
),
|
||||
)
|
||||
openMenu()
|
||||
onView(withText(R.string.readAll)).check(matches(isDisplayed()))
|
||||
@ -57,19 +56,19 @@ class HomeActivityTest {
|
||||
fun testMenuActions() {
|
||||
onView(withId(R.id.action_search)).perform(click())
|
||||
onView(
|
||||
withId(R.id.search_src_text)
|
||||
withId(R.id.search_src_text),
|
||||
).check(matches(isFocused()))
|
||||
onView(isRoot()).perform(ViewActions.pressBack())
|
||||
|
||||
onView(withId(R.id.action_filter)).perform(click())
|
||||
onView(
|
||||
withText(R.string.filter_item_sources)
|
||||
withText(R.string.filter_item_sources),
|
||||
).check(matches(isDisplayed()))
|
||||
onView(
|
||||
withText(R.string.filter_item_tags)
|
||||
withText(R.string.filter_item_tags),
|
||||
).check(matches(isDisplayed()))
|
||||
onView(
|
||||
withId(R.id.floatingActionButton2)
|
||||
withId(R.id.floatingActionButton2),
|
||||
).check(matches(isDisplayed()))
|
||||
onView(isRoot()).perform(ViewActions.pressBack())
|
||||
|
||||
@ -107,14 +106,13 @@ class HomeActivityTest {
|
||||
fun testEmptyView() {
|
||||
onView(withId(R.id.emptyText)).check(matches(isDisplayed()))
|
||||
onView(
|
||||
hasBottombarItemText(R.string.tab_new)
|
||||
hasBottombarItemText(R.string.tab_new),
|
||||
).check(matches(isDisplayed())).check(matches(isSelected()))
|
||||
onView(
|
||||
hasBottombarItemText(R.string.tab_read)
|
||||
hasBottombarItemText(R.string.tab_read),
|
||||
).check(matches(isDisplayed())).check(matches(not(isSelected())))
|
||||
onView(
|
||||
hasBottombarItemText(R.string.tab_favs)
|
||||
hasBottombarItemText(R.string.tab_favs),
|
||||
).check(matches(isDisplayed())).check(matches(not(isSelected())))
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
@ -23,7 +23,6 @@ import org.junit.runner.RunWith
|
||||
@RunWith(AndroidJUnit4::class)
|
||||
@LargeTest
|
||||
class LoginActivityTest {
|
||||
|
||||
@get:Rule
|
||||
val activityRule = ActivityScenarioRule(LoginActivity::class.java)
|
||||
|
||||
@ -52,11 +51,11 @@ class LoginActivityTest {
|
||||
onView(withId(R.id.urlView)).check(matches(isDisplayed()))
|
||||
onView(withId(R.id.selfSigned)).check(matches(isDisplayed())).check(matches(isNotChecked()))
|
||||
.check(
|
||||
matches(isClickable())
|
||||
matches(isClickable()),
|
||||
)
|
||||
onView(withId(R.id.withLogin)).check(matches(isDisplayed()))
|
||||
.check(matches(isNotChecked())).check(
|
||||
matches(isClickable())
|
||||
matches(isClickable()),
|
||||
)
|
||||
}
|
||||
|
||||
@ -80,4 +79,4 @@ class LoginActivityTest {
|
||||
performLogin()
|
||||
onView(withText(R.string.gdpr_dialog_title)).check(matches(isDisplayed()))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -26,11 +26,9 @@ import org.junit.Rule
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
|
||||
|
||||
@RunWith(AndroidJUnit4::class)
|
||||
@LargeTest
|
||||
class SettingsActivityGeneralTest {
|
||||
|
||||
@get:Rule
|
||||
val activityRule = ActivityScenarioRule(LoginActivity::class.java)
|
||||
|
||||
@ -38,7 +36,7 @@ class SettingsActivityGeneralTest {
|
||||
fun init() {
|
||||
loginAndInitHome()
|
||||
openActionBarOverflowOrOptionsMenu(
|
||||
ApplicationProvider.getApplicationContext()
|
||||
ApplicationProvider.getApplicationContext(),
|
||||
)
|
||||
onView(withText(R.string.title_activity_settings)).perform(click())
|
||||
onView(withText(R.string.pref_header_general)).perform(click())
|
||||
@ -48,68 +46,75 @@ class SettingsActivityGeneralTest {
|
||||
fun testGeneral() {
|
||||
onView(withText(R.string.pref_api_items_number_title)).check(matches(isDisplayed()))
|
||||
onView(
|
||||
withSettingsCheckboxWidget(R.string.pref_general_infinite_loading_title)
|
||||
withSettingsCheckboxWidget(R.string.pref_general_infinite_loading_title),
|
||||
).check(
|
||||
matches(
|
||||
allOf(
|
||||
isDisplayed(), not(isChecked())
|
||||
)
|
||||
)
|
||||
isDisplayed(),
|
||||
not(isChecked()),
|
||||
),
|
||||
),
|
||||
)
|
||||
onView(withText(R.string.pref_general_category_links)).check(matches(isDisplayed()))
|
||||
onView(withSettingsCheckboxWidget(R.string.pref_article_viewer_title)).check(
|
||||
matches(
|
||||
allOf(
|
||||
isDisplayed(), isChecked()
|
||||
)
|
||||
)
|
||||
isDisplayed(),
|
||||
isChecked(),
|
||||
),
|
||||
),
|
||||
)
|
||||
onView(withSettingsCheckboxWidget(R.string.reader_static_bar_title)).check(
|
||||
matches(
|
||||
allOf(
|
||||
isDisplayed(), not(isChecked())
|
||||
)
|
||||
)
|
||||
isDisplayed(),
|
||||
not(isChecked()),
|
||||
),
|
||||
),
|
||||
)
|
||||
onView(withSettingsCheckboxFrame(R.string.reader_static_bar_title)).check(
|
||||
matches(
|
||||
isEnabled()
|
||||
)
|
||||
isEnabled(),
|
||||
),
|
||||
)
|
||||
onView(withText(R.string.pref_general_category_displaying)).check(matches(isDisplayed()))
|
||||
onView(withSettingsCheckboxWidget(R.string.pref_switch_card_view_title)).check(
|
||||
matches(
|
||||
allOf(
|
||||
isDisplayed(), not(isChecked())
|
||||
)
|
||||
)
|
||||
isDisplayed(),
|
||||
not(isChecked()),
|
||||
),
|
||||
),
|
||||
)
|
||||
onView(withSettingsCheckboxWidget(R.string.card_height_title)).check(
|
||||
matches(
|
||||
allOf(
|
||||
isDisplayed(), not(isChecked())
|
||||
)
|
||||
)
|
||||
isDisplayed(),
|
||||
not(isChecked()),
|
||||
),
|
||||
),
|
||||
)
|
||||
onView(withSettingsCheckboxFrame(R.string.card_height_title)).check(
|
||||
matches(
|
||||
not(isEnabled())
|
||||
)
|
||||
not(isEnabled()),
|
||||
),
|
||||
)
|
||||
onView(withSettingsCheckboxWidget(R.string.switch_unread_count_title)).check(
|
||||
matches(
|
||||
allOf(
|
||||
isDisplayed(), isChecked()
|
||||
)
|
||||
)
|
||||
isDisplayed(),
|
||||
isChecked(),
|
||||
),
|
||||
),
|
||||
)
|
||||
onView(withId(R.id.settings)).perform(swipeUp())
|
||||
onView(withSettingsCheckboxWidget(R.string.display_all_counts_title)).check(
|
||||
matches(
|
||||
allOf(
|
||||
isDisplayed(), not(isChecked())
|
||||
)
|
||||
)
|
||||
isDisplayed(),
|
||||
not(isChecked()),
|
||||
),
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
@ -120,25 +125,25 @@ class SettingsActivityGeneralTest {
|
||||
|
||||
// Value check
|
||||
onView(
|
||||
withId(android.R.id.edit)
|
||||
withId(android.R.id.edit),
|
||||
).perform(replaceText("AVC"))
|
||||
.check(matches(withText("")))
|
||||
// TODO: should check message error. Not working for api level 30+
|
||||
onView(
|
||||
withId(android.R.id.edit)
|
||||
withId(android.R.id.edit),
|
||||
).perform(replaceText("-1"))
|
||||
.check(matches(withText("")))
|
||||
// TODO: should check message error. Not working for api level 30+
|
||||
onView(
|
||||
withId(android.R.id.edit)
|
||||
withId(android.R.id.edit),
|
||||
).perform(replaceText("300"))
|
||||
.check(matches(withText("")))
|
||||
onView(
|
||||
withId(android.R.id.edit)
|
||||
withId(android.R.id.edit),
|
||||
).perform(typeTextIntoFocusedView("300"))
|
||||
.check(matches(withText("30")))
|
||||
onView(
|
||||
withId(android.R.id.edit)
|
||||
withId(android.R.id.edit),
|
||||
).perform(replaceText("10"))
|
||||
.check(matches(withText("10")))
|
||||
onView(isRoot()).perform(ViewActions.pressBack())
|
||||
@ -157,18 +162,18 @@ class SettingsActivityGeneralTest {
|
||||
// article viewer settings
|
||||
onView(withSettingsCheckboxFrame(R.string.reader_static_bar_title)).check(
|
||||
matches(
|
||||
isEnabled()
|
||||
)
|
||||
isEnabled(),
|
||||
),
|
||||
)
|
||||
onView(withSettingsCheckboxWidget(R.string.pref_article_viewer_title)).perform(click())
|
||||
onView(withSettingsCheckboxFrame(R.string.reader_static_bar_title)).check(
|
||||
matches(
|
||||
not(isEnabled())
|
||||
)
|
||||
not(isEnabled()),
|
||||
),
|
||||
)
|
||||
|
||||
onView(withSettingsCheckboxFrame(R.string.card_height_title)).check(matches(not(isEnabled())))
|
||||
onView(withSettingsCheckboxWidget(R.string.pref_switch_card_view_title)).perform(click())
|
||||
onView(withSettingsCheckboxFrame(R.string.card_height_title)).check(matches(isEnabled()))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -21,11 +21,9 @@ import org.junit.Rule
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
|
||||
|
||||
@RunWith(AndroidJUnit4::class)
|
||||
@LargeTest
|
||||
class SettingsActivityOfflineTest {
|
||||
|
||||
@get:Rule
|
||||
val activityRule = ActivityScenarioRule(LoginActivity::class.java)
|
||||
|
||||
@ -38,7 +36,7 @@ class SettingsActivityOfflineTest {
|
||||
}
|
||||
loginAndInitHome()
|
||||
openActionBarOverflowOrOptionsMenu(
|
||||
ApplicationProvider.getApplicationContext()
|
||||
ApplicationProvider.getApplicationContext(),
|
||||
)
|
||||
onView(withText(R.string.title_activity_settings)).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(
|
||||
matches(
|
||||
allOf(
|
||||
isDisplayed(), not(isChecked())
|
||||
)
|
||||
)
|
||||
isDisplayed(),
|
||||
not(isChecked()),
|
||||
),
|
||||
),
|
||||
)
|
||||
onView(withSettingsCheckboxWidget(R.string.pref_switch_items_caching)).check(
|
||||
matches(
|
||||
allOf(
|
||||
isDisplayed(), not(isChecked())
|
||||
)
|
||||
)
|
||||
isDisplayed(),
|
||||
not(isChecked()),
|
||||
),
|
||||
),
|
||||
)
|
||||
onView(withSettingsCheckboxFrame(R.string.pref_switch_items_caching)).check(
|
||||
matches(
|
||||
isEnabled()
|
||||
)
|
||||
isEnabled(),
|
||||
),
|
||||
)
|
||||
onView(withText(R.string.pref_periodic_refresh_minutes_title)).check(
|
||||
matches(
|
||||
allOf(isNotEnabled(), isDisplayed())
|
||||
)
|
||||
allOf(isNotEnabled(), isDisplayed()),
|
||||
),
|
||||
)
|
||||
|
||||
onView(withSettingsCheckboxWidget(R.string.pref_switch_refresh_when_charging)).check(
|
||||
matches(
|
||||
allOf(
|
||||
isDisplayed(), not(isChecked())
|
||||
)
|
||||
)
|
||||
isDisplayed(),
|
||||
not(isChecked()),
|
||||
),
|
||||
),
|
||||
)
|
||||
onView(withSettingsCheckboxFrame(R.string.pref_switch_refresh_when_charging)).check(
|
||||
matches(
|
||||
isNotEnabled()
|
||||
)
|
||||
isNotEnabled(),
|
||||
),
|
||||
)
|
||||
onView(withSettingsCheckboxWidget(R.string.pref_switch_notify_new_items)).check(
|
||||
matches(
|
||||
allOf(
|
||||
isDisplayed(), not(isChecked())
|
||||
)
|
||||
)
|
||||
isDisplayed(),
|
||||
not(isChecked()),
|
||||
),
|
||||
),
|
||||
)
|
||||
onView(withSettingsCheckboxFrame(R.string.pref_switch_notify_new_items)).check(
|
||||
matches(
|
||||
isNotEnabled()
|
||||
)
|
||||
isNotEnabled(),
|
||||
),
|
||||
)
|
||||
onView(withSettingsCheckboxWidget(R.string.pref_switch_update_sources)).check(
|
||||
matches(
|
||||
allOf(
|
||||
isDisplayed(), isChecked()
|
||||
)
|
||||
)
|
||||
isDisplayed(),
|
||||
isChecked(),
|
||||
),
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
@ -111,50 +114,50 @@ class SettingsActivityOfflineTest {
|
||||
onView(withText(R.string.pref_switch_items_caching_on)).check(matches(isDisplayed()))
|
||||
onView(withSettingsCheckboxFrame(R.string.pref_switch_items_caching)).check(
|
||||
matches(
|
||||
isEnabled()
|
||||
)
|
||||
isEnabled(),
|
||||
),
|
||||
)
|
||||
onView(withText(R.string.pref_periodic_refresh_minutes_title)).check(
|
||||
matches(
|
||||
isNotEnabled()
|
||||
)
|
||||
isNotEnabled(),
|
||||
),
|
||||
)
|
||||
onView(withSettingsCheckboxFrame(R.string.pref_switch_refresh_when_charging)).check(
|
||||
matches(
|
||||
isNotEnabled()
|
||||
)
|
||||
isNotEnabled(),
|
||||
),
|
||||
)
|
||||
onView(withSettingsCheckboxFrame(R.string.pref_switch_notify_new_items)).check(
|
||||
matches(
|
||||
isNotEnabled()
|
||||
)
|
||||
isNotEnabled(),
|
||||
),
|
||||
)
|
||||
|
||||
onView(withText(R.string.pref_switch_periodic_refresh_off)).check(
|
||||
matches(
|
||||
isDisplayed()
|
||||
)
|
||||
isDisplayed(),
|
||||
),
|
||||
)
|
||||
onView(withSettingsCheckboxWidget(R.string.pref_switch_periodic_refresh)).perform(click())
|
||||
onView(withText(R.string.pref_switch_periodic_refresh_on)).check(
|
||||
matches(
|
||||
isDisplayed()
|
||||
)
|
||||
isDisplayed(),
|
||||
),
|
||||
)
|
||||
onView(withSettingsCheckboxFrame(R.string.pref_periodic_refresh_minutes_title)).check(
|
||||
matches(
|
||||
isEnabled()
|
||||
)
|
||||
isEnabled(),
|
||||
),
|
||||
)
|
||||
onView(withSettingsCheckboxFrame(R.string.pref_switch_refresh_when_charging)).check(
|
||||
matches(
|
||||
isEnabled()
|
||||
)
|
||||
isEnabled(),
|
||||
),
|
||||
)
|
||||
onView(withSettingsCheckboxFrame(R.string.pref_switch_notify_new_items)).check(
|
||||
matches(
|
||||
isEnabled()
|
||||
)
|
||||
isEnabled(),
|
||||
),
|
||||
)
|
||||
changeAndCancelSetting("360", "123") {
|
||||
onView(withText(R.string.pref_periodic_refresh_minutes_title)).perform(click())
|
||||
@ -166,4 +169,4 @@ class SettingsActivityOfflineTest {
|
||||
onView(withSettingsCheckboxFrame(R.string.pref_switch_notify_new_items)).perform(click())
|
||||
onView(withSettingsCheckboxWidget(R.string.pref_switch_update_sources)).perform(click())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -19,11 +19,9 @@ import org.junit.Rule
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
|
||||
|
||||
@RunWith(AndroidJUnit4::class)
|
||||
@LargeTest
|
||||
class SettingsActivityReaderTest {
|
||||
|
||||
@get:Rule
|
||||
val activityRule = ActivityScenarioRule(LoginActivity::class.java)
|
||||
|
||||
@ -36,7 +34,7 @@ class SettingsActivityReaderTest {
|
||||
}
|
||||
loginAndInitHome()
|
||||
openActionBarOverflowOrOptionsMenu(
|
||||
ApplicationProvider.getApplicationContext()
|
||||
ApplicationProvider.getApplicationContext(),
|
||||
)
|
||||
onView(withText(R.string.title_activity_settings)).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(
|
||||
matches(
|
||||
allOf(
|
||||
isDisplayed(), not(
|
||||
isChecked()
|
||||
)
|
||||
)
|
||||
)
|
||||
isDisplayed(),
|
||||
not(
|
||||
isChecked(),
|
||||
),
|
||||
),
|
||||
),
|
||||
)
|
||||
onView(withText(R.string.pref_content_reader_font_size)).check(matches(isDisplayed()))
|
||||
onView(withText(R.string.settings_reader_font)).check(matches(isDisplayed()))
|
||||
@ -61,14 +60,14 @@ class SettingsActivityReaderTest {
|
||||
fun testReaderActions() {
|
||||
onView(withText(R.string.pref_switch_actions_pager_scroll_off)).check(
|
||||
matches(
|
||||
isDisplayed()
|
||||
)
|
||||
isDisplayed(),
|
||||
),
|
||||
)
|
||||
onView(withSettingsCheckboxFrame(R.string.pref_switch_actions_pager_scroll)).perform(click())
|
||||
onView(withText(R.string.pref_switch_actions_pager_scroll_on)).check(
|
||||
matches(
|
||||
isDisplayed()
|
||||
)
|
||||
isDisplayed(),
|
||||
),
|
||||
)
|
||||
|
||||
onView(withText(R.string.pref_content_reader_font_size)).perform(click())
|
||||
@ -83,4 +82,4 @@ class SettingsActivityReaderTest {
|
||||
onView(withText(R.string.settings_reader_font)).perform(click())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -20,7 +20,6 @@ import org.junit.runner.RunWith
|
||||
@RunWith(AndroidJUnit4::class)
|
||||
@LargeTest
|
||||
class SettingsActivityTest {
|
||||
|
||||
@get:Rule
|
||||
val activityRule = ActivityScenarioRule(LoginActivity::class.java)
|
||||
lateinit var context: Context
|
||||
@ -35,10 +34,8 @@ class SettingsActivityTest {
|
||||
onView(withText(R.string.title_activity_settings)).perform(click())
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
fun testAllSettings() {
|
||||
|
||||
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_offline)).check(matches(isDisplayed()))
|
||||
@ -48,14 +45,13 @@ class SettingsActivityTest {
|
||||
matches(
|
||||
allOf(
|
||||
isDisplayed(),
|
||||
not(isSelected())
|
||||
)
|
||||
)
|
||||
not(isSelected()),
|
||||
),
|
||||
),
|
||||
)
|
||||
onView(withText(R.string.action_about)).check(matches(isDisplayed()))
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
fun testThemes() {
|
||||
testPreferencesFromArray(context, R.array.ModeTitles) {
|
||||
@ -63,7 +59,6 @@ class SettingsActivityTest {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
fun testExperimentail() {
|
||||
onView(withText(R.string.pref_header_experimental)).perform(click())
|
||||
@ -75,13 +70,11 @@ class SettingsActivityTest {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
fun testBugReports() {
|
||||
onView(withText(R.string.pref_switch_disable_acra)).perform(click())
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
fun testLinks() {
|
||||
onView(withText(R.string.pref_header_links)).perform(click())
|
||||
@ -91,10 +84,9 @@ class SettingsActivityTest {
|
||||
onView(withText(R.string.translation)).check(matches(isDisplayed()))
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
fun testAbout() {
|
||||
onView(withText(R.string.action_about)).perform(click())
|
||||
onView(withText("ACRA")).check(matches(isDisplayed()))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -41,11 +41,10 @@ class SourcesActivityTest {
|
||||
fun addSource() {
|
||||
testAddSourceWithUrl(
|
||||
"https://lorem-rss.herokuapp.com/feed?unit=year&interval=1&length=10",
|
||||
sourceName
|
||||
sourceName,
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
fun addSourceCheckContent() {
|
||||
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.refresh_dialog_message)).check(matches(isDisplayed()))
|
||||
onView(
|
||||
withId(android.R.id.button1)
|
||||
withId(android.R.id.button1),
|
||||
).perform(click())
|
||||
Thread.sleep(10000)
|
||||
onView(withId(R.id.swipeRefreshLayout)).perform(swipeDown())
|
||||
@ -74,10 +73,9 @@ class SourcesActivityTest {
|
||||
onView(withText(sourceName)).check(doesNotExist())
|
||||
}
|
||||
|
||||
|
||||
private fun goToSources() {
|
||||
openMenu()
|
||||
onView(withText(R.string.menu_home_sources))
|
||||
.perform(click())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -35,7 +35,7 @@ import bou.amine.apps.readerforselfossv2.android.utils.openUrlInBrowser
|
||||
import bou.amine.apps.readerforselfossv2.model.SelfossModel
|
||||
import bou.amine.apps.readerforselfossv2.repository.Repository
|
||||
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.BottomNavigationItem
|
||||
import com.ashokvarma.bottomnavigation.TextBadgeItem
|
||||
@ -49,7 +49,10 @@ import org.kodein.di.instance
|
||||
import java.security.MessageDigest
|
||||
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 elementsShown: ItemType = ItemType.UNREAD
|
||||
@ -171,11 +174,12 @@ class HomeActivity : AppCompatActivity(), SearchView.OnQueryTextListener, DIAwar
|
||||
getElementsAccordingToTab()
|
||||
}
|
||||
} else {
|
||||
Toast.makeText(
|
||||
this@HomeActivity,
|
||||
"Found null when swiping at positon $position.",
|
||||
Toast.LENGTH_LONG,
|
||||
).show()
|
||||
Toast
|
||||
.makeText(
|
||||
this@HomeActivity,
|
||||
"Found null when swiping at positon $position.",
|
||||
Toast.LENGTH_LONG,
|
||||
).show()
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -200,15 +204,18 @@ class HomeActivity : AppCompatActivity(), SearchView.OnQueryTextListener, DIAwar
|
||||
tabNewBadge =
|
||||
TextBadgeItem()
|
||||
.setText("")
|
||||
.setHideOnSelect(false).hide(false)
|
||||
.setHideOnSelect(false)
|
||||
.hide(false)
|
||||
tabArchiveBadge =
|
||||
TextBadgeItem()
|
||||
.setText("")
|
||||
.setHideOnSelect(false).hide(false)
|
||||
.setHideOnSelect(false)
|
||||
.hide(false)
|
||||
tabStarredBadge =
|
||||
TextBadgeItem()
|
||||
.setText("")
|
||||
.setHideOnSelect(false).hide(false)
|
||||
.setHideOnSelect(false)
|
||||
.hide(false)
|
||||
|
||||
if (appSettingsService.isDisplayUnreadCountEnabled()) {
|
||||
lifecycleScope.launch {
|
||||
@ -236,14 +243,12 @@ class HomeActivity : AppCompatActivity(), SearchView.OnQueryTextListener, DIAwar
|
||||
BottomNavigationItem(
|
||||
R.drawable.ic_tab_fiber_new_black_24dp,
|
||||
getString(R.string.tab_new),
|
||||
)
|
||||
.setBadgeItem(tabNewBadge)
|
||||
).setBadgeItem(tabNewBadge)
|
||||
val tabArchive =
|
||||
BottomNavigationItem(
|
||||
R.drawable.ic_tab_archive_black_24dp,
|
||||
getString(R.string.tab_read),
|
||||
)
|
||||
.setBadgeItem(tabArchiveBadge)
|
||||
).setBadgeItem(tabArchiveBadge)
|
||||
val tabStarred =
|
||||
BottomNavigationItem(
|
||||
R.drawable.ic_tab_favorite_black_24dp,
|
||||
@ -425,17 +430,17 @@ class HomeActivity : AppCompatActivity(), SearchView.OnQueryTextListener, DIAwar
|
||||
binding.recyclerView.addOnScrollListener(recyclerViewScrollListener)
|
||||
}
|
||||
|
||||
private fun getLastVisibleItem(): Int {
|
||||
return when (val manager = binding.recyclerView.layoutManager) {
|
||||
private fun getLastVisibleItem(): Int =
|
||||
when (val manager = binding.recyclerView.layoutManager) {
|
||||
is StaggeredGridLayoutManager ->
|
||||
manager.findLastCompletelyVisibleItemPositions(
|
||||
null,
|
||||
).last()
|
||||
manager
|
||||
.findLastCompletelyVisibleItemPositions(
|
||||
null,
|
||||
).last()
|
||||
|
||||
is GridLayoutManager -> manager.findLastCompletelyVisibleItemPosition()
|
||||
else -> 0
|
||||
}
|
||||
}
|
||||
|
||||
private fun mayBeEmpty() =
|
||||
if (items.isEmpty()) {
|
||||
@ -577,7 +582,8 @@ class HomeActivity : AppCompatActivity(), SearchView.OnQueryTextListener, DIAwar
|
||||
messageRes: Int,
|
||||
doFn: () -> Unit,
|
||||
) {
|
||||
AlertDialog.Builder(this@HomeActivity)
|
||||
AlertDialog
|
||||
.Builder(this@HomeActivity)
|
||||
.setMessage(messageRes)
|
||||
.setTitle(titleRes)
|
||||
.setPositiveButton(android.R.string.ok) { _, _ -> doFn() }
|
||||
@ -589,7 +595,7 @@ class HomeActivity : AppCompatActivity(), SearchView.OnQueryTextListener, DIAwar
|
||||
override fun onOptionsItemSelected(item: MenuItem): Boolean {
|
||||
when (item.itemId) {
|
||||
R.id.issue_tracker -> {
|
||||
baseContext.openUrlInBrowser(AppSettingsService.trackerUrl)
|
||||
baseContext.openUrlInBrowser(AppSettingsService.TRACKER_URL)
|
||||
return true
|
||||
}
|
||||
|
||||
@ -606,18 +612,19 @@ class HomeActivity : AppCompatActivity(), SearchView.OnQueryTextListener, DIAwar
|
||||
CoroutineScope(Dispatchers.Main).launch {
|
||||
val updatedRemote = repository.updateRemote()
|
||||
if (updatedRemote) {
|
||||
Toast.makeText(
|
||||
this@HomeActivity,
|
||||
R.string.refresh_success_response,
|
||||
Toast.LENGTH_LONG,
|
||||
)
|
||||
.show()
|
||||
Toast
|
||||
.makeText(
|
||||
this@HomeActivity,
|
||||
R.string.refresh_success_response,
|
||||
Toast.LENGTH_LONG,
|
||||
).show()
|
||||
} else {
|
||||
Toast.makeText(
|
||||
this@HomeActivity,
|
||||
R.string.refresh_failer_message,
|
||||
Toast.LENGTH_SHORT,
|
||||
).show()
|
||||
Toast
|
||||
.makeText(
|
||||
this@HomeActivity,
|
||||
R.string.refresh_failer_message,
|
||||
Toast.LENGTH_SHORT,
|
||||
).show()
|
||||
}
|
||||
CountingIdlingResourceSingleton.decrement()
|
||||
}
|
||||
@ -633,25 +640,26 @@ class HomeActivity : AppCompatActivity(), SearchView.OnQueryTextListener, DIAwar
|
||||
CoroutineScope(Dispatchers.Main).launch {
|
||||
val success = repository.markAllAsRead(items)
|
||||
if (success) {
|
||||
Toast.makeText(
|
||||
this@HomeActivity,
|
||||
R.string.all_posts_read,
|
||||
Toast.LENGTH_SHORT,
|
||||
).show()
|
||||
Toast
|
||||
.makeText(
|
||||
this@HomeActivity,
|
||||
R.string.all_posts_read,
|
||||
Toast.LENGTH_SHORT,
|
||||
).show()
|
||||
tabNewBadge.removeBadge()
|
||||
|
||||
getElementsAccordingToTab()
|
||||
} else {
|
||||
Toast.makeText(
|
||||
this@HomeActivity,
|
||||
R.string.all_posts_not_read,
|
||||
Toast.LENGTH_SHORT,
|
||||
).show()
|
||||
Toast
|
||||
.makeText(
|
||||
this@HomeActivity,
|
||||
R.string.all_posts_not_read,
|
||||
Toast.LENGTH_SHORT,
|
||||
).show()
|
||||
}
|
||||
handleListResult()
|
||||
binding.swipeRefreshLayout.isRefreshing = false
|
||||
CountingIdlingResourceSingleton.decrement()
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -661,7 +669,7 @@ class HomeActivity : AppCompatActivity(), SearchView.OnQueryTextListener, DIAwar
|
||||
R.id.action_disconnect -> {
|
||||
needsConfirmation(
|
||||
R.string.confirm_disconnect_title,
|
||||
R.string.confirm_disconnect_description
|
||||
R.string.confirm_disconnect_description,
|
||||
) {
|
||||
runBlocking {
|
||||
repository.logout()
|
||||
@ -702,7 +710,8 @@ class HomeActivity : AppCompatActivity(), SearchView.OnQueryTextListener, DIAwar
|
||||
private fun handleRecurringTask() {
|
||||
if (appSettingsService.isPeriodicRefreshEnabled()) {
|
||||
val myConstraints =
|
||||
Constraints.Builder()
|
||||
Constraints
|
||||
.Builder()
|
||||
.setRequiresBatteryNotLow(true)
|
||||
.setRequiresCharging(appSettingsService.isRefreshWhenChargingOnlyEnabled())
|
||||
.setRequiresStorageNotLow(true)
|
||||
@ -711,19 +720,19 @@ class HomeActivity : AppCompatActivity(), SearchView.OnQueryTextListener, DIAwar
|
||||
val backgroundWork =
|
||||
PeriodicWorkRequestBuilder<LoadingWorker>(
|
||||
appSettingsService.getRefreshMinutes(),
|
||||
TimeUnit.MINUTES
|
||||
)
|
||||
.setConstraints(myConstraints)
|
||||
TimeUnit.MINUTES,
|
||||
).setConstraints(myConstraints)
|
||||
.addTag("selfoss-loading")
|
||||
.build()
|
||||
|
||||
WorkManager.getInstance(
|
||||
baseContext,
|
||||
).enqueueUniquePeriodicWork(
|
||||
"selfoss-loading",
|
||||
ExistingPeriodicWorkPolicy.KEEP,
|
||||
backgroundWork
|
||||
)
|
||||
WorkManager
|
||||
.getInstance(
|
||||
baseContext,
|
||||
).enqueueUniquePeriodicWork(
|
||||
"selfoss-loading",
|
||||
ExistingPeriodicWorkPolicy.KEEP,
|
||||
backgroundWork,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -30,7 +30,9 @@ import org.kodein.di.DIAware
|
||||
import org.kodein.di.android.closestDI
|
||||
import org.kodein.di.instance
|
||||
|
||||
class LoginActivity : AppCompatActivity(), DIAware {
|
||||
class LoginActivity :
|
||||
AppCompatActivity(),
|
||||
DIAware {
|
||||
private var inValidCount: Int = 0
|
||||
private var isWithLogin = false
|
||||
|
||||
@ -108,7 +110,7 @@ class LoginActivity : AppCompatActivity(), DIAware {
|
||||
repository.updateApiInformation()
|
||||
ACRA.errorReporter.putCustomData(
|
||||
"SELFOSS_API_VERSION",
|
||||
appSettingsService.getApiVersion().toString()
|
||||
appSettingsService.getApiVersion().toString(),
|
||||
)
|
||||
CountingIdlingResourceSingleton.decrement()
|
||||
}
|
||||
@ -151,11 +153,12 @@ class LoginActivity : AppCompatActivity(), DIAware {
|
||||
repository.updateApiInformation()
|
||||
} catch (e: Exception) {
|
||||
if (e.message?.startsWith("No transformation found") == true) {
|
||||
Toast.makeText(
|
||||
applicationContext,
|
||||
R.string.application_selfoss_only,
|
||||
Toast.LENGTH_LONG,
|
||||
).show()
|
||||
Toast
|
||||
.makeText(
|
||||
applicationContext,
|
||||
R.string.application_selfoss_only,
|
||||
Toast.LENGTH_LONG,
|
||||
).show()
|
||||
preferenceError()
|
||||
showProgress(false)
|
||||
}
|
||||
@ -270,7 +273,7 @@ class LoginActivity : AppCompatActivity(), DIAware {
|
||||
return when (item.itemId) {
|
||||
R.id.issue_tracker -> {
|
||||
val browserIntent =
|
||||
Intent(Intent.ACTION_VIEW, Uri.parse(AppSettingsService.trackerUrl))
|
||||
Intent(Intent.ACTION_VIEW, Uri.parse(AppSettingsService.TRACKER_URL))
|
||||
startActivity(browserIntent)
|
||||
return true
|
||||
}
|
||||
@ -280,9 +283,9 @@ class LoginActivity : AppCompatActivity(), DIAware {
|
||||
.withAboutIconShown(true)
|
||||
.withAboutVersionShown(true)
|
||||
.withAboutSpecial2("Bug reports")
|
||||
.withAboutSpecial2Description(AppSettingsService.trackerUrl)
|
||||
.withAboutSpecial2Description(AppSettingsService.TRACKER_URL)
|
||||
.withAboutSpecial1("Project Page")
|
||||
.withAboutSpecial1Description(AppSettingsService.sourceUrl)
|
||||
.withAboutSpecial1Description(AppSettingsService.SOURCE_URL)
|
||||
.start(this)
|
||||
true
|
||||
}
|
||||
@ -290,4 +293,4 @@ class LoginActivity : AppCompatActivity(), DIAware {
|
||||
else -> super.onOptionsItemSelected(item)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -9,11 +9,11 @@ import androidx.lifecycle.DefaultLifecycleObserver
|
||||
import androidx.lifecycle.LifecycleOwner
|
||||
import androidx.lifecycle.ProcessLifecycleOwner
|
||||
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.viewmodel.AppViewModel
|
||||
import bou.amine.apps.readerforselfossv2.dao.DriverFactory
|
||||
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.service.AppSettingsService
|
||||
import com.github.ln_12.library.ConnectivityStatus
|
||||
@ -36,21 +36,23 @@ import org.kodein.di.bind
|
||||
import org.kodein.di.instance
|
||||
import org.kodein.di.singleton
|
||||
|
||||
class MyApp : MultiDexApplication(), DIAware {
|
||||
class MyApp :
|
||||
MultiDexApplication(),
|
||||
DIAware {
|
||||
override val di by DI.lazy {
|
||||
bind<AppSettingsService>() with singleton { AppSettingsService(ACRA.isACRASenderServiceProcess() || TestingHelper().isUnitTest()) }
|
||||
import(networkModule)
|
||||
bind<DriverFactory>() with singleton { DriverFactory(applicationContext) }
|
||||
bind<ReaderForSelfossDB>() with singleton { ReaderForSelfossDB(driverFactory.createDriver()) }
|
||||
bind<Repository>() with
|
||||
singleton {
|
||||
Repository(
|
||||
instance(),
|
||||
instance(),
|
||||
isConnectionAvailable,
|
||||
instance(),
|
||||
)
|
||||
}
|
||||
singleton {
|
||||
Repository(
|
||||
instance(),
|
||||
instance(),
|
||||
isConnectionAvailable,
|
||||
instance(),
|
||||
)
|
||||
}
|
||||
bind<ConnectivityStatus>() with singleton { ConnectivityStatus(applicationContext) }
|
||||
bind<AppViewModel>() with singleton { AppViewModel(repository = instance()) }
|
||||
}
|
||||
@ -89,11 +91,12 @@ class MyApp : MultiDexApplication(), DIAware {
|
||||
R.string.network_connectivity_lost
|
||||
}
|
||||
|
||||
Toast.makeText(
|
||||
applicationContext,
|
||||
toastMessage,
|
||||
Toast.LENGTH_SHORT,
|
||||
).show()
|
||||
Toast
|
||||
.makeText(
|
||||
applicationContext,
|
||||
toastMessage,
|
||||
Toast.LENGTH_SHORT,
|
||||
).show()
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -151,13 +154,13 @@ class MyApp : MultiDexApplication(), DIAware {
|
||||
|
||||
val name = getString(R.string.notification_channel_sync)
|
||||
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 newItemsChannelimportance = NotificationManager.IMPORTANCE_DEFAULT
|
||||
val newItemsChannelmChannel =
|
||||
NotificationChannel(
|
||||
AppSettingsService.newItemsChannelId,
|
||||
AppSettingsService.NEW_ITEMS_CHANNEL_ID,
|
||||
newItemsChannelname,
|
||||
newItemsChannelimportance,
|
||||
)
|
||||
@ -199,4 +202,4 @@ class MyApp : MultiDexApplication(), DIAware {
|
||||
super.onPause(owner)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -103,8 +103,7 @@ class ReaderActivity : AppCompatActivity(), DIAware {
|
||||
FragmentStateAdapter(fa) {
|
||||
override fun getItemCount(): Int = allItems.size
|
||||
|
||||
override fun createFragment(position: Int): Fragment =
|
||||
ArticleFragment.newInstance(allItems[position])
|
||||
override fun createFragment(position: Int): Fragment = ArticleFragment.newInstance(allItems[position])
|
||||
}
|
||||
|
||||
override fun onKeyDown(
|
||||
@ -229,4 +228,4 @@ class ReaderActivity : AppCompatActivity(), DIAware {
|
||||
startActivity(intent)
|
||||
overridePendingTransition(0, 0)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -81,4 +81,4 @@ class SourcesActivity : AppCompatActivity(), DIAware {
|
||||
startActivity(Intent(this@SourcesActivity, UpsertSourceActivity::class.java))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -49,7 +49,10 @@ class ItemCardAdapter(
|
||||
return ViewHolder(binding)
|
||||
}
|
||||
|
||||
private fun handleClickListeners(holderBinding: CardItemBinding, position: Int) {
|
||||
private fun handleClickListeners(
|
||||
holderBinding: CardItemBinding,
|
||||
position: Int,
|
||||
) {
|
||||
holderBinding.favButton.setOnClickListener {
|
||||
val item = items[position]
|
||||
if (item.starred) {
|
||||
@ -96,12 +99,13 @@ class ItemCardAdapter(
|
||||
|
||||
binding.title.setLinkTextColor(c.resources.getColor(R.color.colorAccent))
|
||||
|
||||
binding.sourceTitleAndDate.text = try {
|
||||
itm.sourceAuthorAndDate()
|
||||
} catch (e: Exception) {
|
||||
e.sendSilentlyWithAcraWithName("ItemCardAdapter parse date")
|
||||
itm.sourceAuthorOnly()
|
||||
}
|
||||
binding.sourceTitleAndDate.text =
|
||||
try {
|
||||
itm.sourceAuthorAndDate()
|
||||
} catch (e: Exception) {
|
||||
e.sendSilentlyWithAcraWithName("ItemCardAdapter parse date")
|
||||
itm.sourceAuthorOnly()
|
||||
}
|
||||
|
||||
if (!appSettingsService.isFullHeightCardsEnabled()) {
|
||||
binding.itemImage.maxHeight = imageMaxHeight
|
||||
@ -126,4 +130,4 @@ class ItemCardAdapter(
|
||||
}
|
||||
|
||||
inner class ViewHolder(val binding: CardItemBinding) : RecyclerView.ViewHolder(binding.root)
|
||||
}
|
||||
}
|
||||
|
@ -53,12 +53,13 @@ class ItemListAdapter(
|
||||
|
||||
binding.title.setLinkTextColor(c.resources.getColor(R.color.colorAccent))
|
||||
|
||||
binding.sourceTitleAndDate.text = try {
|
||||
itm.sourceAuthorAndDate()
|
||||
} catch (e: Exception) {
|
||||
e.sendSilentlyWithAcraWithName("ItemListAdapter parse date")
|
||||
itm.sourceAuthorOnly()
|
||||
}
|
||||
binding.sourceTitleAndDate.text =
|
||||
try {
|
||||
itm.sourceAuthorAndDate()
|
||||
} catch (e: Exception) {
|
||||
e.sendSilentlyWithAcraWithName("ItemListAdapter parse date")
|
||||
itm.sourceAuthorOnly()
|
||||
}
|
||||
|
||||
if (itm.getThumbnail(repository.baseUrl).isEmpty()) {
|
||||
if (itm.getIcon(repository.baseUrl).isEmpty()) {
|
||||
@ -73,4 +74,4 @@ class ItemListAdapter(
|
||||
}
|
||||
|
||||
inner class ViewHolder(val binding: ListItemBinding) : RecyclerView.ViewHolder(binding.root)
|
||||
}
|
||||
}
|
||||
|
@ -11,14 +11,16 @@ import bou.amine.apps.readerforselfossv2.android.utils.openItemUrl
|
||||
import bou.amine.apps.readerforselfossv2.model.SelfossModel
|
||||
import bou.amine.apps.readerforselfossv2.repository.Repository
|
||||
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 kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.launch
|
||||
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 repository: Repository
|
||||
abstract val binding: ViewBinding
|
||||
@ -45,8 +47,7 @@ abstract class ItemsAdapter<VH : RecyclerView.ViewHolder?> : RecyclerView.Adapte
|
||||
app.findViewById(R.id.coordLayout),
|
||||
R.string.marked_as_read,
|
||||
Snackbar.LENGTH_LONG,
|
||||
)
|
||||
.setAction(R.string.undo_string) {
|
||||
).setAction(R.string.undo_string) {
|
||||
unreadItemAtIndex(item, position, false)
|
||||
}
|
||||
|
||||
@ -66,8 +67,7 @@ abstract class ItemsAdapter<VH : RecyclerView.ViewHolder?> : RecyclerView.Adapte
|
||||
app.findViewById(R.id.coordLayout),
|
||||
R.string.marked_as_unread,
|
||||
Snackbar.LENGTH_LONG,
|
||||
)
|
||||
.setAction(R.string.undo_string) {
|
||||
).setAction(R.string.undo_string) {
|
||||
readItemAtIndex(item, position, false)
|
||||
}
|
||||
|
||||
@ -77,7 +77,10 @@ abstract class ItemsAdapter<VH : RecyclerView.ViewHolder?> : RecyclerView.Adapte
|
||||
s.show()
|
||||
}
|
||||
|
||||
protected fun handleLinkOpening(holderBinding: ViewBinding, position: Int) {
|
||||
protected fun handleLinkOpening(
|
||||
holderBinding: ViewBinding,
|
||||
position: Int,
|
||||
) {
|
||||
holderBinding.root.setOnClickListener {
|
||||
repository.setReaderItems(items)
|
||||
c.openItemUrl(
|
||||
|
@ -23,11 +23,13 @@ import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.launch
|
||||
import org.kodein.di.DIAware
|
||||
import org.kodein.di.instance
|
||||
import java.util.*
|
||||
import java.util.Timer
|
||||
import kotlin.concurrent.schedule
|
||||
|
||||
class LoadingWorker(val context: Context, params: WorkerParameters) :
|
||||
Worker(context, params),
|
||||
class LoadingWorker(
|
||||
val context: Context,
|
||||
params: WorkerParameters,
|
||||
) : Worker(context, params),
|
||||
DIAware {
|
||||
override val di by lazy { (applicationContext as MyApp).di }
|
||||
private val repository: Repository by instance()
|
||||
@ -40,12 +42,13 @@ class LoadingWorker(val context: Context, params: WorkerParameters) :
|
||||
applicationContext.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
|
||||
|
||||
val notification =
|
||||
NotificationCompat.Builder(applicationContext, AppSettingsService.syncChannelId)
|
||||
NotificationCompat
|
||||
.Builder(applicationContext, AppSettingsService.SYNC_CHANNEL_ID)
|
||||
.setContentTitle(context.getString(R.string.loading_notification_title))
|
||||
.setContentText(context.getString(R.string.loading_notification_text))
|
||||
.setOngoing(true)
|
||||
.setPriority(PRIORITY_LOW)
|
||||
.setChannelId(AppSettingsService.syncChannelId)
|
||||
.setChannelId(AppSettingsService.SYNC_CHANNEL_ID)
|
||||
.setSmallIcon(R.drawable.ic_stat_cloud_download_black_24dp)
|
||||
|
||||
notificationManager.notify(1, notification.build())
|
||||
@ -87,19 +90,18 @@ class LoadingWorker(val context: Context, params: WorkerParameters) :
|
||||
PendingIntent.getActivity(context, 0, intent, pflags)
|
||||
|
||||
val newItemsNotification =
|
||||
NotificationCompat.Builder(
|
||||
applicationContext,
|
||||
AppSettingsService.newItemsChannelId,
|
||||
)
|
||||
.setContentTitle(context.getString(R.string.new_items_notification_title))
|
||||
NotificationCompat
|
||||
.Builder(
|
||||
applicationContext,
|
||||
AppSettingsService.NEW_ITEMS_CHANNEL_ID,
|
||||
).setContentTitle(context.getString(R.string.new_items_notification_title))
|
||||
.setContentText(
|
||||
context.getString(
|
||||
R.string.new_items_notification_text,
|
||||
newSize,
|
||||
),
|
||||
)
|
||||
.setPriority(PRIORITY_DEFAULT)
|
||||
.setChannelId(AppSettingsService.newItemsChannelId)
|
||||
).setPriority(PRIORITY_DEFAULT)
|
||||
.setChannelId(AppSettingsService.NEW_ITEMS_CHANNEL_ID)
|
||||
.setContentIntent(pendingIntent)
|
||||
.setAutoCancel(true)
|
||||
.setSmallIcon(R.drawable.ic_tab_fiber_new_black_24dp)
|
@ -115,12 +115,13 @@ class ArticleFragment : Fragment(), DIAware {
|
||||
contentText = item.content
|
||||
contentTitle = item.title.getHtmlDecoded()
|
||||
contentImage = item.getThumbnail(repository.baseUrl)
|
||||
contentSource = try {
|
||||
item.sourceAuthorAndDate()
|
||||
} catch (e: Exception) {
|
||||
e.sendSilentlyWithAcraWithName("Article Fragment parse date")
|
||||
item.sourceAuthorOnly()
|
||||
}
|
||||
contentSource =
|
||||
try {
|
||||
item.sourceAuthorAndDate()
|
||||
} catch (e: Exception) {
|
||||
e.sendSilentlyWithAcraWithName("Article Fragment parse date")
|
||||
item.sourceAuthorOnly()
|
||||
}
|
||||
allImages = item.getImages()
|
||||
|
||||
fontSize = appSettingsService.getFontSize()
|
||||
@ -337,7 +338,10 @@ class ArticleFragment : Fragment(), DIAware {
|
||||
view: WebView?,
|
||||
url: String,
|
||||
): 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)
|
||||
true
|
||||
} else {
|
||||
@ -422,7 +426,6 @@ class ArticleFragment : Fragment(), DIAware {
|
||||
context.theme.resolveAttribute(R.attr.colorOnSurface, colorOnSurface, true)
|
||||
|
||||
context.theme.resolveAttribute(R.attr.colorSurface, colorSurface, true)
|
||||
|
||||
} catch (e: IllegalStateException) {
|
||||
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 ->
|
||||
gestureDetector.onTouchEvent(
|
||||
event
|
||||
event,
|
||||
)
|
||||
}
|
||||
|
||||
@ -598,9 +601,9 @@ class ArticleFragment : Fragment(), DIAware {
|
||||
|
||||
fun performClick(): Boolean {
|
||||
if (allImages != null && (
|
||||
binding.webcontent.hitTestResult.type == WebView.HitTestResult.IMAGE_TYPE ||
|
||||
binding.webcontent.hitTestResult.type == WebView.HitTestResult.SRC_IMAGE_ANCHOR_TYPE
|
||||
)
|
||||
binding.webcontent.hitTestResult.type == WebView.HitTestResult.IMAGE_TYPE ||
|
||||
binding.webcontent.hitTestResult.type == WebView.HitTestResult.SRC_IMAGE_ANCHOR_TYPE
|
||||
)
|
||||
) {
|
||||
val position: Int = allImages.indexOf(binding.webcontent.hitTestResult.extra)
|
||||
|
||||
@ -612,4 +615,4 @@ class ArticleFragment : Fragment(), DIAware {
|
||||
}
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -190,4 +190,4 @@ class FilterSheetFragment : BottomSheetDialogFragment(), DIAware {
|
||||
companion object {
|
||||
const val TAG = "FilterModalBottomSheet"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -40,4 +40,4 @@ fun String.toTextDrawableString(): String {
|
||||
}
|
||||
}
|
||||
return textDrawable.toString()
|
||||
}
|
||||
}
|
||||
|
@ -64,15 +64,14 @@ class SettingsActivity :
|
||||
outState.putCharSequence(TITLE_TAG, title)
|
||||
}
|
||||
|
||||
override fun onSupportNavigateUp(): Boolean {
|
||||
return if (supportFragmentManager.popBackStackImmediate()) {
|
||||
override fun onSupportNavigateUp(): Boolean =
|
||||
if (supportFragmentManager.popBackStackImmediate()) {
|
||||
supportActionBar?.title = getText(R.string.title_activity_settings)
|
||||
false
|
||||
} else {
|
||||
super.onBackPressed()
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
override fun onPreferenceStartFragment(
|
||||
caller: PreferenceFragmentCompat,
|
||||
@ -81,15 +80,17 @@ class SettingsActivity :
|
||||
// Instantiate the new Fragment
|
||||
val args = pref.extras
|
||||
val fragment =
|
||||
supportFragmentManager.fragmentFactory.instantiate(
|
||||
classLoader,
|
||||
pref.fragment.toString(),
|
||||
).apply {
|
||||
arguments = args
|
||||
setTargetFragment(caller, 0)
|
||||
}
|
||||
supportFragmentManager.fragmentFactory
|
||||
.instantiate(
|
||||
classLoader,
|
||||
pref.fragment.toString(),
|
||||
).apply {
|
||||
arguments = args
|
||||
setTargetFragment(caller, 0)
|
||||
}
|
||||
// Replace the existing Fragment with the new Fragment
|
||||
supportFragmentManager.beginTransaction()
|
||||
supportFragmentManager
|
||||
.beginTransaction()
|
||||
.replace(R.id.settings, fragment)
|
||||
.addToBackStack(null)
|
||||
.commit()
|
||||
@ -108,7 +109,7 @@ class SettingsActivity :
|
||||
preferenceManager.findPreference<Preference>(CURRENT_THEME)?.onPreferenceChangeListener =
|
||||
Preference.OnPreferenceChangeListener { _, newValue ->
|
||||
AppCompatDelegate.setDefaultNightMode(
|
||||
newValue.toString().toInt()
|
||||
newValue.toString().toInt(),
|
||||
) // ListPreference Only takes string-arrays ¯\_(ツ)_/¯
|
||||
true
|
||||
}
|
||||
@ -144,11 +145,12 @@ class SettingsActivity :
|
||||
val input: Int = (dest.toString() + source.toString()).toInt()
|
||||
if (input in 1..200) return@InputFilter null
|
||||
} catch (nfe: NumberFormatException) {
|
||||
Toast.makeText(
|
||||
activity,
|
||||
R.string.items_number_should_be_number,
|
||||
Toast.LENGTH_LONG
|
||||
).show()
|
||||
Toast
|
||||
.makeText(
|
||||
activity,
|
||||
R.string.items_number_should_be_number,
|
||||
Toast.LENGTH_LONG,
|
||||
).show()
|
||||
}
|
||||
""
|
||||
},
|
||||
@ -234,19 +236,19 @@ class SettingsActivity :
|
||||
|
||||
preferenceManager.findPreference<Preference>("trackerLink")?.onPreferenceClickListener =
|
||||
Preference.OnPreferenceClickListener {
|
||||
openUrl(AppSettingsService.trackerUrl)
|
||||
openUrl(AppSettingsService.TRACKER_URL)
|
||||
true
|
||||
}
|
||||
|
||||
preferenceManager.findPreference<Preference>("sourceLink")?.onPreferenceClickListener =
|
||||
Preference.OnPreferenceClickListener {
|
||||
openUrl(AppSettingsService.sourceUrl)
|
||||
openUrl(AppSettingsService.SOURCE_URL)
|
||||
false
|
||||
}
|
||||
|
||||
preferenceManager.findPreference<Preference>("translation")?.onPreferenceClickListener =
|
||||
Preference.OnPreferenceClickListener {
|
||||
openUrl(AppSettingsService.translationUrl)
|
||||
openUrl(AppSettingsService.TRANSLATION_URL)
|
||||
false
|
||||
}
|
||||
}
|
||||
@ -260,4 +262,4 @@ class SettingsActivity :
|
||||
setPreferencesFromResource(R.xml.pref_experimental, rootKey)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -3,7 +3,6 @@ package bou.amine.apps.readerforselfossv2.android.testing
|
||||
import androidx.test.espresso.idling.CountingIdlingResource
|
||||
|
||||
object CountingIdlingResourceSingleton {
|
||||
|
||||
private const val RESOURCE = "GLOBAL"
|
||||
|
||||
@JvmField
|
||||
@ -18,4 +17,4 @@ object CountingIdlingResourceSingleton {
|
||||
countingIdlingResource.decrement()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -2,7 +2,6 @@ package bou.amine.apps.readerforselfossv2.android.testing
|
||||
|
||||
import android.os.Build
|
||||
|
||||
|
||||
class TestingHelper {
|
||||
fun isUnitTest(): Boolean {
|
||||
var device = Build.DEVICE
|
||||
@ -16,4 +15,4 @@ class TestingHelper {
|
||||
}
|
||||
return device == "robolectric" && product == "robolectric"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -18,7 +18,6 @@ import bou.amine.apps.readerforselfossv2.model.SelfossModel
|
||||
import bou.amine.apps.readerforselfossv2.utils.toStringUriWithHttp
|
||||
import okhttp3.HttpUrl.Companion.toHttpUrlOrNull
|
||||
|
||||
|
||||
fun Context.openItemUrl(
|
||||
currentItem: Int,
|
||||
linkDecoded: String,
|
||||
@ -42,8 +41,7 @@ fun Context.openItemUrl(
|
||||
}
|
||||
}
|
||||
|
||||
fun String.isUrlValid(): Boolean =
|
||||
this.toHttpUrlOrNull() != null && Patterns.WEB_URL.matcher(this).matches()
|
||||
fun String.isUrlValid(): Boolean = this.toHttpUrlOrNull() != null && Patterns.WEB_URL.matcher(this).matches()
|
||||
|
||||
fun String.isBaseUrlInvalid(): Boolean {
|
||||
val baseUrl = this.toHttpUrlOrNull()
|
||||
@ -61,7 +59,6 @@ fun Context.openItemUrlInBrowserAsNewTask(i: SelfossModel.Item) {
|
||||
}
|
||||
|
||||
fun Context.openUrlInBrowserAsNewTask(url: String) {
|
||||
|
||||
val intent = Intent(Intent.ACTION_VIEW)
|
||||
intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK
|
||||
intent.data = Uri.parse(url)
|
||||
@ -80,7 +77,6 @@ fun Context.mayBeStartActivity(intent: Intent) {
|
||||
} catch (e: ActivityNotFoundException) {
|
||||
Toast.makeText(this, getString(R.string.no_browser), Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class LinkOnTouchListener : View.OnTouchListener {
|
||||
@ -122,4 +118,4 @@ class LinkOnTouchListener : View.OnTouchListener {
|
||||
}
|
||||
return ret
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -6,4 +6,4 @@ import org.acra.ktx.sendSilentlyWithAcra
|
||||
fun Throwable.sendSilentlyWithAcraWithName(name: String) {
|
||||
ACRA.errorReporter.putCustomData("error_source", name)
|
||||
this.sendSilentlyWithAcra()
|
||||
}
|
||||
}
|
||||
|
@ -8,22 +8,19 @@ import org.acra.config.CoreConfiguration
|
||||
import org.acra.config.ReportingAdministrator
|
||||
import org.acra.data.CrashReportData
|
||||
|
||||
|
||||
@AutoService(ReportingAdministrator::class)
|
||||
class AcraReportingAdministrator : ReportingAdministrator {
|
||||
override fun shouldStartCollecting(
|
||||
context: Context,
|
||||
config: CoreConfiguration,
|
||||
reportBuilder: ReportBuilder
|
||||
): Boolean {
|
||||
return reportBuilder.exception !is DeadSystemException && (reportBuilder.exception != null && reportBuilder.exception!!::class.simpleName != "CannotDeliverBroadcastException")
|
||||
}
|
||||
reportBuilder: ReportBuilder,
|
||||
): Boolean =
|
||||
reportBuilder.exception !is DeadSystemException &&
|
||||
(reportBuilder.exception != null && reportBuilder.exception!!::class.simpleName != "CannotDeliverBroadcastException")
|
||||
|
||||
override fun shouldSendReport(
|
||||
context: Context,
|
||||
config: CoreConfiguration,
|
||||
crashReportData: CrashReportData
|
||||
): Boolean {
|
||||
return crashReportData.get("BRAND") != "redroid"
|
||||
}
|
||||
}
|
||||
crashReportData: CrashReportData,
|
||||
): Boolean = crashReportData.get("BRAND") != "redroid"
|
||||
}
|
||||
|
@ -19,11 +19,10 @@ class AppViewModel(private val repository: Repository) : ViewModel() {
|
||||
if (isConnected && !wasConnected && repository.connectionMonitored) {
|
||||
_networkAvailableProvider.emit(true)
|
||||
wasConnected = true
|
||||
} else if (!isConnected && wasConnected && repository.connectionMonitored)
|
||||
{
|
||||
_networkAvailableProvider.emit(false)
|
||||
wasConnected = false
|
||||
}
|
||||
} else if (!isConnected && wasConnected && repository.connectionMonitored) {
|
||||
_networkAvailableProvider.emit(false)
|
||||
wasConnected = false
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -11,13 +11,17 @@ fun dialogMessage(): String {
|
||||
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)
|
||||
val item = this.findItem(id)
|
||||
assertTrue(item.isEnabled)
|
||||
}
|
||||
|
||||
fun Menu.assertVisible(@IdRes id: Int) {
|
||||
fun Menu.assertVisible(
|
||||
@IdRes id: Int,
|
||||
) {
|
||||
val item = this.findItem(id)
|
||||
assertTrue(item.isVisible)
|
||||
}
|
||||
}
|
@ -11,10 +11,8 @@ import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
import org.robolectric.Robolectric
|
||||
|
||||
|
||||
@RunWith(RobotElectriqueRunnerclass::class)
|
||||
@RunWith(RobotElectriqueRunner::class)
|
||||
class LoginActivityTest {
|
||||
|
||||
@Test
|
||||
fun login_shouldDisplay() {
|
||||
Robolectric.buildActivity(LoginActivity::class.java).use { controller ->
|
||||
@ -74,4 +72,4 @@ class LoginActivityTest {
|
||||
assertEquals(expectedIntent.component, actual.component)
|
||||
}
|
||||
}*/
|
||||
}
|
||||
}
|
||||
|
@ -3,10 +3,8 @@ package bou.amine.apps.readerforselfossv2.android.tests.robolectric
|
||||
import org.robolectric.RobolectricTestRunner
|
||||
import org.robolectric.annotation.Config
|
||||
|
||||
class RobotElectriqueRunnerclass(testClass: Class<*>?) :
|
||||
RobolectricTestRunner(testClass) {
|
||||
|
||||
override fun buildGlobalConfig(): Config {
|
||||
return Config.Builder().setSdk(25, 30, 33).build()
|
||||
}
|
||||
}
|
||||
class RobotElectriqueRunner(
|
||||
testClass: Class<*>?,
|
||||
) : RobolectricTestRunner(testClass) {
|
||||
override fun buildGlobalConfig(): Config = 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 NUMBER_ARTICLES = 100
|
||||
private const val NUMBER_UNREAD = 50
|
||||
private const val NUMBER_STARRED = 20
|
||||
|
||||
class RepositoryTest {
|
||||
private val db = mockk<ReaderForSelfossDB>(relaxed = true)
|
||||
private val appSettingsService = mockk<AppSettingsService>()
|
||||
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 fun initializeRepository(
|
||||
@ -75,19 +76,20 @@ class RepositoryTest {
|
||||
every { appSettingsService.isUpdateSourcesEnabled() } returns false
|
||||
|
||||
coEvery { api.apiInformation() } returns
|
||||
StatusAndData(
|
||||
success = true,
|
||||
data = SelfossModel.ApiInformation(
|
||||
StatusAndData(
|
||||
success = true,
|
||||
data =
|
||||
SelfossModel.ApiInformation(
|
||||
"2.19-ba1e8e3",
|
||||
"4.0.0",
|
||||
SelfossModel.ApiConfiguration(false, true)
|
||||
SelfossModel.ApiConfiguration(false, true),
|
||||
),
|
||||
)
|
||||
)
|
||||
coEvery { api.stats() } returns
|
||||
StatusAndData(
|
||||
success = true,
|
||||
data = SelfossModel.Stats(NUMBER_ARTICLES, NUMBER_UNREAD, NUMBER_STARRED),
|
||||
)
|
||||
StatusAndData(
|
||||
success = true,
|
||||
data = SelfossModel.Stats(NUMBER_ARTICLES, NUMBER_UNREAD, NUMBER_STARRED),
|
||||
)
|
||||
|
||||
every { db.itemsQueries.deleteItemsWhereSource(any()) } returns Unit
|
||||
every { db.itemsQueries.items().executeAsList() } returns generateTestDBItems()
|
||||
@ -117,7 +119,7 @@ class RepositoryTest {
|
||||
fun get_api_4_date_with_api_1_version_stored() {
|
||||
every { appSettingsService.getApiVersion() } returns 1
|
||||
coEvery { api.getItems(any(), any(), any(), any(), any(), any(), any()) } returns
|
||||
StatusAndData(success = true, data = generateTestApiItem())
|
||||
StatusAndData(success = true, data = generateTestApiItem())
|
||||
every { appSettingsService.updateApiVersion(any()) } returns Unit
|
||||
|
||||
initializeRepository()
|
||||
@ -133,14 +135,15 @@ class RepositoryTest {
|
||||
fun get_public_access() {
|
||||
every { appSettingsService.updatePublicAccess(any()) } returns Unit
|
||||
coEvery { api.apiInformation() } returns
|
||||
StatusAndData(
|
||||
success = true,
|
||||
data = SelfossModel.ApiInformation(
|
||||
StatusAndData(
|
||||
success = true,
|
||||
data =
|
||||
SelfossModel.ApiInformation(
|
||||
"2.19-ba1e8e3",
|
||||
"4.0.0",
|
||||
SelfossModel.ApiConfiguration(true, true)
|
||||
SelfossModel.ApiConfiguration(true, true),
|
||||
),
|
||||
)
|
||||
)
|
||||
every { appSettingsService.getUserName() } returns ""
|
||||
|
||||
initializeRepository()
|
||||
@ -153,14 +156,15 @@ class RepositoryTest {
|
||||
fun get_public_access_username_not_empty() {
|
||||
every { appSettingsService.updatePublicAccess(any()) } returns Unit
|
||||
coEvery { api.apiInformation() } returns
|
||||
StatusAndData(
|
||||
success = true,
|
||||
data = SelfossModel.ApiInformation(
|
||||
StatusAndData(
|
||||
success = true,
|
||||
data =
|
||||
SelfossModel.ApiInformation(
|
||||
"2.19-ba1e8e3",
|
||||
"4.0.0",
|
||||
SelfossModel.ApiConfiguration(true, true)
|
||||
SelfossModel.ApiConfiguration(true, true),
|
||||
),
|
||||
)
|
||||
)
|
||||
every { appSettingsService.getUserName() } returns "username"
|
||||
|
||||
initializeRepository()
|
||||
@ -173,14 +177,15 @@ class RepositoryTest {
|
||||
fun get_public_access_no_auth() {
|
||||
every { appSettingsService.updatePublicAccess(any()) } returns Unit
|
||||
coEvery { api.apiInformation() } returns
|
||||
StatusAndData(
|
||||
success = true,
|
||||
data = SelfossModel.ApiInformation(
|
||||
StatusAndData(
|
||||
success = true,
|
||||
data =
|
||||
SelfossModel.ApiInformation(
|
||||
"2.19-ba1e8e3",
|
||||
"4.0.0",
|
||||
SelfossModel.ApiConfiguration(true, false)
|
||||
SelfossModel.ApiConfiguration(true, false),
|
||||
),
|
||||
)
|
||||
)
|
||||
every { appSettingsService.getUserName() } returns ""
|
||||
|
||||
initializeRepository()
|
||||
@ -193,14 +198,15 @@ class RepositoryTest {
|
||||
fun get_public_access_disabled() {
|
||||
every { appSettingsService.updatePublicAccess(any()) } returns Unit
|
||||
coEvery { api.apiInformation() } returns
|
||||
StatusAndData(
|
||||
success = true,
|
||||
data = SelfossModel.ApiInformation(
|
||||
StatusAndData(
|
||||
success = true,
|
||||
data =
|
||||
SelfossModel.ApiInformation(
|
||||
"2.19-ba1e8e3",
|
||||
"4.0.0",
|
||||
SelfossModel.ApiConfiguration(false, true)
|
||||
SelfossModel.ApiConfiguration(false, true),
|
||||
),
|
||||
)
|
||||
)
|
||||
every { appSettingsService.getUserName() } returns ""
|
||||
|
||||
initializeRepository()
|
||||
@ -216,10 +222,10 @@ class RepositoryTest {
|
||||
val itemParameters = FakeItemParameters()
|
||||
itemParameters.datetime = "2021-04-23 11:45:32"
|
||||
coEvery { api.getItems(any(), any(), any(), any(), any(), any(), any()) } returns
|
||||
StatusAndData(
|
||||
success = true,
|
||||
data = generateTestApiItem(itemParameters),
|
||||
)
|
||||
StatusAndData(
|
||||
success = true,
|
||||
data = generateTestApiItem(itemParameters),
|
||||
)
|
||||
|
||||
initializeRepository()
|
||||
runBlocking {
|
||||
@ -232,7 +238,7 @@ class RepositoryTest {
|
||||
@Test
|
||||
fun get_newer_items() {
|
||||
coEvery { api.getItems(any(), any(), any(), any(), any(), any(), any()) } returns
|
||||
StatusAndData(success = true, data = generateTestApiItem())
|
||||
StatusAndData(success = true, data = generateTestApiItem())
|
||||
|
||||
initializeRepository()
|
||||
runBlocking {
|
||||
@ -247,7 +253,7 @@ class RepositoryTest {
|
||||
@Test
|
||||
fun get_all_newer_items() {
|
||||
coEvery { api.getItems(any(), any(), any(), any(), any(), any(), any()) } returns
|
||||
StatusAndData(success = true, data = generateTestApiItem())
|
||||
StatusAndData(success = true, data = generateTestApiItem())
|
||||
|
||||
initializeRepository()
|
||||
repository.displayedItems = ItemType.ALL
|
||||
@ -263,7 +269,7 @@ class RepositoryTest {
|
||||
@Test
|
||||
fun get_newer_starred_items() {
|
||||
coEvery { api.getItems(any(), any(), any(), any(), any(), any(), any()) } returns
|
||||
StatusAndData(success = true, data = generateTestApiItem())
|
||||
StatusAndData(success = true, data = generateTestApiItem())
|
||||
|
||||
initializeRepository()
|
||||
repository.displayedItems = ItemType.STARRED
|
||||
@ -302,8 +308,8 @@ class RepositoryTest {
|
||||
coEvery { db.itemsQueries.items().executeAsList() } returns generateTestDBItems(
|
||||
itemParameter1,
|
||||
) +
|
||||
generateTestDBItems(itemParameter2) +
|
||||
generateTestDBItems(itemParameter3)
|
||||
generateTestDBItems(itemParameter2) +
|
||||
generateTestDBItems(itemParameter3)
|
||||
|
||||
every { appSettingsService.isItemCachingEnabled() } returns true
|
||||
|
||||
@ -330,8 +336,8 @@ class RepositoryTest {
|
||||
coEvery { db.itemsQueries.items().executeAsList() } returns generateTestDBItems(
|
||||
itemParameter1,
|
||||
) +
|
||||
generateTestDBItems(itemParameter2) +
|
||||
generateTestDBItems(itemParameter3)
|
||||
generateTestDBItems(itemParameter2) +
|
||||
generateTestDBItems(itemParameter3)
|
||||
|
||||
every { appSettingsService.isItemCachingEnabled() } returns true
|
||||
|
||||
@ -360,7 +366,7 @@ class RepositoryTest {
|
||||
@Test
|
||||
fun get_older_items() {
|
||||
coEvery { api.getItems(any(), any(), any(), any(), any(), any(), any()) } returns
|
||||
StatusAndData(success = true, data = generateTestApiItem())
|
||||
StatusAndData(success = true, data = generateTestApiItem())
|
||||
|
||||
initializeRepository()
|
||||
repository.items = ArrayList(generateTestApiItem())
|
||||
@ -376,7 +382,7 @@ class RepositoryTest {
|
||||
@Test
|
||||
fun get_all_older_items() {
|
||||
coEvery { api.getItems(any(), any(), any(), any(), any(), any(), any()) } returns
|
||||
StatusAndData(success = true, data = generateTestApiItem())
|
||||
StatusAndData(success = true, data = generateTestApiItem())
|
||||
|
||||
initializeRepository()
|
||||
repository.items = ArrayList(generateTestApiItem())
|
||||
@ -393,7 +399,7 @@ class RepositoryTest {
|
||||
@Test
|
||||
fun get_older_starred_items() {
|
||||
coEvery { api.getItems(any(), any(), any(), any(), any(), any(), any()) } returns
|
||||
StatusAndData(success = true, data = generateTestApiItem())
|
||||
StatusAndData(success = true, data = generateTestApiItem())
|
||||
|
||||
initializeRepository()
|
||||
repository.displayedItems = ItemType.STARRED
|
||||
@ -833,7 +839,7 @@ class RepositoryTest {
|
||||
@Test
|
||||
fun create_source() {
|
||||
coEvery { api.createSourceForVersion(any(), any(), any(), any()) } returns
|
||||
SuccessResponse(true)
|
||||
SuccessResponse(true)
|
||||
|
||||
initializeRepository()
|
||||
var response: Boolean
|
||||
@ -861,7 +867,7 @@ class RepositoryTest {
|
||||
@Test
|
||||
fun create_source_but_response_fails() {
|
||||
coEvery { api.createSourceForVersion(any(), any(), any(), any()) } returns
|
||||
SuccessResponse(false)
|
||||
SuccessResponse(false)
|
||||
|
||||
initializeRepository()
|
||||
var response: Boolean
|
||||
@ -889,7 +895,7 @@ class RepositoryTest {
|
||||
@Test
|
||||
fun create_source_without_connection() {
|
||||
coEvery { api.createSourceForVersion(any(), any(), any(), any()) } returns
|
||||
SuccessResponse(true)
|
||||
SuccessResponse(true)
|
||||
|
||||
initializeRepository(MutableStateFlow(false))
|
||||
var response: Boolean
|
||||
@ -962,10 +968,10 @@ class RepositoryTest {
|
||||
@Test
|
||||
fun update_remote() {
|
||||
coEvery { api.update() } returns
|
||||
StatusAndData(
|
||||
success = true,
|
||||
data = "finished",
|
||||
)
|
||||
StatusAndData(
|
||||
success = true,
|
||||
data = "finished",
|
||||
)
|
||||
|
||||
initializeRepository()
|
||||
var response: Boolean
|
||||
@ -980,10 +986,10 @@ class RepositoryTest {
|
||||
@Test
|
||||
fun update_remote_but_response_fails() {
|
||||
coEvery { api.update() } returns
|
||||
StatusAndData(
|
||||
success = false,
|
||||
data = "unallowed access",
|
||||
)
|
||||
StatusAndData(
|
||||
success = false,
|
||||
data = "unallowed access",
|
||||
)
|
||||
|
||||
initializeRepository()
|
||||
var response: Boolean
|
||||
@ -998,10 +1004,10 @@ class RepositoryTest {
|
||||
@Test
|
||||
fun update_remote_with_unallowed_access() {
|
||||
coEvery { api.update() } returns
|
||||
StatusAndData(
|
||||
success = true,
|
||||
data = "unallowed access",
|
||||
)
|
||||
StatusAndData(
|
||||
success = true,
|
||||
data = "unallowed access",
|
||||
)
|
||||
|
||||
initializeRepository()
|
||||
var response: Boolean
|
||||
@ -1016,10 +1022,10 @@ class RepositoryTest {
|
||||
@Test
|
||||
fun update_remote_without_connection() {
|
||||
coEvery { api.update() } returns
|
||||
StatusAndData(
|
||||
success = true,
|
||||
data = "undocumented...",
|
||||
)
|
||||
StatusAndData(
|
||||
success = true,
|
||||
data = "undocumented...",
|
||||
)
|
||||
|
||||
initializeRepository(MutableStateFlow(false))
|
||||
var response: Boolean
|
||||
@ -1109,11 +1115,11 @@ class RepositoryTest {
|
||||
any(),
|
||||
)
|
||||
} returnsMany
|
||||
listOf(
|
||||
StatusAndData(success = true, data = generateTestApiItem(itemParameter1)),
|
||||
StatusAndData(success = true, data = generateTestApiItem(itemParameter2)),
|
||||
StatusAndData(success = true, data = generateTestApiItem(itemParameter1)),
|
||||
)
|
||||
listOf(
|
||||
StatusAndData(success = true, data = generateTestApiItem(itemParameter1)),
|
||||
StatusAndData(success = true, data = generateTestApiItem(itemParameter2)),
|
||||
StatusAndData(success = true, data = generateTestApiItem(itemParameter1)),
|
||||
)
|
||||
|
||||
initializeRepository()
|
||||
prepareSearch()
|
||||
@ -1127,7 +1133,7 @@ class RepositoryTest {
|
||||
@Test
|
||||
fun cache_items_but_response_fails() {
|
||||
coEvery { api.getItems(any(), any(), any(), any(), any(), any(), any()) } returns
|
||||
StatusAndData(success = false, data = generateTestApiItem())
|
||||
StatusAndData(success = false, data = generateTestApiItem())
|
||||
|
||||
initializeRepository()
|
||||
prepareSearch()
|
||||
@ -1141,7 +1147,7 @@ class RepositoryTest {
|
||||
@Test
|
||||
fun cache_items_without_connection() {
|
||||
coEvery { api.getItems(any(), any(), any(), any(), any(), any(), any()) } returns
|
||||
StatusAndData(success = false, data = generateTestApiItem())
|
||||
StatusAndData(success = false, data = generateTestApiItem())
|
||||
|
||||
initializeRepository(MutableStateFlow(false))
|
||||
prepareSearch()
|
||||
@ -1168,4 +1174,4 @@ class RepositoryTest {
|
||||
)
|
||||
repository.searchFilter = "search"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -3,8 +3,8 @@ package bou.amine.apps.readerforselfossv2.tests.repository
|
||||
import bou.amine.apps.readerforselfossv2.dao.ITEM
|
||||
import bou.amine.apps.readerforselfossv2.model.SelfossModel
|
||||
|
||||
fun generateTestDBItems(item: FakeItemParameters = FakeItemParameters()): List<ITEM> {
|
||||
return listOf(
|
||||
fun generateTestDBItems(item: FakeItemParameters = FakeItemParameters()): List<ITEM> =
|
||||
listOf(
|
||||
ITEM(
|
||||
id = item.id,
|
||||
datetime = item.datetime,
|
||||
@ -20,10 +20,9 @@ fun generateTestDBItems(item: FakeItemParameters = FakeItemParameters()): List<I
|
||||
author = item.author,
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
fun generateTestApiItem(item: FakeItemParameters = FakeItemParameters()): List<SelfossModel.Item> {
|
||||
return listOf(
|
||||
fun generateTestApiItem(item: FakeItemParameters = FakeItemParameters()): List<SelfossModel.Item> =
|
||||
listOf(
|
||||
SelfossModel.Item(
|
||||
id = item.id.toInt(),
|
||||
datetime = item.datetime,
|
||||
@ -39,14 +38,13 @@ fun generateTestApiItem(item: FakeItemParameters = FakeItemParameters()): List<S
|
||||
author = item.author,
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
class FakeItemParameters {
|
||||
var id = "20"
|
||||
var datetime = "2022-09-09T03:32:01-04:00"
|
||||
val title = "Etica della ricerca sotto i riflettori."
|
||||
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 starred = true
|
||||
val thumbnail = null
|
||||
@ -56,4 +54,4 @@ class FakeItemParameters {
|
||||
var sourcetitle = "La Chimica e la Società"
|
||||
var tags = "Chimica, Testing"
|
||||
var author = "Someone important"
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user