Merge tag 'v6.9.2' into molly-6.9
This commit is contained in:
commit
b65a9b0728
|
@ -20,7 +20,7 @@ RUN yes | sdkmanager --licenses
|
|||
|
||||
RUN sdkmanager "platform-tools"
|
||||
|
||||
ARG ANDROID_API_LEVEL=32
|
||||
ARG ANDROID_API_LEVEL=33
|
||||
ARG ANDROID_BUILD_TOOLS_VERSION=32.0.0
|
||||
|
||||
RUN sdkmanager "platforms;android-${ANDROID_API_LEVEL}"
|
||||
|
|
|
@ -83,8 +83,8 @@ ktlint {
|
|||
version = "0.43.2"
|
||||
}
|
||||
|
||||
def canonicalVersionCode = 1196
|
||||
def canonicalVersionName = "6.8.3"
|
||||
def canonicalVersionCode = 1199
|
||||
def canonicalVersionName = "6.9.2"
|
||||
def mollyRevision = 0
|
||||
|
||||
def postFixSize = 100
|
||||
|
|
|
@ -86,9 +86,10 @@
|
|||
android:icon="@mipmap/ic_launcher"
|
||||
android:label="@string/app_name"
|
||||
android:supportsRtl="true"
|
||||
tools:replace="android:allowBackup"
|
||||
android:resizeableActivity="true"
|
||||
android:allowBackup="false"
|
||||
android:fullBackupOnly="false"
|
||||
android:allowBackup="true"
|
||||
android:backupAgent=".absbackup.SignalBackupAgent"
|
||||
android:theme="@style/TextSecure.LightTheme"
|
||||
android:largeHeap="true">
|
||||
|
||||
|
|
|
@ -4,8 +4,6 @@ import android.content.Context;
|
|||
import android.net.Uri;
|
||||
import android.provider.DocumentsContract;
|
||||
|
||||
import androidx.annotation.RequiresApi;
|
||||
|
||||
import org.signal.core.util.logging.Log;
|
||||
|
||||
/**
|
||||
|
@ -22,7 +20,6 @@ public class DocumentFileHelper {
|
|||
*
|
||||
* @return true if rename successful
|
||||
*/
|
||||
@RequiresApi(21)
|
||||
public static boolean renameTo(Context context, DocumentFile documentFile, String displayName) {
|
||||
if (documentFile instanceof TreeDocumentFile) {
|
||||
Log.d(TAG, "Renaming document directly");
|
||||
|
|
|
@ -5,7 +5,6 @@ import android.content.Context
|
|||
import android.content.Intent
|
||||
import android.os.Build
|
||||
import androidx.activity.result.contract.ActivityResultContract
|
||||
import androidx.annotation.RequiresApi
|
||||
import androidx.biometric.BiometricManager
|
||||
import androidx.biometric.BiometricPrompt
|
||||
import androidx.biometric.BiometricPrompt.PromptInfo
|
||||
|
@ -44,7 +43,7 @@ class BiometricDeviceAuthentication(
|
|||
Log.i(TAG, "Skipping show system biometric or device lock dialog unless forced")
|
||||
}
|
||||
true
|
||||
} else if (Build.VERSION.SDK_INT >= 21) {
|
||||
} else {
|
||||
if (force) {
|
||||
Log.i(TAG, "firing intent...")
|
||||
showConfirmDeviceCredentialIntent()
|
||||
|
@ -52,9 +51,6 @@ class BiometricDeviceAuthentication(
|
|||
Log.i(TAG, "Skipping firing intent unless forced")
|
||||
}
|
||||
true
|
||||
} else {
|
||||
Log.w(TAG, "Not compatible...")
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -65,7 +61,6 @@ class BiometricDeviceAuthentication(
|
|||
|
||||
class BiometricDeviceLockContract : ActivityResultContract<String, Int>() {
|
||||
|
||||
@RequiresApi(api = 21)
|
||||
override fun createIntent(context: Context, input: String): Intent {
|
||||
val keyguardManager = ServiceUtil.getKeyguardManager(context)
|
||||
return keyguardManager.createConfirmDeviceCredentialIntent(input, "")
|
||||
|
|
|
@ -142,8 +142,8 @@ public class DeviceActivity extends PassphraseRequiredActivity
|
|||
private void goToDeviceLink(@Nullable Uri uri) {
|
||||
deviceLinkFragment.setLinkClickedListener(uri, DeviceActivity.this);
|
||||
|
||||
deviceAddFragment.setSharedElementReturnTransition(TransitionInflater.from(DeviceActivity.this).inflateTransition(R.transition.fragment_shared));
|
||||
deviceAddFragment.setExitTransition(TransitionInflater.from(DeviceActivity.this).inflateTransition(android.R.transition.fade));
|
||||
deviceAddFragment.setSharedElementReturnTransition(TransitionInflater.from(DeviceActivity.this).inflateTransition(R.transition.fragment_shared));
|
||||
deviceAddFragment.setExitTransition(TransitionInflater.from(DeviceActivity.this).inflateTransition(android.R.transition.fade));
|
||||
|
||||
deviceLinkFragment.setSharedElementEnterTransition(TransitionInflater.from(DeviceActivity.this).inflateTransition(R.transition.fragment_shared));
|
||||
deviceLinkFragment.setEnterTransition(TransitionInflater.from(DeviceActivity.this).inflateTransition(android.R.transition.fade));
|
||||
|
|
|
@ -52,22 +52,19 @@ public class DeviceAddFragment extends LoggingFragment {
|
|||
this.devicesImage = container.findViewById(R.id.devices);
|
||||
ViewCompat.setTransitionName(devicesImage, "devices");
|
||||
|
||||
if (Build.VERSION.SDK_INT >= 21) {
|
||||
container.addOnLayoutChangeListener(new View.OnLayoutChangeListener() {
|
||||
@TargetApi(21)
|
||||
@Override
|
||||
public void onLayoutChange(View v, int left, int top, int right, int bottom,
|
||||
int oldLeft, int oldTop, int oldRight, int oldBottom)
|
||||
{
|
||||
v.removeOnLayoutChangeListener(this);
|
||||
container.addOnLayoutChangeListener(new View.OnLayoutChangeListener() {
|
||||
@Override
|
||||
public void onLayoutChange(View v, int left, int top, int right, int bottom,
|
||||
int oldLeft, int oldTop, int oldRight, int oldBottom)
|
||||
{
|
||||
v.removeOnLayoutChangeListener(this);
|
||||
|
||||
Animator reveal = ViewAnimationUtils.createCircularReveal(v, right, bottom, 0, (int) Math.hypot(right, bottom));
|
||||
reveal.setInterpolator(new DecelerateInterpolator(2f));
|
||||
reveal.setDuration(800);
|
||||
reveal.start();
|
||||
}
|
||||
});
|
||||
}
|
||||
Animator reveal = ViewAnimationUtils.createCircularReveal(v, right, bottom, 0, (int) Math.hypot(right, bottom));
|
||||
reveal.setInterpolator(new DecelerateInterpolator(2f));
|
||||
reveal.setDuration(800);
|
||||
reveal.start();
|
||||
}
|
||||
});
|
||||
|
||||
return container;
|
||||
}
|
||||
|
|
|
@ -0,0 +1,11 @@
|
|||
package org.thoughtcrime.securesms.absbackup
|
||||
|
||||
/**
|
||||
* Abstracts away the implementation of pieces of data we want to hand off to various backup services.
|
||||
* Here we can control precisely which data gets backed up and more importantly, what does not.
|
||||
*/
|
||||
interface AndroidBackupItem {
|
||||
fun getKey(): String
|
||||
fun getDataForBackup(): ByteArray
|
||||
fun restoreData(data: ByteArray)
|
||||
}
|
|
@ -0,0 +1,65 @@
|
|||
package org.thoughtcrime.securesms.absbackup
|
||||
|
||||
import android.app.backup.BackupAgent
|
||||
import android.app.backup.BackupDataInput
|
||||
import android.app.backup.BackupDataOutput
|
||||
import android.os.ParcelFileDescriptor
|
||||
import org.signal.core.util.logging.Log
|
||||
import org.thoughtcrime.securesms.absbackup.backupables.KbsAuthTokens
|
||||
import java.io.DataInputStream
|
||||
import java.io.DataOutputStream
|
||||
import java.io.FileInputStream
|
||||
import java.io.FileOutputStream
|
||||
import java.io.IOException
|
||||
|
||||
/**
|
||||
* Uses the [Android Backup Service](https://developer.android.com/guide/topics/data/keyvaluebackup) and backs up everything in [items]
|
||||
*/
|
||||
class SignalBackupAgent : BackupAgent() {
|
||||
private val items: List<AndroidBackupItem> = listOf(
|
||||
KbsAuthTokens,
|
||||
)
|
||||
|
||||
override fun onBackup(oldState: ParcelFileDescriptor?, data: BackupDataOutput, newState: ParcelFileDescriptor) {
|
||||
val contentsHash = cumulativeHashCode()
|
||||
if (oldState == null) {
|
||||
performBackup(data)
|
||||
} else {
|
||||
val hash = try {
|
||||
DataInputStream(FileInputStream(oldState.fileDescriptor)).use { it.readInt() }
|
||||
} catch (e: IOException) {
|
||||
Log.w(TAG, "No old state, may be first backup request or bug with not writing to newState at end.", e)
|
||||
}
|
||||
if (hash != contentsHash) {
|
||||
performBackup(data)
|
||||
}
|
||||
}
|
||||
|
||||
DataOutputStream(FileOutputStream(newState.fileDescriptor)).use { it.writeInt(contentsHash) }
|
||||
}
|
||||
|
||||
private fun performBackup(data: BackupDataOutput) {
|
||||
items.forEach {
|
||||
val backupData = it.getDataForBackup()
|
||||
data.writeEntityHeader(it.getKey(), backupData.size)
|
||||
data.writeEntityData(backupData, backupData.size)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onRestore(dataInput: BackupDataInput, appVersionCode: Int, newState: ParcelFileDescriptor) {
|
||||
while (dataInput.readNextHeader()) {
|
||||
val buffer = ByteArray(dataInput.dataSize)
|
||||
dataInput.readEntityData(buffer, 0, dataInput.dataSize)
|
||||
items.find { dataInput.key == it.getKey() }?.restoreData(buffer)
|
||||
}
|
||||
DataOutputStream(FileOutputStream(newState.fileDescriptor)).use { it.writeInt(cumulativeHashCode()) }
|
||||
}
|
||||
|
||||
private fun cumulativeHashCode(): Int {
|
||||
return items.fold("") { acc: String, androidBackupItem: AndroidBackupItem -> acc + androidBackupItem.getDataForBackup().decodeToString() }.hashCode()
|
||||
}
|
||||
|
||||
companion object {
|
||||
private const val TAG = "SignalBackupAgent"
|
||||
}
|
||||
}
|
|
@ -0,0 +1,40 @@
|
|||
package org.thoughtcrime.securesms.absbackup.backupables
|
||||
|
||||
import com.google.protobuf.InvalidProtocolBufferException
|
||||
import org.signal.core.util.logging.Log
|
||||
import org.thoughtcrime.securesms.absbackup.AndroidBackupItem
|
||||
import org.thoughtcrime.securesms.absbackup.ExternalBackupProtos
|
||||
import org.thoughtcrime.securesms.keyvalue.SignalStore
|
||||
|
||||
/**
|
||||
* This backs up the not-secret KBS Auth tokens, which can be combined with a PIN to prove ownership of a phone number in order to complete the registration process.
|
||||
*/
|
||||
object KbsAuthTokens : AndroidBackupItem {
|
||||
private const val TAG = "KbsAuthTokens"
|
||||
|
||||
override fun getKey(): String {
|
||||
return TAG
|
||||
}
|
||||
|
||||
override fun getDataForBackup(): ByteArray {
|
||||
val registrationRecoveryTokenList = SignalStore.kbsValues().kbsAuthTokenList
|
||||
val proto = ExternalBackupProtos.KbsAuthToken.newBuilder()
|
||||
.addAllToken(registrationRecoveryTokenList)
|
||||
.build()
|
||||
return proto.toByteArray()
|
||||
}
|
||||
|
||||
override fun restoreData(data: ByteArray) {
|
||||
if (SignalStore.kbsValues().kbsAuthTokenList.isNotEmpty()) {
|
||||
return
|
||||
}
|
||||
|
||||
try {
|
||||
val proto = ExternalBackupProtos.KbsAuthToken.parseFrom(data)
|
||||
|
||||
SignalStore.kbsValues().putAuthTokenList(proto.tokenList)
|
||||
} catch (e: InvalidProtocolBufferException) {
|
||||
Log.w(TAG, "Cannot restore KbsAuthToken from backup service.")
|
||||
}
|
||||
}
|
||||
}
|
|
@ -13,7 +13,6 @@ import android.view.ViewGroup
|
|||
import android.view.animation.AccelerateInterpolator
|
||||
import android.view.animation.DecelerateInterpolator
|
||||
import android.view.animation.Interpolator
|
||||
import androidx.annotation.RequiresApi
|
||||
|
||||
private const val POSITION_ON_SCREEN = "signal.circleavatartransition.positiononscreen"
|
||||
private const val WIDTH = "signal.circleavatartransition.width"
|
||||
|
@ -22,7 +21,6 @@ private const val HEIGHT = "signal.circleavatartransition.height"
|
|||
/**
|
||||
* Custom transition for Circular avatars, because once you have multiple things animating stuff was getting broken and weird.
|
||||
*/
|
||||
@RequiresApi(21)
|
||||
class CircleAvatarTransition(context: Context, attrs: AttributeSet?) : Transition(context, attrs) {
|
||||
override fun captureStartValues(transitionValues: TransitionValues) {
|
||||
captureValues(transitionValues)
|
||||
|
|
|
@ -7,11 +7,9 @@ import android.transition.Transition
|
|||
import android.transition.TransitionValues
|
||||
import android.util.AttributeSet
|
||||
import android.view.ViewGroup
|
||||
import androidx.annotation.RequiresApi
|
||||
import androidx.core.animation.doOnEnd
|
||||
import androidx.core.animation.doOnStart
|
||||
|
||||
@RequiresApi(21)
|
||||
class CrossfaderTransition(context: Context, attrs: AttributeSet?) : Transition(context, attrs) {
|
||||
|
||||
companion object {
|
||||
|
|
|
@ -10,7 +10,6 @@ import android.transition.TransitionValues
|
|||
import android.util.AttributeSet
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.annotation.RequiresApi
|
||||
import androidx.core.animation.addListener
|
||||
import androidx.fragment.app.FragmentContainerView
|
||||
|
||||
|
@ -19,7 +18,6 @@ private const val BOUNDS = "signal.wipedowntransition.bottom"
|
|||
/**
|
||||
* WipeDownTransition will animate the bottom position of a view such that it "wipes" down the screen to a final position.
|
||||
*/
|
||||
@RequiresApi(21)
|
||||
class WipeDownTransition(context: Context, attrs: AttributeSet?) : Transition(context, attrs) {
|
||||
override fun captureStartValues(transitionValues: TransitionValues) {
|
||||
captureValues(transitionValues)
|
||||
|
|
|
@ -248,10 +248,6 @@ public class ComposeText extends EmojiEditText {
|
|||
editorInfo.imeOptions &= ~EditorInfo.IME_FLAG_NO_ENTER_ACTION;
|
||||
}
|
||||
|
||||
if (Build.VERSION.SDK_INT < 21) {
|
||||
return inputConnection;
|
||||
}
|
||||
|
||||
if (mediaListener == null) {
|
||||
return inputConnection;
|
||||
}
|
||||
|
|
|
@ -41,7 +41,6 @@ class SignalBottomActionBar(context: Context, attributeSet: AttributeSet) : Line
|
|||
init {
|
||||
orientation = HORIZONTAL
|
||||
setBackgroundResource(R.drawable.signal_bottom_action_bar_background)
|
||||
|
||||
elevation = 20f
|
||||
}
|
||||
|
||||
|
|
|
@ -19,15 +19,8 @@ import java.util.List;
|
|||
public class ExpiredBuildReminder extends Reminder {
|
||||
|
||||
public ExpiredBuildReminder(final Context context) {
|
||||
super(null, Build.VERSION.SDK_INT < 21
|
||||
? context.getString(R.string.ExpiredBuildReminder_api_19_message)
|
||||
: context.getString(R.string.ExpiredBuildReminder_this_version_of_signal_has_expired));
|
||||
|
||||
if (Build.VERSION.SDK_INT < 21) {
|
||||
addAction(new Action(context.getString(R.string.API19Reminder_learn_more), R.id.reminder_action_api_19_learn_more));
|
||||
} else {
|
||||
addAction(new Action(context.getString(R.string.ExpiredBuildReminder_update_now), R.id.reminder_action_update_now));
|
||||
}
|
||||
super(null, context.getString(R.string.ExpiredBuildReminder_this_version_of_signal_has_expired));
|
||||
addAction(new Action(context.getString(R.string.ExpiredBuildReminder_update_now), R.id.reminder_action_update_now));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
package org.thoughtcrime.securesms.components.settings
|
||||
|
||||
import android.graphics.PorterDuff
|
||||
import android.graphics.PorterDuffColorFilter
|
||||
import android.text.Spanned
|
||||
import android.text.method.LinkMovementMethod
|
||||
import android.text.style.ClickableSpan
|
||||
|
@ -230,7 +232,9 @@ class ExternalLinkPreferenceViewHolder(itemView: View) : PreferenceViewHolder<Ex
|
|||
override fun bind(model: ExternalLinkPreference) {
|
||||
super.bind(model)
|
||||
|
||||
val externalLinkIcon = requireNotNull(ContextCompat.getDrawable(context, R.drawable.ic_open_20))
|
||||
val externalLinkIcon = requireNotNull(ContextCompat.getDrawable(context, R.drawable.symbol_open_20)).apply {
|
||||
colorFilter = PorterDuffColorFilter(ContextCompat.getColor(context, R.color.signal_icon_tint_primary), PorterDuff.Mode.SRC_IN)
|
||||
}
|
||||
externalLinkIcon.setBounds(0, 0, ViewUtil.dpToPx(20), ViewUtil.dpToPx(20))
|
||||
|
||||
if (ViewUtil.isLtr(itemView)) {
|
||||
|
|
|
@ -47,7 +47,7 @@ class AppSettingsFragment : DSLSettingsFragment(R.string.text_secure_normal__men
|
|||
|
||||
clickPref(
|
||||
title = DSLSettingsText.from(R.string.AccountSettingsFragment__account),
|
||||
icon = DSLSettingsIcon.from(R.drawable.ic_profile_circle_24),
|
||||
icon = DSLSettingsIcon.from(R.drawable.symbol_person_circle_24),
|
||||
onClick = {
|
||||
findNavController().safeNavigate(R.id.action_appSettingsFragment_to_accountSettingsFragment)
|
||||
}
|
||||
|
@ -55,7 +55,7 @@ class AppSettingsFragment : DSLSettingsFragment(R.string.text_secure_normal__men
|
|||
|
||||
clickPref(
|
||||
title = DSLSettingsText.from(R.string.preferences__linked_devices),
|
||||
icon = DSLSettingsIcon.from(R.drawable.ic_linked_devices_24),
|
||||
icon = DSLSettingsIcon.from(R.drawable.symbol_devices_24),
|
||||
onClick = {
|
||||
findNavController().safeNavigate(R.id.action_appSettingsFragment_to_deviceActivity)
|
||||
}
|
||||
|
@ -63,7 +63,7 @@ class AppSettingsFragment : DSLSettingsFragment(R.string.text_secure_normal__men
|
|||
|
||||
externalLinkPref(
|
||||
title = DSLSettingsText.from(R.string.preferences__donate_to_signal),
|
||||
icon = DSLSettingsIcon.from(R.drawable.ic_heart_24),
|
||||
icon = DSLSettingsIcon.from(R.drawable.symbol_heart_24),
|
||||
linkId = R.string.donate_url
|
||||
)
|
||||
|
||||
|
@ -71,7 +71,7 @@ class AppSettingsFragment : DSLSettingsFragment(R.string.text_secure_normal__men
|
|||
|
||||
clickPref(
|
||||
title = DSLSettingsText.from(R.string.preferences__appearance),
|
||||
icon = DSLSettingsIcon.from(R.drawable.ic_appearance_24),
|
||||
icon = DSLSettingsIcon.from(R.drawable.symbol_appearance_24),
|
||||
onClick = {
|
||||
findNavController().safeNavigate(R.id.action_appSettingsFragment_to_appearanceSettingsFragment)
|
||||
}
|
||||
|
@ -79,7 +79,7 @@ class AppSettingsFragment : DSLSettingsFragment(R.string.text_secure_normal__men
|
|||
|
||||
clickPref(
|
||||
title = DSLSettingsText.from(R.string.preferences_chats__chats),
|
||||
icon = DSLSettingsIcon.from(R.drawable.ic_chat_message_24),
|
||||
icon = DSLSettingsIcon.from(R.drawable.symbol_chat_24),
|
||||
onClick = {
|
||||
findNavController().safeNavigate(R.id.action_appSettingsFragment_to_chatsSettingsFragment)
|
||||
}
|
||||
|
@ -87,7 +87,7 @@ class AppSettingsFragment : DSLSettingsFragment(R.string.text_secure_normal__men
|
|||
|
||||
clickPref(
|
||||
title = DSLSettingsText.from(R.string.preferences__stories),
|
||||
icon = DSLSettingsIcon.from(R.drawable.ic_stories_24),
|
||||
icon = DSLSettingsIcon.from(R.drawable.symbol_stories_24),
|
||||
onClick = {
|
||||
findNavController().safeNavigate(AppSettingsFragmentDirections.actionAppSettingsFragmentToStoryPrivacySettings(R.string.preferences__stories))
|
||||
}
|
||||
|
@ -95,7 +95,7 @@ class AppSettingsFragment : DSLSettingsFragment(R.string.text_secure_normal__men
|
|||
|
||||
clickPref(
|
||||
title = DSLSettingsText.from(R.string.preferences__notifications),
|
||||
icon = DSLSettingsIcon.from(R.drawable.ic_bell_24),
|
||||
icon = DSLSettingsIcon.from(R.drawable.symbol_bell_24),
|
||||
onClick = {
|
||||
findNavController().safeNavigate(R.id.action_appSettingsFragment_to_notificationsSettingsFragment)
|
||||
}
|
||||
|
@ -103,7 +103,7 @@ class AppSettingsFragment : DSLSettingsFragment(R.string.text_secure_normal__men
|
|||
|
||||
clickPref(
|
||||
title = DSLSettingsText.from(R.string.preferences__privacy),
|
||||
icon = DSLSettingsIcon.from(R.drawable.ic_lock_24),
|
||||
icon = DSLSettingsIcon.from(R.drawable.symbol_lock_24),
|
||||
onClick = {
|
||||
findNavController().safeNavigate(R.id.action_appSettingsFragment_to_privacySettingsFragment)
|
||||
}
|
||||
|
@ -119,7 +119,7 @@ class AppSettingsFragment : DSLSettingsFragment(R.string.text_secure_normal__men
|
|||
|
||||
clickPref(
|
||||
title = DSLSettingsText.from(R.string.preferences__data_and_storage),
|
||||
icon = DSLSettingsIcon.from(R.drawable.ic_archive_24dp),
|
||||
icon = DSLSettingsIcon.from(R.drawable.symbol_data_24),
|
||||
onClick = {
|
||||
findNavController().safeNavigate(R.id.action_appSettingsFragment_to_dataAndStorageSettingsFragment)
|
||||
}
|
||||
|
@ -141,7 +141,7 @@ class AppSettingsFragment : DSLSettingsFragment(R.string.text_secure_normal__men
|
|||
|
||||
clickPref(
|
||||
title = DSLSettingsText.from(R.string.preferences__help),
|
||||
icon = DSLSettingsIcon.from(R.drawable.ic_help_24),
|
||||
icon = DSLSettingsIcon.from(R.drawable.symbol_help_24),
|
||||
onClick = {
|
||||
findNavController().safeNavigate(R.id.action_appSettingsFragment_to_helpSettingsFragment)
|
||||
}
|
||||
|
@ -149,7 +149,7 @@ class AppSettingsFragment : DSLSettingsFragment(R.string.text_secure_normal__men
|
|||
|
||||
clickPref(
|
||||
title = DSLSettingsText.from(R.string.AppSettingsFragment__invite_your_friends),
|
||||
icon = DSLSettingsIcon.from(R.drawable.ic_invite_24),
|
||||
icon = DSLSettingsIcon.from(R.drawable.symbol_invite_24),
|
||||
onClick = {
|
||||
findNavController().safeNavigate(R.id.action_appSettingsFragment_to_inviteActivity)
|
||||
}
|
||||
|
|
|
@ -178,7 +178,7 @@ class EditNotificationProfileFragment : DSLSettingsFragment(layoutId = R.layout.
|
|||
emojiView?.setImageDrawable(drawable)
|
||||
viewModel.onEmojiSelected(emoji)
|
||||
} else {
|
||||
emojiView?.setImageResource(R.drawable.ic_add_emoji)
|
||||
emojiView?.setImageResource(R.drawable.symbol_emoji_plus_24)
|
||||
viewModel.onEmojiSelected("")
|
||||
}
|
||||
}
|
||||
|
|
|
@ -28,8 +28,7 @@ class VoiceNoteProximityWakeLockManager(
|
|||
private val mediaController: MediaControllerCompat
|
||||
) : DefaultLifecycleObserver {
|
||||
|
||||
private val wakeLock: PowerManager.WakeLock? =
|
||||
ServiceUtil.getPowerManager(activity.applicationContext).newWakeLock(PowerManager.PROXIMITY_SCREEN_OFF_WAKE_LOCK, TAG)
|
||||
private val wakeLock: PowerManager.WakeLock? = ServiceUtil.getPowerManager(activity.applicationContext).newWakeLock(PowerManager.PROXIMITY_SCREEN_OFF_WAKE_LOCK, TAG)
|
||||
|
||||
private val sensorManager: SensorManager = ServiceUtil.getSensorManager(activity)
|
||||
private val proximitySensor: Sensor? = sensorManager.getDefaultSensor(Sensor.TYPE_PROXIMITY)
|
||||
|
|
|
@ -189,7 +189,6 @@ public final class WebRtcAnswerDeclineButton extends LinearLayout implements Acc
|
|||
swipeUpText.setAlpha(1);
|
||||
swipeDownText.setAlpha(1);
|
||||
answer.setRotation(0);
|
||||
|
||||
answer.getDrawable().setTint(getResources().getColor(R.color.green_600));
|
||||
answer.getBackground().setTint(Color.WHITE);
|
||||
|
||||
|
|
|
@ -0,0 +1,23 @@
|
|||
package org.thoughtcrime.securesms.contacts.paged
|
||||
|
||||
import android.view.View
|
||||
import org.thoughtcrime.securesms.util.adapter.mapping.PagingMappingAdapter
|
||||
|
||||
/**
|
||||
* Default contact search adapter, using the models defined in `ContactSearchItems`
|
||||
*/
|
||||
class ContactSearchAdapter(
|
||||
displayCheckBox: Boolean,
|
||||
displaySmsTag: ContactSearchItems.DisplaySmsTag,
|
||||
recipientListener: (View, ContactSearchData.KnownRecipient, Boolean) -> Unit,
|
||||
storyListener: (View, ContactSearchData.Story, Boolean) -> Unit,
|
||||
storyContextMenuCallbacks: ContactSearchItems.StoryContextMenuCallbacks,
|
||||
expandListener: (ContactSearchData.Expand) -> Unit
|
||||
) : PagingMappingAdapter<ContactSearchKey>() {
|
||||
init {
|
||||
ContactSearchItems.registerStoryItems(this, displayCheckBox, storyListener, storyContextMenuCallbacks)
|
||||
ContactSearchItems.registerKnownRecipientItems(this, displayCheckBox, displaySmsTag, recipientListener)
|
||||
ContactSearchItems.registerHeaders(this)
|
||||
ContactSearchItems.registerExpands(this, expandListener)
|
||||
}
|
||||
}
|
|
@ -18,7 +18,7 @@ sealed class ContactSearchData(val contactSearchKey: ContactSearchKey) {
|
|||
val recipient: Recipient,
|
||||
val count: Int,
|
||||
val privacyMode: DistributionListPrivacyMode
|
||||
) : ContactSearchData(ContactSearchKey.RecipientSearchKey.Story(recipient.id))
|
||||
) : ContactSearchData(ContactSearchKey.RecipientSearchKey(recipient.id, true))
|
||||
|
||||
/**
|
||||
* A row displaying a known recipient.
|
||||
|
@ -27,7 +27,7 @@ sealed class ContactSearchData(val contactSearchKey: ContactSearchKey) {
|
|||
val recipient: Recipient,
|
||||
val shortSummary: Boolean = false,
|
||||
val headerLetter: String? = null
|
||||
) : ContactSearchData(ContactSearchKey.RecipientSearchKey.KnownRecipient(recipient.id))
|
||||
) : ContactSearchData(ContactSearchKey.RecipientSearchKey(recipient.id, false))
|
||||
|
||||
/**
|
||||
* A row containing a title for a given section
|
||||
|
|
|
@ -45,6 +45,18 @@ object ContactSearchItems {
|
|||
)
|
||||
}
|
||||
|
||||
fun registerKnownRecipientItems(
|
||||
mappingAdapter: MappingAdapter,
|
||||
displayCheckBox: Boolean,
|
||||
displaySmsTag: DisplaySmsTag,
|
||||
recipientListener: (View, ContactSearchData.KnownRecipient, Boolean) -> Unit
|
||||
) {
|
||||
mappingAdapter.registerFactory(
|
||||
RecipientModel::class.java,
|
||||
LayoutFactory({ KnownRecipientViewHolder(it, displayCheckBox, displaySmsTag, recipientListener) }, R.layout.contact_search_item)
|
||||
)
|
||||
}
|
||||
|
||||
fun registerHeaders(mappingAdapter: MappingAdapter) {
|
||||
mappingAdapter.registerFactory(
|
||||
HeaderModel::class.java,
|
||||
|
@ -52,21 +64,7 @@ object ContactSearchItems {
|
|||
)
|
||||
}
|
||||
|
||||
fun register(
|
||||
mappingAdapter: MappingAdapter,
|
||||
displayCheckBox: Boolean,
|
||||
displaySmsTag: DisplaySmsTag,
|
||||
recipientListener: RecipientClickListener,
|
||||
storyListener: StoryClickListener,
|
||||
storyContextMenuCallbacks: StoryContextMenuCallbacks,
|
||||
expandListener: (ContactSearchData.Expand) -> Unit
|
||||
) {
|
||||
registerStoryItems(mappingAdapter, displayCheckBox, storyListener, storyContextMenuCallbacks)
|
||||
mappingAdapter.registerFactory(
|
||||
RecipientModel::class.java,
|
||||
LayoutFactory({ KnownRecipientViewHolder(it, displayCheckBox, displaySmsTag, recipientListener) }, R.layout.contact_search_item)
|
||||
)
|
||||
registerHeaders(mappingAdapter)
|
||||
fun registerExpands(mappingAdapter: MappingAdapter, expandListener: (ContactSearchData.Expand) -> Unit) {
|
||||
mappingAdapter.registerFactory(
|
||||
ExpandModel::class.java,
|
||||
LayoutFactory({ ExpandViewHolder(it, expandListener) }, R.layout.contacts_expand_item)
|
||||
|
@ -90,7 +88,7 @@ object ContactSearchItems {
|
|||
/**
|
||||
* Story Model
|
||||
*/
|
||||
private class StoryModel(val story: ContactSearchData.Story, val isSelected: Boolean, val hasBeenNotified: Boolean) : MappingModel<StoryModel> {
|
||||
class StoryModel(val story: ContactSearchData.Story, val isSelected: Boolean, val hasBeenNotified: Boolean) : MappingModel<StoryModel> {
|
||||
|
||||
override fun areItemsTheSame(newItem: StoryModel): Boolean {
|
||||
return newItem.story == story
|
||||
|
@ -226,7 +224,7 @@ object ContactSearchItems {
|
|||
/**
|
||||
* Recipient model
|
||||
*/
|
||||
private class RecipientModel(val knownRecipient: ContactSearchData.KnownRecipient, val isSelected: Boolean, val shortSummary: Boolean) : MappingModel<RecipientModel> {
|
||||
class RecipientModel(val knownRecipient: ContactSearchData.KnownRecipient, val isSelected: Boolean, val shortSummary: Boolean) : MappingModel<RecipientModel> {
|
||||
|
||||
override fun areItemsTheSame(newItem: RecipientModel): Boolean {
|
||||
return newItem.knownRecipient == knownRecipient
|
||||
|
@ -363,7 +361,7 @@ object ContactSearchItems {
|
|||
/**
|
||||
* Mapping Model for section headers
|
||||
*/
|
||||
private class HeaderModel(val header: ContactSearchData.Header) : MappingModel<HeaderModel> {
|
||||
class HeaderModel(val header: ContactSearchData.Header) : MappingModel<HeaderModel> {
|
||||
override fun areItemsTheSame(newItem: HeaderModel): Boolean {
|
||||
return header.sectionKey == newItem.header.sectionKey
|
||||
}
|
||||
|
@ -407,7 +405,7 @@ object ContactSearchItems {
|
|||
/**
|
||||
* Mapping Model for expandable content rows.
|
||||
*/
|
||||
private class ExpandModel(val expand: ContactSearchData.Expand) : MappingModel<ExpandModel> {
|
||||
class ExpandModel(val expand: ContactSearchData.Expand) : MappingModel<ExpandModel> {
|
||||
override fun areItemsTheSame(newItem: ExpandModel): Boolean {
|
||||
return expand.contactSearchKey == newItem.expand.contactSearchKey
|
||||
}
|
||||
|
|
|
@ -12,44 +12,18 @@ sealed class ContactSearchKey {
|
|||
|
||||
/**
|
||||
* Generates a ShareContact object used to display which contacts have been selected. This should *not*
|
||||
* be used for the final sharing process, as it is not always truthful about, for example, KnownRecipient of
|
||||
* be used for the final sharing process, as it is not always truthful about, for example,
|
||||
* a group vs. a group's Story.
|
||||
*/
|
||||
open fun requireShareContact(): ShareContact = error("This key cannot be converted into a ShareContact")
|
||||
|
||||
open fun requireParcelable(): ParcelableRecipientSearchKey = error("This key cannot be parcelized")
|
||||
open fun requireRecipientSearchKey(): RecipientSearchKey = error("This key cannot be parcelized")
|
||||
|
||||
sealed class RecipientSearchKey : ContactSearchKey() {
|
||||
@Parcelize
|
||||
data class RecipientSearchKey(val recipientId: RecipientId, val isStory: Boolean) : ContactSearchKey(), Parcelable {
|
||||
override fun requireRecipientSearchKey(): RecipientSearchKey = this
|
||||
|
||||
abstract val recipientId: RecipientId
|
||||
abstract val isStory: Boolean
|
||||
|
||||
data class Story(override val recipientId: RecipientId) : RecipientSearchKey() {
|
||||
override fun requireShareContact(): ShareContact {
|
||||
return ShareContact(recipientId)
|
||||
}
|
||||
|
||||
override fun requireParcelable(): ParcelableRecipientSearchKey {
|
||||
return ParcelableRecipientSearchKey(ParcelableType.STORY, recipientId)
|
||||
}
|
||||
|
||||
override val isStory: Boolean = true
|
||||
}
|
||||
|
||||
/**
|
||||
* Key to a recipient which already exists in our database
|
||||
*/
|
||||
data class KnownRecipient(override val recipientId: RecipientId) : RecipientSearchKey() {
|
||||
override fun requireShareContact(): ShareContact {
|
||||
return ShareContact(recipientId)
|
||||
}
|
||||
|
||||
override fun requireParcelable(): ParcelableRecipientSearchKey {
|
||||
return ParcelableRecipientSearchKey(ParcelableType.KNOWN_RECIPIENT, recipientId)
|
||||
}
|
||||
|
||||
override val isStory: Boolean = false
|
||||
}
|
||||
override fun requireShareContact(): ShareContact = ShareContact(recipientId)
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -61,19 +35,4 @@ sealed class ContactSearchKey {
|
|||
* Key to an expand button for a given section
|
||||
*/
|
||||
data class Expand(val sectionKey: ContactSearchConfiguration.SectionKey) : ContactSearchKey()
|
||||
|
||||
@Parcelize
|
||||
data class ParcelableRecipientSearchKey(val type: ParcelableType, val recipientId: RecipientId) : Parcelable {
|
||||
fun asRecipientSearchKey(): RecipientSearchKey {
|
||||
return when (type) {
|
||||
ParcelableType.STORY -> RecipientSearchKey.Story(recipientId)
|
||||
ParcelableType.KNOWN_RECIPIENT -> RecipientSearchKey.KnownRecipient(recipientId)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
enum class ParcelableType {
|
||||
STORY,
|
||||
KNOWN_RECIPIENT
|
||||
}
|
||||
}
|
||||
|
|
|
@ -27,26 +27,23 @@ class ContactSearchMediator(
|
|||
displaySmsTag: ContactSearchItems.DisplaySmsTag,
|
||||
mapStateToConfiguration: (ContactSearchState) -> ContactSearchConfiguration,
|
||||
private val contactSelectionPreFilter: (View?, Set<ContactSearchKey>) -> Set<ContactSearchKey> = { _, s -> s },
|
||||
performSafetyNumberChecks: Boolean = true
|
||||
performSafetyNumberChecks: Boolean = true,
|
||||
adapterFactory: AdapterFactory = DefaultAdapterFactory
|
||||
) {
|
||||
|
||||
private val viewModel: ContactSearchViewModel = ViewModelProvider(fragment, ContactSearchViewModel.Factory(selectionLimits, ContactSearchRepository(), performSafetyNumberChecks)).get(ContactSearchViewModel::class.java)
|
||||
|
||||
init {
|
||||
val adapter = adapterFactory.create(
|
||||
displayCheckBox,
|
||||
displaySmsTag,
|
||||
this::toggleSelection,
|
||||
this::toggleStorySelection,
|
||||
StoryContextMenuCallbacks()
|
||||
) { viewModel.expandSection(it.sectionKey) }
|
||||
|
||||
val adapter = PagingMappingAdapter<ContactSearchKey>()
|
||||
recyclerView.adapter = adapter
|
||||
|
||||
ContactSearchItems.register(
|
||||
mappingAdapter = adapter,
|
||||
displayCheckBox = displayCheckBox,
|
||||
displaySmsTag = displaySmsTag,
|
||||
recipientListener = this::toggleSelection,
|
||||
storyListener = this::toggleStorySelection,
|
||||
storyContextMenuCallbacks = StoryContextMenuCallbacks(),
|
||||
expandListener = { viewModel.expandSection(it.sectionKey) }
|
||||
)
|
||||
|
||||
val dataAndSelection: LiveData<Pair<List<ContactSearchData>, Set<ContactSearchKey>>> = LiveDataUtil.combineLatest(
|
||||
viewModel.data,
|
||||
viewModel.selectionState,
|
||||
|
@ -90,7 +87,7 @@ class ContactSearchMediator(
|
|||
return viewModel.errorEventsStream.observeOn(AndroidSchedulers.mainThread())
|
||||
}
|
||||
|
||||
fun addToVisibleGroupStories(groupStories: Set<ContactSearchKey.RecipientSearchKey.Story>) {
|
||||
fun addToVisibleGroupStories(groupStories: Set<ContactSearchKey.RecipientSearchKey>) {
|
||||
viewModel.addToVisibleGroupStories(groupStories)
|
||||
}
|
||||
|
||||
|
@ -143,4 +140,32 @@ class ContactSearchMediator(
|
|||
.show()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Wraps the construction of a PagingMappingAdapter<ContactSearchKey> so that it can
|
||||
* be swapped for another implementation, allow listeners to be wrapped, etc.
|
||||
*/
|
||||
fun interface AdapterFactory {
|
||||
fun create(
|
||||
displayCheckBox: Boolean,
|
||||
displaySmsTag: ContactSearchItems.DisplaySmsTag,
|
||||
recipientListener: (View, ContactSearchData.KnownRecipient, Boolean) -> Unit,
|
||||
storyListener: (View, ContactSearchData.Story, Boolean) -> Unit,
|
||||
storyContextMenuCallbacks: ContactSearchItems.StoryContextMenuCallbacks,
|
||||
expandListener: (ContactSearchData.Expand) -> Unit
|
||||
): PagingMappingAdapter<ContactSearchKey>
|
||||
}
|
||||
|
||||
private object DefaultAdapterFactory : AdapterFactory {
|
||||
override fun create(
|
||||
displayCheckBox: Boolean,
|
||||
displaySmsTag: ContactSearchItems.DisplaySmsTag,
|
||||
recipientListener: (View, ContactSearchData.KnownRecipient, Boolean) -> Unit,
|
||||
storyListener: (View, ContactSearchData.Story, Boolean) -> Unit,
|
||||
storyContextMenuCallbacks: ContactSearchItems.StoryContextMenuCallbacks,
|
||||
expandListener: (ContactSearchData.Expand) -> Unit
|
||||
): PagingMappingAdapter<ContactSearchKey> {
|
||||
return ContactSearchAdapter(displayCheckBox, displaySmsTag, recipientListener, storyListener, storyContextMenuCallbacks, expandListener)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -21,8 +21,7 @@ class ContactSearchRepository {
|
|||
val isSelectable = when (it) {
|
||||
is ContactSearchKey.Expand -> false
|
||||
is ContactSearchKey.Header -> false
|
||||
is ContactSearchKey.RecipientSearchKey.KnownRecipient -> canSelectRecipient(it.recipientId)
|
||||
is ContactSearchKey.RecipientSearchKey.Story -> canSelectRecipient(it.recipientId)
|
||||
is ContactSearchKey.RecipientSearchKey -> canSelectRecipient(it.recipientId)
|
||||
}
|
||||
ContactSearchSelectionResult(it, isSelectable)
|
||||
}.toSet()
|
||||
|
|
|
@ -98,7 +98,7 @@ class ContactSearchViewModel(
|
|||
return selectionStore.state
|
||||
}
|
||||
|
||||
fun addToVisibleGroupStories(groupStories: Set<ContactSearchKey.RecipientSearchKey.Story>) {
|
||||
fun addToVisibleGroupStories(groupStories: Set<ContactSearchKey.RecipientSearchKey>) {
|
||||
disposables += contactSearchRepository.markDisplayAsStory(groupStories.map { it.recipientId }).subscribe {
|
||||
configurationStore.update { state ->
|
||||
state.copy(
|
||||
|
|
|
@ -77,8 +77,8 @@ class SafetyNumberRepository(
|
|||
private fun List<ContactSearchKey>.flattenToRecipientIds(): Set<RecipientId> {
|
||||
return this
|
||||
.map {
|
||||
when (it) {
|
||||
is ContactSearchKey.RecipientSearchKey.KnownRecipient -> {
|
||||
when {
|
||||
it is ContactSearchKey.RecipientSearchKey && !it.isStory -> {
|
||||
val recipient = Recipient.resolved(it.recipientId)
|
||||
if (recipient.isGroup) {
|
||||
recipient.participantIds
|
||||
|
@ -86,7 +86,7 @@ class SafetyNumberRepository(
|
|||
listOf(it.recipientId)
|
||||
}
|
||||
}
|
||||
is ContactSearchKey.RecipientSearchKey.Story -> Recipient.resolved(it.recipientId).participantIds
|
||||
it is ContactSearchKey.RecipientSearchKey -> Recipient.resolved(it.recipientId).participantIds
|
||||
else -> throw AssertionError("Invalid contact selection $it")
|
||||
}
|
||||
}
|
||||
|
|
|
@ -30,8 +30,11 @@ open class ConversationActivity : PassphraseRequiredActivity(), ConversationPare
|
|||
}
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?, ready: Boolean) {
|
||||
shareDataTimestamp = savedInstanceState?.getLong(STATE_WATERMARK, -1L) ?: -1L
|
||||
|
||||
if (savedInstanceState != null) {
|
||||
shareDataTimestamp = savedInstanceState.getLong(STATE_WATERMARK, -1L)
|
||||
} else if (intent.flags and Intent.FLAG_ACTIVITY_LAUNCHED_FROM_HISTORY != 0) {
|
||||
shareDataTimestamp = System.currentTimeMillis()
|
||||
}
|
||||
setContentView(R.layout.conversation_parent_fragment_container)
|
||||
|
||||
if (savedInstanceState == null) {
|
||||
|
|
|
@ -1532,7 +1532,7 @@ public class ConversationParentFragment extends Fragment
|
|||
SafetyNumberBottomSheet
|
||||
.forIdentityRecordsAndDestination(
|
||||
records,
|
||||
new ContactSearchKey.RecipientSearchKey.KnownRecipient(recipient.getId())
|
||||
new ContactSearchKey.RecipientSearchKey(recipient.getId(), false)
|
||||
)
|
||||
.show(getChildFragmentManager());
|
||||
}
|
||||
|
@ -2365,7 +2365,7 @@ public class ConversationParentFragment extends Fragment
|
|||
}
|
||||
|
||||
private void showGroupCallingTooltip() {
|
||||
if (Build.VERSION.SDK_INT == 19 || !SignalStore.tooltips().shouldShowGroupCallingTooltip() || callingTooltipShown) {
|
||||
if (!SignalStore.tooltips().shouldShowGroupCallingTooltip() || callingTooltipShown) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -2636,7 +2636,7 @@ public class ConversationParentFragment extends Fragment
|
|||
TextView message = inviteButton.get().findViewById(R.id.export_sms_message);
|
||||
MaterialButton actionButton = inviteButton.get().findViewById(R.id.export_sms_button);
|
||||
|
||||
message.setText(requireContext().getString(R.string.ConversationActivity__sms_messaging_is_currently_disabled_invite_s_to_to_signal_to_keep_the_conversation_here,
|
||||
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());
|
||||
|
@ -3403,7 +3403,7 @@ public class ConversationParentFragment extends Fragment
|
|||
}
|
||||
|
||||
@Override
|
||||
public void sendAnywayAfterSafetyNumberChangedInBottomSheet(@NonNull List<? extends ContactSearchKey.RecipientSearchKey> destinations) {
|
||||
public void sendAnywayAfterSafetyNumberChangedInBottomSheet(@NonNull List<ContactSearchKey.RecipientSearchKey> destinations) {
|
||||
Log.d(TAG, "onSendAnywayAfterSafetyNumberChange");
|
||||
initializeIdentityRecords().addListener(new AssertedSuccessListener<Boolean>() {
|
||||
@Override
|
||||
|
|
|
@ -220,7 +220,8 @@ final class MenuState {
|
|||
messageRecord.isChangeNumber() ||
|
||||
messageRecord.isBoostRequest() ||
|
||||
messageRecord.isPaymentsRequestToActivate() ||
|
||||
messageRecord.isPaymentsActivated();
|
||||
messageRecord.isPaymentsActivated() ||
|
||||
messageRecord.isSmsExportType();
|
||||
}
|
||||
|
||||
private final static class Builder {
|
||||
|
|
|
@ -40,9 +40,6 @@ class ChatColors(
|
|||
val chatBubbleMask: Drawable
|
||||
get() {
|
||||
return when {
|
||||
false -> {
|
||||
ColorDrawable(Color.TRANSPARENT)
|
||||
}
|
||||
linearGradient != null -> {
|
||||
RotatableGradientDrawable(
|
||||
linearGradient.degrees,
|
||||
|
@ -60,8 +57,7 @@ class ChatColors(
|
|||
* Returns the ColorFilter to apply to a conversation bubble or other relevant piece of UI.
|
||||
*/
|
||||
@IgnoredOnParcel
|
||||
val chatBubbleColorFilter: ColorFilter =
|
||||
PorterDuffColorFilter(Color.TRANSPARENT, PorterDuff.Mode.SRC_IN)
|
||||
val chatBubbleColorFilter: ColorFilter = PorterDuffColorFilter(Color.TRANSPARENT, PorterDuff.Mode.SRC_IN)
|
||||
|
||||
@ColorInt
|
||||
fun asSingleColor(): Int {
|
||||
|
@ -109,7 +105,6 @@ class ChatColors(
|
|||
}
|
||||
|
||||
fun asCircle(): Drawable {
|
||||
|
||||
val toWrap: Drawable = chatBubbleMask
|
||||
val path = Path()
|
||||
|
||||
|
|
|
@ -73,8 +73,8 @@ open class MultiselectForwardActivity : FragmentWrapperActivity(), MultiselectFo
|
|||
} else if (intent == null || !intent.hasExtra(RESULT_SELECTION)) {
|
||||
throw IllegalStateException("Selection contract requires a selection.")
|
||||
} else {
|
||||
val selection: List<ContactSearchKey.ParcelableRecipientSearchKey> = intent.getParcelableArrayListExtra(RESULT_SELECTION)!!
|
||||
selection.map { it.asRecipientSearchKey() }
|
||||
val selection: List<ContactSearchKey.RecipientSearchKey> = intent.getParcelableArrayListExtra(RESULT_SELECTION)!!
|
||||
selection
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -191,7 +191,7 @@ class MultiselectForwardFragment :
|
|||
|
||||
shareSelectionAdapter.submitList(contactSelection.mapIndexed { index, key -> ShareSelectionMappingModel(key.requireShareContact(), index == 0) })
|
||||
|
||||
addMessage.visible = !args.forceDisableAddMessage && contactSelection.any { key -> key !is ContactSearchKey.RecipientSearchKey.Story } && args.multiShareArgs.isNotEmpty()
|
||||
addMessage.visible = !args.forceDisableAddMessage && contactSelection.any { key -> !key.requireRecipientSearchKey().isStory } && args.multiShareArgs.isNotEmpty()
|
||||
|
||||
if (contactSelection.isNotEmpty() && !bottomBar.isVisible) {
|
||||
bottomBar.animation = AnimationUtils.loadAnimation(requireContext(), R.anim.slide_fade_from_bottom)
|
||||
|
@ -237,13 +237,13 @@ class MultiselectForwardFragment :
|
|||
|
||||
setFragmentResultListener(CreateStoryWithViewersFragment.REQUEST_KEY) { _, bundle ->
|
||||
val recipientId: RecipientId = bundle.getParcelable(CreateStoryWithViewersFragment.STORY_RECIPIENT)!!
|
||||
contactSearchMediator.setKeysSelected(setOf(ContactSearchKey.RecipientSearchKey.Story(recipientId)))
|
||||
contactSearchMediator.setKeysSelected(setOf(ContactSearchKey.RecipientSearchKey(recipientId, true)))
|
||||
contactFilterView.clear()
|
||||
}
|
||||
|
||||
setFragmentResultListener(ChooseGroupStoryBottomSheet.GROUP_STORY) { _, bundle ->
|
||||
val groups: Set<RecipientId> = bundle.getParcelableArrayList<RecipientId>(ChooseGroupStoryBottomSheet.RESULT_SET)?.toSet() ?: emptySet()
|
||||
val keys: Set<ContactSearchKey.RecipientSearchKey.Story> = groups.map { ContactSearchKey.RecipientSearchKey.Story(it) }.toSet()
|
||||
val keys: Set<ContactSearchKey.RecipientSearchKey> = groups.map { ContactSearchKey.RecipientSearchKey(it, true) }.toSet()
|
||||
contactSearchMediator.addToVisibleGroupStories(keys)
|
||||
contactSearchMediator.setKeysSelected(keys)
|
||||
contactFilterView.clear()
|
||||
|
@ -350,7 +350,7 @@ class MultiselectForwardFragment :
|
|||
dismissibleDialog?.dismiss()
|
||||
|
||||
val resultsBundle = Bundle().apply {
|
||||
putParcelableArrayList(RESULT_SELECTION, ArrayList(selectedContacts.map { it.requireParcelable() }))
|
||||
putParcelableArrayList(RESULT_SELECTION, ArrayList(selectedContacts.map { it.requireRecipientSearchKey() }))
|
||||
}
|
||||
|
||||
callback.setResult(resultsBundle)
|
||||
|
@ -489,7 +489,7 @@ class MultiselectForwardFragment :
|
|||
}
|
||||
|
||||
override fun onMyStoryConfigured(recipientId: RecipientId) {
|
||||
contactSearchMediator.setKeysSelected(setOf(ContactSearchKey.RecipientSearchKey.Story(recipientId)))
|
||||
contactSearchMediator.setKeysSelected(setOf(ContactSearchKey.RecipientSearchKey(recipientId, true)))
|
||||
contactSearchMediator.refresh()
|
||||
}
|
||||
|
||||
|
|
|
@ -62,14 +62,14 @@ class MultiselectForwardRepository {
|
|||
SignalExecutors.BOUNDED.execute {
|
||||
val filteredContacts: Set<ContactSearchKey> = shareContacts
|
||||
.asSequence()
|
||||
.filter { it is ContactSearchKey.RecipientSearchKey.Story || it is ContactSearchKey.RecipientSearchKey.KnownRecipient }
|
||||
.filter { it is ContactSearchKey.RecipientSearchKey }
|
||||
.toSet()
|
||||
|
||||
val mappedArgs: List<MultiShareArgs> = multiShareArgs.map { it.buildUpon(filteredContacts).build() }
|
||||
val results = mappedArgs.sortedBy { it.timestamp }.map { MultiShareSender.sendSync(it) }
|
||||
|
||||
if (additionalMessage.isNotEmpty()) {
|
||||
val additional = MultiShareArgs.Builder(filteredContacts.filterNot { it is ContactSearchKey.RecipientSearchKey.Story }.toSet())
|
||||
val additional = MultiShareArgs.Builder(filteredContacts.filterNot { it is ContactSearchKey.RecipientSearchKey && it.isStory }.toSet())
|
||||
.withDraftText(additionalMessage)
|
||||
.build()
|
||||
|
||||
|
|
|
@ -2,7 +2,6 @@ package org.thoughtcrime.securesms.conversation.ui.inlinequery
|
|||
|
||||
import android.content.Context
|
||||
import android.graphics.Rect
|
||||
import android.os.Build
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
|
@ -34,10 +33,8 @@ class InlineQueryResultsPopup(
|
|||
private val adapter: MappingAdapter
|
||||
|
||||
init {
|
||||
if (Build.VERSION.SDK_INT >= 21) {
|
||||
contentView.outlineProvider = ViewOutlineProvider.BACKGROUND
|
||||
contentView.clipToOutline = true
|
||||
}
|
||||
contentView.outlineProvider = ViewOutlineProvider.BACKGROUND
|
||||
contentView.clipToOutline = true
|
||||
|
||||
inputMethodMode = INPUT_METHOD_NOT_NEEDED
|
||||
|
||||
|
@ -46,9 +43,7 @@ class InlineQueryResultsPopup(
|
|||
callback = null
|
||||
}
|
||||
|
||||
if (Build.VERSION.SDK_INT >= 21) {
|
||||
elevation = 20f
|
||||
}
|
||||
elevation = 20f
|
||||
|
||||
adapter = InlineQueryAdapter { m -> callback?.onSelection(m) }
|
||||
list.adapter = adapter
|
||||
|
|
|
@ -107,7 +107,7 @@ public class ConversationListArchiveFragment extends ConversationListFragment im
|
|||
|
||||
@Override
|
||||
protected @DrawableRes int getArchiveIconRes() {
|
||||
return R.drawable.ic_unarchive_24;
|
||||
return R.drawable.symbol_archive_android_up_24;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -1258,9 +1258,9 @@ public class ConversationListFragment extends MainFragment implements ActionMode
|
|||
}));
|
||||
|
||||
if (conversation.getThreadRecord().isArchived()) {
|
||||
items.add(new ActionItem(R.drawable.ic_unarchive_24, getResources().getQuantityString(R.plurals.ConversationListFragment_unarchive_plural, 1), () -> handleArchive(id, false)));
|
||||
items.add(new ActionItem(R.drawable.symbol_archive_android_up_24, getResources().getQuantityString(R.plurals.ConversationListFragment_unarchive_plural, 1), () -> handleArchive(id, false)));
|
||||
} else {
|
||||
items.add(new ActionItem(R.drawable.ic_archive_24, getResources().getQuantityString(R.plurals.ConversationListFragment_archive_plural, 1), () -> handleArchive(id, false)));
|
||||
items.add(new ActionItem(R.drawable.symbol_archive_android_24, getResources().getQuantityString(R.plurals.ConversationListFragment_archive_plural, 1), () -> handleArchive(id, false)));
|
||||
}
|
||||
|
||||
items.add(new ActionItem(R.drawable.ic_delete_24, getResources().getQuantityString(R.plurals.ConversationListFragment_delete_plural, 1), () -> handleDelete(id)));
|
||||
|
@ -1301,11 +1301,9 @@ public class ConversationListFragment extends MainFragment implements ActionMode
|
|||
public void onDestroyActionMode(ActionMode mode) {
|
||||
viewModel.endSelection();
|
||||
|
||||
if (Build.VERSION.SDK_INT >= 21) {
|
||||
TypedArray color = getActivity().getTheme().obtainStyledAttributes(new int[] { android.R.attr.statusBarColor });
|
||||
WindowUtil.setStatusBarColor(getActivity().getWindow(), color.getColor(0, Color.BLACK));
|
||||
color.recycle();
|
||||
}
|
||||
TypedArray color = getActivity().getTheme().obtainStyledAttributes(new int[] { android.R.attr.statusBarColor });
|
||||
WindowUtil.setStatusBarColor(getActivity().getWindow(), color.getColor(0, Color.BLACK));
|
||||
color.recycle();
|
||||
|
||||
if (Build.VERSION.SDK_INT >= 23) {
|
||||
TypedArray lightStatusBarAttr = getActivity().getTheme().obtainStyledAttributes(new int[] { android.R.attr.windowLightStatusBar });
|
||||
|
@ -1363,9 +1361,9 @@ public class ConversationListFragment extends MainFragment implements ActionMode
|
|||
}
|
||||
|
||||
if (isArchived()) {
|
||||
items.add(new ActionItem(R.drawable.ic_unarchive_24, getResources().getQuantityString(R.plurals.ConversationListFragment_unarchive_plural, count), () -> handleArchive(selectionIds, true)));
|
||||
items.add(new ActionItem(R.drawable.symbol_archive_android_up_24, getResources().getQuantityString(R.plurals.ConversationListFragment_unarchive_plural, count), () -> handleArchive(selectionIds, true)));
|
||||
} else {
|
||||
items.add(new ActionItem(R.drawable.ic_archive_24, getResources().getQuantityString(R.plurals.ConversationListFragment_archive_plural, count), () -> handleArchive(selectionIds, true)));
|
||||
items.add(new ActionItem(R.drawable.symbol_archive_android_24, getResources().getQuantityString(R.plurals.ConversationListFragment_archive_plural, count), () -> handleArchive(selectionIds, true)));
|
||||
}
|
||||
|
||||
items.add(new ActionItem(R.drawable.ic_delete_24, getResources().getQuantityString(R.plurals.ConversationListFragment_delete_plural, count), () -> handleDelete(selectionIds)));
|
||||
|
@ -1394,7 +1392,7 @@ public class ConversationListFragment extends MainFragment implements ActionMode
|
|||
}
|
||||
|
||||
protected @DrawableRes int getArchiveIconRes() {
|
||||
return R.drawable.ic_archive_24;
|
||||
return R.drawable.symbol_archive_android_24;
|
||||
}
|
||||
|
||||
@WorkerThread
|
||||
|
|
|
@ -202,7 +202,7 @@ class ConversationListFilterPullView @JvmOverloads constructor(
|
|||
animateHelpText = 0
|
||||
helpTextStartFraction = ANIMATE_HELP_TEXT_START_FRACTION
|
||||
binding.helpText.animate().alpha(0f).setListener(object : AnimationCompleteListener() {
|
||||
override fun onAnimationEnd(animation: Animator?) {
|
||||
override fun onAnimationEnd(animation: Animator) {
|
||||
binding.helpText.visibility = INVISIBLE
|
||||
}
|
||||
})
|
||||
|
|
|
@ -335,7 +335,11 @@ public class MessageTable extends DatabaseTable implements MessageTypes, Recipie
|
|||
private static final String RAW_ID_WHERE = TABLE_NAME + "._id = ?";
|
||||
|
||||
private static final String SNIPPET_QUERY = "SELECT " + MessageTable.ID + ", " + MessageTable.TYPE + ", " + MessageTable.DATE_RECEIVED + " FROM " + MessageTable.TABLE_NAME + " " +
|
||||
"WHERE " + MessageTable.THREAD_ID + " = ? AND " + MessageTable.TYPE + " & " + MessageTypes.GROUP_V2_LEAVE_BITS + " != " + MessageTypes.GROUP_V2_LEAVE_BITS + " AND " + MessageTable.STORY_TYPE + " = 0 AND " + MessageTable.PARENT_STORY_ID + " <= 0 " +
|
||||
"WHERE " + MessageTable.THREAD_ID + " = ? AND " +
|
||||
MessageTable.TYPE + " & " + MessageTypes.GROUP_V2_LEAVE_BITS + " != " + MessageTypes.GROUP_V2_LEAVE_BITS + " AND " +
|
||||
MessageTable.STORY_TYPE + " = 0 AND " +
|
||||
MessageTable.PARENT_STORY_ID + " <= 0 AND " +
|
||||
MessageTable.TYPE + " NOT IN (" + MessageTypes.PROFILE_CHANGE_TYPE + ", " + MessageTypes.GV1_MIGRATION_TYPE + ", " + MessageTypes.CHANGE_NUMBER_TYPE + ", " + MessageTypes.BOOST_REQUEST_TYPE + ", " + MessageTypes.SMS_EXPORT_TYPE + ") " +
|
||||
"ORDER BY " + MessageTable.DATE_RECEIVED + " DESC " +
|
||||
"LIMIT 1";
|
||||
|
||||
|
@ -941,7 +945,7 @@ public class MessageTable extends DatabaseTable implements MessageTypes, Recipie
|
|||
}
|
||||
|
||||
if (!silent) {
|
||||
final boolean keepThreadArchived = SignalStore.settings().shouldKeepMutedChatsArchived() && recipient.isMuted();
|
||||
final boolean keepThreadArchived = SignalStore.settings().shouldKeepMutedChatsArchived() && (recipient.isMuted() || (groupRecipient != null && groupRecipient.isMuted()));
|
||||
SignalDatabase.threads().update(threadId, !keepThreadArchived);
|
||||
}
|
||||
|
||||
|
|
|
@ -3277,7 +3277,7 @@ open class RecipientTable(context: Context, databaseHelper: SignalDatabase) : Da
|
|||
}
|
||||
|
||||
fun markPreMessageRequestRecipientsAsProfileSharingEnabled(messageRequestEnableTime: Long) {
|
||||
val whereArgs = SqlUtil.buildArgs(messageRequestEnableTime, messageRequestEnableTime)
|
||||
val whereArgs = SqlUtil.buildArgs(messageRequestEnableTime)
|
||||
val select =
|
||||
"""
|
||||
SELECT r.$ID FROM $TABLE_NAME AS r
|
||||
|
|
|
@ -191,6 +191,24 @@ class StorySendTable(context: Context, databaseHelper: SignalDatabase) : Databas
|
|||
return messageIds
|
||||
}
|
||||
|
||||
fun getStoryMessageFor(recipientId: RecipientId, timestamp: Long): MessageId? {
|
||||
readableDatabase.query(
|
||||
TABLE_NAME,
|
||||
arrayOf(MESSAGE_ID),
|
||||
"$RECIPIENT_ID = ? AND $SENT_TIMESTAMP = ?",
|
||||
SqlUtil.buildArgs(recipientId, timestamp),
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
"1"
|
||||
).use { cursor ->
|
||||
if (cursor.moveToFirst()) {
|
||||
return MessageId(cursor.requireLong(MESSAGE_ID))
|
||||
}
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
override fun remapRecipient(oldId: RecipientId, newId: RecipientId) {
|
||||
val query = "$RECIPIENT_ID = ?"
|
||||
val args = SqlUtil.buildArgs(oldId)
|
||||
|
|
|
@ -1646,7 +1646,8 @@ class ThreadTable(context: Context, databaseHelper: SignalDatabase) : DatabaseTa
|
|||
private fun ConversationFilter.toQuery(): String {
|
||||
return when (this) {
|
||||
ConversationFilter.OFF -> ""
|
||||
ConversationFilter.UNREAD -> " AND $READ != ${ReadStatus.READ.serialize()}"
|
||||
//language=sql
|
||||
ConversationFilter.UNREAD -> " AND ($UNREAD_COUNT > 0 OR $READ == ${ReadStatus.FORCED_UNREAD.serialize()})"
|
||||
ConversationFilter.MUTED -> error("This filter selection isn't supported yet.")
|
||||
ConversationFilter.GROUPS -> error("This filter selection isn't supported yet.")
|
||||
}
|
||||
|
|
|
@ -14,8 +14,7 @@ object TypefaceHelper {
|
|||
weight.value,
|
||||
false
|
||||
)
|
||||
true -> Typeface.create("${family.familyName}-$weightName", Typeface.NORMAL)
|
||||
else -> Typeface.create(family.familyName, if (weight.value > Weight.MEDIUM.value) Typeface.BOLD else Typeface.NORMAL)
|
||||
else -> Typeface.create("${family.familyName}-$weightName", Typeface.NORMAL)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -128,7 +128,7 @@ public class AddGroupDetailsFragment extends LoggingFragment {
|
|||
viewModel.getIsMms().observe(getViewLifecycleOwner(), isMms -> {
|
||||
disappearingMessagesRow.setVisibility(isMms ? View.GONE : View.VISIBLE);
|
||||
mmsWarning.setVisibility(isMms ? View.VISIBLE : View.GONE);
|
||||
mmsWarningText.setText(R.string.AddGroupDetailsFragment__youve_selected_a_contact_that_doesnt_support);
|
||||
mmsWarningText.setText(R.string.AddGroupDetailsFragment__youve_selected_a_contact_that_doesnt_support_signal_groups_mms_removal);
|
||||
name.setHint(isMms ? R.string.AddGroupDetailsFragment__group_name_optional : R.string.AddGroupDetailsFragment__group_name_required);
|
||||
toolbar.setTitle(isMms ? R.string.AddGroupDetailsFragment__create_group : R.string.AddGroupDetailsFragment__name_this_group);
|
||||
});
|
||||
|
|
|
@ -155,7 +155,7 @@ class KeyboardPageSearchView @JvmOverloads constructor(
|
|||
.setDuration(REVEAL_DURATION)
|
||||
.alpha(0f)
|
||||
.setListener(object : AnimationCompleteListener() {
|
||||
override fun onAnimationEnd(animation: Animator?) {
|
||||
override fun onAnimationEnd(animation: Animator) {
|
||||
visibility = INVISIBLE
|
||||
}
|
||||
})
|
||||
|
|
|
@ -3,6 +3,7 @@ package org.thoughtcrime.securesms.keyvalue;
|
|||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import org.signal.core.util.StringStringSerializer;
|
||||
import org.thoughtcrime.securesms.lock.PinHashing;
|
||||
import org.thoughtcrime.securesms.util.JsonUtils;
|
||||
import org.whispersystems.signalservice.api.KbsPinData;
|
||||
|
@ -13,6 +14,8 @@ import java.io.IOException;
|
|||
import java.security.SecureRandom;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
public final class KbsValues extends SignalStoreValues {
|
||||
|
||||
|
@ -24,6 +27,7 @@ public final class KbsValues extends SignalStoreValues {
|
|||
private static final String LAST_CREATE_FAILED_TIMESTAMP = "kbs.last_create_failed_timestamp";
|
||||
public static final String OPTED_OUT = "kbs.opted_out";
|
||||
private static final String PIN_FORGOTTEN_OR_SKIPPED = "kbs.pin.forgotten.or.skipped";
|
||||
private static final String KBS_AUTH_TOKENS = "kbs.kbs_auth_tokens";
|
||||
|
||||
KbsValues(KeyValueStore store) {
|
||||
super(store);
|
||||
|
@ -165,6 +169,30 @@ public final class KbsValues extends SignalStoreValues {
|
|||
putBoolean(PIN_FORGOTTEN_OR_SKIPPED, value);
|
||||
}
|
||||
|
||||
public synchronized void putAuthTokenList(List<String> tokens) {
|
||||
putList(KBS_AUTH_TOKENS, tokens, StringStringSerializer.INSTANCE);
|
||||
}
|
||||
|
||||
public synchronized List<String> getKbsAuthTokenList() {
|
||||
return getList(KBS_AUTH_TOKENS, StringStringSerializer.INSTANCE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Keeps the 10 most recent KBS auth tokens.
|
||||
* @param token
|
||||
* @return whether the token was added (new) or ignored (already existed)
|
||||
*/
|
||||
public synchronized boolean appendAuthTokenToList(String token) {
|
||||
List<String> tokens = getKbsAuthTokenList();
|
||||
if (tokens.contains(token)) {
|
||||
return false;
|
||||
} else {
|
||||
final List<String> result = Stream.concat(Stream.of(token), tokens.stream()).limit(10).collect(Collectors.toList());
|
||||
putAuthTokenList(result);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
/** Should only be called by {@link org.thoughtcrime.securesms.pin.PinState}. */
|
||||
public synchronized void optOut() {
|
||||
getStore().beginWrite()
|
||||
|
|
|
@ -4,7 +4,6 @@ import android.animation.Animator
|
|||
import android.animation.Animator.AnimatorListener
|
||||
import android.annotation.SuppressLint
|
||||
import android.content.Context
|
||||
import android.os.Build
|
||||
import android.util.AttributeSet
|
||||
import android.view.MotionEvent
|
||||
import android.view.animation.PathInterpolator
|
||||
|
@ -103,14 +102,14 @@ class LottieAnimatedButton @JvmOverloads constructor(
|
|||
MotionEvent.ACTION_UP -> {
|
||||
if (isAnimating) {
|
||||
addAnimatorListener(object : AnimatorListener {
|
||||
override fun onAnimationEnd(animation: Animator?) {
|
||||
override fun onAnimationEnd(animation: Animator) {
|
||||
removeAllAnimatorListeners()
|
||||
playAnimationReverse()
|
||||
}
|
||||
|
||||
override fun onAnimationStart(animation: Animator?) {}
|
||||
override fun onAnimationCancel(animation: Animator?) {}
|
||||
override fun onAnimationRepeat(animation: Animator?) {}
|
||||
override fun onAnimationStart(animation: Animator) {}
|
||||
override fun onAnimationCancel(animation: Animator) {}
|
||||
override fun onAnimationRepeat(animation: Animator) {}
|
||||
})
|
||||
} else {
|
||||
playAnimationReverse()
|
||||
|
@ -162,9 +161,7 @@ class AnimatedInOutImageButton @JvmOverloads constructor(
|
|||
.scaleX(1f)
|
||||
.scaleY(1f)
|
||||
|
||||
if (Build.VERSION.SDK_INT >= 21) {
|
||||
animator.interpolator = PathInterpolator(0.4f, 0.0f, 0.2f, 1f)
|
||||
}
|
||||
animator.interpolator = PathInterpolator(0.4f, 0.0f, 0.2f, 1f)
|
||||
|
||||
animator.start()
|
||||
}
|
||||
|
@ -178,9 +175,7 @@ class AnimatedInOutImageButton @JvmOverloads constructor(
|
|||
.scaleY(0.5f)
|
||||
.withEndAction(endAction)
|
||||
|
||||
if (Build.VERSION.SDK_INT >= 21) {
|
||||
animator.interpolator = PathInterpolator(0.4f, 0.0f, 0.2f, 1f)
|
||||
}
|
||||
animator.interpolator = PathInterpolator(0.4f, 0.0f, 0.2f, 1f)
|
||||
animator.start()
|
||||
}
|
||||
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
package org.thoughtcrime.securesms.mediapreview
|
||||
|
||||
import android.content.Context
|
||||
import android.os.Build
|
||||
import android.os.Bundle
|
||||
import androidx.appcompat.app.AppCompatDelegate
|
||||
import androidx.core.content.ContextCompat
|
||||
|
@ -26,11 +25,10 @@ class MediaPreviewV2Activity : PassphraseRequiredActivity(), VoiceNoteMediaContr
|
|||
setContentView(R.layout.activity_mediapreview_v2)
|
||||
voiceNoteMediaController = VoiceNoteMediaController(this)
|
||||
|
||||
if (Build.VERSION.SDK_INT >= 21) {
|
||||
val systemBarColor = ContextCompat.getColor(this, R.color.signal_dark_colorSurface)
|
||||
window.statusBarColor = systemBarColor
|
||||
window.navigationBarColor = systemBarColor
|
||||
}
|
||||
val systemBarColor = ContextCompat.getColor(this, R.color.signal_dark_colorSurface)
|
||||
window.statusBarColor = systemBarColor
|
||||
window.navigationBarColor = systemBarColor
|
||||
|
||||
if (savedInstanceState == null) {
|
||||
val bundle = Bundle()
|
||||
val args = MediaIntentFactory.requireArguments(intent.extras!!)
|
||||
|
|
|
@ -8,7 +8,6 @@ import android.content.DialogInterface
|
|||
import android.content.Intent
|
||||
import android.graphics.PorterDuff
|
||||
import android.graphics.PorterDuffColorFilter
|
||||
import android.os.Build
|
||||
import android.os.Bundle
|
||||
import android.text.SpannableStringBuilder
|
||||
import android.view.LayoutInflater
|
||||
|
@ -153,7 +152,6 @@ class MediaPreviewV2Fragment : LoggingFragment(R.layout.fragment_media_preview_v
|
|||
super.onPageSelected(position)
|
||||
if (position != viewModel.currentPosition) {
|
||||
debouncer.clear()
|
||||
fullscreenHelper.showSystemUI()
|
||||
}
|
||||
viewModel.setCurrentPage(position)
|
||||
}
|
||||
|
@ -244,7 +242,6 @@ class MediaPreviewV2Fragment : LoggingFragment(R.layout.fragment_media_preview_v
|
|||
}
|
||||
bindAlbumRail(albumThumbnailMedia, currentItem)
|
||||
|
||||
fullscreenHelper.showSystemUI()
|
||||
crossfadeViewIn(binding.mediaPreviewDetailsContainer)
|
||||
}
|
||||
|
||||
|
@ -351,9 +348,7 @@ class MediaPreviewV2Fragment : LoggingFragment(R.layout.fragment_media_preview_v
|
|||
scrollAlbumRailToCurrentAdapterPosition()
|
||||
}
|
||||
}
|
||||
if (Build.VERSION.SDK_INT >= 21) {
|
||||
viewPropertyAnimator.interpolator = PathInterpolator(0.17f, 0.17f, 0f, 1f)
|
||||
}
|
||||
viewPropertyAnimator.interpolator = PathInterpolator(0.17f, 0.17f, 0f, 1f)
|
||||
viewPropertyAnimator.start()
|
||||
true
|
||||
} else {
|
||||
|
@ -452,7 +447,6 @@ class MediaPreviewV2Fragment : LoggingFragment(R.layout.fragment_media_preview_v
|
|||
|
||||
if (pagerAdapter.getFragmentTag(viewModel.currentPosition) == tag) {
|
||||
debouncer.clear()
|
||||
fullscreenHelper.showSystemUI()
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -45,6 +45,7 @@ import org.signal.core.util.logging.Log;
|
|||
import org.thoughtcrime.securesms.LoggingFragment;
|
||||
import org.thoughtcrime.securesms.R;
|
||||
import org.thoughtcrime.securesms.animation.AnimationCompleteListener;
|
||||
import org.thoughtcrime.securesms.mediasend.camerax.CameraXModelBlocklist;
|
||||
import org.thoughtcrime.securesms.mediasend.v2.MediaAnimations;
|
||||
import org.thoughtcrime.securesms.mediasend.v2.MediaCountIndicatorButton;
|
||||
import org.thoughtcrime.securesms.mms.DecryptableStreamUriLoader.DecryptableUri;
|
||||
|
@ -59,7 +60,7 @@ import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers;
|
|||
import io.reactivex.rxjava3.disposables.Disposable;
|
||||
|
||||
/**
|
||||
* Camera capture implemented with the legacy camera API's. Should only be used if sdk < 21.
|
||||
* Camera capture implemented with the legacy camera API's. Should only be used if a device is on the {@link CameraXModelBlocklist}.
|
||||
*/
|
||||
public class Camera1Fragment extends LoggingFragment implements CameraFragment,
|
||||
TextureView.SurfaceTextureListener,
|
||||
|
|
|
@ -73,7 +73,6 @@ import io.reactivex.rxjava3.disposables.Disposable;
|
|||
* preferred whenever possible.
|
||||
*/
|
||||
@ExperimentalVideo
|
||||
@RequiresApi(21)
|
||||
public class CameraXFragment extends LoggingFragment implements CameraFragment {
|
||||
|
||||
private static final String TAG = Log.tag(CameraXFragment.class);
|
||||
|
|
|
@ -5,12 +5,10 @@ import android.view.Window;
|
|||
import android.view.WindowManager;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.RequiresApi;
|
||||
import androidx.camera.core.CameraSelector;
|
||||
import androidx.camera.core.ImageCapture;
|
||||
import androidx.camera.view.CameraController;
|
||||
|
||||
@RequiresApi(21)
|
||||
final class CameraXSelfieFlashHelper {
|
||||
|
||||
private static final float MAX_SCREEN_BRIGHTNESS = 1f;
|
||||
|
|
|
@ -15,7 +15,6 @@ import org.thoughtcrime.securesms.R;
|
|||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
@RequiresApi(21)
|
||||
public final class CameraXFlashToggleView extends AppCompatImageView {
|
||||
|
||||
private static final String STATE_FLASH_INDEX = "flash.toggle.state.flash.index";
|
||||
|
|
|
@ -2,7 +2,6 @@ package org.thoughtcrime.securesms.mediasend.camerax
|
|||
|
||||
import android.content.Context
|
||||
import android.os.Build
|
||||
import androidx.annotation.RequiresApi
|
||||
import androidx.camera.view.CameraController
|
||||
import androidx.camera.view.video.ExperimentalVideo
|
||||
import org.signal.core.util.asListContains
|
||||
|
@ -13,7 +12,6 @@ import org.thoughtcrime.securesms.video.VideoUtil
|
|||
/**
|
||||
* Describes device capabilities
|
||||
*/
|
||||
@RequiresApi(21)
|
||||
@ExperimentalVideo
|
||||
sealed class CameraXModePolicy {
|
||||
|
||||
|
|
|
@ -41,7 +41,6 @@ public class CameraXUtil {
|
|||
|
||||
private static final String TAG = Log.tag(CameraXUtil.class);
|
||||
|
||||
@RequiresApi(21)
|
||||
private static final int[] CAMERA_HARDWARE_LEVEL_ORDERING = new int[]{CameraMetadata.INFO_SUPPORTED_HARDWARE_LEVEL_LEGACY,
|
||||
CameraMetadata.INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED,
|
||||
CameraMetadata.INFO_SUPPORTED_HARDWARE_LEVEL_FULL};
|
||||
|
@ -60,7 +59,6 @@ public class CameraXUtil {
|
|||
CameraMetadata.INFO_SUPPORTED_HARDWARE_LEVEL_3};
|
||||
|
||||
@SuppressWarnings("SuspiciousNameCombination")
|
||||
@RequiresApi(21)
|
||||
public static ImageResult toJpeg(@NonNull ImageProxy image, boolean flip) throws IOException {
|
||||
ImageProxy.PlaneProxy[] planes = image.getPlanes();
|
||||
ByteBuffer buffer = planes[0].getBuffer();
|
||||
|
@ -106,10 +104,9 @@ public class CameraXUtil {
|
|||
}
|
||||
|
||||
public static boolean isSupported() {
|
||||
return Build.VERSION.SDK_INT >= 21 && !CameraXModelBlocklist.isBlocklisted();
|
||||
return !CameraXModelBlocklist.isBlocklisted();
|
||||
}
|
||||
|
||||
@RequiresApi(21)
|
||||
public static int toCameraDirectionInt(CameraSelector cameraSelector) {
|
||||
if (cameraSelector == CameraSelector.DEFAULT_FRONT_CAMERA) {
|
||||
return Camera.CameraInfo.CAMERA_FACING_FRONT;
|
||||
|
@ -118,7 +115,6 @@ public class CameraXUtil {
|
|||
}
|
||||
}
|
||||
|
||||
@RequiresApi(21)
|
||||
public static CameraSelector toCameraSelector(@CameraSelector.LensFacing int cameraDirectionInt) {
|
||||
if (cameraDirectionInt == Camera.CameraInfo.CAMERA_FACING_FRONT) {
|
||||
return CameraSelector.DEFAULT_FRONT_CAMERA;
|
||||
|
@ -127,7 +123,6 @@ public class CameraXUtil {
|
|||
}
|
||||
}
|
||||
|
||||
@RequiresApi(21)
|
||||
public static @ImageCapture.CaptureMode int getOptimalCaptureMode() {
|
||||
return FastCameraModels.contains(Build.MODEL) ? ImageCapture.CAPTURE_MODE_MAXIMIZE_QUALITY
|
||||
: ImageCapture.CAPTURE_MODE_MINIMIZE_LATENCY;
|
||||
|
@ -138,7 +133,6 @@ public class CameraXUtil {
|
|||
return Math.max(maxDisplay, 1920);
|
||||
}
|
||||
|
||||
@TargetApi(21)
|
||||
public static @NonNull Size buildResolutionForRatio(int longDimension, @NonNull Rational ratio, boolean isPortrait) {
|
||||
int shortDimension = longDimension * ratio.getDenominator() / ratio.getNumerator();
|
||||
|
||||
|
@ -187,7 +181,6 @@ public class CameraXUtil {
|
|||
return transformedData;
|
||||
}
|
||||
|
||||
@RequiresApi(21)
|
||||
private static boolean shouldCropImage(@NonNull ImageProxy image) {
|
||||
Size sourceSize = new Size(image.getWidth(), image.getHeight());
|
||||
Size targetSize = new Size(image.getCropRect().width(), image.getCropRect().height());
|
||||
|
@ -205,12 +198,10 @@ public class CameraXUtil {
|
|||
return out.toByteArray();
|
||||
}
|
||||
|
||||
@RequiresApi(21)
|
||||
public static boolean isMixedModeSupported(@NonNull Context context) {
|
||||
return getLowestSupportedHardwareLevel(context) != CameraMetadata.INFO_SUPPORTED_HARDWARE_LEVEL_LEGACY;
|
||||
}
|
||||
|
||||
@RequiresApi(21)
|
||||
public static int getLowestSupportedHardwareLevel(@NonNull Context context) {
|
||||
@SuppressLint("RestrictedApi") CameraManager cameraManager = CameraManagerCompat.from(context).unwrap();
|
||||
|
||||
|
@ -241,13 +232,11 @@ public class CameraXUtil {
|
|||
}
|
||||
}
|
||||
|
||||
@RequiresApi(21)
|
||||
private static int maxHardwareLevel() {
|
||||
if (Build.VERSION.SDK_INT >= 24) return CameraMetadata.INFO_SUPPORTED_HARDWARE_LEVEL_3;
|
||||
else return CameraMetadata.INFO_SUPPORTED_HARDWARE_LEVEL_FULL;
|
||||
}
|
||||
|
||||
@RequiresApi(21)
|
||||
private static int smallerHardwareLevel(int levelA, int levelB) {
|
||||
|
||||
int[] hardwareInfoOrdering = getHardwareInfoOrdering();
|
||||
|
@ -258,7 +247,6 @@ public class CameraXUtil {
|
|||
return CameraMetadata.INFO_SUPPORTED_HARDWARE_LEVEL_LEGACY;
|
||||
}
|
||||
|
||||
@RequiresApi(21)
|
||||
private static int[] getHardwareInfoOrdering() {
|
||||
if (Build.VERSION.SDK_INT >= 28) return CAMERA_HARDWARE_LEVEL_ORDERING_28;
|
||||
else if (Build.VERSION.SDK_INT >= 24) return CAMERA_HARDWARE_LEVEL_ORDERING_24;
|
||||
|
|
|
@ -29,7 +29,7 @@ sealed class MediaSelectionDestination {
|
|||
}
|
||||
|
||||
class SingleRecipient(private val id: RecipientId) : MediaSelectionDestination() {
|
||||
override fun getRecipientSearchKey(): ContactSearchKey.RecipientSearchKey = ContactSearchKey.RecipientSearchKey.KnownRecipient(id)
|
||||
override fun getRecipientSearchKey(): ContactSearchKey.RecipientSearchKey = ContactSearchKey.RecipientSearchKey(id, false)
|
||||
|
||||
override fun toBundle(): Bundle {
|
||||
return Bundle().apply {
|
||||
|
@ -39,7 +39,7 @@ sealed class MediaSelectionDestination {
|
|||
}
|
||||
|
||||
class SingleStory(private val id: RecipientId) : MediaSelectionDestination() {
|
||||
override fun getRecipientSearchKey(): ContactSearchKey.RecipientSearchKey = ContactSearchKey.RecipientSearchKey.Story(id)
|
||||
override fun getRecipientSearchKey(): ContactSearchKey.RecipientSearchKey = ContactSearchKey.RecipientSearchKey(id, true)
|
||||
|
||||
override fun toBundle(): Bundle {
|
||||
return Bundle().apply {
|
||||
|
@ -51,8 +51,8 @@ sealed class MediaSelectionDestination {
|
|||
class MultipleRecipients(val recipientSearchKeys: List<ContactSearchKey.RecipientSearchKey>) : MediaSelectionDestination() {
|
||||
|
||||
companion object {
|
||||
fun fromParcel(parcelables: List<ContactSearchKey.ParcelableRecipientSearchKey>): MultipleRecipients {
|
||||
return MultipleRecipients(parcelables.map { it.asRecipientSearchKey() }.filterIsInstance(ContactSearchKey.RecipientSearchKey::class.java))
|
||||
fun fromParcel(parcelables: List<ContactSearchKey.RecipientSearchKey>): MultipleRecipients {
|
||||
return MultipleRecipients(parcelables)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -68,7 +68,7 @@ sealed class MediaSelectionDestination {
|
|||
|
||||
override fun toBundle(): Bundle {
|
||||
return Bundle().apply {
|
||||
putParcelableArrayList(RECIPIENT_LIST, ArrayList(recipientSearchKeys.map { it.requireParcelable() }))
|
||||
putParcelableArrayList(RECIPIENT_LIST, ArrayList(recipientSearchKeys.map { it.requireRecipientSearchKey() }))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -37,7 +37,14 @@ class MediaSelectionGalleryFragment : Fragment(R.layout.fragment_container), Med
|
|||
)
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
val isFirst = arguments?.getBoolean("suppressEmptyError") ?: arguments?.getBoolean("first") ?: false
|
||||
val args = arguments
|
||||
val isFirst = when {
|
||||
args == null -> false
|
||||
args.containsKey("suppressEmptyError") -> args.getBoolean("suppressEmptyError")
|
||||
args.containsKey("first") -> args.getBoolean("first")
|
||||
else -> false
|
||||
}
|
||||
|
||||
sharedViewModel.setSuppressEmptyError(isFirst)
|
||||
mediaGalleryFragment = ensureMediaGalleryFragment()
|
||||
|
||||
|
|
|
@ -86,7 +86,7 @@ class ChooseGroupStoryBottomSheet : FixedRoundedCornerBottomSheetDialogFragment(
|
|||
|
||||
mediator.getSelectionState().observe(viewLifecycleOwner) { state ->
|
||||
adapter.submitList(
|
||||
state.filterIsInstance(ContactSearchKey.RecipientSearchKey.KnownRecipient::class.java)
|
||||
state.filterIsInstance(ContactSearchKey.RecipientSearchKey::class.java)
|
||||
.map { it.recipientId }
|
||||
.mapIndexed { index, recipientId ->
|
||||
ShareSelectionMappingModel(
|
||||
|
@ -146,7 +146,7 @@ class ChooseGroupStoryBottomSheet : FixedRoundedCornerBottomSheetDialogFragment(
|
|||
RESULT_SET,
|
||||
ArrayList(
|
||||
mediator.getSelectedContacts()
|
||||
.filterIsInstance(ContactSearchKey.RecipientSearchKey.KnownRecipient::class.java)
|
||||
.filterIsInstance(ContactSearchKey.RecipientSearchKey::class.java)
|
||||
.map { it.recipientId }
|
||||
)
|
||||
)
|
||||
|
|
|
@ -37,7 +37,7 @@ class ChooseStoryTypeBottomSheet : DSLSettingsBottomSheetFragment(
|
|||
stringId = R.string.ChooseStoryTypeBottomSheet__visible_only_to
|
||||
),
|
||||
icon = DSLSettingsIcon.from(
|
||||
iconId = R.drawable.ic_stories_24,
|
||||
iconId = R.drawable.symbol_stories_24,
|
||||
iconTintId = R.color.signal_colorOnSurface,
|
||||
backgroundId = R.drawable.circle_tintable,
|
||||
backgroundTint = R.color.signal_colorSurface5,
|
||||
|
|
|
@ -251,7 +251,7 @@ class TextStoryPostCreationFragment : Fragment(R.layout.stories_text_post_creati
|
|||
return if (linkPreviewState.linkPreview.isPresent) {
|
||||
linkPreviewState.linkPreview.get()
|
||||
} else if (!linkPreviewState.activeUrlForError.isNullOrEmpty()) {
|
||||
LinkPreview(linkPreviewState.activeUrlForError!!, "", "", 0L, Optional.empty())
|
||||
LinkPreview(linkPreviewState.activeUrlForError!!, linkPreviewState.activeUrlForError!!, "", 0L, Optional.empty())
|
||||
} else {
|
||||
null
|
||||
}
|
||||
|
|
|
@ -64,7 +64,7 @@ class TextStoryPostSendRepository {
|
|||
|
||||
for (contact in contactSearchKey) {
|
||||
val recipient = Recipient.resolved(contact.requireShareContact().recipientId.get())
|
||||
val isStory = contact is ContactSearchKey.RecipientSearchKey.Story || recipient.isDistributionList
|
||||
val isStory = contact.requireRecipientSearchKey().isStory || recipient.isDistributionList
|
||||
|
||||
if (isStory && !recipient.isMyStory) {
|
||||
SignalStore.storyValues().setLatestStorySend(StorySend.newSend(recipient))
|
||||
|
|
|
@ -35,6 +35,7 @@ import org.thoughtcrime.securesms.crypto.SecurityEvent;
|
|||
import org.thoughtcrime.securesms.database.AttachmentTable;
|
||||
import org.thoughtcrime.securesms.database.CallTable;
|
||||
import org.thoughtcrime.securesms.database.GroupTable;
|
||||
import org.thoughtcrime.securesms.database.StorySendTable;
|
||||
import org.thoughtcrime.securesms.database.model.GroupRecord;
|
||||
import org.thoughtcrime.securesms.database.GroupReceiptTable;
|
||||
import org.thoughtcrime.securesms.database.GroupReceiptTable.GroupReceiptInfo;
|
||||
|
@ -1750,12 +1751,20 @@ public final class MessageContentProcessor {
|
|||
|
||||
try {
|
||||
RecipientId storyAuthorRecipient = RecipientId.from(storyContext.getAuthorServiceId());
|
||||
RecipientId selfId = Recipient.self().getId();
|
||||
ParentStoryId parentStoryId;
|
||||
QuoteModel quoteModel = null;
|
||||
long expiresInMillis = 0L;
|
||||
MessageId storyMessageId = null;
|
||||
|
||||
try {
|
||||
MessageId storyMessageId = database.getStoryId(storyAuthorRecipient, storyContext.getSentTimestamp());
|
||||
if (selfId.equals(storyAuthorRecipient)) {
|
||||
storyMessageId = SignalDatabase.storySends().getStoryMessageFor(senderRecipient.getId(), storyContext.getSentTimestamp());
|
||||
}
|
||||
if (storyMessageId == null) {
|
||||
storyMessageId = database.getStoryId(storyAuthorRecipient, storyContext.getSentTimestamp());
|
||||
}
|
||||
|
||||
MmsMessageRecord story = (MmsMessageRecord) database.getMessageRecord(storyMessageId.getId());
|
||||
Recipient threadRecipient = Objects.requireNonNull(SignalDatabase.threads().getRecipientForThreadId(story.getThreadId()));
|
||||
boolean groupStory = threadRecipient.isActiveGroup();
|
||||
|
@ -1942,10 +1951,17 @@ public final class MessageContentProcessor {
|
|||
}
|
||||
|
||||
private long handleSynchronizeSentExpirationUpdate(@NonNull SentTranscriptMessage message)
|
||||
throws MmsException, BadGroupIdException
|
||||
throws MmsException
|
||||
{
|
||||
log(message.getTimestamp(), "Synchronize sent expiration update.");
|
||||
|
||||
Optional<GroupId> groupId = getSyncMessageDestination(message).getGroupId();
|
||||
|
||||
if (groupId.isPresent() && groupId.get().isV2()) {
|
||||
warn(String.valueOf(message.getTimestamp()), "Expiration update received for GV2. Ignoring.");
|
||||
return -1;
|
||||
}
|
||||
|
||||
MessageTable database = SignalDatabase.messages();
|
||||
Recipient recipient = getSyncMessageDestination(message);
|
||||
|
||||
|
@ -1974,9 +1990,9 @@ public final class MessageContentProcessor {
|
|||
try {
|
||||
Optional<SignalServiceDataMessage.Reaction> reaction = message.getDataMessage().get().getReaction();
|
||||
ParentStoryId parentStoryId;
|
||||
SignalServiceDataMessage.StoryContext storyContext = message.getDataMessage().get().getStoryContext().get();
|
||||
MessageTable database = SignalDatabase.messages();
|
||||
Recipient recipient = getSyncMessageDestination(message);
|
||||
SignalServiceDataMessage.StoryContext storyContext = message.getDataMessage().get().getStoryContext().get();
|
||||
MessageTable database = SignalDatabase.messages();
|
||||
Recipient recipient = getSyncMessageDestination(message);
|
||||
QuoteModel quoteModel = null;
|
||||
long expiresInMillis = 0L;
|
||||
RecipientId storyAuthorRecipient = RecipientId.from(storyContext.getAuthorServiceId());
|
||||
|
@ -2206,8 +2222,8 @@ public final class MessageContentProcessor {
|
|||
{
|
||||
log(envelopeTimestamp, "Synchronize sent media message for " + message.getTimestamp());
|
||||
|
||||
MessageTable database = SignalDatabase.messages();
|
||||
Recipient recipients = getSyncMessageDestination(message);
|
||||
MessageTable database = SignalDatabase.messages();
|
||||
Recipient recipients = getSyncMessageDestination(message);
|
||||
Optional<QuoteModel> quote = getValidatedQuote(message.getDataMessage().get().getQuote());
|
||||
Optional<Attachment> sticker = getStickerAttachment(message.getDataMessage().get().getSticker());
|
||||
Optional<List<Contact>> sharedContacts = getContacts(message.getDataMessage().get().getSharedContacts());
|
||||
|
@ -3019,7 +3035,7 @@ public final class MessageContentProcessor {
|
|||
LinkPreview linkPreview = new LinkPreview(url.get(), title.orElse(""), description.orElse(""), preview.getDate(), thumbnail);
|
||||
linkPreviews.add(linkPreview);
|
||||
} else {
|
||||
warn(String.format("Discarding an invalid link preview. hasTitle: %b presentInBody: %b validDomain: %b", hasTitle, presentInBody, validDomain));
|
||||
warn(String.format("Discarding an invalid link preview. hasTitle: %b presentInBody: %b isStoryEmbed: %b validDomain: %b", hasTitle, presentInBody, isStoryEmbed, validDomain));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
package org.thoughtcrime.securesms.pin;
|
||||
|
||||
import android.app.backup.BackupManager;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
|
@ -8,6 +10,7 @@ import org.signal.core.util.logging.Log;
|
|||
import org.signal.libsignal.protocol.InvalidKeyException;
|
||||
import org.thoughtcrime.securesms.KbsEnclave;
|
||||
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies;
|
||||
import org.thoughtcrime.securesms.keyvalue.SignalStore;
|
||||
import org.thoughtcrime.securesms.lock.PinHashing;
|
||||
import org.whispersystems.signalservice.api.KbsPinData;
|
||||
import org.whispersystems.signalservice.api.KeyBackupService;
|
||||
|
@ -69,6 +72,7 @@ public class KbsRepository {
|
|||
|
||||
try {
|
||||
authorization = authorization == null ? kbs.getAuthorization() : authorization;
|
||||
backupAuthToken(authorization);
|
||||
token = kbs.getToken(authorization);
|
||||
} catch (NonSuccessfulResponseCodeException e) {
|
||||
if (e.getCode() == 404) {
|
||||
|
@ -95,6 +99,13 @@ public class KbsRepository {
|
|||
return Objects.requireNonNull(firstKnownTokenData);
|
||||
}
|
||||
|
||||
private static void backupAuthToken(String token) {
|
||||
final boolean tokenIsNew = SignalStore.kbsValues().appendAuthTokenToList(token);
|
||||
if (tokenIsNew) {
|
||||
new BackupManager(ApplicationDependencies.getApplication()).dataChanged();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Invoked during registration to restore the master key based on the server response during
|
||||
* verification.
|
||||
|
|
|
@ -29,6 +29,7 @@ import org.whispersystems.signalservice.internal.contacts.crypto.Unauthenticated
|
|||
|
||||
import java.io.IOException;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.Locale;
|
||||
import java.util.Optional;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
|
|
@ -309,7 +309,7 @@ public class EditProfileFragment extends LoggingFragment {
|
|||
private void handleUpload() {
|
||||
viewModel.getUploadResult().observe(getViewLifecycleOwner(), uploadResult -> {
|
||||
if (uploadResult == EditProfileRepository.UploadResult.SUCCESS) {
|
||||
if (Build.VERSION.SDK_INT >= 21 && !viewModel.isGroup()) {
|
||||
if (!viewModel.isGroup()) {
|
||||
handleFinishedLollipop();
|
||||
}
|
||||
else {
|
||||
|
@ -330,7 +330,6 @@ public class EditProfileFragment extends LoggingFragment {
|
|||
controller.onProfileNameUploadCompleted();
|
||||
}
|
||||
|
||||
@RequiresApi(api = 21)
|
||||
private void handleFinishedLollipop() {
|
||||
int[] finishButtonLocation = new int[2];
|
||||
int[] revealLocation = new int[2];
|
||||
|
|
|
@ -143,7 +143,7 @@ public class EditAboutFragment extends Fragment implements ManageProfileActivity
|
|||
this.emojiView.setImageDrawable(drawable);
|
||||
this.selectedEmoji = emoji;
|
||||
} else {
|
||||
this.emojiView.setImageResource(R.drawable.ic_add_emoji);
|
||||
this.emojiView.setImageResource(R.drawable.symbol_emoji_plus_24);
|
||||
this.selectedEmoji = "";
|
||||
}
|
||||
}
|
||||
|
|
|
@ -230,14 +230,14 @@ public class ManageProfileFragment extends LoggingFragment {
|
|||
|
||||
private void presentAboutEmoji(@NonNull String aboutEmoji) {
|
||||
if (aboutEmoji == null || aboutEmoji.isEmpty()) {
|
||||
binding.manageProfileAboutIcon.setImageDrawable(ResourcesCompat.getDrawable(getResources(), R.drawable.ic_compose_24, null));
|
||||
binding.manageProfileAboutIcon.setImageDrawable(ResourcesCompat.getDrawable(getResources(), R.drawable.symbol_edit_24, null));
|
||||
} else {
|
||||
Drawable emoji = EmojiUtil.convertToDrawable(requireContext(), aboutEmoji);
|
||||
|
||||
if (emoji != null) {
|
||||
binding.manageProfileAboutIcon.setImageDrawable(emoji);
|
||||
} else {
|
||||
binding.manageProfileAboutIcon.setImageDrawable(ResourcesCompat.getDrawable(getResources(), R.drawable.ic_compose_24, null));
|
||||
binding.manageProfileAboutIcon.setImageDrawable(ResourcesCompat.getDrawable(getResources(), R.drawable.symbol_edit_24, null));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1367,7 +1367,7 @@ public class Recipient {
|
|||
}
|
||||
|
||||
public @NonNull FallbackContactPhoto getPhotoForDistributionList() {
|
||||
return new ResourceContactPhoto(R.drawable.ic_stories_24, R.drawable.ic_stories_24, R.drawable.ic_stories_24);
|
||||
return new ResourceContactPhoto(R.drawable.symbol_stories_24, R.drawable.symbol_stories_24, R.drawable.symbol_stories_24);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -5,6 +5,7 @@ import android.app.Application;
|
|||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.annotation.WorkerThread;
|
||||
import androidx.core.app.NotificationManagerCompat;
|
||||
|
||||
import org.signal.core.util.logging.Log;
|
||||
import org.signal.libsignal.protocol.state.PreKeyRecord;
|
||||
|
@ -25,6 +26,7 @@ import org.thoughtcrime.securesms.jobmanager.JobManager;
|
|||
import org.thoughtcrime.securesms.jobs.DirectoryRefreshJob;
|
||||
import org.thoughtcrime.securesms.jobs.RotateCertificateJob;
|
||||
import org.thoughtcrime.securesms.keyvalue.SignalStore;
|
||||
import org.thoughtcrime.securesms.notifications.NotificationIds;
|
||||
import org.thoughtcrime.securesms.pin.PinState;
|
||||
import org.thoughtcrime.securesms.push.AccountManagerFactory;
|
||||
import org.thoughtcrime.securesms.recipients.Recipient;
|
||||
|
@ -168,7 +170,7 @@ public final class RegistrationRepository {
|
|||
SignalStore.account().setServicePassword(registrationData.getPassword());
|
||||
SignalStore.account().setRegistered(true);
|
||||
TextSecurePreferences.setPromptedPushRegistration(context, true);
|
||||
TextSecurePreferences.setUnauthorizedReceived(context, false);
|
||||
NotificationManagerCompat.from(context).cancel(NotificationIds.UNREGISTERED_NOTIFICATION_ID);
|
||||
|
||||
PinState.onRegistration(context, kbsData, pin, hasPin);
|
||||
}
|
||||
|
|
|
@ -99,7 +99,7 @@ object SafetyNumberBottomSheet {
|
|||
fun forIdentityRecordsAndDestinations(identityRecords: List<IdentityRecord>, destinations: List<ContactSearchKey>): Factory {
|
||||
val args = SafetyNumberBottomSheetArgs(
|
||||
identityRecords.map { it.recipientId },
|
||||
destinations.filterIsInstance<ContactSearchKey.RecipientSearchKey>().map { it.requireParcelable() }
|
||||
destinations.filterIsInstance<ContactSearchKey.RecipientSearchKey>().map { it.requireRecipientSearchKey() }
|
||||
)
|
||||
|
||||
return SheetFactory(args)
|
||||
|
@ -115,7 +115,7 @@ object SafetyNumberBottomSheet {
|
|||
fun forIdentityRecordsAndDestination(identityRecords: List<IdentityRecord>, destination: ContactSearchKey): Factory {
|
||||
val args = SafetyNumberBottomSheetArgs(
|
||||
identityRecords.map { it.recipientId },
|
||||
listOf(destination).filterIsInstance<ContactSearchKey.RecipientSearchKey>().map { it.requireParcelable() }
|
||||
listOf(destination).filterIsInstance<ContactSearchKey.RecipientSearchKey>().map { it.requireRecipientSearchKey() }
|
||||
)
|
||||
|
||||
return SheetFactory(args)
|
||||
|
@ -131,14 +131,14 @@ object SafetyNumberBottomSheet {
|
|||
return args!!
|
||||
}
|
||||
|
||||
private fun getDestinationFromRecord(messageRecord: MessageRecord): List<ContactSearchKey.ParcelableRecipientSearchKey> {
|
||||
private fun getDestinationFromRecord(messageRecord: MessageRecord): List<ContactSearchKey.RecipientSearchKey> {
|
||||
val key = if ((messageRecord as? MmsMessageRecord)?.storyType?.isStory == true) {
|
||||
ContactSearchKey.RecipientSearchKey.Story(messageRecord.recipient.id)
|
||||
ContactSearchKey.RecipientSearchKey(messageRecord.recipient.id, true)
|
||||
} else {
|
||||
ContactSearchKey.RecipientSearchKey.KnownRecipient(messageRecord.recipient.id)
|
||||
ContactSearchKey.RecipientSearchKey(messageRecord.recipient.id, false)
|
||||
}
|
||||
|
||||
return listOf(key.requireParcelable())
|
||||
return listOf(key)
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -12,6 +12,6 @@ import org.thoughtcrime.securesms.recipients.RecipientId
|
|||
@Parcelize
|
||||
data class SafetyNumberBottomSheetArgs(
|
||||
val untrustedRecipients: List<RecipientId>,
|
||||
val destinations: List<ContactSearchKey.ParcelableRecipientSearchKey>,
|
||||
val destinations: List<ContactSearchKey.RecipientSearchKey>,
|
||||
val messageId: MessageId? = null
|
||||
) : Parcelable
|
||||
|
|
|
@ -25,7 +25,7 @@ class SafetyNumberBottomSheetViewModel(
|
|||
private const val MAX_RECIPIENTS_TO_DISPLAY = 5
|
||||
}
|
||||
|
||||
private val destinationStore = RxStore(args.destinations.map { it.asRecipientSearchKey() })
|
||||
private val destinationStore = RxStore(args.destinations)
|
||||
val destinationSnapshot: List<ContactSearchKey.RecipientSearchKey>
|
||||
get() = destinationStore.state
|
||||
|
||||
|
|
|
@ -183,11 +183,11 @@ class RotationDialView @JvmOverloads constructor(
|
|||
}
|
||||
|
||||
private inner class GestureListener : GestureDetector.SimpleOnGestureListener() {
|
||||
override fun onDown(e: MotionEvent?): Boolean {
|
||||
override fun onDown(e: MotionEvent): Boolean {
|
||||
return true
|
||||
}
|
||||
|
||||
override fun onScroll(e1: MotionEvent?, e2: MotionEvent?, distanceX: Float, distanceY: Float): Boolean {
|
||||
override fun onScroll(e1: MotionEvent, e2: MotionEvent, distanceX: Float, distanceY: Float): Boolean {
|
||||
val degreeIncrement: Float = distanceX / dimensions.spaceBetweenAngleIndicators
|
||||
val prevDialDegrees = getDialDegrees(degrees)
|
||||
val newDialDegrees = getDialDegrees(degrees + degreeIncrement)
|
||||
|
|
|
@ -24,6 +24,7 @@ import org.thoughtcrime.securesms.util.Util;
|
|||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
|
@ -61,11 +62,9 @@ public final class MultiShareArgs implements Parcelable {
|
|||
}
|
||||
|
||||
protected MultiShareArgs(Parcel in) {
|
||||
List<ContactSearchKey.ParcelableRecipientSearchKey> parcelableRecipientSearchKeys = in.createTypedArrayList(ContactSearchKey.ParcelableRecipientSearchKey.CREATOR);
|
||||
List<ContactSearchKey.RecipientSearchKey> parcelableRecipientSearchKeys = in.createTypedArrayList(ContactSearchKey.RecipientSearchKey.CREATOR);
|
||||
|
||||
contactSearchKeys = parcelableRecipientSearchKeys.stream()
|
||||
.map(ContactSearchKey.ParcelableRecipientSearchKey::asRecipientSearchKey)
|
||||
.collect(Collectors.toSet());
|
||||
contactSearchKeys = new HashSet<>(parcelableRecipientSearchKeys);
|
||||
media = in.createTypedArrayList(Media.CREATOR);
|
||||
draftText = in.readString();
|
||||
stickerLocator = in.readParcelable(StickerLocator.class.getClassLoader());
|
||||
|
@ -197,7 +196,8 @@ public final class MultiShareArgs implements Parcelable {
|
|||
}
|
||||
|
||||
public boolean allRecipientsAreStories() {
|
||||
return !contactSearchKeys.isEmpty() && contactSearchKeys.stream().allMatch(key -> key instanceof ContactSearchKey.RecipientSearchKey.Story);
|
||||
return !contactSearchKeys.isEmpty() && contactSearchKeys.stream()
|
||||
.allMatch(key -> key.requireRecipientSearchKey().isStory());
|
||||
}
|
||||
|
||||
public static final Creator<MultiShareArgs> CREATOR = new Creator<MultiShareArgs>() {
|
||||
|
@ -219,7 +219,7 @@ public final class MultiShareArgs implements Parcelable {
|
|||
|
||||
@Override
|
||||
public void writeToParcel(Parcel dest, int flags) {
|
||||
dest.writeTypedList(Stream.of(contactSearchKeys).map(ContactSearchKey::requireParcelable).toList());
|
||||
dest.writeTypedList(Stream.of(contactSearchKeys).map(ContactSearchKey::requireRecipientSearchKey).toList());
|
||||
dest.writeTypedList(media);
|
||||
dest.writeString(draftText);
|
||||
dest.writeParcelable(stickerLocator, flags);
|
||||
|
@ -267,7 +267,7 @@ public final class MultiShareArgs implements Parcelable {
|
|||
(!media.isEmpty() ||
|
||||
!TextUtils.isEmpty(draftText) ||
|
||||
MediaUtil.isImageOrVideoType(dataType) ||
|
||||
(!contactSearchKeys.isEmpty() && contactSearchKeys.stream().anyMatch(key -> key instanceof ContactSearchKey.RecipientSearchKey.Story)));
|
||||
(!contactSearchKeys.isEmpty() && contactSearchKeys.stream().anyMatch(key -> key.requireRecipientSearchKey().isStory())));
|
||||
}
|
||||
|
||||
public static final class Builder {
|
||||
|
|
|
@ -103,7 +103,7 @@ class ShareActivity : PassphraseRequiredActivity(), MultiselectForwardFragment.C
|
|||
openConversation(
|
||||
ShareEvent.OpenConversation(
|
||||
shareState.loadState.resolvedShareData,
|
||||
ContactSearchKey.RecipientSearchKey.KnownRecipient(directShareTarget)
|
||||
ContactSearchKey.RecipientSearchKey(directShareTarget, false)
|
||||
)
|
||||
)
|
||||
} else {
|
||||
|
@ -134,8 +134,7 @@ class ShareActivity : PassphraseRequiredActivity(), MultiselectForwardFragment.C
|
|||
throw AssertionError("Expected a recipient selection!")
|
||||
}
|
||||
|
||||
val parcelizedKeys: List<ContactSearchKey.ParcelableRecipientSearchKey> = bundle.getParcelableArrayList(MultiselectForwardFragment.RESULT_SELECTION)!!
|
||||
val contactSearchKeys = parcelizedKeys.map { it.asRecipientSearchKey() }
|
||||
val contactSearchKeys: List<ContactSearchKey.RecipientSearchKey> = bundle.getParcelableArrayList(MultiselectForwardFragment.RESULT_SELECTION)!!
|
||||
|
||||
viewModel.onContactSelectionConfirmed(contactSearchKeys)
|
||||
}
|
||||
|
|
|
@ -10,7 +10,6 @@ import android.transition.TransitionValues
|
|||
import android.util.AttributeSet
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.annotation.RequiresApi
|
||||
import androidx.core.view.ViewCompat
|
||||
|
||||
class ScaleTransition : Transition {
|
||||
|
@ -36,7 +35,6 @@ class ScaleTransition : Transition {
|
|||
|
||||
constructor() : super()
|
||||
|
||||
@RequiresApi(21)
|
||||
constructor(context: Context, attrs: AttributeSet) : super(context, attrs)
|
||||
|
||||
override fun getTransitionProperties(): Array<String> {
|
||||
|
@ -108,7 +106,7 @@ class ScaleTransition : Transition {
|
|||
ofFloat(View.SCALE_Y, 1f)
|
||||
).apply {
|
||||
addListener(object : AnimatorListenerAdapter() {
|
||||
override fun onAnimationEnd(animation: Animator?) {
|
||||
override fun onAnimationEnd(animation: Animator) {
|
||||
resetValues(start.view)
|
||||
resetValues(end.view)
|
||||
}
|
||||
|
|
|
@ -125,7 +125,7 @@ class StoryFirstTimeNavigationView @JvmOverloads constructor(
|
|||
|
||||
isPlayingAnimations = true
|
||||
tapToAdvance.addAnimatorListener(object : AnimatorListenerAdapter() {
|
||||
override fun onAnimationEnd(animation: Animator?) {
|
||||
override fun onAnimationEnd(animation: Animator) {
|
||||
if (isPlayingAnimations) {
|
||||
swipeUp.playAnimation()
|
||||
}
|
||||
|
@ -133,7 +133,7 @@ class StoryFirstTimeNavigationView @JvmOverloads constructor(
|
|||
})
|
||||
|
||||
swipeUp.addAnimatorListener(object : AnimatorListenerAdapter() {
|
||||
override fun onAnimationEnd(animation: Animator?) {
|
||||
override fun onAnimationEnd(animation: Animator) {
|
||||
if (isPlayingAnimations) {
|
||||
swipeRight.playAnimation()
|
||||
}
|
||||
|
@ -141,7 +141,7 @@ class StoryFirstTimeNavigationView @JvmOverloads constructor(
|
|||
})
|
||||
|
||||
swipeRight.addAnimatorListener(object : AnimatorListenerAdapter() {
|
||||
override fun onAnimationEnd(animation: Animator?) {
|
||||
override fun onAnimationEnd(animation: Animator) {
|
||||
postDelayed({
|
||||
if (isPlayingAnimations) {
|
||||
startLottieAnimations()
|
||||
|
|
|
@ -227,7 +227,7 @@ object StoryContextMenu {
|
|||
}
|
||||
)
|
||||
add(
|
||||
ActionItem(R.drawable.ic_download_24_tinted, context.getString(R.string.save)) {
|
||||
ActionItem(R.drawable.ic_save_24_tinted, context.getString(R.string.save)) {
|
||||
callbacks.onSave()
|
||||
}
|
||||
)
|
||||
|
|
|
@ -142,11 +142,11 @@ class StoriesLandingFragment : DSLSettingsFragment(layoutId = R.layout.stories_l
|
|||
setEnterSharedElementCallback(object : SharedElementCallback() {
|
||||
override fun onSharedElementStart(sharedElementNames: MutableList<String>?, sharedElements: MutableList<View>?, sharedElementSnapshots: MutableList<View>?) {
|
||||
if (sharedElementNames?.contains("camera_fab") == true) {
|
||||
cameraFab.setImageResource(R.drawable.ic_compose_outline_24)
|
||||
cameraFab.setImageResource(R.drawable.symbol_edit_24)
|
||||
lifecycleDisposable += Single.timer(200, TimeUnit.MILLISECONDS)
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribeBy {
|
||||
cameraFab.setImageResource(R.drawable.ic_camera_outline_24)
|
||||
cameraFab.setImageResource(R.drawable.symbol_camera_24)
|
||||
sharedElementTarget.alpha = 0f
|
||||
}
|
||||
}
|
||||
|
@ -157,7 +157,7 @@ class StoriesLandingFragment : DSLSettingsFragment(layoutId = R.layout.stories_l
|
|||
Permissions.with(this)
|
||||
.request(Manifest.permission.CAMERA)
|
||||
.ifNecessary()
|
||||
.withRationaleDialog(getString(R.string.ConversationActivity_to_capture_photos_and_video_allow_signal_access_to_the_camera), R.drawable.ic_camera_24)
|
||||
.withRationaleDialog(getString(R.string.ConversationActivity_to_capture_photos_and_video_allow_signal_access_to_the_camera), R.drawable.symbol_camera_24)
|
||||
.withPermanentDenialDialog(getString(R.string.ConversationActivity_signal_needs_the_camera_permission_to_take_photos_or_video))
|
||||
.onAllGranted {
|
||||
startActivityIfAble(MediaSelectionActivity.camera(requireContext(), isStory = true))
|
||||
|
|
|
@ -128,7 +128,7 @@ class AddToGroupStoryDelegate(
|
|||
private fun sendNonPreUploadedMedia(result: MediaSendActivityResult) {
|
||||
Log.d(TAG, "Sending non-preupload media.")
|
||||
|
||||
val multiShareArgs = MultiShareArgs.Builder(setOf(ContactSearchKey.RecipientSearchKey.Story(result.recipientId)))
|
||||
val multiShareArgs = MultiShareArgs.Builder(setOf(ContactSearchKey.RecipientSearchKey(result.recipientId, true)))
|
||||
.withMedia(result.nonUploadedMedia.toList())
|
||||
.withDraftText(result.body)
|
||||
.withMentions(result.mentions.toList())
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
package org.thoughtcrime.securesms.stories.viewer
|
||||
|
||||
import android.os.Build
|
||||
import androidx.lifecycle.LiveData
|
||||
import androidx.lifecycle.MutableLiveData
|
||||
import androidx.lifecycle.ViewModel
|
||||
|
@ -31,7 +30,7 @@ class StoryViewerViewModel(
|
|||
storyViewerArgs.storyThumbUri != null -> StoryViewerState.CrossfadeSource.ImageUri(storyViewerArgs.storyThumbUri, storyViewerArgs.storyThumbBlur)
|
||||
else -> StoryViewerState.CrossfadeSource.None
|
||||
},
|
||||
skipCrossfade = storyViewerArgs.isFromNotification || storyViewerArgs.isFromQuote || Build.VERSION.SDK_INT < 21
|
||||
skipCrossfade = storyViewerArgs.isFromNotification || storyViewerArgs.isFromQuote
|
||||
)
|
||||
)
|
||||
|
||||
|
|
|
@ -3,7 +3,6 @@ package org.thoughtcrime.securesms.stories.viewer.first
|
|||
import android.app.Dialog
|
||||
import android.graphics.Color
|
||||
import android.graphics.drawable.ColorDrawable
|
||||
import android.os.Build
|
||||
import android.os.Bundle
|
||||
import android.view.View
|
||||
import android.view.WindowManager
|
||||
|
@ -36,14 +35,12 @@ class StoryFirstTimeNavigationFragment : DialogFragment(R.layout.story_viewer_fi
|
|||
val dialog = super.onCreateDialog(savedInstanceState)
|
||||
dialog.window!!.setBackgroundDrawable(ColorDrawable(Color.TRANSPARENT))
|
||||
|
||||
if (Build.VERSION.SDK_INT >= 21) {
|
||||
dialog.window!!.addFlags(
|
||||
WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION or
|
||||
WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS or
|
||||
WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS or
|
||||
WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS
|
||||
)
|
||||
}
|
||||
dialog.window!!.addFlags(
|
||||
WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION or
|
||||
WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS or
|
||||
WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS or
|
||||
WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS
|
||||
)
|
||||
return dialog
|
||||
}
|
||||
|
||||
|
|
|
@ -1184,7 +1184,7 @@ class StoryViewerPageFragment :
|
|||
scaleFactor = 1f
|
||||
isPerformingEndAnimation = true
|
||||
card.animate().scaleX(1f).scaleY(1f).setListener(object : AnimationCompleteListener() {
|
||||
override fun onAnimationEnd(animation: Animator?) {
|
||||
override fun onAnimationEnd(animation: Animator) {
|
||||
isPerformingEndAnimation = false
|
||||
viewModel.setIsUserScaling(false)
|
||||
sharedViewModel.setIsChildScrolling(false)
|
||||
|
@ -1255,7 +1255,7 @@ class StoryViewerPageFragment :
|
|||
return true
|
||||
}
|
||||
|
||||
override fun onFling(e1: MotionEvent?, e2: MotionEvent?, velocityX: Float, velocityY: Float): Boolean {
|
||||
override fun onFling(e1: MotionEvent, e2: MotionEvent, velocityX: Float, velocityY: Float): Boolean {
|
||||
val isSideSwipe = abs(velocityX) > abs(velocityY)
|
||||
if (!isSideSwipe) {
|
||||
return false
|
||||
|
|
|
@ -387,7 +387,7 @@ class StoryGroupReplyFragment :
|
|||
resendReaction = emoji
|
||||
|
||||
SafetyNumberBottomSheet
|
||||
.forIdentityRecordsAndDestination(error.untrustedRecords, ContactSearchKey.RecipientSearchKey.Story(groupRecipientId))
|
||||
.forIdentityRecordsAndDestination(error.untrustedRecords, ContactSearchKey.RecipientSearchKey(groupRecipientId, true))
|
||||
.show(childFragmentManager)
|
||||
} else {
|
||||
Log.w(TAG, "Failed to send reply", error)
|
||||
|
@ -536,7 +536,7 @@ class StoryGroupReplyFragment :
|
|||
resendMentions = mentions
|
||||
|
||||
SafetyNumberBottomSheet
|
||||
.forIdentityRecordsAndDestination(throwable.untrustedRecords, ContactSearchKey.RecipientSearchKey.Story(groupRecipientId))
|
||||
.forIdentityRecordsAndDestination(throwable.untrustedRecords, ContactSearchKey.RecipientSearchKey(groupRecipientId, true))
|
||||
.show(childFragmentManager)
|
||||
} else {
|
||||
Log.w(TAG, "Failed to send reply", throwable)
|
||||
|
|
|
@ -36,7 +36,7 @@ object StoryGroupReplySender {
|
|||
}
|
||||
|
||||
return messageAndRecipient.flatMapCompletable { (message, recipient) ->
|
||||
UntrustedRecords.checkForBadIdentityRecords(setOf(ContactSearchKey.RecipientSearchKey.KnownRecipient(recipient.id)), System.currentTimeMillis() - IdentityRecordList.DEFAULT_UNTRUSTED_WINDOW)
|
||||
UntrustedRecords.checkForBadIdentityRecords(setOf(ContactSearchKey.RecipientSearchKey(recipient.id, false)), System.currentTimeMillis() - IdentityRecordList.DEFAULT_UNTRUSTED_WINDOW)
|
||||
.andThen(
|
||||
Completable.create {
|
||||
MessageSender.send(
|
||||
|
|
|
@ -89,13 +89,11 @@ public final class FeatureFlags {
|
|||
private static final String PHONE_NUMBER_PRIVACY = "android.pnp";
|
||||
private static final String USE_FCM_FOREGROUND_SERVICE = "android.useFcmForegroundService.3";
|
||||
private static final String STORIES_AUTO_DOWNLOAD_MAXIMUM = "android.stories.autoDownloadMaximum";
|
||||
private static final String GIFT_BADGE_SEND_SUPPORT = "android.giftBadges.sending.3";
|
||||
private static final String TELECOM_MANUFACTURER_ALLOWLIST = "android.calling.telecomAllowList";
|
||||
private static final String TELECOM_MODEL_BLOCKLIST = "android.calling.telecomModelBlockList";
|
||||
private static final String CAMERAX_MODEL_BLOCKLIST = "android.cameraXModelBlockList";
|
||||
private static final String CAMERAX_MIXED_MODEL_BLOCKLIST = "android.cameraXMixedModelBlockList";
|
||||
private static final String RECIPIENT_MERGE_V2 = "android.recipientMergeV2";
|
||||
private static final String SMS_EXPORTER = "android.sms.exporter.2";
|
||||
private static final String HIDE_CONTACTS = "android.hide.contacts";
|
||||
public static final String CREDIT_CARD_PAYMENTS = "android.credit.card.payments.3";
|
||||
private static final String PAYMENTS_REQUEST_ACTIVATE_FLOW = "android.payments.requestActivateFlow";
|
||||
|
@ -105,7 +103,7 @@ public final class FeatureFlags {
|
|||
private static final String CDS_HARD_LIMIT = "android.cds.hardLimit";
|
||||
private static final String CHAT_FILTERS = "android.chat.filters.3";
|
||||
private static final String PAYPAL_ONE_TIME_DONATIONS = "android.oneTimePayPalDonations.2";
|
||||
private static final String PAYPAL_RECURRING_DONATIONS = "android.recurringPayPalDonations";
|
||||
private static final String PAYPAL_RECURRING_DONATIONS = "android.recurringPayPalDonations.2";
|
||||
|
||||
/**
|
||||
* We will only store remote values for flags in this set. If you want a flag to be controllable
|
||||
|
@ -145,13 +143,11 @@ public final class FeatureFlags {
|
|||
PAYMENTS_COUNTRY_BLOCKLIST,
|
||||
USE_FCM_FOREGROUND_SERVICE,
|
||||
STORIES_AUTO_DOWNLOAD_MAXIMUM,
|
||||
GIFT_BADGE_SEND_SUPPORT,
|
||||
TELECOM_MANUFACTURER_ALLOWLIST,
|
||||
TELECOM_MODEL_BLOCKLIST,
|
||||
CAMERAX_MODEL_BLOCKLIST,
|
||||
CAMERAX_MIXED_MODEL_BLOCKLIST,
|
||||
RECIPIENT_MERGE_V2,
|
||||
SMS_EXPORTER,
|
||||
HIDE_CONTACTS,
|
||||
CREDIT_CARD_PAYMENTS,
|
||||
PAYMENTS_REQUEST_ACTIVATE_FLOW,
|
||||
|
|
|
@ -558,8 +558,6 @@ public class TextSecurePreferences {
|
|||
|
||||
if (value) {
|
||||
clearLocalCredentials(context);
|
||||
} else {
|
||||
NotificationManagerCompat.from(context).cancel(NotificationIds.UNREGISTERED_NOTIFICATION_ID);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -7,7 +7,6 @@ import android.util.DisplayMetrics;
|
|||
import android.util.Size;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.RequiresApi;
|
||||
|
||||
import org.thoughtcrime.securesms.mms.MediaConstraints;
|
||||
import org.thoughtcrime.securesms.util.MediaUtil;
|
||||
|
@ -28,7 +27,6 @@ public final class VideoUtil {
|
|||
|
||||
private static final int TOTAL_BYTES_PER_SECOND = (VIDEO_BIT_RATE / 8) + (AUDIO_BIT_RATE / 8);
|
||||
|
||||
@RequiresApi(21)
|
||||
public static final String VIDEO_MIME_TYPE = MediaFormat.MIMETYPE_VIDEO_AVC;
|
||||
public static final String AUDIO_MIME_TYPE = "audio/mp4a-latm";
|
||||
|
||||
|
@ -36,7 +34,6 @@ public final class VideoUtil {
|
|||
|
||||
private VideoUtil() { }
|
||||
|
||||
@RequiresApi(21)
|
||||
public static Size getVideoRecordingSize() {
|
||||
return isPortrait(screenSize())
|
||||
? new Size(VIDEO_SHORT_WIDTH, VIDEO_LONG_WIDTH)
|
||||
|
@ -54,13 +51,11 @@ public final class VideoUtil {
|
|||
return VIDEO_MAX_UPLOAD_LENGTH_S;
|
||||
}
|
||||
|
||||
@RequiresApi(21)
|
||||
private static Size screenSize() {
|
||||
DisplayMetrics metrics = Resources.getSystem().getDisplayMetrics();
|
||||
return new Size(metrics.widthPixels, metrics.heightPixels);
|
||||
}
|
||||
|
||||
@RequiresApi(21)
|
||||
private static boolean isPortrait(Size size) {
|
||||
return size.getWidth() < size.getHeight();
|
||||
}
|
||||
|
|
|
@ -235,7 +235,6 @@ public abstract class AudioManagerCompat {
|
|||
}
|
||||
}
|
||||
|
||||
@RequiresApi(21)
|
||||
private static class Api21AudioManagerCompat extends Api19AudioManagerCompat {
|
||||
|
||||
private static AudioAttributes AUDIO_ATTRIBUTES = new AudioAttributes.Builder()
|
||||
|
|
|
@ -158,7 +158,7 @@ class FullSignalAudioManager(context: Context, eventListener: EventListener?) :
|
|||
updateAudioDeviceState()
|
||||
|
||||
wiredHeadsetReceiver = WiredHeadsetReceiver()
|
||||
context.registerReceiver(wiredHeadsetReceiver, IntentFilter(if (Build.VERSION.SDK_INT >= 21) AudioManager.ACTION_HEADSET_PLUG else Intent.ACTION_HEADSET_PLUG))
|
||||
context.registerReceiver(wiredHeadsetReceiver, IntentFilter(AudioManager.ACTION_HEADSET_PLUG))
|
||||
|
||||
state = State.PREINITIALIZED
|
||||
|
||||
|
|
|
@ -0,0 +1,16 @@
|
|||
/**
|
||||
* Copyright (C) 2023 Open Whisper Systems
|
||||
*
|
||||
* Licensed according to the LICENSE file in this repository.
|
||||
*/
|
||||
|
||||
syntax = "proto3";
|
||||
|
||||
package signal;
|
||||
|
||||
option java_package = "org.thoughtcrime.securesms.absbackup";
|
||||
option java_outer_classname = "ExternalBackupProtos";
|
||||
|
||||
message KbsAuthToken {
|
||||
repeated string token = 1;
|
||||
}
|
Binary file not shown.
Before Width: | Height: | Size: 228 B |
Binary file not shown.
Before Width: | Height: | Size: 170 B |
|
@ -1,9 +0,0 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24">
|
||||
<path
|
||||
android:fillColor="@color/core_ultramarine_light"
|
||||
android:pathData="M17,15a2.98,2.98 0,0 0,-2.184 0.955L8.9,12.728a2.726,2.726 0,0 0,0 -1.456l5.915,-3.227A3.1,3.1 0,1 0,14.1 6.728L8.184,9.955a3,3 0,1 0,0 4.09L14.1,17.272A2.995,2.995 0,1 0,17 15Z"/>
|
||||
</vector>
|
|
@ -0,0 +1,24 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="56dp"
|
||||
android:height="57dp"
|
||||
android:viewportWidth="56"
|
||||
android:viewportHeight="57">
|
||||
<path
|
||||
android:pathData="M17,3C11.477,3 7,7.477 7,13v20c0,5.185 3.947,9.449 9,9.95v8.222c0,1.781 2.154,2.674 3.414,1.414L29,43h8c5.523,0 10,-4.477 10,-10V13c0,-5.523 -4.477,-10 -10,-10H17Z"
|
||||
android:fillColor="#1B1C1F"
|
||||
android:fillType="evenOdd"/>
|
||||
<path
|
||||
android:pathData="M4,13C4,5.82 9.82,0 17,0h20c7.18,0 13,5.82 13,13v20c0,7.18 -5.82,13 -13,13h-6.757l-8.707,8.707c-3.15,3.15 -8.536,0.92 -8.536,-3.535v-5.8C7.777,43.685 4,38.785 4,33L4,13ZM16,42.95c-5.053,-0.501 -9,-4.765 -9,-9.95L7,13C7,7.477 11.477,3 17,3h20c5.523,0 10,4.477 10,10v20c0,5.523 -4.477,10 -10,10h-8l-9.586,9.586c-1.26,1.26 -3.414,0.367 -3.414,-1.414L16,42.95Z"
|
||||
android:fillColor="#5C5E65"
|
||||
android:fillType="evenOdd"/>
|
||||
<path
|
||||
android:pathData="M44,42m-12,0a12,12 0,1 1,24 0a12,12 0,1 1,-24 0"
|
||||
android:fillColor="#4CAF50"/>
|
||||
<path
|
||||
android:pathData="M49.15,36.866a0.75,0.75 0,0 1,0.234 1.035l-6,9.5a0.75,0.75 0,0 1,-1.226 0.06l-3.5,-4.5a0.75,0.75 0,1 1,1.184 -0.922l2.844,3.657 5.43,-8.596a0.75,0.75 0,0 1,1.035 -0.234Z"
|
||||
android:fillColor="#D2EBD3"/>
|
||||
<path
|
||||
android:pathData="M27,11.75A5.25,5.25 0,0 0,21.75 17v3.469a3.5,3.5 0,0 0,-1.369 1.442C20,22.66 20,23.64 20,25.6v1.8c0,1.96 0,2.94 0.381,3.689a3.5,3.5 0,0 0,1.53 1.53C22.66,33 23.64,33 25.6,33h2.8c1.96,0 2.94,0 3.689,-0.382a3.5,3.5 0,0 0,1.53 -1.529C34,30.34 34,29.36 34,27.4v-1.8c0,-1.96 0,-2.94 -0.382,-3.689a3.5,3.5 0,0 0,-1.368 -1.442L32.25,17c0,-2.9 -2.35,-5.25 -5.25,-5.25ZM29.75,20.007L29.75,17a2.75,2.75 0,1 0,-5.5 0v3.007C24.642,20 25.087,20 25.6,20h2.8c0.513,0 0.958,0 1.35,0.007ZM28,26.687a1.75,1.75 0,1 0,-2 0L26,28.5a1,1 0,1 0,2 0v-1.814Z"
|
||||
android:fillColor="#6191F3"
|
||||
android:fillType="evenOdd"/>
|
||||
</vector>
|
|
@ -0,0 +1,23 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="56dp"
|
||||
android:height="56dp"
|
||||
android:viewportWidth="56"
|
||||
android:viewportHeight="56">
|
||||
<path
|
||||
android:pathData="M8,7a4,4 0,0 1,4 -4h16a4,4 0,0 1,4 4v34a4,4 0,0 1,-4 4H12a4,4 0,0 1,-4 -4V7Z"
|
||||
android:fillColor="#1B1C1F"/>
|
||||
<path
|
||||
android:pathData="M12,0h16a7,7 0,0 1,7 7v34a7,7 0,0 1,-7 7L12,48a7,7 0,0 1,-7 -7L5,7a7,7 0,0 1,7 -7ZM12,3a4,4 0,0 0,-4 4v34a4,4 0,0 0,4 4h16a4,4 0,0 0,4 -4L32,7a4,4 0,0 0,-4 -4L12,3Z"
|
||||
android:fillColor="#5C5E65"
|
||||
android:fillType="evenOdd"/>
|
||||
<path
|
||||
android:pathData="M24,15a4,4 0,0 1,4 -4h16a4,4 0,0 1,4 4v34a4,4 0,0 1,-4 4H28a4,4 0,0 1,-4 -4V15Z"
|
||||
android:fillColor="#1B1C1F"/>
|
||||
<path
|
||||
android:pathData="M28,8h16a7,7 0,0 1,7 7v34a7,7 0,0 1,-7 7L28,56a7,7 0,0 1,-7 -7L21,15a7,7 0,0 1,7 -7ZM28,11a4,4 0,0 0,-4 4v34a4,4 0,0 0,4 4h16a4,4 0,0 0,4 -4L48,15a4,4 0,0 0,-4 -4L28,11Z"
|
||||
android:fillColor="#5C5E65"
|
||||
android:fillType="evenOdd"/>
|
||||
<path
|
||||
android:pathData="m31.086,31 l-3.293,-3.293a1,1 0,0 1,1.414 -1.414l5,5a1,1 0,0 1,0 1.414l-5,5a1,1 0,0 1,-1.414 -1.414L31.086,33H21.5a8,8 0,0 1,-8 -8v-3a1,1 0,1 1,2 0v3a6,6 0,0 0,6 6h9.586Z"
|
||||
android:fillColor="#6191F3"/>
|
||||
</vector>
|
|
@ -0,0 +1,24 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="56dp"
|
||||
android:height="57dp"
|
||||
android:viewportWidth="56"
|
||||
android:viewportHeight="57">
|
||||
<path
|
||||
android:pathData="M17,3C11.477,3 7,7.477 7,13v20c0,5.185 3.947,9.449 9,9.95v8.222c0,1.781 2.154,2.674 3.414,1.414L29,43h8c5.523,0 10,-4.477 10,-10V13c0,-5.523 -4.477,-10 -10,-10H17Z"
|
||||
android:fillColor="#1B1C1F"
|
||||
android:fillType="evenOdd"/>
|
||||
<path
|
||||
android:pathData="M4,13C4,5.82 9.82,0 17,0h20c7.18,0 13,5.82 13,13v20c0,7.18 -5.82,13 -13,13h-6.757l-8.707,8.707c-3.15,3.15 -8.536,0.92 -8.536,-3.535v-5.8C7.777,43.685 4,38.785 4,33L4,13ZM16,42.95c-5.053,-0.501 -9,-4.765 -9,-9.95L7,13C7,7.477 11.477,3 17,3h20c5.523,0 10,4.477 10,10v20c0,5.523 -4.477,10 -10,10h-8l-9.586,9.586c-1.26,1.26 -3.414,0.367 -3.414,-1.414L16,42.95Z"
|
||||
android:fillColor="#5C5E65"
|
||||
android:fillType="evenOdd"/>
|
||||
<path
|
||||
android:pathData="M44,42m-12,0a12,12 0,1 1,24 0a12,12 0,1 1,-24 0"
|
||||
android:fillColor="#930006"/>
|
||||
<path
|
||||
android:pathData="M40.53,37.47a0.75,0.75 0,1 0,-1.06 1.06L42.94,42l-3.47,3.47a0.75,0.75 0,1 0,1.06 1.06L44,43.06l3.47,3.47a0.75,0.75 0,1 0,1.06 -1.06L45.06,42l3.47,-3.47a0.75,0.75 0,1 0,-1.06 -1.06L44,40.94l-3.47,-3.47Z"
|
||||
android:fillColor="#FFDAD4"/>
|
||||
<path
|
||||
android:pathData="M32.5,10.75A5.25,5.25 0,0 0,27.25 16v4.007C26.858,20 26.413,20 25.9,20h-2.8c-1.96,0 -2.94,0 -3.689,0.381a3.5,3.5 0,0 0,-1.53 1.53c-0.381,0.749 -0.381,1.729 -0.381,3.689v1.8c0,1.96 0,2.94 0.381,3.689a3.5,3.5 0,0 0,1.53 1.53C20.16,33 21.14,33 23.1,33h2.8c1.96,0 2.94,0 3.689,-0.382a3.5,3.5 0,0 0,1.53 -1.529c0.381,-0.749 0.381,-1.729 0.381,-3.689v-1.8c0,-1.96 0,-2.94 -0.381,-3.689a3.5,3.5 0,0 0,-1.369 -1.442L29.75,16a2.75,2.75 0,1 1,5.5 0v2.5a1.25,1.25 0,1 0,2.5 0L37.75,16c0,-2.9 -2.35,-5.25 -5.25,-5.25ZM25.5,26.686a1.75,1.75 0,1 0,-2 0L23.5,28.5a1,1 0,1 0,2 0v-1.814Z"
|
||||
android:fillColor="#808389"
|
||||
android:fillType="evenOdd"/>
|
||||
</vector>
|
Binary file not shown.
Before Width: | Height: | Size: 264 B |
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue