Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Add peer screen #23

Merged
merged 3 commits into from
Apr 5, 2021
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions app/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,7 @@ dependencies {
androidTestImplementation 'androidx.test.ext:junit:1.1.2'
androidTestImplementation "org.jetbrains.kotlinx:kotlinx-coroutines-test:$kotlinCoroutinesVersion"
androidTestImplementation 'androidx.test.espresso:espresso-core:3.3.0'
androidTestImplementation 'androidx.test.espresso:espresso-intents:3.3.0'
androidTestImplementation('com.schibsted.spain:barista:3.7.0') {
exclude group: 'org.jetbrains.kotlin'
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
package tech.relaycorp.ping.test

import android.app.Activity
import androidx.test.platform.app.InstrumentationRegistry
import androidx.test.runner.lifecycle.ActivityLifecycleMonitorRegistry
import androidx.test.runner.lifecycle.Stage
import org.junit.Assert
import tech.relaycorp.ping.test.WaitAssertions.waitFor
import kotlin.reflect.KClass

object ActivityAssertions {
val currentActivity: Activity?
get() {
InstrumentationRegistry.getInstrumentation().waitForIdleSync()
val activity = arrayOfNulls<Activity>(1)
InstrumentationRegistry.getInstrumentation().runOnMainSync {
val activities =
ActivityLifecycleMonitorRegistry.getInstance()
.getActivitiesInStage(Stage.RESUMED)
if (activities.iterator().hasNext()) {
activity[0] = activities.iterator().next()
}
}
return activity[0]
}

fun assertCurrentActivity(activityKlass: KClass<out Activity>) {
Assert.assertEquals(activityKlass.java.name, currentActivity?.componentName?.className)
}

fun waitForCurrentActivityToBe(activityKlass: KClass<out Activity>) {
waitFor { assertCurrentActivity(activityKlass) }
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@ import tech.relaycorp.ping.AppModule
import tech.relaycorp.ping.awala.AwalaModule
import tech.relaycorp.ping.common.di.AppComponent
import tech.relaycorp.ping.ui.main.MainActivityTest
import tech.relaycorp.ping.ui.peer.PeerActivityTest
import tech.relaycorp.ping.ui.peers.AddPublicPeerActivityTest
import tech.relaycorp.ping.ui.peers.PeerActivityTest
import tech.relaycorp.ping.ui.peers.PeersActivityTest
import tech.relaycorp.ping.ui.ping.PingActivityTest
import javax.inject.Singleton
Expand All @@ -21,6 +22,7 @@ interface AppTestComponent : AppComponent {

// Tests

fun inject(test: AddPublicPeerActivityTest)
fun inject(test: MainActivityTest)
fun inject(test: PeerActivityTest)
fun inject(test: PeersActivityTest)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ class BaseActivityTestRule<T : Activity>(
private val clearPreferencesRule: ClearPreferencesRule = ClearPreferencesRule()
private val clearDatabaseRule: ClearTestDatabaseRule = ClearTestDatabaseRule()
private val clearFilesRule: ClearFilesRule = ClearFilesRule()
private val intentsRule: IntentsRule = IntentsRule()
private val activityTestRule: ActivityTestRule<T> = ActivityTestRule(
activityClass.java,
true,
Expand All @@ -30,6 +31,7 @@ class BaseActivityTestRule<T : Activity>(
.around(clearPreferencesRule)
.around(clearDatabaseRule)
.around(clearFilesRule)
.around(intentsRule)
.apply(base, description)
}

Expand Down
22 changes: 22 additions & 0 deletions app/src/androidTest/java/tech/relaycorp/ping/test/IntentsRule.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package tech.relaycorp.ping.test

import androidx.test.espresso.intent.Intents
import org.junit.rules.TestRule
import org.junit.runner.Description
import org.junit.runners.model.Statement

class IntentsRule : TestRule {

override fun apply(base: Statement, description: Description?) =
object : Statement() {
override fun evaluate() {
try {
Intents.init()
} catch (_: IllegalStateException) {
// Occasionally `Intents.init()` might be called twice before `Intents.release()` is called
}
base.evaluate()
Intents.release()
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
package tech.relaycorp.ping.ui.peers

import android.app.Activity
import android.app.Instrumentation
import android.content.Intent
import android.net.Uri
import androidx.test.espresso.intent.Intents.intending
import androidx.test.espresso.intent.matcher.IntentMatchers.hasAction
import androidx.test.ext.junit.runners.AndroidJUnit4
import com.schibsted.spain.barista.assertion.BaristaVisibilityAssertions.assertDisplayed
import com.schibsted.spain.barista.interaction.BaristaClickInteractions.clickOn
import com.schibsted.spain.barista.interaction.BaristaEditTextInteractions.writeTo
import org.junit.Before
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
import tech.relaycorp.ping.R
import tech.relaycorp.ping.test.ActivityAssertions.waitForCurrentActivityToBe
import tech.relaycorp.ping.test.AppTestProvider.component
import tech.relaycorp.ping.test.AppTestProvider.context
import tech.relaycorp.ping.test.BaseActivityTestRule

@RunWith(AndroidJUnit4::class)
class AddPublicPeerActivityTest {

@Rule
@JvmField
val testRule = BaseActivityTestRule(PeersActivity::class, false)

@Before
fun setUp() {
component.inject(this)
}

@Test
fun addPublicPeerSuccessfully() {
testRule.start()
clickOn(R.id.addPeer)
clickOn(R.string.peer_public)

val address = "ping.awala.services"
writeTo(R.id.addressEdit, address)

intending(hasAction(Intent.ACTION_OPEN_DOCUMENT))
.respondWith(
Instrumentation.ActivityResult(
Activity.RESULT_OK,
Intent().setData(
Uri.parse("android.resource://${context.packageName}/${R.raw.ping_awala_identity}")
)
)
)
clickOn(R.string.peer_certificate_button)

clickOn(R.id.save)

waitForCurrentActivityToBe(PeersActivity::class)
assertDisplayed(address)
}

@Test
fun addPublicPeerMissingCertificate() {
testRule.start()
clickOn(R.id.addPeer)
clickOn(R.string.peer_public)

val address = "ping.awala.services"
writeTo(R.id.addressEdit, address)

clickOn(R.id.save)
assertDisplayed(R.string.peer_add_missing_certificate)
}
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package tech.relaycorp.ping.ui.peer
package tech.relaycorp.ping.ui.peers

import androidx.test.ext.junit.runners.AndroidJUnit4
import com.schibsted.spain.barista.assertion.BaristaVisibilityAssertions.assertDisplayed
Expand All @@ -18,6 +18,7 @@ import tech.relaycorp.ping.test.BaseActivityTestRule
import tech.relaycorp.ping.test.PublicPeerEntityFactory
import tech.relaycorp.ping.test.WaitAssertions.suspendWaitFor
import tech.relaycorp.ping.test.WaitAssertions.waitFor
import tech.relaycorp.ping.ui.peers.PeerActivity
import javax.inject.Inject

@RunWith(AndroidJUnit4::class)
Expand Down
6 changes: 5 additions & 1 deletion app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,11 @@
android:name=".ui.peers.PeersActivity"
android:label="@string/peers" />
<activity
android:name=".ui.peer.PeerActivity"
android:name=".ui.peers.PeerActivity"
android:label="@string/peer" />
<activity
android:name=".ui.peers.AddPublicPeerActivity"
android:label="@string/peer_public"
android:windowSoftInputMode="adjustResize" />
</application>
</manifest>
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,8 @@ package tech.relaycorp.ping.common.di
import dagger.Subcomponent
import tech.relaycorp.gateway.common.di.PerActivity
import tech.relaycorp.ping.ui.main.MainActivity
import tech.relaycorp.ping.ui.peer.PeerActivity
import tech.relaycorp.ping.ui.peers.AddPublicPeerActivity
import tech.relaycorp.ping.ui.peers.PeerActivity
import tech.relaycorp.ping.ui.peers.PeersActivity
import tech.relaycorp.ping.ui.ping.PingActivity

Expand All @@ -13,6 +14,7 @@ interface ActivityComponent {

// Activities

fun inject(activity: AddPublicPeerActivity)
fun inject(activity: MainActivity)
fun inject(activity: PeerActivity)
fun inject(activity: PeersActivity)
Expand Down
30 changes: 30 additions & 0 deletions app/src/main/java/tech/relaycorp/ping/data/ReadFile.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package tech.relaycorp.ping.data

import android.content.Context
import android.net.Uri
import android.provider.OpenableColumns
import javax.inject.Inject

class ReadFile
@Inject constructor(
private val context: Context
) {

fun read(uri: Uri) =
context.contentResolver.openInputStream(uri)?.use {
it.readBytes()
} ?: ByteArray(0)

fun getFileName(uri: Uri): String {
if (uri.scheme.equals("content")) {
context.contentResolver
.query(uri, null, null, null, null)
?.use { cursor ->
if (cursor.moveToFirst()) {
return cursor.getString(cursor.getColumnIndex(OpenableColumns.DISPLAY_NAME))
}
}
}
return uri.path?.split("/")?.lastOrNull() ?: ""
}
}
10 changes: 9 additions & 1 deletion app/src/main/java/tech/relaycorp/ping/domain/AddPublicPeer.kt
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package tech.relaycorp.ping.domain

import tech.relaycorp.awaladroid.endpoint.InvalidThirdPartyEndpoint
import tech.relaycorp.awaladroid.endpoint.PublicThirdPartyEndpoint
import tech.relaycorp.ping.data.database.dao.PublicPeerDao
import tech.relaycorp.ping.data.database.entity.PublicPeerEntity
Expand All @@ -10,8 +11,13 @@ class AddPublicPeer
private val publicPeerDao: PublicPeerDao
) {

@Throws(InvalidIdentityCertificate::class)
suspend fun add(address: String, identity: ByteArray): PublicThirdPartyEndpoint {
val endpoint = PublicThirdPartyEndpoint.import(address, identity)
val endpoint = try {
PublicThirdPartyEndpoint.import(address, identity)
} catch (e: InvalidThirdPartyEndpoint) {
throw InvalidIdentityCertificate(e)
}
publicPeerDao.save(
PublicPeerEntity(
privateAddress = endpoint.privateAddress,
Expand All @@ -21,3 +27,5 @@ class AddPublicPeer
return endpoint
}
}

class InvalidIdentityCertificate(cause: Throwable) : Exception(cause)
16 changes: 13 additions & 3 deletions app/src/main/java/tech/relaycorp/ping/ui/BaseActivity.kt
Original file line number Diff line number Diff line change
@@ -1,17 +1,19 @@
package tech.relaycorp.ping.ui

import android.os.Build
import android.content.Intent
import android.os.Bundle
import android.view.View
import android.view.WindowManager
import androidx.annotation.DrawableRes
import androidx.appcompat.app.AppCompatActivity
import androidx.core.view.WindowCompat
import dev.chrisbanes.insetter.applyInsetter
import kotlinx.android.synthetic.main.common_app_bar.*
import kotlinx.coroutines.channels.sendBlocking
import kotlinx.coroutines.flow.asFlow
import tech.relaycorp.gateway.ui.common.MessageManager
import tech.relaycorp.ping.App
import tech.relaycorp.ping.R
import tech.relaycorp.ping.common.PublishFlow
import tech.relaycorp.ping.ui.common.ActivityResult

abstract class BaseActivity : AppCompatActivity() {

Expand All @@ -20,13 +22,21 @@ abstract class BaseActivity : AppCompatActivity() {

protected val messageManager by lazy { MessageManager(this) }

protected val results get() = _results.asFlow()
private val _results = PublishFlow<ActivityResult>()

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)

// Setup edge-to-edge UI
WindowCompat.setDecorFitsSystemWindows(window, true)
}

override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
_results.sendBlocking(ActivityResult(requestCode, resultCode, data))
}

override fun setContentView(layoutResID: Int) {
super.setContentView(layoutResID)
toolbarTitle?.text = title
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package tech.relaycorp.ping.ui.common

import android.content.Intent

data class ActivityResult(
val requestCode: Int,
val resultCode: Int,
val data: Intent?
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package tech.relaycorp.ping.ui.common

import android.text.Editable
import android.text.TextWatcher

class SimpleTextWatcher(
private val textChanged: (String) -> Unit
) : TextWatcher {
override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) {
}

override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) {
textChanged(s?.toString() ?: "")
}

override fun afterTextChanged(s: Editable?) {
}
}
Loading