No way to get nextToken when using custom GraphQL query?[Amplify] - android

//MovieList function Defination
private fun getMovieList(): GraphQLRequest<MovieDBFinal2> {
val document = ("query getMovieList { "
+ "listMovieDBFinal2s(limit: 1000, filter: {Genre1: {eq: \"Animation\"}}) { "
+ "nextToken "
+ "items { "
+ "IMDB_title "
+ "} "
+ "} "
+ "} ")
return SimpleGraphQLRequest(
document,
Collections.emptyMap(),
TypeMaker.getParameterizedType(QueryResponse::class.java, MovieDBFinal2::class.java),
GsonVariablesSerializer())
}
internal class QueryResponse<T> {
private val listMovieDBFinal2s: QueryList<T>? = null
val moviesList: QueryList<T>?
get() = listMovieDBFinal2s
}
internal class QueryList<T> {
val items: Iterable<T>? = null
val nextToken: String? = null
}
//MovieList function call
Amplify.API.query(
getMovieList(),
{ response ->
if (response.hasData()) {
Log.e("MovieList", "$response")
}
},
{ failure ->
Log.e("MovieList", "Query failed.", failure)
}
)
I tried this type in my schema not working.Github issue
PaginationResult not working.Iterable also not giving any token. Only solution is String.class but that is difficult to serialize. The workaround is make the same request two times once with PaginationResult passing the nextToken as input and second make the same request with string.Class type.

Related

Wait for Preferences Datastore to retrieve data from preferences datastore in workmanager

My app is checking for unread emails in the background, the problem is that i need to save and retrieve lastCheckedDate when i last checked for emails so i can show only newly received emails.
For retrieving data from datastore i use observeLastCheckedDate() and i must call it with handler because if i dont i get:
java.lang.IllegalStateException: Cannot invoke observe on a background thread
Function observeLastCheckedDate() get called but while it finish(updates lastCheckedDate), workManager task is already finished with not-updated var lastchecked date.
In main class i avoid this problem by creating and invoking callback but here that does not work(it makes whole app freeze), so we somehow need to wait for that function to finish or get some new way of retreiving data from datastore.
class WorkerMan(private val mContext: Context, workerParameters: WorkerParameters) :
CoroutineWorker(mContext, workerParameters) {
private lateinit var settingsManager: SettingsManager
var lastCheckedDate: Long = 0
#SuppressLint("RestrictedApi", "CheckResult")
val email = inputData.getString("email")
val password = inputData.getString("password")
val token = inputData.getString("token")
fun saveLastCheckedDate(lastCheckedDate: Long) {
GlobalScope.launch {
settingsManager.storeLastCheckedDate(lastCheckedDate)
}
}
private fun observeLastCheckedDate() {
settingsManager.lastCheckedDateFlow.asLiveData().observe(
ProcessLifecycleOwner.get(),
{
lastCheckedDate = it
println("LASTCHECKEDDATE LOADED")
}
)
}
#SuppressLint("RestrictedApi", "WrongThread")
override suspend fun doWork(): Result {
withContext(Dispatchers.IO) {
settingsManager = SettingsManager(getApplicationContext())
var messageCounter = 0;
val handler = Handler(Looper.getMainLooper())
handler.post {
observeLastCheckedDate()
}
println("**************************************************************************")
println("**************************************************************************")
println("WorkManager: Work called")
println("WorkManager email: " + email)
println("WorkManager: Last Checked Moment : " + lastCheckedDate.toString())
println("WorkManager: Current Moment : " + Instant.now().toEpochMilli())
println("**************************************************************************")
println("**************************************************************************")
try {
val session = Session.getDefaultInstance(Properties())
val store = session.getStore("imaps")
store.connect(
"mail.metropolitan.ac.rs",
993,
email,
password
)
val inbox = store.getFolder("INBOX")
inbox.open(Folder.READ_ONLY)
val messages = inbox.search(
FlagTerm(Flags(Flags.Flag.SEEN), false)
)
Arrays.sort(
messages
) { m1: Message, m2: Message ->
try {
return#sort m2.sentDate.compareTo(m1.sentDate)
} catch (e: MessagingException) {
throw RuntimeException(e)
}
}
// println("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ")
// println("WorkManager Started")
// println("WorkMananager email: " + email)
// val current = LocalTime.now()
// println("WorkMananager time: " + current)
// println("Messages amount: " + messages.size)
// println("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ")
for (message in messages) {
Thread.sleep(1000)
messageCounter++
if (message.receivedDate.toInstant().toEpochMilli() >= lastCheckedDate) {
Thread.sleep(1000)
println("=====================================================")
println("NOTIFIKACIJA")
var title = ""
for (element in message.from) {
title += element.toString().substringAfter("<").substringBefore(">")
title += " "
}
println("Title :" + title)
println("Subject :" + message.subject)
println("Datum i vreme : " + message.receivedDate)
title.replace("[", "")
title.replace("]", "")
send(token, message.subject, title)
}
if (messageCounter > 10) {
break
}
}
saveLastCheckedDate(Instant.now().toEpochMilli())
println("=====================================================")
Log.d("WorkManager", "Job finished")
} catch (e: Exception) {
Log.d("WorkManager error", "doWork not executed")
Log.d("WorkManager error", "error: ")
Log.d("WorkManager error", e.printStackTrace().toString())
} catch (e: NetworkOnMainThreadException) {
Log.d("WorkManager error", "doWork not executed")
Log.d("WorkManager error", "NetworkOnMainThreadException: ")
Log.d("WorkManager error", e.toString())
}
}
return Result.Success();
}
}
fun send(to: String?, body: String?, title: String?): String? {
try {
val apiKey =
"***************************************"
val url = URL("https://fcm.googleapis.com/fcm/send")
val conn = url.openConnection() as HttpURLConnection
conn.doOutput = true
conn.requestMethod = "POST"
conn.setRequestProperty("Content-Type", "application/json")
conn.setRequestProperty("Authorization", "key=$apiKey")
conn.doOutput = true
val message = JSONObject()
message.put("to", to)
message.put("priority", "high")
val notification = JSONObject()
notification.put("title", title)
notification.put("body", body)
message.put("notification", notification)
val os = conn.outputStream
os.write(message.toString().toByteArray())
os.flush()
os.close()
val responseCode = conn.responseCode
println("\nSending 'POST' request to URL : $url")
println("Post parameters : $message")
println("Response Code : $responseCode")
println("Response Code : " + conn.responseMessage)
val `in` = BufferedReader(InputStreamReader(conn.inputStream))
var inputLine: String?
val response = StringBuffer()
while (`in`.readLine().also { inputLine = it } != null) {
response.append(inputLine)
}
`in`.close()
println(response.toString())
return response.toString()
} catch (e: Exception) {
Log.d("WorkManager error", "send not executed")
Log.d("WorkManager error", "error: ")
Log.d("WorkManager error", e.printStackTrace().toString())
} catch (e: NetworkOnMainThreadException) {
Log.d("WorkManager error", "send() not executed")
Log.d("WorkManager error", "NetworkOnMainThreadException: ")
Log.d("WorkManager error", e.toString())
}
return "error"
}
DataStore class:
class SettingsManager(context: Context) {
private val dataStore = context.createDataStore(name = "user_settings_preferencess")
companion object {
val ENABLE_NOTIFICATIONS = preferencesKey<Int>("ENABLE_NOTIFICATIONS")
val ENABLE_MAIL_NOTIFICATIONS = preferencesKey<Int>("ENABLE_MAIL_NOTIFICATIONS")
val LAST_CHECKED_DATE = preferencesKey<Long>("LAST_CHECKED_DATE")
}
//Store user data
suspend fun storeNotifications(enableNotifications: Int) {
dataStore.edit {
it[ENABLE_NOTIFICATIONS] = enableNotifications
}
}
suspend fun storeMailNotifications(enableMailNotifications: Int) {
dataStore.edit {
it[ENABLE_MAIL_NOTIFICATIONS] = enableMailNotifications
}
}
suspend fun storeLastCheckedDate(lastCheckedDate: Long) {
dataStore.edit {
it[LAST_CHECKED_DATE] = lastCheckedDate
}
}
val lastCheckedDateFlow: Flow<Long> = dataStore.data.map {
it[LAST_CHECKED_DATE] ?: 0
}
val enableNotificationsFlow: Flow<Int> = dataStore.data.map {
it[ENABLE_NOTIFICATIONS] ?: 1
}
val enableMailNotificationsFlow: Flow<Int> = dataStore.data.map {
it[ENABLE_MAIL_NOTIFICATIONS] ?: 1
}
}
That's a HUGE mess with threading for simple work. (Never make your thread sleep to wait for a value)
if you going to use coroutines in the worker class. SO DON'T DO THAT
there is an alternative CoroutineWorker to extend your class from it instead of Worker
it will provide you with suspending version of doWork() function
NOTE: remember to add the -ktx version of the work manager dependency

Trouble parsing a JSON string with unknown keys using Retrofit and Gson

I have a JSON response that includes unknown keys (the numbers), which I'm finding difficult to parse using the Gson converter. The cut-down version is
{
"list": {
"14": {
"nickname": "Dave",
"fullname": "David Collins"
},
"68": {
"nickname": "Phil",
"fullname": "Phillip Jordan"
}
},
"action": "load",
"status": "LOADED"
}
The following class structure gives an error Expected BEGIN_OBJECT but was NUMBER at line 1 column 23 path $.list..
data class NameNW(
val fullname: String,
val nickname: String
)
data class NamesListNWEntity(
val action: String,
val list: Map<Int, Map<String, NameNW>>,
val status: String
)
I'm not sure why it's expecting BEGIN_OBJECT when the type is Map<Int... (or perhaps it's not seeing the '{' before the number when it's clearly there), so I'm stuck here. How do I get it to parse the string? Even better, how do I get it to record the number for NameNW? If it's not possible, I can adjust the server output, but that means updating a lot of code on the web client as well, which I'd rather avoid.
My Retrofit code is
interface Network {
#FormUrlEncoded
#POST("serverfile")
suspend fun getNames(
#Field("action") action: String,
): NamesListNWEntity
}
class RetrofitWithCookie #Inject constructor(
context: Context,
gson: Gson
) {
private val mContext = context
private val gson = gson
fun createRetrofit(): Retrofit {
val client: OkHttpClient
val builder = OkHttpClient.Builder()
builder.addInterceptor(AddCookiesInterceptor(mContext))
builder.addInterceptor(ReceivedCookiesInterceptor(mContext))
client = builder.build()
return Retrofit.Builder()
.baseUrl("http://192.168.0.19/")
.client(client)
.addConverterFactory(GsonConverterFactory.create(gson))
.build()
}
}
And I'm calling it using
val namesListNWEntity = Network.getNames("load")
Try with the following code.
data class NameNW(
val fullname: String,
val nickname: String
)
data class NamesListNWEntity(
val action: String,
val list: Map<String, NameNW>,
val status: String
)
// You can also test with static json string
private fun test() {
val apiResponse = "{\n" +
" \"list\": {\n" +
" \"14\": {\n" +
" \"nickname\": \"Dave\",\n" +
" \"fullname\": \"David Collins\"\n" +
" },\n" +
" \"68\": {\n" +
" \"nickname\": \"Phil\",\n" +
" \"fullname\": \"Phillip Jordan\"\n" +
" }\n" +
" },\n" +
" \"action\": \"load\",\n" +
" \"status\": \"LOADED\"\n" +
"}"
val namesListNWEntity: NamesListNWEntity = Gson().fromJson(apiResponse, NamesListNWEntity::class.java)
}

Kotlin coroutines: why can't I see logs until the end?

I'm new with kotlin coroutines and have some doubts. So I'm trying to download a list of fonts using a kotlin coroutine, and I added some logs to see when a font is downloaded, or a message when it already existed. I was expecting to see one log each time a font is accessed, however I see only the progressBar, and when it gets hidden, I see all the logs at once. Am I doing something wrong?
private fun init() {
val job = Job()
val bgScope = CoroutineScope(Dispatchers.IO + job)
bgScope.launch {
getStuff()
}
}
fun getStuff() {
val uiScope = CoroutineScope(Dispatchers.Main + Job())
uiScope.launch {
progressbar.visibility = View.VISIBLE
}
for (font in jsonObject.fontList) {
if (!font.exists()) {
downloadFile(font)
Timber.d("file " + font.id + " downloaded: " + font.exists())
} else {
Timber.d("file " + font.id + " already exists ")
}
}
uiScope.launch {
progressbar.visibility = View.GONE
}
That's because your
for (font in jsonObject.fontList) {
if (!font.exists()) {
downloadFile(font)
Timber.d("file " + font.id + " downloaded: " + font.exists())
} else {
Timber.d("file " + font.id + " already exists ")
}
}
runs in another thread, and delays the response.Therefore, you should modify you progress visibility after the downloadFile has finished.
You should start the coroutine inside your downloadFileMethod() along with the progress bar switching on/off.

Get item from Object ArrayList

How can I call the access_role in OrganizationAccess ?
This is the classes
User
class User : Serializable {
var id = ""
var user_organization_accesses: ArrayList<OrganizationAccess>? = null
}
OrganizationAccess
class OrganizationAccess:Serializable {
var access_role = ""
}
What I have tried
mUser = intent.getSerializableExtra(ARG_PARAM) as User // receive from previous activity
longToast("Role is " + mUser.user_organization_accesses!!.xxx)
What should I write for the xxx ?
I have solved it
longToast("Role is " + mUser.user_organization_accesses!!.component1().access_role)

suspending a kotlin Coroutine does not work with volley

I wrote following (test) function to talk to the google maps api via volley and with coroutines. Sadly it never finishes when calling it with the suspendCoroutine. If I use the same function, drop the coroutine stuff and implement a "normal" callback, everything works fine. I am kind of at a loss whats the problem here. Is anybody able to help?
Code executes until Log.d(LOGTAG, "AFTERAFTER"), but never reaches Log.d("findNaturalLocations", "Response: " + response)
suspend fun testNaturalLocations(tag: Tag, lastKnownUserLocation:
Location): ArrayList<CLDistanceRequest> = suspendCoroutine {
continuation ->
Log.d("findNaturalLocations", "getDistanceAndTimeBetweenLocations")
var distanceRequests = ArrayList<CLDistanceRequest>()
val mapsBaseUrl = "https://maps.googleapis.com/maps/api/place/nearbysearch/"
val mapsOutputFormat = "json"
val location = "location=" + lastKnownUserLocation.latitude.toString() + "," + lastKnownUserLocation.longitude.toString()
val radius = "radius=5000"
val keyword = "keyword=Supermarket"
val name = "name=Supermarket"
val sensor = "sensor=true"
val apiKey = "key=API_KEY"
val finishedUrl = mapsBaseUrl + mapsOutputFormat + "?" + location + "&" + radius + "&" + keyword + "&" + name + "&" + sensor + "&" + apiKey
Log.d(LOGTAG, finishedUrl)
val jsObjectRequest = JsonObjectRequest(Request.Method.GET, finishedUrl, null,
Response.Listener<JSONObject> { response ->
Log.d("findNaturalLocations", "Response: " + response)
var results = response.getJSONArray("results")
// parse distanceRequests, ommitted for brevity
continuation.resume(distanceRequests)
},
Response.ErrorListener { error ->
Log.e("Error", error.localizedMessage, error)
continuation.resumeWithException(error)
}
)
Log.d(LOGTAG, "AFTER");
jsObjectRequest.setShouldCache(false)
CLGlobal.getRequestQueue().add(jsObjectRequest)
Log.d(LOGTAG, "AFTERAFTER");
}
Doing the same with a simple callback works flawlessly.
var i = 0;
runBlocking {
val query = async(CommonPool) {
i = this#CLTaskList.test2()
}
query.await()
}
suspend fun test2():Int = suspendCoroutine<Int> { continuation ->
Log.d("TESTTEST", "TEST2 CALLED")
test {
Log.d("TESTTEST", "CONTINUATION")
continuation.resume(it)
}
}
fun test(completionHandler: (Int) -> Unit) {
Log.d("TESTTEST", "TEST CALLED")
completionHandler(1)
}
As you reveal in your comment, this is how you run the query:
val query = async(CommonPool) {
this#CLTaskList.testNaturalLocations(tags[0],
CLGlobal.getInstance().mLastKnownLocation!!)
}
runBlocking<Unit> { query.await() }
You are close to getting it right, but this piece of code is all backwards:
You let the suspendable function run in a thread pool, relieving some worker thread of the duty to block until you get your answer (it wouldn't care if it's blocked);
You block your GUI thread with runBlocking.
In the correct solution you need none of async, CommonPool or runBlocking, all you need is this:
launch(UI) {
val result = testNaturalLocations(tags[0],
CLGlobal.getInstance().mLastKnownLocation!!)
// deal with the result right here
}
Since testNaturalLocations is a suspendable function, it's not blocking your UI thread, and when the callback you wrote resumes it, your code simply goes on to the next line, with result assigned.

Categories

Resources