How to get response data from a async khttp request in Kotlin - android

I am able to store the access token but now for example i wish to make a Toast message that says "user loggen in as [username]" but i get the error that this task is on a worker thread so i need to to this in the main thread. But i do not know how to get the data from my async request into the main thread and eventually do an explicit Intent to a new Activity while also passing the user's info and access token.
I already have a web application for the project that I'm working on and now i wish to make a companion app that will use my web applications REST api to get and post data. The web application is a Laravel application that uses Passport for handling logins. I'm new to android development but I was able to post my login data from my android app to the Laravel application and get a valid response meaning that the email and password match so I get the user's info and a access token.
inputSubmit.setOnClickListener { _ ->
val email = inputEmail.text.toString()
val password = inputPassword.text.toString()
val address = prefrence.getValueString("API_ADDRESS") + "/login"
val payload = mapOf("email" to email, "password" to password)
var response : String
var loginParsed : Login?
khttp.async.post(address, headers=mapOf("Accept" to "application/json"), data = payload, timeout = 10.0, onResponse = {
println("Status code: $statusCode")
//println("Response text: $text")
response = text
loginParsed = Klaxon().parse<Login>(response) //Works
prefrence.save("API_KEY", loginParsed?.access_token.toString()) //Works
println("Logged in user: " + loginParsed?.user_name.toString()) //Works
Toast.makeText(applicationContext, "Korisnik: " + loginParsed?.user_name.toString(), Toast.LENGTH_LONG).show()
}, onError = {
println("ERROR MESSAGE: $message") //Works
})
//println("Username: " + loginParsed) // ! Variable 'loginParsed' must be intialized
}
What i want is to be able to access the data outside of the khttp.async.post and pass in onto the new Activity and make a Toast message. When i try to make the Toast message i get the following error:
I/System.out: ERROR MESSAGE: Can't create handler inside thread that has not called Looper.prepare()

Related

Dotmim.Sync null reference exception in web proxy post method

I am using Dotmim.sync framework. I am trying to sync an mssql database with my xamarin android app's sqlite database. So I implemented the web proxy to reach the database from the android app.
The proxy starts fine, but then when I call the sync from the android app the Post method gives a null reference error, but I cannot find what is null.
In the ASP.NET Core web app's Startup file:
public void ConfigureServices(IServiceCollection services)
{
services.AddControllersWithViews();
// [Required]: To be able to handle multiple sessions
services.AddMemoryCache();
// [Required]: Get a connection string to your server data source
var connectionString = #"[my connection string]";
// [Required]: Tables list involved in the sync process
var tables = new string[] { "dbo.Album", "dbo.Artist", "dbo.Customer", "dbo.Invoice", "dbo.InvoiceItem", "dbo.Track" };
// [Required]: Add a SqlSyncProvider acting as the server hub.
services.AddSyncServer<SqlSyncChangeTrackingProvider>(connectionString, tables);
}
The SyncController:
[ApiController]
[Route("api/[controller]")]
public class SyncController : ControllerBase
{
private WebServerManager manager;
public SyncController(WebServerManager man) => this.manager = man;
[HttpPost]
public async Task Post()
{
await manager.HandleRequestAsync(this.HttpContext);
} //----> the Null error comes
[HttpGet]
public async Task Get() => await manager.HandleRequestAsync(this.HttpContext);
}
In the android app the sync function that is called:
public async Task SyncDatabase(string connString, Context context)
{
var handler = HttpClientHandlerService.GetInsecureHandler();
HttpClient httpClient = new HttpClient(handler);
httpClient.DefaultRequestHeaders.Host = $"localhost:44372";
var serverOrchestrator = new WebClientOrchestrator("https://10.0.2.2:44372/api/sync", client: httpClient);
// Second provider is using plain sqlite provider
var clientProvider = new SqliteSyncProvider(connString);
var agent = new SyncAgent(clientProvider, serverOrchestrator);
try
{
var result = await agent.SynchronizeAsync(); //---> error comes when this line is called
var output = result.ToString();
output = output.Replace("\n", " ").Replace("\t", " ").Replace("\r", " ");
Toast.MakeText(context, output, ToastLength.Long).Show();
}
catch (Exception e)
{
Toast.MakeText(context, e.Message, ToastLength.Long).Show();
}
}
Let me know what further information should I supply to solve this.
EDIT:
Calling from postman it gives this error: {"tn":"HttpHeaderMissingExceptiopn","m":"Header dotmim-sync-scope-name is missing."}
EDIT2:
Server orchestrator on the client side:
On the server side:
The details of the exception:
SOLUTION:
I tried the sync with a different database and tables, and that worked, so it was clear that dotmim has some problem with the tables I was using. So after lot of thinking I tried with a different schema name instead of dbo, since the other database that worked had something else. And it turns out the sync has some problem if the schema name is dbo, something gets mixed probably when it tries to create its own new tables. So use something different from dbo for schema.

Login to ASP.NET Core application doesn't work on Android real device with .NET HttpClient in Xamarin.Forms app

Got a Xamarin.Forms app which requests data via an API implemented in ASP.NET Core. Before retrieving data the app has to login to the ASP.Net Core application which uses the identity service. The code works fine in iOS Simulator, Android 9 Emulator and on a real iPhone but not on a real Android 9 device! Network connection is working well, because I can access the web application via browser.
I'm using the System.Net.Http.HttpClient in shared project. The identity service requires sending an antiforgery key (By the way: Is there a better way for handling authorization only for the web api? Seems inconvenient handling the antiforgery key). Here's the code which handles the login process:
private async Task LoginAsync(string username, string password)
{
//Get login page with AntiForgeryToken
var pageWithToken = await _httpClient.GetStringAsync(BASE_URL);
//Extract AntiForgeryToken
string verificationToken = GetAntiForgeryToken(pageWithToken);
//Post parameter
var keyValues = new List<KeyValuePair<string, string>>
{
new KeyValuePair<string, string>("Input.Email", username),
new KeyValuePair<string, string>("Input.Password", password),
new KeyValuePair<string, string>("__RequestVerificationToken", verificationToken),
new KeyValuePair<string, string>("Input.RememberMe", "false")
};
var request = new HttpRequestMessage(HttpMethod.Post, "Identity/Account/Login")
{
Content = new FormUrlEncodedContent(keyValues)
};
//login with Post parameter
var response = await _httpClient.SendAsync(request);
//read response - can see here that login failed
var content = await response.Content.ReadAsStringAsync();
}
This is what I see on the server side after sending the Post request with login data:
Microsoft.AspNetCore.Hosting.Internal.WebHost:Information: Request starting HTTP/1.1 POST http://10.219.200.147:33224/Identity/Account/Login application/x-www-form-urlencoded 253
Microsoft.AspNetCore.Routing.EndpointMiddleware:Information: Executing endpoint 'Page: /Account/Login'
Microsoft.AspNetCore.Mvc.RazorPages.Internal.PageActionInvoker:Information: Route matched with {page = "/Account/Login", area = "Identity", action = "", controller = ""}. Executing page /Account/Login
Microsoft.AspNetCore.Mvc.RazorPages.Internal.PageActionInvoker:Information: Executing handler method C4S.WebApp.Areas.Identity.Pages.Account.LoginModel.OnPostAsync with arguments () - ModelState is Valid
Microsoft.EntityFrameworkCore.Infrastructure:Information: Entity Framework Core 2.2.6-servicing-10079 initialized 'ApplicationDbContext' using provider 'Microsoft.EntityFrameworkCore.SqlServer' with options: None
Der Thread 0x28d4 hat mit Code 0 (0x0) geendet.
Microsoft.EntityFrameworkCore.Database.Command:Information: Executed DbCommand (9ms) [Parameters=[#__normalizedUserName_0='?' (Size = 256)], CommandType='Text', CommandTimeout='30']
SELECT TOP(1) [u].[Id], [u].[AccessFailedCount], [u].[ConcurrencyStamp], [u].[Email], [u].[EmailConfirmed], [u].[LockoutEnabled], [u].[LockoutEnd], [u].[NormalizedEmail], [u].[NormalizedUserName], [u].[PasswordHash], [u].[PhoneNumber], [u].[PhoneNumberConfirmed], [u].[SecurityStamp], [u].[TwoFactorEnabled], [u].[UserName]
FROM [AspNetUsers] AS [u]
WHERE [u].[NormalizedUserName] = #__normalizedUserName_0
Microsoft.EntityFrameworkCore.Database.Command:Information: Executed DbCommand (2ms) [Parameters=[#__user_Id_0='?' (Size = 450)], CommandType='Text', CommandTimeout='30']
SELECT [uc].[Id], [uc].[ClaimType], [uc].[ClaimValue], [uc].[UserId]
FROM [AspNetUserClaims] AS [uc]
WHERE [uc].[UserId] = #__user_Id_0
Microsoft.AspNetCore.Authentication.Cookies.CookieAuthenticationHandler:Information: AuthenticationScheme: Identity.Application signed in.
C4S.WebApp.Areas.Identity.Pages.Account.LoginModel:Information: User logged in.
Microsoft.AspNetCore.Mvc.RazorPages.Internal.PageActionInvoker:Information: Executed handler method OnPostAsync, returned result Microsoft.AspNetCore.Mvc.LocalRedirectResult.
Microsoft.AspNetCore.Mvc.Infrastructure.LocalRedirectResultExecutor:Information: Executing LocalRedirectResult, redirecting to /.
Microsoft.AspNetCore.Mvc.RazorPages.Internal.PageActionInvoker:Information: Executed page /Account/Login in 112.8493ms
Microsoft.AspNetCore.Routing.EndpointMiddleware:Information: Executed endpoint 'Page: /Account/Login'
Microsoft.AspNetCore.Hosting.Internal.WebHost:Information: Request finished in 131.0183ms 302
Microsoft.AspNetCore.Hosting.Internal.WebHost:Information: Request starting HTTP/1.1 GET http://10.219.200.147:33224/
Microsoft.AspNetCore.Routing.EndpointMiddleware:Information: Executing endpoint 'Page: /Kontakte/Index'
Microsoft.AspNetCore.Mvc.RazorPages.Internal.PageActionInvoker:Information: Route matched with {page = "/Kontakte/Index", action = "", controller = "", area = ""}. Executing page /Kontakte/Index
Microsoft.AspNetCore.Authorization.DefaultAuthorizationService:Information: Authorization failed.
Microsoft.AspNetCore.Mvc.RazorPages.Internal.PageActionInvoker:Information: Authorization failed for the request at filter 'Microsoft.AspNetCore.Mvc.Authorization.AuthorizeFilter'.
Microsoft.AspNetCore.Mvc.ChallengeResult:Information: Executing ChallengeResult with authentication schemes ().
Microsoft.AspNetCore.Authentication.Cookies.CookieAuthenticationHandler:Information: AuthenticationScheme: Identity.Application was challenged.
Microsoft.AspNetCore.Mvc.RazorPages.Internal.PageActionInvoker:Information: Executed page /Kontakte/Index in 6.2539ms
Microsoft.AspNetCore.Routing.EndpointMiddleware:Information: Executed endpoint 'Page: /Kontakte/Index'
Microsoft.AspNetCore.Hosting.Internal.WebHost:Information: Request finished in 14.9729ms 302
Microsoft.AspNetCore.Hosting.Internal.WebHost:Information: Request starting HTTP/1.1 GET http://10.219.200.147:33224/Identity/Account/Login?ReturnUrl=%2F
Microsoft.AspNetCore.Routing.EndpointMiddleware:Information: Executing endpoint 'Page: /Account/Login'
Microsoft.AspNetCore.Mvc.RazorPages.Internal.PageActionInvoker:Information: Route matched with {page = "/Account/Login", area = "Identity", action = "", controller = ""}. Executing page /Account/Login
Microsoft.AspNetCore.Mvc.RazorPages.Internal.PageActionInvoker:Information: Executing handler method C4S.WebApp.Areas.Identity.Pages.Account.LoginModel.OnGetAsync with arguments (/) - ModelState is Valid
Microsoft.AspNetCore.Authentication.Cookies.CookieAuthenticationHandler:Information: AuthenticationScheme: Identity.External signed out.
Microsoft.AspNetCore.Mvc.RazorPages.Internal.PageActionInvoker:Information: Executed handler method OnGetAsync, returned result .
Microsoft.AspNetCore.Mvc.RazorPages.Internal.PageActionInvoker:Information: Executing an implicit handler method - ModelState is Valid
Microsoft.AspNetCore.Mvc.RazorPages.Internal.PageActionInvoker:Information: Executed an implicit handler method, returned result Microsoft.AspNetCore.Mvc.RazorPages.PageResult.
Microsoft.AspNetCore.Mvc.RazorPages.Internal.PageActionInvoker:Information: Executed page /Account/Login in 11.6849ms
Microsoft.AspNetCore.Routing.EndpointMiddleware:Information: Executed endpoint 'Page: /Account/Login'
Microsoft.AspNetCore.Hosting.Internal.WebHost:Information: Request finished in 20.9535ms 200 text/html; charset=utf-8
I'm wondering why the log first says "User logged in." and later "Authorization failed.". Could there maybe a cookie problem? Just to say, I'm working in a test environment without encryption -> only http.
Thanks for your help!
Finally found a solution! I assumed there's something wrong with Identities cookies because login is successful but further requests seems to forget about that. Identity stores a cookie on clientside for authentication after successfull login and sends it to the server with every request. There's some extra code needed to access HttpClient's cookies while initilization:
HttpClientHandler _httpClientHandler = new HttpClientHandler();
HttpClient _httpClient = new HttpClient(_httpClientHandler) { BaseAddress = new Uri(BASE_URL), Timeout = new TimeSpan(0, 0, 30) };
Adding the HttpClienthandler to the HttpClient did the trick! I'm not sure why that's needed for Android but not for iOS. Maybe someone has a guess?

Use Firebase and Node.js for authenticating user on Android

I'm totally lost as to how to do this. I want to be able to authenticate a user with their username and password only--so I have to use a customAuth from Firebase.
I created a server (node.js) that handles the generation of tokens (runs on Heroku):
var express = require('express')
var Firebase = require('firebase')
var app = express()
app.set('port', (process.env.PORT || 5000))
app.use(express.static(__dirname + '/public'))
app.listen(app.get('port'), function() {
console.log("Node app is running at localhost:" + app.get('port'))
})
var SECRET = "numbers would be here";
var tokenGenerator = new FirebaseTokenGenerator(SECRET);
var AUTH_TOKEN = tokenGenerator.createToken({
uid: "arbitrary",
data: "blahblahblah"});
console.log(AUTH_TOKEN);
var ref = new Firebase("null");
ref.authWithCustomToken(AUTH_TOKEN, function(error, authData) {
if (error) {
console.log("Login Failed!", error);
} else {
console.log("Login Succeeded!", authData);
}
});
Now I have an Android app in which I want to authenticate a user. If I have something like,
Firebase mRef = new Firebase("myFirebaseUrl");
mRef.authWithCustomToken(String token, AuthResultHandler handler); //issue
I don't know how to get the token. Furthermore, I'm not sure I understand how it matters if the token is always the same.
You'll need to come up with a secure way to communicate the username and password from your Android client to the node.js server and to subsequently communicate the resulting token (or any error codes) back from the node.js server to the client.
While this is definitely possible (it's pretty much how Firebase email+password authentication works), it is definitely too broad a topic to cover in a StackOverflow answer. It's a project, rather than a question.
What you can consider is using Firebase email+password auth and then stubbing out the email domain. So if a user signs up with username Nxt3 and password, you simply append a dummy domain to the username and register them as Nxt3#dummydomain.com.

How can i send push notification on any item update event in parse.com

I am using parse for my application
I have one fragment on user which have to write article and save on parse but with not approve
when admin approve that user filed I want to send push notification automatically to that user with particular article is approved message
How can I implement this things..?
Parse.Cloud.afterSave("Data", function(request) {var dirtyKeys = request.object.dirtyKeys();for (var i = 0; i < dirtyKeys.length; ++i) {
var dirtyKey = dirtyKeys[i];
if (dirtyKey === "name") {
//Get value from Data Object
var username = request.object.get("name");
//Set push query
var pushQuery = new Parse.Query(Parse.Installation);
pushQuery.equalTo("name",username);
//Send Push message
Parse.Push.send({
where: pushQuery,
data: {
alert: "Name Updated",
sound: "default"
}
},{
success: function(){
response.success('true');
},
error: function (error) {
response.error(error);
}
});
return; } } response.success();});
If this is a matter of just hitting "approve", I would create a cloud code function for "Approve article" that is called by the admin tapping an approve button. This function sets the approve status and then calls a function (still in cloud code) for sending the push message to the user.
More on cloud code functions: https://parse.com/docs/cloudcode/guide#cloud-code-cloud-functionshttps://parse.com/docs/cloudcode/guide#cloud-code-cloud-functions
Alternatively the approve button can change status and save the document, and then an afterSave() function in cloud code can handle the push. This is less clear, though, since the afterSave() function will always be called when the record is saved, and it would need to check for status and only send push if the article has been approved.

appcelerator titanium - Ti.App.Properties not working on android

I am creating an (iphone/android) mobile app using appcelerator titanium. I have a problem using Ti.App.Properties,
I want to save the user's login data (username and password), I used Ti.App.Properties's getList and setList methods to get and set username and password at app startup. It is working fine on iPhone, but on android the data (username and password) are not retrieved at app startup.
here is the code that is executed at app startup :
var userDataArray=[{title:'name',value:''},
{title:'password',value:''}];
if(Ti.App.Properties.hasProperty("userDataArray"))
{
userDataArray = Ti.App.Properties.getList("userDataArray");
}
else
{
Ti.App.Properties.setList("userDataArray",userDataArray);
}
if((Ti.App.Properties.getList("userDataArray")[0].value.length==0)||(Ti.App.Properties.getList("userDataArray")[1].value.length==0))//check if name, password have no values.. on android, this is always the case, which is not correct
{
//go to login page
}
else if((Ti.App.Properties.getList("userDataArray")[0].value.length>0)&&(Ti.App.Properties.getList("userDataArray")[1].value.length>0))//if both username and password exist
{
//start
}
Thank you
i think your overall approach is flawed, you dont need an array just an map
// save the values as a string..
Ti.App.Properties.setString({"username":"myname", "password":"mypassword"}, "CREDENTIALS");
// retrieve the values as a string, but parse it back into an object
var credObject = JSON.parse(Ti.App.Properties.getString("CREDENTIALS"));
// dump the output
Ti.API.debug("Username "+ credObject.username);
Ti.API.debug("Password "+ credObject.password);
two remarks :
arguments for .setString() is opposite, ie Name then Value
Value must be a string, so you have to stringify() it or enter it as a string
I know this is old, but it's still relevant today as there's not a huge amount of help with Titanium. I handle this in two parts.
Part 1) After the user's credentials have been authenticated...
var username = "some username";
var password = "some password";
// Build the object and then convert it to a json string.
oCredentials = new Object();
oCredentials.username = username;
oCredentials.password = password;
var stringCredentials = JSON.stringify(oCredentials);
// Save the credentials
Ti.App.Properties.setString("Credentials", stringCredentials);
Part 2) Before you prompt the user with the login window/popup/whatever...
// Look for credentials
(function() {
var storedCredentials = Ti.App.Properties.getString("Credentials");
if (storedCredentials){
var oJson = JSON.parse(storedCredentials);
// Call your authentication function
// For example, autoAuthenticate(oJson.username, oJson.password);
} else {
// kick the user out to your login window
// For example, $.loginWindow.open();
}
})();

Categories

Resources