BoundaryCallback Not Called With DataSource - android

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()

Related

android parallel API requests with retrofit and coroutines

I have multiple API requests that need to be called in parallel, the sequence doesn't matter.
What really matters is that all the calls should be requested to implement the UI.
The problem is that sometimes some requests don't get called which returns null values, in other words, NO GUARANTEE THAT ALL THE REQUESTS WILL BE CALLED.
I have read a lot about parallel API requests and launchers but still don't know what I have missed or what I did wrong.
here is my view model class that has all functions
#HiltViewModel
class MatchDetailsViewModel #Inject constructor(
val api:FootballApi,
val app: Application,
): AndroidViewModel(app) {
private val _matchDetailsMutableLiveData = MutableLiveData<ResponseState<FixtureById>>()
private val _matchStatsMutableLiveData = MutableLiveData<ResponseState<Stats>>()
private val _matchLineupsMutableLiveData = MutableLiveData<ResponseState<Lineups>>()
private val _matchBenchMutableLiveData = MutableLiveData<ResponseState<Bench>>()
private val _matchSideLinesMutableLiveData = MutableLiveData<ResponseState<SideLine>>()
private val _matchStandingsMutableLiveData = MutableLiveData<ResponseState<Standings>>()
val matchStandingsLiveData: LiveData<ResponseState<Standings>> = _matchStandingsMutableLiveData
val matchSideLinesLiveData: LiveData<ResponseState<SideLine>> = _matchSideLinesMutableLiveData
val matchStatsLiveData: LiveData<ResponseState<Stats>> = _matchStatsMutableLiveData
val matchDetailsLiveData: LiveData<ResponseState<FixtureById>> = _matchDetailsMutableLiveData
val matchLineupsLiveData: LiveData<ResponseState<Lineups>> = _matchLineupsMutableLiveData
val matchBenchLiveData: LiveData<ResponseState<Bench>> = _matchBenchMutableLiveData
fun callAll() {
viewModelScope.launch {
val getMatchDetailsCall = async { getMatchDetails(1582601) }
val getMatchStatsCall = async { getMatchStats(1582601) }
val getMatchLineupsCall = async { getMatchLineups(1582601) }
val getMatchBenchCall = async { getMatchBench(1582601) }
val getMatchSideLineCall = async { getMatchSideLine(15006543) }
val getMatchStandingsCall = async { getMatchStandings(12880) }
try {
getMatchDetailsCall.await()
getMatchStatsCall.await()
getMatchLineupsCall.await()
getMatchBenchCall.await()
getMatchSideLineCall.await()
getMatchStandingsCall.await()
}
catch (_: Exception){}
}
}
suspend fun getMatchStandings(seasonId: Int) = viewModelScope.launch(Dispatchers.IO) {
_matchStandingsMutableLiveData.postValue(ResponseState.Loading())
try {
val response = api.getMatchStandings(seasonId = seasonId)
Log.i("getMatchStanding()", response.body().toString())
_matchStandingsMutableLiveData.postValue(ResponseState.Success(response.body()!!))
}
catch (exception: Exception) {
Log.e("getMatchStanding()", exception.toString())
}
}
suspend fun getMatchSideLine(id: Int) = viewModelScope.launch(Dispatchers.IO) {
_matchSideLinesMutableLiveData.postValue(ResponseState.Loading())
try {
val response = api.getMatchSideLines(id = id)
Log.i("getMatchSideline()", response.body().toString())
_matchSideLinesMutableLiveData.postValue(ResponseState.Success(response.body()!!))
} catch (exception: Exception) {
Log.e("getMatchSideline()", exception.toString())
}
}
fun getMatchDetails(id: Int) = viewModelScope.launch(Dispatchers.IO) {
_matchDetailsMutableLiveData.postValue(ResponseState.Loading())
try {
val response = api.getMatchDetails(id = id)
Log.i("getMatchDetails()", response.body().toString())
_matchDetailsMutableLiveData.postValue(ResponseState.Success(response.body()!!))
} catch (exception: Exception) {
Log.e("getMatchDetails()", exception.toString())
}
}
suspend fun getMatchStats(id: Int) = viewModelScope.launch(Dispatchers.IO) {
_matchStatsMutableLiveData.postValue(ResponseState.Loading())
try {
val response = api.getMatchStats(id = id)
Log.i("getMatchStats()", response.body().toString())
_matchStatsMutableLiveData.postValue(ResponseState.Success(response.body()!!))
} catch (exception: Exception) {
Log.e("getMatchStats()", exception.toString())
}
}
suspend fun getMatchLineups(id: Int = 6) = viewModelScope.launch(Dispatchers.IO) {
_matchLineupsMutableLiveData.postValue(ResponseState.Loading())
try {
val response = api.getMatchLineups(id = id)
Log.i("getMatchLineups()", response.body().toString())
_matchLineupsMutableLiveData.postValue(ResponseState.Success(response.body()!!))
} catch (exception: Exception) {
Log.e("getMatchLineups()", exception.toString())
}
}
suspend fun getMatchBench(id: Int) = viewModelScope.launch(Dispatchers.IO) {
_matchBenchMutableLiveData.postValue(ResponseState.Loading())
try {
val response = api.getMatchBench(id = id)
Log.i("getMatchBench()", response.body().toString())
_matchBenchMutableLiveData.postValue(ResponseState.Success(response.body()!!))
} catch (exception: Exception) {
Log.e("getMatchBench()", exception.toString())
}
}
}
And here's my call in activity class
lifecycleScope.launch(Dispatchers.IO) {
matchDetailsViewModel.callAll()
}

Firebase Auth with Kotlin Flow

I am learning clean architecture and Kotlin Flow. I want to check is user mail exists in the Firebase Auth base. However, when I threw an error to the flow function, app is crash.
CheckUserUseCase.kt
class CheckUserUseCase #Inject constructor(private val repository: SignInRepository) {
operator fun invoke(mail: String): Flow<Status<Boolean, String>> = flow {
emit(Status.Loading(data = null))
try {
repository.isUserExists(mail = mail)
emit(Status.Success(data = true))
} catch (e: Exception) {
emit(Status.Error(message = e.message, data = false))
}
}
}
SignInRepository.kt
interface SignInRepository {
suspend fun isUserExists(mail: String)
}
SignInRepositoryImpl.kt
class SignInRepositoryImpl #Inject constructor(private val firebaseUserActions: FirebaseUserActions) : SignInRepository {
override suspend fun isUserExists(mail: String) {
firebaseUserActions.isUserExists(mail = mail)
}
}
FirebaseAuthentication.kt
class FirebaseAuthentication #Inject constructor(private val auth: FirebaseAuth) : FirebaseUserActions {
override suspend fun isUserExists(mail: String){
auth.fetchSignInMethodsForEmail(mail).addOnCompleteListener { task ->
task.result.signInMethods?.let {
if (it.size != 0) Log.i("App.tag", "True.")
else throw IOException() <-- Crash point.
}
}.addOnFailureListener { e -> e.printStackTrace() }
.await()
}
}
How can I return a state to Kotlin Flow method? Thank you!
Please try the following approach:
override suspend fun isUserExists(mail: String): Status {
return try {
val result = auth.fetchSignInMethodsForEmail(mail).await()
result.signInMethods?.let {
if (it.isNotEmpty()) {
Status.Success(data = true)
} else {
Status.Error(message = "No data", data = false)
}
} ?: Status.Error(message = "No Data", data = false)
} catch (e: Exception) {
Status.Error(message = e.message, data = false)
}
}
In CheckUserUseCase class just emit the result of calling isUserExists():
emit(Status.Loading(data = null))
emit(repository.isUserExists(mail = mail))
Try
it.size != 0 && it.size != null
and
if (task.isSuccessful()) {
[...]
task.result.signInMethods?.let {
[...]
}

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) {...}
}
}

Android: Kotlin getting data from callback and emit with flow

Everyone, I have this method and want to emit listmessage but list is unable to get the list of values, How can I do it?
fun ReadMessage(dialog: QBChatDialog) = flow{
try {
var list: ArrayList<QBChatMessage> = ArrayList()
chatHelper.ReadChatHistory(dialog, object : QBEntityCallback<ArrayList<QBChatMessage>>{
override fun onSuccess(listmessage: ArrayList<QBChatMessage>?, p1: Bundle?) {
Log.e(TAG, "Reading Message: $p0")
}
override fun onError(p0: QBResponseException?) {
Log.e(TAG, "Reading Message Exception: $p0")
}
})
Log.e(TAG, "Reading Messageeeeeeeeee: $list")
emit(list)
}catch (e: Exception){
Log.e(TAG, "Reading Message Exceptionn: $e")
}
}
You should use CompletableDeferred. You can do something like this:
fun readMessage(dialog: QBChatDialog): Flow<ArrayList<QBChatMessage>> {
val historyDeferred = CompletableDeferred<ArrayList<QBChatMessage>>()
chatHelper.ReadChatHistory(dialog, object : QBEntityCallback<ArrayList<QBChatMessage>> {
override fun onSuccess(listmessage: ArrayList<QBChatMessage>?, p1: Bundle?) {
historyDeferred.complete(listmessage ?: arrayListOf())
}
override fun onError(p0: QBResponseException?) {
historyDeferred.completeExceptionally(p0 ?: CancellationException())
}
})
return flow {
try {
emit(historyDeferred.await())
} catch (e: Exception) {
Log.e(TAG, "Reading Message Exceptionn: $e")
}
}
}

Use Fingerprint and pin

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.

Categories

Resources