I am building a chatting application in android using Kotlin. I want to long press on a message to copy it on the clipboard. This function is working fine but I am unable to show a toast due to some error. Here is my code.
class ChatFromItem(val text: String, val user: User): Item<ViewHolder>() {
override fun bind(viewHolder: ViewHolder, position: Int) {
viewHolder.itemView.textView_from_row.text = text
viewHolder.itemView.textView_from_row.setOnLongClickListener {
Toast.makeText(this, "Message copied", Toast.LENGTH_LONG).show()
val clipboard = it.context.getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager
val myClip = ClipData.newPlainText("label", text)
clipboard.primaryClip = myClip
true
}
val uri = user.profileImageUrl
val target = viewHolder.itemView.imageView_chat_from_row
Picasso.get().load(uri).into(target)
}
override fun getLayout(): Int {
return R.layout.chat_from_row
}
}
Some people might get confused as of why there is a 'true' written at the very bottom of .setOnLongClickListener. Without the toast statement the rest of the code is working fine. I have tested it many times.
I am including an image containing the error message that android studio is showing me.
Thank you in advance.
When you are using setOnLongClickListener { /* your code here */ } in kotlin it is the equivlent to:
setOnLongClickListener(new OnLongClickListener() {public boolean onLongClick(View v){ /* your code here */ }}); in java
Therefore when you do this:
viewHolder.itemView.textView_from_row.setOnLongClickListener {
Toast.makeText(this, "Message copied", Toast.LENGTH_LONG).show()
....
}
It's the equivalent to this:
setOnLongClickListener(new OnLongClickListener() {
#Override
public boolean onLongClick(View v) {
Toast.makeText(this, "Message copied", Toast.LENGTH_LONG).show()
}
});
As you can see the this in the code above is not of the type Context it's of the type OnLongClickListener!
You can find a context using the view that you have:
Toast.makeText(viewHolder.itemView.context, "Message copied", Toast.LENGTH_LONG).show()
You need to pass context or get it from a view, like itemView.context.
Related
I have been facing some issues while converting my android JAVA code into KOTLIN. I want to show a Material Dialog on some condition and want control back to activity as soon as the user clicks on dialog's button.
My java code:
public static void showAlertPopup(Context context, String title, String message) {
try {
MaterialAlertDialogBuilder builder = new MaterialAlertDialogBuilder(context);
builder.setTitle(title != null ? title : context.getString(R.string.alertTitle));
builder.setMessage(message);
builder.setCancelable(false);
builder.setIcon(ContextCompat.getDrawable(context, R.drawable.ic_alert));
builder.setPositiveButton(context.getString(R.string.txtNeutralBtn),
(dialogInterface, i) -> dialogInterface.dismiss());
builder.setBackground(ContextCompat.getDrawable(context, R.drawable.dialog_background));
builder.show();
} catch (Exception e) {
DLiteLogger.WriteLog(MaterialDialogUtility.class, AppSettings.LogLevel.Error, e.getMessage());
}
}
My conversion to Kotlin:
fun showErrorPopup(
context: Context,
message: String?,
callback: OnPositiveButtonClickListener?
) {
MaterialAlertDialogBuilder(context).also {
it.setTitle(context.getString(R.string.errorTitle))
it.setMessage(message)
it.setCancelable(false)
it.setIcon(ContextCompat.getDrawable(context, R.drawable.ic_error))
it.setPositiveButton(context.getString(R.string.txtNeutralBtn))
{ dialogInterface: DialogInterface, _: Int ->
if (callback != null) {
dialogInterface.dismiss()
callback.onPositiveBtnClick(dialogInterface)
} else dialogInterface.dismiss()
}
it.background = ContextCompat.getDrawable(context, R.drawable.dialog_background)
it.show()
}
}
This is the interface I have created in same class:
interface OnPositiveButtonClickListener {
fun onPositiveBtnClick(dialog: DialogInterface?)
}
The issue I am facing currently for below code:
MaterialDialogUtility.showErrorPopup(this#LoginActivity,
getString(R.string.alertIncorrectPassword),
{ dialog ->
Objects.requireNonNull(binding.passwordEt.getText()).clear()
})
is
Type mismatch: inferred type is ([ERROR : ]) -> [ERROR : Cannot infer type variable TypeVariable(_L)] but MaterialDialogUtility.OnPositiveButtonClickListener? was expected
In order to use your interface as a lambda you'll have to convert it to a SAM interface a.k.a functional interface. So something like this:
fun interface OnPositiveButtonClickListener {
fun onPositiveBtnClick(dialog: DialogInterface?)
}
More description about functional interfaces here.
If you don't want to use SAM you can still use your current interface like this:
showErrorPopup(
this,
"Some String",
callback = object: OnPositiveButtonClickListener{
override fun onPositiveBtnClick(dialog: DialogInterface?) {
TODO("Not yet implemented")
}
}
)
I don't know how to make my failure toast message to show only once.
Toast.makeText(this, vm.logInResult.value, Toast.LENGTH_SHORT).show()
private fun addData(edtTxt: String, pasTxt: String) {
val repository = UserRepository()
val viewModelFactory = UserViewModelFactory(repository)
viewModel = ViewModelProvider(this, viewModelFactory).get(UserViewModel::class.java)
viewModel.pushUser(edtTxt, pasTxt)
viewModel.userPush.observe(this, Observer { response ->
if (response.isSuccessful) {
dismissLogoProgressDialog()
Log.d("MainResponse", response.body().toString())
Log.d("MainExecute", response.code().toString())
Log.d("Main", response.message())
val check = response.body()
Log.d("checkdata", "" + check?.userinfo?.email)
val tokn: String = check!!.token
if (sharedPreference.getValueString("token") != null) {
sharedPreference.clearSharedPreference()
}
sharedPreference.save("token", tokn)
sharedPreference.save("login_uid", check.userinfo.uid)
sharedPreference.save("change_pass", pasTxt)
println(check)
startActivity(Intent(this, DashboardActivity::class.java))
finish()
} else {
dismissLogoProgressDialog()
Toast.makeText(this, "Password mismatch", Toast.LENGTH_SHORT).show()
}
})
}
Are you sure you only call this Toast once? Or is this Toast created in a loop? In that case; you need to breakout of the loop first.
The function may have been placed within a loop and the else clause is may always be taken.
Are the Log functions printing anything to the console?
Is there anyway you could edit the question and show us where this function is called?
Writing an Android app with Kotlin using Android Studio. I have several activities and each of them has similar buttons. I added global variable ACTID which references each Activity I have through the map.
Every button has android: onClick="onClick" in its XML file.
So, I tried to make a public function:
public fun allClick(view: View){
val context = ACTIVITY_DICT[ACTID]
val toast = Toast.makeText(context, ACTID.toString(), Toast.LENGTH_LONG)
toast.show()
when (view.id)
{
R.id.nextBtn -> {
val intentNext = Intent(context, ACTIVITY_DICT[ACTID+1]!!::class.java)
context?.startActivity(intentNext)
context?.finish()}
R.id.backBtn -> {
val intentBack = Intent(context, ACTIVITY_DICT[ACTID-1]!!::class.java)
context?.startActivity(intentBack)
context?.finish()}
}
}
However, I cannot set allCLick for onClick. How can I fix it? Would be grateful for any possible help.
You can make a base activity BaseActivity, implement allClick(view: View) method in it and inherit from it other activities:
class BaseActivity : AppCompatActivity() {
public fun allClick(view: View) {
val context = ACTIVITY_DICT[ACTID]
val toast = Toast.makeText(context, ACTID.toString(), Toast.LENGTH_LONG)
toast.show()
when (view.id) {
R.id.nextBtn -> {
val intentNext = Intent(context, ACTIVITY_DICT[ACTID+1]!!::class.java)
context?.startActivity(intentNext)
context?.finish()
}
R.id.backBtn -> {
val intentBack = Intent(context, ACTIVITY_DICT[ACTID-1]!!::class.java)
context?.startActivity(intentBack)
context?.finish()
}
}
}
}
Also add android: onClick="allClick" for every button in its XML file.
I have a project using RecognitionListener written in Kotlin. The speech-to-text function was always a success and never presented any problems.
Since last week, it's onResult function started to be called twice. No changes were made on the project. I tested old versions of the project (from months ago) and those had the same problem.
There are three different cases:
Small text (1 to 8 words) and SpeechRecognizer being stopped automatically -> onResult() called twice;
Big text (9 words or more) and SpeechRecognizer being stopped automatically -> Normal behavior (onResult() called once);
Any text size and SpeechRecognizer stopListening() function called manually (from code) -> Normal behavior.
Here is the VoiceRecognition speech-to-text class code:
class VoiceRecognition(private val activity: Activity, language: String = "pt_BR") : RecognitionListener {
private val AudioLogTag = "AudioInput"
var voiceRecognitionIntentHandler: VoiceRecognitionIntentHandler? = null
var voiceRecognitionOnResultListener: VoiceRecognitionOnResultListener? = null //Must have this
var voiceRecognitionLayoutChanger: VoiceRecognitionLayoutChanger? = null
var isListening = false
private val intent: Intent
private var speech: SpeechRecognizer = SpeechRecognizer.createSpeechRecognizer(activity)
init {
speech.setRecognitionListener(this)
intent = Intent(RecognizerIntent.ACTION_RECOGNIZE_SPEECH)
intent.putExtra(
RecognizerIntent.EXTRA_LANGUAGE_MODEL,
RecognizerIntent.LANGUAGE_MODEL_FREE_FORM
)
intent.putExtra(RecognizerIntent.EXTRA_LANGUAGE, language)
}
//It is important to put this function inside a clickListener
fun listen(): Boolean {
if (ContextCompat.checkSelfPermission(activity, Manifest.permission.RECORD_AUDIO) != PackageManager.PERMISSION_GRANTED) {
ActivityCompat.requestPermissions(activity, arrayOf(Manifest.permission.RECORD_AUDIO), 1)
return false
}
speech.startListening(intent)
Log.i(AudioLogTag, "startListening")
return true
}
//Use this if you want to stop listening but still get recognition results
fun endListening(){
Log.i(AudioLogTag, "stopListening")
speech.stopListening()
isListening = false
}
fun cancelListening(){
Log.i(AudioLogTag, "cancelListening")
speech.cancel()
voiceRecognitionLayoutChanger?.endListeningChangeLayout()
isListening = false
}
override fun onReadyForSpeech(p0: Bundle?) {
Log.i(AudioLogTag, "onReadyForSpeech")
voiceRecognitionLayoutChanger?.startListeningChangeLayout()
isListening = true
}
override fun onRmsChanged(p0: Float) {
// Log.i(AudioLogTag, "onRmsChanged: $p0")
// progressBar.setProgress((Int) p0)
}
override fun onBufferReceived(p0: ByteArray?) {
Log.i(AudioLogTag, "onBufferReceived: $p0")
}
override fun onPartialResults(p0: Bundle?) {
Log.i(AudioLogTag, "onPartialResults")
}
override fun onEvent(p0: Int, p1: Bundle?) {
Log.i(AudioLogTag, "onEvent")
}
override fun onBeginningOfSpeech() {
Log.i(AudioLogTag, "onBeginningOfSpeech")
}
override fun onEndOfSpeech() {
Log.i(AudioLogTag, "onEndOfSpeech")
voiceRecognitionLayoutChanger?.endListeningChangeLayout()
isListening = false
}
override fun onError(p0: Int) {
speech.cancel()
val errorMessage = getErrorText(p0)
Log.d(AudioLogTag, "FAILED: $errorMessage")
voiceRecognitionLayoutChanger?.endListeningChangeLayout()
isListening = false
}
override fun onResults(p0: Bundle?) {
val results: ArrayList<String> = p0?.getStringArrayList(SpeechRecognizer.RESULTS_RECOGNITION) as ArrayList<String>
Log.i(AudioLogTag, "onResults -> ${results.size}")
val voiceIntent: Int? = voiceRecognitionIntentHandler?.getIntent(results[0])
if (voiceIntent != null && voiceIntent != 0) {
voiceRecognitionIntentHandler?.handle(voiceIntent)
return
}
voiceRecognitionOnResultListener!!.onResult(results[0])
}
private fun getErrorText(errorCode: Int): String {
val message: String
when (errorCode) {
SpeechRecognizer.ERROR_AUDIO -> message = "Audio recording error"
SpeechRecognizer.ERROR_CLIENT -> message = "Client side error"
SpeechRecognizer.ERROR_INSUFFICIENT_PERMISSIONS -> message = "Insufficient permissions"
SpeechRecognizer.ERROR_NETWORK -> message = "Network error"
SpeechRecognizer.ERROR_NETWORK_TIMEOUT -> message = "Network timeout"
SpeechRecognizer.ERROR_NO_MATCH -> message = "No match"
SpeechRecognizer.ERROR_RECOGNIZER_BUSY -> message = "RecognitionService busy"
SpeechRecognizer.ERROR_SERVER -> message = "Error from server"
SpeechRecognizer.ERROR_SPEECH_TIMEOUT -> message = "No speech input"
else -> message = "Didn't understand, please try again."
}
return message
}
//Use it in your overriden onPause function.
fun onPause() {
voiceRecognitionLayoutChanger?.endListeningChangeLayout()
isListening = false
speech.cancel()
Log.i(AudioLogTag, "pause")
}
//Use it in your overriden onDestroy function.
fun onDestroy() {
speech.destroy()
}
listen(), endListening() and cancelListening() are all called from a button.
I found this open issue: https://issuetracker.google.com/issues/152628934
As I commented, I assume it is an issue with the "speech recognition service" and not with the Android RecognitionListener class.
this is my temporary workaround
singleResult=true;
#Override
public void onResults(Bundle results) {
Log.d(TAG, "onResults"); //$NON-NLS-1$
if (singleResult) {
ArrayList<String> matches = results.getStringArrayList(SpeechRecognizer.RESULTS_RECOGNITION);
if (matches != null && matches.size() > 0) {
Log.d("single Result", "" + matches.get(0));
}
singleResult=false;
}
getHandler().postDelayed(new Runnable() {
#Override
public void run() {
singleResult=true;
}
},100);
}
I had the same problem and I've just added a boolean flag in my code, but ofcourse it's a temporary solution and I don't know the source of this problem.
val recognizer = SpeechRecognizer.createSpeechRecognizer(context)
recognizer.setRecognitionListener(
object : RecognitionListener {
var singleResult = true
override fun onResults(results: Bundle?) {
if (singleResult) {
results?.getStringArrayList(SpeechRecognizer.RESULTS_RECOGNITION).let {
// do something with result
}
// next result will be ignored
singleResult = false
}
}
}
This just started happening in one of my apps yesterday. I added a boolean to allow the code to execute only once, but I'd love an explanation as to why it suddenly started doing this. Any updates?
I use the the following code based on time differences, which should continue to work if Google ever gets around to fixing this bug.
long mStartTime = System.currentTimeMillis(); // Global Var
#Override
public void onResults(Bundle results)
{
long difference = System.currentTimeMillis() - mStartTime;
if (difference < 100)
{
return;
}
mStartTime = System.currentTimeMillis();
ArrayList<String> textMatchList =
results.getStringArrayList(SpeechRecognizer.RESULTS_RECOGNITION);
Event_Handler(VOICE_DATA, textMatchList.get(0));
// process event
}
ya even I faced the same issue with my app but I fixed it with a custom logic that is using a flag means a variable let it be a temp variable and by default set it as a false.
what you need to do set the temp as true wherever you are starting listening voice.
Then in your handler what you need to do is just add a if condition on the basis of temp variable like
if (temp) {
do something
temp = false
}
so what will happen you handler will be called twice as usual but you will be able to handle this data only.
I am modifying the Android TV sample from Android Studio. Originally clicking on a "TV show", it plays the stored URL on the PlaybackVideoFragment in PlaybackActivity. Now I am modifying to let user choose between using the internal mediaPlayer, or calling an intent to show other system video players. After moving the intent call from "OnItemViewClickedListener" to other function call, it gives some kinds of "null reference". I think it is the "context" problem, but trying "this", "activity", "MainActivity.this", the stored "ctx", etc won't work.
MainActivity.kt
class MainActivity : Activity() {
//...
fun prepareVideo(item: Movie){
if(item.videoUrl == ""){
getVideoUrl(item)
}else{
MainFragment().play(item)
}
}
}
MainFragment.kt
class MainFragment : BrowseFragment() {
private inner class ItemViewClickedListener : OnItemViewClickedListener {
override fun onItemClicked(
itemViewHolder: Presenter.ViewHolder,
item: Any,
rowViewHolder: RowPresenter.ViewHolder,
row: Row
) {
if (item is Movie) {
MainActivity().prepareVideo(item)
} else if (item is String) {
Toast.makeText(activity, item, Toast.LENGTH_SHORT).show()
}
}
}
fun play(item: Movie, extra_url: String = ""){
//should be more lines to do some more things. simplified.
val intent = Intent(activity, PlaybackActivity::class.java)
intent.putExtra("item", item)
startActivity(intent)
}
}
May I know how to fix this, so I can switch to "PlaybackActivity"?
Thanks.