Kotlin Android Studio - "If" is not executing in correct condition - kotlin

I am trying to make a simple Kotlin program of sign in page. The concept here was that this page will get the intent extras coming from the register page and use them to verify if the user logged in with the same registered username and password which would then bring the user to the main page of the app. The problem is that when the user actually does put the correct input, the intent won't execute (or rather the if gate).
At first I thought that the nullsafe strings were the problem when I took the intent data from the register page and so I converted the variable into a string.
But it was still no bueno so I'm not sure what is the wrong here.

You are initializing the username and userPass at creation of the screen so they will be always empty String. Try moving them to inside the listener so they actually will be read out when you click the button. Like
val intent = intent
val registeredUsername = intent.getStringExtra("RegisteredUsername").toString()
val registeredPassword = intent.getStringExtra("RegisteredPassword").toString()
signInbtn.setOnClickListener{
val username = usernameInput.text.toString()
val userPass = passwordInput.text.toString()
Toast.makeText(applicationContext, "username is: $registeredUsername", Toast.LENGTH_LONG).show()
Toast.makeText(applicationContext, "password is: $registeredPassword", Toast.LENGTH_LONG).show()
if ((username == registeredUsername) && (userPass == registeredPassword))
{
val moveMainPage = Intent(this#SignIn, MainPage::class.java)
startActivity(moveMainPage)
}
else
{
invalideUserText.visibility = View.VISIBLE
invalidPassword.visibility = View.VISIBLE
}
}

Related

How to manually update a kotlin flow

I'm trying to update the source of a Flow in Kotlin and I'm not sure if this is the right approach and if it's possible with Flow at all.
I have a database containing posts for a user and this returns me a Flow<List<Post>>.
Now when I select another user I want the flow of the database to return me the posts of the newly selected user:
lateinit var userPosts: Flow<List<Post>>
private set
fun getPostsForUser(user: User) {
userPosts = database.getAllPostsForUser(user)
}
But the flow never gets updated with the data of the new selected user. Is Flow still the right choice in this case and if yes, how can I update the flow with the new posts?
I know how to do it manually with fetching data from the database and emitting it using LiveData, but I would like to avoid handling the update of posts everytime the user posts something new or a post is deleted.
I think maybe you're collecting one flow, and then setting a user, which changes the flow in the property, but not any existing previous flow that's already being collected. I'm not sure how else to explain what's happening.
It may be error-prone to have a public Flow property that is reliant on some other function that takes a parameter. You could return a Flow directly from the function so there is no ambiguity about the behavior. The fragment requests a Flow for a specific User and immediately gets it.
distinctUntilChanged() will prevent it from emitting an unchanged list that results from other changes in the repo.
fun getPostsForUser(user: User) Flow<List<Post>> =
database.getAllPostsForUser(user).distinctUntilChanged()
If you do want to use your pattern, I think it you could do it like this. This allows there to only ever be one Flow so it's safe to start collecting it early. Changing the user will change which values it's publishing. Although this is more complicated than above, it has the advantage of not requiring a data refresh on screen rotations and other config changes.
private val mutableUserPosts = MutableStateFlow<List<Post>>(emptyList())
val userPosts: Flow<List<Post>> = mutableUserPosts
private var userPostsJob: Job? = null
var user: User? = null
set(value) {
field = value
userPostsJob?.cancel()
value ?: return
userPostsJob = database.getAllPostsForUser(value)
.onEach { mutableUserPosts.emit(it) }
.launchIn(viewModelScope)
}
Or as Joffrey suggests, it's simpler with a User Flow if you don't mind using the unstable API function flatMapLatest. The user property here could be dropped if you don't mind exposing a public mutable flow where the value should be set externally. Or if you use this pattern repeatedly, you could make operator extension functions for StateFlow/MutableStateFlow to use it as a property delegate.
private val userFlow = MutableStateFlow<User?>(null)
var user: User?
get() = userFlow.value
set(value) {
userFlow.value = value
}
val userPosts: Flow<List<Post>> = userFlow.flatMapLatest { user ->
if (user == null) emptyFlow() else database.getAllPostsForUser(user)
}
This might be helpful for someone...
When you are collecting a flow downstream and in some situations, you need an updated flow or a completely different flow than before, Use flatmapLatest() function.
Example:
There is a flow of search results in an app. When a user enters a search text flow automatically should update with the latest data according to searched chars.
Place the query text in a StateFlow as,
private var _userSearchText = MutableStateFlow("")
val userSearchText = _userSearchText.asStateFlow()
as the user enters text just update the Stateflow as,
fun setUserNameSearchText(data: String) {
_userSearchText.value = data
}
and your flow collecting like,
val response = userSearchText.flatMapLatest {
searchRepository.getResults(it)
.cachedIn(viewModelScope)
}
Stateflow will trigger the API calls automatically. It will notify all the observers on them.
PS: Open to improving...

android.content.ActivityNotFoundException: No Activity found to handle Intent { act=android.intent.action.VIEW dat=zhanysch#gmail.com }

I new in android , i'm attempting open gmail app through Intent , i have clicked on menu bottom for open E-address in gmail , and i have got problem top sentence .
this is my code
private fun menuClicks() {
binding?.toolbar?.toolbar?.setOnMenuItemClickListener {
when(it.itemId){
R.id.conact ->{
val client = Intent(Intent.ACTION_VIEW , Uri.parse("zhanysch#gmail.com"))
startActivity(client)
return#setOnMenuItemClickListener true
}
R.id.FAQ ->{
findNavController().navigate(R.id.action_mainFragment_to_faqFragment)
return#setOnMenuItemClickListener true
}
R.id.terms ->{
findNavController().navigate(R.id.action_mainFragment_to_termsConditionsFragment)
return#setOnMenuItemClickListener true
}
R.id.Privacy -> {
findNavController().navigate(R.id.action_mainFragment_to_privacyFragment)
return#setOnMenuItemClickListener true
}
else -> super.onOptionsItemSelected(it)
}
}
}
what the problem can any one help me
Your intent for opening Gmail is wrong. I recommend looking into "Send email intent" in Android.
But for the simple case, change this:
val client = Intent(Intent.ACTION_VIEW , Uri.parse("zhanysch#gmail.com"))
into this:
val client = Intent(Intent.ACTION_SENDTO, Uri.parse("mailto:zhanysch#gmail.com"))
For a better, more generic solution, I recommend checking the official docs.

Getting large profile picture from Facebook Graph-API

I am building a Kotlin app and am using FirebaseAuth for login. I activated Facebook login via Firebase and it works fine. My problem is that the created Firebase user's photoUrl: (https://graph.facebook.com/<UserId>/picture) is pointing to a tiny version of it's Facebook profile picture, instead of a normal sized one.
I found this solution: https://stackoverflow.com/a/52099896/13674106. The Google part of this answer worked perfectly for me, but in the Facebook case I am now getting a default avatar image (in the right resolution). My code looks like this:
fun handleSignInSucceeded(dataIntent: Intent?) {
val response = IdpResponse.fromResultIntent(dataIntent)
// For new created users, check if a photo is available from the auth provider
if (response?.isNewUser == true) {
// All users have a default FirebaseAuth provider data - we want to check which is the
// other one
currentUser.value?.providerData
?.find { it.providerId != FirebaseAuthProvider.PROVIDER_ID }?.apply {
val photoUrl = when (providerId) {
GoogleAuthProvider.PROVIDER_ID ->
photoUrl.toString().replace("s96-c", "s400-c").toUri()
FacebookAuthProvider.PROVIDER_ID ->
photoUrl.toString().plus("?type=large").toUri()
else -> null
}
photoUrl?.let { updatePhotoUri(it, false) }
}
}
}
fun updatePhotoUri(photoUrl: Uri, uploadToStorage: Boolean = true): Task<Void>? {
val profileUpdates = UserProfileChangeRequest.Builder()
return if (uploadToStorage) ...
else updateProfile(profileUpdates.setPhotoUri(photoUrl).build())
}
private fun updateProfile(profileUpdates: UserProfileChangeRequest): Task<Void>? {
return currentUser.value?.updateProfile(profileUpdates)
?.addOnCompleteListener {
if (it.isSuccessful) {
Log.d(TAG, "User profile updated.")
_currentUser.value = firebaseAuth.currentUser
}
}
}
Where _currentUser is a private MutableLiveData<FirebaseUser?> shadowed by a public LiveData<FirebaseUser?> called user which is binded to my ImageView on the fragment:
<ImageView
android:id="#+id/image_profile_picture"
...
app:userPhotoSrc="#{profileViewModel.user}" />
Where userPhotoSrc is implemented by the following BindingAdapter using Glide:
#BindingAdapter("userPhotoSrc")
fun ImageView.setUserPhotoSrc(user: FirebaseUser?) {
Glide.with(context)
.load(user?.photoUrl)
.circleCrop()
.fallback(R.drawable.ic_profile_black_24)
.into(this)
}
I checked in debug and the value of user?.photoUrl at that point is https://graph.facebook.com/<UserId>/picture?type=large as expected.
I found this thread: Retrieving Default Image All Url Profile Picture from Facebook Graph API, Which shows a problem similar to mine, but according to the answers there, my code should work by now. I also tried to use the height parameter instead of type, like stated in this answer: https://stackoverflow.com/a/50710161/13674106, But I got the same result.
Please help me solve it,
Omer

Spring Shell - capturing user input in middle of executing ShellMethod

Is the a way to capture user input in middle of executing #ShellMethod. Basically stoping executing of the method to ask for the user input and carrying on after capturing it.
There is possible solution here: https://stackoverflow.com/a/50954716, authored by ZachOfAllTrades
It works only when your app is SpringBoot-based, so you'll have access to the LineReader object, configured by SpringBoot.
#Autowired
LineReader reader;
public String ask(String question) {
return this.reader.readLine("\n" + question + " > ");
}
#ShellMethod(key = { "setService", "select" }, value = "Choose a Speech to Text Service")
public void setService() {
boolean success = false;
do {
String question = "Please select a service.";
// Get Input
String input = this.ask(question);
// Input handling
/*
* do something with input variable
*/
success = true;
}
} while (!success);
}
I didn't try it myself, though.
Use Spring Shell UI Components, now that we're in the future.
"Starting from 2.1.x there is a new component model which provides easier way to create higher level user interaction for usual use cases like asking input in a various forms. These usually are just plain text input or choosing something from a list."
#ShellComponent
public class ComponentCommands extends AbstractShellComponent {
#ShellMethod(key = "component string", value = "String input", group = "Components")
public String stringInput(boolean mask) {
StringInput component = new StringInput(getTerminal(), "Enter value", "myvalue");
component.setResourceLoader(getResourceLoader());
component.setTemplateExecutor(getTemplateExecutor());
if (mask) {
component.setMaskCharater('*');
}
StringInputContext context = component.run(StringInputContext.empty());
return "Got value " + context.getResultValue();
}
}
https://docs.spring.io/spring-shell/docs/2.1.0-SNAPSHOT/site/reference/htmlsingle/#_build_in_components
You should be able to interact directly with System.in although it is not really what Spring Shell is about: commands should be self contained.

ETrade API unattended authentication

Background
The ETrade authentication system has me creating a RequestToken, then executing an Authorization URL, which opens an ETrade page.
The user logs in to authorize the activity on their account.
They receive a pin, which they enter in my app.
I call ExchangeRequestTokenForAccessToken with the RequestToken and the Pin.
Then we are off and running.
Question
The problem is I'm creating a service that runs continuously in the background. There won't be any user to log in. Conversely, I won't be making any trades. Just crunching numbers, looking for stocks that meet certain criteria.
I can't figure how to get this to work unattended.
Thanks, Brad.
Previously, I have used a series of WebRequests and manually added headers to simulate the authorization pages. This worked until about a year ago when ETrade complicated their headers with something that appears to be tracking information. I now use http://watin.org/ to log in, and to strip the Auth Code.
Sloppy code looks like this:
using WatiN.Core; // IE Automation
...
// verify current thread in STA.
Settings.Instance.MakeNewIeInstanceVisible = false;
var ieStaticInstanceHelper = new IEStaticInstanceHelper();
Settings.AutoStartDialogWatcher = false;
using (ieStaticInstanceHelper.IE = new IE())
{
string authCode = "";
ieStaticInstanceHelper.IE.GoTo(GetAuthorizationLink());
if (ieStaticInstanceHelper.IE.ContainsText("Scheduled System Maintenance"))
{
throw new ApplicationException("eTrade down for maintenance.");
}
TextField user = ieStaticInstanceHelper.IE.TextField(Find.ByName("USER"));
TextField pass = ieStaticInstanceHelper.IE.TextField(Find.ById("txtPassword"));
TextField pass2 = ieStaticInstanceHelper.IE.TextField(Find.ByName("PASSWORD"));
Button btn = ieStaticInstanceHelper.IE.Button(Find.ByClass("log-on-btn"));
Button btnAccept = ieStaticInstanceHelper.IE.Button(Find.ByValue("Accept"));
TextField authCodeBox = ieStaticInstanceHelper.IE.TextField(Find.First());
if (user != null && pass != null && btn != null &&
user.Exists && pass2.Exists && btn.Exists)
{
user.Value = username;
pass2.Value = password;
btn.Click();
}
btnAccept.WaitUntilExists(30);
btnAccept.Click();
authCodeBox.WaitUntilExists(30);
authCode = authCodeBox.Value;
SavePin(authCode);
}
Current version of Brad Melton's code.
WatiN has changed and no longer contains the IE.AttachToIE function.
So, IEStaticInstanceHelper is now called StaticBrowserInstanceHelper, but that code is hard to find, so I've included it here.
class StaticBrowserInstanceHelper<T> where T : Browser {
private Browser _browser;
private int _browserThread;
private string _browserHwnd;
public Browser Browser {
get {
int currentThreadId = GetCurrentThreadId();
if (currentThreadId != _browserThread) {
_browser = Browser.AttachTo<T>(Find.By("hwnd", _browserHwnd));
_browserThread = currentThreadId;
}
return _browser;
}
set {
_browser = value;
_browserHwnd = _browser.hWnd.ToString();
_browserThread = GetCurrentThreadId();
}
}
private int GetCurrentThreadId() {
return Thread.CurrentThread.GetHashCode();
}
}
ETrade's login pages have changed as well. They have several. All the login pages I checked consistently had a USER field and a PASSWORD field, but the login buttons had various names that look fragile. So if this doesn't work, that's the first thing I'd check.
Second, if I go directly to the auth page, it prompts to log in, but then it frequently doesn't take you to the auth page.
I got more consistent results by going to the home page to log in, then going to the auth page.
static public string GetPin(string username, string password, string logonLink, string authLink) {
// Settings.Instance.MakeNewIeInstanceVisible = false;
var StaticInstanceHelper = new StaticBrowserInstanceHelper<IE>();
Settings.AutoStartDialogWatcher = false;
// This code doesn't always handle it well when IE is already running, but it won't be in my case. You may need to attach to existing, depending on your context.
using (StaticInstanceHelper.Browser = new IE(logonLink)) {
string authCode = "";
// Browser reference was failing because IE hadn't started up yet.
// I'm in the background, so I don't care how long it takes.
// You may want to do a WaitFor to make it snappier.
Thread.Sleep(5000);
if (StaticInstanceHelper.Browser.ContainsText("Scheduled System Maintenance")) {
throw new ApplicationException("eTrade down for maintenance.");
}
TextField user = StaticInstanceHelper.Browser.TextField(Find.ByName("USER"));
TextField pass2 = StaticInstanceHelper.Browser.TextField(Find.ByName("PASSWORD"));
// Class names of the Logon and Logoff buttons vary by page, so I find by text. Seems likely to be more stable.
Button btnLogOn = StaticInstanceHelper.Browser.Button(Find.ByText("Log On"));
Element btnLogOff = StaticInstanceHelper.Browser.Element(Find.ByText("Log Off"));
Button btnAccept = StaticInstanceHelper.Browser.Button(Find.ByValue("Accept"));
TextField authCodeBox = StaticInstanceHelper.Browser.TextField(Find.First());
if (user != null && btnLogOn != null &&
user.Exists && pass2.Exists && btnLogOn.Exists) {
user.Value = username;
pass2.Value = password;
btnLogOn.Click();
}
Thread.Sleep(1000);
if (StaticInstanceHelper.Browser.ContainsText("Scheduled System Maintenance")) {
Element btnContinue = StaticInstanceHelper.Browser.Element(Find.ByName("continueButton"));
if (btnContinue.Exists)
btnContinue.Click();
}
btnLogOff.WaitUntilExists(30);
// Here we go, finally.
StaticInstanceHelper.Browser.GoTo(authLink);
btnAccept.WaitUntilExists(30);
btnAccept.Click();
authCodeBox.WaitUntilExists(30);
authCode = authCodeBox.Value;
StaticInstanceHelper.Browser.Close();
return authCode;
}
}
Being able to automate it like this means that I no longer care about how long the token is valid. Thanks BradM!
This was amazingly helpful. I used your code plus what was posted here to automate this (because tokens expire daily): E*Trade API frequently returns HTTP 401 Unauthorized when fetching an access token but not always
I made two edits:
Changed the authorize URL to what was posted here: https://seansoper.com/blog/connecting_etrade.html
For the log on button, changed it to search by ID: Button btnLogOn = StaticInstanceHelper.Browser.Button(Find.ById("logon_button"));
I ran into issues with Watin and setting up the Apartmentstate. So did this:
static void Main(string[] args)
{
System.Threading.Thread th = new Thread(new ThreadStart(TestAuth));
th.SetApartmentState(ApartmentState.STA);
th.Start();
th.Join();
}
Then put the code in the TestAuth method.