Save Registered Host on Android

This commit is contained in:
Florian Märkl 2019-10-15 20:13:57 +02:00
commit da73d4e26e
No known key found for this signature in database
GPG key ID: 125BC8A5A6A1E857
8 changed files with 97 additions and 25 deletions

View file

@ -17,8 +17,22 @@
package com.metallic.chiaki.common package com.metallic.chiaki.common
import java.nio.ByteBuffer
import java.nio.ByteOrder
class MacAddress(v: Long) class MacAddress(v: Long)
{ {
constructor(data: ByteArray) : this(
if(data.size != 6)
throw IllegalArgumentException("Data has invalid length for MAC")
else
data.let {
val buf = ByteBuffer.allocate(8)
buf.put(it, 0, 6)
buf.order(ByteOrder.LITTLE_ENDIAN)
buf.getLong(0)
})
val value: Long = v and 0xffffffffffff val value: Long = v and 0xffffffffffff
override fun equals(other: Any?): Boolean = override fun equals(other: Any?): Boolean =
@ -28,4 +42,13 @@ class MacAddress(v: Long)
super.equals(other) super.equals(other)
override fun hashCode() = value.hashCode() override fun hashCode() = value.hashCode()
override fun toString(): String = "%02x:%02x:%02x:%02x:%02x:%02x".format(
value and 0xff,
(value shr 0x8) and 0xff,
(value shr 0x10) and 0xff,
(value shr 0x18) and 0xff,
(value shr 0x20) and 0xff,
(value shr 0x28) and 0xff
)
} }

View file

@ -32,7 +32,7 @@ import io.reactivex.Flowable
) )
]) ])
data class ManualHost( data class ManualHost(
@PrimaryKey(autoGenerate = true) val id: Int = 0, @PrimaryKey(autoGenerate = true) val id: Long = 0,
val host: String, val host: String,
@ColumnInfo(name = "registered_host") val registeredHost: Int? @ColumnInfo(name = "registered_host") val registeredHost: Int?
) )

View file

@ -19,12 +19,15 @@ package com.metallic.chiaki.common
import androidx.room.* import androidx.room.*
import androidx.room.ColumnInfo.BLOB import androidx.room.ColumnInfo.BLOB
import com.metallic.chiaki.lib.RegistHost
import io.reactivex.Flowable import io.reactivex.Flowable
import io.reactivex.Maybe
import io.reactivex.Single
@Suppress("ArrayInDataClass") @Suppress("ArrayInDataClass")
@Entity(tableName = "registered_host") @Entity(tableName = "registered_host")
data class RegisteredHost( data class RegisteredHost(
@PrimaryKey(autoGenerate = true) val id: Int = 0, @PrimaryKey(autoGenerate = true) val id: Long = 0,
@ColumnInfo(name = "ap_ssid") val apSsid: String?, @ColumnInfo(name = "ap_ssid") val apSsid: String?,
@ColumnInfo(name = "ap_bssid") val apBssid: String?, @ColumnInfo(name = "ap_bssid") val apBssid: String?,
@ColumnInfo(name = "ap_key") val apKey: String?, @ColumnInfo(name = "ap_key") val apKey: String?,
@ -35,10 +38,29 @@ data class RegisteredHost(
@ColumnInfo(name = "rp_key_type") val rpKeyType: Int, @ColumnInfo(name = "rp_key_type") val rpKeyType: Int,
@ColumnInfo(name = "rp_key", typeAffinity = BLOB) val rpKey: ByteArray // 0x10 @ColumnInfo(name = "rp_key", typeAffinity = BLOB) val rpKey: ByteArray // 0x10
) )
{
constructor(registHost: RegistHost) : this(
apSsid = registHost.apSsid,
apBssid = registHost.apBssid,
apKey = registHost.apKey,
apName = registHost.apName,
ps4Mac = MacAddress(registHost.ps4Mac),
ps4Nickname = registHost.ps4Nickname,
rpRegistKey = registHost.rpRegistKey,
rpKeyType = registHost.rpKeyType.toInt(),
rpKey = registHost.rpKey
)
}
@Dao @Dao
interface RegisteredHostDao interface RegisteredHostDao
{ {
@Query("SELECT * FROM registered_host") @Query("SELECT * FROM registered_host")
fun getAll(): Flowable<List<RegisteredHost>> fun getAll(): Flowable<List<RegisteredHost>>
@Query("SELECT * FROM registered_host WHERE ps4_mac == :mac LIMIT 1")
fun getByMac(mac: MacAddress): Maybe<RegisteredHost>
@Insert
fun insert(host: RegisteredHost): Single<Long>
} }

View file

@ -40,8 +40,6 @@ import kotlinx.android.synthetic.main.activity_main.*
class MainActivity : AppCompatActivity() class MainActivity : AppCompatActivity()
{ {
private val disposable = CompositeDisposable()
private lateinit var viewModel: MainViewModel private lateinit var viewModel: MainViewModel
private var discoveryMenuItem: MenuItem? = null private var discoveryMenuItem: MenuItem? = null
@ -87,12 +85,6 @@ class MainActivity : AppCompatActivity()
floatingActionButton.isActivated = floatingActionButton.isExpanded floatingActionButton.isActivated = floatingActionButton.isExpanded
} }
override fun onDestroy()
{
super.onDestroy()
disposable.dispose()
}
override fun onStart() override fun onStart()
{ {
super.onStart() super.onStart()

View file

@ -18,7 +18,6 @@
package com.metallic.chiaki.regist package com.metallic.chiaki.regist
import android.app.Activity import android.app.Activity
import android.content.ActivityNotFoundException
import android.content.Intent import android.content.Intent
import android.os.Bundle import android.os.Bundle
import android.text.method.ScrollingMovementMethod import android.text.method.ScrollingMovementMethod
@ -27,6 +26,8 @@ import androidx.appcompat.app.AppCompatActivity
import androidx.lifecycle.Observer import androidx.lifecycle.Observer
import androidx.lifecycle.ViewModelProviders import androidx.lifecycle.ViewModelProviders
import com.metallic.chiaki.R import com.metallic.chiaki.R
import com.metallic.chiaki.common.ext.viewModelFactory
import com.metallic.chiaki.common.getDatabase
import com.metallic.chiaki.lib.RegistInfo import com.metallic.chiaki.lib.RegistInfo
import kotlinx.android.synthetic.main.activity_regist_execute.* import kotlinx.android.synthetic.main.activity_regist_execute.*
import kotlin.math.max import kotlin.math.max
@ -47,7 +48,9 @@ class RegistExecuteActivity: AppCompatActivity()
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
setContentView(R.layout.activity_regist_execute) setContentView(R.layout.activity_regist_execute)
viewModel = ViewModelProviders.of(this).get(RegistExecuteViewModel::class.java) viewModel = ViewModelProviders
.of(this, viewModelFactory { RegistExecuteViewModel(getDatabase(this)) })
.get(RegistExecuteViewModel::class.java)
logTextView.setHorizontallyScrolling(true) logTextView.setHorizontallyScrolling(true)
logTextView.movementMethod = ScrollingMovementMethod() logTextView.movementMethod = ScrollingMovementMethod()

View file

@ -17,13 +17,22 @@
package com.metallic.chiaki.regist package com.metallic.chiaki.regist
import android.util.Log
import androidx.lifecycle.AndroidViewModel
import androidx.lifecycle.LiveData import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel import androidx.lifecycle.ViewModel
import com.metallic.chiaki.common.AppDatabase
import com.metallic.chiaki.common.MacAddress
import com.metallic.chiaki.common.RegisteredHost
import com.metallic.chiaki.common.ext.toLiveData import com.metallic.chiaki.common.ext.toLiveData
import com.metallic.chiaki.lib.* import com.metallic.chiaki.lib.*
import io.reactivex.android.schedulers.AndroidSchedulers
import io.reactivex.disposables.CompositeDisposable
import io.reactivex.rxkotlin.addTo
import io.reactivex.schedulers.Schedulers
class RegistExecuteViewModel: ViewModel() class RegistExecuteViewModel(val database: AppDatabase): ViewModel()
{ {
enum class State enum class State
{ {
@ -42,6 +51,8 @@ class RegistExecuteViewModel: ViewModel()
val logText: LiveData<String> = log.logText.toLiveData() val logText: LiveData<String> = log.logText.toLiveData()
private val disposable = CompositeDisposable()
fun start(info: RegistInfo) fun start(info: RegistInfo)
{ {
if(regist != null) if(regist != null)
@ -67,22 +78,43 @@ class RegistExecuteViewModel: ViewModel()
{ {
when(event) when(event)
{ {
is RegistEventCanceled -> { is RegistEventCanceled -> _state.postValue(State.STOPPED)
_state.postValue(State.STOPPED) is RegistEventFailed -> _state.postValue(State.FAILED)
} is RegistEventSuccess -> registSuccess(event.host)
is RegistEventFailed -> {
_state.postValue(State.FAILED)
}
is RegistEventSuccess -> {
// TODO: save event.host into db
_state.postValue(State.SUCCESSFUL)
}
} }
} }
private fun registSuccess(host: RegistHost)
{
_state.postValue(State.SUCCESSFUL) // TODO: more states
val dao = database.registeredHostDao()
val mac = MacAddress(host.ps4Mac)
//val mac = MacAddress(byteArrayOf(0xc0.toByte(), 0xff.toByte(), 0xff.toByte(), 0xee.toByte(), 0xee.toByte(), 0x42))
dao.getByMac(mac)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.doOnSuccess {
// TODO: show dialog
Log.i("RegistExecuteViewModel", "already exists")
}
.doOnComplete {
dao.insert(RegisteredHost(host))
.subscribeOn(Schedulers.io())
.subscribe()
.addTo(disposable)
}
.subscribe()
.addTo(disposable)
}
override fun onCleared() override fun onCleared()
{ {
super.onCleared() super.onCleared()
regist?.dispose() regist?.dispose()
disposable.dispose()
} }
} }

View file

@ -14,7 +14,7 @@
android:pathData="m7.93,9.425 l-2.324,3.906l121.929,0l2.324,-3.906z" android:pathData="m7.93,9.425 l-2.324,3.906l121.929,0l2.324,-3.906z"
android:strokeAlpha="1" android:strokeAlpha="1"
android:strokeWidth="12.74962807" android:strokeWidth="12.74962807"
android:fillColor="#ffae2f" android:fillColor="#00a7ff"
android:strokeColor="#00000000" android:strokeColor="#00000000"
android:fillAlpha="1"/> android:fillAlpha="1"/>
</vector> </vector>

View file

@ -8,7 +8,7 @@ buildscript {
} }
dependencies { dependencies {
classpath 'com.android.tools.build:gradle:3.5.0' classpath 'com.android.tools.build:gradle:3.5.1'
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
// NOTE: Do not place your application dependencies here; they belong // NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files // in the individual module build.gradle files