Mezon Android

May 1, 2026 · View on GitHub

Native Android client for the Mezon messaging platform. The app is built in Kotlin with a Telegram-style UI layer: custom Canvas-based cells, a custom navigation stack, and Room for offline-first persistence. Architecture centers on controllers, NotificationCenter, and custom views (no Jetpack Compose for primary UI).


Table of contents


Overview

AreaApproach
UICustom View / ViewGroup cells (BaseCell), StaticLayout, shared paints from ThemeColors — optimized for list scrolling
State@Singleton controllers hold in-memory caches (ArrayList, LongSparseArray); UI updates via NotificationCenter (not StateFlow for screens)
PersistenceDual-write: update memory first, then async Room @Upsert on I/O dispatcher
RealtimeOkHttp WebSocket with protobuf Envelope; events fanned out in SocketEventDispatcher
Remote APIKtor + OkHttp, protobuf request/response bodies

Requirements

ToolNotes
Android StudioCurrent stable channel (e.g. Ladybug or newer)
JDK17+ (bundled with Android Studio is fine)
Android SDKcompileSdk 35, minSdk 24 (see app/build.gradle.kts)
Kotlin1.9.x (see gradle/libs.versions.toml)

Getting started

1. Clone this repository (including submodules)

Protobuf definitions live in mezon-protocol, included as a Git submodule at the repo root.

git clone --recurse-submodules <mezon-android-repository-url> ~/AndroidStudioProjects/mezon

If you already cloned without submodules:

cd ~/AndroidStudioProjects/mezon
git submodule update --init --recursive

The submodule tracks github.com/mezonai/mezon-protocol (pinned commit in .gitmodules / superproject).

2. Open and sync

Open the mezon/ directory in Android Studio and let Gradle sync complete.


Protocol buffer setup

  • Generated sources live in the :core-proto module.
  • .proto files are read from the mezon-protocol submodule (Gradle uses the repo root so imports like api/api.proto stay valid).

Firebase configuration

app/google-services.json is not committed. Obtain it from the Firebase project (e.g. mezon-772fa) or from your team, then place it at:

mezon/app/google-services.json

Copy mezon/mezon.secrets.properties.example to mezon/mezon.secrets.properties and fill in values (the real file is not committed; obtain from your team or internal secret store). Gradle fails early if this file is missing.


Build and test

All commands are run from the mezon/ directory:

cd mezon

./gradlew assembleDebug
./gradlew installDebug
./gradlew assembleRelease
./gradlew test

For a full debug build including proto generation:

./gradlew assembleDebug

Updating mezon-protocol

Point the submodule at a newer commit, then regenerate:

cd /path/to/mezon/mezon-protocol
git fetch origin
git checkout main   # or a release tag
git pull origin main

cd /path/to/mezon
git add mezon-protocol
git commit -m "Bump mezon-protocol submodule"

./gradlew :core-proto:generateDebugProto
./gradlew app:compileDebugKotlin

To only sync your working copy to the commit recorded by the parent repo (no bump):

cd /path/to/mezon
git submodule update --init --recursive

Architecture

Data flow (high level):

WebSocket ──► SocketEventDispatcher ──► Controller (cache + async Room)
REST     ──► Controller                ──► NotificationCenter.postOnMainThread
Room     ──► Controller init / cold load ──► same caches + UI events

NotificationCenter ──► BaseFragment.observe() ──► adapters / cell.invalidate / partial row updates

Layers

LayerResponsibility
Controller@Singleton services: synchronized in-memory models, REST and socket side effects, dual-write to Room, post NC events from init / API / socket
NotificationCenterMain-thread event bus (integer event IDs); fragments register with BaseFragment.observe()
BaseFragmentCustom lifecycle (managed by ActionBarLayout, not AndroidX FragmentManager for the main stack)
CellsBaseCell subclasses: onDraw(Canvas), update(mask), shared theme paints
RoomWAL, @Upsert, bounded list queries (e.g. message cap per channel)
SocketEventDispatcherDemultiplexes Envelope into typed SharedFlows for controllers

Single-activity: MainActivity. Screen stack and transitions use ActionBarLayout (custom ArrayList of BaseFragment, animated transitions, swipe-back). This is not the AndroidX Navigation Component graph.

Example NotificationCenter consumers

Event (examples)Typical publisherTypical subscriber
dialogsNeedReloadDialogsController / messages pipelineMessagesFragment
messagesDidLoad / new/update/deleteChatControllerChatFragment
clansDidLoad / channelsClansController / ChannelControllerClansFragment
themeChanged / languageChangedThemeManager / LocaleManagerFragments via rebuild or observers
connectionStateChangedConnectionControllerShell / home
sessionExpiredAuthRepositoryMainActivity

The canonical list and IDs live in NotificationCenter and related controllers.


Repository layout

app/src/main/java/com/mezon/mobile/
├── MainActivity.kt
├── MezonApplication.kt
├── auth/                 # AuthRepository, LoginFragment
├── core/                 # BaseFragment, ActionBarLayout, NotificationCenter, ThemeColors, BaseCell, …
├── data/db/              # MezonDatabase, DAOs, entities
├── di/                   # Hilt modules, dispatchers
├── home/                 # Controllers, MainTabsActivity, chat / messages / clans / profile / notifications
├── network/              # MezonApi, MezonSocket, SocketEventDispatcher, ApiCacheTracker
├── notification/        # FCM, local notifications, active channel
├── session/              # SessionManager, theme, locale
├── ui/cells/             # Reusable custom views (action bar, settings rows, …)
└── util/                 # ContentParser, image helpers, …

A fuller map of types may exist in the parent workspace (e.g. CLAUDE.md at the monorepo root, if present).


Technology stack

AreaTechnology
LanguageKotlin 1.9.x
BuildGradle with Kotlin DSL, KSP (Room, Hilt) — versions in gradle/libs.versions.toml
DIHilt
HTTPKtor + OkHttp
WebSocketOkHttp, binary protobuf
DBRoom, WAL, @Upsert
ImagesCustom MezonImageLoader (OkHttp, memory + disk cache) + ImageReceiver / AvatarDrawable
SessionDataStore Preferences
PushFirebase Cloud Messaging (see BOM in version catalog)
MessagesProtobuf Lite (:core-proto)
ConcurrencyKotlin coroutines, @ApplicationScope / @IoDispatcher

Performance notes

  • Lists: DiffUtil where appropriate; updateVisibleRows(mask)-style partial updates to avoid full adapter churn; scroll-state guards where implemented.
  • Controllers: in-memory update first, Room write off the main thread.
  • Cold start: load bounded data from Room in controllers, then refresh from network.
  • API: ApiCacheTracker (TTL) to reduce duplicate REST work.
  • Canvas: avoid per-frame allocations in onDraw; reuse layouts and paints per cell instance as documented in project guidelines.

Protocol and networking

ArtifactRole
api/api.protoREST messages (com.mezon.mezon.api)
rtapi/realtime.protoWebSocket Envelope and realtime payloads (com.mezon.mezon.rtapi)
  • WebSocket (illustrative): wss://<ws_url>/ws?token=<token>&status=true&platform=1&lang=en&format=protobuf
  • REST: Content-Type: application/proto with bearer token (auth flows may use JSON + Basic as defined by the API).

Internal engineering documentation: keep setup steps in sync with gradle/libs.versions.toml and app/build.gradle.kts when versions change.