Process push notification to display auth request notification (#919)

This commit is contained in:
David Perez 2024-01-31 21:02:12 -06:00 committed by Álison Fernandes
parent 77913805ab
commit ce3066dd54
7 changed files with 155 additions and 1 deletions

View file

@ -44,6 +44,10 @@
<data android:mimeType="video/*" />
<data android:mimeType="text/*" />
</intent-filter>
<intent-filter>
<action android:name="com.x8bit.bitwarden.data.auth.manager.AUTH_REQUEST" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</activity>
<activity

View file

@ -1,6 +1,7 @@
package com.x8bit.bitwarden
import android.app.Application
import com.x8bit.bitwarden.data.auth.manager.AuthRequestNotificationManager
import com.x8bit.bitwarden.data.platform.manager.CrashLogsManager
import com.x8bit.bitwarden.data.platform.manager.NetworkConfigManager
import dagger.hilt.android.HiltAndroidApp
@ -18,4 +19,7 @@ class BitwardenApplication : Application() {
@Inject
lateinit var crashLogsManager: CrashLogsManager
@Inject
lateinit var authRequestNotificationManager: AuthRequestNotificationManager
}

View file

@ -0,0 +1,6 @@
package com.x8bit.bitwarden.data.auth.manager
/**
* Manages the auth request push notifications and handles displaying notifications.
*/
interface AuthRequestNotificationManager

View file

@ -0,0 +1,109 @@
package com.x8bit.bitwarden.data.auth.manager
import android.annotation.SuppressLint
import android.app.PendingIntent
import android.content.Context
import android.content.Intent
import androidx.compose.ui.graphics.Color
import androidx.core.app.NotificationChannelCompat
import androidx.core.app.NotificationCompat
import androidx.core.app.NotificationManagerCompat
import com.x8bit.bitwarden.MainActivity
import com.x8bit.bitwarden.R
import com.x8bit.bitwarden.data.auth.datasource.disk.AuthDiskSource
import com.x8bit.bitwarden.data.autofill.util.toPendingIntentMutabilityFlag
import com.x8bit.bitwarden.data.platform.annotation.OmitFromCoverage
import com.x8bit.bitwarden.data.platform.manager.PushManager
import com.x8bit.bitwarden.data.platform.manager.dispatcher.DispatcherManager
import com.x8bit.bitwarden.data.platform.manager.model.PasswordlessRequestData
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach
/**
* The default implementation of the [AuthRequestNotificationManager].
*/
@OmitFromCoverage
class AuthRequestNotificationManagerImpl(
private val context: Context,
private val authDiskSource: AuthDiskSource,
pushManager: PushManager,
dispatchers: DispatcherManager,
) : AuthRequestNotificationManager {
private val ioScope = CoroutineScope(dispatchers.io)
init {
pushManager
.passwordlessRequestFlow
.onEach(::handlePasswordlessRequestData)
.launchIn(ioScope)
}
@SuppressLint("MissingPermission")
private fun handlePasswordlessRequestData(data: PasswordlessRequestData) {
val notificationManager = NotificationManagerCompat.from(context)
if (notificationManager.areNotificationsEnabled(NOTIFICATION_CHANNEL_ID)) return
// Construct the channel, calling this more than once is safe
notificationManager.createNotificationChannel(
NotificationChannelCompat
.Builder(
NOTIFICATION_CHANNEL_ID,
NotificationManagerCompat.IMPORTANCE_DEFAULT,
)
.setName(context.getString(R.string.pending_log_in_requests))
.build(),
)
// Create the notification
val builder = NotificationCompat.Builder(context, NOTIFICATION_CHANNEL_ID)
.setContentIntent(createContentIntent(data))
.setContentTitle(context.getString(R.string.log_in_requested))
.setContentText(
authDiskSource
.userState
?.accounts
?.get(data.userId)
?.profile
?.email
?.let { context.getString(R.string.confim_log_in_attemp_for_x, it) }
?: context.getString(R.string.confirm_log_in),
)
.setSmallIcon(R.drawable.ic_launcher_foreground)
.setColor(Color.White.value.toInt())
.setAutoCancel(true)
.setTimeoutAfter(NOTIFICATION_DEFAULT_TIMEOUT_MILLIS)
notificationManager.notify(NOTIFICATION_ID, builder.build())
}
private fun createContentIntent(data: PasswordlessRequestData): PendingIntent =
PendingIntent.getActivity(
context,
NOTIFICATION_REQUEST_CODE,
Intent(context, MainActivity::class.java)
.setAction(NOTIFICATION_ACTION)
.putExtra(NOTIFICATION_DATA, data),
PendingIntent.FLAG_UPDATE_CURRENT.toPendingIntentMutabilityFlag(),
)
private fun NotificationManagerCompat.areNotificationsEnabled(
channelId: String,
): Boolean = areNotificationsEnabled() && isChannelEnabled(channelId)
private fun NotificationManagerCompat.isChannelEnabled(
channelId: String,
): Boolean = getChannelImportance(channelId) != NotificationManagerCompat.IMPORTANCE_NONE
private fun NotificationManagerCompat.getChannelImportance(
channelId: String,
): Int = this
.getNotificationChannelCompat(channelId)
?.importance
?: NotificationManagerCompat.IMPORTANCE_DEFAULT
}
const val NOTIFICATION_ACTION: String = "com.x8bit.bitwarden.data.auth.manager.AUTH_REQUEST"
private const val NOTIFICATION_CHANNEL_ID: String = "general_notification_channel"
private const val NOTIFICATION_ID: Int = 2_6072_022
private const val NOTIFICATION_DATA: String = "notificationData"
private const val NOTIFICATION_REQUEST_CODE: Int = 20220801
private const val NOTIFICATION_DEFAULT_TIMEOUT_MILLIS: Long = 15L * 60L * 1_000L

View file

@ -1,10 +1,14 @@
package com.x8bit.bitwarden.data.auth.manager.di
import android.content.Context
import com.x8bit.bitwarden.data.auth.datasource.disk.AuthDiskSource
import com.x8bit.bitwarden.data.auth.manager.AuthRequestNotificationManager
import com.x8bit.bitwarden.data.auth.manager.AuthRequestNotificationManagerImpl
import com.x8bit.bitwarden.data.auth.manager.UserLogoutManager
import com.x8bit.bitwarden.data.auth.manager.UserLogoutManagerImpl
import com.x8bit.bitwarden.data.platform.datasource.disk.PushDiskSource
import com.x8bit.bitwarden.data.platform.datasource.disk.SettingsDiskSource
import com.x8bit.bitwarden.data.platform.manager.PushManager
import com.x8bit.bitwarden.data.platform.manager.dispatcher.DispatcherManager
import com.x8bit.bitwarden.data.tools.generator.datasource.disk.GeneratorDiskSource
import com.x8bit.bitwarden.data.tools.generator.datasource.disk.PasswordHistoryDiskSource
@ -12,6 +16,7 @@ import com.x8bit.bitwarden.data.vault.datasource.disk.VaultDiskSource
import dagger.Module
import dagger.Provides
import dagger.hilt.InstallIn
import dagger.hilt.android.qualifiers.ApplicationContext
import dagger.hilt.components.SingletonComponent
import javax.inject.Singleton
@ -22,6 +27,21 @@ import javax.inject.Singleton
@InstallIn(SingletonComponent::class)
object AuthManagerModule {
@Provides
@Singleton
fun provideAuthRequestNotificationManager(
@ApplicationContext context: Context,
authDiskSource: AuthDiskSource,
pushManager: PushManager,
dispatchers: DispatcherManager,
): AuthRequestNotificationManager =
AuthRequestNotificationManagerImpl(
context = context,
authDiskSource = authDiskSource,
pushManager = pushManager,
dispatchers = dispatchers,
)
@Provides
@Singleton
fun provideUserLogoutManager(

View file

@ -1,12 +1,16 @@
package com.x8bit.bitwarden.data.platform.manager.model
import android.os.Parcelable
import kotlinx.parcelize.Parcelize
/**
* Required data for passwordless requests.
*
* @property loginRequestId The login request ID.
* @property userId The user ID.
*/
@Parcelize
data class PasswordlessRequestData(
val loginRequestId: String,
val userId: String,
)
) : Parcelable

View file

@ -3,6 +3,13 @@
xmlns:android="http://schemas.android.com/apk/res/android">
<application tools:ignore="MissingApplicationIcon">
<service
android:name="com.x8bit.bitwarden.data.push.BitwardenFirebaseMessagingService"
android:exported="false">
<intent-filter>
<action android:name="com.google.firebase.MESSAGING_EVENT" />
</intent-filter>
</service>
<!-- Disable Crashlytics for debug builds -->
<meta-data
android:name="firebase_crashlytics_collection_enabled"