Update QrCodeActivity

This commit is contained in:
Andrew 2024-05-03 13:38:14 +09:30
parent ff64a8bc13
commit 18a4bcdbd9
13 changed files with 149 additions and 198 deletions

View File

@ -0,0 +1,6 @@
package org.thoughtcrime.securesms.database
import android.content.Context
import org.thoughtcrime.securesms.dependencies.DatabaseComponent
fun Context.threadDatabase() = DatabaseComponent.get(this).threadDatabase()

View File

@ -43,6 +43,7 @@ import org.thoughtcrime.securesms.ui.components.OutlineButton
import org.thoughtcrime.securesms.ui.contentDescription import org.thoughtcrime.securesms.ui.contentDescription
import org.thoughtcrime.securesms.ui.session_accent import org.thoughtcrime.securesms.ui.session_accent
import org.thoughtcrime.securesms.util.setUpActionBarSessionLogo import org.thoughtcrime.securesms.util.setUpActionBarSessionLogo
import org.thoughtcrime.securesms.util.start
class LandingActivity : BaseActionBarActivity() { class LandingActivity : BaseActionBarActivity() {
@ -105,7 +106,7 @@ class LandingActivity : BaseActionBarActivity() {
.width(262.dp) .width(262.dp)
.align(Alignment.CenterHorizontally) .align(Alignment.CenterHorizontally)
.contentDescription(R.string.AccessibilityId_restore_account_button) .contentDescription(R.string.AccessibilityId_restore_account_button)
) { startLinkDeviceActivity() } ) { start<LinkDeviceActivity>() }
Spacer(modifier = Modifier.height(8.dp)) Spacer(modifier = Modifier.height(8.dp))
BorderlessButton( BorderlessButton(
text = stringResource(R.string.onboardingTosPrivacy), text = stringResource(R.string.onboardingTosPrivacy),

View File

@ -1,8 +1,6 @@
package org.thoughtcrime.securesms.onboarding package org.thoughtcrime.securesms.onboarding
import android.annotation.SuppressLint import android.annotation.SuppressLint
import android.content.Context
import android.content.Intent
import android.os.Bundle import android.os.Bundle
import androidx.activity.viewModels import androidx.activity.viewModels
import androidx.camera.core.ExperimentalGetImage import androidx.camera.core.ExperimentalGetImage
@ -160,10 +158,6 @@ fun RecoveryPassword(state: LinkDeviceState, onChange: (String) -> Unit = {}, on
} }
} }
fun Context.startLinkDeviceActivity() {
Intent(this, LinkDeviceActivity::class.java).let(::startActivity)
}
class Analyzer( class Analyzer(
private val scanner: BarcodeScanner, private val scanner: BarcodeScanner,
private val onBarcodeScanned: (String) -> Unit private val onBarcodeScanned: (String) -> Unit

View File

@ -43,7 +43,7 @@ import org.thoughtcrime.securesms.ui.ThemeResPreviewParameterProvider
import org.thoughtcrime.securesms.ui.classicDarkColors import org.thoughtcrime.securesms.ui.classicDarkColors
import org.thoughtcrime.securesms.ui.colorDestructive import org.thoughtcrime.securesms.ui.colorDestructive
import org.thoughtcrime.securesms.ui.components.OutlineButton import org.thoughtcrime.securesms.ui.components.OutlineButton
import org.thoughtcrime.securesms.ui.components.QrImageCard import org.thoughtcrime.securesms.ui.components.QrImage
import org.thoughtcrime.securesms.ui.components.SmallButtons import org.thoughtcrime.securesms.ui.components.SmallButtons
import org.thoughtcrime.securesms.ui.components.TemporaryStateButton import org.thoughtcrime.securesms.ui.components.TemporaryStateButton
import org.thoughtcrime.securesms.ui.contentDescription import org.thoughtcrime.securesms.ui.contentDescription
@ -161,7 +161,7 @@ fun RecoveryPasswordCell(seed: String, copySeed:() -> Unit = {}) {
showQr, showQr,
modifier = Modifier.align(Alignment.CenterHorizontally) modifier = Modifier.align(Alignment.CenterHorizontally)
) { ) {
QrImageCard( QrImage(
seed, seed,
modifier = Modifier.padding(vertical = 24.dp), modifier = Modifier.padding(vertical = 24.dp),
contentDescription = "QR code of your recovery password", contentDescription = "QR code of your recovery password",

View File

@ -1,141 +1,96 @@
package org.thoughtcrime.securesms.preferences package org.thoughtcrime.securesms.preferences
import android.content.Intent
import android.graphics.Bitmap
import android.os.Bundle import android.os.Bundle
import android.os.Environment
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.Toast import android.widget.Toast
import androidx.fragment.app.Fragment import androidx.compose.foundation.ExperimentalFoundationApi
import androidx.fragment.app.FragmentPagerAdapter import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.pager.HorizontalPager
import androidx.compose.foundation.pager.rememberPagerState
import androidx.compose.material.MaterialTheme
import androidx.compose.material.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.unit.dp
import network.loki.messenger.R import network.loki.messenger.R
import network.loki.messenger.databinding.ActivityQrCodeBinding
import network.loki.messenger.databinding.FragmentViewMyQrCodeBinding
import org.session.libsession.utilities.Address import org.session.libsession.utilities.Address
import org.session.libsession.utilities.TextSecurePreferences import org.session.libsession.utilities.TextSecurePreferences
import org.session.libsession.utilities.recipients.Recipient import org.session.libsession.utilities.recipients.Recipient
import org.session.libsignal.utilities.PublicKeyValidation import org.session.libsignal.utilities.PublicKeyValidation
import org.thoughtcrime.securesms.PassphraseRequiredActionBarActivity import org.thoughtcrime.securesms.PassphraseRequiredActionBarActivity
import org.thoughtcrime.securesms.conversation.v2.ConversationActivityV2 import org.thoughtcrime.securesms.conversation.v2.ConversationActivityV2
import org.thoughtcrime.securesms.dependencies.DatabaseComponent import org.thoughtcrime.securesms.database.threadDatabase
import org.thoughtcrime.securesms.util.FileProviderUtil import org.thoughtcrime.securesms.ui.components.MaybeScanQrCode
import org.thoughtcrime.securesms.util.QRCodeUtilities import org.thoughtcrime.securesms.ui.components.QrImage
import org.thoughtcrime.securesms.util.ScanQRCodeWrapperFragment import org.thoughtcrime.securesms.ui.components.SessionTabRow
import org.thoughtcrime.securesms.ui.setComposeContent
import org.thoughtcrime.securesms.ui.small
import org.thoughtcrime.securesms.util.ScanQRCodeWrapperFragmentDelegate import org.thoughtcrime.securesms.util.ScanQRCodeWrapperFragmentDelegate
import org.thoughtcrime.securesms.util.toPx import org.thoughtcrime.securesms.util.start
import java.io.File
import java.io.FileOutputStream private val TITLES = listOf(R.string.view, R.string.scan)
class QRCodeActivity : PassphraseRequiredActionBarActivity(), ScanQRCodeWrapperFragmentDelegate { class QRCodeActivity : PassphraseRequiredActionBarActivity(), ScanQRCodeWrapperFragmentDelegate {
private lateinit var binding: ActivityQrCodeBinding
private val adapter = QRCodeActivityAdapter(this)
// region Lifecycle
override fun onCreate(savedInstanceState: Bundle?, isReady: Boolean) { override fun onCreate(savedInstanceState: Bundle?, isReady: Boolean) {
super.onCreate(savedInstanceState, isReady) super.onCreate(savedInstanceState, isReady)
binding = ActivityQrCodeBinding.inflate(layoutInflater)
// Set content view
setContentView(binding.root)
// Set title
supportActionBar!!.title = resources.getString(R.string.activity_qr_code_title) supportActionBar!!.title = resources.getString(R.string.activity_qr_code_title)
// Set up view pager
binding.viewPager.adapter = adapter
binding.tabLayout.setupWithViewPager(binding.viewPager)
}
// endregion
// region Interaction setComposeContent {
override fun handleQRCodeScanned(hexEncodedPublicKey: String) { Tabs(TextSecurePreferences.getLocalNumber(this)!!)
createPrivateChatIfPossible(hexEncodedPublicKey) }
} }
fun createPrivateChatIfPossible(hexEncodedPublicKey: String) { override fun handleQRCodeScanned(string: String) {
if (!PublicKeyValidation.isValid(hexEncodedPublicKey)) { return Toast.makeText(this, R.string.invalid_session_id, Toast.LENGTH_SHORT).show() } if (!PublicKeyValidation.isValid(string)) {
val recipient = Recipient.from(this, Address.fromSerialized(hexEncodedPublicKey), false) return Toast.makeText(this, R.string.invalid_session_id, Toast.LENGTH_SHORT).show()
val intent = Intent(this, ConversationActivityV2::class.java) }
intent.putExtra(ConversationActivityV2.ADDRESS, recipient.address) val recipient = Recipient.from(this, Address.fromSerialized(string), false)
intent.setDataAndType(getIntent().data, getIntent().type) start<ConversationActivityV2> {
val existingThread = DatabaseComponent.get(this).threadDatabase().getThreadIdIfExistsFor(recipient) putExtra(ConversationActivityV2.ADDRESS, recipient.address)
intent.putExtra(ConversationActivityV2.THREAD_ID, existingThread) setDataAndType(intent.data, intent.type)
startActivity(intent) val existingThread = threadDatabase().getThreadIdIfExistsFor(recipient)
putExtra(ConversationActivityV2.THREAD_ID, existingThread)
}
finish() finish()
} }
// endregion
} }
// region Adapter @OptIn(ExperimentalFoundationApi::class)
private class QRCodeActivityAdapter(val activity: QRCodeActivity) : FragmentPagerAdapter(activity.supportFragmentManager) { @Composable
fun Tabs(sessionId: String) {
val pagerState = rememberPagerState { TITLES.size }
override fun getCount(): Int { Column {
return 2 SessionTabRow(pagerState, TITLES)
} HorizontalPager(
state = pagerState,
override fun getItem(index: Int): Fragment { modifier = Modifier.weight(1f)
return when (index) { ) { page ->
0 -> ViewMyQRCodeFragment() when (TITLES[page]) {
1 -> { R.string.view -> QrPage(sessionId)
val result = ScanQRCodeWrapperFragment() R.string.scan -> MaybeScanQrCode()
result.delegate = activity
result.message = activity.resources.getString(R.string.activity_qr_code_view_scan_qr_code_explanation)
result
} }
else -> throw IllegalStateException()
}
}
override fun getPageTitle(index: Int): CharSequence? {
return when (index) {
0 -> activity.resources.getString(R.string.activity_qr_code_view_my_qr_code_tab_title)
1 -> activity.resources.getString(R.string.activity_qr_code_view_scan_qr_code_tab_title)
else -> throw IllegalStateException()
} }
} }
} }
// endregion
// region View My QR Code Fragment @Composable
class ViewMyQRCodeFragment : Fragment() { fun QrPage(string: String) {
private lateinit var binding: FragmentViewMyQrCodeBinding Column(modifier = Modifier.padding(horizontal = 32.dp).fillMaxSize()) {
QrImage(
string = string,
contentDescription = "Your session id",
modifier = Modifier.padding(top = 32.dp, bottom = 12.dp),
icon = R.drawable.session
)
private val hexEncodedPublicKey: String Text(
get() { text = "This is your Account ID. Other users can scan it to start a conversation with you.",
return TextSecurePreferences.getLocalNumber(requireContext())!! textAlign = TextAlign.Center,
} style = MaterialTheme.typography.small
)
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View {
binding = FragmentViewMyQrCodeBinding.inflate(inflater, container, false)
return binding.root
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
val size = toPx(280, resources)
val qrCode = QRCodeUtilities.encode(hexEncodedPublicKey, size, false, false)
binding.qrCodeImageView.setImageBitmap(qrCode)
// val explanation = SpannableStringBuilder("This is your unique public QR code. Other users can scan this to start a conversation with you.")
// explanation.setSpan(StyleSpan(Typeface.BOLD), 8, 34, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE)
binding.explanationTextView.text = resources.getString(R.string.fragment_view_my_qr_code_explanation)
binding.shareButton.setOnClickListener { shareQRCode() }
}
private fun shareQRCode() {
val directory = requireContext().getExternalFilesDir(Environment.DIRECTORY_PICTURES)
val fileName = "$hexEncodedPublicKey.png"
val file = File(directory, fileName)
file.createNewFile()
val fos = FileOutputStream(file)
val size = toPx(280, resources)
val qrCode = QRCodeUtilities.encode(hexEncodedPublicKey, size, false, false)
qrCode.compress(Bitmap.CompressFormat.PNG, 100, fos)
fos.flush()
fos.close()
val intent = Intent(Intent.ACTION_SEND)
intent.putExtra(Intent.EXTRA_STREAM, FileProviderUtil.getUriFor(requireActivity(), file))
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
intent.type = "image/png"
startActivity(Intent.createChooser(intent, resources.getString(R.string.fragment_view_my_qr_code_share_title)))
} }
} }
// endregion

View File

@ -20,16 +20,12 @@ import android.view.inputmethod.EditorInfo
import android.view.inputmethod.InputMethodManager import android.view.inputmethod.InputMethodManager
import android.widget.Toast import android.widget.Toast
import androidx.compose.animation.AnimatedVisibility import androidx.compose.animation.AnimatedVisibility
import androidx.compose.foundation.interaction.InteractionSource
import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.wrapContentHeight
import androidx.compose.material.Card
import androidx.compose.material.MaterialTheme
import androidx.compose.material.Text import androidx.compose.material.Text
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.DisposableEffect import androidx.compose.runtime.DisposableEffect
@ -40,18 +36,13 @@ import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.stringResource import androidx.compose.ui.res.stringResource
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.tooling.preview.PreviewParameter
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import androidx.core.view.isGone
import androidx.core.view.isInvisible import androidx.core.view.isInvisible
import androidx.core.view.isVisible import androidx.core.view.isVisible
import androidx.lifecycle.lifecycleScope import androidx.lifecycle.lifecycleScope
import androidx.localbroadcastmanager.content.LocalBroadcastManager import androidx.localbroadcastmanager.content.LocalBroadcastManager
import dagger.hilt.android.AndroidEntryPoint import dagger.hilt.android.AndroidEntryPoint
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.channels.BufferOverflow
import kotlinx.coroutines.channels.Channel
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext import kotlinx.coroutines.withContext
import network.loki.messenger.BuildConfig import network.loki.messenger.BuildConfig
@ -184,7 +175,7 @@ class SettingsActivity : PassphraseRequiredActionBarActivity() {
override fun onOptionsItemSelected(item: MenuItem): Boolean { override fun onOptionsItemSelected(item: MenuItem): Boolean {
return when (item.itemId) { return when (item.itemId) {
R.id.action_qr_code -> { R.id.action_qr_code -> {
showQRCode() push<QRCodeActivity>()
true true
} }
else -> super.onOptionsItemSelected(item) else -> super.onOptionsItemSelected(item)
@ -325,11 +316,6 @@ class SettingsActivity : PassphraseRequiredActionBarActivity() {
return true return true
} }
private fun showQRCode() {
val intent = Intent(this, QRCodeActivity::class.java)
push(intent)
}
private fun showEditProfilePictureUI() { private fun showEditProfilePictureUI() {
showSessionDialog { showSessionDialog {
title(R.string.activity_settings_set_display_picture) title(R.string.activity_settings_set_display_picture)

View File

@ -0,0 +1,11 @@
package org.thoughtcrime.securesms.ui
import android.app.Activity
import androidx.compose.runtime.Composable
import androidx.compose.ui.platform.ComposeView
fun Activity.setComposeContent(content: @Composable () -> Unit) {
ComposeView(this)
.apply { setContent { AppTheme { content() } } }
.let(::setContentView)
}

View File

@ -58,13 +58,6 @@ typealias CameraPreview = androidx.camera.core.Preview
private const val TAG = "NewMessageFragment" private const val TAG = "NewMessageFragment"
@Composable
fun MaybeScanQrCode(errors: Flow<String> = emptyFlow()) {
LocalContext.current.run {
MaybeScanQrCode(onScan = {})
}
}
@OptIn(ExperimentalPermissionsApi::class) @OptIn(ExperimentalPermissionsApi::class)
@Composable @Composable
fun MaybeScanQrCode( fun MaybeScanQrCode(

View File

@ -2,17 +2,15 @@ package org.thoughtcrime.securesms.ui.components
import android.graphics.Bitmap import android.graphics.Bitmap
import androidx.compose.animation.AnimatedVisibility import androidx.compose.animation.AnimatedVisibility
import androidx.compose.animation.scaleIn import androidx.compose.animation.fadeIn
import androidx.compose.foundation.Image import androidx.compose.foundation.Image
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.aspectRatio import androidx.compose.foundation.layout.aspectRatio
import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.width
import androidx.compose.material.Card import androidx.compose.material.Card
import androidx.compose.material.Icon import androidx.compose.material.Icon
import androidx.compose.material.MaterialTheme
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue import androidx.compose.runtime.getValue
@ -34,21 +32,12 @@ import org.thoughtcrime.securesms.ui.LocalOnLightCell
import org.thoughtcrime.securesms.util.QRCodeUtilities import org.thoughtcrime.securesms.util.QRCodeUtilities
@Composable @Composable
fun QrImageCard( fun QrImage(
string: String, string: String,
contentDescription: String, contentDescription: String,
modifier: Modifier = Modifier, modifier: Modifier = Modifier,
icon: Int = R.drawable.session_shield icon: Int = R.drawable.session_shield
) { ) {
Card(
backgroundColor = LocalLightCell.current,
elevation = 0.dp,
modifier = modifier
) { QrImage(string, contentDescription, icon) }
}
@Composable
fun QrImage(string: String, contentDescription: String, icon: Int = R.drawable.session_shield) {
var bitmap: Bitmap? by remember { var bitmap: Bitmap? by remember {
mutableStateOf(null) mutableStateOf(null)
} }
@ -56,42 +45,57 @@ fun QrImage(string: String, contentDescription: String, icon: Int = R.drawable.s
val scope = rememberCoroutineScope() val scope = rememberCoroutineScope()
LaunchedEffect(string) { LaunchedEffect(string) {
scope.launch(Dispatchers.IO) { scope.launch(Dispatchers.IO) {
bitmap = QRCodeUtilities.encode(string, 400) bitmap = QRCodeUtilities.encode(string, 400).also {
for (y in 150 until 250) {
for (x in 150 until 250) {
it.setPixel(x, y, 0x00000000)
}
}
}
} }
} }
Box( @Composable
modifier = Modifier fun content(modifier: Modifier = Modifier) {
.fillMaxWidth() Box(
.aspectRatio(1f) modifier = modifier
) { .fillMaxWidth()
AnimatedVisibility( .aspectRatio(1f)
visible = bitmap != null,
enter = scaleIn()
) { ) {
bitmap?.let { AnimatedVisibility(
Image( visible = bitmap != null,
bitmap = it.asImageBitmap(), enter = fadeIn(),
modifier = Modifier ) {
.fillMaxWidth() bitmap?.let {
.aspectRatio(1f), Image(
contentDescription = contentDescription, bitmap = it.asImageBitmap(),
colorFilter = ColorFilter.tint(LocalOnLightCell.current) modifier = Modifier
) .fillMaxWidth()
.aspectRatio(1f),
contentDescription = contentDescription,
colorFilter = ColorFilter.tint(LocalOnLightCell.current)
)
}
} }
Icon(
painter = painterResource(id = icon),
contentDescription = "",
tint = LocalOnLightCell.current,
modifier = Modifier
.align(Alignment.Center)
.size(60.dp)
)
} }
}
Icon( if (MaterialTheme.colors.isLight) {
painter = painterResource(id = icon), content(modifier)
contentDescription = "", } else {
tint = LocalOnLightCell.current, Card(
modifier = Modifier backgroundColor = LocalLightCell.current,
.align(Alignment.Center) elevation = 0.dp,
.width(46.dp) modifier = modifier
.height(56.dp) ) { content() }
.background(color = LocalLightCell.current)
.padding(horizontal = 3.dp, vertical = 1.dp)
)
} }
} }

View File

@ -102,6 +102,7 @@ data class ThemeState (
val followSystem: Boolean val followSystem: Boolean
) )
inline fun <reified T: Activity> Context.start() = Intent(this, T::class.java).also(::startActivity)
inline fun <reified T: Activity> Activity.show() = Intent(this, T::class.java).also(::startActivity).also { overridePendingTransition(R.anim.slide_from_bottom, R.anim.fade_scale_out) } inline fun <reified T: Activity> Activity.show() = Intent(this, T::class.java).also(::startActivity).also { overridePendingTransition(R.anim.slide_from_bottom, R.anim.fade_scale_out) }
inline fun <reified T: Activity> Activity.push() = Intent(this, T::class.java).also(::startActivity).also { overridePendingTransition(R.anim.slide_from_right, R.anim.fade_scale_out) } inline fun <reified T: Activity> Activity.push() = Intent(this, T::class.java).also(::startActivity).also { overridePendingTransition(R.anim.slide_from_right, R.anim.fade_scale_out) }
inline fun <reified T: Activity> Context.start() = Intent(this, T::class.java).also(::startActivity)
inline fun <reified T: Activity> Context.start(modify: Intent.() -> Unit) = Intent(this, T::class.java).also(modify).also(::startActivity)

View File

@ -0,0 +1,11 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="52dp"
android:height="63dp"
android:viewportWidth="52"
android:viewportHeight="63">
<path
android:pathData="M43.22,31.788L32.683,25.284H39.825C42.812,25.284 45.676,23.961 47.789,21.608C49.901,19.254 51.087,16.062 51.087,12.734C51.087,9.405 49.901,6.213 47.789,3.86C45.676,1.506 42.812,0.184 39.825,0.184L14.888,0.184C11.148,0.189 7.562,1.847 4.917,4.794C2.272,7.742 0.784,11.738 0.779,15.906C0.781,18.943 1.512,21.925 2.897,24.542C4.282,27.159 6.27,29.315 8.654,30.787L19.193,37.293H12.042C10.552,37.274 9.073,37.584 7.692,38.206C6.31,38.828 5.053,39.75 3.994,40.917C2.934,42.084 2.093,43.474 1.518,45.006C0.944,46.538 0.648,48.182 0.648,49.842C0.648,51.503 0.944,53.146 1.518,54.679C2.093,56.211 2.934,57.6 3.994,58.768C5.053,59.935 6.31,60.856 7.692,61.478C9.073,62.1 10.552,62.41 12.042,62.391H36.979C40.719,62.387 44.305,60.729 46.951,57.781C49.596,54.834 51.083,50.838 51.087,46.67C51.087,43.633 50.357,40.651 48.973,38.034C47.59,35.418 45.604,33.261 43.22,31.788ZM10.338,27.386C8.541,26.275 7.035,24.659 5.971,22.7C4.907,20.74 4.322,18.504 4.273,16.215C4.132,9.538 9.163,4.072 15.156,4.072H39.599C43.781,4.072 47.381,7.638 47.589,12.297C47.64,13.47 47.478,14.642 47.111,15.742C46.745,16.842 46.181,17.848 45.455,18.699C44.729,19.549 43.856,20.226 42.888,20.69C41.92,21.153 40.878,21.392 39.825,21.393H25.748C25.336,21.396 24.941,21.58 24.65,21.906C24.359,22.233 24.196,22.674 24.197,23.134V35.937L10.338,27.386ZM36.71,58.503H12.267C8.088,58.503 4.485,54.937 4.28,50.279C4.228,49.107 4.39,47.935 4.756,46.835C5.123,45.735 5.686,44.729 6.412,43.878C7.138,43.027 8.011,42.35 8.979,41.886C9.946,41.423 10.988,41.183 12.042,41.182H26.12C26.325,41.182 26.528,41.137 26.717,41.049C26.906,40.962 27.078,40.834 27.223,40.672C27.368,40.511 27.482,40.319 27.561,40.108C27.639,39.897 27.679,39.671 27.679,39.443V26.639L41.528,35.19C43.327,36.301 44.834,37.917 45.898,39.879C46.962,41.84 47.547,44.078 47.595,46.369C47.736,53.037 42.705,58.503 36.71,58.503Z"
android:strokeWidth="0.3"
android:fillColor="#000000"
android:strokeColor="#000000"/>
</vector>

View File

@ -1,13 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.viewpager.widget.ViewPager
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/viewPager"
android:layout_width="match_parent"
android:layout_height="match_parent" >
<com.google.android.material.tabs.TabLayout
android:id="@+id/tabLayout"
android:layout_width="match_parent"
android:layout_height="@dimen/tab_bar_height" />
</androidx.viewpager.widget.ViewPager>

View File

@ -1094,6 +1094,8 @@
<string name="hide">Hide</string> <string name="hide">Hide</string>
<string name="recoveryPasswordHideRecoveryPassword">Hide Recovery Password</string> <string name="recoveryPasswordHideRecoveryPassword">Hide Recovery Password</string>
<string name="qrView">View QR</string> <string name="qrView">View QR</string>
<string name="view">View</string>
<string name="scan">Scan</string>
<string name="recoveryPasswordView">View Password</string> <string name="recoveryPasswordView">View Password</string>
<string name="recoveryPasswordHideRecoveryPasswordDescription">Permanently hide your recovery password on this device.</string> <string name="recoveryPasswordHideRecoveryPasswordDescription">Permanently hide your recovery password on this device.</string>
<string name="conversationsNone">You don\'t have any conversations yet</string> <string name="conversationsNone">You don\'t have any conversations yet</string>