Livebeat is now able to send, store and show beats
This commit is contained in:
@@ -1,9 +1,16 @@
|
||||
<?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">
|
||||
|
||||
<uses-permission android:name="android.permission.INTERNET"></uses-permission>
|
||||
<uses-permission android:name="android.permission.FOREGROUND_SERVICE"></uses-permission>
|
||||
<uses-permission android:name="android.permission.INTERNET" />
|
||||
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
|
||||
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
|
||||
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
|
||||
|
||||
<uses-feature
|
||||
android:name="android.hardware.sensor.gyroscope"
|
||||
android:required="true" />
|
||||
|
||||
<application
|
||||
android:allowBackup="true"
|
||||
@@ -11,6 +18,7 @@
|
||||
android:label="@string/app_name"
|
||||
android:roundIcon="@mipmap/ic_launcher_round"
|
||||
android:supportsRtl="true"
|
||||
android:usesCleartextTraffic="true"
|
||||
android:theme="@style/Theme.Livebeat">
|
||||
<service android:name=".TrackerService" />
|
||||
<activity
|
||||
|
||||
@@ -1,31 +0,0 @@
|
||||
package de.nicolasklier.livebeat
|
||||
|
||||
import android.os.Bundle
|
||||
import androidx.fragment.app.Fragment
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.widget.Button
|
||||
import androidx.navigation.fragment.findNavController
|
||||
|
||||
/**
|
||||
* A simple [Fragment] subclass as the default destination in the navigation.
|
||||
*/
|
||||
class FirstFragment : Fragment() {
|
||||
|
||||
override fun onCreateView(
|
||||
inflater: LayoutInflater, container: ViewGroup?,
|
||||
savedInstanceState: Bundle?
|
||||
): View? {
|
||||
// Inflate the layout for this fragment
|
||||
return inflater.inflate(R.layout.fragment_first, container, false)
|
||||
}
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
|
||||
view.findViewById<Button>(R.id.button_first).setOnClickListener {
|
||||
findNavController().navigate(R.id.action_FirstFragment_to_SecondFragment)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,24 +1,202 @@
|
||||
package de.nicolasklier.livebeat
|
||||
|
||||
import android.Manifest
|
||||
import android.annotation.SuppressLint
|
||||
import android.content.BroadcastReceiver
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.content.IntentFilter
|
||||
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.util.Log
|
||||
import android.view.Menu
|
||||
import android.view.MenuItem
|
||||
import android.view.View
|
||||
import android.widget.Button
|
||||
import android.widget.TextView
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.core.app.ActivityCompat
|
||||
import androidx.core.content.ContextCompat
|
||||
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() {
|
||||
|
||||
companion object {
|
||||
@JvmStatic val API_URL = "http://192.168.178.26:8040"
|
||||
var TOKEN = ""
|
||||
}
|
||||
|
||||
private var broadcastReceiver: BroadcastReceiver? = null
|
||||
|
||||
@SuppressLint("HardwareIds")
|
||||
fun checkIfPhoneIsRegistered() {
|
||||
if (TOKEN == "") return;
|
||||
Thread(Runnable {
|
||||
val androidId = Settings.Secure.getString(contentResolver, Settings.Secure.ANDROID_ID)
|
||||
val client = OkHttpClient()
|
||||
val req = Request.Builder()
|
||||
.url("$API_URL/phone/$androidId")
|
||||
.get()
|
||||
.build()
|
||||
val response = client.newCall(req).execute()
|
||||
|
||||
if (response.code != 200) {
|
||||
Snackbar.make(findViewById<FloatingActionButton>(R.id.fab), "Device isn't registered yet.", Snackbar.LENGTH_SHORT)
|
||||
.setBackgroundTint(Color.YELLOW)
|
||||
.setTextColor(Color.BLACK)
|
||||
.show()
|
||||
|
||||
// Register device
|
||||
val phone = Phone(
|
||||
androidId,
|
||||
Build.MODEL,
|
||||
Build.PRODUCT,
|
||||
Build.VERSION.RELEASE,
|
||||
System.getProperty("os.arch")
|
||||
)
|
||||
val moshi = Moshi.Builder().add(KotlinJsonAdapterFactory()).build()
|
||||
val phoneToJson = moshi.adapter(Phone::class.java)
|
||||
val json = phoneToJson.toJson(phone)
|
||||
|
||||
val createPhone = Request.Builder()
|
||||
.url("$API_URL/phone")
|
||||
.post(
|
||||
(json).toRequestBody()
|
||||
)
|
||||
.header("Content-Type", "application/json")
|
||||
.header("token", TOKEN)
|
||||
.build()
|
||||
client.newCall(createPhone).execute()
|
||||
}
|
||||
}).start()
|
||||
}
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
checkPerms()
|
||||
|
||||
super.onCreate(savedInstanceState)
|
||||
setContentView(R.layout.activity_main)
|
||||
setSupportActionBar(findViewById(R.id.toolbar))
|
||||
startService(Intent(this, TrackerService::class.java))
|
||||
|
||||
// Check authorization
|
||||
val backendChecks = Thread(Runnable {
|
||||
val username = findViewById<TextView>(R.id.username).text
|
||||
val password = findViewById<TextView>(R.id.password).text
|
||||
val moshi = Moshi.Builder().add(KotlinJsonAdapterFactory()).build()
|
||||
val jsonToLogin = moshi.adapter(Login::class.java)
|
||||
|
||||
val client = OkHttpClient()
|
||||
val req = Request.Builder()
|
||||
.url("$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
|
||||
|
||||
// Since we are in another thread, we have to get back to the UI thread.
|
||||
this.runOnUiThread(Runnable {
|
||||
findViewById<TextView>(R.id.httpStatus).setTextColor(Color.GREEN)
|
||||
findViewById<TextView>(R.id.httpStatus).text = "CONNECTED"
|
||||
})
|
||||
|
||||
Snackbar.make(findViewById<FloatingActionButton>(R.id.fab), "Login succeeded", Snackbar.LENGTH_SHORT)
|
||||
.setBackgroundTint(Color.GREEN)
|
||||
.setActionTextColor(Color.WHITE)
|
||||
.show()
|
||||
|
||||
checkIfPhoneIsRegistered()
|
||||
} else {
|
||||
// Since we are in another thread, we have to get back to the UI thread.
|
||||
this.runOnUiThread(Runnable {
|
||||
findViewById<TextView>(R.id.httpStatus).setTextColor(Color.RED)
|
||||
findViewById<TextView>(R.id.httpStatus).text = "LOGIN FAILED"
|
||||
})
|
||||
Snackbar.make(findViewById<FloatingActionButton>(R.id.fab), "Login failed", Snackbar.LENGTH_LONG)
|
||||
.setBackgroundTint(Color.RED)
|
||||
.setActionTextColor(Color.WHITE)
|
||||
.show()
|
||||
}
|
||||
})
|
||||
backendChecks.start()
|
||||
|
||||
/**
|
||||
* Here we receive updates from our tracker service.
|
||||
*/
|
||||
this.broadcastReceiver = object : BroadcastReceiver() {
|
||||
@SuppressLint("CutPasteId")
|
||||
override fun onReceive(context: Context, intent: Intent) {
|
||||
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)
|
||||
findViewById<Button>(R.id.signin).isEnabled = false
|
||||
findViewById<Button>(R.id.signin).text = "Signed in"
|
||||
} else {
|
||||
findViewById<TextView>(R.id.httpStatus).text = "OFFLINE"
|
||||
findViewById<TextView>(R.id.httpStatus).setTextColor(Color.RED)
|
||||
findViewById<Button>(R.id.signin).isEnabled = true
|
||||
findViewById<Button>(R.id.signin).text = "Sign in"
|
||||
}
|
||||
}
|
||||
}
|
||||
registerReceiver(broadcastReceiver, IntentFilter("de.nicolasklier.livebeat"))
|
||||
|
||||
findViewById<FloatingActionButton>(R.id.fab).setOnClickListener { view ->
|
||||
Snackbar.make(view, "Replace with your own action", Snackbar.LENGTH_LONG)
|
||||
.setAction("Action", null).show()
|
||||
.setAction("Action", null).show()
|
||||
}
|
||||
}
|
||||
|
||||
private fun checkPerms() {
|
||||
if (ActivityCompat.checkSelfPermission(
|
||||
this,
|
||||
Manifest.permission.ACCESS_FINE_LOCATION
|
||||
) != PackageManager.PERMISSION_GRANTED && ActivityCompat.checkSelfPermission(
|
||||
this,
|
||||
Manifest.permission.ACCESS_COARSE_LOCATION
|
||||
) != PackageManager.PERMISSION_GRANTED
|
||||
) {
|
||||
ActivityCompat.requestPermissions(this, arrayOf(Manifest.permission.ACCESS_FINE_LOCATION), 1)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onStop() {
|
||||
super.onStop()
|
||||
|
||||
if (broadcastReceiver != null) {
|
||||
unregisterReceiver(broadcastReceiver)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -37,4 +215,28 @@ class MainActivity : AppCompatActivity() {
|
||||
else -> super.onOptionsItemSelected(item)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onRequestPermissionsResult(
|
||||
requestCode: Int,
|
||||
permissions: Array<out String>,
|
||||
grantResults: IntArray
|
||||
) {
|
||||
super.onRequestPermissionsResult(requestCode, permissions, grantResults)
|
||||
if (requestCode == 1) {
|
||||
if (grantResults.isNotEmpty() && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
|
||||
if (ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED) {
|
||||
Snackbar.make(findViewById<FloatingActionButton>(R.id.fab), "Location access is allowed", Snackbar.LENGTH_SHORT)
|
||||
.setBackgroundTint(Color.GREEN)
|
||||
.setAction("Action", null).show()
|
||||
}
|
||||
} else {
|
||||
Snackbar.make(findViewById<FloatingActionButton>(R.id.fab), "Location access is required.", Snackbar.LENGTH_INDEFINITE)
|
||||
.setBackgroundTint(Color.RED)
|
||||
.setActionTextColor(Color.WHITE)
|
||||
.setAction("Grant", View.OnClickListener {
|
||||
ActivityCompat.requestPermissions(this, arrayOf(Manifest.permission.ACCESS_FINE_LOCATION), 1)
|
||||
}).show()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
26
android/app/src/main/java/de/nicolasklier/livebeat/Models.kt
Normal file
26
android/app/src/main/java/de/nicolasklier/livebeat/Models.kt
Normal file
@@ -0,0 +1,26 @@
|
||||
package de.nicolasklier.livebeat
|
||||
|
||||
import com.squareup.moshi.JsonClass
|
||||
import com.squareup.moshi.ToJson
|
||||
|
||||
/**
|
||||
* This class represents one singe beat that will be send to the database.
|
||||
*/
|
||||
class Beat(
|
||||
final val token: String, // Token of this phone
|
||||
final val gpsLocation: Array<Double>,
|
||||
final val battery: Int?,
|
||||
final val timestamp: Long
|
||||
) {}
|
||||
|
||||
class Phone(
|
||||
final val androidId: String,
|
||||
final val modelName: String,
|
||||
final val displayName: String,
|
||||
final val operatingSystem: String,
|
||||
final val architecture: String
|
||||
) {}
|
||||
|
||||
class Login(
|
||||
final val token: String
|
||||
) {}
|
||||
@@ -1,31 +0,0 @@
|
||||
package de.nicolasklier.livebeat
|
||||
|
||||
import android.os.Bundle
|
||||
import androidx.fragment.app.Fragment
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.widget.Button
|
||||
import androidx.navigation.fragment.findNavController
|
||||
|
||||
/**
|
||||
* A simple [Fragment] subclass as the second destination in the navigation.
|
||||
*/
|
||||
class SecondFragment : Fragment() {
|
||||
|
||||
override fun onCreateView(
|
||||
inflater: LayoutInflater, container: ViewGroup?,
|
||||
savedInstanceState: Bundle?
|
||||
): View? {
|
||||
// Inflate the layout for this fragment
|
||||
return inflater.inflate(R.layout.fragment_second, container, false)
|
||||
}
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
|
||||
view.findViewById<Button>(R.id.button_second).setOnClickListener {
|
||||
findNavController().navigate(R.id.action_SecondFragment_to_FirstFragment)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,96 +0,0 @@
|
||||
package de.nicolasklier.livebeat;
|
||||
|
||||
import android.app.Notification;
|
||||
import android.app.NotificationChannel;
|
||||
import android.app.NotificationManager;
|
||||
import android.app.PendingIntent;
|
||||
import android.app.Service;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.IntentFilter;
|
||||
import android.graphics.Color;
|
||||
import android.os.IBinder;
|
||||
import android.util.Log;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.core.app.NotificationCompat;
|
||||
|
||||
import com.rabbitmq.client.Channel;
|
||||
import com.rabbitmq.client.Connection;
|
||||
import com.rabbitmq.client.ConnectionFactory;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.concurrent.TimeoutException;
|
||||
|
||||
public class TrackerService extends Service {
|
||||
|
||||
private static final int NOTIF_ID = 1;
|
||||
private static final String NOTIF_CHANNEL_ID = "LiveTracker";
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public IBinder onBind(Intent intent) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int onStartCommand(Intent intent, int flags, int startId) {
|
||||
final Connection[] conn = new Connection[1];
|
||||
|
||||
// This thread only connects to RabbitMQ
|
||||
Thread thread = new Thread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
ConnectionFactory factory = new ConnectionFactory();
|
||||
factory.setUsername("lineage");
|
||||
factory.setPassword("ZSo$X97GQ547JXL7nGq");
|
||||
factory.setVirtualHost("/");
|
||||
factory.setHost("192.168.178.26");
|
||||
factory.setPort(5672);
|
||||
|
||||
try {
|
||||
conn[0] = factory.newConnection();
|
||||
Channel channel = conn[0].createChannel();
|
||||
channel.queueDeclare("Tracker", false, false, false, null);
|
||||
channel.basicPublish("", "Tracker", null, "Test message!!!!!!!!!!!!!!!!!".getBytes());
|
||||
Log.i("RabbitMQ", "run: Published test message");
|
||||
} catch (IOException | TimeoutException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
thread.start();
|
||||
startForeground();
|
||||
|
||||
return super.onStartCommand(intent, flags, startId);
|
||||
}
|
||||
|
||||
private void startForeground() {
|
||||
Intent noticicationIntent = new Intent(this, MainActivity.class);
|
||||
|
||||
PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, noticicationIntent, 0);
|
||||
|
||||
NotificationChannel chan = new NotificationChannel(
|
||||
NOTIF_CHANNEL_ID,
|
||||
"Tracker",
|
||||
NotificationManager.IMPORTANCE_LOW);
|
||||
chan.setLightColor(Color.BLACK);
|
||||
chan.setLockscreenVisibility(Notification.VISIBILITY_SECRET);
|
||||
|
||||
NotificationManager manager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
|
||||
assert manager != null;
|
||||
manager.createNotificationChannel(chan);
|
||||
|
||||
startForeground(NOTIF_ID, new 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)
|
||||
.setColor(Color.BLACK)
|
||||
.build());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,143 @@
|
||||
package de.nicolasklier.livebeat
|
||||
|
||||
import android.Manifest
|
||||
import android.annotation.SuppressLint
|
||||
import android.app.*
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.content.IntentFilter
|
||||
import android.content.pm.PackageManager
|
||||
import android.graphics.Color
|
||||
import android.location.LocationManager
|
||||
import android.os.BatteryManager
|
||||
import android.os.Bundle
|
||||
import android.os.IBinder
|
||||
import android.provider.Settings
|
||||
import android.util.Log
|
||||
import androidx.core.app.ActivityCompat
|
||||
import androidx.core.app.NotificationCompat
|
||||
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 okhttp3.OkHttpClient
|
||||
import okhttp3.Request
|
||||
import java.io.IOException
|
||||
import java.util.concurrent.TimeoutException
|
||||
|
||||
class TrackerService : Service() {
|
||||
|
||||
val conn = arrayOfNulls<Connection>(1)
|
||||
val channel = arrayOfNulls<Channel>(1)
|
||||
|
||||
override fun onBind(intent: Intent): IBinder? {
|
||||
return null
|
||||
}
|
||||
|
||||
private fun connectWithBroker() {
|
||||
// This thread only connects to RabbitMQ
|
||||
val connectionThread = Thread(Runnable {
|
||||
val client = OkHttpClient()
|
||||
val req = Request.Builder()
|
||||
.url(MainActivity.API_URL)
|
||||
.get()
|
||||
.build()
|
||||
|
||||
val factory = ConnectionFactory()
|
||||
factory.username = "lineage"
|
||||
factory.password = "ZSo\$X97GQ547JXL7nGq"
|
||||
factory.virtualHost = "/"
|
||||
factory.host = "nk-home.ddns.net"
|
||||
factory.port = 5672
|
||||
factory.isAutomaticRecoveryEnabled = true
|
||||
try {
|
||||
conn[0] = factory.newConnection()
|
||||
channel[0] = conn[0]?.createChannel()
|
||||
|
||||
val intent = Intent("de.nicolasklier.livebeat")
|
||||
val bundle = Bundle()
|
||||
bundle.putBoolean("statusRabbit", true)
|
||||
bundle.putInt("statusHttp", client.newCall(req).execute().code)
|
||||
intent.putExtras(bundle)
|
||||
this.sendBroadcast(intent)
|
||||
|
||||
channel[0]?.queueDeclare("tracker", 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()
|
||||
}
|
||||
})
|
||||
connectionThread.start()
|
||||
}
|
||||
|
||||
@SuppressLint("HardwareIds")
|
||||
override fun onStartCommand(intent: Intent, flags: Int, startId: Int): Int {
|
||||
val locationManager = getSystemService(Context.LOCATION_SERVICE) as LocationManager
|
||||
|
||||
// Android id as unique identifier
|
||||
val androidId = Settings.Secure.getString(contentResolver, Settings.Secure.ANDROID_ID)
|
||||
|
||||
if (ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED) {
|
||||
locationManager.requestLocationUpdates("gps", 5000, 0f
|
||||
) { location ->
|
||||
Log.i("Location", "Location is: " + location.latitude + " | " + location.longitude)
|
||||
|
||||
/* Get battery */
|
||||
val batteryStatus: Intent? = IntentFilter(Intent.ACTION_BATTERY_CHANGED).let { ifilter ->
|
||||
this@TrackerService.registerReceiver(null, ifilter)
|
||||
}
|
||||
val batteryPct: Float? = batteryStatus?.let { intent ->
|
||||
val level: Int = intent.getIntExtra(BatteryManager.EXTRA_LEVEL, -1)
|
||||
val scale: Int = intent.getIntExtra(BatteryManager.EXTRA_SCALE, -1)
|
||||
level * 100 / scale.toFloat()
|
||||
}
|
||||
|
||||
val beat = Beat(androidId, arrayOf(location.latitude, location.longitude, location.accuracy.toDouble(), location.speed.toDouble()), batteryPct?.toInt(), location.time)
|
||||
val moshi = Moshi.Builder().add(KotlinJsonAdapterFactory()).build()
|
||||
val jsonAdapter: JsonAdapter<Beat> = moshi.adapter(Beat::class.java)
|
||||
|
||||
Thread(Runnable {
|
||||
channel[0]?.basicPublish("", "tracker", null, jsonAdapter.toJson(beat).toByteArray())
|
||||
}).start()
|
||||
}
|
||||
}
|
||||
|
||||
connectWithBroker()
|
||||
startForeground()
|
||||
return super.onStartCommand(intent, flags, startId)
|
||||
}
|
||||
|
||||
private fun startForeground() {
|
||||
val noticicationIntent = Intent(this, MainActivity::class.java)
|
||||
val pendingIntent = PendingIntent.getActivity(this, 0, noticicationIntent, 0)
|
||||
val chan = NotificationChannel(
|
||||
NOTIF_CHANNEL_ID,
|
||||
"Tracker",
|
||||
NotificationManager.IMPORTANCE_LOW)
|
||||
chan.lightColor = Color.BLACK
|
||||
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())
|
||||
}
|
||||
|
||||
companion object {
|
||||
private const val NOTIF_ID = 1
|
||||
private const val NOTIF_CHANNEL_ID = "LiveTracker"
|
||||
}
|
||||
}
|
||||
BIN
android/app/src/main/res/drawable/round_clear_white_18dp.png
Normal file
BIN
android/app/src/main/res/drawable/round_clear_white_18dp.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 221 B |
@@ -15,12 +15,127 @@
|
||||
android:id="@+id/toolbar"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="?attr/actionBarSize"
|
||||
android:background="?attr/colorPrimary"
|
||||
android:background="@color/black"
|
||||
app:popupTheme="@style/Theme.Livebeat.PopupOverlay" />
|
||||
|
||||
</com.google.android.material.appbar.AppBarLayout>
|
||||
|
||||
<include layout="@layout/content_main" />
|
||||
<LinearLayout
|
||||
android:id="@+id/linearLayout"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:background="@color/black2"
|
||||
android:orientation="vertical"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent">
|
||||
|
||||
<Space
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="50dp" />
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="30dp"
|
||||
android:gravity="center"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/httpText"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:gravity="center|center_horizontal|center_vertical"
|
||||
android:text="Backend"
|
||||
android:textAlignment="center"
|
||||
android:textColor="@color/white" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/httpStatus"
|
||||
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="viewStart"
|
||||
android:textAllCaps="true"
|
||||
android:textColor="@color/orange"
|
||||
android:textSize="14sp" />
|
||||
|
||||
</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" />
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_marginLeft="80dp"
|
||||
android:layout_marginRight="80dp"
|
||||
android:orientation="vertical">
|
||||
|
||||
<EditText
|
||||
android:id="@+id/username"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:backgroundTint="@color/white"
|
||||
android:ems="10"
|
||||
android:inputType="textPersonName"
|
||||
android:text="admin"
|
||||
android:textColor="@color/white" />
|
||||
|
||||
<EditText
|
||||
android:id="@+id/password"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:backgroundTint="@color/white"
|
||||
android:ems="10"
|
||||
android:hint="Secret password"
|
||||
android:inputType="textPassword"
|
||||
android:text="$1KDaNCDlyXAOg"
|
||||
android:textColor="@color/white"
|
||||
android:textColorHint="@color/white" />
|
||||
|
||||
<Button
|
||||
android:id="@+id/signin"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="16dp"
|
||||
android:text="Sign in" />
|
||||
</LinearLayout>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<com.google.android.material.floatingactionbutton.FloatingActionButton
|
||||
android:id="@+id/fab"
|
||||
@@ -28,6 +143,8 @@
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="bottom|end"
|
||||
android:layout_margin="@dimen/fab_margin"
|
||||
app:srcCompat="@android:drawable/ic_dialog_email" />
|
||||
app:backgroundTint="@color/orange"
|
||||
app:rippleColor="#FFFFFF"
|
||||
app:srcCompat="@drawable/round_clear_white_18dp" />
|
||||
|
||||
</androidx.coordinatorlayout.widget.CoordinatorLayout>
|
||||
@@ -1,19 +0,0 @@
|
||||
<?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"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
app:layout_behavior="@string/appbar_scrolling_view_behavior">
|
||||
|
||||
<fragment
|
||||
android:id="@+id/nav_host_fragment"
|
||||
android:name="androidx.navigation.fragment.NavHostFragment"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="0dp"
|
||||
app:defaultNavHost="true"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintLeft_toLeftOf="parent"
|
||||
app:layout_constraintRight_toRightOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:navGraph="@navigation/nav_graph" />
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
@@ -1,28 +0,0 @@
|
||||
<?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=".FirstFragment">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/textview_first"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/hello_first_fragment"
|
||||
app:layout_constraintBottom_toTopOf="@id/button_first"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
<Button
|
||||
android:id="@+id/button_first"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/next"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/textview_first" />
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
@@ -1,27 +0,0 @@
|
||||
<?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=".SecondFragment">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/textview_second"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
app:layout_constraintBottom_toTopOf="@id/button_second"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
<Button
|
||||
android:id="@+id/button_second"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/previous"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/textview_second" />
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
@@ -1,28 +0,0 @@
|
||||
<?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>
|
||||
@@ -1,16 +0,0 @@
|
||||
<resources xmlns:tools="http://schemas.android.com/tools">
|
||||
<!-- Base application theme. -->
|
||||
<style name="Theme.Livebeat" parent="Theme.MaterialComponents.DayNight.DarkActionBar">
|
||||
<!-- Primary brand color. -->
|
||||
<item name="colorPrimary">@color/purple_200</item>
|
||||
<item name="colorPrimaryVariant">@color/purple_700</item>
|
||||
<item name="colorOnPrimary">@color/black</item>
|
||||
<!-- Secondary brand color. -->
|
||||
<item name="colorSecondary">@color/teal_200</item>
|
||||
<item name="colorSecondaryVariant">@color/teal_200</item>
|
||||
<item name="colorOnSecondary">@color/black</item>
|
||||
<!-- Status bar color. -->
|
||||
<item name="android:statusBarColor" tools:targetApi="l">?attr/colorPrimaryVariant</item>
|
||||
<!-- Customize your theme here. -->
|
||||
</style>
|
||||
</resources>
|
||||
@@ -1,10 +1,8 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<color name="purple_200">#FFBB86FC</color>
|
||||
<color name="purple_500">#FF6200EE</color>
|
||||
<color name="purple_700">#FF3700B3</color>
|
||||
<color name="teal_200">#FF03DAC5</color>
|
||||
<color name="teal_700">#FF018786</color>
|
||||
<color name="black">#FF000000</color>
|
||||
<color name="black">#0D0D0D</color>
|
||||
<color name="black2">#1C1C1C</color>
|
||||
<color name="green">#B4D9C4</color>
|
||||
<color name="orange">#F24B0F</color>
|
||||
<color name="white">#FFFFFFFF</color>
|
||||
</resources>
|
||||
@@ -1,16 +1,16 @@
|
||||
<resources xmlns:tools="http://schemas.android.com/tools">
|
||||
<!-- Base application theme. -->
|
||||
<style name="Theme.Livebeat" parent="Theme.MaterialComponents.DayNight.DarkActionBar">
|
||||
<style name="Theme.Livebeat" parent="Theme.AppCompat.Light">
|
||||
<!-- Primary brand color. -->
|
||||
<item name="colorPrimary">@color/purple_500</item>
|
||||
<item name="colorPrimaryVariant">@color/purple_700</item>
|
||||
<item name="colorPrimary">@color/black</item>
|
||||
<item name="colorPrimaryVariant">@color/black2</item>
|
||||
<item name="colorOnPrimary">@color/white</item>
|
||||
<!-- Secondary brand color. -->
|
||||
<item name="colorSecondary">@color/teal_200</item>
|
||||
<item name="colorSecondaryVariant">@color/teal_700</item>
|
||||
<item name="colorOnSecondary">@color/black</item>
|
||||
<item name="colorSecondary">@color/orange</item>
|
||||
<item name="colorSecondaryVariant">@color/orange</item>
|
||||
<item name="colorOnSecondary">@color/white</item>
|
||||
<!-- Status bar color. -->
|
||||
<item name="android:statusBarColor" tools:targetApi="l">?attr/colorPrimaryVariant</item>
|
||||
<item name="android:statusBarColor">@color/black2</item>
|
||||
<!-- Customize your theme here. -->
|
||||
</style>
|
||||
|
||||
@@ -20,6 +20,5 @@
|
||||
</style>
|
||||
|
||||
<style name="Theme.Livebeat.AppBarOverlay" parent="ThemeOverlay.AppCompat.Dark.ActionBar" />
|
||||
|
||||
<style name="Theme.Livebeat.PopupOverlay" parent="ThemeOverlay.AppCompat.Light" />
|
||||
</resources>
|
||||
Reference in New Issue
Block a user