Socket connection now works
- Pairing a new device works (I did a lot since the last commit)
This commit is contained in:
@@ -21,6 +21,7 @@ android {
|
||||
release {
|
||||
minifyEnabled false
|
||||
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
|
||||
debuggable false
|
||||
}
|
||||
}
|
||||
compileOptions {
|
||||
@@ -30,10 +31,17 @@ android {
|
||||
kotlinOptions {
|
||||
jvmTarget = '1.8'
|
||||
}
|
||||
buildFeatures {
|
||||
viewBinding true
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
|
||||
implementation ('io.socket:socket.io-client:2.0.1') {
|
||||
// excluding org.json which is provided by Android
|
||||
exclude group: 'org.json', module: 'json'
|
||||
}
|
||||
implementation fileTree(dir: 'libs', include: ['lineage-sdk.jar'])
|
||||
implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
|
||||
implementation 'androidx.core:core-ktx:1.3.2'
|
||||
implementation 'androidx.appcompat:appcompat:1.1.0'
|
||||
@@ -46,6 +54,6 @@ dependencies {
|
||||
implementation 'com.rabbitmq:amqp-client:5.9.0'
|
||||
implementation "com.squareup.okhttp3:okhttp:4.9.0"
|
||||
testImplementation 'junit:junit:4.+'
|
||||
androidTestImplementation 'androidx.test.ext:junit:1.1.1'
|
||||
androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0'
|
||||
androidTestImplementation 'androidx.test.ext:junit:1.1.3'
|
||||
androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0'
|
||||
}
|
||||
BIN
android/app/libs/lineage-sdk.jar
Normal file
BIN
android/app/libs/lineage-sdk.jar
Normal file
Binary file not shown.
@@ -1,7 +1,7 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
package="de.nicolasklier.livebeat">
|
||||
package="de.nicolasklier.livebeat" >
|
||||
|
||||
<uses-permission android:name="android.permission.INTERNET" />
|
||||
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
|
||||
@@ -19,18 +19,26 @@
|
||||
android:label="@string/app_name"
|
||||
android:roundIcon="@mipmap/ic_launcher_round"
|
||||
android:supportsRtl="true"
|
||||
android:usesCleartextTraffic="true"
|
||||
android:theme="@style/Theme.Livebeat">
|
||||
android:theme="@style/Theme.Livebeat"
|
||||
android:usesCleartextTraffic="true" >
|
||||
<activity
|
||||
android:name=".SetupActivity"
|
||||
android:exported="false"
|
||||
android:label="@string/title_activity_setup"
|
||||
android:theme="@style/Theme.Livebeat.NoActionBar" />
|
||||
|
||||
<service android:name=".TrackerService" />
|
||||
<receiver android:name=".BootReceiver">
|
||||
<intent-filter >
|
||||
<action android:name="android.intent.action.BOOT_COMPLETED"/>
|
||||
|
||||
<receiver android:name=".BootReceiver" >
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.BOOT_COMPLETED" />
|
||||
</intent-filter>
|
||||
</receiver>
|
||||
|
||||
<activity
|
||||
android:name=".MainActivity"
|
||||
android:label="@string/app_name"
|
||||
android:theme="@style/Theme.Livebeat.NoActionBar">
|
||||
android:theme="@style/Theme.Livebeat.NoActionBar" >
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN" />
|
||||
|
||||
|
||||
@@ -10,16 +10,13 @@ import android.content.pm.PackageManager
|
||||
import android.graphics.Color
|
||||
import android.os.Build
|
||||
import android.os.Bundle
|
||||
import android.os.Handler
|
||||
import android.os.Looper
|
||||
import android.provider.Settings
|
||||
import android.telephony.TelephonyManager
|
||||
import android.util.Log
|
||||
import android.view.Menu
|
||||
import android.view.MenuItem
|
||||
import android.view.View
|
||||
import android.widget.Button
|
||||
import android.widget.TextView
|
||||
import androidx.annotation.ColorInt
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.core.app.ActivityCompat
|
||||
import androidx.core.content.ContextCompat
|
||||
@@ -27,11 +24,9 @@ import com.google.android.material.floatingactionbutton.FloatingActionButton
|
||||
import com.google.android.material.snackbar.Snackbar
|
||||
import com.squareup.moshi.Moshi
|
||||
import com.squareup.moshi.kotlin.reflect.KotlinJsonAdapterFactory
|
||||
import okhttp3.MediaType.Companion.toMediaType
|
||||
import okhttp3.OkHttpClient
|
||||
import okhttp3.Request
|
||||
import okhttp3.RequestBody.Companion.toRequestBody
|
||||
import java.util.logging.Logger
|
||||
|
||||
@Suppress("NULLABILITY_MISMATCH_BASED_ON_JAVA_ANNOTATIONS")
|
||||
class MainActivity : AppCompatActivity() {
|
||||
@@ -46,6 +41,15 @@ class MainActivity : AppCompatActivity() {
|
||||
|
||||
@SuppressLint("HardwareIds")
|
||||
fun checkIfPhoneIsRegistered() {
|
||||
val pref = this.getPreferences(Context.MODE_PRIVATE) ?: return
|
||||
val accessToken = pref.getString("accessToken", "");
|
||||
|
||||
// App is not setup
|
||||
if (accessToken == "") {
|
||||
val intent = Intent(baseContext, SetupActivity::class.java);
|
||||
startActivity(intent);
|
||||
}
|
||||
|
||||
if (TOKEN == "") return;
|
||||
Thread(Runnable {
|
||||
val androidId = Settings.Secure.getString(contentResolver, Settings.Secure.ANDROID_ID)
|
||||
@@ -95,8 +99,6 @@ class MainActivity : AppCompatActivity() {
|
||||
setContentView(R.layout.activity_main)
|
||||
setSupportActionBar(findViewById(R.id.toolbar))
|
||||
|
||||
val process = Runtime.getRuntime().exec("su")
|
||||
|
||||
// Check authorization
|
||||
val backendChecks = Thread(Runnable {
|
||||
val username = findViewById<TextView>(R.id.username).text
|
||||
@@ -142,7 +144,7 @@ class MainActivity : AppCompatActivity() {
|
||||
USER = jsonToUser.fromJson(userInfoResponseBody)
|
||||
|
||||
// Only start service if authentication went good.
|
||||
startService(Intent(this, TrackerService::class.java))
|
||||
// startService(Intent(this, TrackerService::class.java))
|
||||
|
||||
Snackbar.make(findViewById<FloatingActionButton>(R.id.fab), "Login succeeded", Snackbar.LENGTH_SHORT)
|
||||
.setBackgroundTint(Color.GREEN)
|
||||
@@ -174,14 +176,6 @@ class MainActivity : AppCompatActivity() {
|
||||
val statusRabbit = intent.getBooleanExtra("statusRabbit", false)
|
||||
val statusHttp = intent.getIntExtra("statusHttp", 404)
|
||||
|
||||
if (statusRabbit) {
|
||||
findViewById<TextView>(R.id.rabbitStatus).text = "connected"
|
||||
findViewById<TextView>(R.id.rabbitStatus).setTextColor(Color.GREEN)
|
||||
} else {
|
||||
findViewById<TextView>(R.id.rabbitStatus).text = "disconnected"
|
||||
findViewById<TextView>(R.id.rabbitStatus).setTextColor(Color.RED)
|
||||
}
|
||||
|
||||
/*if (statusHttp == 200) {
|
||||
findViewById<TextView>(R.id.httpStatus).text = "ONLINE (no login)"
|
||||
findViewById<TextView>(R.id.httpStatus).setTextColor(Color.CYAN)
|
||||
@@ -203,6 +197,14 @@ class MainActivity : AppCompatActivity() {
|
||||
}
|
||||
}
|
||||
|
||||
@ColorInt
|
||||
private fun getAccentColor(): Int {
|
||||
val attr = intArrayOf(android.R.attr.colorAccent)
|
||||
val typedArray = obtainStyledAttributes(android.R.style.Theme_DeviceDefault, attr)
|
||||
return typedArray.getColor(0, Color.BLACK)
|
||||
.also { typedArray.recycle() }
|
||||
}
|
||||
|
||||
private fun checkPerms() {
|
||||
if (ActivityCompat.checkSelfPermission(
|
||||
this,
|
||||
|
||||
@@ -22,12 +22,21 @@ class Phone(
|
||||
val architecture: String
|
||||
) {}
|
||||
|
||||
class PhoneRegistration (
|
||||
val phone: Phone,
|
||||
val token: String
|
||||
) {}
|
||||
|
||||
class PhoneSubmitPairCode (
|
||||
val phoneId: String,
|
||||
val code: String
|
||||
) {}
|
||||
|
||||
class User(
|
||||
val name: String,
|
||||
val type: String,
|
||||
val lastLogin: String,
|
||||
val twoFASecret: String?,
|
||||
val brokerToken: String,
|
||||
val createdAt: String
|
||||
)
|
||||
|
||||
|
||||
@@ -0,0 +1,195 @@
|
||||
package de.nicolasklier.livebeat
|
||||
|
||||
import android.content.Context
|
||||
import android.os.Build
|
||||
import android.os.Bundle
|
||||
import android.provider.Settings
|
||||
import android.util.Log
|
||||
import android.view.View
|
||||
import android.widget.*
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.navigation.ui.AppBarConfiguration
|
||||
import com.squareup.moshi.Moshi
|
||||
import com.squareup.moshi.kotlin.reflect.KotlinJsonAdapterFactory
|
||||
import de.nicolasklier.livebeat.databinding.ActivitySetupBinding
|
||||
import de.nicolasklier.livebeat.dialogs.ErrorDialog
|
||||
import de.nicolasklier.livebeat.dialogs.InputDialog
|
||||
import io.socket.client.IO
|
||||
import io.socket.client.Socket
|
||||
import okhttp3.OkHttpClient
|
||||
import okhttp3.Request
|
||||
import okhttp3.RequestBody.Companion.toRequestBody
|
||||
import org.json.JSONObject
|
||||
import java.lang.Exception
|
||||
|
||||
class SetupActivity : AppCompatActivity() {
|
||||
|
||||
private lateinit var appBarConfiguration: AppBarConfiguration
|
||||
private lateinit var binding: ActivitySetupBinding
|
||||
|
||||
private var host = ""
|
||||
private var username = ""
|
||||
private var password = ""
|
||||
private var token = ""
|
||||
private lateinit var socket: Socket;
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
|
||||
binding = ActivitySetupBinding.inflate(layoutInflater)
|
||||
setContentView(binding.root)
|
||||
|
||||
findViewById<Button>(R.id.btnLogin).setOnClickListener {
|
||||
kotlin.run {
|
||||
host = findViewById<EditText>(R.id.inputServerIp).text.toString()
|
||||
username = findViewById<EditText>(R.id.inputUsername).text.toString()
|
||||
password = findViewById<EditText>(R.id.inputPassword).text.toString()
|
||||
|
||||
findViewById<LinearLayout>(R.id.connectLayout).visibility = View.VISIBLE;
|
||||
tryConnect()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun updateStatus(text: String) {
|
||||
runOnUiThread {
|
||||
findViewById<TextView>(R.id.connectStatus).text = text;
|
||||
}
|
||||
}
|
||||
|
||||
private fun throwError(text: String) {
|
||||
val dialog = ErrorDialog(text)
|
||||
dialog.show(supportFragmentManager, "");
|
||||
|
||||
runOnUiThread {
|
||||
findViewById<LinearLayout>(R.id.connectLayout).visibility = View.GONE;
|
||||
}
|
||||
}
|
||||
|
||||
private fun tryConnect() {
|
||||
updateStatus("Connect to backend");
|
||||
|
||||
// Connection values
|
||||
val options = IO.Options.builder()
|
||||
.setTransports(arrayOf("websocket"))
|
||||
.build();
|
||||
|
||||
try {
|
||||
socket = IO.socket("http://$host:8040", options)
|
||||
socket.connect()
|
||||
} catch (e: Exception) {
|
||||
updateStatus("Failed to connect");
|
||||
Log.e("Socket.io", "Unable to connect to socket: ${e.message}")
|
||||
return;
|
||||
}
|
||||
|
||||
socket.on("connect") { args ->
|
||||
run {
|
||||
updateStatus("Connected")
|
||||
login();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun login() {
|
||||
val moshi = Moshi.Builder().add(KotlinJsonAdapterFactory()).build()
|
||||
val jsonToLogin = moshi.adapter(Login::class.java)
|
||||
|
||||
val client = OkHttpClient()
|
||||
val req = Request.Builder()
|
||||
.url("${MainActivity.API_URL}/user/login")
|
||||
.post(
|
||||
("{ \"username\": \"" + username + "\"," +
|
||||
"\"password\": \"" + password + "\" }").toRequestBody()
|
||||
)
|
||||
.header("Content-Type", "application/json")
|
||||
.build()
|
||||
val loginResponse = client.newCall(req).execute()
|
||||
val responseBody = loginResponse.body!!.string()
|
||||
|
||||
if (loginResponse.code == 200) {
|
||||
token = jsonToLogin.fromJson(responseBody)!!.token
|
||||
requestAccess()
|
||||
} else {
|
||||
throwError("Username or password is wrong.")
|
||||
}
|
||||
}
|
||||
|
||||
private fun requestAccess() {
|
||||
var phoneId = "";
|
||||
val androidId = Settings.Secure.getString(contentResolver, Settings.Secure.ANDROID_ID)
|
||||
|
||||
val phone = Phone(
|
||||
androidId,
|
||||
Build.MODEL,
|
||||
Build.PRODUCT,
|
||||
Build.VERSION.BASE_OS + " " + Build.VERSION.RELEASE + " " + Build.VERSION.CODENAME,
|
||||
System.getProperty("os.arch")
|
||||
)
|
||||
val phoneRegistration = PhoneRegistration(
|
||||
phone,
|
||||
token
|
||||
)
|
||||
val moshi = Moshi.Builder().add(KotlinJsonAdapterFactory()).build()
|
||||
val phoneToJson = moshi.adapter(PhoneRegistration::class.java)
|
||||
val json = phoneToJson.toJson(phoneRegistration)
|
||||
|
||||
socket.emit("requestAccess", json)
|
||||
updateStatus("Await new phone id")
|
||||
|
||||
// We received a response from the backend containing the phone id.
|
||||
socket.on("requestAccess") { args ->
|
||||
run {
|
||||
phoneId = (args[0] as JSONObject).getString("phoneId");
|
||||
updateStatus("Await user to enter pair code")
|
||||
|
||||
fun promptUser() {
|
||||
val dialog = InputDialog("Pair code" , "Look into your device overview to get the pair code.") { choice, code ->
|
||||
if (choice == InputDialog.UserChoice.CANCEL) {
|
||||
updateStatus("Process has been canceled by user.")
|
||||
return@InputDialog;
|
||||
}
|
||||
|
||||
updateStatus("Validate code $code for $phoneId")
|
||||
val submitCode = PhoneSubmitPairCode(
|
||||
phoneId,
|
||||
code
|
||||
);
|
||||
|
||||
val submitCodeToJson = moshi.adapter(PhoneSubmitPairCode::class.java)
|
||||
val submitCodeJson = submitCodeToJson.toJson(submitCode)
|
||||
|
||||
socket.emit("submitPairCode", submitCodeJson);
|
||||
}
|
||||
dialog.show(supportFragmentManager, "");
|
||||
}
|
||||
|
||||
// The backend only calls this event again if the code is incorrect. Otherwise `accessGranted` is called.
|
||||
socket.on("submitPairCode") { args ->
|
||||
run {
|
||||
if ((args[0] as String) == "") {
|
||||
updateStatus("Code has been incorrect")
|
||||
|
||||
// Prompt user to enter code as long as it's invalid
|
||||
promptUser()
|
||||
} else {
|
||||
// If response is not empty, we received a token (let's just hope that)
|
||||
updateStatus("Code has been correct")
|
||||
|
||||
val sharedPref = this.getPreferences(Context.MODE_PRIVATE) ?: return@on
|
||||
with (sharedPref.edit()) {
|
||||
putString("accessToken", args[0] as String)
|
||||
commit()
|
||||
}
|
||||
|
||||
// Return to previous activity.
|
||||
this.finish()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
promptUser()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -16,19 +16,27 @@ import android.provider.Settings
|
||||
import android.util.Log
|
||||
import androidx.core.app.ActivityCompat
|
||||
import androidx.core.app.NotificationCompat
|
||||
import androidx.core.app.NotificationCompat.VISIBILITY_PRIVATE
|
||||
import androidx.core.app.NotificationCompat.VISIBILITY_SECRET
|
||||
import com.rabbitmq.client.Channel
|
||||
import com.rabbitmq.client.Connection
|
||||
import com.rabbitmq.client.ConnectionFactory
|
||||
import com.squareup.moshi.JsonAdapter
|
||||
import com.squareup.moshi.Moshi
|
||||
import com.squareup.moshi.kotlin.reflect.KotlinJsonAdapterFactory
|
||||
import io.socket.client.IO
|
||||
import io.socket.client.Socket
|
||||
import io.socket.emitter.Emitter
|
||||
import okhttp3.EventListener
|
||||
import okhttp3.OkHttpClient
|
||||
import okhttp3.Request
|
||||
import java.io.IOException
|
||||
import java.lang.Exception
|
||||
import java.util.concurrent.TimeoutException
|
||||
|
||||
class TrackerService : Service() {
|
||||
|
||||
var isSocketConnected = false;
|
||||
val conn = arrayOfNulls<Connection>(1)
|
||||
val channel = arrayOfNulls<Channel>(1)
|
||||
|
||||
@@ -36,35 +44,47 @@ class TrackerService : Service() {
|
||||
return null
|
||||
}
|
||||
|
||||
private fun connectWithBroker() {
|
||||
private fun connectSocket() {
|
||||
// This thread only connects to RabbitMQ
|
||||
val connectionThread = Thread(Runnable {
|
||||
val client = OkHttpClient()
|
||||
val socket: Socket;
|
||||
|
||||
// Connection values
|
||||
val options = IO.Options.builder()
|
||||
.setTransports(arrayOf("websocket"))
|
||||
.build();
|
||||
|
||||
val factory = ConnectionFactory()
|
||||
factory.username = MainActivity.USER!!.name
|
||||
factory.password = MainActivity.USER!!.brokerToken
|
||||
factory.virtualHost = "/"
|
||||
factory.host = "192.168.178.26"
|
||||
factory.port = 5672
|
||||
factory.isAutomaticRecoveryEnabled = true
|
||||
try {
|
||||
conn[0] = factory.newConnection()
|
||||
channel[0] = conn[0]?.createChannel()
|
||||
socket = IO.socket("http://192.168.178.26:8040", options)
|
||||
socket.connect()
|
||||
} catch (e: Exception) {
|
||||
Log.e("Socket.io", "Unable to connect to socket: ${e.message}")
|
||||
return@Runnable
|
||||
}
|
||||
|
||||
val intent = Intent("de.nicolasklier.livebeat")
|
||||
val bundle = Bundle()
|
||||
bundle.putBoolean("statusRabbit", true)
|
||||
intent.putExtras(bundle)
|
||||
this.sendBroadcast(intent)
|
||||
socket.on("test") { args ->
|
||||
run {
|
||||
Log.i("Socket.io", args[0].toString());
|
||||
}
|
||||
}
|
||||
|
||||
channel[0]?.queueDeclare("tracker-" + factory.username, true, false, false, null)
|
||||
//channel[0]?.basicPublish("", "Tracker", null, "Test message".toByteArray())
|
||||
Log.i("RabbitMQ", "run: Published test message")
|
||||
} catch (e: IOException) {
|
||||
e.printStackTrace()
|
||||
} catch (e: TimeoutException) {
|
||||
e.printStackTrace()
|
||||
socket.on("connect") { args ->
|
||||
run {
|
||||
isSocketConnected = true;
|
||||
}
|
||||
}
|
||||
|
||||
socket.on("disconnect") { args ->
|
||||
run {
|
||||
isSocketConnected = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (socket.connected()) {
|
||||
socket.emit("test", "This is a message from my phone!")
|
||||
Log.i("Socket.io", "Published test message")
|
||||
} else {
|
||||
Log.e("Socket.io", "Cannot send test message because I'm not connected with the Socket.io server.")
|
||||
}
|
||||
})
|
||||
connectionThread.start()
|
||||
@@ -105,13 +125,15 @@ class TrackerService : Service() {
|
||||
}
|
||||
}
|
||||
|
||||
connectWithBroker()
|
||||
startForeground()
|
||||
connectSocket()
|
||||
start()
|
||||
return super.onStartCommand(intent, flags, startId)
|
||||
}
|
||||
|
||||
private fun startForeground() {
|
||||
val noticicationIntent = Intent(this, MainActivity::class.java)
|
||||
private fun start() {
|
||||
val noticicationIntent = Intent(this, MainActivity::class.java).apply {
|
||||
action = "Stop"
|
||||
}
|
||||
val pendingIntent = PendingIntent.getActivity(this, 0, noticicationIntent, 0)
|
||||
val chan = NotificationChannel(
|
||||
NOTIF_CHANNEL_ID,
|
||||
@@ -121,17 +143,24 @@ class TrackerService : Service() {
|
||||
chan.lockscreenVisibility = Notification.VISIBILITY_SECRET
|
||||
val manager = (getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager)
|
||||
manager.createNotificationChannel(chan)
|
||||
startForeground(NOTIF_ID, NotificationCompat.Builder(this, NOTIF_CHANNEL_ID)
|
||||
.setOngoing(true)
|
||||
.setContentTitle("Livebeat")
|
||||
.setContentText("Tracker is running")
|
||||
.setContentIntent(pendingIntent)
|
||||
.setCategory(Notification.CATEGORY_SERVICE)
|
||||
.setPriority(NotificationManager.IMPORTANCE_LOW)
|
||||
.setChannelId(NOTIF_CHANNEL_ID)
|
||||
.setColorized(true)
|
||||
.setColor(Color.BLACK)
|
||||
.build())
|
||||
|
||||
val notification = NotificationCompat.Builder(this, NOTIF_CHANNEL_ID)
|
||||
.setOngoing(true)
|
||||
.setContentTitle("Livebeat")
|
||||
.setContentText("Tracker is running")
|
||||
.setContentIntent(pendingIntent)
|
||||
.setCategory(Notification.CATEGORY_SERVICE)
|
||||
.setPriority(NotificationManager.IMPORTANCE_LOW)
|
||||
.setSmallIcon(R.mipmap.ic_launcher)
|
||||
.setChannelId(NOTIF_CHANNEL_ID)
|
||||
.setColorized(true)
|
||||
.setShowWhen(false)
|
||||
.setVisibility(VISIBILITY_SECRET)
|
||||
.setColor(Color.BLACK)
|
||||
.addAction(R.drawable.ic_launcher_background, "Stop", pendingIntent)
|
||||
.build()
|
||||
|
||||
manager.notify(0, notification)
|
||||
}
|
||||
|
||||
companion object {
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
package de.nicolasklier.livebeat.dialogs
|
||||
|
||||
import android.app.AlertDialog
|
||||
import android.app.Dialog
|
||||
import android.os.Bundle
|
||||
import androidx.fragment.app.DialogFragment
|
||||
|
||||
class ErrorDialog(val message: String) : DialogFragment() {
|
||||
|
||||
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
|
||||
return activity?.let {
|
||||
val builder = AlertDialog.Builder(it)
|
||||
builder.setMessage(message)
|
||||
.setPositiveButton("Ok"
|
||||
) { dialog, _ ->
|
||||
dialog.cancel()
|
||||
}
|
||||
builder.create()
|
||||
} ?: throw IllegalStateException("Activity cannot be null")
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
package de.nicolasklier.livebeat.dialogs
|
||||
|
||||
import android.app.AlertDialog
|
||||
import android.app.Dialog
|
||||
import android.os.Bundle
|
||||
import android.widget.EditText
|
||||
import androidx.fragment.app.DialogFragment
|
||||
import de.nicolasklier.livebeat.R
|
||||
import de.nicolasklier.livebeat.User
|
||||
|
||||
class InputDialog(val title: String, val message: String, val callback: (UserChoice, String) -> Unit) : DialogFragment() {
|
||||
|
||||
enum class UserChoice {
|
||||
CANCEL,
|
||||
SUBMIT
|
||||
}
|
||||
|
||||
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
|
||||
return activity?.let {
|
||||
val inflater = requireActivity().layoutInflater;
|
||||
val view = inflater.inflate(R.layout.input_dialog, null);
|
||||
|
||||
val builder = AlertDialog.Builder(it)
|
||||
builder
|
||||
.setMessage(message)
|
||||
.setTitle(title)
|
||||
.setCancelable(false)
|
||||
.setView(view)
|
||||
.setNegativeButton("Cancel"
|
||||
) { dialog, _ ->
|
||||
callback(UserChoice.CANCEL, "")
|
||||
dialog.cancel()
|
||||
}
|
||||
.setPositiveButton("Submit"
|
||||
) { dialog, _ ->
|
||||
callback(UserChoice.SUBMIT, view.findViewById<EditText>(R.id.input).text.toString());
|
||||
}
|
||||
builder.create()
|
||||
} ?: throw IllegalStateException("Activity cannot be null")
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -64,36 +64,6 @@
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:gravity="center"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/rabbitText"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center_horizontal|center_vertical"
|
||||
android:layout_weight="0"
|
||||
android:text="RabbitMQ"
|
||||
android:textColor="@color/white" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/rabbitStatus"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_margin="20dp"
|
||||
android:layout_weight="0"
|
||||
android:fontFamily="sans-serif-black"
|
||||
android:text="disconnected"
|
||||
android:textAlignment="center"
|
||||
android:textAllCaps="true"
|
||||
android:textColor="@color/orange"
|
||||
android:textSize="14sp" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<Space
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="120dp" />
|
||||
|
||||
119
android/app/src/main/res/layout/activity_setup.xml
Normal file
119
android/app/src/main/res/layout/activity_setup.xml
Normal file
@@ -0,0 +1,119 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
tools:context=".SetupActivity">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/textView"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="176dp"
|
||||
android:layout_marginTop="51dp"
|
||||
android:layout_marginEnd="177dp"
|
||||
android:text="Connect"
|
||||
android:textColor="@color/white"
|
||||
android:textSize="34sp"
|
||||
android:textStyle="bold"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/linearLayout2"
|
||||
android:layout_width="331dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="200dp"
|
||||
android:orientation="vertical"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent">
|
||||
|
||||
<EditText
|
||||
android:id="@+id/inputServerIp"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:ems="10"
|
||||
android:hint="Server IP"
|
||||
android:inputType="textPersonName"
|
||||
android:text="192.168.178.26" />
|
||||
|
||||
<EditText
|
||||
android:id="@+id/inputUsername"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:ems="10"
|
||||
android:hint="Username"
|
||||
android:inputType="textPersonName"
|
||||
android:text="admin"
|
||||
android:textColor="@color/white" />
|
||||
|
||||
<EditText
|
||||
android:id="@+id/inputPassword"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:ems="10"
|
||||
android:hint="Password"
|
||||
android:inputType="textPassword"
|
||||
android:text="$1KDaNCDlyXAOg"
|
||||
android:textColor="@color/white" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/textView2"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="174dp"
|
||||
android:layout_marginTop="12dp"
|
||||
android:layout_marginEnd="179dp"
|
||||
android:text="You'll need to login to a Livebeat server instance."
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintHorizontal_bias="0.489"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/textView" />
|
||||
|
||||
<Button
|
||||
android:id="@+id/btnLogin"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="161dp"
|
||||
android:layout_marginTop="85dp"
|
||||
android:layout_marginEnd="162dp"
|
||||
android:text="Login"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/linearLayout2" />
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/connectLayout"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
android:visibility="invisible"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/btnLogin">
|
||||
|
||||
<ProgressBar
|
||||
android:id="@+id/progressBar"
|
||||
style="?android:attr/progressBarStyle"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="181dp"
|
||||
android:layout_marginTop="39dp"
|
||||
android:layout_marginEnd="182dp"
|
||||
android:progressTint="@color/white"
|
||||
android:visibility="visible" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/connectStatus"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="16dp"
|
||||
android:textAlignment="center" />
|
||||
</LinearLayout>
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
16
android/app/src/main/res/layout/input_dialog.xml
Normal file
16
android/app/src/main/res/layout/input_dialog.xml
Normal file
@@ -0,0 +1,16 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:orientation="vertical"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content">
|
||||
<EditText
|
||||
android:id="@+id/input"
|
||||
android:inputType="number"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="4dp"
|
||||
android:layout_marginLeft="4dp"
|
||||
android:layout_marginRight="4dp"
|
||||
android:layout_marginBottom="16dp"
|
||||
android:fontFamily="sans-serif"/>
|
||||
</LinearLayout>
|
||||
28
android/app/src/main/res/navigation/nav_graph.xml
Normal file
28
android/app/src/main/res/navigation/nav_graph.xml
Normal file
@@ -0,0 +1,28 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<navigation xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:id="@+id/nav_graph"
|
||||
app:startDestination="@id/FirstFragment">
|
||||
|
||||
<fragment
|
||||
android:id="@+id/FirstFragment"
|
||||
android:name="de.nicolasklier.livebeat.FirstFragment"
|
||||
android:label="@string/first_fragment_label"
|
||||
tools:layout="@layout/fragment_first" >
|
||||
|
||||
<action
|
||||
android:id="@+id/action_FirstFragment_to_SecondFragment"
|
||||
app:destination="@id/SecondFragment" />
|
||||
</fragment>
|
||||
<fragment
|
||||
android:id="@+id/SecondFragment"
|
||||
android:name="de.nicolasklier.livebeat.SecondFragment"
|
||||
android:label="@string/second_fragment_label"
|
||||
tools:layout="@layout/fragment_second" >
|
||||
|
||||
<action
|
||||
android:id="@+id/action_SecondFragment_to_FirstFragment"
|
||||
app:destination="@id/FirstFragment" />
|
||||
</fragment>
|
||||
</navigation>
|
||||
3
android/app/src/main/res/values-land/dimens.xml
Normal file
3
android/app/src/main/res/values-land/dimens.xml
Normal file
@@ -0,0 +1,3 @@
|
||||
<resources>
|
||||
<dimen name="fab_margin">48dp</dimen>
|
||||
</resources>
|
||||
3
android/app/src/main/res/values-w1240dp/dimens.xml
Normal file
3
android/app/src/main/res/values-w1240dp/dimens.xml
Normal file
@@ -0,0 +1,3 @@
|
||||
<resources>
|
||||
<dimen name="fab_margin">200dp</dimen>
|
||||
</resources>
|
||||
3
android/app/src/main/res/values-w600dp/dimens.xml
Normal file
3
android/app/src/main/res/values-w600dp/dimens.xml
Normal file
@@ -0,0 +1,3 @@
|
||||
<resources>
|
||||
<dimen name="fab_margin">48dp</dimen>
|
||||
</resources>
|
||||
@@ -9,4 +9,5 @@
|
||||
|
||||
<string name="hello_first_fragment">Hello first fragment</string>
|
||||
<string name="hello_second_fragment">Hello second fragment. Arg: %1$s</string>
|
||||
<string name="title_activity_setup">Setup</string>
|
||||
</resources>
|
||||
@@ -1,6 +1,6 @@
|
||||
<resources xmlns:tools="http://schemas.android.com/tools">
|
||||
<!-- Base application theme. -->
|
||||
<style name="Theme.Livebeat" parent="Theme.AppCompat.Light">
|
||||
<style name="Theme.Livebeat" parent="Theme.MaterialComponents.DayNight">
|
||||
<!-- Primary brand color. -->
|
||||
<item name="colorPrimary">@color/black</item>
|
||||
<item name="colorPrimaryVariant">@color/black2</item>
|
||||
|
||||
Reference in New Issue
Block a user