From bf83914357b0e754abe13b05a045f094ca5a46f8 Mon Sep 17 00:00:00 2001 From: Alex Hart Date: Tue, 25 Mar 2025 09:11:16 -0300 Subject: [PATCH] Convert MainActivity to Kotlin. --- .../thoughtcrime/securesms/MainActivity.java | 305 ------------------ .../thoughtcrime/securesms/MainActivity.kt | 257 +++++++++++++++ .../thoughtcrime/securesms/MainNavigator.java | 12 +- .../securesms/calls/log/CallLogFragment.kt | 9 +- .../settings/app/AppSettingsActivity.kt | 3 + .../conversation/v2/ConversationFragment.kt | 15 +- .../ConversationListFragment.java | 4 +- .../UserNotificationMigrationJob.java | 4 +- 8 files changed, 291 insertions(+), 318 deletions(-) delete mode 100644 app/src/main/java/org/thoughtcrime/securesms/MainActivity.java create mode 100644 app/src/main/java/org/thoughtcrime/securesms/MainActivity.kt diff --git a/app/src/main/java/org/thoughtcrime/securesms/MainActivity.java b/app/src/main/java/org/thoughtcrime/securesms/MainActivity.java deleted file mode 100644 index 9f8a9565cb..0000000000 --- a/app/src/main/java/org/thoughtcrime/securesms/MainActivity.java +++ /dev/null @@ -1,305 +0,0 @@ -package org.thoughtcrime.securesms; - -import android.annotation.SuppressLint; -import android.app.Activity; -import android.content.Context; -import android.content.Intent; -import android.net.Uri; -import android.os.Bundle; -import android.view.View; -import android.view.ViewTreeObserver; - -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; -import androidx.core.content.ContextCompat; -import androidx.lifecycle.ViewModelProvider; - -import com.google.android.material.dialog.MaterialAlertDialogBuilder; - -import org.signal.core.util.BundleExtensions; -import org.signal.core.util.concurrent.LifecycleDisposable; -import org.signal.donations.StripeApi; -import org.thoughtcrime.securesms.calls.YouAreAlreadyInACallSnackbar; -import org.thoughtcrime.securesms.components.ConnectivityWarningBottomSheet; -import org.thoughtcrime.securesms.components.DebugLogsPromptDialogFragment; -import org.thoughtcrime.securesms.components.DeviceSpecificNotificationBottomSheet; -import org.thoughtcrime.securesms.components.PromptBatterySaverDialogFragment; -import org.thoughtcrime.securesms.components.settings.app.AppSettingsActivity; -import org.thoughtcrime.securesms.components.voice.VoiceNoteMediaController; -import org.thoughtcrime.securesms.components.voice.VoiceNoteMediaControllerOwner; -import org.thoughtcrime.securesms.conversationlist.RelinkDevicesReminderBottomSheetFragment; -import org.thoughtcrime.securesms.conversationlist.RestoreCompleteBottomSheetDialog; -import org.thoughtcrime.securesms.devicetransfer.olddevice.OldDeviceExitActivity; -import org.thoughtcrime.securesms.keyvalue.SignalStore; -import org.thoughtcrime.securesms.net.DeviceTransferBlockingInterceptor; -import org.thoughtcrime.securesms.notifications.VitalsViewModel; -import org.thoughtcrime.securesms.stories.Stories; -import org.thoughtcrime.securesms.stories.tabs.ConversationListTab; -import org.thoughtcrime.securesms.stories.tabs.ConversationListTabRepository; -import org.thoughtcrime.securesms.stories.tabs.ConversationListTabsViewModel; -import org.thoughtcrime.securesms.util.AppStartup; -import org.thoughtcrime.securesms.util.CachedInflater; -import org.thoughtcrime.securesms.util.CommunicationActions; -import org.thoughtcrime.securesms.util.DynamicNoActionBarTheme; -import org.thoughtcrime.securesms.util.DynamicTheme; -import org.thoughtcrime.securesms.util.SplashScreenUtil; -import org.thoughtcrime.securesms.util.WindowUtil; - -public class MainActivity extends PassphraseRequiredActivity implements VoiceNoteMediaControllerOwner { - - private static final String KEY_STARTING_TAB = "STARTING_TAB"; - public static final int RESULT_CONFIG_CHANGED = Activity.RESULT_FIRST_USER + 901; - - private final DynamicTheme dynamicTheme = new DynamicNoActionBarTheme(); - private final MainNavigator navigator = new MainNavigator(this); - - private VoiceNoteMediaController mediaController; - private ConversationListTabsViewModel conversationListTabsViewModel; - private VitalsViewModel vitalsViewModel; - - private final LifecycleDisposable lifecycleDisposable = new LifecycleDisposable(); - - private boolean onFirstRender = false; - - public static @NonNull Intent clearTop(@NonNull Context context) { - Intent intent = new Intent(context, MainActivity.class); - - intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | - Intent.FLAG_ACTIVITY_NEW_TASK | - Intent.FLAG_ACTIVITY_SINGLE_TOP); - - return intent; - } - - public static @NonNull Intent clearTopAndOpenTab(@NonNull Context context, @NonNull ConversationListTab startingTab) { - Intent intent = clearTop(context); - intent.putExtra(KEY_STARTING_TAB, startingTab); - - return intent; - } - - @Override - protected void onCreate(Bundle savedInstanceState, boolean ready) { - AppStartup.getInstance().onCriticalRenderEventStart(); - super.onCreate(savedInstanceState, ready); - - setContentView(R.layout.main_activity); - final View content = findViewById(android.R.id.content); - content.getViewTreeObserver().addOnPreDrawListener( - new ViewTreeObserver.OnPreDrawListener() { - @Override - public boolean onPreDraw() { - // Use pre draw listener to delay drawing frames till conversation list is ready - if (onFirstRender) { - content.getViewTreeObserver().removeOnPreDrawListener(this); - return true; - } else { - return false; - } - } - }); - - lifecycleDisposable.bindTo(this); - - mediaController = new VoiceNoteMediaController(this, true); - - - ConversationListTab startingTab = null; - if (getIntent().getExtras() != null) { - startingTab = BundleExtensions.getSerializableCompat(getIntent().getExtras(), KEY_STARTING_TAB, ConversationListTab.class); - } - - ConversationListTabRepository repository = new ConversationListTabRepository(); - ConversationListTabsViewModel.Factory factory = new ConversationListTabsViewModel.Factory(startingTab, repository); - - handleDeeplinkIntent(getIntent()); - - CachedInflater.from(this).clear(); - - conversationListTabsViewModel = new ViewModelProvider(this, factory).get(ConversationListTabsViewModel.class); - updateTabVisibility(); - - vitalsViewModel = new ViewModelProvider(this).get(VitalsViewModel.class); - - lifecycleDisposable.add( - vitalsViewModel - .getVitalsState() - .subscribe(this::presentVitalsState) - ); - } - - @SuppressLint("NewApi") - private void presentVitalsState(VitalsViewModel.State state) { - switch (state) { - case NONE: - break; - case PROMPT_SPECIFIC_BATTERY_SAVER_DIALOG: - DeviceSpecificNotificationBottomSheet.show(getSupportFragmentManager()); - break; - case PROMPT_GENERAL_BATTERY_SAVER_DIALOG: - PromptBatterySaverDialogFragment.show(getSupportFragmentManager()); - break; - case PROMPT_CONNECTIVITY_WARNING: - ConnectivityWarningBottomSheet.show(getSupportFragmentManager()); - break; - case PROMPT_DEBUGLOGS_FOR_NOTIFICATIONS: - DebugLogsPromptDialogFragment.show(this, DebugLogsPromptDialogFragment.Purpose.NOTIFICATIONS); - break; - case PROMPT_DEBUGLOGS_FOR_CRASH: - DebugLogsPromptDialogFragment.show(this, DebugLogsPromptDialogFragment.Purpose.CRASH); - break; - case PROMPT_DEBUGLOGS_FOR_CONNECTIVITY_WARNING: - DebugLogsPromptDialogFragment.show(this, DebugLogsPromptDialogFragment.Purpose.CONNECTIVITY_WARNING); - break; - } - } - - @Override - public Intent getIntent() { - return super.getIntent().setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | - Intent.FLAG_ACTIVITY_NEW_TASK | - Intent.FLAG_ACTIVITY_SINGLE_TOP); - } - - @Override - protected void onNewIntent(Intent intent) { - super.onNewIntent(intent); - handleDeeplinkIntent(intent); - - if (intent.getExtras() != null) { - ConversationListTab startingTab = BundleExtensions.getSerializableCompat(intent.getExtras(), KEY_STARTING_TAB, ConversationListTab.class); - if (startingTab != null) { - switch (startingTab) { - case CHATS -> conversationListTabsViewModel.onChatsSelected(); - case CALLS -> conversationListTabsViewModel.onCallsSelected(); - case STORIES -> { - if (Stories.isFeatureEnabled()) { - conversationListTabsViewModel.onStoriesSelected(); - } - } - } - } - } - } - - @Override - protected void onPreCreate() { - super.onPreCreate(); - dynamicTheme.onCreate(this); - } - - @Override - protected void onResume() { - super.onResume(); - dynamicTheme.onResume(this); - - if (SignalStore.misc().getShouldShowLinkedDevicesReminder()) { - SignalStore.misc().setShouldShowLinkedDevicesReminder(false); - RelinkDevicesReminderBottomSheetFragment.show(getSupportFragmentManager()); - } - - if (SignalStore.registration().isRestoringOnNewDevice()) { - SignalStore.registration().setRestoringOnNewDevice(false); - RestoreCompleteBottomSheetDialog.show(getSupportFragmentManager()); - } else if (SignalStore.misc().isOldDeviceTransferLocked()) { - new MaterialAlertDialogBuilder(this) - .setTitle(R.string.OldDeviceTransferLockedDialog__complete_registration_on_your_new_device) - .setMessage(R.string.OldDeviceTransferLockedDialog__your_signal_account_has_been_transferred_to_your_new_device) - .setPositiveButton(R.string.OldDeviceTransferLockedDialog__done, (d, w) -> OldDeviceExitActivity.exit(this)) - .setNegativeButton(R.string.OldDeviceTransferLockedDialog__cancel_and_activate_this_device, (d, w) -> { - SignalStore.misc().setOldDeviceTransferLocked(false); - DeviceTransferBlockingInterceptor.getInstance().unblockNetwork(); - }) - .setCancelable(false) - .show(); - } - - updateTabVisibility(); - - vitalsViewModel.checkSlowNotificationHeuristics(); - } - - @Override - protected void onStop() { - super.onStop(); - SplashScreenUtil.setSplashScreenThemeIfNecessary(this, SignalStore.settings().getTheme()); - } - - @Override - public void onBackPressed() { - if (!navigator.onBackPressed()) { - super.onBackPressed(); - } - } - - @Override - protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) { - super.onActivityResult(requestCode, resultCode, data); - if (requestCode == MainNavigator.REQUEST_CONFIG_CHANGES && resultCode == RESULT_CONFIG_CHANGED) { - recreate(); - } - } - - private void updateTabVisibility() { - findViewById(R.id.conversation_list_tabs).setVisibility(View.VISIBLE); - WindowUtil.setNavigationBarColor(this, ContextCompat.getColor(this, R.color.signal_colorSurface2)); - } - - public @NonNull MainNavigator getNavigator() { - return navigator; - } - - private void handleDeeplinkIntent(Intent intent) { - handleGroupLinkInIntent(intent); - handleProxyInIntent(intent); - handleSignalMeIntent(intent); - handleCallLinkInIntent(intent); - handleDonateReturnIntent(intent); - } - - private void handleGroupLinkInIntent(Intent intent) { - Uri data = intent.getData(); - if (data != null) { - CommunicationActions.handlePotentialGroupLinkUrl(this, data.toString()); - } - } - - private void handleProxyInIntent(Intent intent) { - Uri data = intent.getData(); - if (data != null) { - CommunicationActions.handlePotentialProxyLinkUrl(this, data.toString()); - } - } - - private void handleSignalMeIntent(Intent intent) { - Uri data = intent.getData(); - if (data != null) { - CommunicationActions.handlePotentialSignalMeUrl(this, data.toString()); - } - } - - private void handleCallLinkInIntent(Intent intent) { - Uri data = intent.getData(); - if (data != null) { - CommunicationActions.handlePotentialCallLinkUrl(this, data.toString(), () -> { - YouAreAlreadyInACallSnackbar.show(findViewById(android.R.id.content)); - }); - } - } - - private void handleDonateReturnIntent(Intent intent) { - Uri data = intent.getData(); - if (data != null && data.toString().startsWith(StripeApi.RETURN_URL_IDEAL)) { - startActivity(AppSettingsActivity.manageSubscriptions(this)); - } - } - - public void onFirstRender() { - onFirstRender = true; - } - - @Override - public @NonNull VoiceNoteMediaController getVoiceNoteMediaController() { - return mediaController; - } -} diff --git a/app/src/main/java/org/thoughtcrime/securesms/MainActivity.kt b/app/src/main/java/org/thoughtcrime/securesms/MainActivity.kt new file mode 100644 index 0000000000..a243cdd56d --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/MainActivity.kt @@ -0,0 +1,257 @@ +/* + * Copyright 2025 Signal Messenger, LLC + * SPDX-License-Identifier: AGPL-3.0-only + */ + +package org.thoughtcrime.securesms + +import android.annotation.SuppressLint +import android.app.Activity +import android.content.Context +import android.content.Intent +import android.os.Bundle +import android.view.View +import android.view.ViewTreeObserver +import androidx.core.content.ContextCompat +import com.google.android.material.dialog.MaterialAlertDialogBuilder +import org.signal.core.util.concurrent.LifecycleDisposable +import org.signal.core.util.getSerializableCompat +import org.signal.donations.StripeApi +import org.thoughtcrime.securesms.calls.YouAreAlreadyInACallSnackbar.show +import org.thoughtcrime.securesms.components.ConnectivityWarningBottomSheet +import org.thoughtcrime.securesms.components.DebugLogsPromptDialogFragment +import org.thoughtcrime.securesms.components.DeviceSpecificNotificationBottomSheet +import org.thoughtcrime.securesms.components.PromptBatterySaverDialogFragment +import org.thoughtcrime.securesms.components.settings.app.AppSettingsActivity.Companion.manageSubscriptions +import org.thoughtcrime.securesms.components.voice.VoiceNoteMediaController +import org.thoughtcrime.securesms.components.voice.VoiceNoteMediaControllerOwner +import org.thoughtcrime.securesms.conversationlist.RelinkDevicesReminderBottomSheetFragment +import org.thoughtcrime.securesms.conversationlist.RestoreCompleteBottomSheetDialog +import org.thoughtcrime.securesms.devicetransfer.olddevice.OldDeviceExitActivity +import org.thoughtcrime.securesms.keyvalue.SignalStore +import org.thoughtcrime.securesms.net.DeviceTransferBlockingInterceptor +import org.thoughtcrime.securesms.notifications.VitalsViewModel +import org.thoughtcrime.securesms.stories.Stories +import org.thoughtcrime.securesms.stories.tabs.ConversationListTab +import org.thoughtcrime.securesms.stories.tabs.ConversationListTabRepository +import org.thoughtcrime.securesms.stories.tabs.ConversationListTabsViewModel +import org.thoughtcrime.securesms.util.AppStartup +import org.thoughtcrime.securesms.util.CachedInflater +import org.thoughtcrime.securesms.util.CommunicationActions +import org.thoughtcrime.securesms.util.DynamicNoActionBarTheme +import org.thoughtcrime.securesms.util.SplashScreenUtil +import org.thoughtcrime.securesms.util.WindowUtil +import org.thoughtcrime.securesms.util.viewModel + +class MainActivity : PassphraseRequiredActivity(), VoiceNoteMediaControllerOwner, MainNavigator.NavigatorProvider { + + companion object { + private const val KEY_STARTING_TAB = "STARTING_TAB" + const val RESULT_CONFIG_CHANGED = Activity.RESULT_FIRST_USER + 901 + + @JvmStatic + fun clearTop(context: Context): Intent { + return Intent(context, MainActivity::class.java) + .setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP or Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_SINGLE_TOP) + } + + @JvmStatic + fun clearTopAndOpenTab(context: Context, startingTab: ConversationListTab): Intent { + return clearTop(context).putExtra(KEY_STARTING_TAB, startingTab) + } + } + + private val dynamicTheme = DynamicNoActionBarTheme() + private val navigator = MainNavigator(this) + private val lifecycleDisposable = LifecycleDisposable() + + private lateinit var mediaController: VoiceNoteMediaController + + override val voiceNoteMediaController: VoiceNoteMediaController + get() = mediaController + + private val conversationListTabsViewModel: ConversationListTabsViewModel by viewModel { + val startingTab = intent.extras?.getSerializableCompat(KEY_STARTING_TAB, ConversationListTab::class.java) + ConversationListTabsViewModel(startingTab ?: ConversationListTab.CHATS, ConversationListTabRepository()) + } + + private val vitalsViewModel: VitalsViewModel by viewModel { + VitalsViewModel(application) + } + + private var onFirstRender = false + + override fun onCreate(savedInstanceState: Bundle?, ready: Boolean) { + AppStartup.getInstance().onCriticalRenderEventStart() + super.onCreate(savedInstanceState, ready) + + setContentView(R.layout.main_activity) + + val content: View = findViewById(android.R.id.content) + content.viewTreeObserver.addOnPreDrawListener(object : ViewTreeObserver.OnPreDrawListener { + override fun onPreDraw(): Boolean { + // Use pre draw listener to delay drawing frames till conversation list is ready + return if (onFirstRender) { + content.viewTreeObserver.removeOnPreDrawListener(this) + true + } else { + false + } + } + }) + + lifecycleDisposable.bindTo(this) + + mediaController = VoiceNoteMediaController(this, true) + conversationListTabsViewModel + + handleDeepLinkIntent(intent) + CachedInflater.from(this).clear() + updateNavigationBarColor() + + lifecycleDisposable += vitalsViewModel.vitalsState.subscribe(this::presentVitalsState) + } + + override fun getIntent(): Intent { + return super.getIntent().setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP or Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_SINGLE_TOP) + } + + override fun onNewIntent(intent: Intent) { + super.onNewIntent(intent) + handleDeepLinkIntent(intent) + + val extras = intent.extras ?: return + val startingTab = extras.getSerializableCompat(KEY_STARTING_TAB, ConversationListTab::class.java) + + when (startingTab) { + ConversationListTab.CHATS -> conversationListTabsViewModel.onChatsSelected() + ConversationListTab.CALLS -> conversationListTabsViewModel.onCallsSelected() + ConversationListTab.STORIES -> { + if (Stories.isFeatureEnabled()) { + conversationListTabsViewModel.onStoriesSelected() + } + } + null -> Unit + } + } + + override fun onPreCreate() { + super.onPreCreate() + dynamicTheme.onCreate(this) + } + + override fun onResume() { + super.onResume() + dynamicTheme.onResume(this) + + if (SignalStore.misc.shouldShowLinkedDevicesReminder) { + SignalStore.misc.shouldShowLinkedDevicesReminder = false + RelinkDevicesReminderBottomSheetFragment.show(supportFragmentManager) + } + + if (SignalStore.registration.restoringOnNewDevice) { + SignalStore.registration.restoringOnNewDevice = false + RestoreCompleteBottomSheetDialog.show(supportFragmentManager) + } else if (SignalStore.misc.isOldDeviceTransferLocked) { + MaterialAlertDialogBuilder(this) + .setTitle(R.string.OldDeviceTransferLockedDialog__complete_registration_on_your_new_device) + .setMessage(R.string.OldDeviceTransferLockedDialog__your_signal_account_has_been_transferred_to_your_new_device) + .setPositiveButton(R.string.OldDeviceTransferLockedDialog__done) { _, _ -> OldDeviceExitActivity.exit(this) } + .setNegativeButton(R.string.OldDeviceTransferLockedDialog__cancel_and_activate_this_device) { _, _ -> + SignalStore.misc.isOldDeviceTransferLocked = false + DeviceTransferBlockingInterceptor.getInstance().unblockNetwork() + } + .setCancelable(false) + .show() + } + + updateNavigationBarColor() + + vitalsViewModel.checkSlowNotificationHeuristics() + } + + override fun onStop() { + super.onStop() + SplashScreenUtil.setSplashScreenThemeIfNecessary(this, SignalStore.settings.theme) + } + + override fun onBackPressed() { + if (!navigator.onBackPressed()) { + super.onBackPressed() + } + } + + override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { + super.onActivityResult(requestCode, resultCode, data) + if (requestCode == MainNavigator.REQUEST_CONFIG_CHANGES && resultCode == RESULT_CONFIG_CHANGED) { + recreate() + } + } + + override fun onFirstRender() { + onFirstRender = true + } + + override fun getNavigator(): MainNavigator { + return navigator + } + + private fun handleDeepLinkIntent(intent: Intent) { + handleGroupLinkInIntent(intent) + handleProxyInIntent(intent) + handleSignalMeIntent(intent) + handleCallLinkInIntent(intent) + handleDonateReturnIntent(intent) + } + + private fun updateNavigationBarColor() { + WindowUtil.setNavigationBarColor(this, ContextCompat.getColor(this, R.color.signal_colorSurface2)) + } + + @SuppressLint("NewApi") + private fun presentVitalsState(state: VitalsViewModel.State) { + when (state) { + VitalsViewModel.State.NONE -> Unit + VitalsViewModel.State.PROMPT_SPECIFIC_BATTERY_SAVER_DIALOG -> DeviceSpecificNotificationBottomSheet.show(supportFragmentManager) + VitalsViewModel.State.PROMPT_GENERAL_BATTERY_SAVER_DIALOG -> PromptBatterySaverDialogFragment.show(supportFragmentManager) + VitalsViewModel.State.PROMPT_DEBUGLOGS_FOR_NOTIFICATIONS -> DebugLogsPromptDialogFragment.show(this, DebugLogsPromptDialogFragment.Purpose.NOTIFICATIONS) + VitalsViewModel.State.PROMPT_DEBUGLOGS_FOR_CRASH -> DebugLogsPromptDialogFragment.show(this, DebugLogsPromptDialogFragment.Purpose.CRASH) + VitalsViewModel.State.PROMPT_CONNECTIVITY_WARNING -> ConnectivityWarningBottomSheet.show(supportFragmentManager) + VitalsViewModel.State.PROMPT_DEBUGLOGS_FOR_CONNECTIVITY_WARNING -> DebugLogsPromptDialogFragment.show(this, DebugLogsPromptDialogFragment.Purpose.CONNECTIVITY_WARNING) + } + } + + private fun handleGroupLinkInIntent(intent: Intent) { + intent.data?.let { data -> + CommunicationActions.handlePotentialGroupLinkUrl(this, data.toString()) + } + } + + private fun handleProxyInIntent(intent: Intent) { + intent.data?.let { data -> + CommunicationActions.handlePotentialProxyLinkUrl(this, data.toString()) + } + } + + private fun handleSignalMeIntent(intent: Intent) { + intent.data?.let { data -> + CommunicationActions.handlePotentialSignalMeUrl(this, data.toString()) + } + } + + private fun handleCallLinkInIntent(intent: Intent) { + intent.data?.let { data -> + CommunicationActions.handlePotentialCallLinkUrl(this, data.toString()) { + show(findViewById(android.R.id.content)) + } + } + } + + private fun handleDonateReturnIntent(intent: Intent) { + intent.data?.let { data -> + if (data.toString().startsWith(StripeApi.RETURN_URL_IDEAL)) { + startActivity(manageSubscriptions(this)) + } + } + } +} diff --git a/app/src/main/java/org/thoughtcrime/securesms/MainNavigator.java b/app/src/main/java/org/thoughtcrime/securesms/MainNavigator.java index 0d7e28c24e..15d74f3500 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/MainNavigator.java +++ b/app/src/main/java/org/thoughtcrime/securesms/MainNavigator.java @@ -4,6 +4,7 @@ import android.app.Activity; import android.content.Intent; import androidx.annotation.NonNull; +import androidx.appcompat.app.AppCompatActivity; import androidx.fragment.app.Fragment; import androidx.fragment.app.FragmentManager; @@ -19,10 +20,10 @@ public class MainNavigator { public static final int REQUEST_CONFIG_CHANGES = 901; - private final MainActivity activity; + private final AppCompatActivity activity; private final LifecycleDisposable lifecycleDisposable; - public MainNavigator(@NonNull MainActivity activity) { + public MainNavigator(@NonNull AppCompatActivity activity) { this.activity = activity; this.lifecycleDisposable = new LifecycleDisposable(); @@ -34,7 +35,7 @@ public class MainNavigator { throw new IllegalArgumentException("Activity must be an instance of MainActivity!"); } - return ((MainActivity) activity).getNavigator(); + return ((NavigatorProvider) activity).getNavigator(); } /** @@ -88,4 +89,9 @@ public class MainNavigator { */ boolean onBackPressed(); } + + public interface NavigatorProvider { + @NonNull MainNavigator getNavigator(); + void onFirstRender(); + } } diff --git a/app/src/main/java/org/thoughtcrime/securesms/calls/log/CallLogFragment.kt b/app/src/main/java/org/thoughtcrime/securesms/calls/log/CallLogFragment.kt index 8e954c6b9a..bcc0dbe157 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/calls/log/CallLogFragment.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/calls/log/CallLogFragment.kt @@ -33,7 +33,7 @@ import org.signal.core.util.DimensionUnit import org.signal.core.util.concurrent.LifecycleDisposable import org.signal.core.util.concurrent.addTo import org.signal.core.util.logging.Log -import org.thoughtcrime.securesms.MainActivity +import org.thoughtcrime.securesms.MainNavigator import org.thoughtcrime.securesms.R import org.thoughtcrime.securesms.calls.YouAreAlreadyInACallSnackbar import org.thoughtcrime.securesms.calls.links.details.CallLinkDetailsActivity @@ -134,7 +134,7 @@ class CallLogFragment : Fragment(R.layout.call_log_fragment), CallLogAdapter.Cal callLogAdapter.setPagingController(viewModel.controller) callLogAdapter.registerAdapterDataObserver(object : RecyclerView.AdapterDataObserver() { override fun onItemRangeInserted(positionStart: Int, itemCount: Int) { - (requireActivity() as? MainActivity)?.onFirstRender() + (requireActivity() as? MainNavigator.NavigatorProvider)?.onFirstRender() callLogAdapter.unregisterAdapterDataObserver(this) } }) @@ -328,8 +328,7 @@ class CallLogFragment : Fragment(R.layout.call_log_fragment), CallLogAdapter.Cal } } - filterViewOffsetChangeListener = AppBarLayout.OnOffsetChangedListener { - layout: AppBarLayout, verticalOffset: Int -> + filterViewOffsetChangeListener = AppBarLayout.OnOffsetChangedListener { layout: AppBarLayout, verticalOffset: Int -> val progress = 1 - verticalOffset.toFloat() / -layout.height binding.pullView.onUserDrag(progress) } @@ -485,6 +484,7 @@ class CallLogFragment : Fragment(R.layout.call_log_fragment), CallLogAdapter.Cal .setPositiveButton(android.R.string.ok, null) .show() } + CallLogDeletionResult.Success -> { Snackbar .make( @@ -494,6 +494,7 @@ class CallLogFragment : Fragment(R.layout.call_log_fragment), CallLogAdapter.Cal ) .show() } + is CallLogDeletionResult.UnknownFailure -> { Log.w(TAG, "Deletion failed.", it.reason) Toast.makeText(requireContext(), R.string.CallLogFragment__deletion_failed, Toast.LENGTH_SHORT).show() diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/AppSettingsActivity.kt b/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/AppSettingsActivity.kt index 62a51ca070..6cfd7db7dd 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/AppSettingsActivity.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/AppSettingsActivity.kt @@ -53,6 +53,7 @@ class AppSettingsActivity : DSLSettingsActivity(), GooglePayComponent { StartLocation.BACKUPS -> AppSettingsFragmentDirections.actionDirectToBackupsPreferenceFragment() StartLocation.HELP -> AppSettingsFragmentDirections.actionDirectToHelpFragment() .setStartCategoryIndex(intent.getIntExtra(HelpFragment.START_CATEGORY_INDEX, 0)) + StartLocation.PROXY -> AppSettingsFragmentDirections.actionDirectToEditProxyFragment() StartLocation.NOTIFICATIONS -> AppSettingsFragmentDirections.actionDirectToNotificationsSettingsFragment() StartLocation.CHANGE_NUMBER -> AppSettingsFragmentDirections.actionDirectToChangeNumberFragment() @@ -64,6 +65,7 @@ class AppSettingsActivity : DSLSettingsActivity(), GooglePayComponent { StartLocation.NOTIFICATION_PROFILE_DETAILS -> AppSettingsFragmentDirections.actionDirectToNotificationProfileDetails( EditNotificationProfileScheduleFragmentArgs.fromBundle(intent.getBundleExtra(START_ARGUMENTS)!!).profileId ) + StartLocation.PRIVACY -> AppSettingsFragmentDirections.actionDirectToPrivacy() StartLocation.LINKED_DEVICES -> AppSettingsFragmentDirections.actionDirectToDevices() StartLocation.USERNAME_LINK -> AppSettingsFragmentDirections.actionDirectToUsernameLinkSettings() @@ -74,6 +76,7 @@ class AppSettingsActivity : DSLSettingsActivity(), GooglePayComponent { CreateFoldersFragmentArgs.fromBundle(intent.getBundleExtra(START_ARGUMENTS)!!).folderId, CreateFoldersFragmentArgs.fromBundle(intent.getBundleExtra(START_ARGUMENTS)!!).threadIds ) + StartLocation.BACKUPS_SETTINGS -> AppSettingsFragmentDirections.actionDirectToBackupsSettingsFragment() } } diff --git a/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/ConversationFragment.kt b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/ConversationFragment.kt index f311122603..fab5c69719 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/ConversationFragment.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/ConversationFragment.kt @@ -1066,6 +1066,7 @@ class ConversationFragment : AvatarDownloadStateCache.DownloadState.FINISHED -> { viewModel.updateThreadHeader() } + AvatarDownloadStateCache.DownloadState.FAILED -> { Snackbar.make(requireView(), R.string.ConversationFragment_photo_failed, Snackbar.LENGTH_LONG).show() presentConversationTitle(recipient) @@ -2255,6 +2256,7 @@ class ConversationFragment : Log.d(TAG, "report spam complete") toast(R.string.ConversationFragment_reported_as_spam_and_blocked) } + is Result.Failure -> { Log.d(TAG, "report spam failed ${result.failure}") toast(GroupErrors.getUserDisplayMessage(result.failure)) @@ -3724,7 +3726,10 @@ class ConversationFragment : MediaUtil.isVideoType(it.contentType) -> VideoSlide(requireContext(), it.uri, it.size, it.isVideoGif, it.width, it.height, it.caption.orNull(), it.transformProperties.orNull()) MediaUtil.isGif(it.contentType) -> GifSlide(requireContext(), it.uri, it.size, it.width, it.height, it.isBorderless, it.caption.orNull()) MediaUtil.isImageType(it.contentType) -> ImageSlide(requireContext(), it.uri, it.contentType, it.size, it.width, it.height, it.isBorderless, it.caption.orNull(), null, it.transformProperties.orNull()) - MediaUtil.isDocumentType(it.contentType) -> { DocumentSlide(requireContext(), it.uri, it.contentType, it.size, it.fileName.orNull()) } + MediaUtil.isDocumentType(it.contentType) -> { + DocumentSlide(requireContext(), it.uri, it.contentType, it.size, it.fileName.orNull()) + } + else -> { Log.w(TAG, "Asked to send an unexpected mimeType: '${it.contentType}'. Skipping.") null @@ -4142,7 +4147,13 @@ class ConversationFragment : .request(Manifest.permission.RECORD_AUDIO) .ifNecessary() .withRationaleDialog(getString(R.string.ConversationActivity_allow_access_microphone), getString(R.string.ConversationActivity_to_send_voice_messages_allow_signal_access_to_your_microphone), R.drawable.ic_mic_24) - .withPermanentDenialDialog(getString(R.string.ConversationActivity_signal_requires_the_microphone_permission_in_order_to_send_audio_messages), null, R.string.ConversationActivity_allow_access_microphone, R.string.ConversationActivity_signal_to_send_audio_messages, this@ConversationFragment.parentFragmentManager) + .withPermanentDenialDialog( + getString(R.string.ConversationActivity_signal_requires_the_microphone_permission_in_order_to_send_audio_messages), + null, + R.string.ConversationActivity_allow_access_microphone, + R.string.ConversationActivity_signal_to_send_audio_messages, + this@ConversationFragment.parentFragmentManager + ) .onAnyDenied { Toast.makeText(this@ConversationFragment.requireContext(), R.string.ConversationActivity_signal_needs_microphone_access_voice_message, Toast.LENGTH_LONG).show() } .execute() } diff --git a/app/src/main/java/org/thoughtcrime/securesms/conversationlist/ConversationListFragment.java b/app/src/main/java/org/thoughtcrime/securesms/conversationlist/ConversationListFragment.java index cb920dc5bd..ebd3b720b6 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/conversationlist/ConversationListFragment.java +++ b/app/src/main/java/org/thoughtcrime/securesms/conversationlist/ConversationListFragment.java @@ -970,8 +970,8 @@ public class ConversationListFragment extends MainFragment implements ActionMode startupStopwatch.split("data-set"); SignalLocalMetrics.ColdStart.onConversationListDataLoaded(); defaultAdapter.unregisterAdapterDataObserver(this); - if (requireActivity() instanceof MainActivity) { - ((MainActivity) requireActivity()).onFirstRender(); + if (requireActivity() instanceof MainNavigator.NavigatorProvider) { + ((MainNavigator.NavigatorProvider) requireActivity()).onFirstRender(); } list.addOnLayoutChangeListener(new View.OnLayoutChangeListener() { @Override diff --git a/app/src/main/java/org/thoughtcrime/securesms/migrations/UserNotificationMigrationJob.java b/app/src/main/java/org/thoughtcrime/securesms/migrations/UserNotificationMigrationJob.java index 1f8c92d1c7..22ca4a10de 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/migrations/UserNotificationMigrationJob.java +++ b/app/src/main/java/org/thoughtcrime/securesms/migrations/UserNotificationMigrationJob.java @@ -10,8 +10,8 @@ import androidx.core.app.NotificationCompat; import androidx.core.app.NotificationManagerCompat; import androidx.core.app.TaskStackBuilder; +import org.signal.core.util.SetUtil; import org.signal.core.util.logging.Log; -import org.thoughtcrime.securesms.MainActivity; import org.thoughtcrime.securesms.NewConversationActivity; import org.thoughtcrime.securesms.R; import org.thoughtcrime.securesms.conversationlist.model.ConversationFilter; @@ -19,10 +19,10 @@ import org.thoughtcrime.securesms.database.SignalDatabase; import org.thoughtcrime.securesms.database.ThreadTable; import org.thoughtcrime.securesms.jobmanager.Job; import org.thoughtcrime.securesms.keyvalue.SignalStore; +import org.thoughtcrime.securesms.MainActivity; import org.thoughtcrime.securesms.notifications.NotificationChannels; import org.thoughtcrime.securesms.notifications.NotificationIds; import org.thoughtcrime.securesms.recipients.RecipientId; -import org.signal.core.util.SetUtil; import org.thoughtcrime.securesms.util.TextSecurePreferences; import java.util.List;