Skip to content
Draft
Show file tree
Hide file tree
Changes from 10 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -6,26 +6,27 @@ import dagger.hilt.android.scopes.ActivityScoped
import io.homeassistant.companion.android.BuildConfig
import io.homeassistant.companion.android.common.data.integration.DeviceRegistration
import io.homeassistant.companion.android.common.data.servers.ServerManager
import io.homeassistant.companion.android.onboarding.getMessagingToken
import io.homeassistant.companion.android.notifications.PushManager
import javax.inject.Inject
import kotlinx.coroutines.launch
import timber.log.Timber

@ActivityScoped
class LaunchPresenterImpl @Inject constructor(
@ActivityContext context: Context,
serverManager: ServerManager
) : LaunchPresenterBase(context as LaunchView, serverManager) {
serverManager: ServerManager,
pushManager: PushManager
) : LaunchPresenterBase(context as LaunchView, serverManager, pushManager) {
override fun resyncRegistration() {
if (!serverManager.isRegistered()) return
serverManager.defaultServers.forEach {
ioScope.launch {
try {
serverManager.integrationRepository(it.id).updateRegistration(
DeviceRegistration(
"${BuildConfig.VERSION_NAME} (${BuildConfig.VERSION_CODE})",
null,
getMessagingToken()
appVersion = "${BuildConfig.VERSION_NAME} (${BuildConfig.VERSION_CODE})",
deviceName = null,
pushToken = pushManager.getToken()
)
)
serverManager.integrationRepository(it.id).getConfig() // Update cached data
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,6 @@ package io.homeassistant.companion.android.notifications
import com.google.firebase.messaging.FirebaseMessagingService
import com.google.firebase.messaging.RemoteMessage
import dagger.hilt.android.AndroidEntryPoint
import io.homeassistant.companion.android.common.data.integration.DeviceRegistration
import io.homeassistant.companion.android.common.data.servers.ServerManager
import javax.inject.Inject
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
Expand All @@ -14,22 +12,16 @@ import timber.log.Timber

@AndroidEntryPoint
class FirebaseCloudMessagingService : FirebaseMessagingService() {
companion object {
private const val SOURCE = "FCM"
}

@Inject
lateinit var serverManager: ServerManager

@Inject
lateinit var messagingManager: MessagingManager
lateinit var pushProvider: FirebasePushProvider

private val mainScope: CoroutineScope = CoroutineScope(Dispatchers.Main + Job())

override fun onMessageReceived(remoteMessage: RemoteMessage) {
Timber.d("From: ${remoteMessage.from}")

messagingManager.handleMessage(remoteMessage.data, SOURCE)
pushProvider.onMessage(this, remoteMessage.data)
}

/**
Expand All @@ -39,23 +31,8 @@ class FirebaseCloudMessagingService : FirebaseMessagingService() {
*/
override fun onNewToken(token: String) {
mainScope.launch {
Timber.d("Refreshed token: $token")
if (!serverManager.isRegistered()) {
Timber.d("Not trying to update registration since we aren't authenticated.")
return@launch
}
serverManager.defaultServers.forEach {
launch {
try {
serverManager.integrationRepository(it.id).updateRegistration(
deviceRegistration = DeviceRegistration(pushToken = token),
allowReregistration = false
)
} catch (e: Exception) {
Timber.e(e, "Issue updating token")
}
}
}
pushProvider.setToken(token)
pushProvider.updateRegistration(this@FirebaseCloudMessagingService, mainScope)
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
package io.homeassistant.companion.android.notifications

import android.content.Context
import com.google.firebase.messaging.FirebaseMessaging
import io.homeassistant.companion.android.common.notifications.PushProvider
import io.homeassistant.companion.android.database.settings.PushProviderSetting
import javax.inject.Inject
import kotlinx.coroutines.sync.Mutex
import kotlinx.coroutines.sync.withLock
import kotlinx.coroutines.tasks.await
import timber.log.Timber

class FirebasePushProvider @Inject constructor(
private val messagingManager: MessagingManager
) : PushProvider {

companion object {
const val SOURCE = "FCM"
}

override val setting = PushProviderSetting.FCM

override fun isAvailable(context: Context): Boolean = true

private var token: String? = null
private val tokenMutex = Mutex()

suspend fun setToken(token: String) = tokenMutex.withLock {
this.token = token
}

override suspend fun getToken(): String {
return tokenMutex.withLock { token } ?: try {
FirebaseMessaging.getInstance().token.await()
} catch (e: Exception) {
Timber.e(e, "Issue getting token")
""
}
}

override fun onMessage(context: Context, notificationData: Map<String, String>) {
messagingManager.handleMessage(notificationData, SOURCE)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,8 @@ import io.homeassistant.companion.android.database.server.ServerSessionInfo
import io.homeassistant.companion.android.database.server.ServerType
import io.homeassistant.companion.android.database.server.ServerUserInfo
import io.homeassistant.companion.android.database.settings.WebsocketSetting
import io.homeassistant.companion.android.notifications.PushManager
import io.homeassistant.companion.android.onboarding.OnboardApp
import io.homeassistant.companion.android.onboarding.getMessagingToken
import io.homeassistant.companion.android.sensors.LocationSensorManager
import io.homeassistant.companion.android.settings.SettingViewModel
import io.homeassistant.companion.android.settings.server.ServerChooserFragment
Expand Down Expand Up @@ -54,6 +54,9 @@ class LaunchActivity : AppCompatActivity(), LaunchView {
@Inject
lateinit var sensorDao: SensorDao

@Inject
lateinit var pushManager: PushManager

private val mainScope = CoroutineScope(Dispatchers.Main + Job())

private val settingViewModel: SettingViewModel by viewModels()
Expand Down Expand Up @@ -138,7 +141,7 @@ class LaunchActivity : AppCompatActivity(), LaunchView {
mainScope.launch {
if (result != null) {
val (url, authCode, deviceName, deviceTrackingEnabled, notificationsEnabled) = result
val messagingToken = getMessagingToken()
val messagingToken = pushManager.getToken()
if (messagingToken.isBlank() && BuildConfig.FLAVOR == "full") {
AlertDialog.Builder(this@LaunchActivity)
.setTitle(commonR.string.firebase_error_title)
Expand Down Expand Up @@ -196,9 +199,9 @@ class LaunchActivity : AppCompatActivity(), LaunchView {
serverManager.authenticationRepository(serverId).registerAuthorizationCode(authCode)
serverManager.integrationRepository(serverId).registerDevice(
DeviceRegistration(
"${BuildConfig.VERSION_NAME} (${BuildConfig.VERSION_CODE})",
deviceName,
messagingToken
appVersion = "${BuildConfig.VERSION_NAME} (${BuildConfig.VERSION_CODE})",
deviceName = deviceName,
pushToken = messagingToken
)
)
serverId = serverManager.convertTemporaryServer(serverId)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package io.homeassistant.companion.android.launch

import io.homeassistant.companion.android.common.data.authentication.SessionState
import io.homeassistant.companion.android.common.data.servers.ServerManager
import io.homeassistant.companion.android.notifications.PushManager
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
Expand All @@ -10,7 +11,8 @@ import kotlinx.coroutines.launch

abstract class LaunchPresenterBase(
private val view: LaunchView,
internal val serverManager: ServerManager
internal val serverManager: ServerManager,
internal val pushManager: PushManager
) : LaunchPresenter {

internal val mainScope: CoroutineScope = CoroutineScope(Dispatchers.Main + Job())
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package io.homeassistant.companion.android.notifications

import io.homeassistant.companion.android.common.notifications.PushProvider
import javax.inject.Inject

class PushManager @Inject constructor(
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add documentation about the purpose of this class.

val providers: Map<Class<*>, @JvmSuppressWildcards PushProvider>
) {

val defaultProvider: PushProvider get() {
return providers[FirebasePushProvider::class.java]!!
}

/**
* Get the push url for the default push provider.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The function returns the token

*/
suspend fun getToken(): String {
return defaultProvider.getToken()
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package io.homeassistant.companion.android.notifications

import dagger.Binds
import dagger.Module
import dagger.hilt.InstallIn
import dagger.hilt.components.SingletonComponent
import dagger.multibindings.ClassKey
import dagger.multibindings.IntoMap
import io.homeassistant.companion.android.common.notifications.PushProvider
import javax.inject.Singleton

@Module
@InstallIn(SingletonComponent::class)
abstract class PushModule {
@Binds
@Singleton
@IntoMap
@ClassKey(FirebasePushProvider::class)
abstract fun bindFirebasePushProvider(firebasePushProvider: FirebasePushProvider): PushProvider
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import android.app.Application
import androidx.lifecycle.AndroidViewModel
import dagger.hilt.android.lifecycle.HiltViewModel
import io.homeassistant.companion.android.BuildConfig
import io.homeassistant.companion.android.database.settings.PushProviderSetting
import io.homeassistant.companion.android.database.settings.SensorUpdateFrequencySetting
import io.homeassistant.companion.android.database.settings.Setting
import io.homeassistant.companion.android.database.settings.SettingsDao
Expand All @@ -20,7 +21,12 @@ class SettingViewModel @Inject constructor(
fun getSetting(id: Int): Setting {
var setting = settingsDao.get(id)
if (setting == null) {
setting = Setting(id, if (BuildConfig.FLAVOR == "full") WebsocketSetting.NEVER else WebsocketSetting.ALWAYS, SensorUpdateFrequencySetting.NORMAL)
setting = Setting(
id,
if (BuildConfig.FLAVOR == "full") WebsocketSetting.NEVER else WebsocketSetting.ALWAYS,
SensorUpdateFrequencySetting.NORMAL,
if (BuildConfig.FLAVOR == "full") PushProviderSetting.FCM else PushProviderSetting.NONE
)
settingsDao.insert(setting)
}
return setting
Expand All @@ -42,4 +48,11 @@ class SettingViewModel @Inject constructor(
settingsDao.update(it)
}
}

fun updatePushProviderSetting(serverId: Int, setting: PushProviderSetting) {
settingsDao.get(serverId)?.let {
it.pushProvider = setting
settingsDao.update(it)
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -22,12 +22,13 @@ import io.homeassistant.companion.android.database.server.ServerConnectionInfo
import io.homeassistant.companion.android.database.server.ServerSessionInfo
import io.homeassistant.companion.android.database.server.ServerType
import io.homeassistant.companion.android.database.server.ServerUserInfo
import io.homeassistant.companion.android.database.settings.PushProviderSetting
import io.homeassistant.companion.android.database.settings.SensorUpdateFrequencySetting
import io.homeassistant.companion.android.database.settings.Setting
import io.homeassistant.companion.android.database.settings.SettingsDao
import io.homeassistant.companion.android.database.settings.WebsocketSetting
import io.homeassistant.companion.android.notifications.PushManager
import io.homeassistant.companion.android.onboarding.OnboardApp
import io.homeassistant.companion.android.onboarding.getMessagingToken
import io.homeassistant.companion.android.sensors.LocationSensorManager
import io.homeassistant.companion.android.settings.language.LanguagesManager
import io.homeassistant.companion.android.themes.ThemesManager
Expand All @@ -50,6 +51,7 @@ class SettingsPresenterImpl @Inject constructor(
private val prefsRepository: PrefsRepository,
private val themesManager: ThemesManager,
private val langsManager: LanguagesManager,
private val pushManager: PushManager,
private val changeLog: ChangeLog,
private val settingsDao: SettingsDao,
private val sensorDao: SensorDao
Expand Down Expand Up @@ -162,7 +164,7 @@ class SettingsPresenterImpl @Inject constructor(
override suspend fun addServer(result: OnboardApp.Output?) {
if (result != null) {
val (url, authCode, deviceName, deviceTrackingEnabled, notificationsEnabled) = result
val messagingToken = getMessagingToken()
val messagingToken = pushManager.getToken()
var serverId: Int? = null
try {
val formattedUrl = UrlUtil.formattedUrlString(url)
Expand All @@ -179,9 +181,9 @@ class SettingsPresenterImpl @Inject constructor(
serverManager.authenticationRepository(serverId).registerAuthorizationCode(authCode)
serverManager.integrationRepository(serverId).registerDevice(
DeviceRegistration(
"${BuildConfig.VERSION_NAME} (${BuildConfig.VERSION_CODE})",
deviceName,
messagingToken
appVersion = "${BuildConfig.VERSION_NAME} (${BuildConfig.VERSION_CODE})",
deviceName = deviceName,
pushToken = messagingToken
)
)
serverManager.getServer()?.id?.let {
Expand Down Expand Up @@ -228,7 +230,8 @@ class SettingsPresenterImpl @Inject constructor(
Setting(
serverId,
if (enabled) WebsocketSetting.ALWAYS else WebsocketSetting.NEVER,
SensorUpdateFrequencySetting.NORMAL
SensorUpdateFrequencySetting.NORMAL,
PushProviderSetting.NONE
)
)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,22 +3,24 @@ package io.homeassistant.companion.android.launch
import io.homeassistant.companion.android.BuildConfig
import io.homeassistant.companion.android.common.data.integration.DeviceRegistration
import io.homeassistant.companion.android.common.data.servers.ServerManager
import io.homeassistant.companion.android.notifications.PushManager
import javax.inject.Inject
import kotlinx.coroutines.launch
import timber.log.Timber

class LaunchPresenterImpl @Inject constructor(
view: LaunchView,
serverManager: ServerManager
) : LaunchPresenterBase(view, serverManager) {
serverManager: ServerManager,
pushManager: PushManager
) : LaunchPresenterBase(view, serverManager, pushManager) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Now that you have an abstraction you could potentially already remove the two version of the LaunchPresenterImpl and keep only the one from full. Let's avoid as much as possible duplicates. It makes maintenance of minimal easier.

override fun resyncRegistration() {
if (!serverManager.isRegistered()) return
serverManager.defaultServers.forEach {
ioScope.launch {
try {
serverManager.integrationRepository(it.id).updateRegistration(
DeviceRegistration(
"${BuildConfig.VERSION_NAME} (${BuildConfig.VERSION_CODE})"
appVersion = "${BuildConfig.VERSION_NAME} (${BuildConfig.VERSION_CODE})"
)
)
serverManager.integrationRepository(it.id).getConfig() // Update cached data
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
package io.homeassistant.companion.android.notifications

import android.content.Context
import io.homeassistant.companion.android.common.notifications.PushProvider
import io.homeassistant.companion.android.database.settings.PushProviderSetting
import javax.inject.Inject
import kotlinx.coroutines.CoroutineScope

class FirebasePushProvider @Inject constructor() : PushProvider {

// Firebase Cloud Messaging depends on Google Play Services,
// and as a result FCM is not supported with the minimal flavor

companion object {
const val SOURCE = "FCM"
}

override val setting = PushProviderSetting.FCM

override fun isAvailable(context: Context): Boolean = false

override fun isEnabled(context: Context): Boolean = false

override fun isEnabled(context: Context, serverId: Int): Boolean = false

override fun getEnabledServers(context: Context): Set<Int> = emptySet()

override suspend fun getToken(): String = ""

override fun onMessage(context: Context, notificationData: Map<String, String>) {}

override suspend fun updateRegistration(context: Context, coroutineScope: CoroutineScope) {}
}
Loading