From 2c7c0c5bda919c5a0a770a01a5bb31b5ae14e6fa Mon Sep 17 00:00:00 2001 From: CLIDragon <84266961+CLIDragon@users.noreply.github.com> Date: Mon, 15 Jul 2024 17:11:59 +1000 Subject: [PATCH] Replace CRLF with LF --- .../java/com/ichi2/anki/ReviewerTest.kt | 416 +++++++++--------- .../com/ichi2/anki/pages/ImageOcclusion.kt | 198 ++++----- .../AccessibilitySettingsFragment.kt | 62 +-- .../com/ichi2/anki/ConstantUniquenessTest.kt | 110 ++--- .../anki/previewer/PreviewerViewModelTest.kt | 60 +-- jitpack.yml | 30 +- 6 files changed, 438 insertions(+), 438 deletions(-) diff --git a/AnkiDroid/src/androidTest/java/com/ichi2/anki/ReviewerTest.kt b/AnkiDroid/src/androidTest/java/com/ichi2/anki/ReviewerTest.kt index 185a64bddd..00734356c4 100755 --- a/AnkiDroid/src/androidTest/java/com/ichi2/anki/ReviewerTest.kt +++ b/AnkiDroid/src/androidTest/java/com/ichi2/anki/ReviewerTest.kt @@ -1,208 +1,208 @@ -/* - * Copyright (c) 2023 - * - * This program is free software; you can redistribute it and/or modify it under - * the terms of the GNU General Public License as published by the Free Software - * Foundation; either version 3 of the License, or (at your option) any later - * version. - * - * This program is distributed in the hope that it will be useful, but WITHOUT ANY - * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A - * PARTICULAR PURPOSE. See the GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along with - * this program. If not, see . - */ -package com.ichi2.anki - -import androidx.recyclerview.widget.RecyclerView -import androidx.test.espresso.Espresso.onView -import androidx.test.espresso.NoMatchingViewException -import androidx.test.espresso.action.ViewActions.click -import androidx.test.espresso.assertion.ViewAssertions.matches -import androidx.test.espresso.contrib.RecyclerViewActions -import androidx.test.espresso.matcher.ViewMatchers.hasDescendant -import androidx.test.espresso.matcher.ViewMatchers.isDisplayed -import androidx.test.espresso.matcher.ViewMatchers.withId -import androidx.test.espresso.matcher.ViewMatchers.withResourceName -import androidx.test.espresso.matcher.ViewMatchers.withText -import androidx.test.ext.junit.rules.ActivityScenarioRule -import androidx.test.ext.junit.runners.AndroidJUnit4 -import com.ichi2.anki.tests.InstrumentedTest -import com.ichi2.anki.tests.libanki.RetryRule -import com.ichi2.anki.testutil.GrantStoragePermission.storagePermission -import com.ichi2.anki.testutil.ThreadUtils -import com.ichi2.anki.testutil.grantPermissions -import com.ichi2.anki.testutil.notificationPermission -import com.ichi2.libanki.Collection -import org.hamcrest.MatcherAssert.assertThat -import org.hamcrest.Matchers.equalTo -import org.junit.Rule -import org.junit.Test -import org.junit.runner.RunWith -import java.lang.AssertionError - -@RunWith(AndroidJUnit4::class) -class ReviewerTest : InstrumentedTest() { - - // Launch IntroductionActivity instead of DeckPicker activity because in CI - // builds, it seems to create IntroductionActivity after the DeckPicker, - // causing the DeckPicker activity to be destroyed. As a consequence, this - // will throw RootViewWithoutFocusException when Espresso tries to interact - // with an already destroyed activity. By launching IntroductionActivity, we - // ensure that IntroductionActivity is launched first and navigate to the - // DeckPicker -> Reviewer activities - @get:Rule - val activityScenarioRule = ActivityScenarioRule(IntroductionActivity::class.java) - - @get:Rule - val runtimePermissionRule = grantPermissions(storagePermission, notificationPermission) - - @get:Rule - val retry = RetryRule(10) - - @Test - fun testCustomSchedulerWithCustomData() { - col.cardStateCustomizer = - """ - states.good.normal.review.easeFactor = 3.0; - states.good.normal.review.scheduledDays = 123; - customData.good.c += 1; - """ - val note = addNoteUsingBasicModel("foo", "bar") - val card = note.firstCard(col) - val deck = col.decks.get(note.notetype.did)!! - card.moveToReviewQueue() - col.backend.updateCards( - listOf( - card.toBackendCard().toBuilder().setCustomData("""{"c":1}""").build() - ), - true - ) - - closeGetStartedScreenIfExists() - closeBackupCollectionDialogIfExists() - reviewDeckWithName(deck.name) - - var cardFromDb = col.getCard(card.id).toBackendCard() - assertThat(cardFromDb.easeFactor, equalTo(card.factor)) - assertThat(cardFromDb.interval, equalTo(card.ivl)) - assertThat(cardFromDb.customData, equalTo("""{"c":1}""")) - - clickShowAnswerAndAnswerGood() - - fun runAssertion() { - cardFromDb = col.getCard(card.id).toBackendCard() - assertThat(cardFromDb.easeFactor, equalTo(3000)) - assertThat(cardFromDb.interval, equalTo(123)) - assertThat(cardFromDb.customData, equalTo("""{"c":2}""")) - } - - try { - runAssertion() - } catch (e: Exception) { - // Give separate threads a greater chance of doing the custom scheduling - // if the card scheduling values aren't updated immediately - ThreadUtils.sleep(2000) - runAssertion() - } - } - - @Test - fun testCustomSchedulerWithRuntimeError() { - // Issue 15035 - runtime errors weren't handled - col.cardStateCustomizer = "states.this_is_not_defined.normal.review = 12;" - addNoteUsingBasicModel() - - closeGetStartedScreenIfExists() - closeBackupCollectionDialogIfExists() - reviewDeckWithName("Default") - - clickShowAnswer() - - ensureAnswerButtonsAreDisplayed() - } - - private fun clickOnDeckWithName(deckName: String) { - onView(withId(R.id.files)).checkWithTimeout(matches(hasDescendant(withText(deckName)))) - onView(withId(R.id.files)).perform( - RecyclerViewActions.actionOnItem( - hasDescendant(withText(deckName)), - click() - ) - ) - } - - private fun clickOnStudyButtonIfExists() { - onView(withId(R.id.studyoptions_start)) - .withFailureHandler { _, _ -> } - .perform(click()) - } - - private fun reviewDeckWithName(deckName: String) { - clickOnDeckWithName(deckName) - // Adding cards directly to the database while in the Deck Picker screen - // will not update the page with correct card counts. Hence, clicking - // on the deck will bring us to the study options page where we need to - // click on the Study button. If we have added cards to the database - // before the Deck Picker screen has fully loaded, then we skip clicking - // the Study button - clickOnStudyButtonIfExists() - } - - private fun clickShowAnswerAndAnswerGood() { - clickShowAnswer() - ensureAnswerButtonsAreDisplayed() - try { - // ...on the command line it has resource name "good_button"... - onView(withResourceName("good_button")).perform(click()) - } catch (e: NoMatchingViewException) { - // ...but in Android Studio it has resource name "flashcard_layout_ease3" !? - onView(withResourceName("flashcard_layout_ease3")).perform(click()) - } - } - - private fun clickShowAnswer() { - try { - // ... on the command line, it has resource name "show_answer"... - onView(withResourceName("show_answer")).perform(click()) - } catch (e: NoMatchingViewException) { - // ... but in Android Studio it has resource name "flashcard_layout_flip" !? - onView(withResourceName("flashcard_layout_flip")).perform(click()) - } - } - - private fun ensureAnswerButtonsAreDisplayed() { - // We need to wait for the card to fully load to allow enough time for - // the messages to be passed in and out of the WebView when evaluating - // the custom JS scheduler code. The ease buttons are hidden until the - // custom scheduler has finished running - try { - // ...on the command line it has resource name "good_button"... - onView(withResourceName("good_button")).checkWithTimeout( - matches(isDisplayed()), - 100 - ) - } catch (e: AssertionError) { - // ...but in Android Studio it has resource name "flashcard_layout_ease3" !? - onView(withResourceName("flashcard_layout_ease3")).checkWithTimeout( - matches(isDisplayed()), - 100 - ) - } - } -} - -private var Collection.cardStateCustomizer: String? - get() = config.get("cardStateCustomizer") - set(value) { config.set("cardStateCustomizer", value) } - -fun closeGetStartedScreenIfExists() { - onView(withId(R.id.get_started)).withFailureHandler { _, _ -> }.perform(click()) -} - -fun closeBackupCollectionDialogIfExists() { - onView(withText(R.string.button_backup_later)) - .withFailureHandler { _, _ -> } - .perform(click()) -} +/* + * Copyright (c) 2023 + * + * This program is free software; you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free Software + * Foundation; either version 3 of the License, or (at your option) any later + * version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A + * PARTICULAR PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ +package com.ichi2.anki + +import androidx.recyclerview.widget.RecyclerView +import androidx.test.espresso.Espresso.onView +import androidx.test.espresso.NoMatchingViewException +import androidx.test.espresso.action.ViewActions.click +import androidx.test.espresso.assertion.ViewAssertions.matches +import androidx.test.espresso.contrib.RecyclerViewActions +import androidx.test.espresso.matcher.ViewMatchers.hasDescendant +import androidx.test.espresso.matcher.ViewMatchers.isDisplayed +import androidx.test.espresso.matcher.ViewMatchers.withId +import androidx.test.espresso.matcher.ViewMatchers.withResourceName +import androidx.test.espresso.matcher.ViewMatchers.withText +import androidx.test.ext.junit.rules.ActivityScenarioRule +import androidx.test.ext.junit.runners.AndroidJUnit4 +import com.ichi2.anki.tests.InstrumentedTest +import com.ichi2.anki.tests.libanki.RetryRule +import com.ichi2.anki.testutil.GrantStoragePermission.storagePermission +import com.ichi2.anki.testutil.ThreadUtils +import com.ichi2.anki.testutil.grantPermissions +import com.ichi2.anki.testutil.notificationPermission +import com.ichi2.libanki.Collection +import org.hamcrest.MatcherAssert.assertThat +import org.hamcrest.Matchers.equalTo +import org.junit.Rule +import org.junit.Test +import org.junit.runner.RunWith +import java.lang.AssertionError + +@RunWith(AndroidJUnit4::class) +class ReviewerTest : InstrumentedTest() { + + // Launch IntroductionActivity instead of DeckPicker activity because in CI + // builds, it seems to create IntroductionActivity after the DeckPicker, + // causing the DeckPicker activity to be destroyed. As a consequence, this + // will throw RootViewWithoutFocusException when Espresso tries to interact + // with an already destroyed activity. By launching IntroductionActivity, we + // ensure that IntroductionActivity is launched first and navigate to the + // DeckPicker -> Reviewer activities + @get:Rule + val activityScenarioRule = ActivityScenarioRule(IntroductionActivity::class.java) + + @get:Rule + val runtimePermissionRule = grantPermissions(storagePermission, notificationPermission) + + @get:Rule + val retry = RetryRule(10) + + @Test + fun testCustomSchedulerWithCustomData() { + col.cardStateCustomizer = + """ + states.good.normal.review.easeFactor = 3.0; + states.good.normal.review.scheduledDays = 123; + customData.good.c += 1; + """ + val note = addNoteUsingBasicModel("foo", "bar") + val card = note.firstCard(col) + val deck = col.decks.get(note.notetype.did)!! + card.moveToReviewQueue() + col.backend.updateCards( + listOf( + card.toBackendCard().toBuilder().setCustomData("""{"c":1}""").build() + ), + true + ) + + closeGetStartedScreenIfExists() + closeBackupCollectionDialogIfExists() + reviewDeckWithName(deck.name) + + var cardFromDb = col.getCard(card.id).toBackendCard() + assertThat(cardFromDb.easeFactor, equalTo(card.factor)) + assertThat(cardFromDb.interval, equalTo(card.ivl)) + assertThat(cardFromDb.customData, equalTo("""{"c":1}""")) + + clickShowAnswerAndAnswerGood() + + fun runAssertion() { + cardFromDb = col.getCard(card.id).toBackendCard() + assertThat(cardFromDb.easeFactor, equalTo(3000)) + assertThat(cardFromDb.interval, equalTo(123)) + assertThat(cardFromDb.customData, equalTo("""{"c":2}""")) + } + + try { + runAssertion() + } catch (e: Exception) { + // Give separate threads a greater chance of doing the custom scheduling + // if the card scheduling values aren't updated immediately + ThreadUtils.sleep(2000) + runAssertion() + } + } + + @Test + fun testCustomSchedulerWithRuntimeError() { + // Issue 15035 - runtime errors weren't handled + col.cardStateCustomizer = "states.this_is_not_defined.normal.review = 12;" + addNoteUsingBasicModel() + + closeGetStartedScreenIfExists() + closeBackupCollectionDialogIfExists() + reviewDeckWithName("Default") + + clickShowAnswer() + + ensureAnswerButtonsAreDisplayed() + } + + private fun clickOnDeckWithName(deckName: String) { + onView(withId(R.id.files)).checkWithTimeout(matches(hasDescendant(withText(deckName)))) + onView(withId(R.id.files)).perform( + RecyclerViewActions.actionOnItem( + hasDescendant(withText(deckName)), + click() + ) + ) + } + + private fun clickOnStudyButtonIfExists() { + onView(withId(R.id.studyoptions_start)) + .withFailureHandler { _, _ -> } + .perform(click()) + } + + private fun reviewDeckWithName(deckName: String) { + clickOnDeckWithName(deckName) + // Adding cards directly to the database while in the Deck Picker screen + // will not update the page with correct card counts. Hence, clicking + // on the deck will bring us to the study options page where we need to + // click on the Study button. If we have added cards to the database + // before the Deck Picker screen has fully loaded, then we skip clicking + // the Study button + clickOnStudyButtonIfExists() + } + + private fun clickShowAnswerAndAnswerGood() { + clickShowAnswer() + ensureAnswerButtonsAreDisplayed() + try { + // ...on the command line it has resource name "good_button"... + onView(withResourceName("good_button")).perform(click()) + } catch (e: NoMatchingViewException) { + // ...but in Android Studio it has resource name "flashcard_layout_ease3" !? + onView(withResourceName("flashcard_layout_ease3")).perform(click()) + } + } + + private fun clickShowAnswer() { + try { + // ... on the command line, it has resource name "show_answer"... + onView(withResourceName("show_answer")).perform(click()) + } catch (e: NoMatchingViewException) { + // ... but in Android Studio it has resource name "flashcard_layout_flip" !? + onView(withResourceName("flashcard_layout_flip")).perform(click()) + } + } + + private fun ensureAnswerButtonsAreDisplayed() { + // We need to wait for the card to fully load to allow enough time for + // the messages to be passed in and out of the WebView when evaluating + // the custom JS scheduler code. The ease buttons are hidden until the + // custom scheduler has finished running + try { + // ...on the command line it has resource name "good_button"... + onView(withResourceName("good_button")).checkWithTimeout( + matches(isDisplayed()), + 100 + ) + } catch (e: AssertionError) { + // ...but in Android Studio it has resource name "flashcard_layout_ease3" !? + onView(withResourceName("flashcard_layout_ease3")).checkWithTimeout( + matches(isDisplayed()), + 100 + ) + } + } +} + +private var Collection.cardStateCustomizer: String? + get() = config.get("cardStateCustomizer") + set(value) { config.set("cardStateCustomizer", value) } + +fun closeGetStartedScreenIfExists() { + onView(withId(R.id.get_started)).withFailureHandler { _, _ -> }.perform(click()) +} + +fun closeBackupCollectionDialogIfExists() { + onView(withText(R.string.button_backup_later)) + .withFailureHandler { _, _ -> } + .perform(click()) +} diff --git a/AnkiDroid/src/main/java/com/ichi2/anki/pages/ImageOcclusion.kt b/AnkiDroid/src/main/java/com/ichi2/anki/pages/ImageOcclusion.kt index 5a92bfc4e9..a9e7ab27dd 100644 --- a/AnkiDroid/src/main/java/com/ichi2/anki/pages/ImageOcclusion.kt +++ b/AnkiDroid/src/main/java/com/ichi2/anki/pages/ImageOcclusion.kt @@ -1,99 +1,99 @@ -/* - * Copyright (c) 2023 Abdo - * - * This program is free software; you can redistribute it and/or modify it under - * the terms of the GNU General Public License as published by the Free Software - * Foundation; either version 3 of the License, or (at your option) any later - * version. - * - * This program is distributed in the hope that it will be useful, but WITHOUT ANY - * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A - * PARTICULAR PURPOSE. See the GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along with - * this program. If not, see . - */ -package com.ichi2.anki.pages - -import android.content.Context -import android.content.Intent -import android.os.Bundle -import android.view.View -import android.webkit.WebView -import androidx.activity.addCallback -import androidx.core.os.bundleOf -import com.google.android.material.appbar.MaterialToolbar -import com.ichi2.anki.R -import com.ichi2.anki.SingleFragmentActivity -import com.ichi2.anki.dialogs.DiscardChangesDialog -import org.json.JSONObject -import timber.log.Timber - -class ImageOcclusion : PageFragment(R.layout.image_occlusion) { - - override fun onViewCreated(view: View, savedInstanceState: Bundle?) { - super.onViewCreated(view, savedInstanceState) - - with(requireActivity()) { - onBackPressedDispatcher.addCallback(this) { - DiscardChangesDialog.showDialog(this@with) { - finish() - } - } - } - - view.findViewById(R.id.toolbar).setOnMenuItemClickListener { - if (it.itemId == R.id.action_save) { - Timber.i("save item selected") - webView.evaluateJavascript("anki.imageOcclusion.save()", null) - } - return@setOnMenuItemClickListener true - } - } - - override fun onCreateWebViewClient(savedInstanceState: Bundle?): PageWebViewClient { - return object : PageWebViewClient() { - override fun onPageFinished(view: WebView?, url: String?) { - super.onPageFinished(view, url) - - val kind = requireArguments().getString(ARG_KEY_KIND) - val noteOrNotetypeId = requireArguments().getLong(ARG_KEY_ID) - val imagePath = requireArguments().getString(ARG_KEY_PATH) - - val options = JSONObject() - options.put("kind", kind) - if (kind == "add") { - options.put("imagePath", imagePath) - options.put("notetypeId", noteOrNotetypeId) - } else { - options.put("noteId", noteOrNotetypeId) - } - - view?.evaluateJavascript("globalThis.anki.imageOcclusion.mode = $options") { - super.onPageFinished(view, url) - } - } - } - } - - companion object { - private const val ARG_KEY_KIND = "kind" - private const val ARG_KEY_ID = "id" - private const val ARG_KEY_PATH = "imagePath" - - fun getIntent(context: Context, kind: String, noteOrNotetypeId: Long, imagePath: String?): Intent { - val suffix = if (kind == "edit") { - "/$noteOrNotetypeId" - } else { - imagePath - } - val arguments = bundleOf( - ARG_KEY_KIND to kind, - ARG_KEY_ID to noteOrNotetypeId, - ARG_KEY_PATH to imagePath, - PATH_ARG_KEY to "image-occlusion$suffix" - ) - return SingleFragmentActivity.getIntent(context, ImageOcclusion::class, arguments) - } - } -} +/* + * Copyright (c) 2023 Abdo + * + * This program is free software; you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free Software + * Foundation; either version 3 of the License, or (at your option) any later + * version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A + * PARTICULAR PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ +package com.ichi2.anki.pages + +import android.content.Context +import android.content.Intent +import android.os.Bundle +import android.view.View +import android.webkit.WebView +import androidx.activity.addCallback +import androidx.core.os.bundleOf +import com.google.android.material.appbar.MaterialToolbar +import com.ichi2.anki.R +import com.ichi2.anki.SingleFragmentActivity +import com.ichi2.anki.dialogs.DiscardChangesDialog +import org.json.JSONObject +import timber.log.Timber + +class ImageOcclusion : PageFragment(R.layout.image_occlusion) { + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + + with(requireActivity()) { + onBackPressedDispatcher.addCallback(this) { + DiscardChangesDialog.showDialog(this@with) { + finish() + } + } + } + + view.findViewById(R.id.toolbar).setOnMenuItemClickListener { + if (it.itemId == R.id.action_save) { + Timber.i("save item selected") + webView.evaluateJavascript("anki.imageOcclusion.save()", null) + } + return@setOnMenuItemClickListener true + } + } + + override fun onCreateWebViewClient(savedInstanceState: Bundle?): PageWebViewClient { + return object : PageWebViewClient() { + override fun onPageFinished(view: WebView?, url: String?) { + super.onPageFinished(view, url) + + val kind = requireArguments().getString(ARG_KEY_KIND) + val noteOrNotetypeId = requireArguments().getLong(ARG_KEY_ID) + val imagePath = requireArguments().getString(ARG_KEY_PATH) + + val options = JSONObject() + options.put("kind", kind) + if (kind == "add") { + options.put("imagePath", imagePath) + options.put("notetypeId", noteOrNotetypeId) + } else { + options.put("noteId", noteOrNotetypeId) + } + + view?.evaluateJavascript("globalThis.anki.imageOcclusion.mode = $options") { + super.onPageFinished(view, url) + } + } + } + } + + companion object { + private const val ARG_KEY_KIND = "kind" + private const val ARG_KEY_ID = "id" + private const val ARG_KEY_PATH = "imagePath" + + fun getIntent(context: Context, kind: String, noteOrNotetypeId: Long, imagePath: String?): Intent { + val suffix = if (kind == "edit") { + "/$noteOrNotetypeId" + } else { + imagePath + } + val arguments = bundleOf( + ARG_KEY_KIND to kind, + ARG_KEY_ID to noteOrNotetypeId, + ARG_KEY_PATH to imagePath, + PATH_ARG_KEY to "image-occlusion$suffix" + ) + return SingleFragmentActivity.getIntent(context, ImageOcclusion::class, arguments) + } + } +} diff --git a/AnkiDroid/src/main/java/com/ichi2/anki/preferences/AccessibilitySettingsFragment.kt b/AnkiDroid/src/main/java/com/ichi2/anki/preferences/AccessibilitySettingsFragment.kt index 2b12c1f85b..3be3ed3f59 100644 --- a/AnkiDroid/src/main/java/com/ichi2/anki/preferences/AccessibilitySettingsFragment.kt +++ b/AnkiDroid/src/main/java/com/ichi2/anki/preferences/AccessibilitySettingsFragment.kt @@ -1,31 +1,31 @@ -/* - * Copyright (c) 2022 Brayan Oliveira - * - * This program is free software; you can redistribute it and/or modify it under - * the terms of the GNU General Public License as published by the Free Software - * Foundation; either version 3 of the License, or (at your option) any later - * version. - * - * This program is distributed in the hope that it will be useful, but WITHOUT ANY - * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A - * PARTICULAR PURPOSE. See the GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along with - * this program. If not, see . - */ -package com.ichi2.anki.preferences - -import com.ichi2.anki.R - -/** - * Fragment with preferences related to notifications - */ -class AccessibilitySettingsFragment : SettingsFragment() { - override val preferenceResource: Int - get() = R.xml.preferences_accessibility - override val analyticsScreenNameConstant: String - get() = "prefs.accessibility" - - override fun initSubscreen() { - } -} +/* + * Copyright (c) 2022 Brayan Oliveira + * + * This program is free software; you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free Software + * Foundation; either version 3 of the License, or (at your option) any later + * version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A + * PARTICULAR PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ +package com.ichi2.anki.preferences + +import com.ichi2.anki.R + +/** + * Fragment with preferences related to notifications + */ +class AccessibilitySettingsFragment : SettingsFragment() { + override val preferenceResource: Int + get() = R.xml.preferences_accessibility + override val analyticsScreenNameConstant: String + get() = "prefs.accessibility" + + override fun initSubscreen() { + } +} diff --git a/AnkiDroid/src/test/java/com/ichi2/anki/ConstantUniquenessTest.kt b/AnkiDroid/src/test/java/com/ichi2/anki/ConstantUniquenessTest.kt index b37cded87d..357ae23cfa 100644 --- a/AnkiDroid/src/test/java/com/ichi2/anki/ConstantUniquenessTest.kt +++ b/AnkiDroid/src/test/java/com/ichi2/anki/ConstantUniquenessTest.kt @@ -1,55 +1,55 @@ -/* - * Copyright (c) 2024 WPum <27683756+WPum@users.noreply.github.com> - * - * This program is free software; you can redistribute it and/or modify it under - * the terms of the GNU General Public License as published by the Free Software - * Foundation; either version 3 of the License, or (at your option) any later - * version. - * - * This program is distributed in the hope that it will be useful, but WITHOUT ANY - * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A - * PARTICULAR PURPOSE. See the GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along with - * this program. If not, see . - */ - -package com.ichi2.anki - -import com.ichi2.anki.notifications.NotificationId -import com.ichi2.anki.worker.UniqueWorkNames -import org.junit.Test -import kotlin.reflect.KClass -import kotlin.reflect.KVisibility -import kotlin.reflect.full.declaredMemberProperties -import kotlin.test.assertFalse -import kotlin.test.assertNotNull - -class ConstantUniquenessTest { - - @Test - fun testConstantUniqueness() { - assertConstantUniqueness(NotificationId::class) - assertConstantUniqueness(UniqueWorkNames::class) - } - - companion object { - - /** - * To check whether all PUBLIC CONST values in an object are unique. - */ - fun assertConstantUniqueness(clazz: KClass) { - assertNotNull(clazz.objectInstance, "Can only check objects for uniqueness") - val valueSet = HashSet() - for (prop in clazz.declaredMemberProperties) { - if (!prop.isConst || prop.visibility != KVisibility.PUBLIC) { - continue - } - // use .call() since clazz represents an object - val value = prop.call() - assertFalse(valueSet.contains(value), "Duplicate value ('$value') for constant in ${clazz.qualifiedName}") - valueSet.add(value) - } - } - } -} +/* + * Copyright (c) 2024 WPum <27683756+WPum@users.noreply.github.com> + * + * This program is free software; you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free Software + * Foundation; either version 3 of the License, or (at your option) any later + * version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A + * PARTICULAR PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ + +package com.ichi2.anki + +import com.ichi2.anki.notifications.NotificationId +import com.ichi2.anki.worker.UniqueWorkNames +import org.junit.Test +import kotlin.reflect.KClass +import kotlin.reflect.KVisibility +import kotlin.reflect.full.declaredMemberProperties +import kotlin.test.assertFalse +import kotlin.test.assertNotNull + +class ConstantUniquenessTest { + + @Test + fun testConstantUniqueness() { + assertConstantUniqueness(NotificationId::class) + assertConstantUniqueness(UniqueWorkNames::class) + } + + companion object { + + /** + * To check whether all PUBLIC CONST values in an object are unique. + */ + fun assertConstantUniqueness(clazz: KClass) { + assertNotNull(clazz.objectInstance, "Can only check objects for uniqueness") + val valueSet = HashSet() + for (prop in clazz.declaredMemberProperties) { + if (!prop.isConst || prop.visibility != KVisibility.PUBLIC) { + continue + } + // use .call() since clazz represents an object + val value = prop.call() + assertFalse(valueSet.contains(value), "Duplicate value ('$value') for constant in ${clazz.qualifiedName}") + valueSet.add(value) + } + } + } +} diff --git a/AnkiDroid/src/test/java/com/ichi2/anki/previewer/PreviewerViewModelTest.kt b/AnkiDroid/src/test/java/com/ichi2/anki/previewer/PreviewerViewModelTest.kt index 6275c77bbd..f13ed608c2 100644 --- a/AnkiDroid/src/test/java/com/ichi2/anki/previewer/PreviewerViewModelTest.kt +++ b/AnkiDroid/src/test/java/com/ichi2/anki/previewer/PreviewerViewModelTest.kt @@ -1,30 +1,30 @@ -/* - * Copyright (c) 2024 Brayan Oliveira - * - * This program is free software; you can redistribute it and/or modify it under - * the terms of the GNU General Public License as published by the Free Software - * Foundation; either version 3 of the License, or (at your option) any later - * version. - * - * This program is distributed in the hope that it will be useful, but WITHOUT ANY - * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A - * PARTICULAR PURPOSE. See the GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along with - * this program. If not, see . - */ -package com.ichi2.anki.previewer - -import org.hamcrest.MatcherAssert.assertThat -import org.hamcrest.Matchers.equalTo -import org.junit.jupiter.api.Test - -class PreviewerViewModelTest { - @Test - fun `type answer fields are removed in questions`() { - assertThat( - PreviewerViewModel.typeAnsQuestionFilter("creu [[type:leu]]"), - equalTo("creu ") - ) - } -} +/* + * Copyright (c) 2024 Brayan Oliveira + * + * This program is free software; you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free Software + * Foundation; either version 3 of the License, or (at your option) any later + * version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A + * PARTICULAR PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ +package com.ichi2.anki.previewer + +import org.hamcrest.MatcherAssert.assertThat +import org.hamcrest.Matchers.equalTo +import org.junit.jupiter.api.Test + +class PreviewerViewModelTest { + @Test + fun `type answer fields are removed in questions`() { + assertThat( + PreviewerViewModel.typeAnsQuestionFilter("creu [[type:leu]]"), + equalTo("creu ") + ) + } +} diff --git a/jitpack.yml b/jitpack.yml index 384a44ef25..83314e1e15 100644 --- a/jitpack.yml +++ b/jitpack.yml @@ -1,15 +1,15 @@ -# We want control over the SDK used to build since we need newer JDKs -# Use the latest LTS supported by temurin with version format xx.yy.zz-tem (ignore sub-version) -# You may find versions e.g. for JDK21 like so https://adoptium.net/temurin/archive/?version=21 -# You may verify that a JDK is installed by looking at the "sdk list java" output from, for example: -# https://jitpack.io/com/github/ankidroid/Anki-Android/v2.18alpha7/build.log (just use a recent tag, -# if it has not built yet you will have to wait for it to build then on refresh the build log should exist) -before_install: - - sdk update - - sdk list java - - sdk install java 21.0.2-tem - - sdk use java 21.0.2-tem - -# We can do the absolute minimum to build the API module, no need to build AnkiDroid module -install: - - ./gradlew :api:publishToMavenLocal +# We want control over the SDK used to build since we need newer JDKs +# Use the latest LTS supported by temurin with version format xx.yy.zz-tem (ignore sub-version) +# You may find versions e.g. for JDK21 like so https://adoptium.net/temurin/archive/?version=21 +# You may verify that a JDK is installed by looking at the "sdk list java" output from, for example: +# https://jitpack.io/com/github/ankidroid/Anki-Android/v2.18alpha7/build.log (just use a recent tag, +# if it has not built yet you will have to wait for it to build then on refresh the build log should exist) +before_install: + - sdk update + - sdk list java + - sdk install java 21.0.2-tem + - sdk use java 21.0.2-tem + +# We can do the absolute minimum to build the API module, no need to build AnkiDroid module +install: + - ./gradlew :api:publishToMavenLocal