|
|
@ -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) {
|
|
|
|
override fun getCount(): Int {
|
|
|
|
val pagerState = rememberPagerState { TITLES.size }
|
|
|
|
return 2
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
override fun getItem(index: Int): Fragment {
|
|
|
|
Column {
|
|
|
|
return when (index) {
|
|
|
|
SessionTabRow(pagerState, TITLES)
|
|
|
|
0 -> ViewMyQRCodeFragment()
|
|
|
|
HorizontalPager(
|
|
|
|
1 -> {
|
|
|
|
state = pagerState,
|
|
|
|
val result = ScanQRCodeWrapperFragment()
|
|
|
|
modifier = Modifier.weight(1f)
|
|
|
|
result.delegate = activity
|
|
|
|
) { page ->
|
|
|
|
result.message = activity.resources.getString(R.string.activity_qr_code_view_scan_qr_code_explanation)
|
|
|
|
when (TITLES[page]) {
|
|
|
|
result
|
|
|
|
R.string.view -> QrPage(sessionId)
|
|
|
|
|
|
|
|
R.string.scan -> MaybeScanQrCode()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
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
|
|
|
|
|
|
|
|
class ViewMyQRCodeFragment : Fragment() {
|
|
|
|
|
|
|
|
private lateinit var binding: FragmentViewMyQrCodeBinding
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
private val hexEncodedPublicKey: String
|
|
|
|
|
|
|
|
get() {
|
|
|
|
|
|
|
|
return TextSecurePreferences.getLocalNumber(requireContext())!!
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
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?) {
|
|
|
|
@Composable
|
|
|
|
super.onViewCreated(view, savedInstanceState)
|
|
|
|
fun QrPage(string: String) {
|
|
|
|
val size = toPx(280, resources)
|
|
|
|
Column(modifier = Modifier.padding(horizontal = 32.dp).fillMaxSize()) {
|
|
|
|
val qrCode = QRCodeUtilities.encode(hexEncodedPublicKey, size, false, false)
|
|
|
|
QrImage(
|
|
|
|
binding.qrCodeImageView.setImageBitmap(qrCode)
|
|
|
|
string = string,
|
|
|
|
// val explanation = SpannableStringBuilder("This is your unique public QR code. Other users can scan this to start a conversation with you.")
|
|
|
|
contentDescription = "Your session id",
|
|
|
|
// explanation.setSpan(StyleSpan(Typeface.BOLD), 8, 34, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE)
|
|
|
|
modifier = Modifier.padding(top = 32.dp, bottom = 12.dp),
|
|
|
|
binding.explanationTextView.text = resources.getString(R.string.fragment_view_my_qr_code_explanation)
|
|
|
|
icon = R.drawable.session
|
|
|
|
binding.shareButton.setOnClickListener { shareQRCode() }
|
|
|
|
)
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
private fun shareQRCode() {
|
|
|
|
Text(
|
|
|
|
val directory = requireContext().getExternalFilesDir(Environment.DIRECTORY_PICTURES)
|
|
|
|
text = "This is your Account ID. Other users can scan it to start a conversation with you.",
|
|
|
|
val fileName = "$hexEncodedPublicKey.png"
|
|
|
|
textAlign = TextAlign.Center,
|
|
|
|
val file = File(directory, fileName)
|
|
|
|
style = MaterialTheme.typography.small
|
|
|
|
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
|
|
|
|
|