How to save string message from BroadcastReceiver and use saved variable in Activity? I'm finding only Toast.makeText examples.
What i'm actually have: working, registered BroadcastReceiver. My app running in DocumentActivity, when i'm hitting Scan button on my DataCollectionTerminal (DTC on Android 7.0), DTC takes message and toast it. I can catch messages from DTC in opened EditText and save it onClick save button.
But what i'm need: Scan button pressed => DTC takes barcode message => send to Activity and save it to some variable => and i can use this var.value in whole Activity, set it TextView, write it to the txt document and etc.
DocumentActivity
class DocumentActivity : AppCompatActivity() {
private val customBroadcastReceiver = CustomBroadcastReceiver()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(exp..R.layout.activity_document)
}
override fun onResume() {
super.onResume()
registerReceiver(
customBroadcastReceiver,
IntentFilter ("com.xcheng.scanner.action.BARCODE_DECODING_BROADCAST")
)
}
override fun onStop() {
super.onStop()
unregisterReceiver(customBroadcastReceiver)
}
fun saveMessage(mes: String){
var code = mes
Toast.makeText(applicationContext, code, Toast.LENGTH_SHORT).show()
...
}}
BroadcastReceiver
class CustomBroadcastReceiver : BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent) {
val type = intent.getStringExtra("EXTRA_BARCODE_DECODING_SYMBOLE")
val barcode = intent.getStringExtra("EXTRA_BARCODE_DECODING_DATA")
val sb = StringBuilder()
sb.append("Type: $type, Barcode:$barcode\n")
Toast.makeText(context, sb.toString(), Toast.LENGTH_SHORT).show()
// Save mes, doesnt work
DocumentActivity().saveCell(barcode)
}}
You are creating another object of DocumentActivity and saving the value in variable for that object, so it will not reflect in cuurent object. Try to make variable static and then update it from broadcasteceiver. For eg variable in DocumentActivity is barCodeVal so,
in DocumentActivity
companion object{
var barcodeVal = //some default value
}
Then from broabcastreceiver
DocumentActivity.barcodeVal = barcode
Related
When registering a BroadcastReceiver with the IntentFilter Intent.ACTION_CLOSE_SYSTEM_DIALOGS, the homekey pressed event is received twice, however the other events like recentapps is received only once. I'm not registering any receivers in the AndroidManifest.xml file.
Following is the code snippet I'm trying to use:
MainActivity.kt
private const val HOME_KEY = "homekey"
private const val RECENT_APPS = "recentapps"
private const val REASON = "reason"
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
findViewById<Button>(R.id.press_me).setOnClickListener {
registerReceiver(
navigationEventReceiver,
IntentFilter(Intent.ACTION_CLOSE_SYSTEM_DIALOGS)
)
}
}
private val navigationEventReceiver = object : BroadcastReceiver() {
override fun onReceive(context: Context?, intent: Intent?) {
if (intent?.action != Intent.ACTION_CLOSE_SYSTEM_DIALOGS) return
val pressedKey = intent.getStringExtra(REASON)
if (pressedKey == HOME_KEY) {
Log.d("Event ", "home key")
}
if (pressedKey == RECENT_APPS) {
Log.d("Event ", "recent apps")
}
}
}
}
The logs that get printed in the logcat after this are as follows:
When pressing home button
D/Event: home key
D/Event: home key
When pressing recent apps button
D/Event: recent apps
I have built a QR scanning application (written in Kotlin, for Android). It scans for a QR code and then returns the URL after its scanned like so.
However, I wish to take it a step further and actually launch the return value of the QR code into a search engine of an internet application and have it display the results of what the QR code had intended. How would I get the application to get the returned URL and redirect the user to the intended place?
Here is my MainActivity.kt for reference:
class MainActivity : AppCompatActivity() {
private lateinit var codeScanner: CodeScanner
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val scannerView = findViewById<CodeScannerView>(R.id.scanner_view)
codeScanner = CodeScanner(this, scannerView)
codeScanner.camera = CodeScanner.CAMERA_BACK
codeScanner.formats = CodeScanner.ALL_FORMATS
codeScanner.autoFocusMode = AutoFocusMode.SAFE
codeScanner.scanMode = ScanMode.SINGLE
codeScanner.isAutoFocusEnabled = true
codeScanner.isFlashEnabled = false
codeScanner.decodeCallback = DecodeCallback {
runOnUiThread {
Toast.makeText(this, "Scan result: ${it.text}", Toast.LENGTH_LONG).show()}
}
scannerView.setOnClickListener {
codeScanner.startPreview()
}
}
override fun onResume() {
super.onResume()
codeScanner.startPreview()
}
override fun onPause() {
codeScanner.releaseResources()
super.onPause()
}
}
I used this answer to build a searchWeb function within my MainActivity.kt class like so;
fun searchWeb(query: String) {
val url = "http://www.google.com"
val intent = Intent(Intent.ACTION_VIEW);
intent.setData(Uri.parse(url))
startActivity(intent)
}
I then called the function within my codeScanner.decodeCallback like so;
codeScanner.decodeCallback = DecodeCallback {
runOnUiThread {
Toast.makeText(this, "Scan result: ${it.text}", Toast.LENGTH_LONG).show()
}
searchWeb(it.text)
}
After doing this, the implicit intent was launched and the QR application launched the internet browser.
I'm trying to get my data from my ViewModel to my MainActivity.
I know that what I'm doing in my MainActivity is very wrong, but I can't seem to configure it in a way that will get the data into the MainActivity. It's just a simple string.
ViewModel
class MovieSearchViewModel : ViewModel() {
var searchTerm = ""
fun getSearchTerm(query: String) {
searchTerm = query
}
}
MainActivity
open class MainActivity : AppCompatActivity() {
private val viewModel: MovieSearchViewModel by viewModels()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
var searchTerm = viewModel.searchTerm
}
}
View Model
you can use LiveData to always get the latest data in Activity
var searchTerm = MutableLiveData("")
fun setSearchTerm(query: String){
searchTerm.value = query
}
MainActivity
in observe it will always run every time there is a data change
viewModel.searchTerm.observe(this){
Log.e("tag", it)
}
or
if you want get the data manually without observe you can do like this
var data = viewModel.searchTerm.value
I want to move from one Activity which displays a RecyclerView to another Activity (detail). But when I added data transmission via Intent, the data always failed to be taken in the Activity detail.
This is the error:
My MainDetail:
private lateinit var viewModel: MainDetailModel
var idAnime: String = "34134"
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main_detail)
idAnime = intent.getStringExtra("idAnime")
println("idanime $idAnime")
setupFragment()
setupViewModel()
}
}
ViewModel:
class MainViewModel(context: Application, private val appRepository: AppRepository, private val contexts: Context) : AndroidViewModel(context), MainItemClickAction {
override fun onItemClicked(detailModel: DetailModel) {
var intent = Intent(contexts, MainDetailActivity::class.java)
intent.putExtra("idAnime",detailModel.mal_id )
contexts.startActivity(intent)
}
}
Check if your field "detailModel.mal_id" mal_id in that case is string, because you're requesting string in details activity. If it's string check also if this "mal_id" is null. Other issues from code you provided can't be seen.
Check your value idAnime, it exists or not, I think is better check all value is empty or not before putting into listView or another view.
Activity receiving intent
class AddNoteActivity : AppCompatActivity() {
private lateinit var addViewModel: NoteViewModel
private lateinit var titleEditText: TextInputEditText
private lateinit var contentEditText: TextInputEditText
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_add_note_activty)
setSupportActionBar(toolbar)
addViewModel = ViewModelProviders.of(this).get(NoteViewModel::class.java)
titleEditText = findViewById(R.id.itemTitle)
contentEditText = findViewById(R.id.itemNote)
val extra = intent.extras
if (extra != null) {
val uuid = extra.getLong("UUID")
val note: Notes? = addViewModel.getNote(uuid)
titleEditText.setText(note!!.title)
contentEditText.setText(note.note)
}
}
}
NoteViewModel class
class NoteViewModel(application: Application) : AndroidViewModel(application) {
companion object {
private var note: Notes = Notes(0, "", "test title", "test ontent")
}
fun getNote(uuid: Long?): Notes {
val job = async(CommonPool) {
getNoteAsyncTask(notesDatabase).execute(uuid)
}
runBlocking { job.await() }
return note
}
class getNoteAsyncTask(database: NotesDatabase) : AsyncTask<Long, Unit, Unit>() {
private val db: NotesDatabase = database
override fun doInBackground(vararg params: Long?) {
note = db.notesDataDao().getNote(params[0])
}
}
}
If I pass an intent to get a Note object from the database with a uuid and set that received data in titleEditText and contentEditText, the data set in the Note was from previous intent invoked when we clicked on the Note item in RecyclerView. On clicking the Note item for the first time, I get the default value which I have set "test title" and "test content".
Aforementioned is the behavior most of the time. Sometimes the data set in titleEditText and contentEditText is of the correct Note object.
Can someone please tell me what I have done wrong? How can I correct my apps behavior?
Unfortunately, there is a big mistake in how you use a view model to provide a data to your view(AddNoteActivity).
Basically, your view never has a chance to wait for the data to be fetched as it always receives a default value. This happens because the AsyncTask runs on its own thread pool so the coroutine completes immediately and returns a default value.
You should consider using LiveData to post a new object to your view and refactor your view model.
So, you need to make a query to the database synchronous and observe changes to a note rather than have a getter for it. Of course, in a real life scenario it might be a good idea to have different kind of states to be able to show a spinner while a user is waiting. But this is another big question. So to keep things simple consider changing your view model to something like that:
class NoteViewModel(private val database: NotesDatabase) : ViewModel { // you do not need an application class here
private val _notes = MutableLiveData<Notes>()
val notes: LiveData<Notes> = _notes
fun loadNotes(uuid: Long) {
launch(CommonPool) {
val notes = database.notesDataDao().getNote(uuid)
_notes.setValue(notes)
}
}
}
Then, you can observe changes to the note field in your activity.
class AddNoteActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
// ...
val noteViewModel = ViewModelProviders.of(this).get(NoteViewModel::class.java)
noteViewModel.notes.observe(this, Observer {
title.text = it.title
content.text = it.note
})
}
}
Also you need to use a ViewModelProvider.Factory to create your view model and properly inject dependencies into it. Try to avoid having a context there as it makes it much harder to test.