I'm very new to Kotlin\Android so this is probably an obvious mistake but I am stuck and need some help.
What I am doing is getting a user to enter their name, taking that name and check against a mySQL DB (this works fine), and then depending on their authorization level return a 0 or 1, this is the part I'm having issues with.
I have tried using return, creating a global class, adding to a mutable list and setting it but nothing works.
In Main I have this.
var useName:String = editText.text.toString()
verifiedID = verifyUser(useName) //Call the function here
// verifiedID = Global.validLogin //Was used to test Global class
ET_HelloW.setText("$useName ID $verifiedID")
In the verify user class
var ucounter = 0
fun verifyUser(userName: String) :Int {
//getting the record values
val userName = userName
var uname: String = ""
var uauth: String = ""
//creating volley string request
// Checking to see if the Username entered exists in the DBEd
val stringRequest = object : StringRequest(Request.Method.POST, PHPPost.URL_CHECK_LOGIN,
Response.Listener<String> { response ->
try {
val obj = JSONObject(response)
if (!obj.getBoolean("error")){
val array = obj.getJSONArray("credens")
for (i in 0..array.length() - 1) {
val objectArtist = array.getJSONObject(i)
uname = objectUser.getString("name")
uauth = objectUser.getString("auth_level")
}
if (uauth.toInt()<2)
{
Global.validLogin = 0 //This sets
ucounter = 0 //This sets
}else{
Global.validLogin = 1 //This sets when checking code but is not picked up when called in Main
ucounter = 1 //Debugger shows that I do reach here and that the var is set, but resets to 0 at the point of returning.
}
}else{
}
} catch (e: JSONException) {
e.printStackTrace()
}
},
object : Response.ErrorListener {
override fun onErrorResponse(volleyError: VolleyError) {
}
}) {
#Throws(AuthFailureError::class)
override fun getParams(): Map<String, String> {
val params = HashMap<String, String>()
params.put("name", userName)
return params
}
}
//adding request to queue
VolleySingleton.instance?.addToRequestQueue(stringRequest)
return ucounter //always returns zero
// return 1 // If set manually this will be returned.
}
I'm pretty sure that the value is being reset to 0 before it gets to the return at the end but even when I set it as a global it did not work.
Again am sure its a rookie mistake but would appreciate some help.
Related
This is my first post on StackOverflow, so please don't kill me for my poor formatting.
I'm trying to make a Work Tracker App, which logs your time of arrival and time of leave in a MySQL database when you press the button in the app.
I want the app to open the correct (is working / is not working) screen when you launch the app, and I kinda managed to make it work with shared preferences, but I figured it would be more reliable if it would request the status from the database.
The table holding the logs looks like this:
user_id | time_of_arrival | time_of_leave
if the user is still in work, there will be a row where he has time_of_arrival, but the time_of_leave field is NULL.
That's what I want to request here:
private fun checkWorking(
sharedPreferences: SharedPreferences,
localContext: Context
) : Boolean {
val userId = sharedPreferences.getString("userId", "").toString()
var isWorking = false
if (userId != "") {
val handler = Handler(Looper.getMainLooper())
handler.post {
val field = arrayOfNulls<String>(1)
field[0] = "user_id"
val data = arrayOfNulls<String>(1)
data[0] = userId
val putData = PutData(
Database().host + Database().databaseName + "checkWorking.php",
"POST",
field,
data
)
if (putData.startPut()) {
if (putData.onComplete()) {
val result = putData.result
if(result == "You are working") {
isWorking = true
}
}
}
}
}
return isWorking
}
here is the php part:
<?php
require "DataBase.php";
$db = new DataBase();
if ($db->dbConnect()) {
if($db->checkWorking("logs", $_POST['user_id'])) {
echo "Success";
} else echo "Failure";
}
?>
and
function checkWorking($table, $userId) {
$userId = $this->prepareData($userId);
$this->sql = "SELECT * FROM " . $table . " WHERE user_id = '" . $userId . "' AND time_of_leave IS NULL";
$result = mysqli_query($this->connect, $this->sql);
if(mysqli_num_rows($result) != 0) {
return true;
}
return false;
}
(The PHP part works correctly, I just wanted to give full insight about my problem)
My problem is that it always returns false, because I read somewhere that the return finishes faster than the handler.post changing the isWorking variable to true.
How can I fix this issue, I legitimately can't figure out anything else I could try.
Thanks in advance!
yes, the return statement is being called before the handler is done since it will be working on a different thread while the return is still on the main thread.
So, you can solve that by using an interface to return the callback whenever it has been received, first you create the interface as follows:
public interface CallbackListener<T> {
void onSuccess(T response);
}
then you have to modify you method to take this interface as a parameter
private fun checkWorking(
sharedPreferences: SharedPreferences,
localContext: Context,
callback: CallbackListener<Boolean>) {
val userId = sharedPreferences.getString("userId", "").toString()
var isWorking = false
if (userId != "") {
CoroutineScope(IO).launch { //running code on background thread
val field = arrayOfNulls<String>(1)
field[0] = "user_id"
val data = arrayOfNulls<String>(1)
data[0] = userId
val putData = PutData(
Database().host + Database().databaseName + "checkWorking.php",
"POST",
field,
data
)
if (putData.startPut()) {
if (putData.onComplete()) {
val result = putData.result
withContext(Main) {//returning to main thread
if (result == "You are working") {
callback.onSuccess(true)
} else
callback.onSuccess(false)
}
}
}
}
I used kotlin Coroutines here instead of handler, but it can be applied to both of them.
then you can call your new method as follows:
checkWorking(
sharedPreferences,
context,
object: CallbackListener<Boolean>{
override fun onSuccess(response: Boolean?) {
//insert your logic here
}
}
)
I have a response listener in my program and I got feedback about it like it is wongfully used.
val jsonObjectRequest = object : JsonObjectRequest(
Method.POST,
url,
sendOrder,
Response.Listener {
val response = it.getJSONObject("data")
val success = response.getBoolean("success")
val LAUNCH_SECOND_ACTIVITY = 1
if (success) {
val intent = Intent(this, PaymentActivity::class.java)
intent.putExtra("total_amount",totalAmount)
startActivityForResult(intent,LAUNCH_SECOND_ACTIVITY)
} else {
## -116,7 +118,7 ## class CartActivity : AppCompatActivity() {
cartProgressLayout.visibility = View.INVISIBLE
},
This is the feedback I got, what does it mean and how to change that ?
Always have handling for unexpected responses like different keys or
values or JSON structure or empty response
You need to introduce exception handling, so that if recevied JSON doesn't satisfy the given structure your app doens't crash
try{
val response = it.getJSONObject("data") // This line can throw exception, if not handled it can cause your app to crash
val success = response.getBoolean("success") // This can also throw exception
val LAUNCH_SECOND_ACTIVITY = 1
if (success) {
val intent = Intent(this, PaymentActivity::class.java)
intent.putExtra("total_amount",totalAmount)
startActivityForResult(intent,LAUNCH_SECOND_ACTIVITY)
}
}
catch(jsonException: JSONException){
// Json parsing failed, notify user if required
}
catch(e: Exception){
// Something else failed, notify user if required
}
I want to change the value of idCity of the given class in kotlin
class City_Id(private var cityName: String) {
var idCity : String? = null
val url = "https://www.metaweather.com/api/location/search/?query="
val jsonObjectRequest = JsonArrayRequest(Request.Method.GET, "$url$cityName", null,
{ response ->
val cityInfo : JSONObject = response.getJSONObject(0)
var cityID : String = cityInfo.getString("woeid")
idCity = cityID
makeLog(cityID)
makeLog("from json $idCity")
},
{ error ->
makeLog("$error")
}
)
fun jsonReturn(): JsonArrayRequest {
return this.jsonObjectRequest
}
private fun makeLog(s: String) {
Log.d("City_Id Activity" , s)
}
}
The variable "idCity" that I changed locally in "jsonObjectRequest" want to change it globally in class "City_Id". When calling "idCity" from different class
it still returning null after changing its value.
Thank you in advance
The problem is JsonArrayRequest is asynchronous and it won't set the global idCity variable as soon as City_id class instance is created. So if you access the idCity property from else where, you will get null till the request is complete. The better way to handle this is to pass an function as parameter to City_id class and then on response pass invoke the function with value.
Something of below sorts
class City_Id(private var cityName: String, block:(idCity: String) -> Unit) {
val url = "https://www.metaweather.com/api/location/search/?query="
val jsonObjectRequest = JsonArrayRequest(Request.Method.GET, "$url$cityName", null,
{ response ->
val cityInfo : JSONObject = response.getJSONObject(0)
var cityID : String = cityInfo.getString("woeid")
makeLog(cityID)
block.invoke(cityId)//pass to caller
},
{ error ->
makeLog("$error")
})
fun jsonReturn(): JsonArrayRequest {
return this.jsonObjectRequest
}
private fun makeLog(s: String) {
Log.d("City_Id Activity" , s)
}
}
And at caller side
City_id("name"){idCity ->
//here you will definitely get the city Id
}
I request your help in solving this pesky problem. I am trying to return the value of 'response' returned by the REST API call, to the calling program. I get null string. Here is the code for your reference. In this code, I want the value of the variable 'response' returned to the caller in onCreate, ie 'restApiResponse' variable.
Thank you very much.
-Vittal
PS: I am a newbie to Kotlin/Android programming.
class OpeningActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_opening)
var restApiResponse = getAPIResponse() // <<<< restApiResponse is empty string
val submitButton = findViewById<Button>(R.id.submitBtn)
submitButton.setOnClickListener() {
val intent = Intent(this, MainActivity::class.java)
startActivity(intent)
}
}
fun getAPIResponse() : String {
val myUrl = "YOUR REST API CALL URL"
var jsonObject = JSONObject();
val queue = Volley.newRequestQueue(this);
cpyResponse = StringBuilder();
val stringRequest = object: StringRequest(Request.Method.GET, myUrl,
Response.Listener<String> { response ->
Log.d("A", "VK: Response is: " + response.substring(0, 500))
cpyResponse.append(response.toString())
// .. cpyResponse now has the content of response.
},
Response.ErrorListener { })
{
override fun getHeaders(): MutableMap<String, String> {
val headers = HashMap<String, String>()
headers.put("X-API-KEY", "r0IS395C2D8ITjSKV05F610yPXsDQZjllmprr");
return headers
}
}
queue.add(stringRequest)
Log.d("A", "VK: Response is: " + response) // <<<<-- value of response is gone.. it is an empty string!!!!!! :(
return cpyResponse.toString() // <<< --- cpyResource is also empty string
}
I am new to Android and Kotlin and am currently working on a centralized API router class.
To achieve this I am using the Fuel Framework.
For the doAsync function, I use the Anko for Kotlin library.
To retrieve an authorization token from the API I currently use this method:
private fun Login(username: String, password: String, callback: (Map<Boolean, String>) -> Unit) {
"/auth/token.json".httpPost()
.header(mapOf("Content-Type" to "application/json"))
.body("""{"username":"$username", "password":"$password"}""", Charsets.UTF_8)
.response { request, response, result ->
request.headers.remove("Accept-Encoding")
when (result) {
is Result.Failure -> {
// val data = result.get()
val ex = result.getException()
val serverResponseJson = response.data.toString(Charsets.UTF_8)
var exceptionMessage = ex.message
val jelement = JsonParser().parse(serverResponseJson)
val jobject = jelement.asJsonObject
val serverResponseError = if (jobject.has("Error")) jobject.get("Error").asString else jobject.get("detail").asString
callback(mapOf(Pair(false, serverResponseError)))
}
is Result.Success -> {
val data = result.get()
val returnJson = data.toString(Charsets.UTF_8)
Log.println(Log.ASSERT, "RESULT_LOGIN", returnJson)
callback(mapOf(Pair(true, returnJson)))
}
}
}
}
I invoke this login method at
val btnLogin = findViewById<Button>(R.id.btn_login)
btnLogin.setOnClickListener { _ ->
doAsync {
val username = findViewById<EditText>(R.id.input_username_login)
val password = findViewById<EditText>(R.id.input_password_login)
Login(username.text.toString(), password.text.toString()) {
// Request was successful
if (it.containsKey(true)) {
// Parse return Json
// e.g. {"id":"36e8fac0-487a-11e8-ad4e-c471feb11e42","token":"d6897a230fd7739e601649bf5fd89ea4b93317f6","expiry":"2018-04-27T17:49:48.721278Z"}
val jelement = JsonParser().parse(it.getValue(true))
val jobject = jelement.asJsonObject
// save field for class-scope access
Constants.token = jobject.get("token").asString
Constants.id = jobject.get("id").asString
}
else{
Toast.makeText(this#LoginActivity, it.getValue(false), Toast.LENGTH_SHORT).show()
}
}
}[30, TimeUnit.SECONDS]
var test = Constants.id;
}
In a separate Constants class, I store the token and id like this:
class Constants {
companion object {
val baseUrl: String = "BASE_URL_TO_MY_API"
val contentTypeJson = "application/json"
lateinit var STOREAGE_PATH: String
// current user details
lateinit var id: String
lateinit var token: String
lateinit var refresh_token: String
// logged in User
lateinit var user: User
}
How do I make sure that the test variable is set after the asynchronous task is done? Currently, I run into
lateinit property id has not been initialized
I have come across the option to limit the task to a timeout such as I have done with [30, TimeUnit.SECONDS], unfortunately, this did not help.
Thanks for the help! Cheers.
I think the problem is where you want to access the result:
val btnLogin = findViewById<Button>(R.id.btn_login)
btnLogin.setOnClickListener { _ ->
doAsync {
val username = findViewById<EditText>(R.id.input_username_login)
val password = findViewById<EditText>(R.id.input_password_login)
var test: String? = null
Login(username.text.toString(), password.text.toString()) {
// Request was successful
if (it.containsKey(true)) {
// Parse return Json
// e.g. {"id":"36e8fac0-487a-11e8-ad4e-c471feb11e42","token":"d6897a230fd7739e601649bf5fd89ea4b93317f6","expiry":"2018-04-27T17:49:48.721278Z"}
val jelement = JsonParser().parse(it.getValue(true))
val jobject = jelement.asJsonObject
// save field for class-scope access
Constants.token = jobject.get("token").asString
Constants.id = jobject.get("id").asString
}
else{
Toast.makeText(this#LoginActivity, it.getValue(false), Toast.LENGTH_SHORT).show()
}
}
test = Constants.id // here test variable surely set if result was successful, otherwise it holds the null value
test?.let{
resultDelivered(it)
}
}[30, TimeUnit.SECONDS]
}
fun resultDelivered(id: String){
// here we know that the async job has successfully finished
}