kopia lustrzana https://github.com/ryukoposting/Signal-Android
Add phased SMS removal UX.
rodzic
8a238a66e7
commit
b6db7e7af6
|
@ -666,6 +666,12 @@
|
|||
android:configChanges="touchscreen|keyboard|keyboardHidden|orientation|screenLayout|screenSize|uiMode"
|
||||
android:launchMode="singleTask" />
|
||||
|
||||
<activity android:name=".megaphone.SmsExportMegaphoneActivity"
|
||||
android:theme="@style/Theme.Signal.DayNight.NoActionBar"
|
||||
android:screenOrientation="portrait"
|
||||
android:configChanges="touchscreen|keyboard|keyboardHidden|orientation|screenLayout|screenSize|uiMode"
|
||||
android:launchMode="singleTask" />
|
||||
|
||||
<activity android:name=".ratelimit.RecaptchaProofActivity"
|
||||
android:theme="@style/Theme.Signal.DayNight.NoActionBar"
|
||||
android:configChanges="touchscreen|keyboard|keyboardHidden|orientation|screenLayout|screenSize|uiMode" />
|
||||
|
@ -685,6 +691,7 @@
|
|||
|
||||
<activity android:name=".exporter.flow.SmsExportActivity"
|
||||
android:theme="@style/Theme.Signal.DayNight.NoActionBar"
|
||||
android:launchMode="singleTask"
|
||||
android:screenOrientation="portrait"
|
||||
android:configChanges="touchscreen|keyboard|keyboardHidden|orientation|screenLayout|screenSize"/>
|
||||
|
||||
|
|
|
@ -103,6 +103,7 @@ public interface BindableConversationItem extends Unbindable, GiphyMp4Playable,
|
|||
void onDonateClicked();
|
||||
void onBlockJoinRequest(@NonNull Recipient recipient);
|
||||
void onRecipientNameClicked(@NonNull RecipientId target);
|
||||
void onInviteToSignalClicked();
|
||||
|
||||
/** @return true if handled, false if you want to let the normal url handling continue */
|
||||
boolean onUrlClicked(@NonNull String url);
|
||||
|
|
|
@ -28,6 +28,7 @@ import org.signal.core.util.logging.Log;
|
|||
import org.thoughtcrime.securesms.components.ContactFilterView;
|
||||
import org.thoughtcrime.securesms.contacts.ContactsCursorLoader.DisplayMode;
|
||||
import org.thoughtcrime.securesms.contacts.sync.ContactDiscovery;
|
||||
import org.thoughtcrime.securesms.keyvalue.SignalStore;
|
||||
import org.thoughtcrime.securesms.recipients.RecipientId;
|
||||
import org.thoughtcrime.securesms.util.DynamicNoActionBarTheme;
|
||||
import org.thoughtcrime.securesms.util.DynamicTheme;
|
||||
|
@ -69,8 +70,8 @@ public abstract class ContactSelectionActivity extends PassphraseRequiredActivit
|
|||
@Override
|
||||
protected void onCreate(Bundle icicle, boolean ready) {
|
||||
if (!getIntent().hasExtra(ContactSelectionListFragment.DISPLAY_MODE)) {
|
||||
int displayMode = Util.isDefaultSmsProvider(this) ? DisplayMode.FLAG_ALL
|
||||
: DisplayMode.FLAG_PUSH | DisplayMode.FLAG_ACTIVE_GROUPS | DisplayMode.FLAG_INACTIVE_GROUPS | DisplayMode.FLAG_SELF;
|
||||
boolean includeSms = Util.isDefaultSmsProvider(this) && SignalStore.misc().getSmsExportPhase().isSmsSupported();
|
||||
int displayMode = includeSms ? DisplayMode.FLAG_ALL : DisplayMode.FLAG_PUSH | DisplayMode.FLAG_ACTIVE_GROUPS | DisplayMode.FLAG_INACTIVE_GROUPS | DisplayMode.FLAG_SELF;
|
||||
getIntent().putExtra(ContactSelectionListFragment.DISPLAY_MODE, displayMode);
|
||||
}
|
||||
|
||||
|
|
|
@ -27,6 +27,7 @@ import org.thoughtcrime.securesms.contacts.ContactsCursorLoader.DisplayMode;
|
|||
import org.thoughtcrime.securesms.contacts.SelectedContact;
|
||||
import org.thoughtcrime.securesms.database.SignalDatabase;
|
||||
import org.thoughtcrime.securesms.groups.SelectionLimits;
|
||||
import org.thoughtcrime.securesms.keyvalue.SignalStore;
|
||||
import org.thoughtcrime.securesms.recipients.Recipient;
|
||||
import org.thoughtcrime.securesms.recipients.RecipientId;
|
||||
import org.thoughtcrime.securesms.sms.MessageSender;
|
||||
|
@ -118,7 +119,7 @@ public class InviteActivity extends PassphraseRequiredActivity implements Contac
|
|||
smsSendButton.setOnClickListener(new SmsSendClickListener());
|
||||
contactFilter.setOnFilterChangedListener(new ContactFilterChangedListener());
|
||||
|
||||
if (Util.isDefaultSmsProvider(this)) {
|
||||
if (Util.isDefaultSmsProvider(this) && SignalStore.misc().getSmsExportPhase().isSmsSupported()) {
|
||||
shareButton.setOnClickListener(new ShareClickListener());
|
||||
smsButton.setOnClickListener(new SmsClickListener());
|
||||
} else {
|
||||
|
|
|
@ -52,6 +52,7 @@ import org.thoughtcrime.securesms.recipients.RecipientId;
|
|||
import org.thoughtcrime.securesms.util.CommunicationActions;
|
||||
import org.thoughtcrime.securesms.util.FeatureFlags;
|
||||
import org.thoughtcrime.securesms.util.LifecycleDisposable;
|
||||
import org.thoughtcrime.securesms.util.Util;
|
||||
import org.thoughtcrime.securesms.util.views.SimpleProgressDialog;
|
||||
|
||||
import java.io.IOException;
|
||||
|
@ -104,12 +105,14 @@ public class NewConversationActivity extends ContactSelectionActivity
|
|||
|
||||
@Override
|
||||
public void onBeforeContactSelected(@NonNull Optional<RecipientId> recipientId, String number, @NonNull Consumer<Boolean> callback) {
|
||||
boolean smsSupported = Util.isDefaultSmsProvider(this) && SignalStore.misc().getSmsExportPhase().isSmsSupported();
|
||||
|
||||
if (recipientId.isPresent()) {
|
||||
launch(Recipient.resolved(recipientId.get()));
|
||||
} else {
|
||||
Log.i(TAG, "[onContactSelected] Maybe creating a new recipient.");
|
||||
|
||||
if (SignalStore.account().isRegistered() && NetworkConstraint.isMet(getApplication())) {
|
||||
if (SignalStore.account().isRegistered()) {
|
||||
Log.i(TAG, "[onContactSelected] Doing contact refresh.");
|
||||
|
||||
AlertDialog progress = SimpleProgressDialog.show(this);
|
||||
|
@ -124,15 +127,31 @@ public class NewConversationActivity extends ContactSelectionActivity
|
|||
resolved = Recipient.resolved(resolved.getId());
|
||||
} catch (IOException e) {
|
||||
Log.w(TAG, "[onContactSelected] Failed to refresh directory for new contact.");
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
return resolved;
|
||||
}, resolved -> {
|
||||
progress.dismiss();
|
||||
launch(resolved);
|
||||
|
||||
if (resolved != null) {
|
||||
if (smsSupported || resolved.isRegistered() && resolved.hasServiceId()) {
|
||||
launch(resolved);
|
||||
} else {
|
||||
new MaterialAlertDialogBuilder(this)
|
||||
.setMessage(getString(R.string.NewConversationActivity__s_is_not_a_signal_user, resolved.getDisplayName(this)))
|
||||
.setPositiveButton(android.R.string.ok, null)
|
||||
.show();
|
||||
}
|
||||
} else {
|
||||
new MaterialAlertDialogBuilder(this)
|
||||
.setMessage(R.string.NetworkFailure__network_error_check_your_connection_and_try_again)
|
||||
.setPositiveButton(android.R.string.ok, null)
|
||||
.show();
|
||||
}
|
||||
});
|
||||
} else {
|
||||
} else if (smsSupported) {
|
||||
launch(Recipient.external(this, number));
|
||||
}
|
||||
}
|
||||
|
@ -260,16 +279,20 @@ public class NewConversationActivity extends ContactSelectionActivity
|
|||
return null;
|
||||
}
|
||||
|
||||
return new ActionItem(
|
||||
R.drawable.ic_phone_right_24,
|
||||
getString(R.string.NewConversationActivity__audio_call),
|
||||
R.color.signal_colorOnSurface,
|
||||
() -> CommunicationActions.startVoiceCall(this, recipient)
|
||||
);
|
||||
if (recipient.isRegistered() || (Util.isDefaultSmsProvider(this) && SignalStore.misc().getSmsExportPhase().isSmsSupported())) {
|
||||
return new ActionItem(
|
||||
R.drawable.ic_phone_right_24,
|
||||
getString(R.string.NewConversationActivity__audio_call),
|
||||
R.color.signal_colorOnSurface,
|
||||
() -> CommunicationActions.startVoiceCall(this, recipient)
|
||||
);
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private @Nullable ActionItem createVideoCallActionItem(@NonNull Recipient recipient) {
|
||||
if (recipient.isSelf() || recipient.isMmsGroup()) {
|
||||
if (recipient.isSelf() || recipient.isMmsGroup() || !recipient.isRegistered()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
|
|
|
@ -6,10 +6,14 @@ import android.view.View
|
|||
import android.view.View.OnLongClickListener
|
||||
import android.view.ViewGroup
|
||||
import androidx.appcompat.widget.AppCompatImageButton
|
||||
import com.google.android.material.snackbar.Snackbar
|
||||
import org.signal.core.util.logging.Log
|
||||
import org.thoughtcrime.securesms.R
|
||||
import org.thoughtcrime.securesms.components.menu.ActionItem
|
||||
import org.thoughtcrime.securesms.components.menu.SignalContextMenu
|
||||
import org.thoughtcrime.securesms.conversation.MessageSendType
|
||||
import org.thoughtcrime.securesms.keyvalue.SignalStore
|
||||
import org.thoughtcrime.securesms.util.Util
|
||||
import org.thoughtcrime.securesms.util.ViewUtil
|
||||
import java.lang.AssertionError
|
||||
import java.util.concurrent.CopyOnWriteArrayList
|
||||
|
@ -30,6 +34,8 @@ class SendButton(context: Context, attributeSet: AttributeSet?) : AppCompatImage
|
|||
private var activeMessageSendType: MessageSendType? = null
|
||||
private var defaultTransportType: MessageSendType.TransportType = MessageSendType.TransportType.SMS
|
||||
private var defaultSubscriptionId: Int? = null
|
||||
|
||||
lateinit var snackbarContainer: View
|
||||
private var popupContainer: ViewGroup? = null
|
||||
|
||||
init {
|
||||
|
@ -146,10 +152,19 @@ class SendButton(context: Context, attributeSet: AttributeSet?) : AppCompatImage
|
|||
}
|
||||
|
||||
override fun onLongClick(v: View): Boolean {
|
||||
if (!isEnabled || availableSendTypes.size == 1) {
|
||||
if (!isEnabled) {
|
||||
return false
|
||||
}
|
||||
|
||||
if (availableSendTypes.size == 1) {
|
||||
return if (!Util.isDefaultSmsProvider(context) || !SignalStore.misc().smsExportPhase.isSmsSupported()) {
|
||||
Snackbar.make(snackbarContainer, R.string.InputPanel__sms_messaging_is_no_longer_supported_in_signal, Snackbar.LENGTH_SHORT).show()
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
val currentlySelected: MessageSendType = selectedSendType
|
||||
|
||||
val items = availableSendTypes
|
||||
|
|
|
@ -1,28 +1,41 @@
|
|||
package org.thoughtcrime.securesms.components.settings.app.chats
|
||||
|
||||
import android.app.Activity
|
||||
import android.content.Intent
|
||||
import androidx.activity.result.ActivityResultLauncher
|
||||
import androidx.activity.result.contract.ActivityResultContracts
|
||||
import androidx.lifecycle.ViewModelProvider
|
||||
import androidx.navigation.Navigation
|
||||
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.app.chats.sms.SmsExportState
|
||||
import org.thoughtcrime.securesms.components.settings.configure
|
||||
import org.thoughtcrime.securesms.exporter.flow.SmsExportActivity
|
||||
import org.thoughtcrime.securesms.exporter.flow.SmsExportDialogs
|
||||
import org.thoughtcrime.securesms.util.adapter.mapping.MappingAdapter
|
||||
import org.thoughtcrime.securesms.util.navigation.safeNavigate
|
||||
|
||||
class ChatsSettingsFragment : DSLSettingsFragment(R.string.preferences_chats__chats) {
|
||||
|
||||
private lateinit var viewModel: ChatsSettingsViewModel
|
||||
private lateinit var smsExportLauncher: ActivityResultLauncher<Intent>
|
||||
|
||||
override fun onResume() {
|
||||
super.onResume()
|
||||
viewModel.refresh()
|
||||
}
|
||||
|
||||
@Suppress("ReplaceGetOrSet")
|
||||
override fun bindAdapter(adapter: MappingAdapter) {
|
||||
val repository = ChatsSettingsRepository()
|
||||
val factory = ChatsSettingsViewModel.Factory(repository)
|
||||
viewModel = ViewModelProvider(this, factory)[ChatsSettingsViewModel::class.java]
|
||||
smsExportLauncher = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) {
|
||||
if (it.resultCode == Activity.RESULT_OK) {
|
||||
SmsExportDialogs.showSmsRemovalDialog(requireContext(), requireView())
|
||||
}
|
||||
}
|
||||
|
||||
viewModel = ViewModelProvider(this).get(ChatsSettingsViewModel::class.java)
|
||||
|
||||
viewModel.state.observe(viewLifecycleOwner) {
|
||||
adapter.submitList(getConfiguration(it).toMappingModelList())
|
||||
|
@ -32,14 +45,46 @@ class ChatsSettingsFragment : DSLSettingsFragment(R.string.preferences_chats__ch
|
|||
private fun getConfiguration(state: ChatsSettingsState): DSLConfiguration {
|
||||
return configure {
|
||||
|
||||
clickPref(
|
||||
title = DSLSettingsText.from(R.string.preferences__sms_mms),
|
||||
onClick = {
|
||||
Navigation.findNavController(requireView()).safeNavigate(R.id.action_chatsSettingsFragment_to_smsSettingsFragment)
|
||||
}
|
||||
)
|
||||
if (!state.useAsDefaultSmsApp) {
|
||||
when (state.smsExportState) {
|
||||
SmsExportState.FETCHING -> Unit
|
||||
SmsExportState.HAS_UNEXPORTED_MESSAGES -> {
|
||||
clickPref(
|
||||
title = DSLSettingsText.from(R.string.SmsSettingsFragment__export_sms_messages),
|
||||
summary = DSLSettingsText.from(R.string.SmsSettingsFragment__you_can_export_your_sms_messages_to_your_phones_sms_database),
|
||||
onClick = {
|
||||
smsExportLauncher.launch(SmsExportActivity.createIntent(requireContext()))
|
||||
}
|
||||
)
|
||||
|
||||
dividerPref()
|
||||
dividerPref()
|
||||
}
|
||||
SmsExportState.ALL_MESSAGES_EXPORTED -> {
|
||||
clickPref(
|
||||
title = DSLSettingsText.from(R.string.SmsSettingsFragment__remove_sms_messages),
|
||||
summary = DSLSettingsText.from(R.string.SmsSettingsFragment__remove_sms_messages_from_signal_to_clear_up_storage_space),
|
||||
onClick = {
|
||||
SmsExportDialogs.showSmsRemovalDialog(requireContext(), requireView())
|
||||
}
|
||||
)
|
||||
|
||||
dividerPref()
|
||||
}
|
||||
SmsExportState.NO_SMS_MESSAGES_IN_DATABASE -> Unit
|
||||
SmsExportState.NOT_AVAILABLE -> Unit
|
||||
}
|
||||
}
|
||||
|
||||
if (state.useAsDefaultSmsApp) {
|
||||
clickPref(
|
||||
title = DSLSettingsText.from(R.string.preferences__sms_mms),
|
||||
onClick = {
|
||||
Navigation.findNavController(requireView()).safeNavigate(R.id.action_chatsSettingsFragment_to_smsSettingsFragment)
|
||||
}
|
||||
)
|
||||
|
||||
dividerPref()
|
||||
}
|
||||
|
||||
switchPref(
|
||||
title = DSLSettingsText.from(R.string.preferences__generate_link_previews),
|
||||
|
|
|
@ -1,9 +1,13 @@
|
|||
package org.thoughtcrime.securesms.components.settings.app.chats
|
||||
|
||||
import org.thoughtcrime.securesms.components.settings.app.chats.sms.SmsExportState
|
||||
|
||||
data class ChatsSettingsState(
|
||||
val generateLinkPreviews: Boolean,
|
||||
val useAddressBook: Boolean,
|
||||
val useSystemEmoji: Boolean,
|
||||
val enterKeySends: Boolean,
|
||||
val chatBackupsEnabled: Boolean
|
||||
val chatBackupsEnabled: Boolean,
|
||||
val useAsDefaultSmsApp: Boolean,
|
||||
val smsExportState: SmsExportState = SmsExportState.FETCHING
|
||||
)
|
||||
|
|
|
@ -2,17 +2,24 @@ package org.thoughtcrime.securesms.components.settings.app.chats
|
|||
|
||||
import androidx.lifecycle.LiveData
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.ViewModelProvider
|
||||
import io.reactivex.rxjava3.disposables.CompositeDisposable
|
||||
import io.reactivex.rxjava3.kotlin.plusAssign
|
||||
import org.thoughtcrime.securesms.components.settings.app.chats.sms.SmsSettingsRepository
|
||||
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies
|
||||
import org.thoughtcrime.securesms.keyvalue.SignalStore
|
||||
import org.thoughtcrime.securesms.util.BackupUtil
|
||||
import org.thoughtcrime.securesms.util.ConversationUtil
|
||||
import org.thoughtcrime.securesms.util.ThrottledDebouncer
|
||||
import org.thoughtcrime.securesms.util.Util
|
||||
import org.thoughtcrime.securesms.util.livedata.Store
|
||||
|
||||
class ChatsSettingsViewModel(private val repository: ChatsSettingsRepository) : ViewModel() {
|
||||
class ChatsSettingsViewModel @JvmOverloads constructor(
|
||||
private val repository: ChatsSettingsRepository = ChatsSettingsRepository(),
|
||||
smsSettingsRepository: SmsSettingsRepository = SmsSettingsRepository()
|
||||
) : ViewModel() {
|
||||
|
||||
private val refreshDebouncer = ThrottledDebouncer(500L)
|
||||
private val disposables = CompositeDisposable()
|
||||
|
||||
private val store: Store<ChatsSettingsState> = Store(
|
||||
ChatsSettingsState(
|
||||
|
@ -20,12 +27,23 @@ class ChatsSettingsViewModel(private val repository: ChatsSettingsRepository) :
|
|||
useAddressBook = SignalStore.settings().isPreferSystemContactPhotos,
|
||||
useSystemEmoji = SignalStore.settings().isPreferSystemEmoji,
|
||||
enterKeySends = SignalStore.settings().isEnterKeySends,
|
||||
chatBackupsEnabled = SignalStore.settings().isBackupEnabled && BackupUtil.canUserAccessBackupDirectory(ApplicationDependencies.getApplication())
|
||||
chatBackupsEnabled = SignalStore.settings().isBackupEnabled && BackupUtil.canUserAccessBackupDirectory(ApplicationDependencies.getApplication()),
|
||||
useAsDefaultSmsApp = Util.isDefaultSmsProvider(ApplicationDependencies.getApplication())
|
||||
)
|
||||
)
|
||||
|
||||
val state: LiveData<ChatsSettingsState> = store.stateLiveData
|
||||
|
||||
init {
|
||||
disposables += smsSettingsRepository.getSmsExportState().subscribe { state ->
|
||||
store.update { it.copy(smsExportState = state) }
|
||||
}
|
||||
}
|
||||
|
||||
override fun onCleared() {
|
||||
disposables.clear()
|
||||
}
|
||||
|
||||
fun setGenerateLinkPreviewsEnabled(enabled: Boolean) {
|
||||
store.update { it.copy(generateLinkPreviews = enabled) }
|
||||
SignalStore.settings().isLinkPreviewsEnabled = enabled
|
||||
|
@ -55,10 +73,4 @@ class ChatsSettingsViewModel(private val repository: ChatsSettingsRepository) :
|
|||
store.update { it.copy(chatBackupsEnabled = backupsEnabled) }
|
||||
}
|
||||
}
|
||||
|
||||
class Factory(private val repository: ChatsSettingsRepository) : ViewModelProvider.Factory {
|
||||
override fun <T : ViewModel> create(modelClass: Class<T>): T {
|
||||
return requireNotNull(modelClass.cast(ChatsSettingsViewModel(repository)))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,9 @@
|
|||
package org.thoughtcrime.securesms.components.settings.app.chats.sms
|
||||
|
||||
enum class SmsExportState {
|
||||
FETCHING,
|
||||
HAS_UNEXPORTED_MESSAGES,
|
||||
ALL_MESSAGES_EXPORTED,
|
||||
NO_SMS_MESSAGES_IN_DATABASE,
|
||||
NOT_AVAILABLE
|
||||
}
|
|
@ -9,18 +9,16 @@ import androidx.activity.result.ActivityResultLauncher
|
|||
import androidx.activity.result.contract.ActivityResultContracts
|
||||
import androidx.lifecycle.ViewModelProvider
|
||||
import androidx.navigation.Navigation
|
||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||
import com.google.android.material.snackbar.Snackbar
|
||||
import org.signal.core.util.concurrent.SignalExecutors
|
||||
import androidx.navigation.fragment.findNavController
|
||||
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.SignalDatabase
|
||||
import org.thoughtcrime.securesms.components.settings.models.OutlinedLearnMore
|
||||
import org.thoughtcrime.securesms.exporter.flow.SmsExportActivity
|
||||
import org.thoughtcrime.securesms.exporter.flow.SmsExportDialogs
|
||||
import org.thoughtcrime.securesms.keyvalue.SignalStore
|
||||
import org.thoughtcrime.securesms.util.SmsUtil
|
||||
import org.thoughtcrime.securesms.util.Util
|
||||
import org.thoughtcrime.securesms.util.adapter.mapping.MappingAdapter
|
||||
import org.thoughtcrime.securesms.util.navigation.safeNavigate
|
||||
|
@ -38,9 +36,11 @@ class SmsSettingsFragment : DSLSettingsFragment(R.string.preferences__sms_mms) {
|
|||
}
|
||||
|
||||
override fun bindAdapter(adapter: MappingAdapter) {
|
||||
OutlinedLearnMore.register(adapter)
|
||||
|
||||
smsExportLauncher = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) {
|
||||
if (it.resultCode == Activity.RESULT_OK) {
|
||||
showSmsRemovalDialog()
|
||||
SmsExportDialogs.showSmsRemovalDialog(requireContext(), requireView())
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -52,16 +52,32 @@ class SmsSettingsFragment : DSLSettingsFragment(R.string.preferences__sms_mms) {
|
|||
}
|
||||
|
||||
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
|
||||
SignalStore.settings().setDefaultSms(Util.isDefaultSmsProvider(requireContext()))
|
||||
if (Util.isDefaultSmsProvider(requireContext())) {
|
||||
SignalStore.settings().setDefaultSms(true)
|
||||
} else {
|
||||
SignalStore.settings().setDefaultSms(false)
|
||||
findNavController().navigateUp()
|
||||
}
|
||||
}
|
||||
|
||||
private fun getConfiguration(state: SmsSettingsState): DSLConfiguration {
|
||||
return configure {
|
||||
|
||||
if (state.useAsDefaultSmsApp) {
|
||||
customPref(
|
||||
OutlinedLearnMore.Model(
|
||||
summary = DSLSettingsText.from(R.string.SmsSettingsFragment__sms_support_will_be_removed_soon_to_focus_on_encrypted_messaging),
|
||||
learnMoreUrl = getString(R.string.sms_export_url)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
when (state.smsExportState) {
|
||||
SmsSettingsState.SmsExportState.FETCHING -> Unit
|
||||
SmsSettingsState.SmsExportState.HAS_UNEXPORTED_MESSAGES -> {
|
||||
SmsExportState.FETCHING -> Unit
|
||||
SmsExportState.HAS_UNEXPORTED_MESSAGES -> {
|
||||
clickPref(
|
||||
title = DSLSettingsText.from(R.string.SmsSettingsFragment__export_sms_messages),
|
||||
summary = DSLSettingsText.from(R.string.SmsSettingsFragment__you_can_export_your_sms_messages_to_your_phones_sms_database),
|
||||
onClick = {
|
||||
smsExportLauncher.launch(SmsExportActivity.createIntent(requireContext()))
|
||||
}
|
||||
|
@ -69,32 +85,31 @@ class SmsSettingsFragment : DSLSettingsFragment(R.string.preferences__sms_mms) {
|
|||
|
||||
dividerPref()
|
||||
}
|
||||
SmsSettingsState.SmsExportState.ALL_MESSAGES_EXPORTED -> {
|
||||
SmsExportState.ALL_MESSAGES_EXPORTED -> {
|
||||
clickPref(
|
||||
title = DSLSettingsText.from(R.string.SmsSettingsFragment__remove_sms_messages),
|
||||
summary = DSLSettingsText.from(R.string.SmsSettingsFragment__remove_sms_messages_from_signal_to_clear_up_storage_space),
|
||||
onClick = {
|
||||
showSmsRemovalDialog()
|
||||
SmsExportDialogs.showSmsRemovalDialog(requireContext(), requireView())
|
||||
}
|
||||
)
|
||||
|
||||
dividerPref()
|
||||
}
|
||||
SmsSettingsState.SmsExportState.NO_SMS_MESSAGES_IN_DATABASE -> Unit
|
||||
SmsSettingsState.SmsExportState.NOT_AVAILABLE -> Unit
|
||||
SmsExportState.NO_SMS_MESSAGES_IN_DATABASE -> Unit
|
||||
SmsExportState.NOT_AVAILABLE -> Unit
|
||||
}
|
||||
|
||||
@Suppress("DEPRECATION")
|
||||
clickPref(
|
||||
title = DSLSettingsText.from(R.string.SmsSettingsFragment__use_as_default_sms_app),
|
||||
summary = DSLSettingsText.from(if (state.useAsDefaultSmsApp) R.string.arrays__enabled else R.string.arrays__disabled),
|
||||
onClick = {
|
||||
if (state.useAsDefaultSmsApp) {
|
||||
if (state.useAsDefaultSmsApp) {
|
||||
@Suppress("DEPRECATION")
|
||||
clickPref(
|
||||
title = DSLSettingsText.from(R.string.SmsSettingsFragment__use_as_default_sms_app),
|
||||
summary = DSLSettingsText.from(R.string.arrays__enabled),
|
||||
onClick = {
|
||||
startDefaultAppSelectionIntent()
|
||||
} else {
|
||||
startActivityForResult(SmsUtil.getSmsRoleIntent(requireContext()), SMS_REQUEST_CODE.toInt())
|
||||
}
|
||||
}
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
switchPref(
|
||||
title = DSLSettingsText.from(R.string.preferences__sms_delivery_reports),
|
||||
|
@ -137,21 +152,4 @@ class SmsSettingsFragment : DSLSettingsFragment(R.string.preferences__sms_mms) {
|
|||
|
||||
startActivityForResult(intent, SMS_REQUEST_CODE.toInt())
|
||||
}
|
||||
|
||||
private fun showSmsRemovalDialog() {
|
||||
MaterialAlertDialogBuilder(requireContext())
|
||||
.setTitle(R.string.RemoveSmsMessagesDialogFragment__remove_sms_messages)
|
||||
.setMessage(R.string.RemoveSmsMessagesDialogFragment__you_can_now_remove_sms_messages_from_signal)
|
||||
.setPositiveButton(R.string.RemoveSmsMessagesDialogFragment__keep_messages) { _, _ ->
|
||||
Snackbar.make(requireView(), R.string.SmsSettingsFragment__you_can_remove_sms_messages_from_signal_in_settings, Snackbar.LENGTH_SHORT).show()
|
||||
}
|
||||
.setNegativeButton(R.string.RemoveSmsMessagesDialogFragment__remove_messages) { _, _ ->
|
||||
SignalExecutors.BOUNDED.execute {
|
||||
SignalDatabase.sms.deleteExportedMessages()
|
||||
SignalDatabase.mms.deleteExportedMessages()
|
||||
}
|
||||
Snackbar.make(requireView(), R.string.SmsSettingsFragment__removing_sms_messages_from_signal, Snackbar.LENGTH_SHORT).show()
|
||||
}
|
||||
.show()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -11,9 +11,9 @@ class SmsSettingsRepository(
|
|||
private val smsDatabase: MessageDatabase = SignalDatabase.sms,
|
||||
private val mmsDatabase: MessageDatabase = SignalDatabase.mms
|
||||
) {
|
||||
fun getSmsExportState(): Single<SmsSettingsState.SmsExportState> {
|
||||
fun getSmsExportState(): Single<SmsExportState> {
|
||||
if (!FeatureFlags.smsExporter()) {
|
||||
return Single.just(SmsSettingsState.SmsExportState.NOT_AVAILABLE)
|
||||
return Single.just(SmsExportState.NOT_AVAILABLE)
|
||||
}
|
||||
|
||||
return Single.fromCallable {
|
||||
|
@ -22,24 +22,24 @@ class SmsSettingsRepository(
|
|||
}
|
||||
|
||||
@WorkerThread
|
||||
private fun checkInsecureMessageCount(): SmsSettingsState.SmsExportState? {
|
||||
private fun checkInsecureMessageCount(): SmsExportState? {
|
||||
val totalSmsMmsCount = smsDatabase.insecureMessageCount + mmsDatabase.insecureMessageCount
|
||||
|
||||
return if (totalSmsMmsCount == 0) {
|
||||
SmsSettingsState.SmsExportState.NO_SMS_MESSAGES_IN_DATABASE
|
||||
SmsExportState.NO_SMS_MESSAGES_IN_DATABASE
|
||||
} else {
|
||||
null
|
||||
}
|
||||
}
|
||||
|
||||
@WorkerThread
|
||||
private fun checkUnexportedInsecureMessageCount(): SmsSettingsState.SmsExportState {
|
||||
private fun checkUnexportedInsecureMessageCount(): SmsExportState {
|
||||
val totalUnexportedCount = smsDatabase.unexportedInsecureMessagesCount + mmsDatabase.unexportedInsecureMessagesCount
|
||||
|
||||
return if (totalUnexportedCount > 0) {
|
||||
SmsSettingsState.SmsExportState.HAS_UNEXPORTED_MESSAGES
|
||||
SmsExportState.HAS_UNEXPORTED_MESSAGES
|
||||
} else {
|
||||
SmsSettingsState.SmsExportState.ALL_MESSAGES_EXPORTED
|
||||
SmsExportState.ALL_MESSAGES_EXPORTED
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,12 +5,4 @@ data class SmsSettingsState(
|
|||
val smsDeliveryReportsEnabled: Boolean,
|
||||
val wifiCallingCompatibilityEnabled: Boolean,
|
||||
val smsExportState: SmsExportState = SmsExportState.FETCHING
|
||||
) {
|
||||
enum class SmsExportState {
|
||||
FETCHING,
|
||||
HAS_UNEXPORTED_MESSAGES,
|
||||
ALL_MESSAGES_EXPORTED,
|
||||
NO_SMS_MESSAGES_IN_DATABASE,
|
||||
NOT_AVAILABLE
|
||||
}
|
||||
}
|
||||
)
|
||||
|
|
|
@ -15,6 +15,7 @@ import org.thoughtcrime.securesms.R
|
|||
import org.thoughtcrime.securesms.components.ContactFilterView
|
||||
import org.thoughtcrime.securesms.contacts.ContactsCursorLoader
|
||||
import org.thoughtcrime.securesms.groups.SelectionLimits
|
||||
import org.thoughtcrime.securesms.keyvalue.SignalStore
|
||||
import org.thoughtcrime.securesms.recipients.RecipientId
|
||||
import org.thoughtcrime.securesms.util.LifecycleDisposable
|
||||
import org.thoughtcrime.securesms.util.Util
|
||||
|
@ -105,7 +106,7 @@ class SelectRecipientsFragment : LoggingFragment(), ContactSelectionListFragment
|
|||
ContactsCursorLoader.DisplayMode.FLAG_HIDE_RECENT_HEADER or
|
||||
ContactsCursorLoader.DisplayMode.FLAG_GROUPS_AFTER_CONTACTS
|
||||
|
||||
if (Util.isDefaultSmsProvider(requireContext())) {
|
||||
if (Util.isDefaultSmsProvider(requireContext()) && SignalStore.misc().smsExportPhase.isSmsSupported()) {
|
||||
mode = mode or ContactsCursorLoader.DisplayMode.FLAG_SMS
|
||||
}
|
||||
|
||||
|
|
|
@ -16,14 +16,17 @@ import org.thoughtcrime.securesms.components.settings.conversation.preferences.L
|
|||
import org.thoughtcrime.securesms.database.AttachmentDatabase
|
||||
import org.thoughtcrime.securesms.database.RecipientDatabase
|
||||
import org.thoughtcrime.securesms.database.model.StoryViewState
|
||||
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies
|
||||
import org.thoughtcrime.securesms.groups.GroupId
|
||||
import org.thoughtcrime.securesms.groups.LiveGroup
|
||||
import org.thoughtcrime.securesms.groups.v2.GroupAddMembersResult
|
||||
import org.thoughtcrime.securesms.keyvalue.SignalStore
|
||||
import org.thoughtcrime.securesms.recipients.Recipient
|
||||
import org.thoughtcrime.securesms.recipients.RecipientId
|
||||
import org.thoughtcrime.securesms.recipients.RecipientUtil
|
||||
import org.thoughtcrime.securesms.util.FeatureFlags
|
||||
import org.thoughtcrime.securesms.util.SingleLiveEvent
|
||||
import org.thoughtcrime.securesms.util.Util
|
||||
import org.thoughtcrime.securesms.util.livedata.LiveDataUtil
|
||||
import org.thoughtcrime.securesms.util.livedata.Store
|
||||
import java.util.Optional
|
||||
|
@ -138,11 +141,17 @@ sealed class ConversationSettingsViewModel(
|
|||
}
|
||||
|
||||
store.update(liveRecipient.liveData) { recipient, state ->
|
||||
val isAudioAvailable = (recipient.isRegistered || (Util.isDefaultSmsProvider(ApplicationDependencies.getApplication()) && SignalStore.misc().smsExportPhase.isSmsSupported())) &&
|
||||
!recipient.isGroup &&
|
||||
!recipient.isBlocked &&
|
||||
!recipient.isSelf &&
|
||||
!recipient.isReleaseNotes
|
||||
|
||||
state.copy(
|
||||
recipient = recipient,
|
||||
buttonStripState = ButtonStripPreference.State(
|
||||
isVideoAvailable = recipient.registered == RecipientDatabase.RegisteredState.REGISTERED && !recipient.isSelf && !recipient.isBlocked && !recipient.isReleaseNotes,
|
||||
isAudioAvailable = !recipient.isGroup && !recipient.isSelf && !recipient.isBlocked && !recipient.isReleaseNotes,
|
||||
isAudioAvailable = isAudioAvailable,
|
||||
isAudioSecure = recipient.registered == RecipientDatabase.RegisteredState.REGISTERED,
|
||||
isMuted = recipient.isMuted,
|
||||
isMuteAvailable = !recipient.isSelf,
|
||||
|
|
|
@ -0,0 +1,35 @@
|
|||
package org.thoughtcrime.securesms.components.settings.models
|
||||
|
||||
import org.thoughtcrime.securesms.components.settings.DSLSettingsText
|
||||
import org.thoughtcrime.securesms.components.settings.PreferenceModel
|
||||
import org.thoughtcrime.securesms.databinding.DslOutlinedLearnMoreBinding
|
||||
import org.thoughtcrime.securesms.util.adapter.mapping.BindingFactory
|
||||
import org.thoughtcrime.securesms.util.adapter.mapping.BindingViewHolder
|
||||
import org.thoughtcrime.securesms.util.adapter.mapping.MappingAdapter
|
||||
|
||||
/**
|
||||
* Show a informational text message in an outlined bubble.
|
||||
*/
|
||||
object OutlinedLearnMore {
|
||||
|
||||
fun register(mappingAdapter: MappingAdapter) {
|
||||
mappingAdapter.registerFactory(Model::class.java, BindingFactory(::ViewHolder, DslOutlinedLearnMoreBinding::inflate))
|
||||
}
|
||||
|
||||
class Model(
|
||||
summary: DSLSettingsText,
|
||||
val learnMoreUrl: String
|
||||
) : PreferenceModel<Model>(summary = summary) {
|
||||
override fun areContentsTheSame(newItem: Model): Boolean {
|
||||
return super.areContentsTheSame(newItem) && learnMoreUrl == newItem.learnMoreUrl
|
||||
}
|
||||
}
|
||||
|
||||
private class ViewHolder(binding: DslOutlinedLearnMoreBinding) : BindingViewHolder<Model, DslOutlinedLearnMoreBinding>(binding) {
|
||||
override fun bind(model: Model) {
|
||||
binding.root.text = model.summary!!.resolve(context)
|
||||
binding.root.setLearnMoreVisible(true)
|
||||
binding.root.setLink(model.learnMoreUrl)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1486,6 +1486,7 @@ public class ConversationFragment extends LoggingFragment implements Multiselect
|
|||
void onVoiceNotePlaybackSpeedChanged(@NonNull Uri uri, float speed);
|
||||
void onRegisterVoiceNoteCallbacks(@NonNull Observer<VoiceNotePlaybackState> onPlaybackStartObserver);
|
||||
void onUnregisterVoiceNoteCallbacks(@NonNull Observer<VoiceNotePlaybackState> onPlaybackStartObserver);
|
||||
void onInviteToSignal();
|
||||
}
|
||||
|
||||
private class ConversationScrollListener extends OnScrollListener {
|
||||
|
@ -2080,6 +2081,11 @@ public class ConversationFragment extends LoggingFragment implements Multiselect
|
|||
RecipientBottomSheetDialogFragment.create(target, recipient.get().getGroupId().orElse(null)).show(getParentFragmentManager(), "BOTTOM");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onInviteToSignalClicked() {
|
||||
listener.onInviteToSignal();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onViewGiftBadgeClicked(@NonNull MessageRecord messageRecord) {
|
||||
if (!MessageRecordUtil.hasGiftBadge(messageRecord)) {
|
||||
|
|
|
@ -189,6 +189,7 @@ import org.thoughtcrime.securesms.database.model.StoryType;
|
|||
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies;
|
||||
import org.thoughtcrime.securesms.events.GroupCallPeekEvent;
|
||||
import org.thoughtcrime.securesms.events.ReminderUpdateEvent;
|
||||
import org.thoughtcrime.securesms.exporter.flow.SmsExportActivity;
|
||||
import org.thoughtcrime.securesms.groups.GroupId;
|
||||
import org.thoughtcrime.securesms.groups.ui.GroupChangeFailureReason;
|
||||
import org.thoughtcrime.securesms.groups.ui.GroupErrors;
|
||||
|
@ -401,7 +402,7 @@ public class ConversationParentFragment extends Fragment
|
|||
private TextView charactersLeft;
|
||||
private ConversationFragment fragment;
|
||||
private Button unblockButton;
|
||||
private Button makeDefaultSmsButton;
|
||||
private Stub<View> smsExportStub;
|
||||
private Button registerButton;
|
||||
private InputAwareLayout container;
|
||||
protected Stub<ReminderView> reminderView;
|
||||
|
@ -925,8 +926,11 @@ public class ConversationParentFragment extends Fragment
|
|||
}
|
||||
|
||||
if (isSingleConversation()) {
|
||||
if (viewModel.isPushAvailable()) inflater.inflate(R.menu.conversation_callable_secure, menu);
|
||||
else if (!recipient.get().isReleaseNotes()) inflater.inflate(R.menu.conversation_callable_insecure, menu);
|
||||
if (viewModel.isPushAvailable()) {
|
||||
inflater.inflate(R.menu.conversation_callable_secure, menu);
|
||||
} else if (!recipient.get().isReleaseNotes() && Util.isDefaultSmsProvider(requireContext()) && SignalStore.misc().getSmsExportPhase().isSmsSupported()) {
|
||||
inflater.inflate(R.menu.conversation_callable_insecure, menu);
|
||||
}
|
||||
} else if (isGroupConversation()) {
|
||||
if (isActiveV2Group && Build.VERSION.SDK_INT > 19) {
|
||||
inflater.inflate(R.menu.conversation_callable_groupv2, menu);
|
||||
|
@ -1303,14 +1307,24 @@ public class ConversationParentFragment extends Fragment
|
|||
private void handleInviteLink() {
|
||||
String inviteText = getString(R.string.ConversationActivity_lets_switch_to_signal, getString(R.string.install_url));
|
||||
|
||||
if (viewModel.isDefaultSmsApplication()) {
|
||||
if (viewModel.isDefaultSmsApplication() && SignalStore.misc().getSmsExportPhase().isSmsSupported()) {
|
||||
composeText.appendInvite(inviteText);
|
||||
} else {
|
||||
} else if (recipient.get().hasSmsAddress()) {
|
||||
Intent intent = new Intent(Intent.ACTION_SENDTO);
|
||||
intent.setData(Uri.parse("smsto:" + recipient.get().requireSmsAddress()));
|
||||
intent.putExtra("sms_body", inviteText);
|
||||
intent.putExtra(Intent.EXTRA_TEXT, inviteText);
|
||||
startActivity(intent);
|
||||
} else {
|
||||
Intent sendIntent = new Intent();
|
||||
sendIntent.setAction(Intent.ACTION_SEND);
|
||||
sendIntent.putExtra(Intent.EXTRA_TEXT, inviteText);
|
||||
sendIntent.setType("text/plain");
|
||||
if (sendIntent.resolveActivity(requireContext().getPackageManager()) != null) {
|
||||
startActivity(Intent.createChooser(sendIntent, getString(R.string.InviteActivity_invite_to_signal)));
|
||||
} else {
|
||||
Toast.makeText(requireContext(), R.string.InviteActivity_no_app_to_share_to, Toast.LENGTH_LONG).show();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1590,11 +1604,13 @@ public class ConversationParentFragment extends Fragment
|
|||
|
||||
if (!recipient.get().isPushGroup() && recipient.get().isForceSmsSelection() && smsEnabled) {
|
||||
sendButton.setDefaultTransport(MessageSendType.TransportType.SMS);
|
||||
viewModel.insertSmsExportUpdateEvent(recipient.get());
|
||||
} else {
|
||||
if (isPushAvailable || isPushGroupConversation() || recipient.get().isServiceIdOnly() || recipient.get().isReleaseNotes() || !smsEnabled) {
|
||||
sendButton.setDefaultTransport(MessageSendType.TransportType.SIGNAL);
|
||||
} else {
|
||||
sendButton.setDefaultTransport(MessageSendType.TransportType.SMS);
|
||||
viewModel.insertSmsExportUpdateEvent(recipient.get());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1997,7 +2013,7 @@ public class ConversationParentFragment extends Fragment
|
|||
emojiDrawerStub = ViewUtil.findStubById(view, R.id.emoji_drawer_stub);
|
||||
attachmentKeyboardStub = ViewUtil.findStubById(view, R.id.attachment_keyboard_stub);
|
||||
unblockButton = view.findViewById(R.id.unblock_button);
|
||||
makeDefaultSmsButton = view.findViewById(R.id.make_default_sms_button);
|
||||
smsExportStub = ViewUtil.findStubById(view, R.id.sms_export_stub);
|
||||
registerButton = view.findViewById(R.id.register_button);
|
||||
container = view.findViewById(R.id.layout_container);
|
||||
reminderView = ViewUtil.findStubById(view, R.id.reminder_stub);
|
||||
|
@ -2028,6 +2044,7 @@ public class ConversationParentFragment extends Fragment
|
|||
joinGroupCallButton = view.findViewById(R.id.conversation_group_call_join);
|
||||
|
||||
sendButton.setPopupContainer((ViewGroup) view);
|
||||
sendButton.setSnackbarContainer(view.findViewById(R.id.fragment_content));
|
||||
|
||||
container.setIsBubble(isInBubble());
|
||||
container.addOnKeyboardShownListener(this);
|
||||
|
@ -2067,7 +2084,6 @@ public class ConversationParentFragment extends Fragment
|
|||
titleView.setOnClickListener(v -> handleConversationSettings());
|
||||
titleView.setOnLongClickListener(v -> handleDisplayQuickContact());
|
||||
unblockButton.setOnClickListener(v -> handleUnblock());
|
||||
makeDefaultSmsButton.setOnClickListener(v -> handleMakeDefaultSms());
|
||||
registerButton.setOnClickListener(v -> handleRegisterForSignal());
|
||||
|
||||
composeText.setOnKeyListener(composeKeyPressedListener);
|
||||
|
@ -2708,17 +2724,29 @@ public class ConversationParentFragment extends Fragment
|
|||
if (!conversationSecurityInfo.isPushAvailable() && isPushGroupConversation()) {
|
||||
unblockButton.setVisibility(View.GONE);
|
||||
inputPanel.setHideForBlockedState(true);
|
||||
makeDefaultSmsButton.setVisibility(View.GONE);
|
||||
smsExportStub.setVisibility(View.GONE);
|
||||
registerButton.setVisibility(View.VISIBLE);
|
||||
} else if (!conversationSecurityInfo.isPushAvailable() && !conversationSecurityInfo.isDefaultSmsApplication() && recipient.hasSmsAddress()) {
|
||||
} else if (!conversationSecurityInfo.isPushAvailable() && !(SignalStore.misc().getSmsExportPhase().isSmsSupported() && conversationSecurityInfo.isDefaultSmsApplication()) && recipient.hasSmsAddress()) {
|
||||
unblockButton.setVisibility(View.GONE);
|
||||
inputPanel.setHideForBlockedState(true);
|
||||
makeDefaultSmsButton.setVisibility(View.VISIBLE);
|
||||
smsExportStub.setVisibility(View.VISIBLE);
|
||||
registerButton.setVisibility(View.GONE);
|
||||
|
||||
TextView message = smsExportStub.get().findViewById(R.id.export_sms_message);
|
||||
MaterialButton actionButton = smsExportStub.get().findViewById(R.id.export_sms_button);
|
||||
if (conversationSecurityInfo.getHasUnexportedInsecureMessages()) {
|
||||
message.setText(R.string.ConversationActivity__sms_messaging_is_no_longer_supported_in_signal_you_can_export_your_messages_to_another_app_on_your_phone);
|
||||
actionButton.setText(R.string.ConversationActivity__export_sms_messages);
|
||||
actionButton.setOnClickListener(v -> startActivity(SmsExportActivity.createIntent(requireContext())));
|
||||
} else {
|
||||
message.setText(requireContext().getString(R.string.ConversationActivity__sms_messaging_is_no_longer_supported_in_signal_invite_s_to_to_signal_to_keep_the_conversation_here, recipient.getDisplayName(requireContext())));
|
||||
actionButton.setText(R.string.ConversationActivity__invite_to_signal);
|
||||
actionButton.setOnClickListener(v -> handleInviteLink());
|
||||
}
|
||||
} else if (recipient.isReleaseNotes() && !recipient.isBlocked()) {
|
||||
unblockButton.setVisibility(View.GONE);
|
||||
inputPanel.setHideForBlockedState(true);
|
||||
makeDefaultSmsButton.setVisibility(View.GONE);
|
||||
smsExportStub.setVisibility(View.GONE);
|
||||
registerButton.setVisibility(View.GONE);
|
||||
|
||||
if (recipient.isMuted()) {
|
||||
|
@ -2735,7 +2763,7 @@ public class ConversationParentFragment extends Fragment
|
|||
boolean inactivePushGroup = isPushGroupConversation() && !recipient.isActiveGroup();
|
||||
inputPanel.setHideForBlockedState(inactivePushGroup);
|
||||
unblockButton.setVisibility(View.GONE);
|
||||
makeDefaultSmsButton.setVisibility(View.GONE);
|
||||
smsExportStub.setVisibility(View.GONE);
|
||||
registerButton.setVisibility(View.GONE);
|
||||
}
|
||||
|
||||
|
@ -3796,6 +3824,11 @@ public class ConversationParentFragment extends Fragment
|
|||
voiceNoteMediaController.getVoiceNotePlaybackState().removeObserver(onPlaybackStartObserver);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onInviteToSignal() {
|
||||
handleInviteLink();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCursorChanged() {
|
||||
if (!reactionDelegate.isShowing()) {
|
||||
|
|
|
@ -170,11 +170,16 @@ class ConversationRepository {
|
|||
}
|
||||
}
|
||||
|
||||
long threadId = SignalDatabase.threads().getThreadIdIfExistsFor(recipient.getId());
|
||||
|
||||
boolean hasUnexportedInsecureMessages = threadId != -1 && SignalDatabase.mmsSms().getUnexportedInsecureMessagesCount(threadId) > 0;
|
||||
|
||||
Log.i(TAG, "Returning registered state...");
|
||||
return new ConversationSecurityInfo(recipient.getId(),
|
||||
registeredState == RecipientDatabase.RegisteredState.REGISTERED && signalEnabled,
|
||||
Util.isDefaultSmsProvider(context),
|
||||
true);
|
||||
true,
|
||||
hasUnexportedInsecureMessages);
|
||||
}).subscribeOn(Schedulers.io());
|
||||
}
|
||||
|
||||
|
@ -193,4 +198,18 @@ class ConversationRepository {
|
|||
listener.onChanged();
|
||||
}).subscribeOn(Schedulers.io());
|
||||
}
|
||||
|
||||
public void insertSmsExportUpdateEvent(Recipient recipient) {
|
||||
SignalExecutors.BOUNDED.execute(() -> {
|
||||
long threadId = SignalDatabase.threads().getThreadIdIfExistsFor(recipient.getId());
|
||||
|
||||
if (threadId == -1 || !Util.isDefaultSmsProvider(context)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (RecipientUtil.isSmsOnly(threadId, recipient) && (!recipient.isMmsGroup() || Util.isDefaultSmsProvider(context))) {
|
||||
SignalDatabase.sms().insertSmsExportMessage(recipient.getId(), threadId);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,5 +6,6 @@ data class ConversationSecurityInfo(
|
|||
val recipientId: RecipientId = RecipientId.UNKNOWN,
|
||||
val isPushAvailable: Boolean = false,
|
||||
val isDefaultSmsApplication: Boolean = false,
|
||||
val isInitialized: Boolean = false
|
||||
val isInitialized: Boolean = false,
|
||||
val hasUnexportedInsecureMessages: Boolean = false
|
||||
)
|
||||
|
|
|
@ -535,6 +535,15 @@ public final class ConversationUpdateItem extends FrameLayout
|
|||
});
|
||||
|
||||
actionButton.setText(R.string.ConversationUpdateItem_donate);
|
||||
} else if (conversationMessage.getMessageRecord().isSmsExportType()) {
|
||||
actionButton.setVisibility(View.VISIBLE);
|
||||
actionButton.setOnClickListener(v -> {
|
||||
if (batchSelected.isEmpty() && eventListener != null) {
|
||||
eventListener.onInviteToSignalClicked();
|
||||
}
|
||||
});
|
||||
|
||||
actionButton.setText(R.string.ConversationActivity__invite_to_signal);
|
||||
} else {
|
||||
actionButton.setVisibility(GONE);
|
||||
actionButton.setOnClickListener(null);
|
||||
|
|
|
@ -442,6 +442,10 @@ public class ConversationViewModel extends ViewModel {
|
|||
EventBus.getDefault().unregister(this);
|
||||
}
|
||||
|
||||
public void insertSmsExportUpdateEvent(@NonNull Recipient recipient) {
|
||||
conversationRepository.insertSmsExportUpdateEvent(recipient);
|
||||
}
|
||||
|
||||
enum Event {
|
||||
SHOW_RECAPTCHA
|
||||
}
|
||||
|
|
|
@ -9,10 +9,12 @@ import androidx.annotation.StringRes
|
|||
import kotlinx.parcelize.Parcelize
|
||||
import org.signal.core.util.logging.Log
|
||||
import org.thoughtcrime.securesms.R
|
||||
import org.thoughtcrime.securesms.keyvalue.SignalStore
|
||||
import org.thoughtcrime.securesms.util.CharacterCalculator
|
||||
import org.thoughtcrime.securesms.util.MmsCharacterCalculator
|
||||
import org.thoughtcrime.securesms.util.PushCharacterCalculator
|
||||
import org.thoughtcrime.securesms.util.SmsCharacterCalculator
|
||||
import org.thoughtcrime.securesms.util.Util
|
||||
import org.thoughtcrime.securesms.util.dualsim.SubscriptionInfoCompat
|
||||
import org.thoughtcrime.securesms.util.dualsim.SubscriptionManagerCompat
|
||||
import java.lang.IllegalArgumentException
|
||||
|
@ -138,22 +140,24 @@ sealed class MessageSendType(
|
|||
|
||||
options += SignalMessageSendType
|
||||
|
||||
try {
|
||||
val subscriptions: Collection<SubscriptionInfoCompat> = SubscriptionManagerCompat(context).activeAndReadySubscriptionInfos
|
||||
if (Util.isDefaultSmsProvider(context) && SignalStore.misc().smsExportPhase.isSmsSupported()) {
|
||||
try {
|
||||
val subscriptions: Collection<SubscriptionInfoCompat> = SubscriptionManagerCompat(context).activeAndReadySubscriptionInfos
|
||||
|
||||
if (subscriptions.size < 2) {
|
||||
options += if (isMedia) MmsMessageSendType() else SmsMessageSendType()
|
||||
} else {
|
||||
options += subscriptions.map {
|
||||
if (isMedia) {
|
||||
MmsMessageSendType(simName = it.displayName, simSubscriptionId = it.subscriptionId)
|
||||
} else {
|
||||
SmsMessageSendType(simName = it.displayName, simSubscriptionId = it.subscriptionId)
|
||||
if (subscriptions.size < 2) {
|
||||
options += if (isMedia) MmsMessageSendType() else SmsMessageSendType()
|
||||
} else {
|
||||
options += subscriptions.map {
|
||||
if (isMedia) {
|
||||
MmsMessageSendType(simName = it.displayName, simSubscriptionId = it.subscriptionId)
|
||||
} else {
|
||||
SmsMessageSendType(simName = it.displayName, simSubscriptionId = it.subscriptionId)
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (e: SecurityException) {
|
||||
Log.w(TAG, "Did not have permission to get SMS subscription details!")
|
||||
}
|
||||
} catch (e: SecurityException) {
|
||||
Log.w(TAG, "Did not have permission to get SMS subscription details!")
|
||||
}
|
||||
|
||||
return options
|
||||
|
|
|
@ -115,6 +115,7 @@ import org.thoughtcrime.securesms.database.ThreadDatabase;
|
|||
import org.thoughtcrime.securesms.database.model.ThreadRecord;
|
||||
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies;
|
||||
import org.thoughtcrime.securesms.events.ReminderUpdateEvent;
|
||||
import org.thoughtcrime.securesms.exporter.flow.SmsExportDialogs;
|
||||
import org.thoughtcrime.securesms.insights.InsightsLauncher;
|
||||
import org.thoughtcrime.securesms.jobs.ServiceOutageDetectionJob;
|
||||
import org.thoughtcrime.securesms.keyvalue.SignalStore;
|
||||
|
@ -126,6 +127,7 @@ import org.thoughtcrime.securesms.megaphone.Megaphone;
|
|||
import org.thoughtcrime.securesms.megaphone.MegaphoneActionController;
|
||||
import org.thoughtcrime.securesms.megaphone.MegaphoneViewBuilder;
|
||||
import org.thoughtcrime.securesms.megaphone.Megaphones;
|
||||
import org.thoughtcrime.securesms.megaphone.SmsExportMegaphoneActivity;
|
||||
import org.thoughtcrime.securesms.mms.GlideApp;
|
||||
import org.thoughtcrime.securesms.notifications.MarkReadReceiver;
|
||||
import org.thoughtcrime.securesms.notifications.profiles.NotificationProfile;
|
||||
|
@ -179,6 +181,7 @@ import java.util.Set;
|
|||
import java.util.UUID;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import static android.app.Activity.RESULT_CANCELED;
|
||||
import static android.app.Activity.RESULT_OK;
|
||||
|
||||
|
||||
|
@ -510,11 +513,16 @@ public class ConversationListFragment extends MainFragment implements ActionMode
|
|||
|
||||
@Override
|
||||
public void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
|
||||
if (resultCode != RESULT_OK) {
|
||||
return;
|
||||
if (requestCode == SmsExportMegaphoneActivity.REQUEST_CODE && SignalStore.misc().getSmsExportPhase().isFullscreen()) {
|
||||
ApplicationDependencies.getMegaphoneRepository().markSeen(Megaphones.Event.SMS_EXPORT);
|
||||
if (resultCode == RESULT_CANCELED) {
|
||||
Snackbar.make(fab, R.string.ConversationActivity__you_will_be_reminded_again_soon, Snackbar.LENGTH_LONG).show();
|
||||
} else {
|
||||
SmsExportDialogs.showSmsRemovalDialog(requireContext(), fab);
|
||||
}
|
||||
}
|
||||
|
||||
if (requestCode == CreateKbsPinActivity.REQUEST_NEW_PIN) {
|
||||
if (resultCode == RESULT_OK && requestCode == CreateKbsPinActivity.REQUEST_NEW_PIN) {
|
||||
Snackbar.make(fab, R.string.ConfirmKbsPinFragment__pin_created, Snackbar.LENGTH_LONG).show();
|
||||
viewModel.onMegaphoneCompleted(Megaphones.Event.PINS_FOR_ALL);
|
||||
}
|
||||
|
|
|
@ -13,6 +13,7 @@ import com.google.android.mms.pdu_alt.NotificationInd;
|
|||
import net.zetetic.database.sqlcipher.SQLiteStatement;
|
||||
|
||||
import org.signal.core.util.CursorUtil;
|
||||
import org.signal.core.util.SQLiteDatabaseExtensionsKt;
|
||||
import org.signal.core.util.SqlUtil;
|
||||
import org.signal.core.util.logging.Log;
|
||||
import org.signal.libsignal.protocol.IdentityKey;
|
||||
|
@ -97,7 +98,6 @@ public abstract class MessageDatabase extends Database implements MmsSmsColumns,
|
|||
public abstract List<MessageRecord> getProfileChangeDetailsRecords(long threadId, long afterTimestamp);
|
||||
public abstract Set<Long> getAllRateLimitedMessageIds();
|
||||
public abstract Cursor getUnexportedInsecureMessages(int limit);
|
||||
public abstract int getInsecureMessageCount();
|
||||
public abstract void deleteExportedMessages();
|
||||
|
||||
public abstract void markExpireStarted(long messageId);
|
||||
|
@ -177,6 +177,7 @@ public abstract class MessageDatabase extends Database implements MmsSmsColumns,
|
|||
public abstract void insertNumberChangeMessages(@NonNull Recipient recipient);
|
||||
public abstract void insertBoostRequestMessage(@NonNull RecipientId recipientId, long threadId);
|
||||
public abstract void insertThreadMergeEvent(@NonNull RecipientId recipientId, long threadId, @NonNull ThreadMergeEvent event);
|
||||
public abstract void insertSmsExportMessage(@NonNull RecipientId recipientId, long threadId);
|
||||
|
||||
public abstract boolean deleteMessage(long messageId);
|
||||
abstract void deleteThread(long threadId);
|
||||
|
@ -247,6 +248,20 @@ public abstract class MessageDatabase extends Database implements MmsSmsColumns,
|
|||
return getMessageCountForRecipientsAndType(getOutgoingInsecureMessageClause());
|
||||
}
|
||||
|
||||
public int getInsecureMessageCount() {
|
||||
try (Cursor cursor = getReadableDatabase().query(getTableName(), SqlUtil.COUNT, getInsecureMessageClause(), null, null, null, null)) {
|
||||
if (cursor.moveToFirst()) {
|
||||
return cursor.getInt(0);
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
public boolean hasSmsExportMessage(long threadId) {
|
||||
return SQLiteDatabaseExtensionsKt.exists(getReadableDatabase(), getTableName(), THREAD_ID_WHERE + " AND " + getTypeField() + " = ?", threadId, Types.SMS_EXPORT_TYPE);
|
||||
}
|
||||
|
||||
final int getSecureMessageCountForInsights() {
|
||||
return getMessageCountForRecipientsAndType(getOutgoingSecureMessageClause());
|
||||
}
|
||||
|
@ -360,16 +375,30 @@ public abstract class MessageDatabase extends Database implements MmsSmsColumns,
|
|||
}
|
||||
|
||||
protected String getInsecureMessageClause() {
|
||||
return getInsecureMessageClause(-1);
|
||||
}
|
||||
|
||||
protected String getInsecureMessageClause(long threadId) {
|
||||
String isSent = "(" + getTypeField() + " & " + Types.BASE_TYPE_MASK + ") = " + Types.BASE_SENT_TYPE;
|
||||
String isReceived = "(" + getTypeField() + " & " + Types.BASE_TYPE_MASK + ") = " + Types.BASE_INBOX_TYPE;
|
||||
String isSecure = "(" + getTypeField() + " & " + (Types.SECURE_MESSAGE_BIT | Types.PUSH_MESSAGE_BIT) + ")";
|
||||
String isNotSecure = "(" + getTypeField() + " <= " + (Types.BASE_TYPE_MASK | Types.MESSAGE_ATTRIBUTE_MASK) + ")";
|
||||
|
||||
return String.format(Locale.ENGLISH, "(%s OR %s) AND NOT %s AND %s", isSent, isReceived, isSecure, isNotSecure);
|
||||
String whereClause = String.format(Locale.ENGLISH, "(%s OR %s) AND NOT %s AND %s", isSent, isReceived, isSecure, isNotSecure);
|
||||
|
||||
if (threadId != -1) {
|
||||
whereClause += " AND " + THREAD_ID + " = " + threadId;
|
||||
}
|
||||
|
||||
return whereClause;
|
||||
}
|
||||
|
||||
public int getUnexportedInsecureMessagesCount() {
|
||||
try (Cursor cursor = getWritableDatabase().query(getTableName(), SqlUtil.COUNT, getInsecureMessageClause() + " AND NOT " + EXPORTED, null, null, null, null)) {
|
||||
return getUnexportedInsecureMessagesCount(-1);
|
||||
}
|
||||
|
||||
public int getUnexportedInsecureMessagesCount(long threadId) {
|
||||
try (Cursor cursor = getWritableDatabase().query(getTableName(), SqlUtil.COUNT, getInsecureMessageClause(threadId) + " AND NOT " + EXPORTED, null, null, null, null)) {
|
||||
if (cursor.moveToFirst()) {
|
||||
return cursor.getInt(0);
|
||||
}
|
||||
|
|
|
@ -578,6 +578,11 @@ public class MmsDatabase extends MessageDatabase {
|
|||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void insertSmsExportMessage(@NonNull RecipientId recipientId, long threadId) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void endTransaction(SQLiteDatabase database) {
|
||||
database.endTransaction();
|
||||
|
@ -2456,17 +2461,6 @@ public class MmsDatabase extends MessageDatabase {
|
|||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getInsecureMessageCount() {
|
||||
try (Cursor cursor = getWritableDatabase().query(TABLE_NAME, SqlUtil.COUNT, getInsecureMessageClause(), null, null, null, null)) {
|
||||
if (cursor.moveToFirst()) {
|
||||
return cursor.getInt(0);
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void deleteExportedMessages() {
|
||||
beginTransaction();
|
||||
|
|
|
@ -82,6 +82,7 @@ public interface MmsSmsColumns {
|
|||
protected static final long CHANGE_NUMBER_TYPE = 14;
|
||||
protected static final long BOOST_REQUEST_TYPE = 15;
|
||||
protected static final long THREAD_MERGE_TYPE = 16;
|
||||
protected static final long SMS_EXPORT_TYPE = 17;
|
||||
|
||||
protected static final long BASE_INBOX_TYPE = 20;
|
||||
protected static final long BASE_OUTBOX_TYPE = 21;
|
||||
|
@ -366,6 +367,10 @@ public interface MmsSmsColumns {
|
|||
return type == BOOST_REQUEST_TYPE;
|
||||
}
|
||||
|
||||
public static boolean isSmsExport(long type) {
|
||||
return type == SMS_EXPORT_TYPE;
|
||||
}
|
||||
|
||||
public static boolean isGroupV2LeaveOnly(long type) {
|
||||
return (type & GROUP_V2_LEAVE_BITS) == GROUP_V2_LEAVE_BITS;
|
||||
}
|
||||
|
|
|
@ -121,7 +121,7 @@ public class MmsSmsDatabase extends Database {
|
|||
MmsDatabase.PARENT_STORY_ID};
|
||||
|
||||
private static final String SNIPPET_QUERY = "SELECT " + MmsSmsColumns.ID + ", 0 AS " + TRANSPORT + ", " + SmsDatabase.TYPE + " AS " + MmsSmsColumns.NORMALIZED_TYPE + ", " + SmsDatabase.DATE_RECEIVED + " AS " + MmsSmsColumns.NORMALIZED_DATE_RECEIVED + " FROM " + SmsDatabase.TABLE_NAME + " " +
|
||||
"WHERE " + MmsSmsColumns.THREAD_ID + " = ? AND " + SmsDatabase.TYPE + " NOT IN (" + SmsDatabase.Types.PROFILE_CHANGE_TYPE + ", " + SmsDatabase.Types.GV1_MIGRATION_TYPE + ", " + SmsDatabase.Types.CHANGE_NUMBER_TYPE + ", " + SmsDatabase.Types.BOOST_REQUEST_TYPE + ") AND " + SmsDatabase.TYPE + " & " + GROUP_V2_LEAVE_BITS + " != " + GROUP_V2_LEAVE_BITS + " " +
|
||||
"WHERE " + MmsSmsColumns.THREAD_ID + " = ? AND " + SmsDatabase.TYPE + " NOT IN (" + SmsDatabase.Types.PROFILE_CHANGE_TYPE + ", " + SmsDatabase.Types.GV1_MIGRATION_TYPE + ", " + SmsDatabase.Types.CHANGE_NUMBER_TYPE + ", " + SmsDatabase.Types.BOOST_REQUEST_TYPE + ", " + SmsDatabase.Types.SMS_EXPORT_TYPE + ") AND " + SmsDatabase.TYPE + " & " + GROUP_V2_LEAVE_BITS + " != " + GROUP_V2_LEAVE_BITS + " " +
|
||||
"UNION ALL " +
|
||||
"SELECT " + MmsSmsColumns.ID + ", 1 AS " + TRANSPORT + ", " + MmsDatabase.MESSAGE_BOX + " AS " + MmsSmsColumns.NORMALIZED_TYPE + ", " + MmsDatabase.DATE_RECEIVED + " AS " + MmsSmsColumns.NORMALIZED_DATE_RECEIVED + " FROM " + MmsDatabase.TABLE_NAME + " " +
|
||||
"WHERE " + MmsSmsColumns.THREAD_ID + " = ? AND " + MmsDatabase.MESSAGE_BOX + " & " + GROUP_V2_LEAVE_BITS + " != " + GROUP_V2_LEAVE_BITS + " AND " + MmsDatabase.STORY_TYPE + " = 0 AND " + MmsDatabase.PARENT_STORY_ID + " <= 0 " +
|
||||
|
@ -401,6 +401,17 @@ public class MmsSmsDatabase extends Database {
|
|||
return count;
|
||||
}
|
||||
|
||||
public int getUnexportedInsecureMessagesCount() {
|
||||
return getUnexportedInsecureMessagesCount(-1);
|
||||
}
|
||||
|
||||
public int getUnexportedInsecureMessagesCount(long threadId) {
|
||||
int count = SignalDatabase.sms().getUnexportedInsecureMessagesCount(threadId);
|
||||
count += SignalDatabase.mms().getUnexportedInsecureMessagesCount(threadId);
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
public int getIncomingMeaningfulMessageCountSince(long threadId, long afterTime) {
|
||||
int count = SignalDatabase.sms().getIncomingMeaningfulMessageCountSince(threadId, afterTime);
|
||||
count += SignalDatabase.mms().getIncomingMeaningfulMessageCountSince(threadId, afterTime);
|
||||
|
|
|
@ -34,6 +34,7 @@ import com.google.protobuf.InvalidProtocolBufferException;
|
|||
import net.zetetic.database.sqlcipher.SQLiteStatement;
|
||||
|
||||
import org.signal.core.util.CursorUtil;
|
||||
import org.signal.core.util.SQLiteDatabaseExtensionsKt;
|
||||
import org.signal.core.util.SqlUtil;
|
||||
import org.signal.core.util.logging.Log;
|
||||
import org.signal.libsignal.protocol.util.Pair;
|
||||
|
@ -300,8 +301,8 @@ public class SmsDatabase extends MessageDatabase {
|
|||
}
|
||||
|
||||
private @NonNull SqlUtil.Query buildMeaningfulMessagesQuery(long threadId) {
|
||||
String query = THREAD_ID + " = ? AND (NOT " + TYPE + " & ? AND " + TYPE + " != ? AND " + TYPE + " != ? AND " + TYPE + " != ? AND " + TYPE + " & " + GROUP_V2_LEAVE_BITS + " != " + GROUP_V2_LEAVE_BITS + ")";
|
||||
return SqlUtil.buildQuery(query, threadId, IGNORABLE_TYPESMASK_WHEN_COUNTING, Types.PROFILE_CHANGE_TYPE, Types.CHANGE_NUMBER_TYPE, Types.BOOST_REQUEST_TYPE);
|
||||
String query = THREAD_ID + " = ? AND (NOT " + TYPE + " & ? AND " + TYPE + " != ? AND " + TYPE + " != ? AND " + TYPE + " != ? AND " + TYPE + " != ? AND " + TYPE + " & " + GROUP_V2_LEAVE_BITS + " != " + GROUP_V2_LEAVE_BITS + ")";
|
||||
return SqlUtil.buildQuery(query, threadId, IGNORABLE_TYPESMASK_WHEN_COUNTING, Types.PROFILE_CHANGE_TYPE, Types.CHANGE_NUMBER_TYPE, Types.SMS_EXPORT_TYPE, Types.BOOST_REQUEST_TYPE);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -918,17 +919,6 @@ public class SmsDatabase extends MessageDatabase {
|
|||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getInsecureMessageCount() {
|
||||
try (Cursor cursor = getWritableDatabase().query(TABLE_NAME, SqlUtil.COUNT, getInsecureMessageClause(), null, null, null, null)) {
|
||||
if (cursor.moveToFirst()) {
|
||||
return cursor.getInt(0);
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void deleteExportedMessages() {
|
||||
beginTransaction();
|
||||
|
@ -1139,6 +1129,32 @@ public class SmsDatabase extends MessageDatabase {
|
|||
ApplicationDependencies.getDatabaseObserver().notifyConversationListeners(threadId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void insertSmsExportMessage(@NonNull RecipientId recipientId, long threadId) {
|
||||
ContentValues values = new ContentValues();
|
||||
values.put(RECIPIENT_ID, recipientId.serialize());
|
||||
values.put(ADDRESS_DEVICE_ID, 1);
|
||||
values.put(DATE_RECEIVED, System.currentTimeMillis());
|
||||
values.put(DATE_SENT, System.currentTimeMillis());
|
||||
values.put(READ, 1);
|
||||
values.put(TYPE, Types.SMS_EXPORT_TYPE);
|
||||
values.put(THREAD_ID, threadId);
|
||||
values.putNull(BODY);
|
||||
|
||||
boolean updated = SQLiteDatabaseExtensionsKt.withinTransaction(getWritableDatabase(), db -> {
|
||||
if (SignalDatabase.sms().hasSmsExportMessage(threadId)) {
|
||||
return false;
|
||||
} else {
|
||||
db.insert(TABLE_NAME, null, values);
|
||||
return true;
|
||||
}
|
||||
});
|
||||
|
||||
if (updated) {
|
||||
ApplicationDependencies.getDatabaseObserver().notifyConversationListeners(threadId);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<InsertResult> insertMessageInbox(IncomingTextMessage message, long type) {
|
||||
boolean tryToCollapseJoinRequestEvents = false;
|
||||
|
|
|
@ -236,6 +236,10 @@ public abstract class MessageRecord extends DisplayRecord {
|
|||
} catch (InvalidProtocolBufferException e) {
|
||||
throw new AssertionError(e);
|
||||
}
|
||||
} else if (isSmsExportType()) {
|
||||
int messageResource = SignalStore.misc().getSmsExportPhase().isSmsSupported() ? R.string.MessageRecord__you_will_on_longer_be_able_to_send_sms_messages_from_signal_soon
|
||||
: R.string.MessageRecord__you_can_no_longer_send_sms_messages_in_signal;
|
||||
return fromRecipient(getIndividualRecipient(), r -> context.getString(messageResource, r.getDisplayName(context)), R.drawable.ic_update_info_16);
|
||||
}
|
||||
|
||||
return null;
|
||||
|
@ -542,6 +546,10 @@ public abstract class MessageRecord extends DisplayRecord {
|
|||
return MmsSmsColumns.Types.isThreadMergeType(type);
|
||||
}
|
||||
|
||||
public boolean isSmsExportType() {
|
||||
return MmsSmsColumns.Types.isSmsExport(type);
|
||||
}
|
||||
|
||||
public boolean isInvalidVersionKeyExchange() {
|
||||
return SmsDatabase.Types.isInvalidVersionKeyExchange(type);
|
||||
}
|
||||
|
@ -562,7 +570,7 @@ public abstract class MessageRecord extends DisplayRecord {
|
|||
return isGroupAction() || isJoined() || isExpirationTimerUpdate() || isCallLog() ||
|
||||
isEndSession() || isIdentityUpdate() || isIdentityVerified() || isIdentityDefault() ||
|
||||
isProfileChange() || isGroupV1MigrationEvent() || isChatSessionRefresh() || isBadDecryptType() ||
|
||||
isChangeNumber() || isBoostRequest() || isThreadMergeEventType();
|
||||
isChangeNumber() || isBoostRequest() || isThreadMergeEventType() || isSmsExportType();
|
||||
}
|
||||
|
||||
public boolean isMediaPending() {
|
||||
|
|
|
@ -5,6 +5,7 @@ import android.content.Intent
|
|||
import androidx.core.app.NotificationCompat
|
||||
import androidx.core.content.ContextCompat
|
||||
import app.cash.exhaustive.Exhaustive
|
||||
import org.signal.core.util.PendingIntentFlags
|
||||
import org.signal.smsexporter.ExportableMessage
|
||||
import org.signal.smsexporter.SmsExportService
|
||||
import org.thoughtcrime.securesms.R
|
||||
|
@ -12,8 +13,11 @@ import org.thoughtcrime.securesms.attachments.AttachmentId
|
|||
import org.thoughtcrime.securesms.database.SignalDatabase
|
||||
import org.thoughtcrime.securesms.database.model.MessageId
|
||||
import org.thoughtcrime.securesms.database.model.databaseprotos.MessageExportState
|
||||
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies
|
||||
import org.thoughtcrime.securesms.exporter.flow.SmsExportActivity
|
||||
import org.thoughtcrime.securesms.notifications.NotificationChannels
|
||||
import org.thoughtcrime.securesms.notifications.NotificationIds
|
||||
import org.thoughtcrime.securesms.notifications.v2.NotificationPendingIntentHelper
|
||||
import org.thoughtcrime.securesms.util.JsonUtils
|
||||
import java.io.InputStream
|
||||
|
||||
|
@ -34,16 +38,47 @@ class SignalSmsExportService : SmsExportService() {
|
|||
private var reader: SignalSmsExportReader? = null
|
||||
|
||||
override fun getNotification(progress: Int, total: Int): ExportNotification {
|
||||
val pendingIntent = NotificationPendingIntentHelper.getActivity(
|
||||
this,
|
||||
0,
|
||||
SmsExportActivity.createIntent(this),
|
||||
PendingIntentFlags.mutable()
|
||||
)
|
||||
|
||||
return ExportNotification(
|
||||
NotificationIds.SMS_EXPORT_SERVICE,
|
||||
NotificationCompat.Builder(this, NotificationChannels.BACKUPS)
|
||||
.setSmallIcon(R.drawable.ic_signal_backup)
|
||||
.setContentTitle(getString(R.string.SignalSmsExportService__exporting_messages))
|
||||
.setContentIntent(pendingIntent)
|
||||
.setProgress(total, progress, false)
|
||||
.build()
|
||||
)
|
||||
}
|
||||
|
||||
override fun getExportCompleteNotification(): ExportNotification? {
|
||||
if (ApplicationDependencies.getAppForegroundObserver().isForegrounded) {
|
||||
return null
|
||||
}
|
||||
|
||||
val pendingIntent = NotificationPendingIntentHelper.getActivity(
|
||||
this,
|
||||
0,
|
||||
SmsExportActivity.createIntent(this),
|
||||
PendingIntentFlags.mutable()
|
||||
)
|
||||
|
||||
return ExportNotification(
|
||||
NotificationIds.SMS_EXPORT_COMPLETE,
|
||||
NotificationCompat.Builder(this, NotificationChannels.APP_ALERTS)
|
||||
.setSmallIcon(R.drawable.ic_notification)
|
||||
.setContentTitle(getString(R.string.SignalSmsExportService__signal_sms_export_complete))
|
||||
.setContentText(getString(R.string.SignalSmsExportService__tap_to_return_to_signal))
|
||||
.setContentIntent(pendingIntent)
|
||||
.build()
|
||||
)
|
||||
}
|
||||
|
||||
override fun getUnexportedMessageCount(): Int {
|
||||
ensureReader()
|
||||
return reader!!.getCount()
|
||||
|
|
|
@ -0,0 +1,25 @@
|
|||
package org.thoughtcrime.securesms.exporter.flow
|
||||
|
||||
import android.os.Bundle
|
||||
import android.view.View
|
||||
import androidx.fragment.app.Fragment
|
||||
import androidx.navigation.fragment.findNavController
|
||||
import androidx.navigation.fragment.navArgs
|
||||
import org.thoughtcrime.securesms.R
|
||||
import org.thoughtcrime.securesms.databinding.ExportSmsCompleteFragmentBinding
|
||||
import org.thoughtcrime.securesms.util.navigation.safeNavigate
|
||||
|
||||
/**
|
||||
* Shown when export sms completes.
|
||||
*/
|
||||
class ExportSmsCompleteFragment : Fragment(R.layout.export_sms_complete_fragment) {
|
||||
|
||||
val args: ExportSmsCompleteFragmentArgs by navArgs()
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
val binding = ExportSmsCompleteFragmentBinding.bind(view)
|
||||
|
||||
binding.exportCompleteNext.setOnClickListener { findNavController().safeNavigate(ExportSmsCompleteFragmentDirections.actionExportingSmsMessagesFragmentToChooseANewDefaultSmsAppFragment()) }
|
||||
binding.exportCompleteStatus.text = getString(R.string.ExportSmsCompleteFragment__d_of_d_messages_exported, args.exportMessageCount, args.exportMessageCount)
|
||||
}
|
||||
}
|
|
@ -4,8 +4,13 @@ import android.os.Bundle
|
|||
import android.view.View
|
||||
import androidx.fragment.app.Fragment
|
||||
import androidx.navigation.fragment.findNavController
|
||||
import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers
|
||||
import io.reactivex.rxjava3.disposables.Disposable
|
||||
import org.signal.smsexporter.DefaultSmsHelper
|
||||
import org.signal.smsexporter.SmsExportProgress
|
||||
import org.signal.smsexporter.SmsExportService
|
||||
import org.thoughtcrime.securesms.R
|
||||
import org.thoughtcrime.securesms.SmsExportDirections
|
||||
import org.thoughtcrime.securesms.databinding.ExportYourSmsMessagesFragmentBinding
|
||||
import org.thoughtcrime.securesms.util.Material3OnScrollHelper
|
||||
import org.thoughtcrime.securesms.util.navigation.safeNavigate
|
||||
|
@ -15,6 +20,8 @@ import org.thoughtcrime.securesms.util.navigation.safeNavigate
|
|||
*/
|
||||
class ExportYourSmsMessagesFragment : Fragment(R.layout.export_your_sms_messages_fragment) {
|
||||
|
||||
private var navigationDisposable = Disposable.disposed()
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
val binding = ExportYourSmsMessagesFragmentBinding.bind(view)
|
||||
|
||||
|
@ -32,4 +39,23 @@ class ExportYourSmsMessagesFragment : Fragment(R.layout.export_your_sms_messages
|
|||
|
||||
Material3OnScrollHelper(requireActivity(), binding.toolbar).attach(binding.scrollView)
|
||||
}
|
||||
|
||||
override fun onResume() {
|
||||
super.onResume()
|
||||
navigationDisposable = SmsExportService
|
||||
.progressState
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe {
|
||||
if (it is SmsExportProgress.Done) {
|
||||
findNavController().safeNavigate(SmsExportDirections.actionDirectToExportSmsCompleteFragment(it.progress))
|
||||
} else if (it is SmsExportProgress.InProgress) {
|
||||
findNavController().safeNavigate(ExportYourSmsMessagesFragmentDirections.actionExportYourSmsMessagesFragmentToExportingSmsMessagesFragment())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onPause() {
|
||||
super.onPause()
|
||||
navigationDisposable.dispose()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -39,7 +39,7 @@ class ExportingSmsMessagesFragment : Fragment(R.layout.exporting_sms_messages_fr
|
|||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe {
|
||||
if (it is SmsExportProgress.Done) {
|
||||
findNavController().safeNavigate(ExportingSmsMessagesFragmentDirections.actionExportingSmsMessagesFragmentToChooseANewDefaultSmsAppFragment())
|
||||
findNavController().safeNavigate(ExportingSmsMessagesFragmentDirections.actionExportingSmsMessagesFragmentToExportSmsCompleteFragment(it.progress))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -55,7 +55,7 @@ class ExportingSmsMessagesFragment : Fragment(R.layout.exporting_sms_messages_fr
|
|||
lifecycleDisposable.bindTo(viewLifecycleOwner)
|
||||
lifecycleDisposable += SmsExportService.progressState.observeOn(AndroidSchedulers.mainThread()).subscribe {
|
||||
when (it) {
|
||||
SmsExportProgress.Done -> Unit
|
||||
is SmsExportProgress.Done -> Unit
|
||||
is SmsExportProgress.InProgress -> {
|
||||
binding.progress.isIndeterminate = false
|
||||
binding.progress.max = it.total
|
||||
|
|
|
@ -2,10 +2,12 @@ package org.thoughtcrime.securesms.exporter.flow
|
|||
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import androidx.core.app.NotificationManagerCompat
|
||||
import androidx.fragment.app.Fragment
|
||||
import androidx.navigation.fragment.NavHostFragment
|
||||
import org.thoughtcrime.securesms.R
|
||||
import org.thoughtcrime.securesms.components.FragmentWrapperActivity
|
||||
import org.thoughtcrime.securesms.notifications.NotificationIds
|
||||
import org.thoughtcrime.securesms.util.WindowUtil
|
||||
|
||||
class SmsExportActivity : FragmentWrapperActivity() {
|
||||
|
@ -13,6 +15,7 @@ class SmsExportActivity : FragmentWrapperActivity() {
|
|||
override fun onResume() {
|
||||
super.onResume()
|
||||
WindowUtil.setLightStatusBarFromTheme(this)
|
||||
NotificationManagerCompat.from(this).cancel(NotificationIds.SMS_EXPORT_COMPLETE)
|
||||
}
|
||||
|
||||
override fun getFragment(): Fragment {
|
||||
|
@ -20,6 +23,7 @@ class SmsExportActivity : FragmentWrapperActivity() {
|
|||
}
|
||||
|
||||
companion object {
|
||||
@JvmStatic
|
||||
fun createIntent(context: Context): Intent = Intent(context, SmsExportActivity::class.java)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,29 @@
|
|||
package org.thoughtcrime.securesms.exporter.flow
|
||||
|
||||
import android.content.Context
|
||||
import android.view.View
|
||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||
import com.google.android.material.snackbar.Snackbar
|
||||
import org.signal.core.util.concurrent.SignalExecutors
|
||||
import org.thoughtcrime.securesms.R
|
||||
import org.thoughtcrime.securesms.database.SignalDatabase
|
||||
|
||||
object SmsExportDialogs {
|
||||
@JvmStatic
|
||||
fun showSmsRemovalDialog(context: Context, view: View) {
|
||||
MaterialAlertDialogBuilder(context)
|
||||
.setTitle(R.string.RemoveSmsMessagesDialogFragment__remove_sms_messages)
|
||||
.setMessage(R.string.RemoveSmsMessagesDialogFragment__you_can_now_remove_sms_messages_from_signal)
|
||||
.setPositiveButton(R.string.RemoveSmsMessagesDialogFragment__keep_messages) { _, _ ->
|
||||
Snackbar.make(view, R.string.SmsSettingsFragment__you_can_remove_sms_messages_from_signal_in_settings, Snackbar.LENGTH_SHORT).show()
|
||||
}
|
||||
.setNegativeButton(R.string.RemoveSmsMessagesDialogFragment__remove_messages) { _, _ ->
|
||||
SignalExecutors.BOUNDED.execute {
|
||||
SignalDatabase.sms.deleteExportedMessages()
|
||||
SignalDatabase.mms.deleteExportedMessages()
|
||||
}
|
||||
Snackbar.make(view, R.string.SmsSettingsFragment__removing_sms_messages_from_signal, Snackbar.LENGTH_SHORT).show()
|
||||
}
|
||||
.show()
|
||||
}
|
||||
}
|
|
@ -21,6 +21,7 @@ import org.thoughtcrime.securesms.contacts.ContactsCursorLoader;
|
|||
import org.thoughtcrime.securesms.contacts.sync.ContactDiscovery;
|
||||
import org.thoughtcrime.securesms.database.RecipientDatabase;
|
||||
import org.thoughtcrime.securesms.groups.ui.creategroup.details.AddGroupDetailsActivity;
|
||||
import org.thoughtcrime.securesms.keyvalue.SignalStore;
|
||||
import org.thoughtcrime.securesms.recipients.Recipient;
|
||||
import org.thoughtcrime.securesms.recipients.RecipientId;
|
||||
import org.thoughtcrime.securesms.util.FeatureFlags;
|
||||
|
@ -50,8 +51,9 @@ public class CreateGroupActivity extends ContactSelectionActivity {
|
|||
intent.putExtra(ContactSelectionListFragment.REFRESHABLE, false);
|
||||
intent.putExtra(ContactSelectionActivity.EXTRA_LAYOUT_RES_ID, R.layout.create_group_activity);
|
||||
|
||||
int displayMode = Util.isDefaultSmsProvider(context) ? ContactsCursorLoader.DisplayMode.FLAG_SMS | ContactsCursorLoader.DisplayMode.FLAG_PUSH
|
||||
: ContactsCursorLoader.DisplayMode.FLAG_PUSH;
|
||||
boolean smsEnabled = Util.isDefaultSmsProvider(context) && SignalStore.misc().getSmsExportPhase().isSmsSupported();
|
||||
int displayMode = smsEnabled ? ContactsCursorLoader.DisplayMode.FLAG_SMS | ContactsCursorLoader.DisplayMode.FLAG_PUSH
|
||||
: ContactsCursorLoader.DisplayMode.FLAG_PUSH;
|
||||
|
||||
intent.putExtra(ContactSelectionListFragment.DISPLAY_MODE, displayMode);
|
||||
intent.putExtra(ContactSelectionListFragment.SELECTION_LIMITS, FeatureFlags.groupLimits().excludingSelf());
|
||||
|
|
|
@ -5,7 +5,7 @@ import androidx.annotation.Nullable;
|
|||
|
||||
import org.thoughtcrime.securesms.database.model.databaseprotos.PendingChangeNumberMetadata;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
public final class MiscellaneousValues extends SignalStoreValues {
|
||||
|
@ -26,6 +26,7 @@ public final class MiscellaneousValues extends SignalStoreValues {
|
|||
private static final String LAST_FCM_FOREGROUND_TIME = "misc.last_fcm_foreground_time";
|
||||
private static final String LAST_FOREGROUND_TIME = "misc.last_foreground_time";
|
||||
private static final String PNI_INITIALIZED_DEVICES = "misc.pni_initialized_devices";
|
||||
private static final String SMS_EXPORT_TIME = "misc.sms_export_time";
|
||||
|
||||
MiscellaneousValues(@NonNull KeyValueStore store) {
|
||||
super(store);
|
||||
|
@ -38,7 +39,9 @@ public final class MiscellaneousValues extends SignalStoreValues {
|
|||
|
||||
@Override
|
||||
@NonNull List<String> getKeysToIncludeInBackup() {
|
||||
return Collections.emptyList();
|
||||
return Arrays.asList(
|
||||
SMS_EXPORT_TIME
|
||||
);
|
||||
}
|
||||
|
||||
public long getLastPrekeyRefreshTime() {
|
||||
|
@ -184,4 +187,15 @@ public final class MiscellaneousValues extends SignalStoreValues {
|
|||
public void setPniInitializedDevices(boolean value) {
|
||||
putBoolean(PNI_INITIALIZED_DEVICES, value);
|
||||
}
|
||||
|
||||
public void setHasSeenSmsExportMegaphone() {
|
||||
if (!getStore().containsKey(SMS_EXPORT_TIME)) {
|
||||
putLong(SMS_EXPORT_TIME, System.currentTimeMillis());
|
||||
}
|
||||
}
|
||||
|
||||
public @NonNull SmsExportPhase getSmsExportPhase() {
|
||||
long now = System.currentTimeMillis();
|
||||
return SmsExportPhase.getCurrentPhase(now - getLong(SMS_EXPORT_TIME, now));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,28 @@
|
|||
package org.thoughtcrime.securesms.keyvalue
|
||||
|
||||
import kotlin.time.Duration.Companion.days
|
||||
|
||||
enum class SmsExportPhase(val duration: Long) {
|
||||
PHASE_1(0.days.inWholeMilliseconds),
|
||||
PHASE_2(45.days.inWholeMilliseconds),
|
||||
PHASE_3(105.days.inWholeMilliseconds);
|
||||
|
||||
fun isSmsSupported(): Boolean {
|
||||
return this != PHASE_3
|
||||
}
|
||||
|
||||
fun isFullscreen(): Boolean {
|
||||
return this != PHASE_1
|
||||
}
|
||||
|
||||
fun isBlockingUi(): Boolean {
|
||||
return this == PHASE_3
|
||||
}
|
||||
|
||||
companion object {
|
||||
@JvmStatic
|
||||
fun getCurrentPhase(duration: Long): SmsExportPhase {
|
||||
return values().findLast { duration >= it.duration }!!
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,5 +1,8 @@
|
|||
package org.thoughtcrime.securesms.megaphone;
|
||||
|
||||
import androidx.annotation.WorkerThread;
|
||||
|
||||
public interface MegaphoneSchedule {
|
||||
@WorkerThread
|
||||
boolean shouldDisplay(int seenCount, long lastSeen, long firstVisible, long currentTime);
|
||||
}
|
||||
|
|
|
@ -3,7 +3,6 @@ package org.thoughtcrime.securesms.megaphone;
|
|||
import android.annotation.SuppressLint;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.net.Uri;
|
||||
import android.os.Build;
|
||||
import android.provider.Settings;
|
||||
|
||||
|
@ -22,8 +21,10 @@ import org.thoughtcrime.securesms.components.settings.app.AppSettingsActivity;
|
|||
import org.thoughtcrime.securesms.database.model.MegaphoneRecord;
|
||||
import org.thoughtcrime.securesms.database.model.RemoteMegaphoneRecord;
|
||||
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies;
|
||||
import org.thoughtcrime.securesms.exporter.flow.SmsExportActivity;
|
||||
import org.thoughtcrime.securesms.jobmanager.impl.NetworkConstraint;
|
||||
import org.thoughtcrime.securesms.keyvalue.SignalStore;
|
||||
import org.thoughtcrime.securesms.keyvalue.SmsExportPhase;
|
||||
import org.thoughtcrime.securesms.lock.SignalPinReminderDialog;
|
||||
import org.thoughtcrime.securesms.lock.SignalPinReminders;
|
||||
import org.thoughtcrime.securesms.lock.v2.CreateKbsPinActivity;
|
||||
|
@ -39,7 +40,6 @@ import org.thoughtcrime.securesms.util.PlayServicesUtil;
|
|||
import org.thoughtcrime.securesms.util.ServiceUtil;
|
||||
import org.thoughtcrime.securesms.util.VersionTracker;
|
||||
import org.thoughtcrime.securesms.util.dynamiclanguage.DynamicLanguageContextWrapper;
|
||||
import org.thoughtcrime.securesms.wallpaper.ChatWallpaperActivity;
|
||||
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
|
@ -108,6 +108,7 @@ public final class Megaphones {
|
|||
put(Event.PINS_FOR_ALL, new PinsForAllSchedule());
|
||||
put(Event.CLIENT_DEPRECATED, SignalStore.misc().isClientDeprecated() ? ALWAYS : NEVER);
|
||||
put(Event.NOTIFICATIONS, shouldShowNotificationsMegaphone(context) ? RecurringSchedule.every(TimeUnit.DAYS.toMillis(30)) : NEVER);
|
||||
put(Event.SMS_EXPORT, new SmsExportReminderSchedule(context));
|
||||
put(Event.BACKUP_SCHEDULE_PERMISSION, shouldShowBackupSchedulePermissionMegaphone(context) ? RecurringSchedule.every(TimeUnit.DAYS.toMillis(3)) : NEVER);
|
||||
put(Event.ONBOARDING, shouldShowOnboardingMegaphone(context) ? ALWAYS : NEVER);
|
||||
put(Event.TURN_OFF_CENSORSHIP_CIRCUMVENTION, shouldShowTurnOffCircumventionMegaphone() ? RecurringSchedule.every(TimeUnit.DAYS.toMillis(7)) : NEVER);
|
||||
|
@ -144,6 +145,8 @@ public final class Megaphones {
|
|||
return buildRemoteMegaphone(context);
|
||||
case BACKUP_SCHEDULE_PERMISSION:
|
||||
return buildBackupPermissionMegaphone(context);
|
||||
case SMS_EXPORT:
|
||||
return buildSmsExportMegaphone(context);
|
||||
default:
|
||||
throw new IllegalArgumentException("Event not handled!");
|
||||
}
|
||||
|
@ -356,6 +359,35 @@ public final class Megaphones {
|
|||
.build();
|
||||
}
|
||||
|
||||
private static @NonNull Megaphone buildSmsExportMegaphone(@NonNull Context context) {
|
||||
SmsExportPhase phase = SignalStore.misc().getSmsExportPhase();
|
||||
|
||||
if (phase == SmsExportPhase.PHASE_1) {
|
||||
return new Megaphone.Builder(Event.SMS_EXPORT, Megaphone.Style.BASIC)
|
||||
.setTitle(R.string.SmsExportMegaphone__sms_support_going_away)
|
||||
.setImage(R.drawable.sms_megaphone)
|
||||
.setBody(R.string.SmsExportMegaphone__sms_support_will_be_removed_soon_to_focus_on_encrypted_messaging)
|
||||
.setActionButton(R.string.SmsExportMegaphone__export_sms, (megaphone, controller) -> controller.onMegaphoneNavigationRequested(SmsExportActivity.createIntent(context), SmsExportMegaphoneActivity.REQUEST_CODE))
|
||||
.setSecondaryButton(R.string.Megaphones_remind_me_later, (megaphone, controller) -> controller.onMegaphoneSnooze(Event.SMS_EXPORT))
|
||||
.setOnVisibleListener((megaphone, controller) -> SignalStore.misc().setHasSeenSmsExportMegaphone())
|
||||
.build();
|
||||
} else {
|
||||
Megaphone.Builder builder = new Megaphone.Builder(Event.SMS_EXPORT, Megaphone.Style.FULLSCREEN)
|
||||
.setOnVisibleListener((megaphone, controller) -> {
|
||||
if (phase.isBlockingUi()) {
|
||||
SmsExportReminderSchedule.setShowPhase3Megaphone(false);
|
||||
}
|
||||
controller.onMegaphoneNavigationRequested(new Intent(context, SmsExportMegaphoneActivity.class), SmsExportMegaphoneActivity.REQUEST_CODE);
|
||||
});
|
||||
|
||||
if (phase.isBlockingUi()) {
|
||||
builder.disableSnooze();
|
||||
}
|
||||
|
||||
return builder.build();
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean shouldShowDonateMegaphone(@NonNull Context context, @NonNull Event event, @NonNull Map<Event, MegaphoneRecord> records) {
|
||||
long timeSinceLastDonatePrompt = timeSinceLastDonatePrompt(event, records);
|
||||
|
||||
|
@ -452,7 +484,8 @@ public final class Megaphones {
|
|||
DONATE_Q2_2022("donate_q2_2022"),
|
||||
TURN_OFF_CENSORSHIP_CIRCUMVENTION("turn_off_censorship_circumvention"),
|
||||
REMOTE_MEGAPHONE("remote_megaphone"),
|
||||
BACKUP_SCHEDULE_PERMISSION("backup_schedule_permission");
|
||||
BACKUP_SCHEDULE_PERMISSION("backup_schedule_permission"),
|
||||
SMS_EXPORT("sms_export");
|
||||
|
||||
private final String key;
|
||||
|
||||
|
|
|
@ -0,0 +1,80 @@
|
|||
package org.thoughtcrime.securesms.megaphone
|
||||
|
||||
import android.app.Activity
|
||||
import android.content.Intent
|
||||
import android.os.Bundle
|
||||
import androidx.activity.result.ActivityResultLauncher
|
||||
import androidx.activity.result.contract.ActivityResultContracts
|
||||
import org.thoughtcrime.securesms.PassphraseRequiredActivity
|
||||
import org.thoughtcrime.securesms.R
|
||||
import org.thoughtcrime.securesms.databinding.SmsExportMegaphoneActivityBinding
|
||||
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies
|
||||
import org.thoughtcrime.securesms.exporter.flow.SmsExportActivity
|
||||
import org.thoughtcrime.securesms.keyvalue.SignalStore
|
||||
import org.thoughtcrime.securesms.util.CommunicationActions
|
||||
import org.thoughtcrime.securesms.util.DynamicNoActionBarTheme
|
||||
import org.thoughtcrime.securesms.util.DynamicTheme
|
||||
|
||||
class SmsExportMegaphoneActivity : PassphraseRequiredActivity() {
|
||||
|
||||
companion object {
|
||||
const val REQUEST_CODE: Short = 5343
|
||||
}
|
||||
|
||||
private val theme: DynamicTheme = DynamicNoActionBarTheme()
|
||||
private lateinit var binding: SmsExportMegaphoneActivityBinding
|
||||
private lateinit var smsExportLauncher: ActivityResultLauncher<Intent>
|
||||
|
||||
override fun onPreCreate() {
|
||||
theme.onCreate(this)
|
||||
}
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?, ready: Boolean) {
|
||||
binding = SmsExportMegaphoneActivityBinding.inflate(layoutInflater)
|
||||
setContentView(binding.root)
|
||||
|
||||
smsExportLauncher = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) {
|
||||
if (it.resultCode == Activity.RESULT_OK) {
|
||||
ApplicationDependencies.getMegaphoneRepository().markSeen(Megaphones.Event.SMS_EXPORT)
|
||||
setResult(Activity.RESULT_OK)
|
||||
finish()
|
||||
}
|
||||
}
|
||||
|
||||
binding.toolbar.setNavigationOnClickListener { onBackPressed() }
|
||||
|
||||
if (SignalStore.misc().smsExportPhase.isBlockingUi()) {
|
||||
binding.headline.setText(R.string.SmsExportMegaphoneActivity__signal_no_longer_supports_sms)
|
||||
binding.description.setText(R.string.SmsExportMegaphoneActivity__signal_has_removed_support_for_sending_sms_messages)
|
||||
binding.description.setLearnMoreVisible(false)
|
||||
binding.laterButton.setText(R.string.SmsExportMegaphoneActivity__learn_more)
|
||||
binding.laterButton.setOnClickListener {
|
||||
CommunicationActions.openBrowserLink(this, getString(R.string.sms_export_url))
|
||||
}
|
||||
} else {
|
||||
binding.headline.setText(R.string.SmsExportMegaphoneActivity__signal_will_no_longer_support_sms)
|
||||
binding.description.setText(R.string.SmsExportMegaphoneActivity__signal_will_soon_remove_support_for_sending_sms_messages)
|
||||
binding.description.setLearnMoreVisible(true)
|
||||
binding.description.setLink(getString(R.string.sms_export_url))
|
||||
binding.laterButton.setText(R.string.SmsExportMegaphoneActivity__remind_me_later)
|
||||
binding.laterButton.setOnClickListener {
|
||||
onBackPressed()
|
||||
}
|
||||
}
|
||||
|
||||
binding.exportButton.setOnClickListener {
|
||||
smsExportLauncher.launch(SmsExportActivity.createIntent(this))
|
||||
}
|
||||
}
|
||||
|
||||
override fun onBackPressed() {
|
||||
ApplicationDependencies.getMegaphoneRepository().markSeen(Megaphones.Event.SMS_EXPORT)
|
||||
setResult(Activity.RESULT_CANCELED)
|
||||
super.onBackPressed()
|
||||
}
|
||||
|
||||
override fun onResume() {
|
||||
super.onResume()
|
||||
theme.onResume(this)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,39 @@
|
|||
package org.thoughtcrime.securesms.megaphone
|
||||
|
||||
import android.content.Context
|
||||
import androidx.annotation.WorkerThread
|
||||
import org.thoughtcrime.securesms.database.SignalDatabase.Companion.mmsSms
|
||||
import org.thoughtcrime.securesms.keyvalue.SignalStore
|
||||
import org.thoughtcrime.securesms.keyvalue.SmsExportPhase
|
||||
import org.thoughtcrime.securesms.util.FeatureFlags
|
||||
import org.thoughtcrime.securesms.util.Util
|
||||
import kotlin.time.Duration.Companion.days
|
||||
|
||||
class SmsExportReminderSchedule(private val context: Context) : MegaphoneSchedule {
|
||||
|
||||
companion object {
|
||||
@JvmStatic
|
||||
var showPhase3Megaphone = true
|
||||
}
|
||||
|
||||
private val basicMegaphoneSchedule = RecurringSchedule(3.days.inWholeMilliseconds)
|
||||
private val fullScreenSchedule = RecurringSchedule(1.days.inWholeMilliseconds)
|
||||
|
||||
@WorkerThread
|
||||
override fun shouldDisplay(seenCount: Int, lastSeen: Long, firstVisible: Long, currentTime: Long): Boolean {
|
||||
return if (shouldShowMegaphone()) {
|
||||
when (SignalStore.misc().smsExportPhase) {
|
||||
SmsExportPhase.PHASE_1 -> basicMegaphoneSchedule.shouldDisplay(seenCount, lastSeen, firstVisible, currentTime)
|
||||
SmsExportPhase.PHASE_2 -> fullScreenSchedule.shouldDisplay(seenCount, lastSeen, firstVisible, currentTime)
|
||||
SmsExportPhase.PHASE_3 -> showPhase3Megaphone
|
||||
}
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
@WorkerThread
|
||||
fun shouldShowMegaphone(): Boolean {
|
||||
return FeatureFlags.smsExporter() && (Util.isDefaultSmsProvider(context) || mmsSms.unexportedInsecureMessagesCount > 0)
|
||||
}
|
||||
}
|
|
@ -74,6 +74,7 @@ public class NotificationChannels {
|
|||
public static final String JOIN_EVENTS = "join_events";
|
||||
public static final String BACKGROUND = "background_connection";
|
||||
public static final String CALL_STATUS = "call_status";
|
||||
public static final String APP_ALERTS = "app_alerts";
|
||||
|
||||
/**
|
||||
* Ensures all of the notification channels are created. No harm in repeat calls. Call is safely
|
||||
|
@ -604,6 +605,7 @@ public class NotificationChannels {
|
|||
NotificationChannel joinEvents = new NotificationChannel(JOIN_EVENTS, context.getString(R.string.NotificationChannel_contact_joined_signal), NotificationManager.IMPORTANCE_DEFAULT);
|
||||
NotificationChannel background = new NotificationChannel(BACKGROUND, context.getString(R.string.NotificationChannel_background_connection), getDefaultBackgroundChannelImportance(notificationManager));
|
||||
NotificationChannel callStatus = new NotificationChannel(CALL_STATUS, context.getString(R.string.NotificationChannel_call_status), NotificationManager.IMPORTANCE_LOW);
|
||||
NotificationChannel appAlerts = new NotificationChannel(APP_ALERTS, context.getString(R.string.NotificationChannel_critical_app_alerts), NotificationManager.IMPORTANCE_HIGH);
|
||||
|
||||
messages.setGroup(CATEGORY_MESSAGES);
|
||||
setVibrationEnabled(messages, SignalStore.settings().isMessageVibrateEnabled());
|
||||
|
@ -619,8 +621,9 @@ public class NotificationChannels {
|
|||
joinEvents.setShowBadge(false);
|
||||
background.setShowBadge(false);
|
||||
callStatus.setShowBadge(false);
|
||||
appAlerts.setShowBadge(false);
|
||||
|
||||
notificationManager.createNotificationChannels(Arrays.asList(messages, calls, failures, backups, lockedStatus, other, voiceNotes, joinEvents, background, callStatus));
|
||||
notificationManager.createNotificationChannels(Arrays.asList(messages, calls, failures, backups, lockedStatus, other, voiceNotes, joinEvents, background, callStatus, appAlerts));
|
||||
|
||||
if (BuildConfig.PLAY_STORE_DISABLED) {
|
||||
NotificationChannel appUpdates = new NotificationChannel(APP_UPDATES, context.getString(R.string.NotificationChannel_app_updates), NotificationManager.IMPORTANCE_HIGH);
|
||||
|
|
|
@ -20,6 +20,7 @@ public final class NotificationIds {
|
|||
public static final int DONOR_BADGE_FAILURE = 630001;
|
||||
public static final int FCM_FETCH = 630002;
|
||||
public static final int SMS_EXPORT_SERVICE = 630003;
|
||||
public static final int SMS_EXPORT_COMPLETE = 630004;
|
||||
public static final int STORY_THREAD = 700000;
|
||||
public static final int MESSAGE_DELIVERY_FAILURE = 800000;
|
||||
public static final int STORY_MESSAGE_DELIVERY_FAILURE = 900000;
|
||||
|
|
|
@ -352,6 +352,11 @@ public class RecipientUtil {
|
|||
return threadId != null && SignalDatabase.mmsSms().getOutgoingSecureConversationCount(threadId) != 0;
|
||||
}
|
||||
|
||||
public static boolean isSmsOnly(long threadId, @NonNull Recipient threadRecipient) {
|
||||
return !threadRecipient.isRegistered() ||
|
||||
noSecureMessagesAndNoCallsInThread(threadId);
|
||||
}
|
||||
|
||||
@WorkerThread
|
||||
private static boolean noSecureMessagesAndNoCallsInThread(@Nullable Long threadId) {
|
||||
if (threadId == null) {
|
||||
|
|
|
@ -33,6 +33,7 @@ import org.thoughtcrime.securesms.components.settings.conversation.preferences.B
|
|||
import org.thoughtcrime.securesms.contacts.avatars.FallbackContactPhoto;
|
||||
import org.thoughtcrime.securesms.contacts.avatars.FallbackPhoto80dp;
|
||||
import org.thoughtcrime.securesms.groups.GroupId;
|
||||
import org.thoughtcrime.securesms.keyvalue.SignalStore;
|
||||
import org.thoughtcrime.securesms.phonenumbers.PhoneNumberFormatter;
|
||||
import org.thoughtcrime.securesms.recipients.Recipient;
|
||||
import org.thoughtcrime.securesms.recipients.RecipientExporter;
|
||||
|
@ -221,10 +222,16 @@ public final class RecipientBottomSheetDialogFragment extends BottomSheetDialogF
|
|||
unblockButton.setVisibility(View.GONE);
|
||||
}
|
||||
|
||||
boolean isAudioAvailable = (recipient.isRegistered() || (Util.isDefaultSmsProvider(requireContext()) && SignalStore.misc().getSmsExportPhase().isSmsSupported())) &&
|
||||
!recipient.isGroup() &&
|
||||
!recipient.isBlocked() &&
|
||||
!recipient.isSelf() &&
|
||||
!recipient.isReleaseNotes();
|
||||
|
||||
ButtonStripPreference.State buttonStripState = new ButtonStripPreference.State(
|
||||
/* isMessageAvailable = */ !recipient.isBlocked() && !recipient.isSelf() && !recipient.isReleaseNotes(),
|
||||
/* isVideoAvailable = */ !recipient.isBlocked() && !recipient.isSelf() && recipient.isRegistered(),
|
||||
/* isAudioAvailable = */ !recipient.isBlocked() && !recipient.isSelf() && !recipient.isReleaseNotes(),
|
||||
/* isAudioAvailable = */ isAudioAvailable,
|
||||
/* isMuteAvailable = */ false,
|
||||
/* isSearchAvailable = */ false,
|
||||
/* isAudioSecure = */ recipient.isRegistered(),
|
||||
|
|
|
@ -1,14 +1,15 @@
|
|||
package org.thoughtcrime.securesms.util.views;
|
||||
|
||||
|
||||
import android.view.View;
|
||||
import android.view.ViewStub;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
public class Stub<T> {
|
||||
public class Stub<T extends View> {
|
||||
|
||||
private ViewStub viewStub;
|
||||
private T view;
|
||||
private T view;
|
||||
|
||||
public Stub(@NonNull ViewStub viewStub) {
|
||||
this.viewStub = viewStub;
|
||||
|
@ -16,7 +17,8 @@ public class Stub<T> {
|
|||
|
||||
public T get() {
|
||||
if (view == null) {
|
||||
view = (T)viewStub.inflate();
|
||||
//noinspection unchecked
|
||||
view = (T) viewStub.inflate();
|
||||
viewStub = null;
|
||||
}
|
||||
|
||||
|
@ -27,4 +29,10 @@ public class Stub<T> {
|
|||
return view != null;
|
||||
}
|
||||
|
||||
public void setVisibility(int visibility) {
|
||||
if (resolved()) {
|
||||
get().setVisibility(visibility);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -0,0 +1,18 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="64dp"
|
||||
android:height="64dp"
|
||||
android:viewportWidth="64"
|
||||
android:viewportHeight="64">
|
||||
<path
|
||||
android:pathData="M21.847,5.719C15.943,5.719 11.156,10.505 11.156,16.41V37.792C11.156,43.697 15.943,48.484 21.847,48.484L21.847,56.655C21.847,58.243 23.767,59.037 24.889,57.915L34.321,48.484H43.23C49.134,48.484 53.921,43.697 53.921,37.792V16.41C53.921,10.505 49.134,5.719 43.23,5.719H21.847Z"
|
||||
android:fillColor="#1B1B1D"
|
||||
android:fillType="evenOdd"/>
|
||||
<path
|
||||
android:pathData="M8.483,16.41C8.483,9.029 14.467,3.046 21.847,3.046H43.23C50.611,3.046 56.594,9.029 56.594,16.41V37.792C56.594,45.173 50.611,51.156 43.23,51.156H35.428L26.779,59.805C23.973,62.611 19.175,60.624 19.175,56.655L19.175,50.889C13.075,49.651 8.483,44.258 8.483,37.792V16.41ZM21.847,48.484C15.943,48.484 11.156,43.697 11.156,37.792V16.41C11.156,10.505 15.943,5.719 21.847,5.719H43.23C49.134,5.719 53.921,10.505 53.921,16.41V37.792C53.921,43.697 49.134,48.484 43.23,48.484H34.321L24.889,57.915C23.767,59.037 21.847,58.243 21.847,56.655L21.847,48.484Z"
|
||||
android:fillColor="#C1C6DD"
|
||||
android:fillType="evenOdd"/>
|
||||
<path
|
||||
android:pathData="M38.893,15C35.52,15 32.785,17.79 32.785,21.232V25.212C32.478,25.21 32.142,25.21 31.772,25.21H27.822C25.784,25.21 24.765,25.21 23.987,25.615C23.302,25.971 22.745,26.538 22.397,27.237C22,28.031 22,29.071 22,31.15V33.06C22,35.139 22,36.179 22.397,36.973C22.745,37.671 23.302,38.239 23.987,38.595C24.765,39 25.784,39 27.822,39H31.772C33.81,39 34.828,39 35.607,38.595C36.291,38.239 36.848,37.671 37.197,36.973C37.593,36.179 37.593,35.139 37.593,33.06V31.15C37.593,29.071 37.593,28.031 37.197,27.237C36.848,26.538 36.291,25.971 35.607,25.615C35.457,25.537 35.298,25.474 35.124,25.423V21.232C35.124,19.108 36.811,17.387 38.893,17.387C40.974,17.387 42.661,19.108 42.661,21.232V23.088C42.661,23.747 43.185,24.282 43.831,24.282C44.476,24.282 45,23.747 45,23.088V21.232C45,17.79 42.266,15 38.893,15ZM30.706,32.364C31.175,32.058 31.486,31.522 31.486,30.912C31.486,29.96 30.73,29.188 29.797,29.188C28.864,29.188 28.107,29.96 28.107,30.912C28.107,31.522 28.418,32.058 28.887,32.364V34.227C28.887,34.739 29.294,35.155 29.797,35.155C30.299,35.155 30.706,34.739 30.706,34.227V32.364Z"
|
||||
android:fillColor="#B6C5FA"
|
||||
android:fillType="evenOdd"/>
|
||||
</vector>
|
|
@ -0,0 +1,10 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="64dp"
|
||||
android:height="64dp"
|
||||
android:viewportWidth="64"
|
||||
android:viewportHeight="64">
|
||||
<path
|
||||
android:pathData="M53.965,13.699C55.201,14.749 55.351,16.602 54.301,17.837L26.858,50.135C26.287,50.807 25.443,51.187 24.561,51.169C23.679,51.152 22.852,50.738 22.308,50.043L9.624,33.836C8.625,32.559 8.85,30.713 10.127,29.714C11.404,28.715 13.249,28.94 14.248,30.217L24.714,43.589L49.826,14.035C50.876,12.799 52.729,12.649 53.965,13.699Z"
|
||||
android:fillColor="#000000"
|
||||
android:fillType="evenOdd"/>
|
||||
</vector>
|
|
@ -0,0 +1,18 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="64dp"
|
||||
android:height="64dp"
|
||||
android:viewportWidth="64"
|
||||
android:viewportHeight="64">
|
||||
<path
|
||||
android:pathData="M21.847,5.719C15.943,5.719 11.156,10.505 11.156,16.41V37.792C11.156,43.697 15.943,48.484 21.847,48.484L21.847,56.655C21.847,58.243 23.767,59.037 24.889,57.915L34.321,48.484H43.23C49.134,48.484 53.921,43.697 53.921,37.792V16.41C53.921,10.505 49.134,5.719 43.23,5.719H21.847Z"
|
||||
android:fillColor="#ffffff"
|
||||
android:fillType="evenOdd"/>
|
||||
<path
|
||||
android:pathData="M8.483,16.41C8.483,9.029 14.467,3.046 21.847,3.046H43.23C50.611,3.046 56.594,9.029 56.594,16.41V37.792C56.594,45.173 50.611,51.156 43.23,51.156H35.428L26.779,59.805C23.973,62.611 19.175,60.624 19.175,56.655L19.175,50.889C13.075,49.651 8.483,44.258 8.483,37.792V16.41ZM21.847,48.484C15.943,48.484 11.156,43.697 11.156,37.792V16.41C11.156,10.505 15.943,5.719 21.847,5.719H43.23C49.134,5.719 53.921,10.505 53.921,16.41V37.792C53.921,43.697 49.134,48.484 43.23,48.484H34.321L24.889,57.915C23.767,59.037 21.847,58.243 21.847,56.655L21.847,48.484Z"
|
||||
android:fillColor="#B4B9C4"
|
||||
android:fillType="evenOdd"/>
|
||||
<path
|
||||
android:pathData="M38.893,15C35.52,15 32.785,17.79 32.785,21.232V25.212C32.478,25.21 32.142,25.21 31.772,25.21H27.822C25.784,25.21 24.765,25.21 23.987,25.615C23.302,25.971 22.745,26.538 22.397,27.237C22,28.031 22,29.071 22,31.15V33.06C22,35.139 22,36.179 22.397,36.973C22.745,37.671 23.302,38.239 23.987,38.595C24.765,39 25.784,39 27.822,39H31.772C33.81,39 34.828,39 35.607,38.595C36.291,38.239 36.848,37.671 37.197,36.973C37.593,36.179 37.593,35.139 37.593,33.06V31.15C37.593,29.071 37.593,28.031 37.197,27.237C36.848,26.538 36.291,25.971 35.607,25.615C35.457,25.537 35.298,25.474 35.124,25.423V21.232C35.124,19.108 36.811,17.387 38.893,17.387C40.974,17.387 42.661,19.108 42.661,21.232V23.088C42.661,23.747 43.185,24.282 43.831,24.282C44.476,24.282 45,23.747 45,23.088V21.232C45,17.79 42.266,15 38.893,15ZM30.706,32.364C31.175,32.058 31.486,31.522 31.486,30.912C31.486,29.96 30.73,29.188 29.797,29.188C28.864,29.188 28.107,29.96 28.107,30.912C28.107,31.522 28.418,32.058 28.887,32.364V34.227C28.887,34.739 29.294,35.155 29.797,35.155C30.299,35.155 30.706,34.739 30.706,34.227V32.364Z"
|
||||
android:fillColor="#3A76F0"
|
||||
android:fillType="evenOdd"/>
|
||||
</vector>
|
|
@ -111,7 +111,7 @@
|
|||
android:paddingTop="16dp"
|
||||
android:paddingEnd="16dp"
|
||||
android:paddingBottom="16dp"
|
||||
android:text="@string/AddGroupDetailsFragment__youve_selected_a_contact_that_doesnt_support"
|
||||
android:text="@string/AddGroupDetailsFragment__youve_selected_a_contact_that_doesnt_support_signal_groups_mms_removal"
|
||||
android:textAppearance="@style/Signal.Text.BodyMedium"
|
||||
android:textColor="@color/signal_colorOnSurfaceVariant" />
|
||||
|
||||
|
|
|
@ -134,13 +134,12 @@
|
|||
android:text="@string/ConversationActivity_unblock"
|
||||
android:visibility="gone" />
|
||||
|
||||
<Button
|
||||
android:id="@+id/make_default_sms_button"
|
||||
android:layout_width="fill_parent"
|
||||
<ViewStub
|
||||
android:id="@+id/sms_export_stub"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:padding="20dp"
|
||||
android:text="@string/conversation_activity__enable_signal_for_sms"
|
||||
android:visibility="gone" />
|
||||
android:inflatedId="@+id/sms_export_view"
|
||||
android:layout="@layout/conversation_activity_sms_export_stub" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/space_left"
|
||||
|
|
|
@ -0,0 +1,19 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:gravity="center_horizontal"
|
||||
android:orientation="vertical"
|
||||
android:padding="24dp">
|
||||
|
||||
<org.thoughtcrime.securesms.util.views.LearnMoreTextView
|
||||
android:id="@+id/sms_disabled_learn_more"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:gravity="center"
|
||||
android:textAppearance="@style/Signal.Text.BodyMedium"
|
||||
android:textColor="@color/signal_colorOnSurfaceVariant"
|
||||
tools:text="SMS is disabled, export done thanks" />
|
||||
|
||||
</LinearLayout>
|
|
@ -0,0 +1,27 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:gravity="center_horizontal"
|
||||
android:orientation="vertical"
|
||||
android:padding="24dp">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/export_sms_message"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:gravity="center"
|
||||
android:textAppearance="@style/Signal.Text.BodyMedium"
|
||||
android:textColor="@color/signal_colorOnSurfaceVariant"
|
||||
tools:text="SMS is disabled, export sms now yes" />
|
||||
|
||||
<com.google.android.material.button.MaterialButton
|
||||
android:id="@+id/export_sms_button"
|
||||
style="@style/Signal.Widget.Button.Large.Tonal"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="24dp"
|
||||
tools:text="Export SMS Messages" />
|
||||
|
||||
</LinearLayout>
|
|
@ -0,0 +1,12 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<org.thoughtcrime.securesms.util.views.LearnMoreTextView xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:id="@+id/button"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginHorizontal="@dimen/dsl_settings_gutter"
|
||||
android:layout_marginVertical="16dp"
|
||||
android:background="@drawable/rounded_outline_12"
|
||||
android:padding="16dp"
|
||||
android:textAppearance="@style/Signal.Text.BodyMedium"
|
||||
tools:text="Mark H is Batman!" />
|
|
@ -0,0 +1,58 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/export_complete_title"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/ExportSmsCompleteFragment__export_complete"
|
||||
android:textAppearance="@style/Signal.Text.TitleLarge"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
<androidx.appcompat.widget.AppCompatImageView
|
||||
android:id="@+id/export_complete_done_icon"
|
||||
android:layout_width="112dp"
|
||||
android:layout_height="112dp"
|
||||
android:layout_marginBottom="16dp"
|
||||
android:background="@drawable/circle_tintable"
|
||||
android:backgroundTint="@color/signal_colorSurface2"
|
||||
android:scaleType="centerInside"
|
||||
app:layout_constraintBottom_toTopOf="@+id/export_complete_title"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:srcCompat="@drawable/ic_complete_64"
|
||||
app:tint="@color/signal_accent_green"
|
||||
tools:ignore="UnusedAttribute,ImageContrastCheck" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/export_complete_status"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="8dp"
|
||||
android:textAppearance="@style/Signal.Text.BodySmall"
|
||||
android:textColor="@color/signal_colorOnSurfaceVariant"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/export_complete_title"
|
||||
tools:text="248 of 248 messages exported" />
|
||||
|
||||
<com.google.android.material.button.MaterialButton
|
||||
android:id="@+id/export_complete_next"
|
||||
style="@style/Signal.Widget.Button.Large.Tonal"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:minWidth="200dp"
|
||||
android:layout_marginBottom="24dp"
|
||||
android:text="@string/ExportSmsCompleteFragment__next"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent" />
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
|
@ -1,9 +1,9 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
xmlns:tools="http://schemas.android.com/tools">
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/headline"
|
||||
|
@ -18,7 +18,6 @@
|
|||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
<!-- TODO [alex] - Final text -->
|
||||
<TextView
|
||||
android:id="@+id/description"
|
||||
android:layout_width="match_parent"
|
||||
|
@ -26,7 +25,7 @@
|
|||
android:layout_marginHorizontal="32dp"
|
||||
android:layout_marginTop="24dp"
|
||||
android:gravity="center"
|
||||
tools:text="[WIP]"
|
||||
android:text="@string/ExportingSmsMessagesFragment__this_may_take_awhile"
|
||||
android:textAppearance="@style/Signal.Text.BodyLarge"
|
||||
android:textColor="@color/signal_colorOnSurfaceVariant"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
|
@ -44,15 +43,15 @@
|
|||
app:layout_constraintWidth_max="260dp" />
|
||||
|
||||
<TextView
|
||||
app:layout_constraintTop_toBottomOf="@id/progress"
|
||||
android:gravity="center"
|
||||
android:layout_marginHorizontal="32dp"
|
||||
android:layout_marginTop="11dp"
|
||||
tools:text="Exporting 5 of 264..."
|
||||
android:id="@+id/progress_label"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginHorizontal="32dp"
|
||||
android:layout_marginTop="11dp"
|
||||
android:gravity="center"
|
||||
android:textAppearance="@style/Signal.Text.BodySmall"
|
||||
android:textColor="@color/signal_colorOnSurfaceVariant" />
|
||||
android:textColor="@color/signal_colorOnSurfaceVariant"
|
||||
app:layout_constraintTop_toBottomOf="@id/progress"
|
||||
tools:text="Exporting 5 of 264..." />
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
|
@ -0,0 +1,99 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:orientation="vertical">
|
||||
|
||||
<androidx.appcompat.widget.Toolbar
|
||||
android:id="@+id/toolbar"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="64dp"
|
||||
android:minHeight="64dp"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:navigationIcon="@drawable/ic_arrow_left_24" />
|
||||
|
||||
<ScrollView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:fillViewport="true">
|
||||
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
|
||||
<androidx.appcompat.widget.AppCompatImageView
|
||||
android:id="@+id/image"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="20dp"
|
||||
android:importantForAccessibility="no"
|
||||
android:scaleType="centerInside"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:srcCompat="@drawable/sms_message" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/headline"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginHorizontal="32dp"
|
||||
android:layout_marginTop="40dp"
|
||||
android:gravity="center"
|
||||
android:textAppearance="@style/Signal.Text.HeadlineLarge"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/image"
|
||||
tools:text="SMS Export megaphone title" />
|
||||
|
||||
<org.thoughtcrime.securesms.util.views.LearnMoreTextView
|
||||
android:id="@+id/description"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginHorizontal="32dp"
|
||||
android:layout_marginTop="24dp"
|
||||
android:gravity="center"
|
||||
android:textAppearance="@style/Signal.Text.BodyLarge"
|
||||
android:textColor="@color/signal_colorOnSurfaceVariant"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/headline"
|
||||
tools:text="SMS export megaphone body" />
|
||||
|
||||
<com.google.android.material.button.MaterialButton
|
||||
android:id="@+id/export_button"
|
||||
style="@style/Signal.Widget.Button.Large.Tonal"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginBottom="16dp"
|
||||
android:minWidth="221dp"
|
||||
android:text="@string/SmsExportMegaphoneActivity__export_sms"
|
||||
app:layout_constraintBottom_toTopOf="@+id/later_button"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/description"
|
||||
app:layout_constraintVertical_bias="1" />
|
||||
|
||||
<com.google.android.material.button.MaterialButton
|
||||
android:id="@+id/later_button"
|
||||
style="@style/Signal.Widget.Button.Large.Secondary"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginBottom="24dp"
|
||||
android:minWidth="221dp"
|
||||
android:textColor="@color/signal_colorOnSurfaceVariant"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintVertical_bias="1"
|
||||
tools:text="@string/SmsExportMegaphoneActivity__remind_me_later" />
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
</ScrollView>
|
||||
</LinearLayout>
|
|
@ -34,7 +34,9 @@
|
|||
app:enterAnim="@anim/fragment_open_enter"
|
||||
app:exitAnim="@anim/fragment_open_exit"
|
||||
app:popEnterAnim="@anim/fragment_close_enter"
|
||||
app:popExitAnim="@anim/fragment_close_exit" />
|
||||
app:popExitAnim="@anim/fragment_close_exit"
|
||||
app:popUpTo="@+id/exportYourSmsMessagesFragment"
|
||||
app:popUpToInclusive="true" />
|
||||
</fragment>
|
||||
|
||||
<fragment
|
||||
|
@ -42,6 +44,23 @@
|
|||
android:name="org.thoughtcrime.securesms.exporter.flow.ExportingSmsMessagesFragment"
|
||||
android:label="fragment_exporting_sms_messages"
|
||||
tools:layout="@layout/exporting_sms_messages_fragment">
|
||||
<action
|
||||
android:id="@+id/action_exportingSmsMessagesFragment_to_exportSmsCompleteFragment"
|
||||
app:destination="@id/exportSmsCompleteFragment"
|
||||
app:enterAnim="@anim/fragment_open_enter"
|
||||
app:exitAnim="@anim/fragment_open_exit"
|
||||
app:popEnterAnim="@anim/fragment_close_enter"
|
||||
app:popExitAnim="@anim/fragment_close_exit" />
|
||||
</fragment>
|
||||
|
||||
<fragment
|
||||
android:id="@+id/exportSmsCompleteFragment"
|
||||
android:name="org.thoughtcrime.securesms.exporter.flow.ExportSmsCompleteFragment"
|
||||
tools:layout="@layout/export_sms_complete_fragment">
|
||||
|
||||
<argument android:name="export_message_count"
|
||||
app:argType="integer" />
|
||||
|
||||
<action
|
||||
android:id="@+id/action_exportingSmsMessagesFragment_to_chooseANewDefaultSmsAppFragment"
|
||||
app:destination="@id/chooseANewDefaultSmsAppFragment"
|
||||
|
@ -49,6 +68,7 @@
|
|||
app:exitAnim="@anim/fragment_open_exit"
|
||||
app:popEnterAnim="@anim/fragment_close_enter"
|
||||
app:popExitAnim="@anim/fragment_close_exit" />
|
||||
|
||||
</fragment>
|
||||
|
||||
<fragment
|
||||
|
@ -57,4 +77,14 @@
|
|||
android:label="fragment_choose_a_new_default_sms_app"
|
||||
tools:layout="@layout/choose_a_new_default_sms_app_fragment" />
|
||||
|
||||
<action
|
||||
android:id="@+id/action_direct_to_exportSmsCompleteFragment"
|
||||
app:destination="@id/exportSmsCompleteFragment"
|
||||
app:enterAnim="@anim/fragment_open_enter"
|
||||
app:exitAnim="@anim/fragment_open_exit"
|
||||
app:popEnterAnim="@anim/fragment_close_enter"
|
||||
app:popExitAnim="@anim/fragment_close_exit"
|
||||
app:popUpTo="@+id/exportYourSmsMessagesFragment"
|
||||
app:popUpToInclusive="true"/>
|
||||
|
||||
</navigation>
|
|
@ -11,6 +11,7 @@
|
|||
<string name="sustainer_boost_and_badges" translatable="false">https://support.signal.org/hc/articles/4408365318426</string>
|
||||
<string name="google_pay_url" translatable="false">https://pay.google.com</string>
|
||||
<string name="donation_decline_code_error_url" translatable="false">https://support.signal.org/hc/articles/4408365318426#errors</string>
|
||||
<string name="sms_export_url" translatable="false">https://support.signal.org/hc/articles/360007321171</string>
|
||||
<string name="signal_me_username_url" translatable="false">https://signal.me/#u/%1$s</string>
|
||||
<string name="signal_me_username_url_no_scheme" translatable="false">signal.me/#u/%1$s</string>
|
||||
|
||||
|
@ -343,6 +344,17 @@
|
|||
|
||||
<string name="ConversationActivity__reported_as_spam_and_blocked">Reported as spam and blocked.</string>
|
||||
|
||||
<!-- Message shown when opening an SMS conversation with SMS disabled and they have unexported sms messages -->
|
||||
<string name="ConversationActivity__sms_messaging_is_no_longer_supported_in_signal_you_can_export_your_messages_to_another_app_on_your_phone">SMS messaging is no longer supported in Signal. You can export your messages to another app on your phone.</string>
|
||||
<!-- Action button shown when in sms conversation, sms is disabled, and unexported sms messages are present -->
|
||||
<string name="ConversationActivity__export_sms_messages">Export SMS messages</string>
|
||||
<!-- Message shown when opening an SMS conversation with SMS disabled and there are no exported messages -->
|
||||
<string name="ConversationActivity__sms_messaging_is_no_longer_supported_in_signal_invite_s_to_to_signal_to_keep_the_conversation_here">SMS messaging is no longer supported in Signal. Invite %1$s to Signal to keep the conversation here.</string>
|
||||
<!-- Action button shown when opening an SMS conversation with SMS disabled and there are no exported messages -->
|
||||
<string name="ConversationActivity__invite_to_signal">Invite to Signal</string>
|
||||
<!-- Snackbar message shown after dismissing the full screen sms export megaphone indicating we'll do it again soon -->
|
||||
<string name="ConversationActivity__you_will_be_reminded_again_soon">You will be reminded again soon.</string>
|
||||
|
||||
<!-- ConversationAdapter -->
|
||||
<plurals name="ConversationAdapter_n_unread_messages">
|
||||
<item quantity="one">%d unread message</item>
|
||||
|
@ -802,11 +814,11 @@
|
|||
<string name="AddGroupDetailsFragment__this_field_is_required">This field is required.</string>
|
||||
<string name="AddGroupDetailsFragment__group_creation_failed">Group creation failed.</string>
|
||||
<string name="AddGroupDetailsFragment__try_again_later">Try again later.</string>
|
||||
<!-- Displayed when adding group details to an MMS Group -->
|
||||
<string name="AddGroupDetailsFragment__youve_selected_a_contact_that_doesnt_support">You\'ve selected a contact that doesn\'t support Signal groups, so this group will be MMS. Custom MMS group names and photos will only be visible to you.</string>
|
||||
<string name="AddGroupDetailsFragment__remove">Remove</string>
|
||||
<string name="AddGroupDetailsFragment__sms_contact">SMS contact</string>
|
||||
<string name="AddGroupDetailsFragment__remove_s_from_this_group">Remove %1$s from this group?</string>
|
||||
<!-- Displayed when adding group details to an MMS Group -->
|
||||
<string name="AddGroupDetailsFragment__youve_selected_a_contact_that_doesnt_support_signal_groups_mms_removal">You\'ve selected a contact that dosen\'t support Signal groups, this group will be MMS. Custom MMS group names and photos will only be visible to you. Support for MMS groups will be removed soon to focus on encrypted messaging.</string>
|
||||
|
||||
<!-- ManageGroupActivity -->
|
||||
<string name="ManageGroupActivity_member_requests_and_invites">Member requests & invites</string>
|
||||
|
@ -990,6 +1002,8 @@
|
|||
|
||||
<!-- InputPanel -->
|
||||
<string name="InputPanel_tap_and_hold_to_record_a_voice_message_release_to_send">Tap and hold to record a voice message, release to send</string>
|
||||
<!-- Message shown if the user tries to switch a conversation from Signal to SMS -->
|
||||
<string name="InputPanel__sms_messaging_is_no_longer_supported_in_signal">SMS messaging is no longer supported in Signal.</string>
|
||||
|
||||
<!-- InviteActivity -->
|
||||
<string name="InviteActivity_share">Share</string>
|
||||
|
@ -1375,6 +1389,11 @@
|
|||
<item quantity="other">%1$s, %2$s, and %3$d others are in the group call</item>
|
||||
</plurals>
|
||||
|
||||
<!-- In-conversation update message to indicate that the current contact is sms only and will need to migrate to signal to continue the conversation in signal. -->
|
||||
<string name="MessageRecord__you_will_on_longer_be_able_to_send_sms_messages_from_signal_soon">You will on longer be able to send SMS messages from Signal soon. Invite %1$s to Signal to keep the conversation here.</string>
|
||||
<!-- In-conversation update message to indicate that the current contact is sms only and will need to migrate to signal to continue the conversation in signal. -->
|
||||
<string name="MessageRecord__you_can_no_longer_send_sms_messages_in_signal">You can no longer send SMS messages in Signal. Invite %1$s to Signal to keep the conversation here.</string>
|
||||
|
||||
<!-- MessageRequestBottomView -->
|
||||
<string name="MessageRequestBottomView_accept">Accept</string>
|
||||
<string name="MessageRequestBottomView_continue">Continue</string>
|
||||
|
@ -2017,6 +2036,9 @@
|
|||
<string name="NotificationChannel_background_connection">Background connection</string>
|
||||
<!-- Notification channel name for showing call status information (like connection, ongoing, etc.) Not ringing. -->
|
||||
<string name="NotificationChannel_call_status">Call status</string>
|
||||
<!-- Notification channel name for occasional alerts to the user -->
|
||||
<string name="NotificationChannel_critical_app_alerts">Critical app alerts</string>
|
||||
|
||||
<!-- ProfileEditNameFragment -->
|
||||
|
||||
<!-- QuickResponseService -->
|
||||
|
@ -3976,6 +3998,12 @@
|
|||
<string name="SmsSettingsFragment__removing_sms_messages_from_signal">Removing SMS messages from Signal…</string>
|
||||
<!-- Snackbar text to indicate can delete later -->
|
||||
<string name="SmsSettingsFragment__you_can_remove_sms_messages_from_signal_in_settings">You can remove SMS messages from Signal in Settings at any time.</string>
|
||||
<!-- Description for export sms preference -->
|
||||
<string name="SmsSettingsFragment__you_can_export_your_sms_messages_to_your_phones_sms_database">You can export your SMS messages to your phone\'s SMS database</string>
|
||||
<!-- Description for remove sms preference -->
|
||||
<string name="SmsSettingsFragment__remove_sms_messages_from_signal_to_clear_up_storage_space">Remove SMS messages from Signal to clear up storage space.</string>
|
||||
<!-- Information message shown at the top of sms settings to indicate it is being removed soon. -->
|
||||
<string name="SmsSettingsFragment__sms_support_will_be_removed_soon_to_focus_on_encrypted_messaging">SMS support will be removed soon to focus on encrypted messaging.</string>
|
||||
|
||||
<!-- NotificationsSettingsFragment -->
|
||||
<string name="NotificationsSettingsFragment__messages">Messages</string>
|
||||
|
@ -4236,6 +4264,8 @@
|
|||
<string name="NewConversationActivity__this_person_is_saved_to_your">This person is saved to your device\'s Contacts. Delete them from your Contacts and try again.</string>
|
||||
<!-- Dialog action to view contact when they can't be removed otherwise -->
|
||||
<string name="NewConversationActivity__view_contact">View contact</string>
|
||||
<!-- Error message shown when looking up a person by phone number and that phone number is not associated with a signal account -->
|
||||
<string name="NewConversationActivity__s_is_not_a_signal_user">%1$s is not a Signal user</string>
|
||||
|
||||
<!-- ContactFilterView -->
|
||||
<string name="ContactFilterView__search_name_or_number">Search name or number</string>
|
||||
|
@ -5289,6 +5319,10 @@
|
|||
<!-- SMS Export Service -->
|
||||
<!-- Displayed in the notification while export is running -->
|
||||
<string name="SignalSmsExportService__exporting_messages">Exporting messages…</string>
|
||||
<!-- Displayed in the notification title when export completes -->
|
||||
<string name="SignalSmsExportService__signal_sms_export_complete">Signal SMS Export Complete</string>
|
||||
<!-- Displayed in the notification message when export completes -->
|
||||
<string name="SignalSmsExportService__tap_to_return_to_signal">Tap to return to Signal</string>
|
||||
|
||||
<!-- ExportYourSmsMessagesFragment -->
|
||||
<!-- Title of the screen -->
|
||||
|
@ -5301,6 +5335,8 @@
|
|||
<!-- ExportingSmsMessagesFragment -->
|
||||
<!-- Title of the screen -->
|
||||
<string name="ExportingSmsMessagesFragment__exporting_sms_messages">Exporting SMS messages</string>
|
||||
<!-- Message of the screen when exporting sms messages -->
|
||||
<string name="ExportingSmsMessagesFragment__this_may_take_awhile">This may take awhile</string>
|
||||
<!-- Progress indicator for export -->
|
||||
<string name="ExportingSmsMessagesFragment__exporting_d_of_d">Exporting %1$d of %2$d…</string>
|
||||
|
||||
|
@ -5368,6 +5404,35 @@
|
|||
<!-- Re-enable backups permission bottom sheet call to action button to open settings -->
|
||||
<string name="BackupSchedulePermissionMegaphone__go_to_settings">Go to settings</string>
|
||||
|
||||
<!-- SmsExportMegaphoneActivity -->
|
||||
<!-- Phase 2 title of full screen megaphone indicating sms will no longer be supported in the near future -->
|
||||
<string name="SmsExportMegaphoneActivity__signal_will_no_longer_support_sms">Signal will no longer support SMS</string>
|
||||
<!-- Phase 3 title of full screen megaphone indicating sms is longer supported -->
|
||||
<string name="SmsExportMegaphoneActivity__signal_no_longer_supports_sms">Signal no longer supports SMS</string>
|
||||
<!-- Phase 2 message describing that sms is going away soon -->
|
||||
<string name="SmsExportMegaphoneActivity__signal_will_soon_remove_support_for_sending_sms_messages">Signal will soon remove support for sending SMS messages because Signal messages provide end-to-end encryption and strong privacy that SMS messages don\'t. This will also allow us to improve the Signal messaging experience.</string>
|
||||
<!-- Phase 3 message describing that sms has gone away -->
|
||||
<string name="SmsExportMegaphoneActivity__signal_has_removed_support_for_sending_sms_messages">Signal has removed support for sending SMS messages because Signal messages provide end-to-end encryption and strong privacy that SMS messages don\'t. This will also allow us to improve the Signal messaging experience.</string>
|
||||
<!-- Button on full screen megaphone that takes the user to the export messages flow -->
|
||||
<string name="SmsExportMegaphoneActivity__export_sms">Export SMS</string>
|
||||
<!-- Button on full screen megaphone that dismiss the megaphone and it'll be shown again at a later time -->
|
||||
<string name="SmsExportMegaphoneActivity__remind_me_later">Remind me later</string>
|
||||
<!-- Button on full screen megaphone that navigates to a web article on sms removal -->
|
||||
<string name="SmsExportMegaphoneActivity__learn_more">Learn more</string>
|
||||
|
||||
<!-- Phase 1 Small megaphone title indicating sms is going away -->
|
||||
<string name="SmsExportMegaphone__sms_support_going_away">SMS support going away</string>
|
||||
<!-- Phase 1 small megaphone description indicating sms is going away -->
|
||||
<string name="SmsExportMegaphone__sms_support_will_be_removed_soon_to_focus_on_encrypted_messaging">SMS support will be removed soon to focus on encrypted messaging.</string>
|
||||
<!-- Phase 1 small megaphone button that takes the user to the sms export flow -->
|
||||
<string name="SmsExportMegaphone__export_sms">Export SMS</string>
|
||||
<!-- Title for screen shown after sms export has completed -->
|
||||
<string name="ExportSmsCompleteFragment__export_complete">Export Complete</string>
|
||||
<!-- Button to continue to next screen -->
|
||||
<string name="ExportSmsCompleteFragment__next">Next</string>
|
||||
<!-- Message showing summary of sms export counts -->
|
||||
<string name="ExportSmsCompleteFragment__d_of_d_messages_exported">%1$d of %2$d messages exported</string>
|
||||
|
||||
<!-- EOF -->
|
||||
|
||||
</resources>
|
||||
|
|
|
@ -50,9 +50,11 @@ import org.thoughtcrime.securesms.database.MmsSmsColumns.Types.OUTGOING_VIDEO_CA
|
|||
import org.thoughtcrime.securesms.database.MmsSmsColumns.Types.PROFILE_CHANGE_TYPE
|
||||
import org.thoughtcrime.securesms.database.MmsSmsColumns.Types.PUSH_MESSAGE_BIT
|
||||
import org.thoughtcrime.securesms.database.MmsSmsColumns.Types.SECURE_MESSAGE_BIT
|
||||
import org.thoughtcrime.securesms.database.MmsSmsColumns.Types.SMS_EXPORT_TYPE
|
||||
import org.thoughtcrime.securesms.database.MmsSmsColumns.Types.SPECIAL_TYPES_MASK
|
||||
import org.thoughtcrime.securesms.database.MmsSmsColumns.Types.SPECIAL_TYPE_GIFT_BADGE
|
||||
import org.thoughtcrime.securesms.database.MmsSmsColumns.Types.SPECIAL_TYPE_STORY_REACTION
|
||||
import org.thoughtcrime.securesms.database.MmsSmsColumns.Types.THREAD_MERGE_TYPE
|
||||
import org.thoughtcrime.securesms.database.MmsSmsColumns.Types.UNSUPPORTED_MESSAGE_TYPE
|
||||
|
||||
object MessageBitmaskColumnTransformer : ColumnTransformer {
|
||||
|
@ -111,6 +113,8 @@ object MessageBitmaskColumnTransformer : ColumnTransformer {
|
|||
isGroupV1MigrationEvent:${type == GV1_MIGRATION_TYPE}
|
||||
isChangeNumber:${type == CHANGE_NUMBER_TYPE}
|
||||
isBoostRequest:${type == BOOST_REQUEST_TYPE}
|
||||
isThreadMerge:${type == THREAD_MERGE_TYPE}
|
||||
isSmsExport:${type == SMS_EXPORT_TYPE}
|
||||
isGroupV2LeaveOnly:${type and GROUP_V2_LEAVE_BITS == GROUP_V2_LEAVE_BITS}
|
||||
isSpecialType:${type and SPECIAL_TYPES_MASK != 0L}
|
||||
isStoryReaction:${type and SPECIAL_TYPES_MASK == SPECIAL_TYPE_STORY_REACTION}
|
||||
|
|
|
@ -81,6 +81,9 @@ class SmsDatabaseTest {
|
|||
TestSms.insert(db, type = MmsSmsColumns.Types.BOOST_REQUEST_TYPE)
|
||||
assertFalse(smsDatabase.hasMeaningfulMessage(1))
|
||||
|
||||
TestSms.insert(db, type = MmsSmsColumns.Types.SMS_EXPORT_TYPE)
|
||||
assertFalse(smsDatabase.hasMeaningfulMessage(1))
|
||||
|
||||
TestSms.insert(db, type = MmsSmsColumns.Types.BASE_INBOX_TYPE or MmsSmsColumns.Types.GROUP_V2_LEAVE_BITS)
|
||||
assertFalse(smsDatabase.hasMeaningfulMessage(1))
|
||||
}
|
||||
|
|
|
@ -50,6 +50,13 @@ fun SupportSQLiteDatabase.select(vararg columns: String): SelectBuilderPart1 {
|
|||
return SelectBuilderPart1(this, arrayOf(*columns))
|
||||
}
|
||||
|
||||
/**
|
||||
* Begins a COUNT statement with a helpful builder pattern.
|
||||
*/
|
||||
fun SupportSQLiteDatabase.count(): SelectBuilderPart1 {
|
||||
return SelectBuilderPart1(this, SqlUtil.COUNT)
|
||||
}
|
||||
|
||||
/**
|
||||
* Begins an UPDATE statement with a helpful builder pattern.
|
||||
*/
|
||||
|
|
|
@ -38,7 +38,7 @@ class MainActivity : AppCompatActivity(R.layout.main_activity) {
|
|||
|
||||
disposables += SmsExportService.progressState.onBackpressureLatest().subscribeOn(Schedulers.computation()).observeOn(AndroidSchedulers.mainThread()).subscribe {
|
||||
when (it) {
|
||||
SmsExportProgress.Done -> {
|
||||
is SmsExportProgress.Done -> {
|
||||
exportStatus.text = "Done"
|
||||
exportProgress.isVisible = true
|
||||
}
|
||||
|
|
|
@ -33,6 +33,10 @@ class TestSmsExportService : SmsExportService() {
|
|||
)
|
||||
}
|
||||
|
||||
override fun getExportCompleteNotification(): ExportNotification? {
|
||||
return null
|
||||
}
|
||||
|
||||
override fun getUnexportedMessageCount(): Int {
|
||||
return 50
|
||||
}
|
||||
|
|
|
@ -25,5 +25,5 @@ sealed class SmsExportProgress {
|
|||
/**
|
||||
* All done.
|
||||
*/
|
||||
object Done : SmsExportProgress()
|
||||
data class Done(val progress: Int) : SmsExportProgress()
|
||||
}
|
||||
|
|
|
@ -4,6 +4,7 @@ import android.app.Notification
|
|||
import android.app.Service
|
||||
import android.content.Intent
|
||||
import android.os.IBinder
|
||||
import androidx.core.app.NotificationManagerCompat
|
||||
import io.reactivex.rxjava3.processors.BehaviorProcessor
|
||||
import org.signal.core.util.Result
|
||||
import org.signal.core.util.Try
|
||||
|
@ -78,7 +79,12 @@ abstract class SmsExportService : Service() {
|
|||
}
|
||||
|
||||
onExportPassCompleted()
|
||||
progressState.onNext(SmsExportProgress.Done)
|
||||
progressState.onNext(SmsExportProgress.Done(progress))
|
||||
|
||||
getExportCompleteNotification()?.let { notification ->
|
||||
NotificationManagerCompat.from(this).notify(notification.id, notification.notification)
|
||||
}
|
||||
|
||||
stopForeground(true)
|
||||
isStarted = false
|
||||
}
|
||||
|
@ -97,6 +103,13 @@ abstract class SmsExportService : Service() {
|
|||
*/
|
||||
protected abstract fun getNotification(progress: Int, total: Int): ExportNotification
|
||||
|
||||
/**
|
||||
* Produces the notification and notification id to display when the export is complete.
|
||||
*
|
||||
* Can be null if no notification is needed (e.g., the user is still in the app)
|
||||
*/
|
||||
protected abstract fun getExportCompleteNotification(): ExportNotification?
|
||||
|
||||
/**
|
||||
* Gets the total number of messages to process. This is only used for the notification and
|
||||
* progress events.
|
||||
|
|
Ładowanie…
Reference in New Issue