Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
43 commits
Select commit Hold shift + click to select a range
8e25033
feat(note-editor): add preview sidebar toggle
sanjaysargam Sep 16, 2026
bbda6a5
fix(import): handle requests to close the error page
david-allison Sep 22, 2026
c06fa7b
fix(tests): ActivityLaunchLoggerTest - stop app launch logging
david-allison Sep 27, 2026
c5b0c04
test: report captured activity launch logs on failure
david-allison Sep 27, 2026
7df3420
Updated strings from Crowdin
Sep 27, 2026
0754265
build: add `versionCode` bump validation
david-allison Sep 26, 2026
2531237
build(release): validate `versionCode` before publishing
david-allison Sep 26, 2026
462c278
ci: enforce `versionCode` bump limits
david-allison Sep 26, 2026
544446e
fix(ci): reconnect log capture if ADB disconnects
david-allison Sep 26, 2026
69374b1
build: enable buildSrc tests in CI
david-allison Sep 26, 2026
de4a16b
ci: target merged PRs at 2.26 milestone
mikehardy Sep 27, 2026
332104e
release: prepare for 2.26.0 alpha series
mikehardy Sep 27, 2026
979fb1a
fix: Commit issues
devchaos0 Sep 22, 2026
9982f6f
docs: update upstream reference to GitHub permalink
devchaos0 Sep 25, 2026
02a804b
docs: update upstream reference to HEAD permalink
devchaos0 Sep 25, 2026
745d967
fix(controls): improve post-assignment dialog
Sanjeev-Narang Aug 20, 2026
17177c5
feat(common): add runCatchingWithLog
criticalAY Aug 16, 2026
8e0e45c
feat(utils): add File.containsFile
criticalAY Aug 16, 2026
9b56768
feat(preferences): add a directory picker
criticalAY Aug 16, 2026
d2d0a1d
feat(settings): use the new directory picker
criticalAY Aug 16, 2026
299d65e
test(preferences): cover the directory picker list
criticalAY Aug 16, 2026
9394fb0
feat(deckpicker): improve clock error message
sanjaysargam Sep 20, 2026
999fcd6
test(reviewer): check each shake opens a dialog
david-allison Sep 27, 2026
c010ecf
fix(image-occlusion): send IDs as strings
david-allison Sep 27, 2026
62cf582
fix(image-occlusion): two calls to super.onPageFinished()
david-allison Sep 27, 2026
c7e4518
test(note-editor): test share & deck defaults
david-allison Sep 20, 2026
da35f25
fix(note-editor): retain deck if note type has associated deck
david-allison Sep 20, 2026
9244939
fix(change-notetype): edge to edge
criticalAY Sep 26, 2026
138ddbf
test(change-notetype): screenshot tests
criticalAY Sep 27, 2026
6b6e1f5
fix(insert-field): edge to edge
criticalAY Sep 27, 2026
79d05d0
test(insert-field): screenshot tests
criticalAY Sep 27, 2026
6b8035e
test(reviewer): MathJax asset URLs
david-allison Sep 22, 2026
0db09ba
fix(reviewer): handle Anki 26.09 '/_anki/js/mathjax.js'
david-allison Sep 22, 2026
0bf29a5
refactor: add `Prefs.useSoftwareRendering`
david-allison Sep 27, 2026
380582d
feat(study-screen): handle 'Disable card hardware render'
david-allison Sep 27, 2026
f1b7173
Updated strings from Crowdin
Sep 28, 2026
f0d946d
fix(ci): avoid duplicate scheduled test iterations
david-allison Sep 28, 2026
ea5b8bb
docs: convert licenses to SPDX format
david-allison Sep 28, 2026
2865c46
refactor: add CommonString and CommonPlurals resource aliases
david-allison Aug 30, 2026
ac2fd53
refactor: use CommonString and CommonPlurals for CrowdIn strings
david-allison Aug 30, 2026
02c7cf2
fix(widgets): define RTL support
david-allison Aug 30, 2026
7a91a4a
feat(bottom-nav): report tab screen views
criticalAY Sep 15, 2026
0c3be71
build(deps): bump undici from 6.28.0 to 6.29.0 in /tools/localization
dependabot[bot] Sep 29, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
41 changes: 41 additions & 0 deletions .github/with_emulator_log.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
#!/bin/bash
# SPDX-License-Identifier: GPL-3.0-or-later

set -euo pipefail

log_file=$1
shift

# Fail before starting the test command if we cannot capture its logs.
: >> "$log_file"

stop_process() {
kill "$1" 2>/dev/null || true
wait "$1" 2>/dev/null || true
}

capture_logcat() {
local child_pid=
trap 'exit 0' INT TERM
trap 'stop_process "$child_pid"' EXIT

# ADB can disconnect after boot, ending logcat before the tests start (Issue 22115).
# Reconnect even on a clean exit. Replaying the buffer also recovers events emitted
# while disconnected; the test reporter deduplicates ignored/failed events.
while true; do
# Background both commands so TERM interrupts wait, including during the delay.
adb logcat '*:D' >> "$log_file" &
child_pid=$!
wait "$child_pid" || true
echo 'Emulator logcat exited; reconnecting in 1 second.' >&2
sleep 1 &
child_pid=$!
wait "$child_pid" || true
done
}

capture_logcat &
capture_pid=$!
# Stop both the retry loop and its child before returning the test command's status.
trap 'stop_process "$capture_pid"' EXIT
"$@"
13 changes: 7 additions & 6 deletions .github/workflows/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,14 @@ following scripts before submitting a PR.

Alternately, you may run the actions on your fork of `Anki-Android`.

| **Job** | **Command** | **Comments** |
|------------------------------------------------------------------------------------------------------------|----------------------------------------------------------------------|-----------------------------------------------------------|
| [Lint (Kotlin)](https://github.com/ankidroid/Anki-Android/blob/main/.github/workflows/lint.yml) | `./gradlew lintAll ktLintCheck lint-rules:test --daemon` | Android lint rules, formatting and tests for lint rules |
| [Lint (JavaScript)](https://github.com/ankidroid/Anki-Android/blob/main/.github/workflows/lint.yml) | See script | Prettier, lint & code formatting |
| [Unit Tests](https://github.com/ankidroid/Anki-Android/blob/main/.github/workflows/tests_unit.yml) | `./gradlew jacocoUnitTestReport --daemon` | Unit tests for the Android Project |
| **Job** | **Command** | **Comments** |
|------------------------------------------------------------------------------------------------------------|----------------------------------------------------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------|
| [Lint (Kotlin)](https://github.com/ankidroid/Anki-Android/blob/main/.github/workflows/lint.yml) | `./gradlew lintAll ktLintCheck lint-rules:test --daemon` | Android lint rules, formatting and tests for lint rules |
| [Lint (JavaScript)](https://github.com/ankidroid/Anki-Android/blob/main/.github/workflows/lint.yml) | See script | Prettier, lint & code formatting |
| [Build Logic](https://github.com/ankidroid/Anki-Android/blob/main/.github/workflows/lint.yml) | `./gradlew -p buildSrc test` and `tools/validate-version-code.sh` | Version-code bump checks |
| [Unit Tests](https://github.com/ankidroid/Anki-Android/blob/main/.github/workflows/tests_unit.yml) | `./gradlew jacocoUnitTestReport --daemon` | Unit tests for the Android Project |
| [Emulator Tests](https://github.com/ankidroid/Anki-Android/blob/main/.github/workflows/tests_emulator.yml) | `TEST_RELEASE_BUILD=true ./gradlew jacocoAndroidTestReport --daemon` | Emulator tests for the Android Project.<br/>CI runs `bash .github/disable_art_metrics.sh` and `adb shell settings put global hide_error_dialogs 1` first |
| [CodeQL](https://github.com/ankidroid/Anki-Android/blob/main/.github/workflows/codeql.yml) | N/A | GitHub-only check.<br/>[Docs](https://codeql.github.com/) |
| [CodeQL](https://github.com/ankidroid/Anki-Android/blob/main/.github/workflows/codeql.yml) | N/A | GitHub-only check.<br/>[Docs](https://codeql.github.com/) |

## Other Workflows

Expand Down
11 changes: 10 additions & 1 deletion .github/workflows/lint.yml
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ jobs:
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
fetch-depth: 1
fetch-depth: 2

- name: Configure JDK
uses: actions/setup-java@dd06d9cba3e5552c54d9f8ea23572deb30010f7c # v6.0.0
Expand All @@ -47,6 +47,15 @@ jobs:
# Comment this and the with: above out for performance testing on a branch
cache-read-only: ${{ github.ref != 'refs/heads/main' }}

- name: Test build logic
run: ./gradlew -p buildSrc test --daemon

# Reject versionCode decreases and increases over 100,000.
- name: Validate versionCode
env:
VERSION_CODE_BASE: ${{ github.event.pull_request.base.sha || github.event.merge_group.base_sha || github.event.before }}
run: tools/validate-version-code.sh "$VERSION_CODE_BASE"

- name: Warm Gradle Cache
# This makes sure we fetch gradle network resources with a retry
uses: nick-fields/retry@ad984534de44a9489a53aefd81eb77f87c70dc60 # v4.0.0
Expand Down
4 changes: 2 additions & 2 deletions .github/workflows/milestone.yml
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ jobs:
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: ${{ github.event.pull_request.number }},
milestone: 77
milestone: 79
});

// graphql query to get referenced issues to merged pull request
Expand Down Expand Up @@ -51,7 +51,7 @@ jobs:
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: issue.number,
milestone: 77
milestone: 79
});
}

2 changes: 2 additions & 0 deletions .github/workflows/publish.yml
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
fetch-depth: 2

- uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0
name: Gradle Cache
Expand Down
3 changes: 1 addition & 2 deletions .github/workflows/tests_emulator.yml
Original file line number Diff line number Diff line change
Expand Up @@ -222,13 +222,12 @@ jobs:
# update workflows/README.md when modifying this script
script: |
touch adb-log.txt
$ANDROID_HOME/platform-tools/adb logcat '*:D' >> adb-log.txt &
bash .github/disable_art_metrics.sh
# Fix "Speech Recognition and Synthesis from Google keeps stopping" causing RootViewWithoutFocusException
bash .github/adb_retry.sh shell settings put global hide_error_dialogs 1
bash .github/adb_retry.sh emu screenrecord start --time-limit 1800 video.webm
sleep 5
./gradlew uninstallAll jacocoAndroidTestReport --daemon
bash .github/with_emulator_log.sh adb-log.txt ./gradlew uninstallAll jacocoAndroidTestReport --daemon

- name: Report Emulator Test Results
if: always() && hashFiles('AnkiDroid/build/outputs/androidTest-results/connected/**/TEST-*.xml') != ''
Expand Down
1 change: 1 addition & 0 deletions .github/workflows/tests_unit.yml
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,7 @@ jobs:
// Goal is to run enough iterations on all systems that we have confidence there are no flakes
if (context.eventName === "schedule") {
const iterationCount = 15
iterationArray = []
for (let i = 1; i <= iterationCount; i++) {
iterationArray.push(i);
}
Expand Down
15 changes: 13 additions & 2 deletions AnkiDroid/build.gradle
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import com.ichi2.anki.gradle.VersionCodeTasks

plugins {
// Gradle plugin portal
alias(libs.plugins.tripletPlay)
Expand Down Expand Up @@ -114,9 +116,9 @@ android {
//
// This ensures the correct ordering between the various types of releases (dev < alpha < beta < release) which is
// needed for upgrades to be offered correctly.
versionCode=22500202
versionCode=22600100
// If you change this to a new version, you probably also want to update .gradle/workflows/milestone.yml for the new version...
versionName="2.25.0beta2"
versionName="2.26.0alpha0"
minSdk = libs.versions.minSdk.get().toInteger()

// After #13695: change .tests_emulator.yml
Expand Down Expand Up @@ -375,6 +377,15 @@ android {
}
}

// register `validateVersionCode` with our (potentially new versionCode).
androidComponents.finalizeDsl { androidDsl ->
def validation = VersionCodeTasks.registerValidationTask(project, androidDsl.defaultConfig.versionCode)
// handle all 'publish' calls, even if run directly
tasks.matching { it.name.startsWith("publish") }.configureEach {
dependsOn(validation)
}
}

play {
serviceAccountCredentials.set(file("${homePath}/src/AnkiDroid-GCP-Publish-Credentials.json"))
track.set('alpha')
Expand Down
17 changes: 2 additions & 15 deletions AnkiDroid/src/androidTest/java/com/ichi2/anki/DeckPickerTest.kt
Original file line number Diff line number Diff line change
@@ -1,18 +1,5 @@
/*
* Copyright (c) 2021 Shridhar Goel <shridhar.goel@gmail.com>
*
* This program is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License as published by the Free Software
* Foundation; either version 3 of the License, or (at your option) any later
* version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT ANY
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
* PARTICULAR PURPOSE. See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with
* this program. If not, see <http://www.gnu.org/licenses/>.
*/
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright (c) 2021 Shridhar Goel <shridhar.goel@gmail.com>

package com.ichi2.anki

Expand Down
18 changes: 3 additions & 15 deletions AnkiDroid/src/androidTest/java/com/ichi2/anki/TestUtils.kt
Original file line number Diff line number Diff line change
@@ -1,18 +1,6 @@
/*
* Copyright (c) 2021 Shridhar Goel <shridhar.goel@gmail.com>
*
* This program is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License as published by the Free Software
* Foundation; either version 3 of the License, or (at your option) any later
* version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT ANY
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
* PARTICULAR PURPOSE. See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with
* this program. If not, see <http://www.gnu.org/licenses/>.
*/
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright (c) 2021 Shridhar Goel <shridhar.goel@gmail.com>

package com.ichi2.anki

import android.app.Activity
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,8 @@ import androidx.test.espresso.matcher.ViewMatchers.isDisplayed
import androidx.test.espresso.matcher.ViewMatchers.withText
import androidx.test.ext.junit.runners.AndroidJUnit4
import com.ichi2.anki.CollectionManager
import com.ichi2.anki.CommonString
import com.ichi2.anki.DeckPicker
import com.ichi2.anki.R
import com.ichi2.anki.common.preferences.sharedPrefs
import com.ichi2.anki.common.storage.CollectionHelper
import com.ichi2.anki.startup.getDefaultAnkiDroidDirectory
Expand Down Expand Up @@ -61,14 +61,14 @@ class CollectionPathMismatchRecoveryTest : InstrumentedTest() {

@Test
fun errorHandlingShowsUseDefaultFolderOption() {
onView(withText(R.string.open_collection_failed_title))
onView(withText(CommonString.open_collection_failed_title))
.inRoot(isDialog())
.check(matches(isDisplayed()))
onView(withText(R.string.error_handling_options)).inRoot(isDialog()).perform(click())
onView(withText(R.string.error_handling_title))
onView(withText(CommonString.error_handling_options)).inRoot(isDialog()).perform(click())
onView(withText(CommonString.error_handling_title))
.inRoot(isDialog())
.check(matches(isDisplayed()))
onView(withText(R.string.backup_use_default_folder))
onView(withText(CommonString.backup_use_default_folder))
.inRoot(isDialog())
.check(matches(isDisplayed()))
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import androidx.test.espresso.matcher.ViewMatchers.isDisplayed
import androidx.test.espresso.matcher.ViewMatchers.withText
import androidx.test.ext.junit.runners.AndroidJUnit4
import com.ichi2.anki.CollectionManager
import com.ichi2.anki.CommonString
import com.ichi2.anki.DeckPicker
import com.ichi2.anki.R
import com.ichi2.anki.backend.DatabaseCorruption
Expand Down Expand Up @@ -55,7 +56,7 @@ class DatabaseCorruptDialogTest : InstrumentedTest() {

@Test
fun testOpenCollectionFailedDialog() {
onView(withText(R.string.open_collection_failed_title))
onView(withText(CommonString.open_collection_failed_title))
.inRoot(isDialog())
.check(matches(isDisplayed()))
}
Expand All @@ -64,7 +65,7 @@ class DatabaseCorruptDialogTest : InstrumentedTest() {
fun testCorruptCollectionDialog() {
val corruptMsg =
testContext.getString(
R.string.corrupt_db_message,
CommonString.corrupt_db_message,
testContext.getString(R.string.repair_deck),
)
onView(withText(corruptMsg))
Expand Down
Original file line number Diff line number Diff line change
@@ -1,18 +1,6 @@
/*
* Copyright (c) 2022 lukstbit <lukstbit@users.noreply.github.com>
*
* This program is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License as published by the Free Software
* Foundation; either version 3 of the License, or (at your option) any later
* version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT ANY
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
* PARTICULAR PURPOSE. See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with
* this program. If not, see <http://www.gnu.org/licenses/>.
*/
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright (c) 2022 lukstbit <lukstbit@users.noreply.github.com>

package com.ichi2.anki.dialogs

import android.os.Bundle
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import androidx.test.espresso.matcher.ViewMatchers.withId
import androidx.test.espresso.matcher.ViewMatchers.withResourceName
import androidx.test.espresso.matcher.ViewMatchers.withText
import androidx.test.ext.junit.runners.AndroidJUnit4
import com.ichi2.anki.CommonString
import com.ichi2.anki.IntentHandler
import com.ichi2.anki.R
import com.ichi2.anki.testutil.GrantStoragePermission
Expand Down Expand Up @@ -82,9 +83,9 @@ class PreferencesNavigationTest {
openSettings()
onView(withId(com.bytehamster.lib.preferencesearch.R.id.search)).perform(click())
onView(allOf(withId(com.bytehamster.lib.preferencesearch.R.id.search), hasFocus())).perform(typeText("Card"))
onView(withText(R.string.card_zoom)).perform(click())
onView(withText(CommonString.card_zoom)).perform(click())
onView(withId(R.id.settings_container)).check(matches(isDisplayed()))
onView(withText(R.string.notification_pref)).perform(click())
onView(withText(CommonString.notification_pref)).perform(click())
pressBack()
onView(withClassName(endsWith("PreferencesActivity"))).check(doesNotExist())
}
Expand Down
14 changes: 7 additions & 7 deletions AnkiDroid/src/androidTest/java/com/ichi2/anki/tests/ACRATest.kt
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import androidx.core.content.edit
import androidx.test.annotation.UiThreadTest
import androidx.test.ext.junit.runners.AndroidJUnit4
import com.ichi2.anki.AnkiDroidApp
import com.ichi2.anki.R
import com.ichi2.anki.CommonString
import com.ichi2.anki.acraCoreConfigBuilder
import com.ichi2.anki.analytics.AnalyticsExceptionHandler
import com.ichi2.anki.analytics.AnkiDroidUsageAnalytics
Expand Down Expand Up @@ -111,7 +111,7 @@ class ACRATest : InstrumentedTest() {
setProductionACRAConfig(sharedPrefs)
verifyACRANotDisabled()

assertToastMessage(R.string.feedback_for_manual_toast_text)
assertToastMessage(CommonString.feedback_for_manual_toast_text)
assertToastIsEnabled()
assertDialogEnabledStatus("Dialog should be enabled", true)
}
Expand Down Expand Up @@ -188,7 +188,7 @@ class ACRATest : InstrumentedTest() {
setProductionACRAConfig(sharedPrefs)
verifyACRANotDisabled()

assertToastMessage(R.string.feedback_auto_toast_text)
assertToastMessage(CommonString.feedback_auto_toast_text)
assertToastIsEnabled()
assertDialogEnabledStatus("Dialog should not be enabled", false)
}
Expand All @@ -204,12 +204,12 @@ class ACRATest : InstrumentedTest() {
verifyACRANotDisabled()

assertDialogEnabledStatus("dialog should be disabled when status is ALWAYS", false)
assertToastMessage(R.string.feedback_auto_toast_text)
assertToastMessage(CommonString.feedback_auto_toast_text)

setAcraReportingMode(FEEDBACK_REPORT_ASK)

assertDialogEnabledStatus("dialog should be re-enabled after changed to ASK", true)
assertToastMessage(R.string.feedback_for_manual_toast_text)
assertToastMessage(CommonString.feedback_for_manual_toast_text)
}

@Test
Expand All @@ -222,11 +222,11 @@ class ACRATest : InstrumentedTest() {
setProductionACRAConfig(sharedPrefs)
verifyACRANotDisabled()

assertToastMessage(R.string.feedback_for_manual_toast_text)
assertToastMessage(CommonString.feedback_for_manual_toast_text)

setAcraReportingMode(FEEDBACK_REPORT_ALWAYS)

assertToastMessage(R.string.feedback_auto_toast_text)
assertToastMessage(CommonString.feedback_auto_toast_text)
}

@Test
Expand Down
Original file line number Diff line number Diff line change
@@ -1,18 +1,6 @@
/*
* Copyright (c) 2015 Timothy Rae <perceptualchaos2@gmail.com>
*
* This program is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License as published by the Free Software
* Foundation; either version 3 of the License, or (at your option) any later
* version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT ANY
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
* PARTICULAR PURPOSE. See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with
* this program. If not, see <http://www.gnu.org/licenses/>.
*/
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright (c) 2015 Timothy Rae <perceptualchaos2@gmail.com>

package com.ichi2.anki.tests

import androidx.test.ext.junit.runners.AndroidJUnit4
Expand Down
Loading
Loading