I am creating a bus log check application, however i ended up encountering an issue when its time to logout.
Problem: The logout button is inside a fragment and i want to make a listener whereby on completion of logging out, the process directs me back to the main activity
class PeopleFragment : Fragment() {
private lateinit var auth: FirebaseAuth
override fun onCreateView( inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
// Inflate the layout for this fragment
val view = inflater.inflate(R.layout.fragment_people, container, false)
auth = FirebaseAuth.getInstance()
view.apply {
signoutbutton.setOnClickListener {
FirebaseAuth.getInstance()
.signOut()
}
}
return view
}
}
Below is the Activity file to the Activity i intend to be directed to:
class LogInActivity : AppCompatActivity() {
private lateinit var auth: FirebaseAuth
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(activity_log_in)
auth = FirebaseAuth.getInstance()
loginbutton.setOnClickListener {
doLogin()
}
tosignupTV.setOnClickListener {
startActivity(Intent(this, SignUpActivity::class.java))
finish()
}
}
private fun doLogin() {
if (loginEnail.text.toString().isEmpty()) {
loginEnail.error = "Please enter email"
loginEnail.requestFocus()
return
}
if (!Patterns.EMAIL_ADDRESS.matcher(loginEnail.text.toString()).matches()) {
loginEnail.error = "Please enter valid email"
loginEnail.requestFocus()
return
}
if (loginPassword.text.toString().isEmpty()) {
loginPassword.error = "Please enter password"
loginPassword.requestFocus()
return
}
auth.signInWithEmailAndPassword( loginEnail.text.toString(), loginPassword.text.toString())
.addOnCompleteListener(this) { task ->
if (task.isSuccessful) {
val user = auth.currentUser
updateUI(user)
} else {
Toast.makeText(baseContext, "login failed.",
Toast.LENGTH_SHORT).show()
updateUI(null)
}
}
}
public override fun onStart() {
super.onStart()
// Check if user is signed in (non-null) and update UI accordingly.
val currentUser = auth.currentUser
updateUI(currentUser)
}
private fun updateUI(currentUser: FirebaseUser?){
if (currentUser != null) {
startActivity(Intent(this, MainActivity::class.java))
finish()
}else{
Toast.makeText(
baseContext, "Please verify your email address.",
Toast.LENGTH_SHORT
).show()
}
}
}
this is the code to the main activity containing all the fragments:
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val homefragment = HomeFragment()
val peoplefragment = PeopleFragment()
val myaccountfragment =MyAccountFragment()
makeCurrentFragment( homefragment)
navvbarr.setOnNavigationItemSelectedListener {
when(it.itemId) {
R.id.navigation_home ->makeCurrentFragment(homefragment)
R.id.navigation_people ->makeCurrentFragment(peoplefragment)
R.id.navigation_my_account ->makeCurrentFragment(myaccountfragment)
}
true
}
}
private fun makeCurrentFragment(fragment: Fragment) =
supportFragmentManager.beginTransaction().apply {
replace(R.id.fragment_layout, fragment)
commit()
}
}
Related
So I got a problem, When the Main Page is shown, it's supposed to call the API to get the data from it. But, the API never called. What I find strange is, that before Main Page, there is a login page that successfully called the API for login. But, on Main Page, the Retrofit/Okhttp never called the Page to get the data. Do you guys know what's wrong?
Here's my code screenshot:
StoryPageSource
StoryRepository
ViewModel
StoryAdapter
MainActivity
class MainActivity : AppCompatActivity() {
private lateinit var bind: ActivityMainBinding
private lateinit var storyAdapter: StoryAdapter
private val timelineStoryViewModel: TimelineStoryViewModel by viewModels {
ViewModelFactory(this, application)
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
bind = ActivityMainBinding.inflate(layoutInflater)
setContentView(bind.root)
showLoadingProcess(true)
displayStories()
bind.floatingActionButton.setOnClickListener {
startActivity(Intent(this, AddStoryActivity::class.java))
}
supportActionBar?.setTitle(R.string.app_name)
}
override fun onCreateOptionsMenu(menu: Menu): Boolean {
val inflaterMenu = menuInflater
inflaterMenu.inflate(R.menu.menus, menu)
return true
}
override fun onOptionsItemSelected(item: MenuItem): Boolean {
when (item.itemId) {
R.id.app_bar_settings -> {
val context = this
val pref = context.getSharedPreferences(
R.string.token_pref.toString(),
Context.MODE_PRIVATE
)
val editor = pref.edit()
editor.remove(R.string.token.toString())
editor.remove(getString(R.string.email))
editor.remove(getString(R.string.password))
editor.apply()
startActivity(Intent(this, LoginActivity::class.java))
}
R.id.app_bar_maps -> {
startActivity(Intent(this, MapsActivity::class.java))
}
}
return super.onOptionsItemSelected(item)
}
private fun displayStories() {
showLoadingProcess(false)
storyAdapter = StoryAdapter()
bind.storyRV.layoutManager = LinearLayoutManager(this)
bind.storyRV.adapter = storyAdapter
timelineStoryViewModel.story().observe(this#MainActivity) { story ->
storyAdapter.submitData(lifecycle, story)
}
storyAdapter.setOnProfileCallback(object : StoryAdapter.OnProfileCallback {
override fun onProfileClicked(data: TimelineStory) {
chosenProfile(data)
}
})
}
private fun chosenProfile(story: TimelineStory) {
var name: TextView = findViewById(R.id.userName)
var storyPict: ImageView = findViewById(R.id.userImageStory)
val optionsCompat: ActivityOptionsCompat =
ActivityOptionsCompat.makeSceneTransitionAnimation(
this,
Pair(storyPict, getString(R.string.imageStoryDetail)),
Pair(name, getString(R.string.nama_pengguna)),
)
val sendData = Intent(this, StoryDetailActivity::class.java)
sendData.putExtra(StoryDetailActivity.EXTRA_STORY, story)
startActivity(sendData, optionsCompat.toBundle())
Toast.makeText(this, "Memuat Story " + story.name, Toast.LENGTH_SHORT).show()
}
private fun showLoadingProcess(isLoading: Boolean) {
if (isLoading) {
bind.loading.visibility = View.VISIBLE
} else {
bind.loading.visibility = View.GONE
}
}
}
I am implementing a function which is to close the application by clicking back button twice. It shows the toast message when it is first clicked. However, the problem is it shows infinite loop error then is termiated. I do not really understand the reason after two days of browsing. Any help will be greatly appreciated.
My fragment code
class RegisteredMainFragment : Fragment() {
private lateinit var binding : FragmentRegisteredMainBinding
private val viewModel : RegisteredMainViewModel by inject()
private lateinit var mContext: MainActivity
override fun onAttach(context: Context) {
super.onAttach(context)
mContext = context as MainActivity
}
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?
): View {
binding = DataBindingUtil.inflate(inflater, R.layout.fragment_registered_main, container, false)
return binding.root
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
binding.viewModel = viewModel
binding.lifecycleOwner = viewLifecycleOwner
onBackPressedCallback()
}
private fun onBackPressedCallback() {
requireActivity().onBackPressedDispatcher.addCallback(viewLifecycleOwner,
object : OnBackPressedCallback(true ) {
override fun handleOnBackPressed() {
if (doubleBackToExitPressedOnce) {
requireFragmentManager().popBackStack()
return
}
doubleBackToExitPressedOnce = true
Toast.makeText(requireContext(), "click back button again", Toast.LENGTH_SHORT).show()
Handler().postDelayed({ doubleBackToExitPressedOnce = false }, 2000)
}
})
}
override fun onResume() {
super.onResume()
viewModel.onAuthExist()
}
override fun onDestroyView() {
super.onDestroyView()
PushObserverService.unregisterObserver(this)
}
}
I solved the problem by writing it in override fun onAttach
Please check the code below
override fun onAttach(context: Context) {
super.onAttach(context)
mContext = context as MainActivity
callback = object : OnBackPressedCallback(true) {
override fun handleOnBackPressed() {
if (doubleBackToExitPressedOnce) {
activity?.finish()
}
doubleBackToExitPressedOnce = true
Handler().postDelayed({ doubleBackToExitPressedOnce = false }, 2000)
showSnackBar(
context = mContext,
layout = binding.layoutMain,
type = Constants.SnackBarTypes.Warn,
message = mContext.getString(R.string.tap_twice_to_terminate)
)
}
}
requireActivity().onBackPressedDispatcher.addCallback(this, callback)
}
I have written the code in Kotlin language
I need the firebase realtime database to add a child with user's UID with email as it's value under the users column when the user signs up. The Login is successful and then it passes to the main activity .The users details is listed in Authentication-> Users but the realtime database is not working and also when I click the back button it opens the main activity again and again ,after a number of clicks it goes back the login page and exits,maybe this is because it could not connect to the database.
Please help me with necessary changes.
Here is the code
MAIN ACTIVITY
private var mFirebaseAnalytics: FirebaseAnalytics?=null
class MainActivity : AppCompatActivity() {
var listofContacts = ArrayList<Contacts>()
var adapter: ContactsAdapter? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
mFirebaseAnalytics= FirebaseAnalytics.getInstance(this)
listofContacts.add(Contacts("NAME", 123456789))
adapter = ContactsAdapter(this, listofContacts)
listview.adapter=adapter
}
inner class ContactsAdapter : BaseAdapter {
var contactslist = ArrayList<Contacts>()
var context: Context? = null
constructor(context: Context, contactslist: ArrayList<Contacts>) : super() {
this.contactslist = contactslist
this.context = context
}
override fun getView(position: Int, convertView: View?, parent: ViewGroup?): View {
val contact = contactslist[position]
val inflater = context!!.getSystemService(Context.LAYOUT_INFLATER_SERVICE) as LayoutInflater
val myView = inflater.inflate(R.layout.contact_view, null)
myView.name.text = contact.name!!
myView.num.text = contact.num.toString()!!
myView.setOnClickListener {
val intent= Intent(context,ContactInfo::class.java!!)
intent.putExtra("name",contact.name!!)
intent.putExtra("num",contact.num!!)
context!!.startActivity(intent)
}
return myView
}
override fun getItem(position: Int): Any {
return contactslist[position]
}
override fun getItemId(position: Int): Long {
return position.toLong()
}
override fun getCount(): Int {
return contactslist.size
}
}
}
LOGIN
private var mAuth:FirebaseAuth?=null
var database=FirebaseDatabase.getInstance()
var myRef=database.reference
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_login)
mAuth = FirebaseAuth.getInstance()
}
fun loginevent(view: View){
login(etun.text.toString(),pw.text.toString())
}
fun login(email:String,password:String){
mAuth!!.createUserWithEmailAndPassword(email,password)
.addOnCompleteListener(this){task ->
if(task.isSuccessful){
Toast.makeText(applicationContext,"Login Successful", Toast.LENGTH_LONG).show()
LoadMain()
}
else
Toast.makeText(applicationContext,"Login Failed", Toast.LENGTH_LONG).show()
}
}
override fun onStart() {
super.onStart()
LoadMain()
}
fun LoadMain(){
var currentuser=mAuth!!.currentUser
if(currentuser!=null) {
myRef.child("Users").child(currentuser.uid).setValue(currentuser.email)
val intent = Intent(this, MainActivity::class.java)
intent.putExtra("email", currentuser.email)
intent.putExtra("uid", currentuser.uid)
startActivity(intent)
}
}
}
I'm trying to add my fragment into stack. So when the next button is clicked, the fragment will added to stack. But then I got some problem that the container id is not found. Here is my code in the next button :
override fun onNextClicked(callback: StepperLayout.OnNextClickedCallback?) {
val myFragment = MotorFragment()
val fragmentManager = fragmentManager
val fragmentTransaction = fragmentManager!!.beginTransaction()
fragmentTransaction.add(R.id.container, myFragment)
fragmentTransaction.addToBackStack("firstFrag")
fragmentTransaction.commit()
Toast.makeText(context,"firstFrag Added", Toast.LENGTH_SHORT).show()
callback?.goToNextStep()
}
I google it but none of the solution work for me. I try to change the
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
getReligion()
}
into this :
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(null)
getReligion()
}
and still got the same error.
And here is my on create code :
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
val v = inflater.inflate(R.layout.fragment_spk_motor, container, false)
return v
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
getReligion()
}
I expect when I click the next button will not force close and success adding into stack. Help please.
My full code :
class MotorFragment : Fragment(), Step, BlockingStep {
var datareligion: ArrayList<MasterReligion?>? = null
lateinit var dataManager: DataManager
val myCalendar = Calendar.getInstance()
companion object {
fun newInstance(): MotorFragment {
return MotorFragment()
}
}
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
val v = inflater.inflate(R.layout.fragment_spk_motor, container, false)
return v
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
getReligion()
}
fun getReligion() {
API.getreligion().enqueue(object : Callback<ArrayList<MasterReligion>> {
override fun onResponse(call: Call<ArrayList<MasterReligion>>, response: Response<ArrayList<MasterReligion>>) {
if (response.code() == 200) {
datareligion = ArrayList()
datareligion?.add(0, null)
response.body()?.forEach { datareligion?.add(it) }
val adapter = MyStepFragment.CustomAdapter<MasterReligion?>(activity, R.layout.spinner_custom, R.layout.spinner_dropdown_item, datareligion?.toTypedArray()!!)
spnOTR.adapter = adapter
}else{
Toast.makeText(activity, "Error", Toast.LENGTH_LONG).show()
}
}
override fun onFailure(call: Call<ArrayList<MasterReligion>>, throwable: Throwable) {
Toast.makeText(activity, "Please check your connection", Toast.LENGTH_LONG).show()
}
})
spnOTR?.onItemSelectedListener = object : AdapterView.OnItemSelectedListener {
override fun onItemSelected(parentView: AdapterView<*>, selectedItemView: View?, position: Int, id: Long) {
if (selectedItemView == null) {
Toast.makeText(context, "Tipe harga jual tidak terpilih", Toast.LENGTH_SHORT).show()
} else {
val data = position
val prefs = PreferenceManager.getDefaultSharedPreferences(activity!!.baseContext) //context
val prefEditor = prefs.edit()
prefEditor.putInt("savedValue", data)
prefEditor.apply()
}
}
override fun onNothingSelected(parentView: AdapterView<*>) {
Toast.makeText(context, "Nothing selected", Toast.LENGTH_SHORT).show()
}
}
}
override fun verifyStep(): VerificationError? {
return null
}
override fun onSelected() {}
override fun onError(error: VerificationError) {}
override fun onBackClicked(callback: StepperLayout.OnBackClickedCallback?) {
TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
}
override fun onCompleteClicked(callback: StepperLayout.OnCompleteClickedCallback?) {
TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
}
override fun onNextClicked(callback: StepperLayout.OnNextClickedCallback?) {
val myFragment = MotorFragment()
val fragmentManager = fragmentManager
val fragmentTransaction = fragmentManager!!.beginTransaction()
fragmentTransaction.add(R.id.container, myFragment)
fragmentTransaction.addToBackStack("firstFrag")
fragmentTransaction.commit()
Toast.makeText(context,"firstFrag Added", Toast.LENGTH_SHORT).show()
callback?.goToNextStep()
}
}
Try this
// Define framelayout in your activity xml , then try to add
fragment
<FrameLayout
xmlns
:android="http://schemas.android.com/apk/res/android
android:layout_width="match_parent
android:layout_height="match_parent
android:id="#+id/container"/>
Hi I have LoginActivity and LoginViewModel and some more classes. I have showLoading and hideLoading in the BaseActivity so it can be accessible from each activity.
I am able to call LoginActivity method from the LoginViewModel like mNavigator?.startForgotPasswordActivity()
I want to call it from the LoginViewModel then what the way to do it using MVVM ? or I am going with wrong approach. Please suggest what is the correct way to do this ?
BaseActivity.kt
abstract class BaseActivity : AppCompatActivity(), AnkoLogger {
private val progressBar: ProgressBar? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
}
protected fun getToolbar(): Toolbar {
val toolbar: Toolbar = findViewById(R.id.toolbar)
setSupportActionBar(toolbar)
return toolbar
}
protected fun performDependencyInjection() {
AndroidInjection.inject(this);
}
#TargetApi(Build.VERSION_CODES.M)
fun requestPermissionsSafely(permissions: Array<String>, requestCode: Int) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
requestPermissions(permissions, requestCode)
}
}
#TargetApi(Build.VERSION_CODES.M)
fun hasPermission(permission: String): Boolean {
return Build.VERSION.SDK_INT < Build.VERSION_CODES.M || checkSelfPermission(permission) == PackageManager.PERMISSION_GRANTED
}
fun isNetworkConnected(): Boolean {
return NetworkUtils.isNetworkConnected(applicationContext)
}
fun showLoading() {
hideLoading()
// show progress bar
}
fun hideLoading() {
// hide progress bar
}
}
LoginActivity.kt
class LoginActivity : BaseActivity(), LoginNavigator {
#Inject
lateinit var loginViewModel: LoginViewModel
override fun onCreate(savedInstanceState: Bundle?) {
performDependencyInjection()
super.onCreate(savedInstanceState)
val activityLoginBinding: ActivityLoginBinding = DataBindingUtil.setContentView<ActivityLoginBinding>(this, R.layout.activity_login)
activityLoginBinding.loginViewModel = loginViewModel
loginViewModel.mNavigator = this
}
override fun startHomeActivity() {
TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
}
override fun startRegistrationActivity() {
startActivity(Intent(this, RegistrationActivity::class.java))
}
override fun startForgotPasswordActivity() {
startActivity(Intent(this, ForgotPasswordActivity::class.java))
}
override fun handleError(throwable: Throwable) {
TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
}
}
LoginViewModel.kt
class LoginViewModel : BaseViewModel<LoginNavigator>(), AnkoLogger {
val emailField = ObservableField<String>()
private val email: String
get() = emailField.get()
val passwordField = ObservableField<String>()
private val password: String
get() = passwordField.get()
#Suppress("PARAMETER_NAME_CHANGED_ON_OVERRIDE")
fun login(view: View) {
if (isEmailAndPasswordValid(email, password)) {
ApiHelperImpl().doServerLoginApiCall(email, password)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribeWith(object : CallbackWrapper<LoginResponse>() {
override fun onSuccess(loginResponse: LoginResponse) {
info { loginResponse }
}
})
}
}
/**
* Validate email and password. It checks email and password is empty or not
* and validate email address is correct or not
* #param email email address for login
* #param password password for login
* #return true if email and password pass all conditions else false
*/
private fun isEmailAndPasswordValid(email: String, password: String): Boolean {
if (email.isEmpty()) return false
if (!Patterns.EMAIL_ADDRESS.matcher(email).matches()) return false
if (password.isEmpty()) return false
return true
}
}
BaseViewModel.kt
abstract class BaseViewModel<N> {
var mNavigator: N? = null
}
There can be 2 approach for same
1) Use all functions i.e related to UI update or UI event listener from a view (Activity or Fragment) according to mvp and from viewmodel only try to manage data like api's and other logic
class LoginActivity : BaseActivity(), LoginNavigator {
#Inject
lateinit var mLoginViewModel: LoginViewModel
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_login)
mLoginViewModel.mNavigator = this
callApi()
}
private fun callApi() {
showLoading()
mLoginViewModel.callApi()
}
override fun openHomeScreen(model: Model) {
hideLoading()
showSnackBar(constraint_root, model.Domain)
}
}
class LoginViewModel(sessionManager: SessionManager, requestInterface: RequestInterface) : BaseViewModel<LoginNavigator>(sessionManager, requestInterface) {
fun callApi() {
requestInterface.getServiceIP()
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(this::handleResponse, this::handleError)
}
private fun handleResponse(model: Model) {
if (model.Domain == null) {
mNavigator!!.openHomeScreen(model)
} else {
}
}
private fun handleError(error: Throwable) {
error.printStackTrace()
}
}
2)
In Login interface add a function
interface LoginNavigator {
fun openHomeScreen()
fun getActivity(): BaseActivity
}
In LoginActivity override the function and return
override fun getActivity(): BaseActivity = this
Now using navigator you can access base activity & call show/hide loader function
mNavigator!!.getActivity().showLoading()