I'm new in android dev and I have an issue with my code. I'm trying to parse data from a json, put the information into an object and then return this from a function.
My code:
data class AccountInfo (
var username: String,
var bio: String,
var avatar: String,
var cover: String,
var created: Long,
var reputation: Double
)
fun getAccountInfo(accessToken: String): AccountInfo {
var info = AccountInfo("username", "", "", "", 0, 0.0)
val jsonObjectRequest: JsonObjectRequest = object : JsonObjectRequest(Method.GET, "https://api.imgur.com/3/account/me", null, Response.Listener { response ->
Log.e(HomeFragment.TAG, "got $response")
try {
val data = JSONObject(response.toString())
val items = data.getJSONObject("data")
Log.e(HomeFragment.TAG, "username=" + items.getString("url"))
info.username = items.getString("url")
Log.e(HomeFragment.TAG, "username second=" + info.username)
info.bio = items.getString("bio")
info.avatar = items.getString("avatar")
info.cover = items.getString("cover")
info.created = items.getLong("created")
info.reputation = items.getDouble("reputation")
} catch (e: JSONException) {
Log.e(HomeFragment.TAG, "Parsing error: " + e.message)
}
}, Response.ErrorListener { error ->
Log.e(HomeFragment.TAG, "Response error: " + error.message)
}) {
#Throws(AuthFailureError::class)
override fun getHeaders(): Map<String, String> {
val headers = HashMap<String, String>()
headers["Authorization"] = "Bearer ${accessToken}"
headers["User-Agent"] = "epicture"
return headers
}
}
// Adding request to request queue
AppController.instance?.addToRequestQueue(jsonObjectRequest)
Log.e(HomeFragment.TAG, "username last=" + info.username)
return info
}
The problem is that the function always returns default initialized object (with "username" for username for exemple) and not the string parsed in my JsonObjectRequest, even if the parsing is working (logs are correct)...
If you have some help or ideas thank you!
You should wait until JsonObjectRequest has finished and returned the result from server then you got the result , You get the result inside Response.Listener callback but the info object that you returned doesn't updated.
use async wait or a suspend function to fetch info from server then return it
Update :
Here is the code :
fun getAccountInfo(
accessToken: String,
onSuccess: (account: AccountInfo) -> Unit,
onError: (e: Exception) -> Unit
) {
val jsonObjectRequest: JsonObjectRequest = object : JsonObjectRequest(
Method.GET,
"https://api.imgur.com/3/account/me",
null,
Response.Listener { response ->
try {
val data = JSONObject(response.toString())
val items = data.getJSONObject("data")
val info = AccountInfo(
username = items.getString("url"),
bio = items.getString("bio"),
avatar = items.getString("avatar"),
cover = items.getString("cover"),
created = items.getLong("created"),
reputation = items.getDouble("reputation")
)
onSuccess.invoke(info) // request success so call this
} catch (e: JSONException) {
onError.invoke(e)
}
},
Response.ErrorListener { error ->
onError.invoke(error)
}) {
#Throws(AuthFailureError::class)
override fun getHeaders(): Map<String, String> {
val headers = HashMap<String, String>()
headers["Authorization"] = "Bearer $accessToken"
headers["User-Agent"] = "epicture"
return headers
}
}
AppController.instance?.addToRequestQueue(jsonObjectRequest)
}
Call this on your Activity
getAccountInfo("access token",
onSuccess = {
// todo show info in UI
Log.d(TAG, "onCreate: onSuccess : $it")
},
onError = {
// toast error or show custom error view
Log.e(TAG, "onCreate: onError: ", it)
}
)
Related
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 using Single.zip() and Function 5 to consolidate 5 API calls into one Single:
private fun loadProfileAndBalances() {
registerSubscription(
Single.zip<AvailableFundsResult, IncomingFundsResult, TotalEarnedResult, TotalDonatedResult, ProfileResult, Unit>(
Interactors.api.paymentsApiClient.getAvailableFunds()
.map<AvailableFundsResult> {
Timber.d("Available Result [${it.amount}]") <------ DollarAmount object with null amount
AvailableFundsResult.Amount(it) }
.onErrorReturn { AvailableFundsResult.Error(it) }
.scheduleIOUI(),
Interactors.api.paymentsApiClient.getIncomingFunds()
.map<IncomingFundsResult> { IncomingFundsResult.Amount(it) }
.onErrorReturn { IncomingFundsResult.Error(it) }
.scheduleIOUI(),
Interactors.api.paymentsApiClient.getTotalEarned()
.map<TotalEarnedResult> { TotalEarnedResult.Amount(it) }
.onErrorReturn { TotalEarnedResult.Error(it) }
.scheduleIOUI(),
Interactors.api.paymentsApiClient.getTotalDonated()
.map<TotalDonatedResult> { TotalDonatedResult.Amount(it) }
.onErrorReturn { TotalDonatedResult.Error(it) }
.scheduleIOUI(),
Interactors.profileManager.getNotCachedProfile()
.map<ProfileResult> { ProfileResult.Profile(it) }
.onErrorReturn { ProfileResult.Error(it) }
.scheduleIOUI(),
Function5 { availableFunds: AvailableFundsResult, incomingFunds: IncomingFundsResult, totalEarned: TotalEarnedResult, totalDonated: TotalDonatedResult, profileResult: ProfileResult ->
availableTotal = when (availableFunds) {
is AvailableFundsResult.Amount ->
availableFunds.result.amount
is AvailableFundsResult.Error -> {
Timber.w(availableFunds.throwable, "Error while fetching sponsorships")
"0.00"
}
}
incomingTotal = when (incomingFunds) {
is IncomingFundsResult.Amount -> incomingFunds.result.amount
is IncomingFundsResult.Error -> {
Toast.makeText(activity, getString(R.string.payout_main_error_loading_totals), Toast.LENGTH_SHORT).show()
"0.00"
}
}
earnedTotal = when (totalEarned) {
is TotalEarnedResult.Amount -> totalEarned.result.amount
is TotalEarnedResult.Error -> {
Toast.makeText(activity, getString(R.string.payout_main_error_loading_totals), Toast.LENGTH_SHORT).show()
"0.00"
}
}
donatedTotal = when (totalDonated) {
is TotalDonatedResult.Amount -> totalDonated.result.amount
is TotalDonatedResult.Error -> {
Toast.makeText(activity, getString(R.string.payout_main_error_loading_totals), Toast.LENGTH_SHORT).show()
"0.00"
}
}
onboardingComplete = when (profileResult) {
is ProfileResult.Profile ->
profileResult.result.isOnboardingCompleted
is ProfileResult.Error -> {
Timber.e(profileResult.throwable, "Error fetching profile")
true
}
}
}
).ignoreElement()
.subscribe(::updateViews) {
it.printStackTrace()
availableTotal = ""
incomingTotal = ""
earnedTotal = ""
donatedTotal = ""
onboardingComplete= false
vWalletRefresher.isRefreshing = false
internetConnectionError(it)
})
}
Each of these API calls is succeeding with code 200. The call made with Interactors.api.paymentsApiClient.getAvailableFunds() is returning {"amount":264.69}, which is parsed into an object of this class:
internal data class DollarAmount(#SerializedName("amount") val amount: String)
The paymentsApiClient repeatedly referenced is built like this:
private fun createNewPaymentsClient(authRefreshClient: AuthRefreshClient,
preferencesInteractor: PreferencesInteractor): PaymentsApiClient {
val loggingInterceptor = run {
val httpLoggingInterceptor = HttpLoggingInterceptor()
httpLoggingInterceptor.apply {
httpLoggingInterceptor.level = if (BuildConfig.DEBUG) HttpLoggingInterceptor.Level.BODY else HttpLoggingInterceptor.Level.NONE
}
}
val okHttpClient = createHttpClientBuilder()
.addInterceptor(createSessionRequestInterceptor())
.addInterceptor(createUserAgentInterceptor(context))
.addInterceptor(loggingInterceptor)
.authenticator(RefreshUserAuthenticator(authRefreshClient, preferencesInteractor,
UnauthorizedNavigator(SDKInternal.appContext, Interactors.preferences)))
.build()
val gson = GsonBuilder().excludeFieldsWithoutExposeAnnotation().setLenient().create()
return Retrofit.Builder()
.client(okHttpClient)
.addCallAdapterFactory(RxJava2CallAdapterFactory.create())
.addConverterFactory(GsonConverterFactory.create(gson))
.baseUrl(Interactors.apiEndpoint)
.build()
.create(PaymentsApiClient::class.java)
}
Despite the API call being successful, the log statement that is printed in Single.zip for a successful retrieval of a DollarAmount via Interactors.api.paymentsApiClient.getAvailableFunds() the is showing its amount as being null, rather than 264.69. What is wrong with my parsing that is making this null?
Edit: As someone noticed, I am wrapping the results in another set of classes:
private sealed class AvailableFundsResult {
data class Error(val throwable: Throwable) : AvailableFundsResult()
data class Amount(val result: DollarAmount) : AvailableFundsResult()
}
private sealed class IncomingFundsResult {
data class Error(val throwable: Throwable) : IncomingFundsResult()
data class Amount(val result: DollarAmount) : IncomingFundsResult()
}
private sealed class TotalEarnedResult {
data class Error(val throwable: Throwable) : TotalEarnedResult()
data class Amount(val result: DollarAmount) : TotalEarnedResult()
}
private sealed class TotalDonatedResult {
data class Error(val throwable: Throwable) : TotalDonatedResult()
data class Amount(val result: DollarAmount) : TotalDonatedResult()
}
private sealed class ProfileResult {
data class Error(val throwable: Throwable) : ProfileResult()
data class Profile(val result: InfluencerProfileDto) : ProfileResult()
}
I don't think this is relevant though because it the amount is coming back null before I even spit out an instance of these wrapper classes.
Edit 2: Class that gets returned from 5th API call:
#JsonClass(generateAdapter = true)
internal data class InfluencerProfileDto(
#Json(name = "id") val id: String,
#Json(name = "emailAddress") val email: String?,
#Json(name = "phoneNumber") val phoneNumber: PhoneNumberDto?,
#Json(name = "isPhoneNumberVerified") val isPhoneNumberVerified: Boolean,
#Json(name = "isEmailVerified") val isEmailVerified: Boolean,
#Json(name = "notificationTimePreference") val notificationTimePreference: String,
#Json(name = "isInstagramConnected") val isInstagramConnected: Boolean,
#Json(name = "isFacebookConnected") val isFacebookConnected: Boolean,
#Json(name = "isTwitterConnected") val isTwitterConnected: Boolean,
#Json(name = "currencyIsoSymbol") val currencyIsoSymbol: String,
#Json(name = "currencySymbol") val currency: String,
#Json(name = "birthDate") val birthDate: Date?,
#Json(name = "name") val name: String?,
#Json(name = "profilePictureUri") val avatarUrl: String?,
#Json(name = "gender") val gender: Gender?)
{
val isOnboardingCompleted: Boolean
get() = gender!= Gender.UNKNOWN && birthDate!= null && !!notificationTimePreference.isNullOrEmpty() && isPhoneNumberVerified && !email.isNullOrEmpty()
}
my approach is in java
first of all we need two libraries that you might not be using in your project ie.
gson => for pasing objects from string and vice versa
volley => for string http requests
please google their official github pages and install the latest versions
then we ll be creating a common result object to bear results
class Result {
boolean error; // this field can be used by the api to tell if there was some error while processing the request
String message; // here api can give you some extra message if there was some error
String result; // here you get the result. you can make it a map as well if you want to receive an object and later parse to a unique object class
}
this is a function you can use for HTTP requets
public static Single<Result> getHttpResult(String api, Map<Stirng, String> params){
return Single.create(emitter -> {
StringRequest stringRequest = new StringRequest(Request.Method.POST, api, response -> {
Log.i("RESPONSE :", response);
try {
Result result = new Gson().fromJson(response, Result.java);
emitter.onSuccess(result);
} catch (JSONException e) {
emitter.onError(e);
}
}, emitter::onError) {
#Override
protected Map<String, String> getParams() {
return params;
}
};
stringRequest.setRetryPolicy(new DefaultRetryPolicy(0, 0, 0));
RequestQueue requestQueue = Volley.newRequestQueue(context);
requestQueue.add(stringRequest);
});
}
this is how you can get multiple results at once and i recommend you also check the internet connectivity if user has enough internet then only make parallel requests else it wont produce good UX
...
List<Single<Result>> tasks = new ArrayList();
tasks.add(getHttpResult("https://..."), /**here you can add a map with params that your api might require for auth and other purposes**/);
tasks.add(...);
tasks.add(...);
tasks.add(...);
Single.zip(tasks, objects -> {
List<Result> results = new ArrayList();
for(Object object : objects){
results.add((Result)object)
}
return results;
}).subscribe(results -> {
// this result object have all the results of http requests
// for distinguising results from each other you can create a field on the result object
});
...
hope my approach is helpful to you ;)
I use this JSON https://api.github.com/users
I need to get string name, followers, following, and more. But on the program says "No value for name". I think I need to go to a specific user example: https://api.github.com/users/mojombo to getting that info, but I don't know-how.
And I using loopj library.
Here's My Code
private fun getDataGitDetail() {
progressBar.visibility = View.VISIBLE
val client = AsyncHttpClient()
client.addHeader("Authorization", "token 6fe9dff2e5e43d25eb3abe9ff508a750b972f725")
client.addHeader("User-Agent", "request")
val url = "https://api.github.com/users"
client.get(url, object : AsyncHttpResponseHandler() {
override fun onSuccess(
statusCode: Int,
headers: Array<Header>,
responseBody: ByteArray
) {
progressBar.visibility = View.INVISIBLE
val result = String(responseBody)
Log.d(TAG, result)
try {
val jsonArray = JSONArray(result)
for (i in 0 until jsonArray.length()) {
val jsonObject = jsonArray.getJSONObject(i)
val username: String? = jsonObject.getString("login")
val name: String? = jsonObject.getString("name")
val avatar: String? = jsonObject.getString("avatar_url")
val company: String? = jsonObject.getString("url")
val location: String? = jsonObject.getString("url")
val repository: Int = 0
val followers: Int = 0
val following: Int = 0
listData.add(
Data(
username,
name,
avatar,
company,
location,
repository,
followers,
following
)
)
}
showRecyclerList()
} catch (e: Exception) {
Toast.makeText(this#MainActivity, e.message, Toast.LENGTH_SHORT)
.show()
e.printStackTrace()
}
}
override fun onFailure(
statusCode: Int,
headers: Array<Header>,
responseBody: ByteArray,
error: Throwable
) {
progressBar.visibility = View.INVISIBLE
val errorMessage = when (statusCode) {
401 -> "$statusCode : Bad Request"
403 -> "$statusCode : Forbidden"
404 -> "$statusCode : Not Found"
else -> "$statusCode : ${error.message}"
}
Toast.makeText(this#MainActivity, errorMessage, Toast.LENGTH_LONG)
.show()
}
})
}
The current response you are getting does not contain a key name in the JSONObject.
If you want the Name of all the users you will have to go to each users endpoint in the api. You'll need to make another request inside your for loop that gets datafrom an endpoint like https://api.github.com/users/mojombo
val jsonArray = JSONArray(result)
for (i in 0 until jsonArray.length()) {
val jsonObject = jsonArray.getJSONObject(i)
val username: String? = jsonObject.getString("login")
//Make the request here using "https://api.github.com/users/" + login
You can then choose to get the rest of the data from either the first response or the 2nd one as both contain that information.
I hope this helps.
No need for a JSON array, cz API https://api.github.com/users/mojombo is JSON Object.
Example:
client.get(url, object : AsyncHttpResponseHandler() {
override fun onSuccess(statusCode: Int, headers: Array<Header>, responseBody: ByteArray) {
try {
//parsing json
val result = String(responseBody)
val responseObject = JSONObject(result)
textView2.text = responseObject.getString("login")
textView3.text = responseObject.getString("name")
textView9.text = responseObject.getString("location")
desc.text = responseObject.getString("company")
view?.let { Glide.with(it).load(responseObject.getString("avatar_url")).into(imageView2) }
} catch (e: Exception) {
Log.d("Exception", e.message.toString())
}
}
}
I'm facing with error for below code
may someone help me
I'm trying to register by this code to an app
registration with VOLLEY and KOTLIN issue , can somebody say me what is issue I'm getting followed error :
I am adding this for more details to can post my question
W/System.err: org.json.JSONException: Value <br of type java.lang.String cannot be converted to JSONObject
at org.json.JSON.typeMismatch(JSON.java:111)
`override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
loading = findViewById<ProgressBar>(R.id.loading)
name = findViewById<EditText>(R.id.name)
email = findViewById<EditText>(R.id.email)
password = findViewById<EditText>(R.id.password)
c_password = findViewById<EditText>(R.id.password)
btn_regist = findViewById<Button>(R.id.btn_regist)
btn_regist.setOnClickListener() { v -> regist() }
}
private fun regist(){
loading.visibility = View.VISIBLE
btn_regist.visibility = View.GONE
val name:String = this.name.text.trim().toString()
val email:String = this.email.text.trim().toString()
val password:String = this.password.text.trim().toString()
val stringRequest = object : StringRequest(com.android.volley.Request.Method.POST, URL_REGIST,
com.android.volley.Response.Listener<String>() { response ->
try {
val jsonObject = JSONObject(response)
val success : String = jsonObject.getString("success")
if (success.equals("1")){
Toast.makeText(this, "Register Success ! Eyyvalll !", Toast.LENGTH_SHORT).show()
}
}
catch (e: JSONException){
e.printStackTrace()
Toast.makeText(this, "Register Error ! goh !!!! :/" + e.toString() , Toast.LENGTH_SHORT).show()
loading.visibility = View.GONE
btn_regist.visibility = View.VISIBLE
}
}, com.android.volley.Response.ErrorListener {
error -> error.printStackTrace()
Toast.makeText(this, "Register Error ! SHIT :/" + error.toString() , Toast.LENGTH_SHORT).show()
loading.visibility = View.GONE
btn_regist.visibility = View.VISIBLE
}) {
// #Throws(AuthFailureError::class)
override fun getParams(): Map<String, String> {
val params = HashMap<String, String>()
params["name"] = name
params["email"] = email
params["password"] = password
return params
}
}
val requestQueue = Volley.newRequestQueue(this)
requestQueue.add(stringRequest)
}`
I thing this error because response result is not a valid json format
com.android.volley.Response.Listener<String>() { response ->
try {
//at this point,you can debug result with console log your response
//because if response have a php error then json format not valid
Log.d("RESPONSE"," $response")
val jsonObject = JSONObject(response)
//at this you can check if json has have key name success
if (jsonObject.has("success")){
val success : String = jsonObject.getString("success")
if (success.equals("1")){
Toast.makeText(this, "Register Success ! Eyyvalll !", Toast.LENGTH_SHORT).show()
}
}
}
......
hope this help
I'm creating a login for my application.
I am currently stuck in posting problems to my API
This is my API that which is made to support login.
{
success: false,
message: "Please provide complete and accurate information.",
data: [ ]
}
fun loginUrlSuccess(urlApi : String) {
Log.d("login", urlApi)
authSignin_cgi = gson.fromJson(urlApi, DtoProfile::class.java)
loginsSuccess = authSignin_cgi.success
val queue = Volley.newRequestQueue(context)
val stringReq = object : StringRequest(Request.Method.POST,urlApi,Response.Listener<String>{ response ->
Log.w("response",response)
Toast.makeText(context,"Loging success..",Toast.LENGTH_SHORT).show()
if (loginsSuccess){
Toast.makeText(context,authSignin_cgi.message,Toast.LENGTH_LONG).show()
} else {
Toast.makeText(context,authSignin_cgi.message,Toast.LENGTH_LONG).show()
}
},Response.ErrorListener { error ->
Log.w("error", error.toString())
Toast.makeText(context, "error..$error",Toast.LENGTH_SHORT).show()
}){
override fun getParams(): MutableMap<String, String> {
val param = HashMap<String, String>()
val userEmail = textEmail.text.toString().trim()
val userPassword = textPassword.text.toString().trim()
param["useremail"] = userEmail
param["userpassword"] = userPassword
return param
}
}
queue.add(stringReq)
}
I get an error from the Logcat screen.
So what do I have to do?
04-04 15:31:43.614 8365-8699/com.example.atimeonlin5 E/Volley: [700] NetworkDispatcher.processRequest: Unhandled exception java.lang.RuntimeException: Bad URL {"success":false,"message":"โปรดระบุข้อมูลให้ถูกต้องครบถ้วน","data":[]}
java.lang.RuntimeException: Bad URL {"success":false,"message":"โปรดระบุข้อมูลให้ถูกต้องครบถ้วน","data":[]}
All right , you should try to construct an Url object instead of type String !
You should use an url (like "http://www.google.com"), not a random string. Your urlApi is not url.
Example from doc:
val textView = findViewById<TextView>(R.id.text)
// ...
// Instantiate the RequestQueue.
val queue = Volley.newRequestQueue(this)
val url = "http://www.google.com"
// Request a string response from the provided URL.
val stringRequest = StringRequest(Request.Method.GET, url,
Response.Listener<String> { response ->
// Display the first 500 characters of the response string.
textView.text = "Response is: ${response.substring(0, 500)}"
},
Response.ErrorListener { textView.text = "That didn't work!" })
// Add the request to the RequestQueue.
queue.add(stringRequest)