Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merge UnifiedPush support into main app #368

Draft
wants to merge 1 commit into
base: molly-7.19
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all 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
10 changes: 10 additions & 0 deletions app/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,10 @@ android {
getByName("androidTest") {
java.srcDir("$projectDir/src/testShared")
}

getByName("main") {
java.srcDir("$projectDir/src/unifiedpush/java")
}
}

compileOptions {
Expand Down Expand Up @@ -530,6 +534,12 @@ dependencies {
implementation(libs.molly.glide.webp.decoder)
implementation(libs.gosimple.nbvcxz)
"fossImplementation"("org.osmdroid:osmdroid-android:6.1.16")
implementation(libs.unifiedpush.connector) {
exclude(group = "org.jetbrains.kotlin", module = "kotlin-stdlib")
}
implementation(libs.unifiedpush.connector.ui) {
exclude(group = "org.jetbrains.kotlin", module = "kotlin-stdlib")
}

"gmsImplementation"(project(":billing"))

Expand Down
1 change: 1 addition & 0 deletions app/proguard/proguard.cfg
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
-dontobfuscate
-keepattributes SourceFile,LineNumberTable
-keep class org.whispersystems.** { *; }
-keep class im.molly.** { *; }
-keep class org.signal.libsignal.net.** { *; }
-keep class org.signal.libsignal.protocol.** { *; }
-keep class org.signal.libsignal.usernames.** { *; }
Expand Down
12 changes: 12 additions & 0 deletions app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -1316,6 +1316,18 @@
</intent-filter>
</receiver>

<receiver
android:name="im.molly.unifiedpush.receiver.UnifiedPushReceiver"
android:enabled="true"
android:exported="true">
<intent-filter>
<action android:name="org.unifiedpush.android.connector.MESSAGE" />
<action android:name="org.unifiedpush.android.connector.UNREGISTERED" />
<action android:name="org.unifiedpush.android.connector.NEW_ENDPOINT" />
<action android:name="org.unifiedpush.android.connector.REGISTRATION_FAILED" />
</intent-filter>
</receiver>

<service
android:name=".gcm.FcmJobService"
android:permission="android.permission.BIND_JOB_SERVICE"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -121,13 +121,15 @@
import org.thoughtcrime.securesms.util.Util;
import org.thoughtcrime.securesms.util.dynamiclanguage.DynamicLanguageContextWrapper;

import im.molly.unifiedpush.util.UnifiedPushHelper;
import java.net.SocketException;
import java.net.SocketTimeoutException;
import java.security.Security;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;

import im.molly.unifiedpush.jobs.UnifiedPushRefreshJob;
import io.reactivex.rxjava3.exceptions.OnErrorNotImplementedException;
import io.reactivex.rxjava3.exceptions.UndeliverableException;
import io.reactivex.rxjava3.plugins.RxJavaPlugins;
Expand Down Expand Up @@ -507,16 +509,20 @@ public void updatePushNotificationServices() {
PlayServicesUtil.PlayServicesStatus fcmStatus = PlayServicesUtil.getPlayServicesStatus(this);

boolean fcmEnabled = SignalStore.account().isFcmEnabled();
boolean unifiedPushEnabled = SignalStore.unifiedpush().isEnabled();
boolean forceWebSocket = SignalStore.internal().isWebsocketModeForced();

if (forceWebSocket || fcmStatus == PlayServicesUtil.PlayServicesStatus.DISABLED) {
if (unifiedPushEnabled || forceWebSocket || fcmStatus == PlayServicesUtil.PlayServicesStatus.DISABLED) {
if (fcmEnabled) {
Log.i(TAG, "Play Services are disabled. Disabling FCM.");
updateFcmStatus(false);
} else {
Log.d(TAG, "FCM is disabled.");
SignalStore.account().setFcmTokenLastSetTime(-1);
}
if (unifiedPushEnabled) {
AppDependencies.getJobManager().add(new UnifiedPushRefreshJob());
}
} else if (fcmStatus == PlayServicesUtil.PlayServicesStatus.SUCCESS && !fcmEnabled &&
SignalStore.account().getFcmTokenLastSetTime() < 0) {
Log.i(TAG, "Play Services are newly-available. Updating to use FCM.");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,8 @@
import org.thoughtcrime.securesms.util.TextSecurePreferences;
import org.thoughtcrime.securesms.util.WindowUtil;

import static im.molly.unifiedpush.util.UnifiedPushHelper.registerAppWithDialogIfNeeded;

public class MainActivity extends PassphraseRequiredActivity implements VoiceNoteMediaControllerOwner {

public static final int RESULT_CONFIG_CHANGED = Activity.RESULT_FIRST_USER + 901;
Expand Down Expand Up @@ -109,6 +111,7 @@ public boolean onPreDraw() {
.getVitalsState()
.subscribe(this::presentVitalsState)
);
registerAppWithDialogIfNeeded(this);
}

@SuppressLint("NewApi")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -219,6 +219,22 @@ class InternalSettingsFragment : DSLSettingsFragment(R.string.preferences__inter
}
)

clickPref(
title = DSLSettingsText.from("Delete UnifiedPush ping"),
summary = DSLSettingsText.from("Make as Molly never received the ping from MollySocket. Will cause UnifiedPush to stop and Websocket to restart."),
onClick = {
MaterialAlertDialogBuilder(requireContext())
.setTitle("Delete UnifiedPush ping?")
.setMessage("Are you sure?")
.setPositiveButton(android.R.string.ok) { _, _ ->
SignalStore.unifiedpush.pinged = false
Toast.makeText(requireContext(), "UnifiedPush ping deleted!", Toast.LENGTH_SHORT).show()
}
.setNegativeButton(android.R.string.cancel, null)
.show()
}
)

dividerPref()

sectionHeaderPref(DSLSettingsText.from("Logging"))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ import org.thoughtcrime.securesms.keyvalue.SignalStore
import org.thoughtcrime.securesms.notifications.NotificationChannels
import org.thoughtcrime.securesms.notifications.TurnOnNotificationsBottomSheet
import org.thoughtcrime.securesms.util.BottomSheetUtil
import org.thoughtcrime.securesms.util.CommunicationActions
import org.thoughtcrime.securesms.util.PlayServicesUtil
import org.thoughtcrime.securesms.util.RingtoneUtil
import org.thoughtcrime.securesms.util.SecurePreferenceManager
Expand Down Expand Up @@ -347,6 +348,7 @@ class NotificationsSettingsFragment : DSLSettingsFragment(R.string.preferences__
val showAlertIcon = when (state.preferredNotificationMethod) {
NotificationDeliveryMethod.FCM -> !state.canReceiveFcm
NotificationDeliveryMethod.WEBSOCKET -> false
NotificationDeliveryMethod.UNIFIEDPUSH -> !state.canReceiveUnifiedPush
}
radioListPref(
title = DSLSettingsText.from(R.string.NotificationsSettingsFragment__delivery_method),
Expand All @@ -358,6 +360,14 @@ class NotificationsSettingsFragment : DSLSettingsFragment(R.string.preferences__
onNotificationMethodChanged(notificationMethodValues[it], state.preferredNotificationMethod)
}
)

clickPref(
title = DSLSettingsText.from(R.string.NotificationsSettingsFragment__configure_unifiedpush),
isEnabled = state.preferredNotificationMethod == NotificationDeliveryMethod.UNIFIEDPUSH,
onClick = {
navigateToUnifiedPushSettings()
}
)
}
}

Expand All @@ -383,9 +393,32 @@ class NotificationsSettingsFragment : DSLSettingsFragment(R.string.preferences__
NotificationDeliveryMethod.WEBSOCKET -> {
viewModel.setPreferredNotificationMethod(method)
}

NotificationDeliveryMethod.UNIFIEDPUSH -> {
if (method != previousMethod) {
MaterialAlertDialogBuilder(requireContext())
.setTitle(R.string.NotificationsSettingsFragment__mollysocket_server)
.setMessage(R.string.NotificationsSettingsFragment__to_use_unifiedpush_you_need_a_mollysocket_server)
.setPositiveButton(R.string.yes) { _, _ ->
viewModel.setPreferredNotificationMethod(method)
navigateToUnifiedPushSettings()
}
.setNegativeButton(R.string.no, null)
.setNeutralButton(R.string.LearnMoreTextView_learn_more) { _, _ ->
CommunicationActions.openBrowserLink(requireContext(), getString(R.string.mollysocket_setup_url))
}
.show()
} else {
navigateToUnifiedPushSettings()
}
}
}
}

private fun navigateToUnifiedPushSettings() {
findNavController().safeNavigate(R.id.action_notificationsSettingsFragment_to_unifiedPushFragment)
}

private fun getRingtoneSummary(uri: Uri): String {
return if (TextUtils.isEmpty(uri.toString())) {
getString(R.string.preferences__silent)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,8 @@ data class NotificationsSettingsState(
val notifyWhenContactJoinsSignal: Boolean,
val isLinkedDevice: Boolean,
val preferredNotificationMethod: NotificationDeliveryMethod,
val canReceiveFcm: Boolean
val canReceiveFcm: Boolean,
val canReceiveUnifiedPush: Boolean
)

data class MessageNotificationsState(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -159,6 +159,7 @@ class NotificationsSettingsViewModel(private val sharedPreferences: SharedPrefer
isLinkedDevice = SignalStore.account.isLinkedDevice,
preferredNotificationMethod = SignalStore.settings.preferredNotificationMethod,
canReceiveFcm = SignalStore.account.canReceiveFcm,
canReceiveUnifiedPush = SignalStore.unifiedpush.isAvailableOrAirGapped
)

private fun canEnableNotifications(): Boolean {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,8 @@ public void onSendError(@NonNull String s, @NonNull Exception e) {
Log.w(TAG, "onSendError()", e);
}

private static void handleReceivedNotification(Context context, @Nullable RemoteMessage remoteMessage) {
// MOLLY: Make this function public to use it from UnifiedPushReceiver
public static void handleReceivedNotification(Context context, @Nullable RemoteMessage remoteMessage) {
boolean highPriority = remoteMessage != null && remoteMessage.getPriority() == RemoteMessage.PRIORITY_HIGH;
try {
Log.d(TAG, String.format(Locale.US, "[handleReceivedNotification] API: %s, RemoteMessagePriority: %s", Build.VERSION.SDK_INT, remoteMessage != null ? remoteMessage.getPriority() : "n/a"));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,8 @@
import java.util.List;
import java.util.Map;

import im.molly.unifiedpush.jobs.UnifiedPushRefreshJob;

public final class JobManagerFactories {

public static Map<String, Job.Factory> getJobFactories(@NonNull Application application) {
Expand Down Expand Up @@ -140,6 +142,7 @@ public static Map<String, Job.Factory> getJobFactories(@NonNull Application appl
put(DownloadLatestEmojiDataJob.KEY, new DownloadLatestEmojiDataJob.Factory());
put(EmojiSearchIndexDownloadJob.KEY, new EmojiSearchIndexDownloadJob.Factory());
put(FcmRefreshJob.KEY, new FcmRefreshJob.Factory());
put(UnifiedPushRefreshJob.KEY, new UnifiedPushRefreshJob.Factory());
put(FetchRemoteMegaphoneImageJob.KEY, new FetchRemoteMegaphoneImageJob.Factory());
put(FontDownloaderJob.KEY, new FontDownloaderJob.Factory());
put(ForceUpdateGroupV2Job.KEY, new ForceUpdateGroupV2Job.Factory());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -317,7 +317,7 @@ class AccountValues internal constructor(store: KeyValueStore, context: Context)

@get:JvmName("isPushAvailable")
val pushAvailable: Boolean
get() = canReceiveFcm
get() = canReceiveFcm || SignalStore.unifiedpush.isAvailableOrAirGapped

/** The FCM token, which allows the server to send us FCM messages. */
var fcmToken: String?
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -551,7 +551,7 @@ public enum Theme {
}

public enum NotificationDeliveryMethod {
FCM, WEBSOCKET;
FCM, WEBSOCKET, UNIFIEDPUSH;

public @NonNull String serialize() {
return name();
Expand All @@ -565,6 +565,7 @@ public enum NotificationDeliveryMethod {
return switch (this) {
case FCM -> R.string.NotificationDeliveryMethod__fcm;
case WEBSOCKET -> R.string.NotificationDeliveryMethod__websocket;
case UNIFIEDPUSH -> R.string.NotificationDeliveryMethod__unifiedpush;
};
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ class SignalStore(context: Application, private val store: KeyValueStore) {
val storyValues = StoryValues(store)
val apkUpdateValues = ApkUpdateValues(store)
val backupValues = BackupValues(store)
val unifiedPushValues = UnifiedPushValues(store)

val plainTextValues = PlainTextSharedPrefsDataStore(context)

Expand Down Expand Up @@ -258,6 +259,11 @@ class SignalStore(context: Application, private val store: KeyValueStore) {
val backup: BackupValues
get() = instance!!.backupValues

@JvmStatic
@get:JvmName("unifiedpush")
val unifiedpush: UnifiedPushValues
get() = instance!!.unifiedPushValues

val groupsV2AciAuthorizationCache: GroupsV2AuthorizationSignalStoreCache
get() = GroupsV2AuthorizationSignalStoreCache.createAciCache(instance!!.store)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,7 @@ public class LogSectionSystemInfo implements LogSection {
builder.append("Network Status : ").append(NetworkUtil.getNetworkStatus(context)).append("\n");
builder.append("Play Services : ").append(getPlayServicesString(context)).append("\n");
builder.append("FCM : ").append(locked ? "Unknown" : SignalStore.account().isFcmEnabled()).append("\n");
builder.append("UnifiedPush : ").append(locked ? "Unknown" : SignalStore.unifiedpush().isEnabled()).append("\n");
builder.append("Locale : ").append(Locale.getDefault()).append("\n");
builder.append("Linked Devices : ").append(locked ? "Unknown" : TextSecurePreferences.isMultiDevice(context)).append("\n");
builder.append("First Version : ").append(TextSecurePreferences.getFirstInstallVersion(context)).append("\n");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -587,7 +587,6 @@ object RegistrationRepository {
return started == true
}

@VisibleForTesting
fun generateSignedAndLastResortPreKeys(identity: IdentityKeyPair, metadataStore: PreKeyMetadataStore): PreKeyCollection {
val signedPreKey = PreKeyUtil.generateSignedPreKey(metadataStore.nextSignedPreKeyId, identity.privateKey)
val lastResortKyberPreKey = PreKeyUtil.generateLastResortKyberPreKey(metadataStore.nextKyberPreKeyId, identity.privateKey)
Expand Down
13 changes: 13 additions & 0 deletions app/src/main/res/navigation/app_settings_with_change_number.xml
Original file line number Diff line number Diff line change
Expand Up @@ -396,6 +396,14 @@
app:popEnterAnim="@anim/fragment_close_enter"
app:popExitAnim="@anim/fragment_close_exit" />

<action
android:id="@+id/action_notificationsSettingsFragment_to_unifiedPushFragment"
app:destination="@id/notificationUnifiedPushFragment"
app:enterAnim="@anim/fragment_open_enter"
app:exitAnim="@anim/fragment_open_exit"
app:popEnterAnim="@anim/fragment_close_enter"
app:popExitAnim="@anim/fragment_close_exit" />

</fragment>

<!-- region Privacy -->
Expand Down Expand Up @@ -803,6 +811,11 @@
app:popExitAnim="@anim/fragment_close_exit" />
</fragment>

<!-- UnifiedPush -->
<fragment
android:id="@+id/notificationUnifiedPushFragment"
android:name="im.molly.unifiedpush.components.settings.app.notifications.UnifiedPushSettingsFragment" />

<fragment
android:id="@+id/editNotificationProfileFragment"
android:name="org.thoughtcrime.securesms.components.settings.app.notifications.profiles.EditNotificationProfileFragment"
Expand Down
Loading