Initial Commit

SIMTAK 1.0 Stable Version

Developed to use with Armatak addon for ARMA 3
Copyright ©2024 Valmo Trindade
All rights reserved.

Take a look on the main project: https://github.com/valmojr/armatak
This commit is contained in:
andonyth
2024-12-05 03:13:06 -03:00
commit 80f49044f6
73 changed files with 3081 additions and 0 deletions

1
app/.gitignore vendored Normal file
View File

@@ -0,0 +1 @@
/build

55
app/build.gradle.kts Normal file
View File

@@ -0,0 +1,55 @@
plugins {
alias(libs.plugins.android.application)
alias(libs.plugins.jetbrains.kotlin.android)
}
android {
namespace = "com.armatak.simtak"
compileSdk = 34
defaultConfig {
applicationId = "com.armatak.simtak"
minSdk = 29
targetSdk = 34
versionCode = 1
versionName = "1.0"
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
}
buildTypes {
release {
isMinifyEnabled = false
proguardFiles(
getDefaultProguardFile("proguard-android-optimize.txt"),
"proguard-rules.pro"
)
}
}
compileOptions {
sourceCompatibility = JavaVersion.VERSION_1_8
targetCompatibility = JavaVersion.VERSION_1_8
}
kotlinOptions {
jvmTarget = "1.8"
}
viewBinding {
enable = true
}
}
dependencies {
implementation(libs.androidx.core.ktx)
implementation(libs.androidx.appcompat)
implementation(libs.material)
implementation(libs.androidx.activity)
implementation(libs.androidx.constraintlayout)
implementation(libs.okhttp)
//Vision - Qr/BarCodeScanner
implementation(libs.play.services.vision)
testImplementation(libs.junit)
androidTestImplementation(libs.androidx.junit)
androidTestImplementation(libs.androidx.espresso.core)
}

21
app/proguard-rules.pro vendored Normal file
View File

@@ -0,0 +1,21 @@
# Add project specific ProGuard rules here.
# You can control the set of applied configuration files using the
# proguardFiles setting in build.gradle.
#
# For more details, see
# http://developer.android.com/guide/developing/tools/proguard.html
# If your project uses WebView with JS, uncomment the following
# and specify the fully qualified class name to the JavaScript interface
# class:
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
# public *;
#}
# Uncomment this to preserve the line number information for
# debugging stack traces.
#-keepattributes SourceFile,LineNumberTable
# If you keep the line number information, uncomment this to
# hide the original source file name.
#-renamesourcefileattribute SourceFile

View File

@@ -0,0 +1,24 @@
package com.armatak.simtak
import androidx.test.platform.app.InstrumentationRegistry
import androidx.test.ext.junit.runners.AndroidJUnit4
import org.junit.Test
import org.junit.runner.RunWith
import org.junit.Assert.*
/**
* Instrumented test, which will execute on an Android device.
*
* See [testing documentation](http://d.android.com/tools/testing).
*/
@RunWith(AndroidJUnit4::class)
class ExampleInstrumentedTest {
@Test
fun useAppContext() {
// Context of the app under test.
val appContext = InstrumentationRegistry.getInstrumentation().targetContext
assertEquals("com.armatak.simtak", appContext.packageName)
}
}

View File

@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:tools="http://schemas.android.com/tools"
xmlns:android="http://schemas.android.com/apk/res/android">
<uses-permission android:name="android.permission.ACCESS_MOCK_LOCATION"
tools:ignore="ProtectedPermissions" />
</manifest>

View File

@@ -0,0 +1,60 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">
<uses-feature
android:name="android.hardware.camera"
android:required="true" />
<uses-feature android:name="android.hardware.camera.autofocus"
android:required="false"/>
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE"/>
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_LOCATION"/>
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/>
<application
android:allowBackup="true"
android:dataExtractionRules="@xml/data_extraction_rules"
android:fullBackupContent="@xml/backup_rules"
android:icon="@drawable/appicon_simtak"
android:usesCleartextTraffic="true"
android:label="@string/app_name"
android:roundIcon="@drawable/appicon_simtak"
android:supportsRtl="true"
android:theme="@style/Theme.SIMTAK"
tools:targetApi="31">
<activity
android:name=".trackerLog.TrackerLogActivity"
android:exported="false" />
<activity
android:name=".ScannerActivity"
android:exported="false" />
<activity
android:name=".HomeActivity"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<service
android:name=".LocationSimulationService"
android:exported="true"
android:foregroundServiceType="location"
android:permission="android.permission.ACCESS_MOCK_LOCATION">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</service>
</application>
</manifest>

View File

@@ -0,0 +1,182 @@
package com.armatak.simtak
import android.Manifest
import android.app.Dialog
import android.content.Intent
import android.content.pm.PackageManager
import android.graphics.Color
import android.graphics.drawable.ColorDrawable
import android.net.Uri
import android.os.Build
import android.os.Bundle
import android.provider.Settings
import android.view.Window
import android.widget.Button
import android.widget.TextView
import android.widget.Toast
import androidx.activity.enableEdgeToEdge
import androidx.activity.result.ActivityResultLauncher
import androidx.activity.result.contract.ActivityResultContracts
import androidx.appcompat.app.AppCompatActivity
import androidx.core.content.ContextCompat
import androidx.core.view.ViewCompat
import androidx.core.view.WindowInsetsCompat
import com.armatak.simtak.databinding.ActivityHomeBinding
import com.armatak.simtak.trackerLog.TrackerLogActivity
class HomeActivity : AppCompatActivity() {
private lateinit var binding: ActivityHomeBinding
private var requestCamera: ActivityResultLauncher<String>? = null
private var requestPermissionLauncher: ActivityResultLauncher<String>? = null
private var requested = false
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityHomeBinding.inflate(layoutInflater)
setContentView(binding.root)
enableEdgeToEdge()
ViewCompat.setOnApplyWindowInsetsListener(binding.root) { v, insets ->
val systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars())
v.setPadding(systemBars.left, systemBars.top, systemBars.right, systemBars.bottom)
insets
}
requestCamera = registerForActivityResult(ActivityResultContracts.RequestPermission()) {
if (it) {
initScan()
} else {
Toast.makeText(
this,
"Sem permissão para acessar a camera. Permita o acesso para continuar",
Toast.LENGTH_LONG
).show()
}
}
requestPermissionLauncher = registerForActivityResult(
ActivityResultContracts.RequestPermission(),
) { isGranted: Boolean ->
if (isGranted) {
Toast.makeText(baseContext, "Notificações Habilitadas", Toast.LENGTH_SHORT).show()
requestCamera?.launch(Manifest.permission.CAMERA)
} else {
Toast.makeText(
baseContext,
"Por favor confirme a permissão",
Toast.LENGTH_SHORT
).show()
}
}
initUI()
}
private fun validateUrl() {
val etServerAddressLayout = binding.etServerAddressLayout
val etServerAddress = binding.etServerAddress
val serverAddress = etServerAddress.text.toString()
val regexPatterns = listOf(
"^ws://\\b(?:(?:2(?:[0-4][0-9]|5[0-5])|[0-1]?[0-9]?[0-9])\\.){3}(?:(?:2([0-4][0-9]|5[0-5])|[0-1]?[0-9]?[0-9]))\\b:[0-9]+\$",
"^ws://([A-Za-z0-9]+(\\.[A-Za-z0-9]+)+)\$"
)
val allowed = regexPatterns.any { patterns ->
Regex(patterns).matches(serverAddress)
}
if(allowed) {
requested = false
val intent = Intent(this, TrackerLogActivity::class.java)
intent.putExtra("webSocketUrl", serverAddress)
startActivity(intent)
} else {
requested = false
etServerAddressLayout.error = "Server Address need to be a valid URL"
}
}
private fun initScan() {
startActivity(Intent(this, ScannerActivity::class.java))
}
private fun initUI() {
binding.btnScanQrCode.setOnClickListener {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
if (ContextCompat.checkSelfPermission(
this,
Manifest.permission.POST_NOTIFICATIONS
) ==
PackageManager.PERMISSION_GRANTED
) {
requestCamera?.launch(Manifest.permission.CAMERA)
} else if (shouldShowRequestPermissionRationale(Manifest.permission.POST_NOTIFICATIONS)) {
requestNotificationByDialog()
} else {
requestPermissionLauncher?.launch(Manifest.permission.POST_NOTIFICATIONS)
}
}
}
binding.btnConnectToServer.setOnClickListener {
val serverAddress = binding.etServerAddress.text.toString()
if (!requested && serverAddress.isNotBlank()){
validateUrl()
} else {
binding.etServerAddressLayout.error = "This Input cannot be blank"
}
}
configureFooterLinks()
}
private fun requestNotificationByDialog() {
val dialog = Dialog(this)
dialog.requestWindowFeature(Window.FEATURE_NO_TITLE)
dialog.setContentView(R.layout.dialog_alert)
dialog.window?.setBackgroundDrawable(ColorDrawable(Color.TRANSPARENT))
val tvTitle: TextView = dialog.findViewById(R.id.title)
val tvMessage: TextView = dialog.findViewById(R.id.message)
val btnCancel: Button = dialog.findViewById(R.id.btnCancel)
val btnAccept: Button = dialog.findViewById(R.id.btnAccept)
btnCancel.text = getString(R.string.cancel)
btnCancel.setOnClickListener {
dialog.dismiss()
}
tvTitle.text = getString(R.string.allowNotificationsPermission)
tvMessage.text = getString(R.string.needNotificationsPermissionMessage)
btnAccept.text = getString(R.string.goToSettings)
btnAccept.setOnClickListener {
val intent = Intent(Settings.ACTION_APP_NOTIFICATION_SETTINGS)
intent.putExtra(Settings.EXTRA_APP_PACKAGE, this.packageName)
startActivity(intent)
dialog.dismiss()
}
dialog.show()
}
private fun configureFooterLinks() {
binding.btnGithubProject.setOnClickListener {
openLink(getString(R.string.githubProjectUrl))
}
binding.btnWiki.setOnClickListener {
openLink(getString(R.string.wikiUrl))
}
binding.btnDiscord.setOnClickListener {
openLink(getString(R.string.discordUrl))
}
binding.btnSteamProject.setOnClickListener {
openLink(getString(R.string.steamUrl))
}
}
private fun openLink(url: String) {
val intent = Intent(Intent.ACTION_VIEW, Uri.parse(url))
startActivity(intent)
}
}

View File

@@ -0,0 +1,237 @@
package com.armatak.simtak
import android.app.NotificationManager
import android.app.Service
import android.content.Intent
import android.location.Location
import android.location.LocationManager
import android.location.provider.ProviderProperties
import android.os.Binder
import android.os.Build
import android.os.Handler
import android.os.IBinder
import android.os.Looper
import android.os.SystemClock
import android.util.Log
import com.armatak.simtak.core.Util.createNotificationChannel
import com.armatak.simtak.core.Util.getActualTime
import com.armatak.simtak.core.Util.getMockLocationStoppedNotification
import com.armatak.simtak.core.Util.getNeedReconnectNotification
import com.armatak.simtak.core.Util.getRunningNotification
import com.armatak.simtak.core.Util.getServiceDestroyedNotification
import com.armatak.simtak.core.Util.getStartedServiceNotification
import com.armatak.simtak.trackerLog.data.models.ConnectionStatus
import com.armatak.simtak.trackerLog.data.models.LogModel
import com.armatak.simtak.trackerLog.data.models.LogTypes
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.collectLatest
import kotlinx.coroutines.launch
import org.json.JSONObject
private const val TAG = "LocationSimulationService"
class LocationSimulationService : Service() {
private val binder = LocationMockBinder()
private lateinit var webSocketClient: WebSocketClient
private var logTrackerMutableList = mutableListOf<LogModel>()
private var lastLineId = 0
private var connectionAttemps = 0
private val _connectionStatus = MutableStateFlow<ConnectionStatus>(ConnectionStatus.InitialValue)
private val connectionStatus : StateFlow<ConnectionStatus> = _connectionStatus
private val _logTracker = MutableStateFlow(emptyList<LogModel>())
private val logTracker : StateFlow<List<LogModel>> = _logTracker
init {
Log.d(TAG, "$TAG, initialized")
}
private val socketListener = object : WebSocketClient.SocketListener {
override fun onMessage(message: String) {
try {
if (message[0] == '{') {
val jsonObject = JSONObject(message)
val latitude = jsonObject.getDouble("latitude")
val longitude = jsonObject.getDouble("longitude")
val bearing = jsonObject.getDouble("bearing")
simulateLocation(latitude, longitude, bearing)
addEntryToLog(message, LogTypes.Normal)
} else {
Log.e(TAG, "Non an JsonObject, text: $message")
addEntryToLog("Non an JsonObject, text: $message", LogTypes.Warning)
}
} catch (e: Exception) {
Log.e(TAG, e.localizedMessage, e)
addEntryToLog(e.localizedMessage, LogTypes.Error)
}
}
}
fun addEntryToLog(message: String?, type: LogTypes) {
lastLineId += 1
logTrackerMutableList.add(
LogModel(
lastLineId,
getActualTime(),
message?:"null",
type
)
)
val newList = logTrackerMutableList.toList()
_logTracker.value = newList
}
fun getLog(): StateFlow<List<LogModel>> {
return logTracker
}
fun getConnectionStatus(): StateFlow<ConnectionStatus> {
return connectionStatus
}
fun connectToServer(url: String){
CoroutineScope(Dispatchers.IO).launch {
try {
connectionAttemps ++
webSocketClient = WebSocketClient.getInstance()
addEntryToLog("Server Address: $url", LogTypes.NetworkOperation)
webSocketClient.setSocketUrl(url)
webSocketClient.setListener(socketListener)
webSocketClient.connect()
_connectionStatus.value = ConnectionStatus.Connected
addEntryToLog("Connection Server Success", LogTypes.NetworkOperation)
} catch (e: Exception) {
Log.e(TAG, e.localizedMessage, e)
when (e.localizedMessage){
"Expected URL scheme 'http' or 'https' but no scheme was found for test u..." -> {
addEntryToLog("Expected ws:// or wss:// scheme, this is only for debug", LogTypes.Warning)
_connectionStatus.value = ConnectionStatus.Disconnected
}
else -> {
if (connectionAttemps < 6){
addEntryToLog("Attemp: $connectionAttemps \nError:${e.localizedMessage}", LogTypes.Warning)
_connectionStatus.value = ConnectionStatus.OnReconnect
Handler(Looper.getMainLooper()).postDelayed({
connectToServer(url)
}, 1500)
} else {
addEntryToLog("Exceed Connection Attemps", LogTypes.Error)
connectionAttemps = -1
_connectionStatus.value = ConnectionStatus.Awaiting
}
}
}
}
}
}
override fun onBind(intent: Intent): IBinder {
return binder
}
private fun simulateLocation(latitude: Double, longitude: Double, bearing: Double) {
val locationManager = baseContext.getSystemService(LOCATION_SERVICE) as LocationManager
// Create a Location Object
val location = Location(LocationManager.GPS_PROVIDER)
location.latitude = latitude
location.longitude = longitude
location.accuracy = 3f
location.altitude = 0.0
location.time = System.currentTimeMillis()
location.bearing = bearing.toFloat()
location.setBearingAccuracyDegrees(0.1F)
location.setVerticalAccuracyMeters(0.1F)
location.setSpeedAccuracyMetersPerSecond(0.01F)
location.elapsedRealtimeNanos = SystemClock.elapsedRealtimeNanos()
var powerUsage = 3
var accuracy = 5
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S){
powerUsage = ProviderProperties.POWER_USAGE_LOW
accuracy = ProviderProperties.ACCURACY_COARSE
}
//Create Test Provider
locationManager.addTestProvider(
LocationManager.GPS_PROVIDER,
false,
false,
false,
false,
false,
true,
true,
powerUsage,
accuracy
)
// Enable Mock Provider
locationManager.setTestProviderEnabled(LocationManager.GPS_PROVIDER, true)
// Mock Location on System
locationManager.setTestProviderLocation(LocationManager.GPS_PROVIDER, location)
}
fun stopByActivity() {
webSocketClient.disconnect()
_connectionStatus.value = ConnectionStatus.Disconnected
addEntryToLog("Connection Stopped", LogTypes.NetworkOperation)
val locationManager = baseContext.getSystemService(LOCATION_SERVICE) as LocationManager
locationManager.removeTestProvider(LocationManager.GPS_PROVIDER)
addEntryToLog("MockLocation Stopped", LogTypes.Warning)
this.stopSelf()
}
override fun onCreate() {
super.onCreate()
val manager = getSystemService(NOTIFICATION_SERVICE) as NotificationManager
createNotificationChannel(manager)
CoroutineScope(Dispatchers.Default).launch{
notifyStatusByPushNotification()
}
}
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
val notification = getStartedServiceNotification(this)
startForeground(5142, notification)
return START_STICKY
}
private suspend fun notifyStatusByPushNotification(){
getConnectionStatus().collectLatest { status ->
when(status){
ConnectionStatus.Awaiting -> {
val notification = getNeedReconnectNotification(this)
val manager = getSystemService(NOTIFICATION_SERVICE) as NotificationManager
manager.notify(5142, notification)
}
ConnectionStatus.Connected -> {
val notification = getRunningNotification(this)
val manager = getSystemService(NOTIFICATION_SERVICE) as NotificationManager
manager.notify(5142, notification)
}
ConnectionStatus.Disconnected -> {
val notification = getMockLocationStoppedNotification(this)
val manager = getSystemService(NOTIFICATION_SERVICE) as NotificationManager
manager.notify(5142, notification)
}
else -> {}
}
}
}
override fun onDestroy() {
super.onDestroy()
val notification = getServiceDestroyedNotification(this)
val manager = getSystemService(NOTIFICATION_SERVICE) as NotificationManager
manager.notify(5142, notification)
Log.e(TAG, "Service destroyed")
}
inner class LocationMockBinder: Binder(){
fun getService() = this@LocationSimulationService
}
}

View File

@@ -0,0 +1,114 @@
package com.armatak.simtak
import android.annotation.SuppressLint
import android.content.Intent
import android.os.Bundle
import android.os.Handler
import android.os.Looper
import android.view.SurfaceHolder
import android.widget.Toast
import androidx.appcompat.app.AppCompatActivity
import androidx.lifecycle.lifecycleScope
import com.armatak.simtak.databinding.ActivityScannerBinding
import com.armatak.simtak.trackerLog.TrackerLogActivity
import com.google.android.gms.vision.CameraSource
import com.google.android.gms.vision.Detector
import com.google.android.gms.vision.barcode.Barcode
import com.google.android.gms.vision.barcode.BarcodeDetector
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import java.io.IOException
class ScannerActivity : AppCompatActivity() {
private lateinit var binding: ActivityScannerBinding
private var tentativa = false
private lateinit var barcodeDetector: BarcodeDetector
private lateinit var cameraSource: CameraSource
var intentData = ""
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityScannerBinding.inflate(layoutInflater)
setContentView(binding.root)
initScanBarcode()
}
private fun initScanBarcode() {
barcodeDetector = BarcodeDetector.Builder(this)
.setBarcodeFormats(Barcode.QR_CODE)
.build()
cameraSource = CameraSource.Builder(this, barcodeDetector)
.setRequestedPreviewSize(1080, 1080)
.setAutoFocusEnabled(true)
.setFacing(CameraSource.CAMERA_FACING_BACK)
.build()
binding.surfaceView.holder.addCallback(object : SurfaceHolder.Callback {
@SuppressLint("MissingPermission")
override fun surfaceCreated(holder: SurfaceHolder) {
try {
cameraSource.start(binding.surfaceView.holder)
} catch (e: IOException) {
e.printStackTrace()
}
}
override fun surfaceChanged(
holder: SurfaceHolder,
format: Int,
width: Int,
height: Int
) {
}
override fun surfaceDestroyed(holder: SurfaceHolder) {
cameraSource.stop()
}
})
barcodeDetector.setProcessor(object : Detector.Processor<Barcode> {
override fun release() {
Toast.makeText(applicationContext, "Scanner was stopped", Toast.LENGTH_SHORT).show()
}
override fun receiveDetections(detections: Detector.Detections<Barcode>) {
val barcodes = detections.detectedItems
if (barcodes.size() != 0) {
Thread.sleep(300)
intentData = barcodes.valueAt(0).displayValue
if (!tentativa) {
initTrackerActivity(intentData)
tentativa = true
}
}
}
})
}
private fun initTrackerActivity(url: String?) {
lifecycleScope.launch(Dispatchers.IO) {
if (url != null) {
val intent = Intent(this@ScannerActivity, TrackerLogActivity::class.java)
intent.putExtra("webSocketUrl", url)
startActivity(intent)
Handler(Looper.getMainLooper()).postDelayed(
{ tentativa = false },
2000
)
} else {
withContext(Dispatchers.Main) {
Toast.makeText(baseContext, "Try Again, Scan Error", Toast.LENGTH_SHORT).show()
}
Handler(Looper.getMainLooper()).postDelayed(
{ tentativa = false },
1500
)
}
}
}
}

View File

@@ -0,0 +1,121 @@
package com.armatak.simtak
import android.util.Log
import okhttp3.OkHttpClient
import okhttp3.Request
import okhttp3.Response
import okhttp3.WebSocketListener
class WebSocketClient {
private lateinit var webSocket: okhttp3.WebSocket
private var socketListener: SocketListener? = null
private var socketUrl = ""
private var shouldReconnect = true
private var client: OkHttpClient? = null
companion object {
private lateinit var instance: WebSocketClient
@JvmStatic
@Synchronized
//This function gives singleton instance of WebSocket.
fun getInstance(): WebSocketClient {
synchronized(WebSocketClient::class) {
if (!::instance.isInitialized) {
instance = WebSocketClient()
}
}
return instance
}
}
fun setListener(listener: SocketListener) {
this.socketListener = listener
}
fun setSocketUrl(socketUrl: String) {
this.socketUrl = socketUrl
}
private fun initWebSocket() {
Log.e("socketCheck", "initWebSocket() socketurl = $socketUrl")
client = OkHttpClient()
val request = Request.Builder().url(url = socketUrl).build()
webSocket = client!!.newWebSocket(request, webSocketListener)
//this must me done else memory leak will be caused
client!!.dispatcher.executorService.shutdown()
}
fun connect() {
Log.e("socketCheck", "connect()")
shouldReconnect = true
initWebSocket()
}
fun reconnect() {
Log.e("socketCheck", "reconnect()")
initWebSocket()
}
//send
@Suppress("unused")
fun sendMessage(message: String) {
Log.e("socketCheck", "sendMessage($message)")
if (::webSocket.isInitialized) webSocket.send(message)
}
//We can close socket by two way:
//1. websocket.webSocket.close(1000, "Dont need connection")
//This attempts to initiate a graceful shutdown of this web socket.
//Any already-enqueued messages will be transmitted before the close message is sent but
//subsequent calls to send will return false and their messages will not be enqueued.
//2. websocket.cancel()
//This immediately and violently release resources held by this web socket,
//discarding any enqueued messages.
//Both does nothing if the web socket has already been closed or canceled.
fun disconnect() {
if (::webSocket.isInitialized) webSocket.close(1000, "Do not need connection anymore.")
shouldReconnect = false
}
interface SocketListener {
fun onMessage(message: String)
}
private val webSocketListener = object : WebSocketListener() {
//called when connection succeeded
//we are sending a message just after the socket is opened
override fun onOpen(webSocket: okhttp3.WebSocket, response: Response) {
Log.e("socketCheck", "onOpen()")
}
//called when text message received
override fun onMessage(webSocket: okhttp3.WebSocket, text: String) {
socketListener?.onMessage(text)
}
//called when binary message received
override fun onClosing(webSocket: okhttp3.WebSocket, code: Int, reason: String) {
Log.e("socketCheck", "onClosing()")
}
override fun onClosed(webSocket: okhttp3.WebSocket, code: Int, reason: String) {
//called when no more messages and the connection should be released
Log.e("socketCheck", "onClosed()")
if (shouldReconnect) reconnect()
}
override fun onFailure(
webSocket: okhttp3.WebSocket, t: Throwable, response: Response?
) {
Log.e("socketCheck", "onFailure()")
if (shouldReconnect) reconnect()
}
}
}

View File

@@ -0,0 +1,65 @@
package com.armatak.simtak.core
import android.app.Notification
import android.app.NotificationChannel
import android.app.NotificationManager
import android.content.Context
import androidx.core.app.NotificationCompat
import com.armatak.simtak.R
import java.time.LocalDateTime
import java.time.format.DateTimeFormatter
object Util {
const val CHANNEL_ID = "websocketChannel"
fun getActualTime(): String {
val now = LocalDateTime.now()
val formatter = DateTimeFormatter.ofPattern("HH:mm:ss:SSS")
return now.format(formatter)
}
fun createNotificationChannel(manager: NotificationManager) {
val websocketChannel = NotificationChannel(
CHANNEL_ID, "websocketChannelName",
NotificationManager.IMPORTANCE_HIGH
)
websocketChannel.description = "websocketChannelDescription"
websocketChannel.enableVibration(true)
manager.createNotificationChannel(websocketChannel)
}
fun getStartedServiceNotification(context: Context): Notification {
return NotificationCompat.Builder(context, CHANNEL_ID)
.setContentTitle("Service Started")
.setContentText("Service is ready to Start Tracking")
.setSmallIcon(R.drawable.appicon_simtak)
.build()
}
fun getRunningNotification(context: Context): Notification {
return NotificationCompat.Builder(context, CHANNEL_ID)
.setContentTitle("Mocking Location")
.setContentText("Service is running")
.setSmallIcon(R.drawable.appicon_simtak)
.build()
}
fun getNeedReconnectNotification(context: Context): Notification{
return NotificationCompat.Builder(context, CHANNEL_ID)
.setContentTitle("Need restart server connection")
.setContentText("Connection attempts failed. Check your network/server and try again")
.setSmallIcon(R.drawable.appicon_simtak)
.build()
}
fun getMockLocationStoppedNotification(context: Context): Notification{
return NotificationCompat.Builder(context, CHANNEL_ID)
.setContentTitle("Mock Location Stopped")
.setContentText("Disconnect from server")
.setSmallIcon(R.drawable.appicon_simtak)
.build()
}
fun getServiceDestroyedNotification(context: Context): Notification{
return NotificationCompat.Builder(context, CHANNEL_ID)
.setContentTitle("Service Destroyed")
.setSmallIcon(R.drawable.appicon_simtak)
.build()
}
}

View File

@@ -0,0 +1,277 @@
package com.armatak.simtak.trackerLog
import android.Manifest
import android.app.Dialog
import android.content.ComponentName
import android.content.Context
import android.content.Intent
import android.content.ServiceConnection
import android.graphics.Color
import android.graphics.drawable.ColorDrawable
import android.os.Build
import android.os.Bundle
import android.os.IBinder
import android.view.Window
import android.widget.Button
import android.widget.TextView
import android.widget.Toast
import androidx.activity.enableEdgeToEdge
import androidx.activity.result.ActivityResultLauncher
import androidx.activity.result.contract.ActivityResultContracts
import androidx.appcompat.app.AppCompatActivity
import androidx.core.view.ViewCompat
import androidx.core.view.WindowInsetsCompat
import androidx.lifecycle.lifecycleScope
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import com.armatak.simtak.LocationSimulationService
import com.armatak.simtak.R
import com.armatak.simtak.databinding.ActivityTrackerLogBinding
import com.armatak.simtak.trackerLog.data.adapters.AdapterLogTracker
import com.armatak.simtak.trackerLog.data.models.ConnectionStatus
import kotlinx.coroutines.flow.collectLatest
import kotlinx.coroutines.launch
class TrackerLogActivity : AppCompatActivity() {
private var connectedToServer: Boolean = false
private lateinit var binding: ActivityTrackerLogBinding
private var url: String = ""
private lateinit var locationPermissionRequest : ActivityResultLauncher<Array<String>>
private var serviceStarted = false
private var rvAdapter = AdapterLogTracker()
private var lastThreeElementsAreVisible = false
private lateinit var mService: LocationSimulationService
private var mBound = false
private val mConnection = object : ServiceConnection{
override fun onServiceConnected(className: ComponentName?, binder : IBinder?) {
val service = binder as LocationSimulationService.LocationMockBinder
mService = service.getService()
mBound = true
initCollectors()
}
override fun onServiceDisconnected(name: ComponentName?) {
mBound = false
}
}
private fun initCollectors() {
lifecycleScope.launch {
if (mBound){
mService.getLog().collect { logModelList ->
rvAdapter.submitList(logModelList)
if (lastThreeElementsAreVisible){
binding.rvLogTracker.smoothScrollToPosition(rvAdapter.itemCount - 1)
}
}
}
}
lifecycleScope.launch {
if (mBound){
mService.getConnectionStatus().collectLatest {
when (it){
ConnectionStatus.Connected -> {
binding.txtServerConnectionStatus.text = getString(R.string.serverConnectionStatusPropertyFormat, "Connected")
}
ConnectionStatus.Disconnected -> {
binding.txtServerConnectionStatus.text = getString(R.string.serverConnectionStatusPropertyFormat, "Disconnected")
}
ConnectionStatus.OnReconnect -> {
binding.txtServerConnectionStatus.text = getString(R.string.serverConnectionStatusPropertyFormat, "OnReconnect")
}
ConnectionStatus.Awaiting -> {
binding.txtServerConnectionStatus.text = getString(R.string.serverConnectionStatusPropertyFormat, "Awaiting")
showConnectionErrorDialog()
}
ConnectionStatus.InitialValue -> {
binding.txtServerConnectionStatus.text = getString(R.string.serverConnectionStatusPropertyFormat, "Not Initialized")
}
}
}
}
}
}
private fun showConnectionErrorDialog() {
val dialog = Dialog(this)
dialog.requestWindowFeature(Window.FEATURE_NO_TITLE)
dialog.setContentView(R.layout.dialog_alert)
dialog.window?.setBackgroundDrawable(ColorDrawable(Color.TRANSPARENT))
val tvTitle: TextView = dialog.findViewById(R.id.title)
val tvMessage: TextView = dialog.findViewById(R.id.message)
val btnCancel: Button = dialog.findViewById(R.id.btnCancel)
val btnAccept: Button = dialog.findViewById(R.id.btnAccept)
btnCancel.text = getString(R.string.cancel)
btnCancel.setOnClickListener {
dialog.dismiss()
}
tvTitle.text = getString(R.string.serverConnectionProblems)
tvMessage.text = getString(R.string.errorDescriptionFormat, "Connection attempts failed. Check your network/server and try again")
btnAccept.text = getString(R.string.tryAgain)
btnAccept.setOnClickListener {
if(mBound) mService.connectToServer(url)
dialog.dismiss()
}
dialog.show()
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityTrackerLogBinding.inflate(layoutInflater)
setContentView(binding.root)
enableEdgeToEdge()
ViewCompat.setOnApplyWindowInsetsListener(binding.root) { v, insets ->
val systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars())
v.setPadding(systemBars.left, systemBars.top, systemBars.right, systemBars.bottom)
insets
}
url = intent.getStringExtra("webSocketUrl") ?: ""
locationPermissionRequest = registerForActivityResult(
ActivityResultContracts.RequestMultiplePermissions()
) { permissions ->
when {
permissions.getOrDefault(Manifest.permission.ACCESS_FINE_LOCATION, false) -> {
if(!serviceStarted){
Intent(this, LocationSimulationService::class.java).also {
startService(it)
bindService(it, mConnection, Context.BIND_AUTO_CREATE)
}
serviceStarted = true
}
if(mBound && !connectedToServer){
mService.connectToServer(url)
connectedToServer = true
}
}
permissions.getOrDefault(Manifest.permission.ACCESS_COARSE_LOCATION, false) -> {
if(!serviceStarted){
Intent(this, LocationSimulationService::class.java).also {
startService(it)
bindService(it, mConnection, Context.BIND_AUTO_CREATE)
}
serviceStarted = true
}
if(mBound && !connectedToServer){
mService.connectToServer(url)
connectedToServer = true
}
}
else -> {
Toast.makeText(baseContext, "Problems with permission", Toast.LENGTH_SHORT)
.show()
}
}
}
}
override fun onStart() {
super.onStart()
initUI()
}
private fun initUI() {
binding.txtServerAddress.text = getString(R.string.serverAddressPropertyFormat, url)
binding.btnBack.setOnClickListener {
if (!connectedToServer){
onBackPressedDispatcher.onBackPressed()
}
}
binding.startService.setOnClickListener {
val connectionStatus = if (mBound){
mService.getConnectionStatus().value
} else {
ConnectionStatus.InitialValue
}
when (connectionStatus){
ConnectionStatus.Awaiting -> requestLocationPermission()
ConnectionStatus.InitialValue -> requestLocationPermission()
ConnectionStatus.Disconnected -> requestLocationPermission()
else -> {
Toast.makeText(this, "SIMTAK Service is already running", Toast.LENGTH_SHORT).show()
}
}
}
if (!serviceStarted){
binding.startService.performClick()
}
binding.stopService.setOnClickListener {
val connectionStatus = if (mBound){
mService.getConnectionStatus().value
} else {
ConnectionStatus.InitialValue
}
when (connectionStatus){
ConnectionStatus.Awaiting -> secureStopService()
ConnectionStatus.Connected -> secureStopService()
ConnectionStatus.OnReconnect -> secureStopService()
else -> {
Toast.makeText(this, "SIMTAK Service is already stopped", Toast.LENGTH_SHORT).show()
}
}
}
setUpLogTrackerRV()
}
private fun setUpLogTrackerRV() {
val rv = binding.rvLogTracker
rv.adapter = rvAdapter
rv.layoutManager = LinearLayoutManager(this)
rv.addOnScrollListener(object : RecyclerView.OnScrollListener(){
override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) {
super.onScrolled(recyclerView, dx, dy)
val layoutManager = recyclerView.layoutManager as LinearLayoutManager?
layoutManager?.let {
val totalItemCount = it.itemCount
val lastVisibleItemPosition = it.findLastVisibleItemPosition()
lastThreeElementsAreVisible = totalItemCount - lastVisibleItemPosition <= 3
}
}
})
}
private fun secureStopService(){
if (mBound){
mService.stopByActivity()
connectedToServer = false
}
}
override fun onResume() {
super.onResume()
if (rvAdapter.itemCount -1 > 0){
binding.rvLogTracker.smoothScrollToPosition(rvAdapter.itemCount - 1)
}
}
private fun requestLocationPermission() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE) {
locationPermissionRequest.launch(
arrayOf(
Manifest.permission.FOREGROUND_SERVICE_LOCATION,
Manifest.permission.ACCESS_FINE_LOCATION,
Manifest.permission.ACCESS_COARSE_LOCATION
)
)
}
}
override fun onDestroy() {
super.onDestroy()
mService.stopByActivity()
unbindService(mConnection)
}
}

View File

@@ -0,0 +1,39 @@
package com.armatak.simtak.trackerLog.data.adapters
import android.view.LayoutInflater
import android.view.ViewGroup
import androidx.recyclerview.widget.DiffUtil
import androidx.recyclerview.widget.ListAdapter
import com.armatak.simtak.R
import com.armatak.simtak.trackerLog.data.models.LogModel
import com.armatak.simtak.trackerLog.data.viewHolder.ViewHolderLogTracker
class AdapterLogTracker: ListAdapter<LogModel, ViewHolderLogTracker>(DIFF_CALLBACK) {
companion object{
private val DIFF_CALLBACK = object : DiffUtil.ItemCallback<LogModel>(){
override fun areItemsTheSame(oldItem: LogModel, newItem: LogModel): Boolean {
return oldItem.idLine == newItem.idLine
}
override fun areContentsTheSame(oldItem: LogModel, newItem: LogModel): Boolean {
return oldItem == newItem
}
}
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolderLogTracker {
val layoutInflater = LayoutInflater.from(parent.context)
return ViewHolderLogTracker(
layoutInflater.inflate(
R.layout.item_log_tracker,
parent,
false
)
)
}
override fun onBindViewHolder(holder: ViewHolderLogTracker, position: Int) {
val item = getItem(position)
holder.render(item)
}
}

View File

@@ -0,0 +1,9 @@
package com.armatak.simtak.trackerLog.data.models
sealed interface ConnectionStatus {
data object Connected : ConnectionStatus
data object Disconnected : ConnectionStatus
data object OnReconnect : ConnectionStatus
data object Awaiting : ConnectionStatus
data object InitialValue : ConnectionStatus
}

View File

@@ -0,0 +1,15 @@
package com.armatak.simtak.trackerLog.data.models
data class LogModel(
val idLine: Int,
val time: String,
val body: String,
val type: LogTypes
)
sealed interface LogTypes {
data object Error : LogTypes
data object Warning : LogTypes
data object Normal : LogTypes
data object NetworkOperation : LogTypes
}

View File

@@ -0,0 +1,45 @@
package com.armatak.simtak.trackerLog.data.viewHolder
import android.view.View
import androidx.recyclerview.widget.RecyclerView.ViewHolder
import com.armatak.simtak.R
import com.armatak.simtak.databinding.ItemLogTrackerBinding
import com.armatak.simtak.trackerLog.data.models.LogModel
import com.armatak.simtak.trackerLog.data.models.LogTypes
class ViewHolderLogTracker(view: View) : ViewHolder(view) {
private val binding = ItemLogTrackerBinding.bind(view)
fun render(item: LogModel) {
val tvLogBody = binding.tvLogBody
val tvLogType = binding.tvLogType
binding.tvLogTime.text = item.time
tvLogBody.text = item.body
tvLogType.apply {
when (item.type) {
LogTypes.Error -> {
tvLogBody.setTextColor(context.getColor(R.color.errorColor))
setTextColor(context.getColor(R.color.errorColor))
text = context.getString(R.string.error)
}
LogTypes.NetworkOperation -> {
tvLogBody.setTextColor(context.getColor(R.color.networkColor))
setTextColor(context.getColor(R.color.networkColor))
text = context.getString(R.string.networkOperation)
}
LogTypes.Normal -> {
tvLogBody.setTextColor(context.getColor(R.color.darkGrey))
setTextColor(context.getColor(R.color.darkGrey))
text = context.getString(R.string.normal)
}
LogTypes.Warning -> {
tvLogBody.setTextColor(context.getColor(R.color.warningColor))
setTextColor(context.getColor(R.color.warningColor))
text = context.getString(R.string.warning)
}
}
}
}
}

View File

@@ -0,0 +1,19 @@
<vector android:height="500dp" android:viewportHeight="1000"
android:viewportWidth="1000" android:width="500dp" xmlns:android="http://schemas.android.com/apk/res/android">
<path android:fillColor="#00000000"
android:pathData="M731.7,11.3H831A157.7,157.7 0,0 1,988.7 169v99.3"
android:strokeColor="#fff" android:strokeLineCap="round"
android:strokeLineJoin="round" android:strokeWidth="22"/>
<path android:fillColor="#00000000"
android:pathData="M988.7,731.7V831A157.7,157.7 0,0 1,831 988.7H731.7"
android:strokeColor="#fff" android:strokeLineCap="round"
android:strokeLineJoin="round" android:strokeWidth="22"/>
<path android:fillColor="#00000000"
android:pathData="M268.3,11.3H169A157.7,157.7 0,0 0,11.3 169v99.3"
android:strokeColor="#fff" android:strokeLineCap="round"
android:strokeLineJoin="round" android:strokeWidth="22"/>
<path android:fillColor="#00000000"
android:pathData="M11.3,731.7V831A157.7,157.7 0,0 0,169 988.7h99.3"
android:strokeColor="#fff" android:strokeLineCap="round"
android:strokeLineJoin="round" android:strokeWidth="22"/>
</vector>

View File

@@ -0,0 +1,46 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:width="680.31dp"
android:height="680.31dp"
android:viewportWidth="680.31"
android:viewportHeight="680.31">
<path
android:fillColor="#FF000000"
android:pathData="M92.18,340.15C76.15,278.43 66.36,217 66.03,153.9c8.93,-1.43 17.25,-2.64 25.52,-4.12c28.79,-5.15 55.83,-15.34 82.3,-27.6c33.51,-15.52 64.93,-34.45 94.7,-56.17c13.83,-10.09 27.44,-20.47 40.98,-30.94c8.11,-6.27 16.01,-12.83 23.75,-19.55c2.87,-2.49 4.5,-1.53 6.93,0.47c14.98,12.33 29.8,24.89 45.23,36.64c30.57,23.27 62.65,44.42 96.97,61.65c37.67,18.91 76.63,34.75 119.31,38.42c5.37,0.46 5.35,3.53 5.29,7.49c-0.66,42.46 -5.59,84.46 -13.93,126.06c-3.53,17.65 -7.63,35.19 -11.71,53.89c4.81,0 9.53,0.34 14.18,-0.08c6.02,-0.55 9.09,1.5 10.64,7.65c2.9,11.51 6.58,22.83 10.07,34.19c14.6,47.51 29.26,95 43.86,142.51c2.54,8.26 1.44,9.7 -7.23,9.71c-53.57,0.02 -107.13,0.08 -160.7,-0.11c-4.9,-0.02 -7.76,1.59 -10.74,5.4c-10.3,13.14 -20.27,26.68 -31.85,38.65c-27.19,28.11 -56.81,53.49 -89.62,74.97c-2.27,1.49 -4.82,2.55 -7.15,3.95c-20.68,12.4 -11.96,12.53 -33.08,-0.3c-32.98,-20.03 -62.8,-44.05 -89.1,-72.24c-13.67,-14.65 -26.52,-30.09 -39.33,-45.5c-2.98,-3.58 -5.78,-4.91 -10.29,-4.9c-53.11,0.14 -106.22,0.09 -159.33,0.09c-10.49,0 -11.18,-0.97 -8.17,-11.05c12.55,-42.04 24.96,-84.11 37.68,-126.09c5.27,-17.39 11.03,-34.65 17.05,-51.79c0.78,-2.21 4.33,-4.47 6.86,-4.81C80.52,339.64 86.07,340.15 92.18,340.15zM337.35,48.77c-23.64,17.52 -46.73,34.97 -70.17,51.93c-30.67,22.18 -63.72,40.19 -98.84,54.29c-23.84,9.58 -48.61,15.72 -75.45,18.38c2.69,56.69 11.2,112.1 25.9,166.46c2.04,0.11 2.94,0.2 3.85,0.2c141.92,0.01 283.83,-0.02 425.75,0.11c4.61,0 6.42,-1.57 7.32,-5.87c4.37,-20.81 9.38,-41.5 13.34,-62.39c2.77,-14.59 4.48,-29.4 6.22,-44.16c2.11,-18 3.76,-36.06 5.46,-52.67c-19.58,-4.51 -37.32,-7.87 -54.65,-12.73c-30.79,-8.63 -59.22,-23.05 -86.22,-39.75c-23.86,-14.75 -46.55,-31.42 -69.41,-47.73C358.77,66.51 347.87,57.1 337.35,48.77zM454.8,534.62c-79.39,0 -156.77,0 -234.15,0c-0.65,0.78 -1.29,1.56 -1.94,2.34c3.85,3.03 7.98,5.76 11.51,9.13c15.51,14.79 30,30.81 46.49,44.39c17.75,14.61 37.28,27.09 56.22,40.22c1.65,1.15 5.3,1.16 7.09,0.12c27.7,-16.2 53.45,-35.12 76.65,-57.29C429.35,561.41 441.26,548.49 454.8,534.62zM497.06,493.41"
tools:ignore="VectorPath" />
<path
android:pathData="M337.35,48.77c10.51,8.33 21.42,17.73 33.08,26.06c22.86,16.31 45.56,32.97 69.41,47.73c27.01,16.7 55.43,31.12 86.22,39.75c17.32,4.86 35.06,8.23 54.65,12.73c-1.7,16.61 -3.34,34.67 -5.46,52.67c-1.73,14.76 -3.45,29.57 -6.22,44.16c-3.97,20.89 -8.97,41.58 -13.34,62.39c-0.9,4.3 -2.71,5.87 -7.32,5.87c-141.92,-0.13 -283.83,-0.1 -425.75,-0.11c-0.9,0 -1.8,-0.09 -3.85,-0.2c-14.7,-54.36 -23.21,-109.76 -25.9,-166.46c26.84,-2.66 51.61,-8.8 75.45,-18.38c35.12,-14.1 68.17,-32.11 98.84,-54.29C290.63,83.75 313.72,66.29 337.35,48.77zM446.57,175.27c-0.65,0.6 -1.3,1.2 -1.95,1.8c0.35,1.84 0.1,4.33 1.2,5.35c1.1,1.02 4.11,1.35 5.27,0.54c1.04,-0.74 1.57,-3.92 0.84,-5.17c-3.67,-6.29 -7.99,-12.19 -11.76,-18.42c-12.65,-20.93 -30.07,-33.52 -55.54,-31.79c-16.32,1.11 -30.75,7.69 -44.08,16.75c-2.86,1.94 -4.63,2.3 -7.76,0.48c-7.81,-4.55 -15.72,-9.09 -24.04,-12.54c-24,-9.97 -49.16,-5.07 -65.28,12.91c-5.41,6.04 -9.3,13.45 -13.82,20.28c-2.43,3.67 -4.85,7.37 -6.99,11.21c-0.95,1.7 -1.2,3.79 -1.76,5.7c1.63,0.45 3.81,1.75 4.75,1.14c1.45,-0.94 2.22,-3.13 2.95,-4.92c0.5,-1.23 -0.02,-3.5 0.72,-3.93c11.14,-6.5 22.09,-13.47 33.78,-18.79c6.01,-2.74 13.36,-0.26 17.07,5.5c6.03,9.37 10.8,19.54 16.11,29.37c-3.63,0.36 -5.71,-0.68 -7.87,-0.92c-10.77,-1.19 -21.56,-2.15 -32.34,-3.22c-2.94,-0.29 -5.86,-0.93 -8.79,-0.93c-1.57,0 -3.42,0.7 -4.6,1.71c-0.57,0.49 -0.37,3.12 0.3,3.57c1.28,0.86 3.99,1.69 4.61,1.06c4.23,-4.34 8.12,-1.62 11.73,0.43c10.94,6.23 20.32,14.15 25.42,24.28c-15.25,10.24 -30.1,20.2 -44.93,30.18c-7.18,4.83 -11.6,13.51 -21.22,15.04c-0.92,0.15 -2.24,2.59 -2.03,3.71c0.23,1.21 1.97,2.86 3.17,2.97c1.16,0.1 2.64,-1.36 3.66,-2.44c1.5,-1.58 2.42,-4.56 4.09,-4.98c13.67,-3.42 27.38,-6.85 41.25,-9.32c9.19,-1.63 18.67,-1.69 29.36,-2.55c-11.44,20.96 -16.58,41.85 -20.49,63.17c-0.43,2.37 -3.12,4.34 -4.8,6.47c-1.01,1.28 -2.08,2.5 -3.12,3.74c1.59,1.09 3.18,3.04 4.75,3.01c1.26,-0.02 2.74,-2.27 3.65,-3.81c1.38,-2.32 1.78,-5.48 3.6,-7.25c15.41,-14.92 31.09,-29.56 46.57,-44.4c2.89,-2.77 5.27,-6.06 7.16,-8.27c15.93,15.32 31.53,30.34 47.17,45.31c2.31,2.21 4.79,4.25 7.22,6.34c2.68,2.31 6.17,3.98 4.98,8.74c-0.21,0.86 2.23,3.42 3.24,3.31c1.72,-0.18 3.28,-1.86 4.91,-2.91c-1.06,-1.23 -2.19,-2.4 -3.17,-3.69c-1.73,-2.27 -3.89,-4.4 -4.9,-6.98c-1.23,-3.14 -1.19,-6.75 -2.15,-10.02c-5.1,-17.35 -10.34,-34.66 -15.52,-51.98c0.58,-0.03 2.36,-0.19 4.14,-0.17c20.72,0.26 40.7,4.87 60.5,10.45c3.22,0.91 7.64,0.09 7.45,5.78c-0.03,0.81 2.62,2.15 4.16,2.39c0.77,0.12 2.71,-1.87 2.58,-2.65c-0.25,-1.53 -1.26,-3.36 -2.53,-4.21c-1.93,-1.3 -4.69,-1.43 -6.51,-2.82c-11.03,-8.45 -21.52,-17.65 -32.92,-25.55c-8.52,-5.91 -18.14,-10.23 -27.21,-15.23c6.11,-11.76 15.6,-19.73 26.74,-25.87c3.42,-1.89 7.11,-4.6 11.11,-0.32c0.65,0.7 3.51,0.15 4.67,-0.72c0.77,-0.58 0.98,-3.44 0.3,-4.07c-1.07,-1 -3.08,-1.35 -4.69,-1.33c-2.94,0.02 -5.87,0.72 -8.81,0.75c-4.75,0.06 -9.58,-0.78 -14.24,-0.19c-7.79,1 -15.48,2.84 -25.23,4.71c5.26,-10.08 9.38,-19.11 14.52,-27.5c4.76,-7.77 14.31,-11.22 24.69,-5.91C426.4,163.01 436.37,169.35 446.57,175.27z"
android:fillColor="#FFFFFF"
tools:ignore="VectorPath" />
<path
android:pathData="M454.8,534.62c-13.54,13.87 -25.45,26.79 -38.13,38.91c-23.2,22.18 -48.95,41.09 -76.65,57.29c-1.79,1.05 -5.43,1.03 -7.09,-0.12c-18.94,-13.13 -38.47,-25.61 -56.22,-40.22c-16.5,-13.58 -30.98,-29.6 -46.49,-44.39c-3.53,-3.37 -7.66,-6.1 -11.51,-9.13c0.65,-0.78 1.29,-1.56 1.94,-2.34C298.03,534.62 375.41,534.62 454.8,534.62z"
android:fillColor="#FFFFFF"/>
<path
android:fillColor="#FF000000"
android:pathData="M446.57,175.27c-10.19,-5.91 -20.16,-12.26 -30.64,-17.62c-10.39,-5.31 -19.93,-1.86 -24.69,5.91c-5.14,8.4 -9.26,17.42 -14.52,27.5c9.75,-1.87 17.43,-3.71 25.23,-4.71c4.66,-0.6 9.49,0.24 14.24,0.19c2.94,-0.03 5.87,-0.73 8.81,-0.75c1.61,-0.01 3.63,0.33 4.69,1.33c0.67,0.63 0.47,3.49 -0.3,4.07c-1.16,0.87 -4.02,1.42 -4.67,0.72c-4,-4.28 -7.69,-1.57 -11.11,0.32c-11.14,6.14 -20.63,14.11 -26.74,25.87c9.07,5 18.69,9.33 27.21,15.23c11.4,7.9 21.89,17.09 32.92,25.55c1.82,1.39 4.58,1.53 6.51,2.82c1.27,0.85 2.28,2.68 2.53,4.21c0.13,0.79 -1.81,2.77 -2.58,2.65c-1.54,-0.23 -4.19,-1.57 -4.16,-2.39c0.19,-5.69 -4.23,-4.87 -7.45,-5.78c-19.81,-5.58 -39.78,-10.19 -60.5,-10.45c-1.78,-0.02 -3.57,0.14 -4.14,0.17c5.19,17.32 10.43,34.63 15.52,51.98c0.96,3.27 0.92,6.88 2.15,10.02c1.01,2.57 3.17,4.71 4.9,6.98c0.98,1.29 2.11,2.47 3.17,3.69c-1.63,1.05 -3.19,2.73 -4.91,2.91c-1.02,0.11 -3.46,-2.45 -3.24,-3.31c1.19,-4.76 -2.3,-6.43 -4.98,-8.74c-2.43,-2.09 -4.91,-4.13 -7.22,-6.34c-15.64,-14.98 -31.24,-30 -47.17,-45.31c-1.89,2.21 -4.27,5.5 -7.16,8.27c-15.49,14.84 -31.17,29.48 -46.57,44.4c-1.82,1.76 -2.22,4.93 -3.6,7.25c-0.92,1.54 -2.4,3.79 -3.65,3.81c-1.57,0.03 -3.17,-1.92 -4.75,-3.01c1.04,-1.25 2.12,-2.47 3.12,-3.74c1.68,-2.13 4.36,-4.1 4.8,-6.47c3.91,-21.32 9.05,-42.22 20.49,-63.17c-10.69,0.87 -20.16,0.92 -29.36,2.55c-13.87,2.47 -27.57,5.9 -41.25,9.32c-1.66,0.42 -2.59,3.4 -4.09,4.98c-1.02,1.08 -2.5,2.54 -3.66,2.44c-1.2,-0.1 -2.94,-1.76 -3.17,-2.97c-0.21,-1.12 1.11,-3.56 2.03,-3.71c9.62,-1.53 14.05,-10.21 21.22,-15.04c14.83,-9.98 29.68,-19.94 44.93,-30.18c-5.09,-10.13 -14.48,-18.05 -25.42,-24.28c-3.6,-2.05 -7.49,-4.77 -11.73,-0.43c-0.61,0.63 -3.33,-0.2 -4.61,-1.06c-0.67,-0.45 -0.87,-3.08 -0.3,-3.57c1.18,-1.01 3.03,-1.71 4.6,-1.71c2.93,0 5.86,0.64 8.79,0.93c10.78,1.07 21.58,2.03 32.34,3.22c2.17,0.24 4.24,1.28 7.87,0.92c-5.31,-9.83 -10.08,-20 -16.11,-29.37c-3.71,-5.76 -11.06,-8.24 -17.07,-5.5c-11.69,5.33 -22.64,12.3 -33.78,18.79c-0.74,0.43 -0.22,2.7 -0.72,3.93c-0.73,1.79 -1.5,3.98 -2.95,4.92c-0.94,0.61 -3.12,-0.69 -4.75,-1.14c0.57,-1.91 0.82,-4 1.76,-5.7c2.14,-3.84 4.56,-7.54 6.99,-11.21c4.52,-6.83 8.41,-14.24 13.82,-20.28c16.12,-17.99 41.28,-22.88 65.28,-12.91c8.32,3.46 16.23,8 24.04,12.54c3.13,1.82 4.9,1.47 7.76,-0.48c13.33,-9.06 27.76,-15.64 44.08,-16.75c25.47,-1.74 42.89,10.86 55.54,31.79c3.77,6.23 8.1,12.13 11.76,18.42c0.72,1.24 0.2,4.43 -0.84,5.17c-1.15,0.81 -4.16,0.48 -5.27,-0.54c-1.11,-1.02 -0.86,-3.51 -1.2,-5.35C445.27,176.46 445.92,175.86 446.57,175.27zM311.8,199.86c4.96,1.46 9.92,2.93 15.7,4.63C325.69,193.56 320.47,191.78 311.8,199.86zM360.9,199.7c-0.23,-0.83 -0.45,-1.66 -0.68,-2.49c-8,-5.19 -12.73,-3.49 -14.83,7.19C351.17,202.65 356.03,201.17 360.9,199.7z"
tools:ignore="VectorPath" />
<path
android:pathData="M311.8,199.86c8.68,-8.08 13.9,-6.3 15.7,4.63C321.72,202.78 316.76,201.32 311.8,199.86z"
android:fillColor="#FFFFFF"/>
<path
android:pathData="M360.9,199.7c-4.87,1.48 -9.73,2.95 -15.51,4.71c2.1,-10.68 6.83,-12.39 14.83,-7.19C360.44,198.04 360.67,198.87 360.9,199.7z"
android:fillColor="#FFFFFF"/>
<path
android:pathData="M60.46,456.26l28.2,-1.85c0.61,4.81 1.85,8.47 3.73,10.99c3.05,4.08 7.42,6.11 13.09,6.11c4.23,0 7.49,-1.04 9.78,-3.13c2.29,-2.08 3.44,-4.5 3.44,-7.25c0,-2.61 -1.09,-4.95 -3.27,-7.01c-2.18,-2.06 -7.24,-4.01 -15.18,-5.84c-13,-3.07 -22.27,-7.15 -27.81,-12.23c-5.58,-5.08 -8.38,-11.56 -8.38,-19.44c0,-5.17 1.43,-10.06 4.29,-14.67c2.86,-4.6 7.15,-8.22 12.89,-10.86c5.74,-2.63 13.6,-3.95 23.59,-3.95c12.26,0 21.6,2.39 28.04,7.18c6.43,4.79 10.26,12.4 11.48,22.84l-27.94,1.72c-0.74,-4.53 -2.3,-7.83 -4.68,-9.89c-2.38,-2.06 -5.66,-3.09 -9.85,-3.09c-3.45,0 -6.04,0.77 -7.79,2.3c-1.75,1.54 -2.62,3.4 -2.62,5.6c0,1.6 0.72,3.05 2.16,4.33c1.4,1.33 4.71,2.57 9.95,3.71c12.96,2.93 22.23,5.9 27.84,8.9c5.6,3 9.68,6.72 12.24,11.16c2.55,4.44 3.83,9.41 3.83,14.91c0,6.46 -1.7,12.41 -5.1,17.86c-3.4,5.45 -8.16,9.58 -14.26,12.4c-6.11,2.82 -13.81,4.23 -23.1,4.23c-16.31,0 -27.61,-3.3 -33.89,-9.89C64.84,474.81 61.28,466.42 60.46,456.26z"
android:fillColor="#FFFFFF"/>
<path
android:pathData="M158.08,388.86h29.71v100.72h-29.71V388.86z"
android:fillColor="#FFFFFF"/>
<path
android:pathData="M203.23,388.86h38.98l15.03,61.28l14.93,-61.28h38.96v100.72h-24.27v-76.81l-18.7,76.81h-21.97l-18.67,-76.81v76.81h-24.27V388.86z"
android:fillColor="#FFFFFF"/>
<path
android:pathData="M317.8,388.86h90.1v24.87h-30.23v75.85h-29.64v-75.85H317.8V388.86z"
android:fillColor="#FFFFFF"/>
<path
android:pathData="M465.69,472.95h-33.65l-4.67,16.63H397.1l36.05,-100.72h32.33l36.04,100.72h-31.04L465.69,472.95zM459.54,451.17l-10.59,-36.21l-10.48,36.21H459.54z"
android:fillColor="#FFFFFF"/>
<path
android:pathData="M505.58,388.86h29.64v38.06l31.05,-38.06h39.42l-35,38.02l36.57,62.7h-36.5l-20.22,-41.42L535.22,465v24.58h-29.64V388.86z"
android:fillColor="#FFFFFF"/>
</vector>

View File

@@ -0,0 +1,5 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:autoMirrored="true" android:height="60dp" android:tint="#6E3B23" android:viewportHeight="24" android:viewportWidth="24" android:width="60dp">
<path android:fillColor="@android:color/white" android:pathData="M20,11H7.83l5.59,-5.59L12,4l-8,8 8,8 1.41,-1.41L7.83,13H20v-2z"/>
</vector>

View File

@@ -0,0 +1,5 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:height="24dp" android:tint="#FFFFFF" android:viewportHeight="24" android:viewportWidth="24" android:width="24dp">
<path android:fillColor="@android:color/white" android:pathData="M16.01,7L16,3h-2v4h-4V3H8v4h-0.01C7,6.99 6,7.99 6,8.99v5.49L9.5,18v3h5v-3l3.5,-3.51v-5.5c0,-1 -1,-2 -1.99,-1.99z"/>
</vector>

View File

@@ -0,0 +1,8 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:height="60dp" android:viewportHeight="32" android:viewportWidth="32" android:width="60dp">
<path android:fillColor="#000000" android:pathData="M20.992,20.163c-1.511,-0.099 -2.699,-1.349 -2.699,-2.877 0,-0.051 0.001,-0.102 0.004,-0.153l-0,0.007c-0.003,-0.048 -0.005,-0.104 -0.005,-0.161 0,-1.525 1.19,-2.771 2.692,-2.862l0.008,-0c1.509,0.082 2.701,1.325 2.701,2.847 0,0.062 -0.002,0.123 -0.006,0.184l0,-0.008c0.003,0.05 0.005,0.109 0.005,0.168 0,1.523 -1.191,2.768 -2.693,2.854l-0.008,0zM11.026,20.163c-1.511,-0.099 -2.699,-1.349 -2.699,-2.877 0,-0.051 0.001,-0.102 0.004,-0.153l-0,0.007c-0.003,-0.048 -0.005,-0.104 -0.005,-0.161 0,-1.525 1.19,-2.771 2.692,-2.862l0.008,-0c1.509,0.082 2.701,1.325 2.701,2.847 0,0.062 -0.002,0.123 -0.006,0.184l0,-0.008c0.003,0.048 0.005,0.104 0.005,0.161 0,1.525 -1.19,2.771 -2.692,2.862l-0.008,0zM26.393,6.465c-1.763,-0.832 -3.811,-1.49 -5.955,-1.871l-0.149,-0.022c-0.005,-0.001 -0.011,-0.002 -0.017,-0.002 -0.035,0 -0.065,0.019 -0.081,0.047l-0,0c-0.234,0.411 -0.488,0.924 -0.717,1.45l-0.043,0.111c-1.03,-0.165 -2.218,-0.259 -3.428,-0.259s-2.398,0.094 -3.557,0.275l0.129,-0.017c-0.27,-0.63 -0.528,-1.142 -0.813,-1.638l0.041,0.077c-0.017,-0.029 -0.048,-0.047 -0.083,-0.047 -0.005,0 -0.011,0 -0.016,0.001l0.001,-0c-2.293,0.403 -4.342,1.06 -6.256,1.957l0.151,-0.064c-0.017,0.007 -0.031,0.019 -0.04,0.034l-0,0c-2.854,4.041 -4.562,9.069 -4.562,14.496 0,0.907 0.048,1.802 0.141,2.684l-0.009,-0.11c0.003,0.029 0.018,0.053 0.039,0.07l0,0c2.14,1.601 4.628,2.891 7.313,3.738l0.176,0.048c0.008,0.003 0.018,0.004 0.028,0.004 0.032,0 0.06,-0.015 0.077,-0.038l0,-0c0.535,-0.72 1.044,-1.536 1.485,-2.392l0.047,-0.1c0.006,-0.012 0.01,-0.027 0.01,-0.043 0,-0.041 -0.026,-0.075 -0.062,-0.089l-0.001,-0c-0.912,-0.352 -1.683,-0.727 -2.417,-1.157l0.077,0.042c-0.029,-0.017 -0.048,-0.048 -0.048,-0.083 0,-0.031 0.015,-0.059 0.038,-0.076l0,-0c0.157,-0.118 0.315,-0.24 0.465,-0.364 0.016,-0.013 0.037,-0.021 0.059,-0.021 0.014,0 0.027,0.003 0.038,0.008l-0.001,-0c2.208,1.061 4.8,1.681 7.536,1.681s5.329,-0.62 7.643,-1.727l-0.107,0.046c0.012,-0.006 0.025,-0.009 0.04,-0.009 0.022,0 0.043,0.008 0.059,0.021l-0,-0c0.15,0.124 0.307,0.248 0.466,0.365 0.023,0.018 0.038,0.046 0.038,0.077 0,0.035 -0.019,0.065 -0.046,0.082l-0,0c-0.661,0.395 -1.432,0.769 -2.235,1.078l-0.105,0.036c-0.036,0.014 -0.062,0.049 -0.062,0.089 0,0.016 0.004,0.031 0.011,0.044l-0,-0.001c0.501,0.96 1.009,1.775 1.571,2.548l-0.04,-0.057c0.017,0.024 0.046,0.04 0.077,0.04 0.01,0 0.02,-0.002 0.029,-0.004l-0.001,0c2.865,-0.892 5.358,-2.182 7.566,-3.832l-0.065,0.047c0.022,-0.016 0.036,-0.041 0.039,-0.069l0,-0c0.087,-0.784 0.136,-1.694 0.136,-2.615 0,-5.415 -1.712,-10.43 -4.623,-14.534l0.052,0.078c-0.008,-0.016 -0.022,-0.029 -0.038,-0.036l-0,-0z"
tools:ignore="VectorPath" />
</vector>

View File

@@ -0,0 +1,22 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:height="60dp" android:viewportHeight="24" android:viewportWidth="24" android:width="60dp">
<path android:fillColor="#FF000000" android:fillType="evenOdd" android:pathData="M12,0.296c-6.627,0 -12,5.372 -12,12c0,5.302 3.438,9.8 8.206,11.387c0.6,0.111 0.82,-0.26 0.82,-0.577c0,-0.286 -0.011,-1.231 -0.016,-2.234c-3.338,0.726 -4.043,-1.416 -4.043,-1.416C4.421,18.069 3.635,17.7 3.635,17.7c-1.089,-0.745 0.082,-0.729 0.082,-0.729c1.205,0.085 1.839,1.237 1.839,1.237c1.07,1.834 2.807,1.304 3.492,0.997C9.156,18.429 9.467,17.9 9.81,17.6c-2.665,-0.303 -5.467,-1.332 -5.467,-5.93c0,-1.31 0.469,-2.381 1.237,-3.221C5.455,8.146 5.044,6.926 5.696,5.273c0,0 1.008,-0.322 3.301,1.23C9.954,6.237 10.98,6.104 12,6.099c1.02,0.005 2.047,0.138 3.006,0.404c2.29,-1.553 3.297,-1.23 3.297,-1.23c0.653,1.653 0.242,2.873 0.118,3.176c0.769,0.84 1.235,1.911 1.235,3.221c0,4.609 -2.807,5.624 -5.479,5.921c0.43,0.372 0.814,1.103 0.814,2.222c0,1.606 -0.014,2.898 -0.014,3.293c0,0.319 0.216,0.694 0.824,0.576c4.766,-1.589 8.2,-6.085 8.2,-11.385C24,5.669 18.627,0.296 12,0.296z"
tools:ignore="VectorPath" />
<path android:fillColor="#FF000000" android:pathData="M4.545,17.526c-0.026,0.06 -0.12,0.078 -0.206,0.037c-0.087,-0.039 -0.136,-0.121 -0.108,-0.18c0.026,-0.061 0.12,-0.078 0.207,-0.037C4.525,17.384 4.575,17.466 4.545,17.526L4.545,17.526z"/>
<path android:fillColor="#FF000000" android:pathData="M5.031,18.068c-0.057,0.053 -0.169,0.028 -0.245,-0.055c-0.079,-0.084 -0.093,-0.196 -0.035,-0.249c0.059,-0.053 0.167,-0.028 0.246,0.056C5.076,17.903 5.091,18.014 5.031,18.068L5.031,18.068z"/>
<path android:fillColor="#FF000000" android:pathData="M5.504,18.759c-0.074,0.051 -0.194,0.003 -0.268,-0.103c-0.074,-0.107 -0.074,-0.235 0.002,-0.286c0.074,-0.051 0.193,-0.005 0.268,0.101C5.579,18.579 5.579,18.707 5.504,18.759L5.504,18.759z"/>
<path android:fillColor="#FF000000" android:pathData="M6.152,19.427c-0.066,0.073 -0.206,0.053 -0.308,-0.046c-0.105,-0.097 -0.134,-0.234 -0.068,-0.307c0.067,-0.073 0.208,-0.052 0.311,0.046C6.191,19.217 6.222,19.355 6.152,19.427L6.152,19.427z"/>
<path android:fillColor="#FF000000" android:pathData="M7.047,19.814c-0.029,0.094 -0.164,0.137 -0.3,0.097C6.611,19.87 6.522,19.76 6.55,19.665c0.028,-0.095 0.164,-0.139 0.301,-0.096C6.986,19.609 7.075,19.719 7.047,19.814L7.047,19.814z"/>
<path android:fillColor="#FF000000" android:pathData="M8.029,19.886c0.003,0.099 -0.112,0.181 -0.255,0.183c-0.143,0.003 -0.26,-0.077 -0.261,-0.174c0,-0.1 0.113,-0.181 0.256,-0.184C7.912,19.708 8.029,19.788 8.029,19.886L8.029,19.886z"/>
<path android:fillColor="#FF000000" android:pathData="M8.943,19.731c0.017,0.096 -0.082,0.196 -0.224,0.222c-0.139,0.026 -0.268,-0.034 -0.286,-0.13c-0.017,-0.099 0.084,-0.198 0.223,-0.224C8.797,19.574 8.925,19.632 8.943,19.731L8.943,19.731z"/>
</vector>

View File

@@ -0,0 +1,170 @@
<?xml version="1.0" encoding="utf-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="108dp"
android:height="108dp"
android:viewportWidth="108"
android:viewportHeight="108">
<path
android:fillColor="#3DDC84"
android:pathData="M0,0h108v108h-108z" />
<path
android:fillColor="#00000000"
android:pathData="M9,0L9,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M19,0L19,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M29,0L29,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M39,0L39,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M49,0L49,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M59,0L59,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M69,0L69,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M79,0L79,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M89,0L89,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M99,0L99,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,9L108,9"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,19L108,19"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,29L108,29"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,39L108,39"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,49L108,49"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,59L108,59"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,69L108,69"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,79L108,79"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,89L108,89"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,99L108,99"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M19,29L89,29"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M19,39L89,39"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M19,49L89,49"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M19,59L89,59"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M19,69L89,69"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M19,79L89,79"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M29,19L29,89"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M39,19L39,89"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M49,19L49,89"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M59,19L59,89"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M69,19L69,89"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M79,19L79,89"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
</vector>

View File

@@ -0,0 +1,30 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:aapt="http://schemas.android.com/aapt"
android:width="108dp"
android:height="108dp"
android:viewportWidth="108"
android:viewportHeight="108">
<path android:pathData="M31,63.928c0,0 6.4,-11 12.1,-13.1c7.2,-2.6 26,-1.4 26,-1.4l38.1,38.1L107,108.928l-32,-1L31,63.928z">
<aapt:attr name="android:fillColor">
<gradient
android:endX="85.84757"
android:endY="92.4963"
android:startX="42.9492"
android:startY="49.59793"
android:type="linear">
<item
android:color="#44000000"
android:offset="0.0" />
<item
android:color="#00000000"
android:offset="1.0" />
</gradient>
</aapt:attr>
</path>
<path
android:fillColor="#FFFFFF"
android:fillType="nonZero"
android:pathData="M65.3,45.828l3.8,-6.6c0.2,-0.4 0.1,-0.9 -0.3,-1.1c-0.4,-0.2 -0.9,-0.1 -1.1,0.3l-3.9,6.7c-6.3,-2.8 -13.4,-2.8 -19.7,0l-3.9,-6.7c-0.2,-0.4 -0.7,-0.5 -1.1,-0.3C38.8,38.328 38.7,38.828 38.9,39.228l3.8,6.6C36.2,49.428 31.7,56.028 31,63.928h46C76.3,56.028 71.8,49.428 65.3,45.828zM43.4,57.328c-0.8,0 -1.5,-0.5 -1.8,-1.2c-0.3,-0.7 -0.1,-1.5 0.4,-2.1c0.5,-0.5 1.4,-0.7 2.1,-0.4c0.7,0.3 1.2,1 1.2,1.8C45.3,56.528 44.5,57.328 43.4,57.328L43.4,57.328zM64.6,57.328c-0.8,0 -1.5,-0.5 -1.8,-1.2s-0.1,-1.5 0.4,-2.1c0.5,-0.5 1.4,-0.7 2.1,-0.4c0.7,0.3 1.2,1 1.2,1.8C66.5,56.528 65.6,57.328 64.6,57.328L64.6,57.328z"
android:strokeWidth="1"
android:strokeColor="#00000000" />
</vector>

View File

@@ -0,0 +1,8 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:height="30dp" android:viewportHeight="24" android:viewportWidth="24" android:width="30dp">
<path android:fillColor="#FF000000" android:pathData="m12.707,12.707l-2.293,2.293 -1.414,-1.414 2.293,-2.293c0.391,-0.391 0.391,-1.023 0,-1.414s-1.023,-0.391 -1.414,0l-2.293,2.293 -1.879,-1.879c-0.391,-0.391 -1.023,-0.391 -1.414,0s-0.391,1.023 0,1.414l0.352,0.352 -1.134,1.135c-1.77,1.769 -1.982,4.515 -0.638,6.519l-2.58,2.58c-0.391,0.391 -0.391,1.023 0,1.414 0.195,0.195 0.451,0.293 0.707,0.293s0.512,-0.098 0.707,-0.293l2.58,-2.58c0.865,0.58 1.868,0.871 2.871,0.871 1.321,0 2.642,-0.503 3.647,-1.509l1.135,-1.134 0.352,0.352c0.195,0.195 0.451,0.293 0.707,0.293s0.512,-0.098 0.707,-0.293c0.391,-0.391 0.391,-1.023 0,-1.414l-1.879,-1.879 2.293,-2.293c0.391,-0.391 0.391,-1.023 0,-1.414s-1.023,-0.391 -1.414,0ZM23.707,0.293c-0.391,-0.391 -1.023,-0.391 -1.414,0l-2.58,2.58c-2.004,-1.344 -4.749,-1.132 -6.519,0.638l-1.135,1.135 -0.353,-0.353c-0.391,-0.391 -1.023,-0.391 -1.414,0s-0.391,1.023 0,1.414l8,8c0.195,0.195 0.451,0.293 0.707,0.293s0.512,-0.098 0.707,-0.293c0.391,-0.391 0.391,-1.023 0,-1.414l-0.353,-0.353 1.135,-1.135c1.77,-1.769 1.982,-4.515 0.638,-6.519l2.58,-2.58c0.391,-0.391 0.391,-1.023 0,-1.414Z"
tools:ignore="VectorPath" />
</vector>

View File

@@ -0,0 +1,8 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:height="60dp" android:viewportHeight="32" android:viewportWidth="32" android:width="60dp">
<path android:fillColor="#000000" android:pathData="M18.102,12.129c0,-0 0,-0 0,-0.001 0,-1.564 1.268,-2.831 2.831,-2.831s2.831,1.268 2.831,2.831c0,1.564 -1.267,2.831 -2.831,2.831 -0,0 -0,0 -0.001,0h0c-0,0 -0,0 -0.001,0 -1.563,0 -2.83,-1.267 -2.83,-2.83 0,-0 0,-0 0,-0.001v0zM24.691,12.135c0,-2.081 -1.687,-3.768 -3.768,-3.768s-3.768,1.687 -3.768,3.768c0,2.081 1.687,3.768 3.768,3.768v0c2.08,-0.003 3.765,-1.688 3.768,-3.767v-0zM10.427,23.76l-1.841,-0.762c0.524,1.078 1.611,1.808 2.868,1.808 1.317,0 2.448,-0.801 2.93,-1.943l0.008,-0.021c0.155,-0.362 0.246,-0.784 0.246,-1.226 0,-1.757 -1.424,-3.181 -3.181,-3.181 -0.405,0 -0.792,0.076 -1.148,0.213l0.022,-0.007 1.903,0.787c0.852,0.364 1.439,1.196 1.439,2.164 0,1.296 -1.051,2.347 -2.347,2.347 -0.324,0 -0.632,-0.066 -0.913,-0.184l0.015,0.006zM15.974,1.004c-7.857,0.001 -14.301,6.046 -14.938,13.738l-0.004,0.054 8.038,3.322c0.668,-0.462 1.495,-0.737 2.387,-0.737 0.001,0 0.002,0 0.002,0h-0c0.079,0 0.156,0.005 0.235,0.008l3.575,-5.176v-0.074c0.003,-3.12 2.533,-5.648 5.653,-5.648 3.122,0 5.653,2.531 5.653,5.653s-2.531,5.653 -5.653,5.653h-0.131l-5.094,3.638c0,0.065 0.005,0.131 0.005,0.199 0,0.001 0,0.002 0,0.003 0,2.342 -1.899,4.241 -4.241,4.241 -2.047,0 -3.756,-1.451 -4.153,-3.38l-0.005,-0.027 -5.755,-2.383c1.841,6.345 7.601,10.905 14.425,10.905 8.281,0 14.994,-6.713 14.994,-14.994s-6.713,-14.994 -14.994,-14.994c-0,0 -0.001,0 -0.001,0h0z"
tools:ignore="VectorPath" />
</vector>

View File

@@ -0,0 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="200dp"
android:height="200dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:fillColor="#FF000000"
android:pathData="M22.922,1.7a2.985,2.985 0,0 0,-2.458 -0.648l-6.18,1.123A3.993,3.993 0,0 0,12 3.461,3.993 3.993,0 0,0 9.716,2.172L3.536,1.049A3,3 0,0 0,0 4L0,20.834l12,2.183 12,-2.183L24,4A2.992,2.992 0,0 0,22.922 1.7ZM11,20.8 L2,19.166L2,4a1,1 0,0 1,1.179 -0.983L9.358,4.14A2,2 0,0 1,11 6.108ZM22,19.164L13,20.8L13,6.108A2,2 0,0 1,14.642 4.14l6.179,-1.123A1,1 0,0 1,22 4Z"/>
</vector>

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@@ -0,0 +1,212 @@
<?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"
android:background="@color/white"
tools:context=".HomeActivity">
<com.google.android.material.card.MaterialCardView
android:id="@+id/bannerHeaderTitle"
android:layout_width="350dp"
android:layout_height="120dp"
android:backgroundTint="@color/accentColor"
app:strokeWidth="0dp"
app:layout_constraintBottom_toTopOf="@+id/txtGuide"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent">
<TextView
android:id="@+id/textHeader"
android:layout_width="300dp"
android:layout_height="wrap_content"
android:text="@string/header_banner_title"
android:textColor="@color/white"
android:textSize="20sp"
android:fontFamily="@font/archivo_black"
android:textAlignment="center"
android:layout_gravity="center"/>
</com.google.android.material.card.MaterialCardView>
<TextView
android:id="@+id/txtGuide"
android:layout_width="300dp"
android:layout_height="wrap_content"
android:layout_marginHorizontal="30dp"
style="@style/Theme.SIMTAK.Font"
android:text="@string/welcome"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="0.35" />
<androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/containeretServerAddress"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="30dp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/txtGuide">
<com.google.android.material.textfield.TextInputLayout
android:id="@+id/etServerAddressLayout"
android:layout_width="235sp"
android:layout_height="wrap_content"
android:hint="@string/serverAddress"
app:boxCornerRadiusBottomEnd="10dp"
app:boxCornerRadiusBottomStart="10dp"
app:boxCornerRadiusTopEnd="10dp"
app:boxCornerRadiusTopStart="10dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toStartOf="@id/btnConnectToServer"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent">
<com.google.android.material.textfield.TextInputEditText
android:id="@+id/etServerAddress"
android:layout_width="match_parent"
android:layout_height="wrap_content"
style="@style/Theme.SIMTAK.EditTextOutlined"
android:inputType="textUri"/>
</com.google.android.material.textfield.TextInputLayout>
<com.google.android.material.button.MaterialButton
android:id="@+id/btnConnectToServer"
style="@style/Widget.Material3.Button.Icon"
android:layout_width="63sp"
android:layout_height="65sp"
android:layout_marginTop="1dp"
android:layout_marginStart="5dp"
app:cornerRadius="10dp"
app:icon="@drawable/ic_plug_connection"
app:iconSize="30dp"
android:contentDescription="@string/content_connect_to_provider_server_address_button"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@+id/etServerAddressLayout"
app:layout_constraintTop_toTopOf="@id/etServerAddressLayout" />
</androidx.constraintlayout.widget.ConstraintLayout>
<androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/dividerOROptions"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="10dp"
android:layout_marginHorizontal="20dp"
app:layout_constraintTop_toBottomOf="@id/containeretServerAddress"
>
<com.google.android.material.divider.MaterialDivider
android:id="@+id/d1"
android:layout_width="0dp"
android:layout_height="wrap_content"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toStartOf="@id/txtOr"/>
<TextView
android:id="@+id/txtOr"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/or"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
android:paddingHorizontal="10dp"
/>
<com.google.android.material.divider.MaterialDivider
android:id="@+id/d2"
android:layout_width="0dp"
android:layout_height="wrap_content"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toEndOf="@id/txtOr"
app:layout_constraintEnd_toEndOf="parent"/>
</androidx.constraintlayout.widget.ConstraintLayout>
<com.google.android.material.button.MaterialButton
android:id="@+id/btnScanQrCode"
android:layout_width="300dp"
android:layout_height="wrap_content"
android:layout_marginHorizontal="80dp"
android:layout_marginTop="10dp"
style="@style/Theme.SIMTAK.Button"
android:text="@string/scanQRCode"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/dividerOROptions" />
<TextView
android:id="@+id/footerInfo"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:layout_constraintBottom_toTopOf="@id/btnGithubProject"
android:layout_marginBottom="10dp"
style="@style/Theme.SIMTAK.Font.InfoLight"
android:text="@string/footerInfo"
android:gravity="center"
android:layout_marginHorizontal="25dp"
/>
<com.google.android.material.button.MaterialButton
android:id="@+id/btnGithubProject"
style="@style/Theme.SIMTAK.Button.TextIconLightButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginHorizontal="25dp"
android:layout_marginBottom="10dp"
android:gravity="center"
android:text="@string/footerProject"
app:icon="@drawable/ic_github"
app:layout_constraintBottom_toTopOf="@id/footerLinks"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent" />
<LinearLayout
android:id="@+id/footerLinks"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:maxWidth="400dp"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
android:layout_marginBottom="20dp"
android:orientation="horizontal">
<com.google.android.material.button.MaterialButton
android:id="@+id/btnWiki"
android:layout_width="0dp"
android:layout_height="match_parent"
style="@style/Theme.SIMTAK.Button.TextIconLightButton"
app:icon="@drawable/ic_wiki_book"
android:text="@string/wiki"
android:layout_weight="1"
/>
<com.google.android.material.button.MaterialButton
android:id="@+id/btnDiscord"
android:layout_width="0dp"
android:layout_height="match_parent"
style="@style/Theme.SIMTAK.Button.TextIconLightButton"
app:icon="@drawable/ic_discord"
android:text="@string/joinOurGiscordGroup"
android:layout_weight="1"
/>
<com.google.android.material.button.MaterialButton
android:id="@+id/btnSteamProject"
android:layout_width="0dp"
android:layout_height="match_parent"
style="@style/Theme.SIMTAK.Button.TextIconLightButton"
app:icon="@drawable/ic_steam"
android:text="@string/steam"
android:layout_weight="1"
/>
</LinearLayout>
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@@ -0,0 +1,47 @@
<?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:id="@+id/main"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".ScannerActivity">
<SurfaceView
android:id="@+id/surfaceView"
android:layout_width="800dp"
android:layout_height="match_parent"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<ImageView
android:id="@+id/aimQrcode"
android:layout_width="350dp"
android:layout_height="350dp"
android:src="@drawable/aim_qrcode"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="0.25"
android:importantForAccessibility="no"/>
<TextView
android:id="@+id/txtQrcodeAssistant"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/aimQrcode"
app:layout_constraintVertical_bias="0.0"
android:layout_marginTop="60dp"
android:layout_marginHorizontal="20dp"
android:textSize="20sp"
android:gravity="center"
android:text="@string/scanQRCodeStepInfo"
android:textColor="#FFFFFFFF"/>
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@@ -0,0 +1,116 @@
<?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"
android:background="@color/white"
tools:context=".trackerLog.TrackerLogActivity">
<androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/topPanel"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingHorizontal="25dp"
android:layout_marginTop="35dp"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent">
<androidx.appcompat.widget.AppCompatImageButton
android:id="@+id/btnBack"
style="Theme.SIMTAK.Button.BackBtnNoBg"
android:layout_width="30dp"
android:layout_height="30dp"
android:backgroundTint="@color/fullTranslucent"
android:foreground="@drawable/baseline_arrow_back_24"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<com.google.android.material.textview.MaterialTextView
android:id="@+id/tvTitleActivity"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginHorizontal="60dp"
style="@style/Theme.SIMTAK.Font.TitleMediumDarkGrey22sp"
android:text="@string/trackerLog"
app:layout_constraintStart_toEndOf="@id/btnBack"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"/>
</androidx.constraintlayout.widget.ConstraintLayout>
<androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/mainContainer"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginHorizontal="25dp"
android:layout_marginTop="20dp"
app:layout_constraintTop_toBottomOf="@id/topPanel">
<TextView
android:id="@+id/txtServerAddress"
android:layout_width="match_parent"
android:layout_height="wrap_content"
style="@style/Theme.SIMTAK.Font"
android:text="@string/serverAddressPropertyEmpty"
app:layout_constraintTop_toTopOf="parent"/>
<TextView
android:id="@+id/txtServerConnectionStatus"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
style="@style/Theme.SIMTAK.Font"
android:text="@string/serverConnectionStatusPropertyEmpty"
app:layout_constraintTop_toBottomOf="@id/txtServerAddress"
app:layout_constraintStart_toStartOf="parent"/>
<com.google.android.material.card.MaterialCardView
android:id="@+id/containerLogTracker"
android:layout_width="match_parent"
android:layout_height="450dp"
app:cardBackgroundColor="@color/white"
app:cardCornerRadius="10dp"
app:strokeColor="@color/softLightGrey"
android:layout_marginTop="10dp"
app:layout_constraintTop_toBottomOf="@id/txtServerConnectionStatus">
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/rvLogTracker"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:itemCount="5"
tools:listitem="@layout/item_log_tracker"
android:background="@color/white" />
</com.google.android.material.card.MaterialCardView>
<com.google.android.material.button.MaterialButton
android:id="@+id/startService"
style="@style/Theme.SIMTAK.Button"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginTop="15dp"
android:text="@string/startTracking"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/containerLogTracker" />
<com.google.android.material.button.MaterialButton
android:id="@+id/stopService"
style="@style/Theme.SIMTAK.Button.TextIconLightButton"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginTop="15dp"
android:textSize="17sp"
android:text="@string/stopTracking"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/startService" />
</androidx.constraintlayout.widget.ConstraintLayout>
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@@ -0,0 +1,67 @@
<?xml version="1.0" encoding="utf-8"?>
<com.google.android.material.card.MaterialCardView xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_height="wrap_content"
xmlns:tools="http://schemas.android.com/tools"
app:cardElevation="0dp"
app:strokeWidth="0dp"
app:cardBackgroundColor="@color/white"
app:cardCornerRadius="15dp">
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:minWidth="220dp"
android:minHeight="120dp"
android:paddingHorizontal="30dp">
<TextView
android:id="@+id/title"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="35dp"
style="@style/Theme.SIMTAK.Font.TitleMediumDarkGrey"
tools:text="TesteDialog"
android:textSize="20sp"
app:layout_constraintTop_toTopOf="parent"/>
<TextView
android:id="@+id/message"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="6dp"
style="@style/Theme.SIMTAK.Font.InfoLight"
android:text="@string/tryAgain"
app:layout_constraintTop_toBottomOf="@id/title"
/>
<androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/containerButtons"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:layout_constraintTop_toBottomOf="@id/message"
app:layout_constraintEnd_toEndOf="parent"
android:layout_marginTop="6dp"
android:paddingBottom="17dp">
<com.google.android.material.button.MaterialButton
android:id="@+id/btnAccept"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="16sp"
app:cornerRadius="0dp"
style="@style/Widget.Material3.Button.TextButton"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintEnd_toEndOf="parent"/>
<com.google.android.material.button.MaterialButton
android:id="@+id/btnCancel"
android:visibility="gone"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="6dp"
android:textSize="16sp"
app:cornerRadius="0dp"
style="@style/Widget.Material3.Button.TextButton"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintEnd_toStartOf="@id/btnAccept"/>
</androidx.constraintlayout.widget.ConstraintLayout>
</androidx.constraintlayout.widget.ConstraintLayout>
</com.google.android.material.card.MaterialCardView>

View File

@@ -0,0 +1,59 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
xmlns:tools="http://schemas.android.com/tools"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:minHeight="50dp"
android:background="@color/white">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginVertical="10dp"
android:orientation="horizontal"
app:layout_constraintBottom_toTopOf="@id/divider"
app:layout_constraintTop_toTopOf="parent">
<TextView
android:id="@+id/tvLogTime"
style="@style/Theme.SIMTAK.Font.InfoLightDarkGrey"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:layout_marginStart="5dp"
android:layout_weight="1"
android:gravity="center"
tools:text="00:00:00:000" />
<TextView
android:id="@+id/tvLogBody"
style="@style/Theme.SIMTAK.Font.InfoLightDarkGrey"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:layout_marginHorizontal="5dp"
android:layout_weight="2"
tools:text="Error de teste " />
<TextView
android:id="@+id/tvLogType"
style="@style/Theme.SIMTAK.Font.InfoLightDarkGrey"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:layout_marginEnd="5dp"
android:layout_weight="1"
android:gravity="center"
android:text="@string/normal" />
</LinearLayout>
<com.google.android.material.divider.MaterialDivider
android:id="@+id/divider"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:layout_constraintBottom_toBottomOf="parent"
android:layout_marginHorizontal="5dp"
app:dividerColor="@color/softLightGrey"
/>
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
<background android:drawable="@drawable/ic_launcher_background" />
<foreground android:drawable="@drawable/ic_launcher_foreground" />
<monochrome android:drawable="@drawable/ic_launcher_foreground" />
</adaptive-icon>

View File

@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
<background android:drawable="@drawable/ic_launcher_background" />
<foreground android:drawable="@drawable/ic_launcher_foreground" />
<monochrome android:drawable="@drawable/ic_launcher_foreground" />
</adaptive-icon>

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 982 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.6 KiB

View File

@@ -0,0 +1,7 @@
<resources >
<!-- Base application theme. -->
<style name="Base.Theme.SIMTAK" parent="Theme.Material3.DayNight.NoActionBar">
<!-- Customize your dark theme here. -->
<!-- <item name="colorPrimary">@color/my_dark_primary</item> -->
</style>
</resources>

View File

@@ -0,0 +1,13 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<color name="black">#FF000000</color>
<color name="white">#FFFFFFFF</color>
<color name="darkGrey">#FF262626</color>
<color name="lightGrey">#FF828282</color>
<color name="softLightGrey">#FFdddddd</color>
<color name="errorColor">#FFCE4444</color>
<color name="networkColor">#1367BA</color>
<color name="warningColor">#FF9800</color>
<color name="accentColor">#6E3B23</color>
<color name="fullTranslucent">#00000000</color>
</resources>

View File

@@ -0,0 +1,40 @@
<resources>
<string name="app_name">SIMTAK</string>
<string name="allowNotificationsPermission" translatable="true">Allow permission for notifications</string>
<string name="needNotificationsPermissionMessage" translatable="true">For trust app working, need allow on system settings</string>
<string name="goToSettings" translatable="true">Go to system settings</string>
<string name="ok">OK</string>
<string name="cancel" translatable="true">Cancel</string>
<string name="tryAgain" translatable="true">Try Again</string>
<string name="serverAddress">Server Address</string>
<string name="scanQRCodeStepInfo" translatable="true">Scan the QR code in your briefing tab to get on the TAK Session</string>
<string name="welcome" translatable="true">Welcome!, Insert the provided Server Address or Scan the QR code in your briefing tab to get on the TAK Session</string>
<string name="header_banner_title">SIMTAK - Simulation for Team Awareness Kit</string>
<string name="or">Or</string>
<string name="scanQRCode">SCAN QR CODE</string>
<string name="content_connect_to_provider_server_address_button" translatable="true">Connect to Provider Server Address Button</string>
<string name="footerInfo" translatable="true">SIMTAK are part of the ARMATAK software bundle. \nARMATAK is currently in the development stage and is subject to unexpected bugs. Please use with caution.</string>
<string name="footerProject" translatable="true">Give us a Star on Github!</string>
<string name="githubProjectUrl" translatable="false">https://github.com/valmojr/armatak</string>
<string name="wikiUrl" translatable="false">https://github.com/valmojr/armatak/wiki</string>
<string name="discordUrl" translatable="false">https://discord.gg/svK64PCycU</string>
<string name="steamUrl" translatable="false">https://steamcommunity.com/sharedfiles/filedetails/?id=3301306282</string>
<string name="wiki">Wiki</string>
<string name="joinOurGiscordGroup" translatable="true">Join our Discord Group</string>
<string name="steam">Steam</string>
<string name="startTracking" translatable="true">Start Tracking</string>
<string name="stopTracking" translatable="true">Stop Tracking</string>
<string name="serverAddressPropertyEmpty" >Server Address:</string>
<string name="serverAddressPropertyFormat">Server Address: %s</string>
<string name="serverConnectionStatusPropertyEmpty" >Connection Status:</string>
<string name="serverConnectionStatusPropertyFormat">Connection Status: %s</string>
<string name="contentBtnToBack" translatable="true">Back Button</string>
<string name="trackerLog">Tracker Log</string>
<string name="serverConnectionProblems" translatable="true">Oops, server has problem!</string>
<string name="errorDescriptionFormat" translatable="true">Error Description: \n%s</string>
<string name="normal" translatable="true">Normal</string>
<string name="networkOperation" translatable="true">Network Operation</string>
<string name="warning" translatable="true">Warning</string>
<string name="error" translatable="true">Error</string>
</resources>

View File

@@ -0,0 +1,75 @@
<resources>
<!-- Base application theme. -->
<style name="Base.Theme.SIMTAK" parent="Theme.Material3.DayNight.NoActionBar">
<!-- Customize your light theme here. -->
<!-- <item name="colorPrimary">@color/my_light_primary</item> -->
</style>
<style name="Theme.SIMTAK" parent="Base.Theme.SIMTAK">
<item name="colorPrimary">@color/accentColor</item>
</style>
<style name="Theme.SIMTAK.EditTextOutlined" parent="Widget.MaterialComponents.TextInputLayout.OutlinedBox">
<item name="android:textAppearance">@font/roboto</item>
<item name="android:textColor">@color/darkGrey</item>
<item name="android:textSize">17sp</item>
<item name="android:textColorHint">@color/accentColor</item>
<item name="boxStrokeColor">@color/accentColor</item>
</style>
<style name="Theme.SIMTAK.Font">
<item name="android:fontFamily">@font/roboto</item>
<item name="android:textColor">@color/darkGrey</item>
<item name="android:textSize">17sp</item>
</style>
<style name="Theme.SIMTAK.Font.TitleMediumDarkGrey22sp">
<item name="android:fontFamily">@font/roboto_medium</item>
<item name="android:textColor">@color/darkGrey</item>
<item name="android:textSize">22sp</item>
<item name="android:elegantTextHeight">true</item>
</style>
<style name="Theme.SIMTAK.Font.TitleMediumDarkGrey">
<item name="android:fontFamily">@font/roboto_medium</item>
<item name="android:textColor">@color/darkGrey</item>
<item name="android:textSize">19sp</item>
</style>
<style name="Theme.SIMTAK.Font.InfoLight">
<item name="android:fontFamily">@font/roboto</item>
<item name="android:textColor">@color/lightGrey</item>
<item name="android:textSize">13sp</item>
</style>
<style name="Theme.SIMTAK.Font.InfoLightDarkGrey">
<item name="android:fontFamily">@font/roboto</item>
<item name="android:textColor">@color/darkGrey</item>
<item name="android:textSize">13sp</item>
</style>
<style name="Theme.SIMTAK.Button">
<item name="android:fontFamily">@font/roboto_medium</item>
<item name="android:textColor">@color/white</item>
<item name="android:textSize">17sp</item>
<item name="android:minHeight">60dp</item>
<item name="cornerRadius">10dp</item>
</style>
<style name="Theme.SIMTAK.IconNoBackGroundButton" parent="Widget.Material3.Button.Icon">
<item name="cornerRadius">10dp</item>
</style>
<style name="Theme.SIMTAK.Button.TextIconLightButton" parent="Widget.Material3.Button.TextButton">
<item name="android:fontFamily">@font/roboto</item>
<item name="android:textColor">@color/lightGrey</item>
<item name="android:textSize">13sp</item>
<item name="cornerRadius">10dp</item>
<item name="iconTint">@color/lightGrey</item>
<item name="iconSize">25dp</item>
<item name="iconGravity">end</item>
</style>
<style name="Theme.SIMTAK.Button.BackBtnNoBg">
<item name="android:contentDescription">@string/contentBtnToBack</item>
<item name="android:foreground">@drawable/baseline_arrow_back_24</item>
<item name="android:foregroundTint">@color/accentColor</item>
<item name="android:backgroundTint">@color/fullTranslucent</item>
<item name="android:contextClickable">true</item>
</style>
</resources>

View File

@@ -0,0 +1,13 @@
<?xml version="1.0" encoding="utf-8"?><!--
Sample backup rules file; uncomment and customize as necessary.
See https://developer.android.com/guide/topics/data/autobackup
for details.
Note: This file is ignored for devices older that API 31
See https://developer.android.com/about/versions/12/backup-restore
-->
<full-backup-content>
<!--
<include domain="sharedpref" path="."/>
<exclude domain="sharedpref" path="device.xml"/>
-->
</full-backup-content>

View File

@@ -0,0 +1,15 @@
<?xml version="1.0" encoding="utf-8"?><!--
Sample data extraction rules file; uncomment and customize as necessary.
See https://developer.android.com/about/versions/12/backup-restore#xml-changes
for details.
-->
<data-extraction-rules>
<cloud-backup>
</cloud-backup>
<!--
<device-transfer>
<include .../>
<exclude .../>
</device-transfer>
-->
</data-extraction-rules>

View File

@@ -0,0 +1,17 @@
package com.armatak.simtak
import org.junit.Test
import org.junit.Assert.*
/**
* Example local unit test, which will execute on the development machine (host).
*
* See [testing documentation](http://d.android.com/tools/testing).
*/
class ExampleUnitTest {
@Test
fun addition_isCorrect() {
assertEquals(4, 2 + 2)
}
}