diff --git a/appdistribution/README.md b/appdistribution/README.md index 86f878b3a..0519b4aee 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 9825af2c2..acc69c192 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 a29b19adf..4d9e2f30b 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 9d703fc5c..8275425fd 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 8debdf326..2ae51a660 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"> +