Top-level function for onClick in Android - android

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.

Related

update specific item in viewPager2 at same time without affecting other items

STORY:-
I'm making app like Instagram Reels, with viewPager2 with RecyclerViewAdapter, all functionality working fine,
only one problem I'm facing right now is when I click on comment button the Comment screen is opening in another activity and when I getting back on reels activity after commenting or without commenting,
video playing again and mixing with current audio that means notifyItemChanged is not working properly in my case,
I just want to update that comment count only without affecting whole adapter items.
Class- AdapterClass : RecyclerView.Adapter
onClickListener for Comment button
binding.ivComment.setOnClickListener(View.OnClickListener {
if (Utility.isOnline(context)) {
itemClick(post)
} else {
val toast: Toast = Toast.makeText(
context,
R.string.msg_no_internet,
Toast.LENGTH_SHORT
)
toast.show()
}
})
fun itemClick(postList: PostList?) {
if (Utility.isOnline(context)) {
val intent = Intent(context, BitesCommentActivity::class.java)
intent.putExtra(PostList.SENDEXTRA, postList)
context.startActivity(intent)
} else {
val toast: Toast = Toast.makeText(context, R.string.msg_no_internet, Toast.LENGTH_SHORT)
toast.show()
}
}
Class- Activity Class
Using BroadcastReceiver for update comment count from CommentActivity
private val mMessageReceiver: BroadcastReceiver = object : BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent) {
val isActivityInForeground = this#BitesSwappableVideosActivity.lifecycle.currentState.isAtLeast(Lifecycle.State.RESUMED)
if (!isActivityInForeground) {
val message = intent.getStringExtra("message")
val epost_id = intent.extras!!
.getLong(Constant.ScreenExtras.EPOST_ID, -1)
val action = intent.extras!!.getInt(Constant.ScreenExtras.ACTION)
for (i in fav.indices) {
val postList: PostList = fav[i]
val updateIndex: Int
if (action == Constant.COMMENT) {
if (postList.postId == epost_id) {
postList.totalComment = postList.totalComment + 1
eBiteReels_adapter.notifyItemChanged(i)
break
}
}
}
} else {
val message = intent.getStringExtra("message")
val epost_id = intent.extras!!
.getLong(Constant.ScreenExtras.EPOST_ID, -1)
val action = intent.extras!!.getInt(Constant.ScreenExtras.ACTION)
for (i in fav.indices) {
val postList: PostList = fav[i]
if (action == Constant.COMMENT) {
if (postList.postId == epost_id) {
postList.totalComment = postList.totalComment + 1
eBiteReels_adapter.notifyItemChanged(i)
break
}
}
}
}
}
}
I've tried
eBiteReels_adapter.notifyItemChanged(i)
but it cause problem with another items especially with videoplayer.
if I remove notifyItemChanged from my code it refreshes comment count when I restart my SwipableVideoActivity

How to resolve the error "LifecycleOwners must call register before they are STARTED"

I am using registerForActivityResult for google sign in implementation in my development. Everything was working fine until I upgraded my fragment dependency to 1.3.0-beta01. The application current crash with the error
java.lang.IllegalStateException: LifecycleOwner SignupChoicesFragment{8e0e269} (193105b9-afe2-4941-a368-266dbc433258) id=0x7f090139} is attempting to register while current state is RESUMED. LifecycleOwners must call register before they are STARTED.
I have used the function before oncreate using lazy loading but it wont work still.
class SignupChoicesFragment : DaggerFragment() {
#Inject
lateinit var viewModelProviderFactory: ViewModelFactory
val userViewModel: UserViewModel by lazy {
ViewModelProvider(this, viewModelProviderFactory).get(UserViewModel::class.java)
}
#Inject
lateinit var mGoogleSignInClient:GoogleSignInClient
val arg:SignupChoicesFragmentArgs by navArgs()
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
// Inflate the layout for this fragment
return inflater.inflate(R.layout.fragment_signup_choices, container, false)
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
google_sign_in_button.setOnClickListener {
val intent = mGoogleSignInClient.signInIntent
val launcher = registerForActivityResult(ActivityResultContracts.StartActivityForResult(), ActivityResultCallback {result->
if (result.resultCode == Activity.RESULT_OK) {
val task = GoogleSignIn.getSignedInAccountFromIntent(result.data)
task.addOnCompleteListener {
if (it.isSuccessful) {
val account: GoogleSignInAccount? =
it.getResult(ApiException::class.java)
val idToken = it.result?.idToken
val email = account?.email
val lastName = account?.familyName
val firstName = account?.givenName
val otherName = account?.displayName
val imageUrl = account?.photoUrl
val category = arg.category
val newUser = User()
newUser.firstName = firstName
newUser.lastName = lastName
newUser.otherName = otherName
newUser.category = category
newUser.email = email
newUser.imageUrl = imageUrl.toString()
userViewModel.currentUser = newUser
newUser.token = idToken
i(title, "idToken $idToken")
requireActivity().gdToast("Authentication successful", Gravity.BOTTOM)
val action = SignupChoicesFragmentDirections.actionSignupChoicesFragmentToEmailSignupFragment()
action.newUser = newUser
goto(action)
} else {
requireActivity().gdToast(
"Authentication Unsuccessful",
Gravity.BOTTOM
)
Log.i(title, "Task not successful")
}
}
} else {
Log.i(title, "OKCODE ${Activity.RESULT_OK} RESULTCODE ${result.resultCode}")
}
}).launch(intent)
}
}
For me, the issue was that I was calling registerForActivityResult within an onClickListener which was only invoked on clicking a button (the app at this point is in state RESUMED). Moving the call outside the button's onClickListener and into the Activity's onCreate method fixed it.
quote from documentation
registerForActivityResult() is safe to call before your fragment or activity is created, allowing it to be used directly when declaring member variables for the returned ActivityResultLauncher instances.
Note: While it is safe to call registerForActivityResult() before your fragment or activity is created, you cannot launch the ActivityResultLauncher until the fragment or activity's Lifecycle has reached CREATED.
so to solve your issue move your register call outside the onCreate() and put it in fragment scope, and on google_sign_in_button click-listener call launch function
Note: if you are using Kotlin-Android-Extention move your click-listener call to onViewCreated()
If you are using a Fragment, please make sure that you are NOT performing the registerForActivityResult on the activity. Fragments also have a registerForActivityResult and that's the one you should use.
you must remove val launcher = registerForActivityResult... out of the setOnClickListener, then save it in a variable, in your example is launcher and in the setOnClickListener execute the variable with .launch, in your example es launcher.
your code would look like this
google_sign_in_button.setOnClickListener {
val intent = mGoogleSignInClient.signInIntent
launcher.launch(intent)
}
private val launcher = registerForActivityResult(ActivityResultContracts.StartActivityForResult(), ActivityResultCallback {result->
if (result.resultCode == Activity.RESULT_OK) {
val task = GoogleSignIn.getSignedInAccountFromIntent(result.data)
task.addOnCompleteListener {
if (it.isSuccessful) {
val account: GoogleSignInAccount? =
it.getResult(ApiException::class.java)
val idToken = it.result?.idToken
val email = account?.email
val lastName = account?.familyName
val firstName = account?.givenName
val otherName = account?.displayName
val imageUrl = account?.photoUrl
val category = arg.category
val newUser = User()
newUser.firstName = firstName
newUser.lastName = lastName
newUser.otherName = otherName
newUser.category = category
newUser.email = email
newUser.imageUrl = imageUrl.toString()
userViewModel.currentUser = newUser
newUser.token = idToken
i(title, "idToken $idToken")
requireActivity().gdToast("Authentication successful", Gravity.BOTTOM)
val action = SignupChoicesFragmentDirections.actionSignupChoicesFragmentToEmailSignupFragment()
action.newUser = newUser
goto(action)
} else {
requireActivity().gdToast(
"Authentication Unsuccessful",
Gravity.BOTTOM
)
Log.i(title, "Task not successful")
}
}
} else {
Log.i(title, "OKCODE ${Activity.RESULT_OK} RESULTCODE ${result.resultCode}")
}
})
Source : https://medium.com/codex/android-runtime-permissions-using-registerforactivityresult-68c4eb3c0b61
registerForActivityResult() is safe to call before your fragment or activity is created, allowing it to be used directly when declaring member variables for the returned ActivityResultLauncher instances.
you should call registerForActivityResult before view created. member variables or onCreate()
If you are working with any third party library then it may happens that you can't see the "registerForActivityResult" in your code but it should be present in classes provided by that same library.
So in this case I will suggest to move out the lines which is related to that library from any listener to the onCreate method.
for example -
btnBackup.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
final RoomBackup roomBackup = new RoomBackup(GoogleDriveActivity.this);
roomBackup.database(LocalDataBase.getInstance(getApplicationContext()));
roomBackup.enableLogDebug(true);
roomBackup.backupIsEncrypted(false);
roomBackup.backupLocation(RoomBackup.BACKUP_FILE_LOCATION_INTERNAL);
roomBackup.onCompleteListener((success, message, exitCode) -> {
Log.d(TAG, "success: " + success + ", message: " + message + ", exitCode: " + exitCode);
if (success) roomBackup.restartApp(new Intent(getApplicationContext(), GoogleDriveActivity.class));
});
roomBackup.restore();
}
});
//// remove other code from listener and shift in onCreate
roomBackup = new RoomBackup(GoogleDriveActivity.this);
roomBackup.database(LocalDataBase.getInstance(getApplicationContext()));
roomBackup.enableLogDebug(true);
roomBackup.backupIsEncrypted(false);
roomBackup.backupLocation(RoomBackup.BACKUP_FILE_LOCATION_INTERNAL);
roomBackup.maxFileCount(5);
roomBackup.onCompleteListener((success, message, exitCode) -> {
Log.d(TAG, "success: " + success + ", message: " + message + ", exitCode: " + exitCode);
if (success) roomBackup.restartApp(new Intent(getApplicationContext(), GoogleDriveActivity.class));
});
/// you can keep only required lines in listener
btnBackup.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
roomBackup.backup();
}
});
That's it!
Found the same issue and manage to get to work with some magic.
In my case, it was happening in an Activity, so I went about it as such:
//...other bits
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(binding.root)
// doing the setup here
setupViews()
}
private fun setupViews() {
val buttonLauncher = navigator.gotoScreenForResult(this) { success ->
if (success) {
setResult(Activity.RESULT_OK)
finish()
}
}
binding.myButton.setOnClickListener {
buttonLauncher.launch(Unit)
}
Where the navigator.gotoScreenForResult would look like the following:
override fun gotoScreenForResult(context: AppCompatActivity, callback: (Boolean) -> Unit): ActivityResultLauncher<Unit> {
val contract = object : ActivityResultContract<Unit, Boolean>() {
override fun createIntent(context: Context, input: Unit?): Intent {
return Intent(context, MyNextActivity::class.java)
}
override fun parseResult(resultCode: Int, intent: Intent?): Boolean {
return resultCode == Activity.RESULT_OK
}
}
return context.registerForActivityResult(contract) { callback(it) }
}
Just make sure the setupViews is done within the onCreate and not on the resume step.

Waiting for some function complete in android

in my android app(media player), i'm scanning all media files in loadingActivity.kt file. and then starting mainActivity.kt. my code is
scanSongs()
intent = Intent(this, MainActivity::class.java)
startActivity(intent)
and my scanSongs() function is
private fun scanSongs() {
val songsJob = async {
val songsFinder = SongsLoader(contentResolver)
songsFinder.load()
songsFinder.getAllSongs()
}
launch {
allSongsList = songsJob.await()
allSongsList?.sortBy { it.getTitle() }
}
}
but before scan complete it is opening mainActivity . and showing empty list . how can i waiting for complete scanSong() , before open mainActivity
Open the MainActivity when you have the allSongsList:
private fun scanSongs() {
val songsJob = async {
val songsFinder = SongsLoader(contentResolver)
songsFinder.load()
songsFinder.getAllSongs()
}
launch {
allSongsList = songsJob.await()
allSongsList?.sortBy { it.getTitle() }
intent = Intent(this, MainActivity::class.java)
startActivity(intent)
}
}
You have to check a condition before start mainactivity.kt that
song list is null or not

null reference when making an intent switching to another activity

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.

Check if EditText is empty kotlin android

How do you check if an EditText is empty? input type number
package com.example.www.myapplication
import android.support.v7.app.AppCompatActivity
import android.os.Bundle
import kotlinx.android.synthetic.main.activity_main.*
import java.util.*
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
button.setOnClickListener {
val inter:Int=editText.text.toString().toInt()
val year: Int = Calendar.getInstance().get(Calendar.YEAR)
val res:Int=year-inter
textView.text=res.toString()
}
}
Harness Kotlin power by using inline extension functions:
editText.text.isNotEmpty().apply {
//do something
}
or use let
Here is the full example with explanation.
//init the edittext
val etMessage = findViewById(R.id.et_message) as EditText
//init the button
val btnClick = findViewById(R.id.btn_click) as Button
btnClick.setOnClickListener{
//read value from EditText to a String variable
val msg: String = etMessage.text.toString()
//check if the EditText have values or not
if(msg.trim().length>0) {
Toast.makeText(applicationContext, "Message : "+msg, Toast.LENGTH_SHORT).show()
}else{
Toast.makeText(applicationContext, "Please enter some message! ", Toast.LENGTH_SHORT).show()
}
}
You can be done by below way
if (mEdtDeviceName.text.toString().trim().isNotEmpty() ||
mEdtDeviceName.text.toString().trim().isNotBlank()) {
// your code
} else {
Toast.makeText(activity, "Error Msg", Toast.LENGTH_SHORT).show()
}
Hey I am using like this in kotlin
val input = editText?.text.toString().trim()
if (input.isNullOrBlank()) {
//Your code for blank edittext
}
Hope this will help you..let me know if any issue....
try this out:
bottom.setOnClickListener{
val new = addText.text.toString()
if (new = isNotEmpty()) {
//do something
} else {
Toast.makeText(context, "Enter some message ", Toast.LENGTH_SHORT).show()
}
}
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val btnSignUp : Button = findViewById(R.id.signUp)
val et_username : EditText = findViewById(R.id.etUsername)
val et_email : EditText = findViewById(R.id.etEmail)
val et_password : EditText = findViewById(R.id.etPassword)
btnSignUp.setOnClickListener{
val user_msg_error: String = et_username.text.toString()
//check if the EditText have values or not
if(user_msg_error.trim().isEmpty()) {
et_username.error = "Required"
Toast.makeText(applicationContext, "User Name Required ", Toast.LENGTH_SHORT).show()
}
else if (et_email.text.toString().trim().isEmpty()) {
et_email.error = "Required"
Toast.makeText(applicationContext, "Email Required ", Toast.LENGTH_SHORT).show()
}
else if (et_password.text.toString().trim().isEmpty()) {
et_password.error = "Required"
Toast.makeText(applicationContext, "Password Required ", Toast.LENGTH_SHORT).show()
}
else{
Toast.makeText(applicationContext, "Login Successful ", Toast.LENGTH_SHORT).show()
// After successful login u will move on next page/ activity
val i = Intent(this,SecondActivity::class.java)
startActivity(i)
}
}
}
}
Try this:
if(TextUtils.isEmpty(editText.getText().toString())){
//Do
}
Been a new guy Tried lots and this Worked for me
if(!editTextTerminalName.text.toString().trim().isNotEmpty()) {
editTextTerminalName?.error = "Required"
}else if(!editTextPassword.text.toString().trim().isNotEmpty()){
editTextPassword?.error = "Required"
}else{
avi.visibility= View.VISIBLE // v letter should be capita
}
if (regemail.isEmpty())
{
Toast.makeText(this,"Enter Email..!!!",Toast.LENGTH_LONG).show()
}
Same solution but using class TextUtil and .isEmpty(charsequence:)
btnGo.setOnClickListener{
val input1 = etName.text.toString.trim() // 1
if(TextUtils.isEmpty(input1)){ // 2
etName.error = "Enter a name" // 3
return#setOnClickListener //4
}
//code to store a Bundle or insert in a sqlitedb etc
// go to secondactiviy
}
user only typed spacebars??? .trim() helps with that
TextUtil is a class .isEmpty one of its methods
displays a clickable red (!) in the EditText and when it is pressed displays "Enter a name" of course you can use getString(R.string.somename)
restarts onclicklistener / "restricts" some actions like change to other acivity, avoinding (for example) passing null a bundle() or insert a null in a db
Just do this, i was facing the same issue. :)
button.setOnClickListener {
val checkUsername = userName.text.toString()
if (checkUsername.isNullOrBlank()) {
Toast.makeText(context, "Please enter your name", Toast.LENGTH_SHORT).show()
} else {
val action = UserLoginFragmentDirections.actionUserLoginFragmentToBmiFragment()
findNavController().navigate(action)
}
}

Categories

Resources