Implement remaining onion request UI

This commit is contained in:
nielsandriesse 2020-05-29 11:16:52 +10:00
parent 326b5a9475
commit 3a646476ff
11 changed files with 205 additions and 36 deletions

View File

@ -5,4 +5,4 @@
<solid android:color="@color/accent" /> <solid android:color="@color/accent" />
</shape> </shape>

View File

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<shape
xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="oval">
<solid android:color="@color/paths_building" />
</shape>

View File

@ -40,13 +40,21 @@
android:layout_centerVertical="true" android:layout_centerVertical="true"
android:layout_marginLeft="64dp" /> android:layout_marginLeft="64dp" />
<org.thoughtcrime.securesms.loki.views.ProfilePictureView <RelativeLayout
android:id="@+id/pathStatusView" android:id="@+id/pathStatusViewContainer"
android:layout_width="@dimen/small_profile_picture_size" android:layout_width="@dimen/small_profile_picture_size"
android:layout_height="@dimen/small_profile_picture_size" android:layout_height="@dimen/small_profile_picture_size"
android:background="@color/red"
android:layout_alignParentRight="true" android:layout_alignParentRight="true"
android:layout_centerVertical="true" /> android:layout_centerVertical="true" >
<org.thoughtcrime.securesms.loki.views.PathStatusView
android:layout_width="@dimen/path_status_view_size"
android:layout_height="@dimen/path_status_view_size"
android:layout_centerVertical="true"
android:layout_alignParentRight="true"
android:layout_marginRight="8dp" />
</RelativeLayout>
</RelativeLayout> </RelativeLayout>

View File

@ -3,6 +3,7 @@
xmlns:android="http://schemas.android.com/apk/res/android" xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:background="@drawable/default_session_background" android:background="@drawable/default_session_background"
android:orientation="vertical" android:orientation="vertical"
android:gravity="center"> android:gravity="center">
@ -32,6 +33,14 @@
android:orientation="vertical" android:orientation="vertical"
android:layout_centerInParent="true" /> android:layout_centerInParent="true" />
<com.github.ybq.android.spinkit.SpinKitView
style="@style/SpinKitView.Large.ThreeBounce"
android:id="@+id/spinner"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
app:SpinKit_Color="@color/text" />
</RelativeLayout> </RelativeLayout>
<Button <Button

View File

@ -28,6 +28,7 @@
<color name="new_conversation_button_collapsed_background">#1F1F1F</color> <color name="new_conversation_button_collapsed_background">#1F1F1F</color>
<color name="pn_option_background">#1B1B1B</color> <color name="pn_option_background">#1B1B1B</color>
<color name="pn_option_border">#212121</color> <color name="pn_option_border">#212121</color>
<color name="paths_building">#FFCE3A</color>
<!-- Session --> <!-- Session -->
<!-- Loki --> <!-- Loki -->

View File

@ -33,6 +33,7 @@
<dimen name="dialog_corner_radius">8dp</dimen> <dimen name="dialog_corner_radius">8dp</dimen>
<dimen name="dialog_button_corner_radius">4dp</dimen> <dimen name="dialog_button_corner_radius">4dp</dimen>
<dimen name="pn_option_corner_radius">8dp</dimen> <dimen name="pn_option_corner_radius">8dp</dimen>
<dimen name="path_status_view_size">8dp</dimen>
<dimen name="path_row_height">56dp</dimen> <dimen name="path_row_height">56dp</dimen>
<dimen name="path_row_dot_size">8dp</dimen> <dimen name="path_row_dot_size">8dp</dimen>
<dimen name="path_row_expanded_dot_size">16dp</dimen> <dimen name="path_row_expanded_dot_size">16dp</dimen>

View File

@ -101,7 +101,7 @@ class HomeActivity : PassphraseRequiredActionBarActivity, ConversationClickListe
profileButton.hexEncodedPublicKey = hexEncodedPublicKey profileButton.hexEncodedPublicKey = hexEncodedPublicKey
profileButton.update() profileButton.update()
profileButton.setOnClickListener { openSettings() } profileButton.setOnClickListener { openSettings() }
pathStatusView.setOnClickListener { showPath() } pathStatusViewContainer.setOnClickListener { showPath() }
// Set up seed reminder view // Set up seed reminder view
val isMasterDevice = (TextSecurePreferences.getMasterHexEncodedPublicKey(this) == null) val isMasterDevice = (TextSecurePreferences.getMasterHexEncodedPublicKey(this) == null)
val hasViewedSeed = TextSecurePreferences.getHasViewedSeed(this) val hasViewedSeed = TextSecurePreferences.getHasViewedSeed(this)

View File

@ -1,10 +1,13 @@
package org.thoughtcrime.securesms.loki.activities package org.thoughtcrime.securesms.loki.activities
import android.content.BroadcastReceiver
import android.content.Context import android.content.Context
import android.content.Intent import android.content.Intent
import android.content.IntentFilter
import android.net.Uri import android.net.Uri
import android.os.Bundle import android.os.Bundle
import android.os.Handler import android.os.Handler
import android.support.v4.content.LocalBroadcastManager
import android.util.AttributeSet import android.util.AttributeSet
import android.util.TypedValue import android.util.TypedValue
import android.view.Gravity import android.view.Gravity
@ -19,35 +22,89 @@ import kotlinx.android.synthetic.main.activity_path.*
import network.loki.messenger.R import network.loki.messenger.R
import org.thoughtcrime.securesms.PassphraseRequiredActionBarActivity import org.thoughtcrime.securesms.PassphraseRequiredActionBarActivity
import org.thoughtcrime.securesms.loki.utilities.animateSizeChange import org.thoughtcrime.securesms.loki.utilities.animateSizeChange
import org.thoughtcrime.securesms.loki.utilities.fadeIn
import org.thoughtcrime.securesms.loki.utilities.fadeOut
import org.thoughtcrime.securesms.loki.utilities.getColorWithID import org.thoughtcrime.securesms.loki.utilities.getColorWithID
import org.whispersystems.signalservice.loki.api.onionrequests.OnionRequestAPI import org.whispersystems.signalservice.loki.api.onionrequests.OnionRequestAPI
import org.whispersystems.signalservice.loki.api.onionrequests.Snode import org.whispersystems.signalservice.loki.api.onionrequests.Snode
class PathActivity : PassphraseRequiredActionBarActivity() { class PathActivity : PassphraseRequiredActionBarActivity() {
private val broadcastReceivers = mutableListOf<BroadcastReceiver>()
// region Lifecycle // region Lifecycle
override fun onCreate(savedInstanceState: Bundle?, isReady: Boolean) { override fun onCreate(savedInstanceState: Bundle?, isReady: Boolean) {
super.onCreate(savedInstanceState, isReady) super.onCreate(savedInstanceState, isReady)
setContentView(R.layout.activity_path) setContentView(R.layout.activity_path)
supportActionBar!!.title = resources.getString(R.string.activity_path_title) supportActionBar!!.title = resources.getString(R.string.activity_path_title)
val youRow = getPathRow("You", null, LineView.Location.Top, 1000, 4000) rebuildPathButton.setOnClickListener { rebuildPath() }
val path = OnionRequestAPI.paths.firstOrNull() ?: return finish() update(false)
val pathRows = path.mapIndexed { index, snode -> registerObservers()
val isGuardSnode = (OnionRequestAPI.guardSnodes.contains(snode)) }
getPathRow(snode, LineView.Location.Middle, index.toLong() * 1000 + 2000, 4000, isGuardSnode)
private fun registerObservers() {
val buildingPathsReceiver: BroadcastReceiver = object : BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent) {
handleBuildingPathsEvent()
}
} }
val destinationRow = getPathRow("Destination", null, LineView.Location.Bottom, 5000, 4000) broadcastReceivers.add(buildingPathsReceiver)
pathRowsContainer.addView(youRow) LocalBroadcastManager.getInstance(this).registerReceiver(buildingPathsReceiver, IntentFilter("buildingPaths"))
for (pathRow in pathRows) { val pathsBuiltReceiver: BroadcastReceiver = object : BroadcastReceiver() {
pathRowsContainer.addView(pathRow)
override fun onReceive(context: Context, intent: Intent) {
handlePathsBuiltEvent()
}
} }
pathRowsContainer.addView(destinationRow) broadcastReceivers.add(pathsBuiltReceiver)
LocalBroadcastManager.getInstance(this).registerReceiver(pathsBuiltReceiver, IntentFilter("pathsBuilt"))
} }
override fun onCreateOptionsMenu(menu: Menu?): Boolean { override fun onCreateOptionsMenu(menu: Menu?): Boolean {
menuInflater.inflate(R.menu.menu_path, menu) menuInflater.inflate(R.menu.menu_path, menu)
return true return true
} }
override fun onDestroy() {
for (receiver in broadcastReceivers) {
LocalBroadcastManager.getInstance(this).unregisterReceiver(receiver)
}
super.onDestroy()
}
// endregion
// region Updating
private fun handleBuildingPathsEvent() { update(false) }
private fun handlePathsBuiltEvent() { update(false) }
private fun update(isAnimated: Boolean) {
pathRowsContainer.removeAllViews()
if (OnionRequestAPI.paths.count() >= OnionRequestAPI.pathCount) {
val path = OnionRequestAPI.paths.firstOrNull() ?: return finish()
val dotAnimationRepeatInterval = path.count().toLong() * 1000 + 2000
val pathRows = path.mapIndexed { index, snode ->
val isGuardSnode = (OnionRequestAPI.guardSnodes.contains(snode))
getPathRow(snode, LineView.Location.Middle, index.toLong() * 1000 + 2000, dotAnimationRepeatInterval, isGuardSnode)
}
val youRow = getPathRow("You", null, LineView.Location.Top, 1000, dotAnimationRepeatInterval)
val destinationRow = getPathRow("Destination", null, LineView.Location.Bottom, path.count().toLong() * 1000 + 2000, dotAnimationRepeatInterval)
val rows = listOf( youRow ) + pathRows + listOf( destinationRow )
for (row in rows) {
pathRowsContainer.addView(row)
}
if (isAnimated) {
spinner.fadeOut()
} else {
spinner.alpha = 0.0f
}
} else {
if (isAnimated) {
spinner.fadeIn()
} else {
spinner.alpha = 1.0f
}
}
}
// endregion // endregion
// region General // region General
@ -108,6 +165,12 @@ class PathActivity : PassphraseRequiredActionBarActivity() {
Toast.makeText(this, R.string.invalid_url, Toast.LENGTH_SHORT).show() Toast.makeText(this, R.string.invalid_url, Toast.LENGTH_SHORT).show()
} }
} }
private fun rebuildPath() {
OnionRequestAPI.guardSnodes = setOf()
OnionRequestAPI.paths = listOf()
OnionRequestAPI.buildPaths()
}
// endregion // endregion
// region Line View // region Line View

View File

@ -1,7 +1,5 @@
package org.thoughtcrime.securesms.loki.activities package org.thoughtcrime.securesms.loki.activities
import android.animation.Animator
import android.animation.AnimatorListenerAdapter
import android.app.Activity import android.app.Activity
import android.content.ClipData import android.content.ClipData
import android.content.ClipboardManager import android.content.ClipboardManager
@ -31,6 +29,8 @@ import org.thoughtcrime.securesms.database.Address
import org.thoughtcrime.securesms.database.DatabaseFactory import org.thoughtcrime.securesms.database.DatabaseFactory
import org.thoughtcrime.securesms.loki.dialogs.ClearAllDataDialog import org.thoughtcrime.securesms.loki.dialogs.ClearAllDataDialog
import org.thoughtcrime.securesms.loki.dialogs.SeedDialog import org.thoughtcrime.securesms.loki.dialogs.SeedDialog
import org.thoughtcrime.securesms.loki.utilities.fadeIn
import org.thoughtcrime.securesms.loki.utilities.fadeOut
import org.thoughtcrime.securesms.loki.utilities.push import org.thoughtcrime.securesms.loki.utilities.push
import org.thoughtcrime.securesms.loki.utilities.toPx import org.thoughtcrime.securesms.loki.utilities.toPx
import org.thoughtcrime.securesms.mms.GlideApp import org.thoughtcrime.securesms.mms.GlideApp
@ -147,7 +147,7 @@ class SettingsActivity : PassphraseRequiredActionBarActivity() {
} }
private fun updateProfile(isUpdatingProfilePicture: Boolean) { private fun updateProfile(isUpdatingProfilePicture: Boolean) {
showLoader() loader.fadeIn()
val promises = mutableListOf<Promise<*, Exception>>() val promises = mutableListOf<Promise<*, Exception>>()
val displayName = displayNameToBeUploaded val displayName = displayNameToBeUploaded
if (displayName != null) { if (displayName != null) {
@ -187,24 +187,9 @@ class SettingsActivity : PassphraseRequiredActionBarActivity() {
profilePictureView.update() profilePictureView.update()
} }
profilePictureToBeUploaded = null profilePictureToBeUploaded = null
hideLoader() loader.fadeOut()
} }
} }
private fun showLoader() {
loader.visibility = View.VISIBLE
loader.animate().setDuration(150).alpha(1.0f).start()
}
private fun hideLoader() {
loader.animate().setDuration(150).alpha(0.0f).setListener(object : AnimatorListenerAdapter() {
override fun onAnimationEnd(animation: Animator?) {
super.onAnimationEnd(animation)
loader.visibility = View.GONE
}
})
}
// endregion // endregion
// region Interaction // region Interaction

View File

@ -1,5 +1,7 @@
package org.thoughtcrime.securesms.loki.utilities package org.thoughtcrime.securesms.loki.utilities
import android.animation.Animator
import android.animation.AnimatorListenerAdapter
import android.animation.FloatEvaluator import android.animation.FloatEvaluator
import android.animation.ValueAnimator import android.animation.ValueAnimator
import android.graphics.PointF import android.graphics.PointF
@ -31,4 +33,19 @@ fun View.animateSizeChange(@DimenRes startSizeID: Int, @DimenRes endSizeID: Int,
this.layoutParams = layoutParams this.layoutParams = layoutParams
} }
animation.start() animation.start()
} }
fun View.fadeIn(duration: Long = 150) {
visibility = View.VISIBLE
animate().setDuration(duration).alpha(1.0f).start()
}
fun View.fadeOut(duration: Long = 150) {
animate().setDuration(duration).alpha(0.0f).setListener(object : AnimatorListenerAdapter() {
override fun onAnimationEnd(animation: Animator?) {
super.onAnimationEnd(animation)
visibility = View.GONE
}
})
}

View File

@ -0,0 +1,77 @@
package org.thoughtcrime.securesms.loki.views
import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import android.content.IntentFilter
import android.support.v4.content.LocalBroadcastManager
import android.util.AttributeSet
import android.view.View
import network.loki.messenger.R
import org.whispersystems.signalservice.loki.api.onionrequests.OnionRequestAPI
class PathStatusView : View {
private val broadcastReceivers = mutableListOf<BroadcastReceiver>()
constructor(context: Context) : super(context) {
initialize()
}
constructor(context: Context, attrs: AttributeSet) : super(context, attrs) {
initialize()
}
constructor(context: Context, attrs: AttributeSet, defStyleAttr: Int) : super(context, attrs, defStyleAttr) {
initialize()
}
constructor(context: Context, attrs: AttributeSet, defStyleAttr: Int, defStyleRes: Int) : super(context, attrs, defStyleAttr, defStyleRes) {
initialize()
}
private fun initialize() {
update()
}
override fun onAttachedToWindow() {
super.onAttachedToWindow()
registerObservers()
}
private fun registerObservers() {
val buildingPathsReceiver: BroadcastReceiver = object : BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent) {
handleBuildingPathsEvent()
}
}
broadcastReceivers.add(buildingPathsReceiver)
LocalBroadcastManager.getInstance(context).registerReceiver(buildingPathsReceiver, IntentFilter("buildingPaths"))
val pathsBuiltReceiver: BroadcastReceiver = object : BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent) {
handlePathsBuiltEvent()
}
}
broadcastReceivers.add(pathsBuiltReceiver)
LocalBroadcastManager.getInstance(context).registerReceiver(pathsBuiltReceiver, IntentFilter("pathsBuilt"))
}
override fun onDetachedFromWindow() {
for (receiver in broadcastReceivers) {
LocalBroadcastManager.getInstance(context).unregisterReceiver(receiver)
}
super.onDetachedFromWindow()
}
private fun handleBuildingPathsEvent() { update() }
private fun handlePathsBuiltEvent() { update() }
private fun update() {
if (OnionRequestAPI.paths.count() >= OnionRequestAPI.pathCount) {
setBackgroundResource(R.drawable.accent_dot)
} else {
setBackgroundResource(R.drawable.paths_building_dot)
}
}
}