Skip to content

Commit 59bffe7

Browse files
authored
Merge pull request #26 from androidseb25/dev
Version 2.0 Release
2 parents 96ca803 + c2b35be commit 59bffe7

File tree

88 files changed

+1456
-144
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

88 files changed

+1456
-144
lines changed
Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
name: Android Release
2+
3+
on:
4+
push:
5+
branches: [ "main" ]
6+
workflow_dispatch: {}
7+
8+
permissions:
9+
contents: write # notwendig, um Releases/Tags anzulegen
10+
11+
jobs:
12+
build-release:
13+
runs-on: ubuntu-latest
14+
15+
env:
16+
# Pfade an dein Modul anpassen, falls nicht "app"
17+
APP_MODULE: app
18+
19+
steps:
20+
- name: Checkout
21+
uses: actions/checkout@v4
22+
23+
- name: Setup JDK 17
24+
uses: actions/setup-java@v4
25+
with:
26+
distribution: temurin
27+
java-version: "17"
28+
cache: gradle
29+
30+
- name: Setup Android SDK
31+
uses: android-actions/setup-android@v3
32+
33+
- name: Decode signing keystore
34+
if: ${{ env.ANDROID_KEYSTORE_BASE64 != '' }}
35+
shell: bash
36+
env:
37+
ANDROID_KEYSTORE_BASE64: ${{ secrets.ANDROID_KEYSTORE_BASE64 }}
38+
run: |
39+
echo "$ANDROID_KEYSTORE_BASE64" | base64 -d > keystore.jks
40+
echo "ANDROID_KEYSTORE_PATH=$GITHUB_WORKSPACE/keystore.jks" >> $GITHUB_ENV
41+
42+
- name: Make gradlew executable
43+
run: chmod +x gradlew
44+
45+
# ---- Version aus Gradle lesen ----
46+
- name: Read versionName from Gradle
47+
id: ver
48+
run: |
49+
VERSION_NAME=$(./gradlew -q :${{ env.APP_MODULE }}:printVersionName)
50+
echo "version_name=$VERSION_NAME" >> $GITHUB_OUTPUT
51+
52+
# ---- Release Build (APK & AAB) ----
53+
- name: Build Release APK
54+
run: ./gradlew :${{ env.APP_MODULE }}:assembleRelease
55+
56+
- name: Build Release AAB
57+
run: ./gradlew :${{ env.APP_MODULE }}:bundleRelease
58+
59+
# ---- Artefakte umbenennen ----
60+
- name: Rename outputs to the correct version
61+
id: rename
62+
run: |
63+
set -e
64+
VER="${{ steps.ver.outputs.version_name }}"
65+
66+
APK_SRC="${{ env.APP_MODULE }}/build/outputs/apk/release"
67+
AAB_SRC="${{ env.APP_MODULE }}/build/outputs/bundle/release"
68+
69+
APK_PATH=$(ls "$APK_SRC"/*.apk | head -n1)
70+
AAB_PATH=$(ls "$AAB_SRC"/*.aab | head -n1)
71+
72+
APK_OUT="ipv64net_v${VER}.apk"
73+
AAB_OUT="ipv64net_v${VER}.aab"
74+
75+
cp "$APK_PATH" "$APK_OUT"
76+
cp "$AAB_PATH" "$AAB_OUT"
77+
78+
echo "apk=$APK_OUT" >> $GITHUB_OUTPUT
79+
echo "aab=$AAB_OUT" >> $GITHUB_OUTPUT
80+
echo "version=$VER" >> $GITHUB_OUTPUT
81+
82+
# ---- Als Build-Artefakt hochladen (optional, nützlich für CI-Download) ----
83+
- name: Upload artifacts
84+
uses: actions/upload-artifact@v4
85+
with:
86+
name: ipv64net-${{ steps.rename.outputs.version }}
87+
path: |
88+
${{ steps.rename.outputs.apk }}
89+
${{ steps.rename.outputs.aab }}
90+
91+
# ---- GitHub Release erzeugen + Dateien anhängen ----
92+
- name: Create GitHub Release
93+
uses: softprops/action-gh-release@v2
94+
with:
95+
tag_name: v${{ steps.rename.outputs.version }}
96+
name: "ipv64net v${{ steps.rename.outputs.version }}"
97+
draft: false
98+
prerelease: false
99+
files: |
100+
${{ steps.rename.outputs.apk }}
101+
${{ steps.rename.outputs.aab }}
102+
env:
103+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

.github/workflows/android.yml

Lines changed: 0 additions & 26 deletions
This file was deleted.

.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,3 +15,6 @@
1515
local.properties
1616
app/release/
1717
*.pepk
18+
*.jks
19+
*.b64
20+
*.pem

.idea/studiobot.xml

Lines changed: 6 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

README.md

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
# IPv64net Android App
2+
3+
This is the official Android app for the [IPv64.net](https://ipv64.net/) service. It's built with modern Android development technologies.
4+
5+
## Features
6+
7+
* **Domain Overview:** Manage your domains and see their status.
8+
* **Healthchecks:** Monitor the health of your services.
9+
* **Integrations:** Connect with other services.
10+
* **Logs:** View logs for your services.
11+
* **QR Code Scanner:** For easy setup of the app.
12+
* **And much more!**
13+
14+
## Technologies Used
15+
16+
* **Kotlin:** The primary programming language.
17+
* **Jetpack Compose:** For building the user interface.
18+
* **OkHttp & Gson:** For networking and JSON parsing.
19+
* **CameraX:** For the QR code scanner functionality.
20+
* **Accompanist:** For permissions handling.
21+
22+
## Building the Project
23+
24+
To build the project, you need Android Studio.
25+
26+
1. Clone the repository: `git clone https://github.com/ipv64/ipv64-android-app.git`
27+
2. Open the project in Android Studio.
28+
3. Build the project using the "Build" menu or by running `./gradlew build` in the terminal.
29+
30+
## Contributing
31+
32+
Contributions are welcome! Please feel free to open an issue or submit a pull request.

app/build.gradle.kts

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ android {
1313
minSdk = 28
1414
targetSdk = 36
1515
versionCode = 20
16-
versionName = "2.0"
16+
versionName = "2.0.0"
1717

1818
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
1919
}
@@ -39,6 +39,12 @@ android {
3939
}
4040
}
4141

42+
tasks.register("printVersionName") {
43+
doLast {
44+
println(android.defaultConfig.versionName)
45+
}
46+
}
47+
4248
dependencies {
4349

4450
implementation(libs.androidx.core.ktx)
@@ -61,6 +67,9 @@ dependencies {
6167
implementation(libs.androidx.camera.lifecycle)
6268
implementation(libs.androidx.camera.view)
6369
implementation(libs.accompanist.permissions)
70+
implementation(libs.androidx.biometric)
71+
implementation(libs.androidx.datastore.preferences)
72+
implementation(libs.androidx.lifecycle.process)
6473
testImplementation(libs.junit)
6574
androidTestImplementation(libs.androidx.junit)
6675
androidTestImplementation(libs.androidx.espresso.core)
17.2 KB
Loading
Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
package de.rpicloud.ipv64net.helper
2+
3+
import android.os.Build
4+
import androidx.appcompat.app.AppCompatActivity
5+
import androidx.biometric.BiometricManager
6+
import androidx.biometric.BiometricManager.Authenticators.BIOMETRIC_STRONG
7+
import androidx.biometric.BiometricManager.Authenticators.DEVICE_CREDENTIAL
8+
import androidx.biometric.BiometricPrompt
9+
import kotlinx.coroutines.channels.Channel
10+
import kotlinx.coroutines.flow.receiveAsFlow
11+
12+
class BiometricPromptManager(
13+
private val activity: AppCompatActivity
14+
) {
15+
private val resultChannel = Channel<BiometricResult>()
16+
val promptResults = resultChannel.receiveAsFlow()
17+
18+
fun showBiometricPrompt(
19+
title: String,
20+
description: String
21+
) {
22+
val manager = BiometricManager.from(activity)
23+
val authenticator = if (Build.VERSION.SDK_INT >= 30) BIOMETRIC_STRONG or DEVICE_CREDENTIAL else BIOMETRIC_STRONG
24+
val promptInfo = BiometricPrompt.PromptInfo.Builder()
25+
.setTitle(title)
26+
.setSubtitle(description)
27+
.setAllowedAuthenticators(authenticator)
28+
29+
if (Build.VERSION.SDK_INT < 30) {
30+
promptInfo.setNegativeButtonText("Cancel")
31+
}
32+
33+
when(manager.canAuthenticate(authenticator)) {
34+
BiometricManager.BIOMETRIC_ERROR_HW_UNAVAILABLE -> {
35+
resultChannel.trySend(BiometricResult.HardwareUnavailable)
36+
return
37+
}
38+
BiometricManager.BIOMETRIC_ERROR_NO_HARDWARE -> {
39+
resultChannel.trySend(BiometricResult.FeatureUnavailable)
40+
return
41+
42+
}
43+
BiometricManager.BIOMETRIC_ERROR_NONE_ENROLLED -> {
44+
resultChannel.trySend(BiometricResult.AuthenticationNotSet)
45+
return
46+
}
47+
else -> Unit
48+
}
49+
50+
val prompt = BiometricPrompt(
51+
activity,
52+
object : BiometricPrompt.AuthenticationCallback() {
53+
override fun onAuthenticationError(errorCode: Int, errString: CharSequence
54+
) {
55+
super.onAuthenticationError(errorCode, errString)
56+
resultChannel.trySend(BiometricResult.AuthenticationError(errString.toString()))
57+
}
58+
59+
override fun onAuthenticationSucceeded(result: BiometricPrompt.AuthenticationResult) {
60+
super.onAuthenticationSucceeded(result)
61+
resultChannel.trySend(BiometricResult.AuthenticationSuccess)
62+
}
63+
64+
override fun onAuthenticationFailed() {
65+
super.onAuthenticationFailed()
66+
resultChannel.trySend(BiometricResult.AuthenticationFailed)
67+
}
68+
}
69+
)
70+
prompt.authenticate(promptInfo.build())
71+
}
72+
73+
sealed interface BiometricResult {
74+
data object HardwareUnavailable : BiometricResult
75+
data object FeatureUnavailable : BiometricResult
76+
data class AuthenticationError(val error: String) : BiometricResult
77+
data object AuthenticationFailed : BiometricResult
78+
data object AuthenticationSuccess: BiometricResult
79+
data object AuthenticationNotSet: BiometricResult
80+
}
81+
}

app/src/main/java/de/rpicloud/ipv64net/helper/NetworkService.kt

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -99,7 +99,7 @@ class NetworkService {
9999
println("♻️ - ${result.email}")
100100
callback(NetworkResult("Success", result, 200))
101101
} else {
102-
callback(NetworkResult("Fehler: ${response.code}", null, response.code))
102+
callback(NetworkResult("Fehler: ${response.code}", responseText, response.code))
103103
}
104104
} catch (e: Exception) {
105105
callback(NetworkResult(e.localizedMessage, null, 500))
@@ -128,7 +128,7 @@ class NetworkService {
128128
println("♻️ - ${result.get_account_info}")
129129
callback(NetworkResult("Success", result, 200))
130130
} else {
131-
callback(NetworkResult("Fehler: ${response.code}", null, response.code))
131+
callback(NetworkResult("Fehler: ${response.code}", responseText, response.code))
132132
}
133133
} catch (e: Exception) {
134134
callback(NetworkResult(e.localizedMessage, null, 500))
@@ -160,7 +160,7 @@ class NetworkService {
160160
}
161161
} else {
162162
withContext(Dispatchers.Main) {
163-
callback(NetworkResult("Fehler: ${response.code}", null, response.code))
163+
callback(NetworkResult("Fehler: ${response.code}", responseText, response.code))
164164
}
165165
}
166166
} catch (e: Exception) {
@@ -192,7 +192,7 @@ class NetworkService {
192192
println("♻️ - ${result.add_domain}")
193193
callback(NetworkResult("Success", result, 200))
194194
} else {
195-
callback(NetworkResult("Fehler: ${response.code}", null, response.code))
195+
callback(NetworkResult("Fehler: ${response.code}", responseText, response.code))
196196
}
197197
} catch (e: Exception) {
198198
callback(NetworkResult(e.localizedMessage, null, 500))
@@ -225,7 +225,7 @@ class NetworkService {
225225
}
226226
} else {
227227
withContext(Dispatchers.Main) {
228-
callback(NetworkResult("Fehler: ${response.code}", null, response.code))
228+
callback(NetworkResult("Fehler: ${response.code}", responseText, response.code))
229229
}
230230
}
231231
} catch (e: Exception) {
@@ -381,7 +381,7 @@ class NetworkService {
381381
}
382382
} else {
383383
withContext(Dispatchers.Main) {
384-
callback(NetworkResult("Fehler: ${response.code}", null, response.code))
384+
callback(NetworkResult("Fehler: ${response.code}", responseText, response.code))
385385
}
386386
}
387387
} catch (e: Exception) {
@@ -463,7 +463,7 @@ class NetworkService {
463463
println("♻️ - ${result.get_account_info}")
464464
callback(NetworkResult("Success", result, 200))
465465
} else {
466-
callback(NetworkResult("Fehler: ${response.code}", null, response.code))
466+
callback(NetworkResult("Fehler: ${response.code}", responseText, response.code))
467467
}
468468
} catch (e: Exception) {
469469
callback(NetworkResult(e.localizedMessage, null, 500))

0 commit comments

Comments
 (0)