I'm still new to Kotlin and Android and developing a project that uses shared preferences.
I have multiple Activities and a user must be logged in to use all functions of the Main Activity.
The Main Acitivity has a menu drawer with few menu items. The first one is redirecting user to Login Activity. The rest of the menu items are hidden until user is logged in.
The process is as follows:
Start with Main Activity -> The application checks if the user is logged in -> If not you must log in -> Go to Login Activity -> Logging in -> Then go back to Main Activity.
However, the main activity does not refresh at all. I mean, I don't even know how to do it. I want my main activity to refresh when I return from login activity.
So far, it only works when I close the app and reopen it. The session is likely cached in Shared preferences.
I tried to start and finish Activites by clicking certain buttons (eg After clicking login button when user passed all credentials) but it didn't work.
The following code checks that the user is logged in when the main activity starts
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val menu: Menu = navView.menu
val target2: MenuItem = menu.findItem(R.id.miItem2)
val target3: MenuItem = menu.findItem(R.id.miItem3)
//region user session
try{
if(!SharedPrefManager.getInstance(this)!!.isLoggedIn()){
target2.isVisible = false
target3.isVisible = false
Toast.makeText(applicationContext, "nie zalogowany", Toast.LENGTH_LONG).show()
}
else{
target2.isVisible = true
target3.isVisible = true
Toast.makeText(applicationContext, "zalogowany", Toast.LENGTH_LONG).show()
}
}catch (e: NullPointerException){
}
//endregion
And there is function isLoggedIn():
fun isLoggedIn(): Boolean{
val sharedPreferences: SharedPreferences = mCtx.getSharedPreferences(SHARED_PREF_NAME, Context.MODE_PRIVATE)
if(sharedPreferences.getString(KEY_USER_EMAIL, null) != null){
return true
}
return false
}
This code below takes me to the Login Activity
val btnLogInActivity : Button = findViewById(R.id.logButton)
btnLogInActivity.setOnClickListener {
val intent = Intent(this, LoginActivity::class.java)
startActivity(intent)
}
And this is the code in Login Activity (userLogin function is not important in this context):
buttonLogin.setOnClickListener {
userLogin()
newMainActivity()
}
private fun newMainActivity(){
val intent = Intent(this, MainActivity::class.java)
startActivity(intent)
}
Ps. If you need more code to fix the problem, let me know
You just have to add finish() in your code. Update your methods with the code below:
btnLogInActivity.setOnClickListener {
val intent = Intent(this, LoginActivity::class.java)
startActivity(intent)
finish()
}
And after login:
private fun newMainActivity(){
val intent = Intent(this, MainActivity::class.java)
startActivity(intent)
finish()
}
Related
I use SharedPreferences to stay logged in, and it works, but when I use it with Firebase the code doesn't work, I don't know if with firebase shared preferences doesn't work for staying logged in, or what I should do, I'm new with android studio and I search in youtube but I can't find how to stay logged in with firebase using Kotlin
Added code sample
For Firebase:
logInButton.setOnClickListener {
if(emailEditText.text.isNotEmpty() && passwordEditText.text.isNotEmpty()){
FirebaseAuth.getInstance()
.signInWithEmailAndPassword(emailEditText.text.toString()
,passwordEditText.text.toString()).addOnCompleteListener {
if(it.isSuccessful){
showHome(it.result?.user?.email?:"",ProviderType.BASIC)
}else{
showAlert()
}
}
}
}
private fun showAlert(){
val builder= AlertDialog.Builder(this)
builder.setTitle("Error")
builder.setMessage("Error")
builder.setPositiveButton("Accept",null)
val dialog:AlertDialog=builder.create()
dialog.show()
}
private fun showHome(email:String,provider:ProviderType){
val homeIntent= Intent(this,Menu::class.java).apply{
putExtra("email",email)
putExtra("provider",provider.name)
}
startActivity(homeIntent)
}
For shared preferences
private lateinit var sharedPreferences: SharedPreferences
var isRemember=false
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
sharedPreferences = getSharedPreferences("SHARED_PREF", Context.MODE_PRIVATE)
isRemember=sharedPreferences.getBoolean("CHECKBOX",false )
if (isRemember){
val intent= Intent(this,Menu::class.java)
startActivity(intent)
finish()
}
fun checkUserValues(){
}
logInButton.setOnClickListener {
val email: String =emailEditText.text.toString()
val password: String = passwordEditText.text.toString()
val checked:Boolean = check_remember.isChecked
val editor : SharedPreferences.Editor = sharedPreferences.edit()
editor.putString("EMAIL",email)
editor.putString("PASSWORD",password)
editor.putBoolean("CHECKBOX",checked)
editor.apply()
Toast.makeText(this, "Logged in save",Toast.LENGTH_LONG).show()
val intent=Intent(this,Menu::class.java)
startActivity(intent)
finish()
}
}
Much appreciated.
I use shared preferences to stay logged in,
Firebase already stores the user credentials and restores them when the user restarts the app. There is no code you need to write for this, as saving the data happens automatically when the user first signs in, and the information is then restored, when the user starts the app again.
I recommend removing the code where you persist the credentials to shared preferences, and leave that to Firebase.
I am using shared preferences to login and logout the user of the application. Once the user logs in, the login screen is not showed again after the application is killed and started again. But when I enter the application and go to the screen after the login page and press back button, it shows the login screen again. I don't want the login screen to be showed again even after the back button is pressed. I want it to go completely out of the aplication on pressing back button.
Here is my code for login :
val sp = getSharedPreferences("login",MODE_PRIVATE)
if (sp.getBoolean("logged", false))
{
login()
}
loginButton.setOnClickListener() {
login()
sp.edit().putBoolean("logged", true).apply()
Log.v("Login Msg", "Login button clicked")
getusername = findViewById(R.id.usernameEditText)
var username = usernameEditText.text
Log.v("username",username.toString())
getpass = findViewById(R.id.passEditText)
var pass = passEditText.text
Log.v("pass",pass.toString()) }
fun login() {
val i = Intent(this, HomeActivity::class.java)
startActivity(i)
}
Here is the code for logout:
logoutButton.setOnClickListener(){
logout()
val sp = getSharedPreferences("login",MODE_PRIVATE)
sp.edit().putBoolean("logged", false).apply()
Log.v("Logout msg", "Logout button clicked")
}
fun logout(){
val i = Intent(this, LogInActivity::class.java)
startActivity(i)
}
Actually we can override "Back Button" method in each activity.
Below I reuse your code to manage logout when back button is pressed, perhaps this is the one you are looking for
override fun onBackPressed() {
super.onBackPressed()
val sp = getSharedPreferences("login",MODE_PRIVATE)
sp.edit().putBoolean("logged", false).apply()
logout()
}
This is happening because the login activity is still there in the back stack after going to the HomeActivity. To achieve your requirement you must remove everything from the back stack before going to the HomeActivity.
Replace your login function with the below function.
fun login() {
val i = Intent(this, MainActivity::class.java)
i.run {
addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP)
addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK)
addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
}
startActivity(i)
}
Here we are basically adding a set of flags to the intent.
Intent.FLAG_ACTIVITY_CLEAR_TOP clears all the view underneath the current view
Intent.FLAG_ACTIVITY_CLEAR_TASK we are removing the existing tasks
Intent.FLAG_ACTIVITY_NEW_TASK we are creating a new task for the HomeActivity.
Intent.FLAG_ACTIVITY_CLEAR_TASK and Intent.FLAG_ACTIVITY_NEW_TASK should be used in conjunction with each other.
I am doing a college project where students and teachers are using school data. I want teachers and students to have access to different data. I want teachers to send to home activity while students to main Activity. The user has already entered his type while Signing up. I want users to get specific activity based on user type. I am getting user-type data but not able to use it for other work.
Here is my code:
override fun onStart() {
super.onStart()
var usertype: String? = null
if (FirebaseAuth.getInstance().currentUser != null) {
var currentUser: String = FirebaseAuth.getInstance().currentUser.uid
val DataBaseReference = FirebaseDatabase.getInstance().getReference().child("Users")
DataBaseReference.addValueEventListener(object : ValueEventListener {
override fun onDataChange(datasnapshot: DataSnapshot) {
usertype = datasnapshot.child(currentUser).child("type").getValue(String::class.java)
usertype?.let { Log.d("usertype", it) }
}
override fun onCancelled(error: DatabaseError) {
}
})
val utype = usertype.toString()
Log.i("utype", utype)
if (utype == "Student"){
val intent = Intent(this, MainActivity::class.java)
intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK or Intent.FLAG_ACTIVITY_NEW_TASK)
startActivity(intent)
finish()
}
else {
val intent = Intent(this, home::class.java)
intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK or Intent.FLAG_ACTIVITY_NEW_TASK)
startActivity(intent)
finish()
}
}
}
The Log result of user-type is a student but utype Log result is null. Now I want to get data of usertype from onDatachange to other function.
I am getting user-type data but not able to use it for other work.
This is happening because you are using the following if statement:
val utype = usertype.toString()
Log.i("utype", utype)
if (utype == "Student"){ ...}
Outside the callback. Firebase API is asynchronous. Any code that needs data from an asynchronous operation needs to be inside the "onDataChange()" method, or be called from there. So the simplest solution, in this case, is to move all the logic related to the above lines of code, in the "onDataChange()" method, inside the scope of let:
usertype = datasnapshot.child(currentUser).child("type").getValue(String::class.java)
usertype?.let {
Log.d("usertype", it)
//Use your logic
}
}
I'm making a system of "sessions" where the user can launch, finish and view his session.
The user go through a first fragment to create his session and then go into a fragment "in session".
If he return to the main menu before finishing his session, I want him to go directly to "in session" without going through the "new session" fragment.
All session data are stored into a local database and I use Kotlin coroutines to fetch data from the db (see code example below)
It's my first time using coroutine, and I will admit it's a bit fuzzy
for me, all the help is welcome
The problem is that when the user press the bouton to navigate, the coroutine finish after the verification to see if there is a current session, that lead to verify a null object or the previous session of the current session, and so navigate to a the "new session" fragment
What I need is a way to wait for the coroutine to finish and then
handle the button click
All the code wrote here is contain inside inside the viewModel.
This is how I setup the Job/Scope
private var viewModelJob = Job()
private val uiScope = CoroutineScope(Dispatchers.Main + viewModelJob)
And this is how I launch the coroutine:
private fun initializeLastSession() {
uiScope.launch {
lastSession.value = getLastSessionFromDatabase()
}
}
private suspend fun getLastSessionFromDatabase(): Session? {
return withContext(Dispatchers.IO) {
var session = database.getLastSession()
session
}
}
The verification is made inside this function
fun isSessionActive(): Boolean {
//Simplified
if (lastSession.value = null) {
return false
} else if (...) {
return true
} else {
return false
}
This last function "isSessionActive" is called from an if statement from the fragment itlsef, when the user press the navigation button.
If it's true then it navigate to "InSession", else in "newSession"
I've seen multiple way of waiting for a coroutine to finish but none match the way I launch it, and even less have a solution that has worked for me.
Would you allow me with a simple example unrelated to your code? But strongly related to the problem:
uiScope.launch{
withContext(Dispatchers.IO){
val dataFromDatabase = getSomeDataFromDatabase()
if (dataFromDatabase.notEmpty()){ //or something
withContext(Dispatchers.Main){
//send data to fragment here :)
}
}
}
}
EDIT:
Since you stated you are in the ViewModel, you don't need to return any value, you need to observe that changed value:
//on top of your ViewModel class:
val yourVariableName: MutableLiveData<Boolean> = MutableLiveData()
//than in your method:
uiScope.launch{
withContext(Dispatchers.IO){
val dataFromDatabase = getSomeDataFromDatabase()
if (dataFromDatabase.notEmpty()){ //or something
withContext(Dispatchers.Main){
if (lastSession.value = null) {
yourVariableName.value = false
} else if (...) {
yourVariableName.value = true
} else {
yourVariableName.value = false
}
}
}
}
}
And than in your fragment:
//after you have successfully instantiated the `ViewModel`:
mViewModel.yourVariableName.observe(this , Observer{ valueYouAreObserving->
// and here you have the value true ore false
Log.d("Tag", $valueYouAreObserving)
})
I've seen this question posed all over the place but I have not seen where anyone has been able to answer why the function doesn't get called.
I've run the app in debug mode with a break point set at the onBackPressed function and it completely ignores it. The compiler even recognizes and changes my red break point into a circle with a line through it.
I have a ContractSelectAdapter with a setOnClickListener passing a selected contract to ContractMenuActivity using putExtra(). Once in the ContractMenuActivity there is a button to open the activity below, UploadImageActivity and while in the activity I want a user to be able to hit the back button to reopen the ContractMenuActivity but within the onCreate function of ContractMenuActivity is a getStringExtra call that causing it to crash because there is no "extra". Which is why I'm trying to use the onBackPressed. In theory it should work perfectly.
I've tried putting a call to the onBackPressed function manually in the onCreate function of UploadImageActivity just to make sure it works and it does. It immediately opens ContractMenuActivity. So the function works it just never gets called for some reason. Extremely frustrating.
the onBackPressed function is at the very end of the UploadImageActivity class (first code excerpt)
Any help would be GREATLY appreciated.
class UploadImageActivity : AppCompatActivity() {
var prefs: Prefs? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_upload_image)
prefs = Prefs(this)
}
override fun onBackPressed()
{
super.onBackPressed()
val returnIntent = Intent(this, ContractMenuActivity::class.java)
returnIntent.putExtra(SELECTED_CONTRACT, prefs!!.rmsAppContractID)
startActivity(returnIntent)
}
}
class ContractMenuActivity : AppCompatActivity()
{
var selectedContract: String = ""
var prefs: Prefs? = null
override fun onCreate(savedInstanceState: Bundle?)
{
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_contract_menu)
prefs = Prefs(this)
val btnUploadImage = findViewById<Button>(R.id.btnUploadImage)
selectedContract = intent.getStringExtra(SELECTED_CONTRACT)
prefs!!.rmsAppContractID = this.selectedContract
var contractSelectMsg = "Contract ${prefs!!.rmsAppContractID} selected"
val duration = Toast.LENGTH_SHORT
Toast.makeText(this, contractSelectMsg, duration).show()
btnUploadImage.setOnClickListener {this.openUploadImage()}
}
public fun openUploadImage()
{
val registered = prefs!!.rmsAppRegistered
var message: String
if(registered)
{
val intent = Intent(this, UploadImageActivity::class.java)
startActivity(intent);
}
else
{
message = "You must Pair this device before you can upload an image."
val intent = Intent(this, PostResponseMessageActivity::class.java).apply {
putExtra(POST_RESPONSE_MESSAGE, message)
}
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(intent);
}
}
}
Parent name in manifest:
<activity
android:name=".ContractMenuActivity"
android:label="#string/contractMenuLabel"
android:parentActivityName=".ContractSelectActivity">
</activity>
<activity
android:name=".UploadImageActivity"
android:label="#string/uploadImageLabel"
android:parentActivityName=".ContractMenuActivity">
</activity>
super.onBackPressed()
Will finish the current activity. If you want to do some action after user press back button, You must to past your code to top of super.onBackPressed().