How to call composable Alertdialog from non composable function - android

I wanted to check whether the phone is rooted or not. For that I wanted to check as soon as the app is clicked.
#HiltAndroidApp class ShoppingApp : Application(), Configuration.Provider {
override fun onCreate() {
super.onCreate() }
This is the first class which is called as soon as the app is launched. Inside this onCreate, I wanted to add check if device is rooted. If its rooted I want to show AlertDialog else navigate to next screen.
When adding composable AlertDialog, its throwing error #Composable should be called from the context of Composable.
In this case, how should I code.

You will need to create a State object in your Application class and make your composable observe that object from an Activity or Fragment.
class ShoppingApp : Application() {
val isRootedState = mutableStateOf(false)
override fun onCreate() {
super.onCreate()
if (isRootedDevice()) {
isRootedState.value = true
}
}
}
class ShoppingActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
val isRooted = remember { (application as ShoppingApp).isRootedState }
if (isRooted.value) {
AlertDialog(
// AlertDialog params
)
}
}
}
//...
}

Related

LiveData not giving callback on lifecycleOwner state change to active state

LiveData used to give callback when lifecycleOwner changed from inactive to active state, therefore we have SingleLiveEvent or Event wrapper as described in this article.
But I am not getting callback on state change if callback was given once, I have created a sample project for same
MainActivity
class MainActivity : AppCompatActivity() {
private val viewModel: MainViewModel by lazy {
ViewModelProvider(this)[MainViewModel::class.java]
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
findViewById<View>(R.id.tv).setOnClickListener {
startActivity(Intent(this, MainActivity2::class.java))
}
viewModel.liveData.observe(this) {
Toast.makeText(this, it, Toast.LENGTH_LONG).show()
}
}
}
ViewModel
class MainViewModel: ViewModel() {
val liveData = MutableLiveData<String>("Random value")
}
In this code, toast is shown when the app launches and it is not shown again if MainActivity2 is started or the app goes to background and then comes back to foreground.

How to add a common button in all activity?

Can we add a common button on all activity without add code in xml file.
we need to add some code in application class and button show in all activity of app.
First create a BaseActivity. Then add this code to onCreate of that:
class BaseActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_nav_host)
//the layout of baseActivity
LinearLayout layout =(LinearLayout) findViewById (R.id.base_activity_layout);
//set the properties for button
Button btnTag = new Button(this);
btnTag.setLayoutParams(
new LayoutParams (LayoutParams.WRAP_CONTENT,
LayoutParams.WRAP_CONTENT
));
btnTag.setText("Button");
btnTag.setId(some_random_id);
//add button to the layout
layout.addView(btnTag);
}
}
Then let other activities which you want to create being inherited from BaseActivity.
class FirstActivity : BaseActivity() {
//
//
}
class SecondActivity : BaseActivity() {
//
//
}
class ThirdActivity : BaseActivity() {
//
//
}
class ButtonActivity: Activity() {
protected var btnCommon: Button
override fun onCreate(saved: Bundle?) {
var layout = findViewById(R.id.layout) as ConstraintLayout
btnCommon = Button(this)
// set Button properties
layout.addView(button)
}
}
How to use
class MyActivity: ButtonActivity() {
onCreate() {
this.btnCommon.setOnClickListener...... // now you can use.
}
}
I saw in your comments that you specifically want to do this solely from your Application class. It is possible to add a callback that is called during lifecycle methods of every Activity, so you could add a button there. Every one of your Activities will have to have a root view in its layout of the same type and with the same ID for this to work.
class MyApplication: Application() {
override fun onCreate() {
super.onCreate()
registerActivityLifecycleCallbacks(object: ActivityLifecycleCallbacks {
override fun onActivityCreated(activity: Activity, savedInstanceState: Bundle?) {
val rootView = activity.findViewById<ConstraintLayout>(R.id.myRootView)
val layoutParams = ConstraintLayout.LayoutParams(
ViewGroup.LayoutParams.WRAP_CONTENT,
ViewGroup.LayoutParams.WRAP_CONTENT
).apply {
// Set up the appropriate layout constraints here
}
val button = Button(/* ... */)
rootView.addView(button, layoutParams)
}
override fun onActivityStarted(activity: Activity) = Unit
override fun onActivityResumed(activity: Activity) = Unit
override fun onActivityPaused(activity: Activity) = Unit
override fun onActivityStopped(activity: Activity) = Unit
override fun onActivitySaveInstanceState(activity: Activity, outState: Bundle) = Unit
override fun onActivityDestroyed(activity: Activity) = Unit
})
}
}
Personally, I think this is messy, and I would do it in a top level function that you call from onCreate() in each of your Activities. Or better yet, use a single Activity in your app and use Fragments for the different screens.

When to call livedata observer?

On a button click i have to get some value from API call and then launch one screen. I have two options:
Call the observer each time when user will click on button.
Call the observer on fragment onActivityCreated() and store the value in variable and act accordingly on button click.
So which approach I should follow?
Actually it's up to you. But i always prefer to call it in Activity's onCreate() function, so activity only has 1 observer. If you call it in button click, it will give you multiple observers as much as button clicking
Here is some example :
class HomeProfileActivity: BaseActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
initObserver()
initView()
}
private fun initObserver() {
viewModel.profileWorkProccess.observe(this, {
swipeRefreshLayout.isRefreshing = it
})
viewModel.isLoadingJobs.observe(this, {
layoutProgressBarJobs.visibility = View.VISIBLE
recyclerViewJobs.visibility = View.GONE
dotsJobs.visibility = View.GONE
})
//other viewmodel observing ......
}
private fun initView() {
imageProfile.loadUrl(user.image, R.drawable.ic_user)
textName.text = identity.user?.fullName
textAddress.text = identity.user?.city
buttonGetData.setOnClickListener { viewModel.getData(this) }
}
}
If the button is placed on the Activity, and data is displayed in the Fragment, you need to store variable in Activity ViewModel and observe it in Fragment
You only need to call observe one time when fragment is created.
For example:
class MyActivity : AppCompatActivity() {
val viewModel: MyActViewModel by viewModels()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
myButton.setOnClickListener { view ->
viewModel.getData()
}
}
}
class MyActViewModel: ViewModel {
val data: LiveData<String> = MutableLiveData()
fun getData() {}
}
class MyFragment: Fragment {
val actViewModel: MyActViewModel by activityViewModels()
override fun onActivityCreated(...) {
....
actViewModel.data.observe(viewLifecycleOwner, Observer { data ->
...
}
}
}

How to use interface in Android

I know the concept of "Interfaces" but I've hard time to understand how to use them in android development.
Let's say I created an interface to decide if to show progress bar or not -
interface ProgressBarInterface {
fun showProgressBar()
fun hideProgressBar()
}
And I implement this inside BaseActivity/MainActivity in single Activity app:
class BaseActivity : AppCompatActivity() , ProgressBarInterface {
private val TAG = "MainActivity"
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
}
override fun showProgressBar() {
}
override fun hideProgressBar() {
}
}
And inside my other activity I've a button, that when I click on it, I want to trigger showProgressBar in the base activity:
button.setOnClickListener {
//Show progress bar
}
How can I interact with the interface to trigger the function inside base activity?
Since you already are implementing the interface in your BaseActivity, you can then just add what you need to do inside the interface methods, and then call them up in any point in your activity, if what you are looking for is to extend this BaseActiviy into more activities you will need to make this BaseActivity abstract then you can extend in each activity this BaseClass and just use the interface methods
abstract class BaseActivity : AppCompatActivity() , ProgressBarInterface {
private val TAG = "MainActivity"
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
}
override fun showProgressBar() {
progressBar.visibility = View.VISIBLE
}
override fun hideProgressBar() {
progressBar.visibility = View.GONE
}
}
and then in your Activities you can extend from BaseActivity() and just use your interface methods as you have defined in that BaseActivity() to prevent coding them again, you can do
class FirstActivity : BaseActivity() {
...
button.setOnClickListener {
showProgressBar()
}
An easier way to show and hide the views? Use extension functions
fun View.show() {
this.visibility = View.VISIBLE
}
fun View.hide() {
this.visibility = View.GONE
}
you can define that extensions in any class, for example ViewUtils.kt and then just call
button.setOnClickListener {
progressBar.show()
}
or
button.setOnClickListener {
progressBar.hide()
}

Kotlin synthetic view in activity gets null when a custom dialog is shown and dismissed

I am updating a view in activity's onCreate method which is working fine using kotlin extension as stated below.
Activity's onCreate
import kotlinx.android.synthetic.main.activity_otpverification.*
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_otpverification)
tvContactNumber.text = getString(R.string.dual_string_value_placeholder)
}
Then at a click of a button I am showing a custom dialog for performing some action. When the dialog is dismissed I update the same textView in activity with the data sent from dialog, but the view tvContact is throwing null exception.
Activity's onClick
override fun onClick(p0: View?) {
when (p0?.id) {
R.id.ivEdit -> {
object : ChangeNumberDialog(this) {
override fun onSubmitClicked(number: String) {
tvContactNumber.text =number
}
}.show()
}
}
}
onSubmitClicked is an abstract method in the dialog which is triggered when the dialog is dismissed.
Error from logcat :
java.lang.IllegalStateException: tvContactNumber must not be null
at com.beat.em.ui.activities.OTPVerificationActivity$onClick$1.onSubmitClicked
(OTPVerificationActivity.kt:211)
onCreate and onClick methods from the ChangeNumberDialog:
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val view = layoutInflater.inflate(R.layout.dialog_change_number, null, false)
setContentView(view)
setCanceledOnTouchOutside(false)
setCancelable(true)
tvSubmit.setOnClickListener(this)
}
override fun onClick(view: View) {
when (view.id) {
R.id.tvSubmit -> {
onSubmitClicked(etNumber.text.toString().trim())
dismiss()
}
}
}
I have just started using kotlin extension and not able to understand the cause. Help appreciated.
The variable you are trying to access is in another scope, try to add explicit scope to the view i.e.
this#YourActivity.tvContactNumber.text = number

Categories

Resources