We are trying with new ApiV2 with base plan id new feature in Play Store but unable to update it to the latest use case as we are unable to fetch the base plan id with getProductDetails().
val queryProductDetailsParams =
QueryProductDetailsParams.newBuilder()
.setProductList(
ImmutableList.of(
Product.newBuilder()
.setProductId("product_id_example")
.setProductType(ProductType.SUBS)
.build()))
.build()
billingClient.queryProductDetailsAsync(
queryProductDetailsParams,
ProductDetailsResponseListener { billingResult, productDetailsList ->
// check billingResult
// process returned productDetailsList
}
)
here we are looking to have values with product details like offers and base plan id for the following subscriptions.
val activity : Activity = ...;
// Retrieve a value for "productDetails" by calling queryProductDetails()
// extension function.
val flowParams = BillingFlowParams.newBuilder()
.setProductDetails(productDetails)
.setOfferToken(selectedOfferIdToken)
.build()
val responseCode = billingClient.launchBillingFlow(activity, flowParams).responseCode
The launchBillingFlow() method returns one of several response codes listed in BillingClient.BillingResponseCode. Be sure to check this result to ensure there were no errors launching the purchase flow. A BillingResponseCode of OK indicates a successful launch.
Proposal
Update the SDK versions which help us to have relevant output like this.
I am trying to use the Amadeus Offers Search API with the following code:
when (val flightOffers = amadeus.shopping.flightOffersSearch.get(
originLocationCode = "MDZ",
destinationLocationCode = "MAD",
departureDate = LocalDate.parse("2020-11-11").toString(),
adults = 2,
max = 1
)) {
is ApiResult.Success -> {
if (flightOffers.succeeded) {
println("RESULT SUCCEEDED")
println(flightOffers.data)
}
else
{
println("RESULT DIDN'T SUCCEEDED")
}
}
is ApiResult.Error -> {
println("RESULT ERROR")
}
}
And if I compile that the logcat output is as follows:
I/System.out: RESULT SUCCEEDED
Which makes me think that flightOffers.data is empty.
However if I try this code:
val flightOffers = amadeus.shopping.flightOffersSearch.get(
originLocationCode = "MDZ",
destinationLocationCode = "MAD",
departureDate = LocalDate.parse("2020-11-11").toString(),
adults = 2,
max = 1
)
println("AMADEUS: $flightOffers")
I get the following output:
I/System.out: AMADEUS: Success(meta=Meta(count=1, links={self=https://test.api.amadeus.com/v2/shopping/flight-offers?originLocationCode=MDZ&destinationLocationCode=MAD&departureDate=2020-11-11&adults=2&max=1}), data=[FlightOfferSearch(type=flight-offer, id=1, source=GDS, instantTicketingRequired=false, nonHomogeneous=false, oneWay=false, lastTicketingDate=2020-05-03, numberOfBookableSeats=7, itineraries=[Itinerary(duration=PT18H, segments=[SearchSegment(departure=AirportInfo(iataCode=MDZ, terminal=null, at=2020-11-11T07:10:00), arrival=AirportInfo(iataCode=AEP, terminal=null, at=2020-11-11T08:45:00), carrierCode=AR, number=1403, aircraft=Aircraft(code=738), duration=PT1H35M, id=1, numberOfStops=0, blacklistedInEU=false, co2Emissions=null), SearchSegment(departure=AirportInfo(iataCode=EZE, terminal=A, at=2020-11-11T13:25:00), arrival=AirportInfo(iataCode=MAD, terminal=1, at=2020-11-12T05:10:00), carrierCode=UX, number=42, aircraft=Aircraft(code=789), duration=PT11H45M, id=2, numberOfStops=0, blacklistedInEU=false, co2Emissions=null)])], price=SearchPrice(currency=EUR, total=1151.26, base=510.0, fees=[Fee(amount=0.0, type=SUPPLIER), Fee(amount=0.0, type=TICKETING)], grandTotal=1151.26), pricingOptions=PricingOptions(includedCheckedBagsOnly=true, fareType=[PUBLISHED], corporateCodes=null, refundableFare=false, noRestrictionFare=false, noPenaltyFare=false), validatingAirlineCodes=[UX], travelerPricings=[TravelerPricing(travelerId=1, fareOption=STANDARD, travelerType=ADULT, price=SearchPrice(currency=EUR, total=575.63, base=255.0, fees=null, grandTotal=0.0), fareDetailsBySegment=[FareDetailsBySegment(segmentId=1, cabin=ECONOMY, fareBasis=ZYYOPO, segmentClass=Q, includedCheckedBags=IncludedCheckedBags(weight=0, weightUnit=null)), FareDetailsBySegment(segmentId=2, cabin=ECONOMY, fareBasis=ZYYOPO, segmentClass=Z, includedCheckedBags=IncludedCheckedBags(weight=0, weightUnit=null))]), TravelerPricing(travelerId=2, fareOption=STANDARD, travelerType=ADULT, price=SearchPrice(currency=EUR, total=575.63, base=255.0, fees=null, grandTotal=0.0), fareDetailsBySegment=[FareDetailsBySegment(segmentId=1, cabin=ECONOMY, fareBasis=ZYYOPO, segmentClass=Q, includedCheckedBags=IncludedCheckedBags(weight=0, weightUnit=null)), FareDetailsBySegment(segmentId=2, cabin=ECONOMY, fareBasis=ZYYOPO, segmentClass=Z, includedCheckedBags=IncludedCheckedBags(weight=0, weightUnit=null))])])], dictionaries={locations={MAD={cityCode=MAD, countryCode=ES}, EZE={cityCode=BUE, countryCode=AR}, MDZ={cityCode=MDZ, countryCode=AR}, AEP={cityCode=BUE, countryCode=AR}}, aircraft={789=BOEING 787-9, 738=BOEING 737-800}, currencies={EUR=EURO}, carriers={AR=AEROLINEAS ARGENTINAS, UX=AIR EUROPA}})
Which means that the API is returning a JSON but then I can't use flightOffers with gson to pass this data to a DataClass because flightOffers is a ApiResult> and I don't know how to use that. According to their library docs it should be done like I did it in the first try.
I appreciate all the help and advice I can get. This is my first Android App.
Nice to see that we have a new Android developer in the community !
So first, in Android you should avoid using println, instead you should use Log.d/e/w/i, this method will print your result in android logcat.
For what I see you successfully setup your project and where able to make query from the sdk.
In the android sdk, every get() will give you a correct data object and not just JSON. You don't have to take care of parsing the answer. The thing you have in your flightOffers.data is in fact a List<FlightOfferSearch> that you can use right away !
I am currently developing an app that will get Fitness History Data from Google Fit. Getting the steps and weight are okay but getting the sleep data is a bit of a problem. I want to get the accurate start and end time but the only way to get that is to bucket it by activity segment. The problem is, when there's a lot of data you are trying to get (the app that I'm currently developing requires to get data from 365 days ago at the most), it will not even return a timeout error and my app will keep loading. It will not even start to read the data from Google Fit. So, I wanna ask if there's a way to get the sleep data by activity segment despite its large size? And please do share your code. And by the way, this is how I get my sleep data:
val sleepReadRequest = DataReadRequest.Builder()
.aggregate(DataType.TYPE_ACTIVITY_SEGMENT, DataType.AGGREGATE_ACTIVITY_SUMMARY)
.bucketByActivitySegment(1, TimeUnit.MINUTES)
.setTimeRange(offset, end, TimeUnit.MILLISECONDS)
.build()
LogUtil.d(TAG, "getting sleep data...")
Fitness.getHistoryClient(
context,
Objects.requireNonNull<GoogleSignInAccount>(GoogleSignIn.getLastSignedInAccount(context))
)
.readData(sleepReadRequest)
.addOnSuccessListener { dataReadResponse ->
LogUtil.d(TAG, "success sleep data")
val secondSet = handleDataReturned(dataReadResponse, false, DateUtil.convertTimeStampToDate(offset, DateUtil.DATE_FORMAT))
dailyData.addAll(secondSet)
val allDailyList = getDailyDataList(dailyData, userHeight)
callback.onGetDataSuccess(allDailyList)
}
.addOnFailureListener { e ->
LogUtil.d(TAG, "fail sleep data")
if (e is ApiException && e.statusCode == GoogleFitError.NOT_SIGNED.code) { // not signed app exception
revokePermission(context)
callback.onGetDataFailure(GoogleFitError.parse(e.statusCode))
} else {
callback.onGetDataFailure(AppError.parse(Throwable(e)))
}
}
.addOnCompleteListener { task ->
LogUtil.d(TAG, "complete sleep data")
callback.onGetDataComplete(task)
}
Rather than aggregating, can you just read the activity segments and iterate through them yourself?
val sleepReadRequest =
DataReadRequest.Builder()
.read(DataType.TYPE_ACTIVITY_SEGMENT)
.setTimeRange(offset, end, TimeUnit.MILLISECONDS)
.build()
You can then retrieve the returned data with DataReadResult#getDataSet(DataType).
If you find that it's timing out (a year of data at once is potentially rather a lot!) I'd suggest batching the request into smaller ones and caching data in the past which is unlikely to change.
I'm trying to get the Free busy data of other people that are within my Google organization using the google-api-services-calendar:v3 for Android (using Kotlin). I'm getting the times just fine for events with a set duration. But all day events don't show up on the list. Documentation on this is almost nowhere to be found and the stuff that I find on developers.google.com contains code that was deprecated in 2013...
// ...
val busyTimesList = mutableListOf<AgendaPlotter.TimeSpan>()
SessionService.sharedInstance.getGoogleAccount(activity)
observeOn(Schedulers.io())
.subscribe {
mCredential!!.selectedAccount = it.account
val request = FreeBusyRequest()
val durationCal = Calendar.getInstance()
durationCal.time = startDay.time
Calendars.startOfDay(durationCal)
request.timeMin = DateTime(durationCal.time)
durationCal.add(Calendar.DATE, 1)
request.timeMax = DateTime(durationCal.time)
val requestItems = listOf(FreeBusyRequestItem().setId("email#from.colleague"))
request.items = requestItems
request.timeZone = TimeZone.getDefault().id
val busyTimes: FreeBusyResponse
try {
val query = mService!!.freebusy().query(request)
// Use partial GET to retrieve only needed fields.
query.fields = "calendars"
busyTimes = query.execute()
busyTimes.calendars.forEach {
it.toPair().second.busy.forEach { timeSpan ->
val busyTime = AgendaPlotter.TimeSpan()
busyTime.fromTime.timeInMillis = timeSpan.start.value
busyTime.toTime.timeInMillis = timeSpan.end.value
busyTimesList.add(busyTime)
}
}
emitter.onNext(busyTimesList)
} catch (e: IOException) {
e.printStackTrace()
// ...
}
}
// ...
So my question, how do I also obtain the whole day events?
After some searching I noticed that there is actually nothing wrong with the API. It's a setting for a whole day event to be busy of free.
By default this is set to free, which makes it not show as a busy time, which makes sense. This goes unnoticed by a lot of people and they will be "free" on that day.
I'm doing a long write to a BLE for making an OTA update, but I need to wait for the write response of the BLE device for sending more data but I don't know how to catch the device write response, I'm using a Samsung galaxy tab s2 with android 7, and Kotlin for my code
override fun otaDataWrite(data:ByteArray) {
manager.connection?.flatMap { rxBleConnection: RxBleConnection? -> rxBleConnection?.createNewLongWriteBuilder()
?.setCharacteristicUuid(OTACharacteristics.OTA_DATA.uuid)
?.setBytes(data)
?.setMaxBatchSize(totalPackages)
?.build()
}?.subscribe({ t: ByteArray? ->
Log.i("arrive", "data ${converter.bytesToHex(t)}")
manageOtaWrite()
}, { t: Throwable? -> t?.printStackTrace() })
every time that I write the characteristic the subscriptions respond me immediately with the written data, I need capture the response of the characteristic, for sending more data
You are writing about response from the characteristic — I assume that the characteristic you refer is the one with UUID=OTA_DATA. The Long Write consist of small writes internally (so called batches).
What you probably want to achieve is something like:
fun otaDataWrite(data: ByteArray) {
manager.connection!!.setupNotification(OTA_DATA) // first we need to get the notification on to get the response
.flatMap { responseNotificationObservable -> // when the notification is ready we create the long write
connection.createNewLongWriteBuilder()
.setCharacteristicUuid(OTA_DATA)
.setBytes(data)
// .setMaxBatchSize() // -> if omitted will default to the MTU (20 bytes if MTU was not changed). Should be used only if a single write should be less than MTU in size
.setWriteOperationAckStrategy { writeCompletedObservable -> // we need to postpone writing of the next batch of data till we get the response notification
Observable.zip( // so we zip the response notification
responseNotificationObservable,
writeCompletedObservable, // with the acknowledgement of the written batch
{ _, writeCompletedBoolean -> writeCompletedBoolean } // when both are available the next batch will be written
)
}
.build()
}
.take(1) // with this line the notification that was set above will be discarded after the long write will finish
.subscribe(
{ byteArray ->
Log.i("arrive", "data ${converter.bytesToHex(byteArray)}")
manageOtaWrite()
},
{ it.printStackTrace() }
)
}
Well, after a lot of testing, I finally develop a standalone class for the OTA update with the android BLE API, and I used it together with all my RxBle methods, I don't know if I have a hardware problem or something else, but I solve the problem, thanks a lot.