Partially implement device linking redesign & fix copy

This commit is contained in:
Niels Andriesse 2020-01-08 15:16:34 +11:00
parent 7da4f1f6ae
commit df61cbb30d
23 changed files with 487 additions and 49 deletions

View File

@ -126,37 +126,32 @@
<!-- Session -->
<activity
android:name="org.thoughtcrime.securesms.loki.redesign.activities.LandingActivity"
android:launchMode="singleTask" />
android:name="org.thoughtcrime.securesms.loki.redesign.activities.LandingActivity" />
<activity
android:name="org.thoughtcrime.securesms.loki.redesign.activities.RegisterActivity"
android:launchMode="singleTask" />
<activity
android:name="org.thoughtcrime.securesms.loki.redesign.activities.DisplayNameActivity"
android:launchMode="singleTask"
android:windowSoftInputMode="adjustResize" />
android:name="org.thoughtcrime.securesms.loki.redesign.activities.RegisterActivity" />
<activity
android:name="org.thoughtcrime.securesms.loki.redesign.activities.RestoreActivity"
android:launchMode="singleTask"
android:windowSoftInputMode="adjustResize" />
<activity
android:name="org.thoughtcrime.securesms.loki.redesign.activities.LinkDeviceActivity"
android:windowSoftInputMode="adjustResize" />
<activity
android:name="org.thoughtcrime.securesms.loki.redesign.activities.DisplayNameActivity"
android:windowSoftInputMode="adjustResize" />
<activity
android:name="org.thoughtcrime.securesms.loki.redesign.activities.HomeActivity"
android:launchMode="singleTask"
android:theme="@style/Session.DarkTheme.NoActionBar" />
<activity
android:name="org.thoughtcrime.securesms.loki.redesign.activities.CreatePrivateChatActivity"
android:launchMode="singleTask" />
<activity
android:name="org.thoughtcrime.securesms.loki.redesign.activities.JoinPublicChatActivity"
android:launchMode="singleTask"
android:windowSoftInputMode="adjustResize" />
<activity
android:name="org.thoughtcrime.securesms.loki.redesign.activities.SettingsActivity"
android:launchMode="singleTask"
android:theme="@style/Session.DarkTheme.NoActionBar" />
<activity
android:name="org.thoughtcrime.securesms.loki.redesign.activities.QRCodeActivity"
android:launchMode="singleTask" />
android:name="org.thoughtcrime.securesms.loki.redesign.activities.QRCodeActivity" />
<activity
android:name="org.thoughtcrime.securesms.loki.redesign.activities.CreatePrivateChatActivity" />
<activity
android:name="org.thoughtcrime.securesms.loki.redesign.activities.JoinPublicChatActivity"
android:windowSoftInputMode="adjustResize" />
<!-- Session -->
<activity android:name="org.thoughtcrime.securesms.loki.LinkedDevicesActivity" />
<activity

View File

@ -1,7 +1,11 @@
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android" >
<set
xmlns:android="http://schemas.android.com/apk/res/android"
android:interpolator="@android:anim/decelerate_interpolator">
<translate
android:duration="350"
android:fromYDelta="100%"
android:toYDelta="0%" />
android:duration="250"
android:fromYDelta="100%"
android:toYDelta="0%" />
</set>

View File

@ -1,5 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<set
xmlns:android="http://schemas.android.com/apk/res/android"
android:interpolator="@android:anim/decelerate_interpolator">

View File

@ -0,0 +1,14 @@
<?xml version="1.0" encoding="utf-8"?>
<android.support.v4.view.ViewPager
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/viewPager"
android:layout_width="match_parent"
android:layout_height="match_parent" >
<android.support.design.widget.TabLayout
style="@style/Session.DarkTabLayout"
android:id="@+id/tabLayout"
android:layout_width="match_parent"
android:layout_height="@dimen/tab_bar_height" />
</android.support.v4.view.ViewPager>

View File

@ -30,7 +30,7 @@
android:layout_marginRight="@dimen/very_large_spacing"
android:textSize="@dimen/medium_font_size"
android:textColor="@color/text"
android:text="Enter the seed that was given to you when you signed up to restore your account." />
android:text="Enter the recovery phrase that was given to you when you signed up to restore your account." />
<EditText
style="@style/SessionEditText"
@ -40,7 +40,7 @@
android:layout_marginLeft="@dimen/very_large_spacing"
android:layout_marginTop="20dp"
android:layout_marginRight="@dimen/very_large_spacing"
android:hint="Enter your seed" />
android:hint="Enter your recovery phrase" />
<View
android:layout_width="match_parent"

View File

@ -205,6 +205,7 @@
android:text="Chats" />
<View
android:id="@+id/linkedDevicesButtonTopSeparator"
android:layout_width="match_parent"
android:layout_height="1px"
android:background="@color/separator" />
@ -221,6 +222,7 @@
android:text="Linked Devices" />
<View
android:id="@+id/seedButtonTopSeparator"
android:layout_width="match_parent"
android:layout_height="1px"
android:background="@color/separator" />
@ -234,7 +236,7 @@
android:textSize="@dimen/medium_font_size"
android:textStyle="bold"
android:gravity="center"
android:text="Show Seed" />
android:text="Show Recovery Phrase" />
<View
android:layout_width="match_parent"

View File

@ -0,0 +1,63 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:background="@drawable/default_dialog_background_inset"
android:gravity="center_horizontal"
android:orientation="vertical"
android:paddingLeft="32dp"
android:paddingTop="@dimen/medium_spacing"
android:paddingRight="32dp"
android:paddingBottom="@dimen/medium_spacing">
<com.github.ybq.android.spinkit.SpinKitView
style="@style/SpinKitView.DoubleBounce"
android:id="@+id/spinner"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:layout_centerInParent="true"
app:SpinKit_Color="@color/text" />
<TextView
android:id="@+id/titleTextView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/large_spacing"
android:text="Waiting for Authorization"
android:textColor="@color/text"
android:textStyle="bold"
android:textSize="@dimen/medium_font_size" />
<TextView
android:id="@+id/explanationTextView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/large_spacing"
android:text="Please check that the words below match those shown on your other device."
android:textColor="@color/text"
android:alpha="0.6"
android:textSize="@dimen/small_font_size"
android:textAlignment="center" />
<TextView
android:id="@+id/mnemonicTextView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/large_spacing"
android:text="puffin circle idled"
android:textColor="@color/text"
android:textSize="@dimen/small_font_size"
android:textAlignment="center" />
<Button
style="@style/UnimportantDialogButton"
android:id="@+id/cancelButton"
android:layout_width="match_parent"
android:layout_height="@dimen/small_button_height"
android:layout_marginTop="@dimen/large_spacing"
android:text="Cancel" />
</LinearLayout>

View File

@ -14,7 +14,7 @@
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Your Seed"
android:text="Your Recovery Phrase"
android:textColor="@color/text"
android:textStyle="bold"
android:textSize="@dimen/medium_font_size" />
@ -33,7 +33,7 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/large_spacing"
android:text="This is your personal password. It can be used to restore your account or migrate your account to a new device."
android:text="This is your personal recovery phrase. It can be used to restore your account or migrate your account to a new device."
android:textColor="@color/text"
android:textSize="@dimen/small_font_size"
android:textAlignment="center"

View File

@ -27,7 +27,7 @@
android:textColor="@color/text"
android:alpha="0.6"
android:textAlignment="center"
android:text="Enter the URL of the public chat you'd like to join" />
android:text="Enter the URL of the channel you'd like to join" />
<View
android:layout_width="0dp"

View File

@ -0,0 +1,59 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@drawable/default_session_background"
android:orientation="vertical">
<View
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"/>
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="@dimen/large_spacing"
android:layout_marginRight="@dimen/large_spacing"
android:textSize="@dimen/very_large_font_size"
android:textStyle="bold"
android:textColor="@color/text"
android:text="Link your device" />
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="@dimen/large_spacing"
android:layout_marginTop="@dimen/medium_spacing"
android:layout_marginRight="@dimen/large_spacing"
android:textSize="@dimen/medium_font_size"
android:textColor="@color/text"
android:text="Enter your Session ID to start the linking process." />
<EditText
style="@style/SessionEditText"
android:id="@+id/sessionIDEditText"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="@dimen/large_spacing"
android:layout_marginTop="20dp"
android:layout_marginRight="@dimen/large_spacing"
android:hint="Enter your session ID" />
<View
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"/>
<Button
style="@style/MediumProminentOutlineButton"
android:id="@+id/requestDeviceLinkButton"
android:layout_width="match_parent"
android:layout_height="@dimen/medium_button_height"
android:layout_marginLeft="90dp"
android:layout_marginRight="90dp"
android:layout_marginBottom="@dimen/medium_spacing"
android:text="Next" />
</LinearLayout>

View File

@ -53,7 +53,7 @@
android:textSize="@dimen/medium_font_size"
android:textColor="@color/text"
android:textAlignment="center"
android:text="This is your unique public QR code. Other users may scan this in order to begin a conversation with you." />
android:text="This is your unique public QR code. Other users can scan this to start a conversation with you." />
<Button
style="@style/MediumUnimportantOutlineButton"

View File

@ -22,7 +22,7 @@
android:layout_width="@dimen/fake_chat_view_bubble_width"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/medium_spacing"
android:text="It's a secure, decentralized cross-platform private messaging app"
android:text="It's a secure, decentralized private messaging app"
android:layout_gravity="left" />
<TextView
@ -31,7 +31,7 @@
android:layout_width="@dimen/fake_chat_view_bubble_width"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/medium_spacing"
android:text="So it doesn't collect my personal information or my conversation metadata? How's it work?"
android:text="So it doesn't collect my personal information or my conversation metadata? How does it work?"
android:layout_gravity="right" />
<TextView

View File

@ -13,6 +13,6 @@
android:textColor="@color/text"
android:alpha="0.6"
android:textSize="@dimen/small_font_size"
android:text="Your Public Key" />
android:text="Your Session ID" />
</RelativeLayout>

View File

@ -9,6 +9,7 @@ import org.thoughtcrime.securesms.ApplicationContext
import org.thoughtcrime.securesms.BaseActionBarActivity
import org.thoughtcrime.securesms.ConversationListActivity
import org.thoughtcrime.securesms.database.DatabaseFactory
import org.thoughtcrime.securesms.loki.redesign.utilities.show
import org.thoughtcrime.securesms.util.TextSecurePreferences
import org.whispersystems.signalservice.api.crypto.ProfileCipher
import org.whispersystems.signalservice.loki.utilities.Analytics
@ -44,7 +45,7 @@ class DisplayNameActivity : BaseActionBarActivity() {
application.setUpP2PAPI()
application.startLongPollingIfNeeded()
application.setUpStorageAPIIfNeeded()
startActivity(Intent(this, ConversationListActivity::class.java))
show(Intent(this, ConversationListActivity::class.java))
finish()
val publicChatAPI = ApplicationContext.getInstance(this).lokiPublicChatAPI
if (publicChatAPI != null) {

View File

@ -256,7 +256,7 @@ class SeedActivity : BaseActionBarActivity(), DeviceLinkingDelegate, ScanListene
TextSecurePreferences.setHasSeenWelcomeScreen(this, false)
TextSecurePreferences.setPromptedPushRegistration(this, false)
}
// endregion
override fun onQrDataFound(data: String?) {
runOnUiThread {
if (data != null && PublicKeyValidation.isValid(data.trim())) {

View File

@ -26,6 +26,7 @@ import org.thoughtcrime.securesms.database.ThreadDatabase
import org.thoughtcrime.securesms.database.model.ThreadRecord
import org.thoughtcrime.securesms.loki.getColorWithID
import org.thoughtcrime.securesms.loki.redesign.utilities.push
import org.thoughtcrime.securesms.loki.redesign.utilities.show
import org.thoughtcrime.securesms.loki.redesign.views.ConversationView
import org.thoughtcrime.securesms.mms.GlideApp
import org.thoughtcrime.securesms.mms.GlideRequests
@ -140,17 +141,17 @@ class HomeActivity : PassphraseRequiredActionBarActivity, ConversationClickListe
private fun openSettings() {
val intent = Intent(this, SettingsActivity::class.java)
startActivity(intent)
show(intent)
}
private fun createPrivateChat() {
val intent = Intent(this, CreatePrivateChatActivity::class.java)
startActivity(intent)
show(intent)
}
private fun joinPublicChat() {
val intent = Intent(this, JoinPublicChatActivity::class.java)
startActivity(intent)
show(intent)
}
private class SwipeCallback(val activity: HomeActivity) : ItemTouchHelper.SimpleCallback(0, ItemTouchHelper.LEFT) {

View File

@ -33,7 +33,7 @@ class JoinPublicChatActivity : PassphraseRequiredActionBarActivity(), ScanQRCode
// Set content view
setContentView(R.layout.activity_join_public_chat)
// Set title
supportActionBar!!.title = "Join Public Chat"
supportActionBar!!.title = "Join Channel"
// Set up view pager
viewPager.adapter = adapter
tabLayout.setupWithViewPager(viewPager)
@ -80,7 +80,7 @@ class JoinPublicChatActivity : PassphraseRequiredActionBarActivity(), ScanQRCode
finish()
}.failUi {
hideLoader()
Toast.makeText(this, "Couldn't Join Public Chat", Toast.LENGTH_SHORT).show()
Toast.makeText(this, "Couldn't Join Channel", Toast.LENGTH_SHORT).show()
}
}
// endregion
@ -99,7 +99,7 @@ private class JoinPublicChatActivityAdapter(val activity: JoinPublicChatActivity
1 -> {
val result = ScanQRCodeWrapperFragment()
result.delegate = activity
result.message = "Scan the QR code of the public chat you'd like to join"
result.message = "Scan the QR code of the channel you'd like to join"
result
}
else -> throw IllegalStateException()
@ -108,7 +108,7 @@ private class JoinPublicChatActivityAdapter(val activity: JoinPublicChatActivity
override fun getPageTitle(index: Int): CharSequence? {
return when (index) {
0 -> "Enter Chat URL"
0 -> "Enter Channel URL"
1 -> "Scan QR Code"
else -> throw IllegalStateException()
}
@ -116,7 +116,7 @@ private class JoinPublicChatActivityAdapter(val activity: JoinPublicChatActivity
}
// endregion
// region Enter Public Key Fragment
// region Enter Chat URL Fragment
class EnterChatURLFragment : Fragment() {
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View {

View File

@ -1,14 +1,36 @@
package org.thoughtcrime.securesms.loki.redesign.activities
import android.content.Intent
import android.os.AsyncTask
import android.os.Bundle
import android.widget.Toast
import kotlinx.android.synthetic.main.activity_landing.*
import network.loki.messenger.R
import org.thoughtcrime.securesms.ApplicationContext
import org.thoughtcrime.securesms.BaseActionBarActivity
import org.thoughtcrime.securesms.crypto.IdentityKeyUtil
import org.thoughtcrime.securesms.database.Address
import org.thoughtcrime.securesms.database.DatabaseFactory
import org.thoughtcrime.securesms.database.IdentityDatabase
import org.thoughtcrime.securesms.logging.Log
import org.thoughtcrime.securesms.loki.redesign.dialogs.LinkDeviceSlaveModeDialog
import org.thoughtcrime.securesms.loki.redesign.dialogs.LinkDeviceSlaveModeDialogDelegate
import org.thoughtcrime.securesms.loki.redesign.utilities.push
import org.thoughtcrime.securesms.loki.redesign.utilities.setUpActionBarSessionLogo
import org.thoughtcrime.securesms.loki.redesign.utilities.show
import org.thoughtcrime.securesms.loki.sendPairingAuthorisationMessage
import org.thoughtcrime.securesms.util.Base64
import org.thoughtcrime.securesms.util.Hex
import org.thoughtcrime.securesms.util.TextSecurePreferences
import org.whispersystems.curve25519.Curve25519
import org.whispersystems.libsignal.ecc.Curve
import org.whispersystems.libsignal.ecc.ECKeyPair
import org.whispersystems.libsignal.util.KeyHelper
import org.whispersystems.signalservice.loki.api.PairingAuthorisation
import org.whispersystems.signalservice.loki.utilities.hexEncodedPublicKey
import org.whispersystems.signalservice.loki.utilities.retryIfNeeded
class LandingActivity : BaseActionBarActivity() {
class LandingActivity : BaseActionBarActivity(), LinkDeviceSlaveModeDialogDelegate {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
@ -17,6 +39,17 @@ class LandingActivity : BaseActionBarActivity() {
fakeChatView.startAnimating()
registerButton.setOnClickListener { register() }
restoreButton.setOnClickListener { restore() }
linkButton.setOnClickListener { linkDevice() }
if (TextSecurePreferences.databaseResetFromUnpair(this)) {
Toast.makeText(this, "Your device was unlinked successfully", Toast.LENGTH_LONG).show()
}
}
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, intent)
if (resultCode != RESULT_OK) { return }
val hexEncodedPublicKey = data!!.getStringExtra("hexEncodedPublicKey")
requestDeviceLink(hexEncodedPublicKey)
}
private fun register() {
@ -28,4 +61,72 @@ class LandingActivity : BaseActionBarActivity() {
val intent = Intent(this, RestoreActivity::class.java)
push(intent)
}
private fun linkDevice() {
val intent = Intent(this, LinkDeviceActivity::class.java)
show(intent, true)
}
private fun requestDeviceLink(hexEncodedPublicKey: String) {
var seed: ByteArray? = null
var keyPair: ECKeyPair? = null
fun generateKeyPair() {
val seedCandidate = Curve25519.getInstance(Curve25519.BEST).generateSeed(16)
try {
keyPair = Curve.generateKeyPair(seedCandidate + seedCandidate) // Validate the seed
} catch (exception: Exception) {
return generateKeyPair()
}
seed = seedCandidate
}
generateKeyPair()
IdentityKeyUtil.save(this, IdentityKeyUtil.lokiSeedKey, Hex.toStringCondensed(seed))
IdentityKeyUtil.save(this, IdentityKeyUtil.IDENTITY_PUBLIC_KEY_PREF, Base64.encodeBytes(keyPair!!.publicKey.serialize()))
IdentityKeyUtil.save(this, IdentityKeyUtil.IDENTITY_PRIVATE_KEY_PREF, Base64.encodeBytes(keyPair!!.privateKey.serialize()))
val userHexEncodedPublicKey = keyPair!!.hexEncodedPublicKey
val registrationID = KeyHelper.generateRegistrationId(false)
TextSecurePreferences.setLocalRegistrationId(this, registrationID)
DatabaseFactory.getIdentityDatabase(this).saveIdentity(Address.fromSerialized(userHexEncodedPublicKey),
IdentityKeyUtil.getIdentityKeyPair(this).publicKey, IdentityDatabase.VerifiedStatus.VERIFIED,
true, System.currentTimeMillis(), true)
TextSecurePreferences.setLocalNumber(this, userHexEncodedPublicKey)
TextSecurePreferences.setHasSeenWelcomeScreen(this, true)
TextSecurePreferences.setPromptedPushRegistration(this, true)
val authorisation = PairingAuthorisation(hexEncodedPublicKey, userHexEncodedPublicKey).sign(PairingAuthorisation.Type.REQUEST, keyPair!!.privateKey.serialize())
if (authorisation == null) {
Log.d("Loki", "Failed to sign device link request.")
reset()
return Toast.makeText(application, "Couldn't link device.", Toast.LENGTH_SHORT).show()
}
val application = ApplicationContext.getInstance(this)
application.startLongPollingIfNeeded()
application.setUpP2PAPI()
application.setUpStorageAPIIfNeeded()
val linkDeviceDialog = LinkDeviceSlaveModeDialog()
linkDeviceDialog.delegate = this
linkDeviceDialog.show(supportFragmentManager, "Link Device Dialog")
AsyncTask.execute {
retryIfNeeded(8) {
sendPairingAuthorisationMessage(this@LandingActivity, authorisation.primaryDevicePublicKey, authorisation)
}
}
}
override fun onDeviceLinkRequestAuthorized(authorization: PairingAuthorisation) {
TextSecurePreferences.setMasterHexEncodedPublicKey(this, authorization.primaryDevicePublicKey)
val intent = Intent(this, HomeActivity::class.java)
show(intent)
finish()
}
override fun onDeviceLinkCanceled() {
reset()
}
private fun reset() {
IdentityKeyUtil.delete(this, IdentityKeyUtil.lokiSeedKey)
TextSecurePreferences.removeLocalNumber(this)
TextSecurePreferences.setHasSeenWelcomeScreen(this, false)
TextSecurePreferences.setPromptedPushRegistration(this, false)
}
}

View File

@ -0,0 +1,103 @@
package org.thoughtcrime.securesms.loki.redesign.activities
import android.content.Intent
import android.os.Bundle
import android.support.v4.app.Fragment
import android.support.v4.app.FragmentPagerAdapter
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.view.inputmethod.InputMethodManager
import android.widget.Toast
import kotlinx.android.synthetic.main.activity_link_device.*
import kotlinx.android.synthetic.main.fragment_enter_session_id.*
import network.loki.messenger.R
import org.thoughtcrime.securesms.BaseActionBarActivity
import org.thoughtcrime.securesms.loki.redesign.fragments.ScanQRCodeWrapperFragment
import org.thoughtcrime.securesms.loki.redesign.fragments.ScanQRCodeWrapperFragmentDelegate
import org.whispersystems.signalservice.loki.utilities.PublicKeyValidation
class LinkDeviceActivity : BaseActionBarActivity(), ScanQRCodeWrapperFragmentDelegate {
private val adapter = LinkDeviceActivityAdapter(this)
// region Lifecycle
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
// Set content view
setContentView(R.layout.activity_link_device)
// Set title
supportActionBar!!.title = "Link Device"
// Set up view pager
viewPager.adapter = adapter
tabLayout.setupWithViewPager(viewPager)
}
// endregion
// region Interaction
override fun handleQRCodeScanned(hexEncodedPublicKey: String) {
requestDeviceLinkIfPossible(hexEncodedPublicKey)
}
fun requestDeviceLinkIfPossible(hexEncodedPublicKey: String) {
if (!PublicKeyValidation.isValid(hexEncodedPublicKey)) {
Toast.makeText(this, "Invalid Session ID", Toast.LENGTH_SHORT).show()
} else {
val intent = Intent()
intent.putExtra("hexEncodedPublicKey", hexEncodedPublicKey)
setResult(RESULT_OK, intent)
finish()
}
}
// endregion
}
// region Adapter
private class LinkDeviceActivityAdapter(val activity: LinkDeviceActivity) : FragmentPagerAdapter(activity.supportFragmentManager) {
override fun getCount(): Int {
return 2
}
override fun getItem(index: Int): Fragment {
return when (index) {
0 -> EnterSessionIDFragment()
1 -> {
val result = ScanQRCodeWrapperFragment()
result.delegate = activity
result.message = "Link to your existing account by going into your in-app settings and clicking \"Linked Devices\""
result
}
else -> throw IllegalStateException()
}
}
override fun getPageTitle(index: Int): CharSequence? {
return when (index) {
0 -> "Enter Session ID"
1 -> "Scan QR Code"
else -> throw IllegalStateException()
}
}
}
// endregion
// region Enter Session ID Fragment
class EnterSessionIDFragment : Fragment() {
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View {
return inflater.inflate(R.layout.fragment_enter_session_id, container, false)
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
requestDeviceLinkButton.setOnClickListener { requestDeviceLinkIfPossible() }
}
private fun requestDeviceLinkIfPossible() {
val inputMethodManager = context!!.getSystemService(BaseActionBarActivity.INPUT_METHOD_SERVICE) as InputMethodManager
inputMethodManager.hideSoftInputFromWindow(sessionIDEditText.windowToken, 0)
val hexEncodedPublicKey = sessionIDEditText.text.trim().toString().toLowerCase()
(activity!! as LinkDeviceActivity).requestDeviceLinkIfPossible(hexEncodedPublicKey)
}
}
// endregion

View File

@ -124,7 +124,7 @@ class ViewMyQRCodeFragment : Fragment() {
val size = toPx(280, resources)
val qrCode = QRCodeUtilities.encode(hexEncodedPublicKey, size)
qrCodeImageView.setImageBitmap(qrCode)
val explanation = SpannableStringBuilder("This is your unique public QR code. Other users may scan this in order to begin a conversation with you.")
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)
explanationTextView.text = explanation
shareButton.setOnClickListener { shareQRCode() }

View File

@ -80,6 +80,13 @@ class SettingsActivity : PassphraseRequiredActionBarActivity() {
publicKeyTextView.text = hexEncodedPublicKey
copyButton.setOnClickListener { copyPublicKey() }
shareButton.setOnClickListener { sharePublicKey() }
val isMasterDevice = (TextSecurePreferences.getMasterHexEncodedPublicKey(this) == null)
if (!isMasterDevice) {
linkedDevicesButtonTopSeparator.visibility = View.GONE
linkedDevicesButton.visibility = View.GONE
seedButtonTopSeparator.visibility = View.GONE
seedButton.visibility = View.GONE
}
seedButton.setOnClickListener { showSeed() }
clearAllDataButton.setOnClickListener { clearAllData() }
}
@ -241,7 +248,7 @@ class SettingsActivity : PassphraseRequiredActionBarActivity() {
}
private fun showSeed() {
SeedDialog().show(supportFragmentManager, "Seed Dialog")
SeedDialog().show(supportFragmentManager, "Recovery Phrase Dialog")
}
private fun clearAllData() {

View File

@ -0,0 +1,76 @@
package org.thoughtcrime.securesms.loki.redesign.dialogs
import android.app.Dialog
import android.graphics.Color
import android.graphics.drawable.ColorDrawable
import android.os.Bundle
import android.os.Handler
import android.support.v4.app.DialogFragment
import android.support.v7.app.AlertDialog
import android.view.LayoutInflater
import android.view.View
import android.widget.LinearLayout
import kotlinx.android.synthetic.main.dialog_link_device_slave_mode.view.*
import network.loki.messenger.R
import org.thoughtcrime.securesms.loki.MnemonicUtilities
import org.thoughtcrime.securesms.util.TextSecurePreferences
import org.thoughtcrime.securesms.util.Util
import org.whispersystems.signalservice.loki.api.DeviceLinkingSession
import org.whispersystems.signalservice.loki.api.DeviceLinkingSessionListener
import org.whispersystems.signalservice.loki.api.PairingAuthorisation
import org.whispersystems.signalservice.loki.crypto.MnemonicCodec
class LinkDeviceSlaveModeDialog : DialogFragment(), DeviceLinkingSessionListener {
private val languageFileDirectory by lazy { MnemonicUtilities.getLanguageFileDirectory(context!!) }
private lateinit var contentView: View
private var authorization: PairingAuthorisation? = null
var delegate: LinkDeviceSlaveModeDialogDelegate? = null
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
val builder = AlertDialog.Builder(context!!)
contentView = LayoutInflater.from(context!!).inflate(R.layout.dialog_link_device_slave_mode, null)
val hexEncodedPublicKey = TextSecurePreferences.getLocalNumber(context)
contentView.mnemonicTextView.text = MnemonicUtilities.getFirst3Words(MnemonicCodec(languageFileDirectory), hexEncodedPublicKey)
contentView.cancelButton.setOnClickListener { onDeviceLinkCanceled() }
builder.setView(contentView)
DeviceLinkingSession.shared.startListeningForLinkingRequests()
DeviceLinkingSession.shared.addListener(this)
val result = builder.create()
result.window?.setBackgroundDrawable(ColorDrawable(Color.TRANSPARENT))
return result
}
override fun onDeviceLinkRequestAuthorized(authorization: PairingAuthorisation) {
if (authorization.type != PairingAuthorisation.Type.GRANT || authorization.secondaryDevicePublicKey != TextSecurePreferences.getLocalNumber(context!!) || this.authorization != null) { return }
Util.runOnMain {
this.authorization = authorization
DeviceLinkingSession.shared.stopListeningForLinkingRequests()
DeviceLinkingSession.shared.removeListener(this)
contentView.spinner.visibility = View.GONE
val titleTextViewLayoutParams = contentView.titleTextView.layoutParams as LinearLayout.LayoutParams
titleTextViewLayoutParams.topMargin = 0
contentView.titleTextView.layoutParams = titleTextViewLayoutParams
contentView.titleTextView.text = "Device Link Authorized"
contentView.explanationTextView.text = "Your device has been linked successfully"
contentView.mnemonicTextView.visibility = View.GONE
contentView.cancelButton.visibility = View.GONE
Handler().postDelayed({
dismiss()
delegate?.onDeviceLinkRequestAuthorized(authorization)
}, 4000)
}
}
private fun onDeviceLinkCanceled() {
DeviceLinkingSession.shared.stopListeningForLinkingRequests()
DeviceLinkingSession.shared.removeListener(this)
dismiss()
delegate?.onDeviceLinkCanceled()
}
}
interface LinkDeviceSlaveModeDialogDelegate {
fun onDeviceLinkRequestAuthorized(authorization: PairingAuthorisation)
fun onDeviceLinkCanceled()
}

View File

@ -21,7 +21,20 @@ fun AppCompatActivity.setUpActionBarSessionLogo() {
supportActionBar!!.setDisplayShowCustomEnabled(true)
}
fun AppCompatActivity.push(intent: Intent) {
startActivity(intent)
fun AppCompatActivity.push(intent: Intent, isForResult: Boolean = false) {
if (isForResult) {
startActivityForResult(intent, 42)
} else {
startActivity(intent)
}
overridePendingTransition(R.anim.slide_from_right, R.anim.fade_scale_out)
}
fun AppCompatActivity.show(intent: Intent, isForResult: Boolean = false) {
if (isForResult) {
startActivityForResult(intent, 42)
} else {
startActivity(intent)
}
overridePendingTransition(R.anim.slide_from_bottom, R.anim.fade_scale_out)
}