diff --git a/appdistribution/README.md b/appdistribution/README.md index 86f878b3ae..0519b4aee6 100644 --- a/appdistribution/README.md +++ b/appdistribution/README.md @@ -2,7 +2,11 @@ ## Introduction -The Firebase App Distribution SDK enables you to display in-app alerts to your testers when new builds of your app are available to install. This quickstart aims to showcase how to use the App Distribution SDK to create and customize new build alerts for your testers. You can read more +The Firebase App Distribution SDK enables you to: +- display in-app alerts to your testers when new builds of your app are available to install; +- collect in-app feedback from your testers. + +This quickstart aims to showcase how to use the App Distribution SDK. You can read more about Firebase App Distribution [here](https://firebase.google.com/docs/app-distribution)! ## Getting Started diff --git a/appdistribution/app/build.gradle b/appdistribution/app/build.gradle index 9825af2c2f..acc69c1926 100644 --- a/appdistribution/app/build.gradle +++ b/appdistribution/app/build.gradle @@ -49,8 +49,10 @@ dependencies { // Import the Firebase BoM (see: https://firebase.google.com/docs/android/learn-more#bom) implementation platform('com.google.firebase:firebase-bom:31.2.0') - // ADD the SDK to the "prerelease" variant only (example) - implementation 'com.google.firebase:firebase-appdistribution-ktx:16.0.0-beta01' + implementation 'com.google.firebase:firebase-appdistribution-api-ktx:16.0.0-beta06' + + // ADD the full SDK implementation to the "debug" variant only + debugImplementation 'com.google.firebase:firebase-appdistribution:16.0.0-beta06' // For an optimal experience using App Distribution, add the Firebase SDK // for Google Analytics. This is recommended, but not required. diff --git a/appdistribution/app/src/main/AndroidManifest.xml b/appdistribution/app/src/main/AndroidManifest.xml index a29b19adf7..4d9e2f30bb 100644 --- a/appdistribution/app/src/main/AndroidManifest.xml +++ b/appdistribution/app/src/main/AndroidManifest.xml @@ -1,6 +1,9 @@ + + + requestPermissionLauncher = + registerForActivityResult(new ActivityResultContracts.RequestPermission(), isGranted -> { + if (isGranted) { + showFeedbackNotification(); + } else { + Toast.makeText( + MainActivity.this, + "You won't be able to tap a notification to send feedback because" + + " the notification permission was denied", + Toast.LENGTH_LONG + ).show(); + } + }); private FirebaseAppDistribution mFirebaseAppDistribution; @@ -21,6 +47,14 @@ protected void onCreate(Bundle savedInstanceState) { setContentView(binding.getRoot()); mFirebaseAppDistribution = FirebaseAppDistribution.getInstance(); + + binding.btShowNotification.setOnClickListener(view -> { + askNotificationPermission(); + }); + + binding.btSendFeedback.setOnClickListener(view -> { + mFirebaseAppDistribution.startFeedback(R.string.feedback_additional_form_text); + }); } @Override @@ -37,4 +71,48 @@ public void onResume() { } }); } + + @Override + protected void onDestroy() { + super.onDestroy(); + // Hide the notification once this activity is destroyed + mFirebaseAppDistribution.cancelFeedbackNotification(); + } + + private void showFeedbackNotification() { + mFirebaseAppDistribution.showFeedbackNotification( + R.string.feedback_additional_form_text, + InterruptionLevel.HIGH + ); + } + + private void askNotificationPermission() { + // This is only necessary for API level >= 33 (TIRAMISU) + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { + if (ContextCompat.checkSelfPermission(this, Manifest.permission.POST_NOTIFICATIONS) == + PackageManager.PERMISSION_GRANTED) { + // All set. We can post notifications + showFeedbackNotification(); + } else if (shouldShowRequestPermissionRationale(Manifest.permission.POST_NOTIFICATIONS)) { + Log.i(TAG, "Showing customer rationale for requesting permission."); + new AlertDialog.Builder(this) + .setMessage("Using a notification to initiate feedback to the developer. " + + "To enable this feature, allow the app to post notifications." + ) + .setPositiveButton("OK", (dialogInterface, i) -> { + Log.i(TAG, "Launching request for permission."); + requestPermissionLauncher.launch(Manifest.permission.POST_NOTIFICATIONS); + }) + .setNegativeButton("No thanks", (dialogInterface, i) -> { + Log.i(TAG, "User denied permission request."); + }) + .show(); + } else { + // Directly ask for the permission + requestPermissionLauncher.launch(Manifest.permission.POST_NOTIFICATIONS); + } + } else { + showFeedbackNotification(); + } + } } diff --git a/appdistribution/app/src/main/java/com/google/firebase/appdistributionquickstart/kotlin/KotlinMainActivity.kt b/appdistribution/app/src/main/java/com/google/firebase/appdistributionquickstart/kotlin/KotlinMainActivity.kt index 9d703fc5c6..8275425fd7 100644 --- a/appdistribution/app/src/main/java/com/google/firebase/appdistributionquickstart/kotlin/KotlinMainActivity.kt +++ b/appdistribution/app/src/main/java/com/google/firebase/appdistributionquickstart/kotlin/KotlinMainActivity.kt @@ -1,10 +1,20 @@ package com.google.firebase.appdistributionquickstart.kotlin +import android.Manifest +import android.content.pm.PackageManager +import android.os.Build import android.os.Bundle +import android.util.Log +import android.widget.Toast +import androidx.activity.result.contract.ActivityResultContracts +import androidx.appcompat.app.AlertDialog import androidx.appcompat.app.AppCompatActivity +import androidx.core.content.ContextCompat import com.google.firebase.appdistribution.FirebaseAppDistribution import com.google.firebase.appdistribution.FirebaseAppDistributionException +import com.google.firebase.appdistribution.InterruptionLevel import com.google.firebase.appdistribution.ktx.appDistribution +import com.google.firebase.appdistributionquickstart.R import com.google.firebase.appdistributionquickstart.databinding.ActivityMainBinding import com.google.firebase.ktx.Firebase @@ -12,12 +22,36 @@ class KotlinMainActivity : AppCompatActivity() { private lateinit var firebaseAppDistribution: FirebaseAppDistribution + // Declare the launcher at the top of your Activity/Fragment: + private val requestPermissionLauncher = registerForActivityResult( + ActivityResultContracts.RequestPermission() + ) { isGranted: Boolean -> + if (isGranted) { + showFeedbackNotification() + } else { + Toast.makeText( + this@KotlinMainActivity, + "You won't be able to tap a notification to send feedback because" + + " the notification permission was denied", + Toast.LENGTH_LONG + ).show() + } + } + override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) val binding = ActivityMainBinding.inflate(layoutInflater) setContentView(binding.root) firebaseAppDistribution = Firebase.appDistribution + + binding.btShowNotification.setOnClickListener { + askNotificationPermission() + } + + binding.btSendFeedback.setOnClickListener { + firebaseAppDistribution.startFeedback(R.string.feedback_additional_form_text) + } } override fun onResume() { @@ -34,10 +68,51 @@ class KotlinMainActivity : AppCompatActivity() { } } + override fun onDestroy() { + super.onDestroy() + // Hide the notification once this activity is destroyed + firebaseAppDistribution.cancelFeedbackNotification() + } + private fun showFeedbackNotification() { + firebaseAppDistribution.showFeedbackNotification( + R.string.feedback_additional_form_text, + InterruptionLevel.HIGH + ) + } + + private fun askNotificationPermission() { + // This is only necessary for API level >= 33 (TIRAMISU) + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { + if (ContextCompat.checkSelfPermission(this, Manifest.permission.POST_NOTIFICATIONS) == + PackageManager.PERMISSION_GRANTED + ) { + // All set. We can post notifications + showFeedbackNotification() + } else if (shouldShowRequestPermissionRationale(Manifest.permission.POST_NOTIFICATIONS)) { + Log.i(TAG, "Showing customer rationale for requesting permission.") + AlertDialog.Builder(this) + .setMessage( + "Using a notification to initiate feedback to the developer. " + + "To enable this feature, allow the app to post notifications." + ) + .setPositiveButton("OK") { _, _ -> + Log.i(TAG, "Launching request for permission.") + requestPermissionLauncher.launch(Manifest.permission.POST_NOTIFICATIONS) + } + .setNegativeButton("No thanks") { _, _ -> Log.i(TAG, "User denied permission request.") } + .show() + } else { + // Directly ask for the permission + requestPermissionLauncher.launch(Manifest.permission.POST_NOTIFICATIONS) + } + } else { + showFeedbackNotification() + } + } companion object { - private const val TAG = "AppDistribution-Quickstart" + private const val TAG = "KotlinMainActivity.kt" } } diff --git a/appdistribution/app/src/main/res/layout/activity_main.xml b/appdistribution/app/src/main/res/layout/activity_main.xml index 8debdf3264..2ae51a660b 100644 --- a/appdistribution/app/src/main/res/layout/activity_main.xml +++ b/appdistribution/app/src/main/res/layout/activity_main.xml @@ -5,7 +5,7 @@ android:layout_width="match_parent" android:layout_height="match_parent" android:padding="16dp" - tools:context=".MainActivity"> + tools:context=".kotlin.KotlinMainActivity"> +