@@ -47,6 +47,7 @@ import androidx.compose.foundation.layout.fillMaxWidth
4747import androidx.compose.foundation.layout.height
4848import androidx.compose.foundation.layout.PaddingValues
4949import androidx.compose.foundation.layout.padding
50+ import androidx.compose.foundation.layout.Row
5051import androidx.compose.material3.Button
5152import androidx.compose.material3.Card
5253import androidx.compose.material3.CircularProgressIndicator
@@ -66,6 +67,7 @@ import androidx.compose.ui.Modifier
6667import androidx.compose.ui.platform.LocalContext
6768import androidx.compose.ui.unit.dp
6869import androidx.compose.ui.window.Dialog
70+ import androidx.compose.ui.window.DialogProperties
6971import androidx.core.content.ContextCompat
7072import androidx.lifecycle.lifecycleScope
7173import androidx.navigation.NavHostController
@@ -129,6 +131,11 @@ class MainActivity : ComponentActivity() {
129131 private var onMediaProjectionPermissionGranted: (() -> Unit )? = null
130132 private var onWebRtcMediaProjectionResult: ((Int , Intent ) -> Unit )? = null
131133
134+ // Payment Dialog State (Task 6)
135+ private var showPaymentMethodDialog by mutableStateOf(false )
136+ private var showPayPalWebViewDialog by mutableStateOf(false )
137+ private var paypalSubscriptionId by mutableStateOf(" " )
138+
132139 private val screenshotRequestHandler = object : BroadcastReceiver () {
133140 override fun onReceive (context : Context ? , intent : Intent ? ) {
134141 if (intent?.action == ACTION_REQUEST_MEDIAPROJECTION_SCREENSHOT ) {
@@ -326,7 +333,7 @@ class MainActivity : ComponentActivity() {
326333 }
327334 } else if (billingResult.responseCode == BillingClient .BillingResponseCode .USER_CANCELED ) {
328335 Log .i(TAG , " purchasesUpdatedListener: User cancelled the purchase flow." )
329- Toast .makeText(this , " Donation process cancelled." , Toast .LENGTH_SHORT ).show()
336+ Toast .makeText(this , " Support cancelled." , Toast .LENGTH_SHORT ).show()
330337 } else {
331338 Log .e(TAG , " purchasesUpdatedListener: Billing error: ${billingResult.debugMessage} (Code: ${billingResult.responseCode} )" )
332339 Toast .makeText(this , " Error during donation process: ${billingResult.debugMessage} " , Toast .LENGTH_LONG ).show()
@@ -405,6 +412,9 @@ class MainActivity : ComponentActivity() {
405412 Log .d(TAG , " onCreate: Calling setupBillingClient." )
406413 setupBillingClient()
407414
415+ Log .d(TAG , " onCreate: Loading Model Preference." )
416+ GenerativeAiViewModelFactory .loadModelPreference(this )
417+
408418 Log .d(TAG , " onCreate: Calling TrialManager.initializeTrialStateFlagsIfNecessary." )
409419 TrialManager .initializeTrialStateFlagsIfNecessary(this )
410420
@@ -511,7 +521,18 @@ class MainActivity : ComponentActivity() {
511521 ActivityResultContracts .StartActivityForResult ()
512522 ) { result ->
513523 if (result.resultCode == Activity .RESULT_OK && result.data != null ) {
514- Log .i(TAG , " WebRTC MediaProjection permission granted." )
524+ Log .i(TAG , " WebRTC MediaProjection permission granted. Starting keep-alive service." )
525+
526+ // Task 4: Keep Service Alive to satisfy Android 14 MediaProjection requirements
527+ val serviceIntent = Intent (this , ScreenCaptureService ::class .java).apply {
528+ action = ScreenCaptureService .ACTION_KEEP_ALIVE_FOR_WEBRTC
529+ }
530+ if (Build .VERSION .SDK_INT >= Build .VERSION_CODES .O ) {
531+ startForegroundService(serviceIntent)
532+ } else {
533+ startService(serviceIntent)
534+ }
535+
515536 onWebRtcMediaProjectionResult?.invoke(result.resultCode, result.data!! )
516537 onWebRtcMediaProjectionResult = null
517538 } else {
@@ -636,6 +657,59 @@ class MainActivity : ComponentActivity() {
636657 }
637658 }
638659 }
660+
661+ // Task 6: Payment Method Dialog
662+ if (showPaymentMethodDialog) {
663+ androidx.compose.material3.AlertDialog (
664+ onDismissRequest = { showPaymentMethodDialog = false },
665+ title = { Text (" Choose Payment Method" ) },
666+ text = {
667+ Column {
668+ Button (
669+ onClick = {
670+ showPaymentMethodDialog = false
671+
672+ // Generate Short UUID
673+ val shortId = java.util.UUID .randomUUID().toString().substring(0 , 8 )
674+
675+ // Save it to SharedPreferences
676+ val ctx = this @MainActivity
677+ ctx.getSharedPreferences(PREFS_NAME , Context .MODE_PRIVATE )
678+ .edit()
679+ .putString(" payment_support_id" , shortId)
680+ .apply ()
681+
682+ Toast .makeText(ctx, " Your Support ID is: $shortId " , Toast .LENGTH_LONG ).show()
683+
684+ val url = " https://www.paypal.com/webapps/billing/subscriptions?plan_id=P-5J921557TD348880GNGUCRSI&custom_id=$shortId "
685+ val intent = Intent (Intent .ACTION_VIEW , android.net.Uri .parse(url))
686+ ctx.startActivity(intent)
687+ },
688+ modifier = Modifier .fillMaxWidth().padding(bottom = 8 .dp)
689+ ) {
690+ Text (" PayPal (2,60 €/Month)" )
691+ }
692+ Button (
693+ onClick = {
694+ showPaymentMethodDialog = false
695+ launchGooglePlayBilling()
696+ },
697+ modifier = Modifier .fillMaxWidth()
698+ ) {
699+ Text (" Google Play (2,90 €/Month)" )
700+ }
701+ }
702+ },
703+ confirmButton = {},
704+ dismissButton = {
705+ TextButton (onClick = { showPaymentMethodDialog = false }) {
706+ Text (" Cancel" )
707+ }
708+ }
709+ )
710+ }
711+
712+
639713 }
640714 }
641715 }
@@ -880,28 +954,32 @@ class MainActivity : ComponentActivity() {
880954 }
881955
882956 private fun initiateDonationPurchase () {
883- Log .d(TAG , " initiateDonationPurchase called." )
957+ Log .d(TAG , " initiateDonationPurchase called. Showing Payment Method Dialog." )
958+ showPaymentMethodDialog = true
959+ }
960+
961+ private fun launchGooglePlayBilling () {
884962 if (! ::billingClient.isInitialized) {
885- Log .e(TAG , " initiateDonationPurchase : BillingClient not initialized." )
963+ Log .e(TAG , " launchGooglePlayBilling : BillingClient not initialized." )
886964 updateStatusMessage(" Payment service not initialized. Please try again later." , true )
887965 return
888966 }
889967 if (! billingClient.isReady) {
890- Log .e(TAG , " initiateDonationPurchase : BillingClient not ready. Connection state: ${billingClient.connectionState} " )
968+ Log .e(TAG , " launchGooglePlayBilling : BillingClient not ready. Connection state: ${billingClient.connectionState} " )
891969 updateStatusMessage(" Payment service not ready. Please try again later." , true )
892970 if (billingClient.connectionState == BillingClient .ConnectionState .CLOSED || billingClient.connectionState == BillingClient .ConnectionState .DISCONNECTED ){
893- Log .d(TAG , " initiateDonationPurchase : BillingClient disconnected, attempting to reconnect." )
971+ Log .d(TAG , " launchGooglePlayBilling : BillingClient disconnected, attempting to reconnect." )
894972 billingClient.startConnection(object : BillingClientStateListener {
895973 override fun onBillingSetupFinished (setupResult : BillingResult ) {
896- Log .i(TAG , " initiateDonationPurchase (reconnect): onBillingSetupFinished. ResponseCode: ${setupResult.responseCode} " )
974+ Log .i(TAG , " launchGooglePlayBilling (reconnect): onBillingSetupFinished. ResponseCode: ${setupResult.responseCode} " )
897975 if (setupResult.responseCode == BillingClient .BillingResponseCode .OK ) {
898- Log .d(TAG , " initiateDonationPurchase (reconnect): Reconnection successful, retrying purchase." )
899- initiateDonationPurchase ()
976+ Log .d(TAG , " launchGooglePlayBilling (reconnect): Reconnection successful, retrying purchase." )
977+ launchGooglePlayBilling ()
900978 } else {
901- Log .e(TAG , " initiateDonationPurchase (reconnect): BillingClient setup failed after disconnect: ${setupResult.debugMessage} " )
979+ Log .e(TAG , " launchGooglePlayBilling (reconnect): BillingClient setup failed after disconnect: ${setupResult.debugMessage} " )
902980 }
903981 }
904- override fun onBillingServiceDisconnected () { Log .w(TAG , " initiateDonationPurchase (reconnect): BillingClient still disconnected." ) }
982+ override fun onBillingServiceDisconnected () { Log .w(TAG , " launchGooglePlayBilling (reconnect): BillingClient still disconnected." ) }
905983 })
906984 }
907985 return
0 commit comments