I'm building an android application in which the user can choose a playlist from youtube and after choosing it I will need to load all the videos in that playlist into a single list.
I found several solutions online but they were using javascript and the ones that were written in rxjava the paginate was using int numbers rather then strings (I won't be able to use range).
For now I'm able to load the first 50 videos, I tried several approaches (one of which was a recursion but apparently I wrote it bad and I ended up using all the api requests in one load).
Here is my code:
fun getVidoesFromPlayList(playListId: String,pageToken: String): Single<MutableList<YTVideo>> {
return youTubeService.getPlayListResponse(playListId, pageToken)
.flatMap { response ->
Observable.just(response.items)
.flatMapIterable { data -> data }
.flatMap { item ->
youTubeService.getVideoResponse(item.snippet!!.resourceId!!.videoId).toObservable()
.subscribeOn(Schedulers.io())
.map { videoResponse ->
YTVideo(
item.snippet.position,
item.snippet.resourceId!!.videoId,
item.snippet.thumbnails!!.medium!!.url,
item.snippet.title,
videoResponse.items!![0].contentDetails!!.duration,
videoResponse.items[0].snippet!!.channelTitle
)
}
}
.repeatUntil { response.nextPageToken!=null }
.toList()
}
}
1) How can I call to getVidoesFromPlayList with a different nextpagetoken while caching the current videos?
2) Is there a way to do it more effecently without using recursion?
I will appreciate any help or guidance, thank you!
Related
I have a hybrid app; some of my Activities use a WebView to display web content. The web app that I show in the WebView has a JS interface that lets me send commands to the web app to navigate different places or do other things.
For example, if I need my web app to navigate to the "user profile" page, I execute a command like:
class SomeActivity: AppCompatActivity {
...
webView.evaluateJavascript("navigateTo(\"userprofile\")")
...
}
Then, I get a response via the JS interface, and the app reacts accordingly.
I introduced a JS queue to improve performance, so the JS commands are executed sequentially. Instead of calling the evaluateJavascript() function directly on the WebView, I've created a custom WebView component with this JS queue set as a property.
class SomeActivity: AppCompatActivity {
...
webView.jsQueue.queueEvaluateJavascript("navigateTo(\"userprofile\")")
...
}
Now I would like to add a new behaviour on top of that, which is being able to pre-process the commands within the queue. What I mean by pre-processing is that if I ever queue commands of the same "type", like:
class SomeActivity: AppCompatActivity {
...
webView.jsQueue.queueEvaluateJavascript("navigateTo(\"userprofile\")")
webView.jsQueue.queueEvaluateJavascript("navigateTo(\"about-me\")")
webView.jsQueue.queueEvaluateJavascript("navigateTo(\"user-list\")")
...
}
What I would like to happen is that the queue is smart enough to ditch those two first "navigate" commands - "navigateTo(\"userprofile\")" and "navigateTo(\"about-me\")" - because I don't want my WebView to navigate to those two places just to finally navigate to "navigateTo(\"user-list\")".
The implementation of this JS queue looks like this:
class JsQueue(
private val webView: WebView,
private val scope: CoroutineScope
) {
init {
scope.launch {
for (jsScript in jsChannel) {
runJs(jsScript)
}
}
}
private val jsChannel = Channel<String>(BUFFERED)
fun queueEvaluateJavascript(script: String) {
runBlocking {
jsChannel.send(script)
}
}
suspend fun runJs(script: String) = suspendCoroutine<String> { cont ->
webView.evaluateJavascript(script) { result ->
cont.resume(result)
}
}
}
How can I pre-process the js commands in the Channel<String> so I
ditch duplicated js commands?
Also, sometimes my WebView will become invisible, and I want to pause the queue when that happens. I wonder if there's any way to programmatically pause a Channel?
Edit #1
Also, sometimes my WebView will become invisible, and I want to
pause the queue when that happens. I wonder if there's any way to programmatically pause a Channel?
I've tried using this PausableDispatcher implementation, and it seems to be doing the trick.
All of the command examples you gave follow a specific pattern: they're all functions. We can use this to our advantage!
First, let's create some terminology.
navigateTo() is a function (of course!).
And lets call the navigateTo part of the function a type.
An example of some types are:
console.log() => `console.log`,
gotoUrl(url) => `gotoUrl`.
I just made this terminology up. But it will help you understand the logic.
Now, what we need to do is look at the array of commands, understand it's type, and check if any other commands have the same type. If they do, they need to be excluded from the queue before the queue is executed.
Easy!
I've written a sample code that you can integrate with your script:
// Example array of commands for demonstration.
let commands = [
'navigateTo("a")',
'navigateTo("b")',
'navigateTo("c")',
];
/** A list of non-duplicate types*/
let types = [];
/** A list of non-duplicate commands */
let newCommands = [];
// Reverse the array because the most important commands start from the end of array.
for(let command of commands.reverse()){
let type = command.slice(0, command.indexOf('('));
// Determine if type already exists
let alreadyExists = false;
for(let commandType of types){
if(type == commandType){
alreadyExists = true;
break;
}
}
if(alreadyExists)
// Type already exists. Do not add to command list.
continue;
// This type & command does not exist.
// Update command & type arrays
types.push(type);
newCommands.push(command)
}
// New Commands
console.log("Commands: ", newCommands);
// If you want to keep same queue order without duplicates:
console.log("Commands: ", newCommands.reverse())
Let me know if I missed the mark answering this question. Otherwise, cheers to a great queue system!
Before coroutines I used callbacks and debugging always gave me a lot of information. I could get the url my API call is going to, Headers I put into Request, interceptors I used, etc..
Now I use coroutines. All I can get when debugging is the final result of request (fail/sucess) with the result data. Nothing of all these valuable info I need is not there.
shortened example of my code:
restService.getVersionInfo().getResult(
success = {
when {
checkIsMandatory(it.lastMandatoryVersion) -> status.postValue(
Status.NewVersionInfo(it.description, true, it.url)
)
else -> initialization()
}
},
error = {
initialization()
}
I pout breakpoints to error or sucess. Am I missing something or coroutines really have this disadvantage. Please inform me
I'm learning Google Play Billing Library to use in my Android App. I sell coin in my app as consumables product.
The flow is: user buy the item, app consume the item, if consume success increment the coin saved in Firestore.
What i want to ask is, what if increment coin saved in Firestore fails? Lets say because network or other things. This can cause a problem for users because our app has already consumed the item and users don't get their coins.
private fun handleConsumablePurchasesAsync(consumables: List<Purchase>) {
Timber.d("handleConsumablePurchasesAsync called")
consumables.forEach { purchase ->
Timber.d("handleConsumablePurchasesAsync foreach it is $purchase")
val params = ConsumeParams
.newBuilder()
.setPurchaseToken(purchase.purchaseToken)
.build()
playStoreBillingClient.consumeAsync(params) { billingResult, purchaseToken ->
when (billingResult.responseCode) {
BillingClient.BillingResponseCode.OK -> {
purchaseToken.apply { disburseConsumableEntitlements(purchase) }
}
else -> Timber.w(billingResult.debugMessage)
}
}
}
}
private fun disburseConsumableEntitlements(purchase: Purchase) {
when (purchase.sku) {
SkuKeys.COINS_5K -> addCoin(5000)
SkuKeys.COINS_10K -> addCoin(10000)
SkuKeys.COINS_100K -> addCoin(100000)
SkuKeys.COINS_500K -> addCoin(500000)
SkuKeys.COINS_1M -> addCoin(1000000)
SkuKeys.COINS_2M -> addCoin(2000000)
}
}
//what if this fails?
private fun addCoin(amount: Long) =
FirestoreRepository.incrementCoins(FirebaseAuthRepository.currentUserId, amount)
How to fix this problem? Are there any better approach?
My proposal,
Consume the item only after writing to Firebase successfully.
When the app is restarted and queryPurchases() returns that he still owns the item then try to update in Firebase again
I recommend save the purchases in to database once consumed , You can sync the data from database to firebase .
Once synced you can remove the data from database .
You can trigger a workmanager to sync the data additionally if required .(would be helpful if user kills the app)
This how ever is not secure as anyone with root access can get into database and manupulate with data . You can encrypt the database to make it little more secure but it wont be 100 % fool proof .
If you had a server then purchases could be validated on server side , but for you I can't think of any other solution .
I started reading about RX on Android . I was trying to do a POST api call from within a rx chain. I wanted to see if this is the right way and that do I need to even create a disposable again when making the call and register the subscribe/observe threads again. Please see the comments in the code.
disposable.add(module.getInfo()
.flatMapSingleElement {
profile ->
profile.getDetails().map {
//IS THIS NEW DISPOSABLE NEEDED
newDisposable.add(
//Retrofit api to return Single<ResponseBody>
//IS THIS THE RIGHT WAY TO MAKE A SEQUENTIAL API CALL IN RX CONSUMING DATA FROM ANOTHER OPERATOR
module.saveImageDetails(
ImageDetails(imageId)
subscribeOn(Schedulers.io().observeOn(Schedulers.io())
.subscribe(
Consumer { handleResponse(it) },
createErrorHandler()
)
)
profile
}
}
.subscribeOn(ioScheduler)
.observeOn(uiScheduler)
.subscribe(//do something)
I am an Android Developer and 0 knowledge in Wix. Is it possible to get a list of products from Wix Store to display it on Android app. I cannot find any documentation for Android.
This is my test website
https://sakurafukuyoshi031.wixsite.com/juhachipawnshop/shop-1
I just want to know if it is possible to get the data so I can display it on my app from their API maybe by using javascript or the webview javascript injection methods Thanks
There isn't an API for Wix Stores just quite yet, but it's coming soon - https://www.wix.com/code/home/coming-soon
There's a way to expose a collection by creating an API using the wix-http-functions but it appears that it's limited to exposing custom collections - not Wix's native collections (Stores/Collections or Stores/Products). The example on the wix-http-functions is pretty self explanatory. Below a modified version of it:
// In http-functions.js
import {ok, notFound, serverError} from 'wix-http-functions';
import wixData from 'wix-data';
// URL looks like:
// https://www.storename.com/_functions/storeProducts/1
// or
// https://user.wixsite.com/mysite/_functions/storeProducts/1
export function get_storeProducts(request) {
let options = {
"headers": {
"Content-Type": "application/json"
}
};
let pagesize=50;
// query a collection to find matching items
return wixData.query("Stores/Products")
// If you replace the "Stores/Products" with a custom collection name it works
.skip((request.path[0] - 1) * pagesize)
.limit(pagesize)
.find()
.then( (results) => {
// matching items were found
if(results.items.length > 0) {
options.body = {
"items": results.items
};
return ok(options);
}
// no matching items found
options.body = {
"error": `'${request.path[0]}' was not found`
};
return notFound(options);
} )
// something went wrong
.catch( (error) => {
options.body = {
"error": error
};
return serverError(options);
} );
}
Unfortunately this produces an error with the native collections such as "Products"
{"error":{"name":"Error","errorGroup":"User","code":"WD_SCHEMA_DOES_NOT_EXIST"}}
(I could not find any documentation for the error - so this is where I got stuck)
If you then create a custom collection in Wix Code under "Database", export the products from "Stores/Products" into a CSV - and then import the CSV into the custom collection (and finally publish/sync the custom collection) you can workaround the apparent limitation of exposing the native product catalog via a custom API. It's not ideal - but could work if your catalog is not changing frequently.