Skip to content

Commit

Permalink
Merge branch 'develop'
Browse files Browse the repository at this point in the history
  • Loading branch information
awasisto committed Apr 25, 2021
2 parents 9449cf4 + 4a3cebe commit 5cb8604
Show file tree
Hide file tree
Showing 24 changed files with 178 additions and 91 deletions.
11 changes: 9 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,18 @@ CamRNG HTTP

An HTTP API server for [CamRNG](https://github.com/awasisto/camrng) quantum random number generator.

https://play.google.com/store/apps/details?id=com.wasisto.camrnghttp

Screenshots
-----------

![CamRNG HTTP screenshot 1](https://lh3.googleusercontent.com/vjYA8K0jdwoIst_mibni7EQkhV3pMs_RBDyHXCiZNKhj1mU09Ib2Hi2oLZWNy2RUEI7V=w250-rw)
![CamRNG HTTP screenshot 2](https://lh3.googleusercontent.com/m8E0Et3TMRamPdRgr8d7x_fbGMRiZBNWhS6lzhe2v6kr3EPmfdXC63ZvcNqXiWG2oVts=w250-rw)
![Screenshot 1](https://i.imgur.com/BlgODG0.png)
![Screenshot 2](https://i.imgur.com/wcz5vkC.png)

Architecture
------------

![Architecture](https://i.imgur.com/LKMst4M.png)

API Documentation
-----------------
Expand Down
6 changes: 3 additions & 3 deletions app/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,8 @@ android {
applicationId "com.wasisto.camrnghttp"
minSdkVersion 21
targetSdkVersion 30
versionCode 2
versionName "0.0.2"
versionCode 3
versionName "0.0.3"
}

buildFeatures {
Expand Down Expand Up @@ -57,7 +57,7 @@ dependencies {

implementation 'com.google.android.material:material:1.3.0'

implementation 'commons-codec:commons-codec:1.15'
implementation 'commons-codec:commons-codec:1.2'

implementation 'org.apache.commons:commons-rng-sampling:1.3'

Expand Down
4 changes: 3 additions & 1 deletion app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,10 @@
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />

<uses-feature android:name="android.hardware.camera" android:required="true" />

<application
android:name=".servermanager.CamrngHttpApp"
android:name=".CamrngHttpApp"
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,10 @@
* limitations under the License.
*/

package com.wasisto.camrnghttp.servermanager
package com.wasisto.camrnghttp

import android.app.Application
import android.util.Log
import com.wasisto.camrnghttp.BuildConfig
import timber.log.Timber

class CamrngHttpApp : Application() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
* limitations under the License.
*/

package com.wasisto.camrnghttp.server.data
package com.wasisto.camrnghttp.random

import android.content.Context
import com.wasisto.camrng.LensFacing
Expand Down
30 changes: 18 additions & 12 deletions app/src/main/java/com/wasisto/camrnghttp/server/api/ApiServer.kt
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,9 @@ package com.wasisto.camrnghttp.server.api
import android.content.Context
import android.net.wifi.WifiManager
import android.text.format.Formatter.formatIpAddress
import com.google.gson.GsonBuilder
import com.koushikdutta.async.http.server.AsyncHttpServer
import com.wasisto.camrnghttp.server.data.CamrngRandomDataSource
import com.wasisto.camrnghttp.random.CamrngRandomDataSource
import com.wasisto.camrnghttp.server.domain.usecases.*
import com.wasisto.camrnghttp.servermanager.domain.interfaces.Server

Expand All @@ -30,30 +31,35 @@ class ApiServer(context: Context) : Server {

private val httpServer = AsyncHttpServer()

private val controller =
Controller(
private val gson = GsonBuilder().disableHtmlEscaping().create()

private val indexController = IndexController()

private val restApiController =
RestApiController(
RandBytesUseCase(camrngRandomDataSource),
RandBoolUseCase(camrngRandomDataSource),
RandInt32UseCase(camrngRandomDataSource),
RandUniformUseCase(camrngRandomDataSource),
RandNormalUseCase(camrngRandomDataSource)
RandNormalUseCase(camrngRandomDataSource),
gson
)

private val errorHandler = ErrorHandler()
private val restApiErrorHandler = RestApiErrorHandler(gson)

private val wifiManager = context.applicationContext.getSystemService(Context.WIFI_SERVICE) as WifiManager

init {
httpServer.get("/") { request, response -> controller.index(request, response) }
httpServer.get("$API_PATH_PREFIX/$ACTION_RANDBYTES") { request, response -> try { controller.randBytes(request, response) } catch (t: Throwable) { errorHandler.handle(t, response) } }
httpServer.get("$API_PATH_PREFIX/$ACTION_RANDBOOL") { request, response -> try { controller.randBool(request, response) } catch (t: Throwable) { errorHandler.handle(t, response) } }
httpServer.get("$API_PATH_PREFIX/$ACTION_RANDINT32") { request, response -> try { controller.randInt32(request, response) } catch (t: Throwable) { errorHandler.handle(t, response) } }
httpServer.get("$API_PATH_PREFIX/$ACTION_RANDUNIFORM") { request, response -> try { controller.randUniform(request, response) } catch (t: Throwable) { errorHandler.handle(t, response) } }
httpServer.get("$API_PATH_PREFIX/$ACTION_RANDNORMAL") { request, response -> try { controller.randNormal(request, response) } catch (t: Throwable) { errorHandler.handle(t, response) } }
httpServer.get("/") { request, response -> indexController.index(request, response) }
httpServer.get("$REST_API_PATH_PREFIX/$ACTION_RANDBYTES") { request, response -> try { restApiController.randBytes(request, response) } catch (t: Throwable) { restApiErrorHandler.handle(t, response) } }
httpServer.get("$REST_API_PATH_PREFIX/$ACTION_RANDBOOL") { request, response -> try { restApiController.randBool(request, response) } catch (t: Throwable) { restApiErrorHandler.handle(t, response) } }
httpServer.get("$REST_API_PATH_PREFIX/$ACTION_RANDINT32") { request, response -> try { restApiController.randInt32(request, response) } catch (t: Throwable) { restApiErrorHandler.handle(t, response) } }
httpServer.get("$REST_API_PATH_PREFIX/$ACTION_RANDUNIFORM") { request, response -> try { restApiController.randUniform(request, response) } catch (t: Throwable) { restApiErrorHandler.handle(t, response) } }
httpServer.get("$REST_API_PATH_PREFIX/$ACTION_RANDNORMAL") { request, response -> try { restApiController.randNormal(request, response) } catch (t: Throwable) { restApiErrorHandler.handle(t, response) } }
}

override fun start(port: Int) {
require(port in 1024..65535) { "port out of range:$port" }
require(port in 1024..65535)
camrngRandomDataSource.start()
httpServer.listen(port)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@

package com.wasisto.camrnghttp.server.api

const val API_PATH_PREFIX = "/api"
const val REST_API_PATH_PREFIX = "/api"

const val CONTENT_TYPE_JSON = "application/json"

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,13 @@
* limitations under the License.
*/

package com.wasisto.camrnghttp.servermanager.ui
package com.wasisto.camrnghttp.server.api

import android.view.View
import androidx.databinding.BindingAdapter
import com.koushikdutta.async.http.server.AsyncHttpServerRequest
import com.koushikdutta.async.http.server.AsyncHttpServerResponse

@BindingAdapter("invisibleUnless")
fun invisibleUnless(view: View, visible: Boolean) {
view.visibility = if (visible) View.VISIBLE else View.INVISIBLE
}
class IndexController {
fun index(request: AsyncHttpServerRequest, response: AsyncHttpServerResponse) {
response.send("API Documentation: <a href=\"https://github.com/awasisto/camrng-http/wiki\">https://github.com/awasisto/camrng-http/wiki</a>")
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,25 +16,21 @@

package com.wasisto.camrnghttp.server.api

import com.google.gson.Gson
import com.koushikdutta.async.http.server.AsyncHttpServerRequest
import com.koushikdutta.async.http.server.AsyncHttpServerResponse
import java.util.*
import com.google.gson.GsonBuilder
import com.wasisto.camrnghttp.server.domain.usecases.*

class Controller(
class RestApiController(
private val randBytesUseCase: RandBytesUseCase,
private val randBoolUseCase: RandBoolUseCase,
private val randInt32UseCase: RandInt32UseCase,
private val randUniformUseCase: RandUniformUseCase,
private val randNormalUseCase: RandNormalUseCase
private val randNormalUseCase: RandNormalUseCase,
private val gson: Gson
) {
private val gson = GsonBuilder().disableHtmlEscaping().create()

fun index(request: AsyncHttpServerRequest, response: AsyncHttpServerResponse) {
response.send("Docs: <a href=\"https://github.com/awasisto/camrng-http/wiki\">https://github.com/awasisto/camrng-http/wiki</a>")
}

fun randBytes(request: AsyncHttpServerRequest, response: AsyncHttpServerResponse) {
val length = request[PARAM_LENGTH]?.toIntOrNull() ?: throw IllegalArgumentException()
require(length >= 0)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,7 @@ import com.google.gson.Gson
import com.koushikdutta.async.http.server.AsyncHttpServerResponse
import timber.log.Timber

class ErrorHandler {

private val gson = Gson()
class RestApiErrorHandler(private val gson: Gson) {

fun handle(t: Throwable, response: AsyncHttpServerResponse) {
when(t) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ package com.wasisto.camrnghttp.server.domain.usecases
import com.wasisto.camrnghttp.server.domain.interfaces.RandomDataSource

class RandBoolUseCase(private val randomDataSource: RandomDataSource) {
operator fun invoke(length: Int) =
operator fun invoke(length: Int): List<Boolean> =
ArrayList<Boolean>(length).apply {
val bytes = ByteArray((length shr 3) + 1).apply { randomDataSource.randBytes(this) }
for (byte in bytes) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ class RandBytesUseCase(private val randomDataSource: RandomDataSource) {

operator fun invoke(length: Int, format: Format): String =
when (format) {
Format.Base64 -> Base64.encodeBase64String(ByteArray(length).apply { randomDataSource.randBytes(this) })
Format.Hex -> Hex.encodeHexString(ByteArray(length).apply { randomDataSource.randBytes(this) })
Format.Base64 -> String(Base64.encodeBase64(ByteArray(length).apply { randomDataSource.randBytes(this) }))
Format.Hex -> String(Hex.encodeHex(ByteArray(length).apply { randomDataSource.randBytes(this) }))
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,7 @@ import android.content.Intent
import android.content.pm.PackageManager
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.view.Menu
import android.view.MenuItem
import android.view.*
import android.widget.Toast
import androidx.core.app.ActivityCompat
import androidx.core.content.ContextCompat
Expand Down Expand Up @@ -61,10 +60,26 @@ class MainActivity : AppCompatActivity() {
viewModel.showErrorEvent.observe(this) {
Toast.makeText(this, R.string.an_error_occurred, Toast.LENGTH_SHORT).show()
}

viewModel.shouldBlankScreen.observe(this) { shouldBlankScreen ->
if (shouldBlankScreen) {
supportActionBar?.hide()
window.decorView.systemUiVisibility = View.SYSTEM_UI_FLAG_HIDE_NAVIGATION or View.SYSTEM_UI_FLAG_FULLSCREEN or View.SYSTEM_UI_FLAG_IMMERSIVE
} else {
supportActionBar?.show()
window.decorView.systemUiVisibility = View.SYSTEM_UI_FLAG_VISIBLE
}
}

window.decorView.setOnSystemUiVisibilityChangeListener {
if (viewModel.shouldBlankScreen.value == true) {
window.decorView.systemUiVisibility = View.SYSTEM_UI_FLAG_HIDE_NAVIGATION or View.SYSTEM_UI_FLAG_FULLSCREEN or View.SYSTEM_UI_FLAG_IMMERSIVE
}
}
}

override fun onCreateOptionsMenu(menu: Menu): Boolean {
menuInflater.inflate(R.menu.menu_server_manager, menu)
menuInflater.inflate(R.menu.menu_main, menu)
return true
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,10 +43,14 @@ class MainViewModel(

val shouldDisableStartStopButton = MutableLiveData(false)

val shouldDisableBlankScreenButton = MutableLiveData(true)

val shouldDisablePortInputField = MutableLiveData(false)

val shouldShowServerRunningIndicator = MutableLiveData(false)

val shouldBlankScreen = MutableLiveData(false)

val shouldKeepScreenOn = MutableLiveData(false)

val showErrorEvent = MutableLiveData<Event<Unit>>()
Expand All @@ -57,6 +61,14 @@ class MainViewModel(
if (!serverRunning) startServer() else stopServer()
}

fun onBlankScreenButtonClick() {
shouldBlankScreen.value = true
}

fun onScreenBlankerOverlayClick() {
shouldBlankScreen.value = false
}

fun onActivityStopped() {
stopServer()
}
Expand All @@ -76,6 +88,7 @@ class MainViewModel(
startStopButtonText.value = R.string.stop_server
shouldShowServerRunningIndicator.value = true
shouldKeepScreenOn.value = true
shouldDisableBlankScreenButton.value = false
}

serverRunning = true
Expand Down Expand Up @@ -106,6 +119,8 @@ class MainViewModel(
startStopButtonText.value = R.string.start_server
shouldShowServerRunningIndicator.value = false
shouldKeepScreenOn.value = false
shouldBlankScreen.value = false
shouldDisableBlankScreenButton.value = true
}

serverRunning = false
Expand Down
Loading

0 comments on commit 5cb8604

Please sign in to comment.