Hi everyone just wondering if anyone knew what we need to do to play IMA ads on chrome cast from a sender app,
From what I understand reading the docs the only way to play it is to send a message to the chromecast receiver with the pubads URL:
private void loadMedia(MediaInfo mediaInfo, Boolean autoplay) {
try {
Log.d(TAG, "loading media");
mRemoteMediaPlayer.load(sApiClient, mediaInfo, autoplay)
.setResultCallback(new ResultCallback<RemoteMediaPlayer.MediaChannelResult>() {
#Override
public void onResult(RemoteMediaPlayer.MediaChannelResult result) {
if (result.getStatus().isSuccess()) {
boolean adStarted = mVideoPlayerController.hasAdStarted();
if (mVideoFragment.isVmap() || !adStarted) {
sendMessage("requestAd," + mAdTagUrl + ","
+ mVideoPlayerController.getCurrentContentTime());
} else {
sendMessage("seek,"
+ mVideoPlayerController.getCurrentContentTime());
}
} else {
Log.e(TAG, "Error loading Media : "
+ result.getStatus().getStatusCode());
}
FYI : the mAdTagUrl is the 'pubads' link
Ex pubs link :
https://pubads.g.doubleclick.net/gampad/ads?sz=640x480&iu=/124319096/external/single_ad_samples&ciu_szs=300x250&impl=s&gdfp_req=1&env=vp&output=vast&unviewed_position_start=1&cust_params=deployment%3Ddevsite%26sample_ct%3Dskippablelinear&correlator=
Incase anyone was looking for the answer :
it's actually in the AdBreak Clip Info Builder -> setVastAdsRequest() Method.
Pass in the 'pubads' link to this method , and you're good to go.
Related
I migrated Google Play Billing Library in Android Studio from 3.0.3 (was working fine) to 4.0.0.
I've checked my Google Play Billing and all seems OK and the SKU status is ACTIVE (no red flags).
I've tried my best to follow migration instructions # https://developer.android.com/google/play/billing/integrate#establish_a_connection_to_google_play
So far, all I can muster is an OK connection to Google Play Billing, that is, after onBillingSetupFinished() method, the BillingClient.BillingResponseCode.OK responds nicely, without error messages.
My problem begins somehere with the call to querySkuDetailsAsync(): There is no response here, not even an error notification. The google website puts a lot of stress emphasis on this call so I sense this is where the fun begins.
I have provided the sample code with the problem. I have used many many fixes from Stack Overflow but now I'm really really stuck and really need this to work.
My problem code below:
'''
/*
//Using the following library in build.graddle for app module
dependencies {
def billing_version = "4.0.0"
implementation "com.android.billingclient:billing:$billing_version"
}
*/
StringBuilder builder4SKUInfo;
private void get_Subscribe2_Characters() {
Subscribe2_Characters_Button.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
//I Toggle Visibility of Views Here
billingClient.startConnection(new BillingClientStateListener() {
//Android Studio auto-prompts to generate onBillingSetupFinished & onBillingServiceDisconnected
#Override
public void onBillingSetupFinished(#NonNull BillingResult billingResultC) {
if (billingResultC.getResponseCode() == BillingClient.BillingResponseCode.OK) {
//BillingResponseCode is OK here: Works Just Fine!
//The problem starts below
String skuToSell = "MySKU_Character_001"; //In my project, the SKU is cut-pasted from Google Play Console
List<String> skuList = new ArrayList<> ();
skuList.add(skuToSell);
SkuDetailsParams.Builder params = SkuDetailsParams
.newBuilder()
.setSkusList(sku_Details) //
.setType(BillingClient.SkuType.SUBS);
billingClient.querySkuDetailsAsync(params.build(),
new SkuDetailsResponseListener() {
#Override
public void onSkuDetailsResponse(#NonNull BillingResult billingResult, #NonNull List<SkuDetails> PurchaseDetailsList) {
//NOTHING! Not getting BillingResult
//Problem seems to at this point
if (PurchaseDetailsList.size() > 0) {
//NOTHING! Not getting size
for (SkuDetails PurchaseSKU_Info : PurchaseDetailsList) {
builder4SKUInfo = new StringBuilder(300);
if (PurchaseSKU_Info.getSku().contains("MySKU_Character_001")) {
String getSKUInfo = (
"\nTitle [Query]: " + PurchaseSKU_Info.getTitle()
+ "\n\nDetails: " + PurchaseSKU_Info.getDescription()
+ "\n\nDuration: " + PurchaseSKU_Info.getSubscriptionPeriod()
+ "\n\nPrice" + PurchaseSKU_Info.getPrice()
+ "\n\nAvoid Problems:\nUpdated Subscription Settings on Google Play"
+ "\n\nIMPORTANT: NOT Transferable"
+ "\n\n For this device only\n");
//+ "\nOther SKUs: " + SKU_Info.getSku()
//"001 = " + billingResultB.getResponseCode()
//+ "\nList Size: " + PurchaseDetailsList.size());
builder4SKUInfo.append(getSKUInfo); //The result I need to use elsewhere
}
}
} else if (billingResult.getResponseCode() == BillingClient.BillingResponseCode.ITEM_ALREADY_OWNED) {
//No Google Play response for this
} else if (billingResult.getResponseCode() == BillingClient.BillingResponseCode.ITEM_NOT_OWNED) {
//No Google Play response for this
} else if (billingResult.getResponseCode() == BillingClient.BillingResponseCode.USER_CANCELED) {
//Do something about cancels
} else if (billingResult.getResponseCode() == BillingClient.BillingResponseCode.BILLING_UNAVAILABLE) {
//No Google Play response for this
} else if (billingResult.getResponseCode() == BillingClient.BillingResponseCode.SERVICE_DISCONNECTED) {
//No Google Play response for this
} else if (billingResult.getResponseCode() == BillingClient.BillingResponseCode.SERVICE_TIMEOUT) {
//No Google Play response for this
} else {
//Following Toast does not show
String SomethingWrong = "Somethings is Wrong" +
"\nUpdate Your Google Play Billing Info" +
"\nCheck Internet Connection";
Toast.makeText(KH.this, SomethingWrong, Toast.LENGTH_LONG).show();
}
}
});
}
}
#Override
public void onBillingServiceDisconnected() {
//Following Toast does not show
String BillingServiceDisconnected = "Billing Service Disconnected" +
"\nUpdate Your Google Play Billing Info" +
"\nCheck Internet Connection";
Toast.makeText(KH.this, BillingServiceDisconnected, Toast.LENGTH_LONG).show();
}
});
}
});
}
'''
So I braved to ask the folks at Google on the issue tracker page and they appropriately and promptly responded, "We now post the results to the background thread instead of the UIThread . . ."
Right away, I knew I had the wrong approach. If the result is delivered to background thread, I had to ditch the 3.x billing approach and start from scratch.
I reached back again to Google for an example and they sent me their GitHub # https://github.com/android/play-billing-samples/tree/main/TrivialDriveJava
The example is akin to an "Intent" but with a lot more code declaration than function selection: has several classes, methods and files to work through. So to fix billing 4.x, the easiest path was to just rip the example into my app, whittled down the errors, gray out methods I don't need and finally overlay my views, refactor classes (fix errors again) and create new user workflows.
Following #Maasaivatar's answer, it works after running the SkuDetailsResponseListener on the main thread:
billingClient.querySkuDetailsAsync(params.build(), (billingResult, list) ->
runOnUiThread(() -> {
// same code as before
}));
We have follow Scottyab Safetynet Library.
We are facing error of “Status{statusCode=NETWORK_ERROR, resolution=null}” event though 4G internet connectivity available in our android device with package name com.safetynet.sample where as sample project is working fine with package name com.scottyab.safetynet.sample. We have check this solution but not work.
Below code where we have facing this issue
private void runSafetyNetTest() {
Log.v(TAG, "running SafetyNet.API Test");
requestNonce = generateOneTimeRequestNonce();
requestTimestamp = System.currentTimeMillis();
writeLog("running SafetyNet.API Test");
SafetyNet.SafetyNetApi.attest(googleApiClient, requestNonce)
.setResultCallback(new ResultCallback<SafetyNetApi.AttestationResult>() {
#Override
public void onResult(final SafetyNetApi.AttestationResult result) {
writeLog("running SafetyNet.API Result");
//result = Status{statusCode=NETWORK_ERROR, resolution=null}
if (!validateResultStatus(result)) {
return;
}
final String jwsResult = result.getJwsResult();
final SafetyNetResponse response = parseJsonWebSignature(jwsResult);
lastResponse = response;
writeLog("Res :: " + response);
//validate payload of the response
if (validateSafetyNetResponsePayload(response)) {
if (!TextUtils.isEmpty(googleDeviceVerificationApiKey)) {
//if the api key is set, run the AndroidDeviceVerifier
AndroidDeviceVerifier androidDeviceVerifier = new AndroidDeviceVerifier(googleDeviceVerificationApiKey, jwsResult);
androidDeviceVerifier.verify(new AndroidDeviceVerifier.AndroidDeviceVerifierCallback() {
#Override
public void error(String errorMsg) {
callback.error(RESPONSE_ERROR_VALIDATING_SIGNATURE, "Response signature validation error: " + errorMsg);
}
#Override
public void success(boolean isValidSignature) {
if (isValidSignature) {
callback.success(response.isCtsProfileMatch(), response.isBasicIntegrity());
} else {
callback.error(RESPONSE_FAILED_SIGNATURE_VALIDATION, "Response signature invalid");
}
}
});
} else {
Log.w(TAG, "No google Device Verification ApiKey defined");
callback.error(RESPONSE_FAILED_SIGNATURE_VALIDATION_NO_API_KEY, "No Google Device Verification ApiKey defined. Marking as failed. SafetyNet CtsProfileMatch: " + response.isCtsProfileMatch());
}
} else {
callback.error(RESPONSE_VALIDATION_FAILED, "Response payload validation failed");
}
}
}
);
}
This might be related to the fact that the attestation API has been marked as deprecated. The new one doesn't depend on the google client API, you should check this. Also Google released an example app using the new api, you can check here.
As per this discussion, the wrong API_KEY may be the reason for the error.
I'm using this tuto to integrate Play Billing Library to my app: http://www.androidrey.com/implement-play-billing-library-in-android-application/ and all works good ... well, not at all. I have problems to know when a suscription was cancelled, I tested all the methods to find a resultCode or something to know this state, but have a method that I could not implement. Could be this the problem?
class: BillingManager.java
public void queryPurchases() {
Runnable queryToExecute = new Runnable() {
#Override
public void run() {
long time = System.currentTimeMillis();
Purchase.PurchasesResult purchasesResult = billingClient.queryPurchases(BillingClient.SkuType.INAPP);
if (areSubscriptionsSupported()) {
Purchase.PurchasesResult subscriptionResult
= billingClient.queryPurchases(BillingClient.SkuType.SUBS);
System.out.println("QUERY 0");
if (subscriptionResult.getResponseCode() == BillingClient.BillingResponse.OK) {
purchasesResult.getPurchasesList().addAll(
subscriptionResult.getPurchasesList());
System.out.println("QUERY 1");
} else {
// Handle any error response codes.
}
} else if (purchasesResult.getResponseCode() == BillingClient.BillingResponse.OK) {
// Skip subscription purchases query as they are not supported.
System.out.println("QUERY 2");
} else {
// Handle any other error response codes.
System.out.println("QUERY 3");
}
onQueryPurchasesFinished(purchasesResult);
System.out.println("QUERY RESULT "+ purchasesResult);
}
};
executeServiceRequest(queryToExecute);
}
private void onQueryPurchasesFinished(Purchase.PurchasesResult result) {
// Have we been disposed of in the meantime? If so, or bad result code, then quit
if (billingClient == null || result.getResponseCode() != BillingClient.BillingResponse.OK) {
Log.w(TAG, "Billing client was null or result code (" + result.getResponseCode()
+ ") was bad – quitting");
return;
}
Log.d(TAG, "Query inventory was successful.");
// Update the UI and purchases inventory with new list of purchases
// mPurchases.clear();
onPurchasesUpdated(BillingClient.BillingResponse.OK, result.getPurchasesList());
}
public boolean areSubscriptionsSupported() {
int responseCode = billingClient.isFeatureSupported(BillingClient.FeatureType.SUBSCRIPTIONS);
if (responseCode != BillingClient.BillingResponse.OK) {
Log.w(TAG, "areSubscriptionsSupported() got an error response: " + responseCode);
}
return responseCode == BillingClient.BillingResponse.OK;
}
It is supposed to be called here: MyBillingUpdateListener.java
public class MyBillingUpdateListener implements BillingManager.BillingUpdatesListener {
//final BillingManager billingManager = new BillingManager(,this );
#Override
public void onBillingClientSetupFinished() {
//billingManager.queryPurchases(); THIS IS WHAT I COULD NOT IMPLEMENT
}
Any help is welcome, thanks!.
Play Billing 1.0 does not have the concept of purchase states (anymore), so there currently is no way to get this information using the Play Billing library.
My understanding is that queryPurchases is supposed to return actual valid purchases only. However, it gets the information from a long living cache and you have no way of updating it manually.
onBillingClientSetupFinished is completely unrelated.
Here is an active discussion on the subject: https://github.com/googlesamples/android-play-billing/issues/122
I am creating a generic Chromecast remote control app. Most of the guts of the app are already created and I've managed to get Chromecast volume control working (by connecting to a Chromecast device along side another app that is casting - YouTube for example).
What I've having difficult with is performing other media commands such as play, pause, seek, etc.
Use case example:
1. User opens YouTube on their android device and starts casting a video.
2. User opens my app and connects to the same Chromecast device.
3. Volume control from my app (works now)
4. Media control (play, pause, etc) (does not yet work)
I found the Cast api reference that explains that you can sendMessage(ApiClient, namespace, message) with media commands; however the "message" (JSON) requires the sessionId of the current application (Youtube in this case). I have tried the following, but the connection to the current application always fails; status.isSuccess() is always false:
Cast.CastApi
.joinApplication(mApiClient)
.setResultCallback(
new ResultCallback<Cast.ApplicationConnectionResult>() {
#Override
public void onResult(
Cast.ApplicationConnectionResult result) {
Status status = result.getStatus();
if (status.isSuccess()) {
ApplicationMetadata applicationMetadata = result
.getApplicationMetadata();
sessionId = result.getSessionId();
String applicationStatus = result
.getApplicationStatus();
boolean wasLaunched = result
.getWasLaunched();
Log.i(TAG,
"Joined Application with sessionId: "
+ sessionId
+ " Application Status: "
+ applicationStatus);
} else {
// teardown();
Log.e(TAG,
"Could not join application: "
+ status.toString());
}
}
});
Is is possible to get the sessionId of an already running cast application from a generic remote control app (like the one I am creating)? If so, am I right in my assumption that I can then perform media commands on the connected Chromecast device using something like this:
JSONObject message = new JSONObject();
message.put("mediaSessionId", sessionId);
message.put("requestId", 9999);
message.put("type", "PAUSE");
Cast.CastApi.sendMessage(mApiClient,
"urn:x-cast:com.google.cast.media", message.toString());
Update:
I have tried the recommendations provided by #Ali Naddaf but unfortunately they are not working. After creating mRemoteMediaPlayer in onCreate, I also do requestStatus(mApiClient) in the onConnected callback (in the ConnectionCallbacks). When I try to .play(mApiClient) I get an IllegalStateException stating that there is no current media session. Also, I tried doing joinApplication and in the callback performed result.getSessionId; which returns null.
A few comments and answers:
You can get the sessionId from the callback of launchApplication or joinApplication; in the "onResult(result)", you can get the sessionId from: result.getSessionId()
YouTube is still not on the official SDK so YMMV, for apps using official SDK, you should be able to use the above approach (most of it)
Why are you trying to set up a message yourself? Why not building a RemoteMediaPlayer and using play/pause that is provided there? Whenever you are working with the media playback through the official channel, always use the RemoteMediaPlayer (don't forget to call requestStatus() on it after creating it).
Yes it is possible , First you have to save sesionId and CastDevice device id
and when remove app from background and again open app please check is there sessionId then call bello line.
Cast.CastApi.joinApplication(apiClient, APP_ID,sid).setResultCallback(connectionResultCallback);
if you get success result then need to implement further process in connectionResultCallback listener.
//Get selected device which you selected before
#Override
public void onRouteAdded(MediaRouter router, MediaRouter.RouteInfo route) {
// Log.d("Route Added", "onRouteAdded");
/* if (router.getRoutes().size() > 1)
Toast.makeText(homeScreenActivity, "'onRouteAdded :: " + router.getRoutes().size() + " -- " + router.getRoutes().get(1).isSelected(), Toast.LENGTH_SHORT).show();
else
Toast.makeText(homeScreenActivity, "'onRouteAdded :: " + router.getRoutes(), Toast.LENGTH_SHORT).show();*/
if (router != null && router.getRoutes() != null && router.getRoutes().size() > 1) {
// Show the button when a device is discovered.
// Toast.makeText(homeScreenActivity, "'onRouteAdded :: " + router.getRoutes().size() + " -- " + router.getRoutes().get(1).isSelected(), Toast.LENGTH_SHORT).show();
mMediaRouteButton.setVisibility(View.VISIBLE);
titleLayout.setVisibility(View.GONE);
castName.setVisibility(View.VISIBLE);
selectedDevice = CastDevice.getFromBundle(route.getExtras());
routeInfoArrayList = router.getRoutes();
titleLayout.setVisibility(View.GONE);
if (!isCastConnected) {
String deid = MyPref.getInstance(homeScreenActivity).readPrefs(MyPref.CAST_DEVICE_ID);
for (int i = 0; i < routeInfoArrayList.size(); i++) {
if (routeInfoArrayList.get(i).getExtras() != null && CastDevice.getFromBundle(routeInfoArrayList.get(i).getExtras()).getDeviceId().equalsIgnoreCase(deid)) {
selectedDevice = CastDevice.getFromBundle(routeInfoArrayList.get(i).getExtras());
routeInfoArrayList.get(i).select();
ReSelectedDevice(selectedDevice, routeInfoArrayList.get(i).getName());
break;
}
}
}
}
}
//Reconnect google Api Client
public void reConnectGoogleApiClient() {
if (apiClient == null) {
Cast.CastOptions apiOptions = new
Cast.CastOptions.Builder(selectedDevice, castClientListener).build();
apiClient = new GoogleApiClient.Builder(this)
.addApi(Cast.API, apiOptions)
.addConnectionCallbacks(reconnectionCallback)
.addOnConnectionFailedListener(connectionFailedListener)
.build();
apiClient.connect();
}
}
// join Application
private final GoogleApiClient.ConnectionCallbacks reconnectionCallback = new GoogleApiClient.ConnectionCallbacks() {
#Override
public void onConnected(Bundle bundle) {
// Toast.makeText(homeScreenActivity, "" + isDeviceSelected(), Toast.LENGTH_SHORT).show();
try {
String sid = MyPref.getInstance(homeScreenActivity).readPrefs(MyPref.CAST_SESSION_ID);
String deid = MyPref.getInstance(homeScreenActivity).readPrefs(MyPref.CAST_DEVICE_ID);
if (sid != null && deid != null && sid.length() > 0 && deid.length() > 0)
Cast.CastApi.joinApplication(apiClient, APP_ID, sid).setResultCallback(connectionResultCallback);
isApiConnected = true;
} catch (Exception e) {
}
}
#Override
public void onConnectionSuspended(int i) {
isCastConnected = false;
isApiConnected = false;
}
};
I have successfully integrated facebook,twitter using social auth,
But i am getting issue while integrating google plus integration.I can login but unable to share anything.
Error :-
org.brickred.socialauth.exception.SocialAuthException: java.io.FileNotFoundException: https://accounts.google.com/o/oauth2/token
If anyone have idea please reply.
Thanks in advance....
I am using the below code :-
Authenticating :-
SocialAuthAdapter googlePlusAdapter;
try
{
googlePlusAdapter = new SocialAuthAdapter(new ResponseListener());
googlePlusAdapter.addCallBack(Provider.GOOGLEPLUS, "http://localhost");
googlePlusAdapter.authorize(this,org.brickred.socialauth.android.SocialAuthAdapter.Provider.GOOGLEPLUS);
} catch (Exception e) {
Toast.makeText(getApplicationContext(),"Exception " + e.toString(), 1).show();
}
Sharing :-
try{
String msg="post from my app";
googlePlusAdapter.updateStatus(msg, new SocialAuthListener<Integer>() {
#Override
public void onExecute(String arg0, Integer t) {
// TODO Auto-generated method stub
Integer status = t;
if (status.intValue() == 200 || status.intValue() == 201 ||status.intValue() == 204)
{ Toast.makeText(getApplicationContext(), "Message posted",Toast.LENGTH_LONG).show();
googlePlusAdapter.signOut(Provider.GOOGLEPLUS.toString());
}
else
Toast.makeText(getApplicationContext(), "Message not posted",Toast.LENGTH_LONG).show();
}
#Override
public void onError(SocialAuthError error) {
// TODO Auto-generated method stub
Toast.makeText(getApplicationContext(), "Message not posted!! Try again",Toast.LENGTH_LONG).show();
}
}, true);
finish();
}
catch(Exception e)
{
}
The problem is that you have created a Client ID for Android application.
You have to create a Client ID for web application, then in that option you have to assing a redirect_uri.
Steps:
Go to https://cloud.google.com/console/project
Select your project
Select APIs & auth, then Credentials
Press Crate new Client ID
Create a Web application type.
Put a valid uri that can respond to YOUR_URL/oauth2callback
So in your code:
googlePlusAdapter.addCallBack(Provider.GOOGLEPLUS, "http://localhost");
You have to write like this:
googlePlusAdapter.addCallBack(Provider.GOOGLEPLUS, same_uri_you_put_in_redirect_uri);
This is because socialauth authenticate through http.