Background:
The CW decoder previously shipped a decompiled copy of the proprietary
Morse Expert 1.15 (com/ve3nea/morse_expert + obfuscated classes,
libnativedecoderjni.so, suncompat black-magic) — a copyright liability.
This removes all of it and reimplements the decoder on the open-source
fldigi (GPL v3) CW engine as a faithful pure-Kotlin port with no JNI.
Changes:
- Delete all Morse Expert reverse-engineered code: MainActivity,
obfuscated packages (B/B0/D/E2/...), suncompat/, pas/nativedecoder,
armeabi-v7a libnativedecoderjni.so, and the original View-based layouts
(activity_main, cw_panel_main, options_menu).
- Add a full fldigi CW pipeline in core/domain/cw:
- CwFldigiDsp: NCO down-conversion, FFT filter, movavg constants
- CwFftFilt: overlap-add FFT band-pass filter (fftfilt port)
- MorseTable + SomTable: full Morse code table + SOM codebook
- CwFldigiDecoder: decode_stream AGC + hysteresis, state machine,
adaptive speed tracking (5-55 WPM), SOM winner/normalize matching
- Rewrite CwDecodeScreen as pure Compose (DeepCW-style waterfall,
live decode line, history, status cards) and CwSettingsDialog
(speed/bandwidth/SOM) with no View interop.
- Replace the Morse Expert panel in TransceiversPage with a Compose
panel driving the same decoder; mic capture at 8000 Hz.
- Drop the forced armeabi-v7a abiFilters now that no native lib exists.
Verification:
- 8 unit tests pass (CQ/HELLO at 18-20 wpm, A-J at 30 wpm with
adaptive tracking, dot/dash/Farnsworth edge cases) — all decode
correctly from synthesized CW.
- :feature:cw and :feature:radar compile; app assembleDebug succeeds.
- APK contains no ve3nea/nativedecoder/morse_expert classes.
40 lines
1.3 KiB
Kotlin
40 lines
1.3 KiB
Kotlin
import java.util.Properties
|
|
|
|
plugins {
|
|
alias(libs.plugins.convention.applicationPlugin)
|
|
}
|
|
|
|
// Load signing config from keystore.properties (gitignored, never commit credentials)
|
|
val keystoreProperties = Properties().apply {
|
|
val propsFile = rootProject.file("keystore.properties")
|
|
if (propsFile.exists()) propsFile.inputStream().use { load(it) }
|
|
}
|
|
|
|
android {
|
|
// CW 解码已迁移为纯 Kotlin fldigi 引擎, 不再有 native 库;
|
|
// 取消 armeabi-v7a 强制, 按设备 ABI 打包(含 x86_64 模拟器)
|
|
defaultConfig {
|
|
}
|
|
androidResources {
|
|
// 显式保留全部语言(防 shrinkResources 丢弃 in/id 印尼语配置); AGP 9 用 localeFilters
|
|
localeFilters += listOf(
|
|
"en", "zh", "tr", "in", "id", "es", "ru", "si", "uk"
|
|
)
|
|
}
|
|
signingConfigs {
|
|
if (keystoreProperties["storeFile"] != null) {
|
|
create("release") {
|
|
storeFile = rootProject.file(keystoreProperties["storeFile"] as String)
|
|
storePassword = keystoreProperties["storePassword"] as String
|
|
keyAlias = keystoreProperties["keyAlias"] as String
|
|
keyPassword = keystoreProperties["keyPassword"] as String
|
|
}
|
|
}
|
|
}
|
|
buildTypes {
|
|
release {
|
|
signingConfig = signingConfigs.findByName("release")
|
|
}
|
|
}
|
|
}
|