Fix foreground service permissions and UI issues

- Add CHANGE_NETWORK_STATE for SDK 36 FGS connectedDevice type
- Add logging to TetherApiService for debugging
- Fix status bar clipping with fitsSystemWindows
- Wrap preview box in MaterialCard with scrolling
- Fix Docker build permissions in justfile
This commit is contained in:
2025-12-19 07:05:00 +01:00
parent cce160aa04
commit 056515a1f2
5 changed files with 47 additions and 21 deletions

1
.gitignore vendored
View File

@@ -14,3 +14,4 @@ local.properties
*.aab *.aab
*.jks *.jks
*.keystore *.keystore
.gradle-cache/

View File

@@ -4,6 +4,7 @@
<uses-permission android:name="android.permission.READ_PHONE_STATE" /> <uses-permission android:name="android.permission.READ_PHONE_STATE" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" /> <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.CHANGE_NETWORK_STATE" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" /> <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" /> <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" /> <uses-permission android:name="android.permission.FOREGROUND_SERVICE" />

View File

@@ -10,6 +10,7 @@ import android.content.Intent
import android.os.Binder import android.os.Binder
import android.os.Build import android.os.Build
import android.os.IBinder import android.os.IBinder
import android.util.Log
import androidx.core.app.NotificationCompat import androidx.core.app.NotificationCompat
import dev.itsh.tetherapi.MainActivity import dev.itsh.tetherapi.MainActivity
import dev.itsh.tetherapi.R import dev.itsh.tetherapi.R
@@ -44,10 +45,12 @@ class TetherApiService : Service() {
private fun startServer(port: Int) { private fun startServer(port: Int) {
if (apiServer?.isAlive == true) { if (apiServer?.isAlive == true) {
Log.d(TAG, "Server already running")
return return
} }
try { try {
Log.d(TAG, "Starting server on port $port")
apiServer = ApiServer(this, port).apply { apiServer = ApiServer(this, port).apply {
start() start()
} }
@@ -57,7 +60,9 @@ class TetherApiService : Service() {
isRunning = true isRunning = true
currentPort = port currentPort = port
Log.d(TAG, "Server started successfully")
} catch (e: Exception) { } catch (e: Exception) {
Log.e(TAG, "Failed to start server", e)
stopSelf() stopSelf()
} }
} }
@@ -126,6 +131,7 @@ class TetherApiService : Service() {
} }
companion object { companion object {
private const val TAG = "TetherApiService"
const val ACTION_START = "dev.itsh.tetherapi.action.START" const val ACTION_START = "dev.itsh.tetherapi.action.START"
const val ACTION_STOP = "dev.itsh.tetherapi.action.STOP" const val ACTION_STOP = "dev.itsh.tetherapi.action.STOP"
const val EXTRA_PORT = "dev.itsh.tetherapi.extra.PORT" const val EXTRA_PORT = "dev.itsh.tetherapi.extra.PORT"

View File

@@ -4,6 +4,7 @@
xmlns:tools="http://schemas.android.com/tools" xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
android:fitsSystemWindows="true"
android:padding="16dp" android:padding="16dp"
tools:context=".MainActivity"> tools:context=".MainActivity">
@@ -152,26 +153,40 @@
app:layout_constraintStart_toStartOf="parent" app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/settingsCard" /> app:layout_constraintTop_toBottomOf="@id/settingsCard" />
<HorizontalScrollView <com.google.android.material.card.MaterialCardView
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="0dp" android:layout_height="0dp"
android:layout_marginTop="8dp" android:layout_marginTop="8dp"
app:cardCornerRadius="8dp"
app:cardElevation="2dp"
app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent" app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent" app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/previewTitle"> app:layout_constraintTop_toBottomOf="@id/previewTitle">
<TextView <ScrollView
android:id="@+id/previewText" android:layout_width="match_parent"
android:layout_width="wrap_content" android:layout_height="match_parent">
android:layout_height="match_parent"
android:background="#F5F5F5"
android:fontFamily="monospace"
android:padding="12dp"
android:textIsSelectable="true"
android:textSize="11sp"
tools:text='{"connection": {...}}' />
</HorizontalScrollView> <HorizontalScrollView
android:layout_width="match_parent"
android:layout_height="wrap_content">
<TextView
android:id="@+id/previewText"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="#F8F9FA"
android:fontFamily="monospace"
android:padding="12dp"
android:textIsSelectable="true"
android:textSize="12sp"
tools:text='{"connection": {...}}' />
</HorizontalScrollView>
</ScrollView>
</com.google.android.material.card.MaterialCardView>
</androidx.constraintlayout.widget.ConstraintLayout> </androidx.constraintlayout.widget.ConstraintLayout>

View File

@@ -2,7 +2,7 @@
# Docker image for Android builds # Docker image for Android builds
android_image := "cimg/android:2025.12" android_image := "cimg/android:2025.12"
gradle_cache := "gradle-cache" gradle_cache := env_var_or_default("GRADLE_CACHE", justfile_directory() + "/.gradle-cache")
# Default task # Default task
default: default:
@@ -12,38 +12,41 @@ default:
build: build:
docker run --rm \ docker run --rm \
-v {{justfile_directory()}}:/project \ -v {{justfile_directory()}}:/project \
-v {{gradle_cache}}:/home/circleci/.gradle \ -v {{gradle_cache}}:/gradle-cache \
-e GRADLE_USER_HOME=/gradle-cache \
-w /project \ -w /project \
{{android_image}} \ {{android_image}} \
./gradlew assembleDebug --no-daemon sh -c './gradlew assembleDebug --no-daemon && chown -R $(stat -c %u:%g /project) /project/.gradle /project/.kotlin /project/app/build 2>/dev/null || true'
@echo "APK: app/build/outputs/apk/debug/app-debug.apk" @echo "APK: app/build/outputs/apk/debug/app-debug.apk"
# Build release APK (requires keystore and env vars) # Build release APK (requires keystore and env vars)
release: release:
docker run --rm \ docker run --rm \
-v {{justfile_directory()}}:/project \ -v {{justfile_directory()}}:/project \
-v {{gradle_cache}}:/home/circleci/.gradle \ -v {{gradle_cache}}:/gradle-cache \
-w /project \ -e GRADLE_USER_HOME=/gradle-cache \
-e KEYSTORE_PASSWORD \ -e KEYSTORE_PASSWORD \
-e KEY_ALIAS \ -e KEY_ALIAS \
-e KEY_PASSWORD \ -e KEY_PASSWORD \
-w /project \
{{android_image}} \ {{android_image}} \
./gradlew assembleRelease --no-daemon sh -c './gradlew assembleRelease --no-daemon && chown -R $(stat -c %u:%g /project) /project/.gradle /project/.kotlin /project/app/build 2>/dev/null || true'
@echo "APK: app/build/outputs/apk/release/app-release.apk" @echo "APK: app/build/outputs/apk/release/app-release.apk"
# Clean build artifacts # Clean build artifacts
clean: clean:
rm -rf app/build build .gradle rm -rf app/build build .gradle .kotlin
@echo "Cleaned" @echo "Cleaned"
# Run gradle tasks # Run gradle tasks
gradle *ARGS: gradle *ARGS:
docker run --rm \ docker run --rm \
-v {{justfile_directory()}}:/project \ -v {{justfile_directory()}}:/project \
-v {{gradle_cache}}:/home/circleci/.gradle \ -v {{gradle_cache}}:/gradle-cache \
-e GRADLE_USER_HOME=/gradle-cache \
-w /project \ -w /project \
{{android_image}} \ {{android_image}} \
./gradlew {{ARGS}} --no-daemon sh -c './gradlew {{ARGS}} --no-daemon && chown -R $(stat -c %u:%g /project) /project/.gradle /project/.kotlin /project/app/build 2>/dev/null || true'
# Generate icons from SVG # Generate icons from SVG
icons: icons: