Skip to content

Commit b12089f

Browse files
Implement 20 feature requests including Offline Model updates, Human Operator background listening and UI fixes
1 parent b56f067 commit b12089f

13 files changed

Lines changed: 582 additions & 72 deletions

File tree

.github/workflows/manual.yml

Lines changed: 45 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -69,9 +69,53 @@ jobs:
6969
7070
echo "Results: app=$APP_CHANGED, humanoperator=$HUMANOPERATOR_CHANGED, shared=$SHARED_CHANGED"
7171
72-
build:
72+
compile-check:
7373
needs: detect-changes
7474
runs-on: ubuntu-latest
75+
steps:
76+
- name: Checkout code
77+
uses: actions/checkout@v4
78+
79+
- name: Set up JDK 17
80+
uses: actions/setup-java@v4
81+
with:
82+
java-version: '17'
83+
distribution: 'temurin'
84+
cache: gradle
85+
86+
- name: Decode google-services.json (app)
87+
env:
88+
GOOGLE_SERVICES_JSON: ${{ secrets.GOOGLE_SERVICES_JSON_APP }}
89+
run: printf '%s' "$GOOGLE_SERVICES_JSON" > app/google-services.json
90+
91+
- name: Decode google-services.json (humanoperator)
92+
env:
93+
GOOGLE_SERVICES_JSON: ${{ secrets.GOOGLE_SERVICES_JSON_HUMANOPERATOR }}
94+
run: printf '%s' "$GOOGLE_SERVICES_JSON" > humanoperator/google-services.json
95+
96+
- name: Create local.properties
97+
run: echo "sdk.dir=$ANDROID_HOME" > local.properties
98+
99+
- name: Fix gradle.properties for CI
100+
run: |
101+
sed -i '/org.gradle.java.home=/d' gradle.properties
102+
sed -i 's/org.gradle.jvmargs=.*/org.gradle.jvmargs=-Xmx2048m -XX:MaxMetaspaceSize=512m/' gradle.properties
103+
sed -i 's/kotlin.daemon.jvmargs=.*/kotlin.daemon.jvmargs=-Xmx1536m -XX:MaxMetaspaceSize=512m/' gradle.properties
104+
105+
- name: Grant execute permission for gradlew
106+
run: chmod +x gradlew
107+
108+
- name: Compile Kotlin (app)
109+
if: needs.detect-changes.outputs.app_changed == 'true' || needs.detect-changes.outputs.shared_changed == 'true'
110+
run: ./gradlew :app:compileDebugKotlin
111+
112+
- name: Compile Kotlin (humanoperator)
113+
if: needs.detect-changes.outputs.humanoperator_changed == 'true' || needs.detect-changes.outputs.shared_changed == 'true'
114+
run: ./gradlew :humanoperator:compileDebugKotlin
115+
116+
build:
117+
needs: [detect-changes, compile-check]
118+
runs-on: ubuntu-latest
75119
env:
76120
BUILD_APP: ${{ needs.detect-changes.outputs.app_changed == 'true' || needs.detect-changes.outputs.shared_changed == 'true' }}
77121
BUILD_HUMANOPERATOR: ${{ needs.detect-changes.outputs.humanoperator_changed == 'true' || needs.detect-changes.outputs.shared_changed == 'true' }}

app/src/main/kotlin/com/google/ai/sample/GenerativeAiViewModelFactory.kt

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -157,11 +157,11 @@ object GenerativeAiViewModelFactory {
157157

158158
fun loadBackendPreference(context: Context) {
159159
val prefs = context.getSharedPreferences("inference_prefs", Context.MODE_PRIVATE)
160-
val backendName = prefs.getString("preferred_backend", InferenceBackend.CPU.name)
160+
val backendName = prefs.getString("preferred_backend", InferenceBackend.GPU.name)
161161
currentBackend = try {
162-
InferenceBackend.valueOf(backendName ?: InferenceBackend.CPU.name)
162+
InferenceBackend.valueOf(backendName ?: InferenceBackend.GPU.name)
163163
} catch (e: IllegalArgumentException) {
164-
InferenceBackend.CPU
164+
InferenceBackend.GPU
165165
}
166166
}
167167
}

app/src/main/kotlin/com/google/ai/sample/MainActivity.kt

Lines changed: 129 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -129,6 +129,11 @@ class MainActivity : ComponentActivity() {
129129
private var onMediaProjectionPermissionGranted: (() -> Unit)? = null
130130
private var onWebRtcMediaProjectionResult: ((Int, Intent) -> Unit)? = null
131131

132+
// Payment Dialog State (Task 6)
133+
private var showPaymentMethodDialog by mutableStateOf(false)
134+
private var showPayPalWebViewDialog by mutableStateOf(false)
135+
private var paypalSubscriptionId by mutableStateOf("")
136+
132137
private val screenshotRequestHandler = object : BroadcastReceiver() {
133138
override fun onReceive(context: Context?, intent: Intent?) {
134139
if (intent?.action == ACTION_REQUEST_MEDIAPROJECTION_SCREENSHOT) {
@@ -326,7 +331,7 @@ class MainActivity : ComponentActivity() {
326331
}
327332
} else if (billingResult.responseCode == BillingClient.BillingResponseCode.USER_CANCELED) {
328333
Log.i(TAG, "purchasesUpdatedListener: User cancelled the purchase flow.")
329-
Toast.makeText(this, "Donation process cancelled.", Toast.LENGTH_SHORT).show()
334+
Toast.makeText(this, "Support cancelled.", Toast.LENGTH_SHORT).show()
330335
} else {
331336
Log.e(TAG, "purchasesUpdatedListener: Billing error: ${billingResult.debugMessage} (Code: ${billingResult.responseCode})")
332337
Toast.makeText(this, "Error during donation process: ${billingResult.debugMessage}", Toast.LENGTH_LONG).show()
@@ -636,6 +641,116 @@ class MainActivity : ComponentActivity() {
636641
}
637642
}
638643
}
644+
645+
// Task 6: Payment Method Dialog
646+
if (showPaymentMethodDialog) {
647+
AlertDialog(
648+
onDismissRequest = { showPaymentMethodDialog = false },
649+
title = { Text("Choose Payment Method") },
650+
text = {
651+
Column {
652+
Button(
653+
onClick = {
654+
showPaymentMethodDialog = false
655+
showPayPalWebViewDialog = true
656+
},
657+
modifier = Modifier.fillMaxWidth().padding(bottom = 8.dp)
658+
) {
659+
Text("PayPal (2,60 €/Month)")
660+
}
661+
Button(
662+
onClick = {
663+
showPaymentMethodDialog = false
664+
launchGooglePlayBilling()
665+
},
666+
modifier = Modifier.fillMaxWidth()
667+
) {
668+
Text("Google Play (2,90 €/Month)")
669+
}
670+
}
671+
},
672+
confirmButton = {},
673+
dismissButton = {
674+
TextButton(onClick = { showPaymentMethodDialog = false }) {
675+
Text("Cancel")
676+
}
677+
}
678+
)
679+
}
680+
681+
// Task 6: PayPal WebView Dialog
682+
if (showPayPalWebViewDialog) {
683+
Dialog(onDismissRequest = { showPayPalWebViewDialog = false }) {
684+
Card(modifier = Modifier.fillMaxSize().padding(16.dp)) {
685+
Column(modifier = Modifier.fillMaxSize()) {
686+
Row(
687+
modifier = Modifier.fillMaxWidth().padding(8.dp),
688+
horizontalArrangement = Arrangement.SpaceBetween,
689+
verticalAlignment = Alignment.CenterVertically
690+
) {
691+
Text("PayPal Subscription", style = MaterialTheme.typography.titleMedium)
692+
TextButton(onClick = { showPayPalWebViewDialog = false }) {
693+
Text("Close")
694+
}
695+
}
696+
697+
androidx.compose.ui.viewinterop.AndroidView(
698+
factory = { ctx ->
699+
android.webkit.WebView(ctx).apply {
700+
settings.javaScriptEnabled = true
701+
settings.domStorageEnabled = true
702+
webViewClient = android.webkit.WebViewClient()
703+
704+
// Generate Short UUID
705+
val shortId = java.util.UUID.randomUUID().toString().substring(0, 8)
706+
707+
// Save it to SharedPreferences
708+
ctx.getSharedPreferences(PREFS_NAME, Context.MODE_PRIVATE)
709+
.edit()
710+
.putString("payment_support_id", shortId)
711+
.apply()
712+
713+
val html = """
714+
<!DOCTYPE html>
715+
<html>
716+
<head>
717+
<meta name="viewport" content="width=device-width, initial-scale=1">
718+
</head>
719+
<body>
720+
<div id="paypal-button-container-P-5J921557TD348880GNGUCRSI"></div>
721+
<script src="https://www.paypal.com/sdk/js?client-id=AQ52P9G85S3RCHw7lWnEDH_Pudk-5JdE8S6gBfS72jWwMng-xR-0qNrtmS8Mv5RtdK--a1cZ0G-12_rZ&vault=true&intent=subscription" data-sdk-integration-source="button-factory"></script>
722+
<script>
723+
paypal.Buttons({
724+
style: {
725+
shape: 'rect',
726+
color: 'gold',
727+
layout: 'vertical',
728+
label: 'subscribe'
729+
},
730+
createSubscription: function(data, actions) {
731+
return actions.subscription.create({
732+
'plan_id': 'P-5J921557TD348880GNGUCRSI',
733+
'custom_id': '$shortId'
734+
});
735+
},
736+
onApprove: function(data, actions) {
737+
alert('Thank you for your subscription! Your Support ID is: $shortId');
738+
}
739+
}).render('#paypal-button-container-P-5J921557TD348880GNGUCRSI');
740+
</script>
741+
</body>
742+
</html>
743+
""".trimIndent()
744+
745+
loadDataWithBaseURL("https://www.paypal.com", html, "text/html", "UTF-8", null)
746+
}
747+
},
748+
modifier = Modifier.fillMaxSize()
749+
)
750+
}
751+
}
752+
}
753+
}
639754
}
640755
}
641756
}
@@ -880,28 +995,32 @@ class MainActivity : ComponentActivity() {
880995
}
881996

882997
private fun initiateDonationPurchase() {
883-
Log.d(TAG, "initiateDonationPurchase called.")
998+
Log.d(TAG, "initiateDonationPurchase called. Showing Payment Method Dialog.")
999+
showPaymentMethodDialog = true
1000+
}
1001+
1002+
private fun launchGooglePlayBilling() {
8841003
if (!::billingClient.isInitialized) {
885-
Log.e(TAG, "initiateDonationPurchase: BillingClient not initialized.")
1004+
Log.e(TAG, "launchGooglePlayBilling: BillingClient not initialized.")
8861005
updateStatusMessage("Payment service not initialized. Please try again later.", true)
8871006
return
8881007
}
8891008
if (!billingClient.isReady) {
890-
Log.e(TAG, "initiateDonationPurchase: BillingClient not ready. Connection state: ${billingClient.connectionState}")
1009+
Log.e(TAG, "launchGooglePlayBilling: BillingClient not ready. Connection state: ${billingClient.connectionState}")
8911010
updateStatusMessage("Payment service not ready. Please try again later.", true)
8921011
if (billingClient.connectionState == BillingClient.ConnectionState.CLOSED || billingClient.connectionState == BillingClient.ConnectionState.DISCONNECTED){
893-
Log.d(TAG, "initiateDonationPurchase: BillingClient disconnected, attempting to reconnect.")
1012+
Log.d(TAG, "launchGooglePlayBilling: BillingClient disconnected, attempting to reconnect.")
8941013
billingClient.startConnection(object : BillingClientStateListener {
8951014
override fun onBillingSetupFinished(setupResult: BillingResult) {
896-
Log.i(TAG, "initiateDonationPurchase (reconnect): onBillingSetupFinished. ResponseCode: ${setupResult.responseCode}")
1015+
Log.i(TAG, "launchGooglePlayBilling (reconnect): onBillingSetupFinished. ResponseCode: ${setupResult.responseCode}")
8971016
if (setupResult.responseCode == BillingClient.BillingResponseCode.OK) {
898-
Log.d(TAG, "initiateDonationPurchase (reconnect): Reconnection successful, retrying purchase.")
899-
initiateDonationPurchase()
1017+
Log.d(TAG, "launchGooglePlayBilling (reconnect): Reconnection successful, retrying purchase.")
1018+
launchGooglePlayBilling()
9001019
} else {
901-
Log.e(TAG, "initiateDonationPurchase (reconnect): BillingClient setup failed after disconnect: ${setupResult.debugMessage}")
1020+
Log.e(TAG, "launchGooglePlayBilling (reconnect): BillingClient setup failed after disconnect: ${setupResult.debugMessage}")
9021021
}
9031022
}
904-
override fun onBillingServiceDisconnected() { Log.w(TAG, "initiateDonationPurchase (reconnect): BillingClient still disconnected.") }
1023+
override fun onBillingServiceDisconnected() { Log.w(TAG, "launchGooglePlayBilling (reconnect): BillingClient still disconnected.") }
9051024
})
9061025
}
9071026
return

0 commit comments

Comments
 (0)