From 980cda7e58fc8f3ca18a7f0582623ff2a936bd20 Mon Sep 17 00:00:00 2001 From: Potato Hatsue <1793913507@qq.com> Date: Sun, 27 Aug 2023 14:45:16 -0400 Subject: [PATCH 001/381] nix: refactor sdk to be used in other projects --- flake.nix | 96 +++++++++++++++++++++++++++++-------------------------- 1 file changed, 51 insertions(+), 45 deletions(-) diff --git a/flake.nix b/flake.nix index 185bf5dfb..466c0f686 100644 --- a/flake.nix +++ b/flake.nix @@ -19,55 +19,61 @@ }; in { devShells = { - default = pkgs.fcitx5-android-shell { }; - noAS = pkgs.fcitx5-android-shell { enableAndroidStudio = false; }; + default = pkgs.fcitx5-android.sdk.shell; + noAS = + pkgs.fcitx5-android.sdk.shell.override { androidStudio = null; }; }; }) // { overlays.default = final: prev: { - fcitx5-android-shell = { enableAndroidStudio ? true }: - with final; - with fcitx5-android-sdk; - mkShell { - buildInputs = [ - androidComposition.androidsdk - extra-cmake-modules - gettext - python39 - icu - ] ++ (if enableAndroidStudio then - [ androidStudioPackages.stable ] - else - [ ]); - ANDROID_SDK_ROOT = - "${androidComposition.androidsdk}/libexec/android-sdk"; - NDK_VERSION = ndkVersion; - BUILD_TOOLS_VERSION = buildToolsVersion; - GRADLE_OPTS = - "-Dorg.gradle.project.android.aapt2FromMavenOverride=${androidComposition.androidsdk}/libexec/android-sdk/build-tools/${buildToolsVersion}/aapt2"; - ECM_DIR = "${extra-cmake-modules}/share/ECM/cmake/"; - JAVA_HOME = "${jdk17}"; - shellHook = '' - export PATH="$ANDROID_SDK_ROOT/cmake/${cmakeVersion}/bin:$PATH" - echo sdk.dir=$ANDROID_SDK_ROOT > local.properties - ''; - }; - fcitx5-android-sdk = rec { - cmakeVersion = "3.22.1"; - buildToolsVersion = "33.0.2"; - platformToolsVersion = "33.0.3"; - platformVersion = "33"; - ndkVersion = "25.2.9519653"; - abiVersions = [ "arm64-v8a" "armeabi-v7a" ]; - androidComposition = prev.androidenv.composeAndroidPackages { - inherit platformToolsVersion ndkVersion; - buildToolsVersions = [ buildToolsVersion ]; - platformVersions = [ platformVersion ]; - inherit abiVersions; - cmakeVersions = [ cmakeVersion ]; + fcitx5-android = { + sdk = final.lib.makeExtensible (self: { + + # Update versions here + # This should match to build-logic/convention/src/main/kotlin/Versions.kt + cmakeVersion = "3.22.1"; + buildToolsVersion = "33.0.2"; + platformToolsVersion = "33.0.3"; + platformVersion = "33"; + ndkVersion = "25.2.9519653"; + + abiVersions = [ "arm64-v8a" "armeabi-v7a" ]; includeNDK = true; - includeEmulator = false; - includeSources = true; - }; + androidComposition = final.androidenv.composeAndroidPackages { + inherit (self) + platformToolsVersion ndkVersion abiVersions includeNDK; + buildToolsVersions = [ self.buildToolsVersion ]; + platformVersions = [ self.platformVersion ]; + cmakeVersions = [ self.cmakeVersion ]; + includeEmulator = false; + includeSources = true; + }; + + shell = final.lib.makeOverridable ({ androidStudio }: + with final; + with self; + mkShell { + buildInputs = [ + androidComposition.androidsdk + extra-cmake-modules + gettext + python39 + icu + androidStudio + ]; + ANDROID_SDK_ROOT = + "${androidComposition.androidsdk}/libexec/android-sdk"; + NDK_VERSION = ndkVersion; + BUILD_TOOLS_VERSION = buildToolsVersion; + GRADLE_OPTS = + "-Dorg.gradle.project.android.aapt2FromMavenOverride=${androidComposition.androidsdk}/libexec/android-sdk/build-tools/${buildToolsVersion}/aapt2"; + ECM_DIR = "${extra-cmake-modules}/share/ECM/cmake/"; + JAVA_HOME = "${jdk17}"; + shellHook = '' + export PATH="$ANDROID_SDK_ROOT/cmake/${cmakeVersion}/bin:$PATH" + echo sdk.dir=$ANDROID_SDK_ROOT > local.properties + ''; + }) { androidStudio = final.androidStudioPackages.stable; }; + }); }; }; }; From 276d2d02e1c24479ab2af54b617fdbbe91b75c7a Mon Sep 17 00:00:00 2001 From: Potato Hatsue <1793913507@qq.com> Date: Sun, 27 Aug 2023 15:04:25 -0400 Subject: [PATCH 002/381] nix: remove unused abiVersions --- flake.nix | 59 +++++++++++++++++++++++++++++-------------------------- 1 file changed, 31 insertions(+), 28 deletions(-) diff --git a/flake.nix b/flake.nix index 466c0f686..217acf92e 100644 --- a/flake.nix +++ b/flake.nix @@ -36,11 +36,9 @@ platformVersion = "33"; ndkVersion = "25.2.9519653"; - abiVersions = [ "arm64-v8a" "armeabi-v7a" ]; includeNDK = true; androidComposition = final.androidenv.composeAndroidPackages { - inherit (self) - platformToolsVersion ndkVersion abiVersions includeNDK; + inherit (self) platformToolsVersion ndkVersion includeNDK; buildToolsVersions = [ self.buildToolsVersion ]; platformVersions = [ self.platformVersion ]; cmakeVersions = [ self.cmakeVersion ]; @@ -48,31 +46,36 @@ includeSources = true; }; - shell = final.lib.makeOverridable ({ androidStudio }: - with final; - with self; - mkShell { - buildInputs = [ - androidComposition.androidsdk - extra-cmake-modules - gettext - python39 - icu - androidStudio - ]; - ANDROID_SDK_ROOT = - "${androidComposition.androidsdk}/libexec/android-sdk"; - NDK_VERSION = ndkVersion; - BUILD_TOOLS_VERSION = buildToolsVersion; - GRADLE_OPTS = - "-Dorg.gradle.project.android.aapt2FromMavenOverride=${androidComposition.androidsdk}/libexec/android-sdk/build-tools/${buildToolsVersion}/aapt2"; - ECM_DIR = "${extra-cmake-modules}/share/ECM/cmake/"; - JAVA_HOME = "${jdk17}"; - shellHook = '' - export PATH="$ANDROID_SDK_ROOT/cmake/${cmakeVersion}/bin:$PATH" - echo sdk.dir=$ANDROID_SDK_ROOT > local.properties - ''; - }) { androidStudio = final.androidStudioPackages.stable; }; + shell = final.lib.makeOverridable + ({ androidStudio, generateLocalProperties }: + with final; + with self; + mkShell { + buildInputs = [ + androidComposition.androidsdk + extra-cmake-modules + gettext + python39 + icu + androidStudio + ]; + ANDROID_SDK_ROOT = + "${androidComposition.androidsdk}/libexec/android-sdk"; + NDK_VERSION = ndkVersion; + BUILD_TOOLS_VERSION = buildToolsVersion; + GRADLE_OPTS = + "-Dorg.gradle.project.android.aapt2FromMavenOverride=${androidComposition.androidsdk}/libexec/android-sdk/build-tools/${buildToolsVersion}/aapt2"; + ECM_DIR = "${extra-cmake-modules}/share/ECM/cmake/"; + JAVA_HOME = "${jdk17}"; + shellHook = '' + export PATH="$ANDROID_SDK_ROOT/cmake/${cmakeVersion}/bin:$PATH" + '' + lib.optionalString generateLocalProperties '' + echo sdk.dir=$ANDROID_SDK_ROOT > local.properties + ''; + }) { + androidStudio = final.androidStudioPackages.stable; + generateLocalProperties = true; + }; }); }; }; From 8f088594b72d3750c032cf46fdec418433343f20 Mon Sep 17 00:00:00 2001 From: rocka Date: Mon, 28 Aug 2023 21:30:50 +0800 Subject: [PATCH 003/381] Split more prefab libraries and unikey plugin (#324) New prefab libraries: fcitx5-lua libime fcitx5-chinese-addons New plugin: fcitx5-unikey --- .gitignore | 15 +--- .gitmodules | 22 +++--- app/build.gradle.kts | 24 +++--- app/src/main/cpp/CMakeLists.txt | 76 +++++++------------ .../cpp/androidfrontend/androidfrontend.cpp | 5 +- .../androidkeyboard.conf.in.in | 3 +- .../cpp/androidkeyboard/androidkeyboard.cpp | 4 +- app/src/main/cpp/libime | 1 - app/src/main/cpp/native-lib.cpp | 12 ++- .../fcitx5/android/ui/main/PluginFragment.kt | 13 ++-- .../src/main/kotlin/FcitxComponentPlugin.kt | 18 +++-- .../main/kotlin/NativeLibConventionPlugin.kt | 1 + lib/fcitx5-chinese-addons/build.gradle.kts | 72 ++++++++++++++++++ .../src/main/AndroidManifest.xml | 3 + .../src/main/cpp/CMakeLists.txt | 63 +++++++++++++++ .../src/main/cpp/fcitx5-chinese-addons | 0 lib/fcitx5-lua/build.gradle.kts | 40 ++++++++++ lib/fcitx5-lua/src/main/AndroidManifest.xml | 3 + lib/fcitx5-lua/src/main/cpp/CMakeLists.txt | 30 ++++++++ .../FindFcitx5ModuleLuaAddonLoader.cmake | 8 ++ .../fcitx5-lua}/src/main/cpp/fcitx5-lua | 0 lib/fcitx5/src/main/cpp/CMakeLists.txt | 10 +-- .../cpp/cmake/Fcitx5AndroidInstallDirs.cmake | 14 ++++ .../src/main/cpp/cmake/FindFcitx5Utils.cmake | 11 ++- .../src/main/cpp/cmake/FindLibIMEPinyin.cmake | 9 --- .../src/main/cpp/cmake/FindLibIMETable.cmake | 9 --- lib/libime/build.gradle.kts | 50 ++++++++++++ lib/libime/src/main/AndroidManifest.xml | 3 + lib/libime/src/main/cpp/CMakeLists.txt | 39 ++++++++++ .../src/main/cpp/cmake/FindLibIMECore.cmake | 23 ++++++ .../src/main/cpp/cmake/FindLibIMEPinyin.cmake | 21 +++++ .../src/main/cpp/cmake/FindLibIMETable.cmake | 21 +++++ lib/libime/src/main/cpp/libime | 1 + plugin/anthy/build.gradle.kts | 8 +- plugin/anthy/src/main/cpp/CMakeLists.txt | 8 +- plugin/unikey/build.gradle.kts | 53 +++++++++++++ .../licenses/libraries/fcitx5-unikey.json | 0 plugin/unikey/src/main/AndroidManifest.xml | 6 ++ plugin/unikey/src/main/cpp/CMakeLists.txt | 21 +++++ .../unikey}/src/main/cpp/fcitx5-unikey | 0 plugin/unikey/src/main/res/values/strings.xml | 6 ++ plugin/unikey/src/main/res/xml/plugin.xml | 6 ++ settings.gradle.kts | 4 + 43 files changed, 587 insertions(+), 149 deletions(-) delete mode 160000 app/src/main/cpp/libime create mode 100644 lib/fcitx5-chinese-addons/build.gradle.kts create mode 100644 lib/fcitx5-chinese-addons/src/main/AndroidManifest.xml create mode 100644 lib/fcitx5-chinese-addons/src/main/cpp/CMakeLists.txt rename {app => lib/fcitx5-chinese-addons}/src/main/cpp/fcitx5-chinese-addons (100%) create mode 100644 lib/fcitx5-lua/build.gradle.kts create mode 100644 lib/fcitx5-lua/src/main/AndroidManifest.xml create mode 100644 lib/fcitx5-lua/src/main/cpp/CMakeLists.txt rename lib/{fcitx5 => fcitx5-lua}/src/main/cpp/cmake/FindFcitx5ModuleLuaAddonLoader.cmake (53%) rename {app => lib/fcitx5-lua}/src/main/cpp/fcitx5-lua (100%) create mode 100644 lib/fcitx5/src/main/cpp/cmake/Fcitx5AndroidInstallDirs.cmake delete mode 100644 lib/fcitx5/src/main/cpp/cmake/FindLibIMEPinyin.cmake delete mode 100644 lib/fcitx5/src/main/cpp/cmake/FindLibIMETable.cmake create mode 100644 lib/libime/build.gradle.kts create mode 100644 lib/libime/src/main/AndroidManifest.xml create mode 100644 lib/libime/src/main/cpp/CMakeLists.txt create mode 100644 lib/libime/src/main/cpp/cmake/FindLibIMECore.cmake create mode 100644 lib/libime/src/main/cpp/cmake/FindLibIMEPinyin.cmake create mode 100644 lib/libime/src/main/cpp/cmake/FindLibIMETable.cmake create mode 160000 lib/libime/src/main/cpp/libime create mode 100644 plugin/unikey/build.gradle.kts rename {app => plugin/unikey}/licenses/libraries/fcitx5-unikey.json (100%) create mode 100644 plugin/unikey/src/main/AndroidManifest.xml create mode 100644 plugin/unikey/src/main/cpp/CMakeLists.txt rename {app => plugin/unikey}/src/main/cpp/fcitx5-unikey (100%) create mode 100644 plugin/unikey/src/main/res/values/strings.xml create mode 100644 plugin/unikey/src/main/res/xml/plugin.xml diff --git a/.gitignore b/.gitignore index cfc079c99..52e0aa79b 100644 --- a/.gitignore +++ b/.gitignore @@ -12,19 +12,12 @@ # Generated asset descriptor /app/src/main/assets/descriptor.json -# Module :plugin:anthy +# Plugins # Installed data -/plugin/anthy/src/main/assets/usr/share/fcitx5/addon -/plugin/anthy/src/main/assets/usr/share/fcitx5/anthy -/plugin/anthy/src/main/assets/usr/share/fcitx5/inputmethod -/plugin/anthy/src/main/assets/usr/share/locale +/plugin/*/src/main/assets/usr/share/fcitx5 +/plugin/*/src/main/assets/usr/share/locale # Generated asset descriptor -/plugin/anthy/src/main/assets/descriptor.json - -# Module :plugin:clipboard-filter -# Generated asset descriptor -/plugin/clipboard-filter/src/main/assets/descriptor.json - +/plugin/*/src/main/assets/descriptor.json # Intellij .idea/* diff --git a/.gitmodules b/.gitmodules index 84535ca20..3517f1560 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,24 +1,24 @@ [submodule "lib/fcitx5/src/main/cpp/fcitx5"] path = lib/fcitx5/src/main/cpp/fcitx5 url = git@github.com:fcitx/fcitx5.git -[submodule "app/src/main/cpp/fcitx5-chinese-addons"] - path = app/src/main/cpp/fcitx5-chinese-addons - url = git@github.com:fcitx/fcitx5-chinese-addons.git -[submodule "app/src/main/cpp/libime"] - path = app/src/main/cpp/libime - url = git@github.com:fcitx/libime.git [submodule "lib/fcitx5/src/main/cpp/prebuilt"] path = lib/fcitx5/src/main/cpp/prebuilt url = git@github.com:fcitx5-android/prebuilt -[submodule "app/src/main/cpp/fcitx5-lua"] - path = app/src/main/cpp/fcitx5-lua +[submodule "lib/fcitx5-lua/src/main/cpp/fcitx5-lua"] + path = lib/fcitx5-lua/src/main/cpp/fcitx5-lua url = git@github.com:fcitx/fcitx5-lua.git -[submodule "app/src/main/cpp/fcitx5-unikey"] - path = app/src/main/cpp/fcitx5-unikey - url = git@github.com:fcitx/fcitx5-unikey.git +[submodule "lib/libime/src/main/cpp/libime"] + path = lib/libime/src/main/cpp/libime + url = git@github.com:fcitx/libime.git +[submodule "lib/fcitx5-chinese-addons/src/main/cpp/fcitx5-chinese-addons"] + path = lib/fcitx5-chinese-addons/src/main/cpp/fcitx5-chinese-addons + url = git@github.com:fcitx/fcitx5-chinese-addons.git [submodule "plugin/anthy/src/main/cpp/anthy-cmake"] path = plugin/anthy/src/main/cpp/anthy-cmake url = git@github.com:fcitx5-android/anthy-cmake.git [submodule "plugin/anthy/src/main/cpp/fcitx5-anthy"] path = plugin/anthy/src/main/cpp/fcitx5-anthy url = git@github.com:fcitx/fcitx5-anthy.git +[submodule "plugin/unikey/src/main/cpp/fcitx5-unikey"] + path = plugin/unikey/src/main/cpp/fcitx5-unikey + url = git@github.com:fcitx/fcitx5-unikey.git diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 2b5e430e8..4479d1bc0 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -27,19 +27,7 @@ android { "copy-fcitx5-modules", // android specific modules "androidfrontend", - "androidkeyboard", - // fcitx5-chinese-addons - "pinyin", - "scel2org5", - "table", - "chttrans", - "fullwidth", - "pinyinhelper", - "punctuation", - // fcitx5-lua - "luaaddonloader", - // fcitx5-unikey - "unikey" + "androidkeyboard" ) } } @@ -84,7 +72,12 @@ aboutLibraries { } fcitxComponent { - installFcitx5Data = true + installLibraries = listOf( + "fcitx5", + "fcitx5-lua", + "libime", + "fcitx5-chinese-addons" + ) } ksp { @@ -94,6 +87,9 @@ ksp { dependencies { ksp(project(":codegen")) implementation(project(":lib:fcitx5")) + implementation(project(":lib:fcitx5-lua")) + implementation(project(":lib:libime")) + implementation(project(":lib:fcitx5-chinese-addons")) implementation(project(":lib:common")) implementation(libs.kotlinx.coroutines) implementation(libs.kotlinx.serialization.json) diff --git a/app/src/main/cpp/CMakeLists.txt b/app/src/main/cpp/CMakeLists.txt index 90aed7708..b33696b0a 100644 --- a/app/src/main/cpp/CMakeLists.txt +++ b/app/src/main/cpp/CMakeLists.txt @@ -2,8 +2,6 @@ cmake_minimum_required(VERSION 3.18) project(fcitx5-android VERSION 0.0.7) -set(CMAKE_CXX_STANDARD 17) - # For reproducible build add_link_options("LINKER:--hash-style=gnu,--build-id=none") @@ -15,10 +13,17 @@ set(CMAKE_MODULE_PATH ${FCITX5_CMAKE_MODULES} ${CMAKE_MODULE_PATH}) find_package(Fcitx5Core MODULE) find_package(Fcitx5Module MODULE) -# install prefix for addon conf and locale -set(CMAKE_INSTALL_PREFIX /usr) -set(FCITX_INSTALL_PKGDATADIR /usr/share/fcitx5) -set(FCITX_INSTALL_LOCALEDIR /usr/share/locale) +find_package(libime REQUIRED CONFIG) +get_target_property(LIBIME_CMAKE_MODULES libime::cmake INTERFACE_INCLUDE_DIRECTORIES) +set(CMAKE_MODULE_PATH ${LIBIME_CMAKE_MODULES} ${CMAKE_MODULE_PATH}) + +find_package(LibIMEPinyin MODULE) +find_package(LibIMETable MODULE) + +find_package(fcitx5-lua REQUIRED CONFIG) +find_package(fcitx5-chinese-addons REQUIRED CONFIG) + +include("${FCITX_INSTALL_CMAKECONFIG_DIR}/Fcitx5Utils/Fcitx5CompilerSettings.cmake") add_subdirectory(po) add_subdirectory(androidfrontend) @@ -43,41 +48,7 @@ set(Boost_DIR "${BOOST_ROOT}/lib/cmake/Boost-${BOOST_VERSION}") foreach (mod IN LISTS BOOST_MODULES) set("boost_${mod}_DIR" "${BOOST_ROOT}/lib/cmake/boost_${mod}-${BOOST_VERSION}") endforeach () - -option(ENABLE_TEST "" OFF) -set(LIBIME_INSTALL_PKGDATADIR table) -add_subdirectory(libime) -# kenlm/util/exception.hh uses __FILE__ macro -target_compile_options(kenlm PRIVATE "-ffile-prefix-map=${CMAKE_CURRENT_SOURCE_DIR}/libime/src/libime/core/kenlm=.") - -# prebuilt lua -set(Lua_DIR "${PREBUILT_DIR}/lua/${ANDROID_ABI}/share/cmake/Lua") -find_package(Lua CONFIG) - -# we are using static linking -option(USE_DLOPEN "" OFF) -add_subdirectory(fcitx5-lua) - -# prebuilt opencc -set(OpenCC_DIR "${PREBUILT_DIR}/opencc/${ANDROID_ABI}/lib/cmake/opencc") -find_package(OpenCC) - -option(ENABLE_TEST "" OFF) -option(ENABLE_GUI "" OFF) -option(ENABLE_BROWSER "" OFF) -option(USE_WEBKIT "" OFF) -option(ENABLE_CLOUDPINYIN "" OFF) -# prefer OpenCC_DIR rather than fcitx5-chinese-addons/cmake/FindOpenCC.cmake -set(CMAKE_FIND_PACKAGE_PREFER_CONFIG ON) -add_subdirectory(fcitx5-chinese-addons) -# rename to include executable in apk -set_target_properties(scel2org5 PROPERTIES OUTPUT_NAME libscel2org5.so) - -option(ENABLE_TEST "" OFF) -option(ENABLE_QT "" OFF) -add_subdirectory(fcitx5-unikey) -# suppress "illegal character encoding in character literal" warning in unikey/data.cpp -target_compile_options(unikey-lib PRIVATE "-Wno-invalid-source-encoding") +find_package(Boost 1.80 REQUIRED COMPONENTS filesystem iostreams) add_library(native-lib SHARED native-lib.cpp) target_link_libraries(native-lib @@ -89,22 +60,27 @@ target_link_libraries(native-lib Fcitx5::Module::QuickPhrase Fcitx5::Module::Unicode Fcitx5::Module::Clipboard + Boost::boost LibIME::Pinyin - LibIME::Table) + LibIME::Table + ) add_custom_target(copy-fcitx5-modules + # fcitx5 COMMAND ${CMAKE_COMMAND} -E copy $ ${CMAKE_LIBRARY_OUTPUT_DIRECTORY} COMMAND ${CMAKE_COMMAND} -E copy $ ${CMAKE_LIBRARY_OUTPUT_DIRECTORY} COMMAND ${CMAKE_COMMAND} -E copy $ ${CMAKE_LIBRARY_OUTPUT_DIRECTORY} COMMAND ${CMAKE_COMMAND} -E copy $ ${CMAKE_LIBRARY_OUTPUT_DIRECTORY} COMMAND ${CMAKE_COMMAND} -E copy $ ${CMAKE_LIBRARY_OUTPUT_DIRECTORY} + # fcitx5-lua + COMMAND ${CMAKE_COMMAND} -E copy $ ${CMAKE_LIBRARY_OUTPUT_DIRECTORY} + # fcitx5-chinese-addons + COMMAND ${CMAKE_COMMAND} -E copy $ ${CMAKE_LIBRARY_OUTPUT_DIRECTORY} + COMMAND ${CMAKE_COMMAND} -E copy $ ${CMAKE_LIBRARY_OUTPUT_DIRECTORY} + COMMAND ${CMAKE_COMMAND} -E copy $ ${CMAKE_LIBRARY_OUTPUT_DIRECTORY} + COMMAND ${CMAKE_COMMAND} -E copy $ ${CMAKE_LIBRARY_OUTPUT_DIRECTORY} + COMMAND ${CMAKE_COMMAND} -E copy $ ${CMAKE_LIBRARY_OUTPUT_DIRECTORY} + COMMAND ${CMAKE_COMMAND} -E copy $ ${CMAKE_LIBRARY_OUTPUT_DIRECTORY} + COMMAND ${CMAKE_COMMAND} -E copy $ ${CMAKE_LIBRARY_OUTPUT_DIRECTORY} COMMENT "Copying fcitx5 module libraries to :app" ) - -add_dependencies(copy-fcitx5-modules - fcitx5::clipboard - fcitx5::imselector - fcitx5::quickphrase - fcitx5::spell - fcitx5::unicode - ) diff --git a/app/src/main/cpp/androidfrontend/androidfrontend.cpp b/app/src/main/cpp/androidfrontend/androidfrontend.cpp index be2675e63..fe1867280 100644 --- a/app/src/main/cpp/androidfrontend/androidfrontend.cpp +++ b/app/src/main/cpp/androidfrontend/androidfrontend.cpp @@ -161,7 +161,10 @@ AndroidFrontend::AndroidFrontend(Instance *instance) eventHandlers_.emplace_back(instance_->watchEvent( EventType::InputContextInputMethodActivated, EventWatcherPhase::Default, - [this](Event &event) { imChangeCallback(); } + [this](Event &event) { + FCITX_UNUSED(event); + imChangeCallback(); + } )); eventHandlers_.emplace_back(instance_->watchEvent( EventType::InputContextUpdateUI, diff --git a/app/src/main/cpp/androidkeyboard/androidkeyboard.conf.in.in b/app/src/main/cpp/androidkeyboard/androidkeyboard.conf.in.in index 7f83929d9..03d617017 100644 --- a/app/src/main/cpp/androidkeyboard/androidkeyboard.conf.in.in +++ b/app/src/main/cpp/androidkeyboard/androidkeyboard.conf.in.in @@ -8,5 +8,4 @@ Configurable=True [Addon/OptionalDependencies] 0=spell -1=quickphrase -;2=emoji \ No newline at end of file +1=quickphrase \ No newline at end of file diff --git a/app/src/main/cpp/androidkeyboard/androidkeyboard.cpp b/app/src/main/cpp/androidkeyboard/androidkeyboard.cpp index bf72be797..2e0dffc23 100644 --- a/app/src/main/cpp/androidkeyboard/androidkeyboard.cpp +++ b/app/src/main/cpp/androidkeyboard/androidkeyboard.cpp @@ -209,6 +209,7 @@ void AndroidKeyboardEngine::setConfig(const RawConfig &config) { } void AndroidKeyboardEngine::activate(const InputMethodEntry &entry, InputContextEvent &event) { + FCITX_UNUSED(entry); auto *inputContext = event.inputContext(); wordHintAction_.setChecked(*config_.enableWordHint); wordHintAction_.update(inputContext); @@ -226,6 +227,7 @@ void AndroidKeyboardEngine::deactivate(const InputMethodEntry &entry, InputConte } void AndroidKeyboardEngine::reset(const InputMethodEntry &entry, InputContextEvent &event) { + FCITX_UNUSED(entry); auto *inputContext = event.inputContext(); resetState(inputContext); inputContext->inputPanel().reset(); @@ -340,7 +342,7 @@ void AndroidKeyboardEngine::invokeActionImpl(const InputMethodEntry &entry, Invo auto *state = inputContext->propertyFor(&factory_); if (event.action() != InvokeActionEvent::Action::LeftClick || cursor < 0 - || cursor > state->buffer_.size()) { + || static_cast(cursor) > state->buffer_.size()) { return InputMethodEngineV3::invokeActionImpl(entry, event); } event.filter(); diff --git a/app/src/main/cpp/libime b/app/src/main/cpp/libime deleted file mode 160000 index ccf4d73c1..000000000 --- a/app/src/main/cpp/libime +++ /dev/null @@ -1 +0,0 @@ -Subproject commit ccf4d73c1e564d9c870f934865ec239536414478 diff --git a/app/src/main/cpp/native-lib.cpp b/app/src/main/cpp/native-lib.cpp index bfb90acd4..3f49a85d8 100644 --- a/app/src/main/cpp/native-lib.cpp +++ b/app/src/main/cpp/native-lib.cpp @@ -136,7 +136,7 @@ class Fcitx { const auto *entry = imMgr.entry(ime.name()); entries.emplace_back(entry); } - return std::move(entries); + return entries; } InputMethodStatus inputMethodStatus() { @@ -163,7 +163,7 @@ class Fcitx { entries.emplace_back(&entry); return true; }); - return std::move(entries); + return entries; } void setEnabledInputMethods(std::vector &entries) { @@ -461,6 +461,9 @@ JNI_OnLoad(JavaVM *jvm, void * /* reserved */) { return JNI_VERSION_1_6; } +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wunused-parameter" + extern "C" JNIEXPORT void JNICALL Java_org_fcitx_fcitx5_android_core_Fcitx_setupLogStream(JNIEnv *env, jclass clazz, jboolean verbose) { @@ -535,9 +538,8 @@ Java_org_fcitx_fcitx5_android_core_Fcitx_startupFcitx(JNIEnv *env, jclass clazz, const char *locale_dir_char = locale_dir.c_str(); fcitx::registerDomain("fcitx5", locale_dir_char); - fcitx::registerDomain("fcitx5-chinese-addons", locale_dir_char); fcitx::registerDomain("fcitx5-lua", locale_dir_char); - fcitx::registerDomain("fcitx5-unikey", locale_dir_char); + fcitx::registerDomain("fcitx5-chinese-addons", locale_dir_char); fcitx::registerDomain("fcitx5-android", locale_dir_char); int extDomainsSize = env->GetArrayLength(extDomains); @@ -1041,3 +1043,5 @@ Java_org_fcitx_fcitx5_android_data_table_TableManager_checkTableDictFormat(JNIEn } return JNI_TRUE; } + +#pragma GCC diagnostic pop diff --git a/app/src/main/java/org/fcitx/fcitx5/android/ui/main/PluginFragment.kt b/app/src/main/java/org/fcitx/fcitx5/android/ui/main/PluginFragment.kt index e9e759f21..54b946a32 100644 --- a/app/src/main/java/org/fcitx/fcitx5/android/ui/main/PluginFragment.kt +++ b/app/src/main/java/org/fcitx/fcitx5/android/ui/main/PluginFragment.kt @@ -87,14 +87,11 @@ class PluginFragment : PaddingPreferenceFragment() { getString(R.string.missing_plugin_descriptor) } is PluginLoadFailed.PathConflict -> { - getString( - R.string.path_conflict, - reason.path, - when (val src = reason.existingSrc) { - FileSource.Main -> R.string.main_program - is FileSource.Plugin -> src.descriptor.name - } - ) + val owner = when (reason.existingSrc) { + FileSource.Main -> getString(R.string.main_program) + is FileSource.Plugin -> reason.existingSrc.descriptor.name + } + getString(R.string.path_conflict, reason.path, owner) } is PluginLoadFailed.PluginAPIIncompatible -> { getString(R.string.incompatible_api, reason.api) diff --git a/build-logic/convention/src/main/kotlin/FcitxComponentPlugin.kt b/build-logic/convention/src/main/kotlin/FcitxComponentPlugin.kt index 582ea770d..28286fd31 100644 --- a/build-logic/convention/src/main/kotlin/FcitxComponentPlugin.kt +++ b/build-logic/convention/src/main/kotlin/FcitxComponentPlugin.kt @@ -13,7 +13,7 @@ import kotlin.io.path.isSymbolicLink class FcitxComponentPlugin : Plugin { abstract class FcitxComponentExtension { - var installFcitx5Data: Boolean = false + var installLibraries: List = emptyList() } companion object { @@ -30,10 +30,10 @@ class FcitxComponentPlugin : Plugin { target.extensions.create("fcitxComponent") target.afterEvaluate { val ext = extensions.getByName("fcitxComponent") - if (ext.installFcitx5Data) { - val libFcitx5 = rootProject.project(":lib:fcitx5") - registerCMakeTask(target, "generate-desktop-file", "config", libFcitx5) - registerCMakeTask(target, "translation-file", "translation", libFcitx5) + ext.installLibraries.forEach { + val project = rootProject.project(":lib:$it") + registerCMakeTask(target, "generate-desktop-file", "config", project) + registerCMakeTask(target, "translation-file", "translation", project) } } } @@ -48,8 +48,12 @@ class FcitxComponentPlugin : Plugin { sourceProject: Project = project ) { val dependencyTask = project.tasks.findByName(INSTALL_TASK) ?: project.task(INSTALL_TASK) - val taskName = if (project === sourceProject) "installProject" else "installFcitx5" - project.task("${taskName}${component.capitalized()}") { + val taskName = if (project === sourceProject) { + "installProject${component.capitalized()}" + } else { + "installLibrary${component.capitalized()}[${sourceProject.name}]" + } + project.task(taskName) { runAfterNativeConfigure(sourceProject) doLast { diff --git a/build-logic/convention/src/main/kotlin/NativeLibConventionPlugin.kt b/build-logic/convention/src/main/kotlin/NativeLibConventionPlugin.kt index 66c2146f6..ac3c1fae3 100644 --- a/build-logic/convention/src/main/kotlin/NativeLibConventionPlugin.kt +++ b/build-logic/convention/src/main/kotlin/NativeLibConventionPlugin.kt @@ -17,6 +17,7 @@ class NativeLibConventionPlugin : NativeBaseConventionPlugin() { } } buildFeatures { + prefab = true prefabPublishing = true } libraryVariants.all { diff --git a/lib/fcitx5-chinese-addons/build.gradle.kts b/lib/fcitx5-chinese-addons/build.gradle.kts new file mode 100644 index 000000000..005a96f81 --- /dev/null +++ b/lib/fcitx5-chinese-addons/build.gradle.kts @@ -0,0 +1,72 @@ +@file:Suppress("UnstableApiUsage") + +plugins { + id("org.fcitx.fcitx5.android.lib-convention") + id("org.fcitx.fcitx5.android.native-lib-convention") + id("org.fcitx.fcitx5.android.fcitx-headers") +} + +android { + namespace = "org.fcitx.fcitx5.android.lib.fcitx5_chinese_addons" + + defaultConfig { + externalNativeBuild { + cmake { + targets( + // dummy "cmake" target + "cmake", + // fcitx5-chinese-addons + "pinyin", + "scel2org5", + "table", + "chttrans", + "fullwidth", + "pinyinhelper", + "punctuation", + ) + } + } + } + + prefab { + create("cmake") { + headerOnly = true + headers = "src/main/cpp/cmake" + } + create("pinyin") { + libraryName = "libpinyin" + // no headers + } + create("table") { + libraryName = "libtable" + // no headers + } + create("scel2org5") { + libraryName = "libscel2org5" + // no headers + } + val moduleHeadersPrefix = "build/headers/usr/include/Fcitx5/Module/fcitx-module" + create("chttrans") { + libraryName = "libchttrans" + // no headers + } + create("fullwidth") { + libraryName = "libfullwidth" + // no headers + } + create("pinyinhelper") { + libraryName = "libpinyinhelper" + headers = "$moduleHeadersPrefix/pinyinhelper" + } + create("punctuation") { + libraryName = "libpunctuation" + headers = "$moduleHeadersPrefix/punctuation" + } + } +} + +dependencies { + implementation(project(":lib:fcitx5")) + implementation(project(":lib:libime")) + implementation(project(":lib:fcitx5-lua")) +} diff --git a/lib/fcitx5-chinese-addons/src/main/AndroidManifest.xml b/lib/fcitx5-chinese-addons/src/main/AndroidManifest.xml new file mode 100644 index 000000000..69fc41290 --- /dev/null +++ b/lib/fcitx5-chinese-addons/src/main/AndroidManifest.xml @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/lib/fcitx5-chinese-addons/src/main/cpp/CMakeLists.txt b/lib/fcitx5-chinese-addons/src/main/cpp/CMakeLists.txt new file mode 100644 index 000000000..e21ef4011 --- /dev/null +++ b/lib/fcitx5-chinese-addons/src/main/cpp/CMakeLists.txt @@ -0,0 +1,63 @@ +cmake_minimum_required(VERSION 3.18) + +project(fcitx5-android-lib-fcitx5-chinese-addons VERSION 0.0.7) + +# For reproducible build +add_link_options("LINKER:--hash-style=gnu,--build-id=none") + +# prefab dependency +find_package(fcitx5 REQUIRED CONFIG) +get_target_property(FCITX5_CMAKE_MODULES fcitx5::cmake INTERFACE_INCLUDE_DIRECTORIES) +set(CMAKE_MODULE_PATH ${FCITX5_CMAKE_MODULES} ${CMAKE_MODULE_PATH}) + +find_package(ECM MODULE) +find_package(Fcitx5Core MODULE) +find_package(Fcitx5Module MODULE) + +find_package(libime REQUIRED CONFIG) +get_target_property(LIBIME_CMAKE_MODULES libime::cmake INTERFACE_INCLUDE_DIRECTORIES) +set(CMAKE_MODULE_PATH ${LIBIME_CMAKE_MODULES} ${CMAKE_MODULE_PATH}) + +find_package(LibIMECore MODULE) +find_package(LibIMEPinyin MODULE) +find_package(LibIMETable MODULE) + +find_package(fcitx5-lua REQUIRED CONFIG) +get_target_property(FCITX5_LUA_CMAKE_MODULES fcitx5-lua::cmake INTERFACE_INCLUDE_DIRECTORIES) +set(CMAKE_MODULE_PATH ${FCITX5_LUA_CMAKE_MODULES} ${CMAKE_MODULE_PATH}) + +find_package(Fcitx5ModuleLuaAddonLoader MODULE) + +# dummy target to export src/main/cpp/cmake +add_custom_target(cmake) + +# prebuilt dir. at least it works. +set(PREBUILT_DIR "${CMAKE_CURRENT_SOURCE_DIR}/../../../../fcitx5/src/main/cpp/prebuilt") + +# prebuilt fmt +set(fmt_DIR "${PREBUILT_DIR}/fmt/${ANDROID_ABI}/lib/cmake/fmt") +find_package(fmt) + +# prebuilt boost +set(BOOST_VERSION "1.80.0") +set(BOOST_MODULES headers filesystem atomic iostreams) +set(BOOST_ROOT "${PREBUILT_DIR}/boost/${ANDROID_ABI}") +set(Boost_DIR "${BOOST_ROOT}/lib/cmake/Boost-${BOOST_VERSION}") +foreach (mod IN LISTS BOOST_MODULES) + set("boost_${mod}_DIR" "${BOOST_ROOT}/lib/cmake/boost_${mod}-${BOOST_VERSION}") +endforeach () + +# prebuilt opencc +set(OpenCC_DIR "${PREBUILT_DIR}/opencc/${ANDROID_ABI}/lib/cmake/opencc") +find_package(OpenCC) + +option(ENABLE_TEST "" OFF) +option(ENABLE_GUI "" OFF) +option(ENABLE_BROWSER "" OFF) +option(USE_WEBKIT "" OFF) +option(ENABLE_CLOUDPINYIN "" OFF) +# prefer OpenCC_DIR rather than fcitx5-chinese-addons/cmake/FindOpenCC.cmake +set(CMAKE_FIND_PACKAGE_PREFER_CONFIG ON) +add_subdirectory(fcitx5-chinese-addons) +# rename to include executable in apk +set_target_properties(scel2org5 PROPERTIES OUTPUT_NAME libscel2org5.so) diff --git a/app/src/main/cpp/fcitx5-chinese-addons b/lib/fcitx5-chinese-addons/src/main/cpp/fcitx5-chinese-addons similarity index 100% rename from app/src/main/cpp/fcitx5-chinese-addons rename to lib/fcitx5-chinese-addons/src/main/cpp/fcitx5-chinese-addons diff --git a/lib/fcitx5-lua/build.gradle.kts b/lib/fcitx5-lua/build.gradle.kts new file mode 100644 index 000000000..7acea2265 --- /dev/null +++ b/lib/fcitx5-lua/build.gradle.kts @@ -0,0 +1,40 @@ +@file:Suppress("UnstableApiUsage") + +plugins { + id("org.fcitx.fcitx5.android.lib-convention") + id("org.fcitx.fcitx5.android.native-lib-convention") + id("org.fcitx.fcitx5.android.fcitx-headers") +} + +android { + namespace = "org.fcitx.fcitx5.android.lib.fcitx5_lua" + + defaultConfig { + externalNativeBuild { + cmake { + targets( + // dummy "cmake" target + "cmake", + // fcitx5-lua + "luaaddonloader" + ) + } + } + } + + prefab { + create("cmake") { + headers = "src/main/cpp/cmake" + headerOnly = true + } + val moduleHeadersPrefix = "build/headers/usr/include/Fcitx5/Module/fcitx-module" + create("luaaddonloader") { + libraryName = "libluaaddonloader" + headers = "$moduleHeadersPrefix/luaaddonloader" + } + } +} + +dependencies { + implementation(project(":lib:fcitx5")) +} diff --git a/lib/fcitx5-lua/src/main/AndroidManifest.xml b/lib/fcitx5-lua/src/main/AndroidManifest.xml new file mode 100644 index 000000000..69fc41290 --- /dev/null +++ b/lib/fcitx5-lua/src/main/AndroidManifest.xml @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/lib/fcitx5-lua/src/main/cpp/CMakeLists.txt b/lib/fcitx5-lua/src/main/cpp/CMakeLists.txt new file mode 100644 index 000000000..6cb35c149 --- /dev/null +++ b/lib/fcitx5-lua/src/main/cpp/CMakeLists.txt @@ -0,0 +1,30 @@ +cmake_minimum_required(VERSION 3.18) + +project(fcitx5-android-lib-fcitx5-lua VERSION 0.0.7) + +# For reproducible build +add_link_options("LINKER:--hash-style=gnu,--build-id=none") + +# prefab dependency +find_package(fcitx5 REQUIRED CONFIG) +get_target_property(FCITX5_CMAKE_MODULES fcitx5::cmake INTERFACE_INCLUDE_DIRECTORIES) +set(CMAKE_MODULE_PATH ${FCITX5_CMAKE_MODULES} ${CMAKE_MODULE_PATH}) + +find_package(ECM MODULE) +find_package(Fcitx5Core MODULE) +find_package(Fcitx5Module MODULE) + +# dummy target to export src/main/cpp/cmake +add_custom_target(cmake) + +# prebuilt dir. at least it works. +set(PREBUILT_DIR "${CMAKE_CURRENT_SOURCE_DIR}/../../../../fcitx5/src/main/cpp/prebuilt") + +# prebuilt lua +set(Lua_DIR "${PREBUILT_DIR}/lua/${ANDROID_ABI}/share/cmake/Lua") +find_package(Lua CONFIG) + +# we are using static linking +option(USE_DLOPEN "" OFF) +option(ENABLE_TEST "" OFF) +add_subdirectory(fcitx5-lua) diff --git a/lib/fcitx5/src/main/cpp/cmake/FindFcitx5ModuleLuaAddonLoader.cmake b/lib/fcitx5-lua/src/main/cpp/cmake/FindFcitx5ModuleLuaAddonLoader.cmake similarity index 53% rename from lib/fcitx5/src/main/cpp/cmake/FindFcitx5ModuleLuaAddonLoader.cmake rename to lib/fcitx5-lua/src/main/cpp/cmake/FindFcitx5ModuleLuaAddonLoader.cmake index 9e85105dc..7df5ed5b8 100644 --- a/lib/fcitx5/src/main/cpp/cmake/FindFcitx5ModuleLuaAddonLoader.cmake +++ b/lib/fcitx5-lua/src/main/cpp/cmake/FindFcitx5ModuleLuaAddonLoader.cmake @@ -1,5 +1,13 @@ set(Fcitx5ModuleLuaAddonLoader_FOUND TRUE) +# find prefab dependency +find_package(fcitx5-lua REQUIRED CONFIG) + +if (NOT TARGET Fcitx5::Module::LuaAddonLoader) + # fix target name + add_library(Fcitx5::Module::LuaAddonLoader ALIAS fcitx5-lua::luaaddonloader) +endif() + include(FindPackageHandleStandardArgs) find_package_handle_standard_args(Fcitx5ModuleLuaAddonLoader FOUND_VAR diff --git a/app/src/main/cpp/fcitx5-lua b/lib/fcitx5-lua/src/main/cpp/fcitx5-lua similarity index 100% rename from app/src/main/cpp/fcitx5-lua rename to lib/fcitx5-lua/src/main/cpp/fcitx5-lua diff --git a/lib/fcitx5/src/main/cpp/CMakeLists.txt b/lib/fcitx5/src/main/cpp/CMakeLists.txt index 3a9d5fbee..aa9e05ce8 100644 --- a/lib/fcitx5/src/main/cpp/CMakeLists.txt +++ b/lib/fcitx5/src/main/cpp/CMakeLists.txt @@ -2,8 +2,6 @@ cmake_minimum_required(VERSION 3.18) project(fcitx5-android-lib-fcitx5 VERSION 0.0.7) -set(CMAKE_CXX_STANDARD 17) - # For reproducible build add_link_options("LINKER:--hash-style=gnu,--build-id=none") @@ -12,13 +10,7 @@ add_custom_target(cmake) # dummy target to export "notifications_public.h" add_custom_target(notifications) -set(CMAKE_INSTALL_PREFIX /usr) -# fcitx-utils/standardpath.cpp uses FCITX_INSTALL_LIBDATADIR, -# which is CMAKE_INSTALL_LIBDATADIR's absolute path -set(CMAKE_INSTALL_LIBDATADIR /usr/lib) -set(FCITX_INSTALL_PKGDATADIR /usr/share/fcitx5) -set(FCITX_INSTALL_LOCALEDIR /usr/share/locale) -set(LIBIME_INSTALL_PKGDATADIR table) +include("${CMAKE_CURRENT_SOURCE_DIR}/cmake/Fcitx5AndroidInstallDirs.cmake") set(CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake" ${CMAKE_MODULE_PATH}) diff --git a/lib/fcitx5/src/main/cpp/cmake/Fcitx5AndroidInstallDirs.cmake b/lib/fcitx5/src/main/cpp/cmake/Fcitx5AndroidInstallDirs.cmake new file mode 100644 index 000000000..c676a7922 --- /dev/null +++ b/lib/fcitx5/src/main/cpp/cmake/Fcitx5AndroidInstallDirs.cmake @@ -0,0 +1,14 @@ +set(CMAKE_INSTALL_PREFIX /usr) +# fcitx5/src/lib/fcitx-utils/Fcitx5UtilsConfig.cmake.in +set(FCITX_INSTALL_USE_FCITX_SYS_PATHS ON) +set(FCITX_INSTALL_PREFIX "/usr") +set(FCITX_INSTALL_INCLUDEDIR "/usr/include") +set(FCITX_INSTALL_LIBDIR "/usr/lib") +set(FCITX_INSTALL_LIBDATADIR "/usr/lib") +set(FCITX_INSTALL_LIBEXECDIR "/usr/lib") +set(FCITX_INSTALL_DATADIR "/usr/share") +set(FCITX_INSTALL_PKGDATADIR "/usr/share/fcitx5") +set(FCITX_INSTALL_BINDIR "/usr/bin") +set(FCITX_INSTALL_LOCALEDIR "/usr/share/locale") +set(FCITX_INSTALL_ADDONDIR "/usr/lib/fcitx5") +set(FCITX_INSTALL_MODULE_HEADER_DIR "/usr/include/Fcitx5/Module/fcitx-module") diff --git a/lib/fcitx5/src/main/cpp/cmake/FindFcitx5Utils.cmake b/lib/fcitx5/src/main/cpp/cmake/FindFcitx5Utils.cmake index 476feb970..e1131eab5 100644 --- a/lib/fcitx5/src/main/cpp/cmake/FindFcitx5Utils.cmake +++ b/lib/fcitx5/src/main/cpp/cmake/FindFcitx5Utils.cmake @@ -1,5 +1,4 @@ set(Fcitx5Utils_FOUND TRUE) -set(FCITX_INSTALL_CMAKECONFIG_DIR "${CMAKE_CURRENT_LIST_DIR}") # find prefab dependency find_package(fcitx5 REQUIRED CONFIG) @@ -11,6 +10,14 @@ endif() # fcitx5_translate_desktop_file needs ${GETTEXT_MSGFMT_EXECUTABLE} find_package(Gettext REQUIRED) + +# dependent projects usually use +# "${FCITX_INSTALL_CMAKECONFIG_DIR}/Fcitx5Utils/Fcitx5CompilerSettings.cmake" +# to locate Fcitx5CompilerSettings +set(FCITX_INSTALL_CMAKECONFIG_DIR "${CMAKE_CURRENT_LIST_DIR}") + +# mimic fcitx5/src/lib/fcitx-utils/Fcitx5UtilsConfig.cmake.in +include("${CMAKE_CURRENT_LIST_DIR}/Fcitx5AndroidInstallDirs.cmake") include("${CMAKE_CURRENT_LIST_DIR}/Fcitx5Utils/Fcitx5Macros.cmake") include(FindPackageHandleStandardArgs) @@ -18,5 +25,5 @@ find_package_handle_standard_args(Fcitx5Utils FOUND_VAR Fcitx5Utils_FOUND REQUIRED_VARS - FCITX_INSTALL_CMAKECONFIG_DIR + Fcitx5Utils_FOUND ) diff --git a/lib/fcitx5/src/main/cpp/cmake/FindLibIMEPinyin.cmake b/lib/fcitx5/src/main/cpp/cmake/FindLibIMEPinyin.cmake deleted file mode 100644 index 1f7685afe..000000000 --- a/lib/fcitx5/src/main/cpp/cmake/FindLibIMEPinyin.cmake +++ /dev/null @@ -1,9 +0,0 @@ -set(LibIMEPinyin_FOUND TRUE) - -include(FindPackageHandleStandardArgs) -find_package_handle_standard_args(LibIMEPinyin - FOUND_VAR - LibIMEPinyin_FOUND - REQUIRED_VARS - LibIMEPinyin_FOUND -) diff --git a/lib/fcitx5/src/main/cpp/cmake/FindLibIMETable.cmake b/lib/fcitx5/src/main/cpp/cmake/FindLibIMETable.cmake deleted file mode 100644 index 9a202119f..000000000 --- a/lib/fcitx5/src/main/cpp/cmake/FindLibIMETable.cmake +++ /dev/null @@ -1,9 +0,0 @@ -set(LibIMETable_FOUND TRUE) - -include(FindPackageHandleStandardArgs) -find_package_handle_standard_args(LibIMETable - FOUND_VAR - LibIMETable_FOUND - REQUIRED_VARS - LibIMETable_FOUND -) diff --git a/lib/libime/build.gradle.kts b/lib/libime/build.gradle.kts new file mode 100644 index 000000000..1284be4c8 --- /dev/null +++ b/lib/libime/build.gradle.kts @@ -0,0 +1,50 @@ +@file:Suppress("UnstableApiUsage") + +plugins { + id("org.fcitx.fcitx5.android.lib-convention") + id("org.fcitx.fcitx5.android.native-lib-convention") + id("org.fcitx.fcitx5.android.fcitx-headers") +} + +android { + namespace = "org.fcitx.fcitx5.android.lib.libime" + + defaultConfig { + externalNativeBuild { + cmake { + targets( + // dummy "cmake" target + "cmake", + // libime + "IMECore", + "IMEPinyin", + "IMETable" + ) + } + } + } + + prefab { + create("cmake") { + headerOnly = true + headers = "src/main/cpp/cmake" + } + val libimeHeadersPrefix = "build/headers/usr/include/LibIME" + create("IMECore") { + libraryName = "libIMECore" + headers = libimeHeadersPrefix + } + create("IMEPinyin") { + libraryName = "libIMEPinyin" + headers = libimeHeadersPrefix + } + create("IMETable") { + libraryName = "libIMETable" + headers = libimeHeadersPrefix + } + } +} + +dependencies { + implementation(project(":lib:fcitx5")) +} diff --git a/lib/libime/src/main/AndroidManifest.xml b/lib/libime/src/main/AndroidManifest.xml new file mode 100644 index 000000000..69fc41290 --- /dev/null +++ b/lib/libime/src/main/AndroidManifest.xml @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/lib/libime/src/main/cpp/CMakeLists.txt b/lib/libime/src/main/cpp/CMakeLists.txt new file mode 100644 index 000000000..be182fd71 --- /dev/null +++ b/lib/libime/src/main/cpp/CMakeLists.txt @@ -0,0 +1,39 @@ +cmake_minimum_required(VERSION 3.18) + +project(fcitx5-android-lib-fcitx5-chinese-addons VERSION 0.0.7) + +# For reproducible build +add_link_options("LINKER:--hash-style=gnu,--build-id=none") + +# prefab dependency +find_package(fcitx5 REQUIRED CONFIG) +get_target_property(FCITX5_CMAKE_MODULES fcitx5::cmake INTERFACE_INCLUDE_DIRECTORIES) +set(CMAKE_MODULE_PATH ${FCITX5_CMAKE_MODULES} ${CMAKE_MODULE_PATH}) + +find_package(ECM MODULE) +find_package(Fcitx5Utils MODULE) + +# dummy target to export src/main/cpp/cmake +add_custom_target(cmake) + +# prebuilt dir. at least it works. +set(PREBUILT_DIR "${CMAKE_CURRENT_SOURCE_DIR}/../../../../fcitx5/src/main/cpp/prebuilt") + +# prebuilt fmt +set(fmt_DIR "${PREBUILT_DIR}/fmt/${ANDROID_ABI}/lib/cmake/fmt") +find_package(fmt) + +# prebuilt boost +set(BOOST_VERSION "1.80.0") +set(BOOST_MODULES headers filesystem atomic iostreams) +set(BOOST_ROOT "${PREBUILT_DIR}/boost/${ANDROID_ABI}") +set(Boost_DIR "${BOOST_ROOT}/lib/cmake/Boost-${BOOST_VERSION}") +foreach (mod IN LISTS BOOST_MODULES) + set("boost_${mod}_DIR" "${BOOST_ROOT}/lib/cmake/boost_${mod}-${BOOST_VERSION}") +endforeach () + +set(LIBIME_INSTALL_PKGDATADIR table) +option(ENABLE_TEST "" OFF) +add_subdirectory(libime) +# kenlm/util/exception.hh uses __FILE__ macro +target_compile_options(kenlm PRIVATE "-ffile-prefix-map=${CMAKE_CURRENT_SOURCE_DIR}/libime/src/libime/core/kenlm=.") diff --git a/lib/libime/src/main/cpp/cmake/FindLibIMECore.cmake b/lib/libime/src/main/cpp/cmake/FindLibIMECore.cmake new file mode 100644 index 000000000..cac00a021 --- /dev/null +++ b/lib/libime/src/main/cpp/cmake/FindLibIMECore.cmake @@ -0,0 +1,23 @@ +set(LibIMECore_FOUND TRUE) + +# find prefab dependency +find_package(fcitx5 REQUIRED CONFIG) +find_package(libime REQUIRED CONFIG) + +if (NOT TARGET LibIME::Core) + # fix target dependency + set_target_properties(libime::IMECore PROPERTIES INTERFACE_LINK_LIBRARIES fcitx5::Fcitx5Utils) + # fix target name + add_library(LibIME::Core ALIAS libime::IMECore) +endif() + +# libime/src/libime/core/LibIMECoreConfig.cmake.in +set(LIBIME_INSTALL_PKGDATADIR table) + +include(FindPackageHandleStandardArgs) +find_package_handle_standard_args(LibIMECore + FOUND_VAR + LibIMECore_FOUND + REQUIRED_VARS + LibIMECore_FOUND +) diff --git a/lib/libime/src/main/cpp/cmake/FindLibIMEPinyin.cmake b/lib/libime/src/main/cpp/cmake/FindLibIMEPinyin.cmake new file mode 100644 index 000000000..2d6a9cf72 --- /dev/null +++ b/lib/libime/src/main/cpp/cmake/FindLibIMEPinyin.cmake @@ -0,0 +1,21 @@ +set(LibIMEPinyin_FOUND TRUE) + +# find prefab dependency +find_package(fcitx5 REQUIRED CONFIG) +find_package(libime REQUIRED CONFIG) + +if (NOT TARGET LibIME::Pinyin) + # fix target dependency + set_target_properties(libime::IMEPinyin PROPERTIES INTERFACE_LINK_LIBRARIES fcitx5::Fcitx5Utils) + set_target_properties(libime::IMEPinyin PROPERTIES INTERFACE_LINK_LIBRARIES libime::IMECore) + # fix target name + add_library(LibIME::Pinyin ALIAS libime::IMEPinyin) +endif() + +include(FindPackageHandleStandardArgs) +find_package_handle_standard_args(LibIMEPinyin + FOUND_VAR + LibIMEPinyin_FOUND + REQUIRED_VARS + LibIMEPinyin_FOUND +) diff --git a/lib/libime/src/main/cpp/cmake/FindLibIMETable.cmake b/lib/libime/src/main/cpp/cmake/FindLibIMETable.cmake new file mode 100644 index 000000000..75b63140b --- /dev/null +++ b/lib/libime/src/main/cpp/cmake/FindLibIMETable.cmake @@ -0,0 +1,21 @@ +set(LibIMETable_FOUND TRUE) + +# find prefab dependency +find_package(fcitx5 REQUIRED CONFIG) +find_package(libime REQUIRED CONFIG) + +if (NOT TARGET LibIME::Table) + # fix target dependency + set_target_properties(libime::IMETable PROPERTIES INTERFACE_LINK_LIBRARIES fcitx5::Fcitx5Utils) + set_target_properties(libime::IMEPinyin PROPERTIES INTERFACE_LINK_LIBRARIES libime::IMECore) + # fix target name + add_library(LibIME::Table ALIAS libime::IMETable) +endif() + +include(FindPackageHandleStandardArgs) +find_package_handle_standard_args(LibIMETable + FOUND_VAR + LibIMETable_FOUND + REQUIRED_VARS + LibIMETable_FOUND +) diff --git a/lib/libime/src/main/cpp/libime b/lib/libime/src/main/cpp/libime new file mode 160000 index 000000000..32ed369c2 --- /dev/null +++ b/lib/libime/src/main/cpp/libime @@ -0,0 +1 @@ +Subproject commit 32ed369c24b244b6cd85b4346d6bd2a5d1df8769 diff --git a/plugin/anthy/build.gradle.kts b/plugin/anthy/build.gradle.kts index ce0ba2822..726c97d01 100644 --- a/plugin/anthy/build.gradle.kts +++ b/plugin/anthy/build.gradle.kts @@ -37,13 +37,7 @@ android { jniLibs { excludes += setOf( "**/libc++_shared.so", - "**/libFcitx5*", - "**/libclipboard.so", - "**/libimselector.so", - "**/libquickphase.so", - "**/libspell.so", - "**/libtest*", - "**/libunicode.so", + "**/libFcitx5*" ) } } diff --git a/plugin/anthy/src/main/cpp/CMakeLists.txt b/plugin/anthy/src/main/cpp/CMakeLists.txt index babc87b54..b6f6a4234 100644 --- a/plugin/anthy/src/main/cpp/CMakeLists.txt +++ b/plugin/anthy/src/main/cpp/CMakeLists.txt @@ -10,11 +10,13 @@ find_package(fcitx5 REQUIRED CONFIG) get_target_property(FCITX5_CMAKE_MODULES fcitx5::cmake INTERFACE_INCLUDE_DIRECTORIES) set(CMAKE_MODULE_PATH ${FCITX5_CMAKE_MODULES} ${CMAKE_MODULE_PATH}) -# install prefix for addon conf and locale -set(FCITX_INSTALL_PKGDATADIR /usr/share/fcitx5) -set(FCITX_INSTALL_LOCALEDIR /usr/share/locale) +find_package(ECM MODULE) +find_package(Fcitx5Core MODULE) +find_package(Fcitx5Module MODULE) set(BUILD_SHARED_LIBS OFF) +# anthy have some GCC pragma that LLVM doesn't recognize +add_compile_options("-Wno-unknown-warning-option") add_subdirectory(anthy-cmake) unset(BUILD_SHARED_LIBS) diff --git a/plugin/unikey/build.gradle.kts b/plugin/unikey/build.gradle.kts new file mode 100644 index 000000000..151a77c07 --- /dev/null +++ b/plugin/unikey/build.gradle.kts @@ -0,0 +1,53 @@ +@file:Suppress("UnstableApiUsage") + +plugins { + id("org.fcitx.fcitx5.android.app-convention") + id("org.fcitx.fcitx5.android.plugin-app-convention") + id("org.fcitx.fcitx5.android.native-app-convention") + id("org.fcitx.fcitx5.android.build-metadata") + id("org.fcitx.fcitx5.android.data-descriptor") + id("org.fcitx.fcitx5.android.fcitx-component") +} + +android { + namespace = "org.fcitx.fcitx5.android.plugin.unikey" + + defaultConfig { + applicationId = "org.fcitx.fcitx5.android.plugin.unikey" + + externalNativeBuild { + cmake { + targets( + "unikey" + ) + } + } + } + + buildTypes { + release { + resValue("string", "app_name", "@string/app_name_release") + } + debug { + resValue("string", "app_name", "@string/app_name_debug") + } + } + + packaging { + jniLibs { + excludes += setOf( + "**/libc++_shared.so", + "**/libFcitx5*" + ) + } + } +} + +aboutLibraries { + configPath = "plugin/unikey/licenses" +} + +dependencies { + implementation(project(":lib:fcitx5")) + implementation(project(":lib:plugin-base")) +} diff --git a/app/licenses/libraries/fcitx5-unikey.json b/plugin/unikey/licenses/libraries/fcitx5-unikey.json similarity index 100% rename from app/licenses/libraries/fcitx5-unikey.json rename to plugin/unikey/licenses/libraries/fcitx5-unikey.json diff --git a/plugin/unikey/src/main/AndroidManifest.xml b/plugin/unikey/src/main/AndroidManifest.xml new file mode 100644 index 000000000..654a95e5f --- /dev/null +++ b/plugin/unikey/src/main/AndroidManifest.xml @@ -0,0 +1,6 @@ + + + + + + diff --git a/plugin/unikey/src/main/cpp/CMakeLists.txt b/plugin/unikey/src/main/cpp/CMakeLists.txt new file mode 100644 index 000000000..bc45463b7 --- /dev/null +++ b/plugin/unikey/src/main/cpp/CMakeLists.txt @@ -0,0 +1,21 @@ +cmake_minimum_required(VERSION 3.18) + +project(fcitx5-android-lib-fcitx5-lua VERSION 0.0.7) + +# For reproducible build +add_link_options("LINKER:--hash-style=gnu,--build-id=none") + +# prefab dependency +find_package(fcitx5 REQUIRED CONFIG) +get_target_property(FCITX5_CMAKE_MODULES fcitx5::cmake INTERFACE_INCLUDE_DIRECTORIES) +set(CMAKE_MODULE_PATH ${FCITX5_CMAKE_MODULES} ${CMAKE_MODULE_PATH}) + +find_package(ECM MODULE) +find_package(Fcitx5Core MODULE) +find_package(Fcitx5Module MODULE) + +option(ENABLE_TEST "" OFF) +option(ENABLE_QT "" OFF) +add_subdirectory(fcitx5-unikey) +# suppress "illegal character encoding in character literal" warning in unikey/data.cpp +target_compile_options(unikey-lib PRIVATE "-Wno-invalid-source-encoding") diff --git a/app/src/main/cpp/fcitx5-unikey b/plugin/unikey/src/main/cpp/fcitx5-unikey similarity index 100% rename from app/src/main/cpp/fcitx5-unikey rename to plugin/unikey/src/main/cpp/fcitx5-unikey diff --git a/plugin/unikey/src/main/res/values/strings.xml b/plugin/unikey/src/main/res/values/strings.xml new file mode 100644 index 000000000..9c54c954b --- /dev/null +++ b/plugin/unikey/src/main/res/values/strings.xml @@ -0,0 +1,6 @@ + + + Fcitx5 for Android (Unikey Plugin | Debug) + Fcitx5 for Android (Unikey Plugin) + Unikey (Vietnamese Input Method) engine support for Fcitx5 + \ No newline at end of file diff --git a/plugin/unikey/src/main/res/xml/plugin.xml b/plugin/unikey/src/main/res/xml/plugin.xml new file mode 100644 index 000000000..303b0b271 --- /dev/null +++ b/plugin/unikey/src/main/res/xml/plugin.xml @@ -0,0 +1,6 @@ + + + 0.1 + fcitx5-unikey + @string/description + \ No newline at end of file diff --git a/settings.gradle.kts b/settings.gradle.kts index c2c77a147..5b28fe259 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -21,8 +21,12 @@ rootProject.name = "fcitx5-android" include(":lib:common") include(":lib:fcitx5") +include(":lib:fcitx5-lua") +include(":lib:libime") +include(":lib:fcitx5-chinese-addons") include(":codegen") include(":app") include(":lib:plugin-base") include(":plugin:anthy") include(":plugin:clipboard-filter") +include(":plugin:unikey") From 3f508ac35a3fecadda3050ed4a0358326c783ec5 Mon Sep 17 00:00:00 2001 From: Rocka Date: Tue, 29 Aug 2023 00:42:10 +0800 Subject: [PATCH 004/381] Fix libFcitx5Utils.so reproducibility --- lib/fcitx5/src/main/cpp/CMakeLists.txt | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/lib/fcitx5/src/main/cpp/CMakeLists.txt b/lib/fcitx5/src/main/cpp/CMakeLists.txt index aa9e05ce8..2948844a5 100644 --- a/lib/fcitx5/src/main/cpp/CMakeLists.txt +++ b/lib/fcitx5/src/main/cpp/CMakeLists.txt @@ -10,8 +10,6 @@ add_custom_target(cmake) # dummy target to export "notifications_public.h" add_custom_target(notifications) -include("${CMAKE_CURRENT_SOURCE_DIR}/cmake/Fcitx5AndroidInstallDirs.cmake") - set(CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake" ${CMAKE_MODULE_PATH}) # cmake/FindECM.cmake @@ -32,6 +30,10 @@ find_package(LibIntl) set(Libevent_DIR "${PREBUILT_DIR}/libevent/${ANDROID_ABI}/lib/cmake/libevent") find_package(Libevent) +include("${CMAKE_CURRENT_SOURCE_DIR}/cmake/Fcitx5AndroidInstallDirs.cmake") +# LIBDATADIR is not a standard CMake install dir, defined in fcitx5/CMakeLists.txt#L169 +set(CMAKE_INSTALL_LIBDATADIR "/usr/lib") + option(ENABLE_TEST "" OFF) option(ENABLE_COVERAGE "" OFF) option(ENABLE_ENCHANT "" OFF) From 03b7f44289f3ee39d0d711c7d041ad17ae528275 Mon Sep 17 00:00:00 2001 From: Rocka Date: Tue, 29 Aug 2023 14:53:51 +0800 Subject: [PATCH 005/381] Fix quick phrase importing --- .../data/quickphrase/QuickPhraseData.kt | 16 +++++++------- .../data/quickphrase/QuickPhraseManager.kt | 21 +++++++------------ .../main/settings/QuickPhraseListFragment.kt | 4 ++-- 3 files changed, 17 insertions(+), 24 deletions(-) diff --git a/app/src/main/java/org/fcitx/fcitx5/android/data/quickphrase/QuickPhraseData.kt b/app/src/main/java/org/fcitx/fcitx5/android/data/quickphrase/QuickPhraseData.kt index 108462a0c..3c108e1d3 100644 --- a/app/src/main/java/org/fcitx/fcitx5/android/data/quickphrase/QuickPhraseData.kt +++ b/app/src/main/java/org/fcitx/fcitx5/android/data/quickphrase/QuickPhraseData.kt @@ -11,15 +11,13 @@ class QuickPhraseData(private val data: List) : companion object { fun fromLines(lines: List): Result = runCatching { - lines - .mapNotNull { - it.trim().takeIf(String::isNotEmpty)?.let { l -> - val key = l.substringBefore(' ') - val value = l.substringAfter(' ') - if (key.isEmpty() || value.isEmpty()) - errorRuntime(R.string.exception_quickphrase_parse, it) - QuickPhraseEntry(key, value) - } + lines.filter { it.isNotBlank() } + .map { + val s = it.trim() + val spaceIndex = s.indexOf(' ') + if (spaceIndex < 0) + errorRuntime(R.string.exception_quickphrase_parse, it) + QuickPhraseEntry(s.substring(0, spaceIndex), s.substring(spaceIndex + 1)) } }.map { QuickPhraseData(it) } } diff --git a/app/src/main/java/org/fcitx/fcitx5/android/data/quickphrase/QuickPhraseManager.kt b/app/src/main/java/org/fcitx/fcitx5/android/data/quickphrase/QuickPhraseManager.kt index b9e34b120..b43b5f09d 100644 --- a/app/src/main/java/org/fcitx/fcitx5/android/data/quickphrase/QuickPhraseManager.kt +++ b/app/src/main/java/org/fcitx/fcitx5/android/data/quickphrase/QuickPhraseManager.kt @@ -1,9 +1,8 @@ package org.fcitx.fcitx5.android.data.quickphrase -import org.fcitx.fcitx5.android.R import org.fcitx.fcitx5.android.core.data.DataManager import org.fcitx.fcitx5.android.utils.appContext -import org.fcitx.fcitx5.android.utils.errorArg +import org.fcitx.fcitx5.android.utils.withTempDir import java.io.File import java.io.InputStream @@ -33,9 +32,7 @@ object QuickPhraseManager { return CustomQuickPhrase(file) } - fun importFromFile(file: File): Result { - if (file.extension != QuickPhrase.EXT) - errorArg(R.string.exception_quickphrase_filename, file.path) + private fun importFromFile(file: File): Result { // throw away data, only ensuring the format is correct return QuickPhraseData.fromLines(file.readLines()).map { val dest = File(customQuickPhraseDir, file.name) @@ -44,15 +41,13 @@ object QuickPhraseManager { } } - fun importFromInputStream(stream: InputStream, name: String): Result { - return stream.use { inputStream -> - val tempFile = File(appContext.cacheDir, name) - tempFile.outputStream().use { outputStream -> - inputStream.copyTo(outputStream) + fun importFromInputStream(stream: InputStream, fileName: String): Result { + return stream.use { i -> + withTempDir { dir -> + val tempFile = dir.resolve(fileName) + tempFile.outputStream().use { o -> i.copyTo(o) } + importFromFile(tempFile) } - val new = importFromFile(tempFile) - tempFile.delete() - return@use new } } diff --git a/app/src/main/java/org/fcitx/fcitx5/android/ui/main/settings/QuickPhraseListFragment.kt b/app/src/main/java/org/fcitx/fcitx5/android/ui/main/settings/QuickPhraseListFragment.kt index 25be22fa3..05e57c0c2 100644 --- a/app/src/main/java/org/fcitx/fcitx5/android/ui/main/settings/QuickPhraseListFragment.kt +++ b/app/src/main/java/org/fcitx/fcitx5/android/ui/main/settings/QuickPhraseListFragment.kt @@ -209,7 +209,7 @@ class QuickPhraseListFragment : Fragment(), OnItemChangedListener { val fileName = cr.queryFileName(uri) ?: return@launch val extName = fileName.substringAfterLast('.') if (extName != QuickPhrase.EXT) { - importErrorDialog(getString(R.string.invalid_quickphrase)) + importErrorDialog(getString(R.string.exception_quickphrase_filename, fileName)) return@launch } val entryName = fileName.substringBeforeLast('.') @@ -227,7 +227,7 @@ class QuickPhraseListFragment : Fragment(), OnItemChangedListener { .build().let { nm.notify(id, it) } try { val inputStream = cr.openInputStream(uri)!! - val imported = QuickPhraseManager.importFromInputStream(inputStream, entryName) + val imported = QuickPhraseManager.importFromInputStream(inputStream, fileName) .getOrThrow() withContext(Dispatchers.Main) { ui.addItem(item = imported) From b64b7bfe322275b83c6b7fca5b97e835cf9f8c42 Mon Sep 17 00:00:00 2001 From: Potato Hatsue <1793913507@qq.com> Date: Tue, 29 Aug 2023 20:47:30 -0400 Subject: [PATCH 006/381] nix: enable cachix in github actions --- .github/workflows/nix.yml | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/.github/workflows/nix.yml b/.github/workflows/nix.yml index 569112040..7ce23ac77 100644 --- a/.github/workflows/nix.yml +++ b/.github/workflows/nix.yml @@ -18,9 +18,11 @@ jobs: submodules: recursive - uses: cachix/install-nix-action@v22 with: - nix_path: nixpkgs=channel:nixos-unstable - - name: Build dev shell - run: nix develop .#noAS + github_access_token: ${{ secrets.GITHUB_TOKEN }} + - uses: cachix/cachix-action@v12 + with: + name: fcitx5-android + authToken: "${{ secrets.CACHIX_AUTH_TOKEN }}" - name: Build Debug APK run: | nix develop .#noAS --command ./gradlew :app:assembleDebug From 233c820bd014b83e3fe554f854c12487e3853408 Mon Sep 17 00:00:00 2001 From: Potato Hatsue <1793913507@qq.com> Date: Tue, 29 Aug 2023 20:50:00 -0400 Subject: [PATCH 007/381] nix: set ANDROID_HOME to ANDROID_SDK_ROOT --- flake.nix | 1 + 1 file changed, 1 insertion(+) diff --git a/flake.nix b/flake.nix index 217acf92e..14cbd851c 100644 --- a/flake.nix +++ b/flake.nix @@ -61,6 +61,7 @@ ]; ANDROID_SDK_ROOT = "${androidComposition.androidsdk}/libexec/android-sdk"; + ANDROID_HOME = ANDROID_SDK_ROOT; NDK_VERSION = ndkVersion; BUILD_TOOLS_VERSION = buildToolsVersion; GRADLE_OPTS = From f86b3bbc83a193aa5cf2b5249125bec268e5ede8 Mon Sep 17 00:00:00 2001 From: Potato Hatsue <1793913507@qq.com> Date: Tue, 29 Aug 2023 20:57:33 -0400 Subject: [PATCH 008/381] nix: fix recursion --- flake.nix | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/flake.nix b/flake.nix index 14cbd851c..43201a07a 100644 --- a/flake.nix +++ b/flake.nix @@ -50,7 +50,7 @@ ({ androidStudio, generateLocalProperties }: with final; with self; - mkShell { + mkShell rec { buildInputs = [ androidComposition.androidsdk extra-cmake-modules From a83db73588b13d95f1369dc285071cb16454c4a0 Mon Sep 17 00:00:00 2001 From: Rocka Date: Wed, 30 Aug 2023 16:41:27 +0800 Subject: [PATCH 009/381] Update fcitx5 submodules --- lib/fcitx5-chinese-addons/src/main/cpp/fcitx5-chinese-addons | 2 +- lib/fcitx5/src/main/cpp/fcitx5 | 2 +- lib/libime/src/main/cpp/libime | 2 +- plugin/unikey/src/main/cpp/fcitx5-unikey | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/fcitx5-chinese-addons/src/main/cpp/fcitx5-chinese-addons b/lib/fcitx5-chinese-addons/src/main/cpp/fcitx5-chinese-addons index 37592053f..a33d09dc2 160000 --- a/lib/fcitx5-chinese-addons/src/main/cpp/fcitx5-chinese-addons +++ b/lib/fcitx5-chinese-addons/src/main/cpp/fcitx5-chinese-addons @@ -1 +1 @@ -Subproject commit 37592053f8ba71f08dde2058c7763f28809e401e +Subproject commit a33d09dc21ec0619beecb7ec874611f3d3a90938 diff --git a/lib/fcitx5/src/main/cpp/fcitx5 b/lib/fcitx5/src/main/cpp/fcitx5 index 4182bb2b7..018d91b81 160000 --- a/lib/fcitx5/src/main/cpp/fcitx5 +++ b/lib/fcitx5/src/main/cpp/fcitx5 @@ -1 +1 @@ -Subproject commit 4182bb2b7024e84581864d25bfea270d2e247ffb +Subproject commit 018d91b81f6c5745641b7a0ea85332d1d9a642c1 diff --git a/lib/libime/src/main/cpp/libime b/lib/libime/src/main/cpp/libime index 32ed369c2..bf1735cd9 160000 --- a/lib/libime/src/main/cpp/libime +++ b/lib/libime/src/main/cpp/libime @@ -1 +1 @@ -Subproject commit 32ed369c24b244b6cd85b4346d6bd2a5d1df8769 +Subproject commit bf1735cd993ba55797b17728a16555a78b6e8b41 diff --git a/plugin/unikey/src/main/cpp/fcitx5-unikey b/plugin/unikey/src/main/cpp/fcitx5-unikey index 0fefefd37..7e1b67388 160000 --- a/plugin/unikey/src/main/cpp/fcitx5-unikey +++ b/plugin/unikey/src/main/cpp/fcitx5-unikey @@ -1 +1 @@ -Subproject commit 0fefefd37367d2372c5f99da1c8d4123adf6581a +Subproject commit 7e1b673885c133dab948fa465b7af00fa788a240 From 3048deaab285fa905406c87dbb658146f1c6f5c5 Mon Sep 17 00:00:00 2001 From: Qijia Liu Date: Fri, 8 Sep 2023 01:16:31 -0400 Subject: [PATCH 010/381] Set default ECM_DIR to homebrew on macOS (#331) --- lib/fcitx5/src/main/cpp/cmake/FindECM.cmake | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lib/fcitx5/src/main/cpp/cmake/FindECM.cmake b/lib/fcitx5/src/main/cpp/cmake/FindECM.cmake index 1f28cf5bc..a4483c5c2 100644 --- a/lib/fcitx5/src/main/cpp/cmake/FindECM.cmake +++ b/lib/fcitx5/src/main/cpp/cmake/FindECM.cmake @@ -1,5 +1,7 @@ if(DEFINED ENV{ECM_DIR}) set(ECM_DIR $ENV{ECM_DIR}) +elseif(CMAKE_HOST_APPLE) + set(ECM_DIR /opt/homebrew/share/ECM/cmake) else() set(ECM_DIR /usr/share/ECM/cmake) endif() From 8028cd9a33e0cdca98ecbdf950a1ca8095eef10a Mon Sep 17 00:00:00 2001 From: Qijia Liu Date: Sat, 9 Sep 2023 06:14:53 -0400 Subject: [PATCH 011/381] Add non-nix macOS CI (#332) --- .github/workflows/pull_request.yml | 19 ++++++++++++++----- lib/fcitx5/src/main/cpp/cmake/FindECM.cmake | 6 +++++- 2 files changed, 19 insertions(+), 6 deletions(-) diff --git a/.github/workflows/pull_request.yml b/.github/workflows/pull_request.yml index 7291ccae1..f226cf92b 100644 --- a/.github/workflows/pull_request.yml +++ b/.github/workflows/pull_request.yml @@ -10,9 +10,12 @@ on: jobs: build_pull_request: - runs-on: ubuntu-22.04 + runs-on: ${{ matrix.os }} strategy: matrix: + os: + - ubuntu-22.04 + - macos-13 abi: - armeabi-v7a - arm64-v8a @@ -22,7 +25,7 @@ jobs: BUILD_ABI: ${{ matrix.abi }} steps: - name: Fetch source code - uses: actions/checkout@v3 + uses: actions/checkout@v4 with: fetch-depth: 0 submodules: recursive @@ -40,11 +43,17 @@ jobs: run: | sdkmanager --install "cmake;3.22.1" - - name: Install system dependencies + - name: Install system dependencies (Ubuntu) + if: ${{ matrix.os == 'ubuntu-22.04' }} run: | sudo apt update sudo apt install extra-cmake-modules gettext + - name: Install system dependencies (macOS) + if: ${{ matrix.os == 'macos-13' }} + run: | + brew install extra-cmake-modules + - name: Setup Gradle uses: gradle/gradle-build-action@v2 @@ -56,7 +65,7 @@ jobs: - name: Upload app uses: actions/upload-artifact@v3 with: - name: app-${{ matrix.abi }} + name: app-${{ matrix.os }}-${{ matrix.abi }} path: app/build/outputs/apk/debug/ - name: Pack plugins @@ -73,5 +82,5 @@ jobs: - name: Upload plugins uses: actions/upload-artifact@v3 with: - name: plugins-${{ matrix.abi }} + name: plugins-${{ matrix.os }}-${{ matrix.abi }} path: plugins-to-upload diff --git a/lib/fcitx5/src/main/cpp/cmake/FindECM.cmake b/lib/fcitx5/src/main/cpp/cmake/FindECM.cmake index a4483c5c2..f643babe0 100644 --- a/lib/fcitx5/src/main/cpp/cmake/FindECM.cmake +++ b/lib/fcitx5/src/main/cpp/cmake/FindECM.cmake @@ -1,7 +1,11 @@ if(DEFINED ENV{ECM_DIR}) set(ECM_DIR $ENV{ECM_DIR}) elseif(CMAKE_HOST_APPLE) - set(ECM_DIR /opt/homebrew/share/ECM/cmake) + if(CMAKE_HOST_SYSTEM_PROCESSOR STREQUAL "arm64") + set(ECM_DIR /opt/homebrew/share/ECM/cmake) + else() + set(ECM_DIR /usr/local/share/ECM/cmake) + endif() else() set(ECM_DIR /usr/share/ECM/cmake) endif() From 8c8860a2620a6ba5fdd16289ee0506d0e02e3772 Mon Sep 17 00:00:00 2001 From: Qijia Liu Date: Sun, 10 Sep 2023 11:38:23 -0400 Subject: [PATCH 012/381] Add Windows CI (#333) Co-authored-by: Rocka --- .github/workflows/pull_request.yml | 14 +++++++++ README.md | 33 ++++++++++++++++++++- lib/fcitx5/src/main/cpp/cmake/FindECM.cmake | 2 ++ 3 files changed, 48 insertions(+), 1 deletion(-) diff --git a/.github/workflows/pull_request.yml b/.github/workflows/pull_request.yml index f226cf92b..ca094c8c8 100644 --- a/.github/workflows/pull_request.yml +++ b/.github/workflows/pull_request.yml @@ -16,6 +16,7 @@ jobs: os: - ubuntu-22.04 - macos-13 + - windows-2022 abi: - armeabi-v7a - arm64-v8a @@ -30,6 +31,12 @@ jobs: fetch-depth: 0 submodules: recursive + - name: Regenerate symlinks pointing to submodule (Windows) + if: ${{ matrix.os == 'windows-2022' }} + run: | + Remove-Item -Recurse app/src/main/assets/usr/share + git checkout -- * + - name: Setup Java uses: actions/setup-java@v3 with: @@ -54,6 +61,12 @@ jobs: run: | brew install extra-cmake-modules + - name: Install system dependencies (Windows) + if: ${{ matrix.os == 'windows-2022' }} + run: | + C:/msys64/usr/bin/pacman -S --noconfirm mingw-w64-ucrt-x86_64-gettext mingw-w64-ucrt-x86_64-extra-cmake-modules + Add-Content $env:GITHUB_PATH "C:/msys64/ucrt64/bin" + - name: Setup Gradle uses: gradle/gradle-build-action@v2 @@ -69,6 +82,7 @@ jobs: path: app/build/outputs/apk/debug/ - name: Pack plugins + shell: bash run: | mkdir plugins-to-upload for i in $(ls plugin) diff --git a/README.md b/README.md index c786c7b44..51894ca6b 100644 --- a/README.md +++ b/README.md @@ -69,6 +69,19 @@ Discuss on Telegram: https://t.me/+hci-DrFVWUM3NTUx ([@fcitx5_android](https://t ### How to set up development environment +
+Prerequisites for Windows + +- Enable [Developer Mode](https://learn.microsoft.com/en-us/windows/apps/get-started/enable-your-device-for-development) so that symlinks can be created without administrator privilege. + +- Enable symlink support for `git`: + + ```powershell + git config --global core.symlinks true + ``` + +
+ First, clone this repository and fetch all submodules: ```sh @@ -76,13 +89,31 @@ git clone git@github.com:fcitx5-android/fcitx5-android.git git submodule update --init --recursive ``` -Install extra-cmake-modules from your distribution software repository: +
+On Windows, you may need to regenerate symlinks to submodules. + +```powershell +Remove-Item -Recurse app/src/main/assets/usr/share +git checkout -- . +``` + +
+ +Install `extra-cmake-modules` and `gettext` with your system package manager: ```sh # For Arch Linux (Arch has gettext in it's base meta package) sudo pacman -S extra-cmake-modules + # For Debian/Ubuntu sudo apt install extra-cmake-modules gettext + +# For macOS +brew install extra-cmake-modules gettext + +# For Windows, install MSYS2 and execute in its shell (UCRT64) +pacman -S mingw-w64-ucrt-x86_64-extra-cmake-modules mingw-w64-ucrt-x86_64-gettext +# then add C:/msys64/ucrt64/bin to PATH ``` Install Android SDK Platform, Android SDK Build-Tools, Android NDK and cmake via SDK Manager in Android Studio: diff --git a/lib/fcitx5/src/main/cpp/cmake/FindECM.cmake b/lib/fcitx5/src/main/cpp/cmake/FindECM.cmake index f643babe0..273bb6605 100644 --- a/lib/fcitx5/src/main/cpp/cmake/FindECM.cmake +++ b/lib/fcitx5/src/main/cpp/cmake/FindECM.cmake @@ -1,5 +1,7 @@ if(DEFINED ENV{ECM_DIR}) set(ECM_DIR $ENV{ECM_DIR}) +elseif(CMAKE_HOST_WIN32) + set(ECM_DIR "C:/msys64/ucrt64/share/ECM/cmake") elseif(CMAKE_HOST_APPLE) if(CMAKE_HOST_SYSTEM_PROCESSOR STREQUAL "arm64") set(ECM_DIR /opt/homebrew/share/ECM/cmake) From 6a4ef8fa32509221054ad5d6770375283b43ebf7 Mon Sep 17 00:00:00 2001 From: Rocka Date: Fri, 15 Sep 2023 12:58:27 +0800 Subject: [PATCH 013/381] Don't switch to newly imported theme when follow system dark mode --- .../android/ui/main/settings/theme/ThemeListFragment.kt | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/app/src/main/java/org/fcitx/fcitx5/android/ui/main/settings/theme/ThemeListFragment.kt b/app/src/main/java/org/fcitx/fcitx5/android/ui/main/settings/theme/ThemeListFragment.kt index f55f25d98..88494b122 100644 --- a/app/src/main/java/org/fcitx/fcitx5/android/ui/main/settings/theme/ThemeListFragment.kt +++ b/app/src/main/java/org/fcitx/fcitx5/android/ui/main/settings/theme/ThemeListFragment.kt @@ -81,7 +81,9 @@ class ThemeListFragment : Fragment() { val theme = result.theme adapter.prependTheme(theme) ThemeManager.saveTheme(theme) - ThemeManager.switchTheme(theme) + if (!followSystemDayNightTheme) { + ThemeManager.switchTheme(theme) + } } is CustomThemeActivity.BackgroundResult.Deleted -> { val name = result.name From 80f72a391f7dc38e5e4c46d3826423763e2a9922 Mon Sep 17 00:00:00 2001 From: Rocka Date: Fri, 15 Sep 2023 13:00:53 +0800 Subject: [PATCH 014/381] Preserve theme colors after entering CustomThemeActivity attach OnCheckedChangeListener after calling setChecked --- .../settings/theme/CustomThemeActivity.kt | 49 ++++++++++--------- 1 file changed, 25 insertions(+), 24 deletions(-) diff --git a/app/src/main/java/org/fcitx/fcitx5/android/ui/main/settings/theme/CustomThemeActivity.kt b/app/src/main/java/org/fcitx/fcitx5/android/ui/main/settings/theme/CustomThemeActivity.kt index 29e468141..dd7179aa6 100644 --- a/app/src/main/java/org/fcitx/fcitx5/android/ui/main/settings/theme/CustomThemeActivity.kt +++ b/app/src/main/java/org/fcitx/fcitx5/android/ui/main/settings/theme/CustomThemeActivity.kt @@ -106,7 +106,10 @@ class CustomThemeActivity : AppCompatActivity() { createTextView(R.string.dark_keys, ripple = true) } private val variantSwitch by lazy { - switch { } + switch { + // Use dark keys by default + isChecked = false + } } private val brightnessLabel by lazy { @@ -253,31 +256,10 @@ class CustomThemeActivity : AppCompatActivity() { croppedImageFile = c srcImageFile = s } - theme = - (if (variantSwitch.isChecked) ThemePreset.TransparentLight else ThemePreset.TransparentDark) - .deriveCustomBackground(n, c.path, s.path) + // Use dark keys by default + theme = ThemePreset.TransparentDark.deriveCustomBackground(n, c.path, s.path) } previewUi = KeyboardPreviewUi(this, theme) - whenHasBackground { - cropLabel.setOnClickListener { - launchCrop(previewUi.intrinsicWidth, previewUi.intrinsicHeight) - } - variantLabel.setOnClickListener { - variantSwitch.isChecked = !variantSwitch.isChecked - } - variantSwitch.setOnCheckedChangeListener { _, isChecked -> - setKeyVariant(it, darkKeys = isChecked) - } - brightnessSeekBar.setOnSeekBarChangeListener(object : - SeekBar.OnSeekBarChangeListener { - override fun onStartTrackingTouch(bar: SeekBar) {} - override fun onStopTrackingTouch(bar: SeekBar) {} - - override fun onProgressChanged(bar: SeekBar, progress: Int, fromUser: Boolean) { - if (fromUser) updateState() - } - }) - } if (theme.backgroundImage == null) { brightnessLabel.visibility = View.GONE cropLabel.visibility = View.GONE @@ -330,6 +312,25 @@ class CustomThemeActivity : AppCompatActivity() { updateState() } } + cropLabel.setOnClickListener { + launchCrop(previewUi.intrinsicWidth, previewUi.intrinsicHeight) + } + variantLabel.setOnClickListener { + variantSwitch.isChecked = !variantSwitch.isChecked + } + // attach OnCheckedChangeListener after calling setChecked (isChecked in kotlin) + variantSwitch.setOnCheckedChangeListener { _, isChecked -> + setKeyVariant(background, darkKeys = isChecked) + } + brightnessSeekBar.setOnSeekBarChangeListener(object : + SeekBar.OnSeekBarChangeListener { + override fun onStartTrackingTouch(bar: SeekBar) {} + override fun onStopTrackingTouch(bar: SeekBar) {} + + override fun onProgressChanged(bar: SeekBar, progress: Int, fromUser: Boolean) { + if (fromUser) updateState() + } + }) } if (newCreated) { From ad6356191d28032df9f8ef1f5313ddcc6c8f381b Mon Sep 17 00:00:00 2001 From: Rocka Date: Fri, 15 Sep 2023 13:02:26 +0800 Subject: [PATCH 015/381] Only call getInlineSuggestions once --- .../org/fcitx/fcitx5/android/input/bar/KawaiiBarComponent.kt | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/app/src/main/java/org/fcitx/fcitx5/android/input/bar/KawaiiBarComponent.kt b/app/src/main/java/org/fcitx/fcitx5/android/input/bar/KawaiiBarComponent.kt index ae3854e67..bb0cb8ad3 100644 --- a/app/src/main/java/org/fcitx/fcitx5/android/input/bar/KawaiiBarComponent.kt +++ b/app/src/main/java/org/fcitx/fcitx5/android/input/bar/KawaiiBarComponent.kt @@ -362,14 +362,15 @@ class KawaiiBarComponent : UniqueViewComponent( @RequiresApi(Build.VERSION_CODES.R) fun handleInlineSuggestions(response: InlineSuggestionsResponse): Boolean { - if (response.inlineSuggestions.isEmpty()) { + val suggestions = response.inlineSuggestions + if (suggestions.isEmpty()) { isInlineSuggestionEmpty = true return true } var pinned: InlineSuggestion? = null val scrollable = mutableListOf() var extraPinnedCount = 0 - response.inlineSuggestions.forEach { + suggestions.forEach { if (it.info.isPinned) { if (pinned == null) { pinned = it From 3c35f36083b652f3a6d8d94fe86a69bd650b68af Mon Sep 17 00:00:00 2001 From: Rocka Date: Fri, 15 Sep 2023 13:38:34 +0800 Subject: [PATCH 016/381] Catch exception when get system props/settings --- .../fcitx5/android/data/InputFeedbacks.kt | 14 +++++++------- .../org/fcitx/fcitx5/android/utils/Utils.kt | 19 ++++++++++++++----- 2 files changed, 21 insertions(+), 12 deletions(-) diff --git a/app/src/main/java/org/fcitx/fcitx5/android/data/InputFeedbacks.kt b/app/src/main/java/org/fcitx/fcitx5/android/data/InputFeedbacks.kt index 7f51002ef..4dbc75fa0 100644 --- a/app/src/main/java/org/fcitx/fcitx5/android/data/InputFeedbacks.kt +++ b/app/src/main/java/org/fcitx/fcitx5/android/data/InputFeedbacks.kt @@ -10,7 +10,7 @@ import org.fcitx.fcitx5.android.data.prefs.AppPrefs import org.fcitx.fcitx5.android.data.prefs.ManagedPreference import org.fcitx.fcitx5.android.utils.appContext import org.fcitx.fcitx5.android.utils.audioManager -import org.fcitx.fcitx5.android.utils.getSystemSetting +import org.fcitx.fcitx5.android.utils.isSystemSettingEnabled import org.fcitx.fcitx5.android.utils.vibrator object InputFeedbacks { @@ -23,15 +23,15 @@ object InputFeedbacks { } } - private var systemSoundEffectsEnabled = false - private var systemHapticFeedbackEnabled = false + private var systemSoundEffects = false + private var systemHapticFeedback = false fun syncSystemPrefs() { - systemSoundEffectsEnabled = getSystemSetting(Settings.System.SOUND_EFFECTS_ENABLED) + systemSoundEffects = isSystemSettingEnabled(Settings.System.SOUND_EFFECTS_ENABLED) // it says "Replaced by using android.os.VibrationAttributes.USAGE_TOUCH" // but gives no clue about how to use it, and this one still works @Suppress("DEPRECATION") - systemHapticFeedbackEnabled = getSystemSetting(Settings.System.HAPTIC_FEEDBACK_ENABLED) + systemHapticFeedback = isSystemSettingEnabled(Settings.System.HAPTIC_FEEDBACK_ENABLED) } private val soundOnKeyPress by AppPrefs.getInstance().keyboard.soundOnKeyPress @@ -53,7 +53,7 @@ object InputFeedbacks { when (hapticOnKeyPress) { InputFeedbackMode.Enabled -> {} InputFeedbackMode.Disabled -> return - InputFeedbackMode.FollowingSystem -> if (!systemHapticFeedbackEnabled) return + InputFeedbackMode.FollowingSystem -> if (!systemHapticFeedback) return } val duration: Long @@ -97,7 +97,7 @@ object InputFeedbacks { when (soundOnKeyPress) { InputFeedbackMode.Enabled -> {} InputFeedbackMode.Disabled -> return - InputFeedbackMode.FollowingSystem -> if (!systemSoundEffectsEnabled) return + InputFeedbackMode.FollowingSystem -> if (!systemSoundEffects) return } val fx = when (effect) { SoundEffect.Standard -> AudioManager.FX_KEYPRESS_STANDARD diff --git a/app/src/main/java/org/fcitx/fcitx5/android/utils/Utils.kt b/app/src/main/java/org/fcitx/fcitx5/android/utils/Utils.kt index d9e80b824..5e1f70062 100644 --- a/app/src/main/java/org/fcitx/fcitx5/android/utils/Utils.kt +++ b/app/src/main/java/org/fcitx/fcitx5/android/utils/Utils.kt @@ -217,13 +217,22 @@ fun SeekBar.setOnChangeListener(listener: SeekBar.(progress: Int) -> Unit) { @SuppressLint("PrivateApi") fun getSystemProperty(key: String): String { - return Class.forName("android.os.SystemProperties") - .getMethod("get", String::class.java) - .invoke(null, key) as String + return try { + Class.forName("android.os.SystemProperties") + .getMethod("get", String::class.java) + .invoke(null, key) as String + } catch (e: Exception) { + "" + } } -fun getSystemSetting(key: String): Boolean = - Settings.System.getInt(appContext.contentResolver, key) == 1 +fun isSystemSettingEnabled(key: String): Boolean { + return try { + Settings.System.getInt(appContext.contentResolver, key) == 1 + } catch (e: Exception) { + false + } +} /** * @return top-level files in zip file From 65ea48338c1f6013af846bbcd4c15d7ac00dd33e Mon Sep 17 00:00:00 2001 From: Qijia Liu Date: Tue, 26 Sep 2023 00:37:43 -0400 Subject: [PATCH 017/381] Fix unikey plugin cmake project name (#337) --- plugin/unikey/src/main/cpp/CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugin/unikey/src/main/cpp/CMakeLists.txt b/plugin/unikey/src/main/cpp/CMakeLists.txt index bc45463b7..4910e39ee 100644 --- a/plugin/unikey/src/main/cpp/CMakeLists.txt +++ b/plugin/unikey/src/main/cpp/CMakeLists.txt @@ -1,6 +1,6 @@ cmake_minimum_required(VERSION 3.18) -project(fcitx5-android-lib-fcitx5-lua VERSION 0.0.7) +project(fcitx5-android-plugin-unikey VERSION 0.0.7) # For reproducible build add_link_options("LINKER:--hash-style=gnu,--build-id=none") From be83d111915e08888289ecb24a5a5c089c54fc3c Mon Sep 17 00:00:00 2001 From: Potato Hatsue <1793913507@qq.com> Date: Tue, 26 Sep 2023 00:38:35 -0400 Subject: [PATCH 018/381] Support Direct Boot mode (#339) Co-authored-by: Rocka --- app/src/main/AndroidManifest.xml | 3 ++ .../fcitx/fcitx5/android/FcitxApplication.kt | 35 +++++++++++++++++-- .../org/fcitx/fcitx5/android/core/Fcitx.kt | 7 ++-- .../fcitx5/android/core/data/DataManager.kt | 21 ++++++++++- .../fcitx/fcitx5/android/data/RecentlyUsed.kt | 11 +++--- .../fcitx5/android/ui/main/MainActivity.kt | 1 - .../fcitx5/android/utils/SystemService.kt | 4 +++ flake.lock | 12 +++---- lib/plugin-base/src/debug/AndroidManifest.xml | 4 ++- .../src/main/AndroidManifest.xml | 7 ++-- 10 files changed, 84 insertions(+), 21 deletions(-) diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index cca6168f0..d1f6d553a 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -50,6 +50,7 @@ android:launchMode="singleTask" /> @@ -104,6 +105,7 @@ diff --git a/app/src/main/java/org/fcitx/fcitx5/android/FcitxApplication.kt b/app/src/main/java/org/fcitx/fcitx5/android/FcitxApplication.kt index 9efd0dac2..7d9c34c5c 100644 --- a/app/src/main/java/org/fcitx/fcitx5/android/FcitxApplication.kt +++ b/app/src/main/java/org/fcitx/fcitx5/android/FcitxApplication.kt @@ -1,11 +1,13 @@ package org.fcitx.fcitx5.android +import android.annotation.SuppressLint import android.app.Application import android.content.BroadcastReceiver import android.content.Context import android.content.Intent import android.content.IntentFilter import android.content.res.Configuration +import android.os.Build import android.os.Process import android.util.Log import androidx.preference.PreferenceManager @@ -17,8 +19,10 @@ import org.fcitx.fcitx5.android.data.clipboard.ClipboardManager import org.fcitx.fcitx5.android.data.prefs.AppPrefs import org.fcitx.fcitx5.android.data.theme.ThemeManager import org.fcitx.fcitx5.android.ui.main.LogActivity +import org.fcitx.fcitx5.android.utils.AppUtil import org.fcitx.fcitx5.android.utils.Locales import org.fcitx.fcitx5.android.utils.isDarkMode +import org.fcitx.fcitx5.android.utils.userManager import timber.log.Timber import kotlin.system.exitProcess @@ -36,11 +40,34 @@ class FcitxApplication : Application() { } } + private val unlockReceiver = object : BroadcastReceiver() { + override fun onReceive(context: Context, intent: Intent) { + if (intent.action != Intent.ACTION_USER_UNLOCKED) return + if (!isDirectBootMode) return + Timber.d("Device unlocked, app will exit now and restart to normal mode") + FcitxDaemon.getFirstConnectionOrNull()?.also { + // try to shutdown fcitx gracefully + FcitxDaemon.stopFcitx() + } + AppUtil.exit() + } + } + + var isDirectBootMode = false + private set + + val directBootAwareContext: Context + @SuppressLint("NewApi") + get() = if (isDirectBootMode) createDeviceProtectedStorageContext() else applicationContext + override fun onCreate() { super.onCreate() + isDirectBootMode = Build.VERSION.SDK_INT >= Build.VERSION_CODES.N && !userManager.isUserUnlocked + val ctx = directBootAwareContext + if (!BuildConfig.DEBUG) { Thread.setDefaultUncaughtExceptionHandler { _, e -> - startActivity(Intent(applicationContext, LogActivity::class.java).apply { + startActivity(Intent(ctx, LogActivity::class.java).apply { flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK putExtra(LogActivity.FROM_CRASH, true) // avoid transaction overflow @@ -58,7 +85,7 @@ class FcitxApplication : Application() { instance = this // we don't have AppPrefs available yet - val sharedPrefs = PreferenceManager.getDefaultSharedPreferences(applicationContext) + val sharedPrefs = PreferenceManager.getDefaultSharedPreferences(ctx) if (BuildConfig.DEBUG || sharedPrefs.getBoolean("verbose_log", false)) { Timber.plant(object : Timber.DebugTree() { override fun log(priority: Int, tag: String?, message: String, t: Throwable?) { @@ -74,6 +101,8 @@ class FcitxApplication : Application() { }) } + Timber.d("isDirectBootMode=$isDirectBootMode") + AppPrefs.init(sharedPrefs) // record last pid for crash logs AppPrefs.getInstance().internal.pid.apply { @@ -82,7 +111,7 @@ class FcitxApplication : Application() { Timber.d("Last pid is $lastPid. Set it to current pid: $currentPid") setValue(currentPid) } - ClipboardManager.init(applicationContext) + ClipboardManager.init(ctx) ThemeManager.init(resources.configuration) Locales.onLocaleChange(resources.configuration) registerReceiver(shutdownReceiver, IntentFilter(Intent.ACTION_SHUTDOWN)) diff --git a/app/src/main/java/org/fcitx/fcitx5/android/core/Fcitx.kt b/app/src/main/java/org/fcitx/fcitx5/android/core/Fcitx.kt index 0a720f30c..90b53eea0 100644 --- a/app/src/main/java/org/fcitx/fcitx5/android/core/Fcitx.kt +++ b/app/src/main/java/org/fcitx/fcitx5/android/core/Fcitx.kt @@ -8,6 +8,7 @@ import kotlinx.coroutines.flow.asSharedFlow import kotlinx.coroutines.launch import kotlinx.coroutines.runBlocking import kotlinx.coroutines.withContext +import org.fcitx.fcitx5.android.FcitxApplication import org.fcitx.fcitx5.android.R import org.fcitx.fcitx5.android.core.data.DataManager import org.fcitx.fcitx5.android.data.clipboard.ClipboardManager @@ -366,6 +367,7 @@ class Fcitx(private val context: Context) : FcitxAPI, FcitxLifecycleOwner { override fun nativeStartup() { DataManager.sync() val locale = Locales.fcitxLocale + val dataDir = DataManager.dataDir.absolutePath val plugins = DataManager.getLoadedPlugins() val nativeLibDir = buildString { append(context.applicationInfo.nativeLibraryDir) @@ -379,14 +381,15 @@ class Fcitx(private val context: Context) : FcitxAPI, FcitxLifecycleOwner { """ Starting fcitx with: locale=$locale + dataDir=$dataDir nativeLibDir=$nativeLibDir extDomains=${extDomains.joinToString()} """.trimIndent() ) - with(context) { + with(FcitxApplication.getInstance().directBootAwareContext) { startupFcitx( locale, - applicationInfo.dataDir, + dataDir, nativeLibDir, (getExternalFilesDir(null) ?: filesDir).absolutePath, (externalCacheDir ?: cacheDir).absolutePath, diff --git a/app/src/main/java/org/fcitx/fcitx5/android/core/data/DataManager.kt b/app/src/main/java/org/fcitx/fcitx5/android/core/data/DataManager.kt index 4a94c3ece..4fbe54dca 100644 --- a/app/src/main/java/org/fcitx/fcitx5/android/core/data/DataManager.kt +++ b/app/src/main/java/org/fcitx/fcitx5/android/core/data/DataManager.kt @@ -43,7 +43,15 @@ object DataManager { json.encodeToString(descriptor) } - val dataDir = File(appContext.applicationInfo.dataDir) + // If Android version supports direct boot, we put the hierarchy in device encrypted storage + // instead of credential encrypted storage so that data can be accessed before user unlock + val dataDir: File = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { + Timber.d("Using device protected storage") + appContext.createDeviceProtectedStorageContext().dataDir + } else { + File(appContext.applicationInfo.dataDir) + } + private val destDescriptorFile = File(dataDir, Const.dataDescriptorName) private fun AssetManager.getDataDescriptor() = @@ -250,6 +258,16 @@ object DataManager { destDescriptorFile.writeText(serializeDataDescriptor(newHierarchy.downToDataDescriptor()).getOrThrow()) callbacks.forEach { it() } callbacks.clear() + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { + // remove old assets from credential encrypted storage + val oldDataDir = appContext.dataDir + val oldDataDescriptor = oldDataDir.resolve(Const.dataDescriptorName) + if (oldDataDescriptor.exists()) { + oldDataDescriptor.delete() + oldDataDir.resolve("README.md").delete() + oldDataDir.resolve("usr").deleteRecursively() + } + } synced = true Timber.d("Synced") } @@ -278,6 +296,7 @@ object DataManager { fun deleteAndSync() { lock.withLock { dataDir.resolve(Const.dataDescriptorName).delete() + dataDir.resolve("README.md").delete() dataDir.resolve("usr").deleteRecursively() } sync() diff --git a/app/src/main/java/org/fcitx/fcitx5/android/data/RecentlyUsed.kt b/app/src/main/java/org/fcitx/fcitx5/android/data/RecentlyUsed.kt index 3129596ec..c635dfcfc 100644 --- a/app/src/main/java/org/fcitx/fcitx5/android/data/RecentlyUsed.kt +++ b/app/src/main/java/org/fcitx/fcitx5/android/data/RecentlyUsed.kt @@ -1,6 +1,6 @@ package org.fcitx.fcitx5.android.data -import org.fcitx.fcitx5.android.utils.appContext +import org.fcitx.fcitx5.android.FcitxApplication // Not thread-safe class RecentlyUsed( @@ -12,10 +12,11 @@ class RecentlyUsed( const val DIR_NAME = "recently_used" } - private val file = appContext.filesDir.resolve(DIR_NAME).run { - mkdirs() - resolve(fileName).apply { createNewFile() } - } + private val file = + FcitxApplication.getInstance().directBootAwareContext.filesDir.resolve(DIR_NAME).run { + mkdirs() + resolve(fileName).apply { createNewFile() } + } fun load() { val xs = file.readLines() diff --git a/app/src/main/java/org/fcitx/fcitx5/android/ui/main/MainActivity.kt b/app/src/main/java/org/fcitx/fcitx5/android/ui/main/MainActivity.kt index 0eaa425c6..edd19baa6 100644 --- a/app/src/main/java/org/fcitx/fcitx5/android/ui/main/MainActivity.kt +++ b/app/src/main/java/org/fcitx/fcitx5/android/ui/main/MainActivity.kt @@ -30,7 +30,6 @@ import org.fcitx.fcitx5.android.utils.applyTranslucentSystemBars import org.fcitx.fcitx5.android.utils.navigateFromMain import splitties.dimensions.dp import splitties.views.topPadding -import timber.log.Timber class MainActivity : AppCompatActivity() { diff --git a/app/src/main/java/org/fcitx/fcitx5/android/utils/SystemService.kt b/app/src/main/java/org/fcitx/fcitx5/android/utils/SystemService.kt index 0e1e21721..e5d3faa1c 100644 --- a/app/src/main/java/org/fcitx/fcitx5/android/utils/SystemService.kt +++ b/app/src/main/java/org/fcitx/fcitx5/android/utils/SystemService.kt @@ -4,6 +4,7 @@ import android.app.NotificationManager import android.content.ClipboardManager import android.content.Context import android.media.AudioManager +import android.os.UserManager import android.os.Vibrator import android.view.inputmethod.InputMethodManager import androidx.core.content.getSystemService @@ -26,3 +27,6 @@ val Fragment.notificationManager val Context.vibrator get() = getSystemService()!! + +val Context.userManager + get() = getSystemService()!! diff --git a/flake.lock b/flake.lock index b938fc4ae..9257228e3 100644 --- a/flake.lock +++ b/flake.lock @@ -21,11 +21,11 @@ "systems": "systems" }, "locked": { - "lastModified": 1689068808, - "narHash": "sha256-6ixXo3wt24N/melDWjq70UuHQLxGV8jZvooRanIHXw0=", + "lastModified": 1694529238, + "narHash": "sha256-zsNZZGTGnMOf9YpHKJqMSsa0dXbfmxeoJ7xHlrt+xmY=", "owner": "numtide", "repo": "flake-utils", - "rev": "919d646de7be200f3bf08cb76ae1f09402b6f9b4", + "rev": "ff7b65b44d01cf9ba6a71320833626af21126384", "type": "github" }, "original": { @@ -36,11 +36,11 @@ }, "nixpkgs": { "locked": { - "lastModified": 1691899779, - "narHash": "sha256-IBf4KVr/UQJlzrqB2/IHtlvmwsvyIVLPerSzCPU/6Xk=", + "lastModified": 1695360818, + "narHash": "sha256-JlkN3R/SSoMTa+CasbxS1gq+GpGxXQlNZRUh9+LIy/0=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "100a1550b0e7a64b960c625b656f9229bdef5f87", + "rev": "e35dcc04a3853da485a396bdd332217d0ac9054f", "type": "github" }, "original": { diff --git a/lib/plugin-base/src/debug/AndroidManifest.xml b/lib/plugin-base/src/debug/AndroidManifest.xml index 84dc0f05f..9149acc34 100644 --- a/lib/plugin-base/src/debug/AndroidManifest.xml +++ b/lib/plugin-base/src/debug/AndroidManifest.xml @@ -12,8 +12,10 @@ + android:theme="@style/DeviceSettingsTheme" + tools:targetApi="n"> - + + tools:targetApi="n"> From 76d0ec607f5c6d2664dcc045d95755138d09917f Mon Sep 17 00:00:00 2001 From: Rocka Date: Wed, 27 Sep 2023 18:34:14 +0800 Subject: [PATCH 019/381] Register ACTION_USER_UNLOCKED receiver on direct boot mode --- .../main/java/org/fcitx/fcitx5/android/FcitxApplication.kt | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/app/src/main/java/org/fcitx/fcitx5/android/FcitxApplication.kt b/app/src/main/java/org/fcitx/fcitx5/android/FcitxApplication.kt index 7d9c34c5c..5895150d7 100644 --- a/app/src/main/java/org/fcitx/fcitx5/android/FcitxApplication.kt +++ b/app/src/main/java/org/fcitx/fcitx5/android/FcitxApplication.kt @@ -62,7 +62,10 @@ class FcitxApplication : Application() { override fun onCreate() { super.onCreate() - isDirectBootMode = Build.VERSION.SDK_INT >= Build.VERSION_CODES.N && !userManager.isUserUnlocked + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N && !userManager.isUserUnlocked) { + isDirectBootMode = true + registerReceiver(unlockReceiver, IntentFilter(Intent.ACTION_USER_UNLOCKED)) + } val ctx = directBootAwareContext if (!BuildConfig.DEBUG) { From d31a01f56a19b1ecd1e62759d8d67597099b5450 Mon Sep 17 00:00:00 2001 From: Rocka Date: Sat, 30 Sep 2023 15:33:09 +0800 Subject: [PATCH 020/381] Write subset of app and theme prefs to device protected storage --- .../fcitx/fcitx5/android/FcitxApplication.kt | 4 + .../fcitx5/android/data/prefs/AppPrefs.kt | 27 +++++ .../android/data/prefs/ManagedPreference.kt | 102 +++++++++++------- .../data/prefs/ManagedPreferenceFragment.kt | 10 +- .../data/theme/ManagedThemePreference.kt | 4 + .../fcitx5/android/data/theme/ThemeManager.kt | 18 ++++ .../main/settings/theme/ThemeListFragment.kt | 8 ++ .../settings/theme/ThemeSettingsFragment.kt | 10 +- 8 files changed, 143 insertions(+), 40 deletions(-) diff --git a/app/src/main/java/org/fcitx/fcitx5/android/FcitxApplication.kt b/app/src/main/java/org/fcitx/fcitx5/android/FcitxApplication.kt index 5895150d7..c1364ae78 100644 --- a/app/src/main/java/org/fcitx/fcitx5/android/FcitxApplication.kt +++ b/app/src/main/java/org/fcitx/fcitx5/android/FcitxApplication.kt @@ -118,6 +118,10 @@ class FcitxApplication : Application() { ThemeManager.init(resources.configuration) Locales.onLocaleChange(resources.configuration) registerReceiver(shutdownReceiver, IntentFilter(Intent.ACTION_SHUTDOWN)) + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N && !isDirectBootMode) { + AppPrefs.getInstance().syncToDeviceEncryptedStorage() + ThemeManager.syncToDeviceEncryptedStorage() + } } override fun onConfigurationChanged(newConfig: Configuration) { diff --git a/app/src/main/java/org/fcitx/fcitx5/android/data/prefs/AppPrefs.kt b/app/src/main/java/org/fcitx/fcitx5/android/data/prefs/AppPrefs.kt index 8a0ab6d36..2e4635ad6 100644 --- a/app/src/main/java/org/fcitx/fcitx5/android/data/prefs/AppPrefs.kt +++ b/app/src/main/java/org/fcitx/fcitx5/android/data/prefs/AppPrefs.kt @@ -2,6 +2,9 @@ package org.fcitx.fcitx5.android.data.prefs import android.content.SharedPreferences import android.os.Build +import androidx.annotation.RequiresApi +import androidx.core.content.edit +import androidx.preference.PreferenceManager import org.fcitx.fcitx5.android.R import org.fcitx.fcitx5.android.data.InputFeedbacks.InputFeedbackMode import org.fcitx.fcitx5.android.input.candidates.HorizontalCandidateMode @@ -351,6 +354,30 @@ class AppPrefs(private val sharedPreferences: SharedPreferences) { } } + @RequiresApi(Build.VERSION_CODES.N) + fun syncToDeviceEncryptedStorage() { + val ctx = appContext.createDeviceProtectedStorageContext() + val sp = PreferenceManager.getDefaultSharedPreferences(ctx) + sp.edit { + listOf( + internal.verboseLog, + internal.editorInfoInspector, + advanced.ignoreSystemCursor, + advanced.resetCursorAfterCommit, + advanced.disableAnimation, + advanced.vivoKeypressWorkaround + ).forEach { + it.putValueTo(this@edit) + } + keyboard.managedPreferences.forEach { + it.value.putValueTo(this@edit) + } + clipboard.managedPreferences.forEach { + it.value.putValueTo(this@edit) + } + } + } + companion object { private var instance: AppPrefs? = null diff --git a/app/src/main/java/org/fcitx/fcitx5/android/data/prefs/ManagedPreference.kt b/app/src/main/java/org/fcitx/fcitx5/android/data/prefs/ManagedPreference.kt index ae0d0f549..b5bd76c10 100644 --- a/app/src/main/java/org/fcitx/fcitx5/android/data/prefs/ManagedPreference.kt +++ b/app/src/main/java/org/fcitx/fcitx5/android/data/prefs/ManagedPreference.kt @@ -3,7 +3,6 @@ package org.fcitx.fcitx5.android.data.prefs import android.content.SharedPreferences import androidx.core.content.edit import org.fcitx.fcitx5.android.utils.WeakHashSet -import timber.log.Timber import kotlin.properties.ReadWriteProperty import kotlin.reflect.KProperty @@ -26,6 +25,8 @@ abstract class ManagedPreference( abstract fun getValue(): T + abstract fun putValueTo(editor: SharedPreferences.Editor) + override fun getValue(thisRef: Any?, property: KProperty<*>): T = getValue() override fun setValue(thisRef: Any?, property: KProperty<*>, value: T) = setValue(value) @@ -62,13 +63,18 @@ abstract class ManagedPreference( sharedPreferences.edit { putBoolean(key, value) } } - override fun getValue(): Boolean = sharedPreferences.all[key]?.let { - it.let { x -> x as? Boolean } - ?: run { - setValue(defaultValue) - defaultValue - } - } ?: defaultValue + override fun getValue(): Boolean { + return try { + sharedPreferences.getBoolean(key, defaultValue) + } catch (e: Exception) { + setValue(defaultValue) + defaultValue + } + } + + override fun putValueTo(editor: SharedPreferences.Editor) { + editor.putBoolean(key, getValue()) + } } class PString(sharedPreferences: SharedPreferences, key: String, defaultValue: String) : @@ -78,13 +84,18 @@ abstract class ManagedPreference( sharedPreferences.edit { putString(key, value) } } - override fun getValue(): String = sharedPreferences.all[key]?.let { - it.let { x -> x as? String } - ?: run { - setValue(defaultValue) - defaultValue - } - } ?: defaultValue + override fun getValue(): String { + return try { + sharedPreferences.getString(key, defaultValue)!! + } catch (e: Exception) { + setValue(defaultValue) + defaultValue + } + } + + override fun putValueTo(editor: SharedPreferences.Editor) { + editor.putString(key, getValue()) + } } class PStringLike( @@ -98,15 +109,20 @@ abstract class ManagedPreference( sharedPreferences.edit { putString(key, codec.encode(value)) } } - override fun getValue(): T = - sharedPreferences.all[key]?.let { raw -> - if (raw !is String) - setValue(defaultValue) - raw.let { it as? String } - ?.runCatching { codec.decode(this) } - ?.onFailure { Timber.w("Failed to decode value '$raw' of preference $key") } - ?.getOrNull() - } ?: defaultValue + override fun getValue(): T { + return try { + sharedPreferences.getString(key, null)?.let { + codec.decode(it) + } ?: defaultValue + } catch (e: Exception) { + setValue(defaultValue) + defaultValue + } + } + + override fun putValueTo(editor: SharedPreferences.Editor) { + editor.putString(key, codec.encode(getValue())) + } } @@ -117,13 +133,18 @@ abstract class ManagedPreference( sharedPreferences.edit { putInt(key, value) } } - override fun getValue(): Int = sharedPreferences.all[key]?.let { - it.let { x -> x as? Int } - ?: run { - setValue(defaultValue) - defaultValue - } - } ?: defaultValue + override fun getValue(): Int { + return try { + sharedPreferences.getInt(key, defaultValue) + } catch (e: Exception) { + setValue(defaultValue) + defaultValue + } + } + + override fun putValueTo(editor: SharedPreferences.Editor) { + editor.putInt(key, getValue()) + } } class PFloat(sharedPreferences: SharedPreferences, key: String, defaultValue: Float) : @@ -132,13 +153,18 @@ abstract class ManagedPreference( sharedPreferences.edit { putFloat(key, value) } } - override fun getValue(): Float = sharedPreferences.all[key]?.let { - it.let { x -> x as? Float } - ?: run { - setValue(defaultValue) - defaultValue - } - } ?: defaultValue + override fun getValue(): Float { + return try { + sharedPreferences.getFloat(key, defaultValue) + } catch (e: Exception) { + setValue(defaultValue) + defaultValue + } + } + + override fun putValueTo(editor: SharedPreferences.Editor) { + editor.putFloat(key, getValue()) + } } } diff --git a/app/src/main/java/org/fcitx/fcitx5/android/data/prefs/ManagedPreferenceFragment.kt b/app/src/main/java/org/fcitx/fcitx5/android/data/prefs/ManagedPreferenceFragment.kt index eedaa1c0f..8451de826 100644 --- a/app/src/main/java/org/fcitx/fcitx5/android/data/prefs/ManagedPreferenceFragment.kt +++ b/app/src/main/java/org/fcitx/fcitx5/android/data/prefs/ManagedPreferenceFragment.kt @@ -1,5 +1,6 @@ package org.fcitx.fcitx5.android.data.prefs +import android.os.Build import android.os.Bundle import androidx.annotation.CallSuper import androidx.lifecycle.lifecycleScope @@ -31,8 +32,15 @@ abstract class ManagedPreferenceFragment(private val preferenceProvider: Managed } } + override fun onStop() { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { + AppPrefs.getInstance().syncToDeviceEncryptedStorage() + } + super.onStop() + } + override fun onDestroy() { - super.onDestroy() evaluator.destroy() + super.onDestroy() } } \ No newline at end of file diff --git a/app/src/main/java/org/fcitx/fcitx5/android/data/theme/ManagedThemePreference.kt b/app/src/main/java/org/fcitx/fcitx5/android/data/theme/ManagedThemePreference.kt index a110be1c3..7fc75c630 100644 --- a/app/src/main/java/org/fcitx/fcitx5/android/data/theme/ManagedThemePreference.kt +++ b/app/src/main/java/org/fcitx/fcitx5/android/data/theme/ManagedThemePreference.kt @@ -21,4 +21,8 @@ class ManagedThemePreference( ThemeManager.getAllThemes().find { it.name == name } } ?: defaultValue + override fun putValueTo(editor: SharedPreferences.Editor) { + editor.putString(key, getValue().name) + } + } \ No newline at end of file diff --git a/app/src/main/java/org/fcitx/fcitx5/android/data/theme/ThemeManager.kt b/app/src/main/java/org/fcitx/fcitx5/android/data/theme/ThemeManager.kt index 083d74410..120b5b8cd 100644 --- a/app/src/main/java/org/fcitx/fcitx5/android/data/theme/ThemeManager.kt +++ b/app/src/main/java/org/fcitx/fcitx5/android/data/theme/ThemeManager.kt @@ -2,8 +2,12 @@ package org.fcitx.fcitx5.android.data.theme import android.content.SharedPreferences import android.content.res.Configuration +import android.os.Build import androidx.annotation.Keep +import androidx.annotation.RequiresApi import androidx.annotation.StringRes +import androidx.core.content.edit +import androidx.preference.PreferenceManager import kotlinx.serialization.json.Json import org.fcitx.fcitx5.android.R import org.fcitx.fcitx5.android.data.prefs.AppPrefs @@ -385,4 +389,18 @@ object ThemeManager { } } + @RequiresApi(Build.VERSION_CODES.N) + fun syncToDeviceEncryptedStorage() { + val ctx = appContext.createDeviceProtectedStorageContext() + val sp = PreferenceManager.getDefaultSharedPreferences(ctx) + sp.edit { + internalPrefs.managedPreferences.forEach { + it.value.putValueTo(this@edit) + } + prefs.managedPreferences.forEach { + it.value.putValueTo(this@edit) + } + } + } + } \ No newline at end of file diff --git a/app/src/main/java/org/fcitx/fcitx5/android/ui/main/settings/theme/ThemeListFragment.kt b/app/src/main/java/org/fcitx/fcitx5/android/ui/main/settings/theme/ThemeListFragment.kt index 88494b122..b5c8d50c8 100644 --- a/app/src/main/java/org/fcitx/fcitx5/android/ui/main/settings/theme/ThemeListFragment.kt +++ b/app/src/main/java/org/fcitx/fcitx5/android/ui/main/settings/theme/ThemeListFragment.kt @@ -1,5 +1,6 @@ package org.fcitx.fcitx5.android.ui.main.settings.theme +import android.os.Build import android.os.Bundle import android.view.LayoutInflater import android.view.View @@ -316,6 +317,13 @@ class ThemeListFragment : Fragment() { exportLauncher.launch(theme.name + ".zip") } + override fun onStop() { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { + ThemeManager.syncToDeviceEncryptedStorage() + } + super.onStop() + } + override fun onDestroy() { ThemeManager.removeOnChangedListener(onThemeChangeListener) super.onDestroy() diff --git a/app/src/main/java/org/fcitx/fcitx5/android/ui/main/settings/theme/ThemeSettingsFragment.kt b/app/src/main/java/org/fcitx/fcitx5/android/ui/main/settings/theme/ThemeSettingsFragment.kt index 2bc5042dd..28f9c2e83 100644 --- a/app/src/main/java/org/fcitx/fcitx5/android/ui/main/settings/theme/ThemeSettingsFragment.kt +++ b/app/src/main/java/org/fcitx/fcitx5/android/ui/main/settings/theme/ThemeSettingsFragment.kt @@ -1,6 +1,14 @@ package org.fcitx.fcitx5.android.ui.main.settings.theme +import android.os.Build import org.fcitx.fcitx5.android.data.prefs.ManagedPreferenceFragment import org.fcitx.fcitx5.android.data.theme.ThemeManager -class ThemeSettingsFragment : ManagedPreferenceFragment(ThemeManager.prefs) +class ThemeSettingsFragment : ManagedPreferenceFragment(ThemeManager.prefs) { + override fun onStop() { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { + ThemeManager.syncToDeviceEncryptedStorage() + } + super.onStop() + } +} From 476a361300815104c58bff9ceda5832927e455b1 Mon Sep 17 00:00:00 2001 From: Rocka Date: Mon, 2 Oct 2023 17:00:13 +0800 Subject: [PATCH 021/381] Update fcitx5 submodules and prebuilt libraries --- app/licenses/libraries/fcitx5-chinese-addons.json | 2 +- app/licenses/libraries/fcitx5-lua.json | 2 +- app/licenses/libraries/fcitx5.json | 2 +- app/licenses/libraries/libime.json | 2 +- app/src/main/cpp/CMakeLists.txt | 11 +++-------- .../src/main/cpp/CMakeLists.txt | 12 ++++++------ .../src/main/cpp/fcitx5-chinese-addons | 2 +- lib/fcitx5-lua/src/main/cpp/fcitx5-lua | 2 +- lib/fcitx5/src/main/cpp/cmake/FindPkgConfig.cmake | 4 ++++ lib/fcitx5/src/main/cpp/cmake/FindZLIB.cmake | 14 ++++++++++++++ lib/fcitx5/src/main/cpp/fcitx5 | 2 +- lib/fcitx5/src/main/cpp/prebuilt | 2 +- lib/libime/src/main/cpp/CMakeLists.txt | 13 +++++++------ lib/libime/src/main/cpp/libime | 2 +- plugin/anthy/licenses/libraries/fcitx5-anthy.json | 2 +- plugin/anthy/src/main/cpp/fcitx5-anthy | 2 +- .../unikey/licenses/libraries/fcitx5-unikey.json | 2 +- plugin/unikey/src/main/cpp/fcitx5-unikey | 2 +- 18 files changed, 47 insertions(+), 33 deletions(-) create mode 100644 lib/fcitx5/src/main/cpp/cmake/FindZLIB.cmake diff --git a/app/licenses/libraries/fcitx5-chinese-addons.json b/app/licenses/libraries/fcitx5-chinese-addons.json index 432a74c66..522bdadf5 100644 --- a/app/licenses/libraries/fcitx5-chinese-addons.json +++ b/app/licenses/libraries/fcitx5-chinese-addons.json @@ -1,6 +1,6 @@ { "uniqueId": "fcitx/fcitx5-chinese-addons", - "artifactVersion": "5.1.0", + "artifactVersion": "5.1.1", "description": "Chinese related addon for fcitx5", "name": "fcitx/fcitx5-chinese-addons", "website": "https://github.com/fcitx/fcitx5-chinese-addons", diff --git a/app/licenses/libraries/fcitx5-lua.json b/app/licenses/libraries/fcitx5-lua.json index 36e42980a..1d3e279ad 100644 --- a/app/licenses/libraries/fcitx5-lua.json +++ b/app/licenses/libraries/fcitx5-lua.json @@ -1,6 +1,6 @@ { "uniqueId": "fcitx/fcitx5-lua", - "artifactVersion": "5.0.10", + "artifactVersion": "5.0.11", "description": "Lua support for fcitx5", "name": "fcitx/fcitx5-lua", "website": "https://github.com/fcitx/fcitx5-lua", diff --git a/app/licenses/libraries/fcitx5.json b/app/licenses/libraries/fcitx5.json index ab814593a..c55f656f8 100644 --- a/app/licenses/libraries/fcitx5.json +++ b/app/licenses/libraries/fcitx5.json @@ -1,6 +1,6 @@ { "uniqueId": "fcitx/fcitx5", - "artifactVersion": "5.1.0", + "artifactVersion": "5.1.1", "description": "Next generation of fcitx", "name": "fcitx/fcitx5", "website": "https://github.com/fcitx/fcitx5", diff --git a/app/licenses/libraries/libime.json b/app/licenses/libraries/libime.json index 44281cca0..e7edf9619 100644 --- a/app/licenses/libraries/libime.json +++ b/app/licenses/libraries/libime.json @@ -1,6 +1,6 @@ { "uniqueId": "fcitx/libime", - "artifactVersion": "1.1.1", + "artifactVersion": "1.1.2", "description": "library to support generic input method implementation", "name": "fcitx/libime", "website": "https://github.com/fcitx/libime", diff --git a/app/src/main/cpp/CMakeLists.txt b/app/src/main/cpp/CMakeLists.txt index b33696b0a..79a9e2696 100644 --- a/app/src/main/cpp/CMakeLists.txt +++ b/app/src/main/cpp/CMakeLists.txt @@ -42,13 +42,8 @@ find_package(Libevent) # prebuilt boost set(BOOST_VERSION "1.80.0") -set(BOOST_MODULES headers filesystem atomic iostreams) -set(BOOST_ROOT "${PREBUILT_DIR}/boost/${ANDROID_ABI}") -set(Boost_DIR "${BOOST_ROOT}/lib/cmake/Boost-${BOOST_VERSION}") -foreach (mod IN LISTS BOOST_MODULES) - set("boost_${mod}_DIR" "${BOOST_ROOT}/lib/cmake/boost_${mod}-${BOOST_VERSION}") -endforeach () -find_package(Boost 1.80 REQUIRED COMPONENTS filesystem iostreams) +list(APPEND CMAKE_FIND_ROOT_PATH "${PREBUILT_DIR}/boost/${ANDROID_ABI}/lib/cmake") +find_package(Boost 1.80 REQUIRED COMPONENTS headers filesystem iostreams) add_library(native-lib SHARED native-lib.cpp) target_link_libraries(native-lib @@ -60,7 +55,7 @@ target_link_libraries(native-lib Fcitx5::Module::QuickPhrase Fcitx5::Module::Unicode Fcitx5::Module::Clipboard - Boost::boost + Boost::headers LibIME::Pinyin LibIME::Table ) diff --git a/lib/fcitx5-chinese-addons/src/main/cpp/CMakeLists.txt b/lib/fcitx5-chinese-addons/src/main/cpp/CMakeLists.txt index e21ef4011..169e9c8a5 100644 --- a/lib/fcitx5-chinese-addons/src/main/cpp/CMakeLists.txt +++ b/lib/fcitx5-chinese-addons/src/main/cpp/CMakeLists.txt @@ -40,12 +40,12 @@ find_package(fmt) # prebuilt boost set(BOOST_VERSION "1.80.0") -set(BOOST_MODULES headers filesystem atomic iostreams) -set(BOOST_ROOT "${PREBUILT_DIR}/boost/${ANDROID_ABI}") -set(Boost_DIR "${BOOST_ROOT}/lib/cmake/Boost-${BOOST_VERSION}") -foreach (mod IN LISTS BOOST_MODULES) - set("boost_${mod}_DIR" "${BOOST_ROOT}/lib/cmake/boost_${mod}-${BOOST_VERSION}") -endforeach () +list(APPEND CMAKE_FIND_ROOT_PATH "${PREBUILT_DIR}/boost/${ANDROID_ABI}/lib/cmake") +find_package(Boost ${BOOST_VERSION} REQUIRED COMPONENTS iostreams CONFIG) + +# prebuilt marisa-tire, OpenCC needs it +set(marisa_DIR "${PREBUILT_DIR}/marisa/${ANDROID_ABI}/lib/cmake/marisa") +find_package(marisa) # prebuilt opencc set(OpenCC_DIR "${PREBUILT_DIR}/opencc/${ANDROID_ABI}/lib/cmake/opencc") diff --git a/lib/fcitx5-chinese-addons/src/main/cpp/fcitx5-chinese-addons b/lib/fcitx5-chinese-addons/src/main/cpp/fcitx5-chinese-addons index a33d09dc2..25d1b7d6c 160000 --- a/lib/fcitx5-chinese-addons/src/main/cpp/fcitx5-chinese-addons +++ b/lib/fcitx5-chinese-addons/src/main/cpp/fcitx5-chinese-addons @@ -1 +1 @@ -Subproject commit a33d09dc21ec0619beecb7ec874611f3d3a90938 +Subproject commit 25d1b7d6c488a589ccd7f5472fb75015cf9f8a19 diff --git a/lib/fcitx5-lua/src/main/cpp/fcitx5-lua b/lib/fcitx5-lua/src/main/cpp/fcitx5-lua index d8a1319c6..821c6dae9 160000 --- a/lib/fcitx5-lua/src/main/cpp/fcitx5-lua +++ b/lib/fcitx5-lua/src/main/cpp/fcitx5-lua @@ -1 +1 @@ -Subproject commit d8a1319c6583b0055b4bd4ea5bbd0292a8f8023a +Subproject commit 821c6dae9eac869428e446b0d8990306548c6806 diff --git a/lib/fcitx5/src/main/cpp/cmake/FindPkgConfig.cmake b/lib/fcitx5/src/main/cpp/cmake/FindPkgConfig.cmake index b4155b1dc..8bedf00d1 100644 --- a/lib/fcitx5/src/main/cpp/cmake/FindPkgConfig.cmake +++ b/lib/fcitx5/src/main/cpp/cmake/FindPkgConfig.cmake @@ -1,5 +1,9 @@ set(PkgConfig_FOUND TRUE) +function(pkg_check_modules) + message("pkg_check_modules: ${ARGV}") +endfunction() + include(FindPackageHandleStandardArgs) find_package_handle_standard_args(PkgConfig FOUND_VAR diff --git a/lib/fcitx5/src/main/cpp/cmake/FindZLIB.cmake b/lib/fcitx5/src/main/cpp/cmake/FindZLIB.cmake new file mode 100644 index 000000000..e91a16780 --- /dev/null +++ b/lib/fcitx5/src/main/cpp/cmake/FindZLIB.cmake @@ -0,0 +1,14 @@ +find_library(ZLIB z) +add_library(ZLIB::ZLIB SHARED IMPORTED) +set_target_properties(ZLIB::ZLIB PROPERTIES IMPORTED_LOCATION ${ZLIB}) +set(ZLIB_LIBRARIES ${ZLIB}) +set(ZLIB_INCLUDE_DIRS "${ANDROID_TOOLCHAIN_ROOT}/sysroot/usr/include") +set(ZLIB_FOUND TRUE) + +include(FindPackageHandleStandardArgs) +find_package_handle_standard_args(ZLIB + FOUND_VAR + ZLIB_FOUND + REQUIRED_VARS + ZLIB_FOUND +) diff --git a/lib/fcitx5/src/main/cpp/fcitx5 b/lib/fcitx5/src/main/cpp/fcitx5 index 018d91b81..7bf330412 160000 --- a/lib/fcitx5/src/main/cpp/fcitx5 +++ b/lib/fcitx5/src/main/cpp/fcitx5 @@ -1 +1 @@ -Subproject commit 018d91b81f6c5745641b7a0ea85332d1d9a642c1 +Subproject commit 7bf330412cef5906eac8a386c4e324ee0e95eac9 diff --git a/lib/fcitx5/src/main/cpp/prebuilt b/lib/fcitx5/src/main/cpp/prebuilt index 2d9dbe072..90fa6fba9 160000 --- a/lib/fcitx5/src/main/cpp/prebuilt +++ b/lib/fcitx5/src/main/cpp/prebuilt @@ -1 +1 @@ -Subproject commit 2d9dbe07228e2036e3b60b49483f22b268b94610 +Subproject commit 90fa6fba91011f0f5c9dbca0ee764447e1be35fd diff --git a/lib/libime/src/main/cpp/CMakeLists.txt b/lib/libime/src/main/cpp/CMakeLists.txt index be182fd71..8eedc56a2 100644 --- a/lib/libime/src/main/cpp/CMakeLists.txt +++ b/lib/libime/src/main/cpp/CMakeLists.txt @@ -25,12 +25,13 @@ find_package(fmt) # prebuilt boost set(BOOST_VERSION "1.80.0") -set(BOOST_MODULES headers filesystem atomic iostreams) -set(BOOST_ROOT "${PREBUILT_DIR}/boost/${ANDROID_ABI}") -set(Boost_DIR "${BOOST_ROOT}/lib/cmake/Boost-${BOOST_VERSION}") -foreach (mod IN LISTS BOOST_MODULES) - set("boost_${mod}_DIR" "${BOOST_ROOT}/lib/cmake/boost_${mod}-${BOOST_VERSION}") -endforeach () +list(APPEND CMAKE_FIND_ROOT_PATH "${PREBUILT_DIR}/boost/${ANDROID_ABI}/lib/cmake") +find_package(Boost ${BOOST_VERSION} REQUIRED COMPONENTS filesystem iostreams CONFIG) + +# prebuilt zstd +set(zstd_DIR "${PREBUILT_DIR}/zstd/${ANDROID_ABI}/lib/cmake/zstd") +find_package(zstd) +add_library(PkgConfig::ZSTD ALIAS zstd::libzstd_static) set(LIBIME_INSTALL_PKGDATADIR table) option(ENABLE_TEST "" OFF) diff --git a/lib/libime/src/main/cpp/libime b/lib/libime/src/main/cpp/libime index bf1735cd9..1462a8181 160000 --- a/lib/libime/src/main/cpp/libime +++ b/lib/libime/src/main/cpp/libime @@ -1 +1 @@ -Subproject commit bf1735cd993ba55797b17728a16555a78b6e8b41 +Subproject commit 1462a81810135f993cd9dfa8711b8716d1d2cb61 diff --git a/plugin/anthy/licenses/libraries/fcitx5-anthy.json b/plugin/anthy/licenses/libraries/fcitx5-anthy.json index 2751b3f44..6e8b9dc31 100644 --- a/plugin/anthy/licenses/libraries/fcitx5-anthy.json +++ b/plugin/anthy/licenses/libraries/fcitx5-anthy.json @@ -1,6 +1,6 @@ { "uniqueId": "fcitx/fcitx5-anthy", - "artifactVersion": "5.1.0", + "artifactVersion": "5.1.1", "description": "Anthy Wrapper for Fcitx", "name": "fcitx/fcitx5-anthy", "website": "https://github.com/fcitx/fcitx5-anthy", diff --git a/plugin/anthy/src/main/cpp/fcitx5-anthy b/plugin/anthy/src/main/cpp/fcitx5-anthy index ba3fae747..8fe86d6c3 160000 --- a/plugin/anthy/src/main/cpp/fcitx5-anthy +++ b/plugin/anthy/src/main/cpp/fcitx5-anthy @@ -1 +1 @@ -Subproject commit ba3fae7474e79b5fefaa82d1dcd373c4335fb90e +Subproject commit 8fe86d6c301014b1ec3c16c27456eecca75bbc00 diff --git a/plugin/unikey/licenses/libraries/fcitx5-unikey.json b/plugin/unikey/licenses/libraries/fcitx5-unikey.json index 1eb14f2e0..c6fc5183e 100644 --- a/plugin/unikey/licenses/libraries/fcitx5-unikey.json +++ b/plugin/unikey/licenses/libraries/fcitx5-unikey.json @@ -1,6 +1,6 @@ { "uniqueId": "fcitx/fcitx5-unikey", - "artifactVersion": "5.1.0", + "artifactVersion": "5.1.1", "description": "Unikey (Vietnamese Input Method) engine support for Fcitx", "name": "fcitx/fcitx5-unikey", "website": "https://github.com/fcitx/fcitx5-unikey", diff --git a/plugin/unikey/src/main/cpp/fcitx5-unikey b/plugin/unikey/src/main/cpp/fcitx5-unikey index 7e1b67388..1df3566d1 160000 --- a/plugin/unikey/src/main/cpp/fcitx5-unikey +++ b/plugin/unikey/src/main/cpp/fcitx5-unikey @@ -1 +1 @@ -Subproject commit 7e1b673885c133dab948fa465b7af00fa788a240 +Subproject commit 1df3566d1825f739bd58d0f041b6c46160ea0a52 From 33aeb1a5b7facb69e4c50ce475516682864a1336 Mon Sep 17 00:00:00 2001 From: Rocka Date: Mon, 2 Oct 2023 17:01:15 +0800 Subject: [PATCH 022/381] Fix AGP lint task --- .../src/main/kotlin/AndroidAppConventionPlugin.kt | 2 -- build-logic/convention/src/main/kotlin/CMakeDirPlugin.kt | 9 +++++++-- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/build-logic/convention/src/main/kotlin/AndroidAppConventionPlugin.kt b/build-logic/convention/src/main/kotlin/AndroidAppConventionPlugin.kt index 737e26295..086f0bf06 100644 --- a/build-logic/convention/src/main/kotlin/AndroidAppConventionPlugin.kt +++ b/build-logic/convention/src/main/kotlin/AndroidAppConventionPlugin.kt @@ -59,8 +59,6 @@ class AndroidAppConventionPlugin : AndroidBaseConventionPlugin() { target.afterEvaluate { tasks.findByName(DataDescriptorPlugin.TASK)?.also { tasks.getByName("merge${variantName}Assets").dependsOn(it) - tasks.getByName("lintAnalyze${variantName}").dependsOn(it) - tasks.getByName("lintReport${variantName}").dependsOn(it) tasks.getByName("lintVitalAnalyzeRelease").dependsOn(it) } } diff --git a/build-logic/convention/src/main/kotlin/CMakeDirPlugin.kt b/build-logic/convention/src/main/kotlin/CMakeDirPlugin.kt index b27bdc307..3933c6461 100644 --- a/build-logic/convention/src/main/kotlin/CMakeDirPlugin.kt +++ b/build-logic/convention/src/main/kotlin/CMakeDirPlugin.kt @@ -7,8 +7,13 @@ import org.gradle.api.Task import java.io.File val Project.cmakeDir: File - get() = extensions.extraProperties.get(CMakeDirPlugin.CMAKE_DIR) as? File - ?: error("Cannot find cmake dir. Did you apply org.fcitx.fcitx5.android.cmake-dir plugin and make your task `runAfterNativeConfigure`?") + get() { + try { + return extensions.extraProperties.get(CMakeDirPlugin.CMAKE_DIR) as File + } catch (e: Exception) { + error("Cannot find cmake dir. Did you apply org.fcitx.fcitx5.android.cmake-dir plugin and make your task `runAfterNativeConfigure`?") + } + } /** * Important: make sure that the task runs after than the native task From 6c65332f2264d16628b246066f7e1b9ddefdad5d Mon Sep 17 00:00:00 2001 From: Rocka Date: Tue, 3 Oct 2023 13:13:58 +0800 Subject: [PATCH 023/381] Support more icons and text label as icon in StatusArea --- .../android/input/status/StatusAreaEntry.kt | 10 ++- .../android/input/status/StatusAreaEntryUi.kt | 73 +++++++++++++++---- .../drawable/ic_baseline_text_format_24.xml | 10 +++ 3 files changed, 78 insertions(+), 15 deletions(-) create mode 100644 app/src/main/res/drawable/ic_baseline_text_format_24.xml diff --git a/app/src/main/java/org/fcitx/fcitx5/android/input/status/StatusAreaEntry.kt b/app/src/main/java/org/fcitx/fcitx5/android/input/status/StatusAreaEntry.kt index fd100bad7..b7bf4cd54 100644 --- a/app/src/main/java/org/fcitx/fcitx5/android/input/status/StatusAreaEntry.kt +++ b/app/src/main/java/org/fcitx/fcitx5/android/input/status/StatusAreaEntry.kt @@ -27,6 +27,9 @@ sealed class StatusAreaEntry( private fun Action.isActive() = icon.endsWith("-active") || isChecked private fun drawableRes(icon: String, active: Boolean = false) = when (icon) { + // androidkeyboard + "tools-check-spelling" -> R.drawable.ic_baseline_spellcheck_24 + // fcitx5-chinese-addons "fcitx-chttrans-active" -> R.drawable.ic_fcitx_status_chttrans_trad "fcitx-chttrans-inactive" -> R.drawable.ic_fcitx_status_chttrans_simp "fcitx-punc-active" -> R.drawable.ic_fcitx_status_punc_active @@ -35,7 +38,12 @@ sealed class StatusAreaEntry( "fcitx-fullwidth-inactive" -> R.drawable.ic_fcitx_status_fullwidth_inactive "fcitx-remind-active" -> R.drawable.ic_fcitx_status_prediction_active "fcitx-remind-inactive" -> R.drawable.ic_fcitx_status_prediction_inactive - "tools-check-spelling" -> R.drawable.ic_baseline_spellcheck_24 + // fcitx5-unikey + "document-edit" -> R.drawable.ic_baseline_edit_24 + "character-set" -> R.drawable.ic_baseline_text_format_24 + "edit-find" -> R.drawable.ic_baseline_search_24 + // fallback + "" -> 0 else -> if (active) R.drawable.ic_baseline_code_24 else R.drawable.ic_baseline_code_off_24 } diff --git a/app/src/main/java/org/fcitx/fcitx5/android/input/status/StatusAreaEntryUi.kt b/app/src/main/java/org/fcitx/fcitx5/android/input/status/StatusAreaEntryUi.kt index 72500dfa3..e0a1c2313 100644 --- a/app/src/main/java/org/fcitx/fcitx5/android/input/status/StatusAreaEntryUi.kt +++ b/app/src/main/java/org/fcitx/fcitx5/android/input/status/StatusAreaEntryUi.kt @@ -3,16 +3,33 @@ package org.fcitx.fcitx5.android.input.status import android.content.Context import android.graphics.PorterDuff import android.graphics.PorterDuffColorFilter +import android.graphics.Typeface import android.graphics.drawable.ShapeDrawable import android.graphics.drawable.shapes.OvalShape +import android.os.Build +import android.util.TypedValue +import android.view.View import android.view.ViewGroup import android.widget.ImageView import org.fcitx.fcitx5.android.data.theme.Theme import org.fcitx.fcitx5.android.input.keyboard.CustomGestureView import splitties.dimensions.dp import splitties.resources.drawable -import splitties.views.dsl.constraintlayout.* -import splitties.views.dsl.core.* +import splitties.views.dsl.constraintlayout.above +import splitties.views.dsl.constraintlayout.below +import splitties.views.dsl.constraintlayout.centerHorizontally +import splitties.views.dsl.constraintlayout.centerOn +import splitties.views.dsl.constraintlayout.constraintLayout +import splitties.views.dsl.constraintlayout.lParams +import splitties.views.dsl.constraintlayout.topOfParent +import splitties.views.dsl.core.Ui +import splitties.views.dsl.core.add +import splitties.views.dsl.core.frameLayout +import splitties.views.dsl.core.imageView +import splitties.views.dsl.core.lParams +import splitties.views.dsl.core.matchParent +import splitties.views.dsl.core.textView +import splitties.views.dsl.core.wrapContent import splitties.views.gravityCenter import splitties.views.imageDrawable @@ -20,11 +37,27 @@ class StatusAreaEntryUi(override val ctx: Context, private val theme: Theme) : U private val bkgDrawable = ShapeDrawable(OvalShape()) - val icon = imageView { + val bkg = frameLayout { background = bkgDrawable + } + + val icon = imageView { scaleType = ImageView.ScaleType.CENTER_INSIDE } + val textIcon = textView { + gravity = gravityCenter + setTextSize(TypedValue.COMPLEX_UNIT_DIP, 20f) + // keep original typeface, apply textStyle only + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) { + // 600 = Semi Bold, 700 = Bold which is too heavy + typeface = Typeface.create(typeface, 600, false) + } else { + setTypeface(typeface, Typeface.BOLD) + } + text + } + val label = textView { textSize = 12f gravity = gravityCenter @@ -33,16 +66,20 @@ class StatusAreaEntryUi(override val ctx: Context, private val theme: Theme) : U override val root = object : CustomGestureView(ctx) { val content = constraintLayout { - add(icon, lParams(dp(48), dp(48)) { + add(bkg, lParams(dp(48), dp(48)) { topOfParent(dp(4)) - startOfParent() - endOfParent() + centerHorizontally() above(label) }) + add(icon, lParams { + centerOn(bkg) + }) + add(textIcon, lParams { + centerOn(bkg) + }) add(label, lParams(wrapContent, wrapContent) { - below(icon, dp(6)) - startOfParent() - endOfParent() + below(bkg, dp(6)) + centerHorizontally() }) } @@ -53,11 +90,19 @@ class StatusAreaEntryUi(override val ctx: Context, private val theme: Theme) : U } fun setEntry(entry: StatusAreaEntry) { - icon.imageDrawable = ctx.drawable(entry.icon) - icon.colorFilter = PorterDuffColorFilter( - if (entry.active) theme.genericActiveForegroundColor else theme.keyTextColor, - PorterDuff.Mode.SRC_IN - ) + val contentColor = + if (entry.active) theme.genericActiveForegroundColor else theme.keyTextColor + if (entry.icon != 0) { + icon.visibility = View.VISIBLE + textIcon.visibility = View.GONE + icon.imageDrawable = ctx.drawable(entry.icon) + icon.colorFilter = PorterDuffColorFilter(contentColor, PorterDuff.Mode.SRC_IN) + } else { + icon.visibility = View.GONE + textIcon.visibility = View.VISIBLE + textIcon.text = entry.label.substring(0, 1) + textIcon.setTextColor(contentColor) + } bkgDrawable.paint.color = if (entry.active) theme.genericActiveBackgroundColor else theme.keyBackgroundColor label.text = entry.label diff --git a/app/src/main/res/drawable/ic_baseline_text_format_24.xml b/app/src/main/res/drawable/ic_baseline_text_format_24.xml new file mode 100644 index 000000000..f13002b08 --- /dev/null +++ b/app/src/main/res/drawable/ic_baseline_text_format_24.xml @@ -0,0 +1,10 @@ + + + From d4e7faecacc26b5a9ebfeeed4435ed5999c7bc66 Mon Sep 17 00:00:00 2001 From: Rocka Date: Tue, 3 Oct 2023 13:24:50 +0800 Subject: [PATCH 024/381] Allow English word hint in InputContext with Sensitive flag --- app/src/main/cpp/androidkeyboard/androidkeyboard.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/app/src/main/cpp/androidkeyboard/androidkeyboard.cpp b/app/src/main/cpp/androidkeyboard/androidkeyboard.cpp index 2e0dffc23..49ab10318 100644 --- a/app/src/main/cpp/androidkeyboard/androidkeyboard.cpp +++ b/app/src/main/cpp/androidkeyboard/androidkeyboard.cpp @@ -287,8 +287,7 @@ bool AndroidKeyboardEngine::updateBuffer(InputContext *inputContext, const std:: auto *state = inputContext->propertyFor(&factory_); const CapabilityFlags noPredictFlag{CapabilityFlag::Password, - CapabilityFlag::NoSpellCheck, - CapabilityFlag::Sensitive}; + CapabilityFlag::NoSpellCheck}; // no spell hint enabled or no supported dictionary if (!*config_.enableWordHint || inputContext->capabilityFlags().testAny(noPredictFlag) || From 477a4a0e3cdf11d5165a752283331024a74bd974 Mon Sep 17 00:00:00 2001 From: Rocka Date: Tue, 3 Oct 2023 22:42:49 +0800 Subject: [PATCH 025/381] Fix PickerWindow RTL layout direction --- .../android/input/picker/PickerPageUi.kt | 24 +++++++++---------- 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/app/src/main/java/org/fcitx/fcitx5/android/input/picker/PickerPageUi.kt b/app/src/main/java/org/fcitx/fcitx5/android/input/picker/PickerPageUi.kt index d4794850a..d3bd132c7 100644 --- a/app/src/main/java/org/fcitx/fcitx5/android/input/picker/PickerPageUi.kt +++ b/app/src/main/java/org/fcitx/fcitx5/android/input/picker/PickerPageUi.kt @@ -28,11 +28,11 @@ import splitties.views.dsl.constraintlayout.below import splitties.views.dsl.constraintlayout.bottomOfParent import splitties.views.dsl.constraintlayout.bottomToTopOf import splitties.views.dsl.constraintlayout.constraintLayout -import splitties.views.dsl.constraintlayout.endOfParent -import splitties.views.dsl.constraintlayout.endToStartOf import splitties.views.dsl.constraintlayout.lParams -import splitties.views.dsl.constraintlayout.startOfParent -import splitties.views.dsl.constraintlayout.startToEndOf +import splitties.views.dsl.constraintlayout.leftOfParent +import splitties.views.dsl.constraintlayout.leftToRightOf +import splitties.views.dsl.constraintlayout.rightOfParent +import splitties.views.dsl.constraintlayout.rightToLeftOf import splitties.views.dsl.constraintlayout.topOfParent import splitties.views.dsl.constraintlayout.topToBottomOf import splitties.views.dsl.core.Ui @@ -128,16 +128,16 @@ class PickerPageUi(override val ctx: Context, val theme: Theme, private val dens // not last row, align bottom to top of first view in next row bottomToTopOf(keyViews[(row + 1) * columnCount]) } - // layout_constraintEnd_to + // layout_constraintRight_to if (i == keyViews.size - 1) { // last key (likely not last column), align end to start of backspace button - endToStartOf(backspaceKey) + rightToLeftOf(backspaceKey) } else if (column == columnCount - 1) { // last column, align end to end of parent - endOfParent() + rightOfParent() } else { // neither, align end to start of next view - endToStartOf(keyViews[i + 1]) + rightToLeftOf(keyViews[i + 1]) } matchConstraintPercentWidth = keyWidth }) @@ -165,13 +165,13 @@ class PickerPageUi(override val ctx: Context, val theme: Theme, private val dens // not last row, align bottom to top of first view in next row bottomToTopOf(keyViews[(row + 1) * columnCount]) } - // layout_constraintStart_to + // layout_constraintLeft_to if (column == 0) { // first column, align start to start of parent - startOfParent() + leftOfParent() } else { // not first column, align start to end of last column - startToEndOf(keyViews[i - 1]) + leftToRightOf(keyViews[i - 1]) } matchConstraintPercentWidth = keyWidth }) @@ -184,7 +184,7 @@ class PickerPageUi(override val ctx: Context, val theme: Theme, private val dens below(keyViews[(rowCount - 2) * columnCount]) // bottom/right corner bottomOfParent() - endOfParent() + rightOfParent() matchConstraintPercentWidth = 0.15f }) } From fdef8e1c39cce645983539b889c65570f4dd9c85 Mon Sep 17 00:00:00 2001 From: Rocka Date: Tue, 3 Oct 2023 23:58:39 +0800 Subject: [PATCH 026/381] Support separator in StatusArea menu --- .../android/input/status/StatusAreaEntry.kt | 16 +++++---- .../android/input/status/StatusAreaWindow.kt | 35 +++++++++++-------- 2 files changed, 30 insertions(+), 21 deletions(-) diff --git a/app/src/main/java/org/fcitx/fcitx5/android/input/status/StatusAreaEntry.kt b/app/src/main/java/org/fcitx/fcitx5/android/input/status/StatusAreaEntry.kt index b7bf4cd54..f3eb0a6a4 100644 --- a/app/src/main/java/org/fcitx/fcitx5/android/input/status/StatusAreaEntry.kt +++ b/app/src/main/java/org/fcitx/fcitx5/android/input/status/StatusAreaEntry.kt @@ -24,9 +24,7 @@ sealed class StatusAreaEntry( StatusAreaEntry(label, icon, active) companion object { - private fun Action.isActive() = icon.endsWith("-active") || isChecked - - private fun drawableRes(icon: String, active: Boolean = false) = when (icon) { + private fun drawableFromIconName(icon: String) = when (icon) { // androidkeyboard "tools-check-spelling" -> R.drawable.ic_baseline_spellcheck_24 // fcitx5-chinese-addons @@ -44,12 +42,18 @@ sealed class StatusAreaEntry( "edit-find" -> R.drawable.ic_baseline_search_24 // fallback "" -> 0 - else -> if (active) R.drawable.ic_baseline_code_24 else R.drawable.ic_baseline_code_off_24 + else -> { + if (icon.endsWith("-inactive")) { + R.drawable.ic_baseline_code_off_24 + } else { + R.drawable.ic_baseline_code_24 + } + } } fun fromAction(it: Action): Fcitx { - val active = it.isActive() - return Fcitx(it, it.shortText, drawableRes(it.icon, active), active) + val active = it.icon.endsWith("-active") || it.isChecked + return Fcitx(it, it.shortText, drawableFromIconName(it.icon), active) } } } \ No newline at end of file diff --git a/app/src/main/java/org/fcitx/fcitx5/android/input/status/StatusAreaWindow.kt b/app/src/main/java/org/fcitx/fcitx5/android/input/status/StatusAreaWindow.kt index 4aedfeeba..374888325 100644 --- a/app/src/main/java/org/fcitx/fcitx5/android/input/status/StatusAreaWindow.kt +++ b/app/src/main/java/org/fcitx/fcitx5/android/input/status/StatusAreaWindow.kt @@ -3,6 +3,7 @@ package org.fcitx.fcitx5.android.input.status import android.view.View import android.widget.PopupMenu import android.widget.Toast +import androidx.core.view.MenuCompat import androidx.lifecycle.lifecycleScope import kotlinx.coroutines.launch import org.fcitx.fcitx5.android.R @@ -10,7 +11,6 @@ import org.fcitx.fcitx5.android.core.Action import org.fcitx.fcitx5.android.daemon.FcitxConnection import org.fcitx.fcitx5.android.daemon.launchOnReady import org.fcitx.fcitx5.android.data.prefs.AppPrefs -import org.fcitx.fcitx5.android.data.theme.Theme import org.fcitx.fcitx5.android.data.theme.ThemeManager import org.fcitx.fcitx5.android.input.FcitxInputMethodService import org.fcitx.fcitx5.android.input.bar.ui.ToolButton @@ -71,8 +71,8 @@ class StatusAreaWindow : InputWindow.ExtendedInputWindow(), } private fun activateAction(action: Action) { - fcitx.launchOnReady { f -> - f.activateAction(action.id) + fcitx.launchOnReady { + it.activateAction(action.id) } } @@ -82,20 +82,26 @@ class StatusAreaWindow : InputWindow.ExtendedInputWindow(), when (entry) { is StatusAreaEntry.Fcitx -> { val menu = entry.action.menu - if (menu != null && menu.isNotEmpty()) { - val popup = PopupMenu(context, view) - menu.forEach { action -> - popup.menu.add(action.shortText).apply { - setOnMenuItemClickListener { - activateAction(action) + if (menu.isNullOrEmpty()) { + activateAction(entry.action) + return + } + val popup = PopupMenu(context, view) + var groupId = 0 // Menu.NONE; ungrouped + menu.forEach { + if (it.isSeparator) { + groupId++ + } else { + popup.menu.add(groupId, 0, 0, it.shortText).apply { + setOnMenuItemClickListener { _ -> + activateAction(it) true } } } - popup.show() - } else { - activateAction(entry.action) } + MenuCompat.setGroupDividerEnabled(popup.menu, true) + popup.show() } is StatusAreaEntry.Android -> when (entry.type) { InputMethod -> fcitx.runImmediately { inputMethodEntryCached }.let { @@ -115,8 +121,7 @@ class StatusAreaWindow : InputWindow.ExtendedInputWindow(), } } - override val theme: Theme - get() = this@StatusAreaWindow.theme + override val theme = this@StatusAreaWindow.theme } } @@ -135,7 +140,7 @@ class StatusAreaWindow : InputWindow.ExtendedInputWindow(), override fun onStatusAreaUpdate(actions: Array) { adapter.entries = arrayOf( *staticEntries, - *actions.map { StatusAreaEntry.fromAction(it) }.toTypedArray() + *Array(actions.size) { StatusAreaEntry.fromAction(actions[it]) } ) } From d981724a81221b410ae911af6eeb311edb1c8450 Mon Sep 17 00:00:00 2001 From: Rocka Date: Wed, 4 Oct 2023 00:00:32 +0800 Subject: [PATCH 027/381] Remove tint attr from drawable resources --- .../android/input/bar/KawaiiBarComponent.kt | 5 +- .../android/input/bar/ui/CandidateUi.kt | 9 +- .../fcitx5/android/input/bar/ui/IdleUi.kt | 12 ++- .../fcitx5/android/input/bar/ui/ToolButton.kt | 21 ++--- .../bar/ui/idle/ClipboardSuggestionUi.kt | 29 +++++-- .../input/clipboard/ClipboardAdapter.kt | 5 +- .../input/clipboard/ClipboardEntryUi.kt | 12 +-- .../input/clipboard/ClipboardInstructionUi.kt | 23 +++-- .../android/input/editing/TextEditingUi.kt | 10 +-- .../fcitx5/android/input/keyboard/KeyView.kt | 10 +-- .../android/input/keyboard/TextKeyboard.kt | 14 ++-- .../android/input/picker/PickerTabsUi.kt | 30 ++++--- .../fcitx5/android/input/popup/PopupMenuUi.kt | 10 +-- .../android/input/status/StatusAreaEntryUi.kt | 7 +- .../android/ui/common/BaseDynamicListUi.kt | 12 ++- .../android/ui/common/DynamicListEntryUi.kt | 37 ++++++--- .../ui/common/DynamicListTouchCallback.kt | 59 ++++++------- .../fcitx5/android/ui/main/LogActivity.kt | 10 ++- .../fcitx5/android/ui/main/MainActivity.kt | 14 +++- .../ui/main/settings/KeyPreferenceUi.kt | 30 +++++-- .../main/settings/QuickPhraseListFragment.kt | 15 ++-- .../settings/theme/CustomThemeActivity.kt | 9 +- .../ui/main/settings/theme/NewThemeEntryUi.kt | 21 +++-- .../main/settings/theme/ThemeListFragment.kt | 4 +- .../main/settings/theme/ThemeThumbnailUi.kt | 29 +++---- .../fcitx5/android/utils/PreferenceScreen.kt | 6 +- .../ic_launcher_foreground_debug.xml | 24 +++--- .../drawable/ic_baseline_access_time_24.xml | 7 +- .../res/drawable/ic_baseline_android_24.xml | 4 +- .../drawable/ic_baseline_arrow_back_24.xml | 11 ++- .../ic_baseline_arrow_downward_24.xml | 9 +- .../ic_baseline_arrow_drop_down_24.xml | 9 +- .../drawable/ic_baseline_arrow_forward_24.xml | 11 ++- .../drawable/ic_baseline_arrow_upward_24.xml | 9 +- .../res/drawable/ic_baseline_backspace_24.xml | 10 +-- .../main/res/drawable/ic_baseline_cake_24.xml | 5 +- .../res/drawable/ic_baseline_check_24.xml | 9 +- .../drawable/ic_baseline_check_circle_24.xml | 9 +- .../main/res/drawable/ic_baseline_code_24.xml | 9 +- .../res/drawable/ic_baseline_code_off_24.xml | 9 +- .../drawable/ic_baseline_content_paste_24.xml | 9 +- .../res/drawable/ic_baseline_dark_mode_24.xml | 5 +- .../res/drawable/ic_baseline_delete_24.xml | 9 +- .../drawable/ic_baseline_delete_sweep_24.xml | 9 +- .../ic_baseline_developer_mode_24.xml | 9 +- .../ic_baseline_directions_car_24.xml | 5 +- .../main/res/drawable/ic_baseline_done_24.xml | 9 +- .../drawable/ic_baseline_drag_handle_24.xml | 9 +- .../main/res/drawable/ic_baseline_edit_24.xml | 9 +- .../drawable/ic_baseline_emoji_objects_24.xml | 5 +- .../drawable/ic_baseline_emoji_people_24.xml | 7 +- .../drawable/ic_baseline_emoji_symbols_24.xml | 17 ++-- .../drawable/ic_baseline_expand_less_24.xml | 9 +- .../drawable/ic_baseline_expand_more_24.xml | 9 +- .../res/drawable/ic_baseline_extension_24.xml | 9 +- .../drawable/ic_baseline_first_page_24.xml | 9 +- .../main/res/drawable/ic_baseline_flag_24.xml | 5 +- .../res/drawable/ic_baseline_flower_24.xml | 5 +- .../drawable/ic_baseline_format_quote_24.xml | 9 +- .../main/res/drawable/ic_baseline_info_24.xml | 5 +- .../res/drawable/ic_baseline_keyboard_24.xml | 9 +- .../ic_baseline_keyboard_arrow_down_24.xml | 9 +- .../ic_baseline_keyboard_arrow_left_24.xml | 9 +- .../ic_baseline_keyboard_arrow_right_24.xml | 9 +- .../ic_baseline_keyboard_arrow_up_24.xml | 9 +- .../ic_baseline_keyboard_return_24.xml | 9 +- .../drawable/ic_baseline_keyboard_tab_24.xml | 9 +- .../ic_baseline_keyboard_tab_reverse_24.xml | 9 +- .../ic_baseline_keyboard_voice_24.xml | 3 +- .../res/drawable/ic_baseline_language_24.xml | 9 +- .../res/drawable/ic_baseline_last_page_24.xml | 9 +- .../drawable/ic_baseline_library_books_24.xml | 10 +-- .../drawable/ic_baseline_light_mode_24.xml | 5 +- .../drawable/ic_baseline_more_horiz_24.xml | 9 +- .../res/drawable/ic_baseline_palette_24.xml | 9 +- .../main/res/drawable/ic_baseline_plus_24.xml | 3 +- .../res/drawable/ic_baseline_push_pin_24.xml | 10 +-- .../main/res/drawable/ic_baseline_redo_24.xml | 11 ++- .../main/res/drawable/ic_baseline_save_24.xml | 9 +- .../res/drawable/ic_baseline_search_24.xml | 9 +- .../main/res/drawable/ic_baseline_send_24.xml | 10 +-- .../res/drawable/ic_baseline_settings_24.xml | 9 +- ...ic_baseline_settings_backup_restore_24.xml | 9 +- .../res/drawable/ic_baseline_space_bar_24.xml | 9 +- .../drawable/ic_baseline_spellcheck_24.xml | 5 +- .../ic_baseline_sports_basketball_24.xml | 19 ++--- .../main/res/drawable/ic_baseline_sync_24.xml | 9 +- .../res/drawable/ic_baseline_tag_faces_24.xml | 5 +- .../drawable/ic_baseline_text_format_24.xml | 3 +- .../main/res/drawable/ic_baseline_tune_24.xml | 9 +- .../main/res/drawable/ic_baseline_undo_24.xml | 11 ++- .../main/res/drawable/ic_capslock_lock.xml | 15 ++-- .../main/res/drawable/ic_capslock_none.xml | 9 +- .../main/res/drawable/ic_capslock_once.xml | 9 +- app/src/main/res/drawable/ic_clipboard.xml | 9 +- app/src/main/res/drawable/ic_cursor_move.xml | 21 +++-- .../ic_fcitx_status_chttrans_simp.xml | 9 +- .../ic_fcitx_status_chttrans_trad.xml | 9 +- .../ic_fcitx_status_fullwidth_active.xml | 9 +- .../ic_fcitx_status_fullwidth_inactive.xml | 9 +- .../ic_fcitx_status_prediction_active.xml | 9 +- .../ic_fcitx_status_prediction_inactive.xml | 9 +- .../drawable/ic_fcitx_status_punc_active.xml | 9 +- .../ic_fcitx_status_punc_inactive.xml | 9 +- .../res/drawable/ic_launcher_background.xml | 83 ++++++++++--------- app/src/main/res/drawable/ic_logo_unicode.xml | 9 +- app/src/main/res/drawable/ic_number_pad.xml | 27 +++--- .../res/drawable/ic_outline_push_pin_24.xml | 9 +- app/src/main/res/drawable/ic_view_private.xml | 15 ++-- app/src/main/res/drawable/symbol_arrow.xml | 5 +- app/src/main/res/drawable/symbol_bracket.xml | 15 ++-- .../main/res/drawable/symbol_fullwidth.xml | 21 +++-- app/src/main/res/drawable/symbol_math.xml | 11 ++- .../drawable/symbol_number_punctuation.xml | 27 +++--- app/src/main/res/drawable/symbol_ordinal.xml | 15 ++-- app/src/main/res/drawable/symbol_other.xml | 5 +- 116 files changed, 685 insertions(+), 698 deletions(-) diff --git a/app/src/main/java/org/fcitx/fcitx5/android/input/bar/KawaiiBarComponent.kt b/app/src/main/java/org/fcitx/fcitx5/android/input/bar/KawaiiBarComponent.kt index bb0cb8ad3..a40e4fa07 100644 --- a/app/src/main/java/org/fcitx/fcitx5/android/input/bar/KawaiiBarComponent.kt +++ b/app/src/main/java/org/fcitx/fcitx5/android/input/bar/KawaiiBarComponent.kt @@ -59,7 +59,6 @@ import splitties.views.backgroundColor import splitties.views.dsl.core.add import splitties.views.dsl.core.lParams import splitties.views.dsl.core.matchParent -import splitties.views.imageResource import java.util.concurrent.Executor import kotlin.coroutines.resume import kotlin.coroutines.suspendCoroutine @@ -255,7 +254,7 @@ class KawaiiBarComponent : UniqueViewComponent( } ) } - candidateUi.expandButton.image.imageResource = R.drawable.ic_baseline_expand_more_24 + candidateUi.expandButton.setIcon(R.drawable.ic_baseline_expand_more_24) } // set expand candidate button to close expand candidate @@ -263,7 +262,7 @@ class KawaiiBarComponent : UniqueViewComponent( candidateUi.expandButton.setOnClickListener { windowManager.attachWindow(KeyboardWindow) } - candidateUi.expandButton.image.imageResource = R.drawable.ic_baseline_expand_less_24 + candidateUi.expandButton.setIcon(R.drawable.ic_baseline_expand_less_24) } // should be used with setExpandButtonToAttach or setExpandButtonToDetach diff --git a/app/src/main/java/org/fcitx/fcitx5/android/input/bar/ui/CandidateUi.kt b/app/src/main/java/org/fcitx/fcitx5/android/input/bar/ui/CandidateUi.kt index 82b1614a1..17b8a8f63 100644 --- a/app/src/main/java/org/fcitx/fcitx5/android/input/bar/ui/CandidateUi.kt +++ b/app/src/main/java/org/fcitx/fcitx5/android/input/bar/ui/CandidateUi.kt @@ -5,7 +5,12 @@ import android.view.View import org.fcitx.fcitx5.android.R import org.fcitx.fcitx5.android.data.theme.Theme import splitties.dimensions.dp -import splitties.views.dsl.constraintlayout.* +import splitties.views.dsl.constraintlayout.before +import splitties.views.dsl.constraintlayout.centerVertically +import splitties.views.dsl.constraintlayout.constraintLayout +import splitties.views.dsl.constraintlayout.endOfParent +import splitties.views.dsl.constraintlayout.lParams +import splitties.views.dsl.constraintlayout.startOfParent import splitties.views.dsl.core.Ui import splitties.views.dsl.core.add import splitties.views.dsl.core.view @@ -13,7 +18,7 @@ import splitties.views.dsl.core.view class CandidateUi(override val ctx: Context, theme: Theme, private val horizontalView: View) : Ui { val expandButton = view(::ToolButton, R.id.expand_candidate_btn) { - setIcon(R.drawable.ic_baseline_expand_more_24, theme.altKeyTextColor) + setIcon(R.drawable.ic_baseline_expand_more_24) setPressHighlightColor(theme.keyPressHighlightColor) visibility = View.INVISIBLE } diff --git a/app/src/main/java/org/fcitx/fcitx5/android/input/bar/ui/IdleUi.kt b/app/src/main/java/org/fcitx/fcitx5/android/input/bar/ui/IdleUi.kt index 04c69e003..48f81fa7a 100644 --- a/app/src/main/java/org/fcitx/fcitx5/android/input/bar/ui/IdleUi.kt +++ b/app/src/main/java/org/fcitx/fcitx5/android/input/bar/ui/IdleUi.kt @@ -18,7 +18,14 @@ import org.fcitx.fcitx5.android.input.bar.ui.idle.NumberRow import org.fcitx.fcitx5.android.input.keyboard.CommonKeyActionListener import org.fcitx.fcitx5.android.input.popup.PopupComponent import splitties.dimensions.dp -import splitties.views.dsl.constraintlayout.* +import splitties.views.dsl.constraintlayout.after +import splitties.views.dsl.constraintlayout.before +import splitties.views.dsl.constraintlayout.centerVertically +import splitties.views.dsl.constraintlayout.constraintLayout +import splitties.views.dsl.constraintlayout.endOfParent +import splitties.views.dsl.constraintlayout.lParams +import splitties.views.dsl.constraintlayout.matchConstraints +import splitties.views.dsl.constraintlayout.startOfParent import splitties.views.dsl.core.Ui import splitties.views.dsl.core.add import splitties.views.dsl.core.lParams @@ -144,8 +151,7 @@ class IdleUi( fun setHideKeyboardIsVoiceInput(isVoiceInput: Boolean, callback: View.OnClickListener) { hideKeyboardButton.setIcon( if (isVoiceInput) R.drawable.ic_baseline_keyboard_voice_24 - else R.drawable.ic_baseline_arrow_drop_down_24, - theme.altKeyTextColor + else R.drawable.ic_baseline_arrow_drop_down_24 ) hideKeyboardButton.setOnClickListener(callback) } diff --git a/app/src/main/java/org/fcitx/fcitx5/android/input/bar/ui/ToolButton.kt b/app/src/main/java/org/fcitx/fcitx5/android/input/bar/ui/ToolButton.kt index 5007f3344..f68c56bbc 100644 --- a/app/src/main/java/org/fcitx/fcitx5/android/input/bar/ui/ToolButton.kt +++ b/app/src/main/java/org/fcitx/fcitx5/android/input/bar/ui/ToolButton.kt @@ -1,8 +1,7 @@ package org.fcitx.fcitx5.android.input.bar.ui import android.content.Context -import android.graphics.PorterDuff -import android.graphics.PorterDuffColorFilter +import android.content.res.ColorStateList import android.widget.ImageView import androidx.annotation.ColorInt import androidx.annotation.DrawableRes @@ -12,13 +11,12 @@ import org.fcitx.fcitx5.android.input.keyboard.CustomGestureView import org.fcitx.fcitx5.android.utils.borderlessRippleDrawable import org.fcitx.fcitx5.android.utils.circlePressHighlightDrawable import splitties.dimensions.dp -import splitties.resources.drawable import splitties.views.dsl.core.add import splitties.views.dsl.core.imageView import splitties.views.dsl.core.lParams import splitties.views.dsl.core.wrapContent import splitties.views.gravityCenter -import splitties.views.imageDrawable +import splitties.views.imageResource import splitties.views.padding class ToolButton(context: Context) : CustomGestureView(context) { @@ -27,11 +25,6 @@ class ToolButton(context: Context) : CustomGestureView(context) { val disableAnimation by AppPrefs.getInstance().advanced.disableAnimation } - constructor(context: Context, @DrawableRes icon: Int, theme: Theme) : this(context) { - setIcon(icon, theme.altKeyTextColor) - setPressHighlightColor(theme.keyPressHighlightColor) - } - val image = imageView { isClickable = false isFocusable = false @@ -39,13 +32,15 @@ class ToolButton(context: Context) : CustomGestureView(context) { scaleType = ImageView.ScaleType.CENTER_INSIDE } - init { + constructor(context: Context, @DrawableRes icon: Int, theme: Theme) : this(context) { + image.imageTintList = ColorStateList.valueOf(theme.altKeyTextColor) + setIcon(icon) + setPressHighlightColor(theme.keyPressHighlightColor) add(image, lParams(wrapContent, wrapContent, gravityCenter)) } - fun setIcon(@DrawableRes icon: Int, @ColorInt color: Int) { - image.imageDrawable = drawable(icon) - image.colorFilter = PorterDuffColorFilter(color, PorterDuff.Mode.SRC_IN) + fun setIcon(@DrawableRes icon: Int) { + image.imageResource = icon } fun setPressHighlightColor(@ColorInt color: Int) { diff --git a/app/src/main/java/org/fcitx/fcitx5/android/input/bar/ui/idle/ClipboardSuggestionUi.kt b/app/src/main/java/org/fcitx/fcitx5/android/input/bar/ui/idle/ClipboardSuggestionUi.kt index 8d98d6da2..e48515236 100644 --- a/app/src/main/java/org/fcitx/fcitx5/android/input/bar/ui/idle/ClipboardSuggestionUi.kt +++ b/app/src/main/java/org/fcitx/fcitx5/android/input/bar/ui/idle/ClipboardSuggestionUi.kt @@ -1,23 +1,38 @@ package org.fcitx.fcitx5.android.input.bar.ui.idle import android.content.Context -import android.graphics.PorterDuff -import android.graphics.PorterDuffColorFilter import android.text.TextUtils import org.fcitx.fcitx5.android.R import org.fcitx.fcitx5.android.data.theme.Theme import org.fcitx.fcitx5.android.input.keyboard.CustomGestureView import org.fcitx.fcitx5.android.utils.rippleDrawable import splitties.dimensions.dp -import splitties.views.dsl.constraintlayout.* -import splitties.views.dsl.core.* -import splitties.views.imageResource +import splitties.resources.drawable +import splitties.views.dsl.constraintlayout.after +import splitties.views.dsl.constraintlayout.before +import splitties.views.dsl.constraintlayout.centerInParent +import splitties.views.dsl.constraintlayout.centerVertically +import splitties.views.dsl.constraintlayout.constraintLayout +import splitties.views.dsl.constraintlayout.endOfParent +import splitties.views.dsl.constraintlayout.lParams +import splitties.views.dsl.constraintlayout.matchConstraints +import splitties.views.dsl.constraintlayout.startOfParent +import splitties.views.dsl.core.Ui +import splitties.views.dsl.core.add +import splitties.views.dsl.core.imageView +import splitties.views.dsl.core.lParams +import splitties.views.dsl.core.matchParent +import splitties.views.dsl.core.textView +import splitties.views.dsl.core.verticalMargin +import splitties.views.dsl.core.wrapContent +import splitties.views.imageDrawable class ClipboardSuggestionUi(override val ctx: Context, private val theme: Theme) : Ui { private val icon = imageView { - imageResource = R.drawable.ic_clipboard - colorFilter = PorterDuffColorFilter(theme.altKeyTextColor, PorterDuff.Mode.SRC_IN) + imageDrawable = drawable(R.drawable.ic_clipboard)!!.apply { + setTint(theme.altKeyTextColor) + } } val text = textView { diff --git a/app/src/main/java/org/fcitx/fcitx5/android/input/clipboard/ClipboardAdapter.kt b/app/src/main/java/org/fcitx/fcitx5/android/input/clipboard/ClipboardAdapter.kt index a62fe2a85..b00dc8f26 100644 --- a/app/src/main/java/org/fcitx/fcitx5/android/input/clipboard/ClipboardAdapter.kt +++ b/app/src/main/java/org/fcitx/fcitx5/android/input/clipboard/ClipboardAdapter.kt @@ -1,7 +1,5 @@ package org.fcitx.fcitx5.android.input.clipboard -import android.graphics.PorterDuff -import android.graphics.PorterDuffColorFilter import android.os.Build import android.view.ViewGroup import android.widget.PopupMenu @@ -79,11 +77,10 @@ abstract class ClipboardAdapter : root.setOnLongClickListener { popupMenu?.dismiss() val iconColor = ctx.styledColor(android.R.attr.colorControlNormal) - val iconColorFilter = PorterDuffColorFilter(iconColor, PorterDuff.Mode.SRC_IN) val popup = PopupMenu(ctx, root) fun menuItem(@StringRes title: Int, @DrawableRes ic: Int, callback: () -> Unit) { popup.menu.add(title).apply { - icon = ctx.drawable(ic)?.apply { colorFilter = iconColorFilter } + icon = ctx.drawable(ic)?.apply { setTint(iconColor) } setOnMenuItemClickListener { callback() true diff --git a/app/src/main/java/org/fcitx/fcitx5/android/input/clipboard/ClipboardEntryUi.kt b/app/src/main/java/org/fcitx/fcitx5/android/input/clipboard/ClipboardEntryUi.kt index d9871dd4a..d8040c9b4 100644 --- a/app/src/main/java/org/fcitx/fcitx5/android/input/clipboard/ClipboardEntryUi.kt +++ b/app/src/main/java/org/fcitx/fcitx5/android/input/clipboard/ClipboardEntryUi.kt @@ -3,8 +3,6 @@ package org.fcitx.fcitx5.android.input.clipboard import android.content.Context import android.content.res.ColorStateList import android.graphics.Color -import android.graphics.PorterDuff -import android.graphics.PorterDuffColorFilter import android.graphics.drawable.GradientDrawable import android.graphics.drawable.RippleDrawable import android.text.TextUtils @@ -12,6 +10,7 @@ import android.view.View import org.fcitx.fcitx5.android.R import org.fcitx.fcitx5.android.data.theme.Theme import splitties.dimensions.dp +import splitties.resources.drawable import splitties.views.dsl.constraintlayout.bottomOfParent import splitties.views.dsl.constraintlayout.centerVertically import splitties.views.dsl.constraintlayout.constraintLayout @@ -23,7 +22,7 @@ import splitties.views.dsl.core.imageView import splitties.views.dsl.core.matchParent import splitties.views.dsl.core.textView import splitties.views.dsl.core.wrapContent -import splitties.views.imageResource +import splitties.views.imageDrawable import splitties.views.setPaddingDp class ClipboardEntryUi(override val ctx: Context, private val theme: Theme) : Ui { @@ -38,9 +37,10 @@ class ClipboardEntryUi(override val ctx: Context, private val theme: Theme) : Ui } val pin = imageView { - imageResource = R.drawable.ic_baseline_push_pin_24 - colorFilter = PorterDuffColorFilter(theme.altKeyTextColor, PorterDuff.Mode.SRC_IN) - alpha = 0.3f + imageDrawable = drawable(R.drawable.ic_baseline_push_pin_24)!!.apply { + setTint(theme.altKeyTextColor) + setAlpha(0.3f) + } } override val root = constraintLayout { diff --git a/app/src/main/java/org/fcitx/fcitx5/android/input/clipboard/ClipboardInstructionUi.kt b/app/src/main/java/org/fcitx/fcitx5/android/input/clipboard/ClipboardInstructionUi.kt index 0ae8c217d..99226949e 100644 --- a/app/src/main/java/org/fcitx/fcitx5/android/input/clipboard/ClipboardInstructionUi.kt +++ b/app/src/main/java/org/fcitx/fcitx5/android/input/clipboard/ClipboardInstructionUi.kt @@ -1,15 +1,23 @@ package org.fcitx.fcitx5.android.input.clipboard import android.content.Context -import android.graphics.PorterDuff -import android.graphics.PorterDuffColorFilter import org.fcitx.fcitx5.android.R import org.fcitx.fcitx5.android.data.theme.Theme import splitties.dimensions.dp +import splitties.resources.drawable +import splitties.views.dsl.constraintlayout.below +import splitties.views.dsl.constraintlayout.constraintLayout +import splitties.views.dsl.constraintlayout.endOfParent +import splitties.views.dsl.constraintlayout.lParams +import splitties.views.dsl.constraintlayout.startOfParent +import splitties.views.dsl.constraintlayout.topOfParent +import splitties.views.dsl.core.Ui +import splitties.views.dsl.core.add +import splitties.views.dsl.core.imageView import splitties.views.dsl.core.styles.AndroidStyles -import splitties.views.dsl.constraintlayout.* -import splitties.views.dsl.core.* -import splitties.views.imageResource +import splitties.views.dsl.core.textView +import splitties.views.dsl.core.wrapContent +import splitties.views.imageDrawable import splitties.views.setPaddingDp sealed class ClipboardInstructionUi(override val ctx: Context, protected val theme: Theme) : Ui { @@ -45,8 +53,9 @@ sealed class ClipboardInstructionUi(override val ctx: Context, protected val the class Empty(ctx: Context, theme: Theme) : ClipboardInstructionUi(ctx, theme) { private val icon = imageView { - imageResource = R.drawable.ic_baseline_content_paste_24 - colorFilter = PorterDuffColorFilter(theme.altKeyTextColor, PorterDuff.Mode.SRC_IN) + imageDrawable = drawable(R.drawable.ic_baseline_content_paste_24)!!.apply { + setTint(theme.altKeyTextColor) + } } private val instructionText = textView { diff --git a/app/src/main/java/org/fcitx/fcitx5/android/input/editing/TextEditingUi.kt b/app/src/main/java/org/fcitx/fcitx5/android/input/editing/TextEditingUi.kt index 7005847cb..85b4dab3d 100644 --- a/app/src/main/java/org/fcitx/fcitx5/android/input/editing/TextEditingUi.kt +++ b/app/src/main/java/org/fcitx/fcitx5/android/input/editing/TextEditingUi.kt @@ -2,8 +2,6 @@ package org.fcitx.fcitx5.android.input.editing import android.content.Context import android.content.res.ColorStateList -import android.graphics.PorterDuff -import android.graphics.PorterDuffColorFilter import android.graphics.drawable.StateListDrawable import android.view.View import androidx.annotation.DrawableRes @@ -18,6 +16,7 @@ import org.fcitx.fcitx5.android.utils.borderDrawable import org.fcitx.fcitx5.android.utils.pressHighlightDrawable import org.fcitx.fcitx5.android.utils.rippleDrawable import splitties.dimensions.dp +import splitties.resources.drawable import splitties.views.dsl.constraintlayout.above import splitties.views.dsl.constraintlayout.below import splitties.views.dsl.constraintlayout.bottomOfParent @@ -36,7 +35,7 @@ import splitties.views.dsl.core.lParams import splitties.views.dsl.core.textView import splitties.views.dsl.core.wrapContent import splitties.views.gravityCenter -import splitties.views.imageResource +import splitties.views.imageDrawable import splitties.views.padding class TextEditingUi(override val ctx: Context, private val theme: Theme) : Ui { @@ -83,8 +82,9 @@ class TextEditingUi(override val ctx: Context, private val theme: Theme) : Ui { } private fun iconButton(@DrawableRes icon: Int) = GImageButton(ctx).apply { - image.imageResource = icon - image.colorFilter = PorterDuffColorFilter(theme.altKeyTextColor, PorterDuff.Mode.SRC_IN) + image.imageDrawable = drawable(icon)!!.apply { + setTint(theme.altKeyTextColor) + } padding = dp(10) applyBorderedBackground() } diff --git a/app/src/main/java/org/fcitx/fcitx5/android/input/keyboard/KeyView.kt b/app/src/main/java/org/fcitx/fcitx5/android/input/keyboard/KeyView.kt index 7b206d3f3..569e29568 100644 --- a/app/src/main/java/org/fcitx/fcitx5/android/input/keyboard/KeyView.kt +++ b/app/src/main/java/org/fcitx/fcitx5/android/input/keyboard/KeyView.kt @@ -23,7 +23,6 @@ import org.fcitx.fcitx5.android.input.keyboard.KeyDef.Appearance.Variant import org.fcitx.fcitx5.android.utils.styledFloat import org.fcitx.fcitx5.android.utils.unset import splitties.dimensions.dp -import splitties.resources.drawable import splitties.views.dsl.constraintlayout.centerHorizontally import splitties.views.dsl.constraintlayout.centerInParent import splitties.views.dsl.constraintlayout.constraintLayout @@ -31,7 +30,7 @@ import splitties.views.dsl.constraintlayout.lParams import splitties.views.dsl.constraintlayout.parentId import splitties.views.dsl.core.* import splitties.views.existingOrNewId -import splitties.views.imageDrawable +import splitties.views.imageResource import splitties.views.padding import kotlin.math.min @@ -330,15 +329,14 @@ class ImageKeyView(ctx: Context, theme: Theme, def: KeyDef.Appearance.Image) : private fun ImageView.configure(theme: Theme, @DrawableRes src: Int, variant: Variant) = apply { isClickable = false isFocusable = false - imageDrawable = drawable(src) - colorFilter = PorterDuffColorFilter( + imageTintList = ColorStateList.valueOf( when (variant) { Variant.Normal -> theme.keyTextColor Variant.AltForeground, Variant.Alternative -> theme.altKeyTextColor Variant.Accent -> theme.accentKeyTextColor - }, - PorterDuff.Mode.SRC_IN + } ) + imageResource = src } @SuppressLint("ViewConstructor") diff --git a/app/src/main/java/org/fcitx/fcitx5/android/input/keyboard/TextKeyboard.kt b/app/src/main/java/org/fcitx/fcitx5/android/input/keyboard/TextKeyboard.kt index 3e00022d7..1223a55f2 100644 --- a/app/src/main/java/org/fcitx/fcitx5/android/input/keyboard/TextKeyboard.kt +++ b/app/src/main/java/org/fcitx/fcitx5/android/input/keyboard/TextKeyboard.kt @@ -11,8 +11,6 @@ import org.fcitx.fcitx5.android.data.prefs.AppPrefs import org.fcitx.fcitx5.android.data.prefs.ManagedPreference import org.fcitx.fcitx5.android.data.theme.Theme import org.fcitx.fcitx5.android.input.popup.PopupAction -import splitties.resources.drawable -import splitties.views.imageDrawable import splitties.views.imageResource @SuppressLint("ViewConstructor") @@ -185,13 +183,11 @@ class TextKeyboard( private fun updateCapsButtonIcon() { caps.img.apply { - imageDrawable = drawable( - when (capsState) { - CapsState.None -> R.drawable.ic_capslock_none - CapsState.Once -> R.drawable.ic_capslock_once - CapsState.Lock -> R.drawable.ic_capslock_lock - } - ) + imageResource = when (capsState) { + CapsState.None -> R.drawable.ic_capslock_none + CapsState.Once -> R.drawable.ic_capslock_once + CapsState.Lock -> R.drawable.ic_capslock_lock + } } } diff --git a/app/src/main/java/org/fcitx/fcitx5/android/input/picker/PickerTabsUi.kt b/app/src/main/java/org/fcitx/fcitx5/android/input/picker/PickerTabsUi.kt index e3bbf133d..54dedd1a1 100644 --- a/app/src/main/java/org/fcitx/fcitx5/android/input/picker/PickerTabsUi.kt +++ b/app/src/main/java/org/fcitx/fcitx5/android/input/picker/PickerTabsUi.kt @@ -1,8 +1,6 @@ package org.fcitx.fcitx5.android.input.picker import android.content.Context -import android.graphics.PorterDuff -import android.graphics.PorterDuffColorFilter import android.graphics.Typeface import androidx.annotation.DrawableRes import androidx.core.view.isVisible @@ -13,8 +11,19 @@ import org.fcitx.fcitx5.android.utils.alpha import org.fcitx.fcitx5.android.utils.pressHighlightDrawable import org.fcitx.fcitx5.android.utils.rippleDrawable import splitties.resources.drawable -import splitties.views.dsl.constraintlayout.* -import splitties.views.dsl.core.* +import splitties.views.dsl.constraintlayout.after +import splitties.views.dsl.constraintlayout.before +import splitties.views.dsl.constraintlayout.centerVertically +import splitties.views.dsl.constraintlayout.constraintLayout +import splitties.views.dsl.constraintlayout.endOfParent +import splitties.views.dsl.constraintlayout.lParams +import splitties.views.dsl.constraintlayout.startOfParent +import splitties.views.dsl.core.Ui +import splitties.views.dsl.core.add +import splitties.views.dsl.core.imageView +import splitties.views.dsl.core.lParams +import splitties.views.dsl.core.textView +import splitties.views.dsl.core.view import splitties.views.gravityCenter import splitties.views.imageDrawable @@ -25,8 +34,7 @@ class PickerTabsUi(override val ctx: Context, val theme: Theme) : Ui { } inner class TabUi : Ui { - override val ctx: Context - get() = this@PickerTabsUi.ctx + override val ctx = this@PickerTabsUi.ctx var position: Int = -1 @@ -36,9 +44,7 @@ class PickerTabsUi(override val ctx: Context, val theme: Theme) : Ui { setTextColor(theme.keyTextColor) } - val icon = imageView { - colorFilter = PorterDuffColorFilter(theme.keyTextColor, PorterDuff.Mode.SRC_IN) - } + val icon = imageView() override val root = view(::CustomGestureView) { add(label, lParams { @@ -64,7 +70,9 @@ class PickerTabsUi(override val ctx: Context, val theme: Theme) : Ui { } fun setIcon(@DrawableRes src: Int) { - icon.imageDrawable = ctx.drawable(src) + icon.imageDrawable = ctx.drawable(src)!!.apply { + setTint(theme.keyTextColor.alpha(0.5f)) + } label.isVisible = false icon.isVisible = true } @@ -72,7 +80,7 @@ class PickerTabsUi(override val ctx: Context, val theme: Theme) : Ui { fun setActive(active: Boolean) { val color = theme.keyTextColor.alpha(if (active) 1f else 0.5f) label.setTextColor(color) - icon.colorFilter = PorterDuffColorFilter(color, PorterDuff.Mode.SRC_IN) + icon.imageDrawable?.setTint(color) } } diff --git a/app/src/main/java/org/fcitx/fcitx5/android/input/popup/PopupMenuUi.kt b/app/src/main/java/org/fcitx/fcitx5/android/input/popup/PopupMenuUi.kt index 6e1cecade..142eec723 100644 --- a/app/src/main/java/org/fcitx/fcitx5/android/input/popup/PopupMenuUi.kt +++ b/app/src/main/java/org/fcitx/fcitx5/android/input/popup/PopupMenuUi.kt @@ -1,8 +1,6 @@ package org.fcitx.fcitx5.android.input.popup import android.content.Context -import android.graphics.PorterDuff -import android.graphics.PorterDuffColorFilter import android.graphics.Rect import android.graphics.drawable.InsetDrawable import android.graphics.drawable.ShapeDrawable @@ -12,11 +10,12 @@ import androidx.core.graphics.ColorUtils import org.fcitx.fcitx5.android.data.theme.Theme import org.fcitx.fcitx5.android.input.keyboard.KeyDef import splitties.dimensions.dp +import splitties.resources.drawable import splitties.views.dsl.core.add import splitties.views.dsl.core.horizontalLayout import splitties.views.dsl.core.imageView import splitties.views.dsl.core.lParams -import splitties.views.imageResource +import splitties.views.imageDrawable import kotlin.math.floor class PopupMenuUi( @@ -60,8 +59,9 @@ class PopupMenuUi( imageView { background = inactiveBackground scaleType = ImageView.ScaleType.CENTER_INSIDE - colorFilter = PorterDuffColorFilter(theme.accentKeyTextColor, PorterDuff.Mode.SRC_IN) - imageResource = it.icon + imageDrawable = drawable(it.icon)!!.apply { + setTint(theme.accentKeyTextColor) + } } } diff --git a/app/src/main/java/org/fcitx/fcitx5/android/input/status/StatusAreaEntryUi.kt b/app/src/main/java/org/fcitx/fcitx5/android/input/status/StatusAreaEntryUi.kt index e0a1c2313..ed94945f1 100644 --- a/app/src/main/java/org/fcitx/fcitx5/android/input/status/StatusAreaEntryUi.kt +++ b/app/src/main/java/org/fcitx/fcitx5/android/input/status/StatusAreaEntryUi.kt @@ -1,8 +1,6 @@ package org.fcitx.fcitx5.android.input.status import android.content.Context -import android.graphics.PorterDuff -import android.graphics.PorterDuffColorFilter import android.graphics.Typeface import android.graphics.drawable.ShapeDrawable import android.graphics.drawable.shapes.OvalShape @@ -95,8 +93,9 @@ class StatusAreaEntryUi(override val ctx: Context, private val theme: Theme) : U if (entry.icon != 0) { icon.visibility = View.VISIBLE textIcon.visibility = View.GONE - icon.imageDrawable = ctx.drawable(entry.icon) - icon.colorFilter = PorterDuffColorFilter(contentColor, PorterDuff.Mode.SRC_IN) + icon.imageDrawable = ctx.drawable(entry.icon)!!.apply { + setTint(contentColor) + } } else { icon.visibility = View.GONE textIcon.visibility = View.VISIBLE diff --git a/app/src/main/java/org/fcitx/fcitx5/android/ui/common/BaseDynamicListUi.kt b/app/src/main/java/org/fcitx/fcitx5/android/ui/common/BaseDynamicListUi.kt index 8e3a11632..85b6dd3f8 100644 --- a/app/src/main/java/org/fcitx/fcitx5/android/ui/common/BaseDynamicListUi.kt +++ b/app/src/main/java/org/fcitx/fcitx5/android/ui/common/BaseDynamicListUi.kt @@ -1,8 +1,6 @@ package org.fcitx.fcitx5.android.ui.common import android.content.Context -import android.graphics.PorterDuff -import android.graphics.PorterDuffColorFilter import android.view.View import android.view.ViewGroup import android.widget.CheckBox @@ -20,6 +18,7 @@ import com.google.android.material.snackbar.BaseTransientBottomBar import com.google.android.material.snackbar.Snackbar import org.fcitx.fcitx5.android.R import splitties.dimensions.dp +import splitties.resources.drawable import splitties.resources.styledColor import splitties.views.backgroundColor import splitties.views.bottomPadding @@ -29,7 +28,7 @@ import splitties.views.dsl.coordinatorlayout.defaultLParams import splitties.views.dsl.core.* import splitties.views.dsl.recyclerview.recyclerView import splitties.views.gravityEndBottom -import splitties.views.imageResource +import splitties.views.imageDrawable import splitties.views.recyclerview.verticalLayoutManager import kotlin.math.min @@ -53,10 +52,9 @@ abstract class BaseDynamicListUi( protected var shouldShowFab = false protected val fab = view(::FloatingActionButton) { - imageResource = R.drawable.ic_baseline_plus_24 - colorFilter = PorterDuffColorFilter( - styledColor(android.R.attr.colorForegroundInverse), PorterDuff.Mode.SRC_IN - ) + imageDrawable = drawable(R.drawable.ic_baseline_plus_24)!!.apply { + setTint(styledColor(android.R.attr.colorForegroundInverse)) + } } sealed class Mode { diff --git a/app/src/main/java/org/fcitx/fcitx5/android/ui/common/DynamicListEntryUi.kt b/app/src/main/java/org/fcitx/fcitx5/android/ui/common/DynamicListEntryUi.kt index 4b8ae5325..9c9b880c2 100644 --- a/app/src/main/java/org/fcitx/fcitx5/android/ui/common/DynamicListEntryUi.kt +++ b/app/src/main/java/org/fcitx/fcitx5/android/ui/common/DynamicListEntryUi.kt @@ -1,29 +1,42 @@ package org.fcitx.fcitx5.android.ui.common import android.content.Context -import android.graphics.PorterDuff -import android.graphics.PorterDuffColorFilter import android.view.View import android.view.ViewGroup import org.fcitx.fcitx5.android.R import splitties.dimensions.dp +import splitties.resources.drawable import splitties.resources.resolveThemeAttribute import splitties.resources.styledColor import splitties.resources.styledDimenPxSize import splitties.resources.styledDrawable import splitties.views.backgroundColor -import splitties.views.dsl.constraintlayout.* -import splitties.views.dsl.core.* -import splitties.views.imageResource +import splitties.views.dsl.constraintlayout.after +import splitties.views.dsl.constraintlayout.before +import splitties.views.dsl.constraintlayout.centerVertically +import splitties.views.dsl.constraintlayout.constraintLayout +import splitties.views.dsl.constraintlayout.endOfParent +import splitties.views.dsl.constraintlayout.lParams +import splitties.views.dsl.constraintlayout.matchConstraints +import splitties.views.dsl.constraintlayout.startOfParent +import splitties.views.dsl.core.Ui +import splitties.views.dsl.core.add +import splitties.views.dsl.core.checkBox +import splitties.views.dsl.core.imageButton +import splitties.views.dsl.core.imageView +import splitties.views.dsl.core.matchParent +import splitties.views.dsl.core.textView +import splitties.views.dsl.core.wrapContent +import splitties.views.imageDrawable import splitties.views.setPaddingDp import splitties.views.textAppearance class DynamicListEntryUi(override val ctx: Context) : Ui { val handleImage = imageView { - imageResource = R.drawable.ic_baseline_drag_handle_24 - colorFilter = - PorterDuffColorFilter(styledColor(android.R.attr.colorAccent), PorterDuff.Mode.SRC_IN) + imageDrawable = drawable(R.drawable.ic_baseline_drag_handle_24)!!.apply { + setTint(styledColor(android.R.attr.colorAccent)) + } setPaddingDp(3, 0, 3, 0) } @@ -38,12 +51,16 @@ class DynamicListEntryUi(override val ctx: Context) : Ui { val editButton = imageButton { background = styledDrawable(android.R.attr.selectableItemBackground) - imageResource = R.drawable.ic_baseline_edit_24 + imageDrawable = drawable(R.drawable.ic_baseline_edit_24)!!.apply { + setTint(styledColor(android.R.attr.colorControlNormal)) + } } val settingsButton = imageButton { background = styledDrawable(android.R.attr.selectableItemBackground) - imageResource = R.drawable.ic_baseline_settings_24 + imageDrawable = drawable(R.drawable.ic_baseline_settings_24)!!.apply { + setTint(styledColor(android.R.attr.colorControlNormal)) + } } override val root: View = constraintLayout { diff --git a/app/src/main/java/org/fcitx/fcitx5/android/ui/common/DynamicListTouchCallback.kt b/app/src/main/java/org/fcitx/fcitx5/android/ui/common/DynamicListTouchCallback.kt index 3a5e623c7..e6f63361f 100644 --- a/app/src/main/java/org/fcitx/fcitx5/android/ui/common/DynamicListTouchCallback.kt +++ b/app/src/main/java/org/fcitx/fcitx5/android/ui/common/DynamicListTouchCallback.kt @@ -1,10 +1,8 @@ package org.fcitx.fcitx5.android.ui.common import android.content.Context +import android.graphics.Bitmap import android.graphics.Canvas -import android.graphics.Paint -import android.graphics.PorterDuff -import android.graphics.PorterDuffColorFilter import android.graphics.Rect import android.graphics.drawable.ColorDrawable import androidx.core.graphics.drawable.toBitmap @@ -28,23 +26,16 @@ open class DynamicListTouchCallback( private var selected = true private var reset = false - private val deleteBackground by lazy { + private val deleteBackground: ColorDrawable by lazy { ColorDrawable().apply { color = ctx.color(R.color.red_400) } } - private val deleteIcon by lazy { - ctx.drawable(R.drawable.ic_baseline_delete_24)!!.toBitmap() - } - - private val deleteIconPaint by lazy { - Paint().apply { - colorFilter = PorterDuffColorFilter( - ctx.styledColor(android.R.attr.colorBackground), - PorterDuff.Mode.SRC_IN - ) - } + private val deleteIcon: Bitmap by lazy { + ctx.drawable(R.drawable.ic_baseline_delete_24)!!.apply { + setTint(ctx.styledColor(android.R.attr.colorBackground)) + }.toBitmap() } // manually call start drag at the on long click listener @@ -94,26 +85,24 @@ open class DynamicListTouchCallback( deleteBackground.apply { bounds = itemView.run { Rect(canvasLeft, top, right, bottom) } }.draw(c) - deleteIcon.also { - val iconMargin = (itemView.height - it.height) / 2 - val revealed = (dX.absoluteValue - iconMargin).toInt() - c.drawBitmap( - it, - /* src = */ Rect( - /* left = */ if (revealed > it.width) 0 else it.width - revealed, - /* top = */ 0, - /* right = */ it.width, - /* bottom = */ it.height - ), - /* dst = */ Rect( - /* left = */ if (revealed > it.width) itemView.right - iconMargin - it.width else canvasLeft, - /* top = */ itemView.top + iconMargin, - /* right = */ itemView.right - iconMargin, - /* bottom = */ itemView.top + iconMargin + it.height - ), - deleteIconPaint - ) - } + val iconMargin = (itemView.height - deleteIcon.height) / 2 + val revealed = (dX.absoluteValue - iconMargin).toInt() + c.drawBitmap( + deleteIcon, + /* src = */ Rect( + /* left = */ if (revealed > deleteIcon.width) 0 else deleteIcon.width - revealed, + /* top = */ 0, + /* right = */ deleteIcon.width, + /* bottom = */ deleteIcon.height + ), + /* dst = */ Rect( + /* left = */ if (revealed > deleteIcon.width) itemView.right - iconMargin - deleteIcon.width else canvasLeft, + /* top = */ itemView.top + iconMargin, + /* right = */ itemView.right - iconMargin, + /* bottom = */ itemView.top + iconMargin + deleteIcon.height + ), + null + ) } ItemTouchHelper.ACTION_STATE_DRAG -> { } diff --git a/app/src/main/java/org/fcitx/fcitx5/android/ui/main/LogActivity.kt b/app/src/main/java/org/fcitx/fcitx5/android/ui/main/LogActivity.kt index 68c00c79e..d371e1acf 100644 --- a/app/src/main/java/org/fcitx/fcitx5/android/ui/main/LogActivity.kt +++ b/app/src/main/java/org/fcitx/fcitx5/android/ui/main/LogActivity.kt @@ -24,6 +24,8 @@ import org.fcitx.fcitx5.android.utils.Logcat import org.fcitx.fcitx5.android.utils.applyTranslucentSystemBars import org.fcitx.fcitx5.android.utils.iso8601UTCDateTime import org.fcitx.fcitx5.android.utils.toast +import splitties.resources.drawable +import splitties.resources.styledColor import splitties.views.topPadding class LogActivity : AppCompatActivity() { @@ -94,7 +96,9 @@ class LogActivity : AppCompatActivity() { override fun onCreateOptionsMenu(menu: Menu): Boolean { if (!fromCrash) { menu.add(R.string.clear).apply { - setIcon(R.drawable.ic_baseline_delete_24) + icon = drawable(R.drawable.ic_baseline_delete_24)!!.apply { + setTint(styledColor(android.R.attr.colorControlNormal)) + } setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM) setOnMenuItemClickListener { logView.clear() @@ -103,7 +107,9 @@ class LogActivity : AppCompatActivity() { } } menu.add(R.string.export).apply { - setIcon(R.drawable.ic_baseline_save_24) + icon = drawable(R.drawable.ic_baseline_save_24)!!.apply { + setTint(styledColor(android.R.attr.colorControlNormal)) + } setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM) setOnMenuItemClickListener { launcher.launch("$packageName-${iso8601UTCDateTime()}.txt") diff --git a/app/src/main/java/org/fcitx/fcitx5/android/ui/main/MainActivity.kt b/app/src/main/java/org/fcitx/fcitx5/android/ui/main/MainActivity.kt index edd19baa6..710af8f29 100644 --- a/app/src/main/java/org/fcitx/fcitx5/android/ui/main/MainActivity.kt +++ b/app/src/main/java/org/fcitx/fcitx5/android/ui/main/MainActivity.kt @@ -29,6 +29,8 @@ import org.fcitx.fcitx5.android.utils.Const import org.fcitx.fcitx5.android.utils.applyTranslucentSystemBars import org.fcitx.fcitx5.android.utils.navigateFromMain import splitties.dimensions.dp +import splitties.resources.drawable +import splitties.resources.styledColor import splitties.views.topPadding class MainActivity : AppCompatActivity() { @@ -111,7 +113,9 @@ class MainActivity : AppCompatActivity() { override fun onCreateOptionsMenu(menu: Menu): Boolean = menu.run { add(R.string.save).apply { - setIcon(R.drawable.ic_baseline_save_24) + icon = drawable(R.drawable.ic_baseline_save_24)!!.apply { + setTint(styledColor(android.R.attr.colorControlNormal)) + } setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM) viewModel.toolbarSaveButtonOnClickListener.apply { observe(this@MainActivity) { listener -> isVisible = listener != null } @@ -152,7 +156,9 @@ class MainActivity : AppCompatActivity() { } add(R.string.edit).apply { - setIcon(R.drawable.ic_baseline_edit_24) + icon = drawable(R.drawable.ic_baseline_edit_24)!!.apply { + setTint(styledColor(android.R.attr.colorControlNormal)) + } setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM) viewModel.toolbarEditButtonVisible.apply { observe(this@MainActivity) { isVisible = it } @@ -165,7 +171,9 @@ class MainActivity : AppCompatActivity() { } add(R.string.delete).apply { - setIcon(R.drawable.ic_baseline_delete_24) + icon = drawable(R.drawable.ic_baseline_delete_24)!!.apply { + setTint(styledColor(android.R.attr.colorControlNormal)) + } setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM) viewModel.toolbarDeleteButtonOnClickListener.apply { observe(this@MainActivity) { listener -> isVisible = listener != null } diff --git a/app/src/main/java/org/fcitx/fcitx5/android/ui/main/settings/KeyPreferenceUi.kt b/app/src/main/java/org/fcitx/fcitx5/android/ui/main/settings/KeyPreferenceUi.kt index e6c8c64e3..b6389304a 100644 --- a/app/src/main/java/org/fcitx/fcitx5/android/ui/main/settings/KeyPreferenceUi.kt +++ b/app/src/main/java/org/fcitx/fcitx5/android/ui/main/settings/KeyPreferenceUi.kt @@ -11,13 +11,30 @@ import org.fcitx.fcitx5.android.core.KeyStates import org.fcitx.fcitx5.android.core.KeySym import org.fcitx.fcitx5.android.input.FcitxInputMethodService import splitties.dimensions.dp +import splitties.resources.drawable import splitties.resources.styledColor import splitties.resources.styledColorSL import splitties.resources.styledDrawable -import splitties.views.dsl.constraintlayout.* -import splitties.views.dsl.core.* +import splitties.views.dsl.constraintlayout.above +import splitties.views.dsl.constraintlayout.after +import splitties.views.dsl.constraintlayout.before +import splitties.views.dsl.constraintlayout.below +import splitties.views.dsl.constraintlayout.bottomOfParent +import splitties.views.dsl.constraintlayout.constraintLayout +import splitties.views.dsl.constraintlayout.endOfParent +import splitties.views.dsl.constraintlayout.lParams +import splitties.views.dsl.constraintlayout.matchConstraints +import splitties.views.dsl.constraintlayout.startOfParent +import splitties.views.dsl.constraintlayout.topOfParent +import splitties.views.dsl.core.Ui +import splitties.views.dsl.core.add +import splitties.views.dsl.core.button +import splitties.views.dsl.core.editText +import splitties.views.dsl.core.imageButton +import splitties.views.dsl.core.textView +import splitties.views.dsl.core.wrapContent import splitties.views.gravityCenter -import splitties.views.imageResource +import splitties.views.imageDrawable class KeyPreferenceUi(override val ctx: Context) : Ui { @@ -26,8 +43,7 @@ class KeyPreferenceUi(override val ctx: Context) : Ui { } private inner class ModifierButton(label: String, val modifier: KeyState) : Ui { - override val ctx: Context - get() = this@KeyPreferenceUi.ctx + override val ctx = this@KeyPreferenceUi.ctx override val root = button { text = label @@ -96,7 +112,9 @@ class KeyPreferenceUi(override val ctx: Context) : Ui { private val clearButton = imageButton { background = styledDrawable(android.R.attr.actionBarItemBackground) - imageResource = R.drawable.ic_baseline_delete_24 + imageDrawable = drawable(R.drawable.ic_baseline_delete_24)!!.apply { + setTint(styledColor(android.R.attr.colorControlNormal)) + } setOnClickListener { setKey(Key.None) } diff --git a/app/src/main/java/org/fcitx/fcitx5/android/ui/main/settings/QuickPhraseListFragment.kt b/app/src/main/java/org/fcitx/fcitx5/android/ui/main/settings/QuickPhraseListFragment.kt index 05e57c0c2..4c92230ce 100644 --- a/app/src/main/java/org/fcitx/fcitx5/android/ui/main/settings/QuickPhraseListFragment.kt +++ b/app/src/main/java/org/fcitx/fcitx5/android/ui/main/settings/QuickPhraseListFragment.kt @@ -37,11 +37,13 @@ import org.fcitx.fcitx5.android.utils.materialTextInput import org.fcitx.fcitx5.android.utils.notificationManager import org.fcitx.fcitx5.android.utils.queryFileName import org.fcitx.fcitx5.android.utils.str +import splitties.resources.drawable +import splitties.resources.styledColor import splitties.views.dsl.core.add import splitties.views.dsl.core.lParams import splitties.views.dsl.core.matchParent import splitties.views.dsl.core.verticalLayout -import splitties.views.imageResource +import splitties.views.imageDrawable import splitties.views.setPaddingDp import java.util.concurrent.atomic.AtomicBoolean @@ -87,10 +89,11 @@ class QuickPhraseListFragment : Fragment(), OnItemChangedListener { dustman.forceDirty() } } + var icon = R.drawable.ic_baseline_settings_24 when (entry) { is BuiltinQuickPhrase -> { if (entry.override != null) { - imageResource = R.drawable.ic_baseline_expand_more_24 + icon = R.drawable.ic_baseline_expand_more_24 setOnClickListener { PopupMenu(requireContext(), this).apply { menu.add(getString(R.string.edit)).setOnMenuItemClickListener { @@ -108,7 +111,7 @@ class QuickPhraseListFragment : Fragment(), OnItemChangedListener { } } } else { - imageResource = R.drawable.ic_baseline_edit_24 + icon = R.drawable.ic_baseline_edit_24 setOnClickListener { edit() } @@ -116,13 +119,15 @@ class QuickPhraseListFragment : Fragment(), OnItemChangedListener { } is CustomQuickPhrase -> { - imageResource = R.drawable.ic_baseline_edit_24 + icon = R.drawable.ic_baseline_edit_24 setOnClickListener { edit() } } } - + imageDrawable = drawable(icon)!!.apply { + setTint(styledColor(android.R.attr.colorControlNormal)) + } } ) { init { diff --git a/app/src/main/java/org/fcitx/fcitx5/android/ui/main/settings/theme/CustomThemeActivity.kt b/app/src/main/java/org/fcitx/fcitx5/android/ui/main/settings/theme/CustomThemeActivity.kt index dd7179aa6..eb975a739 100644 --- a/app/src/main/java/org/fcitx/fcitx5/android/ui/main/settings/theme/CustomThemeActivity.kt +++ b/app/src/main/java/org/fcitx/fcitx5/android/ui/main/settings/theme/CustomThemeActivity.kt @@ -8,8 +8,6 @@ import android.content.Intent import android.graphics.Bitmap import android.graphics.BitmapFactory import android.graphics.Color -import android.graphics.PorterDuff -import android.graphics.PorterDuffColorFilter import android.graphics.Rect import android.graphics.drawable.BitmapDrawable import android.os.Bundle @@ -475,8 +473,7 @@ class CustomThemeActivity : AppCompatActivity() { if (!newCreated) { menu.add(R.string.delete).apply { icon = drawable(R.drawable.ic_baseline_delete_24)!!.apply { - colorFilter = - PorterDuffColorFilter(color(R.color.red_400), PorterDuff.Mode.SRC_IN) + setTint(color(R.color.red_400)) } setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM) setOnMenuItemClickListener { @@ -486,7 +483,9 @@ class CustomThemeActivity : AppCompatActivity() { } } menu.add(R.string.save).apply { - setIcon(R.drawable.ic_baseline_done_24) + icon = drawable(R.drawable.ic_baseline_done_24)!!.apply { + setTint(styledColor(android.R.attr.colorControlNormal)) + } setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM) setOnMenuItemClickListener { done() diff --git a/app/src/main/java/org/fcitx/fcitx5/android/ui/main/settings/theme/NewThemeEntryUi.kt b/app/src/main/java/org/fcitx/fcitx5/android/ui/main/settings/theme/NewThemeEntryUi.kt index 430444579..f31c03c9a 100644 --- a/app/src/main/java/org/fcitx/fcitx5/android/ui/main/settings/theme/NewThemeEntryUi.kt +++ b/app/src/main/java/org/fcitx/fcitx5/android/ui/main/settings/theme/NewThemeEntryUi.kt @@ -2,16 +2,24 @@ package org.fcitx.fcitx5.android.ui.main.settings.theme import android.content.Context import android.graphics.Color -import android.graphics.PorterDuff -import android.graphics.PorterDuffColorFilter import android.view.ViewOutlineProvider import androidx.constraintlayout.widget.ConstraintLayout import org.fcitx.fcitx5.android.R import splitties.dimensions.dp import splitties.resources.drawable import splitties.resources.styledDrawable -import splitties.views.dsl.constraintlayout.* -import splitties.views.dsl.core.* +import splitties.views.dsl.constraintlayout.above +import splitties.views.dsl.constraintlayout.below +import splitties.views.dsl.constraintlayout.bottomOfParent +import splitties.views.dsl.constraintlayout.centerHorizontally +import splitties.views.dsl.constraintlayout.constraintLayout +import splitties.views.dsl.constraintlayout.lParams +import splitties.views.dsl.constraintlayout.topOfParent +import splitties.views.dsl.core.Ui +import splitties.views.dsl.core.add +import splitties.views.dsl.core.imageView +import splitties.views.dsl.core.textView +import splitties.views.dsl.core.wrapContent import splitties.views.imageDrawable class NewThemeEntryUi(override val ctx: Context) : Ui { @@ -21,8 +29,9 @@ class NewThemeEntryUi(override val ctx: Context) : Ui { } val icon = imageView { - imageDrawable = ctx.drawable(R.drawable.ic_baseline_plus_24) - colorFilter = PorterDuffColorFilter(Color.WHITE, PorterDuff.Mode.SRC_IN) + imageDrawable = ctx.drawable(R.drawable.ic_baseline_plus_24)!!.apply { + setTint(Color.WHITE) + } } override val root = constraintLayout { diff --git a/app/src/main/java/org/fcitx/fcitx5/android/ui/main/settings/theme/ThemeListFragment.kt b/app/src/main/java/org/fcitx/fcitx5/android/ui/main/settings/theme/ThemeListFragment.kt index b5c8d50c8..d910cfd6b 100644 --- a/app/src/main/java/org/fcitx/fcitx5/android/ui/main/settings/theme/ThemeListFragment.kt +++ b/app/src/main/java/org/fcitx/fcitx5/android/ui/main/settings/theme/ThemeListFragment.kt @@ -179,7 +179,9 @@ class ThemeListFragment : Fragment() { gravity = gravityVerticalCenter } val settingsButton = imageButton { - imageDrawable = drawable(R.drawable.ic_baseline_settings_24) + imageDrawable = drawable(R.drawable.ic_baseline_settings_24)?.apply { + setTint(styledColor(android.R.attr.colorControlNormal)) + } background = styledDrawable(android.R.attr.actionBarItemBackground) setOnClickListener { findNavController().navigate(R.id.action_themeListFragment_to_themeSettingsFragment) diff --git a/app/src/main/java/org/fcitx/fcitx5/android/ui/main/settings/theme/ThemeThumbnailUi.kt b/app/src/main/java/org/fcitx/fcitx5/android/ui/main/settings/theme/ThemeThumbnailUi.kt index 7ba3d6672..b63e614a2 100644 --- a/app/src/main/java/org/fcitx/fcitx5/android/ui/main/settings/theme/ThemeThumbnailUi.kt +++ b/app/src/main/java/org/fcitx/fcitx5/android/ui/main/settings/theme/ThemeThumbnailUi.kt @@ -1,8 +1,7 @@ package org.fcitx.fcitx5.android.ui.main.settings.theme import android.content.Context -import android.graphics.PorterDuff -import android.graphics.PorterDuffColorFilter +import android.content.res.ColorStateList import android.graphics.drawable.GradientDrawable import android.graphics.drawable.ShapeDrawable import android.graphics.drawable.shapes.OvalShape @@ -14,7 +13,6 @@ import org.fcitx.fcitx5.android.R import org.fcitx.fcitx5.android.data.theme.Theme import org.fcitx.fcitx5.android.utils.rippleDrawable import splitties.dimensions.dp -import splitties.resources.drawable import splitties.views.backgroundColor import splitties.views.dsl.constraintlayout.bottomOfParent import splitties.views.dsl.constraintlayout.centerHorizontally @@ -30,6 +28,7 @@ import splitties.views.dsl.core.imageView import splitties.views.dsl.core.matchParent import splitties.views.dsl.core.view import splitties.views.imageDrawable +import splitties.views.imageResource import splitties.views.setPaddingDp class ThemeThumbnailUi(override val ctx: Context) : Ui { @@ -48,13 +47,11 @@ class ThemeThumbnailUi(override val ctx: Context) : Ui { val checkMark = imageView { scaleType = ImageView.ScaleType.FIT_CENTER - imageDrawable = ctx.drawable(R.drawable.ic_baseline_check_24) } val editButton = imageView { setPaddingDp(16, 4, 4, 16) scaleType = ImageView.ScaleType.FIT_CENTER - imageDrawable = ctx.drawable(R.drawable.ic_baseline_edit_24) } override val root = constraintLayout { @@ -94,29 +91,27 @@ class ThemeThumbnailUi(override val ctx: Context) : Ui { returnKey.background = ShapeDrawable(OvalShape()).apply { paint.color = theme.accentKeyBackgroundColor } - val foreground = PorterDuffColorFilter(theme.altKeyTextColor, PorterDuff.Mode.SRC_IN) + val foregroundTint = ColorStateList.valueOf(theme.altKeyTextColor) editButton.apply { visibility = if (theme is Theme.Custom) View.VISIBLE else View.GONE - colorFilter = foreground background = rippleDrawable(theme.keyPressHighlightColor) + imageTintList = foregroundTint } - checkMark.colorFilter = foreground + checkMark.imageTintList = foregroundTint } fun setChecked(checked: Boolean) { checkMark.isVisible = checked - checkMark.imageDrawable = ctx.drawable(R.drawable.ic_baseline_check_24) + checkMark.imageResource = R.drawable.ic_baseline_check_24 } fun setChecked(state: State) { checkMark.isVisible = state != State.Normal - checkMark.imageDrawable = ctx.drawable( - when (state) { - State.Normal -> R.drawable.ic_baseline_check_24 - State.Selected -> R.drawable.ic_baseline_check_24 - State.LightMode -> R.drawable.ic_baseline_light_mode_24 - State.DarkMode -> R.drawable.ic_baseline_dark_mode_24 - } - ) + checkMark.imageResource = when (state) { + State.Normal -> 0 + State.Selected -> R.drawable.ic_baseline_check_24 + State.LightMode -> R.drawable.ic_baseline_light_mode_24 + State.DarkMode -> R.drawable.ic_baseline_dark_mode_24 + } } } \ No newline at end of file diff --git a/app/src/main/java/org/fcitx/fcitx5/android/utils/PreferenceScreen.kt b/app/src/main/java/org/fcitx/fcitx5/android/utils/PreferenceScreen.kt index cde25801c..78e5e2f6c 100644 --- a/app/src/main/java/org/fcitx/fcitx5/android/utils/PreferenceScreen.kt +++ b/app/src/main/java/org/fcitx/fcitx5/android/utils/PreferenceScreen.kt @@ -6,6 +6,8 @@ import androidx.preference.Preference import androidx.preference.PreferenceCategory import androidx.preference.PreferenceGroup import androidx.preference.PreferenceScreen +import splitties.resources.drawable +import splitties.resources.styledColor fun PreferenceScreen.addCategory(title: String, block: PreferenceCategory.() -> Unit) { val category = PreferenceCategory(context).apply { @@ -33,7 +35,9 @@ fun PreferenceGroup.addPreference( if (icon == null) { isIconSpaceReserved = false } else { - setIcon(icon) + setIcon(context.drawable(icon)?.apply { + setTint(context.styledColor(android.R.attr.colorControlNormal)) + }) } onClick?.also { setOnPreferenceClickListener { _ -> diff --git a/app/src/main/res/drawable-v24/ic_launcher_foreground_debug.xml b/app/src/main/res/drawable-v24/ic_launcher_foreground_debug.xml index 81d694b98..94cd0ce4f 100644 --- a/app/src/main/res/drawable-v24/ic_launcher_foreground_debug.xml +++ b/app/src/main/res/drawable-v24/ic_launcher_foreground_debug.xml @@ -51,27 +51,27 @@ android:strokeWidth="3.77953" android:strokeLineCap="round" /> + android:strokeWidth="1.00157" /> + android:strokeWidth="1" /> + android:strokeWidth="1" /> + android:strokeWidth="1" /> + android:strokeWidth="1" /> + android:strokeWidth="1" /> diff --git a/app/src/main/res/drawable/ic_baseline_access_time_24.xml b/app/src/main/res/drawable/ic_baseline_access_time_24.xml index d168353d1..da839ddc8 100644 --- a/app/src/main/res/drawable/ic_baseline_access_time_24.xml +++ b/app/src/main/res/drawable/ic_baseline_access_time_24.xml @@ -2,12 +2,11 @@ android:width="24dp" android:height="24dp" android:viewportWidth="24" - android:viewportHeight="24" - android:tint="?attr/colorControlNormal"> + android:viewportHeight="24"> diff --git a/app/src/main/res/drawable/ic_baseline_android_24.xml b/app/src/main/res/drawable/ic_baseline_android_24.xml index e5cce70f1..7abb6157a 100644 --- a/app/src/main/res/drawable/ic_baseline_android_24.xml +++ b/app/src/main/res/drawable/ic_baseline_android_24.xml @@ -1,11 +1,9 @@ diff --git a/app/src/main/res/drawable/ic_baseline_arrow_back_24.xml b/app/src/main/res/drawable/ic_baseline_arrow_back_24.xml index 2a31b2ef3..327df952d 100644 --- a/app/src/main/res/drawable/ic_baseline_arrow_back_24.xml +++ b/app/src/main/res/drawable/ic_baseline_arrow_back_24.xml @@ -1,11 +1,10 @@ - + android:viewportHeight="24"> + diff --git a/app/src/main/res/drawable/ic_baseline_arrow_downward_24.xml b/app/src/main/res/drawable/ic_baseline_arrow_downward_24.xml index 8f9be44a7..447527468 100644 --- a/app/src/main/res/drawable/ic_baseline_arrow_downward_24.xml +++ b/app/src/main/res/drawable/ic_baseline_arrow_downward_24.xml @@ -2,9 +2,8 @@ android:width="24dp" android:height="24dp" android:viewportWidth="24" - android:viewportHeight="24" - android:tint="?attr/colorControlNormal"> - + android:viewportHeight="24"> + diff --git a/app/src/main/res/drawable/ic_baseline_arrow_drop_down_24.xml b/app/src/main/res/drawable/ic_baseline_arrow_drop_down_24.xml index ce583469c..5e72babb0 100644 --- a/app/src/main/res/drawable/ic_baseline_arrow_drop_down_24.xml +++ b/app/src/main/res/drawable/ic_baseline_arrow_drop_down_24.xml @@ -2,9 +2,8 @@ android:width="24dp" android:height="24dp" android:viewportWidth="24" - android:viewportHeight="24" - android:tint="?attr/colorControlNormal"> - + android:viewportHeight="24"> + diff --git a/app/src/main/res/drawable/ic_baseline_arrow_forward_24.xml b/app/src/main/res/drawable/ic_baseline_arrow_forward_24.xml index d5e3ac8b2..0ca99223d 100644 --- a/app/src/main/res/drawable/ic_baseline_arrow_forward_24.xml +++ b/app/src/main/res/drawable/ic_baseline_arrow_forward_24.xml @@ -1,11 +1,10 @@ - + android:viewportHeight="24"> + diff --git a/app/src/main/res/drawable/ic_baseline_arrow_upward_24.xml b/app/src/main/res/drawable/ic_baseline_arrow_upward_24.xml index 07b7a8366..c03f6f515 100644 --- a/app/src/main/res/drawable/ic_baseline_arrow_upward_24.xml +++ b/app/src/main/res/drawable/ic_baseline_arrow_upward_24.xml @@ -2,9 +2,8 @@ android:width="24dp" android:height="24dp" android:viewportWidth="24" - android:viewportHeight="24" - android:tint="?attr/colorControlNormal"> - + android:viewportHeight="24"> + diff --git a/app/src/main/res/drawable/ic_baseline_backspace_24.xml b/app/src/main/res/drawable/ic_baseline_backspace_24.xml index 1cfa0d73c..1bb49cc0c 100644 --- a/app/src/main/res/drawable/ic_baseline_backspace_24.xml +++ b/app/src/main/res/drawable/ic_baseline_backspace_24.xml @@ -1,10 +1,10 @@ - + android:viewportHeight="24"> + diff --git a/app/src/main/res/drawable/ic_baseline_cake_24.xml b/app/src/main/res/drawable/ic_baseline_cake_24.xml index 7e38e1e32..73823e43a 100644 --- a/app/src/main/res/drawable/ic_baseline_cake_24.xml +++ b/app/src/main/res/drawable/ic_baseline_cake_24.xml @@ -2,9 +2,8 @@ android:width="24dp" android:height="24dp" android:viewportWidth="24" - android:viewportHeight="24" - android:tint="?attr/colorControlNormal"> + android:viewportHeight="24"> diff --git a/app/src/main/res/drawable/ic_baseline_check_24.xml b/app/src/main/res/drawable/ic_baseline_check_24.xml index 0432fa69b..ee9b144b9 100644 --- a/app/src/main/res/drawable/ic_baseline_check_24.xml +++ b/app/src/main/res/drawable/ic_baseline_check_24.xml @@ -2,9 +2,8 @@ android:width="24dp" android:height="24dp" android:viewportWidth="24" - android:viewportHeight="24" - android:tint="?attr/colorControlNormal"> - + android:viewportHeight="24"> + diff --git a/app/src/main/res/drawable/ic_baseline_check_circle_24.xml b/app/src/main/res/drawable/ic_baseline_check_circle_24.xml index 5e111ca7d..82c21d850 100644 --- a/app/src/main/res/drawable/ic_baseline_check_circle_24.xml +++ b/app/src/main/res/drawable/ic_baseline_check_circle_24.xml @@ -2,9 +2,8 @@ android:width="24dp" android:height="24dp" android:viewportWidth="24" - android:viewportHeight="24" - android:tint="?attr/colorControlNormal"> - + android:viewportHeight="24"> + diff --git a/app/src/main/res/drawable/ic_baseline_code_24.xml b/app/src/main/res/drawable/ic_baseline_code_24.xml index b97ee53fa..123e24c3f 100644 --- a/app/src/main/res/drawable/ic_baseline_code_24.xml +++ b/app/src/main/res/drawable/ic_baseline_code_24.xml @@ -2,9 +2,8 @@ android:width="24dp" android:height="24dp" android:viewportWidth="24" - android:viewportHeight="24" - android:tint="?attr/colorControlNormal"> - + android:viewportHeight="24"> + diff --git a/app/src/main/res/drawable/ic_baseline_code_off_24.xml b/app/src/main/res/drawable/ic_baseline_code_off_24.xml index d2cf6333a..0c5f79308 100644 --- a/app/src/main/res/drawable/ic_baseline_code_off_24.xml +++ b/app/src/main/res/drawable/ic_baseline_code_off_24.xml @@ -2,9 +2,8 @@ android:width="24dp" android:height="24dp" android:viewportWidth="24" - android:viewportHeight="24" - android:tint="?attr/colorControlNormal"> - + android:viewportHeight="24"> + diff --git a/app/src/main/res/drawable/ic_baseline_content_paste_24.xml b/app/src/main/res/drawable/ic_baseline_content_paste_24.xml index 2d8f09a1d..fc37de99a 100644 --- a/app/src/main/res/drawable/ic_baseline_content_paste_24.xml +++ b/app/src/main/res/drawable/ic_baseline_content_paste_24.xml @@ -2,9 +2,8 @@ android:width="24dp" android:height="24dp" android:viewportWidth="24" - android:viewportHeight="24" - android:tint="?attr/colorControlNormal"> - + android:viewportHeight="24"> + diff --git a/app/src/main/res/drawable/ic_baseline_dark_mode_24.xml b/app/src/main/res/drawable/ic_baseline_dark_mode_24.xml index 8767d0088..10cccb646 100644 --- a/app/src/main/res/drawable/ic_baseline_dark_mode_24.xml +++ b/app/src/main/res/drawable/ic_baseline_dark_mode_24.xml @@ -2,9 +2,8 @@ android:width="24dp" android:height="24dp" android:viewportWidth="24" - android:viewportHeight="24" - android:tint="?attr/colorControlNormal"> + android:viewportHeight="24"> diff --git a/app/src/main/res/drawable/ic_baseline_delete_24.xml b/app/src/main/res/drawable/ic_baseline_delete_24.xml index 3c4030b03..7fe15c703 100644 --- a/app/src/main/res/drawable/ic_baseline_delete_24.xml +++ b/app/src/main/res/drawable/ic_baseline_delete_24.xml @@ -2,9 +2,8 @@ android:width="24dp" android:height="24dp" android:viewportWidth="24" - android:viewportHeight="24" - android:tint="?attr/colorControlNormal"> - + android:viewportHeight="24"> + diff --git a/app/src/main/res/drawable/ic_baseline_delete_sweep_24.xml b/app/src/main/res/drawable/ic_baseline_delete_sweep_24.xml index 22560a4f9..d6bb636f3 100644 --- a/app/src/main/res/drawable/ic_baseline_delete_sweep_24.xml +++ b/app/src/main/res/drawable/ic_baseline_delete_sweep_24.xml @@ -2,9 +2,8 @@ android:width="24dp" android:height="24dp" android:viewportWidth="24" - android:viewportHeight="24" - android:tint="?attr/colorControlNormal"> - + android:viewportHeight="24"> + diff --git a/app/src/main/res/drawable/ic_baseline_developer_mode_24.xml b/app/src/main/res/drawable/ic_baseline_developer_mode_24.xml index ded6b0358..dbc260a20 100644 --- a/app/src/main/res/drawable/ic_baseline_developer_mode_24.xml +++ b/app/src/main/res/drawable/ic_baseline_developer_mode_24.xml @@ -2,9 +2,8 @@ android:width="24dp" android:height="24dp" android:viewportWidth="24" - android:viewportHeight="24" - android:tint="?attr/colorControlNormal"> - + android:viewportHeight="24"> + diff --git a/app/src/main/res/drawable/ic_baseline_directions_car_24.xml b/app/src/main/res/drawable/ic_baseline_directions_car_24.xml index dc2afc1f6..ea76ed0b6 100644 --- a/app/src/main/res/drawable/ic_baseline_directions_car_24.xml +++ b/app/src/main/res/drawable/ic_baseline_directions_car_24.xml @@ -2,9 +2,8 @@ android:width="24dp" android:height="24dp" android:viewportWidth="24" - android:viewportHeight="24" - android:tint="?attr/colorControlNormal"> + android:viewportHeight="24"> diff --git a/app/src/main/res/drawable/ic_baseline_done_24.xml b/app/src/main/res/drawable/ic_baseline_done_24.xml index 899cbb684..6245292c3 100644 --- a/app/src/main/res/drawable/ic_baseline_done_24.xml +++ b/app/src/main/res/drawable/ic_baseline_done_24.xml @@ -2,9 +2,8 @@ android:width="24dp" android:height="24dp" android:viewportWidth="24" - android:viewportHeight="24" - android:tint="?attr/colorControlNormal"> - + android:viewportHeight="24"> + diff --git a/app/src/main/res/drawable/ic_baseline_drag_handle_24.xml b/app/src/main/res/drawable/ic_baseline_drag_handle_24.xml index 88fe5b407..672cd70ee 100644 --- a/app/src/main/res/drawable/ic_baseline_drag_handle_24.xml +++ b/app/src/main/res/drawable/ic_baseline_drag_handle_24.xml @@ -2,9 +2,8 @@ android:width="24dp" android:height="24dp" android:viewportWidth="24" - android:viewportHeight="24" - android:tint="?attr/colorControlNormal"> - + android:viewportHeight="24"> + diff --git a/app/src/main/res/drawable/ic_baseline_edit_24.xml b/app/src/main/res/drawable/ic_baseline_edit_24.xml index 2844bafeb..d93cd57f7 100644 --- a/app/src/main/res/drawable/ic_baseline_edit_24.xml +++ b/app/src/main/res/drawable/ic_baseline_edit_24.xml @@ -2,9 +2,8 @@ android:width="24dp" android:height="24dp" android:viewportWidth="24" - android:viewportHeight="24" - android:tint="?attr/colorControlNormal"> - + android:viewportHeight="24"> + diff --git a/app/src/main/res/drawable/ic_baseline_emoji_objects_24.xml b/app/src/main/res/drawable/ic_baseline_emoji_objects_24.xml index c0f24afb3..96efb706a 100644 --- a/app/src/main/res/drawable/ic_baseline_emoji_objects_24.xml +++ b/app/src/main/res/drawable/ic_baseline_emoji_objects_24.xml @@ -2,9 +2,8 @@ android:width="24dp" android:height="24dp" android:viewportWidth="24" - android:viewportHeight="24" - android:tint="?attr/colorControlNormal"> + android:viewportHeight="24"> diff --git a/app/src/main/res/drawable/ic_baseline_emoji_people_24.xml b/app/src/main/res/drawable/ic_baseline_emoji_people_24.xml index c6f8a1b6a..cb83947e4 100644 --- a/app/src/main/res/drawable/ic_baseline_emoji_people_24.xml +++ b/app/src/main/res/drawable/ic_baseline_emoji_people_24.xml @@ -2,12 +2,11 @@ android:width="24dp" android:height="24dp" android:viewportWidth="24" - android:viewportHeight="24" - android:tint="?attr/colorControlNormal"> + android:viewportHeight="24"> diff --git a/app/src/main/res/drawable/ic_baseline_emoji_symbols_24.xml b/app/src/main/res/drawable/ic_baseline_emoji_symbols_24.xml index ac32e8164..ec67a52ca 100644 --- a/app/src/main/res/drawable/ic_baseline_emoji_symbols_24.xml +++ b/app/src/main/res/drawable/ic_baseline_emoji_symbols_24.xml @@ -2,27 +2,26 @@ android:width="24dp" android:height="24dp" android:viewportWidth="24" - android:viewportHeight="24" - android:tint="?attr/colorControlNormal"> + android:viewportHeight="24"> diff --git a/app/src/main/res/drawable/ic_baseline_expand_less_24.xml b/app/src/main/res/drawable/ic_baseline_expand_less_24.xml index 15501459e..10de30eb5 100644 --- a/app/src/main/res/drawable/ic_baseline_expand_less_24.xml +++ b/app/src/main/res/drawable/ic_baseline_expand_less_24.xml @@ -2,9 +2,8 @@ android:width="24dp" android:height="24dp" android:viewportWidth="24" - android:viewportHeight="24" - android:tint="?attr/colorControlNormal"> - + android:viewportHeight="24"> + diff --git a/app/src/main/res/drawable/ic_baseline_expand_more_24.xml b/app/src/main/res/drawable/ic_baseline_expand_more_24.xml index adc215c43..a39aaf016 100644 --- a/app/src/main/res/drawable/ic_baseline_expand_more_24.xml +++ b/app/src/main/res/drawable/ic_baseline_expand_more_24.xml @@ -2,9 +2,8 @@ android:width="24dp" android:height="24dp" android:viewportWidth="24" - android:viewportHeight="24" - android:tint="?attr/colorControlNormal"> - + android:viewportHeight="24"> + diff --git a/app/src/main/res/drawable/ic_baseline_extension_24.xml b/app/src/main/res/drawable/ic_baseline_extension_24.xml index 94055ba42..a186d13f7 100644 --- a/app/src/main/res/drawable/ic_baseline_extension_24.xml +++ b/app/src/main/res/drawable/ic_baseline_extension_24.xml @@ -2,9 +2,8 @@ android:width="24dp" android:height="24dp" android:viewportWidth="24" - android:viewportHeight="24" - android:tint="?attr/colorControlNormal"> - + android:viewportHeight="24"> + diff --git a/app/src/main/res/drawable/ic_baseline_first_page_24.xml b/app/src/main/res/drawable/ic_baseline_first_page_24.xml index 31f30ffe4..a07004188 100644 --- a/app/src/main/res/drawable/ic_baseline_first_page_24.xml +++ b/app/src/main/res/drawable/ic_baseline_first_page_24.xml @@ -2,9 +2,8 @@ android:width="24dp" android:height="24dp" android:viewportWidth="24" - android:viewportHeight="24" - android:tint="?attr/colorControlNormal"> - + android:viewportHeight="24"> + diff --git a/app/src/main/res/drawable/ic_baseline_flag_24.xml b/app/src/main/res/drawable/ic_baseline_flag_24.xml index 150ee8916..c06bf4077 100644 --- a/app/src/main/res/drawable/ic_baseline_flag_24.xml +++ b/app/src/main/res/drawable/ic_baseline_flag_24.xml @@ -2,9 +2,8 @@ android:width="24dp" android:height="24dp" android:viewportWidth="24" - android:viewportHeight="24" - android:tint="?attr/colorControlNormal"> + android:viewportHeight="24"> diff --git a/app/src/main/res/drawable/ic_baseline_flower_24.xml b/app/src/main/res/drawable/ic_baseline_flower_24.xml index 1f843b95a..d6c9eb19a 100644 --- a/app/src/main/res/drawable/ic_baseline_flower_24.xml +++ b/app/src/main/res/drawable/ic_baseline_flower_24.xml @@ -2,9 +2,8 @@ android:width="24dp" android:height="24dp" android:viewportWidth="24" - android:viewportHeight="24" - android:tint="?attr/colorControlNormal"> + android:viewportHeight="24"> diff --git a/app/src/main/res/drawable/ic_baseline_format_quote_24.xml b/app/src/main/res/drawable/ic_baseline_format_quote_24.xml index 705269282..eab1bfe51 100644 --- a/app/src/main/res/drawable/ic_baseline_format_quote_24.xml +++ b/app/src/main/res/drawable/ic_baseline_format_quote_24.xml @@ -2,9 +2,8 @@ android:width="24dp" android:height="24dp" android:viewportWidth="24" - android:viewportHeight="24" - android:tint="?attr/colorControlNormal"> - + android:viewportHeight="24"> + diff --git a/app/src/main/res/drawable/ic_baseline_info_24.xml b/app/src/main/res/drawable/ic_baseline_info_24.xml index 82f78a2d6..a6888b2d3 100644 --- a/app/src/main/res/drawable/ic_baseline_info_24.xml +++ b/app/src/main/res/drawable/ic_baseline_info_24.xml @@ -2,9 +2,8 @@ android:width="24dp" android:height="24dp" android:viewportWidth="24" - android:viewportHeight="24" - android:tint="?attr/colorControlNormal"> + android:viewportHeight="24"> diff --git a/app/src/main/res/drawable/ic_baseline_keyboard_24.xml b/app/src/main/res/drawable/ic_baseline_keyboard_24.xml index 533fc1562..7fd3a9cd6 100644 --- a/app/src/main/res/drawable/ic_baseline_keyboard_24.xml +++ b/app/src/main/res/drawable/ic_baseline_keyboard_24.xml @@ -2,9 +2,8 @@ android:width="24dp" android:height="24dp" android:viewportWidth="24" - android:viewportHeight="24" - android:tint="?attr/colorControlNormal"> - + android:viewportHeight="24"> + diff --git a/app/src/main/res/drawable/ic_baseline_keyboard_arrow_down_24.xml b/app/src/main/res/drawable/ic_baseline_keyboard_arrow_down_24.xml index 884bee144..f31ef83e4 100644 --- a/app/src/main/res/drawable/ic_baseline_keyboard_arrow_down_24.xml +++ b/app/src/main/res/drawable/ic_baseline_keyboard_arrow_down_24.xml @@ -2,9 +2,8 @@ android:width="24dp" android:height="24dp" android:viewportWidth="24" - android:viewportHeight="24" - android:tint="?attr/colorControlNormal"> - + android:viewportHeight="24"> + diff --git a/app/src/main/res/drawable/ic_baseline_keyboard_arrow_left_24.xml b/app/src/main/res/drawable/ic_baseline_keyboard_arrow_left_24.xml index 8bf031f64..173606f2c 100644 --- a/app/src/main/res/drawable/ic_baseline_keyboard_arrow_left_24.xml +++ b/app/src/main/res/drawable/ic_baseline_keyboard_arrow_left_24.xml @@ -2,9 +2,8 @@ android:width="24dp" android:height="24dp" android:viewportWidth="24" - android:viewportHeight="24" - android:tint="?attr/colorControlNormal"> - + android:viewportHeight="24"> + diff --git a/app/src/main/res/drawable/ic_baseline_keyboard_arrow_right_24.xml b/app/src/main/res/drawable/ic_baseline_keyboard_arrow_right_24.xml index d3d62595e..0727c0a1e 100644 --- a/app/src/main/res/drawable/ic_baseline_keyboard_arrow_right_24.xml +++ b/app/src/main/res/drawable/ic_baseline_keyboard_arrow_right_24.xml @@ -2,9 +2,8 @@ android:width="24dp" android:height="24dp" android:viewportWidth="24" - android:viewportHeight="24" - android:tint="?attr/colorControlNormal"> - + android:viewportHeight="24"> + diff --git a/app/src/main/res/drawable/ic_baseline_keyboard_arrow_up_24.xml b/app/src/main/res/drawable/ic_baseline_keyboard_arrow_up_24.xml index 9b15755e5..5c480efce 100644 --- a/app/src/main/res/drawable/ic_baseline_keyboard_arrow_up_24.xml +++ b/app/src/main/res/drawable/ic_baseline_keyboard_arrow_up_24.xml @@ -2,9 +2,8 @@ android:width="24dp" android:height="24dp" android:viewportWidth="24" - android:viewportHeight="24" - android:tint="?attr/colorControlNormal"> - + android:viewportHeight="24"> + diff --git a/app/src/main/res/drawable/ic_baseline_keyboard_return_24.xml b/app/src/main/res/drawable/ic_baseline_keyboard_return_24.xml index 509cf0064..194dfbf2c 100644 --- a/app/src/main/res/drawable/ic_baseline_keyboard_return_24.xml +++ b/app/src/main/res/drawable/ic_baseline_keyboard_return_24.xml @@ -2,9 +2,8 @@ android:width="24dp" android:height="24dp" android:viewportWidth="24" - android:viewportHeight="24" - android:tint="?android:attr/colorControlNormal"> - + android:viewportHeight="24"> + diff --git a/app/src/main/res/drawable/ic_baseline_keyboard_tab_24.xml b/app/src/main/res/drawable/ic_baseline_keyboard_tab_24.xml index 1441707b7..f1ffc9806 100644 --- a/app/src/main/res/drawable/ic_baseline_keyboard_tab_24.xml +++ b/app/src/main/res/drawable/ic_baseline_keyboard_tab_24.xml @@ -2,9 +2,8 @@ android:width="24dp" android:height="24dp" android:viewportWidth="24" - android:viewportHeight="24" - android:tint="?attr/colorControlNormal"> - + android:viewportHeight="24"> + diff --git a/app/src/main/res/drawable/ic_baseline_keyboard_tab_reverse_24.xml b/app/src/main/res/drawable/ic_baseline_keyboard_tab_reverse_24.xml index 7af886cef..5d346f613 100644 --- a/app/src/main/res/drawable/ic_baseline_keyboard_tab_reverse_24.xml +++ b/app/src/main/res/drawable/ic_baseline_keyboard_tab_reverse_24.xml @@ -2,9 +2,8 @@ android:width="24dp" android:height="24dp" android:viewportWidth="24" - android:viewportHeight="24" - android:tint="?attr/colorControlNormal"> - + android:viewportHeight="24"> + diff --git a/app/src/main/res/drawable/ic_baseline_keyboard_voice_24.xml b/app/src/main/res/drawable/ic_baseline_keyboard_voice_24.xml index 022f31cab..267f2654b 100644 --- a/app/src/main/res/drawable/ic_baseline_keyboard_voice_24.xml +++ b/app/src/main/res/drawable/ic_baseline_keyboard_voice_24.xml @@ -1,10 +1,9 @@ diff --git a/app/src/main/res/drawable/ic_baseline_language_24.xml b/app/src/main/res/drawable/ic_baseline_language_24.xml index 3f70646ba..5e28f4931 100644 --- a/app/src/main/res/drawable/ic_baseline_language_24.xml +++ b/app/src/main/res/drawable/ic_baseline_language_24.xml @@ -2,9 +2,8 @@ android:width="24dp" android:height="24dp" android:viewportWidth="24" - android:viewportHeight="24" - android:tint="?attr/colorControlNormal"> - + android:viewportHeight="24"> + diff --git a/app/src/main/res/drawable/ic_baseline_last_page_24.xml b/app/src/main/res/drawable/ic_baseline_last_page_24.xml index 32b69adb1..4d9d657c3 100644 --- a/app/src/main/res/drawable/ic_baseline_last_page_24.xml +++ b/app/src/main/res/drawable/ic_baseline_last_page_24.xml @@ -2,9 +2,8 @@ android:width="24dp" android:height="24dp" android:viewportWidth="24" - android:viewportHeight="24" - android:tint="?attr/colorControlNormal"> - + android:viewportHeight="24"> + diff --git a/app/src/main/res/drawable/ic_baseline_library_books_24.xml b/app/src/main/res/drawable/ic_baseline_library_books_24.xml index acebe2be2..b376b73f5 100644 --- a/app/src/main/res/drawable/ic_baseline_library_books_24.xml +++ b/app/src/main/res/drawable/ic_baseline_library_books_24.xml @@ -2,10 +2,8 @@ android:width="24dp" android:height="24dp" android:viewportWidth="24" - android:viewportHeight="24" - android:tint="?attr/colorControlNormal" - android:autoMirrored="true"> - + android:viewportHeight="24"> + diff --git a/app/src/main/res/drawable/ic_baseline_light_mode_24.xml b/app/src/main/res/drawable/ic_baseline_light_mode_24.xml index 920852dcc..5a386cc6b 100644 --- a/app/src/main/res/drawable/ic_baseline_light_mode_24.xml +++ b/app/src/main/res/drawable/ic_baseline_light_mode_24.xml @@ -2,9 +2,8 @@ android:width="24dp" android:height="24dp" android:viewportWidth="24" - android:viewportHeight="24" - android:tint="?attr/colorControlNormal"> + android:viewportHeight="24"> diff --git a/app/src/main/res/drawable/ic_baseline_more_horiz_24.xml b/app/src/main/res/drawable/ic_baseline_more_horiz_24.xml index 6439bcc7c..3ee36a60d 100644 --- a/app/src/main/res/drawable/ic_baseline_more_horiz_24.xml +++ b/app/src/main/res/drawable/ic_baseline_more_horiz_24.xml @@ -2,9 +2,8 @@ android:width="24dp" android:height="24dp" android:viewportWidth="24" - android:viewportHeight="24" - android:tint="?attr/colorControlNormal"> - + android:viewportHeight="24"> + diff --git a/app/src/main/res/drawable/ic_baseline_palette_24.xml b/app/src/main/res/drawable/ic_baseline_palette_24.xml index 0d97f0315..ad803bbdb 100644 --- a/app/src/main/res/drawable/ic_baseline_palette_24.xml +++ b/app/src/main/res/drawable/ic_baseline_palette_24.xml @@ -2,9 +2,8 @@ android:width="24dp" android:height="24dp" android:viewportWidth="24" - android:viewportHeight="24" - android:tint="?attr/colorControlNormal"> - + android:viewportHeight="24"> + diff --git a/app/src/main/res/drawable/ic_baseline_plus_24.xml b/app/src/main/res/drawable/ic_baseline_plus_24.xml index 5387bd227..44d5b5065 100644 --- a/app/src/main/res/drawable/ic_baseline_plus_24.xml +++ b/app/src/main/res/drawable/ic_baseline_plus_24.xml @@ -1,10 +1,9 @@ \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_baseline_push_pin_24.xml b/app/src/main/res/drawable/ic_baseline_push_pin_24.xml index f1e14a868..7ac28eca3 100644 --- a/app/src/main/res/drawable/ic_baseline_push_pin_24.xml +++ b/app/src/main/res/drawable/ic_baseline_push_pin_24.xml @@ -2,10 +2,8 @@ android:width="24dp" android:height="24dp" android:viewportWidth="24" - android:viewportHeight="24" - android:tint="?attr/colorControlNormal"> - + android:viewportHeight="24"> + diff --git a/app/src/main/res/drawable/ic_baseline_redo_24.xml b/app/src/main/res/drawable/ic_baseline_redo_24.xml index 4e218168e..ff18616d3 100644 --- a/app/src/main/res/drawable/ic_baseline_redo_24.xml +++ b/app/src/main/res/drawable/ic_baseline_redo_24.xml @@ -1,11 +1,10 @@ - + android:viewportHeight="24"> + diff --git a/app/src/main/res/drawable/ic_baseline_save_24.xml b/app/src/main/res/drawable/ic_baseline_save_24.xml index 1a8d86d20..7db25d84d 100644 --- a/app/src/main/res/drawable/ic_baseline_save_24.xml +++ b/app/src/main/res/drawable/ic_baseline_save_24.xml @@ -2,9 +2,8 @@ android:width="24dp" android:height="24dp" android:viewportWidth="24" - android:viewportHeight="24" - android:tint="?attr/colorControlNormal"> - + android:viewportHeight="24"> + diff --git a/app/src/main/res/drawable/ic_baseline_search_24.xml b/app/src/main/res/drawable/ic_baseline_search_24.xml index 07b76d627..c75a5e7a3 100644 --- a/app/src/main/res/drawable/ic_baseline_search_24.xml +++ b/app/src/main/res/drawable/ic_baseline_search_24.xml @@ -2,9 +2,8 @@ android:width="24dp" android:height="24dp" android:viewportWidth="24" - android:viewportHeight="24" - android:tint="?attr/colorControlNormal"> - + android:viewportHeight="24"> + diff --git a/app/src/main/res/drawable/ic_baseline_send_24.xml b/app/src/main/res/drawable/ic_baseline_send_24.xml index f0d63e179..54f9dac6c 100644 --- a/app/src/main/res/drawable/ic_baseline_send_24.xml +++ b/app/src/main/res/drawable/ic_baseline_send_24.xml @@ -2,10 +2,8 @@ android:width="24dp" android:height="24dp" android:viewportWidth="24" - android:viewportHeight="24" - android:tint="?attr/colorControlNormal" - android:autoMirrored="true"> - + android:viewportHeight="24"> + diff --git a/app/src/main/res/drawable/ic_baseline_settings_24.xml b/app/src/main/res/drawable/ic_baseline_settings_24.xml index 41a82ede8..8cc3d19a0 100644 --- a/app/src/main/res/drawable/ic_baseline_settings_24.xml +++ b/app/src/main/res/drawable/ic_baseline_settings_24.xml @@ -2,9 +2,8 @@ android:width="24dp" android:height="24dp" android:viewportWidth="24" - android:viewportHeight="24" - android:tint="?attr/colorControlNormal"> - + android:viewportHeight="24"> + diff --git a/app/src/main/res/drawable/ic_baseline_settings_backup_restore_24.xml b/app/src/main/res/drawable/ic_baseline_settings_backup_restore_24.xml index 1772ed506..d2aab60e5 100644 --- a/app/src/main/res/drawable/ic_baseline_settings_backup_restore_24.xml +++ b/app/src/main/res/drawable/ic_baseline_settings_backup_restore_24.xml @@ -2,9 +2,8 @@ android:width="24dp" android:height="24dp" android:viewportWidth="24" - android:viewportHeight="24" - android:tint="?attr/colorControlNormal"> - + android:viewportHeight="24"> + diff --git a/app/src/main/res/drawable/ic_baseline_space_bar_24.xml b/app/src/main/res/drawable/ic_baseline_space_bar_24.xml index 9dc510a38..e77442136 100644 --- a/app/src/main/res/drawable/ic_baseline_space_bar_24.xml +++ b/app/src/main/res/drawable/ic_baseline_space_bar_24.xml @@ -2,9 +2,8 @@ android:width="24dp" android:height="24dp" android:viewportWidth="24" - android:viewportHeight="24" - android:tint="?attr/colorControlNormal"> - + android:viewportHeight="24"> + diff --git a/app/src/main/res/drawable/ic_baseline_spellcheck_24.xml b/app/src/main/res/drawable/ic_baseline_spellcheck_24.xml index 7da40f83a..db67ac8b6 100644 --- a/app/src/main/res/drawable/ic_baseline_spellcheck_24.xml +++ b/app/src/main/res/drawable/ic_baseline_spellcheck_24.xml @@ -2,9 +2,8 @@ android:width="24dp" android:height="24dp" android:viewportWidth="24" - android:viewportHeight="24" - android:tint="?android:attr/colorControlNormal"> + android:viewportHeight="24"> diff --git a/app/src/main/res/drawable/ic_baseline_sports_basketball_24.xml b/app/src/main/res/drawable/ic_baseline_sports_basketball_24.xml index e50e8b640..b22f6c655 100644 --- a/app/src/main/res/drawable/ic_baseline_sports_basketball_24.xml +++ b/app/src/main/res/drawable/ic_baseline_sports_basketball_24.xml @@ -2,30 +2,29 @@ android:width="24dp" android:height="24dp" android:viewportWidth="24" - android:viewportHeight="24" - android:tint="?attr/colorControlNormal"> + android:viewportHeight="24"> diff --git a/app/src/main/res/drawable/ic_baseline_sync_24.xml b/app/src/main/res/drawable/ic_baseline_sync_24.xml index c2f773a17..7ab03e8f9 100644 --- a/app/src/main/res/drawable/ic_baseline_sync_24.xml +++ b/app/src/main/res/drawable/ic_baseline_sync_24.xml @@ -2,9 +2,8 @@ android:width="24dp" android:height="24dp" android:viewportWidth="24" - android:viewportHeight="24" - android:tint="?attr/colorControlNormal"> - + android:viewportHeight="24"> + diff --git a/app/src/main/res/drawable/ic_baseline_tag_faces_24.xml b/app/src/main/res/drawable/ic_baseline_tag_faces_24.xml index 4797e384e..e6a19d030 100644 --- a/app/src/main/res/drawable/ic_baseline_tag_faces_24.xml +++ b/app/src/main/res/drawable/ic_baseline_tag_faces_24.xml @@ -2,9 +2,8 @@ android:width="24dp" android:height="24dp" android:viewportWidth="24" - android:viewportHeight="24" - android:tint="?attr/colorControlNormal"> + android:viewportHeight="24"> diff --git a/app/src/main/res/drawable/ic_baseline_text_format_24.xml b/app/src/main/res/drawable/ic_baseline_text_format_24.xml index f13002b08..fea0bb4b8 100644 --- a/app/src/main/res/drawable/ic_baseline_text_format_24.xml +++ b/app/src/main/res/drawable/ic_baseline_text_format_24.xml @@ -1,10 +1,9 @@ diff --git a/app/src/main/res/drawable/ic_baseline_tune_24.xml b/app/src/main/res/drawable/ic_baseline_tune_24.xml index e8d34f142..f413b3ab5 100644 --- a/app/src/main/res/drawable/ic_baseline_tune_24.xml +++ b/app/src/main/res/drawable/ic_baseline_tune_24.xml @@ -2,9 +2,8 @@ android:width="24dp" android:height="24dp" android:viewportWidth="24" - android:viewportHeight="24" - android:tint="?attr/colorControlNormal"> - + android:viewportHeight="24"> + diff --git a/app/src/main/res/drawable/ic_baseline_undo_24.xml b/app/src/main/res/drawable/ic_baseline_undo_24.xml index 4ce410880..4e89155a4 100644 --- a/app/src/main/res/drawable/ic_baseline_undo_24.xml +++ b/app/src/main/res/drawable/ic_baseline_undo_24.xml @@ -1,11 +1,10 @@ - + android:viewportHeight="24"> + diff --git a/app/src/main/res/drawable/ic_capslock_lock.xml b/app/src/main/res/drawable/ic_capslock_lock.xml index a0445b41c..9a4a8d631 100644 --- a/app/src/main/res/drawable/ic_capslock_lock.xml +++ b/app/src/main/res/drawable/ic_capslock_lock.xml @@ -2,12 +2,11 @@ android:width="24dp" android:height="24dp" android:viewportWidth="24" - android:viewportHeight="24" - android:tint="?attr/colorControlNormal"> - - + android:viewportHeight="24"> + + diff --git a/app/src/main/res/drawable/ic_capslock_none.xml b/app/src/main/res/drawable/ic_capslock_none.xml index 9eacda5c2..e217bb4c6 100644 --- a/app/src/main/res/drawable/ic_capslock_none.xml +++ b/app/src/main/res/drawable/ic_capslock_none.xml @@ -2,9 +2,8 @@ android:width="24dp" android:height="24dp" android:viewportWidth="24" - android:viewportHeight="24" - android:tint="?attr/colorControlNormal"> - + android:viewportHeight="24"> + diff --git a/app/src/main/res/drawable/ic_capslock_once.xml b/app/src/main/res/drawable/ic_capslock_once.xml index b7ece0166..8f0494315 100644 --- a/app/src/main/res/drawable/ic_capslock_once.xml +++ b/app/src/main/res/drawable/ic_capslock_once.xml @@ -2,9 +2,8 @@ android:width="24dp" android:height="24dp" android:viewportWidth="24" - android:viewportHeight="24" - android:tint="?attr/colorControlNormal"> - + android:viewportHeight="24"> + diff --git a/app/src/main/res/drawable/ic_clipboard.xml b/app/src/main/res/drawable/ic_clipboard.xml index bf34c073d..4b98b511b 100644 --- a/app/src/main/res/drawable/ic_clipboard.xml +++ b/app/src/main/res/drawable/ic_clipboard.xml @@ -2,9 +2,8 @@ android:width="24dp" android:height="24dp" android:viewportWidth="24" - android:viewportHeight="24" - android:tint="?attr/colorControlNormal"> - + android:viewportHeight="24"> + diff --git a/app/src/main/res/drawable/ic_cursor_move.xml b/app/src/main/res/drawable/ic_cursor_move.xml index bbe47e080..8a1a6aa2d 100644 --- a/app/src/main/res/drawable/ic_cursor_move.xml +++ b/app/src/main/res/drawable/ic_cursor_move.xml @@ -2,15 +2,14 @@ android:width="24dp" android:height="24dp" android:viewportWidth="24" - android:viewportHeight="24" - android:tint="?attr/colorControlNormal"> - - - + android:viewportHeight="24"> + + + diff --git a/app/src/main/res/drawable/ic_fcitx_status_chttrans_simp.xml b/app/src/main/res/drawable/ic_fcitx_status_chttrans_simp.xml index 331561354..95c6ed5e9 100644 --- a/app/src/main/res/drawable/ic_fcitx_status_chttrans_simp.xml +++ b/app/src/main/res/drawable/ic_fcitx_status_chttrans_simp.xml @@ -2,9 +2,8 @@ android:width="24dp" android:height="24dp" android:viewportWidth="24" - android:viewportHeight="24" - android:tint="?attr/colorControlNormal"> - + android:viewportHeight="24"> + diff --git a/app/src/main/res/drawable/ic_fcitx_status_chttrans_trad.xml b/app/src/main/res/drawable/ic_fcitx_status_chttrans_trad.xml index fc0f64b26..8e4e8d7d6 100644 --- a/app/src/main/res/drawable/ic_fcitx_status_chttrans_trad.xml +++ b/app/src/main/res/drawable/ic_fcitx_status_chttrans_trad.xml @@ -2,9 +2,8 @@ android:width="24dp" android:height="24dp" android:viewportWidth="24" - android:viewportHeight="24" - android:tint="?attr/colorControlNormal"> - + android:viewportHeight="24"> + diff --git a/app/src/main/res/drawable/ic_fcitx_status_fullwidth_active.xml b/app/src/main/res/drawable/ic_fcitx_status_fullwidth_active.xml index fd2756c20..f82da83ff 100644 --- a/app/src/main/res/drawable/ic_fcitx_status_fullwidth_active.xml +++ b/app/src/main/res/drawable/ic_fcitx_status_fullwidth_active.xml @@ -2,9 +2,8 @@ android:width="24dp" android:height="24dp" android:viewportWidth="24" - android:viewportHeight="24" - android:tint="?attr/colorControlNormal"> - + android:viewportHeight="24"> + diff --git a/app/src/main/res/drawable/ic_fcitx_status_fullwidth_inactive.xml b/app/src/main/res/drawable/ic_fcitx_status_fullwidth_inactive.xml index ebd4c0a8b..d71737741 100644 --- a/app/src/main/res/drawable/ic_fcitx_status_fullwidth_inactive.xml +++ b/app/src/main/res/drawable/ic_fcitx_status_fullwidth_inactive.xml @@ -2,9 +2,8 @@ android:width="24dp" android:height="24dp" android:viewportWidth="24" - android:viewportHeight="24" - android:tint="?attr/colorControlNormal"> - + android:viewportHeight="24"> + diff --git a/app/src/main/res/drawable/ic_fcitx_status_prediction_active.xml b/app/src/main/res/drawable/ic_fcitx_status_prediction_active.xml index 8b7f1e243..0070f5f65 100644 --- a/app/src/main/res/drawable/ic_fcitx_status_prediction_active.xml +++ b/app/src/main/res/drawable/ic_fcitx_status_prediction_active.xml @@ -2,9 +2,8 @@ android:width="24dp" android:height="24dp" android:viewportWidth="24" - android:viewportHeight="24" - android:tint="?attr/colorControlNormal"> - + android:viewportHeight="24"> + diff --git a/app/src/main/res/drawable/ic_fcitx_status_prediction_inactive.xml b/app/src/main/res/drawable/ic_fcitx_status_prediction_inactive.xml index e934defbf..73dbf418a 100644 --- a/app/src/main/res/drawable/ic_fcitx_status_prediction_inactive.xml +++ b/app/src/main/res/drawable/ic_fcitx_status_prediction_inactive.xml @@ -2,9 +2,8 @@ android:width="24dp" android:height="24dp" android:viewportWidth="24" - android:viewportHeight="24" - android:tint="?attr/colorControlNormal"> - + android:viewportHeight="24"> + diff --git a/app/src/main/res/drawable/ic_fcitx_status_punc_active.xml b/app/src/main/res/drawable/ic_fcitx_status_punc_active.xml index ab7b0c30d..47bdc20b4 100644 --- a/app/src/main/res/drawable/ic_fcitx_status_punc_active.xml +++ b/app/src/main/res/drawable/ic_fcitx_status_punc_active.xml @@ -2,9 +2,8 @@ android:width="24dp" android:height="24dp" android:viewportWidth="24" - android:viewportHeight="24" - android:tint="?attr/colorControlNormal"> - + android:viewportHeight="24"> + diff --git a/app/src/main/res/drawable/ic_fcitx_status_punc_inactive.xml b/app/src/main/res/drawable/ic_fcitx_status_punc_inactive.xml index 7d37388a0..fc2e05b02 100644 --- a/app/src/main/res/drawable/ic_fcitx_status_punc_inactive.xml +++ b/app/src/main/res/drawable/ic_fcitx_status_punc_inactive.xml @@ -2,9 +2,8 @@ android:width="24dp" android:height="24dp" android:viewportWidth="24" - android:viewportHeight="24" - android:tint="?attr/colorControlNormal"> - + android:viewportHeight="24"> + diff --git a/app/src/main/res/drawable/ic_launcher_background.xml b/app/src/main/res/drawable/ic_launcher_background.xml index 385ec3f1a..73657a076 100644 --- a/app/src/main/res/drawable/ic_launcher_background.xml +++ b/app/src/main/res/drawable/ic_launcher_background.xml @@ -4,45 +4,46 @@ android:height="108dp" android:viewportWidth="108" android:viewportHeight="108"> - - - - - - - - - - - - - - + + + + + + + + + + + + + + diff --git a/app/src/main/res/drawable/ic_logo_unicode.xml b/app/src/main/res/drawable/ic_logo_unicode.xml index c69e4d3a5..7297c072a 100644 --- a/app/src/main/res/drawable/ic_logo_unicode.xml +++ b/app/src/main/res/drawable/ic_logo_unicode.xml @@ -2,9 +2,8 @@ android:width="24dp" android:height="24dp" android:viewportWidth="24" - android:viewportHeight="24" - android:tint="?attr/colorControlNormal"> - + android:viewportHeight="24"> + diff --git a/app/src/main/res/drawable/ic_number_pad.xml b/app/src/main/res/drawable/ic_number_pad.xml index 9ca81aa24..c2d7c9d98 100644 --- a/app/src/main/res/drawable/ic_number_pad.xml +++ b/app/src/main/res/drawable/ic_number_pad.xml @@ -2,18 +2,17 @@ android:width="24dp" android:height="24dp" android:viewportWidth="24" - android:viewportHeight="24" - android:tint="?attr/colorControlNormal"> - - - - + android:viewportHeight="24"> + + + + diff --git a/app/src/main/res/drawable/ic_outline_push_pin_24.xml b/app/src/main/res/drawable/ic_outline_push_pin_24.xml index b1075975b..9f6f2b6f1 100644 --- a/app/src/main/res/drawable/ic_outline_push_pin_24.xml +++ b/app/src/main/res/drawable/ic_outline_push_pin_24.xml @@ -2,9 +2,8 @@ android:width="24dp" android:height="24dp" android:viewportWidth="24" - android:viewportHeight="24" - android:tint="?attr/colorControlNormal"> - + android:viewportHeight="24"> + diff --git a/app/src/main/res/drawable/ic_view_private.xml b/app/src/main/res/drawable/ic_view_private.xml index c730ea363..1fff3a27d 100644 --- a/app/src/main/res/drawable/ic_view_private.xml +++ b/app/src/main/res/drawable/ic_view_private.xml @@ -2,12 +2,11 @@ android:width="24dp" android:height="24dp" android:viewportWidth="24" - android:viewportHeight="24" - android:tint="?attr/colorControlNormal"> - - + android:viewportHeight="24"> + + diff --git a/app/src/main/res/drawable/symbol_arrow.xml b/app/src/main/res/drawable/symbol_arrow.xml index 27e3d430f..ec2ba08bd 100644 --- a/app/src/main/res/drawable/symbol_arrow.xml +++ b/app/src/main/res/drawable/symbol_arrow.xml @@ -2,9 +2,8 @@ android:width="24dp" android:height="24dp" android:viewportWidth="24" - android:viewportHeight="24" - android:tint="?attr/colorControlNormal"> + android:viewportHeight="24"> diff --git a/app/src/main/res/drawable/symbol_bracket.xml b/app/src/main/res/drawable/symbol_bracket.xml index ea21982c4..0e8b7485a 100644 --- a/app/src/main/res/drawable/symbol_bracket.xml +++ b/app/src/main/res/drawable/symbol_bracket.xml @@ -2,12 +2,11 @@ android:width="24dp" android:height="24dp" android:viewportWidth="24" - android:viewportHeight="24" - android:tint="?attr/colorControlNormal"> - - + android:viewportHeight="24"> + + diff --git a/app/src/main/res/drawable/symbol_fullwidth.xml b/app/src/main/res/drawable/symbol_fullwidth.xml index 9e0432278..e240fb6a0 100644 --- a/app/src/main/res/drawable/symbol_fullwidth.xml +++ b/app/src/main/res/drawable/symbol_fullwidth.xml @@ -2,15 +2,14 @@ android:width="24dp" android:height="24dp" android:viewportWidth="24" - android:viewportHeight="24" - android:tint="?attr/colorControlNormal"> - - - + android:viewportHeight="24"> + + + diff --git a/app/src/main/res/drawable/symbol_math.xml b/app/src/main/res/drawable/symbol_math.xml index ffbce9679..798e87446 100644 --- a/app/src/main/res/drawable/symbol_math.xml +++ b/app/src/main/res/drawable/symbol_math.xml @@ -2,18 +2,17 @@ android:width="24dp" android:height="24dp" android:viewportWidth="24" - android:viewportHeight="24" - android:tint="?attr/colorControlNormal"> + android:viewportHeight="24"> diff --git a/app/src/main/res/drawable/symbol_number_punctuation.xml b/app/src/main/res/drawable/symbol_number_punctuation.xml index 96087558f..1d7e080bc 100644 --- a/app/src/main/res/drawable/symbol_number_punctuation.xml +++ b/app/src/main/res/drawable/symbol_number_punctuation.xml @@ -2,18 +2,17 @@ android:width="24dp" android:height="24dp" android:viewportWidth="24" - android:viewportHeight="24" - android:tint="?attr/colorControlNormal"> - - - - + android:viewportHeight="24"> + + + + diff --git a/app/src/main/res/drawable/symbol_ordinal.xml b/app/src/main/res/drawable/symbol_ordinal.xml index 33842201e..198b6c05f 100644 --- a/app/src/main/res/drawable/symbol_ordinal.xml +++ b/app/src/main/res/drawable/symbol_ordinal.xml @@ -2,12 +2,11 @@ android:width="24dp" android:height="24dp" android:viewportWidth="24" - android:viewportHeight="24" - android:tint="?attr/colorControlNormal"> - - + android:viewportHeight="24"> + + diff --git a/app/src/main/res/drawable/symbol_other.xml b/app/src/main/res/drawable/symbol_other.xml index a2f8b066b..2b83930e7 100644 --- a/app/src/main/res/drawable/symbol_other.xml +++ b/app/src/main/res/drawable/symbol_other.xml @@ -2,9 +2,8 @@ android:width="24dp" android:height="24dp" android:viewportWidth="24" - android:viewportHeight="24" - android:tint="?attr/colorControlNormal"> + android:viewportHeight="24"> From 0a53ba4fd9d50a7bffbd3c8ca5f5a52c2b3d6a08 Mon Sep 17 00:00:00 2001 From: Rocka Date: Wed, 4 Oct 2023 20:13:33 +0800 Subject: [PATCH 028/381] Delay launch of eventHandlerJob in InputView --- .../org/fcitx/fcitx5/android/input/InputView.kt | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/app/src/main/java/org/fcitx/fcitx5/android/input/InputView.kt b/app/src/main/java/org/fcitx/fcitx5/android/input/InputView.kt index 305925177..0d9f532d4 100644 --- a/app/src/main/java/org/fcitx/fcitx5/android/input/InputView.kt +++ b/app/src/main/java/org/fcitx/fcitx5/android/input/InputView.kt @@ -16,6 +16,7 @@ import androidx.annotation.RequiresApi import androidx.constraintlayout.widget.ConstraintLayout import androidx.core.view.* import androidx.lifecycle.lifecycleScope +import kotlinx.coroutines.Job import kotlinx.coroutines.launch import org.fcitx.fcitx5.android.R import org.fcitx.fcitx5.android.core.CapabilityFlags @@ -83,11 +84,7 @@ class InputView( setOnClickListener(placeholderOnClickListener) } - private val eventHandlerJob = service.lifecycleScope.launch { - fcitx.runImmediately { eventFlow }.collect { - handleFcitxEvent(it) - } - } + private val eventHandlerJob: Job private val scope = DynamicScope() private val themedContext = context.withTheme(R.style.Theme_InputViewTheme) @@ -183,6 +180,12 @@ class InputView( // MUST call before any operation setupScope() + eventHandlerJob = service.lifecycleScope.launch { + fcitx.runImmediately { eventFlow }.collect { + handleFcitxEvent(it) + } + } + // restore punctuation mapping in case of InputView recreation fcitx.launchOnReady { punctuation.updatePunctuationMapping(it.statusAreaActionsCached) From e1b7f9bdb935f77f2a3d08918b6917b366ccd266 Mon Sep 17 00:00:00 2001 From: Rocka Date: Wed, 4 Oct 2023 20:13:53 +0800 Subject: [PATCH 029/381] Fix some missing icons --- .../fcitx5/android/input/bar/ui/CandidateUi.kt | 6 ++---- .../fcitx/fcitx5/android/input/bar/ui/TitleUi.kt | 15 +++++++++++---- .../ui/main/settings/theme/ThemeThumbnailUi.kt | 1 + 3 files changed, 14 insertions(+), 8 deletions(-) diff --git a/app/src/main/java/org/fcitx/fcitx5/android/input/bar/ui/CandidateUi.kt b/app/src/main/java/org/fcitx/fcitx5/android/input/bar/ui/CandidateUi.kt index 17b8a8f63..07e901aef 100644 --- a/app/src/main/java/org/fcitx/fcitx5/android/input/bar/ui/CandidateUi.kt +++ b/app/src/main/java/org/fcitx/fcitx5/android/input/bar/ui/CandidateUi.kt @@ -13,13 +13,11 @@ import splitties.views.dsl.constraintlayout.lParams import splitties.views.dsl.constraintlayout.startOfParent import splitties.views.dsl.core.Ui import splitties.views.dsl.core.add -import splitties.views.dsl.core.view class CandidateUi(override val ctx: Context, theme: Theme, private val horizontalView: View) : Ui { - val expandButton = view(::ToolButton, R.id.expand_candidate_btn) { - setIcon(R.drawable.ic_baseline_expand_more_24) - setPressHighlightColor(theme.keyPressHighlightColor) + val expandButton = ToolButton(ctx, R.drawable.ic_baseline_expand_more_24, theme).apply { + id = R.id.expand_candidate_btn visibility = View.INVISIBLE } diff --git a/app/src/main/java/org/fcitx/fcitx5/android/input/bar/ui/TitleUi.kt b/app/src/main/java/org/fcitx/fcitx5/android/input/bar/ui/TitleUi.kt index 340ce68af..06d1ac3a1 100644 --- a/app/src/main/java/org/fcitx/fcitx5/android/input/bar/ui/TitleUi.kt +++ b/app/src/main/java/org/fcitx/fcitx5/android/input/bar/ui/TitleUi.kt @@ -7,7 +7,16 @@ import androidx.core.view.isVisible import org.fcitx.fcitx5.android.R import org.fcitx.fcitx5.android.data.theme.Theme import splitties.dimensions.dp -import splitties.views.dsl.constraintlayout.* +import splitties.views.dsl.constraintlayout.after +import splitties.views.dsl.constraintlayout.bottomOfParent +import splitties.views.dsl.constraintlayout.centerHorizontally +import splitties.views.dsl.constraintlayout.centerVertically +import splitties.views.dsl.constraintlayout.constraintLayout +import splitties.views.dsl.constraintlayout.endOfParent +import splitties.views.dsl.constraintlayout.lParams +import splitties.views.dsl.constraintlayout.matchConstraints +import splitties.views.dsl.constraintlayout.startOfParent +import splitties.views.dsl.constraintlayout.topOfParent import splitties.views.dsl.core.Ui import splitties.views.dsl.core.add import splitties.views.dsl.core.textView @@ -16,9 +25,7 @@ import splitties.views.gravityVerticalCenter class TitleUi(override val ctx: Context, theme: Theme) : Ui { - private val backButton = ToolButton(ctx, R.drawable.ic_baseline_arrow_back_24, theme).apply { - id = R.id.expand_candidate_btn - } + private val backButton = ToolButton(ctx, R.drawable.ic_baseline_arrow_back_24, theme) private val titleText = textView { typeface = Typeface.defaultFromStyle(Typeface.BOLD) diff --git a/app/src/main/java/org/fcitx/fcitx5/android/ui/main/settings/theme/ThemeThumbnailUi.kt b/app/src/main/java/org/fcitx/fcitx5/android/ui/main/settings/theme/ThemeThumbnailUi.kt index b63e614a2..a4127d935 100644 --- a/app/src/main/java/org/fcitx/fcitx5/android/ui/main/settings/theme/ThemeThumbnailUi.kt +++ b/app/src/main/java/org/fcitx/fcitx5/android/ui/main/settings/theme/ThemeThumbnailUi.kt @@ -52,6 +52,7 @@ class ThemeThumbnailUi(override val ctx: Context) : Ui { val editButton = imageView { setPaddingDp(16, 4, 4, 16) scaleType = ImageView.ScaleType.FIT_CENTER + imageResource = R.drawable.ic_baseline_edit_24 } override val root = constraintLayout { From 93f25a8a7c868cb064c81e42a5cbade415864df7 Mon Sep 17 00:00:00 2001 From: Rocka Date: Wed, 4 Oct 2023 20:15:43 +0800 Subject: [PATCH 030/381] Update prebuilt use boost 1.83.0 built by cmake --- app/src/main/cpp/CMakeLists.txt | 3 +-- lib/fcitx5-chinese-addons/src/main/cpp/CMakeLists.txt | 3 +-- lib/fcitx5/src/main/cpp/cmake/FindZLIB.cmake | 6 ++++-- lib/fcitx5/src/main/cpp/prebuilt | 2 +- lib/libime/src/main/cpp/CMakeLists.txt | 3 +-- 5 files changed, 8 insertions(+), 9 deletions(-) diff --git a/app/src/main/cpp/CMakeLists.txt b/app/src/main/cpp/CMakeLists.txt index 79a9e2696..cad904b95 100644 --- a/app/src/main/cpp/CMakeLists.txt +++ b/app/src/main/cpp/CMakeLists.txt @@ -41,9 +41,8 @@ set(Libevent_DIR "${PREBUILT_DIR}/libevent/${ANDROID_ABI}/lib/cmake/libevent") find_package(Libevent) # prebuilt boost -set(BOOST_VERSION "1.80.0") list(APPEND CMAKE_FIND_ROOT_PATH "${PREBUILT_DIR}/boost/${ANDROID_ABI}/lib/cmake") -find_package(Boost 1.80 REQUIRED COMPONENTS headers filesystem iostreams) +find_package(Boost 1.83.0 REQUIRED COMPONENTS headers filesystem iostreams CONFIG) add_library(native-lib SHARED native-lib.cpp) target_link_libraries(native-lib diff --git a/lib/fcitx5-chinese-addons/src/main/cpp/CMakeLists.txt b/lib/fcitx5-chinese-addons/src/main/cpp/CMakeLists.txt index 169e9c8a5..269d3cb17 100644 --- a/lib/fcitx5-chinese-addons/src/main/cpp/CMakeLists.txt +++ b/lib/fcitx5-chinese-addons/src/main/cpp/CMakeLists.txt @@ -39,9 +39,8 @@ set(fmt_DIR "${PREBUILT_DIR}/fmt/${ANDROID_ABI}/lib/cmake/fmt") find_package(fmt) # prebuilt boost -set(BOOST_VERSION "1.80.0") list(APPEND CMAKE_FIND_ROOT_PATH "${PREBUILT_DIR}/boost/${ANDROID_ABI}/lib/cmake") -find_package(Boost ${BOOST_VERSION} REQUIRED COMPONENTS iostreams CONFIG) +find_package(Boost 1.83.0 REQUIRED COMPONENTS iostreams CONFIG) # prebuilt marisa-tire, OpenCC needs it set(marisa_DIR "${PREBUILT_DIR}/marisa/${ANDROID_ABI}/lib/cmake/marisa") diff --git a/lib/fcitx5/src/main/cpp/cmake/FindZLIB.cmake b/lib/fcitx5/src/main/cpp/cmake/FindZLIB.cmake index e91a16780..cdd0f4172 100644 --- a/lib/fcitx5/src/main/cpp/cmake/FindZLIB.cmake +++ b/lib/fcitx5/src/main/cpp/cmake/FindZLIB.cmake @@ -1,6 +1,8 @@ find_library(ZLIB z) -add_library(ZLIB::ZLIB SHARED IMPORTED) -set_target_properties(ZLIB::ZLIB PROPERTIES IMPORTED_LOCATION ${ZLIB}) +if(NOT TARGET ZLIB::ZLIB) + add_library(ZLIB::ZLIB SHARED IMPORTED) + set_target_properties(ZLIB::ZLIB PROPERTIES IMPORTED_LOCATION ${ZLIB}) +endif() set(ZLIB_LIBRARIES ${ZLIB}) set(ZLIB_INCLUDE_DIRS "${ANDROID_TOOLCHAIN_ROOT}/sysroot/usr/include") set(ZLIB_FOUND TRUE) diff --git a/lib/fcitx5/src/main/cpp/prebuilt b/lib/fcitx5/src/main/cpp/prebuilt index 90fa6fba9..1e5240b60 160000 --- a/lib/fcitx5/src/main/cpp/prebuilt +++ b/lib/fcitx5/src/main/cpp/prebuilt @@ -1 +1 @@ -Subproject commit 90fa6fba91011f0f5c9dbca0ee764447e1be35fd +Subproject commit 1e5240b605042f689463b957ec2a8fcbf58ab445 diff --git a/lib/libime/src/main/cpp/CMakeLists.txt b/lib/libime/src/main/cpp/CMakeLists.txt index 8eedc56a2..b4e028734 100644 --- a/lib/libime/src/main/cpp/CMakeLists.txt +++ b/lib/libime/src/main/cpp/CMakeLists.txt @@ -24,9 +24,8 @@ set(fmt_DIR "${PREBUILT_DIR}/fmt/${ANDROID_ABI}/lib/cmake/fmt") find_package(fmt) # prebuilt boost -set(BOOST_VERSION "1.80.0") list(APPEND CMAKE_FIND_ROOT_PATH "${PREBUILT_DIR}/boost/${ANDROID_ABI}/lib/cmake") -find_package(Boost ${BOOST_VERSION} REQUIRED COMPONENTS filesystem iostreams CONFIG) +find_package(Boost 1.83.0 REQUIRED COMPONENTS filesystem iostreams CONFIG) # prebuilt zstd set(zstd_DIR "${PREBUILT_DIR}/zstd/${ANDROID_ABI}/lib/cmake/zstd") From 4d67d318c05b4a8ef56f55d97a0328912b1c9253 Mon Sep 17 00:00:00 2001 From: Rocka Date: Fri, 6 Oct 2023 14:02:09 +0800 Subject: [PATCH 031/381] New androidnotification addon only implements `fcitx::INotifications::showTip` --- .idea/dictionaries/project.xml | 1 + app/build.gradle.kts | 3 +- app/src/main/cpp/CMakeLists.txt | 1 + .../cpp/androidfrontend/androidfrontend.cpp | 8 ++ .../cpp/androidfrontend/androidfrontend.h | 6 +- .../androidfrontend/androidfrontend_public.h | 9 +- .../cpp/androidnotification/CMakeLists.txt | 10 +++ .../androidnotification.cpp | 85 +++++++++++++++++++ .../androidnotification/androidnotification.h | 75 ++++++++++++++++ .../notifications.conf.in.in | 11 +++ app/src/main/cpp/jni-utils.h | 2 + app/src/main/cpp/native-lib.cpp | 5 ++ .../org/fcitx/fcitx5/android/core/Fcitx.kt | 14 +++ 13 files changed, 227 insertions(+), 3 deletions(-) create mode 100644 app/src/main/cpp/androidnotification/CMakeLists.txt create mode 100644 app/src/main/cpp/androidnotification/androidnotification.cpp create mode 100644 app/src/main/cpp/androidnotification/androidnotification.h create mode 100644 app/src/main/cpp/androidnotification/notifications.conf.in.in diff --git a/.idea/dictionaries/project.xml b/.idea/dictionaries/project.xml index 88418956f..c5696f544 100644 --- a/.idea/dictionaries/project.xml +++ b/.idea/dictionaries/project.xml @@ -3,6 +3,7 @@ androidfrontend androidkeyboard + androidnotification berberman constraintlayout cout diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 4479d1bc0..131003125 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -27,7 +27,8 @@ android { "copy-fcitx5-modules", // android specific modules "androidfrontend", - "androidkeyboard" + "androidkeyboard", + "androidnotification" ) } } diff --git a/app/src/main/cpp/CMakeLists.txt b/app/src/main/cpp/CMakeLists.txt index cad904b95..1b6946c8e 100644 --- a/app/src/main/cpp/CMakeLists.txt +++ b/app/src/main/cpp/CMakeLists.txt @@ -28,6 +28,7 @@ include("${FCITX_INSTALL_CMAKECONFIG_DIR}/Fcitx5Utils/Fcitx5CompilerSettings.cma add_subdirectory(po) add_subdirectory(androidfrontend) add_subdirectory(androidkeyboard) +add_subdirectory(androidnotification) # prebuilt dir. at least it works. set(PREBUILT_DIR "${CMAKE_CURRENT_SOURCE_DIR}/../../../../lib/fcitx5/src/main/cpp/prebuilt") diff --git a/app/src/main/cpp/androidfrontend/androidfrontend.cpp b/app/src/main/cpp/androidfrontend/androidfrontend.cpp index fe1867280..81659db9a 100644 --- a/app/src/main/cpp/androidfrontend/androidfrontend.cpp +++ b/app/src/main/cpp/androidfrontend/androidfrontend.cpp @@ -296,6 +296,10 @@ std::vector AndroidFrontend::getCandidates(const int offset, const return ic->getCandidates(offset, limit); } +void AndroidFrontend::showToast(const std::string &s) { + toastCallback(s); +} + void AndroidFrontend::setCommitStringCallback(const CommitStringCallback &callback) { commitStringCallback = callback; } @@ -331,6 +335,10 @@ void AndroidFrontend::handleStatusAreaUpdate() { }); } +void AndroidFrontend::setToastCallback(const ToastCallback &callback) { + toastCallback = callback; +} + class AndroidFrontendFactory : public AddonFactory { public: AddonInstance *create(AddonManager *manager) override { diff --git a/app/src/main/cpp/androidfrontend/androidfrontend.h b/app/src/main/cpp/androidfrontend/androidfrontend.h index a8bd64278..9202b6a90 100644 --- a/app/src/main/cpp/androidfrontend/androidfrontend.h +++ b/app/src/main/cpp/androidfrontend/androidfrontend.h @@ -3,7 +3,6 @@ #include #include -#include #include #include "androidfrontend_public.h" @@ -35,6 +34,7 @@ class AndroidFrontend : public AddonInstance { InputContext *activeInputContext() const; void setCapabilityFlags(uint64_t flag); std::vector getCandidates(const int offset, const int limit); + void showToast(const std::string &s); void setCandidateListCallback(const CandidateListCallback &callback); void setCommitStringCallback(const CommitStringCallback &callback); void setPreeditCallback(const ClientPreeditCallback &callback); @@ -42,6 +42,7 @@ class AndroidFrontend : public AddonInstance { void setKeyEventCallback(const KeyEventCallback &callback); void setInputMethodChangeCallback(const InputMethodChangeCallback &callback); void setStatusAreaUpdateCallback(const StatusAreaUpdateCallback &callback); + void setToastCallback(const ToastCallback &callback); private: FCITX_ADDON_EXPORT_FUNCTION(AndroidFrontend, keyEvent); @@ -55,6 +56,7 @@ class AndroidFrontend : public AddonInstance { FCITX_ADDON_EXPORT_FUNCTION(AndroidFrontend, deactivateInputContext); FCITX_ADDON_EXPORT_FUNCTION(AndroidFrontend, setCapabilityFlags); FCITX_ADDON_EXPORT_FUNCTION(AndroidFrontend, getCandidates); + FCITX_ADDON_EXPORT_FUNCTION(AndroidFrontend, showToast); FCITX_ADDON_EXPORT_FUNCTION(AndroidFrontend, setCandidateListCallback); FCITX_ADDON_EXPORT_FUNCTION(AndroidFrontend, setCommitStringCallback); FCITX_ADDON_EXPORT_FUNCTION(AndroidFrontend, setPreeditCallback); @@ -62,6 +64,7 @@ class AndroidFrontend : public AddonInstance { FCITX_ADDON_EXPORT_FUNCTION(AndroidFrontend, setKeyEventCallback); FCITX_ADDON_EXPORT_FUNCTION(AndroidFrontend, setInputMethodChangeCallback); FCITX_ADDON_EXPORT_FUNCTION(AndroidFrontend, setStatusAreaUpdateCallback); + FCITX_ADDON_EXPORT_FUNCTION(AndroidFrontend, setToastCallback); Instance *instance_; FocusGroup focusGroup_; @@ -80,6 +83,7 @@ class AndroidFrontend : public AddonInstance { KeyEventCallback keyEventCallback = [](const int, const uint32_t, const uint32_t, const bool, const int) {}; InputMethodChangeCallback imChangeCallback = [] {}; StatusAreaUpdateCallback statusAreaUpdateCallback = [] {}; + ToastCallback toastCallback = [](const std::string &) {}; }; } // namespace fcitx diff --git a/app/src/main/cpp/androidfrontend/androidfrontend_public.h b/app/src/main/cpp/androidfrontend/androidfrontend_public.h index f75a72711..cff2ffaf3 100644 --- a/app/src/main/cpp/androidfrontend/androidfrontend_public.h +++ b/app/src/main/cpp/androidfrontend/androidfrontend_public.h @@ -1,7 +1,7 @@ #ifndef _FCITX5_ANDROID_ANDROIDFRONTEND_PUBLIC_H_ #define _FCITX5_ANDROID_ANDROIDFRONTEND_PUBLIC_H_ -#include +#include #include typedef std::function &, const int)> CandidateListCallback; @@ -11,6 +11,7 @@ typedef std::function KeyEventCallback; typedef std::function InputMethodChangeCallback; typedef std::function StatusAreaUpdateCallback; +typedef std::function ToastCallback; FCITX_ADDON_DECLARE_FUNCTION(AndroidFrontend, keyEvent, void(const fcitx::Key &, bool isRelease, const int timestamp)) @@ -45,6 +46,9 @@ FCITX_ADDON_DECLARE_FUNCTION(AndroidFrontend, setCapabilityFlags, FCITX_ADDON_DECLARE_FUNCTION(AndroidFrontend, getCandidates, std::vector(const int, const int)) +FCITX_ADDON_DECLARE_FUNCTION(AndroidFrontend, showToast, + void(const std::string &)) + FCITX_ADDON_DECLARE_FUNCTION(AndroidFrontend, setCandidateListCallback, void(const CandidateListCallback &)) @@ -66,4 +70,7 @@ FCITX_ADDON_DECLARE_FUNCTION(AndroidFrontend, setInputMethodChangeCallback, FCITX_ADDON_DECLARE_FUNCTION(AndroidFrontend, setStatusAreaUpdateCallback, void(const StatusAreaUpdateCallback &)) +FCITX_ADDON_DECLARE_FUNCTION(AndroidFrontend, setToastCallback, + void(const ToastCallback &)) + #endif // _FCITX5_ANDROID_ANDROIDFRONTEND_PUBLIC_H_ diff --git a/app/src/main/cpp/androidnotification/CMakeLists.txt b/app/src/main/cpp/androidnotification/CMakeLists.txt new file mode 100644 index 000000000..86d583495 --- /dev/null +++ b/app/src/main/cpp/androidnotification/CMakeLists.txt @@ -0,0 +1,10 @@ +add_definitions(-DFCITX_GETTEXT_DOMAIN=\"fcitx5-android\") + +add_library(androidnotification MODULE androidnotification.cpp) +target_link_libraries(androidnotification Fcitx5::Core Fcitx5::Utils Fcitx5::Module::Notifications) + +configure_file(notifications.conf.in.in notifications.conf.in @ONLY) +fcitx5_translate_desktop_file(${CMAKE_CURRENT_BINARY_DIR}/notifications.conf.in notifications.conf) +install(FILES "${CMAKE_CURRENT_BINARY_DIR}/notifications.conf" + DESTINATION "${FCITX_INSTALL_PKGDATADIR}/addon" + COMPONENT config) diff --git a/app/src/main/cpp/androidnotification/androidnotification.cpp b/app/src/main/cpp/androidnotification/androidnotification.cpp new file mode 100644 index 000000000..116137b26 --- /dev/null +++ b/app/src/main/cpp/androidnotification/androidnotification.cpp @@ -0,0 +1,85 @@ +#include + +#include +#include + +#include "../androidfrontend/androidfrontend_public.h" + +#include "androidnotification.h" + +namespace fcitx { + +void Notifications::updateConfig() { + hiddenNotifications_.clear(); + for (const auto &id: config_.hiddenNotifications.value()) { + hiddenNotifications_.insert(id); + } +} + +void Notifications::reloadConfig() { + readAsIni(config_, ConfPath); + updateConfig(); +} + +void Notifications::save() { + std::vector values_; + for (const auto &id: hiddenNotifications_) { + values_.push_back(id); + } + config_.hiddenNotifications.setValue(std::move(values_)); + safeSaveAsIni(config_, ConfPath); +} + +uint32_t Notifications::sendNotification( + const std::string &appName, + uint32_t replaceId, + const std::string &appIcon, + const std::string &summary, + const std::string &body, + const std::vector &actions, + int32_t timeout, + NotificationActionCallback actionCallback, + NotificationClosedCallback closedCallback) { + // TODO implement Notification + FCITX_UNUSED(appName); + FCITX_UNUSED(replaceId); + FCITX_UNUSED(appIcon); + FCITX_UNUSED(summary); + FCITX_UNUSED(body); + FCITX_UNUSED(actions); + FCITX_UNUSED(timeout); + FCITX_UNUSED(actionCallback); + FCITX_UNUSED(closedCallback); + return 0; +} + +void Notifications::showTip( + const std::string &tipId, + const std::string &appName, + const std::string &appIcon, + const std::string &summary, + const std::string &body, + int32_t timeout) { + FCITX_UNUSED(appName); + FCITX_UNUSED(appIcon); + FCITX_UNUSED(timeout); + if (hiddenNotifications_.count(tipId)) { + return; + } + std::string const s = summary + ": " + body; + androidfrontend()->call(s); +} + +void Notifications::closeNotification(uint64_t internalId) { + FCITX_UNUSED(internalId); +} + +class NotificationsModuleFactory : public AddonFactory { + AddonInstance *create(AddonManager *manager) override { + return new Notifications(manager->instance()); + } +}; + +} + +FCITX_ADDON_FACTORY(fcitx::NotificationsModuleFactory) diff --git a/app/src/main/cpp/androidnotification/androidnotification.h b/app/src/main/cpp/androidnotification/androidnotification.h new file mode 100644 index 000000000..9ffba53b1 --- /dev/null +++ b/app/src/main/cpp/androidnotification/androidnotification.h @@ -0,0 +1,75 @@ +#ifndef FCITX5_ANDROID_ANDROIDNOTIFICATION_H +#define FCITX5_ANDROID_ANDROIDNOTIFICATION_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +namespace fcitx { + +FCITX_CONFIGURATION(NotificationsConfig, + fcitx::Option> hiddenNotifications{ + this, "HiddenNotifications", + _("Hidden Notifications")};); + +class Notifications final : public AddonInstance { +public: + Notifications(Instance *instance) : instance_(instance) {}; + ~Notifications() = default; + + Instance *instance() { return instance_; } + + void updateConfig(); + void reloadConfig() override; + void save() override; + + const Configuration *getConfig() const override { return &config_; } + + void setConfig(const RawConfig &config) override { + config_.load(config, true); + safeSaveAsIni(config_, ConfPath); + updateConfig(); + } + + FCITX_ADDON_DEPENDENCY_LOADER(androidfrontend, instance_->addonManager()); + + uint32_t sendNotification(const std::string &appName, uint32_t replaceId, + const std::string &appIcon, + const std::string &summary, + const std::string &body, + const std::vector &actions, + int32_t timeout, + NotificationActionCallback actionCallback, + NotificationClosedCallback closedCallback); + + void showTip(const std::string &tipId, const std::string &appName, + const std::string &appIcon, const std::string &summary, + const std::string &body, int32_t timeout); + + void closeNotification(uint64_t internalId); + +private: + FCITX_ADDON_EXPORT_FUNCTION(Notifications, sendNotification); + FCITX_ADDON_EXPORT_FUNCTION(Notifications, showTip); + FCITX_ADDON_EXPORT_FUNCTION(Notifications, closeNotification); + + static const inline std::string ConfPath = "conf/androidnotification.conf"; + + NotificationsConfig config_; + Instance *instance_; + + std::unordered_set hiddenNotifications_; + +}; // class Notifications + +} // namespace fcitx + +#endif //FCITX5_ANDROID_ANDROIDNOTIFICATION_H diff --git a/app/src/main/cpp/androidnotification/notifications.conf.in.in b/app/src/main/cpp/androidnotification/notifications.conf.in.in new file mode 100644 index 000000000..453050f16 --- /dev/null +++ b/app/src/main/cpp/androidnotification/notifications.conf.in.in @@ -0,0 +1,11 @@ +[Addon] +Name=Android Toast & Notification +Type=SharedLibrary +Library=libandroidnotification +Category=Module +Version=@PROJECT_VERSION@ +OnDemand=True +Configurable=True + +[Addon/Dependencies] +0=androidfrontend:@PROJECT_VERSION@ diff --git a/app/src/main/cpp/jni-utils.h b/app/src/main/cpp/jni-utils.h index 5ec574728..3037e2017 100644 --- a/app/src/main/cpp/jni-utils.h +++ b/app/src/main/cpp/jni-utils.h @@ -102,6 +102,7 @@ class GlobalRefSingleton { jmethodID BooleanInit; jclass Fcitx; + jmethodID ShowToast; jmethodID HandleFcitxEvent; jclass InputMethodEntry; @@ -142,6 +143,7 @@ class GlobalRefSingleton { BooleanInit = env->GetMethodID(Boolean, "", "(Z)V"); Fcitx = reinterpret_cast(env->NewGlobalRef(env->FindClass("org/fcitx/fcitx5/android/core/Fcitx"))); + ShowToast = env->GetStaticMethodID(Fcitx, "showToast", "(Ljava/lang/String;)V"); HandleFcitxEvent = env->GetStaticMethodID(Fcitx, "handleFcitxEvent", "(I[Ljava/lang/Object;)V"); InputMethodEntry = reinterpret_cast(env->NewGlobalRef(env->FindClass("org/fcitx/fcitx5/android/core/InputMethodEntry"))); diff --git a/app/src/main/cpp/native-lib.cpp b/app/src/main/cpp/native-lib.cpp index 3f49a85d8..a8006657c 100644 --- a/app/src/main/cpp/native-lib.cpp +++ b/app/src/main/cpp/native-lib.cpp @@ -615,6 +615,10 @@ Java_org_fcitx_fcitx5_android_core_Fcitx_startupFcitx(JNIEnv *env, jclass clazz, } env->CallStaticVoidMethod(GlobalRef->Fcitx, GlobalRef->HandleFcitxEvent, 7, *vararg); }; + auto toastCallback = [](const std::string &s) { + auto env = GlobalRef->AttachEnv(); + env->CallStaticVoidMethod(GlobalRef->Fcitx, GlobalRef->ShowToast, *JString(env, s)); + }; Fcitx::Instance().startup([&](auto *androidfrontend) { FCITX_INFO() << "Setting up callback"; @@ -626,6 +630,7 @@ Java_org_fcitx_fcitx5_android_core_Fcitx_startupFcitx(JNIEnv *env, jclass clazz, androidfrontend->template call(keyEventCallback); androidfrontend->template call(imChangeCallback); androidfrontend->template call(statusAreaUpdateCallback); + androidfrontend->template call(toastCallback); }); FCITX_INFO() << "Finishing startup"; } diff --git a/app/src/main/java/org/fcitx/fcitx5/android/core/Fcitx.kt b/app/src/main/java/org/fcitx/fcitx5/android/core/Fcitx.kt index 90b53eea0..27f105c49 100644 --- a/app/src/main/java/org/fcitx/fcitx5/android/core/Fcitx.kt +++ b/app/src/main/java/org/fcitx/fcitx5/android/core/Fcitx.kt @@ -2,6 +2,7 @@ package org.fcitx.fcitx5.android.core import android.content.Context import androidx.annotation.Keep +import androidx.core.content.ContextCompat import kotlinx.coroutines.channels.BufferOverflow import kotlinx.coroutines.flow.MutableSharedFlow import kotlinx.coroutines.flow.asSharedFlow @@ -15,6 +16,8 @@ import org.fcitx.fcitx5.android.data.clipboard.ClipboardManager import org.fcitx.fcitx5.android.data.prefs.AppPrefs import org.fcitx.fcitx5.android.utils.ImmutableGraph import org.fcitx.fcitx5.android.utils.Locales +import org.fcitx.fcitx5.android.utils.appContext +import org.fcitx.fcitx5.android.utils.toast import timber.log.Timber /** @@ -166,6 +169,17 @@ class Fcitx(private val context: Context) : FcitxAPI, FcitxLifecycleOwner { private companion object JNI { + /** + * called from native-lib + */ + @Suppress("unused") + @JvmStatic + fun showToast(s: String) { + ContextCompat.getMainExecutor(appContext).execute { + appContext.toast(s) + } + } + private val eventFlow_ = MutableSharedFlow>( extraBufferCapacity = 15, From 358c3ce92348322e19657f2d568995c8709022e5 Mon Sep 17 00:00:00 2001 From: Rocka Date: Fri, 6 Oct 2023 14:19:09 +0800 Subject: [PATCH 032/381] Support BulkCandidateList with unknown size (-1) --- .../cpp/androidfrontend/androidfrontend.cpp | 37 ++++++++++++------- .../HorizontalCandidateComponent.kt | 4 +- .../expanded/CandidatesPagingSource.kt | 6 ++- .../window/BaseExpandedCandidateWindow.kt | 2 +- lib/fcitx5/src/main/cpp/fcitx5 | 2 +- 5 files changed, 32 insertions(+), 19 deletions(-) diff --git a/app/src/main/cpp/androidfrontend/androidfrontend.cpp b/app/src/main/cpp/androidfrontend/androidfrontend.cpp index 81659db9a..ef0c60587 100644 --- a/app/src/main/cpp/androidfrontend/androidfrontend.cpp +++ b/app/src/main/cpp/androidfrontend/androidfrontend.cpp @@ -55,7 +55,7 @@ class AndroidInputContext : public InputContext { if (!isPreeditEnabled()) { checkClientPreeditUpdate(); } - InputPanel &ip = inputPanel(); + const InputPanel &ip = inputPanel(); frontend_->updateInputPanel( filterText(ip.preedit()), filterText(ip.auxUp()), @@ -69,12 +69,17 @@ class AndroidInputContext : public InputContext { if (bulk) { size = bulk->totalSize(); // limit candidate count to 16 (for paging) - const int limit = std::min(size, 16); + const int limit = size < 0 ? 16 : std::min(size, 16); for (int i = 0; i < limit; i++) { - auto &candidate = bulk->candidateFromAll(i); - // maybe unnecessary; I don't see anywhere using `CandidateWord::setPlaceHolder` - // if (candidate.isPlaceHolder()) continue; - candidates.emplace_back(filterString(candidate.text())); + try { + auto &candidate = bulk->candidateFromAll(i); + // maybe unnecessary; I don't see anywhere using `CandidateWord::setPlaceHolder` + // if (candidate.isPlaceHolder()) continue; + candidates.emplace_back(filterString(candidate.text())); + } catch (const std::invalid_argument &e) { + size = static_cast(candidates.size()); + break; + } } } else { size = list->size(); @@ -109,16 +114,22 @@ class AndroidInputContext : public InputContext { std::vector candidates; const auto &list = inputPanel().candidateList(); if (list) { + const int last = offset + limit; const auto &bulk = list->toBulk(); if (bulk) { - const int _limit = std::min(bulk->totalSize(), offset + limit); - for (int i = offset; i < _limit; i++) { - auto &candidate = bulk->candidateFromAll(i); - candidates.emplace_back(filterString(candidate.text())); + const int totalSize = bulk->totalSize(); + const int end = totalSize < 0 ? last : std::min(totalSize, last); + for (int i = offset; i < end; i++) { + try { + auto &candidate = bulk->candidateFromAll(i); + candidates.emplace_back(filterString(candidate.text())); + } catch (const std::invalid_argument &e) { + break; + } } } else { - const int _limit = std::min(list->size(), offset + limit); - for (int i = offset; i < _limit; i++) { + const int end = std::min(list->size(), last); + for (int i = offset; i < end; i++) { candidates.emplace_back(filterString(list->candidate(i).text())); } } @@ -134,7 +145,7 @@ class AndroidInputContext : public InputContext { void checkClientPreeditUpdate() { const auto &clientPreedit = filterText(inputPanel().clientPreedit()); - bool empty = clientPreedit.empty(); + const bool empty = clientPreedit.empty(); // skip update if new and old clientPreedit are both empty if (empty && clientPreeditEmpty_) return; clientPreeditEmpty_ = empty; diff --git a/app/src/main/java/org/fcitx/fcitx5/android/input/candidates/HorizontalCandidateComponent.kt b/app/src/main/java/org/fcitx/fcitx5/android/input/candidates/HorizontalCandidateComponent.kt index 99d9600e0..51586680f 100644 --- a/app/src/main/java/org/fcitx/fcitx5/android/input/candidates/HorizontalCandidateComponent.kt +++ b/app/src/main/java/org/fcitx/fcitx5/android/input/candidates/HorizontalCandidateComponent.kt @@ -26,7 +26,6 @@ import org.fcitx.fcitx5.android.input.candidates.expanded.decoration.FlexboxVert import org.fcitx.fcitx5.android.input.dependency.UniqueViewComponent import org.fcitx.fcitx5.android.input.dependency.context import org.fcitx.fcitx5.android.input.dependency.fcitx -import org.fcitx.fcitx5.android.input.dependency.inputMethodService import org.fcitx.fcitx5.android.input.dependency.theme import org.mechdancer.dependency.manager.must import splitties.dimensions.dp @@ -35,7 +34,6 @@ import kotlin.math.max class HorizontalCandidateComponent : UniqueViewComponent(), InputBroadcastReceiver { - private val service by manager.inputMethodService() private val context by manager.context() private val fcitx by manager.fcitx() private val theme by manager.theme() @@ -118,7 +116,7 @@ class HorizontalCandidateComponent : refreshExpanded() bar.expandButtonStateMachine.push( ExpandedCandidatesUpdated, - ExpandedCandidatesEmpty to (adapter.total <= childCount) + ExpandedCandidatesEmpty to (adapter.total == childCount) ) } // no need to override `generate{,Default}LayoutParams`, because HorizontalCandidateViewAdapter diff --git a/app/src/main/java/org/fcitx/fcitx5/android/input/candidates/expanded/CandidatesPagingSource.kt b/app/src/main/java/org/fcitx/fcitx5/android/input/candidates/expanded/CandidatesPagingSource.kt index 614a756b4..6d4df104e 100644 --- a/app/src/main/java/org/fcitx/fcitx5/android/input/candidates/expanded/CandidatesPagingSource.kt +++ b/app/src/main/java/org/fcitx/fcitx5/android/input/candidates/expanded/CandidatesPagingSource.kt @@ -17,7 +17,11 @@ class CandidatesPagingSource(val fcitx: FcitxConnection, val total: Int, val off getCandidates(startIndex, pageSize) } val prevKey = if (startIndex >= pageSize) startIndex - pageSize else null - val nextKey = if (startIndex + pageSize + 1 >= total) null else startIndex + pageSize + val nextKey = if (total > 0) { + if (startIndex + pageSize + 1 >= total) null else startIndex + pageSize + } else { + if (candidates.size < pageSize) null else startIndex + pageSize + } return LoadResult.Page(candidates.toList(), prevKey, nextKey) } diff --git a/app/src/main/java/org/fcitx/fcitx5/android/input/candidates/expanded/window/BaseExpandedCandidateWindow.kt b/app/src/main/java/org/fcitx/fcitx5/android/input/candidates/expanded/window/BaseExpandedCandidateWindow.kt index 1a54372f3..441efcd24 100644 --- a/app/src/main/java/org/fcitx/fcitx5/android/input/candidates/expanded/window/BaseExpandedCandidateWindow.kt +++ b/app/src/main/java/org/fcitx/fcitx5/android/input/candidates/expanded/window/BaseExpandedCandidateWindow.kt @@ -139,7 +139,7 @@ abstract class BaseExpandedCandidateWindow> : override fun onDetached() { bar.expandButtonStateMachine.push( ExpandedCandidatesDetached, - ExpandedCandidatesEmpty to (horizontalCandidate.adapter.total <= adapter.offset) + ExpandedCandidatesEmpty to (horizontalCandidate.adapter.total == adapter.offset) ) candidatesSubmitJob?.cancel() offsetJob?.cancel() diff --git a/lib/fcitx5/src/main/cpp/fcitx5 b/lib/fcitx5/src/main/cpp/fcitx5 index 7bf330412..ae2081cc4 160000 --- a/lib/fcitx5/src/main/cpp/fcitx5 +++ b/lib/fcitx5/src/main/cpp/fcitx5 @@ -1 +1 @@ -Subproject commit 7bf330412cef5906eac8a386c4e324ee0e95eac9 +Subproject commit ae2081cc41ac8bad19af5ca79167744f78d8f19a From 1351cb490d5b299227998adf5e31d204af4d973c Mon Sep 17 00:00:00 2001 From: Rocka Date: Fri, 6 Oct 2023 15:35:07 +0800 Subject: [PATCH 033/381] Fix toolbar close when expandToolbarByDefault enabled --- .../android/input/bar/KawaiiBarComponent.kt | 58 ++++++++++++------- .../fcitx5/android/input/bar/ui/IdleUi.kt | 3 +- 2 files changed, 38 insertions(+), 23 deletions(-) diff --git a/app/src/main/java/org/fcitx/fcitx5/android/input/bar/KawaiiBarComponent.kt b/app/src/main/java/org/fcitx/fcitx5/android/input/bar/KawaiiBarComponent.kt index a40e4fa07..023fb4765 100644 --- a/app/src/main/java/org/fcitx/fcitx5/android/input/bar/KawaiiBarComponent.kt +++ b/app/src/main/java/org/fcitx/fcitx5/android/input/bar/KawaiiBarComponent.kt @@ -83,11 +83,11 @@ class KawaiiBarComponent : UniqueViewComponent( private var clipboardTimeoutJob: Job? = null - private var isClipboardFresh: Boolean = true - private var isInlineSuggestionEmpty: Boolean = true + private var isClipboardFresh: Boolean = false + private var isInlineSuggestionPresent: Boolean = false private var isCapabilityFlagsPassword: Boolean = false private var isKeyboardLayoutNumber: Boolean = false - private var isToolbarManuallyExpanded: Boolean = false + private var isToolbarManuallyToggled: Boolean = false @Keep private val onClipboardUpdateListener = @@ -95,10 +95,10 @@ class KawaiiBarComponent : UniqueViewComponent( if (!clipboardSuggestion.getValue()) return@OnClipboardUpdateListener service.lifecycleScope.launch { if (it.text.isEmpty()) { - isClipboardFresh = true + isClipboardFresh = false } else { idleUi.clipboardUi.text.text = it.text.take(42) - isClipboardFresh = false + isClipboardFresh = true launchClipboardTimeoutJob() } evalIdleUiState() @@ -109,7 +109,7 @@ class KawaiiBarComponent : UniqueViewComponent( private val onClipboardSuggestionUpdateListener = ManagedPreference.OnChangeListener { _, it -> if (!it) { - isClipboardFresh = true + isClipboardFresh = false evalIdleUiState() clipboardTimeoutJob?.cancel() clipboardTimeoutJob = null @@ -135,18 +135,25 @@ class KawaiiBarComponent : UniqueViewComponent( if (timeout < 0L) return clipboardTimeoutJob = service.lifecycleScope.launch { delay(timeout) - isClipboardFresh = true + isClipboardFresh = false clipboardTimeoutJob = null } } private fun evalIdleUiState(fromUser: Boolean = false) { val newState = when { - !isClipboardFresh -> IdleUi.State.Clipboard - !isInlineSuggestionEmpty -> IdleUi.State.InlineSuggestion + isClipboardFresh -> IdleUi.State.Clipboard + isInlineSuggestionPresent -> IdleUi.State.InlineSuggestion isCapabilityFlagsPassword && !isKeyboardLayoutNumber -> IdleUi.State.NumberRow - expandToolbarByDefault || isToolbarManuallyExpanded -> IdleUi.State.Toolbar - else -> IdleUi.State.Empty + /** + * state matrix: + * expandToolbarByDefault + * | \ | true | false + * isToolbarManuallyToggled | true | Empty | Toolbar + * | false | Toolbar | Empty + */ + expandToolbarByDefault == isToolbarManuallyToggled -> IdleUi.State.Empty + else -> IdleUi.State.Toolbar } if (newState == idleUi.currentState) return idleUi.updateState(newState, fromUser) @@ -166,12 +173,19 @@ class KawaiiBarComponent : UniqueViewComponent( private val idleUi: IdleUi by lazy { IdleUi(context, theme, popup, commonKeyActionListener).apply { menuButton.setOnClickListener { - if (idleUi.currentState == IdleUi.State.Toolbar) { - isToolbarManuallyExpanded = false - evalIdleUiState(fromUser = true) - } else { - isToolbarManuallyExpanded = true - idleUi.updateState(IdleUi.State.Toolbar, fromUser = true) + when (idleUi.currentState) { + IdleUi.State.Empty -> { + isToolbarManuallyToggled = !expandToolbarByDefault + evalIdleUiState(fromUser = true) + } + IdleUi.State.Toolbar -> { + isToolbarManuallyToggled = expandToolbarByDefault + evalIdleUiState(fromUser = true) + } + else -> { + isToolbarManuallyToggled = !expandToolbarByDefault + idleUi.updateState(IdleUi.State.Toolbar, fromUser = true) + } } // reset timeout timer (if present) when user switch layout if (clipboardTimeoutJob != null) { @@ -203,8 +217,8 @@ class KawaiiBarComponent : UniqueViewComponent( } clipboardTimeoutJob?.cancel() clipboardTimeoutJob = null - isClipboardFresh = true - evalIdleUiState(fromUser = true) + isClipboardFresh = false + evalIdleUiState() } setOnLongClickListener { ClipboardManager.lastEntry?.let { @@ -311,7 +325,7 @@ class KawaiiBarComponent : UniqueViewComponent( idleUi.privateMode(info.imeOptions.hasFlag(EditorInfo.IME_FLAG_NO_PERSONALIZED_LEARNING)) } isCapabilityFlagsPassword = toolbarNumRowOnPassword && capFlags.has(CapabilityFlag.Password) - isInlineSuggestionEmpty = true + isInlineSuggestionPresent = false if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) { idleUi.inlineSuggestionsBar.clear() } @@ -363,7 +377,7 @@ class KawaiiBarComponent : UniqueViewComponent( fun handleInlineSuggestions(response: InlineSuggestionsResponse): Boolean { val suggestions = response.inlineSuggestions if (suggestions.isEmpty()) { - isInlineSuggestionEmpty = true + isInlineSuggestionPresent = false return true } var pinned: InlineSuggestion? = null @@ -393,7 +407,7 @@ class KawaiiBarComponent : UniqueViewComponent( }.awaitAll() idleUi.inlineSuggestionsBar.setScrollableViews(views) } - isInlineSuggestionEmpty = false + isInlineSuggestionPresent = true evalIdleUiState() return true } diff --git a/app/src/main/java/org/fcitx/fcitx5/android/input/bar/ui/IdleUi.kt b/app/src/main/java/org/fcitx/fcitx5/android/input/bar/ui/IdleUi.kt index 48f81fa7a..72499c3d1 100644 --- a/app/src/main/java/org/fcitx/fcitx5/android/input/bar/ui/IdleUi.kt +++ b/app/src/main/java/org/fcitx/fcitx5/android/input/bar/ui/IdleUi.kt @@ -169,8 +169,9 @@ class IdleUi( fun updateState(state: State, fromUser: Boolean = false) { Timber.d("Switch idle ui to $state") if ( + !fromUser || disableAnimation || - (state == State.InlineSuggestion || currentState == State.InlineSuggestion) && !fromUser + (state == State.InlineSuggestion || currentState == State.InlineSuggestion) ) { clearAnimation() } else { From b1f9d838262a713d7dbd4714143e45eb9309b35a Mon Sep 17 00:00:00 2001 From: Potato Hatsue <1793913507@qq.com> Date: Fri, 6 Oct 2023 05:03:36 -0400 Subject: [PATCH 034/381] Support creating symlinks in data hierarchy (#343) Co-authored-by: Rocka --- .../android/core/data/DataDescriptor.kt | 6 ++- .../fcitx5/android/core/data/DataHierarchy.kt | 45 +++++++++++++++---- .../fcitx5/android/core/data/DataManager.kt | 36 ++++++++------- .../fcitx5/android/core/data/FileAction.kt | 7 ++- .../fcitx/fcitx5/android/utils/FileUtil.kt | 32 +++++++++++++ .../src/main/kotlin/DataDescriptorPlugin.kt | 37 ++++++++++----- plugin/clipboard-filter/build.gradle.kts | 4 +- 7 files changed, 128 insertions(+), 39 deletions(-) create mode 100644 app/src/main/java/org/fcitx/fcitx5/android/utils/FileUtil.kt diff --git a/app/src/main/java/org/fcitx/fcitx5/android/core/data/DataDescriptor.kt b/app/src/main/java/org/fcitx/fcitx5/android/core/data/DataDescriptor.kt index 787b27dc4..d1100ad62 100644 --- a/app/src/main/java/org/fcitx/fcitx5/android/core/data/DataDescriptor.kt +++ b/app/src/main/java/org/fcitx/fcitx5/android/core/data/DataDescriptor.kt @@ -17,5 +17,9 @@ data class DataDescriptor( * path -> sha256 * sha256 will be empty if the path is a directory */ - val files: Map + val files: Map, + /** + * Symbolic links from target -> source + */ + val symlinks: Map = mapOf() ) \ No newline at end of file diff --git a/app/src/main/java/org/fcitx/fcitx5/android/core/data/DataHierarchy.kt b/app/src/main/java/org/fcitx/fcitx5/android/core/data/DataHierarchy.kt index 1acc7af1f..5996bea27 100644 --- a/app/src/main/java/org/fcitx/fcitx5/android/core/data/DataHierarchy.kt +++ b/app/src/main/java/org/fcitx/fcitx5/android/core/data/DataHierarchy.kt @@ -11,30 +11,43 @@ import java.security.MessageDigest class DataHierarchy { private val files = mutableMapOf>() private val descriptorSHA256 = mutableSetOf() - private val plugins = mutableSetOf() + private val symlinks = mutableMapOf>() - class Conflict(val path: String, val src: FileSource) : Exception() + data class PathConflict(val path: String, val src: FileSource) : Exception() + data class SymlinkConflict(val path: String, val src: FileSource) : Exception() /** * Merge a [DataDescriptor] * - * @throws Conflict if a non-directory path is existing in the file list + * @throws PathConflict if a non-directory path already exists in the hierarchy + * @throws SymlinkConflict if a file or directory already exists when creating symlink */ fun install(descriptor: DataDescriptor, src: FileSource) { val newFiles = descriptor.files.mapValues { (path, sha256) -> files[path]?.also { old -> // path conflict when at least one of them is not a directory (empty sha256) if (old.first.isNotEmpty() || sha256.isNotEmpty()) { - throw Conflict(path, old.second) + throw PathConflict(path, old.second) } } Pair(sha256, src) } // merge new files only when there is no conflict with existing files files.putAll(newFiles) - if (src is FileSource.Plugin) { - plugins += src.descriptor + val newSymlinks = descriptor.symlinks.mapValues { (path, source) -> + // path we try to create is already a file or directory in our hierarchy + files[path]?.let { (_, src) -> + throw SymlinkConflict(path, src) + } + // path we try to create is already a symlink in our hierarchy + // but it refers to a different path + symlinks[path]?.let { (existedSource, src) -> + if (source != existedSource) + throw PathConflict(path, src) + } + Pair(source, src) } + symlinks.putAll(newSymlinks) descriptorSHA256.add(descriptor.sha256) } @@ -42,7 +55,10 @@ class DataHierarchy { * Create a [DataDescriptor] from the file list, discarding other information */ fun downToDataDescriptor() = - DataDescriptor(sha256(this), files.mapValues { it.value.first }) + DataDescriptor( + sha256(this), + files.mapValues { it.value.first }, + symlinks.mapValues { it.value.first }) companion object { private val digest by lazy { MessageDigest.getInstance("SHA-256") } @@ -66,7 +82,7 @@ class DataHierarchy { fun diff(old: DataDescriptor, new: DataHierarchy): List { if (old.sha256 == sha256(new)) return emptyList() - return new.files.mapNotNull { (path, v) -> + val diffFiles = new.files.mapNotNull { (path, v) -> val (sha256, src) = v when { path !in old.files && sha256.isNotBlank() -> @@ -86,6 +102,19 @@ class DataHierarchy { FileAction.DeleteDir(path) }) } + val diffLinks = new.symlinks.mapNotNull { (target, v) -> + val (source, _) = v + if (old.symlinks[target] == source) + // old link will be overwritten + null + else + FileAction.CreateSymlink(target, source) + }.toMutableList().apply { + addAll(old.symlinks.filterKeys { it !in new.symlinks }.map { (target, _) -> + FileAction.DeleteFile(target) + }) + } + return diffFiles + diffLinks } } } \ No newline at end of file diff --git a/app/src/main/java/org/fcitx/fcitx5/android/core/data/DataManager.kt b/app/src/main/java/org/fcitx/fcitx5/android/core/data/DataManager.kt index 4fbe54dca..bf67fd918 100644 --- a/app/src/main/java/org/fcitx/fcitx5/android/core/data/DataManager.kt +++ b/app/src/main/java/org/fcitx/fcitx5/android/core/data/DataManager.kt @@ -10,6 +10,7 @@ import kotlinx.serialization.json.Json import org.fcitx.fcitx5.android.BuildConfig import org.fcitx.fcitx5.android.core.data.DataManager.dataDir import org.fcitx.fcitx5.android.utils.Const +import org.fcitx.fcitx5.android.utils.FileUtil import org.fcitx.fcitx5.android.utils.appContext import org.fcitx.fcitx5.android.utils.javaIdRegex import org.xmlpull.v1.XmlPullParser @@ -183,7 +184,7 @@ object DataManager { ?.getOrNull() ?.let { deserializeDataDescriptor(it) } ?.getOrNull() - ?: DataDescriptor("", mapOf()) + ?: DataDescriptor("", mapOf(), mapOf()) // load app's data descriptor val mainDescriptor = @@ -211,13 +212,18 @@ object DataManager { val pluginContext = appContext.createPackageContext(plugin.packageName, 0) val assets = pluginContext.assets val descriptor = runCatching { assets.getDataDescriptor() }.onFailure { - Timber.w("Failed to get or decode data descriptor of ${plugin.name}") + Timber.w("Failed to get or decode data descriptor of '${plugin.name}'") Timber.w(it) }.getOrNull() ?: continue try { newHierarchy.install(descriptor, FileSource.Plugin(plugin)) - } catch (e: DataHierarchy.Conflict) { - Timber.w("Path ${e.path} is already created by ${e.src}") + } catch (e: DataHierarchy.PathConflict) { + Timber.w("Path '${e.path}' has already been created by '${e.src}', cannot create file") + failedPlugins[plugin.packageName] = + PluginLoadFailed.PathConflict(plugin, e.path, e.src) + continue + } catch (e: DataHierarchy.SymlinkConflict) { + Timber.w("Path '${e.path}' has already been created by '${e.src}', cannot create symlink") failedPlugins[plugin.packageName] = PluginLoadFailed.PathConflict(plugin, e.path, e.src) continue @@ -241,10 +247,10 @@ object DataManager { assets.copyFile(it.path) } is FileAction.DeleteDir -> { - deleteDir(it.path) + removePath(it.path).getOrThrow() } is FileAction.DeleteFile -> { - deleteFile(it.path) + removePath(it.path).getOrThrow() } is FileAction.UpdateFile -> { val assets = if (it.src is FileSource.Plugin) @@ -252,6 +258,10 @@ object DataManager { else appContext.assets assets.copyFile(it.path) } + is FileAction.CreateSymlink -> { + removePath(it.path).getOrThrow() + symlink(it.src, it.path).getOrThrow() + } } } // save the new hierarchy as the data descriptor to be used in the next run @@ -272,17 +282,11 @@ object DataManager { Timber.d("Synced") } - private fun deleteFile(path: String) { - val file = File(dataDir, path) - if (file.exists() && file.isFile) - file.delete() - } + private fun removePath(path: String) = + FileUtil.removeFile(dataDir.resolve(path)) - private fun deleteDir(path: String) { - val dir = File(dataDir, path) - if (dir.exists() && dir.isDirectory) - dir.deleteRecursively() - } + private fun symlink(source: String, target: String) = + FileUtil.symlink(dataDir.resolve(source), dataDir.resolve(target)) private fun AssetManager.copyFile(filename: String) { open(filename).use { i -> diff --git a/app/src/main/java/org/fcitx/fcitx5/android/core/data/FileAction.kt b/app/src/main/java/org/fcitx/fcitx5/android/core/data/FileAction.kt index 2be5db1d1..76a8e1155 100644 --- a/app/src/main/java/org/fcitx/fcitx5/android/core/data/FileAction.kt +++ b/app/src/main/java/org/fcitx/fcitx5/android/core/data/FileAction.kt @@ -4,7 +4,7 @@ sealed interface FileAction { val path: String /** - * We want to create files first, then update files, and finally delete directories and files. + * We want to create files first, then update files, delete directories and files, and finally create symlinks */ val ordinal: Int @@ -15,6 +15,11 @@ sealed interface FileAction { val src: FileSource } + data class CreateSymlink(override val path: String, val src: String) : FileAction { + override val ordinal: Int + get() = -1 + } + data class CreateFile(override val path: String, override val src: FileSource) : FileAction, Sourced { diff --git a/app/src/main/java/org/fcitx/fcitx5/android/utils/FileUtil.kt b/app/src/main/java/org/fcitx/fcitx5/android/utils/FileUtil.kt new file mode 100644 index 000000000..ef1950284 --- /dev/null +++ b/app/src/main/java/org/fcitx/fcitx5/android/utils/FileUtil.kt @@ -0,0 +1,32 @@ +package org.fcitx.fcitx5.android.utils + +import android.system.Os +import android.system.OsConstants +import java.io.File +import java.io.IOException + +object FileUtil { + + /** + * Delete a [File]. + * If it's a directory, delete its contents first. + * If it's a symlink, don't follow. + */ + fun removeFile(file: File) = runCatching { + if (!file.exists()) + return Result.success(Unit) + val isSymlink = OsConstants.S_ISLNK(Os.lstat(file.path).st_mode) + // deleteRecursively follows symlink, so we want to make sure it's not a symlink + val result = if (!isSymlink) + file.deleteRecursively() + else + file.delete() + if (!result) + throw IOException("Cannot delete '${file.path}'") + } + + fun symlink(source: File, target: File) = runCatching { + target.parentFile?.mkdirs() + Os.symlink(source.path, target.path) + } +} \ No newline at end of file diff --git a/build-logic/convention/src/main/kotlin/DataDescriptorPlugin.kt b/build-logic/convention/src/main/kotlin/DataDescriptorPlugin.kt index d85c73ea6..df72927c0 100644 --- a/build-logic/convention/src/main/kotlin/DataDescriptorPlugin.kt +++ b/build-logic/convention/src/main/kotlin/DataDescriptorPlugin.kt @@ -7,9 +7,9 @@ import org.gradle.api.file.DirectoryProperty import org.gradle.api.file.RegularFileProperty import org.gradle.api.logging.LogLevel import org.gradle.api.provider.ListProperty +import org.gradle.api.provider.MapProperty import org.gradle.api.tasks.* import org.gradle.kotlin.dsl.create -import org.gradle.kotlin.dsl.getByName import org.gradle.kotlin.dsl.task import org.gradle.work.ChangeType import org.gradle.work.Incremental @@ -25,6 +25,12 @@ interface DataDescriptorPluginExtension { * paths relative to asset dir to be excluded */ val excludes: ListProperty + + /** + * symlinks to create after copying files + * target -> source + */ + val symlinks: MapProperty } /** @@ -41,9 +47,12 @@ class DataDescriptorPlugin : Plugin { override fun apply(target: Project) { val extension = target.extensions.create(TASK) extension.excludes.convention(listOf()) + extension.symlinks.convention(mapOf()) target.task(TASK) { inputDir.set(target.assetsDir) outputFile.set(target.assetsDir.resolve(FILE_NAME)) + excludes.set(extension.excludes) + symlinks.set(extension.symlinks) } target.task(CLEAN_TASK) { delete(target.assetsDir.resolve(FILE_NAME)) @@ -56,7 +65,8 @@ class DataDescriptorPlugin : Plugin { @Serializable data class DataDescriptor( val sha256: String, - val files: Map + val files: Map, + val symlinks: Map = mapOf() ) @get:Incremental @@ -64,24 +74,29 @@ class DataDescriptorPlugin : Plugin { @get:InputDirectory abstract val inputDir: DirectoryProperty + @get:Input + abstract val excludes: ListProperty + + @get:Input + abstract val symlinks: MapProperty + @get:OutputFile abstract val outputFile: RegularFileProperty private val file by lazy { outputFile.get().asFile } - private val excludes by lazy { - project.extensions.getByName(TASK).excludes.get() - } - private fun serialize(map: Map) { - file.deleteOnExit() + private fun serialize(files: Map, symlinks: Map) { + if (symlinks.keys.intersect(files.keys).isNotEmpty()) + throw IllegalArgumentException("Symlink target cannot be path in files") val descriptor = DataDescriptor( Hashing.sha256() .hashString( - map.entries.joinToString { it.key + it.value }, + (files + symlinks).entries.joinToString { it.key + it.value }, Charset.defaultCharset() ).toString(), - map + files, + symlinks ) file.writeText(json.encodeToString(descriptor)) } @@ -119,7 +134,7 @@ class DataDescriptorPlugin : Plugin { logger.log(LogLevel.DEBUG, "${change.changeType}: ${change.normalizedPath}") val relativeFile = change.file.relativeTo(file.parentFile) val key = relativeFile.path - if (change.changeType == ChangeType.REMOVED || key in excludes) { + if (change.changeType == ChangeType.REMOVED || key in excludes.get()) { map.remove(key) } else { map[key] = sha256(change.file) @@ -131,7 +146,7 @@ class DataDescriptorPlugin : Plugin { map[p.path] = "" } } - serialize(map.toSortedMap()) + serialize(map.toSortedMap(), symlinks.get()) } } } \ No newline at end of file diff --git a/plugin/clipboard-filter/build.gradle.kts b/plugin/clipboard-filter/build.gradle.kts index 75a3250aa..b0f480bf0 100644 --- a/plugin/clipboard-filter/build.gradle.kts +++ b/plugin/clipboard-filter/build.gradle.kts @@ -25,8 +25,8 @@ android { } } -configure { - excludes.set(listOf("data.minify.json")) +generateDataDescriptor{ + excludes.add("data.minify.json") } dependencies { From 2fed0f5d68cb8cebb7d0712facbf2eeb413b4d56 Mon Sep 17 00:00:00 2001 From: Rocka Date: Fri, 6 Oct 2023 18:14:28 +0800 Subject: [PATCH 035/381] Detect engine subMode update with StatusAreaEvent --- .../fcitx/fcitx5/android/input/InputView.kt | 34 +++++++++++++++++-- 1 file changed, 31 insertions(+), 3 deletions(-) diff --git a/app/src/main/java/org/fcitx/fcitx5/android/input/InputView.kt b/app/src/main/java/org/fcitx/fcitx5/android/input/InputView.kt index 0d9f532d4..be953986d 100644 --- a/app/src/main/java/org/fcitx/fcitx5/android/input/InputView.kt +++ b/app/src/main/java/org/fcitx/fcitx5/android/input/InputView.kt @@ -14,7 +14,10 @@ import android.widget.ImageView import androidx.annotation.Keep import androidx.annotation.RequiresApi import androidx.constraintlayout.widget.ConstraintLayout -import androidx.core.view.* +import androidx.core.view.ViewCompat +import androidx.core.view.WindowCompat +import androidx.core.view.WindowInsetsCompat +import androidx.core.view.updateLayoutParams import androidx.lifecycle.lifecycleScope import kotlinx.coroutines.Job import kotlinx.coroutines.launch @@ -48,8 +51,24 @@ import org.mechdancer.dependency.DynamicScope import org.mechdancer.dependency.manager.wrapToUniqueComponent import org.mechdancer.dependency.plusAssign import splitties.dimensions.dp -import splitties.views.dsl.constraintlayout.* -import splitties.views.dsl.core.* +import splitties.views.dsl.constraintlayout.above +import splitties.views.dsl.constraintlayout.below +import splitties.views.dsl.constraintlayout.bottomOfParent +import splitties.views.dsl.constraintlayout.centerHorizontally +import splitties.views.dsl.constraintlayout.centerVertically +import splitties.views.dsl.constraintlayout.constraintLayout +import splitties.views.dsl.constraintlayout.endOfParent +import splitties.views.dsl.constraintlayout.endToStartOf +import splitties.views.dsl.constraintlayout.lParams +import splitties.views.dsl.constraintlayout.startOfParent +import splitties.views.dsl.constraintlayout.startToEndOf +import splitties.views.dsl.constraintlayout.topOfParent +import splitties.views.dsl.core.add +import splitties.views.dsl.core.imageView +import splitties.views.dsl.core.matchParent +import splitties.views.dsl.core.view +import splitties.views.dsl.core.withTheme +import splitties.views.dsl.core.wrapContent import splitties.views.imageDrawable @SuppressLint("ViewConstructor") @@ -377,6 +396,15 @@ class InputView( broadcaster.onImeUpdate(it.data) } is FcitxEvent.StatusAreaEvent -> { + // Engine subMode update won't trigger IMChangeEvent, but usually updates StatusArea + fcitx.launchOnReady { + val ime = it.currentIme() + if (ime != it.inputMethodEntryCached) { + service.lifecycleScope.launch { + broadcaster.onImeUpdate(ime) + } + } + } punctuation.updatePunctuationMapping(it.data) broadcaster.onStatusAreaUpdate(it.data) } From 67aa5c2c745e3bf9f65a2fe10a7e5e966df088d0 Mon Sep 17 00:00:00 2001 From: Qijia Liu Date: Fri, 6 Oct 2023 08:35:22 -0700 Subject: [PATCH 036/381] Add RIME plugin (#342) Co-authored-by: Rocka --- .gitmodules | 15 +++++ app/licenses/libraries/boost.json | 2 +- lib/fcitx5/src/main/cpp/prebuilt | 2 +- plugin/rime/build.gradle.kts | 57 +++++++++++++++++ plugin/rime/licenses/libraries/boost.json | 11 ++++ .../rime/licenses/libraries/fcitx5-rime.json | 11 ++++ plugin/rime/licenses/libraries/glog.json | 11 ++++ plugin/rime/licenses/libraries/leveldb.json | 11 ++++ .../rime/licenses/libraries/librime-lua.json | 11 ++++ .../licenses/libraries/librime-octagram.json | 11 ++++ plugin/rime/licenses/libraries/librime.json | 11 ++++ plugin/rime/licenses/libraries/marisa.json | 12 ++++ plugin/rime/licenses/libraries/opencc.json | 11 ++++ .../rime/licenses/libraries/rime-essay.json | 11 ++++ .../licenses/libraries/rime-luna-pinyin.json | 11 ++++ .../rime/licenses/libraries/rime-prelude.json | 11 ++++ .../rime/licenses/libraries/rime-stroke.json | 11 ++++ plugin/rime/licenses/libraries/yaml-cpp.json | 11 ++++ plugin/rime/src/main/AndroidManifest.xml | 6 ++ .../assets/usr/share/rime-data/default.yaml | 1 + .../main/assets/usr/share/rime-data/essay.txt | 1 + .../usr/share/rime-data/key_bindings.yaml | 1 + .../usr/share/rime-data/luna_pinyin.dict.yaml | 1 + .../share/rime-data/luna_pinyin.schema.yaml | 1 + .../rime-data/luna_pinyin_fluency.schema.yaml | 1 + .../rime-data/luna_pinyin_simp.schema.yaml | 1 + .../rime-data/luna_pinyin_tw.schema.yaml | 1 + .../share/rime-data/luna_quanpin.schema.yaml | 1 + .../assets/usr/share/rime-data/pinyin.yaml | 1 + .../usr/share/rime-data/punctuation.yaml | 1 + .../usr/share/rime-data/stroke.dict.yaml | 1 + .../usr/share/rime-data/stroke.schema.yaml | 1 + .../assets/usr/share/rime-data/symbols.yaml | 1 + plugin/rime/src/main/cpp/CMakeLists.txt | 39 ++++++++++++ plugin/rime/src/main/cpp/default.yaml | 63 +++++++++++++++++++ plugin/rime/src/main/cpp/fcitx5-rime | 1 + plugin/rime/src/main/cpp/rime-essay | 1 + plugin/rime/src/main/cpp/rime-luna-pinyin | 1 + plugin/rime/src/main/cpp/rime-prelude | 1 + plugin/rime/src/main/cpp/rime-stroke | 1 + plugin/rime/src/main/res/values/strings.xml | 6 ++ plugin/rime/src/main/res/xml/plugin.xml | 6 ++ settings.gradle.kts | 1 + 43 files changed, 369 insertions(+), 2 deletions(-) create mode 100644 plugin/rime/build.gradle.kts create mode 100644 plugin/rime/licenses/libraries/boost.json create mode 100644 plugin/rime/licenses/libraries/fcitx5-rime.json create mode 100644 plugin/rime/licenses/libraries/glog.json create mode 100644 plugin/rime/licenses/libraries/leveldb.json create mode 100644 plugin/rime/licenses/libraries/librime-lua.json create mode 100644 plugin/rime/licenses/libraries/librime-octagram.json create mode 100644 plugin/rime/licenses/libraries/librime.json create mode 100644 plugin/rime/licenses/libraries/marisa.json create mode 100644 plugin/rime/licenses/libraries/opencc.json create mode 100644 plugin/rime/licenses/libraries/rime-essay.json create mode 100644 plugin/rime/licenses/libraries/rime-luna-pinyin.json create mode 100644 plugin/rime/licenses/libraries/rime-prelude.json create mode 100644 plugin/rime/licenses/libraries/rime-stroke.json create mode 100644 plugin/rime/licenses/libraries/yaml-cpp.json create mode 100644 plugin/rime/src/main/AndroidManifest.xml create mode 120000 plugin/rime/src/main/assets/usr/share/rime-data/default.yaml create mode 120000 plugin/rime/src/main/assets/usr/share/rime-data/essay.txt create mode 120000 plugin/rime/src/main/assets/usr/share/rime-data/key_bindings.yaml create mode 120000 plugin/rime/src/main/assets/usr/share/rime-data/luna_pinyin.dict.yaml create mode 120000 plugin/rime/src/main/assets/usr/share/rime-data/luna_pinyin.schema.yaml create mode 120000 plugin/rime/src/main/assets/usr/share/rime-data/luna_pinyin_fluency.schema.yaml create mode 120000 plugin/rime/src/main/assets/usr/share/rime-data/luna_pinyin_simp.schema.yaml create mode 120000 plugin/rime/src/main/assets/usr/share/rime-data/luna_pinyin_tw.schema.yaml create mode 120000 plugin/rime/src/main/assets/usr/share/rime-data/luna_quanpin.schema.yaml create mode 120000 plugin/rime/src/main/assets/usr/share/rime-data/pinyin.yaml create mode 120000 plugin/rime/src/main/assets/usr/share/rime-data/punctuation.yaml create mode 120000 plugin/rime/src/main/assets/usr/share/rime-data/stroke.dict.yaml create mode 120000 plugin/rime/src/main/assets/usr/share/rime-data/stroke.schema.yaml create mode 120000 plugin/rime/src/main/assets/usr/share/rime-data/symbols.yaml create mode 100644 plugin/rime/src/main/cpp/CMakeLists.txt create mode 100644 plugin/rime/src/main/cpp/default.yaml create mode 160000 plugin/rime/src/main/cpp/fcitx5-rime create mode 160000 plugin/rime/src/main/cpp/rime-essay create mode 160000 plugin/rime/src/main/cpp/rime-luna-pinyin create mode 160000 plugin/rime/src/main/cpp/rime-prelude create mode 160000 plugin/rime/src/main/cpp/rime-stroke create mode 100644 plugin/rime/src/main/res/values/strings.xml create mode 100644 plugin/rime/src/main/res/xml/plugin.xml diff --git a/.gitmodules b/.gitmodules index 3517f1560..19cffebd2 100644 --- a/.gitmodules +++ b/.gitmodules @@ -22,3 +22,18 @@ [submodule "plugin/unikey/src/main/cpp/fcitx5-unikey"] path = plugin/unikey/src/main/cpp/fcitx5-unikey url = git@github.com:fcitx/fcitx5-unikey.git +[submodule "plugin/rime/src/main/cpp/fcitx5-rime"] + path = plugin/rime/src/main/cpp/fcitx5-rime + url = git@github.com:fcitx/fcitx5-rime.git +[submodule "plugin/rime/src/main/cpp/rime-prelude"] + path = plugin/rime/src/main/cpp/rime-prelude + url = git@github.com:rime/rime-prelude.git +[submodule "plugin/rime/src/main/cpp/rime-essay"] + path = plugin/rime/src/main/cpp/rime-essay + url = git@github.com:rime/rime-essay.git +[submodule "plugin/rime/src/main/cpp/rime-luna-pinyin"] + path = plugin/rime/src/main/cpp/rime-luna-pinyin + url = git@github.com:rime/rime-luna-pinyin.git +[submodule "plugin/rime/src/main/cpp/rime-stroke"] + path = plugin/rime/src/main/cpp/rime-stroke + url = git@github.com:rime/rime-stroke.git diff --git a/app/licenses/libraries/boost.json b/app/licenses/libraries/boost.json index 7848e8ef2..7a2285fb6 100644 --- a/app/licenses/libraries/boost.json +++ b/app/licenses/libraries/boost.json @@ -1,6 +1,6 @@ { "uniqueId": "boostorg/boost", - "artifactVersion": "1.80.0", + "artifactVersion": "1.83.0", "description": "Free peer-reviewed portable C++ source libraries", "name": "boostorg/boost", "website": "https://www.boost.org/", diff --git a/lib/fcitx5/src/main/cpp/prebuilt b/lib/fcitx5/src/main/cpp/prebuilt index 1e5240b60..fd3e6c4c1 160000 --- a/lib/fcitx5/src/main/cpp/prebuilt +++ b/lib/fcitx5/src/main/cpp/prebuilt @@ -1 +1 @@ -Subproject commit 1e5240b605042f689463b957ec2a8fcbf58ab445 +Subproject commit fd3e6c4c17499621323f86c631f32948b5c28a10 diff --git a/plugin/rime/build.gradle.kts b/plugin/rime/build.gradle.kts new file mode 100644 index 000000000..55bfc0ad3 --- /dev/null +++ b/plugin/rime/build.gradle.kts @@ -0,0 +1,57 @@ +@file:Suppress("UnstableApiUsage") + +plugins { + id("org.fcitx.fcitx5.android.app-convention") + id("org.fcitx.fcitx5.android.plugin-app-convention") + id("org.fcitx.fcitx5.android.native-app-convention") + id("org.fcitx.fcitx5.android.build-metadata") + id("org.fcitx.fcitx5.android.data-descriptor") + id("org.fcitx.fcitx5.android.fcitx-component") +} + +android { + namespace = "org.fcitx.fcitx5.android.plugin.rime" + + defaultConfig { + applicationId = "org.fcitx.fcitx5.android.plugin.rime" + + externalNativeBuild { + cmake { + targets( + "rime" + ) + } + } + } + + buildTypes { + release { + resValue("string", "app_name", "@string/app_name_release") + } + debug { + resValue("string", "app_name", "@string/app_name_debug") + } + } + + packaging { + jniLibs { + excludes += setOf( + "**/libc++_shared.so", + "**/libFcitx5*" + ) + } + } +} + +generateDataDescriptor { + symlinks.put("usr/share/rime-data/opencc", "usr/share/opencc") +} + +aboutLibraries { + configPath = "plugin/rime/licenses" +} + +dependencies { + implementation(project(":lib:fcitx5")) + implementation(project(":lib:plugin-base")) +} diff --git a/plugin/rime/licenses/libraries/boost.json b/plugin/rime/licenses/libraries/boost.json new file mode 100644 index 000000000..7a2285fb6 --- /dev/null +++ b/plugin/rime/licenses/libraries/boost.json @@ -0,0 +1,11 @@ +{ + "uniqueId": "boostorg/boost", + "artifactVersion": "1.83.0", + "description": "Free peer-reviewed portable C++ source libraries", + "name": "boostorg/boost", + "website": "https://www.boost.org/", + "tag": "native", + "licenses": [ + "BSL-1.0" + ] +} diff --git a/plugin/rime/licenses/libraries/fcitx5-rime.json b/plugin/rime/licenses/libraries/fcitx5-rime.json new file mode 100644 index 000000000..19d0c1215 --- /dev/null +++ b/plugin/rime/licenses/libraries/fcitx5-rime.json @@ -0,0 +1,11 @@ +{ + "uniqueId": "fcitx/fcitx5-rime", + "artifactVersion": "5.1.2", + "description": "Rime Wrapper for Fcitx", + "name": "fcitx/fcitx5-rime", + "website": "https://github.com/fcitx/fcitx5-rime", + "tag": "native", + "licenses": [ + "LGPL-2.1-or-later" + ] +} diff --git a/plugin/rime/licenses/libraries/glog.json b/plugin/rime/licenses/libraries/glog.json new file mode 100644 index 000000000..7dce86a96 --- /dev/null +++ b/plugin/rime/licenses/libraries/glog.json @@ -0,0 +1,11 @@ +{ + "uniqueId": "google/glog", + "artifactVersion": "0.6.0", + "description": "C++ implementation of the Google logging module", + "name": "google/glog", + "website": "https://github.com/google/glog", + "tag": "native", + "licenses": [ + "BSD-3-Clause" + ] +} diff --git a/plugin/rime/licenses/libraries/leveldb.json b/plugin/rime/licenses/libraries/leveldb.json new file mode 100644 index 000000000..efcc5d573 --- /dev/null +++ b/plugin/rime/licenses/libraries/leveldb.json @@ -0,0 +1,11 @@ +{ + "uniqueId": "google/leveldb", + "artifactVersion": "1.23", + "description": "A fast key-value storage library written at Google that provides an ordered mapping from string keys to string values", + "name": "google/leveldb", + "website": "https://github.com/google/leveldb", + "tag": "native", + "licenses": [ + "BSD-3-Clause" + ] +} diff --git a/plugin/rime/licenses/libraries/librime-lua.json b/plugin/rime/licenses/libraries/librime-lua.json new file mode 100644 index 000000000..1e63727a7 --- /dev/null +++ b/plugin/rime/licenses/libraries/librime-lua.json @@ -0,0 +1,11 @@ +{ + "uniqueId": "hchunhui/librime-lua", + "artifactVersion": "5671021", + "description": "Extending RIME with Lua scripts", + "name": "hchunhui/librime-lua", + "website": "https://github.com/hchunhui/librime-lua", + "tag": "native", + "licenses": [ + "BSD-3-Clause" + ] +} diff --git a/plugin/rime/licenses/libraries/librime-octagram.json b/plugin/rime/licenses/libraries/librime-octagram.json new file mode 100644 index 000000000..d57b0fce8 --- /dev/null +++ b/plugin/rime/licenses/libraries/librime-octagram.json @@ -0,0 +1,11 @@ +{ + "uniqueId": "lotem/librime-octagram", + "artifactVersion": "a6ced5a", + "description": "RIME octagram plugin", + "name": "lotem/librime-octagram", + "website": "https://github.com/lotem/librime-octagram", + "tag": "native", + "licenses": [ + "GPL-3.0-only" + ] +} diff --git a/plugin/rime/licenses/libraries/librime.json b/plugin/rime/licenses/libraries/librime.json new file mode 100644 index 000000000..284895459 --- /dev/null +++ b/plugin/rime/licenses/libraries/librime.json @@ -0,0 +1,11 @@ +{ + "uniqueId": "rime/librime", + "artifactVersion": "1.9.0", + "description": "Rime Input Method Engine", + "name": "rime/librime", + "website": "https://github.com/rime/librime", + "tag": "native", + "licenses": [ + "BSD-3-Clause" + ] +} diff --git a/plugin/rime/licenses/libraries/marisa.json b/plugin/rime/licenses/libraries/marisa.json new file mode 100644 index 000000000..b570ebd03 --- /dev/null +++ b/plugin/rime/licenses/libraries/marisa.json @@ -0,0 +1,12 @@ +{ + "uniqueId": "rime/marisa-trie", + "artifactVersion": "0.2.6", + "description": "Matching Algorithm with Recursively Implemented StorAge", + "name": "rime/marisa-trie", + "website": "https://github.com/rime/marisa-trie", + "tag": "native", + "licenses": [ + "BSD-2-Clause", + "LGPL-2.1-or-later" + ] +} diff --git a/plugin/rime/licenses/libraries/opencc.json b/plugin/rime/licenses/libraries/opencc.json new file mode 100644 index 000000000..d1d58905d --- /dev/null +++ b/plugin/rime/licenses/libraries/opencc.json @@ -0,0 +1,11 @@ +{ + "uniqueId": "BYVoid/OpenCC", + "artifactVersion": "1.1.6", + "description": "opensource project for conversions between Traditional Chinese, Simplified Chinese and Japanese Kanji (Shinjitai).", + "name": "BYVoid/OpenCC", + "website": "https://opencc.byvoid.com/", + "tag": "native", + "licenses": [ + "Apache-2.0" + ] +} diff --git a/plugin/rime/licenses/libraries/rime-essay.json b/plugin/rime/licenses/libraries/rime-essay.json new file mode 100644 index 000000000..16fa51efd --- /dev/null +++ b/plugin/rime/licenses/libraries/rime-essay.json @@ -0,0 +1,11 @@ +{ + "uniqueId": "rime/rime-essay", + "artifactVersion": "e0519d0", + "description": "The shared vocabulary and language model", + "name": "rime/rime-essay", + "website": "https://github.com/rime/rime-essay", + "tag": "native", + "licenses": [ + "LGPL-3.0-only" + ] +} diff --git a/plugin/rime/licenses/libraries/rime-luna-pinyin.json b/plugin/rime/licenses/libraries/rime-luna-pinyin.json new file mode 100644 index 000000000..9c06d4187 --- /dev/null +++ b/plugin/rime/licenses/libraries/rime-luna-pinyin.json @@ -0,0 +1,11 @@ +{ + "uniqueId": "rime/rime-luna-pinyin", + "artifactVersion": "79aeae2", + "description": "Rime 預設的拼音輸入方案", + "name": "rime/rime-luna-pinyin", + "website": "https://github.com/rime/rime-luna-pinyin", + "tag": "native", + "licenses": [ + "LGPL-3.0-or-later" + ] +} diff --git a/plugin/rime/licenses/libraries/rime-prelude.json b/plugin/rime/licenses/libraries/rime-prelude.json new file mode 100644 index 000000000..d20c98aa8 --- /dev/null +++ b/plugin/rime/licenses/libraries/rime-prelude.json @@ -0,0 +1,11 @@ +{ + "uniqueId": "rime/rime-prelude", + "artifactVersion": "dd84abe", + "description": "Essential files for building up your Rime configuration", + "name": "rime/rime-prelude", + "website": "https://github.com/rime/rime-prelude", + "tag": "native", + "licenses": [ + "LGPL-3.0-only" + ] +} diff --git a/plugin/rime/licenses/libraries/rime-stroke.json b/plugin/rime/licenses/libraries/rime-stroke.json new file mode 100644 index 000000000..c69396af8 --- /dev/null +++ b/plugin/rime/licenses/libraries/rime-stroke.json @@ -0,0 +1,11 @@ +{ + "uniqueId": "rime/rime-stroke", + "artifactVersion": "e6c7608", + "description": "Rime 五筆畫輸入方案", + "name": "rime/rime-stroke", + "website": "https://github.com/rime/rime-stroke", + "tag": "native", + "licenses": [ + "LGPL-3.0-or-later" + ] +} diff --git a/plugin/rime/licenses/libraries/yaml-cpp.json b/plugin/rime/licenses/libraries/yaml-cpp.json new file mode 100644 index 000000000..78073e81a --- /dev/null +++ b/plugin/rime/licenses/libraries/yaml-cpp.json @@ -0,0 +1,11 @@ +{ + "uniqueId": "jbeder/yaml-cpp", + "artifactVersion": "0.8.0", + "description": "A YAML parser and emitter in C++", + "name": "jbeder/yaml-cpp", + "website": "https://github.com/jbeder/yaml-cpp", + "tag": "native", + "licenses": [ + "MIT" + ] +} diff --git a/plugin/rime/src/main/AndroidManifest.xml b/plugin/rime/src/main/AndroidManifest.xml new file mode 100644 index 000000000..654a95e5f --- /dev/null +++ b/plugin/rime/src/main/AndroidManifest.xml @@ -0,0 +1,6 @@ + + + + + + diff --git a/plugin/rime/src/main/assets/usr/share/rime-data/default.yaml b/plugin/rime/src/main/assets/usr/share/rime-data/default.yaml new file mode 120000 index 000000000..8da0b4f20 --- /dev/null +++ b/plugin/rime/src/main/assets/usr/share/rime-data/default.yaml @@ -0,0 +1 @@ +../../../../cpp/default.yaml \ No newline at end of file diff --git a/plugin/rime/src/main/assets/usr/share/rime-data/essay.txt b/plugin/rime/src/main/assets/usr/share/rime-data/essay.txt new file mode 120000 index 000000000..66bb52008 --- /dev/null +++ b/plugin/rime/src/main/assets/usr/share/rime-data/essay.txt @@ -0,0 +1 @@ +../../../../cpp/rime-essay/essay.txt \ No newline at end of file diff --git a/plugin/rime/src/main/assets/usr/share/rime-data/key_bindings.yaml b/plugin/rime/src/main/assets/usr/share/rime-data/key_bindings.yaml new file mode 120000 index 000000000..5da2132a0 --- /dev/null +++ b/plugin/rime/src/main/assets/usr/share/rime-data/key_bindings.yaml @@ -0,0 +1 @@ +../../../../cpp/rime-prelude/key_bindings.yaml \ No newline at end of file diff --git a/plugin/rime/src/main/assets/usr/share/rime-data/luna_pinyin.dict.yaml b/plugin/rime/src/main/assets/usr/share/rime-data/luna_pinyin.dict.yaml new file mode 120000 index 000000000..e1de1d90a --- /dev/null +++ b/plugin/rime/src/main/assets/usr/share/rime-data/luna_pinyin.dict.yaml @@ -0,0 +1 @@ +../../../../cpp/rime-luna-pinyin/luna_pinyin.dict.yaml \ No newline at end of file diff --git a/plugin/rime/src/main/assets/usr/share/rime-data/luna_pinyin.schema.yaml b/plugin/rime/src/main/assets/usr/share/rime-data/luna_pinyin.schema.yaml new file mode 120000 index 000000000..53c284a62 --- /dev/null +++ b/plugin/rime/src/main/assets/usr/share/rime-data/luna_pinyin.schema.yaml @@ -0,0 +1 @@ +../../../../cpp/rime-luna-pinyin/luna_pinyin.schema.yaml \ No newline at end of file diff --git a/plugin/rime/src/main/assets/usr/share/rime-data/luna_pinyin_fluency.schema.yaml b/plugin/rime/src/main/assets/usr/share/rime-data/luna_pinyin_fluency.schema.yaml new file mode 120000 index 000000000..da7388c9f --- /dev/null +++ b/plugin/rime/src/main/assets/usr/share/rime-data/luna_pinyin_fluency.schema.yaml @@ -0,0 +1 @@ +../../../../cpp/rime-luna-pinyin/luna_pinyin_fluency.schema.yaml \ No newline at end of file diff --git a/plugin/rime/src/main/assets/usr/share/rime-data/luna_pinyin_simp.schema.yaml b/plugin/rime/src/main/assets/usr/share/rime-data/luna_pinyin_simp.schema.yaml new file mode 120000 index 000000000..5255c282e --- /dev/null +++ b/plugin/rime/src/main/assets/usr/share/rime-data/luna_pinyin_simp.schema.yaml @@ -0,0 +1 @@ +../../../../cpp/rime-luna-pinyin/luna_pinyin_simp.schema.yaml \ No newline at end of file diff --git a/plugin/rime/src/main/assets/usr/share/rime-data/luna_pinyin_tw.schema.yaml b/plugin/rime/src/main/assets/usr/share/rime-data/luna_pinyin_tw.schema.yaml new file mode 120000 index 000000000..fd4d43b4c --- /dev/null +++ b/plugin/rime/src/main/assets/usr/share/rime-data/luna_pinyin_tw.schema.yaml @@ -0,0 +1 @@ +../../../../cpp/rime-luna-pinyin/luna_pinyin_tw.schema.yaml \ No newline at end of file diff --git a/plugin/rime/src/main/assets/usr/share/rime-data/luna_quanpin.schema.yaml b/plugin/rime/src/main/assets/usr/share/rime-data/luna_quanpin.schema.yaml new file mode 120000 index 000000000..348536a82 --- /dev/null +++ b/plugin/rime/src/main/assets/usr/share/rime-data/luna_quanpin.schema.yaml @@ -0,0 +1 @@ +../../../../cpp/rime-luna-pinyin/luna_quanpin.schema.yaml \ No newline at end of file diff --git a/plugin/rime/src/main/assets/usr/share/rime-data/pinyin.yaml b/plugin/rime/src/main/assets/usr/share/rime-data/pinyin.yaml new file mode 120000 index 000000000..b5e39c03e --- /dev/null +++ b/plugin/rime/src/main/assets/usr/share/rime-data/pinyin.yaml @@ -0,0 +1 @@ +../../../../cpp/rime-luna-pinyin/pinyin.yaml \ No newline at end of file diff --git a/plugin/rime/src/main/assets/usr/share/rime-data/punctuation.yaml b/plugin/rime/src/main/assets/usr/share/rime-data/punctuation.yaml new file mode 120000 index 000000000..62fd6b63d --- /dev/null +++ b/plugin/rime/src/main/assets/usr/share/rime-data/punctuation.yaml @@ -0,0 +1 @@ +../../../../cpp/rime-prelude/punctuation.yaml \ No newline at end of file diff --git a/plugin/rime/src/main/assets/usr/share/rime-data/stroke.dict.yaml b/plugin/rime/src/main/assets/usr/share/rime-data/stroke.dict.yaml new file mode 120000 index 000000000..ea7fadd53 --- /dev/null +++ b/plugin/rime/src/main/assets/usr/share/rime-data/stroke.dict.yaml @@ -0,0 +1 @@ +../../../../cpp/rime-stroke/stroke.dict.yaml \ No newline at end of file diff --git a/plugin/rime/src/main/assets/usr/share/rime-data/stroke.schema.yaml b/plugin/rime/src/main/assets/usr/share/rime-data/stroke.schema.yaml new file mode 120000 index 000000000..b2a8ad3eb --- /dev/null +++ b/plugin/rime/src/main/assets/usr/share/rime-data/stroke.schema.yaml @@ -0,0 +1 @@ +../../../../cpp/rime-stroke/stroke.schema.yaml \ No newline at end of file diff --git a/plugin/rime/src/main/assets/usr/share/rime-data/symbols.yaml b/plugin/rime/src/main/assets/usr/share/rime-data/symbols.yaml new file mode 120000 index 000000000..02eb06d51 --- /dev/null +++ b/plugin/rime/src/main/assets/usr/share/rime-data/symbols.yaml @@ -0,0 +1 @@ +../../../../cpp/rime-prelude/symbols.yaml \ No newline at end of file diff --git a/plugin/rime/src/main/cpp/CMakeLists.txt b/plugin/rime/src/main/cpp/CMakeLists.txt new file mode 100644 index 000000000..714859c91 --- /dev/null +++ b/plugin/rime/src/main/cpp/CMakeLists.txt @@ -0,0 +1,39 @@ +cmake_minimum_required(VERSION 3.18) + +project(fcitx5-android-plugin-rime VERSION 0.0.7) + +# For reproducible build +add_link_options("LINKER:--hash-style=gnu,--build-id=none") + +# prefab dependency +find_package(fcitx5 REQUIRED CONFIG) +get_target_property(FCITX5_CMAKE_MODULES fcitx5::cmake INTERFACE_INCLUDE_DIRECTORIES) +set(CMAKE_MODULE_PATH ${FCITX5_CMAKE_MODULES} ${CMAKE_MODULE_PATH}) + +find_package(ECM MODULE) +find_package(Fcitx5Core MODULE) +find_package(Fcitx5Module MODULE) + +set(PREBUILT_DIR "${CMAKE_CURRENT_SOURCE_DIR}/../../../../../lib/fcitx5/src/main/cpp/prebuilt") + +set(Rime_VERSION "1.9.0") +set(Rime_dependencies + "${PREBUILT_DIR}/boost/${ANDROID_ABI}/lib/libboost_filesystem.a" + log # glog links to android log + "${PREBUILT_DIR}/glog/${ANDROID_ABI}/lib/libglog.a" + "${PREBUILT_DIR}/leveldb/${ANDROID_ABI}/lib/libleveldb.a" + "${PREBUILT_DIR}/lua/${ANDROID_ABI}/lib/liblua_static.a" + "${PREBUILT_DIR}/marisa/${ANDROID_ABI}/lib/libmarisa.a" + "${PREBUILT_DIR}/opencc/${ANDROID_ABI}/lib/libopencc.a" + "${PREBUILT_DIR}/yaml-cpp/${ANDROID_ABI}/lib/libyaml-cpp.a" + ) +add_library(Rime_static STATIC IMPORTED) +set_target_properties(Rime_static PROPERTIES + IMPORTED_LOCATION "${PREBUILT_DIR}/librime/${ANDROID_ABI}/lib/librime.a" + INTERFACE_INCLUDE_DIRECTORIES "${PREBUILT_DIR}/librime/${ANDROID_ABI}/include" + INTERFACE_LINK_LIBRARIES "${Rime_dependencies}" + ) + +set(RIME_TARGET Rime_static) +set(RIME_DATA_DIR "${CMAKE_INSTALL_DATADIR}/rime-data") +add_subdirectory(fcitx5-rime) diff --git a/plugin/rime/src/main/cpp/default.yaml b/plugin/rime/src/main/cpp/default.yaml new file mode 100644 index 000000000..e497426bc --- /dev/null +++ b/plugin/rime/src/main/cpp/default.yaml @@ -0,0 +1,63 @@ +# Rime default settings +# encoding: utf-8 + +config_version: '0.40' + +schema_list: + - schema: luna_pinyin + - schema: luna_pinyin_tw + - schema: luna_pinyin_simp + - schema: luna_pinyin_fluency + - schema: stroke + +switcher: + caption: 〔方案選單〕 + hotkeys: + - Control+grave + - Control+Shift+grave + - F4 + save_options: + - full_shape + - ascii_punct + - simplification + - extended_charset + - zh_hant + - zh_hans + - zh_hant_tw + fold_options: true + abbreviate_options: true + option_list_separator: '/' + +menu: + page_size: 5 + +punctuator: + full_shape: + __include: punctuation:/full_shape + half_shape: + __include: punctuation:/half_shape + +key_binder: + bindings: + __patch: + - key_bindings:/emacs_editing + - key_bindings:/move_by_word_with_tab + - key_bindings:/paging_with_minus_equal + - key_bindings:/paging_with_comma_period + - key_bindings:/numbered_mode_switch + +recognizer: + patterns: + email: "^[A-Za-z][-_.0-9A-Za-z]*@.*$" + uppercase: "[A-Z][-_+.'0-9A-Za-z]*$" + url: "^(www[.]|https?:|ftp[.:]|mailto:|file:).*$|^[a-z]+[.].+$" + +ascii_composer: + good_old_caps_lock: true + switch_key: + Shift_L: inline_ascii + Shift_R: commit_text + Control_L: noop + Control_R: noop + Caps_Lock: clear + Eisu_toggle: clear diff --git a/plugin/rime/src/main/cpp/fcitx5-rime b/plugin/rime/src/main/cpp/fcitx5-rime new file mode 160000 index 000000000..7568f6736 --- /dev/null +++ b/plugin/rime/src/main/cpp/fcitx5-rime @@ -0,0 +1 @@ +Subproject commit 7568f67362cfbad17653694c123f439f2ff69192 diff --git a/plugin/rime/src/main/cpp/rime-essay b/plugin/rime/src/main/cpp/rime-essay new file mode 160000 index 000000000..e0519d057 --- /dev/null +++ b/plugin/rime/src/main/cpp/rime-essay @@ -0,0 +1 @@ +Subproject commit e0519d0579722a0871efb68189272cba61a7350b diff --git a/plugin/rime/src/main/cpp/rime-luna-pinyin b/plugin/rime/src/main/cpp/rime-luna-pinyin new file mode 160000 index 000000000..79aeae200 --- /dev/null +++ b/plugin/rime/src/main/cpp/rime-luna-pinyin @@ -0,0 +1 @@ +Subproject commit 79aeae200a7370720be98232844c0715f277e1c0 diff --git a/plugin/rime/src/main/cpp/rime-prelude b/plugin/rime/src/main/cpp/rime-prelude new file mode 160000 index 000000000..dd84abecc --- /dev/null +++ b/plugin/rime/src/main/cpp/rime-prelude @@ -0,0 +1 @@ +Subproject commit dd84abecc33f0b05469f1d744e32d2b60b3529e3 diff --git a/plugin/rime/src/main/cpp/rime-stroke b/plugin/rime/src/main/cpp/rime-stroke new file mode 160000 index 000000000..e6c760892 --- /dev/null +++ b/plugin/rime/src/main/cpp/rime-stroke @@ -0,0 +1 @@ +Subproject commit e6c7608925009636577ff7469eecc870f1de18f3 diff --git a/plugin/rime/src/main/res/values/strings.xml b/plugin/rime/src/main/res/values/strings.xml new file mode 100644 index 000000000..372e219ee --- /dev/null +++ b/plugin/rime/src/main/res/values/strings.xml @@ -0,0 +1,6 @@ + + + Fcitx5 for Android (RIME Plugin | Debug) + Fcitx5 for Android (RIME Plugin) + RIME (Rime Input Method Engine) support for Fcitx5 + \ No newline at end of file diff --git a/plugin/rime/src/main/res/xml/plugin.xml b/plugin/rime/src/main/res/xml/plugin.xml new file mode 100644 index 000000000..a4aad4c3c --- /dev/null +++ b/plugin/rime/src/main/res/xml/plugin.xml @@ -0,0 +1,6 @@ + + + 0.1 + fcitx5-rime + @string/description + \ No newline at end of file diff --git a/settings.gradle.kts b/settings.gradle.kts index 5b28fe259..8a7b38fb6 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -30,3 +30,4 @@ include(":lib:plugin-base") include(":plugin:anthy") include(":plugin:clipboard-filter") include(":plugin:unikey") +include(":plugin:rime") From a4d4eb8e8d2098c65df86428635060a3fa4a147a Mon Sep 17 00:00:00 2001 From: Rocka Date: Sat, 7 Oct 2023 13:56:35 +0800 Subject: [PATCH 037/381] Use BreakIterator to extract textIcon character --- .../android/input/status/StatusAreaEntryUi.kt | 21 ++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) diff --git a/app/src/main/java/org/fcitx/fcitx5/android/input/status/StatusAreaEntryUi.kt b/app/src/main/java/org/fcitx/fcitx5/android/input/status/StatusAreaEntryUi.kt index ed94945f1..07281edc5 100644 --- a/app/src/main/java/org/fcitx/fcitx5/android/input/status/StatusAreaEntryUi.kt +++ b/app/src/main/java/org/fcitx/fcitx5/android/input/status/StatusAreaEntryUi.kt @@ -4,12 +4,14 @@ import android.content.Context import android.graphics.Typeface import android.graphics.drawable.ShapeDrawable import android.graphics.drawable.shapes.OvalShape +import android.icu.text.BreakIterator import android.os.Build import android.util.TypedValue import android.view.View import android.view.ViewGroup import android.widget.ImageView import org.fcitx.fcitx5.android.data.theme.Theme +import org.fcitx.fcitx5.android.input.AutoScaleTextView import org.fcitx.fcitx5.android.input.keyboard.CustomGestureView import splitties.dimensions.dp import splitties.resources.drawable @@ -27,6 +29,7 @@ import splitties.views.dsl.core.imageView import splitties.views.dsl.core.lParams import splitties.views.dsl.core.matchParent import splitties.views.dsl.core.textView +import splitties.views.dsl.core.view import splitties.views.dsl.core.wrapContent import splitties.views.gravityCenter import splitties.views.imageDrawable @@ -43,8 +46,7 @@ class StatusAreaEntryUi(override val ctx: Context, private val theme: Theme) : U scaleType = ImageView.ScaleType.CENTER_INSIDE } - val textIcon = textView { - gravity = gravityCenter + val textIcon = view(::AutoScaleTextView) { setTextSize(TypedValue.COMPLEX_UNIT_DIP, 20f) // keep original typeface, apply textStyle only if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) { @@ -53,7 +55,6 @@ class StatusAreaEntryUi(override val ctx: Context, private val theme: Theme) : U } else { setTypeface(typeface, Typeface.BOLD) } - text } val label = textView { @@ -72,7 +73,7 @@ class StatusAreaEntryUi(override val ctx: Context, private val theme: Theme) : U add(icon, lParams { centerOn(bkg) }) - add(textIcon, lParams { + add(textIcon, lParams(wrapContent, wrapContent) { centerOn(bkg) }) add(label, lParams(wrapContent, wrapContent) { @@ -99,11 +100,21 @@ class StatusAreaEntryUi(override val ctx: Context, private val theme: Theme) : U } else { icon.visibility = View.GONE textIcon.visibility = View.VISIBLE - textIcon.text = entry.label.substring(0, 1) + textIcon.text = getFirstCharacter(entry.label) textIcon.setTextColor(contentColor) } bkgDrawable.paint.color = if (entry.active) theme.genericActiveBackgroundColor else theme.keyBackgroundColor label.text = entry.label } + + private fun getFirstCharacter(s: String): String { + return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { + val iterator = BreakIterator.getCharacterInstance() + iterator.setText(s) + s.substring(iterator.first(), iterator.next()) + } else { + s.substring(0, s.offsetByCodePoints(0, 1)) + } + } } From 3e26eab9f53e055e637a7e2a0e462e980dff665a Mon Sep 17 00:00:00 2001 From: Rocka Date: Sat, 7 Oct 2023 19:12:15 +0800 Subject: [PATCH 038/381] Link to librime static library with --whole-archive flag --- plugin/rime/src/main/cpp/CMakeLists.txt | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/plugin/rime/src/main/cpp/CMakeLists.txt b/plugin/rime/src/main/cpp/CMakeLists.txt index 714859c91..7c0d933de 100644 --- a/plugin/rime/src/main/cpp/CMakeLists.txt +++ b/plugin/rime/src/main/cpp/CMakeLists.txt @@ -34,6 +34,10 @@ set_target_properties(Rime_static PROPERTIES INTERFACE_LINK_LIBRARIES "${Rime_dependencies}" ) -set(RIME_TARGET Rime_static) +# https://github.com/rime/librime/blob/1.9.0/src/rime_api.h#L663 +# https://stackoverflow.com/questions/805555/ld-linker-question-the-whole-archive-option +# use $ when cmake updated to 3.24+ +# https://cmake.org/cmake/help/latest/manual/cmake-generator-expressions.7.html#link-features +set(RIME_TARGET "-Wl,--whole-archive" Rime_static "-Wl,--no-whole-archive") set(RIME_DATA_DIR "${CMAKE_INSTALL_DATADIR}/rime-data") add_subdirectory(fcitx5-rime) From a4bb68117fffb112a894ee2d4da1489757a64dcb Mon Sep 17 00:00:00 2001 From: Rocka Date: Sun, 8 Oct 2023 01:59:29 +0800 Subject: [PATCH 039/381] Check clientPreedit update only for androidkeyboard --- .../cpp/androidfrontend/androidfrontend.cpp | 19 ++++--------------- 1 file changed, 4 insertions(+), 15 deletions(-) diff --git a/app/src/main/cpp/androidfrontend/androidfrontend.cpp b/app/src/main/cpp/androidfrontend/androidfrontend.cpp index ef0c60587..c8fb73019 100644 --- a/app/src/main/cpp/androidfrontend/androidfrontend.cpp +++ b/app/src/main/cpp/androidfrontend/androidfrontend.cpp @@ -42,7 +42,7 @@ class AndroidInputContext : public InputContext { } void updatePreeditImpl() override { - checkClientPreeditUpdate(); + frontend_->updateClientPreedit(filterText(inputPanel().clientPreedit())); } void updateInputPanel() { @@ -52,10 +52,10 @@ class AndroidInputContext : public InputContext { // However on Android, androidkeyboard uses clientPreedit unconditionally in order to provide // a more integrated experience, so we need to check clientPreedit update manually even if // clientPreedit is not enabled. - if (!isPreeditEnabled()) { - checkClientPreeditUpdate(); - } const InputPanel &ip = inputPanel(); + if (!isPreeditEnabled() && frontend_->instance()->inputMethod(this) == "keyboard-us") { + frontend_->updateClientPreedit(filterText(ip.clientPreedit())); + } frontend_->updateInputPanel( filterText(ip.preedit()), filterText(ip.auxUp()), @@ -141,17 +141,6 @@ class AndroidInputContext : public InputContext { AndroidFrontend *frontend_; int uid_; - bool clientPreeditEmpty_ = true; - - void checkClientPreeditUpdate() { - const auto &clientPreedit = filterText(inputPanel().clientPreedit()); - const bool empty = clientPreedit.empty(); - // skip update if new and old clientPreedit are both empty - if (empty && clientPreeditEmpty_) return; - clientPreeditEmpty_ = empty; - frontend_->updateClientPreedit(clientPreedit); - } - inline Text filterText(const Text &orig) { return frontend_->instance()->outputFilter(this, orig); } From 91221523233857dda281810bddeb0b01903fd1e5 Mon Sep 17 00:00:00 2001 From: Rocka Date: Sun, 8 Oct 2023 10:42:44 +0800 Subject: [PATCH 040/381] Fix crash on empty StatusArea label (shortText) --- .../org/fcitx/fcitx5/android/input/status/StatusAreaEntryUi.kt | 1 + 1 file changed, 1 insertion(+) diff --git a/app/src/main/java/org/fcitx/fcitx5/android/input/status/StatusAreaEntryUi.kt b/app/src/main/java/org/fcitx/fcitx5/android/input/status/StatusAreaEntryUi.kt index 07281edc5..2e93da080 100644 --- a/app/src/main/java/org/fcitx/fcitx5/android/input/status/StatusAreaEntryUi.kt +++ b/app/src/main/java/org/fcitx/fcitx5/android/input/status/StatusAreaEntryUi.kt @@ -109,6 +109,7 @@ class StatusAreaEntryUi(override val ctx: Context, private val theme: Theme) : U } private fun getFirstCharacter(s: String): String { + if (s.isEmpty()) return "" return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { val iterator = BreakIterator.getCharacterInstance() iterator.setText(s) From 63371b8f7b8c0cf62b9937d968134a3a53fd672f Mon Sep 17 00:00:00 2001 From: Rocka Date: Sun, 8 Oct 2023 10:47:11 +0800 Subject: [PATCH 041/381] Set ShareInputState to "All" on first run --- app/src/main/java/org/fcitx/fcitx5/android/core/Fcitx.kt | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/app/src/main/java/org/fcitx/fcitx5/android/core/Fcitx.kt b/app/src/main/java/org/fcitx/fcitx5/android/core/Fcitx.kt index 27f105c49..86dc9950f 100644 --- a/app/src/main/java/org/fcitx/fcitx5/android/core/Fcitx.kt +++ b/app/src/main/java/org/fcitx/fcitx5/android/core/Fcitx.kt @@ -350,11 +350,14 @@ class Fcitx(private val context: Context) : FcitxAPI, FcitxLifecycleOwner { // will be called in fcitx main thread private fun onFirstRun() { Timber.i("onFirstRun") - getFcitxGlobalConfig()?.get("cfg")?.run { - get("Behavior")["PreeditEnabledByDefault"].value = "False" + getFcitxGlobalConfig()?.get("cfg")?.apply { + get("Behavior").apply { + get("ShareInputState").value = "All" + get("PreeditEnabledByDefault").value = "False" + } setFcitxGlobalConfig(this) } - getFcitxAddonConfig("pinyin")?.get("cfg")?.run { + getFcitxAddonConfig("pinyin")?.get("cfg")?.apply { get("PreeditInApplication").value = "False" get("PreeditCursorPositionAtBeginning").value = "False" get("QuickPhraseKey").value = "" From 1bd77b322ca87ea57b2c4c8d91e122351c438482 Mon Sep 17 00:00:00 2001 From: Rocka Date: Sun, 8 Oct 2023 15:31:21 +0800 Subject: [PATCH 042/381] Update fcitx5 submodules --- lib/fcitx5-chinese-addons/src/main/cpp/fcitx5-chinese-addons | 2 +- lib/fcitx5/src/main/cpp/fcitx5 | 2 +- lib/libime/src/main/cpp/libime | 2 +- plugin/rime/src/main/cpp/fcitx5-rime | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/fcitx5-chinese-addons/src/main/cpp/fcitx5-chinese-addons b/lib/fcitx5-chinese-addons/src/main/cpp/fcitx5-chinese-addons index 25d1b7d6c..09590a938 160000 --- a/lib/fcitx5-chinese-addons/src/main/cpp/fcitx5-chinese-addons +++ b/lib/fcitx5-chinese-addons/src/main/cpp/fcitx5-chinese-addons @@ -1 +1 @@ -Subproject commit 25d1b7d6c488a589ccd7f5472fb75015cf9f8a19 +Subproject commit 09590a938a9e188b31dc8e905fd8339182cae58d diff --git a/lib/fcitx5/src/main/cpp/fcitx5 b/lib/fcitx5/src/main/cpp/fcitx5 index ae2081cc4..e8acb9c69 160000 --- a/lib/fcitx5/src/main/cpp/fcitx5 +++ b/lib/fcitx5/src/main/cpp/fcitx5 @@ -1 +1 @@ -Subproject commit ae2081cc41ac8bad19af5ca79167744f78d8f19a +Subproject commit e8acb9c695222f4fd0d2e8fd22d69c6c65e9f062 diff --git a/lib/libime/src/main/cpp/libime b/lib/libime/src/main/cpp/libime index 1462a8181..e555efb3d 160000 --- a/lib/libime/src/main/cpp/libime +++ b/lib/libime/src/main/cpp/libime @@ -1 +1 @@ -Subproject commit 1462a81810135f993cd9dfa8711b8716d1d2cb61 +Subproject commit e555efb3db3b25e44648018c0ffb5bf0c8d25b10 diff --git a/plugin/rime/src/main/cpp/fcitx5-rime b/plugin/rime/src/main/cpp/fcitx5-rime index 7568f6736..82c4daaf2 160000 --- a/plugin/rime/src/main/cpp/fcitx5-rime +++ b/plugin/rime/src/main/cpp/fcitx5-rime @@ -1 +1 @@ -Subproject commit 7568f67362cfbad17653694c123f439f2ff69192 +Subproject commit 82c4daaf26f7ae7a0325ba65516794162d2746b2 From d7c4123c25ad979525578b1d5bf204306cad40be Mon Sep 17 00:00:00 2001 From: Rocka Date: Mon, 9 Oct 2023 02:21:47 +0800 Subject: [PATCH 043/381] Update fcitx5 submodules fix file permissions issues on device that uses fuse --- app/src/main/cpp/native-lib.cpp | 6 ++++++ lib/fcitx5/src/main/cpp/fcitx5 | 2 +- plugin/rime/src/main/cpp/fcitx5-rime | 2 +- 3 files changed, 8 insertions(+), 2 deletions(-) diff --git a/app/src/main/cpp/native-lib.cpp b/app/src/main/cpp/native-lib.cpp index a8006657c..a44fae79a 100644 --- a/app/src/main/cpp/native-lib.cpp +++ b/app/src/main/cpp/native-lib.cpp @@ -1,5 +1,7 @@ #include +#include + #include #include #include @@ -19,6 +21,7 @@ #include #include #include +#include #include #include @@ -620,6 +623,9 @@ Java_org_fcitx_fcitx5_android_core_Fcitx_startupFcitx(JNIEnv *env, jclass clazz, env->CallStaticVoidMethod(GlobalRef->Fcitx, GlobalRef->ShowToast, *JString(env, s)); }; + umask(007); + fcitx::StandardPath::global().syncUmask(); + Fcitx::Instance().startup([&](auto *androidfrontend) { FCITX_INFO() << "Setting up callback"; readyCallback(); diff --git a/lib/fcitx5/src/main/cpp/fcitx5 b/lib/fcitx5/src/main/cpp/fcitx5 index e8acb9c69..ce98537ff 160000 --- a/lib/fcitx5/src/main/cpp/fcitx5 +++ b/lib/fcitx5/src/main/cpp/fcitx5 @@ -1 +1 @@ -Subproject commit e8acb9c695222f4fd0d2e8fd22d69c6c65e9f062 +Subproject commit ce98537ffe0a1f642c8369b3e6183ce536b0fdf4 diff --git a/plugin/rime/src/main/cpp/fcitx5-rime b/plugin/rime/src/main/cpp/fcitx5-rime index 82c4daaf2..3ca73255b 160000 --- a/plugin/rime/src/main/cpp/fcitx5-rime +++ b/plugin/rime/src/main/cpp/fcitx5-rime @@ -1 +1 @@ -Subproject commit 82c4daaf26f7ae7a0325ba65516794162d2746b2 +Subproject commit 3ca73255b76ed047f346796fc86a337bab060371 From 14ce39762b070782b39fe92d7b702eab64648368 Mon Sep 17 00:00:00 2001 From: Rocka Date: Mon, 9 Oct 2023 12:02:59 +0800 Subject: [PATCH 044/381] Change swipe number keysym back --- .../android/input/keyboard/TextKeyboard.kt | 20 +++++++++---------- .../src/main/cpp/fcitx5-chinese-addons | 2 +- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/app/src/main/java/org/fcitx/fcitx5/android/input/keyboard/TextKeyboard.kt b/app/src/main/java/org/fcitx/fcitx5/android/input/keyboard/TextKeyboard.kt index 1223a55f2..235b28b42 100644 --- a/app/src/main/java/org/fcitx/fcitx5/android/input/keyboard/TextKeyboard.kt +++ b/app/src/main/java/org/fcitx/fcitx5/android/input/keyboard/TextKeyboard.kt @@ -26,16 +26,16 @@ class TextKeyboard( val Layout: List> = listOf( listOf( - AlphabetDigitKey("Q", 1), - AlphabetDigitKey("W", 2), - AlphabetDigitKey("E", 3), - AlphabetDigitKey("R", 4), - AlphabetDigitKey("T", 5), - AlphabetDigitKey("Y", 6), - AlphabetDigitKey("U", 7), - AlphabetDigitKey("I", 8), - AlphabetDigitKey("O", 9), - AlphabetDigitKey("P", 0) + AlphabetKey("Q", "1"), + AlphabetKey("W", "2"), + AlphabetKey("E", "3"), + AlphabetKey("R", "4"), + AlphabetKey("T", "5"), + AlphabetKey("Y", "6"), + AlphabetKey("U", "7"), + AlphabetKey("I", "8"), + AlphabetKey("O", "9"), + AlphabetKey("P", "0") ), listOf( AlphabetKey("A", "@"), diff --git a/lib/fcitx5-chinese-addons/src/main/cpp/fcitx5-chinese-addons b/lib/fcitx5-chinese-addons/src/main/cpp/fcitx5-chinese-addons index 09590a938..65e5b0101 160000 --- a/lib/fcitx5-chinese-addons/src/main/cpp/fcitx5-chinese-addons +++ b/lib/fcitx5-chinese-addons/src/main/cpp/fcitx5-chinese-addons @@ -1 +1 @@ -Subproject commit 09590a938a9e188b31dc8e905fd8339182cae58d +Subproject commit 65e5b010164d1cb0f17791385bfb77b068ff18b0 From 2b64744e619810e810290c4d9401702d73705962 Mon Sep 17 00:00:00 2001 From: Rocka Date: Mon, 9 Oct 2023 12:59:40 +0800 Subject: [PATCH 045/381] Remove KP_* KeySym workaround in popup --- .../fcitx/fcitx5/android/input/keyboard/KeyAction.kt | 10 +++++----- .../fcitx5/android/input/popup/PopupKeyboardUi.kt | 11 +---------- 2 files changed, 6 insertions(+), 15 deletions(-) diff --git a/app/src/main/java/org/fcitx/fcitx5/android/input/keyboard/KeyAction.kt b/app/src/main/java/org/fcitx/fcitx5/android/input/keyboard/KeyAction.kt index 936df4827..4777e84cd 100644 --- a/app/src/main/java/org/fcitx/fcitx5/android/input/keyboard/KeyAction.kt +++ b/app/src/main/java/org/fcitx/fcitx5/android/input/keyboard/KeyAction.kt @@ -19,13 +19,13 @@ sealed class KeyAction { data class CapsAction(val lock: Boolean) : KeyAction() - object QuickPhraseAction : KeyAction() + data object QuickPhraseAction : KeyAction() - object UnicodeAction : KeyAction() + data object UnicodeAction : KeyAction() - object LangSwitchAction : KeyAction() + data object LangSwitchAction : KeyAction() - object ShowInputMethodPickerAction : KeyAction() + data object ShowInputMethodPickerAction : KeyAction() data class LayoutSwitchAction(val act: String = "") : KeyAction() @@ -35,5 +35,5 @@ sealed class KeyAction { data class PickerSwitchAction(val key: PickerWindow.Key? = null) : KeyAction() - object SpaceLongPressAction: KeyAction() + data object SpaceLongPressAction: KeyAction() } diff --git a/app/src/main/java/org/fcitx/fcitx5/android/input/popup/PopupKeyboardUi.kt b/app/src/main/java/org/fcitx/fcitx5/android/input/popup/PopupKeyboardUi.kt index bf820b52c..d192b3b48 100644 --- a/app/src/main/java/org/fcitx/fcitx5/android/input/popup/PopupKeyboardUi.kt +++ b/app/src/main/java/org/fcitx/fcitx5/android/input/popup/PopupKeyboardUi.kt @@ -4,8 +4,6 @@ import android.content.Context import android.graphics.Rect import android.graphics.drawable.GradientDrawable import android.view.ViewOutlineProvider -import org.fcitx.fcitx5.android.core.FcitxKeyMapping -import org.fcitx.fcitx5.android.core.KeySym import org.fcitx.fcitx5.android.data.theme.Theme import org.fcitx.fcitx5.android.input.AutoScaleTextView import org.fcitx.fcitx5.android.input.keyboard.KeyAction @@ -215,14 +213,7 @@ class PopupKeyboardUi( override fun onTrigger(): KeyAction? { val key = keys.getOrNull(focusedIndex) ?: return null - /** - * send `KP_*` KeySym for numeric characters in popup - * see also [org.fcitx.fcitx5.android.input.keyboard.AlphabetDigitKey] - */ - return if (key.length == 1 && key[0].isDigit()) - KeyAction.SymAction(KeySym(FcitxKeyMapping.FcitxKey_KP_0 + key[0].digitToInt())) - else - KeyAction.FcitxKeyAction(key) + return KeyAction.FcitxKeyAction(key) } } From 6e22d34eb741ae241afa6f2eea44822a732fb619 Mon Sep 17 00:00:00 2001 From: Rocka Date: Wed, 11 Oct 2023 21:41:34 +0800 Subject: [PATCH 046/381] Make use of new commitStringWithCursor API --- .../cpp/androidfrontend/androidfrontend.cpp | 14 ++++--- .../cpp/androidfrontend/androidfrontend.h | 4 +- .../androidfrontend/androidfrontend_public.h | 2 +- .../cpp/androidkeyboard/androidkeyboard.cpp | 38 +++++++++-------- .../cpp/androidkeyboard/androidkeyboard.h | 5 ++- app/src/main/cpp/native-lib.cpp | 6 ++- .../fcitx/fcitx5/android/core/FcitxEvent.kt | 13 ++++-- .../fcitx5/android/data/prefs/AppPrefs.kt | 3 -- .../android/input/FcitxInputMethodService.kt | 42 +++++++++++++------ .../input/keyboard/CommonKeyActionListener.kt | 2 +- app/src/main/res/values-zh-rCN/strings.xml | 1 - app/src/main/res/values/strings.xml | 1 - lib/fcitx5/src/main/cpp/fcitx5 | 2 +- 13 files changed, 82 insertions(+), 51 deletions(-) diff --git a/app/src/main/cpp/androidfrontend/androidfrontend.cpp b/app/src/main/cpp/androidfrontend/androidfrontend.cpp index c8fb73019..4abee9059 100644 --- a/app/src/main/cpp/androidfrontend/androidfrontend.cpp +++ b/app/src/main/cpp/androidfrontend/androidfrontend.cpp @@ -10,13 +10,13 @@ namespace fcitx { -class AndroidInputContext : public InputContext { +class AndroidInputContext : public InputContextV2 { public: AndroidInputContext(AndroidFrontend *frontend, InputContextManager &inputContextManager, int uid, const std::string &pkgName) - : InputContext(inputContextManager, pkgName), + : InputContextV2(inputContextManager, pkgName), frontend_(frontend), uid_(uid) { created(); @@ -30,7 +30,11 @@ class AndroidInputContext : public InputContext { [[nodiscard]] const char *frontend() const override { return "androidfrontend"; } void commitStringImpl(const std::string &text) override { - frontend_->commitString(text); + frontend_->commitString(text, -1); + } + + void commitStringWithCursorImpl(const std::string &text, size_t cursor) override { + frontend_->commitString(text, static_cast(cursor)); } void forwardKeyImpl(const ForwardKeyEvent &key) override { @@ -202,8 +206,8 @@ void AndroidFrontend::forwardKey(const Key &key, bool isRelease) { keyEventCallback(sym, key.states(), Key::keySymToUnicode(sym), isRelease, -1); } -void AndroidFrontend::commitString(const std::string &str) { - commitStringCallback(str); +void AndroidFrontend::commitString(const std::string &str, const int cursor) { + commitStringCallback(str, cursor); } void AndroidFrontend::updateCandidateList(const std::vector &candidates, const int size) { diff --git a/app/src/main/cpp/androidfrontend/androidfrontend.h b/app/src/main/cpp/androidfrontend/androidfrontend.h index 9202b6a90..031a1772d 100644 --- a/app/src/main/cpp/androidfrontend/androidfrontend.h +++ b/app/src/main/cpp/androidfrontend/androidfrontend.h @@ -17,7 +17,7 @@ class AndroidFrontend : public AddonInstance { Instance *instance() { return instance_; } void updateCandidateList(const std::vector &candidates, const int size); - void commitString(const std::string &str); + void commitString(const std::string &str, const int cursor); void updateClientPreedit(const Text &clientPreedit); void updateInputPanel(const Text &preedit, const Text &auxUp, const Text &auxDown); void releaseInputContext(const int uid); @@ -77,7 +77,7 @@ class AndroidFrontend : public AddonInstance { void handleStatusAreaUpdate(); CandidateListCallback candidateListCallback = [](const std::vector &, const int) {}; - CommitStringCallback commitStringCallback = [](const std::string &) {}; + CommitStringCallback commitStringCallback = [](const std::string &, const int) {}; ClientPreeditCallback preeditCallback = [](const Text &) {}; InputPanelCallback inputPanelAuxCallback = [](const fcitx::Text &, const fcitx::Text &, const Text &) {}; KeyEventCallback keyEventCallback = [](const int, const uint32_t, const uint32_t, const bool, const int) {}; diff --git a/app/src/main/cpp/androidfrontend/androidfrontend_public.h b/app/src/main/cpp/androidfrontend/androidfrontend_public.h index cff2ffaf3..3505900dc 100644 --- a/app/src/main/cpp/androidfrontend/androidfrontend_public.h +++ b/app/src/main/cpp/androidfrontend/androidfrontend_public.h @@ -5,7 +5,7 @@ #include typedef std::function &, const int)> CandidateListCallback; -typedef std::function CommitStringCallback; +typedef std::function CommitStringCallback; typedef std::function ClientPreeditCallback; typedef std::function InputPanelCallback; typedef std::function KeyEventCallback; diff --git a/app/src/main/cpp/androidkeyboard/androidkeyboard.cpp b/app/src/main/cpp/androidkeyboard/androidkeyboard.cpp index 49ab10318..28fccaf63 100644 --- a/app/src/main/cpp/androidkeyboard/androidkeyboard.cpp +++ b/app/src/main/cpp/androidkeyboard/androidkeyboard.cpp @@ -10,8 +10,6 @@ #include "androidkeyboard.h" -#define FCITX_KEYBOARD_MAX_BUFFER 20 - namespace fcitx { namespace { @@ -85,7 +83,7 @@ void AndroidKeyboardEngine::keyEvent(const InputMethodEntry &entry, KeyEvent &ev // check if we can select candidate. if (auto candList = inputContext->inputPanel().candidateList()) { - int idx = key.keyListIndex(selectionKeys_); + const int idx = key.keyListIndex(selectionKeys_); if (idx >= 0 && idx < candList->size()) { event.filterAndAccept(); candList->candidate(idx).select(inputContext); @@ -93,9 +91,9 @@ void AndroidKeyboardEngine::keyEvent(const InputMethodEntry &entry, KeyEvent &ev } } - bool validSym = isValidSym(key); + const bool validSym = isValidSym(key); - static KeyList FCITX_HYPHEN_APOS = {Key(FcitxKey_minus), Key(FcitxKey_apostrophe)}; + static const KeyList FCITX_HYPHEN_APOS = {Key(FcitxKey_minus), Key(FcitxKey_apostrophe)}; // check for valid character if (key.isSimple() || validSym) { // prepend space before input next word @@ -173,7 +171,7 @@ std::vector AndroidKeyboardEngine::listInputMethods() { void AndroidKeyboardEngine::reloadConfig() { readAsIni(config_, ConfPath); selectionKeys_.clear(); - KeySym syms[] = { + const std::array syms{ FcitxKey_1, FcitxKey_2, FcitxKey_3, FcitxKey_4, FcitxKey_5, FcitxKey_6, FcitxKey_7, FcitxKey_8, FcitxKey_9, FcitxKey_0, }; @@ -252,7 +250,7 @@ void AndroidKeyboardEngine::updateCandidate(const InputMethodEntry &entry, Input results = spell()->call(entry.languageCode(), SpellProvider::Default, state->buffer_.userInput(), - 20); + SpellCandidateSize); } auto candidateList = std::make_unique(); for (const auto &result: results) { @@ -267,10 +265,10 @@ void AndroidKeyboardEngine::updateCandidate(const InputMethodEntry &entry, Input } void AndroidKeyboardEngine::updateUI(InputContext *inputContext) { - auto *state = inputContext->propertyFor(&factory_); - Text preedit(preeditString(inputContext), TextFormatFlag::Underline); - preedit.setCursor(static_cast(state->buffer_.cursorByChar())); - inputContext->inputPanel().setClientPreedit(preedit); + auto [preedit, cursor] = preeditWithCursor(inputContext); + Text clientPreedit(preedit, TextFormatFlag::Underline); + clientPreedit.setCursor(static_cast(cursor)); + inputContext->inputPanel().setClientPreedit(clientPreedit); // we don't want preedit here ... // if (!inputContext->capabilityFlags().test(CapabilityFlag::Preedit)) { // inputContext->inputPanel().setPreedit(preedit); @@ -296,7 +294,7 @@ bool AndroidKeyboardEngine::updateBuffer(InputContext *inputContext, const std:: } auto &buffer = state->buffer_; - auto preedit = preeditString(inputContext); + auto [preedit, cursor] = preeditWithCursor(inputContext); if (preedit != buffer.userInput()) { buffer.clear(); buffer.type(preedit); @@ -304,7 +302,7 @@ bool AndroidKeyboardEngine::updateBuffer(InputContext *inputContext, const std:: buffer.type(chr); - if (buffer.size() >= FCITX_KEYBOARD_MAX_BUFFER) { + if (buffer.size() >= MaxBufferSize) { commitBuffer(inputContext); return true; } @@ -314,11 +312,15 @@ bool AndroidKeyboardEngine::updateBuffer(InputContext *inputContext, const std:: } void AndroidKeyboardEngine::commitBuffer(InputContext *inputContext) { - auto preedit = preeditString(inputContext); + auto [preedit, cursor] = preeditWithCursor(inputContext); if (preedit.empty()) { return; } - inputContext->commitString(preedit); + if (auto icv2 = dynamic_cast(inputContext)) { + icv2->commitStringWithCursor(preedit, cursor); + } else { + inputContext->commitString(preedit); + } resetState(inputContext); inputContext->inputPanel().reset(); inputContext->updatePreedit(); @@ -330,13 +332,13 @@ bool AndroidKeyboardEngine::supportHint(const std::string &language) { return hasSpell; } -std::string AndroidKeyboardEngine::preeditString(InputContext *inputContext) { +std::pair AndroidKeyboardEngine::preeditWithCursor(InputContext *inputContext) { auto *state = inputContext->propertyFor(&factory_); - return state->buffer_.userInput(); + return {state->buffer_.userInput(), state->buffer_.cursorByChar()}; } void AndroidKeyboardEngine::invokeActionImpl(const InputMethodEntry &entry, InvokeActionEvent &event) { - int cursor = event.cursor(); + const int cursor = event.cursor(); auto inputContext = event.inputContext(); auto *state = inputContext->propertyFor(&factory_); if (event.action() != InvokeActionEvent::Action::LeftClick diff --git a/app/src/main/cpp/androidkeyboard/androidkeyboard.h b/app/src/main/cpp/androidkeyboard/androidkeyboard.h index 847b64fc7..3f7207178 100644 --- a/app/src/main/cpp/androidkeyboard/androidkeyboard.h +++ b/app/src/main/cpp/androidkeyboard/androidkeyboard.h @@ -50,6 +50,9 @@ struct AndroidKeyboardEngineState : public InputContextProperty { class AndroidKeyboardEngine final : public InputMethodEngineV3 { public: + static int constexpr MaxBufferSize = 20; + static int constexpr SpellCandidateSize = 20; + AndroidKeyboardEngine(Instance *instance); ~AndroidKeyboardEngine() = default; @@ -100,7 +103,7 @@ class AndroidKeyboardEngine final : public InputMethodEngineV3 { private: bool supportHint(const std::string &language); - std::string preeditString(InputContext *inputContext); + std::pair preeditWithCursor(InputContext *inputContext); Instance *instance_; AndroidKeyboardEngineConfig config_; diff --git a/app/src/main/cpp/native-lib.cpp b/app/src/main/cpp/native-lib.cpp index a44fae79a..bad869dac 100644 --- a/app/src/main/cpp/native-lib.cpp +++ b/app/src/main/cpp/native-lib.cpp @@ -564,10 +564,12 @@ Java_org_fcitx_fcitx5_android_core_Fcitx_startupFcitx(JNIEnv *env, jclass clazz, env->SetObjectArrayElement(vararg, 1, *candidatesArray); env->CallStaticVoidMethod(GlobalRef->Fcitx, GlobalRef->HandleFcitxEvent, 0, *vararg); }; - auto commitStringCallback = [](const std::string &str) { + auto commitStringCallback = [](const std::string &str, const int cursor) { auto env = GlobalRef->AttachEnv(); - auto vararg = JRef(env, env->NewObjectArray(1, GlobalRef->String, nullptr)); + auto stringCursor = JRef(env, env->NewObject(GlobalRef->Integer, GlobalRef->IntegerInit, cursor)); + auto vararg = JRef(env, env->NewObjectArray(2, GlobalRef->Object, nullptr)); env->SetObjectArrayElement(vararg, 0, JString(env, str)); + env->SetObjectArrayElement(vararg, 1, stringCursor); env->CallStaticVoidMethod(GlobalRef->Fcitx, GlobalRef->HandleFcitxEvent, 1, *vararg); }; auto preeditCallback = [](const fcitx::Text &clientPreedit) { diff --git a/app/src/main/java/org/fcitx/fcitx5/android/core/FcitxEvent.kt b/app/src/main/java/org/fcitx/fcitx5/android/core/FcitxEvent.kt index d8acfedc5..00404cb1d 100644 --- a/app/src/main/java/org/fcitx/fcitx5/android/core/FcitxEvent.kt +++ b/app/src/main/java/org/fcitx/fcitx5/android/core/FcitxEvent.kt @@ -35,10 +35,12 @@ sealed class FcitxEvent(open val data: T) { } } - data class CommitStringEvent(override val data: String) : - FcitxEvent(data) { + data class CommitStringEvent(override val data: Data) : + FcitxEvent(data) { override val eventType: EventType get() = EventType.Commit + + data class Data(val text: String, val cursor: Int) } data class ClientPreeditEvent(override val data: FormattedText) : @@ -157,7 +159,12 @@ sealed class FcitxEvent(open val data: T) { params[1] as Array ) ) - EventType.Commit -> CommitStringEvent(params[0] as String) + EventType.Commit -> CommitStringEvent( + CommitStringEvent.Data( + params[0] as String, + params[1] as Int + ) + ) EventType.ClientPreedit -> ClientPreeditEvent(params[0] as FormattedText) EventType.InputPanel -> InputPanelEvent( InputPanelEvent.Data( diff --git a/app/src/main/java/org/fcitx/fcitx5/android/data/prefs/AppPrefs.kt b/app/src/main/java/org/fcitx/fcitx5/android/data/prefs/AppPrefs.kt index 2e4635ad6..77470aa1b 100644 --- a/app/src/main/java/org/fcitx/fcitx5/android/data/prefs/AppPrefs.kt +++ b/app/src/main/java/org/fcitx/fcitx5/android/data/prefs/AppPrefs.kt @@ -30,8 +30,6 @@ class AppPrefs(private val sharedPreferences: SharedPreferences) { inner class Advanced : ManagedPreferenceCategory(R.string.advanced, sharedPreferences) { val ignoreSystemCursor = switch(R.string.ignore_sys_cursor, "ignore_system_cursor", true) - val resetCursorAfterCommit = - switch(R.string.reset_cursor_after_commit, "reset_cursor_after_commit", true) val hideKeyConfig = switch(R.string.hide_key_config, "hide_key_config", true) val disableAnimation = switch(R.string.disable_animation, "disable_animation", false) val vivoKeypressWorkaround = switch( @@ -363,7 +361,6 @@ class AppPrefs(private val sharedPreferences: SharedPreferences) { internal.verboseLog, internal.editorInfoInspector, advanced.ignoreSystemCursor, - advanced.resetCursorAfterCommit, advanced.disableAnimation, advanced.vivoKeypressWorkaround ).forEach { diff --git a/app/src/main/java/org/fcitx/fcitx5/android/input/FcitxInputMethodService.kt b/app/src/main/java/org/fcitx/fcitx5/android/input/FcitxInputMethodService.kt index 3f7609af3..d16191a41 100644 --- a/app/src/main/java/org/fcitx/fcitx5/android/input/FcitxInputMethodService.kt +++ b/app/src/main/java/org/fcitx/fcitx5/android/input/FcitxInputMethodService.kt @@ -85,7 +85,6 @@ class FcitxInputMethodService : LifecycleInputMethodService() { private var highlightColor: Int = 0x66008577 // material_deep_teal_500 with alpha 0.4 private val ignoreSystemCursor by AppPrefs.getInstance().advanced.ignoreSystemCursor - private val resetCursor by AppPrefs.getInstance().advanced.resetCursorAfterCommit private val inlineSuggestions by AppPrefs.getInstance().keyboard.inlineSuggestions @@ -126,7 +125,7 @@ class FcitxInputMethodService : LifecycleInputMethodService() { private fun handleFcitxEvent(event: FcitxEvent<*>) { when (event) { is FcitxEvent.CommitStringEvent -> { - commitText(event.data) + commitText(event.data.text, event.data.cursor) } is FcitxEvent.KeyEvent -> event.data.let event@{ if (it.states.virtual) { @@ -223,24 +222,31 @@ class FcitxInputMethodService : LifecycleInputMethodService() { } } - fun commitText(text: String) { + fun commitText(text: String, cursor: Int = -1) { + val ic = currentInputConnection ?: return + val targetCursor = if (cursor == -1) text.length else cursor + val currentCursor = selection.current.start - composing.start + // when composing text equals commit content, finish composing text as-is if (composing.isNotEmpty() && composingText.toString() == text) { - // when composing text equals commit content, finish composing text as-is - val cursor = composing.end - resetComposingState() - currentInputConnection?.finishComposingText() - if (resetCursor) { - selection.predict(cursor) - currentInputConnection?.setSelection(cursor, cursor) + if (targetCursor != currentCursor) { + val c = composing.start + targetCursor + selection.predict(c) + ic.setSelection(c, c) } + resetComposingState() + ic.finishComposingText() return } // committed text should replace composing (if any), replace selected range (if any), // or simply prepend before cursor val start = if (composing.isEmpty()) selection.latest.start else composing.start - selection.predict(start + text.length) resetComposingState() - currentInputConnection?.commitText(text, 1) + ic.commitText(text, 1) + if (targetCursor != currentCursor) { + val c = start + targetCursor + selection.predict(c) + ic.setSelection(c, c) + } } private fun sendDownKeyEvent(eventTime: Long, keyEventCode: Int, metaState: Int = 0) { @@ -596,6 +602,18 @@ class FcitxInputMethodService : LifecycleInputMethodService() { ic.endBatchEdit() } + /** + * Finish composing text and leave cursor position as-is. + * Also updates internal composing state of [FcitxInputMethodService]. + */ + fun finishComposing() { + val ic = currentInputConnection ?: return + if (composing.isEmpty()) return + composing.clear() + composingText = FormattedText.Empty + ic.finishComposingText() + } + @SuppressLint("RestrictedApi") @RequiresApi(Build.VERSION_CODES.R) override fun onCreateInlineSuggestionsRequest(uiExtras: Bundle): InlineSuggestionsRequest? { diff --git a/app/src/main/java/org/fcitx/fcitx5/android/input/keyboard/CommonKeyActionListener.kt b/app/src/main/java/org/fcitx/fcitx5/android/input/keyboard/CommonKeyActionListener.kt index 1aae96ae2..c3e94cb3e 100644 --- a/app/src/main/java/org/fcitx/fcitx5/android/input/keyboard/CommonKeyActionListener.kt +++ b/app/src/main/java/org/fcitx/fcitx5/android/input/keyboard/CommonKeyActionListener.kt @@ -59,7 +59,7 @@ class CommonKeyActionListener : reset() } else if (inputMethodEntryCached.uniqueName.let { it == "keyboard-us" || it == "unikey" }) { // androidkeyboard clears composing on reset, but we want to commit it as-is - service.currentInputConnection?.finishComposingText() + service.finishComposing() reset() } else { if (!select(0)) reset() diff --git a/app/src/main/res/values-zh-rCN/strings.xml b/app/src/main/res/values-zh-rCN/strings.xml index add608aba..e04ec1e5c 100644 --- a/app/src/main/res/values-zh-rCN/strings.xml +++ b/app/src/main/res/values-zh-rCN/strings.xml @@ -249,5 +249,4 @@ 我不需要 授予权限 剪贴板数据库将会被完全清空,包括已置顶的条目。继续吗? - 提交文本后重置光标位置 \ No newline at end of file diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 730fb63db..5601bc9c2 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -249,5 +249,4 @@ I don\'t need it Grant permission Clipboard database would be completely cleared, including pinned items. Processed? - Reset cursor position after commit text \ No newline at end of file diff --git a/lib/fcitx5/src/main/cpp/fcitx5 b/lib/fcitx5/src/main/cpp/fcitx5 index ce98537ff..edef0e17f 160000 --- a/lib/fcitx5/src/main/cpp/fcitx5 +++ b/lib/fcitx5/src/main/cpp/fcitx5 @@ -1 +1 @@ -Subproject commit ce98537ffe0a1f642c8369b3e6183ce536b0fdf4 +Subproject commit edef0e17f4dc96498f609e6bd5e4c8e98e4973d7 From f193b282a21d8fb417db2d4bc6069e00ec1fe40b Mon Sep 17 00:00:00 2001 From: Rocka Date: Wed, 11 Oct 2023 21:47:05 +0800 Subject: [PATCH 047/381] Refactor JNI InputMethodStatus --- app/src/main/cpp/helper-types.h | 33 ++++++++++------ app/src/main/cpp/native-lib.cpp | 56 ++++++++++++++-------------- app/src/main/cpp/object-conversion.h | 16 ++++---- 3 files changed, 57 insertions(+), 48 deletions(-) diff --git a/app/src/main/cpp/helper-types.h b/app/src/main/cpp/helper-types.h index fa2e9453e..605a38029 100644 --- a/app/src/main/cpp/helper-types.h +++ b/app/src/main/cpp/helper-types.h @@ -7,24 +7,35 @@ class InputMethodStatus { public: - const fcitx::InputMethodEntry *entry; + // fcitx::InputMethodEntry + std::string uniqueName; + std::string name; + std::string nativeName; + std::string icon; + std::string label; + std::string languageCode; + std::string addon; + bool configurable = false; + // fcitx::InputMethodEngine std::string subMode; std::string subModeLabel; std::string subModeIcon; InputMethodStatus(const fcitx::InputMethodEntry *entry, fcitx::InputMethodEngine *engine, - fcitx::InputContext *ic) - : entry(entry) { - if (engine) { - subMode = engine->subMode(*entry, *ic); - subModeLabel = engine->subModeLabel(*entry, *ic); - subModeIcon = engine->subModeIcon(*entry, *ic); - } + fcitx::InputContext *ic) { + uniqueName = entry->uniqueName(); + name = entry->name(); + nativeName = entry->nativeName(); + icon = entry->icon(); + label = entry->label(); + languageCode = entry->languageCode(); + addon = entry->addon(); + configurable = entry->isConfigurable(); + subMode = engine->subMode(*entry, *ic); + subModeLabel = engine->subModeLabel(*entry, *ic); + subModeIcon = engine->subModeIcon(*entry, *ic); } - - InputMethodStatus(const fcitx::InputMethodEntry *entry) - : entry(entry) {} }; class AddonStatus { diff --git a/app/src/main/cpp/native-lib.cpp b/app/src/main/cpp/native-lib.cpp index bad869dac..220e94e70 100644 --- a/app/src/main/cpp/native-lib.cpp +++ b/app/src/main/cpp/native-lib.cpp @@ -142,14 +142,12 @@ class Fcitx { return entries; } - InputMethodStatus inputMethodStatus() { + std::unique_ptr inputMethodStatus() { auto *ic = p_frontend->call(); - auto *engine = p_instance->inputMethodEngine(ic); - const auto *entry = p_instance->inputMethodEntry(ic); - if (engine) { - return {entry, engine, ic}; - } - return {entry}; + if (!ic) return nullptr; + auto *entry = p_instance->inputMethodEntry(ic); + auto *engine = static_cast(p_instance->addonManager().addon(entry->addon(), true)); + return std::make_unique(entry, engine, ic); } void setInputMethod(const std::string &ime) { @@ -282,9 +280,9 @@ class Fcitx { auto &globalConfig = p_instance->globalConfig(); auto &addonManager = p_instance->addonManager(); const auto &enabledAddons = globalConfig.enabledAddons(); - std::unordered_set enabledSet(enabledAddons.begin(), enabledAddons.end()); + const std::unordered_set enabledSet(enabledAddons.begin(), enabledAddons.end()); const auto &disabledAddons = globalConfig.disabledAddons(); - std::unordered_set + const std::unordered_set disabledSet(disabledAddons.begin(), disabledAddons.end()); std::vector addons; for (const auto category: {fcitx::AddonCategory::InputMethod, @@ -304,7 +302,7 @@ class Fcitx { } else if (enabledSet.count(info->uniqueName())) { enabled = true; } - addons.emplace_back(AddonStatus(info, enabled)); + addons.emplace_back(info, enabled); } } return addons; @@ -390,7 +388,7 @@ class Fcitx { fcitx::StatusGroup::InputMethod, fcitx::StatusGroup::AfterInputMethod}) { for (auto act: ic->statusArea().actions(group)) { - actions.emplace_back(ActionEntity(act, ic)); + actions.emplace_back(act, ic); } } return actions; @@ -484,33 +482,33 @@ Java_org_fcitx_fcitx5_android_core_Fcitx_startupFcitx(JNIEnv *env, jclass clazz, } FCITX_INFO() << "Starting..."; - setenv("SKIP_FCITX_PATH", "true", 1); - auto locale_ = CString(env, locale); auto appData_ = CString(env, appData); auto appLib_ = CString(env, appLib); auto extData_ = CString(env, extData); auto extCache_ = CString(env, extCache); - std::string lang_ = fcitx::stringutils::split(*locale_, ":")[0]; - std::string config_home = fcitx::stringutils::joinPath(*extData_, "config"); - std::string data_home = fcitx::stringutils::joinPath(*extData_, "data"); - std::string usr_share = fcitx::stringutils::joinPath(*appData_, "usr", "share"); - std::string locale_dir = fcitx::stringutils::joinPath(usr_share, "locale"); - std::string libime_data = fcitx::stringutils::joinPath(usr_share, "libime"); - std::string lua_path = fcitx::stringutils::concat( + const std::string lang_ = fcitx::stringutils::split(*locale_, ":")[0]; + const std::string config_home = fcitx::stringutils::joinPath(*extData_, "config"); + const std::string data_home = fcitx::stringutils::joinPath(*extData_, "data"); + const std::string usr_share = fcitx::stringutils::joinPath(*appData_, "usr", "share"); + const std::string locale_dir = fcitx::stringutils::joinPath(usr_share, "locale"); + const std::string libime_data = fcitx::stringutils::joinPath(usr_share, "libime"); + const std::string lua_path = fcitx::stringutils::concat( fcitx::stringutils::joinPath(data_home, "lua", "?.lua"), ";", fcitx::stringutils::joinPath(data_home, "lua", "?", "init.lua"), ";", fcitx::stringutils::joinPath(usr_share, "lua", "5.4", "?.lua"), ";", fcitx::stringutils::joinPath(usr_share, "lua", "5.4", "?", "init.lua"), ";", ";" // double semicolon, for default path defined in luaconf.h ); - std::string lua_cpath = fcitx::stringutils::concat( + const std::string lua_cpath = fcitx::stringutils::concat( fcitx::stringutils::joinPath(data_home, "lua", "?.so"), ";", fcitx::stringutils::joinPath(usr_share, "lua", "5.4", "?.so"), ";", ";" ); + // prevent StandardPath from resolving it's hardcoded installation path + setenv("SKIP_FCITX_PATH", "1", 1); // for fcitx default profile [DefaultInputMethod] setenv("LANG", lang_.c_str(), 1); // for libintl-lite loading gettext .mo translations @@ -545,7 +543,7 @@ Java_org_fcitx_fcitx5_android_core_Fcitx_startupFcitx(JNIEnv *env, jclass clazz, fcitx::registerDomain("fcitx5-chinese-addons", locale_dir_char); fcitx::registerDomain("fcitx5-android", locale_dir_char); - int extDomainsSize = env->GetArrayLength(extDomains); + const int extDomainsSize = env->GetArrayLength(extDomains); for (int i = 0; i < extDomainsSize; i++) { auto domain = JRef(env, env->GetObjectArrayElement(extDomains, i)); fcitx::registerDomain(CString(env, domain), locale_dir_char); @@ -604,8 +602,9 @@ Java_org_fcitx_fcitx5_android_core_Fcitx_startupFcitx(JNIEnv *env, jclass clazz, auto imChangeCallback = []() { auto env = GlobalRef->AttachEnv(); auto vararg = JRef(env, env->NewObjectArray(1, GlobalRef->Object, nullptr)); - const auto status = Fcitx::Instance().inputMethodStatus(); - auto obj = JRef(env, fcitxInputMethodStatusToJObject(env, status)); + std::unique_ptr status = Fcitx::Instance().inputMethodStatus(); + if (!status) return; + auto obj = JRef(env, fcitxInputMethodStatusToJObject(env, *status)); env->SetObjectArrayElement(vararg, 0, obj); env->CallStaticVoidMethod(GlobalRef->Fcitx, GlobalRef->HandleFcitxEvent, 6, *vararg); }; @@ -684,8 +683,8 @@ extern "C" JNIEXPORT void JNICALL Java_org_fcitx_fcitx5_android_core_Fcitx_sendKeyToFcitxChar(JNIEnv *env, jclass clazz, jchar c, jint state, jboolean up, jint timestamp) { RETURN_IF_NOT_RUNNING - fcitx::Key parsedKey{fcitx::Key::keySymFromString((const char *) &c), - fcitx::KeyStates(static_cast(state))}; + const fcitx::Key parsedKey{fcitx::Key::keySymFromString(reinterpret_cast(&c)), + fcitx::KeyStates(static_cast(state))}; Fcitx::Instance().sendKey(parsedKey, up, timestamp); } @@ -753,8 +752,9 @@ extern "C" JNIEXPORT jobject JNICALL Java_org_fcitx_fcitx5_android_core_Fcitx_inputMethodStatus(JNIEnv *env, jclass clazz) { RETURN_VALUE_IF_NOT_RUNNING(nullptr) - const auto &status = Fcitx::Instance().inputMethodStatus(); - return fcitxInputMethodStatusToJObject(env, status); + auto status = Fcitx::Instance().inputMethodStatus(); + if (!status) return nullptr; + return fcitxInputMethodStatusToJObject(env, *status); } extern "C" diff --git a/app/src/main/cpp/object-conversion.h b/app/src/main/cpp/object-conversion.h index a84840fde..88454d7ee 100644 --- a/app/src/main/cpp/object-conversion.h +++ b/app/src/main/cpp/object-conversion.h @@ -31,16 +31,14 @@ jobjectArray fcitxInputMethodEntriesToJObjectArray(JNIEnv *env, const std::vecto } jobject fcitxInputMethodStatusToJObject(JNIEnv *env, const InputMethodStatus &status) { - const auto entry = status.entry; - if (status.subMode.empty()) return fcitxInputMethodEntryToJObject(env, entry); return env->NewObject(GlobalRef->InputMethodEntry, GlobalRef->InputMethodEntryInitWithSubMode, - *JString(env, entry->uniqueName()), - *JString(env, entry->name()), - *JString(env, entry->icon()), - *JString(env, entry->nativeName()), - *JString(env, entry->label()), - *JString(env, entry->languageCode()), - entry->isConfigurable(), + *JString(env, status.uniqueName), + *JString(env, status.name), + *JString(env, status.icon), + *JString(env, status.nativeName), + *JString(env, status.label), + *JString(env, status.languageCode), + status.configurable, *JString(env, status.subMode), *JString(env, status.subModeLabel), *JString(env, status.subModeIcon) From 51adb977c81440d54279fb671f2ef1f3bf7c260f Mon Sep 17 00:00:00 2001 From: Rocka Date: Wed, 11 Oct 2023 21:55:20 +0800 Subject: [PATCH 048/381] Fix build --- lib/fcitx5-chinese-addons/src/main/cpp/fcitx5-chinese-addons | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/fcitx5-chinese-addons/src/main/cpp/fcitx5-chinese-addons b/lib/fcitx5-chinese-addons/src/main/cpp/fcitx5-chinese-addons index 65e5b0101..cfd66f8de 160000 --- a/lib/fcitx5-chinese-addons/src/main/cpp/fcitx5-chinese-addons +++ b/lib/fcitx5-chinese-addons/src/main/cpp/fcitx5-chinese-addons @@ -1 +1 @@ -Subproject commit 65e5b010164d1cb0f17791385bfb77b068ff18b0 +Subproject commit cfd66f8def0d75a0d79eb6a2191c2cf86e38b797 From 9d2304fa2cb370c01d029cda685d405a93cfa52c Mon Sep 17 00:00:00 2001 From: Rocka Date: Thu, 12 Oct 2023 20:27:59 +0800 Subject: [PATCH 049/381] Implement pinyin customphrase editor --- app/src/main/cpp/CMakeLists.txt | 7 +- app/src/main/cpp/jni-utils.h | 12 + app/src/main/cpp/native-lib.cpp | 64 ++++++ .../fcitx5/android/core/AddonSubconfig.kt | 4 +- .../org/fcitx/fcitx5/android/core/Fcitx.kt | 2 +- .../data/pinyin/CustomPhraseManager.kt | 11 + .../pinyin/customphrase/PinyinCustomPhrase.kt | 17 ++ .../settings/PinyinCustomPhraseFragment.kt | 217 ++++++++++++++++++ .../main/settings/PreferenceScreenFactory.kt | 14 ++ .../android/utils/config/ConfigDescriptor.kt | 2 + app/src/main/res/navigation/settings_nav.xml | 17 ++ 11 files changed, 364 insertions(+), 3 deletions(-) create mode 100644 app/src/main/java/org/fcitx/fcitx5/android/data/pinyin/CustomPhraseManager.kt create mode 100644 app/src/main/java/org/fcitx/fcitx5/android/data/pinyin/customphrase/PinyinCustomPhrase.kt create mode 100644 app/src/main/java/org/fcitx/fcitx5/android/ui/main/settings/PinyinCustomPhraseFragment.kt diff --git a/app/src/main/cpp/CMakeLists.txt b/app/src/main/cpp/CMakeLists.txt index 1b6946c8e..563bae607 100644 --- a/app/src/main/cpp/CMakeLists.txt +++ b/app/src/main/cpp/CMakeLists.txt @@ -45,10 +45,14 @@ find_package(Libevent) list(APPEND CMAKE_FIND_ROOT_PATH "${PREBUILT_DIR}/boost/${ANDROID_ABI}/lib/cmake") find_package(Boost 1.83.0 REQUIRED COMPONENTS headers filesystem iostreams CONFIG) -add_library(native-lib SHARED native-lib.cpp) +add_library(native-lib SHARED + native-lib.cpp + "${CMAKE_CURRENT_SOURCE_DIR}/../../../../lib/fcitx5-chinese-addons/src/main/cpp/fcitx5-chinese-addons/im/pinyin/customphrase.cpp" + ) target_link_libraries(native-lib log libevent::core + fmt::fmt-header-only Fcitx5::Utils Fcitx5::Config Fcitx5::Core @@ -56,6 +60,7 @@ target_link_libraries(native-lib Fcitx5::Module::Unicode Fcitx5::Module::Clipboard Boost::headers + Boost::iostreams LibIME::Pinyin LibIME::Table ) diff --git a/app/src/main/cpp/jni-utils.h b/app/src/main/cpp/jni-utils.h index 3037e2017..cd221eb1f 100644 --- a/app/src/main/cpp/jni-utils.h +++ b/app/src/main/cpp/jni-utils.h @@ -128,6 +128,12 @@ class GlobalRefSingleton { jclass FormattedText; jmethodID FormattedTextFromByteCursor; + jclass PinyinCustomPhrase; + jmethodID PinyinCustomPhraseInit; + jfieldID PinyinCustomPhraseKey; + jfieldID PinyinCustomPhraseOrder; + jfieldID PinyinCustomPhraseValue; + GlobalRefSingleton(JavaVM *jvm_) : jvm(jvm_) { JNIEnv *env; jvm->AttachCurrentThread(&env, nullptr); @@ -168,6 +174,12 @@ class GlobalRefSingleton { FormattedText = reinterpret_cast(env->NewGlobalRef(env->FindClass("org/fcitx/fcitx5/android/core/FormattedText"))); FormattedTextFromByteCursor = env->GetStaticMethodID(FormattedText, "fromByteCursor", "([Ljava/lang/String;[II)Lorg/fcitx/fcitx5/android/core/FormattedText;"); + + PinyinCustomPhrase = reinterpret_cast(env->NewGlobalRef(env->FindClass("org/fcitx/fcitx5/android/data/pinyin/customphrase/PinyinCustomPhrase"))); + PinyinCustomPhraseInit = env->GetMethodID(PinyinCustomPhrase, "", "(Ljava/lang/String;ILjava/lang/String;)V"); + PinyinCustomPhraseKey = env->GetFieldID(PinyinCustomPhrase, "key", "Ljava/lang/String;"); + PinyinCustomPhraseOrder = env->GetFieldID(PinyinCustomPhrase, "order", "I"); + PinyinCustomPhraseValue = env->GetFieldID(PinyinCustomPhrase, "value", "Ljava/lang/String;"); } const JEnv AttachEnv() const { return JEnv(jvm); } diff --git a/app/src/main/cpp/native-lib.cpp b/app/src/main/cpp/native-lib.cpp index 220e94e70..bbeff652d 100644 --- a/app/src/main/cpp/native-lib.cpp +++ b/app/src/main/cpp/native-lib.cpp @@ -31,6 +31,10 @@ #include #include +#include +#include +#include "../../../../lib/fcitx5-chinese-addons/src/main/cpp/fcitx5-chinese-addons/im/pinyin/customphrase.h" + #include "androidfrontend/androidfrontend_public.h" #include "jni-utils.h" #include "nativestreambuf.h" @@ -1057,4 +1061,64 @@ Java_org_fcitx_fcitx5_android_data_table_TableManager_checkTableDictFormat(JNIEn return JNI_TRUE; } +extern "C" +JNIEXPORT jobjectArray JNICALL +Java_org_fcitx_fcitx5_android_data_pinyin_CustomPhraseManager_load(JNIEnv *env, jclass clazz) { + auto fp = fcitx::StandardPath::global().open(fcitx::StandardPath::Type::PkgData, "pinyin/customphrase", O_RDONLY); + if (fp.fd() < 0) { + FCITX_INFO() << "cannot open pinyin/customphrase"; + return nullptr; + } + boost::iostreams::stream_buffer + buffer(fp.fd(), boost::iostreams::file_descriptor_flags::never_close_handle); + std::istream in(&buffer); + fcitx::CustomPhraseDict dict; + dict.load(in, true); + int size = 0; + dict.foreach([&](const std::string &key, std::vector &items) { + FCITX_UNUSED(key); + size += static_cast(items.size()); + }); + int i = 0; + jobjectArray array = env->NewObjectArray(size, GlobalRef->PinyinCustomPhrase, nullptr); + dict.foreach([&](const std::string &key, std::vector &items) { + for (const auto &item: items) { + env->SetObjectArrayElement(array, i++, + JRef(env, env->NewObject(GlobalRef->PinyinCustomPhrase, GlobalRef->PinyinCustomPhraseInit, + *JString(env, key), + item.order(), + *JString(env, item.value()) + ) + ) + ); + } + }); + return array; +} + +extern "C" +JNIEXPORT void JNICALL +Java_org_fcitx_fcitx5_android_data_pinyin_CustomPhraseManager_save(JNIEnv *env, jclass clazz, jobjectArray items) { + fcitx::CustomPhraseDict dict; + const int size = env->GetArrayLength(items); + for (int i = 0; i < size; i++) { + auto phrase = JRef(env, env->GetObjectArrayElement(items, i)); + auto phraseKey = JRef(env, env->GetObjectField(phrase, GlobalRef->PinyinCustomPhraseKey)); + auto phraseOrder = env->GetIntField(phrase, GlobalRef->PinyinCustomPhraseOrder); + auto phraseValue = JRef(env, env->GetObjectField(phrase, GlobalRef->PinyinCustomPhraseValue)); + dict.addPhrase(*CString(env, phraseKey), + *CString(env, phraseValue), + static_cast(phraseOrder)); + } + fcitx::StandardPath::global().safeSave( + fcitx::StandardPath::Type::PkgData, "pinyin/customphrase", + [&](int fd) { + boost::iostreams::stream_buffer + buffer(fd, boost::iostreams::file_descriptor_flags::never_close_handle); + std::ostream out(&buffer); + dict.save(out); + return true; + }); +} + #pragma GCC diagnostic pop diff --git a/app/src/main/java/org/fcitx/fcitx5/android/core/AddonSubconfig.kt b/app/src/main/java/org/fcitx/fcitx5/android/core/AddonSubconfig.kt index 36e7b23b1..c57e429ee 100644 --- a/app/src/main/java/org/fcitx/fcitx5/android/core/AddonSubconfig.kt +++ b/app/src/main/java/org/fcitx/fcitx5/android/core/AddonSubconfig.kt @@ -8,4 +8,6 @@ suspend fun FcitxAPI.getPunctuationConfig(lang: String) = suspend fun FcitxAPI.savePunctuationConfig(lang: String = "zh_CN", config: RawConfig) = setAddonSubConfig("punctuation", "punctuationmap/$lang", config) -suspend fun FcitxAPI.reloadQuickPhrase() = setAddonSubConfig("quickphrase", "editor") \ No newline at end of file +suspend fun FcitxAPI.reloadQuickPhrase() = setAddonSubConfig("quickphrase", "editor") + +suspend fun FcitxAPI.reloadPinyinCustomPhrase() = setAddonSubConfig("pinyin", "customphrase") diff --git a/app/src/main/java/org/fcitx/fcitx5/android/core/Fcitx.kt b/app/src/main/java/org/fcitx/fcitx5/android/core/Fcitx.kt index 86dc9950f..ad16a1cff 100644 --- a/app/src/main/java/org/fcitx/fcitx5/android/core/Fcitx.kt +++ b/app/src/main/java/org/fcitx/fcitx5/android/core/Fcitx.kt @@ -429,7 +429,7 @@ class Fcitx(private val context: Context) : FcitxAPI, FcitxLifecycleOwner { }) - private suspend fun withFcitxContext(block: suspend () -> T): T = + private suspend inline fun withFcitxContext(crossinline block: suspend () -> T): T = withContext(dispatcher) { block() } diff --git a/app/src/main/java/org/fcitx/fcitx5/android/data/pinyin/CustomPhraseManager.kt b/app/src/main/java/org/fcitx/fcitx5/android/data/pinyin/CustomPhraseManager.kt new file mode 100644 index 000000000..96c502cc4 --- /dev/null +++ b/app/src/main/java/org/fcitx/fcitx5/android/data/pinyin/CustomPhraseManager.kt @@ -0,0 +1,11 @@ +package org.fcitx.fcitx5.android.data.pinyin + +import org.fcitx.fcitx5.android.data.pinyin.customphrase.PinyinCustomPhrase + +object CustomPhraseManager { + @JvmStatic + external fun load(): Array + + @JvmStatic + external fun save(items: Array) +} diff --git a/app/src/main/java/org/fcitx/fcitx5/android/data/pinyin/customphrase/PinyinCustomPhrase.kt b/app/src/main/java/org/fcitx/fcitx5/android/data/pinyin/customphrase/PinyinCustomPhrase.kt new file mode 100644 index 000000000..8c5f11e1d --- /dev/null +++ b/app/src/main/java/org/fcitx/fcitx5/android/data/pinyin/customphrase/PinyinCustomPhrase.kt @@ -0,0 +1,17 @@ +package org.fcitx.fcitx5.android.data.pinyin.customphrase + +import kotlin.math.absoluteValue + +data class PinyinCustomPhrase( + var key: String, + var order: Int, + var value: String +) { + var enabled: Boolean + get() = order > 0 + set(newValue) { + order = (if (newValue) 1 else -1) * order.absoluteValue + } + + fun serialize() = "$key,${order.absoluteValue}=$value" +} diff --git a/app/src/main/java/org/fcitx/fcitx5/android/ui/main/settings/PinyinCustomPhraseFragment.kt b/app/src/main/java/org/fcitx/fcitx5/android/ui/main/settings/PinyinCustomPhraseFragment.kt new file mode 100644 index 000000000..5bb729d67 --- /dev/null +++ b/app/src/main/java/org/fcitx/fcitx5/android/ui/main/settings/PinyinCustomPhraseFragment.kt @@ -0,0 +1,217 @@ +package org.fcitx.fcitx5.android.ui.main.settings + +import android.app.AlertDialog +import android.os.Bundle +import android.text.InputFilter +import android.text.InputType +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import android.view.inputmethod.EditorInfo +import androidx.fragment.app.Fragment +import androidx.fragment.app.activityViewModels +import androidx.lifecycle.lifecycleScope +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.NonCancellable +import kotlinx.coroutines.launch +import org.fcitx.fcitx5.android.core.reloadPinyinCustomPhrase +import org.fcitx.fcitx5.android.data.pinyin.CustomPhraseManager +import org.fcitx.fcitx5.android.data.pinyin.customphrase.PinyinCustomPhrase +import org.fcitx.fcitx5.android.ui.common.BaseDynamicListUi +import org.fcitx.fcitx5.android.ui.common.OnItemChangedListener +import org.fcitx.fcitx5.android.ui.main.MainViewModel +import org.fcitx.fcitx5.android.utils.NaiveDustman +import org.fcitx.fcitx5.android.utils.materialTextInput +import org.fcitx.fcitx5.android.utils.str +import splitties.views.dsl.core.add +import splitties.views.dsl.core.lParams +import splitties.views.dsl.core.matchParent +import splitties.views.dsl.core.verticalLayout +import splitties.views.setPaddingDp +import kotlin.math.absoluteValue +import kotlin.math.min + +class PinyinCustomPhraseFragment : Fragment(), OnItemChangedListener { + + private val viewModel: MainViewModel by activityViewModels() + + private lateinit var ui: BaseDynamicListUi + + private val dustman = NaiveDustman() + + private val initialItems = CustomPhraseManager.load() + + private var keyLabel = KEY + private var orderLabel = ORDER + private var phraseLabel = PHRASE + + override fun onCreateView( + inflater: LayoutInflater, + container: ViewGroup?, + savedInstanceState: Bundle? + ): View { + lifecycleScope.launch { + viewModel.fcitx.runOnReady { + keyLabel = translate(KEY, CHINESE_ADDONS_DOMAIN) + orderLabel = translate(ORDER, CHINESE_ADDONS_DOMAIN) + phraseLabel = translate(PHRASE, CHINESE_ADDONS_DOMAIN) + } + } + val initialEntries = CustomPhraseManager.load().toList() + ui = object : BaseDynamicListUi( + requireContext(), + Mode.FreeAdd("", converter = { PinyinCustomPhrase("", 1, "") }), + initialItems.toList(), + enableOrder = true, + initCheckBox = { entry -> + setOnCheckedChangeListener(null) + isChecked = entry.enabled + setOnCheckedChangeListener { _, checked -> + ui.updateItem(ui.indexItem(entry), entry.copy().apply { enabled = checked }) + } + } + ) { + override fun showEntry(x: PinyinCustomPhrase): String { + val s = x.serialize() + val firstLF = s.indexOf('\n') + val endIndex = min(if (firstLF > 0) firstLF else s.length, 20) + return if (endIndex == s.length) { + s + } else { + s.substring(0, endIndex) + "…" + } + } + + override fun showEditDialog( + title: String, + entry: PinyinCustomPhrase?, + block: (PinyinCustomPhrase) -> Unit + ) { + val (keyLayout, keyField) = materialTextInput { + hint = keyLabel + } + keyField.apply { + isSingleLine = true + filters = arrayOf( + InputFilter { source, _, _, _, _, _ -> + source.filter { it.code in 'A'.code..'Z'.code || it.code in 'a'.code..'z'.code } + } + ) + imeOptions = EditorInfo.IME_ACTION_NEXT + inputType = + InputType.TYPE_CLASS_TEXT or InputType.TYPE_TEXT_VARIATION_NORMAL or InputType.TYPE_TEXT_FLAG_NO_SUGGESTIONS + } + val (orderLayout, orderField) = materialTextInput { + hint = orderLabel + } + orderField.apply { + inputType = + InputType.TYPE_CLASS_NUMBER or InputType.TYPE_NUMBER_VARIATION_NORMAL or InputType.TYPE_NUMBER_FLAG_SIGNED + imeOptions = EditorInfo.IME_ACTION_NEXT + } + val (phraseLayout, phraseField) = materialTextInput { + hint = phraseLabel + } + entry?.apply { + keyField.setText(key) + orderField.setText(order.absoluteValue.toString()) + phraseField.setText(value) + } + val layout = verticalLayout { + setPaddingDp(20, 10, 20, 0) + add(keyLayout, lParams(matchParent)) + add(orderLayout, lParams(matchParent)) + add(phraseLayout, lParams(matchParent)) + } + AlertDialog.Builder(context) + .setTitle(title) + .setView(layout) + .setPositiveButton(android.R.string.ok) { _, _ -> + block( + PinyinCustomPhrase( + keyField.str, + orderField.str.toIntOrNull() ?: 1, + phraseField.str + ) + ) + } + .setNegativeButton(android.R.string.cancel, null) + .show() + } + } + ui.addOnItemChangedListener(this) + ui.addTouchCallback() + resetDustman() + ui.setViewModel(viewModel) + viewModel.enableToolbarEditButton(initialEntries.isNotEmpty()) { + ui.enterMultiSelect(requireActivity().onBackPressedDispatcher) + } + return ui.root + } + + override fun onItemAdded(idx: Int, item: PinyinCustomPhrase) { + dustman.addOrUpdate(item.serialize(), item) + } + + override fun onItemRemoved(idx: Int, item: PinyinCustomPhrase) { + dustman.remove(item.serialize()) + } + + override fun onItemRemovedBatch(indexed: List>) { + batchRemove(indexed) + } + + override fun onItemUpdated(idx: Int, old: PinyinCustomPhrase, new: PinyinCustomPhrase) { + dustman.remove(old.serialize()) + dustman.addOrUpdate(new.serialize(), new) + } + + private fun saveConfig() { + if (!dustman.dirty) return + resetDustman() + lifecycleScope.launch(NonCancellable + Dispatchers.IO) { + CustomPhraseManager.save(ui.entries.toTypedArray()) + viewModel.fcitx.runOnReady { + reloadPinyinCustomPhrase() + } + } + } + + private fun resetDustman() { + dustman.reset(ui.entries.associateBy { it.serialize() }) + } + + override fun onStart() { + super.onStart() + lifecycleScope.launch { + val title = viewModel.fcitx.runOnReady { + translate(MANAGE_CUSTOM_PHRASE, CHINESE_ADDONS_DOMAIN) + } + viewModel.setToolbarTitle(title) + } + viewModel.enableToolbarEditButton(ui.entries.isNotEmpty()) { + ui.enterMultiSelect(requireActivity().onBackPressedDispatcher) + } + } + + override fun onStop() { + saveConfig() + ui.exitMultiSelect() + viewModel.disableToolbarEditButton() + super.onStop() + } + + override fun onDestroy() { + ui.removeItemChangedListener() + super.onDestroy() + } + + companion object { + const val CHINESE_ADDONS_DOMAIN = "fcitx5-chinese-addons" + const val KEY = "Key" + const val ORDER = "Order" + const val PHRASE = "Phrase" + const val MANAGE_CUSTOM_PHRASE = "Manage Custom Phrase" + } + +} diff --git a/app/src/main/java/org/fcitx/fcitx5/android/ui/main/settings/PreferenceScreenFactory.kt b/app/src/main/java/org/fcitx/fcitx5/android/ui/main/settings/PreferenceScreenFactory.kt index d0b69783e..5c5d265fd 100644 --- a/app/src/main/java/org/fcitx/fcitx5/android/ui/main/settings/PreferenceScreenFactory.kt +++ b/app/src/main/java/org/fcitx/fcitx5/android/ui/main/settings/PreferenceScreenFactory.kt @@ -143,6 +143,19 @@ object PreferenceScreenFactory { } } + fun pinyinCustomPhrase() = Preference(context).apply { + setOnPreferenceClickListener { + val currentFragment = fragmentManager.findFragmentById(R.id.nav_host_fragment)!! + val action = when (currentFragment) { + is AddonConfigFragment -> R.id.action_addonConfigFragment_to_pinyinCustomPhraseFragment + is InputMethodConfigFragment -> R.id.action_imConfigFragment_to_pinyinCustomPhraseFragment + else -> throw IllegalStateException("Can not navigate to custom phrase editor from current fragment") + } + currentFragment.findNavController().navigate(action) + true + } + } + fun listPreference(subtype: ConfigType<*>): Preference = object : Preference(context) { override fun onClick() { val currentFragment = fragmentManager.findFragmentById(R.id.nav_host_fragment)!! @@ -222,6 +235,7 @@ object PreferenceScreenFactory { ConfigExternal.ETy.Chttrans -> addonConfigPreference("chttrans") ConfigExternal.ETy.TableGlobal -> addonConfigPreference("table") ConfigExternal.ETy.AndroidTable -> tableInputMethod() + ConfigExternal.ETy.PinyinCustomPhrase -> pinyinCustomPhrase() else -> stubPreference() } is ConfigInt -> { diff --git a/app/src/main/java/org/fcitx/fcitx5/android/utils/config/ConfigDescriptor.kt b/app/src/main/java/org/fcitx/fcitx5/android/utils/config/ConfigDescriptor.kt index f9fa3b209..3d07ac4bf 100644 --- a/app/src/main/java/org/fcitx/fcitx5/android/utils/config/ConfigDescriptor.kt +++ b/app/src/main/java/org/fcitx/fcitx5/android/utils/config/ConfigDescriptor.kt @@ -146,6 +146,7 @@ sealed class ConfigDescriptor : Parcelable { QuickPhrase, Chttrans, TableGlobal, + PinyinCustomPhrase, // manually added on Android side for TableManager AndroidTable @@ -267,6 +268,7 @@ sealed class ConfigDescriptor : Parcelable { "QuickPhrase", "Editor" -> ConfigExternal.ETy.QuickPhrase "Chttrans" -> ConfigExternal.ETy.Chttrans "TableGlobal" -> ConfigExternal.ETy.TableGlobal + "CustomPhrase" -> ConfigExternal.ETy.PinyinCustomPhrase "AndroidTable" -> ConfigExternal.ETy.AndroidTable else -> null } diff --git a/app/src/main/res/navigation/settings_nav.xml b/app/src/main/res/navigation/settings_nav.xml index 584876479..ab8f54899 100644 --- a/app/src/main/res/navigation/settings_nav.xml +++ b/app/src/main/res/navigation/settings_nav.xml @@ -156,6 +156,13 @@ app:exitAnim="@animator/nav_default_exit_anim" app:popEnterAnim="@animator/nav_default_pop_enter_anim" app:popExitAnim="@animator/nav_default_pop_exit_anim" /> + + + \ No newline at end of file From fbec531a868a1b1828f43b0512e437d57f6b5717 Mon Sep 17 00:00:00 2001 From: Rocka Date: Thu, 12 Oct 2023 21:15:15 +0800 Subject: [PATCH 050/381] Fix saving quickphrase with newline character --- .../data/quickphrase/QuickPhraseData.kt | 28 ++++++++++--------- .../data/quickphrase/QuickPhraseEntry.kt | 2 +- .../main/settings/QuickPhraseEditFragment.kt | 13 ++++++--- 3 files changed, 25 insertions(+), 18 deletions(-) diff --git a/app/src/main/java/org/fcitx/fcitx5/android/data/quickphrase/QuickPhraseData.kt b/app/src/main/java/org/fcitx/fcitx5/android/data/quickphrase/QuickPhraseData.kt index 3c108e1d3..17a98a835 100644 --- a/app/src/main/java/org/fcitx/fcitx5/android/data/quickphrase/QuickPhraseData.kt +++ b/app/src/main/java/org/fcitx/fcitx5/android/data/quickphrase/QuickPhraseData.kt @@ -3,22 +3,24 @@ package org.fcitx.fcitx5.android.data.quickphrase import org.fcitx.fcitx5.android.R import org.fcitx.fcitx5.android.utils.errorRuntime -class QuickPhraseData(private val data: List) : - List by data { +class QuickPhraseData(private val data: List) : List by data { fun serialize(): String = joinToString("\n") { it.serialize() } companion object { - fun fromLines(lines: List): Result = - runCatching { - lines.filter { it.isNotBlank() } - .map { - val s = it.trim() - val spaceIndex = s.indexOf(' ') - if (spaceIndex < 0) - errorRuntime(R.string.exception_quickphrase_parse, it) - QuickPhraseEntry(s.substring(0, spaceIndex), s.substring(spaceIndex + 1)) - } - }.map { QuickPhraseData(it) } + fun fromLines(lines: List): Result { + return runCatching { + val list = mutableListOf() + lines.forEach { + if (it.isBlank()) return@forEach + val s = it.trim() + val sep = s.indexOf(' ') + if (sep < 0) + errorRuntime(R.string.exception_quickphrase_parse, it) + list.add(QuickPhraseEntry(s.substring(0, sep), s.substring(sep + 1))) + } + QuickPhraseData(list) + } + } } } \ No newline at end of file diff --git a/app/src/main/java/org/fcitx/fcitx5/android/data/quickphrase/QuickPhraseEntry.kt b/app/src/main/java/org/fcitx/fcitx5/android/data/quickphrase/QuickPhraseEntry.kt index 0034ba30a..8cee2bb9d 100644 --- a/app/src/main/java/org/fcitx/fcitx5/android/data/quickphrase/QuickPhraseEntry.kt +++ b/app/src/main/java/org/fcitx/fcitx5/android/data/quickphrase/QuickPhraseEntry.kt @@ -1,5 +1,5 @@ package org.fcitx.fcitx5.android.data.quickphrase data class QuickPhraseEntry(val keyword: String, val phrase: String) { - fun serialize() = "$keyword $phrase" + fun serialize() = "$keyword ${phrase.replace("\n", "\\n")}" } \ No newline at end of file diff --git a/app/src/main/java/org/fcitx/fcitx5/android/ui/main/settings/QuickPhraseEditFragment.kt b/app/src/main/java/org/fcitx/fcitx5/android/ui/main/settings/QuickPhraseEditFragment.kt index 86467d985..3fbf4922e 100644 --- a/app/src/main/java/org/fcitx/fcitx5/android/ui/main/settings/QuickPhraseEditFragment.kt +++ b/app/src/main/java/org/fcitx/fcitx5/android/ui/main/settings/QuickPhraseEditFragment.kt @@ -2,6 +2,7 @@ package org.fcitx.fcitx5.android.ui.main.settings import android.app.AlertDialog import android.view.View +import android.view.inputmethod.EditorInfo import androidx.core.os.bundleOf import androidx.lifecycle.lifecycleScope import kotlinx.coroutines.Dispatchers @@ -42,6 +43,10 @@ class QuickPhraseEditFragment : ProgressFragment(), OnItemChangedListener Date: Thu, 12 Oct 2023 23:15:26 +0800 Subject: [PATCH 051/381] Fix crash in PinyinCustomPhraseFragment --- app/proguard-rules.pro | 9 +++++++++ .../android/data/pinyin/CustomPhraseManager.kt | 2 +- .../pinyin/customphrase/PinyinCustomPhrase.kt | 16 ++++++++-------- .../main/settings/PinyinCustomPhraseFragment.kt | 6 +++--- 4 files changed, 21 insertions(+), 12 deletions(-) diff --git a/app/proguard-rules.pro b/app/proguard-rules.pro index 4ef03d96c..a853cde43 100644 --- a/app/proguard-rules.pro +++ b/app/proguard-rules.pro @@ -10,6 +10,9 @@ # Keep JNI interface -keep class org.fcitx.fcitx5.android.core.* { *; } +-keep class org.fcitx.fcitx5.android.data.pinyin.customphrase.PinyinCustomPhrase { + public (...); +} # Keep dependency magic -keep class ** extends org.mechdancer.dependency.Component { @@ -17,6 +20,12 @@ boolean equals(java.lang.Object); } +# remove kotlin null checks +-assumenosideeffects class kotlin.jvm.internal.Intrinsics { + static void checkParameterIsNotNull(java.lang.Object, java.lang.String); + static void checkNotNullParameter(java.lang.Object, java.lang.String); +} + # Uncomment this to preserve the line number information for # debugging stack traces. -keepattributes SourceFile,LineNumberTable diff --git a/app/src/main/java/org/fcitx/fcitx5/android/data/pinyin/CustomPhraseManager.kt b/app/src/main/java/org/fcitx/fcitx5/android/data/pinyin/CustomPhraseManager.kt index 96c502cc4..a1137c7f2 100644 --- a/app/src/main/java/org/fcitx/fcitx5/android/data/pinyin/CustomPhraseManager.kt +++ b/app/src/main/java/org/fcitx/fcitx5/android/data/pinyin/CustomPhraseManager.kt @@ -4,7 +4,7 @@ import org.fcitx.fcitx5.android.data.pinyin.customphrase.PinyinCustomPhrase object CustomPhraseManager { @JvmStatic - external fun load(): Array + external fun load(): Array? @JvmStatic external fun save(items: Array) diff --git a/app/src/main/java/org/fcitx/fcitx5/android/data/pinyin/customphrase/PinyinCustomPhrase.kt b/app/src/main/java/org/fcitx/fcitx5/android/data/pinyin/customphrase/PinyinCustomPhrase.kt index 8c5f11e1d..cea619cbc 100644 --- a/app/src/main/java/org/fcitx/fcitx5/android/data/pinyin/customphrase/PinyinCustomPhrase.kt +++ b/app/src/main/java/org/fcitx/fcitx5/android/data/pinyin/customphrase/PinyinCustomPhrase.kt @@ -3,15 +3,15 @@ package org.fcitx.fcitx5.android.data.pinyin.customphrase import kotlin.math.absoluteValue data class PinyinCustomPhrase( - var key: String, - var order: Int, - var value: String + val key: String, + val order: Int, + val value: String ) { - var enabled: Boolean - get() = order > 0 - set(newValue) { - order = (if (newValue) 1 else -1) * order.absoluteValue - } + val enabled: Boolean get() = order > 0 + + fun copyEnabled(e: Boolean): PinyinCustomPhrase { + return copy(order = (if (e) 1 else -1) * order.absoluteValue) + } fun serialize() = "$key,${order.absoluteValue}=$value" } diff --git a/app/src/main/java/org/fcitx/fcitx5/android/ui/main/settings/PinyinCustomPhraseFragment.kt b/app/src/main/java/org/fcitx/fcitx5/android/ui/main/settings/PinyinCustomPhraseFragment.kt index 5bb729d67..a0d09588d 100644 --- a/app/src/main/java/org/fcitx/fcitx5/android/ui/main/settings/PinyinCustomPhraseFragment.kt +++ b/app/src/main/java/org/fcitx/fcitx5/android/ui/main/settings/PinyinCustomPhraseFragment.kt @@ -39,7 +39,7 @@ class PinyinCustomPhraseFragment : Fragment(), OnItemChangedListener() - private val initialItems = CustomPhraseManager.load() + private val initialItems = CustomPhraseManager.load() ?: emptyArray() private var keyLabel = KEY private var orderLabel = ORDER @@ -57,7 +57,7 @@ class PinyinCustomPhraseFragment : Fragment(), OnItemChangedListener( requireContext(), Mode.FreeAdd("", converter = { PinyinCustomPhrase("", 1, "") }), @@ -67,7 +67,7 @@ class PinyinCustomPhraseFragment : Fragment(), OnItemChangedListener - ui.updateItem(ui.indexItem(entry), entry.copy().apply { enabled = checked }) + ui.updateItem(ui.indexItem(entry), entry.copyEnabled(checked)) } } ) { From 879137ac17c1cc0b8242972ade239232da92fb11 Mon Sep 17 00:00:00 2001 From: Rocka Date: Thu, 12 Oct 2023 23:19:49 +0800 Subject: [PATCH 052/381] Use CapabilityFlag to determine commitStringWithCursor availability --- .../main/cpp/androidkeyboard/androidkeyboard.cpp | 4 ++-- .../fcitx/fcitx5/android/core/CapabilityFlag.kt | 16 ++++++++++++++-- 2 files changed, 16 insertions(+), 4 deletions(-) diff --git a/app/src/main/cpp/androidkeyboard/androidkeyboard.cpp b/app/src/main/cpp/androidkeyboard/androidkeyboard.cpp index 28fccaf63..8639f6206 100644 --- a/app/src/main/cpp/androidkeyboard/androidkeyboard.cpp +++ b/app/src/main/cpp/androidkeyboard/androidkeyboard.cpp @@ -316,8 +316,8 @@ void AndroidKeyboardEngine::commitBuffer(InputContext *inputContext) { if (preedit.empty()) { return; } - if (auto icv2 = dynamic_cast(inputContext)) { - icv2->commitStringWithCursor(preedit, cursor); + if (inputContext->capabilityFlags().test(CapabilityFlag::CommitStringWithCursor)) { + inputContext->commitStringWithCursor(preedit, cursor); } else { inputContext->commitString(preedit); } diff --git a/app/src/main/java/org/fcitx/fcitx5/android/core/CapabilityFlag.kt b/app/src/main/java/org/fcitx/fcitx5/android/core/CapabilityFlag.kt index 6f58477a9..461df91db 100644 --- a/app/src/main/java/org/fcitx/fcitx5/android/core/CapabilityFlag.kt +++ b/app/src/main/java/org/fcitx/fcitx5/android/core/CapabilityFlag.kt @@ -6,7 +6,7 @@ import splitties.bitflags.hasFlag /** * translated from - * [fcitx-utils/capabilityflags.h](https://github.com/fcitx/fcitx5/blob/5.0.13/src/lib/fcitx-utils/capabilityflags.h) + * [fcitx-utils/capabilityflags.h](https://github.com/fcitx/fcitx5/blob/5.1.1/src/lib/fcitx-utils/capabilityflags.h) */ @Suppress("unused") enum class CapabilityFlag(val flag: ULong) { @@ -60,6 +60,17 @@ enum class CapabilityFlag(val flag: ULong) { */ ClientSideInputPanel(1UL shl 39), + /** + * Whether client request input method to be disabled. + * Usually this means only allow to type with raw keyboard. + */ + Disable(1UL shl 40), + + /** + * Whether client support commit string with cursor location. + */ + CommitStringWithCursor(1UL shl 41), + PasswordOrSensitive(Password.flag or Sensitive.flag); } @@ -75,7 +86,8 @@ value class CapabilityFlags constructor(val flags: ULong) { val DefaultFlags = CapabilityFlags( CapabilityFlag.Preedit, - CapabilityFlag.ClientUnfocusCommit + CapabilityFlag.ClientUnfocusCommit, + CapabilityFlag.CommitStringWithCursor ) fun fromEditorInfo(info: EditorInfo): CapabilityFlags { From 316b400d31b65adcdc83f1cad7fb6250cfec8652 Mon Sep 17 00:00:00 2001 From: Potato Hatsue <1793913507@qq.com> Date: Sun, 15 Oct 2023 06:49:21 -0400 Subject: [PATCH 053/381] Use fcitx's ini parser (#355) Co-authored-by: Rocka --- app/build.gradle.kts | 1 - app/src/main/cpp/native-lib.cpp | 27 +++ .../org/fcitx/fcitx5/android/core/Types.kt | 14 ++ .../data/table/TableBasedInputMethod.kt | 31 ++- .../fcitx5/android/data/table/TableManager.kt | 3 +- .../org/fcitx/fcitx5/android/utils/Ini.kt | 204 +++--------------- gradle/libs.versions.toml | 1 - 7 files changed, 89 insertions(+), 192 deletions(-) diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 131003125..734580a10 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -115,7 +115,6 @@ dependencies { implementation(libs.androidx.room.ktx) implementation(libs.androidx.room.paging) implementation(libs.androidx.viewpager2) - implementation(libs.konbini) implementation(libs.material) implementation(libs.arrow) implementation(libs.imagecropper) diff --git a/app/src/main/cpp/native-lib.cpp b/app/src/main/cpp/native-lib.cpp index bbeff652d..7a0063891 100644 --- a/app/src/main/cpp/native-lib.cpp +++ b/app/src/main/cpp/native-lib.cpp @@ -23,6 +23,7 @@ #include #include #include +#include #include #include @@ -1121,4 +1122,30 @@ Java_org_fcitx_fcitx5_android_data_pinyin_CustomPhraseManager_save(JNIEnv *env, }); } +extern "C" +JNIEXPORT jobject JNICALL +Java_org_fcitx_fcitx5_android_utils_Ini_readFromIni(JNIEnv *env, jclass clazz, jstring src) { + fcitx::RawConfig config; + FILE *fp = std::fopen(*CString(env, src), "rb"); + if (!fp) { + return nullptr; + } + fcitx::readFromIni(config, fp); + std::fclose(fp); + return fcitxRawConfigToJObject(env, config); +} + +extern "C" +JNIEXPORT void JNICALL +Java_org_fcitx_fcitx5_android_utils_Ini_writeAsIni(JNIEnv *env, jclass clazz, jstring dest, jobject value) { + FILE *fp = std::fopen(*CString(env, dest), "wb"); + if (!fp) { + throwJavaException(env, "Unable to open file"); + return; + } + auto config = jobjectToRawConfig(env, value); + fcitx::writeAsIni(config, fp); + std::fclose(fp); +} + #pragma GCC diagnostic pop diff --git a/app/src/main/java/org/fcitx/fcitx5/android/core/Types.kt b/app/src/main/java/org/fcitx/fcitx5/android/core/Types.kt index 74755093f..969671a2d 100644 --- a/app/src/main/java/org/fcitx/fcitx5/android/core/Types.kt +++ b/app/src/main/java/org/fcitx/fcitx5/android/core/Types.kt @@ -83,6 +83,20 @@ data class RawConfig( return subItems?.find { it.name == name } } + fun getOrCreate(name: String): RawConfig { + val items = subItems + return if (items == null) { + RawConfig(name, "", "", null).also { + subItems = arrayOf(it) + } + } else { + items.find { it.name == name } + ?: RawConfig(name, "", "", null).also { + subItems = items + it + } + } + } + /** * generated by Android Studio */ diff --git a/app/src/main/java/org/fcitx/fcitx5/android/data/table/TableBasedInputMethod.kt b/app/src/main/java/org/fcitx/fcitx5/android/data/table/TableBasedInputMethod.kt index 83f6ba73d..fcc54bdf3 100644 --- a/app/src/main/java/org/fcitx/fcitx5/android/data/table/TableBasedInputMethod.kt +++ b/app/src/main/java/org/fcitx/fcitx5/android/data/table/TableBasedInputMethod.kt @@ -1,47 +1,44 @@ package org.fcitx.fcitx5.android.data.table -import cc.ekblad.konbini.ParserResult import org.fcitx.fcitx5.android.R +import org.fcitx.fcitx5.android.core.RawConfig import org.fcitx.fcitx5.android.data.table.dict.LibIMEDictionary -import org.fcitx.fcitx5.android.utils.IniParser -import org.fcitx.fcitx5.android.utils.IniPrettyPrinter +import org.fcitx.fcitx5.android.utils.Ini import org.fcitx.fcitx5.android.utils.Locales import org.fcitx.fcitx5.android.utils.errorRuntime -import org.fcitx.fcitx5.android.utils.getValue import timber.log.Timber import java.io.File class TableBasedInputMethod(val file: File) { - private var ini = when (val x = IniParser.parse(file.readText())) { - is ParserResult.Error -> errorRuntime(R.string.invalid_im, file.name) - is ParserResult.Ok -> x.result - } + private var ini = Ini.parseIniFromFile(file) ?: errorRuntime(R.string.invalid_im, file.name) var table: LibIMEDictionary? = null val name: String by lazy { - ini.sections[InputMethod]?.let { im -> - val properties = im.data - properties.getValue(NameI18n.format(Locales.languageWithCountry)) - ?: properties.getValue(NameI18n.format(Locales.language)) - ?: properties.getValue(Name) - } ?: errorRuntime(R.string.invalid_im, ERROR_MISSING_INPUT_METHOD_OR_NAME) + ini.get(InputMethod)?.let { + (it.get(NameI18n.format(Locales.languageWithCountry)) + ?: it.get(NameI18n.format(Locales.language)) + ?: it.get(Name))?.value + } ?: errorRuntime( + R.string.invalid_im, + ERROR_MISSING_INPUT_METHOD_OR_NAME + ) } var tableFileName: String - get() = ini.getValue(Table, File) + get() = ini.get(Table, File)?.value ?.substringAfterLast('/') ?: errorRuntime(R.string.invalid_im, ERROR_MISSING_TABLE_OR_FILE) set(value) { - ini.setValue(Table, File, "table/$value") + ini.set(Table, File, value = "table/$value") } val tableFileExists get() = table != null fun save() { - file.writeText(IniPrettyPrinter.pretty(ini)) + Ini.writeIniToFile(ini, file) } fun delete() { diff --git a/app/src/main/java/org/fcitx/fcitx5/android/data/table/TableManager.kt b/app/src/main/java/org/fcitx/fcitx5/android/data/table/TableManager.kt index d706e8ca5..8b8643fa8 100644 --- a/app/src/main/java/org/fcitx/fcitx5/android/data/table/TableManager.kt +++ b/app/src/main/java/org/fcitx/fcitx5/android/data/table/TableManager.kt @@ -27,11 +27,10 @@ object TableManager { ?.mapNotNull { confFile -> runCatching { TableBasedInputMethod.new(confFile).apply { - table = runCatching { + table = File(tableDicDir, tableFileName) .takeIf { it.extension == "dict" } ?.let { LibIMEDictionary(it) } - }.getOrNull() } }.getOrNull() } ?: listOf() diff --git a/app/src/main/java/org/fcitx/fcitx5/android/utils/Ini.kt b/app/src/main/java/org/fcitx/fcitx5/android/utils/Ini.kt index 0cc52b750..4b2cae4df 100644 --- a/app/src/main/java/org/fcitx/fcitx5/android/utils/Ini.kt +++ b/app/src/main/java/org/fcitx/fcitx5/android/utils/Ini.kt @@ -1,192 +1,54 @@ package org.fcitx.fcitx5.android.utils -import cc.ekblad.konbini.* -import org.intellij.lang.annotations.Language +import org.fcitx.fcitx5.android.core.RawConfig +import java.io.File -typealias IniProperties = MutableList> +@JvmInline +value class Ini(val core: RawConfig) { -fun IniProperties.getValue(key: String): String? { - val prop = find { it.data.name == key } ?: return null - return prop.data.value -} + val value: String + get() = core.value -fun IniProperties.setValue(key: String, newValue: String) { - val idx = indexOfFirst { it.data.name == key } - val prop = Ini.Property(key, newValue) - if (idx >= 0) { - set(idx, Ini.Annotated(get(idx).comments, prop)) - } else { - add(Ini.Annotated(prop)) - } -} - -data class Ini( - val properties: IniProperties, - val sections: MutableMap>, - val trailingComments: MutableList -) { - - data class Property(var name: String, var value: String) - - data class Annotated( - val comments: MutableList, - val data: T - ) { - constructor(data: T) : this(mutableListOf(), data) - } - - fun getValue(key: String) = properties.getValue(key) - - fun getValue(section: String, key: String) = sections[section]?.data?.getValue(key) - - fun setValue(key: String, newValue: String) = properties.setValue(key, newValue) - - fun setValue(section: String, key: String, newValue: String) { - val s = sections[section] ?: Annotated(mutableListOf>()).also { - sections[section] = it + fun get(vararg keys: String): Ini? { + if (keys.isEmpty()) return null + var current = core + keys.forEach { + current = current.findByName(it) ?: return null } - s.data.setValue(key, newValue) + return Ini(current) } -} - -object IniPrettyPrinter { - data class PrettyOptions( - val separator: Separator, - val commentStart: CommentStart, - val spaceAroundSeparator: Boolean - ) { - enum class Separator { - Colon, Equal + fun set(vararg keys: String, raw: RawConfig) { + var current = core + keys.forEach { + current = current.getOrCreate(it) } - - enum class CommentStart { - Semicolon, Pound + current.getOrCreate(raw.name).apply { + // RawConfig's comment is immutable; is fine. + value = raw.value + subItems = raw.subItems } } - private val defaultPrettyOptions = PrettyOptions( - PrettyOptions.Separator.Equal, - PrettyOptions.CommentStart.Semicolon, - false - ) - - fun pretty(ini: Ini, prettyOptions: PrettyOptions = defaultPrettyOptions): String { - val sb = StringBuilder() - fun prettyProperty(property: Ini.Property) { - sb.append(property.name) - if (prettyOptions.spaceAroundSeparator) { - sb.append(' ') - } - when (prettyOptions.separator) { - PrettyOptions.Separator.Colon -> sb.append(':') - PrettyOptions.Separator.Equal -> sb.append('=') - } - if (prettyOptions.spaceAroundSeparator) { - sb.append(' ') - } - sb.appendLine(property.value) - } - - fun prettyComments(comments: List) { - comments.forEach { - when (prettyOptions.commentStart) { - PrettyOptions.CommentStart.Semicolon -> sb.append(';') - PrettyOptions.CommentStart.Pound -> sb.append('#') - } - sb.appendLine(it) - } + fun set(vararg keys: String, str: String) { + if (keys.isEmpty()) return + var current = core + keys.forEach { + current = current.getOrCreate(it) } - ini.properties.forEach { - prettyComments(it.comments) - prettyProperty(it.data) - } - if (ini.properties.isNotEmpty()) { - sb.appendLine() - } - ini.sections.forEach { (n, properties) -> - prettyComments(properties.comments) - sb.appendLine("[$n]") - properties.data.forEach { - prettyComments(it.comments) - prettyProperty(it.data) - } - sb.appendLine() - } - prettyComments(ini.trailingComments) - return sb.toString() + current.value = str } -} -object IniParser { - private inline fun lexeme(crossinline parser: Parser) = parser { - val data = parser() - whitespace() - data - } + companion object { + @JvmStatic + private external fun readFromIni(src: String): RawConfig? - @Language("RegExp") - private val name = lexeme(regex("[a-zA-Z0-9._/\\[\\]]+")) + @JvmStatic + private external fun writeAsIni(dest: String, value: RawConfig) - @Language("RegExp") - private val sectionName = lexeme(regex("[a-zA-Z0-9._/]+")) + fun parseIniFromFile(file: File) = readFromIni(file.path)?.let { Ini(it) } - private val value = parser { - @Language("RegExp") - val data = regex("[^\n]*") - whitespace() - data.trim() - } - - private val hash = char('#') - private val semi = char(';') - private val comment = parser { - oneOf(hash, semi) - @Language("RegExp") - val data = regex("[^\n]*") - whitespace() - data - } - private val colon = char(':') - private val equal = char('=') - private val bOpen = parser { whitespace(); char('[') } - private val bClose = parser { char(']'); whitespace() } - - private inline fun annotated(crossinline parser: Parser) = parser { - val comments = many(comment) - val data = parser() - Ini.Annotated(comments.toMutableList(), data) - } - - private val property = annotated(parser { - val n = name() - oneOf(colon, equal) - val v = value() - Ini.Property(n, v) - }) - - private val section = annotated(parser { - val n = bracket(bOpen, bClose, sectionName) - val properties = many(property) - n to properties - }) - - private val ini = parser { - val globals: IniProperties = mutableListOf() - val sections = mutableMapOf>() - whitespace() - many { - oneOf( - section.map { - sections[it.data.first] = - Ini.Annotated(it.comments, it.data.second.toMutableList()) - }, - property.map { globals.add(it) } - ) - } - val trailingComments = annotated(whitespace).map { it.comments }() - Ini(globals, sections, trailingComments) + fun writeIniToFile(ini: Ini, file: File) = writeAsIni(file.path, ini.core) } - fun parse(text: String) = ini.parseToEnd(text) } \ No newline at end of file diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 04b8ec1f2..12c9129e1 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -37,7 +37,6 @@ androidx-room-ktx = { module = "androidx.room:room-ktx", version.ref = "room" } androidx-room-paging = { module = "androidx.room:room-paging", version.ref = "room" } androidx-viewpager2 = { module = "androidx.viewpager2:viewpager2", version = "1.1.0-beta02" } material = { module = "com.google.android.material:material", version = "1.9.0" } -konbini = { group = "cc.ekblad.konbini", name = "konbini", version = "0.1.3" } arrow = { module = "io.arrow-kt:arrow-core", version = "1.2.0" } imagecropper = { module = "com.vanniktech:android-image-cropper", version = "4.5.0" } flexbox = { module = "com.google.android.flexbox:flexbox", version = "3.0.0" } From 405568a2337f449825c18cd9c618de6aa886f4d4 Mon Sep 17 00:00:00 2001 From: Rocka Date: Sun, 15 Oct 2023 18:59:02 +0800 Subject: [PATCH 054/381] Fix build --- .../fcitx/fcitx5/android/data/table/TableBasedInputMethod.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/java/org/fcitx/fcitx5/android/data/table/TableBasedInputMethod.kt b/app/src/main/java/org/fcitx/fcitx5/android/data/table/TableBasedInputMethod.kt index fcc54bdf3..b0f62b32a 100644 --- a/app/src/main/java/org/fcitx/fcitx5/android/data/table/TableBasedInputMethod.kt +++ b/app/src/main/java/org/fcitx/fcitx5/android/data/table/TableBasedInputMethod.kt @@ -31,7 +31,7 @@ class TableBasedInputMethod(val file: File) { ?.substringAfterLast('/') ?: errorRuntime(R.string.invalid_im, ERROR_MISSING_TABLE_OR_FILE) set(value) { - ini.set(Table, File, value = "table/$value") + ini.set(Table, File, str = "table/$value") } val tableFileExists From 091f8a2365b3ad8f71e9c711bd496a1f87b07e66 Mon Sep 17 00:00:00 2001 From: Potato Hatsue <1793913507@qq.com> Date: Sun, 15 Oct 2023 11:51:46 -0400 Subject: [PATCH 055/381] Make fcitx operations in IME service run sequentially (#354) Co-authored-by: Rocka --- .../fcitx5/android/core/FcitxDispatcher.kt | 1 - .../android/input/FcitxInputMethodService.kt | 119 +++++++++--------- .../input/keyboard/CommonKeyActionListener.kt | 40 +++--- flake.lock | 12 +- 4 files changed, 84 insertions(+), 88 deletions(-) diff --git a/app/src/main/java/org/fcitx/fcitx5/android/core/FcitxDispatcher.kt b/app/src/main/java/org/fcitx/fcitx5/android/core/FcitxDispatcher.kt index 93b8145df..47c8ae392 100644 --- a/app/src/main/java/org/fcitx/fcitx5/android/core/FcitxDispatcher.kt +++ b/app/src/main/java/org/fcitx/fcitx5/android/core/FcitxDispatcher.kt @@ -4,7 +4,6 @@ import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Runnable import kotlinx.coroutines.asCoroutineDispatcher -import kotlinx.coroutines.cancel import kotlinx.coroutines.isActive import kotlinx.coroutines.launch import kotlinx.coroutines.runBlocking diff --git a/app/src/main/java/org/fcitx/fcitx5/android/input/FcitxInputMethodService.kt b/app/src/main/java/org/fcitx/fcitx5/android/input/FcitxInputMethodService.kt index d16191a41..c3795f5ff 100644 --- a/app/src/main/java/org/fcitx/fcitx5/android/input/FcitxInputMethodService.kt +++ b/app/src/main/java/org/fcitx/fcitx5/android/input/FcitxInputMethodService.kt @@ -27,15 +27,16 @@ import androidx.autofill.inline.common.ViewStyle import androidx.autofill.inline.v1.InlineSuggestionUi import androidx.core.view.updateLayoutParams import androidx.lifecycle.lifecycleScope +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.CoroutineStart import kotlinx.coroutines.Job +import kotlinx.coroutines.channels.Channel +import kotlinx.coroutines.channels.consumeEach import kotlinx.coroutines.launch -import kotlinx.coroutines.sync.Mutex -import kotlinx.coroutines.sync.withLock import org.fcitx.fcitx5.android.R import org.fcitx.fcitx5.android.core.* import org.fcitx.fcitx5.android.daemon.FcitxConnection import org.fcitx.fcitx5.android.daemon.FcitxDaemon -import org.fcitx.fcitx5.android.daemon.launchOnReady import org.fcitx.fcitx5.android.data.InputFeedbacks import org.fcitx.fcitx5.android.data.prefs.AppPrefs import org.fcitx.fcitx5.android.data.prefs.ManagedPreference @@ -54,13 +55,12 @@ import kotlin.math.max class FcitxInputMethodService : LifecycleInputMethodService() { private lateinit var fcitx: FcitxConnection - private var eventHandlerJob: Job? = null + + private var jobs = Channel(capacity = Channel.UNLIMITED) private val cachedKeyEvents = LruCache(78) private var cachedKeyEventIndex = 0 - private val inputContextMutex = Mutex() - private lateinit var pkgNameCache: PackageNameCache private var inputView: InputView? = null @@ -107,9 +107,28 @@ class FcitxInputMethodService : LifecycleInputMethodService() { } } + private fun postJob(scope: CoroutineScope, block: suspend () -> Unit): Job { + val job = scope.launch(start = CoroutineStart.LAZY) { block() } + jobs.trySend(job) + return job + } + + /** + * Post a fcitx operation to [jobs] to be executed + * + * Unlike `fcitx.runOnReady` or `fcitx.launchOnReady` where + * subsequent operations can start if the prior operation is not finished (suspended), + * [postFcitxJob] ensures that operations are executed sequentially. + */ + fun postFcitxJob(block: suspend FcitxAPI.() -> Unit) = + postJob(fcitx.lifecycleScope) { fcitx.runOnReady(block) } + override fun onCreate() { fcitx = FcitxDaemon.connect(javaClass.name) - eventHandlerJob = lifecycleScope.launch { + lifecycleScope.launch { + jobs.consumeEach { it.join() } + } + lifecycleScope.launch { fcitx.runImmediately { eventFlow }.collect { handleFcitxEvent(it) } @@ -330,7 +349,7 @@ class FcitxInputMethodService : LifecycleInputMethodService() { override fun onConfigurationChanged(newConfig: Configuration) { super.onConfigurationChanged(newConfig) - fcitx.launchOnReady { it.reset() } + postFcitxJob { reset() } } override fun onWindowShown() { @@ -398,15 +417,15 @@ class FcitxInputMethodService : LifecycleInputMethodService() { // skip \t, because it's charCode is different from KeySym // skip \n, because fcitx wants \r for return if (charCode > 0 && charCode != '\t'.code && charCode != '\n'.code) { - fcitx.launchOnReady { - it.sendKey(charCode, states.states, up, timestamp) + postFcitxJob { + sendKey(charCode, states.states, up, timestamp) } return true } val keySym = KeySym.fromKeyEvent(event) if (keySym != null) { - fcitx.launchOnReady { - it.sendKey(keySym, states, up, timestamp) + postFcitxJob { + sendKey(keySym, states, up, timestamp) } return true } @@ -426,13 +445,9 @@ class FcitxInputMethodService : LifecycleInputMethodService() { val uid = currentInputBinding.uid val pkgName = pkgNameCache.forUid(uid) Timber.d("onBindInput: uid=$uid pkg=$pkgName") - lifecycleScope.launch { + postFcitxJob { // ensure InputContext has been created before focusing it - inputContextMutex.withLock { - fcitx.runOnReady { - activate(uid, pkgName) - } - } + activate(uid, pkgName) } } @@ -446,32 +461,24 @@ class FcitxInputMethodService : LifecycleInputMethodService() { val flags = CapabilityFlags.fromEditorInfo(attribute) capabilityFlags = flags Timber.d("onStartInput: initialSel=${selection.current}, restarting=$restarting") - lifecycleScope.launch { - // wait until InputContext created/activated - inputContextMutex.withLock { - fcitx.runOnReady { - if (restarting) { - // when input restarts in the same editor, focus out to clear previous state - focus(false) - // try focus out before changing CapabilityFlags, - // to avoid confusing state of different text fields - } - // EditorInfo can be different in onStartInput and onStartInputView, - // especially in browsers - setCapFlags(flags) - } + // wait until InputContext created/activated + postFcitxJob { + if (restarting) { + // when input restarts in the same editor, focus out to clear previous state + focus(false) + // try focus out before changing CapabilityFlags, + // to avoid confusing state of different text fields } + // EditorInfo can be different in onStartInput and onStartInputView, + // especially in browsers + setCapFlags(flags) } } override fun onStartInputView(info: EditorInfo, restarting: Boolean) { Timber.d("onStartInputView: restarting=$restarting") - lifecycleScope.launch { - inputContextMutex.withLock { - fcitx.runOnReady { - focus(true) - } - } + postFcitxJob { + focus(true) } // because onStartInputView will always be called after onStartInput, // editorInfo and capFlags should be up-to-date @@ -508,10 +515,10 @@ class FcitxInputMethodService : LifecycleInputMethodService() { if (newSelStart != newSelEnd) return // do reset if composing is empty && input panel is not empty if (composing.isEmpty()) { - fcitx.launchOnReady { - if (!it.isEmpty()) { + postFcitxJob { + if (!isEmpty()) { Timber.d("handleCursorUpdate: reset") - it.reset() + reset() } } return @@ -525,10 +532,10 @@ class FcitxInputMethodService : LifecycleInputMethodService() { if (position != composingText.cursor) { // cursor in InvokeActionEvent counts by "UTF-8 characters" val codePointPosition = composingText.codePointCountUntil(position) - fcitx.launchOnReady { - if (updateIndex != cursorUpdateIndex) return@launchOnReady + postFcitxJob { + if (updateIndex != cursorUpdateIndex) return@postFcitxJob Timber.d("handleCursorUpdate: move fcitx cursor to $codePointPosition") - it.moveCursor(codePointPosition) + moveCursor(codePointPosition) } } } else { @@ -538,9 +545,9 @@ class FcitxInputMethodService : LifecycleInputMethodService() { currentInputConnection?.finishComposingText() // `fcitx.reset()` here would commit preedit after new cursor position // since we have `ClientUnfocusCommit`, focus out and in would do the trick - fcitx.launchOnReady { - it.focus(false) - it.focus(true) + postFcitxJob { + focus(false) + focus(true) } } } @@ -680,12 +687,8 @@ class FcitxInputMethodService : LifecycleInputMethodService() { override fun onFinishInputView(finishingInput: Boolean) { Timber.d("onFinishInputView: finishingInput=$finishingInput") currentInputConnection?.finishComposingText() - lifecycleScope.launch { - inputContextMutex.withLock { - fcitx.runOnReady { - focus(false) - } - } + postFcitxJob { + focus(false) } inputView?.finishInput() } @@ -702,12 +705,8 @@ class FcitxInputMethodService : LifecycleInputMethodService() { // currentInputBinding can be null on some devices under some special Multi-screen mode val uid = currentInputBinding?.uid ?: return Timber.d("onUnbindInput: uid=$uid") - lifecycleScope.launch { - inputContextMutex.withLock { - fcitx.runOnReady { - deactivate(uid) - } - } + postFcitxJob { + deactivate(uid) } } @@ -716,8 +715,6 @@ class FcitxInputMethodService : LifecycleInputMethodService() { advanced.disableAnimation.unregisterOnChangeListener(recreateInputViewListener) } ThemeManager.removeOnChangedListener(onThemeChangeListener) - eventHandlerJob?.cancel() - eventHandlerJob = null super.onDestroy() // Fcitx might be used in super.onDestroy() FcitxDaemon.disconnect(javaClass.name) diff --git a/app/src/main/java/org/fcitx/fcitx5/android/input/keyboard/CommonKeyActionListener.kt b/app/src/main/java/org/fcitx/fcitx5/android/input/keyboard/CommonKeyActionListener.kt index c3e94cb3e..adaff4f8e 100644 --- a/app/src/main/java/org/fcitx/fcitx5/android/input/keyboard/CommonKeyActionListener.kt +++ b/app/src/main/java/org/fcitx/fcitx5/android/input/keyboard/CommonKeyActionListener.kt @@ -77,31 +77,31 @@ class CommonKeyActionListener : val listener by lazy { KeyActionListener { action, _ -> when (action) { - is FcitxKeyAction -> fcitx.launchOnReady { - it.sendKey(action.act, KeyState.Virtual.state) + is FcitxKeyAction -> service.postFcitxJob { + sendKey(action.act, KeyState.Virtual.state) } - is SymAction -> fcitx.launchOnReady { - it.sendKey(action.sym, action.states) + is SymAction -> service.postFcitxJob { + sendKey(action.sym, action.states) } - is CommitAction -> fcitx.launchOnReady { - it.commitAndReset() + is CommitAction -> service.postFcitxJob { + commitAndReset() service.lifecycleScope.launch { service.commitText(action.text) } } - is QuickPhraseAction -> fcitx.launchOnReady { - it.commitAndReset() - it.triggerQuickPhrase() + is QuickPhraseAction -> service.postFcitxJob { + commitAndReset() + triggerQuickPhrase() } - is UnicodeAction -> fcitx.launchOnReady { - it.commitAndReset() - it.triggerUnicode() + is UnicodeAction -> service.postFcitxJob { + commitAndReset() + triggerUnicode() } - is LangSwitchAction -> fcitx.launchOnReady { - if (it.enabledIme().size < 2) { + is LangSwitchAction -> service.postFcitxJob { + if (enabledIme().size < 2) { service.lifecycleScope.launch { inputView.showDialog(AddMoreInputMethodsPrompt.build(context)) } } else { - it.enumerateIme() + enumerateIme() } } is ShowInputMethodPickerAction -> showInputMethodPicker() @@ -129,7 +129,7 @@ class CommonKeyActionListener : Stopped -> {} Selection -> service.deleteSelection() Reset -> if (action.totalCnt < 0) { // swipe left - fcitx.launchOnReady { it.reset() } + service.postFcitxJob { reset() } } } backspaceSwipeState = Stopped @@ -146,11 +146,11 @@ class CommonKeyActionListener : is SpaceLongPressAction -> { when (spaceKeyLongPressBehavior) { SpaceLongPressBehavior.None -> {} - SpaceLongPressBehavior.Enumerate -> fcitx.launchOnReady { - it.enumerateIme() + SpaceLongPressBehavior.Enumerate -> service.postFcitxJob { + enumerateIme() } - SpaceLongPressBehavior.ToggleActivate -> fcitx.launchOnReady { - it.toggleIme() + SpaceLongPressBehavior.ToggleActivate -> service.postFcitxJob { + toggleIme() } SpaceLongPressBehavior.ShowPicker -> showInputMethodPicker() } diff --git a/flake.lock b/flake.lock index 9257228e3..75f9e9943 100644 --- a/flake.lock +++ b/flake.lock @@ -3,11 +3,11 @@ "flake-compat": { "flake": false, "locked": { - "lastModified": 1673956053, - "narHash": "sha256-4gtG9iQuiKITOjNQQeQIpoIB6b16fm+504Ch3sNKLd8=", + "lastModified": 1696426674, + "narHash": "sha256-kvjfFW7WAETZlt09AgDn1MrtKzP7t90Vf7vypd3OL1U=", "owner": "edolstra", "repo": "flake-compat", - "rev": "35bb57c0c8d8b62bbfd284272c928ceb64ddbde9", + "rev": "0f9255e01c2351cc7d116c072cb317785dd33b33", "type": "github" }, "original": { @@ -36,11 +36,11 @@ }, "nixpkgs": { "locked": { - "lastModified": 1695360818, - "narHash": "sha256-JlkN3R/SSoMTa+CasbxS1gq+GpGxXQlNZRUh9+LIy/0=", + "lastModified": 1697059129, + "narHash": "sha256-9NJcFF9CEYPvHJ5ckE8kvINvI84SZZ87PvqMbH6pro0=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "e35dcc04a3853da485a396bdd332217d0ac9054f", + "rev": "5e4c2ada4fcd54b99d56d7bd62f384511a7e2593", "type": "github" }, "original": { From 329efad3b6efd6fcce17967bd62c7ec1e3abe657 Mon Sep 17 00:00:00 2001 From: Rocka Date: Mon, 16 Oct 2023 16:21:34 +0800 Subject: [PATCH 056/381] Add option to "Expand keypress area" --- .../fcitx5/android/data/prefs/AppPrefs.kt | 2 + .../fcitx5/android/input/AutoScaleTextView.kt | 6 +- .../android/input/keyboard/BaseKeyboard.kt | 30 ++++- .../fcitx5/android/input/keyboard/KeyView.kt | 112 +++++++++++++----- app/src/main/res/values/strings.xml | 1 + 5 files changed, 117 insertions(+), 34 deletions(-) diff --git a/app/src/main/java/org/fcitx/fcitx5/android/data/prefs/AppPrefs.kt b/app/src/main/java/org/fcitx/fcitx5/android/data/prefs/AppPrefs.kt index 77470aa1b..f23dac9b8 100644 --- a/app/src/main/java/org/fcitx/fcitx5/android/data/prefs/AppPrefs.kt +++ b/app/src/main/java/org/fcitx/fcitx5/android/data/prefs/AppPrefs.kt @@ -145,6 +145,8 @@ class AppPrefs(private val sharedPreferences: SharedPreferences) { ) val showVoiceInputButton = switch(R.string.show_voice_input_button, "show_voice_input_button", false) + val expandKeypressArea = + switch(R.string.expand_keypress_area, "expand_keypress_area", false) val swipeSymbolDirection = list( R.string.swipe_symbol_behavior, "swipe_symbol_behavior", diff --git a/app/src/main/java/org/fcitx/fcitx5/android/input/AutoScaleTextView.kt b/app/src/main/java/org/fcitx/fcitx5/android/input/AutoScaleTextView.kt index c9b8d8b23..d9003a4d3 100644 --- a/app/src/main/java/org/fcitx/fcitx5/android/input/AutoScaleTextView.kt +++ b/app/src/main/java/org/fcitx/fcitx5/android/input/AutoScaleTextView.kt @@ -97,9 +97,9 @@ class AutoScaleTextView @JvmOverloads constructor( } else { textBounds.set( /* left = */ 0, - /* top = */ floor(fontMetrics.top.toDouble()).toInt(), - /* right = */ ceil(paint.measureText(text).toDouble()).toInt(), - /* bottom = */ ceil(fontMetrics.bottom.toDouble()).toInt() + /* top = */ floor(fontMetrics.top).toInt(), + /* right = */ ceil(paint.measureText(text)).toInt(), + /* bottom = */ ceil(fontMetrics.bottom).toInt() ) } needsMeasureText = false diff --git a/app/src/main/java/org/fcitx/fcitx5/android/input/keyboard/BaseKeyboard.kt b/app/src/main/java/org/fcitx/fcitx5/android/input/keyboard/BaseKeyboard.kt index e9b8f3dfd..0c333fb61 100644 --- a/app/src/main/java/org/fcitx/fcitx5/android/input/keyboard/BaseKeyboard.kt +++ b/app/src/main/java/org/fcitx/fcitx5/android/input/keyboard/BaseKeyboard.kt @@ -8,6 +8,7 @@ import androidx.annotation.CallSuper import androidx.annotation.DrawableRes import androidx.constraintlayout.widget.ConstraintLayout import androidx.core.view.children +import androidx.core.view.updateLayoutParams import org.fcitx.fcitx5.android.core.FcitxKeyMapping import org.fcitx.fcitx5.android.core.InputMethodEntry import org.fcitx.fcitx5.android.core.KeyStates @@ -45,10 +46,13 @@ abstract class BaseKeyboard( var keyActionListener: KeyActionListener? = null - private val popupOnKeyPress by AppPrefs.getInstance().keyboard.popupOnKeyPress - private val swipeSymbolDirection by AppPrefs.getInstance().keyboard.swipeSymbolDirection + private val prefs = AppPrefs.getInstance() - private val vivoKeypressWorkaround by AppPrefs.getInstance().advanced.vivoKeypressWorkaround + private val popupOnKeyPress by prefs.keyboard.popupOnKeyPress + private val expandKeypressArea by prefs.keyboard.expandKeypressArea + private val swipeSymbolDirection by prefs.keyboard.swipeSymbolDirection + + private val vivoKeypressWorkaround by prefs.advanced.vivoKeypressWorkaround var popupActionListener: PopupActionListener? = null @@ -71,6 +75,7 @@ abstract class BaseKeyboard( keyRows = keyLayout.map { row -> val keyViews = row.map(::createKeyView) constraintLayout Row@{ + var totalWidth = 0f keyViews.forEachIndexed { index, view -> add(view, lParams { centerVertically() @@ -90,6 +95,25 @@ abstract class BaseKeyboard( val def = row[index] matchConstraintPercentWidth = def.appearance.percentWidth }) + row[index].appearance.percentWidth.let { + // 0f means fill remaining space, thus does not need expanding + totalWidth += if (it != 0f) it else 1f + } + } + if (expandKeypressArea && totalWidth < 1f) { + val free = (1f - totalWidth) / 2f + keyViews.first().apply { + updateLayoutParams { + matchConstraintPercentWidth += free + } + layoutMarginLeft = free / (row.first().appearance.percentWidth + free) + } + keyViews.last().apply { + updateLayoutParams { + matchConstraintPercentWidth += free + } + layoutMarginRight = free / (row.last().appearance.percentWidth + free) + } } } } diff --git a/app/src/main/java/org/fcitx/fcitx5/android/input/keyboard/KeyView.kt b/app/src/main/java/org/fcitx/fcitx5/android/input/keyboard/KeyView.kt index 569e29568..c2a4a54d9 100644 --- a/app/src/main/java/org/fcitx/fcitx5/android/input/keyboard/KeyView.kt +++ b/app/src/main/java/org/fcitx/fcitx5/android/input/keyboard/KeyView.kt @@ -4,13 +4,22 @@ import android.annotation.SuppressLint import android.content.Context import android.content.res.ColorStateList import android.content.res.Configuration -import android.graphics.* -import android.graphics.drawable.* +import android.graphics.Color +import android.graphics.Rect +import android.graphics.Typeface +import android.graphics.drawable.ColorDrawable +import android.graphics.drawable.Drawable +import android.graphics.drawable.GradientDrawable +import android.graphics.drawable.InsetDrawable +import android.graphics.drawable.LayerDrawable +import android.graphics.drawable.RippleDrawable +import android.graphics.drawable.StateListDrawable import android.util.TypedValue import android.view.View import android.widget.ImageView import androidx.annotation.ColorInt import androidx.annotation.DrawableRes +import androidx.annotation.FloatRange import androidx.constraintlayout.widget.ConstraintLayout import androidx.core.view.updateLayoutParams import org.fcitx.fcitx5.android.R @@ -28,11 +37,17 @@ import splitties.views.dsl.constraintlayout.centerInParent import splitties.views.dsl.constraintlayout.constraintLayout import splitties.views.dsl.constraintlayout.lParams import splitties.views.dsl.constraintlayout.parentId -import splitties.views.dsl.core.* +import splitties.views.dsl.core.add +import splitties.views.dsl.core.imageView +import splitties.views.dsl.core.lParams +import splitties.views.dsl.core.matchParent +import splitties.views.dsl.core.view +import splitties.views.dsl.core.wrapContent import splitties.views.existingOrNewId import splitties.views.imageResource import splitties.views.padding import kotlin.math.min +import kotlin.math.roundToInt abstract class KeyView(ctx: Context, val theme: Theme, val def: KeyDef.Appearance) : CustomGestureView(ctx) { @@ -60,7 +75,27 @@ abstract class KeyView(ctx: Context, val theme: Theme, val def: KeyDef.Appearanc if (!boundsValid) updateBounds() } - val layout = constraintLayout { + /** + * KeyView content left margin, in percentage of parent width + */ + @FloatRange(0.0, 1.0) + var layoutMarginLeft = 0f + + /** + * KeyView content right margin, in percentage of parent width + */ + @FloatRange(0.0, 1.0) + var layoutMarginRight = 0f + + /** + * [KeyView] contains 2 parts: `TouchEventView` and `AppearanceView`. + * + * `TouchEventView` is the outer [CustomGestureView] that handles touch events. + * + * `AppearanceView` in the inner [ConstraintLayout], it can be smaller than its parent, + * and holds the [bounds] for popup. + */ + protected val appearanceView = constraintLayout { // sync any state from parent isDuplicateParentStateEnabled = true } @@ -76,7 +111,7 @@ abstract class KeyView(ctx: Context, val theme: Theme, val def: KeyDef.Appearanc // key border if ((bordered && def.border != Border.Off) || def.border == Border.On) { // background: key border - background = LayerDrawable( + appearanceView.background = LayerDrawable( arrayOf( GradientDrawable().apply { cornerRadius = radius @@ -107,11 +142,11 @@ abstract class KeyView(ctx: Context, val theme: Theme, val def: KeyDef.Appearanc setupPressHighlight() } } - add(layout, lParams(matchParent, matchParent)) + add(appearanceView, lParams(matchParent, matchParent)) } private fun setupPressHighlight(mask: Drawable? = null) { - foreground = if (rippled) + appearanceView.foreground = if (rippled) RippleDrawable( ColorStateList.valueOf(theme.keyPressHighlightColor), null, // ripple should be masked with an opaque color @@ -138,17 +173,32 @@ abstract class KeyView(ctx: Context, val theme: Theme, val def: KeyDef.Appearanc override fun setEnabled(enabled: Boolean) { super.setEnabled(enabled) - layout.alpha = if (enabled) 1f else styledFloat(android.R.attr.disabledAlpha) + appearanceView.alpha = if (enabled) 1f else styledFloat(android.R.attr.disabledAlpha) } fun updateBounds() { - val (x, y) = cachedLocation.also { getLocationInWindow(it) } - cachedBounds.set(x, y, x + width, y + height) + val (x, y) = cachedLocation.also { appearanceView.getLocationInWindow(it) } + cachedBounds.set(x, y, x + appearanceView.width, y + appearanceView.height) boundsValid = true } override fun onLayout(changed: Boolean, left: Int, top: Int, right: Int, bottom: Int) { boundsValid = false + if (layoutMarginLeft != 0f || layoutMarginRight != 0f) { + val w = right - left + val h = bottom - top + val layoutWidth = (w * (1f - layoutMarginLeft - layoutMarginRight)).roundToInt() + appearanceView.updateLayoutParams { + leftMargin = (w * layoutMarginLeft).roundToInt() + rightMargin = (w * layoutMarginRight).roundToInt() + } + // sets `measuredWidth` and `measuredHeight` of `AppearanceView` + // https://developer.android.com/guide/topics/ui/how-android-draws#measure + appearanceView.measure( + MeasureSpec.makeMeasureSpec(layoutWidth, MeasureSpec.EXACTLY), + MeasureSpec.makeMeasureSpec(h, MeasureSpec.EXACTLY) + ) + } super.onLayout(changed, left, top, right, bottom) } @@ -160,7 +210,7 @@ abstract class KeyView(ctx: Context, val theme: Theme, val def: KeyDef.Appearanc val minHeight = dp(26) val hInset = dp(10) val vInset = if (h < minHeight) 0 else min((h - minHeight) / 2, dp(16)) - background = InsetDrawable( + appearanceView.background = InsetDrawable( GradientDrawable().apply { cornerRadius = bkgRadius setColor(theme.spaceBarColor) @@ -168,7 +218,7 @@ abstract class KeyView(ctx: Context, val theme: Theme, val def: KeyDef.Appearanc hInset, vInset, hInset, vInset ) // InsetDrawable sets padding to container view; remove padding to prevent text from bing clipped - padding = 0 + appearanceView.padding = 0 // apply press highlight for background area setupPressHighlight( InsetDrawable( @@ -184,14 +234,14 @@ abstract class KeyView(ctx: Context, val theme: Theme, val def: KeyDef.Appearanc val drawableSize = min(min(w, h), dp(35)) val hInset = (w - drawableSize) / 2 val vInset = (h - drawableSize) / 2 - background = InsetDrawable( + appearanceView.background = InsetDrawable( GradientDrawable().apply { shape = GradientDrawable.OVAL setColor(theme.accentKeyBackgroundColor) }, hInset, vInset, hInset, vInset ) - padding = 0 + appearanceView.padding = 0 setupPressHighlight( InsetDrawable( GradientDrawable().apply { @@ -228,7 +278,7 @@ open class TextKeyView(ctx: Context, theme: Theme, def: KeyDef.Appearance.Text) } init { - layout.apply { + appearanceView.apply { add(mainText, lParams(wrapContent, wrapContent) { centerInParent() }) @@ -256,7 +306,9 @@ class AltTextKeyView(ctx: Context, theme: Theme, def: KeyDef.Appearance.AltText) } init { - layout.apply { add(altText, lParams(wrapContent, wrapContent)) } + appearanceView.apply { + add(altText, lParams(wrapContent, wrapContent)) + } applyLayout(resources.configuration.orientation) } @@ -322,7 +374,11 @@ class ImageKeyView(ctx: Context, theme: Theme, def: KeyDef.Appearance.Image) : val img = imageView { configure(theme, def.src, def.variant) } init { - layout.apply { add(img, lParams(wrapContent, wrapContent) { centerInParent() }) } + appearanceView.apply { + add(img, lParams(wrapContent, wrapContent) { + centerInParent() + }) + } } } @@ -347,18 +403,18 @@ class ImageTextKeyView(ctx: Context, theme: Theme, def: KeyDef.Appearance.ImageT } init { - layout.apply { + appearanceView.apply { add(img, lParams(dp(13), dp(13))) - mainText.updateLayoutParams { - centerHorizontally() - bottomToBottom = parentId - bottomMargin = vMargin + dp(4) - topToTop = unset - } - img.updateLayoutParams { - centerHorizontally() - topToTop = parentId - } + } + mainText.updateLayoutParams { + centerHorizontally() + bottomToBottom = parentId + bottomMargin = vMargin + dp(4) + topToTop = unset + } + img.updateLayoutParams { + centerHorizontally() + topToTop = parentId } updateMargins(resources.configuration.orientation) } diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 5601bc9c2..3629ee704 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -249,4 +249,5 @@ I don\'t need it Grant permission Clipboard database would be completely cleared, including pinned items. Processed? + Expand keypress area to the edge \ No newline at end of file From b0a6d1c7f32e433f28e1d30933aa04fc3d4f27a9 Mon Sep 17 00:00:00 2001 From: Rocka Date: Mon, 16 Oct 2023 18:44:22 +0800 Subject: [PATCH 057/381] Add landscape variant of key margin prefs --- .../fcitx5/android/data/theme/ThemeManager.kt | 42 +++++++++++++++++-- .../fcitx5/android/input/keyboard/KeyView.kt | 9 +++- 2 files changed, 45 insertions(+), 6 deletions(-) diff --git a/app/src/main/java/org/fcitx/fcitx5/android/data/theme/ThemeManager.kt b/app/src/main/java/org/fcitx/fcitx5/android/data/theme/ThemeManager.kt index 120b5b8cd..9e7d07801 100644 --- a/app/src/main/java/org/fcitx/fcitx5/android/data/theme/ThemeManager.kt +++ b/app/src/main/java/org/fcitx/fcitx5/android/data/theme/ThemeManager.kt @@ -221,11 +221,45 @@ object ThemeManager { val keyRippleEffect = switch(R.string.key_ripple_effect, "key_ripple_effect", false) - val keyHorizontalMargin = - int(R.string.key_horizontal_margin, "key_horizontal_margin", 3, 0, 8, "dp") + val keyHorizontalMargin: ManagedPreference.PInt + val keyHorizontalMarginLandscape: ManagedPreference.PInt + + init { + val (primary, secondary) = twinInt( + R.string.key_horizontal_margin, + R.string.portrait, + "key_horizontal_margin", + 3, + R.string.landscape, + "key_horizontal_margin_landscape", + 3, + 0, + 24, + "dp" + ) + keyHorizontalMargin = primary + keyHorizontalMarginLandscape = secondary + } - val keyVerticalMargin = - int(R.string.key_vertical_margin, "key_vertical_margin", 7, 0, 16, "dp") + val keyVerticalMargin: ManagedPreference.PInt + val keyVerticalMarginLandscape: ManagedPreference.PInt + + init { + val (primary, secondary) = twinInt( + R.string.key_vertical_margin, + R.string.portrait, + "key_vertical_margin", + 7, + R.string.landscape, + "key_vertical_margin_landscape", + 4, + 0, + 24, + "dp" + ) + keyVerticalMargin = primary + keyVerticalMarginLandscape = secondary + } val keyRadius = int(R.string.key_radius, "key_radius", 4, 0, 48, "dp") diff --git a/app/src/main/java/org/fcitx/fcitx5/android/input/keyboard/KeyView.kt b/app/src/main/java/org/fcitx/fcitx5/android/input/keyboard/KeyView.kt index c2a4a54d9..315d79fdd 100644 --- a/app/src/main/java/org/fcitx/fcitx5/android/input/keyboard/KeyView.kt +++ b/app/src/main/java/org/fcitx/fcitx5/android/input/keyboard/KeyView.kt @@ -63,8 +63,13 @@ abstract class KeyView(ctx: Context, val theme: Theme, val def: KeyDef.Appearanc bordered = prefs.keyBorder.getValue() rippled = prefs.keyRippleEffect.getValue() radius = dp(prefs.keyRadius.getValue().toFloat()) - hMargin = if (def.margin) dp(prefs.keyHorizontalMargin.getValue()) else 0 - vMargin = if (def.margin) dp(prefs.keyVerticalMargin.getValue()) else 0 + val landscape = resources.configuration.orientation == Configuration.ORIENTATION_LANDSCAPE + val hMarginPref = + if (landscape) prefs.keyHorizontalMarginLandscape else prefs.keyHorizontalMargin + val vMarginPref = + if (landscape) prefs.keyVerticalMarginLandscape else prefs.keyVerticalMargin + hMargin = if (def.margin) dp(hMarginPref.getValue()) else 0 + vMargin = if (def.margin) dp(vMarginPref.getValue()) else 0 } private val cachedLocation = intArrayOf(0, 0) From 34b340586f0fb31b1c3254051934a6954ddae032 Mon Sep 17 00:00:00 2001 From: Rocka Date: Mon, 16 Oct 2023 20:09:31 +0800 Subject: [PATCH 058/381] Use ViewPager to switch between theme list and config --- .../fcitx5/android/ui/main/MainActivity.kt | 2 +- .../fcitx5/android/ui/main/MainFragment.kt | 2 +- .../ui/main/settings/theme/ThemeFragment.kt | 126 +++++++++++++++ .../main/settings/theme/ThemeListFragment.kt | 143 +++--------------- .../settings/theme/ThemeSettingsFragment.kt | 10 +- .../org/fcitx/fcitx5/android/utils/AppUtil.kt | 2 +- app/src/main/res/navigation/settings_nav.xml | 19 +-- app/src/main/res/values-de/strings.xml | 2 +- app/src/main/res/values-ru/strings.xml | 2 +- app/src/main/res/values-zh-rCN/strings.xml | 2 +- app/src/main/res/values-zh-rTW/strings.xml | 2 +- app/src/main/res/values/strings.xml | 2 +- 12 files changed, 164 insertions(+), 150 deletions(-) create mode 100644 app/src/main/java/org/fcitx/fcitx5/android/ui/main/settings/theme/ThemeFragment.kt diff --git a/app/src/main/java/org/fcitx/fcitx5/android/ui/main/MainActivity.kt b/app/src/main/java/org/fcitx/fcitx5/android/ui/main/MainActivity.kt index 710af8f29..0302b92d4 100644 --- a/app/src/main/java/org/fcitx/fcitx5/android/ui/main/MainActivity.kt +++ b/app/src/main/java/org/fcitx/fcitx5/android/ui/main/MainActivity.kt @@ -78,7 +78,7 @@ class MainActivity : AppCompatActivity() { } navController.addOnDestinationChangedListener { _, dest, _ -> when (dest.id) { - R.id.themeListFragment -> viewModel.disableToolbarShadow() + R.id.themeFragment -> viewModel.disableToolbarShadow() else -> viewModel.enableToolbarShadow() } } diff --git a/app/src/main/java/org/fcitx/fcitx5/android/ui/main/MainFragment.kt b/app/src/main/java/org/fcitx/fcitx5/android/ui/main/MainFragment.kt index 73081b303..f4ee9c0a9 100644 --- a/app/src/main/java/org/fcitx/fcitx5/android/ui/main/MainFragment.kt +++ b/app/src/main/java/org/fcitx/fcitx5/android/ui/main/MainFragment.kt @@ -64,7 +64,7 @@ class MainFragment : PaddingPreferenceFragment() { addDestinationPreference( R.string.theme, R.drawable.ic_baseline_palette_24, - R.id.action_mainFragment_to_themeListFragment + R.id.action_mainFragment_to_themeFragment ) addDestinationPreference( R.string.clipboard, diff --git a/app/src/main/java/org/fcitx/fcitx5/android/ui/main/settings/theme/ThemeFragment.kt b/app/src/main/java/org/fcitx/fcitx5/android/ui/main/settings/theme/ThemeFragment.kt new file mode 100644 index 000000000..fdc8c9ef6 --- /dev/null +++ b/app/src/main/java/org/fcitx/fcitx5/android/ui/main/settings/theme/ThemeFragment.kt @@ -0,0 +1,126 @@ +package org.fcitx.fcitx5.android.ui.main.settings.theme + +import android.os.Build +import android.os.Bundle +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import android.view.ViewOutlineProvider +import androidx.annotation.Keep +import androidx.fragment.app.Fragment +import androidx.lifecycle.lifecycleScope +import androidx.viewpager2.adapter.FragmentStateAdapter +import androidx.viewpager2.widget.ViewPager2 +import com.google.android.material.tabs.TabLayout +import com.google.android.material.tabs.TabLayoutMediator +import kotlinx.coroutines.launch +import org.fcitx.fcitx5.android.R +import org.fcitx.fcitx5.android.data.theme.ThemeManager +import splitties.dimensions.dp +import splitties.resources.styledColor +import splitties.views.backgroundColor +import splitties.views.dsl.constraintlayout.below +import splitties.views.dsl.constraintlayout.bottomOfParent +import splitties.views.dsl.constraintlayout.centerHorizontally +import splitties.views.dsl.constraintlayout.constraintLayout +import splitties.views.dsl.constraintlayout.endOfParent +import splitties.views.dsl.constraintlayout.lParams +import splitties.views.dsl.constraintlayout.startOfParent +import splitties.views.dsl.constraintlayout.topOfParent +import splitties.views.dsl.core.add +import splitties.views.dsl.core.matchParent +import splitties.views.dsl.core.wrapContent + +class ThemeFragment : Fragment() { + + private lateinit var previewUi: KeyboardPreviewUi + + private lateinit var tabLayout: TabLayout + + private lateinit var viewPager: ViewPager2 + + @Keep + private val onThemeChangeListener = ThemeManager.OnThemeChangeListener { + lifecycleScope.launch { + previewUi.setTheme(it) + } + } + + override fun onCreateView( + inflater: LayoutInflater, + container: ViewGroup?, + savedInstanceState: Bundle? + ): View = with(requireContext()) { + val activeTheme = ThemeManager.getActiveTheme() + + previewUi = KeyboardPreviewUi(this, activeTheme) + ThemeManager.addOnChangedListener(onThemeChangeListener) + val preview = previewUi.root.apply { + scaleX = 0.5f + scaleY = 0.5f + outlineProvider = ViewOutlineProvider.BOUNDS + elevation = dp(4f) + } + + tabLayout = TabLayout(this) + + viewPager = ViewPager2(this).apply { + adapter = object : FragmentStateAdapter(parentFragmentManager, lifecycle) { + override fun getItemCount() = 2 + override fun createFragment(position: Int): Fragment = when (position) { + 0 -> ThemeListFragment() + else -> ThemeSettingsFragment() + } + } + } + + TabLayoutMediator(tabLayout, viewPager) { tab, position -> + tab.text = getString( + when (position) { + 0 -> R.string.theme + else -> R.string.configure + } + ) + }.attach() + + val previewWrapper = constraintLayout { + add(preview, lParams(wrapContent, wrapContent) { + topOfParent(dp(-52)) + startOfParent() + endOfParent() + }) + add(tabLayout, lParams(matchParent, wrapContent) { + centerHorizontally() + bottomOfParent() + }) + backgroundColor = styledColor(android.R.attr.colorPrimary) + elevation = dp(4f) + } + + constraintLayout { + add(previewWrapper, lParams(height = wrapContent) { + topOfParent() + startOfParent() + endOfParent() + }) + add(viewPager, lParams { + below(previewWrapper) + startOfParent() + endOfParent() + bottomOfParent() + }) + } + } + + override fun onStop() { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { + ThemeManager.syncToDeviceEncryptedStorage() + } + super.onStop() + } + + override fun onDestroy() { + ThemeManager.removeOnChangedListener(onThemeChangeListener) + super.onDestroy() + } +} diff --git a/app/src/main/java/org/fcitx/fcitx5/android/ui/main/settings/theme/ThemeListFragment.kt b/app/src/main/java/org/fcitx/fcitx5/android/ui/main/settings/theme/ThemeListFragment.kt index d910cfd6b..11591d458 100644 --- a/app/src/main/java/org/fcitx/fcitx5/android/ui/main/settings/theme/ThemeListFragment.kt +++ b/app/src/main/java/org/fcitx/fcitx5/android/ui/main/settings/theme/ThemeListFragment.kt @@ -1,19 +1,15 @@ package org.fcitx.fcitx5.android.ui.main.settings.theme -import android.os.Build import android.os.Bundle import android.view.LayoutInflater import android.view.View import android.view.ViewGroup -import android.view.ViewOutlineProvider import androidx.activity.result.ActivityResultLauncher import androidx.activity.result.contract.ActivityResultContracts import androidx.annotation.Keep import androidx.appcompat.app.AlertDialog import androidx.fragment.app.Fragment import androidx.lifecycle.lifecycleScope -import androidx.navigation.fragment.findNavController -import androidx.recyclerview.widget.RecyclerView import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.NonCancellable import kotlinx.coroutines.launch @@ -26,26 +22,7 @@ import org.fcitx.fcitx5.android.utils.applyNavBarInsetsBottomPadding import org.fcitx.fcitx5.android.utils.errorDialog import org.fcitx.fcitx5.android.utils.queryFileName import org.fcitx.fcitx5.android.utils.toast -import splitties.dimensions.dp -import splitties.resources.drawable -import splitties.resources.resolveThemeAttribute -import splitties.resources.styledColor import splitties.resources.styledDrawable -import splitties.views.backgroundColor -import splitties.views.dsl.constraintlayout.below -import splitties.views.dsl.constraintlayout.bottomOfParent -import splitties.views.dsl.constraintlayout.constraintLayout -import splitties.views.dsl.constraintlayout.endOfParent -import splitties.views.dsl.constraintlayout.lParams -import splitties.views.dsl.constraintlayout.startOfParent -import splitties.views.dsl.constraintlayout.topOfParent -import splitties.views.dsl.core.add -import splitties.views.dsl.core.imageButton -import splitties.views.dsl.core.textView -import splitties.views.dsl.core.wrapContent -import splitties.views.gravityVerticalCenter -import splitties.views.imageDrawable -import splitties.views.textAppearance import java.util.UUID class ThemeListFragment : Fragment() { @@ -56,11 +33,9 @@ class ThemeListFragment : Fragment() { private lateinit var exportLauncher: ActivityResultLauncher - private lateinit var previewUi: KeyboardPreviewUi + private lateinit var themeListAdapter: ThemeListAdapter - private lateinit var adapter: ThemeListAdapter - - private lateinit var themeList: RecyclerView + private var followSystemDayNightTheme by ThemeManager.prefs.followSystemDayNightTheme private var beingExported: Theme.Custom? = null @@ -71,16 +46,19 @@ class ThemeListFragment : Fragment() { } } - private var followSystemDayNightTheme by ThemeManager.prefs.followSystemDayNightTheme + private suspend fun importErrorDialog(message: String) { + errorDialog(requireContext(), getString(R.string.import_error), message) + } override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) + imageLauncher = registerForActivityResult(CustomThemeActivity.Contract()) { result -> if (result != null) { when (result) { is CustomThemeActivity.BackgroundResult.Created -> { val theme = result.theme - adapter.prependTheme(theme) + themeListAdapter.prependTheme(theme) ThemeManager.saveTheme(theme) if (!followSystemDayNightTheme) { ThemeManager.switchTheme(theme) @@ -90,12 +68,12 @@ class ThemeListFragment : Fragment() { val name = result.name // Update the list first, as we rely on theme changed listener // in the case that the deleted theme was active - adapter.removeTheme(name) + themeListAdapter.removeTheme(name) ThemeManager.deleteTheme(name) } is CustomThemeActivity.BackgroundResult.Updated -> { val theme = result.theme - adapter.replaceTheme(theme) + themeListAdapter.replaceTheme(theme) ThemeManager.saveTheme(theme) } } @@ -119,9 +97,9 @@ class ThemeListFragment : Fragment() { .getOrThrow() withContext(Dispatchers.Main) { if (newCreated) { - adapter.prependTheme(theme) + themeListAdapter.prependTheme(theme) } else { - adapter.replaceTheme(theme) + themeListAdapter.replaceTheme(theme) } if (migrated) { ctx.toast(R.string.theme_migrated) @@ -154,99 +132,35 @@ class ThemeListFragment : Fragment() { } } - private suspend fun importErrorDialog(message: String) { - errorDialog(requireContext(), getString(R.string.import_error), message) - } - override fun onCreateView( inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle? - ): View = with(requireContext()) { - val activeTheme = ThemeManager.getActiveTheme() - - previewUi = KeyboardPreviewUi(this, activeTheme) - val preview = previewUi.root.apply { - scaleX = 0.5f - scaleY = 0.5f - outlineProvider = ViewOutlineProvider.BOUNDS - elevation = dp(4f) + ): View { + themeListAdapter = object : ThemeListAdapter() { + override fun onAddNewTheme() = addTheme() + override fun onSelectTheme(theme: Theme) = selectTheme(theme) + override fun onEditTheme(theme: Theme.Custom) = editTheme(theme) + override fun onExportTheme(theme: Theme.Custom) = exportTheme(theme) } - - val settingsText = textView { - setText(R.string.configure_theme) - textAppearance = resolveThemeAttribute(android.R.attr.textAppearanceListItem) - gravity = gravityVerticalCenter - } - val settingsButton = imageButton { - imageDrawable = drawable(R.drawable.ic_baseline_settings_24)?.apply { - setTint(styledColor(android.R.attr.colorControlNormal)) - } - background = styledDrawable(android.R.attr.actionBarItemBackground) - setOnClickListener { - findNavController().navigate(R.id.action_themeListFragment_to_themeSettingsFragment) - } - } - - val previewWrapper = constraintLayout { - add(preview, lParams(wrapContent, wrapContent) { - topOfParent(dp(-52)) - startOfParent() - endOfParent() - }) - add(settingsText, lParams(wrapContent, dp(48)) { - startOfParent(dp(64)) - bottomOfParent(dp(4)) - }) - add(settingsButton, lParams(dp(48), dp(48)) { - endOfParent(dp(64)) - bottomOfParent(dp(4)) - }) - backgroundColor = styledColor(android.R.attr.colorPrimary) - elevation = dp(4f) - } - - themeList = ResponsiveThemeListView(this).apply { - this@ThemeListFragment.adapter = object : ThemeListAdapter() { - override fun onAddNewTheme() = addTheme() - override fun onSelectTheme(theme: Theme) = selectTheme(theme) - override fun onEditTheme(theme: Theme.Custom) = editTheme(theme) - override fun onExportTheme(theme: Theme.Custom) = exportTheme(theme) - }.apply { - setThemes(ThemeManager.getAllThemes()) - } - adapter = this@ThemeListFragment.adapter - updateSelectedThemes() - applyNavBarInsetsBottomPadding() - } - + themeListAdapter.setThemes(ThemeManager.getAllThemes()) + updateSelectedThemes() ThemeManager.addOnChangedListener(onThemeChangeListener) - - constraintLayout { - add(previewWrapper, lParams(height = wrapContent) { - topOfParent() - startOfParent() - endOfParent() - }) - add(themeList, lParams { - below(previewWrapper) - startOfParent() - endOfParent() - bottomOfParent() - }) + return ResponsiveThemeListView(requireContext()).apply { + adapter = themeListAdapter + applyNavBarInsetsBottomPadding() } } private fun updateSelectedThemes(activeTheme: Theme? = null) { val active = activeTheme ?: ThemeManager.getActiveTheme() - previewUi.setTheme(active) var light: Theme? = null var dark: Theme? = null if (followSystemDayNightTheme) { light = ThemeManager.prefs.lightModeTheme.getValue() dark = ThemeManager.prefs.darkModeTheme.getValue() } - adapter.setSelectedThemes(active, light, dark) + themeListAdapter.setSelectedThemes(active, light, dark) } private fun addTheme() { @@ -278,7 +192,7 @@ class ThemeListFragment : Fragment() { override fun onClick(theme: Theme.Builtin) { val newTheme = theme.deriveCustomNoBackground(UUID.randomUUID().toString()) - adapter.prependTheme(newTheme) + themeListAdapter.prependTheme(newTheme) ThemeManager.saveTheme(newTheme) dialog.dismiss() } @@ -295,7 +209,7 @@ class ThemeListFragment : Fragment() { val ctx = requireContext() AlertDialog.Builder(ctx) .setIcon(ctx.styledDrawable(android.R.attr.alertDialogIcon)) - .setTitle(R.string.configure_theme) + .setTitle(R.string.configure) .setMessage(R.string.theme_message_follow_system_day_night_mode_enabled) .setPositiveButton(android.R.string.ok, null) .setNegativeButton(R.string.disable_it) { _, _ -> @@ -319,13 +233,6 @@ class ThemeListFragment : Fragment() { exportLauncher.launch(theme.name + ".zip") } - override fun onStop() { - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { - ThemeManager.syncToDeviceEncryptedStorage() - } - super.onStop() - } - override fun onDestroy() { ThemeManager.removeOnChangedListener(onThemeChangeListener) super.onDestroy() diff --git a/app/src/main/java/org/fcitx/fcitx5/android/ui/main/settings/theme/ThemeSettingsFragment.kt b/app/src/main/java/org/fcitx/fcitx5/android/ui/main/settings/theme/ThemeSettingsFragment.kt index 28f9c2e83..2bc5042dd 100644 --- a/app/src/main/java/org/fcitx/fcitx5/android/ui/main/settings/theme/ThemeSettingsFragment.kt +++ b/app/src/main/java/org/fcitx/fcitx5/android/ui/main/settings/theme/ThemeSettingsFragment.kt @@ -1,14 +1,6 @@ package org.fcitx.fcitx5.android.ui.main.settings.theme -import android.os.Build import org.fcitx.fcitx5.android.data.prefs.ManagedPreferenceFragment import org.fcitx.fcitx5.android.data.theme.ThemeManager -class ThemeSettingsFragment : ManagedPreferenceFragment(ThemeManager.prefs) { - override fun onStop() { - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { - ThemeManager.syncToDeviceEncryptedStorage() - } - super.onStop() - } -} +class ThemeSettingsFragment : ManagedPreferenceFragment(ThemeManager.prefs) diff --git a/app/src/main/java/org/fcitx/fcitx5/android/utils/AppUtil.kt b/app/src/main/java/org/fcitx/fcitx5/android/utils/AppUtil.kt index 8827990dc..3620aacca 100644 --- a/app/src/main/java/org/fcitx/fcitx5/android/utils/AppUtil.kt +++ b/app/src/main/java/org/fcitx/fcitx5/android/utils/AppUtil.kt @@ -42,7 +42,7 @@ object AppUtil { launchMainToDest(context, R.id.imListFragment) fun launchMainToThemeList(context: Context) = - launchMainToDest(context, R.id.themeListFragment) + launchMainToDest(context, R.id.themeFragment) fun launchMainToInputMethodConfig(context: Context, uniqueName: String, displayName: String) = launchMainToDest( diff --git a/app/src/main/res/navigation/settings_nav.xml b/app/src/main/res/navigation/settings_nav.xml index ab8f54899..f376adb65 100644 --- a/app/src/main/res/navigation/settings_nav.xml +++ b/app/src/main/res/navigation/settings_nav.xml @@ -58,8 +58,8 @@ app:popEnterAnim="@animator/nav_default_pop_enter_anim" app:popExitAnim="@animator/nav_default_pop_exit_anim" /> - - Tastenradius Horizontaler Tastenrand Vertikaler Tastenrand - Thema konfigurieren + Konfigurieren Bild wählen ... Dunkle Tasten Bild neu zuschneiden diff --git a/app/src/main/res/values-ru/strings.xml b/app/src/main/res/values-ru/strings.xml index 32ea43824..8e3f388a4 100644 --- a/app/src/main/res/values-ru/strings.xml +++ b/app/src/main/res/values-ru/strings.xml @@ -110,7 +110,7 @@ Радиус клавиш Горизонтальное поле клавиш Вертикальное поле клавиш - Настроить тему + Настроить Выбрать изображение… Темные клавиши Повторное кадрирование изображения diff --git a/app/src/main/res/values-zh-rCN/strings.xml b/app/src/main/res/values-zh-rCN/strings.xml index e04ec1e5c..66be9a90f 100644 --- a/app/src/main/res/values-zh-rCN/strings.xml +++ b/app/src/main/res/values-zh-rCN/strings.xml @@ -110,7 +110,7 @@ 按键圆角半径 按键横向边距 按键纵向边距 - 配置主题 + 配置 选择图片… 暗色按键 重新裁剪图片 diff --git a/app/src/main/res/values-zh-rTW/strings.xml b/app/src/main/res/values-zh-rTW/strings.xml index 153d7565d..47c71e0bc 100644 --- a/app/src/main/res/values-zh-rTW/strings.xml +++ b/app/src/main/res/values-zh-rTW/strings.xml @@ -110,7 +110,7 @@ 按鍵圓角半徑 按鍵橫向邊距 按鍵垂直邊距 - 配置主題 + 配置 選擇圖片… 深色按鍵 重新裁剪圖片 diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 3629ee704..b8c74cb2f 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -110,7 +110,7 @@ Key radius Key horizontal margin Key vertical margin - Configure Theme + Configure Choose Image… Dark Keys Re-Crop Image From abc48a63543ffdfa0a20f28e4ca0f1ded4894425 Mon Sep 17 00:00:00 2001 From: Rocka Date: Wed, 18 Oct 2023 01:53:49 +0800 Subject: [PATCH 059/381] Set pinyin customphrase include dir in cmake config --- app/src/main/cpp/CMakeLists.txt | 13 ++++++++----- app/src/main/cpp/native-lib.cpp | 2 +- .../src/main/cpp/cmake/FindLibIMEPinyin.cmake | 2 ++ lib/libime/src/main/cpp/cmake/FindLibIMETable.cmake | 2 ++ 4 files changed, 13 insertions(+), 6 deletions(-) diff --git a/app/src/main/cpp/CMakeLists.txt b/app/src/main/cpp/CMakeLists.txt index 563bae607..366dcb538 100644 --- a/app/src/main/cpp/CMakeLists.txt +++ b/app/src/main/cpp/CMakeLists.txt @@ -17,6 +17,7 @@ find_package(libime REQUIRED CONFIG) get_target_property(LIBIME_CMAKE_MODULES libime::cmake INTERFACE_INCLUDE_DIRECTORIES) set(CMAKE_MODULE_PATH ${LIBIME_CMAKE_MODULES} ${CMAKE_MODULE_PATH}) +find_package(LibIMECore MODULE) find_package(LibIMEPinyin MODULE) find_package(LibIMETable MODULE) @@ -45,14 +46,15 @@ find_package(Libevent) list(APPEND CMAKE_FIND_ROOT_PATH "${PREBUILT_DIR}/boost/${ANDROID_ABI}/lib/cmake") find_package(Boost 1.83.0 REQUIRED COMPONENTS headers filesystem iostreams CONFIG) -add_library(native-lib SHARED - native-lib.cpp - "${CMAKE_CURRENT_SOURCE_DIR}/../../../../lib/fcitx5-chinese-addons/src/main/cpp/fcitx5-chinese-addons/im/pinyin/customphrase.cpp" - ) +set(CHINESE_ADDONS_PINYIN_DIR "${CMAKE_CURRENT_SOURCE_DIR}/../../../../lib/fcitx5-chinese-addons/src/main/cpp/fcitx5-chinese-addons/im/pinyin") +add_library(pinyin-customphrase STATIC "${CHINESE_ADDONS_PINYIN_DIR}/customphrase.cpp") +target_include_directories(pinyin-customphrase INTERFACE "${CHINESE_ADDONS_PINYIN_DIR}") +target_link_libraries(pinyin-customphrase PRIVATE fmt::fmt-header-only Fcitx5::Utils LibIME::Core) + +add_library(native-lib SHARED native-lib.cpp) target_link_libraries(native-lib log libevent::core - fmt::fmt-header-only Fcitx5::Utils Fcitx5::Config Fcitx5::Core @@ -63,6 +65,7 @@ target_link_libraries(native-lib Boost::iostreams LibIME::Pinyin LibIME::Table + pinyin-customphrase ) add_custom_target(copy-fcitx5-modules diff --git a/app/src/main/cpp/native-lib.cpp b/app/src/main/cpp/native-lib.cpp index 7a0063891..932e67b98 100644 --- a/app/src/main/cpp/native-lib.cpp +++ b/app/src/main/cpp/native-lib.cpp @@ -34,7 +34,7 @@ #include #include -#include "../../../../lib/fcitx5-chinese-addons/src/main/cpp/fcitx5-chinese-addons/im/pinyin/customphrase.h" +#include "customphrase.h" #include "androidfrontend/androidfrontend_public.h" #include "jni-utils.h" diff --git a/lib/libime/src/main/cpp/cmake/FindLibIMEPinyin.cmake b/lib/libime/src/main/cpp/cmake/FindLibIMEPinyin.cmake index 2d6a9cf72..af9c2a237 100644 --- a/lib/libime/src/main/cpp/cmake/FindLibIMEPinyin.cmake +++ b/lib/libime/src/main/cpp/cmake/FindLibIMEPinyin.cmake @@ -4,6 +4,8 @@ set(LibIMEPinyin_FOUND TRUE) find_package(fcitx5 REQUIRED CONFIG) find_package(libime REQUIRED CONFIG) +find_package(LibIMECore MODULE) + if (NOT TARGET LibIME::Pinyin) # fix target dependency set_target_properties(libime::IMEPinyin PROPERTIES INTERFACE_LINK_LIBRARIES fcitx5::Fcitx5Utils) diff --git a/lib/libime/src/main/cpp/cmake/FindLibIMETable.cmake b/lib/libime/src/main/cpp/cmake/FindLibIMETable.cmake index 75b63140b..a09a5c32e 100644 --- a/lib/libime/src/main/cpp/cmake/FindLibIMETable.cmake +++ b/lib/libime/src/main/cpp/cmake/FindLibIMETable.cmake @@ -4,6 +4,8 @@ set(LibIMETable_FOUND TRUE) find_package(fcitx5 REQUIRED CONFIG) find_package(libime REQUIRED CONFIG) +find_package(LibIMECore MODULE) + if (NOT TARGET LibIME::Table) # fix target dependency set_target_properties(libime::IMETable PROPERTIES INTERFACE_LINK_LIBRARIES fcitx5::Fcitx5Utils) From e80db2c982dae216b78c82704d75471e5319c386 Mon Sep 17 00:00:00 2001 From: Rocka Date: Wed, 18 Oct 2023 01:55:52 +0800 Subject: [PATCH 060/381] Fix a deadlock case in FcitxDispatcher --- .../org/fcitx/fcitx5/android/core/FcitxDispatcher.kt | 12 ++++-------- .../org/fcitx/fcitx5/android/core/FcitxLifecycle.kt | 11 ++++------- 2 files changed, 8 insertions(+), 15 deletions(-) diff --git a/app/src/main/java/org/fcitx/fcitx5/android/core/FcitxDispatcher.kt b/app/src/main/java/org/fcitx/fcitx5/android/core/FcitxDispatcher.kt index 47c8ae392..d092b9845 100644 --- a/app/src/main/java/org/fcitx/fcitx5/android/core/FcitxDispatcher.kt +++ b/app/src/main/java/org/fcitx/fcitx5/android/core/FcitxDispatcher.kt @@ -96,7 +96,7 @@ class FcitxDispatcher(private val controller: FcitxController) : CoroutineDispat Timber.i("FcitxDispatcher stop()") return if (isRunning.compareAndSet(true, false)) { runBlocking { - bypass() + controller.nativeScheduleEmpty() runningLock.withLock { val rest = queue.toList() queue.clear() @@ -106,18 +106,14 @@ class FcitxDispatcher(private val controller: FcitxController) : CoroutineDispat } else emptyList() } - // bypass nativeLoopOnce if no code is executing in native dispatcher - private fun bypass() { - if (nativeLoopLock.isLocked) - controller.nativeScheduleEmpty() - } - override fun dispatch(context: CoroutineContext, block: Runnable) { if (!isRunning.get()) { throw IllegalStateException("Dispatcher is not in running state!") } queue.offer(WrappedRunnable(block)) - bypass() + // always call `nativeScheduleEmpty()` to prevent `nativeLoopOnce()` from blocking + // the thread when we have something to run + controller.nativeScheduleEmpty() } companion object { diff --git a/app/src/main/java/org/fcitx/fcitx5/android/core/FcitxLifecycle.kt b/app/src/main/java/org/fcitx/fcitx5/android/core/FcitxLifecycle.kt index e69fcbaec..9eb197f4a 100644 --- a/app/src/main/java/org/fcitx/fcitx5/android/core/FcitxLifecycle.kt +++ b/app/src/main/java/org/fcitx/fcitx5/android/core/FcitxLifecycle.kt @@ -94,28 +94,25 @@ fun interface FcitxLifecycleObserver { class FcitxLifecycleCoroutineScope( val lifecycle: FcitxLifecycle, override val coroutineContext: CoroutineContext = SupervisorJob() -) : - CoroutineScope, FcitxLifecycleObserver { +) : CoroutineScope, FcitxLifecycleObserver { override fun onStateChanged(event: FcitxLifecycle.Event) { if (lifecycle.currentState >= FcitxLifecycle.State.STOPPING) { coroutineContext.cancelChildren() } } - } suspend fun FcitxLifecycle.whenAtState( state: FcitxLifecycle.State, block: suspend CoroutineScope.() -> T ): T = - if (state == currentState) - block(lifecycleScope) + if (state == currentState) block(lifecycleScope) else AtStateHelper(this, state).run(block) -suspend fun FcitxLifecycle.whenReady(block: suspend CoroutineScope.() -> T) = +suspend inline fun FcitxLifecycle.whenReady(noinline block: suspend CoroutineScope.() -> T) = whenAtState(FcitxLifecycle.State.READY, block) -suspend fun FcitxLifecycle.whenStopped(block: suspend CoroutineScope.() -> T) = +suspend inline fun FcitxLifecycle.whenStopped(noinline block: suspend CoroutineScope.() -> T) = whenAtState(FcitxLifecycle.State.STOPPED, block) fun FcitxLifecycle.launchWhenReady(block: suspend CoroutineScope.() -> T) = From 67606eb4f0c9e15ebfa0b06bc28064fcb326afa0 Mon Sep 17 00:00:00 2001 From: Rocka Date: Wed, 18 Oct 2023 02:04:17 +0800 Subject: [PATCH 061/381] Re-create InputView on expandKeypressArea pref change --- .../org/fcitx/fcitx5/android/input/FcitxInputMethodService.kt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/app/src/main/java/org/fcitx/fcitx5/android/input/FcitxInputMethodService.kt b/app/src/main/java/org/fcitx/fcitx5/android/input/FcitxInputMethodService.kt index c3795f5ff..d0b73275d 100644 --- a/app/src/main/java/org/fcitx/fcitx5/android/input/FcitxInputMethodService.kt +++ b/app/src/main/java/org/fcitx/fcitx5/android/input/FcitxInputMethodService.kt @@ -135,6 +135,7 @@ class FcitxInputMethodService : LifecycleInputMethodService() { } pkgNameCache = PackageNameCache(this) AppPrefs.getInstance().apply { + keyboard.expandKeypressArea.registerOnChangeListener(recreateInputViewListener) advanced.disableAnimation.registerOnChangeListener(recreateInputViewListener) } ThemeManager.addOnChangedListener(onThemeChangeListener) @@ -712,6 +713,7 @@ class FcitxInputMethodService : LifecycleInputMethodService() { override fun onDestroy() { AppPrefs.getInstance().apply { + keyboard.expandKeypressArea.unregisterOnChangeListener(recreateInputViewListener) advanced.disableAnimation.unregisterOnChangeListener(recreateInputViewListener) } ThemeManager.removeOnChangedListener(onThemeChangeListener) From d3be515e21eab80ce4570e6afe99b56d3372dc92 Mon Sep 17 00:00:00 2001 From: Rocka Date: Wed, 18 Oct 2023 02:04:55 +0800 Subject: [PATCH 062/381] Update fcitx5 submodules --- lib/fcitx5-chinese-addons/src/main/cpp/fcitx5-chinese-addons | 2 +- lib/fcitx5/src/main/cpp/fcitx5 | 2 +- lib/libime/src/main/cpp/libime | 2 +- plugin/anthy/src/main/cpp/fcitx5-anthy | 2 +- plugin/rime/src/main/cpp/fcitx5-rime | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/lib/fcitx5-chinese-addons/src/main/cpp/fcitx5-chinese-addons b/lib/fcitx5-chinese-addons/src/main/cpp/fcitx5-chinese-addons index cfd66f8de..75893d066 160000 --- a/lib/fcitx5-chinese-addons/src/main/cpp/fcitx5-chinese-addons +++ b/lib/fcitx5-chinese-addons/src/main/cpp/fcitx5-chinese-addons @@ -1 +1 @@ -Subproject commit cfd66f8def0d75a0d79eb6a2191c2cf86e38b797 +Subproject commit 75893d0664074a4b1d22121952b595c028a4dee4 diff --git a/lib/fcitx5/src/main/cpp/fcitx5 b/lib/fcitx5/src/main/cpp/fcitx5 index edef0e17f..cd872a152 160000 --- a/lib/fcitx5/src/main/cpp/fcitx5 +++ b/lib/fcitx5/src/main/cpp/fcitx5 @@ -1 +1 @@ -Subproject commit edef0e17f4dc96498f609e6bd5e4c8e98e4973d7 +Subproject commit cd872a1528206bceec26d2d6efa5e3a978df72de diff --git a/lib/libime/src/main/cpp/libime b/lib/libime/src/main/cpp/libime index e555efb3d..78795aca1 160000 --- a/lib/libime/src/main/cpp/libime +++ b/lib/libime/src/main/cpp/libime @@ -1 +1 @@ -Subproject commit e555efb3db3b25e44648018c0ffb5bf0c8d25b10 +Subproject commit 78795aca1f18a2fbb31d8128a7c350cdd6e680b1 diff --git a/plugin/anthy/src/main/cpp/fcitx5-anthy b/plugin/anthy/src/main/cpp/fcitx5-anthy index 8fe86d6c3..2f24fe967 160000 --- a/plugin/anthy/src/main/cpp/fcitx5-anthy +++ b/plugin/anthy/src/main/cpp/fcitx5-anthy @@ -1 +1 @@ -Subproject commit 8fe86d6c301014b1ec3c16c27456eecca75bbc00 +Subproject commit 2f24fe967b6df61c6e37d0ee618bf5e53a9e35c2 diff --git a/plugin/rime/src/main/cpp/fcitx5-rime b/plugin/rime/src/main/cpp/fcitx5-rime index 3ca73255b..c71ea8ce2 160000 --- a/plugin/rime/src/main/cpp/fcitx5-rime +++ b/plugin/rime/src/main/cpp/fcitx5-rime @@ -1 +1 @@ -Subproject commit 3ca73255b76ed047f346796fc86a337bab060371 +Subproject commit c71ea8ce23e710c70831f1f0a4354349c6dafeba From fc12700c3cedb3744972240a9b342229d6cf42d3 Mon Sep 17 00:00:00 2001 From: Qijia Liu Date: Tue, 17 Oct 2023 11:26:31 -0700 Subject: [PATCH 063/381] Add hangul plugin (#350) Co-authored-by: Rocka --- .github/workflows/pull_request.yml | 2 +- .gitmodules | 3 ++ README.md | 6 ++- lib/fcitx5/src/main/cpp/prebuilt | 2 +- plugin/hangul/build.gradle.kts | 53 +++++++++++++++++++ .../licenses/libraries/fcitx5-hangul.json | 11 ++++ .../hangul/licenses/libraries/libhangul.json | 11 ++++ plugin/hangul/src/main/AndroidManifest.xml | 6 +++ .../src/main/assets/usr/share/libhangul | 1 + plugin/hangul/src/main/cpp/CMakeLists.txt | 26 +++++++++ plugin/hangul/src/main/cpp/fcitx5-hangul | 1 + plugin/hangul/src/main/res/values/strings.xml | 6 +++ plugin/hangul/src/main/res/xml/plugin.xml | 6 +++ settings.gradle.kts | 1 + 14 files changed, 131 insertions(+), 4 deletions(-) create mode 100644 plugin/hangul/build.gradle.kts create mode 100644 plugin/hangul/licenses/libraries/fcitx5-hangul.json create mode 100644 plugin/hangul/licenses/libraries/libhangul.json create mode 100644 plugin/hangul/src/main/AndroidManifest.xml create mode 120000 plugin/hangul/src/main/assets/usr/share/libhangul create mode 100644 plugin/hangul/src/main/cpp/CMakeLists.txt create mode 160000 plugin/hangul/src/main/cpp/fcitx5-hangul create mode 100644 plugin/hangul/src/main/res/values/strings.xml create mode 100644 plugin/hangul/src/main/res/xml/plugin.xml diff --git a/.github/workflows/pull_request.yml b/.github/workflows/pull_request.yml index ca094c8c8..85b76f18e 100644 --- a/.github/workflows/pull_request.yml +++ b/.github/workflows/pull_request.yml @@ -34,7 +34,7 @@ jobs: - name: Regenerate symlinks pointing to submodule (Windows) if: ${{ matrix.os == 'windows-2022' }} run: | - Remove-Item -Recurse app/src/main/assets/usr/share + Remove-Item -Recurse app/src/main/assets/usr/share, plugin/hangul/src/main/assets/usr/share/libhangul git checkout -- * - name: Setup Java diff --git a/.gitmodules b/.gitmodules index 19cffebd2..1a95b9e38 100644 --- a/.gitmodules +++ b/.gitmodules @@ -37,3 +37,6 @@ [submodule "plugin/rime/src/main/cpp/rime-stroke"] path = plugin/rime/src/main/cpp/rime-stroke url = git@github.com:rime/rime-stroke.git +[submodule "plugin/hangul/src/main/cpp/fcitx5-hangul"] + path = plugin/hangul/src/main/cpp/fcitx5-hangul + url = git@github.com:fcitx/fcitx5-hangul.git diff --git a/README.md b/README.md index 51894ca6b..243c055f3 100644 --- a/README.md +++ b/README.md @@ -21,8 +21,10 @@ GitHub: [![release version](https://img.shields.io/github/v/release/fcitx5-andro - English (with spell check) - Chinese (Pinyin, Shuangpin, Wubi, Cangjie and custom tables) -- Vietnamese (Based on UniKey, supports Telex, VNI and VIQR) +- Vietnamese (via [UniKey Plugin](./plugin/unikey), supports Telex, VNI and VIQR) - Japanese (via [Anthy Plugin](./plugin/anthy)) +- Korean (via [Hangul Plugin](./plugin/hangul)) +- Generic (via [RIME Plugin](./plugin/rime)) ### Implemented Features @@ -93,7 +95,7 @@ git submodule update --init --recursive On Windows, you may need to regenerate symlinks to submodules. ```powershell -Remove-Item -Recurse app/src/main/assets/usr/share +Remove-Item -Recurse app/src/main/assets/usr/share, plugin/hangul/src/main/assets/usr/share/libhangul git checkout -- . ``` diff --git a/lib/fcitx5/src/main/cpp/prebuilt b/lib/fcitx5/src/main/cpp/prebuilt index fd3e6c4c1..65ed2d60f 160000 --- a/lib/fcitx5/src/main/cpp/prebuilt +++ b/lib/fcitx5/src/main/cpp/prebuilt @@ -1 +1 @@ -Subproject commit fd3e6c4c17499621323f86c631f32948b5c28a10 +Subproject commit 65ed2d60fb155dfa37c1a8c1d5ec87b09b08fd40 diff --git a/plugin/hangul/build.gradle.kts b/plugin/hangul/build.gradle.kts new file mode 100644 index 000000000..97ede4aeb --- /dev/null +++ b/plugin/hangul/build.gradle.kts @@ -0,0 +1,53 @@ +@file:Suppress("UnstableApiUsage") + +plugins { + id("org.fcitx.fcitx5.android.app-convention") + id("org.fcitx.fcitx5.android.plugin-app-convention") + id("org.fcitx.fcitx5.android.native-app-convention") + id("org.fcitx.fcitx5.android.build-metadata") + id("org.fcitx.fcitx5.android.data-descriptor") + id("org.fcitx.fcitx5.android.fcitx-component") +} + +android { + namespace = "org.fcitx.fcitx5.android.plugin.hangul" + + defaultConfig { + applicationId = "org.fcitx.fcitx5.android.plugin.hangul" + + externalNativeBuild { + cmake { + targets( + "hangul" + ) + } + } + } + + buildTypes { + release { + resValue("string", "app_name", "@string/app_name_release") + } + debug { + resValue("string", "app_name", "@string/app_name_debug") + } + } + + packaging { + jniLibs { + excludes += setOf( + "**/libc++_shared.so", + "**/libFcitx5*" + ) + } + } +} + +aboutLibraries { + configPath = "plugin/hangul/licenses" +} + +dependencies { + implementation(project(":lib:fcitx5")) + implementation(project(":lib:plugin-base")) +} diff --git a/plugin/hangul/licenses/libraries/fcitx5-hangul.json b/plugin/hangul/licenses/libraries/fcitx5-hangul.json new file mode 100644 index 000000000..cc92fb037 --- /dev/null +++ b/plugin/hangul/licenses/libraries/fcitx5-hangul.json @@ -0,0 +1,11 @@ +{ + "uniqueId": "fcitx/fcitx5-hangul", + "artifactVersion": "5.1.0", + "description": "Hangul Wrapper for Fcitx", + "name": "fcitx/fcitx5-hangul", + "website": "https://github.com/fcitx/fcitx5-hangul", + "tag": "native", + "licenses": [ + "LGPL-2.1-or-later" + ] +} diff --git a/plugin/hangul/licenses/libraries/libhangul.json b/plugin/hangul/licenses/libraries/libhangul.json new file mode 100644 index 000000000..42ad3873e --- /dev/null +++ b/plugin/hangul/licenses/libraries/libhangul.json @@ -0,0 +1,11 @@ +{ + "uniqueId": "libhangul/libhangul", + "artifactVersion": "0.1.0", + "description": "A library to support hangul input method logic", + "name": "libhangul/libhangul", + "website": "https://github.com/libhangul/libhangul", + "tag": "native", + "licenses": [ + "LGPL-2.1-or-later" + ] +} diff --git a/plugin/hangul/src/main/AndroidManifest.xml b/plugin/hangul/src/main/AndroidManifest.xml new file mode 100644 index 000000000..654a95e5f --- /dev/null +++ b/plugin/hangul/src/main/AndroidManifest.xml @@ -0,0 +1,6 @@ + + + + + + diff --git a/plugin/hangul/src/main/assets/usr/share/libhangul b/plugin/hangul/src/main/assets/usr/share/libhangul new file mode 120000 index 000000000..c365d0cff --- /dev/null +++ b/plugin/hangul/src/main/assets/usr/share/libhangul @@ -0,0 +1 @@ +../../../../../../../lib/fcitx5/src/main/cpp/prebuilt/libhangul/data/libhangul \ No newline at end of file diff --git a/plugin/hangul/src/main/cpp/CMakeLists.txt b/plugin/hangul/src/main/cpp/CMakeLists.txt new file mode 100644 index 000000000..9865bf609 --- /dev/null +++ b/plugin/hangul/src/main/cpp/CMakeLists.txt @@ -0,0 +1,26 @@ +cmake_minimum_required(VERSION 3.18) + +project(fcitx5-android-plugin-hangul VERSION 0.0.7) + +# For reproducible build +add_link_options("LINKER:--hash-style=gnu,--build-id=none") + +# prefab dependency +find_package(fcitx5 REQUIRED CONFIG) +get_target_property(FCITX5_CMAKE_MODULES fcitx5::cmake INTERFACE_INCLUDE_DIRECTORIES) +set(CMAKE_MODULE_PATH ${FCITX5_CMAKE_MODULES} ${CMAKE_MODULE_PATH}) + +find_package(ECM MODULE) +find_package(Fcitx5Core MODULE) +find_package(Fcitx5Module MODULE) + +set(PREBUILT_DIR "${CMAKE_CURRENT_SOURCE_DIR}/../../../../../lib/fcitx5/src/main/cpp/prebuilt") + +add_library(Hangul_static STATIC IMPORTED) +set_target_properties(Hangul_static PROPERTIES + IMPORTED_LOCATION "${PREBUILT_DIR}/libhangul/${ANDROID_ABI}/lib/libhangul.a" + INTERFACE_INCLUDE_DIRECTORIES "${PREBUILT_DIR}/libhangul/${ANDROID_ABI}/include/hangul-1.0" + ) + +set(HANGUL_TARGET Hangul_static) +add_subdirectory(fcitx5-hangul) diff --git a/plugin/hangul/src/main/cpp/fcitx5-hangul b/plugin/hangul/src/main/cpp/fcitx5-hangul new file mode 160000 index 000000000..0470df3b4 --- /dev/null +++ b/plugin/hangul/src/main/cpp/fcitx5-hangul @@ -0,0 +1 @@ +Subproject commit 0470df3b40ce7a61a0990d6691d3c7209ffc92da diff --git a/plugin/hangul/src/main/res/values/strings.xml b/plugin/hangul/src/main/res/values/strings.xml new file mode 100644 index 000000000..2364089f3 --- /dev/null +++ b/plugin/hangul/src/main/res/values/strings.xml @@ -0,0 +1,6 @@ + + + Fcitx5 for Android (Hangul Plugin | Debug) + Fcitx5 for Android (Hangul Plugin) + Hangul (Korean input method) engine support for Fcitx5 + \ No newline at end of file diff --git a/plugin/hangul/src/main/res/xml/plugin.xml b/plugin/hangul/src/main/res/xml/plugin.xml new file mode 100644 index 000000000..0836e1e7a --- /dev/null +++ b/plugin/hangul/src/main/res/xml/plugin.xml @@ -0,0 +1,6 @@ + + + 0.1 + fcitx5-hangul + @string/description + \ No newline at end of file diff --git a/settings.gradle.kts b/settings.gradle.kts index 8a7b38fb6..5f6ca4acc 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -31,3 +31,4 @@ include(":plugin:anthy") include(":plugin:clipboard-filter") include(":plugin:unikey") include(":plugin:rime") +include(":plugin:hangul") From 55f03277f5838c008cdb4872855eaa6ba7eaa043 Mon Sep 17 00:00:00 2001 From: Rocka Date: Wed, 18 Oct 2023 16:52:33 +0800 Subject: [PATCH 064/381] Remove FcitxDispatcher's nativeLoopLock mutex --- .../java/org/fcitx/fcitx5/android/core/FcitxDispatcher.kt | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/app/src/main/java/org/fcitx/fcitx5/android/core/FcitxDispatcher.kt b/app/src/main/java/org/fcitx/fcitx5/android/core/FcitxDispatcher.kt index d092b9845..5749808f7 100644 --- a/app/src/main/java/org/fcitx/fcitx5/android/core/FcitxDispatcher.kt +++ b/app/src/main/java/org/fcitx/fcitx5/android/core/FcitxDispatcher.kt @@ -52,7 +52,6 @@ class FcitxDispatcher(private val controller: FcitxController) : CoroutineDispat fun nativeExit() } - private val nativeLoopLock = Mutex() private val runningLock = Mutex() private val queue = ConcurrentLinkedQueue() @@ -64,17 +63,15 @@ class FcitxDispatcher(private val controller: FcitxController) : CoroutineDispat * This function returns immediately */ fun start() { + Timber.d("FcitxDispatcher start()") internalScope.launch { runningLock.withLock { - Timber.d("FcitxDispatcher start()") if (isRunning.compareAndSet(false, true)) { Timber.d("nativeStartup()") controller.nativeStartup() while (isActive && isRunning.get()) { // blocking... - nativeLoopLock.withLock { - controller.nativeLoopOnce() - } + controller.nativeLoopOnce() // do scheduled jobs while (true) { val block = queue.poll() ?: break From 3b27846dbd195ab83059e1157539d39b89ad84b2 Mon Sep 17 00:00:00 2001 From: Rocka Date: Wed, 18 Oct 2023 17:01:21 +0800 Subject: [PATCH 065/381] Implement "replace table dict" UI --- .../fcitx5/android/data/table/TableManager.kt | 40 ++++++--- .../main/settings/TableInputMethodFragment.kt | 84 +++++++++++++++++-- app/src/main/res/values-de/strings.xml | 2 +- app/src/main/res/values-zh-rCN/strings.xml | 2 +- app/src/main/res/values-zh-rTW/strings.xml | 2 +- app/src/main/res/values/strings.xml | 4 +- 6 files changed, 110 insertions(+), 24 deletions(-) diff --git a/app/src/main/java/org/fcitx/fcitx5/android/data/table/TableManager.kt b/app/src/main/java/org/fcitx/fcitx5/android/data/table/TableManager.kt index 8b8643fa8..f1a8db4c5 100644 --- a/app/src/main/java/org/fcitx/fcitx5/android/data/table/TableManager.kt +++ b/app/src/main/java/org/fcitx/fcitx5/android/data/table/TableManager.kt @@ -22,18 +22,15 @@ object TableManager { ).also { it.mkdirs() } fun inputMethods(): List = - inputMethodDir - .listFiles() - ?.mapNotNull { confFile -> - runCatching { - TableBasedInputMethod.new(confFile).apply { - table = - File(tableDicDir, tableFileName) - .takeIf { it.extension == "dict" } - ?.let { LibIMEDictionary(it) } + inputMethodDir.listFiles()?.mapNotNull { confFile -> + runCatching { + TableBasedInputMethod.new(confFile).apply { + runCatching { + table = LibIMEDictionary(File(tableDicDir, tableFileName)) } - }.getOrNull() - } ?: listOf() + } + }.getOrNull() + } ?: emptyList() fun importFromZip(src: InputStream): Result = runCatching { @@ -92,6 +89,27 @@ object TableManager { return im } + fun replaceTableDict( + im: TableBasedInputMethod, + dictName: String, + dictStream: InputStream + ): Result = runCatching { + withTempDir { tempDir -> + val dictFile = File(tempDir, dictName).also { + it.outputStream().use { o -> dictStream.use { i -> i.copyTo(o) } } + } + val dict = Dictionary.new(dictFile)!! + runCatching { + dict.toLibIMEDictionary(File(tempDir, im.tableFileName)) + }.onSuccess { + it.file.copyTo(File(tableDicDir, im.tableFileName)) + }.onFailure { + dictFile.delete() + errorRuntime(R.string.invalid_table_dict, it.message) + }.getOrThrow() + } + } + @JvmStatic external fun tableDictConv(src: String, dest: String, mode: Boolean) diff --git a/app/src/main/java/org/fcitx/fcitx5/android/ui/main/settings/TableInputMethodFragment.kt b/app/src/main/java/org/fcitx/fcitx5/android/ui/main/settings/TableInputMethodFragment.kt index 11e2762e9..9789653e9 100644 --- a/app/src/main/java/org/fcitx/fcitx5/android/ui/main/settings/TableInputMethodFragment.kt +++ b/app/src/main/java/org/fcitx/fcitx5/android/ui/main/settings/TableInputMethodFragment.kt @@ -31,6 +31,7 @@ import org.fcitx.fcitx5.android.utils.NaiveDustman import org.fcitx.fcitx5.android.utils.errorDialog import org.fcitx.fcitx5.android.utils.notificationManager import org.fcitx.fcitx5.android.utils.queryFileName +import splitties.resources.drawable import splitties.resources.styledDrawable import splitties.views.imageDrawable @@ -41,10 +42,12 @@ class TableInputMethodFragment : Fragment(), OnItemChangedListener private lateinit var confLauncher: ActivityResultLauncher private lateinit var dictLauncher: ActivityResultLauncher + private lateinit var replaceLauncher: ActivityResultLauncher private var confUri: Uri? = null private var dictUri: Uri? = null private var filesSelectionDialog: AlertDialog? = null + private var tableToReplace: TableBasedInputMethod? = null private val dustman = NaiveDustman() @@ -56,16 +59,18 @@ class TableInputMethodFragment : Fragment(), OnItemChangedListener - if (it.tableFileExists) return@setOnClickListener + visibility = View.VISIBLE + imageDrawable = + if (it.tableFileExists) drawable(R.drawable.ic_baseline_edit_24) + else styledDrawable(android.R.attr.alertDialogIcon) + setOnClickListener { _ -> + tableToReplace = it lifecycleScope.launch { - errorDialog( - requireContext(), - getString(R.string.table_file_does_not_exist_title), - getString(R.string.table_file_does_not_exist_message, it.tableFileName) - ) + if (it.tableFileExists) { + showReplaceTableDialog(it) + } else { + showMissingTableDictDialog(it) + } } } } @@ -138,6 +143,9 @@ class TableInputMethodFragment : Fragment(), OnItemChangedListener if (uri != null) prepareDictFromUri(uri) } + replaceLauncher = registerForActivityResult(ActivityResultContracts.GetContent()) { uri -> + if (uri != null) replaceDictFromUri(uri) + } } private fun showImportDialog() { @@ -305,6 +313,64 @@ class TableInputMethodFragment : Fragment(), OnItemChangedListener + replaceLauncher.launch("*/*") + } + .setNegativeButton(android.R.string.cancel, null) + .show() + } + + private fun showMissingTableDictDialog(im: TableBasedInputMethod) { + AlertDialog.Builder(requireContext()) + .setIconAttribute(android.R.attr.alertDialogIcon) + .setTitle(R.string.table_file_does_not_exist_title) + .setMessage(getString(R.string.table_file_does_not_exist_message, im.tableFileName)) + .setPositiveButton(android.R.string.ok, null) + .setNeutralButton(R.string.table_file_placeholder) { _, _ -> + replaceLauncher.launch("*/*") + } + .show() + } + private fun reloadConfig() { if (!dustman.dirty) return resetDustman() diff --git a/app/src/main/res/values-de/strings.xml b/app/src/main/res/values-de/strings.xml index a3765a1a0..7ceb460df 100644 --- a/app/src/main/res/values-de/strings.xml +++ b/app/src/main/res/values-de/strings.xml @@ -140,7 +140,7 @@ Die Erweiterung von \"%1$s\" ist keine Tabelleneingabemethode (.zip) Konfigurationsdatei der Tabelleneingabemethode \"%1$s\" existiert schon Tabelle nicht auffindbar - Das Wörterbuch der Tabelleneingabe \"%1$s\" ist nicht vorhanden. Bitte checken Sie die dict-Datei oder löschen und reimportieren Sie sie. + Das Wörterbuch der Tabelleneingabe \"%1$s\" ist nicht vorhanden. Bitte checken Sie die dict-Datei reimportieren Sie sie. Tabelle importieren Aus ZIP-Datei... Aus getrennten Dateien ... diff --git a/app/src/main/res/values-zh-rCN/strings.xml b/app/src/main/res/values-zh-rCN/strings.xml index 66be9a90f..06e64347a 100644 --- a/app/src/main/res/values-zh-rCN/strings.xml +++ b/app/src/main/res/values-zh-rCN/strings.xml @@ -182,7 +182,7 @@ \"%1$s\" 的拓展名不是码表输入法包( .zip ) 码表输入法配置文件 \"%1$s\" 已存在 码表不可用 - 码表词典 \"%1$s\" 不存在。请检查词典文件,或删除并重新导入。 + 码表词典 \"%1$s\" 不存在。请检查词典文件,或重新导入。 导入码表 从 zip 导入… 从单独的文件导入… diff --git a/app/src/main/res/values-zh-rTW/strings.xml b/app/src/main/res/values-zh-rTW/strings.xml index 47c71e0bc..8e1765dc5 100644 --- a/app/src/main/res/values-zh-rTW/strings.xml +++ b/app/src/main/res/values-zh-rTW/strings.xml @@ -182,7 +182,7 @@ \"%1$s\" 的副檔名不是碼表輸入法包( .zip ) 碼表輸入法配置檔 \"%1$s\" 已存在 碼表不可用 - 碼表字典 \"%1$s\" 不存在。請檢查字典文件,或刪除並重新匯入。 + 碼表字典 \"%1$s\" 不存在。請檢查字典文件,並重新匯入。 匯入碼表 從 zip 匯入… 從單獨的檔案匯入… diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index b8c74cb2f..ae76d25a1 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -182,7 +182,7 @@ The extension of \"%1$s\" does not indicate a table input method package (.zip) Table input method configuration file \"%1$s\" already exists Table unavailable - Table dictionary \"%1$s\" does not exist. Please check the table dict file or delete and re-import. + Table dictionary \"%1$s\" does not exist. Please check the dict file or re-import. Import Table From zip… From separate files… @@ -250,4 +250,6 @@ Grant permission Clipboard database would be completely cleared, including pinned items. Processed? Expand keypress area to the edge + Update table + Table dictionary \"%1$s\" exists. You may import a new dict and replace it. \ No newline at end of file From a4b484d70e2781713eb0fc91f8b297437b828cfb Mon Sep 17 00:00:00 2001 From: Rocka Date: Wed, 18 Oct 2023 19:02:11 +0800 Subject: [PATCH 066/381] Exclude androidx profileinstaller from deps and other minor cleanup... --- app/build.gradle.kts | 7 +++++++ app/src/main/AndroidManifest.xml | 5 +++++ .../android/input/FcitxInputMethodService.kt | 4 +--- .../android/input/status/StatusAreaWindow.kt | 15 ++++++++------ app/src/main/res/layout/activity_setup.xml | 20 +++++++++++-------- gradle.properties | 2 ++ 6 files changed, 36 insertions(+), 17 deletions(-) diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 734580a10..62d92fe95 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -137,3 +137,10 @@ dependencies { androidTestImplementation(libs.androidx.lifecycle.testing) androidTestImplementation(libs.junit) } + +// remove Baseline Profile Installer or whatever it is... +configurations { + all { + exclude(group = "androidx.profileinstaller", module = "profileinstaller") + } +} diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index d1f6d553a..7c16296c4 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -160,6 +160,11 @@ android:name="splitties.init.AppCtxInitializer" android:value="androidx.startup" tools:node="remove" /> + + diff --git a/app/src/main/java/org/fcitx/fcitx5/android/input/FcitxInputMethodService.kt b/app/src/main/java/org/fcitx/fcitx5/android/input/FcitxInputMethodService.kt index d0b73275d..85596a058 100644 --- a/app/src/main/java/org/fcitx/fcitx5/android/input/FcitxInputMethodService.kt +++ b/app/src/main/java/org/fcitx/fcitx5/android/input/FcitxInputMethodService.kt @@ -671,7 +671,7 @@ class FcitxInputMethodService : LifecycleInputMethodService() { .addStyle(style) .build() val spec = InlinePresentationSpec - .Builder(InlinePresentationSpecMinSize, InlinePresentationSpecMaxSize) + .Builder(Size(0, 0), Size(Int.MAX_VALUE, Int.MAX_VALUE)) .setStyle(styleBundle) .build() return InlineSuggestionsRequest.Builder(listOf(spec)) @@ -724,8 +724,6 @@ class FcitxInputMethodService : LifecycleInputMethodService() { companion object { const val DeleteSurroundingFlag = "org.fcitx.fcitx5.android.DELETE_SURROUNDING" - private val InlinePresentationSpecMinSize = Size(0, 0) - private val InlinePresentationSpecMaxSize = Size(Int.MAX_VALUE, Int.MAX_VALUE) } } \ No newline at end of file diff --git a/app/src/main/java/org/fcitx/fcitx5/android/input/status/StatusAreaWindow.kt b/app/src/main/java/org/fcitx/fcitx5/android/input/status/StatusAreaWindow.kt index 374888325..e509b082e 100644 --- a/app/src/main/java/org/fcitx/fcitx5/android/input/status/StatusAreaWindow.kt +++ b/app/src/main/java/org/fcitx/fcitx5/android/input/status/StatusAreaWindow.kt @@ -1,9 +1,9 @@ package org.fcitx.fcitx5.android.input.status +import android.os.Build import android.view.View import android.widget.PopupMenu import android.widget.Toast -import androidx.core.view.MenuCompat import androidx.lifecycle.lifecycleScope import kotlinx.coroutines.launch import org.fcitx.fcitx5.android.R @@ -81,18 +81,19 @@ class StatusAreaWindow : InputWindow.ExtendedInputWindow(), override fun onItemClick(view: View, entry: StatusAreaEntry) { when (entry) { is StatusAreaEntry.Fcitx -> { - val menu = entry.action.menu - if (menu.isNullOrEmpty()) { + val actions = entry.action.menu + if (actions.isNullOrEmpty()) { activateAction(entry.action) return } val popup = PopupMenu(context, view) + val menu = popup.menu var groupId = 0 // Menu.NONE; ungrouped - menu.forEach { + actions.forEach { if (it.isSeparator) { groupId++ } else { - popup.menu.add(groupId, 0, 0, it.shortText).apply { + menu.add(groupId, 0, 0, it.shortText).apply { setOnMenuItemClickListener { _ -> activateAction(it) true @@ -100,7 +101,9 @@ class StatusAreaWindow : InputWindow.ExtendedInputWindow(), } } } - MenuCompat.setGroupDividerEnabled(popup.menu, true) + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) { + menu.setGroupDividerEnabled(true) + } popup.show() } is StatusAreaEntry.Android -> when (entry.type) { diff --git a/app/src/main/res/layout/activity_setup.xml b/app/src/main/res/layout/activity_setup.xml index 654961078..4c8eee7aa 100644 --- a/app/src/main/res/layout/activity_setup.xml +++ b/app/src/main/res/layout/activity_setup.xml @@ -5,34 +5,38 @@ android:layout_width="match_parent" android:layout_height="match_parent" tools:context=".ui.setup.SetupActivity"> + + app:layout_constraintTop_toTopOf="parent" /> +