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" />
</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_marginLeft="64dp" />
<org.thoughtcrime.securesms.loki.views.ProfilePictureView
android:id="@+id/pathStatusView"
<RelativeLayout
android:id="@+id/pathStatusViewContainer"
android:layout_width="@dimen/small_profile_picture_size"
android:layout_height="@dimen/small_profile_picture_size"
android:background="@color/red"
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>

View File

@ -3,6 +3,7 @@
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_session_background"
android:orientation="vertical"
android:gravity="center">
@ -32,6 +33,14 @@
android:orientation="vertical"
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>
<Button

View File

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

View File

@ -33,6 +33,7 @@
<dimen name="dialog_corner_radius">8dp</dimen>
<dimen name="dialog_button_corner_radius">4dp</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_dot_size">8dp</dimen>
<dimen name="path_row_expanded_dot_size">16dp</dimen>

View File

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

View File

@ -1,10 +1,13 @@
package org.thoughtcrime.securesms.loki.activities
import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import android.content.IntentFilter
import android.net.Uri
import android.os.Bundle
import android.os.Handler
import android.support.v4.content.LocalBroadcastManager
import android.util.AttributeSet
import android.util.TypedValue
import android.view.Gravity
@ -19,35 +22,89 @@ import kotlinx.android.synthetic.main.activity_path.*
import network.loki.messenger.R
import org.thoughtcrime.securesms.PassphraseRequiredActionBarActivity
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.whispersystems.signalservice.loki.api.onionrequests.OnionRequestAPI
import org.whispersystems.signalservice.loki.api.onionrequests.Snode
class PathActivity : PassphraseRequiredActionBarActivity() {
private val broadcastReceivers = mutableListOf<BroadcastReceiver>()
// region Lifecycle
override fun onCreate(savedInstanceState: Bundle?, isReady: Boolean) {
super.onCreate(savedInstanceState, isReady)
setContentView(R.layout.activity_path)
supportActionBar!!.title = resources.getString(R.string.activity_path_title)
val youRow = getPathRow("You", null, LineView.Location.Top, 1000, 4000)
val path = OnionRequestAPI.paths.firstOrNull() ?: return finish()
val pathRows = path.mapIndexed { index, snode ->
val isGuardSnode = (OnionRequestAPI.guardSnodes.contains(snode))
getPathRow(snode, LineView.Location.Middle, index.toLong() * 1000 + 2000, 4000, isGuardSnode)
rebuildPathButton.setOnClickListener { rebuildPath() }
update(false)
registerObservers()
}
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)
pathRowsContainer.addView(youRow)
for (pathRow in pathRows) {
pathRowsContainer.addView(pathRow)
broadcastReceivers.add(buildingPathsReceiver)
LocalBroadcastManager.getInstance(this).registerReceiver(buildingPathsReceiver, IntentFilter("buildingPaths"))
val pathsBuiltReceiver: BroadcastReceiver = object : BroadcastReceiver() {
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 {
menuInflater.inflate(R.menu.menu_path, menu)
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
// region General
@ -108,6 +165,12 @@ class PathActivity : PassphraseRequiredActionBarActivity() {
Toast.makeText(this, R.string.invalid_url, Toast.LENGTH_SHORT).show()
}
}
private fun rebuildPath() {
OnionRequestAPI.guardSnodes = setOf()
OnionRequestAPI.paths = listOf()
OnionRequestAPI.buildPaths()
}
// endregion
// region Line View

View File

@ -1,7 +1,5 @@
package org.thoughtcrime.securesms.loki.activities
import android.animation.Animator
import android.animation.AnimatorListenerAdapter
import android.app.Activity
import android.content.ClipData
import android.content.ClipboardManager
@ -31,6 +29,8 @@ import org.thoughtcrime.securesms.database.Address
import org.thoughtcrime.securesms.database.DatabaseFactory
import org.thoughtcrime.securesms.loki.dialogs.ClearAllDataDialog
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.toPx
import org.thoughtcrime.securesms.mms.GlideApp
@ -147,7 +147,7 @@ class SettingsActivity : PassphraseRequiredActionBarActivity() {
}
private fun updateProfile(isUpdatingProfilePicture: Boolean) {
showLoader()
loader.fadeIn()
val promises = mutableListOf<Promise<*, Exception>>()
val displayName = displayNameToBeUploaded
if (displayName != null) {
@ -187,24 +187,9 @@ class SettingsActivity : PassphraseRequiredActionBarActivity() {
profilePictureView.update()
}
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
// region Interaction

View File

@ -1,5 +1,7 @@
package org.thoughtcrime.securesms.loki.utilities
import android.animation.Animator
import android.animation.AnimatorListenerAdapter
import android.animation.FloatEvaluator
import android.animation.ValueAnimator
import android.graphics.PointF
@ -31,4 +33,19 @@ fun View.animateSizeChange(@DimenRes startSizeID: Int, @DimenRes endSizeID: Int,
this.layoutParams = layoutParams
}
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)
}
}
}