Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
78 changes: 18 additions & 60 deletions .github/workflows/android-build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,10 @@ on:
branches: [ "main", "develop" ]

jobs:
test:
verify:
name: Test & Lint
runs-on: ubuntu-latest

steps:
- name: Checkout code
uses: actions/checkout@v4
Expand Down Expand Up @@ -39,7 +40,10 @@ jobs:
- name: Run unit tests
run: ./gradlew testDebugUnitTest

- name: Upload Test Reports (xml+html)
- name: Run lint
run: ./gradlew lintDebug

- name: Upload test reports
if: always()
uses: actions/upload-artifact@v4
with:
Expand All @@ -48,47 +52,6 @@ jobs:
**/build/test-results/
**/build/reports/tests/

- name: Upload test results
uses: actions/upload-artifact@v4
if: always()
with:
name: test-results
path: |
**/build/test-results/
**/build/reports/tests/

lint:
runs-on: ubuntu-latest

steps:
- name: Checkout code
uses: actions/checkout@v4

- name: Set up JDK 17
uses: actions/setup-java@v4
with:
java-version: '17'
distribution: 'temurin'

- name: Setup Gradle
uses: gradle/gradle-build-action@v3

- name: Grant execute permission for gradlew
run: chmod +x gradlew

- name: Cache Gradle packages
uses: actions/cache@v3
with:
path: |
~/.gradle/caches
~/.gradle/wrapper
key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle*', '**/gradle-wrapper.properties') }}
restore-keys: |
${{ runner.os }}-gradle-

- name: Run lint
run: ./gradlew lintDebug

- name: Upload lint results
uses: actions/upload-artifact@v4
if: always()
Expand All @@ -97,9 +60,13 @@ jobs:
path: '**/build/reports/lint-results-*.html'

build:
name: Build ${{ matrix.variant }}
runs-on: ubuntu-latest
needs: [test, lint]

needs: verify
strategy:
matrix:
variant: [Debug, Release]

steps:
- name: Checkout code
uses: actions/checkout@v4
Expand All @@ -126,20 +93,11 @@ jobs:
restore-keys: |
${{ runner.os }}-gradle-

- name: Build debug APK
run: ./gradlew assembleDebug

- name: Build release APK
run: ./gradlew assembleRelease

- name: Upload debug APK
uses: actions/upload-artifact@v4
with:
name: debug-apk
path: app/build/outputs/apk/debug/*.apk
- name: Build ${{ matrix.variant }} APK
run: ./gradlew assemble${{ matrix.variant }}

- name: Upload release APK
- name: Upload ${{ matrix.variant }} APK
uses: actions/upload-artifact@v4
with:
name: release-apk
path: app/build/outputs/apk/release/*.apk
name: ${{ matrix.variant }}-apk
path: app/build/outputs/apk/**/*.apk
36 changes: 21 additions & 15 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ jobs:
with:
distribution: temurin
java-version: 17

- name: Setup Gradle
uses: gradle/gradle-build-action@v2
with:
Expand All @@ -38,30 +38,27 @@ jobs:
- name: Grant execute permission for Gradlew
run: chmod +x ./gradlew

- name: Build APK
- name: Build Release APK (with Tor)
run: ./gradlew assembleRelease --no-daemon --stacktrace

- name: List APK files
run: |
echo "APK files built:"
find app/build/outputs/apk/release -name "*.apk" -type f
find app/build/outputs/apk -name "*.apk" -type f

- name: Rename APK
run: |
mv app/build/outputs/apk/release/app-release-unsigned.apk app/build/outputs/apk/release/bitchat.apk
mv app/build/outputs/apk/release/app-release-unsigned.apk app/build/outputs/apk/release/bitchat-android.apk

- name: DEBUG
run: |
set -x

pwd

ls -all

cd app/build/outputs/
ls -all
tree
tree || ls -R

# Optional: Sign APK (requires secrets)
# - name: Sign APK
# uses: r0adkll/sign-android-release@v1
Expand All @@ -75,7 +72,7 @@ jobs:
- name: Upload APK as artifact
uses: actions/upload-artifact@v4
with:
name: bitchat-release-apk-${{ github.ref_name }}
name: bitchat-android-apk-${{ github.ref_name }}
path: app/build/outputs/apk/release/*.apk
retention-days: 30
if-no-files-found: error
Expand All @@ -88,13 +85,22 @@ jobs:
- name: Download APK artifact
uses: actions/download-artifact@v4
with:
name: bitchat-release-apk-${{ github.ref_name }}
path: .
name: bitchat-android-apk-${{ github.ref_name }}
path: release

- name: Create GitHub Release
uses: softprops/action-gh-release@v2
with:
files: bitchat.apk
files: |
release/bitchat-android.apk
name: Release ${{ github.ref_name }}
body: |
## bitchat Android Release

**bitchat-android.apk** (~15MB)
- Secure P2P messaging over Bluetooth mesh and Nostr
- Built-in Tor support (custom Arti build with 16KB page size)
- Compatible with Google Play requirements (Nov 2025+)
- Cross-platform compatible with iOS version
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
8 changes: 7 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,9 @@ dependency-reduced-pom.xml
# Other
*.log
.cxx/
*build/
# Gradle/Android build directories (but not tools/arti-build/)
**/build/
!tools/arti-build/
out/
gen/
*~
Expand All @@ -57,3 +59,7 @@ google-services.json
# Keystore files
*.jks
*.keystore

# Arti build artifacts (cloned repo and Rust build cache)
tools/arti-build/.arti-source/
tools/arti-build/target/
18 changes: 16 additions & 2 deletions app/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -30,13 +30,24 @@ android {
}

buildTypes {
debug {
ndk {
// Include x86_64 for emulator support during development
abiFilters += listOf("arm64-v8a", "x86_64")
}
}
release {
isMinifyEnabled = true
isShrinkResources = true
proguardFiles(
getDefaultProguardFile("proguard-android-optimize.txt"),
"proguard-rules.pro"
)
ndk {
// ARM64-only to minimize APK size (~5.8MB savings)
// Excludes x86_64 as emulator not needed for production builds
abiFilters += listOf("arm64-v8a")
}
}
}
compileOptions {
Expand Down Expand Up @@ -95,8 +106,11 @@ dependencies {
// WebSocket
implementation(libs.okhttp)

// Arti (Tor in Rust) Android bridge - use published AAR with native libs
implementation("info.guardianproject:arti-mobile-ex:1.2.3")
// Arti (Tor in Rust) Android bridge - custom build from latest source
// Built with rustls, 16KB page size support, and onio//un service client
// Native libraries are in src/tor/jniLibs/ (extracted from arti-custom.aar)
// Only included in tor flavor to reduce APK size for standard builds
// Note: AAR is kept in libs/ for reference, but libraries loaded from jniLibs/

// Google Play Services Location
implementation(libs.gms.location)
Expand Down
11 changes: 7 additions & 4 deletions app/proguard-rules.pro
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,12 @@
-keep class com.bitchat.android.nostr.** { *; }
-keep class com.bitchat.android.identity.** { *; }

# Arti (Tor) ProGuard rules
# Keep Tor implementation (always included)
-keep class com.bitchat.android.net.RealTorProvider { *; }

# Arti (Custom Tor implementation in Rust) ProGuard rules
-keep class info.guardianproject.arti.** { *; }
-keep class org.torproject.jni.** { *; }
-keepnames class org.torproject.jni.**
-keep class org.torproject.arti.** { *; }
-keepnames class org.torproject.arti.**
-dontwarn info.guardianproject.arti.**
-dontwarn org.torproject.jni.**
-dontwarn org.torproject.arti.**
11 changes: 7 additions & 4 deletions app/src/main/java/com/bitchat/android/BitchatApplication.kt
Original file line number Diff line number Diff line change
Expand Up @@ -3,18 +3,21 @@ package com.bitchat.android
import android.app.Application
import com.bitchat.android.nostr.RelayDirectory
import com.bitchat.android.ui.theme.ThemePreferenceManager
import com.bitchat.android.net.TorManager
import com.bitchat.android.net.ArtiTorManager

/**
* Main application class for bitchat Android
*/
class BitchatApplication : Application() {

override fun onCreate() {
super.onCreate()

// Initialize Tor first so any early network goes over Tor
try { TorManager.init(this) } catch (_: Exception) { }
try {
val torProvider = ArtiTorManager.getInstance()
torProvider.init(this)
} catch (_: Exception){}

// Initialize relay directory (loads assets/nostr_relays.csv)
RelayDirectory.initialize(this)
Expand Down
Loading
Loading