From b826352ce7cb2580b818d892b692bfcb10753c61 Mon Sep 17 00:00:00 2001 From: lisa-signal Date: Wed, 18 Jun 2025 11:13:33 -0400 Subject: [PATCH] Add deprecation notification when build expires. --- .../jobs/DeprecatedNotificationJob.kt | 81 +++++++++++++++++++ .../securesms/jobs/JobManagerFactories.java | 3 +- .../securesms/keyvalue/MiscellaneousValues.kt | 12 ++- app/src/main/res/values/strings.xml | 5 ++ 4 files changed, 98 insertions(+), 3 deletions(-) create mode 100644 app/src/main/java/org/thoughtcrime/securesms/jobs/DeprecatedNotificationJob.kt diff --git a/app/src/main/java/org/thoughtcrime/securesms/jobs/DeprecatedNotificationJob.kt b/app/src/main/java/org/thoughtcrime/securesms/jobs/DeprecatedNotificationJob.kt new file mode 100644 index 0000000000..7a51023915 --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/jobs/DeprecatedNotificationJob.kt @@ -0,0 +1,81 @@ +/* + * Copyright 2025 Signal Messenger, LLC + * SPDX-License-Identifier: AGPL-3.0-only + */ + +package org.thoughtcrime.securesms.jobs + +import android.app.PendingIntent +import android.content.Intent +import android.net.Uri +import androidx.core.app.NotificationCompat +import org.signal.core.util.logging.Log +import org.thoughtcrime.securesms.BuildConfig +import org.thoughtcrime.securesms.R +import org.thoughtcrime.securesms.dependencies.AppDependencies +import org.thoughtcrime.securesms.jobmanager.Job +import org.thoughtcrime.securesms.notifications.NotificationChannels +import org.thoughtcrime.securesms.notifications.NotificationIds +import org.thoughtcrime.securesms.util.ServiceUtil +import kotlin.time.Duration.Companion.days + +/** + * Notifies users that their build expired and redirects to the download page on click. + */ +class DeprecatedNotificationJob private constructor(parameters: Parameters) : Job(parameters) { + companion object { + const val KEY: String = "DeprecatedNotificationJob" + private val TAG = Log.tag(DeprecatedNotificationJob::class.java) + + @JvmStatic + fun enqueue() { + AppDependencies.jobManager.add(DeprecatedNotificationJob()) + } + } + + private constructor() : this( + Parameters.Builder() + .setQueue("DeprecatedNotificationJob") + .setLifespan(7.days.inWholeMilliseconds) + .setMaxAttempts(Parameters.UNLIMITED) + .build() + ) + + override fun serialize(): ByteArray? = null + + override fun getFactoryKey(): String = KEY + + override fun run(): Result { + if (NotificationChannels.getInstance().areNotificationsEnabled()) { + val intent: Intent + + if (BuildConfig.MANAGES_APP_UPDATES) { + Log.d(TAG, "Showing deprecated notification for website APK") + intent = Intent(Intent.ACTION_VIEW, Uri.parse("https://signal.org/android/apk")) + } else { + Log.d(TAG, "Showing deprecated notification for PlayStore") + val packageName = context.packageName + intent = Intent(Intent.ACTION_VIEW, Uri.parse("market://details?id=$packageName")) + } + + val pendingIntent = PendingIntent.getActivity(context, 0, intent, PendingIntent.FLAG_IMMUTABLE) + val builder = NotificationCompat.Builder(context, NotificationChannels.getInstance().APP_ALERTS) + .setSmallIcon(R.drawable.ic_notification) + .setContentTitle(context.getString(R.string.DeprecatedNotificationJob_update_signal)) + .setContentText(context.getString(R.string.DeprecatedNotificationJob_this_version_of_signal_has_expired)) + .setContentIntent(pendingIntent) + + ServiceUtil.getNotificationManager(context).notify(NotificationIds.APK_UPDATE_PROMPT_INSTALL, builder.build()) + } + + return Result.success() + } + + override fun onFailure() = Unit + + class Factory : Job.Factory { + override fun create(parameters: Parameters, serializedData: ByteArray?): DeprecatedNotificationJob { + return DeprecatedNotificationJob(parameters) + } + } +} diff --git a/app/src/main/java/org/thoughtcrime/securesms/jobs/JobManagerFactories.java b/app/src/main/java/org/thoughtcrime/securesms/jobs/JobManagerFactories.java index 59dbc17b8a..af69b07e14 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/jobs/JobManagerFactories.java +++ b/app/src/main/java/org/thoughtcrime/securesms/jobs/JobManagerFactories.java @@ -151,7 +151,7 @@ public final class JobManagerFactories { put(CopyAttachmentToArchiveJob.KEY, new CopyAttachmentToArchiveJob.Factory()); put(CreateReleaseChannelJob.KEY, new CreateReleaseChannelJob.Factory()); put(DeleteAbandonedAttachmentsJob.KEY, new DeleteAbandonedAttachmentsJob.Factory()); - put(NewLinkedDeviceNotificationJob.KEY, new NewLinkedDeviceNotificationJob.Factory()); + put(DeprecatedNotificationJob.KEY, new DeprecatedNotificationJob.Factory()); put(DeviceNameChangeJob.KEY, new DeviceNameChangeJob.Factory()); put(DirectoryRefreshJob.KEY, new DirectoryRefreshJob.Factory()); put(DownloadLatestEmojiDataJob.KEY, new DownloadLatestEmojiDataJob.Factory()); @@ -207,6 +207,7 @@ public final class JobManagerFactories { put(MultiDeviceVerifiedUpdateJob.KEY, new MultiDeviceVerifiedUpdateJob.Factory()); put(MultiDeviceViewOnceOpenJob.KEY, new MultiDeviceViewOnceOpenJob.Factory()); put(MultiDeviceViewedUpdateJob.KEY, new MultiDeviceViewedUpdateJob.Factory()); + put(NewLinkedDeviceNotificationJob.KEY, new NewLinkedDeviceNotificationJob.Factory()); put(NullMessageSendJob.KEY, new NullMessageSendJob.Factory()); put(OptimizeMediaJob.KEY, new OptimizeMediaJob.Factory()); put(OptimizeMessageSearchIndexJob.KEY, new OptimizeMessageSearchIndexJob.Factory()); diff --git a/app/src/main/java/org/thoughtcrime/securesms/keyvalue/MiscellaneousValues.kt b/app/src/main/java/org/thoughtcrime/securesms/keyvalue/MiscellaneousValues.kt index bc18296fd0..a61de8e499 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/keyvalue/MiscellaneousValues.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/keyvalue/MiscellaneousValues.kt @@ -3,6 +3,7 @@ package org.thoughtcrime.securesms.keyvalue import org.thoughtcrime.securesms.components.settings.app.usernamelinks.UsernameQrCodeColorScheme import org.thoughtcrime.securesms.database.model.databaseprotos.PendingChangeNumberMetadata import org.thoughtcrime.securesms.jobmanager.impl.ChangeNumberConstraintObserver +import org.thoughtcrime.securesms.jobs.DeprecatedNotificationJob import org.thoughtcrime.securesms.keyvalue.protos.LeastActiveLinkedDevice class MiscellaneousValues internal constructor(store: KeyValueStore) : SignalStoreValues(store) { @@ -69,9 +70,16 @@ class MiscellaneousValues internal constructor(store: KeyValueStore) : SignalSto var lastProfileRefreshTime by longValue(LAST_PROFILE_REFRESH_TIME, 0) /** - * Whether or not the client is currently in a 'deprecated' state, disallowing network access. + * Whether or not the client is currently in a 'deprecated' state, disallowing network access. Send a notification if the client changes from not deprecated to deprecated state. */ - var isClientDeprecated: Boolean by booleanValue(CLIENT_DEPRECATED, false) + var isClientDeprecated: Boolean + get() = getBoolean(CLIENT_DEPRECATED, false) + set(isDeprecated) { + if (isDeprecated && !isClientDeprecated) { + DeprecatedNotificationJob.enqueue() + } + putBoolean(CLIENT_DEPRECATED, isDeprecated) + } /** * Whether or not we've locked the device after they've transferred to a new one. diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index edab413f01..825e0e3862 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -1177,6 +1177,11 @@ This version of Signal has expired. Update now to send and receive messages. Update now + + Update Signal + + This version of Signal has expired. Tap to update to send and receive messages. + %d pending member request.