I am trying to get my data from Activity to Fragment but couldn't be able to do it ı think there is a problem about requestCodes and resultCodes but o couln't find it. I made a little research but the answers didn't help me.
My companion objects in the fragment
companion object {
const val REQUEST_PERMISSION = 1001
const val REQUEST_MULTIPLE_PERMISSION = 1002
const val REQUEST_TO_POST = 1003
}
Fragment to activity
view.testButton.setOnClickListener {
val intent = Intent (activity, ReaderActivity::class.java)
startActivityForResult(intent,101)
}
Activity to fragment
card_results = result;
Log.d(TAG, "ResultActivity data is :" + result);
Intent returnIntent = new Intent();
returnIntent.putExtra("cardResult", card_results );
setResult(Activity.RESULT_OK, returnIntent);
finish();
Fragment functions to handle data
private fun getDataFromNFC(intent: Intent){
Log.d(TAG, "getDataFromNFC: start")
try {
val result:String = intent.getStringExtra("cardResult").toString()
Log.d(TAG, "getDataFromNFC data is : $result")
}catch (e: NullPointerException){
Log.d(TAG, "error: $e")
Toast.makeText(activity,"Add a card",Toast.LENGTH_SHORT).show()
}
Log.d(TAG, "getDataFromNFC: ends")
}
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
Log.d(TAG, "onActivityResult: start")
if (requestCode == 101) {
when (requestCode) {
REQUEST_PERMISSION -> {
if (data != null) {
getDataFromNFC(data)
}
}
REQUEST_MULTIPLE_PERMISSION -> {
// Do something if success / failed
}
REQUEST_TO_POST -> {
// Parse result and do something
}
}
}
super.onActivityResult(requestCode, resultCode, data)
Log.d(TAG, "onActivityResult: ends")
}
The only request code you are using is 101 when you use this code: startActivityForResult(intent,101).
Then in onActivityResult, you first check if the request code is 101, which is fine, but then you have a when(requestCode) block that does stuff only if the request code is not 101, which will never be true for any of those cases, especially inside an if-block that already guaranteed that the request code is 101.
Also, it should be noted that startActivityForResult() is now deprecated in favor of Activity Result APIs.
Related
Here is, what I'm trying to do:
A Switch is turned on, starting a service in another thread (works fine so far)
When this service is successful, it should then start another function within the main thread
I don't mind whether the function is called directly by the service or the service is returning a "success"-value to the main thread, what then starts the next function from there.
Here is, what the important parts of the code looks like:
Main thread:
class SendNotif : AppCompatActivity() {
val context = this
private lateinit var Switch: Switch
// Start LocationService when the switch is on
Switch.setOnCheckedChangeListener { buttonView, isChecked ->
if (isChecked) {
Toast.makeText(context, "Starting LocationService", Toast.LENGTH_SHORT).show()
Intent(applicationContext, LocationService::class.java).apply {
action = LocationService.ACTION_START
startService(this)
}
} else {
Toast.makeText(context, "Stopping LocationService", Toast.LENGTH_SHORT).show()
Intent(applicationContext, LocationService::class.java).apply {
action = LocationService.ACTION_STOP
startService(this)
}
}
}
}
fun InitiateMessage() {
// This is the function, that is supposed to start after the LocationService
}
}
This is the LocationService. After being successful, the function InitiateMessage() should start.
class LocationService: Service() {
private val serviceScope = CoroutineScope(SupervisorJob() + Dispatchers.IO)
private lateinit var locationClient: LocationClient
var lat = 0.0F
var long = 0.0F
override fun onBind(p0: Intent?): IBinder? {
return null
}
override fun onCreate() {
super.onCreate()
locationClient = DefaultLocationClient(
applicationContext,
LocationServices.getFusedLocationProviderClient(applicationContext)
)
}
// Start or stop the service
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
when(intent?.action) {
ACTION_START -> start()
ACTION_STOP -> stop()
}
return super.onStartCommand(intent, flags, startId)
}
private fun start() {
// Starting notification
val notification = NotificationCompat.Builder(this, "location")
.setContentTitle("Tracking location...")
.setContentText("Location: null")
.setSmallIcon(R.drawable.ic_launcher_background)
// Can't swipe this notification away
.setOngoing(true)
val notificationManager = getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
// Starting the location updates
locationClient
// Every 10 seconds
.getLocationUpdates(10000L)
.catch { e -> e.printStackTrace() }
.onEach { location ->
lat = location.latitude.toString().toFloat() // .takeLast(3) // taking only the last 3 digits
long = location.longitude.toString().toFloat() // .takeLast(3)
val updatedNotification = notification.setContentText(
"Location: ($lat, $long)"
)
// notificationManager.notify(1, updatedNotification.build())
// Geofence
MyGeofence(lat, long)
}
.launchIn(serviceScope)
// startForeground(1, notification.build())
}
private fun stop() {
// Stopping the notification
stopForeground(true)
// Stopping the location service
stopSelf()
}
override fun onDestroy() {
super.onDestroy()
serviceScope.cancel()
}
companion object {
const val ACTION_START = "ACTION_START"
const val ACTION_STOP = "ACTION_STOP"
}
fun MyGeofence(lat : Float, long : Float){
val context = this
var db = DataBaseHandler(context)
var data = db.readData()
// Setting the accuracy of the geofence
val acc = 2
val safelat : Double = data.get(0).LocLat.toFloat().round(acc)
val safelong = data.get(0).LocLong.toFloat().round(acc) // .take(acc).take(acc)
val h = Handler(context.mainLooper)
if(safelat == lat.toFloat().round(acc) && safelong == long.toFloat().round(acc)){
h.post(Runnable { Toast.makeText(context, "You have reached your safe refuge! " + lat.toFloat().round(acc) + " " + long.toFloat().round(acc), Toast.LENGTH_LONG).show() })
// ToDo: Right hereafter the function InitiateMessage() should start
}
else{
h.post(Runnable { Toast.makeText(context, "You are still in great danger! " + lat.toFloat().round(acc) + " " + long.toFloat().round(acc), Toast.LENGTH_LONG).show() })
}
}
fun Float.round(decimals: Int): Double {
var multiplier = 1.0
repeat(decimals) { multiplier *= 10 }
return round(this * multiplier) / multiplier
}
}
So far, I tried it with a Looper, which did not work.
java.lang.RuntimeException: Can't create handler inside thread Thread[DefaultDispatcher-worker-1,5,main] that has not called Looper.prepare()
But I guess the far easier way would be a returned value by the service. How do I implement this, and how do I start the next function through this returned value?
I solved my problem with an observe-function and a companion object, that is a MutableLiveData.
The companion object is placed inside the main thread:
companion object {
// var iamsafe: Boolean = false
val iamsafe: MutableLiveData<Boolean> by lazy {
MutableLiveData<Boolean>()
}
}
The observe-function is placed within onCreate:
val safeObserver = Observer<Boolean> { newState ->
Toast.makeText(context, "Initiating message to my mate.", Toast.LENGTH_SHORT).show()
InitiateMessage()
}
iamsafe.observe(this, safeObserver)
The companion is changed in the second thread like this:
SendNotif.iamsafe.postValue (true)
Main Fragment:
here were initializing the recyclerview and the viewmodel and getting the data to submit to the adapter:
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
// Inflate the layout for this fragment
val binding: FragmentMainBinding = FragmentMainBinding.inflate(inflater, container, false)
this.binding = FragmentMainBinding.inflate(layoutInflater)
dataFromViewModel = arrayListOf()
setupViewModel()
setupList()
setupView()
return binding.root;
}
private fun setupView() {
viewLifecycleOwner.lifecycleScope.launchWhenCreated {
viewModel.listData.collectLatest {
Log.e("Output Frg", it.toString())
if (!dataFromViewModel.contains(it)) {
dataFromViewModel.add(it)
mainAdapter.submitData(it)
mainAdapter.notifyDataSetChanged()
Log.e(TAG, "setupView: ${mainAdapter.repos.toString()}", )
}
}
}
}
private fun setupList() {
mainAdapter = MainAdapter()
val gridLayoutManager: GridLayoutManager = GridLayoutManager(context, 2)
binding.recyclerview.apply {
layoutManager = gridLayoutManager
adapter = mainAdapter
}
// mainAdapter.addLoadStateListener { loadState ->
//
// if (loadState.refresh is LoadState.Loading && !flag) {
// binding.progressbar.visibility = View.VISIBLE
// flag = true
// } else {
// binding.progressbar.visibility = View.GONE
// }
// }
}
private fun setupViewModel() {
viewModel =
ViewModelProvider(
this,
ViewModelFactory(RetrofitService.getApiService())
)[MainViewModel::class.java]
}
Main ViewModel:
here were just calling the postdatasource function and adding it to the listData value
class MainViewModel(private val apiService: RetrofitService) : ViewModel() {
val listData = Pager(PagingConfig(pageSize = 6)) {
PostDataSource(apiService)
}.flow.cachedIn(viewModelScope)
}
PostDataSource:
Here, we have extended PostDataSource with PagingSource which will implement a suspend load function which will help us to load the data.
PostDataSource also takes a primary constructor parameter APIService. PostDataSource acts here as a repository and the load function gets the data from the API.
Since the load function is a suspend function, we can call other suspend functions inside it without any issues which we created in APIService.
In the PostDataSource, we take two parameters one of integer type and other of the data type we have to load on the list item. The integer parameter represents the page number here.
Here, we get the page number from params and assign it to nextPage variable using param.key and if it returns null, we set a default value 1.
We also do the API call and get assign the response to the response variable using APIService which we passed as a constructor parameter to PostDataSource class.
After doing all the operations with the successful response, we return the LoadResult.Page object here with the required data and it something went wrong we use LoadResult.Error.
We are also passing null as the next key if there is no corresponding data in that direction.
class PostDataSource(private val apiService: RetrofitService) : PagingSource<Int, repo>() {
override suspend fun load(params: LoadParams<Int>): LoadResult<Int, repo> {
try {
val currentLoadingPageKey = params.key ?: 1
val response = apiService.getListData(currentLoadingPageKey)
val responseData = mutableListOf<repo>()
var repos : List<repo>
val data = response
responseData.addAll(data)
Log.e(TAG, "load: $responseData", )
val prevKey = if (currentLoadingPageKey == 1) null else currentLoadingPageKey - 1
return LoadResult.Page(
data = responseData,
prevKey = prevKey,
nextKey = currentLoadingPageKey.plus(1)
)
} catch (e: Exception) {
return LoadResult.Error(e)
}
}
override fun getRefreshKey(state: PagingState<Int, repo>): Int? {
return state.anchorPosition?.let { anchorPosition ->
val anchorPage = state.closestPageToPosition(anchorPosition)
anchorPage?.prevKey?.plus(1) ?: anchorPage?.nextKey?.minus(1)
}
}
// fun getAllrepos() = retrofitService.getAllrepos()
}
MainAdapter:
Now, we will update the MainListAdapter. We will extend the MainListAdapter with PagingDataAdapter and the PagingDataAdapter will take the type of data we need to display in the list and the ViewHolder.
It also takes a DiffUtil callback, as a parameter to its primary constructor which helps the PagingDataAdapter to update the items if they are changed or updated. And DiffUtil callback is used because they are more performant.
Now, the MainListAdapter looks like,
class MainAdapter : PagingDataAdapter<repo, MainAdapter.ViewHolder>(DiffCallBack) , Filterable {
private lateinit var Repos : List<repo>
class ViewHolder(view: View, mlistener: onItemClickListener) : RecyclerView.ViewHolder(view)
private lateinit var mlistener: onItemClickListener
interface onItemClickListener {
fun OnItemClick(position: Int)
}
var repos: MutableList<repo> = ArrayList()
private var reposFull: MutableList<repo> = ArrayList()
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
holder.itemView.findViewById<TextView>(R.id.title).text = getItem(position)?.name
Log.e(TAG,"name ${holder.itemView.findViewById<TextView>(R.id.title).text}", )
Glide.with(holder.itemView.context)
.load(getItem(position)?.owner?.avatar_url)
.into(holder.itemView.findViewById(R.id.imgCircle))
// val repo = repos[position]
//
//
// val mainViewHolder: ViewHolder = holder as ViewHolder
// mainViewHolder.itemView Title?.text = repo.name
// mainViewHolder.Image?.let {
// Glide.with(holder.itemView.context).load(repo.owner.avatar_url).into(
// it
// )
// }
}
fun submitList(list: PagingData<repo>) {
Repos = list as List<repo>
notifyDataSetChanged()
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
return ViewHolder(
LayoutInflater
.from(parent.context)
.inflate(R.layout.repo_layout, parent, false), mlistener
)
}
fun setOnItemClickListener(listener: onItemClickListener) {
mlistener = listener
}
object DiffCallBack : DiffUtil.ItemCallback<repo>() {
override fun areItemsTheSame(oldItem: repo, newItem: repo): Boolean {
return oldItem.id == newItem.id
}
override fun areContentsTheSame(oldItem: repo, newItem: repo): Boolean {
return oldItem == newItem
}
}
// class MainViewHolder(itemView: View, listener: onItemClickListener) : RecyclerView.ViewHolder(itemView) {
// var Title: TextView? =
// itemView.findViewById<View>(com.lau.google_rep.R.id.title) as TextView
// var Image: ImageView? =
// itemView.findViewById<View>(com.lau.google_rep.R.id.imgCircle) as ImageView
//
// init {
// itemView.setOnClickListener {
// listener.OnItemClick(adapterPosition)
// }
// }
// }
override fun getFilter(): Filter {
return exampleFilter
}
private val exampleFilter: Filter = object : Filter() {
override fun performFiltering(constraint: CharSequence?): FilterResults? {
val filteredList: MutableList<repo> = ArrayList()
if (constraint == null || constraint.length == 0) {
filteredList.addAll(reposFull)
} else {
val filterPattern = constraint.toString().toLowerCase().trim { it <= ' ' }
for (item in reposFull) {
if (item.name.toLowerCase().contains(filterPattern)) {
filteredList.add(item)
}
}
}
Log.d(TAG, "publishResults: $filteredList")
val results = FilterResults()
results.values = filteredList
return results
}
#SuppressLint("NotifyDataSetChanged")
override fun publishResults(constraint: CharSequence?, results: FilterResults) {
repos.clear()
(results.values as? Collection<repo>)?.let { repos.addAll(it) }
notifyDataSetChanged()
}
}
}
retrofit service:
interface RetrofitService {
// #Headers("Authorization: token ")
#GET("repos")
suspend fun getListData(#Query("page") pageNumber: Int): List<repo>
companion object{
private val moshi = Moshi.Builder()
.add(KotlinJsonAdapterFactory())
.build()
fun getApiService() = Retrofit.Builder()
.baseUrl("https://api.github.com/orgs/google/")
.addConverterFactory(MoshiConverterFactory.create(moshi))
.build()
.create(RetrofitService::class.java)
}
}
I am using BiometricPromt in my application.
Now I need to implement way for user to enter the PIN when Biometric is not available or enrolled.
I can use .setDeviceCredentialAllowed(true) but problem with it is when Biometric is not available or enrolled it won't show any dialog, and even if user use PIN if Biometric is available it doesn't registrate it in onAuthenticationSucceeded.
So now I am trying to implement negative button so user can enter PIN and I can catch it and see if user entered it correctly but I can't find anywhere if it is possible to do with default android PIN manager, so I don't have to implement any 3rd party library?
My current code is:
fun biometricCheck(context: Context, fragment: FragmentActivity){
val executor = ContextCompat.getMainExecutor(context)
val biometricManager = BiometricManager.from(context)
init(fragment)
when {
biometricManager.canAuthenticate() == BiometricManager.BIOMETRIC_SUCCESS -> {
authUser(executor, context, fragment)
}
biometricManager.canAuthenticate() == BiometricManager.BIOMETRIC_ERROR_NO_HARDWARE -> {
//No hardware
}
biometricManager.canAuthenticate() == BiometricManager.BIOMETRIC_ERROR_HW_UNAVAILABLE -> {
//Hardware unvailable
}
biometricManager.canAuthenticate() == BiometricManager.BIOMETRIC_ERROR_NONE_ENROLLED -> {
//NONE ENROLLED
}
}
}
private fun init(fragment: FragmentActivity){
//Some code
}
private fun authUser(executor: Executor, context: Context, fragment: FragmentActivity) {
val promptInfo = BiometricPrompt.PromptInfo.Builder()
.setTitle("Authentication")
//.setSubtitle("Owner confirmation")
.setDescription("Scan fingerprint or enter PIN")
//.setDeviceCredentialAllowed(true)
.setNegativeButtonText("TEST")
.build()
val biometricPrompt = BiometricPrompt(fragment, executor,
object : BiometricPrompt.AuthenticationCallback() {
override fun onAuthenticationSucceeded(
result: BiometricPrompt.AuthenticationResult
) {
super.onAuthenticationSucceeded(result)
val intent = Intent(context, MainActivity::class.java)
context.startActivity(intent)
}
override fun onAuthenticationError(
errorCode: Int, errString: CharSequence
) {
super.onAuthenticationError(errorCode, errString)
//Error auth
if (errorCode == BiometricConstants.ERROR_USER_CANCELED) {
//Some long code
}
}
if(errorCode == BiometricPrompt.ERROR_NEGATIVE_BUTTON) {
Log.e(TAG, "User clicked")
//loginWithPassword() // I don't know what to implement here
}
}
override fun onAuthenticationFailed() {
super.onAuthenticationFailed()
//Failed auth
}
})
biometricPrompt.authenticate(promptInfo)
}
I have a remote server from where I want to fetch 20 items(Job) per api call and show them in RecyclerView using paging library.
For that, I want to show a loading indicator at the beginning of the first api call when list of items is being fetched from the server. Everything is okay if data is fetched successfully. That means the loading indicator got invisible if data loaded successfully. The code is given bellow.
JobService.KT
#GET(Constants.API_JOB_LIST)
fun getJobPost(
#Query("page") pageNumber: Int
): Observable<Response<JobResponse>>
JobResponse.kt
data class JobResponse(
#SerializedName("status") val status: Int? = null,
#SerializedName("message") val message: Any? = null,
#SerializedName("data") val jobData: JobData? = null
)
JobData.kt
data class JobData(
#SerializedName("jobs") val jobs: List<Job?>? = null,
#SerializedName("total") val totalJob: Int? = null,
#SerializedName("page") val currentPage: Int? = null,
#SerializedName("showing") val currentlyShowing: Int? = null,
#SerializedName("has_more") val hasMore: Boolean? = null
)
NetworkState.kt
sealed class NetworkState {
data class Progress(val isLoading: Boolean) : NetworkState()
data class Failure(val errorMessage: String?) : NetworkState()
companion object {
fun loading(isLoading: Boolean): NetworkState = Progress(isLoading)
fun failure(errorMessage: String?): NetworkState = Failure(errorMessage)
}
}
Event.kt
open class Event<out T>(private val content: T) {
private var hasBeenHandled = false
fun getContentIfNotHandled() = if (hasBeenHandled) {
null
} else {
hasBeenHandled = true
content
}
fun peekContent() = content
}
JobDataSource.kt
class JobDataSource(
private val jobService: JobService,
private val compositeDisposable: CompositeDisposable
) : PageKeyedDataSource<Int, Job>() {
val paginationState: MutableLiveData<Event<NetworkState>> = MutableLiveData()
val initialLoadingState: MutableLiveData<Event<NetworkState>> = MutableLiveData()
val totalJob: MutableLiveData<Event<Int>> = MutableLiveData()
companion object {
private const val FIRST_PAGE = 1
}
override fun loadInitial(params: LoadInitialParams<Int>, callback: LoadInitialCallback<Int, Job>) {
compositeDisposable += jobService.getJobPost(FIRST_PAGE)
.performOnBackgroundOutputOnMain()
.doOnSubscribe { initialLoadingState.postValue(Event(loading(true))) }
.doOnTerminate { initialLoadingState.postValue(Event(loading(false))) }
.subscribe({
if (it.isSuccessful) {
val jobData = it.body()?.jobData
totalJob.postValue(Event(jobData?.totalJob!!))
jobData.jobs?.let { jobs -> callback.onResult(jobs, null, FIRST_PAGE+1) }
} else {
val error = Gson().fromJson(it.errorBody()?.charStream(), ApiError::class.java)
when (it.code()) {
CUSTOM_STATUS_CODE -> initialLoadingState.postValue(Event(failure(error.message!!)))
else -> initialLoadingState.postValue(Event(failure("Something went wrong")))
}
}
}, {
if (it is IOException) {
initialLoadingState.postValue(Event(failure("Check Internet Connectivity")))
} else {
initialLoadingState.postValue(Event(failure("Json Parsing error")))
}
})
}
override fun loadAfter(params: LoadParams<Int>, callback: LoadCallback<Int, Job>) {
compositeDisposable += jobService.getJobPost(params.key)
.performOnBackgroundOutputOnMain()
.doOnSubscribe { if (params.key != 2) paginationState.postValue(Event(loading(true))) }
.doOnTerminate { paginationState.postValue(Event(loading(false))) }
.subscribe({
if (it.isSuccessful) {
val jobData = it.body()?.jobData
totalJob.postValue(Event(jobData?.totalJob!!))
jobData.jobs?.let { jobs -> callback.onResult(jobs, if (jobData.hasMore!!) params.key+1 else null) }
} else {
val error = Gson().fromJson(it.errorBody()?.charStream(), ApiError::class.java)
when (it.code()) {
CUSTOM_STATUS_CODE -> initialLoadingState.postValue(Event(failure(error.message!!)))
else -> initialLoadingState.postValue(Event(failure("Something went wrong")))
}
}
}, {
if (it is IOException) {
paginationState.postValue(Event(failure("Check Internet Connectivity")))
} else {
paginationState.postValue(Event(failure("Json Parsing error")))
}
})
}
override fun loadBefore(params: LoadParams<Int>, callback: LoadCallback<Int, Job>) {}
}
JobDataSourceFactory.kt
class JobDataSourceFactory(
private val jobService: JobService,
private val compositeDisposable: CompositeDisposable
): DataSource.Factory<Int, Job>() {
val jobDataSourceLiveData = MutableLiveData<JobDataSource>()
override fun create(): DataSource<Int, Job> {
val jobDataSource = JobDataSource(jobService, compositeDisposable)
jobDataSourceLiveData.postValue(jobDataSource)
return jobDataSource
}
}
JobBoardViewModel.kt
class JobBoardViewModel(
private val jobService: JobService
) : BaseViewModel() {
companion object {
private const val PAGE_SIZE = 20
private const val PREFETCH_DISTANCE = 20
}
private val jobDataSourceFactory: JobDataSourceFactory = JobDataSourceFactory(jobService, compositeDisposable)
var jobList: LiveData<PagedList<Job>>
init {
val config = PagedList.Config.Builder()
.setPageSize(PAGE_SIZE)
.setInitialLoadSizeHint(PAGE_SIZE)
.setPrefetchDistance(PREFETCH_DISTANCE)
.setEnablePlaceholders(false)
.build()
jobList = LivePagedListBuilder(jobDataSourceFactory, config).build()
}
fun getPaginationState(): LiveData<Event<NetworkState>> = Transformations.switchMap<JobDataSource, Event<NetworkState>>(
jobDataSourceFactory.jobDataSourceLiveData,
JobDataSource::paginationState
)
fun getInitialLoadingState(): LiveData<Event<NetworkState>> = Transformations.switchMap<JobDataSource, Event<NetworkState>>(
jobDataSourceFactory.jobDataSourceLiveData,
JobDataSource::initialLoadingState
)
fun getTotalJob(): LiveData<Event<Int>> = Transformations.switchMap<JobDataSource, Event<Int>>(
jobDataSourceFactory.jobDataSourceLiveData,
JobDataSource::totalJob
)
}
JobBoardFragment.kt
class JobBoardFragment : BaseFragment() {
private val viewModel: JobBoardViewModel by lazy {
getViewModel { JobBoardViewModel(ApiFactory.jobListApi) }
}
private val jobAdapter by lazy {
JobAdapter {
val bundle = Bundle()
bundle.putInt(CLICKED_JOB_ID, it.jobId!!)
navigateTo(R.id.jobBoard_to_jobView, R.id.home_navigation_fragment, bundle)
}
}
override fun getLayoutResId() = R.layout.fragment_job_board
override fun initWidget() {
job_list_recycler_view.adapter = jobAdapter
back_to_main_image_view.setOnClickListener { onBackPressed() }
}
override fun observeLiveData() {
with(viewModel) {
jobList.observe(this#JobBoardFragment, Observer {
jobAdapter.submitList(it)
})
getInitialLoadingState().observe(this#JobBoardFragment, Observer {
it.getContentIfNotHandled()?.let { state ->
when (state) {
is Progress -> {
if (state == loading(true)) {
network_loading_indicator.visible()
} else {
network_loading_indicator.visibilityGone()
}
}
is Failure -> context?.showToast(state.errorMessage.toString())
}
}
})
getPaginationState().observe(this#JobBoardFragment, Observer {
it.getContentIfNotHandled()?.let { state ->
when (state) {
is Progress -> {
if (state == loading(true)) {
pagination_loading_indicator.visible()
} else {
pagination_loading_indicator.visibilityGone()
}
}
is Failure -> context?.showToast(state.errorMessage.toString())
}
}
})
getTotalJob().observe(this#JobBoardFragment, Observer {
it.getContentIfNotHandled()?.let { state ->
job_board_text_view.visible()
with(profile_completed_image_view) {
visible()
text = state.toString()
}
}
})
}
}
}
But the problem is if data fetching failed due to internet connectivity or any other server related problem loading indicator does not invisible that means it still loading though I make the loadingStatus false and error message is shown. it means .doOnTerminate { initialLoadingState.postValue(Event(loading(false))) } is not called if error occured. This is the first problem. Another problem is loadInitial() and loadAfter() is being called simultaneously at the first call. But I just want the loadInitial() method is called at the beginning. after scrolling loadAfter() method will be called.
Try replacing all your LiveData's postValue() methods by setValue() or simply .value =.
The problem is that the postValue() method is for updating the value from a background thread to observers in the main thread. In this case you are always changing the values from the main thread itself, so you should use .value =.
Hope it's not too late.
I'm using EMDK 2.5 (VS2008 and VC# and .NetCF3.5) Barcode2 class from the library to write a sample application to scan bar codes. I followed the samples available in EMDK namely CS_Barcode2Sample1 project.Every time I hardware trigger the scan the notification "E_SCN_READINCOMPATIBLE" is thrown and not able to retrieve the scanned data. The documentation doesn't say much about the cause of E_SCN_READINCOMPATIBLE notification and no luck from Google search. I tried several options including making use of Symbol.Barcode and the outcome is same.
I also tried EMDK 2.3 but the result is same.
I've pasted the whole code here....
public partial class Form1 : Form
{
private Barcode2 myBarcode2 = null;
public Form1()
{
InitializeComponent();
InitBarcode();
}
public bool InitBarcode()
{
// If the Barcode2 object is already initialized then fail the initialization.
if (myBarcode2 != null)
{
return false;
}
else // Else initialize the reader.
{
try
{
Symbol.Barcode2.Device[] AvailableDevices = Symbol.Barcode2.Devices.SupportedDevices;
if (AvailableDevices.Length == 0)
{
return false;
}
if (AvailableDevices.Length == 1)
{
//get the first available scanner in the list
Symbol.Barcode2.Device MyDevice = AvailableDevices[0];
// Create the reader, based on selected device.
myBarcode2 = new Barcode2(MyDevice);
// Attach a scan notification handler.
//this.myScanNotifyHandler = new Barcode2.OnScanHandler(myBarcode2_ScanNotify);
myBarcode2.OnScan += myBarcode2_ScanNotify;
// Attach a status notification handler.
//this.myStatusNotifyHandler = new Barcode2.OnStatusHandler(myBarcode2_StatusNotify);
myBarcode2.OnStatus += myBarcode2_StatusNotify;
myBarcode2.Config.TriggerMode = TRIGGERMODES.HARD;
// Submit a scan.
myBarcode2.Scan(5000);
}
}
catch (OperationFailureException ex)
{
MessageBox.Show("Exception Raised 1");
return false;
}
catch (InvalidRequestException ex)
{
MessageBox.Show("Exception Raised 2");
return false;
}
catch (InvalidIndexerException ex)
{
MessageBox.Show("Exception Raised 3");
return false;
}
}
return false;
}
private void myBarcode2_ScanNotify(ScanDataCollection scanDataCollection)
{
// Checks if the BeginInvoke method is required because the OnScan delegate is called by a different thread
if (this.InvokeRequired)
{
// Executes the OnScan delegate asynchronously on the main thread
this.BeginInvoke(new Barcode2.OnScanHandler(myBarcode2_ScanNotify), new object[] { scanDataCollection });
}
else
{
// Get ScanData
ScanData scanData = scanDataCollection.GetFirst;
int i;
switch (scanData.Result)
{
case Symbol.Barcode2.Results.SUCCESS:
String str = scanData.Text;
myBarcode2.Config.TriggerMode = TRIGGERMODES.HARD;
myBarcode2.Scan(5000);
break;
case Symbol.Barcode2.Results.E_SCN_READTIMEOUT:
break;
case Symbol.Barcode2.Results.CANCELED:
break;
case Symbol.Barcode2.Results.E_SCN_DEVICEFAILURE:
i = 93;
break;
default:
if (scanData.Result == Symbol.Barcode2.Results.E_SCN_READINCOMPATIBLE)
{
// If the failure is E_SCN_READINCOMPATIBLE, exit the application.
MessageBox.Show("Fatal Error");
this.Close();
return;
}
break;
}
}
}
private void myBarcode2_StatusNotify(StatusData statusData)
{
// Checks if the Invoke method is required because the OnStatus delegate is called by a different thread
if (this.InvokeRequired)
{
// Executes the OnStatus delegate on the main thread
this.Invoke(new Barcode2.OnStatusHandler(myBarcode2_StatusNotify), new object[] { statusData });
}
else
{
int i;
switch (statusData.State)
{
case States.IDLE:
break;
case States.READY:
break;
default:
break;
}
}
}
}
}
I've went thru this recently also, as I observed, it probably due to the scanner device is occupied by other application, where the scan request has been queued already, you can go to memory management, and kill the suspect app, and try your app again.
Refer to the Symbol FAQ