Use Fingerprint and pin - android

Hello I'm trying to use a fingerprint and pin code to access my android application. I encrypt the data using the Cipher. I store encrypted pin in a SharedPreferences. I can decrypt the pin code with a fingerprint, but I can not decrypt them if I enter the pin manually. I mean, I enter the pin in EditText and try to compare it with the encrypted pin in the SharedPreferences.
CryptoUtils class:
#TargetApi(Build.VERSION_CODES.M)
object CryptoUtils {
private val TAG = CryptoUtils::class.java.simpleName
private val KEY_ALIAS = "key_for_pin"
private val KEY_STORE = "AndroidKeyStore"
private val TRANSFORMATION = "RSA/ECB/OAEPWithSHA-256AndMGF1Padding"
private var sKeyStore: KeyStore? = null
private var sKeyPairGenerator: KeyPairGenerator? = null
private var sCipher: Cipher? = null
private val keyStore: Boolean
get() {
try {
sKeyStore = KeyStore.getInstance(KEY_STORE)
sKeyStore!!.load(null)
return true
} catch (e: KeyStoreException) {
e.printStackTrace()
} catch (e: IOException) {
e.printStackTrace()
} catch (e: NoSuchAlgorithmException) {
e.printStackTrace()
} catch (e: CertificateException) {
e.printStackTrace()
}
return false
}
private val keyPairGenerator: Boolean
#TargetApi(Build.VERSION_CODES.M)
get() {
try {
sKeyPairGenerator = KeyPairGenerator.getInstance(KeyProperties.KEY_ALGORITHM_RSA, KEY_STORE)
return true
} catch (e: NoSuchAlgorithmException) {
e.printStackTrace()
} catch (e: NoSuchProviderException) {
e.printStackTrace()
}
return false
}
private val cipher: Boolean
#SuppressLint("GetInstance")
get() {
try {
sCipher = Cipher.getInstance(TRANSFORMATION)
return true
} catch (e: NoSuchAlgorithmException) {
e.printStackTrace()
} catch (e: NoSuchPaddingException) {
e.printStackTrace()
}
return false
}
private val key: Boolean
get() {
try {
return sKeyStore!!.containsAlias(KEY_ALIAS) || generateNewKey()
} catch (e: KeyStoreException) {
e.printStackTrace()
}
return false
}
val cryptoObject: FingerprintManagerCompat.CryptoObject?
#Nullable
get() = if (prepare() && initCipher(Cipher.DECRYPT_MODE)) {
FingerprintManagerCompat.CryptoObject(sCipher!!)
} else null
fun encode(inputString: String): String? {
try {
if (prepare() && initCipher(Cipher.ENCRYPT_MODE)) {
val bytes = sCipher!!.doFinal(inputString.toByteArray())
return Base64.encodeToString(bytes, Base64.NO_WRAP)
}
} catch (exception: IllegalBlockSizeException) {
exception.printStackTrace()
} catch (exception: BadPaddingException) {
exception.printStackTrace()
}
return null
}
fun decode(encodedString: String, cipher: Cipher): String? {
try {
val bytes = Base64.decode(encodedString, Base64.NO_WRAP)
return String(cipher.doFinal(bytes))
} catch (exception: IllegalBlockSizeException) {
exception.printStackTrace()
} catch (exception: BadPaddingException) {
exception.printStackTrace()
}
return null
}
private fun prepare(): Boolean {
return keyStore && cipher && key
}
#TargetApi(Build.VERSION_CODES.M)
private fun generateNewKey(): Boolean {
if (keyPairGenerator) {
try {
sKeyPairGenerator!!.initialize(
KeyGenParameterSpec.Builder(KEY_ALIAS, KeyProperties.PURPOSE_ENCRYPT or KeyProperties.PURPOSE_DECRYPT)
.setDigests(KeyProperties.DIGEST_SHA256, KeyProperties.DIGEST_SHA512)
.setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_RSA_OAEP)
.setUserAuthenticationRequired(true)
.build())
sKeyPairGenerator!!.generateKeyPair()
return true
} catch (e: InvalidAlgorithmParameterException) {
e.printStackTrace()
}
}
return false
}
private fun initCipher(mode: Int): Boolean {
try {
sKeyStore!!.load(null)
when (mode) {
Cipher.ENCRYPT_MODE -> initEncodeCipher(mode)
Cipher.DECRYPT_MODE -> initDecodeCipher(mode)
else -> return false //this cipher is only for encode\decode
}
return true
} catch (exception: KeyPermanentlyInvalidatedException) {
deleteInvalidKey()
} catch (e: KeyStoreException) {
e.printStackTrace()
} catch (e: CertificateException) {
e.printStackTrace()
} catch (e: UnrecoverableKeyException) {
e.printStackTrace()
} catch (e: IOException) {
e.printStackTrace()
} catch (e: NoSuchAlgorithmException) {
e.printStackTrace()
} catch (e: InvalidKeyException) {
e.printStackTrace()
} catch (e: InvalidKeySpecException) {
e.printStackTrace()
} catch (e: InvalidAlgorithmParameterException) {
e.printStackTrace()
}
return false
}
#Throws(KeyStoreException::class, NoSuchAlgorithmException::class, UnrecoverableKeyException::class, InvalidKeyException::class)
private fun initDecodeCipher(mode: Int) {
val key = sKeyStore!!.getKey(KEY_ALIAS, null) as PrivateKey
sCipher!!.init(mode, key)
}
#Throws(KeyStoreException::class, InvalidKeySpecException::class, NoSuchAlgorithmException::class, InvalidKeyException::class, InvalidAlgorithmParameterException::class)
private fun initEncodeCipher(mode: Int) {
val key = sKeyStore!!.getCertificate(KEY_ALIAS).publicKey
// workaround for using public key
// from https://developer.android.com/reference/android/security/keystore/KeyGenParameterSpec.html
val unrestricted = KeyFactory.getInstance(key.getAlgorithm()).generatePublic(X509EncodedKeySpec(key.getEncoded()))
// from https://code.google.com/p/android/issues/detail?id=197719
val spec = OAEPParameterSpec("SHA-256", "MGF1", MGF1ParameterSpec.SHA1, PSource.PSpecified.DEFAULT)
sCipher!!.init(mode, unrestricted, spec)
}
fun deleteInvalidKey() {
if (keyStore) {
try {
sKeyStore!!.deleteEntry(KEY_ALIAS)
} catch (e: KeyStoreException) {
e.printStackTrace()
}
}
}
}
FingerprintUtils class:
enum class SensorState {
NOT_SUPPORTED,
NOT_BLOCKED,
NO_FINGERPRINTS,
READY
}
fun checkFingerprintCompatibility(context: Context): Boolean {
return FingerprintManagerCompat.from(context).isHardwareDetected
}
#TargetApi(Build.VERSION_CODES.JELLY_BEAN)
fun checkSensorState(context: Context): SensorState {
if (checkFingerprintCompatibility(context)) {
val keyguardManager =
context.getSystemService(Context.KEYGUARD_SERVICE) as KeyguardManager
if (!keyguardManager.isKeyguardSecure) {
return SensorState.NOT_BLOCKED
}
return if (!FingerprintManagerCompat.from(context).hasEnrolledFingerprints()) {
SensorState.NO_FINGERPRINTS
} else SensorState.READY
} else {
return SensorState.NOT_SUPPORTED
}
}
#TargetApi(Build.VERSION_CODES.JELLY_BEAN)
fun isSensorStateAt(state: SensorState, context: Context): Boolean {
return checkSensorState(context) == state
}
FingerPrintHelper class which I use in Activity:
class FingerprintHelper(private val mContext: Context, private val etPincode: PinField) : FingerprintManagerCompat.AuthenticationCallback() {
private var mCancellationSignal: CancellationSignal? = null
internal fun startAuth(cryptoObject: FingerprintManagerCompat.CryptoObject) {
mCancellationSignal = CancellationSignal()
val manager = FingerprintManagerCompat.from(mContext)
manager.authenticate(cryptoObject, 0, mCancellationSignal, this, null)
}
internal fun cancel() {
if (mCancellationSignal != null) {
mCancellationSignal!!.cancel()
}
}
override fun onAuthenticationError(errMsgId: Int, errString: CharSequence?) {
Toast.makeText(mContext, errString, Toast.LENGTH_SHORT).show()
}
override fun onAuthenticationHelp(helpMsgId: Int, helpString: CharSequence?) {
Toast.makeText(mContext, helpString, Toast.LENGTH_SHORT).show()
}
override fun onAuthenticationSucceeded(result: FingerprintManagerCompat.AuthenticationResult?) {
val cipher = result!!.cryptoObject.cipher
val encoded = PasswordStorage.getPasswordCode()
val decoded = CryptoUtils.decode(encoded!!, cipher!!)
etPincode.setText(decoded)
Toast.makeText(mContext, "success", Toast.LENGTH_SHORT).show()
}
override fun onAuthenticationFailed() {
Toast.makeText(mContext, "try again", Toast.LENGTH_SHORT).show()
}
}
Fingerprint work perfectly. It decrypt my pincode from SharedPreferences.
But how can I decrypt it manually without fingerprint?

I would encrypt a salted password and store it (as SHA256) in Android preferences and then when user enters password I would encrypt the password again and checks if the product of SHA256 is the same.

Related

Live Data Observer called only once _ Android

Live Data Observer called only once. It is not updating the data from server when api is called again to update UI.
Here is my activity:
class LoginActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
onClick(){
callAPI(binding.edtEMail.text.toString(), binding.edtPasswords.text.toString())
}
}
private fun callAPI(userName: String, password: String) {
var factory = object : ViewModelProvider.Factory {
override fun <T : ViewModel> create(modelClass: Class<T>): T {
return LoginViewModel(
networkAvailable,
application,
getLoginUseCase,
userName,
password
) as T
}
}
val loginViewModel: LoginViewModel by lazy {
ViewModelProvider(this, factory)[LoginViewModel::class.java]
}
loginViewModel.loginMainEntity.observe(this, Observer {
when (it) {
is Resource.Success -> {
it.data?.let { it ->
val intent = Intent(this, MainActivity::class.java)
startActivity(intent)
finish()
}
}
is Resource.Error -> {
it.message?.let { it ->
when (it) {
getString(R.string.invalid_login) -> {
Toast.makeText(this, R.string.error_user_pass, Toast.LENGTH_LONG)
.show()
}
getString(R.string.service_not_available) -> {
Toast.makeText(
this,
R.string.error_service_not_available,
Toast.LENGTH_LONG
)
.show()
}
else -> {
Toast.makeText(this, it, Toast.LENGTH_LONG)
.show()
}
}
}
}
}
})
}
}
And here is LoginViewModel:
class LoginViewModel constructor
(
private val networkAvailable: NetworkAvailable,
private val app: Application,
private val getLoginUseCase: GetLoginUseCase,
private val userName: String,
private val password: String
) : ViewModel() {
private val _loginMainEntity = MutableLiveData<Resource<LoginMainEntity>>()
val loginMainEntity: LiveData<Resource<LoginMainEntity>>
get() = _loginMainEntity
init {
loginValues()
}
private fun loginValues() {
viewModelScope.launch {
try {
_loginMainEntity.postValue(Resource.Loading())
} catch (e: Exception) {
_loginMainEntity.postValue(Resource.Error(app.resources.getString(R.string.unknown)))
}
try {
if (networkAvailable.isNetworkConnected()) {
try {
_loginMainEntity.postValue(getLoginUseCase.execute(userName, password))
} catch (e: Exception) {
_loginMainEntity.postValue(Resource.Error(app.resources.getString(R.string.error_api)))
}
} else if (!networkAvailable.isNetworkConnected()) {
try {
_loginMainEntity.postValue(Resource.Error(app.resources.getString(R.string.error_api_network)))
} catch (e: Exception) {
_loginMainEntity.postValue(Resource.Error(app.resources.getString(R.string.error_api_network)))
}
}
} catch (e: Exception) {
try {
_loginMainEntity.postValue(Resource.Error(app.resources.getString(R.string.error_api)))
} catch (e: Exception) {
_loginMainEntity.postValue(Resource.Error(app.resources.getString(R.string.error_api)))
}
}
}
}
}

How to get variables out from a asynctask + try & catch block in kotlin (android studio)?

In these below two functions I am getting referrerUrl and addId. I want both of them to be fetched in onCreate but don't know how because it is in try & catch block also the getGaid() function is not running without AsyncTask.
fun getreferrUrl() {
//to install referrer client
val referrerClient = InstallReferrerClient.newBuilder(this).build()
referrerClient.startConnection(object : InstallReferrerStateListener {
override fun onInstallReferrerSetupFinished(responseCode: Int) {
when (responseCode) {
InstallReferrerResponse.OK -> {
// Connection established.
try {
val response: ReferrerDetails = referrerClient.installReferrer
val referrerUrl = response.installReferrer
// here we need referrerUrl out from this fuction
} catch (e: RemoteException) {
e.printStackTrace()
}
}
//
fun getGaid() {
AsyncTask.execute {
try {
val adInfo = AdvertisingIdClient.getAdvertisingIdInfo(this)
val myId: String = if (adInfo != null) adInfo.id else null.toString()
//here we need myId out from this fuction
} catch (e: java.lang.Exception) {...}
}
}
In onCreate we need both of those strings.
// In onCreate
val url = "http://instbng.com?device_id=$device_id&
&kd_id=$kd_id&ref=$referrerUrl&gaid=$myId"
loadUrl(url)
Without coroutines, you can put the results in properties, and create a function that uses both properties and call it from both callbacks. I renamed your get... functions to fetch... since they are asynchronous. The word get in a function name implies they are synchronous.
private var referrerUrl: String? = null
private var myId: String? = null
override fun onCreate(bundle: SavedInstanceState?) {
super.onCreate(bundle)
//...
fetchReferrerUrl()
fetchGaId()
}
// proceeds with workflow if referrerUrl and myId are both available
private fun proceedIfReady() {
val referrer = referrerUrl ?: return
val id = myId ?: return
val url = "http://instbng.com?device_id=$device_id&kd_id=$kd_id&ref=$referrer&gaid=$idd"
loadUrl(url)
}
fun fetchReferrerUrl() {
val referrerClient = InstallReferrerClient.newBuilder(this).build()
referrerClient.startConnection(object : InstallReferrerStateListener {
override fun onInstallReferrerSetupFinished(responseCode: Int) {
when (responseCode) {
InstallReferrerResponse.OK -> {
// Connection established.
try {
val response: ReferrerDetails = referrerClient.installReferrer
referrerUrl = response.installReferrer
proceedIfReady()
} catch (e: RemoteException) {
e.printStackTrace()
}
}
}
}
//... handle closed connection callback
}
}
private fun fetchGaId() {
AsyncTask.execute {
try {
val adInfo = AdvertisingIdClient.getAdvertisingIdInfo(this)
runOnUiThread { // in a Fragment use view?.post
myId = if (adInfo != null) adInfo.id else null.toString()
proceedIfReady()
}
} catch (e: java.lang.Exception) {...}
}
}

How to connect BT barcode reader correctly?

I have two type of BT reader on of them is a Symbol CS3070 and the other one is a Datalogic DBT6400.
For the DBT6400 device I found an Android SDK with which the connection is very simple. But for the first one there is no supported SDK.
So I decided to connect to these device directly without SDK. Everything works fine,until the device disconnected because of sleep mode or because of I switch from one to another device.
In this case I get a lot of exception inside the while loop, because of the socket is closed. I have a tip for this why it's happen, maybe because of the coroutine, if it is possible.
UPDATE: So I found the problem, a put the catch block this line of code: reader=0, and there is only 1 error, so it's seems good. But I still don't know, is it a good solution?
My CustomReader class
class CustomReader(
private val deviceName: String,
onBarcodeRead: (barcode: String?) -> Unit
) : BarcodeReader(onBarcodeRead), CoroutineScope {
private val bluetoothAdapter = BluetoothAdapter.getDefaultAdapter()
private var pairedDevices: Set<BluetoothDevice>? = null
private var device: BluetoothDevice? = null
private lateinit var uuid: UUID
private var socket: BluetoothSocket? = null
private var connected: Boolean = false
private val localJob: Job = Job()
override val coroutineContext: CoroutineContext
get() = Dispatchers.IO + localJob
override fun init(context: Context) {
pairedDevices = bluetoothAdapter.bondedDevices
}
override fun connect(address: String) {
try {
bluetoothAdapter.startDiscovery()
uuid = UUID.fromString("00001101-0000-1000-8000-00805F9B34FB")
device = pairedDevices?.firstOrNull { x -> x.name.startsWith(deviceName) }
if(device == null){
Timber.d("CustomReader: $deviceName: Device not found")
return
}
initSocket()
launch {
run()
}
} catch (e: Exception) {
Timber.e(e)
}
}
override fun disconnect() {
try {
socket?.close()
socket = null
} catch (e: IOException) {
Timber.d("CustomReader: $deviceName: Socket closed")
}
localJob.cancel()
}
override fun destroy() {
try {
socket?.close()
socket = null
} catch (e: IOException) {
Timber.d("CustomReader: $deviceName: Socket closed")
}
localJob.cancel()
}
override fun getAddress(): String? {
return device?.address
}
override fun isConnected(): Boolean {
return connected
}
private suspend fun run() {
try {
socket?.let { socket ->
socket.connect()
connected = true
val buffer = ByteArray(1024)
var read = 0
do {
try {
read = socket.inputStream.read(buffer)
val data = String(buffer, 0, read)
onBarcodeRead.invoke(data)
} catch (e: Exception) {
Timber.e(e)
}
} while (read > 0)
CoroutineScope()
}
}catch (e: Exception){
Timber.d("CustomReader: $deviceName Disconnected")
connected = false
}
}
fun initSocket(){
if(socket != null)
return
socket = device?.createRfcommSocketToServiceRecord(uuid)
bluetoothAdapter.cancelDiscovery()
}
fun closeSocket(){
try {
socket?.close()
}catch (e: Exception){
Timber.d("CustomReader: $deviceName: Socket closed")
}
}
}
**BletoothHandlar class:**
object BluetoothHandler {
#JvmStatic
private var currentReader: BarcodeReader? = null
#JvmStatic
private val barcodeLiveData = MutableLiveData<EventWrapper<String?>>()
#JvmStatic
fun getData(): LiveData<EventWrapper<String?>> = barcodeLiveData
init {
fixedRateTimer(
name = "blueooth-reconnect",
daemon = true,
initialDelay = 0L,
period = 5000
) {
if (currentReader == null) {
Timber.d("No reader device connected")
return#fixedRateTimer
}
if (currentReader?.isConnected() == true) {
Timber.d("reader connected, doing nothing")
} else if (currentReader?.isConnected() == false) {
Timber.d("reader not connected, reconnecting")
currentReader?.connect(currentReader!!.getAddress()!!)
}
}
}
#JvmStatic
fun connectToDevice(device: BluetoothDeviceCustom, context: Context): BarcodeReader? {
if (device.address == currentReader?.getAddress() && currentReader?.isConnected() == true) {
Timber.d("device already connected")
return currentReader
}
//ha változott a kiválasztott olvasó
if (device.address != currentReader?.getAddress()) {
currentReader?.disconnect()
}
val reader = createReader(device)
if (reader == null) {
currentReader?.disconnect()
} else {
reader.init(context)
reader.connect(device.address)
}
currentReader = reader
return reader
}
#JvmStatic
fun disconnect() {
currentReader?.disconnect()
currentReader?.destroy()
currentReader = null
}
#JvmStatic
fun getConnectedDevice(): BarcodeReader? {
return currentReader
}
#JvmStatic
private fun createReader(bluetoothDeviceCustom: BluetoothDeviceCustom): BarcodeReader? {
return CustomReader(bluetoothDeviceCustom.name) {
barcodeLiveData.postValue(EventWrapper(it))
}
/*return if (bluetoothDeviceCustom.name.startsWith("DBT6400")) {
DatalogicDBT6400 {
barcodeLiveData.postValue(EventWrapper(it))
}
} else if (bluetoothDeviceCustom.name.startsWith("CS3070")) {
CustomReader(bluetoothDeviceCustom.name) {
barcodeLiveData.postValue(EventWrapper(it))
}
} else {
null
}*/
}
}
So, here it is my solution, I don't think it is the best, but it works.
const val UUID_STRING = "00001101-0000-1000-8000-00805F9B34FB"
class CustomReader(
onBarcodeRead: (barcode: String?) -> Unit
) : BarcodeReader(onBarcodeRead), CoroutineScope {
private val bluetoothAdapter = BluetoothAdapter.getDefaultAdapter()
private var pairedDevices: Set<BluetoothDevice>? = null
private var device: BluetoothDevice? = null
private var socket: BluetoothSocket? = null
private val localJob: Job = Job()
private var deviceName = ""
private val uuid = UUID.fromString(UUID_STRING)
override val coroutineContext: CoroutineContext
get() = Dispatchers.IO + localJob
override fun init(context: Context) {
pairedDevices = bluetoothAdapter.bondedDevices
}
private val singleExecutor: ExecutorCoroutineDispatcher =
Executors.newFixedThreadPool(1).asCoroutineDispatcher()
override fun connect(address: String) {
try {
device = pairedDevices?.firstOrNull { x -> x.address == address }
if(device == null){
Timber.d("CustomReader: $deviceName: Device not found")
return
}
deviceName = device?.name ?: "N/A"
initSocket()
launch {
withContext(singleExecutor){
run()
}
}
} catch (e: Exception) {
Timber.e(e)
}
}
override fun disconnect() {
closeSocket()
localJob.cancel()
}
override fun getAddress(): String? {
return device?.address
}
override fun isConnected(): Boolean {
return socket?.isConnected ?: false
}
private suspend fun run() {
try {
socket?.let { socket ->
socket.connect()
val buffer = ByteArray(1024)
var read = 0
do {
try {
read = socket.inputStream.read(buffer)
val data = String(buffer, 0, read)
onBarcodeRead.invoke(data)
} catch (e: Exception) {
Timber.d("CustomReader: $deviceName Socket disconnected")
read = -1
closeSocket()
}
} while (read > 0)
}
}catch (e: Exception){
Timber.d("CustomReader : $deviceName Disconnected")
}
}
private fun closeSocket(){
try {
socket?.close()
}catch (e: IOException){
Timber.d("CustomReader: $deviceName Socket closed")
}
socket = null
}
private fun initSocket(){
if(socket != null)
return
socket = device?.createRfcommSocketToServiceRecord(uuid)
}
}

android Kotlin socket scanner.hasNext() has No response

Server-side language: Python
Client-side language: Kotlin, Android
It receives data from different lines at one line.
class SampleActivity : AppCompatActivity() {
private val TAG = "SAMPLE_ACTIVITY_TAG"
private lateinit var editTextMessage: EditText
private val IPs = listOf("192.168.100.30")
private val PORTs = listOf(5050)
private val INDEX_SERVER = 0
private var socket: Socket? = null
private val IP = IPs[INDEX_SERVER]
private val PORT = PORTs[INDEX_SERVER]
private lateinit var outPrintWriter: PrintWriter
private val CONNECTION_TOKEN = "fab2bd65f67193d761c06b07a708d3232f0d8569"
private val TAG_CONNECT_START = "[CONNECT]"
private val TAG_CONNECT_END = "[/CONNECT]"
private val TAG_TRANSFER_START = "[TRANSFER]"
private val TAG_TRANSFER_END = "[/TRANSFER]"
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
editTextMessage = findViewById(R.id.editTextMessage)
CoroutineScope(Dispatchers.IO).launch {
connect()
}
buttonSubmit.setOnClickListener {
if(editTextMessage.text.toString().trim().isEmpty()){
return#setOnClickListener
}
CoroutineScope(Dispatchers.IO).launch {
send(messageConverter(TAG_TRANSFER_START + editTextMessage.text.toString().trim() + TAG_TRANSFER_END))
}
}
}
override fun onDestroy() {
outPrintWriter.close()
super.onDestroy()
}
private fun messageConverter(text: String): String {
Log.w(TAG, "[CONVERTING] \nMessage: $text\nAscii: ${text.toASCII()}")
return text.toASCII() // Defined in extention.kt
}
private suspend fun connect() {
try {
Log.w(TAG, "[CONNECTING] Connecting to: $IP:$PORT")
socket = Socket(IP, PORT)
socket?.receiveBufferSize = 1024
socket?.soTimeout = 1 * 60 * 60
outPrintWriter = PrintWriter(socket!!.getOutputStream())
send(messageConverter(TAG_CONNECT_START + CONNECTION_TOKEN + TAG_CONNECT_END))
receive()
} catch (uHE: UnknownHostException) {
Log.e(TAG, "[ERROR] UnknownHostException, ${uHE.message}", uHE)
} catch (eIO: IOException) {
Log.e(TAG, "[ERROR] IOException, ${eIO.message}", eIO)
} catch (e: Exception) {
Log.e(TAG, "[ERROR] Exception, ${e.message}", e)
}
}
private suspend fun send(text: String) {
try {
Log.w(TAG, "[SENDING] Message: $text")
outPrintWriter.print(text)
outPrintWriter.flush()
Log.w(TAG, "[SENDING] finished .")
clearMessageBox()
} catch (eIO: IOException) {
Log.e(TAG, "[ERROR] IOException, ${eIO.message}", eIO)
} catch (e: Exception) {
Log.e(TAG, "[ERROR] IOException, ${e.message}", e)
}
}
private suspend fun receive() {
try {
val scanner = Scanner(
socket?.getInputStream()
)
val lines = ArrayList<String>()
var line: String?
while (scanner.hasNextLine()) {
Logger.w(TAG, "[RECEIVING] Scanner hasNext")
line = scanner.nextLine()
if (!line.isNullOrEmpty()) {
lines.add(line)
}
}
Logger.w(TAG, "[RECEIVING] Exit While loop : ${lines}")
} catch (eIO: IOException) {
Log.e(TAG, "[ERROR] Receive IOException error: ${eIO.message}", eIO)
} catch (e: Exception) {
Log.e(TAG, "[ERROR] Receive Exception error: ${e.message}", e)
}
}
private suspend fun clearMessageBox() {
withContext(Main) {
editTextMessage.text.clear()
}
}
}
Logs:
[CONNECTING] Connecting to: 192.168.100.30:5050
[CONVERTING]
Message: [CONNECT]fab2bd65f67193d761c06b07a708d3232f0d8569[/CONNECT]
Ascii: 91,67,79,78,78,69,67,84,93,102,97,98,50,98,51,50,102,48,100,49,55,56,99,48,54,55,48,56,53,55,97,100,54,53,102,54,54,98,48,100,51,50,57,49,57,51,100,55,54,91,47,67,79,78,78,69,67,84,93
[SENDING] Message: 91,67,79,78,78,69,67,84,93,102,97,98,50,98,51,50,102,48,100,49,55,56,99,48,54,55,48,56,53,55,97,100,54,53,102,54,54,98,48,100,51,50,57,49,57,51,100,55,54,91,47,67,79,78,78,69,67,84,93
[SENDING] finished.
[RECEIVING] Scanner hasNext
[RECEIVING] Exit While loop : [OK1OK2]
Log: **[RECEIVING] Exit While loop :** shall be `[OK1, OK2]`
Server send messagesin every second twice
I just checked some questions and samples b, unfortunately in my case, no one solved the above issue

BoundaryCallback Not Called With DataSource

I created a boundary callback and a data source, the data source reads from the DB and the boundary callback is supposed to make the API calls and persist to the DB.
However, the overridden methods in boundary callback are never called, I don't know if there is a rule that says you can't mix both. But it is driving me crazy and I don't get it.
private val postsDataFactory = object: DataSource.Factory<String, Post>() {
override fun create(): DataSource<String, Post> {
return ItemDataSource(application)
}
}
private val postsBoundaryCallback = object: PagedList.BoundaryCallback<Post>(){
override fun onZeroItemsLoaded() {
super.onZeroItemsLoaded()
viewModelScope.launch {
try {
val posts = postsRepo.findPosts(mutableMapOf("limit" to "1"))
database.postDao().create(posts)
} catch (e: IOException) {
Log.d("NETWORK_ERROR", e.message)
} catch (e: Exception) {
Log.d("UNCAUGHT_ERROR", e.message)
}
}
}
override fun onItemAtEndLoaded(itemAtEnd: Post) {
viewModelScope.launch {
try {
val posts = postsRepo.findPosts(mutableMapOf(
"before" to itemAtEnd.id,
"limit" to "1"
))
database.postDao().create(posts)
} catch (e: IOException) {
Log.d("NETWORK_ERROR", e.message)
} catch (e: Exception) {
Log.d("UNCAUGHT_ERROR", e.message)
}
}
}
}
private val postPageConfig = PagedList
.Config
.Builder()
.setPageSize(10)
.setInitialLoadSizeHint(10)
.build()
val postsLiveData = LivePagedListBuilder(
postsDataFactory,
postPageConfig
)
.setBoundaryCallback(postsBoundaryCallback)
.build()

Categories

Resources