Add release signing, fix release build, update README
- Configure release signing via environment variables (android-signing-env) - Enable BuildConfig generation and guard HttpLoggingInterceptor to debug only - Move okhttp.logging to implementation so release build compiles - Update README: add E2EE, verification, push notifications, download link Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
5791742c95
commit
d4b37437a3
3 changed files with 53 additions and 19 deletions
43
README.md
43
README.md
|
|
@ -10,6 +10,13 @@ A minimal Android Matrix chat client with a terminal aesthetic — dark backgrou
|
|||
- Text-only chat (no images, no avatars)
|
||||
- Real-time sync via Matrix `/sync` long-polling
|
||||
- Persistent session (survives app restart)
|
||||
- End-to-end encryption (E2EE) via libolm / Megolm
|
||||
- SAS device verification
|
||||
- Push notifications (foreground via sync loop; background via FCM + Cloudflare Worker)
|
||||
|
||||
## Download
|
||||
|
||||
Pre-built APKs are available on the [releases page](https://git.tadix.dev/ltadeu6/matrix-android/releases).
|
||||
|
||||
## Screenshots / UI description
|
||||
|
||||
|
|
@ -103,27 +110,37 @@ Enter your full Matrix username (`@user:homeserver.tld`) and password directly.
|
|||
|
||||
```
|
||||
app/src/main/java/com/ltadeu6/matrix/
|
||||
├── MainActivity.kt — single Activity; handles SSO deep link
|
||||
├── MainActivity.kt — single Activity; handles SSO deep link
|
||||
├── navigation/
|
||||
│ └── AppNavigation.kt — Compose NavHost (Login → Rooms → Chat)
|
||||
│ └── AppNavigation.kt — Compose NavHost (Login → Rooms → Chat)
|
||||
├── auth/
|
||||
│ ├── LoginScreen.kt — login UI
|
||||
│ └── LoginViewModel.kt — SSO URL builder, login calls
|
||||
│ ├── LoginScreen.kt — login UI
|
||||
│ └── LoginViewModel.kt — SSO URL builder, login calls
|
||||
├── rooms/
|
||||
│ └── RoomListScreen.kt — joined rooms list
|
||||
│ └── RoomListScreen.kt — joined rooms list
|
||||
├── chat/
|
||||
│ ├── ChatScreen.kt — message list + input bar
|
||||
│ └── ChatViewModel.kt — sends messages, maps sync state
|
||||
│ ├── ChatScreen.kt — message list + input bar
|
||||
│ └── ChatViewModel.kt — sends messages, maps sync state
|
||||
├── matrix/
|
||||
│ ├── MatrixApi.kt — Retrofit interface (Matrix REST v3)
|
||||
│ ├── MatrixModels.kt — serialization data classes + local models
|
||||
│ └── MatrixSession.kt — singleton: auth, sync loop, state flows
|
||||
│ ├── MatrixApi.kt — Retrofit interface (Matrix REST v3)
|
||||
│ ├── MatrixModels.kt — serialization data classes + local models
|
||||
│ ├── MatrixSession.kt — singleton: auth, sync loop, state flows
|
||||
│ ├── CryptoService.kt — E2EE: Olm/Megolm, OTK management
|
||||
│ ├── VerificationService.kt — SAS device verification
|
||||
│ ├── NotificationHelper.kt — notification channel + posting
|
||||
│ └── MatrixFirebaseMessagingService.kt — FCM push (app killed)
|
||||
├── verification/
|
||||
│ └── VerificationScreen.kt — verification UI
|
||||
└── ui/theme/
|
||||
└── Theme.kt — terminal color scheme (green on black)
|
||||
└── Theme.kt — terminal color scheme (green on black)
|
||||
```
|
||||
|
||||
**State flow**: `MatrixSession` owns a `syncState: StateFlow<SyncState>` that all screens observe. The background sync loop continuously updates it via `/sync` long-polling.
|
||||
|
||||
## Push notifications
|
||||
|
||||
When the app is in the foreground or background, notifications are fired directly from the sync loop. When the app is killed, push is delivered via FCM through a self-hosted Cloudflare Worker (`gateway/`) that acts as a Matrix push gateway.
|
||||
|
||||
## Dependencies
|
||||
|
||||
| Library | Purpose |
|
||||
|
|
@ -135,10 +152,10 @@ app/src/main/java/com/ltadeu6/matrix/
|
|||
| retrofit2-kotlinx-serialization-converter | Retrofit ↔ kotlinx bridge |
|
||||
| kotlinx.coroutines | Async / sync loop |
|
||||
| androidx.browser (Custom Tabs) | SSO browser login |
|
||||
| libolm (native) | E2EE: Olm/Megolm encryption |
|
||||
| Firebase Messaging | FCM push notifications |
|
||||
|
||||
## Known limitations
|
||||
|
||||
- No E2E encryption (plain-text transport only; suitable for unencrypted rooms)
|
||||
- No push notifications
|
||||
- No media/file messages (text only)
|
||||
- Room list is not paginated (loads whatever `/sync` returns on first sync)
|
||||
|
|
|
|||
|
|
@ -18,6 +18,16 @@ android {
|
|||
versionName = "0.1.0"
|
||||
}
|
||||
|
||||
signingConfigs {
|
||||
create("release") {
|
||||
storeFile = file(System.getenv("ANDROID_KEYSTORE_PATH") ?: "")
|
||||
storeType = System.getenv("ANDROID_KEYSTORE_TYPE") ?: "PKCS12"
|
||||
keyAlias = System.getenv("ANDROID_KEY_ALIAS") ?: ""
|
||||
storePassword = System.getenv("ANDROID_KEYSTORE_PASSWORD") ?: ""
|
||||
keyPassword = System.getenv("ANDROID_KEY_PASSWORD") ?: ""
|
||||
}
|
||||
}
|
||||
|
||||
buildTypes {
|
||||
release {
|
||||
isMinifyEnabled = true
|
||||
|
|
@ -25,6 +35,7 @@ android {
|
|||
getDefaultProguardFile("proguard-android-optimize.txt"),
|
||||
"proguard-rules.pro"
|
||||
)
|
||||
signingConfig = signingConfigs.getByName("release")
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -39,6 +50,7 @@ android {
|
|||
|
||||
buildFeatures {
|
||||
compose = true
|
||||
buildConfig = true
|
||||
}
|
||||
|
||||
packaging {
|
||||
|
|
@ -68,5 +80,5 @@ dependencies {
|
|||
implementation(platform(libs.firebase.bom))
|
||||
implementation(libs.firebase.messaging)
|
||||
debugImplementation(libs.androidx.ui.tooling)
|
||||
debugImplementation(libs.okhttp.logging)
|
||||
implementation(libs.okhttp.logging)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -25,7 +25,8 @@ import android.util.Log
|
|||
import okhttp3.MediaType.Companion.toMediaType
|
||||
import okhttp3.OkHttpClient
|
||||
import okhttp3.Request
|
||||
import okhttp3.logging.HttpLoggingInterceptor
|
||||
import com.ltadeu6.matrix.BuildConfig
|
||||
import okhttp3.Interceptor
|
||||
import retrofit2.Retrofit
|
||||
import java.util.concurrent.TimeUnit
|
||||
import java.util.concurrent.atomic.AtomicInteger
|
||||
|
|
@ -1331,12 +1332,16 @@ class MatrixSession private constructor(private val appContext: Context) {
|
|||
}
|
||||
|
||||
private fun buildApi(homeserver: String) {
|
||||
val logging = HttpLoggingInterceptor().apply { level = HttpLoggingInterceptor.Level.BODY }
|
||||
val client = OkHttpClient.Builder()
|
||||
val clientBuilder = OkHttpClient.Builder()
|
||||
.connectTimeout(30, TimeUnit.SECONDS)
|
||||
.readTimeout(60, TimeUnit.SECONDS)
|
||||
.addInterceptor(logging)
|
||||
.build()
|
||||
if (BuildConfig.DEBUG) {
|
||||
val logging = okhttp3.logging.HttpLoggingInterceptor().apply {
|
||||
level = okhttp3.logging.HttpLoggingInterceptor.Level.BODY
|
||||
}
|
||||
clientBuilder.addInterceptor(logging)
|
||||
}
|
||||
val client = clientBuilder.build()
|
||||
api = Retrofit.Builder()
|
||||
.baseUrl(homeserver)
|
||||
.client(client)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue