Convert MainActivity to Kotlin.

This commit is contained in:
Alex Hart
2025-03-25 09:11:16 -03:00
committed by Cody Henthorne
parent e22c403b10
commit bf83914357
8 changed files with 291 additions and 318 deletions

View File

@@ -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;
}
}

View File

@@ -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))
}
}
}
}

View File

@@ -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();
}
}

View File

@@ -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()

View File

@@ -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()
}
}

View File

@@ -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()
}

View File

@@ -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

View File

@@ -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;