Skip to content

Commit

Permalink
[i224] Protect deleteDevice from repeated usage
Browse files Browse the repository at this point in the history
  • Loading branch information
kanat authored and JcMinarro committed Dec 22, 2023
1 parent b2478bc commit e9a7f0d
Show file tree
Hide file tree
Showing 3 changed files with 78 additions and 17 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -34,8 +34,11 @@ import io.getstream.chat.android.client.api.models.QueryChannelRequest
import io.getstream.chat.android.client.api.models.QueryChannelsRequest
import io.getstream.chat.android.client.api.models.QueryUsersRequest
import io.getstream.chat.android.client.api.models.SendActionRequest
import io.getstream.chat.android.client.api.models.identifier.AddDeviceIdentifier
import io.getstream.chat.android.client.api.models.identifier.DeleteDeviceIdentifier
import io.getstream.chat.android.client.api.models.identifier.DeleteMessageIdentifier
import io.getstream.chat.android.client.api.models.identifier.DeleteReactionIdentifier
import io.getstream.chat.android.client.api.models.identifier.GetDevicesIdentifier
import io.getstream.chat.android.client.api.models.identifier.GetMessageIdentifier
import io.getstream.chat.android.client.api.models.identifier.GetRepliesIdentifier
import io.getstream.chat.android.client.api.models.identifier.GetRepliesMoreIdentifier
Expand Down Expand Up @@ -1364,16 +1367,19 @@ internal constructor(
@CheckResult
public fun getDevices(): Call<List<Device>> {
return api.getDevices()
.share(userScope) { GetDevicesIdentifier() }
}

@CheckResult
public fun deleteDevice(device: Device): Call<Unit> {
return api.deleteDevice(device)
.share(userScope) { DeleteDeviceIdentifier(device) }
}

@CheckResult
public fun addDevice(device: Device): Call<Unit> {
return api.addDevice(device)
.share(userScope) { AddDeviceIdentifier(device) }
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import io.getstream.chat.android.client.ChatClient
import io.getstream.chat.android.client.api.models.QueryChannelRequest
import io.getstream.chat.android.client.api.models.QueryChannelsRequest
import io.getstream.chat.android.client.api.models.SendActionRequest
import io.getstream.chat.android.models.Device
import io.getstream.chat.android.models.FilterObject
import io.getstream.chat.android.models.Member
import io.getstream.chat.android.models.Message
Expand Down Expand Up @@ -274,3 +275,35 @@ internal fun SendMessageIdentifier(
result = 31 * result + messageId.hashCode()
return result
}

/**
* Identifier for a [ChatClient.getDevices] call.
*/
@Suppress("FunctionName", "FunctionOnlyReturningConstant")
internal fun GetDevicesIdentifier(): Int {
return "GetDevices".hashCode()
}

/**
* Identifier for a [ChatClient.addDevice] call.
*/
@Suppress("FunctionName", "FunctionOnlyReturningConstant")
internal fun AddDeviceIdentifier(
device: Device,
): Int {
var result = "AddDevice".hashCode()
result = 31 * result + device.hashCode()
return result
}

/**
* Identifier for a [ChatClient.deleteDevice] call.
*/
@Suppress("FunctionName", "FunctionOnlyReturningConstant")
internal fun DeleteDeviceIdentifier(
device: Device,
): Int {
var result = "DeleteDevice".hashCode()
result = 31 * result + device.hashCode()
return result
}
Original file line number Diff line number Diff line change
Expand Up @@ -21,19 +21,24 @@ import android.content.SharedPreferences
import androidx.core.content.edit
import io.getstream.chat.android.client.ChatClient
import io.getstream.chat.android.client.extensions.getNonNullString
import io.getstream.chat.android.core.utils.Debouncer
import io.getstream.chat.android.models.Device
import io.getstream.chat.android.models.PushProvider
import io.getstream.log.taggedLogger
import io.getstream.result.Result

internal class PushTokenUpdateHandler(context: Context) {
private val logger by taggedLogger("Chat:Notifications")
private val logger by taggedLogger("Chat:Notifications-UH")

private val prefs: SharedPreferences = context.applicationContext.getSharedPreferences(
PREFS_NAME,
Context.MODE_PRIVATE,
)

private val chatClient: ChatClient get() = ChatClient.instance()

private val updateDebouncer = Debouncer(DEBOUNCE_TIMEOUT)
private val deleteDebouncer = Debouncer(DEBOUNCE_TIMEOUT)

private var userPushToken: UserPushToken
set(value) {
prefs.edit(true) {
Expand All @@ -58,26 +63,42 @@ internal class PushTokenUpdateHandler(context: Context) {
*/
suspend fun updateDeviceIfNecessary(device: Device) {
val userPushToken = device.toUserPushToken()
if (device.isValid() && this.userPushToken != userPushToken) {
removeStoredDevice()
when (val result = ChatClient.instance().addDevice(device).await()) {
is Result.Success -> {
this.userPushToken = userPushToken
logger.i { "Device registered with token ${device.token} (${device.pushProvider.key})" }
}
is Result.Failure -> logger.e { "Error registering device ${result.value.message}" }
if (!device.isValid()) return
if (this.userPushToken == userPushToken) return
updateDebouncer.submitSuspendable {
logger.d { "[updateDeviceIfNecessary] device: $device" }
val removed = removeStoredDeviceInternal()
logger.v { "[updateDeviceIfNecessary] removed: $removed" }
val result = chatClient.addDevice(device).await()
if (result.isSuccess) {
this.userPushToken = userPushToken
val pushProvider = device.pushProvider.key
logger.i { "[updateDeviceIfNecessary] device registered with token($pushProvider): ${device.token}" }
} else {
logger.e { "[updateDeviceIfNecessary] failed registering device ${result.errorOrNull()?.message}" }
}
}
}

suspend fun removeStoredDevice() {
userPushToken.toDevice()
deleteDebouncer.submitSuspendable {
logger.v { "[removeStoredDevice] no args" }
val removed = removeStoredDeviceInternal()
logger.i { "[removeStoredDevice] removed: $removed" }
}
}

private suspend fun removeStoredDeviceInternal(): Boolean {
val device = userPushToken.toDevice()
.takeIf { it.isValid() }
?.let {
if (ChatClient.instance().deleteDevice(it).await() is Result.Success) {
userPushToken = UserPushToken("", "", "", null)
}
}
?: return false
val result = chatClient.deleteDevice(device).await()
if (result.isSuccess) {
userPushToken = UserPushToken("", "", "", null)
return true
}
logger.e { "[removeStoredDeviceInternal] failed: ${result.errorOrNull()}" }
return false
}

private data class UserPushToken(
Expand All @@ -93,10 +114,11 @@ internal class PushTokenUpdateHandler(context: Context) {
private const val KEY_TOKEN = "token"
private const val KEY_PUSH_PROVIDER = "push_provider"
private const val KEY_PUSH_PROVIDER_NAME = "push_provider_name"
private const val DEBOUNCE_TIMEOUT = 200L
}

private fun Device.toUserPushToken() = UserPushToken(
userId = ChatClient.instance().getCurrentUser()?.id ?: "",
userId = chatClient.getCurrentUser()?.id ?: "",
token = token,
pushProvider = pushProvider.key,
providerName = providerName,
Expand Down

0 comments on commit e9a7f0d

Please sign in to comment.