kopia lustrzana https://github.com/ryukoposting/Signal-Android
749 wiersze
26 KiB
Kotlin
749 wiersze
26 KiB
Kotlin
package org.thoughtcrime.securesms.components.settings.app.internal
|
|
|
|
import android.content.ClipData
|
|
import android.content.ClipboardManager
|
|
import android.content.Context
|
|
import android.content.DialogInterface
|
|
import android.os.Bundle
|
|
import android.view.View
|
|
import android.widget.Toast
|
|
import androidx.lifecycle.ViewModelProvider
|
|
import androidx.navigation.fragment.findNavController
|
|
import androidx.recyclerview.widget.LinearLayoutManager
|
|
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
|
import org.signal.core.util.AppUtil
|
|
import org.signal.core.util.concurrent.SignalExecutors
|
|
import org.signal.core.util.concurrent.SimpleTask
|
|
import org.signal.ringrtc.CallManager
|
|
import org.thoughtcrime.securesms.BuildConfig
|
|
import org.thoughtcrime.securesms.R
|
|
import org.thoughtcrime.securesms.components.settings.DSLConfiguration
|
|
import org.thoughtcrime.securesms.components.settings.DSLSettingsFragment
|
|
import org.thoughtcrime.securesms.components.settings.DSLSettingsText
|
|
import org.thoughtcrime.securesms.components.settings.configure
|
|
import org.thoughtcrime.securesms.database.LocalMetricsDatabase
|
|
import org.thoughtcrime.securesms.database.LogDatabase
|
|
import org.thoughtcrime.securesms.database.MegaphoneDatabase
|
|
import org.thoughtcrime.securesms.database.SignalDatabase
|
|
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies
|
|
import org.thoughtcrime.securesms.jobmanager.JobTracker
|
|
import org.thoughtcrime.securesms.jobs.DownloadLatestEmojiDataJob
|
|
import org.thoughtcrime.securesms.jobs.EmojiSearchIndexDownloadJob
|
|
import org.thoughtcrime.securesms.jobs.PnpInitializeDevicesJob
|
|
import org.thoughtcrime.securesms.jobs.RefreshAttributesJob
|
|
import org.thoughtcrime.securesms.jobs.RefreshOwnProfileJob
|
|
import org.thoughtcrime.securesms.jobs.RemoteConfigRefreshJob
|
|
import org.thoughtcrime.securesms.jobs.RetrieveRemoteAnnouncementsJob
|
|
import org.thoughtcrime.securesms.jobs.RotateProfileKeyJob
|
|
import org.thoughtcrime.securesms.jobs.StorageForcePushJob
|
|
import org.thoughtcrime.securesms.jobs.SubscriptionKeepAliveJob
|
|
import org.thoughtcrime.securesms.jobs.SubscriptionReceiptRequestResponseJob
|
|
import org.thoughtcrime.securesms.keyvalue.SignalStore
|
|
import org.thoughtcrime.securesms.megaphone.MegaphoneRepository
|
|
import org.thoughtcrime.securesms.megaphone.Megaphones
|
|
import org.thoughtcrime.securesms.payments.DataExportUtil
|
|
import org.thoughtcrime.securesms.storage.StorageSyncHelper
|
|
import org.thoughtcrime.securesms.util.ConversationUtil
|
|
import org.thoughtcrime.securesms.util.FeatureFlags
|
|
import org.thoughtcrime.securesms.util.adapter.mapping.MappingAdapter
|
|
import org.thoughtcrime.securesms.util.navigation.safeNavigate
|
|
import java.util.Optional
|
|
import java.util.concurrent.TimeUnit
|
|
import kotlin.math.max
|
|
import kotlin.time.Duration.Companion.seconds
|
|
|
|
class InternalSettingsFragment : DSLSettingsFragment(R.string.preferences__internal_preferences) {
|
|
|
|
private lateinit var viewModel: InternalSettingsViewModel
|
|
|
|
private var scrollToPosition: Int = 0
|
|
private val layoutManager: LinearLayoutManager?
|
|
get() = recyclerView?.layoutManager as? LinearLayoutManager
|
|
|
|
override fun onPause() {
|
|
super.onPause()
|
|
val firstVisiblePosition: Int? = layoutManager?.findFirstVisibleItemPosition()
|
|
if (firstVisiblePosition != null) {
|
|
SignalStore.internalValues().lastScrollPosition = firstVisiblePosition
|
|
}
|
|
}
|
|
|
|
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
|
super.onViewCreated(view, savedInstanceState)
|
|
scrollToPosition = SignalStore.internalValues().lastScrollPosition
|
|
}
|
|
|
|
override fun bindAdapter(adapter: MappingAdapter) {
|
|
val repository = InternalSettingsRepository(requireContext())
|
|
val factory = InternalSettingsViewModel.Factory(repository)
|
|
viewModel = ViewModelProvider(this, factory)[InternalSettingsViewModel::class.java]
|
|
|
|
viewModel.state.observe(viewLifecycleOwner) {
|
|
adapter.submitList(getConfiguration(it).toMappingModelList()) {
|
|
if (scrollToPosition != 0) {
|
|
layoutManager?.scrollToPositionWithOffset(scrollToPosition, 0)
|
|
scrollToPosition = 0
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
private fun getConfiguration(state: InternalSettingsState): DSLConfiguration {
|
|
return configure {
|
|
sectionHeaderPref(DSLSettingsText.from("Account"))
|
|
|
|
clickPref(
|
|
title = DSLSettingsText.from("Refresh attributes"),
|
|
summary = DSLSettingsText.from("Forces a write of capabilities on to the server followed by a read."),
|
|
onClick = {
|
|
refreshAttributes()
|
|
}
|
|
)
|
|
|
|
clickPref(
|
|
title = DSLSettingsText.from("Refresh profile"),
|
|
summary = DSLSettingsText.from("Forces a refresh of your own profile."),
|
|
onClick = {
|
|
refreshProfile()
|
|
}
|
|
)
|
|
|
|
clickPref(
|
|
title = DSLSettingsText.from("Rotate profile key"),
|
|
summary = DSLSettingsText.from("Creates a new versioned profile, and triggers an update of any GV2 group you belong to."),
|
|
onClick = {
|
|
rotateProfileKey()
|
|
}
|
|
)
|
|
|
|
clickPref(
|
|
title = DSLSettingsText.from("Refresh remote config"),
|
|
summary = DSLSettingsText.from("Forces a refresh of the remote config locally instead of waiting for the elapsed time."),
|
|
onClick = {
|
|
refreshRemoteValues()
|
|
}
|
|
)
|
|
|
|
dividerPref()
|
|
|
|
sectionHeaderPref(DSLSettingsText.from("Miscellaneous"))
|
|
|
|
switchPref(
|
|
title = DSLSettingsText.from("'Internal Details' button"),
|
|
summary = DSLSettingsText.from("Show a button in conversation settings that lets you see more information about a user."),
|
|
isChecked = state.seeMoreUserDetails,
|
|
onClick = {
|
|
viewModel.setSeeMoreUserDetails(!state.seeMoreUserDetails)
|
|
}
|
|
)
|
|
|
|
switchPref(
|
|
title = DSLSettingsText.from("Shake to Report"),
|
|
summary = DSLSettingsText.from("Shake your phone to easily submit and share a debug log."),
|
|
isChecked = state.shakeToReport,
|
|
onClick = {
|
|
viewModel.setShakeToReport(!state.shakeToReport)
|
|
}
|
|
)
|
|
|
|
clickPref(
|
|
title = DSLSettingsText.from("Clear keep longer logs"),
|
|
onClick = {
|
|
clearKeepLongerLogs()
|
|
}
|
|
)
|
|
|
|
dividerPref()
|
|
|
|
sectionHeaderPref(DSLSettingsText.from("Payments"))
|
|
|
|
clickPref(
|
|
title = DSLSettingsText.from("Copy payments data"),
|
|
summary = DSLSettingsText.from("Copy all payment records to clipboard."),
|
|
onClick = {
|
|
copyPaymentsDataToClipboard()
|
|
}
|
|
)
|
|
|
|
dividerPref()
|
|
|
|
sectionHeaderPref(DSLSettingsText.from("Storage Service"))
|
|
|
|
switchPref(
|
|
title = DSLSettingsText.from("Disable syncing"),
|
|
summary = DSLSettingsText.from("Prevent syncing any data to/from storage service."),
|
|
isChecked = state.disableStorageService,
|
|
onClick = {
|
|
viewModel.setDisableStorageService(!state.disableStorageService)
|
|
}
|
|
)
|
|
|
|
clickPref(
|
|
title = DSLSettingsText.from("Sync now"),
|
|
summary = DSLSettingsText.from("Enqueue a normal storage service sync."),
|
|
onClick = {
|
|
enqueueStorageServiceSync()
|
|
}
|
|
)
|
|
|
|
clickPref(
|
|
title = DSLSettingsText.from("Overwrite remote data"),
|
|
summary = DSLSettingsText.from("Forces remote storage to match the local device state."),
|
|
onClick = {
|
|
enqueueStorageServiceForcePush()
|
|
}
|
|
)
|
|
|
|
dividerPref()
|
|
|
|
sectionHeaderPref(DSLSettingsText.from("Groups V2"))
|
|
|
|
switchPref(
|
|
title = DSLSettingsText.from("Force invites"),
|
|
summary = DSLSettingsText.from("Members will not be added directly to a GV2 even if they could be."),
|
|
isChecked = state.gv2forceInvites,
|
|
onClick = {
|
|
viewModel.setGv2ForceInvites(!state.gv2forceInvites)
|
|
}
|
|
)
|
|
|
|
switchPref(
|
|
title = DSLSettingsText.from("Ignore server changes"),
|
|
summary = DSLSettingsText.from("Changes in server's response will be ignored, causing passive voice update messages if P2P is also ignored."),
|
|
isChecked = state.gv2ignoreServerChanges,
|
|
onClick = {
|
|
viewModel.setGv2IgnoreServerChanges(!state.gv2ignoreServerChanges)
|
|
}
|
|
)
|
|
|
|
switchPref(
|
|
title = DSLSettingsText.from("Ignore P2P changes"),
|
|
summary = DSLSettingsText.from("Changes sent P2P will be ignored. In conjunction with ignoring server changes, will cause passive voice."),
|
|
isChecked = state.gv2ignoreP2PChanges,
|
|
onClick = {
|
|
viewModel.setGv2IgnoreP2PChanges(!state.gv2ignoreP2PChanges)
|
|
}
|
|
)
|
|
|
|
dividerPref()
|
|
|
|
sectionHeaderPref(DSLSettingsText.from("Network"))
|
|
|
|
switchPref(
|
|
title = DSLSettingsText.from("Force websocket mode"),
|
|
summary = DSLSettingsText.from("Pretend you have no Play Services. Ignores websocket messages and keeps the websocket open in a foreground service. You have to manually force-stop the app for changes to take effect."),
|
|
isChecked = state.forceWebsocketMode,
|
|
onClick = {
|
|
viewModel.setForceWebsocketMode(!state.forceWebsocketMode)
|
|
SimpleTask.run({
|
|
val jobState = ApplicationDependencies.getJobManager().runSynchronously(RefreshAttributesJob(), 10.seconds.inWholeMilliseconds)
|
|
return@run jobState.isPresent && jobState.get().isComplete
|
|
}, { success ->
|
|
if (success) {
|
|
Toast.makeText(context, "Successfully refreshed attributes. Force-stop the app for changes to take effect.", Toast.LENGTH_SHORT).show()
|
|
} else {
|
|
Toast.makeText(context, "Failed to refresh attributes.", Toast.LENGTH_SHORT).show()
|
|
}
|
|
})
|
|
}
|
|
)
|
|
|
|
switchPref(
|
|
title = DSLSettingsText.from("Allow censorship circumvention toggle"),
|
|
summary = DSLSettingsText.from("Allow changing the censorship circumvention toggle regardless of network connectivity."),
|
|
isChecked = state.allowCensorshipSetting,
|
|
onClick = {
|
|
viewModel.setAllowCensorshipSetting(!state.allowCensorshipSetting)
|
|
}
|
|
)
|
|
|
|
dividerPref()
|
|
|
|
sectionHeaderPref(DSLSettingsText.from("Conversations and Shortcuts"))
|
|
|
|
clickPref(
|
|
title = DSLSettingsText.from("Delete all dynamic shortcuts"),
|
|
summary = DSLSettingsText.from("Click to delete all dynamic shortcuts"),
|
|
onClick = {
|
|
deleteAllDynamicShortcuts()
|
|
}
|
|
)
|
|
|
|
dividerPref()
|
|
|
|
sectionHeaderPref(DSLSettingsText.from("Emoji"))
|
|
|
|
val emojiSummary = if (state.emojiVersion == null) {
|
|
"Use built-in emoji set"
|
|
} else {
|
|
"Current version: ${state.emojiVersion.version} at density ${state.emojiVersion.density}"
|
|
}
|
|
|
|
switchPref(
|
|
title = DSLSettingsText.from("Use built-in emoji set"),
|
|
summary = DSLSettingsText.from(emojiSummary),
|
|
isChecked = state.useBuiltInEmojiSet,
|
|
onClick = {
|
|
viewModel.setUseBuiltInEmoji(!state.useBuiltInEmojiSet)
|
|
}
|
|
)
|
|
|
|
clickPref(
|
|
title = DSLSettingsText.from("Force emoji download"),
|
|
summary = DSLSettingsText.from("Download the latest emoji set if it\\'s newer than what we have."),
|
|
onClick = {
|
|
ApplicationDependencies.getJobManager().add(DownloadLatestEmojiDataJob(true))
|
|
}
|
|
)
|
|
|
|
clickPref(
|
|
title = DSLSettingsText.from("Force search index download"),
|
|
summary = DSLSettingsText.from("Download the latest emoji search index if it\\'s newer than what we have."),
|
|
onClick = {
|
|
EmojiSearchIndexDownloadJob.scheduleImmediately()
|
|
}
|
|
)
|
|
|
|
dividerPref()
|
|
|
|
sectionHeaderPref(DSLSettingsText.from("Sender Key"))
|
|
|
|
clickPref(
|
|
title = DSLSettingsText.from("Clear all state"),
|
|
summary = DSLSettingsText.from("Click to delete all sender key state"),
|
|
onClick = {
|
|
clearAllSenderKeyState()
|
|
}
|
|
)
|
|
|
|
clickPref(
|
|
title = DSLSettingsText.from("Clear shared state"),
|
|
summary = DSLSettingsText.from("Click to delete all sharing state"),
|
|
onClick = {
|
|
clearAllSenderKeySharedState()
|
|
}
|
|
)
|
|
|
|
switchPref(
|
|
title = DSLSettingsText.from("Remove 2 person minimum"),
|
|
summary = DSLSettingsText.from("Remove the requirement that you need at least 2 recipients to use sender key."),
|
|
isChecked = state.removeSenderKeyMinimium,
|
|
onClick = {
|
|
viewModel.setRemoveSenderKeyMinimum(!state.removeSenderKeyMinimium)
|
|
}
|
|
)
|
|
|
|
switchPref(
|
|
title = DSLSettingsText.from("Delay resends"),
|
|
summary = DSLSettingsText.from("Delay resending messages in response to retry receipts by 10 seconds."),
|
|
isChecked = state.delayResends,
|
|
onClick = {
|
|
viewModel.setDelayResends(!state.delayResends)
|
|
}
|
|
)
|
|
|
|
dividerPref()
|
|
|
|
sectionHeaderPref(DSLSettingsText.from("Local Metrics"))
|
|
|
|
clickPref(
|
|
title = DSLSettingsText.from("Clear local metrics"),
|
|
summary = DSLSettingsText.from("Click to clear all local metrics state."),
|
|
onClick = {
|
|
clearAllLocalMetricsState()
|
|
}
|
|
)
|
|
|
|
dividerPref()
|
|
|
|
sectionHeaderPref(DSLSettingsText.from("Group call server"))
|
|
|
|
radioPref(
|
|
title = DSLSettingsText.from("Production server"),
|
|
summary = DSLSettingsText.from(BuildConfig.SIGNAL_SFU_URL),
|
|
isChecked = state.callingServer == BuildConfig.SIGNAL_SFU_URL,
|
|
onClick = {
|
|
viewModel.setInternalGroupCallingServer(BuildConfig.SIGNAL_SFU_URL)
|
|
}
|
|
)
|
|
|
|
BuildConfig.SIGNAL_SFU_INTERNAL_NAMES.zip(BuildConfig.SIGNAL_SFU_INTERNAL_URLS)
|
|
.forEach { (name, server) ->
|
|
radioPref(
|
|
title = DSLSettingsText.from("$name server"),
|
|
summary = DSLSettingsText.from(server),
|
|
isChecked = state.callingServer == server,
|
|
onClick = {
|
|
viewModel.setInternalGroupCallingServer(server)
|
|
}
|
|
)
|
|
}
|
|
|
|
sectionHeaderPref(DSLSettingsText.from("Calling options"))
|
|
|
|
radioListPref(
|
|
title = DSLSettingsText.from("Audio processing method"),
|
|
listItems = CallManager.AudioProcessingMethod.values().map { it.name }.toTypedArray(),
|
|
selected = CallManager.AudioProcessingMethod.values().indexOf(state.callingAudioProcessingMethod),
|
|
onSelected = {
|
|
viewModel.setInternalCallingAudioProcessingMethod(CallManager.AudioProcessingMethod.values()[it])
|
|
}
|
|
)
|
|
|
|
radioListPref(
|
|
title = DSLSettingsText.from("Bandwidth mode"),
|
|
listItems = CallManager.BandwidthMode.values().map { it.name }.toTypedArray(),
|
|
selected = CallManager.BandwidthMode.values().indexOf(state.callingBandwidthMode),
|
|
onSelected = {
|
|
viewModel.setInternalCallingBandwidthMode(CallManager.BandwidthMode.values()[it])
|
|
}
|
|
)
|
|
|
|
switchPref(
|
|
title = DSLSettingsText.from("Disable Telecom integration"),
|
|
isChecked = state.callingDisableTelecom,
|
|
onClick = {
|
|
viewModel.setInternalCallingDisableTelecom(!state.callingDisableTelecom)
|
|
}
|
|
)
|
|
|
|
if (SignalStore.donationsValues().getSubscriber() != null) {
|
|
dividerPref()
|
|
|
|
sectionHeaderPref(DSLSettingsText.from("Badges"))
|
|
|
|
clickPref(
|
|
title = DSLSettingsText.from("Enqueue redemption."),
|
|
onClick = {
|
|
enqueueSubscriptionRedemption()
|
|
}
|
|
)
|
|
|
|
clickPref(
|
|
title = DSLSettingsText.from("Enqueue keep-alive."),
|
|
onClick = {
|
|
enqueueSubscriptionKeepAlive()
|
|
}
|
|
)
|
|
|
|
clickPref(
|
|
title = DSLSettingsText.from("Set error state."),
|
|
onClick = {
|
|
findNavController().safeNavigate(InternalSettingsFragmentDirections.actionInternalSettingsFragmentToDonorErrorConfigurationFragment())
|
|
}
|
|
)
|
|
|
|
clickPref(
|
|
title = DSLSettingsText.from("Clear keep-alive timestamps"),
|
|
onClick = {
|
|
SignalStore.donationsValues().subscriptionEndOfPeriodRedemptionStarted = 0L
|
|
SignalStore.donationsValues().subscriptionEndOfPeriodConversionStarted = 0L
|
|
SignalStore.donationsValues().setLastEndOfPeriod(0L)
|
|
Toast.makeText(context, "Cleared", Toast.LENGTH_SHORT).show()
|
|
}
|
|
)
|
|
}
|
|
|
|
dividerPref()
|
|
|
|
sectionHeaderPref(DSLSettingsText.from("Release channel"))
|
|
|
|
clickPref(
|
|
title = DSLSettingsText.from("Set last version seen back 10 versions"),
|
|
onClick = {
|
|
SignalStore.releaseChannelValues().highestVersionNoteReceived = max(SignalStore.releaseChannelValues().highestVersionNoteReceived - 10, 0)
|
|
}
|
|
)
|
|
|
|
clickPref(
|
|
title = DSLSettingsText.from("Reset donation megaphone"),
|
|
onClick = {
|
|
SignalDatabase.remoteMegaphones.debugRemoveAll()
|
|
MegaphoneDatabase.getInstance(ApplicationDependencies.getApplication()).let {
|
|
it.delete(Megaphones.Event.REMOTE_MEGAPHONE)
|
|
it.markFirstVisible(Megaphones.Event.DONATE_Q2_2022, System.currentTimeMillis() - TimeUnit.DAYS.toMillis(31))
|
|
}
|
|
// Force repository database cache refresh
|
|
MegaphoneRepository(ApplicationDependencies.getApplication()).onFirstEverAppLaunch()
|
|
}
|
|
)
|
|
|
|
clickPref(
|
|
title = DSLSettingsText.from("Fetch release channel"),
|
|
onClick = {
|
|
SignalStore.releaseChannelValues().previousManifestMd5 = ByteArray(0)
|
|
RetrieveRemoteAnnouncementsJob.enqueue(force = true)
|
|
}
|
|
)
|
|
|
|
clickPref(
|
|
title = DSLSettingsText.from("Add sample note"),
|
|
onClick = {
|
|
viewModel.addSampleReleaseNote()
|
|
}
|
|
)
|
|
|
|
dividerPref()
|
|
|
|
sectionHeaderPref(DSLSettingsText.from("CDS"))
|
|
|
|
clickPref(
|
|
title = DSLSettingsText.from("Clear history"),
|
|
summary = DSLSettingsText.from("Clears all CDS history, meaning the next sync will consider all numbers to be new."),
|
|
onClick = {
|
|
clearCdsHistory()
|
|
}
|
|
)
|
|
|
|
clickPref(
|
|
title = DSLSettingsText.from("Clear all service IDs"),
|
|
summary = DSLSettingsText.from("Clears all known service IDs (except your own) for people that have phone numbers. Do not use on your personal device!"),
|
|
onClick = {
|
|
clearAllServiceIds()
|
|
}
|
|
)
|
|
|
|
clickPref(
|
|
title = DSLSettingsText.from("Clear all profile keys"),
|
|
summary = DSLSettingsText.from("Clears all known profile keys (except your own). Do not use on your personal device!"),
|
|
onClick = {
|
|
clearAllProfileKeys()
|
|
}
|
|
)
|
|
|
|
dividerPref()
|
|
|
|
sectionHeaderPref(DSLSettingsText.from("Stories"))
|
|
|
|
clickPref(
|
|
title = DSLSettingsText.from("Clear onboarding state"),
|
|
summary = DSLSettingsText.from("Clears onboarding flag and triggers download of onboarding stories."),
|
|
isEnabled = state.canClearOnboardingState,
|
|
onClick = {
|
|
viewModel.onClearOnboardingState()
|
|
}
|
|
)
|
|
|
|
clickPref(
|
|
title = DSLSettingsText.from("Clear choose initial my story privacy state"),
|
|
isEnabled = true,
|
|
onClick = {
|
|
SignalStore.storyValues().userHasBeenNotifiedAboutStories = false
|
|
}
|
|
)
|
|
|
|
clickPref(
|
|
title = DSLSettingsText.from("Clear first time navigation state"),
|
|
isEnabled = true,
|
|
onClick = {
|
|
SignalStore.storyValues().userHasSeenFirstNavView = false
|
|
}
|
|
)
|
|
|
|
clickPref(
|
|
title = DSLSettingsText.from("Stories dialog launcher"),
|
|
onClick = {
|
|
findNavController().safeNavigate(InternalSettingsFragmentDirections.actionInternalSettingsFragmentToStoryDialogsLauncherFragment())
|
|
}
|
|
)
|
|
|
|
dividerPref()
|
|
|
|
sectionHeaderPref(DSLSettingsText.from("PNP"))
|
|
|
|
clickPref(
|
|
title = DSLSettingsText.from("Trigger No-Op Change Number"),
|
|
summary = DSLSettingsText.from("Mimics the 'Hello world' event"),
|
|
isEnabled = true,
|
|
onClick = {
|
|
SimpleTask.run(viewLifecycleOwner.lifecycle, {
|
|
ApplicationDependencies.getJobManager().runSynchronously(PnpInitializeDevicesJob(), 10.seconds.inWholeMilliseconds)
|
|
}, { state ->
|
|
if (state.isPresent) {
|
|
Toast.makeText(context, "Job finished with result: ${state.get()}!", Toast.LENGTH_SHORT).show()
|
|
viewModel.refresh()
|
|
} else {
|
|
Toast.makeText(context, "Job timed out after 10 seconds!", Toast.LENGTH_SHORT).show()
|
|
}
|
|
})
|
|
}
|
|
)
|
|
|
|
clickPref(
|
|
title = DSLSettingsText.from("Reset 'PNP initialized' state"),
|
|
summary = DSLSettingsText.from("Current initialized state: ${state.pnpInitialized}"),
|
|
isEnabled = state.pnpInitialized,
|
|
onClick = {
|
|
viewModel.resetPnpInitializedState()
|
|
}
|
|
)
|
|
|
|
clickPref(
|
|
title = DSLSettingsText.from("Clear Username education ui hint"),
|
|
onClick = {
|
|
SignalStore.uiHints().clearHasSeenUsernameEducation()
|
|
}
|
|
)
|
|
|
|
if (FeatureFlags.chatFilters()) {
|
|
dividerPref()
|
|
sectionHeaderPref(DSLSettingsText.from("Chat Filters"))
|
|
clickPref(
|
|
title = DSLSettingsText.from("Reset pull to refresh tip count"),
|
|
onClick = {
|
|
SignalStore.uiHints().resetNeverDisplayPullToRefreshCount()
|
|
}
|
|
)
|
|
}
|
|
}
|
|
}
|
|
|
|
private fun copyPaymentsDataToClipboard() {
|
|
MaterialAlertDialogBuilder(requireContext())
|
|
.setMessage(
|
|
"""
|
|
Local payments history will be copied to the clipboard.
|
|
It may therefore compromise privacy.
|
|
However, no private keys will be copied.
|
|
""".trimIndent()
|
|
)
|
|
.setPositiveButton(
|
|
"Copy"
|
|
) { _: DialogInterface?, _: Int ->
|
|
val context: Context = ApplicationDependencies.getApplication()
|
|
val clipboard = context.getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager
|
|
|
|
SimpleTask.run<Any?>(
|
|
SignalExecutors.UNBOUNDED,
|
|
{
|
|
val tsv = DataExportUtil.createTsv()
|
|
val clip = ClipData.newPlainText(context.getString(R.string.app_name), tsv)
|
|
clipboard.setPrimaryClip(clip)
|
|
null
|
|
},
|
|
{
|
|
Toast.makeText(
|
|
context,
|
|
"Payments have been copied",
|
|
Toast.LENGTH_SHORT
|
|
).show()
|
|
}
|
|
)
|
|
}
|
|
.setNegativeButton(android.R.string.cancel, null)
|
|
.show()
|
|
}
|
|
|
|
private fun refreshAttributes() {
|
|
ApplicationDependencies.getJobManager()
|
|
.startChain(RefreshAttributesJob())
|
|
.then(RefreshOwnProfileJob())
|
|
.enqueue()
|
|
Toast.makeText(context, "Scheduled attribute refresh", Toast.LENGTH_SHORT).show()
|
|
}
|
|
|
|
private fun refreshProfile() {
|
|
ApplicationDependencies.getJobManager().add(RefreshOwnProfileJob())
|
|
Toast.makeText(context, "Scheduled profile refresh", Toast.LENGTH_SHORT).show()
|
|
}
|
|
|
|
private fun rotateProfileKey() {
|
|
ApplicationDependencies.getJobManager().add(RotateProfileKeyJob())
|
|
Toast.makeText(context, "Scheduled profile key rotation", Toast.LENGTH_SHORT).show()
|
|
}
|
|
|
|
private fun refreshRemoteValues() {
|
|
Toast.makeText(context, "Running remote config refresh, app will restart after completion.", Toast.LENGTH_LONG).show()
|
|
SignalExecutors.BOUNDED.execute {
|
|
val result: Optional<JobTracker.JobState> = ApplicationDependencies.getJobManager().runSynchronously(RemoteConfigRefreshJob(), TimeUnit.SECONDS.toMillis(10))
|
|
|
|
if (result.isPresent && result.get() == JobTracker.JobState.SUCCESS) {
|
|
AppUtil.restart(requireContext())
|
|
} else {
|
|
Toast.makeText(context, "Failed to refresh config remote config.", Toast.LENGTH_SHORT).show()
|
|
}
|
|
}
|
|
}
|
|
|
|
private fun enqueueStorageServiceSync() {
|
|
StorageSyncHelper.scheduleSyncForDataChange()
|
|
Toast.makeText(context, "Scheduled routine storage sync", Toast.LENGTH_SHORT).show()
|
|
}
|
|
|
|
private fun enqueueStorageServiceForcePush() {
|
|
ApplicationDependencies.getJobManager().add(StorageForcePushJob())
|
|
Toast.makeText(context, "Scheduled storage force push", Toast.LENGTH_SHORT).show()
|
|
}
|
|
|
|
private fun deleteAllDynamicShortcuts() {
|
|
ConversationUtil.clearAllShortcuts(requireContext())
|
|
Toast.makeText(context, "Deleted all dynamic shortcuts.", Toast.LENGTH_SHORT).show()
|
|
}
|
|
|
|
private fun clearAllSenderKeyState() {
|
|
SignalDatabase.senderKeys.deleteAll()
|
|
SignalDatabase.senderKeyShared.deleteAll()
|
|
Toast.makeText(context, "Deleted all sender key state.", Toast.LENGTH_SHORT).show()
|
|
}
|
|
|
|
private fun clearAllSenderKeySharedState() {
|
|
SignalDatabase.senderKeyShared.deleteAll()
|
|
Toast.makeText(context, "Deleted all sender key shared state.", Toast.LENGTH_SHORT).show()
|
|
}
|
|
|
|
private fun clearAllLocalMetricsState() {
|
|
LocalMetricsDatabase.getInstance(ApplicationDependencies.getApplication()).clear()
|
|
Toast.makeText(context, "Cleared all local metrics state.", Toast.LENGTH_SHORT).show()
|
|
}
|
|
|
|
private fun enqueueSubscriptionRedemption() {
|
|
SubscriptionReceiptRequestResponseJob.createSubscriptionContinuationJobChain().enqueue()
|
|
}
|
|
|
|
private fun enqueueSubscriptionKeepAlive() {
|
|
SubscriptionKeepAliveJob.enqueueAndTrackTime(System.currentTimeMillis())
|
|
}
|
|
|
|
private fun clearCdsHistory() {
|
|
SignalDatabase.cds.clearAll()
|
|
SignalStore.misc().cdsToken = null
|
|
Toast.makeText(context, "Cleared all CDS history.", Toast.LENGTH_SHORT).show()
|
|
}
|
|
|
|
private fun clearAllServiceIds() {
|
|
MaterialAlertDialogBuilder(requireContext())
|
|
.setTitle("Clear all serviceIds?")
|
|
.setMessage("Are you sure? Never do this on a non-test device.")
|
|
.setPositiveButton(android.R.string.ok) { _, _ ->
|
|
SignalDatabase.recipients.debugClearServiceIds()
|
|
Toast.makeText(context, "Cleared all service IDs.", Toast.LENGTH_SHORT).show()
|
|
}
|
|
.setNegativeButton(android.R.string.cancel) { d, _ ->
|
|
d.dismiss()
|
|
}
|
|
.show()
|
|
}
|
|
|
|
private fun clearAllProfileKeys() {
|
|
MaterialAlertDialogBuilder(requireContext())
|
|
.setTitle("Clear all profile keys?")
|
|
.setMessage("Are you sure? Never do this on a non-test device.")
|
|
.setPositiveButton(android.R.string.ok) { _, _ ->
|
|
SignalDatabase.recipients.debugClearProfileData()
|
|
Toast.makeText(context, "Cleared all profile keys.", Toast.LENGTH_SHORT).show()
|
|
}
|
|
.setNegativeButton(android.R.string.cancel) { d, _ ->
|
|
d.dismiss()
|
|
}
|
|
.show()
|
|
}
|
|
|
|
private fun clearKeepLongerLogs() {
|
|
SimpleTask.run({
|
|
LogDatabase.getInstance(requireActivity().application).clearKeepLonger()
|
|
}) {
|
|
Toast.makeText(requireContext(), "Cleared keep longer logs", Toast.LENGTH_SHORT).show()
|
|
}
|
|
}
|
|
}
|