0
0
mirror of https://github.com/ankidroid/Anki-Android.git synced 2024-09-19 19:42:17 +02:00

Dependency Updates 20220219 (#13305)

* Bump org.jlleitschuh.gradle:ktlint-gradle from 11.1.0 to 11.2.0 (#13265)

* Bump org.jlleitschuh.gradle:ktlint-gradle from 11.1.0 to 11.2.0

Bumps org.jlleitschuh.gradle:ktlint-gradle from 11.1.0 to 11.2.0.

---
updated-dependencies:
- dependency-name: org.jlleitschuh.gradle:ktlint-gradle
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

* build(deps): move ktlint rules to .editorconfig

With new syntax

----

An individual property can be enabled or disabled with a rule property.
The name of the rule property consists of the ktlint_ prefix followed
by the rule set id followed by a _ and the rule id.

> https://pinterest.github.io/ktlint/faq/#why-is-editorconfig-property-disabled_rules-deprecated-and-how-do-i-resolve-this

----

disabled_rules is deprecated:

https://pinterest.github.io/ktlint/faq/#why-is-editorconfig-property-disabled_rules-deprecated-and-how-do-i-resolve-this

ktlint-gradle v11.2.0 resolves a few issues around this

> Fixed disabled_rules set only in editorconfig in ktlint 0.46+
> Fixed disabled_rules warning when using new editorconfig syntax in ktlint 0.48+

https://github.com/JLLeitschuh/ktlint-gradle/releases/tag/v11.2.0

* lint: fix new ktlint errors

Caused when we remove the version specifier in ktlint-gradle

* Argument should be on a separate line (unless all arguments can fit a single line)
* File annotations should be separated from file contents with a blank line
* Declarations and declarations with comments should have an empty space between.
* Multiple annotations should not be placed on the same line as the annotated construct

Removed unused `KotlinCleanup` class rather than fixing lint errors

* build(deps): depend on ktlint-gradle's ktlint

Some lint indentation was reduced due to this.
Fixed it manually as I didn't like the change

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: David Allison <62114487+david-allison@users.noreply.github.com>

* autoformat squash

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: David Allison <62114487+david-allison@users.noreply.github.com>
This commit is contained in:
Mike Hardy 2023-02-19 12:15:26 -05:00 committed by GitHub
parent 741ed5e034
commit a06e727f60
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
307 changed files with 1986 additions and 989 deletions

3
.editorconfig Normal file
View File

@ -0,0 +1,3 @@
[*.{kt,kts}]
ktlint_disabled_rules = no-wildcard-imports
# ktlint_standard_no-wildcard-imports = disabled

View File

@ -63,7 +63,6 @@ class ACRATest : InstrumentedTest() {
@Test
@Throws(Exception::class)
fun testDebugConfiguration() {
// Debug mode overrides all saved state so no setup needed
CrashReportService.setDebugACRAConfig(sharedPrefs)
assertArrayEquals(
@ -91,7 +90,6 @@ class ACRATest : InstrumentedTest() {
@Test
@Throws(Exception::class)
fun testProductionConfigurationUserDisabled() {
// set up as if the user had prefs saved to disable completely
setReportConfig(CrashReportService.FEEDBACK_REPORT_NEVER)

View File

@ -71,6 +71,7 @@ class ContentProviderTest : InstrumentedTest() {
// Whether tear down should be executed. I.e. if set up was not cancelled.
private var mTearDown = false
@KotlinCleanup("lateinit")
private var mNumDecksBeforeTest = 0
@ -290,7 +291,8 @@ class ContentProviderTest : InstrumentedTest() {
col = reopenCol() // test that the changes are physically saved to the DB
assertNotNull("Check template uri", templateUri)
assertEquals(
"Check template uri ord", expectedOrd.toLong(),
"Check template uri ord",
expectedOrd.toLong(),
ContentUris.parseId(
templateUri!!
)
@ -606,7 +608,8 @@ class ContentProviderTest : InstrumentedTest() {
i.toString()
)
assertThat(
"Update rows", cr.update(tmplUri, cv, null, null),
"Update rows",
cr.update(tmplUri, cv, null, null),
`is`(
greaterThan(0)
)
@ -649,7 +652,8 @@ class ContentProviderTest : InstrumentedTest() {
assertNotNull(allModels)
allModels.use {
assertThat(
"Check that there is at least one result", allModels.count,
"Check that there is at least one result",
allModels.count,
`is`(greaterThan(0))
)
while (allModels.moveToNext()) {
@ -689,7 +693,8 @@ class ContentProviderTest : InstrumentedTest() {
val numCards =
allModels.getInt(allModels.getColumnIndex(FlashCardsContract.Model.NUM_CARDS))
assertThat(
"Check that valid number of cards", numCards,
"Check that valid number of cards",
numCards,
`is`(
greaterThanOrEqualTo(1)
)
@ -958,7 +963,11 @@ class ContentProviderTest : InstrumentedTest() {
val col = col
val sched = col.sched
val reviewInfoCursor = contentResolver.query(
FlashCardsContract.ReviewInfo.CONTENT_URI, null, null, null, null
FlashCardsContract.ReviewInfo.CONTENT_URI,
null,
null,
null,
null
)
assertNotNull(reviewInfoCursor)
assertEquals("Check that we actually received one card", 1, reviewInfoCursor.count)
@ -1002,7 +1011,11 @@ class ContentProviderTest : InstrumentedTest() {
val selectedDeckBeforeTest = col.decks.selected()
col.decks.select(1) // select Default deck
val reviewInfoCursor = contentResolver.query(
FlashCardsContract.ReviewInfo.CONTENT_URI, null, deckSelector, deckArguments, null
FlashCardsContract.ReviewInfo.CONTENT_URI,
null,
deckSelector,
deckArguments,
null
)
assertNotNull(reviewInfoCursor)
assertEquals("Check that we actually received one card", 1, reviewInfoCursor.count)
@ -1168,7 +1181,6 @@ class ContentProviderTest : InstrumentedTest() {
*/
@Test
fun testSuspendCard() {
// get the first card due
// ----------------------
val col = col
@ -1190,6 +1202,7 @@ class ContentProviderTest : InstrumentedTest() {
val reviewInfoUri = FlashCardsContract.ReviewInfo.CONTENT_URI
val noteId = card.note().id
val cardOrd = card.ord
@KotlinCleanup("rename, while valid suspend is a kotlin soft keyword")
val values = ContentValues().apply {
val suspend = 1
@ -1304,6 +1317,7 @@ class ContentProviderTest : InstrumentedTest() {
private val TEST_MODEL_AFMT = arrayOf("{{BACK}}", "{{FRONTS}}")
private val TEST_NOTE_FIELDS = arrayOf("dis is za Fr0nt", "Te\$t")
private const val TEST_MODEL_CSS = "styleeeee"
@Suppress("SameParameterValue")
private fun setupNewNote(
col: com.ichi2.libanki.Collection,
@ -1318,7 +1332,8 @@ class ContentProviderTest : InstrumentedTest() {
}
newNote.addTag(tag)
assertThat(
"At least one card added for note", col.addNote(newNote),
"At least one card added for note",
col.addNote(newNote),
`is`(
greaterThanOrEqualTo(1)
)

View File

@ -38,6 +38,7 @@ class LayoutValidationTest : InstrumentedTest() {
@JvmField // required for Parameter
@Parameterized.Parameter(1)
var name: String? = null
@Test
@Throws(Exception::class)
fun ensureLayout() {

View File

@ -44,8 +44,10 @@ class NotificationChannelTest : InstrumentedTest() {
var runtimePermissionRule = GrantStoragePermission.instance
private var mCurrentAPI = -1
private var mTargetAPI = -1
@KotlinCleanup("lateinit")
private var mManager: NotificationManager? = null
@Before
@UiThreadTest
fun setUp() {

View File

@ -44,7 +44,6 @@ class HttpTest {
@Suppress("DEPRECATION")
@Test
fun testLogin() {
val username = "AnkiDroidInstrumentedTestUser"
val password = "AnkiDroidInstrumentedTestInvalidPass"
val invalidPayload = Connection.Payload(arrayOf(username, password, HostNum(null)))

View File

@ -59,6 +59,7 @@ class ImportTest : InstrumentedTest() {
// Allowing it to re-run now, 3 times, in case it flakes again.
@get:Rule
var retry = RetryRule(10)
@Before
@Throws(IOException::class)
fun setUp() {
@ -75,7 +76,6 @@ class ImportTest : InstrumentedTest() {
@Test
@Throws(IOException::class, JSONException::class, ImportExportException::class)
fun testAnki2Mediadupes() {
// add a note that references a sound
var n = testCol.newNote()
n.setField(0, "[sound:foo.mp3]")

View File

@ -26,10 +26,11 @@ object GrantStoragePermission {
val storagePermission = if (
targetSdkVersion >= Build.VERSION_CODES.R &&
Build.VERSION.SDK_INT >= Build.VERSION_CODES.R
)
) {
null
else
} else {
android.Manifest.permission.WRITE_EXTERNAL_STORAGE
}
/**
* Storage is longer necessary for API 30+
@ -38,8 +39,11 @@ object GrantStoragePermission {
val instance: TestRule = grantPermissions(storagePermission)
}
val notificationPermission = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU)
android.Manifest.permission.POST_NOTIFICATIONS else null
val notificationPermission = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
android.Manifest.permission.POST_NOTIFICATIONS
} else {
null
}
/** Grants permissions, given some may be invalid */
fun grantPermissions(vararg permissions: String?): TestRule {

View File

@ -30,32 +30,50 @@ object ViewAnimation {
when (type) {
SLIDE_IN_FROM_RIGHT -> {
animation = translateAnimation(
+1.0f, 0.0f, 0.0f, 0.0f
+1.0f,
0.0f,
0.0f,
0.0f
)
}
SLIDE_OUT_TO_RIGHT -> {
animation = translateAnimation(
0.0f, +1.0f, 0.0f, 0.0f
0.0f,
+1.0f,
0.0f,
0.0f
)
}
SLIDE_IN_FROM_LEFT -> {
animation = translateAnimation(
-1.0f, 0.0f, 0.0f, 0.0f
-1.0f,
0.0f,
0.0f,
0.0f
)
}
SLIDE_OUT_TO_LEFT -> {
animation = translateAnimation(
0.0f, -1.0f, 0.0f, 0.0f
0.0f,
-1.0f,
0.0f,
0.0f
)
}
SLIDE_IN_FROM_BOTTOM -> {
animation = translateAnimation(
0.0f, 0.0f, +1.0f, 0.0f
0.0f,
0.0f,
+1.0f,
0.0f
)
}
SLIDE_IN_FROM_TOP -> {
animation = translateAnimation(
0.0f, 0.0f, -1.0f, 0.0f
0.0f,
0.0f,
-1.0f,
0.0f
)
}
}

View File

@ -177,12 +177,16 @@ abstract class AbstractFlashcardViewer :
protected var answerField: FixedEditText? = null
protected var flipCardLayout: LinearLayout? = null
private var easeButtonsLayout: LinearLayout? = null
@KotlinCleanup("internal for AnkiDroidJsApi")
internal var easeButton1: EaseButton? = null
@KotlinCleanup("internal for AnkiDroidJsApi")
internal var easeButton2: EaseButton? = null
@KotlinCleanup("internal for AnkiDroidJsApi")
internal var easeButton3: EaseButton? = null
@KotlinCleanup("internal for AnkiDroidJsApi")
internal var easeButton4: EaseButton? = null
protected var topBarLayout: RelativeLayout? = null
@ -223,6 +227,7 @@ abstract class AbstractFlashcardViewer :
private var mViewerUrl: String? = null
private var mAssetLoader: WebViewAssetLoader? = null
private val mFadeDuration = 300
@KotlinCleanup("made internal for tests")
@VisibleForTesting(otherwise = VisibleForTesting.PROTECTED)
internal var sched: AbstractSched? = null
@ -651,7 +656,9 @@ abstract class AbstractFlashcardViewer :
override fun onKeyDown(keyCode: Int, event: KeyEvent): Boolean {
return if (processCardFunction { cardWebView: WebView? -> processHardwareButtonScroll(keyCode, cardWebView) }) {
true
} else super.onKeyDown(keyCode, event)
} else {
super.onKeyDown(keyCode, event)
}
}
public override val currentCardId: CardId? get() = currentCard?.id
@ -824,7 +831,6 @@ abstract class AbstractFlashcardViewer :
legacyUndo()
} else {
return launchCatchingTask {
if (!backendUndoAndShowPopup(findViewById(R.id.flip_card))) {
legacyUndo()
}
@ -1246,7 +1252,6 @@ abstract class AbstractFlashcardViewer :
}
protected open fun restoreCollectionPreferences(col: Collection) {
// These are preferences we pull out of the collection instead of SharedPreferences
try {
mShowNextReviewTime = col.get_config_boolean("estTimes")
@ -1652,104 +1657,106 @@ abstract class AbstractFlashcardViewer :
override fun executeCommand(which: ViewerCommand, fromGesture: Gesture?): Boolean {
return if (isControlBlocked && which !== ViewerCommand.EXIT) {
false
} else when (which) {
ViewerCommand.SHOW_ANSWER -> {
if (displayAnswer) {
return false
} else {
when (which) {
ViewerCommand.SHOW_ANSWER -> {
if (displayAnswer) {
return false
}
displayCardAnswer()
true
}
displayCardAnswer()
true
}
ViewerCommand.FLIP_OR_ANSWER_EASE1 -> {
flipOrAnswerCard(EASE_1)
true
}
ViewerCommand.FLIP_OR_ANSWER_EASE2 -> {
flipOrAnswerCard(EASE_2)
true
}
ViewerCommand.FLIP_OR_ANSWER_EASE3 -> {
flipOrAnswerCard(EASE_3)
true
}
ViewerCommand.FLIP_OR_ANSWER_EASE4 -> {
flipOrAnswerCard(EASE_4)
true
}
ViewerCommand.FLIP_OR_ANSWER_RECOMMENDED -> {
flipOrAnswerCard(getRecommendedEase(false))
true
}
ViewerCommand.FLIP_OR_ANSWER_BETTER_THAN_RECOMMENDED -> {
flipOrAnswerCard(getRecommendedEase(true))
true
}
ViewerCommand.EXIT -> {
closeReviewer(RESULT_DEFAULT, false)
true
}
ViewerCommand.UNDO -> {
if (!isUndoAvailable) {
return false
ViewerCommand.FLIP_OR_ANSWER_EASE1 -> {
flipOrAnswerCard(EASE_1)
true
}
ViewerCommand.FLIP_OR_ANSWER_EASE2 -> {
flipOrAnswerCard(EASE_2)
true
}
ViewerCommand.FLIP_OR_ANSWER_EASE3 -> {
flipOrAnswerCard(EASE_3)
true
}
ViewerCommand.FLIP_OR_ANSWER_EASE4 -> {
flipOrAnswerCard(EASE_4)
true
}
ViewerCommand.FLIP_OR_ANSWER_RECOMMENDED -> {
flipOrAnswerCard(getRecommendedEase(false))
true
}
ViewerCommand.FLIP_OR_ANSWER_BETTER_THAN_RECOMMENDED -> {
flipOrAnswerCard(getRecommendedEase(true))
true
}
ViewerCommand.EXIT -> {
closeReviewer(RESULT_DEFAULT, false)
true
}
ViewerCommand.UNDO -> {
if (!isUndoAvailable) {
return false
}
undo()
true
}
ViewerCommand.EDIT -> {
editCard(fromGesture)
true
}
ViewerCommand.TAG -> {
showTagsDialog()
true
}
ViewerCommand.BURY_CARD -> buryCard()
ViewerCommand.BURY_NOTE -> buryNote()
ViewerCommand.SUSPEND_CARD -> suspendCard()
ViewerCommand.SUSPEND_NOTE -> suspendNote()
ViewerCommand.DELETE -> {
showDeleteNoteDialog()
true
}
ViewerCommand.PLAY_MEDIA -> {
playSounds(true)
true
}
ViewerCommand.PAGE_UP -> {
onPageUp()
true
}
ViewerCommand.PAGE_DOWN -> {
onPageDown()
true
}
ViewerCommand.ABORT_AND_SYNC -> {
abortAndSync()
true
}
ViewerCommand.RECORD_VOICE -> {
recordVoice()
true
}
ViewerCommand.REPLAY_VOICE -> {
replayVoice()
true
}
ViewerCommand.TOGGLE_WHITEBOARD -> {
toggleWhiteboard()
true
}
ViewerCommand.SHOW_HINT -> {
loadUrlInViewer("javascript: showHint();")
true
}
ViewerCommand.SHOW_ALL_HINTS -> {
loadUrlInViewer("javascript: showAllHints();")
true
}
else -> {
Timber.w("Unknown command requested: %s", which)
false
}
undo()
true
}
ViewerCommand.EDIT -> {
editCard(fromGesture)
true
}
ViewerCommand.TAG -> {
showTagsDialog()
true
}
ViewerCommand.BURY_CARD -> buryCard()
ViewerCommand.BURY_NOTE -> buryNote()
ViewerCommand.SUSPEND_CARD -> suspendCard()
ViewerCommand.SUSPEND_NOTE -> suspendNote()
ViewerCommand.DELETE -> {
showDeleteNoteDialog()
true
}
ViewerCommand.PLAY_MEDIA -> {
playSounds(true)
true
}
ViewerCommand.PAGE_UP -> {
onPageUp()
true
}
ViewerCommand.PAGE_DOWN -> {
onPageDown()
true
}
ViewerCommand.ABORT_AND_SYNC -> {
abortAndSync()
true
}
ViewerCommand.RECORD_VOICE -> {
recordVoice()
true
}
ViewerCommand.REPLAY_VOICE -> {
replayVoice()
true
}
ViewerCommand.TOGGLE_WHITEBOARD -> {
toggleWhiteboard()
true
}
ViewerCommand.SHOW_HINT -> {
loadUrlInViewer("javascript: showHint();")
true
}
ViewerCommand.SHOW_ALL_HINTS -> {
loadUrlInViewer("javascript: showAllHints();")
true
}
else -> {
Timber.w("Unknown command requested: %s", which)
false
}
}
}
@ -2602,6 +2609,7 @@ abstract class AbstractFlashcardViewer :
* Should be protected, using non-JVM static members protected in the superclass companion is unsupported yet
*/
const val INITIAL_HIDE_DELAY = 200
// I don't see why we don't do this by intent.
/** to be sent to and from the card editor */
@set:VisibleForTesting(otherwise = VisibleForTesting.NONE)

View File

@ -64,6 +64,7 @@ open class AnkiDroidApp : Application() {
/** An exception if the WebView subsystem fails to load */
private var mWebViewError: Throwable? = null
private val mNotifications = MutableLiveData<Void?>()
@KotlinCleanup("can move analytics here now")
override fun attachBaseContext(base: Context) {
// update base context with preferred app language before attach
@ -260,7 +261,9 @@ open class AnkiDroidApp : Application() {
// throw new IllegalStateException(
// "Synthetic stacktrace didn't have enough elements: are you using proguard?");
// --- end of alteration from upstream Timber.DebugTree.getTag ---
} else createStackElementTag(stackTrace[CALL_STACK_INDEX])
} else {
createStackElementTag(stackTrace[CALL_STACK_INDEX])
}
}
// ---- END copied from Timber.DebugTree because DebugTree.getTag() is package private ----

View File

@ -39,7 +39,7 @@ fun DeckPicker.importColpkg(colpkgPath: String) {
if (progress.hasImporting()) {
text = progress.importing
}
},
}
) {
CollectionManager.importColpkg(colpkgPath)
}

View File

@ -41,7 +41,7 @@ fun DeckPicker.importApkgs(apkgPaths: List<String>) {
if (progress.hasImporting()) {
text = progress.importing
}
},
}
) {
undoableOp {
importAnkiPackage(apkgPath)
@ -105,7 +105,7 @@ suspend fun AnkiActivity.exportApkg(
if (progress.hasExporting()) {
text = progress.exporting
}
},
}
) {
withCol {
newBackend.exportAnkiPackage(apkgPath, withScheduling, withMedia, limit)
@ -115,14 +115,14 @@ suspend fun AnkiActivity.exportApkg(
suspend fun AnkiActivity.exportColpkg(
colpkgPath: String,
withMedia: Boolean,
withMedia: Boolean
) {
withProgress(
extractProgress = {
if (progress.hasExporting()) {
text = progress.exporting
}
},
}
) {
withCol {
newBackend.exportCollectionPackage(colpkgPath, withMedia, true)

View File

@ -35,7 +35,6 @@ suspend fun FragmentActivity.backendUndoAndShowPopup(anchorView: View? = null):
}
showSnackbar(TR.undoActionUndone(changes.operation)) {
// A snackbar may obscure vital elements (e.g: the answer buttons on the Reviewer)
// `anchorView` stops this
anchorView?.let { setAnchorView(anchorView) }

View File

@ -113,7 +113,9 @@ open class BackupManager {
// If have no backups, then a backup is necessary
return if (len <= 0) {
false
} else colBackups[len - 1].lastModified() == colFile.lastModified()
} else {
colBackups[len - 1].lastModified() == colFile.lastModified()
}
// no collection changes means we don't need a backup
}

View File

@ -239,6 +239,7 @@ open class CardBrowser :
@get:VisibleForTesting(otherwise = VisibleForTesting.NONE)
var isInMultiSelectMode = false
private set
@get:VisibleForTesting(otherwise = VisibleForTesting.NONE)
var isTruncated = false
private val mCheckedCards = Collections.synchronizedSet(LinkedHashSet<CardCache>())
@ -399,7 +400,8 @@ open class CardBrowser :
if (searchName.isNullOrEmpty()) {
showThemedToast(
this@CardBrowser,
getString(R.string.card_browser_list_my_searches_new_search_error_empty_name), true
getString(R.string.card_browser_list_my_searches_new_search_error_empty_name),
true
)
return
}
@ -413,7 +415,8 @@ open class CardBrowser :
} else {
showThemedToast(
this@CardBrowser,
getString(R.string.card_browser_list_my_searches_new_search_error_dup), true
getString(R.string.card_browser_list_my_searches_new_search_error_dup),
true
)
}
}
@ -443,8 +446,8 @@ open class CardBrowser :
* Change Deck
* @param did Id of the deck
*/
@VisibleForTesting
// TODO: This function can be simplified a lot
@VisibleForTesting
fun moveSelectedCardsToDeck(did: DeckId) {
val selectedDeck = col.decks.get(did)
// TODO: Currently try-catch is at every level which isn't required, simplify that
@ -577,7 +580,8 @@ open class CardBrowser :
val cardsColumn1Spinner = findViewById<Spinner>(R.id.browser_column1_spinner)
val column1Adapter = ArrayAdapter.createFromResource(
this,
R.array.browser_column1_headings, android.R.layout.simple_spinner_item
R.array.browser_column1_headings,
android.R.layout.simple_spinner_item
)
column1Adapter.setDropDownViewResource(R.layout.spinner_custom_layout)
cardsColumn1Spinner.adapter = column1Adapter
@ -606,7 +610,8 @@ open class CardBrowser :
val cardsColumn2Spinner = findViewById<Spinner>(R.id.browser_column2_spinner)
val column2Adapter = ArrayAdapter.createFromResource(
this,
R.array.browser_column2_headings, android.R.layout.simple_spinner_item
R.array.browser_column2_headings,
android.R.layout.simple_spinner_item
)
// The custom layout for the adapter is used to prevent the overlapping of various interactive components on the screen
column2Adapter.setDropDownViewResource(R.layout.spinner_custom_layout)
@ -638,7 +643,8 @@ open class CardBrowser :
cardsAdapter = MultiColumnListAdapter(
this,
R.layout.card_item_browser,
columnsContent, intArrayOf(R.id.card_sfld, R.id.card_column2),
columnsContent,
intArrayOf(R.id.card_sfld, R.id.card_column2),
sflRelativeFontSize,
sflCustomFont
)
@ -666,12 +672,7 @@ open class CardBrowser :
cardsListView!!.setOnItemLongClickListener { _: AdapterView<*>?, view: View?, position: Int, _: Long ->
if (isInMultiSelectMode) {
var hasChanged = false
for (
i in min(mLastSelectedPosition, position)..max(
mLastSelectedPosition,
position
)
) {
for (i in min(mLastSelectedPosition, position)..max(mLastSelectedPosition, position)) {
val card = cardsListView!!.getItemAtPosition(i) as CardCache
// Add to the set of checked cards
@ -697,8 +698,12 @@ open class CardBrowser :
window.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_HIDDEN)
val deckId = col.decks.selected()
deckSpinnerSelection = DeckSpinnerSelection(
this, col, findViewById(R.id.toolbar_spinner),
showAllDecks = true, alwaysShowDefault = false, showFilteredDecks = true
this,
col,
findViewById(R.id.toolbar_spinner),
showAllDecks = true,
alwaysShowDefault = false,
showFilteredDecks = true
)
inCardsMode = AnkiDroidApp.getSharedPrefs(this).getBoolean("inCardsMode", true)
isTruncated = AnkiDroidApp.getSharedPrefs(this).getBoolean("isTruncated", false)
@ -738,7 +743,6 @@ open class CardBrowser :
}
}
KeyEvent.KEYCODE_E -> {
// Ctrl+Shift+E: Export (TODO)
if (event.isCtrlPressed) {
Timber.i("Ctrl+E: Add Note")
@ -1127,8 +1131,10 @@ open class CardBrowser :
val searchTerms = mSearchView!!.query.toString()
showDialogFragment(
newInstance(
null, mMySearchesDialogListener,
searchTerms, CardBrowserMySearchesDialog.CARD_BROWSER_MY_SEARCHES_TYPE_SAVE
null,
mMySearchesDialogListener,
searchTerms,
CardBrowserMySearchesDialog.CARD_BROWSER_MY_SEARCHES_TYPE_SAVE
)
)
return true
@ -1142,8 +1148,10 @@ open class CardBrowser :
)
showDialogFragment(
newInstance(
savedFilters, mMySearchesDialogListener,
"", CardBrowserMySearchesDialog.CARD_BROWSER_MY_SEARCHES_TYPE_LIST
savedFilters,
mMySearchesDialogListener,
"",
CardBrowserMySearchesDialog.CARD_BROWSER_MY_SEARCHES_TYPE_LIST
)
)
return true
@ -1566,7 +1574,9 @@ open class CardBrowser :
mTagsDialogListenerAction = TagsDialogListenerAction.EDIT_TAGS
val dialog = mTagsDialogFactory!!.newTagsDialog().withArguments(
TagsDialog.DialogType.EDIT_TAGS,
checkedTags, uncheckedTags, allTags
checkedTags,
uncheckedTags,
allTags
)
showDialogFragment(dialog)
}
@ -1574,7 +1584,9 @@ open class CardBrowser :
private fun showFilterByTagsDialog() {
mTagsDialogListenerAction = TagsDialogListenerAction.FILTER
val dialog = mTagsDialogFactory!!.newTagsDialog().withArguments(
TagsDialog.DialogType.FILTER_BY_TAG, ArrayList(0), col.tags.all()
TagsDialog.DialogType.FILTER_BY_TAG,
ArrayList(0),
col.tags.all()
)
showDialogFragment(dialog)
}
@ -1708,10 +1720,12 @@ open class CardBrowser :
override val subtitleText: String
get() {
val count = cardCount
@androidx.annotation.StringRes val subtitleId = if (inCardsMode)
@androidx.annotation.StringRes val subtitleId = if (inCardsMode) {
R.plurals.card_browser_subtitle
else
} else {
R.plurals.card_browser_subtitle_notes_mode
}
return resources.getQuantityString(subtitleId, count, count)
}
@ -2035,8 +2049,11 @@ open class CardBrowser :
protected suspend fun renderBrowserQAParams(firstVisibleItem: Int, visibleItemCount: Int, cards: CardCollection<CardCache>) {
Timber.d("Starting Q&A background rendering")
val result = renderBrowserQA(
cards, firstVisibleItem, visibleItemCount,
mColumn1Index, mColumn2Index
cards,
firstVisibleItem,
visibleItemCount,
mColumn1Index,
mColumn2Index
) {
// Note: This is called every time a card is rendered.
// It blocks the long-click callback while the task is running, so usage of the task should be minimized
@ -2466,7 +2483,9 @@ open class CardBrowser :
}
return if (javaClass != other.javaClass) {
false
} else id == (other as CardCache).id
} else {
id == (other as CardCache).id
}
}
override fun hashCode(): Int {

View File

@ -267,7 +267,9 @@ class CardInfo : AnkiActivity() {
fun intervalAsTimeSeconds(): Long {
return if (ivl < 0) {
-ivl
} else ivl * Stats.SECONDS_PER_DAY
} else {
ivl * Stats.SECONDS_PER_DAY
}
}
// saves space if we just use seconds rather than a "s" suffix

View File

@ -187,13 +187,15 @@ class CardTemplateBrowserAppearanceEditor : AnkiActivity() {
fun fromIntent(intent: Intent?): Result? {
return if (intent == null) {
null
} else try {
val question = intent.getStringExtra(INTENT_QUESTION_FORMAT)
val answer = intent.getStringExtra(INTENT_ANSWER_FORMAT)
Result(question, answer)
} catch (e: Exception) {
Timber.w(e, "Could not read result from intent")
null
} else {
try {
val question = intent.getStringExtra(INTENT_QUESTION_FORMAT)
val answer = intent.getStringExtra(INTENT_ANSWER_FORMAT)
Result(question, answer)
} catch (e: Exception) {
Timber.w(e, "Could not read result from intent")
null
}
}
}
}
@ -205,6 +207,7 @@ class CardTemplateBrowserAppearanceEditor : AnkiActivity() {
/** Specified the card browser should use the default template formatter */
const val VALUE_USE_DEFAULT = ""
@CheckResult
fun getIntentFromTemplate(context: Context, template: JSONObject): Intent {
val browserQuestionTemplate = template.getString("bqfmt")

View File

@ -584,7 +584,8 @@ open class CardTemplateEditor : AnkiActivity(), DeckSelectionListener {
return false
}
},
viewLifecycleOwner, Lifecycle.State.RESUMED
viewLifecycleOwner,
Lifecycle.State.RESUMED
)
}
@ -688,7 +689,6 @@ open class CardTemplateEditor : AnkiActivity(), DeckSelectionListener {
val currentDeletes = tempModel.getDeleteDbOrds(position)
// TODO - this is a SQL query on GUI thread - should see a DeckTask conversion ideally
if (col.models.getCardIdsForModel(tempModel.modelId, currentDeletes) == null) {
// It is possible but unlikely that a user has an in-memory template addition that would
// generate cards making the deletion safe, but we don't handle that. All users who do
// not already have cards generated making it safe will see this error message:
@ -748,7 +748,8 @@ open class CardTemplateEditor : AnkiActivity(), DeckSelectionListener {
R.plurals.card_template_editor_confirm_delete,
numAffectedCards
),
numAffectedCards, tmpl.optString("name")
numAffectedCards,
tmpl.optString("name")
)
d.setArgs(msg)
@ -918,8 +919,10 @@ open class CardTemplateEditor : AnkiActivity(), DeckSelectionListener {
private const val EDITOR_NOTE_ID = "noteId"
private const val EDITOR_START_ORD_ID = "ordId"
private const val CARD_INDEX = "card_ord"
@Suppress("unused")
private const val REQUEST_PREVIEWER = 0
@Suppress("unused")
private const val REQUEST_CARD_BROWSER_APPEARANCE = 1
}

View File

@ -385,7 +385,9 @@ open class CardTemplatePreviewer : AbstractFlashcardViewer() {
override val isEmpty: Boolean
get() = if (mNote != null) {
false
} else super.isEmpty
} else {
super.isEmpty
}
/** Override the method that fetches the model so we can render unsaved models */
override fun model(): Model {

View File

@ -75,7 +75,8 @@ open class CollectionHelper {
Timber.i("Begin openCollection: %s", path)
val backend = BackendFactory.getBackend(context)
val collection = Storage.collection(
context, path,
context,
path,
server = false,
log = true,
backend = backend
@ -223,13 +224,15 @@ open class CollectionHelper {
}
val required = Formatter.formatShortFileSize(context, mRequiredSpace)
val insufficientSpace = context.resources.getString(
R.string.integrity_check_insufficient_space, required
R.string.integrity_check_insufficient_space,
required
)
// Also concat in the extra content showing the current free space.
val currentFree = Formatter.formatShortFileSize(context, mFreeSpace)
val insufficientSpaceCurrentFree = context.resources.getString(
R.string.integrity_check_insufficient_space_extra_content, currentFree
R.string.integrity_check_insufficient_space_extra_content,
currentFree
)
return insufficientSpace + insufficientSpaceCurrentFree
}
@ -539,10 +542,12 @@ open class CollectionHelper {
getDefaultAnkiDroidDirectory(context),
"androidTest"
).absolutePath
} else PreferenceExtensions.getOrSetString(
preferences,
PREF_COLLECTION_PATH
) { getDefaultAnkiDroidDirectory(context) }
} else {
PreferenceExtensions.getOrSetString(
preferences,
PREF_COLLECTION_PATH
) { getDefaultAnkiDroidDirectory(context) }
}
}
/** Fetches additional collection data not required for

View File

@ -296,12 +296,14 @@ object CollectionManager {
// out our own code, and standard dalvik/java.lang stack frames
val caller = stackTraceElements.filter {
val klass = it.className
for (
text in listOf(
"CollectionManager", "dalvik", "java.lang",
"CollectionHelper", "AnkiActivity"
)
) {
val toCheck = listOf(
"CollectionManager",
"dalvik",
"java.lang",
"CollectionHelper",
"AnkiActivity"
)
for (text in toCheck) {
if (text in klass) {
return@filter false
}

View File

@ -180,7 +180,7 @@ private fun showError(context: Context, msg: String, exception: Throwable) {
suspend fun <T> Backend.withProgress(
extractProgress: ProgressContext.() -> Unit,
updateUi: ProgressContext.() -> Unit,
block: suspend CoroutineScope.() -> T,
block: suspend CoroutineScope.() -> T
): T {
return coroutineScope {
val monitor = launch {
@ -292,7 +292,7 @@ private suspend fun <T> withProgressDialog(
private suspend fun monitorProgress(
backend: Backend,
extractProgress: ProgressContext.() -> Unit,
updateUi: ProgressContext.() -> Unit,
updateUi: ProgressContext.() -> Unit
) {
val state = ProgressContext(Progress.getDefaultInstance())
while (true) {
@ -313,7 +313,7 @@ data class ProgressContext(
var progress: Progress,
var text: String = "",
/** If set, shows progress bar with a of b complete. */
var amount: Pair<Int, Int>? = null,
var amount: Pair<Int, Int>? = null
)
@Suppress("Deprecation") // ProgressDialog deprecation

View File

@ -48,12 +48,22 @@ object CrashReportService {
/** Our ACRA configurations, initialized during Application.onCreate() */
@JvmStatic
private var logcatArgs = arrayOf(
"-t", "100", "-v", "time", "ActivityManager:I", "SQLiteLog:W", AnkiDroidApp.TAG + ":D", "*:S"
"-t",
"100",
"-v",
"time",
"ActivityManager:I",
"SQLiteLog:W",
AnkiDroidApp.TAG + ":D",
"*:S"
)
@JvmStatic
private var dialogEnabled = true
@JvmStatic
private lateinit var toastText: String
@JvmStatic
lateinit var acraCoreConfigBuilder: CoreConfigurationBuilder
private set

View File

@ -34,7 +34,7 @@ fun DeckPicker.handleDatabaseCheck() {
}
}
},
onCancel = null,
onCancel = null
) {
withCol {
newBackend.fixIntegrity()

View File

@ -136,7 +136,9 @@ class DeckOptionsActivity :
mValues["reminderEnabled"] = reminder.getBoolean("enabled").toString()
mValues["reminderTime"] = String.format(
"%1$02d:%2$02d", reminderTime.getLong(0), reminderTime.getLong(1)
"%1$02d:%2$02d",
reminderTime.getLong(0),
reminderTime.getLong(1)
)
} else {
mValues["reminderEnabled"] = "false"
@ -171,8 +173,10 @@ class DeckOptionsActivity :
fun preConfChange() {
val res = deckOptionsActivity.resources
progressDialog = StyledProgressDialog.show(
deckOptionsActivity as Context, null,
res?.getString(R.string.reordering_cards), false
deckOptionsActivity as Context,
null,
res?.getString(R.string.reordering_cards),
false
)
}
@ -282,7 +286,8 @@ class DeckOptionsActivity :
// Don't remove the options group if it's the default group
UIUtils.showThemedToast(
this@DeckOptionsActivity,
resources.getString(R.string.default_conf_delete_error), false
resources.getString(R.string.default_conf_delete_error),
false
)
} else {
// Remove options group, handling the case where the user needs to confirm full sync
@ -346,7 +351,8 @@ class DeckOptionsActivity :
applicationContext,
mOptions.getLong("id").toInt(),
Intent(applicationContext, ReminderService::class.java).putExtra(
ReminderService.EXTRA_DECK_OPTION_ID, mOptions.getLong("id")
ReminderService.EXTRA_DECK_OPTION_ID,
mOptions.getLong("id")
),
0
)
@ -698,7 +704,6 @@ class DeckOptionsActivity :
companion object {
fun reminderToCalendar(time: Time, reminder: JSONObject): Calendar {
val calendar = time.calendar()
calendar[Calendar.HOUR_OF_DAY] = reminder.getJSONArray("time").getInt(0)

View File

@ -22,6 +22,7 @@
// usage of 'this' in constructors when class is non-final - weak warning
// should be OK as this is only non-final for tests
@file:Suppress("LeakingThis")
package com.ichi2.anki
import android.Manifest
@ -137,6 +138,7 @@ import kotlin.system.measureTimeMillis
const val MIGRATION_WAS_LAST_POSTPONED_AT_SECONDS = "secondWhenMigrationWasPostponedLast"
const val POSTPONE_MIGRATION_INTERVAL_DAYS = 5L
/**
* The current entry point for AnkiDroid. Displays decks, allowing users to study. Many other functions.
*
@ -179,6 +181,7 @@ open class DeckPicker :
private var mShortAnimDuration = 0
private var mBackButtonPressedToExit = false
private lateinit var mDeckPickerContent: RelativeLayout
@Suppress("Deprecation") // TODO: Encapsulate ProgressDialog within a class to limit the use of deprecated functionality
private var mProgressDialog: android.app.ProgressDialog? = null
private var mStudyoptionsFrame: View? = null // not lateInit - can be null
@ -191,6 +194,7 @@ open class DeckPicker :
private lateinit var mNoDecksPlaceholder: LinearLayout
private lateinit var mPullToSyncWrapper: SwipeRefreshLayout
private lateinit var mReviewSummaryTextView: TextView
@KotlinCleanup("make lateinit, but needs more changes")
private var mUnmountReceiver: BroadcastReceiver? = null
private lateinit var mFloatingActionMenu: DeckPickerFloatingActionMenu
@ -343,7 +347,9 @@ open class DeckPicker :
if (context.mProgressDialog == null || !context.mProgressDialog!!.isShowing) {
context.mProgressDialog = StyledProgressDialog.show(
context,
context.resources.getString(R.string.import_title), null, false
context.resources.getString(R.string.import_title),
null,
false
)
}
}
@ -377,7 +383,8 @@ open class DeckPicker :
context.mProgressDialog = StyledProgressDialog.show(
context,
context.resources.getString(R.string.import_title),
context.resources.getString(R.string.import_replacing), false
context.resources.getString(R.string.import_replacing),
false
)
}
}
@ -390,6 +397,7 @@ open class DeckPicker :
context.mProgressDialog!!.setMessage(value)
}
}
// ----------------------------------------------------------------------------
// ANDROID ACTIVITY METHODS
// ----------------------------------------------------------------------------
@ -1176,7 +1184,6 @@ open class DeckPicker :
}
private fun showStartupScreensAndDialogs(preferences: SharedPreferences, skip: Int) {
// For Android 8/8.1 we want to use software rendering by default or the Reviewer UI is broken #7369
if (sdkVersion == Build.VERSION_CODES.O ||
sdkVersion == Build.VERSION_CODES.O_MR1
@ -1693,7 +1700,8 @@ open class DeckPicker :
if (mProgressDialog == null || !mProgressDialog!!.isShowing) {
try {
mProgressDialog = StyledProgressDialog.show(
this@DeckPicker, resources.getString(R.string.sync_title),
this@DeckPicker,
resources.getString(R.string.sync_title),
"""
${resources.getString(R.string.sync_title)}
${resources.getString(R.string.sync_up_down_size, mCountUp, mCountDown)}
@ -1818,7 +1826,8 @@ open class DeckPicker :
diff >= 86100 -> {
// The difference if more than a day minus 5 minutes acceptable by ankiweb error
res.getString(
R.string.sync_log_clocks_unsynchronized, diff,
R.string.sync_log_clocks_unsynchronized,
diff,
res.getString(R.string.sync_log_clocks_unsynchronized_date)
)
}
@ -1826,7 +1835,8 @@ open class DeckPicker :
// The difference would be within limit if we adjusted the time by few hours
// It doesn't work for all timezones, but it covers most and it's a guess anyway
res.getString(
R.string.sync_log_clocks_unsynchronized, diff,
R.string.sync_log_clocks_unsynchronized,
diff,
res.getString(R.string.sync_log_clocks_unsynchronized_tz)
)
}
@ -1922,7 +1932,8 @@ open class DeckPicker :
if (dialogMessage == null) {
dialogMessage = res.getString(
R.string.sync_log_error_specific,
code.toString(), result[1]
code.toString(),
result[1]
)
}
} else {
@ -2041,7 +2052,9 @@ open class DeckPicker :
val frag = supportFragmentManager.findFragmentById(R.id.studyoptions_fragment)
return if (frag is StudyOptionsFragment) {
frag
} else null
} else {
null
}
}
/**
@ -2161,7 +2174,8 @@ open class DeckPicker :
setAction(R.string.study_more) {
val d = mCustomStudyDialogFactory.newCustomStudyDialog().withArguments(
CustomStudyDialog.ContextMenuConfiguration.LIMITS,
col.decks.selected(), true
col.decks.selected(),
true
)
showDialogFragment(d)
}
@ -2200,7 +2214,8 @@ open class DeckPicker :
setAction(R.string.custom_study) {
val d = mCustomStudyDialogFactory.newCustomStudyDialog().withArguments(
CustomStudyDialog.ContextMenuConfiguration.EMPTY_SCHEDULE,
col.decks.selected(), true
col.decks.selected(),
true
)
showDialogFragment(d)
}
@ -2306,7 +2321,8 @@ open class DeckPicker :
mNoDecksPlaceholder.visibility = if (isEmpty) View.VISIBLE else View.GONE
} else {
val translation = TypedValue.applyDimension(
TypedValue.COMPLEX_UNIT_DIP, 8f,
TypedValue.COMPLEX_UNIT_DIP,
8f,
resources.displayMetrics
)
val decksListShown = mDeckPickerContent.visibility == View.VISIBLE
@ -2691,8 +2707,10 @@ open class DeckPicker :
internal inner class CheckDatabaseListener : TaskListener<String, Pair<Boolean, CheckDatabaseResult?>?>() {
override fun onPreExecute() {
mProgressDialog = StyledProgressDialog.show(
this@DeckPicker, AnkiDroidApp.appResources.getString(R.string.app_name),
resources.getString(R.string.check_db_message), false
this@DeckPicker,
AnkiDroidApp.appResources.getString(R.string.app_name),
resources.getString(R.string.check_db_message),
false
)
}
@ -2930,6 +2948,7 @@ open class DeckPicker :
get() = getSharedPrefs(baseContext).getLong(MIGRATION_WAS_LAST_POSTPONED_AT_SECONDS, 0L)
set(timeInSecond) = getSharedPrefs(baseContext)
.edit { putLong(MIGRATION_WAS_LAST_POSTPONED_AT_SECONDS, timeInSecond) }
/**
* Show a dialog offering to migrate, postpone or learn more.
*/
@ -2945,7 +2964,8 @@ open class DeckPicker :
else -> R.string.migration_update_request_dialog_download_warning
}
@Language("HTML") val message = """${getString(R.string.migration_update_request)}
@Language("HTML")
val message = """${getString(R.string.migration_update_request)}
<br>
<br>${getString(ifYouUninstallMessageId)}"""
@ -2993,7 +3013,7 @@ data class OptionsMenuState(
/** If undo is available, a string describing the action. */
val undoIcon: String?,
val syncIcon: SyncIconState,
val offerToMigrate: Boolean,
val offerToMigrate: Boolean
)
enum class SyncIconState {
@ -3001,10 +3021,11 @@ enum class SyncIconState {
PendingChanges,
FullSync,
NotLoggedIn,
/**
* The icon should appear as disabled. Currently only occurs during scoped storage migration.
*/
Disabled,
Disabled
}
/**

View File

@ -57,7 +57,7 @@ class DeckSpinnerSelection(
private val spinner: Spinner,
private val showAllDecks: Boolean,
private val alwaysShowDefault: Boolean,
private val showFilteredDecks: Boolean,
private val showFilteredDecks: Boolean
) {
/**
* All of the decks shown to the user.
@ -112,7 +112,6 @@ class DeckSpinnerSelection(
}
val noteDeckAdapter: ArrayAdapter<String?> = object : ArrayAdapter<String?>(context, R.layout.multiline_spinner_item, deckNames as List<String?>) {
override fun getDropDownView(position: Int, convertView: View?, parent: ViewGroup): View {
// Cast the drop down items (popup items) as text view
val tv = super.getDropDownView(position, convertView, parent) as TextView
@ -189,7 +188,9 @@ class DeckSpinnerSelection(
fun selectDeckById(deckId: DeckId, setAsCurrentDeck: Boolean): Boolean {
return if (deckId == ALL_DECKS_ID) {
selectAllDecks()
} else selectDeck(deckId, setAsCurrentDeck)
} else {
selectDeck(deckId, setAsCurrentDeck)
}
}
/**

View File

@ -55,6 +55,7 @@ class FieldEditText : FixedEditText, NoteService.NoteField {
private var mOrigBackground: Drawable? = null
private var mSelectionChangeListener: TextSelectionListener? = null
private var mImageListener: ImagePasteListener? = null
@VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)
var clipboard: ClipboardManager? = null
@ -103,7 +104,8 @@ class FieldEditText : FixedEditText, NoteService.NoteField {
val inputConnection = super.onCreateInputConnection(editorInfo) ?: return null
EditorInfoCompat.setContentMimeTypes(editorInfo, IMAGE_MIME_TYPES)
ViewCompat.setOnReceiveContentListener(
this, IMAGE_MIME_TYPES,
this,
IMAGE_MIME_TYPES,
object : OnReceiveContentListener {
override fun onReceiveContent(view: View, payload: ContentInfoCompat): ContentInfoCompat? {
val pair = payload.partition { item -> item.uri != null }
@ -219,7 +221,9 @@ class FieldEditText : FixedEditText, NoteService.NoteField {
private fun onImagePaste(imageUri: Uri?): Boolean {
return if (imageUri == null) {
false
} else mImageListener!!.onImagePaste(this, imageUri)
} else {
mImageListener!!.onImagePaste(this, imageUri)
}
}
override fun onRestoreInstanceState(state: Parcelable) {

View File

@ -54,8 +54,10 @@ class FilterSheetBottomFragment :
private lateinit var flagListAdapter: FlagsAdapter
private lateinit var flagToggleIcon: ImageView
/** Heading of the Flags filter section */
private lateinit var filterHeaderFlags: TextView
/** Icon of the Flags filter section */
private lateinit var filterIconFlags: ImageView
@ -145,9 +147,7 @@ class FilterSheetBottomFragment :
}
flagsHeaderLayout.setOnClickListener {
if (SystemClock.elapsedRealtime() - lastClickTime > DELAY_TIME) {
lastClickTime = SystemClock.elapsedRealtime().toInt()
if (flagsRecyclerViewLayout.isVisible) {
@ -164,7 +164,6 @@ class FilterSheetBottomFragment :
private fun createQuery(
flagList: Set<SearchNode.Flag>
): String {
if (flagList.isEmpty()) {
return ""
}
@ -207,6 +206,7 @@ class FilterSheetBottomFragment :
inner class ViewHolder(view: View) : RecyclerView.ViewHolder(view) {
private val itemTextView: TextView = view.findViewById(R.id.filter_list_item)
val icon: ImageView = view.findViewById(R.id.filter_list_icon)
/** Checks whether flagSearchItems was empty before adding new element to it */
private fun onFlagItemClicked(item: Flags) {

View File

@ -47,6 +47,7 @@ private const val GITHUB_COMMITS = "https://github.com/ankidroid/Anki-Android/co
*/
class Info : AnkiActivity() {
private var mWebView: WebView? = null
@SuppressLint("SetJavaScriptEnabled")
override fun onCreate(savedInstanceState: Bundle?) {
if (showedActivityFailedScreen(savedInstanceState)) {
@ -110,7 +111,6 @@ class Info : AnkiActivity() {
mWebView!!.settings.javaScriptEnabled = true
mWebView!!.webViewClient = object : WebViewClient() {
override fun onPageFinished(view: WebView, url: String) {
/* The order of below javascript code must not change (this order works both in debug and release mode)
* or else it will break in any one mode.
*/

View File

@ -30,7 +30,6 @@ object InitialActivity {
/** Returns null on success */
@CheckResult
fun getStartupFailureType(context: Context): StartupFailure? {
// A WebView failure means that we skip `AnkiDroidApp`, and therefore haven't loaded the collection
if (AnkiDroidApp.webViewFailedToLoad()) {
return StartupFailure.WEBVIEW_FAILED

View File

@ -33,7 +33,7 @@ object LeakCanaryConfiguration {
retainedVisibleThreshold = 0,
referenceMatchers = AndroidReferenceMatchers.appDefaults,
computeRetainedHeapSize = false,
maxStoredHeapDumps = 0,
maxStoredHeapDumps = 0
)
}

View File

@ -63,10 +63,11 @@ object MetaDB {
private fun openDB(context: Context) {
try {
mMetaDb = context.openOrCreateDatabase(DATABASE_NAME, 0, null).let {
if (it.needUpgrade(DATABASE_VERSION))
if (it.needUpgrade(DATABASE_VERSION)) {
upgradeDB(it, DATABASE_VERSION)
else
} else {
it
}
}
Timber.v("Opening MetaDB")
} catch (e: Exception) {
@ -225,7 +226,10 @@ object MetaDB {
mMetaDb!!.execSQL(
"INSERT INTO languages (did, ord, qa, language) " + " VALUES (?, ?, ?, ?);",
arrayOf<Any>(
did, ord, qa.int, language
did,
ord,
qa.int,
language
)
)
Timber.v("Store language for deck %d", did)
@ -233,7 +237,10 @@ object MetaDB {
mMetaDb!!.execSQL(
"UPDATE languages SET language = ? WHERE did = ? AND ord = ? AND qa = ?;",
arrayOf<Any>(
language, did, ord, qa.int
language,
did,
ord,
qa.int
)
)
Timber.v("Update language for deck %d", did)
@ -460,8 +467,13 @@ object MetaDB {
var cursor: Cursor? = null
try {
cursor = mMetaDb!!.query(
"smallWidgetStatus", arrayOf("due", "eta"),
null, null, null, null, null
"smallWidgetStatus",
arrayOf("due", "eta"),
null,
null,
null,
null,
null
)
if (cursor.moveToNext()) {
return intArrayOf(cursor.getInt(0), cursor.getInt(1))

View File

@ -50,6 +50,7 @@ open class MyAccount : AnkiActivity() {
private lateinit var mUsername: EditText
private lateinit var mPassword: TextInputEditField
private lateinit var mUsernameLoggedIn: TextView
@Suppress("Deprecation")
private var mProgressDialog: android.app.ProgressDialog? = null
var toolbar: Toolbar? = null
@ -116,7 +117,8 @@ open class MyAccount : AnkiActivity() {
mLoginListener,
Connection.Payload(
arrayOf(
username, password,
username,
password,
getInstance(this)
)
)
@ -155,7 +157,8 @@ open class MyAccount : AnkiActivity() {
mLoginListener,
Connection.Payload(
arrayOf(
username, password,
username,
password,
getInstance(this)
)
)
@ -258,8 +261,10 @@ open class MyAccount : AnkiActivity() {
Timber.d("loginListener.onPreExecute()")
if (mProgressDialog == null || !mProgressDialog!!.isShowing) {
mProgressDialog = StyledProgressDialog.show(
this@MyAccount, null,
resources.getString(R.string.alert_logging_message), false
this@MyAccount,
null,
resources.getString(R.string.alert_logging_message),
false
)
}
}

View File

@ -61,6 +61,7 @@ abstract class NavigationDrawerActivity :
*/
protected var fragmented = false
private var mNavButtonGoesBack = false
// Navigation drawer list item entries
private lateinit var mDrawerLayout: DrawerLayout
private lateinit var mNavigationView: NavigationView
@ -77,7 +78,9 @@ abstract class NavigationDrawerActivity :
// Using ClosableDrawerLayout as a parent view.
val closableDrawerLayout = LayoutInflater.from(this).inflate(
navigationDrawerLayout, null, false
navigationDrawerLayout,
null,
false
) as ClosableDrawerLayout
// Get CoordinatorLayout using resource ID
val coordinatorLayout = LayoutInflater.from(this)

View File

@ -134,6 +134,7 @@ class NoteEditor : AnkiActivity(), DeckSelectionListener, SubtitleListener, Tags
// non-null after onCollectionLoaded
private var mEditorNote: Note? = null
/* Null if adding a new card. Presently NonNull if editing an existing note - but this is subject to change */
private var mCurrentEditedCard: Card? = null
private var mSelectedTags: ArrayList<String>? = null
@ -142,6 +143,7 @@ class NoteEditor : AnkiActivity(), DeckSelectionListener, SubtitleListener, Tags
var deckId: DeckId = 0
private set
private var mAllModelIds: List<Long>? = null
@KotlinCleanup("this ideally should be Int, Int?")
private var mModelChangeFieldMap: MutableMap<Int, Int>? = null
private var mModelChangeCardMap: HashMap<Int, Int?>? = null
@ -201,7 +203,9 @@ class NoteEditor : AnkiActivity(), DeckSelectionListener, SubtitleListener, Tags
}
return if (allFieldsHaveContent()) {
R.string.note_editor_no_cards_created_all_fields
} else R.string.note_editor_no_cards_created
} else {
R.string.note_editor_no_cards_created
}
// Otherwise, display "no cards created".
}
@ -360,7 +364,9 @@ class NoteEditor : AnkiActivity(), DeckSelectionListener, SubtitleListener, Tags
}
mDeckSpinnerSelection =
DeckSpinnerSelection(
this, col, findViewById(R.id.note_deck_spinner),
this,
col,
findViewById(R.id.note_deck_spinner),
showAllDecks = false,
alwaysShowDefault = true,
showFilteredDecks = false
@ -429,7 +435,6 @@ class NoteEditor : AnkiActivity(), DeckSelectionListener, SubtitleListener, Tags
}
private fun modifyCurrentSelection(formatter: Toolbar.TextFormatter, textBox: FieldEditText) {
// get the current text and selection locations
val selectionStart = textBox.selectionStart
val selectionEnd = textBox.selectionEnd
@ -623,7 +628,9 @@ class NoteEditor : AnkiActivity(), DeckSelectionListener, SubtitleListener, Tags
// changed fields?
return if (isFieldEdited) {
true
} else isTagsEdited
} else {
isTagsEdited
}
// changed tags?
}
@ -805,7 +812,6 @@ class NoteEditor : AnkiActivity(), DeckSelectionListener, SubtitleListener, Tags
val dialog = ConfirmationDialog()
dialog.setArgs(res.getString(R.string.full_sync_confirmation))
val confirm = Runnable {
// Bypass the check once the user confirms
col.modSchemaNoCheck()
try {
@ -1182,7 +1188,6 @@ class NoteEditor : AnkiActivity(), DeckSelectionListener, SubtitleListener, Tags
}
}
REQUEST_TEMPLATE_EDIT -> {
// Model can change regardless of exit type - update ourselves and CardBrowser
mReloadRequired = true
mEditorNote!!.reloadModel()
@ -1416,8 +1421,9 @@ class NoteEditor : AnkiActivity(), DeckSelectionListener, SubtitleListener, Tags
@NeedsTest("If a field is sticky after synchronization, the toggleStickyButton should be activated.")
private fun setToggleStickyButtonListener(toggleStickyButton: ImageButton?, index: Int) {
if (currentFields.getJSONObject(index).getBoolean("sticky"))
if (currentFields.getJSONObject(index).getBoolean("sticky")) {
mToggleStickyText.getOrPut(index) { "" }
}
if (mToggleStickyText[index] == null) {
toggleStickyButton!!.background.alpha = 64
} else {
@ -1684,8 +1690,12 @@ class NoteEditor : AnkiActivity(), DeckSelectionListener, SubtitleListener, Tags
private fun updateToolbar() {
val editorLayout = findViewById<View>(R.id.note_editor_layout)
val bottomMargin =
if (shouldHideToolbar()) 0 else resources.getDimension(R.dimen.note_editor_toolbar_height)
.toInt()
if (shouldHideToolbar()) {
0
} else {
resources.getDimension(R.dimen.note_editor_toolbar_height)
.toInt()
}
val params = editorLayout.layoutParams as MarginLayoutParams
params.bottomMargin = bottomMargin
editorLayout.layoutParams = params
@ -1718,7 +1728,6 @@ class NoteEditor : AnkiActivity(), DeckSelectionListener, SubtitleListener, Tags
}
val buttons = toolbarButtons
for (b in buttons) {
// 0th button shows as '1' and is Ctrl + 1
val visualIndex = b.index + 1
var text = visualIndex.toString()

View File

@ -80,7 +80,7 @@ abstract class Onboarding<Feature>(
DECK_PICKER_ONBOARDING,
REVIEWER_ONBOARDING,
NOTE_EDITOR_ONBOARDING,
CARD_BROWSER_ONBOARDING,
CARD_BROWSER_ONBOARDING
)
)
}

View File

@ -32,6 +32,7 @@ class OnboardingUtils {
* Preference can be toggled by visiting 'Advanced' settings in the app.
*/
const val SHOW_ONBOARDING = "showOnboarding"
@VisibleForTesting
val featureConstants: MutableSet<String> = HashSet()

View File

@ -121,6 +121,7 @@ open class Reviewer :
@get:VisibleForTesting(otherwise = VisibleForTesting.NONE)
var whiteboard: Whiteboard? = null
protected set
// Record Audio
/** File of the temporary mic record */
@get:VisibleForTesting(otherwise = VisibleForTesting.NONE)
@ -150,7 +151,8 @@ open class Reviewer :
val cardCount: Int = result!!.value.result.size
showThemedToast(
this,
resources.getQuantityString(toastResourceId, cardCount, cardCount), true
resources.getQuantityString(toastResourceId, cardCount, cardCount),
true
)
}
}
@ -201,7 +203,9 @@ open class Reviewer :
val shownAsToolbarButton = mActionButtons.findMenuItem(ActionButtons.RES_FLAG)?.isActionButton == true
return if (shownAsToolbarButton && !mPrefFullscreenReview) {
CardMarker.FLAG_NONE
} else actualValue
} else {
actualValue
}
}
override fun createWebView(): WebView {
@ -600,7 +604,8 @@ open class Reviewer :
private fun openOrToggleMicToolbar() {
if (!canRecordAudio(this)) {
ActivityCompat.requestPermissions(
this, arrayOf(Manifest.permission.RECORD_AUDIO),
this,
arrayOf(Manifest.permission.RECORD_AUDIO),
REQUEST_AUDIO_PERMISSION
)
} else {
@ -632,7 +637,8 @@ open class Reviewer :
return
}
val lp2 = FrameLayout.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT
ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.MATCH_PARENT
)
audioView!!.layoutParams = lp2
val micToolBarLayer = findViewById<LinearLayout>(R.id.mic_tool_bar_layer)
@ -924,7 +930,9 @@ open class Reviewer :
override fun onKeyUp(keyCode: Int, event: KeyEvent): Boolean {
return if (mProcessor.onKeyUp(keyCode, event)) {
true
} else super.onKeyUp(keyCode, event)
} else {
super.onKeyUp(keyCode, event)
}
}
private fun <T> setupSubMenu(menu: Menu, @IdRes parentMenu: Int, subMenuProvider: T) where T : ActionProvider?, T : SubMenuProvider? {
@ -1419,20 +1427,27 @@ open class Reviewer :
private fun suspendNoteAvailable(): Boolean {
return if (currentCard == null || isControlBlocked) {
false
} else col.db.queryScalar(
"select 1 from cards where nid = ? and id != ? and queue != " + Consts.QUEUE_TYPE_SUSPENDED + " limit 1",
currentCard!!.nid, currentCard!!.id
) == 1
} else {
col.db.queryScalar(
"select 1 from cards where nid = ? and id != ? and queue != " + Consts.QUEUE_TYPE_SUSPENDED + " limit 1",
currentCard!!.nid,
currentCard!!.id
) == 1
}
// whether there exists a sibling not buried.
}
@KotlinCleanup("mCurrentCard handling")
private fun buryNoteAvailable(): Boolean {
return if (currentCard == null || isControlBlocked) {
false
} else col.db.queryScalar(
"select 1 from cards where nid = ? and id != ? and queue >= " + Consts.QUEUE_TYPE_NEW + " limit 1",
currentCard!!.nid, currentCard!!.id
) == 1
} else {
col.db.queryScalar(
"select 1 from cards where nid = ? and id != ? and queue >= " + Consts.QUEUE_TYPE_NEW + " limit 1",
currentCard!!.nid,
currentCard!!.id
) == 1
}
// Whether there exists a sibling which is neither suspended nor buried
}

View File

@ -186,5 +186,5 @@ data class DownloadFile(
val url: String,
val userAgent: String,
val contentDisposition: String,
val mimeType: String,
val mimeType: String
) : Serializable

View File

@ -140,7 +140,8 @@ class SharedDecksDownloadFragment : Fragment() {
activity?.registerReceiver(mOnComplete, IntentFilter(DownloadManager.ACTION_DOWNLOAD_COMPLETE))
val currentFileName = URLUtil.guessFileName(
fileToBeDownloaded.url, fileToBeDownloaded.contentDisposition,
fileToBeDownloaded.url,
fileToBeDownloaded.contentDisposition,
fileToBeDownloaded.mimeType
)

View File

@ -115,8 +115,12 @@ class Statistics : NavigationDrawerActivity(), DeckSelectionListener, SubtitleLi
else -> Timber.w("Unknown defaultDeck: %s", defaultDeck)
}
mDeckSpinnerSelection = DeckSpinnerSelection(
this, col,
findViewById(R.id.toolbar_spinner), showAllDecks = true, alwaysShowDefault = true, showFilteredDecks = true
this,
col,
findViewById(R.id.toolbar_spinner),
showAllDecks = true,
alwaysShowDefault = true,
showFilteredDecks = true
)
mDeckSpinnerSelection.initializeActionBarDeckSpinner(this.supportActionBar!!)
mDeckSpinnerSelection.selectDeckById(mStatsDeckId, false)
@ -285,7 +289,8 @@ class Statistics : NavigationDrawerActivity(), DeckSelectionListener, SubtitleLi
mTabLayoutMediator.detach()
}
mTabLayoutMediator = TabLayoutMediator(
slidingTabLayout, mActivityPager
slidingTabLayout,
mActivityPager
) { tab: TabLayout.Tab, position: Int -> tab.text = getTabTitle(position) }
mTabLayoutMediator.attach()
}

View File

@ -583,7 +583,6 @@ class StudyOptionsFragment : Fragment(), Toolbar.OnMenuItemClickListener {
private fun rebuildUi(result: DeckStudyData?, refreshDecklist: Boolean) {
dismissProgressDialog()
if (result != null) {
// Don't do anything if the fragment is no longer attached to it's Activity or col has been closed
if (activity == null) {
Timber.e("StudyOptionsFragment.mRefreshFragmentListener :: can't refresh")
@ -718,8 +717,10 @@ class StudyOptionsFragment : Fragment(), Toolbar.OnMenuItemClickListener {
*/
@Suppress("unused")
private const val BROWSE_CARDS = 3
@Suppress("unused")
private const val STATISTICS = 4
@Suppress("unused")
private const val DECK_OPTIONS = 5

View File

@ -47,6 +47,7 @@ object SyncPreferences {
const val CURRENT_SYNC_URI = "currentSyncUri"
const val CUSTOM_SYNC_URI = "syncBaseUrl"
const val CUSTOM_SYNC_ENABLED = CUSTOM_SYNC_URI + VersatileTextWithASwitchPreference.SWITCH_SUFFIX
// Used in the legacy schema path
const val HOSTNUM = "hostNum"
}
@ -106,7 +107,7 @@ fun isLoggedIn() = AnkiDroidApp.getSharedPrefs(AnkiDroidApp.instance).getString(
fun DeckPicker.handleNewSync(
conflict: Connection.ConflictResolution?,
syncMedia: Boolean,
syncMedia: Boolean
) {
val auth = this.syncAuth() ?: return
val deckPicker = this
@ -177,7 +178,7 @@ private fun cancelSync(backend: Backend) {
private suspend fun handleNormalSync(
deckPicker: DeckPicker,
auth: SyncAuth,
syncMedia: Boolean,
syncMedia: Boolean
) {
val output = deckPicker.withProgress(
extractProgress = {
@ -242,7 +243,7 @@ private fun fullDownloadProgress(title: String): ProgressContext.() -> Unit {
private suspend fun handleDownload(
deckPicker: DeckPicker,
auth: SyncAuth,
syncMedia: Boolean,
syncMedia: Boolean
) {
deckPicker.withProgress(
extractProgress = fullDownloadProgress(TR.syncDownloadingFromAnkiweb()),
@ -272,7 +273,7 @@ private suspend fun handleDownload(
private suspend fun handleUpload(
deckPicker: DeckPicker,
auth: SyncAuth,
syncMedia: Boolean,
syncMedia: Boolean
) {
deckPicker.withProgress(
extractProgress = fullDownloadProgress(TR.syncUploadingToAnkiweb()),
@ -325,7 +326,7 @@ private suspend fun handleMediaSync(
},
updateUi = {
dialog.setMessage(text)
},
}
) {
withContext(Dispatchers.IO) {
backend.syncMedia(auth)

View File

@ -172,7 +172,9 @@ class Whiteboard(activity: AnkiActivity, handleMultiTouch: Boolean, inverted: Bo
MotionEvent.ACTION_POINTER_UP -> trySecondFingerClick(event)
else -> false
}
} else false
} else {
false
}
}
/**
@ -521,7 +523,8 @@ class Whiteboard(activity: AnkiActivity, handleMultiTouch: Boolean, inverted: Bo
val whiteboard = Whiteboard(context, handleMultiTouch, currentTheme.isNightMode)
mWhiteboardMultiTouchMethods = whiteboardMultiTouchMethods
val lp2 = FrameLayout.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT
ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.MATCH_PARENT
)
whiteboard.layoutParams = lp2
val fl = context.findViewById<FrameLayout>(R.id.whiteboard)

View File

@ -66,7 +66,9 @@ class AnkiDroidCrashReportDialog : CrashReportDialog(), DialogInterface.OnClickL
override fun buildCustomView(savedInstanceState: Bundle?): View {
val preferences = AnkiDroidApp.getSharedPrefs(this)
val inflater = layoutInflater
@SuppressLint("InflateParams") val rootView = // when you inflate into an alert dialog, you have no parent view
@SuppressLint("InflateParams")
val rootView = // when you inflate into an alert dialog, you have no parent view
inflater.inflate(R.layout.feedback, null)
mAlwaysReportCheckBox = rootView.findViewById(R.id.alwaysReportCheckbox)
mAlwaysReportCheckBox?.isChecked = preferences.getBoolean("autoreportCheckboxValue", true)

View File

@ -39,6 +39,7 @@ import timber.log.Timber
@KotlinCleanup("see if we can make variables lazy, or properties without the `s` prefix")
object UsageAnalytics {
const val ANALYTICS_OPTIN_KEY = "analyticsOptIn"
@KotlinCleanup("lateinit")
private var sAnalytics: GoogleAnalytics? = null
private var sOriginalUncaughtExceptionHandler: Thread.UncaughtExceptionHandler? = null
@ -136,13 +137,9 @@ object UsageAnalytics {
Thread.setDefaultUncaughtExceptionHandler(sOriginalUncaughtExceptionHandler)
sOriginalUncaughtExceptionHandler = null
}
/**
* Determine whether we are disabled or not
*/
/**
* Allow users to enable or disable analytics
*
* @param optIn true allows collection of analytics information
*/
@set:Synchronized
private var optIn: Boolean
@ -174,7 +171,6 @@ object UsageAnalytics {
*/
@Synchronized
fun reInitialize() {
// send any pending async hits, re-chain default exception handlers and re-init
Timber.i("reInitialize()")
sAnalytics!!.flush()
@ -283,7 +279,9 @@ object UsageAnalytics {
// if we're not under the ACRA process then we're fine to initialize a WebView
return if (!ACRA.isACRASenderServiceProcess()) {
true
} else hasSetDataDirectory()
} else {
hasSetDataDirectory()
}
// If we have a custom data directory, then the crash will not occur.
}
@ -313,7 +311,6 @@ object UsageAnalytics {
*/
private class AndroidDefaultRequest : DefaultRequest() {
fun setAndroidRequestParameters(context: Context): DefaultRequest {
// Are we running on really large screens or small screens? Send raw screen size
try {
val size = DisplayUtils.getDisplayDimensions(context)

View File

@ -87,6 +87,7 @@ class CardAppearance(private val customFonts: ReviewerCustomFonts, private val c
// font-weight to 700
return content.replace("font-weight:600;", "font-weight:700;")
}
/**
* hasUserDefinedNightMode finds out if the user has included class .night_mode in card's stylesheet
*/

View File

@ -43,7 +43,7 @@ class CardHtml(
private val getAnswerContentWithoutFrontSide_slow: (() -> String),
@RustCleanup("too many variables, combine once we move away from backend")
private var questionSound: List<SoundOrVideoTag>? = null,
private var answerSound: List<SoundOrVideoTag>? = null,
private var answerSound: List<SoundOrVideoTag>? = null
) {
fun getSoundTags(sideFor: Side): List<SoundOrVideoTag> {
if (sideFor == this.side) {

View File

@ -24,6 +24,7 @@ class CardTemplate(template: String) {
private var mPreClass: String? = null
private var mPreContent: String? = null
private var mPostContent: String? = null
@CheckResult
fun render(content: String, style: String, script: String, cardClass: String): String {
return mPreStyle + style + mPreScript + script + mPreClass + cardClass + mPreContent + content + mPostContent

View File

@ -36,7 +36,7 @@ fun interface GestureListener {
}
enum class Gesture(
@get:JvmName("getResourceId") val resourceId: Int,
@get:JvmName("getResourceId") val resourceId: Int
) {
SWIPE_UP(R.string.gestures_swipe_up),
SWIPE_DOWN(R.string.gestures_swipe_down),
@ -75,6 +75,7 @@ enum class TapGestureMode {
* are ambiguous in a nine-point system and thus not interchangeable
*/
FOUR_POINT,
/**
* Divide the screen into 9 equally sized squares for touch targets.
* Better for tablets

View File

@ -54,8 +54,9 @@ class GestureProcessor(private val processor: ViewerCommand.CommandProcessor?) {
val associatedCommands = HashMap<Gesture, ViewerCommand>()
for (command in ViewerCommand.values()) {
for (mappableBinding in MappableBinding.fromPreference(preferences, command)) {
if (mappableBinding.binding.isGesture)
if (mappableBinding.binding.isGesture) {
associatedCommands[mappableBinding.binding.gesture!!] = command
}
}
}
gestureDoubleTap = associatedCommands[Gesture.DOUBLE_TAP]

View File

@ -46,9 +46,11 @@ class TypeAnswer(
/** What the learner actually typed (externally mutable) */
var input = ""
/** Font face of the 'compare to' field */
var font = ""
private set
/** The font size of the 'compare to' field */
var size = 0
private set

View File

@ -124,19 +124,23 @@ enum class ViewerCommand(val resourceId: Int) {
when (this) {
FLIP_OR_ANSWER_EASE1 -> from(
keyCode(KeyEvent.KEYCODE_BUTTON_Y, CardSide.BOTH),
keyCode(KeyEvent.KEYCODE_1, CardSide.ANSWER), keyCode(KeyEvent.KEYCODE_NUMPAD_1, CardSide.ANSWER)
keyCode(KeyEvent.KEYCODE_1, CardSide.ANSWER),
keyCode(KeyEvent.KEYCODE_NUMPAD_1, CardSide.ANSWER)
)
FLIP_OR_ANSWER_EASE2 -> from(
keyCode(KeyEvent.KEYCODE_BUTTON_X, CardSide.BOTH),
keyCode(KeyEvent.KEYCODE_2, CardSide.ANSWER), keyCode(KeyEvent.KEYCODE_NUMPAD_2, CardSide.ANSWER)
keyCode(KeyEvent.KEYCODE_2, CardSide.ANSWER),
keyCode(KeyEvent.KEYCODE_NUMPAD_2, CardSide.ANSWER)
)
FLIP_OR_ANSWER_EASE3 -> from(
keyCode(KeyEvent.KEYCODE_BUTTON_B, CardSide.BOTH),
keyCode(KeyEvent.KEYCODE_3, CardSide.ANSWER), keyCode(KeyEvent.KEYCODE_NUMPAD_3, CardSide.ANSWER)
keyCode(KeyEvent.KEYCODE_3, CardSide.ANSWER),
keyCode(KeyEvent.KEYCODE_NUMPAD_3, CardSide.ANSWER)
)
FLIP_OR_ANSWER_EASE4 -> from(
keyCode(KeyEvent.KEYCODE_BUTTON_A, CardSide.BOTH),
keyCode(KeyEvent.KEYCODE_4, CardSide.ANSWER), keyCode(KeyEvent.KEYCODE_NUMPAD_4, CardSide.ANSWER)
keyCode(KeyEvent.KEYCODE_4, CardSide.ANSWER),
keyCode(KeyEvent.KEYCODE_NUMPAD_4, CardSide.ANSWER)
)
FLIP_OR_ANSWER_RECOMMENDED -> from(
keyCode(KeyEvent.KEYCODE_DPAD_CENTER, CardSide.BOTH),

View File

@ -172,7 +172,6 @@ class CreateDeckDialog(private val context: Context, private val title: Int, pri
if (deckName.isNotEmpty()) {
when (deckDialogType) {
DeckDialogType.DECK -> {
// create deck
createDeck(deckName)
}
@ -180,12 +179,10 @@ class CreateDeckDialog(private val context: Context, private val title: Int, pri
renameDeck(deckName)
}
DeckDialogType.SUB_DECK -> {
// create sub deck
createSubDeck(parentId!!, deckName)
}
DeckDialogType.FILTERED_DECK -> {
// create filtered deck
createFilteredDeck(deckName)
}

View File

@ -64,7 +64,6 @@ class DatabaseErrorDialog : AsyncDialogFragment() {
}
return when (type) {
DIALOG_LOAD_FAILED -> {
// Collection failed to load; give user the option of either choosing from repair options, or closing
// the activity
dialog.show {
@ -81,7 +80,6 @@ class DatabaseErrorDialog : AsyncDialogFragment() {
}
}
DIALOG_DB_ERROR -> {
// Database Check failed to execute successfully; give user the option of either choosing from repair
// options, submitting an error report, or closing the activity
dialog.show {
@ -103,7 +101,6 @@ class DatabaseErrorDialog : AsyncDialogFragment() {
}
}
DIALOG_ERROR_HANDLING -> {
// The user has asked to see repair options; allow them to choose one of the repair options or go back
// to the previous dialog
val options = ArrayList<String>(6)
@ -176,7 +173,6 @@ class DatabaseErrorDialog : AsyncDialogFragment() {
}
}
DIALOG_REPAIR_COLLECTION -> {
// Allow user to run BackupManager.repairCollection()
dialog.show {
contentNullable(message)
@ -189,7 +185,6 @@ class DatabaseErrorDialog : AsyncDialogFragment() {
}
}
DIALOG_RESTORE_BACKUP -> {
// Allow user to restore one of the backups
val path = CollectionHelper.getCollectionPath(requireContext())
mBackups = BackupManager.getBackups(File(path))
@ -244,7 +239,6 @@ class DatabaseErrorDialog : AsyncDialogFragment() {
dialog
}
DIALOG_NEW_COLLECTION -> {
// Allow user to create a new empty collection
dialog.show {
contentNullable(message)
@ -263,7 +257,6 @@ class DatabaseErrorDialog : AsyncDialogFragment() {
}
}
DIALOG_CONFIRM_DATABASE_CHECK -> {
// Confirmation dialog for database check
dialog.show {
contentNullable(message)
@ -275,7 +268,6 @@ class DatabaseErrorDialog : AsyncDialogFragment() {
}
}
DIALOG_CONFIRM_RESTORE_BACKUP -> {
// Confirmation dialog for backup restore
dialog.show {
contentNullable(message)
@ -287,7 +279,6 @@ class DatabaseErrorDialog : AsyncDialogFragment() {
}
}
DIALOG_FULL_SYNC_FROM_SERVER -> {
// Allow user to do a full-sync from the server
dialog.show {
contentNullable(message)
@ -299,7 +290,6 @@ class DatabaseErrorDialog : AsyncDialogFragment() {
}
}
DIALOG_DB_LOCKED -> {
// If the database is locked, all we can do is ask the user to exit.
dialog.show {
contentNullable(message)
@ -444,8 +434,10 @@ class DatabaseErrorDialog : AsyncDialogFragment() {
/** If the database is at a version higher than what we can currently handle */
const val INCOMPATIBLE_DB_VERSION = 10
/** If the disk space is full **/
const val DIALOG_DISK_FULL = 11
// public flag which lets us distinguish between inaccessible and corrupt database
var databaseCorruptFlag = false

View File

@ -180,7 +180,9 @@ class DeckPickerContextMenu(private val collection: Collection) : AnalyticsDialo
val cls = loadFragmentClass(classLoader, className)
return if (cls == DeckPickerContextMenu::class.java) {
newDeckPickerContextMenu()
} else super.instantiate(classLoader, className)
} else {
super.instantiate(classLoader, className)
}
}
private fun newDeckPickerContextMenu(): DeckPickerContextMenu =

View File

@ -344,11 +344,15 @@ open class DeckSelectionDialog : AnalyticsDialogFragment() {
if (deckId == Stats.ALL_DECKS_ID) {
return if (other.deckId == Stats.ALL_DECKS_ID) {
0
} else -1
} else {
-1
}
}
return if (other.deckId == Stats.ALL_DECKS_ID) {
1
} else DeckNameComparator.INSTANCE.compare(name, other.name)
} else {
DeckNameComparator.INSTANCE.compare(name, other.name)
}
}
companion object {

View File

@ -42,7 +42,7 @@ class ExportCompleteDialog(private val listener: ExportCompleteDialogListener) :
super.onCreate(savedInstanceState)
val options = listOf(
getString(R.string.export_select_save_app),
getString(R.string.export_select_share_app),
getString(R.string.export_select_share_app)
)
return MaterialDialog(requireActivity()).show {
title(text = notificationTitle)

View File

@ -52,18 +52,26 @@ object HelpDialog {
UsageAnalytics.sendAnalyticsEvent(UsageAnalytics.Category.LINK_CLICKED, UsageAnalytics.Actions.OPENED_HELPDIALOG)
val allItems = arrayOf<RecursivePictureMenu.Item>(
ItemHeader(
R.string.help_title_using_ankidroid, R.drawable.ic_manual_black_24dp, UsageAnalytics.Actions.OPENED_USING_ANKIDROID,
R.string.help_title_using_ankidroid,
R.drawable.ic_manual_black_24dp,
UsageAnalytics.Actions.OPENED_USING_ANKIDROID,
FunctionItem(
R.string.help_item_ankidroid_manual, R.drawable.ic_manual_black_24dp, UsageAnalytics.Actions.OPENED_ANKIDROID_MANUAL
R.string.help_item_ankidroid_manual,
R.drawable.ic_manual_black_24dp,
UsageAnalytics.Actions.OPENED_ANKIDROID_MANUAL
) { activity -> openManual(activity) },
LinkItem(R.string.help_item_anki_manual, R.drawable.ic_manual_black_24dp, UsageAnalytics.Actions.OPENED_ANKI_MANUAL, R.string.link_anki_manual),
LinkItem(R.string.help_item_ankidroid_faq, R.drawable.ic_help_black_24dp, UsageAnalytics.Actions.OPENED_ANKIDROID_FAQ, R.string.link_ankidroid_faq)
),
ItemHeader(
R.string.help_title_get_help, R.drawable.ic_help_black_24dp, UsageAnalytics.Actions.OPENED_GET_HELP,
R.string.help_title_get_help,
R.drawable.ic_help_black_24dp,
UsageAnalytics.Actions.OPENED_GET_HELP,
LinkItem(R.string.help_item_mailing_list, R.drawable.ic_email_black_24dp, UsageAnalytics.Actions.OPENED_MAILING_LIST, R.string.link_forum),
FunctionItem(
R.string.help_item_report_bug, R.drawable.ic_bug_report_black_24dp, UsageAnalytics.Actions.OPENED_REPORT_BUG
R.string.help_item_report_bug,
R.drawable.ic_bug_report_black_24dp,
UsageAnalytics.Actions.OPENED_REPORT_BUG
) { activity -> openFeedback(activity) },
exceptionReportItem
),
@ -77,7 +85,9 @@ object HelpDialog {
LinkItem(R.string.help_item_twitter, R.drawable.twitter, UsageAnalytics.Actions.OPENED_TWITTER, R.string.link_twitter)
),
ItemHeader(
R.string.help_title_privacy, R.drawable.ic_baseline_privacy_tip_24, UsageAnalytics.Actions.OPENED_PRIVACY,
R.string.help_title_privacy,
R.drawable.ic_baseline_privacy_tip_24,
UsageAnalytics.Actions.OPENED_PRIVACY,
LinkItem(R.string.help_item_ankidroid_privacy_policy, R.drawable.ic_baseline_policy_24, UsageAnalytics.Actions.OPENED_ANKIDROID_PRIVACY_POLICY, R.string.link_ankidroid_privacy_policy),
LinkItem(R.string.help_item_ankiweb_privacy_policy, R.drawable.ic_baseline_policy_24, UsageAnalytics.Actions.OPENED_ANKIWEB_PRIVACY_POLICY, R.string.link_ankiweb_privacy_policy),
LinkItem(R.string.help_item_ankiweb_terms_and_conditions, R.drawable.ic_baseline_description_24, UsageAnalytics.Actions.OPENED_ANKIWEB_TERMS_AND_CONDITIONS, R.string.link_ankiweb_terms_and_conditions)
@ -96,7 +106,9 @@ object HelpDialog {
rateAppItem,
LinkItem(R.string.help_item_support_other_ankidroid, R.drawable.ic_help_black_24dp, UsageAnalytics.Actions.OPENED_OTHER, R.string.link_contribution),
FunctionItem(
R.string.send_feedback, R.drawable.ic_email_black_24dp, UsageAnalytics.Actions.OPENED_SEND_FEEDBACK
R.string.send_feedback,
R.drawable.ic_email_black_24dp,
UsageAnalytics.Actions.OPENED_SEND_FEEDBACK
) { activity -> openFeedback(activity) }
)
val itemList = ArrayList(listOf(*allItems))
@ -234,7 +246,8 @@ object HelpDialog {
val wasReportSent = CrashReportService.sendReport(activity)
if (!wasReportSent) {
showThemedToast(
activity, activity.getString(R.string.help_dialog_exception_report_debounce),
activity,
activity.getString(R.string.help_dialog_exception_report_debounce),
true
)
}

View File

@ -60,7 +60,7 @@ class ImportFileSelectionFragment {
R.drawable.ic_manual_black_24dp,
UsageAnalytics.Actions.IMPORT_COLPKG_FILE,
OpenFilePicker(DeckPicker.PICK_APKG_FILE)
),
)
)
if (!BackendFactory.defaultLegacySchema) {
val mimes = arrayOf("text/plain", "text/comma-separated-values", "text/csv", "text/tab-separated-values")

View File

@ -31,7 +31,7 @@ object KeySelectionDialogUtils {
KeyEvent.KEYCODE_ALT_RIGHT,
KeyEvent.KEYCODE_META_LEFT,
KeyEvent.KEYCODE_META_RIGHT,
KeyEvent.KEYCODE_FUNCTION,
KeyEvent.KEYCODE_FUNCTION
)
return { !modifierKeyCodes.contains(it) }
}

View File

@ -124,7 +124,9 @@ class MediaCheckDialog : AsyncDialogFragment() {
get() {
return if (requireArguments().getInt("dialogType") == DIALOG_CONFIRM_MEDIA_CHECK) {
resources.getString(R.string.check_media_title)
} else resources.getString(R.string.app_name)
} else {
resources.getString(R.string.app_name)
}
}
override val dialogHandlerMessage: Message

View File

@ -40,5 +40,5 @@ class ModelBrowserContextMenu : AnalyticsDialogFragment() {
enum class ModelBrowserContextMenuAction(val order: Int, @StringRes val actionTextResId: Int) {
Template(0, R.string.model_browser_template),
Rename(1, R.string.model_browser_rename),
Delete(2, R.string.model_browser_delete),
Delete(2, R.string.model_browser_delete)
}

View File

@ -88,17 +88,19 @@ class RescheduleDialog : IntegerDialog() {
)
return if (currentCard.isInDynamicDeck) {
message
} else """
} else {
"""
$message
${
resources.getQuantityString(
R.plurals.reschedule_card_dialog_interval,
currentCard.ivl,
currentCard.ivl
)
resources.getQuantityString(
R.plurals.reschedule_card_dialog_interval,
currentCard.ivl,
currentCard.ivl
)
}
""".trimIndent()
}
""".trimIndent()
}
}
}

View File

@ -168,7 +168,8 @@ fun openUrl(activity: AnkiActivity): OpenUri = activity::openUrl
* @return the dialog
*/
fun AlertDialog.Builder.addScopedStorageLearnMoreLinkAndShow(@Language("HTML") message: String): AlertDialog {
@Language("HTML") val messageWithLink = """$message
@Language("HTML")
val messageWithLink = """$message
<br>
<br><a href='${context.getString(R.string.link_scoped_storage_faq)}'>${context.getString(R.string.scoped_storage_learn_more)}</a>
""".trimIndent().parseAsHtml()

View File

@ -61,8 +61,10 @@ class SimpleMessageDialog : AsyncDialogFragment() {
companion object {
/** The title of the notification/dialog */
private const val ARGS_TITLE = "title"
/** The content of the notification/dialog */
private const val ARGS_MESSAGE = "message"
/**
* If the calling activity should be reloaded when 'OK' is pressed.
* @see SimpleMessageDialogListener.dismissSimpleMessageDialog

View File

@ -48,7 +48,6 @@ class SyncErrorDialog : AsyncDialogFragment() {
.cancelable(true)
return when (requireArguments().getInt("dialogType")) {
DIALOG_USER_NOT_LOGGED_IN_SYNC -> {
// User not logged in; take them to login screen
dialog.show {
iconAttr(R.attr.dialogSyncErrorIcon)
@ -59,7 +58,6 @@ class SyncErrorDialog : AsyncDialogFragment() {
}
}
DIALOG_CONNECTION_ERROR -> {
// Connection error; allow user to retry or cancel
dialog.show {
iconAttr(R.attr.dialogSyncErrorIcon)
@ -73,7 +71,6 @@ class SyncErrorDialog : AsyncDialogFragment() {
}
}
DIALOG_SYNC_CONFLICT_RESOLUTION -> {
// Sync conflict; allow user to cancel, or choose between local and remote versions
dialog.show {
iconAttr(R.attr.dialogSyncErrorIcon)
@ -91,7 +88,6 @@ class SyncErrorDialog : AsyncDialogFragment() {
}
}
DIALOG_SYNC_CONFLICT_CONFIRM_KEEP_LOCAL -> {
// Confirmation before pushing local collection to server after sync conflict
dialog.show {
iconAttr(R.attr.dialogSyncErrorIcon)
@ -104,7 +100,6 @@ class SyncErrorDialog : AsyncDialogFragment() {
}
}
DIALOG_SYNC_CONFLICT_CONFIRM_KEEP_REMOTE -> {
// Confirmation before overwriting local collection with server collection after sync conflict
dialog.show {
iconAttr(R.attr.dialogSyncErrorIcon)
@ -117,7 +112,6 @@ class SyncErrorDialog : AsyncDialogFragment() {
}
}
DIALOG_SYNC_SANITY_ERROR -> {
// Sync sanity check error; allow user to cancel, or choose between local and remote versions
dialog.show {
positiveButton(R.string.sync_sanity_local) {
@ -132,7 +126,6 @@ class SyncErrorDialog : AsyncDialogFragment() {
}
}
DIALOG_SYNC_SANITY_ERROR_CONFIRM_KEEP_LOCAL -> {
// Confirmation before pushing local collection to server after sanity check error
dialog.show {
positiveButton(R.string.dialog_positive_replace) {
@ -143,7 +136,6 @@ class SyncErrorDialog : AsyncDialogFragment() {
}
}
DIALOG_SYNC_SANITY_ERROR_CONFIRM_KEEP_REMOTE -> {
// Confirmation before overwriting local collection with server collection after sanity check error
dialog.show {
positiveButton(R.string.dialog_positive_replace) {
@ -201,7 +193,9 @@ class SyncErrorDialog : AsyncDialogFragment() {
get() {
return if (requireArguments().getInt("dialogType") == DIALOG_USER_NOT_LOGGED_IN_SYNC) {
resources.getString(R.string.sync_error)
} else title
} else {
title
}
}
private val message: String?
@ -229,7 +223,9 @@ class SyncErrorDialog : AsyncDialogFragment() {
get() {
return if (requireArguments().getInt("dialogType") == DIALOG_USER_NOT_LOGGED_IN_SYNC) {
resources.getString(R.string.not_logged_in_title)
} else message
} else {
message
}
}
override val dialogHandlerMessage: Message

View File

@ -130,7 +130,6 @@ class CustomStudyDialog(private val collection: Collection, private val customSt
customStudyListener?.showDialogFragment(d)
}
STUDY_TAGS -> {
/*
* This is a special Dialog for CUSTOM STUDY, where instead of only collecting a
* number, it is necessary to collect a list of tags. This case handles the creation
@ -139,13 +138,13 @@ class CustomStudyDialog(private val collection: Collection, private val customSt
val currentDeck = requireArguments().getLong("did")
val dialogFragment = TagsDialog().withArguments(
TagsDialog.DialogType.CUSTOM_STUDY_TAGS, ArrayList(),
TagsDialog.DialogType.CUSTOM_STUDY_TAGS,
ArrayList(),
ArrayList(collection.tags.byDeck(currentDeck, true))
)
customStudyListener?.showDialogFragment(dialogFragment)
}
else -> {
// User asked for a standard custom study option
val d = CustomStudyDialog(collection, customStudyListener)
.withArguments(
@ -179,7 +178,8 @@ class CustomStudyDialog(private val collection: Collection, private val customSt
*/
// Input dialogs
// Show input dialog for an individual custom study dialog
@SuppressLint("InflateParams") val v = requireActivity().layoutInflater.inflate(R.layout.styled_custom_study_details_dialog, null)
@SuppressLint("InflateParams")
val v = requireActivity().layoutInflater.inflate(R.layout.styled_custom_study_details_dialog, null)
val textView1 = v.findViewById<TextView>(R.id.custom_study_details_text1)
val textView2 = v.findViewById<TextView>(R.id.custom_study_details_text2)
val editText = v.findViewById<EditText>(R.id.custom_study_details_edittext2)
@ -234,9 +234,11 @@ class CustomStudyDialog(private val collection: Collection, private val customSt
arrayOf(
String.format(
Locale.US,
"rated:%d:1", n
"rated:%d:1",
n
),
Consts.DYN_MAX_SIZE, Consts.DYN_RANDOM
Consts.DYN_MAX_SIZE,
Consts.DYN_RANDOM
),
false
)
@ -247,9 +249,11 @@ class CustomStudyDialog(private val collection: Collection, private val customSt
arrayOf(
String.format(
Locale.US,
"prop:due<=%d", n
"prop:due<=%d",
n
),
Consts.DYN_MAX_SIZE, Consts.DYN_DUE
Consts.DYN_MAX_SIZE,
Consts.DYN_DUE
),
true
)
@ -263,7 +267,8 @@ class CustomStudyDialog(private val collection: Collection, private val customSt
arrayOf(
"is:new added:" +
n,
Consts.DYN_MAX_SIZE, Consts.DYN_OLDEST
Consts.DYN_MAX_SIZE,
Consts.DYN_OLDEST
),
false
)
@ -333,7 +338,8 @@ class CustomStudyDialog(private val collection: Collection, private val customSt
JSONArray(),
arrayOf(
sb.toString(),
Consts.DYN_MAX_SIZE, Consts.DYN_RANDOM
Consts.DYN_MAX_SIZE,
Consts.DYN_RANDOM
),
true
)
@ -375,8 +381,12 @@ class CustomStudyDialog(private val collection: Collection, private val customSt
}
EMPTY_SCHEDULE -> // Special custom study options to show when extending the daily study limits is not applicable
return listOf(
STUDY_FORGOT, STUDY_AHEAD, STUDY_RANDOM,
STUDY_PREVIEW, STUDY_TAGS, DECK_OPTIONS
STUDY_FORGOT,
STUDY_AHEAD,
STUDY_RANDOM,
STUDY_PREVIEW,
STUDY_TAGS,
DECK_OPTIONS
)
}
}
@ -501,6 +511,7 @@ class CustomStudyDialog(private val collection: Collection, private val customSt
*/
interface ContextMenuAttribute<T> where T : Enum<*> {
val value: Int
@get:StringRes val stringResource: Int?
}

View File

@ -26,7 +26,9 @@ class CustomStudyDialogFactory(val collectionSupplier: Supplier<Collection>, pri
val cls = loadFragmentClass(classLoader, className)
return if (cls == CustomStudyDialog::class.java) {
newCustomStudyDialog()
} else super.instantiate(classLoader, className)
} else {
super.instantiate(classLoader, className)
}
}
fun newCustomStudyDialog(): CustomStudyDialog {

View File

@ -38,6 +38,7 @@ class TagsArrayAdapter(private val tags: TagsList, private val resources: Resour
internal lateinit var node: TagTreeNode
internal val mExpandButton: ImageButton = itemView.findViewById(R.id.id_expand_button)
internal val mCheckBoxView: CheckBoxTriStates = itemView.findViewById(R.id.tags_dialog_tag_item_checkbox)
// TextView contains the displayed tag (only the last part), while the full tag is stored in TagTreeNode
internal val mTextView: TextView = itemView.findViewById(R.id.tags_dialog_tag_item_text)

View File

@ -136,7 +136,8 @@ class TagsDialog : AnalyticsDialogFragment {
"filled as prefix properly. In other dialog types, long-clicking a tag behaves like a short click."
)
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
@SuppressLint("InflateParams") val tagsDialogView = LayoutInflater.from(activity).inflate(R.layout.tags_dialog, null, false)
@SuppressLint("InflateParams")
val tagsDialogView = LayoutInflater.from(activity).inflate(R.layout.tags_dialog, null, false)
mTagsListRecyclerView = tagsDialogView.findViewById(R.id.tags_dialog_tags_list)
val tagsListRecyclerView: RecyclerView? = mTagsListRecyclerView
tagsListRecyclerView?.requestFocus()
@ -174,7 +175,8 @@ class TagsDialog : AnalyticsDialogFragment {
.positiveButton(text = mPositiveText!!) {
tagsDialogListener.onSelectedTags(
mTags!!.copyOfCheckedTagList(),
mTags!!.copyOfIndeterminateTagList(), mSelectedOption
mTags!!.copyOfIndeterminateTagList(),
mSelectedOption
)
}
.negativeButton(R.string.dialog_cancel)

View File

@ -23,7 +23,9 @@ class TagsDialogFactory(val listener: TagsDialogListener) : ExtendedFragmentFact
val cls = loadFragmentClass(classLoader, className)
return if (cls == TagsDialog::class.java) {
newTagsDialog()
} else super.instantiate(classLoader, className)
} else {
super.instantiate(classLoader, className)
}
}
fun newTagsDialog(): TagsDialog {

View File

@ -37,7 +37,8 @@ interface TagsDialogListener {
fun onSelectedTags(selectedTags: List<String>, indeterminateTags: List<String>, option: Int)
fun <F> F.registerFragmentResultReceiver() where F : Fragment, F : TagsDialogListener {
parentFragmentManager.setFragmentResultListener(
ON_SELECTED_TAGS_KEY, this,
ON_SELECTED_TAGS_KEY,
this,
FragmentResultListener { _: String?, bundle: Bundle ->
val selectedTags: List<String> = bundle.getStringArrayList(ON_SELECTED_TAGS__SELECTED_TAGS)!!
val indeterminateTags: List<String> = bundle.getStringArrayList(ON_SELECTED_TAGS__INDETERMINATE_TAGS)!!

View File

@ -129,6 +129,7 @@ class TagsList constructor(
addAncestors(tag)
return true
}
/**
* Mark a tag as checked tag.
* Optionally mark ancestors as indeterminate

View File

@ -202,7 +202,7 @@ class ActivityExportingDelegate(private val activity: AnkiActivity, private val
activity.getString(
R.string.export_email_text,
activity.getString(R.string.link_manual),
activity.getString(R.string.link_distributions),
activity.getString(R.string.link_distributions)
)
)
.intent.apply {
@ -311,13 +311,15 @@ class ActivityExportingDelegate(private val activity: AnkiActivity, private val
if (::mExportFileName.isInitialized && !mExportFileName.endsWith(".colpkg")) return
AnkiDroidApp.getSharedPrefs(activity).edit {
putLong(
LAST_SUCCESSFUL_EXPORT_AT_SECOND_KEY, TimeManager.time.intTime()
LAST_SUCCESSFUL_EXPORT_AT_SECOND_KEY,
TimeManager.time.intTime()
)
}
val col = collectionSupplier.get()
AnkiDroidApp.getSharedPrefs(activity).edit {
putLong(
LAST_SUCCESSFUL_EXPORT_AT_MOD_KEY, col.mod
LAST_SUCCESSFUL_EXPORT_AT_MOD_KEY,
col.mod
)
}
}

View File

@ -34,7 +34,9 @@ internal class ExportDialogsFactory(
}
return if (cls == ExportCompleteDialog::class.java) {
newExportCompleteDialog()
} else super.instantiate(classLoader, className)
} else {
super.instantiate(classLoader, className)
}
}
fun newExportDialog(): ExportDialog {

View File

@ -81,6 +81,7 @@ class SetupCollectionFragment : Fragment() {
enum class CollectionSetupOption {
/** Continues to the DeckPicker with a new collection */
DeckPickerWithNewCollection,
/** Syncs an existing profile from AnkiWeb */
SyncFromExistingAccount
}

View File

@ -125,7 +125,6 @@ class TgzPackageExtract(private val context: Context) {
*/
@Throws(Exception::class)
fun extractTarGzipToAddonFolder(tarballFile: File, addonsPackageDir: AddonsPackageDir) {
require(isGzip(tarballFile)) { context.getString(R.string.not_valid_js_addon, tarballFile.absolutePath) }
try {
@ -285,7 +284,6 @@ class TgzPackageExtract(private val context: Context) {
while (total + BUFFER <= TOO_BIG_SIZE &&
tarInputStream.read(data, 0, BUFFER).also { count = it } != -1
) {
bufferOutput.write(data, 0, count)
total += count

View File

@ -57,6 +57,7 @@ class Directory private constructor(val directory: File) {
* Otherwise returns `null`.
*/
fun createInstance(path: String): Directory? = createInstance(File(path))
/**
* Returns a [Directory] from [file] if `Directory` precondition holds; i.e. [file] is an existing directory.
* Otherwise returns `null`.
@ -67,6 +68,7 @@ class Directory private constructor(val directory: File) {
}
return Directory(file)
}
/** Creates an instance. Only call it if [Directory] preconditions are known to be true */
fun createInstanceUnsafe(file: File) = Directory(file)
}

View File

@ -30,6 +30,7 @@ import java.io.File
class DiskFile private constructor(val file: File) {
/** @see [File.renameTo] */
fun renameTo(destination: File): Boolean = file.renameTo(destination)
/** @see [FileUtils.contentEquals] */
fun contentEquals(f2: File): Boolean = FileUtils.contentEquals(file, f2)
override fun toString(): String = file.canonicalPath

View File

@ -352,7 +352,8 @@ class AudioView private constructor(context: Context, resPlay: Int, resPause: In
CrashReportService.sendExceptionReport(e, "Unable to create recorder tool bar")
showThemedToast(
context,
context.getText(R.string.multimedia_editor_audio_view_create_failed).toString(), true
context.getText(R.string.multimedia_editor_audio_view_create_failed).toString(),
true
)
null
}

View File

@ -53,8 +53,9 @@ class MediaPlayer :
override fun setDataSource(context: Context, uri: Uri) {
super.setDataSource(context, uri)
if (state == IDLE)
if (state == IDLE) {
state = INITIALIZED
}
}
override fun setDataSource(
@ -64,44 +65,51 @@ class MediaPlayer :
cookies: MutableList<HttpCookie>?
) {
super.setDataSource(context, uri, headers, cookies)
if (state == IDLE)
if (state == IDLE) {
state = INITIALIZED
}
}
override fun setDataSource(context: Context, uri: Uri, headers: MutableMap<String, String>?) {
super.setDataSource(context, uri, headers)
if (state == IDLE)
if (state == IDLE) {
state = INITIALIZED
}
}
override fun setDataSource(path: String?) {
super.setDataSource(path)
if (state == IDLE)
if (state == IDLE) {
state = INITIALIZED
}
}
override fun setDataSource(afd: AssetFileDescriptor) {
super.setDataSource(afd)
if (state == IDLE)
if (state == IDLE) {
state = INITIALIZED
}
}
override fun setDataSource(fd: FileDescriptor?) {
super.setDataSource(fd)
if (state == IDLE)
if (state == IDLE) {
state = INITIALIZED
}
}
override fun setDataSource(fd: FileDescriptor?, offset: Long, length: Long) {
super.setDataSource(fd, offset, length)
if (state == IDLE)
if (state == IDLE) {
state = INITIALIZED
}
}
override fun setDataSource(dataSource: MediaDataSource?) {
super.setDataSource(dataSource)
if (state == IDLE)
if (state == IDLE) {
state = INITIALIZED
}
}
override fun reset() {

View File

@ -103,7 +103,8 @@ open class LoadPronunciationActivity : AnkiActivity(), DialogInterface.OnCancelL
mLanguageLister = LanguageListerBeolingus()
mSpinnerFrom = Spinner(this)
val adapter = ArrayAdapter(
this, android.R.layout.simple_spinner_item,
this,
android.R.layout.simple_spinner_item,
mLanguageLister.languages
)
adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item)

View File

@ -102,7 +102,8 @@ class MultimediaEditFieldActivity : AnkiActivity(), OnRequestPermissionsResultCa
if (field is AudioRecordingField && !Permissions.canRecordAudio(this)) {
Timber.d("Requesting Audio Permissions")
ActivityCompat.requestPermissions(
this, arrayOf(Manifest.permission.RECORD_AUDIO),
this,
arrayOf(Manifest.permission.RECORD_AUDIO),
REQUEST_AUDIO_PERMISSION
)
return true
@ -114,7 +115,8 @@ class MultimediaEditFieldActivity : AnkiActivity(), OnRequestPermissionsResultCa
) {
Timber.d("Requesting Camera Permissions")
ActivityCompat.requestPermissions(
this, arrayOf(Manifest.permission.CAMERA),
this,
arrayOf(Manifest.permission.CAMERA),
REQUEST_CAMERA_PERMISSION
)
return true

View File

@ -44,7 +44,8 @@ class PickStringDialogFragment : DialogFragment() {
builder.setTitle(mTitle)
val adapter = ArrayAdapter(
requireActivity(),
R.layout.simple_list_item_1, mPossibleChoices!!
R.layout.simple_list_item_1,
mPossibleChoices!!
)
builder.setAdapter(adapter, mListener)
return builder.create()

View File

@ -96,6 +96,7 @@ class BasicImageFieldController : FieldControllerBase(), IFieldController {
return min(height * 0.4, width * 0.6).toInt()
}
private lateinit var cropImageRequest: ActivityResultLauncher<CropImageContractOptions>
@VisibleForTesting
lateinit var registryToUse: ActivityResultRegistry

View File

@ -118,7 +118,8 @@ class BasicMediaClipFieldController : FieldControllerBase(), IFieldController {
if (cursor == null) {
showThemedToast(
AnkiDroidApp.instance.applicationContext,
AnkiDroidApp.instance.getString(R.string.multimedia_editor_something_wrong), true
AnkiDroidApp.instance.getString(R.string.multimedia_editor_something_wrong),
true
)
return
}
@ -137,7 +138,8 @@ class BasicMediaClipFieldController : FieldControllerBase(), IFieldController {
CrashReportService.sendExceptionReport(e, "Media Clip addition failed. Name " + mediaClipFullName + " / cursor mime type column type " + cursor.getType(2))
showThemedToast(
AnkiDroidApp.instance.applicationContext,
AnkiDroidApp.instance.getString(R.string.multimedia_editor_something_wrong), true
AnkiDroidApp.instance.getString(R.string.multimedia_editor_something_wrong),
true
)
return
}
@ -154,7 +156,8 @@ class BasicMediaClipFieldController : FieldControllerBase(), IFieldController {
CrashReportService.sendExceptionReport(e, "handleMediaSelection:tempFile")
showThemedToast(
AnkiDroidApp.instance.applicationContext,
AnkiDroidApp.instance.getString(R.string.multimedia_editor_something_wrong), true
AnkiDroidApp.instance.getString(R.string.multimedia_editor_something_wrong),
true
)
return
}
@ -175,7 +178,8 @@ class BasicMediaClipFieldController : FieldControllerBase(), IFieldController {
CrashReportService.sendExceptionReport(e, "handleMediaSelection:copyFromProvider")
showThemedToast(
AnkiDroidApp.instance.applicationContext,
AnkiDroidApp.instance.getString(R.string.multimedia_editor_something_wrong), true
AnkiDroidApp.instance.getString(R.string.multimedia_editor_something_wrong),
true
)
}
}

View File

@ -72,6 +72,7 @@ class ImageField : FieldBase(), IField {
companion object {
private const val serialVersionUID = 4431611060655809687L
@VisibleForTesting
fun formatImageFileName(file: File): String {
return if (file.exists()) {

View File

@ -66,7 +66,9 @@ class MultimediaEditableNote : IMultimediaEditableNote {
override fun getField(index: Int): IField? {
return if (index in 0 until numberOfFields) {
fieldsPrivate[index]
} else null
} else {
null
}
}
override fun setField(index: Int, field: IField?): Boolean {

View File

@ -41,7 +41,9 @@ open class LanguageListerBase {
fun getCodeFor(Language: String): String? {
return if (mLanguageMap.containsKey(Language)) {
mLanguageMap[Language]
} else null
} else {
null
}
}
val languages: ArrayList<String>

View File

@ -64,6 +64,7 @@ class Toolbar : FrameLayout {
var formatListener: TextFormatListener? = null
private val mToolbar: LinearLayout
private val mToolbarLayout: LinearLayout
/** A list of buttons, typically user-defined which modify text + selection */
private val mCustomButtons: MutableList<View> = ArrayList()
private val mRows: MutableList<LinearLayout> = ArrayList()

View File

@ -59,13 +59,13 @@ class ManageNotetypes : AnkiActivity() {
launchForChanges<ModelFieldEditor>(
mapOf(
"title" to it.name,
"noteTypeID" to it.id,
"noteTypeID" to it.id
)
)
},
onEditCards = { launchForChanges<CardTemplateEditor>(mapOf("modelId" to it.id)) },
onRename = ::renameNotetype,
onDelete = ::deleteNotetype,
onDelete = ::deleteNotetype
)
}
private val outsideChangesLauncher =
@ -227,7 +227,7 @@ class ManageNotetypes : AnkiActivity() {
optionsToDisplay.map {
String.format(
if (it.isStandard) addPrefixStr else clonePrefixStr,
it.name,
it.name
)
}
).apply {

View File

@ -46,7 +46,7 @@ internal class NotetypesAdapter(
private val onShowFields: (NoteTypeUiModel) -> Unit,
private val onEditCards: (NoteTypeUiModel) -> Unit,
private val onRename: (NoteTypeUiModel) -> Unit,
private val onDelete: (NoteTypeUiModel) -> Unit,
private val onDelete: (NoteTypeUiModel) -> Unit
) : ListAdapter<NoteTypeUiModel, NotetypeViewHolder>(notetypeNamesAndCountDiff) {
private val layoutInflater = LayoutInflater.from(context)
@ -56,7 +56,7 @@ internal class NotetypesAdapter(
onDelete = onDelete,
onRename = onRename,
onEditCards = onEditCards,
onShowFields = onShowFields,
onShowFields = onShowFields
)
}
@ -70,7 +70,7 @@ internal class NotetypeViewHolder(
onShowFields: (NoteTypeUiModel) -> Unit,
onEditCards: (NoteTypeUiModel) -> Unit,
onRename: (NoteTypeUiModel) -> Unit,
onDelete: (NoteTypeUiModel) -> Unit,
onDelete: (NoteTypeUiModel) -> Unit
) : RecyclerView.ViewHolder(rowView) {
val name: TextView = rowView.findViewById(R.id.note_name)
val useCount: TextView = rowView.findViewById(R.id.note_use_count)

View File

@ -25,7 +25,7 @@ import anki.notetypes.NotetypeNameIdUseCount
internal data class NoteTypeUiModel(
val id: Long,
val name: String,
val useCount: Int,
val useCount: Int
)
internal fun NotetypeNameIdUseCount.toUiModel(): NoteTypeUiModel =

View File

@ -84,6 +84,7 @@ class PagesActivity : AnkiActivity() {
* as arguments of the [PageFragment] that will be opened
*/
const val EXTRA_PAGE_ARGS = "pageArgs"
/**
* Extra key of [PagesActivity]'s intent that must be included and
* hold the name of an [Anki HTML page](https://github.com/ankitects/anki/tree/main/ts)

View File

@ -31,7 +31,7 @@ class CustomSyncServerSettingsFragment : SettingsFragment() {
override fun initSubscreen() {
listOf(
R.string.custom_sync_server_collection_url_key,
R.string.custom_sync_server_collection_url_key
).forEach {
requirePreference<VersatileTextPreference>(it).continuousValidator =
VersatileTextPreference.Validator { value ->

View File

@ -53,8 +53,10 @@ class NotificationsSettingsFragment : SettingsFragment() {
scheduleNotification(TimeManager.time, requireContext())
} else {
val intent = CompatHelper.compat.getImmutableBroadcastIntent(
requireContext(), 0,
Intent(requireContext(), NotificationService::class.java), 0
requireContext(),
0,
Intent(requireContext(), NotificationService::class.java),
0
)
val alarmManager = requireActivity().getSystemService(ALARM_SERVICE) as AlarmManager
alarmManager.cancel(intent)

View File

@ -123,7 +123,8 @@ class Preferences :
pref: Preference
): Boolean {
val fragment = supportFragmentManager.fragmentFactory.instantiate(
classLoader, pref.fragment!!
classLoader,
pref.fragment!!
)
fragment.arguments = pref.extras
supportFragmentManager.commit {

Some files were not shown because too many files have changed in this diff Show More