diff --git a/res/layout/activity_new_conversation.xml b/res/layout/activity_new_conversation.xml
index 16350585f3..13eb1f2896 100644
--- a/res/layout/activity_new_conversation.xml
+++ b/res/layout/activity_new_conversation.xml
@@ -28,6 +28,17 @@
android:layout_marginTop="24dp"
android:text="@string/activity_new_conversation_public_key_explanation" />
+
+
+ app:cpb_textIdle="@string/activity_new_conversation_next_button_title" />
diff --git a/res/layout/fragment_scan_qr_code.xml b/res/layout/fragment_scan_qr_code.xml
new file mode 100644
index 0000000000..ab9407f515
--- /dev/null
+++ b/res/layout/fragment_scan_qr_code.xml
@@ -0,0 +1,38 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/res/values/strings.xml b/res/values/strings.xml
index 59b20c7c58..c3872573de 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -1588,7 +1588,8 @@
New Conversation
Public Key
Enter the public key of the person you\'d like to securely message. They can share their public key with you by going into Loki Messenger\'s in-app settings and clicking \"Share Public Key\".
- Next
+ Scan a QR Code Instead
+ Next
Invalid Public Key
Accept
@@ -1606,5 +1607,10 @@
Your QR Code
This is your personal QR code. Other people can scan it to start a secure conversation with you.
+ Loki Messenger needs camera access to scan QR codes.
+ Loki Messenger can\'t scan QR codes without camera access.
+
+ Scan QR Code
+ Scan the QR code of the person you\'d like to securely message. They can find their QR code by going into Loki Messenger\'s in-app settings and clicking \"Show QR Code\".
diff --git a/src/org/thoughtcrime/securesms/loki/NewConversationActivity.kt b/src/org/thoughtcrime/securesms/loki/NewConversationActivity.kt
index 8303739595..e1b6d59ba4 100644
--- a/src/org/thoughtcrime/securesms/loki/NewConversationActivity.kt
+++ b/src/org/thoughtcrime/securesms/loki/NewConversationActivity.kt
@@ -1,21 +1,23 @@
package org.thoughtcrime.securesms.loki
+import android.Manifest
import android.content.Intent
import android.os.Bundle
import android.view.MenuItem
import android.widget.Toast
-import kotlinx.android.synthetic.main.activity_new_conversation.*
-import network.loki.messenger.R;
+import network.loki.messenger.R
import org.thoughtcrime.securesms.PassphraseRequiredActionBarActivity
import org.thoughtcrime.securesms.conversation.ConversationActivity
import org.thoughtcrime.securesms.database.Address
import org.thoughtcrime.securesms.database.DatabaseFactory
import org.thoughtcrime.securesms.database.ThreadDatabase
+import org.thoughtcrime.securesms.permissions.Permissions
+import org.thoughtcrime.securesms.qr.ScanListener
import org.thoughtcrime.securesms.recipients.Recipient
import org.thoughtcrime.securesms.util.DynamicTheme
import org.whispersystems.signalservice.loki.utilities.PublicKeyValidation
-class NewConversationActivity : PassphraseRequiredActionBarActivity() {
+class NewConversationActivity : PassphraseRequiredActionBarActivity(), ScanListener {
private val dynamicTheme = DynamicTheme()
override fun onPreCreate() {
@@ -23,10 +25,10 @@ class NewConversationActivity : PassphraseRequiredActionBarActivity() {
}
override fun onCreate(bundle: Bundle?, isReady: Boolean) {
- setContentView(R.layout.activity_new_conversation)
supportActionBar!!.setTitle(R.string.activity_new_conversation_title)
supportActionBar!!.setDisplayHomeAsUpEnabled(true)
- nextButton.setOnClickListener { startNewConversationIfPossible() }
+ val fragment = NewConversationFragment()
+ initFragment(android.R.id.content, fragment, null)
}
public override fun onResume() {
@@ -42,8 +44,25 @@ class NewConversationActivity : PassphraseRequiredActionBarActivity() {
return super.onOptionsItemSelected(item)
}
- private fun startNewConversationIfPossible() {
- val hexEncodedPublicKey = publicKeyEditText.text.toString().trim()
+ fun scanQRCode() {
+ Permissions.with(this)
+ .request(Manifest.permission.CAMERA)
+ .ifNecessary()
+ .withPermanentDenialDialog(getString(R.string.fragment_qr_code_camera_permission_dialog_message))
+ .onAllGranted {
+ val fragment = ScanQRCodeFragment()
+ fragment.setScanListener(this)
+ supportFragmentManager.beginTransaction().replace(android.R.id.content, fragment).addToBackStack(null).commitAllowingStateLoss()
+ }
+ .onAnyDenied { Toast.makeText(this, R.string.fragment_qr_code_camera_permission_denied_message, Toast.LENGTH_SHORT).show() }
+ .execute()
+ }
+
+ override fun onQrDataFound(hexEncodedPublicKey: String) {
+ startNewConversationIfPossible(hexEncodedPublicKey)
+ }
+
+ fun startNewConversationIfPossible(hexEncodedPublicKey: String) {
if (PublicKeyValidation.isValid(hexEncodedPublicKey)) {
val contact = Recipient.from(this, Address.fromSerialized(hexEncodedPublicKey), true)
val intent = Intent(this, ConversationActivity::class.java)
diff --git a/src/org/thoughtcrime/securesms/loki/NewConversationFragment.kt b/src/org/thoughtcrime/securesms/loki/NewConversationFragment.kt
new file mode 100644
index 0000000000..322cd6f74d
--- /dev/null
+++ b/src/org/thoughtcrime/securesms/loki/NewConversationFragment.kt
@@ -0,0 +1,35 @@
+package org.thoughtcrime.securesms.loki
+
+import android.os.Bundle
+import android.support.v4.app.Fragment
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import kotlinx.android.synthetic.main.activity_new_conversation.*
+import network.loki.messenger.R
+
+class NewConversationFragment() : Fragment() {
+
+ override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
+ return inflater.inflate(R.layout.activity_new_conversation, container, false)
+ }
+
+ override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
+ super.onViewCreated(view, savedInstanceState)
+ qrCodeButton.setOnClickListener {
+ val activity = activity as NewConversationActivity
+ activity.scanQRCode()
+ }
+ nextButton.setOnClickListener {
+ val activity = activity as NewConversationActivity
+ val hexEncodedPublicKey = publicKeyEditText.text.toString().trim()
+ activity.startNewConversationIfPossible(hexEncodedPublicKey)
+ }
+ }
+
+ override fun onResume() {
+ super.onResume()
+ val activity = activity as NewConversationActivity
+ activity.supportActionBar!!.setTitle(R.string.activity_new_conversation_title)
+ }
+}
\ No newline at end of file
diff --git a/src/org/thoughtcrime/securesms/loki/ScanQRCodeFragment.kt b/src/org/thoughtcrime/securesms/loki/ScanQRCodeFragment.kt
new file mode 100644
index 0000000000..322217dce4
--- /dev/null
+++ b/src/org/thoughtcrime/securesms/loki/ScanQRCodeFragment.kt
@@ -0,0 +1,98 @@
+package org.thoughtcrime.securesms.loki
+
+import android.annotation.TargetApi
+import android.content.res.Configuration
+import android.os.Build
+import android.os.Bundle
+import android.support.v4.app.Fragment
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewAnimationUtils
+import android.view.ViewGroup
+import android.view.animation.DecelerateInterpolator
+import android.widget.LinearLayout
+import network.loki.messenger.R
+import org.thoughtcrime.securesms.components.camera.CameraView
+import org.thoughtcrime.securesms.qr.ScanListener
+import org.thoughtcrime.securesms.qr.ScanningThread
+import org.thoughtcrime.securesms.util.ViewUtil
+
+class ScanQRCodeFragment : Fragment() {
+
+ private var container: ViewGroup? = null
+ private var overlay: LinearLayout? = null
+ private var scannerView: CameraView? = null
+ private var scanningThread: ScanningThread? = null
+ private var scanListener: ScanListener? = null
+
+ override fun onCreateView(inflater: LayoutInflater, viewGroup: ViewGroup?, bundle: Bundle?): View? {
+ this.container = ViewUtil.inflate(inflater, viewGroup!!, R.layout.fragment_scan_qr_code)
+ this.overlay = ViewUtil.findById(this.container!!, R.id.overlay)
+ this.scannerView = ViewUtil.findById(this.container!!, R.id.cameraView)
+
+ if (resources.configuration.orientation == Configuration.ORIENTATION_LANDSCAPE) {
+ this.overlay!!.orientation = LinearLayout.HORIZONTAL
+ } else {
+ this.overlay!!.orientation = LinearLayout.VERTICAL
+ }
+
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
+ this.container!!.addOnLayoutChangeListener(object : View.OnLayoutChangeListener {
+ @TargetApi(Build.VERSION_CODES.LOLLIPOP)
+ override fun onLayoutChange(v: View, left: Int, top: Int, right: Int, bottom: Int,
+ oldLeft: Int, oldTop: Int, oldRight: Int, oldBottom: Int) {
+ v.removeOnLayoutChangeListener(this)
+
+ val reveal = ViewAnimationUtils.createCircularReveal(v, right, bottom, 0f, Math.hypot(right.toDouble(), bottom.toDouble()).toInt().toFloat())
+ reveal.interpolator = DecelerateInterpolator(2f)
+ reveal.duration = 800
+ reveal.start()
+ }
+ })
+ }
+
+ return this.container
+ }
+
+ override fun onResume() {
+ super.onResume()
+ this.scanningThread = ScanningThread()
+ this.scanningThread!!.setScanListener(scanListener)
+ this.scannerView!!.onResume()
+ this.scannerView!!.setPreviewCallback(scanningThread!!)
+ this.scanningThread!!.start()
+ val activity = activity as NewConversationActivity
+ activity.supportActionBar!!.setTitle(R.string.fragment_scan_qr_code_title)
+ }
+
+ override fun onPause() {
+ super.onPause()
+ this.scannerView!!.onPause()
+ this.scanningThread!!.stopScanning()
+ }
+
+ override fun onConfigurationChanged(newConfiguration: Configuration?) {
+ super.onConfigurationChanged(newConfiguration)
+
+ this.scannerView!!.onPause()
+
+ if (newConfiguration!!.orientation == Configuration.ORIENTATION_LANDSCAPE) {
+ overlay!!.orientation = LinearLayout.HORIZONTAL
+ } else {
+ overlay!!.orientation = LinearLayout.VERTICAL
+ }
+
+ this.scannerView!!.onResume()
+ this.scannerView!!.setPreviewCallback(scanningThread!!)
+ }
+
+ fun setScanListener(scanListener: ScanListener) {
+ this.scanListener = scanListener
+
+ if (this.scanningThread != null) {
+ this.scanningThread!!.setScanListener(scanListener)
+ }
+ }
+
+
+}
\ No newline at end of file