That fuck shit the fascists are using
1/*
2 * Copyright 2023 Signal Messenger, LLC
3 * SPDX-License-Identifier: AGPL-3.0-only
4 */
5
6package org.tm.archive.components
7
8import android.os.Bundle
9import android.view.LayoutInflater
10import android.view.View
11import android.view.ViewGroup
12import android.widget.Toast
13import androidx.appcompat.app.AppCompatActivity
14import androidx.core.os.bundleOf
15import androidx.fragment.app.viewModels
16import androidx.lifecycle.Lifecycle
17import org.signal.core.util.ResourceUtil
18import org.signal.core.util.concurrent.LifecycleDisposable
19import org.tm.archive.R
20import org.tm.archive.databinding.PromptLogsBottomSheetBinding
21import org.tm.archive.dependencies.ApplicationDependencies
22import org.tm.archive.keyvalue.SignalStore
23import org.tm.archive.util.BottomSheetUtil
24import org.tm.archive.util.CommunicationActions
25import org.tm.archive.util.NetworkUtil
26import org.tm.archive.util.SupportEmailUtil
27
28class DebugLogsPromptDialogFragment : FixedRoundedCornerBottomSheetDialogFragment() {
29
30 companion object {
31 private const val KEY_PURPOSE = "purpose"
32
33 @JvmStatic
34 fun show(activity: AppCompatActivity, purpose: Purpose) {
35 if (!activity.lifecycle.currentState.isAtLeast(Lifecycle.State.RESUMED)) {
36 return
37 }
38
39 if (NetworkUtil.isConnected(activity) && activity.supportFragmentManager.findFragmentByTag(BottomSheetUtil.STANDARD_BOTTOM_SHEET_FRAGMENT_TAG) == null) {
40 DebugLogsPromptDialogFragment().apply {
41 arguments = bundleOf(
42 KEY_PURPOSE to purpose.serialized
43 )
44 }.show(activity.supportFragmentManager, BottomSheetUtil.STANDARD_BOTTOM_SHEET_FRAGMENT_TAG)
45
46 when (purpose) {
47 Purpose.NOTIFICATIONS -> SignalStore.uiHints().lastNotificationLogsPrompt = System.currentTimeMillis()
48 Purpose.CRASH -> SignalStore.uiHints().lastCrashPrompt = System.currentTimeMillis()
49 }
50 }
51 }
52 }
53
54 override val peekHeightPercentage: Float = 0.66f
55 override val themeResId: Int = R.style.Widget_Signal_FixedRoundedCorners_Messages
56
57 private val binding by ViewBinderDelegate(PromptLogsBottomSheetBinding::bind)
58
59 private val viewModel: PromptLogsViewModel by viewModels(
60 factoryProducer = {
61 val purpose = Purpose.deserialize(requireArguments().getInt(KEY_PURPOSE))
62 PromptLogsViewModel.Factory(ApplicationDependencies.getApplication(), purpose)
63 }
64 )
65
66 private val disposables: LifecycleDisposable = LifecycleDisposable()
67
68 override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View {
69 return inflater.inflate(R.layout.prompt_logs_bottom_sheet, container, false)
70 }
71
72 override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
73 disposables.bindTo(viewLifecycleOwner)
74
75 val purpose = Purpose.deserialize(requireArguments().getInt(KEY_PURPOSE))
76
77 when (purpose) {
78 Purpose.NOTIFICATIONS -> {
79 binding.title.setText(R.string.PromptLogsSlowNotificationsDialog__title)
80 }
81 Purpose.CRASH -> {
82 binding.title.setText(R.string.PromptLogsSlowNotificationsDialog__title_crash)
83 }
84 }
85
86 binding.submit.setOnClickListener {
87 val progressDialog = SignalProgressDialog.show(requireContext())
88 disposables += viewModel.submitLogs().subscribe({ result ->
89 submitLogs(result, purpose)
90 progressDialog.dismiss()
91 dismissAllowingStateLoss()
92 }, { _ ->
93 Toast.makeText(requireContext(), getString(R.string.HelpFragment__could_not_upload_logs), Toast.LENGTH_LONG).show()
94 progressDialog.dismiss()
95 dismissAllowingStateLoss()
96 })
97 }
98
99 binding.decline.setOnClickListener {
100 if (purpose == Purpose.NOTIFICATIONS) {
101 SignalStore.uiHints().markDeclinedShareNotificationLogs()
102 }
103
104 dismissAllowingStateLoss()
105 }
106 }
107
108 override fun onStart() {
109 super.onStart()
110 viewModel.onVisible()
111 }
112
113 private fun submitLogs(debugLog: String, purpose: Purpose) {
114 CommunicationActions.openEmail(
115 requireContext(),
116 SupportEmailUtil.getSupportEmailAddress(requireContext()),
117 getString(R.string.DebugLogsPromptDialogFragment__signal_android_support_request),
118 getEmailBody(debugLog, purpose)
119 )
120 }
121
122 private fun getEmailBody(debugLog: String?, purpose: Purpose): String {
123 val suffix = StringBuilder()
124
125 if (debugLog != null) {
126 suffix.append("\n")
127 suffix.append(getString(R.string.HelpFragment__debug_log))
128 suffix.append(" ")
129 suffix.append(debugLog)
130 }
131
132 val category = when (purpose) {
133 Purpose.NOTIFICATIONS -> ResourceUtil.getEnglishResources(requireContext()).getString(R.string.DebugLogsPromptDialogFragment__slow_notifications_category)
134 Purpose.CRASH -> ResourceUtil.getEnglishResources(requireContext()).getString(R.string.DebugLogsPromptDialogFragment__crash_category)
135 }
136
137 return SupportEmailUtil.generateSupportEmailBody(
138 requireContext(),
139 R.string.DebugLogsPromptDialogFragment__signal_android_support_request,
140 " - $category",
141 "\n\n",
142 suffix.toString()
143 )
144 }
145
146 enum class Purpose(val serialized: Int) {
147
148 NOTIFICATIONS(1), CRASH(2);
149
150 companion object {
151 fun deserialize(serialized: Int): Purpose {
152 for (value in values()) {
153 if (value.serialized == serialized) {
154 return value
155 }
156 }
157
158 throw IllegalArgumentException("Invalid value: $serialized")
159 }
160 }
161 }
162}