Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
aa361b8
Fix RecentlyUsed#insert with existing value
rocka Oct 21, 2025
ae0f26b
Add option to draw key border stroke instead of shadow (#799)
plateaukao Oct 26, 2025
2148dee
Fix EditorInfoWindow crash on empty contentMimeTypes
rocka Oct 26, 2025
13dbc01
Update dependencies
rocka Oct 31, 2025
d7e5df6
Update fcitx5 submodules
rocka Oct 31, 2025
8c61267
Sync translation
rocka Oct 31, 2025
8f2db70
Include package signature info and link to other platforms in README.md
rocka Oct 31, 2025
fe75dff
Update licenses for libraries
rocka Nov 1, 2025
5a4870c
Bump 0.1.2
rocka Nov 1, 2025
adbe322
Migrate deprecations in gradle 8.14 (#783)
rocka Nov 2, 2025
4ad17d1
Update dependencies and migrate deprecations
rocka Nov 2, 2025
bca654e
Improve BaseDynamicListUi FAB animation
rocka Nov 2, 2025
18c659c
Fix CandidatesView popup without focusing input
rocka Nov 2, 2025
5806ea5
Call clearMetaKeyStates on hardware modifier key up
rocka Nov 6, 2025
6076112
Unify fcitx KeySym & KeyState construction from Android KeyEvent
rocka Nov 7, 2025
af8ad14
Avoid showing CharacterPickerDialog with keyboard shortcut
rocka Nov 7, 2025
ca24108
Fix BaseDynamicListUi FAB animation glich
rocka Nov 7, 2025
47e83af
Fix KeyState form space key
rocka Nov 7, 2025
77bcd44
Show CandidatesView when input method changes by keyboard shortcut
rocka Nov 7, 2025
4a955a1
Clear callbacks on CandidateViewHolder once recycled
rocka Nov 8, 2025
23c4d31
Make a new JNI method focusOutIn
rocka Nov 8, 2025
2ad65bd
Restore InputView state when switching between virtual/physical keyboard
rocka Nov 8, 2025
a8edb17
Fix restoring InputView state
rocka Nov 8, 2025
f7d8e44
Added a file with a Russian translation and fixed a typo in the plugi…
gostsdmitry Nov 9, 2025
85196e6
Force show CandidatesView only when input method switched by user
rocka Nov 9, 2025
2f71e4b
Merge branch 'dev' into pull_2025.11.10
expoli Nov 10, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions .github/workflows/fdroid.yml
Original file line number Diff line number Diff line change
Expand Up @@ -32,12 +32,12 @@ jobs:
fail-fast: false
steps:
- name: Fetch fdroiddata
uses: actions/checkout@v4
uses: actions/checkout@v5
with:
repository: f-droid/fdroiddata

- name: Fetch fdroidserver
uses: actions/checkout@v4
uses: actions/checkout@v5
with:
repository: f-droid/fdroidserver
path: fdroidserver
Expand Down Expand Up @@ -117,7 +117,7 @@ jobs:
$fdroid build --verbose --test --scan-binary --on-server --no-tarball $build

- name: Upload Artifact
uses: actions/upload-artifact@v4
uses: actions/upload-artifact@v5
if: ${{ success() || failure() }}
with:
name: fdroid-${{ matrix.abi }}
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/nix.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ jobs:
runs-on: ubuntu-24.04
steps:
- name: Fetch source code
uses: actions/checkout@v4
uses: actions/checkout@v5
with:
fetch-depth: 0
submodules: recursive
Expand Down
6 changes: 3 additions & 3 deletions .github/workflows/publish.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ jobs:
runs-on: ubuntu-24.04
steps:
- name: Fetch source code
uses: actions/checkout@v4
uses: actions/checkout@v5
with:
fetch-depth: 0
submodules: recursive
Expand All @@ -24,7 +24,7 @@ jobs:
sudo apt install extra-cmake-modules gettext

- name: Setup Java
uses: actions/setup-java@v4
uses: actions/setup-java@v5
with:
distribution: "temurin"
java-version: "17"
Expand All @@ -35,7 +35,7 @@ jobs:
packages: cmake;3.31.6

- name: Setup Gradle
uses: gradle/actions/setup-gradle@v4
uses: gradle/actions/setup-gradle@v5

- name: Publish build convention and libs
env:
Expand Down
10 changes: 5 additions & 5 deletions .github/workflows/pull_request.yml
Original file line number Diff line number Diff line change
Expand Up @@ -21,13 +21,13 @@ jobs:
- windows-2022
steps:
- name: Fetch source code
uses: actions/checkout@v4
uses: actions/checkout@v5
with:
fetch-depth: 0
submodules: recursive

- name: Setup Java
uses: actions/setup-java@v4
uses: actions/setup-java@v5
with:
distribution: "temurin"
java-version: "17"
Expand Down Expand Up @@ -58,15 +58,15 @@ jobs:
Add-Content $env:GITHUB_PATH "C:/msys64/ucrt64/bin"

- name: Setup Gradle
uses: gradle/actions/setup-gradle@v4
uses: gradle/actions/setup-gradle@v5

- name: Build Release APK
run: |
./gradlew :app:assembleRelease
./gradlew :assembleReleasePlugins

- name: Upload app
uses: actions/upload-artifact@v4
uses: actions/upload-artifact@v5
with:
name: app-${{ matrix.os }}
path: app/build/outputs/apk/release/
Expand All @@ -84,7 +84,7 @@ jobs:
done

- name: Upload plugins
uses: actions/upload-artifact@v4
uses: actions/upload-artifact@v5
with:
name: plugins-${{ matrix.os }}
path: plugins-to-upload
30 changes: 22 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,17 +4,30 @@

## Download

### Latest CI builds

Jenkins: [![build status](https://img.shields.io/jenkins/build.svg?jobUrl=https://jenkins.fcitx-im.org/job/android/job/fcitx5-android/)](https://jenkins.fcitx-im.org/job/android/job/fcitx5-android/)

### Tagged releases

GitHub: [![release version](https://img.shields.io/github/v/release/fcitx5-android/fcitx5-android)](https://github.com/fcitx5-android/fcitx5-android/releases)

[<img src="https://github.com/rubenpgrady/get-it-on-github/raw/refs/heads/main/get-it-on-github.png" alt="Git it on GitHub" width="207" height="80">](https://github.com/fcitx5-android/fcitx5-android/releases/latest)
[<img src="https://fdroid.gitlab.io/artwork/badge/get-it-on.png" alt="Get it on F-Droid" width="207" height="80">](https://f-droid.org/packages/org.fcitx.fcitx5.android)
[<img alt="Get it on Google Play" src="https://play.google.com/intl/en_us/badges/static/images/badges/en_badge_web_generic.png" width="207" height="80">](https://play.google.com/store/apps/details?id=org.fcitx.fcitx5.android)

You can also download the **latest CI build** on our Jeninks server: [![build status](https://img.shields.io/jenkins/build.svg?jobUrl=https://jenkins.fcitx-im.org/job/android/job/fcitx5-android/)](https://jenkins.fcitx-im.org/job/android/job/fcitx5-android/)

> [!NOTE]
> APKs downloaded from GitHub Release/F-Droid/Jenkins have the same signature, which means they're compatible when upgrading, but Google Play's do not.
> <details>
> <summary>(click here for detailed signature info)</summary>
> <ul>
> <li>Package Name: <code>org.fcitx.fcitx5.android</code></li>
> <li>Certificate SHA-256 fingerprint:</li>
> <ul>
> <li>GitHub Release/Jenkins/F-Droid</li>
> <code>E4:DB:1E:9E:DF:F1:36:29:D0:7D:E4:BB:F8:16:5F:E9:BD:85:57:AB:55:09:26:72:DA:8E:40:DB:E4:84:EC:D7</code>
> <li>Google Play</li>
> <code>06:53:6F:F6:E8:76:C0:14:E1:4B:44:6F:61:FA:2B:80:9E:06:67:39:A1:D1:17:0D:0A:7A:89:88:4C:48:00:33</code>
> </ul>
> </ul>
> </details>

In case you want Fcitx5 on other platforms: [macOS](https://github.com/fcitx-contrib/fcitx5-macos), [iOS](https://github.com/fcitx-contrib/fcitx5-ios), [HarmonyOS](https://github.com/fcitx-contrib/fcitx5-harmony), [ChromeOS](https://github.com/fcitx-contrib/fcitx5-chrome), [Windows](https://github.com/fcitx-contrib/fcitx5-windows); or [try Fcitx5 in the browser](https://fcitx-contrib.github.io/online/index.html)

## Project status

### Supported Languages
Expand All @@ -41,6 +54,7 @@ GitHub: [![release version](https://img.shields.io/github/v/release/fcitx5-andro
- Long press popup keyboard for convenient symbol input
- Symbol and Emoji picker
- Plugin System for loading addons from other installed apk
- Floating candidates panel when using physical keyboard

### Planned Features

Expand Down
2 changes: 1 addition & 1 deletion app/licenses/libraries/boost.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"uniqueId": "boostorg/boost",
"artifactVersion": "1.86.0",
"artifactVersion": "1.87.0",
"description": "Free peer-reviewed portable C++ source libraries",
"name": "boostorg/boost",
"website": "https://www.boost.org/",
Expand Down
2 changes: 1 addition & 1 deletion app/licenses/libraries/fcitx5-chinese-addons.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"uniqueId": "fcitx/fcitx5-chinese-addons",
"artifactVersion": "5.1.7",
"artifactVersion": "5.1.10",
"description": "Chinese related addon for fcitx5",
"name": "fcitx/fcitx5-chinese-addons",
"website": "https://github.com/fcitx/fcitx5-chinese-addons",
Expand Down
2 changes: 1 addition & 1 deletion app/licenses/libraries/fcitx5.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"uniqueId": "fcitx/fcitx5",
"artifactVersion": "5.1.12",
"artifactVersion": "5.1.16",
"description": "Next generation of fcitx",
"name": "fcitx/fcitx5",
"website": "https://github.com/fcitx/fcitx5",
Expand Down
11 changes: 0 additions & 11 deletions app/licenses/libraries/fmt.json

This file was deleted.

2 changes: 1 addition & 1 deletion app/licenses/libraries/libime.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"uniqueId": "fcitx/libime",
"artifactVersion": "1.1.10",
"artifactVersion": "1.1.12",
"description": "library to support generic input method implementation",
"name": "fcitx/libime",
"website": "https://github.com/fcitx/libime",
Expand Down
2 changes: 1 addition & 1 deletion app/licenses/libraries/libuv.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"uniqueId": "libuv/libuv",
"artifactVersion": "1.49.2",
"artifactVersion": "1.50.0",
"description": "Cross-platform asynchronous I/O",
"name": "libuv/libuv",
"website": "https://libuv.org/",
Expand Down
12 changes: 12 additions & 0 deletions app/licenses/libraries/zstd.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
{
"uniqueId": "facebook/zstd",
"artifactVersion": "1.5.7",
"description": "Zstandard - Fast real-time compression algorithm",
"name": "facebook/zstd",
"website": "https://www.zstd.net",
"tag": "native",
"licenses": [
"BSD-3-Clause",
"GPL-2.0-only"
]
}
62 changes: 51 additions & 11 deletions app/src/main/cpp/androidfrontend/androidfrontend.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,12 @@
#include <fcitx/addonfactory.h>
#include <fcitx/addonmanager.h>
#include <fcitx/candidatelist.h>
#include <fcitx/focusgroup.h>
#include <fcitx/inputcontextmanager.h>
#include <fcitx/inputmethodengine.h>
#include <fcitx/focusgroup.h>
#include <fcitx/inputmethodmanager.h>
#include <fcitx/inputpanel.h>
#include <fcitx/statusarea.h>
#include <fcitx-utils/event.h>

#include "androidfrontend.h"
Expand Down Expand Up @@ -286,29 +288,40 @@ AndroidFrontend::AndroidFrontend(Instance *instance)
EventType::InputContextInputMethodActivated,
EventWatcherPhase::Default,
[this](Event &event) {
FCITX_UNUSED(event);
imChangeCallback();
auto &e = static_cast<InputMethodActivatedEvent &>(event);
if (e.inputContext() != activeIC_) return;
imChangeCallback(makeInputMethodStatus(activeIC_));
}
));
eventHandlers_.emplace_back(instance_->watchEvent(
EventType::InputContextSwitchInputMethod,
EventWatcherPhase::Default,
[this](Event &event) {
auto &e = static_cast<InputContextSwitchInputMethodEvent &>(event);
if (e.inputContext() != activeIC_) return;
const auto &reason = static_cast<std::underlying_type<InputMethodSwitchedReason>::type>(e.reason());
const std::string &oldIM = e.oldInputMethod();
switchInputMethodCallback(reason, oldIM);
}
));
eventHandlers_.emplace_back(instance_->watchEvent(
EventType::InputContextFlushUI,
EventWatcherPhase::Default,
[this](Event &event) {
auto &e = static_cast<InputContextFlushUIEvent &>(event);
if (e.inputContext() != activeIC_) return;
switch (e.component()) {
case UserInterfaceComponent::InputPanel: {
if (activeIC_) {
activeIC_->updateInputPanel();
if (pagingMode_ == 0) {
activeIC_->updateCandidatesBulk();
} else {
activeIC_->updateCandidatesPaged();
}
activeIC_->updateInputPanel();
if (pagingMode_ == 0) {
activeIC_->updateCandidatesBulk();
} else {
activeIC_->updateCandidatesPaged();
}
break;
}
case UserInterfaceComponent::StatusArea: {
statusAreaUpdateCallback();
statusAreaUpdateCallback(makeStatusAreaActions(activeIC_), makeInputMethodStatus(activeIC_));
break;
}
}
Expand Down Expand Up @@ -442,6 +455,11 @@ void AndroidFrontend::showToast(const std::string &s) {

void AndroidFrontend::setCandidatePagingMode(const int mode) {
pagingMode_ = mode;
if (mode == 0) {
activeIC_->updateCandidatesBulk();
} else {
activeIC_->updateCandidatesPaged();
}
}

void AndroidFrontend::updatePagedCandidate(const PagedCandidateEntity &paged) {
Expand Down Expand Up @@ -489,6 +507,28 @@ void AndroidFrontend::setPagedCandidateCallback(const PagedCandidateCallback &ca
pagedCandidateCallback = callback;
}

void AndroidFrontend::setSwitchInputMethodCallback(const SwitchInputMethodCallback &callback) {
switchInputMethodCallback = callback;
}

InputMethodStatus AndroidFrontend::makeInputMethodStatus(InputContext *ic) {
auto *entry = instance_->inputMethodEntry(ic);
auto *engine = instance_->inputMethodEngine(ic);
return {entry, engine, ic};
}

std::vector<ActionEntity> AndroidFrontend::makeStatusAreaActions(fcitx::InputContext *ic) {
auto actions = std::vector<ActionEntity>();
for (auto group: {fcitx::StatusGroup::BeforeInputMethod,
fcitx::StatusGroup::InputMethod,
fcitx::StatusGroup::AfterInputMethod}) {
for (auto act: ic->statusArea().actions(group)) {
actions.emplace_back(act, ic);
}
}
return actions;
}

class AndroidFrontendFactory : public AddonFactory {
public:
AddonInstance *create(AddonManager *manager) override {
Expand Down
32 changes: 19 additions & 13 deletions app/src/main/cpp/androidfrontend/androidfrontend.h
Original file line number Diff line number Diff line change
Expand Up @@ -23,30 +23,30 @@ class AndroidFrontend : public AddonInstance {

Instance *instance() { return instance_; }

void updateCandidateList(const std::vector<std::string> &candidates, const int size);
void commitString(const std::string &str, const int cursor);
void updateCandidateList(const std::vector<std::string> &candidates, int size);
void commitString(const std::string &str, int cursor);
void updateClientPreedit(const Text &clientPreedit);
void updateInputPanel(const Text &preedit, const Text &auxUp, const Text &auxDown);
void releaseInputContext(const int uid);
void releaseInputContext(int uid);
void updatePagedCandidate(const PagedCandidateEntity &paged);

void keyEvent(const Key &key, bool isRelease, const int timestamp);
void keyEvent(const Key &key, bool isRelease, int timestamp);
void forwardKey(const Key &key, bool isRelease);
bool selectCandidate(int idx);
bool isInputPanelEmpty();
void resetInputContext();
void repositionCursor(int idx);
void focusInputContext(bool focus);
void activateInputContext(const int uid, const std::string &pkgName);
void deactivateInputContext(const int uid);
void activateInputContext(int uid, const std::string &pkgName);
void deactivateInputContext(int uid);
[[nodiscard]] InputContext *activeInputContext() const;
void setCapabilityFlags(uint64_t flag);
std::vector<std::string> getCandidates(const int offset, const int limit);
std::vector<CandidateAction> getCandidateActions(const int idx);
void triggerCandidateAction(const int idx, const int actionIdx);
void deleteSurrounding(const int before, const int after);
std::vector<std::string> getCandidates(int offset, int limit);
std::vector<CandidateAction> getCandidateActions(int idx);
void triggerCandidateAction(int idx, int actionIdx);
void deleteSurrounding(int before, int after);
void showToast(const std::string &s);
void setCandidatePagingMode(const int mode);
void setCandidatePagingMode(int mode);
void offsetCandidatePage(int delta);
void setCandidateListCallback(const CandidateListCallback &callback);
void setCommitStringCallback(const CommitStringCallback &callback);
Expand All @@ -58,6 +58,7 @@ class AndroidFrontend : public AddonInstance {
void setDeleteSurroundingCallback(const DeleteSurroundingCallback &callback);
void setToastCallback(const ToastCallback &callback);
void setPagedCandidateCallback(const PagedCandidateCallback &callback);
void setSwitchInputMethodCallback(const SwitchInputMethodCallback &callback);

private:
FCITX_ADDON_EXPORT_FUNCTION(AndroidFrontend, keyEvent);
Expand Down Expand Up @@ -86,6 +87,7 @@ class AndroidFrontend : public AddonInstance {
FCITX_ADDON_EXPORT_FUNCTION(AndroidFrontend, setDeleteSurroundingCallback);
FCITX_ADDON_EXPORT_FUNCTION(AndroidFrontend, setToastCallback);
FCITX_ADDON_EXPORT_FUNCTION(AndroidFrontend, setPagedCandidateCallback);
FCITX_ADDON_EXPORT_FUNCTION(AndroidFrontend, setSwitchInputMethodCallback);

Instance *instance_;
FocusGroup focusGroup_;
Expand All @@ -99,11 +101,15 @@ class AndroidFrontend : public AddonInstance {
ClientPreeditCallback preeditCallback = [](const Text &) {};
InputPanelCallback inputPanelCallback = [](const fcitx::Text &, const fcitx::Text &, const Text &) {};
KeyEventCallback keyEventCallback = [](const int, const uint32_t, const uint32_t, const bool, const int) {};
InputMethodChangeCallback imChangeCallback = [] {};
StatusAreaUpdateCallback statusAreaUpdateCallback = [] {};
InputMethodChangeCallback imChangeCallback = [](const InputMethodStatus &) {};
StatusAreaUpdateCallback statusAreaUpdateCallback = [](const std::vector<ActionEntity> &, const InputMethodStatus &) {};
DeleteSurroundingCallback deleteSurroundingCallback = [](const int, const int) {};
ToastCallback toastCallback = [](const std::string &) {};
PagedCandidateCallback pagedCandidateCallback = [](const PagedCandidateEntity &) {};
SwitchInputMethodCallback switchInputMethodCallback = [](const int, const std::string &) {};

InputMethodStatus makeInputMethodStatus(InputContext* ic);
std::vector<ActionEntity> makeStatusAreaActions(InputContext* ic);
};
} // namespace fcitx

Expand Down
Loading
Loading